Docker
Dockerfile
Compose
Development and Production instances
- Runs an image from the local repository and exits immediately.
$ sudo docker run localhost:5000/local-centos
$
- Runs an image from the local repostory and gets an interactive TTY shell.
$ sudo docker run -i -t localhost:5000/local-centos
[root@6bbf8236255a /]#
- Run an image from the local repository. Start with -d and -it to run in daemon mode and be able to reattach.
$ s docker run -d -it localhost:5000/local-centos bash
000c8a767f2754ac1ad6cbf62c85922b79cf85a2ea163eda888d45a78d568581
$ s docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
000c8a767f27 localhost:5000/local-centos "bash" 4 seconds ago Up 3 seconds dazzling_hodgkin
d51ca36e5f42 registry:2 "/entrypoint.sh /etc…" 37 hours ago Up 37 hours 0.0.0.0:5000->5000/tcp local-repository
- Show the ports exposed by a specific container. Note:
docker ps
ordocker container ls
lists the ports in order of external then internal port, whereasdocker port
lists the ports in order of internal then external ports.
$ docker port nginx
- Reattach to the instance running in the background. Exits and stops the running instance.
$ s docker attach 00
[root@000c8a767f27 /]# exit
- Reattach to the instance running in the background. Ctrl-p + Ctrl-q detaches from instance and leaves it running in the background.
$ s docker attach 00
[root@000c8a767f27 /]# read escape sequence
- Find the IP address of a running container.
$ s docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d72a1dd7e14e debian "sleep 3600" 5 minutes ago Up 5 minutes trusting_hoover
$ s docker inspect d7 | grep "IPAddress"
"SecondaryIPAddresses": null,
"IPAddress": "172.17.0.2",
"IPAddress": "172.17.0.2",
$ ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.081 ms
64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.100 ms
^C
--- 172.17.0.2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1009ms
rtt min/avg/max/mdev = 0.081/0.090/0.100/0.013 ms
$ s docker stop d7
d7
$ s docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
$ s docker inspect d7 | grep "IPAddress"
"SecondaryIPAddresses": null,
"IPAddress": "",
"IPAddress": "",
- Pull a reference image from hub.docker.com.
$ docker pull httpd
Using default tag: latest
latest: Pulling from library/httpd
f189db1b88b3: Pull complete
ba2d31d4e2e7: Pull complete
23a65f5e3746: Pull complete
5e8eccbd4bc6: Pull complete
4c145eec18d8: Pull complete
1c74ffd6a8a2: Pull complete
1421f0320e1b: Pull complete
Digest: sha256:8631904c6e92918b6c7dd82b72512714e7fbc3f1a1ace2de17cb2746c401b8fb
Status: Downloaded newer image for httpd:latest
$ docker images -a | { head -1; grep [h]ttpd; }
REPOSITORY TAG IMAGE ID CREATED SIZE
httpd latest d595a4011ae3 5 days ago 178 MB
- Run the local image, then make a change to the running container.
$ docker run -it --name httpd-template-base -p 8080:80 -e TERM=xterm -d httpd
86a43e482df77319fed13db5ff3f600ca2293c0d2b20fb1b1d41c2d30586aa63
OR
$ docker run -it --env HTTP_PROXY="http://proxy.corporate.com:81" --name httpd-template-base2 -p 8080:80 -e TERM=xterm -d httpd
2c5fb262a533813ff6026a9ba1bc64622cba872f9b96326bd38fa25eafc90815
$ docker ps -a | { head -1; grep [h]ttpd; }
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
86a43e482df7 httpd "httpd-foreground" 23 seconds ago Up 23 seconds 0.0.0.0:8080->80/tcp httpd-template-base
- Connect interactively to the container running in the background and make changes to it.
$ docker exec -it 86 bash
root@2c5fb262a533:/usr/local/apache2# export http_proxy="http://proxy.corporate.com:81"
root@2c5fb262a533:/usr/local/apache2# apt-get update
Get:1 http://security.debian.org jessie/updates InRelease [44.9 kB]
Ign http://deb.debian.org jessie InRelease
Get:2 http://deb.debian.org jessie-updates InRelease [145 kB]
Get:3 http://security.debian.org jessie/updates/main amd64 Packages [656 kB]
Get:4 http://deb.debian.org jessie-backports InRelease [166 kB]
Ign http://deb.debian.org stretch InRelease
Get:5 http://deb.debian.org jessie Release.gpg [2420 B]
Get:6 http://deb.debian.org jessie-updates/main amd64 Packages [23.0 kB]
Get:7 http://deb.debian.org stretch Release.gpg [2434 B]
Get:8 http://deb.debian.org jessie Release [148 kB]
Get:9 http://deb.debian.org jessie-backports/main amd64 Packages [1172 kB]
Get:10 http://deb.debian.org stretch Release [118 kB]
Get:11 http://deb.debian.org jessie/main amd64 Packages [9098 kB]
Get:12 http://deb.debian.org stretch/main amd64 Packages [9500 kB]
Fetched 21.1 MB in 1min 4s (325 kB/s)
Reading package lists... Done
root@2c5fb262a533:/usr/local/apache2# apt-get install vim
...
- Exit the container and commit the changes to a new image.
root@2c5fb262a533:/usr/local/apache2# exit
$
$ docker commit 2c httpd-template
sha256:69b333de9627c43e4005cf33398840b65ddb362191e24d8d89f6ad10cc9ae8bb
$
$ docker images | { head -1; grep [h]ttpd; }
REPOSITORY TAG IMAGE ID CREATED SIZE
httpd-template latest 69b333de9627 39 seconds ago 227 MB
$
- Save the new image as a
.tar
archive. The.tar
archive can be scp'd to another host and brought up as a container.
$ docker save -o http-template.tar httpd-template
$ ls *.tar
http-template.tar
- Use
docker load
to import the.tar
archive as an image.
$ docker load -i http-template.tar
Loaded image: httpd-template:latest
- Cleaning up stopped containers.
docker rm -v $(docker ps -aq -f status=exited)
- Kill all running containers.
docker kill $(docker ps -q)
- Delete all stopped containers.
docker rm $(docker ps -a -q)
- Delete all images.
docker rmi $(docker images -q)
- Prune all dangling images.
docker rmi $(docker images -f "dangling=true" -q)
- FROM instruction specifies the base image to use. All
Dockerfiles
must have a FROM instruction as the first non-comment instruction. - RUN instructions specify a shell command to execute inside the image.
$ cat Dockerfile
FROM ubuntu
RUN apt-get update && apt-get install -y python python-pip
RUN pip -v install --trusted-host=pypi.org --trusted-host=files.pythonhosted.org --proxy=http://proxy.domain.com:81 flask
COPY app.py /opt/
ENTRYPOINT FLASK_APP=/opt/app.py flask run --host=0.0.0.0 --port=80
- Build the image by running
docker build
inside the same directory where theDockerfile
lives. Use--no-cache
to do a clean build without relying on the cache from the last build.
docker build --no-cache --build-arg http_proxy=http://proxy.domain.com:81 -t my-simple-webapp .
- Run the image by typing
docker run -it <image_name:tag> [command_to_be_executed_on_image]
.
$ docker run -it mydjango:v0.1
root@75d08980aade:/#
root@75d08980aade:/# ls
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
root@75d08980aade:/# exit
- Start the image as a daemon and connect to the running container.
$ docker run -it -d mydjango:v0.1
574da457a562e129027c07fb0acfc21eab55e9d0529f0b2a856525586bd45e37
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
574da457a562 mydjango:v0.1 "/bin/bash" 13 seconds ago Up 13 seconds hopeful_hypatia
$ docker attach 574da457a562
root@574da457a562:/#
- Execute commands in a running container and exit.
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7c971d9f0929 55e83ce465d6 "/bin/sh -c '/bin/..." 6 minutes ago Up 6 minutes relaxed_sammet
$ docker exec 7c971d9f0929 ls -la /repo/
total 20
dr-xrwx--x 4 root www-data 4096 Aug 17 05:04 .
drwxr-xr-x 51 root root 4096 Aug 17 05:02 ..
drwxr-xr-x 4 root root 4096 Aug 17 05:04 .temp
-r--r--r-- 1 root root 15 Aug 17 05:02 Archive-Update-in-Progress-7c971d9f0929
drwxr-xr-x 5 root root 4096 Aug 17 05:05 dists
-rw-r--r-- 1 root root 0 Aug 17 04:55 test.txt
docker-compose
is a wrapper around thedocker
command.docker-compose build
will read thedocker-compose.yml
file, look for allservices
containing thebuild:
statement and run adocker build
for each one.- Only builds the images, does not start the containers:
docker-compose build
- Builds the images if the images do not exist and starts the containers for the
services
in thedocker-compose.yml
file. If you make changes to theDockerfile
that the image is based on and want the image to inherit the changes, you can just add the--build
option instead of manually deleting the image and rerunning.
docker-compose up
- Forces a build of the images even when it is not needed (like using the cache). Ordering of the flags are significant.
docker-compose --verbose up --build
- Running the development server.
$ docker-compose up --build
Building hello
Step 1/8 : FROM python:3
---> 825141134528
Step 2/8 : ENV PYTHONUNBUFFERED 1
---> Using cache
---> b90b94a5958d
Step 3/8 : WORKDIR /app
---> Using cache
---> 897498d3ff70
Step 4/8 : COPY app.py /app
---> Using cache
---> dba72def05e5
Step 5/8 : COPY requirements.txt /app
---> Using cache
---> 2a830a50f513
Step 6/8 : RUN pip install --trusted-host pypi.org --trusted-host files.pythonhosted.org --proxy http://proxy.bloomberg.com:81 -r requirements.txt
---> Using cache
---> 26a49354bbbe
Step 7/8 : EXPOSE 80
---> Using cache
---> 326c8e1fb02c
Step 8/8 : CMD python app.py
---> Using cache
---> 5352b7d87725
Successfully built 5352b7d87725
Recreating flaskapp_hello_1
Attaching to flaskapp_hello_1
hello_1 | * Serving Flask app "app" (lazy loading)
hello_1 | * Environment: production
hello_1 | WARNING: Do not use the development server in a production environment.
hello_1 | Use a production WSGI server instead.
hello_1 | * Debug mode: on
hello_1 | * Running on http://0.0.0.0:80/ (Press CTRL+C to quit)
hello_1 | * Restarting with stat
hello_1 | * Debugger is active!
hello_1 | * Debugger PIN: 197-442-924
hello_1 | 10.144.24.78 - - [04/Aug/2018 15:34:37] "GET /how%20are%20you? HTTP/1.1" 200 -
- Starts the containers in detached mode and run them in the background.
docker-compose up -d
- Check if the containers are running.
$ docker-compose ps
Name Command State Ports
-----------------------------------------------------------
flaskapp_web_1 python app.py Up 0.0.0.0:80->80/tcp
- Check the console logs of the detached containers.
docker-compose logs
- Stop the running containers.
$ docker-compose ps
Name Command State Ports
-----------------------------------------------------------
flaskapp_web_1 python app.py Up 0.0.0.0:80->80/tcp
$ docker-compose stop
Stopping flaskapp_web_1 ... done
$ docker-compose ps
Name Command State Ports
-----------------------------------------------
flaskapp_web_1 python app.py Exit 0
- Delete the stopped containers.
$ docker-compose ps
Name Command State Ports
-----------------------------------------------
flaskapp_web_1 python app.py Exit 0
$ docker-compose down
Removing flaskapp_web_1 ... done
Removing network flaskapp_default
$ docker-compose ps
Name Command State Ports
------------------------------
- To ensure we can
attach
to a container already running in the background, ensurestdin_open: true
andtty: true
are configured indocker-compose.yml
.
stdin_open: true
tty: true
- Run a container in the background with the
-d
flag. Ifstdin_open: true
andtty: true
are configured indocker-compose.yml
you can attach to the container and detaach from it withCtrl-p
+Ctrl-q
without killing the container.
$ docker-compose up --build -d
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9f24ee3ba251 nginxdocker_nginx "nginx -g 'daemon ..." 33 seconds ago Up 32 seconds 0.0.0.0:80->80/tcp nginx_prod
$ docker attach nginx_prod
10.144.24.78 - - [16/Aug/2018:02:49:46 +0000] "GET /guacamole/ HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36" "-"
10.144.24.78 - - [16/Aug/2018:02:49:46 +0000] "GET /guacamole/api/languages?token=85875af0ebba63c00dac313322a9328e38fb98eb2573bdc2c31a3da9f06cfba7 HTTP/1.1" 200 115 "http://10.150.139.249/guacamole/" "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36" "-"
10.144.24.78 - - [16/Aug/2018:02:49:46 +0000] "POST /guacamole/api/tokens HTTP/1.1" 403 161 "http://10.150.139.249/guacamole/" "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36" "-"
10.144.24.78 - - [16/Aug/2018:02:49:46 +0000] "GET /guacamole/images/logo-144.png HTTP/1.1" 200 9167 "http://10.150.139.249/guacamole/" "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36" "-"
- Use
volumes
indocker-compose.yml
to mount files on the host to a location in a container. Any changes to the file on the host will only be reflected in the container after runningdocker restart <container_id>
. There will be a disconnection.
volumes:
- ./app/user-mapping.xml:/etc/guacamole/user-mapping.xml
- Running a development environment on high-ports and with these files modified to a certain extend.
.
+-- apps
| +-- app.py <- Modify for development.
| +-- Dockerfile <- Modify for development.
| +-- requirements.txt
| +-- templates
| | +-- index.html
| +-- uwsgi.ini <- Not required for development.
+-- docker-compose.yml <- Modify for development.
+-- nginx
+-- Dockerfile <- Not required for development.
+-- nginx.conf <- Not required for development.
$ grep 5000 apps/app.py
app.run(host="0.0.0.0", port='5000', debug=True)
$ egrep "^CMD" apps/Dockerfile
CMD ["python", "app.py"]
$ cat docker-compose.yml
version: '2'
services:
webui:
build: ./apps
ports:
- "5000:5000"
#nginx:
# build: ./nginx
# volumes:
# - ./nginx/nginx.conf:/etc/nginx/nginx.conf
# ports:
# - "81:81"
# links:
# - webui
- Go into the project directory and run
docker-compose ps
to see the running containers. In the dev directory you'll be able to see it's running containers only.
$ docker-compose ps
Name Command State Ports
--------------------------------------------------------------------
flaskappdev_webui_1 python app.py Up 0.0.0.0:5000->5000/tcp
docker ps
will show the global pool of running containers.
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2299a9e0015b flaskappdev_webui "python app.py" 4 minutes ago Up 4 minutes 0.0.0.0:5000->5000/tcp flaskappdev_webui_1
de447f5a75df flaskappprod_nginx "nginx -g 'daemon ..." 16 hours ago Up 16 hours 0.0.0.0:80->80/tcp flaskappprod_nginx_1
53f46ed54816 flaskappprod_webui "uwsgi --ini /app/..." 16 hours ago Up 16 hours 0.0.0.0:3031->3031/tcp flaskappprod_webui_1
- Testing application in development environment by temporarily resetting
http_proxy
environment variable.
$ http_proxy='' curl -vvv -L -k curl -H "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36" http://127.0.0.1:5000/hello/foo
- Limit container resources in the
docker-compose.yml
file.
$ cat docker-compose.yml
version: '2'
services:
webui:
build: ./apps
container_name: "flaskapp_prod_webui"
ports:
- "3031:3031"
nginx:
build: ./nginx
container_name: "flaskapp_prod_nginx"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
ports:
- "80:80"
links:
- webui
mem_limit: 100000000 <- limits to 100MB!
$ docker stats fa2b6dedadd9
CONTAINER CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
c46d96b4e41a 0.00% 1.43 MiB / 190.7 MiB 0.75% 1.74 kB / 648 B 0 B / 0 B 2
^^^^^^^^^
- Docker production directory layout and configuration files.
.
+-- apps
| +-- app.py
| +-- Dockerfile
| +-- requirements.txt
| +-- templates
| | +-- index.html
| +-- uwsgi.ini
+-- docker-compose.yml
+-- nginx
+-- Dockerfile
+-- nginx.conf
- Bring up the containers in the background (daemon mode) with the
-d
flag.
docker-compose up --build -d
- List all containers that are running, either in the foreground or background.
$ docker-compose ps
Name Command State Ports
-------------------------------------------------------------------------------------
flaskapp_prod_nginx nginx -g daemon off; -c /e ... Up 0.0.0.0:80->80/tcp
flaskapp_prod_webui uwsgi --ini /app/uwsgi.ini Up 0.0.0.0:3031->3031/tcp
- In order to execute commands on the containers running in the background, use one of these
docker
commands.
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0d7959f678d5 prod_nginx "nginx -g 'daemon ..." 42 seconds ago Up 41 seconds 0.0.0.0:80->80/tcp flaskapp_prod_nginx
cfffe567b1d9 prod_webui "uwsgi --ini /app/..." 42 seconds ago Up 41 seconds 0.0.0.0:3031->3031/tcp flaskapp_prod_webui
$ docker exec -it cfffe567b1d9 ls -lFR /app
/app:
total 20
-rw-rw-r-- 1 root root 399 Aug 8 12:39 app.py
-rw-rw-r-- 1 root root 27 Aug 6 08:51 requirements.txt
drwxr-xr-x 2 root root 4096 Aug 9 06:13 static/
drwxr-xr-x 2 root root 4096 Aug 9 06:13 templates/
-rw-rw-r-- 1 root root 197 Aug 9 06:02 uwsgi.ini
/app/static:
total 4
-rw-rw-r-- 1 root root 28 Aug 8 14:23 style.css
/app/templates:
total 12
-rw-rw-r-- 1 root root 488 Aug 9 02:02 base.html
-rw-rw-r-- 1 root root 428 Aug 9 01:43 index.html
-rw-rw-r-- 1 root root 335 Aug 9 02:04 welcome.html
- To attach to a container running in the background do the following. Notice that the restricted shell is a good security measure.
$ docker exec -it cfffe567b1d9 bash
root@cfffe567b1d9:/app# ifconfig
bash: ifconfig: command not found
root@cfffe567b1d9:/app# reboot
bash: reboot: command not found