Ceph 和防火墙的故事

实验目的

前段时间,有一个存储节点的系统盘进行了重装,按照正常流程:重装系统->配置系统->部署ceph->重建journal->OSD上线,很快就可以恢复了,可是当时这个节点的OSD上线后,导致前台的VM批量不能开机,紧急处理方法是临时先将这个节点的OSD下线,VM就能正常开机了,后来思考了半天意识到可能是防火墙没有关导致的,并且这个问题的现象是可以复现的,于是就有了下面的实验,目的是探讨下一个节点的防火墙没有关闭,对整个ceph集群有什么样的影响。

实验过程

这里完全模拟上述环境进行搭建了一个类似的ceph环境:

  • CentOS 7.1511 + Hammer 0.94.7。
  • 三个主机ceph-1,ceph-2,ceph-3,每台上面部署一个MON和一个OSD。
  • 建立三个pool:size-1,size-2,size-3,各有1,2,3个副本。
  • 删除ceph-2这个节点上的MON,卸载OSD,开始实验。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@ceph-1 cluster]# ceph -s
cluster f1136238-80d6-4c8e-95be-787fba2c2624
health HEALTH_WARN
...
1/3 in osds are down
1 mons down, quorum 0,2 ceph-1,ceph-3
monmap e1: 3 mons at {ceph-1=172.23.0.101:6789/0,ceph-2=172.23.0.102:6789/0,ceph-3=172.23.0.103:6789/0}
election epoch 20, quorum 0,2 ceph-1,ceph-3
osdmap e31: 3 osds: 2 up, 3 in; 44 remapped pgs
pgmap v49: 256 pgs, 4 pools, 0 bytes data, 0 objects
74420 kB used, 4083 GB / 4083 GB avail
128 active+undersized+degraded
62 active+clean
44 active+remapped
22 stale+active+clean

开启ceph-2的防火墙,重装ceph-2这个节点上的MON和OSD,注意重装MON我采用的是mon create-initial,重装OSD我采用的是直接挂载启动:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@ceph-2 mon]# systemctl start firewalld
[root@ceph-1 cluster]# ceph-deploy mon create-initial
[root@ceph-2 mon]# mount /dev/sdb1 /var/lib/ceph/osd/ceph-1/
[root@ceph-2 mon]# service ceph start osd.1
=== osd.1 ===
create-or-move updated item name 'osd.1' weight 1.99 at location {host=ceph-2,root=default} to crush map
Starting Ceph osd.1 on ceph-2...
Running as unit ceph-osd.1.1477890009.210635415.service.
[root@ceph-2 mon]# ceph -s
cluster f1136238-80d6-4c8e-95be-787fba2c2624
health HEALTH_OK
monmap e1: 3 mons at {ceph-1=172.23.0.101:6789/0,ceph-2=172.23.0.102:6789/0,ceph-3=172.23.0.103:6789/0}
election epoch 32, quorum 0,1,2 ceph-1,ceph-2,ceph-3
osdmap e82: 3 osds: 3 up, 3 in
pgmap v132: 256 pgs, 4 pools, 0 bytes data, 0 objects
118 MB used, 6125 GB / 6125 GB avail
256 active+clean

这时候,集群是正常的!这是很可怕的事情,注意,osd.1ceph-2这个节点上的,该节点的防火墙被打开了。

现在来做几个有意思的实验来说明防火墙的影响,我们先找几个有代表性的PG:

1
2
3
4
5
6
7
8
9
10
[root@ceph-2 mon]# ceph df
GLOBAL:
SIZE AVAIL RAW USED %RAW USED
6125G 6125G 118M 0
POOLS:
NAME ID USED %USED MAX AVAIL OBJECTS
rbd 0 0 0 2041G 0
size-1 1 0 0 6125G 0
size-2 2 0 0 3062G 0
size-3 3 0 0 2041G 0
Num PG Pool OSD
T1 1.1a size-1 [1]
T2 1.1c size-1 [2]
2.3 size-2 [2,0]
T3 2.5 size-2 [2,1]
T4 2.25 size-2 [1,0]
3.1 size-3 [1,0,2]

T1: 首先,向PG 1.1a写入数据,分为在ceph-2节点执行写入指令,和在其他节点执行写入指令:

