ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

OpenClaw进阶实战(二十九):Docker Compose生产级配置——健康检查、日志轮转与环境隔离

OpenClaw进阶实战(二十九):Docker Compose生产级配置——健康检查、日志轮转与环境隔离 1. 从 docker run 到生产编排OpenClaw 部署踩过的坑OpenClaw 用docker run跑起来很快一条命令就能在本地把 Gateway 拉起来接上飞书或钉钉就能对话。但当你把它放到一台真实服务器上、接入真实业务流量之后问题会一个接一个冒出来容器半夜挂了没人知道第二天早上才发现服务中断日志文件无限增长某天磁盘写满导致整个服务不可用Token 明文写在 compose 文件里一旦仓库泄露就是安全事故多个 Agent 共享同一套环境变量一个技能把内存吃光其他技能跟着一起崩。这些问题的根源不是 OpenClaw 本身而是开发态编排和生产态编排之间的差距。开发态只关心能不能跑起来生产态要关心挂了能不能自动恢复、日志会不会撑爆磁盘、凭据会不会泄露、服务之间会不会互相影响。Docker Compose 本身完全支持这些能力只是大多数人写 compose 文件时只写了image和ports把healthcheck、logging、deploy.resources、security_opt这些生产级字段全跳过了。这篇是 OpenClaw 进阶实战系列的第二十九篇聚焦 Docker Compose 下的生产级落地围绕健康检查、日志轮转、环境隔离三条主线展开。我会给出一套可以直接复制的compose.yaml骨架配合 TaoToken 统一 Key/API 通道的配置片段然后逐个演示健康检查探针、日志轮转参数、环境变量隔离的验证动作。目标很明确把你手里那份能跑的 compose 文件升级成稳运行、可运维的生产编排。适合已经完成 OpenClaw 基础安装、了解 Docker 基本操作、准备把服务接入真实渠道的读者。2. 前置准备TaoToken 统一 Key 与 API 通道在写 compose 文件之前先把模型 API 通道这件事理清楚。OpenClaw 的 Agent 需要调用大模型如果你同时用 OpenAI、Anthropic、通义等多个供应商每个供应商一套 Key、一套计费、一套限流环境变量会变得非常混乱。更麻烦的是这些 Key 如果直接写进 compose 文件等于把凭据暴露在版本控制里。我的做法是用 TaoToken 做统一入口把多供应商的 Key 收敛成一个通道。TaoToken 提供兼容 OpenAI 风格的 API 端点OpenClaw 侧只需要配置一个 base URL 和一个 Key就能访问背后多个模型。这样环境变量从 N 个变成 1 个隔离和轮换都简单很多。具体操作先到官网 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 注册账号然后在控制台 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 创建一个 API Key。创建时建议按用途命名比如openclaw-prod方便后续审计。Key 生成后只显示一次复制到你的密码管理器里。拿到 Key 之后OpenClaw 侧的配置分两步。第一步是在.env文件里写入 Key注意这个文件权限要设成 600# 生成 OpenClaw 内部 Token用于 Gateway 自身鉴权 OPENCLAW_TOKEN$(openssl rand -hex 32) # 生成数据库密码 DB_PASSWORD$(openssl rand -hex 16) # 生成 Redis 密码 REDIS_PASSWORD$(openssl rand -hex 16) # 写入 .env cat .env EOF OPENCLAW_TOKEN${OPENCLAW_TOKEN} OPENCLAW_IMAGEghcr.io/openclaw/openclaw:v2026.3.8 POSTGRES_DBopenclaw POSTGRES_USERopenclaw POSTGRES_PASSWORD${DB_PASSWORD} REDIS_PASSWORD${REDIS_PASSWORD} LOG_LEVELinfo # TaoToken 统一通道 TAOTOKEN_API_KEYsk-你的TaoToken密钥 TAOTOKEN_BASE_URLhttps://taotoken.net/api EOF chmod 600 .env第二步是在 OpenClaw 的配置文件里把模型 provider 指向 TaoToken 的端点。TaoToken 的 API 地址是 https://taotoken.net/api 兼容 OpenAI 的/v1/chat/completions路径。配置文件config/openclaw.json里这样写{ gateway: { port: 18789, bind: loopback, token: ${OPENCLAW_TOKEN}, trustedProxies: [127.0.0.1, 172.16.0.0/12] }, agents: { defaults: { model: { provider: openai-compatible, baseUrl: ${TAOTOKEN_BASE_URL}, apiKey: ${TAOTOKEN_API_KEY}, model: gpt-4o-mini }, sandbox: { mode: non-main, scope: agent, workspaceAccess: none } } }, logging: { level: info, format: json } }这里有个关键点baseUrl和apiKey都通过环境变量注入配置文件本身不含任何明文凭据。这样配置文件可以安全地放进 Git 仓库凭据只存在于服务器的.env里。如果你需要查看当前可用的模型列表可以到模型对话页面 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite 确认把model字段换成你实际要用的模型名。注意.env文件必须chmod 600并且加入.gitignore。我见过太多项目把.env误提交到仓库Key 泄露后被人刷了几百刀。这一步不能省。3. 可复制的生产级 compose.yaml 骨架下面这份compose.yaml是完整可用的骨架包含健康检查、日志轮转、资源限制、环境隔离四块核心配置。你可以直接复制到/opt/openclaw/compose.yaml然后按注释替换域名和镜像版本。name: openclaw-prod networks: openclaw-net: driver: bridge ipam: config: - subnet: 172.20.0.0/16 volumes: postgres_data: redis_data: services: postgres: image: postgres:15-alpine container_name: openclaw-postgres restart: unless-stopped environment: POSTGRES_DB: ${POSTGRES_DB:-openclaw} POSTGRES_USER: ${POSTGRES_USER:-openclaw} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} PGDATA: /var/lib/postgresql/data/pgdata volumes: - postgres_data:/var/lib/postgresql/data - ./backups:/backups:ro networks: - openclaw-net healthcheck: test: [CMD-SHELL, pg_isready -U ${POSTGRES_USER:-openclaw} -d ${POSTGRES_DB:-openclaw}] interval: 10s timeout: 5s retries: 5 start_period: 30s deploy: resources: limits: memory: 512M cpus: 0.5 logging: driver: json-file options: max-size: 50m max-file: 3 redis: image: redis:7-alpine container_name: openclaw-redis restart: unless-stopped command: redis-server --requirepass ${REDIS_PASSWORD} --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru volumes: - redis_data:/data networks: - openclaw-net healthcheck: test: [CMD, redis-cli, -a, ${REDIS_PASSWORD}, ping] interval: 10s timeout: 3s retries: 5 deploy: resources: limits: memory: 256M cpus: 0.25 logging: driver: json-file options: max-size: 50m max-file: 3 openclaw-gateway: image: ${OPENCLAW_IMAGE:-ghcr.io/openclaw/openclaw:latest} container_name: openclaw-gateway restart: unless-stopped environment: NODE_ENV: production OPENCLAW_TOKEN: ${OPENCLAW_TOKEN} OPENCLAW_LOG_LEVEL: ${LOG_LEVEL:-info} DATABASE_URL: postgresql://${POSTGRES_USER:-openclaw}:${POSTGRES_PASSWORD}postgres:5432/${POSTGRES_DB:-openclaw} REDIS_URL: redis://default:${REDIS_PASSWORD}redis:6379 TAOTOKEN_API_KEY: ${TAOTOKEN_API_KEY} TAOTOKEN_BASE_URL: ${TAOTOKEN_BASE_URL} volumes: - ./config/openclaw.json:/home/node/.openclaw/openclaw.json:ro - ./logs:/home/node/.openclaw/logs:rw - ./workspace:/home/node/.openclaw/workspace:rw ports: - 127.0.0.1:18789:18789 networks: - openclaw-net depends_on: postgres: condition: service_healthy redis: condition: service_healthy deploy: resources: limits: memory: 2G cpus: 2 reservations: memory: 512M cpus: 0.5 healthcheck: test: [CMD, wget, -qO-, http://localhost:18789/healthz] interval: 30s timeout: 10s retries: 3 start_period: 60s security_opt: - no-new-privileges:true user: 1000:1000 stop_grace_period: 30s logging: driver: json-file options: max-size: 100m max-file: 5 compress: true这份配置里有几个字段值得单独说明。healthcheck用的是wget而不是curl因为 OpenClaw 的基础镜像基于 Debian slim默认不带 curl用 wget 可以避免额外装包。start_period: 60s给服务足够的启动时间避免刚启动就被判定为不健康而反复重启。user: 1000:1000让容器以非 root 用户运行配合no-new-privileges防止提权。logging.options里的max-size和max-file是日志轮转的核心compress: true会把旧日志压缩进一步省磁盘。环境隔离这块我用了三层第一层是.env文件与 compose 文件分离凭据不进版本控制第二层是每个服务只拿到自己需要的环境变量postgres 拿不到 TAOTOKEN_API_KEYgateway 拿不到 postgres 的 root 密码第三层是网络隔离所有服务在openclaw-net这个自定义 bridge 网络里通过服务名互相访问外部只能通过127.0.0.1:18789这个绑定访问 gateway。4. 验证请求健康检查、日志轮转、环境隔离逐个跑通配置写完不算完得实际验证。下面三个验证动作每个都给出命令和预期结果。4.1 健康检查探针验证先启动服务cd /opt/openclaw docker compose up -d docker compose psdocker compose ps的输出里每个服务的 STATUS 列应该显示Up (healthy)。如果显示Up (health: starting)说明还在start_period内等 60 秒再看。如果显示Up (unhealthy)说明健康检查失败了需要排查。手动测试 gateway 的健康检查端点curl -s http://127.0.0.1:18789/healthz # 预期输出ok查看 Docker 记录的健康检查历史docker inspect openclaw-gateway --format {{json .State.Health}} | jq输出里会有一个Log数组记录每次探测的时间、退出码和输出。如果ExitCode是 0说明探测成功如果是 1 或 137说明失败。这个历史对于排查为什么容器反复重启非常有用。验证自动重启手动 kill 掉 gateway 进程观察 Docker 是否自动拉起。docker compose exec openclaw-gateway kill 1 sleep 5 docker compose ps openclaw-gateway # 预期STATUS 重新变为 Up (healthy) 或 Up (health: starting)4.2 日志轮转参数验证日志轮转的配置在logging.options里但 Docker 不会主动告诉你轮转是否生效。验证方法是查看容器的 LogConfigdocker inspect openclaw-gateway --format {{json .HostConfig.LogConfig}} | jq预期输出{ Type: json-file, Config: { max-size: 100m, max-file: 5, compress: true } }如果Config是空的说明 compose 文件里的logging字段没被正确解析检查缩进和字段名。再验证实际日志文件的大小限制。Docker 的 json-file 驱动会把日志写到/var/lib/docker/containers/container-id/container-id-json.log。查看当前大小docker inspect openclaw-gateway --format {{.LogPath}} | xargs ls -lh随着服务运行这个文件会增长但不会超过 100MB。超过后 Docker 会自动轮转生成-json.log.1、-json.log.2等文件最多保留 5 个。你可以用docker compose logs查看合并后的日志不用关心底层文件。结构化日志验证OpenClaw 配置里logging.format设为json日志输出应该是 JSON 格式。验证docker compose logs --tail 5 openclaw-gateway | jq -r .level .message 2/dev/null || docker compose logs --tail 5 openclaw-gateway如果输出是 JSON 对象说明结构化日志生效如果是纯文本检查config/openclaw.json里的logging.format字段。4.3 环境隔离验证验证凭据没有泄露到不该去的地方。先确认 postgres 容器里拿不到 TaoToken 的 Keydocker compose exec postgres env | grep -i taotoken # 预期无输出再确认 gateway 容器里能拿到 Key但拿不到 postgres 的 root 密码gateway 只拿到 DATABASE_URL里面包含密码但这是连接必需的docker compose exec openclaw-gateway env | grep -E TAOTOKEN|OPENCLAW_TOKEN # 预期显示 TAOTOKEN_API_KEY 和 OPENCLAW_TOKEN值非空验证非 root 用户运行docker compose exec openclaw-gateway whoami # 预期node 或 UID 1000 对应的用户名验证端口只绑定本地ss -tlnp | grep 18789 # 预期LISTEN 0 4096 127.0.0.1:18789不是 0.0.0.0:18789如果显示0.0.0.0:18789说明 compose 文件里的ports写成了18789:18789需要改成127.0.0.1:18789:18789。这一步很关键直接暴露公网等于把 Gateway 的鉴权层暴露在扫描器面前。5. 本篇常见错排查容器反复重启日志显示健康检查失败。最常见的原因是start_period太短服务还没初始化完就被判定为不健康。OpenClaw 首次启动要连数据库、跑迁移、加载技能60 秒是保守值如果你的机器慢可以调到 120s。另一个原因是健康检查命令本身有问题比如镜像里没有 wget。验证方法docker compose exec openclaw-gateway wget -qO- http://localhost:18789/healthz如果报 command not found换成curl或node -e require(http).get(...)。日志磁盘满但 logging 配置看起来没问题。检查是不是有其他容器没配 logging或者 Docker 的默认日志驱动被改过。docker info | grep Logging Driver看全局驱动。另外OpenClaw 自己写的日志./logs目录不受 Docker 日志轮转管理需要在 OpenClaw 配置里单独设logging.maxSize和logging.maxFiles或者用 logrotate 处理。环境变量没生效容器里读到的是空值。检查.env文件的位置Docker Compose 默认从当前工作目录读.env如果你在别的目录执行docker compose需要加--env-file /opt/openclaw/.env。另一个常见错误是.env文件里有空格或引号比如TAOTOKEN_API_KEY sk-xxxDocker Compose 不会自动去空格值会变成sk-xxx带引号。写成TAOTOKEN_API_KEYsk-xxx即可。数据库连接失败报 password authentication failed。大概率是.env里的POSTGRES_PASSWORD和DATABASE_URL里的密码不一致。DATABASE_URL是在 compose 文件里用${POSTGRES_PASSWORD}拼出来的如果.env改了密码但没重建容器旧容器里还是旧密码。解决docker compose down -v清掉数据卷重建或者进 postgres 容器手动改密码。注意-v会删数据生产环境慎用。TaoToken 请求返回 401 或 403。先确认 Key 是否正确复制有没有多余空格。然后确认TAOTOKEN_BASE_URL是https://taotoken.net/api不要带尾部斜杠。如果还是失败到控制台 https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 检查 Key 是否被禁用或额度耗尽。接入细节可以参考文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。6. 下一步把编排能力延伸到长期运行场景这套 compose 配置解决的是单机生产级部署的问题。健康检查让服务挂了能自动恢复日志轮转让磁盘不会被撑爆环境隔离让凭据和资源各归其位。但如果你要跑的是长期编码任务或者多 Agent 协作的 Agent 工作流单机 Compose 会有瓶颈资源不够、无法水平扩展、故障域太大。这种场景下建议把模型调用通道和编排层分开考虑。模型通道继续用 TaoToken 统一管理编排层可以看 Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 它针对长时间运行的编码和 Agent 任务做了连接保活和额度优化。如果你用的是 Claude Code 这类工具Anthropic 兼容通道的配置方式在 https://taotoken.net/claude-code-anthropic?utm_sourcetaotoken_aicg_blog_endutm_contentClaudeCodeAnthropicutm_campaignrewrite 有说明。回到当前这篇你可以先做一件事把上面那份 compose 文件里的openclaw-gateway服务复制一份改名为openclaw-worker去掉ports绑定把depends_on保留然后docker compose up -d。这样你就有了一个最简的网关 Worker分离架构Worker 专门跑异步任务网关只负责接收请求。这是从单机编排走向多服务编排的第一步也是下一篇 K8s 部署的铺垫。
返回列表