骏马金龙

网名骏马金龙,钟情于IT世界里的各种原理和实现机制,强迫症重症患者。爱研究、爱翻译、爱分享。特借此一亩三分田记录自己成长点滴!!!
我本问道人,道心不坚,必将与道无缘!

drbd(二):配置和使用

drbd的简介、同步机制和安装见另一篇文章:drbd(一):简介、同步机制和安装

本文所述为drbd8.4的配置,和8.4版本之前的版本,以及drbd9版本的差别都非常大。

1.drbd配置文件

drbd的主配置文件/etc/drbd.conf,为了管理的便捷性,在此文件中使用了include指令指定了包含的配置文件段,默认的是在/etc/drbd.d/目录下。在此目录有全局配置文件global_common.conf和其他配置文件*.res文件。其中在主配置文件中include全局配置文件的指令只能出现一个,且必须出现在最前面。

两个节点的配置文件应尽量完全一致。

/usr/share/doc/drbd-版本/下有drbd.conf的样例配置文件。

以下是global_common.conf的结构。

global {
usage-count yes;  # 是否参加drbd的使用者统计,默认此选项为YES
}

common {      # common段定义每一个资源从此继承的参数,非必须,但建议将多个资源共享的参数定义在此以降低配置文件的复杂度
        handlers {
}
        startup {
        }
        options {
        }
        disk {
}
        net {
        }
}

全局配置修改如下:

global {
        usage-count no;
}

common {
handlers{       # 定义出现以下问题(如splitbrain或out-of-sync错误)时处理策略
          pri-on-incon-degr "/usr/lib/drbd/notify-pri-on-incon-degr.sh; /usr/lib/drbd/notify-emergency-reboot.sh; echo b > /proc/sysrq-trigger ; reboot -f";
          pri-lost-after-sb "/usr/lib/drbd/notify-pri-lost-after-sb.sh; /usr/lib/drbd/notify-emergency-reboot.sh; echo b > /proc/sysrq-trigger ; reboot -f";
          local-io-error "/usr/lib/drbd/notify-io-error.sh; /usr/lib/drbd/notify-emergency-shutdown.sh; echo o > /proc/sysrq-trigger ; halt -f";
          split-brain "/usr/lib/drbd/notify-split-brain.sh root";
          out-of-sync "/usr/lib/drbd/notify-out-of-sync.sh root";
}
        disk {
              on-io-error detach; # 当发生io错误时,直接拔除备节点设备
              resync-rate 600M;
        }
       # syncer {       # 注意,8.4版本已不支持该选项,要配置相关项,参见https://docs.linbit.com/docs/users-guide-8.4/#s-recent-changes-config
       #    rate 10M;   # re-sync速率,官方建议设置为网络IO和磁盘IO能力最小者的30%
       #    verify-alg crc32c;  # 用于校验block是否一致
        }
        protocol C; # 定义使用C协议,即同步复制。可不定义,此为默认
}

再新建一个配置文件data1.res,里面定义资源。

resource data1 {   # 定义资源名称
  on drbd1.longshuai.com {   # 指定在drbd1.longshuai.com节点上,节点名要和uname -n一致 
    device    /dev/drbd0;    # 指定drbd设备,0是其此设备号
    disk      /dev/sdb5;     # 指定要同步的数据分区
    address   192.168.100.51:7788; # 指定监听用来同步数据的地址和端口,此处指定为数据同步专用地址eth1
    meta-disk /dev/sdb1;  
}
  on drbd2.longshuai.com {
    device    /dev/drbd0;
    disk      /dev/sdb5;
    address   192.168.100.52:7788;
    meta-disk /dev/sdb1;
  }
}

或者简化为如下:

resource data1 {
    device    /dev/drbd0;
    disk      /dev/sdb5;
    meta-disk /dev/sdb1[0];
    on drbd1.longshuai.com {
        address   192.168.100.51:7788;
    }
    on drbd2.longshuai.com {
        address   192.168.100.52:7788;
    }
}

resource段用于定义drbd资源,每个资源通常定义在一个单独的位于/etc/drbd.d目录中的以.res结尾的文件中。资源在定义时必须为其命名,名字可以由非空白的ASCII字符组成。每一个资源段的定义中至少要包含两个节点,其它参数均可以从common段或drbd的默认中进行继承。

