Compare commits

..

7 Commits

Author SHA1 Message Date
uumas
e4222635d9 Merge branch 'master' of git.uumas.fi:uumas/ansible-docker 2024-07-28 01:20:34 +03:00
uumas
a773666715 Update wordpress to use service role 2024-07-28 01:20:10 +03:00
uumas
5921c7d5db jitsi: adjustements etc 2024-07-28 01:14:45 +03:00
uumas
79ba8658a0 Migrate docker_volumes -> docker_mounts 2024-07-28 01:14:14 +03:00
uumas
fa1cdcc348 authentik: migrate to service role 2024-07-28 01:12:43 +03:00
uumas
87bb985211 Add service role 2024-07-28 01:12:26 +03:00
uumas
64d074ea4b alpine: import_role -> include_role 2024-07-28 01:10:49 +03:00
33 changed files with 1064 additions and 37 deletions

View File

@@ -1,7 +1,7 @@
---
- name: Alpine container
import_role:
include_role:
name: container
vars:
docker_service: alpine

View File

@@ -5,8 +5,8 @@
authentik_service_name: "authentik{{ '_' + docker_service_suffix if docker_service_suffix is defined else '' }}"
- name: Authentik container
ansible.builtin.import_role:
name: container
ansible.builtin.include_role:
name: service
vars:
docker_service: authentik
docker_image: beryju/authentik:latest
@@ -19,17 +19,18 @@
docker_env: "{{ authentik_common_env | combine(authentik_env) }}"
docker_mounts:
- path: /media
absolute_name: "{{ authentik_service_name }}_media"
name: "media"
- path: /templates
absolute_name: "{{ authentik_service_name }}_templates"
name: "templates"
- path: /certs
name: "{{ authentik_service_name }}_certs"
name: "certs"
- name: Authentik worker container
ansible.builtin.import_role:
name: container
ansible.builtin.include_role:
name: service
vars:
docker_service: authentik_worker
docker_namespace: authentik
docker_service: worker
docker_image: beryju/authentik:latest
docker_command:
- worker
@@ -37,8 +38,8 @@
docker_env: "{{ authentik_common_env | combine(authentik_env) }}"
docker_mounts:
- path: /media
absolute_name: "{{ authentik_service_name }}_media"
name: "media"
- path: /templates
absolute_name: "{{ authentik_service_name }}_templates"
name: "templates"
- path: /certs
name: "{{ authentik_service_name }}_certs"
name: "certs"

View File

@@ -1,8 +1,8 @@
---
authentik_common_env:
AUTHENTIK_REDIS__HOST: authentik_redis
AUTHENTIK_POSTGRESQL__HOST: authentik_db
AUTHENTIK_REDIS__HOST: authentik-redis
AUTHENTIK_POSTGRESQL__HOST: authentik-db
AUTHENTIK_POSTGRESQL__USER: authentik
AUTHENTIK_POSTGRESQL__NAME: authentik
AUTHENTIK_POSTGRESQL__PASSWORD: "{{ database_passwords.authentik }}"

View File

@@ -1,5 +1,20 @@
---
- name: Deprecation warning
debug:
msg: >
The role uumas.docker.container has been deprecated. You should switch to
uumas.docker.service instead. It has the following syntax changes:\n
* container name suffixes are separated by - instead of _\n
* May require updating hostnames in configs or database\n
* May require fixing any ports, database_passwords or
docker_vhost_domains variables to new syntax\n
* May require renaming database user and database for existing database
* docker_volume_type defaults to bind. Set it to named if you want to
continue using named volumes.\n
uumas.docker.container doesn't receive any updates and may be removed
after May 2024
- name: Container role initialization
import_tasks: init.yml

View File

@@ -6,9 +6,11 @@ dependencies:
docker_image: gitea/gitea:latest
docker_image_http_port: 3000
docker_database: postgres
docker_volumes:
- gitea_data:/data
- /var/lib/gitea/.ssh/:/data/git/.ssh
docker_mounts:
- name: gitea_data
path: /data
- src: /var/lib/gitea/.ssh/
path: /data/git/.ssh
docker_published_ports:
- "127.0.0.1:{{ ports.gitea.ssh }}:22"
docker_env:

View File

@@ -6,8 +6,9 @@ dependencies:
docker_image: quay.io/hedgedoc/hedgedoc:latest
docker_image_http_port: 3000
docker_database: postgres
docker_volumes:
- hedgedoc_uploads:/hedgedoc/public/uploads
docker_mounts:
- name: hedgedoc_uploads
path: /hedgedoc/public/uploads
docker_env:
CMD_DB_URL: postgres://hedgedoc:{{ database_passwords.hedgedoc }}@hedgedoc_db:5432/hedgedoc
CMD_DOMAIN: "{{ docker_vhost_domains.hedgedoc[0] }}"

View File

@@ -37,6 +37,7 @@
ENABLE_NOISY_MIC_DETECTION: '0'
ENABLE_BREAKOUT_ROOMS: '1'
JICOFO_AUTH_USER: focus
COLIBRI_WEBSOCKET_JVB_LOOKUP_NAME: jvb.meet.jitsi
XMPP_BOSH_URL_BASE: 'http://xmpp.meet.jitsi:5280'
XMPP_DOMAIN: meet.jitsi
XMPP_AUTH_DOMAIN: auth.meet.jitsi
@@ -45,8 +46,6 @@
restart_policy: always
networks:
- name: meet.jitsi
aliases:
- meet.jitsi
register: jitsi_meet_web_out
- name: Jitsi meet prosody
@@ -105,7 +104,7 @@
networks:
- name: meet.jitsi
aliases:
- meet.jitsi
- jicofo.meet.jitsi
- name: Jitsi meet video bridge
docker_container:
@@ -133,4 +132,4 @@
networks:
- name: meet.jitsi
aliases:
- meet.jitsi
- jvb.meet.jitsi

