生产部署¶
本指南介绍如何使用 Docker Compose、GHCR 容器镜像和 Caddy 反向代理将 Inklet 后端和门户部署到生产服务器。
服务器目录结构¶
生产部署使用简洁的目录布局:
所有应用配置通过 .env 文件管理。Docker 镜像从 GitHub Container Registry (GHCR) 拉取。
Docker Compose¶
docker-compose.yml 文件定义了两个使用 GHCR 预构建镜像的服务:
services:
backend:
image: ghcr.io/inklet-2026/backend:latest
restart: unless-stopped
ports:
- "4000:4000"
env_file:
- .env
depends_on:
- db
portal-web:
image: ghcr.io/inklet-2026/portal-web:latest
restart: unless-stopped
ports:
- "3000:3000"
env_file:
- .env
db:
image: postgres:16-alpine
restart: unless-stopped
volumes:
- pgdata:/var/lib/postgresql/data
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
ports:
- "127.0.0.1:5432:5432"
volumes:
pgdata:
数据库备份
PostgreSQL 数据存储在 Docker 卷中。请确保制定了备份策略。建议使用 pg_dump 配合 cron 定时任务,或在生产环境中使用托管数据库服务。
Caddy 反向代理¶
Caddy 作为反向代理,通过 Let's Encrypt 自动配置 HTTPS 证书。
/etc/caddy/Caddyfile:
portal.iminklet.com {
reverse_proxy localhost:3000
}
auth.iminklet.com {
reverse_proxy localhost:4000
}
Caddy 自动完成以下工作:
- 从 Let's Encrypt 获取并续期 TLS 证书
- 将 HTTP 重定向到 HTTPS
- 提供 HTTP/2 和 HTTP/3 支持
安装 Caddy
# Debian/Ubuntu
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy
编辑 Caddyfile 后,重新加载 Caddy:
CI/CD 流水线¶
Docker 镜像由 GitHub Actions 自动构建和发布。
构建触发条件¶
工作流在匹配 v* 模式的标签推送时触发:
构建流程¶
- GitHub Actions 在打标签的提交上检出代码
- 使用仓库中的
Dockerfile构建 Docker 镜像 - 同时打上版本标签和
latest标签:ghcr.io/inklet-2026/backend:v1.2.0ghcr.io/inklet-2026/backend:latest
- 将两个标签推送到 GHCR
创建发布¶
CI 流水线自动构建并推送镜像。然后在服务器上部署:
sudo docker compose -f ~/deploy/docker-compose.yml pull
sudo docker compose -f ~/deploy/docker-compose.yml up -d
部署命令¶
拉取最新镜像¶
启动/重启服务¶
查看日志¶
# 所有服务
sudo docker compose -f ~/deploy/docker-compose.yml logs -f
# 指定服务
sudo docker compose -f ~/deploy/docker-compose.yml logs -f backend
停止服务¶
回滚到指定版本¶
如果部署出现问题,将镜像固定到特定版本标签:
# 编辑 docker-compose.yml 使用指定版本
# image: ghcr.io/inklet-2026/backend:v1.1.0
sudo docker compose -f ~/deploy/docker-compose.yml pull
sudo docker compose -f ~/deploy/docker-compose.yml up -d
环境变量¶
.env 文件包含后端和门户的所有配置。在 ~/deploy/.env 创建该文件:
后端配置¶
| 变量 | 描述 | 示例 |
|---|---|---|
DATABASE_URL |
PostgreSQL 连接字符串 | postgres://user:pass@db:5432/inklet?sslmode=disable |
POSTGRES_USER |
PostgreSQL 用户名(用于数据库容器) | inklet |
POSTGRES_PASSWORD |
PostgreSQL 密码(用于数据库容器) | strong-random-password |
POSTGRES_DB |
PostgreSQL 数据库名(用于数据库容器) | inklet |
JWT_SECRET |
用于签发 JWT 令牌的密钥 | 64-char-random-hex-string |
PORT |
后端 HTTP 端口 | 4000 |
AWS IoT Core¶
| 变量 | 描述 | 示例 |
|---|---|---|
AWS_REGION |
AWS 区域 | us-east-1 |
AWS_ACCESS_KEY_ID |
用于 IoT Core 的 IAM 访问密钥 | AKIA... |
AWS_SECRET_ACCESS_KEY |
IAM 密钥 | wJal... |
IOT_ENDPOINT |
AWS IoT Core 数据端点 | xxxx-ats.iot.us-east-1.amazonaws.com |
IOT_CERT_PATH |
后端 MQTT 证书路径 | /certs/backend.cert.pem |
IOT_KEY_PATH |
后端 MQTT 私钥路径 | /certs/backend.private.key |
IOT_ROOT_CA_PATH |
AWS IoT 根 CA 路径 | /certs/root.pem |
FACTORY_SECRET |
用于 NFC 签名验证的 HMAC 密钥 | 32-byte-hex-string |
OAuth¶
| 变量 | 描述 | 示例 |
|---|---|---|
GOOGLE_CLIENT_ID |
Google OAuth 客户端 ID | 123456.apps.googleusercontent.com |
GOOGLE_CLIENT_SECRET |
Google OAuth 客户端密钥 | GOCSPX-... |
APPLE_CLIENT_ID |
Apple OAuth 服务 ID | com.iminklet.auth |
APPLE_TEAM_ID |
Apple Developer Team ID | ABC123DEF4 |
APPLE_KEY_ID |
Apple Sign In 私钥 ID | XYZ789 |
APPLE_PRIVATE_KEY |
Apple Sign In 私钥(PEM 格式) | -----BEGIN PRIVATE KEY-----\n... |
Stripe 计费¶
| 变量 | 描述 | 示例 |
|---|---|---|
STRIPE_SECRET_KEY |
Stripe API 密钥 | sk_live_... |
STRIPE_WEBHOOK_SECRET |
Stripe Webhook 签名密钥 | whsec_... |
STRIPE_PRICE_MONTHLY |
月度计划的 Stripe Price ID | price_... |
STRIPE_PRICE_YEARLY |
年度计划的 Stripe Price ID | price_... |
Portal Web¶
| 变量 | 描述 | 示例 |
|---|---|---|
VITE_AUTH_URL |
后端 API URL(门户前端使用) | https://auth.iminklet.com |
URL 和 CORS¶
| 变量 | 描述 | 示例 |
|---|---|---|
FRONTEND_URL |
门户 URL(用于 CORS 和 OAuth 重定向) | https://portal.iminklet.com |
BACKEND_URL |
后端公开 URL | https://auth.iminklet.com |
密钥管理
切勿将 .env 文件提交到版本控制。使用安全的方式将密钥传输到生产服务器(例如 scp、密钥管理器或加密存储)。
DNS 配置¶
为你的域名配置 DNS 记录:
| 记录类型 | 名称 | 值 |
|---|---|---|
| A | portal.iminklet.com |
服务器 IP 地址 |
| A | auth.iminklet.com |
服务器 IP 地址 |
DNS 指向服务器后,Caddy 会自动配置 TLS 证书。
健康检查¶
验证部署是否正常:
# 后端健康检查
curl https://auth.iminklet.com/health
# 门户(返回 HTML)
curl -s -o /dev/null -w "%{http_code}" https://portal.iminklet.com
预期结果:后端返回 {"status":"ok"},门户返回 HTTP 200。