1
2
3
4
5
6
7
8
9
10
11
# -------在ceph-2写入,很快返回,写入成功。
[root@ceph-2 mon]# ceph osd map size-1 file
osdmap e173 pool 'size-1' (1) object 'file' -> pg 1.2e6fb49a (1.1a) -> up ([1], p1) acting ([1], p1)
[root@ceph-2 mon]# rados -p size-1 put file /etc/ceph/ceph.conf
[root@ceph-2 mon]#
# -------在ceph-1写入,指令卡死,写入失败。
[root@ceph-1 cluster]# rados -p size-1 put file /etc/ceph/ceph.conf
2016-10-31 01:31:10.903053 7fa436afb700 0 -- 172.23.0.101:0/3070569903 >> 172.23.0.102:6800/6965 pipe(0x2f9a010 sd=5 :0 s=1 pgs=0 cs=0 l=1 c=0x2f92a80).fault
^Z
[1]+ 已停止 rados -p size-1 put file /etc/ceph/ceph.conf

可见,在ceph-2节点上,可以正常写入,因为这不需要通过防火墙,而在ceph-1写入时,客户端需要连接到ceph-2osd.1,此时防火墙打开,请求被挡住了,写入失败。

T2: 现在向PG 1.1c写入数据,同样分为在ceph-2内外两种情况:

1
2
3
4
5
6
7
8
# -------在ceph-2写入,很快返回,写入成功。
[root@ceph-2 mon]# ceph osd map size-1 file-2
osdmap e201 pool 'size-1' (1) object 'file-2' -> pg 1.fb2f2dc (1.1c) -> up ([2], p2) acting ([2], p2)
[root@ceph-2 mon]# rados -p size-1 put file-2 /etc/ceph/ceph.conf
[root@ceph-2 mon]#
# -------在ceph-1写入,很快返回,写入成功。
[root@ceph-1 cluster]# rados -p size-1 put file-2 /etc/ceph/ceph.conf
[root@ceph-1 cluster]#

因为PG 1.1c位于osd.2ceph-3这个节点上,所以从ceph-2这个节点写入没有问题,同样从ceph-1节点写入也没有问题。

很容易猜测到,向PG 2.3写入都正常,实验现象也一致。

T3: 继续向PG 2.5写入数据:

1
2
3
4
5
6
7
8
9
#--------在ceph-2写入,很快返回,写入成功
[root@ceph-2 mon]# ceph osd map size-2 file-1
osdmap e229 pool 'size-2' (2) object 'file-1' -> pg 2.9c8cdcc5 (2.5) -> up ([2,1], p2) acting ([2,1], p2)
[root@ceph-2 mon]# rados -p size-2 put file-1 /etc/ceph/ceph.conf
[root@ceph-2 mon]#
# -------在ceph-1写入,居然,写入成功了
[root@ceph-1 cluster]# rados -p size-2 put file-1 /etc/ceph/ceph.conf
[root@ceph-1 cluster]#

很容易理解,在ceph-2中写入成功。但是在ceph-1中写入也成功了,这里我的理解是这样的,首先客户端向osd.2写入主副本,然后osd.2osd.1进行从副本的拷贝,但是这里的拷贝应该是失败的,因为osd.1的防火墙被打开了,但是这里是成功的,是因为osd.1在上线时,建立了和osd.2的peer关系,从osd.1->osd.2建立了socket通讯关系,那么这条通讯线路应该形如osd.1<->osd.2,所以,osd.2就可以向osd.1进行副本拷贝。

T4 : 综合T1的外部写入失败,和T3的外部写入成功,我找了一个PG,其OSD分布为[1,0],也就是说,主OSD在osd.1上,实验结果如下:

1
2
3
4
5
6
7
8
9
10
[root@ceph-3 ~]# ceph osd map size-2 file-6
osdmap e412 pool 'size-2' (2) object 'file-6' -> pg 2.bc387525 (2.25) -> up ([1,0], p1) acting ([1,0], p1)
# ------在ceph-2上写入,很快返回,写入成功
[root@ceph-2 ~]# rados -p size-2 put file-6 /etc/ceph/ceph.conf
[root@ceph-2 ~]#
# ------在ceph-1上写入,卡死,写入失败。
[root@ceph-1 ~]# rados -p size-2 put file-6 /etc/ceph/ceph.conf
2016-10-31 04:51:19.329528 7f20d686b700 0 -- 172.23.0.101:0/3159973327 >> 172.23.0.102:6800/3532 pipe(0x7f20cc00c3b0 sd=6 :0 s=1 pgs=0 cs=0 l=1 c=0x7f20cc010650).fault
^Z
[1]+ 已停止 rados -p size-2 put file-6 /etc/ceph/ceph.conf

