当前位置: 首页 > 工具软件 > mgmt > 使用案例 >

GlusterFS探究(一): dht,afr,fuse, mgmt 层 几个问题总结

庄宇
2023-12-01

 0, 跟踪学习 在dht层实现 的 rebalance 操作

                       a, 在目录上记录 layout范围的结果,
                                  i, mkdir (只在mkdir时刻) 会 有一个 layout对象,除了 start_off 和 stop_off 其他值都可以填充;
                                       而其他值的填充 必须继续往下走。

                                 ii, dht_layout 读写 disk ? ?  客户端第一次 读时 会恢复到mem??? (where?)

 

                                iii,目录是 无 拓扑关系;  /dir1/dir2  是两个目录, 分别为 /dir1 和 /dir1/dir2两个;

 

                      b, dht_layout_search  (用 (path + filename)来计算,并在上级 目录的 ctx里 寻找 对应的访问,然后 确定 哪一个brick通信);

                     c, dht_subvol_get_cached /dht_subvol_get_hashed  (cached 指最早加入的 brick 对应的client端!!)

 

                    d, 可能是因为brick里数据量太少,导致rebalance操作一直没发生 拷贝数据的现象,所以先暂停一下。

 

           1,跟踪学习 replace-brick 操作的实现

                    比如 命令

                        gluster volume replace-brick test 172.16.30.185:/sam6 172.16.30.185:/sam7  start force

 

                    也就是用本机的 /sam7 代替 /sam6。

 

                       a,     当glusterd 进程 处理这个命令时,会修改 /sam6对应brick进程的配置文件,在 features/index 添加一个子xlator , 就是

                                 cluster/pump ->  protocol/client,  充当拷贝数据的客户端。(设为进程A )

 

                               另外, 会生成一个 以protocol/server ->features/locks->storage/posix 为配置文件,生成一个 新glusterfs进程,

                               作为实现replace-brick功能的临时进程。(设为进程B )

 

                      b,     实际的拷贝数据 是 这两个 进程 在socket.c 中 __socket_rwv函数中实现,

                                     A 进程 会打开在原来  brick 上的 老文件,并读取,再写到 对B 的套接字里。

                                     B 进程 不断接受从A来的数据,并且在新的 brick中创建同样的文件。

 

                     c,      具体实现 实在 进程B,会从 老brick上获取每个文件gfid, 并在新brick的 /sam7中查找,因为查找不到,

                            就会导致 cluster/afr层的self-heal

 

                               至于 cluster/afr 层 具体的 self-heal 的过程还得再跟踪下日志和代码。

 

                      d,  replace-brick操作 在gdb跟进程时,只要有耽误,数据就不会再被拷贝,重启服务也不行,只能清理环境,重新再来一遍。                     

 

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXx


         1,  cluster/afr 层 selfheal 过程实现

 

                         比如 命令

                        gluster volume replace-brick test 172.16.30.185:/sam6 172.16.30.185:/sam7  start force

 

                         老brick 对应进程设为 A , 新 的临时 rb进程设为B。

 

               a,    过程可能是:

                        进程B从进程A获取在A上所有 文件和目录的gfid,  然后将 /sam7看作是一个与/sam6互为镜像的brick。而此时它刚刚online, 在上面执行lookup.

                         而  cluster/afr 实现机制,发现 /sam7上 任何一个文件有问题,就会从 /sam6上 拷贝一个文件 覆盖替换掉 /sam6上同名文件。

 

                    (大致过程应该是这样的,而是 还得有更仔细的分析日志的验证。 想法可以有,但是验证需要不少时间。 )

 

               b,   从调用 afr_launch_self_heal 函数进入 selfheal过程。(应该是features/index 层调用这个函数)

 

                       流程大概是:  1,从副本中找到一个能用的 brick对应的xlator;

                                               2, 修复 父亲目录;

                                               3, 修复 entry

                                               4, 修复  metadata

                                               5, 修复  data

 

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


      1,  dht 层 每个目录的 layout信息 是否 保存 到集群的对应目录扩展属性上

 

           因为挂载的客户端毕竟是多变的,而每个目录的layout信息显然 非常重要。

          分析了一下代码, 每个目录的inode的 inode_ctx 中保存的是layout数据,对它的操作也只是在内存中,并没有跟对应brick发起通信。

           这个是否可靠?还是因为对应的代码没找到?

 

           实验:清空所有日志,在 /mnt 下创建一个目录,来分析整个过程。

           分析了storage/posix代码和日志,发现 服务器端实际上只是在对应目录的扩展属性里记录了gfid, (按照设计,这个gfid 包含了 根目录的路径前缀)。并 没有记录 layout信息。

            结论: 每个目录layout信息确实只在客户端内存里,与服务器端无关。

                         layout信息中缓存的是last 在目录下动作(如本身创建,目录下建立文件等,不包括创建子目录)以来,在本卷中各个brick的可用状态。          如果出现某个brick DOWN情况,这样这个目录的layout信息就是有问题的,它会临时把 新文件创建到 临时的brick上。但是过一段时间,会进行 self-heal,即 在 brick 全UP 情况下 应该在的brick上创建链接文件。以待以后来rebalance .

 

     2, 修改了日后实现 ()

          a, 每条日志头上增加一个序列号,方便分清楚次序。

          b,  对最核心的xlator_fops中 各个函数,建立了 fopslog 日志,只记录 它的名字和序列号。

          c,  (将做 )按照 各个 进程 建立目录,所有的日志落在这个目录下。

 

      3,iatt  gfid  stat 信息

           iatt 信息是 glusterfs自己定义的 文件属性信息的结构,它主要是posix的 stat结构(时间增加了微妙部分),和 gfid.

          gfid 是 整个集群里的 inode-number, 唯一标志了一个文件和目录。

          在posix层 最重要的是 通过系统调用 fstat(取stat) 和 lgetxattr (取gfid), 来构造 一个 iatt 或者将新的iatt落盘(实际上只有gfid). 

 

 

 

 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

      


          1, Gluster 使用 fuse 实现挂载功能

          

              
              a,         { ls -l }  => kernel => fuse-k => fuse-u => glfs-graph

                    fuse-u实现 命令行的功能, 即来自fuse-k解析的请求. (这一切 在 fuse-loop大循环中完成, 其监听fd,处理请求)

                   线程 B: 死循环中,平时一直监听/dev/fuse 的fd, 有task时被唤醒,走graph流程来处理请求。(A, A-cbk 阻塞在哪里?)
                   线程 A: 解析请求,并且 添加 task, 并且 最终把数据按照 fuse spec 格式 写到 /dev/fuse 打开的fd 中去。

                    

                   Gluster里fuse相关代码是 fuse-u 相关实现部分,也就是,当标准命令行处理gluster里面的数据时的实现。

           

                   由于客户端只会启动一个进程,而且在挂载点操作很方便,这样就方便了复现和调试 。

                    另外,标准命令行实现是由coreutils库来实现的。通常每个命令都是依次调用几个系统调用函数,将取得的结果返回给客户。       

                  使用GDB 跟挂载的进程,能很轻松的跟踪到 每个命令实现时,实际上调用了那些的posix标准函数。      

          

             b,          fuse:   fuse_attr
                           glfs:   iatt
                         posix:   stat

                      这三个结构是三个系统中定义的元数据相关的核心数据结构。非常核心的功能实现时将这些数据结构互相转换。

 

             c,     实现挂载功能时,fuse成为一个xlator, 并且在被画在客户端graph上。     

                  

                      在 客户端进程的核心结构上, 一般是 fuse xlator作为功能栈的 链表头节点。

                    当阻塞在 readv系统调用上的 fuse xlator 线程B 接受到内核数据后,按照 fuse 标准格式 会解析 OPCODE,它们是posix标准的接口

                    操作码。然后 会验证数据,在有必要时 会执行 STACK_WIND,将调用请求按照功能栈向下面调用,最终是Gluster来处理的请求。 

                       

                     还需解决问题, i,  挂载时端口映射问题; ii, syncop 做的什么。iii,fuse 层缓存数据了么?  iiii, inode  是 独立的一份数据,而每个

                     xlator只是 借用了 唯一的 inode地址来 ctx ?


 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


