--- - name: Deploy Docker Image to Application Stack hosts: "{{ deployment_hosts | default('production') }}" gather_facts: yes become: no vars: # Determine stack path based on environment application_stack_dest: >- {%- if deployment_environment == 'staging' -%} {{ staging_stack_path | default(stacks_base_path + '/staging') }} {%- else -%} {{ app_stack_path | default(stacks_base_path + '/production') }} {%- endif -%} application_compose_suffix: >- {%- if deployment_environment == 'staging' -%} staging.yml {%- else -%} production.yml {%- endif -%} # Image to deploy (can be overridden via -e image_tag=...) image_tag: "{{ image_tag | default('latest') }}" docker_registry: "{{ docker_registry | default('registry.michaelschiemer.de') }}" app_name: "{{ app_name | default('framework') }}" # Full image URL deploy_image: "{{ docker_registry }}/{{ app_name }}:{{ image_tag }}" # Deployment environment (staging or production) deployment_environment: "{{ deployment_environment | default('production') }}" tasks: - name: Determine Docker registry password from vault or extra vars ansible.builtin.set_fact: registry_password: >- {%- if docker_registry_password is defined and docker_registry_password | string | trim != '' -%} {{ docker_registry_password }} {%- elif vault_docker_registry_password is defined and vault_docker_registry_password | string | trim != '' -%} {{ vault_docker_registry_password }} {%- else -%} {{ '' }} {%- endif -%} no_log: yes - name: Check if registry is accessible ansible.builtin.uri: url: "http://{{ docker_registry }}/v2/" method: GET status_code: [200, 401] timeout: 5 register: registry_check ignore_errors: yes delegate_to: "{{ inventory_hostname }}" become: no - name: Login to Docker registry community.docker.docker_login: registry_url: "{{ docker_registry }}" username: "{{ docker_registry_username | default('admin') }}" password: "{{ registry_password }}" when: - registry_password | string | trim != '' - registry_check.status | default(0) in [200, 401] no_log: yes ignore_errors: yes register: docker_login_result - name: Pull Docker image community.docker.docker_image: name: "{{ deploy_image }}" source: pull pull: true register: image_pull_result failed_when: image_pull_result.failed | default(false) - name: Verify image exists locally community.docker.docker_image_info: name: "{{ deploy_image }}" register: image_info failed_when: image_info.failed | default(false) - name: Update docker-compose file with new image tag ansible.builtin.replace: path: "{{ application_stack_dest }}/docker-compose.{{ application_compose_suffix }}" regexp: '^(\s+image:\s+)({{ docker_registry }}/{{ app_name }}:)(.*)$' replace: '\1\2{{ image_tag }}' register: compose_update_result failed_when: false changed_when: compose_update_result.changed | default(false) - name: Update docker-compose file with new image (alternative pattern) ansible.builtin.replace: path: "{{ application_stack_dest }}/docker-compose.{{ application_compose_suffix }}" regexp: 'image:\s+{{ docker_registry }}/{{ app_name }}:.*' replace: 'image: {{ deploy_image }}' register: compose_update_alt when: compose_update_result.changed == false failed_when: false changed_when: compose_update_alt.changed | default(false) - name: Ensure Docker networks exist community.docker.docker_network: name: "{{ item }}" state: present loop: - traefik-public - app-internal ignore_errors: yes - name: Deploy application stack with new image shell: | cd {{ application_stack_dest }} docker compose -f docker-compose.base.yml -f docker-compose.{{ application_compose_suffix }} up -d --pull missing --force-recreate --remove-orphans register: compose_deploy_result changed_when: true - name: Wait for containers to start ansible.builtin.pause: seconds: 15 - name: Check container status shell: | cd {{ application_stack_dest }} docker compose -f docker-compose.base.yml -f docker-compose.{{ application_compose_suffix }} ps register: container_status changed_when: false - name: Display deployment summary ansible.builtin.debug: msg: | ======================================== Image Deployment Summary ======================================== Image: {{ deploy_image }} Tag: {{ image_tag }} Environment: {{ deployment_environment }} Stack: {{ application_stack_dest }} Status: SUCCESS ======================================== Container Status: {{ container_status.stdout | default('Not available') }} ========================================