一、为什么需要 Service?

Pod 是临时的:当 Pod 故障重启、扩缩容或节点故障时,Pod 的 IP 会变化。如果客户端直接用 Pod IP 访问,会因为 IP 变动导致服务中断。Service 的作用:

  1. 固定入口:给一组 Pod 分配一个稳定的访问地址(IP 或域名)。
  2. 负载均衡:自动把请求分发到后端多个 Pod 上,实现流量分担。
  3. 服务发现:通过 Kubernetes 内部 DNS,让集群内的 Pod 可以用 Service 名称访问服务(无需记 IP)。

二、Service 的核心概念

  • 标签选择器(Selector):Service 通过标签关联后端 Pod(类似 “按标签找人”)。
  • 端口映射:Service 暴露一个端口,转发到 Pod 的实际端口(如 Service 的 80 端口转发到 Pod 的 80 端口)。
  • 类型(Type):不同类型的 Service 决定了服务的暴露方式(内部访问、外部访问等)。

三、Service 的 5 种类型(含特殊类型)

Kubernetes 提供了 4 种标准 Service 类型和 1 种特殊的 “无头服务”,每种适用于不同场景:

类型作用说明适用场景
ClusterIP仅在集群内部暴露服务,生成一个集群内可见的虚拟 IP(默认类型)集群内服务通信(如前端访问后端 API)
NodePort在每个节点上开放一个静态端口,外部可通过 “节点 IP: 端口” 访问开发 / 测试环境,临时从外部访问服务
LoadBalancer借助云厂商负载均衡器(如 AWS ELB),分配公网 IP 供外部访问生产环境,需要公网访问的服务
ExternalName将 Service 映射到集群外的域名(如 baidu.com),无需关联 Pod访问集群外部服务(如外部数据库)
Headless无固定 IP,DNS 直接返回后端所有 Pod 的 IP 列表有状态应用(如 StatefulSet),需要知道每个 Pod 地址

四、详细实操:每种 Service 类型的配置与验证

1. ClusterIP(默认类型)

场景:仅允许集群内的 Pod 访问服务(如后端 API 供前端访问)。

(1)准备后端 Pod(用 Deployment 管理)

先创建一组 nginx Pod 作为服务提供方,nginx-deploy.yaml:

apiVersion: apps/v1  # Deployment 的 API 版本
kind: Deployment     # 资源类型:Deployment
metadata:
  name: nginx-deploy  # Deployment 名称
spec:
  replicas: 2         # 启动 2 个 Pod 副本(便于测试负载均衡)
  selector:           # 标签选择器:管理标签为 app=nginx 的 Pod
    matchLabels:
      app: nginx
  template:           # Pod 模板
    metadata:
      labels:
        app: nginx    # Pod 的标签(必须与 selector 匹配)
    spec:
      containers:
      - name: nginx   # 容器名称
        image: nginx:1.25  # 容器镜像(nginx 官方镜像)
        ports:
        - containerPort: 80  # 容器内 nginx 监听的端口

创建 Deployment:

kubectl apply -f nginx-deploy.yaml

验证 Pod 状态:

kubectl get pods -l app=nginx  # -l 按标签筛选 Pod
------------------------------------------------------------------------------------------------------------
# 预期输出(NAME 前缀可能不同):
[root@k8s-master Service]# kubectl get pod
NAME                            READY   STATUS    RESTARTS   AGE
nginx-deploy-8646c796dc-jmdjf   1/1     Running   0          13s
nginx-deploy-8646c796dc-ksgx8   1/1     Running   0          13s

(2)创建 ClusterIP Service

clusterip-svc.yaml:

apiVersion: v1  # Service 的 API 版本(固定为 v1)
kind: Service   # 资源类型:Service
metadata:
  name: nginx-clusterip  # Service 名称(集群内通过此名称访问)
