563 lines
21 KiB
Markdown
563 lines
21 KiB
Markdown
|
|
---
|
|||
|
|
tags: [k8s, devops, container-orchestration]
|
|||
|
|
create time: 2026-07-07 15:48
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# Kubernetes 概述
|
|||
|
|
|
|||
|
|
## 概述
|
|||
|
|
|
|||
|
|
Kubernetes(简称 K8s)是一个开源的**容器编排平台**,由 Google 内部的 Borg 系统演化而来,于 2014 年开源,目前由 CNCF(Cloud Native Computing Foundation)托管。它的核心使命是:**让部署、扩展和管理容器化应用变得自动化且可预测**。简单来说,Docker 解决了「如何打包一个应用」的问题,而 K8s 解决的是「如何在成百上千台机器上高效地运行和管理这些应用」的问题。
|
|||
|
|
|
|||
|
|
## 为什么需要 K8s
|
|||
|
|
|
|||
|
|
### 从 Docker 单机到集群编排
|
|||
|
|
|
|||
|
|
让我们先回顾一下容器技术的演进路径:
|
|||
|
|
|
|||
|
|
1. **裸机/虚拟机时代**:应用直接部署在物理机或 VM 上,手动管理依赖、端口和资源分配。
|
|||
|
|
2. **Docker 单机时代**:容器解决了环境一致性和依赖打包的问题,但单机资源有限,且面临应用故障恢复、滚动更新等挑战。
|
|||
|
|
3. **集群编排时代**:当你的服务需要跨多台机器运行时,你需要一个「大脑」来决定:把容器调度到哪台机器?挂了怎么重启?流量怎么分配?——这就是 K8s 存在的意义。
|
|||
|
|
|
|||
|
|
> [!tip] 一个类比
|
|||
|
|
> 如果 Docker 集装箱标准化了「货物打包」,那 K8s 就是自动化码头——它决定集装箱该放哪艘船、航线怎么规划、遇到风暴如何重新调度。
|
|||
|
|
|
|||
|
|
### Docker Compose vs Docker Swarm vs Kubernetes
|
|||
|
|
|
|||
|
|
| 特性 | Docker Compose | Docker Swarm | Kubernetes |
|
|||
|
|
|------|---------------|-------------|------------|
|
|||
|
|
| **定位** | 单机多容器编排 | 轻量级集群编排 | 企业级容器编排平台 |
|
|||
|
|
| **规模** | 单主机 | 中小规模集群 | 大规模生产环境 |
|
|||
|
|
| **学习曲线** | 低 | 中 | 高 |
|
|||
|
|
| **自动扩缩容** | 不支持 | 基础支持 | HPA / VPA / Cluster Autoscaler |
|
|||
|
|
| **自愈能力** | 无 | 基础(重启策略) | 强(Liveness/Readiness Probe + 控制器) |
|
|||
|
|
| **滚动更新** | 不支持 | 支持 | 细粒度控制(maxUnavailable / maxSurge) |
|
|||
|
|
| **生态** | 仅 Docker 生态 | 仅 Docker 生态 | CNCF 生态,插件丰富 |
|
|||
|
|
| **服务发现** | 容器名 DNS | 内置 DNS | CoreDNS + Service 资源 |
|
|||
|
|
| **适用场景** | 本地开发、测试 | 快速上手小集群 | 生产环境首选 |
|
|||
|
|
|
|||
|
|
> [!info] 为什么 Swarm 没能打败 K8s?
|
|||
|
|
> Docker Swarm 的设计哲学是「简单优先」,但现实世界的生产需求远比想象中复杂。K8s 的声明式 API、丰富的控制器模型、以及 CNCF 生态的飞轮效应,最终让它成为事实标准。
|
|||
|
|
|
|||
|
|
## 核心架构
|
|||
|
|
|
|||
|
|
K8s 采用经典的 **Master-Worker 架构**(官方文档中 Master 节点现在更常称为 Control Plane)。
|
|||
|
|
|
|||
|
|
### 架构总览
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
graph TB
|
|||
|
|
subgraph ControlPlane["Control Plane(控制平面)"]
|
|||
|
|
API["API Server<br/>集群的唯一入口"]
|
|||
|
|
etcd["etcd<br/>分布式 KV 存储"]
|
|||
|
|
sched["Scheduler<br/>Pod 调度决策"]
|
|||
|
|
cm["Controller Manager<br/>维持期望状态"]
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
subgraph Worker1["Worker Node 1"]
|
|||
|
|
kubelet1["kubelet<br/>管理 Pod 生命周期"]
|
|||
|
|
proxy1["kube-proxy<br/>网络规则管理"]
|
|||
|
|
cr1["Container Runtime<br/>containerd / CRI-O"]
|
|||
|
|
P1a["Pod A"]
|
|||
|
|
P1b["Pod B"]
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
subgraph Worker2["Worker Node 2"]
|
|||
|
|
kubelet2["kubelet"]
|
|||
|
|
proxy2["kube-proxy"]
|
|||
|
|
cr2["Container Runtime"]
|
|||
|
|
P2a["Pod C"]
|
|||
|
|
P2b["Pod D"]
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
subgraph Client["用户 / CI/CD"]
|
|||
|
|
kubectl["kubectl / SDK"]
|
|||
|
|
end
|
|||
|
|
|
|||
|
|
kubectl -->|"REST API"| API
|
|||
|
|
API <-->|"读写"| etcd
|
|||
|
|
API -->|"监听事件"| sched
|
|||
|
|
API -->|"监听事件"| cm
|
|||
|
|
kubelet1 -->|"汇报状态"| API
|
|||
|
|
kubelet2 -->|"汇报状态"| API
|
|||
|
|
kubelet1 --> cr1
|
|||
|
|
kubelet2 --> cr2
|
|||
|
|
cr1 --> P1a & P1b
|
|||
|
|
cr2 --> P2a & P2b
|
|||
|
|
proxy1 -.->|"Service 流量"| P1a & P1b
|
|||
|
|
proxy2 -.->|"Service 流量"| P2a & P2b
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Control Plane 组件详解
|
|||
|
|
|
|||
|
|
#### API Server(kube-apiserver)
|
|||
|
|
|
|||
|
|
API Server 是整个集群的**唯一入口**,所有组件之间的通信都通过它进行。它承担的核心职责:
|
|||
|
|
|
|||
|
|
- **RESTful API 网关**:暴露 K8s API,所有 `kubectl` 操作最终都到达这里
|
|||
|
|
- **认证与授权**:RBAC、ServiceAccount、Webhook Token 等
|
|||
|
|
- **准入控制(Admission Control)**:在资源持久化之前进行校验和修改(如 MutatingWebhook、ValidatingWebhook)
|
|||
|
|
- **Watch 机制**:其他组件通过 Watch API Server 获取资源变更事件
|
|||
|
|
|
|||
|
|
> [!tip] 为什么所有组件都要通过 API Server 通信?
|
|||
|
|
> 这是 K8s 的一个重要设计决策——**中心化 API 网关**保证了统一的认证、授权、审计和数据校验逻辑。如果各组件直接读写 etcd,安全策略将难以实施。
|
|||
|
|
|
|||
|
|
#### etcd
|
|||
|
|
|
|||
|
|
etcd 是一个高可用的分布式 KV 存储,是 K8s 的「唯一数据源」。所有集群状态(Pod、Service、ConfigMap 等资源对象)都持久化在 etcd 中。
|
|||
|
|
|
|||
|
|
- 基于 **Raft 共识算法**保证数据一致性
|
|||
|
|
- 生产环境通常部署 3 或 5 个节点(奇数个,便于选举)
|
|||
|
|
- **Watch 机制**:支持高效地监听 key 的变更
|
|||
|
|
|
|||
|
|
> [!warning] etcd 是整个集群的 Single Point of Truth
|
|||
|
|
> 如果 etcd 数据丢失且没有备份,整个集群状态将不可恢复。生产环境务必做好 etcd 的定期备份。
|
|||
|
|
|
|||
|
|
#### Scheduler(kube-scheduler)
|
|||
|
|
|
|||
|
|
Scheduler 负责将新创建的 Pod **绑定到合适的 Node** 上。调度过程分为两个阶段:
|
|||
|
|
|
|||
|
|
1. **过滤(Filtering)**:排除不满足条件的 Node(如资源不足、Taint 不匹配、亲和性规则冲突)
|
|||
|
|
2. **打分(Scoring)**:对剩余 Node 进行评分,选择得分最高的
|
|||
|
|
|
|||
|
|
常见调度策略:
|
|||
|
|
|
|||
|
|
- **资源请求与限制**:基于 Pod 的 `requests` 和 `limits`
|
|||
|
|
- **亲和性与反亲和性**:`nodeAffinity`、`podAffinity`、`podAntiAffinity`
|
|||
|
|
- **Taint 和 Toleration**:驱逐不兼容的 Pod
|
|||
|
|
- **拓扑分布约束**:`topologySpreadConstraints` 跨可用区均匀分布
|
|||
|
|
|
|||
|
|
#### Controller Manager(kube-controller-manager)
|
|||
|
|
|
|||
|
|
Controller Manager 是一组控制器的集合,每个控制器负责一种资源类型的「调谐循环(Reconciliation Loop)」。核心思想:**持续比较「期望状态」和「实际状态」,并采取行动使实际状态趋近期望状态**。
|
|||
|
|
|
|||
|
|
常见控制器:
|
|||
|
|
|
|||
|
|
| 控制器 | 职责 |
|
|||
|
|
|--------|------|
|
|||
|
|
| Deployment Controller | 管理 ReplicaSet 的创建/更新 |
|
|||
|
|
| ReplicaSet Controller | 确保指定数量的 Pod 副本运行 |
|
|||
|
|
| Node Controller | 监控 Node 心跳,处理节点故障 |
|
|||
|
|
| Job Controller | 管理一次性任务的 Pod |
|
|||
|
|
| ServiceAccount Controller | 为新 Namespace 创建默认 ServiceAccount |
|
|||
|
|
|
|||
|
|
### Worker Node 组件详解
|
|||
|
|
|
|||
|
|
#### kubelet
|
|||
|
|
|
|||
|
|
kubelet 是运行在每个 Worker Node 上的**代理进程**,负责:
|
|||
|
|
|
|||
|
|
- 从 API Server 获取分配给本节点的 Pod Spec
|
|||
|
|
- 调用 Container Runtime 拉取镜像、启动容器
|
|||
|
|
- 执行健康检查(Liveness Probe、Readiness Probe、Startup Probe)
|
|||
|
|
- 向 API Server 汇报节点和 Pod 状态
|
|||
|
|
- 管理 Volume 的挂载和卸载
|
|||
|
|
|
|||
|
|
> [!info] kubelet 不是通过 API Server 启动容器的
|
|||
|
|
> kubelet 通过 **CRI(Container Runtime Interface)** 与容器运行时交互,而非直接调用 Docker CLI。
|
|||
|
|
|
|||
|
|
#### kube-proxy
|
|||
|
|
|
|||
|
|
kube-proxy 负责在每个节点上维护**网络规则**,实现 Service 的负载均衡。它有三种工作模式:
|
|||
|
|
|
|||
|
|
| 模式 | 实现方式 | 性能 | 推荐度 |
|
|||
|
|
|------|---------|------|--------|
|
|||
|
|
| iptables | Linux iptables 规则 | 中等(规则数线性增长) | 默认模式 |
|
|||
|
|
| IPVS | Linux IPVS(内核级 LB) | 高(哈希表查找) | 大规模集群推荐 |
|
|||
|
|
| nftables | nftables 规则 | 高 | K8s 1.29+ 实验性 |
|
|||
|
|
|
|||
|
|
#### Container Runtime
|
|||
|
|
|
|||
|
|
Container Runtime 是真正执行容器生命周期操作的组件。K8s 通过 **CRI(Container Runtime Interface)** 标准化了与运行时的交互方式:
|
|||
|
|
|
|||
|
|
- **containerd**:目前最主流的选择,Docker 的核心运行时组件被独立出来
|
|||
|
|
- **CRI-O**:Red Hat 主导,专为 K8s 设计的轻量级运行时
|
|||
|
|
- **Docker**:自 K8s 1.24 起不再直接支持(dockershim 已移除),但 containerd 仍可使用
|
|||
|
|
|
|||
|
|
## 关键概念
|
|||
|
|
|
|||
|
|
### Pod
|
|||
|
|
|
|||
|
|
Pod 是 K8s 中**最小的可部署单元**,而不是容器。一个 Pod 可以包含一个或多个容器,它们:
|
|||
|
|
|
|||
|
|
- 共享同一个 Network Namespace(即共享 IP 和端口空间)
|
|||
|
|
- 可以通过 Volume 共享存储
|
|||
|
|
- 总是被调度到同一个 Node 上
|
|||
|
|
|
|||
|
|
> [!tip] 为什么需要 Pod 而不是直接管理容器?
|
|||
|
|
> 现实中有些应用需要「伴生容器(Sidecar)」——比如日志收集器、Service Mesh 代理。Pod 将这些紧密耦合的容器打包在一起,共享网络和存储,比独立容器更优雅。
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
# 一个包含 Sidecar 的 Pod 示例
|
|||
|
|
apiVersion: v1
|
|||
|
|
kind: Pod
|
|||
|
|
metadata:
|
|||
|
|
name: app-with-logging
|
|||
|
|
labels:
|
|||
|
|
app: my-app # 用于 Service 选择和筛选
|
|||
|
|
env: production
|
|||
|
|
spec:
|
|||
|
|
containers:
|
|||
|
|
- name: app
|
|||
|
|
image: my-app:v1.2.3
|
|||
|
|
ports:
|
|||
|
|
- containerPort: 8080
|
|||
|
|
resources:
|
|||
|
|
requests:
|
|||
|
|
cpu: "100m" # 0.1 核
|
|||
|
|
memory: "128Mi"
|
|||
|
|
limits:
|
|||
|
|
cpu: "500m" # 最多 0.5 核
|
|||
|
|
memory: "512Mi"
|
|||
|
|
livenessProbe:
|
|||
|
|
httpGet:
|
|||
|
|
path: /healthz
|
|||
|
|
port: 8080
|
|||
|
|
initialDelaySeconds: 10
|
|||
|
|
periodSeconds: 5
|
|||
|
|
readinessProbe:
|
|||
|
|
httpGet:
|
|||
|
|
path: /ready
|
|||
|
|
port: 8080
|
|||
|
|
periodSeconds: 3
|
|||
|
|
- name: log-collector
|
|||
|
|
image: fluentd:v1.16
|
|||
|
|
volumeMounts:
|
|||
|
|
- name: log-volume
|
|||
|
|
mountPath: /var/log/app
|
|||
|
|
volumes:
|
|||
|
|
- name: log-volume
|
|||
|
|
emptyDir: {} # Pod 内共享的临时目录
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Pod 生命周期中的几个关键阶段**:
|
|||
|
|
|
|||
|
|
| Phase | 含义 |
|
|||
|
|
|-------|------|
|
|||
|
|
| Pending | 已被 API Server 接受,但尚未调度或镜像未拉取 |
|
|||
|
|
| Running | 至少一个容器正在运行 |
|
|||
|
|
| Succeeded | 所有容器正常退出(退出码 0) |
|
|||
|
|
| Failed | 至少一个容器异常退出 |
|
|||
|
|
| Unknown | 无法获取 Pod 状态(通常是 Node 通信故障) |
|
|||
|
|
|
|||
|
|
### Node
|
|||
|
|
|
|||
|
|
Node 是 K8s 集群中的**工作机器**(物理机或虚拟机)。每个 Node 包含运行 Pod 所需的服务(kubelet、kube-proxy、Container Runtime)。
|
|||
|
|
|
|||
|
|
查看 Node 信息:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 查看集群所有节点
|
|||
|
|
kubectl get nodes -o wide
|
|||
|
|
|
|||
|
|
# 查看某个节点的详细信息(资源、条件、运行的 Pod)
|
|||
|
|
kubectl describe node <node-name>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Node 有几个重要的**条件(Conditions)**:
|
|||
|
|
|
|||
|
|
| Condition | 说明 |
|
|||
|
|
|-----------|------|
|
|||
|
|
| Ready | 节点健康且准备好接受 Pod |
|
|||
|
|
| MemoryPressure | 节点内存不足 |
|
|||
|
|
| DiskPressure | 节点磁盘空间不足 |
|
|||
|
|
| PIDPressure | 节点进程数过多 |
|
|||
|
|
| NetworkUnavailable | 节点网络未正确配置 |
|
|||
|
|
|
|||
|
|
### Namespace
|
|||
|
|
|
|||
|
|
Namespace 是 K8s 中的**逻辑隔离机制**,用于在同一物理集群中划分多个虚拟集群。适用于:
|
|||
|
|
|
|||
|
|
- 多团队共享集群(`team-a`、`team-b`)
|
|||
|
|
- 环境隔离(`dev`、`staging`、`production`)
|
|||
|
|
- 资源配额管理(每个 Namespace 设置独立的 ResourceQuota)
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
# 创建一个 Namespace
|
|||
|
|
apiVersion: v1
|
|||
|
|
kind: Namespace
|
|||
|
|
metadata:
|
|||
|
|
name: team-backend
|
|||
|
|
labels:
|
|||
|
|
team: backend
|
|||
|
|
env: production
|
|||
|
|
---
|
|||
|
|
# 在该 Namespace 下设置资源配额
|
|||
|
|
apiVersion: v1
|
|||
|
|
kind: ResourceQuota
|
|||
|
|
metadata:
|
|||
|
|
name: team-backend-quota
|
|||
|
|
namespace: team-backend
|
|||
|
|
spec:
|
|||
|
|
hard:
|
|||
|
|
requests.cpu: "10" # 总 CPU 请求上限 10 核
|
|||
|
|
requests.memory: "20Gi" # 总内存请求上限 20Gi
|
|||
|
|
limits.cpu: "20"
|
|||
|
|
limits.memory: "40Gi"
|
|||
|
|
pods: "50" # 最多 50 个 Pod
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
K8s 默认创建的 Namespace:
|
|||
|
|
|
|||
|
|
| Namespace | 用途 |
|
|||
|
|
|-----------|------|
|
|||
|
|
| `default` | 未指定 Namespace 时的默认值 |
|
|||
|
|
| `kube-system` | K8s 系统组件(CoreDNS、kube-proxy 等) |
|
|||
|
|
| `kube-public` | 公共资源(集群信息等) |
|
|||
|
|
| `kube-node-lease` | 节点心跳 Lease 对象 |
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 在指定 Namespace 下操作
|
|||
|
|
kubectl get pods -n team-backend
|
|||
|
|
|
|||
|
|
# 设置默认 Namespace(避免每次都输入 -n)
|
|||
|
|
kubectl config set-context --current --namespace=team-backend
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
> [!warning] Namespace 不提供网络隔离
|
|||
|
|
> 默认情况下,不同 Namespace 的 Pod 可以互相通信。如需网络隔离,需要配合 **NetworkPolicy** 实现。
|
|||
|
|
|
|||
|
|
### Label & Selector
|
|||
|
|
|
|||
|
|
Label 是附加在 K8s 对象上的**键值对**,是 K8s 组织和筛选资源的核心机制。你可能会问:「为什么 K8s 不用目录/文件夹的方式来组织资源?」——因为资源之间的关系是多对多的,一个 Pod 可能同时属于某个应用、某个环境、某个团队,Label 的灵活性远超树形结构。
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
# Label 的常见命名约定
|
|||
|
|
metadata:
|
|||
|
|
labels:
|
|||
|
|
app.kubernetes.io/name: my-app # 应用名称
|
|||
|
|
app.kubernetes.io/version: "1.2.3" # 版本
|
|||
|
|
app.kubernetes.io/component: frontend # 组件
|
|||
|
|
app.kubernetes.io/part-of: platform # 所属系统
|
|||
|
|
app.kubernetes.io/managed-by: helm # 管理工具
|
|||
|
|
env: production # 环境
|
|||
|
|
team: backend # 团队
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Selector 的三种用法**:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 等值选择器
|
|||
|
|
kubectl get pods -l app=my-app,env=production
|
|||
|
|
|
|||
|
|
# 集合选择器
|
|||
|
|
kubectl get pods -l 'env in (production, staging)'
|
|||
|
|
kubectl get pods -l 'env notin (dev)'
|
|||
|
|
|
|||
|
|
# 否定选择器
|
|||
|
|
kubectl get pods -l 'app!=my-app'
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
在资源定义中使用 Selector(以 Service 为例):
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
apiVersion: v1
|
|||
|
|
kind: Service
|
|||
|
|
metadata:
|
|||
|
|
name: my-app-svc
|
|||
|
|
spec:
|
|||
|
|
selector:
|
|||
|
|
app: my-app # 匹配所有带 app=my-app 标签的 Pod
|
|||
|
|
env: production
|
|||
|
|
ports:
|
|||
|
|
- port: 80
|
|||
|
|
targetPort: 8080
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Annotation
|
|||
|
|
|
|||
|
|
Annotation 用于存储**非标识性的附加信息**,这些信息不会被 K8s API 用于筛选,但对工具、库和运维人员很有用。典型的 Annotation 内容:
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
metadata:
|
|||
|
|
annotations:
|
|||
|
|
# 记录维护者信息
|
|||
|
|
owner: "backend-team@company.com"
|
|||
|
|
# Git 提交信息(CI/CD 注入)
|
|||
|
|
git-commit: "abc123f"
|
|||
|
|
git-branch: "main"
|
|||
|
|
# 构建时间
|
|||
|
|
build-time: "2026-07-07T15:00:00Z"
|
|||
|
|
# Ingress 配置(Nginx Ingress Controller 使用)
|
|||
|
|
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
|
|||
|
|
nginx.ingress.kubernetes.io/ssl-redirect: "true"
|
|||
|
|
# Prometheus 自动发现
|
|||
|
|
prometheus.io/scrape: "true"
|
|||
|
|
prometheus.io/port: "9090"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
> [!tip] Label vs Annotation 如何选择?
|
|||
|
|
> 问自己一个问题:「我需要根据这个信息来筛选或分组资源吗?」—— 如果是,用 Label;如果只是附加的描述性信息,用 Annotation。Label 有长度和字符限制,Annotation 则更宽松。
|
|||
|
|
|
|||
|
|
## K8s 与 Docker 的关系
|
|||
|
|
|
|||
|
|
### 常见误解
|
|||
|
|
|
|||
|
|
很多初学者会问:「K8s 是不是要替代 Docker?」答案是:**不完全是**。准确地说:
|
|||
|
|
|
|||
|
|
- K8s 替代的是 Docker Swarm(编排层面的竞争)
|
|||
|
|
- K8s **不再直接使用 Docker Engine** 作为容器运行时(自 1.24 起)
|
|||
|
|
- 但你仍然可以**用 Docker 构建镜像**,K8s 运行的是 OCI 标准镜像,与构建工具无关
|
|||
|
|
|
|||
|
|
### 容器运行时的演进
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
timeline
|
|||
|
|
title K8s 容器运行时演进
|
|||
|
|
2014-2016 : Docker 是唯一选择
|
|||
|
|
: kubelet 直接调用 Docker API
|
|||
|
|
2017 : CRI 标准引入
|
|||
|
|
: dockershim 作为适配层
|
|||
|
|
2018 : containerd 从 Docker 拆分
|
|||
|
|
: CRI-O 项目成熟
|
|||
|
|
2020 : dockershim 弃用公告
|
|||
|
|
2022-04 : K8s 1.24 移除 dockershim
|
|||
|
|
: containerd 成为主流
|
|||
|
|
2024+ : CRI-O 广泛使用
|
|||
|
|
: Kata / gVisor 沙箱运行时兴起
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**CRI(Container Runtime Interface)** 是 K8s 定义的一套 gRPC 接口规范,任何实现了 CRI 的运行时都可以被 kubelet 使用:
|
|||
|
|
|
|||
|
|
| 运行时 | 特点 | 适用场景 |
|
|||
|
|
|--------|------|---------|
|
|||
|
|
| containerd | Docker 核心组件独立版,功能全面 | 通用场景,最广泛使用 |
|
|||
|
|
| CRI-O | 专为 K8s 设计,轻量级 | Red Hat / OpenShift 生态 |
|
|||
|
|
| Kata Containers | 轻量级 VM,安全隔离 | 多租户、不可信工作负载 |
|
|||
|
|
| gVisor (runsc) | 用户态内核沙箱 | 安全敏感场景 |
|
|||
|
|
|
|||
|
|
> [!info] 理解 CRI 的分层
|
|||
|
|
> kubelet → CRI (gRPC) → containerd → runc → Linux Kernel(namespaces, cgroups)
|
|||
|
|
>
|
|||
|
|
> 每一层都有明确的职责边界。containerd 负责镜像管理和容器生命周期,runc 负责实际创建 Linux 容器。
|
|||
|
|
|
|||
|
|
### 为什么移除 dockershim?
|
|||
|
|
|
|||
|
|
移除 dockershim 的主要原因:
|
|||
|
|
|
|||
|
|
1. **维护成本**:dockershim 是 kubelet 内部的一层额外抽象,增加代码复杂度
|
|||
|
|
2. **性能开销**:kubelet → dockershim → Docker daemon → containerd,调用链过长
|
|||
|
|
3. **Docker 的「额外功能」**:Docker Engine 包含很多 K8s 不需要的组件(如 Docker Swarm、docker build 等),但作为运行时又必须加载整个 daemon
|
|||
|
|
4. **CRI 标准成熟**:containerd 和 CRI-O 直接实现 CRI,不再需要中间层
|
|||
|
|
|
|||
|
|
## 部署方式对比
|
|||
|
|
|
|||
|
|
| 方案 | 类型 | 节点数 | 生产就绪 | 学习成本 | 适用场景 |
|
|||
|
|
|------|------|--------|---------|---------|---------|
|
|||
|
|
| **minikube** | 单节点本地集群 | 1 | 否 | 低 | 本地开发和学习,支持 Addons |
|
|||
|
|
| **kind** | Docker 容器模拟 Node | 1-N | 否 | 低 | CI/CD 测试、多节点本地模拟 |
|
|||
|
|
| **kubeadm** | 生产级部署工具 | N | 是 | 中高 | 自建集群、对基础设施有完全控制 |
|
|||
|
|
| **EKS** | AWS 托管 | N | 是 | 中 | AWS 生态用户 |
|
|||
|
|
| **AKS** | Azure 托管 | N | 是 | 中 | Azure 生态用户 |
|
|||
|
|
| **GKE** | Google Cloud 托管 | N | 是 | 中 | GCP 生态用户,Autopilot 模式 |
|
|||
|
|
| **ACK** | 阿里云托管 | N | 是 | 中 | 国内云生态用户 |
|
|||
|
|
|
|||
|
|
> [!tip] 如何选择?
|
|||
|
|
> - **学习阶段**:minikube 或 kind(零成本,快速启动)
|
|||
|
|
> - **CI/CD**:kind(轻量,测试完即销毁)
|
|||
|
|
> - **小团队生产**:托管服务(减少运维负担)
|
|||
|
|
> - **大规模/定制需求**:kubeadm 或 Cluster API
|
|||
|
|
|
|||
|
|
## 常见陷阱与最佳实践
|
|||
|
|
|
|||
|
|
### 陷阱
|
|||
|
|
|
|||
|
|
1. **不设置 Resource Requests/Limits**
|
|||
|
|
```yaml
|
|||
|
|
# 反面教材:Pod 没有资源声明
|
|||
|
|
containers:
|
|||
|
|
- name: app
|
|||
|
|
image: my-app:v1
|
|||
|
|
```
|
|||
|
|
后果:调度器无法做出合理决策,某个 Pod 可能吃掉整个节点的资源,导致其他 Pod 被 OOM Kill。
|
|||
|
|
|
|||
|
|
2. **把所有东西塞进一个 Pod**
|
|||
|
|
Pod 中的容器应该有紧密的生命周期耦合。如果两个应用可以独立部署、独立扩缩容,它们应该在不同的 Pod 中。
|
|||
|
|
|
|||
|
|
3. **忽略 Liveness/Readiness Probe**
|
|||
|
|
没有 Probe 的 Pod,K8s 无法知道它是否真的健康。一个「Running」状态的 Pod,内部应用可能已经死锁。
|
|||
|
|
|
|||
|
|
4. **在生产环境使用 `latest` 标签**
|
|||
|
|
```yaml
|
|||
|
|
# 反面教材
|
|||
|
|
image: my-app:latest
|
|||
|
|
```
|
|||
|
|
你无法确定每次部署用的是哪个版本,回滚更是噩梦。始终使用明确的版本标签(如 `v1.2.3` 或 Git SHA)。
|
|||
|
|
|
|||
|
|
5. **直接操作集群资源而不使用 GitOps**
|
|||
|
|
手动 `kubectl apply` 的变更不可追溯、不可审计。使用 Helm / Kustomize + ArgoCD / Flux 进行声明式管理。
|
|||
|
|
|
|||
|
|
### 最佳实践
|
|||
|
|
|
|||
|
|
1. **声明式 > 命令式**:始终用 YAML 定义资源,而不是 `kubectl run`/`kubectl create` 的命令式操作。声明式配置可版本控制、可审计、可复现。
|
|||
|
|
|
|||
|
|
2. **合理使用 Namespace**:按团队或环境划分 Namespace,配合 ResourceQuota 和 LimitRange 进行资源管控。
|
|||
|
|
|
|||
|
|
3. **Pod 反亲和性确保高可用**:
|
|||
|
|
```yaml
|
|||
|
|
spec:
|
|||
|
|
affinity:
|
|||
|
|
podAntiAffinity:
|
|||
|
|
preferredDuringSchedulingIgnoredDuringExecution:
|
|||
|
|
- weight: 100
|
|||
|
|
podAffinityTerm:
|
|||
|
|
labelSelector:
|
|||
|
|
matchExpressions:
|
|||
|
|
- key: app
|
|||
|
|
operator: In
|
|||
|
|
values: ["my-app"]
|
|||
|
|
topologyKey: kubernetes.io/hostname
|
|||
|
|
```
|
|||
|
|
这确保同一应用的多个副本不会被调度到同一个 Node 上。
|
|||
|
|
|
|||
|
|
4. **使用 `imagePullPolicy` 控制镜像拉取策略**:
|
|||
|
|
- `Always`:总是拉取(适用于 `latest` 标签,但不推荐用 latest)
|
|||
|
|
- `IfNotPresent`:本地没有才拉取(推荐用于明确版本标签)
|
|||
|
|
- `Never`:从不拉取(仅用于本地开发)
|
|||
|
|
|
|||
|
|
5. **优雅终止(Graceful Shutdown)**:
|
|||
|
|
```yaml
|
|||
|
|
spec:
|
|||
|
|
terminationGracePeriodSeconds: 30
|
|||
|
|
containers:
|
|||
|
|
- name: app
|
|||
|
|
lifecycle:
|
|||
|
|
preStop:
|
|||
|
|
exec:
|
|||
|
|
command: ["/bin/sh", "-c", "sleep 5"]
|
|||
|
|
```
|
|||
|
|
`preStop` 中的 `sleep 5` 给 kube-proxy 更新规则留出时间,避免请求被发送到正在终止的 Pod。
|
|||
|
|
|
|||
|
|
6. **LimitRange 设置默认值**:
|
|||
|
|
```yaml
|
|||
|
|
apiVersion: v1
|
|||
|
|
kind: LimitRange
|
|||
|
|
metadata:
|
|||
|
|
name: default-limits
|
|||
|
|
namespace: team-backend
|
|||
|
|
spec:
|
|||
|
|
limits:
|
|||
|
|
- default:
|
|||
|
|
cpu: "500m"
|
|||
|
|
memory: "256Mi"
|
|||
|
|
defaultRequest:
|
|||
|
|
cpu: "100m"
|
|||
|
|
memory: "128Mi"
|
|||
|
|
type: Container
|
|||
|
|
```
|
|||
|
|
防止单个未配置资源限制的容器拖垮整个节点。
|
|||
|
|
|
|||
|
|
## 延伸阅读
|
|||
|
|
|
|||
|
|
- [Kubernetes 官方文档](https://kubernetes.io/zh-cn/docs/home/) — 最权威的参考
|
|||
|
|
- [Kubernetes 官方教程](https://kubernetes.io/zh-cn/docs/tutorials/) — 动手入门
|
|||
|
|
- [CNCF 技术雷达](https://www.cncf.io/tech-radars/) — 云原生技术选型参考
|
|||
|
|
- [Kelsey Hightower - Kubernetes The Hard Way](https://github.com/kelseyhightower/kubernetes-the-hard-way) — 手动从零搭建 K8s 集群,深入理解每个组件
|
|||
|
|
- 《Kubernetes in Action (第二版)》 — Marko Lukša 著,深入原理的经典教材
|
|||
|
|
|
|||
|
|
## 关联笔记
|
|||
|
|
|
|||
|
|
- [[k8s/k8s-workload-resources]] — Deployment、StatefulSet、DaemonSet 等工作负载详解
|
|||
|
|
- [[k8s/k8s-networking]] — Service、Ingress、NetworkPolicy 网络模型
|
|||
|
|
- [[k8s/k8s-storage]] — PV、PVC、StorageClass 存储体系
|