chore: sync staging workspace
This commit is contained in:
@@ -6,21 +6,35 @@ This directory contains Ansible playbooks and configuration for deploying the Cu
|
||||
|
||||
```
|
||||
deployment/ansible/
|
||||
├── ansible.cfg # Ansible configuration
|
||||
├── ansible.cfg # Ansible configuration
|
||||
├── inventory/
|
||||
│ └── production.yml # Production server inventory
|
||||
│ ├── production.yml # Production server inventory
|
||||
│ └── local.yml # Local testing inventory
|
||||
├── playbooks/
|
||||
│ ├── setup-production-secrets.yml # Deploy secrets
|
||||
│ ├── deploy-update.yml # Deploy application updates
|
||||
│ ├── rollback.yml # Rollback deployments
|
||||
│ ├── setup-infrastructure.yml # Provision core stacks
|
||||
│ ├── setup-production-secrets.yml # Deploy secrets
|
||||
│ ├── setup-wireguard.yml # Setup WireGuard VPN server
|
||||
│ ├── add-wireguard-client.yml # Add WireGuard client
|
||||
│ ├── sync-code.yml # Git-based code sync
|
||||
│ └── README-WIREGUARD.md # WireGuard documentation
|
||||
├── scripts/ # Helper scripts for secrets & credentials
|
||||
├── roles/ # Reusable roles (e.g. application stack)
|
||||
├── secrets/
|
||||
│ ├── .gitignore # Prevent committing secrets
|
||||
│ └── production.vault.yml.example # Example vault file
|
||||
└── templates/
|
||||
└── .env.production.j2 # Environment file template
|
||||
├── application.env.j2 # Application stack environment
|
||||
├── gitea-app.ini.j2 # Gitea configuration
|
||||
├── minio.env.j2 # MinIO environment
|
||||
├── monitoring.env.j2 # Monitoring stack environment
|
||||
├── wireguard-client.conf.j2 # WireGuard client config
|
||||
└── wireguard-server.conf.j2 # WireGuard server config
|
||||
|
||||
## Roles
|
||||
|
||||
Stack-spezifische Aufgaben liegen in `roles/` (z. B. `application`, `traefik`, `registry`). Playbooks wie `setup-infrastructure.yml` importieren diese Rollen direkt. Die Application-Rolle kann mit Variablen wie `application_sync_files=false` oder `application_compose_recreate="always"` konfiguriert werden (siehe `playbooks/deploy-update.yml` als Beispiel).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
@@ -37,6 +51,7 @@ deployment/ansible/
|
||||
- Create `.vault_pass` file in `secrets/` directory
|
||||
- Add vault password to this file (one line)
|
||||
- File is gitignored for security
|
||||
- 📖 **Detaillierte Dokumentation:** [docs/guides/vault-password.md](../docs/guides/vault-password.md)
|
||||
|
||||
## Setup Instructions
|
||||
|
||||
@@ -66,6 +81,8 @@ echo "your-vault-password-here" > secrets/.vault_pass
|
||||
chmod 600 secrets/.vault_pass
|
||||
```
|
||||
|
||||
**📖 Für detaillierte Informationen:** Siehe [docs/guides/vault-password.md](../docs/guides/vault-password.md)
|
||||
|
||||
### 3. Configure SSH Key
|
||||
|
||||
```bash
|
||||
@@ -265,11 +282,12 @@ docker login registry.michaelschiemer.de
|
||||
# SSH to production server
|
||||
ssh -i ~/.ssh/production deploy@94.16.110.151
|
||||
|
||||
# Check Docker service logs
|
||||
docker service logs app_app
|
||||
# Check Docker Compose service logs
|
||||
docker compose -f {{ app_stack_path }}/docker-compose.yml logs app
|
||||
docker compose -f {{ app_stack_path }}/docker-compose.yml logs nginx
|
||||
|
||||
# Check stack status
|
||||
docker stack ps app
|
||||
docker compose -f {{ app_stack_path }}/docker-compose.yml ps
|
||||
```
|
||||
|
||||
## CI/CD Integration
|
||||
@@ -283,19 +301,19 @@ Vault password is stored as Gitea Actions secret: `ANSIBLE_VAULT_PASSWORD`
|
||||
|
||||
## Inventory Variables
|
||||
|
||||
All deployment variables are defined in `inventory/production.yml`:
|
||||
All zentralen Variablen werden in `group_vars/production.yml` gepflegt und können bei Bedarf im Inventory überschrieben werden. Häufig verwendete Werte:
|
||||
|
||||
| Variable | Description | Default |
|
||||
|----------|-------------|---------|
|
||||
| `docker_registry` | Docker registry URL | registry.michaelschiemer.de |
|
||||
| `app_name` | Application name | framework |
|
||||
| `app_domain` | Production domain | michaelschiemer.de |
|
||||
| `stack_name` | Docker stack name | app |
|
||||
| `compose_file` | Docker Compose file path | /home/deploy/docker-compose.prod.yml |
|
||||
| `secrets_path` | Secrets directory | /home/deploy/secrets |
|
||||
| `backups_path` | Backups directory | /home/deploy/backups |
|
||||
| `max_rollback_versions` | Backup retention | 5 |
|
||||
| `health_check_url` | Health check endpoint | https://michaelschiemer.de/health |
|
||||
| Variable | Beschreibung | Standardwert |
|
||||
|----------|--------------|--------------|
|
||||
| `deploy_user_home` | Home-Verzeichnis des Deploy-Users | `/home/deploy` |
|
||||
| `stacks_base_path` | Basispfad für Docker Compose Stacks | `/home/deploy/deployment/stacks` |
|
||||
| `app_stack_path` | Pfad zum Application Stack | `/home/deploy/deployment/stacks/application` |
|
||||
| `backups_path` | Ablageort für Deployment-Backups | `/home/deploy/deployment/backups` |
|
||||
| `docker_registry` | Interner Registry-Endpunkt (lokal) | `localhost:5000` |
|
||||
| `docker_registry_external` | Externer Registry-Endpunkt | `registry.michaelschiemer.de` |
|
||||
| `app_domain` | Produktions-Domain | `michaelschiemer.de` |
|
||||
| `health_check_url` | Health-Check Endpoint | `https://michaelschiemer.de/health` |
|
||||
| `max_rollback_versions` | Anzahl vorgehaltener Backups | `5` |
|
||||
|
||||
## Backup Management
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ gathering = smart
|
||||
fact_caching = jsonfile
|
||||
fact_caching_connection = /tmp/ansible_facts
|
||||
fact_caching_timeout = 3600
|
||||
roles_path = roles
|
||||
|
||||
[ssh_connection]
|
||||
pipelining = True
|
||||
|
||||
@@ -15,6 +15,7 @@ docker_registry_external: "registry.michaelschiemer.de"
|
||||
docker_registry_username_default: "admin"
|
||||
# docker_registry_password_default should be set in vault as vault_docker_registry_password
|
||||
# If not using vault, override via -e docker_registry_password_default="your-password"
|
||||
docker_registry_password_default: ""
|
||||
registry_auth_path: "{{ stacks_base_path }}/registry/auth"
|
||||
|
||||
# Application Configuration
|
||||
|
||||
66
deployment/ansible/playbooks/check-staging-logs.yml
Normal file
66
deployment/ansible/playbooks/check-staging-logs.yml
Normal file
@@ -0,0 +1,66 @@
|
||||
---
|
||||
- name: Check Staging Container Logs
|
||||
hosts: production
|
||||
gather_facts: yes
|
||||
become: no
|
||||
|
||||
tasks:
|
||||
- name: Check staging container status
|
||||
shell: |
|
||||
cd ~/deployment/stacks/staging && docker compose ps
|
||||
args:
|
||||
executable: /bin/bash
|
||||
register: container_status
|
||||
changed_when: false
|
||||
|
||||
- name: Get staging-app logs
|
||||
shell: |
|
||||
cd ~/deployment/stacks/staging && docker compose logs --tail=100 staging-app
|
||||
args:
|
||||
executable: /bin/bash
|
||||
register: staging_app_logs
|
||||
changed_when: false
|
||||
|
||||
- name: Get staging-nginx logs
|
||||
shell: |
|
||||
cd ~/deployment/stacks/staging && docker compose logs --tail=100 staging-nginx
|
||||
args:
|
||||
executable: /bin/bash
|
||||
register: staging_nginx_logs
|
||||
changed_when: false
|
||||
|
||||
- name: Get staging-queue-worker logs
|
||||
shell: |
|
||||
cd ~/deployment/stacks/staging && docker compose logs --tail=100 staging-queue-worker
|
||||
args:
|
||||
executable: /bin/bash
|
||||
register: staging_queue_logs
|
||||
changed_when: false
|
||||
|
||||
- name: Get staging-scheduler logs
|
||||
shell: |
|
||||
cd ~/deployment/stacks/staging && docker compose logs --tail=100 staging-scheduler
|
||||
args:
|
||||
executable: /bin/bash
|
||||
register: staging_scheduler_logs
|
||||
changed_when: false
|
||||
|
||||
- name: Display container status
|
||||
debug:
|
||||
msg: "{{ container_status.stdout_lines }}"
|
||||
|
||||
- name: Display staging-app logs
|
||||
debug:
|
||||
msg: "{{ staging_app_logs.stdout_lines }}"
|
||||
|
||||
- name: Display staging-nginx logs
|
||||
debug:
|
||||
msg: "{{ staging_nginx_logs.stdout_lines }}"
|
||||
|
||||
- name: Display staging-queue-worker logs
|
||||
debug:
|
||||
msg: "{{ staging_queue_logs.stdout_lines }}"
|
||||
|
||||
- name: Display staging-scheduler logs
|
||||
debug:
|
||||
msg: "{{ staging_scheduler_logs.stdout_lines }}"
|
||||
@@ -133,31 +133,13 @@
|
||||
when: true
|
||||
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
|
||||
# Always restart when deploying, even if image already exists
|
||||
# This ensures code changes are applied even with same image tag
|
||||
when: true
|
||||
|
||||
- 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: Redeploy application stack with new image
|
||||
import_role:
|
||||
name: application
|
||||
vars:
|
||||
application_sync_files: false
|
||||
application_compose_recreate: "always"
|
||||
application_remove_orphans: true
|
||||
|
||||
- name: Get deployed image information
|
||||
shell: |
|
||||
@@ -175,8 +157,9 @@
|
||||
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' }}
|
||||
Stack Deploy: {{ 'UPDATED' if application_stack_changed else 'NO_CHANGE' }}
|
||||
Health Status: {{ application_health_output if application_health_output != '' else 'All services healthy' }}
|
||||
Health Check HTTP Status: {{ application_healthcheck_status }}
|
||||
dest: "{{ backups_path }}/{{ deployment_timestamp | regex_replace(':', '-') }}/deployment_metadata.txt"
|
||||
owner: "{{ ansible_user }}"
|
||||
group: "{{ ansible_user }}"
|
||||
@@ -200,7 +183,9 @@
|
||||
- "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' }}"
|
||||
- "Stack Deploy: {{ 'UPDATED' if application_stack_changed else 'NO_CHANGE' }}"
|
||||
- "Health Output: {{ application_health_output if application_health_output != '' else 'All services healthy' }}"
|
||||
- "Health Check HTTP Status: {{ application_healthcheck_status }}"
|
||||
- "Health Check URL: {{ health_check_url }}"
|
||||
- ""
|
||||
- "Next: Verify application is healthy"
|
||||
|
||||
96
deployment/ansible/playbooks/diagnose-staging-502.yml
Normal file
96
deployment/ansible/playbooks/diagnose-staging-502.yml
Normal file
@@ -0,0 +1,96 @@
|
||||
---
|
||||
- name: Diagnose Staging 502 Error
|
||||
hosts: production
|
||||
gather_facts: yes
|
||||
become: no
|
||||
|
||||
tasks:
|
||||
- name: Check Nginx error logs
|
||||
shell: |
|
||||
cd ~/deployment/stacks/staging && docker compose logs --tail=50 staging-nginx 2>&1 | grep -iE "(error|502|bad gateway|php|fpm)" || echo "No errors found in nginx logs"
|
||||
args:
|
||||
executable: /bin/bash
|
||||
register: nginx_errors
|
||||
changed_when: false
|
||||
|
||||
- name: Check PHP-FPM status in staging-app
|
||||
shell: |
|
||||
cd ~/deployment/stacks/staging && docker compose exec -T staging-app php-fpm-healthcheck 2>&1 || echo "PHP-FPM healthcheck failed"
|
||||
args:
|
||||
executable: /bin/bash
|
||||
register: php_fpm_status
|
||||
changed_when: false
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Test PHP-FPM connection from nginx container
|
||||
shell: |
|
||||
cd ~/deployment/stacks/staging && docker compose exec -T staging-nginx sh -c "nc -zv staging-app 9000 2>&1 || echo 'Connection test failed'" || echo "Connection test failed"
|
||||
args:
|
||||
executable: /bin/bash
|
||||
register: connection_test
|
||||
changed_when: false
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Check if PHP-FPM is listening
|
||||
shell: |
|
||||
cd ~/deployment/stacks/staging && docker compose exec -T staging-app sh -c "netstat -tlnp 2>/dev/null | grep 9000 || ss -tlnp 2>/dev/null | grep 9000 || echo 'Port 9000 not found'" || echo "Could not check ports"
|
||||
args:
|
||||
executable: /bin/bash
|
||||
register: php_fpm_port
|
||||
changed_when: false
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Check staging-app process list
|
||||
shell: |
|
||||
cd ~/deployment/stacks/staging && docker compose exec -T staging-app sh -c "ps aux | grep -E '(php-fpm|nginx)' | head -20" || echo "Could not get process list"
|
||||
args:
|
||||
executable: /bin/bash
|
||||
register: processes
|
||||
changed_when: false
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Check network connectivity
|
||||
shell: |
|
||||
cd ~/deployment/stacks/staging && docker compose exec -T staging-nginx sh -c "ping -c 2 staging-app 2>&1 || echo 'Ping failed'" || echo "Network test failed"
|
||||
args:
|
||||
executable: /bin/bash
|
||||
register: network_test
|
||||
changed_when: false
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Check if /var/www/html/public/index.php exists
|
||||
shell: |
|
||||
cd ~/deployment/stacks/staging && docker compose exec -T staging-nginx sh -c "test -f /var/www/html/public/index.php && echo 'index.php exists' || echo 'index.php NOT FOUND'" || echo "Could not check file"
|
||||
args:
|
||||
executable: /bin/bash
|
||||
register: index_php_check
|
||||
changed_when: false
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Display Nginx error logs
|
||||
debug:
|
||||
msg: "{{ nginx_errors.stdout_lines }}"
|
||||
|
||||
- name: Display PHP-FPM status
|
||||
debug:
|
||||
msg: "{{ php_fpm_status.stdout_lines }}"
|
||||
|
||||
- name: Display connection test
|
||||
debug:
|
||||
msg: "{{ connection_test.stdout_lines }}"
|
||||
|
||||
- name: Display PHP-FPM port check
|
||||
debug:
|
||||
msg: "{{ php_fpm_port.stdout_lines }}"
|
||||
|
||||
- name: Display processes
|
||||
debug:
|
||||
msg: "{{ processes.stdout_lines }}"
|
||||
|
||||
- name: Display network test
|
||||
debug:
|
||||
msg: "{{ network_test.stdout_lines }}"
|
||||
|
||||
- name: Display index.php check
|
||||
debug:
|
||||
msg: "{{ index_php_check.stdout_lines }}"
|
||||
71
deployment/ansible/playbooks/fix-staging-complete.yml
Normal file
71
deployment/ansible/playbooks/fix-staging-complete.yml
Normal file
@@ -0,0 +1,71 @@
|
||||
---
|
||||
- name: Complete Fix for Staging (502 + Git)
|
||||
hosts: production
|
||||
gather_facts: yes
|
||||
become: no
|
||||
|
||||
tasks:
|
||||
- name: Check if index.php exists in staging-app
|
||||
shell: |
|
||||
cd ~/deployment/stacks/staging && docker compose exec -T staging-app sh -c "ls -la /var/www/html/public/index.php 2>&1" || echo "index.php NOT FOUND"
|
||||
args:
|
||||
executable: /bin/bash
|
||||
register: index_php_app
|
||||
changed_when: false
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Check if index.php exists in staging-nginx
|
||||
shell: |
|
||||
cd ~/deployment/stacks/staging && docker compose exec -T staging-nginx sh -c "ls -la /var/www/html/public/index.php 2>&1" || echo "index.php NOT FOUND"
|
||||
args:
|
||||
executable: /bin/bash
|
||||
register: index_php_nginx
|
||||
changed_when: false
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Check PHP-FPM listen configuration
|
||||
shell: |
|
||||
cd ~/deployment/stacks/staging && docker compose exec -T staging-app sh -c "cat /usr/local/etc/php-fpm.d/www.conf | grep -E '(^listen|^listen.allowed_clients|^listen.owner|^listen.group|^user|^group)' | head -20" || echo "Could not read config"
|
||||
args:
|
||||
executable: /bin/bash
|
||||
register: php_fpm_full_config
|
||||
changed_when: false
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Test actual HTTP request to staging-app
|
||||
shell: |
|
||||
cd ~/deployment/stacks/staging && docker compose exec -T staging-nginx sh -c "curl -v http://staging-app:9000/index.php 2>&1 | head -30" || echo "HTTP test failed"
|
||||
args:
|
||||
executable: /bin/bash
|
||||
register: http_test
|
||||
changed_when: false
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Fix Git ownership permanently in staging-app entrypoint
|
||||
shell: |
|
||||
cd ~/deployment/stacks/staging && docker compose exec -T staging-app sh -c "cd /var/www/html && git config --global --add safe.directory /var/www/html && git config --global --get-all safe.directory" || echo "Git config failed"
|
||||
args:
|
||||
executable: /bin/bash
|
||||
register: git_config_check
|
||||
changed_when: false
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Display index.php check in app
|
||||
debug:
|
||||
msg: "{{ index_php_app.stdout_lines }}"
|
||||
|
||||
- name: Display index.php check in nginx
|
||||
debug:
|
||||
msg: "{{ index_php_nginx.stdout_lines }}"
|
||||
|
||||
- name: Display PHP-FPM config
|
||||
debug:
|
||||
msg: "{{ php_fpm_full_config.stdout_lines }}"
|
||||
|
||||
- name: Display HTTP test
|
||||
debug:
|
||||
msg: "{{ http_test.stdout_lines }}"
|
||||
|
||||
- name: Display git config
|
||||
debug:
|
||||
msg: "{{ git_config_check.stdout_lines }}"
|
||||
83
deployment/ansible/playbooks/fix-staging-issues.yml
Normal file
83
deployment/ansible/playbooks/fix-staging-issues.yml
Normal file
@@ -0,0 +1,83 @@
|
||||
---
|
||||
- name: Fix Staging Issues (502 Error + Git Ownership)
|
||||
hosts: production
|
||||
gather_facts: yes
|
||||
become: no
|
||||
|
||||
tasks:
|
||||
- name: Get recent nginx error logs
|
||||
shell: |
|
||||
cd ~/deployment/stacks/staging && docker compose logs --tail=100 staging-nginx 2>&1
|
||||
args:
|
||||
executable: /bin/bash
|
||||
register: nginx_all_logs
|
||||
changed_when: false
|
||||
|
||||
- name: Test PHP-FPM connection with curl
|
||||
shell: |
|
||||
cd ~/deployment/stacks/staging && docker compose exec -T staging-nginx sh -c "curl -v http://staging-app:9000 2>&1 | head -20" || echo "Connection test completed"
|
||||
args:
|
||||
executable: /bin/bash
|
||||
register: curl_test
|
||||
changed_when: false
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Check PHP-FPM configuration
|
||||
shell: |
|
||||
cd ~/deployment/stacks/staging && docker compose exec -T staging-app sh -c "cat /usr/local/etc/php-fpm.d/www.conf | grep -E '(listen|listen.allowed_clients)' | head -10" || echo "Could not read PHP-FPM config"
|
||||
args:
|
||||
executable: /bin/bash
|
||||
register: php_fpm_config
|
||||
changed_when: false
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Fix Git ownership issue in staging-app
|
||||
shell: |
|
||||
cd ~/deployment/stacks/staging && docker compose exec -T staging-app sh -c "cd /var/www/html && git config --global --add safe.directory /var/www/html && echo 'Git safe.directory configured'" || echo "Git config failed"
|
||||
args:
|
||||
executable: /bin/bash
|
||||
register: git_fix
|
||||
changed_when: false
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Test if nginx can reach PHP-FPM
|
||||
shell: |
|
||||
cd ~/deployment/stacks/staging && docker compose exec -T staging-nginx sh -c "echo 'GET /index.php HTTP/1.0' | nc staging-app 9000 2>&1 | head -10" || docker compose exec -T staging-nginx sh -c "timeout 2 bash -c '</dev/tcp/staging-app/9000' && echo 'Port 9000 is reachable' || echo 'Port 9000 not reachable'" || echo "Could not test connection"
|
||||
args:
|
||||
executable: /bin/bash
|
||||
register: port_test
|
||||
changed_when: false
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Check if nginx can read public directory
|
||||
shell: |
|
||||
cd ~/deployment/stacks/staging && docker compose exec -T staging-nginx sh -c "ls -la /var/www/html/public/ | head -10" || echo "Could not list public directory"
|
||||
args:
|
||||
executable: /bin/bash
|
||||
register: public_dir_check
|
||||
changed_when: false
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Display all nginx logs
|
||||
debug:
|
||||
msg: "{{ nginx_all_logs.stdout_lines[-30:] }}"
|
||||
|
||||
- name: Display curl test
|
||||
debug:
|
||||
msg: "{{ curl_test.stdout_lines }}"
|
||||
|
||||
- name: Display PHP-FPM config
|
||||
debug:
|
||||
msg: "{{ php_fpm_config.stdout_lines }}"
|
||||
|
||||
- name: Display git fix result
|
||||
debug:
|
||||
msg: "{{ git_fix.stdout_lines }}"
|
||||
|
||||
- name: Display port test
|
||||
debug:
|
||||
msg: "{{ port_test.stdout_lines }}"
|
||||
|
||||
- name: Display public directory check
|
||||
debug:
|
||||
msg: "{{ public_dir_check.stdout_lines }}"
|
||||
@@ -41,284 +41,33 @@
|
||||
|
||||
# 1. Deploy Traefik (Reverse Proxy & SSL)
|
||||
- name: Deploy Traefik stack
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ stacks_base_path }}/traefik"
|
||||
state: present
|
||||
pull: always
|
||||
register: traefik_output
|
||||
|
||||
- name: Wait for Traefik to be ready
|
||||
wait_for:
|
||||
timeout: "{{ wait_timeout }}"
|
||||
when: traefik_output.changed
|
||||
|
||||
- name: Check Traefik logs for readiness hint
|
||||
shell: docker compose logs traefik 2>&1 | grep -Ei "(configuration loaded|traefik[[:space:]]v|Starting provider|Server configuration loaded)" || true
|
||||
args:
|
||||
chdir: "{{ stacks_base_path }}/traefik"
|
||||
register: traefik_logs
|
||||
until: traefik_logs.stdout != ""
|
||||
retries: 6
|
||||
delay: 10
|
||||
changed_when: false
|
||||
ignore_errors: yes
|
||||
import_role:
|
||||
name: traefik
|
||||
|
||||
# 2. Deploy PostgreSQL (Database)
|
||||
- name: Deploy PostgreSQL stack
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ stacks_base_path }}/postgresql"
|
||||
state: present
|
||||
pull: always
|
||||
register: postgres_output
|
||||
|
||||
- name: Wait for PostgreSQL to be ready
|
||||
wait_for:
|
||||
timeout: "{{ wait_timeout }}"
|
||||
when: postgres_output.changed
|
||||
|
||||
- name: Check PostgreSQL logs for readiness
|
||||
shell: docker compose logs postgres 2>&1 | grep -Ei "(ready to accept connections|database system is ready)" || true
|
||||
args:
|
||||
chdir: "{{ stacks_base_path }}/postgresql"
|
||||
register: postgres_logs
|
||||
until: postgres_logs.stdout != ""
|
||||
retries: 6
|
||||
delay: 10
|
||||
changed_when: false
|
||||
ignore_errors: yes
|
||||
import_role:
|
||||
name: postgresql
|
||||
|
||||
# 3. Deploy Docker Registry (Private Registry)
|
||||
- name: Ensure Registry auth directory exists
|
||||
file:
|
||||
path: "{{ registry_auth_path }}"
|
||||
state: directory
|
||||
mode: '0755'
|
||||
become: yes
|
||||
|
||||
- name: Optionally load registry credentials from vault
|
||||
include_vars:
|
||||
file: "{{ playbook_dir }}/../secrets/production.vault.yml"
|
||||
no_log: yes
|
||||
ignore_errors: yes
|
||||
delegate_to: localhost
|
||||
become: no
|
||||
|
||||
- name: Set registry credentials from vault or defaults
|
||||
set_fact:
|
||||
registry_username: "{{ vault_docker_registry_username | default(docker_registry_username_default) }}"
|
||||
registry_password: "{{ vault_docker_registry_password | default(docker_registry_password_default) }}"
|
||||
no_log: true
|
||||
|
||||
- name: Fail if registry password is not set
|
||||
fail:
|
||||
msg: "Registry password must be set in vault or docker_registry_password_default"
|
||||
when: registry_password is not defined or registry_password == ""
|
||||
|
||||
- name: Create Registry htpasswd file if missing
|
||||
shell: |
|
||||
if [ ! -f {{ registry_auth_path }}/htpasswd ]; then
|
||||
docker run --rm --entrypoint htpasswd httpd:2 -Bbn {{ registry_username }} {{ registry_password }} > {{ registry_auth_path }}/htpasswd
|
||||
chmod 644 {{ registry_auth_path }}/htpasswd
|
||||
fi
|
||||
args:
|
||||
executable: /bin/bash
|
||||
become: yes
|
||||
changed_when: true
|
||||
register: registry_auth_created
|
||||
no_log: true
|
||||
|
||||
- name: Deploy Docker Registry stack
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ stacks_base_path }}/registry"
|
||||
state: present
|
||||
pull: always
|
||||
register: registry_output
|
||||
|
||||
- name: Wait for Docker Registry to be ready
|
||||
wait_for:
|
||||
timeout: "{{ wait_timeout }}"
|
||||
when: registry_output.changed
|
||||
|
||||
- name: Check Registry logs for readiness
|
||||
shell: docker compose logs registry 2>&1 | grep -Ei "(listening on|listening at|http server)" || true
|
||||
args:
|
||||
chdir: "{{ stacks_base_path }}/registry"
|
||||
register: registry_logs
|
||||
until: registry_logs.stdout != ""
|
||||
retries: 6
|
||||
delay: 10
|
||||
changed_when: false
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Verify Registry is accessible
|
||||
uri:
|
||||
url: "http://127.0.0.1:5000/v2/_catalog"
|
||||
user: "{{ registry_username }}"
|
||||
password: "{{ registry_password }}"
|
||||
status_code: 200
|
||||
timeout: 5
|
||||
register: registry_check
|
||||
ignore_errors: yes
|
||||
changed_when: false
|
||||
no_log: true
|
||||
|
||||
- name: Display Registry status
|
||||
debug:
|
||||
msg: "Registry accessibility: {{ 'SUCCESS' if registry_check.status == 200 else 'FAILED - may need manual check' }}"
|
||||
import_role:
|
||||
name: registry
|
||||
|
||||
# 4. Deploy MinIO (Object Storage)
|
||||
- name: Optionally load MinIO secrets from vault
|
||||
include_vars:
|
||||
file: "{{ playbook_dir }}/../secrets/production.vault.yml"
|
||||
no_log: yes
|
||||
ignore_errors: yes
|
||||
delegate_to: localhost
|
||||
become: no
|
||||
|
||||
- name: Set MinIO root password from vault or generate
|
||||
set_fact:
|
||||
minio_password: "{{ vault_minio_root_password | default(lookup('password', '/dev/null length=32 chars=ascii_letters,digits,punctuation')) }}"
|
||||
no_log: yes
|
||||
|
||||
- name: Set MinIO root user from vault or use default
|
||||
set_fact:
|
||||
minio_user: "{{ vault_minio_root_user | default('minioadmin') }}"
|
||||
|
||||
- name: Ensure MinIO stack directory exists
|
||||
file:
|
||||
path: "{{ stacks_base_path }}/minio"
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: Create MinIO stack .env file
|
||||
template:
|
||||
src: "{{ playbook_dir }}/../templates/minio.env.j2"
|
||||
dest: "{{ stacks_base_path }}/minio/.env"
|
||||
owner: "{{ ansible_user }}"
|
||||
group: "{{ ansible_user }}"
|
||||
mode: '0600'
|
||||
vars:
|
||||
minio_root_user: "{{ minio_user }}"
|
||||
minio_root_password: "{{ minio_password }}"
|
||||
minio_api_domain: "{{ minio_api_domain }}"
|
||||
minio_console_domain: "{{ minio_console_domain }}"
|
||||
no_log: yes
|
||||
|
||||
- name: Deploy MinIO stack
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ stacks_base_path }}/minio"
|
||||
state: present
|
||||
pull: always
|
||||
register: minio_output
|
||||
|
||||
- name: Wait for MinIO to be ready
|
||||
wait_for:
|
||||
timeout: "{{ wait_timeout }}"
|
||||
when: minio_output.changed
|
||||
|
||||
- name: Check MinIO logs for readiness
|
||||
shell: docker compose logs minio 2>&1 | grep -Ei "(API:|WebUI:|MinIO Object Storage Server)" || true
|
||||
args:
|
||||
chdir: "{{ stacks_base_path }}/minio"
|
||||
register: minio_logs
|
||||
until: minio_logs.stdout != ""
|
||||
retries: 6
|
||||
delay: 10
|
||||
changed_when: false
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Verify MinIO health endpoint
|
||||
uri:
|
||||
url: "http://127.0.0.1:9000/minio/health/live"
|
||||
method: GET
|
||||
status_code: [200, 404, 502, 503]
|
||||
timeout: 5
|
||||
register: minio_health_check
|
||||
ignore_errors: yes
|
||||
changed_when: false
|
||||
|
||||
- name: Display MinIO status
|
||||
debug:
|
||||
msg: "MinIO health check: {{ 'SUCCESS' if minio_health_check.status == 200 else 'FAILED - Status: ' + (minio_health_check.status|string) }}"
|
||||
import_role:
|
||||
name: minio
|
||||
|
||||
# 5. Deploy Gitea (CRITICAL - Git Server + MySQL + Redis)
|
||||
- name: Deploy Gitea stack
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ stacks_base_path }}/gitea"
|
||||
state: present
|
||||
pull: always
|
||||
register: gitea_output
|
||||
|
||||
- name: Wait for Gitea to be ready
|
||||
wait_for:
|
||||
timeout: "{{ wait_timeout }}"
|
||||
when: gitea_output.changed
|
||||
|
||||
- name: Check Gitea logs for readiness
|
||||
shell: docker compose logs gitea 2>&1 | grep -Ei "(Listen:|Server is running|Starting server)" || true
|
||||
args:
|
||||
chdir: "{{ stacks_base_path }}/gitea"
|
||||
register: gitea_logs
|
||||
until: gitea_logs.stdout != ""
|
||||
retries: 12
|
||||
delay: 10
|
||||
changed_when: false
|
||||
ignore_errors: yes
|
||||
import_role:
|
||||
name: gitea
|
||||
|
||||
# 6. Deploy Monitoring (Portainer + Grafana + Prometheus)
|
||||
- name: Optionally load monitoring secrets from vault
|
||||
include_vars:
|
||||
file: "{{ playbook_dir }}/../secrets/production.vault.yml"
|
||||
no_log: yes
|
||||
ignore_errors: yes
|
||||
delegate_to: localhost
|
||||
become: no
|
||||
|
||||
- name: Set Grafana admin password from vault or generate
|
||||
set_fact:
|
||||
grafana_admin_password: "{{ vault_grafana_admin_password | default(lookup('password', '/dev/null length=25 chars=ascii_letters,digits')) }}"
|
||||
|
||||
- name: Set Prometheus password from vault or generate
|
||||
set_fact:
|
||||
prometheus_password: "{{ vault_prometheus_password | default(lookup('password', '/dev/null length=25 chars=ascii_letters,digits')) }}"
|
||||
|
||||
- name: Generate Prometheus BasicAuth hash
|
||||
shell: |
|
||||
docker run --rm httpd:alpine htpasswd -nbB admin "{{ prometheus_password }}" 2>/dev/null | cut -d ":" -f 2
|
||||
register: prometheus_auth_hash
|
||||
changed_when: false
|
||||
no_log: yes
|
||||
|
||||
- name: Set Prometheus BasicAuth string
|
||||
set_fact:
|
||||
prometheus_auth: "admin:{{ prometheus_auth_hash.stdout }}"
|
||||
|
||||
- name: Ensure monitoring stack directory exists
|
||||
file:
|
||||
path: "{{ stacks_base_path }}/monitoring"
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: Create monitoring stack .env file
|
||||
template:
|
||||
src: "{{ playbook_dir }}/../templates/monitoring.env.j2"
|
||||
dest: "{{ stacks_base_path }}/monitoring/.env"
|
||||
owner: "{{ ansible_user }}"
|
||||
group: "{{ ansible_user }}"
|
||||
mode: '0600'
|
||||
no_log: yes
|
||||
|
||||
- name: Deploy Monitoring stack
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ stacks_base_path }}/monitoring"
|
||||
state: present
|
||||
pull: always
|
||||
register: monitoring_output
|
||||
|
||||
- name: Wait for Monitoring to be ready
|
||||
wait_for:
|
||||
timeout: "{{ wait_timeout }}"
|
||||
when: monitoring_output.changed
|
||||
import_role:
|
||||
name: monitoring
|
||||
|
||||
# Verification
|
||||
- name: List all running containers
|
||||
@@ -345,180 +94,42 @@
|
||||
msg: "Gitea HTTPS check: {{ 'SUCCESS' if gitea_http_check.status == 200 else 'FAILED - Status: ' + (gitea_http_check.status|string) }}"
|
||||
|
||||
# 7. Deploy Application Stack
|
||||
- name: Optionally load application secrets from vault
|
||||
include_vars:
|
||||
file: "{{ playbook_dir }}/../secrets/production.vault.yml"
|
||||
no_log: yes
|
||||
ignore_errors: yes
|
||||
delegate_to: localhost
|
||||
become: no
|
||||
|
||||
- name: Check if PostgreSQL .env exists
|
||||
stat:
|
||||
path: "{{ stacks_base_path }}/postgresql/.env"
|
||||
register: postgres_env_file
|
||||
changed_when: false
|
||||
|
||||
- name: Extract PostgreSQL password from .env file
|
||||
shell: "grep '^POSTGRES_PASSWORD=' {{ stacks_base_path }}/postgresql/.env 2>/dev/null | cut -d'=' -f2- || echo ''"
|
||||
register: postgres_password_from_file
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
when: postgres_env_file.stat.exists
|
||||
no_log: yes
|
||||
|
||||
- name: Set application database password (from file, vault, or generate)
|
||||
set_fact:
|
||||
app_db_password: "{{ postgres_password_from_file.stdout if (postgres_env_file.stat.exists and postgres_password_from_file.stdout != '') else (vault_db_root_password | default(lookup('password', '/dev/null length=32 chars=ascii_letters,digits,punctuation'))) }}"
|
||||
no_log: yes
|
||||
|
||||
- name: Set application redis password from vault or generate
|
||||
set_fact:
|
||||
app_redis_password: "{{ vault_redis_password | default(lookup('password', '/dev/null length=32 chars=ascii_letters,digits,punctuation')) }}"
|
||||
|
||||
- name: Ensure application stack directory exists
|
||||
file:
|
||||
path: "{{ stacks_base_path }}/application"
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: Check if application stack docker-compose.yml exists locally
|
||||
stat:
|
||||
path: "{{ playbook_dir }}/../../stacks/application/docker-compose.yml"
|
||||
register: app_compose_local
|
||||
delegate_to: localhost
|
||||
become: no
|
||||
|
||||
- name: Copy application stack docker-compose.yml to server
|
||||
copy:
|
||||
src: "{{ playbook_dir }}/../../stacks/application/docker-compose.yml"
|
||||
dest: "{{ stacks_base_path }}/application/docker-compose.yml"
|
||||
owner: "{{ ansible_user }}"
|
||||
group: "{{ ansible_user }}"
|
||||
mode: '0644'
|
||||
when: app_compose_local.stat.exists
|
||||
|
||||
- name: Check if application stack nginx directory exists locally
|
||||
stat:
|
||||
path: "{{ playbook_dir }}/../../stacks/application/nginx/"
|
||||
register: app_nginx_local
|
||||
delegate_to: localhost
|
||||
become: no
|
||||
|
||||
- name: Copy application stack nginx configuration to server
|
||||
copy:
|
||||
src: "{{ playbook_dir }}/../../stacks/application/nginx/"
|
||||
dest: "{{ stacks_base_path }}/application/nginx/"
|
||||
owner: "{{ ansible_user }}"
|
||||
group: "{{ ansible_user }}"
|
||||
mode: '0644'
|
||||
when: app_nginx_local.stat.exists
|
||||
|
||||
- name: Create application stack .env file
|
||||
template:
|
||||
src: "{{ playbook_dir }}/../templates/application.env.j2"
|
||||
dest: "{{ stacks_base_path }}/application/.env"
|
||||
owner: "{{ ansible_user }}"
|
||||
group: "{{ ansible_user }}"
|
||||
mode: '0600'
|
||||
vars:
|
||||
db_password: "{{ app_db_password }}"
|
||||
db_user: "{{ db_user | default(db_user_default) }}"
|
||||
db_name: "{{ db_name | default(db_name_default) }}"
|
||||
redis_password: "{{ app_redis_password }}"
|
||||
app_domain: "{{ app_domain }}"
|
||||
no_log: yes
|
||||
|
||||
- name: Deploy Application stack
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ stacks_base_path }}/application"
|
||||
state: present
|
||||
pull: always
|
||||
register: application_output
|
||||
|
||||
- name: Wait for Application to be ready
|
||||
wait_for:
|
||||
timeout: "{{ wait_timeout }}"
|
||||
when: application_output.changed
|
||||
|
||||
- name: Wait for application containers to be healthy
|
||||
pause:
|
||||
seconds: 30
|
||||
when: application_output.changed
|
||||
|
||||
- name: Check application container health status
|
||||
shell: |
|
||||
docker compose -f {{ stacks_base_path }}/application/docker-compose.yml ps --format json | jq -r '.[] | select(.Health != "healthy" and .Health != "" and .Health != "starting") | "\(.Name): \(.Health)"' || echo "All healthy or no health checks"
|
||||
args:
|
||||
executable: /bin/bash
|
||||
register: app_health_status
|
||||
changed_when: false
|
||||
ignore_errors: yes
|
||||
- name: Deploy Application Stack
|
||||
import_role:
|
||||
name: application
|
||||
|
||||
- name: Display application health status
|
||||
debug:
|
||||
msg: "Application health: {{ app_health_status.stdout if app_health_status.stdout != '' else 'All services healthy or starting' }}"
|
||||
|
||||
- name: Wait for app container to be ready before migration
|
||||
wait_for:
|
||||
timeout: 60
|
||||
when: application_output.changed
|
||||
|
||||
- name: Check if app container is running
|
||||
shell: |
|
||||
docker compose -f {{ stacks_base_path }}/application/docker-compose.yml ps app | grep -q "Up" || exit 1
|
||||
args:
|
||||
executable: /bin/bash
|
||||
register: app_container_running
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
when: application_output.changed
|
||||
|
||||
- name: Run database migrations
|
||||
shell: |
|
||||
docker compose -f {{ stacks_base_path }}/application/docker-compose.yml exec -T app php console.php db:migrate
|
||||
args:
|
||||
executable: /bin/bash
|
||||
register: migration_result
|
||||
changed_when: true
|
||||
failed_when: false
|
||||
ignore_errors: yes
|
||||
when: application_output.changed and app_container_running.rc == 0
|
||||
msg: "Application health: {{ application_health_output if application_health_output != '' else 'All services healthy or starting' }}"
|
||||
|
||||
- name: Display migration result
|
||||
debug:
|
||||
msg: |
|
||||
Migration Result:
|
||||
{{ migration_result.stdout if migration_result.rc == 0 else 'Migration may have failed - check logs with: docker compose -f ' + stacks_base_path + '/application/docker-compose.yml logs app' }}
|
||||
when: application_output.changed
|
||||
|
||||
- name: Verify application accessibility via HTTPS
|
||||
uri:
|
||||
url: "{{ health_check_url }}"
|
||||
method: GET
|
||||
validate_certs: no
|
||||
status_code: [200, 404, 502, 503]
|
||||
timeout: 10
|
||||
register: app_health_check
|
||||
ignore_errors: yes
|
||||
when: application_output.changed
|
||||
{{ application_migration_stdout if application_migration_stdout != '' else 'Migration may have failed - check logs with: docker compose -f ' + application_stack_dest + '/docker-compose.yml logs app' }}
|
||||
when: application_stack_changed and application_run_migrations
|
||||
|
||||
- name: Display application accessibility status
|
||||
debug:
|
||||
msg: "Application health check: {{ 'SUCCESS (HTTP ' + (app_health_check.status|string) + ')' if app_health_check.status == 200 else 'FAILED or not ready yet (HTTP ' + (app_health_check.status|string) + ')' }}"
|
||||
when: application_output.changed
|
||||
msg: >-
|
||||
Application health check: {{
|
||||
'SUCCESS (HTTP ' + (application_healthcheck_status | string) + ')'
|
||||
if application_healthcheck_status == 200 else
|
||||
'FAILED or not ready yet (HTTP ' + (application_healthcheck_status | string) + ')'
|
||||
}}
|
||||
when: application_stack_changed and application_healthcheck_url | length > 0
|
||||
|
||||
- name: Summary
|
||||
debug:
|
||||
msg:
|
||||
- "=== Infrastructure Deployment Complete ==="
|
||||
- "Traefik: {{ 'Deployed' if traefik_output.changed else 'Already running' }}"
|
||||
- "PostgreSQL: {{ 'Deployed' if postgres_output.changed else 'Already running' }}"
|
||||
- "Docker Registry: {{ 'Deployed' if registry_output.changed else 'Already running' }}"
|
||||
- "MinIO: {{ 'Deployed' if minio_output.changed else 'Already running' }}"
|
||||
- "Gitea: {{ 'Deployed' if gitea_output.changed else 'Already running' }}"
|
||||
- "Monitoring: {{ 'Deployed' if monitoring_output.changed else 'Already running' }}"
|
||||
- "Application: {{ 'Deployed' if application_output.changed else 'Already running' }}"
|
||||
- "Traefik: {{ 'Deployed' if traefik_stack_changed else 'Already running' }}"
|
||||
- "PostgreSQL: {{ 'Deployed' if postgresql_stack_changed else 'Already running' }}"
|
||||
- "Docker Registry: {{ 'Deployed' if registry_stack_changed else 'Already running' }}"
|
||||
- "MinIO: {{ 'Deployed' if minio_stack_changed else 'Already running' }}"
|
||||
- "Gitea: {{ 'Deployed' if gitea_stack_changed else 'Already running' }}"
|
||||
- "Monitoring: {{ 'Deployed' if monitoring_stack_changed else 'Already running' }}"
|
||||
- "Application: {{ 'Deployed' if application_stack_changed else 'Already running' }}"
|
||||
- ""
|
||||
- "Next Steps:"
|
||||
- "1. Access Gitea at: https://{{ gitea_domain }}"
|
||||
|
||||
34
deployment/ansible/roles/application/defaults/main.yml
Normal file
34
deployment/ansible/roles/application/defaults/main.yml
Normal file
@@ -0,0 +1,34 @@
|
||||
---
|
||||
# Source path for application stack files on the control node
|
||||
application_stack_src: "{{ role_path }}/../../stacks/application"
|
||||
|
||||
# Destination path on the target host (defaults to configured app_stack_path)
|
||||
application_stack_dest: "{{ app_stack_path | default(stacks_base_path + '/application') }}"
|
||||
|
||||
# Template used to generate the application .env file
|
||||
application_env_template: "{{ role_path }}/../../templates/application.env.j2"
|
||||
|
||||
# Optional vault file containing secrets (loaded if present)
|
||||
application_vault_file: "{{ role_path }}/../../secrets/production.vault.yml"
|
||||
|
||||
# Whether to synchronize stack files from repository
|
||||
application_sync_files: true
|
||||
|
||||
# Compose recreate strategy ("auto", "always", "never")
|
||||
application_compose_recreate: "auto"
|
||||
|
||||
# Whether to remove orphaned containers during compose up
|
||||
application_remove_orphans: false
|
||||
|
||||
# Whether to run database migrations after (re)deploying the stack
|
||||
application_run_migrations: true
|
||||
|
||||
# Optional health check URL to verify after deployment
|
||||
application_healthcheck_url: "{{ health_check_url | default('') }}"
|
||||
|
||||
# Timeout used for waits in this role
|
||||
application_wait_timeout: "{{ wait_timeout | default(60) }}"
|
||||
application_wait_interval: 5
|
||||
|
||||
# Command executed inside the app container to run migrations
|
||||
application_migration_command: "php console.php db:migrate"
|
||||
69
deployment/ansible/roles/application/tasks/deploy.yml
Normal file
69
deployment/ansible/roles/application/tasks/deploy.yml
Normal file
@@ -0,0 +1,69 @@
|
||||
---
|
||||
- name: Deploy application stack
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ application_stack_dest }}"
|
||||
state: present
|
||||
pull: always
|
||||
recreate: "{{ application_compose_recreate }}"
|
||||
remove_orphans: "{{ application_remove_orphans | bool }}"
|
||||
register: application_compose_result
|
||||
|
||||
- name: Wait for application container to report Up
|
||||
shell: |
|
||||
docker compose -f {{ application_stack_dest }}/docker-compose.yml ps app | grep -Eiq "Up|running"
|
||||
register: application_app_running
|
||||
changed_when: false
|
||||
until: application_app_running.rc == 0
|
||||
retries: "{{ ((application_wait_timeout | int) + (application_wait_interval | int) - 1) // (application_wait_interval | int) }}"
|
||||
delay: "{{ application_wait_interval | int }}"
|
||||
when: application_compose_result.changed
|
||||
|
||||
- name: Ensure app container is running before migrations
|
||||
shell: |
|
||||
docker compose -f {{ application_stack_dest }}/docker-compose.yml ps app | grep -Eiq "Up|running"
|
||||
args:
|
||||
executable: /bin/bash
|
||||
register: application_app_container_running
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
when: application_compose_result.changed
|
||||
|
||||
- name: Run database migrations
|
||||
shell: |
|
||||
docker compose -f {{ application_stack_dest }}/docker-compose.yml exec -T app {{ application_migration_command }}
|
||||
args:
|
||||
executable: /bin/bash
|
||||
register: application_migration_result
|
||||
changed_when: true
|
||||
failed_when: false
|
||||
ignore_errors: yes
|
||||
when:
|
||||
- application_run_migrations
|
||||
- application_compose_result.changed
|
||||
- application_app_container_running.rc == 0
|
||||
|
||||
- name: Collect application container status
|
||||
shell: docker compose -f {{ application_stack_dest }}/docker-compose.yml ps
|
||||
register: application_ps
|
||||
changed_when: false
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Perform application health check
|
||||
uri:
|
||||
url: "{{ application_healthcheck_url }}"
|
||||
method: GET
|
||||
validate_certs: no
|
||||
status_code: [200, 404, 502, 503]
|
||||
timeout: 10
|
||||
register: application_healthcheck_result
|
||||
ignore_errors: yes
|
||||
when:
|
||||
- application_healthcheck_url | length > 0
|
||||
- application_compose_result.changed
|
||||
|
||||
- name: Set application role summary facts
|
||||
set_fact:
|
||||
application_stack_changed: "{{ application_compose_result.changed | default(false) }}"
|
||||
application_health_output: "{{ application_ps.stdout | default('') }}"
|
||||
application_healthcheck_status: "{{ application_healthcheck_result.status | default('unknown') }}"
|
||||
application_migration_stdout: "{{ application_migration_result.stdout | default('') }}"
|
||||
7
deployment/ansible/roles/application/tasks/main.yml
Normal file
7
deployment/ansible/roles/application/tasks/main.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
- name: Synchronize application stack files
|
||||
include_tasks: sync.yml
|
||||
when: application_sync_files | bool
|
||||
|
||||
- name: Deploy application stack
|
||||
include_tasks: deploy.yml
|
||||
94
deployment/ansible/roles/application/tasks/sync.yml
Normal file
94
deployment/ansible/roles/application/tasks/sync.yml
Normal file
@@ -0,0 +1,94 @@
|
||||
---
|
||||
- name: Ensure application stack destination directory exists
|
||||
file:
|
||||
path: "{{ application_stack_dest }}"
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: Check if vault file exists locally
|
||||
stat:
|
||||
path: "{{ application_vault_file }}"
|
||||
delegate_to: localhost
|
||||
register: application_vault_stat
|
||||
become: no
|
||||
|
||||
- name: Optionally load application secrets from vault
|
||||
include_vars:
|
||||
file: "{{ application_vault_file }}"
|
||||
when: application_vault_stat.stat.exists
|
||||
no_log: yes
|
||||
delegate_to: localhost
|
||||
become: no
|
||||
|
||||
- name: Check if PostgreSQL .env exists on target host
|
||||
stat:
|
||||
path: "{{ stacks_base_path }}/postgresql/.env"
|
||||
register: application_postgres_env_file
|
||||
changed_when: false
|
||||
|
||||
- name: Extract PostgreSQL password from .env file
|
||||
shell: "grep '^POSTGRES_PASSWORD=' {{ stacks_base_path }}/postgresql/.env 2>/dev/null | cut -d'=' -f2- || echo ''"
|
||||
register: application_postgres_password
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
when: application_postgres_env_file.stat.exists
|
||||
no_log: yes
|
||||
|
||||
- name: Determine application database password
|
||||
set_fact:
|
||||
application_db_password: >-
|
||||
{{ (application_postgres_env_file.stat.exists and application_postgres_password.stdout != '') |
|
||||
ternary(application_postgres_password.stdout,
|
||||
vault_db_root_password | default(lookup('password', '/dev/null length=32 chars=ascii_letters,digits,punctuation'))) }}
|
||||
no_log: yes
|
||||
|
||||
- name: Determine application redis password
|
||||
set_fact:
|
||||
application_redis_password: "{{ vault_redis_password | default(lookup('password', '/dev/null length=32 chars=ascii_letters,digits,punctuation')) }}"
|
||||
no_log: yes
|
||||
|
||||
- name: Check if application docker-compose source exists locally
|
||||
stat:
|
||||
path: "{{ application_stack_src }}/docker-compose.yml"
|
||||
delegate_to: localhost
|
||||
register: application_compose_src
|
||||
become: no
|
||||
|
||||
- name: Copy application docker-compose to target host
|
||||
copy:
|
||||
src: "{{ application_stack_src }}/docker-compose.yml"
|
||||
dest: "{{ application_stack_dest }}/docker-compose.yml"
|
||||
owner: "{{ ansible_user }}"
|
||||
group: "{{ ansible_user }}"
|
||||
mode: '0644'
|
||||
when: application_compose_src.stat.exists
|
||||
|
||||
- name: Check if nginx configuration exists locally
|
||||
stat:
|
||||
path: "{{ application_stack_src }}/nginx"
|
||||
delegate_to: localhost
|
||||
register: application_nginx_src
|
||||
become: no
|
||||
|
||||
- name: Synchronize nginx configuration
|
||||
copy:
|
||||
src: "{{ application_stack_src }}/nginx/"
|
||||
dest: "{{ application_stack_dest }}/nginx/"
|
||||
owner: "{{ ansible_user }}"
|
||||
group: "{{ ansible_user }}"
|
||||
mode: '0644'
|
||||
when: application_nginx_src.stat.exists
|
||||
|
||||
- name: Render application environment file
|
||||
template:
|
||||
src: "{{ application_env_template }}"
|
||||
dest: "{{ application_stack_dest }}/.env"
|
||||
owner: "{{ ansible_user }}"
|
||||
group: "{{ ansible_user }}"
|
||||
mode: '0600'
|
||||
vars:
|
||||
db_password: "{{ application_db_password }}"
|
||||
db_user: "{{ db_user | default(db_user_default) }}"
|
||||
db_name: "{{ db_name | default(db_name_default) }}"
|
||||
redis_password: "{{ application_redis_password }}"
|
||||
app_domain: "{{ app_domain }}"
|
||||
4
deployment/ansible/roles/gitea/defaults/main.yml
Normal file
4
deployment/ansible/roles/gitea/defaults/main.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
gitea_stack_path: "{{ stacks_base_path }}/gitea"
|
||||
gitea_wait_timeout: "{{ wait_timeout | default(60) }}"
|
||||
gitea_wait_interval: 5
|
||||
35
deployment/ansible/roles/gitea/tasks/main.yml
Normal file
35
deployment/ansible/roles/gitea/tasks/main.yml
Normal file
@@ -0,0 +1,35 @@
|
||||
---
|
||||
- name: Deploy Gitea stack
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ gitea_stack_path }}"
|
||||
state: present
|
||||
pull: always
|
||||
register: gitea_compose_result
|
||||
|
||||
- name: Check Gitea container status
|
||||
shell: |
|
||||
docker compose -f {{ gitea_stack_path }}/docker-compose.yml ps gitea | grep -Eiq "Up|running"
|
||||
register: gitea_state
|
||||
changed_when: false
|
||||
until: gitea_state.rc == 0
|
||||
retries: "{{ ((gitea_wait_timeout | int) + (gitea_wait_interval | int) - 1) // (gitea_wait_interval | int) }}"
|
||||
delay: "{{ gitea_wait_interval | int }}"
|
||||
failed_when: gitea_state.rc != 0
|
||||
when: not ansible_check_mode
|
||||
|
||||
- name: Check Gitea logs for readiness
|
||||
shell: docker compose logs gitea 2>&1 | grep -Ei "(Listen:|Server is running|Starting server)" || true
|
||||
args:
|
||||
chdir: "{{ gitea_stack_path }}"
|
||||
register: gitea_logs
|
||||
until: gitea_logs.stdout != ""
|
||||
retries: 12
|
||||
delay: 10
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
when: not ansible_check_mode
|
||||
|
||||
- name: Record Gitea deployment facts
|
||||
set_fact:
|
||||
gitea_stack_changed: "{{ gitea_compose_result.changed | default(false) }}"
|
||||
gitea_log_hint: "{{ gitea_logs.stdout | default('') }}"
|
||||
6
deployment/ansible/roles/minio/defaults/main.yml
Normal file
6
deployment/ansible/roles/minio/defaults/main.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
minio_stack_path: "{{ stacks_base_path }}/minio"
|
||||
minio_wait_timeout: "{{ wait_timeout | default(60) }}"
|
||||
minio_wait_interval: 5
|
||||
minio_env_template: "{{ role_path }}/../../templates/minio.env.j2"
|
||||
minio_vault_file: "{{ role_path }}/../../secrets/production.vault.yml"
|
||||
90
deployment/ansible/roles/minio/tasks/main.yml
Normal file
90
deployment/ansible/roles/minio/tasks/main.yml
Normal file
@@ -0,0 +1,90 @@
|
||||
---
|
||||
- name: Check if MinIO vault file exists
|
||||
stat:
|
||||
path: "{{ minio_vault_file }}"
|
||||
delegate_to: localhost
|
||||
register: minio_vault_stat
|
||||
become: no
|
||||
|
||||
- name: Optionally load MinIO secrets from vault
|
||||
include_vars:
|
||||
file: "{{ minio_vault_file }}"
|
||||
when: minio_vault_stat.stat.exists
|
||||
no_log: yes
|
||||
delegate_to: localhost
|
||||
become: no
|
||||
|
||||
- name: Set MinIO root password from vault or generate
|
||||
set_fact:
|
||||
minio_root_password: "{{ vault_minio_root_password | default(lookup('password', '/dev/null length=32 chars=ascii_letters,digits,punctuation')) }}"
|
||||
no_log: yes
|
||||
|
||||
- name: Set MinIO root user from vault or use default
|
||||
set_fact:
|
||||
minio_root_user: "{{ vault_minio_root_user | default('minioadmin') }}"
|
||||
no_log: yes
|
||||
|
||||
- name: Ensure MinIO stack directory exists
|
||||
file:
|
||||
path: "{{ minio_stack_path }}"
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: Create MinIO stack .env file
|
||||
template:
|
||||
src: "{{ minio_env_template }}"
|
||||
dest: "{{ minio_stack_path }}/.env"
|
||||
owner: "{{ ansible_user }}"
|
||||
group: "{{ ansible_user }}"
|
||||
mode: '0600'
|
||||
|
||||
- name: Deploy MinIO stack
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ minio_stack_path }}"
|
||||
state: present
|
||||
pull: always
|
||||
register: minio_compose_result
|
||||
|
||||
- name: Check MinIO container status
|
||||
shell: |
|
||||
docker compose -f {{ minio_stack_path }}/docker-compose.yml ps minio | grep -Eiq "Up|running"
|
||||
register: minio_state
|
||||
changed_when: false
|
||||
until: minio_state.rc == 0
|
||||
retries: "{{ ((minio_wait_timeout | int) + (minio_wait_interval | int) - 1) // (minio_wait_interval | int) }}"
|
||||
delay: "{{ minio_wait_interval | int }}"
|
||||
failed_when: minio_state.rc != 0
|
||||
when: not ansible_check_mode
|
||||
|
||||
- name: Check MinIO logs for readiness
|
||||
shell: docker compose logs minio 2>&1 | grep -Ei "(API:|WebUI:|MinIO Object Storage Server)" || true
|
||||
args:
|
||||
chdir: "{{ minio_stack_path }}"
|
||||
register: minio_logs
|
||||
until: minio_logs.stdout != ""
|
||||
retries: 6
|
||||
delay: 10
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
when: not ansible_check_mode
|
||||
|
||||
- name: Verify MinIO health endpoint
|
||||
uri:
|
||||
url: "http://127.0.0.1:9000/minio/health/live"
|
||||
method: GET
|
||||
status_code: [200, 404, 502, 503]
|
||||
timeout: 5
|
||||
register: minio_health_check
|
||||
ignore_errors: yes
|
||||
changed_when: false
|
||||
when: not ansible_check_mode
|
||||
|
||||
- name: Display MinIO status
|
||||
debug:
|
||||
msg: "MinIO health check: {{ 'SUCCESS' if minio_health_check.status == 200 else 'FAILED - Status: ' + (minio_health_check.status|string) }}"
|
||||
when: not ansible_check_mode
|
||||
|
||||
- name: Record MinIO deployment facts
|
||||
set_fact:
|
||||
minio_stack_changed: "{{ minio_compose_result.changed | default(false) }}"
|
||||
minio_health_status: "{{ minio_health_check.status | default('unknown') }}"
|
||||
5
deployment/ansible/roles/monitoring/defaults/main.yml
Normal file
5
deployment/ansible/roles/monitoring/defaults/main.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
monitoring_stack_path: "{{ stacks_base_path }}/monitoring"
|
||||
monitoring_wait_timeout: "{{ wait_timeout | default(60) }}"
|
||||
monitoring_env_template: "{{ role_path }}/../../templates/monitoring.env.j2"
|
||||
monitoring_vault_file: "{{ role_path }}/../../secrets/production.vault.yml"
|
||||
68
deployment/ansible/roles/monitoring/tasks/main.yml
Normal file
68
deployment/ansible/roles/monitoring/tasks/main.yml
Normal file
@@ -0,0 +1,68 @@
|
||||
---
|
||||
- name: Check if monitoring vault file exists
|
||||
stat:
|
||||
path: "{{ monitoring_vault_file }}"
|
||||
delegate_to: localhost
|
||||
register: monitoring_vault_stat
|
||||
become: no
|
||||
|
||||
- name: Optionally load monitoring secrets from vault
|
||||
include_vars:
|
||||
file: "{{ monitoring_vault_file }}"
|
||||
when: monitoring_vault_stat.stat.exists
|
||||
no_log: yes
|
||||
delegate_to: localhost
|
||||
become: no
|
||||
|
||||
- name: Set Grafana admin password from vault or generate
|
||||
set_fact:
|
||||
grafana_admin_password: "{{ vault_grafana_admin_password | default(lookup('password', '/dev/null length=25 chars=ascii_letters,digits')) }}"
|
||||
no_log: yes
|
||||
|
||||
- name: Set Prometheus password from vault or generate
|
||||
set_fact:
|
||||
prometheus_password: "{{ vault_prometheus_password | default(lookup('password', '/dev/null length=25 chars=ascii_letters,digits')) }}"
|
||||
no_log: yes
|
||||
|
||||
- name: Generate Prometheus BasicAuth hash
|
||||
shell: |
|
||||
docker run --rm httpd:alpine htpasswd -nbB admin "{{ prometheus_password }}" 2>/dev/null | cut -d ":" -f 2
|
||||
register: prometheus_auth_hash
|
||||
changed_when: false
|
||||
no_log: yes
|
||||
|
||||
- name: Set Prometheus BasicAuth string
|
||||
set_fact:
|
||||
prometheus_auth: "admin:{{ prometheus_auth_hash.stdout }}"
|
||||
no_log: yes
|
||||
|
||||
- name: Ensure monitoring stack directory exists
|
||||
file:
|
||||
path: "{{ monitoring_stack_path }}"
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: Create monitoring stack .env file
|
||||
template:
|
||||
src: "{{ monitoring_env_template }}"
|
||||
dest: "{{ monitoring_stack_path }}/.env"
|
||||
owner: "{{ ansible_user }}"
|
||||
group: "{{ ansible_user }}"
|
||||
mode: '0600'
|
||||
no_log: yes
|
||||
|
||||
- name: Deploy Monitoring stack
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ monitoring_stack_path }}"
|
||||
state: present
|
||||
pull: always
|
||||
register: monitoring_compose_result
|
||||
|
||||
- name: Wait for Monitoring to be ready
|
||||
wait_for:
|
||||
timeout: "{{ monitoring_wait_timeout }}"
|
||||
when: monitoring_compose_result.changed
|
||||
|
||||
- name: Record monitoring deployment facts
|
||||
set_fact:
|
||||
monitoring_stack_changed: "{{ monitoring_compose_result.changed | default(false) }}"
|
||||
4
deployment/ansible/roles/postgresql/defaults/main.yml
Normal file
4
deployment/ansible/roles/postgresql/defaults/main.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
postgresql_stack_path: "{{ stacks_base_path }}/postgresql"
|
||||
postgresql_wait_timeout: "{{ wait_timeout | default(60) }}"
|
||||
postgresql_wait_interval: 5
|
||||
23
deployment/ansible/roles/postgresql/tasks/main.yml
Normal file
23
deployment/ansible/roles/postgresql/tasks/main.yml
Normal file
@@ -0,0 +1,23 @@
|
||||
---
|
||||
- name: Deploy PostgreSQL stack
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ postgresql_stack_path }}"
|
||||
state: present
|
||||
pull: always
|
||||
register: postgresql_compose_result
|
||||
|
||||
- name: Check PostgreSQL container status
|
||||
shell: |
|
||||
docker compose -f {{ postgresql_stack_path }}/docker-compose.yml ps postgres | grep -Eiq "Up|running"
|
||||
register: postgresql_state
|
||||
changed_when: false
|
||||
until: postgresql_state.rc == 0
|
||||
retries: "{{ ((postgresql_wait_timeout | int) + (postgresql_wait_interval | int) - 1) // (postgresql_wait_interval | int) }}"
|
||||
delay: "{{ postgresql_wait_interval | int }}"
|
||||
failed_when: postgresql_state.rc != 0
|
||||
when: not ansible_check_mode
|
||||
|
||||
- name: Record PostgreSQL deployment facts
|
||||
set_fact:
|
||||
postgresql_stack_changed: "{{ postgresql_compose_result.changed | default(false) }}"
|
||||
postgresql_log_hint: ""
|
||||
5
deployment/ansible/roles/registry/defaults/main.yml
Normal file
5
deployment/ansible/roles/registry/defaults/main.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
registry_stack_path: "{{ stacks_base_path }}/registry"
|
||||
registry_wait_timeout: "{{ wait_timeout | default(60) }}"
|
||||
registry_wait_interval: 5
|
||||
registry_vault_file: "{{ role_path }}/../../secrets/production.vault.yml"
|
||||
115
deployment/ansible/roles/registry/tasks/main.yml
Normal file
115
deployment/ansible/roles/registry/tasks/main.yml
Normal file
@@ -0,0 +1,115 @@
|
||||
---
|
||||
- name: Ensure Registry auth directory exists
|
||||
file:
|
||||
path: "{{ registry_auth_path }}"
|
||||
state: directory
|
||||
mode: '0755'
|
||||
become: yes
|
||||
|
||||
- name: Check if registry vault file exists
|
||||
stat:
|
||||
path: "{{ registry_vault_file }}"
|
||||
delegate_to: localhost
|
||||
register: registry_vault_stat
|
||||
become: no
|
||||
|
||||
- name: Optionally load registry credentials from vault
|
||||
include_vars:
|
||||
file: "{{ registry_vault_file }}"
|
||||
when: registry_vault_stat.stat.exists
|
||||
no_log: yes
|
||||
delegate_to: localhost
|
||||
become: no
|
||||
register: registry_vault_vars
|
||||
failed_when: false
|
||||
|
||||
- name: Fail if registry vault decryption failed
|
||||
fail:
|
||||
msg: >
|
||||
Failed to decrypt {{ registry_vault_file }}.
|
||||
Provide a valid vault password (e.g. via --vault-password-file) or update docker_registry_password_default.
|
||||
when:
|
||||
- not ansible_check_mode
|
||||
- registry_vault_stat.stat.exists
|
||||
- registry_vault_vars is defined
|
||||
- registry_vault_vars.failed | default(false)
|
||||
|
||||
- name: Set registry credentials from vault or defaults or generate
|
||||
set_fact:
|
||||
registry_username: "{{ vault_docker_registry_username | default(docker_registry_username_default) }}"
|
||||
registry_password: >-
|
||||
{{
|
||||
vault_docker_registry_password
|
||||
| default(docker_registry_password_default)
|
||||
| default(lookup('password', '/dev/null length=32 chars=ascii_letters,digits'))
|
||||
}}
|
||||
no_log: true
|
||||
|
||||
- name: Create Registry htpasswd file if missing
|
||||
shell: |
|
||||
docker run --rm --entrypoint htpasswd httpd:2 -Bbn {{ registry_username }} {{ registry_password }} > {{ registry_auth_path }}/htpasswd
|
||||
chmod 644 {{ registry_auth_path }}/htpasswd
|
||||
args:
|
||||
executable: /bin/bash
|
||||
creates: "{{ registry_auth_path }}/htpasswd"
|
||||
become: yes
|
||||
no_log: true
|
||||
when: not ansible_check_mode
|
||||
|
||||
- name: Deploy Docker Registry stack
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ registry_stack_path }}"
|
||||
state: present
|
||||
pull: always
|
||||
register: registry_compose_result
|
||||
|
||||
- name: Wait for Docker Registry to be ready
|
||||
wait_for:
|
||||
timeout: "{{ registry_wait_timeout }}"
|
||||
when: registry_compose_result.changed
|
||||
|
||||
- name: Check Registry container status
|
||||
shell: |
|
||||
docker compose -f {{ registry_stack_path }}/docker-compose.yml ps registry | grep -Eiq "Up|running"
|
||||
register: registry_state
|
||||
changed_when: false
|
||||
until: registry_state.rc == 0
|
||||
retries: "{{ ((registry_wait_timeout | int) + (registry_wait_interval | int) - 1) // (registry_wait_interval | int) }}"
|
||||
delay: "{{ registry_wait_interval | int }}"
|
||||
failed_when: registry_state.rc != 0
|
||||
when: not ansible_check_mode
|
||||
|
||||
- name: Check Registry logs for readiness
|
||||
shell: docker compose logs registry 2>&1 | grep -Ei "(listening on|listening at|http server)" || true
|
||||
args:
|
||||
chdir: "{{ registry_stack_path }}"
|
||||
register: registry_logs
|
||||
until: registry_logs.stdout != ""
|
||||
retries: 6
|
||||
delay: 10
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
when: not ansible_check_mode
|
||||
|
||||
- name: Verify Registry is accessible
|
||||
uri:
|
||||
url: "http://127.0.0.1:5000/v2/_catalog"
|
||||
user: "{{ registry_username }}"
|
||||
password: "{{ registry_password }}"
|
||||
status_code: 200
|
||||
timeout: 5
|
||||
register: registry_check
|
||||
ignore_errors: yes
|
||||
changed_when: false
|
||||
no_log: true
|
||||
when: not ansible_check_mode
|
||||
|
||||
- name: Display Registry status
|
||||
debug:
|
||||
msg: "Registry accessibility: {{ 'SUCCESS' if registry_check.status == 200 else 'FAILED - may need manual check' }}"
|
||||
when: not ansible_check_mode
|
||||
|
||||
- name: Record registry deployment facts
|
||||
set_fact:
|
||||
registry_stack_changed: "{{ registry_compose_result.changed | default(false) }}"
|
||||
registry_access_status: "{{ registry_check.status | default('unknown') }}"
|
||||
4
deployment/ansible/roles/traefik/defaults/main.yml
Normal file
4
deployment/ansible/roles/traefik/defaults/main.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
traefik_stack_path: "{{ stacks_base_path }}/traefik"
|
||||
traefik_wait_timeout: "{{ wait_timeout | default(60) }}"
|
||||
traefik_wait_interval: 5
|
||||
23
deployment/ansible/roles/traefik/tasks/main.yml
Normal file
23
deployment/ansible/roles/traefik/tasks/main.yml
Normal file
@@ -0,0 +1,23 @@
|
||||
---
|
||||
- name: Deploy Traefik stack
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: "{{ traefik_stack_path }}"
|
||||
state: present
|
||||
pull: always
|
||||
register: traefik_compose_result
|
||||
|
||||
- name: Check Traefik container status
|
||||
shell: |
|
||||
docker compose -f {{ traefik_stack_path }}/docker-compose.yml ps traefik | grep -Eiq "Up|running"
|
||||
register: traefik_state
|
||||
changed_when: false
|
||||
until: traefik_state.rc == 0
|
||||
retries: "{{ ((traefik_wait_timeout | int) + (traefik_wait_interval | int) - 1) // (traefik_wait_interval | int) }}"
|
||||
delay: "{{ traefik_wait_interval | int }}"
|
||||
failed_when: traefik_state.rc != 0
|
||||
when: not ansible_check_mode
|
||||
|
||||
- name: Record Traefik deployment facts
|
||||
set_fact:
|
||||
traefik_stack_changed: "{{ traefik_compose_result.changed | default(false) }}"
|
||||
traefik_log_hint: ""
|
||||
Reference in New Issue
Block a user