5 Working With Containers and Images

This chapter describes how to use the Docker Engine to run containers and how to obtain the images that are used to create a container. Other information specific to container and image configuration is also provided. In this chapter is assumed that images and containers are hosted on Oracle Linux 7.

Pulling Oracle Linux Images From a Container Registry

You can get Oracle Linux images to run on the Docker Engine from the oraclelinux repository at the Docker Hub. For a list of the Oracle Linux images that are available, see https://hub.docker.com/_/oraclelinux/).

An Internet connection is required to pull images from the Docker Hub or the Oracle Container Registry. If you make use of a proxy server to access the Internet, see Configuring a Proxy Server.

Oracle Linux images, along with many other Oracle product images, are also hosted on the Oracle Container Registry at https://container-registry.oracle.com and on the Docker Hub at https://hub.docker.com. More information on using the Oracle Container Registry to pull images is covered in Pulling Images From the Oracle Container Registry. See Using Third-Party Registries for more information on using the Docker Hub.

To download an Oracle Linux image, use the docker pull command. For example, to pull an Oracle Linux image from the Docker Hub:

docker pull oraclelinux:7-slim
Trying to pull repository docker.io/library/oraclelinux ... 
7-slim: Pulling from docker.io/library/oraclelinux
977461c90301: Pull complete 
Digest: sha256:0743f72832d8744a89b7be31b38b9fb2e5390044cbb153cd97b3e797723e4704
Status: Downloaded newer image for oraclelinux:7-slim

To display a list of the images that you have downloaded to a system, use the docker images command, for example:

docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
oraclelinux         7-slim              c2b5cb5bcd9d        7 days ago          118MB
oraclelinux         7                   31f4bed1dc33        7 days ago          232MB
oraclelinux         latest              31f4bed1dc33        7 days ago          232MB
oraclelinux         8                   8988c7081e1f        5 weeks ago         411MB

Each image in the repository is distinguished by its TAG value and its unique IMAGE ID. In the example, the tags 7 and latest refer to the same image ID for Oracle Linux 7.

When new images are made available for Oracle Linux updates, the tags 7, 8, and latest are updated in the oraclelinux repository to refer to the appropriate newest version.

If an image is downloaded from an alternate registry to the default registry, the REPOSITORY value also indicates the registry from which the image was pulled. For example:

docker images
REPOSITORY                                     TAG        IMAGE ID        CREATED       SIZE
container-registry.oracle.com/os/oraclelinux   latest     31f4bed1dc33    7 days ago    232MB

See Setting Container Registry Options for more information on adding registries and configuring a default registry.

Enabling or Disabling Docker Content Trust

Content Trust allows you to verify the authenticity, integrity, and publication date of Docker images that are made available on the Docker Hub Registry.

By default, Content Trust is disabled. To enable Content Trust for signing and verifying Docker images that you build, push to, or pull from the Docker Hub, set the DOCKER_CONTENT_TRUST environment variable, for example:

export DOCKER_CONTENT_TRUST=1                  

If you use sudo to run Docker commands, specify the -E option to preserve the environment or use visudo to add the following line to /etc/sudoers:

Defaults        env_keep += "DOCKER_CONTENT_TRUST"

For individual docker build, docker push, or docker pull commands, you can specify the --disable-content-trust=false and --disable-content-trust=true options to enable or disable Content Trust.

For more information, see https://docs.docker.com/engine/security/trust/content_trust/.

Enabling FIPS Mode in Containers

To run containers in FIPS mode, you must first enable FIPS mode on your Oracle Linux host system.

For more information about Oracle Linux 7 releases that have FIPS validated cryptographic modules available for installation, see Oracle Linux 7: Security Guide.

Note:

Oracle provides FIPS compliant container images by using the slim-fips tag. Container images tagged as FIPS compliant include compliant cryptographic package versions and initial image setup required for container FIPS mode. See The slim Tag for more information.

For Oracle Linux 7 Containers:

To enable FIPS mode in an Oracle Linux 7 container, install the dracut-fips package or mount /etc/system-fips from the host. For more information about mounting host files and directories from inside a container, see Accessing External Files From Docker Containers.

For Oracle Linux 8 Containers:

To enable FIPS mode in an Oracle Linux 8 container, mount /etc/system-fips from the host. For more information about mounting host files and directories from inside a container, see Accessing External Files From Docker Containers.

Additionally, bind mount FIPS cryptographic policies within the container from /usr/share/crypto-policies/back-ends/FIPS to /etc/crypto-policies/back-ends:

mount --bind /usr/share/crypto-policies/back-ends/FIPS /etc/crypto-policies/back-ends               

Creating and Running Docker Containers

You use the docker run command to run an application inside a container, for example:

docker run -i -t --name guest oraclelinux:7-slim
bash-4.2# cat /etc/oracle-release
Oracle Linux Server release 7.7
bash-4.2# exit              

This example runs an interactive bash shell using the Oracle Linux 7 image named oraclelinux:7-slim to provide the container. The /bin/bash command is the default command run for all oraclelinux base images. The -t and -i options allow you to use a pseudo-terminal to run the container interactively.

The following examples may use the prompt [root@host ~] and [root@guest ~] (or similar) to represent the prompts shown by the host and by the container respectively. The actual prompt displayed by the container may be different.

The --name option specifies the name guest for the container instance.

Docker does not remove the container when it exits and we can restart it at a later time, for example:

[root@host ~]# docker start guest
guest

If an image does not already exist on your system, the Docker Engine performs a docker pull operation to download the image from the Docker Hub (or from another repository that you specify) as shown in the following example:

[root@host ~]# docker run -i -t --rm container-registry.oracle.com/os/oraclelinux:7-slim

