Recently I blogged about how to build a Lightning Component based Tic-tac-toe app. Last week I showed how to run that same app locally and on Heroku using Aura OSS. Now, let’s take one step further and see how to ‘dockerize’ it i.e. run that app inside a Docker container.
“Docker allows you to package an application with all of its dependencies into a standardized unit for software development” – docker.com
Essentially Docker allows us to package everything like operating system, source code, libraries, database and so on, that’s needed to run the app in the form of an “image” (think of it as one huge file). Further, it allows us to run that “image” in a container.
In addition, Docker provides APIs similar to Git and has a registry called Docker Hub similar to Github. But unlike Github, Docker Hub allows us to create, update and clone an image.
You’ll never have a situation where “it works on one machine but not on others” because the entire machine can be cloned and shared.
You can push docker images to cloud services like Heroku and know it works there as well as everything is a “clone”.
There are many more but I won’t cover them here.
Let’s understand it better by looking at how it looks and works.
What A Typical Docker Installation Looks Like
Docker can be installed on Mac, Windows or on Linux. This blog talks about Docker on Mac.
When you first install docker it installs a Virtual Machine(boot2docker VM), a docker server inside that VM. It then installs two CLIs(boot2docker CLI and docker CLI) on the mac.
Boot2docker CLI: This tool is used to manage the VM itself. For example: “boot2docker restart”, restarts the VM and “boot2docker ip” provides the “ip” of the VM.
Docker CLI: This tool talks to the docker server that’s running inside the VM. This can be used to create images, start container, stop container.
Docker server: This listens to docker CLI from within VM and creates images, containers and so on inside the VM.
Image: An Image special software that can be executed by docker. An Image usually contains some Operating system and then a bunch of software. In our example, our Tictactoe image(“rajaraodv/tictactoe”) contains: Debian OS, maven3, Java 7 and Tictactoe app.
Container: A container essentially “stripped-to-basics” version of Linux. You’ll load an image into the container and run that image. In the docker world, you need to have an image file before you can run it in a container.
Getting An Image
You can get an image from Docker Hub by simply running: docker pull <imageName>
docker pull “rajaraodv/tictactoe” //downloads tictactoe image docker pull node //downloads latest node.js image
Building An Image Via Dockerfile
Dockerfile is simply a file that allows us to create a new image on top of an existing image. It’s similar to package.json in Node.js or maven in Java.
For example, Dockerfile for our Tictactoe app looks like below:
FROM maven:3.3.3-jdk-7 // Download an image w/ jdk7 and maven 3.3.3 from Docker hub MAINTAINER Raja Rao DV "firstname.lastname@example.org" // Maintainer's info RUN mkdir --parents /usr/src/app // Create this folder ADD . /usr/src/app // Copy current folder (in mac) to /usr/src/app in the image. WORKDIR /usr/src/app // Make /usr/src/app the working directory RUN mvn clean install // Run maven clean install to install the project EXPOSE 8080 // This exposes port 8080 to the VM. CMD mvn jetty:run // Execute mvn jetty:run when we run this in a container
Then, to build the image, the command looks like below. The command tells Docker to read Dockerfile from the current directory and build an image with name(aka tag) rajaraodv/tictactoe by reading its contents.
docker build -t rajaraodv/tictactoe . //notice there is a "dot" at the end
Running The Image In A Container
Once you have the image, you can run it in a container by running: docker run <imageName>
docker run “rajaraodv/tictactoe” //Downloads (if not locally available) & runs tictactoe image docker run node //Downloads(if not locally available) and runs latest node.js image
Most images you need to run with some options like: open up ports, run a command etc. For example the below command runs rajaraodv/tictactoe image and maps 8080 port w/ name “tictactoe”
docker run --name tictactoe -p 8080:8080 rajaraodv/tictactoe
Using Docker For Development
Docker is all about preventing any changes in the environment. However development is all about constant change in source code, code compilation, browser refresh and so on. Luckily Docker provides allows us to mount a directory at runtime (once the container is running). This is huge. This means we can mount/share our app’s source code folder on mac to some folder inside the container.
In the below command, we are mapping /Users/rraodv/tictactoe/ on mac where I develop the app to /usr/src/app inside the linux container (you should map your machine’s folder).
docker run -v /Users/rraodv/tictactoe/:/usr/src/app --name tictactoe -p 8080:8080 rajaraodv/tictactoe
Remember that in the Dockerfile, we have CMD mvn jetty:run to be executed when the image is run. So when we execute the above command, we have Java, Maven, Jetty are all running in the container(dockerhost) but the source code folder is mounted/shared from Mac (localhost).
This means, we can change the source code on Mac(localhost) and refresh browser to see the changes. This also means, we can develop the Java app without installing specific version of Java, Jetty and Maven on our Mac!
Developing The TicTacToe App In Docker
Open (/src/main/webapp/index.jsp) and change the absolute path for /src/main/webapp/WEB-INF/components/ to match your local machine
This is required because of Java’s JNI bug (Nothing to do with Docker)
Without this, making change to component files (.cmp) files won’t be compiled by Java and so you won’t see any change when you refresh the browser.
docker run -v /path/to/tictactoe/:/usr/src/app –name tictactoe -p 8080:8080 rajaraodv/tictactoe
Open browser and point to <dockerhost IP>:8080
- Run boot2docker ip to get dockerhost’s IP.
From this point onwards, you can make changes to the source code and refresh browser to change.
If you have any questions, please feel free to contact me on Twitter at: @rajaraodv