Kubernetes 权威指南 4 源码:

​链接​


  • Kubernetes CSI

Kubernetes CSI

Kubernetes 从 1.9 版本开始引入容器存储接口 Container Storage Interface(CSI)机制,用于在 Kubernetes 和外部存储系统之间建立一套标准的存储管理接口,通过该接口为容器提供存储服务。CSI 到 Kubernetes 1.10 版本升级为 Beta 版,到 Kubernetes 1.13 版本升级为 GA 版,已逐渐成熟。

Kubernetes 通过 PV、PVC、Storageclass 已经提供了一种强大的基于插件的存储管理机制,但是各种存储插件提供的存储服务都是基于一种被称为 "in-tree"(树内)的方式提供的,这要求存储插件的代码必须被放进 Kubernetes 的主干代码库中才能被 Kubernetes 调用,属于紧耦合的开发模式。这种 "in-tree" 方式会带来一些问题:


  • 存储插件的代码需要与 Kubernetes 的代码放在同一代码库中,并与 Kubernetes 的二进制文件共同发布。
  • 存储插件代码的开发者必须遵循 Kubernetes 的代码开发规范。
  • 存储插件代码的开发者必须遵循 Kubernetes 的发布流程,包括添加对 Kubernetes 存储系统的支持和错误修复。
  • Kubernetes 社区需要对存储插件的代码进行维护,包括审核、测试等工作。
  • 存储插件代码中的问题可能会影响 Kubernetes 组件的运行并且很难排查问题。
  • 存储插件代码与 Kubernetes 的核心组件(kubelet 和 kube-controller-manager)享有相同的系统特权权限,可能存在可靠性和安全性问题。

Kubernetes 已有的 Flex Volume 插件机制视图通过外部存储暴露一个基于可执行程序(exec)的 API 来解决这些问题。尽管它允许第三方存储提供商在 Kubernetes 核心代码之外开发存储驱动,但仍然有两个问题没有得到很好的解决:


  • 部署第三方驱动的可执行文件仍然需要宿主机的 root 权限,存在安全隐患。
  • 存储插件在执行 mount、attach 这些操作时,通常需要在宿主机上安装一些第三方工具包和依赖库,使得部署过程更加复杂,例如部署 Ceph 时需要安装 rbd 库,部署 GlusterFS 时需要安装 mount.glusterfs 库,等等。

基于以上这些问题和考虑,Kubernetes 逐步推出与容器对接的存储接口标准,存储提供方只需要基于标准接口进行存储插件的实现,就能使用 Kubernetes 的原生存储机制为容器提供存储服务。这套标准被称为 CSI(容器存储接口)。在 CSI 成为 Kubernetes 的存储提供标准之后,存储提供方的代码就能和 Kubernetes 代码彻底解耦,部署也与 Kubernetes 核心组件分离,显然,存储插件的开发由提供方自行维护,就能为 Kubernetes 用户提供更多的存储功能,也更加安全可靠。基于 CSI 的存储插件机制也被称为 "out-of-tree"(树外)的服务提供方式,是未来 Kubernetes 第三方存储插件的标准方案。

CSI 存储插件的挂件组件和部署架构

其中主要包括两个组件:CSI Controller 和 CSI Node。

CSI Controller

CSI Controller 的主要功能是提供存储服务视角对存储资源和存储卷进行管理和操作。在 Kubernetes 中建议将其部署为单实例 Pod,可以实验 StatefulSet 或 Deployment 控制器进行部署,设置副本数量为 1,保证为一种存储插件只运行一个控制器实例。

在这个 Pod 内部署两个容器,如下所述。

(1)与 Master(kube-controller-manager)通信的辅助 sidecar 容器。

在 sidecar 容器内由可以包含 external-attacher 和 external-provisioner 两个容器,它们的功能分别如下:


  • external-attacher:监控 VolumeAttachment 资源对象的变更,触发针对 CSI 端点的 ControllerPublish 和 ControllerUnpublish 操作。
  • external-provisioner:监控 PersistentVolumeClaim 资源对象的变更,触发针对 CSI 端点 CreateVolume 和 DeleteVolume 操作。

(2)CSI Driver 存储驱动容器,由第三方存储提供商提供,需要实现上述接口。

这两个容器通过本地 Socket(Unix Domain Socket,UDS),并使用 gRPC 协议进行通信。sidecar 容器通过 Socket 调用 CSO Driver 容器的 CSI 接口,CSI Driver 容器负责具体的存储操作。

CSI Node

CSI Node 的主要功能是对主机(Node)上的 Volume 进行管理和操作。在 Kubernetes 中建议将其部署为 DaemonSet,在每个 Node 上都运行一个 Pod。

在这个 Pod 中部署以下两个容器:

(1)与 Kubelet 通信的辅助 sidecar 容器 node-driver-registrar,主要功能是将存储驱动注册到 Kubelet 中;

(2)CSI Driver 存储驱动容器,由第三方存储提供商提供,主要功能是接收 kubelet 的调用,需要实现一系列与 Node 相关的 CSI 接口,例如 NodePublishVolume 接口(用于 Volume 挂载到容器内的目标路径)、NodeUnpublishVolume 接口(用于从容器中卸载 Volume),等等。

node-driver-registrar 容器与 kubelet 通过 Node 主机的一个 hostPath 目录下的 unix socket 进行通信。CSI Driver 容器与 kubelet 通过 Node 主机的另一个 hostPath 目录下的 unix socket 进行通信,同时需要将 kubelet 的工作目录(默认为 /var/lib/kubelet)挂载给 CSI Driver 容器,用于为 Pod 进行 Volume 的管理操作(包括 mount,umount 等)。

CSI 存储插件的使用实例

下面以 csi-hostpath 插件为例,对如何部署 CSI 插件、用户如何使用 CSI 插件提供的存储资源进行详细说明。

(1)设置 Kubernetes 服务启动参数。为 kube-apiserver、kube-controller-manager 和 kubelet 服务的启动参数添加:


​--feature-gates=VolumeSnapshotDataSource=​​​​true​​​​,CSINodeInfo=​​​​true​​​​,CSIDriverRegistry=​​​​true​


这 3 个特性开关是 Kubernetes 从 1.12 版本引入的 Alpha 版功能,CSINodeInfo 和 CSIDriverRegistry 需要手工创建其相应的 CRD 资源对象。

Kubernetes 1.10 版本所需的 CSIPersistentVolume 和 MountPropagation 特性开关已经默认启用,KubeletPluginsWatcher 特性开关也在 Kubernetes 1.12 版本中默认开启,无需在命令行参数中指定。

(2)创建 CSINodeInfo 和 CSIDriverRegistry CRD 资源对象。

csidriver.yml 展开源码

csinodeinfo 资源如下:

csinodeinfo.yml 展开源码

创建资源:


​$ kubectl apply -f csidriver.yml -f csinodeinfo.yml​

​customresourcedefinition.apiextensions.k8s.io​​​​/csidrivers​​​​.csi.storage.k8s.io created​

​customresourcedefinition.apiextensions.k8s.io​​​​/csinodeinfos​​​​.csi.storage.k8s.io created​


(3)创建 csi-hostpath 存储插件相关组件,包括 csi-hostpath-atttacher、csi-hostpath-provisioner 和 csi-hostpathplugin(其中包含 csi-node-driver-registrar 和 hostpathplugin)。其中为每个组件都配置了相应的 RBAC 权限控制规则,对于安全访问 Kubernetes 资源对象非常重要。

csi-hostpath-attacher.yml


​apiVersion: v1​

​kind: ServiceAccount​

​metadata:​

​name: csi-attacher​

​# replace with non-default namespace name​

​namespace: default​

​---​

​kind: ClusterRole​

​apiVersion: rbac.authorization.k8s.io​​​​/v1​

​metadata:​

​name: external-attacher-runner​

​rules:​

​- apiGroups: [​​​​""​​​​]​

​resources: [​​​​"persistentvolumes"​​​​]​

​verbs: [​​​​"get"​​​​, ​​​​"list"​​​​, ​​​​"watch"​​​​, ​​​​"update"​​​​]​

​- apiGroups: [​​​​""​​​​]​

​resources: [​​​​"nodes"​​​​]​

​verbs: [​​​​"get"​​​​, ​​​​"list"​​​​, ​​​​"watch"​​​​]​

​- apiGroups: [​​​​"csi.storage.k8s.io"​​​​]​

​resources: [​​​​"csinodeinfos"​​​​]​

​verbs: [​​​​"get"​​​​, ​​​​"list"​​​​, ​​​​"watch"​​​​]​

​- apiGroups: [​​​​"storage.k8s.io"​​​​]​

​resources: [​​​​"volumeattachments"​​​​]​

​verbs: [​​​​"get"​​​​, ​​​​"list"​​​​, ​​​​"watch"​​​​, ​​​​"update"​​​​]​

​---​

​kind: ClusterRoleBinding​

​apiVersion: rbac.authorization.k8s.io​​​​/v1​

​metadata:​

​name: csi-attacher-role​

​subjects:​

​- kind: ServiceAccount​

​name: csi-attacher​

​# replace with non-default namespace name​

​namespace: default​

​roleRef:​

​kind: ClusterRole​

​name: external-attacher-runner​

​apiGroup: rbac.authorization.k8s.io​

​---​

​kind: Role​

​apiVersion: rbac.authorization.k8s.io​​​​/v1​

​metadata:​

​# replace with non-default namespace name​

​namespace: default​

​name: external-attacher-cfg​

​rules:​

​- apiGroups: [​​​​""​​​​]​

​resources: [​​​​"configmaps"​​​​]​

​verbs: [​​​​"get"​​​​, ​​​​"watch"​​​​, ​​​​"list"​​​​, ​​​​"delete"​​​​, ​​​​"update"​​​​, ​​​​"create"​​​​]​

​---​

​kind: RoleBinding​

​apiVersion: rbac.authorization.k8s.io​​​​/v1​

​metadata:​

​name: csi-attacher-role-cfg​

​# replace with non-default namespace name​

​namespace: default​

​subjects:​

​- kind: ServiceAccount​

​name: csi-attacher​

​# replace with non-default namespace name​

​namespace: default​

​roleRef:​

​kind: Role​

​name: external-attacher-cfg​

​apiGroup: rbac.authorization.k8s.io​


​---​

​kind: Service​

​apiVersion: v1​

​metadata:​

​name: csi-hostpath-attacher​

​labels:​

​app: csi-hostpath-attacher​

​spec:​

​selector:​

​app: csi-hostpath-attacher​

​ports:​

​- name: dummy​

​port: 12345​

​---​

​kind: StatefulSet​

​apiVersion: apps​​​​/v1​

​metadata:​

​name: csi-hostpath-attacher​

​spec:​

​serviceName: ​​​​"csi-hostpath-attacher"​

​replicas: 1​

​selector:​

​matchLabels:​

​app: csi-hostpath-attacher​

​template:​

​metadata:​

​labels:​

​app: csi-hostpath-attacher​

​spec:​

​serviceAccountName: csi-attacher​

​containers:​

​- name: csi-attacher​

​image: quay.io​​​​/k8scsi/csi-attacher​​​​:v1.0.1​

​imagePullPolicy: IfNotPresent​

​args:​

​- --​​​​v​​​​=5​

​- --csi-address=$(ADDRESS)​

​env​​​​:​

​- name: ADDRESS​

​value: ​​​​/csi/csi​​​​.sock​

