# 02-05 Nginx 验证矩阵(Ingress / IngressRoute)— 综合一键部署 > **定位**:02 系列尾部,整合 02-01~02-04 的综合一键部署。4 种组合(控制节点/工作节点 × Ingress/IngressRoute)均有具体 Deployment + Service + 路由,节点 IP 访问(如 `http://入口IP/demo-m1/`)。**分课清单**另见 `ansible/files/02-01/`~`02-04/`(与下表 YAML **同构副本**,便于单篇学习;改矩阵时请同步或接受漂移)。 ## TL;DR - **自动化验收**:`./ansible/bin/verify.sh run 02-05` - **关键前置**:按本文「前置条件」准备环境变量/Secret/入口 IP - **成功判据**:达到本文「预期」且 playbook 断言通过 - **排障**:见本文「排障」 ## 前置条件 - 已完成 `01-02-k3s-工作节点.md`(Traefik 与 LB 可用) - **M1**:Deployment 需含 `nodeSelector: node-role.kubernetes.io/control-plane: ""` 及控制平面污点的 **toleration**(见下 YAML),否则控制节点若有 `NoSchedule` 污点会导致 Pod 一直 Pending、访问 /demo-m1 报 "no available server";若控制节点无该标签,需先打标或改 M1 为 hostname(同 M2) - **M2**:Deployment 必须含 `nodeSelector: kubernetes.io/hostname: <控制节点名>`(示例 `ylc61`),按实际修改 - **M3**:Deployment 含 `nodeSelector: node-role.kubernetes.io/worker: ""`,随机调度到任一工作节点;需工作节点有该标签 - **M4**:Deployment 必须含 `nodeSelector: kubernetes.io/hostname: ylc64`(指定工作节点),按实际修改 ## 部署说明(Pod、路径) | 场景 | 路径 | 说明 | | ---------------------- | ---------- | ----------------- | | M1 控制节点 + Ingress | `/demo-m1` | nginx-m1,随机一台控制节点 | | M2 控制节点 + IngressRoute | `/demo-m2` | nginx-m2,指定一台控制节点 | | M3 工作节点 + Ingress | `/demo-m3` | nginx-m3,随机一台工作节点 | | M4 工作节点 + IngressRoute | `/demo-m4` | nginx-m4,指定一台工作节点 | ## 完整配置(与 Ansible 共用) 配置位于 `ansible/files/02-05/`(4 个文件对应 M1~M4),文档与 Ansible 共用此目录: | 文件 | 场景 | 路径 | 节点 | |------|------|------|------| | 01-control-ingress.yaml | M1 控制+Ingress | /demo-m1 | nodeSelector control-plane + toleration | | 02-control-ingressroute.yaml | M2 控制+IngressRoute | /demo-m2 | hostname 指定(默认 ylc61) | | 03-worker-ingress.yaml | M3 工作+Ingress | /demo-m3 | nodeSelector worker(随机) | | 04-worker-ingressroute.yaml | M4 工作+IngressRoute | /demo-m4 | hostname 指定(默认 ylc64) | 按需修改 **M2**(`ylc61`)、**M4**(`ylc64`)的 hostname;**M3** 需工作节点有 `node-role.kubernetes.io/worker` 标签。计算机名使用短主机名(ylc61~ylc64)便于配合 Cloudflare CDN。 ## 部署 ```bash kubectl apply -f ansible/files/02-05/ -R kubectl get pod,svc,ing,ingressroute -n default -o wide ``` ## 验证(用 IP 访问) 直接用入口节点 IP 访问(将 `192.168.2.61` 改为你的入口 IP;按 01-02/01-05 已配 LB 时任选节点 IP)。 ```bash for path in demo-m1 demo-m2 demo-m3 demo-m4; do code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 3 "http://192.168.2.61/${path}/" 2>/dev/null || echo "---") echo "/${path}/: ${code}" done ``` 预期:4 个路径均返回 `200`,页面分别显示 **M1**、**M2**、**M3**、**M4**(及对应说明、Backend: M1 等),便于区分是哪个后端。 ### M1 仍显示 nginx 欢迎页时:进入容器排查 **若访问 /demo-m1 报 "no available server"**:多为 M1 Pod 未调度(Pending),Service 无 endpoint。控制节点常有 `node-role.kubernetes.io/control-plane:NoSchedule` 污点,Deployment 需加上述 **toleration** 才能调度。可用下面命令逐项检查: ```bash # 1. M1 Pod 是否存在、是否 Running(Pending 说明未调度) kubectl get pod -n default -l app=nginx-m1 -o wide # 2. M1 Service 是否有 endpoint(无 endpoint 则 Traefik 报 no available server) kubectl get endpoints -n default nginx-m1 # 3. 若 Pod 为 Pending,看调度失败原因(Events 里会写 taint/未满足) kubectl describe pod -n default -l app=nginx-m1 # 4. 确认控制节点标签与污点(M1 需调度到带 control-plane 的节点,且 Deployment 需有对应 toleration) kubectl get nodes -o custom-columns=NAME:.metadata.name,LABEL:.metadata.labels.node-role\.kubernetes\.io/control-plane,TAINT:.spec.taints ``` 若 Events 为 **`node(s) didn't match Pod's node affinity/selector`**:说明没有任何节点带 `node-role.kubernetes.io/control-plane` 标签,需给控制节点打标(控制节点一般为运行 k3s server 的那台,如 ylc61): ```bash # 先看节点名:kubectl get nodes # 再给控制节点打标(把 ylc61 换成实际控制节点名) kubectl label node ylc61 node-role.kubernetes.io/control-plane= --overwrite ``` 打标后 M1 Pod 会自动调度到该节点并 Running;若仍 Pending,再执行一次 `kubectl get pod -n default -l app=nginx-m1 -o wide` 与 `kubectl describe pod -n default -l app=nginx-m1` 看 Events。 下面为"M1 能访问但仍是默认页"时的容器内排查。在**已配置 KUBECONFIG 的机器**上执行(将 `KUBECONFIG` 改为实际路径,如 `/etc/rancher/k3s/k3s.yaml`): ```bash # 1. 看 html 目录下有哪些文件(是否包含我们挂的 index.html) kubectl exec -n default deployment/nginx-m1 -- ls -la /usr/share/nginx/html/ # 2. 看 index.html 内容(应是 M1 的 HTML,若仍是 Welcome to nginx 说明挂载未生效) kubectl exec -n default deployment/nginx-m1 -- cat /usr/share/nginx/html/index.html # 3. 看 conf.d 下有哪些配置(是否有多个 server,或 default.conf 被覆盖) kubectl exec -n default deployment/nginx-m1 -- ls -la /etc/nginx/conf.d/ # 4. 看 default.conf 内容(应含 root、index、X-Backend M1) kubectl exec -n default deployment/nginx-m1 -- cat /etc/nginx/conf.d/default.conf # 5. 看 nginx 最终生效的 server 配置(确认谁在监听 80、root 指向哪) kubectl exec -n default deployment/nginx-m1 -- nginx -T 2>&1 | grep -A 200 "server {" ``` **根据输出可判断**:若 `index.html` 仍是默认欢迎页 → ConfigMap/volumeMount 未生效或未覆盖到该路径;若 `conf.d/` 下有多于一个 `*.conf` 或 `default.conf` 不是我们的内容 → 配置被覆盖或未挂载;若 `nginx -T` 里 80 端口的 `root` 不是 `/usr/share/nginx/html` 或没有我们的 `location` → 被其他 server 块优先。把上述命令的输出贴出后,即可针对性改 manifest 或挂载方式。 **为何 M1~M4 都是单文件(subPath)挂载,却只有 M1 不正常?** Manifest 里四份写法一致,若只有 M1 仍显示默认页,多半是集群里 nginx-m1 的 Deployment/ReplicaSet 曾用旧 spec 部署过,导致当前 Pod 未带 volumeMount。根因未查清时,最稳妥是**删除 M1 部署再重新部署**(见下)。 **单文件部署(按目录 apply 全部 M1~M4)**: ```bash # 在仓库根目录执行时: kubectl apply -f ansible/files/02-05/ -R # 若当前在 ansible/ 目录下,改用: kubectl apply -f files/02-05-nginx-matrix/ -R ``` **M1 未生效时:删除部署再重新部署(推荐)** 先删 M1 的 Deployment,再 apply 01,这样会新建唯一的 ReplicaSet,不会留下旧 spec 的 Pod。 ```bash # 1. 只删 M1 的 Deployment(Service/Ingress/Middleware/ConfigMap 保留,稍后 apply 会复用或更新) kubectl delete deployment nginx-m1 -n default # 2. 重新部署 M1(在 ansible/ 目录下) kubectl apply -f files/02-05-nginx-matrix/01-control-ingress.yaml # 若在仓库根目录: # kubectl apply -f ansible/files/02-05/01-control-ingress.yaml # 3. 等 Pod Running 后验证 kubectl get pod -n default -l app=nginx-m1 kubectl exec -n default deployment/nginx-m1 -- cat /usr/share/nginx/html/index.html kubectl exec -n default deployment/nginx-m1 -- cat /etc/nginx/conf.d/default.conf ``` 使用 Ansible 部署时,playbook 会自动跑一遍上述诊断并打印「M1 容器内诊断结果」,便于直接查看。 ## Ansible 一键部署 可使用 Ansible playbook 自动完成复制 manifests、apply、等待 Pod 就绪及 curl 验证: - **Playbook**:`ansible/playbooks/verify/02-05.yml` - **Manifests 位置**:`ansible/files/02-05/`(M1 control-plane / M2 M4 节点名 ylc61、ylc64,M3 worker;按实际修改 M2/M4 节点名) - **执行(在 ansible/ 目录下)**: ```bash cd ansible ansible-playbook -i inventory.ini playbooks/verify/02-05.yml ``` 若 manifests 目录未找到,可改为在仓库根目录执行: ```bash ansible-playbook -i ansible/inventory.ini ansible/playbooks/verify/02-05.yml ``` Playbook 会:拷贝 manifests 到控制节点 → **先删除全部 nginx 矩阵 Deployment**(nginx-m1~m4,若存在)→ `kubectl apply` → `rollout restart` M1~M4 → 等待 Pod 就绪 → 输出 16 个目标(4 节点 × 4 路径)的 curl 矩阵。先删再 apply 可避免旧 ReplicaSet 导致任一 Mx 仍显示默认页。 ## 删除 **手动 kubectl apply 的**:用同一目录删除 ```bash kubectl delete -f ansible/files/02-05/ -R ``` **Ansible playbook 部署的**:在仓库根或 ansible 同级的机器上,用 manifests 删除(需配置 KUBECONFIG) ```bash export KUBECONFIG=/etc/rancher/k3s/k3s.yaml # 或从控制节点拷贝 kubeconfig kubectl delete -f ansible/files/02-05/ -R ``` 统一使用仓库真源目录清理,避免与临时目录副本发生偏差。 **按资源名删除**(适用于 manifests 已不可用) ```bash kubectl delete deployment,svc,ingress -n default nginx-m1 nginx-m2 nginx-m3 nginx-m4 kubectl delete ingressroute -n default nginx-m2 nginx-m4 kubectl delete middleware -n default stripprefix-m1 stripprefix-m2 stripprefix-m3 stripprefix-m4 kubectl delete configmap -n default nginx-m1-html nginx-m2-html nginx-m3-html nginx-m4-html ``` ## 下一步 - `03-01-k3s-traefik-dashboard.md`:Dashboard - `03-02-k3s-traefik-acme.md` - 返回 `00-00-构建总览.md` 按导航继续 ## 相关文档 - `02-01`~`02-04`:分篇说明(路径 /demo-m1~m4、nodeSelector 与本文一致) - `03-02-k3s-traefik-acme.md` - `06-01-k3s-networkpolicy-故障排查.md` ## 排障 - **先看 playbook 输出**:失败时先定位是 deploy/wait/http_check 哪一步。 - **集群侧总览**:`kubectl get nodes -o wide`、`kubectl -n kube-system get pods -o wide`。 - **事件与日志**:`kubectl -n describe ...`、`kubectl -n logs ... --tail=200`。