diff --git a/.gitignore b/.gitignore index 24bb2bc..66ffdec 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,4 @@ charts/ # Keys keys/ old/ +.env.yml \ No newline at end of file diff --git a/Makefile b/Makefile index 8b3bf0c..4bb9f17 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ ansible-workers: ansible-stack: cd ansible && ansible-playbook -i ../vagrant/.vagrant/provisioners/ansible/inventory/vagrant_ansible_inventory swarm-stack.yml ansible-full: - make ansible-master && make ansible-workers && make ansible-full + make ansible-master && make ansible-workers && make ansible-stack setup-keys: mkdir -p ./keys @@ -32,3 +32,5 @@ swarm-check: cd vagrant && vagrant ssh swarm-master -c "docker info"|grep -e "Managers" -e "Nodes" service-ls: cd vagrant && vagrant ssh swarm-master -c "docker service ls" +service-portainer-restart: + cd vagrant && vagrant ssh swarm-master -c "docker service rm portainer_portainer" diff --git a/ansible/files/docker-base-stack.yml b/ansible/files/docker-base-stack.yml index 4e499ba..3ac8b23 100644 --- a/ansible/files/docker-base-stack.yml +++ b/ansible/files/docker-base-stack.yml @@ -19,7 +19,22 @@ services: restart_policy: condition: on-failure replicas: 1 - + # db: + # image: postgres + # restart: always + # environment: + # POSTGRES_PASSWORD: example + # volumes: + # - pgdata:/var/lib/postgresql/data + # networks: + # - portainer_net + # deploy: + # placement: + # constraints: + # - node.role == manager + # restart_policy: + # condition: on-failure + # replicas: 1 volumes: portainer_data: external: true diff --git a/ansible/group_vars/all.yml b/ansible/group_vars/all.yml index d1c4e2e..4383ddd 100644 --- a/ansible/group_vars/all.yml +++ b/ansible/group_vars/all.yml @@ -12,3 +12,5 @@ docker_packages_state: present docker_users: - vagrant +# docker_hub_username: "{{ lookup('env_file', '.env', 'DOCKER_HUB_USERNAME') }}" +# docker_hub_password: "{{ lookup('env_file', '.env', 'DOCKER_HUB_PASSWORD') }}" \ No newline at end of file diff --git a/ansible/roles/common/tasks/main.yml b/ansible/roles/common/tasks/main.yml index 185081c..a35de24 100644 --- a/ansible/roles/common/tasks/main.yml +++ b/ansible/roles/common/tasks/main.yml @@ -1,3 +1,14 @@ + +- name: Load environment variables from .env file + include_vars: + file: .env.yml + name: env_vars + +- name: Set Docker Hub credentials from env vars + set_fact: + docker_hub_username: "{{ env_vars.DOCKER_HUB_USERNAME }}" + docker_hub_password: "{{ env_vars.DOCKER_HUB_PASSWORD }}" + - name: Check memory and swap usage command: free -m register: memory_info @@ -6,6 +17,14 @@ - name: apt update ansible.builtin.apt: update_cache: yes + +- name: install packages + ansible.builtin.apt: + name: + - jq + - vim + - curl + state: present - name: Ensure Python pip is installed package: @@ -35,3 +54,38 @@ until: docker_service.status.ActiveState == "active" retries: 10 delay: 20 + +# - name: Create Docker config directory for vagrant user +# file: +# path: /home/vagrant/.docker +# state: directory +# mode: '0700' +# owner: vagrant +# group: vagrant + +- name: Login to Docker Hub as vagrant user + community.docker.docker_login: + username: "{{ docker_hub_username }}" + password: "{{ docker_hub_password }}" + # config_path: "/home/vagrant/.docker" + reauth: true + become_user: vagrant + when: docker_hub_username is defined and docker_hub_password is defined + register: docker_login_result + +- name: Debug Docker Hub credentials + debug: + msg: | + Username: {{ docker_hub_username }} + Password: {{ docker_hub_password }} + +- name: Debug Docker login result + debug: + msg: | + === DOCKER LOGIN RESULT === + Changed: {{ docker_login_result.changed }} + Failed: {{ docker_login_result.failed | default(false) }} + Message: {{ docker_login_result.msg | default('No message') }} + Login successful: {{ docker_login_result.login_successful | default('Unknown') }} + Full result: {{ docker_login_result }} + # when: docker_hub_username is defined and docker_hub_password is defined \ No newline at end of file diff --git a/ansible/swarm-master.yml b/ansible/swarm-master.yml index d748148..ae53827 100644 --- a/ansible/swarm-master.yml +++ b/ansible/swarm-master.yml @@ -33,6 +33,7 @@ state: present advertise_addr: "{{ ansible_eth1.ipv4.address }}" listen_addr: "{{ ansible_eth1.ipv4.address }}" + - name: print listen addr debug: msg: "{{ ansible_eth1.ipv4.address }}" diff --git a/ansible/swarm-node.yml b/ansible/swarm-node.yml index 0501ec1..8b2cc71 100644 --- a/ansible/swarm-node.yml +++ b/ansible/swarm-node.yml @@ -20,27 +20,28 @@ set_fact: worker_token: "{{ worker_token_file.content | b64decode | trim }}" - - name: Debug all relevant variables - debug: - msg: | - === SWARM JOIN DEBUG INFO === - Current host: {{ inventory_hostname }} - Current host IP: {{ ansible_host }} + # - name: Debug all relevant variables + # debug: + # msg: | + # === SWARM JOIN DEBUG INFO === + # Current host: {{ inventory_hostname }} + # Current host IP: {{ ansible_host }} + # ansible_eth1={{ ansible_eth1.ipv4.address }} - Master group hosts: {{ groups['swarm_master'] }} - First master: {{ groups['swarm_master'][0] }} - - Master hostvars: - - ansible_host: {{ hostvars[groups['swarm_master'][0]]['ansible_host'] }} - - inventory_hostname: {{ hostvars[groups['swarm_master'][0]]['inventory_hostname'] }} - - Remote address calculation: - - Raw master ansible_host: {{ hostvars[groups['swarm_master'][0]]['ansible_host'] }} - - Fallback to hostname: {{ groups['swarm_master'][0] }} - - Final address: {{ hostvars[groups['swarm_master'][0]]['ansible_host'] | default(groups['swarm_master'][0]) }} - - With port: {{ hostvars[groups['swarm_master'][0]]['ansible_host'] | default(groups['swarm_master'][0]) }}:2377 - - Join token (first 10 chars): {{ worker_token[:10] }}... + # Master group hosts: {{ groups['swarm_master'] }} + # First master: {{ groups['swarm_master'][0] }} + + # Master hostvars: + # - ansible_host: {{ hostvars[groups['swarm_master'][0]]['ansible_host'] }} + # - inventory_hostname: {{ hostvars[groups['swarm_master'][0]]['inventory_hostname'] }} + + # Remote address calculation: + # - Raw master ansible_host: {{ hostvars[groups['swarm_master'][0]]['ansible_host'] }} + # - Fallback to hostname: {{ groups['swarm_master'][0] }} + # - Final address: {{ hostvars[groups['swarm_master'][0]]['ansible_host'] | default(groups['swarm_master'][0]) }} + # - With port: {{ hostvars[groups['swarm_master'][0]]['ansible_host'] | default(groups['swarm_master'][0]) }}:2377 + + # Join token (first 10 chars): {{ worker_token[:10] }}... - name: Gather master facts ansible.builtin.setup: @@ -48,21 +49,23 @@ run_once: true register: master_facts - - name: Debug master hostvars - debug: - msg: | - Master hostvars keys: {{ hostvars[groups['swarm_master'][0]].keys() | list }} - Master ansible_eth1: {{ hostvars[groups['swarm_master'][0]].ansible_eth1 | default('Not available') }} - Master node: {{ groups['swarm_master'][0] }} - Master ansible_default_ipv4: {{ hostvars[groups['swarm_master'][0]] }} - Master ansible_default_ipv4: {{ master_facts.ansible_facts }} - FINAL IP: {{ master_facts.ansible_facts ['ansible_eth1']['ipv4']['address'] }} + # - name: Debug master hostvars + # debug: + # msg: | + # Master hostvars keys: {{ hostvars[groups['swarm_master'][0]].keys() | list }} + # Master ansible_eth1: {{ hostvars[groups['swarm_master'][0]].ansible_eth1 | default('Not available') }} + # Master node: {{ groups['swarm_master'][0] }} + # Master ansible_default_ipv4: {{ hostvars[groups['swarm_master'][0]] }} + # Master ansible_default_ipv4: {{ master_facts.ansible_facts }} + # remote_addrs: {{ master_facts.ansible_facts ['ansible_eth1']['ipv4']['address'] }} + # advertise_addr: {{ ansible_eth1.ipv4.address }} - name: Add nodes community.docker.docker_swarm: state: join join_token: "{{ worker_token }}" remote_addrs: - - "{{ master_facts.ansible_facts ['ansible_eth1']['ipv4']['address'] }}:2377" + - "{{ master_facts.ansible_facts['ansible_eth1']['ipv4']['address'] }}:2377" advertise_addr: "{{ ansible_eth1.ipv4.address }}" + listen_addr: "{{ ansible_eth1.ipv4.address }}:2377" \ No newline at end of file diff --git a/ansible/swarm-stack.yml b/ansible/swarm-stack.yml index c6f5d2e..eb07b9d 100644 --- a/ansible/swarm-stack.yml +++ b/ansible/swarm-stack.yml @@ -12,7 +12,7 @@ - name: Deploy Portainer stack from compose file community.docker.docker_stack: - name: portainer + name: mgmt state: present compose: - "{{ lookup('file', 'files/docker-base-stack.yml') | from_yaml }}" \ No newline at end of file diff --git a/test-usage/Makefile b/test-usage/Makefile new file mode 100644 index 0000000..3e32117 --- /dev/null +++ b/test-usage/Makefile @@ -0,0 +1,5 @@ +deploy: + ansible-playbook -i ../vagrant/.vagrant/provisioners/ansible/inventory/vagrant_ansible_inventory playbook.yml + +watch: + find . -type f | entr -p make deploy \ No newline at end of file diff --git a/test-usage/ansible.cfg b/test-usage/ansible.cfg new file mode 100644 index 0000000..f4e454c --- /dev/null +++ b/test-usage/ansible.cfg @@ -0,0 +1,6 @@ +[defaults] +host_key_checking=False +# stdout_callback = minimal +color = true +interpreter_python = /usr/bin/python3.9 +deprecation_warnings=False \ No newline at end of file diff --git a/test-usage/files/docker-compose.yml b/test-usage/files/docker-compose.yml new file mode 100644 index 0000000..93d3911 --- /dev/null +++ b/test-usage/files/docker-compose.yml @@ -0,0 +1,129 @@ +services: + traefik: + image: traefik:v3.4 + + networks: + # Connect to the 'traefik_proxy' overlay network for inter-container communication across nodes + - traefik_proxy + + ports: + # Expose Traefik's entry points to the Swarm + # Swarm requires the long syntax for ports. + - target: 80 # Container port (Traefik web entry point) + published: 80 # Host port exposed on the nodes + protocol: tcp + # 'host' mode binds directly to the node's IP where the task runs. + # 'ingress' mode uses Swarm's Routing Mesh (load balances across nodes). + # Choose based on your load balancing strategy. 'host' is often simpler if using an external LB. + mode: host + - target: 443 # Container port ( Traefik websecure entry point) + published: 443 # Host port + protocol: tcp + mode: host + - target: 8080 # Container port (Traefik web entry point) + published: 8080 # Host port exposed on the nodes + protocol: tcp + mode: host + + volumes: + # Mount the Docker socket for the Swarm provider + # This MUST be run from a manager node to access the Swarm API via the socket. + - /var/run/docker.sock:/var/run/docker.sock:ro # Swarm API socket + # - ./certs:/certs:ro + # - ./dynamic:/dynamic:ro + + # Traefik Static configuration via command-line arguments + command: + # HTTP EntryPoint + - "--entrypoints.web.address=:80" + + # Configure HTTP to HTTPS Redirection + # - "--entrypoints.web.http.redirections.entrypoint.to=websecure" + # - "--entrypoints.web.http.redirections.entrypoint.scheme=https" + # - "--entrypoints.web.http.redirections.entrypoint.permanent=true" + + # HTTPS EntryPoint + - "--entrypoints.websecure.address=:443" + - "--entrypoints.websecure.http.tls=true" + + # Attach dynamic TLS file + # - "--providers.file.filename=/dynamic/tls.yaml" + + # Providers + + # Enable the Docker Swarm provider (instead of Docker provider) + - "--providers.swarm.endpoint=unix:///var/run/docker.sock" + + # Watch for Swarm service changes (requires socket access) + - "--providers.swarm.watch=true" + + # Recommended: Don't expose services by default; require explicit labels + - "--providers.swarm.exposedbydefault=false" + + # Specify the default network for Traefik to connect to services + - "--providers.swarm.network=myappstack_traefik_proxy" + + # API & Dashboard + - "--api.dashboard=true" # Enable the dashboard + - "--api.insecure=true" # Explicitly disable insecure API mod + + # Observability + - "--log.level=INFO" # Set the Log Level e.g INFO, DEBUG + - "--accesslog=true" # Enable Access Logs + - "--metrics.prometheus=true" # Enable Prometheus + + deploy: + mode: replicated + replicas: 1 + placement: + + # Placement constraints restrict where Traefik tasks can run. + # Running on manager nodes is common for accessing the Swarm API via the socket. + constraints: + - node.role == manager + + # Traefik Dynamic configuration via labels + # In Swarm, labels on the service definition configure Traefik routing for that service. + labels: + - "traefik.enable=true" + + # Dashboard router + - "traefik.http.routers.dashboard.rule=PathPrefix(`/d2`)" + - "traefik.http.routers.dashboard.entrypoints=web" + - "traefik.http.middlewares.dashboard-stripprefix.stripprefix.prefixes=/d2" + - "traefik.http.routers.dashboard.middlewares=dashboard-stripprefix" + - "traefik.http.routers.dashboard.service=api@internal" + - "traefik.http.routers.dashboard.tls=false" + + # Basic‑auth middleware + # - "traefik.http.middlewares.dashboard-auth.basicauth.users=" + # - "traefik.http.routers.dashboard.middlewares=dashboard-auth@swarm" + + # Service hint + - "traefik.http.services.traefik.loadbalancer.server.port=8080" + + # Deploy the Whoami application + whoami: + image: traefik/whoami + ports: + - "8081:80" + networks: + - traefik_proxy + deploy: + labels: + # Enable Service discovery for Traefik + - "traefik.enable=true" + # Define the Whoami router rule - match root path + - "traefik.http.routers.whoami.rule=PathPrefix(`/whoami`)" + # Expose Whoami on the HTTP entrypoint + - "traefik.http.routers.whoami.entrypoints=web" + - "traefik.http.middlewares.whoami-stripprefix.stripprefix.prefixes=/whoami" + - "traefik.http.routers.whoami.middlewares=whoami-stripprefix" + # Expose the whoami port number to Traefik + - "traefik.http.services.whoami.loadbalancer.server.port=80" + +# Define the overlay network for Swarm +networks: + traefik_proxy: + driver: overlay + # attachable: true \ No newline at end of file diff --git a/test-usage/playbook.yml b/test-usage/playbook.yml new file mode 100644 index 0000000..dd8aed6 --- /dev/null +++ b/test-usage/playbook.yml @@ -0,0 +1,14 @@ +--- +- hosts: swarm_master + become: true + + roles: + + tasks: + - name: Deploy myappstack stack from compose file + community.docker.docker_stack: + name: myappstack + state: present + prune: true + compose: + - "{{ lookup('file', 'files/docker-compose.yml') | from_yaml }}" \ No newline at end of file diff --git a/vagrant/Vagrantfile b/vagrant/Vagrantfile index 4166bdf..ce7abd9 100644 --- a/vagrant/Vagrantfile +++ b/vagrant/Vagrantfile @@ -1,7 +1,13 @@ -num_workers = 7 +num_workers = 1 nodes = [ - { hostname: 'swarm-master', ip: '192.168.56.10', ram: 256, cpus: 1, groups: ['swarm_master'] } + { + hostname: 'swarm-master', + ip: '192.168.56.10', + ram: 256, + cpus: 1, + groups: ['swarm_master'], + }, ] (1..num_workers).each do |i| @@ -15,6 +21,8 @@ nodes = [ end Vagrant.configure('2') do |config| + # config.vbguest.auto_update = true + groups = {} nodes.each do |node| node[:groups].each do |group| @@ -25,6 +33,7 @@ Vagrant.configure('2') do |config| config.vm.provision 'ansible' do |ansible| ansible.playbook = 'setup.yml' + ansible.config_file = 'ansible.cfg' ansible.groups = groups # ansible.verbose = true diff --git a/vagrant/ansible.cfg b/vagrant/ansible.cfg new file mode 100644 index 0000000..f4e454c --- /dev/null +++ b/vagrant/ansible.cfg @@ -0,0 +1,6 @@ +[defaults] +host_key_checking=False +# stdout_callback = minimal +color = true +interpreter_python = /usr/bin/python3.9 +deprecation_warnings=False \ No newline at end of file