In my previous blog, I gave a brief introduction to Docker. In this part, we will dig deep into Docker containers.
We know that a container is the runtime instance of an image. You can start multiple containers from a single image.
Keeping aside the theory, let's play around with containers.
Understanding Docker containers commands
Let's try something exciting – download and run an NGINX container by running the following two commands:
$ docker image pull nginx # Pulling nginx image
$ docker container run -d --name nginx-test -p 8080:80 nginx # Running container
# Pulling Image
deep@Latitude-5590:~/explore-docker$ docker image pull nginx
Using default tag: latest
latest: Pulling from library/nginx
6ec7b7d162b2: Pull complete
cb420a90068e: Pull complete
2766c0bf2b07: Pull complete
e05167b6a99d: Pull complete
70ac9d795e79: Pull complete
Digest: sha256:4cf620a5c81390ee209398ecc18e5fb9dd0f5155cd82adcbae532fec94006fb9
Status: Downloaded newer image for nginx:latest
docker.io/library/nginx:latest
# Running Container
deep@Latitude-5590:~/explore-docker$ docker container run -d --name nginx-test -p 8080:80 nginx
ace004cb4246b59eef353dab9ab53b3e2bb352e8eab88b98f8fdac2ba2b911ca
deep@Latitude-5590:~/explore-docker$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ace004cb4246 nginx "/docker-entrypoint.…" 4 minutes ago Up 4 minutes 0.0.0.0:8080->80/tcp nginx-test
As you can see from our docker container run command we have three flags.
- -d stands for --detach
- --name flag assign's name to containers
- -p flag maps port
If we hadn't added -d flag, then our container would have executed in the foreground, which means that our Terminal would have been frozen until we pressed Ctrl + C. To understand this, try running the above command without -d flag and see the output of running the container in the foreground.
Once you run the command, open a browser http://localhost:9090/ . As you load the page, you will notice that your page visit is printed to the screen. Hitting refresh in your browser will display more hits, until you press Ctrl + C back in the Terminal:
# without -d flag
deep@Latitude-5590:~/explore-docker$ docker container run --name nginx-foreground -p 9090:80 nginx
172.17.0.1 - - [02/Jan/2021:12:03:48 +0000] "GET / HTTP/1.1" 200 612 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:84.0) Gecko/20100101 Firefox/84.0" "-"
2021/01/02 12:03:49 [error] 30#30: *1 open() "/usr/share/nginx/html/favicon.ico" failed (2: No such file or directory), client: 172.17.0.1, server: localhost, request: "GET /favicon.ico HTTP/1.1", host: "localhost:9090", referrer: "http://localhost:9090/"
172.17.0.1 - - [02/Jan/2021:12:03:49 +0000] "GET /favicon.ico HTTP/1.1" 404 153 "http://localhost:9090/" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:84.0) Gecko/20100101 Firefox/84.0" "-"
Running docker container ls -a or docker ps -a shows that you have two containers, one of which has exited:
deep@Latitude-5590:~/explore-docker$ docker container ls -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
61c6de3ae9e0 nginx "/docker-entrypoint.…" 10 minutes ago Exited (0) 3 seconds ago nginx-foreground
ace004cb4246 nginx "/docker-entrypoint.…" 38 minutes ago Up 38 minutes 0.0.0.0:8080->80/tcp nginx-test
So, what happened when we ran the command with and without -d flag? When we removed the detach flag, Docker connected us to the NGINX process directly within the container. When we used Ctrl + C, we sent an instruction to the NGINX process to terminate it.
The --name flag , We gave a name to container nginx-foreground using the --name flag, as we cannot have two containers with the same name. That's why the name generator functions assign a random name to containers you do not wish to name yourself.
The - p port flag, We asked Docker to map port 9090 to port 80 on the container. This was because we cannot assign more than one process to a port on a host machine, so if we attempted to launch our second container with the same port as the first, we would have received an error message as shown below.
deep@Latitude-5590:~/explore-docker$ docker container run --name nginx-foreground -p 8080:80 nginx
docker: Error response from daemon: Conflict. The container name "/nginx-foreground" is already in use by container "61c6de3ae9e025567554b4aeaa697790518f5f4b7ad392b77b2a50d3a654e741". You have to remove (or rename) that container to be able to reuse that name.
See 'docker run --help'.
Interacting with containers
EXEC
The exec command initiates a second process within the container that you can interact with. For example, to see your system hosts, we can run the following command:
deep@Latitude-5590:~/explore-docker$ docker container exec nginx-test cat /etc/hosts 127.0.0.1 localhost ::1 localhost ip6-localhost ip6-loopback fe00::0 ip6-localnet ff00::0 ip6-mcastprefix ff02::1 ip6-allnodes ff02::2 ip6-allrouters
The above command will initiate a second process, the cat command, in this case, prints the contents of /etc/hosts to stdout. The second process will then terminate, leaving the container as it is. Another practical example, which is used a lot by developers, is initiating a bash process.
deep@Latitude-5590:~/explore-docker$ docker container exec -i -t nginx-test /bin/bash root@ace004cb4246:/# ls bin dev docker-entrypoint.sh home lib64 mnt proc run srv tmp var boot docker-entrypoint.d etc lib media opt root sbin sys usr
The above command initiates a bash process, flag -i and -t flags to keep open console access to our container. The -i stands for --interactive, which instructs Docker to keep stdin open so that we can send commands to the process. The -t flag is short for --tty and allocates a pseudo-TTY to the session.
NOTE
- TTY is an acronym used to describe text-only consoles in modern computing.
- stdin Standard input (stdin) is the handle that our process reads to get information from the end-user. Standard output (stdout) is where the process writes normal information
ATTACH
Use docker attach to attach your terminal’s standard input, output, and error (or any combination of the three) to a running container using the container’s ID or name. We still have our nginx-test container running, so let's connect to that by running this command:
$ docker container attach nginx-test
# Attach deep@Latitude-5590:~/explore-docker$ docker container attach nginx-test 172.17.0.1 - - [02/Jan/2021:16:39:25 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:84.0) Gecko/20100101 Firefox/84.0" "-" 172.17.0.1 - - [02/Jan/2021:16:39:25 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:84.0) Gecko/20100101 Firefox/84.0" "-" # After Ctrl + C deep@Latitude-5590:~/explore-docker$ docker container ls CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Opening your browser and going to localhost:8080 will print the NGINX access logs to the screen. Pressing Ctrl + C will terminate the process and return your Terminal to normal and you will see there are no running containers.
Start the container again using command $ docker container start nginx-test. This will start the container back up in the initial launched state. Let's reattach to our process, but this time with an additional option - $ docker container attach --sig-proxy=false nginx-test
# Restarting Container deep@Latitude-5590:~/explore-docker$ docker container start nginx-test nginx-test # Reattaching with --sig-proxy=false deep@Latitude-5590:~/explore-docker$ docker container attach --sig-proxy=false nginx-test 172.17.0.1 - - [02/Jan/2021:16:51:13 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:84.0) Gecko/20100101 Firefox/84.0" "-" 172.17.0.1 - - [02/Jan/2021:16:51:13 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:84.0) Gecko/20100101 Firefox/84.0" "-" # After Ctrl + C deep@Latitude-5590:~/explore-docker$ docker container ls CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES ace004cb4246 nginx "/docker-entrypoint.…" 5 hours ago Up 30 seconds 0.0.0.0:8080->80/tcp nginx-test
Hitting the container's URL a few times and then pressing Ctrl + C will detach us from the NGINX process, but this time, rather than terminating the NGINX process, it will just return us to our Terminal.
LOGS
The logs command is pretty self-explanatory. For example, to view the last entries written to stdout for our nginx-test container, you can use the following command:
$ docker container logs --tail 5 nginx-test
deep@Latitude-5590:~/explore-docker$ docker container logs --tail 5 nginx-test 10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf /docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh /docker-entrypoint.sh: Configuration complete; ready for start up 172.17.0.1 - - [04/Jan/2021:11:41:55 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:84.0) Gecko/20100101 Firefox/84.0" "-" 172.17.0.1 - - [04/Jan/2021:11:41:55 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:84.0) Gecko/20100101 Firefox/84.0"
To see the logs in real-time you can simply run the following command:
$ docker container logs -f nginx-test, Here -f flag stands for --follow.
To check logs for a particular DateTime you can use:
$ docker container logs --since 2020-12-31T15:52:00 -t nginx-test
TOP
The top command, lists the process running inside the container, and can be used as follows:
deep@Latitude-5590:~/explore-docker$ docker container top nginx-test UID PID PPID C STIME TTY TIME CMD root 9157 9138 0 17:10 ? 00:00:00 nginx: master process nginx -g daemon off; systemd+ 9238 9157 0 17:10 ? 00:00:00 nginx: worker process
STATS
The stats command provides real-time information.
deep@Latitude-5590:~/explore-docker$ docker container stats nginx-test CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS 108b1eb32312 nginx-test 0.00% 4.762MiB / 15.5GiB 0.03% 12.3kB / 956B 9.5MB / 8.19kB 2
Containers states and miscellaneous commands
Let's launch a few containers first, run the following command:
$ for i in {1..4}; do docker container run -d --name nginx$(printf "$i") nginx; done
This will launch 4 containers as shown below, Let's proceed further:
deep@Latitude-5590:~/explore-docker$ for i in {1..4}; do docker container run -d --name nginx$(printf "$i") nginx; done
b1083d403ff3ea43840dfdf26888dca296c7bcda68d8a65197bd02380eb43150
85cf7861d52b0fe197ac08165a5b4525be89e0b28b9216b49fa85846278041c6
05be13ad42b7de676c0b971088c211adfe9ea85fc151d0e5dc4f8e912c339513
e8453941a4b5b79ab9a29f91e3412668645d92bdb472e4cfdb49cbd9775fb3ff
deep@Latitude-5590:~/explore-docker$ docker container ls -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e8453941a4b5 nginx "/docker-entrypoint.…" 2 minutes ago Up 2 minutes 80/tcp nginx4
05be13ad42b7 nginx "/docker-entrypoint.…" 2 minutes ago Up 2 minutes 80/tcp nginx3
85cf7861d52b nginx "/docker-entrypoint.…" 2 minutes ago Up 2 minutes 80/tcp nginx2
b1083d403ff3 nginx "/docker-entrypoint.…" 2 minutes ago Up 2 minutes 80/tcp nginx1
108b1eb32312 nginx "/docker-entrypoint.…" About an hour ago Up About an hour 0.0.0.0:8080->80/tcp nginx-test
PAUSE AND UNPAUSE
Let's see how pause works. To do this, simply run the following:
$ docker container pause nginx2
deep@Latitude-5590:~/explore-docker$ docker container pause nginx2 nginx2 deep@Latitude-5590:~/explore-docker$ docker container ls CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 52d8b8e0c0f8 nginx "/docker-entrypoint.…" 10 minutes ago Up 10 minutes 80/tcp nginx5 e8453941a4b5 nginx "/docker-entrypoint.…" 10 minutes ago Up 10 minutes 80/tcp nginx4 05be13ad42b7 nginx "/docker-entrypoint.…" 10 minutes ago Up 10 minutes 80/tcp nginx3 85cf7861d52b nginx "/docker-entrypoint.…" 10 minutes ago Up 10 minutes (Paused) 80/tcp nginx2 b1083d403ff3 nginx "/docker-entrypoint.…" 10 minutes ago Up 10 minutes 80/tcp nginx1 108b1eb32312 nginx "/docker-entrypoint.…" About an hour ago Up About an hour 0.0.0.0:8080->80/tcp nginx-test
You can see above, container nginx2 has been suspended or paused by cgroups freezer. Similarly to resume a paused container use:
$ docker container unpause nginx2
STOP, START, RESTART, and KILL
We are already familiar with start command. The stop command is similar to Ctrl + C.
$ docker container stop nginx2
On running this command, a request called SIGTERM is sent to terminate the process. If the process has not terminated itself within a grace period i.e 60 sec (default). This will immediately terminate the process, not giving any time to finish the running task. This can create a problem, You can override the grace period by using -t flag (--time). For example, wait up to 60 sec before killing the process.
$ docker container stop -t 60 nginx3
deep@Latitude-5590:~/explore-docker$ docker container stop -t 60 nginx3 nginx3 deep@Latitude-5590:~/explore-docker$ docker container ls CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 52d8b8e0c0f8 nginx "/docker-entrypoint.…" 33 minutes ago Up 33 minutes 80/tcp nginx5 e8453941a4b5 nginx "/docker-entrypoint.…" 33 minutes ago Up 33 minutes 80/tcp nginx4 85cf7861d52b nginx "/docker-entrypoint.…" 33 minutes ago Up 33 minutes 80/tcp nginx2 b1083d403ff3 nginx "/docker-entrypoint.…" 33 minutes ago Up 33 minutes 80/tcp nginx1 108b1eb32312 nginx "/docker-entrypoint.…" 2 hours ago Up 2 hours 0.0.0.0:8080->80/tcp nginx-test deep@Latitude-5590:~/explore-docker$ docker container start nginx3 nginx3
The restart command stops and starts the container. Also, as with stop, you can pass the -t flag.
$ docker container restart -t 60 nginx4
The kill command kills the container immediately sending SIGKILL signal.
$ docker container kill nginx5
REMOVING CONTAINERS
The command docker container ls -a will show the status of all containers including the Exited once. To remove the exited containers, you can use prune command:
$ docker container prune
deep@Latitude-5590:~/explore-docker$ docker container ls -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 52d8b8e0c0f8 nginx "/docker-entrypoint.…" 51 minutes ago Up 51 minutes 80/tcp nginx5 e8453941a4b5 nginx "/docker-entrypoint.…" 51 minutes ago Exited (0) 2 seconds ago # prune deep@Latitude-5590:~/explore-docker$ docker container prune WARNING! This will remove all stopped containers. Are you sure you want to continue? [y/N] y Deleted Containers: e8453941a4b5b79ab9a29f91e3412668645d92bdb472e4cfdb49cbd9775fb3ff Total reclaimed space: 1.114kB
Sometimes, removing a container will not work as it is still running. If we want to force a removal, no matter what the condition of the container currently is, we can use the command-line parameter -f or --force.
$ docker container rm -f /container name or ID/ ==> Avoid this, As it doesn’t bother asking nicely with a SIGTERM, it goes straight to the SIGKILL. You’re literally giving the container, and the app it’s running, no chance to complete any operation and gracefully exit.
Thank you for your time.