212 lines
7.4 KiB
YAML
212 lines
7.4 KiB
YAML
---
|
|
# =============================================================================
|
|
# Docker Deployment with Infisical Secrets
|
|
# =============================================================================
|
|
# This playbook demonstrates best practices from Virgo-Core:
|
|
# - Infisical secrets management (using reusable task)
|
|
# - Proper error handling with changed_when/failed_when
|
|
# - Idempotent command execution
|
|
# - No secrets in logs (no_log: true)
|
|
# - Fully qualified module names (FQCN)
|
|
# - Task organization with blocks
|
|
|
|
- name: Deploy Docker application with secrets from Infisical
|
|
hosts: docker_hosts
|
|
become: true
|
|
gather_facts: true
|
|
|
|
vars:
|
|
app_name: "my-application"
|
|
app_dir: "/opt/{{ app_name }}"
|
|
infisical_project_id: "7b832220-24c0-45bc-a5f1-ce9794a31259"
|
|
infisical_env: "prod"
|
|
infisical_path: "/doggos-cluster"
|
|
|
|
# ==========================================================================
|
|
# Pre-flight Checks
|
|
# ==========================================================================
|
|
|
|
pre_tasks:
|
|
- name: Validate required variables
|
|
ansible.builtin.assert:
|
|
that:
|
|
- app_name is defined and app_name | length > 0
|
|
- app_dir is defined
|
|
- infisical_project_id is defined
|
|
fail_msg: "Required variables not set"
|
|
success_msg: "All required variables present"
|
|
tags: [always]
|
|
|
|
- name: Check if Docker is installed
|
|
ansible.builtin.command: which docker
|
|
register: docker_check
|
|
changed_when: false
|
|
failed_when: false
|
|
tags: [always]
|
|
|
|
- name: Fail if Docker not installed
|
|
ansible.builtin.fail:
|
|
msg: |
|
|
Docker is not installed on {{ inventory_hostname }}
|
|
Please install Docker first: sudo apt install docker.io
|
|
when: docker_check.rc != 0
|
|
tags: [always]
|
|
|
|
# ==========================================================================
|
|
# Main Tasks
|
|
# ==========================================================================
|
|
|
|
tasks:
|
|
# ========================================================================
|
|
# Retrieve Secrets from Infisical
|
|
# ========================================================================
|
|
|
|
- name: Secrets Management Block
|
|
block:
|
|
- name: Retrieve database password from Infisical
|
|
ansible.builtin.include_tasks: ../../tasks/infisical-secret-lookup.yml
|
|
vars:
|
|
secret_name: 'DB_PASSWORD'
|
|
secret_var_name: 'db_password'
|
|
fallback_env_var: 'DB_PASSWORD' # Optional fallback
|
|
|
|
- name: Retrieve API key from Infisical
|
|
ansible.builtin.include_tasks: ../../tasks/infisical-secret-lookup.yml
|
|
vars:
|
|
secret_name: 'API_KEY'
|
|
secret_var_name: 'api_key'
|
|
fallback_env_var: 'API_KEY'
|
|
|
|
- name: Retrieve Redis password from Infisical
|
|
ansible.builtin.include_tasks: ../../tasks/infisical-secret-lookup.yml
|
|
vars:
|
|
secret_name: 'REDIS_PASSWORD'
|
|
secret_var_name: 'redis_password'
|
|
fallback_env_var: 'REDIS_PASSWORD'
|
|
|
|
tags: [secrets, config]
|
|
|
|
# ========================================================================
|
|
# Application Setup
|
|
# ========================================================================
|
|
|
|
- name: Application Deployment Block
|
|
block:
|
|
- name: Create application directory
|
|
ansible.builtin.file:
|
|
path: "{{ app_dir }}"
|
|
state: directory
|
|
owner: root
|
|
group: root
|
|
mode: '0755'
|
|
|
|
- name: Deploy application configuration
|
|
ansible.builtin.template:
|
|
src: app-config.yml.j2
|
|
dest: "{{ app_dir }}/config.yml"
|
|
owner: root
|
|
group: root
|
|
mode: '0600' # Secure permissions for config with secrets
|
|
notify: Restart application
|
|
no_log: true # Config contains secrets
|
|
|
|
- name: Deploy Docker Compose file
|
|
ansible.builtin.template:
|
|
src: docker-compose.yml.j2
|
|
dest: "{{ app_dir }}/docker-compose.yml"
|
|
owner: root
|
|
group: root
|
|
mode: '0644'
|
|
|
|
rescue:
|
|
- name: Report deployment failure
|
|
ansible.builtin.fail:
|
|
msg: "Failed to deploy application configuration"
|
|
|
|
tags: [deploy, config]
|
|
|
|
# ========================================================================
|
|
# Docker Operations (with proper idempotency)
|
|
# ========================================================================
|
|
|
|
- name: Docker Management Block
|
|
block:
|
|
- name: Check if container is already running
|
|
ansible.builtin.command: docker ps --filter name={{ app_name }} --format "{{ '{{' }}.Names{{ '}}' }}"
|
|
register: container_check
|
|
changed_when: false
|
|
failed_when: false
|
|
|
|
- name: Pull Docker images
|
|
ansible.builtin.command: docker-compose -f {{ app_dir }}/docker-compose.yml pull
|
|
args:
|
|
chdir: "{{ app_dir }}"
|
|
register: pull_result
|
|
changed_when: "'Downloaded newer image' in pull_result.stdout"
|
|
when: container_check.stdout != app_name
|
|
|
|
- name: Start Docker containers
|
|
ansible.builtin.command: docker-compose -f {{ app_dir }}/docker-compose.yml up -d
|
|
args:
|
|
chdir: "{{ app_dir }}"
|
|
register: compose_up
|
|
changed_when: "'Creating' in compose_up.stderr or 'Starting' in compose_up.stderr"
|
|
when: container_check.stdout != app_name
|
|
|
|
- name: Wait for application to be healthy
|
|
ansible.builtin.uri:
|
|
url: "http://localhost:8080/health"
|
|
status_code: 200
|
|
register: health_check
|
|
until: health_check.status == 200
|
|
retries: 30
|
|
delay: 10
|
|
changed_when: false
|
|
|
|
rescue:
|
|
- name: Show container logs on failure
|
|
ansible.builtin.command: docker-compose -f {{ app_dir }}/docker-compose.yml logs --tail=50
|
|
args:
|
|
chdir: "{{ app_dir }}"
|
|
register: container_logs
|
|
changed_when: false
|
|
|
|
- name: Report Docker failure
|
|
ansible.builtin.fail:
|
|
msg: |
|
|
Docker deployment failed
|
|
Logs: {{ container_logs.stdout }}
|
|
|
|
tags: [deploy, docker]
|
|
|
|
# ========================================================================
|
|
# Verification
|
|
# ========================================================================
|
|
|
|
- name: Verify application is running
|
|
ansible.builtin.command: docker ps --filter name={{ app_name }} --filter status=running --format "{{ '{{' }}.Status{{ '}}' }}"
|
|
register: running_check
|
|
changed_when: false
|
|
failed_when: "'Up' not in running_check.stdout"
|
|
tags: [verify]
|
|
|
|
- name: Report deployment success
|
|
ansible.builtin.debug:
|
|
msg: |
|
|
✓ Application deployed successfully
|
|
Container: {{ app_name }}
|
|
Status: {{ running_check.stdout }}
|
|
Health endpoint: http://{{ inventory_hostname }}:8080/health
|
|
tags: [verify]
|
|
|
|
# ==========================================================================
|
|
# Handlers
|
|
# ==========================================================================
|
|
|
|
handlers:
|
|
- name: Restart application
|
|
ansible.builtin.command: docker-compose -f {{ app_dir }}/docker-compose.yml restart
|
|
args:
|
|
chdir: "{{ app_dir }}"
|
|
changed_when: true
|