Files
michaelschiemer/deployment/ansible/playbooks/deploy-update.yml
Michael Schiemer de8fed8711 feat: Complete deployment setup for code pushes
- Add pre-flight checks in deploy-update.yml
- Automatically copy docker-compose.yml and nginx config in setup-infrastructure.yml
- Add comprehensive deployment documentation
- Ready for automated code deployments via CI/CD pipeline
2025-10-31 10:31:56 +01:00

199 lines
7.4 KiB
YAML

---
- name: Deploy Application Update via Docker Compose
hosts: production
gather_facts: yes
become: no
vars:
# These should be passed via -e from CI/CD
image_tag: "{{ image_tag | default('latest') }}"
git_commit_sha: "{{ git_commit_sha | default('unknown') }}"
deployment_timestamp: "{{ deployment_timestamp | default(ansible_date_time.iso8601) }}"
app_stack_path: "{{ deploy_user_home }}/deployment/stacks/application"
pre_tasks:
- name: Optionally load registry credentials from encrypted vault
include_vars:
file: "{{ playbook_dir }}/../secrets/production.vault.yml"
no_log: yes
ignore_errors: yes
delegate_to: localhost
become: no
- name: Derive docker registry credentials from vault when not provided
set_fact:
docker_registry_username: "{{ docker_registry_username | default(vault_docker_registry_username | default(omit)) }}"
docker_registry_password: "{{ docker_registry_password | default(vault_docker_registry_password | default(omit)) }}"
- name: Verify Docker is running
systemd:
name: docker
state: started
register: docker_service
become: yes
- name: Fail if Docker is not running
fail:
msg: "Docker service is not running"
when: docker_service.status.ActiveState != 'active'
- name: Ensure application stack directory exists
file:
path: "{{ app_stack_path }}"
state: directory
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: '0755'
- name: Check if docker-compose.yml exists in application stack
stat:
path: "{{ app_stack_path }}/docker-compose.yml"
register: compose_file_exists
- name: Fail if docker-compose.yml doesn't exist
fail:
msg: |
Application Stack docker-compose.yml not found at {{ app_stack_path }}/docker-compose.yml
The Application Stack must be deployed first via:
ansible-playbook -i inventory/production.yml playbooks/setup-infrastructure.yml
This will create the application stack with docker-compose.yml and .env file.
when: not compose_file_exists.stat.exists
- name: Create backup directory
file:
path: "{{ backups_path }}/{{ deployment_timestamp | regex_replace(':', '-') }}"
state: directory
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: '0755'
tasks:
- name: Verify docker-compose.yml exists
stat:
path: "{{ app_stack_path }}/docker-compose.yml"
register: compose_file_check
- name: Fail if docker-compose.yml doesn't exist
fail:
msg: |
Application Stack docker-compose.yml not found at {{ app_stack_path }}/docker-compose.yml
The Application Stack must be deployed first via:
ansible-playbook -i inventory/production.yml playbooks/setup-infrastructure.yml
This will create the application stack with docker-compose.yml and .env file.
when: not compose_file_check.stat.exists
- name: Backup current deployment metadata
shell: |
docker compose -f {{ app_stack_path }}/docker-compose.yml ps --format json 2>/dev/null > {{ backups_path }}/{{ deployment_timestamp | regex_replace(':', '-') }}/current_containers.json || true
docker compose -f {{ app_stack_path }}/docker-compose.yml config 2>/dev/null > {{ backups_path }}/{{ deployment_timestamp | regex_replace(':', '-') }}/docker-compose-config.yml || true
args:
executable: /bin/bash
changed_when: false
ignore_errors: yes
when: compose_file_check.stat.exists
- name: Login to Docker registry (if credentials provided)
community.docker.docker_login:
registry_url: "{{ docker_registry_url }}"
username: "{{ docker_registry_username }}"
password: "{{ docker_registry_password }}"
no_log: yes
when:
- docker_registry_username is defined
- docker_registry_password is defined
- name: Pull new Docker image
community.docker.docker_image:
name: "{{ app_image }}"
tag: "{{ image_tag }}"
source: pull
force_source: yes
register: image_pull
- name: Verify image was pulled successfully
fail:
msg: "Failed to pull image {{ app_image }}:{{ image_tag }}"
when: image_pull.failed
- name: Update docker-compose.yml with new image tag (all services)
replace:
path: "{{ app_stack_path }}/docker-compose.yml"
regexp: '^(\s+image:\s+){{ app_image }}:.*$'
replace: '\1{{ app_image }}:{{ image_tag }}'
when: image_pull.changed or image_tag != 'latest'
register: compose_updated
- name: Restart application stack with new image
community.docker.docker_compose_v2:
project_src: "{{ app_stack_path }}"
state: present
pull: always
recreate: always
remove_orphans: yes
register: stack_deploy
when: image_pull.changed or compose_updated.changed
- name: Wait for services to be healthy
wait_for:
timeout: 60
changed_when: false
- name: Check container health status
shell: |
docker compose -f {{ app_stack_path }}/docker-compose.yml ps --format json | jq -r '.[] | select(.Health != "healthy" and .Health != "") | "\(.Name): \(.Health)"' || echo "All healthy or no health checks"
args:
executable: /bin/bash
register: health_status
changed_when: false
ignore_errors: yes
- name: Get deployed image information
shell: |
docker compose -f {{ app_stack_path }}/docker-compose.yml config | grep -E "^\s+image:" | head -1 | awk '{print $2}' || echo "unknown"
args:
executable: /bin/bash
register: deployed_image
changed_when: false
- name: Record deployment metadata
copy:
content: |
Deployment Timestamp: {{ deployment_timestamp }}
Git Commit: {{ git_commit_sha }}
Image Tag: {{ image_tag }}
Deployed Image: {{ deployed_image.stdout }}
Image Pull: {{ 'SUCCESS' if image_pull.changed else 'SKIPPED (already exists)' }}
Stack Deploy: {{ 'UPDATED' if stack_deploy.changed else 'NO_CHANGE' }}
Health Status: {{ health_status.stdout if health_status.stdout != '' else 'All services healthy' }}
dest: "{{ backups_path }}/{{ deployment_timestamp | regex_replace(':', '-') }}/deployment_metadata.txt"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: '0644'
- name: Cleanup old backups (keep last {{ max_rollback_versions }})
shell: |
cd {{ backups_path }}
ls -dt */ 2>/dev/null | tail -n +{{ max_rollback_versions + 1 }} | xargs -r rm -rf
args:
executable: /bin/bash
changed_when: false
ignore_errors: yes
post_tasks:
- name: Display deployment summary
debug:
msg:
- "=== Deployment Summary ==="
- "Image: {{ app_image }}:{{ image_tag }}"
- "Commit: {{ git_commit_sha }}"
- "Timestamp: {{ deployment_timestamp }}"
- "Image Pull: {{ 'SUCCESS' if image_pull.changed else 'SKIPPED' }}"
- "Stack Deploy: {{ 'UPDATED' if stack_deploy.changed else 'NO_CHANGE' }}"
- "Health Check URL: {{ health_check_url }}"
- ""
- "Next: Verify application is healthy"