K8s Service 核心实战:从访问方式到代理模式全解析
一、Service 核心概念
在 K8s 集群中,Pod 具有"用后即焚"的特性,重建后 IP 地址会动态变化,直接访问 Pod 会导致服务不可用。 Service 正是为解决这一问题而生:它作为 Pod 的固定代理入口,通过 Label 关联 Pod,无论 Pod 如何重建、IP 如何变化,都能通过 Service 稳定访问 Pod 提供的服务。
Service 工作原理
Service 的本质是一条代理规则 ,真正实现流量转发的是集群中的 kube-proxy 组件:
- 创建 Service 时,会向
kube-proxy下发代理规则; kube-proxy根据规则将请求转发到后端 Pod;- 核心依赖两种代理模式:
iptables和ipvs。
二、Service 代理模式
1. iptables 模式
- 实现方式 :基于内核模块
netfilter实现流量转发,内核层转发效率较高; - 缺点:负载均衡策略单一(仅随机转发),无 Pod 健康检查能力,后端 Pod 不可用时无法自动剔除。
2. ipvs 模式
ipvs(Virtual Server)同样基于 netfilter,但功能更强大:
- 优势:支持丰富的负载均衡算法(轮询、加权轮询、最小连接数、最少负载等),自带 Pod 健康检查、连接重试;
- 前提:集群节点需安装 ipvs 内核模块,否则会自动降级为 iptables 模式。
实操:验证并开启 ipvs 模式
bash
# 1. 检查 ipvs 内核模块是否加载
lsmod | grep -e ip_vs -e nf_conntrack_ipv4
# 正常输出示例:
# ip_vs_sh
# ip_vs_wrr
# ip_vs_rr
# ip_vs
# nf_conntrack
# 2. 验证 ipvs 功能(需安装 ipvsadm)
ipvsadm -Ln
# 3. 修改 kube-proxy 配置,开启 ipvs 模式(kubeadm 部署需手动配置)
kubectl edit cm kube-proxy -n kube-system
# 找到 mode 字段,修改为:
mode: "ipvs"
# 4. 删除现有 kube-proxy Pod(会自动重建并加载新配置)
kubectl get pod --show-labels -n kube-system | grep kube-proxy
kubectl delete pod -l k8s-app=kube-proxy -n kube-system
# 5. 验证 Pod 重建完成
kubectl get po -n kube-system | grep kube-proxy
三、Service 常用访问方式
K8s 提供 3 种核心 Service 类型,适配不同访问场景:
| 类型 | 特点 | 适用场景 |
|---|---|---|
| ClusterIP | 仅分配集群内部虚拟 IP,外部无法访问 | 集群内 Pod 间通信 |
| NodePort | 节点暴露 30000-32767 端口,外部可访问 | 测试/小流量外部访问 |
| LoadBalancer | 对接公有云负载均衡(阿里云/AWS 等) | 生产环境外部大规模访问 |
四、实战案例
案例 1:ClusterIP 类型(集群内访问)
创建 3 个 Nginx Pod,通过 ClusterIP 类型 Service 代理,实现集群内访问。
资源清单(hpa-nginx.yml)
yaml
apiVersion: v1
kind: Service
metadata:
name: svc-nginx
namespace: test
spec:
type: ClusterIP # 默认类型,可省略
ports: # 端口映射
- port: 80 # Service 暴露的内部端口
targetPort: 80 # Pod 内容器的端口
selector: # 关联 Label 为 app=deploy-nginx 的 Pod
app: deploy-nginx
---
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: hpa-nginx
namespace: test
spec:
minReplicas: 3
maxReplicas: 10
targetCPUUtilizationPercentage: 6
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: deploy-nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-nginx
namespace: test
spec:
replicas: 3
selector:
matchLabels:
app: deploy-nginx
template:
metadata:
labels:
app: deploy-nginx # 与 Service selector 标签一致
spec:
containers:
- name: nginx
image: nginx:1.17.0
ports:
- containerPort: 80
protocol: TCP
执行命令
bash
# 1. 创建资源
kubectl create -f hpa-nginx.yml
# 2. 查看 Pod 信息
kubectl get pod -n test -o wide
# 3. 查看 Service 信息(获取 ClusterIP)
kubectl get svc -n test
# 输出示例:
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
# svc-nginx ClusterIP 10.110.174.205 <none> 80/TCP 10s
# 4. 验证集群内访问(修改 Pod 页面区分不同实例)
kubectl exec -it <pod-name> -n test -- /bin/bash
# 在容器内修改 index.html,写入 Pod IP
echo $(hostname -i) > /usr/share/nginx/html/index.html
# 5. 循环访问 Service,验证负载均衡
while :
do
curl <ClusterIP> # 替换为实际的 Service ClusterIP
sleep 2
done
总结:ClusterIP 仅能在集群内部节点/Pod 访问,外部主机无法访问。
案例 2:NodePort 类型(外部访问)
修改上述清单,将 Service 改为 NodePort 类型,实现外部主机访问集群内 Pod。
资源清单(hpa-nginx.yml)
yaml
apiVersion: v1
kind: Service
metadata:
name: svc-nginx
namespace: test
spec:
type: NodePort # 改为 NodePort 类型
ports:
- port: 80 # Service 内部端口
targetPort: 80 # Pod 容器端口
nodePort: 30007 # 自定义节点暴露端口(可选,范围 30000-32767)
selector: # 关联 Pod 标签
app: deploy-nginx
---
# 以下 HPA、Deployment 部分与案例 1 一致,省略(直接复用即可)
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: hpa-nginx
namespace: test
spec:
minReplicas: 3
maxReplicas: 10
targetCPUUtilizationPercentage: 6
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: deploy-nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-nginx
namespace: test
spec:
replicas: 3
selector:
matchLabels:
app: deploy-nginx
template:
metadata:
labels:
app: deploy-nginx
spec:
containers:
- name: nginx
image: nginx:1.17.0
ports:
- containerPort: 80
protocol: TCP
执行命令
bash
# 1. 创建/更新资源
kubectl create -f hpa-nginx.yml
# 若已创建,用更新命令:kubectl apply -f hpa-nginx.yml
# 2. 查看 Service 信息
kubectl get svc -n test
# 输出示例:
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
# svc-nginx NodePort 10.110.174.205 <none> 80:32388/TCP 13s
# 说明:80(内部端口)、32388(节点暴露的 NodePort 端口)
# 3. 外部主机访问测试
# 访问地址:http://<集群节点IP>:<NodePort端口>
# 示例:http://192.168.0.13:32388
提示 :若自定义
nodePort,需确保端口在 30000-32767 范围内,且未被节点占用;若不指定,K8s 会随机分配端口。