1, gd_svc_cli_actors 这个 是在哪里起作用的?
         这是个struct数组。而这个struct有个成员为函数,在这个函数中会最终调用glusterd_op_begin_synctask 
             (里面实作是gd_sync_task_begin)

                                  而这个整体 和  gd_svc_peer_prog, gd_svc_cli_prog, gd_svc_mgmt_prog,
                                   gluster_pmap_prog,  gluster_handshake_prog, glusterd_mgmt_hndsk_prog,

                                   这些个整体被 glusterd_program_register 加载到 glusterd 地址空间中来执行。


2, 这个流程在 gd_sync_task_begin 函数中确定好了。

            a, glusterd_lock 

            b, glusterd_op_set_op 

            c, gd_build_peers_list  

            d, gd_lock_op_phase 

            e, glusterd_op_build_payload 
                 //往glusterd进程的一个成员dict添加了一个值,下面流程会用到。
                 //哪个dict ?? 栈上构造的临时空容器,会按后面的需要采集glusterd维护的数据信息。


            f,gd_stage_op_phase

            g, gd_brick_op_phase 

            h, gd_commit_op_phase 


            这些流程中 的每一般 对会按照 op 分别调用不同的函数。在处理结束后,会更新 glusterd保存的元数据信息。

        3,一个dict指针打天下  不断处理会更新其中某些 key 对应的value.

             任何操作都会需要一些glusterd进程缓存的信息,dict会事先采集这些信息,而这个本身也充当对 操作的validate。

 

       
        4, 返回给命令行的结果 是在rpc-xdr来实现,按照xdr标准格式解析请求,再将结果按标准格式返回给gluster命令行进程。

 

           最好能够 多 用GDB跟踪一下glusterd进程,熟悉很多的接口。

 类似资料: