Sheepdog之dog源码解析

Sheepdog简介

Sheepdog是一个虚拟块设备存储系统,旨在为虚拟机提供高可用的块级别的存储,能够扩展到数千台机器,支持快照、克隆等高级特性。
其架构图如图所示:
Sheepdog


dog

Sheepdog的dog处理输入命令,将一系列命令转换为相应的操作发送到sheep
Sheepdog
虚拟机通过相应的模块提交创建虚拟块设备的请求,Sheepdog通过一致性哈希算法,类似于Ceph组织整个集群,不同的虚拟机可以配置不同的Sheepdog,这样可以消除单台服务器的瓶颈,但是也会带来一致性开销比较大的问题。getway、dog和sheep都是属于sheepdog的模块,getway将请求转发到相应的机器进行处理。dog是Sheepdog中的命令行工具,用于处理用户输入的一些列命令,通过sock本地通信文件提交到getway进行处理。

源码结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
sheepdog
│···
└─── dog
│farm
│benchmark.c Dog的benchmark
│cluster.c Sheepdog集群命令处理文件
│common.c 通用函数模块
│dog.c 程序初始化和入口文件
│nfs.c NFS文件系统相关命令
│node.c 节点命令处理文件
│trace.c 跟踪及调试文件
│treeview.c 查看跟踪及调试文件
│upgrade.c 更新操作
│vdi.c 块设备命令处理文件
│dog.h dog操作头文件
│treeview.h vid树头文件
│···

执行流程

主流程


vdi命令

  • check:检查并修复镜像的一致性
  • create:创建一个镜像
  • snapshot:创建一个快照
  • clone:克隆一个镜像
  • delete:删除一个镜像
  • rollback:回滚到一个快照
  • list:列出所有镜像
  • tree:以树的格式显示镜像
  • graph:以graphviz dot的格式显示镜像
  • object:显示镜像的对象信息
  • track:在镜像中显示对象的历史信息
  • setattr:设置VDI属性
  • getattr:获取VDI属性
  • resize:改变镜像的大小
  • read:从镜像中读取信息
  • write:从镜像中写入信息
  • backup:在两个快照之间创建增量备份并输出到STDOUT
  • restore:从STDIN提供的备份恢复快照映像
  • alter-copy:这是vdi的冗余等级
  • lock:列出或解锁vdi设备

cluster命令

  • info:显示集群的信息
  • format:创建一个Sheepdog存储
  • shutdown:停止Sheepdoog
  • snapshot:创建集群的快照
  • recover:恢复集群
  • reweight:重新调整集群
  • check:检查并修复Sheepdog集群
  • alter-copy:设置集群的冗余等级

node命令

  • kill:杀死某个节点
  • list:列出所有节点
  • info:显示每个节点的信息
  • recovery:显示恢复信息或设置/获取节点的恢复速度限制
  • md:磁盘的有关操作
  • stat:显示节点的stat信息
  • log:显示或设置节点的日志等级
  • vnodes:设置一个新的节点
  • format:初始化节点的存储格式

关键结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Sheepdog命令结构体
struct command {
const char *name; // 名称
const struct subcommand *sub; // 子命令
int (*parser)(int, const char *); // 执行函数
};
// Sheepdog子命令结构体
struct subcommand {
const char *name; // 名称
const char *arg; // 参数
const char *opts; // 选项
const char *desc; // 描述
const struct subcommand *sub; // 子命令集
unsigned long flags; // 子命令标志位
int (*fn)(int, char **); // 执行函数
const struct sd_option *options; // 命令选项信息
};
//

VDI创建

VDI的创建过程是在vdi_create()函数中,这个函数的原型如下:

1
static int vdi_create(int argc, char **argv);

VDI设备创建的命令为 dog ,整个执行过程如下:
主流程

1
2
#define SD_DEFAULT_BLOCK_SIZE_SHIFT 22
object_size = (UINT32_C(1) << vdi_cmd_data.block_size_shift

Sheepdog中对象默认大小为1左移22位,十进制为4194304,即4M

1
2
#define OLD_MAX_DATA_OBJS (1ULL << 20)
old_max_total_size = object_size * OLD_MAX_DATA_OBJS;

Sheepdog中VDI设备默认的最大容量为1左移20位再乘以对象大小,为4T,如果创建大容量的VDI设备, 可以添加-y选项,或者设置更大的对象块大小

1
2
3
#define MAX_DATA_OBJS (1ULL << 32)
#define SD_OLD_MAX_VDI_SIZE (SD_DATA_OBJ_SIZE * OLD_MAX_DATA_OBJS)
#define SD_MAX_VDI_SIZE (SD_DATA_OBJ_SIZE * MAX_DATA_OBJS)

SD_MAX_VDI_SIZE定义的是Sheepdog支持的VDI设备的最大容量,为16PB


补充:

此处主要用于补充源码中未接触到的知识点。

likely() & unlikely()

Sheepdog中函数分配内存时判断指针是否为空使用了unlikely函数。
likely()和unlikely()函数的混定义如下:

1
2
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)

使用了__builtin_expect函数,其好处是告诉编译器很有可能会发生的时,从而将很有可能发生的事顺序编译,从而避免jmp指令跳转造成CPU指令顺序混乱,从而影响CPU指令执行效率。


get_opt() && getopt_long()

Linux中,对程序参数进行处理的函数。
函数原型:

1
2
3
4
5
6
7
8
9
10
11
12
#include <unistd.h>
int getopt(int argc, char * const argv[],
const char *optstring);
#include <getopt.h>
int getopt_long(int argc, char * const argv[],
const char *optstring,
const struct option *longopts, int *longindex);
int getopt_long_only(int argc, char * const argv[],
const char *optstring,
const struct option *longopts, int *longindex);


epoll

epoll是Linux为了处理大批量句柄,从内核2.5.4之后引进的新特性。在Linux网络编程中,很长的时间都在使用select来做事件触发。相比于select,epoll最大的好处在于它不会随着监听fd数目的增长而降低效率。因为在内核中的select实现中,它是采用轮询来处理的,轮询的fd数目越多,自然耗时越多。

参考资料:
sheepdog(牧羊犬):一种EBS的开源实现
分布式系统sheepdog之dog执行流程