This repository provisions and operates a K3s-based Kubernetes cluster for canopy node testing, then generates and applies per-chain configuration artifacts (genesis, keystore, accounts, delegators, committees) into the cluster as ConfigMaps. It also includes a workflow for installing Helm, TLS via Hetzner DNS, and a monitoring stack (Prometheus, Grafana, Loki, Promtail). An Ansible-based setup can create/add nodes, update the cluster, and configure TLS/monitoring end-to-end.
- Debian-based OS (e.g., Ubuntu)
- Non-root user with sudo privileges
- SSH access (public key authentication)
- Firewall allowances:
- Inbound TCP 22 from your IP (SSH)
- Inbound TCP 80 from 0.0.0.0/0 (HTTP for apps/Let’s Encrypt)
- Inbound TCP 443 from 0.0.0.0/0 (HTTPS for apps)
- Inbound TCP 6443 between cluster nodes (K3s API server)
- Inbound TCP 10250 between cluster nodes (Kubelet API)
- Inbound UDP 8472 between cluster nodes (Flannel VXLAN)
- Inbound TCP 2379-2380 for embedded etcd (required for HA embedded etcd)
- Optional: Inbound TCP 6443 from your local IP (kubectl access)
- Domain using Hetzner DNS nameservers
- Wildcard A record pointing to the cluster's public IPs (e.g., A *.example.com -> 192.168.0.1, 192.168.0.2)
- Hetzner read/write API token (for cert-manager DNS01)
- Required packages:
make,kubectl,helm,ansible,cilium,go(to build the tools) - With Homebrew installed, the others can be installed via:
make helpers/brew-install-requirements
- SSH access from this machine to all cluster servers
- Discord webhook URL (for Grafana alerts)
Makefile— primary entry point for workflows:- Ansible orchestration (setup, TLS, monitoring)
- Helm-based canopy workload management
go-scripts/genesis-generator— CLI to generate chain artifacts underartifacts/<CONFIG>/chain_<id>/...plus sharedids.jsoncmd/k8s-applier— CLI to apply the generated artifacts to Kubernetes as ConfigMapsinit-node— auxiliary node init program
ansible/— inventory, example secrets, playbooks, and collection requirementscluster/— TLS and monitoring setup scripts, and Helm chart forcanopy
-
Clone the repository:
git clone https://github.com/canopy-network/k8s-node-tester.git cd k8s-node-tester -
Prepare Ansible inventory:
cp ansible/inventory.example.yml ansible/inventory.yml # edit ansible/inventory.yml with server/agent hosts and SSH users -
Prepare TLS and domain secrets:
cp ansible/secrets.example.yml ansible/secrets.yml # edit domain, email, hetzner api token; optionally set k3s token -
Run the full cluster setup (installs requirements, creates/updates nodes, TLS, monitoring):
make ansible/cluster-setup
- This will:
- Install k3s server and agent nodes
- Install ansible requirements
- Run the base site playbook
- Install Helm
- Configure TLS with Hetzner (uses
ansible/secrets.yml) - Install monitoring (Prometheus, Grafana, Loki, Promtail)
- This will:
-
To later add a new node, update
ansible/inventory.ymland run:make ansible/site
-
Build the Go tools (required for config generation/apply):
make go-scripts/build
Note: Manual provisioning instead of Ansible can be done if desired. See Makefile targets
in the manual setup section for manual workflows.
Profiles live in:
They define:
- Global generation parameters (e.g., concurrency, JSON formatting)
- Node count
- Per-chain composition (validators, full nodes, accounts, delegators, committees)
Available profiles include default, min, medium, max, and mature. Check the script's
README file for details.
-
Ensure
kubectlis targeting the cluster:kubectl config use-context k3s-ansible
-
Generate and apply artifacts for a given profile:
make test/prepare CONFIG=default
CONFIGis the name of the profile to use from the profiles set in the genesisconfigs.yamlfile.
-
Start canopy workloads:
make test/start NODES=4
- Must Set
NODESto match or align with the config'snodes.countin the selected profile for correct scaling.
- Must Set
Install Chaos Mesh (one-time):
make chaos/mesh
# or manually:
helm repo add chaos-mesh https://charts.chaos-mesh.org
helm repo update
helm upgrade --install chaos-mesh chaos-mesh/chaos-mesh -n chaos-mesh --create-namespace \
-f ./cluster/chaos-mesh/values.yamlThen configure networkChaos in cluster/canopy/helm/values.yaml. You can define multiple
faults at once via networkChaos.experiments. Each experiment renders a separate
NetworkChaos resource, or a Schedule resource if schedule is set for periodic runs.
By default the selector targets only the canopy pods (app=node).
Example:
networkChaos:
enabled: true
experiments:
- name: canopy-latency
action: delay
delay:
latency: "150ms"
jitter: "20ms"
correlation: "25"
- name: canopy-loss
action: loss
loss:
loss: "2"
correlation: "0"
- name: canopy-egress-blackhole
action: loss
mode: random-max-percent
value: "30"
direction: to
duration: "5s"
schedule: "@every 1m"
loss:
loss: "100"
correlation: "0"This is useful for testing live chains in an isolated network environment. Need to follow a strict order of operations:
- On the secrets file, need to complete the dns rewrites with the domains of the nodes to mimic.
The nodes on the following
ids.jsonfile must be set on this same order. Example:
dns:
rewrites:
namespace: canopy
service: p2p
statefulset_name: node
domains:
- domain-1.com
- domain-2.com- On
configs/genesis/artifactsmake a folder with the name of the configuration, i.e.isolated - on
isolated/ids.json, create akeysobject which will contain the information of each node. Remember. The order of these nodes must match the order of the domains set earlier. Refer to the genesis generator's README for more information on the meaning of each field.
{
"main-accounts": {},
"keys": {
"node-1": {
"id": 1,
"chainId": 1,
"rootChainId": 1,
"rootChainNode": 1,
"peerNode": 1,
"address": "02cd4...",
"publicKey": "abda38...",
"privateKey": "6c275...",
"nodeType": "validator",
"domain": "domain-1.com"
},
"node-2": {
"id": 2,
"chainId": 1,
"rootChainId": 1,
"rootChainNode": 2,
"peerNode": 2,
"address": "02cd4e...",
"publicKey": "abda38e...",
"privateKey": "6c27505...",
"nodeType": "validator",
"domain": "domain-2.com"
},
}
}- On
isolared/chain_N(For this example 1) fill the requiredconfig.json,genesis.jsonandkeystore.jsonaccording to the genesis README - On canopy configs file set
isolate: true - Run
make genesis/apply CONFIG=isolatedandmake test/start NODES=2depending on the config name and number of nodes running on the setup
Hint: To fill up all the artifacts for genesis, you could run make genesis/prepare CONFIG=default and
replace all the generated files with the custom configuration.
With only the genesis files, the isolated enviroment will start at the genesis block, in order to start at a specific block:
- On the canopy configs file set
sharedPVC.active: true. Which will start a broken setup with theshared-filesdeployment - Copy the canopy chain files to the shared folder with
kubectl cp ./{files} shared-files-{id}:/shared/chain_1/(currently only working for chain 1) - Run
make test/destroyand rerun the start process for a working setup, the pods will automatically copy the files on that folder to their canopy folder
-
Remove Helm release and ConfigMaps used by tests:
make test/destroy
-
Tear down the entire K3s cluster via Ansible:
make ansible/teardown