Chinaunix首页 | 论坛 | 博客
  • 博客访问: 96098
  • 博文数量: 38
  • 博客积分: 950
  • 博客等级: 准尉
  • 技术积分: 235
  • 用 户 组: 普通用户
  • 注册时间: 2011-03-01 10:17
文章分类
文章存档

2011年(38)

我的朋友

分类: LINUX

2011-05-15 11:09:17

介绍

今天在调研分布式文件系统时,非常偶然的机会看到sheepdog ,然后接着又看到KVM这个关键字,让我异常兴奋,这不是我一直在找的KVM镜像存储的分布式存储系统吗,原来KVM/Qemu最近已经开始对其进行了开发,并在Qemu 0.13.0版本后加入了与sheepdog的支持,太佩服国外的开源贡献者。

sheepdog(牧羊犬) 官方网站:

Sheepdog is a distributed storage system for KVM. It provides highly available block level storage volumes that can be attached to KVM virtual machines. Sheepdog scales to several hundreds nodes, and supports advanced volume management features such as snapshot, cloning, and thin provisioning.

安装步骤1,环境准备

 

2,安装corosync

corosync 是linux 集群管理的引擎,具体请参看

点击隐藏SHELL CODE
$ tar -xzvf corosync-1.3.0.tar.gz $ cd corosync-1.3.0 $ ./autogen.sh $ ./configure $ sudo make install
3,qemu 0.13的安装 (略)4,sheepdog安装
点击隐藏SHELL CODE
$ tar -xzvf sheepdog-0.2.0.tar.gz $ cd sheepdog-0.2.0 $ ./autogen.sh $ ./configure $ sudo make install

这时可能会出现linux/signalfd.h 这个文件找不到,以及后续会出现signalfd undefined reference 的错误,我的系统是CentOS ,出现了这种错误,出现这种情况的话,我简单的解决方法是对signalfd进行模拟,这点我参考了qemu的有关实现,
具体是在sheep/work.c中加入以下代码:
代码太长,我贴到最后了,请参看附件

5,sheepdog的具体使用

1> 配置/etc/corosync/corosync.conf
mv /etc/corosync/corosync.conf.example /etc/corosync/corosync.conf
同时修改,bindnetaddr字段为自己的ip地址,mcastaddr(怎么修改暂时不清楚,应该是广播的地址,保持默认值即可)
2> 启动corosync
# corosync {注:需要是root账户}
3> 如果是ext3文件系统,需要加入user_xattr

点击隐藏SHELL CODE
mount -o remount,user_xattr /

4> 启动Sheep

点击隐藏SHELL CODE
# sheep ~/store_dir #不能是相对路径,相对路径会出错 # collie cluster format --copies=3

5> 查看状态

点击隐藏SHELL CODE
# collie node list # collie cluster info # colli vdi list

6> 创建镜像

点击隐藏SHELL CODE
# qemu-img create sheepdog:Alice 256G # qemu-img convert ~/amd64.raw sheepdog:Bob

不过我在Convert时一直提示不成功,error while writing
查找原因:通过查看sheep.log日志文件,可以发现时由于Too Many open files 错误提示,原来是打开太多文件所造成的,因为sheepdog对文件进行4M进行分片的,这样会导致大量的文件打开,而一般的系统进程最多的文件数是1024 所以就这个错误!
解决方法:ulimit -n 4096 {或者更大的值} 这样修改只是在目前会话中有效,需要长久生效,请参看这篇文章

总结

sheepdog作为一个新的项目,感觉还是挺不错的,刚好弥补了开源虚拟化中有关镜像存储的问题,为后续的虚拟机迁移做准备,目前sheepdog暂时还未支持migrate ,不过看它的TODO LIST 下一步已经开始做了,系统能够尽快做出来,下一步主要是看下它的源码,因为很多地方会出现诡异的错误的,还需要解决呀!

本文地址:http://www.yaronspace.cn/blog/index.php/archives/1065

附件代码:

