Container Management with Podman on CentOS/RHEL

Tyler Maginnis | February 23, 2024

PodmancontainersCentOSRHELDockerKubernetescontainerization

Need Professional CentOS/RHEL Support?

Get expert assistance with your centos/rhel support implementation and management. Tyler on Tech Louisville provides priority support for Louisville businesses.

Same-day service available for Louisville area

Container Management with Podman on CentOS/RHEL

Podman provides a daemonless container engine for developing, managing, and running OCI containers on CentOS/RHEL systems. This guide covers Podman's advanced features including rootless containers, pod management, and Kubernetes compatibility.

Podman Fundamentals

Understanding Podman Architecture

Podman's daemonless design:

# Install Podman
yum install -y podman podman-docker podman-compose

# Verify installation
podman version
podman info

# Podman components
# - podman: CLI tool for managing containers
# - buildah: Tool for building OCI container images
# - skopeo: Tool for working with remote image registries
# - runc: OCI container runtime

# Enable podman socket for Docker compatibility
systemctl enable --now podman.socket

# Test Docker compatibility
docker version  # Using podman-docker alias

Basic Container Operations

Essential Podman commands:

# Search for images
podman search nginx
podman search --filter=is-official nginx

# Pull images
podman pull nginx:latest
podman pull registry.access.redhat.com/ubi8/ubi:latest

# List images
podman images
podman image ls --all

# Run containers
podman run -d --name webserver -p 8080:80 nginx
podman run -it --rm centos:8 /bin/bash

# Container management
podman ps
podman ps -a
podman logs webserver
podman exec -it webserver /bin/bash
podman stop webserver
podman start webserver
podman rm webserver

# Container inspection
podman inspect webserver
podman stats --no-stream

Rootless Containers

Configure Rootless Podman

Run containers without root privileges:

# Enable lingering for user
loginctl enable-linger $USER

# Configure subuid and subgid
sudo usermod --add-subuids 100000-165535 $USER
sudo usermod --add-subgids 100000-165535 $USER

# Verify configuration
podman unshare cat /proc/self/uid_map
podman system migrate

# Run rootless container
podman run -d --name rootless-nginx -p 8080:80 nginx

# Check container user namespace
podman exec rootless-nginx cat /proc/self/uid_map

# Configure storage for rootless
mkdir -p ~/.config/containers
cat > ~/.config/containers/storage.conf <<EOF
[storage]
driver = "overlay"
runroot = "/run/user/$(id -u)/containers"
graphroot = "/home/$USER/.local/share/containers/storage"

[storage.options.overlay]
mount_program = "/usr/bin/fuse-overlayfs"
EOF

Rootless Networking

Configure networking for rootless containers:

# Install slirp4netns for rootless networking
yum install slirp4netns

# Create rootless network
podman network create --driver bridge rootless-net

# Run container with custom network
podman run -d --name webapp \
    --network rootless-net \
    -p 8080:80 \
    nginx

# Port forwarding for rootless containers
podman run -d --name app \
    --publish 127.0.0.1:8080:80 \
    --publish [::1]:8080:80 \
    nginx

# Configure firewall for rootless containers
echo "net.ipv4.ip_unprivileged_port_start=80" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

Building Container Images

Using Dockerfile with Podman

Build images with Podman:

# Create Dockerfile
cat > Dockerfile <<EOF
FROM centos:8
LABEL maintainer="admin@example.com"

RUN yum update -y && \
    yum install -y httpd && \
    yum clean all

COPY index.html /var/www/html/
EXPOSE 80

CMD ["/usr/sbin/httpd", "-D", "FOREGROUND"]
EOF

# Create content
echo "<h1>Hello from Podman</h1>" > index.html

# Build image
podman build -t myapp:latest .
podman build --no-cache -t myapp:v1.0 .

# Multi-stage build
cat > Dockerfile.multistage <<EOF
# Build stage
FROM golang:1.17 AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp

# Runtime stage
FROM centos:8-minimal
COPY --from=builder /app/myapp /usr/local/bin/
CMD ["myapp"]
EOF

podman build -f Dockerfile.multistage -t myapp:slim .

Using Buildah

Advanced image building with Buildah:

# Install Buildah
yum install buildah

# Create container from scratch
container=$(buildah from scratch)

# Mount container filesystem
mountpoint=$(buildah mount $container)

# Install packages
yum install --installroot $mountpoint --releasever 8 \
    --setopt install_weak_deps=false -y \
    coreutils bash httpd

