Initial commit

Basic roles for installing podman, creating containers, networks and
services
This commit is contained in:
uumas
2024-07-28 16:13:03 +03:00
commit 5c46597261
24 changed files with 515 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
---
container_command: []
container_user: ""
container_mounts: []
container_publish_ports: []
container_networks: []
container_env: {}
container_auto_start: true
container_auto_update: true
container_requires: []
container_wants: []

View File

@@ -0,0 +1,7 @@
---
- name: "Restart container service {{ container_name }}"
ansible.builtin.systemd_service:
name: "{{ container_name }}.service"
state: restarted
daemon_reload: true
ignore_errors: '{{ ansible_check_mode }}'

View File

@@ -0,0 +1,101 @@
---
argument_specs:
main:
short_description: Sets up podman container with systemd units (quadlet)
options:
container_name:
description: Name of the container. Must be unique within a host.
type: str
required: true
container_image:
description: "The image to run in the container, in FQIN format (registry/imagename:tag)"
type: str
required: true
container_command:
description: Command to start the container with.
type: list
required: false
default: []
elements: str
container_user:
description: The UID to run as inside the container
type: str
required: false
default: ""
container_mounts:
description: List of bind mounts or volumes to be mounted inside the container.
type: list
required: false
default: []
elements: dict
options:
type:
description: Type of volume
type: str
required: true
choices:
- volume
- bind
source:
description:
- Mount source.
- If mount type is volume, name of the volume.
- If mount type is bind, host path to bind mount inside the container.
type: str
required: true
destination:
description: Path inside the container to mount at
type: str
required: true
readonly:
description: If true, volume will be mounted as read only inside the container
type: bool
required: false
default: false
container_publish_ports:
description: "A list of published ports in docker format (<host listen address>:<host port>:<container port>)"
type: list
required: false
default: []
elements: str
container_networks:
description: A list of podman networks for the container.
type: list
required: false
default: []
elements: str
container_env:
description: A dict of environment variables for the container
type: dict
required: false
default: {}
container_requires:
description: >
List of systemd units (like other containers) this one depends on.
You should ensure they are created before this one, or at least within
the same play, before handlers are flushed.
type: list
required: false
default: []
elements: str
container_wants:
description: >
List of systemd units (like other containers) this one wants.
You should ensure they are created within the same play, before handlers are flushed.
type: list
required: false
default: []
elements: str
container_auto_start:
description: Set to false to not start the container automatically on boot or restart on failure.
type: bool
required: false
default: true
container_auto_update:
description: Whether to let podman automatically update the container whenever the specified image gets updated
type: bool
required: false
default: true

View File

@@ -0,0 +1,3 @@
---
dependencies:
- role: podman

View File

@@ -0,0 +1,16 @@
---
- name: Create networks for container {{ container_name }}
ansible.builtin.include_role:
name: network
vars:
network_name: "{{ network }}"
loop: "{{ container_networks }}"
loop_control:
loop_var: network
- name: Create container service {{ container_name }}
ansible.builtin.template:
src: container.j2
dest: "/etc/containers/systemd/{{ container_name }}.container"
mode: "0600"
notify: "Restart container service {{ container_name }}"

View File

@@ -0,0 +1,46 @@
# {{ ansible_managed }}
[Unit]
Description=Container {{ container_name }}
{% for requirement in container_requires %}
Requires={{ requirement }}
After={{ requirement }}
{% endfor %}
{% for want in container_wants %}
Requires={{ want }}
Before={{ want }}
{% endfor %}
[Container]
Image={{ container_image }}
ContainerName={{ container_name }}
{% if container_command | length > 0 %}
Exec="{{ container_command | join('" "') }}"
{% endif %}
{% if container_user | length > 0 %}
User={{ container_user }}
{% endif %}
{% for mount in container_mounts %}
Mount={% for key, value in mount.items() %}{{ key }}={{ value }}{% if not loop.last %},{% endif %}{% endfor %}
{% endfor %}
{% for network in container_networks %}
Network={{ network }}.network
{% endfor %}
{% for port in container_publish_ports %}
PublishPort={{ port }}
{% endfor %}
{% for key, value in container_env.items() %}
Environment={{ key }}={{ value }}
{% endfor %}
{% if container_auto_update %}
AutoUpdate=registry
{% endif %}
{% if container_auto_start %}
[Service]
Restart=always
[Install]
WantedBy=multi-user.target
{% endif %}

