Keel 自建部署 SOP

操作手册:从零部署一套 Keel 自建实例(Update server + Cloud Build server + 前端 CDN)。目标用户:自建 Keel 后端的运维 / DevOps, 不是普通 Keel 用户(普通用户用 npx @keel-ai/cli ... 即可)。

预计时长:~60 分钟(不含审批等待)。

0. 前置条件

域名1 个根域,如 mycorp.dev。下设 update.mycorp.dev + cdn.mycorp.dev 两个子域。
SSL 证书Let’s Encrypt 或 阿里云免费证书。两个子域各 1 张。
服务器1 台 4C8G+ ECS / VPS 跑 Update server + Cloud Build server。或拆两台。
阿里云账号OSS bucket + CDN domain。
DiskUpdate server 元数据走 SQLite(默认);后续会加 Postgres backend。

1. 阿里云 OSS bucket

# 1.1 创建 bucket(华东 1 / 华北 2 任选;建议跟服务器同区域)
aliyun oss mb oss://keel-bundles --acl=public-read \
  --region=cn-hangzhou

# 1.2 创建 RAM 用户 + 给它 OSS 写权限
aliyun ram CreateUser --UserName keel-update-uploader
aliyun ram CreateAccessKey --UserName keel-update-uploader
# ↑ 记下 AccessKeyId + AccessKeySecret,下面填到 KEEL_UPDATE_S3_*

# 1.3 把 OSS-Read-Write policy 绑给这个用户(限定到这个 bucket)
cat > /tmp/keel-policy.json <<'EOF'
{
  "Version": "1",
  "Statement": [{
    "Effect": "Allow",
    "Action": ["oss:PutObject", "oss:GetObject", "oss:DeleteObject"],
    "Resource": ["acs:oss:*:*:keel-bundles/*"]
  }]
}
EOF
aliyun ram CreatePolicy --PolicyName keel-bundles-rw \
  --PolicyDocument file:///tmp/keel-policy.json
aliyun ram AttachPolicyToUser --PolicyName keel-bundles-rw \
  --PolicyType Custom --UserName keel-update-uploader

2. 阿里云 CDN

# 2.1 创建 CDN 域名指向 OSS bucket
aliyun cdn AddCdnDomain \
  --DomainName cdn.mycorp.dev \
  --CdnType download \
  --Sources '[{"content":"keel-bundles.oss-cn-hangzhou.aliyuncs.com","type":"oss","port":80,"priority":20,"weight":15}]'

# 2.2 上传 SSL 证书 + 启用 HTTPS
aliyun cdn SetDomainServerCertificate \
  --DomainName cdn.mycorp.dev \
  --ServerCertificateStatus on \
  --CertName keel-cdn-cert \
  --ServerCertificate "$(cat fullchain.pem)" \
  --PrivateKey "$(cat privkey.pem)"

# 2.3 域名解析(在你的 DNS 控制台):
#   CNAME  cdn.mycorp.dev → cdn.mycorp.dev.w.alikunlun.com (CDN console 给的目标)

3. SQLite 数据目录

# Update server 默认用 SQLite 存 manifest + patch 元数据。
# 单实例 + 本地 SSD 够 100k 设备的 polling 流量。
# Postgres backend 在路线图上,目前只支持 mem / sqlite:<path>。
mkdir -p /var/lib/keel
chown keel:keel /var/lib/keel

4. Update server 部署

# 4.1 服务器上 clone + build
git clone https://github.com/appunvs/keel.git
cd keel
go build -o /usr/local/bin/keel-update-server ./cmd/update-server

# 4.2 systemd unit
cat > /etc/systemd/system/keel-update.service <<'EOF'
[Unit]
Description=KAS Update Server
After=network.target

[Service]
EnvironmentFile=/etc/keel/update.env
ExecStart=/usr/local/bin/keel-update-server
Restart=always
RestartSec=5
User=keel

[Install]
WantedBy=multi-user.target
EOF

# 4.3 env 文件
mkdir -p /etc/keel
cat > /etc/keel/update.env <<EOF
KEEL_UPDATE_LISTEN=:8090
KEEL_UPDATE_DB=sqlite:/var/lib/keel/update.db
KEEL_UPDATE_BLOB_STORE=s3
KEEL_UPDATE_S3_ENDPOINT=https://oss-cn-hangzhou.aliyuncs.com
KEEL_UPDATE_S3_REGION=cn-hangzhou
KEEL_UPDATE_S3_BUCKET=keel-bundles
KEEL_UPDATE_S3_ACCESS_KEY=<from step 1.2>
KEEL_UPDATE_S3_SECRET_KEY=<from step 1.2>
KEEL_UPDATE_PUBLIC_URL_BASE=https://cdn.mycorp.dev
EOF
chmod 600 /etc/keel/update.env

