☰
麒麟V10离线安装Kubernetes 1.32.11全流程实践
2026/10/15 22:14:27 网站建设 项目流程

前阵子被拉去给一批新到的麒麟V10服务器装Kubernetes,机房在隔离网里,没法访问外网,版本还要卡在1.32.11。网上在线安装的教程一抓一大把,可真到了离线环境,几乎所有教程都会卡死在第一步:下载命令根本没法定外网跑。这篇就把我在麒麟V10上离线安装k8s 1.32.11的完整过程写下来,从离线介质准备、系统适配、部署实操到踩坑复盘,适合正在隔离网或离线环境里做K8s集群部署的同学参考。

环境先交代一下:我这批服务器是麒麟V10服务器版(x86_64架构),控制平面一台,工作节点两台,共享一套内网yum源,但内容很旧,只有基础系统包。整个过程最麻烦的不是装K8s本身,而是把所有该带的依赖、二进制、镜像都想全,然后一次性搬进内网。

1. 离线部署的总体设计:版本、架构与介质清单

离线安装最忌讳上来就装,装到一半发现缺东西。别问我怎么知道的,我第一遍就是因为少了一个conntrack-tools的RPM包,卡在kubeadm init前面整整半天。所以第一步,先把版本组合、节点规划、介质清单全部定下来。

1.1 先聊版本组合:为什么是 1.32.11 + containerd 1.7.x

Kubernetes从1.24之后就彻底移除了dockershim,容器运行时基本只有containerd和CRI-O这类选择。生产环境我一般直接用containerd,理由很简单:RPM包和二进制都方便离线准备,kubelet对接稳定,社区默认就是它。

K8s 1.32.11在1.32.x这个补丁系列里处于比较靠后的位置,稳定性已经经过多轮验证。containerd我选的是1.7.x系列,这个版本对SystemdCgroup支持成熟,和1.32的kubelet配合没什么坑。CNI选Flannel,没选Calico,原因后面会说。

这里有一个关键点:K8s的版本选择不是越新越好,而是要和操作系统生态匹配。麒麟V10的内核一般不会太新,而K8s 1.32要求Linux内核足够支持overlayfs、iptables、ipvs这些基础能力,4.19+基本都能满足。版本太高反而可能出现内核模块不匹配的问题。

1.2 节点规划:控制平面、工作节点与资源建议

规划节点时先确认架构和角色分配。我这边是3台:

节点角色数量最低建议配置主要负载
控制平面14核8G,系统盘100Gkube-apiserver、etcd、controller-manager、scheduler
工作节点24核8G,系统盘100G+数据盘100Gkubelet、containerd、业务Pod
介质制作机1(有外网)2核4G即可下载RPM、二进制、镜像并打包

这里要特别提醒:控制平面的磁盘不要抠门。etcd默认数据目录在/var/lib/etcd,虽然一开始占用不大,但集群运行半年后日志和事件数据会慢慢膨胀。工作节点的数据盘建议单独挂给containerd,别跟系统盘混在一起,这个我后面会展开讲。

1.3 离线介质的内容清单:一个文件都不能少

离线介质包不是简单地拷几个RPM就完事,里面至少包含四类东西:

  1. K8s核心二进制:kubeadm、kubelet、kubectl,我直接下载官方RPM包,省去手动解压二进制再写systemd服务文件的麻烦。
  2. 容器运行时:containerd本体加cri-containerd可能涉及的文件,还有CNI插件(cni-plugins)。
  3. 系统依赖RPM:kubelet、containerd运行时依赖的库和工具,比如libseccomp、conntrack-tools、socat、ipvsadm等,内网yum源里不一定全,必须提前准备。
  4. 容器镜像:K8s控制面组件镜像(kube-apiserver、etcd、coredns、pause等)、网络组件镜像(flannel)、业务测试镜像(nginx)。
  5. CNI部署manifest:比如flannel的yaml文件,离线环境没法从GitHub直接拉,必须提前下好。

我把这些写成了一张Excel清单,每项都标注了版本号和来源,后面所有机器都按这张表干活。这个习惯建议养成,尤其多人协作时,清单能避免“我以为你拷了”这种悲剧。

2. 麒麟V10系统侧准备:装之前必须改掉的四个地方

麒麟V10虽然整体跟RHEL系的CentOS很像,但细节上有自己的脾气。系统侧我踩过的坑多半是“看起来没问题,装到一半就报错”。

2.1 内核与架构摸底

麒麟V10的服务器版内核通常基于4.19或5.x,x86_64架构。动手前先跑一下这几个命令:

uname -a cat /etc/os-release arch

根据输出确认发行版本、内核和架构,再去选对应版本的RPM和二进制包。比如arch是x86_64,下载K8s二进制时就要选linux-amd64,不要搞成arm64,这个低级错误真有人犯过。

另外看看/var/lib/dpkg或/var/lib/rpm是否存在,确定包管理器是dpkg还是rpm。麒麟V10不同子版本可能一个基于Debian系,一个基于RPM系,这个直接影响离线RPM的兼容性。我这边全是RPM系,下面所有命令也都按RPM系来写。

2.2 加载 overlay 与 br_netfilter,配好内核参数

containerd需要overlayfs支持镜像分层,K8s的网络转发需要br_netfilter。这俩内核模块不加载,后面会出现各种玄学问题,比如Pod网络不通、service不能访问。

写一个加载模块的配置:

cat > /etc/modules-load.d/k8s.conf <<'EOF' overlay br_netfilter EOF modprobe overlay modprobe br_netfilter

然后配置内核参数:

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

注意net.ipv4.ip_forward一定要设成1。默认情况下很多Linux发行版ipv4转发是关的,K8s的Pod跨节点通信依赖这个开关,不然后面node之间互相访问Pod会超时。

2.3 swap、防火墙与 SELinux:该关的关,该放行的放行

Kubelet默认不允许swap开着,至少1.32版本上还是这样。所以先关掉swap:

swapoff -a sed -i '/ swap / s/^/#/' /etc/fstab

别只执行swapoff -a就完事,重启后fstab还会把它挂回来。记得注释fstab里的swap行。

防火墙和SELinux是另一对常客。安全要求高的环境不建议直接firewalld stop,可以把K8s需要的端口放行:

  • 控制平面:6443(apiserver),2379-2380(etcd),10250(kubelet),10259(scheduler),10257(controller-manager)
  • 工作节点:10250,30000-32767(NodePort)

如果只想快速验证,直接关掉firewalld:

systemctl stop firewalld && systemctl disable firewalld

SELinux也一样,要么放行,要么setenforce 0并修改/etc/selinux/config。我通常在内网测试环境选择直接关闭,但在生产环境还是建议保留SELinux并用策略放行,只是麒麟V10的SELinux策略对K8s的支持有时不太完善,需要额外调。

2.4 磁盘规划:别让系统盘被容器镜像塞满

工作节点上containerd默认数据目录是/var/lib/containerd,系统盘空间如果只有100G,跑几个镜像和数据卷就可能撑爆。我习惯单独挂一块数据盘给containerd,挂载参数同时考虑限额:

mkfs.xfs /dev/sdb mkdir -p /var/lib/containerd mount /dev/sdb /var/lib/containerd echo "/dev/sdb /var/lib/containerd xfs defaults 0 0" >> /etc/fstab

为什么不建议用ext4?xfs在处理大文件和高并发写入时更稳,尤其容器镜像分层多,overlayfs在xfs下表现更好。当然ext4也能用,只是个人经验里xfs的坑少一些。

控制平面上的etcd数据目录/var/lib/etcd也可以单独挂盘,但测试环境不强制;生产环境一定挂,因为etcd是集群的命根子,磁盘满了整个集群直接不可写。

3. 制作离线介质包:在有网机器上一次准备到位

这一步需要一台能访问外网的机器,通常是运维的笔记本或者一台临时跳板机。介质包做得好不好,直接决定内网部署顺不顺利。

3.1 K8s 二进制包与 RPM 依赖下载

先建目录结构:

mkdir -p k8s-1.32.11-offline/{rpm,bin,images,manifests} cd k8s-1.32.11-offline

K8s官方RPM仓库在特定镜像站有同步,可以在介质机上配置临时yum源,然后下载。先用yumdownloader把依赖一并拉下来:

yum install -y yum-utils yumdownloader --resolve --destdir=./rpm \ kubeadm \ kubelet \ kubectl

如果直接下载官方二进制,可以用:

wget https://dl.k8s.io/v1.32.11/kubernetes-server-linux-amd64.tar.gz

这个Server包比较大,但里面包含完整的kube-apiserver、kube-controller-manager、kube-scheduler等二进制,用来部署控制平面非常方便。如果你倾向于RPM方式,那RPM仓库里的kubeadm、kubelet、kubectl三个包就够了,注意把它们的依赖RPM一起--resolve下来。

containerd和CNI插件也是关键:

wget https://github.com/containerd/containerd/releases/download/v1.7.x/containerd-1.7.x-linux-amd64.tar.gz wget https://github.com/containernetworking/plugins/releases/download/v1.x/cni-plugins-linux-amd64-v1.x.tgz

版本号按你实际选择去release页面核对。顺便把runc的二进制也装了,containerd的tar包里通常不会自带runc,单独下载一份:

wget https://github.com/opencontainers/runc/releases/download/v1.1.x/runc.amd64

最后把依赖系统工具装齐:

yum install -y yum-utils tar socat conntrack-tools ipvsadm ipset

这些工具的内网系统不一定自带,先在外网机器上下载RPM包放进rpm目录。

3.2 K8s 系统镜像导出:用 kubeadm 的清单做事

K8s控制面镜像版本多且杂,手敲很容易错。最稳妥的办法是用kubeadm自身列出的镜像清单:

kubeadm config images list --kubernetes-version=v1.32.11

输出大概是下面这些:

  • registry.k8s.io/kube-apiserver:v1.32.11
  • registry.k8s.io/kube-controller-manager:v1.32.11
  • registry.k8s.io/kube-scheduler:v1.32.11
  • registry.k8s.io/kube-proxy:v1.32.11
  • registry.k8s.io/etcd:3.5.x
  • registry.k8s.io/coredns/coredns:v1.11.x
  • registry.k8s.io/pause:3.10

注意kubeadm config images list输出的版本号会随K8s依赖组件的更新变化,你要以实际输出为准。然后在介质机上用containerd或docker把这些镜像拉下来再导出:

ctr -n k8s.io images pull registry.k8s.io/kube-apiserver:v1.32.11 # 逐个pull后导出成tar ctr -n k8s.io images export ./images/k8s-images.tar \ registry.k8s.io/kube-apiserver:v1.32.11 \ registry.k8s.io/kube-controller-manager:v1.32.11 \ ...

如果介质机上有docker,也可以用docker pull加docker save,效果一样。导出成一个总tar包的好处是内网导入时只需执行一次命令。

Flannel镜像同样提前拉下来:

docker pull flannel/flannel:v0.25.7 docker pull flannel/flannel-cni-plugin:v1.5.1-flannel2 docker save flannel/flannel:v0.25.7 flannel/flannel-cni-plugin:v1.5.1-flannel2 > ./images/flannel-images.tar

业务测试镜像也顺手放一个:

docker pull nginx:alpine docker save nginx:alpine > ./images/nginx-test.tar

3.3 校验、打包与内网落地

介质包准备好了,不能急着拷进去,先做两件事:

第一,校验sha256。把每个tar包、RPM包都算一遍哈希,内网导入后可以通过sha256sum -c验证文件完整性。离线环境没有外网下载二次校验的可能,介质机的哈希表就是唯一凭证。

第二,写README。把介质包内容、版本、导入顺序全写进一个txt文件。别嫌啰嗦,一个月后再回来维护,你看到'README'里的步骤能省很多时间。

最后打包上传到内网存储或直接U盘拷贝:

tar czf k8s-1.32.11-offline.tar.gz k8s-1.32.11-offline/