点击隐藏C CODE
#ifndef _SIGNALFD #include     /* * include/linux/signalfd.h * * Copyright (C) 2007 Davide Libenzi * */   /* For O_CLOEXEC and O_NONBLOCK */ #include     /* Flags for signalfd4. */ #define SFD_CLOEXEC O_CLOEXEC #define SFD_NONBLOCK O_NONBLOCK   struct signalfd_siginfo { __u32 ssi_signo; __s32 ssi_errno; __s32 ssi_code; __u32 ssi_pid; __u32 ssi_uid; __s32 ssi_fd; __u32 ssi_tid; __u32 ssi_band; __u32 ssi_overrun; __u32 ssi_trapno; __s32 ssi_status; __s32 ssi_int; __u64 ssi_ptr; __u64 ssi_utime; __u64 ssi_stime; __u64 ssi_addr;   /* * Pad strcture to 128 bytes. Remember to update the * pad size when you add new members. We use a fixed * size structure to avoid compatibility problems with * future versions, and we leave extra space for additional * members. We use fixed size members because this strcture * comes out of a read(2) and we really don't want to have * a compat on read(2). */ __u8 __pad[48]; };   void qemu_set_cloexec(int fd) { int f; f = fcntl(fd, F_GETFD); fcntl(fd, F_SETFD, f | FD_CLOEXEC); } struct sigfd_compat_info { sigset_t mask; int fd; };   static void *sigwait_compat(void *opaque) { struct sigfd_compat_info *info = opaque; int err; sigset_t all;   sigfillset(&all); sigprocmask(SIG_BLOCK, &all, NULL);   do { siginfo_t siginfo;   err = sigwaitinfo(&info->mask, &siginfo); if (err == -1 && errno == EINTR) { err = 0; continue; }   if (err > 0) { char buffer[128]; size_t offset = 0;   memcpy(buffer, &err, sizeof(err)); while (offset < sizeof(buffer)) { ssize_t len;   len = write(info->fd, buffer + offset, sizeof(buffer) - offset); if (len == -1 && errno == EINTR) continue;   if (len <= 0) { err = -1; break; }   offset += len; } } } while (err >= 0);   return NULL; }   static int qemu_signalfd_compat(const sigset_t *mask) { pthread_attr_t attr; pthread_t tid; struct sigfd_compat_info *info; int fds[2];   info = malloc(sizeof(*info)); if (info == NULL) { errno = ENOMEM; return -1; }   if (pipe(fds) == -1) { free(info); return -1; }   qemu_set_cloexec(fds[0]); qemu_set_cloexec(fds[1]);   memcpy(&info->mask, mask, sizeof(*mask)); info->fd = fds[1];   pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);   pthread_create(&tid, &attr, sigwait_compat, info);   pthread_attr_destroy(&attr);   return fds[0]; } #endif



sheepdog源码分析之关键模块介绍(一)

1)   Worker工作线程模块

该模块是作为sheepdog工作线程模块,存在多个工作线程,默认NR_WORKER_THREAD =64个工作线程;线程的入口函数为worker_routine,同时struct work_queue中pending_list是双向链表,worker_routine从该链表中读取任务,然后执行,接着将执行过的任务放到struct worker_info中finished_list双向链表,然后向main thread发送一个信号,接着调用bs_thread_request_done来执行finished_list中任务的done函数,该函数的作用是发送响应信息。涉及的文件主要是worker.c和Worker.h

struct  work_queue {

int wq_state;  //

int nr_active;  //当前活跃的任务数目

struct list_head pending_list;//待执行的任务列表

struct list_head blocked_list; //没有用处了

};

struct  worker_info {

struct list_head  worker_info_siblings;//链表的连接器,目前只有一个worker_info

int nr_threads;  //线程个数

pthread_mutex_t  finished_lock; //

struct list_head  finished_list;

/* wokers sleep on this and signaled by tgtd */

pthread_cond_t pending_cond;

/* locked by tgtd and workers */

pthread_mutex_t  pending_lock;

/* protected by pending_lock */

struct work_queue  q;

pthread_mutex_t  startup_lock;

pthread_t  worker_thread[0]; //工作线程的数据结构

};

这里需要特殊说明的是,有关任务Worker的属性WORK_SIMPLE和WORK_ORDERED,及有关block相关的函数,在0.2版本中应该是没有用处的,当时我也迷惑一阵。

Sheepdog存在三类的工作任务:

  • Request: 所有来自客户端或者其他sheep的请求
  • Recovery_work: 数据恢复任务
  • Delete_work: 删除vdi的任务
  • Cpg_event_work: 该任务作为cpg有关集群管理的任务,例如节点加入和send_message消息发送等,sheep保证当前系统只运行一个cpg_event_work任务,从sys->cpg_event_siblings中是未执行的任务



sheepdog源码分析之关键数据结构介绍
关键数据结构的说明

1.

struct  sd_req {

uint8_t   proto_ver;

uint8_t   opcode; //操作类型

uint16_t   flags;//

uint32_t   epoch;

uint32_t  id;

uint32_t  data_length;

uint32_t   opcode_specific[8];

};

struct  sd_rsp {

uint8_t              proto_ver;

uint8_t              opcode;

uint16_t       flags;

uint32_t       epoch;

uint32_t     id;

uint32_t      data_length;

uint32_t      result;

uint32_t        opcode_specific[7];

};

这两个数据结构应该是作为抽象类,可以看出sizeof(struct sd_req) == sizeof(struct sd_rsp),这个是设计者故意为之,因为在发送请求和接收响应时,客户端是使用同一片内存区域;

2.

struct  sd_obj_req {

uint8_t     proto_ver;

uint8_t     opcode;

uint16_t    flags;

uint32_t    epoch;

uint32_t        id;

uint32_t        data_length;

uint64_t        oid;//object id

uint64_t        cow_oid;

uint32_t        copies;//副本个数

uint32_t        tgt_epoch;

uint64_t        offset;

};

struct  sd_obj_rsp {

uint8_t     proto_ver;

uint8_t     opcode;

uint16_t    flags;

uint32_t    epoch;

uint32_t        id;

uint32_t        data_length;

uint32_t        result;

uint32_t        copies;

uint32_t        pad[6];

};

对object进行请求及响应,这里需要说明的一点:object在Sheepdog中作为数据存储单元,分为data_object 和vdi_object,分别存储数据和vdi的元数据,即后面提到的sheepdog_inode的内容,分片大小为4M。不知作者为何分这么小的分片?

structsd_vdi_req{

uint8_t proto_ver;

uint8_t opcode;

uint16_t flags;

uint32_t epoch;

uint32_t id;

uint32_t data_length;

uint64_t vdi_size; //vdi的大小

uint32_t base_vdi_id;

uint32_t copies;

uint32_t snapid;

uint32_t pad[3];

};

structsd_vdi_rsp {

uint8_t proto_ver;

uint8_t opcode;

uint16_t flags;

uint32_t epoch;

uint32_t id;

uint32_t data_length;

uint32_t result;

uint32_t rsvd;

uint32_t vdi_id;

uint32_t pad[5];

};

vdi进行有关操作的请求和响应

3.

struct  sd_vdi_req {

uint8_t     proto_ver;

uint8_t     opcode;

uint16_t    flags;

uint32_t    epoch;

uint32_t        id;

uint32_t        data_length;

uint64_t    vdi_size; //vdi的大小

uint32_t        base_vdi_id;

uint32_t    copies;

uint32_t        snapid;

uint32_t        pad[3];

};

struct  sd_vdi_rsp {

uint8_t     proto_ver;

uint8_t     opcode;

uint16_t    flags;

uint32_t    epoch;

uint32_t        id;

uint32_t        data_length;

uint32_t        result;

uint32_t        rsvd;

uint32_t        vdi_id;

uint32_t        pad[5];

};

vdi进行有关操作的请求和响应

4

struct  sd_so_req {

uint8_t              proto_ver;

uint8_t              opcode;

uint16_t   flags;

uint32_t   epoch;

uint32_t        id;

uint32_t        data_length;

uint64_t   oid;

uint64_t   ctime;

uint32_t   copies;

uint32_t   tag;

uint32_t   opcode_specific[2];

};

struct  sd_so_rsp {

uint8_t              proto_ver;

uint8_t              opcode;

uint16_t   flags;

uint32_t   epoch;

uint32_t        id;

uint32_t        data_length;

uint32_t        result;

uint32_t   copies;

uint64_t   ctime;

uint64_t   oid;

uint32_t   opcode_specific[2];

};

这对请求和响应的数据结构,对应的opcodeSD_OP_MAKE_FS,对整个集群进行format,并提供copies参数,指定默认的副本的个数;

5

struct  sd_list_req {

uint8_t              proto_ver;

uint8_t              opcode;

uint16_t      flags;

uint32_t      epoch;

uint32_t     id;

uint32_t     data_length;

uint64_t     start; //start_hval

uint64_t     end; //end_hval

uint32_t     tgt_epoch; //epoch参数

uint32_t     pad[3];

};

struct  sd_list_rsp {

uint8_t              proto_ver;

uint8_t              opcode;

uint16_t   flags;

uint32_t   epoch;

uint32_t        id;

uint32_t        data_length;

uint32_t        result;

uint32_t        rsvd;

uint64_t        next;

uint32_t        pad[4];

};

主要用于SD_OP_GET_OBJ_LIST操作,获得对应区间上node节点上的object list

6

struct  sd_node_req {

uint8_t              proto_ver;

uint8_t              opcode;

uint16_t      flags;

uint32_t      epoch;

uint32_t     id;

uint32_t     data_length;

uint32_t      request_ver;

uint32_t      pad[7];

};

struct  sd_node_rsp {

uint8_t              proto_ver;

uint8_t              opcode;

uint16_t      flags;

uint32_t      epoch;

uint32_t        id;

uint32_t        data_length;

uint32_t        result;

uint32_t       nr_nodes;

uint32_t       local_idx;

uint32_t       master_idx;

uint64_t       store_size;

uint64_t       store_free;

};

针对SD_OP_STAT_SHEEPSD_OP_GET_NODE_LIST操作,获得node 详细信息,包括store_size store_free等,或者是获得node list.

7

struct  sheepdog_inode {

char name[SD_MAX_VDI_LEN];  //vdi的名字

char tag[SD_MAX_VDI_TAG_LEN]; //tag

uint64_t ctime; // create time

uint64_t snap_ctime; //snapshot time

uint64_t vm_clock_nsec;

uint64_t vdi_size; //vdi size

uint64_t vm_state_size;

uint16_t copy_policy;

uint8_t  nr_copies; //副本的个数

uint8_t  block_size_shift; //data object size

uint32_t snap_id; //snapshot of this vdi

uint32_t vdi_id; //vdi id

uint32_t parent_vdi_id;

uint32_t child_vdi_id[MAX_CHILDREN];

uint32_t data_vdi_id[MAX_DATA_OBJS]; //data object id array

};

sheepdog_inode相当于sheep中存储一个镜像文件,都会存在这个结构与之对应,该结构中保存了数据objectid数组,相当与镜像文件的元数据,同时该结构会持久化保存到vid object中;

8

enum  conn_state {

C_IO_HEADER = 0,

C_IO_DATA_INIT,

C_IO_DATA,

C_IO_END,

C_IO_CLOSED,

};

struct  connection {

int fd;   //sockfd

enum conn_state  c_rx_state; //当前receive状态

int rx_length;

void *rx_buf;

struct sd_req  rx_hdr;

enum conn_state  c_tx_state; //当前transfer状态

int tx_length;

void *tx_buf;

struct sd_rsp  tx_hdr;

};

struct connection结构存储socket连接的状态信息

9

struct  client_info {

struct connection conn;  //conn state

struct request *rx_req;  //current rx_req

struct request *tx_req;  //current tx_req

struct list_head  reqs;  //client 已经收到的request

struct list_head done_reqs; //已经处理完的request,待发送response

int  refcnt;  //引用计数,request 的个数

};

作为client保存信息,其中conn保存连接状态,reqs代表已经收到的requestdone_reqs代表待发送响应的request.

10

enum cpg_event_type {

CPG_EVENT_CONCHG,

CPG_EVENT_DELIVER,

CPG_EVENT_REQUEST,

};

struct cpg_event {

enum cpg_event_type ctype;

struct list_head cpg_event_list;

unsigned int skip;

};

typedef void (*req_end_t) (struct request *);

struct  request {

struct cpg_event cev;

struct sd_req rq;

struct sd_rsp rp;

void *data;

struct client_info *ci;

struct list_head  r_siblings;  //client_info->reqs

struct list_head  r_wlist;    //client_info->done_reqs

struct list_head  pending_list; //sys->pending_list

uint64_t local_oid[2];

struct sheepdog_node_list_entry  entry[SD_MAX_NODES];

int  nr_nodes;

int  check_consistency;

req_end_t  done;

struct work  work;

};