Unable to find image 'container-registry.oracle.com/os/oraclelinux:7-slim' locally
Trying to pull repository container-registry.oracle.com/os/oraclelinux ... 
7-slim: Pulling from container-registry.oracle.com/os/oraclelinux
Digest: sha256:267f37439471f1c5eae586394c85e743b887c7f97e4733e10e466158083c021e
Status: Downloaded newer image for container-registry.oracle.com/os/oraclelinux:7-slim
[root@guest /]# cat /etc/oracle-release 
Oracle Linux Server release 7.7
[root@guest /]# exit
exit
[root@host ~]# 

Because we specified the --rm option instead of naming the container, Docker removes the container when it exits and we cannot restart it.

From another shell window, you can use the docker ps command to display information about the containers that are currently running, for example:

[root@host ~]# docker ps
CONTAINER ID  IMAGE               COMMAND     CREATED       STATUS        PORTS  NAMES
68359521c0b7  oraclelinux:7-slim  "/bin/bash" 2 hours ago   Up 8 minutes         guest

The container named guest with the ID 68359521c0b7 is currently running the command /bin/bash. It is more convenient to manage a container by using its name than by its ID.

To display the processes that a container is running, use the docker top command:

[root@host ~]# docker top guest
UID    PID    PPID   C   STIME   TTY     TIME       CMD
root   31252  31235  0   05:59   pts/0   00:00:00   /bin/bash

You can use the docker exec command to run additional processes in a container that is already running, for example:

[root@host ~]# docker exec -i -t guest bash
[root@guest ~]#

You can also use the docker create command to set up a container that you can start at a later time, for example:

[root@host ~]# docker create -i -t --name newguest oraclelinux:7-slim
b4c224f83e35927f67b973febb006b0af4d037f41c30e1f4bdcc4b822e12fd0f
[root@host ~]# docker start -a -i newguest
[root@newguest ~]#

The -a and -i options to docker start attach the current shell's standard input, output, and error streams to those of the container and also cause all signals to be forwarded to the container.

You can exit a container by typing Ctrl-D or exit at the bash command prompt inside the container or by using the docker stop command:

[root@host ~]# docker stop guest

The -a option to docker ps displays all containers that are currently running or that have exited.

[root@host ~]# docker ps -a
CONTAINER ID  IMAGE              COMMAND  CREATED  STATUS                     PORTS  NAMES
b4c224f83e35  oraclelinux:7-slim ...      ...      Exited (0) About a minute ago     newguest
68359521c0b7  oraclelinux:7-slim ...      ...      Exited (137) 45 seconds ago       guest

You can use docker start to restart a stopped container. After reattaching to it, the contents remain unchanged from the last time that you used the container.

[root@host ~]# docker start -a -i guest
[root@guest ~]# touch /tmp/foobar
[root@guest ~]# exit
[root@host ~]# docker start -a -i guest
[root@guest ~]# ls -l /tmp/foobar
-rw-r--r-- 1 root root 0 Nov 26 06:27 /tmp/foobar

Because the container preserves any changes that you make to it, you can reconfigure files and install packages in the container without worrying that your changes will disappear.

You can use the docker logs command to watch what is happening inside a container, for example:

[root@host ~]# docker logs -f guest
bash-4.2# exit
exit
bash-4.2# ls -l /tmp/foobar
-rw-r--r-- 1 root root 0 Nov 26 06:33 /tmp/foobar

The -f option causes the command to update its output as events happen in the container. Type Ctrl-C to exit the command.

You can obtain full information about a container in JSON format by using the docker inspect command. This command also allows you to retrieve specified elements of the configuration, for example:

[root@host ~]# docker inspect --format='{{ .State.Running }}' guest

If you need to remove a container permanently so that you can create a new container with the same name, use the docker rm command:

[root@host ~]# docker rm guest

Note:

If you specify the --rm option when you run a container, Docker removes the container when the container exits. You cannot combine the --rm option with the -d option.

Specifying the -f option to docker rm kills a running container before removing it. In previous versions, the same command stops the container before removing it. If you want to stop a container safely, use docker stop.

Configuring How Docker Restarts Containers

To specify how you want Docker to handle a container when it exits, you can use the --restart option with docker run and docker create:

--restart=always

Docker always attempts to restart the container when the container exits.

--restart=no

Docker does not attempt to restart the container when the container exits. This is the default policy.

--restart=on-failure[:max-retry ]

Docker attempts to restarts the container if the container returns a non-zero exit code. You can optionally specify the maximum number of times that Docker will try to restart the container.

Controlling Capabilities and Making Host Devices Available to Containers

If you specify the --privileged=true option to docker create or docker run, the container has access to all the devices on the host, which can present a security risk. For more precise control, you can use the --cap-add and --cap-drop options to restrict the capabilities of a container, for example:

[root@host ~]# docker run --cap-add=ALL --cap-drop=NET_ADMIN -i -t --rm oraclelinux:7
[root@guest /]# ip route del default
RTNETLINK answers: Operation not permitted

This example grants all capabilities except NET_ADMIN to the container so that it is not able to perform network-administration operations. For more information, see the capabilities(7) manual page.

To make only individual devices on the host available to a container, you can use the --device option with docker run and docker create:

--device=host_devname [:container_devname [:permissions ]]

host_devname is the name of the host device.

container_devname is an optional name for the name of the device in the container.

permissions optionally specifies the permissions that the container has on the device, which is a combination of the following codes:

m

Grants mknod permission. For example, you can use mknod to set permission bits or the SELinux context for the device file.

r

Grants read permission.

w

Grants write permission. For example, you can use a command such as mkfs to format the device.

For example, --device=/dev/sdd:/dev/xvdd:r would make the host device /dev/sdd available to the container as the device /dev/xvdd with read-only permission.

Attention:

Do not make block devices that can easily be removed from the system available to untrusted containers.

Accessing the Host's Process ID Namespace

