Add docs.svc.plus deployment playbook
This commit is contained in:
parent
c0f1a1c2ee
commit
26499f5602
58
deploy_docs_svc_plus.yml
Normal file
58
deploy_docs_svc_plus.yml
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
- name: Deploy managed docs.svc.plus service
|
||||||
|
hosts: "{{ docs_service_target_host | default(docs_service_hosts | default('docs')) }}"
|
||||||
|
gather_facts: true
|
||||||
|
become: true
|
||||||
|
vars:
|
||||||
|
docs_service_image_ref: >-
|
||||||
|
{{
|
||||||
|
(lookup('ansible.builtin.env', 'DOCS_IMAGE_REF') | default('', true) | trim)
|
||||||
|
or
|
||||||
|
(
|
||||||
|
(lookup('ansible.builtin.env', 'DOCS_IMAGE_REPO') | default('ghcr.io/x-evor/docs', true))
|
||||||
|
~ ':'
|
||||||
|
~ (lookup('ansible.builtin.env', 'DOCS_IMAGE_TAG') | default('latest', true))
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
docs_service_image_repo: >-
|
||||||
|
{{ lookup('ansible.builtin.env', 'DOCS_IMAGE_REPO')
|
||||||
|
| default('ghcr.io/x-evor/docs', true) }}
|
||||||
|
docs_service_image_tag: >-
|
||||||
|
{{ lookup('ansible.builtin.env', 'DOCS_IMAGE_TAG')
|
||||||
|
| default('latest', true) }}
|
||||||
|
docs_service_pull_image: >-
|
||||||
|
{{ lookup('ansible.builtin.env', 'DOCS_PULL_IMAGE')
|
||||||
|
| default(true, true) | bool }}
|
||||||
|
docs_service_knowledge_repo_path_host: >-
|
||||||
|
{{ lookup('ansible.builtin.env', 'DOCS_KNOWLEDGE_REPO_PATH_HOST')
|
||||||
|
| default('', true) }}
|
||||||
|
docs_service_internal_service_token: >-
|
||||||
|
{{
|
||||||
|
lookup('ansible.builtin.env', 'DOCS_INTERNAL_SERVICE_TOKEN')
|
||||||
|
| default(lookup('ansible.builtin.env', 'INTERNAL_SERVICE_TOKEN') | default('', true), true)
|
||||||
|
}}
|
||||||
|
docs_service_reload_interval: >-
|
||||||
|
{{ lookup('ansible.builtin.env', 'DOCS_RELOAD_INTERVAL')
|
||||||
|
| default('5m', true) }}
|
||||||
|
docs_service_container_port: >-
|
||||||
|
{{ lookup('ansible.builtin.env', 'DOCS_SERVICE_PORT')
|
||||||
|
| default('8084', true) }}
|
||||||
|
docs_service_host_port: >-
|
||||||
|
{{ lookup('ansible.builtin.env', 'DOCS_HOST_PORT')
|
||||||
|
| default('18086', true) }}
|
||||||
|
roles:
|
||||||
|
- roles/vhosts/docker
|
||||||
|
- roles/vhosts/caddy
|
||||||
|
- roles/vhosts/docs_service
|
||||||
|
|
||||||
|
- name: Sync docs DNS records when requested
|
||||||
|
hosts: localhost
|
||||||
|
connection: local
|
||||||
|
gather_facts: false
|
||||||
|
tasks:
|
||||||
|
- name: Reconcile Cloudflare DNS for docs target host
|
||||||
|
when: docs_service_sync_dns | default(false)
|
||||||
|
ansible.builtin.include_role:
|
||||||
|
name: cloudflare_svc_plus_dns
|
||||||
|
vars:
|
||||||
|
cloudflare_dns_source_hosts:
|
||||||
|
- "{{ docs_service_target_host | default(docs_service_hosts | default('docs')) }}"
|
||||||
@ -4,8 +4,8 @@
|
|||||||
cn-front.svc.plus ansible_host=47.120.61.35 ansible_user=root ansible_ssh_user=root firewall_manage_ufw=false service_domains=cn-front.svc.plus
|
cn-front.svc.plus ansible_host=47.120.61.35 ansible_user=root ansible_ssh_user=root firewall_manage_ufw=false service_domains=cn-front.svc.plus
|
||||||
|
|
||||||
[jp_xhttp_contabo_host]
|
[jp_xhttp_contabo_host]
|
||||||
# services: api.svc.plus, console.svc.plus, accounts.svc.plus, acp-server.svc.plus, xworkmate-bridge.svc.plus, vault.svc.plus, openclaw.svc.plus, postgresql.svc.plus
|
# services: api.svc.plus, console.svc.plus, docs.svc.plus, accounts.svc.plus, acp-server.svc.plus, xworkmate-bridge.svc.plus, vault.svc.plus, openclaw.svc.plus, postgresql.svc.plus
|
||||||
jp-xhttp-contabo.svc.plus ansible_host=46.250.251.132 ansible_user=root ansible_ssh_user=root service_domains=api.svc.plus,console.svc.plus,accounts.svc.plus,acp-server.svc.plus,xworkmate-bridge.svc.plus,vault.svc.plus,openclaw.svc.plus,postgresql.svc.plus xray_exporter_node_id_custom=jp-xhttp-contabo.svc.plus
|
jp-xhttp-contabo.svc.plus ansible_host=46.250.251.132 ansible_user=root ansible_ssh_user=root service_domains=api.svc.plus,console.svc.plus,docs.svc.plus,accounts.svc.plus,acp-server.svc.plus,xworkmate-bridge.svc.plus,vault.svc.plus,openclaw.svc.plus,postgresql.svc.plus xray_exporter_node_id_custom=jp-xhttp-contabo.svc.plus
|
||||||
|
|
||||||
[tky_proxy_host]
|
[tky_proxy_host]
|
||||||
# services: tky-proxy.svc.plus
|
# services: tky-proxy.svc.plus
|
||||||
@ -37,6 +37,9 @@ jp-xhttp-contabo.svc.plus
|
|||||||
[accounts]
|
[accounts]
|
||||||
jp-xhttp-contabo.svc.plus
|
jp-xhttp-contabo.svc.plus
|
||||||
|
|
||||||
|
[docs]
|
||||||
|
jp-xhttp-contabo.svc.plus
|
||||||
|
|
||||||
[apisix]
|
[apisix]
|
||||||
jp-xhttp-contabo.svc.plus
|
jp-xhttp-contabo.svc.plus
|
||||||
|
|
||||||
|
|||||||
35
roles/vhosts/docs_service/defaults/main.yml
Normal file
35
roles/vhosts/docs_service/defaults/main.yml
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
---
|
||||||
|
docs_service_base_dir: "{{ lookup('ansible.builtin.env', 'DOCS_BASE_DIR') | default('/opt/docs-svc-plus', true) }}"
|
||||||
|
docs_service_compose_file: "{{ docs_service_base_dir }}/docker-compose.yml"
|
||||||
|
docs_service_runtime_env_file: "{{ docs_service_base_dir }}/.env.runtime"
|
||||||
|
docs_service_project_name: "{{ lookup('ansible.builtin.env', 'DOCS_PROJECT_NAME') | default('docs-svc-plus', true) }}"
|
||||||
|
docs_service_server_name: docs
|
||||||
|
docs_service_release_id: "{{ lookup('env', 'RELEASE_ID') | default(lookup('pipe', 'git -C ' ~ playbook_dir ~ ' rev-parse --short HEAD'), true) }}"
|
||||||
|
docs_service_hostname: "{{ inventory_hostname | default(ansible_facts['hostname']) | default('unknown-host', true) }}"
|
||||||
|
docs_service_canonical_domain: "{{ lookup('ansible.builtin.env', 'DOCS_CANONICAL_DOMAIN') | default('docs.svc.plus', true) }}"
|
||||||
|
docs_service_served_domains: "{{ lookup('ansible.builtin.env', 'DOCS_SERVED_DOMAINS') | default(docs_service_canonical_domain, true) }}"
|
||||||
|
docs_service_domain_slug: "{{ docs_service_canonical_domain | replace('.', '-') }}"
|
||||||
|
docs_service_caddy_conf_dir: /etc/caddy/conf.d
|
||||||
|
docs_service_caddy_fragment_name: "{{ docs_service_server_name }}-{{ docs_service_release_id }}-{{ docs_service_hostname }}-{{ docs_service_domain_slug }}.caddy"
|
||||||
|
docs_service_caddy_fragment_path: "{{ docs_service_caddy_conf_dir }}/{{ docs_service_caddy_fragment_name }}"
|
||||||
|
docs_service_manage_caddy: true
|
||||||
|
|
||||||
|
docs_service_image_ref: "{{ docs_service_image_repo }}:{{ docs_service_image_tag }}"
|
||||||
|
docs_service_image_repo: ghcr.io/x-evor/docs
|
||||||
|
docs_service_image_tag: latest
|
||||||
|
docs_service_pull_image: true
|
||||||
|
docs_service_registry: "{{ lookup('ansible.builtin.env', 'DOCS_REGISTRY') | default('ghcr.io', true) }}"
|
||||||
|
docs_service_registry_username: "{{ lookup('ansible.builtin.env', 'GHCR_USERNAME') | default('', true) }}"
|
||||||
|
docs_service_registry_password: "{{ lookup('ansible.builtin.env', 'GHCR_PASSWORD') | default(lookup('ansible.builtin.env', 'GHCR_TOKEN') | default('', true), true) }}"
|
||||||
|
|
||||||
|
docs_service_container_port: "8084"
|
||||||
|
docs_service_host_port: "18086"
|
||||||
|
docs_service_runtime_port: "{{ docs_service_container_port }}"
|
||||||
|
|
||||||
|
docs_service_knowledge_repo_path_host: "{{ lookup('ansible.builtin.env', 'DOCS_KNOWLEDGE_REPO_PATH_HOST') | default('', true) }}"
|
||||||
|
docs_service_knowledge_repo_path_container: /knowledge
|
||||||
|
|
||||||
|
docs_service_internal_service_token: "{{ lookup('ansible.builtin.env', 'DOCS_INTERNAL_SERVICE_TOKEN') | default(lookup('ansible.builtin.env', 'INTERNAL_SERVICE_TOKEN') | default('', true), true) }}"
|
||||||
|
docs_service_reload_interval: "{{ lookup('ansible.builtin.env', 'DOCS_RELOAD_INTERVAL') | default('5m', true) }}"
|
||||||
|
|
||||||
|
docs_service_healthcheck_url: "http://127.0.0.1:{{ docs_service_host_port }}/healthz"
|
||||||
185
roles/vhosts/docs_service/tasks/main.yml
Normal file
185
roles/vhosts/docs_service/tasks/main.yml
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
---
|
||||||
|
- name: Ensure docs service base directory exists
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ docs_service_base_dir }}"
|
||||||
|
state: directory
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0755"
|
||||||
|
|
||||||
|
- name: Assert docs deploy uses prebuilt registry image
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- docs_service_image_ref | trim | length > 0
|
||||||
|
- "'/' in (docs_service_image_ref | trim)"
|
||||||
|
- "':' in (docs_service_image_ref | trim)"
|
||||||
|
fail_msg: >-
|
||||||
|
DOCS deploy requires a prebuilt image reference via DOCS_IMAGE_REF or
|
||||||
|
DOCS_IMAGE_REPO + DOCS_IMAGE_TAG. This role never builds images on the
|
||||||
|
target host.
|
||||||
|
|
||||||
|
- name: Assert docs knowledge repo path is configured
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- docs_service_knowledge_repo_path_host | trim | length > 0
|
||||||
|
fail_msg: >-
|
||||||
|
DOCS_KNOWLEDGE_REPO_PATH_HOST must be exported before running this playbook.
|
||||||
|
|
||||||
|
- name: Assert docs internal service token is configured
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- docs_service_internal_service_token | trim | length > 0
|
||||||
|
fail_msg: >-
|
||||||
|
DOCS_INTERNAL_SERVICE_TOKEN or INTERNAL_SERVICE_TOKEN must be exported
|
||||||
|
before running this playbook.
|
||||||
|
|
||||||
|
- name: Check docs knowledge repo path exists on target host
|
||||||
|
ansible.builtin.stat:
|
||||||
|
path: "{{ docs_service_knowledge_repo_path_host }}"
|
||||||
|
register: docs_service_knowledge_repo_stat
|
||||||
|
|
||||||
|
- name: Assert docs knowledge repo path exists on target host
|
||||||
|
ansible.builtin.assert:
|
||||||
|
that:
|
||||||
|
- docs_service_knowledge_repo_stat.stat.exists
|
||||||
|
- docs_service_knowledge_repo_stat.stat.isdir
|
||||||
|
fail_msg: >-
|
||||||
|
The configured docs knowledge repo path {{ docs_service_knowledge_repo_path_host }}
|
||||||
|
does not exist or is not a directory on the target host.
|
||||||
|
|
||||||
|
- name: Log into container registry for docs service
|
||||||
|
ansible.builtin.shell: |
|
||||||
|
set -euo pipefail
|
||||||
|
printf '%s' '{{ docs_service_registry_password }}' | docker login {{ docs_service_registry }} -u '{{ docs_service_registry_username }}' --password-stdin
|
||||||
|
args:
|
||||||
|
executable: /bin/bash
|
||||||
|
no_log: true
|
||||||
|
when:
|
||||||
|
- docs_service_registry_username | length > 0
|
||||||
|
- docs_service_registry_password | length > 0
|
||||||
|
|
||||||
|
- name: Render docs compose file
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: docker-compose.yml.j2
|
||||||
|
dest: "{{ docs_service_compose_file }}"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
|
||||||
|
- name: Render docs runtime env file
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: env.runtime.j2
|
||||||
|
dest: "{{ docs_service_runtime_env_file }}"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0600"
|
||||||
|
no_log: true
|
||||||
|
|
||||||
|
- name: Validate docs compose file
|
||||||
|
ansible.builtin.command: >-
|
||||||
|
docker compose
|
||||||
|
--project-name {{ docs_service_project_name }}
|
||||||
|
-f {{ docs_service_compose_file }}
|
||||||
|
--env-file {{ docs_service_runtime_env_file }}
|
||||||
|
config
|
||||||
|
args:
|
||||||
|
chdir: "{{ docs_service_base_dir }}"
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Pull docs service image
|
||||||
|
ansible.builtin.command: >-
|
||||||
|
docker compose
|
||||||
|
--project-name {{ docs_service_project_name }}
|
||||||
|
-f {{ docs_service_compose_file }}
|
||||||
|
--env-file {{ docs_service_runtime_env_file }}
|
||||||
|
pull docs
|
||||||
|
args:
|
||||||
|
chdir: "{{ docs_service_base_dir }}"
|
||||||
|
when: docs_service_pull_image | bool
|
||||||
|
|
||||||
|
- name: Start docs container
|
||||||
|
ansible.builtin.command: >-
|
||||||
|
docker compose
|
||||||
|
--project-name {{ docs_service_project_name }}
|
||||||
|
-f {{ docs_service_compose_file }}
|
||||||
|
--env-file {{ docs_service_runtime_env_file }}
|
||||||
|
up -d --remove-orphans docs
|
||||||
|
args:
|
||||||
|
chdir: "{{ docs_service_base_dir }}"
|
||||||
|
|
||||||
|
- name: Ensure Caddy fragment directory exists
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: "{{ docs_service_caddy_conf_dir }}"
|
||||||
|
state: directory
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0755"
|
||||||
|
when: docs_service_manage_caddy | bool
|
||||||
|
|
||||||
|
- name: Render docs Caddy fragment
|
||||||
|
ansible.builtin.template:
|
||||||
|
src: Caddyfile.j2
|
||||||
|
dest: "{{ docs_service_caddy_fragment_path }}"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0644"
|
||||||
|
when: docs_service_manage_caddy | bool
|
||||||
|
|
||||||
|
- name: Remove obsolete docs Caddy fragments
|
||||||
|
ansible.builtin.shell: |
|
||||||
|
set -euo pipefail
|
||||||
|
shopt -s nullglob
|
||||||
|
current="{{ docs_service_caddy_fragment_path }}"
|
||||||
|
changed=0
|
||||||
|
for candidate in {{ docs_service_caddy_conf_dir }}/docs*.caddy {{ docs_service_caddy_conf_dir }}/docs*.caddy.bak*; do
|
||||||
|
if [ "$candidate" != "$current" ]; then
|
||||||
|
rm -f "$candidate"
|
||||||
|
changed=1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [ "$changed" -eq 1 ]; then
|
||||||
|
echo changed
|
||||||
|
fi
|
||||||
|
args:
|
||||||
|
executable: /bin/bash
|
||||||
|
register: docs_service_caddy_cleanup
|
||||||
|
changed_when: docs_service_caddy_cleanup.stdout | trim != ""
|
||||||
|
when: docs_service_manage_caddy | bool
|
||||||
|
|
||||||
|
- name: Validate Caddy config after updating docs fragment
|
||||||
|
ansible.builtin.command: caddy validate --config /etc/caddy/Caddyfile
|
||||||
|
changed_when: false
|
||||||
|
when:
|
||||||
|
- docs_service_manage_caddy | bool
|
||||||
|
- not ansible_check_mode
|
||||||
|
|
||||||
|
- name: Reload Caddy after updating docs fragment
|
||||||
|
ansible.builtin.service:
|
||||||
|
name: caddy
|
||||||
|
state: reloaded
|
||||||
|
when:
|
||||||
|
- docs_service_manage_caddy | bool
|
||||||
|
- not ansible_check_mode
|
||||||
|
|
||||||
|
- name: Check docs health endpoint
|
||||||
|
ansible.builtin.uri:
|
||||||
|
url: "{{ docs_service_healthcheck_url }}"
|
||||||
|
method: GET
|
||||||
|
status_code: 200
|
||||||
|
register: docs_service_healthcheck
|
||||||
|
retries: 10
|
||||||
|
delay: 3
|
||||||
|
until: docs_service_healthcheck.status == 200
|
||||||
|
changed_when: false
|
||||||
|
when: not ansible_check_mode
|
||||||
|
|
||||||
|
- name: Show docs compose status
|
||||||
|
ansible.builtin.command: >-
|
||||||
|
docker compose
|
||||||
|
--project-name {{ docs_service_project_name }}
|
||||||
|
-f {{ docs_service_compose_file }}
|
||||||
|
--env-file {{ docs_service_runtime_env_file }}
|
||||||
|
ps
|
||||||
|
args:
|
||||||
|
chdir: "{{ docs_service_base_dir }}"
|
||||||
|
changed_when: false
|
||||||
10
roles/vhosts/docs_service/templates/Caddyfile.j2
Normal file
10
roles/vhosts/docs_service/templates/Caddyfile.j2
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{{ '{$SERVED_DOMAINS}' }} {
|
||||||
|
encode zstd gzip
|
||||||
|
|
||||||
|
reverse_proxy 127.0.0.1:{{ docs_service_host_port }} {
|
||||||
|
header_up Host {host}
|
||||||
|
header_up X-Forwarded-Host {host}
|
||||||
|
header_up X-Forwarded-Proto {scheme}
|
||||||
|
header_up X-Forwarded-For {remote_host}
|
||||||
|
}
|
||||||
|
}
|
||||||
10
roles/vhosts/docs_service/templates/docker-compose.yml.j2
Normal file
10
roles/vhosts/docs_service/templates/docker-compose.yml.j2
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
services:
|
||||||
|
docs:
|
||||||
|
image: ${IMAGE:?set IMAGE in .env.runtime}
|
||||||
|
restart: unless-stopped
|
||||||
|
env_file:
|
||||||
|
- .env.runtime
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:{{ docs_service_host_port }}:{{ docs_service_container_port }}"
|
||||||
|
volumes:
|
||||||
|
- "{{ docs_service_knowledge_repo_path_host }}:{{ docs_service_knowledge_repo_path_container }}"
|
||||||
6
roles/vhosts/docs_service/templates/env.runtime.j2
Normal file
6
roles/vhosts/docs_service/templates/env.runtime.j2
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
IMAGE={{ docs_service_image_ref }}
|
||||||
|
SERVED_DOMAINS={{ docs_service_served_domains }}
|
||||||
|
KNOWLEDGE_REPO_PATH={{ docs_service_knowledge_repo_path_container }}
|
||||||
|
DOCS_SERVICE_PORT={{ docs_service_container_port }}
|
||||||
|
INTERNAL_SERVICE_TOKEN={{ docs_service_internal_service_token }}
|
||||||
|
DOCS_RELOAD_INTERVAL={{ docs_service_reload_interval }}
|
||||||
Loading…
Reference in New Issue
Block a user