介绍
Pod 是临时的。Deployment 在滚动更新、重启或扩缩容期间会替换 Pod,因此客户端不应直接连接到单个 Pod 的 IP。Service 为客户端提供了一个稳定的名称和虚拟 IP,然后利用标签(label)将流量路由到当前匹配的 Pod。
在本实验中,你将创建一个小型 Web Deployment,使用 ClusterIP Service 将其暴露,检查 Service 如何选择后端 Pod,从客户端 Pod 测试该 Service,并对 Deployment 进行扩容以观察 Service 端点的更新情况。
创建 Web Deployment
在此步骤中,你将准备一个命名空间并创建一个稍后可通过 Service 暴露的 Deployment。
从 /home/labex/project 开始:
mkdir -p /home/labex/project
cd /home/labex/project
检查 Kubernetes API 服务器是否可达。如果虚拟机刚启动且节点尚未就绪,请等待几秒钟后再运行该命令。
kubectl get nodes --request-timeout=5s
预期输出包含:
NAME STATUS ROLES AGE VERSION
labex-v135 Ready control-plane ... v1.35...
等待集群网络 Pod 就绪。本实验通过 Service 在 Pod 之间发送流量,因此在测试访问之前,集群网络必须处于健康状态。
kubectl wait --namespace kube-system --for=condition=Ready pod -l k8s-app=calico-node --timeout=120s
预期输出:
pod/calico-node-... condition met
创建一个名为 ckad-prep-service 的命名空间,并将其设置为当前命名空间:
kubectl create namespace ckad-prep-service
kubectl config set-context --current --namespace=ckad-prep-service
创建一个名为 web-front 的 Deployment,其中包含一个 NGINX Pod。--port=80 标志用于在 Pod 模板中记录容器端口,它本身不会创建 Service。
kubectl create deployment web-front --image=nginx:1.25 --port=80
等待 Deployment 可用,然后检查 Pod 标签。标签 app=web-front 非常重要,因为 Service 将使用它来查找后端 Pod。
kubectl rollout status deployment/web-front --timeout=120s
kubectl get pods -l app=web-front --show-labels
预期输出包含:
deployment "web-front" successfully rolled out
NAME READY STATUS RESTARTS AGE LABELS
web-front-... 1/1 Running 0 ... app=web-front,...
使用 ClusterIP Service 暴露 Deployment
在此步骤中,你将为 Deployment 创建一个 Service,并检查将 Service 连接到 Pod 的字段。
使用 kubectl expose deployment 创建一个 ClusterIP Service。Service 端口为 80,--target-port=80 告诉 Service 将流量转发到匹配 Pod 上的 80 端口。
kubectl expose deployment web-front --name=web-front --port=80 --target-port=80
预期输出:
service/web-front exposed
检查该 Service:
kubectl get service web-front
预期输出包含:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
web-front ClusterIP ... <none> 80/TCP ...
现在检查 Service 的选择器(selector)和端口映射。选择器是 Service 查找 Pod 的方式;端口映射是客户端流量到达容器的方式。
kubectl get service web-front -o jsonpath='{.spec.selector.app}{" "}{.spec.ports[0].port}{":"}{.spec.ports[0].targetPort}{"\n"}'
预期输出:
web-front 80:80
Kubernetes 将选定的后端地址存储在 EndpointSlice 中。列出为该 Service 创建的 EndpointSlice:
kubectl get endpointslice -l kubernetes.io/service-name=web-front
预期输出包含:
NAME ADDRESSTYPE PORTS ENDPOINTS AGE
web-front-... IPv4 80 ... ...
从集群内部测试 Service
在此步骤中,你将创建一个临时客户端 Pod 并向 Service 发送 HTTP 请求。ClusterIP Service 旨在用于集群内访问,因此从另一个 Pod 进行测试是验证它的最直接方法。
创建一个持续运行的客户端 Pod:
kubectl run service-client --image=busybox:1.36 --restart=Never --command -- sleep 3600
等待客户端 Pod 就绪:
kubectl wait --for=condition=Ready pod/service-client --timeout=120s
预期输出:
pod/service-client condition met
在客户端 Pod 内使用 wget 请求 http://web-front。名称 web-front 会解析为命名空间内的 Service。
kubectl exec service-client -- wget -qO- http://web-front | head -n 5
预期输出包含默认 NGINX 页面的开头:
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
这证明了客户端已成功连接到稳定的 Service 名称,并且 Service 已将流量转发到当前的后端 Pod。
扩容后端并重新检查端点
在此步骤中,你将对 Deployment 进行扩容,并确认 Service 在后端端点发生变化时,仍保持相同的稳定访问入口。
将 Deployment 扩容至两个副本:
kubectl scale deployment web-front --replicas=2
kubectl rollout status deployment/web-front --timeout=120s
预期输出包含:
deployment.apps/web-front scaled
deployment "web-front" successfully rolled out
列出 Pod。现在应该有两个带有相同 app=web-front 标签的运行中后端 Pod。
kubectl get pods -l app=web-front
预期输出包含两个 Pod:
NAME READY STATUS RESTARTS AGE
web-front-... 1/1 Running 0 ...
web-front-... 1/1 Running 0 ...
统计 Service 后端的端点数量。该命令会为每个端点地址打印一行,然后统计行数。
kubectl get endpointslice -l kubernetes.io/service-name=web-front -o jsonpath='{range .items[*].endpoints[*]}{.addresses[0]}{"\n"}{end}' | wc -l
预期输出:
2
再次从同一个客户端 Pod 请求该 Service:
kubectl exec service-client -- wget -qO- http://web-front | grep '<title>Welcome to nginx'
预期输出:
<title>Welcome to nginx!</title>
总结
你使用 ClusterIP Service 暴露了一个 Deployment,并验证了完整的集群内访问路径。你创建了一个后端 Deployment,创建了一个带有选择器和端口映射的 Service,检查了为该 Service 生成的 EndpointSlice,从客户端 Pod 测试了访问,随后对 Deployment 进行了扩容,并确认 Service 继续将流量路由到更新后的后端集合。


