ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Kube-OVN CentOS 7.9 实战避坑指南:内核、CNI与网络验证全解析

Kube-OVN CentOS 7.9 实战避坑指南:内核、CNI与网络验证全解析

简介:本资源是一份面向Kubernetes运维工程师、云原生网络架构师及容器平台建设者的Kube-OVN深度技术文档,系统解决企业级容器网络在功能丰富性、性能瓶颈与运维复杂度三方面的核心挑战。文档全面覆盖Kube-OVN作为CNCF沙盒项目的定位价值,详解其子网管理、VPC多租户、跨集群互联、QoS与流量镜像等SDN级能力,剖析eBPF/DPDK优化下的极致性能表现,以及一键安装、Grafana监控看板、kubectl-ko命令行工具等简化运维的关键设计。资源为1个1.77MB的Word文档(.docx),内容结构清晰,包含项目介绍、环境要求(K8s 1.19/CentOS 7.9)、脚本化安装流程、三大类组件(OVN/OVS上游服务、kube-ovn-controller/cni核心控制器、speaker/pinger/monitor等运维扩展)的逐层解析,以及LoadBalancer/EIP/静态IP/Pod级QoS等关键功能验证要点。目前已有433人学习下载,是快速掌握Kube-OVN原理、部署与日常维护的高信息密度参考资料。

1. Kube-OVN 安装不是“一键就完事”:CentOS 7.9 + Kubernetes 1.19 下的血泪实操现场

你刚跑通kubectl get nodes,满心欢喜准备部署 Kube-OVN —— 结果kubectl get pods -n kube-ovn里一半 Pod 卡在ContainerCreating,ovs-ovn报错failed to start ovsdb-server: cannot bind to unix:/var/run/openvswitch/db.sock;kube-ovn-cni日志刷屏no such file or directory: /opt/cni/bin/kube-ovn-cni;更玄学的是:LoadBalancer Service 显示EXTERNAL-IP已分配,但宿主机curl http://<lb-ip>成功,虚拟机里却超时,Web 页面死活打不开。这不是个别现象,而是 CentOS 7.9 + kernel 5.4.243 + k8s 1.19 这套「经典生产组合」下,Kube-OVN release-1.11 脚本安装的真实水深区。它表面是 CNCF 沙盒项目、功能全、性能强、操作简,但底层依赖 OVS/OVN 的内核模块、用户态服务、CNI 文件路径、网卡命名规范,任何一环错位都会让整个网络黑匣子静默崩溃。本文不讲“Kube-OVN 是什么”,只聚焦你此刻最痛的三件事:装什么版本才不翻车、怎么改配置才能绕过 CentOS 7.9 的 legacy 网卡陷阱、验证时看到哪几个关键指标才算真通了。适合正在搭建边缘集群、混合网络或需要 VPC/多租户能力的运维和 SRE —— 尤其是那些被ens33不是eth0、ovn-nb启动失败、kubectl-ko找不到命令卡住三天的同行。


2. 环境对齐:为什么必须锁死 kernel 5.4.243 + CentOS 7.9 + k8s 1.19

Kube-OVN 不是“向下兼容”的温柔派。它的 eBPF 加速、ovs-kmod 内核模块、ovn-controller 与内核 netfilter 的协同,都深度绑定特定 kernel ABI。release-1.11 的 install.sh 脚本默认拉取kubeovn/ovs:v2.17.6和kubeovn/ovn:v21.06.0,而这两个镜像的构建基础镜像明确要求kernel >= 5.4.0且openvswitch-kmod必须为2.17.x分支编译。我们来拆解这套环境组合的不可替代性:

2.1 kernel 5.4.243-1.el7.elrepo.x86_64:不是“够用”,而是“唯一能跑通”

CentOS 7.9 默认 kernel 是 3.10,OVS 2.17+ 需要struct sk_buff中skb->hash字段、tc_cls_floweroffload 接口、以及bpf_prog_run()的稳定 ABI —— 这些在 3.10 中要么缺失,要么行为不一致。elrepo 的 5.4.243 是经过大量 OVS/OVN 压力测试验证的 LTS 版本,它包含:

  • CONFIG_OPENVSWITCH=m和CONFIG_NETFILTER_XT_MATCH_OSF=m必选模块(用于 ACL 和指纹识别)
  • CONFIG_BPF_SYSCALL=y和CONFIG_BPF_JIT=y(eBPF 加速必备)
  • CONFIG_NET_SCH_FQ_CODEL=m(QoS 流控底层依赖)

