Goglides Dev 🌱

Cover image for All you need to know-Manage your application using Podman
Balkrishna Pandey
Balkrishna Pandey

Posted on

All you need to know-Manage your application using Podman

Introduction of containers using podman

What is containers?

Containers is a form of virtualization that enables applications to be packaged, deployed, run, and managed in isolated environments.

  • Container technology helps organizations reduce costs associated with managing multiple instances of the same application by allowing them to package the application into one consistent unit across multiple environments.
  • Containers also enable faster deployment, as applications are pre-packaged and can be deployed easily on multiple servers.
  • Container technology facilitates improved scalability and flexibility for IT teams. With containers, teams can quickly scale up or down new services in response to changes in demand.

Container technology isolates the application code, dependencies, and system libraries into separate containers, which are then managed independently from the underlying operating system. Each container is lightweight, standalone, and can run anywhere without any additional configuration or setup. This enables applications to be quickly moved across multiple environments and makes them easier to deploy. The containers also provide greater security by isolating the application from the underlying operating system and other applications on the same server. Finally, container technology simplifies scalability, as teams can easily add more resources or remove existing ones as needed.

Traditional deployment vs Containers

Pictures speak thousands of words; take a look at the following diagrams, which explains the difference between containers and traditional deployment.

Traditional deployment methods, including physical and virtual servers, have been around for decades. They require installing an operating system and all the necessary software applications to be managed manually or through scripting. It's a time-consuming process that can take time to maintain, especially when dealing with multiple applications across multiple environments.

Standard Software deployment workflow

Containers provide an alternative approach to traditional deployment methods. Containers are lightweight, self-contained packages of software code that can be easily deployed on any server or cloud environment without requiring manual installation and configuration. With containers, applications can be quickly spun up and run in isolated environments with no manual setup required. This helps speed up development cycles while making managing and scaling applications across multiple domains easier.

Traditional vs Containers

Overview of the Container Layered File System

Have you ever wondered how Container Technology works? How can it contain an entire application running on a virtual machine or in the cloud? It all starts with the container-layered file system. This file system comprises a set of read-only layers managed via a Union File System (UnionFs) such as AUFS, btrfs, vfs, and devicemapper. When we start a new container, container technology creates a read-write layer on top of these image layers. This read-write layer allows the container to run as a standard Linux file system. As changes occur inside the container, they create working copies in this read-write layer—but when the container is stopped or deleted, that same read-write layer is lost. Let's take a closer look at how this all works.

Layered file system

The Benefits of Using Layers

The main benefit of using layers is that they make it easier to keep track of changes over time. If you have an application that frequently updates its codebase or configuration files, you can use layers to maintain separate versions of each change without managing multiple containers. Layers also save disk space by allowing multiple containers to share standard files and directories without storing them separately for each instance. This makes creating and managing more complex applications much more efficient and cost-effective.

Container-layered file systems also provide security benefits due to their isolated nature. Each container runs in its isolated environment, and any changes made within one container do not affect other containers unless explicitly shared between them—which helps ensure data integrity, especially when dealing with confidential information such as passwords or customer data. Finally, because each application has its distinct layer structure, it can be easily moved between different environments or machines without requiring extensive rework or rewriting code—making deploying applications across various platforms much more straightforward than before!

Using UnionFs For Security and Performance

Underneath all this technology lies UnionFs—a type of filesystem used by Container Technology to allow multiple images (or containers) to be mounted onto one host system without conflicting with one another's resources. UnionFs works by combining several separate filesystems into one logical "union" filesystem that can efficiently store both static data (like images) and dynamic data (like user sessions). UnionFs provides built-in security features such as access control lists (ACLs), which help protect your application from malicious attacks by limiting access only to authorized users. And finally, because UnionFs doesn't need to write out every single change that occurs within a given image/container on disk every time it happens—it improves overall performance significantly compared with traditional methods of storing data within an operating system!

The Container Layered File System makes Container Technology so effective at providing an isolated environment for running applications in the cloud or on virtual machines—allowing developers to deploy their applications quickly and securely across different platforms with minimal effort required in terms of reworking code or rewriting configuration files! By leveraging the power of UnionFs underneath it all, Container Technology provides unparalleled performance while maintaining robust security measures like ACLs that help protect your applications from malicious threats and unauthorized users! So if you're looking for an efficient way to manage large-scale applications both now and in the future—you should consider exploring Docker's Container Layered File System further!

What is a Container Image?

Now that we understand containers and how they work, let's discuss container images. A container image is a file that contains all the necessary information needed to run an application inside of a container, including code, configuration files, system libraries, and even environment variables. Think of it as a snapshot of an application's state at a given time. This snapshot can then be used to easily create and deploy exact copies of the application in other environments without needing to manually re-install or configure each instance. Container images can also be updated and shared with others—making it an excellent way for developers to distribute their applications and updates across different systems quickly.

Container images are commonly created from Dockerfiles, text files that specify which base image a container should be built from, and any extra files or dependencies needed to run the application.

Create custom container images

What is a Containerfile/ Dockerfile?

