# k8s-csi-fs **Repository Path**: j2soft/k8s-csi-fs ## Basic Information - **Project Name**: k8s-csi-fs - **Description**: 一个kubernetes CSI 插件,支持NFS、SSHFS 两种远程文件系统。通过文件来模拟固定容量的卷(Volume)。 - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 1 - **Created**: 2024-05-27 - **Last Updated**: 2024-05-27 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # intro 一个kubernetes CSI 插件,支持NFS、SSHFS 两种远程文件系统。通过文件来模拟固定容量的卷(Volume)。 CSI Version: 1.8 以[MIT许可证](https://www.mit-license.org/)开源。 TODOS: + [OK] 搭建开发环境 + [OK] 测试文件作为磁盘 + [OK] 实现identify 服务 + [OK] 实现controller 服务 + [OK] 实现node 服务 + [OK] 单元测试通过 + [OK] 解决mount时文件名过长的不能挂载的问题 + [OK] 编写yaml与在k8s中测试 + [OK] 支持分页获取卷列表 + [] 支持parameter方式创建卷 (comming soon) + [OK] 支持sshfs方式的卷 + [OK] 支持s3fs方式的卷 ![](./doc/README.png) # 使用方法 插件已上编译并上传dockerhub, 目前仅支持x64平台。 参考 `yamls/deploy.yaml` 修改其中的环境变量即可, 修改后使用kubectl 部署插件 即可 。 ```shell kubectl apply -f yamls/deploy.yaml ``` 你也可以用以下命令测试插件是否启用成功 ```shell kubectl apply -f yamls/example.yaml ``` 下面是在开发环境上测试的结果。插件已经能够在k8s集群上正常工作。 ![](./doc/success-1.png) ![](./doc/success-2.png) ![](./doc/success-3.png) 支持的环境变量 ```shell REPO_TYPE # 可取值为NFS、SSH、S3 REPO_NFS_URI # 例如 127.0.0.1:/nfsroot REPO_SSH_URI # 例如 cloudea@192.168.39.1:/home/cloudea/test/volume REPO_SSH_PASS # 例如 15115103140 REPO_S3_URI # 例如 http://192.168.39.1:9000/ REPO_S3_BUCKET # 例如 test REPO_S3_ACCESS_KEY # 例如 minioadmin REPO_S3_SECRET_KEY # 例如 minioadmin ``` # 开发环境 + ubuntu 22.04 x64 + go 1.20 + protoc 3.12.4 + go plugin ```shell # 安装protobuf 编译器 apt install -y protobuf-compiler protoc --version # 安装go 插件 go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28 go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2 ``` # 运行 ## 使用makefile(推荐) ```shell make run # 运行 make test # 单元测试 make # 打包镜像 ``` ## 使用go原生 运行 ```shell # 编译proto protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative driver/proto/csi.proto # 运行 go get . go run . # 预览 grpcui -plaintext localhost:50051 ``` 黑盒测试 ```shell go test -run ^TestMyDriver$ cloudea.com/k8s-csi-fs/test ``` 构建 ``` go build . ``` # 手工RPC测试 ``` grpcui -plaintext -unix -bind=0.0.0.0 /tmp/csi.sock ``` # 相关资料 ## 开发过程 ### 实现插和件服务。 有**两类**插件,分别是: + 结点插件:每结点在跑 + 控制器插件:可以跑在任方地何 需要实现三类务服 + Identity Service: 结点插件和控制器插件都需要实现 + Create/Delete Volume 创建/删除卷 + Publish/Unpublish Volume 在某个结点上挂载或缷载(本质上是赋节点的访问权限) + Node Service: 只要实现`NodePublishVolume`,`NodeUnpublishVolume`,`NodeGetCapabilities` 三个必需的 + Stage/UnStage Volume 格式化/取消格式化 + Publish/UnPublish Volume 挂载到Pod或/取消挂载 + Controller Service(可选), > Plugins SHOULD expose all RPCs for an interface: Controller plugins SHOULD implement all RPCs for the Controller service. Unsupported RPCs SHOULD return an appropriate error code that indicates such (e.g. CALL_NOT_IMPLEMENTED). 典型的生命周期 ```text CreateVolume +------------+ DeleteVolume +------------->| CREATED +--------------+ | +---+----^---+ | | Controller | | Controller v +++ Publish | | Unpublish +++ |X| Volume | | Volume | | +-+ +---v----+---+ +-+ | NODE_READY | +---+----^---+ Node | | Node Stage | | Unstage Volume | | Volume +---v----+---+ | VOL_READY | +---+----^---+ Node | | Node Publish | | Unpublish Volume | | Volume +---v----+---+ | PUBLISHED | +------------+ Controller Controller Publish Unpublish Volume +------------+ Volume +------------->+ NODE_READY +--------------+ | +---+----^---+ | | Node | | Node v +++ Publish | | Unpublish +++ |X| <-+ Volume | | Volume | | +++ | +---v----+---+ +-+ | | | PUBLISHED | | | +------------+ +----+ Validate Volume Capabilities ``` 并发性:对于同一个卷,同一时间只会有一个请求。 数据长度:string最长128bytes和map[string]string最长4kB 超时:超时会重试 ### 使用单元测试 使用sanity 作为单元测试。[](https://kubernetes-csi.github.io/docs/unit-testing.html) ### 编写yaml `deploy.yaml` 一键部署插件 `example.yaml` 一键启动使用样例 注意私有仓镜像的写法 ``` yaml apiVersion: v1 kind: Pod metadata: name: private-reg spec: containers: - name: private-reg-container image: # 例如 your.private.registry.example.com/janedoe/jdoe-private:v1 imagePullSecrets: - name: regcred # 参考后面 ``` ### 部署到k8s 1. 部署一个内网registry给k8s用。 [https://docs.docker.com/registry](https://docs.docker.com/registry) ```shell docker run -d -p 5000:5000 --name registry registry:2 ``` 在`/etc/docker/daemon.json`中添加 `"insecure-registries":["192.168.39.1:5000"]` 并重启docker : `systemctl restart docker` > 注意,在k8s中也会出现因为不是https而不能访问registry的问题 > https://goharbor.io/ 是另一个私有镜像仓库 2. 打包镜像并推到registry ```shell docker build -t k8s-csi-fs:1.0 . docker image tag k8s-csi-fs:1.0 192.168.39.1:5000/k8s-csi-fs:1.0 docker push 192.168.39.1:5000/k8s-csi-fs:1.0 docker pull 192.168.39.1:5000/k8s-csi-fs:1.0 #测试从远端拉取镜像 ``` 3. k8s 部署 [https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/) ``` kubectl create secret docker-registry regcred --docker-server= --docker-username= --docker-password= --docker-email= # 创建私有登录凭证 kubectl apply -f deploy.yaml kubectl apply -f example.yaml ``` ## NFS或SSHFS地址的配置方法 + 法1:使用环境变量,因此可以在执行安装存储插件时的yaml时配置(本文的方法) + 法2:使用StorageClass中的parameter属性参数 (实际应该实现的方法) ## 使用文件作为磁盘。 在一个目录上挂载包含包含文件系统的文件的步骤: 1. 使用一个循环设备(/dev/loop)连接文件(使用命令 losetup) 2. 在目录上挂载该循环设备(使用命令 mount) ```shell losetup -f #查找空闲设备 ## 法1 dd if=/dev/zero of=floppy.img bs=512 count=2880 # 创建1.44m的空文件 losetup /dev/loop1 floppy.img # 把文件虚拟成块设备 # 如果需要分区:fdisk /dev/loop1 mount /dev/loop1 /tmp #挂载,挂载要需前需要创建文件系统,如法2 umount /tmp losetup -d /dev/loop1 ## 法2 dd if=/dev/zero of=loopfile.img bs=1G count=1 mkfs.ext4 loopfile.img file loopfile.img #查看文件类型 mount -o loop loopfile.img /mnt/loopback # -o loop 内部默认将文件和loop0 挂载起来了 umount /mnt/loopback ``` # 参考 1. https://github.com/digitalocean/csi-digitalocean 2. https://kubernetes-csi.github.io/docs/introduction.html 3. https://github.com/container-storage-interface/spec/blob/master/spec.md#rpc-interface CSI 规范