spec:
  type: ClusterIP        # 类型:ClusterIP(默认类型,可省略)
  selector:              # 标签选择器:关联后端 Pod
    app: nginx           # 只关联标签为 app=nginx 的 Pod(与 Deployment 中的 Pod 标签一致)
  ports:                 # 端口映射配置(数组,可配置多个端口)
  - port: 80             # Service 自身暴露的端口(集群内访问时用这个端口)
    targetPort: 80       # 后端 Pod 上的目标端口(必须与 Pod 的 containerPort 一致)
    protocol: TCP        # 协议(默认 TCP,可选 UDP)

配置说明:

  • selector 是核心:Service 通过这个配置找到要代理的 Pod(必须和 Pod 标签完全匹配)。
  • port vs targetPort:客户端访问 Service 的 port,Service 会转发到 Pod 的 targetPort。
(3)创建并验证 Service

创建 Service:

kubectl apply -f clusterip-svc.yaml

查看 Service 信息:

kubectl get svc nginx-clusterip  # svc 是 service 的缩写
------------------------------------------------------------------------------------------------------------
# 预期输出:
[root@k8s-master Service]# kubectl get svc
NAME              TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
kubernetes        ClusterIP   10.96.0.1       <none>        443/TCP   6d18h
nginx-clusterip   ClusterIP   10.100.63.142   <none>        80/TCP    10s
[root@k8s-master Service]# kubectl get svc nginx-clusterip 
NAME              TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
nginx-clusterip   ClusterIP   10.100.63.142   <none>        80/TCP    36s
(4)验证集群内访问

Service 仅集群内可见,需在集群内的 Pod 中测试访问:

步骤 1:启动一个临时测试 Pod(用 busybox):

kubectl run -it --rm busybox --image=busybox:1.36 -- sh
# 说明:
# -it:交互式终端
# --rm:退出后自动删除 Pod
# sh:进入容器后执行的命令

步骤 2:在测试 Pod 内访问 Service:有两种方式(二选一):

  • 通过 Service 的 ClusterIP:

    wget -qO- 10.100.63.142:80  # 替换为实际的 CLUSTER-IP
    
    -----------------------------------------------------------------------------------------------------
    [root@k8s-master Service]# kubectl run -it --rm busybox --image=busybox:1.36 -- sh
    If you don't see a command prompt, try pressing enter.
    / # 
    / # 
    / # wget -qO- 10.100.63.142:80
    <!DOCTYPE html>
    <html>
    <head>
    <title>Welcome to nginx!</title>
    <style>
    html { color-scheme: light dark; }
    body { width: 35em; margin: 0 auto;
    font-family: Tahoma, Verdana, Arial, sans-serif; }
    </style>
    </head>
    <body>
    <h1>Welcome to nginx!</h1>
    <p>If you see this page, the nginx web server is successfully installed and
    working. Further configuration is required.</p>
    
    <p>For online documentation and support please refer to
    <a href="http://nginx.org/">nginx.org</a>.<br/>
    Commercial support is available at
    <a href="http://nginx.com/">nginx.com</a>.</p>
    
    <p><em>Thank you for using nginx.</em></p>
    </body>
    </html>
    
    
  • 通过 Service 名称(K8s 内部 DNS 自动解析):

    wget -qO- nginx-clusterip:80
    -----------------------------------------------------------------------------------------------------
    / # wget -qO- nginx-clusterip:80
    <!DOCTYPE html>
    <html>
    <head>
    <title>Welcome to nginx!</title>
    <style>
    html { color-scheme: light dark; }
    body { width: 35em; margin: 0 auto;
    font-family: Tahoma, Verdana, Arial, sans-serif; }
    </style>
    </head>
    <body>
    <h1>Welcome to nginx!</h1>
    <p>If you see this page, the nginx web server is successfully installed and
    working. Further configuration is required.</p>
    
    <p>For online documentation and support please refer to
    <a href="http://nginx.org/">nginx.org</a>.<br/>
    Commercial support is available at
    <a href="http://nginx.com/">nginx.com</a>.</p>
    
    <p><em>Thank you for using nginx.</em></p>
    </body>
    </html>
    
    

预期结果:输出 nginx 的默认欢迎页面内容(证明访问成功)。

(5)验证负载均衡

Service 会自动把请求分发到多个 Pod,测试方法:

步骤 1:打开两个终端,分别查看两个 Pod 的日志:

# 终端 1(查看第一个 Pod 日志):
kubectl logs -f nginx-deploy-8646c796dc-jmdjf  # 替换为实际 Pod 名称

# 终端 2(查看第二个 Pod 日志):
kubectl logs -f nginx-deploy-8646c796dc-ksgx8  # 替换为另一个 Pod 名称

步骤 2:在测试 Pod 内多次访问 Service:

# 在之前的 busybox 终端中执行:
while true; do wget -qO- nginx-clusterip:80; sleep 2; done  # 每 2 秒访问一次

------------------------------------------------------------------------------------------------------------
#终端1验证:
2025/11/10 01:14:33 [notice] 1#1: start worker process 32
10.244.169.151 - - [10/Nov/2025:01:18:12 +0000] "GET / HTTP/1.1" 200 615 "-" "Wget" "-"
10.244.169.151 - - [10/Nov/2025:01:19:04 +0000] "GET / HTTP/1.1" 200 615 "-" "Wget" "-"
10.244.169.152 - - [10/Nov/2025:01:21:38 +0000] "GET / HTTP/1.1" 200 615 "-" "Wget" "-"
10.244.169.152 - - [10/Nov/2025:01:21:42 +0000] "GET / HTTP/1.1" 200 615 "-" "Wget" "-"
10.244.169.152 - - [10/Nov/2025:01:21:46 +0000] "GET / HTTP/1.1" 200 615 "-" "Wget" "-"
10.244.169.152 - - [10/Nov/2025:01:21:52 +0000] "GET / HTTP/1.1" 200 615 "-" "Wget" "-"
10.244.169.152 - - [10/Nov/2025:01:21:58 +0000] "GET / HTTP/1.1" 200 615 "-" "Wget" "-"
------------------------------------------------------------------------------------------------------------
#终端2验证
2025/11/10 01:14:32 [notice] 1#1: start worker process 32
10.244.169.152 - - [10/Nov/2025:01:21:36 +0000] "GET / HTTP/1.1" 200 615 "-" "Wget" "-"
10.244.169.152 - - [10/Nov/2025:01:21:40 +0000] "GET / HTTP/1.1" 200 615 "-" "Wget" "-"
10.244.169.152 - - [10/Nov/2025:01:21:44 +0000] "GET / HTTP/1.1" 200 615 "-" "Wget" "-"
10.244.169.152 - - [10/Nov/2025:01:21:48 +0000] "GET / HTTP/1.1" 200 615 "-" "Wget" "-"
10.244.169.152 - - [10/Nov/2025:01:21:50 +0000] "GET / HTTP/1.1" 200 615 "-" "Wget" "-"
10.244.169.152 - - [10/Nov/2025:01:21:54 +0000] "GET / HTTP/1.1" 200 615 "-" "Wget" "-"
10.244.169.152 - - [10/Nov/2025:01:21:56 +0000] "GET / HTTP/1.1" 200 615 "-" "Wget" "-"
10.244.169.152 - - [10/Nov/2025:01:22:00 +0000] "GET / HTTP/1.1" 200 615 "-" "Wget" "-"

预期结果:两个终端的日志会交替出现访问记录(轮询负载均衡)。

(6)清理资源(重要)
# 退出 busybox 终端(按 Ctrl + D)
kubectl delete -f clusterip-svc.yaml  # 删除 Service
kubectl delete -f nginx-deploy.yaml   # 删除 Deployment 和 Pod
# 验证:kubectl get svc,deploy,pods 确认无相关资源
2. NodePort(节点端口)