A Containerfile is a text file that has instructions for building an image. The Containerfile is usually named either "Containerfile" or "Dockerfile." The instructions in the Containerfile tell how to make the image. Here are common directives.

  • # comments are preceded by a # and ignored by the parser
  • FROM directive specifies which base image you want to use as a starting point for your image. This can be any publicly available container image on the container registry or your private registry. Specifying multiple FROM directives is also possible if you want to create an intermediate layer between two images.
  • LABEL directive adds meta-data about the image, such as version number and author name.
  • MAINTAINER directive allows you to specify the maintainer of the image.
  • RUN directive is used to execute any commands required for building the image.
  • EXPOSE directive indicates which ports are exposed by the container when it runs.
  • ENV directive allows you to set environment variables inside the container.
  • ADD directive is used to copy files from the host machine into the image. The ADD instruction also allows you to specify a resource using a web address.
  • COPY directive is similar to ADD, but it's more commonly used because it allows you to copy files from the host machine into the image.
  • USER directive allows you to run commands as a specific user.
  • ENTRYPOINT directive specifies the command that should be executed when the container is started.
  • CMD directive specifies the default parameters that should be passed to the entrypoint command when the container is started. It's possible to override these parameters when the container is started.

These are some basic directives you can use in your Containerfile to build images. There are many more directives available that can be used to create more complex and powerful images.

podman commit

The podman commit command can create an image containing all the changes made within a container. This command can also be used to customize the image by tagging it with a specific label, setting author and message fields, and making changes to instructions. To use this command, simply type podman commit <container_name> hit enter. You can use this command multiple times in order to add more layers of customization or make additional changes to your current image.

Syntax:

podman commit [OPTIONS] CONTAINER [REPOSITORY[:PORT]/]IMAGE_NAME[:TAG]
Enter fullscreen mode Exit fullscreen mode

Examples:

podman commit nginx
podman commit -q --pause=false nginx nginx nginx-new-tag-01
podman commit -q --message "hello podman commit" nginx nginx:new-tag-02
podman commit -q --author "Balkrishna Pandey" nginx nginx-new-tag-03
Enter fullscreen mode Exit fullscreen mode
  • Lets test this,
podman run -d -p 8080:80 --name nginx nginx
podman exec -it nginx bash 
echo "hello podman commit" > /usr/share/nginx/html/index.html
exit
podman commit nginx nginx-commit:0.1
Enter fullscreen mode Exit fullscreen mode
  • Now run nginx application using new image tag
podman run  -d -p 8090:80 nginx-commit:0.1
curl localhost:8090

Output:
hello podman commit
Enter fullscreen mode Exit fullscreen mode

Hello world python?

We have enough theory, to begin with, a containerization process in action. To begin, let's make sure we have a working podman environment setup on our machine. After that we will create a Containerfile/Dockerfile and build a container image with a basic python application. Inside Containerfile, we'll specify the base image from which our container will be built and add any additional dependencies required to run our application.

I have created another blog post on creating python web applications from scratch, which you can find here. Let me grab some snippets from the same blog post. Create a file called app.py in your project's root directory and type the following code into it:

from flask import Flask
app = Flask(__name__)
@app.route("/")
def index():
    return "Hello, Class EX-180!"
Enter fullscreen mode Exit fullscreen mode

The next step is creating a requirements.txt file.

flask
Enter fullscreen mode Exit fullscreen mode

Next, you'll create a file called Dockerfile in your project's root directory. This file contains instructions for how to build your application's container image.

FROM python:3.8-slim-buster
WORKDIR /app
COPY requirements.txt requirements.txt
RUN pip3 install -r requirements.txt
COPY . .
CMD [ "python3", "-m" , "flask", "run", "--host=0.0.0.0"]
Enter fullscreen mode Exit fullscreen mode

Next, you'll use the Dockerfile build a container image for your application.

podman build . -t flask-app:v0.1
Enter fullscreen mode Exit fullscreen mode

Now that you have built a container image for your application, you can run it as a container. In the terminal, type the following command:

podman run -d -p 5000:5000 flask-app:v0.1
Enter fullscreen mode Exit fullscreen mode

Now that your application is running as a container, you can test it to ensure it's working as expected.

First make sure process is running,

podman ps
Enter fullscreen mode Exit fullscreen mode

You should see an output similar to this,

f1fd7c36bd44  flask-app       "python3 -m flask ru…"  3 seconds ago  Up 3 seconds  0.0.0.0:5000->5000/tcp      epic_albattani
Enter fullscreen mode Exit fullscreen mode

To test your application, open a web browser and navigate to http://localhost:5000/. If everything is working correctly, you should see the following output:

Hello, Class EX-180!
Enter fullscreen mode Exit fullscreen mode

Manage custom container images?

Repository vs Registry vs Tags

It is important to be able to distinguish between a registry, a repository, and a tag when you are getting into container development.

  • A registry is a service that hosts and distributes images,
  • While a repository simply holds related images in one place, such as different versions of the same application or service.
  • To differentiate between these various versions, tags, which are alphanumeric identifiers, can be attached to the images.

What is a Container Registry?

A container registry is an online service that stores versioned images of your containerized applications. It also allows you to store, organize, distribute and manage these images in a secure environment. A container registry provides an easy way to track the changes made to each application's appearance and offers quick access when needed to those same images and versions stored within the registry. Additionally, the security features allow users to control who can access their images and which versions are visible at any given time.

What is container registry

Public Registry/ Private Registry

Registries can be either public or private. Public registries are open for anyone to upload and download images, such as the Docker Hub and Quay.io, while Private registries require authentication before allowing access. To log in to a registry with Podman, the podman login command is used. This command requires the registry URL, username and password to access the registry. After logging in, you can then push or pull images from that registry.

How Does Container Registry Work?

When using a container registry, developers can upload their code into the repository using Docker Hub, Quay, or other services. It can then be stored in the registry so that other users can access it from anywhere with an internet connection. The uploaded code will be saved in its repository under its unique name; this enables other users to quickly identify which application an image belongs to when searching for it in the registry. Additionally, any changes are made to the application's code or image during development. In that case, another version of that same image can be easily created and uploaded into the same repository for others to access.

Benefits of Using a Container Registry

Why we need container registry
Using a container registry has several benefits, including

  • Increased reliability concerning applications being deployed consistently across multiple environments;
  • Improved security via user-based authentication controls on who can access specific versions of applications;
  • Improved efficiency due to faster build times as images are pre-built instead of having to build them every time they are needed;
  • Increased scalability as new versions of applications can easily be added without overwriting existing ones;
  • And cost savings as users don't have to constantly rebuild their images every time they need them due to them already being stored in the registry.

Interacting with registry

Configuring and Interacting with Registries

podman search

# Search nginx image across all container registry
podman search nginx

# Search nginx image accross all container registry but output only top 3
podman search nginx --limit 3

# Search nginx images in redhat registry and display top 3
podman search registry.access.redhat.com/nginx --limit 3

# Search nginx image in docker.io and list all tags
podman search docker.io/library/nginx --list-tags

# Search only official nginx images and list top 3
podman search --filter=is-official --limit 3 nginx

# Search nginx, limit 2 and display in tabular format name only, also skip tls verification
podman search --format "table {{.Name}}" nginx --tls-verify  --limit 2

# Search nginx, limit 3 and display in tabular format registry and name only
podman search --format "table {{.Index}} {{.Name}}" registry.access.redhat.com/nginx --limit 3
Enter fullscreen mode Exit fullscreen mode

podman pull

podman pull --help
podman pull nginx
podman pull nginx:1.11.1
podman pull nginx --all-tags
Enter fullscreen mode Exit fullscreen mode

podman login/logout

Here are some variation of login command:

# Login to provided registry 
podman login registry.redhat.io

# Login to provided resgirty and write more detailed information to stdout
podman login registry.redhat.io -v

# Login o registry with username and password in cli (insecure mostly useful for automated processes)
podman login registry.redhat.io --username <username> --password <password>

# Login to registry and skip tls verification
podman login registry.redhat.io --username <username> --password <password> --tls-verify=false

# Login to registry using supplied auth file, default location of this file is $HOME/.config/containers/auth.json
podman login registry.redhat.io --authfile=./auth.json

# Logout from existing session
podman logout registry.redhat.io
Enter fullscreen mode Exit fullscreen mode

Configuration file for podman registry

  • To customize your registries for the podman command, you must update the /etc/containers/registries.conf file.

  • Interacting with insecure registry, customize /etc/containers/registries.conf

[[registry]]
location = "localhost:5000"
insecure = true
Enter fullscreen mode Exit fullscreen mode

Setup the Local Registry

The following steps will walk you through setting up your own local registry using Podman:

  • Make sure that you have Podman installed on your system by running. If it’s not installed yet, follow instructions for installing it on your platform (e.g., Linux or macOS).
podman version
Enter fullscreen mode Exit fullscreen mode
  • Create the directory where the registry will be stored with the command
mkdir -p /var/lib/registry
Enter fullscreen mode Exit fullscreen mode

Note: for MacBook connect to VM first using podman machine ssh and create this folder inside VM. You may also need to change the permission of folder if you are running podman without sudo user chown -R core:core /var/lib/registry.

  • Run Podman with the command
podman run --privileged -d --name registry -p 5000:5000 -v /var/lib/registry:/var/lib/registry --restart=always registry:2
Enter fullscreen mode Exit fullscreen mode

You can also use following instead of using local volume mount,

podman volume create registry
podman run --privileged -d --name registry -p 5000:5000 -v registry:/var/lib/registry --restart=always registry:2
Enter fullscreen mode Exit fullscreen mode
  • Edit the registries configuration file, by changing registries = [] to registries = ['localhost:5000']

Or add following lines to the same file.

[[registry]]
location = "localhost:5000"
insecure = true
Enter fullscreen mode Exit fullscreen mode
vim /etc/containers/registries.conf
Enter fullscreen mode Exit fullscreen mode
  • Test out your new local registry by running following commands:
podman pull alpine 
podman pull alpine:3

podman tag alpine localhost:5000/alpine 
podman tag alpine:3 localhost:5000/alpine:3

podman push localhost:5000/alpine
podman push localhost:5000/alpine:3
Enter fullscreen mode Exit fullscreen mode
  • Verify that everything worked correctly by running
podman pull localhost:5000/alpine
curl -Ls http://localhost:5000/v2/_catalog | python3 -m json.tool
Enter fullscreen mode Exit fullscreen mode

Output:

{
    "repositories": [
        "alpine"
    ]
}
Enter fullscreen mode Exit fullscreen mode

Verify tags,

curl -Ls http://localhost:5000/v2/alpine/tags/list | python3 -m json.tool
Enter fullscreen mode Exit fullscreen mode

Output:

{
    "name": "alpine",
    "tags": [
        "latest",
        "3"
    ]
}
Enter fullscreen mode Exit fullscreen mode
  • You're all set! Now you can start pushing and pulling images from your new local registry!

