Merge branch 'main' of ssh://git.valid.dk:2222/daniel/ansible-podman into HEAD

This commit is contained in:
Daniel Akulenok
2025-09-17 14:46:07 +02:00
5 changed files with 293 additions and 253 deletions

529
README.md
View File

@@ -1,182 +1,163 @@
Podman # Podman Role
======
This Ansible role installs and configures Podman container runtime, and provides comprehensive container, pod, network, and volume management capabilities. **Bootstrap containerized applications with Podman in minutes.**
Requirements ## 🚀 Quick Start
------------
- Ansible 2.11 or higher
- Target systems: Ubuntu 20.04+, Debian 11+
- containers.podman collection (for container management tasks)
Role Variables
--------------
### Package Installation
* `podman_packages`: List of Podman packages to install (includes core and additional packages for Debian/Ubuntu)
### Configuration Variables
* `podman_configure_registries`: Configure container registries (default: `true`)
* `podman_registries_conf_path`: Path to registries configuration (default: `/etc/containers/registries.conf`)
* `podman_registries_additional`: Additional registry configurations for special cases
**Note**: Registry configuration is now unified with the image signature policy through `podman_policy_trusted_registries`. Each registry in the policy configuration includes both security settings (signature verification) and registry behavior (insecure, blocked, unqualified search).
* `podman_configure_storage`: Configure storage settings (default: `true`)
* `podman_storage_conf_path`: Path to storage configuration (default: `/etc/containers/storage.conf`)
* `podman_storage_driver`: Storage driver to use (default: `overlay`)
* `podman_storage_runroot`: Runtime storage path (default: `/run/containers/storage`)
* `podman_storage_graphroot`: Persistent storage path (default: `/var/lib/containers/storage`)
* `podman_configure_policy`: Configure container policy (default: `true`)
* `podman_policy_path`: Path to policy configuration (default: `/etc/containers/policy.json`)
### Image Signature Policy Variables
* `podman_policy_default_type`: Default policy for unlisted registries (`"insecureAcceptAnything"` or `"reject"`)
* `podman_policy_reject_unknown_registries`: Reject images from unlisted registries (default: `false`)
* `podman_policy_trusted_registries`: Unified registry configuration for both policy and registries.conf
Each registry in `podman_policy_trusted_registries` supports:
**Security Policy Options:**
- `type`: Verification type (`"insecureAcceptAnything"`, `"signedBy"`, `"reject"`)
- `keyPath`: Path to GPG key file (for `signedBy` type)
- `keyData`: Inline GPG key data (alternative to `keyPath`)
**Registry Configuration Options:**
- `insecure`: Allow insecure (HTTP) connections (default: `false`)
- `blocked`: Block access to this registry (default: `false`)
- `unqualified_search`: Include in unqualified image searches (default: `true`)
- `mirror`: List of mirror registries for redundancy/performance
### 1. Basic Setup
```yaml ```yaml
# Unified registry and policy configuration - hosts: servers
podman_policy_trusted_registries: roles:
- registry: "docker.io" - podman
# Policy settings
type: "insecureAcceptAnything"
# Registry settings
insecure: false
blocked: false
unqualified_search: true
- registry: "internal-registry.company.com"
# Policy settings
type: "signedBy"
keyPath: "/etc/pki/containers/company.gpg"
# Registry settings
insecure: false
blocked: false
unqualified_search: true
# Mirror configuration
mirror:
- location: "backup-registry.company.com"
insecure: false
# Development configuration (default)
podman_policy_default_type: "insecureAcceptAnything"
podman_policy_reject_unknown_registries: false
# Production configuration with signature verification
podman_policy_default_type: "reject"
podman_policy_reject_unknown_registries: true
``` ```
### Service Variables ### 2. Run Your First Container
```yaml
- hosts: servers
roles:
- role: podman
vars:
podman_containers:
- name: nginx
image: nginx:latest
state: started
ports:
- "80:80"
```
* `podman_enable_socket`: Enable Podman socket service (default: `false`) ### 3. Common Patterns
* `podman_enable_api_service`: Enable Podman API service (default: `false`)
### Container Management Variables **Web application with database:**
```yaml
podman_containers:
- name: webapp
image: myapp:latest
state: started
ports:
- "8080:8080"
env:
DATABASE_URL: "postgresql://postgres@db:5432/app"
- name: database
image: postgres:15
state: started
volumes:
- "db-data:/var/lib/postgresql/data"
env:
POSTGRES_DB: app
POSTGRES_PASSWORD: secret
podman_volumes:
- name: db-data
```
**That's it!** Podman will be installed, configured, and your containers will be running with systemd services automatically created.
---
## 📋 Requirements
- **Ansible**: 2.11+
- **Target OS**: Ubuntu 20.04+, Debian 11+
- **Collection**: `containers.podman` (auto-installed)
---
## 🔧 Complete Feature Reference
### Container Management
```yaml ```yaml
podman_containers: podman_containers:
- name: nginx - name: my-app
image: docker.io/nginx:latest image: nginx:latest
state: started state: started # started|stopped|present|absent
ports: ports:
- "8080:80" - "8080:80"
- "443:443"
volumes: volumes:
- "/etc/nginx/conf.d:/etc/nginx/conf.d:ro" - "/host/path:/container/path"
- "volume-name:/data"
env: env:
NGINX_HOST: example.com ENV_VAR: value
restart_policy: always
user: nginx
networks: networks:
- podman - app-network
restart_policy: always # no|always|on-failure|unless-stopped
user: "1000:1000"
labels: labels:
app: webserver app: web
version: "1.0" environment: prod
memory: "1g"
cpu_shares: 1024
device:
- "/dev/sda:/dev/xvda:rwm"
security_opt:
- "seccomp=unconfined"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost"]
interval: 30s
timeout: 10s
retries: 3
# Systemd service generation (optional)
generate_systemd:
path: "/etc/systemd/system"
restart_policy: always
after: ["network.target"]
wants: ["network-online.target"]
``` ```
### Network Management Variables ### Network Management
```yaml ```yaml
podman_networks: podman_networks:
- name: app-network - name: app-network
driver: bridge
subnet: "10.89.0.0/24"
gateway: "10.89.0.1"
state: present state: present
internal: false driver: bridge # bridge|macvlan|ipvlan
disable_dns: false subnet: "172.20.0.0/16"
gateway: "172.20.0.1"
internal: false # true for isolated networks
dns: dns:
- "8.8.8.8" - "8.8.8.8"
- "1.1.1.1"
options: options:
mtu: 1500 mtu: 1500
vlan: 100 vlan: 100
ipam_driver: "host-local"
interface_name: "podman1" # Advanced networking
route:
- "10.10.0.0/16,192.168.1.1"
- name: macvlan-net - name: macvlan-net
driver: macvlan driver: macvlan
macvlan: "eth0" macvlan: "eth0" # Parent interface
subnet: "192.168.1.0/24" subnet: "192.168.1.0/24"
- name: ipv6-net - name: ipv6-net
driver: bridge driver: bridge
subnet: "fd00::/64" subnet: "fd00::/64"
ipv6: true ipv6: true
recreate: false
``` ```
### Volume Management Variables ### Volume Management
```yaml ```yaml
podman_volumes: podman_volumes:
- name: app-data - name: app-data
state: present state: present
driver: local driver: local # local|tmpfs
labels: labels:
environment: production
backup: daily backup: daily
environment: prod
options: options:
- "device=/dev/sdb1" - "device=/dev/sdb1"
- "type=ext4" - "type=ext4"
- "o=rw" - "o=rw"
- name: tmpfs-volume - name: tmpfs-volume
state: present
driver: tmpfs driver: tmpfs
options: options:
- "tmpfs-size=100m" - "tmpfs-size=100m"
- "tmpfs-mode=1777" - "tmpfs-mode=1777"
- name: quadlet-volume
state: quadlet
driver: local
quadlet_filename: "custom-volume"
quadlet_file_mode: "0640"
quadlet_options:
- "Group=192"
- "Copy=true"
recreate: false
debug: false
``` ```
### Pod Management Variables ### Pod Management
```yaml ```yaml
podman_pods: podman_pods:
@@ -184,7 +165,6 @@ podman_pods:
state: started state: started
ports: ports:
- "8080:80" - "8080:80"
- "3306:3306"
networks: networks:
- frontend - frontend
hostname: webapp hostname: webapp
@@ -192,162 +172,223 @@ podman_pods:
- "8.8.8.8" - "8.8.8.8"
labels: labels:
app: webapp app: webapp
tier: frontend
volumes: volumes:
- "webapp-data:/data" - "webapp-data:/data"
infra: true
infra_image: "k8s.gcr.io/pause:3.1"
memory: "2g" memory: "2g"
cpu_shares: "1024" cpu_shares: 1024
security_opt: share: "net,ipc" # Shared namespaces
- "seccomp=unconfined" infra: true # Use infra container
add_host: infra_image: "registry.k8s.io/pause:3.9"
- "database.local:127.0.0.1"
share: "net,ipc"
userns: "auto"
device:
- "/dev/sda:/dev/xvda:rwm"
sysctl:
net.core.somaxconn: "1024"
exit_policy: "stop"
- name: quadlet-pod
state: quadlet
quadlet_filename: "custom-pod"
quadlet_options:
- "AutoUpdate=registry"
generate_systemd:
path: "/etc/systemd/system"
restart_policy: "always"
``` ```
### Systemd Service Generation ### Advanced Configuration
The role can automatically generate systemd service files for containers and pods. This functionality helps in managing container lifecycle through systemd.
* `podman_generate_systemd`: Enable systemd service generation (default: `true`)
* `podman_systemd_dir`: Directory for generated service files (default: `/etc/systemd/system`)
**Global Systemd Options** (`podman_systemd_options`):
#### Registry & Security Policy
```yaml ```yaml
# Basic registry setup (development)
podman_policy_default_type: "insecureAcceptAnything"
podman_policy_reject_unknown_registries: false
# Production security (with signatures)
podman_policy_default_type: "reject"
podman_policy_reject_unknown_registries: true
podman_policy_trusted_registries:
- registry: "docker.io"
type: "insecureAcceptAnything"
unqualified_search: true
- registry: "internal.company.com"
type: "signedBy"
keyPath: "/etc/pki/containers/company.gpg"
insecure: false
mirror:
- location: "backup.company.com"
```
#### Systemd Service Generation
```yaml
# Global systemd settings
podman_generate_systemd: true
podman_systemd_options: podman_systemd_options:
new: true # Generate new service files restart_policy: always
force: true # Overwrite existing files stop_timeout: 120
restart_policy: always # Default restart policy after: ["network.target"]
stop_timeout: 120 # Stop timeout in seconds wants: ["network-online.target"]
no_header: false # Include header in service files container_prefix: "container-"
wants: [] # Systemd unit Wants pod_prefix: "pod-"
after: [] # Systemd unit After
requires: [] # Systemd unit Requires
container_prefix: "container-" # Prefix for container service names
pod_prefix: "pod-" # Prefix for pod service names
restart_sec: 30 # Restart delay in seconds
``` ```
**Per-Container/Pod Configuration:** #### Resource Cleanup
You can override global systemd options for individual containers or pods:
```yaml
podman_containers:
- name: webapp
image: nginx:latest
systemd:
restart_policy: always
after: ["network.target"]
wants: ["network-online.target"]
restart_sec: 10
podman_pods:
- name: database
systemd:
restart_policy: on-failure
requires: ["network.target"]
time: 180
```
When `systemd` is defined for a container or pod, the role will:
1. Generate a systemd service file
2. Place it in the specified directory
3. Reload systemd daemon
4. (Optional) Enable and start the service
**Note:** Container/pod-specific options take precedence over global options defined in `podman_systemd_options`.
### Resource Pruning
The role can automatically clean up unused Podman resources to free up disk space and maintain system hygiene.
* `podman_prune_enabled`: Enable automatic pruning of unused resources (default: `true`)
* `podman_prune_options`: Configuration for what should be pruned
```yaml ```yaml
# Auto-cleanup unused resources
podman_prune_enabled: true
podman_prune_options: podman_prune_options:
container: true # Remove stopped containers container: true # Remove stopped containers
image: true # Remove unused images image: true # Remove unused images
network: true # Remove unused networks network: true # Remove unused networks
system: true # Prune all unused data
system_all: true # Prune all unused data including build cache
volume: true # Remove unused volumes volume: true # Remove unused volumes
system: true # Full system cleanup
``` ```
You can selectively disable certain types of pruning by setting their values to `false`: #### Storage Configuration
```yaml ```yaml
podman_prune_options: podman_configure_storage: true
container: true # Still remove containers podman_storage_driver: overlay
image: false # Keep all images podman_storage_graphroot: /var/lib/containers/storage
network: true # Remove unused networks podman_storage_runroot: /run/containers/storage
system: false # Keep system data
system_all: false # Keep build cache
volume: false # Keep all volumes
``` ```
Dependencies #### API & Socket Services
------------
* `containers.podman` collection for container management tasks
Example Playbook
----------------
```yaml ```yaml
- hosts: servers podman_enable_socket: true # Enable Podman socket
podman_enable_api_service: true # Enable REST API
```
---
## 🏷️ Available Tags
Run specific parts of the role:
```bash
# Install only
ansible-playbook -t podman-install playbook.yml
# Configure only
ansible-playbook -t podman-configure playbook.yml
# Manage containers only
ansible-playbook -t podman-containers playbook.yml
# Manage networks only
ansible-playbook -t podman-networks playbook.yml
```
**Available tags:**
- `podman` - Run everything
- `podman-install` - Package installation
- `podman-configure` - Configuration files
- `podman-services` - System services
- `podman-networks` - Network management
- `podman-volumes` - Volume management
- `podman-pods` - Pod management
- `podman-containers` - Container management
- `podman-systemd` - Systemd service generation
- `podman-prune` - Resource cleanup
---
## 📚 Example Playbooks
### Development Environment
```yaml
- hosts: dev-servers
roles: roles:
- role: keepit.podman - role: podman
vars: vars:
# Permissive for development
podman_policy_default_type: "insecureAcceptAnything"
podman_enable_socket: true podman_enable_socket: true
podman_containers: podman_containers:
- name: web-server - name: dev-web
image: nginx:latest image: nginx:latest
state: started state: started
ports: ports:
- "80:80" - "8080:80"
volumes: volumes:
- "/var/www/html:/usr/share/nginx/html:ro" - "./web:/usr/share/nginx/html"
podman_networks:
- name: web-network
subnet: "172.20.0.0/16"
``` ```
Tags ### Production Environment
---- ```yaml
- hosts: prod-servers
roles:
- role: podman
vars:
# Strict security for production
podman_policy_default_type: "reject"
podman_policy_reject_unknown_registries: true
podman_policy_trusted_registries:
- registry: "registry.company.com"
type: "signedBy"
keyPath: "/etc/pki/containers/prod.gpg"
* `podman` - Run all tasks podman_containers:
* `podman-install` - Install packages only - name: prod-app
* `podman-configure` - Configure Podman only image: registry.company.com/app:v1.2.3
* `podman-services` - Manage services only state: started
* `podman-networks` - Manage networks only restart_policy: always
* `podman-volumes` - Manage volumes only memory: "2g"
* `podman-pods` - Manage pods only cpu_shares: 2048
* `podman-containers` - Manage containers only healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/health"]
interval: 30s
timeout: 10s
retries: 3
```
License ### Multi-Service Application
------- ```yaml
- hosts: app-servers
roles:
- role: podman
vars:
podman_networks:
- name: app-network
subnet: "172.20.0.0/16"
podman_volumes:
- name: postgres-data
- name: redis-data
- name: app-uploads
podman_containers:
# Database
- name: postgres
image: postgres:15
state: started
networks:
- app-network
volumes:
- "postgres-data:/var/lib/postgresql/data"
env:
POSTGRES_DB: myapp
POSTGRES_PASSWORD: "{{ vault_db_password }}"
# Cache
- name: redis
image: redis:7-alpine
state: started
networks:
- app-network
volumes:
- "redis-data:/data"
# Application
- name: app
image: myapp:latest
state: started
networks:
- app-network
ports:
- "80:8080"
volumes:
- "app-uploads:/app/uploads"
env:
DATABASE_URL: "postgresql://postgres:{{ vault_db_password }}@postgres:5432/myapp"
REDIS_URL: "redis://redis:6379"
depends_on:
- postgres
- redis
```
---
## 📄 License
MIT MIT
Author Information ## 👤 Author
------------------
Daniel Akulenok <podman@valid.dk> Daniel Akulenok <podman@valid.dk>