场景:需要从集群外部访问服务(如开发环境中让外部浏览器访问 Web 服务)。原理:在每个节点上开放一个静态端口(NodePort),外部通过 “任意节点 IP:NodePort” 访问,请求会转发到 Service,再到 Pod。

(1)复用后端 Deployment

如果已清理,重新创建 nginx Deployment:

kubectl apply -f nginx-deploy.yaml
(2)创建 NodePort Service

nodeport-svc.yaml:

apiVersion: v1
kind: Service
metadata:
  name: nginx-nodeport  # Service 名称
spec:
  type: NodePort        # 类型:NodePort
  selector:
    app: nginx          # 关联标签为 app=nginx 的 Pod
  ports:
  - port: 80            # Service 内部端口(集群内访问用)
    targetPort: 80      # 后端 Pod 端口(与 containerPort 一致)
    nodePort: 30080     # 节点上开放的端口(必须在 30000-32767 之间,可选)
    protocol: TCP

配置说明:

  • nodePort:K8s 规定必须在 30000-32767 之间(避免与节点原有端口冲突)。如果不指定,K8s 会自动分配一个。
  • 外部访问路径:节点 IP:nodePort → 节点转发到 Service 的 port → 转发到 Pod 的 targetPort。
(3)创建并验证 Service

创建 Service:

kubectl apply -f nodeport-svc.yaml

查看 Service 信息:

kubectl get svc nginx-nodeport
------------------------------------------------------------------------------------------------------------
# 预期输出:
[root@k8s-master Service]# kubectl get svc
NAME             TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
kubernetes       ClusterIP   10.96.0.1        <none>        443/TCP        6d18h
nginx-nodeport   NodePort    10.108.167.158   <none>        80:30080/TCP   15s
(4)获取节点 IP

外部访问需要知道节点的 IP:

kubectl get nodes -o wide
------------------------------------------------------------------------------------------------------------
# 预期输出(关注 INTERNAL-IP):
[root@k8s-master Service]# kubectl get nodes -o wide
NAME         STATUS   ROLES                  AGE     VERSION   INTERNAL-IP    EXTERNAL-IP   OS-IMAGE                KERNEL-VERSION           CONTAINER-RUNTIME
k8s-master   Ready    control-plane,master   6d18h   v1.23.0   10.132.47.60   <none>        CentOS Linux 7 (Core)   3.10.0-1160.el7.x86_64   docker://26.1.4
k8s-node1    Ready    <none>                 6d17h   v1.23.0   10.132.47.61   <none>        CentOS Linux 7 (Core)   3.10.0-1160.el7.x86_64   docker://26.1.4
k8s-node2    Ready    <none>                 6d17h   v1.23.0   10.132.47.62   <none>        CentOS Linux 7 (Core)   3.10.0-1160.el7.x86_64   docker://26.1.4
k8s-node3    Ready    <none>                 6d17h   v1.23.0   10.132.47.63   <none>        CentOS Linux 7 (Core)   3.10.0-1160.el7.x86_64   docker://26.1.4

(5)验证外部访问

根据你的环境选择访问方式:

  • 本地虚拟机 / 服务器集群:直接用节点 IP + nodePort 访问:

    curl 10.132.47.61:30080  # 替换为实际节点 IP 和 nodePort
    -----------------------------------------------------------------------------------------------------
    [root@k8s-master Service]# curl 10.132.47.61:30080
    <!DOCTYPE html>
    <html>
    <head>
    <title>Welcome to nginx!</title>
    <style>
    html { color-scheme: light dark; }
    body { width: 35em; margin: 0 auto;
    font-family: Tahoma, Verdana, Arial, sans-serif; }
    </style>
    </head>
    <body>
    <h1>Welcome to nginx!</h1>
    <p>If you see this page, the nginx web server is successfully installed and
    working. Further configuration is required.</p>
    
    <p>For online documentation and support please refer to
    <a href="http://nginx.org/">nginx.org</a>.<br/>
    Commercial support is available at
    <a href="http://nginx.com/">nginx.com</a>.</p>
    
    <p><em>Thank you for using nginx.</em></p>
    </body>
    </html>
    
    # 或在浏览器访问 http://10.132.47.61:30080,应看到 nginx 页面
    
    
  • 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • minikube 环境:minikube 会模拟节点,需用专用命令暴露端口:

    minikube service nginx-nodeport --url
    # 输出示例:http://192.168.49.2:30080,直接在浏览器访问该地址
    
(6)清理资源
kubectl delete -f nodeport-svc.yaml
kubectl delete -f nginx-deploy.yaml
# 验证:确认资源已删除
3. LoadBalancer(负载均衡器)

场景:生产环境中需要通过公网 IP 稳定访问服务(依赖云厂商支持,如 AWS、阿里云)。原理:在 NodePort 基础上,云厂商会自动创建一个负载均衡器,并分配公网 IP,外部直接通过该 IP 访问。

(1)复用后端 Deployment
kubectl apply -f nginx-deploy.yaml
(2)创建 LoadBalancer Service

loadbalancer-svc.yaml:

apiVersion: v1
kind: Service
metadata:
  name: nginx-loadbalancer  # Service 名称
spec:
  type: LoadBalancer        # 类型:LoadBalancer
  selector:
    app: nginx              # 关联标签为 app=nginx 的 Pod
  ports:
  - port: 80
    targetPort: 80
    protocol: TCP

配置说明:

  • 云环境中,创建后会自动分配一个公网 IP(EXTERNAL-IP),外部直接通过该 IP 访问(无需记节点 IP)。
  • 本地集群(如 minikube)中,EXTERNAL-IP 会显示为 <pending>(无法创建真实负载均衡器),但仍可通过 NodePort 访问(类似 NodePort 类型)。
(3)创建并验证 Service

创建 Service:

kubectl apply -f loadbalancer-svc.yaml

查看 Service 信息:

kubectl get svc nginx-loadbalancer
(4)验证访问(云环境)

直接通过 EXTERNAL-IP 访问:

curl 123.45.67.89  # 替换为实际的 EXTERNAL-IP
(5)清理资源
kubectl delete -f loadbalancer-svc.yaml
kubectl delete -f nginx-deploy.yaml
4. ExternalName(外部名称)

场景:需要在集群内访问外部服务(如外部数据库、第三方 API),通过 Service 名称间接访问,避免硬编码外部域名。原理:将 Service 映射到一个外部域名(如 www.baidu.com),集群内访问 Service 名称时,会自动解析为外部域名。

(1)创建 ExternalName Service

externalname-svc.yaml:

apiVersion: v1
kind: Service
metadata:
  name: externalname-demo  # Service 名称(集群内用此名称访问)
spec:
  type: ExternalName       # 类型:ExternalName
  externalName: www.baidu.com  # 映射到外部域名(这里以百度为例)

配置说明:

  • 无 selector 和 ports:因为它不关联集群内的 Pod,只做 DNS 别名映射。
  • 集群内访问 externalname-demo.default.svc.cluster.local(Service 完整域名)时,会被 DNS 解析为 www.baidu.com。
(2)创建并验证 Service

创建 Service:

kubectl apply -f externalname-svc.yaml

查看 Service 信息:

kubectl get svc externalname-demo
(3)验证 DNS 解析和访问

在集群内的 Pod 中测试:

步骤 1:启动临时测试 Pod:

kubectl run -it --rm busybox --image=busybox:1.36 -- sh

步骤 2:解析 Service 域名:

nslookup externalname-demo.default.svc.cluster.local
# 预期输出(返回百度的 IP):
# Server:    10.96.0.10
# Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local
# Name:      externalname-demo.default.svc.cluster.local
# Address 1: 180.101.50.242
# Address 2: 180.101.50.188

