ci: setup CI/CD pipeline with Gitea Actions and secrets configuration
This commit is contained in:
@@ -1,14 +1,15 @@
|
||||
---
|
||||
- name: Deploy Application Update
|
||||
- name: Deploy Application Update via Docker Compose
|
||||
hosts: production
|
||||
gather_facts: yes
|
||||
become: 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
|
||||
@@ -29,12 +30,21 @@
|
||||
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: Create backup directory
|
||||
file:
|
||||
path: "{{ backups_path }}/{{ deployment_timestamp | regex_replace(':', '-') }}"
|
||||
@@ -46,16 +56,15 @@
|
||||
tasks:
|
||||
- name: Backup current deployment metadata
|
||||
shell: |
|
||||
docker service inspect {{ stack_name }}_app --format '{{ "{{" }}.Spec.TaskTemplate.ContainerSpec.Image{{ "}}" }}' \
|
||||
> {{ backups_path }}/{{ deployment_timestamp | regex_replace(':', '-') }}/current_image.txt || true
|
||||
docker stack ps {{ stack_name }} --format 'table {{ "{{" }}.Name{{ "}}" }}\t{{ "{{" }}.Image{{ "}}" }}\t{{ "{{" }}.CurrentState{{ "}}" }}' \
|
||||
> {{ backups_path }}/{{ deployment_timestamp | regex_replace(':', '-') }}/stack_status.txt || true
|
||||
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
|
||||
|
||||
- name: Login to Docker registry (if credentials provided)
|
||||
docker_login:
|
||||
community.docker.docker_login:
|
||||
registry_url: "{{ docker_registry_url }}"
|
||||
username: "{{ docker_registry_username }}"
|
||||
password: "{{ docker_registry_password }}"
|
||||
@@ -65,43 +74,55 @@
|
||||
- docker_registry_password is defined
|
||||
|
||||
- name: Pull new Docker image
|
||||
docker_image:
|
||||
community.docker.docker_image:
|
||||
name: "{{ app_image }}"
|
||||
tag: "{{ image_tag }}"
|
||||
source: pull
|
||||
force_source: yes
|
||||
register: image_pull
|
||||
|
||||
- name: Update docker-compose.prod.yml with new image tag
|
||||
lineinfile:
|
||||
path: "{{ compose_file }}"
|
||||
- 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 }}:.*$'
|
||||
line: '\1{{ app_image }}:{{ image_tag }}'
|
||||
backrefs: yes
|
||||
when: compose_file is file
|
||||
replace: '\1{{ app_image }}:{{ image_tag }}'
|
||||
when: image_pull.changed or image_tag != 'latest'
|
||||
register: compose_updated
|
||||
|
||||
- name: Deploy stack update
|
||||
docker_stack:
|
||||
name: "{{ stack_name }}"
|
||||
compose:
|
||||
- "{{ compose_file }}"
|
||||
- name: Restart application stack with new image
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ app_stack_path }}"
|
||||
state: present
|
||||
prune: yes
|
||||
pull: always
|
||||
recreate: always
|
||||
remove_orphans: yes
|
||||
register: stack_deploy
|
||||
when: image_pull.changed or compose_updated.changed
|
||||
|
||||
- name: Wait for service to be updated
|
||||
command: >
|
||||
docker service ps {{ stack_name }}_app
|
||||
--filter "desired-state=running"
|
||||
--format '{{ "{{" }}.CurrentState{{ "}}" }}'
|
||||
register: service_status
|
||||
until: "'Running' in service_status.stdout"
|
||||
retries: 30
|
||||
delay: 10
|
||||
- name: Wait for services to be healthy
|
||||
wait_for:
|
||||
timeout: 60
|
||||
changed_when: false
|
||||
|
||||
- name: Get updated service info
|
||||
command: docker service inspect {{ stack_name }}_app --format '{{ "{{" }}.Spec.TaskTemplate.ContainerSpec.Image{{ "}}" }}'
|
||||
- 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
|
||||
|
||||
@@ -112,7 +133,9 @@
|
||||
Git Commit: {{ git_commit_sha }}
|
||||
Image Tag: {{ image_tag }}
|
||||
Deployed Image: {{ deployed_image.stdout }}
|
||||
Stack Deploy Output: {{ stack_deploy }}
|
||||
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 }}"
|
||||
@@ -121,17 +144,22 @@
|
||||
- name: Cleanup old backups (keep last {{ max_rollback_versions }})
|
||||
shell: |
|
||||
cd {{ backups_path }}
|
||||
ls -t | tail -n +{{ max_rollback_versions + 1 }} | xargs -r rm -rf
|
||||
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 completed successfully!"
|
||||
- "=== Deployment Summary ==="
|
||||
- "Image: {{ app_image }}:{{ image_tag }}"
|
||||
- "Commit: {{ git_commit_sha }}"
|
||||
- "Timestamp: {{ deployment_timestamp }}"
|
||||
- "Health check URL: {{ health_check_url }}"
|
||||
- "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"
|
||||
|
||||
Reference in New Issue
Block a user