k8s

k8s

Posted by fjw on May 17, 2023
k8s

基于wsl2安装k8s

参考单机快速体验k8s集群的测试环境

kubeasz版本选用最新的release版本,因为你懂的原因,docker镜像仓库已经需要vpn代理,所以说的不需要vpn根本不存在

  1. wsl需要systemctl 去自启动k8s,操作依赖root,需要修改wsl

image-20241011105735437

  1. 安装必要的依赖,如iptables等,具体安装过程可以查看错误原因一步步装完依赖(问题不多)

  2. 安装完成后,有swap,kubelet会失败,需要修改unit-file,后面重启systemctl restart kubelet

image-20241005211506610

  1. 这个错误是 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。image-20260114212750046

名词解释

CNI

CNI(Container Network Interface)是一个用于容器网络的标准接口规范,主要用于在容器编排系统(如 Kubernetes)中管理容器的网络连接。CNI 旨在为容器创建、配置和管理网络接口,并保证容器的生命周期与其网络配置同步。

CRI

在 Kubernetes 中,CRI 代表 Container Runtime Interface(容器运行时接口)。这是 Kubernetes 用来与底层容器运行时进行交互的标准接口。

CRI 的作用

CRI 允许 Kubernetes 通过一个标准接口与任何符合 CRI 规范的容器运行时交互。这意味着 Kubernetes 不依赖于具体的容器运行时,而是通过 CRI 与运行时进行通信。这使得 Kubernetes 更加灵活,可以选择不同的容器运行时,而不会影响 Kubernetes 的核心功能。

CRI 的组件

CRI 的两个主要组件是:

  1. Kubelet:Kubernetes 集群中的每个节点上运行的组件。它负责与 CRI 兼容的容器运行时进行交互,管理容器的生命周期、拉取镜像、监控和日志等。
  2. 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 可以执行以下任务:

  1. 启动和停止容器:Kubelet 通过 CRI 指定的接口启动和停止容器。
  2. 镜像管理:Kubelet 使用 CRI 拉取和管理容器镜像。
  3. 获取容器和镜像状态:Kubelet 通过 CRI 获取容器的状态信息,如运行状态、资源使用情况等。
  4. 日志管理:通过 CRI,Kubelet 可以收集和管理容器日志。

CRI 的工作流程

当 Kubernetes 调度一个 Pod 时,Kubelet 负责调用 CRI 接口来与容器运行时进行交互,完成容器的创建、启动和管理。具体流程如下:

  1. 镜像拉取:Kubelet 通过 CRI 拉取所需的容器镜像。
  2. 容器启动:Kubelet 通过 CRI 启动容器,并为其配置网络、存储等资源。
  3. 监控容器状态:Kubelet 通过 CRI 定期获取容器的状态,并确保容器按预期运行。
  4. 容器停止:当需要删除 Pod 或容器时,Kubelet 通过 CRI 停止并删除容器。

网络

在 Kubernetes 集群中,Pod 和 Pod 之间的通信是网络架构的核心部分,确保分布式应用能够在集群内部各个节点上的 Pod 之间进行数据交换。Pod 间的通信通过 Kubernetes 的网络模型和底层网络实现来保证。Kubernetes 要求集群中的所有 Pod 都能彼此通信,无论它们是否位于同一节点上。

Pod 间通信的实现方式

Pod 和 Pod 之间的通信主要依赖于 Kubernetes 网络插件(CNI 插件),这些插件提供了不同的网络实现方式来保证跨节点的 Pod 通信。

常见的 Pod 间通信实现方式有:

  1. Overlay 网络(如 VXLAN):通过在物理网络上构建虚拟网络,使跨节点的 Pod 能够通过虚拟网络通信。
  2. Underlay 网络(如 BGP):直接利用底层物理网络,通过路由协议确保 Pod 的跨节点通信。
  3. IPIP 隧道:在不同节点之间建立 IP-in-IP 隧道,将 Pod 的 IP 数据封装传输。
  4. Host-Gateway 模式:通过主机的物理网络直接路由 Pod 的流量,不依赖隧道或虚拟网络。

其中,VXLANBGP 是常用的两种实现跨节点 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 通信流程

  1. Pod A 想要与另一个节点上的 Pod B 通信,Pod A 发出的流量通过 CNI 插件被转发给本节点的 VTEP。
  2. VTEP 负责将数据帧封装到 VXLAN 隧道中,分配 VNI 并通过 IP 网络(物理网络)将流量发送到目标节点的 VTEP。
  3. 目标节点的 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 通信流程

  1. 当一个新的 Pod 启动时,运行 BGP 的节点会通过 BGP 协议将这个 Pod 的网络信息(如 Pod IP)通告给其他节点。
  2. 其他节点接收到路由信息后,会将其添加到本地的路由表中。
  3. 当本节点上的 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

  1. 支持声明式表达

  2. 支持滚动升级和回滚

    原理: 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

匹配规则

  1. 自动关联体系,配置selector
  2. 手动关联体系,无配置selector

存储

ConfigMap

  1. 通过环境变量使用

    1
    2
    3
    4
    5
    6
    
    env:
      - name: userName
        valueFrom:
          configMapKeyRef:
            name: literal-config
            key: name
    
  2. 文件挂载

    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中的定义或者容器镜像中来说更加安全和灵活

特性

  1. 只将secret分发到需要访问secret的pod所在机器节点,保障安全性
  2. 只存储在内存中,永不鞋服物理内存,节点删除secret不需要擦除磁盘数据
  3. etcd加密存储secret,一定程度保证安全

类型

image-20241009134813396

使用

环境变量

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 可以通过两种方式将这些信息暴露给容器:

  1. 环境变量 (env)
  2. 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_NAMEPOD_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.namemetadata.namespacemetadata.labelsmetadata.annotations 等。
  • 资源请求和限制spec.containers.resources.requests.cpuspec.containers.resources.limits.memory 等。
  • 节点信息spec.nodeName

典型用例

  1. 日志中标识 Pod:通过 Downward API,将 Pod 的名称和命名空间注入到日志文件中,帮助在日志中区分不同的 Pod 实例。
  2. 资源调整:容器可以根据它分配的资源(CPU、内存)自动调整其行为。
  3. Pod 的自描述:容器可以通过 Downward API 来获取 Pod 的元数据信息,并在运行时根据这些信息做一些行为上的调整。

Downward API 使得应用可以更好地与 Kubernetes 集成,从而减少对硬编码信息的依赖,使得 Pod 更加灵活和自适应。

Volume

存在的意义

  1. 数据持久化:容器本身的文件系统是临时的,当容器终止时会丢失所有数据。如果需要在 Pod 重新调度或容器重启后保留数据,就需要使用 Volume

  2. 容器之间的数据共享:在一个 Pod 中的多个容器可以通过挂载同一个 Volume 来共享数据。

  3. 多种存储后端支持: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 重启或重新调度时不会丢失。

用途

  • 长期持久化数据。
  • 数据库或其他状态存储应用。

示例

  1. 定义一个 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"
  1. 创建 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
  1. 使用 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 的用途
  1. 动态供应存储卷:当用户创建一个 PersistentVolumeClaim(PVC)并指定了 StorageClass,Kubernetes 会自动根据 StorageClass 的配置动态地创建一个符合要求的存储卷。
  2. 指定存储类型和参数StorageClass 允许你定义不同类型的存储,如高性能 SSD、标准 HDD,或者不同的访问模式(ReadWriteOnceReadOnlyMany 等)。
  3. 跨云供应:在云环境(如 AWS、GCP、Azure)中,StorageClass 允许你指定使用哪种云提供的存储类型(如 AWS EBS、GCP Persistent Disk、Azure Disk)。
StorageClass 工作机制

当你定义一个 StorageClass,它会包含关于存储卷类型、参数以及如何动态创建卷的配置信息。然后,当应用需要存储时,用户可以通过 PersistentVolumeClaim 引用相应的 StorageClass,Kubernetes 会根据这个 StorageClass 自动创建一个符合要求的持久卷。

StorageClass 的关键属性
  1. provisioner:指定存储提供者,例如 AWS EBS、GCP Persistent Disk、NFS 等。provisioner 负责调用底层的存储系统来动态创建存储卷。
  2. parameters:可以设置存储系统的具体参数,例如磁盘类型、文件系统类型、区域等。
  3. reclaimPolicy:定义存储卷的回收策略,常见的值有 DeleteRetain,控制存储卷在使用完成后的处理方式。
  4. 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 的工作涉及以下几个关键组件:

  1. Kubernetes API Server
    • API Server 处理来自用户的存储卷请求,并将请求传递给 Kubernetes 的 kube-controller-managerkubelet
  2. kube-controller-manager
    • kube-controller-manager 中包含了 PersistentVolumeController,它负责处理存储卷的生命周期管理(例如创建和删除 PV)。
    • 它通过 CSI 接口与存储系统交互,并动态创建、删除、扩展存储卷。
  3. kubelet
    • kubelet 负责在每个节点上挂载和卸载存储卷。它通过调用 CSI 驱动来将存储卷挂载到 Pod 中运行的节点上。
  4. CSI 驱动
    • CSI 驱动是由存储提供商开发的插件,运行在 Kubernetes 集群中,用来处理实际的存储操作。它负责与底层存储系统(如 AWS EBS、GCE Persistent Disk、Ceph 等)进行交互。
    • CSI 驱动包括两个关键组件:
      • Controller:运行在控制平面中,负责处理卷的创建、删除、快照等操作。
      • Node Plugin:运行在每个节点上,负责挂载、卸载卷到节点上。
常见的 CSI 驱动
  1. AWS EBS CSI 驱动:用于动态创建和管理 AWS Elastic Block Store(EBS)。
  2. GCE Persistent Disk CSI 驱动:用于 Google Cloud 的持久磁盘(Persistent Disk)。
  3. Azure Disk CSI 驱动:用于 Azure 的托管磁盘(Managed Disk)。
  4. Ceph CSI 驱动:用于管理 Ceph 集群的存储卷,支持 Ceph RBD 和 CephFS。
  5. OpenStack Cinder CSI 驱动:用于 OpenStack 环境中的块存储服务 Cinder。
  6. NFS CSI 驱动:用于动态创建和挂载 NFS 存储卷。
CSI 的优势
  1. 灵活的存储支持:通过 CSI,Kubernetes 可以支持多种不同的存储系统,无论是本地存储、云存储还是分布式存储系统。
  2. 存储插件的独立性:存储提供商可以独立于 Kubernetes 核心开发、发布和维护他们的 CSI 插件。这减少了 Kubernetes 核心的复杂性,同时加速了存储插件的迭代。
  3. 标准化接口:CSI 提供了一套标准化的接口,简化了存储系统的集成流程,让 Kubernetes 可以无缝支持更多存储后端。
CSI内联卷

内联卷(inline volumes) 是 Kubernetes 中的一种存储卷定义方式,它允许在 Pod 规范中直接定义存储卷,而无需使用 PersistentVolume(PV)和 PersistentVolumeClaim(PVC) 这类独立的资源对象。与传统的使用 PVC 来声明持久存储不同,内联卷的定义与 Pod 的生命周期严格绑定,通常是用于临时存储或不需要持久化的存储需求。

在 CSI(Container Storage Interface) 中,CSI 内联卷 是指可以直接在 Pod 规范中声明 CSI 卷,而不通过 StorageClassPersistentVolumeClaim 来动态请求存储。这种方式适用于一些临时存储的场景,且并非所有 CSI 驱动都支持内联卷。

内联卷与标准 CSI 卷的区别
  • 标准 CSI 卷:通常需要先定义 StorageClass,然后通过 PersistentVolumeClaim(PVC) 申请存储,Kubernetes 通过 CSI 驱动动态创建 PersistentVolume(PV)。这些存储卷与 Pod 的生命周期分离,通常用于持久化存储。
  • 内联 CSI 卷:存储卷是直接在 Pod 规范中定义的,不涉及 StorageClassPVC,而是通过 CSI 驱动直接挂载。卷的生命周期与 Pod 绑定,通常不用于持久化存储。
CSI 内联卷的用途
  1. 临时存储需求:适用于一些不需要持久化存储的应用场景,例如缓存、临时数据存储等。
  2. 快速挂载特定卷:有时你只需在某个 Pod 中挂载一次性存储,而无需创建独立的 PVCPV
  3. 与 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 驱动时,用户可以通过 StorageClassPersistentVolumeClaim 动态创建和管理存储卷,满足不同存储需求。

CSI 的出现大大增强了 Kubernetes 在存储领域的扩展性和灵活性,为现代云原生应用提供了强大的存储支持。

调度器

节点选择

预选

  1. 节点上剩余的资源是否大于pod请求的资源
  2. 如果pod指定了NodeName,检查节点名称是否与nodeName匹配
  3. 节点上已经使用的port是否和pod申请的port冲突
  4. 过滤掉和pod指定的label不匹配的节点
  5. 已经mount的volume和pod指定的volume不冲突,除非他们都是只读

优选

  1. 通过cpu和mem的使用率来决定权重,倾向于资源使用比例更低的节点
  2. cpu和mem使用率接近的1:1的优先使用,和1策略一起使用
  3. 倾向于已经下载镜像的节点

节点选择器(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)

NodeAffinityNodeSelector 的增强版本,支持更灵活的调度规则,比如软约束(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 的基本组成包括以下四个部分:

  1. Role:定义特定命名空间内的权限。
  2. ClusterRole:定义集群范围内的权限,或者在多个命名空间中共享的权限。
  3. RoleBinding:将 Role 绑定到用户、组或服务账户,使其能够在特定命名空间内执行操作。
  4. ClusterRoleBinding:将 ClusterRole 绑定到用户、组或服务账户,使其能够在整个集群范围内执行操作。
RBAC 的工作原理
  1. RoleClusterRole:定义权限(即哪些操作可以执行)。
    • Role:作用域仅限于命名空间。
    • ClusterRole:可用于集群范围内的资源或共享给多个命名空间的资源。
  2. RoleBindingClusterRoleBinding:将角色绑定到主体(用户、组或服务账户)。
    • RoleBinding:在特定命名空间内,将 RoleClusterRole 绑定给主体,允许他们在该命名空间中操作。
    • ClusterRoleBinding:将 ClusterRole 绑定给主体,使其在整个集群范围内操作。
详细的 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 执行 getlistwatch 操作。

定义 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 定义了集群级别的权限,允许主体对 nodesdeployments 资源执行操作。

创建 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

ClusterRoleBindingClusterRole 绑定到用户、组或服务账户,使其在整个集群范围内生效。

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

除了将 RoleClusterRole 绑定给用户外,Kubernetes 也支持将它们绑定到 ServiceAccount(服务账户),服务账户是 Kubernetes 中 Pod 与 API Server 交互的主要身份。

当 Kubernetes 创建一个 ServiceAccount 时,它会为每个 ServiceAccount 生成三个文件,这些文件通常存放在挂载到 Pod 的 /var/run/secrets/kubernetes.io/serviceaccount 目录下。生成的文件如下:

  1. token 文件
    • 这是一个 JSON Web Token (JWT),用于身份验证。
    • Pod 中的应用程序可以通过读取这个 token 来访问 Kubernetes API Server。
    • 这个 token 被挂载到 Pod 中,并且可以用来代表 Pod 进行认证请求。
  2. ca.crt 文件
    • 这是集群的根证书,用于 API Server 的 TLS 证书验证。
    • Pod 中的应用程序使用这个证书来验证与 API Server 的安全连接,确保与 API Server 之间的通信是加密且可信的。
  3. 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 的最佳实践
  1. 最小权限原则:始终遵循最小权限原则,为用户和服务账户授予最小化的必要权限,避免滥用或权限扩大。
  2. 隔离命名空间的权限:使用 Role 而不是 ClusterRole,确保权限只作用于特定命名空间,而不是整个集群,减少潜在的安全风险。
  3. 定期审查权限:定期检查和审查 RoleBindingClusterRoleBinding,确保没有未使用的过期角色绑定或不再需要的权限。
  4. 服务账户分离职责:为每个 Pod 或服务组件分配不同的 ServiceAccount,并使用 RoleBindingClusterRoleBinding 进行精细化权限控制。

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:通过动态准入控制,用户可以使用 MutatingAdmissionWebhookValidatingAdmissionWebhook 实现自定义的准入控制逻辑。这允许集成外部系统来动态地修改或验证资源配置。

    示例:

    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” 安全保障总结

  1. 认证:确保请求的合法来源。
  2. 授权:确保请求有权限执行特定操作。
  3. 准入控制:在请求进入系统前,进一步应用安全策略或做出验证,确保符合集群的配置和安全要求。

通过认证、授权和准入控制的组合,Kubernetes 确保集群中的每个请求都经过严格的身份验证、权限校验和安全策略检查,提供了全面的安全保障体系。