Cardano on Kubernetes
Advanced enterprise setup with Kubernetes

Cardano Namespace

Running Cardano on Kubernetes is easy. First, lets create a dedicated namespace for it and make it the default. In case you don't have a running Kubernetes cluster just yet, take a look at Creating a Kubernetes Cluster​
1
$ kubectl create namespace cardano
2
namespace/cardano created
3
​
4
$ kubectl config set-context --current --namespace=cardano
5
Context "gke_beaconchain_us-central1-a_cardano" modified.
Copied!
Great, that is working.

Attaching Node Labels

Here we get the external IP for our relay node in the cluster
1
$ kubectl get nodes --output wide
2
​
3
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
4
gke-cardano-default-pool-9bec42f2-g9p2 Ready <none> 14m v1.18.12-gke.1210 10.128.0.33 34.71.138.50 Container-Optimized OS from Google 5.4.49+ docker://19.3.9
5
gke-cardano-default-pool-9bec42f2-w5w3 Ready <none> 14m v1.18.12-gke.1210 10.128.0.32 35.223.44.154 Container-Optimized OS from Google 5.4.49+ docker://19.3.9
6
​
7
EXTERNAL_IP=34.71.138.50
8
NODE_PORT=30010
9
​
10
# The IP address that you choose must be a valid IPv4 or IPv6 address from within the service-cluster-ip-range CIDR range.
11
RELAY_CLUSTER_IP=10.3.240.100
12
BPROD_CLUSTER_IP=10.3.240.200
13
​
Copied!
We want to deploy the relay to a node with a known IP. Here we assign node type labels that we later use in node selectors.
1
$ kubectl label nodes gke-cardano-default-pool-9bec42f2-g9p2 nodeType=relay
2
$ kubectl label nodes gke-cardano-default-pool-9bec42f2-w5w3 nodeType=bprod
Copied!

Create a Firewall Rule

Here we create a firewall rule for incoming traffic to a given node port.
1
$ gcloud compute firewall-rules create k8s-relay --allow tcp:$NODE_PORT
2
​
3
NAME NETWORK DIRECTION PRIORITY ALLOW DENY DISABLED
4
k8s-relay default INGRESS 1000 tcp:30010 False
Copied!

Relay Configuration

Next, we create a configuration map for the Relay
1
RELAY_TOPOLOGY=`cat << EOF
2
{
3
"Producers": [
4
{
5
"addr": "relays-new.cardano-mainnet.iohk.io",
6
"port": 3001,
7
"valency": 1
8
},
9
{
10
"addr": "$BPROD_CLUSTER_IP",
11
"port": 3001,
12
"valency": 1
13
}
14
]
15
}
16
EOF`
17
​
18
$ kubectl create configmap relaycfg \
19
--from-literal=publicIP="$EXTERNAL_IP:$NODE_PORT" \
20
--from-literal=customPeers="$BPROD_CLUSTER_IP:3001" \
21
--from-literal=topology="$RELAY_TOPOLOGY"
22
​
23
configmap/relaycfg created
Copied!

Block Producer Configuration

Next, we create a configuration map for the Block Producer
1
BPROD_TOPOLOGY=`cat << EOF
2
{
3
"Producers": [
4
{
5
"addr": "$RELAY_CLUSTER_IP",
6
"port": 3001,
7
"valency": 1
8
}
9
]
10
}
11
EOF`
12
​
13
$ kubectl create configmap bprodcfg \
14
--from-literal=topology="$BPROD_TOPOLOGY"
15
​
16
configmap/bprodcfg created
17
​
Copied!

Block Producer Keys Secret

Next, we store the Block Producer configuration keys in a Kubernetes Secret.
1
$ kubectl create secret generic nodekeys \
2
--from-file=./cardano/keys/pool/kes.skey \
3
--from-file=./cardano/keys/pool/vrf.skey \
4
--from-file=./cardano/keys/pool/node.cert
5
​
6
secret/nodekeys created
Copied!

Deploying the Pods

Finally, we deploy a Cardano pods like this ...
1
$ kubectl apply -f nix/docker/k8s/cardano-nodes.yaml
2
​
3
storageclass.storage.k8s.io/cardano-standard-rwo created
4
persistentvolumeclaim/relay-data created
5
pod/relay created
6
service/relay-np created
7
service/relay-clip created
8
persistentvolumeclaim/bprod-data created
9
pod/bprod created
10
service/bprod-clip created
Copied!
For details you may want to have a look at nix/docker/k8s/cardano-nodes.yaml.

Looking at the Relay output

