Long running services with Docker
Overview
Teaching: 10 min
Exercises: 5 minQuestions
Objectives
Learn how to start containers for a running (web) service
Starting a long-running service in a container
Containers are useful for running services, like web-servers etc. Many come packaged from the developers, so you can start one easily, but first you need to find the one you want to run. You can either search on Docker Hub, or you can use the docker search
command. Nginx is a popular web-server, let’s look for that:
$ docker search nginx
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
nginx Official build of Nginx. 4719 [OK]
jwilder/nginx-proxy Automated Nginx reverse proxy for docker c... 877 [OK]
richarvey/nginx-php-fpm Container running Nginx + PHP-FPM capable ... 311 [OK]
million12/nginx-php Nginx + PHP-FPM 5.5, 5.6, 7.0 (NG), CentOS... 76 [OK]
webdevops/php-nginx Nginx with PHP-FPM 63 [OK]
maxexcloo/nginx-php Framework container with nginx and PHP-FPM... 58 [OK]
bitnami/nginx Bitnami nginx Docker Image 20 [OK]
gists/nginx Nginx on Alpine 8 [OK]
evild/alpine-nginx Minimalistic Docker image with Nginx 8 [OK]
million12/nginx Nginx: extensible, nicely tuned for better... 8 [OK]
maxexcloo/nginx Framework container with nginx installed. 7 [OK]
webdevops/nginx Nginx container 7 [OK]
1science/nginx Nginx Docker images based on Alpine Linux 4 [OK]
ixbox/nginx Nginx on Alpine Linux. 3 [OK]
drupaldocker/nginx NGINX for Drupal 3 [OK]
yfix/nginx Yfix own build of the nginx-extras package 2 [OK]
frekele/nginx docker run --rm --name nginx -p 80:80 -p 4... 2 [OK]
servivum/nginx Nginx Docker Image with Useful Tools 2 [OK]
dock0/nginx Arch container running nginx 2 [OK]
blacklabelops/nginx Dockerized Nginx Reverse Proxy Server. 2 [OK]
xataz/nginx Light nginx image 2 [OK]
radial/nginx Spoke container for Nginx, a high performa... 1 [OK]
tozd/nginx Dockerized nginx. 1 [OK]
c4tech/nginx Several nginx images for web applications. 0 [OK]
unblibraries/nginx Baseline non-PHP nginx container 0 [OK]
The official build of Nginx seems to be very popular, let’s go with that:
$ docker pull nginx
Using default tag: latest
latest: Pulling from library/nginx
386a066cd84a: Pull complete
386dc9762af9: Pull complete
d685e39ac8a4: Pull complete
Digest: sha256:3861a20a81e4ba699859fe0724dc6afb2ce82d21cd1ddc27fff6ec76e4c2824e
Status: Downloaded newer image for nginx:latest
Now, in order to run a web server such as a Nginx, we are going to use some additional Docker options, to:
- open up communication ports
- run the container in background
- give the container a specific name
We will also use a number of new Docker commands. Let’s start with a known one and add some extra flags:
$ docker run -p 80:80 --name=nginx nginx
The option -p 80:80
tells Docker to map port 80
on the host to port 80
in the container, so you can communicate with it.
We’ve also introduced a second new option: --name
. Docker automatically names containers, but these names don’t reflect the purpose of the container (e.g. pensive_booth
was the name of the nginx container I ran for this example without the --name
option). You can name your container whatever you want, but it’s helpful to give it a name similar to the container you’re using, or the specific service or application you’re running in the container. This can be useful when we need to act on the container while it’s running, e.g. to stop it, or to get logs, as we’ll see soon. In this example, we’ve called our container nginx
.
Note also that we didn’t tell docker what program to run, that’s baked into the container in this case. More on that later.
Now, go to your browser and in the address bar enter localhost
if you are running Docker on your machine, or <Your VM's IP Address>
if you are running on a cloud service. You should see a page with a Welcome to nginx! message. On your terminal where you ran the docker command, you’ll see some log information:
172.17.0.1 - - [30/Nov/2016:18:07:59 +0000] "GET / HTTP/1.1" 200 612 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:49.0) Gecko/20100101 Firefox/49.0" "-"
2016/11/30 18:07:59 [error] 7#7: *1 open() "/usr/share/nginx/html/favicon.ico" failed (2: No such file or directory), client: 172.17.0.1, server: localhost, request: "GET /favicon.ico HTTP/1.1", host: "localhost"
172.17.0.1 - - [30/Nov/2016:18:07:59 +0000] "GET /favicon.ico HTTP/1.1" 404 169 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:49.0) Gecko/20100101 Firefox/49.0" "-"
172.17.0.1 - - [30/Nov/2016:18:07:59 +0000] "GET /favicon.ico HTTP/1.1" 404 169 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:49.0) Gecko/20100101 Firefox/49.0" "-"
2016/11/30 18:07:59 [error] 7#7: *1 open() "/usr/share/nginx/html/favicon.ico" failed (2: No such file or directory), client: 172.17.0.1, server: localhost, request: "GET /favicon.ico HTTP/1.1", host: "localhost"
That’s a good start, but you now have a terminal tied up with nginx, and if you hit CTRL-C
in that terminal, your web-server dies.
To practice a different solution, let’s first stop this container. How to? In a new terminal, use the docker stop
command!
$ docker stop nginx
nginx
Check that it’s gone:
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Now let’s go ahead with using the Docker option -d
to run the container in the background instead (daemon mode):
$ docker run -d -p 80:80 --name=nginx nginx
48a2dca14407484ca4e7f564d6e8c226d8fdd8441e5196577b2942383b251106
Go back to your browser, reload localhost
(or <Your VM's IP Address>
), and you should get the page loaded again.
We can view the logs of our nginx service with the docker logs
command, followed by the container name, nginx
:
$ docker logs --follow nginx
172.17.0.1 - - [30/Nov/2016:18:18:40 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:49.0) Gecko/20100101 Firefox/49.0" "-"
This gives us a live look at what is going on in our nginx container (try reloading your webpage and see what the logs look like). Note that the --follow
option keeps the terminal open and will update the logs in real-time. If you omit it, docker logs
would simply display the last few lines of the log file.
If you hit CTRL-C
now, your container is still running, in the background. You can see this with the docker ps
command:
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
48a2dca14407 nginx "nginx -g 'daemon off" 3 minutes ago Up 3 minutes 443/tcp, 0.0.0.0:80->80/tcp nginx
You can open a shell into the running container, if you wish, using docker exec
. This can be handy for debugging:
$ docker exec -t -i nginx /bin/bash
root@48a2dca14407:/#
root@48a2dca14407:/# exit # or hit CTRL-D
Finally, let’s stop this container:
$ docker stop nginx
nginx
and then let’s remove it to avoid container name clashes in the future:
$ docker rm nginx
nginx
Start a Jupyter web server using a container
First, pull the container image
jupyter/scipy-notebook
.Solution
$ docker pull jupyter/scipy-notebook
Using default tag: latest latest: Pulling from jupyter/scipy-notebook a48c500ed24e: Already exists [..] 5c52a47b5569: Pull complete Digest: sha256:a2518b3a15c207dbe4997ac94c91966256ac94792b09844fbdfe53015927136f Status: Downloaded newer image for jupyter/scipy-notebook:latest
Now, how would you use this image to start a Jupyter webserver in daemon mode?
A couple of extra requirements:
- assign the name
jupyter
to the container- map the current host working directory as
/home/jovyan/data
(see previous episode)- use the
--rm
flag from a previous episode, so that the container gets deleted upon exitSome hints:
- use the default HTTP port
80
for the host, to navigate to the corresponding page in the web browser using default syntax- this
jupyter
image wants to use port8888
for the container- no command needs to be specified for that container, the default behaviour is to start the webserver
Solution
$ docker run -d --name=jupyter -v `pwd`:/home/jovyan/data --rm -p 80:8888 jupyter/scipy-notebook
a14f3c38dc24d745c470657ccdb62d77ce5a746fb37f5e02c21c978df4e156d1
Use
docker logs
to look for an accesstoken
string.Solution
$ docker logs jupyter
Executing the command: jupyter notebook [I 07:55:56.797 NotebookApp] Writing notebook server cookie secret to /home/jovyan/.local/share/jupyter/runtime/notebook_cookie_secret [I 07:55:58.101 NotebookApp] JupyterLab extension loaded from /opt/conda/lib/python3.7/site-packages/jupyterlab [I 07:55:58.101 NotebookApp] JupyterLab application directory is /opt/conda/share/jupyter/lab [I 07:55:58.103 NotebookApp] Serving notebooks from local directory: /home/jovyan [I 07:55:58.103 NotebookApp] The Jupyter Notebook is running at: [I 07:55:58.103 NotebookApp] http://(a14f3c38dc24 or 127.0.0.1):8888/?token=abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuv [I 07:55:58.103 NotebookApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation). [C 07:55:58.106 NotebookApp] To access the notebook, open this file in a browser: file:///home/jovyan/.local/share/jupyter/runtime/nbserver-6-open.html Or copy and paste one of these URLs: http://(a14f3c38dc24 or 127.0.0.1):8888/?token=abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuv
Copy the token, in this example
abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuv
, in the clipboard. Then use your web browser to go tolocalhost
if you are running Docker on your machine, or<Your VMs IP Address>
if you are running on a cloud service. Paste the token to access your containerised Jupyter server!Once you are done, close the browser window, then stop the container.
Solution
$ docker stop jupyter
jupyter
Key Points
You-ve learned how to run long-running services (like a web server) through containers
Use the flag
-d
to run the containers in backgroundUse the flag
-p <host port:<container port>
to map communication portsAdditional options to manage and query containers include
--name
anddocker logs