容器中持久化的文件生命周期是短暂的,如果容器中程序崩溃宕机,kubelet 就会重新启动,容器中的文件将会丢失,所以对于有状态的应用容器中持久化存储是至关重要的一个环节;另外很多时候一个 Pod 中可能包含多个 Docker 镜像,在 Pod 内数据也需要相互共享,Kubernetes  中 Pod 也可以增加副本数量,遇到故障时 Pod 可以转移到其它节点,为了浮动节点都能够访问统一的持久化存储以及容器间共享数据,Kubernetes 中定义了 Volume 来解决这些问题 ,从本质上讲,Volume 只是一个目录,可能包含一些数据,Pod 中的容器可以访问它。该目录是何种形式,是由所使用的 Volume 类型决定的。

Volume 数据卷类型

Kubernetes 支持很多 Volume(https://kubernetes.io/docs/concepts/storage/volumes/#types-of-volumes)类型:

如果使用公有云,根据不同的云厂商提供的服务可以选择如下类型

  • awsElasticBlockStore
  • azureDisk
  • azureFile

以下介绍一些常用的类型

emptyDir

一般适用与临时文件场景,如上传图片运行时生成的流文件,Pod 中的容器都能够完全读写,但是 Pod 如果被移除,数据也就被删除,容器宕机不会删除 Pod ,因此不会造成数据丢失。要使用 Volume ,Pod 中需要使用.spec.volumes 配置定义类型,然后使用 .spec.containers.volumeMounts 配置定义挂载的信息。

apiVersion: v1
kind: Pod
metadata:
name: test-pd
spec:
containers:
- image: k8s.gcr.io/test-webserver
name: test-container
volumeMounts:
- mountPath: /cache
name: cache-volume
volumes:
- name: cache-volume
emptyDir: {}

hostPath

hostPath Volume 为 Pod 挂载宿主机上的目录或文件,使得容器可以使用宿主机的高速文件系统进行存储。缺点是,Pod 是动态在各个节点上调度。当一个 Pod 在当前节点上启动并通过 hostPath存储了文件到本地以后,下次调度到另一个节点上启动时,就无法使用在之前节点上存储的文件。

apiVersion: v1
kind: Pod
metadata:
name: test-pod
spec:
containers:
- image: test-container
name: test-name
volumeMounts:
- name: test-volume
mountPath: /cache
volumes:
- name: test-volume
hostPath:
path: /data

nfs

我们前面使用的 hostPath 和 emptyDir 类型的 Volume 有可能被 kubelet 清理掉,也不能被“迁移”到其他节点上,不具备持久化特性。 NFS(网络文件系统)服务需要搭建好,共享到 Pod 中,与  emptyDir 移除 Pod 时删除的内容不同,NFS 卷的内容将被保留,下次运行 Pod 可以继续使用,对于一些 IO 与网络要求不高的的场景可以使用。

apiVersion: v1
kind: PersistentVolume
metadata:
name: test--nfs-pv
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteMany
flexVolume:
driver: "k8s/nfs"
fsType: "nfs"
options:
server: "192.168.10.100" # NFS 服务器地址
path: "/"

cephfs

Cephfs 是一个分布式存储系统,诞生于2004年,最早致力于开发下一代高性能分布式文件系统的项目。提前是也需要提前搭建好存储集群服务,也可以使用 Rook (支持 Ceph),现属于 CNCF 孵化项目。

kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: fast
provisioner: kubernetes.io/rbd
parameters:
monitors: 10.16.153.105:6789
adminId: kube
adminSecretName: ceph-secret
adminSecretNamespace: kube-system
pool: kube
userId: kube
userSecretName: ceph-secret-user
userSecretNamespace: default
fsType: ext4
imageFormat: "2"
imageFeatures: "layering"

glusterfs

GlusterFS 是一个开源的分布式文件系统,具有强大的横向扩展能力,通过扩展能够支持数 PB 存储容量和处理数千客户端。

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: slow
provisioner: kubernetes.io/glusterfs
parameters:
resturl: "http://127.0.0.1:8081"
clusterid: "630372ccdc720a92c681fb928f27b53f"
restauthenabled: "true"
restuser: "admin"
secretNamespace: "default"
secretName: "heketi-secret"
gidMin: "40000"
gidMax: "50000"
volumetype: "replicate:3"

配置 GlusterFS

准备三台服务器

172.23.216.48   gfs_node_1
172.23.216.49 gfs_node_2
172.23.216.50 gfs_node_3

安装 Glusterfs

$ yum install centos-release-gluster
$ yum install glusterfs-server
$ systemctl start glusterd.service
$ systemctl enable glusterd.service
$ systemctl status glusterd.service

创建存储目录

$ mkdir /opt/gfs_data

添加节点

$ gluster peer probe node2
$ gluster peer probe node3

查看节点

$ gluster peer status
Number of Peers: 2

Hostname: 172.23.216.49
Uuid: 4dcfad42-e327-4a79-8a5a-a55dc92982ba
State: Peer in Cluster (Connected) Hostname: 172.23.216.50
Uuid: 84e90bcf-af22-4cac-a6b1-e3e0d87d7eb4
State: Peer in Cluster (Connected)

创建数据卷(测试使用分布式模式,生产勿用)

# 复制模式
$ gluster volume create k8s-volume replica 3 transport tcp gfs_node_1:/opt/gfs_data gfs_node_2:/opt/gfs_data gfs_node_3:/opt/gfs_data force
# 分布卷(默认模式)
$ gluster volume create k8s-volume transport tcp 172.23.216.48:/opt/gfs_data 172.23.216.49:/opt/gfs_data 172.23.216.50:/opt/gfs_data force

备注:其他卷模式

CentOS7安装GlusterFS

启动数据卷

$ gluster volume start k8s-volume
$ gluster volume info
Volume Name: k8s-volume
Type: Distribute
Volume ID: 1203a7ab-45c5-49f0-a920-cbbe8968fefa
Status: Started
Snapshot Count: 0
Number of Bricks: 3
Transport-type: tcp
Bricks:
Brick1: 172.23.216.48:/opt/gfs_data
Brick2: 172.23.216.49:/opt/gfs_data
Brick3: 172.23.216.50:/opt/gfs_data
Options Reconfigured:
transport.address-family: inet
nfs.disable: on

相关命令

#为存储池添加/移除服务器节点
$ gluster peer probe
$ gluster peer detach
$ gluster peer status
#创建/启动/停止/删除卷
$ gluster volume create [stripe | replica ] [transport [tcp | rdma | tcp,rdma]] ...
$ gluster volume start
$ gluster volume stop
$ gluster volume delete 注意,删除卷的前提是先停止卷。 #查看卷信息
$ gluster volume list
$ gluster volume info [all]
$ gluster volume status [all]
$ gluster volume status [detail| clients | mem | inode | fd] #查看本节点的文件系统信息: $ df -h []
#查看本节点的磁盘信息:
$ fdisk -l

Kubernetes 中配置 GlusterFS

要在一个 Pod 里声明 Volume,只要在 Pod 里加上 spec.volumes 字段即可。然后在这个字段里定义一个具体 Volume 的类型,  参考官方文档:https://github.com/kubernetes/examples/tree/master/staging/volumes/glusterfs

修改如下:

glusterfs-endpoints

{
"kind": "Endpoints",
"apiVersion": "v1",
"metadata": {
"name": "glusterfs-cluster"
},
"subsets": [
{
"addresses": [
{
"ip": "172.23.216.48"
}
],
"ports": [
{
"port": 1000
}
]
},
{
"addresses": [
{
"ip": "172.23.216.49"
}
],
"ports": [
{
"port": 1000
}
]
},
{
"addresses": [
{
"ip": "172.23.216.50"
}
],
"ports": [
{
"port": 1000
}
]
}
]
}

glusterfs-service.json

{
"kind": "Service",
"apiVersion": "v1",
"metadata": {
"name": "glusterfs-cluster"
},
"spec": {
"ports": [
{"port": 1000}
]
}
}

glusterfs-pod.json(创建测试 Pod)

{
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"name": "glusterfs"
},
"spec": {
"containers": [
{
"name": "glusterfs",
"image": "nginx",
"volumeMounts": [
{
"mountPath": "/mnt/glusterfs",
"name": "glusterfsvol"
}
]
}
],
"volumes": [
{
"name": "glusterfsvol",
"glusterfs": {
"endpoints": "glusterfs-cluster",
"path": "k8s-volume",
"readOnly": true
}
}
]
}
}

依次执行

$ kubectl apply -f glusterfs-endpoints.json
$ kubectl get ep $ kubectl apply -f glusterfs-service.json
$ kubectl get svc
# 查看测试 Pod
$ kubectl apply -f glusterfs-pod.json
$ kubectl get pods
$ kubectl describe pods/glusterfs
$ kubectl exec glusterfs -- mount | grep gluster

持久存储卷(Persistent Volume,PV)和持久存储卷声明(Persistent Volume Claim,PVC)

在 Kubernetes 中还引入了一组叫作 Persistent Volume Claim(PVC)和 Persistent Volume(PV)的 API 对象,很大程度简化了用户声明和使用持久化 Volume 的门槛。比如 PV 是群集中已由管理员配置的一块存储,一般系统管理员创建 endpoint、Service、PV ;PVC 是由开发人员进行配置 ,Pod 挂载到 PVC 中,PVC 可以向 PV 申请指定大小的存储资源并设置访问模式,而不需要关注存储卷采用何种技术实现。

  • PersistentVolume(PV)是集群中由管理员配置的一块存储。它是集群中的资源,就和节点是集群资源一样。PV 是卷插件比如 Volumes,但是它的生命周期独立于使用 PV 的任何 pod 个体。该API对象捕获实现存储的详细信息,包括 NFS、iSCSI 或着是云服务商特定的存储系统。

  • PersistentVolumeClaim(PVC)是用户关于存储的请求。它类似于一个 pod,pod 消耗节点资源,而 PVC 消耗 PV 资源。Pods 可以请求特定级别的资源(CPU和内容),而Claim可以请求特定的大小和访问模式(例如,可以一次读/写或者多次只读)。

定义 PV

$ vi glusterfs-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: gluster-dev-volume
spec:
capacity:
storage: 8Gi
accessModes:
- ReadWriteMany
glusterfs:
endpoints: "glusterfs-cluster"
path: "k8s-volume"
readOnly: false

执行

$ kubectl apply -f glusterfs-pv.yaml
$ kubectl get pv

定义 PVC

$ cat glusterfs-pvc.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: glusterfs-nginx
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 2Gi

执行

$ kubectl apply -f glusterfs-pvc.yaml
$ kubectl get pvc

备注:访问模式

ReadWriteOnce – the volume can be mounted as read-write by a single node
ReadOnlyMany – the volume can be mounted read-only by many nodes
ReadWriteMany – the volume can be mounted as read-write by many nodes

可以在 Dashboard 中查看存储卷的信息

测试数据卷

$  wget https://raw.githubusercontent.com/kubernetes/website/master/content/en/examples/application/deployment.yaml
$ vi deployment.yaml apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 2 # tells deployment to run 2 pods matching the template
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
volumeMounts:
- name: gluster-dev-volume
mountPath: "/usr/share/nginx/html"
volumes:
- name: gluster-dev-volume
persistentVolumeClaim:
claimName: glusterfs-nginx

执行

$ kubectl apply -f deployment.yaml
$ kubectl describe deployment nginx-deployment
$ kubectl get pods -l app=nginx
$ kubectl get pods -l app=nginx
NAME READY STATUS RESTARTS AGE
nginx-deployment-5c689d88bb-7rx7d 1/1 Running 0 2d21h
nginx-deployment-5c689d88bb-hfqzm 1/1 Running 0 2d21h
nginx-deployment-5c689d88bb-tlwmn 1/1 Running 0 2d21h

创建文件

$ kubectl exec -it nginx-deployment-5c689d88bb-7rx7d -- touch index.html

最后查询 GlusterFS数据卷上是否有数据验证下即可。

Dynamic Provisioning 与 Storage Classes

前面介绍 PV 和 PVC 的时候,一般 PV 这个对象的创建,是由运维人员完成,PVC 可能是开发人员定义。在大规模的生产环境里,可能有很多 PVC ,这意味着运维人员必须得事先创建出成千上万个 PV,这其实是一个非常麻烦的工作,所以在 Kubernetes 还引入了可以自动创建 PV 的机制 Dynamic Provisioning 概念,Dynamic Provisioning 机制工作的核心,在于一个名叫 StorageClass 对象。管理员可以定义 Storage Class 来描述他们提供的存储类型,根据 Storage Class 对象中设置的参数自动分配 PV ,比如可以分别定义两种 Storage Class :slow 和 fast。slow 对接 sc1(机械硬盘),fast 对接 gp2(固态硬盘)。应用可以根据业务的性能需求,分别选择不同的存储方式。下面创建 StorageClass 使用 StorageClass 连接 Heketi,根据需要自动创建 GluserFS 的 Volume,StorageClass 还是要系统管理员创建,不同的 PVC 可以用同一个 StorageClass 配置。

官方的文档与视频介绍:

备注:并不是所有的存储方式都支持 Dynamic Provisioning 特性,官方文档列出了默认支持 Dynamic Provisioning 的内置存储插件,当然也可以扩展第三方的存储组件 kubernetes-incubator/external-storage 实现。

配置 Heketi

GlusterFS 是个开源的分布式文件系统,而 Heketi 在其上提供了 REST 形式的 API,二者协同为 Kubernetes 提供了存储卷的自动供给能力。按照官方的  persistent-volume-provisioning 示列,这里需要配置 Heketi 提供了一个管理 GlusterFS 集群的 RESTTful 服务,提供 API 接口供 Kubernetes 调用 。

$ yum install epel-release
$ yum install heketi heketi-client

查看版本

$ heketi --version
Heketi 7.0.0
$ heketi --help
Heketi is a restful volume management server Usage:
heketi [flags]
heketi [command] Examples:
heketi --config=/config/file/path/ Available Commands:
db heketi db management
help Help about any command Flags:
--config string Configuration file
-h, --help help for heketi
-v, --version Show version Use "heketi [command] --help" for more information about a command.

修改 Heketi 配置文件

vi /etc/heketi/heketi.json

#修改端口,默认 8080(服务器 8080 占用了)
"port": "8000",
......
# 允许认证
"use_auth": true,
......
# 修改admin用户的key
"key": "testtoken"
......
# 配置ssh的所需证书,对集群中的机器免密登陆
"executor": "ssh",
"sshexec": {
"keyfile": "/etc/heketi/heketi_key",
"user": "root",
"port": "22",
"fstab": "/etc/fstab"
},
......
# 定义heketi数据库文件位置
"db": "/var/lib/heketi/heketi.db"
......
#修改日志级别
"loglevel" : "info"

配置 SSH 密钥

#生成 rsa
ssh-keygen -t rsa -q -f /etc/heketi/heketi_key -N ''
chmod 700 /etc/heketi/heketi_key.pub # 复制 ssh 公钥上传到 GlusterFS 三台服务器(heketi 也可以单独部署)
ssh-copy-id -i /etc/heketi/heketi_key.pub root@172.23.216.48
ssh-copy-id -i /etc/heketi/heketi_key.pub root@172.23.216.49
ssh-copy-id -i /etc/heketi/heketi_key.pub root@172.23.216.50 # 验证是否能通过ssh密钥正常连接到 glusterfs 节点
ssh -i /etc/heketi/heketi_key root@172.23.216.49

启动 Heketi

$ nohup heketi --config=/etc/heketi/heketi.json &
nohup: ignoring input and appending output to ‘nohup.out’
$ cat nohup.out
Heketi 7.0.0
[heketi] INFO 2018/11/09 15:50:36 Loaded ssh executor
[heketi] INFO 2018/11/09 15:50:36 GlusterFS Application Loaded
[heketi] INFO 2018/11/09 15:50:36 Started Node Health Cache Monitor
Authorization loaded
Listening on port 8000
测试 Heketi 服务端
$ curl http://localhost:8000/hello
Hello from Heketi
Heketi 要求在每个 GlusterFS 节点上配备裸磁盘 device,不支持文件系統,一般如下配置,可以通过 fdisk –l 命令查看。
系统盘:/dev/vda
数据盘:/dev/vdb
云硬盘:/dev/vdc
查看磁盘
$ fdisk -l

Disk /dev/sda: 53.7 GB, 53687091200 bytes, 104857600 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk label type: dos
Disk identifier: 0x000d3387 Device Boot Start End Blocks Id System
/dev/sda1 * 2048 411647 204800 83 Linux
/dev/sda2 411648 8800255 4194304 82 Linux swap / Solaris
/dev/sda3 8800256 104857599 48028672 83 Linux
$ df -lh
Filesystem Size Used Avail Use% Mounted on
/dev/sda3 46G 5.1G 41G 11% /
devtmpfs 3.9G 0 3.9G 0% /dev
tmpfs 3.9G 0 3.9G 0% /dev/shm
tmpfs 3.9G 172M 3.7G 5% /run
tmpfs 3.9G 0 3.9G 0% /sys/fs/cgroup
/dev/sda1 197M 167M 30M 85% /boot
overlay 46G 5.1G 41G 11% /var/lib/docker/containers/1c3c53802122a9ce7e3044e83f22934bb700baeda1bedc249558e9a068e892a7/mounts/shm
overlay 46G 5.1G 41G 11% /var/lib/docker/overlay2/bbda116e3a230e59710afd2d9ec92817d65d71b82ccebf4d71bfc589c3605b75/merged
tmpfs 3.9G 12K 3.9G 1% /var/lib/kubelet/pods/fb62839c-dc19-11e8-90ea-0050569f4a19/volumes/kubernetes.io~secret/coredns-token-v245h
tmpfs 3.9G 12K 3.9G 1% /var/lib/kubelet/pods/fb638fce-dc19-11e8-90ea-0050569f4a19/volumes/kubernetes.io~secret/coredns-token-v245h
overlay 46G 5.1G 41G 11% /var/lib/docker/overlay2/a85cbca8be37d9e00565d83350721091105b74e1609d399a0bb1bb91a2c56e09/merged
shm 64M 0 64M 0%
tmpfs 783M 0 783M 0% /run/user/0
vdb 3.9G 0 3.9G 0% /mnt/disks/vdb
vdc 3.9G 0 3.9G 0% /mnt/disks/vdc
配置  topology-sample.json 
{
"clusters": [
{
"nodes": [
{
"node": {
"hostnames": {
"manage": [
"172.23.216.48"
],
"storage": [
"172.23.216.48"
]
},
"zone": 1
},
"devices": [
"/dev/vdb"
]
},
{
"node": {
"hostnames": {
"manage": [
"172.23.216.49"
],
"storage": [
"172.23.216.49"
]
},
"zone": 1
},
"devices": [
"/dev/vdb"
]
},
{
"node": {
"hostnames": {
"manage": [
"172.23.216.50"
],
"storage": [
"172.23.216.50"
]
},
"zone": 1
},
"devices": [
"/dev/vdb"
]
}
]
}
]
}

添加节点

$ heketi-cli --server http://localhost:8000 --user admin --secret "testtoken" topology load --json=topology-sample.json
Creating cluster ... ID: c2834ba9a3b5b6975150ad396b5ed7ca
Allowing file volumes on cluster.
Allowing block volumes on cluster.
Creating node 172.23.216.48 ... ID: 8c5cbad748520b529ea20f5296921928
Adding device /dev/vdb ... Unable to add device: Device /dev/vdb not found.
Found node 172.23.216.49 on cluster c13ecf0a70808a3dc8abcd8de908c1ea
Adding device /dev/vdb ... Unable to add device: Device /dev/vdb not found.
Found node 172.23.216.50 on cluster c13ecf0a70808a3dc8abcd8de908c1ea
Adding device /dev/vdb ... Unable to add device: Device /dev/vdb not found.

其他命令
#创建 cluster
heketi-cli --server http://localhost:8000 --user admin --secret "testtoken" topology load --json=topology-sample.json #创建 volume
heketi-cli --server http://localhost:8000 --user admin --secret "testtoken" volume create --size=3 --replica=2 #查看节点
heketi-cli --server http://localhost:8000 --user admin --secret "testtoken" node list

!!!由于没有多余的挂载磁盘,参考其他文章吧。

参考文章:

创建 StorageClass
vi glusterfs-storageclass.yaml

apiVersion: storage.k8s.io/v1beta1
kind: StorageClass
metadata:
name: glusterfs-sc
provisioner: kubernetes.io/glusterfs
parameters:
resturl: "http://172.23.216.48:8000"
restauthenabled: "true"
restuser: "admin"
restuserkey: "testtoken"
volumetype: "replicate:2"
上述 provisioner: kubernetes.io/glusterfs 是 Kubernetes 内置的存储插件的名字,不同的存储方式不同。

执行

$ kubectl apply -f glusterfs-storageclass.yaml
$ kubectl get sc
NAME PROVISIONER AGE
glusterfs-sc kubernetes.io/glusterfs 59s
上述是 Gluster github 上的示列使用 restuserkey 的方式 ,  Kubernetes 官方推荐的方式把 key 使用 secret 保存
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: slow
provisioner: kubernetes.io/glusterfs
parameters:
resturl: "http://127.0.0.1:8081"
clusterid: "630372ccdc720a92c681fb928f27b53f"
restuser: "admin"
secretNamespace: "default"
secretName: "heketi-secret"
gidMin: "40000"
gidMax: "50000"
volumetype: "replicate:3"
volumeoptions: "client.ssl on, server.ssl on"
volumenameprefix: "dept-dev"
snapfactor: "10"
---
apiVersion: v1
kind: Secret
metadata:
name: heketi-secret
namespace: default
data:
# base64 encoded password. E.g.: echo -n "mypassword" | base64
key: bXlwYXNzd29yZA==
type: kubernetes.io/glusterfs
示列:
创建 PVC
vi glusterfs-mysql-pvc.yaml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: glusterfs-mysql-pvc
annotations:
volume.beta.kubernetes.io/storage-class: glusterfs-sc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi

执行

$ kubectl apply -f glusterfs-mysql-pvc.yaml
persistentvolumeclaim/glusterfs-mysql-pvc created

有了 Dynamic Provisioning 机制,运维人员只需要在 Kubernetes 集群里创建出数量有限的 StorageClass 对象就可以了。运维人员在 Kubernetes 集群里创建出了各种各样的 PV 模板。开发人员提交了包含 StorageClass 字段的 PVC 之后,Kubernetes 就会根据这个 StorageClass 创建出对应的 PV。

Local Persistent Volume

实际运用中还有一种特殊的场景,比如在容器中部署数据库(主从同步数据,写数据频率高)对 IO 的性能与网络都会要求很高,用户希望 Kubernetes 能够直接使用宿主机上的本地磁盘目录,而不依赖于远程存储服务,来提供“持久化”的容器 Volume。这样做的好处很明显,由于这个 Volume 直接使用的是本地磁盘,尤其是 SSD 盘,读写性能相比于大多数远程存储来说要好很多。相比分布式存储缺点是数据一旦损坏,不具有备份与恢复的能力,需要定时备份到其他地方。

官方文档与资源:

有两种方式解决上述需求:

  • 在宿主机上额外挂载一个 SSD 磁盘,相当于增加一块本地磁盘,适用于生产环境。

  • 在宿主机上创建一个挂载点,挂载几个 RAM Disk 来模拟本地磁盘,适用于测试环境

模拟创建两块数据盘(vdb、vdc)

$ mkdir /mnt/disks
$ for vol in vdb vdc; do
mkdir /mnt/disks/$vol
mount -t tmpfs $vol /mnt/disks/$vol
done

创建 PV

vi local-pv.yaml

apiVersion: v1
kind: PersistentVolume
metadata:
name: example-pv
spec:
capacity:
storage: 5Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Delete
storageClassName: local-storage
local:
path: /mnt/disks/vdb
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- kubernetes-node-1 #指定固定 node 节点

查看

$ kubectl create -f local-pv.yaml
persistentvolume/example-pv created
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
example-pv 2Gi RWO Delete Available local-storage 12s $ kubectl describe pv example-pv
Name: example-pv
Labels: <none>
Annotations: <none>
Finalizers: [kubernetes.io/pv-protection]
StorageClass: local-storage
Status: Available
Claim:
Reclaim Policy: Delete
Access Modes: RWO
Capacity: 2Gi
Node Affinity:
Required Terms:
Term 0: kubernetes.io/hostname in [kubernetes-node-1]
Message:
Source:
Type: LocalVolume (a persistent volume backed by local storage on a node)
Path: /mnt/disks/vdb
Events: <none>

