How to make your project workflow effortless with Docker (Part 3)

on

Part 1: Technologies that we will use and why

Part 2: Getting your local development environment started with Vagrant and Ansible

Part 3: Getting started with Docker and Jenkins

Part 4: Using Docker in AWS through Ansible

Hi amigos. Welcome back! I hope your Vagrant machine is working correctly and is up and running along with your Jenkins server. If not go to the previous tutorial and come back when you have that going on. So ssh into your vagrant machine with vagrant ssh and lets start working.

In this tutorial we will go through the basics of Docker by building a container from scratch, then we will make a Dockerfile to build our Node app and see it working in our local environment. After that we will configure Jenkins to follow the steps we just made to automatize the process. Remember if you want me to dive more into an specific thing in the tutorial leave it in the comments and I can update it.

Before we start head over to Dockerhub and make a new account. And change the ouruser name in all the steps to your own Dockerhub username.

A pretty shiny new thing called Docker

Docker has been receiving a lot of attention this past months. And it is because it pretty much rocks. Although Docker is not something terribly new and some may say it has its flaws. It is still a pretty interesting project that streamlined some pretty complex things.

So before we start, Docker has official tutorials (which are awesome) that you should definitely check out. I’ll touch base on some of the concepts from this tutorials but not all of them. This series of tutorials are just introductory. So for now, and if you want, take a look at this tutorials from Docker:

Ok lets start.

Docker “brain dead” basics

So first we have docker ps. This will let you see the docker container that are currently running. If you add the flag -a you will see all the docker containers that ran and then stopped. For now run sudo docker ps and you should see the Jenkins container.

docker ps

I decided not to give sudo permission to Docker. But if you want to do so you can run sudo usermod -a -G docker vagrant Then ssh out and in again.

Now lets run our first Docker container. We will run a Dockerized Ubuntu that will remove itself when it finishes running that will echo “Hello World”.
So we run sudo docker run busybox /bin/echo ‘Hello world’. It will download the latest busybox docker image and run “Hello World”.

docker run 1

Now if we want to go inside the Docker container and use an interactive shell we will use the flags -it. An example would be sudo docker run -it busybox. You can exit by typing exit.

docker run -it

But if you run sudo docker ps you will see that only the Jenkins container is running and that is because the containers stopped after the process exits. You can verify by running sudo docker ps -a. If we wanted to make a container like a daemon a continue running we will add the flag -d.

