Add managed accounts.svc.plus deployment

This commit is contained in:
Haitao Pan 2026-04-05 18:58:09 +08:00
parent 47504726a3
commit e9ea0b1d3b
10 changed files with 389 additions and 1 deletions

View File

@ -0,0 +1,8 @@
- name: Deploy managed accounts.svc.plus service
hosts: accounts
gather_facts: false
become: true
vars:
accounts_service_image_tag: "{{ lookup('ansible.builtin.env', 'ACCOUNTS_IMAGE_TAG') | default('70c6a3f8', true) }}"
roles:
- roles/vhosts/accounts_service

View File

@ -4,6 +4,9 @@ cn-front.svc.plus ansible_host=47.120.61.35 ansible_user=roo
[agent_proxy]
jp-xhttp-contabo.svc.plus ansible_host=46.250.251.132 ansible_user=root
[accounts]
acp-server.svc.plus ansible_host=46.250.251.132 ansible_user=root
[k3s]
jp-k3s-vultr.svc.plus ansible_host=167.179.110.129 ansible_user=root
@ -16,4 +19,3 @@ ansible_host_key_checking=False
# SSH 密钥或密码(二选一)
ansible_ssh_private_key_file=~/.ssh/id_rsa
k3s_platform_git_private_key=~/.ssh/id_rsa

View File

@ -0,0 +1,54 @@
---
accounts_service_image_repo: ghcr.io/x-evor/accounts
accounts_service_image_tag: latest
accounts_service_pull_image: true
accounts_service_container_port: 8080
accounts_service_base_dir: /opt/cloud-neutral/accounts/managed
accounts_service_shared_network: cn-toolkit-shared
accounts_service_dns_servers:
- 1.1.1.1
- 8.8.8.8
accounts_service_caddyfile_path: /etc/caddy/Caddyfile
accounts_service_caddy_conf_dir: /etc/caddy/conf.d
accounts_service_caddy_fragment_path: /etc/caddy/conf.d/accounts-contabo-e700175.caddy
accounts_service_caddy_sites:
- server_names:
- accounts.svc.plus
- accounts-contabo-e700175.svc.plus
default_forwarded_host: svc.plus
upstream: 127.0.0.1:18081
accounts_service_env_defaults:
DB_HOST: stunnel-client
DB_NAME: account
DB_PASSWORD: ""
DB_PORT: "15432"
DB_USER: svcplus_vps
INTERNAL_SERVICE_TOKEN: ""
POSTGRES_PASSWORD: ""
POSTGRES_USER: svcplus_vps
SMTP_FROM: XControl Account <noreply@svc.plus>
SMTP_HOST: smtp.qq.com
SMTP_PASSWORD: ""
SMTP_PORT: "587"
SMTP_USERNAME: ""
XWORKMATE_VAULT_ADDR: https://vault.svc.plus
XWORKMATE_VAULT_MOUNT: kv
XWORKMATE_VAULT_NAMESPACE: ""
XWORKMATE_VAULT_TOKEN: ""
accounts_service_targets:
- name: prod
compose_dir: "{{ accounts_service_base_dir }}/prod"
compose_file: "{{ accounts_service_base_dir }}/prod/docker-compose.yml"
env_file: "{{ accounts_service_base_dir }}/prod/env/app.env"
config_file: "{{ accounts_service_base_dir }}/prod/config/account.yaml"
legacy_env_file: /opt/cloud-neutral/accounts/accounts-51945b5-contabo/env/app.env
container_name: accounts-managed-prod-contabo
host_port: 18081
- name: preview
compose_dir: "{{ accounts_service_base_dir }}/preview"
compose_file: "{{ accounts_service_base_dir }}/preview/docker-compose.yml"
env_file: "{{ accounts_service_base_dir }}/preview/env/app.env"
config_file: "{{ accounts_service_base_dir }}/preview/config/account.yaml"
legacy_env_file: /opt/cloud-neutral/accounts/accounts-preview-51945b5-contabo/env/app.env
container_name: accounts-managed-preview-contabo
host_port: 28081

View File

@ -0,0 +1,5 @@
---
- name: Reload caddy
ansible.builtin.systemd:
name: caddy
state: reloaded

View File

@ -0,0 +1,45 @@
---
- name: Ensure accounts service base directory exists
ansible.builtin.file:
path: "{{ accounts_service_base_dir }}"
state: directory
owner: root
group: root
mode: "0755"
- name: Ensure Caddy fragment directory exists for accounts service
ansible.builtin.file:
path: "{{ accounts_service_caddy_conf_dir }}"
state: directory
owner: root
group: root
mode: "0755"
- name: Deploy managed accounts Caddy fragment
ansible.builtin.template:
src: accounts-site.caddy.j2
dest: "{{ accounts_service_caddy_fragment_path }}"
owner: root
group: root
mode: "0644"
notify: Reload caddy
- name: Ensure Caddy is enabled and running for accounts service
ansible.builtin.systemd:
name: caddy
enabled: true
state: started
- name: Ensure shared Docker network exists for accounts service
ansible.builtin.command: docker network inspect "{{ accounts_service_shared_network }}"
changed_when: false
- name: Deploy managed accounts compose targets
ansible.builtin.include_tasks: target.yml
loop: "{{ accounts_service_targets }}"
loop_control:
loop_var: accounts_service_target
- name: Validate Caddy configuration for accounts service
ansible.builtin.command: caddy validate --config "{{ accounts_service_caddyfile_path }}"
changed_when: false

View File

@ -0,0 +1,101 @@
---
- name: Ensure compose directories exist for {{ accounts_service_target.name }}
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: root
group: root
mode: "0755"
loop:
- "{{ accounts_service_target.compose_dir }}"
- "{{ accounts_service_target.compose_dir }}/env"
- "{{ accounts_service_target.compose_dir }}/config"
- name: Check for managed env file for {{ accounts_service_target.name }}
ansible.builtin.stat:
path: "{{ accounts_service_target.env_file }}"
register: accounts_service_target_managed_env_stat
- name: Check for legacy env file for {{ accounts_service_target.name }}
ansible.builtin.stat:
path: "{{ accounts_service_target.legacy_env_file }}"
register: accounts_service_target_legacy_env_stat
- name: Seed managed env file from legacy deployment for {{ accounts_service_target.name }}
ansible.builtin.copy:
src: "{{ accounts_service_target.legacy_env_file }}"
dest: "{{ accounts_service_target.env_file }}"
remote_src: true
owner: root
group: root
mode: "0600"
when:
- not accounts_service_target_managed_env_stat.stat.exists
- accounts_service_target_legacy_env_stat.stat.exists
- name: Render managed env file from defaults for {{ accounts_service_target.name }}
ansible.builtin.template:
src: app.env.j2
dest: "{{ accounts_service_target.env_file }}"
owner: root
group: root
mode: "0600"
when:
- not accounts_service_target_managed_env_stat.stat.exists
- not accounts_service_target_legacy_env_stat.stat.exists
- name: Ensure managed CONFIG_TEMPLATE is present for {{ accounts_service_target.name }}
ansible.builtin.lineinfile:
path: "{{ accounts_service_target.env_file }}"
regexp: '^CONFIG_TEMPLATE='
line: 'CONFIG_TEMPLATE=/etc/xcontrol/account.template.yaml'
state: present
insertafter: '^CONFIG_PATH='
- name: Ensure managed CONFIG_PATH is present for {{ accounts_service_target.name }}
ansible.builtin.lineinfile:
path: "{{ accounts_service_target.env_file }}"
regexp: '^CONFIG_PATH='
line: 'CONFIG_PATH=/etc/xcontrol/account.yaml'
state: present
insertbefore: BOF
- name: Render managed account config for {{ accounts_service_target.name }}
ansible.builtin.template:
src: account.yaml.j2
dest: "{{ accounts_service_target.config_file }}"
owner: root
group: root
mode: "0644"
- name: Render compose file for {{ accounts_service_target.name }}
ansible.builtin.template:
src: docker-compose.yml.j2
dest: "{{ accounts_service_target.compose_file }}"
owner: root
group: root
mode: "0644"
- name: Pull image for {{ accounts_service_target.name }}
ansible.builtin.command: docker compose -f "{{ accounts_service_target.compose_file }}" pull app
args:
chdir: "{{ accounts_service_target.compose_dir }}"
when: accounts_service_pull_image | bool
- name: Remove any container currently bound to {{ accounts_service_target.host_port }}
ansible.builtin.shell: |
set -euo pipefail
ids="$(docker ps --filter publish={{ accounts_service_target.host_port }} -q)"
if [ -z "${ids}" ]; then
exit 0
fi
docker rm -f ${ids}
args:
executable: /bin/bash
register: accounts_service_target_port_cleanup
changed_when: accounts_service_target_port_cleanup.stdout | trim != ""
- name: Start managed compose target for {{ accounts_service_target.name }}
ansible.builtin.command: docker compose -f "{{ accounts_service_target.compose_file }}" up -d --force-recreate --remove-orphans
args:
chdir: "{{ accounts_service_target.compose_dir }}"

