Kubernetes 中的 Service 是一种抽象,用于定义一组 Pod 的访问策略。由于 Pod 是动态创建和销毁的,其 IP 地址会变化,Service 为客户端提供了稳定的访问入口(虚拟 IP 或 DNS),并实现了负载均衡。根据暴露方式的不同,Service 分为四种类型:ClusterIP、NodePort、LoadBalancer 和 Headless。本文将逐一介绍它们的概念,并通过完整实例演示其用法。
1. ClusterIP 类型的 Service
ClusterIP 是默认的 Service 类型。它在集群内部创建一个虚拟 IP(ClusterIP),仅允许集群内部的客户端(如其他 Pod)访问。流量通过 kube-proxy 转发到后端 Pod,并自动实现负载均衡。
实例:创建一个 ClusterIP Service,将 8000 端口映射到后端 Nginx 的 80 端口
1.1 准备后端 Pod(使用 Deployment)
bash
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
EOF
1.2 创建 ClusterIP Service
bash
cat > clusterip.yaml <<EOF
apiVersion: v1
kind: Service
metadata:
name: my-clusterip-service
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 8000
targetPort: 80
EOF
kubectl create -f clusterip.yaml
1.3 查看 Service
bash
kubectl get service my-clusterip-service
输出示例:
text
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE my-clusterip-service ClusterIP 10.108.51.155 <none> 8000/TCP 18s
1.4 在集群内部访问 Service
启动一个临时 Pod 测试:
bash
kubectl run curl-test --image=curlimages/curl --rm -it --restart=Never -- curl http://10.108.51.155:8000
或直接使用 curl(如果从集群节点执行):
bash
curl http://10.108.51.155:8000
输出应显示 Nginx 欢迎页。
1.5 清理
bash
kubectl delete -f clusterip.yaml kubectl delete deployment nginx-deployment
2. NodePort 类型的 Service
NodePort 在 ClusterIP 的基础上,在每个节点上开放一个静态端口(默认 30000-32767)。外部客户端可以通过 任意节点IP:NodePort 访问服务。流量进入节点端口后,继续通过 ClusterIP 转发到 Pod。
实例:创建一个 NodePort Service,暴露 8000 端口,节点端口设为 30080
2.1 准备后端 Pod(同上)
bash
kubectl create deployment nginx --image=nginx --replicas=2
2.2 创建 NodePort Service
bash
cat > nodeport.yaml <<EOF
apiVersion: v1
kind: Service
metadata:
name: my-nodeport-service
spec:
type: NodePort
selector:
app: nginx
ports:
- port: 8000 # Service 端口
targetPort: 80 # Pod 端口
nodePort: 30080 # 节点端口(可选,不指定则随机分配)
EOF
kubectl create -f nodeport.yaml
2.3 查看 Service
bash
kubectl get service my-nodeport-service
输出示例:
text
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE my-nodeport-service NodePort 10.104.12.34 <none> 8000:30080/TCP 10s
2.4 从外部访问
获取任意节点的 IP(例如 192.168.1.100),在浏览器或 curl 中访问:
bash
curl http://192.168.1.100:30080
2.5 清理
bash
kubectl delete -f nodeport.yaml kubectl delete deployment nginx
3. LoadBalancer 类型的 Service
LoadBalancer 在 NodePort 的基础上,利用云提供商(如 AWS、GCP、Azure)的负载均衡器,为 Service 分配一个公网 IP。外部流量通过该公网 IP 进入,然后分发到各节点的 NodePort(或直接到 Pod,取决于云厂商实现)。
实例:创建一个 LoadBalancer Service(假设在云环境下)
3.1 准备后端 Pod
bash
kubectl create deployment nginx --image=nginx --replicas=2
3.2 创建 LoadBalancer Service
bash
cat > loadbalancer.yaml <<EOF
apiVersion: v1
kind: Service
metadata:
name: my-loadbalancer-service
spec:
type: LoadBalancer
selector:
app: nginx
ports:
- protocol: TCP
port: 80 # 负载均衡器监听的端口
targetPort: 80
EOF
kubectl create -f loadbalancer.yaml
3.3 查看 Service 并等待 EXTERNAL-IP
bash
kubectl get service my-loadbalancer-service -w
初始时 EXTERNAL-IP 可能为 <pending>,等待云厂商分配后变为真实 IP:
text
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE my-loadbalancer-service LoadBalancer 10.106.211.14 35.223.144.15 80:31000/TCP 2m
3.4 通过公网 IP 访问
bash
curl http://35.223.144.15
3.5 清理
bash
kubectl delete -f loadbalancer.yaml kubectl delete deployment nginx
注意:在本地环境(如 minikube)可借助
minikube tunnel模拟 LoadBalancer,或使用 MetalLB 等工具。
4. Headless Service
Headless Service 不分配 ClusterIP,而是将 .spec.clusterIP 设为 None。客户端通过 DNS 查询 Service 名称时,会获得所有就绪 Pod 的 IP 列表(A 记录),适用于需要直接连接 Pod 的场景,如 StatefulSet 中的有状态应用。
实例:创建一个 Headless Service,并通过 DNS 发现后端 Pod
4.1 准备后端 Pod(带标签 app=nginx)
bash
kubectl create deployment nginx --image=nginx --replicas=2
4.2 创建 Headless Service
bash
cat > headless.yaml <<EOF
apiVersion: v1
kind: Service
metadata:
name: my-headless-service
spec:
clusterIP: None # 关键字段
selector:
app: nginx
ports:
- port: 80
targetPort: 80
EOF
kubectl create -f headless.yaml
4.3 查看 Service
bash
kubectl get service my-headless-service
输出示例:
text
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE my-headless-service ClusterIP None <none> 80/TCP 10s
4.4 通过 DNS 查询 Pod IP
启动一个用于 DNS 查询的 Pod(如 busybox):
bash
kubectl run dns-test --image=busybox:1.28 --rm -it --restart=Never -- nslookup my-headless-service.default.svc.cluster.local
输出示例:
text
Server: 10.96.0.10 Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local Name: my-headless-service.default.svc.cluster.local Address 1: 10.244.1.5 Address 2: 10.244.2.7
其中 Address 1 和 Address 2 即为两个 Nginx Pod 的 IP。
4.5 清理
bash
kubectl delete -f headless.yaml kubectl delete deployment nginx
总结
| 类型 | 是否分配 ClusterIP | 集群内访问方式 | 集群外访问方式 | 典型用途 |
|---|---|---|---|---|
| ClusterIP | 是 | ClusterIP:Port 或域名 | 无 | 内部服务通信 |
| NodePort | 是 | ClusterIP:Port | 节点IP:NodePort | 对外暴露服务(开发/测试) |
| LoadBalancer | 是 | ClusterIP:Port | 云负载均衡器公网IP | 生产环境对外暴露服务 |
| Headless | 否 | 域名返回 Pod IP 列表 | 无 | 有状态应用(如数据库)的直接连接 |
选择合适的 Service 类型,可以帮助我们灵活地将应用暴露给内部或外部客户端,并充分利用 Kubernetes 的服务发现与负载均衡能力。