这是「Docker Compose」系列的第一篇。我打算把自己在跑、用着舒服的那些 Compose 部署慢慢分享出来。第一篇就从群里那个 QQ 机器人开始。
目前正在群里跑的 QQ 机器人,背后是这么一套东西:机器人框架(AstrBot)+ QQ 接入(PMHQ / LLBot)+ 代码沙盒(Docker Socket Proxy / Shipyard)+ 订阅推送(nonebot-bison / we-mp-rss)。
⚠️ 下面所有密钥、IP、QQ 号都换成了占位符,真实值请放进自己的 .env,不要直接提交/公开。
# ============================================================
# QQ Bot All-in-One — AstrBot 全栈编排
# 提示:Compose V2 已不需要顶部 version 字段,故删除
# 提示:敏感值统一用 ${VAR} 占位,真实值写进同目录 .env
# ============================================================
services:
# ──────────────────────────────────────────────
# 🛡️ 安保组|Docker Socket 门神
# 沙盒功能需要操作 Docker,但绝不把整个 docker.sock 直接交给上层。
# 用代理只放行「建/销毁容器」这类最小权限,挡住提权与横向移动。
# ──────────────────────────────────────────────
dockerproxy:
image: tecnativa/docker-socket-proxy:latest
container_name: docker-proxy
restart: always
privileged: false # 代理本身不需要特权
environment:
- CONTAINERS=1 # 允许创建/管理沙盒容器
- NETWORKS=1 # 允许为沙盒分配网络
- IMAGES=1 # 允许拉取沙盒镜像
- POST=1 # 允许写类请求(创建等)
- DELETE=1 # 允许销毁跑完的废弃沙盒
- EXEC=0 # ❌ 禁止进入其它容器执行命令(防横向移动)
- VOLUMES=1 # 允许操作数据卷
- SYSTEM=0 # ❌ 禁止系统级接管
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro # 只读挂载宿主 socket
networks:
- astrbot_network
# ──────────────────────────────────────────────
# ⚙️ 沙盒组|Shipyard Neo Bay(沙盒控制面)
# 机器人要执行代码时,由它去(经上面的代理)创建/回收一次性沙盒容器。
# ──────────────────────────────────────────────
shipyard-bay:
image: shipyard-neo-bay:latest
container_name: shipyard-bay
restart: always
environment:
- BAY_API_KEY=${BAY_API_KEY} # 控制面鉴权密钥 —— 放进 .env,切勿明文
volumes:
- /mnt/data/docker_data/astrbot/data/shipyard/bay_data:/app/data
- /mnt/data/docker_data/astrbot/data/shipyard/cargos:/var/lib/bay/cargos # 沙盒产物/缓存
- /mnt/data/docker_data/astrbot/data/shipyard-neo/config.yaml:/app/config.yaml:ro
networks:
- astrbot_network
# ──────────────────────────────────────────────
# 🤖 核心组|AstrBot(机器人大脑 / 网关)
# 消息路由、插件、LLM 调用都在这;唯一对外开放 WebUI 端口。
# ──────────────────────────────────────────────
astrbot:
image: soulter/astrbot:latest
container_name: astrbot
restart: always
ports:
- "6185:6185" # WebUI(公网机建议绑 127.0.0.1 或走反代)
volumes:
- /mnt/data/docker_data/astrbot/data:/AstrBot/data
networks:
- astrbot_network
- ragflow # 额外接 RAGFlow 知识库网络
environment:
- TZ=Asia/Taipei
# ──────────────────────────────────────────────
# 💬 QQ 接入组(一)|PMHQ(NTQQ 进程层)
# 注意:这里用了 privileged —— 与上面的最小权限隔离是反例,
# 但 NTQQ 运行需要,属于「明知风险、单点破例」,已用内部网络收口。
# ──────────────────────────────────────────────
pmhq:
image: linyuchen/pmhq:latest
container_name: pmhq
privileged: true # ⚠️ NTQQ 需要;仅此一处破例
restart: unless-stopped
environment:
- ENABLE_HEADLESS=false # 有头更稳定;想省内存可设 true
- AUTO_LOGIN_QQ=${AUTO_LOGIN_QQ:-} # 自动登录的 QQ 号,留空则手动扫码
volumes:
- /mnt/data/docker_data/astrbot/llbot/QQ:/root/.config/QQ # QQ 登录态/数据
- /mnt/data/docker_data/astrbot/llbot/config:/app/llbot/data:rw
- /mnt/data/docker_data/astrbot/data:/AstrBot/data
networks:
- astrbot_network
healthcheck: # 探测 NTQQ 进程是否就绪
test: ["CMD", "curl", "-f", "http://localhost:13000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
# ──────────────────────────────────────────────
# 💬 QQ 接入组(二)|LLBot(OneBot/协议层,替代 NapCat)
# 把 PMHQ 的 NTQQ 包装成 OneBot 协议,再喂给 AstrBot。
# ──────────────────────────────────────────────
llbot:
image: linyuchen/llbot:latest
container_name: llbot
restart: unless-stopped
extra_hosts:
- "host.docker.internal:host-gateway" # 容器内回连宿主机
environment:
- PMHQ_HOST=pmhq # 指向上面的 NTQQ 进程层
- WEBUI_PORT=3080
ports:
- "3080:3080" # WebUI
- "3001:3001" # OneBot 11 WebSocket(按协议配置增删)
volumes:
- /mnt/data/docker_data/astrbot/llbot/QQ:/root/.config/QQ
- /mnt/data/docker_data/astrbot/llbot/config:/app/llbot/data:rw
- /mnt/data/docker_data/astrbot/data:/AstrBot/data
networks:
- astrbot_network
depends_on:
- pmhq # 想更稳可改 condition: service_healthy
healthcheck: # 探测 node 进程是否存活
test: ["CMD", "sh", "-c", "ps | grep '[n]ode'"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
# ──────────────────────────────────────────────
# 📡 推送组(一)|nonebot-bison(多平台订阅推送,替换 elf-rss)
# 订阅各平台动态并推到群里;这里用 nobrowser 镜像、关掉截图省资源。
# ──────────────────────────────────────────────
bison:
image: felinae98/nonebot-bison:v0.9.14-nobrowser
container_name: bison
restart: always
networks:
- astrbot_network
ports:
- "8088:8080" # 管理后台
volumes:
- /mnt/data/docker_data/astrbot/bison/data:/data
- /mnt/data/docker_data/astrbot/bison/data/patch_rss_interval.py:/app/extra_plugins/patch_rss_interval.py:ro # 自定义补丁:调订阅间隔
environment:
- TZ=Asia/Taipei
- HOST=0.0.0.0
- PORT=8080
- DRIVER=~fastapi+~aiohttp
- COMMAND_START=["/", ""]
- SUPERUSERS=["${BISON_SUPERUSER}"] # 管理员 QQ —— 用 .env 占位
- NICKNAME=["bison"]
- BISON_CONFIG_PATH=/data
- BISON_OUTER_URL=${BISON_OUTER_URL} # 对外访问地址 —— 别把真实 IP 写死
- BISON_TO_ME=true
- BISON_USE_QUEUE=true # 推送走队列,削峰
- BISON_USE_PIC=false # 纯文本,不渲染图片(配合 nobrowser)
- BISON_INIT_FILTER=true
- BISON_FILTER_LOG=false
- BISON_RESEND_TIMES=2 # 失败重发次数
- HTMLRENDER_CI_MODE=true
depends_on:
- llbot
# ──────────────────────────────────────────────
# 📡 推送组(二)|we-mp-rss(微信公众号转 RSS)
# ──────────────────────────────────────────────
we-mp-rss:
image: ghcr.io/rachelos/we-mp-rss:latest
container_name: we-mp-rss
restart: always
networks:
- astrbot_network
ports:
- "8001:8001"
volumes:
- /mnt/data/docker_data/astrbot/we-mp-rss/data:/app/data
environment:
- TZ=Asia/Taipei
- TOKEN_EXPIRE_MINUTES=5256000 # token 有效期(约 10 年)
- DB=sqlite:////app/data/db.db
- ENABLE_JOB=True # 开启定时抓取
- SPAN_INTERVAL=15 # 抓取间隔(分钟)
- GATHER.CONTENT=False # 默认只抓标题/链接,不抓正文
- MAX_PAGE=5
- GATHER.CONTENT_AUTO_CHECK=True
- GATHER.CONTENT_AUTO_INTERVAL=5
- THREADS=4
- CUSTOM_WEBHOOK=${WE_MP_RSS_WEBHOOK} # ⚠️ 原值含账号密码,务必放 .env
- SEND_CODE=True # 授权过期时推送扫码提醒
- CODE_TITLE=WeRSS授权过期,请重新扫码
- LOG_FILE=/app/data/we-mp-rss.log
- LOG_LEVEL=DEBUG # 上线后建议降到 INFO
# 两个网络都设为 external —— 需提前 docker network create
networks:
astrbot_network:
external: true
ragflow:
external: true
整套跑起来后,群里那只机器人就同时拥有了:聊天 / 跑代码(带沙盒)/ 多平台订阅推送 三件套。具体如何部署,参见Docker相关技术,我用的是Portainer可视化面板,也推荐尝试。

评论 / Comments NOTHING