--- # Production Deployment Playbook f?r Semaphore # Deployed die Anwendung auf Production-Server - name: Deploy to Production hosts: production gather_facts: yes become: yes vars: registry_url: "{{ registry_url | default('registry.michaelschiemer.de') }}" image_name: "{{ image_name | default('framework') }}" image_tag: "{{ image_tag | default('latest') }}" registry_user: "{{ registry_user | default('admin') }}" registry_password: "{{ registry_password | required }}" deployment_path: "{{ deployment_path | default('~/deployment/stacks/application') }}" repo_url: "{{ repo_url | default('https://git.michaelschiemer.de/michael/michaelschiemer.git') }}" tasks: - name: Display deployment info debug: msg: - "?? Starting production deployment..." - "Host: {{ inventory_hostname }}" - "Registry: {{ registry_url }}" - "Image: {{ image_name }}:{{ image_tag }}" - "Path: {{ deployment_path }}" - name: Ensure deployment directory exists file: path: "{{ deployment_path }}" state: directory mode: '0755' - name: Login to Docker registry docker_login: username: "{{ registry_user }}" password: "{{ registry_password }}" registry_url: "{{ registry_url }}" - name: Pull Docker image docker_image: name: "{{ registry_url }}/{{ image_name }}:{{ image_tag }}" source: pull - name: Check if docker-compose files exist stat: path: "{{ deployment_path }}/docker-compose.base.yml" register: base_compose_exists - name: Check if docker-compose.production.yml exists stat: path: "{{ deployment_path }}/docker-compose.production.yml" register: production_compose_exists - name: Copy docker-compose files if missing copy: src: "{{ item.src }}" dest: "{{ deployment_path }}/{{ item.dest }}" mode: '0644' loop: - { src: "docker-compose.base.yml", dest: "docker-compose.base.yml" } - { src: "docker-compose.production.yml", dest: "docker-compose.production.yml" } when: not (item.dest == "docker-compose.base.yml" and base_compose_exists.stat.exists) or not (item.dest == "docker-compose.production.yml" and production_compose_exists.stat.exists) delegate_to: localhost become: no - name: Update docker-compose.production.yml with new image replace: path: "{{ deployment_path }}/docker-compose.production.yml" regexp: 'image:.*{{ image_name }}:.*' replace: 'image: {{ registry_url }}/{{ image_name }}:{{ image_tag }}' - name: Update docker-compose.production.yml with new image (alternative format) replace: path: "{{ deployment_path }}/docker-compose.production.yml" regexp: 'image:.*{{ image_name }}@.*' replace: 'image: {{ registry_url }}/{{ image_name }}:{{ image_tag }}' - name: Ensure Docker networks exist docker_network: name: traefik-public state: present ignore_errors: yes - name: Deploy services docker_compose_v2: project_src: "{{ deployment_path }}" files: - "{{ deployment_path }}/docker-compose.base.yml" - "{{ deployment_path }}/docker-compose.production.yml" pull: missing state: present recreate: always - name: Wait for services to start pause: seconds: 15 - name: Check container status command: docker compose -f {{ deployment_path }}/docker-compose.base.yml -f {{ deployment_path }}/docker-compose.production.yml ps register: container_status - name: Display container status debug: var: container_status.stdout_lines - name: Health check uri: url: https://michaelschiemer.de/health method: GET status_code: [200, 201, 204] validate_certs: no register: health_result retries: 10 delay: 10 until: health_result.status == 200 ignore_errors: yes - name: Display health check result debug: msg: "? Health check passed!" if health_result.status == 200 else "?? Health check failed" - name: Summary debug: msg: - "? Production deployment completed!" - "Image: {{ registry_url }}/{{ image_name }}:{{ image_tag }}" - "URL: https://michaelschiemer.de"