Service - 服务发现
Service - 服务发现
为什么需要 Service
Pod 的 IP 地址是动态变化的,会因为以下原因改变:
- Pod 重启(新 IP)
- Pod 扩缩容(增减 Pod)
- Pod 调度到不同节点
- 滚动更新(创建新 Pod,删除旧 Pod)
问题:如何稳定地访问一组 Pod?
答案:使用 Service!
Service 是什么
Service 是 Kubernetes 中的抽象概念,定义了一组 Pod 的访问策略。
核心功能:
- 服务发现:通过固定的 DNS 名称访问
- 负载均衡:自动分发流量到多个 Pod
- 稳定入口:提供不变的 ClusterIP
Service 类型
Kubernetes 提供四种 Service 类型:
1. ClusterIP(默认)
只在集群内部可访问,是最常用的类型。
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
type: ClusterIP
selector:
app: nginx
ports:
- protocol: TCP
port: 80 # Service 端口
targetPort: 80 # Pod 端口
kubectl apply -f service.yaml
# 查看 Service
kubectl get svc nginx-service
# 输出
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-service ClusterIP 10.96.123.45 <none> 80/TCP 1m
访问方式:
# 在集群内访问
curl http://10.96.123.45
# 通过 DNS 访问(推荐)
curl http://nginx-service
curl http://nginx-service.default.svc.cluster.local
2. NodePort
在每个节点上暴露一个端口,可从集群外访问。
apiVersion: v1
kind: Service
metadata:
name: nginx-nodeport
spec:
type: NodePort
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
nodePort: 30080 # 可选,不指定则自动分配 30000-32767
kubectl get svc nginx-nodeport
# 输出
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-nodeport NodePort 10.96.123.46 <none> 80:30080/TCP 1m
访问方式:
# 通过任意节点 IP + NodePort 访问
curl http://<NodeIP>:30080
# Minikube 可以直接获取 URL
minikube service nginx-nodeport --url
3. LoadBalancer
使用云服务商的负载均衡器(AWS ELB、GCP Load Balancer 等)。
apiVersion: v1
kind: Service
metadata:
name: nginx-lb
spec:
type: LoadBalancer
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
kubectl get svc nginx-lb
# 输出(云环境)
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-lb LoadBalancer 10.96.123.47 203.0.113.10 80:31234/TCP 1m
访问方式:
# 通过 EXTERNAL-IP 访问
curl http://203.0.113.10
4. ExternalName
将服务映射到外部 DNS 名称。
apiVersion: v1
kind: Service
metadata:
name: external-db
spec:
type: ExternalName
externalName: mysql.example.com
集群内访问 external-db 会被重定向到 mysql.example.com。
创建 Service
方法一:YAML 配置
apiVersion: v1
kind: Service
metadata:
name: webapp-service
labels:
app: webapp
spec:
type: ClusterIP
selector:
app: webapp
ports:
- name: http
protocol: TCP
port: 80
targetPort: 8080
- name: https
protocol: TCP
port: 443
targetPort: 8443
方法二:命令行创建
# 为 Deployment 创建 Service
kubectl expose deployment webapp --port=80 --target-port=8080
# 创建 NodePort Service
kubectl expose deployment webapp --type=NodePort --port=80
# 创建 LoadBalancer Service
kubectl expose deployment webapp --type=LoadBalancer --port=80
Service 工作原理
标签选择器
Service 通过标签选择器找到对应的 Pod:
# Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: webapp
spec:
replicas: 3
selector:
matchLabels:
app: webapp # Pod 标签
template:
metadata:
labels:
app: webapp # Pod 标签
spec:
containers:
- name: webapp
image: nginx
---
# Service
apiVersion: v1
kind: Service
metadata:
name: webapp-service
spec:
selector:
app: webapp # 匹配 Pod 标签
ports:
- port: 80
targetPort: 80
Endpoints
Service 会自动创建 Endpoints 对象,记录所有匹配的 Pod IP。
# 查看 Endpoints
kubectl get endpoints webapp-service
# 输出
NAME ENDPOINTS AGE
webapp-service 10.244.0.5:80,10.244.0.6:80,... 1m
# 详细信息
kubectl describe endpoints webapp-service
服务发现
DNS 解析
Kubernetes 提供内置 DNS 服务(CoreDNS),自动为 Service 创建 DNS 记录。
DNS 格式:
<service-name>.<namespace>.svc.cluster.local
示例:
# 同命名空间内访问
curl http://nginx-service
# 跨命名空间访问
curl http://nginx-service.default
# 完整域名
curl http://nginx-service.default.svc.cluster.local
环境变量
Pod 创建时,Kubernetes 会注入 Service 相关的环境变量:
# 进入 Pod
kubectl exec -it <pod-name> -- env | grep SERVICE
# 输出
NGINX_SERVICE_SERVICE_HOST=10.96.123.45
NGINX_SERVICE_SERVICE_PORT=80
NGINX_SERVICE_PORT=tcp://10.96.123.45:80
注意:只有 Pod 创建时已存在的 Service 才会注入环境变量。
会话亲和性
默认情况下,Service 随机分发请求。可以配置会话亲和性:
apiVersion: v1
kind: Service
metadata:
name: webapp-service
spec:
selector:
app: webapp
sessionAffinity: ClientIP # 基于客户端 IP 的会话保持
sessionAffinityConfig:
clientIP:
timeoutSeconds: 10800 # 3 小时
ports:
- port: 80
targetPort: 8080
Headless Service
不需要负载均衡和单独的 ClusterIP,直接返回所有 Pod IP。
apiVersion: v1
kind: Service
metadata:
name: webapp-headless
spec:
clusterIP: None # 关键:设置为 None
selector:
app: webapp
ports:
- port: 80
targetPort: 8080
用途:
- StatefulSet 中的 Pod 身份识别
- 自己实现负载均衡逻辑
- 服务网格(Service Mesh)
# DNS 查询返回所有 Pod IP
nslookup webapp-headless
# 输出
Name: webapp-headless.default.svc.cluster.local
Address: 10.244.0.5
Address: 10.244.0.6
Address: 10.244.0.7
多端口 Service
一个 Service 可以暴露多个端口:
apiVersion: v1
kind: Service
metadata:
name: webapp-service
spec:
selector:
app: webapp
ports:
- name: http
protocol: TCP
port: 80
targetPort: 8080
- name: https
protocol: TCP
port: 443
targetPort: 8443
- name: metrics
protocol: TCP
port: 9090
targetPort: 9090
实战示例
完整的 Deployment + Service
# Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: webapp
spec:
replicas: 3
selector:
matchLabels:
app: webapp
template:
metadata:
labels:
app: webapp
spec:
containers:
- name: webapp
image: nginx:1.21
ports:
- containerPort: 80
---
# ClusterIP Service
apiVersion: v1
kind: Service
metadata:
name: webapp-clusterip
spec:
type: ClusterIP
selector:
app: webapp
ports:
- port: 80
targetPort: 80
---
# NodePort Service
apiVersion: v1
kind: Service
metadata:
name: webapp-nodeport
spec:
type: NodePort
selector:
app: webapp
ports:
- port: 80
targetPort: 80
nodePort: 30080
测试服务
# 应用配置
kubectl apply -f webapp.yaml
# 查看资源
kubectl get deployment,svc,pod
# 测试 ClusterIP(集群内)
kubectl run test-pod --image=busybox --rm -it -- sh
wget -O- http://webapp-clusterip
# 测试 NodePort(集群外)
curl http://<NodeIP>:30080
# Minikube
minikube service webapp-nodeport
常用命令
# 创建 Service
kubectl expose deployment <name> --port=80
kubectl apply -f service.yaml
# 查看 Service
kubectl get svc
kubectl get svc <name>
kubectl describe svc <name>
# 查看 Endpoints
kubectl get endpoints <name>
# 查看 Service 详情
kubectl get svc <name> -o yaml
# 删除 Service
kubectl delete svc <name>
# 端口转发(本地测试)
kubectl port-forward svc/<name> 8080:80
最佳实践
1. 使用有意义的名称
metadata:
name: user-api-service # 好
# name: service1 # 不好
2. 明确指定端口名称
ports:
- name: http # 明确
port: 80
- name: grpc # 明确
port: 9090
3. 生产环境使用 ClusterIP + Ingress
不要直接使用 NodePort 暴露服务到外网,而是:
外部流量 → Ingress → Service (ClusterIP) → Pods
4. 健康检查配合 Service
readinessProbe:
httpGet:
path: /health
port: 8080
只有就绪的 Pod 才会被加入 Service 的 Endpoints。
小结
Service 是 Kubernetes 中实现服务发现和负载均衡的核心资源:
- ClusterIP:集群内部访问(最常用)
- NodePort:通过节点端口访问
- LoadBalancer:使用云负载均衡器
- ExternalName:映射外部服务
关键概念:
- 通过标签选择器关联 Pod
- 自动创建 DNS 记录
- 自动更新 Endpoints
- 内置负载均衡
下一章我们将学习 ConfigMap 和 Secret,实现配置管理。