In Teil 1 dieser Artikelserie habe ich mein Ansinnen ausführlich beschrieben. Dieser Teil widmet sich der Entwicklung einer Ansible-Rolle zum Deployment des Nextcloud-Apache-Container-Images.
In den folgenden Abschnitten beschreibe ich die Einrichtung eines Python Virtual Environments, die Installation von Ansible in dem zuvor erstellten Environment und die Installation der Ansible-Collection containers.podman, bevor ich mich abschließend der eigentlichen Ansible-Rolle widme.
Python Virtual Environments für Ansible
Zur Einrichtung habe ich mich an den englischsprachigen Artikel „How to set up and use Python virtual environments for Ansible“ von Gineesh Madapparambath gehalten. Die notwendigen Schritte werden hier kurz und bündig dokumentiert.
[t14s ~]$ python3 --version
Python 3.9.7
[t14s ~]$ mkdir python-venv
[t14s ~]$ cd !$
cd python-venv
[t14s python-venv]$ python3 -m venv ansible-core2.x
[t14s python-venv]$ source ansible-core2.x/bin/activate
(ansible-core2.x) [jkastning@t14s python-venv]$ python3 -m pip install --upgrade pip
Requirement already satisfied: pip in ./ansible-core2.x/lib/python3.9/site-packages (21.0.1)
Collecting pip
Downloading pip-21.3.1-py3-none-any.whl (1.7 MB)
|████████████████████████████████| 1.7 MB 2.3 MB/s
Installing collected packages: pip
Attempting uninstall: pip
Found existing installation: pip 21.0.1
Uninstalling pip-21.0.1:
Successfully uninstalled pip-21.0.1
Successfully installed pip-21.3.1
(ansible-core2.x) [t14s python-venv]$ python3 -m pip install ansible-core
Collecting ansible-core
[...]
(ansible-core2.x) [t14s python-venv]$ ansible --version
ansible [core 2.11.6]
config file = None
configured module search path = ['/home/tronde/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /home/tronde/python-venv/ansible-core2.x/lib64/python3.9/site-packages/ansible
ansible collection location = /home/tronde/.ansible/collections:/usr/share/ansible/collections
executable location = /home/tronde/python-venv/ansible-core2.x/bin/ansible
python version = 3.9.7 (default, Aug 30 2021, 00:00:00) [GCC 11.2.1 20210728 (Red Hat 11.2.1-1)]
jinja version = 3.0.2
libyaml = True
Damit ist die Installation von ansible-core
abgeschlossen. Im folgenden Code-Block wird geprüft, ob Ansible sich grundsätzlich mit dem Zielsystem verbinden und dort einen Python-Interpreter identifizieren kann.
(ansible-core2.x) [t14s python-venv]$ ansible -i hosts --private-key ~/.ssh/ansible_id_rsa -m ping example.com
example.com | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python3"
},
"changed": false,
"ping": "pong"
}
Installation der Ansible-Collection containers.podman
Um Podman auf dem Zielsystem konfigurieren zu können, wird die genannte Ansible-Collection benötigt, welche mit folgendem Befehl installiert werden kann. Der Code-Block zeigt zusätzlich die Ausgabe während der Installation.
(ansible-core2.x) [t14s ansible-core2.x]$ ansible-galaxy collection install containers.podman
Starting galaxy collection install process
Process install dependency map
Starting collection install process
Downloading https://galaxy.ansible.com/download/containers-podman-1.8.2.tar.gz to /home/tronde/.ansible/tmp/ansible-local-8729oh0om8w3/tmp7tv2yrae/containers-podman-1.8.2-9rw3fd1y
Installing 'containers.podman:1.8.2' to '/home/tronde/.ansible/collections/ansible_collections/containers/podman'
containers.podman:1.8.2 was installed successfully
Ansible-Rolle: Deployment von Nextcloud und MariaDB als Pod
Nextcloud benötigt für den Betrieb eine Datenbank. Hierfür könnte man eine integrierte SQLite nutzen. Dies wird jedoch nur für kleine Umgebungen empfohlen. Während der Entstehung dieses Artikels wird MariaDB als Datenbank-Backend vom Nextlcoud-Projekt empfohlen. Daher habe ich mich entschieden, das Nextcloud-Image zusammen mit einem MariaDB-Container zu deployen. Dazu greife ich auf die beiden folgenden Container-Repositorien zurück:
- Nextcloud (Official Image) — https://hub.docker.com/_/nextcloud
- MariaDB (Official Image) — https://hub.docker.com/_/mariadb
Das Grundgerüst bzw. die Verzeichnisstruktur für die Ansible-Rolle wurde erstellt mit:
$ ansible-galaxy role init --offline ansible_role_deploy_nextcloud_with_mariadb_pod
Die aktuelle Version der Ansible-Rolle ist auf GitHub zu finden. Ich werde ihre Bestandteile hier im Einzelnen vorstellen.
Die Variablen in defaults/main.yml
In der Datei defaults/main.yml
habe ich Standardwerte für Variablen definiert, die geeignet sind, eine funktionsfähige Nextcloud-Instanz zu initialisieren. Die Bezeichner der Variablen sind dabei der Dokumentation der verwendeten Container-Repositorien entnommen.
In Zeile 4-7 und 10 werden die Namen für Podman-Volumes definiert, welche die persistent zu speichernden Daten aufnehmen werden.
1 ---
2 # defaults file for ansible_role_deploy_nextcloud_with_mariadb_pod
3 # Podman volumes for Nextcloud
4 NC_HTML: nc_html
5 NC_APPS: nc_apps
6 NC_CONFIG: nc_config
7 NC_DATA: nc_data
8
9 # Podman volume for MariaDB
10 MYSQL_DATA: mysql_data
Die Zeilen 13-17 definieren Variablen für die MariaDB-Instanz, wie z.B. Namen der Datenbank, Benutzername und Passwörter für diese Datenbank und den DB-Host. Diese werden neben dem MariaDB-Container auch von dem Nextcloud-Container benötigt, um eine Verbindung zur Datenbank herstellen zu können.
12 # MySQL/MariaDB vars
13 MYSQL_DATABASE: nc_db
14 MYSQL_USER: nextcloud
15 MYSQL_PASSWORD: ToPSeCrEt2021!
16 MYSQL_ROOT_PASSWORD: ToPSeCrEt2021!
17 MYSQL_HOST: 127.0.0.1
18
19 # Vars for MariaDB container
20 MARIADB_CONMON_PIDFILE: /tmp/mariadb_conmon.pid
21 MARIADB_IMAGE: docker.io/library/mariadb:10.5.7
22 MARIADB_NAME: nc_mariadb
Zeile 20-22 definiert Variablen, die für den MariaDB-Container benötigt werden. Hier wird z.B. die Version des Container-Images (MARIADB_IMAGE
) und ein Name für die Container-Instanz (MARIADB_NAME
) festgelegt.
Die folgenden Zeilen widmen sich den Variablen für den Nextcloud-Container. Dort werden in den Zeilen 25 u. 26 Benutzername und Passwort für den Nextcloud-Admin definiert, gefolgt von einigen Variablen, welche bei Nutzung eines Reverse-Proxy benötigt werden und SMTP-Variablen, welche der Nextcloud den Mailversand ermöglichen.
24 # Nextcloud vars
25 NEXTCLOUD_ADMIN_USER: nc_admin
26 NEXTCLOUD_ADMIN_PASSWORD: VSnfD2021!
27 NEXTCLOUD_OVERWRITEPROTOCOL: ""
28 NEXTCLOUD_OVERWRITECLIURL: ""
29 NEXTCLOUD_TRUSTED_DOMAINS: ""
30
31 # SMTP vars
32 SMTP_HOST: smtp.example.com
33 SMTP_SECURE: tls # ssl to use SSL, or tls zu use STARTTLS
34 SMTP_PORT: 587 # (25, 465 for SSL, 587 for STARTTLS)
35 SMTP_AUTHTYPE: LOGIN
36 SMTP_NAME: bob@example.com
37 SMTP_PASSWORD: MailSecret1!
38 MAIL_FROM_ADDRESS: no-reply@example.com
39 MAIL_DOMAIN: "@example.com"
Bei den SMTP-Variablen handelt es sich um Beispiel-Werte. Diese müssen an die konkrete Umgebung angepasst werden.
Es folgen nun noch ein paar Variablen, welche dem Pod und dem Nextcloud-Container einen Namen geben, sowie die Version des zu verwendenden Nextcloud-Container-Images festlegen.
41 # Vars for podman-pod(1)
42 POD_NAME: nc_pod
43 POD_PORT: 127.0.0.1:40231:80
44 POD_INFRA_CONMON_PIDFILE: /tmp/nc_pod_infra.pid
45
46 # Vars for Nextcloud container
47 NC_CONMON_PIDFILE: /tmp/nc_conmon.pid
48 NC_IMAGE: docker.io/library/nextcloud:23-apache
49 NC_NAME: nextcloud
Durch POD_PORT: 127.0.0.1:40231:80
wird definiert, dass der Port 40231 an das Loopback-Interface gebunden und mit Port 80 des Pods verknüpft wird. Mit dieser Einstellung ist die Nextcloud-Instanz nur von dem Host aus erreichbar, auf dem sie ausgebracht wurde. Möchte man sie auch von anderen Hosts aus erreichbar machen, kann man entweder den Teil mit 127.0.0.1:
weglassen oder einen Reverse-Proxy wie z.B. NGINX verwenden. Ich empfehle an dieser Stelle letzteres.
Hinweis: In defauts/main.yml
stehen Passwörter im Klartext. Diese sind mit der Veröffentlichung der Ansible-Rolle allgemein bekannt und sollten gegen solche ersetzt werden, die geheimgehalten werden. Dies kann z.B. geschehen, in dem man die entsprechenden Variablen in vars/main.yml
oder host_vars/hostname
neu definiert. Es bietet sich an, diese zusätzlich mit Ansible-Vault zu verschlüsseln.
Die Tasks in tasks/main.yml
Im vorstehenden Abschnitt wurden die Variablen definiert, welche für die nun folgenden Tasks benötigt werden. Diese sind in tasks/main.yml
definiert und werden im folgenden wieder abschnittsweise erläutert.
1 ---
2 # tasks file for ansible_role_deploy_nextcloud_with_mariadb_pod
3 - name: Main folder, needed for updating
4 containers.podman.podman_volume:
5 state: present
6 name: "{{ NC_HTML }}"
7 recreate: no
8 debug: no
9
10 - name: Volume for installed/modified apps
11 containers.podman.podman_volume:
12 state: present
13 name: "{{ NC_APPS }}"
14 recreate: no
15 debug: no
16
17 - name: Volume for local configuration
18 containers.podman.podman_volume:
19 state: present
20 name: "{{ NC_CONFIG }}"
21 recreate: no
22 debug: no
23
24 - name: Volume for the actual data of Nextcloud
25 containers.podman.podman_volume:
26 state: present
27 name: "{{ NC_DATA }}"
28 recreate: no
29 debug: no
30
31 - name: Volume for the MySQL data files
32 containers.podman.podman_volume:
33 state: present
34 name: "{{ MYSQL_DATA }}"
35 recreate: no
36 debug: no
Die ersten Zeilen enthalten Tasks, durch welche die Podman-Volumes zur persistenten Datenspeicherung auf dem Zielsystem erstellt werden. Diese Tasks sind, wie für Ansible üblich, deklarativ und idempotent. Existiert ein Volume bereits, liefert der entsprechende Task ein ‚OK‘ zurück, da keine Aktionen erforderlich sind.
Die folgenden Zeilen erstellen den Podman-Pod und fügen ihm einen Nextcloud- sowie einen MariaDB-Container hinzu. Die Dokumentation der verwendeten Module findet sich in Punkt 5 und 6 im Abschnitt Quellen und weiterführende Links.
38 - name: Create the podman-pod(1)
39 containers.podman.podman_pod:
40 debug: no
41 infra: yes
42 infra_conmon_pidfile: "{{ POD_INFRA_CONMON_PIDFILE }}"
43 publish: "{{ POD_PORT }}"
44 name: "{{ POD_NAME }}"
45 state: started
46
47 - name: Create MariaDB container
48 containers.podman.podman_container:
49 debug: yes
50 conmon_pidfile: "{{ MARIADB_CONMON_PIDFILE }}"
51 image: "{{ MARIADB_IMAGE }}"
52 image_strict: yes
53 pod: "{{ POD_NAME }}"
54 recreate: yes
55 state: started
56 name: "{{ MARIADB_NAME }}"
57 env:
58 MYSQL_USER: "{{ MYSQL_USER }}"
59 MYSQL_PASSWORD: "{{ MYSQL_PASSWORD }}"
60 MYSQL_ROOT_PASSWORD: "{{ MYSQL_ROOT_PASSWORD }}"
61 MYSQL_DATABASE: "{{ MYSQL_DATABASE }}"
62 volume: "{{ MYSQL_DATA }}:/var/lib/mysql:Z"
63
64 - name: Wait for DB to initilize
65 wait_for:
66 timeout: 20
67
68 - name: Create Nextcloud container
69 containers.podman.podman_container:
70 debug: no
71 conmon_pidfile: "{{ NC_CONMON_PIDFILE }}"
72 image: "{{ NC_IMAGE }}"
73 image_strict: yes
74 pod: "{{ POD_NAME }}"
75 recreate: yes
76 state: started
77 name: "{{ NC_NAME }}"
78 env:
79 MYSQL_DATABASE: "{{ MYSQL_DATABASE }}"
80 MYSQL_USER: "{{ MYSQL_USER }}"
81 MYSQL_PASSWORD: "{{ MYSQL_PASSWORD }}"
82 MYSQL_HOST: "{{ MYSQL_HOST }}"
83 NEXTCLOUD_ADMIN_USER: "{{ NEXTCLOUD_ADMIN_USER }}"
84 NEXTCLOUD_ADMIN_PASSWORD: "{{ NEXTCLOUD_ADMIN_PASSWORD }}"
85 NEXTCLOUD_TRUSTED_DOMAINS: "{{ NEXTCLOUD_TRUSTED_DOMAINS }}"
86 SMTP_HOST: "{{ SMTP_HOST }}"
87 SMTP_SECURE: "{{ SMTP_SECURE }}"
88 SMTP_PORT: "{{ SMTP_PORT }}"
89 SMTP_AUTHTYPE: "{{ SMTP_AUTHTYPE }}"
90 SMTP_NAME: "{{ SMTP_NAME }}"
91 SMTP_PASSWORD: "{{ SMTP_PASSWORD }}"
92 MAIL_FROM_ADDRESS: "{{ MAIL_FROM_ADDRESS }}"
93 MAIL_DOMAIN: "{{ MAIL_DOMAIN }}"
94 OVERWRITEPROTOCOL: "{{ NEXTCLOUD_OVERWRITEPROTOCOL }}"
95 OVERWRITECLIURL: "{{ NEXTCLOUD_OVERWRITECLIURL }}"
96 volume:
97 - "{{ NC_HTML }}:/var/www/html:Z"
98 - "{{ NC_APPS }}:/var/www/html/custom_apps:Z"
99 - "{{ NC_CONFIG }}:/var/www/html/config:Z"
100 - "{{ NC_DATA }}:/var/www/html/data:Z"
In Zeile 64-66 habe ich einen Task definiert, der einfach nur 20 Sekunden wartet. Dies wurde erforderlich, da ich Laufzeitprobleme feststellen konnte, wenn der Nextcloud-Container startet, bevor die Datenbank im MariaDB-Container initialisiert war. Dieses Konstrukt ist nicht schön und ich bin für Verbesserungsvorschläge offen.
Zwischenfazit
Die Erstellung der Ansible-Rolle hat länger gedauert, als angenommen. Dies liegt nur zum Teil in meiner spärlichen Freizeit begründet. Einen größeren Einfluss darauf hatte die Dokumentation zum Nextcloud-Repository. Diese geht davon aus, dass man ein Dockerfile bzw. Docker-Compose verwendet. So war noch etwas Internet-Recherche erforderlich, um den Pod letztendlich ans Laufen zu bringen.
Dieser Artikel beschäftigte sich mit den Tag-1-Aufgaben, an deren Ende eine Nextcloud-Instanz ausgebracht wurde, welche an einen Reverse-Proxy angebunden werden kann.
Im nächsten Artikel gehe ich auf die Konfiguration des NGINX-Reverse-Proxy ein. Hierbei habe ich einige Überraschungen erlebt, welche mich an der Reife des Projekts [2] zweifeln lassen.
Quellen und weiterführende Links
- Nextcloud System Requirements — https://docs.nextcloud.com/server/latest/admin_manual/installation/system_requirements.html
- Nextcloud (Official Image) — https://hub.docker.com/_/nextcloud
- MariaDB (Official Image) — https://hub.docker.com/_/mariadb
- GitHub Tronde/ansible_role_deploy_nextcloud_with_mariadb_pod
- podman_pod – Manage Podman pods
- podman_container – Manage podman containers