k8s Service
一、为什么需要 Service?
Pod 是临时的:当 Pod 故障重启、扩缩容或节点故障时,Pod 的 IP 会变化。如果客户端直接用 Pod IP 访问,会因为 IP 变动导致服务中断。Service 的作用:
- 固定入口:给一组 Pod 分配一个稳定的访问地址(IP 或域名)。
- 负载均衡:自动把请求分发到后端多个 Pod 上,实现流量分担。
- 服务发现:通过 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 标签完全匹配)。portvstargetPort:客户端访问 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: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 |
------ | ------------------------------------- |
| 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 |
更多推荐


所有评论(0)