1
roles/service/README.md Normal file
View File

@@ -0,0 +1 @@
Sets up a docker service

View File

@@ -0,0 +1,28 @@
---
docker_namespace: "{{ docker_service }}"
docker_service_suffix: ""
docker_volume_type: bind
docker_restart_policy: always
reverse_proxy_type: caddy
docker_proxy_target_protocol: http
docker_additional_env: {}
docker_additional_services: []
docker_database: none
docker_mounts: []
docker_network_mode: ""
docker_networks: []
docker_env: {}
docker_published_ports: []
docker_host_user: false
dockerfile: []
docker_mariadb_config: {}
docker_redis_persistence: false
docker_phpmyadmin_basicauth: true
docker_phpmyadmin_basicauth_users: {}
timezone: Etc/UTC

View File

@@ -0,0 +1,13 @@
---
- name: Restart container
community.docker.docker_container:
name: "{{ docker_service_name }}"
restart: true
when: not container_out.changed
- name: Ensure container running
community.docker.docker_container:
name: "{{ docker_service_name }}"
state: started
when: not ansible_check_mode

View File

@@ -0,0 +1,216 @@
---
argument_specs:
main:
short_description: Docker service
description: "Sets up a docker service. Supports defining networks, building a custom image, setting up memcached, databases and a reverse proxy, creating a user on the host to run the container as, named volumes, bind mounts (either auto-created or existing)."
options:
docker_service:
description: "The name of the docker service (example: gitea)"
type: str
required: true
docker_namespace:
description: The namespace of the service. This can be used when multiple containers should be part of the service. Used as the default network, the name of the directory where bind mounts are created and the prefix for volume names.
type: str
required: false
default: "{{ docker_service }}"
docker_service_suffix:
description: "A suffix used to allow running multiple instances of the same service on a host. If docker_service is gitea and docker_service_suffix production, the service will be gitea-production"
type: str
required: false
default: ""
docker_image:
description: "Docker image to use for the container. If dockerfile is defined, it will be used as base for locally built image (example: gitea/gitea:latest)"
type: str
required: true
dockerfile:
description: "A list of dockerfile instructions to add to the base image"
type: list
elements: str
required: false
default: []
docker_host_user:
description: "If true, creates a user on the host for this service. The container will run as this user's uid/gid. Bind mount volumes will be owned by this user."
type: bool
required: false
default: true
docker_database:
description: "Database type to set up. It will be run in a docker container accessible to the service at host <service name (with -suffix if suffix is defined)>-db on default port."
type: str
required: false
choices:
- postgres
- mariadb
- mongo
- none
default: none
database_passwords:
description: >
database_passwords[docker_service] is a string with the password used
for communication between the service and database. Required if
docker_database is postgres or mariadb. For mariadb, you can also
define database_passwords[docker_service + '_root'] if you want to
set a password for the mariadb root user
type: dict
required: false
docker_mariadb_config:
description: |
A dict specifying config for mariadb. For example
mysqld:
innodb_buffer_pool_size: '4G'
aria_pagecache_buffer_size': '4G'
would set
[mysqld]
innodb_buffer_pool_size = 4G
aria_pagecache_buffer_size = 4G
type: dict
required: false
default: {}
docker_additional_services:
description: "List of additional services to configure (in separate containers). These will be accessible with hostname <docker_service_name>[_suffix]_<additional_service>"
type: list
required: false
elements: str
choices:
- memcached
- redis
default: []
docker_redis_persistence:
description: Whether to save redis data to persistent storage
type: bool
required: false
default: false
docker_phpmyadmin_basicauth:
description: Whether to enable basicauth for phpmyadmin
type: bool
required: false
default: true
docker_phpmyadmin_basicauth_users:
description: Dict of users and their password hashes for phpmyadmin basic auth. Required if docker_phpmyadmin_basicauth is true and phpmyadmin is used.
type: dict
required: false
default: {}
docker_volume_type:
description: "Defines whether to use named volumes or bind mounts for mounts with name"
type: str
required: false
choices:
- named
- bind
default: named
docker_mounts:
description: "List of bind mounts or volumes to be mounted inside the container. Each element is a dict with path and exactly one of name, src or template"
type: list
required: false
default: []
elements: dict
options:
path:
description: "The 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. Only applies for named and src mounts."
type: bool
required: false
default: false
mode:
description: "Permissions for the created/templated directory. Defaults to '0644' for files, '0755' for directories. Doesn't apply for named volumes."
type: str
required: false
name:
description: "If docker_volume_type is named, the name of the named volume to be mounted at path. If docker_volume_type is bind, the name of the folder to create under /opt/<service>[/suffix]/mounts/ and mount at path."
type: str
required: false
src:
description: "Host path to bind mount inside the container."
type: str
required: false
template:
description: "Name of template without .j2 extension. Will be templated at /opt/<service>[/suffix]/mounts/<template> and mounted read only inside the container."
type: str
required: false
copypath:
description: "Name of file or directory to copy. Will be deployed from files/<copypath> to /opt/<service>[/suffix]/mounts/<copypath> and mounted read only inside the container."
type: str
required: false
reverse_proxy_type:
description: "Defines which kind of reverse proxy to configure for the container. Traefik support is experimental."
type: str
required: false
choices:
- caddy
- traefik
- none
default: caddy
docker_image_http_port:
description: "The port for http listener inside the container. Will be mapped to the host port defined in ports. Required if reverse_proxy_type is not none."
type: int
required: false
docker_proxy_target_protocol:
description: "Protocol to use for proxy connection"
type: str
required: false
choices:
- http
- https
default: http
ports:
description: "ports[docker_service]['http'] or ports[docker_service]['https'] defines the port on which the container will listen on for reverse proxy connections. Required if reverse_proxy_type is caddy. If ports[docker_service]['phpmyadmin'] is defined and docker_database is mariadb, phpmyadmin will is installed."
type: dict
docker_vhost_domains:
description: "docker_vhost_domains[docker_service] is a list which defines which domains should be proxied to the container. Required if reverse_proxy_type is not none. docker_vhost_domains[docker_service + '-phpmyadmin'] is used for phpmyadmin"
type: dict
docker_published_ports:
description: "A list of published ports in docker format (<host listen address>:<host port>:<container port>)"
type: list
required: false
default: []
docker_networks:
description: A list of docker networks for docker service. Defaults to docker name
type: list
required: false
default:
- name: "{{ docker_namespace }}"
elements: dict
options:
name:
description: Name of the docker network
type: str
required: true
docker_env:
description: "A dict of environment variables for the container"
type: dict
required: false
default: {}
docker_entrypoint:
description: "Docker entrypoint as list of command and arguments"
type: list
required: false
elements: str
docker_command:
description: "Docker command as list of arguments or command and arguments"
type: list
required: false
elements: str
docker_memory:
description: Memory limit for the container
type: str
required: false
docker_restart_policy:
description: Restart policy of the service
type: str
required: false
default: always
docker_network_mode:
description: "Docker container network mode"
type: str
required: false
default: ""
timezone:
description: "Timezone set in the TZ env var"
type: str
required: false
default: Etc/UTC

View File

@@ -0,0 +1,4 @@
---
dependencies:
- role: docker

View File

@@ -0,0 +1,18 @@
---
- name: Converge
hosts: all
tasks:
- name: Nginx
include_role:
name: service
vars:
docker_service: nginx
docker_image: nginx
docker_image_http_port: 80
admin_email: test@example.com
ports:
nginx:
http: 28001
docker_vhost_domains:
nginx:
- localhost

View File

@@ -0,0 +1,15 @@
---
dependency:
name: galaxy
driver:
name: podman
platforms:
- name: bullseye
image: git.uumas.fi/uumas/molecule-testbed:bullseye-docker
command: /lib/systemd/systemd
pre_build_image: true
privileged: true
provisioner:
name: ansible
verifier:
name: ansible

View File

@@ -0,0 +1,83 @@
---
- name: Verify
hosts: all
gather_facts: false
tasks:
- name: Ensure https://localhost returns 200
ansible.builtin.uri:
url: https://localhost
validate_certs: false
return_content: true
register: get_localhost
- name: Assert caddy responded on https://localhost
ansible.builtin.assert:
that: "(get_localhost.server | split(', '))[0] == 'Caddy'"
- name: Assert nginx responded on https://localhost
ansible.builtin.assert:
that: "(get_localhost.server | split(', '))[1].startswith('nginx')"
- name: Get /opt/nginx directory info
ansible.builtin.stat:
path: /opt/nginx
register: opt_nginx_stat
- name: Assert /opt/nginx doesn't exist
ansible.builtin.assert:
that: not opt_nginx_stat.stat.exists
msg: /opt/nginx should not have been created but it was
- name: Get host passwd nginx user
ansible.builtin.getent:
database: passwd
key: nginx
fail_key: false
- name: Assert nginx user does not exist
ansible.builtin.assert:
that: getent_passwd.nginx == None
msg: "nginx user should not exist but it does ({{ getent_passwd }})"
- name: Get nginx container info
community.docker.docker_container_info:
name: nginx
register: container_out
- name: Assert container port 80 forwarded to host 28001
ansible.builtin.assert:
that:
- "container_out.container.HostConfig.PortBindings['80/tcp'] is defined"
- "container_out.container.HostConfig.PortBindings['80/tcp'][0].HostPort == '28001'"
msg: "Container port 80 not correctly forwarded to host port. Port bindings output was {{ container_out.container.HostConfig.PortBindings }}"
- name: Assert container user not set
ansible.builtin.assert:
that: container_out.container.Config.User == ""
- name: Get container image info
community.docker.docker_image_info:
name: "{{ container_out.container.Image }}"
register: container_image_out
- name: Assert nginx image not built locally
assert:
that: container_image_out.images[0].RepoTags[0] == 'nginx:latest'
msg: "Nginx image tag incorrect. It should have been nginx:latest but it was {{ container_image_out.images[0].RepoTags }}"
- name: Get docker host info
community.docker.docker_host_info:
volumes: true
register: docker_host_out
- name: Assert all containers are running
ansible.builtin.assert:
that: docker_host_out.host_info.Containers == docker_host_out.host_info.ContainersRunning
msg: There should have been {{ docker_host_out.host_info.Containers }} containers running but there were {{ docker_host_out.host_info.ContainersRunning }}
- name: Assert no extra containers were created
ansible.builtin.assert:
that: docker_host_out.host_info.Containers == 1
msg: There should have been 1 container created but there were {{ docker_host_out.host_info.Containers }}
- name: Assert no extra images were pulled
ansible.builtin.assert:
that: docker_host_out.host_info.Images == 1
msg: There should have been 1 image present but there were {{ docker_host_out.host_info.Images }}
- name: Assert no volumes were created
ansible.builtin.assert:
that: docker_host_out.volumes | length == 0
msg: There should have been no volumes present but there were {{ docker_host_out.volumes | length }}

View File

@@ -0,0 +1,51 @@
---
- name: Store docker additional services so it doesn't get reset
ansible.builtin.set_fact:
_docker_additional_services: "{{ docker_additional_services }}"
- name: "Memcached container for {{ docker_service_name }}"
ansible.builtin.include_role:
name: service
vars:
docker_namespace: "{{ _docker_namespace }}"
docker_service: memcached
docker_image: memcached:alpine
reverse_proxy_type: none
docker_mounts: []
docker_published_ports: []
docker_env: {}
docker_additional_env: {}
docker_networks: []
docker_database: none
docker_additional_services: []
docker_host_user: false
dockerfile: []
docker_command: "{{ omit }}"
docker_entrypoint: "{{ omit }}"
docker_memory: "{{ omit }}"
when: "'memcached' in _docker_additional_services"
- name: "Redis container for {{ docker_service_name }}"
ansible.builtin.include_role:
name: service
vars:
docker_namespace: "{{ _docker_namespace }}"
docker_service: redis
docker_image: redis:alpine
reverse_proxy_type: none
docker_mounts:
- name: redis
path: /data
docker_command: "{{ '--save 60 1' if docker_redis_persistence else omit }}"
docker_published_ports: []
docker_env: {}
docker_additional_env: {}
docker_networks: []
docker_database: none
docker_additional_services: []
docker_host_user: false
dockerfile: []
docker_entrypoint: "{{ omit }}"
docker_memory: "{{ omit }}"
when: "'redis' in _docker_additional_services"

View File

@@ -0,0 +1,94 @@
---
- name: Set postgres container vars
ansible.builtin.set_fact:
db_container_image: 'postgres:14-alpine'
db_container_env:
POSTGRES_USER: "{{ docker_service_underscore_name }}"
POSTGRES_PASSWORD: "{{ database_passwords[docker_service_name] }}"
db_container_data: /var/lib/postgresql/data
when: docker_database == 'postgres'
- name: Set mariadb container vars
ansible.builtin.set_fact:
db_container_image: mariadb:10
db_container_env:
MARIADB_USER: "{{ docker_service_underscore_name }}"
MARIADB_DATABASE: "{{ docker_service_underscore_name }}"
MARIADB_PASSWORD: "{{ database_passwords[docker_service_name] }}"
MARIADB_RANDOM_ROOT_PASSWORD: "{{ database_passwords[docker_service_name + '_root'] is not defined | string }}"
MARIADB_ROOT_PASSOWRD: "{{ database_passwords[docker_service_name + '_root'] | default(omit) }}"
db_container_data: /var/lib/mysql
db_image_port: 3306
when: docker_database == 'mariadb'
- name: Set mongo container vars
ansible.builtin.set_fact:
db_container_image: 'mongo:latest'
db_container_data: /data/db
when: docker_database == 'mongo'
- name: Define db container data mount
set_fact:
db_container_mounts:
- name: db
path: "{{ db_container_data }}"
mode: '0700'
- name: Define db container config mount
set_fact:
db_container_mounts: "{{ db_container_mounts + [{'template': 'mariadb.cnf', 'path': '/etc/mysql/conf.d/custom.cnf'}] }}"
when: db_config_mounts_needed
- name: Set db published ports var
set_fact:
db_published_ports: ["127.0.0.1:{{ ports[docker_service_name].db }}:{{ db_image_port }}"]
when: ports[docker_service_name].db is defined
- name: Database container for {{ docker_service_name }}
ansible.builtin.include_role:
name: service
vars:
docker_namespace: "{{ _docker_namespace }}"
docker_service: db
docker_image: "{{ db_container_image }}"
reverse_proxy_type: none
docker_mounts: "{{ db_container_mounts }}"
docker_published_ports: "{{ db_published_ports | default([]) }}"
docker_env: "{{ db_container_env | default({}) }}"
docker_additional_env: {}
docker_networks: []
docker_database: none
docker_additional_services: []
docker_host_user: false
dockerfile: []
docker_command: "{{ omit }}"
docker_entrypoint: "{{ omit }}"
docker_memory: "{{ omit }}"
- name: phpMyAdmin container for {{ docker_service_name }}
ansible.builtin.include_role:
name: service
vars:
docker_namespace: "{{ _docker_namespace }}"
docker_service: phpmyadmin
docker_image: phpmyadmin
docker_mounts: []
docker_published_ports:
- "127.0.0.1:{{ ports[docker_service_name]['phpmyadmin'] }}:80"
docker_env:
PMA_ABSOLUTE_URI: "https://{{ docker_vhost_domains[docker_service_name + '_phpmyadmin'][0] }}"
PMA_HOST: "{{ docker_service_name }}-db"
docker_additional_env: {}
docker_networks: []
docker_database: none
docker_additional_services: []
docker_host_user: false
dockerfile: []
docker_command: "{{ omit }}"
docker_entrypoint: "{{ omit }}"
docker_memory: "{{ omit }}"
docker_proxy_target_protocol: http
vhost_basicauth: "{{ docker_phpmyadmin_basicauth }}"
vhost_basicauth_users: "{{ docker_phpmyadmin_basicauth_users }}"
when: docker_database == 'mariadb' and ports[docker_service_name]['phpmyadmin'] is defined

View File

@@ -0,0 +1,14 @@
---
- name: "Create user for {{ docker_service_name }}"
user:
name: "{{ docker_service_underscore_name }}"
home: "/opt/{{ docker_namespace }}/{{ docker_service_suffix }}"
create_home: false
system: true
shell: /bin/bash
register: user
- name: Set docker container user
set_fact:
docker_user: "{{ user.uid }}:{{ user.group }}"

View File

@@ -0,0 +1,71 @@
---
- name: Image build
when: dockerfile_needed
block:
- name: Set docker_build_directory variable
set_fact:
docker_build_directory: /opt/{{ docker_namespace }}/build
- name: Create container build directory
file:
path: "{{ docker_build_directory }}"
state: directory
- name: Put dockerfile in place
template:
src: Dockerfile.j2
dest: "{{ docker_build_directory }}/Dockerfile"
mode: 0644
- name: Build docker image for {{ docker_service }}
docker_image:
name: "local_{{ docker_service }}"
source: build
force_source: true
build:
pull: true
path: "{{ docker_build_directory }}"
register: built_image
changed_when:
- not ansible_check_mode
- built_image.changed
- name: Pull container image for {{ docker_service_name }}
docker_image:
name: "{{ docker_image }}"
source: pull
force_source: true
register: pulled_image
when: not dockerfile_needed
changed_when:
- not ansible_check_mode
- pulled_image.changed
- name: Set container_image variable
set_fact:
container_image: "{{ item.image }}"
when: item.skipped is not defined or not item.skipped
loop:
- "{{ built_image }}"
- "{{ pulled_image }}"
- name: Check mode image info
when: ansible_check_mode
block:
- name: Get docker image info for check mode
docker_image_info:
name: "{{ ('local_' + docker_service) if dockerfile | length > 0 else docker_image }}"
register: existing_image
- name: Set check mode container_image variable
set_fact:
container_image: "{{ existing_image.images[0] }}"
when: existing_image.images | length > 0
- name: Set image user variable
set_fact:
image_user: "{{ container_image.Config.User }}"
when:
- not ansible_check_mode
- container_image.Config.User | int

View File

@@ -0,0 +1,63 @@
---
- name: Fail if docker_volumes defined
ansible.builtin.fail:
msg: "docker_volumes is not supported anymore. Use docker_mounts instead!"
when: docker_volumes is defined
- name: Store variables to be reset in the end
ansible.builtin.set_fact:
_docker_service_name: "{{ docker_service_name }}"
_docker_mount_definition: "{{ docker_mount_definition }}"
_container_published_ports: "{{ container_published_ports }}"
_container_image: "{{ container_image }}"
_container_networks: "{{ container_networks }}"
_template_mounts_needed: "{{ template_mounts_needed }}"
_copypath_mounts_needed: "{{ copypath_mounts_needed }}"
_volumes_needed: "{{ volumes_needed }}"
_dockerfile_needed: "{{ dockerfile_needed }}"
_db_config_mounts_needed: "{{ db_config_mounts_needed }}"
_bind_volumes_needed: "{{ bind_volumes_needed }}"
_named_volumes_needed: "{{ named_volumes_needed }}"
_create_opt_directory: "{{ create_opt_directory }}"
_create_mounts_directory: "{{ create_mounts_directory }}"
_docker_service_underscore_name: "{{ docker_service_underscore_name }}"
_image_user: "{{ image_user }}"
when: create_mounts_directory is defined
- name: Initialize variables
ansible.builtin.set_fact:
_docker_namespace: "{{ docker_namespace }}"
docker_service_name: "{{ docker_namespace }}"
docker_mount_definition: []
container_published_ports: []
container_image: ''
image_user: ''
container_networks: []
- name: Add suffix to docker_service_name
ansible.builtin.set_fact:
docker_service_name: "{{ docker_service_name }}-{{ docker_service_suffix }}"
when: docker_service_suffix | length > 0
- name: Add docker_service to docker_service_name
ansible.builtin.set_fact:
docker_service_name: "{{ docker_service_name }}-{{ docker_service }}"
when: docker_namespace != docker_service
- name: Set assistive variables
set_fact:
docker_service_underscore_name: "{{ docker_service_name | replace('-', '_') }}"
template_mounts_needed: "{{ docker_mounts | selectattr('template', 'defined') | list | length > 0 }}"
copypath_mounts_needed: "{{ docker_mounts | selectattr('copypath', 'defined') | list | length > 0 }}"
volumes_needed: "{{ docker_mounts | selectattr('name', 'defined') | list | length > 0 or docker_database != 'none' }}"
dockerfile_needed: "{{ dockerfile | length > 0 }}"
db_config_mounts_needed: "{{ docker_mariadb_config | length > 0 }}"
- name: Set more assistive variables
set_fact:
bind_volumes_needed: "{{ volumes_needed and docker_volume_type == 'bind' }}"
named_volumes_needed: "{{ volumes_needed and docker_volume_type == 'named' }}"
- name: Set even more assistive variables
set_fact:
create_opt_directory: "{{ dockerfile_needed or docker_host_user or bind_volumes_needed or template_mounts_needed or copypath_mounts_needed or db_config_mounts_needed }}"
create_mounts_directory: "{{ bind_volumes_needed or template_mounts_needed or copypath_mounts_needed or db_config_mounts_needed }}"

View File

@@ -0,0 +1,139 @@
---
- name: Container role initialization
import_tasks: init.yml
- name: Docker network
when: docker_network_mode != 'host' or docker_networks | length > 0
block:
- name: Set networks variable to {{ docker_namespace + ('-' + docker_service_suffix if docker_service_suffix | length > 0 else '') }}
ansible.builtin.set_fact:
container_networks:
- name: "{{ docker_namespace + ('-' + docker_service_suffix if docker_service_suffix | length > 0 else '') }}"
when: docker_networks | length == 0
- name: Set networks variable to {{ docker_networks }}
ansible.builtin.set_fact:
container_networks: "{{ docker_networks }}"
when: docker_networks | length > 0
- name: Create docker networks
community.docker.docker_network:
name: "{{ item.name }}"
loop: "{{ container_networks }}"
- name: Reverse proxy for container
include_tasks: proxy.yml
when: reverse_proxy_type != 'none'
- name: Create directory /opt/{{ docker_namespace }}
ansible.builtin.file:
path: "/opt/{{ docker_namespace }}"
state: directory
mode: 0755
when: create_opt_directory
- name: Container image
import_tasks: image.yml
- name: Container user
include_tasks: host_user.yml
when: docker_host_user
- name: Create suffix directory
when: create_opt_directory and docker_service_suffix | length > 0
block:
- name: Create directory /opt/{{ docker_namespace + '/' + docker_service_suffix }}
ansible.builtin.file:
path: "/opt/{{ docker_namespace }}/{{ docker_service_suffix }}"
state: directory
owner: "{{ user.uid | default(omit) }}"
group: "{{ user.group | default(omit) }}"
mode: 0755
- name: Set container_workdir variable
ansible.builtin.set_fact:
container_workdir: /opt/{{ docker_namespace }}/{{ docker_service_suffix }}
- name: Set container_workdir variable
ansible.builtin.set_fact:
container_workdir: /opt/{{ docker_namespace }}
when: docker_service_suffix | length == 0
- name: Create mounts directory
when: create_mounts_directory
block:
- name: Set docker_mounts_dir
ansible.builtin.set_fact:
docker_mounts_dir: "{{ container_workdir }}/mounts"
- name: Create directory {{ docker_mounts_dir }}
ansible.builtin.file:
path: "{{ docker_mounts_dir }}"
state: directory
owner: "{{ user.uid | default(omit) }}"
group: "{{ user.group | default(omit) }}"
mode: 0700
- name: Database container
include_tasks: database.yml
when: docker_database != 'none'
- name: Additional services
include_tasks: additional.yml
when: docker_additional_services | length > 0
- name: Container mounts
import_tasks: mounts.yml
- name: "Ensure container with legacy name doesn't exist for {{ docker_service_underscore_name }}"
community.docker.docker_container:
name: "{{ docker_service_underscore_name }}"
state: absent
when: docker_service_underscore_name != docker_service_name
- name: Ensure network with legacy name doesn't exist
community.docker.docker_network:
name: "{{ docker_service_underscore_name }}"
state: absent
when: docker_service_underscore_name != docker_service_name
- name: "Container for {{ docker_service_name }}"
community.docker.docker_container:
name: "{{ docker_service_name }}"
image: "{{ container_image.Id if (not ansible_check_mode) or (container_image | length > 0) else docker_image }}"
user: "{{ docker_user if docker_host_user else omit }}"
mounts: "{{ docker_mount_definition }}"
published_ports: "{{ container_published_ports + docker_published_ports }}"
labels: "{{ traefik_labels | default(omit) }}"
env: "{{ docker_env | combine(docker_additional_env) | combine({'TZ': timezone}) }}"
entrypoint: "{{ docker_entrypoint | default(omit) }}"
command: "{{ docker_command | default(omit) }}"
memory: "{{ docker_memory | default(omit) }}"
restart_policy: "{{ docker_restart_policy }}"
network_mode: "{{ docker_network_mode if docker_network_mode | length > 0 else omit }}"
networks: "{{ container_networks }}"
log_driver: local
state: "{{ 'started' if docker_restart_policy == 'always' else 'present' }}"
register: container_out
notify: Ensure container running
- name: Flush handlers to trigger container restart
ansible.builtin.meta: flush_handlers
- name: Reset variables to their original values
ansible.builtin.set_fact:
docker_service_name: "{{ _docker_service_name }}"
docker_mount_definition: "{{ _docker_mount_definition }}"
container_published_ports: "{{ _container_published_ports }}"
container_image: "{{ _container_image }}"
container_networks: "{{ _container_networks }}"
template_mounts_needed: "{{ _template_mounts_needed }}"
copypath_mounts_needed: "{{ _copypath_mounts_needed }}"
volumes_needed: "{{ _volumes_needed }}"
dockerfile_needed: "{{ _dockerfile_needed }}"
db_config_mounts_needed: "{{ _db_config_mounts_needed }}"
bind_volumes_needed: "{{ _bind_volumes_needed }}"
named_volumes_needed: "{{ _named_volumes_needed }}"
create_opt_directory: "{{ _create_opt_directory }}"
create_mounts_directory: "{{ _create_mounts_directory }}"
docker_service_underscore_name: "{{ _docker_service_underscore_name }}"
image_user: "{{ _image_user }}"
when: _docker_service_name is defined

View File

@@ -0,0 +1,64 @@
---
- name: Create directories and put files in them
when: create_mounts_directory
block:
- name: Define mount directory owner
set_fact:
mount_owner: "{{ user.uid if docker_host_user else image_user }}"
mount_group: "{{ user.group if docker_host_user else '' }}"
- name: "Create docker bind mount directories for {{ docker_service_name }}"
file:
path: "{{ docker_mounts_dir }}/{{ item.name }}"
state: directory
owner: "{{ mount_owner if (item.set_owner is not defined or item.set_owner) and mount_owner | length > 0 else omit }}"
group: "{{ mount_group if (item.set_group is not defined or item.set_group) and mount_group | length > 0 else omit }}"
mode: "{{ item.mode | default('0755') }}"
when: item.name is defined and docker_volume_type == 'bind'
loop: "{{ docker_mounts }}"
- name: Set docker_mount_definition for named binds
set_fact:
docker_mount_definition: "{{ docker_mount_definition + [{'source': docker_mounts_dir + '/' + item.name, 'target': item.path, 'type': 'bind', 'read_only': item.readonly | default(false)}] }}"
when: item.name is defined and docker_volume_type == 'bind'
loop: "{{ docker_mounts }}"
- name: Template docker template mounts for {{ docker_service_name }}
template:
src: "{{ item.template }}.j2"
dest: "{{ docker_mounts_dir }}/{{ item.template }}"
mode: "{{ item.mode | default('0644') }}"
when: item.template is defined
loop: "{{ docker_mounts }}"
notify: Restart container
- name: Set docker_mount_definition for template mounts
set_fact:
docker_mount_definition: "{{ docker_mount_definition + [{'source': docker_mounts_dir + '/' + item.template, 'target': item.path, 'type': 'bind', 'read_only': true}] }}"
when: item.template is defined
loop: "{{ docker_mounts }}"
- name: Copy docker copypath mounts for {{ docker_service_name }}
copy:
src: "files/{{ item.copypath }}"
dest: "{{ docker_mounts_dir }}/"
mode: "{{ item.mode | default('0755') }}"
when: item.copypath is defined
loop: "{{ docker_mounts }}"
notify: Restart container
- name: Set docker_mount_definition for copypath mounts
set_fact:
docker_mount_definition: "{{ docker_mount_definition + [{'source': docker_mounts_dir + '/' + item.copypath, 'target': item.path, 'type': 'bind', 'read_only': true}] }}"
when: item.copypath is defined
loop: "{{ docker_mounts }}"
- name: Set docker_mount_definition for named volumes
set_fact:
docker_mount_definition: "{{ docker_mount_definition + [{'source': docker_namespace + ('-' + docker_service_suffix if docker_service_suffix | length > 0 else '') + '-' + item.name, 'target': item.path, 'type': 'volume', 'read_only': item.readonly | default(false)}] }}"
when: docker_volume_type == 'named' and item.name is defined
loop: "{{ docker_mounts }}"
- name: Set docker_mount_definition for src binds
set_fact:
docker_mount_definition: "{{ docker_mount_definition + [{'source': item.src, 'target': item.path, 'type': 'bind', 'read_only': item.readonly | default(false)}] }}"
when: item.src is defined
loop: "{{ docker_mounts }}"

View File

@@ -0,0 +1,31 @@
---
- name: "Ensure reverse proxy with legacy name doesn't exist"
include_role:
name: uumas.general.vhost
vars:
vhost_id: "{{ docker_service_underscore_name }}"
vhost_state: absent
when: docker_service_underscore_name != docker_service_name
- name: Reverse proxy
include_role:
name: uumas.general.vhost
vars:
vhost_type: reverse_proxy
vhost_id: "{{ docker_service_name }}"
proxy_target_protocol: "{{ docker_proxy_target_protocol }}"
vhost_domains: "{{ docker_vhost_domains[docker_service_name] }}"
proxy_target_port: "{{ ports[docker_service_name][proxy_target_protocol] }}"
when: reverse_proxy_type != 'traefik'
- name: Set published ports variable to http port
set_fact:
container_published_ports: ["127.0.0.1:{{ ports[docker_service_name][docker_proxy_target_protocol] }}:{{ docker_image_http_port }}"]
when:
- docker_network_mode != 'host'
- reverse_proxy_type != 'traefik'
- name: Include traefik vars
include_vars: traefik.yml
when: reverse_proxy_type == 'traefik'

View File

@@ -0,0 +1,6 @@
# {{ ansible_managed }}
FROM {{ docker_image }}
{% for item in dockerfile %}
{{ item }}
{% endfor %}

View File

@@ -0,0 +1,8 @@
# {{ ansible_managed }}
{% for section in docker_mariadb_config | dict2items %}
[{{ section.key }}]
{% for item in section.value | dict2items %}
{{ item.key }} = {{ item.value }}
{% endfor %}
{% endfor %}

3
roles/service/todo Normal file
View File

@@ -0,0 +1,3 @@
Reverse proxy for phpmyadmin
Make docker_additional_services a loop of include_tasks

View File