# Configure container
buildah config --cmd "/usr/sbin/httpd -D FOREGROUND" $container
buildah config --port 80 $container
buildah config --label maintainer="admin@example.com" $container

# Commit container
buildah commit $container myapp:buildah

# Unmount and remove working container
buildah umount $container
buildah rm $container

Pod Management

Creating and Managing Pods

Work with Podman pods:

# Create pod
podman pod create --name webapp-pod \
    --publish 8080:80 \
    --publish 3306:3306

# List pods
podman pod list
podman pod ps

# Add containers to pod
podman run -d --pod webapp-pod \
    --name webapp-nginx \
    nginx

podman run -d --pod webapp-pod \
    --name webapp-mysql \
    -e MYSQL_ROOT_PASSWORD=secret \
    mysql:5.7

# Pod operations
podman pod start webapp-pod
podman pod stop webapp-pod
podman pod restart webapp-pod
podman pod logs webapp-pod

# Inspect pod
podman pod inspect webapp-pod

# Remove pod and containers
podman pod rm -f webapp-pod

Kubernetes YAML Generation

Generate Kubernetes manifests:

# Generate pod YAML
podman generate kube webapp-pod > webapp-pod.yaml

# Create pod from YAML
podman play kube webapp-pod.yaml

# Generate systemd unit files
podman generate systemd --new --name webapp-pod > webapp-pod.service

# Complex pod example
cat > complex-pod.yaml <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: complex-app
spec:
  containers:
  - name: frontend
    image: nginx:latest
    ports:
    - containerPort: 80
    volumeMounts:
    - name: shared-data
      mountPath: /usr/share/nginx/html
  - name: backend
    image: node:14
    command: ["node", "server.js"]
    ports:
    - containerPort: 3000
    volumeMounts:
    - name: shared-data
      mountPath: /app/data
  volumes:
  - name: shared-data
    emptyDir: {}
EOF

podman play kube complex-pod.yaml

Container Orchestration

Systemd Integration

Manage containers with systemd:

# Create container with systemd integration
podman create --name systemd-nginx \
    --systemd=true \
    -p 8080:80 \
    nginx

# Generate systemd service
podman generate systemd --new --name systemd-nginx \
    > ~/.config/systemd/user/container-nginx.service

# Enable and start service
systemctl --user daemon-reload
systemctl --user enable container-nginx.service
systemctl --user start container-nginx.service

# Auto-update containers
cat > ~/.config/systemd/user/podman-auto-update.service <<EOF
[Unit]
Description=Podman auto-update service
Documentation=man:podman-auto-update(1)

[Service]
Type=oneshot
ExecStart=/usr/bin/podman auto-update

[Install]
WantedBy=multi-user.target
EOF

cat > ~/.config/systemd/user/podman-auto-update.timer <<EOF
[Unit]
Description=Podman auto-update timer

[Timer]
OnCalendar=daily
Persistent=true

[Install]
WantedBy=timers.target
EOF

systemctl --user enable podman-auto-update.timer

Container Compose

Use podman-compose for multi-container apps:

# Install podman-compose
pip3 install podman-compose

# Create docker-compose.yml
cat > docker-compose.yml <<EOF
version: '3'

services:
  web:
    image: nginx:latest
    ports:
      - "8080:80"
    volumes:
      - ./html:/usr/share/nginx/html
    depends_on:
      - app

  app:
    image: node:14
    command: node server.js
    working_dir: /app
    volumes:
      - ./app:/app
    environment:
      - NODE_ENV=production
      - DB_HOST=db

  db:
    image: postgres:13
    environment:
      - POSTGRES_DB=myapp
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=password
    volumes:
      - db_data:/var/lib/postgresql/data

volumes:
  db_data:
EOF

# Run with podman-compose
podman-compose up -d
podman-compose ps
podman-compose logs
podman-compose down

Storage Management

Volume Management

Work with Podman volumes:

# Create volumes
podman volume create webapp-data
podman volume create --opt size=10G large-data

# List volumes
podman volume ls
podman volume inspect webapp-data

# Use volumes in containers
podman run -d --name webapp \
    -v webapp-data:/var/www/html \
    -p 8080:80 \
    nginx

# Backup volume
podman run --rm \
    -v webapp-data:/source:ro \
    -v $(pwd):/backup \
    centos:8 \
    tar czf /backup/webapp-data-backup.tar.gz -C /source .

# Restore volume
podman volume create webapp-data-restored
podman run --rm \
    -v webapp-data-restored:/target \
    -v $(pwd):/backup:ro \
    centos:8 \
    tar xzf /backup/webapp-data-backup.tar.gz -C /target

