I’ve written about Docker before, as I am a big fan of it. And for this post, I’m going to talk about some practical situations in which I’ve used Docker in real life, both for testing and software development!
But first, let’s recap what Docker IS and IS NOT:
- Docker containers spin up quickly (1-2 seconds or less)
- Docker containers DO have separated process tress and filesystems
- Docker containers ARE NOT virtual machines
- Docker containers ARE intended to be ephemeral. (short-lived)
- You CAN, however, mount filesystems from the host machine into Docker, so those files can live on after the container shuts down (or is killed).
- You SHOULD only run one service per Docker container.
Everybody got that? Good. Now, let’s get into some real life things I’ve used Docker for.
Experimenting in Linux
Want to test out some commands or maybe a shell script that you’re worried might be destructive? No worries, try it in a Docker container, and if you nuke the filesystem, there will be no long-term consequences.
# # Start a container with Alpine Linux # $ docker run -it alpine # # Let's do something dumb # $ rm /bin/ls $ ls -l /bin/sh: ls: not found # # Just exit the container, restart it, and our filesystem is back! # / # exit [unifi:~/tmp ] $ docker run -it alpine / # ls bin dev etc home lib media mnt proc root run sbin srv sys tmp usr var
And all of the above takes just a couple of seconds! This works with other Linux distros as well, such as CentOS and Unbuntu–just change your Docker command accordingly:
docker run -it centos docker run -it ubuntu
Yes, that means you could run CentOS in a container under Ubuntu or vice-versa. Docker doesn’t care. 🙂
Serving Static Content
Want to serve up static content for building a website? No problem–there is an nginx container that is part of Docker Hub which makes this painless:
docker run -v htdocs/:/usr/share/nginx/html -p 8080:80 -d nginx
Now, just go to http://localhost:8080/ in your web broswer and you’ll see your site’s content.
Would you like to test out a custom configuration with nginx as well? Just use -v again:
docker run \ -v htdocs/:/usr/share/nginx/html \ -v nginx.conf:/etc/nginx/nginx.conf \ -p 8080:80 -d nginx
Each time you do this, the container will spin up in 1-2 seconds at the most. This makes setup and teardown much easier, as you will not need to keep standing up VMs in Vagrant.
Complex Development Environments
If you are building a full blown web application, you’ll have multiple moving parts. Using my website SEPTA Stats as an example, it has Nginx for static assets, PHP for server side code execution, Redis for caching, and Splunk for data ingestion and report generation. By putting everything into a single Docker Compose file, I can now spin up that entire environment with a single docker-compose up -d command. That’s a huge step up from having to use Ansible to set things up in a virtual machine in the past.
When I ported SEPTA Stats over to Docker, I noticed that I was running PHP 5.6. The line in my docker-compose.yml file looked like this:
I wanted to switch to PHP 7.2 without causing excessive downtime. In the old days, that would involve standing up a new VM somewhere with PHP 7.2, deploying my code, fixing all of the bugs, deploying to my production server, and then shutting down the webserver. But with Docker, it’s so much easier. I started by replacing the above line for my PHP container with this one:
I then killed, removed, and restarted the PHP container on my Mac. I then started loading pages on http://localhost:8080/ and fixing problems in the PHP code as I found them. Once I fixed all of the problems, I committed the PHP code and my changed docker-compose.yml file and deployed them on the production server as follows:
docker-compose kill php docker-compose rm -f php git pull docker-compose up -d php
The above only took a couple of seconds. Fantastic!
Complex Development Environments, Part 2
You should be using Python 3. But lets say you’re working on a system that has Python 2 installed–it can sometimes be challenging to have two version of Python that co-exist. With Docker, you can work around that!
Let’s say you’re in the directory of your favorite Python 3 project. Go ahead and put this into a Dockerfile:
FROM alpine RUN apk add python3 COPY requirements.txt / RUN pip3 install -r /requirements.txt WORKDIR /mnt
You can now build that Docker container:
docker build . -t test-python3
Once built, the Docker container will contain a copy of Python 3 along with whatever modules your project requires. You can now spin up that container interactively and run Python code inside of it:
$ docker run -it -v $(pwd):/mnt test-python3 /mnt # ./hello.py Hello World!
Since your directory has been mounted inside of the Docker container, you can now use your favorite editor on the host machine to your Python code, and run it inside of the Docker container.
If you want the Python script to execute automatically when the container is run, that gets into ENTRYPOINT, which is beyond the scope of this particular post. 🙂
This last use case is not something I’m proud of, but is a reality in many workplaces: legacy code and environments. I once inherited a Node.js project which was written using Node.js 0.10. I had concerns about intentionally installing an old version of Node onto our production servers, as an upgrade might cause a new version of Node to be installed and break things. Docker to the rescue!
Let’s start with building a Dockerfile to install Node 0.10.0 on CentOS:
FROM centos RUN yum install -y centos-release-scl RUN yum install -y nodejs010
You can now build and run that container interactively to see that Node.js 0.10 is installed:
$ docker build -t node-0.10 . $ docker run -it node-0.10 [root@780f33eb99b1 /]# scl enable nodejs010 'bash -c "node -v"' v0.10.40
Again, this isn’t something that is always a GREAT idea, but at the very least, if you’re forced to run legacy code, you can have that code hosted on a modern server or cloud instance which is not legacy.