Lab 10 - Special Topics
Docker
This exercise is designed to give you some hands-on experience with Docker!
By the end of this assignment, you should be able to:
- Create and use a Docker container interactively
- Create a Dockerfile, which allows you to declaratively define your containers
- Dockerize and run a simple web application
Installing Docker
One of the annoying things about Docker is it’s usually not in apt
and if it is, it’s always under a different name.
Here’s a link to the .deb
for Docker Community Edition you can download and install on your VM: https://download.docker.com/linux/debian/dists/stretch/pool/stable/amd64/docker-ce_17.06.1~ce-0~debian_amd64.deb
wget https://download.docker.com/linux/debian/dists/stretch/pool/stable/amd64/docker-ce_17.06.1~ce-0~debian_amd64.deb
sudo dpkg -i docker-ce_17.06.1~ce-0~debian_amd64.deb
It’ll complain about dependencies or something, so just run the following command to wrap up
After installing, I recommend following these instructions so you can use docker as a non-root user. This means you won’t have to type sudo docker
all the time. This is optional but for the rest of this exercise I’m going to assume that you did this. You might see some weird output like
sent invalidate(passwd) request, exiting
sent invalidate(group) request, exiting
But it should work anyway.
Creating your first Docker container
To verify that you installed things correctly, try running
docker run hello-world
You should see some friendly output like so:
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
b04784fba78d: Already exists
Digest: sha256:f3b3b28a45160805bb16542c9531888519430e9e6d6ffc09d72261b0d26ff74f
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://cloud.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/engine/userguide/
Some quick definitions from Docker’s website:
An image is a lightweight, stand-alone, executable package that includes everything needed to run a piece of software,
including the code, a runtime, libraries, environment variables, and config files.
A container is a runtime instance of an image—what the image becomes in memory when actually executed.
It runs completely isolated from the host environment by default, only accessing host files and ports if configured to do so.
Be sure to read through the output from running the hello-world image to get an understanding of what the Docker daemon was doing.
Running an interactive container
We’re now going to walk you through running a container interactively. This is useful if you ever need to play around and
install stuff on a bare system without messing up your current system. Try running the following command:
docker run -it ubuntu:latest
The -i
flag tells docker to keep STDIN
open to your container, and the -t
flag allocates a pseudo-TTY for you.
Basically you need both for you to have a shell into your newly started container.
Try installing some packages from apt
or just play around. It should look like a bare Linux system.
You can exit the container with CTRL+D
.
Questions
- What user are you logged in as by default?
- If you start and then exit an interactive container, and then use the docker run -it ubuntu:latest command again,
is it the same container? How can you tell?
Dockerfiles
A more powerful way to interface with Docker is by using a Dockerfile. A Dockerfile allows you to define an image by
specifying all of the commands you would type manually to create an image. Docker can then build images from a specified
Dockerfile. These Dockerfiles can be put into version control and the images distributed as a binary to keep track of both
how the image is constructed and also to keep pre-built images around.
Dockerfiles are very powerful and have many different commands and features. We’ll go over a basic example, but you should
check out the reference page if you are trying to do anything more complex.
Here is an example Dockerfile that will build an image that has python3.6 installed.
It will also run python3.6 directly, so you’ll be at a python prompt instead of a bash prompt when you run it.
FROM ubuntu:zesty
RUN apt-get update && apt-get install -y python3.6 --no-install-recommends
CMD ["/usr/bin/python3.6", "-i"]
Note: there are some “best practices” for writing Dockerfiles that the above example doesn’t use,
because it’s a basic example. If you’re interested in this stuff, check out this article.
What is this doing? We specify a base image ubuntu:zesty
(the zesty version of ubuntu).
We then specify that we should run (RUN
) the command apt-get update
and then apt-get install python3.6
so we can
install python3.6
(note that this package probably isn’t in apt on your own VMs!).
Then we set the default command (CMD
) of the container to run the python3.6
interpreter in interactive mode.
Copy the contents of the Dockerfile above into a file named Dockerfile
.
Then use Docker to build it with the following command:
docker build -t mypython:latest .
This tells Docker to look in the current directory for a Dockerfile to build, and build it.
The -t
flag tells it to tag this build with the name mypython:latest
.
Docker will look for a Dockerfile in the current directory since you specified .
You can see all of the images you’ve built on your machine with the docker images
command.
Questions
- Run the image you just built. As before, don’t forget the
-it
flags, and you can exit using CTRL+D
. What do you observe?
- Write and build a Dockerfile that installs the packages
fortune
and fortunes-min
and runs the fortune
executable
(located in /usr/games/fortune
after you install it). Note that you won’t need to use the -it
flags when you run the container as fortune doesn’t need STDIN
.
Submit your Dockerfile with this lab. Hint: if you’re having trouble writing your Dockerfile, try booting an interactive container and installing both packages.
How can you translate what you did interactively to a Dockerfile?
- Paste the output of your
docker images
command after questions 1 and 2.
Dockerizing a Web Application
For our last trick, we’re going to take a rusty old Django web application and Dockerize it! By this, I mean that
we are going to package the application and its dependencies within a Docker image. We can then distribute this image
and/or run it as a container without changing our host system.
For simplicity I did the steps of making the Dockerfile and web application for you.
Be sure to take a look at the Dockerfile anyway, as you should be able to understand (at a high level) what each
line is doing by this point in the course. Now, it’s your job to figure out how to run it successfully on your machine.
Get started by cloning the repo: git clone https://github.com/upenu/django-polls-example
and cd
into the directory.
Build the Dockerfile into an image and tag it with mydjango:latest
. Refer to the previous examples if you don’t
know what commands to use.
Docker creates a separate network for containers, so you will need to do forward your host port to your container’s port
(this is called port forwarding, or port mapping). The container is listening on port 80, so let’s try to forward our
host machine’s port 5050 to the container’s port 80 when we run the container:
docker run -p=5050:80 mydjango:latest
Don’t expect to see any output until you visit the web application from your host machine. The -p
flag takes in a colon
separated pair of HOST_PORT:CONTAINER_PORT
(it can actually accept a ton of more options, but don’t worry about that for
now). You should be able to view the web application from your host machine by visiting <url_of_host_machine>:5050
,
assuming you don’t have anything else running on that port.
Use CTRL+C
to exit the container this time.
Cool. So we can now run this application within a container successfully. You can also try running it in detached mode
using the -d
flag:
docker run -d -p=5050:80 mydjango:latest
The web application should still be available, but the container is now running in the background. To view the status of
containers running on your system, use the docker ps
command. To stop this container, use docker stop <container_id>
.
You can find the container ID from the output of docker ps
.
Congratulations! You’ve successfully Dockerized and ran a web application with vastly different requirements and configuration than your
host machine, without affecting your setup on your machine :) There’s a lot more about Docker and containers to learn about,
but I hope this was enough to wrap your head around the basic concepts and get some experience working with it.
For further reading, I recommend just reading the official documentation so you can see what is possible with the Docker
container format.
Questions
- Paste your output of docker ps after you run the container in detached mode
- What are the advantages of using containers compared to virtual machines? (look at the reading!!)