
1. 为什么还要在 CentOS 7 上折腾 CTDB 源码安装CTDB 是集群化 Samba 的核心组件全称 Cluster Trivial Database它把原本单机的 TDB 数据库改造成可以在多节点间同步的分布式数据库从而让 Samba 具备高可用和横向扩展能力。简单说如果你想让多台文件服务器对外呈现同一个共享目录、同一套用户权限、同一份锁状态CTDB 就是绕不开的一环。它负责协调节点间的锁、通知、恢复和故障切换让客户端在某个节点宕机时几乎无感地漂移到其他节点。那为什么不用 yum 直接装原因很现实CentOS 7 官方仓库里的 CTDB 版本偏旧很多新特性比如更细粒度的恢复锁、改进的 IP 接管逻辑根本带不上而且 CTDB 与 Samba、TDB 之间存在严格的版本耦合yum 装出来的组合经常出现 ABI 不匹配跑着跑着就报tdb符号找不到。源码编译虽然麻烦但能精确控制版本组合也方便打补丁和调优。这篇文章就是把我从零编译 CTDB、再到跑通基本集群操作的完整过程拆开讲包括依赖怎么选、configure 参数为什么这么写、编译报错怎么定位、以及第一次启动集群时那些文档里不会写的坑。适合谁看如果你已经对 Samba 单机共享比较熟想进一步做高可用文件集群或者你正在维护一套老旧的 CentOS 7 环境、没法升级系统却又要上 CTDB那这篇内容基本可以照着抄。我会尽量把每一步的“为什么”讲清楚而不是只丢一串命令。2. 编译前的依赖梳理与版本组合决策2.1 CTDB 到底依赖哪些东西CTDB 的源码通常随 Samba 源码包一起发布也可以单独从 Samba 的 Git 仓库里取ctdb子目录。它编译时依赖的东西不算特别多但每一样都卡版本TDBCTDB 的底层存储引擎必须和 CTDB 版本匹配。源码编译时一般用 Samba 自带的 libtdb不要用系统里那个独立的 tdb-devel否则符号版本对不上。Talloc内存池分配库Samba 全家桶都靠它同样建议用源码内自带的。Popt命令行参数解析系统自带的 popt-devel 一般够用。libnl处理网络接口和路由信息CTDB 接管 IP 时要用。CentOS 7 上装libnl-devel即可。libuuid生成唯一标识libuuid-devel。zlib压缩相关zlib-devel。readlinectdb交互式命令行要用readline-devel。pkg-config编译时找库的路径pkgconfig。这里有个经验CentOS 7 默认的 GCC 是 4.8.5编译较新的 Samba/CTDB 源码时对 C99 支持基本够但如果你拿的是很新的源码分支可能会遇到-stdgnu11相关报错那时候要么降源码版本要么手动装 devtoolset。我这次选的是与 CentOS 7 生命周期匹配较好的 Samba 4.10 系列源码CTDB 版本随之为 4.10稳定性经过大量生产验证。2.2 为什么坚持源码内自带的 TDB 和 Talloc很多人图省事直接yum install tdb-devel talloc-devel然后 configure 时用系统的。这样做的直接后果是CTDB 编译时链接的是系统 tdb 的.so但运行时 Samba 又可能加载自己那套两个版本一混ctdbd启动时就会报undefined symbol: tdb_...。更隐蔽的是有些符号虽然存在但语义变了集群跑一段时间后出现数据不一致排查起来极其痛苦。所以我的做法是把 Samba 源码包解压后先进入lib/tdb和lib/talloc分别编译安装到独立前缀比如/usr/local/ctdb-deps然后在编译 CTDB 时通过--with-libtdb和--with-libtalloc指过去。这样整套依赖是自洽的不会和系统库打架。代价是路径要写对后面会讲。2.3 目录规划与用户准备我习惯把自编译的东西统一放在/usr/local/下避免污染系统目录mkdir -p /usr/local/ctdb-deps mkdir -p /usr/local/ctdb mkdir -p /opt/srcCTDB 运行需要专门的系统用户和目录。源码安装不会自动建得手动来groupadd -r ctdb useradd -r -g ctdb -s /sbin/nologin ctdb mkdir -p /var/lib/ctdb /var/log/ctdb /etc/ctdb chown -R ctdb:ctdb /var/lib/ctdb /var/log/ctdb/etc/ctdb放配置文件/var/lib/ctdb放持久化数据库和恢复锁/var/log/ctdb放日志。这三个目录的权限很关键如果 ctdb 用户没有写权限ctdbd启动时会直接退出日志里只留一句Failed to create ...新手很容易卡在这。3. 从源码到可执行文件编译链路实操3.1 先编 TDB 和 Talloc进入 Samba 源码目录后cd /opt/src/samba-4.10.x/lib/talloc ./configure --prefix/usr/local/ctdb-deps make -j$(nproc) make install cd ../tdb ./configure --prefix/usr/local/ctdb-deps make -j$(nproc) make install装完后确认一下ls /usr/local/ctdb-deps/lib应该能看到libtalloc.so、libtdb.so等。如果这里就报错多半是缺libbsd-devel或libcap-devel按提示补装即可。3.2 CTDB 的 configure 参数逐个拆解CTDB 源码在 Samba 包的ctdb子目录。configure 时我用的参数如下cd /opt/src/samba-4.10.x/ctdb ./configure \ --prefix/usr/local/ctdb \ --localstatedir/var \ --sysconfdir/etc \ --with-libtdb/usr/local/ctdb-deps \ --with-libtalloc/usr/local/ctdb-deps \ --with-libtevent/usr/local/ctdb-deps \ --without-cluster-libdlm \ --disable-rpath逐个说--prefix安装根目录ctdbd会装到/usr/local/ctdb/sbin。--localstatedir/var让运行时数据默认落到/var/lib/ctdb和前面建的目录对上。--with-libtdb等强制用我们自编译的依赖避免系统库干扰。--without-cluster-libdlm除非你用 DLM 做集群锁否则关掉减少依赖。CTDB 自己有一套恢复锁机制普通场景不需要 DLM。--disable-rpath不把库路径硬编码进二进制方便以后迁移或换路径。configure 跑完会输出一份摘要重点看tdb、talloc、tevent那几行是不是指向/usr/local/ctdb-deps。如果显示no或者指向/usr说明路径没被识别后面编译必挂。3.3 编译与安装中的典型报错第一次make时我遇到两个报错比较有代表性报错一fatal error: tdb.h: No such file or directory。原因是 configure 虽然找到了 tdb 库但头文件路径没加进CFLAGS。解决办法是手动补export CFLAGS-I/usr/local/ctdb-deps/include export LDFLAGS-L/usr/local/ctdb-deps/lib然后重新 configure 和 make。报错二链接阶段undefined reference to tevent_...。这是 tevent 没编或没指对。tevent 在 Samba 源码的lib/tevent下按 3.1 的方式编一遍再在 configure 里加--with-libtevent即可。编译通过后make install装完检查/usr/local/ctdb/sbin/ctdbd --version /usr/local/ctdb/bin/ctdb --version能打印版本号就说明二进制没问题。3.4 让系统找到自编译的库因为--disable-rpath运行时得告诉系统去哪找.soecho /usr/local/ctdb-deps/lib /etc/ld.so.conf.d/ctdb-deps.conf ldconfig这一步不做ctdbd启动时会报error while loading shared libraries: libtdb.so.1。很多人编译顺利、一启动就崩八成是这里漏了。4. 单节点跑通 CTDB配置与首次启动4.1 最小配置文件长什么样CTDB 的主配置是/etc/ctdb/ctdb.conf老版本可能是nodes和public_addresses分开。我建议先用单节点模式验证配置文件尽量精简[cluster] transport tcp node address 192.168.10.11 recovery lock /var/lib/ctdb/recovery.lock database directory /var/lib/ctdb log level NOTICEtransport tcp表示节点间用 TCP 通信适合大多数环境。node address是本节点 IP必须和实际网卡地址一致写错了集群起不来。recovery lock是恢复锁文件单节点时它主要起保护作用多节点时它决定谁能发起恢复。4.2 启动 ctdbd 并观察日志启动命令/usr/local/ctdb/sbin/ctdbd --configfile/etc/ctdb/ctdb.conf --logfile/var/log/ctdb/ctdbd.log第一次启动建议前台加--interactive或-i这样日志直接打屏方便看/usr/local/ctdb/sbin/ctdbd -i --configfile/etc/ctdb/ctdb.conf正常的话你会看到类似Starting CTDB... Node 192.168.10.11 is now the leader Recovery mode started Recovery mode ended如果卡在Recovery mode started不动通常是恢复锁文件权限不对或者数据库目录不可写。检查/var/lib/ctdb的属主是不是 ctdb。4.3 用 ctdb 命令行验证状态另开一个终端export PATH/usr/local/ctdb/bin:$PATH ctdb status ctdb ip ctdb nodesctdb status会列出各节点的状态单节点时应该显示OK。ctdb ip显示当前节点接管的 IP单节点时可能为空因为还没配 public address。ctdb nodes列出节点列表。这里有个细节ctdb命令行默认连本机 4379 端口如果ctdbd没起来或者防火墙挡了会报Failed to connect to daemon。CentOS 7 默认开着 firewalld记得放行firewall-cmd --permanent --add-port4379/tcp firewall-cmd --reload4.4 单节点阶段最容易忽略的权限问题我踩过的一个坑ctdbd以 root 启动但内部会降权到 ctdb 用户去写数据库。如果/var/lib/ctdb是 root 属主启动时看似正常一旦触发恢复就报Permission denied。所以前面建目录时chown -R ctdb:ctdb不能省。另外recovery.lock文件如果预先创建了也要保证 ctdb 可写否则恢复锁拿不到集群会一直卡在恢复模式。5. 扩展到多节点节点列表与 IP 接管5.1 节点列表的正确写法多节点时ctdb.conf里要加节点列表。老版本用单独的nodes文件每行一个 IP192.168.10.11 192.168.10.12 192.168.10.13新版本可以在ctdb.conf里用[cluster]段的node address配合nodes文件。关键是所有节点的 nodes 文件内容必须完全一致顺序也要一致。CTDB 用节点在列表中的位置来分配节点号node number顺序不同会导致节点号错乱恢复时互相不认。5.2 public address 与 IP 漂移CTDB 的高可用核心是 IP 漂移每个节点声明自己可以接管的“公共 IP”当某节点故障其他节点把这些 IP 接过去。配置在public_addresses文件192.168.10.101/24 eth0 192.168.10.102/24 eth0格式是IP/掩码 网卡。这些 IP 必须是客户端访问共享用的地址不能和节点管理 IP 混用。配置好后重启 ctdb用ctdb ip就能看到 IP 分配情况192.168.10.101 0 OK 192.168.10.102 1 OK后面的数字是当前持有该 IP 的节点号。手动触发漂移ctdb ipreallocate或者模拟节点故障ctdb disable ctdb ipreallocatedisable会把本节点标记为不可用它持有的 IP 会被其他节点接管。这个命令在维护时非常有用比直接关机优雅得多。5.3 节点间通信的防火墙与时间同步多节点最容易翻车的地方是通信。CTDB 节点间除了 4379还会用一批动态端口做恢复和数据库同步。最省事的做法是在集群内网放开所有端口或者至少放行 4379 和 9000-9999 范围。另外节点间时间必须同步否则恢复锁和选举逻辑会出问题。CentOS 7 上配好 chronydsystemctl enable chronyd systemctl start chronyd chronyc sources时间偏差超过几秒集群就可能反复进入恢复模式日志里刷Recovery mode started却永远不结束。6. 踩坑复盘那些文档不会告诉你的细节6.1 恢复锁文件放在共享存储上的必要性单节点时恢复锁放本地没问题但多节点时如果每个节点用自己的本地锁文件会出现“脑裂”两个节点同时认为自己该发起恢复数据就乱了。正确做法是把恢复锁放在所有节点都能访问的共享存储上比如 NFS 或共享块设备这样同一时刻只有一个节点能拿到锁。如果暂时没有共享存储至少保证锁文件路径在所有节点上指向同一个网络位置而不是各自的/var/lib/ctdb。6.2 数据库目录不要放在 NFS 上和恢复锁相反CTDB 的数据库目录/var/lib/ctdb必须放在本地磁盘。TDB 对文件锁和 mmap 的要求很高NFS 上的锁语义不可靠放上去会导致数据库损坏。我见过有人图省事把整个/var/lib/ctdb挂到 NFS结果集群跑几小时就崩一次日志里全是tdb相关错误。记住恢复锁可以共享数据库必须本地。6.3 日志级别调太高反而看不清问题log level DEBUG会打出海量日志真正关键的恢复事件被淹没。我一般先用NOTICE出问题时临时调到INFO定位到具体模块后再针对该模块开DEBUG。CTDB 支持按模块设级别比如log level NOTICE:RECOVERDEBUG这样只放大恢复相关日志效率高很多。6.4 节点名解析失败导致的启动挂起CTDB 启动时会解析 nodes 文件里的 IP如果某个 IP 反解不到主机名或者/etc/hosts里没有对应条目启动会卡住等 DNS 超时。解决办法是在所有节点的/etc/hosts里把集群 IP 和主机名写死不依赖 DNS。这个坑很隐蔽因为日志里只显示Waiting for ...不报错新手容易以为是网络慢。7. 和 Samba 联动时的版本对齐与验证7.1 CTDB 与 Samba 必须同源编译CTDB 最终是给 Samba 用的两者必须来自同一份源码、同一版本。如果你用源码编了 CTDB 4.10却用 yum 装了 Samba 4.9那么 Samba 调 CTDB 的接口会对不上表现为smbd启动时报ctdb相关符号缺失或者共享目录能挂载但锁完全失效。正确做法是Samba 也用同一份源码编译configure 时加--with-ctdb/usr/local/ctdb让 Samba 链接到我们编的 CTDB。7.2 验证集群共享是否真正生效配好 Samba 后从客户端挂载共享然后在两个节点上分别操作同一个文件观察锁是否互斥。更直接的验证是用ctdb命令看数据库状态ctdb getdbmap ctdb catdb locking.tdbgetdbmap列出当前集群里所有 TDB 数据库catdb能 dump 某个库的内容。如果locking.tdb里能看到你刚操作的文件记录说明 CTDB 正在正常协调锁。这一步是判断“集群到底有没有在工作”的硬指标比看日志靠谱。7.3 故障切换的实测方法真正验证高可用得模拟节点宕机。最温和的方式是ctdb disable加ctdb ipreallocate观察 IP 是否漂到其他节点、客户端是否短暂中断后恢复。更彻底的是直接poweroff一个节点然后在剩余节点上ctdb status看故障节点是否被标记为DISCONNECTED以及它的 IP 是否被接管。实测下来TCP 传输模式下切换通常在几秒内完成客户端如果配了重试基本无感。8. 我个人在实际操作中的几点体会源码编译 CTDB 这件事难点从来不在编译本身而在版本组合和权限细节。我前后在几套环境里做过总结下来最值得记住的三条第一TDB、Talloc、Tevent 一定用源码内自带的别偷懒用系统包省下的几分钟会变成后面几小时的排查第二数据库目录必须本地、恢复锁尽量共享这两个路径搞反了集群迟早出问题第三节点列表在所有机器上必须一字不差包括顺序这是很多“莫名其妙起不来”的根源。另外CentOS 7 虽然老但它的稳定性和广泛的硬件兼容性让它在很多存量环境里还得继续跑。源码编译给了我们在这类环境里用上新版 CTDB 的可能代价就是要多花点心思在依赖和路径上。如果你也在维护类似的老环境希望这篇内容能帮你少走几个我走过的弯路。后续如果要做更复杂的多网段接管或者和 GlusterFS 联动那又是另一个话题了有机会再展开聊。