27

Kubernetes与GlusterFS的爱恨情仇

 3 years ago
source link: http://dockone.io/article/10390
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.

Kubernetes,容器管理领域的领导者;GlusterFS,分布式文件系统的后起之秀,他们之间会擦出什么样的火花呢?

用Kubernetes部署的应用可以分为无状态的和有状态的,无状态的应用没有数据,Pod(一个或若干容器的集合)挂了被重新拉起,或者在Kubernetes集群不同的Node节点(可以认为是一台物理机或虚拟机)之间飘来飘去,都没有关系;有状态的应用有数据需要保存,如果容器挂了被重新拉起,容器里面保存的数据就没了。这时候我们自然而然的想到可以把数据映射到容器所在主机上,就像我们使用Docker时经常做的一样,可是这时候有个问题是,Kubernetes集群一般有多个Node节点,如果容器在挂了被重新拉起的时候被调度到其他的Node节点,那映射在原先主机上的数据还是在原先主机上,新的容器还是没有原来的数据。

怎么办呢?

这时候我们的另一位主角就要出场了——对,就是把数据存储在分布式存储GlusterFS上,Pod通过网络连接到分布式存储,这样不管Pod怎么在不同的Node节点间飘,连接的都是同一个分布式存储,数据都还在。

这么说是Kubernetes需要GlusterFS了,都说“被需要的都有恃无恐”,那么GlusterFS是不是可以有恃无恐了呢?--当然不是,毕竟在这个残酷的世界,没有人是可以活得那么舒服的。事实上,Kubernetes的选择很多,目前Kubernetes支持的存储有下面这些:

  • gcePersistentDisk
  • awsElasticBlockStore
  • AzureFile
  • AzureDisk
  • FC(Fibre Channel)
  • FlexVolume
  • Flocker
  • NFS
  • iSCSI
  • RBD(Ceph Block Device)
  • CephFS
  • Cinder(OpenStack Block Storage)
  • Glusterfs
  • VsphereVolume
  • Quobyte Volumes
  • HostPath(就是刚才说的映射到主机的方式,多个Node节点会有问题)
  • VMware Photon
  • Portworx Volumes
  • ScaleIO Volumes
  • StorageOS

Kubernetes有这么多选择,GlusterFS只是其中之一,GlusterFS也有自己的优点,它是一个开源的分布式文件系统,具有强大的横向扩展能力,通过扩展能够支持数PB存储容量和处理数千客户端。GlusterFS借助TCP/IP或InfiniBand RDMA网络将物理分布的存储资源聚集在一起,使用单一全局命名空间来管理数据。GlusterFS的Volume有多种模式,复制模式可以保证数据的高可靠性,条带模式可以提高数据的存取速度,分布模式可以提供横向扩容支持,几种模式可以组合使用实现优势互补。

下面就来看看Kubernetes和GlusterFS是怎么结合起来的吧,实战开始了。

部署Kubernetes

部署方法可参考Kubernetes官网。

假设Kubernetes部署在

Master:

192.168.XX.A

Node:

192.168.XX.A

192.168.XX.B

192.168.XX.C

Kubernetes版本:

# kubectl --version

Kubernetes v1.5.2

部署GlusterFS

部署机器(这里跟Kubernetes部署在同样的机器):

192.168.XX.A

192.168.XX.B

192.168.XX.C

在每台机器的/etc/hosts加上:

192.168.XX.A glusterfs1

192.168.XX.B glusterfs2

192.168.XX.C glusterfs3

安装yum源(每台机器执行):

yum install centos-release-gluster

安装GlusterFS(每台机器执行):

yum -y install glusterfs glusterfs-fuse glusterfs-server

安装结束。

启动GlusterFS(每台机器执行):

systemctl start glusterd.service  

systemctl enable glusterd.service  

组建集群(192.168.XX.A 机器执行):

gluster peer probe glusterfs2  

gluster peer probe glusterfs3  

验证(192.168.XX.A 机器执行):

# gluster peer status 

Number of Peers: 2



Hostname: glusterfs2

Uuid: 30efc726-35b5-4502-8f7f-f238ea44f3aa

State: Peer in Cluster (Connected)

Other names:

192.168.XX.B



Hostname: glusterfs3

Uuid: 2c7aaa1b-4d51-4560-88be-cbe42e30b7a3

State: Peer in Cluster (Connected)

Other names:

192.168.XX.C

看到其他两个点的信息即代表GlusterFS集群组建成功。

Kubernetes使用GlusterFS

有两种方式,手动和自动,手动需要每次使用存储时自己创建GlusterFS的卷(GlusterFS的数据存储在卷Volume上);自动利用Kubernetes的 Dynamic Provisioning 特性,可以由Kubernetes自动创建GlusterFS卷,但是需要先部署Heketi软件,并且安装GlusterFS的机器上还要有裸磁盘。

手动方式:

1)创建GlusterFS卷

新建卷(3个副本的复制模式):

(在每台机器执行:)

mkdir -p /data/brick1/gv0



(在一台机器执行:)

gluster volume create gv0 replica 3 glusterfs1:/data/brick1/gv0 glusterfs2:/data/brick1/gv0 glusterfs3:/data/brick1/gv0 force

启动卷:

(在一台机器执行:)

gluster volume start gv0 

查看卷:

(在一台机器执行:)

# gluster volume info  



Volume Name: gv0

Type: Replicate

Volume ID: 2f8147de-fcb6-4219-81a3-71d6cfcaa609

Status: Started

Snapshot Count: 0

Number of Bricks: 1 x 3 = 3

Transport-type: tcp

Bricks:

Brick1: glusterfs1:/data/brick1/gv0

Brick2: glusterfs2:/data/brick1/gv0

Brick3: glusterfs3:/data/brick1/gv0

Options Reconfigured:

transport.address-family: inet

nfs.disable: on

可以看到所创建的卷的信息。

2)Kubernetes创建PV等存储

Kubernetes用PV(PersistentVolume)、PVC(PersistentVolumeClaim)来使用GlusterFS的存储,PV与GlusterFS的Volume相连,相当于提供存储设备,所以需要由知道GlusterFS Volume的系统管理员创建(这里我们自己就是系统管理员);PVC消耗PV提供的存储,由应用部署人员创建,应用直接使用PVC进而使用PV的存储。

以下操作在Kubernetes Master节点执行。

系统管理员创建Endpoint、Service、PV(Endpoint和Service不用每次都建,可以复用):

先创建三个文件:

glusterfs-endpoints.json:

{

"kind": "Endpoints",

"apiVersion": "v1",

"metadata": {

"name": "glusterfs-cluster"

},

"subsets": [

{

  "addresses": [

    {

      "ip": "192.168.XX.A"

    }

  ],

  "ports": [

    {

      "port": 1

    }

  ]

},

{

  "addresses": [

    {

      "ip": "192.168.XX.B"

    }

  ],

  "ports": [

    {

      "port": 1

    }

  ]

},

{

  "addresses": [

    {

      "ip": "192.168.XX.C"

    }

  ],

  "ports": [

    {

      "port": 1

    }

  ]

}

]

} 

glusterfs-service.json:

{

"kind": "Service",

"apiVersion": "v1",

"metadata": {

"name": "glusterfs-cluster"

},

"spec": {

"ports": [

  {"port": 1}

]

}

} 

glusterfs-pv.yaml:

apiVersion: v1

kind: PersistentVolume

metadata:

name: gluster-dev-volume1

labels:

name: mysql1 

spec:

capacity:

storage: 1Gi

accessModes:

- ReadWriteMany

glusterfs:

endpoints: "glusterfs-cluster"

path: "gv0"

readOnly: false

执行命令创建:

kubectl apply -f glusterfs-endpoints.json

kubectl apply -f glusterfs-service.json

kubectl apply -f glusterfs-pv.yaml

查看Endpoint、Service、PV:

kubectl get ep

kubectl get svc

kubectl get pv

3)Kubernetes创建应用

应用部署人员创建PVC及应用(这里以MySQL为例):

创建两个文件:

glusterfs-pvc.yaml:

kind: PersistentVolumeClaim

apiVersion: v1

metadata:

name: glusterfs-mysql1

spec:

accessModes:

- ReadWriteMany

resources:

requests:

  storage: 1Gi

selector:

matchLabels:

  name: "mysql1"

mysql-deployment.yaml:

apiVersion: v1

kind: Service

metadata:

name: mysql

labels:

name: mysql

spec:

type: NodePort

ports:

- name: mysqlport

port: 3306

nodePort: 31016

selector:

name: mysql

---

apiVersion: extensions/v1beta1 

kind: Deployment 

metadata: 

name: mysql

spec: 

replicas: 1

template: 

metadata: 

  labels: 

    name: mysql

spec: 

  containers: 

    - name: mysqlcontainer

      image: registry.hundsun.com/library/mysql:5.7.12

      imagePullPolicy: IfNotPresent

      env:

      - name: MYSQL_ROOT_PASSWORD

        value: root12345

      ports: 

        - containerPort: 3306

      volumeMounts:

        - name: gluster-mysql-data

          mountPath: "/var/lib/mysql"

  volumes:

  - name: gluster-mysql-data

    persistentVolumeClaim:

      claimName: glusterfs-mysql1

执行命令创建:

kubectl apply -f glusterfs-pvc.yaml

kubectl apply -f mysql-deployment.yaml

查看PVC、Service:

kubectl get pvc

kubectl get svc

查看Pod:

# kubectl get po -o wide

NAME                       READY     STATUS    RESTARTS   AGE       IP            NODE

mysql-1858843218-68cts     1/1       Running   0          30m       10.254.39.9   192.168.XX.C

看到MySQL Pod是Running状态,表示容器创建成功,运行在 192.168.XX.C 节点上。

4)测试

可以用MySQL客户端连接(连接的IP是任意Kubernetes Node节点IP,31016和root12345是mysql-deployment.yaml里指定的映射端口和MySQL root用户密码),连接后我们建一个数据库liao:

# ./mysql -h 192.168.XX.A -P 31016 -uroot -proot12345

Welcome to the MariaDB monitor.  Commands end with ; or \g.

Your MySQL connection id is 3

Server version: 5.7.12 MySQL Community Server (GPL)



Copyright (c) 2000, 2016, Oracle, MariaDB Corporation Ab and others.



Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.



MySQL [(none)]> 

MySQL [(none)]> create database liao;

Query OK, 1 row affected (0.02 sec)



MySQL [(none)]> 

MySQL [(none)]> show databases;

+--------------------+

| Database           |

+--------------------+

| information_schema |

| liao               |

| mysql              |

| performance_schema |

| sys                |

+--------------------+

5 rows in set (0.01 sec)



MySQL [(none)]> 

可以看到已经成功创建了一个数据库liao。

现在我们测试一下Pod重新调度后数据还在不在:

删除Pod :

kubectl delete po mysql-1858843218-68cts

Pod被删除后Kubernetes会自动把它重新拉起,在哪个Node节点上拉起由Kubernetes自己调度决定。

过一小会儿重新查看Pod:

# kubectl get po -o wide

NAME                       READY     STATUS    RESTARTS   AGE       IP            NODE

mysql-1858843218-v1fvf     1/1       Running   0          28s       10.254.34.5   192.168.XX.A

可以看到Pod已经被调度到192.168.XX.A上。

MySQL客户端连接并查看数据库:

# ./mysql -h 192.168.XX.A -P 31016 -uroot -proot12345

Welcome to the MariaDB monitor.  Commands end with ; or \g.

Your MySQL connection id is 2

Server version: 5.7.12 MySQL Community Server (GPL)



Copyright (c) 2000, 2016, Oracle, MariaDB Corporation Ab and others.



Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.



MySQL [(none)]> 

MySQL [(none)]> 

MySQL [(none)]> show databases;

+--------------------+

| Database           |

+--------------------+

| information_schema |

| liao               |

| mysql              |

| performance_schema |

| sys                |

+--------------------+

5 rows in set (0.03 sec)



MySQL [(none)]> 

可以看到我们建的liao数据库还在,说明虽然Pod重新调度,但数据还在。

有人会疑问这里两次连接的都是192.168.XX.A的31016端口,为什么说连接到了不同的Pod,这是因为Kubernetes的kube-proxy会把我们配置的Service里映射的端口在每个Kubernetes Node上都开出来,也就是在任何一个Kubernetes Node上都能连接到Pod,Pod重新调度后,还是在任何一个Kubernetes Node上都能连接,但后面的Pod其实已经是新的了。

自动方式

自动方式需要先部署Heketi软件,Heketi用来管理GlusterFS,并提供RESTful API接口供Kubernetes调用。Heketi需要使用裸磁盘,假设三个GlusterFS节点上都挂了一块裸磁盘 /dev/xvde。

1)部署Heketi

部署在:

192.168.XX.A

安装:

yum install heketi heketi-client -y

修改/etc/heketi/heketi.json(省略了没有修改的部分):

{

...

"port": "8083",



...



"_glusterfs_comment": "GlusterFS Configuration",

"glusterfs": {

"_executor_comment": [

  "Execute plugin. Possible choices: mock, ssh",

  "mock: This setting is used for testing and development.",

  "      It will not send commands to any node.",

  "ssh:  This setting will notify Heketi to ssh to the nodes.",

  "      It will need the values in sshexec to be configured.",

  "kubernetes: Communicate with GlusterFS containers over",

  "            Kubernetes exec api."

],

"executor": "ssh",



"_sshexec_comment": "SSH username and private key file information",

"sshexec": {

  "keyfile": "/home/liao/key/key",

  "user": "root",

  "port": "22",

  "fstab": "/etc/fstab"

},



...

}

} 

这里主要把端口改为8083了(防止冲突),executor改为ssh, sshexec的各项配置也做了相应修改。

其中的Keyfile制作方法:

ssh-keygen -t rsa

输入key(随便起的名字),一直回车。

制作完成后会在当前目录下生成key、key.pub,把 key.pub上传到GlusterFS三台服务器的/root/.ssh/下面,并重命名为authorized_keys,/etc/heketi/heketi.json中的Keyfile指向生成的key(包含路径)。

启动:

systemctl enable heketi

systemctl start heketi

看日志:

journalctl -u heketi

(Heketi数据目录: /var/lib/heketi)

验证:

curl http://192.168.XX.A:8083/hello

或:

heketi-cli --server http://192.168.XX.A:8083 cluster list

配置节点:

新建topology.json:

{

"clusters": [

    {

        "nodes": [

            {

                "node": {

                    "hostnames": {

                        "manage": [

                            "glusterfs1"

                        ],

                        "storage": [

                            "192.168.XX.A"

                        ]

                    },

                    "zone": 1

                },

                "devices": [

                    "/dev/xvde"

                ]

            },

            {

                "node": {

                    "hostnames": {

                        "manage": [

                            "glusterfs2"

                        ],

                        "storage": [

                            "192.168.XX.B"

                        ]

                    },

                    "zone": 2

                },

                "devices": [

                    "/dev/xvde"

                ]

            },

            {

                "node": {

                    "hostnames": {

                        "manage": [

                            "glusterfs3"

                        ],

                        "storage": [

                            "192.168.XX.C"

                        ]

                    },

                    "zone": 1

                },

                "devices": [

                    "/dev/xvde"

                ]

            }

        ]

    }

]

} 

载入配置:

export HEKETI_CLI_SERVER=http://192.168.XX.A:8083

heketi-cli topology load --json=topology.json

查看拓扑:

heketi-cli topology info

建个大小为2G的volume试试:

heketi-cli volume create --size=2

查看:

heketi-cli volume list

删除:

heketi-cli volume delete <Id>

2)Kubernetes创建StorageClass

Kubernetes通过创建StorageClass来使用 Dynamic Provisioning 特性,StorageClass连接Heketi,可以根据需要自动创建GluserFS的Volume,StorageClass还是要系统管理员创建,不过StorageClass不需要每次创建,因为这个不需要很多,不同的PVC可以用同一个StorageClass。

新建文件:

glusterfs-storageclass.yaml:

apiVersion: storage.k8s.io/v1beta1

kind: StorageClass

metadata:

name: slow

provisioner: kubernetes.io/glusterfs

parameters:

resturl: "http://192.168.XX.A:8083"

volumetype: "replicate:3"

replicate:3代表会创建三个副本复制模式的GluserFS Volume。

执行命令创建:

kubectl apply -f glusterfs-storageclass.yaml

查看:

# kubectl get storageclass

NAME      TYPE

slow      kubernetes.io/glusterfs

3)Kubernetes创建应用

应用部署人员创建PVC及应用(还是以MySQL为例)。

创建两个文件:

glusterfs-pvc.yaml:

kind: PersistentVolumeClaim

apiVersion: v1

metadata:

name: glusterfs-mysql1

annotations:

volume.beta.kubernetes.io/storage-class: "slow"

spec:

accessModes:

- ReadWriteMany

resources:

requests:

  storage: 1Gi

mysql-deployment.yaml:

apiVersion: v1

kind: Service

metadata:

name: mysql

labels:

name: mysql

spec:

type: NodePort

ports:

- name: mysqlport

port: 3306

nodePort: 31016

selector:

name: mysql

---

apiVersion: extensions/v1beta1 

kind: Deployment 

metadata: 

name: mysql

spec: 

replicas: 1

template: 

metadata: 

  labels: 

    name: mysql

spec: 

  containers: 

    - name: mysqlcontainer

      image: registry.hundsun.com/library/mysql:5.7.12

      imagePullPolicy: IfNotPresent

      env:

      - name: MYSQL_ROOT_PASSWORD

        value: root12345

      ports: 

        - containerPort: 3306

      volumeMounts:

        - name: gluster-mysql-data

          mountPath: "/var/lib/mysql"

  volumes:

  - name: gluster-mysql-data

    persistentVolumeClaim:

      claimName: glusterfs-mysql1

执行命令创建:

kubectl apply -f glusterfs-pvc.yaml

kubectl apply -f mysql-deployment.yaml

查看Endpoint、Service、PV,可以发现这些都自动建好了:

# kubectl get ep

NAME                                 ENDPOINTS                                                     AGE

glusterfs-dynamic-glusterfs-mysql1   192.168.XX.A:1,192.168.XX.B:1,192.168.XX.C:1                  9s
# kubectl get svc

NAME                                 CLUSTER-IP       EXTERNAL-IP     PORT(S)     AGE

glusterfs-dynamic-glusterfs-mysql1   10.254.132.6     <none>          1/TCP       42s
# kubectl get pv

NAME                                       CAPACITY   ACCESSMODES   RECLAIMPOLICY   STATUS    CLAIM                      REASON    AGE

pvc-f81121a0-ae8b-11e7-a91a-286ed488c82a   1Gi        RWX           Delete          Bound     default/glusterfs-mysql1             4m

查看PVC:

# kubectl get pvc

NAME               STATUS    VOLUME                                     CAPACITY   ACCESSMODES   AGE

glusterfs-mysql1   Bound     pvc-f81121a0-ae8b-11e7-a91a-286ed488c82a   1Gi        RWX           5m

可以看到PV和PVC已经绑定好。

还是可以用刚才的命令连接到MySQL:

mysql -h 192.168.XX.A -P 31016 -uroot -proot12345

按刚才的方式测试MySQL Pod重新调度后数据还在不在,可以发现数据还在。

从上面可以看到手动方式需要系统管理员每次手动建GlusterFS的Volume和Kubernetes的PV,或者系统管理员事先建好一批Volume和PV;而自动方式不需要,Kubernetes可以根据应用部署人员的需要动态创建Volume和PV,节省了很多工作量,推荐使用自动方式。

原文链接:

作者:廖林荣


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK