麒麟V10-SP1离线部署Docker与Milvus向量数据库全流程
2026/9/16 0:21:55 网站建设 项目流程

最近在忙信创迁移,核心任务是把原本跑在CentOS 7上的应用服务,整体搬到银河麒麟高级服务器操作系统V10-SP1上。第一个绕不开的坎,就是Docker的离线部署。生产网段不能连外网,既不能用yum在线装,也不能docker pull拉镜像,所有东西都得在隔离环境里手工搬进去。这次要部署的组件里,最关键的是Milvus向量数据库——它承担着知识库语义检索和向量召回的功能,能不能稳定跑起来直接决定业务是否可用。我把整个部署过程记录下来,从离线物料准备、Docker安装、镜像导入,到Milvus单机版编排,再到SP1上踩过的坑,全都摊开讲一遍。

1. 信创迁移的整体设计与方案选型

1.1 为什么是这个组合:麒麟V10-SP1 + Milvus

银河麒麟V10-SP1是信创环境里最常见的服务器操作系统之一,x86_64架构支持很成熟,跑主流Docker版本没有问题。Milvus则是当前使用最广的开源向量数据库,专门处理海量向量的相似度检索。大模型知识库这类场景,先把文档切片做embedding,再把向量灌进Milvus,查询时做top-K召回,比用传统数据库硬扛like查询快几个量级。

信创环境下应用要真正落地,光有操作系统还不够,中间件和数据库这一层必须能离线装、能稳定跑。所以选型就定成:麒麟V10-SP1做底座,Docker做运行时,Milvus提供向量检索能力。这套组合的好处是,后面再迁移其他业务时,Docker环境是通用的,可以把更多组件逐步搬过来,不用每个组件都重新解决运行环境问题。

有人会问,Milvus不是可以在宿主机上直接跑二进制吗?理论上可以,但Milvus依赖etcd、MinIO等一堆组件,版本兼容、动态库、配置文件散落各处,排查起来非常痛苦。用Docker做封装以后,业务无关的运行细节都被收敛到镜像里,迁移的边界就清晰了。

1.2 为什么坚持Docker离线部署,而不是源码编译

我在做方案时也认真想过,要不要直接在麒麟SP1上源码编译Milvus。结论是:离线环境下源码编译的不可控因素太多。Milvus的主程序、etcd、MinIO、底层依赖库,每一层编译都可能因为缺少某个开发包卡住,而且信创机器很难临时装这些编译依赖。就算编译成功,版本和现有业务验证过的版本有偏差,风险也不小。

Docker离线部署的核心思路是:提前在一台有网的、与目标机器同架构的机器上,把安装包和镜像全部准备好,打包带到目标环境导入。这个方式的优点很直接:环境一致性有保障,镜像里把运行时、动态库、配置都固化好了;回滚也简单,删掉容器换一个镜像就能回到之前版本;后续批量分发到多台服务器时,复制同样的tar包就能完成。

当然,Docker本身也需要一些系统软件包,这部分要在物料准备阶段提前下载rpm包。不能完全无脑,但工作量比源码编译少太多,尤其是面对Milvus这种组件较多的项目时,省下的时间是很可观的。

1.3 离线部署的前置准备清单

先把我这次用到的物料清单列出来,方便你照着准备:

  • 一台已经装好银河麒麟V10-SP1的目标服务器,x86_64架构,建议至少4核8G内存,磁盘预留50GB以上。
  • Docker相关rpm包:docker-ce、docker-ce-cli、containerd.io、docker-compose-plugin,版本我用的20.10.x与1.6.x组合。
  • Milvus镜像与依赖镜像tar包:milvusdb/milvus:v2.6.8quay.io/coreos/etcd:v3.5.5minio/minio:RELEASE.2023-03-20T20-16-18Z
  • docker compose独立二进制,如果rpm包里的plugin没能生效时备用。
  • 一个容量足够的U盘或移动硬盘,建议exFAT格式,因为镜像tar包动辄几个GB,FAT32单文件4GB限制会卡住。
  • 可选的pymilvus离线whl包,方便在目标机器上直接做连通性测试。

这些物料最好集中放在一个目录里,比如/data/offline/,方便后续校验和管理。

2. 离线物料准备工作

2.1 在联网机器上准备Docker安装包

准备物料需要一台联网的机器,架构一定要和目标服务器一致,我这里都是x86_64。用yumdownloader可以一次性把rpm包和依赖拉下来:

yum install -y yum-utils yumdownloader --resolve docker-ce-20.10.24 docker-ce-cli-20.10.24 containerd.io-1.6.28 docker-compose-plugin-2.21.0

如果没有现成的Docker仓库,先配置好官方yum源再执行。下载完成后,目录里会出现docker-ce、docker-ce-cli、containerd.io、docker-compose-plugin等rpm文件,大概率还有libseccomp这类基础依赖。把它们全部带上,尤其是libseccomp,麒麟SP1上如果版本太低,容器启动时会报seccomp相关的权限错误。这里有一个容易踩的细节:rpm包不要光拿主包,--resolve参数能自动把依赖拉全,少了它后面在离线机上装到一半才发现缺包就很被动。

2.2 Milvus 2.6.8依赖镜像的下载与导出

还是在这台联网机器上,先确保Docker能用,然后拉取Milvus单机版需要的三个镜像:

docker pull milvusdb/milvus:v2.6.8 docker pull quay.io/coreos/etcd:v3.5.5 docker pull minio/minio:RELEASE.2023-03-20T20-16-18Z

拉完后检查一下:

docker images

确认镜像完整存在后,用docker save导出。我习惯合并成一个tar包,导入时一次搞定:

docker save milvusdb/milvus:v2.6.8 quay.io/coreos/etcd:v3.5.5 minio/minio:RELEASE.2023-03-20T20-16-18Z -o milvus-all-images.tar

如果你后面需要在多台机器上分别部署,也可以拆开save,方便按组件分发。但拆开保存会多占用一些磁盘空间,因为etcd和minio的基础层在多个tar里会有重复。我建议在一台机器上部署时用合并包,在多台机器间分发时分开。

2.3 物料转移与文件校验

物料准备好以后,把rpm目录和镜像tar包复制到U盘或者移动硬盘,再拷到麒麟SP1服务器上。大文件传输过程中可能出现损坏,强烈建议做MD5校验。先在联网机器上生成校验值:

md5sum milvus-all-images.tar docker-ce-*.rpm > checksums.txt

到目标机器上执行:

md5sum -c checksums.txt

只要输出全部是OK,再继续下一步。这一步看起来多花几分钟,但能避免装到一半发现镜像加载失败,找回U盘重新拷贝的尴尬。我自己是吃过亏的,所以现在无论多急都会先校验。

3. 银河麒麟V10-SP1上离线安装Docker

3.1 系统检查:内核、磁盘、防火墙一个都不能少

登录目标服务器,第一件事是确认系统版本和内核:

cat /etc/os-release uname -r

正常会看到银河麒麟V10-SP1的标识,内核一般4.19。接着看磁盘规划和内存:

df -h free -h

Milvus跑起来以后,etcd、MinIO、Milvus三块数据都在增长,尤其MinIO里存的是向量索引文件,数据量大了很占空间。如果系统盘空间紧,建议单独挂载一块数据盘,后面Docker的data-root就指到数据盘去。

再查防火墙和SELinux状态:

systemctl status firewalld getenforce

我的建议是:测试环境直接关掉firewalld,省得端口放行的问题反复折腾;生产环境如果必须开防火墙,就把Milvus用到的端口放行。常用端口有19530(Milvus客户端)、9091(Prometheus/健康检查)、9000与9001(MinIO API与控制台)、2379(etcd)。SELinux如果处于enforcing,先临时setenforce 0验证一下业务是否正常,确认是SELinux拦截后,再决定调整文件上下文还是保持关闭。不要一上来就永久关闭,有些信创环境验收会检查SELinux状态。

3.2 Docker离线rpm安装与daemon.json配置

把rpm包放到目标机器目录,比如/opt/docker-rpm,然后执行:

cd /opt/docker-rpm yum install -y ./docker-ce-*.rpm

这里建议用yum install而不是rpm -ivh,因为yum会自动识别当前目录下所有的rpm包并处理依赖关系。如果提示缺依赖,就把缺的rpm也放到同目录,继续执行yum install即可。

安装完成后,先不急着启动,配置daemon.json。我一般这样写:

mkdir -p /etc/docker cat > /etc/docker/daemon.json <<EOF { "data-root": "/data/docker", "log-driver": "json-file", "log-opts": { "max-size": "50m", "max-file": "5" }, "storage-driver": "overlay2" } EOF

解释一下这几个配置:>systemctl enable --now docker systemctl status docker docker info docker version

重点关注docker info里的几个信息:Storage Driver是否为overlay2,Cgroup版本,以及是否报WARNING。如果看到overlay2说明存储驱动正常。

再检查docker compose:

docker compose version

如果命令不存在,有可能是rpm包不完整或者plugin没生效。这时候可以用独立二进制兜底:在联网机器上下载docker-compose-linux-x86_64,拷到目标机器后放到/usr/local/bin/docker-compose,加执行权限:

chmod +x /usr/local/bin/docker-compose docker-compose version

Docker环境稳定以后,再继续导入Milvus镜像。

4. Milvus镜像导入与单机版编排

4.1 镜像导入、打标签与一致性确认

导入镜像:

docker load -i /data/offline/milvus-all-images.tar

导入完成后立即确认:

docker images

如果save时候是完整名称,load后也会保留完整名称。看到三个镜像都在列表里,标签和拉取时一致,就不用额外docker tag了。有一个小坑:如果你save的时候只写了短名称,load出来的镜像repository和tag可能不完整,这时候compose文件里引用的image名字就要跟着调整,否则docker compose拉镜像会去远程仓库,然后因为没网直接失败。我的建议是,compose文件写好之后,一定要检查每个image:字段都对应docker images里的实际名称。

4.2 手写docker-compose.yml并逐项解读

创建部署目录:

mkdir -p /opt/milvus cd /opt/milvus

然后创建docker-compose.yml。我这次用的2.6.8单机版,完整配置如下:

version: "3.5" services: etcd: image: quay.io/coreos/etcd:v3.5.5 environment: - ETCD_AUTO_COMPACTION_MODE=revision - ETCD_AUTO_COMPACTION_RETENTION=1000 - ETCD_QUOTA_BACKEND_BYTES=4294967296 - ETCD_SNAPSHOT_COUNT=50000 volumes: - ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/etcd:/etcd command: etcd -advertise-client-urls=http://etcd:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd healthcheck: test: ["CMD", "etcdctl", "endpoint", "health"] interval: 30s timeout: 20s retries: 3 restart: always minio: image: minio/minio:RELEASE.2023-03-20T20-16-18Z environment: MINIO_ACCESS_KEY: minioadmin MINIO_SECRET_KEY: minioadmin volumes: - ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/minio:/minio_data command: minio server /minio_data --console-address ":9001" healthcheck: test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"] interval: 30s timeout: 20s retries: 3 restart: always standalone: image: milvusdb/milvus:v2.6.8 command: ["milvus", "run", "standalone"] security_opt: - seccomp:unconfined environment: ETCD_ENDPOINTS: etcd:2379 MINIO_ADDRESS: minio:9000 volumes: - ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/milvus:/var/lib/milvus healthcheck: test: ["CMD", "curl", "-f", "http://localhost:9091/healthz"] interval: 30s start_period: 90s timeout: 20s retries: 3 ports: - "19530:19530" - "9091:9091" depends_on: - "etcd" - "minio" restart: always

简单解读一下三个服务的职责。etcd是Milvus的元数据存储,collection、partition、segment等元信息都放这里。环境变量里的ETCD_AUTO_COMPACTION_MODE=revisionETCD_AUTO_COMPACTION_RETENTION=1000用来定期压缩历史版本,避免etcd空间被无限增长的事务记录撑爆。ETCD_QUOTA_BACKEND_BYTES设置了后端存储告警阈值,默认4GB,单机场景够用。

MinIO是外部对象存储,这是Milvus 2.x的常见部署模式,Milvus不再使用内置对象存储,而是把向量数据文件和索引文件写到MinIO。这样数据与计算分离,后面如果要扩节点或者迁移数据,对象存储是可以独立管理的。MINIO_ACCESS_KEYMINIO_SECRET_KEY我暂时用默认值,生产环境一定要改,而且建议通过环境变量文件传入,别直接硬编码在compose里。

standalone服务是Milvus的单机模式,内部自带proxy、datanode、indexnode、querynode等组件。ETCD_ENDPOINTS指向compose网络内的etcd服务名,MINIO_ADDRESS指向minio:9000。容器内部通过服务名互相访问,走的都是compose默认的bridge网络。depends_on只保证启动顺序,不保证服务健康,所以etcd和minio的健康检查配置很有必要。

4.3 一次性启动三件套并验证服务

启动服务:

cd /opt/milvus docker compose up -d

然后观察状态:

docker compose ps

一开始容器可能处于health: starting状态,属正常。等待一两分钟后再看,三个服务应该都能变成healthy或者running。如果standalone一直重启,先看日志:

docker compose logs -f standalone docker compose logs etcd docker compose logs minio

看到Milvus日志出现类似proxy startedquery node started等关键字,就说明核心组件都起来了。更严谨的验证是用pymilvus连一下:

pip install pymilvus==2.6.8 python3

进入Python交互环境:

from pymilvus import connections connections.connect(host="127.0.0.1", port="19530") print("connected")

能打印connected,说明客户端和服务端链路正常。如果你还想进一步验证插入和检索,可以创建一个简单collection,插入几条随机向量,再执行search。这一步能确认存储链路是否完整,因为有些环境端口通了,但写入MinIO或etcd时会有权限问题,只有真实写入才能发现。

5. SP1专属避坑指南与实操心得

5.1 SP1高频报错速查表

我把这次部署中遇到的和身边同事常踩的问题整理成了一张表,遇到问题可以先对照查找:

报错或异常常见原因处理方式
overlay/overlay: unsupported内核未加载overlay模块执行modprobe overlay,并写入/etc/modules-load.d/
error creating overlay mount数据分区不支持d_type使用ext4格式化数据盘,或将Docker数据目录改到ext4分区
iptables failed: iptables --wait -t natDocker与nftables/iptables不兼容安装iptables-nft,停止firewalld,必要时重启dockerd
容器内时间差8小时容器默认UTC时区compose中增加TZ: Asia/Shanghai环境变量
Permission denied等挂载报错SELinux拦截或目录权限不对临时setenforce 0定位,调整目录context或授予权限
Milvus容器不断重启etcd或MinIO服务未就绪查看etcd/minio日志,确认容器间DNS解析是否正常
disk space exhausted日志或数据占满磁盘检查daemon.json日志限制,确认数据盘大小
docker-compose: command not foundcompose plugin未安装或不生效确认docker-compose-plugin rpm,或使用独立二进制

5.2 内核模块、存储驱动与iptables处理

SP1和CentOS/RHEL系类似,但有个很典型的问题:部分内核模块默认没有加载,特别是br_netfilter。没有它,容器跨主机通信、端口映射都容易出问题。安装Docker后先执行:

modprobe br_netfilter modprobe overlay echo "br_netfilter" > /etc/modules-load.d/docker.conf

同时调整内核参数,让IPv4转发和iptables桥接生效:

cat > /etc/sysctl.d/docker.conf <<EOF net.ipv4.ip_forward = 1 net.bridge.bridge-nf-call-iptables = 1 EOF sysctl --system

另一个高频问题是存储驱动overlay2。SP1上如果Docker数据目录所在分区是xfs且没有开启ftype=1,创建overlay2挂载点时会报错。docker info能看到存储驱动,但真正报错时在容器启动阶段。处理办法是:数据盘格式化时用mkfs.ext4,或者确保xfs格式化带有-n ftype=1参数。如果现有分区不愿意重格,可以临时把storage-driver改成vfs,但vfs性能很差,只适合应急验证,不建议长期跑。

