第8章:K8s 核心概念(上)- 工作负载
我们的 K8s 集群已经就绪,就像一个建好的港口。现在,我们需要学习如何把我们的“集装箱”(容器)运送到这个港口,并让港口按照我们的意愿来管理它们。
在 K8s 的世界里,我们不直接操作单个容器。相反,我们通过操作更高级别的 “工作负载 (Workload)” 资源来管理应用的生命周期。本章,我们将学习两个最核心的工作负载资源:Pod 和 Deployment。
8.1 Pod:K8s 的原子调度单元
在 Docker 中,最基本的管理单元是容器。但在 K8s 中,情况有所不同。
Pod 是 Kubernetes 中创建和管理的、最小的可部署计算单元。
把它想象成一个“豆荚”。一个豆荚(Pod)里,可以包含一个或多个紧密相关的豆子(容器)。
-
一个 Pod 里有什么?
- 一个或多个容器:这些容器共享同一个网络空间(它们可以用
localhost互相访问)和存储卷(数据卷)。 - 网络资源:每个 Pod 都会被分配一个集群内唯一的 IP 地址。
- 存储资源:可以为 Pod 挂载存储卷。
- 一个或多个容器:这些容器共享同一个网络空间(它们可以用
-
为什么需要 Pod? 为什么不直接管理容器呢?因为有些应用场景,需要多个容器“像一个单元一样”被部署和管理。最经典的例子是“边车模式 (Sidecar)”:一个主应用容器,旁边跟着一个专门负责日志收集或网络代理的辅助容器。这两个容器生命周期高度一致,需要被一起调度、一起扩缩容。Pod 完美地解决了这个问题。
-
黄金法则: 虽然一个 Pod 可以有多个容器,但在绝大多数情况下(99%),我们的实践是“一个 Pod,一个容器”。除非你有非常明确的理由,否则请坚持这个原则,它能让你的应用架构更清晰。
所以,你可以暂时简单地理解为:Pod 就是对 Docker 容器的一层封装。
8.2 ReplicaSet & Deployment:应用的扩缩容与滚动更新
我们很少会直接创建一个孤零零的 Pod。为什么?因为如果这个 Pod 所在的 Node 节点宕机了,这个 Pod 也就消失了,K8s 不会自动重建它。这显然不满足我们对高可用的要求。
我们需要一个更高级别的“老板”来照看我们的 Pod。
-
ReplicaSet (副本集)
- 职责:它的唯一职责,就是确保在任何时候,都有指定数量的、一模一样的 Pod 副本在运行。
- 工作方式:你告诉 ReplicaSet:“我需要3个A应用的Pod副本”。它就会创建3个。如果有一个Pod死掉了,ReplicaSet 会立刻发现,并马上创建一个新的来替代它,始终维持总数为3。
-
Deployment
-
职责:这是我们在生产环境中最常用的工作负载资源。它是一个更高级别的控制器,它管理 ReplicaSet,并为我们提供了更强大的功能,特别是声明式的应用更新(滚动更新)和回滚。
-
工作方式:
- 你创建一个 Deployment,在里面定义你应用的期望状态(比如,使用
my-app:1.0镜像,需要3个副本)。 - Deployment 会创建一个 ReplicaSet。
- ReplicaSet 再去创建3个 Pod。
- 这个关系链是 Deployment -> ReplicaSet -> Pods。
- 你创建一个 Deployment,在里面定义你应用的期望状态(比如,使用
-
为什么需要 Deployment? 想象一下你要发布新版本
my-app:1.1。你只需要修改 Deployment 的定义,把镜像版本从1.0改成1.1。Deployment 就会智能地为你执行一次滚动更新 (Rolling Update):它会创建一个新的、使用新版本镜像的 ReplicaSet,然后一个一个地用新 Pod 替换旧 Pod,直到全部替换完成。整个过程服务不中断,非常平滑。
-
8.3 [实战] 将我们的 Web 应用部署到 K8s
理论讲完了,让我们把之前在 Docker 篇创建的 my-app 应用,部署到我们的 Minikube 集群中。
第一步:准备 YAML 文件
与 K8s 交互,就是与 YAML 文件打交道。在你的 my-app 项目根目录下,创建一个名为 deployment.yaml 的文件。
# 指定 API 版本
apiVersion: apps/v1
# 指定资源类型为 Deployment
kind: Deployment
# 元数据,描述这个 Deployment
metadata:
name: my-app-deployment
# 规格,定义期望状态
spec:
# 期望的 Pod 副本数量
replicas: 2
# 选择器,告诉 Deployment 要管理哪些 Pod
selector:
matchLabels:
app: my-app
# Pod 的模板,定义了要创建的 Pod 长什么样
template:
metadata:
# Pod 自身的标签,必须和上面的 selector 匹配
labels:
app: my-app
spec:
# 定义 Pod 中的容器
containers:
- name: my-app-container
# 使用哪个镜像
# 注意:这里需要使用你推送到 Docker Hub 的镜像地址
# image: <your-dockerhub-username>/my-app:1.1
# 这里我们使用本地构建的镜像,需要单独执行构建命令,在示例项目代码中,
# 执行 "npm run build:minikube" 即可构建并将镜像加载到 Minikube 中,这个过程可能会比较耗时,需要等待完成后才能继续,不然找不到镜像
# "build:minikube": "eval $(minikube docker-env) && docker build -t my-app:1.1 ."
image: my-app:1.1
# 容器监听的端口
ports:
- containerPort: 8080
重要提示:关于镜像 Minikube 运行在一个独立的 Docker 环境中,它默认是无法访问你主机本地 Docker 里的镜像的。你有两个选择:
- 推荐:将你构建好的镜像
push到 Docker Hub,然后在 YAML 文件里使用完整的 Docker Hub 镜像地址,如docker.io/<your-username>/my-app:1.1。 - 开发技巧:执行
eval $(minikube docker-env)(macOS/Linux) 或minikube docker-env | Invoke-Expression(PowerShell)。这个命令会将你的命令行环境指向 Minikube 内的 Docker daemon。这样,你再执行docker build,镜像就会被直接构建到 Minikube 内部,可以直接使用。
第二步:应用你的配置
使用 kubectl apply 命令,将你的“期望状态”提交给 K8s 集群。
kubectl apply -f deployment.yaml
apply: K8s 的核心命令之一,表示“让集群的状态与这个文件描述的一致”。-f: 指定配置文件的路径。
执行后,如果成功,会提示 deployment.apps/my-app-deployment created。
提示:虽然创建成功,但是,这只是创建了 Deployment,没有创建 Service,所以,我们还不能直接访问这个应用。关于 Service,我们会在后面的章节中详细介绍。
第三步:检查部署状态
# 查看 Deployment 的状态
kubectl get deployment my-app-deployment
# 查看 ReplicaSet
kubectl get rs
# 查看 Pods,你应该能看到两个 my-app-deployment-xxx 的 Pod 正在运行
kubectl get pods
你还可以用 describe 命令查看更详细的信息,这在排错时非常有用:
kubectl describe deployment my-app-deploymentkubectl describe pod <pod_name>
8.4 [实战] 体验“永不宕机”的滚动更新
现在,来体验一下 K8s 最令人兴奋的功能。
体验自愈能力 手动删除一个 Pod,模拟故障:
kubectl get pods
# 找到一个 Pod 的名字,比如 my-app-deployment-xxxx
kubectl delete pod <pod_name>
kubectl get pods
删除后,你立刻再次执行 kubectl get pods,你会发现 K8s 几乎在瞬间就创建了一个新的 Pod 来替代被删除的那个,replicas 始终保持为2。这就是 ReplicaSet 的功劳。
体验滚动更新
-
修改代码并构建新镜像:
- 修改
app.js,比如返回 "Hello, Kubernetes!"。 - 构建一个新版本的镜像
my-app:1.2,并推送到 Docker Hub (或者构建到 Minikube 内部)。
- 修改
-
更新 Deployment: 有两种方式:
- 方法一(推荐):直接修改
deployment.yaml文件,将image的 tag 改为1.2,然后再次执行kubectl apply -f deployment.yaml。K8s 会智能地计算出差异并只更新变化的部分。 - 方法二(命令式):
kubectl set image deployment/my-app-deployment my-app-container=<your-dockerhub-username>/my-app:1.2
- 方法一(推荐):直接修改
-
观察更新过程: 执行更新后,立刻打开另一个终端,执行
kubectl get pods -w(-w表示 watch,实时监控变化)。你会看到 K8s 正在:- 创建一个新的 Pod (版本1.2)
- 等新 Pod 启动成功后,删除一个旧的 Pod (版本1.1)
- 再创建一个新 Pod,再删除一个旧 Pod...
- 直到所有 Pod 都更新为版本1.2。
整个过程服务不中断。这就是滚动更新的魅力!
体验回滚 如果发现新版本有 Bug 怎么办?一键回滚!
# 查看发布历史
kubectl rollout history deployment/my-app-deployment
# 回滚到上一个版本
kubectl rollout undo deployment/my-app-deployment
K8s 会用同样平滑的方式,将应用回滚到之前的稳定版本。
8.5 本章小结
你已经掌握了在 K8s 中部署无状态应用的核心技能。
- 本章回顾:
- 我们理解了 Pod 是 K8s 中最小的部署单元,通常坚持“一个 Pod 一个容器”的原则。
- 我们学习了通过 Deployment 来管理应用的副本数量、自愈和滚动更新。
- 我们掌握了通过编写 YAML 文件来向 K8s 声明我们的应用部署意图。
- 我们使用
kubectl apply将应用部署到了 K8s,并用get,describe,delete等命令进行了交互。 - 我们亲身体验了 K8s 强大的自愈能力、滚动更新和一键回滚功能。
目前,我们的应用虽然已经在 K8s 中运行起来了,但外界还无法访问它。在下一章,我们将学习 K8s 的网络核心——Service,来为我们的应用打开通往世界的大门。