基于wsl2安装k8s
kubeasz版本选用最新的release版本,因为你懂的原因,docker镜像仓库已经需要vpn代理,所以说的不需要vpn根本不存在
- wsl需要systemctl 去自启动k8s,操作依赖root,需要修改wsl

-
安装必要的依赖,如iptables等,具体安装过程可以查看错误原因一步步装完依赖(问题不多)
-
安装完成后,有swap,kubelet会失败,需要修改unit-file,后面重启systemctl restart kubelet

-
这个错误是 Calico 的 install-cni init container(或 calico-node DaemonSet 中的某个 init 容器)在尝试启动时,Kubernetes/containerd 检测到 /var/run/calico 这个路径的挂载传播属性(mount propagation)不符合要求,导致容器 spec 生成失败。
核心报错:
text
1
path "/var/run/calico" is mounted on "/" but it is not a shared mount
这通常发生在 WSL2 环境下(尤其是单节点自建集群、kind、k3s、minikube 或 microk8s),因为 WSL2 的文件系统(9p 或 virtiofs)对 mount propagation(共享/从属/私有传播)的支持非常有限,默认很多挂载点都是 private,而不是 shared/slave。

名词解释
CNI
CNI(Container Network Interface)是一个用于容器网络的标准接口规范,主要用于在容器编排系统(如 Kubernetes)中管理容器的网络连接。CNI 旨在为容器创建、配置和管理网络接口,并保证容器的生命周期与其网络配置同步。
CRI
在 Kubernetes 中,CRI 代表 Container Runtime Interface(容器运行时接口)。这是 Kubernetes 用来与底层容器运行时进行交互的标准接口。
CRI 的作用
CRI 允许 Kubernetes 通过一个标准接口与任何符合 CRI 规范的容器运行时交互。这意味着 Kubernetes 不依赖于具体的容器运行时,而是通过 CRI 与运行时进行通信。这使得 Kubernetes 更加灵活,可以选择不同的容器运行时,而不会影响 Kubernetes 的核心功能。
CRI 的组件
CRI 的两个主要组件是:
- Kubelet:Kubernetes 集群中的每个节点上运行的组件。它负责与 CRI 兼容的容器运行时进行交互,管理容器的生命周期、拉取镜像、监控和日志等。
- CRI 兼容的容器运行时:这些是实现了 CRI 接口的容器运行时,负责具体的容器操作。常见的 CRI 兼容容器运行时包括:
- containerd:一个轻量的容器运行时,最初是 Docker 的一部分,现在被广泛用于 Kubernetes 集群中。
- CRI-O:一个专门为 Kubernetes 设计的轻量级容器运行时,支持 OCI(Open Container Initiative)标准。
- Docker (通过
dockershim):早期版本的 Kubernetes 通过dockershim支持 Docker。Kubernetes 1.20 开始,dockershim被弃用,但仍然可以通过第三方工具继续使用 Docker。
CRI 的核心功能
通过 CRI,Kubelet 可以执行以下任务:
- 启动和停止容器:Kubelet 通过 CRI 指定的接口启动和停止容器。
- 镜像管理:Kubelet 使用 CRI 拉取和管理容器镜像。
- 获取容器和镜像状态:Kubelet 通过 CRI 获取容器的状态信息,如运行状态、资源使用情况等。
- 日志管理:通过 CRI,Kubelet 可以收集和管理容器日志。
CRI 的工作流程
当 Kubernetes 调度一个 Pod 时,Kubelet 负责调用 CRI 接口来与容器运行时进行交互,完成容器的创建、启动和管理。具体流程如下:
- 镜像拉取:Kubelet 通过 CRI 拉取所需的容器镜像。
- 容器启动:Kubelet 通过 CRI 启动容器,并为其配置网络、存储等资源。
- 监控容器状态:Kubelet 通过 CRI 定期获取容器的状态,并确保容器按预期运行。
- 容器停止:当需要删除 Pod 或容器时,Kubelet 通过 CRI 停止并删除容器。
网络
在 Kubernetes 集群中,Pod 和 Pod 之间的通信是网络架构的核心部分,确保分布式应用能够在集群内部各个节点上的 Pod 之间进行数据交换。Pod 间的通信通过 Kubernetes 的网络模型和底层网络实现来保证。Kubernetes 要求集群中的所有 Pod 都能彼此通信,无论它们是否位于同一节点上。
Pod 间通信的实现方式
Pod 和 Pod 之间的通信主要依赖于 Kubernetes 网络插件(CNI 插件),这些插件提供了不同的网络实现方式来保证跨节点的 Pod 通信。
常见的 Pod 间通信实现方式有:
- Overlay 网络(如 VXLAN):通过在物理网络上构建虚拟网络,使跨节点的 Pod 能够通过虚拟网络通信。
- Underlay 网络(如 BGP):直接利用底层物理网络,通过路由协议确保 Pod 的跨节点通信。
- IPIP 隧道:在不同节点之间建立 IP-in-IP 隧道,将 Pod 的 IP 数据封装传输。
- Host-Gateway 模式:通过主机的物理网络直接路由 Pod 的流量,不依赖隧道或虚拟网络。
其中,VXLAN 和 BGP 是常用的两种实现跨节点 Pod 通信的重要技术,分别代表了Overlay 网络和Underlay 网络的实现方式。
VXLAN(Virtual Extensible LAN)
VXLAN 是一种 Overlay 网络技术,通过在现有的物理网络上构建虚拟网络,实现不同节点上的 Pod 间的通信。VXLAN 使用隧道技术,将原本在二层网络(如 Pod 到 Pod 的通信)上的流量封装在三层网络(IP 网络)中进行传输,能够跨越不同的网络段实现虚拟化的二层网络。
VXLAN 的核心概念
- VNI(VXLAN Network Identifier):VXLAN 的虚拟网络 ID,相当于 VLAN 的扩展。VNI 用于标识不同的虚拟网络,每个虚拟网络都有唯一的 VNI。
- VXLAN 隧道:VXLAN 把二层以太网帧封装在 UDP 包中,形成三层隧道,通过物理网络进行传输。隧道的两端是不同 Kubernetes 节点的网络接口。
- VTEP(VXLAN Tunnel Endpoints):VXLAN 隧道的两端节点(Kubernetes 的不同节点)称为 VTEP。VTEP 负责将流量封装并发送到其他节点的 VTEP,接收端 VTEP 解封装后将数据包交付给目标 Pod。
VXLAN 通信流程
- Pod A 想要与另一个节点上的 Pod B 通信,Pod A 发出的流量通过 CNI 插件被转发给本节点的 VTEP。
- VTEP 负责将数据帧封装到 VXLAN 隧道中,分配 VNI 并通过 IP 网络(物理网络)将流量发送到目标节点的 VTEP。
- 目标节点的 VTEP 收到封装的数据包,解封装并将数据包交给目标 Pod B。
VXLAN 的优点
- 隔离性好:VXLAN 提供了虚拟网络隔离,可以在相同的物理网络中为不同租户或应用创建隔离的虚拟网络。
- 扩展性强:VXLAN 能够支持比传统 VLAN 更多的虚拟网络(VLAN 仅支持 4096 个,而 VXLAN 可以支持 1600 万个)。
- 无需修改物理网络:通过 VXLAN 的 Overlay 网络实现 Pod 间通信,无需对底层物理网络进行大的改动。
常用的 Kubernetes CNI 插件支持 VXLAN
- Flannel:Flannel 支持 VXLAN 模式,将 Kubernetes 节点之间的 Pod 网络流量封装在 VXLAN 隧道中。
- Weave:Weave 也支持基于 VXLAN 的 Overlay 网络,适合大规模集群环境。
BGP(Border Gateway Protocol)
BGP 是一种动态路由协议,通常用于 Internet 的自治系统之间的路由传递。在 Kubernetes 中,BGP 作为 Underlay 网络方案,可以直接通过底层物理网络进行 Pod 间通信,而无需通过 Overlay 隧道。
BGP 的核心概念
- 自治系统(AS):BGP 通常在不同的自治系统之间传递路由信息,但在 Kubernetes 中,BGP 可以在同一自治系统内部的不同节点间传递路由信息。
- 动态路由:BGP 动态学习和传播路由信息,使得不同节点之间自动建立正确的路由,以确保 Pod 之间的跨节点通信。
- 路由传播:当新的 Pod 在某个节点上启动时,该节点会通过 BGP 协议将该 Pod 的路由信息通知其他节点,使得其他节点可以通过 BGP 路由将数据流量转发到正确的节点。
BGP 通信流程
- 当一个新的 Pod 启动时,运行 BGP 的节点会通过 BGP 协议将这个 Pod 的网络信息(如 Pod IP)通告给其他节点。
- 其他节点接收到路由信息后,会将其添加到本地的路由表中。
- 当本节点上的 Pod 需要与另一个节点上的 Pod 通信时,BGP 会将流量通过物理网络直接路由到目标节点,而无需通过隧道。
BGP 的优点
- 性能优异:BGP 是一种基于底层物理网络的方案,没有 Overlay 网络的额外封装和解封装开销,因此性能较高,适合大规模生产环境。
- 网络透明性:BGP 不依赖隧道,所有流量在物理网络上是可见的,便于进行网络监控和管理。
- 动态路由:BGP 动态地学习和分发路由,可以随着网络拓扑的变化自动调整路由,具有很好的扩展性和灵活性。
常用的 Kubernetes CNI 插件支持 BGP
- Calico:Calico 是一种常用的 CNI 插件,支持通过 BGP 实现 Pod 间通信。Calico 通过在每个 Kubernetes 节点上运行 BGP 路由器,实现直接基于物理网络的路由,不使用 Overlay 隧道。
- BIRD:Calico 内部集成了 BIRD 路由器,用来运行 BGP 协议,管理路由表,并与其他 BGP 对等体交换路由信息。
VXLAN 和 BGP 的对比
| 对比项 | VXLAN | BGP |
|---|---|---|
| 网络类型 | Overlay 网络 | Underlay 网络 |
| 封装方式 | 通过 VXLAN 隧道封装 Pod 数据帧 | 基于底层物理网络直接路由 |
| 隔离性 | 提供逻辑隔离(基于 VNI) | 无内置隔离,需要结合网络策略实现 |
| 性能 | 因封装和解封装有一定开销,性能较低 | 性能高,没有封装开销 |
| 扩展性 | 支持大规模网络环境(数百万个虚拟网络) | 依赖物理网络的扩展能力 |
| 配置难度 | 易于部署,对物理网络要求低 | 配置较为复杂,要求物理网络支持 BGP |
| 常见 CNI 插件 | Flannel、Weave | Calico、Cilium |
总结
- VXLAN 是一种基于 Overlay 网络的实现,能够通过在现有物理网络上构建虚拟网络来实现 Pod 之间的通信,适合无需修改底层物理网络的环境。
- BGP 是一种基于 Underlay 网络的方案,通过动态路由协议直接在物理网络上实现 Pod 之间的跨节点通信,具有高性能和动态路由的优势,但对物理网络要求较高。
具体选择哪种方案取决于集群的规模、性能要求、网络复杂度以及底层物理网络的支持情况。如果需要简化网络配置并支持大规模的动态 Pod 通信,VXLAN 是一种很好的选择;而对于注重性能和网络透明性的场景,BGP 则更为合适。
控制器
Pod控制器
守护进程类型
RC控制器
保障当前的pod数量和期望值一致
RS控制器
功能和RC控制器类似,多了标签选择器的运算方式
Deployment
-
支持声明式表达
-
支持滚动升级和回滚
原理: Deployment > RS > Pod升级保留原本的RS
DaemonSet
每个节点有且只有一个pod运行,随节点数增加和回收
StatefulSet
特性:
有序创建,倒序回收(发信号)
pod级别的数据持久化: pod -> pvc -> pv -> nfs
稳定的访问方式
批处理类型
job控制器
保障批处理任务一个或多个成功为止
Service
工作原理
userspace
kube-proxy 监听apiserver,将service变化修改本地的iptables规则,代理来自当前节点pod的用户请求
iptables
kube-proxy 监听apiserver,将service变化修改本地的iptables规则;
优点: 功能解耦,kube-proxy压力较小
ipvs
1、相较于iptables使用链表存储规则,ipvs使用hash来管理路由策略,能更快地进行规则匹配和转发;pod数量提升时,iptables的性能瓶颈会越来越明显
2、相较于iptables每次更新重新生成规则链,ipvs增量更新,影响更小
3、支持更多的协议,比如sctp
4、更多的负载均衡策略 加权,最少连接,最少最近使用,基于目的地址hash等
类型
ClusterIp
默认类型,自动分配一个只有cluster内部可以访问的虚拟ip
NodePort
在ClusterIp基础上为Service 在每台机器上绑定一个端口,这样可以通过NodePort访问该服务
LoadBalancer
在NodePort的基础上,接触cloud provider创建一个外部负载均衡器,并将请求转发到NodePort
ExternalName
把集群外部的服务引入到集群内部来,在集群内部直接使用,没有任何类型代理被创建
Endpoint
匹配规则
- 自动关联体系,配置selector
- 手动关联体系,无配置selector
存储
ConfigMap
-
通过环境变量使用
1 2 3 4 5 6
env: - name: userName valueFrom: configMapKeyRef: name: literal-config key: name
-
文件挂载
1 2 3 4 5 6 7 8 9 10
containers: - name: myapp image: test:V1.0 volumeMounts: - name: config-volume mountPath: /etc/config volumes: - name: config-volume configMap: name: literal-config
Secret
定义
Secret对象类型用来保存敏感信息,例如密码,OAuth令牌和ssh密钥。这些信息放在secret中比放在Pod中的定义或者容器镜像中来说更加安全和灵活
特性
- 只将secret分发到需要访问secret的pod所在机器节点,保障安全性
- 只存储在内存中,永不鞋服物理内存,节点删除secret不需要擦除磁盘数据
- etcd加密存储secret,一定程度保证安全
类型

使用
环境变量
1
2
3
4
5
6
env:
- name: userName
valueFrom:
secretKeyRef:
name: mysecret
key: name
文件
1
2
3
4
5
6
7
8
9
10
11
12
containers:
- name: mypod
image: redis
volumeMounts:
- name: foo
mountPath: "/etc/foo"
readOnly: true
volumes:
- name: foo
secret:
secretName: mysecret
optional: true
DownwardApi
存在的意义
在 Kubernetes 中,Downward API 是一种机制,允许 Pod 从 Kubernetes 系统中获取有关自身的信息,而不需要手动配置这些信息。通过 Downward API,Pod 内的容器可以访问到一些关于自身的元数据和状态,例如资源限制、节点信息、Pod 名称、命名空间等。
Downward API 可以通过两种方式将这些信息暴露给容器:
- 环境变量 (
env) - Volume 文件 (
volume)
使用环境变量获取信息
可以使用 Downward API 将 Pod 的信息注入到环境变量中。以下是一个示例,展示如何将 Pod 的名称和命名空间暴露为环境变量:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: v1
kind: Pod
metadata:
name: mypod
namespace: default
spec:
containers:
- name: mycontainer
image: nginx
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
在这个示例中,POD_NAME 和 POD_NAMESPACE 环境变量会在容器启动时被注入,容器可以直接读取这些环境变量。
使用 Volume 文件获取信息
另一个方式是通过将这些信息写入容器内的文件中,然后让容器读取这些文件。以下示例展示如何通过 Downward API 将信息写入到文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: v1
kind: Pod
metadata:
name: mypod
namespace: default
spec:
containers:
- name: mycontainer
image: nginx
volumeMounts:
- name: downward-api-volume
mountPath: /etc/podinfo
volumes:
- name: downward-api-volume
downwardAPI:
items:
- path: "labels"
fieldRef:
fieldPath: metadata.labels
- path: "annotations"
fieldRef:
fieldPath: metadata.annotations
在这个示例中,Pod 的标签和注解会被写入 /etc/podinfo/labels 和 /etc/podinfo/annotations 文件中,容器可以读取这些文件获取相应的信息。
Downward API 支持的字段
Downward API 支持从 Pod 和容器的以下字段中获取信息:
- Pod 元数据:
metadata.name、metadata.namespace、metadata.labels、metadata.annotations等。 - 资源请求和限制:
spec.containers.resources.requests.cpu、spec.containers.resources.limits.memory等。 - 节点信息:
spec.nodeName。
典型用例
- 日志中标识 Pod:通过 Downward API,将 Pod 的名称和命名空间注入到日志文件中,帮助在日志中区分不同的 Pod 实例。
- 资源调整:容器可以根据它分配的资源(CPU、内存)自动调整其行为。
- Pod 的自描述:容器可以通过 Downward API 来获取 Pod 的元数据信息,并在运行时根据这些信息做一些行为上的调整。
Downward API 使得应用可以更好地与 Kubernetes 集成,从而减少对硬编码信息的依赖,使得 Pod 更加灵活和自适应。
Volume
存在的意义
-
数据持久化:容器本身的文件系统是临时的,当容器终止时会丢失所有数据。如果需要在 Pod 重新调度或容器重启后保留数据,就需要使用
Volume。 -
容器之间的数据共享:在一个 Pod 中的多个容器可以通过挂载同一个
Volume来共享数据。 -
多种存储后端支持:Kubernetes 支持多种存储后端,例如本地存储、网络文件系统(如 NFS)、云存储(如 AWS EBS、GCP Persistent Disk)等,满足不同的存储需求。
常见的 Volume 类型
1. emptyDir
emptyDir 是最基础的一种 Volume 类型。它为 Pod 提供一个空目录,生命周期与 Pod 一致。Pod 重启时,emptyDir 中的数据会保留,但如果 Pod 被删除或重新调度到其他节点,数据将丢失。
用途:
- 临时存储。
- 容器间共享临时数据。
示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: v1
kind: Pod
metadata:
name: emptydir-pod
spec:
containers:
- name: nginx
image: nginx
volumeMounts:
- mountPath: /usr/share/nginx/html
name: html
volumes:
- name: html
emptyDir: {}
2. hostPath
hostPath 允许容器访问宿主机节点的文件系统。它会将宿主机上的一个目录或文件挂载到容器中。
用途:
- 调试目的或特殊的文件系统访问。
- 需要访问宿主机文件系统的场景。
注意:hostPath 具有较高的权限,因此在生产环境中应谨慎使用,可能导致安全问题。
示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
apiVersion: v1
kind: Pod
metadata:
name: hostpath-pod
spec:
containers:
- name: test-container
image: busybox
command: ['sh', '-c', 'while true; do sleep 3600; done']
volumeMounts:
- mountPath: /data
name: mydir
volumes:
- name: mydir
hostPath:
path: /mnt/data # 宿主机上的路径
type: Directory
3. persistentVolumeClaim (PVC)
persistentVolumeClaim 是 Kubernetes 中持久化存储的核心组件,它结合 PersistentVolume (PV) 来为 Pod 提供持久存储。PVC 是用户请求存储的一种声明,它绑定到集群中的一个 PersistentVolume,并确保数据在 Pod 重启或重新调度时不会丢失。
用途:
- 长期持久化数据。
- 数据库或其他状态存储应用。
示例:
- 定义一个
PersistentVolume(PV):
1
2
3
4
5
6
7
8
9
10
11
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-example
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/mnt/data"
- 创建
PersistentVolumeClaim(PVC):
1
2
3
4
5
6
7
8
9
10
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-example
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
- 使用
PVC在 Pod 中挂载:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
apiVersion: v1
kind: Pod
metadata:
name: pvc-pod
spec:
containers:
- name: my-container
image: nginx
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: mypvc
volumes:
- name: mypvc
persistentVolumeClaim:
claimName: pvc-example
4. NFS
NFS (Network File System) 允许多个 Pod 通过网络共享同一个存储卷。可以将远程 NFS 服务器上的目录挂载到 Kubernetes 中的 Pod 里。
用途:
- 需要跨节点共享存储的数据,如日志存储、共享配置文件等。
示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: v1
kind: Pod
metadata:
name: nfs-pod
spec:
containers:
- name: my-container
image: nginx
volumeMounts:
- mountPath: /usr/share/nginx/html
name: nfs-volume
volumes:
- name: nfs-volume
nfs:
server: nfs.example.com
path: /exported/path
5.StorageClass
StorageClass 的用途
- 动态供应存储卷:当用户创建一个
PersistentVolumeClaim(PVC)并指定了StorageClass,Kubernetes 会自动根据StorageClass的配置动态地创建一个符合要求的存储卷。 - 指定存储类型和参数:
StorageClass允许你定义不同类型的存储,如高性能 SSD、标准 HDD,或者不同的访问模式(ReadWriteOnce、ReadOnlyMany等)。 - 跨云供应:在云环境(如 AWS、GCP、Azure)中,
StorageClass允许你指定使用哪种云提供的存储类型(如 AWS EBS、GCP Persistent Disk、Azure Disk)。
StorageClass 工作机制
当你定义一个 StorageClass,它会包含关于存储卷类型、参数以及如何动态创建卷的配置信息。然后,当应用需要存储时,用户可以通过 PersistentVolumeClaim 引用相应的 StorageClass,Kubernetes 会根据这个 StorageClass 自动创建一个符合要求的持久卷。
StorageClass 的关键属性
provisioner:指定存储提供者,例如 AWS EBS、GCP Persistent Disk、NFS 等。provisioner负责调用底层的存储系统来动态创建存储卷。parameters:可以设置存储系统的具体参数,例如磁盘类型、文件系统类型、区域等。reclaimPolicy:定义存储卷的回收策略,常见的值有Delete和Retain,控制存储卷在使用完成后的处理方式。allowVolumeExpansion:定义是否允许扩展存储卷的容量。
StorageClass 配置示例
NFS StorageClass
在使用 NFS 作为存储系统时,StorageClass 也可以定义如何动态创建 NFS 存储卷:
1
2
3
4
5
6
7
8
9
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-storage
provisioner: example.com/nfs
parameters:
server: nfs-server.example.com
path: /exported/path
reclaimPolicy: Retain
使用 StorageClass 和 PVC
用户在申请存储时,通过 PersistentVolumeClaim(PVC)引用一个 StorageClass,Kubernetes 会根据 PVC 的需求和 StorageClass 自动创建和绑定存储卷。
PVC 示例:
1
2
3
4
5
6
7
8
9
10
11
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: aws-ebs # 使用 AWS EBS StorageClass
storageClassName指定了该PVC应使用的StorageClass,Kubernetes 会根据aws-ebs定义的配置动态创建存储卷。
StorageClass 中的 reclaimPolicy
- Delete:当 PVC 被删除时,关联的存储卷(PV)也会被删除。
- Retain:即使 PVC 被删除,存储卷仍然保留,管理员需要手动管理或删除 PV。
StorageClass 适用的存储系统
StorageClass 支持多种存储系统,具体取决于底层的 provisioner。以下是一些常见的存储提供者:
- AWS EBS:用于动态创建 AWS 的 Elastic Block Store(EBS)卷。
- GCP PD:用于创建 Google Cloud Platform 的持久磁盘。
- *Azure Disk**:用于 Azure 提供的块存储。
- NFS:允许通过网络文件系统挂载存储卷。
- Ceph RBD:支持 Ceph 分布式存储系统。
- GlusterFS:支持 GlusterFS 分布式文件系统。
- Cinder:用于 OpenStack 提供的存储卷。
总结
- StorageClass 是 Kubernetes 动态创建和管理存储卷的关键机制。它定义了不同存储系统的配置参数,允许根据需要动态地创建
PersistentVolume(PV)。 - StorageClass 支持多种存储后端,包括云提供商(如 AWS、GCP、Azure)、本地存储(如 NFS、Ceph、GlusterFS)等。
- 使用
PersistentVolumeClaim(PVC)时,可以通过指定storageClassName来动态申请存储卷,简化了存储管理。
StorageClass 提供了一种抽象化的存储配置方式,使 Kubernetes 能够更高效地管理持久化存储卷,特别是在云环境中,动态存储卷供应是 StorageClass 的核心功能。
6.CSI
在 Kubernetes 中,CSI(Container Storage Interface) 是一个用于存储系统的标准接口,它允许 Kubernetes 动态挂载、创建和管理存储卷。CSI 使得 Kubernetes 可以与多种存储提供商集成,无论是本地存储、云存储还是网络文件系统。通过使用 CSI,存储提供商可以开发自己的插件,并与 Kubernetes 无缝集成,实现动态存储卷的创建和管理。
CSI 的背景
在 CSI 出现之前,Kubernetes 是通过内置的 Volume 插件支持不同的存储系统。这种方式虽然直接,但增加了维护难度:Kubernetes 核心需要针对每个存储系统进行更新和维护。而 CSI 的出现,则将存储系统和 Kubernetes 核心代码分离,使得存储提供商可以以独立的方式开发和维护自己的存储插件,而不需要修改 Kubernetes 核心代码。
CSI 的工作原理
CSI 是一套标准的接口,允许存储提供商为 Kubernetes 提供自定义的存储插件。这些插件通过标准化的接口向 Kubernetes 提供存储服务,支持的功能包括:
- 创建和删除存储卷。
- 挂载和卸载存储卷到容器。
- 扩展存储卷大小。
- 快照和还原存储卷。
这些功能通过 CSI 驱动(插件)来实现。存储提供商可以开发自己的 CSI 驱动,并将其部署到 Kubernetes 集群中,Kubernetes 会通过 CSI 驱动与底层存储系统进行交互。
CSI 组件
CSI 的工作涉及以下几个关键组件:
- Kubernetes API Server:
- API Server 处理来自用户的存储卷请求,并将请求传递给 Kubernetes 的
kube-controller-manager和kubelet。
- API Server 处理来自用户的存储卷请求,并将请求传递给 Kubernetes 的
- kube-controller-manager:
kube-controller-manager中包含了PersistentVolumeController,它负责处理存储卷的生命周期管理(例如创建和删除 PV)。- 它通过 CSI 接口与存储系统交互,并动态创建、删除、扩展存储卷。
- kubelet:
kubelet负责在每个节点上挂载和卸载存储卷。它通过调用 CSI 驱动来将存储卷挂载到 Pod 中运行的节点上。
- CSI 驱动:
- CSI 驱动是由存储提供商开发的插件,运行在 Kubernetes 集群中,用来处理实际的存储操作。它负责与底层存储系统(如 AWS EBS、GCE Persistent Disk、Ceph 等)进行交互。
- CSI 驱动包括两个关键组件:
- Controller:运行在控制平面中,负责处理卷的创建、删除、快照等操作。
- Node Plugin:运行在每个节点上,负责挂载、卸载卷到节点上。
常见的 CSI 驱动
- AWS EBS CSI 驱动:用于动态创建和管理 AWS Elastic Block Store(EBS)。
- GCE Persistent Disk CSI 驱动:用于 Google Cloud 的持久磁盘(Persistent Disk)。
- Azure Disk CSI 驱动:用于 Azure 的托管磁盘(Managed Disk)。
- Ceph CSI 驱动:用于管理 Ceph 集群的存储卷,支持 Ceph RBD 和 CephFS。
- OpenStack Cinder CSI 驱动:用于 OpenStack 环境中的块存储服务 Cinder。
- NFS CSI 驱动:用于动态创建和挂载 NFS 存储卷。
CSI 的优势
- 灵活的存储支持:通过 CSI,Kubernetes 可以支持多种不同的存储系统,无论是本地存储、云存储还是分布式存储系统。
- 存储插件的独立性:存储提供商可以独立于 Kubernetes 核心开发、发布和维护他们的 CSI 插件。这减少了 Kubernetes 核心的复杂性,同时加速了存储插件的迭代。
- 标准化接口:CSI 提供了一套标准化的接口,简化了存储系统的集成流程,让 Kubernetes 可以无缝支持更多存储后端。
CSI内联卷
内联卷(inline volumes) 是 Kubernetes 中的一种存储卷定义方式,它允许在 Pod 规范中直接定义存储卷,而无需使用 PersistentVolume(PV)和 PersistentVolumeClaim(PVC) 这类独立的资源对象。与传统的使用 PVC 来声明持久存储不同,内联卷的定义与 Pod 的生命周期严格绑定,通常是用于临时存储或不需要持久化的存储需求。
在 CSI(Container Storage Interface) 中,CSI 内联卷 是指可以直接在 Pod 规范中声明 CSI 卷,而不通过 StorageClass 和 PersistentVolumeClaim 来动态请求存储。这种方式适用于一些临时存储的场景,且并非所有 CSI 驱动都支持内联卷。
内联卷与标准 CSI 卷的区别
- 标准 CSI 卷:通常需要先定义
StorageClass,然后通过PersistentVolumeClaim(PVC) 申请存储,Kubernetes 通过 CSI 驱动动态创建PersistentVolume(PV)。这些存储卷与 Pod 的生命周期分离,通常用于持久化存储。 - 内联 CSI 卷:存储卷是直接在 Pod 规范中定义的,不涉及
StorageClass和PVC,而是通过 CSI 驱动直接挂载。卷的生命周期与 Pod 绑定,通常不用于持久化存储。
CSI 内联卷的用途
- 临时存储需求:适用于一些不需要持久化存储的应用场景,例如缓存、临时数据存储等。
- 快速挂载特定卷:有时你只需在某个 Pod 中挂载一次性存储,而无需创建独立的
PVC和PV。 - 与 Pod 生命周期绑定:内联卷的生命周期与 Pod 一致,当 Pod 被删除时,卷也会被删除。
CSI 内联卷的配置
要使用内联 CSI 卷,需要确保使用的 CSI 驱动支持这种方式。以下是内联 CSI 卷的配置示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: v1
kind: Pod
metadata:
name: csi-inline-pod
spec:
containers:
- name: my-container
image: busybox
command: ['sh', '-c', 'echo "hello" > /mnt/inline-volume/data && sleep 3600']
volumeMounts:
- mountPath: /mnt/inline-volume
name: my-inline-volume
volumes:
- name: my-inline-volume
csi:
driver: my-csi-driver # 指定 CSI 驱动
volumeAttributes: # 定义存储卷的属性
storage.kubernetes.io/csiProvisionerIdentity: "1234567890"
volumeHandle: "my-volume-handle" # 由 CSI 驱动识别的卷 ID
总结
- CSI 是一种标准接口,用于在 Kubernetes 中动态创建、挂载、卸载和管理存储卷。
- 它将存储插件与 Kubernetes 核心解耦,使得存储提供商可以独立开发和维护他们的 CSI 驱动。
- 通过 CSI 驱动,Kubernetes 支持多种存储系统,如 AWS EBS、GCP Persistent Disk、Azure Disk、Ceph、NFS 等。
- 使用 CSI 驱动时,用户可以通过
StorageClass和PersistentVolumeClaim动态创建和管理存储卷,满足不同存储需求。
CSI 的出现大大增强了 Kubernetes 在存储领域的扩展性和灵活性,为现代云原生应用提供了强大的存储支持。
调度器
节点选择
预选
- 节点上剩余的资源是否大于pod请求的资源
- 如果pod指定了NodeName,检查节点名称是否与nodeName匹配
- 节点上已经使用的port是否和pod申请的port冲突
- 过滤掉和pod指定的label不匹配的节点
- 已经mount的volume和pod指定的volume不冲突,除非他们都是只读
优选
- 通过cpu和mem的使用率来决定权重,倾向于资源使用比例更低的节点
- cpu和mem使用率接近的1:1的优先使用,和1策略一起使用
- 倾向于已经下载镜像的节点
节点选择器(NodeSelector)
NodeSelector 是最简单的节点调度方式,通过在 Pod 的 spec.nodeSelector 字段指定键值对,限制 Pod 只能调度到拥有特定标签的节点上。
配置方法
1
2
3
4
5
6
7
apiVersion: v1
kind: Pod
metadata:
name: example-pod
spec:
nodeSelector:
disktype: ssd
在这个例子中,Pod 只会调度到带有标签 disktype=ssd 的节点上。
节点亲和性(NodeAffinity)
NodeAffinity 是 NodeSelector 的增强版本,支持更灵活的调度规则,比如软约束(preferential)和硬约束(required)。可以根据节点标签的表达式进行调度选择。
配置方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: v1
kind: Pod
metadata:
name: example-pod
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: disktype
operator: In
values:
- ssd
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: disktype
operator: In
values:
- hdd
这里定义了一个硬约束:Pod 只能调度到 disktype=ssd 的节点上,并且有一个软约束:如果有 disktype=hdd 的节点,可以优先调度到这些节点上。
Pod 亲和性和反亲和性(Pod Affinity & Anti-Affinity)
Pod 亲和性和反亲和性用来控制 Pod 是否应该与其他特定的 Pod 部署在相同的节点或不同的节点上。
配置 Pod 亲和性
1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: v1
kind: Pod
metadata:
name: example-pod
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app: frontend
topologyKey: "kubernetes.io/hostname"
这个配置要求 Pod 调度到与标签为 app=frontend 的 Pod 在同一个节点的节点上。
配置 Pod 反亲和性
1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: v1
kind: Pod
metadata:
name: example-pod
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app: frontend
topologyKey: "kubernetes.io/hostname"
这个配置要求 Pod 调度到与标签为 app=frontend 的 Pod 在不同节点的节点上。
污点和容忍(Taints and Tolerations)
节点可以使用污点(Taint)标记,禁止 Pod 调度到这些节点上,除非 Pod 声明了相应的容忍(Toleration)。
设置节点污点
1
kubectl taint nodes node1 key=value:NoSchedule
这个命令会为节点 node1 设置一个污点,表示不允许 Pod 调度到这个节点,除非 Pod 有相应的容忍。
配置 Pod 容忍
1
2
3
4
5
6
7
8
9
10
apiVersion: v1
kind: Pod
metadata:
name: example-pod
spec:
tolerations:
- key: "key"
operator: "Equal"
value: "value"
effect: "NoSchedule"
这个 Pod 可以容忍 key=value:NoSchedule 这种污点,从而被调度到带有该污点的节点上
k8s集群安全机制
认证(Authentication)
认证确保访问 Kubernetes API 的实体身份的真实性,包括用户、服务账户、节点等。
主要认证机制
- 客户端证书认证:通过 TLS 客户端证书来识别用户或集群组件。
- Bearer Token:Kubernetes 使用 Token,特别是服务账户中的 Bearer Token 来验证请求来源的身份。
- OpenID Connect (OIDC):Kubernetes 支持与 OIDC 身份提供者(如 Google、Azure 等)集成,用于用户的身份验证。
- Webhook 认证:通过外部 Webhook 服务进行自定义身份验证逻辑。
认证机制确保了 Kubernetes 集群中所有实体的身份合法性。
授权(Authorization)
授权是在身份验证后,决定该用户或组件是否有权限执行某一特定操作。Kubernetes 支持多种授权机制来进行权限控制。
授权机制可以确保请求只有在拥有正确权限时才能执行。
常用授权方式
基于角色的访问控制(RBAC)
RBAC(基于角色的访问控制,Role-Based Access Control)是 Kubernetes 中的核心授权机制,用于控制用户、组和服务账户对集群资源的访问权限。RBAC 通过定义角色和绑定(Role/ClusterRole 和 RoleBinding/ClusterRoleBinding)来为不同主体分配权限。
RBAC 的基本组成包括以下四个部分:
- Role:定义特定命名空间内的权限。
- ClusterRole:定义集群范围内的权限,或者在多个命名空间中共享的权限。
- RoleBinding:将
Role绑定到用户、组或服务账户,使其能够在特定命名空间内执行操作。 - ClusterRoleBinding:将
ClusterRole绑定到用户、组或服务账户,使其能够在整个集群范围内执行操作。
RBAC 的工作原理
- Role 和 ClusterRole:定义权限(即哪些操作可以执行)。
- Role:作用域仅限于命名空间。
- ClusterRole:可用于集群范围内的资源或共享给多个命名空间的资源。
- RoleBinding 和 ClusterRoleBinding:将角色绑定到主体(用户、组或服务账户)。
- RoleBinding:在特定命名空间内,将
Role或ClusterRole绑定给主体,允许他们在该命名空间中操作。 - ClusterRoleBinding:将
ClusterRole绑定给主体,使其在整个集群范围内操作。
- RoleBinding:在特定命名空间内,将
详细的 RBAC 配置步骤
定义 Role
Role 用于在特定命名空间内定义权限规则。
1
2
3
4
5
6
7
8
9
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default # 命名空间内生效
name: pod-reader
rules:
- apiGroups: [""] # "" 表示 core API group
resources: ["pods"] # 资源类型
verbs: ["get", "list", "watch"] # 可以执行的操作
在上面的例子中,我们创建了一个 Role,它允许主体在 default 命名空间内对 pods 执行 get、list 和 watch 操作。
定义 ClusterRole
ClusterRole 的作用域是整个集群,或者可以跨多个命名空间进行复用。
1
2
3
4
5
6
7
8
9
10
11
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: cluster-admin-role
rules:
- apiGroups: [""] # 对核心 API 组的权限
resources: ["nodes"] # 对 nodes 资源的权限
verbs: ["get", "list"] # 可以执行的操作
- apiGroups: ["extensions"] # 对扩展 API 组的权限
resources: ["deployments"]
verbs: ["get", "list", "watch", "create", "update", "delete"]
这个 ClusterRole 定义了集群级别的权限,允许主体对 nodes 和 deployments 资源执行操作。
创建 RoleBinding
RoleBinding 将一个 Role 绑定到用户、组或服务账户,使其在特定命名空间内生效。
1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-pods-binding
namespace: default
subjects:
- kind: User # 绑定的主体
name: "john" # 用户名
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role # 引用的 Role
name: pod-reader # 关联的 Role 名称
apiGroup: rbac.authorization.k8s.io
在上面的配置中,我们将 pod-reader 这个 Role 绑定到名为 “john” 的用户,使他在 default 命名空间内拥有对 pods 的读取权限。
创建 ClusterRoleBinding
ClusterRoleBinding 将 ClusterRole 绑定到用户、组或服务账户,使其在整个集群范围内生效。
1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: cluster-admin-binding
subjects:
- kind: User # 绑定的主体
name: "admin" # 用户名
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole # 引用的 ClusterRole
name: cluster-admin-role # 关联的 ClusterRole 名称
apiGroup: rbac.authorization.k8s.io
在这个示例中,我们将 cluster-admin-role 绑定到名为 “admin” 的用户,使他拥有集群范围内的管理员权限。
绑定 ServiceAccount
除了将 Role 和 ClusterRole 绑定给用户外,Kubernetes 也支持将它们绑定到 ServiceAccount(服务账户),服务账户是 Kubernetes 中 Pod 与 API Server 交互的主要身份。
当 Kubernetes 创建一个 ServiceAccount 时,它会为每个 ServiceAccount 生成三个文件,这些文件通常存放在挂载到 Pod 的 /var/run/secrets/kubernetes.io/serviceaccount 目录下。生成的文件如下:
token文件:- 这是一个 JSON Web Token (JWT),用于身份验证。
- Pod 中的应用程序可以通过读取这个
token来访问 Kubernetes API Server。 - 这个
token被挂载到 Pod 中,并且可以用来代表 Pod 进行认证请求。
ca.crt文件:- 这是集群的根证书,用于 API Server 的 TLS 证书验证。
- Pod 中的应用程序使用这个证书来验证与 API Server 的安全连接,确保与 API Server 之间的通信是加密且可信的。
namespace文件:- 这个文件包含当前 Pod 所在的命名空间名称。
- 应用程序可以通过读取该文件来确定 Pod 所属的命名空间,这对于需要动态获取命名空间的应用来说非常有用。
这三个文件一起确保了 Pod 使用关联的 ServiceAccount 来与 Kubernetes API 进行身份验证和安全通信。
1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-pods-sa-binding
namespace: default
subjects:
- kind: ServiceAccount # 绑定的主体类型为 ServiceAccount
name: my-service-account # 服务账户名称
namespace: default # 服务账户所属的命名空间
roleRef:
kind: Role
name: pod-reader # 关联的 Role 名称
apiGroup: rbac.authorization.k8s.io
在这个例子中,my-service-account 服务账户在 default 命名空间内被授予了 pod-reader 角色所定义的权限。
RBAC 的最佳实践
- 最小权限原则:始终遵循最小权限原则,为用户和服务账户授予最小化的必要权限,避免滥用或权限扩大。
- 隔离命名空间的权限:使用
Role而不是ClusterRole,确保权限只作用于特定命名空间,而不是整个集群,减少潜在的安全风险。 - 定期审查权限:定期检查和审查
RoleBinding和ClusterRoleBinding,确保没有未使用的过期角色绑定或不再需要的权限。 - 服务账户分离职责:为每个 Pod 或服务组件分配不同的
ServiceAccount,并使用RoleBinding或ClusterRoleBinding进行精细化权限控制。
ABAC(基于属性的访问控制)
基于策略文件控制权限。
Node Authorization
限制节点只允许访问和操作与其相关的资源。
Webhook 授权
通过外部服务决定是否授权请求。
准入控制(Admission Control)
准入控制(Admission Control)是 Kubernetes 中的一组拦截器,它们在请求通过认证和授权后、最终提交到 API Server 前,进一步检查和修改请求。准入控制的主要目的是在资源创建或修改时,应用额外的安全策略或约束。
准入控制类型
Kubernetes 中的准入控制器可以分为两类:
- Mutating Admission Controllers(可变准入控制器):这些控制器可以修改请求,通常用于注入一些配置。例如,
MutatingAdmissionWebhook可以自动为 Pod 注入 sidecar 容器。 - Validating Admission Controllers(验证型准入控制器):这些控制器用于验证请求是否合法或符合策略要求,如果不合法,则会拒绝请求。例如,
PodSecurityPolicy就是一种验证型准入控制器,用于确保 Pod 符合安全策略。
常用准入控制器
- LimitRanger:确保 Pod 不会超过资源配额限制。
- ResourceQuota:确保命名空间内的资源不会超出配额。
- ServiceAccount:确保每个 Pod 都有一个关联的服务账户。
动态准入控制(Dynamic Admission Control)
-
Admission Webhooks:通过动态准入控制,用户可以使用
MutatingAdmissionWebhook和ValidatingAdmissionWebhook实现自定义的准入控制逻辑。这允许集成外部系统来动态地修改或验证资源配置。示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
apiVersion: admissionregistration.k8s.io/v1 kind: MutatingWebhookConfiguration metadata: name: example-mutating-webhook webhooks: - name: mutate.example.com clientConfig: service: name: webhook-service namespace: default path: "/mutate" rules: - operations: ["CREATE"] apiGroups: [""] apiVersions: ["v1"] resources: ["pods"]
Kubernetes 中的 “3A” 安全保障总结
- 认证:确保请求的合法来源。
- 授权:确保请求有权限执行特定操作。
- 准入控制:在请求进入系统前,进一步应用安全策略或做出验证,确保符合集群的配置和安全要求。
通过认证、授权和准入控制的组合,Kubernetes 确保集群中的每个请求都经过严格的身份验证、权限校验和安全策略检查,提供了全面的安全保障体系。