Files
iotlcm/docs/deploy/build-image.md
T
MiFaZhan 73adad97f3 docs: 部署手册适配一键部署,去掉环境变量与示例配置
部署仓已改为开箱即用:config/ 直接是配置文件、口令等值内置在
docker-compose.yml、不再有 .env。

同步调整:
- 概览:部署流程改为 git clone + docker compose up -d 三步
- 配置说明:重写为「编排层 + 应用层」,说明为何不用环境变量
  (GoFrame 不展开配置文件中的 ${VAR})、改口令需同步三处、
  口令不能含 @ : / 等字符
- 启动与验证:去掉准备步骤,schema 脚本数改为 13 个
- 环境准备:镜像仓库改为描述编排文件中的实际地址
- 升级备份/离线/分离/自建镜像:.env 相关操作改为修改编排文件
2026-10-29 09:20:09 +08:00

9.8 KiB
Raw Blame History

title, description, outline
title description outline
自建镜像 从源码构建各服务的 Docker 镜像 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 改为公共镜像:

# 修改前(私有仓库,外部不可达)
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
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 基础镜像均为公共镜像,可直接构建。 :::

构建命令

后端服务

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 需指定上下文目录:

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/

前端服务

cd iotlcm-admin-ui/vue
docker build -t your-registry/iotlcm/iotlcm-admin-ui:$VERSION -f build/Dockerfile .
cd iotlcm-app/app
docker build -t your-registry/iotlcm/iotlcm-app-web:$VERSION .

构建参数说明

参数 用途
BuildVersion 写入二进制,可通过版本接口查询
CommitID 提交号,便于追溯
BuildTime 构建时间

openapi、worker-svr 等仓库支持用 GOPROXY 构建参数替换代理:

docker build --build-arg GOPROXY=https://goproxy.cn,direct -t ... .

镜像命名规则

部署编排按以下名称拉取镜像,自建时需保持一致。<镜像仓库> 即官方仓库前缀 crpi-qflrqrxoohllkvgc.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 会随补丁升级而漂)。部署前先把五个镜像同步到官方仓库:

REPO=crpi-qflrqrxoohllkvgc.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,保证多台机器拉到的镜像完全一致。 :::

同步完成后校验:

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 里的镜像前缀改指向它:

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
# docker-compose.yml —— 逐个替换 image 前缀
image: 'harbor.internal.example.com/iotlcm/iotlcm-openapi:1.0.3'

::: tip 只覆盖部分服务 只想替换其中一两个服务时,把对应镜像推到自有仓库,其余保留官方地址。此时把 docker-compose.yml 里那几个 image 单独写成完整地址,其余保持官方仓库前缀即可。 :::

批量构建脚本

#!/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 的修正提交回仓库。 :::

验证构建结果

# 镜像大小
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}}'

构建后按启动与验证的清单做一次完整部署验证,不要只看镜像是否构建成功。

常见问题

现象 原因 处理
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