ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

银河麒麟V10 aarch64 Docker安装与版本锁定实践

银河麒麟V10 aarch64 Docker安装与版本锁定实践

简介:面向在国产化服务器上部署容器环境的中高级运维人员,这份手册聚焦银河麒麟 V10 SP1 Server 缺少官方 docker server 软件包的实际困境,给出可落地的安装路径。内容围绕第三方阿里源与麒麟官方源的双源配置展开,逐步记录 docker-ce-cli 与 docker-ce 18.09.7 的安装、服务启动及版本验证,并特别说明新版 docker 因 container-selinux 依赖无法满足而安装失败的报错与取舍,便于读者少走弯路。资源包仅含 1 个 docx 文件,约 17KB,篇幅精炼,按目录分为配置 yum 源与通过 yum 安装 docker 两部分,适合边操作边对照。目前已有 7220 人学习下载,可作为国产化平台容器部署的排错参考与步骤清单。

1. 一套 aarch64 服务器上的 Docker 安装,卡住的往往不是命令

国产化替代的项目里,给银河麒麟 V10 SP1 Server 装 Docker,最容易出问题的地方几乎从来不在命令本身。yum install docker-ce这条路谁都认得,但真正让人卡上半天的是两个问题:系统官方源里根本没有 docker-ce;第三方源 aarch64 目录下最新版又依赖一个系统里装不上的 container-selinux。这份手册给出的解法是把版本固定在 18.09.7-3.el7,用两个源凑齐依赖关系,再把安装拆成 cli → ce 两步走。它适合手里有一台麒麟服务器、要在这上面跑容器但不想从源码编译的人,也适合要给一批机器做批量部署、需要一套能复现步骤的运维。

2. 配两个 yum 源:baseurl 里那个7和$basearch填错就全盘皆输

2.1 先接受「官方源里没有 docker-ce」这个事实

麒麟 V10 SP1 的包管理是 rpm + dnf,$releasever在这套系统上会解析成10,而 docker-ce 的仓库目录只按 CentOS 的7和8两套组织。这就意味着 baseurl 里必须手写死centos/7,一旦照抄网上那种$releasever的写法,dnf 会拼出.../linux/centos/10/aarch64/stable/这种路径,然后给你一个 404 或者Failed to download metadata for repo。这是第一个坑,也是最不值得踩的一个坑。

第二个事实是 aarch64 上的 docker-ce 版本比 x86_64 少得多。20.10 系列起,docker-ce对container-selinux >= 2:2.74是硬依赖,而这个包在麒麟的官方源里要么没有、要么版本停在更低的号上,dnf 解析依赖时就只能报「nothing provides」。理解了这两点,后面的所有配置就都是在给依赖关系做补全。

2.2 docker-ce.repo 的五个字段逐个拆开

仓库文件放在/etc/yum.repos.d/下,文件名随便起但建议带.repo后缀,内容按下面写。镜像站的主机名我留了占位符,换成你能连通的国内公开镜像站即可,注意路径部分别改:

# /etc/yum.repos.d/docker-ce.repo [docker-ce-stable] name=Docker CE Stable - 7 # 关键是 centos/7 这一段:不能用 $releasever,否则会解析成 10 # $basearch 在 aarch64 机器上会自动展开成 aarch64,不用手写 baseurl=https://<mirror-host>/docker-ce/linux/centos/7/$basearch/stable enabled=1 # gpgcheck 和 gpgkey 必须成对出现,只开 check 不给 key 会直接失败 gpgcheck=1 gpgkey=https://<mirror-host>/docker-ce/linux/centos/gpg

$basearch这层抽象值得单独说一句:它取的是 rpm 的架构宏,不是uname -m的原始输出,在部分 arm 机器上uname -m给的是aarch64而宏可能是aarch64,两者一致,但如果你在 x86 上做交叉验证,$basearch会变成x86_64,路径自然就不同了。想确认展开结果,用dnf repolist -v看实际请求的 URL 最稳。

stable目录里放的是 GA 版本,也就是正式发布版。同级的还有test和nightly,除非你在做特性验证,否则不要碰。另外还有一个细节:docker-ce.repo一旦启用,docker-ce、docker-ce-cli、containerd.io、docker-ce-rootless-extras这些包都会从这个源解析,如果镜像站只同步了部分包,就会出现「包找不到」的假象,用dnf --showduplicates list docker-ce-cli能立刻分辨是源没同步还是版本号写错。

2.3 麒麟官方源负责补全依赖关系

第三方源只提供 docker 自己的包,container-selinux、libcgroup、device-mapper-libs这些底层依赖仍然要从系统源拿。装过麒麟的系统,/etc/yum.repos.d/下通常已经有一份名字类似kylin_aarch64.repo的文件,先ls看一眼再动手,别急着新建把原来的覆盖掉。仓库段大概长这样:

# 若系统自带则直接沿用,不要重复新建同名文件 [ks10-os-media] name = Kylin Linux Advanced Server 10 - Os # 地址从系统自带的 repo 文件或安装 ISO 里的 media.repo 抄,别手敲 baseurl = <官方源地址>/v10/sp1-openstack-q/aarch64/os/ gpgcheck = 0 enabled = 1

gpgcheck = 0在这里是常见做法,原因是内网或离线源的签名链经常没同步完整,开了校验反而装不上。这是一个用安全性换可用性的取舍,心里要有数:能开校验就开,开不了就把源地址限制在受控网段内。

提示:两个源的enabled必须同时为 1。有人配完 docker 源就把麒麟源整个注释掉,结果container-selinux拉不到,报错信息还停留在 docker 包上,很容易误判成源地址写错了。

2.4 刷新缓存并验证源真的生效

配置写完不要直接装,先花三十秒验证,能省掉后面半小时的排查:

# 清掉旧缓存,避免旧元数据干扰解析 dnf clean all # 重新拉取所有启用仓库的元数据 dnf makecache # 看仓库列表,确认两个源都是 enabled 且 URL 展开正确 dnf repolist -v | grep -E 'docker-ce|kylin|Repo-baseurl' # 只列 docker-ce 的可用版本,倒序方便看最新的几个 dnf --showduplicates list docker-ce 2>/dev/null | sort -r | head -20

--showduplicates这个参数是关键,默认list只显示每个包的最高版本,而我们要找的恰恰是一个不在最高位的 18.09.7。如果这条命令的输出里看不到18.09.7-3.el7,说明镜像站的 aarch64 目录没同步到这个版本,换一个镜像站比继续折腾更快。

3. 把版本钉在 18.09.7:cli → ce 的两步安装与三条验证

3.1 为什么不能一条yum install docker-ce了事

直接敲yum install docker-ce时,dnf 会在所有可用版本里挑「最优候选」,也就是版本号最高的那个。在麒麟上这个候选通常是 20.10.x,而它的依赖container-selinux >= 2:2.74在当前源里满足不了,dnf 就抛出那条经典的cannot install the best candidate for the job。错误信息指向的是 docker-ce,根因却在依赖包上,这就是为什么很多人第一反应是源配错了。

拆成两步的实际收益有三个:第一,docker-ce-cli的依赖树浅,先装能把「源是否可用」这个变量单独隔离出来验证;第二,docker-ce对docker-ce-cli的版本要求是精确匹配(18.09.7-3.el7 对 18.09.7-3.el7),先把 cli 钉死,第二步就不会被解析器拉到别的版本上去;第三,真出错时你能明确知道是 cli 阶段还是 daemon 阶段的问题。

至于--skip-broken和--nobest,这两个参数在报错信息里被 dnf 主动推荐,但在这个场景下都是陷阱。--skip-broken会跳过装不上的包继续装,结果就是 docker-ce 装上了、cli 没装,docker命令直接不存在;--nobest会让 dnf 退而求其次选低版本,看起来成功了,但选到哪个版本不可控。血泪经验是:这两个参数只在明确知道后果时用,排错阶段不要碰。

3.2 安装、启动与开机自启

# 1) 先装 CLI,版本号写死,-y 避免交互卡在脚本里 dnf install -y docker-ce-cli-18.09.7-3.el7 # 2) 再装 daemon 本体,版本必须与 CLI 严格对齐 dnf install -y docker-ce-18.09.7-3.el7 # 3) 开机自启 + 立即启动,一条命令搞定两件事 systemctl enable --now docker # 4) 看服务状态,--no-pager 防止输出被 less 截住 systemctl status docker --no-pager

第二步如果仍然报container-selinux相关错误,说明麒麟源里确实没有满足条件的版本,这时候的正确做法是去麒麟源里查一下现有版本:dnf list container-selinux,如果版本号低于2.74,只能退回到 18.09.7 这条线,别硬升级底层 selinux 包——那会牵动整个系统的安全策略,风险远大于收益。

systemctl enable --now是两件事的合并写法,等价于systemctl enable docker && systemctl start docker。有些老脚本里还会见到service docker start,在 systemd 环境下不建议再用,因为它绕过了 unit 文件里的ExecStartPre检查,出问题时日志里看不到关键信息。

3.3 三条验证:版本、运行时状态、容器能跑起来

# 版本号核对,输出应为 18.09.7, build 2d0083d docker --version # 服务端信息:重点看 Server Version、Storage Driver、Cgroup Driver docker info --format 'Server={{.ServerVersion}} Driver={{.Driver}} Cgroup={{.CgroupDriver}}'

docker info里三个字段值得逐个看。Server Version要和客户端版本一致,不一致说明 cli 和 ce 装成了不同版本,属于典型的「分两步装但第二步被解析到别的版本」;Storage Driver在麒麟上一般是overlay2,如果显示vfs或者devicemapper,性能和磁盘占用都会差一个量级,需要检查内核模块;Cgroup Driver默认是cgroupfs,这是后面接 Kubernetes 时最常见的改动点。

真正跑通一个容器才算验证完成。内网机器拉不到公网镜像,用save/load这组命令把镜像搬过去,这是离线环境的标准做法:

# 在一台能联网、架构相同(aarch64)的机器上准备镜像 docker pull hello-world docker save hello-world -o hello-world.tar # 把 tar 拷到麒麟机器后导入 docker load -i hello-world.tar # 跑一次,--rm 让容器退出后自动清理 docker run --rm hello-world

docker save保留的是镜像的完整层结构和元数据,docker export导出的是容器文件系统快照、会丢元数据,两者别混用。导入后docker images看到的REPOSITORY和TAG可能与源端一致,也可能变成<none>,后者说明导出时用了错误的方式。

4. 避坑排查:五条从报错到修好的实录

4.1 报错nothing provides container-selinux >= 2:2.74

现象是dnf install docker-ce直接失败,错误末尾提示try to add '--skip-broken'。原因是 dnf 选中了 20.10 系列作为最优候选,而这个版本的硬依赖在麒麟源里满足不了。解决办法不是加参数跳过,而是把版本降到 18.09.7 这条不依赖高版本 selinux 的线,按 3.2 的两步走。如果业务上确实需要 20.10,那就得单独编译或找一个适配的container-selinux包,成本会高出很多。

4.2Job for docker.service failed且日志停在 bridge 初始化

现象是systemctl start docker失败,journalctl -u docker -n 50里最后一行是Error initializing network controller: could not create bridge。原因通常是宿主机上已经有别的组件占用了docker0这个网段,或者 firewalld 的 FORWARD 链策略是 DROP,导致 bridge 建不起来。解决分两步:先用ip addr show docker0确认网段冲突,冲突就在daemon.json里改bip和default-address-pools;没有冲突就检查iptables -L FORWARD -n的默认策略,把它改成 ACCEPT 再重启服务。

4.3 拉镜像超时或报x509: certificate signed by unknown authority

现象是docker pull卡在Pulling from然后超时,或者直连内网 registry 时报证书错误。前者的原因是默认走公网 registry,内网根本不通;后者是自建仓库用了自签证书。解决方式都在daemon.json里:内网环境配registry-mirrors指向可达的加速地址,自签证书的仓库把地址写进insecure-registries(注意这里只写host:port,不要带https://前缀,带了反而不生效)。

4.4/var/lib/docker把根分区撑满,系统起不来

现象是跑了一段时间后df -h显示/使用率 100%,systemctl各种服务开始报错。原因是镜像层、容器日志、volume 全默认落在/var/lib/docker,而麒麟装机时根分区经常只给了几十 G。解决要分两步走:先临时清理docker system prune -a腾出空间让系统恢复,再把>{ "exec-opts": ["native.cgroupdriver=systemd"], "data-root": "/data/docker", "log-driver": "json-file", "log-opts": { "max-size": "100m", "max-file": "3" }, "insecure-registries": ["registry.internal:5000"], "registry-mirrors": ["https://<mirror-host>"], "live-restore": true }

逐个说:exec-opts把 cgroup driver 从cgroupfs换成systemd,这是后面上 Kubernetes 的前置条件,早改早省事;># 1) 停服务,确保没有进程占用 /var/lib/docker systemctl stop docker # 2) 用 rsync 而不是 cp,保留权限位和硬链接,-a 归档模式 rsync -aHAX --progress /var/lib/docker/ /data/docker/ # 3) 确认大小对得上再删原目录,别急着 rm du -sh /var/lib/docker /data/docker # 4) 改 daemon.json 的># 组已存在时 -f 不会报错 groupadd -f docker # 把用户加入 docker 组,重新登录后生效 usermod -aG docker deploy # 当前会话立即生效,省得退出重登 newgrp docker

这里有个安全边界要说清楚:docker组的成员等价于 root,因为能挂载宿主机根目录进容器。所以这个组只给需要的人加,别图省事给所有人开。

防火墙这边,麒麟默认跑 firewalld,而 docker 会自己写 iptables 规则,两者容易打架。最常见的表现是容器能启动但访问不了外网。稳妥做法是把docker0网卡放进 trusted 区:

# 把 docker0 加入 trusted 区,避免被默认 zone 的规则拦掉 firewall-cmd --permanent --zone=trusted --add-interface=docker0 firewall-cmd --reload

反过来,如果你需要限制容器只能访问特定网段,就别用 trusted,改成在DOCKER-USER链里写规则,这条链是给用户自定义预留的,容器重启不会把它冲掉。

6. 让这套流程可重放:版本锁、离线 rpm 与一张验收表

单台机器装完就完事了,一批机器就不一样了。第二批机器装的时候镜像站可能同步了新版本,dnf install docker-ce-18.09.7-3.el7依然能装,但docker-ce-cli如果没写版本号就会被拉到新版,然后 cli 和 daemon 版本错位,docker命令能敲但连不上 daemon。

6.1 用 versionlock 把版本固定住

# versionlock 在 dnf-plugins-core 里,没装过要先装 dnf install -y 'dnf-command(versionlock)' # 把两个包一起锁住,少锁一个等于没锁 dnf versionlock add docker-ce-18.09.7-3.el7 docker-ce-cli-18.09.7-3.el7 # 确认锁生效 dnf versionlock list

锁住之后dnf update不会动这两个包,要升级必须先versionlock delete。这个习惯我建议在所有需要长期稳定运行的机器上都养成,尤其是在源指向公开镜像站、版本随时会变的环境里。

完全离线的场景还得把 rpm 提前下下来。dnf download --resolve --alldeps会把整棵依赖树拉齐,注意必须在同架构、同系统版本的机器上下载,x86 上下的包搬到 aarch64 上一个都装不上:

# 下载 docker 及其全部依赖到本地目录 dnf download --resolve --alldeps --destdir=/tmp/docker-rpms \ docker-ce-18.09.7-3.el7 docker-ce-cli-18.09.7-3.el7 # 目标机上禁用所有源,只用本地 rpm 安装,避免又去联网解析 dnf install -y --disablerepo='*' /tmp/docker-rpms/*.rpm

6.2 一张验收表,装完照着打勾

检查项命令合格标准
客户端版本docker --version18.09.7, build 2d0083d
服务端版本docker info --format '{{.ServerVersion}}'与客户端一致
存储驱动docker info --format '{{.Driver}}'overlay2
cgroup 驱动docker info --format '{{.CgroupDriver}}'systemd(对接 K8s 时)
数据目录docker info --format '{{.DockerRootDir}}'指向独立分区
开机自启systemctl is-enabled dockerenabled
容器连通性docker run --rm hello-world输出 Hello from Docker!

这张表的价值不在单次安装,而在批量部署时能一眼看出哪台机器没配好。我一般会把它写成一个 shell 脚本,七条检查全过才返回 0,接进批量执行工具里,几十台机器跑一遍就知道哪几台需要返工。

从那以后我每次给麒麟机器装 docker,都会先把dnf repolist -v的 URL 展开结果贴进变更单,再走两步安装、再跑一遍这张表——看着啰嗦,但确实没再出现过「装完能用、重启就挂」的情况。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进