- Create AnsibleDeployStage using framework's Process module for secure command execution - Integrate AnsibleDeployStage into DeploymentPipelineCommands for production deployments - Add force_deploy flag support in Ansible playbook to override stale locks - Use PHP deployment module as orchestrator (php console.php deploy:production) - Fix ErrorAggregationInitializer to use Environment class instead of $_ENV superglobal Architecture: - BuildStage → AnsibleDeployStage → HealthCheckStage for production - Process module provides timeout, error handling, and output capture - Ansible playbook supports rollback via rollback-git-based.yml - Zero-downtime deployments with health checks
143 lines
4.1 KiB
YAML
143 lines
4.1 KiB
YAML
---
|
|
# Git-based Rollback Playbook
|
|
# Rolls back to the previous release by switching the symlink
|
|
#
|
|
# Usage:
|
|
# ansible-playbook -i inventories/production/hosts.yml playbooks/rollback-git-based.yml
|
|
# ansible-playbook -i inventories/production/hosts.yml playbooks/rollback-git-based.yml --extra-vars "rollback_to=20241025123456"
|
|
|
|
- name: Rollback Custom PHP Framework (Git-based)
|
|
hosts: web_servers
|
|
become: true
|
|
|
|
vars:
|
|
app_name: michaelschiemer
|
|
app_user: deploy
|
|
app_group: deploy
|
|
app_base_path: "/var/www/{{ app_name }}"
|
|
releases_path: "{{ app_base_path }}/releases"
|
|
current_path: "{{ app_base_path }}/current"
|
|
|
|
pre_tasks:
|
|
- name: Check if deployment lock exists
|
|
stat:
|
|
path: "{{ app_base_path }}/.deploy.lock"
|
|
register: deploy_lock
|
|
|
|
- name: Fail if deployment is in progress
|
|
fail:
|
|
msg: "Cannot rollback - deployment in progress"
|
|
when: deploy_lock.stat.exists
|
|
|
|
- name: Create rollback lock
|
|
file:
|
|
path: "{{ app_base_path }}/.rollback.lock"
|
|
state: touch
|
|
owner: "{{ app_user }}"
|
|
group: "{{ app_group }}"
|
|
|
|
tasks:
|
|
- name: Get current release
|
|
stat:
|
|
path: "{{ current_path }}"
|
|
register: current_release
|
|
|
|
- name: Fail if no current release exists
|
|
fail:
|
|
msg: "No current release found at {{ current_path }}"
|
|
when: not current_release.stat.exists
|
|
|
|
- name: Get list of all releases
|
|
find:
|
|
paths: "{{ releases_path }}"
|
|
file_type: directory
|
|
register: all_releases
|
|
|
|
- name: Sort releases by creation time (newest first)
|
|
set_fact:
|
|
sorted_releases: "{{ all_releases.files | sort(attribute='ctime', reverse=true) }}"
|
|
|
|
- name: Determine target release for rollback
|
|
set_fact:
|
|
target_release: "{{ rollback_to if rollback_to is defined else sorted_releases[1].path }}"
|
|
|
|
- name: Verify target release exists
|
|
stat:
|
|
path: "{{ target_release }}"
|
|
register: target_release_stat
|
|
|
|
- name: Fail if target release doesn't exist
|
|
fail:
|
|
msg: "Target release not found: {{ target_release }}"
|
|
when: not target_release_stat.stat.exists
|
|
|
|
- name: Display rollback information
|
|
debug:
|
|
msg:
|
|
- "Current release: {{ current_release.stat.lnk_source }}"
|
|
- "Rolling back to: {{ target_release }}"
|
|
|
|
- name: Switch symlink to previous release
|
|
file:
|
|
src: "{{ target_release }}"
|
|
dest: "{{ current_path }}"
|
|
state: link
|
|
owner: "{{ app_user }}"
|
|
group: "{{ app_group }}"
|
|
force: yes
|
|
|
|
- name: Wait for application to be ready
|
|
wait_for:
|
|
timeout: 5
|
|
delegate_to: localhost
|
|
|
|
- name: Health check after rollback
|
|
uri:
|
|
url: "http://{{ ansible_host }}/health/summary"
|
|
method: GET
|
|
return_content: yes
|
|
status_code: 200
|
|
register: health_check
|
|
retries: 3
|
|
delay: 5
|
|
until: health_check.status == 200
|
|
ignore_errors: yes
|
|
|
|
- name: Log rollback
|
|
lineinfile:
|
|
path: "{{ app_base_path }}/deploy.log"
|
|
line: "[{{ ansible_date_time.iso8601 }}] ROLLBACK: {{ current_release.stat.lnk_source }} -> {{ target_release }} - Health: {{ health_check.status | default('FAILED') }}"
|
|
create: yes
|
|
|
|
- name: Display rollback result
|
|
debug:
|
|
msg:
|
|
- "=========================================="
|
|
- "Rollback completed"
|
|
- "Previous: {{ current_release.stat.lnk_source }}"
|
|
- "Current: {{ target_release }}"
|
|
- "Health check: {{ health_check.status | default('FAILED') }}"
|
|
- "=========================================="
|
|
|
|
post_tasks:
|
|
- name: Remove rollback lock
|
|
file:
|
|
path: "{{ app_base_path }}/.rollback.lock"
|
|
state: absent
|
|
|
|
rescue:
|
|
- name: Remove rollback lock on failure
|
|
file:
|
|
path: "{{ app_base_path }}/.rollback.lock"
|
|
state: absent
|
|
|
|
- name: Log rollback failure
|
|
lineinfile:
|
|
path: "{{ app_base_path }}/deploy.log"
|
|
line: "[{{ ansible_date_time.iso8601 }}] ROLLBACK FAILED"
|
|
create: yes
|
|
|
|
- name: Fail with error message
|
|
fail:
|
|
msg: "Rollback failed"
|