# Clean up volumes
podman volume prune
podman volume rm webapp-data

Advanced Storage Options

Configure storage drivers and options:

# Configure storage driver
cat > /etc/containers/storage.conf <<EOF
[storage]
driver = "overlay"
runroot = "/var/run/containers/storage"
graphroot = "/var/lib/containers/storage"

[storage.options]
additionalimagestores = [
  "/var/lib/shared/overlay-images",
  "/var/lib/shared/overlay-layers"
]

[storage.options.overlay]
mountopt = "nodev,metacopy=on"
EOF

# Use bind mounts with SELinux
podman run -d --name webapp \
    -v /host/data:/container/data:Z \
    -p 8080:80 \
    nginx

# Named volumes with options
podman volume create --opt type=nfs \
    --opt o=addr=nfs-server.example.com,rw \
    --opt device=:/exports/data \
    nfs-volume

Network Configuration

Advanced Networking

Configure container networking:

# Create custom networks
podman network create --driver bridge \
    --subnet 172.20.0.0/16 \
    --gateway 172.20.0.1 \
    --ip-range 172.20.240.0/20 \
    custom-net

# Create macvlan network
podman network create -d macvlan \
    --subnet 192.168.1.0/24 \
    --gateway 192.168.1.1 \
    -o parent=eth0 \
    macvlan-net

# Connect container to multiple networks
podman run -d --name multi-net \
    --network custom-net \
    nginx

podman network connect macvlan-net multi-net

# Configure DNS
podman run -d --name webapp \
    --dns 8.8.8.8 \
    --dns-search example.com \
    --add-host db-server:192.168.1.100 \
    nginx

# Network aliases
podman run -d --name database \
    --network custom-net \
    --network-alias db \
    --network-alias mysql \
    mysql:5.7

Container Communication

Inter-container networking:

# Create pod with shared network
podman pod create --name app-pod \
    --share net \
    --publish 8080:80

# Add containers sharing localhost
podman run -d --pod app-pod \
    --name frontend \
    nginx

podman run -d --pod app-pod \
    --name backend \
    -e FRONTEND_URL=http://localhost:80 \
    node:14

# Container-to-container communication
podman network create app-network

podman run -d --name api \
    --network app-network \
    --hostname api-server \
    myapi:latest

podman run -d --name web \
    --network app-network \
    -e API_HOST=api-server \
    myweb:latest

Security Features

Container Security

Implement security best practices:

# Run with security options
podman run -d --name secure-app \
    --security-opt no-new-privileges \
    --security-opt label=disable \
    --cap-drop ALL \
    --cap-add NET_BIND_SERVICE \
    --read-only \
    --tmpfs /tmp \
    nginx

# User namespace mapping
podman run -d --name mapped-user \
    --userns=keep-id \
    --user 1000:1000 \
    nginx

# SELinux contexts
podman run -d --name selinux-app \
    --security-opt label=type:container_t \
    --security-opt label=level:s0:c100,c200 \
    nginx

# Seccomp profiles
podman run -d --name restricted-app \
    --security-opt seccomp=/path/to/seccomp.json \
    nginx

# AppArmor profiles (if available)
podman run -d --name apparmor-app \
    --security-opt apparmor=custom-profile \
    nginx

Image Security Scanning

Scan container images:

# Install skopeo
yum install skopeo

# Inspect remote images
skopeo inspect docker://docker.io/nginx:latest

# Copy images between registries
skopeo copy \
    docker://docker.io/nginx:latest \
    docker://myregistry.example.com/nginx:latest

# Vulnerability scanning with Trivy
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin
trivy image nginx:latest

# Scan with podman
podman image trust show nginx

Registry Management

Private Registry Setup

Deploy private container registry:

# Run registry container
podman run -d --name registry \
    -p 5000:5000 \
    -v registry-data:/var/lib/registry \
    registry:2

# Configure insecure registry
cat >> /etc/containers/registries.conf <<EOF
[[registry]]
location = "localhost:5000"
insecure = true
EOF

# Push to private registry
podman tag myapp:latest localhost:5000/myapp:latest
podman push localhost:5000/myapp:latest

# Pull from private registry
podman pull localhost:5000/myapp:latest

# Secure registry with TLS
mkdir -p certs
openssl req -newkey rsa:4096 -nodes -sha256 \
    -keyout certs/domain.key -x509 -days 365 \
    -out certs/domain.crt \
    -subj "/C=US/ST=State/L=City/O=Organization/CN=registry.example.com"

