.NET Core + Kubernetes:Volume

和 Docker 类似,Kubernetes 中也提供了 Volume 来实现数据卷挂载,但 Kubernetes 中 Volume 是基于 Pod,而不是容器,它可被 Pod 中多个容器共享,另外 Kubernetes 中提供比较丰富的 Volume 类型,如:emptyDirhostPathnfspersistentVolumeClaimdownwardAPIsecretconfigMap 等,每种类型都有其特点及使用场景。

下面将介绍几种常用 Volume 类型的使用方式,在这之前先在 k8sdemo .NET Core 服务中添加以下两个接口(镜像版本升级为 1.2.0),以方便后面效果演示。

[HttpGet]
public string GetConfig([FromQuery]string key)
{
  // ......
}

[HttpGet]
public string GetVolumeData()
{
  // ......
}

GetConfig:通过传入配置文件 appsettings.json 的 key 获取对应值;
GetVolumeData:获取容器内 /Data/data.txt 的文件内容;

emptyDir

emptyDir 的初始状态是一个没有任何内容的 Volume,其生命周期与 Pod 一致,当 Pod 中的容器挂掉时,emptyDir Volume 中的内容不会被清除,容器重启后数据依然可见。只有当整个 Pod 从集群中被删除,emptyDir Volume 中的内容才会被清除。如下:emptyDir Volume 位于 Pod 内。

通过以下配置文件创建的 Pod 中将包含 k8sdemo 和 busybox 两个 container,busybox 是一个集成了一些常用 linux 命令的镜像,这里将使用它在 Pod 内进行文件内容修改。k8sdemo 容器的 /app/Data/ 目录文件与 busybox 容器的 /data/ 目录文件将通过 emptyDir Volume 进行共享。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: emptydir-demo
spec:
  replicas: 1
  selector:
    matchLabels:
      name: emptydir-demo
  template:
    metadata:
      labels:
        name: emptydir-demo
    spec:
      containers:
      - name: k8sdemo
        image: beckjin/k8sdemo:1.2.0
        volumeMounts:
        - mountPath: /app/Data/
          name: share
        ports:
        - containerPort: 80
      - name: busybox
        image: busybox
        command:
        - "/bin/sh"
        - "-c"
        - "sleep 3600"
        volumeMounts:
        - mountPath: /data/
          name: share 
      volumes:
      - name: share
        emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
  name: emptydir-demo-service
spec:
  selector:
    name: emptydir-demo
  type: NodePort
  ports:
  - port: 80
    targetPort: 80

执行命令 kubectl exec -it emptydir-demo-746f49b55b-p6pzz -c busybox -- /bin/sh 进入 busybox 容器,然后执行 echo 'emptyDir Volume' > /data/data.txt,最后访问 k8sdemo 服务的 GetVolumeData 接口获取文件内容:

hostPath

hostPath 类型是挂载宿主机上的文件或目录到 Pod 中,与 Pod 所在的 Node 是强关联的,所以当 Pod 因重启被重新调度时,一定要确保所在主机的相关文件或目录的正确性,如下:

如下配置中 replicas 字段设置为 2 ,正常情况下 Pod 将会在 node1 和 node2 上分别被创建,另外 hostPath 字段中的 path 指定了 /data/k8sdemo/ 目录挂载到容器内的 /app/Data/,所以分别在 node1 和 node2 创建 /data/k8sdemo/data.txt ,内容为 node1 hostPath Volumenode2 hostPath Volume

kind: Deployment
metadata:
  name: hostpath-demo
spec:
  replicas: 2
  selector:
    matchLabels:
      name: hostpath-demo
  template:
    metadata:
      labels:
        name: hostpath-demo
    spec:
      containers:
      - name: k8sdemo
        image: beckjin/k8sdemo:1.2.0
        volumeMounts:
        - mountPath: /app/Data/
          name: share
        ports:
        - containerPort: 80
      volumes:
      - name: share
        hostPath:
          path: /data/k8sdemo
          type: Directory
---
apiVersion: v1
kind: Service
metadata:
  name: hostpath-demo-service
spec:
  selector:
    name: hostpath-demo
  type: NodePort
  ports:
  - port: 81
    targetPort: 80

访问 k8sdemo 服务的 GetVolumeData 接口获取文件内容,当路由到不同 Pod(即不同的 node) 返回内容将不一样,如下:

nfs

NFS(network file system) 网络文件系统,类似 Windows 中的文件夹共享。首先在 Kubernetes 集群外搭建一个 NFS Server,然后指定文件目录进行共享,最终与 Pod 内的容器关联,实现数据卷挂载,如下:

NFS Server 搭建

  1. 在机器上安装依赖组件(集群外的机器 192.168.1.13,并关闭防火墙
    yum install -y nfs-utils rpcbind
    
  2. 将主机上的 /share 目录作为共享目录,如果多个目录可以添加多行
    [root@localhost ~]# vim /etc/exports
    /share  192.168.1.0/24(insecure,rw,no_root_squash)
    
  3. 启动 NFS
    systemctl start rpcbind.service
    systemctl enable rpcbind.service
    
    systemctl start nfs.service
    systemctl enable nfs.service
    
  4. Kubernetes 集群内各节点安装 nfs-utils,方便使用 showmount
    yum install -y nfs-utils
    

完成以上步骤后,在 Kubernetes 集群中任意节点执行 showmount -e 192.168.1.13 验证是否正常:

如下配置中 volumes 指定了 nfs 字段配置,即将 NFS Server 中的 /share 目录挂载到容器内的 /app/Data/,与 hostPath Volume 类型的主要区别是依赖单独的 NFS Server,和 node 本身并不耦合。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nfs-demo
spec:
  replicas: 2
  selector:
    matchLabels:
      name: nfs-demo
  template:
    metadata:
      labels:
        name: nfs-demo
    spec:
      containers:
      - name: k8sdemo
        image: beckjin/k8sdemo:1.2.0
        volumeMounts:
        - mountPath: /app/Data
          name: share
        ports:
        - containerPort: 80
      volumes:
      - name: share
        nfs:
          server: 192.168.1.13
          path: /share
---
apiVersion: v1
kind: Service
metadata:
  name: nfs-demo-service
spec:
  selector:
    name: nfs-demo
  type: NodePort
  ports:
  - port: 82
    targetPort: 80

在 NFS Server 中执行 echo 'nfs Volume' > /share/data.txt,然后访问 k8sdemo 服务的 GetVolumeData 接口获取文件内容,如下:

persistentVolumeClaim

PersistentVolumeClaim(PVC)PersistentVolume(PV) 在使用上是一对密不可分的组合,PV 主要是资源对象定义,PVC 主要是对应资源对象的引用,PV 支持 多种插件类型 进行实现,以下将继续使用 NFS 来作为 PV 插件。

如下图:首先基于 PV 插件在 Kubernetes 集群中创建各种资源规格的 PV,根据 Pod 需要存储卷资源创建 PVC,Kubernetes 将符合资源规格要求且消耗资源最小的 PV 绑定到 PVC,PV 和 PVC 是一对一的关系,如果找不到符合条件的 PV,PVC 会一直处于未绑定状态,PVC 绑定成功后可被 Pod 内的容器引用。

NFS Server 添加 mount 目录

修改 NFS Server /etc/exports 并生效 ,在 Kubernetes 集群中任意节点执行 showmount -e 192.168.1.13 验证是否正常:

创建 PV

以下配置将会创建3个 PV,storage 分别为 500M、1G、2G。

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-share-a
spec:
  nfs:
    path: /share_a
    server: 192.168.1.13
  accessModes:
  - ReadWriteMany
  capacity:
    storage: 500Mi
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-share-b
spec:
  nfs:
    path: /share_b
    server: 192.168.1.13
  accessModes:
  - ReadWriteMany
  capacity:
    storage: 1Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-share-c
spec:
  nfs:
    path: /share_c
    server: 192.168.1.13
  accessModes:
  - ReadWriteMany
  capacity:
    storage: 2Gi

创建 PVC

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-k8sdemo
spec:
  accessModes:
  - ReadWriteMany
  resources:
    requests:
      storage: 1Gi

PVC 创建成功后,pv-share-b 的 STATUS 会变为 Bound,同时 CLAIM 属性会显示相关的 PVC,从上图也可以看出使用的是最小符合资源规格的 PV,并不会将 pv-share-c 绑定到当前 PVC。更多关于 PV 和 PVC 属性说明可参考:persistent-volumes

创建 Pod

如下配置中 volumes 指定了 persistentVolumeClaim 字段配置,这里只需要设置 claimName 为前面创建的 PVC 名称 pvc-k8sdemo 即可,使用上比较简单。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: pvc-demo
spec:
  replicas: 2
  selector:
    matchLabels:
      name: pvc-demo
  template:
    metadata:
      labels:
        name: pvc-demo
    spec:
      containers:
      - name: k8sdemo
        image: beckjin/k8sdemo:1.2.0
        volumeMounts:
        - mountPath: /app/Data
          name: share
        ports:
        - containerPort: 80
      volumes:
      - name: share
        persistentVolumeClaim:
          claimName: pvc-k8sdemo
---
apiVersion: v1
kind: Service
metadata:
  name: pvc-demo-service
spec:
  selector:
    name: pvc-demo
  type: NodePort
  ports:
  - port: 83
    targetPort: 80

在 NFS Server 中执行 echo 'pvc Volume share_a' > /share_a/data.txt,share_b、share_c 类似,然后访问 k8sdemo 服务的 GetVolumeData 接口获取文件内容,如下:

configMap

configMap 主要使镜像和配置文件解耦,以便实现镜像的可移植性和可复用性,configMap 是配置信息的集合,可直接注入到 Pod 的容器中使用,扮演着配置中心的角色。configMap 可以以数据卷的形式挂载,也可以基于环境变量的形式注入到 Pod 容器中使用。另外 secret 是一种相对安全的 configMap,它默认会将配置信息进行 base64 编码,使配置不是明文直接存储在 configMap 中,起到一定的保护作用。

下面主要介绍 configMap 以数据卷挂载方式的使用,如下图,在 Kubernetes 集群中创建一个 configMap 资源类型,然后供 Pod 内的容器使用。

如下,创建一个数据卷形式的 ConfigMap,appsettings.json 是 .NET Core 程序内使用的配置文件。

apiVersion: v1
kind: ConfigMap
metadata:
  name: configmap-k8sdemo
data:
  appsettings.json: |-
    {
      "ServiceName": "k8sdemo"
    }

如下配置中 volumes 指定了 configMap 资源的名称为以上创建的 configMap 对象:configmap-k8sdemo

apiVersion: apps/v1
kind: Deployment
metadata:
  name: configmap-demo
spec:
  replicas: 2
  selector:
    matchLabels:
      name: configmap-demo
  template:
    metadata:
      labels:
        name: configmap-demo
    spec:
      containers:
      - name: k8sdemo
        image: beckjin/k8sdemo:1.2.0
        volumeMounts:
        - name: configfile
          mountPath: /app/appsettings.json
          subPath: appsettings.json
        ports:
        - containerPort: 80
      volumes:
      - name: configfile
        configMap:
          name: configmap-k8sdemo
          items:
          - key: appsettings.json
            path: appsettings.json
---
apiVersion: v1
kind: Service
metadata:
  name: configmap-demo-service
spec:
  selector:
    name: configmap-demo
  type: NodePort
  ports:
  - port: 84
    targetPort: 80

通过访问 k8sdemo 服务的 GetConfig 接口获取指定 key 的值:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,378评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,356评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,702评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,259评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,263评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,036评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,349评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,979评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,469评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,938评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,059评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,703评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,257评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,262评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,501评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,792评论 2 345