b4944d7c21
官方仓库把全部镜像放在同一个镜像仓库 iotlcm/iotlcm 下,靠 tag 区分服务: .../iotlcm/iotlcm:mysql-8.0 .../iotlcm/iotlcm:iotlcm-admin-svr-v1.0.4 .../iotlcm/iotlcm:iotlcm-app-server-v1.0.2 改动页面: - prerequisites:镜像仓库章节重写,补充命名空间/镜像仓库/tag 三层结构说明, 不再提 library/ 命名空间 - configuration:镜像地址示例与 tag 规则表 - build-image:构建命令、命名规则表、基础组件同步脚本、验证命令同步改 - offline:离线镜像清单 - split:分离部署示例 已构建通过,6 个相关页面均可访问。
293 lines
10 KiB
Markdown
293 lines
10 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: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:iotlcm-admin-svr-$VERSION \
|
|
-f server/build/Dockerfile server/
|
|
```
|
|
|
|
### 前端服务
|
|
|
|
```bash
|
|
cd iotlcm-admin-ui/vue
|
|
docker build -t your-registry/iotlcm/iotlcm:iotlcm-admin-ui-$VERSION -f build/Dockerfile .
|
|
```
|
|
|
|
```bash
|
|
cd iotlcm-app/app
|
|
docker build -t your-registry/iotlcm/iotlcm:iotlcm-app-web-$VERSION .
|
|
```
|
|
|
|
### 构建参数说明
|
|
|
|
| 参数 | 用途 |
|
|
| --- | --- |
|
|
| `BuildVersion` | 写入二进制,可通过版本接口查询 |
|
|
| `CommitID` | 提交号,便于追溯 |
|
|
| `BuildTime` | 构建时间 |
|
|
|
|
`openapi`、`worker-svr` 等仓库支持用 `GOPROXY` 构建参数替换代理:
|
|
|
|
```bash
|
|
docker build --build-arg GOPROXY=https://goproxy.cn,direct -t ... .
|
|
```
|
|
|
|
## 镜像命名规则
|
|
|
|
官方仓库把**所有镜像放在同一个镜像仓库** `iotlcm/iotlcm` 下,靠 tag 区分服务与版本:
|
|
|
|
```text
|
|
<Registry>/iotlcm/iotlcm:<服务名>-<版本>
|
|
```
|
|
|
|
| 服务 | tag | 完整示例 |
|
|
| --- | --- | --- |
|
|
| admin-svr | `iotlcm-admin-svr-<TAG>` | `.../iotlcm/iotlcm:iotlcm-admin-svr-v1.0.4` |
|
|
| admin-ui | `iotlcm-admin-ui-<TAG>` | `.../iotlcm/iotlcm:iotlcm-admin-ui-v1.0.4` |
|
|
| openapi | `iotlcm-openapi-<TAG>` | `.../iotlcm/iotlcm:iotlcm-openapi-v1.0.4` |
|
|
| app-server | `iotlcm-app-server-<TAG>` | `.../iotlcm/iotlcm:iotlcm-app-server-v1.0.2` |
|
|
| app-web | `iotlcm-app-web-<TAG>` | `.../iotlcm/iotlcm:iotlcm-app-web-v1.0.2` |
|
|
| upstream-chan | `iotlcm-upstream-chan-<TAG>` | `.../iotlcm/iotlcm:iotlcm-upstream-chan-v1.0.4` |
|
|
| upstream-proxy | `iotlcm-upstream-proxy-<TAG>` | `.../iotlcm/iotlcm:iotlcm-upstream-proxy-v1.0.4` |
|
|
| worker-svr | `iotlcm-worker-svr-<TAG>` | `.../iotlcm/iotlcm:iotlcm-worker-svr-v1.0.4` |
|
|
| sms | `iotlcm-sms-<TAG>` | `.../iotlcm/iotlcm:iotlcm-sms-v1.0.4` |
|
|
| bot | `iotlcm-bot-<TAG>` | `.../iotlcm/iotlcm:iotlcm-bot-v1.0.4` |
|
|
| docs | `iotlcm-docs-<TAG>` | `.../iotlcm/iotlcm:iotlcm-docs-v1.0.4` |
|
|
| onelink-mock | `iotlcm-onelink-mock-<TAG>` | `.../iotlcm/iotlcm:iotlcm-onelink-mock-v1.0.4` |
|
|
|
|
基础组件(`mysql`、`redis`、`rabbitmq`、`consul`、`nginx`)不需要构建,从上游同步到同一仓库即可,tag 为 `<组件>-<版本>`,见下节。
|
|
|
|
::: warning tag 必须带上服务名
|
|
同一个仓库里 tag 不能重复。只用版本号(如 `v1.0.4`)会让 12 个服务互相覆盖,因此 tag 固定为 `<服务名>-<版本>`。
|
|
:::
|
|
|
|
## 同步基础组件
|
|
|
|
平台编排不再从 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/iotlcm/iotlcm:$dst"
|
|
docker push "$REPO/iotlcm/iotlcm:$dst"
|
|
}
|
|
|
|
# 参数:<上游镜像> <目标 tag(组件名-版本)>
|
|
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`),tag 写成 `mysql-8.0.43`,并把 `docker-compose.yml` 里对应的 `image` 同步改掉,保证多台机器拉到的镜像完全一致。
|
|
:::
|
|
|
|
同步完成后校验:
|
|
|
|
```bash
|
|
for tag in mysql-8.0 redis-7.2 rabbitmq-4.3.5-management consul-1.22 nginx-1.30; do
|
|
docker manifest inspect "$REPO/iotlcm/iotlcm:$tag" >/dev/null 2>&1 \
|
|
&& echo "✅ $tag" || echo "❌ $tag"
|
|
done
|
|
```
|
|
|
|
## 推送到自己的仓库
|
|
|
|
自建镜像不需要推送回官方仓库。推到自有仓库后,把 `docker-compose.yml` 里的镜像前缀改指向它:
|
|
|
|
```bash
|
|
REG=harbor.internal.example.com
|
|
TAG=1.0.3
|
|
|
|
docker tag iotlcm-openapi:local $REG/iotlcm/iotlcm:iotlcm-openapi-$TAG
|
|
docker login $REG
|
|
docker push $REG/iotlcm/iotlcm:iotlcm-openapi-$TAG
|
|
```
|
|
|
|
```yaml
|
|
# docker-compose.yml —— 逐个替换 image 前缀
|
|
image: 'harbor.internal.example.com/iotlcm/iotlcm:iotlcm-openapi-v1.0.4'
|
|
```
|
|
|
|
::: 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:iotlcm-openapi-$TAG --help 2>&1 | head
|
|
|
|
# 检查架构
|
|
docker inspect your-registry/iotlcm/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` |
|