title: K8s 系列 | 第 5 天:Service 与网络基础:ClusterIP、NodePort、LoadBalancer 详解
tags:
– Kubernetes
– K8s系列
– DevOps
– Service
– 网络
– 容器编排
– ClusterIP
– NodePort
– LoadBalancer
K8s 系列 | 第 5 天:Service 与网络基础:ClusterIP、NodePort、LoadBalancer 详解
第 5 / 30 天
一、引言
在前四天的学习中,我们已经掌握了 K8s 的核心概念(第1天)、集群搭建(第2天)、Pod 生命周期管理(第3天)以及 Deployment 声明式部署(第4天)。我们能够运行 Pod 并通过 Deployment 管理副本,但这些 Pod 的 IP 地址是动态分配的——Pod 重启、滚动更新、节点故障都会导致 IP 变化。那么,如何让客户端稳定访问一组动态变化的 Pod?
答案是 Service。Service 是 Kubernetes 中网络抽象的核心层,它为 Pod 提供稳定的访问入口和负载均衡能力。今天我们将深入解析三种最常用的 Service 类型:ClusterIP、NodePort 和 LoadBalancer,并通过实战带你掌握 K8s 服务暴露的全貌。
二、核心概念:Service 的工作原理
2.1 为什么需要 Service?
Pod 是非永久性的资源,它们可以被创建、销毁、调度到不同节点。每个 Pod 获得一个集群内 IP(Cluster IP),但这个 IP 在 Pod 重建后会变化。Service 通过 Selector(标签选择器) 动态匹配一组 Pod,并提供一个固定的虚拟 IP(VIP) 和 DNS 名称,流量到达 VIP 后由 kube-proxy 转发到后端 Pod。
2.2 Service 的三大要素
| 要素 | 说明 |
|---|---|
| Cluster IP | Service 在集群内部的虚拟 IP,固定不变 |
| Port 映射 | port(Service 端口)→ targetPort(Pod 容器端口)→ nodePort(节点端口,仅 NodePort 类型) |
| Endpoints | 匹配到标签的 Pod IP:Port 列表,由 Endpoint Controller 自动维护 |
2.3 Service 流量转发机制
kube-proxy 支持三种模式(按演进顺序):
- userspace 模式(已弃用):用户态代理,性能差
- iptables 模式(默认):利用 Linux iptables NAT 规则,随机转发,无健康检查
- IPVS 模式(推荐生产):基于内核 IPVS,支持多种调度算法(rr、wrr、lc 等),性能更高
# 检查当前集群 kube-proxy 模式
kubectl get configmap kube-proxy -n kube-system -o yaml | grep mode
三、准备工作:部署一个示例应用
我们先部署一个 Nginx Deployment 作为测试目标:
# nginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-demo
labels:
app: nginx-demo
spec:
replicas: 3
selector:
matchLabels:
app: nginx-demo
template:
metadata:
labels:
app: nginx-demo
spec:
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
# 应用 Deployment
kubectl apply -f nginx-deployment.yaml
# 确认三个 Pod 都 Running
kubectl get pods -l app=nginx-demo -o wide
四、ClusterIP:集群内部访问(默认类型)
ClusterIP 是 Service 的默认类型,只能在集群内部访问。它分配一个虚拟 IP(Cluster IP),同集群内的 Pod 可以通过这个 IP 或 DNS 名称访问 Service。
4.1 创建 ClusterIP Service
# svc-clusterip.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-clusterip
spec:
selector:
app: nginx-demo
ports:
- protocol: TCP
port: 80 # Service 端口
targetPort: 80 # Pod 容器端口
type: ClusterIP
# 创建 Service
kubectl apply -f svc-clusterip.yaml
# 查看 Service 详情
kubectl get svc nginx-clusterip
# 查看自动生成的 Endpoints
kubectl get endpoints nginx-clusterip
4.2 验证 ClusterIP 可达性
# 在集群内任意 Pod 中测试 Service 可达性
kubectl run test-pod --image=busybox -it --rm --restart=Never -- wget -qO- http://nginx-clusterip:80
# 也可以使用 DNS 完整名称
kubectl run test-pod2 --image=busybox -it --rm --restart=Never -- wget -qO- http://nginx-clusterip.default.svc.cluster.local:80
4.3 关键特性
- Cluster IP 固定不变:只要 Service 存在,IP 就不会变化
- DNS 自动注册:格式为
<service-name>.<namespace>.svc.cluster.local - 内部负载均衡:kube-proxy 自动将流量分发到后端多个 Pod
五、NodePort:外部流量接入基础
NodePort 在 ClusterIP 基础上,在每个 Node 上开放一个静态端口(范围 30000-32767),外部可以通过 任意节点IP:NodePort 访问 Service。
5.1 创建 NodePort Service
# svc-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-nodeport
spec:
selector:
app: nginx-demo
ports:
- protocol: TCP
port: 80 # Service Cluster IP 端口
targetPort: 80 # Pod 容器端口
nodePort: 30080 # 节点端口(可选,不指定则随机分配)
type: NodePort
# 创建 NodePort Service
kubectl apply -f svc-nodeport.yaml
# 查看 NodePort 端口分配
kubectl get svc nginx-nodeport
# 查看节点 IP
kubectl get nodes -o wide
5.2 验证外部访问
# 从集群外部(例如宿主机或另一台机器)访问
# 假设节点 IP 为 192.168.1.100
curl http://192.168.1.100:30080
# 任何节点的同一端口都能访问(kube-proxy 负责转发)
curl http://192.168.1.101:30080
5.3 NodePort 的局限性
| 问题 | 说明 |
|---|---|
| 端口范围受限 | 仅 30000-32767,难以用于 80/443 标准端口 |
| 节点 IP 变化 | 节点宕机或新增时,外部需要感知变化 |
| 缺少健康检查 | 单点流量打到故障节点时才会转发到正常 Pod |
| 安全风险 | 每个 Node 都暴露端口,攻击面扩大 |
适用场景:开发测试环境、演示、小型服务暴露,或作为 LoadBalancer/Ingress 的底层实现。
六、LoadBalancer:云原生外部暴露
LoadBalancer 是 NodePort 的扩展,它在 NodePort 基础上调用云平台(AWS、GCP、Azure、阿里云等)的负载均衡器 API,自动创建一个外部 LB 并将流量导入集群。
6.1 创建 LoadBalancer Service
# svc-loadbalancer.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-lb
spec:
selector:
app: nginx-demo
ports:
- protocol: TCP
port: 80
targetPort: 80
type: LoadBalancer
# 创建 LoadBalancer Service
kubectl apply -f svc-loadbalancer.yaml
# 查看外部 IP(EXTERNAL-IP 列)
kubectl get svc nginx-lb
# 等待 EXTERNAL-IP 分配后
curl http://<EXTERNAL-IP>:80
6.2 本地环境(无云 LB)的解决方案:MetalLB
如果你在裸金属或本地集群(如 kubeadm、minikube、k3s)中,没有云平台的 LB 支持,可以使用 MetalLB 实现类似功能:
# 安装 MetalLB(Layer2 模式)
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.14.8/config/manifests/metallb-native.yaml
# 创建 IP 地址池(需与集群网络在同一子网)
cat <<EOF | kubectl apply -f -
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: first-pool
namespace: metallb-system
spec:
addresses:
- 192.168.1.200-192.168.1.220
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: l2-advertise
namespace: metallb-system
EOF
# 创建 LoadBalancer Service 后即可获得外部 IP
kubectl get svc nginx-lb
七、三种 Service 类型对比总结
| 特性 | ClusterIP | NodePort | LoadBalancer |
|---|---|---|---|
| 访问范围 | 集群内部 | 集群内外(节点IP:NPort) | 集群内外(LB IP) |
| 端口 | 自定义 | 30000-32767 | 自定义 |
| 负载均衡 | kube-proxy | kube-proxy | 云 LB + kube-proxy |
| 成本 | 免费 | 免费 | 云资源计费 |
| 适合场景 | 内部微服务通信 | 开发测试、调试 | 生产对外暴露 |
| 外部 IP | 无 | 节点 IP | 云 LB IP |
| 配置复杂度 | 最低 | 低 | 中(依赖云平台) |
关键理解:NodePort = ClusterIP + 节点端口映射,LoadBalancer = NodePort + 云 LB 集成。三者是层层叠加的关系。
八、生产实战:Service 配置要点
8.1 Session 保持(Session Affinity)
需要将同一个客户端的请求始终转发到同一个 Pod 时,配置 sessionAffinity:
apiVersion: v1
kind: Service
metadata:
name: nginx-sticky
spec:
selector:
app: nginx-demo
ports:
- port: 80
targetPort: 80
sessionAffinity: ClientIP
sessionAffinityConfig:
clientIP:
timeoutSeconds: 10800 # 会话保持时间,默认 10800s(3小时)
type: ClusterIP
8.2 自定义 Endpoints(外部服务接入)
如果后端服务不在 K8s 内(如外部数据库),可以手动管理 Endpoints:
# 创建不带 Selector 的 Service
apiVersion: v1
kind: Service
metadata:
name: external-db
spec:
ports:
- port: 3306
targetPort: 3306
---
# 手动指定 Endpoints
apiVersion: v1
kind: Endpoints
metadata:
name: external-db
subsets:
- addresses:
- ip: 192.168.100.50
ports:
- port: 3306
# 验证 Endpoints
kubectl get endpoints external-db
# 集群内 Pod 即可通过 svc 名称访问外部数据库
kubectl run test-db --image=mysql:8.0 -it --rm -- mysql -h external-db -u root -p
8.3 多端口 Service
当 Pod 暴露多个端口时,需要为每个端口命名:
apiVersion: v1
kind: Service
metadata:
name: multi-port-svc
spec:
selector:
app: my-app
ports:
- name: http
port: 80
targetPort: 8080
- name: metrics
port: 9090
targetPort: 9090
type: ClusterIP
8.4 ExternalName Service(DNS 别名转发)
将 Service 映射到集群外的 DNS 名称,无需 Cluster IP:
apiVersion: v1
kind: Service
metadata:
name: external-api
spec:
type: ExternalName
externalName: api.example.com
# 集群内通过 svc DNS 访问,实际解析为 api.example.com
kubectl run test --image=busybox -it --rm -- nslookup external-api
九、常见问题(FAQ)
Q1:为什么我访问 NodePort 时连接被拒绝?
排查步骤:
# 1. 确认 Service 已创建
kubectl get svc
# 2. 确认 Endpoints 不为空(有匹配的 Pod)
kubectl get endpoints nginx-nodeport
# 3. 检查防火墙是否放行了 nodePort 端口
iptables -L INPUT -n | grep 30080
# 4. 确认 kube-proxy 运行正常
kubectl get pod -n kube-system -l k8s-app=kube-proxy
Q2:LoadBalancer 的 EXTERNAL-IP 一直显示 <pending>?
- 在云平台:检查 LB 配额是否用尽、IAM 权限是否正确
- 在本地集群:确认 MetalLB(或其他 LB 方案)已正确安装且 IP 池配置无误
Q3:Service 的 Cluster IP 能固定吗?
可以,在 Service spec 中指定 clusterIP 字段(需在网络规划的范围内):
apiVersion: v1
kind: Service
metadata:
name: fixed-ip-svc
spec:
clusterIP: 10.96.0.100 # 需在 service-cluster-ip-range 范围内
selector:
app: my-app
ports:
- port: 80
Q4:如何调试 Service 流量转发?
# 查看 iptables 规则(kube-proxy iptables 模式)
iptables-save -t nat | grep nginx-clusterip
# 使用 tcpdump 抓包确认流量去向
kubectl exec -it <pod-name> -- tcpdump -i eth0 port 80
# 检查 kube-proxy 日志
kubectl logs -n kube-system -l k8s-app=kube-proxy --tail=50
十、总结
今天我们系统学习了 K8s Service 的三种核心类型:
- ClusterIP —— 集群内部微服务通信的标准方式,通过 DNS 名称实现服务发现
- NodePort —— 最简单的集群外部暴露方案,适合开发测试环境
- LoadBalancer —— 生产环境的对外暴露方案,云原生集成 LB
掌握 Service 是理解 K8s 网络的基石。实际工作中,ClusterIP + Ingress 的组合是最常见的生产环境架构模式(NodePort 通常不直接暴露给外部,而是作为 Ingress Controller 的底层支撑)。
📖 系列目录
- 第 1 天:Kubernetes 是什么?核心概念与架构全景解析
- 第 2 天:手把手搭建你的第一个 K8s 集群(kubeadm 实战)
- 第 3 天:Pod 详解:K8s 最小的调度单元与生命周期管理
- 第 4 天:Deployment 与 ReplicaSet:声明式应用管理
- 第 5 天:Service 与网络基础:ClusterIP、NodePort、LoadBalancer 详解 ⬅️
- 第 6 天:Namespace 与资源配额:多租户隔离基础(预告)
🔮 下期预告
第 6 天:Namespace 与资源配额:多租户隔离基础
当你的集群中运行着多个团队、多个项目的应用时,如何实现资源隔离、权限管理和配额限制?下期我们将深入 Namespace、ResourceQuota 与 LimitRange,掌握 K8s 多租户的基础设施。
本系列文章持续更新中,欢迎收藏关注!















暂无评论内容