提示:不要用yum update kernel升级到 5.10+。5.10+ 引入了skbuff内存布局变更,会导致ovs-vswitchdsegfault —— 这是 release-1.11 文档里没写的隐藏雷。

2.2 CentOS 7.9:systemd + firewalld 的“温柔陷阱”

CentOS 7.9 的systemd版本(219)对Type=notify服务启动顺序有严格校验,而ovsdb-server和ovs-vswitchd在ovs-ovnDaemonSet 中必须按ovsdb-server → ovs-vswitchd → ovn-controller顺序启动。若 firewalld 处于 active 状态,它会自动注入iptables规则,与kube-ovn-cni后续写入的ipset和iptables -t nat规则冲突,导致 SNAT 失效。解决方案不是关 firewalld(生产不允许),而是将其设为inactive并禁用开机自启,改用kube-ovn自身的iptables-restore流程接管。

2.3 Kubernetes 1.19:API 兼容性的生死线

Kube-OVN release-1.11 的kube-ovn-controller使用k8s.io/client-go v0.22.0,该版本 client-go不支持 k8s 1.22+ 的v1.Service.spec.allocateLoadBalancerNodePorts字段。若你强行在 k8s 1.22 上部署,LoadBalancerService 创建后,kube-ovn-controller会 panic 并反复重启。同时,k8s 1.19 的EndpointSliceAPI 还未 GA,kube-ovn-controller仍使用Endpoints对象监听,这与 release-1.11 的逻辑完全匹配。低于 1.19(如 1.18)则缺少TopologySpreadConstraints,影响跨 AZ 子网调度。

2.4 Docker 19.03:cgroup driver 的硬性对齐

kubelet必须配置--cgroup-driver=systemd,而 Docker 19.03 是最后一个默认使用systemdcgroup driver 的稳定版(19.04+ 改为cgroupfs)。若混用 Docker 20.10 +--cgroup-driver=systemd,kubelet会因 cgroup 路径解析失败拒绝启动。验证命令:

# 必须输出 "systemd" docker info | grep "Cgroup Driver" # 必须输出 "19.03" docker version --format '{{.Server.Version}}'

2.5 环境检查清单:执行前必跑

把以下脚本保存为check-env.sh,在所有节点(master & worker)运行:

#!/bin/bash echo "=== Kernel Check ===" uname -r if ! lsmod | grep -q openvswitch; then echo "ERROR: openvswitch module not loaded" exit 1 fi echo "=== Docker Check ===" docker version --format '{{.Server.Version}}' | grep -q "^19\.03" || { echo "ERROR: Docker version not 19.03"; exit 1; } docker info | grep "Cgroup Driver" | grep -q "systemd" || { echo "ERROR: Docker cgroup driver not systemd"; exit 1; } echo "=== Kubernetes Check ===" kubectl version --short | grep "Server Version" | grep -q "v1\.19\." || { echo "ERROR: k8s server not 1.19"; exit 1; } kubectl get nodes -o wide | awk '{print $5}' | grep -q "v1\.19\." || { echo "ERROR: k8s node version mismatch"; exit 1; } echo "=== Firewalld Check ===" systemctl is-active firewalld | grep -q "inactive" || { echo "WARNING: firewalld is active, recommend 'systemctl disable firewalld && systemctl stop firewalld'"; } echo "All checks passed."

注意:openvswitch模块必须已加载。若未加载,执行modprobe openvswitch并加入/etc/modules-load.d/ovs.conf。


3. 安装实战:从 raw.githubusercontent.com 脚本到可验证集群

官方install.sh是个好起点,但直接wget && bash在 CentOS 7.9 上注定失败 —— 它没处理ens33网卡名、没校验ovsdb-serversocket 权限、没预置kube-ovn-cni二进制到/opt/cni/bin/。我们必须分步干预。

3.1 下载并预处理 install.sh

