--- # ansible/playbooks/docker/swarm_preflight.yml # # Swarm Foundation Pre-flight # =========================== # Addresses all four hard prerequisites before any service can be deployed to # the swarm. Run this once after swarm_bootstrap and before swarm_stack_deploy. # # Prerequisites satisfied: # 1. NFS mounts — /mnt/homelab + /mnt/media mounted on every node # 2. proxy-net — overlay network present on the swarm (172.20.0.0/24) # 3. Node labels — role=manager / role=worker applied to every node # 4. /opt/stacks — deploy root created on every node (owned by lab user) # # Usage: # # Dry-run (safe, no changes): # ansible-playbook -i inventory/hosts.ini playbooks/docker/swarm_preflight.yml --check # # # Live run: # ansible-playbook -i inventory/hosts.ini playbooks/docker/swarm_preflight.yml # # # Single-concern run: # ansible-playbook -i inventory/hosts.ini playbooks/docker/swarm_preflight.yml --tags storage # ansible-playbook -i inventory/hosts.ini playbooks/docker/swarm_preflight.yml --tags network # ansible-playbook -i inventory/hosts.ini playbooks/docker/swarm_preflight.yml --tags labels # ansible-playbook -i inventory/hosts.ini playbooks/docker/swarm_preflight.yml --tags stacks_root # # Verification (post-run): # ansible swarm_hosts -i inventory/hosts.ini -m command -a "findmnt /mnt/homelab" # ansible swarm_hosts -i inventory/hosts.ini -m stat -a "path=/opt/stacks" # docker node ls --format '{{ "{{" }}.Hostname{{ "}}" }}\t{{ "{{" }}.Labels{{ "}}" }}' # docker network inspect proxy-net ############################################################################### # PLAY 1 — Storage: NFS mounts + /opt/stacks on every swarm node # ############################################################################### - name: "Swarm pre-flight | Storage" hosts: swarm_hosts become: true gather_facts: false tags: [storage, stacks_root] vars: lab_user: "{{ lab_ansible_user | default('chester') }}" roles: - role: storage_mounts tags: [storage] tasks: - name: "Create /opt/stacks deploy root" ansible.builtin.file: path: /opt/stacks state: directory owner: "{{ lab_user }}" group: "{{ lab_user }}" mode: "0755" tags: [stacks_root] ############################################################################### # PLAY 2 — Network: ensure proxy-net overlay exists (run from one manager) # ############################################################################### - name: "Swarm pre-flight | proxy-net overlay network" hosts: swarm_managers[0] become: false gather_facts: false tags: [network] roles: - role: swarm_overlay_network tags: [network] ############################################################################### # PLAY 3 — Labels: apply role=manager / role=worker to every swarm node # ############################################################################### - name: "Swarm pre-flight | Node labels" hosts: swarm_managers[0] become: false gather_facts: false tags: [labels] tasks: - name: "Apply role=manager label to manager nodes" ansible.builtin.command: >- docker node update --label-add role=manager {{ item }} loop: "{{ groups['swarm_managers'] }}" changed_when: false # docker node update is idempotent — labels are additive and # re-applying the same label does not change cluster state. tags: [labels] - name: "Apply role=worker label to worker nodes" ansible.builtin.command: >- docker node update --label-add role=worker {{ item }} loop: "{{ groups['swarm_workers'] }}" changed_when: false tags: [labels] - name: "Show node label summary" ansible.builtin.shell: >- for node in $(docker node ls --format "{{ '{{' }}.Hostname{{ '}}' }}"); do echo "$node $(docker node inspect $node --format '{{ '{{' }}json .Spec.Labels{{ '}}' }}')"; done register: swarm_node_summary changed_when: false tags: [labels] - name: "Print node label summary" ansible.builtin.debug: msg: "{{ swarm_node_summary.stdout_lines }}" tags: [labels]