Kubernetes Service 类型详解:ClusterIP / NodePort / LoadBalancer / Headless

Source

Kubernetes 中的 Service 是一种抽象,用于定义一组 Pod 的访问策略。由于 Pod 是动态创建和销毁的,其 IP 地址会变化,Service 为客户端提供了稳定的访问入口(虚拟 IP 或 DNS),并实现了负载均衡。根据暴露方式的不同,Service 分为四种类型:ClusterIPNodePortLoadBalancer 和 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 的服务发现与负载均衡能力。