Files
iotlcm/docs/deploy/build-image.md
T
Dai0814 f169afc1bc docs: 补充其他部署方式(二进制、自建镜像、分离、离线)
新增「其他部署方式」分组,共 5 页:
- 方式对比与选型:5 种方式对照、服务依赖表、服务间通信易错点
- 二进制部署:各服务 Go 版本/CGO/构建目录/产物名对照表,
  admin-svr 需 CGO 的说明,交叉编译限制,systemd unit 模板,
  静态前端交给 nginx,升级回滚流程
- 自建镜像:各仓 Dockerfile 位置与构建上下文、构建参数、
  镜像命名规则、批量构建脚本、常见问题
- 分离部署:中间件独立,业务机精简 compose,配置改为外部地址
- 离线部署:镜像导出导入、校验、内网镜像仓库方案

同时记录一个实际阻塞:openapi/app/admin-ui 共 5 个 Dockerfile
的 FROM 仍指向私有镜像仓库,外部环境无法构建,页内给出替换方法。
2026-10-28 14:20:06 +08:00

242 lines
7.9 KiB
Markdown

---
title: 自建镜像
description: 从源码构建各服务的 Docker 镜像
outline: deep
---
# 自建镜像
平台镜像由官方仓库分发。如有定制需求(改代码、加依赖、适配私有基础镜像),可从源码自行构建。
## 构建环境
| 项 | 要求 |
| --- | --- |
| Docker | 20.10 以上 |
| 网络 | 需访问基础镜像仓库与 Go/npm 依赖源 |
| 磁盘 | 单服务构建约 1~2 GB(含构建缓存) |
## 各服务构建文件
| 服务 | 仓库 | Dockerfile | 构建上下文 |
| --- | --- | --- | --- |
| admin-svr | `iotlcm-admin-svr` | `server/build/Dockerfile` | `server/` |
| openapi | `iotlcm-openapi` | `build/Dockerfile` | 仓库根目录 |
| worker-svr | `iotlcm-worker-svr` | `build/Dockerfile` | 仓库根目录 |
| upstream-chan | `iotlcm-upstream-chan` | `build/Dockerfile` | 仓库根目录 |
| upstream-proxy | `iotlcm-upstream-proxy` | `build/Dockerfile` | 仓库根目录 |
| app-server | `iotlcm-app` | `server/Dockerfile` | `server/` |
| app-web | `iotlcm-app` | `app/Dockerfile` | `app/` |
| admin-ui | `iotlcm-admin-ui` | `vue/build/Dockerfile` | `vue/` |
| sms | `iotlcm-sms` | `build/Dockerfile` | 仓库根目录 |
| bot | `iotlcm-bot` | `Dockerfile` | 仓库根目录 |
| docs | `iotlcm-docs` | `Dockerfile` | 仓库根目录 |
| onelink-mock | `iotlcm-onelink-mock` | `Dockerfile` | 仓库根目录 |
## ⚠️ 先处理私有基础镜像
**当前部分仓库的 Dockerfile 仍指向私有镜像仓库**,外部环境无法拉取,直接构建会失败:
| 仓库 | 文件 | 问题 |
| --- | --- | --- |
| `iotlcm-openapi` | `manifest/docker/Dockerfile` | `FROM` 指向私有仓库 |
| `iotlcm-app` | `app/Dockerfile` | 2 处 `FROM` 指向私有仓库 |
| `iotlcm-app` | `server/Dockerfile` | 2 处 `FROM` 指向私有仓库 |
| `iotlcm-admin-ui` | `vue/build/Dockerfile` | 2 处 `FROM` 指向私有仓库 |
| `iotlcm-admin-ui` | `vue/build/DockerfileNoBuild` | `FROM` 指向私有仓库 |
构建前把这些 `FROM` 改为公共镜像:
```dockerfile
# 修改前(私有仓库,外部不可达)
FROM crpi-xxxxxxxx.cn-beijing.personal.cr.aliyuncs.com/xxx/golangalpine:1.25.7-alpine3.23 AS builder
# 修改后(公共镜像)
FROM golang:1.25-alpine AS builder
```
```dockerfile
FROM crpi-xxxxxxxx.cn-beijing.personal.cr.aliyuncs.com/xxx/nginx:1.30.0-alpine
# 改为
FROM nginx:1.30-alpine
```
::: tip 其余仓库的 Dockerfile 已可用
`admin-svr`、`worker-svr`、`upstream-chan`、`upstream-proxy`、`sms`、`bot`、`docs`、`onelink-mock` 的 Dockerfile 基础镜像均为公共镜像,可直接构建。
:::
## 构建命令
### 后端服务
```bash
COMMIT=$(git rev-parse --short HEAD)
VERSION=$(git describe --tags --always)
BUILD_TIME=$(date -u '+%Y-%m-%dT%H:%M:%SZ')
docker build \
--build-arg BuildVersion=$VERSION \
--build-arg CommitID=$COMMIT \
--build-arg BuildTime=$BUILD_TIME \
-t your-registry/iotlcm/iotlcm-openapi:$VERSION \
.
```
`admin-svr` 与 `app-server` 需指定上下文目录:
```bash
cd iotlcm-admin-svr
docker build \
--build-arg BuildVersion=$VERSION \
--build-arg CommitID=$COMMIT \
--build-arg BuildTime=$BUILD_TIME \
-t your-registry/iotlcm/iotlcm-admin-svr:$VERSION \
-f server/build/Dockerfile server/
```
### 前端服务
```bash
cd iotlcm-admin-ui/vue
docker build -t your-registry/iotlcm/iotlcm-admin-ui:$VERSION -f build/Dockerfile .
```
```bash
cd iotlcm-app/app
docker build -t your-registry/iotlcm/iotlcm-app-web:$VERSION .
```
### 构建参数说明
| 参数 | 用途 |
| --- | --- |
| `BuildVersion` | 写入二进制,可通过版本接口查询 |
| `CommitID` | 提交号,便于追溯 |
| `BuildTime` | 构建时间 |
`openapi`、`worker-svr` 等仓库支持用 `GOPROXY` 构建参数替换代理:
```bash
docker build --build-arg GOPROXY=https://goproxy.cn,direct -t ... .
```
## 镜像命名规则
部署编排按以下名称拉取镜像,自建时需保持一致:
| 服务 | 镜像名 |
| --- | --- |
| admin-svr | `<REGISTRY>/iotlcm/iotlcm-admin-svr:<TAG>` |
| admin-ui | `<REGISTRY>/iotlcm/iotlcm-admin-ui:<TAG>` |
| openapi | `<REGISTRY>/iotlcm/iotlcm-openapi:<TAG>` |
| app-server | `<REGISTRY>/iotlcm/iotlcm-app-server:<TAG>` |
| app-web | `<REGISTRY>/iotlcm/iotlcm-app-web:<TAG>` |
| upstream-chan | `<REGISTRY>/iotlcm/iotlcm-upstream-chan:<TAG>` |
| upstream-proxy | `<REGISTRY>/iotlcm/iotlcm-upstream-proxy:<TAG>` |
| worker-svr | `<REGISTRY>/iotlcm/iotlcm-worker-svr:<TAG>` |
| sms | `<REGISTRY>/iotlcm/iotlcm-sms:<TAG>` |
| bot | `<REGISTRY>/iotlcm/iotlcm-bot:<TAG>` |
| docs | `<REGISTRY>/iotlcm/iotlcm-docs:<TAG>` |
| onelink-mock | `<REGISTRY>/iotlcm/iotlcm-onelink-mock:<TAG>` |
基础组件(`library/mysql`、`library/redis` 等)直接使用上游官方镜像,无需自建。
## 推送到自己的仓库
```bash
REGISTRY=harbor.internal.example.com
TAG=1.0.3
docker tag iotlcm-openapi:local $REGISTRY/iotlcm/iotlcm-openapi:$TAG
docker push $REGISTRY/iotlcm/iotlcm-openapi:$TAG
```
部署时 `.env` 改为:
```bash
REGISTRY=harbor.internal.example.com
TAG=1.0.3
```
## 批量构建脚本
```bash
#!/bin/bash
set -euo pipefail
REGISTRY=${REGISTRY:?请设置 REGISTRY}
TAG=${TAG:-$(date +%Y%m%d)}
WORKDIR=${WORKDIR:-./repos}
# 服务:仓库:上下文:产物镜像名
SERVICES=(
"admin-svr:iotlcm-admin-svr:server/:iotlcm-admin-svr"
"openapi:iotlcm-openapi:.:iotlcm-openapi"
"worker-svr:iotlcm-worker-svr:.:iotlcm-worker-svr"
"upstream-chan:iotlcm-upstream-chan:.:iotlcm-upstream-chan"
"upstream-proxy:iotlcm-upstream-proxy:.:iotlcm-upstream-proxy"
"app-server:iotlcm-app:server/:iotlcm-app-server"
"sms:iotlcm-sms:.:iotlcm-sms"
"bot:iotlcm-bot:.:iotlcm-bot"
)
mkdir -p "$WORKDIR"
for item in "${SERVICES[@]}"; do
IFS=':' read -r name repo ctx image <<< "$item"
echo "=== 构建 $name ==="
if [ ! -d "$WORKDIR/$repo" ]; then
git clone "https://git.5iot.com/iotlcm-public/$repo.git" "$WORKDIR/$repo"
fi
VERSION=$(git -C "$WORKDIR/$repo" describe --tags --always 2>/dev/null || echo unknown)
COMMIT=$(git -C "$WORKDIR/$repo" rev-parse --short HEAD 2>/dev/null || echo unknown)
find "$WORKDIR/$repo" -name Dockerfile -exec \
sed -i 's#FROM crpi-[^ ]*/\([a-z]*\):\([0-9.]*-alpine[0-9.]*\)#FROM \1:\2#' {} \;
docker build \
--build-arg BuildVersion="$VERSION" \
--build-arg CommitID="$COMMIT" \
--build-arg BuildTime="$(date -u '+%Y-%m-%dT%H:%M:%SZ')" \
-t "$REGISTRY/iotlcm/$image:$TAG" \
-f "$WORKDIR/$repo/${ctx}Dockerfile" \
"$WORKDIR/$repo/$ctx"
done
echo "全部完成,镜像标签:$REGISTRY/iotlcm/*:$TAG"
```
::: warning 脚本里的 sed 只能应急
上面的 `sed` 是粗略替换,把私有仓库地址映射为公共镜像名。仅适用于地址格式规整的情况,用前先 `git diff` 确认改对了。正式做法应把 Dockerfile 的修正提交回仓库。
:::
## 验证构建结果
```bash
# 镜像大小
docker images | grep iotlcm
# 二进制能否启动
docker run --rm your-registry/iotlcm/iotlcm-openapi:$TAG --help 2>&1 | head
# 检查架构
docker inspect your-registry/iotlcm/iotlcm-openapi:$TAG \
--format '{{.Architecture}}/{{.Os}}'
```
构建后按[启动与验证](install)的清单做一次完整部署验证,不要只看镜像是否构建成功。
## 常见问题
| 现象 | 原因 | 处理 |
| --- | --- | --- |
| `pull access denied` / `unauthorized` | Dockerfile 里是私有仓库地址 | 按上文替换 `FROM` |
| `go mod download` 超时 | 网络无法访问 Go 模块代理 | 加 `--build-arg GOPROXY=https://goproxy.cn,direct` |
| `npm ci` 失败 | 前端仓库 lock 文件与 package.json 不一致 | 改用 `npm install`,并注意提交 lock 变更 |
| 镜像体积过大 | 未使用多阶段构建,或缓存未清理 | 确认用的是仓库自带的 Dockerfile(均为多阶段) |
| 容器启动后立即退出 | 镜像内缺少配置文件 | 容器认为 `config/config.yaml` 必须存在,需挂载或用占位配置 |
| `exec format error` | 构建架构与运行架构不一致 | 加 `--platform linux/amd64` |