Service 类型与网络访问
核心问题:Pod IP 会随着重建而改变——怎样给应用一个稳定的访问入口?外部用户怎样访问集群内的服务?
为什么需要 Service
Pod 是短暂的,它的 IP 在每次重建后都会改变。Service 提供一个稳定的虚拟 IP(ClusterIP)和 DNS 名称,始终指向满足条件的 Pod 集合。
graph LR
CLIENT["客户端
或其他 Pod"] SVC["Service
10.96.10.100
(ClusterIP,永远不变)"] P1["Pod A
10.244.1.5"] P2["Pod B
10.244.2.8"] P3["Pod C
10.244.3.12"] CLIENT --> SVC SVC -->|kube-proxy 负载均衡| P1 SVC -->|kube-proxy 负载均衡| P2 SVC -->|kube-proxy 负载均衡| P3
或其他 Pod"] SVC["Service
10.96.10.100
(ClusterIP,永远不变)"] P1["Pod A
10.244.1.5"] P2["Pod B
10.244.2.8"] P3["Pod C
10.244.3.12"] CLIENT --> SVC SVC -->|kube-proxy 负载均衡| P1 SVC -->|kube-proxy 负载均衡| P2 SVC -->|kube-proxy 负载均衡| P3
Service 通过 Label Selector 关联 Pod
# Service 选择所有 app=api 的 Pod
apiVersion: v1
kind: Service
metadata:
name: api-service
namespace: production
spec:
selector:
app: api-server # 匹配 Pod 的 labels
ports:
- name: http
protocol: TCP
port: 80 # Service 对外暴露的端口
targetPort: 3000 # Pod 上实际的端口(containerPort)
四种 Service 类型
ClusterIP(默认)
仅集群内部可访问,分配一个虚拟 IP。微服务间调用的标准方式:
spec:
type: ClusterIP # 默认值,可省略
selector:
app: api-server
ports:
- port: 80
targetPort: 3000
# 其他 Pod 通过 DNS 访问:
# http://api-service.production.svc.cluster.local:80
# 同 Namespace 内简写:http://api-service:80
NodePort
在每个节点上暴露一个端口(30000–32767),适合开发测试:
spec:
type: NodePort
selector:
app: api-server
ports:
- port: 80 # ClusterIP 端口(内部访问)
targetPort: 3000 # Pod 端口
nodePort: 30080 # 节点端口(可省略,自动分配)
访问方式:http://<任意节点IP>:30080
⚠️ 生产环境不推荐:端口范围有限,需要在安全组开放所有节点的这个端口。
LoadBalancer
在云平台上自动创建云负载均衡器(ALB / NLB / GCE LB),分配公网 IP:
spec:
type: LoadBalancer
selector:
app: api-server
ports:
- port: 80
targetPort: 3000
# AWS 注解(可选)
annotations:
service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing"
kubectl get service api-service -n production
# NAME TYPE CLUSTER-IP EXTERNAL-IP
# api-service LoadBalancer 10.96.10.100 52.1.2.3 ← 公网 IP
⚠️ 每个 LoadBalancer Service 都会创建一个云负载均衡器,费用高昂。生产建议用 Ingress 统一入口。
ExternalName
将 Service 映射到集群外部的 DNS 名称:
spec:
type: ExternalName
externalName: rds-endpoint.ap-southeast-1.rds.amazonaws.com
集群内 Pod 通过 db-service.production.svc.cluster.local 访问外部 RDS。
Ingress:HTTP 路由层
Ingress 是 HTTP/HTTPS 的 L7 路由规则,通过 Ingress Controller 实现:
graph LR
USER["用户请求"]
LB["云 LoadBalancer
(一个,共享成本)"] ING["Ingress Controller
(Nginx / Traefik / ALB)"] SVC1["api-service
:80"] SVC2["web-service
:80"] SVC3["admin-service
:80"] USER --> LB --> ING ING -->|api.example.com/v1| SVC1 ING -->|www.example.com| SVC2 ING -->|admin.example.com| SVC3
(一个,共享成本)"] ING["Ingress Controller
(Nginx / Traefik / ALB)"] SVC1["api-service
:80"] SVC2["web-service
:80"] SVC3["admin-service
:80"] USER --> LB --> ING ING -->|api.example.com/v1| SVC1 ING -->|www.example.com| SVC2 ING -->|admin.example.com| SVC3
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: main-ingress
namespace: production
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/ssl-redirect: "true"
cert-manager.io/cluster-issuer: "letsencrypt-prod" # 自动 TLS
spec:
ingressClassName: nginx
tls:
- hosts:
- api.example.com
- www.example.com
secretName: example-com-tls
rules:
- host: api.example.com
http:
paths:
- path: /v1
pathType: Prefix
backend:
service:
name: api-service
port:
number: 80
- host: www.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-service
port:
number: 80
安装 Ingress Controller
# Nginx Ingress Controller(最通用)
helm upgrade --install ingress-nginx ingress-nginx \
--repo https://kubernetes.github.io/ingress-nginx \
--namespace ingress-nginx \
--create-namespace \
--set controller.replicaCount=2 \
--set controller.metrics.enabled=true
# AWS Load Balancer Controller(EKS 原生方案)
helm upgrade --install aws-load-balancer-controller \
eks/aws-load-balancer-controller \
-n kube-system \
--set clusterName=prod-cluster \
--set serviceAccount.create=false \
--set serviceAccount.name=aws-load-balancer-controller
NetworkPolicy:Pod 间网络隔离
默认情况下,所有 Pod 之间互通。NetworkPolicy 实现微隔离:
# 只允许来自 api 服务的请求到达 db
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: db-access-policy
namespace: production
spec:
podSelector:
matchLabels:
app: postgres # 应用于 postgres Pod
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
app: api-server # 只允许 api-server Pod 进入
ports:
- protocol: TCP
port: 5432
egress:
- to:
- namespaceSelector:
matchLabels:
name: kube-system # 允许 DNS 查询
ports:
- protocol: UDP
port: 53
DNS 解析规则
<service>.<namespace>.svc.cluster.local
例:
api-service.production.svc.cluster.local → 完整 FQDN
api-service.production → 跨 Namespace 简写
api-service → 同 Namespace 内访问
# 在 Pod 内调试 DNS
kubectl run -it dns-test --image=busybox --rm -- nslookup api-service.production
# 查看 Service 的 Endpoints(确认 Pod 正确关联)
kubectl get endpoints api-service -n production
kubectl describe service api-service -n production
常见错误
| 问题 | 原因 | 解决 |
|---|---|---|
| Service 有 ClusterIP 但无 Endpoints | Pod 的 labels 与 selector 不匹配 | kubectl get endpoints 确认,检查 label |
| Ingress 无法路由(404 / 503) | Ingress Controller 未安装,或 ingressClassName 不对 | 检查 kubectl get ingressclass |
| 集群内 DNS 解析失败 | CoreDNS 崩溃 | kubectl get pods -n kube-system 检查 coredns 状态 |
| LoadBalancer 一直 Pending | 云平台权限不足,无法创建负载均衡器 | 检查 Node IAM Role 是否有 elasticloadbalancing:* 权限 |