Kubernetes 服务暴露终极指南:从 Service 到 Ingress,深入理解 kube-proxy 与流量治理

一文搞懂 K8s 服务发现、负载均衡、kube-proxy 三种模式、Ingress 部署与 12 个生产级规则实战


写在前面

在 Kubernetes 集群中,Pod 是动态的——它们随时可能被重建、迁移、扩缩容。如何让客户端(无论是集群内部还是外部)稳定、可靠地访问这些不断变化的 Pod?答案是 ServiceIngress

  • Service 提供稳定的访问入口(ClusterIP、NodePort、LoadBalancer),并负责 Pod 的负载均衡。
  • Ingress 提供七层(HTTP/HTTPS)路由能力,支持域名、路径分发、SSL 终止、重写、限流等高级功能。

本文将系统讲解:

  • Service 的四种类型与三种服务发现方式
  • 会话保持与金丝雀发布
  • kube-proxy 的 iptables 与 IPVS 模式原理(附实验验证)
  • Ingress 控制器(ingress-nginx)部署
  • 12 个生产级 Ingress 规则(多域名、路径重写、HTTPS 强制、限流、跨域、白名单等)

无论你是刚入门的 K8s 新手,还是正在优化集群网络的运维工程师,这篇实战指南都能帮你彻底搞懂“怎么把服务放出去”。


一、Service:K8s 的稳定访问入口

1.1 为什么需要 Service?

Pod 有自己的 IP,但 Pod 随时可能被删除重建(IP 变化)。Service 通过标签选择器动态关联一组 Pod,并提供固定的 ClusterIP 和 DNS 名称,实现:

  • 服务发现:客户端只需访问 Service 的固定地址
  • 负载均衡:流量在 Service 后端的所有健康 Pod 之间分发
  • 解耦:无需关心 Pod 的 IP 变化

1.2 创建 Service 的基本方式

# 方式1:命令行 expose
kubectl expose deployment web --port=8080 --target-port=80

# 方式2:YAML 文件
apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  selector:
    app: web
  ports:
  - port: 8080
    targetPort: 80
  type: ClusterIP

二、Service 的三种发现方式

集群内的 Pod 如何访问 Service?有三种途径:

2.1 直接访问 ClusterIP

kubectl get svc web   # 看到 ClusterIP: 10.103.19.150
curl http://10.103.19.150:8080

2.2 环境变量

Kubernetes 会在 Pod 启动时注入 Service 相关的环境变量:

kubectl run test --rm -it --image=busybox -- sh
/ # env | grep WEB_SERVICE
WEB_SERVICE_HOST=10.103.19.150
WEB_SERVICE_PORT=8080

2.3 DNS(CoreDNS)

Kubeadm 部署的集群默认安装 CoreDNS。Pod 可通过 <service>.<namespace>.svc.cluster.local 访问。

# 在 Pod 内
wget -qO- http://web.services.svc.cluster.local:8080
# 同一 Namespace 下可简写为
wget -qO- http://web:8080

三、Service 的四种类型

3.1 ClusterIP(默认)

仅集群内部可访问。适合内部微服务间通信。

type: ClusterIP

3.2 NodePort

在每个节点上开放一个固定端口(默认 30000-32767),外部可通过 <NodeIP>:<NodePort> 访问。

kubectl expose deployment web --type=NodePort --port=8080 --target-port=80
kubectl get svc web   # 输出:8080:31917/TCP
curl http://10.1.8.31:31917

原理:kube-proxy 在每个节点监听该端口,并将流量转发到 ClusterIP。

3.3 LoadBalancer

需要云厂商或本地 LB 方案(如 MetalLB)。它会在 ClusterIP 和 NodePort 基础上再分配一个外部 IP。

注意:裸机环境需部署 MetalLB。

3.4 Headless Service

设置 clusterIP: None,不分配 ClusterIP,直接返回 Pod IP 列表(通过 DNS A 记录)。适合 StatefulSet 等需要直连 Pod 的场景。

spec:
  clusterIP: None
  selector:
    app: web

四、会话保持(Session Affinity)

