技术方案之 基于kata direct volume特性, 实现安全容器KataContainer的 CSI block volume直通方案
在 Kubernetes
中集成 Kata Containers
可以为容器运行时提供更好的安全性
和隔离性
,但在存储方面
仍然还存在一些限制与不足。
目前方案:virtiofs协议
Kata Containers
在 2.4 版本之前,挂载 PV
的整个过程与 CSI
之间是没有任何交互的,而且也不能直接使用 CSI
挂载的 PV
,只能通过 virtiofs 协议
将宿主机上的存储卷以文件共享的方式提供给 Kata Containers
虚拟机中的 container
使用。
virtiofs 协议
的实现方式如下图所示:
这种方式虽然能够解决 PV
存储挂载的问题,但与直接在宿主机上使用存储卷相比,由于 virtiofs
实现方式的 I/O
路径过长,会带来不少的性能损耗。
经过环境实测,总结出以下几方面问题:
-
稳定性方面
:在实际环境中,对virtiofs
共享的盘进行fio 测试
,我们经常能观察到 io 不连续的现象,并且在对盘进行加压测试时,会影响到容器中其他进程的响应,比如 ssh 进程响应会超时; -
性能方面
:与直接在宿主机上使用存储卷相比,virtiofs
共享盘在iops
和带宽
方面有不少差距; -
功能方面
:virtiofs
共享盘方式无法在线调整PV
大小,需要通过重启Pod
才能使VM
能感知到PV
大小变化;
优化方案
基于 virtiofs 现存问题,Kata Containers
在 2.4 版本提供了 direct assigned volume
功能,能够将文件系统挂载操作从宿主机移动到 Guest
中,相当于是一种 block volume
直通方案。和 virtiofs
相比,不仅能提供接近直接宿主机上使用存储卷的性能,而且还能支持 native FS
。由于不需要借助于 virtiofs
,在使用上会更加稳定,也能带来安全性方面的提升。同时,这个特性还支持在线修改 PV
存储大小。
Kata Containers 存储卷直通
方案如下图所示:
具体kata direct assigned volume
特性设计:https://github.com/kata-containers/kata-containers/blob/main/docs/design/direct-blk-device-assignment.md
在 CSI 中实现 direct volume
前提条件
-
由于需要在
host
上创建文件,CSI node
服务在部署时需要把/run/kata-containers/shared/direct-volumes
目录以hostpath
方式挂载到pod
里。 -
CSI
在挂载时需要明确知道所挂载的volume
是否是以direct volume
这种方式挂载,所以需要有一种机制能通知到CSI
,可以借助以下三种方式:通过
StorageClass
指定direct volume
属性在
PVC
对象里通过annotation
打上direct volume
属性,同时 CSI 插件需要打开--extra-create-metadata
属性来帮助CSI
能从K8s apiserver
查询到PVC
的annotation
信息通过查询
Pod
的runtimeclass
信息来判断是否是Kata direct volume
挂载
实现步骤
以下步骤主要都在 CSI NodePublishVolume
接口里实现:
- 把远程存储的
block device
挂载到host
上。 - 根据实际需求场景对
block device
做文件系统格式化。 - 生成
mountinfo.json
信息并把mountinfo.json
信息传递给 Kata。
mountInfo
的内容是 json 格式,主要数据结构如下:
// MountInfo contains the information needed by Kata to consume a host block device and mount it as a filesystem inside the guest VM.
type MountInfo struct {
// The type of the volume (ie. block)
VolumeType string `json:"volume-type"`
// The device backing the volume.
Device string `json:"device"`
// The filesystem type to be mounted on the volume.
FsType string `json:"fstype"`
// Additional metadata to pass to the agent regarding this volume.
Metadata map[string]string `json:"metadata,omitempty"`
// Additional mount options.
Options []string `json:"options,omitempty"`
}
其中 CSI 侧主要需要传递以下三个字段信息即可,例如:
mountInfo := &volume.MountInfo{
VolumeType: "block", // 设备类型
Device: dev/sdd, // 块设备路径
FsType: ext4, // 文件系统类型
}
volume.Add("/run/kata-containers/shared/direct-volumes/volume-path(base64加密)/", mountInfo)
CSI
负责把 mountinfo.json
传递给 Kata
,Kata
会在容器所在 host
的 /run/kata-containers/shared/direct-volumes/volume-path(base64加密)/
目录下生成 mountinfo.json
文件,目前 CSI
有两种方式可以传递 mountinfo.json
信息
- 通过调用
kata-container
代码里direct volume
模块的add
方法传递mountinfo.json
信息,部分代码实例如下:
import (
"encoding/json"
volume "github.com/kata-containers/kata-containers/src/runtime/pkg/direct-volume"
"google.golang.org/grpc/status"
klog "k8s.io/klog/v2"
)
// NodePublishVolume 中发布
func AddDirectVolume(volumePath, device, fsType string) error {
mountInfo := &volume.MountInfo{
VolumeType: "block",
Device: device,
FsType: fsType,
}
mi, err := json.Marshal(mountInfo)
if err != nil {
klog.Errorf("addDirectVolume - json.Marshal failed: ", err.Error())
return status.Errorf(codes.Internal, "json.Marshal failed: %s", err.Error())
}
if err := volume.Add(volumePath, string(mi)); err != nil {
klog.Errorf("addDirectVolume - add direct volume failed: ", err.Error())
return status.Errorf(codes.Internal, "add direct volume failed: %s", err.Error())
}
klog.Infof("add direct volume done: %s%s", volumePath, string(mi))
return nil
}
// NodeUnpublishVolume 中remove掉
if err := volume.Remove(targetPath); err != nil {
log.Errorf("NodeUnpublishVolume: kata direct volume remove failed: %s", err.Error())
}
- 通过
kata-runtime CLI
命令传递mountinfo.json
信息:
$ kata-runtime direct-volume add --volume-path [volumePath] --mount-info [mountinfo.json]
$ kata-runtime direct-volume remove --volume-path [volumePath] --mount-info [mountinfo.json]
最后会在容器所在 host 的 /run/kata-containers/shared/direct-volumes/volume-path(base64加密)/
目录下生成 mountinfo.json
文件,然后 Kata Containers
会在启动容器时检查该目录是否有 mountinfo.json
文件并解析该文件,同时更新容器 spec
中 mount
信息,将直通卷的信息加入进去,然后将修改后的 spec
传给 kata-agent
;
方案限制:
1.使用 direct volume
方式的 PV
只能给一个 Pod
使用,所以在创建 PVC
时需要指定 accessMode
为 ReadWriteOnce
2.使用 direct volume
方式不支持更高级的 volume
功能,比如:fsGroup
、fsGroupChangePolicy
和 subPath
未来:kata 联动 kubelet/kube-apiserver,实现CSI 卷的运行时辅助挂载
社区未通过的最终方案:KEP-2857:持久卷的运行时辅助安装
Demo
---
apiVersion: v1
kind: Pod
metadata:
name: app
spec:
runtime-class: kata-qemu # 方式一:通过设置runtime-class,使用kata 运行时; 其下面的卷都是直通卷
containers:
- name: app
image: centos
command: ["/bin/sh"]
args: ["-c", "while true; do echo $(date -u) >> /data/out.txt; sleep 5; done"]
volumeMounts:
- name: persistent-storage
mountPath: /data
volumes:
- name: persistent-storage
persistentVolumeClaim:
claimName: ebs-claim
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
annotations:
skip-hostmount: "true" # 方式二:通知csi为直通卷
name: ebs-claim
spec:
accessModes:
- ReadWriteOncePod
volumeMode: Filesystem
storageClassName: ebs-sc
resources:
requests:
storage: 1Gi
---
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: ebs-sc
provisioner: local.csi.cmss.com # 方式三:特定的csi driver实现 kata direct assigned volume
volumeBindingMode: WaitForFirstConsumer
parameters:
csi.storage.k8s.io/fstype: ext4
基于kata容器直接分配卷的本地csi驱动程序
代码仓库:https://github.com/kubeservice-stack/kata-local-csi-driver
「如果这篇文章对你有用,请随意打赏」
如果这篇文章对你有用,请随意打赏
使用微信扫描二维码完成支付