mirror of
https://github.com/KevinMidboe/playbooks-retailor.git
synced 2026-09-24 05:39:05 +00:00
Compare commits
19 Commits
bd8065b657
...
871b8d1f83
| Author | SHA1 | Date | |
|---|---|---|---|
| 871b8d1f83 | |||
| c349880bf5 | |||
| c18487ac23 | |||
| d9d1f6ca0b | |||
| 8aae1a9c1f | |||
| 2d168877c8 | |||
| 0b3bb1e3f4 | |||
| 5d01d6555b | |||
| b9b02edf3e | |||
| a861de3392 | |||
| 4c4ab3184f | |||
| 094d02ff7f | |||
| c89e8c565a | |||
| ecdf64ffac | |||
| 524e0d44f7 | |||
| 6ce2d9d3bd | |||
| 1fc2506cf4 | |||
| 7013159c29 | |||
| d5450fd311 |
@@ -1 +1,2 @@
|
|||||||
.env
|
.env
|
||||||
|
.DS_Store
|
||||||
|
|||||||
+15
-4
@@ -1,10 +1,7 @@
|
|||||||
---
|
---
|
||||||
ansible_python_interpreter: /usr/bin/python3
|
ansible_python_interpreter: /usr/bin/python3
|
||||||
install_cadvisor_exporter: false
|
|
||||||
install_nginx_exporter: false
|
|
||||||
install_redis_exporter: false
|
|
||||||
users:
|
users:
|
||||||
- username: forge
|
- username: kevin
|
||||||
oh_my_zsh:
|
oh_my_zsh:
|
||||||
theme: robbyrussell
|
theme: robbyrussell
|
||||||
- username: root
|
- username: root
|
||||||
@@ -13,3 +10,17 @@ users:
|
|||||||
custom_firewall_ports:
|
custom_firewall_ports:
|
||||||
- 9100 # node exporter
|
- 9100 # node exporter
|
||||||
- 9191 # fail2ban exporter
|
- 9191 # fail2ban exporter
|
||||||
|
# exporter versions
|
||||||
|
node_exporter_version: "1.9.0"
|
||||||
|
fail2ban_exporter_version: "0.10.2"
|
||||||
|
cadvisor_version: "0.46.0"
|
||||||
|
#redis_exporter_version: ??
|
||||||
|
mysqld_exporter_version: "0.17.2"
|
||||||
|
nginx_exporter_version: "1.4.1"
|
||||||
|
# disable exporters by default
|
||||||
|
install_node_exporter: true
|
||||||
|
install_fail2ban_exporter: true
|
||||||
|
install_cadvisor_exporter: false
|
||||||
|
install_redis_exporter: false
|
||||||
|
install_mysqld_exporter: false
|
||||||
|
install_nginx_exporter: false
|
||||||
|
|||||||
+21
-2
@@ -1,6 +1,25 @@
|
|||||||
---
|
---
|
||||||
|
## REQUIRED
|
||||||
|
# DOMAIN ""
|
||||||
|
# ANALYTICS_IP ""
|
||||||
|
# WEB_IPS '["", ""]'
|
||||||
|
|
||||||
|
# apt_packages:
|
||||||
|
# - nginx
|
||||||
custom_firewall_ports:
|
custom_firewall_ports:
|
||||||
- 80
|
- 80
|
||||||
- 443
|
- 443
|
||||||
install_nginx_exporter: true
|
# - 9100 # traefik exporter - internal only
|
||||||
description: "load balancer serving web servers for retailor.io"
|
# - 8080 # traefik dashboard - internal only
|
||||||
|
# install_nginx_exporter: true
|
||||||
|
description: "load balancer serving traefik webserver for retailor.io"
|
||||||
|
|
||||||
|
traefik_image: "traefik:v3.1"
|
||||||
|
traefik_acme_email: webmaster@retailor.io
|
||||||
|
traefik_config_dir: "/etc/traefik"
|
||||||
|
traefik_dynamic_dir: "/etc/traefik/dynamic"
|
||||||
|
|
||||||
|
traefik_web_port: 80
|
||||||
|
traefik_websecure_port: 443
|
||||||
|
traefik_dashboard_port: 8080
|
||||||
|
traefik_dashboard_insecure: true
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
---
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Global variables for the docker + umami deployment
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
custom_firewall_ports:
|
||||||
|
- 3000
|
||||||
|
description: Umami web analytics
|
||||||
|
|
||||||
|
# --- Docker role ---
|
||||||
|
docker_users_to_add:
|
||||||
|
- "{{ ansible_user }}" # add the SSH user to the docker group
|
||||||
|
|
||||||
|
# --- Umami role ---
|
||||||
|
umami_base_dir: /opt/umami
|
||||||
|
umami_image: "ghcr.io/umami-software/umami:latest"
|
||||||
|
umami_container_name: umami
|
||||||
|
umami_port: 3000 # host port Umami will be exposed on
|
||||||
|
|
||||||
|
# Address the umami_port is bound to on the host.
|
||||||
|
# - "127.0.0.1" if your reverse proxy runs on this same host (loopback only)
|
||||||
|
# - "0.0.0.0" if your reverse proxy is elsewhere (another host/container) and
|
||||||
|
# needs to reach this port over the network
|
||||||
|
umami_bind_address: "0.0.0.0"
|
||||||
|
|
||||||
|
# Postgres settings (used by the umami-db container)
|
||||||
|
umami_db_container_name: umami-db
|
||||||
|
umami_db_image: "postgres:15-alpine"
|
||||||
|
umami_db_name: umami
|
||||||
|
umami_db_user: umami
|
||||||
|
# IMPORTANT: override this in inventory/vars, --extra-vars, or (better) Ansible Vault.
|
||||||
|
# Do not leave the default password in production.
|
||||||
|
umami_db_password: "fc7tBTxsuAulmYmSl8AE0q13eq3f94f7u3u6"
|
||||||
|
|
||||||
|
# A random string used by Umami to encrypt/salt data. Generate your own, e.g.:
|
||||||
|
# openssl rand -hex 32
|
||||||
|
# Override this in production via --extra-vars or vault.
|
||||||
|
umami_app_secret: "dichotomizing-triads-cosmochemistry-amidols"
|
||||||
+1
-2
@@ -1,8 +1,7 @@
|
|||||||
---
|
---
|
||||||
custom_firewall_ports:
|
custom_firewall_ports:
|
||||||
- 80
|
- 80
|
||||||
- 443
|
|
||||||
apt_packages:
|
apt_packages:
|
||||||
- nfs-common
|
- nfs-common
|
||||||
install_nginx_exporter: true
|
install_cadvisor_exporter: true
|
||||||
description: 'webserver node for hosting retailor laravel project'
|
description: 'webserver node for hosting retailor laravel project'
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
---
|
---
|
||||||
apt_packages:
|
apt_packages:
|
||||||
- nfs-common
|
- nfs-common
|
||||||
|
install_cadvisor_exporter: true
|
||||||
description: 'queue server for laravel'
|
description: 'queue server for laravel'
|
||||||
|
|||||||
@@ -5,5 +5,6 @@
|
|||||||
- role: roles/base-packages # - Basic server setup and configuration
|
- role: roles/base-packages # - Basic server setup and configuration
|
||||||
- role: roles/fail2ban # - SSH Brute force protection
|
- role: roles/fail2ban # - SSH Brute force protection
|
||||||
- role: roles/firewall # - Firewall, if firewall_enable is true
|
- role: roles/firewall # - Firewall, if firewall_enable is true
|
||||||
|
- role: roles/users
|
||||||
- role: roles/oh-my-zsh
|
- role: roles/oh-my-zsh
|
||||||
- role: roles/motd
|
- role: roles/motd
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
- name: Docker on host
|
||||||
|
hosts: all
|
||||||
|
roles:
|
||||||
|
- role: roles/docker
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
---
|
---
|
||||||
- name: Install ELK stack using Docker
|
- name: Install ELK stack using Docker
|
||||||
hosts: elasticsearch
|
hosts: all
|
||||||
vars:
|
vars:
|
||||||
elk_version: "8.17.0"
|
elk_version: "9.4.4"
|
||||||
roles:
|
roles:
|
||||||
# TODO check for docker install before purging it. This nukes containers
|
# TODO check for docker install before purging it. This nukes containers
|
||||||
# we might not want destroyed.
|
# we might not want destroyed.
|
||||||
# Leave a file, or read the same input requirements into a apt check
|
# Leave a file, or read the same input requirements into a apt check
|
||||||
# which skips purge if satisfied.
|
# which skips purge if satisfied.
|
||||||
# - role: roles/docker
|
- role: roles/docker
|
||||||
- role: roles/elasticsearch
|
- role: roles/elasticsearch
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
---
|
---
|
||||||
- name: Install prometheus & grafana using Docker
|
- name: Install prometheus & grafana using Docker
|
||||||
hosts: grafana
|
hosts: all
|
||||||
vars:
|
vars:
|
||||||
prometheus_version: "latest"
|
prometheus_version: "latest"
|
||||||
grafana_version: "latest"
|
grafana_version: "latest"
|
||||||
cadvisor_version: "0.46.0"
|
cadvisor_version: "0.46.0"
|
||||||
roles:
|
roles:
|
||||||
# - role: roles/docker
|
- role: roles/docker
|
||||||
- role: roles/prometheus-grafana
|
- role: roles/prometheus-grafana
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Install tailscale
|
|
||||||
hosts: all
|
|
||||||
roles:
|
|
||||||
- role: roles/tailscale
|
|
||||||
vars:
|
|
||||||
tailscale_authkey: "{{ lookup('env', 'TAILSCALE_KEY') }}"
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
- name: Install traefik using Docker
|
||||||
|
hosts: all
|
||||||
|
roles:
|
||||||
|
- role: roles/firewall
|
||||||
|
- role: roles/docker
|
||||||
|
- role: roles/env
|
||||||
|
- role: roles/traefik
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
- name: Install umami using Docker
|
||||||
|
hosts: all
|
||||||
|
vars:
|
||||||
|
umami_version: "9.4.4"
|
||||||
|
roles:
|
||||||
|
- role: roles/firewall
|
||||||
|
- role: roles/docker
|
||||||
|
- role: roles/env
|
||||||
|
- role: roles/umami
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
# docker
|
||||||
|
|
||||||
|
Installs Docker CE from the official Docker apt repository on Ubuntu/Debian.
|
||||||
|
|
||||||
|
## What changed from the original role
|
||||||
|
|
||||||
|
The original role had several bugs that would have made it fail or behave
|
||||||
|
unpredictably:
|
||||||
|
|
||||||
|
- **`tasks/main.yml` was dead code that duplicated `install.yml` incorrectly.**
|
||||||
|
Nothing ever included it (only `main-distro-check.yml` was wired up), and
|
||||||
|
it referenced undefined variables (`install_packages`,
|
||||||
|
`clean_install_remove_packages` had no `become`, etc). It's been replaced
|
||||||
|
by a single real entrypoint.
|
||||||
|
- **`install.yml` added the apt repo but never fetched the GPG key**, so
|
||||||
|
`apt-get update` would fail signature verification. Key download and repo
|
||||||
|
registration are now in one idempotent flow using `get_url` +
|
||||||
|
`apt_repository` instead of raw `shell` commands.
|
||||||
|
- **`check_distro.yml` used `meta: end_play`**, which ends the *entire
|
||||||
|
Ansible run* for *all* hosts in the play, not just this role/host. This is
|
||||||
|
almost never what you want in a multi-host play. It's replaced with a
|
||||||
|
`docker_distro_supported` fact that gates subsequent tasks, with an
|
||||||
|
optional hard `fail` (`docker_fail_on_unsupported_distro`, default `true`).
|
||||||
|
- **`service.yml` had the exact same task listed twice** (one with a `when`
|
||||||
|
guard, one without). Deduplicated into a single task.
|
||||||
|
- **Package state `latest`** was used for prerequisites and Docker itself,
|
||||||
|
which causes unnecessary upgrades (and potential breakage) on every run.
|
||||||
|
Changed to `present`; use `docker_install_packages` + your own
|
||||||
|
update strategy if you want upgrades.
|
||||||
|
- **No handler was ever notified.** Installing/upgrading Docker packages now
|
||||||
|
notifies the `Restart Docker` handler.
|
||||||
|
- **`docker` group creation used the `user` module** to create a "user" named
|
||||||
|
docker, which is not the same as a group and wasn't added to any actual
|
||||||
|
user. Replaced with a proper `group` task plus a `docker_users` list you
|
||||||
|
populate to add real users to the group.
|
||||||
|
- All tasks now use fully-qualified module names (`ansible.builtin.*`) and
|
||||||
|
`name:`/`state:` keyword syntax instead of the deprecated
|
||||||
|
`key=value` shorthand.
|
||||||
|
|
||||||
|
## Role variables
|
||||||
|
|
||||||
|
See `defaults/main.yml` for the full list. Commonly overridden:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
docker_users:
|
||||||
|
- deploy
|
||||||
|
- ci
|
||||||
|
|
||||||
|
docker_install_packages:
|
||||||
|
- docker-ce
|
||||||
|
- docker-ce-cli
|
||||||
|
- containerd.io
|
||||||
|
- docker-buildx-plugin
|
||||||
|
- docker-compose-plugin
|
||||||
|
|
||||||
|
docker_fail_on_unsupported_distro: true
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example playbook
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- hosts: docker_hosts
|
||||||
|
become: true
|
||||||
|
roles:
|
||||||
|
- role: docker
|
||||||
|
vars:
|
||||||
|
docker_users:
|
||||||
|
- "{{ ansible_user }}"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tags
|
||||||
|
|
||||||
|
- `docker_check_distro`
|
||||||
|
- `docker_install`
|
||||||
|
- `docker_users`
|
||||||
|
- `docker_service`
|
||||||
@@ -1,4 +1,12 @@
|
|||||||
clean_install_remove_packages:
|
---
|
||||||
|
# Distros this role knows how to configure the Docker apt repo for.
|
||||||
|
docker_supported_distros:
|
||||||
|
- ubuntu
|
||||||
|
- debian
|
||||||
|
|
||||||
|
# Packages to purge before installing Docker CE, to avoid conflicts
|
||||||
|
# with distro-provided / older Docker packages.
|
||||||
|
docker_clean_install_remove_packages:
|
||||||
- docker.io
|
- docker.io
|
||||||
- docker-doc
|
- docker-doc
|
||||||
- docker-compose
|
- docker-compose
|
||||||
@@ -6,10 +14,31 @@ clean_install_remove_packages:
|
|||||||
- containerd
|
- containerd
|
||||||
- runc
|
- runc
|
||||||
|
|
||||||
install_packages:
|
# Packages installed from the official Docker apt repository.
|
||||||
|
docker_install_packages:
|
||||||
- docker-ce
|
- docker-ce
|
||||||
- docker-ce-cli
|
- docker-ce-cli
|
||||||
- containerd.io
|
- containerd.io
|
||||||
- docker-buildx-plugin
|
- docker-buildx-plugin
|
||||||
- docker-compose
|
|
||||||
- docker-compose-plugin
|
- docker-compose-plugin
|
||||||
|
|
||||||
|
# Base URL for the Docker apt repository. {{ '%s' }} is filled in with the
|
||||||
|
# lowercased distro name (ubuntu/debian).
|
||||||
|
docker_apt_repo_url: "https://download.docker.com/linux"
|
||||||
|
|
||||||
|
# Where the Docker GPG signing key is stored.
|
||||||
|
docker_apt_keyring_dir: /etc/apt/keyrings
|
||||||
|
docker_apt_keyring_file: "{{ docker_apt_keyring_dir }}/docker.asc"
|
||||||
|
|
||||||
|
# apt cache freshness, in seconds, before Ansible refreshes it.
|
||||||
|
docker_apt_cache_valid_time: 3600
|
||||||
|
|
||||||
|
# Users to add to the "docker" group (passwordless docker CLI access).
|
||||||
|
docker_users: []
|
||||||
|
|
||||||
|
# Whether to start and enable the docker service.
|
||||||
|
docker_service_enabled: true
|
||||||
|
docker_service_state: started
|
||||||
|
|
||||||
|
# Fail the play if the distro isn't supported, instead of silently skipping.
|
||||||
|
docker_fail_on_unsupported_distro: true
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
- name: Restart Docker
|
- name: Restart Docker
|
||||||
systemd:
|
ansible.builtin.systemd:
|
||||||
name: docker
|
name: docker
|
||||||
state: restarted
|
state: restarted
|
||||||
|
|||||||
@@ -1,13 +1,22 @@
|
|||||||
---
|
---
|
||||||
galaxy_info:
|
galaxy_info:
|
||||||
|
role_name: docker
|
||||||
author: Your Name
|
author: Your Name
|
||||||
description: Ansible role to install and manage Docker on Debian
|
description: Installs and manages Docker CE from the official Docker apt repository
|
||||||
license: MIT
|
license: MIT
|
||||||
min_ansible_version: "2.9"
|
min_ansible_version: "2.14"
|
||||||
platforms:
|
platforms:
|
||||||
|
- name: Ubuntu
|
||||||
|
versions:
|
||||||
|
- focal
|
||||||
|
- jammy
|
||||||
|
- noble
|
||||||
- name: Debian
|
- name: Debian
|
||||||
versions:
|
versions:
|
||||||
- all
|
- bullseye
|
||||||
|
- bookworm
|
||||||
galaxy_tags:
|
galaxy_tags:
|
||||||
- docker
|
- docker
|
||||||
|
- containers
|
||||||
|
|
||||||
dependencies: []
|
dependencies: []
|
||||||
|
|||||||
@@ -1,27 +1,22 @@
|
|||||||
---
|
---
|
||||||
- name: Check if the current distro is supported (Ubuntu or Debian)
|
- name: Determine if the current distro is supported
|
||||||
set_fact:
|
ansible.builtin.set_fact:
|
||||||
distro_supported: "{{ ansible_facts['distribution'].lower() in supported_distros }}"
|
docker_distro_supported: "{{ ansible_facts['distribution'] | lower in docker_supported_distros }}"
|
||||||
tags:
|
|
||||||
- check_distro
|
|
||||||
|
|
||||||
- name: Set installation URL based on the distro
|
- name: Fail on unsupported distro
|
||||||
set_fact:
|
ansible.builtin.fail:
|
||||||
install_url: "https://download.docker.com/linux/{{ ansible_facts['distribution'].lower() }} {{ ansible_distribution_release }} stable"
|
msg: >-
|
||||||
when: distro_supported
|
{{ ansible_facts['distribution'] }} is not a supported distribution for
|
||||||
tags:
|
the docker role. Supported: {{ docker_supported_distros | join(', ') }}.
|
||||||
- set_url
|
when:
|
||||||
|
- not docker_distro_supported
|
||||||
- name: Log Unsupported Distro
|
- docker_fail_on_unsupported_distro
|
||||||
debug:
|
|
||||||
msg: "The {{ ansible_facts['distribution'] }} distribution is not supported. Skipping Docker installation."
|
|
||||||
when: not distro_supported
|
|
||||||
tags:
|
|
||||||
- unsupported_distro
|
|
||||||
|
|
||||||
- name: Skip Docker installation task if distro is unsupported
|
|
||||||
meta: end_play
|
|
||||||
when: not distro_supported
|
|
||||||
tags:
|
|
||||||
- end_play
|
|
||||||
|
|
||||||
|
- name: Notify unsupported distro is being skipped
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: >-
|
||||||
|
{{ ansible_facts['distribution'] }} is not supported by the docker role.
|
||||||
|
Skipping Docker installation on this host.
|
||||||
|
when:
|
||||||
|
- not docker_distro_supported
|
||||||
|
- not docker_fail_on_unsupported_distro
|
||||||
|
|||||||
@@ -1,12 +1,51 @@
|
|||||||
---
|
---
|
||||||
- name: Add Docker repository
|
- name: Remove conflicting/legacy Docker packages
|
||||||
apt_repository:
|
ansible.builtin.package:
|
||||||
repo: deb {{ install_url }}
|
name: "{{ docker_clean_install_remove_packages }}"
|
||||||
state: present
|
state: absent
|
||||||
when: distro_supported
|
|
||||||
|
|
||||||
- name: Install Docker
|
- name: Install prerequisite packages
|
||||||
apt:
|
ansible.builtin.package:
|
||||||
name: "{{ docker_package }}"
|
name:
|
||||||
|
- ca-certificates
|
||||||
|
- curl
|
||||||
|
- gnupg
|
||||||
state: present
|
state: present
|
||||||
when: distro_supported
|
update_cache: true
|
||||||
|
|
||||||
|
- name: Ensure apt keyrings directory exists
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ docker_apt_keyring_dir }}"
|
||||||
|
state: directory
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
- name: Download Docker's official GPG key
|
||||||
|
ansible.builtin.get_url:
|
||||||
|
url: "{{ docker_apt_repo_url }}/{{ ansible_facts['distribution'] | lower }}/gpg"
|
||||||
|
dest: "{{ docker_apt_keyring_file }}"
|
||||||
|
mode: "0644"
|
||||||
|
force: true
|
||||||
|
|
||||||
|
- name: Add Docker apt repository
|
||||||
|
ansible.builtin.apt_repository:
|
||||||
|
repo: >-
|
||||||
|
deb [arch={{ 'amd64' if ansible_facts['architecture'] == 'x86_64' else ansible_facts['architecture'] }}
|
||||||
|
signed-by={{ docker_apt_keyring_file }}]
|
||||||
|
{{ docker_apt_repo_url }}/{{ ansible_facts['distribution'] | lower }}
|
||||||
|
{{ ansible_facts['distribution_release'] }} stable
|
||||||
|
state: present
|
||||||
|
filename: docker
|
||||||
|
update_cache: false
|
||||||
|
|
||||||
|
- name: Update apt cache
|
||||||
|
ansible.builtin.apt:
|
||||||
|
update_cache: true
|
||||||
|
cache_valid_time: "{{ docker_apt_cache_valid_time }}"
|
||||||
|
|
||||||
|
- name: Install Docker packages
|
||||||
|
ansible.builtin.apt:
|
||||||
|
pkg: "{{ item }}"
|
||||||
|
state: present
|
||||||
|
update_cache: yes
|
||||||
|
with_items: "{{ docker_install_packages | default([]) }}"
|
||||||
|
notify: Restart Docker
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
---
|
|
||||||
- name: Include distro check tasks
|
|
||||||
include_tasks: check_distro.yml
|
|
||||||
tags:
|
|
||||||
- check_distro
|
|
||||||
|
|
||||||
- name: Include Docker installation tasks if distro is supported
|
|
||||||
include_tasks: install.yml
|
|
||||||
when: distro_supported
|
|
||||||
tags:
|
|
||||||
- install
|
|
||||||
+19
-41
@@ -1,45 +1,23 @@
|
|||||||
---
|
---
|
||||||
- name: Clean install by removing any docker package
|
- name: Assert supported distribution
|
||||||
package: name={{ item }} state=absent
|
ansible.builtin.include_tasks: check_distro.yml
|
||||||
with_items: "{{ clean_install_remove_packages }}"
|
tags:
|
||||||
|
- docker_check_distro
|
||||||
|
|
||||||
- name: Ensure curl & ca-certs are installed
|
- name: Install Docker
|
||||||
package:
|
ansible.builtin.include_tasks: install.yml
|
||||||
name:
|
when: docker_distro_supported
|
||||||
- ca-certificates
|
tags:
|
||||||
- curl
|
- docker_install
|
||||||
- gnupg
|
|
||||||
state: latest
|
|
||||||
|
|
||||||
- name: Ensure docker keyring file exists
|
- name: Configure Docker group membership
|
||||||
file:
|
ansible.builtin.include_tasks: users.yml
|
||||||
path: /etc/apt/keyrings/docker.gpg
|
when: docker_distro_supported and docker_users | length > 0
|
||||||
state: touch
|
tags:
|
||||||
|
- docker_users
|
||||||
- name: Download docker gpg key and add to keyrings
|
|
||||||
shell: |
|
|
||||||
install -m 0755 -d /etc/apt/keyrings
|
|
||||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor --yes -o /etc/apt/keyrings/docker.gpg
|
|
||||||
chmod a+r /etc/apt/keyrings/docker.gpg
|
|
||||||
|
|
||||||
- name: Sign and add docker deb source
|
- name: Manage Docker service
|
||||||
shell: |
|
ansible.builtin.include_tasks: service.yml
|
||||||
echo \
|
when: docker_distro_supported
|
||||||
"deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
|
tags:
|
||||||
"$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
|
- docker_service
|
||||||
tee /etc/apt/sources.list.d/docker.list > /dev/null
|
|
||||||
|
|
||||||
- name: Update apt sources
|
|
||||||
become: true
|
|
||||||
apt:
|
|
||||||
update_cache: yes
|
|
||||||
cache_valid_time: 1
|
|
||||||
|
|
||||||
- name: Install docker packages
|
|
||||||
package: name={{ item }} state=latest
|
|
||||||
with_items: "{{ install_packages }}"
|
|
||||||
|
|
||||||
- name: Ensure group docker exists
|
|
||||||
user:
|
|
||||||
name: docker
|
|
||||||
state: present
|
|
||||||
|
|||||||
@@ -1,14 +1,6 @@
|
|||||||
---
|
---
|
||||||
- name: Start and enable Docker service
|
- name: Start and enable Docker service
|
||||||
systemd:
|
ansible.builtin.systemd:
|
||||||
name: docker
|
name: docker
|
||||||
enabled: yes
|
enabled: "{{ docker_service_enabled }}"
|
||||||
state: started
|
state: "{{ docker_service_state }}"
|
||||||
|
|
||||||
- name: Start and enable Docker service
|
|
||||||
systemd:
|
|
||||||
name: docker
|
|
||||||
enabled: yes
|
|
||||||
state: started
|
|
||||||
when: distro_supported
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
- name: Ensure docker group exists
|
||||||
|
ansible.builtin.group:
|
||||||
|
name: docker
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Add users to the docker group
|
||||||
|
ansible.builtin.user:
|
||||||
|
name: "{{ item }}"
|
||||||
|
groups: docker
|
||||||
|
append: true
|
||||||
|
loop: "{{ docker_users }}"
|
||||||
@@ -1,9 +1,4 @@
|
|||||||
---
|
---
|
||||||
- name: Create a Docker network for Elasticsearch
|
|
||||||
docker_network:
|
|
||||||
name: elk_network
|
|
||||||
state: present
|
|
||||||
|
|
||||||
- name: Pull Elasticsearch Docker image
|
- name: Pull Elasticsearch Docker image
|
||||||
docker_image:
|
docker_image:
|
||||||
name: docker.elastic.co/elasticsearch/elasticsearch-wolfi:{{ elk_version }}
|
name: docker.elastic.co/elasticsearch/elasticsearch-wolfi:{{ elk_version }}
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
- name: Create a Docker network for Elasticsearch
|
||||||
|
docker_network:
|
||||||
|
name: elk_network
|
||||||
|
state: present
|
||||||
|
driver: bridge
|
||||||
|
ipam_config:
|
||||||
|
- subnet: "172.19.0.0/16"
|
||||||
|
gateway: "172.19.0.1"
|
||||||
|
iprange: "172.19.0.0/24"
|
||||||
|
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
# ensure we have variables from .env files
|
# ensure we have variables from .env files
|
||||||
- include_tasks: ../roles/env/tasks/main.yml
|
- include_tasks: ../roles/env/tasks/main.yml
|
||||||
|
|
||||||
|
- include_tasks: elk-network.yml
|
||||||
- include_tasks: elasticsearch.yml
|
- include_tasks: elasticsearch.yml
|
||||||
- include_tasks: kibana.yml
|
- include_tasks: kibana.yml
|
||||||
- include_tasks: logstash.yml
|
- include_tasks: logstash.yml
|
||||||
|
|||||||
Vendored
+7
-3
@@ -1,12 +1,16 @@
|
|||||||
---
|
---
|
||||||
- name: Read .env file
|
- name: Read .env file
|
||||||
ansible.builtin.slurp:
|
ansible.builtin.slurp:
|
||||||
src: "../.env"
|
src: "{{ playbook_dir }}/../.env"
|
||||||
delegate_to: localhost
|
delegate_to: localhost
|
||||||
register: env_file
|
register: env_file
|
||||||
|
|
||||||
- name: Parse .env file
|
- name: Build list of key/value pairs
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
env_vars: "{{ dict(env_file['content'] | b64decode | split('\n') | select('search', '=') | map('split', '=', 1) | list) }}"
|
env_lines: "{{ (env_file['content'] | b64decode).splitlines() | select('match', '^[^#=]+=.*$') | list }}"
|
||||||
|
|
||||||
|
- name: Parse .env file into a dict
|
||||||
|
ansible.builtin.set_fact:
|
||||||
|
env_vars: "{{ env_vars | default({}) | combine({ (item.split('=', 1)[0] | trim): (item.split('=', 1)[1] | trim) }) }}"
|
||||||
|
loop: "{{ env_lines }}"
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
owner: root
|
owner: root
|
||||||
group: root
|
group: root
|
||||||
mode: +x
|
mode: +x
|
||||||
when: description
|
when: description is defined and description | length > 0
|
||||||
|
|
||||||
- name: check if help-text motd exists
|
- name: check if help-text motd exists
|
||||||
stat:
|
stat:
|
||||||
|
|||||||
@@ -1,9 +1,4 @@
|
|||||||
---
|
---
|
||||||
- name: Create a Docker network for Grafana
|
|
||||||
docker_network:
|
|
||||||
name: monitoring_network
|
|
||||||
state: present
|
|
||||||
|
|
||||||
- name: Create grafana datasources directory on host
|
- name: Create grafana datasources directory on host
|
||||||
file:
|
file:
|
||||||
path: /etc/grafana/datasources
|
path: /etc/grafana/datasources
|
||||||
@@ -87,7 +82,7 @@
|
|||||||
- /etc/grafana/datasources:/etc/grafana/provisioning/datasources
|
- /etc/grafana/datasources:/etc/grafana/provisioning/datasources
|
||||||
- /etc/grafana/dashboards:/etc/grafana/provisioning/dashboards
|
- /etc/grafana/dashboards:/etc/grafana/provisioning/dashboards
|
||||||
networks:
|
networks:
|
||||||
- name: monitoring_network
|
- name: monitoring
|
||||||
env:
|
env:
|
||||||
GF_SECURITY_ADMIN_PASSWORD: "{{ env_vars.GRAFANA_PASSWORD }}" # Customize the password
|
GF_SECURITY_ADMIN_PASSWORD: "{{ env_vars.GRAFANA_PASSWORD }}" # Customize the password
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,4 @@
|
|||||||
---
|
---
|
||||||
- name: Create a Docker network for loki
|
|
||||||
docker_network:
|
|
||||||
name: monitoring_network
|
|
||||||
state: present
|
|
||||||
|
|
||||||
- name: Pull loki Docker image
|
- name: Pull loki Docker image
|
||||||
docker_image:
|
docker_image:
|
||||||
name: grafana/loki:latest
|
name: grafana/loki:latest
|
||||||
@@ -33,4 +28,4 @@
|
|||||||
volumes:
|
volumes:
|
||||||
- /etc/loki/config.yml:/etc/loki/config.yml
|
- /etc/loki/config.yml:/etc/loki/config.yml
|
||||||
networks:
|
networks:
|
||||||
- name: monitoring_network
|
- name: monitoring
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
# ensure we have variables from .env files
|
# ensure we have variables from .env files
|
||||||
- include_tasks: ../roles/env/tasks/main.yml
|
- include_tasks: ../roles/env/tasks/main.yml
|
||||||
|
|
||||||
|
- include_tasks: monitoring-network.yml
|
||||||
- include_tasks: prometheus.yml
|
- include_tasks: prometheus.yml
|
||||||
- include_tasks: grafana.yml
|
- include_tasks: grafana.yml
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
- name: Create a Docker network for Monitoring
|
||||||
|
docker_network:
|
||||||
|
name: monitoring
|
||||||
|
state: present
|
||||||
@@ -1,9 +1,4 @@
|
|||||||
---
|
---
|
||||||
- name: Create a Docker network for Prometheus
|
|
||||||
docker_network:
|
|
||||||
name: monitoring_network
|
|
||||||
state: present
|
|
||||||
|
|
||||||
- name: Pull Prometheus Docker image
|
- name: Pull Prometheus Docker image
|
||||||
docker_image:
|
docker_image:
|
||||||
name: prom/prometheus
|
name: prom/prometheus
|
||||||
@@ -32,5 +27,5 @@
|
|||||||
volumes:
|
volumes:
|
||||||
- /etc/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
|
- /etc/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
|
||||||
networks:
|
networks:
|
||||||
- name: monitoring_network
|
- name: monitoring
|
||||||
|
|
||||||
|
|||||||
Submodule roles/tailscale deleted from d225db4915
@@ -0,0 +1,14 @@
|
|||||||
|
traefik_config_dir: "/etc/traefik"
|
||||||
|
traefik_dynamic_dir: "/etc/traefik/dynamic"
|
||||||
|
traefik_web_port: 80
|
||||||
|
traefik_websecure_port: 443
|
||||||
|
traefik_dashboard_port: 8080
|
||||||
|
traefik_metrics_port: 8100
|
||||||
|
traefik_dashboard_insecure: true # ufw should only allow from tailscale or vnet
|
||||||
|
|
||||||
|
domain: "{{ lookup('env', 'DOMAIN') | default('', true) }}"
|
||||||
|
# Single source of truth for static routing. Expected to be overridden
|
||||||
|
# from group_vars, -e, or an env-var lookup at the play level — see
|
||||||
|
# playbook.yml for the TRAEFIK_ROUTES example.
|
||||||
|
traefik_web_private_ips: "{{ lookup('env', 'WEB_IPS') | default('[]', true) | from_json }}"
|
||||||
|
traefik_analytics_private_ip: "{{ lookup('env', 'ANALYTICS_IP') | default('', true) }}"
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
- name: Reload systemd
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
daemon_reload: true
|
||||||
|
|
||||||
|
- name: Restart traefik
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: traefik
|
||||||
|
state: restarted
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
---
|
||||||
|
- name: Fail fast if no routes were supplied
|
||||||
|
ansible.builtin.fail:
|
||||||
|
msg: >-
|
||||||
|
traefik_web_private_ips is empty. Set the WEB_IPS env var
|
||||||
|
(JSON list) before running, or override the variable directly, e.g.
|
||||||
|
-e traefik_web_private_ips='["10.0.1.11","10.0.1.12"]'
|
||||||
|
when: env_vars.WEB_IPS | length == 0
|
||||||
|
|
||||||
|
- name: Ensure Traefik config directories exist
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ item }}"
|
||||||
|
state: directory
|
||||||
|
mode: "0755"
|
||||||
|
loop:
|
||||||
|
- "{{ traefik_config_dir }}"
|
||||||
|
- "{{ traefik_dynamic_dir }}"
|
||||||
|
|
||||||
|
- name: Create acme.json with correct permissions
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ traefik_config_dir }}/acme.json"
|
||||||
|
state: touch
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0600"
|
||||||
|
modification_time: preserve
|
||||||
|
access_time: preserve
|
||||||
|
|
||||||
|
- name: Deploy Traefik static config
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: traefik.yml.j2
|
||||||
|
dest: "{{ traefik_config_dir }}/traefik.yml"
|
||||||
|
mode: "0644"
|
||||||
|
notify: Restart traefik
|
||||||
|
|
||||||
|
- name: Deploy dynamic downstream routes file
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: dynamic-hosts.yml.j2
|
||||||
|
dest: "{{ traefik_dynamic_dir }}/hosts.yml"
|
||||||
|
mode: "0644"
|
||||||
|
notify: Restart traefik
|
||||||
|
|
||||||
|
- name: Deploy dynamic analytics file
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: dynamic-analytics.yml.j2
|
||||||
|
dest: "{{ traefik_dynamic_dir }}/analytics.yml"
|
||||||
|
mode: "0644"
|
||||||
|
notify: Restart traefik
|
||||||
|
|
||||||
|
- name: Pull traefik image
|
||||||
|
community.docker.docker_image:
|
||||||
|
name: "{{ traefik_image }}"
|
||||||
|
source: pull
|
||||||
|
notify: Restart traefik
|
||||||
|
|
||||||
|
- name: Deploy traefik systemd unit
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: traefik.service.j2
|
||||||
|
dest: /etc/systemd/system/traefik.service
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
notify: Reload systemd
|
||||||
|
|
||||||
|
- name: Enable and start traefik service
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: traefik
|
||||||
|
enabled: true
|
||||||
|
state: started
|
||||||
|
daemon_reload: true
|
||||||
|
|
||||||
|
- name: Remove dangling/old traefik images
|
||||||
|
community.docker.docker_prune:
|
||||||
|
images: true
|
||||||
|
images_filters:
|
||||||
|
dangling: true
|
||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
- include_tasks: install.yml
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
## Managed by Ansible — do not edit by hand.
|
||||||
|
## Source: traefik_analytics_private_ip (group_vars/lb.yml)
|
||||||
|
http:
|
||||||
|
routers:
|
||||||
|
analytics:
|
||||||
|
rule: "Host(`analytics.{{ env_vars.DOMAIN }}`)"
|
||||||
|
entryPoints:
|
||||||
|
- websecure
|
||||||
|
service: analytics-svc
|
||||||
|
tls:
|
||||||
|
certResolver: letsencrypt
|
||||||
|
|
||||||
|
services:
|
||||||
|
analytics-svc:
|
||||||
|
loadBalancer:
|
||||||
|
servers:
|
||||||
|
- url: "http://{{ env_vars.ANALYTICS_IP }}:3000"
|
||||||
|
healthCheck:
|
||||||
|
path: /api/heartbeat
|
||||||
|
interval: "10s"
|
||||||
|
timeout: "3s"
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
## Managed by Ansible — do not edit by hand.
|
||||||
|
## Source: traefik_web_private_ips (group_vars/lb.yml)
|
||||||
|
http:
|
||||||
|
routers:
|
||||||
|
retailor-web:
|
||||||
|
rule: "Host(`app.{{ env_vars.DOMAIN }}`)"
|
||||||
|
service: retailor-web-svc
|
||||||
|
entryPoints:
|
||||||
|
- websecure
|
||||||
|
tls:
|
||||||
|
certResolver: letsencrypt
|
||||||
|
|
||||||
|
services:
|
||||||
|
retailor-web-svc:
|
||||||
|
loadBalancer:
|
||||||
|
servers:
|
||||||
|
{% for web_ip in env_vars.WEB_IPS | from_json %}
|
||||||
|
- url: "http://{{ web_ip }}:80"
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Traefik reverse proxy
|
||||||
|
After=docker.service network-online.target
|
||||||
|
Requires=docker.service
|
||||||
|
Wants=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
TimeoutStartSec=0
|
||||||
|
Restart=always
|
||||||
|
RestartSec=5
|
||||||
|
|
||||||
|
# Clean up any stale container from a previous run/crash
|
||||||
|
ExecStartPre=-/usr/bin/docker rm -f traefik
|
||||||
|
|
||||||
|
ExecStart=/usr/bin/docker run --rm \
|
||||||
|
--name traefik \
|
||||||
|
--network host \
|
||||||
|
--security-opt no-new-privileges:true \
|
||||||
|
-v /var/run/docker.sock:/var/run/docker.sock:ro \
|
||||||
|
-v {{ traefik_config_dir }}/traefik.yml:/etc/traefik/traefik.yml:ro \
|
||||||
|
-v {{ traefik_dynamic_dir }}:/etc/traefik/dynamic:ro \
|
||||||
|
-v {{ traefik_config_dir }}/acme.json:/etc/traefik/acme.json \
|
||||||
|
{{ traefik_image }}
|
||||||
|
|
||||||
|
ExecStop=/usr/bin/docker stop traefik
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
entryPoints:
|
||||||
|
web:
|
||||||
|
address: ":{{ traefik_web_port }}"
|
||||||
|
websecure:
|
||||||
|
address: ":{{ traefik_websecure_port }}"
|
||||||
|
traefik:
|
||||||
|
address: ":{{ traefik_dashboard_port }}"
|
||||||
|
metrics:
|
||||||
|
address: ":{{ traefik_metrics_port }}"
|
||||||
|
|
||||||
|
providers:
|
||||||
|
file:
|
||||||
|
directory: "{{ traefik_dynamic_dir }}"
|
||||||
|
watch: true
|
||||||
|
|
||||||
|
api:
|
||||||
|
dashboard: true
|
||||||
|
insecure: {{ traefik_dashboard_insecure | lower }}
|
||||||
|
|
||||||
|
log:
|
||||||
|
level: INFO
|
||||||
|
|
||||||
|
accessLog: {}
|
||||||
|
|
||||||
|
certificatesResolvers:
|
||||||
|
letsencrypt:
|
||||||
|
acme:
|
||||||
|
email: "{{ traefik_acme_email }}"
|
||||||
|
storage: /etc/traefik/acme.json
|
||||||
|
httpChallenge:
|
||||||
|
entryPoint: web
|
||||||
|
|
||||||
|
metrics:
|
||||||
|
prometheus:
|
||||||
|
entryPoint: metrics
|
||||||
|
addEntryPointsLabels: true
|
||||||
|
addServicesLabels: true
|
||||||
|
addRoutersLabels: true
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
umami_python_docker_sdk_packages:
|
||||||
|
- python3-pip
|
||||||
|
- python3-docker
|
||||||
|
|
||||||
|
umami_restart_policy: unless-stopped
|
||||||
|
umami_docker_network: umami_net
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
docker_apt_arch: "{{ 'amd64' if ansible_architecture == 'x86_64' else 'arm64' }}"
|
||||||
|
docker_packages:
|
||||||
|
- docker-ce
|
||||||
|
- docker-ce-cli
|
||||||
|
- containerd.io
|
||||||
|
- docker-buildx-plugin
|
||||||
|
- docker-compose-plugin
|
||||||
|
|
||||||
|
docker_service_state: started
|
||||||
|
docker_service_enabled: true
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
- name: restart docker
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: docker
|
||||||
|
state: restarted
|
||||||
|
daemon_reload: true
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
---
|
||||||
|
- name: Ensure required system packages are present
|
||||||
|
ansible.builtin.apt:
|
||||||
|
name:
|
||||||
|
- ca-certificates
|
||||||
|
- curl
|
||||||
|
- gnupg
|
||||||
|
- lsb-release
|
||||||
|
state: present
|
||||||
|
update_cache: true
|
||||||
|
cache_valid_time: 3600
|
||||||
|
|
||||||
|
- name: Ensure /etc/apt/keyrings directory exists
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: /etc/apt/keyrings
|
||||||
|
state: directory
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
- name: Check if Docker GPG key already present
|
||||||
|
ansible.builtin.stat:
|
||||||
|
path: /etc/apt/keyrings/docker.asc
|
||||||
|
register: docker_gpg_key
|
||||||
|
|
||||||
|
- name: Download Docker's official GPG key
|
||||||
|
ansible.builtin.get_url:
|
||||||
|
url: https://download.docker.com/linux/ubuntu/gpg
|
||||||
|
dest: /etc/apt/keyrings/docker.asc
|
||||||
|
mode: "0644"
|
||||||
|
when: not docker_gpg_key.stat.exists
|
||||||
|
|
||||||
|
- name: Add Docker apt repository (deb822 format)
|
||||||
|
ansible.builtin.deb822_repository:
|
||||||
|
name: docker
|
||||||
|
types: [deb]
|
||||||
|
uris: "https://download.docker.com/linux/ubuntu"
|
||||||
|
suites: "{{ ansible_distribution_release }}"
|
||||||
|
components: [stable]
|
||||||
|
architectures: "{{ docker_apt_arch }}"
|
||||||
|
signed_by: /etc/apt/keyrings/docker.asc
|
||||||
|
state: present
|
||||||
|
register: docker_repo_added
|
||||||
|
|
||||||
|
- name: Update apt cache after adding Docker repo
|
||||||
|
ansible.builtin.apt:
|
||||||
|
update_cache: true
|
||||||
|
when: docker_repo_added.changed
|
||||||
|
|
||||||
|
- name: Install Docker Engine and plugins
|
||||||
|
ansible.builtin.apt:
|
||||||
|
name: "{{ docker_packages }}"
|
||||||
|
state: present
|
||||||
|
notify: restart docker
|
||||||
|
|
||||||
|
- name: Ensure docker service is started and enabled
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: docker
|
||||||
|
state: "{{ docker_service_state }}"
|
||||||
|
enabled: "{{ docker_service_enabled }}"
|
||||||
|
|
||||||
|
- name: Ensure docker group exists
|
||||||
|
ansible.builtin.group:
|
||||||
|
name: docker
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Add user(s) to the docker group
|
||||||
|
ansible.builtin.user:
|
||||||
|
name: "{{ item }}"
|
||||||
|
groups: docker
|
||||||
|
append: true
|
||||||
|
loop: "{{ docker_users_to_add }}"
|
||||||
|
register: docker_group_added
|
||||||
|
|
||||||
|
- name: Notify about needing to re-login for group changes
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: >-
|
||||||
|
User '{{ item.item }}' was added to the docker group. They will need to
|
||||||
|
log out and back in (or start a new SSH session) for this to take effect
|
||||||
|
without using sudo for docker commands.
|
||||||
|
loop: "{{ docker_group_added.results }}"
|
||||||
|
when: item.changed
|
||||||
|
|
||||||
|
- name: Verify Docker is installed and working
|
||||||
|
ansible.builtin.command: docker --version
|
||||||
|
register: docker_version_output
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Show installed Docker version
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: "{{ docker_version_output.stdout }}"
|
||||||
|
|
||||||
|
- name: Verify Docker Compose plugin is installed
|
||||||
|
ansible.builtin.command: docker compose version
|
||||||
|
register: docker_compose_version_output
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Show installed Docker Compose version
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: "{{ docker_compose_version_output.stdout }}"
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
- name: recreate umami stack
|
||||||
|
community.docker.docker_compose_v2:
|
||||||
|
project_src: "{{ umami_base_dir }}"
|
||||||
|
state: present
|
||||||
|
recreate: always
|
||||||
|
listen: recreate umami stack
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
---
|
||||||
|
- name: Ensure pip and Docker SDK for Python are installed (needed by community.docker modules)
|
||||||
|
ansible.builtin.apt:
|
||||||
|
name: "{{ umami_python_docker_sdk_packages }}"
|
||||||
|
state: present
|
||||||
|
update_cache: true
|
||||||
|
cache_valid_time: 3600
|
||||||
|
|
||||||
|
- name: Ensure Umami base directory exists
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ umami_base_dir }}"
|
||||||
|
state: directory
|
||||||
|
mode: "0750"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
|
||||||
|
- name: Deploy docker-compose.yml for Umami stack
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: docker-compose.yml.j2
|
||||||
|
dest: "{{ umami_base_dir }}/docker-compose.yml"
|
||||||
|
mode: "0640"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
notify: recreate umami stack
|
||||||
|
|
||||||
|
- name: Flush handlers so compose file changes apply before we verify status
|
||||||
|
ansible.builtin.meta: flush_handlers
|
||||||
|
|
||||||
|
- name: Start (or ensure running) the Umami + Postgres stack
|
||||||
|
community.docker.docker_compose_v2:
|
||||||
|
project_src: "{{ umami_base_dir }}"
|
||||||
|
state: present
|
||||||
|
register: umami_compose_result
|
||||||
|
|
||||||
|
- name: Show compose deployment result
|
||||||
|
ansible.builtin.debug:
|
||||||
|
var: umami_compose_result.changed
|
||||||
|
|
||||||
|
- name: Wait for Umami HTTP endpoint to become available
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "http://127.0.0.1:{{ umami_port }}/api/heartbeat"
|
||||||
|
status_code: 200
|
||||||
|
register: umami_health
|
||||||
|
until: umami_health.status == 200
|
||||||
|
retries: 5
|
||||||
|
delay: 10
|
||||||
|
|
||||||
|
- name: Umami health check result
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: "Umami is up and responding on port {{ umami_port }}."
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
{{ ansible_managed | comment }}
|
||||||
|
# Based on: https://github.com/umami-software/umami/blob/master/docker-compose.yml
|
||||||
|
services:
|
||||||
|
umami:
|
||||||
|
image: "{{ umami_image }}"
|
||||||
|
container_name: "{{ umami_container_name }}"
|
||||||
|
ports:
|
||||||
|
- "{{ umami_bind_address }}:{{ umami_port }}:3000"
|
||||||
|
environment:
|
||||||
|
DATABASE_URL: "postgresql://{{ umami_db_user }}:{{ umami_db_password }}@db:5432/{{ umami_db_name }}"
|
||||||
|
APP_SECRET: "{{ umami_app_secret }}"
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
condition: service_healthy
|
||||||
|
init: true
|
||||||
|
restart: always
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "curl http://localhost:3000/api/heartbeat"]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
|
||||||
|
db:
|
||||||
|
image: "{{ umami_db_image }}"
|
||||||
|
container_name: "{{ umami_db_container_name }}"
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: "{{ umami_db_name }}"
|
||||||
|
POSTGRES_USER: "{{ umami_db_user }}"
|
||||||
|
POSTGRES_PASSWORD: "{{ umami_db_password }}"
|
||||||
|
volumes:
|
||||||
|
- umami-db-data:/var/lib/postgresql/data
|
||||||
|
restart: always
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
umami-db-data:
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
---
|
||||||
|
- name: Ensure required packages for user management are present
|
||||||
|
ansible.builtin.apt:
|
||||||
|
name:
|
||||||
|
- sudo
|
||||||
|
- acl
|
||||||
|
state: present
|
||||||
|
update_cache: true
|
||||||
|
become: true
|
||||||
|
|
||||||
|
- name: Create primary group for each user
|
||||||
|
ansible.builtin.group:
|
||||||
|
name: "{{ item.username }}"
|
||||||
|
state: present
|
||||||
|
loop: "{{ users }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.username }}"
|
||||||
|
become: true
|
||||||
|
|
||||||
|
- name: Create user accounts
|
||||||
|
ansible.builtin.user:
|
||||||
|
name: "{{ item.username }}"
|
||||||
|
group: "{{ item.username }}"
|
||||||
|
groups: "{{ item.groups | default([]) }}"
|
||||||
|
append: true
|
||||||
|
shell: "{{ item.shell | default('/bin/bash') }}"
|
||||||
|
create_home: true
|
||||||
|
password: "{{ item.password | default(omit) }}"
|
||||||
|
update_password: on_create
|
||||||
|
state: present
|
||||||
|
loop: "{{ users }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.username }}"
|
||||||
|
become: true
|
||||||
|
|
||||||
|
- name: Ensure .ssh directory exists for each user
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "/home/{{ item.username }}/.ssh"
|
||||||
|
state: directory
|
||||||
|
owner: "{{ item.username }}"
|
||||||
|
group: "{{ item.username }}"
|
||||||
|
mode: "0700"
|
||||||
|
loop: "{{ users }}"
|
||||||
|
loop_control:
|
||||||
|
label: "{{ item.username }}"
|
||||||
|
become: true
|
||||||
|
when: item.ssh_key is defined
|
||||||
Reference in New Issue
Block a user