Podman image command

Podman Images

podman image

podman image --help

# List images in local storage
podman image list

# Show history of a specified image
podman image history localhost/flask-app:v0.1
Enter fullscreen mode Exit fullscreen mode
# Inspect changes to the image's file systems
podman image diff localhost/flask-app:v0.1

Output:
C /app
A /app/Dockerfile
A /app/app.py
C /app/requirements.txt
Enter fullscreen mode Exit fullscreen mode
# Remove unused images
podman image prune

Output:
WARNING! This command removes all dangling images.
Are you sure you want to continue? [y/N] y
Enter fullscreen mode Exit fullscreen mode
# Display the configuration of an image
podman image inspect localhost/flask-app:v0.1
Enter fullscreen mode Exit fullscreen mode

Image local operations; Save and Load

We can use podman save command to create tar file:

podman save --help
Enter fullscreen mode Exit fullscreen mode

Let's use a same image we created for our hello-world python application.

podman images | grep flask-app

Output:
localhost/flask-app         v0.1             c65ba788425a  2 days ago   134 MB
Enter fullscreen mode Exit fullscreen mode
podman save -o flask-app-v0.1.tar localhost/flask-app:v0.1
Enter fullscreen mode Exit fullscreen mode

This will create a tar file with name flask-app-v0.1.tar

ls -lh flask-app-v0.1.tar

Output:
-rw-r--r--  1 bpandey  staff   128M Dec 20 16:27 flask-app-v0.1.tar
Enter fullscreen mode Exit fullscreen mode

We can send this tar file to load our container image in a different location without using the container registry. This process is usually prevalent in disconnected/air-gapped installations.

To load this tar file, you can use the following command. Let's delete the existing image first;

podman rmi localhost/flask-app:v0.1
podman images | grep flask-app
Enter fullscreen mode Exit fullscreen mode

And to load this image, run this command:

podman load -i flask-app-v0.1.tar
Enter fullscreen mode Exit fullscreen mode

Image tags

An image tag is a name assigned to an image. The tag can be used to identify a specific version of an image and helps provide reproducible environments. Tags are usually separated by colons and can be used to specify versions, such as 1.0:latest. The podman pull command will download the latest version of the requested tagged image if no specific tag is provided, or it will download the specified tagged version if selected.

We can use podman tag command which adds one or more additional names to locally-stored image.

podman tag <SOURCE_IMAGE> TARGET_NAME [TARGET_NAME...]

podman tag c65ba788425a localhost/flask-app:v0.1-newtag
podman tag localhost/flask-app:v0.1 localhost/flask-app:v0.1-newtag1
podman tag localhost/flask-app:v0.1 localhost/flask-app:v0.1-newtag2 localhost/flask-app:v0.1-newtag3
Enter fullscreen mode Exit fullscreen mode

Lets check the final output:

podman images localhost/flask-app

REPOSITORY           TAG           IMAGE ID      CREATED     SIZE
localhost/flask-app  v0.1          c65ba788425a  2 days ago  134 MB
localhost/flask-app  v0.1-newtag1  c65ba788425a  2 days ago  134 MB
localhost/flask-app  v0.1-newtag2  c65ba788425a  2 days ago  134 MB
localhost/flask-app  v0.1-newtag   c65ba788425a  2 days ago  134 MB
localhost/flask-app  v0.1-newtag3  c65ba788425a  2 days ago  134 MB
Enter fullscreen mode Exit fullscreen mode

Interacting with Container

podman ps

  • list all running containers
podman ps
Enter fullscreen mode Exit fullscreen mode
  • list all containers including stopped containers
podman ps -a
# Display the total file sizes and sort by id
podman ps -a --size --sort id
# Do not truncate the output
podman ps --size --sort id --no-trunc
Enter fullscreen mode Exit fullscreen mode
  • Pretty-print containers to JSON or using a Go template
podman ps -a --format "{{.ID}}  {{.Image}}  {{.Labels}}  {{.Mounts}}"
Enter fullscreen mode Exit fullscreen mode

podman run

Podman run a command in a new container from the given image, optionally overriding the default entrypoint. This is an interactive command, similar to running a shell in an interactive container instance.

To run a command in a container, use the podman pull command to download an image from Docker Hub or other registry, followed by the podman login command to authenticate with that registry.

podman login <registry>
podman pull <registry>/<image_name>:<tag>
Enter fullscreen mode Exit fullscreen mode

podman run entrypoint override

Once authenticated, you can use the podman run command to start a container from the downloaded image and optionally specify an entrypoint for running the desired command.

podman run --entrypoint "/bin/sh" <registry>/<image_name>:<tag> -c "<command>"
Enter fullscreen mode Exit fullscreen mode

The above command will start a container from the specified registry and image tag, override the default entrypoint with /bin/sh and run the given command inside the container. This allows you to execute arbitrary commands in an isolated environment without having to manage a separate container instance.

For example:

podman pull docker.io/library/nginx:1.10
podman run -it --entrypoint "/bin/bash" docker.io/library/nginx:1.10
podman run --entrypoint "/bin/bash" docker.io/library/nginx:1.10 -c ls
podman run nginx ls -alF /etc
Enter fullscreen mode Exit fullscreen mode

podman run to deploy application

  • Run application in foreground
