在编写 docker-compose.yml 之前,必须厘清 OpenClaw 的架构分层:
- Web Frontend (Next.js):提供可视化 Skill 编排界面,依赖 Node.js 运行时。
- API Server (Python/FastAPI):核心逻辑层,处理 Agent 调度、工具调用(Tool Use)与上下文管理。
- Worker (Celery):异步任务执行器,负责耗时操作(如文档解析、RAG 检索、外部 API 调用)。
- Message Broker (Redis):Celery 的任务队列后端,建议启用持久化防止任务丢失。
- Metadata DB (PostgreSQL):存储 Agent 配置、对话历史、用户权限等结构化数据。
- Vector Store (Qdrant/Milvus):可选组件,用于 RAG 场景的向量检索。
关键认知:Worker 与 API Server 必须共享相同的代码版本与环境变量,否则会出现序列化错误。
二、Docker Compose 编排实战
以下配置经过阿里云 ECS(2C4G)与本地 NAS(QNAP TS-464C)双环境验证,针对国内网络环境优化了镜像源与构建策略。
version: "3.8"
services:
# 1. PostgreSQL 主库
postgres:
image: postgres:15-alpine
container_name: openclaw_db
restart: unless-stopped
environment:
POSTGRES_USER: ${DB_USER:-openclaw}
POSTGRES_PASSWORD: ${DB_PASSWORD:-changeme}
POSTGRES_DB: ${DB_NAME:-openclaw_prod}
volumes:
- ./data/postgres:/var/lib/postgresql/data
- ./init-scripts:/docker-entrypoint-initdb.d # 初始化 SQL
networks:
- openclaw_net
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER} -d ${DB_NAME}"]
interval: 10s
timeout: 5s
retries: 5
# 2. Redis 消息队列
redis:
image: redis:7-alpine
container_name: openclaw_redis
restart: unless-stopped
command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD:-redis123}
volumes:
- ./data/redis:/data
networks:
- openclaw_net
# 3. API Server 主服务
api:
build:
context: ./backend
dockerfile: Dockerfile.prod # 使用多阶段构建减少体积
container_name: openclaw_api
restart: unless-stopped
environment:
- DATABASE_URL=postgresql://${DB_USER}:${DB_PASSWORD}@postgres:5432/${DB_NAME}
- REDIS_URL=redis://:${REDIS_PASSWORD}@redis:6379/0
- SECRET_KEY=${SECRET_KEY:-your-secret-key-here}
- LLM_API_KEY=${LLM_API_KEY} # 阿里云百炼或 OpenAI Key
- LLM_BASE_URL=${LLM_BASE_URL:-https://dashscope.aliyuncs.com/compatible-mode/v1}
- LOG_LEVEL=INFO
volumes:
- ./logs:/app/logs
- ./uploads:/app/uploads # 文件上传目录
networks:
- openclaw_net
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_started
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3
# 4. Celery Worker 异步任务
worker:
build:
context: ./backend
dockerfile: Dockerfile.prod
container_name: openclaw_worker
restart: unless-stopped
command: celery -A app.core.celery worker -l info -c 4 # 并发数4
environment:
- DATABASE_URL=postgresql://${DB_USER}:${DB_PASSWORD}@postgres:5432/${DB_NAME}
- REDIS_URL=redis://:${REDIS_PASSWORD}@redis:6379/0
- SECRET_KEY=${SECRET_KEY}
- LLM_API_KEY=${LLM_API_KEY}
- LLM_BASE_URL=${LLM_BASE_URL}
- PYTHONPATH=/app
volumes:
- ./logs:/app/logs
- ./uploads:/app/uploads
networks:
- openclaw_net
depends_on:
- redis
- postgres
# 5. Frontend 前端界面
web:
build:
context: ./frontend
dockerfile: Dockerfile
container_name: openclaw_web
restart: unless-stopped
environment:
- NEXT_PUBLIC_API_URL=http://api:8000 # 内部通信
- NEXT_PUBLIC_WS_URL=ws://api:8000/ws
networks:
- openclaw_net
depends_on:
- api
# 6. Nginx 反向代理(生产必需)
nginx:
image: nginx:alpine
container_name: openclaw_nginx
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/ssl:/etc/nginx/ssl:ro # SSL证书
- ./logs/nginx:/var/log/nginx
networks:
- openclaw_net
depends_on:
- web
- api
networks:
openclaw_net:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/16
volumes:
postgres_data:
redis_data:
关键配置解析:
- 健康检查(Healthcheck):PostgreSQL 和 API 服务均配置了健康检查,避免服务未就绪时导致依赖方启动失败。
- 资源限制:Worker 通过
-c 4限制并发数为 4,防止低配置服务器(2C4G)因并发过高导致 OOM。 - 网络隔离:自定义
openclaw_net网段(172.20.0.0/16),避免与宿主机现有网络冲突。
三、生产环境踩坑实录
1. 权限陷阱:容器内非 root 运行
默认情况下,Celery Worker 若以 root 运行,会触发 Python 的 RuntimeWarning,且存在安全风险。需在 Dockerfile.prod 中显式创建用户:
# Dockerfile.prod 片段
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
RUN chown -R appuser:appgroup /app/logs /app/uploads
USER appuser
坑点:若宿主机挂载的 ./logs 目录权限为 root,容器内非 root 用户将无写入权限,导致 Worker 启动失败。解决命令:
chown -R 1000:1000 ./logs ./uploads # 1000 为容器内 appuser 的 UID
2. 数据库连接池耗尽
OpenClaw 默认使用 SQLAlchemy 的默认连接池配置,在高并发场景下(如批量导入文档),极易触发 FATAL: sorry, too many clients already。
解决方案:在 API 和 Worker 的环境变量中增加连接池配置:
environment:
- DATABASE_POOL_SIZE=10
- DATABASE_MAX_OVERFLOW=20
- DATABASE_POOL_RECYCLE=3600
并在 PostgreSQL 侧调整 max_connections:
# 在 init-scripts/01-config.sql 中
ALTER SYSTEM SET max_connections = 200;
3. 国内网络下的模型 API 超时
默认配置下,OpenClaw 请求阿里云百炼或 OpenAI API 时,若宿主机位于国内且未配置代理,常因网络抖动导致 ReadTimeout。
硬核解决方案:在 Docker Compose 中注入代理环境变量,并配置 DNS:
services:
api:
environment:
- HTTP_PROXY=http://host.docker.internal:7890 # Clash 宿主机代理
- HTTPS_PROXY=http://host.docker.internal:7890
- NO_PROXY=postgres,redis,localhost,127.0.0.1
dns:
- 223.5.5.5 # 阿里 DNS,避免运营商 DNS 污染
- 8.8.8.8
worker:
environment:
- HTTP_PROXY=http://host.docker.internal:7890
- HTTPS_PROXY=http://host.docker.internal:7890
注意:若使用 host 网络模式,需将 host.docker.internal 替换为宿主机实际内网 IP。
四、性能调优与监控
1. 资源限制与 OOM 防护
生产环境务必设置内存限制,防止某个服务内存泄漏拖垮整台宿主机:
services:
worker:
deploy:
resources:
limits:
cpus: '1.5'
memory: 2G
reservations:
cpus: '0.5'
memory: 512M
2. 日志轮转(Logrotate)
容器默认日志存储于 JSON 文件,长期运行会撑爆磁盘。需在宿主机配置 logrotate:
# /etc/logrotate.d/docker-container-logs
/var/lib/docker/containers/*/*.log {
rotate 7
daily
compress
size=100M
missingok
delaycompress
copytruncate
}
或使用 Docker 自带的日志驱动限制:
services:
api:
logging:
driver: "json-file"
options:
max-size: "100m"
max-file: "3"
结语
OpenClaw 的出现,标志着个人或中小团队构建私有化 AI Agent 工作流的门槛已大幅降低。通过 Docker Compose 进行自托管,不仅能完全掌控数据主权,更能基于内部业务系统深度定制 Skill 工具链。
然而,生产级部署绝非简单的 docker-compose up -d。从数据库连接池调优、容器权限管控到网络代理的精细配置,每一个细节都关乎系统的稳定性与安全性。希望本文的硬核拆解,能帮助你在私有化部署的道路上避开那些我曾踩过的深坑。
你目前在部署 OpenClaw 或类似的 AI Agent 平台时遇到了哪些棘手问题?欢迎在评论区分享你的报错日志与架构设计,我们一起探讨更优雅的解法。