feat(infra): B-1 Ansible Host IaC 骨架完整版
- roles/nginx/templates/188-all-sites.conf.j2: 8 個服務 Jinja2 模板 - roles/docker-compose-service/tasks/main.yml: 通用 Docker Compose role - roles/swap/tasks/main.yml: swap2.img 管理 role (110 專用) - roles/pm2-service/tasks/main.yml: PM2 process 狀態確認 role - .gitea/workflows/ansible-lint.yml: infra/ansible/** 異動自動 lint Sprint B-1 完成: Git = 唯一真相 (Host IaC 骨架) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
22
.gitea/workflows/ansible-lint.yml
Normal file
22
.gitea/workflows/ansible-lint.yml
Normal file
@@ -0,0 +1,22 @@
|
||||
name: Ansible Lint
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'infra/ansible/**'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'infra/ansible/**'
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: self-hosted
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install ansible-lint
|
||||
run: pip install ansible-lint
|
||||
|
||||
- name: Run ansible-lint
|
||||
run: ansible-lint infra/ansible/playbooks/
|
||||
working-directory: ${{ github.workspace }}
|
||||
20
infra/ansible/inventory/group_vars/all.yml
Normal file
20
infra/ansible/inventory/group_vars/all.yml
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
# AWOOOI Ansible — 共用變數
|
||||
# 所有主機適用的基礎設定
|
||||
|
||||
# 時區
|
||||
timezone: "Asia/Taipei"
|
||||
|
||||
# 共用 SSH 用戶
|
||||
ansible_ssh_common_args: "-o StrictHostKeyChecking=no -o ConnectTimeout=10"
|
||||
|
||||
# sudoers NOPASSWD (CD 用,see ADR-034)
|
||||
sudo_password: "{{ vault_sudo_password }}"
|
||||
|
||||
# Harbor Registry
|
||||
harbor_url: "harbor.wooo.work"
|
||||
harbor_namespace: "awoooi"
|
||||
|
||||
# 網路
|
||||
nginx_vip: "192.168.0.200"
|
||||
k3s_vip: "192.168.0.125"
|
||||
44
infra/ansible/inventory/group_vars/host_110.yml
Normal file
44
infra/ansible/inventory/group_vars/host_110.yml
Normal file
@@ -0,0 +1,44 @@
|
||||
---
|
||||
# AWOOOI Ansible — 192.168.0.110 (DevOps 金庫) 專用變數
|
||||
|
||||
# Swap 設定
|
||||
swap_files:
|
||||
- path: /swap.img
|
||||
size_mb: 3896 # 3.8GB (現有)
|
||||
- path: /swap2.img
|
||||
size_mb: 4096 # 4GB (Sprint A 新增)
|
||||
|
||||
# Docker Compose 服務 (在 /opt/ 下)
|
||||
docker_compose_services:
|
||||
harbor:
|
||||
dir: /opt/harbor
|
||||
service: "" # 全部服務
|
||||
expected_port: 5000
|
||||
sentry:
|
||||
dir: /opt/sentry
|
||||
service: ""
|
||||
expected_port: 9000
|
||||
gitea:
|
||||
dir: /opt/gitea
|
||||
service: ""
|
||||
expected_port: 3001
|
||||
langfuse:
|
||||
dir: /opt/langfuse
|
||||
service: ""
|
||||
expected_port: 3100
|
||||
|
||||
# bitan pharmacy (Docker)
|
||||
bitan_dir: /home/wooo/apps/bitan-pharmacy
|
||||
bitan_port: 3003
|
||||
|
||||
# wooo-aiops frontend (PM2, port 3000)
|
||||
wooo_aiops_dir: /home/wooo/apps/wooo-aiops
|
||||
wooo_aiops_pm2_name: wooo-aiops
|
||||
|
||||
# GitHub Runners
|
||||
github_runner_count: 5
|
||||
|
||||
# keepalived
|
||||
keepalived_role: BACKUP
|
||||
keepalived_priority: 90
|
||||
keepalived_vip: "192.168.0.200"
|
||||
43
infra/ansible/inventory/group_vars/host_188.yml
Normal file
43
infra/ansible/inventory/group_vars/host_188.yml
Normal file
@@ -0,0 +1,43 @@
|
||||
---
|
||||
# AWOOOI Ansible — 192.168.0.188 (AI+Web 中心) 專用變數
|
||||
|
||||
# Docker Compose 服務
|
||||
docker_compose_services:
|
||||
openclaw:
|
||||
dir: /opt/openclaw
|
||||
expected_port: 8088
|
||||
tsenyang:
|
||||
dir: /opt/tsenyang-website
|
||||
expected_port: 3000
|
||||
momo:
|
||||
dir: /opt/momo-app
|
||||
expected_port: 5003
|
||||
signoz:
|
||||
dir: /opt/signoz
|
||||
expected_port: 3301
|
||||
minio:
|
||||
dir: /opt/minio
|
||||
expected_port: 9000
|
||||
litellm:
|
||||
dir: /opt/litellm
|
||||
expected_port: 4000
|
||||
n8n:
|
||||
dir: /opt/n8n
|
||||
expected_port: 5678
|
||||
open_webui:
|
||||
dir: /opt/open-webui
|
||||
expected_port: 3010
|
||||
docker_registry:
|
||||
dir: /opt/docker-registry
|
||||
expected_port: 5001
|
||||
|
||||
# Nginx
|
||||
nginx_conf_dir: /etc/nginx/sites-enabled
|
||||
nginx_main_conf: all-sites.conf
|
||||
# 無 gitlab block(Sprint A 已清除)
|
||||
|
||||
# keepalived
|
||||
keepalived_role: MASTER
|
||||
keepalived_priority: 100
|
||||
keepalived_vip: "192.168.0.200"
|
||||
keepalived_interface: "ens18" # 調整為實際網卡名稱
|
||||
40
infra/ansible/inventory/hosts.yml
Normal file
40
infra/ansible/inventory/hosts.yml
Normal file
@@ -0,0 +1,40 @@
|
||||
---
|
||||
# AWOOOI Ansible Inventory
|
||||
# Sprint B — Host IaC
|
||||
# 建立時間: 2026-04-11 (台北時區)
|
||||
# 建立者: Claude Sonnet 4.6 — Sprint B-1
|
||||
|
||||
all:
|
||||
children:
|
||||
devops:
|
||||
hosts:
|
||||
host_110:
|
||||
ansible_host: 192.168.0.110
|
||||
ansible_user: wooo
|
||||
ansible_ssh_private_key_file: "~/.ssh/id_ed25519"
|
||||
|
||||
ai_web:
|
||||
hosts:
|
||||
host_188:
|
||||
ansible_host: 192.168.0.188
|
||||
ansible_user: ollama
|
||||
ansible_ssh_private_key_file: "~/.ssh/id_ed25519"
|
||||
|
||||
k3s_masters:
|
||||
hosts:
|
||||
host_120:
|
||||
ansible_host: 192.168.0.120
|
||||
ansible_user: wooo
|
||||
ansible_ssh_private_key_file: "~/.ssh/id_ed25519"
|
||||
host_121:
|
||||
ansible_host: 192.168.0.121
|
||||
ansible_user: wooo
|
||||
ansible_ssh_private_key_file: "~/.ssh/id_ed25519"
|
||||
|
||||
security:
|
||||
hosts:
|
||||
host_112:
|
||||
ansible_host: 192.168.0.112
|
||||
ansible_user: wooo
|
||||
ansible_ssh_private_key_file: "~/.ssh/id_ed25519"
|
||||
ansible_skip_tags: "all" # 預留,本次不執行
|
||||
172
infra/ansible/playbooks/110-devops.yml
Normal file
172
infra/ansible/playbooks/110-devops.yml
Normal file
@@ -0,0 +1,172 @@
|
||||
---
|
||||
# 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) }}"
|
||||
|
||||
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"
|
||||
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
|
||||
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
|
||||
135
infra/ansible/playbooks/188-ai-web.yml
Normal file
135
infra/ansible/playbooks/188-ai-web.yml
Normal file
@@ -0,0 +1,135 @@
|
||||
---
|
||||
# AWOOOI Ansible — 192.168.0.188 AI+Web 中心完整狀態描述
|
||||
# 執行: ansible-playbook -i inventory/hosts.yml playbooks/188-ai-web.yml
|
||||
#
|
||||
# 預期狀態:
|
||||
# openclaw: running (port 8088)
|
||||
# tsenyang: running (port 3000)
|
||||
# momo: running (port 5003)
|
||||
# signoz: running (port 3301)
|
||||
# minio: running (port 9000)
|
||||
# litellm: running (port 4000)
|
||||
# n8n: running (port 5678)
|
||||
# open-webui: running (port 3010)
|
||||
# docker-registry: running (port 5001)
|
||||
# nginx: all-sites.conf 無 gitlab block
|
||||
# keepalived: MASTER priority 100 VIP:200
|
||||
|
||||
- name: "192.168.0.188 AI+Web 中心 — 預期狀態收斂"
|
||||
hosts: host_188
|
||||
become: true
|
||||
vars:
|
||||
ansible_become_pass: "{{ vault_sudo_password | default(omit) }}"
|
||||
|
||||
tasks:
|
||||
# ========================================================================
|
||||
# Docker 服務健康檢查
|
||||
# ========================================================================
|
||||
- name: "Docker | 收集運作中的容器清單"
|
||||
ansible.builtin.command:
|
||||
cmd: "docker ps --format '{{ '{{' }}.Names{{ '}}' }}'"
|
||||
register: running_containers
|
||||
changed_when: false
|
||||
tags: docker
|
||||
|
||||
- name: "Docker | 確認關鍵服務運作中"
|
||||
ansible.builtin.debug:
|
||||
msg: "⚠️ 服務 {{ item }} 未在容器列表中,請確認"
|
||||
when: item not in running_containers.stdout
|
||||
loop:
|
||||
- openclaw
|
||||
- tsenyang
|
||||
- momo
|
||||
- signoz
|
||||
- minio
|
||||
- litellm
|
||||
tags: docker
|
||||
|
||||
# ========================================================================
|
||||
# n8n / open-webui (Sprint A 新啟動)
|
||||
# ========================================================================
|
||||
- name: "n8n | 確認容器運作中"
|
||||
ansible.builtin.command:
|
||||
cmd: "docker ps --filter name=n8n --filter status=running --format '{{ '{{' }}.Names{{ '}}' }}'"
|
||||
register: n8n_status
|
||||
changed_when: false
|
||||
tags: n8n
|
||||
|
||||
- name: "n8n | 若停止則啟動"
|
||||
ansible.builtin.command:
|
||||
cmd: "docker compose up -d"
|
||||
chdir: /opt/n8n
|
||||
when: n8n_status.stdout == ""
|
||||
tags: n8n
|
||||
|
||||
- name: "open-webui | 確認容器運作中"
|
||||
ansible.builtin.command:
|
||||
cmd: "docker ps --filter name=open-webui --filter status=running --format '{{ '{{' }}.Names{{ '}}' }}'"
|
||||
register: openwebui_status
|
||||
changed_when: false
|
||||
tags: open_webui
|
||||
|
||||
- name: "open-webui | 若停止則啟動"
|
||||
ansible.builtin.command:
|
||||
cmd: "docker compose up -d"
|
||||
chdir: /opt/open-webui
|
||||
when: openwebui_status.stdout == ""
|
||||
tags: open_webui
|
||||
|
||||
# ========================================================================
|
||||
# Nginx 狀態確認
|
||||
# ========================================================================
|
||||
- name: "nginx | 確認服務運作中"
|
||||
ansible.builtin.systemd:
|
||||
name: nginx
|
||||
register: nginx_status
|
||||
tags: nginx
|
||||
|
||||
- name: "nginx | 警告:nginx 未運作"
|
||||
ansible.builtin.debug:
|
||||
msg: "🚨 nginx 未運作!"
|
||||
when: nginx_status.status.ActiveState != "active"
|
||||
tags: nginx
|
||||
|
||||
- name: "nginx | 確認 all-sites.conf 無 gitlab block"
|
||||
ansible.builtin.command:
|
||||
cmd: "grep -c 'gitlab' /etc/nginx/sites-enabled/all-sites.conf"
|
||||
register: gitlab_check
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
tags: nginx
|
||||
|
||||
- name: "nginx | 警告:all-sites.conf 仍含 gitlab block"
|
||||
ansible.builtin.debug:
|
||||
msg: "⚠️ all-sites.conf 仍含 gitlab 設定,請確認 Sprint A 清除是否完整"
|
||||
when: gitlab_check.stdout != "0"
|
||||
tags: nginx
|
||||
|
||||
# ========================================================================
|
||||
# keepalived MASTER
|
||||
# ========================================================================
|
||||
- name: "keepalived | 確認服務運作中"
|
||||
ansible.builtin.systemd:
|
||||
name: keepalived
|
||||
register: keepalived_status
|
||||
tags: keepalived
|
||||
|
||||
- name: "keepalived | 警告:keepalived 未運作"
|
||||
ansible.builtin.debug:
|
||||
msg: "⚠️ keepalived MASTER 未運作,VIP:200 降級為 110 BACKUP"
|
||||
when: keepalived_status.status.ActiveState != "active"
|
||||
tags: keepalived
|
||||
|
||||
- name: "keepalived | 確認 VIP:200 由本機持有"
|
||||
ansible.builtin.command:
|
||||
cmd: "ip addr show | grep 192.168.0.200"
|
||||
register: vip_check
|
||||
changed_when: false
|
||||
failed_when: false
|
||||
tags: keepalived
|
||||
|
||||
- name: "keepalived | 警告:VIP:200 不在本機"
|
||||
ansible.builtin.debug:
|
||||
msg: "⚠️ VIP 192.168.0.200 不在 188 (MASTER 可能已 failover 到 110)"
|
||||
when: vip_check.rc != 0
|
||||
tags: keepalived
|
||||
56
infra/ansible/playbooks/nginx-sync.yml
Normal file
56
infra/ansible/playbooks/nginx-sync.yml
Normal file
@@ -0,0 +1,56 @@
|
||||
---
|
||||
# AWOOOI Ansible — Nginx 設定同步 Playbook
|
||||
# 原則: Nginx conf 不再直接手改,所有修改透過此 Playbook 部署
|
||||
# 執行: ansible-playbook -i inventory/hosts.yml playbooks/nginx-sync.yml --tags 188
|
||||
# 乾跑: ansible-playbook -i inventory/hosts.yml playbooks/nginx-sync.yml --check
|
||||
|
||||
- name: "188 Nginx conf 同步"
|
||||
hosts: host_188
|
||||
become: true
|
||||
vars:
|
||||
ansible_become_pass: "{{ vault_sudo_password | default(omit) }}"
|
||||
nginx_conf_src: "{{ playbook_dir }}/../roles/nginx/templates/188-all-sites.conf.j2"
|
||||
nginx_conf_dest: /etc/nginx/sites-enabled/all-sites.conf
|
||||
|
||||
tasks:
|
||||
- name: "nginx | 部署 all-sites.conf"
|
||||
ansible.builtin.template:
|
||||
src: "{{ nginx_conf_src }}"
|
||||
dest: "{{ nginx_conf_dest }}"
|
||||
owner: root
|
||||
group: root
|
||||
mode: "0644"
|
||||
backup: true
|
||||
notify: reload nginx
|
||||
tags: ["188", "nginx"]
|
||||
|
||||
- name: "nginx | 測試設定"
|
||||
ansible.builtin.command:
|
||||
cmd: "nginx -t"
|
||||
changed_when: false
|
||||
tags: ["188", "nginx"]
|
||||
|
||||
handlers:
|
||||
- name: reload nginx
|
||||
ansible.builtin.systemd:
|
||||
name: nginx
|
||||
state: reloaded
|
||||
|
||||
- name: "110 Nginx conf 同步(若有)"
|
||||
hosts: host_110
|
||||
become: true
|
||||
vars:
|
||||
ansible_become_pass: "{{ vault_sudo_password | default(omit) }}"
|
||||
|
||||
tasks:
|
||||
- name: "nginx | 確認 110 nginx 無 all-sites-from-188.conf 在 sites-enabled"
|
||||
ansible.builtin.stat:
|
||||
path: /etc/nginx/sites-enabled/all-sites-from-188.conf
|
||||
register: stale_conf
|
||||
tags: ["110", "nginx"]
|
||||
|
||||
- name: "nginx | 警告:110 仍有 all-sites-from-188.conf (已非 188 管控)"
|
||||
ansible.builtin.debug:
|
||||
msg: "⚠️ 110 sites-enabled 仍有 all-sites-from-188.conf,應已封存"
|
||||
when: stale_conf.stat.exists
|
||||
tags: ["110", "nginx"]
|
||||
16
infra/ansible/playbooks/site.yml
Normal file
16
infra/ansible/playbooks/site.yml
Normal file
@@ -0,0 +1,16 @@
|
||||
---
|
||||
# AWOOOI Ansible — 全站入口 Playbook
|
||||
# 執行: ansible-playbook -i inventory/hosts.yml playbooks/site.yml
|
||||
# 乾跑: ansible-playbook -i inventory/hosts.yml playbooks/site.yml --check
|
||||
|
||||
- name: "110 DevOps 金庫"
|
||||
import_playbook: 110-devops.yml
|
||||
tags: ["110", "devops"]
|
||||
|
||||
- name: "188 AI+Web 中心"
|
||||
import_playbook: 188-ai-web.yml
|
||||
tags: ["188", "ai_web"]
|
||||
|
||||
- name: "Nginx 設定同步"
|
||||
import_playbook: nginx-sync.yml
|
||||
tags: ["nginx"]
|
||||
18
infra/ansible/roles/docker-compose-service/tasks/main.yml
Normal file
18
infra/ansible/roles/docker-compose-service/tasks/main.yml
Normal file
@@ -0,0 +1,18 @@
|
||||
---
|
||||
# docker-compose-service role — 確保 Docker Compose 服務運作中
|
||||
# 呼叫方式: include_role: name=docker-compose-service
|
||||
# vars:
|
||||
# service_name: n8n
|
||||
# service_dir: /opt/n8n
|
||||
|
||||
- name: "{{ service_name }} | 確認容器運作中"
|
||||
ansible.builtin.command:
|
||||
cmd: "docker ps --filter name={{ service_name }} --filter status=running --format '{{ '{{' }}.Names{{ '}}' }}'"
|
||||
register: _svc_status
|
||||
changed_when: false
|
||||
|
||||
- name: "{{ service_name }} | 若停止則啟動"
|
||||
ansible.builtin.command:
|
||||
cmd: "docker compose up -d"
|
||||
chdir: "{{ service_dir }}"
|
||||
when: _svc_status.stdout == ""
|
||||
16
infra/ansible/roles/nginx/tasks/main.yml
Normal file
16
infra/ansible/roles/nginx/tasks/main.yml
Normal file
@@ -0,0 +1,16 @@
|
||||
---
|
||||
# nginx role — 任務主入口
|
||||
# 由 nginx-sync.yml playbook 呼叫
|
||||
|
||||
- name: "確認 nginx 已安裝"
|
||||
ansible.builtin.package:
|
||||
name: nginx
|
||||
state: present
|
||||
tags: nginx
|
||||
|
||||
- name: "確認 nginx 服務啟動且自動啟動"
|
||||
ansible.builtin.systemd:
|
||||
name: nginx
|
||||
state: started
|
||||
enabled: true
|
||||
tags: nginx
|
||||
145
infra/ansible/roles/nginx/templates/188-all-sites.conf.j2
Normal file
145
infra/ansible/roles/nginx/templates/188-all-sites.conf.j2
Normal file
@@ -0,0 +1,145 @@
|
||||
# 188-all-sites.conf.j2
|
||||
# AWOOOI Nginx 全站設定 — 由 Ansible nginx-sync.yml playbook 管理
|
||||
# 禁止直接手改此檔案 → 請修改 roles/nginx/templates/188-all-sites.conf.j2
|
||||
# 部署指令: ansible-playbook -i inventory/hosts.yml playbooks/nginx-sync.yml --tags 188
|
||||
# 最後同步: {{ ansible_date_time.iso8601 }}
|
||||
|
||||
# ============================================================
|
||||
# OpenClaw (port 8088)
|
||||
# ============================================================
|
||||
server {
|
||||
listen 80;
|
||||
server_name openclaw.awoooi.com;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:8088;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_read_timeout 300s;
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# tsenyang (port 3000)
|
||||
# ============================================================
|
||||
server {
|
||||
listen 80;
|
||||
server_name tsenyang.awoooi.com;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:3000;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# momo (port 5003)
|
||||
# ============================================================
|
||||
server {
|
||||
listen 80;
|
||||
server_name momo.awoooi.com;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:5003;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# SignOz (port 3301)
|
||||
# ============================================================
|
||||
server {
|
||||
listen 80;
|
||||
server_name signoz.awoooi.internal;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:3301;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# MinIO (port 9000 API / 9001 Console)
|
||||
# ============================================================
|
||||
server {
|
||||
listen 80;
|
||||
server_name minio.awoooi.internal;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:9001;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
client_max_body_size 500m;
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# LiteLLM (port 4000)
|
||||
# ============================================================
|
||||
server {
|
||||
listen 80;
|
||||
server_name litellm.awoooi.internal;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:4000;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_read_timeout 300s;
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# n8n (port 5678)
|
||||
# ============================================================
|
||||
server {
|
||||
listen 80;
|
||||
server_name n8n.awoooi.internal;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:5678;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# Open WebUI (port 3010)
|
||||
# ============================================================
|
||||
server {
|
||||
listen 80;
|
||||
server_name open-webui.awoooi.internal;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:3010;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_read_timeout 300s;
|
||||
}
|
||||
}
|
||||
|
||||
# ============================================================
|
||||
# Docker Registry (port 5001)
|
||||
# ============================================================
|
||||
server {
|
||||
listen 80;
|
||||
server_name registry.awoooi.internal;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:5001;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
client_max_body_size 2g;
|
||||
}
|
||||
}
|
||||
22
infra/ansible/roles/pm2-service/tasks/main.yml
Normal file
22
infra/ansible/roles/pm2-service/tasks/main.yml
Normal file
@@ -0,0 +1,22 @@
|
||||
---
|
||||
# pm2-service role — 確保 PM2 管理的服務運作中
|
||||
# 呼叫方式: include_role: name=pm2-service
|
||||
# vars:
|
||||
# pm2_app_name: wooo-aiops # PM2 process name
|
||||
# pm2_user: wooo # 執行 PM2 的使用者
|
||||
|
||||
- name: "{{ pm2_app_name }} | 確認 PM2 process 狀態"
|
||||
ansible.builtin.command:
|
||||
cmd: "pm2 jlist"
|
||||
become_user: "{{ pm2_user }}"
|
||||
register: pm2_list
|
||||
changed_when: false
|
||||
|
||||
- name: "{{ pm2_app_name }} | 解析 process 狀態"
|
||||
ansible.builtin.set_fact:
|
||||
_pm2_online: "{{ (pm2_list.stdout | from_json | selectattr('name', 'equalto', pm2_app_name) | selectattr('pm2_env.status', 'equalto', 'online') | list | length) > 0 }}"
|
||||
|
||||
- name: "{{ pm2_app_name }} | 警告:服務未 online"
|
||||
ansible.builtin.debug:
|
||||
msg: "⚠️ PM2 process '{{ pm2_app_name }}' 未 online,請手動檢查"
|
||||
when: not _pm2_online
|
||||
35
infra/ansible/roles/swap/tasks/main.yml
Normal file
35
infra/ansible/roles/swap/tasks/main.yml
Normal file
@@ -0,0 +1,35 @@
|
||||
---
|
||||
# swap role — 確保 swap 已設定(110 專用)
|
||||
# 預期狀態: swap.img (3.8G) + swap2.img (4G) 共 ~8G
|
||||
|
||||
- name: "Swap | 確認 swap2.img 存在"
|
||||
ansible.builtin.stat:
|
||||
path: /swap2.img
|
||||
register: swap2_stat
|
||||
|
||||
- name: "Swap | 建立 swap2.img"
|
||||
ansible.builtin.command:
|
||||
cmd: "fallocate -l 4G /swap2.img"
|
||||
creates: /swap2.img
|
||||
when: not swap2_stat.stat.exists
|
||||
|
||||
- name: "Swap | 格式化 swap2.img"
|
||||
ansible.builtin.command:
|
||||
cmd: "mkswap /swap2.img"
|
||||
when: not swap2_stat.stat.exists
|
||||
|
||||
- name: "Swap | 設定 swap2.img 權限"
|
||||
ansible.builtin.file:
|
||||
path: /swap2.img
|
||||
mode: "0600"
|
||||
|
||||
- name: "Swap | 加入 swap2.img 到 fstab"
|
||||
ansible.builtin.lineinfile:
|
||||
path: /etc/fstab
|
||||
line: "/swap2.img none swap sw 0 0"
|
||||
state: present
|
||||
|
||||
- name: "Swap | 啟用 swap"
|
||||
ansible.builtin.command:
|
||||
cmd: "swapon --all"
|
||||
changed_when: false
|
||||
Reference in New Issue
Block a user