Training: Dockerizing your first application

April 20, 2017 - by Alexandra White

For this tutorial, I’ll be dockerizing 2048, an HTML+JS application popularized by the mobile game. I've now found the best way to occupy my time, playing 2048 while doing "research" for how to create a tutorial on Docker and Triton. And you, too, can create and procrastinate/play 2048.

Watch the webinar

This webinar will walk you step by step through deploying 2048 locally and in the cloud.

Full tutorial

Step 1: installing Docker

The most important step to be successful is to actually install Docker. The Docker website has getting started guides for Mac, Windows, and Linux.

Once you’ve installed Docker, be sure to test and ensure that Docker is properly installed on your laptop, open your command line terminal. Some good commands to run are docker --version to see what version of Docker is installed as well as docker ps -a to see if there are any existing containers. If this is your first time, the answer to that question should be that there are none. We’ll use this command again later to make sure that the container we build is up and running.

Once you’ve confirmed Docker daemon is running properly, it’s time to see what Virtual Machines (VMs) are available. If you have a newer Mac then you can just use Docker for Mac (which came out of beta in the last couple of weeks) and ignore anything here about Docker Machine. If you’re running an older version of Windows or MacOS, the Docker Toolbox comes pre-installed with Docker Machine which lets you create your own virtual servers on your computer, cloud providers, or your data center.

Docker Machine will create the server, install Docker on it, and configure the Docker client to talk with it. If you’re running Linux, refer to the Linux distribution instructions when installing Docker Engine. Full disclosure: this article is geared towards those running Windows or MacOS.

If using Docker Machine, you can see what machines have been created by running docker-machine ls. default will be created by default. To see all of the commands available for docker-machine, run:

docker-machine help

You will encounter a list of commands including start, stop, status, and ip. More about the use cases for some of these commands and your Docker Machine VM later.

Step 2: dockerize our application

We’ll be working to dockerize the 2048, which was created as a web application. First thing’s first: download a copy of this repository or fork it via git. To dockerize this application, all we need to do is create a Dockerfile within the directory containing the application.

In your terminal cd into the directory where you’ve installed the application. To create your Dockerfile, run the following command:

touch Dockerfile

The command ‘touch’ creates an empty Dockerfile which you can now open in your text editor of choice (mine is Sublime Text) or your favorite Unix editor (such as vi or pico). In our Dockerfile we are going to lay out a specific set of instructions for how to build our container which will have all of the resources needed to run our application.

It’s important to choose a base image to pull in order for Docker to know where to start. This base image can be FROM scratch, which is Docker’s minimal image that indicates the build process will begin at the next command in the Dockerfile. We’ll be using Nginx as our base image, so that our application has a web server to run on. Lucky for us, there is an official Nginx repository that already exists. Running Nginx is going to be our first task within the Dockerfile:

#FROM is the base image for which we will run our application
FROM nginx:latest

Note: Docker uses the # as an indicator a line is a comment and not actionable code. I’ll be using it quite a bit, as I believe it’s important to comment your work to understand it in the future.

By adding the tag :latest to the image, we’ll ensure we have the most up-to-date version of Nginx running, and there will be no need in the future to update a version number. That said, if future versions of Nginx could break your application, you will want to indicate a specific version number. That will ensure your application builds with the same base image version each time, but it means you'll have to update the tag to get a new version of Nginx.

Our next step will be to copy the contents of our application into the Nginx environment. There are numerous files and folders within the 2048, and it’s important that they are copied into the appropriate folders.

COPY syntax is as follows:

COPY <src directory or file> <destination directory or file>

For copying this application, add the following to your Dockerfile:

# Copy files and directories from the application
COPY index.html /usr/share/nginx/html
COPY favicon.ico /usr/share/nginx/html
COPY Rakefile /usr/share/nginx/html
COPY style/ /usr/share/nginx/html/style/
COPY meta/ /usr/share/nginx/html/meta/
COPY js/ /usr/share/nginx/html/js/

Finally, we’ll be telling Docker what port to use for our application. Insert this final line into your Dockerfile:

# Tell Docker we are going to use this port
EXPOSE 80

Congratulations. You have a complete Dockerfile which looks like this:

#FROM is the base image for which we will run our application
FROM nginx:latest

# Copy files and directories from the application
COPY index.html /usr/share/nginx/html
COPY favicon.ico /usr/share/nginx/html
COPY Rakefile /usr/share/nginx/html
COPY style/ /usr/share/nginx/html/style/
COPY meta/ /usr/share/nginx/html/meta/
COPY js/ /usr/share/nginx/html/js/

# Tell Docker we are going to use this port
EXPOSE 80

Adding a Dockerfile is not enough to build and run the application, it's just the instructions for how to build it. You’ll need to create the Docker image with docker build first and then use docker run to start the container.

Step 3: VMs and running your application

For Mac and PC users, when you installed Docker, you'll automatically have set up a virtual machine.

Set Up a Virtual Machine

Let’s list our machines again to make sure that they exist by running docker-machine ls:

NAME      ACTIVE   DRIVER       STATE     URL                         SWARM   DOCKER    ERRORS
default   *        virtualbox   Running   tcp://192.168.99.100:2376           v1.12.0  

If the state of your machine is stopped, you must get it up and running before you continue:

docker-machine start default

You’ll need to connect your shell to the VM environment so that you’ll be able to run other Docker commands and set up your application. This is done by running the following command in your terminal:

eval $(docker-machine env default)

Extra: Technically, the above command opens a sub-shell to run docker-machine env default, then the output of that command is run in your main shell using eval. You can see all the environment variables that were set by running the command this way:

docker-machine env default

Your Docker host VM is now up and running and you’re ready to build your application.

Building and running your application

Make sure you’re in the directory of your application (and if not,cd back into it) and run the following command:

docker build -t twentyfortyeight .

Eventually, you'll want to upload that image to Docker Hub, so you'll need to tag the image with your Docker Hub username:

docker tag twentyfortyeight <username>/twentyfortyeight:<tag>

Replace <username> with your Docker Hub username.

This command will build a container called twentyfortyeight. You can see a list of your running containers again with docker ps. It should look like this:

CONTAINER ID        IMAGE                 COMMAND              CREATED             STATUS              PORTS                NAMES
70d80bfbd593        twentyfortyeight      "httpd-foreground"   6 hours ago         Up 6 hours          0.0.0.0:80->80/tcp   adoring_dijkstra

You’ve successfully created a container, so it’s time to run your application. Enter the following command into your terminal:

docker run -d -p 80:80 twentyfortyeight

Let’s break down this command. docker run is going to tell your application to start running. -d detaches from the container, meaning your application runs in the background rather than the foreground. p 80:80 tells the app to run on port 80. twentyfortyeight is the name of the container that you’re choosing to run (the same one you just built).

It’s possible that you’ll receive an error related to your choice of port. If that happens, another application or container is using that port. You have two choices: stop the other application running on port 80 to replace it with 2048 or use a different port. To stop the other application, run docker kill $(docker ps -a -q) to kill all running containers. Go back and docker run 2048 again and you should be all set.

If you don’t want to do kill the other containers, run the following command:

docker run -d -p 80 twentyfortyeight

Instead of assigning twentyfortyeight to port 80 specifically, a randomized port will be generated.

Your application is now running! But how do you see it in action? You’ll need the IP address of your VM. You can get this information by running the following command:

docker-machine ip default

This will give you the IP address of the VM default on your localhost. If you’re on Linux and therefore aren’t using Docker machine, you can get the IP address by running ip addr. If you’re not on port 80, you’ll need to refer to the specific port at the end of your IP address, starting with a colon.

Copy the string you receive after running the command and paste it into your browser of choice. When you load the IP address, you’ll know you were successful when you see the following:

The 2048 game.

Let’s make sure everything works. Use your arrow keys to move the numbers around the board and up your score.

Congratulations. You have successfully dockerized your first app and run it on your local machine.

Step 4: pushing to Docker Hub

Now that you’ve dockerized your app, let’s push this image to Docker Hub so that other users can download it play the game on their local machines. The official documentation is on the Docker website, but we’ll walk through the basics of getting this image up. If you haven’t already, you’ll need to create a Docker Hub account and login in your terminal:

docker login

Enter the username and password you’ve created. If everything worked correctly you should receive a “login succeeded” message.

Create a repository of the same name as your image on Docker Hub. You’ll notice something about the set up: your username is a part of the image name, the second part of the pull request Docker argument.

The Docker pull command for your app.

You’re ready to push this app to Docker Hub. Once again, replace the string <username> with your actual username and run the following command:

docker push <username>/twentyfortyeight:<tag>

That’s it. Your image is now ready to be pulled and run by anyone. You can get my image on Docker Hub: docker pull heyawhite/twentyfortyeight.

Step 5: run your application on Triton

It’s awesome that we can play 2048 on our local machine, but it will be even better to be able to share it with the world. You can do so by deploying it on Triton.

If you haven’t already, sign up for Triton at my.joyent.com. We will need to install the Triton CLI tool, which works with the Triton Cloud or your own private data center to deploy containers to a public cloud.

Once Triton is installed, you can connect Docker to the Triton Cloud by setting up Triton environment variables:

eval "$(triton env)"