iptables问题也很折磨人。麒麟SP1在部分版本里默认用的是nftables,而Docker希望操作iptables转发链。如果启动容器时看到Failed to program NAT chain之类的错误,先停止firewalld:

systemctl disable --now firewalld

如果还不行,安装iptables-nft或iptables-services套件,再重启dockerd。这个问题的根源是iptables与nftables语法不兼容,Docker写入NAT链失败,容器端口映射就会异常。

5.3 时间、数据目录与安全策略调整

容器和宿主机时间差8小时,几乎每次部署都会遇到。compose里对每个服务增加TZ: Asia/Shanghai环境变量,比如etcd的environment列表里加上一行,minio和standalone同理。也可以统一挂载宿主机时间配置:

volumes: - /etc/localtime:/etc/localtime:ro

两种方式我都在生产用过,效果相同。时间不一致会导致日志时间错乱,虽然不影响Milvus主流程,但排查问题的时候非常误导人。

数据持久化目录方面,我用的${DOCKER_VOLUME_DIRECTORY:-.}/volumes这种写法,意思是默认在compose文件同级目录生成volumes文件夹,也可以通过环境变量DOCKER_VOLUME_DIRECTORY整体改到其他位置。比如想放到数据盘:

export DOCKER_VOLUME_DIRECTORY=/data/milvus docker compose up -d

这样所有中间数据都落在/data/milvus/volumes下。后面要备份,直接打包这个目录即可。注意改目录后要确保目录属主和权限正确,否则容器以内部用户写文件时可能报Permission denied。

SELinux在SP1上默认可能是enforcing,容器挂载宿主目录时经常被拦截。先用setenforce 0验证一下,如果确实是SELinux导致的问题,可以在不关闭SELinux的前提下调整挂载目录的上下文:

chcon -Rt svirt_sandbox_file_t /data/milvus/volumes /data/docker

如果项目验收没有强制要求,长期跑测试环境也可以直接关闭SELinux,修改/etc/selinux/config后重启生效。我个人的倾向是:生产环境尽量保留SELinux并正确设置上下文,测试环境关闭来省时间。

5.4 我的SP1实操心得

这次做完以后,有几个比较深的体会。

第一个,物料准备阶段无论多着急,都一定要在联网机器上把rpm依赖和镜像全部核对一遍。离线环境最大的成本就是“返工”,漏一个依赖,就可能要用U盘再跑一趟机房。rpm依赖问题可以通过yumdownloader --resolve解决,镜像可以通过docker images核对,tar包用md5校验,这些步骤看起来琐碎,但每一道都是在给后面省时间。

第二个,Milvus单机版的资源占用比想象中要高。etcd、MinIO、Milvus三个容器同时跑,内存最少给到8GB,磁盘上MinIO的索引文件增长非常快。我在测试机上一开始只分了4GB内存,结果Milvus起来后频繁因为内存不足触发OOM,容器一直重启。后来加内存加swap,才稳定下来。建议正式使用前用真实数据量压测一轮,确认资源水位。

第三个,验证不能只看docker compose ps。容器是running状态不代表Milvus的链路是通的,etcd可能连不上,MinIO的写入可能失败,这些在业务查询时才会暴露。所以我在部署完成后,一定会用pymilvus创建一个临时collection,插入几条向量再search一次,把写入、索引、查询全链路跑通才算结束。

第四个,后续如果要在SP1上继续扩展其他组件,这套离线Docker环境可以直接复用。比如离线部署redis、mysql、superset,或者跑一个本地大模型推理服务,都可以用同样的方式提前制备rpm和镜像。事实上我下一阶段计划把内网镜像仓库和监控组件也纳入这套信创离线体系里,让新机器上线时不再依赖U盘逐个拷包。

信创迁移这件事,看起来是操作系统切换,真正花时间的其实还是应用中间层的适配和交付。Docker这套离线部署思路在很多SP1项目里都能复用,先把底座搭稳,后面再迁什么都顺。希望这篇笔记能让你少走几步弯路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询