SAMA 0.37 Docker Compose 部署指南
以官方 docker-compose 形态从零部署 SAMA 0.37.2 全栈(sama-server 双节点 + sama-client 静态构建 + nginx 双端口入口 + MinIO + MongoDB 6.0.11 + Redis),覆盖 .env.docker 全量配置、数据库迁移、静态资源卷初始化两个必踩坑、注册/登录/频道/消息/文件全链路验证与数据持久化,2026-10-30 实测通过。
SAMA 0.37 Docker Compose 部署指南
目标:用官方主推的 Docker Compose 形态在单机部署一套可用的 SAMA 聊天服务(7 容器全栈),并完成注册、登录、频道、消息、文件上传下载的全链路验证。本文所有命令与回显均来自 2026-10-30 在 Ubuntu(Docker 28 + Compose v2)上的实测。SAMA 未提供原生二进制发行包,服务端以 Node.js 源码 + Docker 为唯一官方部署形态。组件背景见 《SAMA 0.37 介绍与选型指南》。
1. 前置条件
| 项 | 要求(实测环境) |
|---|---|
| 操作系统 | Ubuntu 22.04(任意可跑 Docker 的 Linux) |
| Docker / Compose | Docker 24+,Compose v2(docker compose version) |
| 硬件 | 2C4G 起步(7 容器全栈实测内存峰值约 1.5GB) |
| 端口 | 宿主机暴露 8161(Web 客户端)、8162(API/WS),可自行调整 |
| 网络 | 构建期需访问 GitHub(client 容器克隆源码)与 npm registry |
2. 获取源码与目录结构
git clone https://github.com/SAMA-Communications/sama-server.git
cd sama-server
关键文件(实测目录):
sama-server/
├── Dockerfile.local # server 镜像:node:22 构建期 npm install,运行期 pm2-runtime
├── docker-compose.yml # 官方全栈编排(extends prefs/docker-compose/services/*.yml)
├── prefs/
│ ├── client/Dockerfile.frontend # client 镜像:alpine/git 克隆 sama-client → node:22 构建 → 静态产物
│ ├── nginx/Dockerfile.nginx # nginx 镜像 + default.conf(upstream 双节点负载均衡)
│ └── docker-compose/services/ # frontend/backend/s3/db/push-daemon 分文件定义
├── migrations/ # migrate-mongo 迁移脚本(必须执行,见 §5 坑一)
└── pm2.config.cjs # 容器内 PM2 集群配置
3. 编写 .env.docker(实测全量)
在 sama-server/ 根目录创建 .env.docker(server、s3、s3-service 共用),以下为实测通过的最小完整集,密钥请全部换成自己的随机值:
NODE_ENV=production
APP_PORT=9001
# MongoDB / Redis(容器网段 IP 或服务名,见 §4 静态 IP)
MONGODB_URL=mongodb://mongo/samadb
REDIS_URL=redis://redis:6379
# 对象存储:MinIO 驱动
STORAGE_DRIVER=minio
MINIO_ROOT_USER=<minio-root-user>
MINIO_ROOT_PASSWORD=<minio-root-password>
MINIO_ACCESS_KEY=<s3-service-账号-access-key>
MINIO_SECRET_KEY=<s3-service-账号-secret-key>
MINIO_BUCKET_NAME=samabucket
MINIO_USE_SSL=false
# 会话/消息预载与上限
CONVERSATION_MAX_PARTICIPANTS=50
CONVERSATION_PRELOAD_COUNT=30
MESSAGE_PRELOAD_COUNT=30
SEARCH_PRELOAD_COUNT=30
# JWT:access 3 小时,refresh 14 天
JWT_ACCESS_SECRET=<openssl rand -hex 32>
JWT_ACCESS_TOKEN_EXPIRES_IN=10800
JWT_REFRESH_SECRET=<openssl rand -hex 32>
JWT_REFRESH_TOKEN_EXPIRES_IN=12096000
# 加密消息/设备令牌 TTL(秒)
ENCRYPTION_MESSAGE_EXPIRED_IN=2592000
ENCRYPTION_DEVICE_TOKEN_EXPIRES_IN=2592000
OPERATIONS_LOG_EXPIRES_IN=1209600
NODE_CLUSTER_DATA_EXPIRES_IN=10000
# 文件预签名 URL 有效期(秒):上传 1h,下载 7d
FILE_UPLOAD_URL_EXPIRES_IN=3600
FILE_DOWNLOAD_URL_EXPIRES_IN=604800
SSL_KEY_FILE_NAME=
SSL_CERT_FILE_NAME=
CORS_ORIGIN=http://localhost:8162
COOKIE_SECRET=<openssl rand -hex 32>
HTTP_ADMIN_API_KEY=<管理端点 API Key>
MINIO_ROOT_USER/PASSWORD是 MinIO 管理员;MINIO_ACCESS_KEY/SECRET_KEY是给 SAMA server 用的服务账号(由 s3-service 容器用mc admin user svcacct add创建,见 §4)。两者必须不同。参数逐项含义见 《SAMA 0.37 配置参数详解》。
4. docker-compose.yml(实测可用的完整版)
官方 docker-compose.yml 使用 extends 组合,可直接使用;以下为展开后的等效关键结构(实测 IP 规划:mongo .4 / redis .5 / s3 .2,server 用服务名解析亦可):
services:
sama-client: # 一次性构建容器
build:
context: ./prefs/client
dockerfile: Dockerfile.frontend
args:
VITE_SOCKET_CONNECT: ws://localhost:8162 # 浏览器侧 WS 地址
VITE_HTTP_CONNECT: http://localhost:8162 # 浏览器侧 HTTP 地址
VITE_PUBLIC_VAPID_KEY: <WebPush 公钥>
VITE_MESSAGES_COUNT_TO_PRELOAD: 30
BRANCH: main # 锁定 client 分支
volumes: [sama-client-dist:/dist]
command: /bin/sh -c "cp -r /tmp/dist/* /dist && echo 'Static files are ready'"
nginx:
depends_on: [sama-client, sama-server]
ports: ["8162:9000", "8161:10000"] # 8162=API/WS(LB), 8161=Web SPA
volumes: [sama-client-dist:/usr/share/nginx/html:ro]
restart: always
sama-server:
build: { context: ., dockerfile: Dockerfile.local }
depends_on: { mongo: { condition: service_healthy } }
entrypoint: sh -c 'npm run start'
volumes: [./.env.docker:/app/.env]
environment: ["MINIO_ENDPOINT=s3", "MINIO_PORT=9000"]
restart: always
s3:
image: minio/minio
command: server --address 0.0.0.0:9000 --console-address :9001 /data
volumes: [data:/data]
env_file: [./.env.docker]
healthcheck: { test: ["CMD", "mc", "ready", "local"], interval: 30s, retries: 3 }
s3-service: # 一次性初始化容器
image: minio/minio
depends_on: { s3: { condition: service_healthy } }
env_file: [./.env.docker]
entrypoint: >
/bin/sh -c "
/usr/bin/mc alias set sama http://s3:9000 $${MINIO_ROOT_USER} $${MINIO_ROOT_PASSWORD};
/usr/bin/mc mb --ignore-existing sama/samabucket;
/usr/bin/mc policy set public sama/samabucket;
/usr/bin/mc admin user svcacct add --access-key $${MINIO_ACCESS_KEY} --secret-key $${MINIO_SECRET_KEY} sama $${MINIO_ROOT_USER}"
mongo:
image: mongo:6.0.11
volumes: [mongo:/data/db]
environment: ["MONGO_INITDB_DATABASE=samadb"]
healthcheck: { test: echo 'db.runCommand("ping").ok' | mongosh localhost:27017/samadb --quiet, interval: 30s, retries: 3 }
restart: always
redis:
image: redis
restart: always
volumes: { data: {}, mongo: {}, sama-client-dist: {} }
nginx 双 server 块(实测 prefs/nginx/default.conf):listen 9000 把 / 反代到 upstream loadbalance { server sama-server:9001; server sama-server:9002; }(带 Upgrade 头支持 WS);listen 10000 直接服务 SPA 静态文件并 try_files $uri /index.html。
启动:
docker compose up -d --build
实测构建约 8–15 分钟(client 的 npm install 是大头);7 个容器最终形态:
quzuo-sama-nginx-1 Up 0.0.0.0:8162->9000/tcp, 0.0.0.0:8161->10000/tcp
quzuo-sama-sama-server-1 Up 52403/tcp(内部 PM2 双节点 9001/9002)
quzuo-sama-s3-service-1 Exited (0) # 一次性任务,正常退出
quzuo-sama-sama-client-1 Exited (0) # 一次性构建,正常退出
quzuo-sama-s3-1 Up (healthy)
quzuo_sama_mongo Up (healthy)
quzuo_sama_redis Up
5. 两个必踩坑(实测踩坑实录)
坑一:必须手动执行数据库迁移,否则无法注册用户
首次启动后若直接注册,WS 返回 422 INCORRECT_LOGIN 类错误;查看数据库会发现 organizations 集合为空。SAMA 把生成 default 组织的工作放在 migrate-mongo 迁移里,镜像启动命令并不执行它。实测执行:
docker compose exec sama-server sh -c "cd /app && npm run migrate-mongo up"
# MIGRATED UP: 20240812113744-set-encrypted-key-indexes.js
# ...(共 20 个迁移)
# MIGRATED UP: 20260204123952-blocked-users-unique.js
验证:organizations 中出现 {_id: 'default', name: 'default'}。建议把这条命令加入首次部署清单或自定义 entrypoint。
坑二:client 容器只 echo 不拷贝,SPA 打开是 nginx 欢迎页
官方 prefs/docker-compose/services/frontend.yml 中 client 容器命令是 cp -r /tmp/dist/* /dist && echo 'Static files are ready';镜像本身把产物构建到 /tmp/dist 并声明 VOLUME /dist,但镜像内没有任何指令把 /tmp/dist 拷进 /dist——volume 内容完全依赖运行命令完成拷贝。若 compose 里写成了只 echo(或漏掉 command),8161 端口将一直是 nginx 默认欢迎页。实测补救(无需重新构建镜像):
docker run --rm -v <project>_sama-client-dist:/dist sama-sama-client \
sh -c "cp -r /tmp/dist/* /dist/"
curl -s http://localhost:8161/ | grep -o '<title>[^<]*' # → <title>SAMA
6. 全链路功能验证(2026-10-30 实测)
API 走 8162,Web 客户端打开 http://<host>:8161。用 wscat/Node ws 连 ws://<host>:8162 逐项验证(完整报文与回显见 《SAMA 0.37 API 详解》):
| # | 动作 | 实测结果 |
|---|---|---|
| 1 | GET /health | {"status":"ok","hostname":"<容器ID>"} |
| 2 | WS user_create 注册 alice/bob | 返回 response.user._id |
| 3 | POST /login | 200,返回 access_token + Set-Cookie refresh_token |
| 4 | WS connect(token+device_id) | {"response":{"success":true}} |
| 5 | conversation_create type=c 频道 | 返回频道 _id、subscribers_count:2 |
| 6 | WS message 发送 | {"ask":{"mid":"mid-001","server_mid":"...","t":...}} |
| 7 | bob conversation_subscribe | {"response":{"success":true}} |
| 8 | bob message_list | 收到 alice 消息体 |
| 9 | create_files 申请上传 URL | 返回 upload_url(MinIO 预签名) |
| 10 | HTTP PUT 上传文件到预签名 URL | 200 |
| 11 | get_file_urls 下载 URL | 返回 7 天有效期预签名 URL |
| 12 | 发送带附件消息 | ask.server_mid 返回,消息体含 attachments |
7. 数据持久化与运维
- 卷:
data(MinIO 对象)、mongo(数据库)、sama-client-dist(前端静态文件)。备份 MongoDB 卷 + MinIO 卷即可完整恢复聊天数据。 - 升级:
git pull && docker compose up -d --build;跨版本先npm run migrate-mongo status确认迁移状态,再执行up。 - 重置:
docker compose down -v会连卷一起删除(含聊天记录),慎用。 - HTTPS:公网部署建议在 nginx 前再加一层 TLS 或改写
Dockerfile.nginx挂证书,并把VITE_*_CONNECT与CORS_ORIGIN改为https://域名(Web Push 强制要求 HTTPS)。
参数如何调优继续阅读 《SAMA 0.37 配置参数详解》;API 对接阅读 《SAMA 0.37 API 详解》。