上述 PV 中 local 字段,指定了它是一个 Local Persistent Volume;而 path 字段,指定的是这个 PV 对应的本地磁盘的路径 ,意味着如果 Pod 要想使用这个 PV,那它就必须运行在 kubernetes-node-1 节点上

创建 StorageClass

vi local-sc.yaml

kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer

执行

$ kubectl create -f local-sc.yaml
$ kubectl get sc
NAME PROVISIONER AGE
local-storage kubernetes.io/no-provisioner 6s

创建 PVC(声明 storageClassName 是 local-storage

vi local-pvc.yaml 
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: example-local-claim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: local-storage #指定 sc

执行

$ kubectl apply -f local-pvc.yaml
persistentvolumeclaim/example-local-claim created
$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
example-local-claim Bound example-pv 2Gi RWO local-storage 2s

查看

上图显示 PV 与 PVC 已经是 Bound 状态,由于我的 Node 节点有2个,但是模拟的磁盘只在 Node-1 节点,所以我们要指定 Pod 运行的固定的 Node-1 节点上,我们通过打标签的方式,这样 Kubernetes 会调度 Pod 到指定的 Node上。

打标签

$ kubectl label nodes kubernetes-node-1 zone=node-1
$ kubectl get nodes --show-labels
NAME STATUS ROLES AGE VERSION LABELS
kubernetes-master Ready master 10d v1.12.2 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=kubernetes-master,node-role.kubernetes.io/master=
kubernetes-node-1 Ready <none> 10d v1.12.2 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=kubernetes-node-1,zone=node-1
kubernetes-node-2 Ready <none> 10d v1.12.2 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=kubernetes-node-2

部署 Nginx 测试

vi nginx-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
#nodeSelector:
#zone: node-1
nodeName: kubernetes-node-1 #指定调度节点为 kubernetes-node-1
containers:
- name: nginx-pv-container
image: nginx:1.10.3
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
volumeMounts:
- name: example-pv-storage
mountPath: "/usr/share/nginx/html"
volumes:
- name: example-pv-storage
persistentVolumeClaim:
claimName: example-local-claim

执行

$ kubectl create -f nginx-deployment.yaml
$ kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE
nginx-deployment-56bc98977b-jqg44 1/1 Running 0 104s 10.40.0.4 kubernetes-node-1 <none>
nginx-deployment-56bc98977b-tbkxr 1/1 Running 0 56s 10.40.0.5 kubernetes-node-1 <none>

创建文件

$ kubectl exec -it nginx-deployment-56bc98977b-jqg44 -- /bin/sh
# cd /usr/share/nginx/html
# touch test.html

查看文件

[root@kubernetes-node-1 vdb]# ll
total 0
-rw-r--r--. 1 root root 0 Nov 10 02:17 test.html

REFER:
https://kubernetes.io/docs/concepts/storage/volumes/
https://github.com/kubernetes/examples/tree/master/staging/volumes
https://www.ibm.com/developerworks/cn/opensource/os-cn-glusterfs-docker-volume/index.html
https://kubernetes.io/docs/tutorials/stateful-application/mysql-wordpress-persistent-volume/
https://docs.gluster.org/en/latest/
https://jimmysong.io/posts/kubernetes-with-glusterfs/

关于 Kubernetes 中的 Volume 与 GlusterFS 分布式存储的更多相关文章

  1. Kubernetes中的Volume介绍

    Kubernetes中支持的所有磁盘挂载卷简介发表于 2018年1月26日 Weihai Feb 10,2016 7400 字 | 阅读需要 15 分钟 容器磁盘上的文件的生命周期是短暂的,这就使得在 ...

  2. 在Kubernetes中部署GlusterFS+Heketi

    目录 简介 Gluster-Kubernetes 部署 环境准备 下载相关文件 部署glusterfs 部署heketi server端 配置heketi client 简介 在上一篇<独立部署 ...

  3. kubernetes对象之Volume

    系列目录 概述 Volume是对各种存储资源的抽象.虚拟化.为管理.控制.使用存储资源提供统一接口.Openstack中的volume为虚拟机提供存储,Docker中的volume为容器提供存储.因为 ...

  4. Centos7下GlusterFS分布式存储集群环境部署记录

    0)环境准备 GlusterFS至少需要两台服务器搭建,服务器配置最好相同,每个服务器两块磁盘,一块是用于安装系统,一块是用于GlusterFS. 192.168.10.239 GlusterFS-m ...

  5. GlusterFS分布式存储集群部署记录-相关补充

    接着上一篇Centos7下GlusterFS分布式存储集群环境部署记录文档,继续做一些补充记录,希望能加深对GlusterFS存储操作的理解和熟悉度. ======================== ...

  6. GlusterFS分布式存储学习笔记

    分布式文件系统 分布式文件系统(Distributed File System)是指文件系统管理的物理存储资源并不直接与本地节点相连,而是分布于计算网络中的一个或者多个节点的计算机上.目前意义上的分布 ...

  7. Kubernetes 中的pv和pvc

    原文地址:http://www.cnblogs.com/leidaxia/p/6485646.html 持久卷 PersistentVolumes 本文描述了 Kubernetes 中的 Persis ...

  8. Glusterfs 分布式存储安装部署

    Glusterfs 分布式存储部署 是存储当中可以选择的一种 现在很多虚拟化 云计算都在用软件存储 例如 ceph Glusterfs 等等 今天我们部署一下Glusterfs环境 GlusterFs ...

  9. kubernetes中的Pause容器如何理解?

    前几篇文章都是讲的Kubernetes集群和相关组件的部署,但是部署只是入门的第一步,得理解其中的一些知识才行.今天给大家分享下Kubernets的pause容器的作用. Pause容器 全称infr ...

随机推荐

  1. SpringMVC类型转换器、属性编辑器

    对于MVC框架,参数绑定一直觉得是很神奇很方便的一个东西,在参数绑定的过程中利用了属性编辑器.类型转换器 参数绑定流程 参数绑定:把请求中的数据,转化成指定类型的对象,交给处理请求的方法 请求进入到D ...

  2. varnish 隐藏版本号

    varnish 隐藏方法: 修改default.vcl配置文件. 找到或添加 vcl_deliver 子程序,代码如下: 1 2 3 4 5 sub vcl_deliver {        unse ...

  3. &lt;input type=&quot;file&quot; /&gt;浏览时只显示指定文件类型

    <input type="file" />浏览时只显示指定文件类型 <input type="file" accept="appli ...

  4. Linux rsync 同步

    rsync 是一个快速增量文件传输工具,它可以用于在同一主机备份内部的备分,我们还可以把它作为不同主机网络备份工具之用.本文主要讲述的是如何自架rsync服务器,以实现文件传输.备份和镜像.相对tar ...

  5. DataTable转List&lt;Model&gt;通用类【实体转换辅助类】

    /// <summary> /// DataTable转List<Model>通用类[实体转换辅助类] /// </summary> public class Mo ...

  6. flask中jinjia2模板使用详解2

    接上文 注释的使用 在jinjia2模板中,使用{# #}进行代码注释,如下所示 运行后发现,注释不会被render出来 去掉空行 两种方法可以去掉jinjia2模板中的空白行,一是设置jinjia2 ...

  7. DSAPI 导出EXEDLL函数到字符串

    EXE或者DLL写好了,要开始写函数说明文档了,可是有时里面的函数太多,怎么能自动列出来呢?在DSAPI中提供了该功能(目前没有做参数类型导出,以后有时间会添加). 先准备一个已经写好的EXE或DLL ...

  8. React Native移动开发实战-5-Android平台的调试技巧

    Android平台的调试和其他平台的调试也很类似,例如:在Android Studio打开的工程中,打开源码MainActivity.java,然后,将鼠标移至代码编辑区的左侧后,单击鼠标即可添加断点 ...

  9. 关于 ImageLoader 说的够细了。。。

    简介ImageLoader(一) 分类: android 开源及第三方项目2014-05-30 12:14 14126人阅读 评论(0) 收藏 举报 ImageLoader 使用该开源项目的之前,先给 ...

  10. Jedis使用过程中踩过的那些坑

    1. 一个 大坑:若实例化 JedisShardInfo 时不设置节点名称(name属性),那么当Redis节点列表的顺序发生变化时,会发生“ 键 rehash 现象” 使用BTrace追踪redis ...