mirror of
https://github.com/KevinMidboe/playbooks-retailor.git
synced 2026-09-07 13:32:06 +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
|
||||
.DS_Store
|
||||
|
||||
+15
-4
@@ -1,10 +1,7 @@
|
||||
---
|
||||
ansible_python_interpreter: /usr/bin/python3
|
||||
install_cadvisor_exporter: false
|
||||
install_nginx_exporter: false
|
||||
install_redis_exporter: false
|
||||
users:
|
||||
- username: forge
|
||||
- username: kevin
|
||||
oh_my_zsh:
|
||||
theme: robbyrussell
|
||||
- username: root
|
||||
@@ -13,3 +10,17 @@ users:
|
||||
custom_firewall_ports:
|
||||
- 9100 # node 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:
|
||||
- 80
|
||||
- 443
|
||||
install_nginx_exporter: true
|
||||
description: "load balancer serving web servers for retailor.io"
|
||||
# - 9100 # traefik exporter - internal only
|
||||
# - 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:
|
||||
- 80
|
||||
- 443
|
||||
apt_packages:
|
||||
- nfs-common
|
||||
install_nginx_exporter: true
|
||||
install_cadvisor_exporter: true
|
||||
description: 'webserver node for hosting retailor laravel project'
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
---
|
||||
apt_packages:
|
||||
- nfs-common
|
||||
install_cadvisor_exporter: true
|
||||
description: 'queue server for laravel'
|
||||
|
||||
@@ -5,5 +5,6 @@
|
||||
- role: roles/base-packages # - Basic server setup and configuration
|
||||
- role: roles/fail2ban # - SSH Brute force protection
|
||||
- role: roles/firewall # - Firewall, if firewall_enable is true
|
||||
- role: roles/users
|
||||
- role: roles/oh-my-zsh
|
||||
- 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
|
||||
hosts: elasticsearch
|
||||
hosts: all
|
||||
vars:
|
||||
elk_version: "8.17.0"
|
||||
elk_version: "9.4.4"
|
||||
roles:
|
||||
# TODO check for docker install before purging it. This nukes containers
|
||||
# we might not want destroyed.
|
||||
# Leave a file, or read the same input requirements into a apt check
|
||||
# which skips purge if satisfied.
|
||||
# - role: roles/docker
|
||||
- role: roles/docker
|
||||
- role: roles/elasticsearch
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
- name: Install prometheus & grafana using Docker
|
||||
hosts: grafana
|
||||
hosts: all
|
||||
vars:
|
||||
prometheus_version: "latest"
|
||||
grafana_version: "latest"
|
||||
cadvisor_version: "0.46.0"
|
||||
roles:
|
||||
# - role: roles/docker
|
||||
- role: roles/docker
|
||||
- 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-doc
|
||||
- docker-compose
|
||||
@@ -6,10 +14,31 @@ clean_install_remove_packages:
|
||||
- containerd
|
||||
- runc
|
||||
|
||||
install_packages:
|
||||
# Packages installed from the official Docker apt repository.
|
||||
docker_install_packages:
|
||||
- docker-ce
|
||||
- docker-ce-cli
|
||||
- containerd.io
|
||||
- docker-buildx-plugin
|
||||
- docker-compose
|
||||
- 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
|
||||
systemd:
|
||||
ansible.builtin.systemd:
|
||||
name: docker
|
||||
state: restarted
|
||||
|
||||
@@ -1,13 +1,22 @@
|
||||
---
|
||||
galaxy_info:
|
||||
role_name: docker
|
||||
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
|
||||
min_ansible_version: "2.9"
|
||||
min_ansible_version: "2.14"
|
||||
platforms:
|
||||
- name: Ubuntu
|
||||
versions:
|
||||
- focal
|
||||
- jammy
|
||||
- noble
|
||||
- name: Debian
|
||||
versions:
|
||||
- all
|
||||
- bullseye
|
||||
- bookworm
|
||||
galaxy_tags:
|
||||
- docker
|
||||
- containers
|
||||
|
||||
dependencies: []
|
||||
|
||||
@@ -1,27 +1,22 @@
|
||||
---
|
||||
- name: Check if the current distro is supported (Ubuntu or Debian)
|
||||
set_fact:
|
||||
distro_supported: "{{ ansible_facts['distribution'].lower() in supported_distros }}"
|
||||
tags:
|
||||
- check_distro
|
||||
- name: Determine if the current distro is supported
|
||||
ansible.builtin.set_fact:
|
||||
docker_distro_supported: "{{ ansible_facts['distribution'] | lower in docker_supported_distros }}"
|
||||
|
||||
- name: Set installation URL based on the distro
|
||||
set_fact:
|
||||
install_url: "https://download.docker.com/linux/{{ ansible_facts['distribution'].lower() }} {{ ansible_distribution_release }} stable"
|
||||
when: distro_supported
|
||||
tags:
|
||||
- set_url
|
||||
|
||||
- name: Log 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: Fail on unsupported distro
|
||||
ansible.builtin.fail:
|
||||
msg: >-
|
||||
{{ ansible_facts['distribution'] }} is not a supported distribution for
|
||||
the docker role. Supported: {{ docker_supported_distros | join(', ') }}.
|
||||
when:
|
||||
- not docker_distro_supported
|
||||
- docker_fail_on_unsupported_distro
|
||||
|
||||
- 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
|
||||
apt_repository:
|
||||
repo: deb {{ install_url }}
|
||||
state: present
|
||||
when: distro_supported
|
||||
- name: Remove conflicting/legacy Docker packages
|
||||
ansible.builtin.package:
|
||||
name: "{{ docker_clean_install_remove_packages }}"
|
||||
state: absent
|
||||
|
||||
- name: Install Docker
|
||||
apt:
|
||||
name: "{{ docker_package }}"
|
||||
- name: Install prerequisite packages
|
||||
ansible.builtin.package:
|
||||
name:
|
||||
- ca-certificates
|
||||
- curl
|
||||
- gnupg
|
||||
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
|
||||
package: name={{ item }} state=absent
|
||||
with_items: "{{ clean_install_remove_packages }}"
|
||||
- name: Assert supported distribution
|
||||
ansible.builtin.include_tasks: check_distro.yml
|
||||
tags:
|
||||
- docker_check_distro
|
||||
|
||||
- name: Ensure curl & ca-certs are installed
|
||||
package:
|
||||
name:
|
||||
- ca-certificates
|
||||
- curl
|
||||
- gnupg
|
||||
state: latest
|
||||
- name: Install Docker
|
||||
ansible.builtin.include_tasks: install.yml
|
||||
when: docker_distro_supported
|
||||
tags:
|
||||
- docker_install
|
||||
|
||||
- name: Ensure docker keyring file exists
|
||||
file:
|
||||
path: /etc/apt/keyrings/docker.gpg
|
||||
state: touch
|
||||
|
||||
- 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: Configure Docker group membership
|
||||
ansible.builtin.include_tasks: users.yml
|
||||
when: docker_distro_supported and docker_users | length > 0
|
||||
tags:
|
||||
- docker_users
|
||||
|
||||
- name: Sign and add docker deb source
|
||||
shell: |
|
||||
echo \
|
||||
"deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
|
||||
"$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
|
||||
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
|
||||
- name: Manage Docker service
|
||||
ansible.builtin.include_tasks: service.yml
|
||||
when: docker_distro_supported
|
||||
tags:
|
||||
- docker_service
|
||||
|
||||
@@ -1,14 +1,6 @@
|
||||
---
|
||||
- name: Start and enable Docker service
|
||||
systemd:
|
||||
ansible.builtin.systemd:
|
||||
name: docker
|
||||
enabled: yes
|
||||
state: started
|
||||
|
||||
- name: Start and enable Docker service
|
||||
systemd:
|
||||
name: docker
|
||||
enabled: yes
|
||||
state: started
|
||||
when: distro_supported
|
||||
|
||||
enabled: "{{ docker_service_enabled }}"
|
||||
state: "{{ docker_service_state }}"
|
||||
|
||||
@@ -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
|
||||
docker_image:
|
||||
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
|
||||
- include_tasks: ../roles/env/tasks/main.yml
|
||||
|
||||
- include_tasks: elk-network.yml
|
||||
- include_tasks: elasticsearch.yml
|
||||
- include_tasks: kibana.yml
|
||||
- include_tasks: logstash.yml
|
||||
|
||||
Vendored
+7
-3
@@ -1,12 +1,16 @@
|
||||
---
|
||||
- name: Read .env file
|
||||
ansible.builtin.slurp:
|
||||
src: "../.env"
|
||||
src: "{{ playbook_dir }}/../.env"
|
||||
delegate_to: localhost
|
||||
register: env_file
|
||||
|
||||
- name: Parse .env file
|
||||
- name: Build list of key/value pairs
|
||||
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
|
||||
group: root
|
||||
mode: +x
|
||||
when: description
|
||||
when: description is defined and description | length > 0
|
||||
|
||||
- name: check if help-text motd exists
|
||||
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
|
||||
file:
|
||||
path: /etc/grafana/datasources
|
||||
@@ -87,7 +82,7 @@
|
||||
- /etc/grafana/datasources:/etc/grafana/provisioning/datasources
|
||||
- /etc/grafana/dashboards:/etc/grafana/provisioning/dashboards
|
||||
networks:
|
||||
- name: monitoring_network
|
||||
- name: monitoring
|
||||
env:
|
||||
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
|
||||
docker_image:
|
||||
name: grafana/loki:latest
|
||||
@@ -33,4 +28,4 @@
|
||||
volumes:
|
||||
- /etc/loki/config.yml:/etc/loki/config.yml
|
||||
networks:
|
||||
- name: monitoring_network
|
||||
- name: monitoring
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# ensure we have variables from .env files
|
||||
- include_tasks: ../roles/env/tasks/main.yml
|
||||
|
||||
- include_tasks: monitoring-network.yml
|
||||
- include_tasks: prometheus.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
|
||||
docker_image:
|
||||
name: prom/prometheus
|
||||
@@ -32,5 +27,5 @@
|
||||
volumes:
|
||||
- /etc/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
|
||||
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