From d3188806005aedd059fca3581e33271f2ff78723 Mon Sep 17 00:00:00 2001 From: aleidk Date: Tue, 10 Dec 2024 16:26:17 -0300 Subject: [PATCH] feat: add worker nodes to docker swarm setup --- hosts/inventory.ini | 13 ++- playbooks/setup/alpine.yaml | 40 ++++--- ...rguments_specs.yaml => argument_specs.yml} | 1 + roles/common/tasks/main.yaml | 7 +- roles/docker/files/openrc.sh | 61 ++++++++++ roles/docker/meta/argument_specs.yml | 25 ++++ roles/docker/tasks/docker_alpine.yaml | 20 ++++ roles/docker/tasks/main.yaml | 107 +++--------------- roles/docker/tasks/swarm_manager.yaml | 81 +++++++++++++ roles/docker/tasks/swarm_worker.yaml | 9 ++ 10 files changed, 252 insertions(+), 112 deletions(-) rename roles/common/meta/{arguments_specs.yaml => argument_specs.yml} (96%) create mode 100755 roles/docker/files/openrc.sh create mode 100644 roles/docker/meta/argument_specs.yml create mode 100644 roles/docker/tasks/docker_alpine.yaml create mode 100644 roles/docker/tasks/swarm_manager.yaml create mode 100644 roles/docker/tasks/swarm_worker.yaml diff --git a/hosts/inventory.ini b/hosts/inventory.ini index 320db68..1b5dcfd 100644 --- a/hosts/inventory.ini +++ b/hosts/inventory.ini @@ -1,5 +1,12 @@ -[homelab] +[homelab:children] +docker + +[docker:children] +docker_managers +docker_workers + +[docker_managers] 10.0.10.50 -[docker] -10.0.10.50 docker_swarm_manager=true +[docker_workers] +10.0.10.[51:52] diff --git a/playbooks/setup/alpine.yaml b/playbooks/setup/alpine.yaml index 495fba7..d2e4977 100644 --- a/playbooks/setup/alpine.yaml +++ b/playbooks/setup/alpine.yaml @@ -1,6 +1,6 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/ansible/ansible-lint/refs/heads/main/src/ansiblelint/schemas/playbook.json -# > /COPILOT_GENERATE I'm writing an ansible playbook to setup a new alpine server, I want you to help me to generate some tasks - +--- - name: Setup an alpine machine hosts: homelab user: root @@ -41,24 +41,12 @@ line: 'permit persist :wheel' state: present - - name: Install Docker - ansible.builtin.package: - state: present - name: - - docker - - docker-cli-compose - - py3-yaml - - py3-pip - - py3-docker-py - - name: Common setup hosts: homelab user: root roles: - role: common vars: - extra_groups: - - docker robo_allowed_commands: - "docker system dial-stdio" @@ -67,3 +55,27 @@ user: root roles: - docker + vars: + users: + - aleidk + - robo + +- name: Setup docker swarm manager + hosts: docker_managers + user: root + tasks: + - name: Setup swarm manager + include_role: + name: docker + tasks_from: swarm_manager.yaml + +- name: Setup docker swarm workers + hosts: docker_workers + user: root + tasks: + - name: "Setup swarm workers" + include_role: + name: docker + tasks_from: swarm_worker.yaml + vars: + managers_group: docker_managers diff --git a/roles/common/meta/arguments_specs.yaml b/roles/common/meta/argument_specs.yml similarity index 96% rename from roles/common/meta/arguments_specs.yaml rename to roles/common/meta/argument_specs.yml index 899da2b..c049872 100644 --- a/roles/common/meta/arguments_specs.yaml +++ b/roles/common/meta/argument_specs.yml @@ -9,6 +9,7 @@ argument_specs: type: "list" elements: "str" required: false + default: [] description: - "Additional groups that will be added to each user" robo_allowed_commands: diff --git a/roles/common/tasks/main.yaml b/roles/common/tasks/main.yaml index 4b619e5..ea577eb 100644 --- a/roles/common/tasks/main.yaml +++ b/roles/common/tasks/main.yaml @@ -1,10 +1,5 @@ # yaml-language-server: $schema=https://raw.githubusercontent.com/ansible/ansible-lint/refs/heads/main/src/ansiblelint/schemas/tasks.json -- name: Create a user group named docker - loop: "{{ extra_groups }}" - ansible.builtin.group: - name: "{{ item }}" - - name: Setup users loop: "{{ users }}" ansible.builtin.user: @@ -14,7 +9,7 @@ shell: "{{ item.shell }}" create_home: true password: "{{ (item.password != '!' or item.password != '*') | ternary(item.password | password_hash('sha512'), item.password) }}" - groups: "{{ item.groups + extra_groups }}" + groups: "{{ item.groups + (extra_groups | default([])) }}" - name: Add SSH public key to users loop: "{{ users }}" diff --git a/roles/docker/files/openrc.sh b/roles/docker/files/openrc.sh new file mode 100755 index 0000000..5365558 --- /dev/null +++ b/roles/docker/files/openrc.sh @@ -0,0 +1,61 @@ +#!/sbin/openrc-run +supervisor=supervise-daemon + +name="Docker Daemon" +description="Persistent process that manages docker containers" +description_reload="Reload configuration without exiting" + +command="${DOCKERD_BINARY:-/usr/bin/dockerd}" +command_args="${DOCKER_OPTS}" + +DOCKER_LOGFILE="${DOCKER_LOGFILE:-/var/log/${RC_SVCNAME}.log}" +DOCKER_ERRFILE="${DOCKER_ERRFILE:-${DOCKER_LOGFILE}}" +DOCKER_OUTFILE="${DOCKER_OUTFILE:-${DOCKER_LOGFILE}}" +if [ "$DOCKER_ERRFILE" = "$DOCKER_OUTFILE" ]; then + LOGPROXY_OPTS="$LOGPROXY_OPTS -m" +fi +export \ + LOGPROXY_CHMOD="${LOGPROXY_CHMOD:-0644}" \ + LOGPROXY_LOG_DIRECTORY="${LOGPROXY_LOG_DIRECTORY:-/var/log}" \ + LOGPROXY_ROTATION_SIZE="${LOGPROXY_ROTATION_SIZE:-104857600}" \ + LOGPROXY_ROTATION_TIME="${LOGPROXY_ROTATION_TIME:-86400}" \ + LOGPROXY_ROTATION_SUFFIX="${LOGPROXY_ROTATION_SUFFIX:-.%Y%m%d%H%M%S}" \ + LOGPROXY_ROTATED_FILES="${LOGPROXY_ROTATE_FILES:-5}" + +output_logger="log_proxy $LOGPROXY_OPTS $DOCKER_OUTFILE" +error_logger="log_proxy $LOGPROXY_OPTS $DOCKER_ERRFILE" + +extra_started_commands="reload" + +rc_ulimit="" +RC_ULIMIT="" +if [ "$1" = "start" ]; then + if [ $BASH ]; then + rc_ulimit="${DOCKER_ULIMIT:--c unlimited -n 1048576 -u unlimited}" + else + ulimit -c unlimited + ulimit -n 1048576 + ulimit -p unlimited + fi +fi + +retry="${DOCKER_RETRY:-TERM/60/KILL/10}" + +if [ -e /etc/profile.d/proxy.sh ]; then + . /etc/profile.d/proxy.sh +fi + +depend() { + need sysfs cgroups net + after firewall +} + +start_pre() { + checkpath -f -m 0644 -o root:docker "$DOCKER_ERRFILE" "$DOCKER_OUTFILE" +} + +reload() { + ebegin "Reloading configuration" + $supervisor $RC_SVCNAME --signal HUP + eend $? +} diff --git a/roles/docker/meta/argument_specs.yml b/roles/docker/meta/argument_specs.yml new file mode 100644 index 0000000..d4f5e48 --- /dev/null +++ b/roles/docker/meta/argument_specs.yml @@ -0,0 +1,25 @@ +--- +argument_specs: + main: + short_description: Main entrypoint + author: + - aleidk + options: + users: + type: "list" + elements: "str" + default: [] + description: + - "list of users to add to the docker group" + + swarm_workers: + short_description: Docker swarm workers setup + author: + - aleidk + options: + managers_group: + type: "str" + required: true + description: + - "The host group of the managers" + - "Used to obtain the advertised address and join tokens" diff --git a/roles/docker/tasks/docker_alpine.yaml b/roles/docker/tasks/docker_alpine.yaml new file mode 100644 index 0000000..2c6c2b4 --- /dev/null +++ b/roles/docker/tasks/docker_alpine.yaml @@ -0,0 +1,20 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/ansible/ansible-lint/refs/heads/main/src/ansiblelint/schemas/tasks.json +--- +- name: Install Docker + ansible.builtin.package: + state: present + name: + - docker + - docker-cli-compose + - py3-yaml + - py3-pip + - py3-docker-py + +- name: Copy openrc.sh to /etc/init.d/docker + copy: + src: files/openrc.sh + dest: /etc/init.d/docker + mode: '0755' + owner: root + group: root + diff --git a/roles/docker/tasks/main.yaml b/roles/docker/tasks/main.yaml index 4354687..bdc01af 100644 --- a/roles/docker/tasks/main.yaml +++ b/roles/docker/tasks/main.yaml @@ -1,94 +1,23 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/ansible/ansible-lint/refs/heads/main/src/ansiblelint/schemas/tasks.json +--- +- name: Create a user group named docker + ansible.builtin.group: + name: "docker" + +- name: Add users to docker group + loop: "{{ users }}" + ansible.builtin.user: + state: present + append: true + name: "{{ item }}" + groups: "docker" + +- name: Install docker in Alpine + import_tasks: docker_alpine.yaml + when: ansible_facts['os_family']|lower == 'alpine' + - name: Start docker service ansible.builtin.service: name: docker state: started enabled: true - -- name: Setup Docker Swarm - when: docker_swarm_manager | bool - block: - - name: Enable Docker Swarm mode - community.docker.docker_swarm: - state: present - - - name: Create Traefik network - community.docker.docker_network: - name: reverse-proxy - driver: overlay - attachable: true - - - name: Deploy Traefik service - community.docker.docker_compose_v2: - remove_orphans: true - project_name: reverse-proxy - definition: - networks: - reverse-proxy: - external: true - services: - traefik: - container_name: traefix-proxy - image: 'traefik:latest' - restart: unless-stopped - networks: - - reverse-proxy - ports: - # listen on host ports without ingress network - - target: 80 - published: 80 - protocol: tcp - mode: host - - target: 443 - published: 443 - protocol: tcp - mode: host - - target: 8080 - published: 8080 - protocol: tcp - mode: host - volumes: - - /var/run/docker.sock:/var/run/docker.sock:ro - healthcheck: - test: 'wget -qO- http://localhost:80/ping || exit 1' - interval: 4s - timeout: 2s - retries: 5 - command: - - '--ping=true' - - '--ping.entrypoint=http' - - '--api.dashboard=true' - - '--api.insecure=true' - - '--entrypoints.http.address=:80' - - '--entryPoints.http.forwardedHeaders.trustedIPs=10.0.10.0/24' - - '--entrypoints.http.http.encodequerysemicolons=true' - - '--entryPoints.http.http2.maxConcurrentStreams=50' - # - "--providers.swarm.endpoint=tcp://{{ ansible_default_ipv4.address }}:2375" - - --providers.swarm.exposedByDefault=false - - --providers.swarm.network=reverse-proxy - deploy: - mode: global - placement: - constraints: - - node.role==manager - labels: - - traefik.enable=true - - traefik.http.routers.traefik.entrypoints=http - - traefik.http.routers.traefik.service=api@internal - - traefik.http.services.traefik.loadbalancer.server.port=8080 - - - name: Check if Docker context exists - local_action: ansible.builtin.command docker context inspect {{ ansible_hostname }} - register: context_exists - ignore_errors: true - - - name: Create Docker context for each Swarm manager machine - local_action: > - ansible.builtin.command docker context create {{ ansible_hostname }} --docker "host=ssh://{{ ansible_default_ipv4.address }}" - when: context_exists.stderr != '' - -- name: Join Docker Swarm as a worker - community.docker.docker_swarm: - state: join - join_token: "{{ hostvars['manager']['docker_swarm_worker_token'] }}" - remote_addrs: ["{{ hostvars['manager']['ansible_default_ipv4']['address'] }}"] - when: not docker_swarm_manager | bool diff --git a/roles/docker/tasks/swarm_manager.yaml b/roles/docker/tasks/swarm_manager.yaml new file mode 100644 index 0000000..d9dd926 --- /dev/null +++ b/roles/docker/tasks/swarm_manager.yaml @@ -0,0 +1,81 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/ansible/ansible-lint/refs/heads/main/src/ansiblelint/schemas/tasks.json +--- +- name: Enable Docker Swarm mode + register: swarm_info + community.docker.docker_swarm: + state: present + +- name: Create Traefik network + community.docker.docker_network: + name: reverse-proxy + driver: overlay + attachable: true + +- name: Check if Docker context exists + local_action: ansible.builtin.command docker context inspect {{ ansible_hostname }} + register: context_exists + ignore_errors: true + +- name: Create Docker context for each Swarm manager machine + local_action: > + ansible.builtin.command docker context create {{ ansible_hostname }} --docker "host=ssh://{{ ansible_default_ipv4.address }}" + when: context_exists.stderr != '' + +- name: Deploy Traefik service + community.docker.docker_compose_v2: + remove_orphans: true + project_name: reverse-proxy + definition: + networks: + reverse-proxy: + external: true + services: + traefik: + container_name: traefix-proxy + image: 'traefik:latest' + restart: unless-stopped + networks: + - reverse-proxy + ports: + # listen on host ports without ingress network + - target: 80 + published: 80 + protocol: tcp + mode: host + - target: 443 + published: 443 + protocol: tcp + mode: host + - target: 8080 + published: 8080 + protocol: tcp + mode: host + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + healthcheck: + test: 'wget -qO- http://localhost:80/ping || exit 1' + interval: 4s + timeout: 2s + retries: 5 + command: + - '--ping=true' + - '--ping.entrypoint=http' + - '--api.dashboard=true' + - '--api.insecure=true' + - '--entrypoints.http.address=:80' + - '--entryPoints.http.forwardedHeaders.trustedIPs=10.0.10.0/24' + - '--entrypoints.http.http.encodequerysemicolons=true' + - '--entryPoints.http.http2.maxConcurrentStreams=50' + # - "--providers.swarm.endpoint=tcp://{{ ansible_default_ipv4.address }}:2375" + - --providers.swarm.exposedByDefault=false + - --providers.swarm.network=reverse-proxy + deploy: + mode: global + placement: + constraints: + - node.role==manager + labels: + - traefik.enable=true + - traefik.http.routers.traefik.entrypoints=http + - traefik.http.routers.traefik.service=api@internal + - traefik.http.services.traefik.loadbalancer.server.port=8080 diff --git a/roles/docker/tasks/swarm_worker.yaml b/roles/docker/tasks/swarm_worker.yaml new file mode 100644 index 0000000..3a693d3 --- /dev/null +++ b/roles/docker/tasks/swarm_worker.yaml @@ -0,0 +1,9 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/ansible/ansible-lint/refs/heads/main/src/ansiblelint/schemas/tasks.json +--- +- name: Join Docker Swarm as a worker + vars: + key: "{{ groups[managers_group] | map('extract', hostvars, ['swarm_info', 'swarm_facts', 'JoinTokens', 'Worker']) | list | first }}" + community.docker.docker_swarm: + state: join + join_token: "{{ key }}" + remote_addrs: "{{ groups[managers_group] | map('extract', hostvars, ['ansible_default_ipv4', 'address']) }}"