一、Ceph简介

官网:https://ceph.com/en/

  https://docs.ceph.com/en/latest/start/intro/

ceph 是一种开源的分布式的存储系统 包含以下几种存储类型: 块存储(rbd),对象存储(RADOS Fateway),文件系统(cephfs)

块存储(rbd):

块是一个字节序列(例如,512 字节的数据块)。 基于块的存储接口是使用旋转介质(如硬 盘,CD,软盘甚至传统的 9 轨磁带)存储数据的最常用方法;

Ceph 块设备是精简配置,可调 整大小并存储在 Ceph 集群中多个 OSD 条带化的数据。 Ceph 块设备利用 RADOS 功能,如快 照,复制和一致性。 Ceph 的 RADOS 块设备(RBD)使用内核模块或 librbd 库与 OSD 进行交互;Ceph 的块设备为内核模块或 QVM 等 KVM 以及依赖 libvirt 和 QEMU 与 Ceph 块设备集成的 OpenStack 和 CloudStack 等基于云的计算系统提供高性能和无限可扩展性。 可以使用同一 个集群同时运行 Ceph RADOS Gateway,CephFS 文件系统和 Ceph 块设备。

linux 系统中,ls /dev/下有很多块设备文件,这些文件就是我们添加硬盘时识别出来的; rbd 就是由 Ceph 集群提供出来的块设备。可以这样理解,sda 是通过数据线连接到了真实的 硬盘,而 rbd 是通过网络连接到了 Ceph 集群中的一块存储区域,往 rbd 设备文件写入数据, 最终会被存储到 Ceph 集群的这块区域中;

总结:块设备可理解成一块硬盘,用户可以直接使用不含文件系统的块设备,也可以将其格 式化成特定的文件系统,由文件系统来组织管理存储空间,从而为用户提供丰富而友好的数 据操作支持。

文件系统:

Ceph 文件系统(CephFS)是一个符合 POSIX 标准的文件系统,它使用 Ceph 存储集群来存储 其数据。 Ceph 文件系统使用与 Ceph 块设备相同的 Ceph 存储集群系统。

用户可以在块设备上创建 xfs 文件系统,也可以创建 ext4 等其他文件系统,Ceph 集群实现了自己的文件系统来组织管理集群的存储空间,用户可以直接将 Ceph 集群的文件系统挂载到用户机上使用,Ceph 有了块设备接口,在块设备上完全可以构建一个文件系统,那么 Ceph 为什么还需要文件系统接口呢?

主要是因为应用场景的不同,Ceph 的块设备具有优异的读写性能,但不能多处挂载同时读写,目前主要用在 OpenStack 上作为虚拟磁盘,而 Ceph 的文件系统接口读写性能较块设备接口差,但具有优异的共享性。

对象存储:

Ceph 对象存储使用 Ceph 对象网关守护进程(radosgw),它是一个用于与 Ceph 存储集群交 互的 HTTP 服务器。由于它提供与 OpenStack Swift 和 Amazon S3 兼容的接口,因此 Ceph 对 象网关具有自己的用户管理。 Ceph 对象网关可以将数据存储在用于存储来自 Ceph 文件系统客户端或 Ceph 块设备客户端的数据的相同 Ceph 存储集群中:使用方式就是通过 http 协议上传下载删除对象(文件即对象)。

有了块设备接口存储和文件系统接口存储,为什么还整个对象存储呢?

Ceph 的块设备存储具有优异的存储性能但不具有共享性,而 Ceph 的文件系统具有共享性然而性能较块设备存储差,为什么不权衡一下存储性能和共享性,整个具有共享性而存储性能好于文件系统存储的存储呢,对象存储就这样出现了。

分布式存储的优点:

高可靠:既满足存储读取不丢失,还要保证数据长期存储。 在保证部分硬件损坏后依然可 以保证数据安全

高性能:读写速度快

可扩展:分布式存储的优势就是“分布式”,所谓的“分布式”就是能够将多个物理节点整合在一起形成共享的存储池,节点可以线性扩充,这样可以源源不断的通过扩充节点提升性 能和扩大容量,这是传统存储阵列无法做到的

二、Ceph核心组件介绍

在 ceph 集群中,不管你是想要提供对象存储,块设备存储,还是文件系统存储,所有 Ceph 存储集群部署都是从设置每个 Ceph 节点,网络和 Ceph 存储开始 的。 Ceph 存储集群至少需要一个 Ceph Monitor,Ceph Manager 和 Ceph OSD(对象存储守护进程)。 运行 Ceph Filesystem 客户端时也需要 Ceph 元数据服务器。

Monitors:Ceph 监视器(ceph-mon)维护集群状态的映射,包括监视器映射,管理器映射, OSD 映射和 CRUSH 映射。这些映射是 Ceph 守护进程相互协调所需的关键集群状态。监视器 还负责管理守护进程和客户端之间的身份验证。冗余和高可用性通常至少需要三个监视器。

Managers:Ceph Manager 守护程序(ceph-mgr)负责跟踪运行时指标和 Ceph 集群的当前状态,包括存储利用率,当前性能指标和系统负载。Ceph Manager 守护进程还托管基于 python 的模块来管理和公开 Ceph 集群信息,包括基于 Web 的 Ceph Dashboard 和 REST API。高可用性通常至少需要两名 Managers

Ceph OSD:Ceph OSD(对象存储守护进程,ceph-osd)存储数据,处理数据复制,恢复,重新平衡,并通过检查其他 Ceph OSD 守护进程来获取心跳,为 Ceph 监视器和管理器提供一些 监视信息。冗余和高可用性通常至少需要 3 个 Ceph OSD。

MDS:Ceph 元数据服务器(MDS,ceph-mds)代表 Ceph 文件系统存储元数据(即Ceph 块设备和 Ceph 对象存储不使用 MDS)。 Ceph 元数据服务器允许 POSIX 文件系统用户执行基本命令(如 ls,find 等),而不会给 Ceph 存储集群带来巨大负担。

三、安装Ceph集群

3.1 初始化实验环境

ceph准备3台服务器,每台服务器3块硬盘,k8s可以在一起可以独立,最好独立,但是网络相通

主机名 IP 配置 用途
master 192.168.10.10 centos 7.9 4核4G 3块20G硬盘 ceph控制节点、osd、k8s控制节点
node1 192.168.10.11 centos7.9 2核2G 3块20G硬盘 ceph-mon监控节点、osd、k8s工作节点
node2 192.168.10.12 centos7.9 2核2G 3块20G硬盘 ceph-osd对象存储节点、k8s工作节点
# 环境初始化
1.配置主机名并写入到hosts文件中
2.配置ssh信任
3.关闭防火墙、关闭selinux
4.配置Ceph安装源,并将ceph.repo放到每台机器
yum install -y yum-utils && yum-config-manager --add-repo https://dl.fedoraproject.org/pub/epel/7/x86_64/ && sudo yum install --nogpgcheck -y epel-release && sudo rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-7 && rm /etc/yum.repos.d/dl.fedoraproject.org* [root@master yum.repos.d]# cat /etc/yum.repos.d/ceph.repo
[Ceph]
name=Ceph packages for $basearch
baseurl=http://mirrors.aliyun.com/ceph/rpm-jewel/el7/x86_64/
enabled=1
gpgcheck=0
type=rpm-md
gpgkey=https://mirrors.aliyun.com/ceph/keys/release.asc
priority=1 [Ceph-noarch]
name=Ceph noarch packages
baseurl=http://mirrors.aliyun.com/ceph/rpm-jewel/el7/noarch/
enabled=1
gpgcheck=0
type=rpm-md
gpgkey=https://mirrors.aliyun.com/ceph/keys/release.asc
priority=1 [ceph-source]
name=Ceph source packages
baseurl=http://mirrors.aliyun.com/ceph/rpm-jewel/el7/SRPMS/
enabled=1
gpgcheck=0
type=rpm-md
gpgkey=https://mirrors.aliyun.com/ceph/keys/release.asc
priority=1 清空缓存
yum clean all
生成新的缓存
yum makecache fast
升级 yum 源
yum -y update 5.安装iptables
yum install iptables-services -y
service iptables stop && systemctl disable iptables 6.时间同步
ntpdate time1.aliyun.com
7. 安装基础软件包
yum install -y yum-utils device-mapper-persistent-data lvm2 wget net-tools nfs-utils lrzsz gcc gcc-c++ make cmake libxml2-devel openssl-devel curl curl-devel unzip sudo ntp libaio-devel wget vim ncurses-devel autoconf automake zlib-devel python-devel epel-release openssh-server socat ipvsadm conntrack ntpdate telnet deltarpm
3.2 安装Ceph集群
# 1.在master节点安装ceph-deploy
yum install python-setuptools ceph-deploy -y # 在master、node1、node2节点安装ceph
yum install ceph ceph-radosgw -y # 查看ceph版本
[root@master ~]# ceph --version
ceph version 10.2.11 (e4b061b47f07f583c92a050d9e84b1813a35671e) # master节点
cd /etc/ceph
[root@master ceph]# ceph-deploy new master node1 node2
[root@master ceph]# ls
ceph.conf ceph-deploy-ceph.log ceph.mon.keyring rbdmap
Ceph 配置文件、一个 monitor 密钥环和一个日志文件
# 2.安装ceph-monitor
# 修改ceph配置文件:默认副本数从 3 改成 1;把 osd_pool_default_size = 2加入[global]段,这样只有 2 个 osd 也能达到 active+clean 状态:
[root@master ceph]# cat ceph.conf
[global]
fsid = 11e3daf7-ff48-4770-ae92-9bc6d9612144
mon_initial_members = master, node1, node2
mon_host = 192.168.10.10,192.168.10.11,192.168.10.12
auth_cluster_required = cephx
auth_service_required = cephx
auth_client_required = cephx
osd_pool_default_size = 2 mon clock drift allowed = 0.500
mon clock drift warn backoff = 10 mon clock drift allowed #监视器间允许的时钟漂移量默认值 0.05
mon clock drift warn backoff #时钟偏移警告的退避指数。默认值 5 ceph 对每个mon之间的时间同步延时默认要求在 0.05s 之间,这个时间有的时候太短了。所以如果 ceph 集群如果出现 clock 问题就检查 ntp 时间同步或者适当放宽这个误差时间。
cephx 是认证机制是整个 Ceph 系统的用户名/密码
# 3.配置初始 monitor、收集所有的密钥
[root@master ceph]# ceph-deploy mon create-initial
[root@master ceph]# ls *.keyring
ceph.bootstrap-mds.keyring ceph.bootstrap-mgr.keyring ceph.bootstrap-osd.keyring ceph.bootstrap-rgw.keyring ceph.client.admin.keyring ceph.mon.keyring # 4.部署osd
[root@master ceph]# ceph-deploy osd prepare master:/dev/sdb
[root@master ceph]# ceph-deploy osd prepare node1:/dev/sdb
[root@master ceph]# ceph-deploy osd prepare node2:/dev/sdb
# 查看磁盘
[root@master ceph]# lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sda 8:0 0 40G 0 disk
├─sda1 8:1 0 1G 0 part /boot
└─sda2 8:2 0 39G 0 part
├─centos-root 253:0 0 35G 0 lvm /
└─centos-swap 253:1 0 4G 0 lvm
sdb 8:16 0 20G 0 disk
├─sdb1 8:17 0 15G 0 part /var/lib/ceph/osd/ceph-0
└─sdb2 8:18 0 5G 0 part
sdc 8:32 0 20G 0 disk
sr0 11:0 1 973M 0 rom
# 激活osd
[root@master ceph]# ceph-deploy osd activate master:/dev/sdb1
[root@master ceph]# ceph-deploy osd activate node1:/dev/sdb1
[root@master ceph]# ceph-deploy osd activate node2:/dev/sdb1
# 查看osd
[root@master ceph]# ceph osd tree
ID WEIGHT TYPE NAME UP/DOWN REWEIGHT PRIMARY-AFFINITY
-1 0.04376 root default
-2 0.01459 host master
0 0.01459 osd.0 up 1.00000 1.00000
-3 0.01459 host node1
1 0.01459 osd.1 up 1.00000 1.00000
-4 0.01459 host node2
2 0.01459 osd.2 up 1.00000 1.00000
# 查看状态
[root@master ceph]# ceph-deploy osd list master node1 node2
3.3 创建ceph文件系统
# 5.创建mds
[root@master ceph]# ceph-deploy mds create master node1 node2
#查看是否有文件系统
[root@master ceph]# ceph fs ls
No filesystems enabled
# 6.创建存储池
[root@master ceph]# ceph osd pool create cephfs_data 26
pool 'cephfs_data' created
[root@master ceph]# ceph osd pool create cephfs_metadata 26
pool 'cephfs_metadata' created # 创建文件系统
[root@master ceph]# ceph fs new simon cephfs_metadata cephfs_data
new fs with metadata pool 2 and data pool 1
其中:new 后的fsname可自定义

[root@master ceph]# ceph fs ls
name: simon, metadata pool: cephfs_metadata, data pools: [cephfs_data ].
# 查看mds
[root@master ceph]# ceph mds stat
e7: 1/1/1 up {0=node2=up:active}, 2 up:standby
active 是活跃的,另 1 个是处于热备份的状态

一个 cephfs 至少要求两个 librados 存储池,一个为 data,一个为 metadata。当配置这两个存储池时,注意:

  1. 为 metadata pool 设置较高级别的副本级别,因为 metadata 的损坏可能导致整个文件系统不用

  2. 建议,metadata pool 使用低延时存储,比如 SSD,因为 metadata 会直接影响客户端的响应速度。

关于创建存储池:

确定 pg_num 取值是强制性的,因为不能自动计算。下面是几个常用的值:
*少于 5 个 OSD 时可把 pg_num 设置为 128
*OSD 数量在 5 到 10 个时,可把 pg_num 设置为 512
*OSD 数量在 10 到 50 个时,可把 pg_num 设置为 4096
*OSD 数量大于 50 时,你得理解权衡方法、以及如何自己计算 pg_num 取值
*自己计算 pg_num 取值时可借助 pgcalc 工具
随着 OSD 数量的增加,正确的 pg_num 取值变得更加重要,因为它显著地影响着集群的行为、以及出错时的数据持久性(即灾难性事件导致数据丢失的概率)。
# health HEALTH_OK表示集群正常
[root@master ceph]# ceph -s
cluster 11e3daf7-ff48-4770-ae92-9bc6d9612144
health HEALTH_OK
monmap e1: 3 mons at {master=192.168.10.10:6789/0,node1=192.168.10.11:6789/0,node2=192.168.10.12:6789/0}
election epoch 6, quorum 0,1,2 master,node1,node2
fsmap e7: 1/1/1 up {0=node2=up:active}, 2 up:standby
osdmap e20: 3 osds: 3 up, 3 in
flags sortbitwise,require_jewel_osds
pgmap v46: 116 pgs, 3 pools, 2068 bytes data, 20 objects
323 MB used, 45723 MB / 46046 MB avail
116 active+clean
3.4 测试k8s挂载ceph rbd
# ceph网段和k8s网段一定要能ping通
[root@master ceph]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
master Ready control-plane,master 11d v1.20.7
node1 Ready worker 11d v1.20.7
node2 Ready worker 11d v1.20.7 # 1.在 k8s 的各个节点
yum install ceph-common -y # 2.将ceph配置文件拷贝到k8s控制节点和工作节点
scp /etc/ceph/* node1:/etc/ceph/ # 3.创建ceph rbd
# 先创建pool池k8srbd1,占用pgs为56
[root@master ceph]# ceph osd pool create k8srbd1 56
pool 'k8srbd1' created
# 在pool池k8srbd1 创建rbd,名称rbda,rbda大小为1024M
[root@master ceph]# rbd create rbda -s 1024 -p k8srbd1
# 禁用,挂载rbd不会出现问题
[root@master ceph]# rbd feature disable k8srbd1/rbda object-map fast-diff deep-flatten
# 在k8s控制机创建ceph配置文件目录
mkdir /root/ceph
cd /root/ceph
[root@master ceph]# cat pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: testrbd
spec:
containers:
- image: nginx
name: nginx
imagePullPolicy: IfNotPresent
volumeMounts:
- name: testrbd
mountPath: /mnt
volumes:
- name: testrbd
rbd:
monitors:
- '192.168.10.10:6789'
- '192.168.10.11:6789'
- '192.168.10.12:6789'
pool: k8srbd1
image: rbda
fsType: xfs
readOnly: false
user: admin
keyring: /etc/ceph/ceph.client.admin.keyring [root@master ceph]# kubectl apply -f pod.yaml
pod/testrbd created
[root@master ceph]# kubectl get pods
NAME READY STATUS RESTARTS AGE
testrbd 1/1 Running 0 49s [root@master ceph]# kubectl describe pods testrbd
Volumes:
testrbd:
Type: RBD (a Rados Block Device mount on the host that shares a pod's lifetime)
CephMonitors: [192.168.10.10:6789 192.168.10.11:6789 192.168.10.12:6789]
RBDImage: rbda
FSType: xfs
RBDPool: k8srbd1
RadosUser: admin
Keyring: /etc/ceph/ceph.client.admin.keyring
SecretRef: nil
ReadOnly: false
default-token-cj9pm:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-cj9pm
Optional: false
[root@master ceph]# kubectl exec -it testrbd -- /bin/sh
# df -h
Filesystem Size Used Avail Use% Mounted on
overlay 17G 9.6G 7.5G 57% /
tmpfs 64M 0 64M 0% /dev
tmpfs 910M 0 910M 0% /sys/fs/cgroup
/dev/rbd0 1014M 33M 982M 4% /mnt
/dev/mapper/centos-root 17G 9.6G 7.5G 57% /etc/hosts
shm 64M 0 64M 0% /dev/shm
tmpfs 910M 12K 910M 1% /run/secrets/kubernetes.io/serviceaccount
tmpfs 910M 0 910M 0% /proc/acpi
tmpfs 910M 0 910M 0% /proc/scsi
tmpfs 910M 0 910M 0% /sys/firmware # 之前创建的pool池里rbda已经被使用,如果再次挂载,pod会变成pending状态,不创建就卡着
[root@master ceph]# kubectl get pods
NAME READY STATUS RESTARTS AGE
testrbd 1/1 Running 0 8m37s
testrbd1 0/1 Pending       0 118s
# 可以发现rbda已经被使用了
[root@master ceph]# kubectl describe pods testrbd1
Normal SuccessfulAttachVolume 59s attachdetach-controller AttachVolume.Attach succeeded for volume "testrbd"
Warning FailedMount 1s kubelet MountVolume.WaitForAttach failed for volume "testrbd" : rbd image k8srbd1/rbda is still being used # 再次在k8srbd1里创建一个rbda1
[root@master ceph]# rbd create rbda1 -s 1024 -p k8srbd1
[root@master ceph]# rbd feature disable k8srbd1/rbda1 object-map fast-diff deep-flatten
[root@master ceph]# kubectl apply -f pod-1.yaml
pod/testrbd1 created
# pod-1.yaml只需要修改name和image: rbda1
[root@master ceph]# kubectl get pods
NAME READY STATUS RESTARTS AGE
testrbd 1/1 Running 0 17m
testrbd1 1/1 Running 0 58s
3.5 基于ceph rbd生成pv

(1)创建 ceph-secret 这个 k8s secret 对象,这个 secret 对象用于 k8s volume 插件访问 ceph 集群,获取 client.admin 的 keyring 值,并用 base64 编码,在 master(ceph 管理节点)操作

[root@master ceph]# ceph auth get-key client.admin|base64   # ceph控制节点
QVFCYWh3UmpDWjlkSHhBQW9CN2hGbk1LSFhubE1URmNqRzllbEE9PQ==

(2)创建 ceph 的 secret,在 k8s 的控制节点操作,创建yaml文件:

[root@master ceph]# cat ceph-secret.yaml # k8s控制节点
apiVersion: v1
kind: Secret
metadata:
name: ceph-secret
data:
key: QVFCYWh3UmpDWjlkSHhBQW9CN2hGbk1LSFhubE1URmNqRzllbEE9PQ== [root@master ceph]# kubectl apply -f ceph-secret.yaml
secret/ceph-secret created # 3.回到ceph管理节点创建pool池
[root@master ceph]# ceph osd pool create k8stest 56
pool 'k8stest' created
[root@master ceph]# rbd create rbda -s 1024 -p k8stest
[root@master ceph]# rbd feature disable k8stest/rbda object-map fast-diff deep-flatten
# 4. 创建pv
[root@master ceph]# cat pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: ceph-pv
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
rbd:
monitors:
- '192.168.10.10:6789'
- '192.168.10.11:6789'
- '192.168.10.12:6789'
pool: k8stest
image: rbda
user: admin
secretRef:
name: ceph-secret
fsType: xfs
readOnly: false
persistentVolumeReclaimPolicy: Recycle [root@master ceph]# kubectl apply -f pv.yaml
persistentvolume/ceph-pv created
[root@master ceph]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
ceph-pv 1Gi RWO Recycle Available 4s # 5.创建pvc
[root@master ceph]# cat pvc.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: ceph-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi persistentvolumeclaim/ceph-pvc created
[root@master ceph]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
ceph-pvc Bound ceph-pv 1Gi RWO 3s # 6.测试挂载pvc:把pvc做成卷
[root@master ceph]# cat pod-2.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 2 # tells deployment to run 2 pods matching the template
template: # create pods using pod definition in this template
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
volumeMounts:
- mountPath: "/ceph-data"
name: ceph-data
volumes:
- name: ceph-data
persistentVolumeClaim:
claimName: ceph-pvc [root@master ceph]# kubectl apply -f pod-2.yaml
deployment.apps/nginx-deployment created
[root@master ceph]# kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-deployment-fdff5b9c8-9r4mj 1/1 Running 0 5m38s
nginx-deployment-fdff5b9c8-htf6s 0/1 ContainerCreating 0 5m38s
# 出现下面这个情况的原因是:2个nginx分别部署在不同的node工作节点上
[root@master ceph]# kubectl describe pods nginx-deployment-fdff5b9c8-htf6s
Warning FailedAttachVolume 83s attachdetach-controller Multi-Attach error for volume "ceph-pv" Volume is already used by pod(s) nginx-deployment-fdff5b9c8-9r4mj

# 删除重建可以成功 kubectl delete pods nginx-deployment-fdff5b9c8-htf6s

[root@master ceph]# kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-deployment-fdff5b9c8-9r4mj 1/1 Running 0 16m
nginx-deployment-fdff5b9c8-twtms 1/1 Running 0 17s

通过上面实验可以发现 pod 是可以以 ReadWriteOnce 共享挂载相同的 pvc 的

注意:ceph rbd 块存储的特点

ceph rbd 块存储能在同一个 node 上跨 pod 以 ReadWriteOnce 共享挂载

ceph rbd 块存储能在同一个 node 上同一个 pod 多个容器中以 ReadWriteOnce 共享挂载

ceph rbd 块存储不能跨 node 以 ReadWriteOnce 共享挂载

如果一个使用ceph rdb 的pod所在的node挂掉,这个pod虽然会被调度到其它node, 但是由于 rbd 不能跨 node 多次挂载和挂掉的 pod 不能自动解绑 pv 的问题,这个新 pod 不会正常运行

Deployment 更新特性:

deployment 触发更新的时候,它确保至少所需 Pods 75% 处于运行状态(最大不可用 比例为 25%)。故像一个 pod 的情况,肯定是新创建一个新的 pod,新 pod 运行正常之 后,再关闭老的 pod。

默认情况下,它可确保启动的 Pod 个数比期望个数最多多出 25%

问题:

结合 ceph rbd 共享挂载的特性和 deployment 更新的特性,我们发现原因如下:

由于 deployment 触发更新,为了保证服务的可用性,deployment 要先创建一个 pod 并运行正常之后,再去删除老 pod。而如果新创建的 pod 和老 pod 不在一个 node,就 会导致此故障。

解决办法:

  1,使用能支持跨 node 和 pod 之间挂载的共享存储,例如 cephfs,GlusterFS 等

  2,给 node 添加 label,只允许 deployment 所管理的 pod 调度到一个固定的 node 上。 (不建议,这个 node 挂掉的话,服务就故障了)

3.6 基于Storageclass 动态生成pv

kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: rbd-provisioner
rules:
- apiGroups: [""]
resources: ["persistentvolumes"]
verbs: ["get", "list", "watch", "create", "delete"]
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["get", "list", "watch", "update"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["events"]
verbs: ["create", "update", "patch"]
- apiGroups: [""]
resources: ["services"]
resourceNames: ["kube-dns","coredns"]
verbs: ["list", "get"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: rbd-provisioner
subjects:
- kind: ServiceAccount
name: rbd-provisioner
namespace: default
roleRef:
kind: ClusterRole
name: rbd-provisioner
apiGroup: rbac.authorization.k8s.io
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: rbd-provisioner
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get"]
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: rbd-provisioner
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: rbd-provisioner
subjects:
- kind: ServiceAccount
name: rbd-provisioner
namespace: default
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: rbd-provisioner
spec:
selector:
matchLabels:
app: rbd-provisioner
replicas: 1
strategy:
type: Recreate
template:
metadata:
labels:
app: rbd-provisioner
spec:
containers:
- name: rbd-provisioner
image: quay.io/xianchao/external_storage/rbd-provisioner:v1
imagePullPolicy: IfNotPresent
env:
- name: PROVISIONER_NAME
value: ceph.com/rbd
serviceAccount: rbd-provisioner
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: rbd-provisioner

rbd-provisioner.yaml

# 1.创建rbd的供应商,#把 rbd-provisioner.tar.gz 上传到node上,导入镜像
docker load -i rbd-provisioner.tar.gz
Loaded image: quay.io/xianchao/external_storage/rbd-provisioner:v1 [root@master ceph]# kubectl apply -f rbd-provisioner.yaml
clusterrole.rbac.authorization.k8s.io/rbd-provisioner created
clusterrolebinding.rbac.authorization.k8s.io/rbd-provisioner created
role.rbac.authorization.k8s.io/rbd-provisioner created
rolebinding.rbac.authorization.k8s.io/rbd-provisioner created
deployment.apps/rbd-provisioner created
serviceaccount/rbd-provisioner created [root@master ceph]# kubectl get pods
NAME READY STATUS RESTARTS AGE
rbd-provisioner-685746688f-nnsrh 1/1 Running 0 112s # 2.创建ceph-secret-1.yaml和之前一样获取设置key值
cd /etc/ceph
ceph auth get-key client.admin| base64
[root@master ceph]# cat ceph-secret-1.yaml
apiVersion: v1
kind: Secret
metadata:
name: ceph-secret-1
type: "ceph.com/rbd" # 和rbd-provisioner一致
data:
key: QVFCYWh3UmpDWjlkSHhBQW9CN2hGbk1LSFhubE1URmNqRzllbEE9PQ== [root@master ceph]# kubectl apply -f ceph-secret-1.yaml
secret/ceph-secret-1 created
[root@master ceph]# kubectl get Secret
NAME TYPE DATA AGE
ceph-secret Opaque 1 61m
ceph-secret-1 ceph.com/rbd 1 7s # 创建pool池
[root@master ceph]# ceph osd pool create k8stest1 56
pool 'k8stest1' created
# 3.创建storageclass
[root@master ceph]# cat storageclass.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: k8s-rbd
provisioner: ceph.com/rbd
parameters:
monitors: 192.168.10.10:6789,192.168.10.11:6789,192.168.10.12:6789
adminId: admin
adminSecretName: ceph-secret-1
pool: k8stest1
userId: admin
userSecretName: ceph-secret-1
fsType: xfs
imageFormat: "2"
imageFeatures: "layering" [root@master ceph]# kubectl apply -f storageclass.yaml
storageclass.storage.k8s.io/k8s-rbd created
[root@master ceph]# kubectl get StorageClass
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE
k8s-rbd ceph.com/rbd Delete Immediate false 20s # 查看rbd-provisioner-685746688f-nnsrh日志发现有一个报错,报错原因是 1.20 版本仅用了 selfLink
[root@master ceph]# kubectl logs rbd-provisioner-685746688f-nnsrh
E0823 10:00:41.362761 1 event.go:259] Could not construct reference to: '&v1.Endpoints{TypeMeta:v1.TypeMeta{Kind:"", APIVersion:""},
ObjectMeta:v1.ObjectMeta{Name:"ceph.com-rbd", GenerateName:"", Namespace:"default", SelfLink:"", UID:"58a63d0d-6870-4be5-a8ba-83fc0ee0343b", ResourceVersion:"87013",
Generation:0, CreationTimestamp:v1.Time{Time:time.Time{wall:0x0, ext:63796845641, loc:(*time.Location)(0x1bc94e0)}}, DeletionTimestamp:(*v1.Time)(nil),
DeletionGracePeriodSeconds:(*int64)(nil), Labels:map[string]string(nil), Annotations:map[string]string{"control-plane.alpha.kubernetes.io/leader":
"{\"holderIdentity\":\"rbd-provisioner-685746688f-nnsrh_723aeee5-22ca-11ed-8eb6-ce86e577e758\",\"leaseDurationSeconds\":15,\"acquireTime\":\"2022-08-23T10:00:41Z\",
\"renewTime\":\"2022-08-23T10:00:41Z\",\"leaderTransitions\":0}"}, OwnerReferences:[]v1.OwnerReference(nil), Initializers:(*v1.Initializers)(nil), Finalizers:[]string(nil),
ClusterName:""}, Subsets:[]v1.EndpointSubset(nil)}' due to: 'selfLink was empty, can't make reference'. Will not report event: 'Normal' 'LeaderElection'
'rbd-provisioner-685746688f-nnsrh_723aeee5-22ca-11ed-8eb6-ce86e577e758 became leader' # 生产环境一定要是高可用,否则可能会影响业务
# 解决方法:
# api配置文件中新增一条
vim /etc/kubernetes/manifests/kube-apiserver.yaml
- --feature-gates=RemoveSelfLink=false [root@master ceph]# kubectl apply -f /etc/kubernetes/manifests/kube-apiserver.yaml
pod/kube-apiserver created
# 然后删除临时生成的kube-apiserver状态为CrashLoopBackOff的pod节点
# 如果是高可用的集群,改完生效后删除临时pod,再去改第二个控制节点
[root@master ceph]# kubectl delete pods kube-apiserver -n kube-system
pod "kube-apiserver" deleted # 4.创建pvc
[root@master ceph]# cat rbd-pvc.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: rbd-pvc
spec:
accessModes:
- ReadWriteOnce
volumeMode: Filesystem
resources:
requests:
storage: 1Gi
storageClassName: k8s-rbd [root@master ceph]# kubectl apply -f rbd-pvc.yaml
persistentvolumeclaim/rbd-pvc created # 已经绑定到自动创建的STORAGECLASS里了
[root@master ceph]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
rbd-pvc Bound pvc-c854c881-d01f-4da3-804e-41e80aa80515 1Gi RWO k8s-rbd 4s
# 查看rbd-pvc日志
[root@master ceph]# kubectl describe pvc rbd-pvc
# 5.创建pod,挂载pvc
[root@master ceph]# cat pod-sto.yaml
apiVersion: v1
kind: Pod
metadata:
labels:
test: rbd-pod
name: ceph-rbd-pod
spec:
containers:
- name: ceph-rbd-nginx
image: nginx
imagePullPolicy: IfNotPresent
volumeMounts:
- name: ceph-rbd
mountPath: /mnt
readOnly: false
volumes:
- name: ceph-rbd
persistentVolumeClaim:
claimName: rbd-pvc [root@master ceph]# kubectl apply -f pod-sto.yaml
pod/ceph-rbd-pod created
[root@master ceph]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
ceph-rbd-pod 1/1 Running 0 30s 10.244.104.3 node2 <none> <none>
3.7 k8s挂载cephfs
[root@master ceph]# ceph fs ls
name: simon, metadata pool: cephfs_metadata, data pools: [cephfs_data ] # 1.创建ceph子目录
为了别的地方能挂载 cephfs,先创建一个 secretfile
[root@master ceph]# cat /etc/ceph/ceph.client.admin.keyring |grep key|awk -F" " '{print $3}' > /etc/ceph/admin.secret
挂载 cephfs 的根目录到集群的 mon 节点下的一个目录,比如data,因为挂载后,我们就可以直接在data 下面用 Linux 命令创建子目录了。
[root@master ~]# mkdir /root/data
[root@master ~]# mount -t ceph 192.168.10.12:6789:/ /root/data -o name=admin,secretfile=/etc/ceph/admin.secret
[root@master ~]# df -h
文件系统 容量 已用 可用 已用% 挂载点
192.168.10.12:6789:/ 45G 408M 45G 1% /root/data 在 cephfs 的根目录里面创建了一个子目录 lucky,k8s 以后就可以挂载这个目录
[root@master data]# mkdir /root/data/lucky
[root@master data]# chmod 0777 lucky #2.测试k8s的pod挂载cephfs
# 创建k8s链接ceph使用secret
将/etc/ceph/ceph.client.admin.keyring 里面的 key 的值转换为
base64
[root@master ceph]# cat cephfs-secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: cephfs-secret
data:
key: QVFCYWh3UmpDWjlkSHhBQW9CN2hGbk1LSFhubE1URmNqRzllbEE9PQ== [root@master ceph]# kubectl apply -f cephfs-secret.yaml
secret/cephfs-secret created
[root@master ceph]# kubectl get Secret
NAME TYPE DATA AGE
cephfs-secret Opaque 1 10s # 3.创建pv
[root@master ceph]# cat cephfs-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: cephfs-pv
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteMany
cephfs:
monitors:
- 192.168.10.12:6789
path: /lucky
user: admin
readOnly: false
secretRef:
name: cephfs-secret
persistentVolumeReclaimPolicy: Recycle # 4.创建pvc
[root@master ceph]# cat cephfs-pvc.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: cephfs-pvc
spec:
accessModes:
- ReadWriteMany
volumeName: cephfs-pv
resources:
requests:
storage: 1Gi [root@master ceph]# kubectl apply -f cephfs-pv.yaml
persistentvolume/cephfs-pv created
[root@master ceph]# kubectl apply -f cephfs-pvc.yaml
persistentvolumeclaim/cephfs-pvc created
[root@master ceph]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
cephfs-pv 1Gi RWX Recycle Bound default/cephfs-pvc 10s
[root@master ceph]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
cephfs-pvc Bound cephfs-pv 1Gi RWX 6s # 创建pod,挂载cephfs-pvc
[root@master ceph]# cat cephfs-pod-1.yaml
apiVersion: v1
kind: Pod
metadata:
name: cephfs-pod-1
spec:
containers:
- image: nginx
name: nginx
imagePullPolicy: IfNotPresent
volumeMounts:
- name: test-v1
mountPath: /mnt
volumes:
- name: test-v1
persistentVolumeClaim:
claimName: cephfs-pvc [root@master ceph]# cat cephfs-pod-2.yaml
apiVersion: v1
kind: Pod
metadata:
name: cephfs-pod-2
spec:
containers:
- image: nginx
name: nginx
imagePullPolicy: IfNotPresent
volumeMounts:
- name: test-v1
mountPath: /mnt
volumes:
- name: test-v1
persistentVolumeClaim:
claimName: cephfs-pvc [root@master ceph]# kubectl apply -f cephfs-pod-1.yaml
pod/cephfs-pod-1 created
[root@master ceph]# kubectl apply -f cephfs-pod-2.yaml
pod/cephfs-pod-2 created
[root@master ceph]# kubectl get pods
NAME READY STATUS RESTARTS AGE
cephfs-pod-1 1/1 Running 0 76s
cephfs-pod-2 1/1 Running 0 32s # 测试
[root@master ceph]# kubectl exec -it cephfs-pod-1 -- /bin/sh
# cd /mnt
# ls
# touch hello
# echo aa > 1.txt
# touch welcome
# exit
[root@master lucky]# kubectl exec -it cephfs-pod-2 -- /bin/sh
# cd /mnt
#echo 222> 2.txt
# exit
# 查看cephfs文件目录下内容
[root@master lucky]# pwd
/root/data/lucky
[root@master lucky]# ls
1.txt 2.txt hello welcome
[root@master lucky]# cat 1.txt
aa
[root@master lucky]# cat 2.txt
222
[root@master lucky]# echo aaaaaa > a.txt
[root@master lucky]# kubectl exec -it cephfs-pod-2 -- /bin/sh
# cd /mnt
# cat a.txt
aaaaaa

k8s对接Ceph实现持久化存储(16)的更多相关文章

  1. k8s对接ceph存储

    前提条件:已经部署好ceph集群 本次实验由于环境有限,ceph集群是部署在k8s的master节点上的 一.创建ceph存储池 在ceph集群的mon节点上执行以下命令: ceph osd pool ...

  2. 通过Heketi管理GlusterFS为K8S集群提供持久化存储

    参考文档: Github project:https://github.com/heketi/heketi MANAGING VOLUMES USING HEKETI:https://access.r ...

  3. k8s使用ceph作为后端存储挂载

    一.在ceph集群上操作: 1.创建池(主要使用存储类来进行持久卷的挂载,其他的挂载方式不好使也太麻烦):ceph osd pool create k8s 64 二.在k8s上操作: 1.安装客户端( ...

  4. K8S学习笔记之k8s使用ceph实现动态持久化存储

    0x00 概述 本文章介绍如何使用ceph为k8s提供动态申请pv的功能.ceph提供底层存储功能,cephfs方式支持k8s的pv的3种访问模式ReadWriteOnce,ReadOnlyMany ...

  5. 使用Ceph集群作为Kubernetes的动态分配持久化存储(转)

    使用Docker快速部署Ceph集群 , 然后使用这个Ceph集群作为Kubernetes的动态分配持久化存储. Kubernetes集群要使用Ceph集群需要在每个Kubernetes节点上安装ce ...

  6. 4.深入k8s:容器持久化存储

    从一个例子入手PV.PVC Kubernetes 项目引入了一组叫作 Persistent Volume Claim(PVC)和 Persistent Volume(PV)的 API 对象用于管理存储 ...

  7. k8s使用ceph的rbd作后端存储

    k8s使用rbd作后端存储 k8s里的存储方式主要有三种.分别是volume.persistent volumes和dynamic volume provisioning. volume: 就是直接挂 ...

  8. k8s的持久化存储PV&&PVC

    1.PV和PVC的引入 Volume 提供了非常好的数据持久化方案,不过在可管理性上还有不足. 拿前面 AWS EBS 的例子来说,要使用 Volume,Pod 必须事先知道如下信息: 当前 Volu ...

  9. k8s集群,使用pvc方式实现数据持久化存储

    环境: 系统 华为openEulerOS(CentOS7) k8s版本 1.17.3 master 192.168.1.244 node1 192.168.1.245 介绍: 在Kubernetes中 ...

  10. 如何接入 K8s 持久化存储?K8s CSI 实现机制浅析

    作者 王成,腾讯云研发工程师,Kubernetes contributor,从事数据库产品容器化.资源管控等工作,关注 Kubernetes.Go.云原生领域. 概述 进入 K8s 的世界,会发现有很 ...

随机推荐

  1. 记录--实时音视频功能简析(live-pusher与live-player)

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 使用须知 2017年下半年,微信6.5.21版本支持在线音视频功能.开发者可以通过两个音视频组件 和 实现实时地在线直播.视频通话.语音通 ...

  2. Mysql中的锁(case篇)

    case1(表锁的读-写-读阻塞) 上篇文档中提到过 WRITE locks normally have higher priority than READ locks to ensure that ...

  3. ChatGPT 指令大全

    1.写报告 报告开头 我现在正在 报告的情境与目的 .我的简报主题是 主题 ,请提供 数字 种开头方式,要简单到 目标族群 能听懂,同时要足够能吸引人,让他们愿意专心听下去. 我现在正在修台大的简报课 ...

  4. 使用Dockerfile部署springboot打包jar包

    1.docker下载JDK1.8镜像 docker pull java:8 2.进行需要编译的镜像文件目录编写Dockerfile文件 #依赖的父镜像FROM java:8#作者MAINTAINER ...

  5. WPF实现树形表格控件(TreeListView)

    前言 本文将探讨如何利用WPF框架实现树形表格控件,该控件不仅能够有效地展示复杂的层级数据,还能够提供丰富的个性化定制选项.我们将介绍如何使用WPF提供的控件.模板.布局.数据绑定等技术来构建这样一个 ...

  6. #zkw线段树#洛谷 3792 由乃与大母神原型和偶像崇拜

    题目 给你一个长为 \(n\) 的序列 \(a\) 每次两个操作: 修改 \(x\) 位置的值为 \(y\) 查询区间 \([l,r]\) 是否可以重排为值域上连续的一段 分析 直接维护区间最大值和最 ...

  7. C++ 模板和泛型编程详解

    C++中的模板和泛型编程是非常重要的概念.模板是一种将数据类型作为参数的通用程序设计方法.它们允许开发人员编写可以处理各种数据类型的代码,而无需为每种数据类型编写不同的代码.下面介绍了一些关于C++中 ...

  8. Python 中的字符串基础与应用

    在Python中,字符串可以用单引号或双引号括起来.'hello' 与 "hello" 是相同的.您可以使用print()函数显示字符串文字: 示例: print("He ...

  9. 报名直达丨HarmonyOS开发者创新大赛线下城市交流会来了,约吗?

    HarmonyOS开发者创新大赛线下城市交流会 正式开启啦! 在这里,将有专业的讲师 与HarmonyOS开发者们面对面演示开发实操.交流开发心得 还等什么?赶快扫码报名吧~↓↓↓ 城市交流会报名链接 ...

  10. android 当在github下载一个android项目后

    前言 github是一个很好的社区,有时候呢,我们从android下载项目下来无法build,有很多原因,比如说库不见了,或者说自己没有安装过支持的api版本等,会遇到各种问题. 在此总结一下. 问题 ...