You can make the host's process ID namespace visible from inside a container by specifying the --pid=host option to docker run. A suggested use of this mode is to debug host processes by using containerized debugging tools.

Attention:

Host mode is inherently insecure as it gives a container full access to D-Bus and other system services on the host.

Mounting a Host's root File System in Read-Only Mode

You can mount the host's root file system in read-only mode from a container by specifying the --read-only=true option to docker create or docker run. You can use this mode to restrict write access by a containerized application.

Creating a Docker Image From an Existing Container

If you modify the contents of a container, you can use the docker commit command to save the current state of the container as an image.

The following example demonstrates how to modify a container based on the oraclelinux:7-slim image so that it can run an Apache HTTP server. After stopping the container, the image mymod/httpd:v1 is created from it.

Tip:

The oraclelinux:7-slim and oraclelinux:8-slim images provide the bare minimum operating system required for Oracle Linux 7 and Oracle Linux 8. Using these images can help to reduce resource usage when running containers based on them. You can also ensure that the image that you create is limited to the base requirements for your application.

To create an Apache server image from an oraclelinux:7-slim container:

  1. Run the bash shell inside a container named httpd1:

    docker run -i -t --name httpd1 oraclelinux:7-slim /bin/bash
    [root@httpd1 ~]#
  2. If you use a web proxy, edit the yum configuration on the guest as described in Oracle Linux 7: Managing Software.

  3. Install the httpd package:

    [root@httpd1 ~]# yum -y install httpd
  4. If required, create the web content to be displayed under the /var/www/html directory hierarchy on the guest.

  5. Exit the guest by simply using the exit command from within the interactive guest session:

    [root@httpd1 ~]# exit
    exit

    Or by using the docker stop command on the host:

    docker stop httpd1
  6. Create the image mymod/httpd with the tag v1 using the ID of the container that you stopped:

    docker commit -m "ol7-slim + httpd" -a "A N Other" \
      `docker ps -l -q` mymod/httpd:v1
    sha256:b03fbc3216882a25e32c92caa2e797469a1ac98e5fc90affa07263b8cb0aa799

    Use the -m and -a options to document the image and its author. The command returns the full version of the new image's ID.

    Tip:

    The docker ps -l -q command returns the ID of the last created container. We used this command in the example to obtain the ID of the container that we wanted to use to generate the image. You may, alternatively, specify the ID directly or use an alternate variation on this command to obtain the correct ID.

    If you use the docker images command, the new image now appears in the list:

    docker images        
    REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
    mymod/httpd         v1                  b03fbc321688        2 minutes ago       426MB
    oraclelinux         7-slim              c2b5cb5bcd9d        7 days ago          118MB
  7. Remove the container named httpd1.

    docker rm httpd1

You can now use the new image to create a container that works as a web server, for example:

docker run -d --name newguest -p 8080:80 mymod/httpd:v1 /usr/sbin/httpd -D FOREGROUND

The -d option runs the command non-interactively in the background and displays the full version of the unique container ID. The -p 8080:80 option maps port 80 in the guest to port 8080 on the host. You can view the port mapping by running docker ps, for example:

docker ps
CONTAINER ID  IMAGE           COMMAND                 CREATED        STATUS        PORTS                 NAMES
154f05ea464e  mymod/httpd:v1  "/usr/sbin/httpd -D …"  2 minutes ago  Up 2 minutes  0.0.0.0:8080->80/tcp  newguest

Alternately, use the docker port command, for example:

docker port newguest 80
0.0.0.0:8080

Note:

The docker ps command displays the short version of the container ID. You can use the --no-trunc option to display the long version.

The default IP address value of 0.0.0.0 means that the port mapping applies to all network interfaces on the host. You can restrict the IP addresses to which the remapping applies by using multiple -p options, for example:

docker run -d --name newguest -p 127.0.0.1:8080:80 -p 192.168.1.2:8080:80 \
  mymod/httpd:v1 /usr/sbin/httpd -D FOREGROUND               

You can view the web content served by the guest by pointing a browser at port 8080 on the host. If you access the content from a different system, you might need to allow incoming connections to the port on the host, for example:

firewall-cmd --zone=public --permanent --add-port=8080/tcp               

If you need to remove an image, use the docker rmi command:

docker rmi mymod/httpd:v1
Untagged: mymod/httpd:v1
Deleted: sha256:b03fbc3216882a25e32c92caa2e797469a1ac98e5fc90affa07263b8cb0aa799
Deleted: sha256:f10c5b69ca9c3df53412238eefac72522720bc7c1a6a8eb6d21801c23a81c126

Note:

You cannot remove the image of a running container.

In a production environment, using the docker commit command to create an image does not provide a convenient record of how you created the image so you might find it difficult to recreate an image that has been lost or become corrupted. The preferred method for creating an image is to set up a Dockerfile, in which you define instructions that allow Docker to build the image for you. See Creating a Docker Image From a Dockerfile.

Creating a Docker Image From a Dockerfile

You use the docker build command to create a Docker image from the definition contained in a Dockerfile.

The following example demonstrates how to build an image named mymod/httpd with the tag v2 based on the oraclelinux:7-slim image so that it can run an Apache HTTP server.

