Service 与网络
Kubernetes 网络是集群中最重要的基础设施之一。它解决了容器化应用之间如何通信的问题,让 Pod 无论运行在哪个节点上,都能够相互访问。Service 则为这组动态变化的 Pod 提供了稳定的访问入口,是 Kubernetes 实现服务发现和负载均衡的核心机制。
理解 Kubernetes 网络,需要从两个层面来看:首先是 Pod 网络,它确保每个 Pod 都有自己的 IP 地址,Pod 之间可以直接通信;其次是 Service 网络,它在 Pod 之上构建了一层抽象,提供稳定的访问方式。这两层网络协同工作,构成了 Kubernetes 强大的网络能力。
Kubernetes 网络模型
Kubernetes 定义了一套简洁但功能强大的网络模型,所有 CNI 插件都必须遵循这套模型。理解这套模型有助于我们排查网络问题,也能更好地理解为什么 Kubernetes 网络是这样设计的。
核心原则
Kubernetes 网络模型建立在几个基本原则之上。每个 Pod 都拥有一个独立的 IP 地址,这个地址在集群范围内唯一,不需要做任何网络地址转换(NAT)。这意味着 Pod 之间可以直接使用对方的 IP 地址通信,就像传统网络中的主机一样简单。
同一个 Pod 内的所有容器共享同一个网络命名空间,它们可以通过 localhost 直接通信,共享同一个 IP 地址和端口空间。这种设计让同一个 Pod 内的容器协作变得非常自然,比如一个应用容器和一个 sidecar 代理容器可以通过 localhost 高效地交换数据。
不同 Pod 之间的通信不需要经过 NAT,无论它们运行在同一个节点还是不同节点上。这个原则简化了应用的开发和调试,因为应用看到的源 IP 就是真实的客户端 IP,而不是某个中间代理的 IP。
节点上的系统进程(如 kubelet)可以与该节点上的所有 Pod 通信。这为监控、日志采集等功能提供了便利。
网络实现方式
Kubernetes 本身并不实现 Pod 网络,而是通过 CNI(Container Network Interface)插件来实现。CNI 是一套标准接口,让 Kubernetes 可以与各种网络解决方案集成。常见的 CNI 插件包括 Flannel、Calico、Cilium 等,它们各有特点,适用于不同的场景。
选择 CNI 插件时需要考虑几个因素:网络性能要求、是否需要网络策略支持、集群规模、以及是否需要高级特性如网络加密等。我们会在后面详细介绍各个 CNI 插件的特点。
Service 详解
Service 是 Kubernetes 中最基础也是最重要的网络抽象。当你的应用以 Pod 形式运行时,Pod 的 IP 地址是动态变化的——每次 Pod 重启或迁移到其他节点,IP 都会改变。这对于客户端来说是个大问题,因为它们无法知道应该连接哪个 IP 地址。Service 就是用来解决这个问题的。
Service 的工作原理
Service 为一组 Pod 提供了一个稳定的访问入口。它有自己的 IP 地址(称为 ClusterIP),这个 IP 在 Service 的整个生命周期内保持不变。客户端只需要知道 Service 的 IP 地址,Service 会自动将请求转发到后端的 Pod 上。
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app.kubernetes.io/name: MyApp
ports:
- protocol: TCP
port: 80 # Service 暴露的端口
targetPort: 9376 # Pod 上应用的端口
这个示例创建了一个名为 my-service 的 Service,它会选择所有带有 app.kubernetes.io/name: MyApp 标签的 Pod,并将到达 Service 80 端口的流量转发到 Pod 的 9376 端口。
Service 通过 EndpointSlice 对象来追踪后端 Pod。EndpointSlice 存储了所有健康 Pod 的 IP 地址和端口信息,当 Pod 数量变化时,EndpointSlice 会自动更新。这种设计比传统的 Endpoints 对象更高效,特别是在大规模集群中。
kube-proxy 与代理模式
kube-proxy 是实现 Service 负载均衡的关键组件。它运行在每个节点上,监听 Service 和 EndpointSlice 的变化,然后配置节点的网络规则,将到达 Service IP 的流量转发到后端 Pod。
kube-proxy 支持多种代理模式,每种模式有不同的特点和适用场景。
iptables 模式
iptables 模式是 Linux 节点上的默认模式(在较新版本中已被 nftables 模式取代)。kube-proxy 使用 Linux 内核的 iptables 接口来配置数据包转发规则。
当请求到达 Service 的 ClusterIP 时,iptables 规则会随机选择一个后端 Pod,然后将流量转发过去。这种方式的优点是效率高,因为所有转发都在内核空间完成,不需要用户空间的代理程序介入。但它也有缺点:在大规模集群中,iptables 规则数量会非常庞大,更新规则的延迟较高。
# 查看 iptables 规则(示意)
sudo iptables -t nat -L KUBE-SERVICES -n
从 Kubernetes 1.28 开始,iptables 模式进行了优化,只更新实际变化的 Service 规则,大大提高了更新效率。对于大规模集群,可以通过调整 minSyncPeriod 参数来控制同步频率,在实时性和性能之间取得平衡。
IPVS 模式
IPVS(IP Virtual Server)模式专门为大规模集群设计。它使用 Linux 内核的 IPVS 模块来实现负载均衡,相比 iptables,IPVS 使用哈希表来存储规则,查找速度更快。
IPVS 支持多种负载均衡算法:
- rr(Round Robin):轮询,将请求依次分发给每个后端
- wrr(Weighted Round Robin):加权轮询,根据权重分配流量
- lc(Least Connection):最少连接,将请求分发给当前连接数最少的后端
- sh(Source Hashing):源地址哈希,相同客户端 IP 的请求总是发送到同一个后端
- mh(Maglev Hashing):Maglev 哈希算法,提供更好的哈希一致性
要使用 IPVS 模式,需要确保节点上已加载 IPVS 内核模块:
# 检查 IPVS 模块是否可用
lsmod | grep ip_vs
# 加载 IPVS 模块(如果未加载)
modprobe ip_vs
modprobe ip_vs_rr
modprobe ip_vs_wrr
需要注意的是,IPVS 模式在 Kubernetes 1.35 中已被标记为废弃,推荐使用 nftables 模式替代。
nftables 模式
nftables 是 iptables 的继任者,提供了更好的性能和可扩展性。从 Kubernetes 1.33 开始,nftables 模式成为稳定版本,并作为 Linux 节点的默认代理模式。
nftables 使用新的内核 API,相比 iptables 有以下优势:
- 更高效的规则更新机制,增量更新而不是全量替换
- 更好的大规模集群支持
- 更简洁的配置语法
使用 nftables 模式需要 Linux 内核 5.13 或更高版本。从 iptables 迁移到 nftables 时需要注意一些行为差异,比如 NodePort 默认只在节点的主 IP 地址上监听,而不是所有本地 IP。
Windows kernelspace 模式
Windows 节点使用专门的 kernelspace 模式,它通过 Windows Virtual Filtering Platform (VFP) 实现数据包过滤和转发。这个模式支持 Direct Server Return (DSR),可以优化 Pod 之间的通信路径。
Service 类型详解
Kubernetes 提供了四种 Service 类型,每种类型适用于不同的场景。
ClusterIP(默认类型)
ClusterIP 类型的 Service 只在集群内部可访问,这是最常用的类型。它分配一个集群内部的虚拟 IP 地址,集群内的 Pod 可以通过这个 IP 访问 Service。
apiVersion: v1
kind: Service
metadata:
name: backend-service
spec:
type: ClusterIP
selector:
app: backend
ports:
- name: http
port: 80
targetPort: 8080
protocol: TCP
ClusterIP 特别适合内部服务,比如数据库、缓存、内部 API 等。由于不暴露到集群外部,安全性更好。
NodePort
NodePort 类型的 Service 在每个节点的特定端口上暴露服务。Kubernetes 会在每个节点上开放一个端口(默认范围 30000-32767),外部流量可以通过 节点IP:NodePort 访问 Service。
apiVersion: v1
kind: Service
metadata:
name: frontend-service
spec:
type: NodePort
selector:
app: frontend
ports:
- name: http
port: 80
targetPort: 8080
nodePort: 30080 # 可选,不指定则自动分配
protocol: TCP
NodePort 的优点是简单直接,不需要额外的负载均衡器。缺点是端口号范围有限,而且需要知道节点的 IP 地址。适合开发测试环境或小型部署。
LoadBalancer
LoadBalancer 类型的 Service 会请求云提供商创建一个外部负载均衡器,并将流量转发到 Service。这是在云环境中暴露服务的标准方式。
apiVersion: v1
kind: Service
metadata:
name: web-service
annotations:
# 云提供商特定的注解
service.beta.kubernetes.io/aws-load-balancer-type: nlb
spec:
type: LoadBalancer
selector:
app: web
ports:
- name: http
port: 80
targetPort: 8080
创建 LoadBalancer 类型的 Service 后,云提供商会分配一个外部 IP 地址,可以通过 kubectl get svc 查看分配的 IP。这种方式适合生产环境,提供了高可用性和自动扩展能力。
ExternalName
ExternalName 类型的 Service 将服务映射到一个外部 DNS 名称,而不是 Pod。这让你可以在集群内部使用统一的服务发现机制来访问外部服务。
apiVersion: v1
kind: Service
metadata:
name: external-database
spec:
type: ExternalName
externalName: database.example.com
集群内的 Pod 可以通过 external-database.default.svc.cluster.local 访问外部数据库,Service 会返回 CNAME 记录指向 database.example.com。这种类型不创建 ClusterIP,也不需要 selector。
无头服务(Headless Service)
当不需要负载均衡或单一 Service IP 时,可以将 clusterIP 设置为 None,创建一个无头服务。这种服务直接解析到后端 Pod 的 IP 地址。
apiVersion: v1
kind: Service
metadata:
name: headless-service
spec:
clusterIP: None # 关键:设置为 None
selector:
app: stateful-app
ports:
- port: 80
targetPort: 8080
无头服务适合以下场景:
- 需要直接访问特定 Pod(如 StatefulSet)
- 客户端自己做负载均衡
- 服务发现需要知道所有后端地址
DNS 查询无头服务会返回所有后端 Pod 的 A 记录:
# 查询无头服务的 DNS 记录
nslookup headless-service.default.svc.cluster.local
# 返回结果示例
# Name: headless-service.default.svc.cluster.local
# Address: 10.244.1.5
# Address: 10.244.2.3
# Address: 10.244.3.7
会话亲和性
某些应用需要同一个客户端的请求始终发送到同一个后端 Pod,这称为会话亲和性(Session Affinity)。Kubernetes 支持基于客户端 IP 的会话亲和性。
apiVersion: v1
kind: Service
metadata:
name: sticky-service
spec:
selector:
app: my-app
ports:
- port: 80
targetPort: 8080
sessionAffinity: ClientIP # 启用会话亲和性
sessionAffinityConfig:
clientIP:
timeoutSeconds: 10800 # 会话保持时间,默认 3 小时
需要注意的是,基于 IP 的会话亲和性并不完美。如果客户端通过代理或负载均衡器访问,源 IP 可能是代理的 IP 而不是真实客户端 IP,导致会话分配不均衡。
流量策略
Service 支持两种流量策略,用于控制流量路由方式。
内部流量策略
internalTrafficPolicy 控制集群内部流量的路由方式:
apiVersion: v1
kind: Service
metadata:
name: local-service
spec:
selector:
app: my-app
ports:
- port: 80
targetPort: 8080
internalTrafficPolicy: Local # 只路由到本地节点的 Pod
设置为 Local 时,Service 只会将流量转发到与客户端同一节点上的 Pod。如果本地没有健康的 Pod,流量会被丢弃。这种策略可以减少跨节点网络跳转,降低延迟。
外部流量策略
externalTrafficPolicy 控制 NodePort 和 LoadBalancer 类型 Service 的外部流量路由:
apiVersion: v1
kind: Service
metadata:
name: external-service
spec:
type: LoadBalancer
selector:
app: my-app
ports:
- port: 80
targetPort: 8080
externalTrafficPolicy: Local # 保留客户端源 IP
设置为 Local 有两个好处:
- 保留客户端源 IP:默认情况下,流量会经过 SNAT,后端 Pod 看到的是节点 IP 而非真实客户端 IP。设置 Local 后,源 IP 会被保留。
- 减少网络跳转:流量只发送到接收入口流量的节点上的 Pod,不会转发到其他节点。
多端口 Service
一个 Service 可以同时暴露多个端口:
apiVersion: v1
kind: Service
metadata:
name: multi-port-service
spec:
selector:
app: my-app
ports:
- name: http # 多端口时必须指定名称
port: 80
targetPort: 8080
protocol: TCP
- name: https
port: 443
targetPort: 8443
protocol: TCP
- name: metrics
port: 9090
targetPort: 9090
protocol: TCP
当 Service 有多个端口时,必须为每个端口指定名称,以便区分。
DNS 服务发现
Kubernetes 集群内部运行 DNS 服务(通常是 CoreDNS),自动为 Service 和 Pod 创建 DNS 记录。这让服务发现变得非常简单——只需要使用服务名称,而不需要记住 IP 地址。
Service DNS 记录
每个 Service 都会获得一个 DNS 名称,格式为 <service-name>.<namespace>.svc.cluster.local。
# 完整格式
curl http://my-service.default.svc.cluster.local:80
# 同一命名空间内可以简写
curl http://my-service:80
# 跨命名空间访问
curl http://my-service.other-namespace.svc.cluster.local:80
这种命名方式让服务之间的依赖关系变得清晰,也便于跨命名空间访问服务。
Pod DNS 记录
默认情况下,Pod 也有 DNS 记录,格式为 <pod-ip>.<namespace>.pod.cluster.local。其中 Pod IP 需要用短横线替换点号:
# Pod IP 为 10.244.1.5
nslookup 10-244-1-5.default.pod.cluster.local
你还可以通过设置 Pod 的 hostname 和 subdomain 字段来自定义 DNS 名称:
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
hostname: web-app # Pod 的主机名
subdomain: my-subdomain # 子域名,需要匹配一个无头服务
containers:
- name: app
image: nginx
如果存在同名的无头服务,这个 Pod 的完整域名将是 web-app.my-subdomain.default.svc.cluster.local。
DNS 配置
Pod 可以自定义 DNS 配置:
apiVersion: v1
kind: Pod
metadata:
name: custom-dns-pod
spec:
dnsPolicy: ClusterFirst # DNS 策略
dnsConfig:
nameservers:
- 8.8.8.8
searches:
- my-domain.local
options:
- name: ndots
value: "2"
containers:
- name: app
image: nginx
dnsPolicy 有几种选项:
- ClusterFirst:优先使用集群 DNS,失败时使用宿主机 DNS
- Default:直接使用宿主机的 DNS 配置
- ClusterFirstWithHostNet:与 ClusterFirst 类似,用于 hostNetwork 的 Pod
- None:完全自定义 DNS 配置,必须指定 dnsConfig
CoreDNS 配置
CoreDNS 是 Kubernetes 默认的 DNS 服务器,通过 ConfigMap 配置:
apiVersion: v1
kind: ConfigMap
metadata:
name: coredns
namespace: kube-system
data:
Corefile: |
.:53 {
errors # 错误日志
health { # 健康检查端点
lameduck 5s
}
ready # 就绪检查端点
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
prometheus :9153 # Prometheus 指标
forward . /etc/resolv.conf {
max_concurrent 1000
}
cache 30 # DNS 缓存 30 秒
loop # 检测解析循环
reload # 自动重载配置
loadbalance # 负载均衡 A 记录
}
这个配置定义了 CoreDNS 如何处理各种 DNS 查询。kubernetes 插件处理集群内部的 DNS 查询,forward 插件将外部查询转发给上游 DNS 服务器。
Ingress 与 Gateway API
Ingress 和 Gateway API 是 Kubernetes 中管理外部 HTTP/HTTPS 流量入口的核心机制。它们提供了比 Service 更丰富的路由功能,支持基于主机名和路径的路由、TLS 终止、负载均衡等特性。
Ingress 简介
Ingress 通过定义规则将外部流量路由到集群内的 Service。与 NodePort 或 LoadBalancer 类型的 Service 相比,Ingress 的主要优势在于:
- 统一入口:一个 IP 地址可以服务多个域名和应用
- 基于路径的路由:根据 URL 路径将流量分发到不同服务
- TLS 终止:集中处理 HTTPS 证书
- 成本优化:减少对外部负载均衡器的需求
基本示例:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web-ingress
spec:
ingressClassName: nginx
rules:
- host: example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api-service
port:
number: 8080
- path: /
pathType: Prefix
backend:
service:
name: web-service
port:
number: 80
Gateway API 简介
Gateway API 是 Ingress 的继任者,解决了 Ingress API 的诸多局限性。Kubernetes 官方推荐新项目使用 Gateway API。
Gateway API 的优势:
| 特性 | Ingress | Gateway API |
|---|---|---|
| 多协议支持 | 仅 HTTP/HTTPS | HTTP、HTTPS、TCP、UDP、gRPC |
| 角色分离 | 单一资源 | Gateway + HTTPRoute 分离 |
| 流量拆分 | 依赖注解 | 原生支持权重路由 |
| RBAC 支持 | 有限 | 原生支持精细权限控制 |
基本示例:
# 定义 Gateway
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: my-gateway
spec:
gatewayClassName: nginx
listeners:
- name: http
protocol: HTTP
port: 80
---
# 定义路由
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: my-route
spec:
parentRefs:
- name: my-gateway
hostnames:
- "example.com"
rules:
- backendRefs:
- name: my-service
port: 80
关于 Ingress 和 Gateway API 的详细配置、常用注解、TLS 配置、多控制器共存、金丝雀发布等高级主题,请参阅专门的教程页面:Ingress 入口管理
NetworkPolicy 详解
NetworkPolicy 是 Kubernetes 的网络策略资源,用于控制 Pod 之间的网络流量。它让你可以实现细粒度的访问控制,比如只允许特定的 Pod 访问数据库,或者拒绝所有入站流量。
前提条件
NetworkPolicy 需要网络插件支持。不是所有 CNI 插件都实现了 NetworkPolicy 功能。以下是主流 CNI 插件的支持情况:
| CNI 插件 | NetworkPolicy 支持 |
|---|---|
| Calico | 完整支持,功能最强大 |
| Cilium | 完整支持,支持 L7 策略 |
| Flannel | 不支持(需配合 Calico) |
| Weave Net | 完整支持 |
创建 NetworkPolicy 时,如果网络插件不支持,策略不会生效,但也不会报错。
隔离模式
理解 NetworkPolicy 的隔离模式很重要。默认情况下,Pod 是非隔离的,允许所有进出流量。当创建了一个选择该 Pod 的 NetworkPolicy 并指定了 Ingress 或 Egress 策略类型时,Pod 就变成了隔离状态。
隔离状态的 Pod 只允许符合策略规则的流量。多个策略之间是叠加关系,只要任一策略允许,流量就可以通过。
入站规则(Ingress)
入站规则控制哪些流量可以进入 Pod:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend
namespace: production
spec:
podSelector:
matchLabels:
app: backend # 选择后端 Pod
policyTypes:
- Ingress
ingress:
- from:
# 允许来自特定 Pod 的流量
- podSelector:
matchLabels:
app: frontend
# 允许来自特定命名空间的流量
- namespaceSelector:
matchLabels:
environment: production
# 允许来自特定 IP 段的流量
- ipBlock:
cidr: 10.0.0.0/8
except:
- 10.0.1.0/24 # 排除这个段
ports:
- protocol: TCP
port: 8080
这个策略允许以下流量访问后端 Pod 的 8080 端口:
- 带有
app: frontend标签的 Pod(同一命名空间) - 带有
environment: production标签的命名空间中的所有 Pod - 来自 10.0.0.0/8 网段的流量(排除 10.0.1.0/24)
出站规则(Egress)
出站规则控制 Pod 可以访问哪些目标:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns-and-db
namespace: production
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Egress
egress:
# 允许 DNS 查询
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
# 允许访问数据库
- to:
- podSelector:
matchLabels:
app: database
ports:
- protocol: TCP
port: 5432
这个策略限制后端 Pod 只能访问 DNS 服务和数据库。
选择器的组合逻辑
理解选择器的组合逻辑对于正确编写策略非常重要:
ingress:
- from:
# OR 关系:两个独立的元素
- podSelector:
matchLabels:
role: frontend
- namespaceSelector:
matchLabels:
project: myproject
# 以上表示:来自 frontend Pod 或 myproject 命名空间的流量
- from:
# AND 关系:同一个元素中的两个字段
- podSelector:
matchLabels:
role: frontend
namespaceSelector:
matchLabels:
project: myproject
# 以上表示:来自 myproject 命名空间中 frontend Pod 的流量
默认策略模式
以下是几种常用的默认策略模式:
默认拒绝所有入站流量
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: production
spec:
podSelector: {} # 选择命名空间中的所有 Pod
policyTypes:
- Ingress
# 不指定 ingress 规则,表示拒绝所有入站流量
这个策略让命名空间进入"默认拒绝"模式,必须显式创建允许规则才能让流量进入。
默认拒绝所有出站流量
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-egress
namespace: production
spec:
podSelector: {}
policyTypes:
- Egress
默认允许所有入站流量
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-ingress
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- {} # 空规则表示允许所有
默认允许所有出站流量
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-egress
namespace: production
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- {}
微服务网络策略示例
下面是一个完整的微服务场景网络策略配置:
# 命名空间标签
apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
name: production
---
# 前端服务策略:允许外部入站,允许访问后端和 DNS
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: frontend-policy
namespace: production
spec:
podSelector:
matchLabels:
app: frontend
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: ingress-nginx
ports:
- protocol: TCP
port: 8080
egress:
# DNS
- to:
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
# 后端服务
- to:
- podSelector:
matchLabels:
app: backend
ports:
- protocol: TCP
port: 8080
---
# 后端服务策略:只允许前端访问,允许访问数据库和 DNS
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-policy
namespace: production
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
egress:
# DNS
- to:
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
# 数据库
- to:
- podSelector:
matchLabels:
app: database
ports:
- protocol: TCP
port: 5432
---
# 数据库策略:只允许后端访问
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: database-policy
namespace: production
spec:
podSelector:
matchLabels:
app: database
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
app: backend
ports:
- protocol: TCP
port: 5432
egress:
# 只允许 DNS
- to:
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
这个配置实现了经典的分层安全模型:前端可以接收外部流量并访问后端,后端只能接收前端流量并访问数据库,数据库只能接收后端流量。每一层都只开放必要的端口,最小化攻击面。
CNI 插件详解
CNI(Container Network Interface)是 Kubernetes 网络的核心组件,负责实现 Pod 网络。选择合适的 CNI 插件对集群的网络性能和功能至关重要。
Flannel
Flannel 是最简单易用的 CNI 插件,由 CoreOS 开发。它的设计目标是让网络配置尽可能简单。
工作原理
Flannel 为每个节点分配一个子网,所有 Pod 的 IP 地址都从该节点的子网中分配。跨节点的 Pod 通信通过封装(overlay)网络实现。
Flannel 支持多种后端:
- VXLAN:最常用,性能好,兼容性强
- host-gw:性能最好,但要求节点在同一个二层网络
- UDP:兼容性最好,但性能最差,仅用于调试
部署示例
# 使用默认配置部署(VXLAN 后端)
kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml
# 使用自定义 Pod CIDR
kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml
# 需要先修改 kube-flannel.yml 中的 Network 配置
特点总结
- 优点:部署简单、稳定性好、社区成熟
- 缺点:不支持 NetworkPolicy、功能相对简单
- 适用场景:开发测试环境、小型生产集群、不需要网络策略的场景
Calico
Calico 是功能最强大的 CNI 插件,由 Tigera 开发维护。它不仅提供网络功能,还提供完整的网络安全解决方案。
工作原理
Calico 使用纯路由方式实现 Pod 网络,不使用封装。它通过 BGP 协议在节点之间交换路由信息,让每个节点都知道如何路由到其他节点上的 Pod IP。
这种设计带来了几个优势:
- 性能优异:没有封装开销,延迟更低
- 可调试性强:使用标准 IP 路由,可以用常规网络工具排查
- 与现有网络集成:可以与物理网络直接通信
网络策略能力
Calico 提供了比 Kubernetes 原生 NetworkPolicy 更强大的策略能力:
# Calico NetworkPolicy 示例
apiVersion: projectcalico.org/v3
kind: NetworkPolicy
metadata:
name: allow-https
namespace: production
spec:
selector: app == 'web'
ingress:
- action: Allow
protocol: TCP
destination:
ports:
- 443
source:
# 支持 FQDN,可以限制访问特定域名
domains:
- "api.example.com"
egress:
- action: Allow
protocol: TCP
destination:
# 支持域名限制
domains:
- "*.googleapis.com"
Calico 还支持:
- 策略层级(Tiers):定义策略的优先级顺序
- 全局策略:应用于整个集群的策略
- DNS 策略:基于域名的访问控制
- L7 策略:HTTP 层面的访问控制
数据平面选项
Calico 支持多种数据平面:
- iptables:默认选项,兼容性最好
- eBPF:性能最好,需要较新的内核
- Windows HNS:Windows 节点支持
特点总结
- 优点:功能完整、性能优异、策略强大、支持大规模部署
- 缺点:配置复杂、学习曲线陡峭
- 适用场景:生产环境、需要网络安全的企业级部署、大规模集群
Cilium
Cilium 是新一代 CNI 插件,基于 eBPF 技术。它提供了传统 CNI 无法实现的高级功能,特别适合云原生环境。
eBPF 技术优势
eBPF(extended Berkeley Packet Filter)允许在 Linux 内核中安全地运行沙盒程序。Cilium 利用 eBPF 实现了:
- 高性能数据路径:数据包处理完全在内核空间,无需切换到用户空间
- 动态更新:策略更新不需要重启或重载网络规则
- 深度可观测性:可以观测每个网络连接的详细信息
核心功能
1. 身份感知安全
Cilium 为每组具有相同标签的 Pod 分配一个安全身份。网络策略基于身份而非 IP 地址,这使得策略在 Pod IP 变化时仍然有效。
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: allow-frontend
spec:
endpointSelector:
matchLabels:
app: backend
ingress:
- fromEndpoints:
- matchLabels:
app: frontend
toPorts:
- ports:
- port: "8080"
protocol: TCP
2. L7 策略
Cilium 支持应用层(HTTP、Kafka、DNS 等)的策略:
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: api-policy
spec:
endpointSelector:
matchLabels:
app: backend
ingress:
- fromEndpoints:
- matchLabels:
app: frontend
toPorts:
- ports:
- port: "8080"
protocol: TCP
rules:
http:
- method: GET
path: "/api/v1/.*"
- method: POST
path: "/api/v1/users"
3. 服务网格集成
Cilium 可以替代传统的 sidecar 代理,提供 mTLS、流量管理等 Service Mesh 功能,但不需要每个 Pod 都运行代理容器。
4. Hubble 可观测性
Hubble 是 Cilium 的可观测性组件,提供:
- 实时网络流量可视化
- 服务依赖关系图
- DNS 查询追踪
- 策略执行日志
# 查看 Hubble UI
cilium hubble ui
特点总结
- 优点:性能卓越、功能先进、可观测性强、支持 L7 策略
- 缺点:需要较新内核、学习曲线陡峭、生产部署经验相对较少
- 适用场景:追求高性能和先进功能的生产环境、需要深度可观测性的场景
CNI 插件对比
| 特性 | Flannel | Calico | Cilium |
|---|---|---|---|
| 网络模式 | Overlay(VXLAN) | 纯路由/BGP | eBPF |
| 性能 | 中等 | 高 | 最高 |
| NetworkPolicy | 不支持 | 完整支持 | 完整支持 + L7 |
| 配置复杂度 | 简单 | 中等 | 复杂 |
| 可观测性 | 无 | 基础 | 强大(Hubble) |
| 大规模支持 | 一般 | 好 | 优秀 |
| 内核要求 | 低 | 低 | 高(5.10+) |
| 社区活跃度 | 中 | 高 | 高 |
选择建议:
- Flannel:快速入门、开发测试、不需要网络策略
- Calico:生产环境、需要网络安全、需要 BGP 集成
- Cilium:追求最新技术、需要高性能和深度可观测性
网络故障排查
Kubernetes 网络问题排查需要系统化的方法。下面介绍常见的排查思路和工具。
常用排查命令
# 查看 Service 状态
kubectl get svc -o wide
# 查看 Endpoints
kubectl get endpoints <service-name>
# 查看 Ingress 状态
kubectl get ingress
# 查看 NetworkPolicy
kubectl get networkpolicy -A
# 查看 CoreDNS 日志
kubectl logs -n kube-system -l k8s-app=kube-dns
# 进入 Pod 测试网络
kubectl exec -it <pod-name> -- sh
# 在 Pod 内执行
nslookup kubernetes.default
curl http://<service-name>:<port>
ping <pod-ip>
常见问题排查
Service 无法访问
- 检查 Endpoints 是否正常:
kubectl get endpoints <service-name>
# 如果 Endpoints 为空,说明没有 Pod 匹配 Service 的 selector
- 检查 Pod 标签:
kubectl get pods -l app=<app-name> -o wide
- 检查 Pod 是否健康:
kubectl describe pod <pod-name>
# 查看 Readiness Probe 状态
DNS 解析失败
- 检查 CoreDNS 是否运行:
kubectl get pods -n kube-system -l k8s-app=kube-dns
- 检查 CoreDNS 配置:
kubectl get configmap coredns -n kube-system -o yaml
- 测试 DNS 解析:
kubectl exec -it <pod-name> -- nslookup kubernetes.default
# 如果使用短名称失败,尝试完整域名
kubectl exec -it <pod-name> -- nslookup kubernetes.default.svc.cluster.local
跨节点 Pod 无法通信
- 检查 CNI 插件状态:
# 查看 CNI 配置
ls /etc/cni/net.d/
# 查看 CNI 日志
journalctl -u kubelet | grep cni
- 检查节点网络:
# 在节点上执行
ip route
iptables -L -n -v
- 检查防火墙:
# 确保 CNI 使用的端口开放
# Flannel VXLAN: 8472/UDP
# Calico BGP: 179/TCP
# Calico VXLAN: 4789/UDP
网络调试工具
部署一个临时的调试 Pod 来排查网络问题:
apiVersion: v1
kind: Pod
metadata:
name: network-debug
spec:
containers:
- name: debug
image: nicolaka/netshoot
command: ["sleep", "3600"]
securityContext:
capabilities:
add: ["NET_ADMIN"]
这个镜像包含了常用的网络调试工具:curl、wget、nslookup、dig、ping、traceroute、nc、ss 等。
最佳实践
Service 使用建议
-
优先使用 ClusterIP + Ingress:生产环境推荐使用 ClusterIP 类型的 Service,通过 Ingress 暴露外部访问。这样可以将外部流量管理和内部服务发现解耦。
-
为 Service 添加标签和注解:便于管理、监控和自动化工具识别。
-
合理使用会话亲和性:只在必要时使用,避免影响负载均衡。
-
配置健康检查:确保 Readiness Probe 正确配置,让 Service 只路由到健康的 Pod。
网络策略建议
-
默认拒绝,显式允许:在命名空间级别创建默认拒绝策略,然后按需创建允许规则。
-
分层设计策略:按照应用层级(前端、后端、数据库)设计策略,每层只开放必要的端口。
-
不要忘记 DNS:出站策略必须允许访问 DNS 服务,否则服务发现会失败。
-
测试策略生效:使用调试 Pod 验证策略是否按预期工作。
CNI 选择建议
-
评估需求:根据性能要求、安全需求、运维复杂度选择合适的 CNI。
-
预留网络规划:提前规划 Pod CIDR 和 Service CIDR,避免与现有网络冲突。
-
监控网络性能:部署后持续监控网络延迟、吞吐量等指标。
-
保持版本更新:CNI 插件更新频繁,保持更新以获得更好的性能和安全修复。
小结
本章我们系统学习了 Kubernetes 网络的核心概念:
Service 是 Kubernetes 服务发现的基础,提供了稳定的访问入口和负载均衡能力。我们学习了四种 Service 类型、会话亲和性、流量策略等高级特性,以及 kube-proxy 的多种代理模式。
DNS 服务发现 让服务访问变得简单,我们了解了 CoreDNS 的配置和 Pod DNS 自定义方式。
Ingress 提供了 HTTP/HTTPS 路由能力,支持多种控制器实现。我们比较了 NGINX、Traefik、HAProxy 三种主流控制器的特点,以及 TLS 配置、多控制器共存等高级用法。
NetworkPolicy 实现了细粒度的网络访问控制,我们学习了入站和出站规则、选择器组合逻辑,以及微服务场景的完整策略设计。
CNI 插件 是 Pod 网络的实现基础。Flannel 简单易用、Calico 功能强大、Cilium 技术先进,各有特点适用于不同场景。
理解这些网络概念对于构建可靠的 Kubernetes 应用至关重要。下一章我们将学习存储管理。