Qingular

Service 类型

·CKAk8s练习

CKA Domain 3 — Kubernetes Service 类型详解(ClusterIP、NodePort、LoadBalancer、Headless、ExternalName)

← 返回 CKA 练习目录

概述

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

考试提示