Kubernetes进阶:Service与Ingress

K8s 里 Pod 是临时的,IP 随时变。Service 和 Ingress 是解决服务暴露和流量管理的两个核心抽象,这篇梳理它们的原理和典型配置。

Service 的三种主要类型

ClusterIP(默认)

ClusterIP 只在集群内部可达,分配一个虚拟 IP,所有发往这个 VIP 的流量会被 kube-proxy 转发到后端 Pod:

apiVersion: v1
kind: Service
metadata:
  name: backend-svc
spec:
  type: ClusterIP
  selector:
    app: backend
  ports:
    - port: 80
      targetPort: 8080

集群内其他 Pod 可以用 backend-svc.default.svc.cluster.local 或直接 backend-svc 访问。这是最常用的类型,适合服务间内部通信。

NodePort

NodePort 在 ClusterIP 基础上,在每个 Node 上开一个端口(默认范围 30000-32767),外部可以通过 <NodeIP>:<NodePort> 访问:

apiVersion: v1
kind: Service
metadata:
  name: backend-nodeport
spec:
  type: NodePort
  selector:
    app: backend
  ports:
    - port: 80
      targetPort: 8080
      nodePort: 30080

适合开发测试环境,生产环境一般不直接用 NodePort,因为端口范围有限且不够灵活。

LoadBalancer

LoadBalancer 在 NodePort 基础上,自动向云厂商申请一个外部负载均衡器。在 AWS/GCP/Azure 等环境下,创建 LoadBalancer 类型的 Service 会自动配置 ELB/NLB 等:

apiVersion: v1
kind: Service
metadata:
  name: backend-lb
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-type: nlb
spec:
  type: LoadBalancer
  selector:
    app: backend
  ports:
    - port: 443
      targetPort: 8080

每个 LoadBalancer Service 都会创建一个独立的 LB 实例,成本较高。如果有多个服务需要暴露,用 Ingress 更经济。

DNS 服务发现

K8s 内置 CoreDNS,每个 Service 创建后自动注册 DNS 记录。解析规则:

  • 同 namespace:直接用 Service 名,如 backend-svc
  • 跨 namespace:backend-svc.other-namespace
  • 完整 FQDN:backend-svc.default.svc.cluster.local

对于 Headless Service(clusterIP: None),DNS 查询直接返回后端 Pod 的 IP 列表,而不是 VIP。这在 StatefulSet 场景下很有用,比如数据库集群的各节点需要直接互相访问。

Ingress 与 Ingress Controller

Ingress 是 K8s 定义的 HTTP/HTTPS 路由规则资源,但它本身不工作——需要部署一个 Ingress Controller 来实际执行路由。

最常用的是 Nginx Ingress Controller,部署方式通常是 Helm:

helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm install ingress-nginx ingress-nginx/ingress-nginx

一个基本的 Ingress 配置:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
  annotations:
    nginx.ingress.kubernetes.io/newrite-target: /
spec:
  ingressClassName: nginx
  rules:
    - host: api.example.com
      http:
        paths:
          - path: /v1
            pathType: Prefix
            backend:
              service:
                name: api-v1-svc
                port:
                  number: 80
          - path: /v2
            pathType: Prefix
            backend:
              service:
                name: api-v2-svc
                port:
                  number: 80
    - host: web.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: web-svc
                port:
                  number: 80

一个 Ingress Controller 可以服务多个 Ingress 规则,多个域名/路径共享一个 LB,比给每个服务配 LoadBalancer 省钱得多。

TLS 配置

Ingress 支持 TLS 终止,把证书配成 Secret 后引用即可:

kubectl create secret tls app-tls \
  --cert=tls.crt \
  --key=tls.key
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress-tls
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - api.example.com
        - web.example.com
      secretName: app-tls
  rules:
    - host: api.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: api-svc
                port:
                  number: 80

生产环境推荐配合 cert-manager 自动签发和续期 Let's Encrypt 证书,省去手动管理证书的麻烦。

实践建议

几点经验总结:

  • 集群内部通信一律用 ClusterIP + DNS,不要硬编码 IP
  • 需要对外暴露 HTTP 服务时优先用 Ingress,一个入口管多个服务
  • Ingress annotations 很强大,限速、重写、CORS、认证等都可以在 annotation 层面配置
  • 注意 Ingress Controller 本身的高可用,至少部署 2 个副本
  • 灰度发布可以利用 Nginx Ingress 的 canary annotation,按权重或 header 分流