6、Kubernetes的Servicekube‑proxy
一、VS Code 编写 K8s 资源文件
1.1 安装与扩展
- VS Code 分 User Installer / System Installer,功能无差别,仅安装位置、自动更新权限不同。
- 两个必备扩展:
- YAML by Red Hat:通用 YAML 支持,负责语法校验、Schema 校验、字段补全、悬浮文档、格式化、锚点跳转。
- Kubernetes by Microsoft:k8s 开发运维工作台,依赖 Red Hat YAML 扩展;提供 Cluster Explorer(上下文 / 命名空间 / 工作负载 / Service/Pod/Node 实时树视图)、apply/delete/describe/ 日志跟随 / 进 Pod shell / 端口转发、Helm 操作、CRD Schema 智能感知。
1.2 连接集群
- 下载
kubectl.exe放到$HOME\.kube\并加入 PATH。 - 将集群凭据
config放到$HOME\.kube\config,左侧 Cluster Explorer 即可看到集群。 - 编写资源时输入
p选择Kubernetes Pod自动生成模板;补全小技巧:关键字后加冒号:。
二、Service 是什么
2.1 背景问题(两个前置实验结论)
- Pod IP 仅集群内部可达,集群外部无法通过 PodIP 访问 Pod。
- 控制器动态创建 / 销毁 Pod,Pod IP 不断变化;
hostPort占用节点端口,同一节点无法跑多个同端口 Pod。 - 前端应用无法跟踪后端 Pod 的 IP 变化,也无法做负载均衡。
2.2 Service 概念
- Service 把一组 Pod 的网络应用统一暴露成网络服务;拥有固定不变的 ClusterIP 和端口,为后端 Pod 提供负载均衡。
- Service 通过
selector标签选择后端 Pod,自动维护 Service ↔ Pod(Endpoints)映射关系;后端 Pod 增删变化对客户端透明。 - 关键目标:无需改造老应用即可接入服务发现。
三、Service 基本管理
3.1 命令创建
# 创建 Deployment(3 副本 httpd) kubectl create deployment web --image=hub.laoma.cloud/library/httpd:2.4.58 --replicas=3 # 创建 ClusterIP 类型 Service:访问 ClusterIP 的 8080 转发到 Pod 的 80 kubectl create service clusterip web --tcp=8080:80 kubectl get svc kubectl describe service web # 查看 Selector、Endpoints、Session Affinity kubectl describe svc web | grep -e Endpoints -e IP: curl 查看到的IP地址:8080 # 访问测试--tcp=8080:80:Service 端口 8080 → 后端 Pod 端口 80。- Service 默认标签
app=<svc-name>;Endpoints字段列出当前后端 PodIP:Port 列表。
3.2 负载均衡验证
# 把每个 Pod 主页改成自己的 Pod 名 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 # 连续访问 100 次,统计各 Pod 命中次数(轮询负载均衡) for i in {1..100}; do curl -s 自己的IP地址:8080; done | sort | uniq -c- 新创建标签匹配
app=web的 Pod 会自动被 Service 纳入后端;rollout restart重建 Pod 后 Service 仍能动态发现新 Pod。 - Deployment 的 selector 与 Service 的 selector 可以是不同标签(如 deploy 匹配
app1=web1,svc 匹配app2=web2)。
3.3 expose 与 yaml 创建
# expose 方式:--port=Service端口 --target-port=Pod端口 --selector=标签 kubectl expose deployment web --port=8080 --target-port=80 --selector=app2=web2 # 导出 yaml 模板 kubectl create service clusterip web --tcp=8080:80 -o yaml --dry-run=client > svc-web.yml kubectl apply -f svc-web.yml注意:Service IP 只做 TCP/UDP 端口转发,ping 不通 ClusterIP,但可以 ping 通 PodIP。
Service 核心 yaml 字段
apiVersion: v1 kind: Service metadata: name: web labels: app: web spec: type: ClusterIP selector: app: web ports: - name: 8080-80 port: 8080 # ClusterIP 监听端口 protocol: TCP targetPort: 80 # 后端 Pod 端口四、Service 发现(集群内三种访问方式)
4.1 通过 IP 访问
实验:mysql Pod + ClusterIP Service,WordPress 通过 ServiceIP 连接数据库。
# mysql Pod + Service kubectl run mysql --image=hub.laoma.cloud/library/mysql --image-pull-policy=IfNotPresent \ --env=MYSQL_ROOT_PASSWORD=redhat --env=MYSQL_USER=tom \ --env=MYSQL_PASSWORD=redhat --env=MYSQL_DATABASE=blog \ --dry-run=client -o yaml > pod-mysql.yaml kubectl apply -f pod-mysql.yaml kubectl expose pod mysql --port=3306 --target-port=3306 # WordPress 直接用 ServiceIP 作为数据库地址 kubectl run wordpress --image=hub.laoma.cloud/library/wordpress --image-pull-policy=IfNotPresent \ --env=WORDPRESS_DB_USER=tom --env=WORDPRESS_DB_PASSWORD=redhat \ --env=WORDPRESS_DB_NAME=blog --env=WORDPRESS_DB_HOST=10.111.69.45 kubectl expose pod wordpress --port=80 --target-port=80 --type NodePort4.2 通过环境变量访问
- Pod 启动时,kubelet 把已存在的 Service 注入环境变量:
<SVC>_SERVICE_HOST、<SVC>_SERVICE_PORT<SVC>_PORT_<PORT>_TCP_ADDR、<SVC>_PORT_<PORT>_TCP_PORT、<SVC>_PORT等。
- 限制:Service 必须先于 Pod 创建,后创建的 Service 不会回填到已有 Pod;Pod 只能访问同一命名空间的 Service。
# 进入临时 Pod 查看注入变量 kubectl run test --rm -it --image=busybox sh env | grep MYSQL # MYSQL_SERVICE_HOST=10.111.69.45 MYSQL_SERVICE_PORT=3306 # WordPress 用环境变量引用 --env=WORDPRESS_DB_HOST='$(MYSQL_SERVICE_HOST)'4.3 通过 DNS 名称访问(推荐)
- kubeadm 默认安装CoreDNS(Service 名
kube-dns,ClusterIP10.96.0.10,端口 53);新 Service 创建后 CoreDNS 自动添加 DNS 记录。 - Pod 的
/etc/resolv.conf:nameserver 10.96.0.10,搜索域svc.cluster.local等。 - 访问格式:
- 同命名空间:
<service>或<service>.<namespace> - 跨命名空间:
<service>.<namespace>.svc.cluster.local
- 同命名空间:
kubectl run busybox --rm -it --image=busybox /bin/sh wget wordpress:80 # 同 ns,省略命名空间 wget wordpress.services:80 # 显式指定 ns wget wordpress.services.svc.cluster.local:80 # WordPress 直接用 Service 名连库 --env=WORDPRESS_DB_HOST=mysql五、Service 类型(4 种 + Headless)
表格
| 类型 | 作用范围 | 说明 |
|---|---|---|
| ClusterIP(默认) | 仅集群内部 | 从预留 IP 池分配内部 ClusterIP;其他类型都基于它构建;可在spec.clusterIP指定合法 IP,置"None"即无头服务 |
| NodePort | 集群所有节点 | 在每个节点上开放同一端口(默认 30000‑32767),外部通过节点IP:NodePort访问;内部仍先经过 ClusterIP |
| LoadBalancer | 集群外部 | 需要云厂商 LB 或第三方 MetalLB 提供独立外部 IP;K8s 本身不实现 LB |
| ExternalName | 集群内部 | 把 Service 映射成外部域名的 CNAME 记录,不创建代理、不分配 IP、不做负载均衡 |
| Headless | 集群内部 | clusterIP: None,不分配 ClusterIP,kube‑proxy 不处理;DNS 直接返回后端 Pod 的 A/AAAA 记录 |
5.1 ClusterIP
默认类型;可手动指定spec.clusterIP,必须落在service-cluster-ip-rangeCIDR 内,非法值 API 返回 422。
5.2 NodePort
kubectl expose deployment web --type NodePort --port=8080 --target-port=80 \ -o yaml --dry-run=client > service-NodePort.yaml kubectl apply -f service-NodePort.yaml kubectl get service # 8080:31917/TCP # 访问任一节点 curl http://10.1.8.30:31917 curl http://10.1.8.31:31917 curl http://10.1.8.32:31917三个端口概念:
nodePort:节点上监听的端口(30000‑32767,可手动指定固定端口如30080);port:ClusterIP 上监听端口;targetPort:Pod 监听端口。
iptables 规则验证(每个节点额外增加 KUBE‑NODEPORTS 链规则):
iptables-save | grep 31917 # -A KUBE-NODEPORTS --dport 31917 -j KUBE-SVC-WE5D4GWSX3MMPCHH iptables-save | grep KUBE-SVC-WE5D4GWSX3MMPCHH # 规则链按概率分流到各 KUBE-SEP-xxx 后端端点链5.3 LoadBalancer(MetalLB 实现,裸金属集群)
K8s 自身不实现 LoadBalancer,裸金属环境用MetalLB提供外部 IP。
部署步骤
# 下载并修改镜像仓库地址 wget https://laoma.cloud/.../metallb-0.14.8.tar.gz tar -xf metallb-0.14.8.tar.gz sed -i 's/quay.io/hub.laoma.cloud/g' metallb-0.14.8/config/manifests/metallb-native.yaml kubectl apply -f metallb-0.14.8/config/manifests/metallb-native.yaml kubectl get all -n metallb-system # controller 1 副本 + speaker DaemonSet 每节点一个配置地址池 + L2 宣告
# 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 --- # L2.yaml apiVersion: metallb.io/v1beta1 kind: L2Advertisement metadata: name: example namespace: metallb-systemkubectl apply -f ippool.yaml kubectl apply -f L2.yaml创建 LoadBalancer Service 并测试
kubectl expose deployment web --type LoadBalancer --port=80 --target-port=80 \ -o yaml --dry-run=client > service-LoadBalancer.yaml kubectl apply -f service-LoadBalancer.yaml kubectl get service # web LoadBalancer 10.109.101.67 10.1.8.40 80:32101/TCP curl http://10.1.8.40:80 # 注意:用 Service 端口 80,不是 NodePort 32101流量路径客户端 → MetalLB VIP(L2 模式 ARP 宣告,流量进入集群任意节点)→ 节点内核拦截 Service IP → kube‑proxy 匹配 iptables/IPVS 规则 → Service 选择健康 Pod → CNI 跨节点转发 → Pod 网络命名空间 → Pod 响应原路返回。
MetalLB 只负责 ARP 宣告 + 占用 IP,转发仍由 kube‑proxy + Service 完成。
5.4 ExternalName
apiVersion: v1 kind: Service metadata: name: www spec: type: ExternalName externalName: www.laoma.cloud查询www.<ns>.svc.cluster.local时 DNS 返回 CNAME 记录www.laoma.cloud;重定向发生在 DNS 层,不经过代理转发。
5.5 Headless Service(clusterIP: None)
- 不分配 ClusterIP,kube‑proxy 不处理,平台不提供负载均衡。
- 带 selector:控制平面创建 EndpointSlice,DNS 直接返回后端 Pod 的 A/AAAA 记录(用于 StatefulSet、自定义负载均衡、直连 Pod)。
- 不带 selector:不创建 EndpointSlice;ExternalName 走 CNAME,其他类型对就绪端点配置 A/AAAA 记录;此时
port必须与targetPort匹配。
六、Service 会话保持(Session Affinity)
6.1 原理
spec.sessionAffinity:None(默认)/ClientIP(基于客户端 IP 亲和)。sessionAffinityConfig.clientIP.timeoutSeconds:粘性时长,默认10800 秒(3 小时)。- 首次访问记录「客户端 IP ↔ Pod」映射,后续请求转发同一 Pod;适合有状态服务:Java Web 登录态、购物车、WebSocket、游戏长连接。
6.2 实验
kubectl create deployment web --image=hub.laoma.cloud/library/httpd --replicas=2 kubectl expose deployment web --port 80 # 改各 Pod 主页为 Pod 名 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 # 默认无亲和:20 次请求两个 Pod 各 10 次 for i in {1..20}; do curl -s 10.111.90.183; done | sort | uniq -c # 开启 ClientIP 亲和 kubectl patch svc web -p '{"spec":{"sessionAffinity":"ClientIP"}}' # 同一客户端 20 次请求全部落到同一个 Pod for i in {1..20}; do curl -s 10.111.90.183; done | sort | uniq -c七、金丝雀发布(Canary)
7.1 思路
同时保留旧版本(stable)与新版本(canary)两套 Deployment,共用同一个 Service,用track标签区分;通过调整两套副本比例控制新版本流量占比,验证无误后逐步全量。
7.2 实验步骤
# 1. 准备两个版本页面 ConfigMap mkdir web echo hello nginx 1.28 > web/index28.html echo hello nginx 1.29 > web/index29.html kubectl create configmap web --from-file=./web旧版本 Deployment(track: stable,10 副本)
apiVersion: apps/v1 kind: Deployment metadata: labels: {app: web} name: web-28 spec: replicas: 10 selector: matchLabels: {app: web, tier: frontend, track: stable} template: metadata: labels: {app: web, tier: frontend, track: stable} spec: containers: - image: hub.laoma.cloud/library/nginx:1.28 name: nginx imagePullPolicy: IfNotPresent ports: - containerPort: 80 volumeMounts: - name: webcontent mountPath: "/usr/share/nginx/html" volumes: - name: webcontent configMap: name: web items: - key: index28.html path: index.htmlService(selector 只匹配 app + tier,不区分 track)
apiVersion: v1 kind: Service metadata: labels: {app: web} name: web spec: ports: - port: 80 protocol: TCP targetPort: 80 selector: app: web tier: frontend新版本 Deployment(track: canary,1 副本):镜像nginx:1.29,ConfigMap 取index29.html,标签track: canary。
7.3 流量比例验证
kubectl apply -f webapp-1.28.yaml kubectl apply -f webapp-svc.yaml kubectl apply -f webapp-1.29.yaml # 8 旧 + 2 新 → 约 80% / 20% 流量 kubectl scale deployment web-28 --replicas 8 kubectl scale deployment web-29 --replicas 2 for i in {1..50}; do curl -s 10.98.235.36; done | sort -n | uniq -c # 39 hello nginx 1.28 / 11 hello nginx 1.29 # 逐步扩大新版本:6 旧 + 4 新 kubectl scale deployment web-28 --replicas 6 kubectl scale deployment web-29 --replicas 4八、kube‑proxy
8.1 作用
kube‑proxy 是节点上运行的核心网络组件(DaemonSet),监听 Service 与 Endpoint 变化,负责Service → 后端 Pod 的流量转发与负载均衡。
8.2 三种工作模式对比
表格
| 模式 | 地位 | 性能 | 适用场景 | 特点 |
|---|---|---|---|---|
| iptables | 默认模式 | 中(服务数 < 1000) | 小规模、环境稳定 | 依赖内核 netfilter,规则链随服务 / 端点增多膨胀,匹配逐条遍历,规则多时 CPU 高、延迟大 |
| IPVS | 生产推荐 | 极高(十万级服务) | 中大规模生产环境 | 基于内核 IPVS + 哈希表,O (1) 查找,绕过复杂规则链,支持多种调度算法 |
| Userspace | 已废弃 | 低 | 仅旧版兼容 | 全用户态转发,性能最差;K8s 1.25+ 已移除 |
生产建议:IPVS 模式 + Calico 网络插件。
8.3 查看与切换模式
# 查看 ConfigMap 中的 mode(空字符串即默认 iptables) kubectl get cm -n kube-system kube-proxy -o yaml | grep mode # 查看当前运行模式(日志中 "Using iptables proxy" / "Using ipvs Proxier") kubectl get pods -n kube-system -l k8s-app=kube-proxy -o name kubectl logs -n kube-system kube-proxy-9xfwh | grep Using # 切换为 IPVS:编辑 ConfigMap,mode: "ipvs" kubectl edit configmap -n kube-system kube-proxy # 重启 DaemonSet 生效 kubectl rollout restart daemonset -n kube-system kube-proxy # 再次验证日志 kubectl logs -n kube-system <new-pod> | grep Using8.4 IPVS 模式
工作原理:kube‑proxy 在宿主机内核创建 IPVS 虚拟服务器,把后端 Pod 注册为 Real Server;规则存于哈希表(O (1)),流量到达后按调度算法直接转发到 Pod,绕过 iptables 规则链。
支持的调度算法
表格
| 算法 | 含义 | 适用场景 |
|---|---|---|
rr | 轮询 | 节点配置一致的简单均衡 |
wrr | 加权轮询 | 最推荐,通用 Web/API/ 网关;权重正比于 Podresources.requests.cpu/memory |
lc | 最少连接 | 长连接、WebSocket、游戏服务 |
dh | 目标地址哈希 | 会话保持 |
sh | 源地址哈希 | 配合sessionAffinity: ClientIP |
sed | 最短预期延迟 | ‑ |
nq | 最少队列 | ‑ |
实验验证
# 1. 创建 3 副本 nginx Deployment,主页写 Pod 名,创建 Service kubectl create deployment web --image=nginx --replicas=3 for pod in $(kubectl get pods -o custom-columns=NAME:.metadata.name --no-headers); do kubectl exec $pod -- bash -c "echo $pod > /usr/share/nginx/html/index.html" done kubectl expose deployment web --port=80 # 2. 查看 IPVS 虚拟服务器与后端 Real Server ipvsadm -Lnt 10.103.143.120:80 # TCP 10.103.143.120:80 rr # -> 10.224.19.38:80 Masq 1 ... # -> 10.224.113.164:80 Masq 1 ... # -> 10.224.113.165:80 Masq 1 ... # 3. 验证轮询:60 次访问各 Pod 命中 20 次 for i in {1..60}; do curl -s 10.103.143.120; done | sort | uniq -c切换调度算法
kubectl edit configmap kube-proxy -n kube-system # ipvs: # scheduler: "wrr" # mode: "ipvs" kubectl rollout restart ds kube-proxy -n kube-system生产最佳配置(KubeProxyConfiguration 节选)
mode: ipvs ipvs: scheduler: "rr" # rr / wrr / lc 三选一 tcpTimeout: 900 tcpFinTimeout: 120 udpTimeout: 300 excludeCIDRs: - "127.0.0.1/32" strictARP: true oomScoreAdj: -999 iptables: minSyncPeriod: 5s syncPeriod: 30s logging: format: text verbosity: 2会话保持优先级
- Service 配置
sessionAffinity: ClientIP→ kube‑proxy 自动使用 IPVS 的SH(源地址哈希),保证同一 IP → 同一 Pod,优先级最高。 - Service 不配置亲和性 → 使用 IPVS
scheduler中设置的算法(rr /lc/wrr)。
8.5 iptables 模式
工作原理:kube‑proxy 监听 Service/Endpoint 变化,在宿主机内核动态生成 iptables 规则链;流量经 netfilter 逐条匹配规则,完成 DNAT 与负载均衡。
规则链分层(全链路)
KUBE-SERVICES:服务总入口,匹配目的 ClusterIP + 端口,跳转到该 Service 专属调度链。KUBE-SVC-XXX:Service 专属调度链;非 Pod 网段流量先做KUBE-MARK-MASQSNAT 标记;再用statistic --mode random按概率分流到各端点链。KUBE-SEP-XXX:后端端点链,执行DNAT --to-destination <PodIP:Port>。
实验验证
# 保存规则并按 ServiceIP 追踪链路 iptables-save > iptables.list grep 10.103.143.120 iptables.list grep KUBE-SVC-7D76YWGERGEPC4GC iptables.list grep -e KUBE-SEP-HYZM2VM7RCC7M2HX -e KUBE-SEP-CHPZVFCOFL4YLCYM -e KUBE-SEP-HM7RLUR2RXSQ3D6F iptables.list | grep DNAT3 个后端 Pod 时概率分配为 1/3、1/2(剩余)、无条件兜底,合计各 1/3。
缺点:每新增 Service/Endpoint 都要增改规则;服务超过 1000 后规则链膨胀,CPU 占用飙升、延迟显著增加。
8.6 Userspace 模式
kube‑proxy 在用户态监听 Service 端口,接收流量后本地代理转发到后端 Pod;全用户态处理,性能极低;K8s 1.25+ 已彻底移除。
九、环境清理
kubectl delete ns services