feat(docker/zitadel): production-ready ACME automation + TLS proxy + idempotent init

This commit is contained in:
Haitao Pan 2025-12-02 19:53:59 +08:00
parent 927568e912
commit 76ee00f393
7 changed files with 113 additions and 12 deletions

View File

@ -7,6 +7,6 @@
zitadel_masterkey: MasterkeyNeedsToHave32Characters
zitadel_workspace: /opt/zitadel
roles:
- vhosts/common/
#- vhosts/common/
- vhosts/docker/
- docker/zitadel/

View File

@ -12,7 +12,7 @@ global-homepage.svc.plus ansible_host=167.179.72.223
smtp.svc.plus ansible_host=45.130.167.90
[bootstrap]
auth.svc.plus ansible_host=18.163.190.24 ansible_user=ubuntu ansible_ssh_private_key_file=~/.ssh/id_rsa
auth.svc.plus ansible_host=34.92.122.119 ansible_user=root ansible_ssh_private_key_file=~/.ssh/id_rsa
[all:vars]
ansible_port=22

View File

@ -13,6 +13,16 @@
- "{{ zitadel_workspace }}/nginx"
- "{{ zitadel_workspace }}/nginx/conf.d"
- name: Ensure Zitadel workspace ownership
become: true
ansible.builtin.file:
path: "{{ zitadel_workspace }}"
state: directory
recurse: true
owner: "1000"
group: "1000"
mode: "0755"
- name: Template Zitadel configuration files
become: true
ansible.builtin.template:
@ -22,6 +32,7 @@
loop:
- { src: 'docker-compose.yaml', dest: 'docker-compose.yaml' }
- { src: 'nginx/conf.d/default.conf', dest: 'nginx/conf.d/default.conf' }
- { src: 'nginx/conf.d/bootstrap-nginx.conf', dest: 'nginx/conf.d/bootstrap-nginx.conf' }
- name: Copy Zitadel static files
become: true
@ -33,8 +44,62 @@
- { src: 'run.sh', dest: 'run.sh', mode: '0755' }
- { src: 'nginx/nginx.conf', dest: 'nginx/nginx.conf' }
- name: Bring up Zitadel stack
- name: Bootstrap NGINX (80-only for ACME)
become: true
ansible.builtin.command: docker-compose -f {{ zitadel_workspace }}/docker-compose.yaml up -d
command: docker compose --profile bootstrap -f {{ zitadel_workspace }}/docker-compose.yaml up -d bootstrap-nginx
args:
chdir: "{{ zitadel_workspace }}"
- name: Run certbot initial ACME challenge
become: true
command: docker compose --profile bootstrap -f {{ zitadel_workspace }}/docker-compose.yaml run --rm certbot
args:
chdir: "{{ zitadel_workspace }}"
- name: Destroy Bootstrap NGINX (80-only for ACME)
become: true
command: docker compose --profile bootstrap -f {{ zitadel_workspace }}/docker-compose.yaml down bootstrap-nginx
args:
chdir: "{{ zitadel_workspace }}"
# -------------------------------------------------------------------
# 1. 判断 Zitadel 是否已经初始化
# (是否已经生成 login-client.pat 或其它初始化标记)
# -------------------------------------------------------------------
- name: Check if Zitadel initialized
stat:
path: "{{ zitadel_workspace }}/login-client.pat"
register: zitadel_initialized
# -------------------------------------------------------------------
# 2. 如果未初始化,先清理所有可能失败的残留容器、状态
# -------------------------------------------------------------------
- name: Zitadel containers and Zitadel postgres volume (cleanup)
become: true
shell: |
docker compose -f {{ zitadel_workspace }}/docker-compose.yaml down || true
docker volume rm zitadel_data || true
args:
chdir: "{{ zitadel_workspace }}"
when: not zitadel_initialized.stat.exists
# -------------------------------------------------------------------
# 3. 执行第一次初始化init + setup
# -------------------------------------------------------------------
- name: Run Zitadel init (one-time)
become: true
shell: |
docker compose -f {{ zitadel_workspace }}/docker-compose.yaml run --rm zitadel-init || true
args:
chdir: "{{ zitadel_workspace }}"
when: not zitadel_initialized.stat.exists
# -------------------------------------------------------------------
# 4. 启动正式 Zitadel stackstart-only
# -------------------------------------------------------------------
- name: Bring up Zitadel stack
become: true
command: docker compose -f {{ zitadel_workspace }}/docker-compose.yaml up -d
args:
chdir: "{{ zitadel_workspace }}"

View File

@ -129,7 +129,7 @@ services:
restart: unless-stopped
volumes:
- "{{ zitadel_workspace }}/nginx/nginx.conf:/etc/nginx/nginx.conf"
- "{{ zitadel_workspace }}/nginx/conf.d:/etc/nginx/conf.d:ro"
- "{{ zitadel_workspace }}/nginx/conf.d/default.conf:/etc/nginx/conf.d/default.conf:ro"
- "{{ zitadel_workspace }}/certbot/conf:/etc/letsencrypt"
- "{{ zitadel_workspace }}/certbot/www:/var/www/certbot"
ports:
@ -141,7 +141,27 @@ services:
zitadel-external-tls:
condition: service_healthy
bootstrap-nginx:
profiles: ["bootstrap"]
image: nginx:mainline-alpine
container_name: bootstrap-nginx
volumes:
- "{{ zitadel_workspace }}/certbot/www:/var/www/certbot"
- "{{ zitadel_workspace }}/certbot/conf:/etc/letsencrypt"
- "{{ zitadel_workspace }}/nginx/nginx.conf:/etc/nginx/nginx.conf"
- "{{ zitadel_workspace }}/nginx/conf.d/bootstrap-nginx.conf:/etc/nginx/conf.d/bootstrap-nginx.conf"
ports:
- "80:80" # 暂时只占用80
networks:
- app
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost"]
interval: 3s
timeout: 2s
retries: 10
start_period: 3s
certbot:
profiles: ["bootstrap"]
image: certbot/certbot
container_name: certbot
command: >
@ -150,13 +170,12 @@ services:
--email manbuzhe2009@qq.com
--agree-tos
--no-eff-email
--keep-until-expiring
--non-interactive
-d {{ zitadel_domain }}
volumes:
- "{{ zitadel_workspace }}/certbot/conf:/etc/letsencrypt"
- "{{ zitadel_workspace }}/certbot/www:/var/www/certbot"
depends_on:
proxy-external-tls:
condition: service_started
networks:
- app

View File

@ -0,0 +1,11 @@
server {
listen 80;
server_name {{ zitadel_domain }};
location ^~ /.well-known/acme-challenge/ {
root /var/www/certbot;
}
# 不 redirect不要 https
# certbot 需要纯 http 验证
}

View File

@ -1,11 +1,17 @@
- name: Set timezone
shell: "timedatectl set-timezone Asia/Shanghai"
- name: Set hostname
shell: "hostname -F /etc/hostname"
- name: Render hostname file
ansible.builtin.template:
src: templates/hostname.j2
dest: /etc/hostname
owner: root
group: root
mode: "0644"
- name: update /etc/hostname
template: src=templates/hostname dest=/etc/hostname owner=root group=root mode=0644 unsafe_writes=yes
- name: Set hostname using modern module
hostname:
name: "{{ inventory_hostname }}"
- name: Update /etc/hosts
template: src=templates/hosts dest=/etc/hosts owner=root group=root mode=0644 force=yes unsafe_writes=yes