Service 类型
CKA Domain 3 — Kubernetes Service 类型详解(ClusterIP、NodePort、LoadBalancer、Headless、ExternalName)
概述
Service 是 Kubernetes 中用于暴露 Pod 网络访问的抽象层。它为一组动态变化的 Pod 提供稳定的访问入口(DNS 名称或 IP 地址)。
1. ClusterIP(默认类型)
只能在集群内部访问,是默认 Service 类型。
apiVersion: v1
kind: Service
metadata:
name: my-clusterip-svc
spec:
type: ClusterIP
selector:
app: my-app
ports:
- port: 80
targetPort: 8080
# 创建 ClusterIP Service
kubectl create service clusterip my-svc --tcp=80:8080
# 或者通过 expose 命令
kubectl expose deployment my-deploy --port=80 --target-port=8080 --name=my-svc
2. NodePort
在每个节点上打开一个静态端口(30000-32767),将流量转发到 Service。
apiVersion: v1
kind: Service
metadata:
name: my-nodeport-svc
spec:
type: NodePort
selector:
app: my-app
ports:
- port: 80
targetPort: 8080
nodePort: 30080 # 可选,未指定则随机分配
# 创建 NodePort Service
kubectl create service nodeport my-svc --tcp=80:8080 --node-port=30080
# 或者 expose 时指定类型
kubectl expose deployment my-deploy --type=NodePort --port=80 --target-port=8080 --name=my-nodeport-svc
访问方式:<NodeIP>:<NodePort>
3. LoadBalancer
在 NodePort 基础上,自动创建云负载均衡器(仅云环境有效)。
apiVersion: v1
kind: Service
metadata:
name: my-lb-svc
spec:
type: LoadBalancer
selector:
app: my-app
ports:
- port: 80
targetPort: 8080
# 创建 LoadBalancer Service
kubectl create service loadbalancer my-svc --tcp=80:8080
# 或者 expose
kubectl expose deployment my-deploy --type=LoadBalancer --port=80 --target-port=8080
查看 External IP(通常在云环境中几分钟后分配):
kubectl get svc my-lb-svc
# 注意 EXTERNAL-IP 列
4. Headless Service
设置 clusterIP: None,不会分配 ClusterIP,DNS 查询会返回所有后端 Pod 的 IP 地址列表。常用于 StatefulSet。
apiVersion: v1
kind: Service
metadata:
name: my-headless-svc
spec:
clusterIP: None
selector:
app: my-app
ports:
- port: 80
targetPort: 8080
StatefulSet 配合使用:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
serviceName: "my-headless-svc"
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
Pod DNS 格式:<pod-name>.<service-name>.<namespace>.svc.cluster.local
# 验证 Headless Service 的 DNS 解析
kubectl run dns-test --image=busybox:1.28 --rm -it --restart=Never -- nslookup my-headless-svc
5. ExternalName
通过 DNS CNAME 将 Service 映射到外部域名。没有 Selector,不定义端口。
apiVersion: v1
kind: Service
metadata:
name: my-external-svc
spec:
type: ExternalName
externalName: api.example.com
# 验证 ExternalName 解析
kubectl run dns-test --image=busybox:1.28 --rm -it --restart=Never -- nslookup my-external-svc
6. Endpoints 与 EndpointSlice
Service 通过 Selector 匹配 Pod 后,自动创建 Endpoints 资源。
# 查看 Endpoints
kubectl get endpoints my-svc
kubectl describe endpoints my-svc
# 查看 EndpointSlice(K8s v1.21+)
kubectl get endpointslices
手动创建指向外部服务的 Endpoints 示例(无 Selector 的 Service):
apiVersion: v1
kind: Service
metadata:
name: external-db
spec:
ports:
- port: 3306
---
apiVersion: v1
kind: Endpoints
metadata:
name: external-db
subsets:
- addresses:
- ip: 192.168.1.100
ports:
- port: 3306
常用命令汇总
# 创建 Service
kubectl create service clusterip my-svc --tcp=80:8080
kubectl create service nodeport my-svc --tcp=80:8080
kubectl create service loadbalancer my-svc --tcp=80:8080
# expose 命令(最常用)
kubectl expose deployment my-deploy --type=ClusterIP --port=80 --target-port=8080
kubectl expose pod my-pod --port=80 --target-port=8080 --name=my-svc
# 查看 Service
kubectl get svc
kubectl get svc -o wide
kubectl describe svc my-svc
# 端口转发(临时调试)
kubectl port-forward svc/my-svc 8080:80
kubectl port-forward pod/my-pod 8080:80
🧪 完整操作实例:通过不同类型的 Service 暴露应用
场景描述
创建一个 Nginx Deployment,分别用 ClusterIP、NodePort 和 Headless Service 暴露,验证每种类型的访问方式。
前置条件
- 集群正常运行,有至少一个可用节点
- kubectl 已配置好集群访问
操作步骤
Step 1: 创建 Deployment
kubectl create deployment nginx --image=nginx:1.25 --replicas=2
# 预期输出:deployment.apps/nginx created
kubectl get pods -l app=nginx -o wide
# 预期输出:两个 Pod 处于 Running 状态,各有一个 IP
Step 2: 创建 ClusterIP Service
kubectl expose deployment nginx --name=nginx-clusterip --port=80 --target-port=80 --type=ClusterIP
# 预期输出:service/nginx-clusterip exposed
kubectl get svc nginx-clusterip
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
# nginx-clusterip ClusterIP 10.96.123.45 <none> 80/TCP 5s
Step 3: 从集群内部测试 ClusterIP
# 启动临时测试 Pod 访问 ClusterIP
kubectl run test-pod --image=busybox:1.28 --rm -it --restart=Never -- wget -qO- http://nginx-clusterip
# 预期输出:nginx 欢迎页面的 HTML(<!DOCTYPE html>...)
# 或者使用 nslookup 验证 DNS 解析
kubectl run dns-test --image=busybox:1.28 --rm -it --restart=Never -- nslookup nginx-clusterip
# 预期输出:Server: 10.96.0.10
# Name: nginx-clusterip.default.svc.cluster.local
# Address: 10.96.123.45
Step 4: 将 Service 改为 NodePort
# 删除原 Service,重新创建 NodePort 类型
kubectl delete svc nginx-clusterip
kubectl expose deployment nginx --name=nginx-nodeport --port=80 --target-port=80 --type=NodePort
# 预期输出:service/nginx-nodeport exposed
kubectl get svc nginx-nodeport
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
# nginx-nodeport NodePort 10.96.67.89 <none> 80:30080/TCP 5s
# 获取节点 IP(可在任意节点上访问)
kubectl get nodes -o wide
# 记录任意节点的 Internal-IP 或 External-IP,例如 192.168.1.10
Step 5: 从集群外测试 NodePort
# 从集群外或节点上访问
curl http://192.168.1.10:30080
# 预期输出:nginx 欢迎页面的 HTML
# 如果从本机访问 minikube 集群
minikube service nginx-nodeport --url
# 预期输出:http://127.0.0.1:xxxxx
Step 6: 创建 Headless Service
# 删除之前的 NodePort Service
kubectl delete svc nginx-nodeport
# 创建 Headless Service(通过 YAML,因为 kubectl expose 无法直接设置 clusterIP: None)
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
name: nginx-headless
spec:
clusterIP: None
selector:
app: nginx
ports:
- port: 80
targetPort: 80
EOF
# 预期输出:service/nginx-headless created
kubectl get svc nginx-headless
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
# nginx-headless ClusterIP None <none> 80/TCP 5s
Step 7: 测试 Headless Service DNS 解析
# 验证 DNS 返回所有后端 Pod IP
kubectl run dns-test --image=busybox:1.28 --rm -it --restart=Never -- nslookup nginx-headless
# 预期输出:
# Server: 10.96.0.10
# Address: 10.96.0.10:53
# Name: nginx-headless.default.svc.cluster.local
# Address: 10.244.1.5 # Pod-1 的 IP
# Name: nginx-headless.default.svc.cluster.local
# Address: 10.244.2.7 # Pod-2 的 IP
# 使用 dig 查看详细记录(需要 netshoot 镜像)
kubectl run dig-pod --image=nicolaka/netshoot --rm -it --restart=Never -- dig nginx-headless A +short
# 预期输出:
# 10.244.1.5
# 10.244.2.7
验证结果
# 三种 Service 类型对比
kubectl get svc
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
# kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 1h
# nginx-headless ClusterIP None <none> 80/TCP 5m
# 检查 Endpoints 确认后端 Pod
kubectl get endpoints nginx-headless
# NAME ENDPOINTS AGE
# nginx-headless 10.244.1.5:80,10.244.2.7:80 5m
# 清理资源
kubectl delete deployment nginx
kubectl delete svc nginx-headless
考试提示
-
ClusterIP 是默认类型,如果不指定
--type,kubectl expose默认创建 ClusterIP -
NodePort 端口范围固定为 30000-32767,如果未指定
--node-port则随机分配 -
Headless Service 通常与 StatefulSet 配合使用,DNS 返回所有 Pod IP 而非虚拟 IP
-
CKA 关键命令:
kubectl expose deployment、kubectl create service和kubectl run ... -- nslookup是高频考点 -
如果 DNS 测试 Pod 无法启动,检查
busybox:1.28是否可用;推荐使用1.28版本,新版本可能缺少 nslookup 命令 -
https://kubernetes.io/docs/concepts/services-networking/service/
-
https://kubernetes.io/docs/tasks/debug/debug-application/debug-service/
-
https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/
-
https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/