Files
adcloud-server/docs/docker-deployment.md
codex-bot b43043255c
Docker test deployment / test-build-deploy (push) Successful in 4m44s
Add versioned event registry and transactional feature rules
2026-09-16 04:46:17 +08:00

114 lines
7.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Docker / Gitea Actions 测试部署
## 运行方式
工作流 `.gitea/workflows/docker-deploy.yml` 使用现有 `ubuntu-latest` runner 标签,在推送 main 或手动 dispatch 时运行:
1. 验证作业可以访问 Docker daemon。
2. 使用 Gitea 自动签发的短期作业 token 下载精确提交的源码,不将认证信息放入 URL、命令行或日志。
3. 用 uv 和 uv.lock 构建 test 镜像,执行 Ruff 与 pytest。外部数据库测试未配置环境时跳过。
4. 构建不含开发依赖的非 root runtime 镜像,包含 PostgreSQL / Cassandra Python 驱动,允许以后配置存储;不运行数据库服务。
5. 通过独立 Compose 项目启动临时 PostgreSQL / Cassandra,执行全部数据库与注册表测试(包含 6 个真实数据库测试);结束后清理该 CI 项目的容器和数据卷。
6. 用 Compose 部署持久化 PostgreSQL / Cassandra、API、特征 worker 和画像 worker,检查 JSONB 存储、画像投影与宿主机 HTTP 端口。
7. 如提供公网 URL,再探测公网 `/health`。后续可从外部执行匿名和埋点测试。
Docker 镜像直接保存在该 daemon 上,本版不依赖镜像仓库登录。
## 公网端口
```text
http://CI_SERVER_PUBLIC_IP:18080
↓ Docker published port
容器 :8000
↓
/v1/identity/*
/v1/events
/v1/users/*
/v1/ads/*
```
默认 `0.0.0.0:18080:8000`。Docker 的 published port 映射到 **daemon 所在服务器**,不是任务容器自己的 localhost。
服务器公网 IP、路由、云安全组和防火墙也必须允许 18080/TCP,映射本身不能修改云端安全组。
这是用于合成测试数据的 HTTP 环境,`COOKIE_SECURE=false`;正式使用需要 HTTPS 和 Secure Cookie,已有 Nginx 443 模板可接入。
`/health`、`/docs`、`/openapi.json` 可用于测试;画像查询、Stitch 和历史查询保持服务端凭据鉴权。
CORS 默认允许 localhost:3000、5173、5174;接入控制台域名时设置 `DEPLOY_CORS_ORIGINS`。
## Runner 要求
runner v0.6.1 的 `container.docker_host` 默认空字符串,会自动查找 daemon 并将 socket 提供给 job。
若显式配置为 `-`,作业不会获得 Docker host,第一步会失败。直接使用宿主机 socket 的配置示例:
```yaml
container:
docker_host: unix:///var/run/docker.sock
```
如果 runner 本身在 Docker 中,runner 容器需要挂载宿主机 `/var/run/docker.sock`。
本工作流不额外申请 privileged 或用户自定义 host bind mount;使用 runner 提供的 daemon。
如果使用 Docker-in-Docker,18080 会发布在 DinD daemon 的命名空间中,未必是公网宿主机。
需要切换为宿主机 daemon,或由服务器管理员额外转发 DinD 端口。看到 runner 的 Idle 状态不能证明公网部署已配置好。
Docker socket 具有管理 daemon 的能力,因此部署工作流仅对 main 的 push/dispatch 执行,不接收不可信 PR。
## 手动运行 / 配置
手动 dispatch 可提供:
- `deploy_port`:宿主机公开端口,默认 18080,允许 1024–65535。
- `public_base_url`:例如 `http://SERVER_IP:18080`;用于公网探测和访问地址报告。
也可由仓库管理员设置 `ADCLOUD_DEPLOY_PORT`、`ADCLOUD_PUBLIC_BASE_URL` 两个 Actions 变量;手动输入优先。
执行部署的 bot 只需仓库写入权限;修改 runner 配置和 Actions 变量需要对应管理权限。
在有 Docker 的目标服务器手动部署:
```sh
docker build --target runtime --tag adcloud-server:test .
DEPLOY_IMAGE=adcloud-server:test bash scripts/compose_stack.sh deploy
```
当前公网环境使用 PostgreSQL 16 保存身份图谱和 JSONB 权威画像,Cassandra 4.1 保存画像投影,adcloud-events 消费事件规则任务,adcloud-profiles 消费画像 outbox。数据库不发布宿主机端口,只能通过项目 Docker 内网连接。
Cassandra 在此合成测试环境内关闭 TLS / 认证,单节点 RF=1;生产环境需另外配置认证、TLS、备份和多节点副本。
数据库和应用合计内存限制约 4 GiB,服务器应留出至少 4.5 GiB 可用内存(CI 临时数据库会额外占用内存),首次启动 Cassandra 可能需要几分钟。
`compose.yml` 使用健康检查和一次性 CQL 初始化任务控制启动顺序。
临时 CI 使用 `adcloud-ci-<commit>-<run>` 独立项目;PostgreSQL 密码随机生成。Cassandra 测试同时连接 CI PostgreSQL,验证双存储组合。失败时也会清理该项目。
手动 Compose 操作需通过脚本加载 config 卷中持久化的 PostgreSQL 密码;不要直接输出 `docker compose config`(其中含展开的连接凭据)。
## 持久化、权限和回滚
- Compose 项目为 `adcloud-test`;PostgreSQL 和 Cassandra 分别使用项目专属命名数据卷。
- `adcloud-server-test-config` 复用原身份密钥卷;首次补充随机 PostgreSQL 密码,重部署复用,配置文件权限 0600,不输出到日志。
- API / worker 使用 UID/GID 10001、只读根文件系统、去除 capabilities、限制内存和进程;均使用 `restart=unless-stopped`。
- named lock 防止并发部署。确认无部署仍在运行后才能清理遗留的 `adcloud-server-test-deploy-lock`。
- 首次切换前先等待数据库就绪;旧 SQLite 容器仅停止,旧数据卷保留,无自动数据迁移。
- 首次 Compose 切换失败时尽可能恢复旧 SQLite 容器;后续 Compose 更新不是原子部署,不保证 schema 迁移可回滚。公网 API 切换有短暂停机。
- 测试部署不自动清除持久化数据卷或旧镜像。原 `deploy_docker.sh` 保留用于独立 SQLite 部署,不与 Compose 同时运行。
## HTTP 验证
`smoke_api.py` 检查系统/模块路由、匿名 Cookie、埋点、未授权拒绝、可信 Stitch、画像继承和历史事件回溯。
使用合成邮箱与独立匿名身份,会向测试数据库写入少量演示数据。
`--require-projection` 还会验证 PostgreSQL / JSONB 配置,并等待画像状态变为 Cassandra `current`,避免降级到关系型读取掩盖 worker 故障。
验证宿主机端口时,临时探测容器挂载配置卷为只读,读取凭据在进程内发送,不将凭据放到命令参数或日志中。
公网健康检查:
```sh
curl --fail http://SERVER_IP:18080/health
```
外部匿名和事件检查(不需要服务端凭据):
```sh
uv run python scripts/smoke_api.py --base-url http://SERVER_IP:18080 --public-only
```
不要将 SERVICE_TOKEN 放入浏览器或 SDK。需要管理接口测试时,在服务器的受控环境读取部署配置,或配置你自己的受保护测试凭据。
参考:[Docker 端口映射](https://docs.docker.com/engine/network/port-publishing/)、[runner v0.6.1 配置](https://gitea.com/gitea/act_runner/src/tag/v0.6.1/internal/pkg/config/config.example.yaml)、[uv Docker 集成](https://docs.astral.sh/uv/guides/integration/docker/)。
注册表默认强制应用凭据和已发布事件版本;smoke 会创建合成应用、注册 add_cart、重试同一 event_id 并验证数码意向 +1,只输出验证结论。公网 public-only 检查健康、匿名 Cookie 和未注册事件拒绝,不携带管理凭据。