If you are already familiar with containerization, you would probably have come across the word ‘Docker’. If not then we need to start by defining two terms: containerization and container engine. So, what is containerization? It is the use of containers to deploy applications and containers enable packaging of applications together with all the required dependencies and can run anywhere. A container engine is required to run containers and docker is the most common and mostly used container engine. There are are alternatives to docker, one of them being podman.

What is Podman? As explained above, it works similar to docker, therefore, it is a container engine. It is developed by Redhat to ease the management of pods and containers. Unlike docker, podman is a daemonless container engine, used in running and managing OCI (Open Container Initiative) containers. It supports rootless mode. Docker on the other hand has a central daemon that runs as root.

Another term which you may have come across when working with docker is docker compose. With docker compose, it is possible to deploy more that one interdependent containers using a single YAML file termed as docker compose file. In a situation where you need to deploy more that one applications to make up another application, docker compose comes handy. All the application images and dependencies are defined in a single file and a single command (docker compose up) is used to bring up all the defined containers. An equivalent alternative for podman is Podman Compose. Let’s go ahead to define Podman Compose and learn how it can be used in container orchestration.

What is Podman Compose?

Just like docker compose, podman compose is used to make it easy to deploy interdependent containers by defining in a single file. Podman compose picks the content of a docker-compose file and runs the individual containers as defined Note that Podman compose makes use of docker-compose.yml files without making any changes in it. The difference comes when bringing up the containers where podman-compose up is used instead of docker-compose up. Since Podman uses pods, the containers will be placed in a single pod sharing the same network. A pod can contain a single container or can be a group of containers sharing resources such as network and storage. Placing the containers in a pod is what brings the difference between docker compose and podman compose.

How to Use Podman Compose to Manage containers.

To be able to use Podman Compose you first need to install it. You should also have podman installed already.

Install Podman and Podman Compose

To install Podman, run the below commands depending on your distribution:

# Install Podman on MacOS
brew install podman

# Install Podman on Arch Linux| Manjaro Linux
sudo pacman -S podman

# Install Podman on CentOS
sudo yum -y install podman

# Install Podman on Debian
sudo apt-get update
sudo apt-get -y install podman

# Install Podman on Fedora
sudo dnf -y install podman

# Install Podman on Opensuse
sudo zypper install podman

Once you have Podman installed, proceed to install podman-compose as shown below:

To install the latest development version from Github, run the below commands

# Install pyyaml if it doesn't already exist

$ sudo python3 -m pip install pyyaml
Defaulting to user installation because normal site-packages is not writeable
Collecting pyyaml
  Downloading PyYAML-5.4.1-cp39-cp39-manylinux1_x86_64.whl (630 kB)
     |████████████████████████████████| 630 kB 117 kB/s 
Installing collected packages: pyyaml
Successfully installed pyyaml-5.4.1

# Install podman-compose

$ sudo pip3 install https://github.com/containers/podman-compose/archive/devel.tar.gz
Collecting https://github.com/containers/podman-compose/archive/devel.tar.gz
  Downloading https://github.com/containers/podman-compose/archive/devel.tar.gz
     / 30 kB 151 kB/s
Building wheels for collected packages: podman-compose
  Building wheel for podman-compose (setup.py) ... done
  Created wheel for podman-compose: filename=podman_compose-0.1.7.dev0-py2.py3-none-any.whl size=25002 sha256=0aa83809b9267e113857449859e67cd6d47274930bb7dbe0c0312bcb42ad7562
  Stored in directory: /tmp/pip-ephem-wheel-cache-u0uyiqb0/wheels/13/cf/44/3b0512e143ac722c31929fad1e4af926b0745e6086bb9910aa
Successfully built podman-compose
Installing collected packages: podman-compose
  Attempting uninstall: podman-compose
    Found existing installation: podman-compose 0.1.5
    Uninstalling podman-compose-0.1.5:
      Successfully uninstalled podman-compose-0.1.5
Successfully installed podman-compose-0.1.7.dev0

Or install using PIP as shown:

# Install podman-compose

$ sudo pip3 install podman-compose
Collecting podman-compose
  Downloading podman_compose-0.1.5-py2.py3-none-any.whl (20 kB)
Requirement already satisfied: pyyaml in /usr/lib/python3/dist-packages (from podman-compose) (5.3.1)
Installing collected packages: podman-compose
  WARNING: The script podman-compose is installed in '/home/lorna/.local/bin' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.

# Add to PATH, edit the command to suit your own user instead of 'lorna'
$ export PATH=/home/lorna/.local/bin:$PATH

# Confirm you can run 'podman-compose' command
$ podman-compose --help
usage: podman-compose [-h] [-f FILE] [-p PROJECT_NAME]
                      [--podman-path PODMAN_PATH] [--no-ansi] [--no-cleanup]
                      [--dry-run]
                      [-t {1pod,1podfw,hostnet,cntnet,publishall,identity}]
                      {pull,push,build,up,down,run,start,stop,restart} ...

optional arguments:
  -h, --help            show this help message and exit
  -f FILE, --file FILE  Specify an alternate compose file (default: docker-
                        compose.yml)
  -p PROJECT_NAME, --project-name PROJECT_NAME
                        Specify an alternate project name (default: directory
                        name)
  --podman-path PODMAN_PATH
                        Specify an alternate path to podman (default: use
                        location in $PATH variable)
  --no-ansi             Do not print ANSI control characters
  --no-cleanup          Do not stop and remove existing pod & containers
  --dry-run             No action; perform a simulation of commands
  -t {1pod,1podfw,hostnet,cntnet,publishall,identity}, --transform_policy {1pod,1podfw,hostnet,cntnet,publishall,identity}
                        how to translate docker compose to podman
                        [1pod|hostnet|accurate]

command:
  {pull,push,build,up,down,run,start,stop,restart}
    pull                pull stack images
    push                push stack images
    build               build stack images
    up                  Create and start the entire stack or some of its
                        services
    down                tear down entire stack
    run                 create a container similar to a service to run a one-
                        off command
    start               start specific services
    stop                stop specific services
    restart             restart specific services

You can also download the executable file as below:

sudo curl -o /usr/local/bin/podman-compose https://raw.githubusercontent.com/containers/podman-compose/devel/podman_compose.py

sudo chmod +x /usr/local/bin/podman-compose

If you are running on Fedora, you can simply install with package manager

sudo dnf install podman-compose

Using podman-compose

To use podman-compose, let’s go ahead to create a docker-compose file to hold the container requirements. Let’s assume that we have a docker-compose file for deploying LAMP server. If we were to run the services separately, we would need different containers for php-apache and mariadb. With the use of docker compose, we would have a Docker-compose.yml file with the content as shown:

version: '3'
services:
    php-apache:
        image: php:7.3-apache
        ports:
            - 8080:80
        volumes:
            - apache:/var/www/html
        links:
            - 'mariadb'

    mariadb:
        image: mariadb:10.5
        volumes:
            - mariadb:/var/lib/mysql
        environment:
            TZ: "Europe/Rome"
            MYSQL_ALLOW_EMPTY_PASSWORD: "no"
            MYSQL_ROOT_PASSWORD: "rootpwd"
            MYSQL_USER: 'testuser'
            MYSQL_PASSWORD: 'testpassword'
            MYSQL_DATABASE: 'testdb'

volumes:
    mariadb:
    apache:

Note that Podman always creates volumes on demand when they do not already exist. As such, you don’t have to first create the persistent volumes.

Now run podman-compose up command to bring up the defined containers. You should notice that Podman first creates a pod named after the directory it is running from (in my case lorna) then checks foe any specified volumes in the configuration and creates them if they don’t exist .

When prompted to choose an image, I am picking an image from docker.io

$ podman-compose up -d
['podman', '--version', '']
using podman version: podman version 3.1.2
podman pod create --name=lorna --share net -p 80:80
45cd7f72fb8d46d82d840fdc6bb7acc22bb43049ee332c3b5440c170030e13ad
0
podman volume inspect lorna_mariadb || podman volume create lorna_mariadb
['podman', 'volume', 'inspect', 'lorna_mariadb']
podman create --name=lorna_mariadb_1 --pod=lorna --label io.podman.compose.config-hash=123 --label io.podman.compose.project=lorna --label io.podman.compose.version=0.0.1 --label com.docker.compose.project=lorna --label com.docker.compose.project.working_dir=/home/lorna --label com.docker.compose.project.config_files=docker-compose.yml --label com.docker.compose.container-number=1 --label com.docker.compose.service=mariadb -e TZ=Europe/Rome -e MYSQL_ALLOW_EMPTY_PASSWORD=no -e MYSQL_ROOT_PASSWORD=rootpwd -e MYSQL_USER=testuser -e MYSQL_PASSWORD=testpassword -e MYSQL_DATABASE=testdb -v lorna_mariadb:/var/lib/mysql --add-host php-apache:127.0.0.1 --add-host lorna_php-apache_1:127.0.0.1 --add-host mariadb:127.0.0.1 --add-host lorna_mariadb_1:127.0.0.1 mariadb:10.5
? Please select an image: 
    registry.fedoraproject.org/mariadb:10.5
    registry.access.redhat.com/mariadb:10.5
  ▸ docker.io/library/mariadb:10.5
    quay.io/mariadb:10.5

The images will be pulled and run accordingly as shown:

---
Trying to pull docker.io/library/mariadb:10.5...
Getting image source signatures
Copying blob 329b1f41043f done  
Copying blob c549ccf8d472 done  
Copying blob 2bc055a5511d done  
Copying blob 9f8d09317d80 done  
Copying blob 26ea6552a462 done  
Copying blob e989e430508e done  
Copying blob cdba2af19f87 done  
Copying blob 04fe4f90eab8 done  
Copying blob 389c6b423e31 done  
Copying blob bef640655d86 done  
Copying config 6d5c5ed114 done  
Writing manifest to image destination
Storing signatures
90f9fb0328abce51a13889f78d2be64b04fa72c1f9c94a6e64a88dc69e8ba101
0

---
Trying to pull docker.io/library/php:7.3-apache...
Getting image source signatures
Copying blob 12b28f3797fb skipped: already exists  
Copying blob b4d181a07f80 skipped: already exists  
Copying blob 78b85dd8f014 skipped: already exists  
Copying blob 8589b26a90be skipped: already exists  
Copying blob f5af5d641946 skipped: already exists  
Copying blob 614ec6f0b8d6 skipped: already exists  
Copying blob 96bcb7d2e6b0 skipped: already exists  
Copying blob 04e3b41f326d done  
Copying blob 66dc712e236b done  
Copying blob cfad420f634b done  
Copying blob bb0fd0eca074 done  
Copying blob f4bd4cce9e4b done  
Copying blob 6b25019c1023 done  
Copying blob bba8d6a6e59f done  
Copying config 5445627cce done  
Writing manifest to image destination
Storing signatures
643d248a888753c6d6dcc2897bd723a448f3402d02bf0ca8efcbdad5db70d0d9
0

Now confirm created pods

$ podman pod ls
POD ID        NAME    STATUS   CREATED        INFRA ID      # OF CONTAINERS
de5fd41affee  lorna   Running  5 minutes ago  ca804e1613d9  3

As you can see, there is a pod created called lorna and having 3 containers. Confirm the containers as well.

$ podman ps
CONTAINER ID  IMAGE                             COMMAND               CREATED         STATUS            PORTS                 NAMES
ca804e1613d9  k8s.gcr.io/pause:3.5                                    10 minutes ago  Up 9 minutes ago  0.0.0.0:8080->80/tcp  de5fd41affee-infra
90f9fb0328ab  docker.io/library/mariadb:10.5    mysqld                9 minutes ago   Up 9 minutes ago  0.0.0.0:8080->80/tcp  lorna_mariadb_1
643d248a8887  docker.io/library/php:7.3-apache  apache2-foregroun...  8 minutes ago   Up 8 minutes ago  0.0.0.0:8080->80/tcp  lorna_php-apache_1

Check that the volumes were created as well

$ podman volume ls
DRIVER      VOLUME NAME
local       lorna_apache
local       lorna_mariadb

To bring down all the containers at ago;

podman-compose down

To delete the pod;

podman pod rm <podname/podID>

To delete a container;

podman rm <containerID>

To delete an image

podman rmi <ImageID>

You have noticed that Podman names the containers the same one when using docker-compose. Podman stores its persistent volumes in /home/$USER/.local/share/containers/storage/volumes. Here, you will find your created volumes, for my case lorna_apache and lorna_mariadb. This are the directories to create put your website files. For example you can create your index.php file inside lorna_apache folder which will then be mapped to /var/www/html of the container.

Conclusion

In this guide, we have learned how to manage containers with podman-compose. We have seen that podman-compose makes use of a docker-file as it is without having to alter anything. Unlike docker-compose, podman-compose creates a pod and places all the containers defined in the yaml file in it.

The containers will share storage and network. Podman-compose also creates persistent volumes in demand and you do not have to create them before deploying the containers. I hope the guide has been useful in bringing you up to speed in using podman-compose to manage containers. More interesting guides below:

Best Docker Learning Courses:

LEAVE A REPLY

Please enter your comment!
Please enter your name here