View File

@ -0,0 +1,113 @@
mode: "server-agent"
log:
level: info
auth:
enable: true
token:
publicToken: "${AUTH_TOKEN_PUBLIC_TOKEN:-xcontrol-public-token-2024}"
refreshSecret: "${AUTH_TOKEN_REFRESH_SECRET:-xcontrol-refresh-secret-2024}"
accessSecret: "${AUTH_TOKEN_ACCESS_SECRET:-xcontrol-access-secret-2024}"
accessExpiry: "1h"
refreshExpiry: "168h"
oauth:
redirectUrl: "${OAUTH_REDIRECT_URL}"
frontendUrl: "${OAUTH_FRONTEND_URL:-https://console.svc.plus}"
github:
clientId: "${GITHUB_CLIENT_ID}"
clientSecret: "${GITHUB_CLIENT_SECRET}"
google:
clientId: "${GOOGLE_CLIENT_ID}"
clientSecret: "${GOOGLE_CLIENT_SECRET}"
server:
addr: ":8080"
readTimeout: 15s
writeTimeout: 15s
publicUrl: "https://accounts.svc.plus"
allowedOrigins:
- "https://dev.svc.plus"
- "https://dev-homepage.svc.plus"
- "https://www.svc.plus"
- "https://global-homepage.svc.plus"
- "https://accounts.svc.plus"
- "https://console.svc.plus"
- "https://localhost:8443"
- "http://localhost:8080"
- "http://127.0.0.1:8080"
- "http://localhost:3001"
- "http://127.0.0.1:3001"
- "http://localhost:3000"
- "http://127.0.0.1:3000"
tls:
enabled: false
certFile: ""
keyFile: ""
caFile: ""
clientCAFile: ""
redirectHttp: false
store:
driver: "postgres"
dsn: "postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}?sslmode=disable"
maxOpenConns: 30
maxIdleConns: 10
session:
ttl: 24h
cache: "redis"
redis:
addr: "127.0.0.1:6379"
password: ""
smtp:
host: "${SMTP_HOST}"
port: ${SMTP_PORT}
username: "${SMTP_USERNAME}"
password: "${SMTP_PASSWORD}"
from: "${SMTP_FROM}"
replyTo: ""
timeout: 10s
tls:
mode: "auto"
insecureSkipVerify: false
xray:
sync:
enabled: false
interval: 5m
outputPath: "/usr/local/etc/xray/config.json"
templatePath: "account/config/xray.config.template.json"
validateCommand: []
restartCommand:
- "systemctl"
- "restart"
- "xray.service"
reviewAccount:
enabled: true
email: "review@svc.plus"
name: "Review"
password: "Review123!"
groups:
- "User"
- "Beta"
- "Review"
- "ReadOnly Role"
permissions:
- "admin.settings.read"
- "admin.users.metrics.read"
- "admin.users.list.read"
- "admin.agents.status.read"
- "admin.blacklist.read"
agent:
id: "account-primary"
controllerUrl: "http://127.0.0.1:8080"
apiToken: "replace-with-agent-token"
httpTimeout: 15s
statusInterval: 1m
syncInterval: 5m
tls:
insecureSkipVerify: false