​volumeMounts:​

​- mountPath: ​​​​/csi​

​name: socket-​​​​dir​

​volumes:​

​- hostPath:​

​path: ​​​​/var/lib/kubelet/plugins/csi-hostpath​

​type​​​​: DirectoryOrCreate​

​name: socket-​​​​dir​


csi-hostpath-provisioner.yml 展开源码Kubernetes CSI_ideKubernetes CSI_服务器_02

大家都知道,香港的服务器作为网站的空间的选择具有独特的优势。从网络运营的专业角度来说,香港服务器首先是没有像国内服务器需要备案的步骤,这可省掉了一部分时间,其次,香港服务器的速度相对于国内的服务器来说快的多。当然,了解服务器的大神们都知道,欧美地区的主流还是以美国服务器为主的市场导向。不过萝卜青菜各有所爱。今天,我想帮助您分析怎么去选择香港服务器。

  香港服务器的选择在核心性能上与海外独立服务器的差别具体细节如下:

  第一,服务与技术支持。

  作为空间是我们需要需要长期存放网站重要数据的地方。只要外贸企业做一天,香港服务器就要运行一天!因此,在选择服务器时,外贸企业参考目标更多的是 24 小时不间断技术支持的香港服务器!

  第二,带宽。

  很多企业使用香港服务器比较看中的就是它的带宽,这也是访问速度的先决条件。这里推荐3A网络香港服务器,它采用的是BGP线路,国际带宽。对于企业用户来说,访问速度、数据安全都有非常大的保障,直接连接国际主干网。

  第二,机房。

  在选择香港机房时,良好的服务不仅为我们提供了非常可靠的技术支持和保证,而且其配套设施,包括带宽、机房环境、温度和数据安全,也决定了我们看中它的原因。

  第三,存储空间及相关设施

  在选择服务器的时候我们需要判断网站的数据资料存放要多少存储空间,如何选择中央处理器也即是cpu的大小。这块可以联系网站客服为您量身定制

我相信香港服务器是我们经常接触的海外服务器之一,大家在选择的时候也会是通过以上几个方面选择的,不过还是在这友情提醒下业界大佬们还是刚接触这个的新人,在选择时可以通过服务器的价格,服务态度和直观的测试速度上多做对比,这样选择的产品服务才是比较满意的嘛!

csi-hostpathplugin.yml 展开源码

创建服务:


​$ kubectl apply -f csi-hostpath-attacher.yml -f csi-hostpath-provisioner.yml -f csi-hostpathplugin.yml​

​serviceaccount​​​​/csi-attacher​​ ​​created​

​clusterrole.rbac.authorization.k8s.io​​​​/external-attacher-runner​​ ​​created​

​clusterrolebinding.rbac.authorization.k8s.io​​​​/csi-attacher-role​​ ​​created​

​role.rbac.authorization.k8s.io​​​​/external-attacher-cfg​​ ​​created​

​rolebinding.rbac.authorization.k8s.io​​​​/csi-attacher-role-cfg​​ ​​created​

​service​​​​/csi-hostpath-attacher​​ ​​created​

​statefulset.apps​​​​/csi-hostpath-attacher​​ ​​created​

​serviceaccount​​​​/csi-provisioner​​ ​​created​

​clusterrole.rbac.authorization.k8s.io​​​​/external-provisioner-runner​​ ​​created​

​clusterrolebinding.rbac.authorization.k8s.io​​​​/csi-provisioner-role​​ ​​created​

​role.rbac.authorization.k8s.io​​​​/external-provisioner-cfg​​ ​​created​

​rolebinding.rbac.authorization.k8s.io​​​​/csi-provisioner-role-cfg​​ ​​created​

​service​​​​/csi-hostpath-provisioner​​ ​​created​

​statefulset.apps​​​​/csi-hostpath-provisioner​​ ​​created​

​serviceaccount​​​​/csi-node-sa​​ ​​created​

​clusterrole.rbac.authorization.k8s.io​​​​/driver-registrar-runner​​ ​​created​

​clusterrolebinding.rbac.authorization.k8s.io​​​​/csi-driver-registrar-role​​ ​​created​

​daemonset.apps​​​​/csi-hostpathplugin​​ ​​created​


确保 3 个 Pod 都运行正常:


​$ kubectl get pod​

​NAME                                READY   STATUS    RESTARTS   AGE​

​csi-hostpath-attacher-0             1​​​​/1​​     ​​Running   0          109s​

​csi-hostpath-provisioner-0          1​​​​/1​​     ​​Running   0          109s​

​csi-hostpathplugin-44s8n            2​​​​/2​​     ​​Running   0          109s​

​csi-hostpathplugin-qdfht            2​​​​/2​​     ​​Running   0          109s​


至此就完成了 CSI 存储插件的部署。

(4)应用容器使用 CSI 存储。应用程序如果希望使用 CSI 存储插件提供的存储服务,则仍然使用 Kubernetes 动态存储管理机制。首先通过创建 StorageClass 和 PVC 为应用容器准备存储资源,然后容器就可以挂载 PVC 到容器内的目录进行使用了。

创建一个 StorageClass,provisioner 为 CSI 存储插件的类型,

csi-storageclass.yml


​apiVersion: storage.k8s.io​​​​/v1​

​kind: StorageClass​

​metadata:​

​name: csi-hostpath-sc​

​provisioner: csi-hostpath​

​reclaimPolicy: Delete​

​volumeBindingMode: Immediate​


创建一个 PV,引用刚刚创建的 StorageClass,申请存储空间为 1GiB:

csi-pvc.yml


​apiVersion: v1​

​kind: PersistentVolumeClaim​

​metadata:​

​name: csi-pvc​

​spec:​

​accessModes:​

​- ReadWriteOnce​

​resources:​

​requests:​

​storage: 1Gi​

​storageClassName: csi-hostpath-sc​


创建服务:


​$ kubectl apply -f csi-storageclass.yml -f csi-pvc.yml​

​storageclass.storage.k8s.io​​​​/csi-hostpath-sc​​ ​​created​

​persistentvolumeclaim​​​​/csi-pvc​​ ​​created​


查看 PVC 和系统自动创建的 PV,状态为 Bound,说明创建成功:


​$ kubectl get pv,pvc​

​NAME                                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM             STORAGECLASS      REASON   AGE​

​persistentvolume​​​​/pvc-8882d70e-92a4-11e9-81cd-42010aae0008​​   ​​1Gi        RWO            Delete           Bound    default​​​​/csi-pvc​​   ​​csi-hostpath-sc            30s​


​NAME                            STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS      AGE​

​persistentvolumeclaim​​​​/csi-pvc​​   ​​Bound    pvc-8882d70e-92a4-11e9-81cd-42010aae0008   1Gi        RWO            csi-hostpath-sc   32s​


最后,在应用容器的配置使用该 PVC:

pod.yml


​kind: Pod​

​apiVersion: v1​

​metadata:​

​name: my-csi-app​

​spec:​

​containers:​

​- name: my-csi-app​

​image: busybox​

​imagePullPolicy: IfNotPresent​

​command​​​​: [ ​​​​"sleep"​​​​, ​​​​"1000000"​​ ​​]​

​volumeMounts:​

​- mountPath: ​​​​"/data"​

​name: my-csi-volume​

​volumes:​

​- name: my-csi-volume​

​persistentVolumeClaim:​

​claimName: csi-pvc​


在创建 Pod 成功之后,应用容器中的 /data 目录使用的就是 CSI 存储插件提供的存储。通过 kubelet 的日志可以查看到 Volume 挂载的详细过程:

pod.yml


​$ kubectl apply -f pod.yml​

​pod​​​​/my-csi-app​​ ​​created​