1. 为什么在 FreeBSD 上部署 Buildbot 不是“装个包就完事”——从系统哲学说起Buildbot 是一个老牌但极其扎实的持续集成与构建调度系统它的核心价值不在于界面多炫而在于对构建流程的原子级控制能力、对异构环境的天然兼容性以及极低的资源开销。而 FreeBSD —— 这个以“正确性、稳定性、网络栈完备性”为基因的操作系统恰恰是运行 Buildbot 最值得被重新重视的平台之一。但现实是绝大多数 Buildbot 教程都默认你用的是 Ubuntu 或 CentOS直接apt install buildbot或dnf install buildbot就能跑起来。FreeBSD 没有这种“一键幻觉”。它要求你理解Buildbot 不是一个黑盒服务而是一组可拆解、可隔离、可审计的 Python 进程FreeBSD 也不是 Linux 的复刻版它的 jail 机制、IPFW 防火墙、NAT 策略、服务管理rc.d vs systemd全都有自己的逻辑闭环。我第一次在 FreeBSD 13 上部署 Buildbot 时卡在了整整三天。不是因为不会写配置而是因为没想明白一个问题Buildbot 的 master 和 worker 之间到底需要哪几层网络通路而 FreeBSD 的网络模型又该在哪一层介入比如worker 要连 master 的 8010 端口Web UI这是 HTTP 流量要连 9989 端口PB 协议这是二进制 RPC 流量如果 worker 在 jail 里master 在宿主机上那这个连接是走 loopback 还是走虚拟网卡如果 worker 在另一台物理机上中间隔着一台 FreeBSD 路由器那 NAT 规则该加在 master 侧还是 router 侧更关键的是Buildbot 的 Web UI 默认绑定127.0.0.1这在 jail 场景下意味着什么—— 它意味着你根本看不到页面除非你明确告诉它监听0.0.0.0并且 IPFW 允许外部访问。这就是为什么“How To Set Up Buildbot on FreeBSD”这个标题背后藏着的不是安装步骤而是一套完整的系统工程思维。它要求你同时懂三件事Buildbot 的进程模型与通信协议、FreeBSD 的网络栈分层从 ifconfig 到 ipfw 到 natd、以及 jail 的资源边界与网络虚拟化原理。这三者一旦错配就会出现“服务明明在跑但页面打不开”“worker 显示 online但 build 一直 pending”“日志里全是 connection refused”这类典型症状。而这些症状在 Linux 教程里几乎不会出现因为 Linux 的默认网络模型更“宽松”也更“不透明”。所以这篇内容不是教你怎么敲几行命令而是带你重建一套认知框架把 Buildbot 当作一个需要被 FreeBSD “收编”的外来服务而不是一个可以随意塞进系统的插件。你会看到每一个配置项的选择背后都是 FreeBSD 哲学的一次落地——比如我们不用pkg install buildbot而是用pip install --user因为 FreeBSD 的 pkg 仓库里 Buildbot 版本滞后严重且不提供buildbot-worker的独立包我们不直接在 root 下跑 master而是创建专用用户buildbot因为 FreeBSD 的权限模型强调最小特权原则我们不依赖systemd而是手写rc.d脚本因为 FreeBSD 的服务生命周期管理必须和rcorder严格对齐。这些选择没有一个是“为了不同而不同”每一个都对应着一个真实踩过的坑一次服务崩溃后的回溯或一次安全审计时的红牌警告。提示如果你正准备在生产环境部署 CI/CD且底层是 FreeBSD无论是物理机、VM 还是云实例请务必把本文当作一份“系统级部署说明书”而非“快速入门指南”。跳过任何一节都可能在未来某次系统升级后让你花一整天去排查一个本可避免的连接超时。2. 从零构建 Buildbot 运行时环境为什么 pip virtualenv 是唯一可靠路径FreeBSD 的官方软件包仓库pkg对 Python 生态的支持长期处于一种“可用但不可靠”的状态。Buildbot 尤其典型pkg search buildbot可能返回py39-buildbot-3.10.1但当你pkg install py39-buildbot后执行buildbot --version大概率会报错ImportError: cannot import name twisted。原因很简单——pkg 里的 Buildbot 是静态打包的它依赖的 Twisted、Automat、Jinja2 等库版本锁死在打包那一刻。而 Buildbot 的 master 和 worker 是两个独立的 Python 包它们对依赖的版本要求并不完全一致。pkg install py39-buildbot只装了 masterpy39-buildbot-worker可能根本不存在或者版本不匹配。更麻烦的是FreeBSD 的pkg默认不处理 Python 的site-packages冲突一旦你后续用pip升级了某个依赖整个 Buildbot 就可能瘫痪。我试过三种方案方案一纯 pkg—— 失败。buildbot-worker create-worker命令缺失buildbot start报 Twisted 版本冲突。方案二pkg pip 混合—— 更糟。pkg install py39-twisted后再pip install buildbot结果 pip 把 pkg 装的 twisted 覆盖了导致其他 pkg 软件如py39-matrix-synapse启动失败。方案三完全脱离 pkg用 pip virtualenv—— 成功且稳定至今。这就是为什么本文所有操作都基于pip和virtualenv。这不是“折腾”而是 FreeBSD 下 Python 应用部署的黄金标准。它确保了 Buildbot 的运行时环境完全隔离、完全可控、完全可复现。2.1 创建专用用户与目录结构FreeBSD 的安全模型要求服务进程不能以 root 身份运行。我们创建一个名为buildbot的系统用户它没有登录 shell主目录设为/usr/local/buildbot# 创建用户禁用 shell设置主目录 sudo pw useradd buildbot -c Buildbot Service User -d /usr/local/buildbot -s /usr/sbin/nologin -m # 设置目录权限确保 buildbot 用户完全拥有 sudo chown -R buildbot:buildbot /usr/local/buildbot这个目录结构不是随便选的。/usr/local/是 FreeBSD 的“第三方软件”标准位置符合 FHS文件系统层次结构标准。/usr/local/buildbot下我们将建立清晰的子目录/usr/local/buildbot/ ├── master/ # master 配置与运行目录 ├── workers/ # 所有 worker 的根目录每个 worker 一个子目录 ├── venv/ # Python 虚拟环境统一管理所有依赖 └── logs/ # 全局日志便于集中轮转注意不要把 master 和 worker 放在同一个虚拟环境中。Buildbot 的 master 和 worker 虽然同源但它们的依赖树存在细微差异。例如worker 需要buildbot-worker包而 master 需要buildbot包。混在一起会导致pip list输出混乱升级时极易出错。我们采用“一个虚拟环境多个入口脚本”的方式既共享底层 Python 解释器又保持逻辑隔离。2.2 构建并激活虚拟环境FreeBSD 13 默认自带 Python 3.9但 Buildbot 3.10 推荐使用 Python 3.11。我们先确认系统 Python 版本python3 --version # 输出应为 Python 3.11.x如果版本不符用pkg install python311安装并通过ln -sf /usr/local/bin/python3.11 /usr/local/bin/python3切换默认。然后创建虚拟环境# 切换到 buildbot 用户环境 sudo -u buildbot -i # 进入主目录 cd /usr/local/buildbot # 创建虚拟环境注意使用 --system-site-packages 是错误的 python3 -m venv venv # 激活虚拟环境 source venv/bin/activate此时你的命令行提示符前会加上(venv)表示已进入隔离环境。接下来安装 Buildbot 核心组件# 升级 pip 到最新版FreeBSD 自带的 pip 版本太旧 pip install --upgrade pip # 安装 master注意buildbot 包本身只包含 master pip install buildbot3.10.1 # 安装 worker单独安装版本必须与 master 严格一致 pip install buildbot-worker3.10.1 # 验证安装 buildbot --version # 应输出 3.10.1 buildbot-worker --version # 应输出 3.10.1关键经验buildbot-worker的版本号必须与buildbot完全一致。Buildbot 官方文档明确警告“master and worker must be the same version”。我曾因 worker 用 3.10.0、master 用 3.10.1导致 worker 连接后立即断开日志里只有Invalid protocol version这一行查了六小时才定位到版本不匹配。FreeBSD 的 pkg 无法保证这种精确同步而 pip 可以。2.3 初始化 master 并生成基础配置现在我们为 master 创建运行目录并生成骨架配置# 退出虚拟环境暂时 deactivate # 切换回 buildbot 用户创建 master 目录 sudo -u buildbot -i cd /usr/local/buildbot mkdir -p master # 初始化 master这会生成 master.cfg.sample buildbot create-master --force mastercreate-master命令会在master/目录下生成master.cfg.sample一个详尽的示例配置超过 1000 行覆盖了从认证、数据库、Web UI 到构建步骤的所有选项。public_html/存放自定义 Web UI 静态资源。twistd.pidmaster 进程的 PID 文件运行后生成。但master.cfg.sample太庞大不适合直接修改。我们创建一个极简但可运行的master.cfg# /usr/local/buildbot/master/master.cfg from buildbot.plugins import * # 1. 数据库配置 c[db] { db_url: sqlite:///state.sqlite, } # 2. Web UI 配置 c[www] { port: 8010, plugins: {console_view: {}, grid_view: {}}, auth: util.InMemoryAuth({admin: password}), # 仅用于测试 avatar_methods: [util.AvatarGravatar()], } # 3. 工人配置 c[workers] [worker.Worker(example-worker, pass)] # 4. Builder 配置 c[builders] [ util.BuilderConfig( namerun-tests, workernames[example-worker], factoryfactory.BuildFactory([ steps.ShellCommand(command[echo, Hello from FreeBSD!]), ]) ) ] # 5. 调度器配置 c[schedulers] [ schedulers.SingleBranchScheduler( nameall, change_filterutil.ChangeFilter(branchmain), treeStableTimer5, builderNames[run-tests] ) ]这个配置做了四件关键事使用 SQLite 作为数据库轻量、免运维适合起步Web UI 绑定到8010端口并启用基础认证用户名admin密码password定义了一个名为example-worker的 worker密码为pass创建了一个最简单的 Builder只执行一条echo命令。实操心得c[www][port]默认是8010但它绑定的是127.0.0.1。如果你希望从其他机器访问 Web UI必须显式设置c[www][address] 0.0.0.0:8010。否则即使 IPFW 放行了 8010你也只能在本机 curl 访问。这个细节在几乎所有教程里都被忽略但在 FreeBSD jail 或远程开发场景下是第一个拦路虎。3. 网络策略的终极战场IPFW NAT 如何精准护航 Buildbot 流量Buildbot 的网络模型看似简单master 监听一个端口8010 Web UIworker 主动连接 master 的另一个端口9989 PB 协议。但当你的部署涉及 FreeBSD 的 jail、NAT 或跨网段 worker 时这个模型立刻变得立体而复杂。FreeBSD 的 IPFWIP Firewall和natdNAT daemon不是可有可无的附加组件而是构建可信、可控、可审计的 Buildbot 网络的基石。它们的作用层级远高于 Linux 的 iptables —— IPFW 工作在内核网络栈的最底层ip_input和ip_output钩子能精确控制每一个数据包的流向而natd则是 FreeBSD 原生的、高度优化的用户态 NAT 实现与 IPFW 深度集成。我们面对的真实场景有三类场景 Amaster 在宿主机worker 在 jail 内→ 需要 jail 能访问宿主机的 9989 端口且 Web UI 能从外部访问 8010场景 Bmaster 在 jail 内worker 在另一台物理机→ 需要宿主机做 DNAT将外部 8010 请求转发到 jail 的 8010场景 Cmaster 在宿主机worker 在另一台公网机器中间有 FreeBSD 路由器→ 需要在路由器上配置 SNAT/DNAT确保 worker 能连通 master 的 9989。本文以场景 A为基准最常见、最具教学价值因为它同时涵盖了 loopback、jail 网络、IPFW 规则、端口暴露等全部要素。3.1 jail 网络模型与 Buildbot 的适配FreeBSD jail 的网络默认是vnet模式需要内核支持或shared模式。vnet模式为每个 jail 提供独立的网络栈更安全但配置复杂shared模式则让 jail 共享宿主机的网络栈通过ip4.addr参数分配一个别名 IP。对于 Buildbot我们推荐shared模式原因有二worker 连接 master 的 9989 端口本质上是同一台机器上的进程间通信走127.0.0.1效率最高shared模式下jail 内的 worker 可以直接telnet 127.0.0.1 9989测试连通性无需额外路由配置。创建一个用于 worker 的 jail# 创建 jail 配置文件 sudo tee /etc/jail.conf EOF worker-jail { host.hostname worker-jail.local; path /usr/jails/worker-jail; interface lo1; # 使用 lo1 作为 jail 的虚拟接口 ip4.addr 127.0.1.100; # 分配一个 127.0.0.0/8 网段的地址 exec.start /bin/sh /etc/rc; exec.stop /bin/sh /etc/rc.shutdown; mount.devfs; } EOF # 创建 jail 根文件系统 sudo mkdir -p /usr/jails/worker-jail sudo freebsd-install -f /usr/jails/worker-jail -r 13.2-RELEASE # 启动 jail sudo jail -c worker-jail关键点在于ip4.addr 127.0.1.100。这个地址属于127.0.0.0/8网段与127.0.0.1是同一网络因此 jail 内的 worker 可以直接用127.0.0.1:9989连接宿主机上的 master无需经过任何 NAT 或路由。这是 FreeBSD jail 的一个精妙设计它利用了 loopback 网络的“本地性”既实现了进程隔离又保留了最高的通信效率。3.2 IPFW 规则从“允许一切”到“精确放行”FreeBSD 默认不启用防火墙。我们必须手动加载 IPFW 并编写规则。规则顺序至关重要IPFW 是按序匹配第一条匹配即生效。我们的目标是允许所有 loopback 流量127.0.0.0/8允许 jail127.0.1.100访问宿主机的 9989 端口允许外部网络访问宿主机的 8010 端口Web UI拒绝所有其他入站连接。创建/etc/ipfw.rules#!/bin/sh # Flush all existing rules ipfw -q flush # Allow all loopback traffic (critical for jail communication) ipfw add 100 allow all from 127.0.0.0/8 to 127.0.0.0/8 # Allow jail (127.0.1.100) to access masters PB port (9989) ipfw add 200 allow tcp from 127.0.1.100 to me 9989 # Allow external access to Web UI (8010) ipfw add 300 allow tcp from any to me 8010 # Deny all other incoming TCP connections ipfw add 400 deny tcp from any to me # Allow all outgoing connections (for master to fetch code, etc.) ipfw add 500 allow ip from me to any # Default deny rule (catch-all) ipfw add 600 deny ip from any to any然后启用 IPFW# 加载规则 sudo ipfw -q -f /etc/ipfw.rules # 设置开机启动 echo firewall_enableYES | sudo tee -a /etc/rc.conf echo firewall_script/etc/ipfw.rules | sudo tee -a /etc/rc.conf重要提醒规则200 allow tcp from 127.0.1.100 to me 9989是 jail 场景下的生命线。如果这条规则缺失worker 会显示Connection refused因为 IPFW 在 kernel 层就丢弃了这个包。而100 allow all from 127.0.0.0/8 to 127.0.0.0/8则是基础保障确保127.0.0.1和127.0.1.100之间的通信畅通无阻。这两条规则构成了 Buildbot 在 FreeBSD jail 中运行的网络基石。3.3 NAT 的角色何时需要何时不需要从上面的分析可以看出在场景 Amaster 宿主机 worker jail中我们根本不需要 NAT。因为127.0.1.100和127.0.0.1是同一网络流量不经过任何网卡自然也不经过natd。NAT 是为了解决“私有 IP 无法直接路由到公网”的问题而127.0.0.0/8是专门为此设计的“本地环回网络”它天生就是不可路由的也无需 NAT。那么NAT 在 Buildbot 部署中何时真正必要答案是当 worker 位于一个与 master 不同的、需要地址转换的网络时。例如worker 在一个192.168.10.0/24的内部网络master 在10.0.0.1两者之间有一台 FreeBSD 路由器worker 在云服务商的 VPC 内如172.31.0.0/16master 在你的数据中心中间通过 IPsec 隧道连接。此时你需要在 FreeBSD 路由器上配置natd。一个典型的natd配置/etc/natd.conf如下# 将来自 192.168.10.0/24 的流量SNAT 为路由器的公网 IP interface em0 dynamic yes redirect_port tcp 10.0.0.100:9989 192.168.10.50:9989 use_sockets yes其中redirect_port是关键它告诉natd“当外部连接em0接口的某个端口时请把流量转发给192.168.10.50:9989”。这解决了 worker 主动连接 master 的问题。但请注意natd只处理“出站”流量的地址转换SNAT而redirect_port是一种特殊的 DNAT端口转发它需要与 IPFW 的forward规则配合才能生效。实战教训我曾在一个客户现场把natd配置在了 master 本机上而不是中间的路由器上。结果是worker 能连上 master但 master 无法反向连接 worker 的 SSH用于代码检出因为natd的 SNAT 只改了源 IP没改目的 IP。正确的做法是在 worker 所在网络的出口网关即 FreeBSD 路由器上配置natd并对10.0.0.100:9989做 DNAT。这个教训让我彻底理解了 NAT 的“方向性”——它不是一个全局开关而是一个有明确上下文的流量重写引擎。4. 服务化与自动化手写 rc.d 脚本让 Buildbot 真正融入 FreeBSD 生态在 Linux 世界systemd是事实标准systemctl enable buildbot-master一行搞定。FreeBSD 没有systemd它有自己的服务管理框架rc.d。rc.d的核心思想是“脚本即服务”每个服务都对应一个/usr/local/etc/rc.d/下的 shell 脚本该脚本定义了start,stop,restart,status等命令。它不追求花哨的功能只求稳定、可预测、可审计。对于 Buildbot 这种需要长期运行、对启动顺序有要求比如必须在数据库服务之后启动的服务rc.d是比任何第三方进程管理器supervisord, circus都更原生、更可靠的选择。4.1 master 的 rc.d 脚本不只是启动更是生命周期管理创建/usr/local/etc/rc.d/buildbot-master#!/bin/sh # # PROVIDE: buildbot-master # REQUIRE: DAEMON local_unbound # 依赖 DNS 服务可选 # KEYWORD: shutdown # . /etc/rc.subr namebuildbot-master rcvarbuildbot_master_enable # 默认配置 load_rc_config $name : ${buildbot_master_enable:NO} : ${buildbot_master_user:buildbot} : ${buildbot_master_dir:/usr/local/buildbot/master} : ${buildbot_master_venv:/usr/local/buildbot/venv} # 构建启动命令 command/usr/sbin/daemon command_args-f -T buildbot_master -u ${buildbot_master_user} \ ${buildbot_master_venv}/bin/buildbot start --nodaemon ${buildbot_master_dir} # 状态检查检查 twistd.pid 是否存在且进程在运行 pidfile${buildbot_master_dir}/twistd.pid start_cmd${command} ${command_args} stop_cmd${buildbot_master_venv}/bin/buildbot stop ${buildbot_master_dir} run_rc_command $1这个脚本的关键点在于REQUIRE: DAEMON表明它依赖于 FreeBSD 的基础守护进程框架command/usr/sbin/daemon是 FreeBSD 的标准守护进程包装器它确保 Buildbot 进程在后台正确运行并能被rc.d正确追踪command_args中的--nodaemon是必须的因为daemon已经负责了守护化Buildbot 自身不能再 forkpidfile指向 Buildbot 自动生成的twistd.pidrc.d通过读取这个文件来判断服务是否在运行。启用服务# 添加到 rc.conf echo buildbot_master_enableYES | sudo tee -a /etc/rc.conf # 立即启动 sudo service buildbot-master start # 查看状态 sudo service buildbot-master status4.2 worker 的 rc.d 脚本如何优雅地管理多个 worker一个 Buildbot master 通常连接多个 workerLinux worker、FreeBSD worker、Windows worker。在 FreeBSD 上我们倾向于为每个 worker 创建一个独立的 jail并为每个 jail 编写一个对应的 rc.d 脚本。这样做的好处是启动/停止互不影响日志完全隔离资源CPU、内存可以通过 jail 的cpuset和memoryuse参数精确限制安全边界清晰一个 worker 被攻破不会影响其他 worker。为worker-jail创建/usr/local/etc/rc.d/worker-jail#!/bin/sh # # PROVIDE: worker-jail # REQUIRE: buildbot-master # KEYWORD: shutdown # . /etc/rc.subr nameworker-jail rcvarworker_jail_enable load_rc_config $name : ${worker_jail_enable:NO} : ${worker_jail_name:worker-jail} # jail 命令 jail_cmd/usr/sbin/jail jail_flags-c start_cmdjail -c ${worker_jail_name} stop_cmdjail -r ${worker_jail_name} # 状态检查检查 jail 是否在运行 status_cmdjls | grep -q ^${worker_jail_name} run_rc_command $1注意REQUIRE: buildbot-master。这告诉rcorderworker-jail必须在buildbot-master之后启动。因为 worker 需要 master 先监听好 9989 端口才能成功连接。rcorder会自动解析这个依赖关系并在service -r时按正确顺序执行。4.3 日志轮转与监控让运维不再“盲人摸象”Buildbot 的日志分散在多个地方master 的twistd.log、每个 worker 的twistd.log、以及 Web UI 的访问日志如果启用了c[www][logfileName]。FreeBSD 的标准日志轮转工具是newsyslog。我们为 master 日志配置轮转创建/usr/local/etc/newsyslog.conf.d/buildbot.conf# logfilename [owner:group] mode count size when flags [/pid_file] [sig_num] /usr/local/buildbot/master/twistd.log buildbot:buildbot 644 7 * T00 JN /usr/local/buildbot/master/twistd.pid /usr/local/buildbot/workers/*/twistd.log buildbot:buildbot 644 7 * T00 JN7表示保留 7 个历史日志文件*表示当日志大小超过 0 字节即只要有新日志就轮转T00表示每天凌晨 0 点轮转J表示使用 bzip2 压缩N表示轮转后不创建新文件由 Buildbot 自己创建/usr/local/buildbot/master/twistd.pid是发送 HUP 信号的目标通知 Buildbot 重新打开日志文件。经验之谈newsyslog的N标志是关键。如果不加Nnewsyslog会创建一个空的twistd.log而 Buildbot 进程仍在向旧的 inode 写入导致磁盘空间不释放。加了N后newsyslog只重命名文件然后发 HUPBuildbot 收到信号后会自己打开一个新的twistd.log。这是 FreeBSD 日志管理的精髓工具只做它该做的事进程自己管理自己的状态。5. 从“能跑”到“稳跑”生产环境的加固清单与排错链路部署完成buildbot start成功Web UI 能打开worker 显示 online第一个 build 也成功了……这仅仅是万里长征第一步。真正的挑战在于如何让这套系统在无人值守的情况下稳定运行数月甚至数年这需要一套覆盖配置、监控、备份、升级的完整加固清单。而当问题真的发生时它一定会发生你需要的不是 Google而是一条清晰、可复现的排错链路。5.1 生产环境加固清单Checklist类别项目说明检查命令配置安全master.cfg权限必须为600属主buildbotls -l /usr/local/buildbot/master/master.cfg配置安全Web UI 认证禁用InMemoryAuth改用HTPasswdAuth或 LDAPgrep auth /usr/local/buildbot/master/master.cfg配置安全数据库SQLite 不适用于高并发必须切换为 PostgreSQLgrep db_url /usr/local/buildbot/master/master.cfg服务健壮性自动重启rc.d脚本需添加procname和pidfile确保service restart可靠ps aux | grep buildbot服务健壮性资源限制为buildbot用户设置login.conf限制 CPU 和内存limits -U buildbot网络健壮性IPFW 持久化确保/etc/ipfw.rules在 reboot 后仍生效ipfw list | head -5数据可靠性数据库备份每日自动备份state.sqlite或 PostgreSQL dumpfind /var/backups -name buildbot-*.sql.gz数据可靠性配置版本化master.cfg必须纳入 Git每次修改都有 commit messagecd /usr/local/buildbot/master git log -1这份清单不是一次性的而是一个持续的过程。例如“数据库切换为 PostgreSQL”这一项其背后是一整套迁移流程安装postgresql15-server初始化数据库集群创建buildbot用户和数据库修改master.cfg的db_url导出 SQLite 数据用buildbot export再导入到 PostgreSQL用buildbot import。每一步都有其技术细节和潜在陷阱但它们共同指向一个目标让 Buildbot 从一个“玩具”变成一个真正可信赖的生产级基础设施。5.2 典型故障的完整排错链路当一个 build 卡在pending状态worker 显示offline而你ping得通、telnet也通时该怎么办以下是我在 FreeBSD 上处理此类问题的标准链路Step 1确认 master 进程状态# 检查进程是否存在 ps aux | grep buildbot | grep -v grep # 检查 PID 文件是否有效 cat /usr/local/buildbot/master/twistd.pid # 检查日志末尾是否有异常 tail -20 /usr/local/buildbot/master/twistd.log如果ps没有输出说明 master 没在运行直接service buildbot-master start如果有输出但日志里有OSError: [Errno 48] Address already in use说明端口被占lsof -i :9989找出并 kill 进程。Step 2确认 worker 进程状态在 jail 内# 进入 jail sudo jexec worker-jail sh # 检查 worker 进程 ps aux | grep buildbot-worker # 检查 worker 日志 tail -20 twistd.log如果 worker 进程不存在检查/usr/local/buildbot/workers/worker-jail/twistd.log常见错误是Authentication failed这意味着master.cfg里的 worker 名称或密码与buildbot-worker命令中的不一致。Step 3网络连通性深度验证# 在 jail 内测试到 master 的 9989 端口 telnet 127.0.0.1 9989 # 如果不通检查 IPFW 规则 ipfw list | grep 9989 # 如果规则存在检查是否被更早的规则如 deny拦截 ipfw list | head -10telnet是最原始、最可靠的