642 lines
19 KiB
Markdown
642 lines
19 KiB
Markdown
|
|
---
|
|||
|
|
tags: [k8s, service, networking, devops]
|
|||
|
|
create time: 2026-07-07 15:48
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# Kubernetes Service 与网络
|
|||
|
|
|
|||
|
|
## 概述
|
|||
|
|
|
|||
|
|
Kubernetes 中 Pod 的生命周期是短暂的——它们随时可能被销毁重建,每次重建都会获得新的 IP 地址。如果服务之间直接通过 Pod IP 通信,任何一次 Pod 重启都会导致调用方断连。**Service** 正是为了解决这个问题而生:它为一组动态变化的 Pod 提供一个稳定的访问入口(虚拟 IP),配合 DNS 和负载均衡,让服务发现变得透明且可靠。
|
|||
|
|
|
|||
|
|
## K8s 网络模型
|
|||
|
|
|
|||
|
|
Kubernetes 对集群网络有三个核心要求,任何 CNI(Container Network Interface)插件都必须满足:
|
|||
|
|
|
|||
|
|
| 原则 | 说明 |
|
|||
|
|
|------|------|
|
|||
|
|
| **Pod-to-Pod** | 集群内任意两个 Pod 可以直接通过 IP 通信,无需 NAT |
|
|||
|
|
| **Pod-to-Service** | Pod 通过 Service 的虚拟 IP(ClusterIP)访问后端 Pod,由 kube-proxy 实现负载均衡 |
|
|||
|
|
| **External-to-Service** | 集群外部流量可以通过 NodePort、LoadBalancer 或 Ingress 进入集群 |
|
|||
|
|
|
|||
|
|
### CNI 插件简介
|
|||
|
|
|
|||
|
|
CNI 插件负责为 Pod 分配 IP 地址并配置网络连通性。常见实现:
|
|||
|
|
|
|||
|
|
- **Calico**:基于 BGP 路由,支持 NetworkPolicy,性能优秀
|
|||
|
|
- **Flannel**:简单轻量,使用 VXLAN overlay,适合小规模集群
|
|||
|
|
- **Cilium**:基于 eBPF,高性能,支持 L7 网络策略
|
|||
|
|
- **Weave Net**:自带加密,配置简单
|
|||
|
|
|
|||
|
|
> [!tip] 如何选择
|
|||
|
|
> 小规模或学习环境用 Flannel 足够;生产环境推荐 Calico 或 Cilium,它们对 NetworkPolicy 的支持更完善。
|
|||
|
|
|
|||
|
|
## Service 详解
|
|||
|
|
|
|||
|
|
### ClusterIP
|
|||
|
|
|
|||
|
|
**默认类型**,为 Service 分配一个集群内部虚拟 IP,仅集群内可访问。
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
apiVersion: v1
|
|||
|
|
kind: Service
|
|||
|
|
metadata:
|
|||
|
|
name: my-app-svc
|
|||
|
|
namespace: default
|
|||
|
|
spec:
|
|||
|
|
type: ClusterIP # 默认值,可省略
|
|||
|
|
selector:
|
|||
|
|
app: my-app
|
|||
|
|
ports:
|
|||
|
|
- protocol: TCP
|
|||
|
|
port: 80 # Service 端口
|
|||
|
|
targetPort: 8080 # Pod 端口
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
流量路径:`Client Pod → ClusterIP(虚拟 IP)→ kube-proxy → 后端 Pod:8080`
|
|||
|
|
|
|||
|
|
ClusterIP 是一个"虚拟 IP"——它不存在于任何网卡上,只存在于 iptables/IPVS 规则中。
|
|||
|
|
|
|||
|
|
### NodePort
|
|||
|
|
|
|||
|
|
在 ClusterIP 的基础上,在**每个节点**上开一个端口(默认范围 30000-32767),外部流量可通过 `<NodeIP>:<NodePort>` 访问。
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
apiVersion: v1
|
|||
|
|
kind: Service
|
|||
|
|
metadata:
|
|||
|
|
name: my-app-nodeport
|
|||
|
|
spec:
|
|||
|
|
type: NodePort
|
|||
|
|
selector:
|
|||
|
|
app: my-app
|
|||
|
|
ports:
|
|||
|
|
- protocol: TCP
|
|||
|
|
port: 80 # Service 端口(集群内部访问)
|
|||
|
|
targetPort: 8080 # Pod 端口
|
|||
|
|
nodePort: 30080 # 节点端口(可选,不指定则自动分配)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
> [!info] iptables 规则链路
|
|||
|
|
> 当外部请求到达 `<NodeIP>:30080` 时,kube-proxy 在每个节点上创建的 iptables 规则会将流量 DNAT 到后端 Pod 的 IP:Port。这意味着即使请求落在没有运行目标 Pod 的节点上,流量也会被正确转发。
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
flowchart LR
|
|||
|
|
A[External Client] -->|NodeIP:30080| B[Node]
|
|||
|
|
B -->|iptables DNAT| C[Pod on Node A]
|
|||
|
|
B -->|iptables DNAT| D[Pod on Node B]
|
|||
|
|
B -->|iptables DNAT| E[Pod on Node C]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### LoadBalancer
|
|||
|
|
|
|||
|
|
在 NodePort 的基础上,向云厂商申请一个外部负载均衡器(如 AWS ELB、GCP CLB),自动将外部流量分发到各节点的 NodePort。
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
apiVersion: v1
|
|||
|
|
kind: Service
|
|||
|
|
metadata:
|
|||
|
|
name: my-app-lb
|
|||
|
|
annotations:
|
|||
|
|
# 云厂商特定注解,例如 AWS:
|
|||
|
|
service.beta.kubernetes.io/aws-load-balancer-type: nlb
|
|||
|
|
spec:
|
|||
|
|
type: LoadBalancer
|
|||
|
|
selector:
|
|||
|
|
app: my-app
|
|||
|
|
ports:
|
|||
|
|
- protocol: TCP
|
|||
|
|
port: 80
|
|||
|
|
targetPort: 8080
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
> [!warning] 继承关系
|
|||
|
|
> `LoadBalancer` = `ClusterIP` + `NodePort` + 云厂商 LB。如果没有云厂商支持(如裸金属环境),LoadBalancer 类型的 Service 会一直停留在 `<pending>` 状态。此时可以考虑 MetalLB 作为替代方案。
|
|||
|
|
|
|||
|
|
### ExternalName
|
|||
|
|
|
|||
|
|
将 Service 映射到一个外部 DNS 名称(CNAME 记录),不涉及任何代理和负载均衡。
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
apiVersion: v1
|
|||
|
|
kind: Service
|
|||
|
|
metadata:
|
|||
|
|
name: external-db
|
|||
|
|
namespace: default
|
|||
|
|
spec:
|
|||
|
|
type: ExternalName
|
|||
|
|
externalName: db.example.com # 映射到外部域名
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**适用场景**:
|
|||
|
|
- 集群内应用需要访问外部数据库或 API
|
|||
|
|
- 跨命名空间/跨集群服务映射
|
|||
|
|
- 迁移过程中临时将内部 Service 指向外部服务
|
|||
|
|
|
|||
|
|
当 Pod 查询 `external-db.default.svc.cluster.local` 时,CoreDNS 会返回 `db.example.com` 的 CNAME 记录,Pod 再对 `db.example.com` 发起真正的 DNS 查询。
|
|||
|
|
|
|||
|
|
### Service 类型对比
|
|||
|
|
|
|||
|
|
| 类型 | ClusterIP | NodePort | LoadBalancer | ExternalName |
|
|||
|
|
|------|-----------|----------|--------------|--------------|
|
|||
|
|
| **默认** | 是 | 否 | 否 | 否 |
|
|||
|
|
| **集群内访问** | 支持 | 支持 | 支持 | 通过 CNAME |
|
|||
|
|
| **集群外访问** | 不支持 | 支持(NodeIP:Port) | 支持(外部 IP) | 不适用 |
|
|||
|
|
| **端口范围** | 任意 | 30000-32767 | 任意(LB 端口) | 无端口 |
|
|||
|
|
| **典型场景** | 内部微服务互调 | 开发测试暴露 | 生产环境对外服务 | 访问外部服务 |
|
|||
|
|
| **云厂商依赖** | 无 | 无 | 需要 | 无 |
|
|||
|
|
|
|||
|
|
## Endpoints 与 EndpointSlices
|
|||
|
|
|
|||
|
|
### Endpoints
|
|||
|
|
|
|||
|
|
每个 Service 背后都有一个同名的 **Endpoints** 对象,记录了当前所有健康 Pod 的 IP:Port 列表。当 Pod 就绪(Readiness Probe 通过)时自动加入,Pod 删除时自动移除。
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 查看 Service 对应的 Endpoints
|
|||
|
|
kubectl get endpoints my-app-svc
|
|||
|
|
# NAME ENDPOINTS AGE
|
|||
|
|
# my-app-svc 10.244.1.5:8080,10.244.2.8:8080 5m
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### EndpointSlices
|
|||
|
|
|
|||
|
|
Kubernetes 1.21 引入 EndpointSlices 作为 Endpoints 的替代方案。
|
|||
|
|
|
|||
|
|
> [!info] 为什么需要 EndpointSlices?
|
|||
|
|
> 在大规模集群中,一个 Service 可能关联成百上千个 Pod。Endpoints 对象会将所有 Pod IP 存储在单个对象中,导致:
|
|||
|
|
> 1. **更新风暴**:任何一个 Pod 变化都要更新整个 Endpoints 对象
|
|||
|
|
> 2. **内存压力**:kube-proxy 和所有节点的 kubelet 都要 watch 这个大对象
|
|||
|
|
> 3. **API Server 压力**:每次更新都要全量推送
|
|||
|
|
>
|
|||
|
|
> EndpointSlices 将后端列表拆分成多个小切片(默认每个切片最多 100 个端点),增量更新,大幅降低开销。
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 查看 EndpointSlices
|
|||
|
|
kubectl get endpointslices -l kubernetes.io/service-name=my-app-svc
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
flowchart TB
|
|||
|
|
subgraph Endpoints 旧方式
|
|||
|
|
S1[Service] --> E1[Endpoints<br/>全部 Pod IP 在一个对象中]
|
|||
|
|
end
|
|||
|
|
subgraph EndpointSlices 新方式
|
|||
|
|
S2[Service] --> ES1[EndpointSlice-0<br/>Pod 1-100]
|
|||
|
|
S2 --> ES2[EndpointSlice-1<br/>Pod 101-200]
|
|||
|
|
S2 --> ES3[EndpointSlice-2<br/>Pod 201-300]
|
|||
|
|
end
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## DNS 解析机制
|
|||
|
|
|
|||
|
|
### CoreDNS
|
|||
|
|
|
|||
|
|
Kubernetes 使用 **CoreDNS** 作为集群内置 DNS 服务(1.12 之前使用 kube-dns)。每个 Service 创建后,CoreDNS 会自动为其注册 DNS 记录。
|
|||
|
|
|
|||
|
|
### Service DNS 格式
|
|||
|
|
|
|||
|
|
| 资源类型 | DNS 格式 | 示例 |
|
|||
|
|
|----------|----------|------|
|
|||
|
|
| 普通 Service | `<svc>.<ns>.svc.cluster.local` | `my-app.default.svc.cluster.local` |
|
|||
|
|
| Headless Service | `<svc>.<ns>.svc.cluster.local` → 解析到所有 Pod IP | 直接返回 Pod IP 列表 |
|
|||
|
|
| Pod | `<pod-ip-dash>.<ns>.pod.cluster.local` | `10-244-1-5.default.pod.cluster.local` |
|
|||
|
|
|
|||
|
|
> [!tip] 同命名空间内可以省略后缀
|
|||
|
|
> 在 `default` 命名空间内的 Pod 访问同命名空间的 Service,可以直接用 `my-app-svc`,无需写全限定域名。
|
|||
|
|
|
|||
|
|
### Headless Service
|
|||
|
|
|
|||
|
|
当 Service 的 `clusterIP` 设置为 `None` 时,它成为一个 **Headless Service**。此时 DNS 查询不会返回一个虚拟 IP,而是直接返回所有后端 Pod 的 IP 列表。
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
apiVersion: v1
|
|||
|
|
kind: Service
|
|||
|
|
metadata:
|
|||
|
|
name: my-app-headless
|
|||
|
|
spec:
|
|||
|
|
clusterIP: None # Headless
|
|||
|
|
selector:
|
|||
|
|
app: my-app
|
|||
|
|
ports:
|
|||
|
|
- port: 80
|
|||
|
|
targetPort: 8080
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**典型应用场景**:
|
|||
|
|
- **StatefulSet**:有状态应用(如数据库集群)需要知道每个 Pod 的独立地址
|
|||
|
|
- **自定义负载均衡**:客户端自己实现负载均衡逻辑
|
|||
|
|
- **服务发现**:客户端需要获取所有后端地址
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 查询 Headless Service 的 DNS
|
|||
|
|
nslookup my-app-headless.default.svc.cluster.local
|
|||
|
|
# Name: my-app-headless.default.svc.cluster.local
|
|||
|
|
# Address: 10.244.1.5
|
|||
|
|
# Address: 10.244.2.8
|
|||
|
|
# Address: 10.244.3.12
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## kube-proxy 深入
|
|||
|
|
|
|||
|
|
kube-proxy 是运行在每个节点上的网络代理,负责实现 Service 的负载均衡。它有三种工作模式:
|
|||
|
|
|
|||
|
|
### iptables 模式(默认)
|
|||
|
|
|
|||
|
|
kube-proxy 监听 Service 和 Endpoints 的变化,在每个节点上维护 iptables 规则链。
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
flowchart TB
|
|||
|
|
A[请求到达 ClusterIP] --> B[KUBE-SERVICES 链]
|
|||
|
|
B --> C{匹配 Service?}
|
|||
|
|
C -->|是| D[KUBE-SVC-XXX 链]
|
|||
|
|
D --> E[概率匹配 -m statistic --mode random]
|
|||
|
|
E -->|概率 50%| F[KUBE-SEP-AAA 链 → Pod A]
|
|||
|
|
E -->|概率 50%| G[KUBE-SEP-BBB 链 → Pod B]
|
|||
|
|
C -->|否| H[REJECT]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**规则链路详解**:
|
|||
|
|
1. `KUBE-SERVICES`:总入口,匹配目标 IP:Port 确定 Service
|
|||
|
|
2. `KUBE-SVC-XXX`:Service 级别链,使用 `statistic` 模块做随机负载均衡
|
|||
|
|
3. `KUBE-SEP-XXX`:Endpoint 级别链,执行 DNAT 将目标地址转换为 Pod IP:Port
|
|||
|
|
|
|||
|
|
> [!warning] iptables 模式的局限
|
|||
|
|
> - 规则数量与 Service × Pod 数量成正比,大规模集群下规则链很长
|
|||
|
|
> - 线性遍历匹配,时间复杂度 O(n)
|
|||
|
|
> - 不支持连接追踪以外的负载均衡算法
|
|||
|
|
|
|||
|
|
### IPVS 模式
|
|||
|
|
|
|||
|
|
IPVS(IP Virtual Server)是 Linux 内核中的四层负载均衡器,性能远优于 iptables。
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
# kube-proxy 配置启用 IPVS
|
|||
|
|
apiVersion: kubeproxy.config.k8s.io/v1alpha1
|
|||
|
|
kind: KubeProxyConfiguration
|
|||
|
|
mode: "ipvs"
|
|||
|
|
ipvs:
|
|||
|
|
scheduler: "rr" # 负载均衡算法:rr / lc / sh / sed / nq
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**支持的负载均衡算法**:
|
|||
|
|
|
|||
|
|
| 算法 | 全称 | 说明 |
|
|||
|
|
|------|------|------|
|
|||
|
|
| `rr` | Round Robin | 轮询(默认) |
|
|||
|
|
| `lc` | Least Connections | 最少连接数 |
|
|||
|
|
| `sh` | Source Hashing | 源地址哈希(会话保持) |
|
|||
|
|
| `sed` | Shortest Expected Delay | 最短预期延迟 |
|
|||
|
|
| `nq` | Never Queue | 永不排队 |
|
|||
|
|
|
|||
|
|
### iptables vs IPVS 对比
|
|||
|
|
|
|||
|
|
| 维度 | iptables | IPVS |
|
|||
|
|
|------|----------|------|
|
|||
|
|
| **性能** | 规则多时下降明显 | 哈希表查找,O(1) |
|
|||
|
|
| **最大 Service 数** | 约 5000 开始明显退化 | 可支撑数万 |
|
|||
|
|
| **负载均衡算法** | 仅随机 | rr / lc / sh / sed / nq |
|
|||
|
|
| **连接保持** | 有限支持 | 原生支持 |
|
|||
|
|
| **依赖** | iptables(用户态工具) | ipvs 内核模块 |
|
|||
|
|
| **调试** | `iptables -L -n -t nat` | `ipvsadm -Ln` |
|
|||
|
|
|
|||
|
|
> [!tip] 生产建议
|
|||
|
|
> 节点 Service 数超过 1000 时,强烈建议切换到 IPVS 模式。
|
|||
|
|
|
|||
|
|
## Ingress 基础
|
|||
|
|
|
|||
|
|
Service(NodePort/LoadBalancer)工作在四层(TCP/UDP),而 **Ingress** 工作在七层(HTTP/HTTPS),提供基于域名和路径的路由能力。
|
|||
|
|
|
|||
|
|
### Ingress Controller
|
|||
|
|
|
|||
|
|
Ingress 资源本身只是一份声明式配置,真正执行路由逻辑的是 **Ingress Controller**。常见的 Ingress Controller:
|
|||
|
|
|
|||
|
|
- **Nginx Ingress Controller**:最广泛使用,社区版 + F5 版
|
|||
|
|
- **Traefik**:自动服务发现,配置简单
|
|||
|
|
- **HAProxy**:高性能,企业级特性丰富
|
|||
|
|
- **Istio Gateway**:Service Mesh 生态中的网关
|
|||
|
|
|
|||
|
|
### Path-based 路由
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
apiVersion: networking.k8s.io/v1
|
|||
|
|
kind: Ingress
|
|||
|
|
metadata:
|
|||
|
|
name: path-based-ingress
|
|||
|
|
annotations:
|
|||
|
|
nginx.ingress.kubernetes.io/rewrite-target: /
|
|||
|
|
spec:
|
|||
|
|
ingressClassName: nginx
|
|||
|
|
rules:
|
|||
|
|
- host: app.example.com
|
|||
|
|
http:
|
|||
|
|
paths:
|
|||
|
|
- path: /api
|
|||
|
|
pathType: Prefix
|
|||
|
|
backend:
|
|||
|
|
service:
|
|||
|
|
name: api-svc
|
|||
|
|
port:
|
|||
|
|
number: 80
|
|||
|
|
- path: /web
|
|||
|
|
pathType: Prefix
|
|||
|
|
backend:
|
|||
|
|
service:
|
|||
|
|
name: web-svc
|
|||
|
|
port:
|
|||
|
|
number: 80
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Host-based 路由
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
apiVersion: networking.k8s.io/v1
|
|||
|
|
kind: Ingress
|
|||
|
|
metadata:
|
|||
|
|
name: host-based-ingress
|
|||
|
|
spec:
|
|||
|
|
ingressClassName: nginx
|
|||
|
|
rules:
|
|||
|
|
- host: api.example.com
|
|||
|
|
http:
|
|||
|
|
paths:
|
|||
|
|
- path: /
|
|||
|
|
pathType: Prefix
|
|||
|
|
backend:
|
|||
|
|
service:
|
|||
|
|
name: api-svc
|
|||
|
|
port:
|
|||
|
|
number: 80
|
|||
|
|
- host: web.example.com
|
|||
|
|
http:
|
|||
|
|
paths:
|
|||
|
|
- path: /
|
|||
|
|
pathType: Prefix
|
|||
|
|
backend:
|
|||
|
|
service:
|
|||
|
|
name: web-svc
|
|||
|
|
port:
|
|||
|
|
number: 80
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### TLS 终止
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
apiVersion: networking.k8s.io/v1
|
|||
|
|
kind: Ingress
|
|||
|
|
metadata:
|
|||
|
|
name: tls-ingress
|
|||
|
|
annotations:
|
|||
|
|
nginx.ingress.kubernetes.io/ssl-redirect: "true"
|
|||
|
|
spec:
|
|||
|
|
ingressClassName: nginx
|
|||
|
|
tls:
|
|||
|
|
- hosts:
|
|||
|
|
- app.example.com
|
|||
|
|
secretName: app-tls-secret # 存放证书的 Secret
|
|||
|
|
rules:
|
|||
|
|
- host: app.example.com
|
|||
|
|
http:
|
|||
|
|
paths:
|
|||
|
|
- path: /
|
|||
|
|
pathType: Prefix
|
|||
|
|
backend:
|
|||
|
|
service:
|
|||
|
|
name: my-app-svc
|
|||
|
|
port:
|
|||
|
|
number: 80
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
证书需要提前创建为 Kubernetes Secret:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
kubectl create secret tls app-tls-secret \
|
|||
|
|
--cert=tls.crt \
|
|||
|
|
--key=tls.key
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
flowchart LR
|
|||
|
|
A[Client] -->|HTTPS| B[Ingress Controller<br/>TLS 终止]
|
|||
|
|
B -->|HTTP| C[Service: my-app-svc]
|
|||
|
|
C --> D[Pod A]
|
|||
|
|
C --> E[Pod B]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## NetworkPolicy
|
|||
|
|
|
|||
|
|
默认情况下,Kubernetes 集群中所有 Pod 之间可以互相通信。**NetworkPolicy** 允许你定义精细的网络访问控制规则。
|
|||
|
|
|
|||
|
|
> [!warning] 前提条件
|
|||
|
|
> NetworkPolicy 需要 CNI 插件支持。Flannel 不支持 NetworkPolicy,Calico 和 Cilium 支持。
|
|||
|
|
|
|||
|
|
### 默认全通策略
|
|||
|
|
|
|||
|
|
如果不创建任何 NetworkPolicy,所有流量都是放行的:
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
flowchart LR
|
|||
|
|
A[Pod A] -->|允许| B[Pod B]
|
|||
|
|
B -->|允许| A
|
|||
|
|
A -->|允许| C[Pod C]
|
|||
|
|
C -->|允许| B
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 限制 Pod 间通信
|
|||
|
|
|
|||
|
|
**示例:只允许前端 Pod 访问后端 Pod 的 8080 端口**
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
apiVersion: networking.k8s.io/v1
|
|||
|
|
kind: NetworkPolicy
|
|||
|
|
metadata:
|
|||
|
|
name: backend-policy
|
|||
|
|
namespace: default
|
|||
|
|
spec:
|
|||
|
|
podSelector:
|
|||
|
|
matchLabels:
|
|||
|
|
app: backend # 策略作用于 backend Pod
|
|||
|
|
policyTypes:
|
|||
|
|
- Ingress
|
|||
|
|
ingress:
|
|||
|
|
- from:
|
|||
|
|
- podSelector:
|
|||
|
|
matchLabels:
|
|||
|
|
app: frontend # 只允许来自 frontend Pod
|
|||
|
|
ports:
|
|||
|
|
- protocol: TCP
|
|||
|
|
port: 8080 # 只允许访问 8080 端口
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**示例:限制出站流量,只允许访问指定 DNS**
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
apiVersion: networking.k8s.io/v1
|
|||
|
|
kind: NetworkPolicy
|
|||
|
|
metadata:
|
|||
|
|
name: restrict-egress
|
|||
|
|
namespace: default
|
|||
|
|
spec:
|
|||
|
|
podSelector:
|
|||
|
|
matchLabels:
|
|||
|
|
app: my-app
|
|||
|
|
policyTypes:
|
|||
|
|
- Egress
|
|||
|
|
egress:
|
|||
|
|
- to:
|
|||
|
|
- namespaceSelector:
|
|||
|
|
matchLabels:
|
|||
|
|
name: kube-system # 允许访问 kube-system 命名空间
|
|||
|
|
ports:
|
|||
|
|
- protocol: UDP
|
|||
|
|
port: 53 # 允许 DNS 查询
|
|||
|
|
- to:
|
|||
|
|
- ipBlock:
|
|||
|
|
cidr: 10.0.0.0/8 # 允许访问内网
|
|||
|
|
ports:
|
|||
|
|
- protocol: TCP
|
|||
|
|
port: 443
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
> [!info] 策略叠加规则
|
|||
|
|
> - 一旦某个 Pod 被任何 NetworkPolicy 选中,未被策略明确允许的流量将被**拒绝**
|
|||
|
|
> - 多个 NetworkPolicy 取**并集**(任一策略允许即放行)
|
|||
|
|
> - 只定义 `policyTypes: [Ingress]` 不影响出站,反之亦然
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
flowchart TB
|
|||
|
|
subgraph 策略生效后
|
|||
|
|
A[frontend Pod] -->|允许: TCP 8080| B[backend Pod]
|
|||
|
|
C[other Pod] -->|拒绝| B
|
|||
|
|
B -->|未定义 Egress 策略<br/>默认放行| D[任意目标]
|
|||
|
|
end
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 常见陷阱与最佳实践
|
|||
|
|
|
|||
|
|
### Service 无法访问排查清单
|
|||
|
|
|
|||
|
|
当 Service 访问不通时,按以下顺序排查:
|
|||
|
|
|
|||
|
|
1. **确认 Pod 运行正常**
|
|||
|
|
```bash
|
|||
|
|
kubectl get pods -l app=my-app -o wide
|
|||
|
|
kubectl logs <pod-name>
|
|||
|
|
kubectl describe pod <pod-name>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
2. **确认 Endpoints 已注册**
|
|||
|
|
```bash
|
|||
|
|
kubectl get endpoints my-app-svc
|
|||
|
|
# 如果 ENDPOINTS 为 <none>,说明没有健康的 Pod 匹配 selector
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
3. **确认 selector 匹配**
|
|||
|
|
```bash
|
|||
|
|
# 对比 Service selector 和 Pod labels
|
|||
|
|
kubectl get svc my-app-svc -o jsonpath='{.spec.selector}'
|
|||
|
|
kubectl get pods --show-labels
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
4. **确认端口正确**
|
|||
|
|
```bash
|
|||
|
|
# 检查 targetPort 是否与 Pod 实际监听端口一致
|
|||
|
|
kubectl get svc my-app-svc -o yaml | grep -A5 ports
|
|||
|
|
kubectl exec <pod-name> -- ss -tlnp
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
5. **确认 DNS 解析**
|
|||
|
|
```bash
|
|||
|
|
kubectl run test --rm -it --image=busybox -- nslookup my-app-svc
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
6. **确认网络策略未阻断**
|
|||
|
|
```bash
|
|||
|
|
kubectl get networkpolicy -n <namespace>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
7. **确认 kube-proxy 正常**
|
|||
|
|
```bash
|
|||
|
|
kubectl get pods -n kube-system -l k8s-app=kube-proxy
|
|||
|
|
# 检查 iptables 规则是否存在
|
|||
|
|
iptables -t nat -L KUBE-SERVICES | grep my-app-svc
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Session Affinity
|
|||
|
|
|
|||
|
|
默认情况下 Service 使用轮询负载均衡。如果需要会话保持(同一客户端始终访问同一 Pod),可以启用 Session Affinity:
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
apiVersion: v1
|
|||
|
|
kind: Service
|
|||
|
|
metadata:
|
|||
|
|
name: sticky-svc
|
|||
|
|
spec:
|
|||
|
|
selector:
|
|||
|
|
app: my-app
|
|||
|
|
sessionAffinity: ClientIP # 基于客户端 IP 保持会话
|
|||
|
|
sessionAffinityConfig:
|
|||
|
|
clientIP:
|
|||
|
|
timeoutSeconds: 1800 # 会话超时时间(默认 10800 秒)
|
|||
|
|
ports:
|
|||
|
|
- port: 80
|
|||
|
|
targetPort: 8080
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
> [!warning] Session Affinity 注意事项
|
|||
|
|
> - 在 iptables 模式下,基于源 IP 的会话保持可能在 NAT 环境下失效
|
|||
|
|
> - IPVS 模式支持更精细的会话保持(`sh` 算法)
|
|||
|
|
> - 对于 HTTP 应用,更推荐使用 Cookie-based Affinity(在 Ingress 层实现)
|
|||
|
|
|
|||
|
|
### External Traffic Policy
|
|||
|
|
|
|||
|
|
控制外部流量如何路由到 Pod,只对 NodePort 和 LoadBalancer 类型 Service 生效:
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
apiVersion: v1
|
|||
|
|
kind: Service
|
|||
|
|
metadata:
|
|||
|
|
name: my-app-svc
|
|||
|
|
spec:
|
|||
|
|
type: LoadBalancer
|
|||
|
|
externalTrafficPolicy: Local # 或 Cluster(默认)
|
|||
|
|
selector:
|
|||
|
|
app: my-app
|
|||
|
|
ports:
|
|||
|
|
- port: 80
|
|||
|
|
targetPort: 8080
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
| 策略 | 行为 | 优点 | 缺点 |
|
|||
|
|
|------|------|------|------|
|
|||
|
|
| **Cluster(默认)** | 流量可能被转发到其他节点上的 Pod | 负载均衡更均匀 | 多一跳转发,丢失源 IP |
|
|||
|
|
| **Local** | 流量只转发到本节点上的 Pod | 保留源 IP,减少转发延迟 | Pod 分布不均时负载不均衡 |
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
flowchart TB
|
|||
|
|
subgraph "externalTrafficPolicy: Cluster"
|
|||
|
|
A1[Client] -->|请求| B1[Node 1]
|
|||
|
|
B1 -->|可能转发| C1[Node 2 的 Pod]
|
|||
|
|
B1 -->|本地| D1[Node 1 的 Pod]
|
|||
|
|
end
|
|||
|
|
subgraph "externalTrafficPolicy: Local"
|
|||
|
|
A2[Client] -->|请求| B2[Node 1]
|
|||
|
|
B2 -->|仅本地| D2[Node 1 的 Pod]
|
|||
|
|
B2 -.->|不转发| C2[Node 2 的 Pod]
|
|||
|
|
end
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 其他最佳实践
|
|||
|
|
|
|||
|
|
- **使用命名空间隔离 Service**:避免命名冲突,便于权限管理
|
|||
|
|
- **合理设置 readinessProbe**:Pod 未就绪时不会被加入 Endpoints,避免流量打到未准备好的实例
|
|||
|
|
- **避免使用 ExternalName 映射到集群内部 Service**:可能导致 DNS 解析循环
|
|||
|
|
- **大规模集群启用 EndpointSlices**:减少 API Server 和 kube-proxy 的压力
|
|||
|
|
- **生产环境使用 IPVS 模式**:在 Service 数量较多时性能显著优于 iptables
|
|||
|
|
- **为关键 Service 配置 PodDisruptionBudget**:确保滚动更新和节点维护时服务可用性
|
|||
|
|
|
|||
|
|
## 延伸阅读
|
|||
|
|
|
|||
|
|
- [[k8s-01-overview]] — Kubernetes 基础概念总览
|
|||
|
|
- [[k8s-04-deployment-and-replicaset]] — Deployment 与 ReplicaSet 详解
|