默认 Service 轮询分发请求。若需同一客户端始终访问同一 Pod,启用 sessionAffinity: ClientIP

kubectl patch svc web -p '{"spec":{"sessionAffinity":"ClientIP"}}'

可调整超时时间(默认 10800 秒):

kubectl patch svc web -p '{"spec":{"sessionAffinityConfig":{"clientIP":{"timeoutSeconds":3600}}}}'

五、金丝雀发布与 Service 配合

通过 两个 Deployment + 同一个 Service 实现流量灰度:

  • 稳定版 Deployment 带有 track: stable 标签,副本数 10
  • 金丝雀版 Deployment 带有 track: canary 标签,副本数 1
  • Service 的 selector 只选择 app=web(不含 track),因此两个版本都在后端

逐步调整副本数,即可按比例分配流量。

kubectl scale deployment web-stable --replicas=8
kubectl scale deployment web-canary --replicas=2
# 此时 20% 流量进入 canary

六、kube-proxy 工作原理

kube-proxy 是 K8s 的网络代理组件,负责实现 Service 的转发。它有三种模式:

模式地位性能特点
iptables默认中(<1000 服务)规则链线性匹配,大规模时性能下降
IPVS推荐极高(10w+ 服务)内核哈希表,O(1) 查找,支持多种算法
Userspace已废弃(1.25+)用户态转发,性能差

6.1 iptables 模式详解

当 Service 创建后,kube-proxy 在 nat 表中生成规则链:

  • KUBE-SERVICES:总入口
  • KUBE-SVC-XXX:每个 Service 的负载均衡链(随机概率分发)
  • KUBE-SEP-XXX:每个 Endpoint 的 DNAT 链

验证

iptables-save | grep <Service-ClusterIP>

6.2 IPVS 模式(生产首选)

IPVS 基于内核哈希表,性能远超 iptables,且支持多种调度算法:rr、wrr、lc、sh 等。

切换 IPVS 模式

kubectl edit configmap -n kube-system kube-proxy
# 修改 mode: "ipvs"
kubectl rollout restart ds -n kube-system kube-proxy

验证

kubectl logs -n kube-system kube-proxy-xxx | grep "Using ipvs Proxier"
ipvsadm -Ln   # 查看虚拟服务器和 Real Server

七、Ingress:七层路由网关

7.1 Ingress 是什么?

Ingress 是 K8s 的 API 资源,定义 HTTP/HTTPS 路由规则(域名、路径)。它本身不处理流量,需要 Ingress 控制器(如 ingress-nginx、Traefik)解析规则并配置实际的负载均衡器。

典型架构

Client --> Ingress Controller (Nginx) --> Service --> Pod

7.2 部署 ingress-nginx

本次选用 ingress-nginx 控制器(v1.11.2),支持 K8s 1.30。

# 下载部署文件(已修改镜像源)
kubectl apply -f deploy.yaml

部署后会创建:

  • 命名空间 ingress-nginx
  • LoadBalancer 类型的 Service(需 MetalLB 分配外部 IP)
  • Deployment + DaemonSet
kubectl get svc -n ingress-nginx
# 输出包含 EXTERNAL-IP(如 10.1.8.40)

7.3 Ingress 规则结构与基础示例

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-ingress
spec:
  ingressClassName: nginx
  rules:
  - host: www.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: webapp
            port:
              number: 80

八、生产级 Ingress 规则 12 项实战

以下实验基于两个测试服务:webapp01webapp02,均已暴露为 ClusterIP Service。

8.1 多域名虚拟主机

rules:
- host: webapp01.example.com
  http:
    paths:
    - path: /
      pathType: Prefix
      backend:
        service:
          name: webapp01
          port:
            number: 80
- host: webapp02.example.com
  http:
    paths:
    - path: /
      pathType: Prefix
      backend:
        service:
          name: webapp02
          port:
            number: 80

8.2 同一域名多路径分流

- host: www.example.com
  http:
    paths:
    - path: /
      pathType: Prefix
      backend:
        service:
          name: webapp01
          port:
            number: 80
    - path: /games
      pathType: Prefix
      backend:
        service:
          name: webapp02
          port:
            number: 80

