29

.NET Core + Kubernetes:Volume | Beck's Blog

 3 years ago
source link: http://beckjin.com/2020/06/24/aspnet-k8s-volume/?
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

.NET Core + Kubernetes:Volume

发表于 2020-06-24

| 分类于 ASP.NET Core

| 阅读次数: 104

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

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

1
2
3
4
5
6
7
8
9
10
11
[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 内。

emptyDirStructure.png

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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

emptyDirService.png

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

emptyDirResult.png

hostPath

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

hostPathStructure.png

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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

hostPathService.png

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

hostPathResult.png

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

nfsStructure.png

NFS Server 搭建

  1. 在机器上安装依赖组件(集群外的机器 192.168.1.13,并关闭防火墙

    1
    yum install -y nfs-utils rpcbind
  2. 将主机上的 /share 目录作为共享目录,如果多个目录可以添加多行

    1
    2
    [root@localhost ~]# vim /etc/exports
    /share 192.168.1.0/24(insecure,rw,no_root_squash)
  3. 启动 NFS

    1
    2
    3
    4
    5
    systemctl start rpcbind.service
    systemctl enable rpcbind.service

    systemctl start nfs.service
    systemctl enable nfs.service
  4. Kubernetes 集群内各节点安装 nfs-utils,方便使用 showmount

    1
    yum install -y nfs-utils

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

nfsMount.png

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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

nfsService.png

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

nfsResult.png

persistentVolumeClaim

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

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

pvcStructure.png

NFS Server 添加 mount 目录

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

pvcMount.png

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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

pv.png

创建 PVC

1
2
3
4
5
6
7
8
9
10
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-k8sdemo
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi

pvc.png

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

创建 Pod

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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

pvcService.png

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

pvcResult.png

configMap

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

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

configMapStructure.png

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

1
2
3
4
5
6
7
8
9
apiVersion: v1
kind: ConfigMap
metadata:
name: configmap-k8sdemo
data:
appsettings.json: |-
{
"ServiceName": "k8sdemo"
}

configMap.png

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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

configMapService.png

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

configMapResult.png


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK