
一、场景引入
你刚把应用从虚拟机迁移到 Kubernetes,Pod 跑起来了,服务也部署了,但前端调用后端 API 却超时了。你排查了半天,发现 Pod IP 每次重建都会变,Service 名称解析时有时无,跨命名空间的访问更是莫名其妙地不通——这不是个例,这是每个 K8s 初学者都会遇到的"网络三座大山"。
Kubernetes 网络是整个集群的神经系统,CNI 插件决定了 Pod 如何通信,Service 机制决定了应用如何发现彼此,而 CoreDNS 则是这一切的名字解析基础。理解这三者的协作原理,是从"能部署"到"能运维"的关键跨越。
本文将带你从 CNI 插件选型、Service 四种类型、CoreDNS 工作原理到 Ingress 流量入口,逐一拆解 K8s 网络的核心知识,让你彻底搞清楚集群内的流量到底是怎么走的。
二、核心知识点详解
知识点1:CNI 插件——Pod 网络的底层引擎
【概念】CNI(Container Network Interface)是 Kubernetes 的网络插件接口规范,定义了容器运行时如何调用网络插件来为 Pod 配置网络。每个 Pod 启动时,kubelet 会调用 CNI 插件为其创建网络命名空间、分配 IP、设置路由。
【原理】Kubernetes 网络模型要求每个 Pod 拥有独立 IP,且所有 Pod 之间可以直接通信(无 NAT)。CNI 插件就是实现这个模型的"施工队"——它在每个节点上创建网桥或隧道,把本地 Pod 的流量转发到其他节点。主流方案有两类:Overlay 网络(如 Flannel VXLAN)在现有网络上封装隧道,简单但有一定开销;路由模式(如 Calico BGP)直接用路由表转发,性能更好但配置更复杂。
【实操】查看当前集群使用的 CNI 插件:
# 查看 CNI 配置文件$ ls /etc/cni/net.d/10-calico.conflist calico-kubeconfig# 查看 CNI 插件二进制$ ls /opt/cni/bin/bandwidth calico dhcp firewall host-local loopback portmap tuning# 查看 Calico 版本和状态$ calicoctl versionClient Version: v3.27.0Cluster Version: v3.27.0# 查看 Pod 网络分配情况$ kubectl get pods -n kube-system -l k8s-app=calico-nodeNAME READY STATUS RESTARTS AGEcalico-node-abc12 1/1 Running 0 5d
【应用】
小型测试集群:使用 Flannel VXLAN,一条命令部署,零配置启动
生产环境:使用 Calico BGP 模式,网络性能接近物理网络,支持 NetworkPolicy
混合云场景:使用 Cilium,基于 eBPF 实现高性能网络和安全可观测性
【踩坑】
⚠️ 切换 CNI 插件时必须重建所有 Pod!CNI 配置变更后,已有 Pod 的网络命名空间不会自动更新。正确做法:先驱逐节点上的 Pod → 更新 CNI 配置 → 重启 kubelet → 验证新 Pod 网络正常 → 逐节点滚动操作。
知识点2:Service 四种类型——流量的入口与出口
【概念】Service 是 Kubernetes 中将一组 Pod 暴露为网络服务的抽象。它通过 Label Selector 关联后端 Pod,并提供稳定的虚拟 IP(ClusterIP)和 DNS 名称,让客户端无需关心 Pod IP 的变化。
【原理】Service 的核心是 iptables/ipvs 规则。当流量到达 Service 的 ClusterIP 时,kube-proxy 在内核层面将请求随机(或按策略)转发到某个后端 Pod。四种类型逐层扩展:ClusterIP 仅集群内可达;NodePort 在每个节点开端口暴露;LoadBalancer 调用云厂商 API 创建外部负载均衡器;ExternalName 返回 CNAME 记录做 DNS 重定向。
【实操】创建并验证各类型 Service:
# ClusterIP(默认类型)$ kubectl expose deployment nginx --port=80 --target-port=8080 --name=nginx-svc --dry-run=client -o yamlapiVersion: v1kind: Servicemetadata:name: nginx-svcspec:type: ClusterIP # 集群内访问selector:app: nginxports:- port: 80targetPort: 8080# NodePort 类型$ kubectl expose deployment nginx --port=80 --target-port=8080 --type=NodePort --name=nginx-nodeport$ kubectl get svc nginx-nodeportNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEnginx-nodeport NodePort 10.96.142.18 <none> 80:31204/TCP 5s# 任意节点 IP:31204 即可访问# 查看后端 Endpoints$ kubectl get endpoints nginx-svcNAME ENDPOINTS AGEnginx-svc 10.244.1.5:8080,10.244.2.3:8080,... 2m
【应用】
微服务内部调用:使用 ClusterIP,服务间通过 DNS 名称互访
开发调试暴露服务:使用 NodePort,快速从外部访问
生产对外服务:使用 LoadBalancer,配合云厂商 ELB/SLB
【踩坑】
💡 NodePort 端口范围默认 30000-32767,可通过 kube-apiserver 的 --service-node-port-range 参数修改。但务必确认防火墙规则已放行对应端口,否则外部无法访问。
知识点3:CoreDNS——集群内的 DNS 解析中枢
【概念】CoreDNS 是 Kubernetes 集群的默认 DNS 服务器,负责为集群内所有 Service 和 Pod 提供名称解析。它替代了早期的 kube-dns,成为 K8s 网络可观测性的关键组件。
【原理】CoreDNS 通过 watch API 监听 Service 和 Endpoints 的变化,实时更新 DNS 记录。普通 Service 解析为 ClusterIP;Headless Service(ClusterIP: None)直接返回后端 Pod IP 列表。Pod 的 DNS 格式为 <pod-ip>.<namespace>.pod.cluster.local。CoreDNS 支持插件化扩展,如 hosts 插件自定义解析、forward 插件转发外部查询。
【实操】验证 CoreDNS 解析:
# 查看 CoreDNS Pod$ kubectl get pods -n kube-system -l k8s-app=kube-dnsNAME READY STATUS AGEcoredns-6d4b5cb45d-abc12 1/1 Running 5d# 在 Pod 内测试 DNS 解析$ kubectl run test --image=busybox:1.36 --rm -it --restart=Never -- nslookup nginx-svc.default.svc.cluster.localServer: 10.96.0.10Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.localName: nginx-svc.default.svc.cluster.localAddress 1: 10.96.142.18 nginx-svc.default.svc.cluster.local# Headless Service 直接返回 Pod IP$ kubectl expose deployment nginx --port=80 --target-port=8080 --cluster-ip=None --name=nginx-headless$ nslookup nginx-headless.default.svc.cluster.localName: nginx-headless.default.svc.cluster.localAddress 1: 10.244.1.5 10-244-1-5.nginx.default.svc.cluster.localAddress 2: 10.244.2.3 10-244-2-3.nginx.default.svc.cluster.local# 自定义 CoreDNS 配置$ kubectl edit configmap coredns -n kube-system# 添加 hosts 插件:# hosts {# 192.168.1.100 myapp.internal# fallthrough# }
【应用】
服务发现:应用通过
<svc>.<ns>.svc.cluster.local互访StatefulSet 场景:Headless Service + StatefulSet 实现稳定的 Pod DNS
混合环境:通过 forward 插件将公司内网域名转发到内部 DNS
【踩坑】
⚠️ CoreDNS 的 ndots 机制!默认搜索域为 5 级(ndots:5),短名称如 my-svc 会先追加 default.svc.cluster.local 等后缀逐级查询,导致多次 DNS 请求。跨命名空间访问务必使用完整 FQDN:my-svc.production.svc.cluster.local,可减少 4 次无效查询。
知识点4:Ingress——集群的流量大门
【概念】Ingress 是 Kubernetes 中管理集群入站 HTTP/HTTPS 流量的 API 对象,它根据域名和路径将外部流量路由到不同的 Service,实现七层负载均衡。
【原理】Ingress Controller(如 Nginx Ingress、Traefik)是实际运行的逆向代理,它 watch Ingress 资源的变化,动态生成代理配置。流量路径:客户端 → 负载均衡器 → Ingress Controller → Service → Pod。Ingress 支持基于域名的虚拟主机、基于路径的路由、TLS 终止和会话亲和等高级功能。
【实操】部署 Nginx Ingress 并配置路由:
# 安装 Nginx Ingress Controller$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/cloud/deploy.yaml# 创建 Ingress 规则$ cat <<EOF | kubectl apply -f -apiVersion: networking.k8s.io/v1kind: Ingressmetadata:name: my-app-ingressannotations:nginx.ingress.kubernetes.io/rewrite-target: /spec:ingressClassName: nginxtls:- hosts: [app.example.com]secretName: app-tlsrules:- host: app.example.comhttp:paths:- path: /apipathType: Prefixbackend:service:name: api-svcport: {number: 80}- path: /pathType: Prefixbackend:service:name: web-svcport: {number: 80}EOF# 验证 Ingress$ kubectl get ingressNAME CLASS HOSTS ADDRESS PORTS AGEmy-app-ingress nginx app.example.com 34.120.1.2 80,443 1m
【应用】
多域名虚拟主机:一个 Ingress Controller 服务多个域名
蓝绿部署:通过 Ingress 权重调整实现流量灰度切换
API 网关:配合 OAuth2 Proxy 插件实现认证鉴权
【踩坑】
💡 Ingress 的 pathType 很关键!Exact 精确匹配,Prefix 前缀匹配(/api 匹配 /api/v1 但不匹配 /api2),ImplementationSpecific 由 Controller 决定。生产环境务必明确指定,避免路由歧义。
知识点5:kube-proxy 与 iptables/ipvs——流量转发的内核引擎
【概念】kube-proxy 是每个节点上运行的网络代理,负责实现 Service 的负载均衡和流量转发。它支持两种模式:iptables 和 ipvs。
【原理】iptables 模式下,kube-proxy 为每个 Service 生成 NAT 规则链,随机选择后端 Pod。ipvs 模式利用 Linux 内核的 IPVS(IP Virtual Server)模块,支持多种调度算法(轮询、最少连接、源地址哈希等),在大规模集群中规则匹配效率远高于 iptables 的线性遍历。当 Service 数量超过 1000 个时,ipvs 性能优势明显。
【实操】切换 kube-proxy 模式:
# 查看当前模式$ kubectl get configmap kube-proxy -n kube-system -o jsonpath='{.data.configConf}' | grep modemode: ""# 切换到 ipvs 模式$ kubectl get configmap kube-proxy -n kube-system -o yaml | sed 's/mode: ""/mode: "ipvs"/' | kubectl apply -f -# 重启 kube-proxy$ kubectl rollout restart daemonset kube-proxy -n kube-system# 验证 ipvs 规则$ ipvsadm -LnTCP 10.96.142.18:80 rr-> 10.244.1.5:8080 Masq 1 0 0-> 10.244.2.3:8080 Masq 1 0 0# 查看调度算法$ ipvsadm -Ln --schedulerTCP 10.96.142.18:80 rr # rr=RoundRobin
【应用】
默认 iptables:适合小规模集群(Service <500),配置简单
ipvs 模式:生产大规模集群首选,支持多种调度策略
会话保持:设置
service.spec.sessionAffinity: ClientIP实现同一客户端请求到同一 Pod
【踩坑】
⚠️ 切换 ipvs 模式前确保内核模块已加载!执行 modprobe ip_vs ip_vs_rr ip_vs_wrr 并写入 /etc/modules-load.d/ipvs.conf。否则 kube-proxy 回退到 iptables 模式且无报错提示,难以察觉。
三、实战案例:电商系统的 K8s 网络全景配置
需求:某电商系统需部署到 K8s,包含前端 Web、后端 API、数据库三个服务,要求外部通过 HTTPS 访问,内部服务间通过 DNS 通信,数据库使用 Headless Service 保证 Pod DNS 稳定。
方案:Calico BGP 网络 + Ingress HTTPS 终止 + ClusterIP 内部通信 + Headless Service for StatefulSet。
# 1. API Service(集群内通信)apiVersion: v1kind: Servicemetadata:name: api-svcnamespace: productionspec:type: ClusterIPselector:app: apiports:- port: 80targetPort: 8080---# 2. MySQL Headless Service(StatefulSet 专用)apiVersion: v1kind: Servicemetadata:name: mysqlnamespace: productionspec:clusterIP: None # Headlessselector:app: mysqlports:- port: 3306---# 3. Ingress 配置 HTTPS 路由apiVersion: networking.k8s.io/v1kind: Ingressmetadata:name: shop-ingressnamespace: productionannotations:cert-manager.io/cluster-issuer: letsencrypt-prodspec:ingressClassName: nginxtls:- hosts: [shop.example.com]secretName: shop-tlsrules:- host: shop.example.comhttp:paths:- path: /apipathType: Prefixbackend:service:name: api-svcport: {number: 80}- path: /pathType: Prefixbackend:service:name: web-svcport: {number: 80}---# 4. 验证全链路$ kubectl run test --image=busybox:1.36 --rm -it --restart=Never -- wget -qO- http://api-svc.production.svc.cluster.local/health{"status":"ok"}$ kubectl run test --image=busybox:1.36 --rm -it --restart=Never -- nslookup mysql-0.mysql.production.svc.cluster.localName: mysql-0.mysql.production.svc.cluster.localAddress 1: 10.244.3.5
验证结果:外部通过 https://shop.example.com 访问 → Ingress TLS 终止 → 路由到 web-svc 或 api-svc → API 服务通过 DNS api-svc.production 内部通信 → MySQL 通过 Headless Service 的稳定 Pod DNS mysql-0.mysql 连接。全链路端到端打通,流量路径清晰可控。
四、经验总结
最佳实践清单:
CNI 选型先行:测试用 Flannel,生产用 Calico/Cilium,明确后不再轻易更换
Service 命名规范:
<app>-<type>-svc,如user-api-svc,避免歧义跨命名空间必须使用完整 FQDN,避免 ndots 导致多余查询
生产环境 Ingress + cert-manager 自动管理 TLS 证书
Service 数量超过 500 个时,果断切换 ipvs 模式
什么时候用/不用:
用 ClusterIP:微服务内部通信(绝大多数场景)
用 NodePort:临时调试、无云厂商 LB 的裸金属集群
不用 Service 直接暴露 Pod:Pod IP 随时变化,不可靠
不用 iptables 模式:大规模集群(Service >1000)
进阶学习路径:
NetworkPolicy 网络隔离策略 → 多租户安全隔离
Cilium eBPF 数据面 → 可观测性 + 高性能网络
Gateway API → Ingress 的下一代替代,更灵活的路由模型
Service Mesh(Istio/Linkerd)→ 流量治理、熔断、链路追踪
感谢阅读!对您有帮助的话,点亮👍🏻❤️,关注公众号,转发给需要的朋友~ 原创转载请联系授权。
夜雨聆风