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

19
LICENSE Normal file
View File

@@ -0,0 +1,19 @@
MIT License Copyright (c) 2024 uumas
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next
paragraph) shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

1
README.md Normal file
View File

@@ -0,0 +1 @@
Roles for installing services in podman containers

10
galaxy.yaml Normal file
View File

@@ -0,0 +1,10 @@
---
namespace: uumas
name: podman
description: Roles for installing services in podman containers
readme: README.md
version: 0.1.0
repository: "https://git.uumas.fi/uumas/ansible-podman"
license_file: LICENSE
authors:
- uumas

2
meta/runtime.yaml Normal file
View File

@@ -0,0 +1,2 @@
---
requires_ansible: ">=2.15.0"

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