View File

@ -0,0 +1,14 @@
{% for site in accounts_service_caddy_sites %}
{{ site.server_names | join(', ') }} {
encode zstd gzip
map {http.request.header.X-Forwarded-Host} {accounts_forwarded_host} {
"" {{ site.default_forwarded_host | default("") }}
default {http.request.header.X-Forwarded-Host}
}
reverse_proxy {{ site.upstream }} {
# Preserve caller-provided tenant host (for example console.svc.plus -> accounts proxy).
# When the request comes directly to accounts.svc.plus, fall back to the managed shared host.
header_up X-Forwarded-Host {accounts_forwarded_host}
}
}
{% endfor %}

View File

@ -0,0 +1,21 @@
# Managed by Ansible. Do not edit on the host.
CONFIG_PATH=/etc/xcontrol/account.yaml
CONFIG_TEMPLATE=/etc/xcontrol/account.template.yaml
DB_HOST={{ accounts_service_env_defaults.DB_HOST }}
DB_NAME={{ accounts_service_env_defaults.DB_NAME }}
DB_PASSWORD={{ accounts_service_env_defaults.DB_PASSWORD }}
DB_PORT={{ accounts_service_env_defaults.DB_PORT }}
DB_USER={{ accounts_service_env_defaults.DB_USER }}
INTERNAL_SERVICE_TOKEN={{ accounts_service_env_defaults.INTERNAL_SERVICE_TOKEN }}
POSTGRES_PASSWORD={{ accounts_service_env_defaults.POSTGRES_PASSWORD }}
POSTGRES_USER={{ accounts_service_env_defaults.POSTGRES_USER }}
SMTP_FROM={{ accounts_service_env_defaults.SMTP_FROM }}
SMTP_HOST={{ accounts_service_env_defaults.SMTP_HOST }}
SMTP_PASSWORD={{ accounts_service_env_defaults.SMTP_PASSWORD }}
SMTP_PORT={{ accounts_service_env_defaults.SMTP_PORT }}
SMTP_USERNAME={{ accounts_service_env_defaults.SMTP_USERNAME }}
XWORKMATE_VAULT_ADDR={{ accounts_service_env_defaults.XWORKMATE_VAULT_ADDR }}
XWORKMATE_VAULT_NAMESPACE={{ accounts_service_env_defaults.XWORKMATE_VAULT_NAMESPACE }}
XWORKMATE_VAULT_TOKEN={{ accounts_service_env_defaults.XWORKMATE_VAULT_TOKEN }}
XWORKMATE_VAULT_MOUNT={{ accounts_service_env_defaults.XWORKMATE_VAULT_MOUNT }}

View File

@ -0,0 +1,25 @@
services:
app:
image: "{{ accounts_service_image_repo }}:{{ accounts_service_image_tag }}"
container_name: {{ accounts_service_target.container_name }}
restart: unless-stopped
env_file:
- ./env/app.env
environment:
PORT: "{{ accounts_service_container_port }}"
volumes:
- ./config/account.yaml:/etc/xcontrol/account.template.yaml:ro
ports:
- "127.0.0.1:{{ accounts_service_target.host_port }}:{{ accounts_service_container_port }}"
dns:
{% for dns_server in accounts_service_dns_servers %}
- "{{ dns_server }}"
{% endfor %}
networks:
- default
- shared_stunnel
networks:
shared_stunnel:
external: true
name: "{{ accounts_service_shared_network }}"