1
$ kubectl logs --tail=500 -f relay
2
​
3
Running the cardano node ...
4
Generating /var/cardano/config/mainnet-topology.json ...
5
CARDANO_CONFIG=/opt/cardano/config/mainnet-config.json
6
CARDANO_TOPOLOGY=/var/cardano/config/mainnet-topology.json
7
CARDANO_BIND_ADDR=0.0.0.0
8
CARDANO_PORT=3001
9
CARDANO_DATABASE_PATH=/opt/cardano/data
10
CARDANO_SOCKET_PATH=/opt/cardano/ipc/socket
11
CARDANO_LOG_DIR=/opt/cardano/logs
12
CARDANO_PUBLIC_IP=34.71.138.50:30010
13
CARDANO_CUSTOM_PEERS=10.3.240.200:3001
14
CARDANO_UPDATE_TOPOLOGY=true
15
CARDANO_BLOCK_PRODUCER=false
16
cardano-node run --config /opt/cardano/config/mainnet-config.json --topology /var/cardano/config/mainnet-topology.json --database-path /opt/cardano/data --socket-path /opt/cardano/ipc/socket --host-addr 0.0.0.0 --port 3001
17
Topology update: 47 * * * * root topologyUpdate
18
Initially waiting for 10 minutes ...
19
Listening on http://127.0.0.1:12798
20
[relay:cardano.node.networkMagic:Notice:5] [2021-02-27 14:35:06.97 UTC] NetworkMagic 764824073
21
[relay:cardano.node.basicInfo.protocol:Notice:5] [2021-02-27 14:35:06.97 UTC] Byron; Shelley
22
[relay:cardano.node.basicInfo.version:Notice:5] [2021-02-27 14:35:06.97 UTC] 1.25.1
23
...
24
[relay:cardano.node.ChainDB:Notice:35] [2021-02-27 14:35:08.22 UTC] Chain extended, new tip: 1dbc81e3196ba4ab9dcb07e1c37bb28ae1c289c0707061f28b567c2f48698d50 at slot 1
25
[relay:cardano.node.ChainDB:Notice:35] [2021-02-27 14:35:08.22 UTC] Chain extended, new tip: 52b7912de176ab76c233d6e08ccdece53ac1863c08cc59d3c5dec8d924d9b536 at slot 2
26
[relay:cardano.node.ChainDB:Notice:35] [2021-02-27 14:35:08.22 UTC] Chain extended, new tip: be06c81f4ad34d98578b67840d8e65b2aeb148469b290f6b5235e41b75d38572 at slot 3
Copied!

Looking at the Block Producer output

1
$ kubectl logs --tail=500 -f bprod
2
​
3
Running the cardano node ...
4
Generating /var/cardano/config/mainnet-topology.json ...
5
CARDANO_CONFIG=/opt/cardano/config/mainnet-config.json
6
CARDANO_TOPOLOGY=/var/cardano/config/mainnet-topology.json
7
CARDANO_BIND_ADDR=0.0.0.0
8
CARDANO_PORT=3001
9
CARDANO_DATABASE_PATH=/opt/cardano/data
10
CARDANO_SOCKET_PATH=/opt/cardano/ipc/socket
11
CARDANO_LOG_DIR=/opt/cardano/logs
12
CARDANO_PUBLIC_IP=
13
CARDANO_CUSTOM_PEERS=
14
CARDANO_UPDATE_TOPOLOGY=false
15
CARDANO_BLOCK_PRODUCER=true
16
CARDANO_SHELLEY_KES_KEY=/var/cardano/config/keys/kes.skey
17
CARDANO_SHELLEY_VRF_KEY=/var/cardano/config/keys/vrf.skey
18
CARDANO_SHELLEY_OPERATIONAL_CERTIFICATE=/var/cardano/config/keys/node.cert
19
cardano-node run --config /opt/cardano/config/mainnet-config.json --topology /var/cardano/config/mainnet-topology.json --database-path /opt/cardano/data --socket-path /opt/cardano/ipc/socket --host-addr 0.0.0.0 --port 3001 --shelley-kes-key /var/cardano/config/keys/kes.skey --shelley-vrf-key /var/cardano/config/keys/vrf.skey --shelley-operational-certificate /var/cardano/config/keys/node.cert
20
Listening on http://127.0.0.1:12798
21
[bprod:cardano.node.networkMagic:Notice:5] [2021-02-27 14:35:06.97 UTC] NetworkMagic 764824073
22
[bprod:cardano.node.basicInfo.protocol:Notice:5] [2021-02-27 14:35:06.97 UTC] Byron; Shelley
23
[bprod:cardano.node.basicInfo.version:Notice:5] [2021-02-27 14:35:06.97 UTC] 1.25.1
24
...
25
[bprod:cardano.node.ChainDB:Notice:35] [2021-02-27 14:35:08.22 UTC] Chain extended, new tip: 1dbc81e3196ba4ab9dcb07e1c37bb28ae1c289c0707061f28b567c2f48698d50 at slot 1
26
[bprod:cardano.node.ChainDB:Notice:35] [2021-02-27 14:35:08.22 UTC] Chain extended, new tip: 52b7912de176ab76c233d6e08ccdece53ac1863c08cc59d3c5dec8d924d9b536 at slot 2
27
[bprod:cardano.node.ChainDB:Notice:35] [2021-02-27 14:35:08.22 UTC] Chain extended, new tip: be06c81f4ad34d98578b67840d8e65b2aeb148469b290f6b5235e41b75d38572 at slot 3
Copied!

Deleting Resources

To stop the nodes, we can simply delete their respective pod. The Cardano node process will shutdown gracefully and continue from where it left off when we redeploy the pod.
1
$ kubectl delete pod,svc --all
2
​
3
pod "bprod" deleted
4
pod "relay" deleted
5
service "bprod-clip" deleted
6
service "relay-clip" deleted
7
service "relay-np" deleted
Copied!
The persistent volume is a cluster resource that survives namespace deletion.
1
$ kubectl delete pvc,pv --all
2
​
3
persistentvolumeclaim "relay-data" deleted
4
persistentvolume "pvc-53819848-09bf-4bc7-9a26-25e318b1f98e" deleted
Copied!

Deleting the cluster

1
$ gcloud container clusters delete $CLUSTER_NAME \
2
--zone=$CLUSTER_ZONE
Copied!
​