For example we can make a container that echos “Hello World” like this sudo docker run -d busybox /bin/sh -c “while true; do echo hello world; sleep 1; done”. Then we run sudo docker ps to verify it is running and see the name of the container (In my case it is evil_archimedes, you can also use the Docker ID. So we can use sudo docker logs evil_archimedes and see it is printing “Hello World” to oblivion.

docker -d

And to stop the container we would run sudo docker stop evil_archimedes. And to remove it completely use sudo docker rm evil_archimedes.

docker stop

Making a Docker image for ourselves

Now that we have a basic understanding of Docker commands lets start working with Docker images. First we will make a Docker image using commit and then using a Dockerfile.

For now lets run an official Node container, install nodemon and commit the changes. So lets make our new container with the following command sudo docker -it --name nodemon node:0.10 /bin/bash, this will make the container and in an interactive shell and run /bin/bash. The inside the container we just install nodemon npm install nodemon. After it finishes we exit the container. We learned a new flag here --name lets us specify the name of our docker container.

docker node 1

Then we commit the changes with sudo docker commit -m “Added nodemon” -a “Roberto Amador” nodemon ouruser/nodemon:v1

Then we can commit the changes using this command sudo docker commit -m “Added nodemon” -a “Roberto Amador” nodemon ouruser/nodemon:v1. Just like git -m lets us specify the commit message and -a the author of the commit. Then we tell to commit the changes made to nodemon as ouruser/nodemon:v1. Where ouruser/nodemon is the name of the new image and v1 is the tag. Then we can check our new image with the command sudo docker images.

docker node 2

Now we can use the new docker image as we please. Run this command to try it out sudo docker run -it ouruser/nodemon:v1 /bin/bash.

docker node 3

Building an image from a Dockerfile

Using the docker commit command is a pretty simple way of extending an image but it’s a bit cumbersome and it’s not easy to share a development process for images amongst a team. Instead we can use a new command, docker build, to build new images from scratch.

To do this we create a Dockerfile that contains a set of instructions that tell Docker how to build our image. Here is my Dockerfile for the Node app.


FROM node:0.10
MAINTAINER Roberto Amador

RUN mkdir -p /app
WORKDIR /app

ADD . /app

EXPOSE 3000

CMD ["node", "app.js"]

FROM node:0.10 We specify from which docker image we want to work. In this case the official Node image.
RUN mkdir -p /app We will run this command inside the container. The command makes a new folder where our files will reside.
WORKDIR /app We will now make the default work directory the folder we just created. So every time we run something in this container it will run inside this folder by default.
ADD . /app ADD will copy the files we have on our local machine to the docker container.
EXPOSE 3000 Then we expose the port 3000 for Express to work
CMD [“node”, “app.js”] Then the default run command will be running node with our app as an argument.

And so, to build the first image with a Dockerfile we will run sudo docker build -t ouruser/hello-node:v0 /vagrant/app/

docker build

Cool!! We just built our first Node app image. Now we can run sudo docker run -d -p 3000:3000 ouruser/hello-node:v0 and a new container will start.

docker hellonode 1

And we can see the result if we go to http://localhost:3000/

docker hellonode 2

You can even try out and test the code in a new container and just delete it afterwards!! Just run sudo docker run -rm ouruser/hello-node:v0 ./node_modules/mocha/bin/mocha ./test/test.js. The --rm tag tells docker to delete the container after it has done its job. Pretty nifty when you just want to run something once and not keep the baggage.

docker test

Dockerhub

And the final step is to upload the docker image to Dockerhub. Our online Docker image registry. Before we start make sure your account is active and log in into Dockerhub.

When that is done go to your terminal and inside your Vagrant machine log in to Dockerhub with sudo docker login and follow the instructions

docker login

So now lets verify by uploading our node app image to Dockerhub. To do this run sudo docker push ouruser/hello-node:v0

docker push

And now we can go check our profile in Dockerhub and see the new Docker container repo.

dockerhub repo

dockerhub tag 0

Jenkins and Docker

Now that we have the basics out of the way we pretty much are going to tell Jenkins to do the last steps everytime we want. Pretty straight forward. So if you go to http://localhost:8080/ you will go to the Jenkins server. Inside that server there is a Docker job already configured.

jenkins

First lets see the configuration so it can use our Vagrant machine as our local node. Go to Manage Jenkins>Manage Nodes>Wrench in Local

jenkins vagrant

The user and password for Vagrant is “vagrant”.

Now that the Node is configured go back to the main page and lets see what the Docker job does. Click Docker>Configure. There you will see a lot of configuration options. For the sake of simplicity I left it so that everytime that you want to build a new version you will have to click the Build Now button.

Go to the bottom of the page and look for the Build tab. In the first box you will see the same steps we just made minutes ago. We are only adding the $BUILD_NUMBER which is a Jenkins variable that keeps tabs on the latest build.

The second box will be covered more in detail on the next tutorial. It runs the Ansible playbook that will run the latest version of our container in an Amazon instance.

But anyway lets test it! Go back to the Docker job main page and click Build Now.

jenkins docker build 1

Now you will see that a new build has started. Build number 1. Click it and then go to the console output. It will do all the steps we just mentioned and it will fail at the Amazon step.

jenkins docker build 2

But the new Docker version will still be pushed to Dockerhub. It just created a new build, ran our tests on the new container and pushed it to the internet with just one button!! And the coolest thing is that you can make Jenkins start the build when, for example, you commit a change to the master branch in your git repository.

dockerhub tag 01

And that is all for now. Please look forward for the next and final part were we will make Ansible download and run our latest container in an Amazon instance!! See ya next time. If you have any suggestions or questions about this tutorial please leave it in the comments.