Exercise 1.2 - Working with Docker Images

Return to Workshop

Run Docker

When you execute a docker run command, you essentially spin up and create a new container from a Docker image. That container consists of the contents of the image, plus features based on any additional options you pass on the docker run command line.

The command you pass on the docker run command line sees the inside the container as its running environment so, by default, very little can be seen of the host system. For example, by default, the running applications sees:

  • The file system provided by the Docker image.

  • A new process table from inside the container (no processes from the host can be seen).

  • New network interfaces (by default, a separate docker network interface provides a private IP address to each container via DHCP).

If you want to make a directory from the host available to the container, map network ports from the container to the host, limit the amount of memory the container can use, or expand the CPU shares available to the container, you can do those things from the docker run command line.

Launch a container and explore inside interactively.

Docker run command
docker run --rm -it registry.access.redhat.com/rhel7/rhel /bin/bash

Commands to try:

ls
whoami
pwd
uname -a
docker shell
Figure 2: In docker container
# to exit
exit

Docker Volumes

To show that docker volumes can be used to mount external file systems with useful functions and data we will mount a system volume to a container and use commands that are not present in the container. The following command will fail as the "ip" command is not present in the base rhel7 container image.

Volume Mounts

nonexistent command "ip"
docker run --rm -it rhel7 /usr/sbin/ip a

....
/usr/bin/docker-current: Error response from daemon: invalid header field value "oci runtime error: container_linux.go:247: starting container process caused \"exec: \\\"/usr/sbin/ip\\\": stat /usr/sbin/ip: no such file or directory\"\n".

Running the same command with the -v switch will run the ip command successfully

mounting ip command via volumes
docker run -v /usr/sbin:/usr/sbin --rm -it rhel7 /usr/sbin/ip a

....
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
10: eth0@if11: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP
    link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.17.0.2/16 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:acff:fe11:2/64 scope link tentative
       valid_lft forever preferred_lft forever

Volume Mounts (continued)

Volume mounts can be used for logging to the container host for event retention

mounting logs via volumes
docker run -v /dev/log:/dev/log --rm rhel7 logger Testing logging to the host

Search for the container event on the container host log

search the container host
journalctl | grep "Testing logging"

Persistent container data

The -w switch specifies the working directory where binaries are executed. If unspecified, the root directory (/) is used.

Run Python simple server
docker run -d -p 8080:8000 --name="python_web" \
       -w /opt \
       -v /opt/rhel_data:/opt rhel7 \
       /bin/python -m SimpleHTTPServer 8000

Verify the container is running using the docker ps switch

docker ps -a

CONTAINER ID IMAGE COMMAND                CREATED        STATUS    PORTS                   NAMES
6932ab070fa5 rhel  "/bin/python -m Simpl" 2 minutes ago  Up 2 min  0.0.0.0:8080->8000/tcp  python_web

Run the following URL command to show there are no files present in the container’s /opt directory.

Check to see no files in opt
ll /opt/rhel_data/

total 0
Check to see no files via the web server
curl localhost:8080


<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><html>
<title>Directory listing for /</title>
<body>
<h2>Directory listing for /</h2>
<hr>
<ul>
</ul>
<hr>
</body>
</html>

Now create several files with a script in the host /opt/rhel_data directory.

for loop script
for i in {1..10}; do touch /opt/rhel_data/file${i}; done

View the newly created files in /opt/rhel_data

ll /opt/rhel_data/

total 0
-rw-r--r--. 1 root root 0 Feb 14 22:38 file1
-rw-r--r--. 1 root root 0 Feb 14 22:38 file10
-rw-r--r--. 1 root root 0 Feb 14 22:38 file2
-rw-r--r--. 1 root root 0 Feb 14 22:38 file3
-rw-r--r--. 1 root root 0 Feb 14 22:38 file4
-rw-r--r--. 1 root root 0 Feb 14 22:38 file5
-rw-r--r--. 1 root root 0 Feb 14 22:38 file6
-rw-r--r--. 1 root root 0 Feb 14 22:38 file7
-rw-r--r--. 1 root root 0 Feb 14 22:38 file8
-rw-r--r--. 1 root root 0 Feb 14 22:38 file9

Then use curl to view the files from the python webserver that is serving files from the mounted /opt/rhel_data volume.

Check to see new files in the web server’s /opt
curl localhost:8080

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><html>
<title>Directory listing for /</title>
<body>
<h2>Directory listing for /</h2>
<hr>
<ul>
<li><a href="file1">file1</a>
<li><a href="file10">file10</a>
<li><a href="file2">file2</a>
<li><a href="file3">file3</a>
<li><a href="file4">file4</a>
<li><a href="file5">file5</a>
<li><a href="file6">file6</a>
<li><a href="file7">file7</a>
<li><a href="file8">file8</a>
<li><a href="file9">file9</a>
</ul>
<hr>
</body>
</html>

Container metadata

Docker images have metadata associated with them that can tell you a lot about processes and network settings.

Docker inspect
docker inspect python_web

[{
    "AppArmorProfile": "",
    "Args": [
        "-m",
        "SimpleHTTPServer",
        "8000"
    ],
...
    "ProcessLabel": "system_u:system_r:svirt_lxc_net_t:s0:c253,c723",
...

Scripting Pro Tips

You can use a dot notation to parse the metadata returned by docker inspect and use it in your scripting to quickly access properties you need.

docker inspect -f {{.NetworkSettings.IPAddress}} python_web

172.17.0.3
docker run --rm -it rhel7 bash

[root@790377e6e083 ~]# cat /proc/1/cgroup
10:hugetlb:/system.slice/docker-790377e6e083a5461b40406908d2cbb74389fadac0e2db611bf87674a1c4dc3d.scope
9:perf_event:/
8:blkio:/system.slice/docker-790377e6e083a5461b40406908d2cbb74389fadac0e2db611bf87674a1c4dc3d.scope
7:net_cls:/system.slice/docker-790377e6e083a5461b40406908d2cbb74389fadac0e2db611bf87674a1c4dc3d.scope
6:freezer:/system.slice/docker-790377e6e083a5461b40406908d2cbb74389fadac0e2db611bf87674a1c4dc3d.scope
5:devices:/system.slice/docker-790377e6e083a5461b40406908d2cbb74389fadac0e2db611bf87674a1c4dc3d.scope
4:memory:/system.slice/docker-790377e6e083a5461b40406908d2cbb74389fadac0e2db611bf87674a1c4dc3d.scope
3:cpuacct,cpu:/system.slice/docker-790377e6e083a5461b40406908d2cbb74389fadac0e2db611bf87674a1c4dc3d.scope
2:cpuset:/system.slice/docker-790377e6e083a5461b40406908d2cbb74389fadac0e2db611bf87674a1c4dc3d.scope
1:name=systemd:/system.slice/docker-790377e6e083a5461b40406908d2cbb74389fadac0e2db611bf87674a1c4dc3d.scope

Return to Workshop