简介
Service 为集群内的应用提供稳定的访问端点。Ingress 在 Service 前面增加 HTTP 路由规则,让多个应用可以通过主机名或 URL 路径共享同一个入口。
你将检查已准备好的 ingress-nginx 控制器,部署两个后端应用,添加基于主机和路径的路由,并跟踪请求经过 Ingress、Service 和 Pod 的完整链路。
在整个实验过程中,请牢记请求路径:客户端 → ingress-nginx 入口 → Ingress 规则 → Service → 就绪 Pod。当请求失败时,按这个顺序检查各个环节,比把 Ingress 当成一个无法拆解的黑盒更容易定位问题。
检查 Ingress 控制器
本步骤将检查 CKA 环境已经提供的控制器,而不是再次安装一个副本。
Ingress 对象只是配置,不是代理进程。只有兼容的控制器持续监视它时,Ingress 才会生效。首先确认这两部分基础设施都存在:名为 nginx 的 IngressClass,以及一个可用的 ingress-nginx 控制器 Deployment。
cd /home/labex/project/ingress-routing
kubectl get ingressclasses
kubectl get deployments,pods,services -n ingress-nginx
kubectl wait --for=condition=Available deployment/ingress-nginx-controller -n ingress-nginx --timeout=60s
Ingress 资源保存路由意图。ingress-nginx 控制器会监视 ingressClassName 为 nginx 的资源,并据此配置实际的代理。
在这个本地集群中,控制器 Service 通过 NodePort 暴露代理。稍后你将查询该端口,而不是把端口号硬编码到命令中。
准备好的环境应显示一个 nginx 类别、一个可用的控制器,以及一个 NodePort Service:
NAME CONTROLLER
nginx k8s.io/ingress-nginx
deployment.apps/ingress-nginx-controller 1/1 ...
service/ingress-nginx-controller NodePort ... 80:.../TCP,443:.../TCP
你可能还会看到两个状态为 Completed 的 admission Pod。它们是已经执行完毕的安装 Job,并不是失败的控制器副本。
部署两个 Service 后端
本步骤将通过一个多文档文件,创建两个单副本 Web 工作负载及其对应的 ClusterIP Service。
这些后端使用了 Kubernetes 入门课程中的概念,因此本步骤的重点是它们之间的路由关系,而不是 Deployment 语法。该文件包含由 --- 分隔的五个 YAML 文档:一个 Namespace,以及每个应用各自的 Deployment 和 Service。请将每个 Service.spec.selector 与对应 Pod 的标签放在一起检查;在 Ingress 能够访问后端之前,两者必须匹配。
cd /home/labex/project/ingress-routing
cat <<'EOF' > backends.yaml
apiVersion: v1
kind: Namespace
metadata: {name: practice-ingress}
---
apiVersion: apps/v1
kind: Deployment
metadata: {name: storefront, namespace: practice-ingress}
spec:
replicas: 1
selector: {matchLabels: {app: storefront}}
template:
metadata: {labels: {app: storefront}}
spec:
containers: [{name: web, image: nginx:1.27-alpine, imagePullPolicy: IfNotPresent, ports: [{name: http, containerPort: 80}]}]
---
apiVersion: v1
kind: Service
metadata: {name: storefront, namespace: practice-ingress}
spec: {selector: {app: storefront}, ports: [{name: http, port: 80, targetPort: http}]}
---
apiVersion: apps/v1
kind: Deployment
metadata: {name: reports, namespace: practice-ingress}
spec:
replicas: 1
selector: {matchLabels: {app: reports}}
template:
metadata: {labels: {app: reports}}
spec:
containers: [{name: web, image: httpd:2.4-alpine, imagePullPolicy: IfNotPresent, ports: [{name: http, containerPort: 80}]}]
---
apiVersion: v1
kind: Service
metadata: {name: reports, namespace: practice-ingress}
spec: {selector: {app: reports}, ports: [{name: http, port: 80, targetPort: http}]}
EOF
一次性应用全部五个文档,然后等待两个 Deployment 完成滚动发布。分开等待可以清楚地显示具体是哪个后端出现问题,例如镜像或选择器配置错误:
kubectl apply -f backends.yaml
kubectl rollout status deployment/storefront -n practice-ingress --timeout=60s
kubectl rollout status deployment/reports -n practice-ingress --timeout=60s
Ingress 只能将流量路由到 Service,因此每个后端都必须已经拥有就绪端点。
在创建路由规则前,先明确确认这一前置条件:
kubectl get services,endpointslices -n practice-ingress
每个 Service 都应对应一个包含一个就绪地址的 EndpointSlice。
service/reports ClusterIP ... 80/TCP
service/storefront ClusterIP ... 80/TCP
endpointslice.../reports-... IPv4 80 10.244.104...
endpointslice.../storefront-... IPv4 80 10.244.104...
生成的后缀和地址会有所不同,但每个 Service 名称都应对应一个非空的 EndpointSlice。Ingress 无法修复没有后端的 Service;这项检查会先验证底层服务是否正常。
添加基于主机的路由
本步骤将 store.practice.test 路由到 storefront Service。
Ingress 规则包含三个路由决策:host、path 和后端 Service 端口。ingressClassName: nginx 用于选择控制器。重写注解是控制器专用配置,它会将发往后端的请求路径修改为 /,这样两个演示 Web 服务器都能返回页面。
cd /home/labex/project/ingress-routing
cat <<'EOF' > ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: practice-routing
namespace: practice-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- host: store.practice.test
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: storefront
port: {name: http}
EOF
应用规则,然后使用 describe 查看控制器对规则的解析结果,其中包括已经解析出的后端端点:
kubectl apply -f ingress.yaml
kubectl describe ingress practice-routing -n practice-ingress
pathType: Prefix 会匹配 / 及其子路径。重写注解会将选中的请求发送到后端的 /,演示 Web 服务器在该路径提供页面。主机名在这里充当 HTTP 路由键;对于这个本地测试,不需要配置公共 DNS。
describe 的输出应将规则完整解析到某个 Pod 地址:
Rules:
Host Path Backends
store.practice.test
/ storefront:http (10.244.104...:80)
在创建后的本地环境中,顶层 Address 暂时为空并不表示失败。已经解析出的 storefront:http 后端及其 Pod 地址才是关键证据。
添加路径路由并测试请求
本步骤将添加 /reports 路由,并通过节点入口测试两条规则。
JSON Patch 路径以 /- 结尾。对于数组来说,- 表示追加新项目,而不是替换现有的 / 路由。新的后端通过名为 http 的端口指向 reports Service。
请只运行一次这个追加补丁。与 kubectl apply 不同,向 /- 追加操作不是幂等的:重复执行会再添加一个完全相同的 /reports 项目,而不会识别之前已经添加过的项目。
下面两个赋值语句会自动发现入口地址,而不是将地址硬编码到命令中。minikube ip -p labex-v135 返回指定 Minikube 配置文件的节点 IP,命令替换 $(...) 会将每条命令的输出保存到 Shell 变量中。JSONPath 查询会选取名为 http 的 Service 端口,并提取其 NodePort。
kubectl patch ingress practice-routing -n practice-ingress --type='json' -p='[{"op":"add","path":"/spec/rules/0/http/paths/-","value":{"path":"/reports","pathType":"Prefix","backend":{"service":{"name":"reports","port":{"name":"http"}}}}}]'
NODE_IP=$(minikube ip -p labex-v135)
HTTP_NODE_PORT=$(kubectl get service ingress-nginx-controller -n ingress-nginx -o jsonpath='{.spec.ports[?(@.name=="http")].nodePort}')
echo "Ingress endpoint: ${NODE_IP}:${HTTP_NODE_PORT}"
这两个变量避免了在操作说明中嵌入特定环境的地址。现在,使用规则所要求的 Host 请求头发送请求。对于每条 curl 命令,-s 会隐藏进度信息,--retry 5 会重试暂时性失败,--retry-connrefused 会在连接最初被拒绝时也进行重试,--retry-delay 2 会在两次尝试之间等待两秒。-H 用于设置 HTTP Host 请求头,管道则将响应发送给 grep,因此只显示用于识别页面的文本。
curl -s --retry 5 --retry-connrefused --retry-delay 2 -H 'Host: store.practice.test' "http://${NODE_IP}:${HTTP_NODE_PORT}/" | grep 'Welcome to nginx'
curl -s --retry 5 --retry-connrefused --retry-delay 2 -H 'Host: store.practice.test' "http://${NODE_IP}:${HTTP_NODE_PORT}/reports" | grep 'It works'
这两个响应证明了同一个入口可以将请求路由到不同的 Service。
第一个响应来自 NGINX,第二个响应来自 Apache httpd。页面文本不同是有意设计的,用于证明路径规则确实选择了不同的后端,而不只是证明代理返回了 HTTP 200。
预期匹配的行如下:
<title>Welcome to nginx!</title>
<h1>Welcome to nginx!</h1>
<title>It works! Apache httpd</title>
<p>It works!</p>
这个结果也说明了自定义 Host 请求头的重要性:两个请求都访问同一个节点 IP 和 NodePort,真正决定规则和后端的是主机名与路径。
总结
你复用了已准备好的 ingress-nginx 控制器,将 Ingress 规则连接到具有就绪端点的 Service,并通过一个入口测试了基于主机和路径的路由。