podman run nginx
Enter fullscreen mode Exit fullscreen mode
  • Run application detach mode (background process)
podman run -d nginx
Enter fullscreen mode Exit fullscreen mode
  • Run application mount container port 80 to external port 8080
podman run -d -p 8080:80 nginx
Enter fullscreen mode Exit fullscreen mode
  • Podman assign random port if you are not specifying the port
# Run command twice for testing
podman run -d -p 80 nginx
podman run -d -p 80 nginx
podman ps | grep nginx 
Enter fullscreen mode Exit fullscreen mode

Output:

ea9860ced6ca  docker.io/library/nginx:latest  nginx -g daemon o...  2 days ago  Up 2 days ago  0.0.0.0:43927->80/tcp   peaceful_elion
6b61a99396c6  docker.io/library/nginx:latest  nginx -g daemon o...  2 days ago  Up 2 days ago  0.0.0.0:36677->80/tcp   inspiring_nightingale
Enter fullscreen mode Exit fullscreen mode

You can see the ports in output above or you can also run podman port command,

podman port peaceful_elion

Output:
80/tcp -> 0.0.0.0:43927

podman port inspiring_nightingale

Output:
80/tcp -> 0.0.0.0:36677
Enter fullscreen mode Exit fullscreen mode

You should able to access both nginx application as follows,

curl localhost:43927
curl localhost:36677
Enter fullscreen mode Exit fullscreen mode
  • You can now access nginx from your terminal
curl localhost:8080
Enter fullscreen mode Exit fullscreen mode
  • Fun container with friendly container name
podman run -d  --name nginx -p 8080:80 nginx
podman ps | grep nginx

Output:

5ae43ff75abd  docker.io/library/nginx:latest  nginx -g daemon o...  8 seconds ago   Up 8 seconds ago   0.0.0.0:8080->80/tcp    nginx
Enter fullscreen mode Exit fullscreen mode

podman run: environment variable

Podman can add environment variables to containers when they start using the -e flag with the run subcommand.

podman run --name nginx -e USERNAME=goglides -e PASSWORD=password nginx printenv USERNAME PASSWORD

Output:
goglides
password
Enter fullscreen mode Exit fullscreen mode

Persist data: Bind-Mounting Host Path

The -v and --volume options allow users to mount a directory from their local system into the container. This means that users can access files on their local system while working inside the container. The syntax for this command is as follows:

podman run -d -v <host dir>:<container dir> <image>
Enter fullscreen mode Exit fullscreen mode

Users should always specify an absolute path for both host directories and container directories when using this command so that Podman knows exactly where it should mount the content. It is also important to note that SELinux contexts may need to be adjusted when using this approach so that Podman can properly access files within the volume being mounted. Here I am using :Z option to adjust SELinux profile.

podman run -d --name mysql -v /var/home/core/mysql:/var/lib/mysql:Z -p 3307:3306 -e MYSQL_ROOT_PASSWORD=secure -e MYSQL_USER=user mysql
Enter fullscreen mode Exit fullscreen mode

Persist data: Using Podman Volume Command

An alternative way of managing volumes in Podman containers is by utilizing podman volume. This command creates a storage device that is managed by Podman and can then be attached at runtime when working with containers. This approach provides several advantages over bind-mounting options such as better integration between containers, more control over data stored within volumes, and most importantly, automatically taking care of SELinux contexts whenever new content is added or removed from containers. To create a volume with podman volume use the following syntax:

podman volume create <volume name>
Enter fullscreen mode Exit fullscreen mode

Once created, users can then attach this volume at runtime using either the --volume option or --volumes-from option (for example, --volumes-from=my_volume).

podman volume create mysql
podman run -d --name mysql -v mysql:/var/lib/mysql  -p 3307:3306 -e MYSQL_ROOT_PASSWORD=secure -e MYSQL_PASSWORD=secure -e MYSQL_USER=user -e MYSQL_ROOT_HOST=127.0.0.1 mysql
Enter fullscreen mode Exit fullscreen mode

Now you should able to connect to mysql from your localhost as follows,

mysql -uuser -psecure -h 127.0.0.1 -P3307
mysql> show databases;

Output:
+--------------------+
| Database           |
+--------------------+
| information_schema |
| performance_schema |
+--------------------+
2 rows in set (0.01 sec)
Enter fullscreen mode Exit fullscreen mode

podman unshare: volumes with rootless podman

What is Rootless Podman?
Rootless Podman allows users to run containers without requiring root privileges. Podman uses the user namespace feature to map the user’s uid and primary gid into a range of subuids and subgids to create a separate namespace where they appear as UID 0 and GID 0, respectively. This means that any ranges matching those users and groups will also be mapped with the help of newuidmap(1) and newgidmap(1).

Rootless podman unshare lets you run a command in the same user namespace as your containers. This can be useful for troubleshooting unprivileged operations or manually clearing storage. Additionally, it allows you to use podman unshare chown to grant the container user ID permissions to write to your directory.

  • Lets create a working directory and use podman unshare to runs command in a modified user namespace.
mkdir /home/redhat/mysql
podman unshare chown -R 27:27 /home/redhat/mysql
Enter fullscreen mode Exit fullscreen mode
  • Now we can use semanage fcontext command to label this path. This command accept regular expression as follows,
sudo semanage fcontext -a -t container_file_t '/home/redhat/mysql(/.*)?'
Enter fullscreen mode Exit fullscreen mode
  • Restore files default SELinux security contexts as follows,
sudo restorecon -Rv /home/redhat/mysql

Output:
Relabeled /home/redhat/mysql from unconfined_u:object_r:user_home_t:s0 to unconfined_u:object_r:container_file_t:s0
Enter fullscreen mode Exit fullscreen mode
ls -ldZ /home/redhat/mysql

Output:
drwxr-xr-x. 2 100026 100026 unconfined_u:object_r:container_file_t:s0 6 Dec 22 10:02 /home/redhat/mysql
Enter fullscreen mode Exit fullscreen mode
  • Verify that the SELinux context type for the directory is container_file_t
sudo semanage fcontext -l |grep /home/redhat/mysql

Output:
/home/redhat/mysql(/.*)?                           all files          system_u:object_r:container_file_t:s0 
Enter fullscreen mode Exit fullscreen mode

Now we can use the /home/redhat/mysql host directory for MySQL server database files. This is where we expect to find the database files inside a MySQL container image named mysql.

podman run -d --name mysql -v /home/redhat/mysql:/var/lib/mysql  -p 3306:3306 -e MYSQL_ROOT_PASSWORD=secure -e MYSQL_PASSWORD=secure -e MYSQL_USER=user -e MYSQL_ROOT_HOST=127.0.0.1 mysql
Enter fullscreen mode Exit fullscreen mode
  • Verify if the mount is working or not, lets create table first.
podman exec -it mysql  bash
mysql  -uroot -psecure

mysql> show databases;
mysql> create database demo;
mysql> use demo;
mysql> create table ex180 (name varchar(255), address varchar(255));
mysql> show tables;
Enter fullscreen mode Exit fullscreen mode

Now delete the mysql container,

podman stop mysql
podman rm mysql 

# Verify everything is cleaned-up properly
podman ps | grep mysql
Enter fullscreen mode Exit fullscreen mode

Now lets recreate the database with same mount point /home/redhat/mysql

podman run -d --name mysql -v /home/redhat/mysql:/var/lib/mysql  -p 3306:3306 -e MYSQL_ROOT_PASSWORD=secure -e MYSQL_PASSWORD=secure -e MYSQL_USER=user -e MYSQL_ROOT_HOST=127.0.0.1 mysql
Enter fullscreen mode Exit fullscreen mode

You should see the database demo and table ex180.

$ podman exec -it mysql mysql  -uroot -psecure

mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 8
Server version: 8.0.31 MySQL Community Server - GPL

Copyright (c) 2000, 2022, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.


mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| demo               |
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
5 rows in set (0.00 sec)

mysql> use demo;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> show tables;
+----------------+
| Tables_in_demo |
+----------------+
| ex180          |
+----------------+
1 row in set (0.00 sec)

mysql> desc ex180;
+---------+--------------+------+-----+---------+-------+
| Field   | Type         | Null | Key | Default | Extra |
+---------+--------------+------+-----+---------+-------+
| name    | varchar(255) | YES  |     | NULL    |       |
| address | varchar(255) | YES  |     | NULL    |       |
+---------+--------------+------+-----+---------+-------+
2 rows in set (0.00 sec)
Enter fullscreen mode Exit fullscreen mode

podman start

  • start the latest container podman is aware of
podman start --latest
Enter fullscreen mode Exit fullscreen mode
  • start and attach
podman start -i --attach nginx
Enter fullscreen mode Exit fullscreen mode

podman stop

  • Stop nginx container, you can also use container IDs
podman stop nginx
podman stop 43cee7688eb0
Enter fullscreen mode Exit fullscreen mode
  • Stop the latest container podman is aware of
podman stop --latest
Enter fullscreen mode Exit fullscreen mode
  • Seconds to wait for stop before killing the container (default 10)
podman stop --time 2 nginx
Enter fullscreen mode Exit fullscreen mode

podman generate

generate systemd

  • Generate systemd units for a pod or container. The generated units can later be controlled via systemctl(1).
podman generate systemd flask-app

podman generate systemd --files --new --name flask-app

Output:
WARN[0000] The generated units should be placed on your remote system 
/Users/bpandey/container-flask-app.service
Enter fullscreen mode Exit fullscreen mode

And here is the content of the file, cat /Users/bpandey/container-flask-app.service

# container-flask-app.service
# autogenerated by Podman 4.3.1
# Mon Dec 19 07:21:07 UTC 2022

[Unit]
Description=Podman container-flask-app.service
Documentation=man:podman-generate-systemd(1)
Wants=network-online.target
After=network-online.target
RequiresMountsFor=%t/containers

[Service]
Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=on-failure
TimeoutStopSec=70
ExecStartPre=/bin/rm \
    -f %t/%n.ctr-id
ExecStart=/usr/bin/podman run \
    --cidfile=%t/%n.ctr-id \
    --cgroups=no-conmon \
    --rm \
    --sdnotify=conmon \
    --replace \
    -d \
    --name flask-app \
    -p 5001:5000 flask-app:v0.1
ExecStop=/usr/bin/podman stop \
    --ignore -t 10 \
    --cidfile=%t/%n.ctr-id
ExecStopPost=/usr/bin/podman rm \
    -f \
    --ignore -t 10 \
    --cidfile=%t/%n.ctr-id
Type=notify
NotifyAccess=all

