说明
最近由于需要部署有状态服务,没有云环境的支持,不能很好的通过cloud provider调用云磁盘EBS,所以在node节点部署了一套glusterFS分布式文件存储系统,用来提供非结构化数据存储,存储图片,大文件等等.
Kubernetes中使用GlusterFS作为持久化存储,要提供storageClass使用需要依赖Heketi工具。Heketi是一个具有resetful接口的glusterfs管理程序,作为kubernetes的Storage存储的external provisioner。 “Heketi提供了一个RESTful管理界面,可用于管理GlusterFS卷的生命周期。借助Heketi,像OpenStack Manila,Kubernetes和OpenShift这样的云服务可以动态地配置GlusterFS卷和任何支持的持久性类型。Heketi将自动确定整个集群的brick位置,确保将brick及其副本放置在不同的故障域中。Heketi还支持任意数量的GlusterFS集群,允许云服务提供网络文件存储,而不受限于单个GlusterFS集群。
heketi:提供基于RESTful接口管理glusterfs的功能,可以方便的创建集群管理glusterfs的node,device,volume;与k8s结合可以创建动态的PV,扩展glusterfs存储的动态管理功能。主要用来管理glusterFS volume的生命周期,初始化时候就要分配好裸磁盘(未格式化)设备.
注意事项
- 安装Glusterfs客户端:每个kubernetes集群的节点需要安装gulsterfs的客户端,如glusterfs-cli,glusterfs-fuse.主要用于在每个node节点挂载volume。
- 加载内核模块:每个kubernetes集群的节点运行
modprobe dm_thin_pool
,加载内核模块。 - 高可用(至少三个节点):至少需要节点用来部署glusterfs集群,并且这3个节点每个节点需要至少一个空余的磁盘。
基础设施要求:
- 正在运行的Kubernetes集群,至少有三个node节点,每个节点至少有一个可用的裸块设备(如EBS卷或本地磁盘,就是没有格式化的).
- 用于运行GlusterFS节点必须为GlusterFS通信打开相应的端口(如果开启了防火墙的情况下,没开防火墙就不需要这些操作)。在每个节点上运行以下命令
$ sudo iptables -N heketi
$ sudo iptables -A heketi -p tcp -m state --state NEW -m tcp --dport 24007 -j $ sudo ACCEPT
$ sudo iptables -A heketi -p tcp -m state --state NEW -m tcp --dport 24008 -j ACCEPT
$ sudo iptables -A heketi -p tcp -m state --state NEW -m tcp --dport 2222 -j ACCEPT
$ sudo iptables -A heketi -p tcp -m state --state NEW -m multiport --dports 49152:49251 -j ACCEPT
$ sudo service iptables save
Heketi安装
Heketi是由golang编写,直接静态编译运行二进制即可也可以通过yum安装以及docker部署,主要会产生db文件存储cluster、node、device、volume等信息。
#/bin/bash
wget -c https://github.com/heketi/heketi/releases/download/v8.0.0/heketi-v8.0.0.linux.amd64.tar.gz
tar xzf heketi-v8.0.0.linux.amd64.tar.gz
mkdir -pv /data/heketi/{bin,conf,data}
cp heketi/heketi.json /data/heketi/conf/
cp heketi/{heketi,heketi-cli} /data/heketi/bin/
mv heketi-v8.0.0.linux.amd64.tar.gz /tmp/
创建ssh-key
我们glusterFS部署在k8s集群外,所以heketi通过ssh管理glusterFS。需要创建免秘钥登陆到所有glusterFS节点。
$ sudo ssh-keygen -f /data/heketi/conf/heketi_key -t rsa -N ''
# 将公钥放到所有GlusterFS节点
$ sudo ssh-copy-id -i /data/heketi/conf/heketi_key.pub root@node1
$ ...
配置文件修改
参考https://github.com/heketi/heketi/blob/master/client/cli/go/topology-sample.json
{
"_port_comment": "Heketi Server Port Number",
"port": "18080",
"_use_auth": "Enable JWT authorization. Please enable for deployment",
# 开启认证
"use_auth": true,
"_jwt": "Private keys for access",
"jwt": {
"_admin": "Admin has access to all APIs",
# admin用户的key
"admin": {
"key": "adminkey"
},
"_user": "User only has access to /volumes endpoint",
"user": {
"key": "userkey"
}
},
"_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."
],
# 修改执行插件为ssh,并配置ssh的所需证书,注意要能对集群中的机器免密ssh登陆,使用ssh-copy-id把pub key拷到每台glusterfs服务器上
# heketi 通过ssh远程控制glusterfs的volume周期管理。
"executor": "ssh",
"_sshexec_comment": "SSH username and private key file information",
"sshexec": {
# ssh-keygen 生成,然后拷贝到glusterfs所在机器
"keyfile": "/data/heketi/conf/heketi_key",
"user": "root",
"port": "22",
# 每创建一个volume,heketi会刷新fstab配置保证机器自启动会自动挂载。
"fstab": "/etc/fstab"
},
"_kubeexec_comment": "Kubernetes configuration",
"kubeexec": {
"host" :"https://kubernetes.host:8443",
"cert" : "/path/to/crt.file",
"insecure": false,
"user": "kubernetes username",
"password": "password for kubernetes user",
"namespace": "OpenShift project or Kubernetes namespace",
"fstab": "Optional: Specify fstab file on node. Default is /etc/fstab"
},
"_db_comment": "Database file name",
# 修改heketi默认db路径
"db": "/data/heketi/data/heketi.db",
"_loglevel_comment": [
"Set log level. Choices are:",
" none, critical, error, warning, info, debug",
"Default is warning"
],
"loglevel" : "debug"
}
}
注意:
- 需要说明的是,heketi有三种executor,分别为mock、ssh、kubernetes,建议在测试环境使用mock,生产环境使用ssh,当glusterfs以容器的方式部署在kubernetes上时,才使用kubernetes。我们这里将glusterfs和heketi独立部署,使用ssh的方式。
- 使用docker部署的时候,还需将/var/lib/heketi/mounts 挂载至容器里面, heketi 会将此目录作为 gluster volume的挂载点。
[root@k8s1 ~]# more /etc/fstab
#
# /etc/fstab
# Created by anaconda on Sun Oct 15 15:19:00 2017
#
# Accessible filesystems, by reference, are maintained under '/dev/disk'
# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
#
UUID=eb448abb-3012-4d8d-bcde-94434d586a31 / ext4 defaults 1 1
UUID=63b64b3a-2961-4798-b7f1-cc97484ee49f /data ext4 defaults 1 1
/dev/mapper/vg_fd3a11426117508af77aa38e9565ce65-brick_4b7854340fa8ee78106b1b1446abf078 /var/lib/heketi/mounts/vg_fd3a11426117508af77aa38e9565ce65/brick_4b
7854340fa8ee78106b1b1446abf078 xfs rw,inode64,noatime,nouuid 1 2
/dev/mapper/vg_fd3a11426117508af77aa38e9565ce65-brick_1904166cdf0846ee649ccefe66ce1e50 /var/lib/heketi/mounts/vg_fd3a11426117508af77aa38e9565ce65/brick_19
04166cdf0846ee649ccefe66ce1e50 xfs rw,inode64,noatime,nouuid 1 2
systemd配置
# cat /usr/lib/system/systemd/heketi.service
[Unit]
Description=RESTful based volume management framework for GlusterFS
Before=network-online.target
After=network-online.target
Documentation=https://github.com/heketi/heketi
[Service]
Type=simple
LimitNOFILE=65536
ExecStart=/data/heketi/bin/heketi --config=/data/heketi/conf/heketi.json
KillMode=process
Restart=on-failure
RestartSec=5
SuccessExitStatus=15
StandardOutput=syslog
StandardError=syslog
[Install]
WantedBy=multi-user.target
启动heketi服务
$ sudo systemctl start heketi
$sudo systemctl enable heketi
$sudo systemctl status heketi
Heketi管理GlusterFS
添加cluster
$ sudo heketi-cli --user admin --server http://10.111.209.188:18080 --secret adminkey --json cluster create
{"id":"d102a74079dd79aceb3c70d6a7e8b7c4","nodes":[],"volumes":[],"block":true,"file":true,"blockvolumes":[]}
将3个glusterfs节点作为node添加到cluster
由于我们开启了heketi认证,所以每次执行heketi-cli操作时,都需要带上一堆的认证字段,比较麻烦,我在这里创建一个别名来避免相关操作:
$ sudo alias heketi-cli='heketi-cli --server "[http://192.168.75.175:18080"](http://192.168.75.175:18080/) --user "admin" --secret "adminkey"'
添加节点
$sudo heketi-cli --json node add --cluster "d102a74079dd79aceb3c70d6a7e8b7c4" --management-host-name k8s1 --storage-host-name 10.111.209.188 --zone 1
$sudo heketi-cli --json node add --cluster "d102a74079dd79aceb3c70d6a7e8b7c4" --management-host-name k8s1 --storage-host-name 10.111.209.189 --zone 1
$sudo heketi-cli --json node add --cluster "d102a74079dd79aceb3c70d6a7e8b7c4" --management-host-name k8s3 --storage-host-name 10.111.209.190 --zone 1
注意: 如果heketi 运行账号为非root,需要保证有sudo权限。
添加device
机器只是作为gluster的运行单元,volume是基于device创建的。同时需要特别说明的是,目前heketi仅支持使用裸分区或裸磁盘(未格式化)添加为device,不支持文件系统。
# --node参数给出的id是上一步创建node时生成的,这里只给出一个添加的示例,实际配置中,要添加每一个节点的每一块用于存储的硬盘`
$ sudo heketi-cli -json device add -name="/dev/vdc1" --node ``"2e4dc73fb2657586e7a9e64e39c8f01a"
$ sudo heketi-cli node list`
Id:2e4dc73fb2657586e7a9e64e39c8f01a Cluster:d102a74079dd79aceb3c70d6a7e8b7c4
Id:43a661a917c42dfe9b6659b8c9d848e9 Cluster:d102a74079dd79aceb3c70d6a7e8b7c4
Id:ce5a9e2983d067051b6ccad0a4bd0988 Cluster:d102a74079dd79aceb3c70d6a7e8b7c4
生产实际配置
以上ad-hoc命令均可通过配置文件创建然后导入:
$ sudo cat /data/heketi/conf/topology.json
{
"clusters": [
{
"nodes": [
{
"node": {
"hostnames": {
"manage": [
"k8s1"
],
"storage": [
"10.111.209.188"
]
},
"zone": 1
},
"devices": [
"/dev/vdc1"
]
},
{
"node": {
"hostnames": {
"manage": [
"k8s2"
],
"storage": [
"10.111.209.189"
]
},
"zone": 1
},
"devices": [
"/dev/vdc1"
]
},
{
"node": {
"hostnames": {
"manage": [
"k8s3"
],
"storage": [
"10.111.209.190"
]
},
"zone": 1
},
"devices": [
"/dev/vdc1"
]
}
]
}
]
}
创建:
$ sudo heketi-cli topology load --json topology.json
添加volume
这里仅仅是做一个测试,实际使用中,会由kubernetes自动创建pv.
创建一个大小为3G,副本为2的volume
$ sudo heketi-cli volume create --size 3 --replica 2
Name: vol_685f9aea1896f53f30a22a9d15de1680
Size: 3
Volume Id: 685f9aea1896f53f30a22a9d15de1680
Cluster Id: d102a74079dd79aceb3c70d6a7e8b7c4
Mount: 10.111.209.188:vol_685f9aea1896f53f30a22a9d15de1680
Mount Options: backup-volfile-servers=10.111.209.190,10.111.209.189
Block: false
Free Size: 0
Reserved Size: 0
Block Hosting Restriction: (none)
Block Volumes: []
Durability Type: replicate
Distributed+Replica: 2
kubernetes storageclass 配置
创建storageclass
添加storageclass-glusterfs.yaml文件,内容如下:
---
apiVersion: v1
kind: Secret
metadata:
name: heketi-secret
namespace: default
data:
# base64 encoded password. E.g.: echo -n "mypassword" | base64
key: YWRtaW5rZXk=
type: kubernetes.io/glusterfs
---
apiVersion: storage.k8s.io/v1beta1
kind: StorageClass
metadata:
name: glusterfs
provisioner: kubernetes.io/glusterfs
allowVolumeExpansion: true
parameters:
resturl: "http://10.111.209.188:18080"
clusterid: "6fd6bf78b84315e12abcf8b6db6b1a40"
restauthenabled: "true"
restuser: "admin"
#secretNamespace: "default"
#secretName: "heketi-secret"
restuserkey: "adminkey"
gidMin: "40000"
gidMax: "50000"
volumetype: "replicate:2"
# kubectl apply -f glusterfs-storageclass.yml
secret/heketi-secret created
storageclass.storage.k8s.io/glusterfs created
# kubectl get sc
NAME PROVISIONER AGE
glusterfs (default) kubernetes.io/glusterfs 2m19s
注意:
- storageclass.beta.kubernetes.io/is-default-class: "true" #表示此storageClass作为default sc,创建pvc不指定sc时,默认使用此sc.
- reclaimPolicy: Retain #表示pv回收策略为保留,删除pvc时将不删除pv。
更详细的用法参考:https://kubernetes.io/docs/concepts/storage/storage-classes/#glusterfs
创建pvc
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: glusterfs-mysql1
namespace: default
annotations:
volume.beta.kubernetes.io/storage-class: "glusterfs"
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
# kubectl create -f glusterfs-pvc.yaml
persistentvolumeclaim/glusterfs-mysql1 created
# kubectl get pv,pvc
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
persistentvolume/pvc-11aa48c5-fe0f-11e8-9803-00163e13b711 1Gi RWX Retain Bound default/glusterfs-mysql1 glusterfs 2m
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/glusterfs-mysql1 Bound pvc-11aa48c5-fe0f-11e8-9803-00163e13b711 1Gi RWX glusterfs 2m20s
创建pod,使用pvc
---
kind: Deployment
apiVersion: extensions/v1beta1
metadata:
name: mysql
namespace: default
spec:
replicas: 1
template:
metadata:
labels:
name: mysql
spec:
containers:
- name: mysql
image: mysql:5.7
imagePullPolicy: IfNotPresent
env:
- name: MYSQL_ROOT_PASSWORD
value: root123456
ports:
- containerPort: 3306
volumeMounts:
- name: gluster-mysql-data
mountPath: "/var/lib/mysql"
volumes:
- name: gluster-mysql-data
persistentVolumeClaim:
claimName: glusterfs-mysql1
# kubectl create -f /etc/kubernetes/mysql-deployment.yaml
deployment.extensions/mysql created
# kubectl exec -it mysql-84786cf494-hb2ss -- df -PHT /var/lib/mysql
Filesystem Type Size Used Avail Use% Mounted on
10.111.209.188:vol_426e62366141c789dac33f2e68dfb13b fuse.glusterfs 1.1G 266M 798M 25% /var/lib/mysql
创建statefulset
---
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None
selector:
app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: nginx
spec:
selector:
matchLabels:
app: nginx # has to match .spec.template.metadata.labels
serviceName: "nginx"
replicas: 3 # by default is 1
template:
metadata:
labels:
app: nginx # has to match .spec.selector.matchLabels
spec:
terminationGracePeriodSeconds: 10
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "glusterfs"
resources:
requests:
storage: 1Gi
# kubectl apply -f nginx-statefulset.yml
service/nginx created
statefulset.apps/nginx created
# kubectl get pod,pv,pvc
NAME READY STATUS RESTARTS AGE
pod/nginx-0 1/1 Running 0 116s
pod/nginx-1 1/1 Running 0 98s
pod/nginx-2 1/1 Running 0 91s
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
persistentvolume/pvc-5ac3eba9-fe12-11e8-a19e-00163e14b18f 1Gi RWO Retain Bound default/www-nginx-0 glusterfs 99s
persistentvolume/pvc-65f27519-fe12-11e8-a19e-00163e14b18f 1Gi RWO Retain Bound default/www-nginx-1 glusterfs 93s
persistentvolume/pvc-69b31512-fe12-11e8-a19e-00163e14b18f 1Gi RWO Retain Bound default/www-nginx-2 glusterfs 86s
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/www-nginx-0 Bound pvc-5ac3eba9-fe12-11e8-a19e-00163e14b18f 1Gi RWO glusterfs 116s
persistentvolumeclaim/www-nginx-1 Bound pvc-65f27519-fe12-11e8-a19e-00163e14b18f 1Gi RWO glusterfs 98s
persistentvolumeclaim/www-nginx-2 Bound pvc-69b31512-fe12-11e8-a19e-00163e14b18f 1Gi RWO glusterfs 91s
我们可以看到RECLAIM POLICY: Retain ,经过测试
- 删除pvc,pv status会变成Released状态,且不会被删除
- 删除pv, 通过heketi-cli volume list查看,volume不会被删除
kubernetes pv和gluster volume不一致时,可使用heketi来统一管理volume.此文档heketi和glusterfs都在kubernetes集群外部署。对于支持AWS EBS的磁盘,可通过EBS storageClass方式将glusterFS heketi部署在容器中管理.参考https://github.com/gluster/gluster-kubernetes