从YAML到Nginx服务访问:动手理解 Kubernetes Pod、Deployment 与 Service

文章目录

  • [创建 pod](#创建 pod)
  • [创建 deployment](#创建 deployment)
    • [检查 YAML 是否能被 Kubernetes 接受](#检查 YAML 是否能被 Kubernetes 接受)
    • [真正创建 kubectl apply -f](#真正创建 kubectl apply -f)
    • [为什么需要 Deployment 和 ReplicaSet 两层?](#为什么需要 Deployment 和 ReplicaSet 两层?)
    • 通过实验验证自动恢复
    • 对比实验前后
  • [为什么需要 Service](#为什么需要 Service)
    • [Service 是怎么解决这个问题的?](#Service 是怎么解决这个问题的?)
    • [Pod 的标签](#Pod 的标签)
    • [先理解 Service YAML 的核心配置](#先理解 Service YAML 的核心配置)
  • 创建service
    • [创建 nginx-service.yaml](#创建 nginx-service.yaml)
    • [检查 Service 实际发现了哪些后端](#检查 Service 实际发现了哪些后端)
    • [为什么要看 EndpointSlice?](#为什么要看 EndpointSlice?)
  • [真正从 Service 入口访问 Nginx](#真正从 Service 入口访问 Nginx)
  • [验证后端 Pod 变化时,Service 会怎样](#验证后端 Pod 变化时,Service 会怎样)

创建命名空间

bash 复制代码
kubectl create namespace nginx-yaml-lab

创建 pod

nginx-pod.yaml

yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
  namespace: nginx-yaml-lab
  labels:
    app: nginx
spec:
  containers:
    - name: nginx
      image: m.daocloud.io/docker.io/library/nginx:stable
      ports:
        - containerPort: 80

验证Nginx是否真的能响应HTTP

bash 复制代码
kubectl exec -n nginx-yaml-lab nginx-pod -- curl -I --max-time 5 http://127.0.0.1/

创建 deployment

vi nginx-deployment.yaml

yaml 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  namespace: nginx-yaml-lab
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx-deployment
  template:
    metadata:
      labels:
        app: nginx-deployment
    spec:
      containers:
        - name: nginx
          image: m.daocloud.io/docker.io/library/nginx:stable
          ports:
            - containerPort: 80

检查 YAML 是否能被 Kubernetes 接受

bash 复制代码
kubectl apply --dry-run=server -f nginx-deployment.yaml

这里的 --dry-run=server 是服务端试运行:Kubernetes API Server 会验证请求,但不会真正创建 Deployment。

deployment.apps/nginx-deployment created (server dry run)就说明服务端验证通过了;这里的 created 只是试运行结果,不代表实际资源已经创建。

真正创建 kubectl apply -f

bash 复制代码
kubectl apply -f nginx-deployment.yaml

然后查看:

bash 复制代码
kubectl get deployment,replicaset,pod -n nginx-yaml-lab

为什么需要 Deployment 和 ReplicaSet 两层?

这里是 Kubernetes 的关键机制。

  • Deployment:负责管理应用发布,例如滚动更新、回滚,以及管理 ReplicaSet。
  • ReplicaSet:负责让实际 Pod 副本数尽量符合期望值。
  • Pod:实际运行容器的工作负载。

例如,你声明 replicas: 2,ReplicaSet 就会持续比较期望副本数与实际副本数。

如果实际只剩 1 个 Pod,它会尝试创建一个新的 Pod 补足;如果实际变成 3 个,则会尝试缩减到 2 个。

这就是控制器的调谐机制(Reconciliation):持续比较期望状态和实际状态,并尝试消除差异。

通过实验验证自动恢复

我们已经看到两个 Pod 正常运行。现在做一个小实验,观察删除其中一个 Pod 后,ReplicaSet 会不会创建替代者。

先列出由 Deployment 创建的两个 Pod:

bash 复制代码
kubectl get pods -n nginx-yaml-lab -l app=nginx-deployment -o wide

这里的 -l app=nginx-deployment 是标签选择器,只筛选标签匹配的 Pod,因此不会选中独立的 nginx-pod。

实验:删除一个 Pod,观察 Kubernetes 如何恢复

我们先预测结果,再动手。

假设删除 nginx-deployment-784659cff6-cfgxd:

  1. 这个 Pod 对象会被删除。
  2. ReplicaSet 发现实际副本数暂时从 2 变成 1。
  3. ReplicaSet 尝试创建一个新 Pod,让实际副本数恢复到 2。
  4. 新 Pod 通常会有不同的名称和 IP,原 Pod 不会复活。
    这次只操作我们自己创建的 nginx-yaml-lab 命名空间,不影响你原有的博客服务。
    执行删除:
bash 复制代码
kubectl delete pod nginx-deployment-784659cff6-cfgxd -n nginx-yaml-lab

然后马上查看:

bash 复制代码
kubectl get pods -n nginx-yaml-lab -l app=nginx-deployment -o wide

对比实验前后

我们观察到三个关键事实:

  • 副本数量仍然是 2。
  • 被删除的 Pod 没有恢复原样,而是出现了一个新名称的 Pod。
  • 新 Pod 获得了新的 IP:10.42.0.194。
    这正是 ReplicaSet 的作用:维持期望的副本数量,而不是保证某个特定 Pod 永远存在。

背后的机制到底是什么?

你可以把它理解为一个持续运行的闭环:

这个过程叫作 Reconciliation(调谐)。它不是只在删除时运行一次,而是持续工作;控制器发现实际状态与期望状态有差异,就会尝试修正。

这里还有一个重要区别:如果只是删除一个 Pod,ReplicaSet 通常会补建它;如果你删除的是整个 Deployment,情况就不同了,因为负责管理它的上级对象也被删除了

为什么需要 Service

现在你有两个 Nginx Pod:

  • 一个 Pod 的 IP 是 10.42.0.193。
  • 另一个 Pod 的 IP 是 10.42.0.194。

想象一下,如果前端应用要访问这个 Nginx,应该写哪个 IP?如果某个 Pod 被替换,IP 变了,调用方是不是也要跟着修改?

这正是 Kubernetes 中 Service 要解决的问题:为一组符合标签条件的 Pod 提供稳定的访问地址,并在符合条件的后端之间转发流量。

如果你要让集群内的其他 Pod 访问这两个 Nginx 副本,你觉得应该直接使用某一个 Pod 的 IP,还是创建一个 Service?为什么?

Service 是怎么解决这个问题的?

Service 通常通过标签选择器找到符合条件的 Pod,并维护对应的 EndpointSlice。集群网络组件再根据 Service 的规则转发流量。

要注意,Service 提供稳定入口和后端发现;它不承诺每个 Pod 收到完全相同数量的请求。实际分流取决于网络实现、连接复用等因素。

Pod 的标签

创建 Service 前,先确认两个副本都带有我们预期的标签。

bash 复制代码
kubectl get pods -n nginx-yaml-lab -l app=nginx-deployment --show-labels

能看到两个 Pod 都带有:app=nginx-deployment;

因为我们准备创建的 Service 会用这个标签来识别后端,而不是把某个 Pod 的 IP 写进配置。

先理解 Service YAML 的核心配置

接下来要创建的 nginx-service.yaml 大致是这样的:

yaml 复制代码
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
  namespace: nginx-yaml-lab
spec:
  type: ClusterIP
  selector:
    app: nginx-deployment
  ports:
    - port: 80
      targetPort: 80

几个配置分别代表:

配置项 作用
type: ClusterIP 创建集群内部可访问的稳定虚拟 IP
selector 选择标签为 app=nginx‑deployment 的 Pod
port: 80 Service 对外提供的端口
targetPort: 80 将流量转发到后端容器的 80 端口
这里的 port 和 targetPort 是两个不同概念。你可以把前者理解为服务入口的端口,后者理解为后端应用实际接收流量的端口。

创建service

vi nginx-service.yaml

yaml 复制代码
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
  namespace: nginx-yaml-lab
spec:
  type: ClusterIP
  selector:
    app: nginx-deployment
  ports:
    - port: 80
      targetPort: 80

创建前,先理解一个关键点

Service 不会直接把流量发给 Deployment,也不需要你手动填写两个 Pod 的 IP。它通过 selector 匹配 Pod 标签,Kubernetes 再维护对应的 EndpointSlice,记录当前符合条件的后端地址。

所以,即使某个 Pod 被删除、替换并获得了新 IP,只要新 Pod 仍然符合标签选择条件,Service 的后端列表就会随之更新。

创建 nginx-service.yaml

bash 复制代码
kubectl apply -f nginx-service.yaml

然后查看 Service:

bash 复制代码
kubectl get service nginx-service -n nginx-yaml-lab

Service 已经创建成功了。现在你有了一个稳定的集群内入口:

bash 复制代码
nginx-service
ClusterIP: 10.43.131.88
Port:      80/TCP
Type:      ClusterIP

但注意:Service 创建成功,不代表它已经找到后端 Pod。 我们需要继续验证。

检查 Service 实际发现了哪些后端

bash 复制代码
kubectl get endpointslice -n nginx-yaml-lab -l kubernetes.io/service-name=nginx-service

重点观察输出里的 ADDRESSTYPE、PORTS 和 ENDPOINTS(不同 kubectl 版本的列名可能略有差异)。

我们预期会看到两个后端地址:

  • 10.42.0.193
  • 10.42.0.194
    这两个地址对应刚才的两个 Nginx Pod。

为什么要看 EndpointSlice?

因为 Service 的 ClusterIP 是一个稳定的虚拟入口,而 EndpointSlice 记录了这个 Service 当前发现的后端端点。它能帮助我们区分两类问题:

  • Service 存在,但没有匹配到后端 Pod。
  • Service 已经匹配到后端,但实际请求仍然失败。
bash 复制代码
Service: nginx-service
ClusterIP: 10.43.131.88
Port: 80

EndpointSlice:
10.42.0.194:80
10.42.0.193:80

我们现在已经有了完整的证据链:

  1. Deployment 声明需要 2 个副本。
  2. ReplicaSet 创建并维持了 2 个 Pod。
  3. Service 根据 app=nginx-deployment 标签选择后端。
  4. EndpointSlice 中确实记录了两个 Pod 的 IP 和端口。

这里再强调一个重要区别:EndpointSlice 记录后端地址;Service 的 ClusterIP 提供稳定入口;集群网络组件负责将流量转发到后端。

真正从 Service 入口访问 Nginx

之前我们用 curl http://127.0.0.1/ 测试的是容器内部的 Nginx。现在我们要验证的是另一件事:通过 Service 的虚拟 IP 访问它。

为了避免在服务器上安装软件,也不碰现有博客和 Jenkins 服务,我们直接使用已有的 Nginx Pod 来测试。

bash 复制代码
kubectl exec -n nginx-yaml-lab nginx-pod -- curl -I --max-time 5 http://10.43.131.88/

HTTP/1.1 200 OK,说明你已经成功通过 Service 的 ClusterIP 访问到了 Nginx。

到这里,我们已经亲手验证了 Kubernetes 中一条完整的服务访问链路:

  • Deployment:声明需要 2 个副本。
  • ReplicaSet:维持副本数量。
  • Pod:实际运行 Nginx。
  • Service:提供稳定的集群内访问入口。
  • EndpointSlice:记录当前匹配的后端地址。

验证后端 Pod 变化时,Service 会怎样

我们来做一个小实验。刚才 EndpointSlice 记录了两个后端:

  • 10.42.0.193
  • 10.42.0.194
    现在我们删除其中一个后端 Pod,看看 Kubernetes 会不会同时更新 EndpointSlice,以及 Service 是否还能正常响应。

删除一个pod

先查看当前 Pod:

bash 复制代码
kubectl get pods -n nginx-yaml-lab -l app=nginx-deployment -o wide

从输出中找出名称对应 IP 为 10.42.0.194 的 Pod。然后执行删除:

bash 复制代码
kubectl delete pod nginx-deployment-784659cff6-6c7fx -n nginx-yaml-lab

对照一下实验结果

EndpointSlice 现在记录的是:

  • 10.42.0.193
  • 10.42.0.195
    旧 IP 10.42.0.194 已经被新 IP 10.42.0.195 替代。与此同时,Pod 数量仍然是 2。

这里到底是谁在做什么?

把这几个组件的职责分清楚,是 Kubernetes 面试中的重要基础。

  • Deployment:管理发布版本,并维护 ReplicaSet。
  • ReplicaSet:发现副本数量不足,创建新的 Pod。
  • EndpointSlice 控制器:根据 Service 的选择器及后端 Pod 状态,更新服务对应的端点信息。
  • Service:提供稳定的虚拟访问入口,集群网络组件根据规则把流量转发到可用后端。

因此,Pod IP 改变并不要求调用方修改 Service 地址。只要 Service 本身没变,调用方就可以继续使用原来的 ClusterIP。

不过要注意:控制器和网络规则的更新需要时间,所以在删除与替换的短暂过程中,不能保证每一个请求都成功。

最后验证一下:Service 的地址有没有变化?

之前的 Service ClusterIP 是 10.43.131.88。现在先检查它是否保持不变:

bash 复制代码
kubectl get service nginx-service -n nginx-yaml-lab

然后再次从独立 Pod 发起请求:

bash 复制代码
kubectl exec -n nginx-yaml-lab nginx-pod -- curl -I --max-time 5 http://10.43.131.88/

HTTP/1.1 200 OK,我们就又验证了一件事:后端 Pod 被替换后,客户端依然可以通过同一个 Service 地址访问应用。

相关推荐
白帽攻防录1 小时前
SRC 挖洞:DHCP 服务器上的四个 UAF,内网原生 RCE 怎么打域管
运维·服务器·网络安全·内网安全
王琦03181 小时前
jenkins使用手册
运维·jenkins
VidDown2 小时前
用户说“卡顿“,服务器一切正常:播放质量监测与 QoE 指标
运维·服务器·ffmpeg·音视频·视频
瑞克-桑切斯2 小时前
13、DNS、DHCP 与 FTP 协议详解
linux·运维·云计算
程序员-Benothing2 小时前
Linux性能分析方法论:USE、RED、火焰图与perf实战
linux·运维·服务器
问心无愧05132 小时前
[ACTF2020 新生赛]Exec
运维·服务器·笔记
cg.family2 小时前
K8S:scale 与 delete pod 核心区别
运维·docker·容器
开开心心_Every2 小时前
黄金价格软件国内外价格实时监控超方便
运维·服务器·游戏·智能手机·pdf·lua·perl
Initialize293082 小时前
systemd 服务起不来?五步排查法
运维