[Install]
WantedBy=default.target
Enter fullscreen mode Exit fullscreen mode
  • Now we can use systemctl to enable the service
systemctl --user enable --now ./container-flask-app.service

Output:
Created symlink /var/home/core/.config/systemd/user/container-flask-app.service → /var/home/core/container-flask-app.service.
Created symlink /var/home/core/.config/systemd/user/default.target.wants/container-flask-app.service → /var/home/core/container-flask-app.service.
Enter fullscreen mode Exit fullscreen mode
  • Make sure to reload the domain
systemctl --user daemon-reload 
Enter fullscreen mode Exit fullscreen mode
  • Check the status
systemctl --user status container-flask-app
Enter fullscreen mode Exit fullscreen mode

Output:

â—Ź container-flask-app.service - Podman container-flask-app.service
     Loaded: loaded (/var/home/core/.config/systemd/user/container-flask-app.service; enabled; preset: disabled)
     Active: active (running) since Mon 2022-12-19 07:56:41 UTC; 54s ago
       Docs: man:podman-generate-systemd(1)
   Main PID: 14858 (conmon)
      Tasks: 13 (limit: 2265)
     Memory: 3.8M
        CPU: 464ms
     CGroup: /user.slice/user-1000.slice/[email protected]/app.slice/container-flask-app.service
             ├─14845 rootlessport
             ├─14850 rootlessport-child
             └─14858 /usr/bin/conmon --api-version 1 -c f461bfdc6345158be3ed3af1a1d387b61c20365a5b3114250778db574a15c330 -u f461bfdc6345>

Dec 19 07:56:41 localhost.localdomain podman[14726]: 2022-12-19 07:56:41.583345026 +0000 UTC m=+0.499888940 container init f461bfdc63451>
Dec 19 07:56:41 localhost.localdomain systemd[894]: Started container-flask-app.service - Podman container-flask-app.service.
Dec 19 07:56:41 localhost.localdomain podman[14726]: 2022-12-19 07:56:41.601982691 +0000 UTC m=+0.518525521 container start f461bfdc6345>
Dec 19 07:56:41 localhost.localdomain podman[14726]: f461bfdc6345158be3ed3af1a1d387b61c20365a5b3114250778db574a15c330
Dec 19 07:56:41 localhost.localdomain flask-app[14858]:  * Debug mode: off
Dec 19 07:56:41 localhost.localdomain flask-app[14858]: WARNING: This is a development server. Do not use it in a production deployment.>
Dec 19 07:56:41 localhost.localdomain flask-app[14858]:  * Running on all addresses (0.0.0.0)
Dec 19 07:56:41 localhost.localdomain flask-app[14858]:  * Running on http://127.0.0.1:5000
Dec 19 07:56:41 localhost.localdomain flask-app[14858]:  * Running on http://10.88.0.7:5000
Dec 19 07:56:41 localhost.localdomain flask-app[14858]: Press CTRL+C to quit
Enter fullscreen mode Exit fullscreen mode
  • Everything looks good, Now you can run all systemctl commands as the user (include the --user flag).
systemctl --user status container-flask-app
systemctl --user start container-flask-app
systemctl --user restart container-flask-app
systemctl --user stop container-flask-app
Enter fullscreen mode Exit fullscreen mode
  • If you want to automatically run this service when machine starts make sure to enable user lingering. In my case its already enabled.
loginctl show-user core | grep -i linger

Output:
Linger=yes
Enter fullscreen mode Exit fullscreen mode
  • If its not enabled you can use following command to enable it.
loginctl enable-linger

# Verify
loginctl show-user core | grep -i linger
Enter fullscreen mode Exit fullscreen mode

generate kube files

  • Command generates Kubernetes Pod, Service or PersistenVolumeClaim YAML (v1 specification) from Podman containers, pods or volumes.
# Just generate pod config
podman generate  kube flask-app

# Generate pod and services both
podman generate kube flask-app -s
Enter fullscreen mode Exit fullscreen mode

Output:

# Save the output of this file and use kubectl create -f to import
# it into Kubernetes.
#
# Created with podman-4.3.1
apiVersion: v1
kind: Service
metadata:
  creationTimestamp: "2022-12-19T07:16:46Z"
  labels:
    app: flask-app-pod
  name: flask-app-pod
spec:
  ports:
  - name: "5000"
    nodePort: 32001
    port: 5000
    targetPort: 5000
  selector:
    app: flask-app-pod
  type: NodePort
---
apiVersion: v1
kind: Pod
metadata:
  annotations:
    io.kubernetes.cri-o.TTY/flask-app: "false"
    io.podman.annotations.autoremove/flask-app: "FALSE"
    io.podman.annotations.init/flask-app: "FALSE"
    io.podman.annotations.privileged/flask-app: "FALSE"
    io.podman.annotations.publish-all/flask-app: "FALSE"
  creationTimestamp: "2022-12-19T07:16:46Z"
  labels:
    app: flask-app-pod
  name: flask-app-pod
spec:
  automountServiceAccountToken: false
  containers:
  - image: localhost/flask-app:v0.1
    name: flask-app
    ports:
    - containerPort: 5000
      hostPort: 5001
    securityContext:
      capabilities:
        drop:
        - CAP_MKNOD
        - CAP_NET_RAW
        - CAP_AUDIT_WRITE
  enableServiceLinks: false
Enter fullscreen mode Exit fullscreen mode