其中上述配置文件的meta-disk有三种记录方式:internal/device/device[index_num]。其中不管是哪种方式,metadata存放的分区不能格式化,哪怕使用internal时metadata和一般data在同一个分区也不能格式化该分区。

internal是将元数据也写入到数据分区的尾部,即数据和元数据同分区。如果指定的device没有给定index时,则表示元数据存储到该设备中。如果某节点指定device[index_num],那么指定几次元数据分区索引就必须大于128M的几倍,例如上述文件中drbd1.longshuai.com节点指定了/dev/sdb1[0],那么sdb1就必须大于128M,如果此时其他资源的节点也指定了同一台服务器的/dev/sdb1[1],则指定了两次就必须大于256M。指定为internal和device时,元数据区的大小是drbd自行计算的。

on hostname段中,hostname必须和uname -n一致,因为drbd默认是基于主机名进行解析的。但是,drbd支持基于ip地址的解析,这时drbd根据ip地址进行漂移,ip地址在哪个主机上,就解析到哪个主机上。具体内容见官方手册Floating peers

2.创建metadata块(初始化)并计算metadata区的大小

drbdadm create-md [all|resource_names]

两节点都初始化。

[root@drbd1 drbd.d]# drbdadm create-md data1
initializing activity log
NOT initializing bitmap
Writing meta data...
New drbd meta data block successfully created.

初始化成功后,可以使用下面几个命令来获取drbd的metadata区信息。

[root@drbd1 ~]# drbdadm show-gi data1

       +--<  Current data generation UUID  >-
       |               +--<  Bitmap's base data generation UUID  >-
       |               |                 +--<  younger history UUID  >-
       |               |                 |         +-<  older history  >-
       V               V                 V         V
0000000000000004:0000000000000000:0000000000000000:0000000000000000:0:0:0:0:0:0:0
                                                                    ^ ^ ^ ^ ^ ^ ^
                                      -<  Data consistency flag  >--+ | | | | | |
                             -<  Data was/is currently up-to-date  >--+ | | | | |
                                  -<  Node was/is currently primary  >--+ | | | |
                                  -<  Node was/is currently connected  >--+ | | |
         -<  Node was in the progress of setting all bits in the bitmap  >--+ | |
                        -<  The peer's disk was out-dated or inconsistent  >--+ |
      -<  This node was a crashed primary, and has not seen its peer since   >--+

flags: Secondary, StandAlone, Inconsistent
meta-data: clean
zero size device -- never seen peer yet?

从上面命令的结果中,可以看出数据的代数,还能获取一些节点状态信息。

关于drbd的代数,它在drbd的内部机制中,用于实现:
(1).判断两节点是否是同一个集群的节点。也就是说,对方节点是不是自己的对端节点。因为有可能出现意外连接,却不是自己对端的情况。
(2).判断节点设备是否需要全部重新同步(re-sync)或者部分重新同步。
(3).判断重新同步的方向。即是从节点1重新同步到节点2还是从节点2重新同步到节点1。
(3).标识节点是否处于脑裂(brain split)。

[root@drbd1 ~]# drbdadm dump-md data1
# DRBD meta data dump
# 2018-03-29 22:53:52 +0800 [1522335232]
# drbd1.longshuai.com> drbdmeta 0 v08 /dev/sdb1 flex-external dump-md
#

version "v08";

# md_size_sect 1951744
# md_offset 0
# al_offset 4096
# bm_offset 36864

uuid {
    0x0000000000000004; 0x0000000000000000; 0x0000000000000000; 0x0000000000000000;
    flags 0x00000000;
}
# al-extents 257;
la-size-sect 0;
bm-byte-per-bit 4096;
device-uuid 0x0A293D126547895D;
la-peer-max-bio-size 0;
al-stripes 1;
al-stripe-size-4k 8;
# bm-bytes 0;
bm {
}
# bits-set 0;

从此命令中可以获知不同标记代数的uuid值,以及metadata的元数据信息,例如md_size_sect=1951744表示元数据所在分区占用了1951744个扇区。注意,该命令不要在drbd设备已启动的情况下执行。

知道这两个命令可以获取一些信息后,现在我们要做的是计算metadata部分的数据大小。这个大小在"修改drbd设备空间大小"时有用。

首先获取元数据所在分区的扇区数。即上面结果中的"md_size_sect"。不过也可以使用块设备工具blockdev来获取。