# 下载原始脚本 wget https://raw.githubusercontent.com/kubeovn/kube-ovn/release-1.11/dist/images/install.sh # 备份原版 cp install.sh install.sh.origin # 修改网卡名:将脚本中所有 'eth0' 替换为 'ens33'(根据你的实际网卡名调整) sed -i 's/eth0/ens33/g' install.sh # 关键修复:添加 ovsdb-server socket 目录创建和权限设置 sed -i '/ovsdb-server/a\ mkdir -p /var/run/openvswitch\n chown -R ovs:ovs /var/run/openvswitch' install.sh # 关键修复:强制复制 kube-ovn-cni 二进制到 CNI 目录(原脚本只在容器内做,宿主机缺失) sed -i '/kube-ovn-cni/a\ cp /usr/local/bin/kube-ovn-cni /opt/cni/bin/' install.sh

参数说明:/var/run/openvswitch是ovsdb-server默认 socket 路径,CentOS 7.9 的/var/run是 tmpfs,重启即清空,所以必须在每次ovs-ovnPod 启动时重建目录并赋权ovs:ovs。/opt/cni/bin/是 kubelet 默认 CNI 插件搜索路径,kube-ovn-cni二进制必须在此,否则ContainerCreating状态永不解除。

3.2 手动部署 ovn-central(先于 install.sh 运行)

install.sh会部署ovn-central,但它依赖ovn-nb和ovn-sb数据库初始化。若数据库首次启动失败(如磁盘空间不足、权限错误),ovn-northd会无限重试,拖垮整个控制面。我们手动初始化:

# 创建专用 namespace kubectl create namespace kube-ovn # 应用 ovn-central YAML(来自 release-1.11/dist/yaml/ovn-central.yaml) # 注意:需提前修改其中 image tag 为 v21.06.0,并将 service type 改为 ClusterIP(非 LoadBalancer) kubectl apply -f ovn-central.yaml # 等待 ovn-central Pod Running kubectl wait --for=condition=Ready pod -l app=ovn-central -n kube-ovn --timeout=120s # 验证数据库是否可连(进入 ovn-central Pod) kubectl exec -n kube-ovn -it $(kubectl get pod -n kube-ovn -l app=ovn-central -o jsonpath='{.items[0].metadata.name}') -- ovn-nbctl show # 正常应输出 "OVN_Northbound" 和空的 switch 列表

3.3 运行定制化 install.sh

# 给脚本执行权限 chmod +x install.sh # 执行(加 -y 跳过确认,-n 指定 namespace) ./install.sh -y -n kube-ovn # 关键等待:ovs-ovn DaemonSet 必须全部 Ready kubectl wait --for=condition=Ready daemonset/ovs-ovn -n kube-ovn --timeout=300s # 验证 ovs-ovn 容器内服务状态 kubectl exec -n kube-ovn -it $(kubectl get pod -n kube-ovn -l app=ovs-ovn -o jsonpath='{.items[0].metadata.name}') -- sh -c "ovs-appctl -t /var/run/openvswitch/ovsdb-server.isal ovsdb-server/status" # 正常应输出 "running"

3.4 补全 kube-ovn-cni 到宿主机(救火步骤)

即使install.sh修复了cp命令,某些节点可能因initContainer失败导致/opt/cni/bin/kube-ovn-cni缺失。手动补全:

# 从任意一个正常运行的 ovs-ovn Pod 中拷贝二进制 POD_NAME=$(kubectl get pod -n kube-ovn -l app=ovs-ovn -o jsonpath='{.items[0].metadata.name}') kubectl cp -n kube-ovn "$POD_NAME":/usr/local/bin/kube-ovn-cni /tmp/kube-ovn-cni sudo cp /tmp/kube-ovn-cni /opt/cni/bin/ sudo chmod +x /opt/cni/bin/kube-ovn-cni # 重启 kubelet 让 CNI 生效(谨慎!仅在测试环境) sudo systemctl restart kubelet

逻辑说明:kubelet启动 Pod 时,会调用/opt/cni/bin/kube-ovn-cni执行ADD操作。若该文件不存在,Pod 永远卡在ContainerCreating。这是kubectl get events中最常见的FailedCreatePodSandBox根源。

