feat: add system maintenance automation
This commit is contained in:
6
Makefile
6
Makefile
@@ -215,6 +215,10 @@ logs-production: ## Show production logs
|
||||
@echo "📋 Showing production logs..."
|
||||
@cd deployment && make logs-prod-php
|
||||
|
||||
logs-staging: ## Show staging-app container logs via SSH
|
||||
@echo "📋 Showing staging-app logs..."
|
||||
@ssh -i ~/.ssh/production deploy@94.16.110.151 "cd ~/deployment/stacks/staging && docker compose logs -f staging-app"
|
||||
|
||||
# SSL Certificate Management (PHP Framework Integration)
|
||||
ssl-init: ## Initialize Let's Encrypt certificates
|
||||
@echo "🔒 Initializing SSL certificates..."
|
||||
@@ -249,4 +253,4 @@ ssl-backup: ## Backup Let's Encrypt certificates
|
||||
push-staging: ## Pusht den aktuellen Stand nach origin/staging
|
||||
git push origin HEAD:staging
|
||||
|
||||
.PHONY: up down build restart logs ps phpinfo deploy setup clean clean-coverage status fix-ssh-perms setup-ssh test test-coverage test-coverage-html test-unit test-framework test-domain test-watch test-parallel test-profile test-filter security-check security-audit-json security-check-prod update-production restart-production deploy-production-quick status-production logs-production ssl-init ssl-init-staging ssl-test ssl-renew ssl-status ssl-backup push-staging
|
||||
.PHONY: up down build restart logs ps phpinfo deploy setup clean clean-coverage status fix-ssh-perms setup-ssh test test-coverage test-coverage-html test-unit test-framework test-domain test-watch test-parallel test-profile test-filter security-check security-audit-json security-check-prod update-production restart-production deploy-production-quick status-production logs-production logs-staging ssl-init ssl-init-staging ssl-test ssl-renew ssl-status ssl-backup push-staging
|
||||
|
||||
@@ -2,6 +2,15 @@
|
||||
# Production Deployment - Centralized Variables
|
||||
# These variables are used across all playbooks
|
||||
|
||||
# System Maintenance
|
||||
system_update_packages: true
|
||||
system_apt_upgrade: dist
|
||||
system_enable_unattended_upgrades: true
|
||||
system_enable_unattended_reboot: false
|
||||
system_unattended_reboot_time: "02:00"
|
||||
system_enable_unattended_timer: true
|
||||
system_enable_docker_prune: false
|
||||
|
||||
# Deployment Paths
|
||||
deploy_user_home: "/home/deploy"
|
||||
stacks_base_path: "{{ deploy_user_home }}/deployment/stacks"
|
||||
|
||||
@@ -25,6 +25,11 @@
|
||||
docker_registry_username: "{{ docker_registry_username | default(vault_docker_registry_username | default(docker_registry_username_default)) }}"
|
||||
docker_registry_password: "{{ docker_registry_password | default(vault_docker_registry_password | default(docker_registry_password_default)) }}"
|
||||
|
||||
- name: Ensure system packages are up to date
|
||||
include_role:
|
||||
name: system
|
||||
when: system_update_packages | bool
|
||||
|
||||
- name: Verify Docker is running
|
||||
systemd:
|
||||
name: docker
|
||||
|
||||
@@ -26,6 +26,11 @@
|
||||
msg: "Deployment stacks directory not found at {{ stacks_base_path }}"
|
||||
when: not stacks_dir.stat.exists
|
||||
|
||||
- name: Ensure system packages are up to date
|
||||
include_role:
|
||||
name: system
|
||||
when: system_update_packages | bool
|
||||
|
||||
# Create external networks required by all stacks
|
||||
- name: Create traefik-public network
|
||||
community.docker.docker_network:
|
||||
|
||||
11
deployment/ansible/playbooks/system-maintenance.yml
Normal file
11
deployment/ansible/playbooks/system-maintenance.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
- name: Apply system maintenance on production hosts
|
||||
hosts: production
|
||||
gather_facts: yes
|
||||
become: yes
|
||||
|
||||
tasks:
|
||||
- name: Run system maintenance role
|
||||
include_role:
|
||||
name: system
|
||||
when: system_update_packages | bool
|
||||
@@ -79,6 +79,14 @@
|
||||
mode: '0644'
|
||||
when: application_nginx_src.stat.exists
|
||||
|
||||
- name: Expose secrets for template rendering
|
||||
set_fact:
|
||||
db_password: "{{ application_db_password }}"
|
||||
redis_password: "{{ application_redis_password }}"
|
||||
db_username: "{{ db_user | default(db_user_default) }}"
|
||||
db_name: "{{ db_name | default(db_name_default) }}"
|
||||
no_log: yes
|
||||
|
||||
- name: Render application environment file
|
||||
template:
|
||||
src: "{{ application_env_template }}"
|
||||
@@ -86,9 +94,3 @@
|
||||
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,3 +4,4 @@ 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"
|
||||
minio_healthcheck_enabled: false
|
||||
|
||||
@@ -77,14 +77,18 @@
|
||||
register: minio_health_check
|
||||
ignore_errors: yes
|
||||
changed_when: false
|
||||
when: not ansible_check_mode
|
||||
when:
|
||||
- not ansible_check_mode
|
||||
- minio_healthcheck_enabled | bool
|
||||
|
||||
- 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
|
||||
when:
|
||||
- not ansible_check_mode
|
||||
- minio_healthcheck_enabled | bool
|
||||
|
||||
- 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') }}"
|
||||
minio_health_status: "{{ minio_health_check.status | default('disabled' if not minio_healthcheck_enabled else 'unknown') }}"
|
||||
|
||||
@@ -3,3 +3,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"
|
||||
registry_healthcheck_enabled: true
|
||||
registry_healthcheck_url: "http://127.0.0.1:5000/v2/_catalog"
|
||||
|
||||
@@ -93,7 +93,7 @@
|
||||
|
||||
- name: Verify Registry is accessible
|
||||
uri:
|
||||
url: "http://127.0.0.1:5000/v2/_catalog"
|
||||
url: "{{ registry_healthcheck_url }}"
|
||||
user: "{{ registry_username }}"
|
||||
password: "{{ registry_password }}"
|
||||
status_code: 200
|
||||
@@ -102,14 +102,18 @@
|
||||
ignore_errors: yes
|
||||
changed_when: false
|
||||
no_log: true
|
||||
when: not ansible_check_mode
|
||||
when:
|
||||
- not ansible_check_mode
|
||||
- registry_healthcheck_enabled | bool
|
||||
|
||||
- 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
|
||||
when:
|
||||
- not ansible_check_mode
|
||||
- registry_healthcheck_enabled | bool
|
||||
|
||||
- name: Record registry deployment facts
|
||||
set_fact:
|
||||
registry_stack_changed: "{{ registry_compose_result.changed | default(false) }}"
|
||||
registry_access_status: "{{ registry_check.status | default('unknown') }}"
|
||||
registry_access_status: "{{ registry_check.status | default('disabled' if not registry_healthcheck_enabled else 'unknown') }}"
|
||||
|
||||
9
deployment/ansible/roles/system/defaults/main.yml
Normal file
9
deployment/ansible/roles/system/defaults/main.yml
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
system_update_packages: true
|
||||
system_apt_cache_valid_time: 3600
|
||||
system_apt_upgrade: dist
|
||||
system_enable_unattended_upgrades: true
|
||||
system_enable_unattended_reboot: false
|
||||
system_unattended_reboot_time: "02:00"
|
||||
system_enable_unattended_timer: true
|
||||
system_enable_docker_prune: false
|
||||
130
deployment/ansible/roles/system/tasks/main.yml
Normal file
130
deployment/ansible/roles/system/tasks/main.yml
Normal file
@@ -0,0 +1,130 @@
|
||||
---
|
||||
- name: Refresh apt cache on Debian-based systems
|
||||
ansible.builtin.apt:
|
||||
update_cache: yes
|
||||
cache_valid_time: "{{ system_apt_cache_valid_time }}"
|
||||
become: yes
|
||||
when:
|
||||
- ansible_os_family == 'Debian'
|
||||
- system_update_packages | bool
|
||||
|
||||
- name: Upgrade packages on Debian-based systems
|
||||
ansible.builtin.apt:
|
||||
upgrade: "{{ system_apt_upgrade }}"
|
||||
autoremove: yes
|
||||
become: yes
|
||||
when:
|
||||
- ansible_os_family == 'Debian'
|
||||
- system_update_packages | bool
|
||||
|
||||
- name: Upgrade packages on RedHat-based systems
|
||||
ansible.builtin.yum:
|
||||
name: '*'
|
||||
state: latest
|
||||
become: yes
|
||||
when:
|
||||
- ansible_os_family == 'RedHat'
|
||||
- system_update_packages | bool
|
||||
|
||||
- name: Warn about unsupported package manager
|
||||
ansible.builtin.debug:
|
||||
msg: "System package updates are not implemented for {{ ansible_os_family }}"
|
||||
changed_when: false
|
||||
when:
|
||||
- system_update_packages | bool
|
||||
- ansible_os_family not in ['Debian', 'RedHat']
|
||||
|
||||
- name: Install unattended-upgrades packages
|
||||
ansible.builtin.package:
|
||||
name:
|
||||
- unattended-upgrades
|
||||
- apt-listchanges
|
||||
state: present
|
||||
become: yes
|
||||
when:
|
||||
- ansible_os_family == 'Debian'
|
||||
- system_enable_unattended_upgrades | bool
|
||||
|
||||
- name: Configure unattended upgrades periodic execution
|
||||
ansible.builtin.copy:
|
||||
dest: /etc/apt/apt.conf.d/20auto-upgrades
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
content: |
|
||||
APT::Periodic::Update-Package-Lists "1";
|
||||
APT::Periodic::Download-Upgradeable-Packages "1";
|
||||
APT::Periodic::AutocleanInterval "7";
|
||||
APT::Periodic::Unattended-Upgrade "1";
|
||||
become: yes
|
||||
when:
|
||||
- ansible_os_family == 'Debian'
|
||||
- system_enable_unattended_upgrades | bool
|
||||
|
||||
- name: Configure unattended upgrade reboot preference
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/apt/apt.conf.d/50unattended-upgrades
|
||||
regexp: '^//?\s*Unattended-Upgrade::Automatic-Reboot\s+'
|
||||
line: 'Unattended-Upgrade::Automatic-Reboot "{{ system_enable_unattended_reboot | ternary("true", "false") }}";'
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
create: yes
|
||||
become: yes
|
||||
when:
|
||||
- ansible_os_family == 'Debian'
|
||||
- system_enable_unattended_upgrades | bool
|
||||
|
||||
- name: Configure unattended upgrade reboot time
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/apt/apt.conf.d/50unattended-upgrades
|
||||
regexp: '^//?\s*Unattended-Upgrade::Automatic-Reboot-Time\s+'
|
||||
line: 'Unattended-Upgrade::Automatic-Reboot-Time "{{ system_unattended_reboot_time }}";'
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
create: yes
|
||||
become: yes
|
||||
when:
|
||||
- ansible_os_family == 'Debian'
|
||||
- system_enable_unattended_upgrades | bool
|
||||
- system_enable_unattended_reboot | bool
|
||||
|
||||
- name: Disable unattended reboot time when automatic reboot is off
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/apt/apt.conf.d/50unattended-upgrades
|
||||
regexp: '^Unattended-Upgrade::Automatic-Reboot-Time\s+'
|
||||
state: absent
|
||||
owner: root
|
||||
group: root
|
||||
mode: '0644'
|
||||
become: yes
|
||||
when:
|
||||
- ansible_os_family == 'Debian'
|
||||
- system_enable_unattended_upgrades | bool
|
||||
- not system_enable_unattended_reboot | bool
|
||||
|
||||
- name: Ensure unattended upgrade timers are enabled
|
||||
ansible.builtin.systemd:
|
||||
name: "{{ item }}"
|
||||
enabled: true
|
||||
state: started
|
||||
become: yes
|
||||
loop:
|
||||
- apt-daily.timer
|
||||
- apt-daily-upgrade.timer
|
||||
- unattended-upgrades.service
|
||||
when:
|
||||
- ansible_os_family == 'Debian'
|
||||
- system_enable_unattended_upgrades | bool
|
||||
- system_enable_unattended_timer | bool
|
||||
|
||||
- name: Prune unused Docker data
|
||||
community.docker.docker_prune:
|
||||
containers: true
|
||||
images: true
|
||||
networks: true
|
||||
volumes: false
|
||||
builder_cache: true
|
||||
become: yes
|
||||
when: system_enable_docker_prune | bool
|
||||
@@ -16,6 +16,10 @@ APP_URL=https://{{ app_domain }}
|
||||
# Using PostgreSQL from postgres stack
|
||||
DB_HOST=postgres
|
||||
DB_PORT={{ db_port | default('5432') }}
|
||||
DB_DATABASE={{ db_name | default(db_name_default) }}
|
||||
DB_USERNAME={{ db_user | default(db_user_default) }}
|
||||
DB_PASSWORD={{ db_password }}
|
||||
# Legacy variables (kept for backward compatibility)
|
||||
DB_NAME={{ db_name | default(db_name_default) }}
|
||||
DB_USER={{ db_user | default(db_user_default) }}
|
||||
DB_PASS={{ db_password }}
|
||||
|
||||
@@ -289,12 +289,16 @@ services:
|
||||
volumes:
|
||||
app-code:
|
||||
name: app-code
|
||||
external: true
|
||||
app-storage:
|
||||
name: app-storage
|
||||
external: true
|
||||
app-logs:
|
||||
name: app-logs
|
||||
external: true
|
||||
redis-data:
|
||||
name: redis-data
|
||||
external: true
|
||||
|
||||
networks:
|
||||
traefik-public:
|
||||
|
||||
Reference in New Issue
Block a user