homelab/ansible/ansible-old/playbooks/docker/deploy_traefik_kop.yml

161 lines
6.6 KiB
YAML

---
# playbooks/docker/deploy_traefik_kop.yml
#
# Purpose:
# Deploy the traefik-kop Swarm service, which bridges Swarm service labels
# to Traefik routing via Redis. Once deployed, any Swarm service labelled
# with traefik.enable=true will have its routes published automatically.
#
# Architecture:
# Swarm services → traefik-kop → Redis (10.0.0.151:6379) → Traefik (heimdall)
# traefik-kop reads Docker service state on the Swarm manager and writes
# routing rules to Redis. Traefik's redis provider picks them up in real time.
#
# Pre-requisites:
# - Swarm must be active and swarm-manager-1 (10.0.0.211) must be reachable
# - Redis on Heimdall (10.0.0.151:6379) must be running
# - community.docker collection installed: ansible-galaxy collection install community.docker
#
# Usage:
# ansible-playbook -i inventory/hosts.ini playbooks/docker/deploy_traefik_kop.yml
#
# Dry-run (no changes to Swarm):
# ansible-playbook -i inventory/hosts.ini playbooks/docker/deploy_traefik_kop.yml --check
#
# Tear down:
# ansible-playbook -i inventory/hosts.ini playbooks/docker/deploy_traefik_kop.yml \
# -e "stack_state=absent"
#
# Labelling Swarm services for auto-discovery:
# After this deploys, Swarm services only need these labels (under deploy.labels):
#
# deploy:
# labels:
# - "traefik.enable=true"
# - "traefik.http.routers.<name>.rule=Host(`<domain>.castaldifamily.com`)"
# - "traefik.http.routers.<name>.entrypoints=websecure"
# - "traefik.http.routers.<name>.tls.certresolver=cloudflare"
# - "traefik.http.services.<name>.loadbalancer.server.port=<port>"
#
# NOTE: Use deploy.labels (not top-level labels) for Swarm services.
# Top-level labels apply to the container image; deploy.labels apply
# to the Swarm service — which is what traefik-kop reads.
- name: Deploy traefik-kop Swarm stack
hosts: swarm_managers
become: false
gather_facts: false
vars:
traefik_kop_stack_state: "{{ stack_state | default('present') }}"
vars_files:
- ../../group_vars/all.yml
tasks:
# --------------------------------------------------
# STEP 1: Assert Swarm is active and reachable
# --------------------------------------------------
- name: Verify target is an active Swarm manager
ansible.builtin.command: >
docker info --format '{{ "{{" }}.Swarm.LocalNodeState{{ "}}" }}|{{ "{{" }}.Swarm.ControlAvailable{{ "}}" }}'
register: _swarm_info
changed_when: false
when: inventory_hostname == groups['swarm_managers'][0]
- name: Assert Swarm manager pre-conditions
ansible.builtin.assert:
that:
- _swarm_info.stdout is search('active')
- _swarm_info.stdout is search('true')
fail_msg: >-
{{ inventory_hostname }} must be an active Swarm manager.
Current state: {{ _swarm_info.stdout | default('unknown') }}
when: inventory_hostname == groups['swarm_managers'][0]
# --------------------------------------------------
# STEP 2: Ensure proxy-net overlay network exists
# WHY: The traefik-kop stack declares proxy-net as an external overlay.
# Future Swarm services join this network to be discoverable by kop.
# This network is separate from the bridge of the same name on Heimdall.
# WHY attachable: allows standalone containers to join for debugging.
# --------------------------------------------------
- name: Ensure proxy-net overlay network exists on Swarm
community.docker.docker_network:
name: "{{ edge_routing.swarm.proxy_network }}"
driver: overlay
attachable: true
state: present
when: inventory_hostname == groups['swarm_managers'][0]
tags: [network]
# --------------------------------------------------
# STEP 3: Verify Redis is reachable from manager
# WHY: Fail fast before deploying — if kop can't reach Redis, the
# container will start but immediately fail to publish routes.
# --------------------------------------------------
- name: Verify Redis on Heimdall is reachable from Swarm manager
ansible.builtin.wait_for:
host: "{{ edge_routing.edge_host.ip }}"
port: 6379
timeout: 10
state: started
when: inventory_hostname == groups['swarm_managers'][0]
tags: [preflight]
# --------------------------------------------------
# STEP 4: Deploy traefik-kop stack
# WHY swarm_stack_deploy role: handles template render, compose validation,
# docker stack deploy idempotently, and external network pre-checks.
# --------------------------------------------------
- name: Deploy traefik-kop stack
ansible.builtin.include_role:
name: swarm_stack_deploy
vars:
stack_name: "traefik-kop"
stack_compose_src: "{{ playbook_dir }}/../../templates/stacks/traefik-kop.stack.yml"
stack_state: "{{ traefik_kop_stack_state }}"
stack_required_external_networks:
- "{{ edge_routing.swarm.proxy_network }}"
stack_required_directories: []
when: inventory_hostname == groups['swarm_managers'][0]
tags: [deploy]
# --------------------------------------------------
# STEP 5: Verify the service is running
# --------------------------------------------------
- name: Wait for traefik-kop service to converge
ansible.builtin.command: >
docker service ls --filter name=traefik-kop_traefik-kop --format '{{ "{{" }}.Replicas{{ "}}" }}'
register: _kop_replicas
retries: 6
delay: 5
until: _kop_replicas.stdout is search('1/1')
changed_when: false
when:
- inventory_hostname == groups['swarm_managers'][0]
- traefik_kop_stack_state == 'present'
- not ansible_check_mode
tags: [verify]
- name: Report deployment result
ansible.builtin.debug:
msg:
- "================================================"
- "traefik-kop deployment complete."
- "================================================"
- "Stack : traefik-kop"
- "Manager : {{ inventory_hostname }} ({{ ansible_host | default('') }})"
- "Redis : {{ edge_routing.integration.redis_addr }}"
- "Bind IP : {{ edge_routing.swarm.bind_ip }}"
- "Network : {{ edge_routing.swarm.proxy_network }} (overlay)"
- "------------------------------------------------"
- "To verify routes in Redis, run on Heimdall:"
- " docker exec redis redis-cli keys 'traefik/*'"
- "================================================"
when: inventory_hostname == groups['swarm_managers'][0]
tags: [always]