View File

@@ -0,0 +1,9 @@
---
argument_specs:
main:
short_description: Sets up podman network with systemd unit (quadlet)
options:
network_name:
description: Name of the network. Must be unique within a host.
type: str
required: true

View File

@@ -0,0 +1,3 @@
---
dependencies:
- role: podman

View File

@@ -0,0 +1,7 @@
---
- name: "Create container network service {{ network_name }}"
ansible.builtin.template:
src: network.j2
dest: "/etc/containers/systemd/{{ network_name }}.network"
mode: "0644"
notify: Reload systemd daemon

View File

@@ -0,0 +1,7 @@
# {{ ansible_managed }}
[Unit]
Description=Container network {{ network_name }}
[Network]
NetworkName={{ network_name }}

View File

@@ -0,0 +1,4 @@
---
- name: Reload systemd daemon
ansible.builtin.systemd_service:
daemon_reload: true

View File

@@ -0,0 +1,5 @@
---
argument_specs:
main:
short_description: Installs podman
options: {}

View File

@@ -0,0 +1,16 @@
---
- name: Ensure host distribution is supported
ansible.builtin.import_role:
name: uumas.general.compatcheck
vars:
compatcheck_supported_distributions:
- name: debian
version_min: 13
- name: ubuntu
version_min: 24
tags: podman
- name: Install podman
ansible.builtin.apt:
name: podman
tags: podman

View File

@@ -0,0 +1,11 @@
---
service_domains: []
service_container_publish_ports: []
service_container_mounts: []
service_container_env: {}
service_additional_containers: []
service_requires: []
service_auto_update: true

View File

@@ -0,0 +1,7 @@
---
- name: "Restart socat socket for {{ service_name }}"
ansible.builtin.systemd_service:
name: "{{ service_name }}-socat.socket"
state: restarted
daemon_reload: true
ignore_errors: '{{ ansible_check_mode }}'

View File

@@ -0,0 +1,144 @@
---
argument_specs:
main:
short_description: Sets up a service in podman container(s)
options:
service_name:
description: Name of the service.
type: str
required: true
service_domains:
description: A list of domains which should be proxied to the main service container
type: list
required: false
default: []
service_container_http_port:
description:
- Port inside the container where http requests will be proxied to.
- Required if service_domains is not empty.
type: int
required: false
service_container_image:
description: "The image to run in the service container(s), in FQIN format (registry/imagename:tag)."
type: str
required: true
service_container_publish_ports:
description: "A list of published ports in docker format (<host listen address>:<host port>:<container port>)"
type: list
required: false
default: []
service_container_mounts:
description: List of bind mounts or volumes to be mounted inside the service container(s).
type: list
required: false
default: []
elements: dict
options:
type:
description: Type of volume
type: str
required: true
choices:
- volume
- bind
source:
description:
- Mount source.
- If mount type is volume, name of the volume.
- If mount type is bind, host path to bind mount inside the container.
type: str
required: true
destination:
description: Path inside the container to mount at
type: str
required: true
readonly:
description: If true, volume will be mounted as read only inside the container
type: bool
required: false
default: false
service_container_env:
description: A dict of environment variables for the service container(s)
type: dict
required: false
default: {}
service_additional_containers:
description:
- List of additional containers for the sercice.
- >
Will inherit most options from main service container, except for publish_ports.
All options can be overridden per-container.
type: list
required: false
default: []
elements: dict
options:
name:
description:
- Name of the container.
- >
This will be appended to the service name, so if for example service name is
nextcloud and this variable is cron, the resulting container will be called
nextcloud-cron
type: str
required: true
image:
description: "The image to run in the container, in FQIN format (registry/image:tag)"
type: str
required: false
default: "{{ service_container_image }}"
mounts:
description: List of bind mounts or volumes to be mounted inside the main service container.
type: list
required: false
default: "{{ service_container_mounts }}"
elements: dict
options:
type:
description: Type of volume
type: str
required: true
choices:
- volume
- bind
source:
description:
- Mount source.
- If mount type is volume, name of the volume.
- If mount type is bind, host path to bind mount inside the container.
type: str
required: true
destination:
description: Path inside the container to mount at
type: str
required: true
readonly:
description: If true, volume will be mounted as read only inside the container
type: bool
required: false
default: false
publish_ports:
description: "A list of published ports in docker format (<host listen address>:<host port>:<container port>)"
type: list
required: false
default: []
env:
description: A dict of environment variables for the container
type: dict
required: false
default: {}
service_requires:
description: List of systemd units this service container depends on.
type: list
required: false
default: []
elements: str
service_auto_update:
description: Whether to let podman automatically update the service containers whenever the specified image gets updated
type: bool
required: false
default: true

