K8s学习笔记:03-Service网络

对应章节:第三章 Service 网络 / 3.1~3.7
日期:2026年7月13日


Pod 的网络困境与 Service 的诞生

问题:Pod 的 IP 是”临时”的

第二章你见过:删一个 Pod,Deployment 立刻补一个新的。但新 Pod 的 IP 和旧的不一样

1
2
3
kubectl get pods -o wide      # 记下某个 Pod 的 IP,比如 10.244.0.5
kubectl delete pod <name> # 删掉它
kubectl get pods -o wide # 新 Pod 起来了,IP 变成 10.244.0.8

扩缩容更是如此:3 个 Pod 有 3 个不同 IP,缩到 1 个又只剩 1 个。Pod IP 是牛,不是宠物——随时会变。

真实困境

场景 直接用 Pod IP 的问题
前端要连后端 后端 Pod 一重建,IP 变了,前端连不上
3 个后端副本 前端该连哪个 IP?怎么负载均衡?
扩缩容 Pod 数量一变,IP 列表全乱

Service 出场

Service 给一组 Pod 提供一个固定不变的虚拟 IP(ClusterIP)和 DNS 名字,并自动把流量负载均衡到后面的 Pod。

1
2
3
4
5
6
7
8
9
10
11
   ┌──────── Service ────────┐
│ 固定 IP: 10.96.0.50 │ ← 永不改变的入口
│ DNS: my-svc
└────────────┬────────────┘
│ 自动负载均衡
┌────────────┼────────────┐
│ │ │
┌──Pod──┐ ┌──Pod──┐ ┌──Pod──┐
10.244 │ │10.244 │ │10.244 │ ← IP 随便变
│ .0.5 │ │ .0.6 │ │ .0.7
└───────┘ └───────┘ └───────┘

类比 Docker

Docker K8s
docker run -p 8080:80(端口映射) Service(NodePort)
Nginx 反向代理 + upstream 列表 Service 自动维护后端 Pod 列表

关键认知:你访问 Service,Service 帮你转发到某个健康的 Pod。Pod 怎么变,Service 的 IP 和名字都不变。


Service 靠 label 关联 Pod(核心机制)

Service 不关心 Pod 的 IP,它靠 label selector(标签选择器) 动态找 Pod。

1
2
3
4
5
6
Service (selector: app=nginx)
"谁贴了 app=nginx 标签,流量就转给谁"

Pod (labels: app=nginx) ← 匹配 ✔
Pod (labels: app=nginx) ← 匹配 ✔
Pod (labels: app=redis) ← 不匹配 ✗
  • Pod 有 labels(标签,键值对,如 app=nginx
  • Service 有 selector(选择器,如 app=nginx
  • selector 匹配上哪些 Pod,流量就转发给哪些——动态的,新起的 Pod 只要标签对,自动加入

背后有个对象叫 Endpoints(或 EndpointSlice),实时记录”当前匹配到的 Pod IP 列表”:

1
2
kubectl get endpoints <service-name>
# 显示当前 Service 后面挂了哪些 Pod IP,Pod 增减这个列表自动更新

这就是为什么 Pod IP 变了不要紧——Endpoints 会自动跟着更新,Service 的入口始终不变。


三种 Service 类型

类型 作用范围 谁能访问 典型用途
ClusterIP(默认) 集群内部 只有集群内的其他 Pod 后端服务、数据库,内部互调
NodePort 集群外 通过 节点IP:端口 从外部访问 开发测试、临时对外暴露
LoadBalancer 集群外 云厂商给一个公网 IP 生产环境对外服务(需云平台/MetalLB)

层层递进的关系:

1
2
3
4
5
LoadBalancer   (在 NodePort 基础上,加一个云负载均衡器 + 公网IP

NodePort (在 ClusterIP 基础上,每个节点开一个端口对外)

ClusterIP (最基础:一个集群内部虚拟 IP

记忆法:ClusterIP 对内,NodePort 对外(土办法),LoadBalancer 对外(云上正规办法)。


ClusterIP:集群内部稳定入口(默认类型)

创建

先确保有一个 Deployment(第二章那个),然后 expose 出一个 Service:

1
2
3
4
5
6
7
8
9
# 用 expose 给 Deployment 建一个 ClusterIP Service
kubectl expose deploy my-nginx-deploy --name=my-nginx-svc --port=80 --target-port=80

# 查看
kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 23h
my-nginx-svc ClusterIP 10.96.200.94 <none> 80/TCP 39s
## kubernetes 是系统自己创建的,详情可见第三章的踩坑与知识扩展
  • --port=80:Service 自己监听的端口
  • --target-port=80:转发到 Pod 容器的端口

验证 selector 关联

1
2
kubectl get svc my-nginx-svc -o wide      # 看 SELECTOR 列
kubectl get endpoints my-nginx-svc # 看后面挂了哪几个 Pod IP

对比 kubectl get pods -o wide 的 IP,Endpoints 里应该正好是这些 Pod 的 IP。

集群内访问测试

ClusterIP 只能在集群内部访问。起一个临时 Pod 从内部 curl:

1
2
3
4
5
kubectl run test --rm -it --image=busybox:1.36 --restart=Never -- sh
# 进去后:
wget -qO- my-nginx-svc # 用 DNS 名字访问,返回 nginx 欢迎页
wget -qO- 10.96.x.x # 用 ClusterIP 访问,效果相同
exit

试着多 wget 几次,如果后端有多个 Pod,请求会被分发到不同 Pod(负载均衡)。


NodePort:把服务暴露到集群外

ClusterIP 集群外访问不了。要从虚拟机外部(物理机浏览器)访问,用 NodePort。

1
2
3
4
5
6
7
8
# 删掉原来的 ClusterIP,改建 NodePort(或直接建新的)
kubectl delete svc my-nginx-svc
kubectl expose deploy my-nginx-deploy --name=my-nginx-np --type=NodePort --port=80 --target-port=80

kubectl get svc my-nginx-np
# NAME TYPE CLUSTER-IP PORT(S) AGE
# my-nginx-np NodePort 10.96.x.x 80:31234/TCP 5s
# ^^^^^ 这个就是节点对外开的端口

80:31234 的含义:

1
2
集群外 → 节点IP:31234 → Service:80 → Pod:80
(NodePort) (ClusterIP) (目标端口)

NodePort 端口范围默认 30000-32767,不指定会随机分配。

Kind 环境的坑(重要)

Kind 的”节点”是 Docker 容器,NodePort 端口默认不会自动映射到宿主机。有两种解决办法,我们实操时再详细走:

  • 办法 A:建集群时在 kind-config.yaml 里配 extraPortMappings 把节点端口映射出来(需重建集群)
  • 办法 B:临时用 kubectl port-forward svc/my-nginx-np 8080:80 --address 0.0.0.0 转发(简单,适合测试)

这与第一章坑 4(port-forward 只绑 127.0.0.1)一脉相承——Kind 的网络模型和真实集群有差异,NodePort 在真机上直接就能用,在 Kind 里要多一步。


DNS 服务发现

集群内的 Service 都有 DNS 名字,Pod 之间靠名字互相找,不用记 IP。

1
2
3
4
5
6
7
完整域名格式:
<service名>.<namespace>.svc.cluster.local

namespace 内可简写:
my-nginx-svc ← 最常用
namespace
my-nginx-svc.default ← 加 namespace

这是微服务互调的基础:前端配置里写 http://backend-svc,K8s 的 DNS(CoreDNS)自动解析到对应 Service 的 ClusterIP,再转发到后端 Pod。代码里写服务名,不写 IP——这是 K8s 服务发现的精髓。

1
2
3
4
# 在临时 Pod 里验证 DNS 解析
kubectl run test --rm -it --image=busybox:1.36 --restart=Never -- sh
nslookup my-nginx-svc # 解析出 ClusterIP
exit

章节练习

练习 1:ClusterIP + 关联验证

1
2
3
4
# 1. 确保 my-nginx-deploy 有 3 个副本(kubectl scale)
# 2. expose 一个 ClusterIP Service
# 3. kubectl get endpoints——确认后面挂了 3 个 Pod IP
# 4. 删掉一个 Pod,再看 endpoints——IP 列表自动更新了吗?

练习 2:集群内访问 + 负载均衡

1
2
3
# 1. 起 busybox 临时 Pod
# 2. 多次 wget Service 的 DNS 名字
# 3. (可选)给每个 nginx Pod 改不同首页内容,观察请求被分发到不同 Pod

练习 3:NodePort 对外暴露

1
2
3
# 1. 建一个 NodePort Service
# 2. kubectl get svc 看分配的 3xxxx 端口
# 3. 用 port-forward --address 0.0.0.0 从物理机浏览器访问

练习 4:DNS 服务发现

1
2
# 1. busybox 临时 Pod 里 nslookup Service 名字
# 2. 观察解析出的 ClusterIP,和 kubectl get svc 对比是否一致

自检清单

  • 能说清为什么不能直接用 Pod IP(IP 会变)
  • 理解 Service 靠 label selector 动态关联 Pod
  • 知道 Endpoints 是”当前匹配的 Pod IP 列表”,自动更新
  • 分得清 ClusterIP / NodePort / LoadBalancer 三种类型的用途
  • 能 expose 出 ClusterIP 并在集群内用 DNS 名字访问
  • 能建 NodePort 并从集群外访问(Kind 里知道要多一步)
  • 理解 DNS 服务发现:代码里写服务名不写 IP

本章核心认知回顾

1
2
3
4
5
6
7
8
9
10
11
Service = 一组 Pod 的稳定入口(固定 ClusterIP + DNS 名字)

label selector 动态匹配 Pod → Endpoints 实时维护 IP 列表

三种类型:
ClusterIP 对内,集群里 Pod 用名字/虚拟IP 访问
NodePort 对外,节点IP:3xxxx(Kind 里节点是 Docker 容器,要 port-forward 补一步)
LoadBalancer 对外,云厂商给公网 IP(生产环境)

DNS 服务发现:<service>.<namespace>.svc.cluster.local
代码里写服务名,永远不写 IP —— 这是微服务互调的根基

K8s学习笔记:03-Service网络
https://bote798.top/2026/07/13/K8s学习笔记-03-Service网络/
作者
bote798
发布于
2026年7月13日
更新于
2026年7月16日
许可协议