此为南京大学 2024 年春季学期云计算课程的第二次实践内容。

实践原始要求:采用成熟、简单的 ceph-deploy 实现 Ceph 集群(nautilus版本)的部署。

在实践课开始之前,我就想动手开始做,奈何没有文档,所以提前琢磨了一下。

本文的内容主要参考了 Ceph 官方文档只讲了一半的知乎专栏TinyChen 的博客与 GPT4。

关于 Ceph 集群的介绍,本文不再赘述,有兴趣的读者可以参考 Ceph 官方文档

本文记录了 Sakiyary 在 Docker 的容器内(赛博洁癖,没事找事)使用 ceph-deploy 部署 Ceph 集群的过程。实现了 Ceph 的节点(MON x1、MGR x1、OSD x3)与 Dashboard 的部署。

环境说明

  • 计算机属性:软院云塑料云上的某一台服务器
  • 操作系统:Ubuntu 18.04
  • 假物理内存大小:16 GB
  • 假硬盘大小:145 GB
  • Docker 版本:20.10.21
  • 用户:root

所以本文所有命令中均没有 sudo 前缀。

可自行替换本文中的 /root/ 路径为 /home/<用户名>/,并添加 sudo 前缀。

经完整部署后预估 Ceph 集群(Docker部署 3 个节点)所需占用的最小硬盘空间为 10 GB 左右(不含 Docker 本体,仅估计 OSD 大小与镜像大小)。

准备工作

Docker 的安装、科学网络的配置等工作不在本文的讨论范围内,这里只讨论 Ceph 部署的过程。

若干参考文档中均指出了 Ceph 不同节点间需要同步时钟,但由于本人这次是在 Docker 容器内部署,每个容器均调用了宿主机的时间,所以这里不需要再进行时钟同步。

创建虚拟逻辑卷

由于服务器仅有一块物理硬盘,但是部署 1 个 Ceph 集群就至少应该有 3 个 OSD 节点,也就是 3 个硬盘设备(逻辑卷),所以这里需要先创建三个逻辑卷。(仅创建物理卷是不够的,Ceph 只认逻辑卷,血的教训,被网爆了很久……)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 在宿主机的 root 用户下
# 创建三个 Image
mkdir -p /root/ceph
touch /root/ceph/osd1.img
touch /root/ceph/osd2.img
touch /root/ceph/osd3.img

# 灌零进去 灌多少自己看着办
# 我每个都灌了 10 GB (1G * 10) 部署完后认为每个 OSD 只要 3 GB (1G * 3) 就够了,共 9 GB
dd if=/dev/zero of=/root/ceph/osd1.img bs=1G count=3
dd if=/dev/zero of=/root/ceph/osd2.img bs=1G count=3
dd if=/dev/zero of=/root/ceph/osd3.img bs=1G count=3

# 创建物理卷
# 防止占用已有的 /dev/loop* 设备,这里从 11 开始
losetup /dev/loop11 /root/ceph/osd1.img
losetup /dev/loop12 /root/ceph/osd2.img
losetup /dev/loop13 /root/ceph/osd3.img
pvcreate /dev/loop11
pvcreate /dev/loop12
pvcreate /dev/loop13

# 创建卷组
vgcreate ceph-vg-1 /dev/loop11
vgcreate ceph-vg-2 /dev/loop12
vgcreate ceph-vg-3 /dev/loop13

# 创建逻辑卷
lvcreate -l 100%FREE -n osd-lv-1 ceph-vg-1
lvcreate -l 100%FREE -n osd-lv-2 ceph-vg-2
lvcreate -l 100%FREE -n osd-lv-3 ceph-vg-3

配置宿主机自交的 SSH 免密登录

Ceph 集群中的节点之间需要 SSH 免密登录,这里提前配置好可以省去非常多的重复操作(血的教训)。

思路是让三个容器都使用同一个 .ssh 目录,这样就不用在每个容器中都配置一遍 SSH 了,实现全联通。

1
2
3
4
5
6
7
8
9
10
11
# 在宿主机的 root 用户下
# 创建 .ssh 目录
mkdir -p /root/.ssh

# 生成密钥对
ssh-keygen -t rsa -f /root/.ssh/id_rsa -C "root@se-cloud"
cat /root/.ssh/id_rsa.pub >> /root/.ssh/authorized_keys

# 更改权限
chmod 700 /root/.ssh
chmod 600 /root/.ssh/authorized_keys

创建 Docker 网络

这里创建一个 Docker 网络,方便容器之间的通信。

1
2
3
# 在宿主机的 root 用户下
# 创建 Docker 网络
docker network create --subnet 192.168.102.0/24 ceph-network

如果不指定 --subnet,Docker 会自动分配一个子网,但这个默认的子网很大概率会与南大鼓楼二舍有线网网段冲突(172.17.0.0/16),导致服务器失联,故建议手动指定一个不冲突的子网。

部署 Ceph 集群

经过 Sakiyary 的反复试错,反复调整,反复查资料,将几乎所有的重复步骤都整合进了 Dockerfile 中,这里只需要构建一个专属镜像就可以避免很多重复操作。(都是血的教训……)

注:这里并未使用 Ceph 官方的镜像,而是使用 CentOS 7 作为基础镜像,再在其基础上使用 ceph-deploy 安装 Ceph,这样可以更好地控制 Ceph 的版本,也可以更好地理解 Ceph 的安装过程,最为重要的是满足实践原始要求。

构建带有 Systemd + Ceph 的 CentOS 镜像

1
2
3
4
5
# 在宿主机的 root 用户下
# 拉取 CentOS 7 镜像
docker pull centos:7
# 创建 Dockerfile
vim /root/ceph/Dockerfile

这里给出我的 Dockerfile,部分内容需要自行填写,可以根据自己的需求进行修改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# 相关教程都用的 CentOS,所以我不敢用其他的(老实人)
FROM centos:7
ENV container docker

# 安装 systemd,去除不需要的文件,并使能 systemd,这是至关重要的一步!
RUN (cd /lib/systemd/system/sysinit.target.wants/; for i in *; do [ $i == systemd-tmpfiles-setup.service ] || rm -f $i; done); \
rm -f /lib/systemd/system/multi-user.target.wants/*;\
rm -f /etc/systemd/system/*.wants/*;\
rm -f /lib/systemd/system/local-fs.target.wants/*; \
rm -f /lib/systemd/system/sockets.target.wants/*udev*; \
rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \
rm -f /lib/systemd.system/basic.target.wants/*;\
rm -f /lib/systemd.system/anaconda.target.wants/*;

# 设置系统代理,我这边默认科学,不然容器内连不了网,登 p.nju 太麻烦了
ENV HTTP_PROXY="http://你自己的地址:端口" \
HTTPS_PROXY="http://你自己的地址:端口" \
http_proxy="http://你自己的地址:端口" \
https_proxy="http://你自己的地址:端口"

# 添加 hosts 文件项,使得容器之间可以通过主机名互相访问,网段根据刚刚创建的 Docker 网络来设置
RUN echo "192.168.102.2 centos-1" >> /etc/hosts && \
echo "192.168.102.3 centos-2" >> /etc/hosts && \
echo "192.168.102.4 centos-3" >> /etc/hosts

# 添加 Ceph 官方源,若需要镜像源(包括常规软件源),可自行更改
RUN echo -e "[Ceph]\nname=Ceph Nautilus\nbaseurl=http://download.ceph.com/rpm-nautilus/el7/\$basearch\nenabled=1\ngpgcheck=0\npriority=1" > /etc/yum.repos.d/ceph-nautilus.repo && \
echo -e "[Ceph-noarch]\nname=Ceph Noarch\nbaseurl=http://download.ceph.com/rpm-nautilus/el7/noarch\nenabled=1\ngpgcheck=0\npriority=1" >> /etc/yum.repos.d/ceph-nautilus.repo && \
echo -e "[Ceph-source]\nname=Ceph Source\nbaseurl=http://download.ceph.com/rpm-nautilus/el7/SRPMS\nenabled=0\ngpgcheck=0\npriority=1" >> /etc/yum.repos.d/ceph-nautilus.repo

# 安装软件,有些软件是我为了调试装进去的
RUN yum update -y && \
yum install -y vim wget openssh-clients openssh-server net-tools chrony conntrack ipset jq iptables curl sysstat libseccomp socat git python-setuptools epel-release ceph-deploy && \
yum install -y ceph-mon ceph-radosgw ceph-mds ceph-mgr ceph-osd ceph-common

# 创建 ceph-admin 目录
RUN mkdir -p /root/ceph-admin

# 创建 SSH 文件夹(其实不需要),并初始化 SSH 密钥(这个是需要的)
RUN mkdir -p /root/.ssh && \
chmod 700 /root/.ssh && \
ssh-keygen -A

# 启用 sshd 服务,这个 sshd 甚至还是现装的
RUN systemctl enable sshd

# 设置 systemd 为初始进程
CMD ["/usr/sbin/init"]

编写完 Dockerfile 后,构建镜像。

1
2
3
# 在宿主机的 root 用户下
# 构建镜像,/root/ceph 为 Dockerfile 所在目录
docker build -t centos-ceph:7 /root/ceph
若出现 `/etc/hosts`: Read-only file system 报错

此为 Docker 的安全机制导致的。在 Docker 中,某些文件和目录(如 /etc/hosts/etc/resolv.conf/etc/hostname)是自动管理的,通常不允许在构建过程中直接修改。

请将 Dockerfile 中 22~24 行与 /etc/hosts 相关的 RUN 命令删除,改为在容器启动时执行。

请用新增参数后的 `docker run` 指令替换下一节的指令
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
docker run \
-d \
--privileged \
--name centos-1 \
--hostname centos-1 \
--network ceph-network \
-p 8011:8011 \
--device /dev/ceph-vg-1/osd-lv-1:/dev/ceph-vg-1/osd-lv-1 \
-v /sys/fs/cgroup:/sys/fs/cgroup:ro \
-v /dev:/dev \
-v /root/.ssh:/root/.ssh \
--add-host centos-1:192.168.102.2 \
--add-host centos-2:192.168.102.3 \
--add-host centos-3:192.168.102.4 \
centos-ceph:7

等待漫长的构建过程结束后,我们就可以使用这个镜像来创建容器了。如果你的网络不好的话,很可能会在构建时卡死在更新软件源/安装软件那几步。

构建完的镜像可以通过 docker images 查看,大小约为 1.01 GB。

创建 Ceph 容器

这里使用 docker-compose 来运行容器会更加好。我这里就偷懒了,复制了三条 docker run 命令完事,不过记得要按顺序输入,也不建议更改容器的主机名称 hostname ,Ceph 要求主机名与域名相同,域名已在 Dockerfile 中设置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 在宿主机的 root 用户下
docker run \
-d \
--privileged \
--name centos-1 \
--hostname centos-1 \
--network ceph-network \
-p 8011:8011 \
--device /dev/ceph-vg-1/osd-lv-1:/dev/ceph-vg-1/osd-lv-1 \
-v /sys/fs/cgroup:/sys/fs/cgroup:ro \
-v /dev:/dev \
-v /root/.ssh:/root/.ssh \
centos-ceph:7

# 下同,复制粘贴改变量
docker run -d --privileged --name centos-2 --hostname centos-2 --network ceph-network -p 8012:8012 --device /dev/ceph-vg-2/osd-lv-2:/dev/ceph-vg-2/osd-lv-2 -v /sys/fs/cgroup:/sys/fs/cgroup:ro -v /dev:/dev -v /root/.ssh:/root/.ssh centos-ceph:7
docker run -d --privileged --name centos-3 --hostname centos-3 --network ceph-network -p 8013:8013 --device /dev/ceph-vg-3/osd-lv-3:/dev/ceph-vg-3/osd-lv-3 -v /sys/fs/cgroup:/sys/fs/cgroup:ro -v /dev:/dev -v /root/.ssh:/root/.ssh centos-ceph:7

如果读不懂这几行运行容器的命令也很正常,因为这里的所有参数都是我每摔倒一次、去问一下 GPT4、再加上去的,诸如端口映射、设备映射、挂载目录、Systemd 的启动等等,读不懂但是好奇的也可以去问问 GPT。

进入容器

在创建完三个容器之后,最简单的就是开三个终端,分屏操作,Windows 11 的三页分屏就很爽。

在三个终端分别输入下面三条命令进入容器内。

1
2
3
4
5
6
7
# 在宿主机的 root 用户下
# 第一个终端
docker exec -it centos-1 /bin/bash
# 第二个终端
docker exec -it centos-2 /bin/bash
# 第三个终端
docker exec -it centos-3 /bin/bash

三页分屏

测试容器网络与 SSH

分别进入容器后,可以先测试一下Docker 内部网络与 SSH 互联是否正常。

1
2
3
4
5
6
7
# 在容器 centos-1 内
ping centos-2
ping centos-3
ssh root@centos-2
# 若正常,则应输入 yes,成功进入后输入 exit 退出
ssh root@centos-3
# 若正常,则应输入 yes,成功进入后输入 exit 退出

部署 MON 与 MGR 节点

接下来就是部署 Ceph 集群了,这里只需要在一个容器内执行即可。我选择在 centos-1 容器内执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 在容器 centos-1 内
# 进入预先创建的 ceph-admin 目录
cd ~/ceph-admin

# 初始化 Ceph 集群
ceph-deploy new --cluster-network 192.168.102.0/24 --public-network 192.168.102.0/24 centos-1

# 创建初始 Monitors 节点
ceph-deploy mon create-initial
# 可能出现诸多异常
# 可参考本文的参考文档对照解决

# 将 admin 配置文件部署到所有节点
ceph-deploy admin centos-1 centos-2 centos-3

# 另一个参考文档中使用了这句的参数,本人没有实验,一并输入了,不产生额外影响
ceph-deploy --overwrite-conf admin centos-1 centos-2 centos-3

# 创建 Managers 节点
ceph-deploy mgr create centos-1

成功创建完 MON 节点与 MGR 节点后,就可以在另外两个容器中查看 Ceph 集群的状态了。

1
2
3
# 在容器 centos-2/3 内
ceph health
ceph -s

若显示如下,则说明 Ceph 集群的 MON 节点与 MGR 节点部署成功。打码部分为下一小节的内容。

Ceph 集群状态 MON&MGR

HEALTH_WARN 不影响正常使用,可以忽略,也可在 centos-1 容器内执行如下命令解决。

1
2
3
# 在容器 centos-1 内
# 禁用不安全的全局 ID 回收
ceph config set mon auth_allow_insecure_global_id_reclaim false

部署 OSD 节点

接下来就是部署 OSD 了,这里也只需要在一个容器内执行即可。我选择在 centos-1 容器内执行。这一步折磨了我非常久,最终在 GPT4 的指导下将准备工作中的物理卷转为逻辑卷后才成功。

1
2
3
4
5
6
7
8
# 在容器 centos-1 内
# 创建 OSD,挂载逻辑卷
ceph-deploy osd create --data /dev/ceph-vg-1/osd-lv-1 centos-1
ceph-deploy osd create --data /dev/ceph-vg-2/osd-lv-2 centos-2
ceph-deploy osd create --data /dev/ceph-vg-3/osd-lv-3 centos-3

# 查看 OSD 信息
ceph osd tree

同理,成功创建完 OSD 后,就可以在另外两个容器中查看 Ceph 集群的完整状态了。

Ceph 集群状态 OSD

注意到,仅搭建 Ceph 集群而不使用的话,每个 OSD 的大小可以适当缩小,这里我每个 OSD 都是 10 GB,实际上只需要 2 GB 就够了,3 GB 较为保险,故 OSD 总共只需要 9 GB 的硬盘空间来保证实践的顺利进行。

部署 Ceph Dashboard

Ceph Dashboard 是 Ceph 集群的 Web 管理界面,可以通过浏览器访问,方便地查看集群的状态、监控集群的运行情况、管理集群的配置等。

Dashboard 只需要在一个节点上部署即可,这里我选择在 centos-1 容器内部署。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 在容器 centos-1 内
# 安装 Dashboard
yum install -y ceph-mgr-dashboard

# 启用 Dashboard
ceph mgr module enable dashboard

# 配置 Dashboard 的 SSL、IP 和端口
ceph config set mgr mgr/dashboard/ssl false
ceph config set mgr mgr/dashboard/server_addr 0.0.0.0
# 注意到,在运行容器时,centos-1 容器映射了 8011 端口出去,所以这里使用 8011 端口
ceph config set mgr mgr/dashboard/server_port 8011
ceph config set mgr mgr/dashboard/ssl_server_port 8011

# 重启 Dashboard 服务以使配置生效
ceph mgr module disable dashboard
ceph mgr module enable dashboard

# 配置 Dashboard 用户
echo "admin" > /tmp/pwd.txt
# 账密均为 admin
ceph dashboard ac-user-create admin -i /tmp/pwd.txt administrator
rm /tmp/pwd.txt
# 测试 Dashboard 登录
ceph dashboard ac-user-show admin

运行完上述命令后,如果容器、Docker、宿主机(服务器)的网络配置均没有问题,防火墙均开放 8011 端口的话,就可以在浏览器中输入 http://<服务器 IP>:8011 访问 Ceph 的 Dashboard 了。

Ceph Dashboard

总结

本来以为 Docker 完成这种部署任务可以轻而易举的(就像隔壁 Flink 一样),结果一来是官方文档没有 Docker 相关,二来是互联网上经验教程屈指可数,同样没有同时与 Docker / ceph-deploy 相关的内容,三来是本人的赛博洁癖越发严重,不想为这个小任务去装庞大的 VMWare,所以就坚定地磕磕绊绊地用 Docker 和 ceph-deploy 把 Ceph 成果部署完了。感谢 GPT4 的指导,让我少走了很多弯路!

也是因为在实践的过程中,记录下了很多重复性的命令(用来复制的,虽然后来抽象成了 Dockerfile),所以才突发奇想,马上建个博客,把这个过程记录下来,也算是对自己通了个宵的一个总结。

后日谈

要开始做清理啦!这里提供了一些清理的命令,以免在实验结束后占用服务器资源。

停止与删除容器

1
2
3
# 在宿主机的 root 用户下
docker stop centos-1 centos-2 centos-3
docker rm centos-1 centos-2 centos-3

删除 Docker 网络

1
2
# 在宿主机的 root 用户下
docker network rm ceph-network

删除 Docker 镜像

1
2
# 在宿主机的 root 用户下
docker rmi centos-ceph:7 centos:7

删除逻辑卷

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 在宿主机的 root 用户下
lvremove /dev/ceph-vg-1/osd-lv-1
lvremove /dev/ceph-vg-2/osd-lv-2
lvremove /dev/ceph-vg-3/osd-lv-3
vgremove ceph-vg-1
vgremove ceph-vg-2
vgremove ceph-vg-3
pvremove /dev/loop11
pvremove /dev/loop12
pvremove /dev/loop13
losetup -d /dev/loop11
losetup -d /dev/loop12
losetup -d /dev/loop13
rm -f /root/ceph/osd*.img

# 如果物理卷删除失败的话,可以尝试是否还存在设备映射
dmsetup ls

# 如果因为还存在设备映射导致删除失败,可以尝试先删除设备映射再删除物理卷
dmsetup remove ceph--vg--1-osd--lv--1
dmsetup remove ceph--vg--2-osd--lv--2
dmsetup remove ceph--vg--3-osd--lv--3

删除 SSH 密钥

这个好像也不用删。