play for installing traefik on lb servers

This commit is contained in:
2026-08-05 20:50:58 +02:00
parent ecdf64ffac
commit c89e8c565a
10 changed files with 237 additions and 2 deletions
+14
View File
@@ -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) }}"
+9
View File
@@ -0,0 +1,9 @@
---
- name: Reload systemd
ansible.builtin.systemd:
daemon_reload: true
- name: Restart traefik
ansible.builtin.systemd:
name: traefik
state: restarted
+77
View File
@@ -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
+1
View File
@@ -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 %}
- 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
+38
View File
@@ -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