podman cp

The Podman cp command allows you to copy files/folders between a container and the local filesystem. The source path (SRC_PATH) is the file or directory on the local filesystem that you want to copy, while the destination path (DEST_PATH) is where you want it copied in the container. The great thing about Podman is that it works with running and stopped containers, so you don't need to worry about getting your containers up and running before copying files.

podman cp [options] [CONTAINER:]SRC_PATH [CONTAINER:]DEST_PATH
Enter fullscreen mode Exit fullscreen mode

Example:

touch index.html
echo "hello world" > index.html
podman cp index.html flask-app:/app/.
podman exec flask-app ls /app
Enter fullscreen mode Exit fullscreen mode

Output:

Dockerfile
__pycache__
app.py
index.html
requirements.txt
Enter fullscreen mode Exit fullscreen mode

Debugging containers

podman exec

podman run -d --name flask-app -p 5001:5000 flask-app:v0.1
Enter fullscreen mode Exit fullscreen mode
podman exec -it flask-app bash
root@783ef2ed9a36:/app# ls

Output:
Dockerfile  __pycache__  app.py  requirements.txt
Enter fullscreen mode Exit fullscreen mode
  • Run cat command to display content of a file
podman exec flask-app cat app.py

Output:
from flask import Flask
app = Flask(__name__)
@app.route("/")
def index():
    return "Hello, Class EX-180!"
Enter fullscreen mode Exit fullscreen mode
  • Run command to update content of a file
podman exec flask-app sed -rie "s/EX-180/EX-1900/g" app.py
podman exec flask-app cat app.py

Output:
from flask import Flask
app = Flask(__name__)
@app.route("/")
def index():
    return "Hello, Class EX-1900!"
Enter fullscreen mode Exit fullscreen mode

podman logs

  • Retrieve logs from one or more container. > Note: logs does not support multiple containers when run remotely.
podman logs flask-app

Output:
 * Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on all addresses (0.0.0.0)
 * Running on http://127.0.0.1:5000
 * Running on http://10.88.0.5:5000
Press CTRL+C to quit
10.88.0.5 - - [18/Dec/2022 22:52:29] "GET / HTTP/1.1" 200 -
10.88.0.5 - - [18/Dec/2022 22:56:19] "GET / HTTP/1.1" 200 -
Enter fullscreen mode Exit fullscreen mode
  • Output the specified number of LINES at the end of the logs. Defaults to -1, which prints all lines.
podman logs --tail 2 flask-app

Output:
10.88.0.5 - - [18/Dec/2022 22:52:29] "GET / HTTP/1.1" 200 -
10.88.0.5 - - [18/Dec/2022 22:56:19] "GET / HTTP/1.1" 200 -
Enter fullscreen mode Exit fullscreen mode
  • Follow log output since TIMESTAMP, until TIMESTAMP, The default follow is false. TIMESTAMP can be a Unix timestamp, date formatted timestamp, or Go duration string relative to the client machine’s time.
podman logs --follow=true --since 2022-01-01 flask-app
podman logs --follow=true --since 100h1m flask-app
podman logs --since 100h1m --until 2022-12-21 flask-app
Enter fullscreen mode Exit fullscreen mode
  • Output the timestamps in the log
podman logs -t  flask-app
Enter fullscreen mode Exit fullscreen mode

podman inspect

Podman inspect displays the low-level information on an objects (container, image, network, pod, volume).

podman inspect
podman container inspect
podman image inspect
podman network inspect
podman pod inspect
podman volume inspect
Enter fullscreen mode Exit fullscreen mode
podman inspect flask-app
Enter fullscreen mode Exit fullscreen mode
podman inspect --format "imageId: {{.Id}} NetworkMode: {{.HostConfig.NetworkMode}}" flask-app

Outout
imageId: 783ef2ed9a36cdf68fb4b3c3bbe334c5cc25362310cb0bc4f4db5c66eab0ec55 NetworkMode: bridge
Enter fullscreen mode Exit fullscreen mode
  • You can also pass type in inspect command. Type can be "image", "container", "pod", "network", "volume", or "all". Default is all.

You can test this behaviour using following commands.

podman tag localhost/flask-app:v0.1 flask-app
podman volume create flask-app
podman network create flask-app
podman pod create --name flask-app

podman inspect flask-app --type image
podman inspect flask-app --type container
podman inspect flask-app --type pod
podman inspect flask-app --type network
podman inspect flask-app --type volume
podman inspect flask-app # --type all
Enter fullscreen mode Exit fullscreen mode

podman events

This command will show you a list of events. By default, it will show you new events as they occur. You can also use the --since and --until options to see events that happened in the past. Some examples of events command,

podman events 
podman events --since 1h3m
podman events --since 1h3m --until 30m
podman events --filter event=create
Enter fullscreen mode Exit fullscreen mode

Cleanup

podman rmi nginx
podman rm nginx
podman stop
podman image prune
podman container cleanup nginx
podman container cleanup nginx mysql 69da2311
Enter fullscreen mode Exit fullscreen mode

The -a option stands for "all" in Podman commands. This means that when you specify this option in a podman command, it will apply the action (e.g., rm or stop) to all available containers or images on your system. For example, if you use the command podman rm -a it will remove all of the existing containers on your system. Similarly, if you use podman stop -a it will stop all of the running containers on your system.

Top comments (0)