8.3 路径重写(剥离前缀)

场景:前端访问 /webapp01/api/xxx,后端只认 /api/xxx

metadata:
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
  rules:
  - host: www.example.com
    http:
      paths:
      - path: /webapp01/(.*)
        pathType: ImplementationSpecific
        backend:
          service:
            name: webapp01
            port:
              number: 80

8.4 HTTPS 强制 + TLS 证书

# 生成自签名证书
openssl req -x509 -newkey rsa:2048 -nodes -keyout tls.key -out tls.crt -days 365 -subj "/CN=www.example.com"
kubectl create secret tls www-tls --key=tls.key --cert=tls.crt

Ingress 配置:

metadata:
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
spec:
  tls:
  - hosts:
    - www.example.com
    secretName: www-tls
  rules:
  - host: www.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: webapp01
            port:
              number: 80

8.5 限流(防刷)

annotations:
  nginx.ingress.kubernetes.io/limit-connections: "50"   # 单IP最大并发连接
  nginx.ingress.kubernetes.io/limit-rps: "20"           # 单IP每秒请求数

8.6 自定义超时

annotations:
  nginx.ingress.kubernetes.io/proxy-connect-timeout: "10"
  nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
  nginx.ingress.kubernetes.io/proxy-send-timeout: "60"

8.7 跨域 CORS

annotations:
  nginx.ingress.kubernetes.io/enable-cors: "true"
  nginx.ingress.kubernetes.io/cors-allow-origin: "*"
  nginx.ingress.kubernetes.io/cors-allow-methods: "GET,POST,PUT,DELETE,OPTIONS"

8.8 IP 白名单

annotations:
  nginx.ingress.kubernetes.io/whitelist-source-range: "10.1.8.0/24,192.168.0.0/16"

8.9 透传真实客户端 IP

annotations:
  nginx.ingress.kubernetes.io/x-forwarded-for: "true"
  nginx.ingress.kubernetes.io/proxy-real-ip-cidr: "10.0.0.0/8"

8.10 静态资源缓存

annotations:
  nginx.ingress.kubernetes.io/proxy-cache: "true"
  nginx.ingress.kubernetes.io/proxy-cache-valid: "200 302 10m"

8.11 金丝雀(权重分流)

annotations:
  nginx.ingress.kubernetes.io/canary: "true"
  nginx.ingress.kubernetes.io/canary-weight: "10"   # 10% 流量到金丝雀服务

8.12 自定义错误页面

annotations:
  nginx.ingress.kubernetes.io/custom-http-errors: "404,500,502,503"

九、总结与最佳实践

9.1 Service 选型建议

场景推荐类型
内部微服务通信ClusterIP
开发测试环境暴露NodePort
生产环境外部入口LoadBalancer + Ingress
StatefulSet 直连 PodHeadless Service

9.2 kube-proxy 选型

  • 小规模(<1000 服务):iptables 足够
  • 中大规模生产务必切换 IPVS,性能提升数倍

9.3 Ingress 最佳实践

  1. 生产环境必须启用 HTTPS 强制 + 真实证书
  2. 配置合理的 限流和超时,防止雪崩
  3. 使用 路径重写 剥离路由前缀,避免后端感知前端路径
  4. 内部管理后台加 IP 白名单
  5. 金丝雀发布通过 Ingress 注解实现,比手动调整 Service 更方便

9.4 常见问题排查

  • Service 无法访问:检查 selector 是否匹配 Pod label;targetPort 是否与容器端口一致
  • Ingress 404:确认 path 类型(Prefix/Exact)和重写规则是否正确
  • kube-proxy 切换 IPVS 失败:确认内核模块已加载(ip_vs 等),安装 ipvsadm 验证

写在最后

这篇文章我们从 Service 的基础概念一直讲到 Ingress 的十二个生产级规则,并深入解析了 kube-proxy 的两种核心模式。希望你能在实际集群中动手实验,把理论变成肌肉记忆。

本文所有 YAML 和命令均基于 K8s v1.30 + containerd + ingress-nginx v1.11.2 实测,请根据你的版本微调。

更多推荐