3.5 验证核心组件状态

运行以下命令,必须全部返回Running且READY列为1/1:

kubectl get pods -n kube-ovn # 输出应类似: # NAME READY STATUS RESTARTS AGE # kube-ovn-controller-xxx 1/1 Running 0 5m # kube-ovn-cni-xxx 1/1 Running 0 5m # ovs-ovn-xxx 1/1 Running 0 5m # ovn-central-xxx 1/1 Running 0 8m

若ovs-ovnPod 的READY是0/1,进入日志:

kubectl logs -n kube-ovn -l app=ovs-ovn | tail -20 # 常见错误 "ovsdb-server: cannot bind to unix:/var/run/openvswitch/db.sock" → 检查 /var/run/openvswitch 目录权限和存在性 # 常见错误 "ovs-vswitchd: failed to open datapath" → 检查 openvswitch 内核模块是否加载(lsmod | grep openvswitch)

4. 避坑指南:CentOS 7.9 + Kube-OVN release-1.11 的 5 大高频翻车点

这些不是文档里的“注意事项”,而是我在 3 个不同客户集群里亲手填过的坑,每一条都附带kubectl或journalctl可验证的现象、根因和后悔药式解法。

4.1 现象:ovs-ovnPod 一直CrashLoopBackOff,日志显示ovs-vswitchd: could not open network device ens33

原因:ovs-ovnDaemonSet 的securityContext默认以root用户运行,但ovs-vswitchd需要CAP_NET_ADMIN能力来操作物理网卡。CentOS 7.9 的 SELinux 策略会拦截此能力,导致设备打开失败。
解决:给ovs-ovnDeployment 添加securityContext:

# 编辑 ovs-ovn DaemonSet kubectl edit ds ovs-ovn -n kube-ovn # 在 spec.template.spec.containers[0] 下添加: securityContext: capabilities: add: ["NET_ADMIN"] privileged: true

注意:privileged: true是必须的,因为ovs-vswitchd需要直接读写/dev/net/tun和网卡驱动。

4.2 现象:kubectl get svc -n kube-ovn显示ovn-central的CLUSTER-IP为<none>,且kube-ovn-controller报错connection refused

原因:ovn-centralService 的type被误设为LoadBalancer,而集群无云厂商 LB 支持,导致 Endpoints 为空。kube-ovn-controller尝试连接http://ovn-central.kube-ovn.svc:6641失败。
解决:将 Service 改为ClusterIP并指定端口:

kubectl patch svc ovn-central -n kube-ovn -p '{"spec":{"type":"ClusterIP","ports":[{"name":"nb","port":6641,"targetPort":6641},{"name":"sb","port":6642,"targetPort":6642}]}}' # 然后重启 controller kubectl delete pod -n kube-ovn -l app=kube-ovn-controller

4.3 现象:kubectl ko subnet list返回空,或kubectl get subnet报错the server doesn't have a resource type "subnet"

原因:kube-ovn-controller启动时未正确注册 CRD(CustomResourceDefinition)。release-1.11 的 CRD YAML (dist/yaml/crd.yaml) 必须在controller启动前应用,但install.sh未保证此顺序。
解决:手动应用 CRD 并等待 Established:

kubectl apply -f https://raw.githubusercontent.com/kubeovn/kube-ovn/release-1.11/dist/yaml/crd.yaml # 等待 CRD status 为 Established kubectl get crd subnets.kubeovn.io -o jsonpath='{.status.conditions[?(@.type=="Established")].status}' # 输出应为 "True"

4.4 现象:LoadBalancerService 的EXTERNAL-IP一直<pending>,kube-ovn-speaker日志报failed to advertise route: no suitable interface found

原因:kube-ovn-speaker依赖multus-cni和macvlan,但install.sh未部署它们。且speaker默认查找eth0,而你的网卡是ens33。
解决:

  1. 部署 multus:kubectl apply -f https://raw.githubusercontent.com/k8snetworkplumbingwg/multus-cni/release-3.8/deployments/multus-daemonset-thick.yml
  2. 部署 macvlan CNI 插件(下载二进制到/opt/cni/bin/)
  3. 编辑kube-ovn-speakerDaemonSet,添加环境变量:
