Files
iotlcm/docs/deploy/build-image.md
T

284 lines
9.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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 ... .
```
## 镜像命名规则
部署编排按以下名称拉取镜像,自建时需保持一致。`<镜像仓库>` 即官方仓库前缀 `crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com`:
| 服务 | 镜像名 |
| --- | --- |
| admin-svr | `<镜像仓库>/iotlcm/iotlcm-admin-svr:<TAG>` |
| admin-ui | `<镜像仓库>/iotlcm/iotlcm-admin-ui:<TAG>` |
| openapi | `<镜像仓库>/iotlcm/iotlcm-openapi:<TAG>` |
| app-server | `<镜像仓库>/iotlcm/iotlcm-app-server:<TAG>` |
| app-web | `<镜像仓库>/iotlcm/iotlcm-app-web:<TAG>` |
| upstream-chan | `<镜像仓库>/iotlcm/iotlcm-upstream-chan:<TAG>` |
| upstream-proxy | `<镜像仓库>/iotlcm/iotlcm-upstream-proxy:<TAG>` |
| worker-svr | `<镜像仓库>/iotlcm/iotlcm-worker-svr:<TAG>` |
| sms | `<镜像仓库>/iotlcm/iotlcm-sms:<TAG>` |
| bot | `<镜像仓库>/iotlcm/iotlcm-bot:<TAG>` |
| docs | `<镜像仓库>/iotlcm/iotlcm-docs:<TAG>` |
| onelink-mock | `<镜像仓库>/iotlcm/iotlcm-onelink-mock:<TAG>` |
基础组件(`mysql`、`redis`、`rabbitmq`、`consul`、`nginx`)不需要构建,只需从上游同步到 `<镜像仓库>/library/`,见下节。
## 同步基础组件
平台编排不再从 Docker Hub 直连基础组件(公网不稳定,且 `8.0` 这类 tag 会随补丁升级而漂)。部署前先把五个镜像同步到官方仓库:
```bash
REPO=crpi-qflrqrxoohlkkvgc.cn-chengdu.personal.cr.aliyuncs.com
docker login --username=sdqmx $REPO
# 按 docker-compose.yml 中固定的版本拉取并推送
sync_image() {
local src=$1 dst=$2
docker pull "$src"
docker tag "$src" "$REPO/library/$dst"
docker push "$REPO/library/$dst"
}
sync_image mysql:8.0 mysql:8.0
sync_image redis:7.2 redis:7.2
sync_image rabbitmq:4.3.5-management rabbitmq:4.3.5-management
sync_image hashicorp/consul:1.22 consul:1.22
sync_image nginx:1.30 nginx:1.30
```
::: warning 同步时把版本号固定到补丁位
`mysql:8.0` 这类浮动 tag 会随上游发新补丁而变化。建议同步时锁定到具体补丁版(如 `mysql:8.0.43`),并把 `docker-compose.yml` 的 `MYSQL_VERSION` 同步改为 `8.0.43`,保证多台机器拉到的镜像完全一致。
:::
同步完成后校验:
```bash
for img in mysql:8.0 redis:7.2 rabbitmq:4.3.5-management consul:1.22 nginx:1.30; do
docker manifest inspect "$REPO/library/$img" >/dev/null 2>&1 \
&& echo "✅ $img" || echo "❌ $img"
done
```
## 推送到自己的仓库
自建镜像不需要推送回官方仓库。推到自有仓库后,把 `docker-compose.yml` 里的镜像前缀改指向它:
```bash
REG=harbor.internal.example.com
TAG=1.0.3
docker tag iotlcm-openapi:local $REG/iotlcm/iotlcm-openapi:$TAG
docker login $REG
docker push $REG/iotlcm/iotlcm-openapi:$TAG
```
```yaml
# docker-compose.yml —— 逐个替换 image 前缀
image: 'harbor.internal.example.com/iotlcm/iotlcm-openapi:1.0.3'
```
::: tip 只覆盖部分服务
只想替换其中一两个服务时,把对应镜像推到自有仓库,其余保留官方地址。此时把 `docker-compose.yml` 里那几个 `image` 单独写成完整地址,其余保持官方仓库前缀即可。
:::
## 批量构建脚本
```bash
#!/bin/bash
set -euo pipefail
REPO=${REPO:?请设置 REPO}
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 "$REPO/iotlcm/$image:$TAG" \
-f "$WORKDIR/$repo/${ctx}Dockerfile" \
"$WORKDIR/$repo/$ctx"
done
echo "全部完成,镜像标签:$REPO/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` |