podman run -d --name secure-registry \
    -p 443:443 \
    -v $(pwd)/certs:/certs:ro \
    -v registry-data:/var/lib/registry \
    -e REGISTRY_HTTP_ADDR=0.0.0.0:443 \
    -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
    -e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
    registry:2

Migration and Compatibility

Docker to Podman Migration

Migrate from Docker:

# Export Docker images
docker save -o images.tar $(docker images -q)

# Import to Podman
podman load -i images.tar

# Convert Docker Compose
pip3 install podman-compose
podman-compose up -d

# Alias docker to podman
alias docker=podman

# Socket compatibility
systemctl enable --now podman.socket
export DOCKER_HOST=unix:///run/user/$(id -u)/podman/podman.sock

# Migrate volumes
for vol in $(docker volume ls -q); do
    docker run --rm -v $vol:/source:ro -v $(pwd):/backup alpine \
        tar czf /backup/$vol.tar.gz -C /source .
    podman volume create $vol
    podman run --rm -v $vol:/target -v $(pwd):/backup:ro alpine \
        tar xzf /backup/$vol.tar.gz -C /target
done

Monitoring and Troubleshooting

Container Monitoring

Monitor container performance:

# Real-time stats
podman stats

# Container health checks
podman run -d --name healthcheck-app \
    --health-cmd="curl -f http://localhost/ || exit 1" \
    --health-interval=30s \
    --health-retries=3 \
    --health-timeout=10s \
    --health-start-period=40s \
    nginx

# Check container health
podman healthcheck run healthcheck-app
podman inspect healthcheck-app --format='{{.State.Health.Status}}'

# Export metrics
podman system df
podman system info --format json

# Integration with monitoring tools
cat > /usr/local/bin/podman-exporter.sh <<'EOF'
#!/bin/bash
# Export Podman metrics for Prometheus

echo "# HELP podman_containers_total Total number of containers"
echo "# TYPE podman_containers_total gauge"
echo "podman_containers_total $(podman ps -aq | wc -l)"

echo "# HELP podman_containers_running Running containers"
echo "# TYPE podman_containers_running gauge"
echo "podman_containers_running $(podman ps -q | wc -l)"

echo "# HELP podman_images_total Total number of images"
echo "# TYPE podman_images_total gauge"
echo "podman_images_total $(podman images -q | wc -l)"
EOF
chmod +x /usr/local/bin/podman-exporter.sh

Troubleshooting

Debug container issues:

# Debug commands
podman logs --tail 50 -f container-name
podman events --since 1h
podman inspect container-name

# System cleanup
podman system prune -a
podman volume prune
podman image prune -a

# Reset Podman
podman system reset

# Debug networking
podman network inspect bridge
podman port container-name
nsenter -t $(podman inspect -f '{{.State.Pid}}' container-name) -n ip a

# Storage debugging
podman info --debug
podman system df -v

Best Practices

Production Guidelines

  1. Use rootless containers when possible for better security
  2. Implement health checks for all production containers
  3. Set resource limits to prevent resource exhaustion
  4. Use specific image tags instead of 'latest'
  5. Scan images for vulnerabilities before deployment
  6. Implement proper logging and monitoring
  7. Regular cleanup of unused resources

Container Management Script

#!/bin/bash
# Comprehensive container management script

echo "=== Podman Container Health Check ==="

# Check running containers
running_containers=$(podman ps -q | wc -l)
echo "Running containers: $running_containers"

# Check unhealthy containers
unhealthy=$(podman ps --filter health=unhealthy -q | wc -l)
if [ $unhealthy -gt 0 ]; then
    echo "WARNING: $unhealthy unhealthy containers detected"
    podman ps --filter health=unhealthy
fi

# Check disk usage
disk_usage=$(podman system df --format "{{.Type}}\t{{.Size}}" | grep Images | awk '{print $2}')
echo "Image storage usage: $disk_usage"

# Check for containers with high memory usage
podman stats --no-stream --format "table {{.Container}}\t{{.MemPerc}}" | \
    awk '$2 > 80 {print "High memory usage:", $1, $2"%"}'

# Clean up stopped containers older than 24 hours
podman container prune -f --filter until=24h

echo "Container health check completed"

Conclusion

Podman provides a secure, daemonless container runtime perfect for CentOS/RHEL environments. Its compatibility with Docker, support for rootless containers, and integration with systemd make it an excellent choice for enterprise container deployments. Master these features to build robust, secure containerized applications.