> For the complete documentation index, see [llms.txt](https://freebsd-journal-cn.bsdcn.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://freebsd-journal-cn.bsdcn.org/20170102-quan-qiu-ke-fang-wen-de-freebsd/flightaware-and-freebsd.md).

# FlightAware 与 FreeBSD

作者：Sean Kelly

## FlightAware 与 FreeBSD

它是最棒的——大多数情况下。

如果你还没听说过我们，FlightAware 是全球最大的航班追踪数据公司，为超过 10,000 家航空运营商和服务公司提供全球航班追踪解决方案。我们通过网站和移动应用向公众提供免费的航班追踪服务，但同时也面向全球航空运营商提供其他商业产品和服务。我们之所以能实现这一切，靠的是聚合来自自有地面站、覆盖 55 个以上国家的政府数据源，以及其他卫星数据提供商的数据。然后，我们接收所有这些数据，对其解读，把它们转换为对正在发生情况的更全局视图。可以想见，这需要相当数量的处理与存储能力。

在 FlightAware，我们的业务至今都建立在 FreeBSD 之上，我们是 FreeBSD 的坚定拥趸。我们有四名员工是 FreeBSD 项目的非活跃成员，包括我们的 CEO 和 CTO。直到最近，FreeBSD 几乎完全支撑着我们的网站和服务，仅有少数例外。但这种情况开始改变，我愿意解释一下原因。不过在此之前，先回顾我们与 FreeBSD 的渊源，以及我们如何使用它。

我们的 CEO 曾经运营 ftp2.freebsd.org，并向 Ports 树贡献过内容。他从 2.0-ALPHA 时代就开始使用 FreeBSD，至今仍持续在工作和个人生活中使用。我们的 CTO 资历更深，可以追溯到 386BSD 的起点，当时他维护补丁包，后来为 T1 驱动贡献了回环检测。再往前追溯，我们的一位开发者大量参与过 2BSD 和 4.1BSD 的开发，后来又协助了《FreeBSD Handbook》的编写。最后，我从 4.0 发布时开始使用 FreeBSD，贡献过若干补丁，以及软件看门狗子系统的最初实现。

除 FreeBSD 外，我们大量使用 Tcl 编程语言。这包括我们的网站，它用 Tcl 编写，由 Apache 的 mod\_rivet 扩展提供服务。我们 Tcl 包的若干 Port 位于 Ports 树中，包括 speedtables、casstcl、yajl-tcl、tcllauncher、tclreadline 和 tclbsd。这些 Port 由 tcltk@ 和 Pietro Cerruti 维护。Pietro 虽不是 FlightAware 员工，但他慷慨地同步更新 Port 以配合我们的软件发布。我们之所以使用 FreeBSD，部分原因包括网络栈的稳定性与健壮性、社区的凝聚力、从内核到用户态对系统的整体把控，以及 ZFS。这一切结合在一起，为我们的服务器和服务创造了一个最优且可信赖的开源环境。

## 我们如何使用 FreeBSD

像许多其他公司一样，我们在 FreeBSD 上运行 Web 服务器、Varnish 服务器、邮件服务器和 DNS 服务器。虽然这些并不算特别不寻常，但我们在 FreeBSD 平台上做的其他一些事情可能更让读者感兴趣：包括定制的 FreeBSD 安装器、高事务量的 RDBMS、几种使用共享内存的不同内存数据库，当然，所有这些都利用了出色的 ZFS 文件系统。

## 安装：一台服务器诞生了

虽然 bsdinstall 相比 sysinstall 已是巨大进步，但我们仍需要一种自动化程度更高的方案。我们几乎所有服务器都从相同的基础安装开始，因此反复向 bsdinstall 录入相同信息毫无必要。取而代之，我们开发了一套集成 bsdinstall 与 bsdconfig 的脚本。这些脚本在 mfsBSD PXE 环境中运行，执行以下操作：

* 销毁任何现有的硬件 RAID 配置与分区
* 清除磁盘上任何已有的 ZFS 标签
* 在所有内置磁盘上创建新的 zpool
* 创建所有支持启动环境的相应 ZFS 文件系统
* 拉取 FreeBSD 安装文件并解包
* 创建一个用户
* 配置所有网络参数

完成后，机器重启，我们会看到一个菜单，可从中选择要安装的组件。每个组件由 shell 脚本内的一个 shell 函数管理，便于管理与修改。需要新组件时，只需创建一个新函数。所有这些都是逐步演进而来的。理想情况下，我们希望有一种安装器能 PXE 启动，找到与 MAC 地址匹配的配置文件，并据此进行完全无人值守安装。如果这一能力内置在 FreeBSD 中就更好了——这样 FreeBSD 就会有一套标准化的零接触部署系统，每家用户都不必自行造轮子。

## 传统数据库：让它更快

虽然不如我们的定制安装器那般新颖，但 FreeBSD 在 FlightAware 的另一项用途是为 PostgreSQL 数据库集群提供动力。我们选择 FreeBSD，正是看中 ZFS 提供的数据完整性功能。另一项额外好处是 LZ4 压缩，其压缩比约为 2.15 倍，性能损耗微乎其微。集群本身是标准的 PostgreSQL 9.4 部署，使用原生流复制支持。我们使用 pgpool 来负载均衡只读事务，所有写事务则发往主节点。这让我们能将查询分散到更多服务器上，由于大多数事务都是只读的，从而提升了事务处理能力。2015 年 10 月，我们将 PostgreSQL 服务器从 24 块硬盘组成的 ZFS 镜像对替换为 4 块 NVMe SSD 组成的镜像对。在我们解决并最终禁用 TRIM 引发的问题之后，这是一次巨大的性能提升。当执行 `zpool create` 时，NVMe 子系统会超时，因为 ZFS 试图对整个池执行 TRIM，而 NVMe 等待 SSD 完成的耐心耗尽。在正常运行期间，对 Samsung SSD 执行 TRIM 也会引发问题，曾导致几次服务中断。PostgreSQL 清理 pg\_xlog 文件时会因 TRIM 短暂阻塞 I/O。后来我们换成了 Intel SSD，看起来不存在此问题，但我们仍让 TRIM 保持禁用状态。为支持这一切的备份，我们用 Tcl 开发了一套自定义工具集，用于管理 ZFS 快照。该工具在数据库服务器上创建快照，发送到另一台主机暂存，再从那里分发到各个站点。这些工具还管理快照的保留策略，保留一定数量的按小时、按天、按周快照。

如你所见，共享内存对我们是一项重要技术。我们在内部工具以及 PostgreSQL 等第三方项目中都大量使用。我们发现，SHM 的整体性能在过去几年有所提升，希望这种情况能够持续。我们注意到 FreeBSD 9 时有显著改进，FreeBSD 10 时又有小幅提升。

## 内存数据库：让快的更快

除 PostgreSQL 数据库外，我们还有名为 Birdseye 的内部软件。Birdseye 是用 Tcl 和 C 编写的内存数据库，基于我们开源的 speedtables 项目。Birdseye 使用共享内存存储我们所知的全部飞机约 24 小时的位置数据。单台机器上的多个 Birdseye 进程共享同一共享内存段，从而能同时服务更多客户端。客户端（例如网站）可以连接到这些 Birdseye 服务器，发起各种查询，从而非常快速地判断一架飞机正在做什么、做过什么，甚至在任意地理矩形框内正在发生什么。除 Birdseye 外，我们还有另一种内存数据库 Superbird。Superbird 同样基于 speedtables 项目，用 Tcl 和一些生成的 C 编写。Superbird 会定期读入 PostgreSQL 表中已发生的任何变更，并将其保留在内存中的 speedtables 数据库里。网站利用这一点，通过访问本地内存缓存副本，对繁忙表进行更快的查询，而不必访问远端 PostgreSQL 服务器。换言之，Superbird 是利用更新轮询的数据库缓存层。

## 痛点：并非一切完美

现在已介绍完 FreeBSD 在我们这里如何工作，若不讨论痛点就有失偏颇。FlightAware 正在迅速成长，我们需要以越来越快的速度部署新系统和服务。此外，数据增长和不断攀升的摄取速率，正迫使我们重新评估如何存储、处理和分析数据集。这时，我们选择 FreeBSD 越来越难以继续维持和说服自己。

## 安装：一支服务器大军

FreeBSD 的一大优点是，安装到系统上之后基本可以放心不管。你显然会想安装更新等等，但系统总体上极为稳定，几乎不需要持续维护和照料。结果就是我们倾向于把 FreeBSD 系统视为独特的个体，而非完全可抛弃的资源大军。我们会增删软件，但很少需要重新干净安装。问题在于，这种方式扩展性不佳。结果，我们开始把操作系统安装视为一种可商品化的事物。如前所述，我们已经构建了自己的 FreeBSD 安装器，主要满足我们应对这种变化的需要。它还不够好，我们想加入更多功能，比如让它能通过 TFTP 获取配置文件以实现完全无人值守安装。也许可以有一个基础配置文件，再加上基于 MAC 地址或 SMBIOS 系统序列号的覆盖文件。但与其由我们为自己的工具实现这一点，不如让这些功能直接进入 FreeBSD 安装器更为理想。FreeBSD 安装器应通过文档化的脚本功能支持完全无人值守安装。Linux 世界早已凭借 Red Hat kickstart 系统或 Debian 预配置文件征服了这一领域。安装器可由 PXE 启动，再用 DHCP 选项指引它从何处下载配置数据。这将让一次性部署或重新部署数百台服务器变得轻而易举。

## 容器与隔离：当 Jail 不够用时

我已经分享了关于 FreeBSD 安装过程可以如何改进的设想，接下来解释一下我们想用那些无人值守安装的服务器大军做什么。我们正迅速告别在标准系统安装上部署服务的做法。像许多云规模公司一样，我们正在采用容器化方案，让我们能在一台硬件上轻松部署大量服务，而不会引入传统方式带来的软件和库依赖问题。我们希望每一次基础安装都完全相同，仅作为承载应用容器的平台。这简化了安装和管理。作为通往这一目标的一条潜在路径，我们评估了 Jail，发现它们大体可用。但我们没有朝这个方向推进，因为我们遇到了几个问题。一个突出问题是缺少 per-jail 共享内存。FreeBSD Jail 的共享内存彼此之间以及与基本系统之间并未隔离。这种缺乏隔离意味着 PostgreSQL 和 Birdseye 等服务无法在相邻 Jail 中安全部署，除非特别小心地避免冲突或被利用。例如，在相邻 Jail 中运行 PostgreSQL 是有风险的，因为 pgsql 用户具有相同的 UID，每个 Jail 因此都能访问另一个 Jail 的 IPC 资源。尽管如此，将 RCTL 加入 GENERIC 似乎已带来很多改进，但我们也希望在这方面有更多投入。VIMAGE 也应稳定并加入。此外，我们希望对共享内存等内容引入更多命名空间，以实现真正的隔离。这是 Linux 领先的领域之一，由此催生了 Docker 和 Kubernetes 等技术的繁荣。容器机制本身固然重要，如何管理它同样重要。目前，FreeBSD 有许多处于不同支持和开发状态的 Jail 管理方式：ezjail、iocage、CBSD、**jail(8)** 的 **/etc/jail.conf** 以及许多自研工具和衍生品。虽然选择多通常是好事，但这种多样化的生态也使自动化和编排工具难以一致地支持 Jail。如果作为基本系统能将所有这些多样特性收归一处——一套 API 和命令一统天下——那将是再好不过。

## 64 位 Java：比之前好一倍

如前所述，我们的数据集在增长，摄取的数据量也在增长。这迫使我们转向 Cassandra、Kafka 和 Spark 等更新的技术，用于存储、移动和理解这一切。这些技术有一个共同点：它们都运行在 Java 运行时环境上。FreeBSD 上的 Java 选项不多。你可以构建并使用 OpenJDK 这一开源 Java 实现，它能编译为原生 64 位实现。也可以通过 FreeBSD Linux 模拟层使用 Oracle 的 32 位 Linux JRE。然而，直到最近才支持 64 位 Linux 系统调用，我们也尚未在 FreeBSD 上成功运行 64 位 Oracle JRE。最后，FreeBSD 基金会曾提供 Oracle JRE 的 FreeBSD 原生二进制，但已不再提供。你大概会纳闷我们的问题是什么，为什么不直接用 OpenJDK。答案是我们在它上面遇到过难以定位的问题。在 Kafka 上，我们使用 OpenJDK 时看到了周期性的消息损坏，而使用 Oracle JRE 时并未出现。更不用说 Oracle JRE 是 Java 的权威实现，这在生产环境中至关重要。之后，我们选择使用 Oracle JRE，至少等我们回头重新测试并协助修复 OpenJDK 时再考虑。我们要 64 位，选择了 Oracle 的 JRE 作为前进方向。这意味着我们不得不转向 Linux 来运行 Kafka、Cassandra 和 Spark 环境。随着我们持续构建这些服务，这会导致我们拥有越来越多的 Linux。

## 系统调优：我的服务器没那么弱

另一个值得更多关注的领域是 FreeBSD 安装的出厂调优。许多默认值似乎是为支持最低公分母而调优的，最好能实现一种在安装时可选的配置档机制。让我用具体例子说明。现在，我们编译 Tcl 解释器时会附加一个 CFLAGS 值 `-DFD_SETSIZE=4096`，以覆盖 **\<sys/select.h>** 中默认的 1024。我们知道 `select()` 并不理想，也悬赏征求 Tcl 的 kqueue 支持，但 1024 对于现代网络应用来说似乎也太小了。希望这个值能调高，这样 FreeBSD 系统就能开箱即用，处理它原本可以毫不费力支持的所有并发连接，而无需用户进一步调整。默认的日志轮转大小也值得审视。根据全新 FreeBSD 安装上的 **/etc/newsyslog.conf**，大多数日志文件应在增长到 100KB 时轮转。我不知道你怎么想，但我的 **/var/log/messages** 和 **/var/log/auth.log** 没多久就会达到这一阈值。在你能买到 10TB 硬盘和 1TB SSD 的时代，这显然是配置档系统的候选对象，提供嵌入式、服务器、桌面等不同选项，让目标轮转大小更合理。即使 100KB 对于基于 SD 卡的系统也嫌小。既然在抱怨日志，顺便也提几个建议。能否为 **/etc/syslog.conf** 提供一个 include 指令？`newsyslog.conf` 已经有了一个，syslog 看起来也该有。对 `syslog.conf` 中的 `!` 程序说明符做一些改进也不错。眼下，为了把所有来自 postgres 的消息记录到 **/var/log/postgres.log**，我得这么写：

```sh
# 捕获 PostgreSQL 日志
!postgres *.* /var/log/postgres.log

# 捕获所有其他日志
!-postgres *.* /var/log/all.log
```

如果有一种程序说明符能匹配除其它已定义程序说明符之外的一切，那就太好了。这样你就可以有一条 catch-all 规则，而不必逐一列出排除项。这也能绕过我遇到的另一个问题：`syslog.conf` 的最大行长太短，无法一一列出。最后，另一个可改进的领域是整体网络栈调优。如果你在谷歌上搜索，会找到许多页面告诉你该设置哪些 `sysctl` 和可调参数来最大化网络栈和 10GigE 网卡吞吐。如果在安装时能有一档配置选择，把系统调优为适合 Varnish、nginx、Apache 等不同场景，以及 GigE、10GigE 或 40GigE 网络，那就太好了。眼下，要把它配置好并完全理解它，多少算是一门暗黑技艺。

## 调试：因为东西会坏

如果 zpool 命令崩溃了你怎么办？我会启动调试器，希望通过加载 core 文件看到它在哪里崩溃。可惜，安装 FreeBSD release 版本时基本系统不带调试符号。这是为什么？太大了吗，就像那些 100KB 的日志文件一样？如果基础二进制没有被 strip，或者至少有一个可选系统组件包含所有基础二进制和库的符号文件，那就太好了。同样，Ports 树默认构建软件时不带符号。多数 Port 有 DEBUG 选项，启用后软件会带符号构建，但通常会禁用所有编译器优化。这对生产环境并不理想。我希望看到一个通用选项，可以构建带优化同时也带调试符号的 Port，最好默认如此。我知道用 `-O` 或 `-O2` 构建的程序调试会更难，但总比没有强。最后，所有东西都应默认带 DTRACE。DTRACE 是一款出色的工具，FlightAware 不止一次用它来理解性能问题或系统故障。PostgreSQL 和 Tcl 都方便地具备全面的 DTRACE 支持。然而，Ports 默认不启用它。在我们看来，每个 Port 都应默认带 DTRACE 构建。日常不一定用得上，但与调试符号一样，问题真正出现时它无价。

## 收尾

希望我已经讲清楚，FlightAware 确实偏爱并倡导 FreeBSD。话虽如此，总有改进空间，我在这里着手解释了 FreeBSD 哪些方面可以改进，以更好地服务我们以及想必其他用户。是时候开始研究如何更轻松地增加 FreeBSD 在野安装数量，并让它更容易被视为一种商品化平台了。

***

**Sean Kelly** 自 2012 年起加入 FlightAware 团队，担任 IT 运营总监。在该职位上，Sean 设计并监督支撑 FlightAware 的全球基础设施以及公司两处办公点所有 IT 系统的增长与维护。Sean 还是 FreeBSD 项目的前提交者，至少从 4.0 时代起就开始使用 FreeBSD。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://freebsd-journal-cn.bsdcn.org/20170102-quan-qiu-ke-fang-wen-de-freebsd/flightaware-and-freebsd.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
