
本篇是实际项目中的信创迁移操作,基于信创ARM64服务器,K8s集群环境下的MinIO部署实战。没有套路,只有真实踩过的坑。
公司推进项目信创改造,要求所有中间件迁移到ARM64架构的K8s集群中。消息队列用的是QMQ,对象存储选了MinIO。
环境信息:
MinIO的部署看起来很简单——拉个镜像跑起来就行。但实际操作时,光是拿到一个可用的ARM64二进制文件,就折腾了大半天,尤其是在公司网络有限制的情况下。
MinIO官方下载地址是 https://dl.min.io/server/minio/release/linux-arm64/minio。
curl -L -C - --noproxy '*' --connect-timeout 30 -o minio \
https://dl.min.io/server/minio/release/linux-arm64/minio在公司内网络下速度大概25KB/s,105MB的文件,下了将近一个小时,到24MB断了。加了 --retry 10 自动重试,还是断断续续,最终没能下完。
curl -L -x http://代理ip:代理端口 --connect-timeout 30 \
-o minio https://dl.min.io/server/minio/release/linux-arm64/minio直接超时,公司代理对dl.min.io的HTTPS连接不稳定,SSL握手经常中断。
有人在上游维护了ARM64构建版本(golithus/minio-builds),试了公司代理、直连,全部失败。
也试了,无响应。
之前部署MariaDB时发现DaoCloud的Docker镜像源(docker.m.daocloud.io)在国内速度极快,秒拉。
docker pull docker.m.daocloud.io/minio/minio:latest很快就完成了镜像拉取,然后从镜像里把二进制抠出来:
docker create --name minio-extract docker.m.daocloud.io/minio/minio:latest
docker cp minio-extract:/usr/bin/minio ./minio
docker rm minio-extract验证一下:
$ ls -lh minio
-rwxr-xr-x 100M minio
$ file minio
ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV),
statically linked, stripped补充说明:2025年10月起,MinIO官方不再提供预编译二进制下载,dl.min.io上的旧版本链接也可能随时失效。通过Docker镜像提取是目前最可靠的方式。
项目里QMQ的Dockerfile长这样:
FROM nexus.xx.in:8082/baseimage/openjdk8:latest
WORKDIR /app
COPY app/ /app/
RUN ... mkdir -p /app/qmq/logs ...
CMD ["/app/start_metaserver.sh", "start"]照着写了一版MinIO的,基础镜像用了 eclipse-temurin:8-jre-jammy。构建完了才反应过来——MinIO是Go写的静态二进制,根本不需要JRE,白白多耗了几百M的空间。
FROM alpine:3.19
WORKDIR /app
COPY minio /app/minio
COPY docker-entrypoint.sh /app/docker-entrypoint.sh
RUN apk add --no-cache ca-certificates \
&& chmod +x /app/minio /app/docker-entrypoint.sh \
&& addgroup -S minio \
&& adduser -S -G minio minio \
&& mkdir -p /data \
&& chown -R minio:minio /data /app
USER minio
EXPOSE 9000 9500
ENTRYPOINT ["/app/docker-entrypoint.sh"]
CMD ["minio", "server", "/data", "--console-address", ":9500"]构建报错:apk add ca-certificates 失败。原因还是公司代理——Alpine的apk仓库走HTTPS,被SSL拦截了。
MinIO是静态链接,不需要动态库。ca-certificates只是用于HTTPS客户端验证,内网环境下HTTP访问完全够用。如果后续需要HTTPS,可以在K8s里挂载证书文件。
另外把minio二进制放到根目录,调整后的Dockerfile如下:
FROM alpine:3.19
WORKDIR /
LABEL maintainer="MinIO, Inc." \
description="MinIO high performance object storage"
COPY minio /minio
COPY docker-entrypoint.sh /docker-entrypoint.sh
RUN chmod +x /minio /docker-entrypoint.sh \
&& addgroup -S minio \
&& adduser -S -G minio minio \
&& mkdir -p /data \
&& chown -R minio:minio /data
USER minio
EXPOSE 9000 9500
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["/minio", "server", "/data", "--console-address", ":9500"]入口脚本:
#!/bin/sh
# 如果参数不是以 /minio 开头,自动补上
if [ "${1}" != "/minio" ]; then
if [ -n "${1}" ]; then
set -- /minio "$@"
fi
fi
exec "$@"构建验证:
$ docker build -t minio:latest .
$ docker images minio:latest
minio:latest 218MB
$ docker run --rm -d --name minio-test \
-p 19000:9000 -p 19500:9500 \
-e MINIO_ROOT_USER=minioadmin \
-e MINIO_ROOT_PASSWORD=minioadmin123 \
minio:latest
$ sleep 5 && curl -s http://localhost:19000/minio/health/live
# 空响应 = 200 OK
$ curl -s http://localhost:19500/ | head -3
<!doctype html>...MinIO Console...API和Console都正常。
最初用 volumeClaimTemplates 自动创建PVC,后来对齐了公司现有的部署规范,改用预创建PVC。
这个改动有个坑:StatefulSet的 volumeClaimTemplates 是不可变字段,改不了。必须先删掉旧StatefulSet再重新创建:
# 删StatefulSet(用--cascade=orphan保留Pod,可选)
kubectl -n goo delete statefulset minio --cascade=orphan
# 删旧PVC
kubectl -n goo delete pvc data-minio-0验证后,得到最终的YAML文件,完整内容如下所示:
apiVersion: v1
kind: Service
metadata:
name: minio
namespace: goo
labels:
app: minio
spec:
type: ClusterIP
ports:
- name: api
port: 9000
targetPort: 9000
- name: console
port: 9500
targetPort: 9500
selector:
app: minio
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: minio
namespace: goo
labels:
app: minio
spec:
serviceName: minio
replicas: 1
podManagementPolicy: OrderedReady
updateStrategy:
type: RollingUpdate
selector:
matchLabels:
app: minio
template:
metadata:
labels:
app: minio
spec:
containers:
- name: minio
image: minio:latest
imagePullPolicy: IfNotPresent
args:
- server
- /minio
- --console-address
- ":9500"
env:
- name: TZ
value: Asia/Shanghai
- name: MINIO_ROOT_USER
value: "minioadmin"
- name: MINIO_ROOT_PASSWORD
value: "minioadmin123"
ports:
- containerPort: 9000
- containerPort: 9500
readinessProbe:
httpGet:
path: /minio/health/live
port: 9000
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
livenessProbe:
httpGet:
path: /minio/health/live
port: 9000
initialDelaySeconds: 60
periodSeconds: 20
timeoutSeconds: 5
resources:
requests:
cpu: "500m"
memory: "1Gi"
limits:
cpu: "2"
memory: "2Gi"
volumeMounts:
- name: kmp-nas
mountPath: /minio
volumes:
- name: kmp-nas
persistentVolumeClaim:
claimName: kmp-nas-pvc这里面有三点需要特别注意,否则无法启动,或及时启动显示的bucket也不对:
1.数据目录和挂载路径必须一致
-- args里的 /minio 和 volumeMounts的 mountPath: /minio 要对上,否则MinIO写的数据不在PVC上,Pod重建就丢。
2.PVC要提前创建
-- 改用预创建PVC后,volumeClaimTemplates 删掉了,K8s不会自动创建PVC。
3.健康检查用MinIO原生端点
--/minio/health/live 是MinIO标准健康检查接口,不要用Spring Boot的 /actuator/health(MinIO没有这个端点)。
Dockerfile支持通过 ARG 指定基础镜像地址,方便对接公司内部镜像仓库:
ARG BASE_IMAGE=alpine:3.19
FROM ${BASE_IMAGE}流水线命令:
# 构建
docker build \
--build-arg BASE_IMAGE=nexus.xx.in:8082/library/alpine:3.19 \
-t nexus.xx.in:8082/library/minio:latest .
# 推送
docker push nexus.xx.in:8082/library/minio:latest
# 部署
kubectl apply -f k8s-minio.yaml前提是alpine基础镜像已经推到公司仓库。这是一次性操作:
docker pull docker.m.daocloud.io/library/alpine:3.19
docker tag docker.m.daocloud.io/library/alpine:3.19 \
nexus.xx.in:8082/library/alpine:3.19
docker push nexus.xx.in:8082/library/alpine:3.19部署完新MinIO后,如果需要从旧实例迁移数据,下面是实操方法。
MinIO官方客户端 mc 支持 mirror 命令,可以在两个MinIO实例之间实现数据同步:
# 安装mc(同样从Docker镜像提取)
docker create --name mc-extract docker.m.daocloud.io/minio/mc:latest
docker cp mc-extract:/usr/bin/mc ./mc
docker rm mc-extract
# 配置源和目标
./mc alias set old http://旧MinIO地址:9000 olduser oldpass
./mc alias set new http://新MinIO地址:9000 minioadmin minioadmin123
# 全量同步
./mc mirror --overwrite --watch old new--watch 参数会持续监听源端变化,适合不停机迁移。去掉 --watch 就是一次性全量同步。
如果两个MinIO版本一致,且能直接访问存储卷,最简单的方式是停掉旧实例,把数据目录整体拷贝过来:
# 停旧实例
kubectl -n goo scale statefulset minio-old --replicas=0
# 拷贝数据
kubectl -n goo cp minio-old-0:/minio/data-new/ ./minio-backup/
kubectl -n goo cp ./minio-backup/ minio-0:/minio/
# 启新实例
kubectl -n goo scale statefulset minio --replicas=11. 版本兼容性
MinIO的存储格式在不同大版本间可能不兼容。建议新旧实例使用相同版本。可以用 minio --version 确认:
$ docker run --rm minio:latest minio --version
minio version RELEASE.2025-09-07T16-13-09Z (commit-id=07c3a429)
Runtime: go1.24.6 linux/arm642. Bucket和Access Key需要手动迁移
mc mirror 只同步对象数据,不会迁移Bucket配置、访问策略、Access Key。迁移完后需要手动创建:
# 列出旧实例所有bucket
./mc ls old/
# 逐个创建并同步策略
./mc mb new/mybucket
./mc policy set download new/mybucket # 按需设置策略
# Access Key需要在Console中手动创建,或通过mc admin命令
./mc admin user add new newuser newpassword3. 确认数据完整性
# 对比两边文件数量
./mc ls --recursive old/ | wc -l
./mc ls --recursive new/ | wc -l
# 抽样校验MD5
./mc cat --json old/mybucket/file.json | jq .etag
./mc cat --json new/mybucket/file.json | jq .etag4. 不要同时写两端
迁移过程中,必须确保只有一个实例在接收写入,否则会导致最终数据不一致。正确的操作顺序是:旧实例停写 → 全量同步 → 切换流量到新实例 → 旧实例下线。
5. 大数据量迁移的带宽问题
如果数据量在TB级别,那么跨网络迁移会很慢。mc mirror 支持断点续传(--overwrite 会跳过已存在的且大小一致的对象),但如果网络不稳定,建议先导出到本地存储再导入,这样可靠性会提高很多:
# 导出到本地
./mc mirror old/ /local-backup/minio/
# 从本地导入
./mc mirror /local-backup/minio/ new/坑 | 原因 | 解决方案 |
|---|---|---|
dl.min.io下载断断续续 | 公司代理SSL拦截 + 国际网络不稳定 | 从DaoCloud Docker镜像提取二进制 |
apk add 失败 | Alpine仓库HTTPS被代理拦截 | 去掉ca-certificates,内网HTTP够用 |
Dockerfile用JRE基础镜像 | 照搬Java项目思路 | MinIO是Go静态二进制,用alpine即可 |
entrypoint报 minio: not found | 二进制不在PATH中 | 入口脚本中用绝对路径 /minio |
StatefulSet改volumeClaimTemplates报错 | 该字段不可变 | 先删除StatefulSet再重新创建 |
Console打开看不到菜单 | 未登录 | 用root账号登录后菜单完整显示 |
Readiness探针一直失败 | 用了 /actuator/health(Spring端点) | MinIO原生端点是 /minio/health/live |
minio-pipeline/ ├── Dockerfile # 构建文件 ├── docker-entrypoint.sh # 入口脚本 ├── minio # ARM64二进制(100MB) ├── k8s-minio.yaml # K8s部署文件 └── build.sh # 可选构建脚本
镜像信息:
信创环境下部署开源组件,最大的障碍往往不是技术本身,而是网络限制。Docker Hub被阻断、GitHub不稳定、各种CDN被代理拦截——这些问题在x86环境用国内镜像源很容易解决,但到了ARM64信创环境,由于很多镜像源没有对应的ARM64版本,只能自己折腾,通过源码编译、翻箱倒柜寻找镜像的方式解决。
整个过程的核心经验就一条:善用Docker镜像提取二进制。DaoCloud(docker.m.daocloud.io)在国内的加速效果很好,几乎所有官方镜像都能秒拉。拉下来之后 docker create + docker cp 就能把需要的文件抠出来,比直接下载二进制靠谱得多。
希望这篇文章能帮到同样在信创环境踩坑的同学。