内网任意一台机器解压后共享给其他节点即可。

4. 内网部署实操:从 containerd 到集群可用

介质包到了内网,部署开始。

4.1 安装 containerd 并改对三处配置

首先装依赖RPM。麒麟V10内网的yum源不完整,直接用本地目录安装:

rpm -ivh ./rpm/*.rpm --nodeps

--nodeps是双刃剑,依赖缺失时它会跳过检查,但运行时会报错。我更推荐用yum localinstall:

yum localinstall -y ./rpm/*.rpm

它会尝试从内网yum源补全缺失依赖,实在找不到再人工介入。我这边有几台机器缺socat和libseccomp,都是从介质包补上的。

然后解压containerd和CNI插件:

tar Cxzf /usr/local -C /tmp containerd-1.7.x-linux-amd64.tar.gz mkdir -p /opt/cni/bin tar Cxzf /opt/cni/bin -C /tmp cni-plugins-linux-amd64-v1.x.tgz

生成containerd默认配置:

mkdir -p /etc/containerd containerd config default > /etc/containerd/config.toml

这个默认配置要改三处。第一处是SystemdCgroup从false改成true,否则kubelet和containerd的cgroup管理方式不一致,Pod创建会报错。

[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc] systemd_cgroup = true

第二处是sandbox_image,换成我们导入的pause镜像地址。离线网络环境下如果保持默认值,kubelet一旦需要创建sandbox就会试图去联网拉镜像。

[plugins."io.containerd.grpc.v1.cri"] sandbox_image = "registry.k8s.io/pause:3.10"

第三处是registry.mirrors配置,为了内网如果有私有镜像仓库时能优先走内网地址,避免每次拉镜像都超时。如果没有私有仓库,可以留空或指定镜像站。

改完启动containerd:

systemctl daemon-reload systemctl enable --now containerd

4.2 导入镜像:namespace 错了等于白导

containerd的镜像导入必须注意namespace。默认ctr命令操作的namespace是default,但kubelet通过CRI调用containerd时使用的是k8s.io这个namespace。用ctr images import导入到default,kubelet照样看不到。

正确的导入姿势:

ctr -n k8s.io images import ./images/k8s-images.tar ctr -n k8s.io images import ./images/flannel-images.tar ctr -n k8s.io images import ./images/nginx-test.tar

导入后用以下命令确认:

ctr -n k8s.io images list | grep registry.k8s.io

这个坑我第一遍就踩了,当时导完半天,结果Pod一直ImagePullBackOff,查到最后发现是namespace不对。记住:CRI用的namespace是k8s.io。

4.3 kubeadm init 与配置文件

不推荐直接写一长串命令行参数,建议把配置写进yaml文件,方便留存和审计。先创建kubeadm.yml:

apiVersion: kubeadm.k8s.io/v1beta4 kind: InitConfiguration localAPIEndpoint: advertiseAddress: 192.168.10.11 bindPort: 6443 nodeRegistration: name: master01 criSocket: unix:///var/run/containerd/containerd.sock --- apiVersion: kubeadm.k8s.io/v1beta4 kind: ClusterConfiguration kubernetesVersion: v1.32.11 controlPlaneEndpoint: "192.168.10.11:6443" networking: podSubnet: "10.244.0.0/16" serviceSubnet: "10.96.0.0/12"

podSubnet用的是10.244.0.0/16,这个网段要和后面Flannel的默认网段一致,不然装完网络组件后Pod拿不到IP。controlPlaneEndpoint填高可用虚拟IP或第一个控制平面节点IP,即使单控制平面也建议写明白。

执行初始化:

kubeadm init --config=kubeadm.yml

如果一切顺利,最后会输出一段kubeadm join命令。记得保存,或者用kubeadm token create --print-join-command重新生成。

初始化完成后,按提示配置kubeconfig:

mkdir -p $HOME/.kube cp -i /etc/kubernetes/admin.conf $HOME/.kube/config chown $(id -u):$(id -g) $HOME/.kube/config

4.4 工作节点 join 与 CNI 网络一次搞定

工作节点上先装好同样的依赖RPM、containerd,并且导入介质包里的镜像。然后执行控制平面生成的join命令:

kubeadm join 192.168.10.11:6443 --token xxx \ --discovery-token-ca-cert-hash sha256:xxx

join成功后再装CNI。我选Flannel的原因就是简单,就一个yaml文件加几个镜像,离线部署压力小:

kubectl apply -f ./manifests/kube-flannel.yml

注意kube-flannel.yml要在介质准备阶段下载好,或者在控制平面节点能联网时下载,否则离线环境手抖都抖不出来。Flannel装完后默认使用10.244.0.0/16网段,和kubeadm.yml里的podSubnet吻合。

等每个节点上的kube-flannel-dsPod变为Running,基本上网络就通了。

4.5 集群健康检查:别急着跑业务

装完之后先看一轮状态:

kubectl get nodes kubectl get pods -A

然后跑到工作节点上看Pod网络情况:

kubectl run ngx --image=nginx:alpine --port=80 kubectl get pod -o wide

从工作节点或控制平面去curl这个Pod的IP,如果能通,说明Pod间网络正常。再尝试创建一个NodePort Service,验证节点端口转发能力。这套检查流程虽然简单,但能在业务部署前把底层网络问题暴露干净。

5. 离线安装踩坑记录:五个让我多花了一个周末的问题

单看步骤很顺,但中间我实际踩了不少坑。这些坑每个都能让部署暂停半小时以上,一一列出来,希望各位能绕开。

5.1 Pod 卡在 ContainerCreating,罪魁祸首是 pause 镜像

现象:kubeadm init完成后,kube-system里的Pod全部卡在ContainerCreating,describe一看是sandbox创建失败。

排查:执行ctr -n k8s.io images list | grep pause,结果里没找到pause镜像。原因是我在一台机器上导镜像时只导了控制面组件镜像,忘了把pause单独拉出来。而containerd默认配置里的sandbox_image指向registry.k8s.io/pause:3.10,离线环境下无法拉取,Pod就永远建不起来。

解决:介质机补下pause镜像,docker pull registry.k8s.io/pause:3.10,导出,再到内网导入,重启containerd。另外我建议在containerd配置里显式写清楚sandbox_image,不要依赖默认值——版本升级时默认值可能变,显式配置能减少差异。

5.2 join 失败却提示 timeout?先查 /etc/hosts 和主机名

现象:工作节点执行kubeadm join,报timeout waiting for the kubelet这类错误,信息很泛,让人无从下手。

排查链路:先看systemctl status kubelet,发现kubelet在反复重启,日志里报证书或节点注册相关的错误。再看/etc/hosts,发现工作节点的主机名并没有解析到本机IP。Kubelet启动后要注册到apiserver,它得能把自己地址报上去,而hostname解析不畅时注册就卡住。

解决:在所有节点/etc/hosts里加上:

192.168.10.11 master01 192.168.10.21 node01 192.168.10.22 node02

然后重启kubelet。这个问题在纯内网环境特别常见,因为内网DNS不一定建得全。

5.3 kube-proxy 引用 ipvs 模式,系统却缺模块

现象:kube-system里kube-proxyPod反复CrashLoopBackOff,日志中提示can not find ipvs或者ipset相关错误。

原因:1.32版本的kube-proxy默认或配置文件中可能使用ipvs转发模式,但麒麟V10系统默认没有加载ip_vs内核模块,也没有安装ipvsadm、ipset这些工具。

解决:

yum install -y ipvsadm ipset cat >> /etc/modules-load.d/k8s.conf <<'EOF' ip_vs ip_vs_rr ip_vs_wrr ip_vs_sh nf_conntrack EOF modprobe ip_vs modprobe ip_vs_rr modprobe ip_vs_wrr modprobe ip_vs_sh modprobe nf_conntrack

如果还是不行,就把kube-proxy的mode改回iptables,配置里把mode: iptables显式写好。生产环境用iptables虽然性能略低,但胜在依赖少、好排查,小规模集群完全够用。

5.4 RPM 安装顺序带来的依赖连环坑

现象:直接执行rpm -ivh kubelet-1.32.11.rpm,系统提示缺少kubernetes-cni或libseccomp依赖。

原因:RPM安装不像yum会自动解析依赖,离线介质包里的RPM必须一次性安装或者按依赖顺序装。

解决:其实介质机在下载时就可以把依赖一起下全。内网安装时最好用:

yum localinstall -y ./rpm/*.rpm

如果某些RPM依然被yum拒绝,再手动指定依赖顺序:先装libseccomp、conntrack-tools、socat、kubernetes-cni,最后装kubelet、kubeadm、kubectl。依赖顺序这个事,介质机上下载多花五分钟,内网能省两小时。

5.5 重启之后集群失联:enable 从左往右都要配

现象:全部装完主备确认没问题,结果晚上机房断电重启,第二天集群连不上了。

排查:kubectl get nodes超时,登录控制平面看systemctl status kubelet,发现kubelet是inactive状态。再看containerd,也是没开机自启。

原因:安装过程中我手动启动过服务,但没有执行systemctl enable。重启后containerd和kubelet都没拉起来,kubelet的enable是RPM包安装时配好的,但我用的二进制解压方式没有systemd服务文件。

解决:用RPM方式安装的跑:

systemctl enable --now containerd systemctl enable --now kubelet

用二进制方式安装的,需要自己准备systemd service文件。检查一下/usr/lib/systemd/system/下有没有kubelet.service,没有就从介质包里补一个。

另外etcd是静态Pod,由kubelet拉起,所以kubelet没起来时etcd也会一起“消失”,这也是重启后连不上集群的核心原因。

6. 收尾:这套离线方案后续怎么维护

集群跑起来只是开始,后续的维护才见真章。离线环境没有外网,一切都要靠介质包和备份来兜底。

6.1 介质包是“活化石”,升级扩容都靠它

离线部署的介质包必须留好,它是你这个集群版本的“活化石”。以后要扩容节点,直接拿同一套介质包按第4章的流程再走一遍;要升级补丁版本,则在介质机上重新拉新的二进制和镜像,打包后再上传内网。

我建议给介质包打上版本标签,例如k8s-1.32.11-offline-2025xx.tar.gz,哪怕K8s升级了新版本,旧的介质包也别删。K8s补丁版本之间一般可以滚动升级,但你总会遇到某台老节点需要回滚或重装的情况,那时候旧介质包就是救命稻草。

6.2 该备份的文件清单

至少备份这几类东西:

  • /etc/kubernetes/:kubeadm配置、证书、admin.conf
  • /etc/containerd/config.toml:containerd配置
  • /etc/sysctl.d/k8s.conf:内核参数
  • /var/lib/etcd/:etcd数据(如果有条件,定期做etcd快照)
  • kubeadm.yml:初始化配置文件

建议写个cron脚本,定期把etcd快照和kubeadm配置打包放到NFS或独立备份机。离线环境最怕的是“一切都在那台机器上”,结果那台机器坏了。

6.3 有条件的话,再往前一步做内网镜像仓库

纯离线介质包适合一两次部署和几十台以内的集群。如果这套环境要长期运营、节点会不断增长,建议在条件允许时搭一个内网镜像仓库(比如基于私有Registry或Harbor),把镜像统一推送到内网仓库,各节点从仓库拉取。

介质包的导入是静态的,镜像仓库是动态的。前者适合开局,后者适合运维。两者可以并存:介质包用于节点初始化,镜像仓库用于日常调度和扩容。先问自己一个问题:“这套集群会不会活过三个月?”会的话,镜像仓库值得提前准备。

最后讲点实在的,我在这套麒麟V10离线环境上最大的体会是:离线部署的复杂度并不在K8s本身,而在“能不能把所有变量前置控制住”。版本锁定、依赖齐全、镜像清单精确,离线环境会比在线环境更容易复现结果——因为没有人会跑着跑着偷偷给你装个新版本。把介质包维护好、把备份做勤快,这套集群就能安心跑很久。

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

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

立即咨询