Kubernetes Service 进阶与 Ingress 实战笔记(MetalLB · ExternalName · IPVS · Ingress)
Kubernetes Service 进阶与 Ingress 实战笔记(MetalLB · ExternalName · IPVS · Ingress)
本手册由
master30_2026-08-11_10_01_10.log(8/11 全天实验)整理而成,在 kubeadm v1.30.2 集群(master30 + worker31/32,运行第 6 天)上完成。
一条主线:先把 Service 的流量链路(iptables)彻底看懂 → 用 MetalLB 让 Service 拥有外部 IP → ExternalName 映射集群外域名 → 验证轮询 / 会话保持 / 版本权重 → 把 kube-proxy 从 iptables 切成 IPVS → 最后用 Ingress-nginx 统一对外暴露多域名、多路径、HTTPS。命令提示符保留真实时间戳,报错与修复全部来自实际操作。
📑 目录
| # | 章节 | 核心内容 |
|---|---|---|
| 一 | 整体框架与实验总览 | 实验流程图、节点环境、主题速览 |
| 二 | Service 流量链路:iptables 分析 | KUBE-SVC / KUBE-SEP / KUBE-MARK-MASQ、随机概率 |
| 三 | MetalLB:LoadBalancer 落地 | 部署控制器、IPAddressPool、L2Advertisement、外部 IP |
| 四 | ExternalName:集群外域名映射 | CNAME、dig 验证、busybox 容器内访问 |
| 五 | Service 轮询与会话保持 | 20 次压测、sessionAffinity: ClientIP |
| 六 | ConfigMap 挂载与版本权重分流 | 副本 8:2、流量占比验证 |
| 七 | kube-proxy 切换 IPVS 模式 | mode 配置、rollout restart、ipvsadm 查看 |
| 八 | IPVS 调度算法探究 | rr 轮询 20/20/20、修改 scheduler 的正确姿势 |
| 九 | Ingress-nginx 控制器部署 | 镜像替换、Job immutable 报错与修复 |
| 十 | Ingress 多域名路由 | 一个 Ingress 多个 host |
| 十一 | Ingress 多路径路由 | / 与 /games 分发、/etc/hosts 优先级 |
| 十二 | Ingress 路径重写 | rewrite-target 与 use-regex |
| 十三 | Ingress TLS 与强制 HTTPS | 自签证书、tls secret、308 跳转 |
| 十四 | 环境清理与恢复 | 删除 ingress-nginx / ingress 命名空间 |
| 十五 | 常见错误与排查(重点) | 本次实验全部报错:原因 + 修复 |
| 十六 | 命令速查表与小结 | Service / IPVS / Ingress 常用命令 |
一、整体框架与实验总览
1.1 实验流程图
1.2 节点环境
| 节点 | 主机名 | IP | 角色 |
|---|---|---|---|
| master30 | master30.ningcode.cn | 10.1.8.30 | 控制平面 |
| worker31 | worker31.ningcode.cn | 10.1.8.31 | 工作节点 |
| worker32 | worker32.ningcode.cn | 10.1.8.32 | 工作节点 |
- Pod 网段
10.224.0.0/16;Service 网段10.96.0.0/12 - 私有镜像仓库
hub.laoma.cloud(拉取加速用);实验软件包来自192.168.46.200/class/course-materials/softwares/stage03/ - 本次会话 kubectl 当前命名空间沿用上次实验的
services(kubectl config set-context会持久化),Ingress 实验单独使用ingress命名空间
1.3 主题速览
| 主题 | 核心知识点 | 日志时间 |
|---|---|---|
| Service 链路 | KUBE-SVC / KUBE-SEP / MARK-MASQ、random 概率 0.5 | 10:04–10:19 |
| MetalLB | metallb-native、IPAddressPool、L2Advertisement | 11:04–11:15 |
| ExternalName | CNAME 映射、dig 验证、容器内 wget | 11:16–12:29 |
| Service 负载 | 随机概率压测、会话保持 ClientIP | 12:30–12:33 |
| ConfigMap | 版本权重 8:2、流量占比验证 | 13:45–13:58 |
| IPVS | kube-proxy mode=ipvs、ipvsadm 验证 | 14:17–15:09 |
| Ingress | 控制器部署、多域名 / 多路径 / 重写 / TLS | 15:09–16:47 |
二、Service 流量链路:iptables 分析
2.1 为什么要看 iptables
Service 只是一个"虚拟 IP + 规则",真正干活的是节点上的 kube-proxy。iptables 模式下,kube-proxy 会生成三类链:
KUBE-SERVICES:流量总入口,命中 ClusterIP 的包进入对应的KUBE-SVC-xxx链KUBE-SVC-xxx:负载均衡链,把包随机/按概率转发到某个KUBE-SEP-xxx(后端 Pod)KUBE-SEP-xxx:Endpoint(Pod)链,做 DNAT 到真实 Pod IPKUBE-MARK-MASQ:给来自集群外部的包打上0x4000标记,后续统一做 SNAT(源地址转换)
2.2 查看 MASQ 标记链
root@master30 ~ 10:04:40# iptables-save |grep KUBE-MARK-MASQ
:KUBE-MARK-MASQ - [0:0]
-A KUBE-EXT-7D76YWGERGEPC4GC -m comment --comment "masquerade traffic for services/web external destinations" -j KUBE-MARK-MASQ
-A KUBE-MARK-MASQ -j MARK --set-xmark 0x4000/0x4000
-A KUBE-SEP-HUGMKHV43KCB5G6S -s 10.224.195.130/32 -m comment --comment "services/web" -j KUBE-MARK-MASQ
-A KUBE-SEP-N6LLPMTIQEKB7LJM -s 10.224.83.129/32 -m comment --comment "services/web" -j KUBE-MARK-MASQ
......(kube-dns、kubernetes 等其他 Service 的链省略)
-A KUBE-SVC-7D76YWGERGEPC4GC ! -s 10.224.0.0/16 -d 10.107.179.7/32 -p tcp -m comment --comment "services/web cluster IP" -m tcp --dport 8080 -j KUBE-MARK-MASQ
--set-xmark 0x4000/0x4000:打标记,告诉 conntrack/iptables 这个包需要被 MASQUERADE! -s 10.224.0.0/16:源地址不是 Pod 网段(即来自集群外部)的包才需要打标记- 当时
webService 端口还是 8080(后续应用 LoadBalancer 清单后才改成 80)
2.3 查看负载均衡链(随机概率)
root@master30 ~ 10:18:38# iptables-save |grep KUBE-SVC-7D76YWGERGEPC4GC
:KUBE-SVC-7D76YWGERGEPC4GC - [0:0]
-A KUBE-EXT-7D76YWGERGEPC4GC -j KUBE-SVC-7D76YWGERGEPC4GC
-A KUBE-SERVICES -d 10.107.179.7/32 -p tcp -m comment --comment "services/web cluster IP" -m tcp --dport 8080 -j KUBE-SVC-7D76YWGERGEPC4GC
-A KUBE-SVC-7D76YWGERGEPC4GC ! -s 10.224.0.0/16 -d 10.107.179.7/32 -p tcp -m tcp --dport 8080 -j KUBE-MARK-MASQ
-A KUBE-SVC-7D76YWGERGEPC4GC -m comment --comment "services/web -> 10.224.195.130:80" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-HUGMKHV43KCB5G6S
-A KUBE-SVC-7D76YWGERGEPC4GC -m comment --comment "services/web -> 10.224.83.129:80" -j KUBE-SEP-N6LLPMTIQEKB7LJM
- 后端 1(10.224.195.130)带
--mode random --probability 0.50000000000:只有 50% 的概率被选中 - 后端 2 没有概率参数:剩下的 50% 走它(隐含默认规则)
- 实验者批注(日志原文):
root@master30 ~ 10:19:02# #--probability 0.50000000000采用随机模块,所以不能完全轮询
2.4 要点
- iptables 模式本质是随机概率分流,两个后端时大致 50/50,但不是严格轮询,压测小样本时会看到 11:9、10:10 之类的偏差
- 链路小结:
KUBE-SERVICES → KUBE-SVC(随机选后端)→ KUBE-SEP(DNAT 到 Pod)→ KUBE-MARK-MASQ(外部流量打标记做 SNAT) - 这个结论为后面"为什么换 IPVS(严格 rr 轮询)"埋下伏笔
三、MetalLB:LoadBalancer 落地
3.1 概念
云平台上有云 LB 插件给 LoadBalancer 类型的 Service 分配外部 IP;裸金属集群没有,需要 MetalLB。本次部署 v0.14.8,使用 L2 模式:speaker 以 ARP 应答的方式把池子里分配的 VIP 宣告到二层网络,流量先到节点再 DNAT 进集群。
3.2 下载、解压、改镜像
root@master30 ~ 11:04:18# wget http://192.168.46.200/class/course-materials/softwares/stage03/metallb-0.14.8.tar.gz
--2026-08-11 11:04:23-- http://192.168.46.200/class/course-materials/softwares/stage03/metallb-0.14.8.tar.gz
HTTP request sent, awaiting response... 200 OK
metallb-0.14.8.tar.gz 100%[==================================================>] 14.15M 11.2MB/s in 1.3s
root@master30 ~ 11:04:24# tar -xf metallb-0.14.8.tar.gz
# 查看清单里的镜像(注意:目录名后面要带 .yaml)
root@master30 ~ 11:05:27# grep image metallb-0.14.8/config/manifests/metallb-native.yaml
image: quay.io/metallb/controller:v0.14.8
image: quay.io/metallb/speaker:v0.14.8
# 替换成私有仓库,避免走外网拉镜像太慢
root@master30 ~ 11:05:33# sed -i 's/quay.io/hub.laoma.cloud/g' metallb-0.14.8/config/manifests/metallb-native.yaml
root@master30 ~ 11:08:03# #修改镜像,走外网太慢了
3.3 部署控制器与 speaker
root@master30 ~ 11:08:30# kubectl apply -f metallb-0.14.8/config/manifests/metallb-native.yaml
namespace/metallb-system created
customresourcedefinition.apiextensions.k8s.io/bfdprofiles.metallb.io created
customresourcedefinition.apiextensions.k8s.io/ipaddresspools.metallb.io created
customresourcedefinition.apiextensions.k8s.io/l2advertisements.metallb.io created
......
deployment.apps/controller created
daemonset.apps/speaker created
validatingwebhookconfiguration.admissionregistration.k8s.io/metallb-webhook-configuration created
root@master30 ~ 11:09:08# kubectl get all -n metallb-system
NAME READY STATUS RESTARTS AGE
pod/controller-d6499775f-68xb9 1/1 Running 0 29s
pod/speaker-8qhf8 0/1 Running 0 29s
pod/speaker-h2m6g 0/1 Running 0 29s
pod/speaker-r5gpn 0/1 Running 0 29s
......
daemonset.apps/speaker 3 3 0 3 0 kubernetes.io/os=linux 29s
deployment.apps/controller 1/1 1 1 29s
speaker 是 DaemonSet,每个节点一个;刚创建时 0/1,等镜像拉取完成后变 Running。
3.4 配置地址池(IPAddressPool)与 L2 通告
root@master30 ~ 11:09:23# cat << 'EOF' > ippool.yaml
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: first-pool
namespace: metallb-system
spec:
addresses:
- 10.1.8.40-10.1.8.80
EOF
root@master30 ~ 11:10:25# kubectl apply -f ippool.yaml
ipaddresspool.metallb.io/first-pool created
root@master30 ~ 11:10:30# cat << 'EOF' > L2.yaml
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: example
namespace: metallb-system
EOF
root@master30 ~ 11:10:36# kubectl apply -f L2.yaml
l2advertisement.metallb.io/example created
IPAddressPool:可分配的 VIP 范围(本次为 10.1.8.40 ~ 10.1.8.80)L2Advertisement:声明用 L2(ARP)方式通告这些 IP
3.5 把 Service 改成 LoadBalancer
先用 kubectl expose --dry-run=client 生成清单再应用:
root@master30 ~ 11:10:40# kubectl expose deployment web --type LoadBalancer --port=80 --target-port=80 -o yaml --dry-run=client > service-LoadBalancer.yaml
root@master30 ~ 11:15:19# cat service-LoadBalancer.yaml
apiVersion: v1
kind: Service
metadata:
creationTimestamp: null
labels:
app: web
name: web
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: web
type: LoadBalancer
status:
loadBalancer: {}
root@master30 ~ 11:15:25# kubectl apply -f service-LoadBalancer.yaml
service/web configured
root@master30 ~ 11:15:40# kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
web LoadBalancer 10.107.179.7 10.1.8.40 80:30639/TCP 73m
root@master30 ~ 11:15:46# curl -s http://10.1.8.40:80
<html><body><h1>It works!</h1></body></html>
EXTERNAL-IP变为10.1.8.40,来自地址池的第一个可用 IP,直接 curl 即可访问- 该 IP 后来被
webService 释放,Ingress-nginx 控制器的 LoadBalancer 又复用了它(见第九章)
3.6 要点
- 裸金属上 LoadBalancer ≠ 云负载均衡,只是 MetalLB 分配的一个 VIP + 二层 ARP 宣告
- 换镜像用
sed批量替换,替换后建议grep image确认 - 删除 LoadBalancer Service 会释放 VIP,池子里可重复使用
四、ExternalName:集群外域名映射
4.1 概念
ExternalName 类型的 Service 不建 ClusterIP,也不生成 iptables/ipvs 规则、没有 Endpoints。它只在集群 DNS(CoreDNS)里注册一条 CNAME 记录:集群内访问 my-service.services.svc.cluster.local 时,DNS 会返回配置的 externalName 对应的 A 记录。
4.2 编写 ExternalName
第一次在 YAML 里写了 namespace: prod,但该命名空间不存在,报错:
root@master30 ~ 11:42:44# cat ExternalName.yaml
apiVersion: v1
kind: Service
metadata:
name: my-service
namespace: prod
spec:
type: ExternalName
externalName: www.ningcode.cn
root@master30 ~ 11:42:47# kubectl apply -f ExternalName.yaml
Error from server (NotFound): error when creating "ExternalName.yaml": namespaces "prod" not found
去掉 namespace 字段(落到当前命名空间 services),重新应用:
root@master30 ~ 11:43:17# cat ExternalName.yaml
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
type: ExternalName
externalName: www.ningcode.cn
root@master30 ~ 11:43:20# kubectl apply -f ExternalName.yaml
service/my-service created
root@master30 ~ 11:43:23# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-service ExternalName <none> www.ningcode.cn <none> 11s
web LoadBalancer 10.107.179.7 10.1.8.40 80:30639/TCP 101m
CLUSTER-IP为<none>,EXTERNAL-IP列显示的就是目标域名www.ningcode.cn
4.3 安装 dig 验证工具
root@master30 ~ 11:43:52# apt install -y bind-dnsutils
E: Package 'bind-dnsutils' has no installation candidate
# Ubuntu 24.04 包名是 bind9-dnsutils
root@master30 ~ 11:44:13# apt install -y bind9-dnsutils
......(安装过程省略)
4.4 用 dig 看集群 DNS 解析
# web 普通 Service:返回 ClusterIP
root@master30 ~ 11:44:55# dig @10.96.0.10 web.services.svc.cluster.local
;; ANSWER SECTION:
web.services.svc.cluster.local. 30 IN A 10.107.179.7
# my-service(ExternalName):返回 CNAME + 真实 A 记录
root@master30 ~ 11:45:26# dig @10.96.0.10 my-service.services.svc.cluster.local
;; ANSWER SECTION:
my-service.services.svc.cluster.local. 30 IN CNAME www.ningcode.cn.
www.ningcode.cn. 30 IN A 47.99.246.250
@10.96.0.10指定用 CoreDNS 查询(kube-dns 的 ClusterIP)- 普通 Service → A 记录指向 ClusterIP;ExternalName → CNAME 指向外部域名,再递归解析出公网 A 记录(47.99.246.250 是用户云服务器)
4.5 宿主机 vs 容器内访问
# 宿主机直接 curl:解析失败(宿主机的 /etc/resolv.conf 不走集群 DNS)
root@master30 ~ 11:46:26# curl my-service.services.svc.cluster.local
curl: (6) Could not resolve host: my-service.services.svc.cluster.local
进 busybox 容器验证(容器走集群 DNS):
# 第一次写错参数
root@master30 ~ 11:47:09# kubectl run test --rm --it --image busybox -- sh
error: unknown flag: --it
root@master30 ~ 11:47:31# kubectl run test --rm -it --image busybox -- sh
If you don't see a command prompt, try pressing enter.
/ # curl my-service.services.svc.cluster.local
sh: curl: not found # busybox 里没有 curl,用 wget
/ # wget my-service.services.svc.cluster.local
Connecting to my-service.services.svc.cluster.local (47.99.246.250:80)
saving to 'index.html'
index.html 100% |**********************************************| 1326 0:00:00 ETA
'index.html' saved
/ # exit
pod "test" deleted
4.6 要点
- ExternalName 适合把集群外服务(老系统、云数据库、公网网站)映射成集群内 DNS 名字,业务代码不用改地址
- 集群内解析必须用集群 DNS:宿主机 curl 会失败,容器内正常;排障用
dig @10.96.0.10 名称.services.svc.cluster.local kubectl run交互参数是-it,写成--it会报 unknown flag
五、Service 轮询与会话保持
5.1 重建 web 应用
原 web 资源还在,先删再建,避免同名冲突:
root@master30 ~ 12:30:32# kubectl create deployment web --image=hub.laoma.cloud/library/httpd --replicas=2
error: failed to create deployment: deployments.apps "web" already exists
root@master30 ~ 12:30:41# kubectl delete deployments.apps web
deployment.apps "web" deleted
root@master30 ~ 12:30:50# kubectl create deployment web --image=hub.laoma.cloud/library/httpd --replicas=2
deployment.apps/web created
# Service 同名也冲突,先删旧的 LoadBalancer Service 再 expose
root@master30 ~ 12:30:55# kubectl expose deployment web --port 80
Error from server (AlreadyExists): services "web" already exists
root@master30 ~ 12:31:12# kubectl delete service web
service "web" deleted
root@master30 ~ 12:31:32# kubectl expose deployment web --port 80
service/web exposed
5.2 给两个 Pod 写入不同的主页内容
root@master30 ~ 12:31:38# for pod in $(kubectl get pods -o name | awk -F/ '{print $2}'); do kubectl exec -it $pod -- bash -c "echo $pod > htdocs/index.html"; done
root@master30 ~ 12:32:01# kubectl get svc web
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
web ClusterIP 10.104.183.228 <none> 80/TCP 49s
5.3 压测:iptables 随机概率(11:9)
root@master30 ~ 12:32:37# for i in {1..20};do curl -s 10.104.183.228;done|sort |uniq -c
11 web-5885dfff8c-nfm6n
9 web-5885dfff8c-pqwsz
20 次请求不是严格的 10:10,而是 11:9——正是第二章 iptables random 0.5 概率分流的体现。
5.4 开启会话保持
root@master30 ~ 12:33:03# kubectl patch svc web -p '{"spec":{"sessionAffinity":"ClientIP"}}'
service/web patched
root@master30 ~ 12:33:07# for i in {1..20};do curl -s 10.104.183.228;done|sort |uniq -c
20 web-5885dfff8c-nfm6n
root@master30 ~ 12:33:17# for i in {1..20};do curl -s 10.104.183.228;done|sort |uniq -c
20 web-5885dfff8c-nfm6n
root@master30 ~ 12:33:22# for i in {1..20};do curl -s 10.104.183.228;done|sort |uniq -c
20 web-5885dfff8c-nfm6n
5.5 要点
- 默认
sessionAffinity: None:同源 IP 的请求会被随机分到不同后端 sessionAffinity: ClientIP:按来源 IP 哈希固定到同一后端(20/20),适合有状态会话(登录态、购物车)- 会话保持粒度是 ClientIP,不是 Session/Cookie;要按 Cookie 做粘性需要 Ingress 的 affinity 或应用层解决
- 同名资源(Deployment / Service)创建前先确认是否存在,避免 AlreadyExists 报错
六、ConfigMap 挂载与版本权重分流
6.1 准备两个版本的文件并创建 ConfigMap
root@master30 ~ 12:33:23# mkdir web
root@master30 ~ 13:45:26# echo hello nginx 1.28 > web/index28.html
root@master30 ~ 13:45:35# echo hello nginx 1.29 > web/index29.html
root@master30 ~ 13:46:04# kubectl create configmap web --from-file=./web
configmap/web created
root@master30 ~ 13:47:10# kubectl get configmaps web -o yaml |grep ^data -A4
data:
index28.html: |
hello nginx 1.28
index29.html: |
hello nginx 1.29
6.2 两个版本 Deployment 挂载 ConfigMap(参考清单)
日志中的 YAML 由 vim 编写、未回显,这里给出与实验结果一致的参考写法:两个 Deployment 的 Pod 打相同标签 app: web(这样同一个 Service 都能选中),各自用 subPath 挂载不同版本的 index 文件:
# webapp-1.28.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-28
spec:
replicas: 2
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
version: "1.28"
spec:
containers:
- name: httpd
image: hub.laoma.cloud/library/httpd
ports:
- containerPort: 80
volumeMounts:
- name: web-cm
mountPath: /usr/local/apache2/htdocs/index.html
subPath: index28.html
volumes:
- name: web-cm
configMap:
name: web
# webapp-1.29.yaml:同上,name: web-29、subPath: index29.html、version: "1.29"
# webapp-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: web
spec:
selector:
app: web
ports:
- port: 80
targetPort: 80
6.3 部署并缩放成 8:2
root@master30 ~ 13:48:14# kubectl apply -f webapp-1.28.yaml
deployment.apps/web-28 created
root@master30 ~ 13:48:37# kubectl apply -f webapp-svc.yaml
service/web configured
root@master30 ~ 13:50:38# kubectl apply -f webapp-1.29.yaml
deployment.apps/web-29 created
root@master30 ~ 13:50:48# kubectl scale deployment web-28 --replicas 8
deployment.apps/web-28 scaled
root@master30 ~ 13:51:09# kubectl scale deployment web-29 --replicas 2
deployment.apps/web-29 scaled
6.4 压测观察流量占比
第一轮:扩容完立刻压,50 次全部落在 1.28(刚扩容的 web-29 Pod 还没进 Endpoints / 未就绪):
root@master30 ~ 13:51:14# for i in {1..50}; do curl -s 10.104.183.228; done | sort -n|uniq -c
50 hello nginx 1.28
第二轮:删掉所有资源(kubectl delete deployments.apps web-28 web-29、kubectl delete service web)后重新 apply,等 Pod 全部 Running 再压测,占比与副本比 8:2 基本一致:
root@master30 ~ 13:56:28# kubectl apply -f webapp-1.28.yaml
root@master30 ~ 13:56:36# kubectl apply -f webapp-svc.yaml
service/web created
root@master30 ~ 13:56:54# kubectl apply -f webapp-1.29.yaml
root@master30 ~ 13:57:09# kubectl scale deployment web-28 --replicas 8
root@master30 ~ 13:57:28# kubectl scale deployment web-29 --replicas 2
root@master30 ~ 13:57:34# for i in {1..50}; do curl -s 10.108.166.139; done | sort -n|uniq -c
44 hello nginx 1.28
6 hello nginx 1.29
root@master30 ~ 13:57:51# for i in {1..50}; do curl -s 10.108.166.139; done | sort -n|uniq -c
42 hello nginx 1.28
8 hello nginx 1.29
root@master30 ~ 13:58:01# for i in {1..50}; do curl -s 10.108.166.139; done | sort -n|uniq -c
46 hello nginx 1.28
4 hello nginx 1.29
6.5 要点
- Service 的负载均衡粒度是 Pod(Endpoint),副本数 8:2 → 流量占比约 8:2,这就是"按副本数做灰度/权重分流"的原理
- 压测前先
kubectl get pods确认全部Running、kubectl get endpoints web确认 Endpoints 数量=10,否则结果会失真 - ConfigMap 用
subPath挂载到已有文件时,改 ConfigMap 内容不会热更新到容器内已挂载文件,需要重建 Pod 才能生效
七、kube-proxy 切换 IPVS 模式
7.1 查看当前模式
root@master30 ~ 14:17:24# kubectl create ns services
namespace/services created
root@master30 ~ 14:17:31# kubectl config set-context --current --namespace services
Context "kubernetes-admin@kubernetes" modified.
root@master30 ~ 14:17:50# kubectl get cm -n kube-system kube-proxy -o yaml|grep mode
mode: "" # 空 = 默认 iptables
# 从 kube-proxy 日志确认
root@master30 ~ 14:18:55# kubectl get pods -n kube-system -l k8s-app=kube-proxy -o name
pod/kube-proxy-2c5tb
pod/kube-proxy-5ltr7
pod/kube-proxy-8mg2v
root@master30 ~ 14:18:58# kubectl logs -n kube-system kube-proxy-2c5tb |grep Using
I0811 05:39:05.970175 1 server_linux.go:69] "Using iptables proxy"
I0811 05:39:06.061279 1 server_linux.go:165] "Using iptables Proxier"
7.2 修改配置并重启 kube-proxy
root@master30 ~ 14:19:27# kubectl edit configmaps -n kube-system kube-proxy
# 把 mode: "" 改成 mode: "ipvs",保存退出
root@master30 ~ 14:20:16# kubectl get cm -n kube-system kube-proxy -o yaml|grep mode
mode: "ipvs"
root@master30 ~ 14:20:20# kubectl rollout restart daemonset -n kube-system kube-proxy
daemonset.apps/kube-proxy restarted
# 新 Pod 起来后看日志
root@master30 ~ 14:20:57# kubectl get pods -n kube-system -l k8s-app=kube-proxy -o name
pod/kube-proxy-dgzk8
pod/kube-proxy-n284c
pod/kube-proxy-ndn2h
root@master30 ~ 14:21:20# kubectl logs -n kube-system kube-proxy-dgzk8 |grep Using
I0811 06:21:01.740336 1 server_linux.go:233] "Using ipvs Proxier"
7.3 建 3 副本应用并压测
root@master30 ~ 14:21:39# kubectl create deployment web --image=nginx --replicas=3
deployment.apps/web created
把每个 Pod 的主页写成自己的名字(注意:nginx 首页路径是 /usr/share/nginx/html/index.html):
root@master30 ~ 14:25:02# kubectl get pods -o wide |awk '{print $1,$6}'
NAME IP
web-7c56dcdb9b-dngnd 10.224.195.146
web-7c56dcdb9b-ngmn5 10.224.83.146
web-7c56dcdb9b-w4fgq 10.224.83.147
root@master30 ~ 14:25:24# curl http://10.224.195.146
web-7c56dcdb9b-dngnd
root@master30 ~ 14:25:41# curl http://10.224.83.146
web-7c56dcdb9b-ngmn5
root@master30 ~ 14:25:54# curl http://10.224.83.147
web-7c56dcdb9b-w4fgq
root@master30 ~ 14:25:59# kubectl expose deployment web --port=80
service/web exposed
root@master30 ~ 14:26:21# kubectl get svc web
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
web ClusterIP 10.96.153.122 <none> 80/TCP 5s
7.4 ipvsadm 查看虚拟服务与后端
root@master30 ~ 14:26:26# ipvsadm -Lnt 10.96.153.122:80
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 10.96.153.122:80 rr
-> 10.224.83.146:80 Masq 1 0 0
-> 10.224.83.147:80 Masq 1 0 0
-> 10.224.195.146:80 Masq 1 0 0
Scheduler列为rr(Round Robin 轮询),Masq表示转发方式为 NAT 伪装
7.5 压测:严格轮询 20/20/20
root@master30 ~ 14:26:52# for i in {1..60}; do curl -s 10.96.153.122; done| sort | uniq -c
20 web-7c56dcdb9b-dngnd
20 web-7c56dcdb9b-ngmn5
20 web-7c56dcdb9b-w4fgq
60 次请求严格 20/20/20——与 iptables 的随机概率形成鲜明对比。
7.6 要点
- 切换三步:
kubectl edit cm -n kube-system kube-proxy改 mode →kubectl rollout restart daemonset -n kube-system kube-proxy→ 看日志确认Using ipvs Proxier - ipvs 模式用内核的 LVS 能力,默认
rr轮询、性能更好、规则更直观(ipvsadm -Lnt一眼看到后端列表) - 建完 Deployment 后 Pod 还没 Ready 时立刻 exec 会报
container not found,等 PodRunning再操作
八、IPVS 调度算法探究
8.1 实验过程
日志里想改调度算法,但 kubectl edit 没有保存就退出了,所以本次没有真正改成功(这也是一个很好的反面教材):
root@master30 ~ 14:49:40# #更改调度算法:
root@master30 ~ 15:08:53# kubectl edit configmap kube-proxy -n kube-system
Edit cancelled, no changes made.
8.2 正确姿势(按实验意图补齐)
IPVS 调度算法由 kube-proxy 的 ConfigMap 控制,默认 rr,可改成 wrr(加权轮询)、lc(最少连接)、sh(源地址哈希,天然会话保持)等:
# 1. 编辑 ConfigMap,把 ipvs.scheduler(或顶层 scheduler)字段改成目标算法
kubectl edit configmap kube-proxy -n kube-system
# scheduler: "rr" → scheduler: "wrr"
# 2. 重启 DaemonSet 使配置生效
kubectl rollout restart daemonset -n kube-system kube-proxy
# 3. 验证:Scheduler 列变为 wrr
ipvsadm -Lnt 10.96.153.122:80
8.3 要点
kubectl edit不保存退出会提示Edit cancelled, no changes made.,配置不会变- 改完 ConfigMap 必须重启 kube-proxy 才生效
rr适合后端能力相同的场景;后端性能有差异时用wrr配 Weight;长连接 / 有状态场景用sh(等价 ClientIP 会话保持)
九、Ingress-nginx 控制器部署
9.1 概念
Service 的负载均衡局限在"IP + 端口"层面;Ingress 工作在 HTTP 层,按域名 / 路径把请求路由到不同 Service。Ingress 只是规则,真正干活的是 Ingress Controller(本次用 ingress-nginx v1.11.2,本质是一个反向代理 Pod)。
9.2 下载、解压、替换镜像
root@master30 ~ 15:09:34# kubectl create ns ingress
namespace/ingress created
root@master30 ~ 15:09:53# kubectl config set-context --current --namespace ingress
Context "kubernetes-admin@kubernetes" modified.
root@master30 ~ 15:10:04# wget http://192.168.46.200/class/course-materials/softwares/stage03/ingress-nginx-controller-v1.11.2.tar.gz
--2026-08-11 15:10:16-- http://192.168.46.200/class/course-materials/softwares/stage03/ingress-nginx-controller-v1.11.2.tar.gz
Length: 3902473 (3.7M) [application/octet-stream]
ingress-nginx-controller-v1.11.2. 100%[=====================================>] 3.72M 11.2MB/s in 0.3s
root@master30 ~ 15:10:16# tar -xf ingress-nginx-controller-v1.11.2.tar.gz
root@master30 ~ 15:10:34# grep image: ingress-nginx-controller-v1.11.2/deploy/static/provider/cloud/deploy.yaml|uniq
image: registry.k8s.io/ingress-nginx/controller:v1.11.2@sha256:d5f8217feeac4887cb1ed21f27c2674e58be06bd8f5184cacea2a69abaf78dce
image: registry.k8s.io/ingress-nginx/kube-webhook-certgen:v1.4.3@sha256:a320a50cc91bd15fd2d6fa6de58bd98c1bd64b9a6f926ce23a600d87043455a3
# 去掉 @sha256 digest 并换成私有仓库(apply 之前必须改好!)
root@master30 ~ 15:19:33# sed -ir 's#@sha256.*##;s/registry.k8s.io/hub.laoma.cloud/' ingress-nginx-controller-v1.11.2/deploy/static/provider/cloud/deploy.yaml
root@master30 ~ 15:20:14# grep image: ingress-nginx-controller-v1.11.2/deploy/static/provider/cloud/deploy.yaml|uniq
image: hub.laoma.cloud/ingress-nginx/controller:v1.11.2
image: hub.laoma.cloud/ingress-nginx/kube-webhook-certgen:v1.4.3
9.3 踩坑:Job 的 template 不可变
第一次 apply 时用的是原镜像(registry.k8s.io),Job 已创建;第二次(改完镜像)直接再 apply 想原地更新 Job,报错 field is immutable:
root@master30 ~ 15:19:41# kubectl apply -f ingress-nginx-controller-v1.11.2/deploy/static/provider/cloud/deploy.yaml
namespace/ingress-nginx unchanged
......
deployment.apps/ingress-nginx-controller configured
Error from server (Invalid): error when applying patch:
......
Job.batch "ingress-nginx-admission-create" is invalid: spec.template: Invalid value: ... : field is immutable
原因:Job 的
spec.template创建后不可修改。第一次 apply 用旧镜像创建了ingress-nginx-admission-create/patch两个 Job,第二次 apply 想改它们 → 报错。
修复:先删掉两个旧 Job,再重新 apply:
root@master30 ~ 15:20:54# kubectl delete job -n ingress-nginx ingress-nginx-admission-create
root@master30 ~ 15:20:54# kubectl delete job -n ingress-nginx ingress-nginx-admission-patch
job.batch "ingress-nginx-admission-create" deleted
job.batch "ingress-nginx-admission-patch" deleted
root@master30 ~ 15:21:48# kubectl apply -f ingress-nginx-controller-v1.11.2/deploy/static/provider/cloud/deploy.yaml
namespace/ingress-nginx unchanged
......
deployment.apps/ingress-nginx-controller configured
job.batch/ingress-nginx-admission-create created
job.batch/ingress-nginx-admission-patch created
9.4 验证控制器就绪
root@master30 ~ 15:21:53# kubectl get all -n ingress-nginx
NAME READY STATUS RESTARTS AGE
pod/ingress-nginx-admission-create-wcc5z 0/1 Completed 0 11s
pod/ingress-nginx-admission-patch-452n6 0/1 Completed 0 11s
pod/ingress-nginx-controller-8659885ffd-kzdww 1/1 Running 0 2m15s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/ingress-nginx-controller LoadBalancer 10.101.176.159 10.1.8.40 80:31920/TCP,443:30883/TCP 10m
service/ingress-nginx-controller-admission ClusterIP 10.105.228.17 <none> 443/TCP 10m
......
job.batch/ingress-nginx-admission-create Complete 1/1 3s 11s
job.batch/ingress-nginx-admission-patch Complete 1/1 3s 11s
- 控制器 Service 是 LoadBalancer,MetalLB 分配
10.1.8.40(上一轮 web LoadBalancer 已删除,VIP 被复用) - 两个 admission Job
Completed说明 webhook 证书已生成
顺手清理旧镜像的 ReplicaSet(已缩到 0,删掉更干净):
root@master30 ~ 15:23:00# kubectl delete rs ingress-nginx-controller-7d4db76476 -n ingress-nginx
replicaset.apps "ingress-nginx-controller-7d4db76476" deleted
9.5 要点
- 镜像替换必须在 apply 之前完成;对已创建的资源改 spec 前先
kubectl delete对应资源 sed -ir 's#@sha256.*##;s/registry.k8s.io/hub.laoma.cloud/':#作分隔符避免与/冲突,@sha256.*去掉 digest- 控制器入口是
service/ingress-nginx-controller(LoadBalancer 10.1.8.40,80/443),后面所有 Ingress 规则都从它进入
十、Ingress 多域名路由
10.1 准备两个后端应用
webapp01 / webapp02 各 2 副本(httpd),分别写入不同主页内容,并 expose 成 ClusterIP Service:
root@master30 ~ 15:23:43# kubectl create deployment webapp01 --image=hub.laoma.cloud/library/httpd --replicas=2
deployment.apps/webapp01 created
root@master30 ~ 15:53:08# kubectl exec -it webapp01-587d58c655-4cvxf -- bash -c "echo hello webapp01 pod1 > htdocs/index.html"
root@master30 ~ 15:53:47# kubectl exec -it webapp01-587d58c655-lbfwx -- bash -c "echo hello webapp01 pod2 > htdocs/index.html"
root@master30 ~ 15:54:09# kubectl expose deployment webapp01 --port=80 --target-port=80
service/webapp01 exposed
root@master30 ~ 15:54:35# kubectl create deployment webapp02 --image=hub.laoma.cloud/library/httpd --replicas=2
deployment.apps/webapp02 created
# 给 webapp02 两个 Pod 写主页,并额外建 games 目录
root@master30 ~ 15:57:30# kubectl exec -it webapp02-56cfd678d6-cd4ss -- bash -c "echo hellow webapp02 pod2 > htdocs/index.html;mkdir htdocs/games;echo hello webapp02 game2 > htdocs/games/index.html"
root@master30 ~ 15:58:01# kubectl expose deployment webapp02 --port=80 --target-port=80
service/webapp02 exposed
10.2 多域名 Ingress(一个 Ingress 多个 host)
日志中清单由 vim 编写未回显,describe 显示的实际规则为 webapp01.ningcode.cn / 与 webapp02.ningcode.cn /,等价写法如下:
# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: multi-host-ingress
spec:
ingressClassName: nginx
rules:
- host: webapp01.ningcode.cn
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: webapp01
port:
number: 80
- host: webapp02.ningcode.cn
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: webapp02
port:
number: 80
root@master30 ~ 15:59:25# kubectl apply -f ingress.yaml
ingress.networking.k8s.io/multi-host-ingress created
root@master30 ~ 15:59:31# kubectl describe ingress multi-host-ingress
Name: multi-host-ingress
Namespace: ingress
Address: 10.1.8.40
Ingress Class: nginx
Rules:
Host Path Backends
---- ---- --------
webapp01.ningcode.cn
/ webapp01:80 (10.224.195.147:80,10.224.83.154:80)
webapp02.ningcode.cn
/ webapp02:80 (10.224.195.148:80,10.224.83.155:80)
Events:
Normal Sync 4s nginx-ingress-controller Scheduled for sync
Address: 10.1.8.40:Ingress 的入口就是 ingress-nginx 控制器 Service 的 LoadBalancer IP- 两个域名共用同一个 IP,靠 HTTP 的
Host头区分后端
10.3 验证后端轮询
# webapp01 的 ClusterIP:pod1/pod2 严格 30/30
root@master30 ~ 16:03:15# for i in {1..60}; do curl -s 10.111.64.166; done| sort | uniq -c
30 hello webapp01 pod1
30 hello webapp01 pod2
# webapp02 的 ClusterIP:pod1/pod2 严格 30/30
root@master30 ~ 16:03:31# for i in {1..60}; do curl -s 10.110.47.159; done| sort | uniq -c
30 hellow webapp02 pod1
30 hellow webapp02 pod2
提示:域名测试要在客户端改
/etc/hosts,否则webapp01.ningcode.cn会去公网解析(详见下一章)。
10.4 要点
- Ingress 按
Host精确匹配域名,再按path匹配路径;匹配不到走 Default backend(没有则 404) - 一个 Ingress 可以写多个
rules(多域名),也可以一个域名下写多个paths(多路径,见下一章) ingressClassName: nginx指定由哪个控制器处理(对应集群里的 IngressClass 资源)
十一、Ingress 多路径路由
11.1 一个域名按路径分流
# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: multi-path-ingress
spec:
ingressClassName: nginx
rules:
- host: www.ningcode.cn
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: webapp01
port:
number: 80
- path: /games
pathType: Prefix
backend:
service:
name: webapp02
port:
number: 80
root@master30 ~ 16:15:28# kubectl apply -f ingress.yaml
ingress.networking.k8s.io/multi-path-ingress created
root@master30 ~ 16:15:43# kubectl describe ingress multi-path-ingress
Name: multi-path-ingress
Namespace: ingress
Rules:
Host Path Backends
---- ---- --------
www.ningcode.cn
/ webapp01:80 (10.224.195.147:80,10.224.83.154:80)
/games webapp02:80 (10.224.195.148:80,10.224.83.155:80)
Events:
Normal Sync 16s nginx-ingress-controller Scheduled for sync
11.2 踩坑:域名解析到了公网
直接 curl,请求跑到了公网云服务器(www.ningcode.cn 解析到 47.99.246.250,返回宝塔默认页"没有找到站点"):
root@master30 ~ 16:17:47# curl www.ningcode.cn
<!doctype html>
<title>没有找到站点</title>
......(公网服务器返回的默认页面)
root@master30 ~ 16:17:57# #这里跑到公网去了,要配etc hosts文件,让优先级更高
11.3 修改 /etc/hosts 让本地优先
# /etc/hosts 追加一行(10.1.8.40 是 ingress-nginx 的 LoadBalancer IP)
10.1.8.40 www.ningcode.cn
改完后验证(第一次改完 IP 不对会 Failed to connect,vim 改对后正常):
root@master30 ~ 16:33:41# curl www.ningcode.cn
curl: (7) Failed to connect to www.ningcode.cn port 80 after 0 ms: Couldn't connect to server
root@master30 ~ 16:33:56# curl www.ningcode.cn
hello webapp01 pod1
root@master30 ~ 16:34:00# curl www.ningcode.cn
hello webapp01 pod2
......(pod1/pod2 交替返回)
root@master30 ~ 16:34:03# curl www.ningcode.cn/games/
hello webapp02 game1
/→ webapp01(pod1/pod2 轮询)/games/→ webapp02 的 games 子目录
11.4 要点
- 域名解析优先级:
/etc/hosts> DNS 服务器;实验环境记得加 hosts,否则流量不会进集群 pathType: Prefix按前缀匹配,/games匹配/games/、/games/xxx;末尾斜杠访问/games/更规范- 测试前先
kubectl describe ingress确认Address已分配、规则已 Sync
十二、Ingress 路径重写(rewrite-target)
12.1 需求
想让 /webapp01/xxx 转发给 webapp01,且后端收到的是裁剪后的路径 /xxx(等价 Nginx 的 proxy_pass /),而不是带着 /webapp01 前缀。需要两个注解:
nginx.ingress.kubernetes.io/use-regex: "true":启用正则路径nginx.ingress.kubernetes.io/rewrite-target: /$1:把正则捕获组$1作为转发路径
# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: multi-path-ingress
annotations:
nginx.ingress.kubernetes.io/use-regex: "true"
nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
ingressClassName: nginx
rules:
- host: www.ningcode.cn
http:
paths:
- path: /webapp01/(.*)
pathType: ImplementationSpecific
backend:
service:
name: webapp01
port:
number: 80
- path: /webapp02/(.*)
pathType: ImplementationSpecific
backend:
service:
name: webapp02
port:
number: 80
root@master30 ~ 16:40:45# kubectl apply -f ingress.yaml
ingress.networking.k8s.io/multi-path-ingress created
root@master30 ~ 16:41:00# kubectl describe ingress multi-path-ingress
Name: multi-path-ingress
Rules:
Host Path Backends
---- ---- --------
www.ningcode.cn
/webapp01/(.*) webapp01:80 (10.224.195.147:80,10.224.83.154:80)
/webapp02/(.*) webapp02:80 (10.224.195.148:80,10.224.83.155:80)
Annotations: nginx.ingress.kubernetes.io/rewrite-target: /$1
nginx.ingress.kubernetes.io/use-regex: true
12.2 验证
# 根路径没有规则了 → nginx 404(符合预期)
root@master30 ~ 16:41:08# curl www.ningcode.cn
<html><head><title>404 Not Found</title></head>......</html>
# 重写后正常访问(路径前缀被裁剪)
root@master30 ~ 16:42:07# curl www.ningcode.cn/webapp01/
hello webapp01 pod2
root@master30 ~ 16:42:15# curl www.ningcode.cn/webapp01/
hello webapp01 pod1
root@master30 ~ 16:42:18# curl www.ningcode.cn/webapp02/
hellow webapp02 pod2
root@master30 ~ 16:42:21# curl www.ningcode.cn/webapp02/
hellow webapp01 pod1
# 后端 httpd 没有 aaa 页面 → 后端 404
root@master30 ~ 16:42:22# curl www.ningcode.cn/webapp02/aaa
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<title>404 Not Found</title>
12.3 要点
rewrite-target: /$1+use-regex: true+pathType: ImplementationSpecific三件套缺一不可- 加了正则规则后,原
/规则若已删除,根路径会 404——这是预期行为,不是故障 - 区分两种 404:控制器 nginx 的 404(路径没匹配到规则)vs 后端应用的 404(规则命中了但后端没有该资源)
十三、Ingress TLS 与强制 HTTPS
13.1 生成自签证书
root@master30 ~ 16:45:03# openssl genrsa -out www.key 2048
root@master30 ~ 16:45:20# openssl req -new -key www.key -out www.csr -subj "/C=CN/ST=JS/L=NJ/O=LM/OU=DEVOPS/CN=www.laoma.cloud/emailAddress=webadmin@laoma.cloud"
root@master30 ~ 16:45:27# openssl x509 -req -days 3650 -in www.csr -signkey www.key -out www.crt
Certificate request self-signature ok
subject=C = CN, ST = JS, L = NJ, O = LM, OU = DEVOPS, CN = www.laoma.cloud, emailAddress = webadmin@laoma.cloud
13.2 创建 TLS Secret 并编写 Ingress
root@master30 ~ 16:45:32# kubectl create secret tls www-tls --cert=./www.crt --key=./www.key
secret/www-tls created
# ingress.yaml(tls-ingress)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: tls-ingress
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
spec:
ingressClassName: nginx
tls:
- hosts:
- www.laoma.cloud
secretName: www-tls
rules:
- host: www.ningcode.cn
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: webapp01
port:
number: 80
root@master30 ~ 16:46:12# kubectl apply -f ingress.yaml
ingress.networking.k8s.io/tls-ingress created
root@master30 ~ 16:46:32# kubectl describe ingress tls-ingress
Name: tls-ingress
TLS:
www-tls terminates www.laoma.cloud
Rules:
Host Path Backends
---- ---- --------
www.ningcode.cn
/ webapp01:80 (10.224.195.147:80,10.224.83.154:80)
Annotations: nginx.ingress.kubernetes.io/force-ssl-redirect: true
nginx.ingress.kubernetes.io/ssl-redirect: true
13.3 验证:HTTP 强制跳转 HTTPS
# 不带 -L:返回 308 重定向(强制 HTTPS 生效)
root@master30 ~ 16:47:25# curl -k http://www.ningcode.cn/
<html><head><title>308 Permanent Redirect</title></head>......</html>
# 带 -L:跟随跳转后正常访问
root@master30 ~ 16:46:42# curl -Lk http://www.ningcode.cn/
hello webapp01 pod1
root@master30 ~ 16:47:22# curl -Lk http://www.ningcode.cn/
hello webapp01 pod2
# 直接 https 访问(-k 忽略自签证书告警)
root@master30 ~ 16:47:32# curl -k https://www.ningcode.cn/
hello webapp01 pod2
root@master30 ~ 16:47:38# curl -k https://www.ningcode.cn/
hello webapp01 pod1
13.4 要点
- TLS 三步:openssl 自签 →
kubectl create secret tls→ Ingress 的spec.tls里挂 secretName ssl-redirect(默认开)与force-ssl-redirect都是把 HTTP 跳转到 HTTPS;返回码是 308- 自签证书访问用
-k忽略校验;生产环境用证书管理工具(cert-manager)签发受信任证书 - 注意本实验中证书 CN 是
www.laoma.cloud,而测试域名是www.ningcode.cn,-k下不受影响;浏览器访问会告警
十四、环境清理与恢复
实验收尾删除测试环境(webapp 应用在 ingress 命名空间,Ingress 控制器在 ingress-nginx 命名空间):
root@master30 ~ 16:48:43# #清理环境
root@master30 ~ 16:48:59# kubectl delete ns ingress-nginx
namespace "ingress-nginx" deleted
删除 ingress 命名空间时卡住了(里面有 webapp01/webapp02 等存量资源,且有 webhook 校验),被 ^C 中断:
root@master30 ~ 16:51:00# ^C
root@master30 ~ 16:51:04# kubectl get all
NAME READY STATUS RESTARTS AGE
pod/webapp01-587d58c655-4cvxf 1/1 Running 0 58m
pod/webapp02-56cfd678d6-cd4ss 1/1 Running 0 55m
service/webapp01 ClusterIP 10.111.64.166 <none> 80/TCP 56m
service/webapp02 ClusterIP 10.110.47.159 <none> 80/TCP 52m
......
14.1 清理的正确姿势(按实验意图补齐)
# 1. 先删 Ingress 规则(避免 webhook 校验拦截)
kubectl delete ingress --all -n ingress
# 2. 再删应用
kubectl delete deployment,service --all -n ingress
# 3. 最后删命名空间(此时应为空,秒删)
kubectl delete ns ingress
# 4. 确认
kubectl get ns
14.2 要点
- 命名空间处于 Terminating 状态卡住时,多半是里面还有资源或 finalizer 没清掉;先删资源再删命名空间
ingress-nginx命名空间删除后,之前创建的 Ingress 规则会全部失效(控制器没了)- MetalLB(metallb-system)保留未删:后续实验还要用 LoadBalancer 分配外部 IP
十五、常见错误与排查(重点)
15.1 命令语法类
| 报错 | 原因 | 修复 |
|---|---|---|
kubectl run test --rm --it ... → error: unknown flag: --it | 交互参数写错 | 用 -it |
apt install -y bind-dnsutils → E: Package has no installation candidate | Ubuntu 24.04 包名变化 | 安装 bind9-dnsutils |
kubectl get pods ... -i name → error: unknown shorthand flag: 'i' | 输出格式参数写错 | 用 -o name |
kubectl get all -n ingress-nginx -w → you may only specify a single resource type | -w 不支持 all 多类型 | 用 watch kubectl get all -n ingress-nginx |
grep image metallb-0.14.8/config/manifests/metallb-native → No such file or directory | 文件名少了 .yaml | 补全 metallb-native.yaml |
| `kubectl get configmaps web -o yaml | grep ^data -A4 data: …` → `bash: hello: command not found` | 输出内容被误粘贴进命令 |
kubectl exec ... bash -c"echo ..."(少空格) | -c 与脚本粘连 | 写成 bash -c "..." |
15.2 状态 / 资源类
| 报错 | 原因 | 修复 |
|---|---|---|
Error from server (NotFound): namespaces "prod" not found | ExternalName 写了不存在的命名空间 | 删掉 namespace 字段或用已存在的命名空间 |
deployments.apps "web" already exists / services "web" already exists | 同名资源已存在 | 先 kubectl delete 旧资源再创建 |
curl: (6) Could not resolve host: my-service.services.svc.cluster.local | 宿主机不走集群 DNS | 用 dig @10.96.0.10 验证,或进容器访问 |
curl: (7) Failed to connect ... Couldn't connect to server | /etc/hosts 里 IP 不对或服务未就绪 | vim 修改 /etc/hosts 后重试 |
error: Internal error occurred: ... container not found ("nginx") | Pod 还在创建,容器没起来 | 等 Pod Running 再 kubectl exec |
curl www.ningcode.cn 返回公网页面 | 域名解析到公网 | /etc/hosts 加 10.1.8.40 www.ningcode.cn |
nginx 的 404 Not Found(无标题) | Ingress 没有匹配的路径规则 | 检查 rules / 路径是否匹配(rewrite 后根路径无规则属预期) |
httpd 的 404 Not Found(有标题) | 规则命中但后端应用没有该资源 | 检查后端目录 / 文件是否存在 |
308 Permanent Redirect | 强制 HTTPS 生效 | curl -L 跟随跳转,或直接访问 https |
15.3 Ingress / 控制器专项
| 报错 | 原因 | 修复 |
|---|---|---|
Job.batch "ingress-nginx-admission-xxx" is invalid: ... field is immutable | 改镜像后直接 apply,想原地更新已创建的 Job | kubectl delete job -n ingress-nginx ingress-nginx-admission-create ingress-nginx-admission-patch 后重新 apply |
describe ingress 时 Address 为空 | 控制器还没同步完成 | 稍等再 describe;检查 IngressClass / 控制器是否 Running |
describe ingress mulit-path-ingress → NotFound | 拼写错误 | 核对名字(multi-path-ingress) |
kubectl edit configmap kube-proxy → Edit cancelled, no changes made. | 编辑器里没保存就退出 | 重新 edit 并 :wq 保存;改完重启 kube-proxy |
15.4 概念要点对照
| 对比项 | iptables 模式 | ipvs 模式 |
|---|---|---|
| 负载均衡方式 | random 概率(0.5+默认) | 内核 LVS 调度(默认 rr 严格轮询) |
| 查看规则 | iptables-save | ipvsadm -Lnt |
| 会话保持 | sessionAffinity: ClientIP | 调度算法 sh / ClientIP |
十六、命令速查表与小结
16.1 命令速查表
| 场景 | 命令 |
|---|---|
| 查看 Service 流量链路 | iptables-save |grep KUBE-SVC-xxx / ipvsadm -Lnt <ClusterIP>:<port> |
| 会话保持 | kubectl patch svc <name> -p '{"spec":{"sessionAffinity":"ClientIP"}}' |
| 创建 ConfigMap | kubectl create configmap <name> --from-file=./dir |
| 版本权重 | kubectl scale deployment <name> --replicas 8 + 压测 for i in {1..50}; do curl -s <IP>; done|sort|uniq -c |
| kube-proxy 切 IPVS | kubectl edit cm -n kube-system kube-proxy(mode: “ipvs”)→ kubectl rollout restart daemonset -n kube-system kube-proxy |
| 部署 MetalLB | apply metallb-native.yaml + IPAddressPool + L2Advertisement |
| 部署 Ingress-nginx | 改镜像后 kubectl apply -f .../deploy.yaml(Job 报错先 delete job) |
| 查看 Ingress 规则 | kubectl describe ingress <name> |
| 路径重写 | 注解 use-regex: "true" + rewrite-target: /$1,pathType 用 ImplementationSpecific |
| TLS | openssl genrsa/req/x509 → kubectl create secret tls → ingress spec.tls + force-ssl-redirect |
| 集群 DNS 验证 | dig @10.96.0.10 <name>.services.svc.cluster.local |
16.2 小结
- Service 的转发不是黑盒:iptables 模式是"随机概率"(两个后端约 50/50),ipvs 模式才是严格轮询(rr),排障时先分清当前模式
- 裸金属上 LoadBalancer 靠 MetalLB:IPAddressPool 给 VIP,L2Advertisement 用 ARP 宣告,删 Service 会释放 IP
- ExternalName 只动 DNS 不动数据面:适合把外部服务映射进集群,验证走
dig @10.96.0.10 - 副本数 = 流量权重:Service 按 Endpoint 转发,8:2 副本就是约 8:2 流量,是灰度发布的基础;压测前确认 Endpoints 就绪
- Ingress 是规则、控制器才是执行者:多域名靠 Host、多路径靠 path、裁剪路径靠 rewrite-target,全部收敛到控制器的一个 LoadBalancer IP 上
- 镜像替换要在 apply 之前完成,Job 的 template 不可变;
kubectl edit不保存等于没改——这两个坑本次都真实踩过
完。本手册基于
master30_2026-08-11_10_01_10.log整理,命令时间戳均为日志原始记录;省略了重复命令与长输出,报错、修复、验证结果与日志一致。
更多推荐
所有评论(0)