To create a Docker image from a Dockerfile:

  1. Make a directory where you can create the Dockerfile, for example:

    mkdir -p /var/docker_projects/mymod/httpd                     

    Note:

    You do not need to create the Dockerfile on the same system on which you want to deploy containers that you create from the image. The only requirement is that the Docker Engine can access the Dockerfile.

  2. In the new directory, create the Dockerfile, which is usually named Dockerfile. The following Dockerfile contents are specific to the example:

    # Dockerfile that modifies oraclelinux:7-slim to include an Apache HTTP server
    FROM oraclelinux:7-slim
    MAINTAINER A N Other <another@example.com>
    RUN sed -i -e '/^\[main\]/aproxy=http://proxy.example.com:80' /etc/yum.conf
    RUN yum -y install httpd
    RUN echo "HTTP server running on guest" > /var/www/html/index.html
    EXPOSE 80
    ENTRYPOINT /usr/sbin/httpd -D FOREGROUND

    The # prefix in the first line indicates that the line is a comment. The remaining lines start with the following instruction keywords that define how Docker creates the image:

    ENTRYPOINT

    Specifies the command that a container created from the image always runs. In this example, the command is /usr/sbin/httpd -D FOREGROUND, which starts the HTTP server process.

    EXPOSE

    Defines that the specified port is available to service incoming requests. You can use the -p or -P options with docker run to map this port to another port on the host. Alternatively, you can use the --link option with docker run to allow another container to access the port over Docker's internal network (see Communicating Between Docker Containers).

    FROM

    Defines the image that Docker uses as a basis for the new image.

    MAINTAINER

    Defines who is responsible for the Dockerfile.

    RUN

    Defines the commands that Docker runs to modify the new image. In the example, the RUN lines set up the web proxy, install the httpd package, and create a simple home page for the server.

    For more information about other instructions that you can use in a Dockerfile, see https://docs.docker.com/engine/reference/builder/.

  3. Use the docker build command to create the image :

    docker build --tag="mymod/httpd:v2" /var/docker_projects/mymod/httpd/
    Sending build context to Docker daemon  2.048kB
    Step 1/6 : FROM oraclelinux:7-slim
    Trying to pull repository docker.io/library/oraclelinux ... 
    7-slim: Pulling from docker.io/library/oraclelinux
    a8d84c1f755a: Pull complete 
    Digest: sha256:d574213fa96c19ae00269730510c4d81a9979ce2a432ede7a62b62d594cc5f0b
    Status: Downloaded newer image for oraclelinux:7-slim
     ---> c3d869388183
    Step 2/6 : MAINTAINER A N Other <another@example.com>
     ---> Running in 26b0ba9f45e8
    Removing intermediate container 26b0ba9f45e8
     ---> f399f426b849
    Step 3/6 : RUN yum -y install httpd
     ---> Running in d75a9f312202
    Loaded plugins: ovl
    Resolving Dependencies
    --> Running transaction check
    ---> Package httpd.x86_64 0:2.4.6-88.0.1.el7 will be installed
    ...
    Complete!
    Removing intermediate container d75a9f312202
     ---> aa3ab87bcae3
    Step 4/6 : RUN echo "HTTP server running on guest" > /var/www/html/index.html
     ---> Running in dddedfc56849
    Removing intermediate container dddedfc56849
     ---> 8fedc8516013
    Step 5/6 : EXPOSE 80
     ---> Running in 6775d6e3996f
    Removing intermediate container 6775d6e3996f
     ---> 74a960cf0ae9
    Step 6/6 : ENTRYPOINT /usr/sbin/httpd -D FOREGROUND
     ---> Running in 8b6e6f61a2c7
    Removing intermediate container 8b6e6f61a2c7
     ---> b29dea525f0a
    Successfully built b29dea525f0a
    Successfully tagged mymod/httpd:v2

Having built the image, you can test it by creating a container instance named httpd2:

docker run -d --name httpd2 -P mymod/httpd:v2

Note:

You do not need to specify /usr/sbin/httpd -D FOREGROUND as this command is now built into the container.

The -P option specifies that Docker should map the ports exposed by the guest to a random available high-order port (higher than 30000) on the host.

You can use docker inspect to return the host port that Docker maps to TCP port 80:

docker inspect --format='{{ .NetworkSettings.Ports }}' httpd2
map[80/tcp:[map[HostIp:0.0.0.0 HostPort:49153]]]

In this example, TCP port 80 in the guest is mapped to TCP port 49153 on the host.

You can view the web content served by the guest by pointing a browser at port 49153 on the host. If you access the content from a different system, you might need to allow incoming connections to the port on the host.

You can open the port by updating the firewall:

firewall-cmd --add-port=49153/tcp
firewall-cmd --permanent --add-port=49153/tcp

You can also use curl to test that the server is working:

curl http://localhost:49153
HTTP server running on guest

Creating Multi-Stage Docker Image Builds

From Oracle Container Runtime for Docker 17.06, it is possible to perform multi-stage builds from a single Dockerfile. This allows you to perform interim build or compilation steps during the creation of the final image, without including all of the build tools and artifacts in the final image. This helps to reduce image sizes, and improves performance. It also allows you to deliver an image containing only the required binary and not all of the layers that were required to produce the binary.

In this section, we provide a very simple example scenario, where the source of a program is built in an interim compiler image and the resulting binary is copied into a separate image to produce the final target image. This entire build is handled by a single Dockerfile.

Create a simple "hello world" style program in C, by pasting the following text into a file named hello.c:

#include <stdio.h>

int
main (void)
{
  printf ("Hello, world!\n");
  return 0;
}

Create a Dockerfile that contains the following text:

FROM gcc AS BUILD
COPY . /usr/src/hello
WORKDIR /usr/src/hello
RUN gcc -Wall hello.c -o hello

FROM oraclelinux:7-slim
COPY --from=BUILD /usr/src/hello/hello hello
CMD ["./hello"]

Note that there are two FROM lines in this Dockerfile. The first FROM statement pulls the latest gcc image from the Docker hub and uses the AS syntax to assign it a name that we can refer to later when copying elements from this temporary build environment to our target image.

In the build environment, the source file is copied into the image and the gcc compiler is run against the source file to produce a hello binary.

The second FROM statement pulls the oraclelinux:7-slim image. This image is used to host the hello binary, which is copied into it directly from the build environment. By doing this, the source, the compiler and any other build artifacts can be excluded from the final image.

To build the new image and run it, try running the following:

