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 分流