Server 请求的详细信息

11

struct cluster_info {

cpg_handle_t handle;

/* set after finishing the JOIN procedure */

int join_finished;

uint32_t this_nodeid;

uint32_t this_pid;

struct sheepdog_node_list_entry  this_node;

uint32_t epoch;

uint32_t status;

/*

* we add a node to cpg_node_list in confchg then move it to

* sd_node_list when the node joins sheepdog.

*/

struct list_head  cpg_node_list;

struct list_head  sd_node_list;

struct list_head  pending_list;   //未收到响应的request

DECLARE_BITMAP(vdi_inuse, SD_NR_VDIS);

struct list_head  outstanding_req_list;

struct list_head  req_wait_for_obj_list;

struct list_head  consistent_obj_list;

uint32_t  nr_sobjs;  //副本个数

struct list_head  cpg_event_siblings;

struct cpg_event  *cur_cevent;

unsigned long  cpg_event_work_flags;

int  nr_outstanding_io;

int  nr_outstanding_reqs;

uint32_t  recovered_epoch;

};

extern struct cluster_info *sys;

整个集群的信息




sheepdog源码学习笔记一

最近这两天一直在看sheepdog的源码,有关sheepdog的用法,请参考我的另一篇博客:KVM分布式共享存储解决方案-sheepdog 总的来说,sheepdog的代码量不是很大,在一万行左右,比起其他的分布式文件系统如kfs,ceph等还是比较轻量级的,而且定位也是针对qemu/kvm等volume的解决方案.

sheepdog原理介绍

1. sheepdog是作为虚拟机kvm的volume使用的,是非普通的文件系统,这点和Amazon的EBS(Elastic Block Store)比较类似

2. sheepdog是一种对称(symmetric)的结构,各个节点的地位相同,没有中心节点,没有meta-server,使用 对物理节点进行管理

3. sheepdog中的对象存储分为两类,其一是One reader One Writer 其二是No writer multiple reader 而且对象是4M大小分片的,使用“一致性哈希”算法来确定对象存储位置,多副本存储

sheepdog的代码结构./collie/treeview.c ./collie/collie.c ./sheep/vdi.c ./sheep/store.c ./sheep/sdnet.c ./sheep/work.c ./sheep/sheep.c ./sheep/group.c ./lib/logger.c ./lib/event.c ./lib/net.c

sheep目录下是有关sheepdog的大部分逻辑的处理,部署在各个节点上
collie目录是作为管理管理sheep的代码
lib目录下是关于网络、日志和事件等处理模型
…..
今天暂时写到这里……待续




sheepdog源码学习二之代码目录结构介绍

目录结构

include/

  • config.h: 定义公共的宏
  • bitops.h: 有关的位操作,主要是针对oid的使用情况
  • util.h: 公用操作的实现
  • list.h: 双向链表的实现,主要是参考linux内核代码的实现
  • event.h: epoll异步事件模型
  • logger.h: 日志操作
  • net.h: socket网络IO
  • sheepdog_proto.h: sheepdog中用到的操作类型及数据结构的定义:
  • sheep.h: Sheep本身需要的数据结构和操作类型,与sheedog_proto.h为何分开定义暂不清楚

lib/

  • logger.c: 有关日志文件的操作的实现
  • net.c: 有关socket网络IO的实现
  • event.c: 事件模型的有关实现

sheep/

  • sheep_priv.h: 定义相关数据结构和声明相关函数
  • work.h: 定义工作队列对外提供的数据结构和API
  • work.c: 实现工作线程
  • sdnet.c: 对网络IO的进一步封装,包括回调函数的定义
  • group.c: 利用corosync对组进行管理
  • store.c:  sheepdog有关数据存储、epoch和日志的操作
  • vdi.c:    sheepdog中vdi的相关操作
  • sheep.c:  sheep的main函数入口

collie/

  • treeview.h: vdi tree的有关操作
  • treeview.c: vdi tree的实现
  • collie.c: 对sheep进行管理实现




阅读(5338) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~