docker build -t hello-world ./
Sending build context to Docker daemon  35.38MB
Step 1/7 : FROM gcc AS BUILD
 ---> 7d9419e269c3
Step 2/7 : COPY . /usr/src/hello
 ---> ee7310cc4464
Removing intermediate container 1d51e6f16833
Step 3/7 : WORKDIR /usr/src/hello
 ---> 2c0298733ba0
Removing intermediate container 46a09ccc06d6
Step 4/7 : RUN gcc -Wall hello.c -o hello
 ---> Running in f003deeebc20
 ---> 67c85367cac1
Removing intermediate container f003deeebc20
Step 5/7 : FROM oraclelinux:7-slim
 ---> da5e55a16f7a
Step 6/7 : COPY --from=BUILD /usr/src/hello/hello hello
 ---> 8bd284b0d7eb
Removing intermediate container d71eee578325
Step 7/7 : CMD ./hello
 ---> Running in d6051d9e0a9d
 ---> dac5aa2d651d
Removing intermediate container d6051d9e0a9d
Successfully built dac5aa2d651d
Successfully tagged hello-world:latest
docker run hello-world
Hello, world!

The hello-world image is generated to contain and run the hello binary, but doesn't contain any of the components that were required to build the binary. The final image has less layers, is smaller and excludes any of the build steps in its history.

About Docker Networking

The Docker networking features allow you to create secure networks of web applications that can communicate while running in separate containers. By default, Docker configures two types of network (as displayed by the docker network ls command):

host

If you specify the --net=host option to the docker create or docker run commands, Docker uses the host's network stack for the container. The network configuration of the container is the same as that of the host and the container shares the service ports that are available to the host. This configuration does not provide any network isolation for a container.

bridge

By default, Docker attaches containers to a bridge network named bridge. When you run a command such as ip link show on the host, the bridge is visible as the docker0 network interface. You can use the bridge network to connect separate application containers. The docker network inspect bridge command allows you to examine the network configuration of the bridge, which is displayed in JSON format. Docker sets up a default subnet address, network mask, and gateway for the bridge network and automatically assigns subnet addresses to containers that you add to the bridge network. Containers on the default bridge network can communicate with each other on this network directly, although there is domain name resolution within this network to make containers specifically aware of each other.

A container can communicate with other containers on a bridge network but not with other networks unless you also attach it to those networks. To define the networks that a container should use, specify a --net= bridge-network-name option for each network to the docker create or docker run commands. To attach a running container to a network, you can use the docker network connect network-name container-name command.

You can use the docker network create --driver bridge bridge-network-name command to create user-defined bridge networks that expose container network ports that can be accessed by external networks and other containers. You specify --net= bridge-network-name to docker create or docker run to attach the container to this network. More information on user-defined networking is provided in Communicating Between Docker Containers.

About Multihost Networking

A bridge network provides network isolation but it limits container connections to a single host system unless you use a complex user-defined bridge. Docker includes the VXLAN-based overlay network driver that supports multihost networking, where you can attach separate application containers running on multiple Docker hosts to the same virtual overlay network. Before you can create an overlay network, you must configure a key-value (KV) service such as Consul, Etcd, or ZooKeeper that the Docker hosts can access to share configuration information. You can then configure the Docker daemon on each host to access the KV server by specifying appropriate values to the –cluster-advertise and --cluster-store options. Next you use the docker network create -driver overlay multihost-network-name command on one of the hosts to create the overlay network. Having created the overlay network, you can attach the container to this network by specifying --net= multihost-network-name to docker create or docker run.

For more information, see https://docs.docker.com/engine/userguide/networking/.

Communicating Between Docker Containers

All containers are automatically added to the default bridge network and assigned IP addresses by the Docker Engine. This means that containers are effectively able to communicate directly using the bridge network. However there is no automatic service discovery on the default bridge network. If containers need to be able to resolve IP addresses by container name, you should use a user-defined network instead.

You can use the --link option with docker run to make network connection information about a server container available to a client container. For example to link a client container, client1, to a server container, httpd_server, you could run:

docker run --rm -t -i --name client1 --link http-server:server oraclelinux /bin/bash       

The client container uses a private networking interface to access the exposed port in the server container. Docker sets environment variables about the server container in the client container that describe the interface and the ports that are available. The server container name and IP address are also set in /etc/hosts in the client container, to facilitate easy access.

The --link option is considered a legacy feature and may be deprecated in future releases. It is not recommended in most cases.

The preferred approach to setting up communications between containers is to create user-defined networks. These networks provide better isolation and can perform DNS resolution of container names to IP addresses. A variety of network drivers are available, but the most commonly used is the bridged network which behaves similarly to the default bridge network but which provides additional features.

The following example shows how to create a simple user-defined network bridge and how to connect containers to it, to allow them to communicate easily with each other.

  1. Create a network using the bridge driver.

    docker network create --driver bridge http_network

    In the example, the network is named http_network.

    You can check that the network has been created and which driver it is using:

    docker network ls
    NETWORK ID          NAME                DRIVER              SCOPE
    094c50739e14        bridge              bridge              local
    7eff8115af9a        host                host                local
    4a03450bf054        http_network        bridge              local
    457c4070f5a2        none                null                local

    You can also inspect the network object to discover more information:

    docker network inspect http_network
    [
        {
            "Name": "http_network",
            "Id": "4a03450bf054a6d4d4db52da36eab8d934d35bf961b3b3adb4fe20be54c0fdac",
            "Created": "2019-02-06T04:40:47.177691733-08:00",
            "Scope": "local",
            "Driver": "bridge",
            "EnableIPv6": false,
            "IPAM": {
                "Driver": "default",
                "Options": {},
                "Config": [
                    {
                        "Subnet": "172.18.0.0/16",
                        "Gateway": "172.18.0.1"
                    }
                ]
            },
            "Internal": false,
            "Attachable": false,
            "Ingress": false,
            "ConfigFrom": {
                "Network": ""
            },
            "ConfigOnly": false,
            "Containers": {},
            "Options": {},
            "Labels": {}
        }
    ]
  2. Connect existing containers to the user-defined network.

    docker network connect http_network http-server
    docker network connect http_network client1

    In this example, http-server and client1 are existing containers that are connected to the newly created http_network bridge network.

  3. Connect a new container to the user-defined network, using the --network option.

    docker run --rm -t -i --name client2 --network http_network oraclelinux:7 /bin/bash 

    You can check that domain name resolution is working from within the container by pinging any other container on the network by its container name:

    [root@client1 ~]# ping -c 1 http-server
                            
    PING http-server (172.18.0.2) 56(84) bytes of data.
    64 bytes from http-server.http_network (172.18.0.2): icmp_seq=1 ttl=64 time=0.162 ms
    
    --- http-server ping statistics ---
    1 packets transmitted, 1 received, 0% packet loss, time 0ms
    rtt min/avg/max/mdev = 0.162/0.162/0.162/0.000 ms

    You can access services on containers within the network using their container names. For example:

    [root@client1 ~]# curl http://http-server
    HTTP server running on guest

For more information, see https://docs.docker.com/engine/userguide/networking/.

Accessing External Files From Docker Containers

You can use the -v option with docker run to make a file or file system available inside a container. The following example demonstrates how to make web pages on the host available to an HTTP server running in a container.

Create the file /var/www/html/index.html on the host and run an HTTP server container that mounts this file:

echo "This text was created in a file on the host" > /var/www/html/index.html
docker run -d --name newguest3 -P \
  -v /var/www/html/index.html:/var/www/html/index.html:ro mymod/httpd:v2

The :ro modifier specifies that a container mounts a file or file system read-only. To mount a file or file system read-writable, specify the :rw modifier instead or omit the modifier altogether.

Check that the HTTP server is not running on the host:

[root@host ~]# curl http://localhost
curl: (7) couldn't connect to host
[root@host ~]# systemctl status httpd
httpd is stopped

Even though an HTTP server is not running directly on the host, you can display the new web page served by the newguest3 container:

docker inspect --format='{{ .NetworkSettings.Ports }}' newguest3
map[80/tcp:[map[HostIp:0.0.0.0 HostPort:49153]]]
[root@host ~]# curl http://localhost:49153
This text was created in a file on the host

Any changes that you make to the /var/www/html/index.html file on the host are reflected in the mounted file in the container:

echo "Change the file on the host" > /var/www/html/index.html 
[root@host ~]# curl http://localhost:49153
Change the file on the host

Even if you delete the file on the host, it is still visible in the container:

rm -f /var/www/html/index.html 
[root@host ~]# ls -l /var/www/html/index.html
ls: cannot access /var/www/html/index.html: No such file or directory
[root@host ~]# curl http://localhost:49153
Change the file on the host

It is not possible to use a Dockerfile to define how to mount a file or file system from a host. Docker applications are intended to be portable and it is unlikely that a file or file system that exists on the original host would be available on another system. If you want external file data to be portable, you can encapsulate it in a data volume container. See Creating and Using Data Volume Containers.

Creating and Using Data Volume Containers

If you specify a single directory argument to the -v option of docker run, Docker creates the directory in the container and marks it as a data volume that other containers can mount. You can also use the VOLUME instruction in a Dockerfile to create this data volume in an image. A container that contains such a data volume is called a data volume container. After populating the data volume with files, you can use the --volumes-from option of docker run to have other containers mount the volume and access its data.

Note:

When you use docker rm to remove a container that has associated data volumes, specify the -v option to remove these volumes. Unassociated volumes waste disk space and are difficult to remove.

The following example creates a data volume container that an HTTP server container can use as the source of its web content.

To create a data volume container image and an instance of a data volume container from this image:

  1. Make a directory where you can create the Dockerfile for the data volume container image, for example:

    mkdir -p /var/docker_projects/mymod/dvc                     
  2. In the new directory, create a Dockerfile named Dockerfile that defines the image for a data volume container:

    # Dockerfile that modifies oraclelinux:7-slim to create a data volume container
    FROM oraclelinux:7-slim
    MAINTAINER A N Other <another@example.com>
    RUN mkdir -p /var/www/html
    RUN echo "This is the content for file1.html" > /var/www/html/file1.html
    RUN echo "This is the content for file2.html" > /var/www/html/file2.html
    RUN echo "This is the content for index.html" > /var/www/html/index.html
    VOLUME /var/www/html
    ENTRYPOINT /usr/bin/tail -f /dev/null

    The RUN instructions create a /var/www/html directory that contains three simple files.

    The VOLUME instruction makes the directory available as a volume that other containers can mount by using the --volumes-from option to docker run.

    The ENTRYPOINT instruction specifies the command that a container created from the image always runs. To prevent the container from exiting, the /usr/bin/tail -f /dev/null command blocks until you use a command such as docker stop dvc1 to stop the container.

  3. Use the docker build command to create the image:

    docker build --tag="mymod/dvc:v1" /var/docker_projects/mymod/dvc/
    Sending build context to Docker daemon  2.048kB
    Step 1/8 : FROM oraclelinux:7-slim
     ---> c2b5cb5bcd9d
    Step 2/8 : MAINTAINER A N Other <another@example.com>
     ---> Running in 56c7b79c246e
    Removing intermediate container 56c7b79c246e
     ---> 620ff82e21cb
    Step 3/8 : RUN mkdir -p /var/www/html
     ---> Running in ac91306f3d74
    Removing intermediate container ac91306f3d74
     ---> 379c58d9eab9
    Step 4/8 : RUN echo "This is the content for file1.html" > /var/www/html/file1.html
     ---> Running in 981773ba0210
    Removing intermediate container 981773ba0210
     ---> 2ee97d83b582
    Step 5/8 : RUN echo "This is the content for file2.html" > /var/www/html/file2.html
     ---> Running in 36e8550c9a8b
    Removing intermediate container 36e8550c9a8b
     ---> 4ba8d28df981
    Step 6/8 : RUN echo "This is the content for index.html" > /var/www/html/index.html
     ---> Running in 6f15a403b4f6
    Removing intermediate container 6f15a403b4f6
     ---> 550bb92c154b
    Step 7/8 : VOLUME /var/www/html
     ---> Running in 1806e5d6e643
    Removing intermediate container 1806e5d6e643
     ---> 0e3de4ac4c9c
    Step 8/8 : ENTRYPOINT /usr/bin/tail -f /dev/null
     ---> Running in 6cde4f965504
    Removing intermediate container 6cde4f965504
     ---> 5e4e2780503b
    Successfully built 5e4e2780503b
    Successfully tagged mymod/dvc:v1
  4. Create an instance of the data volume container, for example dvc1:

    docker run -d --name dvc1 mymod/dvc:v1 tail -f /dev/null

To test that other containers can mount the data volume (/var/www/html) from dvc1, create a container named websvr that runs an HTTP server and mounts its data volume from dvc1.

docker run -d --volumes-from dvc1 --name websvr -P mymod/httpd:v2

After finding out the correct port to use on the host, use curl to test that websvr correctly serves the content of all three files that were set up in the image. For example:

[root@host ~]# docker port websvr 80
0.0.0.0:32769
[root@host ~]# curl http://localhost:32769
This is the content for index.html
[root@host ~]# curl http://localhost:32769/file1.html
This is the content for file1.html
[root@host ~]# curl http://localhost:32769/file2.html
This is the content for file2.html

Moving Data Between Docker Containers and the Host

You can use the -v option of docker run to copy volume data between a data volume container and the host. For example, you might want to back up the data so that you can restore it to the same data volume container or to copy it to a different data volume container.

The examples in this section assume that Docker is running two instances of the data volume container image mymod/dvc:v1 that is described in Creating and Using Data Volume Containers. You can use the following commands to start these containers:
docker run -d --name dvc1 mymod/dvc:v1
docker run -d --name dvc2 mymod/dvc:v1                 
To copy the data from a data volume to the host, mount the volume from another container and use the cp command to copy the data to the host, for example:
docker run --rm --volumes-from dvc1 -v /var/tmp:/host:rw oraclelinux:7-slim \
   cp -r /var/www/html /host/dvc1_files

The container mounts the host directory /var/tmp read-writable as /host, mounts all the volumes, including /var/www/html, that dvc1 exports, and copies the file hierarchy under /var/www/html to /host/dvc1_files, which corresponds to /var/tmp/dvc1_files on the host.

To copy the backup of dvc1's data from the host to another data volume container dvc2, use a command such as the following:

docker run --rm --volumes-from dvc2 -v /var/tmp:/host:ro \
  oraclelinux:7-slim cp -a -T /host/dvc1_files /var/www/html               

The container mounts the host directory /var/tmp read-only as /host, mounts the volumes exported by dvc2, and copies the file hierarchy under /host/dvc1_files (/var/tmp/dvc1_files on the host) to /var/www/html, which corresponds to a volume that dvc2 exports.

You could also use a command such as tar to back up and restore the data as a single archive file, for example:

docker run --rm --volumes-from dvc1 -v /var/tmp:/host:rw \
  oraclelinux:7-slim tar -cPvf /host/dvc1_files.tar /var/www/html
ls -l /var/tmp/dvc1_files.tar
-rw-r--r--. 1 root root 10240 Aug 31 14:37 /var/tmp/dvc1_files.tar
docker run --rm --volumes-from dvc2 -i -t --name guest -v /var/tmp:/host:ro \
  oraclelinux:7-slim /bin/bash

On the guest, run the following commands:

[root@guest ~]# rm /var/www/html/*.html
[root@guest ~]# ls -l /var/www/html/*.html
total 0
[root@guest ~]# tar -xPvf /host/dvc1_files.tar
var/www/html/
var/www/html/file1.html
var/www/html/file2.html
var/www/html/index.html
[root@guest ~]# ls -l /var/www/html
total 12
-rw-r--r--. 1 root root 35 Aug 30 09:02 file1.html
-rw-r--r--. 1 root root 35 Aug 30 09:03 file2.html
-rw-r--r--. 1 root root 35 Aug 30 09:03 index.html
[root@guest ~]# exit
exit

This example uses a transient, interactive container named guest to extract the contents of the archive to dvc2.

Using Labels to Define Metadata

You can use labels to add metadata to the Docker daemon and to Docker containers and images. In the Dockerfile, a LABEL instruction defines an image label that can contain one or more key-value pairs, for example:

LABEL com.mydom.dept="ITGROUP" \
      com.mydom.version="1.0.0-ga" \
      com.mydom.is-final \
      com.mydom.released="June 6, 2015"

In this example, each key name is prefixed by the domain name in reverse DNS form (com.mydom.) to guard against name-space conflicts. Key values are always expressed as strings and are not interpreted by Docker. If you omit the value, you can use the presence or absence of the key in the metadata to encode information such as the release status. The backslash characters allow you to extend the label definition across several lines.

You can use the docker inspect command to display the labels that are associated with an image, for example:

docker inspect 7ac15076dcc1
...
"Labels": {
    "com.mydom.dept": "ITGROUP",
    "com.mydom.version": "1.0.0-ga",
    "com.mydom.is-final": "",
    "com.mydom.release-date": "June 6, 2015"
}
...

You can use the --filter "label=key [=value ]" option with the docker images and docker ps commands to list the images and running containers on which a metadata value has been set, for example:

docker images --filter "label=com.mydom.dept='DEVGROUP'"
docker ps --filter "label=com.mydom.is-beta2"
docker ps --filter "label=env=Oracle\ Linux\ 7"            

For containers, you can use --label key=[value] options with the docker create and docker run commands to define key-value pairs, for example:

docker run -i -t --rm testapp:1.0 --label run="11" --label platform="Oracle Linux 7"            

For the Docker Engine, you can use --label key=[value] options if you start docker from the command line or edit the docker configuration file /etc/sysconfig/docker.

OPTIONS=" --label com.mydom.dept='DEVGROUP'"

Alternately, you can append these options to a list in the /etc/docker/daemon.json file, for example:

{
  "labels": ["com.mydom.dept='DEVGROUP'", "com.mydom.version='1.0.0-ga'"]
}

Note:

After adding or modifying a configuration file while the docker service is running, run the command systemctl daemon-reload to tell systemd to reload the configuration for the service.

As containers and the Docker daemon are transitory and run in a known environment, it is not usually necessary to apply reverse domain name prefixes to key names.

Defining the Logging Driver

You can use the --log-driver option with the docker create and docker run commands to specify the logging driver that a container should use:

json-file

Write log messages to a JSON file that you can examine by using the docker logs command, for example:

docker logs --follow --timestamps=false container_name                     

This is the default logging driver.

none

Disable logging.

syslog

Write log messages to syslog.

About Image Digests

Registry version 2 or later images can be identified by their digest (for example, sha256:digest_value_in_hexadecimal ). You can list the digest by specifying the --digests option to the docker images command. You can use a digest with the docker create, docker pull, docker rmi, and docker run commands and with the FROM instruction in a Dockerfile.

Specifying Control Groups for Containers

You can use the --cgroup-parent option with the docker create command to specify the control group (cgroup) in which a container should run.

Limiting CPU Usage by Containers

To control a container's CPU usage, you can use the --cpu-period and --cpu-quota options with the docker create and docker run commands.

The --cpu-quota option specifies the number of microseconds that a container has access to CPU resources during a period specified by --cpu-period. As the default value of --cpu-period is 100000, setting the value of --cpu-quota to 25000 limits a container to 25% of the CPU resources. By default, a container can use all available CPU resources, which corresponds to a --cpu-quota value of -1.

Enabling a Container to Use the Host's UTS Namespace

By default, a container runs with a UTS namespace (which defines the system name and domain) that is different from the UTS namespace of the host. To make a container use the same UTS namespace as the host, you can use the --uts=host option with the docker create and docker run commands. This setting allows the container to track the UTS namespace of the host or to set the host name and domain from the container.

Attention:

As the container has full access to the UTS namespace of the host, this feature is inherently insecure.

Setting ulimit Values on Containers

The --ulimit option to docker run allows you to specify ulimit values for a container, for example:

docker run -i -t --rm myapp:2.0 --ulimit nofile=128:256 --ulimit nproc=32:64            

This example sets a soft limit of 128 open files and 32 child processes and a hard limit of 256 open files and 64 child processes on the container.

You can set default ulimit values for all containers by specifying default-ulimits options in a /etc/docker/daemon.json configuration file, for example:

"default-ulimits": {
  "nofile": {
      "Name": "nofile",
      "Hard": 128,
      "Soft": 256
    },
    "nproc" : {
      "Name": "nproc",
      "Hard": 32,
      "Soft": 64
    }
},

Note:

After adding or modifying the configuration file while the docker service is running, run the command systemctl daemon-reload to tell systemd to reload the configuration for the service.

Any ulimit values that you specify for a container override the default values that you set for the daemon.

Building Images With Resource Constraints

You can specify cgroup resource constraints to docker build, for example:

docker build --cpu-shares=100 --memory=1024m \
   --tag="mymod/myapp:1.0" /var/docker_projects/mymod/myapp/

Any containers that you generate from the image inherit these resource constraints.

You can use the docker stats command to display a container's resource usage, for example:

docker stats cntr1 cntr2
CONTAINER ID     NAME          CPU %      MEM USAGE/LIMIT     MEM %     NET I/O            BLOCK I/O     PIDS
1ab12958b915     cntr1         0.05%      504 KiB/128 MiB     0.39%     2.033 KiB/40 B     13.7MB/1MB    1 
3cf41296a324     cntr2         0.08%      1.756 MiB/128 MiB   1.37%     5.002 KiB/92 B     15.8MB/3MB    1

Committing, Exporting, and Importing Images

You can use the docker commit command to save the current state of a container to an image.

docker commit \
  [--author="name"] \
  [--change="instructions"]... \
  [--message="text"] \
  [--pause=false] container [repository[:tag] 

You can use this image to create new containers, for example to debug the container independently of the existing container.

You can use the docker export command to export a container to another system as an image tar file.

docker export [--output="filename"] container               

Note:

You need to export separately any data volumes that the container uses. See Moving Data Between Docker Containers and the Host.

To import the image tar file, use docker import and specify the image URL or read the file from the standard input.

docker import [--change="instructions"]... URL [repository[:tag]
docker import [--change="instructions"]... - [repository[:tag] < filename

You can use --change options with docker commit and docker import to specify Dockerfile instructions that modify the configuration of the image, for example:

docker commit --change "LABEL com.mydom.status='Debug'" 7ac15076dcc1 mymod/debugimage:v1      

For docker commit, you can specify the following instructions: ADD, CMD, COPY, ENTRYPOINT, ENV, EXPOSE, FROM, LABEL, MAINTAINER, RUN, USER, VOLUME, and WORKDIR.

For docker import, you can specify the following instructions: CMD, ENTRYPOINT, ENV, EXPOSE, ONBUILD, USER, VOLUME, and WORKDIR.