Running Truffle in a Docker container
Introduction
In my earlier post about creating a decentralized application using Truffle and Metamask I used my own machine to install all dependencies and start developing. Since I am switching to Docker for most of my projects because of the flexibility, reproducability and safety of containerized environments, I also convert the Truffle project to Docker. As an inspiration I studied the example made by Douglas von Kohorn.
Docker
Docker installation
Make sure Docker and docker-compose are installed on the machine.
jitsejan@ssdnodes-jj-kvm:~$ sudo apt-get install docker.io docker-compose
jitsejan@ssdnodes-jj-kvm:~$ docker -v
Docker version 1.12.6, build 78d1802
jitsejan@ssdnodes-jj-kvm:~$ docker-compose -v
docker-compose version 1.8.0, build unknown
TestRPC image
First of all, we need to setup a test network to play around with the Truffle app that we are going to create. Since we do not want to use the official Ethereum blockchain network, we use testrpc
as created by Tim Coulter. See Github for the official repository.
jitsejan@ssdnodes-jj-kvm:~/docker/testrpc$ nano Dockerfile
The Dockerfile contains the few steps that are needed to create the TestRPC image. It will retrieve the Linux distro with node installed, install the node module ethereumjs-testrpc
and open up port 8545, which is the default port for testrpc.
# Node image
FROM node:latest
# Maintainer
MAINTAINER Jitse-Jan van Waterschoot <[email protected]>
# Install the packages
RUN npm install -g --save ethereumjs-testrpc
# Expose port
EXPOSE 8545
# Start TestRPC
ENTRYPOINT ["testrpc"]
From the Dockerfile we will create the image on the local machine and push it to Docker.io.
jitsejan@ssdnodes-jj-kvm:~/docker/testrpc$ docker build . -t jitsejan/testrpc
jitsejan@ssdnodes-jj-kvm:~/docker/testrpc$ docker login
Login with your Docker ID to push and pull images from Docker Hub. If you dont have a Docker ID, head over to https://hub.docker.com to create one.
Username (jitsejan):
Password:
Login Succeeded
jitsejan@ssdnodes-jj-kvm:~/docker/testrpc$ docker push jitsejan/testrpc
jitsejan@ssdnodes-jj-kvm:~/docker/testrpc$ docker images | grep testrpc
jitsejan/testrpc latest a5cae5578720 10 minutes ago 716 MB
The image can now be found at hub.docker.com.
Truffle image
Next we need to create the environment where we can develop our Truffle application. We will use again a Dockerfile, but in this case we will install truffle
.
# Node image
FROM node:latest
# Maintainer
MAINTAINER Jitse-Jan van Waterschoot <[email protected]>
# Create code directory
RUN mkdir /code
# Set working directory
WORKDIR /code
# Install Truffle
RUN npm install -g truffle
We can build and push the image again, finally the image will be available locally to be used by Docker. Again I use the docker push to add the image to my hub.docker.com.
jitsejan@ssdnodes-jj-kvm:~/docker/truffle-application$ docker images | grep truffle-application
jitsejan/truffle-application latest 1987e7928f6b 58 minutes ago 693.3 MB
Docker compose
The docker-compose.yml
contains the information to start the two images, map the ports and start the testrpc network. It will try to retrieve the two Docker images locally and otherwise retrieve them from hub.docker.com. The testrpc is started with host 0.0.0.0 in order for the truffle3 container to access the network.
version: '2'
services:
testrpc:
image: jitsejan/testrpc
command: bash -c "testrpc -h 0.0.0.0"
ports:
- "7000:8545"
truffle3:
image: jitsejan/truffle-application
command: bash
stdin_open: true
tty: true
ports:
- "7001:8080"
volumes:
- ./:/code
Docker start
Once the docker-compose.yml is in place, we can start the testrpc and truffle3 container by running the following command:
jitsejan@ssdnodes-jj-kvm:~/docker/truffle-application$ docker-compose -f docker-compose.yml up -d
and verify if both containers are running:
jitsejan@ssdnodes-jj-kvm:~/docker/truffle-application$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
cda7d1c5c2a0 jitsejan/truffle-application "bash" 6 minutes ago Up About a minute 0.0.0.0:7001->8080/tcp truffleapplication_truffle3_1
36f0bf0d4d25 jitsejan/testrpc "testrpc bash -c 'tes" 9 minutes ago Up About a minute 0.0.0.0:7000->8545/tcp truffleapplication_testrpc_1
Creating the app
Now we connect to the Truffle machine and create the application.
Versions of the tools
jitsejan@ssdnodes-jj-kvm:~/docker/truffle-application$ docker attach truffleapplication_truffle3_1
root@cda7d1c5c2a0:/code# npm -v
5.3.0
root@cda7d1c5c2a0:/code# node -v
v8.4.0
root@cda7d1c5c2a0:/code# truffle version
Truffle v3.4.11 (core: 3.4.11)
Solidity v0.4.15 (solc-js)
Initialize a Truffle application
Use the unbox function of Truffle to create a sample webpack application.
root@cda7d1c5c2a0:/code# truffle unbox webpack
root@cda7d1c5c2a0:/code# ls
Dockerfile box-img-lg.png contracts migrations package-lock.json test truffle.js
app box-img-sm.png docker-compose.yml node_modules package.json tmp-276YgmHxiGG8WL webpack.config.js
Compile the contracts and migrate them to the network.
root@cda7d1c5c2a0:/code# truffle compile
Compiling ./contracts/ConvertLib.sol...
Compiling ./contracts/MetaCoin.sol...
Compiling ./contracts/Migrations.sol...
Writing artifacts to ./build/contracts
root@cda7d1c5c2a0:/code# truffle migrate
Could not connect to your Ethereum client. Please check that your Ethereum client:
- is running
- is accepting RPC connections (i.e., "--rpc" option is used in geth)
- is accessible over the network
- is properly configured in your Truffle configuration file (truffle.js)
In order to be able to migrate, we need to modify truffle.js
. It is easier to change this is on the host machine instead of the Docker machine, since most probably there is a text editor available.
jitsejan@ssdnodes-jj-kvm:~/docker/truffle-application$ ls
app box-img-sm.png contracts Dockerfile node_modules package-lock.json tmp-276YgmHxiGG8WL webpack.config.js
box-img-lg.png build docker-compose.yml migrations package.json test truffle.js
Lets edit the truffle.js and update the network settings.
jitsejan@ssdnodes-jj-kvm:~/docker/truffle-application$ sudo nano truffle.js
Change the host from localhost to testrpc, since that is the name we assigned in our Docker setup.
// Allows us to use ES6 in our migrations and tests.
require('babel-register')
module.exports = {
networks: {
development: {
host: 'testrpc',
port: 8545,
network_id: '*' // Match any network id
}
}
}
With the updated network settings, lets try to migrate the contracts again to the network.
root@cda7d1c5c2a0:/code# truffle migrate
Using network 'development'.
Running migration: 1_initial_migration.js
Deploying Migrations...
... 0x14946649e8489d36a12a5bbaeb61a306b6e2e52c9f33ff4d56d40f6f1472640c
Migrations: 0x32e2f019bbdea7786e9b1d1c577f14bac8693464
Saving successful migration to network...
... 0x16bfd6088ce282fa7859de9b347e3cdefc3536ad987b6c0dbe12c37dda31be57
Saving artifacts...
Running migration: 2_deploy_contracts.js
Deploying ConvertLib...
... 0xba333153ec8f1e5c0d51455d4dbf58e50e8cd20f9a1b7176dd846fce24aa336c
ConvertLib: 0x97063aa24f9913c98c76f93f64d30a2d5475a7df
Linking ConvertLib to MetaCoin
Deploying MetaCoin...
... 0xa11480d9333044537a8a0e1798f6afa33ad74a80f739982e55bf02ae6a3915f2
MetaCoin: 0x749764ff660f2572405f3a1674488077666b866a
Saving successful migration to network...
... 0x6d837a4071eb419f31fe055f0278889382af9fc010552ced0d8060f584054835
Saving artifacts...
Now we need to build and deploy the application.
root@cda7d1c5c2a0:/code# npm run build
root@cda7d1c5c2a0:/code# npm run dev
This will output that the application is running on localhost:8080, but visiting this link shows an error. We need to modify the settings for webpack and add the host for the development server. Change package.json
jitsejan@ssdnodes-jj-kvm:~/docker/truffle-application$ sudo nano package.json
and change the following line
"dev": "webpack-dev-server"
to
"dev": "webpack-dev-server --host 0.0.0.0"
to make the server accept external traffic. Run again
root@d546db65c693:/code# npm run dev
and now the application is served on http://0.0.0.0:8080/. Visit the IP of the machine on which you are developing on port 7001 (remember we mapped the port from 8080 to 7001 in the docker-compose file) and the MetaCoin application should be visible. However, an error pops up.
There was an error fetching your accounts.
Why? Because the Truffle app cannot connect to the Ethereum test network yet. In order for the connection to work, we need to modify the code of the app.js inside the Truffle application and change the IP for the Web3 client to the external IP, and the port to 7000 since we mapped port 8545 to 7000 in docker-compose. Open the app.js with nano
jitsejan@ssdnodes-jj-kvm:~/docker/truffle-application$ sudo nano app/javascripts/app.js
and change the line
window.web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
to
window.web3 = new Web3(new Web3.providers.HttpProvider("http://EXTERNAL_IP:7000"));
replacing the EXTERNAL_IP with the IP of the development machine where the Docker container with testrpc is running. Again run
root@d546db65c693:/code# npm run dev
and this time no error should pop up when the page is visited. If everything went find you should see the MetaCoin application with 10000 META.
Hopefully this can serve as a base to create your own Dapp using Truffle.
My final code can be found on my Github.