| **$PWD** is a variable that stands for present working directory
-> Multiple volumes flags can be supplied
You can inspect the mounted volumes by running (providingthe container name)
.. raw:: html
docker container inspect web1
and checking the "Mounts” section content
.. important::
* Changes applied within the mapped source directory are applied to the docker container's mounted copy
* CAREFUL: Changes made to the container's mounted volume are also made to the source directory
* Changes made outside a mapped directory are not applied to running containers (out of scope)
Debugging Tips and Tricks
-------------------------
.. hint::
Problem
Cannot interact with alpine filesystem of a running container using VirtualBox (Docker
Toolbox, Windows issue only)
Cause
inotify (notifies OS about file system changes) does behave differently on alpine.
It seems no problem when alpine is used on a Linux docker installation, though.
Solution
Change alpine to slim in your dockerfile: python3.7-alpine -> python3.7-slim-stretch
Connecting to a running container's file system
.. raw:: html
docker container exec -it <container_name> sh
| -> Opens up the containers shell (using 'bash’ or ‘sh’ when running other Linux distributions)
| -> Navigates to the specified working directory (WORKDIR) from the dockerfile
Command is useful to make sure, you are really running what you think you're running
Exit the exec session by hitting Ctrl+D
You can use any other command instead of 'sh' e.g. flask --version which prints the
flask version info to the local terminal window
.. important::
All commands docker executes on the containers host OS are executed as root user.
If new files are created by docker, they are owned by root (Linux only).
To prevent this use the --user flag
.. code-block:: none
docker container exec -it --user "$(id -u):$(id -g)" web1 touch hi.txt
Linking Containers with Docker Networks
---------------------------------------
Install redis container
.. raw:: html
docker pull redis:5.0-alpine
* Docker containers can run on multiple networks
* Same as computers in general, docker containers must be on the same network
* If they are scattered across multiple networks, a bridge must be created between the
various docker container networks
Docker creates several networks on it own. Check them out
.. raw:: html
* bridge: stands for the outside docker network (listed in your networks when running ifconfig)
* 'host' and 'none' are required by the docker daemon
* if no other network is specified for a container, the **bridge** is used as default
Inspect bridge network
.. raw:: html
docker network inspect bridge
-> "Containers" entry lists all running containers on that network
Running containers on the same network can talk to each other via their local IP addresses
(here web2 and redis are launched to the bridge network)
Check out a running containers IP address (here: redis)
.. code-block:: none
docker exec redis ifconfig
Check their communication by piing the other container
.. code-block:: none
docker exec web2 ping 172.17.0.2
Check the hosts mapping file -> other container is added with ip and its container ID
.. code-block:: none
docker exec redis cat /etc/hosts
Creating a new network for Docker is useful, as it automatically the containers names to
its current IP addresses, even if those change, due to their unique ID (automatic DNS)
.. raw:: html
docker network create --driver bridge <some_network_name>
-> When inspecting the new network, it is visible that it uses the 172.18.0.0 subnet whereas
the bridge used the 172.17.0.0
Launch a container on a specified network (created a network named 'firstnetwork')
.. raw:: html
docker container run -itd --name web2 -p 5000:5000 --rm -e FLASK_APP=app.py -e FLASK_DEBUG=1 --net firstnetwork -v $PWD:/app web1
Now we can access each container on firstnetwork with their name (not their IP addresses anymore)
.. code-block:: none
docker exec web2 ping redis
Persisting data to your docker host
-----------------------------------
* Restoring data after the container has been stopped
* For storing data of redis we will use a named volume (web2_redis:/data)
* Perfect for databases
Create a named volume
.. raw:: html
docker volume create web2_redis
web2_redis is the volume name here
List all volumes of docker host
.. raw:: html
more details
.. raw:: html
docker volume inspect web2_redis
will show, where the volume will be mounted on the docker image
.. important::
The mount point is not accessible on the local system (at least on OSX it is not)
-> this is a design concept, the path is a place on the “remote” machine filesystem
(from: https://github.com/docker/for-win/issues/6875)
Add the data volume to redis container when starting it
.. raw:: html
docker container run -itd --name redis -p 6379:6379 --rm -e --net firstnetwork-v web2_redis:/data redis:5.0-alpine
.. hint::
Redis specific stuff: Save current data
Since redis is a in-memory storage, it must be manually saved to the file system.
.. code-block:: none
docker exec redis redis-cli save
This will create a dump.rdb file within the /data directory (on local redis container mount point)
and on root dir of the redis container.
When stopping the container and restarting it, previous data is still there
This method is applicable for all kind of database -> you must merely find out, where
such data needs to be stored inside the container (/data for redis)
.. note::
There should be no data stored inside the docker container once the application is
running in production, since the application is supposed to be easily installable on
various systems (portable)
Sharing Data between Containers
-------------------------------
* link data between containers without touching the docker host
* most popular use case is a web application using static assets (CSS, Javascript)
* Those static files are supposed to be shared via a webserver like nginx
New setting in the dockerfile
.. raw:: html
* means, that the app/public folder is supposed to be exposed as a volume when containers
is running
* multiple volume instructions are possible
Re-build the docker image (since dockerfile has changed) and start the container
Restart the redis container with referencing the new exposed volume of web2
.. raw:: html
docker container run -itd --name redis -p 6379:6379 --rm -e --net firstnetwork -v web2_redis:/data --volumes-from web2 redis:5.0-alpine
-> volumes-from
Changes to the shared volumes are immediately visible to the other containers that added it (and vice versa)
In order to share directories, those must reside on the same docker host system
.. important::
**Not recommended method**
Volumes can be shared without editing the docker file:
* add a -v flag to the container that shares a directory (e.g. -v /app/public)
* execute redis as above
-> the shared volumes must be specified in the command, not the dockerfile
Optimizing your Docker images (decrease your docker images size)
----------------------------------------------------------------
Use a *.dockerignore* file to reduce image size and not publish secure files:
* during container creation docker will evaluate the file when COPY or ADD commands
are executed
* WORKDIR is the default start path for all ignore paths
.. code-block:: none
.dockerignore #ignore the file itself
.git/ #ignore a directory
.foo/\* #ignore all files within a directory (but not the dir itself)
\**/*.txt #ignore all files with txt extension
!special.txt #make exception to above rule for special.txt file
-> Check out the updated dockerfile run script in the repository
Make .dockerignore a whitelist (instead of a blacklist)
.. code-block:: none
*
!keepthisfile.txt
Running scripts when a container starts
---------------------------------------
* Usage: Same docker images is supposed to be used for several projects, but each
project has a slightly different setup routine
* Usually separate dockerfiles are needed in which most commands will be the same for
each application
* It technically works, but all these files need to be maintained if the image changes
**Scenario:** Adding a database to all these projects (e.g. PostgreSQL), setting a own user
login and table name for each project's dockerfile -> very tedious
| -> Postgres is able to setup a table with username and password via a shell command (if
not existing already)
| -> This script can be referenced as an entry point for each project's dockerfile
#. Add the entry point script to your dockerfile (located in root dir, not working dir):
.. code-block:: none
COPY docker-entrypoint.sh /
#. Make entrypoint script executable
.. code-block:: none
RUN chmod +x /docker-entrypoint.sh
#. Add entrypoint pointing to the copied script
.. raw:: html
ENTRYPOINT ["/docker-entrypoint.sh"]
| Rebuild the application docker image
| Restart the redis container and the start the new application container
The used shell script:
* within your entrypoint script, you can print terminal message by using **echo** (shell) e.g.
.. code-block:: none
echo "The Dockerfile ENTRYPOINT has been executed!"
* an environmental variable is set (with a default value)
.. code-block:: none
export WEB2_COUNTER_MSG="${WEB2_COUNTER_MSG:-some default value if not specified}
* ${WEB2_COUNTER_MSG} is a shell variable, which can be overridden by passing it into
the docker command using the -e flag
.. hint::
Python is able to access environmental variables by using:
.. code-block:: none
os.getenv() e.g os.getenv('WEB2_COUNTER_MSG, '')
**Explaining exec "$@"**
* Dockerfile's CMD instruction passes whatever comes after to a default entry point
(if none is defined)
* This default entry point is "/bin/sh -c" (run whatever I pass as a command (-c) in /bin/sh)
* Since our entrypoint now overrides this default, the "CMD flask --host ..." command actually does this
.. raw:: html
bin/sh -c "$ && flask --host=0.0.0.0 --port=5000"
where **$** stands for our docker-entrypoint.sh content
* the last command
.. code-block:: none
exec "$@"
then does take every command given by you on the command line (e.g. docker container run ...)
and executes it ($@ stands for all command-line arguments)
-> basically continues the docker command
-> The ENTRYPOINT defines what to run when using CMD in a dockerfile
Used for
* database migration
* nginx config modification
Cleaning up after yourself
--------------------------
**Get info**
Check out all containers (running and stopped)
.. code-block:: none
docker container ls -a
Check docker's currently used disk space
.. raw:: html
Check docker images
.. code-block:: none
docker image ls
* images with name are not used by anything -> they're dangling
* these may be failed builds or builds that were replaced by a new image using the
same tag (or both using the 'latest' tag)
* dangling images are declared as reclaimable
Get containers and images spaces usage together (verbose output)
.. raw:: html
Get system info
.. raw:: html
(also verifies that docker daemon runs well)
**Cleaning up**
Remove all stopped containers, unused volumes and networks and all dangling images
.. raw:: html
| **-f** ... force deleting (no confirmation needed)
| **-a** ... removes all unused images (not associated to a container) -> use with
care, images associated with stopped containers are deleted (images must be rebuild
to retrieve them)
Stop multiple containers
.. code-block:: none
docker container stop ...
Stop all running containers (at least one must be running, otherwise reports error)
.. code-block:: none
docker container stop $(container ls -a -q)
**-q** means quiet mode, and outputs the container id as string
Removing multiple images
.. code-block:: none
docker image rm : ...