前言
本文仅代表作者的个人观点;
本文的内容仅限于技术探讨,不能直接作为指导生产环境的素材;
本文素材是红帽公司产品技术和手册;
本文分为系列文章,一共六篇,本文是第五篇。
第一篇:
第二篇:
干货巨献:Openshift3.9的网络管理大全.加长篇---Openshift3.9学习系列第二篇
第三篇:
身份验证和权限管理---Openshift3.9学习系列第三篇
第四篇:
容器计算资源管理&网络QoS的实现---Openshift3.9学习系列第四篇
第五篇:
厉害了!全CI/CD工具链的实现 | 基于OCP离线: Openshift3.9学习系列第五篇
一、容器原生存储
CNS:container native storage,容器原生存储。OCP的容器原生存储使用glusterfs。
针对Openshift的应用场景,我们对GlusterFS、Ceph RBD以及NFS的优缺点进行分析:
对比项 | Ceph RBD | Glusterfs | SAN+NFS |
---|---|---|---|
Openshift平台容器数据持久化的支持 | 支持Pod级的动态创建,不支持ReadWriteMany 当Kubernetes运行在OpenStack上时,它是最好的存储 | 支持动态分配 支持ReadWriteOnce和ReadWriteMany Container Native Storage | 静态支持,手动和静态预先支持,空间分配低效,容器级别安全性有待提升 普遍使用的,易于设置PoC,易于理解 支持ReadWriteOnce和ReadWriteMany |
高可用 | Ceph系统提供了对象、块、和文件存储功能,使用CRUSH算法维护存储对象与存储服务器的对应关系,无Master设计。对象复制,集群扩容,数据迁移,故障检测和处理等复杂功能由Ceph OSD(Object Storage Device)提供,避免了单点失败 | Glusterfs开源的分布式文件系统,没有元数据服务器层,存储使用弹性哈希算法来查找存储池中的数据(通过文件名来计算哈希值),从而消除了单点故障和导致 I/O 瓶颈的常见根源和故障多发情况 | 依赖于存储硬件和NFS |
数据保护 | Ceph OSD 守护进程自动在其它 Ceph 节点上创建对象副本来确保数据安全和高可用性,存储池快照 | 数据分布与跨节点的多个bricks,支持在线卷快照(Volume Snapshot),可恢复镜像时间点数据,同时支持跨区域(WLAN)的异步主备份卷复制 | 依赖于存储硬件RAID、快照、和复制 |
扩展性能 | 可以动态添加节点和硬盘 | 可以动态增加或缩减数据存储池和节点 | 可以动态增加或缩减数据存储池,依赖于存储硬件 |
caching/分层存储能力 | 支持,比如:ssd盘组成的缓冲层(IO性能要求高的应用)而相对低速、便宜的设备,作为经济存储层(IO性能要求低) | 支持,比如:ssd盘组成的缓冲层(IO性能要求高的应用)而相对低速、便宜的设备,作为经济存储层(IO性能要求低) | 支持,依赖于存储硬件 |
安装和管理 | 安装简单,维护较复杂 | 安装、维护简单 | 安装、维护简单 |
故障恢复 | 但节点失效时,自动迁移数据,重新复制副本 | 当节点、硬件、磁盘、网络故障时,系统能自动处理,无须管理员介入。 | 依赖于存储硬件 |
成本 | 硬件成本低 | 硬件成本低 | 硬件成本高 |
所以,建议用glusterfs与OCP配合使用。
OCP和Gluster的集成方式如下:
首先,gluster节点也是OCP的计算节点,通常是三个。
三个gluster节点上,每个节点运行一个glusterfs-storage pod,它是glusterfs的daemonSet。
除此之外,OCP和Gluster的集成,有个重要的组件heketi。
Heketi是一个具有resetful接口的glusterfs管理程序,作为kubernetes的Storage存储的external provisioner。Heketi提供了一个RESTful管理界面,可用于管理GlusterFS卷的生命周期。借助Heketi,像OpenStack Manila,Kubernetes和OpenShift这样的云服务可以动态地配置GlusterFS卷和任何支持的持久性类型。Heketi将自动确定整个集群的brick位置,确保将brick及其副本放置在不同的故障域中。Heketi还支持任意数量的GlusterFS集群,允许云服务提供网络文件存储,而不受限于单个GlusterFS集群。
OCP和gluster的集成方案中,将heketi用于大多数常见的卷管理操作,例如创建,删除和调整大小。 默认情况下,heketi将创建三副本的卷,即每个文件在三个不同节点上有三个副本的卷。
参照下图:
而在OCP和Gluster的集成方案中,glusterfs-storage pod和Heketi都会运行到一个项目中:
除此之外,我们还需要在default项目中创建一个glusterfs的endpoint和一个service。
glusterfs的endpoint直接表明了glusterfs节点(node3、node4、node5)的IP
当一个新建的pod想使用glusterfs的资源的时候,需要在pv的描述中指明endpoint(后面会有详细介绍)。
使用这种方式的时候(使用默认的storage class),就需要手工先在glusterfs上创建一个volume,使用hetiki的cli创建。
如果我们想对存储动态管理,也就是说,我需要PV的时候,后台glusterfs自动创建对应的volume,那就需要自定义一个storage class,将它指明对hetiki API的调用。并且在pv和pvc的配置中指定storage class。
这样当我们申请pv的时候,glusterfs才通过hetiki在glusterfs上创建volume,一键式完成pv和glusterfs volume的创建。
二、实验环境介绍
现有的一套OCP环境:
存储使用的是NFS方式:
三、 OCP集群动态扩容
接下来,我们配置gluster。我们新增加三个节点,到OCP集群中,用于运行gluster。
修改ansible inventory文件,并将new_nodes子项添加到[OSEv3:children]部分。
vi /etc/ansible/hosts
增加[new_nodes]
[OSEv3:children]
lb
masters
etcd
nodes
nfs
#glusterfs
new_nodes
## These are CNS nodes
[new_nodes]
## These are CNS nodes
[new_nodes]
support1.b434.internal openshift_hostname=support1.b434.internal openshift_node_labels="{'env':'glusterfs', 'cluster': 'b434'}"
support2.b434.internal openshift_hostname=support2.b434.internal openshift_node_labels="{'env':'glusterfs', 'cluster': 'b434'}"
support3.b434.internal openshift_hostname=support3.b434.internal openshift_node_labels="{'env':'glusterfs', 'cluster': 'b434'}"
[glusterfs]
support1.GUID.internal glusterfs_devices='[ "/dev/xvdd" ]'
support2.GUID.internal glusterfs_devices='[ "/dev/xvdd" ]'
support3.GUID.internal glusterfs_devices='[ "/dev/xvdd" ]'
需要注意的是,新增加节点的ovs插件策略,需要与现有集群一致(
redhat/openshift-ovs-networkpolicy):
每个Node上的配置文件:/etc/origin/node/node-config.yaml
执行预安装:
ansible-playbook /usr/share/ansible/openshift-ansible/playbooks/prerequisites.yml
运行脚本进行OCP节点扩容:
ansible-playbook /usr/share/ansible/openshift-ansible/playbooks/openshift-node/scaleup.yml
给三个节点增加label:
oc label node support1.b434.internal logging-infra-fluentd=true
oc label node support2.b434.internal logging-infra-fluentd=true
oc label node support3.b434.internal logging-infra-fluentd=true
扩容成功以后,将ansible inventory上的new_nodes删掉。
四、gluster安装之前的准备工作
修改ansible inventory文件
[OSEv3:vars]
openshift_deployment_type=openshift-enterprise
openshift_master_cluster_method=native
openshift_master_cluster_hostname=loadbalancer1.b434.internal
openshift_master_cluster_public_hostname=loadbalancer.b434.example.opentlc.com
openshift_master_default_subdomain=apps.test.example.com
openshift_master_default_subdomain=apps.b434.example.opentlc.com
containerized=false
openshift_hosted_infra_selector='env=infra'
openshift_hosted_node_selector='env=app'
openshift_master_identity_providers=[{'name': 'ldap', 'challenge': 'true', 'logi n': 'true', 'kind': 'LDAPPasswordIdentityProvider','attributes': {'id': ['dn'], 'email': ['mail'], 'name': ['cn'], 'preferredUsername': ['uid']}, 'bindDN': 'uid =admin,cn=users,cn=accounts,dc=shared,dc=example,dc=opentlc,dc=com', 'bindPasswo rd': 'r3dh4t1!', 'ca': '/etc/origin/master/ipa-ca.crt','insecure': 'false', 'url ': 'ldaps://ipa.shared.example.opentlc.com:636/cn=users,cn=accounts,dc=shared,dc =example,dc=opentlc,dc=com?uid?sub?(memberOf=cn=ocp-users,cn=groups,cn=accounts, dc=shared,dc=example,dc=opentlc,dc=com)'}]
openshift_master_ldap_ca_file=/root/ipa-ca.crt
os_sdn_network_plugin_name='redhat/openshift-ovs-multitenant'
###########################################################################
### OpenShift GlusterFS Vars
###########################################################################
# CNS storage for applications
# openshift_storage_glusterfs_namespace=app-storage
openshift_storage_glusterfs_block_deploy=false
###########################################################################
### Ansible Vars
###########################################################################
timeout=60
ansible_become=yes
ansible_ssh_user=ec2-user
# disable memory check, as we are not a production environment
openshift_disable_check="memory_availability"
# Set this line to enable NFS
openshift_enable_unsupported_configurations=True
###########################################################################
### OpenShift Hosts
###########################################################################
[OSEv3:children]
lb
masters
etcd
nodes
nfs
glusterfs
[lb]
loadbalancer1.b434.internal
[masters]
master1.b434.internal
master2.b434.internal
master3.b434.internal
[etcd]
master1.b434.internal
master2.b434.internal
master3.b434.internal
[nodes]
## These are the masters
master1.b434.internal openshift_hostname=master1.b434.internal openshift_node_l abels="{'env': 'master', 'cluster': 'b434'}"
master2.b434.internal openshift_hostname=master2.b434.internal openshift_node_l abels="{'env': 'master', 'cluster': 'b434'}"
master3.b434.internal openshift_hostname=master3.b434.internal openshift_node_l abels="{'env': 'master', 'cluster': 'b434'}"
## These are infranodes
infranode1.b434.internal openshift_hostname=infranode1.b434.internal openshift_ node_labels="{'env':'infra', 'cluster': 'b434'}"
infranode2.b434.internal openshift_hostname=infranode2.b434.internal openshift_ node_labels="{'env':'infra', 'cluster': 'b434'}"
## These are regular nodes
node1.b434.internal openshift_hostname=node1.b434.internal openshift_node_label s="{'env':'app', 'cluster': 'b434'}"
node2.b434.internal openshift_hostname=node2.b434.internal openshift_node_label s="{'env':'app', 'cluster': 'b434'}"
node3.b434.internal openshift_hostname=node3.b434.internal openshift_node_label s="{'env':'app', 'cluster': 'b434'}"
## These are CNS nodes
support1.b434.internal openshift_hostname=support1.b434.internal openshift_node _labels="{'env':'glusterfs', 'cluster': 'b434'}"
support2.b434.internal openshift_hostname=support2.b434.internal openshift_node _labels="{'env':'glusterfs', 'cluster': 'b434'}"
support3.b434.internal openshift_hostname=support3.b434.internal openshift_node _labels="{'env':'glusterfs', 'cluster': 'b434'}"
[nfs]
support1.b434.internal openshift_hostname=support1.b434.internal
[glusterfs]
support1.b434.internal glusterfs_devices='[ "/dev/xvdd" ]'
support2.b434.internal glusterfs_devices='[ "/dev/xvdd" ]'
support3.b434.internal glusterfs_devices='[ "/dev/xvdd" ]'
执行脚本,安装glusterfs:
ansible-playbook /usr/share/ansible/openshift-ansible/playbooks/openshift-glusterfs/config.yml
安装成功以后,OCP中会创建一个新的项目:glusterfs,里面有几个pod:
而这几个pod,只运行在指定的三个support节点上:
五、设置动态预配置
在本节中,我们将为GlusterFS和旧存储(NFS)设置包含存储类的动态预配置。默认情况下,GlusterFS安装程序会创建一个名为glusterfs-storage的存储类。我们将该存储类设置为默认值。我们可以为旧存储分配不同的存储类,并了解PVC绑定到非默认PV所需的特殊设置。最后,部署应用程序并记录它如何使用默认存储类,动态配置PV和PVC以分配存储。然后查看远程卷上的实际存储。
查看已经安装的存储类:
为应用创建一个新的存储类,叫development-storage,他就是一个动态分配的storage class,我们可以看到指明了调用heketi。
cat << EOF | oc create -f -
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: development-storage
parameters:
resturl: http://heketi-storage-glusterfs.apps.b434.example.opentlc.com
restuser: admin
secretName: heketi-storage-admin-secret
secretNamespace: glusterfs
volumenameprefix: "development"
provisioner: kubernetes.io/glusterfs
reclaimPolicy: Delete
EOF
为旧的NFS基础结构和uservol存储创建名为old-infra的存储类:
cat << EOF > old-infra.yml
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: old-infra
provisioner: no-provisioning
parameters:
EOF
oc create -f old-infra.yml
用old-infra替换所有NFS PV的存储类:
oc get pv -o yaml | sed '/persistentVolumeReclaimPolicy/a \ storageClassName: old-infra' | oc replace -f -
检查:
通过将storageclass.kubernetes.io/is-default-class批注的值更改为true,将开发存储类设置为默认值:
oc patch storageclass development-storage -p '{"metadata": {"annotations": {"storageclass.kubernetes.io/is-default-class": "true"}}}'
请注意,开发存储存储类(动态分配的)现在是默认类。
部署示例应用程序并观察它是否使用默认存储类:
oc new-project smoke-test
oc new-app nodejs-mongo-persistent
应用开始创建后,会创建pv,这时候pv定义的配置,调用heketi自动创建了glusterfs的volume:
被自动创建的pv如下,可以看出来是自动创建的:
oc get pvc
我们设置多个存储类的时候,也方便我们在创建pv的时候,根据需要进行选择:
很显然,动态分配的方式更灵活有效,更有生命力!
六、查看存储数据
登录一个pod,查看vol:
查看一个volume的详细信息:
sh-4.2# gluster vol info development_glusterfs_mongodb_e37f52c1-8652-11e8-9cd2-068204e4c046
Volume Name: development_glusterfs_mongodb_e37f52c1-8652-11e8-9cd2-068204e4c046
Type: Replicate
Volume ID: 29a559da-cb71-448e-ade6-1cf7d5e0f0e9
Status: Started
Snapshot Count: 0
Number of Bricks: 1 x 3 = 3
Transport-type: tcp
Bricks:
Brick1: 192.199.0.8:/var/lib/heketi/mounts/vg_e7b8320763dd4f5f2f4d331191d6c919/brick_9ac71e8944e165923a3e055a140dee42/brick
Brick2: 192.199.0.142:/var/lib/heketi/mounts/vg_7a4df0d142c82309861c38e8b641f487/brick_0d144473601c1902c1c4affbb7ecd234/brick
Brick3: 192.199.0.215:/var/lib/heketi/mounts/vg_f19f54b310e0e99cd24ce287795c5055/brick_680a4e0a80c34b1f79dd1c81600420a3/brick
Options Reconfigured:
transport.address-family: inet
nfs.disable: on
cluster.brick-multiplex: on
在上面的信息中:三个brick来源于三个support节点。
在pod中查看一个brick的内容,我们能够看到里面存放的文件:
七、 对docker-registry做存储迁移(采用静态创建pv方式)
在本小节中,我们将OCP的docker registry从NFS迁移到GlusterFS StorageClass以用于托管应用程序。
首先,创建Gluster服务和端点
在本节中,在default项目中创建Gluster service和endpoint。由于docker-registry集成容器注册表的关键特性,需要使用静态配置的卷来确保服务的配额,以保证访问和性能。
将GlusterFS作为静态持久卷访问的每个项目/命名空间(与上面的动态存储类相反)必须具有在该命名空间中为GlusterFS访问创建的OpenShift服务和端点对象。
oc project default
[root@master1 ~]# cat /root/gluster-service-endpoints.yaml
apiVersion: v1
kind: Service
metadata:
name: gluster-registry-service
spec:
ports:
- port: 1
---
apiVersion: v1
kind: Endpoints
metadata:
name: gluster-registry-endpoints
subsets:
- addresses:
- ip: 192.199.0.142
ports:
- port: 1
- addresses:
- ip: 192.199.0.215
ports:
- port: 1
- addresses:
- ip: 192.199.0.8
ports:
- port: 1
从default项目中创建Gluster服务和端点对象:
oc create -f gluster-service-endpoints.yaml
确认gluster的svc和endpoint已经成功创建:
手动配置GlusterFS卷
接下来,将使用heketi-cli为docker-registry手动设置GlusterFS存储卷。 需要为用于NFS docker-registry同一fsGroup创建此卷。
检查已经运行的docker-registry pod以确定要在GlusterFS卷中使用的正确fsGroup:
查看目前的id:1000000000
接下来,登录到 Heketipod:
设置环境变量,列出已经有的volume:
export HEKETI_CLI_KEY=$HEKETI_ADMIN_KEY
export HEKETI_CLI_USER=admin
heketi-cli volume list
使用上面确定的REGISTRY_GID为静态配置的注册表创建新卷:
heketi-cli volume create --size=20 --gid=1000000000 --name=gluster-registry-volume
使用SSH访问您的一个infra节点并切换到root以测试安装新创建的卷.
在infra1节点上,挂载volume的一个副本以确保组ID与请求的匹配:
export GUID=`hostname|awk -F. '{print $2}'`
echo $GUID
mkdir -p /mnt/glusterfs/gluster-registry-volume
mount -t glusterfs support1.$GUID.internal:/gluster-registry-volume /mnt/glusterfs/gluster-registry-volume/
命令行查看,新创建的gluster volume已经创建成功:
验证已mount目录的内容,并验证是否显示了正确的fsGroup,验证结果一致:
umount volume并清除挂载点:
umount /mnt/glusterfs/gluster-registry-volume
rmdir /mnt/glusterfs/gluster-registry-volume
rmdir /mnt/glusterfs
通过为docker-registry指示正确的accessModes和persistentVolumeReclaimPolicy以及GlusterFS的正确endpoints和path,为卷创建PV。
(需要一个gluster-registry-claim及其命名空间的claimRef,以便PVC不会绑定到不同的PV)
oc project default
cat << EOF | oc create -f -
apiVersion: v1
kind: PersistentVolume
metadata:
name: gluster-registry-volume
spec:
storageClassName: reg-gluster
capacity:
storage: 20Gi
accessModes:
- ReadWriteMany
glusterfs:
endpoints: gluster-registry-endpoints
path: gluster-registry-volume
readOnly: false
claimRef:
name: gluster-registry-claim
namespace: default
persistentVolumeReclaimPolicy: Retain
EOF
oc get pv gluster-registry-volume
创建PVC:
此PVC的名称和命名空间与上面PV中的claimRef中的名称和命名空间相匹配。
我们还需要通过指示正确的volumeName和storageClassName来将PVC与PV匹配。
cat << EOF | oc create -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: gluster-registry-claim
spec:
accessModes:
- ReadWriteMany
volumeName: gluster-registry-volume
storageClassName: ""
resources:
requests:
storage: 20Gi
EOF
八、备份注册表中的现有容器映像
从现有的基于NFS的注册表卷备份现有容器映像。
使用oc rsync将注册表文件系统的内容复制到堡垒上的目录中:
修改卷定义并重新部署。
使用新存储修改注册表部署配置中的卷定义,然后重新部署注册表。
使用新的GlusterFS支持的卷替换旧版(NFS)卷,并允许注册表使用新存储重新部署:
oc volume dc/docker-registry --add --overwrite --name=registry-storage -m /registry -t pvc --claim-name=gluster-registry-claim
使用oc describe dc / docker-registry验证docker registry 存储卷现在是否正在使用gluster-registry-claim pvc。
登录docker-registry所在的infranode,查看gluster volume已经mount到本节点:
使用oc rsync将docker-registry的先前内容还原到新的持久卷中:
以karla身份登录并使用skopeo验证更改:
oc login -u karla -p r3dh4t1!
yum -y install skopeo
oc adm policy add-cluster-role-to-user cluster-admin karla
skopeo --debug inspect --creds karla:$(oc whoami -t) --tls-verify=false docker://docker-registry-default.apps.$GUID.example.opentlc.com/openshift/dotnet
测试能够向docker-Registry成功push镜像。
oc login -u karla -p r3dh4t1!
docker login -u openshift -p $(oc whoami -t) docker-registry.default.svc:5000
docker pull docker.io/busybox
docker tag docker.io/busybox docker-registry.default.svc:5000/openshift/busybox
docker push docker-registry.default.svc:5000/openshift/busybox
oc login -u system:admin
魏新宇