In this tutorial I will demonstrate how to run a Ethereum 2 validator within a kubernetes cluster. We use Prysm as Eth2 and openethereum as Eth1 client.
The most obvious things first. Although openethereum and Prysm offer a Docker image, both of them will tell you to use the native client. One reason could be the focus on the main goal: providing a stable and functional client. A second once could be the reason that you can’t use more than one client at a time. In fact, if you try to run two clients together it can damage the network and cost you Ether. This means that one of the big advantages of containers, the elasticity, is not applicable here. But if you want to do it just because you can or you enjoy the self healing of damaged pods while you drive around with your lambo you earned from staking Ether your are good to go.
I don’t want to give you a large introduction to the new Ethereum ecosystem, you can read all relevant infos on their website. However there are a few things you need to know before we start.
This tutorial was done within the Ethereum testnet. To be able to setup a validator you need 32 Ether. First you need a wallet which is able to connect to the testnet. I would recoment MetaMask. Make sure to select the gorli network. Afterwards you can get Ether for free from the görli foucet. Those Ether have no value, but can be used for testing.
Its woth mentioning that Ethereum 2 isn’t a new system. Its more an overlay to Ethereum 1. The old Ethereum 1 blockchain will be a part of Ethereum 2. This means we have to install not only the Eth2 beacon node and the ETH 2 validator but also the traditional Eth1 client. At the end our cluster will look like this:
Setup the environment
If not already done, we have to install kubernetes. I used microk8s, but minikube is also a feasible choice. I used linux as the operating system. You can also use Windows, but I wan’t be able to support you with your setup. Installing kubernetes is often tricky but there are many tutorials out there that will help you.
We also need some permanent storage for our clients. The chain synchronization can take several days. You don’t want to start all over again if you restart your deployment. To store the chain information we create a simple folder in my home directory.
mkdir ~/.local/share/k8s/eth/testnet/openethereum
mkdir ~/.local/share/k8s/eth/testnet/prysm/validator
mkdir ~/.local/share/k8s/eth/testnet/prysm/beacon
Installing the Ethereum 1 client
As I already mentioned, we will use openethereum as our Eth1 client. Feel free to use another client if you want to.
apiVersion: apps/v1 kind: Deployment metadata: name: openethereum-deployment labels: app: openethereum spec: selector: matchLabels: app: openethereum replicas: 1 template: metadata: labels: app: openethereum spec: containers: - name: openethereum image: openethereum/openethereum:latest volumeMounts: - name: openethereum mountPath: /home/openethereum/.local/share/openethereum/ ports: - containerPort: 30303 protocol: UDP - containerPort: 30303 protocol: TCP - containerPort: 8545 protocol: TCP - containerPort: 8546 protocol: TCP args: - --unsafe-expose #- --nat #- extip:127.0.0.1 - --base-path - /home/openethereum/.local/share/openethereum - --chain - goerli volumes: - name: openethereum hostPath: path: /home/<YOUR_HOME_FOLDER>/.local/share/k8s/eth/testnet/openethereum --- apiVersion: v1 kind: Service metadata: name: openethereum-service spec: #type: NodePort selector: app: openethereum ports: - protocol: TCP name: eth-tcp port: 30303 targetPort: 30303 #nodePort: 30303 - protocol: UDP name: eth-udp port: 30303 targetPort: 30303 #nodePort: 30303 - protocol: TCP name: rpc port: 8545 targetPort: 8545 - protocol: TCP name: websocket port: 8546 targetPort: 8546
First, copy the code listing and save it as openethereum.yaml
. Make sure to replace <YOUR_HOME_FOLDER>
with your user or the entire path with the path you want the files to be stored.
You can execute the deployment with
kubectl apply -y openethereum.yaml
This will pull the docker image and create a deployment. It will also create a service to expose the needed ports. The client will be executed with the parameters: --unsafe-expose --base-path /home/openethereum/.local/share/openethereum --chain goerli
.
--unsafe-expose
is needed because every pod will have an own network endpoint within the virtual network of kubernetes. Usually you shouln’t use this option if you run the client directly on your local machine because all clients run on the same host and you don’t want to expose the management port 8545 to another host. But since we are using kubernetes this isn’t a problem because the network will be secured by other methods.
Be carefull to connect to the testnet by using --chain goerli
. If you want to connect to the mainnet later just remove those lines.
Make sure everything went well by listing your active pods.
kubectl get pod | grep openethereum
This will return a pod name hopefully with the status Running. Now we have to wait for the synchronization. This will take a couple of hours, maybe days. You can check the current state or errors by examining the log.
kubectl logs <YOUR_POD_NAME>
As long as it says something like syncing we have to wait. It will show you the current block it is working on. You can estimate the remaining time by checking out the latest block number on Etherscan for görli, your current block and the blocks processed every second by your client.
After syncing is done the processor activity of your machine should drop to normal. Remember; we don’t want to mine ETH, we just need to examine the Eth1 chain for our validator. One of the big advantages of proof-of-stake.
During the first sync openethereum warps with snapshots to the nearest block that’s a divider by 5000 and starts the normal syncing process. In the background its loads the missed acient blocks. Unfortunatelly we can’t start the beacon chain until everything is fully synced because the beacon node needs to validate all genesis validators for the genesis block. The first validators where created about a month before the genesis block. So all blocks starting from November 2020 are needed.
Installing the beacon node
The next thing would be the beacon node. The beacon node is similar to the Ethereum 1 client. It also examines the blockchain. In this case the beacon chain. Because the old Ethereum chain is still a part of Ethereum 2 the beacon node has also to validate the old chain. This is done via a RPC connection to the Ethereum 1 client. To create a deployment for the beacon node we create a file called prysmbeacon.yaml
.
apiVersion: apps/v1 kind: Deployment metadata: name: prysmbeacon-deployment labels: app: prysmbeacon spec: selector: matchLabels: app: prysmbeacon strategy: type: Recreate replicas: 1 template: metadata: labels: app: prysmbeacon spec: containers: - name: prysmbeacon image: gcr.io/prysmaticlabs/prysm/beacon-chain:stable volumeMounts: - name: prysmbeacon mountPath: /data ports: - containerPort: 12000 protocol: UDP - containerPort: 13000 protocol: TCP - containerPort: 4000 protocol: TCP args: - --accept-terms-of-use - --datadir=/data - --rpc-host=0.0.0.0 - --monitoring-host=0.0.0.0 #- --p2p-host-ip=127.0.0.1 - --http-web3provider=http://openethereum-service:8545 - --pyrmont volumes: - name: prysmbeacon hostPath: path: /home/<YOUR_HOME_FOLDER>/.local/share/k8s/eth/testnet/prysm/beacon --- apiVersion: v1 kind: Service metadata: name: prysmbeacon-service spec: #type: NodePort selector: app: prysmbeacon ports: - protocol: TCP name: beacon-tcp port: 4000 targetPort: 4000 - protocol: TCP name: beacon2-tcp port: 13000 targetPort: 13000 #nodePort: 13000 - protocol: TCP name: beacon-udp port: 12000 targetPort: 12000 #nodePort: 12000
Deploy the beacon node the same way we did with the Ethereum 1 client. Don’t forget to replace <YOUR_HOME_FOLDER>
with your actual home folder.
kubectl apply -f prysmvalidator.yaml
The interesting arguments are --http-web3provider=http://openethereum-service:8545
which is the connection to the Ethereum 1 chain and --pyrmont
which ensures you are working on the testnet. --accept-terms-of-use
is necessary because you don’t have an interactive screen.
Create an Ethereum 2 validator key
Go to the Pyrmont Launch Pad and follow each step. Select openethereum and Prysm as clients. After everything is done you should have uploaded your deposit-data to the website, send 32 Ether to the burning contract and have received a validator keyfile.
Import the validator keystore
With the next step we will import the validator key into our validator directory. Don’t even think about using the docker client for this task. The cli is much faster and easier to use.
First we have to download it.
curl https://raw.githubusercontent.com/prysmaticlabs/prysm/master/prysm.sh --output prysm.sh && chmod +x prysm.sh
After that we can use it to import our validator key.
./prysm.sh validator accounts import --keys-dir=<VALIDATOR_KEY_FOLDER> --pyrmont
We have to enter a new password for the wallet and also the one you used for generating the validator key. Copy the keystore into your share folder.
~/.local/share/k8s/prysm/validator/wallet/direct/accounts/all-accounts.keystore.json
We will import the keystore later within the validator. To keep your password save we create a kubernetes secret. Therefore we have to encrypt it as base64.
echo -n "my wallet password" | base64
The base64 encryption is not used for security but to avoid problems with the encoding of special characters when importing it into the kubernetes secret.
apiVersion: v1 kind: Secret metadata: name: prysmvalidator-wallet-secret data: wallet-password: <REPLACE_ME>
Replace the secret with your base64 string and save the code listing as prysmvalidator-wallet-secret.yaml
. Execute it with kubectl.
kubectl apply -f prysmvalidator-wallet-secret.yaml
You can delete the file afterwards to keep your password save.
Now we have to wait. The validator deposit will take roughly 15 hours. We also have to wait for the beacon chain to finish syncing bevor we can start the validator.
Installing the validator
The last part we need to do is to install the validator. First we create a new file location. Ensure you have the following path ready.
mkdir ~/.local/share/k8s/eth/testnet/prysm/validator/db
apiVersion: apps/v1 kind: Deployment metadata: name: prysmvalidator-deployment labels: app: prysmvalidator spec: selector: matchLabels: app: prysmvalidator strategy: type: Recreate replicas: 1 template: metadata: labels: app: prysmvalidator spec: containers: - name: prysmvalidator image: gcr.io/prysmaticlabs/prysm/validator:stable volumeMounts: - name: prysmvalidator mountPath: /data - name: prysmvalidator-wallet-secret mountPath: /var/wallet-secret readOnly: true args: - --accept-terms-of-use - --beacon-rpc-provider=prysmbeacon-service:4000 - --wallet-dir=/data/wallet - --datadir=/data/db - --wallet-password-file=/var/wallet-secret/wallet-password - --pyrmont volumes: - name: prysmvalidator hostPath: path: /home/<YOUR_HOME_FOLDER>/.local/share/k8s/prysm/validator - name: prysmvalidator-wallet-secret secret: secretName: prysmvalidator-wallet-secret
Again, we have to replace <YOUR_HOME_FOLDER>
. Everything else should be easy to unserstand except for the secret. We load the secret as volume prysmvalidator-wallet-secret
into the path /var/wallet-secret
which creates a password file. With the parameter --wallet-password-file
we tell the validator to read that file as keystore password.
To see if everything is working you should check out the logs of your pod. You can also Monitor your validator at sites like beaconcha.in. Look for your validator public key in the output and search for it on the validator overview.
kubectl logs prysmvalidator-deployment-<YOUR_POD_ID> | grep pubKey
As soon as the validator is approved it should start generating Ether.
Open discovery ports
This step is not necessary but will help other clients to find you faster, by letting them establish a connection to your host. You may have noticed, that I commented out some nodePorts for the services. You can uncomment them and make them available outside of the kubernetes virtual network. Keep in mind that your node may not have all ports available. In this case simply use other ports. You can find the necessary parameters in the documentation from openethereum or prysm. Don’t route the RPC Ports to the outside world! This could be dangerous. Only route the discovery ports. If you use a router you may also have to forward those ports to your machine.
Because our pods run within a virtual network they don’t know your real ip address. You have to set it manually. You can do this for the openethereum client with the parameter --nat
. The beacon chain client can be configured with --p2p-host-ip
. Here you can also use --p2p-host-dns
if you use a dynamic dns service because of changing ip addresses.
Safe disc space
If you want to safe some disc space you can disable the syncing process of the old blocks for the beacon chain with --disable-sync
. With this parameter the beacon chain skip the initial sync and start normal syncing right away. Make sure to do your deposit after that.
Unfortunately the ethereum 1 chain needs a lot of space. You need at least a 1TB SSD drive to be able to create a Eth1 client supported beacon chain. Because the beacon chain needs some ancient blocks for the genesis validation it is not possible to deactivate the genesis blocks on openethereum.
One trick would be to do the initial sync with an public chain provider like Alchemy . They offer free quotas that should be enough for the initial syncing. After that you could return to your own openethereum client with no ancient blocks. To do so you have to add the parameter --no-ancient-blocks
. But be warned. This is absolutely not recommended.