Working With Containers
This lesson focuses on how to get things done using two important parts of the Docker toolchain, Docker Compose and Docker Machine.
Before we start, please be sure that Docker Compose and Docker Machine are installed. The steps required for installation will not be covered in this lesson.
First, we will introduce you to Docker Compose for modeling multi-container applications through configuration files.
Hands-on practice will demonstrate Docker Compose’s functionality via a microsurface application.
You will learn how to build the YAML file required by our application, use Redis to start the server, and, finally, launch the application.
We will then focus on Docker Machine, which is a useful utility for bootstrapping Docker hosts using local virtual machines or Cloud providers.
We will create a demo machine on EC2 to get a feel for Docker Machine.
Finally, we will deploy our application to a Docker host.
Hello and welcome back to the Introduction to Docker Course from Cloud Academy. I'm Adam Hawkins, and I'll be your instructor for this lesson.
This lesson focuses on how to get things done using two important parts of the Docker toolchain, Docker Compose and Docker Machine. We'll move quickly to reach our objective of deploying a microservices application. This is not gonna be a rock solid production deployment. Instead, it will give you a taste of what may be accomplished using the Docker toolchain. This lesson requires that Docker Compose and Docker Machine are installed. I'm skipping the installation steps because I know that you can handle that. So friends, buckle up. This baby is about to hit 88 miles per hour.
Docker Compose is a tool to model a multi-container application through a simple configuration file. Docker Compose is touted as a tool for development and for production. In theory, you can write the Docker Compose with that YAML file for development, and then tell Compose to deploy to any Docker Host, be it staging, production, or whatever other environments you may have. Docker Compose plays a large role in the wider ecosystem. It's usually the first tool in the toolchain used after Docker.
Let's dive into Docker Compose and see how it all shakes out. Our test bit application has three containers. A server container accepts an HTTP Pulse request to increment a counter. It also accepts an HTTP GET request to retrieve the counter. The server stores the counter in Redis. One container is continually making random Pulse requests to increment the counter. The third container loops and continually prints the counter to standard out. The server is written in Node.js and the other two are simple Bash scripts making cURLs to the server. Each container also has its own Docker file.
We could do the directory layout here. The functionality is not sophisticated, but does give us enough components to create a microsurface application and demonstrate Docker Compose's functionalities. Docker Compose manages things called services. Each service is an individual container. Docker Compose allows you to set most options in the config file that you could set via docker run.
Let's build up the docker-compose YAML file on the order our application requires. First, set the config file version. Version two is the current and expected version. Now we go about declaring all the services. First up, Redis. This container uses a pre-built image. Declare that here. We'll also use a latest tag. Docker Compose services can either use pre-built image or be configured to build an image. Now tell Docker Compose to build a server from everything in the server directory.
Next, tell Docker Compose that this service depends on another service. The server cannot start without Redis. Add Redis to the depends_on key. Docker Compose uses information to start the application in the appropriate order. We must also tell the server container where to find Redis. This is done via an environment variable. Add the REDIS_URL to the server environment variables. We can use service names as host names just as we saw in the previous lessons. Now for the counter and puller containers. These follow the same structure with different dependencies and environment variables. These services depend on the server and require the SERVER_URL environment variable.
That was pretty straightforward. We finished building up the config file for a three container application. Let's take the application for a spin. Docker Compose commands closely mirror their Docker counterparts. Compose also adds its own commands on top of the existing Docker commands. The key difference is Docker Compose commands operate on one or more services, so we can tell Docker Compose to build the entire application with a single command. I've already built the application so everything is already cached. We can also use Docker Compose to build a single service, or even multiple. You get the idea.
Tell Compose to boot the application. This happens to be the up-sub command. Running docker-compose up without any arguments will start all the containers in the foreground in their declared dependency order and stream logs standard out. Fire this baby up. Now the application is running in the foreground. Let's exit this so we can move on to something else.
Now let's start the application in the background. Docker-compose up also accepts the -d flag. Use docker-compose ps to get information on all services. Get a log snapshot with docker-compose logs. Also stream logs with the -f flag. You can follow individual services as well, or even multiple. Again, you get the idea. So how do we handle changes to the files? Let's demonstrate by making the server print debug output on every request. Use the up command with a --build option. Docker Compose will build all images and restart the applicable containers. Compose is smart enough to know what needs to be restarted. Here we see our debugging output. That was a brief introduction to Docker Compose. There's much more you can do with Docker Compose, but that's all we can cover in this lesson.
Now let's turn our attention to Docker Machine. Docker Machine is another common part of the Docker toolchain. Docker Machine is a useful utility for bootstrapping Docker hosts using local virtual machines or various Cloud providers. Let's create a demo machine on Amazon EC2 to get a feel for Docker Machine. You'll need your AWS key set as environment variables for this demo. The docker-machine create command launches new Docker hosts. Here we set the driver option to EC2 and give our machine a name. Launching the EC2 instance will take a minute or two, and then Docker Machine will take over the rest. We can check the Docker Machines we have access to with docker-machine ls. Our machine is up and running.
Now I need to point our Docker client to this Docker host. Docker Machine includes an env command. It outputs a shell script for changing the Docker environment variables. Let's run it and see what's going on. See that it exports all environment variables required to talk to this Docker host. The output also includes instructions for how to configure the shell. Let's do that. Now all Docker commands run like normal.
Let's check docker ps. Nothing is running. Let's change that by starting our Docker Compose application. First we'll expose port 80 on the server so we can curl the container. The syntax is similar to docker runs -p option. Here we map host port 80 to container port 80. Start the application like last time. This is the first time using these images on this Docker host, so they must be pulled. Check docker-compose ps. Now I can see everything is up. Find the IP of this Docker host using docker-machine ip. We can use the IP to curl the server container. Well look at that: 363 counters. You may be thinking, "So what?" about these counters, but what did we just do?
Let's recap. First, we built a micro-services application with Docker Compose. Then we used Docker Machine to create a Docker host in a public Cloud. Then we deployed our application to the new Docker host. We did all of this is what? Just under 10 minutes? I gotta say, that's really impressive. This stuff is so powerful. We've only scratched the surface on these tools. I wish I had more time in this course to show you things like Docker Compose overrides, building deploy clients at Docker Machine, or even creating Dockers from clusters. There's just not enough time.
So this is where I leave you in this lesson. The next lesson covers some important points that didn't make it into this course. It also gives you jumping-off points to continue your Docker journey.
Adam is backend/service engineer turned deployment and infrastructure engineer. His passion is building rock solid services and equally powerful deployment pipelines. He has been working with Docker for years and leads the SRE team at Saltside. Outside of work he's a traveller, beach bum, and trance addict.