關於 Kubernetes 中的 Volume 與 GlusterFS 分布式存儲


容器中持久化的文件生命周期是短暫的,如果容器中程序崩潰宕機,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 中查看存儲卷的信息

image

測試數據卷

$  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

查看

image

image

上圖顯示 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/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM