ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Kubernetes离线部署与Calico网络排错:内网集群搭建实战

Kubernetes离线部署与Calico网络排错:内网集群搭建实战 干运维这些年最让我头疼的不是Kubernetes本身怎么用而是在一个彻底没有外网的机房里把一套多节点集群从零搭起来。前段时间正好接手了这样一个任务新机房的内网完全是物理隔离的U盘和移动硬盘是唯一能进出的通道需要在几台机器上部署一套Kubernetes集群网络组件还必须用Calico。整个过程踩了不少坑尤其是Calico这块排错排到凌晨是常有的事。这篇文档算是把这套离线部署流程和Calico排错经验整理清楚了适合正在准备内网集群、或者被Calico折腾得焦头烂额的运维和部署工程师。内容不追求讲得多深重点是可落地、能直接照抄顺便把我实际踩过的坑都标注出来。1. 离线部署的整体设计与思路拆解1.1 什么场景必须做离线部署很多人可能觉得离线部署就是把安装包考过去装一下其实没那么简单。集群涉及的不只是几个rpm包还有一堆镜像、网络组件、RBAC配置、证书文件任何一个环节漏了后续都可能卡住。最常见的离线场景有这么几种一是生产机房物理隔离业务数据不允许出内网二是政企、教育、金融机构的内网环境安全合规要求严格机器不允许配置外网出口三是临时搭建的演示环境现场根本没有互联网可用。这些环境有一个共同特点外网是奢侈品内网才是主场。所以离线部署不是“能不能装”的问题而是在这种受限环境下能不能把集群装得和在线环境一样稳定。离线部署的困难点往往不在Kubernetes本身而在依赖链。控制面组件要镜像、etcd要镜像、CoreDNS要镜像CNI插件、kube-proxy、pause容器也一个不能少。再加上机器架构可能是x86也可能是ARM镜像架构一旦不对部署后一堆CrashLoopBackOff等着你。所以离线部署第一步不是急着敲命令而是先把清单列清楚你需要哪几个组件、哪些镜像、哪些二进制文件、目录结构怎么组织。1.2 离线包的三种组织方式离线部署的物料组织方式我见过三种按可靠程度排序第一种是把所有镜像打入tar包逐台导入到节点本地镜像仓库第二种是在内网搭建一个私有Registry把全部镜像push进去各节点从Registry拉取第三种是直接把二进制和配置文件全做成绿色版手工写systemd服务来启动。实际项目里我推荐的是“内网Registry rpm包集合 离线配置文件目录”的组合。为什么这么组合因为Kubernetes无论是kubeadm初始化还是节点加入都需要从镜像仓库拉取镜像。如果只有tar包每台节点都要手动导入一遍节点一多非常容易漏。而内网Registry只要初始化一次所有节点都把--image-repository指向它跟在线部署的体验几乎一样。rpm包则解决基础依赖的问题比如kubeadm、kubelet、kubectl、containerd、conntrack、ipset这些提前下载好内网用yum本地源或者rpm -Uvh批量安装。配置文件目录里放的是后续要用的所有manifest、证书、环境变量脚本方便统一管理。常见实践是这样组织的offline-k8s/ ├── rpm/ │ ├── kubeadm-1.28.2-0.x86_64.rpm │ ├── kubelet-1.28.2-0.x86_64.rpm │ ├── kubectl-1.28.2-0.x86_64.rpm │ └── containerd-1.7.x.x86_64.rpm ├── images/ │ ├── k8s-control-plane.tar │ ├── calico.tar │ └── pause.tar ├── registry/ │ └── registry-2.tar └── manifests/ ├── calico.yaml └── kubeadm-config.yaml分发方案就看现场条件了。机器少直接U盘拷机器多可以先拷到一台跳板机再开一个临时HTTP服务python3 -m http.server让其他节点拉取。我建议先在跳板机上把物料整理好再统一分发省得每台机器都要插一遍U盘。1.3 为什么网络组件选CalicoCNI插件有flannel、Calico、Weave、Cilium为什么我在这个项目里选Calico核心原因有三个一是性能好Calico基于BGP路由协议Pod网络走的是三层路由不走隧道封装时性能接近物理网络二是功能强支持NetworkPolicy能实现精细化流量管控这在政企内网往往是刚需三是资料多、排错可查性强社区里踩坑记录一大把真出问题能快速定位。flannel虽然简单但它的VXLAN封装带来的性能损耗在跨节点场景下比较明显而且不支持NetworkPolicy。Cilium更现代但eBPF特性依赖较新的内核在内网这些旧内核系统上容易水土不服对排错要求也高。综合来看Calico在稳定性和功能之间是最平衡的选择。但选型是一回事部署和排错是另一回事。Calico在离线环境里的坑特别集中镜像架构不对、自动检测网卡失败、BGP会话建不起来、MTU设置错误这几类问题我后面单独开一节细讲。2. 核心组件详解与离线部署要点2.1 Kubernetes控制面组件到底有哪几个先带大家把控制面组件过一遍毕竟离线部署你得先知道自己在装什么。控制面有四个核心组件kube-apiserver是整个集群的入口所有请求先过它也是唯一需要直接和etcd交互的组件kube-controller-manager负责节点控制器、副本控制器这些常规控制逻辑kube-scheduler负责把Pod调度到合适的节点etcd保存集群所有状态数据是唯一的状态存储。工作节点上则是kubelet对接容器运行时、管理Pod生命周期和kube-proxy实现Service负载均衡规则。离线部署时这些组件对应的镜像分别是registry.k8s.io/kube-apiserver、kube-controller-manager、kube-scheduler、kube-proxy、pause、coredns和etcd外加一个registry镜像用来跑内网私有仓库。很多人离线部署失败不是不会装而是忘了Calico还需要自己的镜像。Calico在Kubernetes里以DaemonSet方式运行每台节点上都会有一个calico-node容器镜像名称通常是calico/node和calico/cni部分版本还需要calico/kube-controllers。这几个镜像如果不提前准备好就算Kubernetes集群本身起来了节点也会一直处于NotReady状态。2.2 Calico的工作原理与流量路径排错之前必须先理解Calico怎么转发的。Calico的核心思想是“每台节点都是一个BGP路由器”它把Pod的IP地址通过BGP协议通告给集群内其他节点让每个节点都知道“目标Pod IP在哪个节点上”。具体到数据转发路径是这样的Pod内的流量从veth虚拟网卡出来到达宿主机上的calixxx网卡每创建一个Pod就有一对veth宿主机查路由表发现目标IP对应的下一跳是另一台节点于是把数据包从物理网卡发出。如果启用了IPIP模式数据包会在外层再包一层IP头对端节点收到后解封装再交给Pod。这个过程中Felix组件负责维护宿主机上的路由、iptables规则和网卡BIRD组件负责BGP路由的广播和接收CNI插件负责把Pod的网络命名空间和veth网卡挂到Calico的配置里。理解了这条链路排错就有方向了。跨节点Pod不通要么是路由没被BGP广播出去要么是BGP会话没建立要么是iptables规则挡住了要么是MTU不一致导致大包不通、小包通。每一条都能对应到具体的排查命令。2.3 关键参数怎么选Pod网段、MTU与网络模式离线部署Calico之前有几个参数必须先想清楚不然部署完再改就是灾难。首先是Pod网段CIDR。这个值要跟kubeadm init的--pod-network-cidr保持一致Calico默认值是192.168.0.0/16我习惯改成10.244.0.0/16因为内网物理网段经常是192.168.x.x两个网段一旦重叠路由全乱。如果你是给现有网络扩容注意Pod网段不能和物理网络、Service网段重叠这是硬性要求。其次是MTU最大传输单元。Calico默认自动探测MTU但在内网环境经常探测不准这种情况下必须手动指定。一般物理网卡标准是1500Calico在IPIP和VXLAN模式下需要给隧道头预留空间IPIP模式要减20字节设成1480VXLAN模式要减50字节设成1450。如果某些网络设备MTU较小还得继续压低。这里容易踩的坑是MTU设置不一致会导致跨节点大包ping不通ping -M do -s 1472能通但普通大包不通排查起来很迷惑。最后是网络模式。Calico支持三种跨节点模式BGP直接路由、IPIP隧道、VXLAN隧道。内网环境如果所有节点在同一个二层网络优先用BGP性能最好把CALICO_IPV4POOL_IPIP设为Never跨网段、跨数据中心传播路由的场景用IPIP更省事VXLAN适合底层网络不支持BGP的情况但性能损耗最大。我这次部署的机房节点在同一个VLAN里就直接关了IPIP走纯BGP模式。3. 实操过程离线部署一套多节点集群3.1 节点环境准备与内核参数所有的离线部署第一步永远是检查机器。我在实际项目中吃过亏两台机器看起来硬件配置一样结果一个内核模块没加载节点加入集群后直接NotReady。所以以下操作建议每台节点都执行一遍。先关闭交换分区Kubernetes强制要求否则kubelet会报错swapoff -a sed -ri s/.*swap.*/#/ /etc/fstab然后确认必要的内核模块已经加载modprobe br_netfilter modprobe ip_vs modprobe ip_vs_rr modprobe ip_vs_wrr modprobe ip_vs_sh modprobe nf_conntrack_ipv4在/etc/modules-load.d/k8s.conf里写入这些模块名保证开机自动加载。接着调整/etc/sysctl.d/k8s.confnet.bridge.bridge-nf-call-iptables 1 net.bridge.bridge-nf-call-ip6tables 1 net.ipv4.ip_forward 1 net.ipv6.conf.all.forwarding 1 vm.swappiness 0执行sysctl --system生效。这里有个容易被忽略的点很多国产化环境默认没有br_netfilter模块或者内核里该模块叫别的名字需要先lsmod | grep br_netfilter确认。没有的话后面所有的Service访问都会出问题不是iptables规则不对而是网桥流量根本没走iptables链。3.2 安装containerd并导入离线镜像容器运行时我用的是containerd它是Kubernetes现在默认推荐的运行时比Docker更轻也避开了Docker带来的额外依赖。离线安装containerd很简单rpm包直接装就行。装完先配置默认配置是/etc/containerd/config.toml生成默认配置的命令是containerd config default /etc/containerd/config.toml有两个关键项必须改一是把SystemdCgroup设为true让容器使用systemd管理cgroup否则kubelet会报cgroup驱动不匹配二是修改sandbox_image也就是pause容器镜像地址默认路径是registry.k8s.io/pause:3.9离线环境根本拉不到必须改成内网Registry的地址。然后启动containerd用crictl验证systemctl start containerd crictl images接下来导入Calico和控制面镜像。如果你已经建好了内网Registry最简单的是直接把镜像push上去。但没有Registry的时候逐台导入tar包也能用ctr -n k8s.io images import calico.tar ctr -n k8s.io images import k8s-control-plane.tar注意这里必须带-n k8s.io命名空间containerd里通过CRI创建的镜像都在这个命名空间下。我刚开始就忘带这个参数镜像导入了默认命名空间kubelet照样拉不到。3.3 kubeadm初始化控制节点所有准备就绪后在控制节点上执行kubeadm init。我使用的参数大致如下kubeadm init \ --control-plane-endpoint192.168.1.10:6443 \ --pod-network-cidr10.244.0.0/16 \ --kubernetes-versionv1.28.2 \ --image-repositoryregistry.internal:5000/k8s.io \ --upload-certs--image-repository指向内网Registry里存放Kubernetes镜像的目录。如果你没有Registry这一步会直接卡在拉取镜像上报Failed to pull image registry.k8s.io/kube-apiserver之类的错误。所以建议先把Registry搭起来一步到位后面所有节点都能用。初始化完成后按提示执行以下命令mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config然后查看节点状态这时候节点应该还是NotReady因为没装网络插件属正常现象。如果你的控制节点还需要容忍NoSchedule的污点来承担业务Pod执行kubectl taint nodes --all node-role.kubernetes.io/control-plane-3.4 部署Calico的正确顺序很多文档让你一上来就apply Calico但我觉得顺序很重要先把控制面Pod确认跑起来再看Calico的日志。直接在控制节点上执行kubectl apply -f calico.yaml为什么强调calico.yaml要提前改好参数我在实际操作中必改的就这么几处。第一处是CALICO_IPV4POOL_CIDR改成和kubeadm init一致的10.244.0.0/16第二处是IP_AUTODETECTION_METHOD写成网卡匹配规则比如interfaceeth.*这样Calico会自动找eth开头的网卡不会误选到其他虚拟网卡第三处是网络模式如果确定用纯BGP模式把CALICO_IPV4POOL_IPIP设成Never。部署完成后通过以下命令观察kubectl get pods -n kube-system -o wide正常的应该看到calico-node-xxxxx全部Runningcalico-kube-controllers-xxxxxRunning节点变成Ready。这个过程中最忌讳的就是日志不看就开始怀疑镜像问题。先看Pod状态再看日志再查路由表按顺序来。3.5 工作节点加入与跨节点验证工作节点上的操作和控制节点差不多准备好环境、装好containerd、导入镜像后在控制节点上生成join命令kubeadm token create --print-join-command把命令复制到工作节点执行。如果初始化时用了--upload-certs高可用控制节点还需要额外传--certificate-key但普通工作节点不需要。加入完成后回到控制节点确认所有节点Ready。然后做一次跨节点通信测试部署一个小测试Pod或者直接用kubectl run nginx --imagenginx然后ping它的Pod IP。这里我建议用两个不同节点上的Pod互相ping能通才算网络真正通了。只在本节点测试没有意义验证不了Calico的BGP转发链路。4. Calico常见故障排查实战4.1 calico-node一直CrashLoopBackOff这是离线部署里出现频率最高的故障。现象是Pod反复重启日志里有明显的错误信息。最常见的原因是自动检测节点IP失败。我遇到过一次节点上有eth0和docker0或一个奇怪的网桥Calico自动选了错误网卡导致Felix起不来。遇到这个问题先看日志kubectl logs -n kube-system calico-node-xxxxx -c calico-node日志里如果出现Failed to auto-detect an IPv4 address或者Unable to detect IP address from interface那就实锤是网卡识别问题。解法就是手动指定- name: IP_AUTODETECTION_METHOD value: interfaceeth.*注意eth.*这个正则要匹配你实际的网卡名如果网卡是ens192、enp0s3正则得改成ens.*或enp.*或者干脆精确指定interfaceens192。改完重新apply让DaemonSet滚动重启。这个坑在国产化机器上尤其常见因为部分发行版网卡命名规则不太一样。4.2 跨节点Pod IP不通节点都Ready了但跨节点的Pod ping不通。这类问题的排查顺序很固定先确认路由表再看BGP会话最后查防火墙。在节点上执行ip route看有没有到Pod网段的路由。如果路由表里压根没有对端Pod网段说明BGP路由没学到。继续用calicoctl检查calicoctl node status这个命令会显示当前节点和其他节点的BGP连接状态。如果显示Connection refused多半是BGP端口179被防火墙挡了或者节点之间网络不通。如果显示Neighbor received AS number mismatch那是AS号配置不一致。Calico默认所有节点在同一个AS用默认配置就行但如果手动改了AS号一定要确保全局一致。还有一种情况是路由有了但隧道不通。如果确认使用的是IPIP模式检查物理网卡MTU两端MTU不一致会导致包被丢弃。测试方法是ping -M do -s 1472 对端PodIP能通说明路由和封装没问题不通的话重点查MTU。4.3 节点始终NotReady节点NotReady的原因很多但排除Kubelet问题后最常见的还是CNI配置问题。先看节点事件kubectl describe node node-name如果事件里有Failed to create pod sandbox或者network plugin not ready那几乎可以确定是Calico的CNI插件配置没生效。登录节点查看/etc/cni/net.d/目录应该有Calico生成的配置文件10-calico.conflist。如果这个文件缺失检查calico-node容器的日志通常是cni插件安装失败。手动补齐的方式是把Calico提供的cni二进制文件拷贝到/opt/cni/bin/重新创建Pod。4.4 常见问题速查表症状可能原因排查命令处理措施calico-node CrashLoopBackOff节点IP自动检测失败kubectl logs -n kube-system calico-node-xxx设置IP_AUTODETECTION_METHOD指定网卡跨节点Pod不通BGP会话未建立calicoctl node status、ip route检查179端口、检查AS号配置节点NotReadyCNI插件未安装或配置缺失kubectl describe node、查看/etc/cni/net.d/手动部署cni插件、重启containerdPod一直Pending没有网络插件或调度问题kubectl describe pod确认calico已apply、检查污点容忍Service无法访问kube-proxy未就绪或ipvs模块缺失kubectl logs -n kube-system kube-proxy-xxx确认ip_vs模块加载、切换iptables模式大包不通小包通MTU配置不一致ping -M do -s 1472统一Calico MTU和物理网卡配置4.5 回滚与恢复Calico版本升级或者配置改崩了怎么办我的建议是把当前配置备份下来出问题快速回滚。Calico的配置大部分存在Kubernetes的CRD里用kubectl get felixconfiguration -o yaml felix-backup.yaml可以备份。如果只是改坏了某个节点上的CNI配置重启节点上的calico-node容器通常能自动恢复kubectl delete pod -n kube-system -l k8s-appcalico-nodeDaemonSet会自动重新拉起Pod。但如果是BGP配置或者全局Felix配置改坏了回滚就麻烦一些建议在改动前对etcd做快照ETCDCTL_API3 etcdctl --cacert/etc/kubernetes/pki/etcd/ca.crt \ --cert/etc/kubernetes/pki/etcd/server.crt \ --key/etc/kubernetes/pki/etcd/server.key \ snapshot save /tmp/etcd-snapshot.db有了快照再坏的配置都能恢复到快照时间点这是最后的保命手段。5. 国产化ARM环境与安全加固的补充记录5.1 飞腾ARM架构与国产操作系统适配随着国产化推进我后面又在一批飞腾ARM64架构、银河麒麟系统的机器上做了一次类似的部署对比之前x86环境最大的差异集中在镜像架构、内核模块和基础工具链上。镜像是第一道坎。x86上正常拉取的镜像在ARM节点上根本跑不起来日志会报exec format error。解决办法是提前准备好ARM64架构的镜像。检查镜像架构的一个直接方法是用crictl或skopeo查看manifestskopeo inspect docker://registry.internal:5000/k8s.io/pause:3.9 | grep -i architecture更严格的做法是在打镜像包时用docker buildx生成multi-arch的manifest列表推送到内网Registry后任何架构的节点都能自动拉取到对应平台镜像。国产化环境里的kubeadm、etcd、calicoctl、crictl这些二进制工具也要找对应的arm64版本不能直接拷贝x86的用我在实操中专门整理过一个工具和镜像的版本对照清单。第二道坎是内核模块。麒麟默认安装的内核可能没有编译nf_conntrack_ipv4这类模块加载时会报错。换个思路确认模块是否编译为内核模块文件但未自动加载手动modprobe一下看看。实在没有就得改装内核或者用nf_conntrack替代同时注意iptables的兼容性。第三道坎是基础依赖。部分国产系统基于较老的发行版制作glibc版本低导致新版本的Kubernetes二进制跑不起来。这时候最好的办法是选择和系统相匹配的Kubernetes旧版本或者使用系统自带仓库里的老版本工具。不建议强行升级系统库那可能引发更多兼容性问题。5.2 API Server未授权访问防护离线环境容易给人一种“内网安全”的错觉但内网不等于安全。近两年各类未授权访问事件里Kubernetes API Server是重灾区原因就是6443端口暴露在大网段里而--anonymous-auth还是默认开启或者RBAC配置过于宽松。部署时建议在/etc/kubernetes/manifests/kube-apiserver.yaml里强制加上几个参数- --anonymous-authfalse - --authorization-modeRBAC - --service-account-lookuptrue - --enable-admission-pluginsNamespaceLifecycle,LimitRanger,ServiceAccount,NodeRestriction这些参数可以在kubeadm init时通过apiServer配置传入。节点层面再配合iptables只允许内网管理网段访问6443效果会好很多。还有个容易被忽略的点是calicoctl的证书权限我见过有人把calicoctl的kubeconfig权限设成了cluster-admin建议用一个只读或仅限calico命名空间的自定义RBAC角色。5.3 离线集群的升级与巡检建议集群装完不是终点。离线环境的升级比在线环境麻烦很多因为版本对应的镜像、二进制都要重新准备一遍。我的经验是每半年做一次版本升级升级前务必看Kubernetes和Calico的版本兼容矩阵Calico官方文档有明确的兼容性表格用不对版本会导致API对象不兼容网络策略完全失效。巡检方面日常需要盯的就三样etcd备份是否在跑、节点磁盘使用率、Calico Felix的日志错误量。前两个好理解第三个是我吃过亏的Felix偶尔会因为IP冲突或者iptables规则更新失败报错但集群表面上看一切正常直到某个Pod跨节点访问突然断了才发现。建议用kubectl logs -n kube-system -l k8s-appcalico-node --tail50定期查看日志里出现ERROR级别的内容就要及时处理。6. 最后再分享一点实战心得这个项目做下来我最深的体会是离线部署的坑90%都不在Kubernetes本身而在“环境差异”。同样的Calico配置文件在Ubuntu上一切正常换到国产化系统上就自动检测错网卡同样的MTU设置在一台机器的VLAN里没问题换一个网络环境就开始丢包。所以我在整个部署过程中养成了一个习惯任何一次操作前先把环境基线摸清楚——网卡名、MTU、内核版本、系统发行版、路由表、防火墙规则全部列出来再动手。磨刀不误砍柴工这个习惯帮我省了很多深夜排错的精力。再分享一个小技巧离线部署时一定要准备一个“救急U盘”里面不光有安装包还要有这些工具的副本crictl、etcdctl、calicoctl、一个精简版的registry镜像、一个busybox镜像。别小看这个U盘很多故障发生的时候你没有外网可以现下工具这一个小U盘可能救你一次。这套流程跑通以后后续再给别的环境部署就变成流水线了物料打包、Registry初始化、kubeadm init、Calico调参、节点加入、跨节点验证。每一个环节都有固定检查点和固定命令。如果你正准备做离线部署建议先在虚拟机里把这个流程完整跑一遍尤其是Calico的网卡自动检测和MTU这两个坑提前踩一遍比在生产上踩好得多。
返回列表