View File

@@ -0,0 +1,23 @@
---
- name: Mounts for {{ service_name }}
ansible.builtin.include_tasks: mounts.yaml
when: service_container_mounts | length > 0
- name: Main container for {{ service_name }}
ansible.builtin.import_role:
name: container
vars:
container_name: "{{ service_name }}"
container_image: "{{ service_container_image }}"
container_mounts: "{{ _service_container_mounts }}"
container_publish_ports: "{{ service_container_publish_ports }}"
container_networks:
- "{{ service_name }}"
container_env: "{{ service_container_env }}"
container_requires: "{{ service_requires }}"
container_wants: "{{ [service_name + '-socat.socket'] if service_domains | length > 0 else [] }}"
container_auto_update: "{{ service_auto_update }}"
- name: Reverse proxy for {{ service_name }}
ansible.builtin.include_tasks: proxy.yaml
when: service_domains | length > 0

View File

@@ -0,0 +1,22 @@
---
- name: Initialize variables
ansible.builtin.set_fact:
_service_container_mounts: []
- name: Set container named mounts
ansible.builtin.set_fact:
_service_container_mounts: >
{{ _service_container_mounts +
[mount | combine({'source': service_name + '-' + mount.source})] }}
when: mount.type == 'volume'
loop: "{{ service_container_mounts }}"
loop_control:
loop_var: mount
- name: Set container named mounts
ansible.builtin.set_fact:
_service_container_mounts: "{{ _service_container_mounts + [mount] }}"
when: mount.type == 'bind'
loop: "{{ service_container_mounts }}"
loop_control:
loop_var: mount

View File

@@ -0,0 +1,35 @@
---
- name: Socat socket for {{ service_name }}
ansible.builtin.template:
src: socat.socket.j2
dest: /etc/systemd/system/{{ service_name }}-socat.socket
mode: "0644"
notify: Restart socat socket for {{ service_name }}
- name: Socat container for {{ service_name }}
ansible.builtin.include_role:
name: container
vars:
container_name: "{{ service_name }}-socat"
container_image: "docker.io/alpine/socat:latest"
container_command:
- "ACCEPT-FD:3,fork"
- "TCP:{{ service_name }}:{{ service_container_http_port }}"
container_user: nobody
container_networks:
- "{{ service_name }}"
container_requires:
- "{{ service_name }}-socat.socket"
- "{{ service_name }}.service"
container_auto_start: false
container_auto_update: "{{ service_auto_update }}"
- name: Reverse proxy for {{ service_name }}
ansible.builtin.import_role:
name: uumas.general.vhost
vars:
vhost_type: reverse_proxy
vhost_id: "{{ service_name }}"
vhost_domains: "{{ service_domains }}"
vhost_proxy_target_netproto: unix
vhost_proxy_target_socket: "/run/{{ service_name }}-socat.sock"

View File

@@ -0,0 +1,6 @@
# {{ ansible_managed }}
[Unit]
Description={{ service_name }} socat socket
[Socket]
ListenStream=/run/{{ service_name }}-socat.sock