You can make sure this was successful by runing docker info, which should include your my.joyent.com username in the output. The numbers of containers and images may be different for you depending on previous work done with your Triton account.

$ docker info
Containers: 0
Running: 0
Paused: 0
Stopped: 0
Images: 10
Storage Driver: sdc
SDCAccount: <your Triton username>
Logging Driver: json-file
Kernel Version: 3.12.0-1-amd64
Operating System: SmartDataCenter

It’s time to run your container and play 2048 on Triton. To do so, enter the following command:

docker run -d -p 80 --label triton.cns.services=twentyfortyeight <username>/twentyfortyeight

Note: the --label triton.cns.services in this docker run command is optional, but we'll talk about what that does below.

With that docker run, Triton will pull your container from Docker Hub, then run it on the Triton cloud with no need to setup or manage any VMs. If you want to actually see it on the web and share it with your friends on twitter or via email, you’re going to need the IP address. We’ll be using docker inspect to return information about our container, but first we need to find our container ID.

Run docker ps to list your running containers.

CONTAINER ID  IMAGE              COMMAND            CREATED            STATUS      PORTS  NAMES
f519c5fk2128  yourusername/twentyfortyeight "httpd-foreground" About a minute ago Up 55 seconds 0.0.0.0:80->80/tcp       evil_carson

To get the IP address you can use either the container ID or the container name, which was randomly generated for you. Sidebar: someone built an application based on Docker’s naming process which will randomly generate container names.

Get the IP address associated with your container by replacing <container> with the ID or name in the following command:

triton instance ip <container>

Your console will print the IP address of your container. Open that IP address in your favorite browser and start playing 2048.

However, if you're concerned that the rush of people using your 2048 containers will require more instances for redundancy or scale, or if you're worried about having to update users or DNS settings when you replace or upgrade this very important container, Triton CNS can help.

Triton CNS is an automated DNS solution that allows you to run multiple containers with the same DNS name. Make sure you have Triton CNS enabled by running:

triton account update triton_cns_enabled=true

You can see the DNS names assigned to your containers by running:

triton instance get <container name>

You will receive a lot of information about your instance, similar to the following:

{
    "id": "f599b5fd-2134-4a68-8554-dc699d9357a9",
    "name": "adoring_dijkstra",
    "type": "smartmachine",
    "brand": "lx",
    "state": "running",
    "image": "6e9f2ba8-0ec3-3b9e-86a9-c0b84f0d042a",
    [...]
    "dns_names": [
        "9b21a36a-f0cc-4a1a-f26b-fd642fc10534.inst.d9a01feb-be7d-6a32-b58d-ec4a2bf4ba7d.us-east-2.cns.joyent.com",
        "adoring_dijkstra.inst.d9a01feb-be7d-6a32-b58d-ec4a2bf4ba7d.us-east-2.cns.joyent.com",
        "twentyfortyeight.svc.d9a01feb-be7d-6a32-b58d-ec4a2bf4ba7d.us-east-2.cns.joyent.com",
        "9b21a36a-f0cc-4a1a-f26b-fd642fc10534.inst.d9a01feb-be7d-6a32-b58d-ec4a2bf4ba7d.us-east-2.triton.zone",
        "adoring_dijkstra.inst.d9a01feb-be7d-6a32-b58d-ec4a2bf4ba7d.us-east-2.triton.zone",
        "twentyfortyeight.svc.d9a01feb-be7d-6a32-b58d-ec4a2bf4ba7d.us-east-2.triton.zone"
    ]
}

What we care about is the last section, dns_names. This will list all the DNS names assigned to the instance. Some of those DNS names point to the public IP address and some point to the private IP for my instance (we'll talk all about networking in Docker on Triton in an upcoming blog post). And some of the names point to this specific instance, while others point to all my containers that provide the same service, which I specified with the --label triton.cns.services=twentyfortyeight when I did the docker run.

The DNS name for all my containers that provide the same service looks like this:

<service name>.svc.<account uuid>.<data center name>.triton.zone

The awesome thing about Triton CNS automated DNS is that those *.svc.* names work for any number of instances of the same app, and even if I redeploy my app. Triton CNS will keep the DNS information up to date.

Copy the DNS name and paste it into your browser of choice to ensure it’s working as expected. You now have a URL which you can share with the world.

That said, it is an awfully long string. With that DNS name, you can always assign it a custom domain name, or you can create a short link with your favorite service.

If you need any further proof that Triton made this super easy to get your application running and shareable, go play 2048 at game.alexandra.space.

Resources

Looking for other simple Docker on Triton tutorials? You can go back and read my first blog post to dockerize a simple pullquote application.

If you're ready to move to the next level, check out one of these blog posts: