Run#

Here are some different options associated with running Docker containers.

Ports#

To be able to communicate with docker containers from outside the environment, you need to bind ports on the host to the ports on the container. To do this, use -p <port in the host>:<port in the container> when starting the container.


The next cell starts the container with nc listening on port 999 port. At the same time container maps port 888 of the host to the port 999 of the container.

docker run -itd -p 888:999 --name port_example alpine sh -c "
apk update && apk add netcat-openbsd
nc -w 1 -l 0.0.0.0 999"
sleep 3 # wait some time to install netcat
9b38436b8510279cd380b59999b8f8517004753d955631fb9320be489a630415

The fact that container corresponds to port 888 on the host to the and port 999 on the container is displayed in the PORTS field of the docker ps command. The following cell shows it, and 888 -> 999 means that port 888 on the host corresponds to the port 999 on the host.

docker ps
CONTAINER ID   IMAGE     COMMAND                   CREATED         STATUS         PORTS                                     NAMES
9b38436b8510   alpine    "sh -c '\napk update …"   9 seconds ago   Up 9 seconds   0.0.0.0:888->999/tcp, [::]:888->999/tcp   port_example

The next code throws a message to the localhost 888.

echo "message to the conatiner" | nc localhost 888

The next cell shows the last line from the container logs - it corresponds to the message sent by the host.

docker logs port_example | tail -n 1
docker rm port_example &> /dev/null
message to the conatiner

Environment variables#

Some tools’ configurations use environment variables, so if you are using such tools, you’ll need to pass environment variables to the container. There are two options in the docker run command that allow you to manage environment variables:

  • --env (-e): allows setting a variable and its value directly from the CLI. You should use the syntax -e <variable_name>=<value>.

  • --env-file: allows loading variables from a specific .env file.


The following cell shows the usage of the -e option — it creates a Docker container with the variable TOY_VARIABLE in it. As proof that the variable was actually created, its value was printed from the container.

docker run --rm -dit \
    --name env_example \
    -e TOY_VARIABLE="hello from toy variable" alpine &> /dev/null
docker exec env_example sh -c "echo \$TOY_VARIABLE"
docker stop env_example &> /dev/null
hello from toy variable

Now let’s try the same with variables defined in the .env file — just like in the following file:

cat << EOF > env_values
VAL1=10
VAL2="text value"
EOF

The following container loads the file we defined earlier and then displays the variables mentioned in the file — everything works well.

docker run --rm -dit \
    --name env_file_example \
    --env-file env_values \
    alpine &> /dev/null
docker exec env_file_example sh -c "echo \$VAL1 \$VAL2"
docker stop env_file_example &> /dev/null
10 "text value"

Don’t forget to clean up the file that we used for the experiment.

rm env_values

Memory#

The -m option allows you to specify the memory limit for the container. For more detailed information, refer to the official documentation page.


The following example launches two containers: one with the default -m option and another with -m 1g. It then displays the output of the docker stats command.

docker run -itd --rm --name memory_full alpine &> /dev/null
docker run -itd --rm -m 1g --name memory_limit alpine &> /dev/null

docker stats --no-stream

docker stop memory_full memory_limit &> /dev/null
CONTAINER ID   NAME           CPU %     MEM USAGE / LIMIT   MEM %     NET I/O       BLOCK I/O   PIDS
c697ea033dd4   memory_limit   0.00%     500KiB / 1GiB       0.05%     266B / 0B     0B / 0B     1
b0c9337d2eb0   memory_full    0.00%     500KiB / 31.01GiB   0.00%     2.25kB / 0B   0B / 0B     1

Notice that in the MEM USAGE / LIMIT column for the memory_limit container, the value is 1GiB.

CPUs#

The --cpus option of the docker run command allows you to specify the number of CPU cores that will be allocated to the running Docker container.


As an example, consider an Alpine Docker container running the stress-ng --cpu 3 command, which forces the system to utilize 3 CPU cores.

The following cell runs this container without any restrictions on the number of CPUs it can utilize.

docker run -id --rm --name cpu_experiments alpine sh -c \
    "apk update && apk add stress-ng && stress-ng --cpu 3" &> /dev/nbull
sleep 3
docker stats --no-stream
docker stop cpu_experiments &> /dev/null
CONTAINER ID   NAME              CPU %     MEM USAGE / LIMIT     MEM %     NET I/O           BLOCK I/O   PIDS
a310ffc6534e   cpu_experiments   301.56%   49.14MiB / 31.01GiB   0.15%     5.77MB / 75.6kB   0B / 0B     4

In the docker stats output, we observe 300% under the CPU column, indicating that all three CPU cores are being fully utilized.

Now, we’ll run a similar example, but with the key difference of using the --cpus 2 option in the docker run command.

docker run -id --rm --name cpu_experiments --cpus 2 alpine sh -c \
    "apk update && apk add stress-ng && stress-ng --cpu 3" &> /dev/null
sleep 3
docker stats --no-stream
docker stop cpu_experiments &> /dev/null &> /dev/null
CONTAINER ID   NAME              CPU %     MEM USAGE / LIMIT     MEM %     NET I/O          BLOCK I/O   PIDS
78c586383384   cpu_experiments   200.44%   49.13MiB / 31.01GiB   0.15%     5.86MB / 138kB   0B / 0B     4

Although stress-ng tries to use 3 CPU cores, in reality docker does not allow more than 2 cores.