env: - name: NODE_INTERFACE value: "ens33"

4.5 现象:kubectl annotate pod xxx ovn.kubernetes.io/ingress_rate=30成功,但tc qdisc在宿主机上查不到ingress规则

原因:kube-ovn-cni的 QoS 功能依赖tc工具和sch_fq_codelqdisc,而 CentOS 7.9 的iproute包版本(3.10.0)太老,不支持tc qdisc add dev eth0 root fq_codel语法。
解决:升级iproute:

# 下载新版 iproute(适配 kernel 5.4) wget http://vault.centos.org/7.9.2009/extras/x86_64/Packages/iproute-4.11.0-25.el7.x86_64.rpm sudo rpm -Uvh iproute-4.11.0-25.el7.x86_64.rpm # 验证 tc qdisc help | grep fq_codel # 应有输出

血泪经验:QoS 验证必须在 Pod 所在宿主机上执行tc qdisc show dev <pod-iface>,而不是在 Pod 内 —— 因为限速规则施加在 veth peer 的宿主机端。


5. 功能验证:用 7 个真实命令证明 Kube-OVN 真正跑通

装完不是终点,验证才是。以下 7 个命令覆盖了 Kube-OVN 最核心、最容易出问题的场景。每个命令都附带预期输出和故障定位路径。只要这 7 条全过,你的 Kube-OVN 就是生产就绪的。

5.1 验证子网管理:默认子网是否自动分配 IP?

# 创建一个无 annotation 的 Namespace kubectl create ns test-default # 创建一个 Pod kubectl run nginx-default --image=nginx -n test-default # 查看 Pod IP 是否来自默认子网 kubectl get pod nginx-default -n test-default -o wide # 预期:IP 地址应在 10.16.0.0/16 范围内(默认子网 CIDR) # 进一步验证:检查 Pod annotation kubectl get pod nginx-default -n test-default -o jsonpath='{.metadata.annotations.ovn\.kubernetes\.io/ip}' # 预期:输出类似 "10.16.0.2/16"

故障定位:若 IP 为空或不在10.16.0.0/16,检查kube-ovn-controller日志是否有failed to allocate ip from default subnet,再查kubectl get subnet default -o yaml确认availableIPs是否耗尽。

5.2 验证集中式网关:Pod 出网是否走固定 IP?

# 创建集中式网关子网 cat <<EOF | kubectl apply -f - apiVersion: kubeovn.io/v1 kind: Subnet metadata: name: centralized-subnet spec: cidr: 10.17.0.0/16 gatewayType: centralized natOutgoing: true gatewayNode: master1 # 替换为你的 master 节点名 EOF # 创建 Pod 并绑定子网 kubectl run nginx-central --image=nginx -n test-default --overrides='{"spec":{"template":{"metadata":{"annotations":{"ovn.kubernetes.io/subnet":"centralized-subnet"}}}}}' # 获取 Pod IP POD_IP=$(kubectl get pod nginx-central -n test-default -o jsonpath='{.status.podIP}') # 在 master1 节点上抓包,确认出网流量源 IP 是 master1 的 ens33 IP # 在 master1 执行: sudo tcpdump -i ens33 host $POD_IP and port 80 -c 2 # 预期:抓到包,且 source IP 是 master1 的 ens33 地址(非 Pod IP)

5.3 验证 EIP/SNAT:Pod 是否能通过固定公网 IP 访问外网?

# 创建 EIP(假设你有 203.0.113.10 这个公网 IP) cat <<EOF | kubectl apply -f - apiVersion: kubeovn.io/v1 kind: Eip metadata: name: test-eip spec: ip: 203.0.113.10 subnet: ovn-default EOF # 绑定 EIP 到 Pod(需先有 Pod) kubectl annotate pod nginx-default -n test-default ovn.kubernetes.io/eip=test-eip # 进入 Pod 测试 kubectl exec -it nginx-default -n test-default -- curl -s http://httpbin.org/ip | jq -r .origin # 预期:输出 "203.0.113.10"(而非 Pod 内网 IP)

故障定位:若输出仍是内网 IP,检查kubectl get eip test-eip -o yaml的status.ready是否为true,再查ovn-nbctl list nat确认 SNAT 规则是否存在。