View File

@@ -5,11 +5,6 @@
podman_packages: podman_packages:
- podman - podman
- crun - crun
- buildah
- skopeo
- fuse-overlayfs
- slirp4netns
- uidmap
# Podman configuration # Podman configuration
podman_configure_registries: true podman_configure_registries: true

View File

@@ -14,7 +14,7 @@ platforms:
- name: debian-bullseye - name: debian-bullseye
image: docker.io/jrei/systemd-debian:12 image: docker.io/jrei/systemd-debian:12
<<: *platform_defaults <<: *platform_defaults
- name: debian-bullseye - name: debian-trixie
image: docker.io/jrei/systemd-debian:13 image: docker.io/jrei/systemd-debian:13
<<: *platform_defaults <<: *platform_defaults

View File

@@ -5,7 +5,7 @@
tasks: tasks:
- name: Ensure apt cache is up-to-date - name: Ensure apt cache is up-to-date
ansible.builtin.apt: ansible.builtin.apt:
update_cache: yes update_cache: true
cache_valid_time: 3600 cache_valid_time: 3600
- name: Install prerequisites used by the role/tests - name: Install prerequisites used by the role/tests

View File

@@ -3,7 +3,9 @@
- name: Generate systemd service files for containers - name: Generate systemd service files for containers
vars: vars:
systemd_opts: "{{ item.systemd if item.systemd is defined else (item.generate_systemd if item.generate_systemd is defined else {}) }}" systemd_opts: "{{
item.systemd if item.systemd is defined
else (item.generate_systemd if item.generate_systemd is defined else {}) }}"
containers.podman.podman_generate_systemd: containers.podman.podman_generate_systemd:
name: "{{ item.name }}" name: "{{ item.name }}"
dest: "{{ podman_systemd_dir }}" dest: "{{ podman_systemd_dir }}"
@@ -30,7 +32,9 @@
- name: Generate systemd service files for pods - name: Generate systemd service files for pods
vars: vars:
systemd_opts: "{{ item.systemd if item.systemd is defined else (item.generate_systemd if item.generate_systemd is defined else {}) }}" systemd_opts: "{{
item.systemd if item.systemd is defined
else (item.generate_systemd if item.generate_systemd is defined else {}) }}"
containers.podman.podman_generate_systemd: containers.podman.podman_generate_systemd:
name: "{{ item.name }}" name: "{{ item.name }}"
dest: "{{ podman_systemd_dir }}" dest: "{{ podman_systemd_dir }}"