这里的情况就很容易理解了,因为客户端从ceph-1节点上写入数据时,是建立与osd.1的通讯关系,这时候被防火墙挡住,所以写入失败。

对于三副本,我们可以按照刚刚四个实验进行猜测,对于形如[1,0,2]的PG,只要从外部写入都会是失败的,因为主OSD:osd.1的防火墙被打开,无法建立通讯,但是对于形如[0,1,2]的PG,写入会成功,因为主OSD可以正常通讯,然后osd.1和其他OSD从osd.1的主机内部向外建立通讯关系,使得他们之间的通讯是没有被防火墙阻碍的。这些猜测已经证实过,没有问题。

在实验过程中,查看ceph -w可以看到如下一段log:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
mon.0 [INF] pgmap v846: 256 pgs: 256 active+clean; 1190 bytes data, 133 MB used, 6125 GB / 6125 GB avail
mon.0 [INF] osd.1 172.23.0.102:6800/3532 failed (8 reports from 2 peers after 21.271377 >= grace 20.995909)
mon.0 [INF] osdmap e546: 3 osds: 2 up, 3 in
mon.0 [INF] pgmap v847: 256 pgs: 89 stale+active+clean, 167 active+clean; 1190 bytes data, 133 MB used, 6125 GB / 6125 GB avail
mon.0 [INF] osdmap e547: 3 osds: 2 up, 3 in
mon.0 [INF] pgmap v848: 256 pgs: 89 stale+active+clean, 167 active+clean; 1190 bytes data, 133 MB used, 6125 GB / 6125 GB avail
mon.0 [INF] osd.1 172.23.0.102:6800/3532 boot
mon.0 [INF] osdmap e548: 3 osds: 3 up, 3 in
mon.0 [INF] pgmap v849: 256 pgs: 89 stale+active+clean, 167 active+clean; 1190 bytes data, 133 MB used, 6125 GB / 6125 GB avail
mon.0 [INF] osdmap e549: 3 osds: 3 up, 3 in
mon.0 [INF] pgmap v850: 256 pgs: 89 stale+active+clean, 167 active+clean; 1190 bytes data, 133 MB used, 6125 GB / 6125 GB avail
osd.1 [WRN] map e547 wrongly marked me down
mon.0 [INF] pgmap v851: 256 pgs: 89 stale+active+clean, 167 active+clean; 1190 bytes data, 132 MB used, 6125 GB / 6125 GB avail
mon.0 [INF] pgmap v852: 256 pgs: 256 active+clean; 1190 bytes data, 137 MB used, 6125 GB / 6125 GB avail

有几个关键字:

  • 8 reports from 2 peers after 21.271377 >= grace 20.995909
  • 3 osds: 2 up, 3 in
  • osd.1 172.23.0.102:6800/3532 boot
  • map e547 wrongly marked me down
  • pgmap v852: 256 pgs: 256 active+clean;

也就是说在一些peer关系的OSD报告这个osd.1无法建立peer关系后,也就是osd.0osd.2从外部向osd.1建立peer关系失败了,集群就会主动上报osd.1down,但是实际上,osd.1并没有挂掉,因为它无法接受从外部过来的通讯请求,所以它向集群汇报自己被错误的标记为down了,这时候是osd.1自内向外发送请求,所以不会被堵塞,然后集群在接收到自UP信息之后,会再次将该OSD标记为UP,然后就进入了一个死循环,如果grace时间被设置较长,那么很容忽略掉这个OSD的down的过程,而长时间认为这个OSD是UP的,从人为角度来看,UP的OSD写入数据是没有问题的,但是实际上,Openstack+Ceph的写入可以看做都是从外部写入的,所以对于主OSD的防火墙被打开的PG,所有请求会被阻塞。再回过头来看最开始的VM无法开机的情况就可以理解了,同时,关闭那个节点的所有OSD后,主OSD被转移到其他节点的副OSD上了,写入就可以成功了。

实验结论

这个实验正好验证了ceph的写入过程:client-> osd.primary -> osd.secondary,打开防火墙会阻塞client->osd.primary的写操作,但是不会阻塞副本间的拷贝操作,我的理解就是osd.primary<->osd.secondary 通讯关系可以从防火墙内部向外部建立,一旦建立就不会受防火墙阻塞,所以尽管osd.seconday防火墙被打开,也可以正常写入。