前置概念
1.spdkcsi : spdk提供的csi对接插件,其本身不在spdk代码中,为spdk为k8s-csi提供的对接插件
2.spdk-server : 真正提供存储服务的spdk服务器节点,接收业务端connect/login/io请求
3.spdk-server-api : spdk-serve本身提供的是基于socket文件的rpc统信,为对外暴露更通用的配置接口,spdk提供了rpc_http_proxy.py代理,该代理的作用是将apt-restful请求转换为spdk-server-rpc请求
4.调用顺序: k8s-> spdkcsi -> spdk-server-api -> spdk-server-rpc -> spdk-server
1.配置文件解析
配置文件用于告知spdk插件,spdk-server地址和支持的传输协议,用于spdkcsi插件向spdk-server(以rpc_http_proxy.py方式运行的http代理转发给spdk-server)发送配置请求
配置spdk节点地址和传输协议支持通过deploy时的配置项ConfigMap
# cat ./deploy/kubernetes/config-map.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: spdkcsi-cm
data:
config.json: |-
{
"nodes": [
{
"name": "k8s-1_24-oe2203-214-spdk",
"rpcURL": "http://10.24.8.214:9009", # spdk-server的rpc_http_proxy地址,该路径仅用来走配置请求
"targetType": "nvme-tcp", # 当前spdk-server支持的传输协议
"targetAddr": "10.24.8.214" # 当前spdk-server对外暴露的IO target地址,该路径仅用来走IO请求,用于后面connect/login使用存储资源
}
]
}
该配置项通过controller部署文件中spdkcsi-config配置进入controller容器的/etc/spdkcsi-config/config.json文件
# cat ./deploy/kubernetes/controller.yaml
...
- name: spdkcsi-controller
image: spdkcsi/spdkcsi:canary
imagePullPolicy: "IfNotPresent"
args:
- "--v=5"
- "--endpoint=unix:///csi/csi-provisioner.sock"
- "--nodeid=$(NODE_ID)"
- "--controller"
env:
- name: NODE_ID
valueFrom:
fieldRef:
fieldPath: spec.nodeName
volumeMounts:
- name: socket-dir
mountPath: /csi
- name: spdkcsi-config # 管理下面的卷列表定义中的spdkcsi-config
mountPath: /etc/spdkcsi-config/ # 卷挂载位置
readOnly: true
volumes: # 此处定义卷列表
- name: socket-dir
emptyDir:
medium: "Memory"
- name: spdkcsi-config # 定义卷名称
configMap: # 实际对应为configMap类型
name: spdkcsi-cm # 在configMap中的名称
...
controller在启动时读取该配置文件
其中SPDKCSI_CONFIG是环境变量如果指定具体的配置文件值
/etc/spdkcsi-config/config.json是如果没有环境变量,则默认使用的值
当前实际代码运行时并没有设置SPDKCSI_CONFIG环境变量的地方,仅在test程序中,通过该环境变量传递参数
实际使用controllerserver_test.go时,可以临时创建/etc/spdkcsi-config/config.json
# cat pkg/spdk/controllerserver.go**
func newControllerServer(d *csicommon.CSIDriver) (*controllerServer, error) {
...
# 此处通过配置文件,注册所有的spdk-server配置节点
config, err := util.NewCSIControllerConfig("SPDKCSI_CONFIG", "/etc/spdkcsi-config/config.json")
if err != nil {
return nil, err
}
for i := range config.Nodes {
server.spdkNodeConfigs[config.Nodes[i].Name] = &config.Nodes[i]
}
...
}
2.请求认证
认证用于spdkcsi向spdk-servcer-api执行配置请求时的用户名和密码
以下是认证配置文件,如果有多个spdk-server-api可以写多个
# cat ./deploy/kubernetes/secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: spdkcsi-secret
stringData:
secret.json: |-
{
"rpcTokens": [
{
"name": "k8s-1_24-oe2203-214-spdk", # 此名称和config-map.yaml配置中的name对应
"username": "spdkcsiuser", # 启动rpc_http_proxy.py时指定,如/root/spdk/scripts/rpc_http_proxy.py 10.24.8.214 9009 spdkcsiuser spdkcsipass
"password": "spdkcsipass"
}
]
}
该配置项通过定义sc的时候传入
# cat deploy/kubernetes/storageclass.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: spdkcsi-sc
provisioner: csi.spdk.io
parameters:
fsType: ext4
csi.storage.k8s.io/provisioner-secret-name: spdkcsi-secret # 此处定义使用secret
csi.storage.k8s.io/provisioner-secret-namespace: default
reclaimPolicy: Delete
volumeBindingMode: Immediate
后续在逻辑请求时,请求函数的参数将传递该参数给csi的实现,如
# 创建卷函数入口
func (cs *ControllerServer) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest) (*csi.CreateVolumeResponse, error) {
# csi.CreateVolumeRequest定义中会将secrete传递
type CreateVolumeRequest struct {
...
// Secrets required by plugin to complete volume creation request.
// This field is OPTIONAL. Refer to the `Secrets Requirements`
// section on how to use this field.
Secrets map[string]string `protobuf:"bytes,5,rep,name=secrets,proto3" json:"secrets,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
...
在spds-csi中,通过节点和认证名字的配对,确定哪个节点使用哪个认证
# cat pkg/spdk/controllerserver.go
func (cs *controllerServer) getSpdkNode(nodeName string, secrets map[string]string) (util.SpdkNode, error) {
...
for i := range spdkSecrets.Tokens {
token := spdkSecrets.Tokens[i]
if token.Name == nodeName { # 在此处进行config-map.yaml和secret.yaml名字对比,找到对应的用户信息
spdkNode, err := util.NewSpdkNode(node.URL, token.UserName, token.Password, node.TargetType, node.TargetAddr)
if err != nil {
klog.Errorf("failed to create spdk node %s: %s", node.Name, err.Error())
}
return spdkNode, nil
}
}
...
}