From d47cd6ff5e627978979d2e0d09df35e60372b962 Mon Sep 17 00:00:00 2001 From: Haitao Pan Date: Mon, 22 Dec 2025 20:18:49 +0800 Subject: [PATCH] Add inventory files and update XControl deploy config --- ansible.cfg | 26 ++++++ deploy_xcontrol_web.yml | 3 + group_vars/all.yml | 5 + hosts/all | 19 ++++ hosts/gpu_k8s_cluster | 10 ++ hosts/k3s-cluster | 12 +++ hosts/vpn | 2 + roles/docker/XControl/defaults/main.yml | 6 ++ roles/docker/XControl/files/nginx/nginx.conf | 2 +- .../XControl/templates/docker-compose.yaml | 32 +++++-- .../templates/nginx/conf.d/default.conf | 93 ++++++++++++++++++- 11 files changed, 199 insertions(+), 11 deletions(-) create mode 100644 ansible.cfg create mode 100644 group_vars/all.yml create mode 100644 hosts/all create mode 100644 hosts/gpu_k8s_cluster create mode 100644 hosts/k3s-cluster create mode 100644 hosts/vpn diff --git a/ansible.cfg b/ansible.cfg new file mode 100644 index 0000000..784e1f6 --- /dev/null +++ b/ansible.cfg @@ -0,0 +1,26 @@ +[defaults] +# 常用参数 +inventory = ./inventory # 默认清单文件路径,可按需改 +vault_password_file = ~/.vault_password +timeout = 10 +forks = 10 +poll_interval = 10 +transport = smart +gathering = smart + +# 输出配置:推荐 yaml,兼容性最好 +stdout_callback = yaml +bin_ansible_callbacks = True +callbacks_enabled = profile_tasks,timer + +# Python 解释器 +ansible_python_interpreter = /usr/bin/python3 + +# 其他常用设置 +host_key_checking = False +deprecation_warnings = False + +[inventory] +cache = True +cache_plugin = jsonfile +cache_timeout = 3600 diff --git a/deploy_xcontrol_web.yml b/deploy_xcontrol_web.yml index 09639ce..1478ec1 100644 --- a/deploy_xcontrol_web.yml +++ b/deploy_xcontrol_web.yml @@ -3,6 +3,9 @@ become: true vars: group: mail + xcontrol_dashboard_active_color: blue + xcontrol_dashboard_blue_image: manbuzhe2009/dashboard:latest + xcontrol_dashboard_green_image: manbuzhe2009/dashboard:latest roles: #- roles/vhosts/common/ - roles/vhosts/nodejs/ diff --git a/group_vars/all.yml b/group_vars/all.yml new file mode 100644 index 0000000..f04e15a --- /dev/null +++ b/group_vars/all.yml @@ -0,0 +1,5 @@ +ansible_port: 22 +ansible_ssh_user: ubuntu +ansible_ssh_private_key_file: ~/.ssh/id_rsa +ansible_host_key_checking: False + diff --git a/hosts/all b/hosts/all new file mode 100644 index 0000000..c8aba05 --- /dev/null +++ b/hosts/all @@ -0,0 +1,19 @@ +[all] +hw-node.svc.plus ansible_host=139.9.139.22 ansible_ssh_user=root +cn-gateway.svc.plus ansible_host=8.130.10.142 ansible_ssh_user=root +us-gateway.svc.plus ansible_host=52.196.108.28 ansible_ssh_user=ubuntu +global-gateway.svc.plus ansible_host=54.183.199.99 ansible_ssh_user=ubuntu +canada-gateway.svc.plus ansible_host=3.96.167.208 ansible_ssh_user=ubuntu +vault.onwalk.net ansible_host=3.101.151.231 ansible_ssh_user=ubuntu +ldap.svc.plus ansible_host=35.182.63.247 ansible_ssh_user=ubuntu +keycloak.svc.plus ansible_host=3.99.126.158 ansible_ssh_user=ubuntu +observability.onwalk.net ansible_host=54.153.80.120 ansible_ssh_user=ubuntu +argocd.svc.plus ansible_host=13.57.247.27 ansible_ssh_user=ubuntu + +[gateway] +vpn-gateway.svc.plus ansible_host=167.179.72.223 ansible_ssh_user=root + +[all:vars] +ansible_port=22 +ansible_ssh_private_key_file=~/.ssh/id_rsa +ansible_host_key_checking=False diff --git a/hosts/gpu_k8s_cluster b/hosts/gpu_k8s_cluster new file mode 100644 index 0000000..36e0b65 --- /dev/null +++ b/hosts/gpu_k8s_cluster @@ -0,0 +1,10 @@ +[all] +k8s-1 ansible_host=13.158.69.227 +k8s-2 ansible_host=57.183.6.87 +k8s-3 ansible_host=43.207.133.165 + +[all:vars] +ansible_port=22 +ansible_ssh_user=ubuntu +ansible_ssh_private_key_file=~/.ssh/id_rsa +ansible_host_key_checking=False diff --git a/hosts/k3s-cluster b/hosts/k3s-cluster new file mode 100644 index 0000000..117c650 --- /dev/null +++ b/hosts/k3s-cluster @@ -0,0 +1,12 @@ +[all] +cn-gateway.svc.plus ansible_host=10.254.0.1 +cn-k3s-server.svc.plus ansible_host=10.254.0.3 +cn-hw-node.svc.plus ansible_host=10.254.0.4 +global-gateway.svc.plus ansible_host=10.255.0.1 +global-k3s-server.svc.plus ansible_host=10.255.0.3 + +[all:vars] +ansible_port=22 +ansible_ssh_user=ubuntu +ansible_ssh_private_key_file=~/.ssh/id_rsa +ansible_host_key_checking=False diff --git a/hosts/vpn b/hosts/vpn new file mode 100644 index 0000000..24ab9dd --- /dev/null +++ b/hosts/vpn @@ -0,0 +1,2 @@ +[vpn-gateway] +xproxy.onwalk.net ansible_host=43.206.158.21 diff --git a/roles/docker/XControl/defaults/main.yml b/roles/docker/XControl/defaults/main.yml index 719ff61..9095800 100644 --- a/roles/docker/XControl/defaults/main.yml +++ b/roles/docker/XControl/defaults/main.yml @@ -71,6 +71,12 @@ xcontrol_account_image: ghcr.io/cloud-neutral-toolkit/account:latest xcontrol_rag_image: manbuzhe2009/rag-server:latest xcontrol_dashboard_image: manbuzhe2009/dashboard:latest xcontrol_db_image: manbuzhe2009/postgres-runtime:latest +xcontrol_dashboard_blue_image: "{{ xcontrol_dashboard_image }}" +xcontrol_dashboard_green_image: "{{ xcontrol_dashboard_image }}" +xcontrol_dashboard_active_color: blue +xcontrol_dashboard_health_path: / +xcontrol_dashboard_health_interval_seconds: 5 +xcontrol_dashboard_health_timeout_ms: 1000 # RAG server configuration defaults xcontrol_rag_server_addr: ":8090" diff --git a/roles/docker/XControl/files/nginx/nginx.conf b/roles/docker/XControl/files/nginx/nginx.conf index 9fe4ac3..c6c98b5 100644 --- a/roles/docker/XControl/files/nginx/nginx.conf +++ b/roles/docker/XControl/files/nginx/nginx.conf @@ -1,5 +1,5 @@ events {} http { - include /etc/nginx/conf.d/*.conf; + include /usr/local/openresty/nginx/conf/conf.d/*.conf; } diff --git a/roles/docker/XControl/templates/docker-compose.yaml b/roles/docker/XControl/templates/docker-compose.yaml index 35509d7..81fd2ad 100644 --- a/roles/docker/XControl/templates/docker-compose.yaml +++ b/roles/docker/XControl/templates/docker-compose.yaml @@ -54,14 +54,26 @@ services: - app - db - dashboard: - image: "{{ xcontrol_dashboard_image }}" - container_name: dashboard + dashboard-blue: + image: "{{ xcontrol_dashboard_blue_image }}" + container_name: dashboard-blue + restart: unless-stopped + environment: + PORT: 3000 + depends_on: + account: + condition: service_started + rag-server: + condition: service_started + networks: + - app + + dashboard-green: + image: "{{ xcontrol_dashboard_green_image }}" + container_name: dashboard-green restart: unless-stopped environment: PORT: 3000 - ports: - - "3000:3000" depends_on: account: condition: service_started @@ -71,12 +83,12 @@ services: - app proxy-external-tls: - image: nginx:mainline-alpine + image: openresty/openresty:alpine container_name: proxy-external-tls restart: unless-stopped volumes: - - "{{ xcontrol_workspace }}/nginx/nginx.conf:/etc/nginx/nginx.conf" - - "{{ xcontrol_workspace }}/nginx/conf.d:/etc/nginx/conf.d:ro" + - "{{ xcontrol_workspace }}/nginx/nginx.conf:/usr/local/openresty/nginx/conf/nginx.conf" + - "{{ xcontrol_workspace }}/nginx/conf.d:/usr/local/openresty/nginx/conf/conf.d:ro" - "{{ xcontrol_workspace }}/certbot/conf:/etc/letsencrypt" - "{{ xcontrol_workspace }}/certbot/www:/var/www/certbot" ports: @@ -89,7 +101,9 @@ services: condition: service_started rag-server: condition: service_started - dashboard: + dashboard-blue: + condition: service_started + dashboard-green: condition: service_started redis: diff --git a/roles/docker/XControl/templates/nginx/conf.d/default.conf b/roles/docker/XControl/templates/nginx/conf.d/default.conf index fb6a9e2..48b6237 100644 --- a/roles/docker/XControl/templates/nginx/conf.d/default.conf +++ b/roles/docker/XControl/templates/nginx/conf.d/default.conf @@ -1,3 +1,69 @@ +lua_shared_dict dashboard_health 1m; + +init_worker_by_lua_block { + local function check_backend(host, port, path, timeout_ms) + local sock = ngx.socket.tcp() + sock:settimeout(timeout_ms) + local ok, err = sock:connect(host, port) + if not ok then + sock:close() + return false + end + + local req = "GET " .. path .. " HTTP/1.0\r\nHost: " .. host .. "\r\nConnection: close\r\n\r\n" + local bytes, send_err = sock:send(req) + if not bytes then + sock:close() + return false + end + + local line, recv_err = sock:receive("*l") + sock:close() + if not line then + return false + end + + local code = tonumber(line:match("^HTTP/%d+%.%d+ (%d%d%d)")) + if not code then + return false + end + + return code >= 200 and code < 400 + end + + local dict = ngx.shared.dashboard_health + local interval = {{ xcontrol_dashboard_health_interval_seconds }} + local path = "{{ xcontrol_dashboard_health_path }}" + local timeout = {{ xcontrol_dashboard_health_timeout_ms }} + + local backends = { + blue = { host = "dashboard-blue", port = 3000 }, + green = { host = "dashboard-green", port = 3000 }, + } + + local function poll(premature) + if premature then + return + end + + for name, backend in pairs(backends) do + local healthy = check_backend(backend.host, backend.port, path, timeout) + dict:set(name, healthy and 1 or 0) + dict:set(name .. "_ts", ngx.time()) + end + + local ok, err = ngx.timer.at(interval, poll) + if not ok then + ngx.log(ngx.ERR, "failed to reschedule dashboard health check: ", err) + end + end + + local ok, err = ngx.timer.at(0, poll) + if not ok then + ngx.log(ngx.ERR, "failed to schedule dashboard health check: ", err) + end +} + # ---------------------------------------------------- # 80 - ACME Challenge + Redirect to HTTPS for homepage # ---------------------------------------------------- @@ -27,8 +93,33 @@ server { ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers on; + set $dashboard_preferred "{{ xcontrol_dashboard_active_color }}"; + set $dashboard_upstream ""; + location / { - proxy_pass http://dashboard:3000; + access_by_lua_block { + local preferred = ngx.var.dashboard_preferred + local dict = ngx.shared.dashboard_health + + local function is_healthy(name) + local val = dict:get(name) + return val == 1 + end + + local fallback = preferred == "blue" and "green" or "blue" + local chosen = preferred + if not is_healthy(preferred) and is_healthy(fallback) then + chosen = fallback + end + + if chosen == "blue" then + ngx.var.dashboard_upstream = "dashboard-blue:3000" + else + ngx.var.dashboard_upstream = "dashboard-green:3000" + end + } + + proxy_pass http://$dashboard_upstream; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto https; }