--- # AWOOOI Ansible — 192.168.0.110 DevOps 金庫完整狀態描述 # 執行: ansible-playbook -i inventory/hosts.yml playbooks/110-devops.yml # # 預期狀態: # swap: 8GB (swap.img 3.8G + swap2.img 4G) # harbor: running (docker compose, port 5000) # sentry: running (docker compose, 0 restarting) # gitea: running (port 3001) # langfuse: running (port 3100) # bitan: running docker (port 3003) # wooo-aiops: running PM2 (port 3000) # runners: 5x GitHub Actions runners (systemd) # nginx: harbor conf -> 127.0.0.1:5000 # keepalived: BACKUP priority 90 VIP:200 - name: "192.168.0.110 DevOps 金庫 — 預期狀態收斂" hosts: host_110 become: true vars: ansible_become_pass: "{{ vault_sudo_password | default(omit) }}" roles: - role: cold-start-monitor tags: cold_start_monitor - role: runner-guardrails tags: runner_guardrails - role: host-textfile-exporters vars: host_textfile_user: wooo host_textfile_host_label: "110" host_textfile_manage_runaway_process: true host_textfile_manage_systemd_units: true host_textfile_systemd_unit_glob: "actions.runner.*.service" host_textfile_systemd_units: - docker.service - nginx.service - prometheus.service - alertmanager.service - gitea.service tags: textfile_exporters tasks: # ======================================================================== # Swap 檢查 # ======================================================================== - name: "Swap | 確認 swap.img 存在" ansible.builtin.stat: path: /swap.img register: swap1_stat tags: swap - name: "Swap | 確認 swap2.img 存在" ansible.builtin.stat: path: /swap2.img register: swap2_stat tags: swap - name: "Swap | 建立 swap2.img (若不存在)" ansible.builtin.command: cmd: "fallocate -l 4G /swap2.img" creates: /swap2.img when: not swap2_stat.stat.exists tags: swap - name: "Swap | 格式化 swap2.img (若剛建立)" ansible.builtin.command: cmd: "mkswap /swap2.img" changed_when: true when: not swap2_stat.stat.exists tags: swap - name: "Swap | 設定 swap2.img 權限" ansible.builtin.file: path: /swap2.img mode: "0600" tags: swap - name: "Swap | 加入 swap2.img 到 fstab" ansible.builtin.lineinfile: path: /etc/fstab line: "/swap2.img none swap sw 0 0" state: present tags: swap - name: "Swap | 啟用 swap" ansible.builtin.command: cmd: "swapon --all" changed_when: false tags: swap # ======================================================================== # Docker 服務健康檢查 # ======================================================================== - name: "Docker | 確認 Harbor 容器運作中" ansible.builtin.command: cmd: "docker ps --filter name=harbor --filter status=running --format '{{ '{{' }}.Names{{ '}}' }}'" register: harbor_status changed_when: false tags: docker - name: "Docker | Harbor 狀態警告" ansible.builtin.debug: msg: "⚠️ Harbor 容器未完全運作,請手動檢查 /opt/harbor" when: harbor_status.stdout == "" tags: docker - name: "Docker | 確認 Gitea 容器運作中" ansible.builtin.command: cmd: "docker ps --filter name=gitea --filter status=running --format '{{ '{{' }}.Names{{ '}}' }}'" register: gitea_status changed_when: false tags: docker - name: "Docker | 確認 Sentry 無 restart 容器" ansible.builtin.command: cmd: "docker ps --filter status=restarting --filter name=sentry --format '{{ '{{' }}.Names{{ '}}' }}'" register: sentry_restarting changed_when: false tags: docker - name: "Docker | Sentry crash loop 警告" ansible.builtin.debug: msg: "⚠️ Sentry 容器 crash loop: {{ sentry_restarting.stdout }}" when: sentry_restarting.stdout != "" tags: docker # ======================================================================== # bitan pharmacy Docker 服務 # ======================================================================== - name: "Bitan | 確認 bitan container 運作中" ansible.builtin.command: cmd: "docker ps --filter name=bitan --filter status=running --format '{{ '{{' }}.Names{{ '}}' }}'" register: bitan_status changed_when: false tags: bitan - name: "Bitan | 若停止則啟動" ansible.builtin.command: cmd: "docker compose up -d" chdir: /home/wooo/apps/bitan-pharmacy changed_when: true when: bitan_status.stdout == "" tags: bitan # ======================================================================== # GitHub Runners # ======================================================================== - name: "Runners | 確認 runner 服務狀態" ansible.builtin.systemd: name: "github-runner-{{ item }}" register: runner_status loop: "{{ range(1, github_runner_count + 1) | list }}" ignore_errors: true tags: runners # ======================================================================== # keepalived # ======================================================================== - name: "Keepalived | 確認服務運作中" ansible.builtin.systemd: name: keepalived register: keepalived_status tags: keepalived - name: "Keepalived | 警告:keepalived 未運作" ansible.builtin.debug: msg: "⚠️ keepalived 未運作,VIP 200 可能失效" when: keepalived_status.status.ActiveState != "active" tags: keepalived # ======================================================================== # Nginx harbor conf 指向確認 # ======================================================================== - name: "Nginx | 確認 harbor nginx conf 存在" ansible.builtin.stat: path: /etc/nginx/sites-enabled/harbor.conf register: harbor_nginx tags: nginx - name: "Nginx | 確認 harbor conf 指向 :5000 (非 :5050)" ansible.builtin.command: cmd: "grep -c ':5050' /etc/nginx/sites-enabled/harbor.conf" register: harbor_conf_check changed_when: false failed_when: false when: harbor_nginx.stat.exists tags: nginx - name: "Nginx | 警告:harbor conf 仍指向 :5050" ansible.builtin.debug: msg: "⚠️ harbor nginx conf 仍有 :5050,請確認已修正為 :5000" when: harbor_nginx.stat.exists and harbor_conf_check.stdout != "0" tags: nginx # ======================================================================== # Prometheus rule source/runtime convergence # ======================================================================== - name: "Monitoring | 部署 Prometheus rule drift guard" ansible.builtin.copy: src: "{{ playbook_dir }}/../../../scripts/ops/prometheus-rule-drift-guard.sh" dest: /home/wooo/scripts/prometheus-rule-drift-guard.sh owner: wooo group: wooo mode: "0755" tags: - monitoring - textfile_exporters - name: "Monitoring | 安裝 Prometheus rule drift guard cron" ansible.builtin.cron: name: "AWOOOI Prometheus rule drift guard" user: wooo minute: "*/5" job: "/home/wooo/scripts/prometheus-rule-drift-guard.sh >/tmp/awoooi-prometheus-rule-drift-guard.log 2>&1" tags: - monitoring - textfile_exporters # ======================================================================== # Backup / offsite source-of-truth # ======================================================================== - name: "Backup | 確認 /backup/scripts 存在" ansible.builtin.file: path: /backup/scripts state: directory owner: wooo group: wooo mode: "0755" tags: backup_jobs - name: "Backup | 部署 110 備份腳本" ansible.builtin.copy: src: "{{ playbook_dir }}/../../../scripts/backup/{{ item }}" dest: "/backup/scripts/{{ item }}" owner: wooo group: wooo mode: "0755" loop: - common.sh - backup-all.sh - backup-status.sh - backup-gitea.sh - gitea-repo-bundle-backup.sh - gitea-bundle-backup-sync-188.sh - backup-harbor.sh - backup-momo.sh - backup-awoooi.sh - backup-awoooi-frequent.sh - backup-configs.sh - check-backup-integrity.sh - verify-gitea-backup.sh - sync-offsite-backups.sh - offsite-escrow-evidence-report.sh - verify-offsite-full-sync.sh tags: backup_jobs - name: "Backup | 安裝 AWOOOI daily backup Telegram heartbeat" ansible.builtin.cron: name: "AWOOOI daily backup Telegram heartbeat" user: wooo minute: "5" hour: "6" job: "/backup/scripts/backup-status.sh >/tmp/awoooi-backup-status.cron.log 2>&1" tags: backup_jobs - name: "Backup | 安裝 Gitea bundle 每日本機產生、188 同步與還原驗證 cron" ansible.builtin.cron: name: "AWOOOI Gitea bundle daily backup sync and restore verifier" user: wooo minute: "15" hour: "0" job: >- PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin /backup/scripts/gitea-bundle-backup-sync-188.sh >> /backup/logs/gitea-bundle-sync-188.log 2>&1 tags: backup_jobs - name: "Backup | 安裝 offsite escrow evidence report cron" ansible.builtin.cron: name: "AWOOOI offsite escrow evidence report" user: wooo minute: "10" hour: "7" job: "/backup/scripts/offsite-escrow-evidence-report.sh --no-color >/tmp/awoooi-offsite-escrow-evidence.cron.log 2>&1" tags: backup_jobs - name: "Backup | 安裝 offsite full sync verifier cron" ansible.builtin.cron: name: "AWOOOI offsite full sync verifier" user: wooo minute: "20" hour: "7" job: "/backup/scripts/verify-offsite-full-sync.sh --write-textfile --no-color >/tmp/awoooi-offsite-full-sync-verify.cron.log 2>&1" tags: backup_jobs