@@ -6,8 +6,9 @@ dependencies:
docker_image: quay.io/wekan/wekan
docker_image_http_port: 8080
docker_database: mongo
docker_volumes:
- wekan_data:/data
docker_mounts:
- name: wekan_data
path: /data
docker_env:
MONGO_URL: mongodb://wekan_db:27017/wekan
ROOT_URL: "https://{{ docker_vhost_domains.wekan[0] }}"

View File

@@ -1,5 +1,7 @@
---
docker_service_name: wordpress
wordpress_tag: php8.1
docker_additional_services: []

View File

@@ -0,0 +1,25 @@
---
- name: Converge
hosts: all
tasks:
- name: "Include wordpress"
ansible.builtin.include_role:
name: "wordpress"
vars:
docker_additional_services:
- memcached
- redis
wordpress_php_extensions:
- pdo_mysql
wordpress_additional_volumes:
- www
- log
database_passwords:
wordpress: molecule
docker_vhost_domains:
wordpress:
- localhost
admin_email: molecule@example.com
ports:
wordpress:
http: 28001

View File

@@ -0,0 +1,15 @@
---
dependency:
name: galaxy
driver:
name: podman
platforms:
- name: bullseye
image: git.uumas.fi/uumas/molecule-testbed:bullseye-docker
command: /lib/systemd/systemd
pre_build_image: true
privileged: true
provisioner:
name: ansible
verifier:
name: ansible

View File

@@ -0,0 +1,44 @@
---
# This is an example playbook to execute Ansible tests.
- name: Verify
hosts: all
gather_facts: false
tasks:
- name: Ensure https://localhost responds appropriately
ansible.builtin.uri:
url: https://localhost
validate_certs: false
return_content: true
register: get_localhost
- name: "Assert caddy responded on https://localhost"
ansible.builtin.assert:
that: "(get_localhost.server | split(', '))[0] == 'Caddy'"
- name: "Assert apache responded on https://localhost"
ansible.builtin.assert:
that: "(get_localhost.server | split(', '))[1].startswith('Apache')"
- name: Assert there was a redirect
ansible.builtin.assert:
that: "get_localhost.redirected == true"
- name: Get docker host info
community.docker.docker_host_info:
volumes: true
register: docker_host_out
- name: Assert all containers are running
ansible.builtin.assert:
that: docker_host_out.host_info.Containers == docker_host_out.host_info.ContainersRunning
msg: There should have been {{ docker_host_out.host_info.Containers }} containers running but there were {{ docker_host_out.host_info.ContainersRunning }}
- name: Assert correct number of containers were created
ansible.builtin.assert:
that: docker_host_out.host_info.Containers == 4
msg: There should have been 4 containers created but there were {{ docker_host_out.host_info.Containers }}
- name: Assert correct number of images were pulled
ansible.builtin.assert:
that: docker_host_out.host_info.Images == 6
msg: There should have been 4 images present but there were {{ docker_host_out.host_info.Images }}
- name: Assert no volumes were created
ansible.builtin.assert:
that: docker_host_out.volumes | length == 0
msg: There should have been no volumes present but there were {{ docker_host_out.volumes | length }}

View File

@@ -6,16 +6,16 @@
- name: Set docker service full name (required because docker_env uses it)
set_fact:
docker_service_name: "wordpress_{{ docker_service_suffix }}"
docker_service_name: "wordpress-{{ docker_service_suffix }}"
when: docker_service_suffix is defined
- name: Add memcached php extension to container
set_fact:
dockerfile: >
"{{ dockerfile +
['RUN apt-get update && \
apt-get install -y libmemcached-dev libssl-dev zlib1g-dev && \
pecl install memcached-3.2.0 && docker-php-ext-enable memcached'] }}"
{{ dockerfile +
['RUN apt-get update &&
apt-get install -y libmemcached-dev libssl-dev zlib1g-dev &&
pecl install memcached-3.2.0 && docker-php-ext-enable memcached'] }}
when: "'memcached' in docker_additional_services"
- name: Add pdo_mysql php extension to container
@@ -28,19 +28,19 @@
loop: "{{ wordpress_additional_volumes | default([]) }}"
- name: Wordpress container setup
include_role:
name: container
ansible.builtin.include_role:
name: service
vars:
docker_service: wordpress
docker_image: wordpress:{{ wordpress_tag }}
docker_image_http_port: 80
docker_database: mariadb
docker_volumes: "{{ wordpress_docker_volumes + wordpress_www_volume + wordpress_log_volume }}"
docker_mounts: "{{ wordpress_docker_mounts + wordpress_www_volume + wordpress_log_volume }}"
docker_volume_type: bind
docker_env:
WORDPRESS_DB_HOST: "{{ docker_service_name }}_db"
WORDPRESS_DB_NAME: "{{ docker_service_name }}"
WORDPRESS_DB_USER: "{{ docker_service_name }}"
WORDPRESS_DB_HOST: "{{ docker_service_name }}-db"
WORDPRESS_DB_NAME: "{{ docker_service_name | replace('-', '_') }}"
WORDPRESS_DB_USER: "{{ docker_service_name | replace('-', '_') }}"
WORDPRESS_DB_PASSWORD: "{{ database_passwords[docker_service_name] }}"
WORDPRESS_CONFIG_EXTRA: |
define('WP_SITEURL', 'https://{{ docker_vhost_domains[docker_service_name][0] }}');

View File

@@ -1,6 +1,6 @@
---
wordpress_docker_volumes:
wordpress_docker_mounts:
- name: html
path: /var/www/html
- template: php.ini