informer
提供一个保持更新的 k8s 资源的本地缓存。
informer
#
flannel 是由 CoreOS 开发的一个简单易用的容器网络插件
网络是 k8s 中至关重要的一部分, 这里以简单的 flannel 为例做深入分析
以下介绍在 chart 方式部署的 flannel
flanneld 进程以 daemonset/kube-flannel-ds 方式运行在所有 node 上, 负责从提前配置好的网络池中分配子网租约 (subnet lease) 给 node.
flanneld 使用 k8s api 或者 etcd 存储网络配置、分配的子网和任何补充数据(如 node 的 public ip), 在 k8s 中使用一般不会单独提供 etcd 去存储这些数据.
几个名词解释:
subnet
: 对应 node.spec.podCIDR
。backend
: 负责 node
之间 pod
通讯的后端。flanneld 进程通过监听 node
资源来生成 subnet event
, 然后在对应 backend
的 handleSubnetEvents
方法中处理逻辑,对于 vxlan backend
主要是按顺序设置 arp
, fdb
和 route
来实现pod跨节点通讯。
RKE2 自定义调度器配置
NodeResourcesFit
是一个调度插件, 检查节点是否拥有 Pod 请求的所有资源, 得分可以使用以下三种策略之一:
LeastAllocated
(默认)、MostAllocated
和 RequestedToCapacityRatio
实现了多个扩展点: preFilter
、filter
、preScore
、score
我这里自定义使用 MostAllocated
策略, 优选分配比率较高的节点
# /etc/rancher/rke2/kube-scheduler-config.yaml
apiVersion: kubescheduler.config.k8s.io/v1
kind: KubeSchedulerConfiguration
clientConnection:
kubeconfig: /var/lib/rancher/rke2/server/cred/scheduler.kubeconfig
profiles:
- schedulerName: default-scheduler
pluginConfig:
- name: NodeResourcesFit
args:
scoringStrategy:
type: MostAllocated
resources:
- name: cpu
weight: 1
- name: memory
weight: 1
修改 /etc/rancher/rke2/config.yaml
kube-scheduler-arg:
+ - config=/etc/rancher/rke2/kube-scheduler-config.yaml
会重新生成 kube-scheduler 的 static pod manifest 文件 /var/lib/rancher/rke2/agent/pod-manifests/kube-scheduler.yaml
会挂载 /etc/rancher/rke2/kube-scheduler-config.yaml
文件到 pod 中
CRI 全称为 Container Runtime Interface
(容器运行时接口), 是 kubelet
与 容器运行时进行通讯的主要协议。
是 k8s 根据 OCI runtime-spec
cri-api 主要定义了六个接口:
staging/src/k8s.io/cri-api/
├── pkg
│ ├── apis
│ │ ├── runtime
│ │ │ └── v1
│ │ │ ├── api.pb.go {RuntimeServiceClient RuntimeServiceServer ImageServiceClient ImageServiceServer}
│ │ │ ├── api.proto
│ │ │ └── constants.go
│ │ ├── services.go {RuntimeService ImageManagerService}
CNI 全称 Container Network Interface
, 容器网络接口, cni 插件是可执行文件, 一般位于 /opt/cni/bin/
目录
在 k8s 中, kubelet 调用 cri 创建 sandbox 时(RunPodSandbox)会先去创建 network namespace, 然后创建 pause 和 其他容器并将容器加入到同一个 network namespace 中
cni spec 文档: https://www.cni.dev/docs/spec/
有如下环境变量参数:
CNI_COMMAND
: 对应操作 ADD
, DEL
, CHECK
, or VERSION
.CNI_CONTAINERID
: 容器 idCNI_NETNS
: 如 /var/run/netns/[nsname]
CNI_IFNAME
: 要在容器中创建的接口名称, 一般容器中都是 eth0
CNI_ARGS
: 额外的 kv 参数, 如 FOO=BAR;ABC=123
CNI_PATH
: 搜索 cni plugin 可执行文件的目录主要是 cmdAdd
和 cmdDel
两个函数, 对应 CNI spec 中的 ADD
和 DEL
两个主要操作
CSI 全称为 Container Storage Interface
, 容器存储接口
要实现一个第三方的 csi driver 需要实现下面的 gRPC service csi spec
// 如果 NodeServer 和 ControllerServer 对应服务运行在不同 pod 中, 那么两个服务都要实现 IdentityServer
type IdentityServer interface {
// 用来获取插件名称
GetPluginInfo(context.Context, *GetPluginInfoRequest) (*GetPluginInfoResponse, error)
GetPluginCapabilities(context.Context, *GetPluginCapabilitiesRequest) (*GetPluginCapabilitiesResponse, error)
Probe(context.Context, *ProbeRequest) (*ProbeResponse, error)
mustEmbedUnimplementedIdentityServer()
}
type ControllerServer interface {
// 创建 volume, 如 ceph 创建一个 rbd 或者 hostpath 创建一个目录
CreateVolume(context.Context, *CreateVolumeRequest) (*CreateVolumeResponse, error)
// 删除 volume, 如 ceph 删除一个 rbd 或者 hostpath 删除一个目录
DeleteVolume(context.Context, *DeleteVolumeRequest) (*DeleteVolumeResponse, error)
// 将 volume attach 到 node 上, 如 rbd 通过 rbd map 命令 attach, 成功后 node 上会多出一个 rbdx 的 block 设备
ControllerPublishVolume(context.Context, *ControllerPublishVolumeRequest) (*ControllerPublishVolumeResponse, error)
// 将 volume 从 node 上 detach, 如 rbd 通过 rbd unmap 命令 detach
ControllerUnpublishVolume(context.Context, *ControllerUnpublishVolumeRequest) (*ControllerUnpublishVolumeResponse, error)
ValidateVolumeCapabilities(context.Context, *ValidateVolumeCapabilitiesRequest) (*ValidateVolumeCapabilitiesResponse, error)
// 列出所有 volume
ListVolumes(context.Context, *ListVolumesRequest) (*ListVolumesResponse, error)
GetCapacity(context.Context, *GetCapacityRequest) (*GetCapacityResponse, error)
ControllerGetCapabilities(context.Context, *ControllerGetCapabilitiesRequest) (*ControllerGetCapabilitiesResponse, error)
CreateSnapshot(context.Context, *CreateSnapshotRequest) (*CreateSnapshotResponse, error)
DeleteSnapshot(context.Context, *DeleteSnapshotRequest) (*DeleteSnapshotResponse, error)
ListSnapshots(context.Context, *ListSnapshotsRequest) (*ListSnapshotsResponse, error)
ControllerExpandVolume(context.Context, *ControllerExpandVolumeRequest) (*ControllerExpandVolumeResponse, error)
ControllerGetVolume(context.Context, *ControllerGetVolumeRequest) (*ControllerGetVolumeResponse, error)
ControllerModifyVolume(context.Context, *ControllerModifyVolumeRequest) (*ControllerModifyVolumeResponse, error)
mustEmbedUnimplementedControllerServer()
}
// 这些会被 kubelet 调用
type NodeServer interface {
// format (如果没format), mount 到 node 的 global directory
NodeStageVolume(context.Context, *NodeStageVolumeRequest) (*NodeStageVolumeResponse, error)
// umount
NodeUnstageVolume(context.Context, *NodeUnstageVolumeRequest) (*NodeUnstageVolumeResponse, error)
// mount --bind 到 pod directory
NodePublishVolume(context.Context, *NodePublishVolumeRequest) (*NodePublishVolumeResponse, error)
// umount --bind
NodeUnpublishVolume(context.Context, *NodeUnpublishVolumeRequest) (*NodeUnpublishVolumeResponse, error)
NodeGetVolumeStats(context.Context, *NodeGetVolumeStatsRequest) (*NodeGetVolumeStatsResponse, error)
NodeExpandVolume(context.Context, *NodeExpandVolumeRequest) (*NodeExpandVolumeResponse, error)
NodeGetCapabilities(context.Context, *NodeGetCapabilitiesRequest) (*NodeGetCapabilitiesResponse, error)
NodeGetInfo(context.Context, *NodeGetInfoRequest) (*NodeGetInfoResponse, error)
mustEmbedUnimplementedNodeServer()
}
Sidecar Containers 是一系列标准容器,用于简化 CSI 插件的开发和部署
...构建多平台容器镜像
buildx 是 Docker 的一个 CLI 插件,用于扩展来自于 Moby BuildKit 项目的构建功能。
注意:buildx 需要 Docker 19.03 或更高版本。
BuildKit是一个build引擎,它接收一个配置文件(Dockerfile),并转化成一个制品(容器镜像或其他制品)。相较与传统的build具有多阶段并发构建、更好的layer缓存支持等优点,Dockerfile中的RUN指令会被runc执行。
Docker Engine 从 23.0.0 版本开始默认在Linux上使用Buildx和BuildKit为builder。
一个 builder 是一个 BuildKit 守护进程,BuildKit是build引擎,它解决Dockerfile中的构建步骤,以生成容器镜像或其他制品。
Build 驱动有多种,例如 docker
、docker-container
、kubernetes
、remote
等。
docker
使用捆绑在Docker守护进程中的BuildKit库。默认的Builder使用的该驱动。docker-container
使用Docker创建一个专用的BuildKit容器。kubernetes
在Kubernetes集群中创建BuildKit pods。remote
直接连接到手动管理的BuildKit守护进程。Feature | docker | docker-container | kubernetes | remote |
---|---|---|---|---|
Automatically load image | ✅ | |||
Cache export | ✓* | ✅ | ✅ | ✅ |
Tarball output | ✅ | ✅ | ✅ | |
Multi-arch images | ✅ | ✅ | ✅ | |
BuildKit configuration | ✅ | Managed externally |
docker engine 会自动创建一个默认的 builder 实例,例如 default
。默认的驱动是 docker
,不支持多平台构建。
一个 namespace 将全局系统资源封装在一个抽象中,使得 namespace 内的进程看起来像是拥有该全局资源的独立实例。 对全局资源的更改只对属于同一 namespace 的其他进程可见。
Linux 上可用的 namespace 有:
namespace 类型 | 隔离内容 |
---|---|
cgroup | Cgroup 根目录 |
ipc | System V IPC, POSIX 消息队列 |
network | 网络设备、协议栈、端口等 |
mount | 挂载点 |
pid | 进程 ID |
time | 启动时间和单调时钟 |
user | 用户和组 ID |
uts | 主机名和 NIS 域名 |
目前主要用 cgroup v2, 下面记录 k8s 如何通过 cgroup v2 管理 cpu 和 memory 资源
Guaranteed
: /sys/fs/cgroup/kubepods.slice/
Burstable
: /sys/fs/cgroup/kubepods.slice/kubepods-burstable.slice
BestEffort
: /sys/fs/cgroup/kubepods.slice/kubepods-besteffort.slice
这里有个注意点:根据 pod QOS 类型不同, cgroup 创建的目录也不一样, 但是 container runtime 是不知道 kubelet 定义的 QOS 级别的, 所以 kubelet 通过 CRI 调用 container runtime 时会携带 CgroupParent
来指定对应 QOS 对应的 cgroup parent.
最终 container runtime 调用 runc 创建 sandbox 和 container, 并且会设置对应的 cgroup 和设置 cpu.max
与 memory.max
, 将 sandbox 和 container 启动进程的 PID 添加进 cgroup.procs
根据创建 bridge 网络和创建虚拟机时使用 cloudinit 初始化创建虚拟机, 并配置静态ip如下
主机名 | 配置 | ip (域名) | 系统盘 / 数据盘 |
---|---|---|---|
k8s-node01 | 8核16G | 192.168.1.218 (lb.k8s.lan ) | 50GB / 100GB*1 |
k8s-node02 | 8核16G | 192.168.1.219 | 50GB / 100GB*1 |
k8s-node03 | 8核16G | 192.168.1.220 | 50GB / 100GB*1 |
在 k8s-node01 节点执行
# 初始化 rke2 配置文件
mkdir -p /etc/rancher/rke2
cat <<EOF > /etc/rancher/rke2/config.yaml
tls-san:
- lb.k8s.lan
write-kubeconfig-mode: "0600"
disable-cloud-controller: true
# cni 单独部署, 如无特殊需求, 这里也可以直接指定 flannel 或 calico
cni: none
debug: true
# 指定 kube-scheduler 自定义参数, 会自动覆盖到 /var/lib/rancher/rke2/agent/pod-manifests/kube-scheduler.yaml
kube-scheduler-arg:
- v=4
- bind-address=0.0.0.0
kube-controller-manager-arg:
- bind-address=0.0.0.0
etcd-expose-metrics: true
EOF
curl -sfL https://rancher-mirror.rancher.cn/rke2/install.sh | INSTALL_RKE2_MIRROR=cn sh -
systemctl enable rke2-server.service
systemctl start rke2-server.service
tls-san
#tls-san
在 server 的 TLS 证书中增加了多个地址作为 Subject Alternative Name
, 这样就可以通过 lb.k8s.lan
和 各个 server 节点 ip 访问 apiserver 服务.