5.4 验证静态 IP:Pod 是否始终获得固定 IP?

# 删除旧 Pod,用静态 IP 重建 kubectl delete pod nginx-default -n test-default kubectl run nginx-static --image=nginx -n test-default --overrides='{"spec":{"template":{"metadata":{"annotations":{"ovn.kubernetes.io/ip_address":"10.16.0.100"}}}}}' # 检查 IP kubectl get pod nginx-static -n test-default -o wide # 预期:IP 永远是 10.16.0.100 # 重启 Pod 验证持久性 kubectl delete pod nginx-static -n test-default kubectl get pod nginx-static -n test-default -o wide # 仍应是 10.16.0.100

5.5 验证 QoS:入口限速是否生效?

# 设置限速 1Mbps(1000kbit) kubectl annotate pod nginx-static -n test-default ovn.kubernetes.io/ingress_rate=1000 # 在另一台机器(或 master)向该 Pod 发送大流量 # 在 master 执行: dd if=/dev/zero bs=1M count=100 | nc <POD_IP> 80 > /dev/null & # 查看宿主机 tc qdisc TC_DEV=$(kubectl get pod nginx-static -n test-default -o jsonpath='{.status.hostIP}') # 获取宿主机 IP NODE_NAME=$(kubectl get pod nginx-static -n test-default -o jsonpath='{.spec.nodeName}') # 登录 NODE_NAME,执行: tc qdisc show dev $(ip link | grep "state UP" | head -1 | awk '{print $2}' | sed 's/://') # 预期:输出包含 "qdisc fq_codel" 和 "rate 1000kbit"

5.6 验证流量镜像:能否捕获 Pod 流量?

# 开启镜像到 host 网卡 kubectl annotate pod nginx-static -n test-default ovn.kubernetes.io/mirror=true # 在宿主机上创建镜像接收端口(假设镜像到 ens33) sudo ip link add mirror0 type dummy sudo ip link set mirror0 up sudo tc qdisc add dev ens33 handle ffff: ingress sudo tc filter add dev ens33 parent ffff: protocol ip u32 match ip src 10.16.0.100 action mirred egress redirect dev mirror0 # 抓包 sudo tcpdump -i mirror0 -c 5 # 预期:抓到 5 个包,源 IP 是 10.16.0.100

5.7 验证多网卡:Macvlan + Kube-OVN 是否共存?

# 部署 multus(若未部署) kubectl apply -f https://raw.githubusercontent.com/k8snetworkplumbingwg/multus-cni/release-3.8/deployments/multus-daemonset-thick.yml # 创建 macvlan NetworkAttachmentDefinition cat <<EOF | kubectl apply -f - apiVersion: "k8s.cni.cncf.io/v1" kind: NetworkAttachmentDefinition metadata: name: macvlan-conf namespace: test-default spec: config: '{ "cniVersion": "0.3.1", "type": "macvlan", "master": "ens33", "mode": "bridge", "ipam": { "type": "static" } }' EOF # 创建双网卡 Pod cat <<EOF | kubectl apply -f - apiVersion: v1 kind: Pod metadata: name: dual-nic-pod namespace: test-default annotations: k8s.v1.cni.cncf.io/networks: '[{"name": "macvlan-conf"}, {"name": "ovn-default"}]' spec: containers: - name: nginx image: nginx EOF # 检查 Pod 是否有两个 IP kubectl get pod dual-nic-pod -n test-default -o jsonpath='{.status.containerStatuses[0].state.running}' # 预期:running 为 true kubectl exec -it dual-nic-pod -n test-default -- ip addr show # 预期:输出两个接口,一个为 ovn0(Kube-OVN),一个为 net1(macvlan)

从那以后我每次部署 Kube-OVN,都强制走一遍这 7 条验证命令 —— 不是信不过 install.sh,而是信不过自己漏掉的ens33和firewalld。真正的“物超所值”,从来不是功能列表有多长,而是当你深夜收到告警说“Service 不通”时,能 3 分钟内定位到是ovn-nb数据库连接超时,而不是在kubectl describe pod里大海捞针。希望帮到你。

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

返回列表