[root@drbd1 ~]# blockdev --getsz /dev/sdb1
1951744

有了该值,根据计算公式:Size = (md_size_sect/2^18)*8*N+72进行计算。其中md_size_sect如上计算,N是对端的数量,一般情况下drbd实现的是双节点,因此N=1,可以不用考虑。计算的结果是扇区数。

因此,此处计算的结果为:1951744/1024/256*8+72=131.5625共132扇区。如果要转换为KB大小,则再除以2即可(假设扇区大小为512bytes)。

3.在节点上启动drbd

可使用如下命令来管理资源的启动、停止。

drbdadm {up|down} {all|resource_names}

例如:

drbdadm up data1

如果启动报以下错误,则可能是因为对metadata进行了格式化。解决方法是将其删除再重新创建但不要格式化。

0: Failure: (119) No valid meta-data signature found.

此时可以查看/proc/drbd文件来查看drbd中配置的资源的状态,也可以使用命令drbd-overview RESOURCEdrbdadm status RESOURCE来查看。如果角色ro是从/从,则表明配置成功,若有一端是unknown,则表明和对方不能通信,可能是主机路由的问题,这样会导致脑裂的问题。

cat /proc/drbd
version: 8.4.9-1 (api:1/proto:86-101)
GIT-hash: 9976da086367a2476503ef7f6b13d4567327a280 build by mockbuild@Build64R6, 2016-12-13 18:38:15
 0: cs:Connected ro:Secondary/Secondary ds:Inconsistent/Inconsistent C r-----
    ns:0 nr:0 dw:0 dr:0 al:8 bm:0 lo:0 pe:0 ua:0 ap:0 ep:1 wo:f oos:6297452

状态信息极其重要,其中:

  • cs:connection state,必须是connected状态才表示连接成功。
  • ro:roles,各节点是是primary还是secondary。
  • ds:disk state,数据的状态,由此判断是否要同步、重新同步、正在同步、同步完成、是否一致等等信息。

关于drbd的状态信息,见另一篇文章:drbd的状态说明

实际上,drbdadm up启动drbd设备时做了很多工作,如果细致划分的话,可以将"drbdadm up"拆分为以下几个动作:

  1. 将drbd的资源关联到底层设备(metadata和data区)上,使之能通过底层设备存、取数据。该过程调用的是drbdsetup程序。
    drbdadm attach data1
    
  2. 加载drbd资源的同步参数。
    drbdadm syncer data1
    
  3. 连接对端。
    drbdadm connect data1
    
    这些命令在drbdadm中部分已失效,放在这里只是为了说明"up"时所执行的几个步骤。

到目前为止,drbd的资源已经关联完成,也已经准备好进行同步,所不知道的仅仅只是谁作为同步的源端,谁做为同步的目标端,也就是primary和secondary的角色。

4.实现drbd的主从同步

在需要设置为主机点的机器上执行:

drbdadm primary --force data1  # 第一次初始化同步只能执行这个
# 或者
drbdsetup /dev/drbd0 primary

其中"--force"表示强制升级为primary,这会导致本节点的数据强制同步到对端上。

在drbd8.4之前,没有"--force"选项,而是使用等价的"--overwrite-data-of-peer",由于它调用的是drbdsetup,所以同时还要在选项前加上"--"表示drbdadm将此选项传递给drbdsetup,即:

drbdadm -- --overwrite-data-of-peer primary data1

由于是第一次执行同步,因此该过程会同步整个分区进行初始化。

[root@drbd1 ~]# cat /proc/drbd
version: 8.4.10-1 (api:1/proto:86-101)
GIT-hash: a4d5de01fffd7e4cde48a080e2c686f9e8cebf4c build by mockbuild@, 2017-09-15 14:23:22
 0: cs:SyncSource ro:Primary/Secondary ds:UpToDate/Inconsistent C r-----
    ns:812136 nr:0 dw:0 dr:813048 al:8 bm:0 lo:0 pe:0 ua:0 ap:0 ep:1 wo:f oos:4069272
        [==>.................] sync'ed: 16.7% (3972/4764)M
        finish: 0:02:24 speed: 28,216 (17,652) K/sec

可以看到本端已经设置为主节点,且ds的一端是uptodate状态,最后还看到了镜像到对方的进度信息。

当镜像完成后,再查看资源的信息。可以发现两端的ds都变成uptodate状态了。说明镜像完成了。

[root@drbd1 ~]# cat /proc/drbd
version: 8.4.10-1 (api:1/proto:86-101)
GIT-hash: a4d5de01fffd7e4cde48a080e2c686f9e8cebf4c build by mockbuild@, 2017-09-15 14:23:22
 0: cs:Connected ro:Primary/Secondary ds:UpToDate/UpToDate C r-----
    ns:4881408 nr:0 dw:0 dr:4882320 al:8 bm:0 lo:0 pe:0 ua:0 ap:0 ep:1 wo:f oos:0

5.数据同步和主从角色切换

现在已经有一端是主节点了。drbd的主从节点中,只有主节点是可以挂载并进行读写的。所以在主节点上挂载drbd设备(可能需要重新格式化data分区,而且如果metadata使用的是internal模式,则需要格式化分区才OK)。

挂载drbd到mnt,然后向其中拷贝一个文件。

[root@drbd1 ~]# mount /dev/drbd0 /mnt
[root@drbd1 ~]# cp /etc/inittab /mnt
[root@drbd1 ~]# ls /mnt
inittab  lost+found

如果对端也有这个文件则说明同步成功了,但是对端是从节点,无法读取数据,所以只能切换主从角色再查看。不过在,实验环境下直接把从节点的drbd给down掉,再直接挂载/dev/sdb5也是可以的。

首先将主节点切换为从节点,必须先卸载挂载点才行。

[root@drbd1 ~]# umount /mnt
[root@drbd1 ~]# drbdadm secondary data1 
[root@drbd1 ~]# drbd-overview
 0:data1/0  Connected Secondary/Secondary UpToDate/UpToDate

再在从节点上将自己设置为主节点,然后挂载drbd设备,再查看是否有文件同步过来了。

[root@drbd2 ~]# drbdadm primary data1
[root@drbd2 ~]# mount /dev/drbd0 /mnt
[root@drbd2 ~]# ls /mnt
inittab  lost+found

这说明实现了数据同步。但是这样手动切换来切换去的很麻烦,一般会将其交给heartbeat或者corosync来管理,实现drbd的自动切换。

6.drbd脑裂的解决方法

当DRBD的两节点都发现对方后,并都交换了初始化握手协议后,发现双方都是primary角色,就会出现脑裂。出现脑裂后,由于两端节点都可以挂载、写数据,会导致数据的混乱。

当检测到出现脑裂时,drbd会立即中断双方的连接,并在日志中记录

Split-Brain detected, dropping connection!

在出现脑裂的时候,一定有一端的连接状态处于StandAlone,另一端的状态可能是StandAlone(当双方同时检测到脑裂),也可能是Connecting(当一端先检测到脑裂立即中断连接后使得另一端无法再检测出脑裂)。

出现脑裂的时候,如果没有配置drbd自动从脑裂状态恢复,那么必须进行人为的手动干涉。干涉的方法是放弃一端的数据,这一端称为"脑裂的受害者",另一端保存数据的节点则称为"脑裂的幸存者"。

处理方法如下:

(1).在脑裂的受害者节点上执行:

drbdadm disconnect {resource | all}   # 先中断连接,防止再次写入数据
drbdadm secondary {resource | all}    # 设置为secondary
drbdadm connect --discard-my-data {resource | all}   # 丢弃数据,并再次连接

(2).如果脑裂的幸存者节点也是StandAlone状态,则执行下面的命令重新连接,如果仍然保持Connecting状态,则不需任何操作:

drbdadm disconnect {resource | all}
drbdadm connect    {resource | all}

当干涉完两边后,两边重新建立连接,重新握手交换初始化协议信息。此时,脑裂的受害者端的状态将变为SyncTarget,表示同步的目标端,它将从另一节点上获取数据。

但注意,受害者在SyncTarget状态下同步数据时,并不会从幸存者节点获取所有数据,而是按照自己节点上的事务信息进行回滚,再从幸存者节点上获取回滚后还缺少的数据因此,drbd脑裂后完成数据同步也是很快的。

7.drbd多卷组配置

前文实验中使用的配置文件如下:

[root@drbd2 ~]# drbdadm dump data1
# resource data1 on drbd2.longshuai.com: not ignored, not stacked
# defined at /etc/drbd.d/data1.res:1
resource data1 {
    on drbd1.longshuai.com {
        device           /dev/drbd0 minor 0;
        disk             /dev/sdb5;
        meta-disk        /dev/sdb1;
        address          ipv4 192.168.100.51:7788;
    }
    on drbd2.longshuai.com {
        device           /dev/drbd0 minor 0;
        disk             /dev/sdb5;
        meta-disk        /dev/sdb1;
        address          ipv4 192.168.100.52:7788;
    }
}

它等价于:

resource data1 {
        device /dev/drbd0;
        disk /dev/sdb5;
        meta-disk /dev/sdb1;
        on drbd1.longshuai.com {
                address 192.168.100.51:7788;
        }
        on drbd2.longshuai.com {
                address 192.168.100.52:7788;
        }
}

其实它还等价于:

resource data1 {
        volume 0 {
                device /dev/drbd0;
                disk /dev/sdb5;
                meta-disk /dev/sdb1;
        }
        on drbd1.longshuai.com {
                address 192.168.100.51:7788;
        }
        on drbd2.longshuai.com {
                address 192.168.100.52:7788;
        }
}

drbd会为没有分卷组的资源使用默认卷组"volume 0"。

如果需要多个底层设备(磁盘、分区、LVM、RAID等)提供drbd的同步功能,可以设置多个卷组。

例如,除了上面使用的/dev/sdb{5,1}外,添加/dev/sdc1(data区)、/dev/sdc2(metadata区)到另一个卷组。

resource data1 {
        volume 0 {
                device /dev/drbd0;
                disk /dev/sdb5;
                meta-disk /dev/sdb1;
        }
        volume 1 {
                device /dev/drbd1;
                disk /dev/sdc1;
                meta-disk /dev/sdc2;
        }
        on drbd1.longshuai.com {
                address 192.168.100.51:7788;
        }
        on drbd2.longshuai.com {
                address 192.168.100.52:7788;
        }
}

如果第二个卷组是在drbd已经运行后再添加的完全的新分区,则需要先创建元数据区、调整配置文件、启动资源等过程。

两边节点都执行:

drbdadm create-md data1/1  # data1/1表示data1资源下的卷组1
drbdadm adjust data1       # 调整资源,相当于reload资源配置文件

主节点执行:

drbdadm --force primary data1/1

查看两卷组的状态信息:

[root@drbd1 ~]# cat /proc/drbd                 
version: 8.4.10-1 (api:1/proto:86-101)
GIT-hash: a4d5de01fffd7e4cde48a080e2c686f9e8cebf4c build by mockbuild@, 2017-09-15 14:23:22
 0: cs:Connected ro:Primary/Secondary ds:UpToDate/UpToDate C r-----
    ns:76408 nr:0 dw:76408 dr:3441 al:22 bm:0 lo:0 pe:0 ua:0 ap:0 ep:1 wo:f oos:0
 1: cs:SyncSource ro:Primary/Secondary ds:UpToDate/Inconsistent C r-----
    ns:5352 nr:0 dw:0 dr:6264 al:8 bm:0 lo:0 pe:0 ua:0 ap:0 ep:1 wo:f oos:4876056
        [>....................] sync'ed:  0.2% (4760/4764)M
        finish: 0:43:32 speed: 1,784 (1,784) K/sec

虽然可以通过resource_name/volume_id的方式指定某个资源中的卷组,甚至上面通过data1/1来指定其作为primary。

但一定要注意,资源是有层次的,卷组是资源中的一个子对象,它代表的只是底层设备的一个关联对象。因此,在资源层次上的操作(如资源、连接),必须指定整个资源,而不能指定资源中的某个卷组。例如:

drbadm {up | down | connect | disconnect | primary | secondary} <resource>

如果这些命令指定资源中的某个卷组,则会报告错误,类似如下:

[root@drbd90 ~]# drbdadm down rs0/0     
down operates on whole resources, but you specified a specific volume!

但是,在底层设备的关联层次上,可以指定资源中的单个卷组。例如:

drbsetup {attach | detach} <volume_id>

也就是说,每个资源中要么完全是primary,要么完全是secondary,而不能volume 0是primary,volume 1是secondary,因为primary、secondary是资源层次上的概念,而不是设备层次的概念。

再次提醒,两端的配置文件应当尽量保持一致,因此最好不要随意分区。

posted @ 2018-03-30 22:09  骏马金龙  阅读(...)  评论(...编辑  收藏