# 4.4 启动
systemctl enable --now keel-update
systemctl status keel-update

5. nginx / 反向代理

server {
    listen 443 ssl http2;
    server_name update.mycorp.dev;

    ssl_certificate     /etc/letsencrypt/live/update.mycorp.dev/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/update.mycorp.dev/privkey.pem;

    # Bundle uploads can be tens of MB — raise default limits.
    client_max_body_size 128m;
    proxy_request_buffering off;
    proxy_read_timeout 300s;

    location / {
        proxy_pass http://127.0.0.1:8090;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

6. Cloud Build server (可选)

如果你只用 OTA 不需要服务端编 bundle,可以跳过这一步。你的 RN 开发者本地用 keel build --local 编完直接 keel publish 就行。

如果要让 Cloud Build 跑(远程编 bundle,类似 EAS Build):

go build -o /usr/local/bin/keel-build-server ./cmd/build-server

# Cloud Build 在 Docker 容器里编 metro + Hermes,需要 docker。
apt-get install -y docker-ce docker-ce-cli containerd.io
systemctl enable --now docker

# build the metro+hermes builder image
bash cloud/packaging/build-cloud-build.sh

# systemd unit + nginx 类似 Update server。监听 :8091。

7. 烟测

# 7.1 健康检查
curl https://update.mycorp.dev/health
# {"status":"ok"}

# 7.2 上传一个 bundle (multipart 模式)
curl -X POST https://update.mycorp.dev/v1/myproj/update/bundles \
  -F 'metadata={"version":"1.0.0","content_hash":"sha256:abc...","platform":"ios","channel":"production","runtime_version":"1.0","size_bytes":1234};type=application/json' \
  -F 'bundle=@dist/index.ios.bundle;type=application/octet-stream'

# 7.3 promote + 拉 manifest
curl -X POST https://update.mycorp.dev/v1/myproj/update/bundles/abc.../promote \
  -H 'Content-Type: application/json' \
  -d '{"platform":"ios","channel":"production","runtime_version":"1.0"}'

curl 'https://update.mycorp.dev/v1/myproj/update/manifest?platform=ios&channel=production&runtime_version=1.0'
# {"current":{"hash":"sha256:abc...","url":"https://cdn.mycorp.dev/...",...},"patches":[]}

8. 灰度发布

# 50% 流量
curl -X POST https://update.mycorp.dev/v1/myproj/update/bundles/<hash>/promote \
  -d '{"platform":"ios","channel":"production","runtime_version":"1.0","rollout":50}'

# 看着 metrics 没崩 → 推全量
curl -X POST https://update.mycorp.dev/v1/myproj/update/bundles/<hash>/promote \
  -d '{"platform":"ios","channel":"production","runtime_version":"1.0","rollout":100}'

# 出事 → 回滚到上一个 hash(已经是 promoted 状态过的 bundle)
keel rollback --to=<previous-hash>

9. Submit driver 凭据

如果你要用 KAS Submit 自动上架(不只是 OTA),需要给每个市场配 凭据。每个 driver 的具体凭据见 keel/cloud/internal/submit/drivers/<market>/Cred* 常量。

# 例:苹果 App Store
keel submit configure ios_appstore \
  --issuer_id="..." --key_id="..." --p8_path="./AuthKey_XXX.p8" \
  --bundle_id="com.mycorp.app"

# 例:华为 AGC
keel submit configure huawei \
  --app_id="..." --client_id="..." --client_secret="..." \
  --project_id="..."

凭据存在 ~/.keel/credentials.yml,权限 0600。CI 流水线建议用 环境变量覆盖(每个 driver 的 README 会列)。

10. 监控 + 容灾

做法
健康监控UptimeRobot 5min ping update.mycorp.dev/health
日志systemd-journald → ELK / 阿里云 SLS(参考 Mortar SLS 集成)
Postgres 备份每日全量 + 1h binlog;阿里云 RDS 默认带
OSS 备份OSS 跨区域复制启用一次;多 region 同步
CDN 故障域名解析 fallback 到 OSS 直连域

11. 升级

cd /opt/keel
git pull
go build -o /usr/local/bin/keel-update-server ./cmd/update-server
systemctl restart keel-update

数据库迁移 Update server 自动跑(启动时扫 internal/update/ schema 文件)。

故障排查清单

症状可能原因
/health 返回 503DB 不可达 / OSS endpoint 错 / 凭据失效
Manifest 200 但 bundle URL 404OSS bucket 未开 public-read 或 CDN 没回源
Patch 流没生效客户端 current_hash 不在 patches 中 → 客户端走全量下载(正常)
Submit 报 401凭据过期或 client_id 写错
Cloud Build 任务卡住docker daemon 挂了 / 磁盘满 / metro 缓存损坏

参考