步骤 3:通过 Service 名称访问外部服务:

wget -qO- externalname-demo  # 实际访问的是 www.baidu.com
# 预期输出:百度首页的 HTML 内容
(4)清理资源
# 退出 busybox 终端(Ctrl + D)
kubectl delete -f externalname-svc.yaml
5. Headless Service(无头服务)

场景:有状态应用(如数据库集群、ZooKeeper)需要知道每个 Pod 的 IP 地址,而不是通过一个统一的 Service IP 访问。原理:无头服务没有固定的 ClusterIP,DNS 解析时会直接返回所有关联 Pod 的 IP 列表,客户端可自行选择访问哪个 Pod。

(1)复用后端 Deployment
kubectl apply -f nginx-deploy.yaml
(2)创建 Headless Service

headless-svc.yaml:

apiVersion: v1
kind: Service
metadata:
  name: nginx-headless  # Service 名称
spec:
  clusterIP: None       # 关键:设置为 None 表示无头服务(无固定 IP)
  selector:
    app: nginx          # 关联标签为 app=nginx 的 Pod
  ports:
  - port: 80
    targetPort: 80
    protocol: TCP

配置说明:

  • clusterIP: None 是无头服务的标志,K8s 不会为其分配 ClusterIP。
  • 主要用于 StatefulSet 控制器:每个 Pod 有固定名称(如 web-0、web-1),结合无头服务的 DNS 解析,可实现 Pod 间的稳定通信。
(3)创建并验证 Service

创建 Service:

kubectl apply -f headless-svc.yaml

查看 Service 信息:

kubectl get svc nginx-headless
# 预期输出(CLUSTER-IP 为 None):
# NAME             TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
# nginx-headless   ClusterIP   None         <none>        80/TCP    10s
(4)验证 DNS 解析(返回所有 Pod IP)

在集群内的 Pod 中测试:

步骤 1:启动临时测试 Pod:

kubectl run -it --rm busybox --image=busybox:1.36 -- sh

步骤 2:解析无头服务域名:

nslookup nginx-headless.default.svc.cluster.local
# 预期输出(返回 2 个 Pod 的 IP,与后端 Pod 数量一致):
# Server:    10.96.0.10
# Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local
# Name:      nginx-headless.default.svc.cluster.local
# Address 1: 10.244.1.15  # 第一个 Pod 的 IP
# Address 2: 10.244.2.16  # 第二个 Pod 的 IP
(5)清理资源
# 退出 busybox 终端
kubectl delete -f headless-svc.yaml
kubectl delete -f nginx-deploy.yaml

五、总结

Service 类型核心特点访问方式关键配置
ClusterIP集群内固定 IP,仅内部访问Service 名称:port 或 ClusterIP:port无需特殊配置(默认类型)
NodePort节点开放静态端口,外部可访问节点 IP:NodePorttype: NodePort + nodePort
LoadBalancer云厂商负载均衡器,公网 IP 访问EXTERNAL-IP:porttype: LoadBalancer
ExternalName映射到外部域名,无选择器Service 名称 解析为外部域名type: ExternalName + externalName
Headless无固定 IP,返回所有 Pod IP直接访问 Pod IP(通过 DNS 解析)clusterIP: None

------ | ------------------------------------- |
| ClusterIP | 集群内固定 IP,仅内部访问 | Service 名称:port 或 ClusterIP:port | 无需特殊配置(默认类型) |
| NodePort | 节点开放静态端口,外部可访问 | 节点 IP:NodePort | type: NodePort + nodePort |
| LoadBalancer | 云厂商负载均衡器,公网 IP 访问 | EXTERNAL-IP:port | type: LoadBalancer |
| ExternalName | 映射到外部域名,无选择器 | Service 名称 解析为外部域名 | type: ExternalName + externalName |
| Headless | 无固定 IP,返回所有 Pod IP | 直接访问 Pod IP(通过 DNS 解析) | clusterIP: None |

更多推荐