目录
- [1. Kubernetes 基础](#1. Kubernetes 基础 "#1-kubernetes-%E5%9F%BA%E7%A1%80")
- [1.1 Kubernetes 架构](#1.1 Kubernetes 架构 "#11-kubernetes-%E6%9E%B6%E6%9E%84")
- [1.2 集群搭建](#1.2 集群搭建 "#12-%E9%9B%86%E7%BE%A4%E6%90%AD%E5%BB%BA")
- [1.3 kubectl 命令](#1.3 kubectl 命令 "#13-kubectl-%E5%91%BD%E4%BB%A4")
- [2. 核心对象](#2. 核心对象 "#2-%E6%A0%B8%E5%BF%83%E5%AF%B9%E8%B1%A1")
- [2.1 Pod](#2.1 Pod "#21-pod")
- [2.2 Deployment](#2.2 Deployment "#22-deployment")
- [2.3 Service](#2.3 Service "#23-service")
- [2.4 Namespace](#2.4 Namespace "#24-namespace")
- [2.5 Label 和 Selector](#2.5 Label 和 Selector "#25-label-%E5%92%8C-selector")
- [3. 服务发现和负载均衡](#3. 服务发现和负载均衡 "#3-%E6%9C%8D%E5%8A%A1%E5%8F%91%E7%8E%B0%E5%92%8C%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1")
- [3.1 Ingress](#3.1 Ingress "#31-ingress")
- [3.2 网络](#3.2 网络 "#32-%E7%BD%91%E7%BB%9C")
- [4. 存储和配置](#4. 存储和配置 "#4-%E5%AD%98%E5%82%A8%E5%92%8C%E9%85%8D%E7%BD%AE")
- [4.1 ConfigMap 和 Secret](#4.1 ConfigMap 和 Secret "#41-configmap-%E5%92%8C-secret")
- [4.2 存储](#4.2 存储 "#42-%E5%AD%98%E5%82%A8")
1. Kubernetes 基础
1.1 Kubernetes 架构
Kubernetes(K8s)采用 Master-Worker 架构,将集群分为控制平面和数据平面。
Master 节点(控制平面)
Master 节点也是一台服务器(物理机或虚拟机),但它不运行业务容器,而是专门负责集群的管理和调度决策。可以理解为"指挥中心"------它决定哪个 Pod 跑在哪台 Worker 上、监控集群健康状态、处理所有 API 请求。生产环境通常部署 3 个 Master 实现高可用。
核心组件:
| 组件 | 职责 |
|---|---|
| kube-apiserver | 集群的统一入口,所有操作都通过 REST API 与它交互 |
| kube-scheduler | 监听未调度的 Pod,根据资源需求、亲和性等策略将 Pod 分配到合适的 Worker 节点 |
| kube-controller-manager | 运行各种控制器(Deployment、ReplicaSet、Node 控制器等),确保集群实际状态与期望状态一致 |
| etcd | 分布式键值存储,保存集群所有配置数据和状态信息,是集群的"数据库" |
请求链路 :
kubectl→kube-apiserver→ 写入etcd→kube-scheduler调度 →kubelet执行
Worker 节点(数据平面)
Worker 节点可以理解为一台服务器(物理机或虚拟机)。每个 Worker 节点就是集群中的一台机器,上面运行着实际的应用容器。一个集群通常由多个 Worker 节点组成,K8s 负责将 Pod 调度到这些"服务器"上运行。
一个 Worker 上可以运行多个 Pod。 Pod 是 K8s 最小的调度单位,每个 Pod 里包含一个或多个容器(通常是一个)。可以简单理解为:
- Pod ≈ Docker Container 的包装层:大多数情况下 1 个 Pod = 1 个容器,但 Pod 提供了共享网络和存储的能力,允许多个紧密耦合的容器在同一个 Pod 内协作(如 sidecar 模式)。
- Pod 有自己的 IP 地址,Pod 内的容器共享该 IP 和端口空间。
核心组件:
| 组件 | 职责 |
|---|---|
| kubelet | 每个 Worker 节点上的代理,负责管理 Pod 生命周期,向 apiserver 汇报节点状态 |
| kube-proxy | 每个节点上的网络代理。当外部请求访问 Service 时,kube-proxy 负责将流量合理转发到后端的某个 Pod 上(负载均衡)。它通过维护 iptables/ipvs 规则实现这一点 |
| Container Runtime | 容器运行时(如 containerd、CRI-O),负责拉取镜像、启动/停止容器 |
类比:Worker 节点 = 一台服务器,Pod = 服务器上运行的一个进程/应用,kube-proxy = 这台服务器上的"负载均衡器/路由器"
架构图
1.2 集群搭建
常见搭建方式对比
| 方式 | 适用场景 | 特点 |
|---|---|---|
| Kind | CI/CD、本地测试 | 用 Docker 容器模拟 K8s 节点,启动快 |
| kubeadm | 生产/准生产环境 | 官方工具,支持多节点集群搭建 |
| 云服务 | 生产环境 | EKS(AWS)、AKS(Azure)、GKE(Google)、ACK(阿里云),免运维 |
在 WSL 环境搭建(Kind)
--namevscontrol-plane的区别:
--name my-cluster→ 集群的名字,给整个集群起个名,方便管理多个集群时区分control-plane→ 节点的角色,表示这个节点充当 Master(管理节点)集群名是标识,节点角色决定职责,两者不是一个层级的概念。
bash
# 安装 Kind
curl -Lo ./kind https://kind.sigs.k8s.io/dl/latest/kind-linux-amd64
sudo install kind /usr/local/bin/kind
rm kind
# 创建集群(--name 是集群名称,默认只创建 1 个 control-plane 节点)
kind create cluster --name my-cluster
# 查看集群名列表
kind get clusters
# 输出:my-cluster
# 查看节点及角色
kubectl get nodes
# NAME STATUS ROLES AGE
# my-cluster-control-plane Ready control-plane 5m ← Master 节点
# (默认不带配置文件只有 1 个 Master,无 Worker)
# 创建多节点集群(使用配置文件)
# cluster = 整个集群(Master + Worker 的集合)
cat <<EOF | kind create cluster --config=-
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane # Master 节点(管理调度)
- role: worker # Worker 节点 1(跑业务 Pod)
- role: worker # Worker 节点 2(跑业务 Pod)
EOF
# 查看多节点集群的节点
kubectl get nodes
# NAME STATUS ROLES AGE
# my-cluster-control-plane Ready control-plane 5m ← Master
# my-cluster-worker Ready <none> 5m ← Worker 1
# my-cluster-worker2 Ready <none> 5m ← Worker 2
# 删除集群
kind delete cluster --name my-cluster
Kind 在 WSL2 中常见问题
创建集群时报错 could not find a log line that matches "Reached target .*Multi-User System.*|detected cgroup v1",通常是 cgroup 配置不兼容导致。
解决方法 :在 Windows 下创建/编辑 C:\Users\<你的用户名>\.wslconfig:
ini
[wsl2]
memory=4GB
processors=2
kernelCommandLine=systemd.unified_cgroup_hierarchy=1
然后在 Windows PowerShell 中重启 WSL:
powershell
wsl --shutdown
重新进入 WSL 后再创建集群即可。
1.3 kubectl 命令
安装和配置
kubeconfig 文件默认位于 ~/.kube/config,包含集群地址、认证信息和上下文。
| 命令 | 说明 |
|---|---|
kubectl config view |
查看 kubectl 配置 |
kubectl config current-context |
查看当前上下文(当前连接的集群) |
kubectl config use-context <context-name> |
切换上下文 |
kubectl config get-contexts |
查看所有上下文 |
kubectl config set-context --current --namespace=<ns> |
设置默认命名空间 |
常用命令
资源查看
| 命令 | 说明 |
|---|---|
kubectl get namespaces |
查看所有命名空间 |
kubectl get nodes |
查看节点 |
kubectl get nodes -o wide |
查看节点详细信息(IP、OS 等) |
kubectl get pods |
查看当前命名空间的 Pod |
kubectl get pods -n <namespace> |
查看指定命名空间的 Pod |
kubectl get pods -A |
查看所有命名空间的 Pod |
kubectl get pods -o wide |
显示 Pod 详细信息(节点、IP) |
kubectl get pods -w |
实时监听 Pod 变化 |
kubectl get svc |
查看 Service |
kubectl get deploy |
查看 Deployment |
kubectl get configmap |
查看 ConfigMap |
kubectl get ingress |
查看 Ingress |
kubectl get all -n <namespace> |
查看指定命名空间所有资源 |
kubectl describe pod <pod-name> |
查看 Pod 详情 |
kubectl describe node <node-name> |
查看 Node 详情 |
kubectl describe svc <service-name> |
查看 Service 详情 |
资源创建与管理
| 命令 | 说明 |
|---|---|
kubectl apply -f deployment.yaml |
通过 YAML 创建/更新资源 |
kubectl apply -f ./manifests/ |
应用目录下所有 YAML |
kubectl delete -f deployment.yaml |
删除 YAML 中定义的资源 |
kubectl delete pod <pod-name> |
删除指定 Pod |
kubectl delete deploy <deploy-name> |
删除指定 Deployment |
kubectl create deployment nginx --image=nginx:latest |
快速创建 Deployment(不推荐生产使用) |
kubectl expose deployment nginx --port=80 --type=NodePort |
暴露 Deployment 为 Service |
扩缩容
| 命令 | 说明 |
|---|---|
kubectl scale deployment <name> --replicas=3 |
手动扩缩容 |
kubectl get hpa |
查看 HPA(自动扩缩容) |
查看日志
| 命令 | 说明 |
|---|---|
kubectl logs <pod-name> |
查看 Pod 日志 |
kubectl logs -f <pod-name> |
实时跟踪日志(类似 tail -f) |
kubectl logs --tail=100 <pod-name> |
查看最近 100 行 |
kubectl logs --since=1h <pod-name> |
查看最近 1 小时的日志 |
kubectl logs <pod-name> -c <container-name> |
多容器 Pod 指定容器查看日志 |
kubectl logs <pod-name> --previous |
查看上一个已终止容器的日志(排查 CrashLoopBackOff) |
进入容器
| 命令 | 说明 |
|---|---|
kubectl exec -it <pod-name> -- /bin/bash |
进入容器的 bash |
kubectl exec -it <pod-name> -- /bin/sh |
进入容器的 sh(无 bash 时使用) |
kubectl exec -it <pod-name> -c <container-name> -- /bin/bash |
多容器 Pod 指定容器进入 |
kubectl exec <pod-name> -- cat /etc/nginx/nginx.conf |
执行单条命令(不进入交互模式) |
kubectl exec <pod-name> -- env |
查看环境变量 |
kubectl exec <pod-name> -- ls /app |
查看目录 |
kubectl cp <pod-name>:/path/file ./local-file |
从容器复制文件到本地 |
kubectl cp ./local-file <pod-name>:/path/file |
从本地复制文件到容器 |
常用排障命令速查
Pod 异常排查流程:
| 步骤 | 命令 | 说明 |
|---|---|---|
| 1 | kubectl get pods |
查看 Pod 状态 |
| 2 | kubectl describe pod <pod-name> |
查看事件和详情 |
| 3 | kubectl logs <pod-name> |
查看日志 |
| 4 | kubectl logs <pod-name> --previous |
查看上次崩溃日志 |
| 5 | kubectl exec -it <pod-name> -- /bin/sh |
进入容器排查 |
其他排障命令:
| 命令 | 说明 |
|---|---|
kubectl top nodes |
查看节点资源使用(需 metrics-server) |
kubectl top pods |
查看 Pod 资源使用(需 metrics-server) |
kubectl port-forward pod/<pod-name> 8080:80 |
端口转发 Pod(本地调试) |
kubectl port-forward svc/<svc-name> 8080:80 |
端口转发 Service(本地调试) |
2. 核心对象
2.1 Pod
2.1.1 Pod 概念和作用
Pod 是 Kubernetes 中最小的可部署单元。一个 Pod 封装了一个或多个容器(通常是一个),它们共享:
- 网络 :同一个 Pod 内的容器共享 IP 地址和端口空间,可通过
localhost互相通信 - 存储:可以挂载相同的 Volume,实现数据共享
- 生命周期:Pod 内的容器一起启动、一起销毁
为什么不直接用容器? Pod 提供了容器之上的抽象层,支持多容器协作(sidecar 模式)、共享网络/存储、统一调度等能力。
2.1.2 Pod 创建和管理
通过 YAML 创建 Pod
yaml
# nginx-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod # Pod 名称
labels:
app: nginx # 标签,用于 Service 选择
spec:
containers:
- name: nginx # 容器名称
image: nginx:1.25 # 镜像
ports:
- containerPort: 80 # 容器暴露的端口
| 操作 | 命令 |
|---|---|
| 创建 Pod | kubectl apply -f nginx-pod.yaml |
| 查看 Pod | kubectl get pods |
| 查看详情 | kubectl describe pod nginx-pod |
| 删除 Pod | kubectl delete pod nginx-pod |
| 删除(通过文件) | kubectl delete -f nginx-pod.yaml |
⚠️ 生产环境中不要直接创建 Pod,应使用 Deployment 来管理,这样才有副本管理、滚动更新等能力。
2.1.3 Pod 生命周期
Pod 从创建到销毁经历以下阶段:
| 阶段 | 说明 |
|---|---|
| Pending | Pod 已被创建,但容器还未启动(可能在拉取镜像或等待调度) |
| Running | Pod 已绑定到节点,所有容器已启动 |
| Succeeded | 所有容器正常执行完毕并退出(常见于 Job) |
| Failed | 至少一个容器以非零状态退出 |
| Unknown | 无法获取 Pod 状态(通常是与节点通信失败) |
常见异常状态:
| 状态 | 原因 | 排查方式 |
|---|---|---|
CrashLoopBackOff |
容器反复崩溃重启 | kubectl logs <pod> --previous |
ImagePullBackOff |
镜像拉取失败 | 检查镜像名、tag、仓库权限 |
Pending |
无可用节点或资源不足 | kubectl describe pod <pod> 查看 Events |
OOMKilled |
内存超出限制 | 调整 resources.limits.memory |
2.1.4 多容器 Pod
一个 Pod 可以包含多个容器,常见模式:
| 模式 | 说明 | 示例 |
|---|---|---|
| Sidecar | 辅助容器增强主容器功能 | 日志收集器、代理 |
| Ambassador | 代理容器处理外部通信 | 数据库连接代理 |
| Adapter | 转换容器输出格式 | 日志格式转换 |
yaml
# 多容器 Pod 示例(Sidecar 模式)
apiVersion: v1
kind: Pod
metadata:
name: multi-container-pod
spec:
containers:
- name: app # 主容器:业务应用
image: my-app:1.0
ports:
- containerPort: 8080
volumeMounts:
- name: shared-logs
mountPath: /var/log/app
- name: log-collector # Sidecar:日志收集
image: fluentd:latest
volumeMounts:
- name: shared-logs
mountPath: /var/log/app
volumes:
- name: shared-logs # 共享 Volume(见下方说明)
emptyDir: {}
Volume 说明:
shared-logs→ Volume 的名称(自己定义的,用来在容器中引用)emptyDir→ Volume 的类型(决定数据存在哪里、生命周期多长)
emptyDir类型会在 Worker 节点的磁盘上 创建一个临时目录,不是在 Pod 内部。
特性 说明 存储位置 Worker 节点的文件系统上(如 /var/lib/kubelet/pods/<pod-id>/volumes/)生命周期 与 Pod 相同,Pod 删除时目录也会被清除 容器重启 Pod 内容器重启时数据不会丢失(因为 Volume 在节点上,不在容器内) 用途 同一 Pod 内多个容器之间共享数据(如上例中主容器写日志,Sidecar 读日志) 其他 Volume 类型对比:
类型 存储位置 生命周期 emptyDirWorker 节点磁盘 随 Pod 销毁 hostPathWorker 节点指定路径 独立于 Pod,节点存在就在。⚠️ Pod 调度到其他节点时无法访问原数据 persistentVolumeClaim外部存储(云盘、NFS 等) 独立于 Pod 和节点
2.1.5 Init 容器
Init 容器在主容器启动之前运行,用于初始化工作。特点:
- 按顺序逐个执行,前一个成功后才启动下一个
- 全部成功后,主容器才会启动
- 失败则 Pod 重启(遵循 restartPolicy)
常见用途:等待依赖服务就绪、初始化配置文件、数据库迁移
yaml
apiVersion: v1
kind: Pod
metadata:
name: app-with-init
spec:
initContainers:
- name: wait-for-db # 等待数据库就绪
image: busybox:1.36
command: ['sh', '-c', 'until nc -z mysql-service 3306; do echo waiting for db; sleep 2; done']
- name: init-config # 初始化配置
image: busybox:1.36
command: ['sh', '-c', 'cp /config-template/* /app/config/']
volumeMounts:
- name: config
mountPath: /app/config
containers:
- name: app # 主容器(Init 全部成功后才启动)
image: my-app:1.0
volumeMounts:
- name: config
mountPath: /app/config
volumes:
- name: config
emptyDir: {}
2.2 Deployment
2.2.1 概念和作用
Deployment 是管理 Pod 的高层控制器,提供:
- 副本管理:确保指定数量的 Pod 始终运行
- 滚动更新:无停机更新应用版本
- 回滚:一键回退到之前的版本
- 自愈:Pod 崩溃后自动重建
层级关系:Deployment → ReplicaSet → Pod。Deployment 管理 ReplicaSet,ReplicaSet 管理 Pod。
运行位置 :Deployment 本身是一份"期望状态"声明,存储在 Master 的 etcd 中,由
kube-controller-manager管理。真正跑在 Worker 上的是 Deployment 创建出来的 Pod。例如:Deployment 告诉 Master:"我要 3 个 nginx Pod",Master 的 Scheduler 负责把这 3 个 Pod 分配到不同的 Worker 上运行。
2.2.2 创建 Deployment
yaml
# nginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3 # 副本数
selector:
matchLabels:
app: nginx # 选择器,匹配 Pod 标签
template: # Pod 模板
metadata:
labels:
app: nginx # Pod 标签(必须与 selector 匹配)
spec:
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
resources:
requests: # 最低资源需求
cpu: 100m
memory: 128Mi
limits: # 资源上限
cpu: 500m
memory: 256Mi
| 操作 | 命令 |
|---|---|
| 创建 | kubectl apply -f nginx-deployment.yaml |
| 查看 | kubectl get deploy |
| 查看详情 | kubectl describe deploy nginx-deployment |
| 查看管理的 Pod | kubectl get pods -l app=nginx(app=nginx 对应 YAML 中 labels 定义的标签) |
| 删除 | kubectl delete deploy nginx-deployment |
2.2.3 滚动更新和回滚
滚动更新
bash
# 方式1:修改 YAML 中的 image 后重新 apply
kubectl apply -f nginx-deployment.yaml
# 方式2:直接命令行更新镜像
# 格式:kubectl set image deployment/<deployment名称> <容器名称>=<新镜像>
# nginx-deployment → metadata.name,nginx → containers[].name(不是 label)
kubectl set image deployment/nginx-deployment nginx=nginx:1.26
# 查看更新进度
kubectl rollout status deployment/nginx-deployment
更新策略(在 YAML 中配置):
yaml
spec:
strategy:
type: RollingUpdate # 滚动更新(默认)
rollingUpdate:
maxSurge: 1 # 更新时最多多出 1 个 Pod
maxUnavailable: 0 # 更新时不允许不可用的 Pod
回滚
| 操作 | 命令 |
|---|---|
| 查看更新历史 | kubectl rollout history deployment/nginx-deployment |
| 回滚到上一个版本 | kubectl rollout undo deployment/nginx-deployment |
| 回滚到指定版本 | kubectl rollout undo deployment/nginx-deployment --to-revision=2 |
| 暂停更新 | kubectl rollout pause deployment/nginx-deployment |
| 恢复更新 | kubectl rollout resume deployment/nginx-deployment |
2.2.4 副本数管理
副本数(replicas)即 Pod 的数量 。replicas: 3 表示 K8s 始终维持 3 个相同的 Pod 运行。扩缩容就是动态调整这个数字。
| 操作 | 命令 |
|---|---|
| 手动扩缩容 | kubectl scale deployment nginx-deployment --replicas=5(将 Pod 数量调整为 5) |
| 查看副本状态 | kubectl get deploy nginx-deployment |
输出示例:
vbnet
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deployment 3/3 3 3 10m
^^^
就绪/期望 副本数
2.3 Service
2.3.1 概念和作用
Service 为一组 Pod 提供稳定的网络访问入口。解决的问题:
- Pod 的 IP 是动态分配的,Pod 重建后 IP 会变
- 需要在多个 Pod 之间做负载均衡
- 需要一个固定的域名/IP 供其他服务访问
类比 :Pod 是后端服务器,Service 就是前面的负载均衡器,提供一个稳定的地址。
Deployment 与 Service 的关系
Deployment 和 Service 是独立对象 ,不是一对一绑定。它们通过 Label 关联:
| 关系 | 说明 |
|---|---|
| 1 Deployment → 0 Service | 内部服务不需要被其他服务访问时 |
| 1 Deployment → 1 Service | 最常见,一个应用一个访问入口 |
| 1 Deployment → 多个 Service | 同一应用暴露不同端口/协议(如 HTTP + gRPC) |
| 多个 Deployment → 1 Service | 少见,但 Service 只要 label 匹配就能指向多组 Pod |
Deployment 创建 Pod 跑在已有的 Worker 上,不会新建 Worker。Pod 由
kube-scheduler自动调度到合适的节点。
2.3.2 Service 类型
| 类型 | 说明 | 访问范围 |
|---|---|---|
| ClusterIP | 默认类型,分配一个集群内部 IP | 仅集群内部访问 |
| NodePort | 在每个节点上开放一个固定端口(30000-32767) | 集群外部可通过 节点IP:NodePort 访问 |
| LoadBalancer | 在 NodePort 基础上,创建云厂商的外部负载均衡器 | 外部访问(需云环境支持) |
ClusterIP(默认)
yaml
# cluster-ip-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
type: ClusterIP # 默认,可省略
selector:
app: nginx # 选择标签为 app=nginx 的 Pod
ports:
- port: 80 # Service 端口
targetPort: 80 # Pod 容器端口
NodePort
yaml
# node-port-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-nodeport
spec:
type: NodePort
selector:
app: nginx
ports:
- port: 80 # Service 端口(集群内访问)
targetPort: 80 # Pod 容器端口
nodePort: 30080 # 节点端口(外部访问),不指定则自动分配
LoadBalancer
yaml
# load-balancer-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-lb
spec:
type: LoadBalancer
selector:
app: nginx
ports:
- port: 80
targetPort: 80
2.3.3 集群内访问 Service(服务发现)
完整示例:Deployment + Service
yaml
# nginx-app.yaml(一个文件中包含 Deployment 和 Service,用 --- 分隔)
# ===== Deployment:创建 3 个 nginx Pod =====
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment # Deployment 名称
namespace: dev # 自定义命名空间(需提前创建:kubectl create namespace dev)
spec:
replicas: 3 # 创建 3 个 Pod
selector:
matchLabels:
app: nginx # ─┐ 选择器,匹配下方 Pod 的 labels
template: # │
metadata: # │
labels: # │
app: nginx # ─┘ Pod 标签(与 selector 和 Service 关联的纽带)
spec:
containers:
- name: nginx # 容器名称
image: nginx:1.25
ports:
- containerPort: 80 # 容器监听 80 端口
---
# ===== Service:为上面的 Pod 提供统一访问入口 =====
apiVersion: v1
kind: Service
metadata:
name: nginx-service # Service 名称(集群内通过此名称访问)
namespace: dev # 与 Deployment 同一命名空间
spec:
type: ClusterIP # 仅集群内部可访问
selector:
app: nginx # ← 通过此标签关联上面 Deployment 创建的 Pod
ports:
- port: 80 # Service 暴露的端口
targetPort: 80 # 转发到 Pod 的 80 端口
| 操作 | 命令 |
|---|---|
| 创建命名空间 | kubectl create namespace dev |
| 部署 | kubectl apply -f nginx-app.yaml |
| 查看 Deployment | kubectl get deploy -n dev |
| 查看关联的 Pod | kubectl get pods -l app=nginx -n dev |
| 查看 Service | kubectl get svc nginx-service -n dev |
| 查看 Service 背后的 Pod IP | kubectl get endpoints nginx-service -n dev |
| 集群内访问测试(同命名空间) | kubectl run test -n dev --rm -it --image=busybox -- wget -qO- http://nginx-service |
| 集群内访问测试(跨命名空间) | kubectl run test --rm -it --image=busybox -- wget -qO- http://nginx-service.dev.svc.cluster.local |
关联关系 :Deployment 的
selector.matchLabels和 Service 的selector都通过app: nginx这个标签找到同一组 Pod。
集群内的 Pod 可以通过以下方式访问 Service:
| 方式 | 格式 | 示例 |
|---|---|---|
| 同命名空间 | <service-name> |
curl http://nginx-service |
| 跨命名空间 | <service-name>.<namespace>.svc.cluster.local |
curl http://nginx-service.dev.svc.cluster.local |
以上方 nginx-app.yaml 为例,各字段对应关系:
完整域名:
nginx-service.dev.svc.cluster.local
nginx-service→ Service 的metadata.namedev→ Service 的metadata.namespacesvc.cluster.local→ K8s 固定后缀svc.cluster.local→ K8s 固定后缀
K8s 自带 DNS 服务(CoreDNS),自动为每个 Service 创建 DNS 记录。
2.3.4 Endpoints
Endpoints 是 Service 背后实际对应的 Pod IP 列表。Service 通过 selector 匹配 Pod,自动维护 Endpoints。
沿用上方 nginx-app.yaml 部署后,查看 Endpoints:
bash
# 查看 nginx-service 的 Endpoints(nginx-service 即 YAML 中 metadata.name)
kubectl get endpoints nginx-service
# 输出示例:
# NAME ENDPOINTS AGE
# nginx-service 10.244.1.5:80,10.244.2.3:80,10.244.3.7:80 5m
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
# 三个 Pod 的实际 IP:端口
当 Pod 被创建/销毁时,Endpoints 会自动更新,流量只会转发到健康的 Pod。
2.4 Namespace
2.4.1 概念和作用
Namespace(命名空间)用于将集群资源划分为多个逻辑隔离的空间。
常见用途:
- 按环境隔离:
dev、staging、production - 按团队隔离:
team-a、team-b - 按项目隔离:
project-x、project-y
K8s 默认的 Namespace:
| Namespace | 说明 |
|---|---|
default |
未指定命名空间时的默认空间 |
kube-system |
K8s 系统组件(apiserver、scheduler 等) |
kube-public |
所有用户可读的公共资源 |
kube-node-lease |
节点心跳相关 |
沿用上方 nginx-app.yaml(已指定 namespace: dev)进行验证:
| 操作 | 命令 |
|---|---|
| 查看所有命名空间 | kubectl get namespaces |
| 创建命名空间 | kubectl create namespace dev |
| 部署资源到 dev | kubectl apply -f nginx-app.yaml(YAML 中已指定 namespace: dev) |
| 查看 dev 命名空间的 Pod | kubectl get pods -n dev |
| 查看 dev 命名空间所有资源 | kubectl get all -n dev |
| 设置默认命名空间(免 -n) | kubectl config set-context --current --namespace=dev |
| 删除命名空间(会删除其下所有资源) | kubectl delete namespace dev |
2.4.2 资源隔离
Namespace 提供的是逻辑隔离,不同命名空间的资源互不可见(同类型下名称可重复)。
如上方 nginx-app.yaml 所示,在 YAML 中通过 metadata.namespace: dev 指定资源归属的命名空间。
⚠️ 注意:
- Namespace 是逻辑隔离,不是网络隔离。默认情况下,不同 Namespace 的 Pod 之间可以互相通信
- 需要网络隔离时,需配合 NetworkPolicy 使用
- Node 和 PersistentVolume 是集群级资源,不属于任何 Namespace
2.5 Label 和 Selector
Label(标签)
Label 是附加到 K8s 对象上的键值对,用于标识和分类资源。
yaml
metadata:
labels:
app: nginx # 应用名
env: production # 环境
tier: frontend # 层级
version: v1.0 # 版本
| 操作 | 命令 |
|---|---|
| 给 Pod 添加标签 | kubectl label pod nginx-pod env=dev |
| 修改已有标签 | kubectl label pod nginx-pod env=staging --overwrite |
| 删除标签 | kubectl label pod nginx-pod env- |
| 查看标签 | kubectl get pods --show-labels |
Selector(选择器)
Selector 用于根据 Label 筛选资源。是 Service、Deployment 等关联 Pod 的核心机制。
命令行使用:
| 操作 | 命令 |
|---|---|
| 等值筛选 | kubectl get pods -l app=nginx |
| 多条件筛选(AND) | kubectl get pods -l app=nginx,env=dev |
| 不等于 | kubectl get pods -l env!=production |
| 集合筛选 | kubectl get pods -l 'env in (dev,staging)' |
YAML 中使用(Deployment 通过 selector 关联 Pod):
yaml
# Deployment 的 selector 与 Pod 的 labels 必须匹配
spec:
selector:
matchLabels:
app: nginx # ─┐
template: # │ 必须一致
metadata: # │
labels: # │
app: nginx # ─┘
Service 通过 selector 关联 Pod:
yaml
# Service 选择所有带 app=nginx 标签的 Pod
spec:
selector:
app: nginx # 匹配 Pod 的 labels
ports:
- port: 80
Label + Selector 是 K8s 的核心关联机制:Deployment 通过它管理 Pod,Service 通过它转发流量,NetworkPolicy 通过它控制网络策略。
3. 服务发现和负载均衡
3.1 Ingress
3.1.1 概念和作用
在第 2 章中,Service(NodePort/LoadBalancer)可以将流量从集群外部引入,但存在局限:
| 方式 | 局限 |
|---|---|
| NodePort | 端口范围有限(30000-32767),每个 Service 占一个端口,不支持域名 |
| LoadBalancer | 每个 Service 创建一个云负载均衡器,成本高 |
Ingress 解决的问题:用一个统一入口管理所有外部访问,支持:
- 域名路由 :
api.example.com→ Service A,web.example.com→ Service B - 路径路由 :
example.com/api→ Service A,example.com/web→ Service B - TLS/HTTPS:统一管理 SSL 证书
- 负载均衡:一个入口分发到多个后端 Service
类比:Service 是每个应用的"后门",Ingress 是整个集群的"前台大门 + 路由器"。
3.1.2 Ingress Controller
Ingress 本身只是一份路由规则的定义 (YAML),不会自动生效。需要部署 Ingress Controller 来实际执行这些规则。
| Ingress Controller | 说明 |
|---|---|
| Nginx Ingress Controller | 最常用,基于 Nginx 反向代理 |
| Traefik | 轻量,自动发现服务,适合微服务 |
| HAProxy | 高性能,企业级 |
| 云厂商 | AWS ALB、GCE、阿里云 SLB,与云服务深度集成 |
关系:Ingress(规则) + Ingress Controller(执行者) = 完整的外部访问方案。类似 Deployment(规则)需要 kubelet(执行者)来创建 Pod。
在 Kind 中安装 Nginx Ingress Controller:
bash
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml
# 等待 Controller 就绪
kubectl wait --namespace ingress-nginx \
--for=condition=ready pod \
--selector=app.kubernetes.io/component=controller \
--timeout=90s
3.1.3 Ingress 规则和配置
沿用上方 nginx-app.yaml(Deployment + Service 部署在 dev 命名空间),为其创建 Ingress:
yaml
# nginx-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-ingress
namespace: dev # 与 Service 同一命名空间
annotations:
nginx.ingress.kubernetes.io/rewrite-target: / # URL 重写规则
spec:
ingressClassName: nginx # 指定使用哪个 Ingress Controller
rules:
- host: app.example.com # 域名(匹配请求的 Host 头)
http:
paths:
- path: / # 路径匹配
pathType: Prefix # Prefix=前缀匹配,Exact=精确匹配
backend:
service:
name: nginx-service # ← 转发到的 Service(metadata.name)
port:
number: 80 # ← Service 的端口
nginx-ingress.yaml是独立文件,与nginx-app.yaml分开执行即可。只要满足:
- Ingress Controller 已安装
nginx-app.yaml已部署(Deployment + Service 在dev命名空间运行中)- Ingress 的
namespace与 Service 相同,service.name与 Service 的metadata.name一致
| 操作 | 命令 |
|---|---|
| 创建 Ingress | kubectl apply -f nginx-ingress.yaml |
| 查看 Ingress | kubectl get ingress -n dev |
| 查看详情 | kubectl describe ingress nginx-ingress -n dev |
| 删除 Ingress | kubectl delete ingress nginx-ingress -n dev |
本地访问 Ingress(Kind/WSL 环境)
Kind 没有云负载均衡器,Ingress Controller 的 EXTERNAL-IP 会一直显示 <pending>。通过以下步骤在本地访问:
步骤一:配置 hosts
在 WSL 中添加域名映射(Windows 的 hosts 对 WSL 内的 curl 无效):
bash
echo "127.0.0.1 app.example.com" | sudo tee -a /etc/hosts
步骤二:端口转发
bash
# 方式1:使用非特权端口(无需 sudo)
kubectl port-forward -n ingress-nginx svc/ingress-nginx-controller 8080:80
# 方式2:使用 80 端口(需指定 kubeconfig,因为 sudo 下 root 的 kubeconfig 不同)
sudo kubectl port-forward -n ingress-nginx svc/ingress-nginx-controller 80:80 --kubeconfig=$HOME/.kube/config
步骤三:测试访问(新开一个终端)
bash
# 方式1 对应
curl http://app.example.com:8080
# 方式2 对应
curl http://app.example.com
⚠️
port-forward是前台进程,终端关闭则停止。生产环境不会用此方式,而是通过云负载均衡器或 NodePort 暴露。
3.1.4 域名路由和路径路由
域名路由(多个域名 → 不同 Service)
yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: multi-host-ingress
namespace: dev
spec:
ingressClassName: nginx
rules:
- host: api.example.com # 域名 A
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-service # → 转发到 api-service
port:
number: 8080
- host: web.example.com # 域名 B
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-service # → 转发到 web-service
port:
number: 80
路径路由(同一域名,不同路径 → 不同 Service)
yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: path-based-ingress
namespace: dev
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
ingressClassName: nginx
rules:
- host: example.com
http:
paths:
- path: /api(/|$)(.*) # example.com/api/* → api-service
pathType: ImplementationSpecific
backend:
service:
name: api-service
port:
number: 8080
- path: /web(/|$)(.*) # example.com/web/* → web-service
pathType: ImplementationSpecific
backend:
service:
name: web-service
port:
number: 80
TLS/HTTPS 配置
yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: tls-ingress
namespace: dev
spec:
ingressClassName: nginx
tls:
- hosts:
- app.example.com
secretName: app-tls-secret # 存储证书的 Secret 名称
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx-service
port:
number: 80
证书通过
kubectl create secret tls app-tls-secret --cert=tls.crt --key=tls.key -n dev创建。
访问流程总结:
用户请求 → Ingress Controller(Nginx) → 根据域名/路径匹配 Ingress 规则 → 转发到 Service → kube-proxy 负载均衡 → Pod
3.2 网络
3.2.1 Kubernetes 网络模型
K8s 网络遵循三条基本原则:
| 原则 | 说明 |
|---|---|
| Pod 间通信 | 所有 Pod 可以直接通过 IP 互相通信,无需 NAT |
| Pod 与 Service | Pod 通过 Service 的 ClusterIP 或 DNS 名称访问其他服务 |
| 外部与集群 | 通过 NodePort、LoadBalancer 或 Ingress 访问集群内服务 |
四种通信场景:
| 场景 | 实现方式 |
|---|---|
| 同一 Pod 内容器间 | 通过 localhost(共享网络命名空间) |
| 同一节点的 Pod 间 | 通过虚拟网桥(如 cbr0/cni0)直接通信 |
| 跨节点的 Pod 间 | 通过 CNI 插件建立的覆盖网络(Overlay Network) |
| 外部访问 Pod | 通过 Service(NodePort/LoadBalancer)或 Ingress |
3.2.2 CNI(Container Network Interface)
CNI 是 K8s 的网络插件规范,负责为 Pod 分配 IP、建立跨节点通信。K8s 本身不实现网络,由 CNI 插件完成。
| CNI 插件 | 特点 | 适用场景 |
|---|---|---|
| Flannel | 简单轻量,配置少 | 学习、小型集群 |
| Calico | 支持网络策略(NetworkPolicy),性能好 | 生产环境,需要网络隔离 |
| Cilium | 基于 eBPF,高性能,可观测性强 | 大规模生产,安全要求高 |
| Weave | 自动发现,加密通信 | 多云环境 |
Kind 和 Minikube 默认自带 CNI 插件(kindnet / bridge),学习阶段无需额外安装。
CNI 工作流程:
arduino
Pod 创建 → kubelet 调用 CNI 插件 → 分配 IP → 配置网络(veth pair、路由规则) → Pod 可通信
3.2.3 Service Mesh
Service Mesh(服务网格)是在 K8s 网络之上的应用层网络方案,用于管理微服务间的通信。
为什么需要 Service Mesh? K8s 的 Service + kube-proxy 只提供基础的 L4(TCP)负载均衡,缺少:
| 需求 | Service Mesh 提供的能力 |
|---|---|
| 流量管理 | 灰度发布、A/B 测试、流量镜像、熔断、重试 |
| 可观测性 | 分布式链路追踪、指标采集、访问日志 |
| 安全 | 服务间 mTLS 加密、访问控制 |
| 策略 | 限流、超时配置、故障注入 |
主流 Service Mesh:
| 方案 | 说明 |
|---|---|
| Istio | 功能最全面,社区最大,学习曲线较陡 |
| Linkerd | 轻量级,性能好,易于上手 |
| Consul Connect | HashiCorp 出品,与 Consul 服务发现集成 |
Istio 架构简述:
Envoy] B[Pod B] --- SB[Sidecar Proxy
Envoy] SA <-->|加密通信| SB end subgraph Control Plane["控制平面"] IS[istiod
配置管理/证书/服务发现] end IS -->|下发配置| SA IS -->|下发配置| SB
Service Mesh 通过在每个 Pod 旁注入 Sidecar 代理(如 Envoy),拦截所有进出流量,实现上述能力。业务代码无需修改。
⚠️ Service Mesh 属于进阶内容,小型项目或学习阶段不必使用。
4. 存储和配置
4.1 ConfigMap 和 Secret
ConfigMap
ConfigMap 用于存储非敏感的配置数据(键值对或配置文件),与应用代码解耦。
创建 ConfigMap
bash
# 方式1:命令行创建
kubectl create configmap app-config \
--from-literal=DB_HOST=mysql-service \
--from-literal=DB_PORT=3306 \
-n dev
# 方式2:从文件创建
kubectl create configmap nginx-config \
--from-file=nginx.conf \
-n dev
yaml
# 方式3:YAML 创建
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: dev
data:
DB_HOST: mysql-service # 键值对形式
DB_PORT: "3306"
app.properties: | # 文件形式(多行内容)
server.port=8080
spring.datasource.url=jdbc:mysql://mysql-service:3306/mydb
使用 ConfigMap
方式1:注入为环境变量
yaml
apiVersion: v1
kind: Pod
metadata:
name: app-pod
namespace: dev
spec:
containers:
- name: app
image: my-app:1.0
env:
- name: DB_HOST # 容器中的环境变量名
valueFrom:
configMapKeyRef:
name: app-config # ConfigMap 名称
key: DB_HOST # ConfigMap 中的 key
- name: DB_PORT
valueFrom:
configMapKeyRef:
name: app-config
key: DB_PORT
方式2:挂载为配置文件
yaml
apiVersion: v1
kind: Pod
metadata:
name: app-pod
namespace: dev
spec:
containers:
- name: app
image: my-app:1.0
volumeMounts:
- name: config-volume
mountPath: /app/config # 挂载到容器内的路径
volumes:
- name: config-volume
configMap:
name: app-config # ConfigMap 名称
# ConfigMap 的每个 key 变成 /app/config/ 下的一个文件
| 操作 | 命令 |
|---|---|
| 查看 ConfigMap | kubectl get configmap -n dev |
| 查看内容 | kubectl describe configmap app-config -n dev |
| 删除 | kubectl delete configmap app-config -n dev |
Secret
Secret 用于存储敏感数据(密码、Token、证书等),与 ConfigMap 类似但数据以 Base64 编码存储。
创建 Secret
bash
# 方式1:命令行创建
kubectl create secret generic db-secret \
--from-literal=DB_USER=admin \
--from-literal=DB_PASSWORD=p@ssw0rd \
-n dev
yaml
# 方式2:YAML 创建(值需 Base64 编码)
apiVersion: v1
kind: Secret
metadata:
name: db-secret
namespace: dev
type: Opaque
data:
DB_USER: YWRtaW4= # echo -n 'admin' | base64
DB_PASSWORD: cEBzc3cwcmQ= # echo -n 'p@ssw0rd' | base64
使用 Secret
yaml
# 注入为环境变量(与 ConfigMap 类似,将 configMapKeyRef 换为 secretKeyRef)
apiVersion: v1
kind: Pod
metadata:
name: app-pod
namespace: dev
spec:
containers:
- name: app
image: my-app:1.0
env:
- name: DB_USER
valueFrom:
secretKeyRef:
name: db-secret # Secret 名称
key: DB_USER
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: DB_PASSWORD
| 操作 | 命令 |
|---|---|
| 查看 Secret | kubectl get secret -n dev |
| 查看内容(Base64 编码) | kubectl get secret db-secret -n dev -o yaml |
| 解码查看 | `kubectl get secret db-secret -n dev -o jsonpath='{.data.DB_PASSWORD}' |
⚠️ Secret 不是真正的加密,Base64 只是编码,任何人拿到都能解码。生产环境建议配合 RBAC 限制访问权限,或使用外部密钥管理(如 Vault、AWS Secrets Manager)。
ConfigMap vs Secret 对比
| 对比项 | ConfigMap | Secret |
|---|---|---|
| 用途 | 非敏感配置(数据库地址、端口等) | 敏感数据(密码、Token、证书) |
| 存储方式 | 明文 | Base64 编码 |
| 大小限制 | 1MB | 1MB |
| 使用方式 | 环境变量 / 挂载文件 | 环境变量 / 挂载文件 |
| 引用方式 | configMapKeyRef |
secretKeyRef |
4.2 存储
Volume(卷)
在 2.1.4 多容器 Pod 中已介绍过 emptyDir。Volume 是 K8s 中为 Pod 提供存储的机制,生命周期与 Pod 绑定或独立于 Pod。
常用 Volume 类型回顾:
| 类型 | 存储位置 | 生命周期 | 适用场景 |
|---|---|---|---|
emptyDir |
Worker 节点磁盘 | 随 Pod 销毁 | Pod 内容器间共享临时数据 |
hostPath |
Worker 节点指定路径 | 节点存在就在 | 访问节点文件(日志、Docker socket) |
configMap |
etcd(通过 ConfigMap) | 随 ConfigMap 存在 | 挂载配置文件 |
secret |
etcd(通过 Secret) | 随 Secret 存在 | 挂载敏感配置 |
persistentVolumeClaim |
外部存储 | 独立于 Pod 和节点 | 数据库、文件存储等持久化需求 |
PersistentVolume(PV)和 PersistentVolumeClaim(PVC)
emptyDir 和 hostPath 的数据会随 Pod/节点丢失,不适合需要持久化存储的场景(如数据库)。
K8s 通过 PV + PVC 机制实现持久化存储:
| 概念 | 说明 | 类比 |
|---|---|---|
| PersistentVolume (PV) | 集群中一块预先准备好的存储资源 | 硬盘 |
| PersistentVolumeClaim (PVC) | Pod 对存储的申请("我需要 10Gi 空间") | 购买硬盘的订单 |
流程:管理员创建 PV → 开发者创建 PVC 申请存储 → K8s 自动将 PVC 绑定到匹配的 PV → Pod 挂载 PVC 使用
创建 PV
yaml
# pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: my-pv # PV 是集群级资源,不属于任何 namespace
spec:
capacity:
storage: 10Gi # 存储容量
accessModes:
- ReadWriteOnce # 访问模式(见下方说明)
persistentVolumeReclaimPolicy: Retain # 回收策略(见下方说明)
storageClassName: manual # 存储类名(与 PVC 匹配)
hostPath:
path: /data/my-pv # 存储在节点上的路径(仅测试用)
访问模式:
| 模式 | 简写 | 说明 |
|---|---|---|
| ReadWriteOnce | RWO | 单节点读写 |
| ReadOnlyMany | ROX | 多节点只读 |
| ReadWriteMany | RWX | 多节点读写(需存储支持,如 NFS) |
回收策略:
| 策略 | 说明 |
|---|---|
| Retain | PVC 删除后 PV 保留数据,需手动清理 |
| Delete | PVC 删除后 PV 和存储一起删除(云盘常用) |
| Recycle | 清除数据后重新可用(已废弃) |
创建 PVC
yaml
# pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-pvc
namespace: dev
spec:
accessModes:
- ReadWriteOnce # 需与 PV 匹配
resources:
requests:
storage: 5Gi # 申请 5Gi(PV 需 ≥ 5Gi)
storageClassName: manual # 需与 PV 的 storageClassName 匹配
在 Pod 中使用 PVC
yaml
apiVersion: v1
kind: Pod
metadata:
name: db-pod
namespace: dev
spec:
containers:
- name: mysql
image: mysql:8.0
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: DB_PASSWORD
volumeMounts:
- name: db-storage
mountPath: /var/lib/mysql # MySQL 数据目录
volumes:
- name: db-storage
persistentVolumeClaim:
claimName: my-pvc # 引用 PVC 名称
| 操作 | 命令 |
|---|---|
| 创建 PV | kubectl apply -f pv.yaml |
| 创建 PVC | kubectl apply -f pvc.yaml |
| 查看 PV | kubectl get pv |
| 查看 PVC | kubectl get pvc -n dev |
| 查看绑定状态 | PV 和 PVC 的 STATUS 都显示 Bound 表示绑定成功 |
StorageClass
手动创建 PV 太繁琐。StorageClass 实现动态供给:PVC 创建时自动创建对应的 PV。
yaml
# storageclass.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-storage
provisioner: kubernetes.io/aws-ebs # 存储供应商(不同环境不同)
parameters:
type: gp3 # 存储类型参数
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer # 等 Pod 调度后再创建 PV
使用 StorageClass 的 PVC(无需手动创建 PV):
yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: auto-pvc
namespace: dev
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
storageClassName: fast-storage # 引用 StorageClass,自动创建 PV
流程对比:
- 手动:管理员创建 PV → 开发者创建 PVC → 绑定
- 动态(StorageClass):开发者创建 PVC(指定 StorageClass)→ K8s 自动创建 PV → 自动绑定
| 操作 | 命令 |
|---|---|
| 查看 StorageClass | kubectl get storageclass |
| 查看默认 StorageClass | kubectl get sc(带 (default) 标记的) |
常见 provisioner:
| 环境 | provisioner |
|---|---|
| AWS EBS | kubernetes.io/aws-ebs 或 ebs.csi.aws.com |
| GCE PD | kubernetes.io/gce-pd |
| 阿里云 | diskplugin.csi.alibabacloud.com |
| NFS | nfs-subdir-external-provisioner |
| 本地测试(Kind) | rancher.io/local-path(Kind 默认自带) |