Bookmark this page

Managing the Container Lifecycle

Objectives

  • Manage the lifecycle of a container from creation to deletion.

Container Lifecycle

Podman provides a set of subcommands to create and manage containers. You can use those subcommands to manage the container and container image lifecycle.

The following figure shows the most commonly used Podman subcommands that change the container and image state:

Figure 13.13: Podman lifecycle commands

Podman also provides a set of subcommands to obtain information about running and stopped containers.

You can use these subcommands to extract information from containers and images for debugging, updating, or reporting purposes. The following figure shows the most commonly used subcommands that query information from containers and images:

Figure 13.14: Podman query commands

This lecture covers the basic operations that you can use to manage containers. Commands that are explained in this lecture accept either a container ID or the container name.

Listing Containers

You can list running containers with the podman ps command.

[user@host ~]$ podman ps
CONTAINER ID  IMAGE         COMMAND              CREATED STATUS PORTS       NAMES
0ae7be593698  ...server     /bin/sh -c python... ...ago  Up...  ...8000/tcp httpd
c42e7dca12d9  ...helloworld /bin/sh -c nginx...  ...ago  Up...  ...8080/tcp nginx

Each row describes information about the container, such as the image that is used to start the container, the command executed when the container started, and the container uptime.

You can include stopped containers in the output by adding the --all or -a flag to the podman ps command.

[user@host ~]$ podman ps --all
CONTAINER ID  IMAGE         COMMAND              CREATED STATUS    PORTS        NAMES
0ae7be593698  ...server     /bin/sh -c python... ...ago  Up...     ...8000/tcp  httpd
bd5ada1b6321  ...httpd-24   /usr/bin/run-http... ...ago  Exited... ...8080/tcp  upbeat...
c42e7dca12d9  ...helloworld /bin/sh -c nginx ... ...ago  Up...     ...8080/tcp  nginx

Inspecting Containers

In the context of Podman, inspecting a container means retrieving the full information of the container. The podman inspect command returns a JSON array with information about different aspects of the container, such as networking settings, CPU usage, environment variables, status, port mapping, or volumes. The following snippet is a sample output of the podman inspect command.

[user@host ~]$ podman inspect 7763097d11ab
[
    {
        "Id": "7763...cbc0",
        "Created": "2022-05-04T10:00:32.988377257-03:00",
        "Path": "container-entrypoint",
        "Args": [
            "/usr/bin/run-httpd"
        ],
        "State": {
            "OciVersion": "1.0.2-dev",
            "Status": "running",
            "Running": true,
            "Paused": false,
            "Restarting": false,
            "OOMKilled": false,
            "Dead": false,
            "Pid": 9746,
...output omitted...
        "Image": "d2b9...fa0a",
        "ImageName": "registry.access.redhat.com/ubi8/httpd-24:latest",
        "Rootfs": "",
...output omitted...
            "Env": [
                "PATH=/opt/app-root/src/bin:/opt/app-root/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                "TERM=xterm",
                "container=oci",
                "HTTPD_VERSION=2.4",
...output omitted...
            "CpuCount": 0,
            "CpuPercent": 0,
            "IOMaximumIOps": 0,
            "IOMaximumBandwidth": 0,
            "CgroupConf": null
        }
    }
]

The previous JSON output omits most fields. Because the full JSON output is long, you might find it difficult to find the specific fields that you want to retrieve. You can pass a --format flag to the podman inspect command to filter the command output. The --format flag expects a Go template expression as a parameter, which you can use to select specific fields in the JSON output.

Go template expressions use annotations, which are delimited by double curly braces ({{ and }}), to refer to elements, such as fields or keys, in a data structure. The data structure elements are separated by a dot (.) and must start in uppercase.

In the following command, the Status field of the State object is retrieved for a container called redhat.

[user@host ~]$ podman inspect \
 --format='{{.State.Status}}' redhat
running

In the preceding example, the podman inspect command uses the supplied Go template to navigate the JSON array of the redhat container. The template returns the Status field value of the State object from the JSON array.

Refer to the references section for more information about the Go templating language.

Stopping Containers Gracefully

You can stop a container gracefully by using the podman stop command. When you execute the podman stop command, Podman sends a SIGTERM signal to the container. Processes use the SIGTERM signal to implement clean-up procedures before stopping.

The following command stops a container with a container ID 1b982aeb75dd.

[user@host ~]$ podman stop 1b982aeb75dd
1b982aeb75dd

You can stop all the running containers by using the --all or -a flag. In the following example, the command stops three containers.

[user@host ~]$ podman stop --all
4aea...0c2a
6b18...54ea
7763...cbc0

If a container does not respond to the SIGTERM signal, then Podman sends a SIGKILL signal to forcefully stop the container. Podman waits 10 seconds by default before sending the SIGKILL signal. You can change the default behavior by using the --time flag.

[user@host ~]$ podman stop --time=100

In the previous example, Podman gives the container a grace period of 100 seconds before sending the killing signal.

Stopping a container differs from removing a container. Stopped containers are present on the host machine and can be listed by using the podman ps --all command.

Additionally, when the containerized process finishes, the container enters the exited state. Such a process might finish for a number of reasons, such as an error, OOM (out-of-memory) state, or successfully finishing. Podman lists exited containers with other stopped containers.

Stopping Containers Forcefully

You can send the SIGKILL signal to the container by using the podman kill command. In the following example, a container called httpd is stopped forcefully.

[user@host ~]$ podman kill httpd
httpd

Pausing Containers

Both podman stop and podman kill commands eventually send a SIGKILL signal to the container. The podman pause command suspends all processes in the container by sending the SIGSTOP signal.

[user@host ~]$ podman pause 4f2038c05b8c
4f2038c05b8c

The podman unpause command resumes a paused container.

[user@host ~]$ podman unpause 4f2038c05b8c
4f2038c05b8c

Restarting Containers

Execute the podman restart command to restart a running container. You can also use the command to start stopped containers.

The following command restarts a container called nginx.

[user@host ~]$ podman restart nginx
1b98...75dd

Removing Containers

Use the podman rm command to remove a stopped container. The following command removes a stopped container with the container ID c58cfd4b90df.

[user@host ~]$ podman rm c58cfd4b90df
c58c...90df

You cannot remove running containers by default. You must stop the running container first and then remove it. The following command tries to remove a running container.

[user@host ~]$ podman rm c58cfd4b90df
Error: cannot remove container c58c...90df as it is running - running or paused containers cannot be removed without force: container state improper

You can add the --force (or -f) flag to remove the container forcefully.

[user@host ~]$ podman rm c58cfd4b90df --force
c58c...90df

You can also add the --all (or -a) flag to remove all stopped containers. This flag fails to remove running containers. The following command removes two containers.

[user@host ~]$ podman rm --all
6b18...54ea
6c0d...a6fb

You can combine the --force and --all flags to remove all containers, including running containers.

Container Persistent Storage

By default, when you run a container, all of the content uses the container-based image. Given the ephemeral nature of container images, all of the new data that the user or the application writes is lost after removing the container.

To make data persistent, you can use host file-system content in the container with the --volume (-v) option. You must consider file-system level permissions when you use this volume type in a container.

In the MariaDB container image, the mysql user must own the /var/lib/mysql directory, the same as if MariaDB was running on the host machine. The directory to mount into the container must have mysql as the user and group owner (or the UID and GID of the mysql user, if MariaDB is not installed on the host machine). If you run a container as the root user, then the UIDs and GIDs on your host machine match the UIDs and GIDs inside the container.

The UID and GID matching configuration does not occur the same way in a rootless container. In a rootless container, the user has root access from within the container, because Podman launches a container inside the user namespace.

You can use the podman unshare command to run a command inside the user namespace. To obtain the UID mapping for your user namespace, use the podman unshare cat command.

[user@host ~]$ podman unshare cat /proc/self/uid_map
         0       1000          1
         1     100000      65536
[user@host ~]$ podman unshare cat /proc/self/gid_map
         0       1000          1
         1     100000      65536

The preceding output shows that in the container, the root user (UID and GID of 0) maps to your user (UID and GID of 1000) on the host machine. In the container, the UID and GID of 1 maps to the UID and GID of 100000 on the host machine. Every UID and GID after 1 increments by 1. For example, the UID and GID of 30 inside a container maps to the UID and GID of 100029 on the host machine.

You use the podman exec command to view the mysql user UID and GID inside the container that is running with ephemeral storage.

[user@host ~]$ podman exec -it db01 grep mysql /etc/passwd
mysql:x:27:27:MySQL Server:/var/lib/mysql:/sbin/nologin

You decide to mount the /home/user/db_data directory into the db01 container to provide persistent storage on the /var/lib/mysql directory of the container. You then create the /home/user/db_data directory, and use the podman unshare command to set the user namespace UID and GID of 27 as the owner of the directory.

[user@host ~]$ mkdir /home/user/db_data
[user@host ~]$ podman unshare chown 27:27 /home/user/db_data

The UID and GID of 27 in the container maps to the UID and GID of 100026 on the host machine. You can verify the mapping by viewing the ownership of the /home/user/db_data directory with the ls command.

[user@host ~]$ ls -l /home/user/
total 0
drwxrwxr-x. 3  100026  100026 18 May  5 14:37 db_data
...output omitted...

Now that the correct file-system level permissions are set, you use the podman run command -v option to mount the directory.

[user@host ~]$ podman run -d --name db01 \
-e MYSQL_USER=student \
-e MYSQL_PASSWORD=student \
-e MYSQL_DATABASE=dev_data \
-e MYSQL_ROOT_PASSWORD=redhat \
-v /home/user/db_data:/var/lib/mysql \
registry.lab.example.com/rhel8/mariadb-105

You notice that the db01 container is not running.

[user@host ~]$ podman ps -a
CONTAINER ID  IMAGE                                              COMMAND         CREATED         STATUS                     PORTS       NAMES
dfdc20cf9a7e  registry.lab.example.com/rhel8/mariadb-105:latest  run-mysqld      29 seconds ago  Exited (1) 29 seconds ago              db01

The podman container logs command shows a permission error for the /var/lib/mysql/db_data directory.

[user@host ~]$ podman container logs db01
...output omitted...
---> 16:41:25     Initializing database ...
---> 16:41:25     Running mysql_install_db ...
mkdir: cannot create directory '/var/lib/mysql/db_data': Permission denied
Fatal error Can't create database directory '/var/lib/mysql/db_data'

This error happens because of the incorrect SELinux context that is set on the /home/user/db_data directory on the host machine.

SELinux Contexts for Container Storage

You must set the container_file_t SELinux context type before you can mount the directory as persistent storage to a container. If the directory does not have the container_file_t SELinux context, then the container cannot access the directory. You can append the Z option to the argument of the podman run command -v option to automatically set the SELinux context on the directory.

So you use the podman run -v /home/user/db_data:/var/lib/mysql:Z command to set the SELinux context for the /home/user/db_data directory when you mount it as persistent storage for the /var/lib/mysql directory.

[user@host ~]$ podman run -d --name db01 \
-e MYSQL_USER=student \
-e MYSQL_PASSWORD=student \
-e MYSQL_DATABASE=dev_data \
-e MYSQL_ROOT_PASSWORD=redhat \
-v /home/user/db_data:/var/lib/mysql:Z \
registry.lab.example.com/rhel8/mariadb-105

You then verify that the correct SELinux context is set on the /home/user/db_data directory with the ls command -Z option.

[user@host ~]$ ls -Z /home/user/
system_u:object_r:container_file_t:s0:c81,c1009 db_data
...output omitted...

Start a Containerized Service on Boot

In a traditional environment, administrators configure applications such as web servers or databases to start at boot time, and run indefinitely as a systemd service. Systemd is a system and service management tool for Linux operating systems. Systemd uses service unit files to start and stop applications, or to enable them to start at boot time. Typically, an administrator manages these applications with the systemctl command. As a regular user, you can create systemd unit files to manage your rootless containers. You can use this configuration to manage your container as a regular systemd service with the systemctl command.

Generate a Systemd Unit File

To create a systemd unit file for a specified service container, use the podman generate systemd command. Use the --name option to specify the container's name. The --files option generates files instead of printing to standard output (STDOUT).

[user@host ~]$ podman generate systemd --name web --files
/home/user/container-web.service

The preceding command creates the container-web.service unit file for a container called web. After reviewing and adapting the generated service configuration to your requirements, store the unit file in the user's systemd configuration directory (~/.config/systemd/user/).

After adding or modifying unit files, you must use the systemctl command to reload the systemd configuration.

[user@host ~]$ systemctl --user daemon-reload

Managing the Containerized Service

To manage a containerized service, use the systemctl command.

[user@host ~]$ systemctl --user [start, stop, status, enable, disable] container-web.service

When you use the --user option, by default, systemd starts the service when you log in, and stops it when you log out. You can start your enabled services at the operating system boot, and stop them on shutdown, by running the loginctl enable-linger command.

[user@host ~]$ loginctl enable-linger

To revert the operation, use the loginctl disable-linger command.

References

podman-inspect(1), podman-stop(1), podman-restart(1), podman-rm(1),podman-run(1), and podman-unshare(1) man pages

Go Templates Package

For more information, refer to the Porting Containers to systemd Using Podman chapter in the Red Hat Enterprise Linux 9 Building, Running, and Managing Containers guide at https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/9/html-single/building_running_and_managing_containers/index#assembly_porting-containers-to-systemd-using-podman_building-running-and-managing-containers

Revision: rh134-9.3-5fd2368