Build Blockchain with Docker Network (Part 2)
TL:DR Sorry, there is no such thing today pal! But well, the source code…
In the first part of this series, we already know how to make a subnetwork using Docker, where we can create any many (well, theoretically of course) nodes as we want. In this part of the series, we are going to have them connected together like a real mesh.

1) Genesis
If you have coded your own baby blockchain, you will know that for every blockchain that exists out there, there is always an initial block hard-coded into the chain, so called the genesis block. From this first-ever block, the chain can keep growing with all the minings and transactions.
For the network we are building, there must be also a genesis node as well. This genesis node will have a fixed IP address so any new-born node will come to it asking for the blockchain’s current state: the chain itself, and the addresses of other nodes the genesis node is aware of.

To make a genesis node, we simply split our current service node in docker-compose into two: the genesis & the nodes. We also assign a fixed IP address to the genesis node. This IP address of the genesis node should be somehow implanted in the app’s source code. You can implement this any way you like. Here I am using an environment variable:
version: '2.1'
services:
genesis:
build: .
image: genesis:latest
ports:
- "3000:3000"
networks:
blockchain:
ipv4_address: 172.16.238.10
node:
image: genesis:latest
ports:
- "3001-3005:3000"
environment:
GENESIS_IP: 172.16.238.10
networks:
- blockchainNote that we also have to rename the docker image to genesis since we set the genesis node to be built first.
Since the obvious order to bring whole network up is the genesis node first, then all other nodes to follow, we also consider adding “depends_on” config to node service:
node:
...
depends_on:
- genesis
networks:
- blockchainPretty legit huh? Well No!
2) Container Startup Order
Since the genesis node take some time to start, all other nodes will get pretty confused as they come to life seeing Daddy ain’t home just yet. We need to fix that, by telling the nodes to constantly check if they can already communicate with the genesis node. Docker already provides a guideline for this here.
What we need to do is to write a small loop shell script to check if the genesis IP is ready for interaction.
#!/bin/bash
# delay.sh
set -e
cmd="$@"
until curl $GENESIS_NODE; do
>&2 echo "Node is unavailable - sleeping"
sleep 5
done
>&2 echo "Node is up - executing command"
exec $cmdFor the node service, we will run this script in prior to the Command CMD defined in the Dockerfile to do the checking before actually starting the program. Also we must provide permission for the shell script so it can execute properly. In the Dockerfile, put this:
...
RUN ["chmod", "+x", "/path_to_the_workdir/delay.sh"]
CMD [....] # your actual command to start the programThat’s all for the container startup order!
3) Port Config
The last step is to config what port to expose for each container. To make it neat, let’s re-configure our app to use port 80 when it joins the Docker subnet.
To do this, in the app’s source code we specify the port it needs to utilize through an environment variable, eg PORT. This part is also up to you depending on the language, framework or library you are using.
Then we define the variable in docker-compose file:
version: '2.1'
services:
genesis:
...
expose:
- "80"
environment:
- PORT: 80
ports:
- "3000:80"
node:
...
expose:
- "80"
ports:
- "3001-3005:80"
environment:
PORT: 80
GENESIS_IP: 172.16.238.104) Final
If you have finished all the steps above, now you can start the network up and enjoy the fruit of your labor.
Hit the terminal and type in:
$ docker-compose up --scale node=4Note that we may need to add the flag --force-recreate to explicitly ask Docker to rebuild everything with the new config and modified source code.
It runs, doesn’t it? Well, congratulation!
Let’s play with it a bit. Here I request the chain and peer list from the genesis node:
{
"chain": [
{
"index": 1,
"time": 1535777828,
"proof": "vutr.io"
}
],
"nodes": [
"172.16.238.3",
"172.16.238.4",
"172.16.238.2",
"172.16.238.5"
]
}
// GET http://localhost:3000
// HTTP/1.1 200 OK
// Date: Sat, 01 Sep 2018 05:01:25 GMT
// Content-Type: application/json; charset=utf-8
// Content-Length: 129
// Server: Jetty(7.x.y-SNAPSHOT)
// Request duration: 0.177676sVoila! We have a node list with 4 IPs collected from 4 other nodes in our network.
Let’s mine a block on the node “172.16.238.2” and see what will happen. Note that this node exposes itself through port 3001.
{
"index": 2,
"time": 1535778596,
"proof": 5153
}
// GET http://localhost:3001/mine
// HTTP/1.1 201 Created
// Date: Sat, 01 Sep 2018 05:09:56 GMT
// Content-Type: application/json; charset=utf-8
// Content-Length: 42
// Server: Jetty(7.x.y-SNAPSHOT)
// Request duration: 0.289508sThat’s a new block. Did it get populated to other nodes? Let’s inspect that:
{
"chain": [
{
"index": 1,
"time": 1535777852,
"proof": "vutr.io"
},
{
"index": 2,
"time": 1535778596,
"proof": 5153
}
],
"nodes": [
"172.16.238.3",
"172.16.238.4",
"172.16.238.2",
"172.16.238.5",
"172.16.238.10"
]
}
// GET http://localhost:3002
// HTTP/1.1 200 OK
// Date: Sat, 01 Sep 2018 05:11:06 GMT
// Content-Type: application/json; charset=utf-8
// Content-Length: 188
// Server: Jetty(7.x.y-SNAPSHOT)
// Request duration: 0.030396sThere it is! Beautiful, isn’t it?
Conclusion
By the end of this simple tutorial, I hope you already have a good picture of what you can do with Docker Network. While the Toy Blockchain we have here is just a very small application that makes use of it, it is certainly entertaining enough and should provide a good start for your IPO next month — which I do hope you will (jk).
Anyway, Happy Dockerizing Everything!
