# systemd 下的 Manticore Search：超越 fork、PID 文件和猜测

在 systemd 下运行 Manticore Search，获得更准确的状态报告、更干净的重载、基于 journal 的日志，以及针对 RT 工作负载更安全的关闭。

如果你在 Linux 上运行 Manticore Search，systemd 应该是管理它的默认方式。

这句话现在听起来显而易见，但很长一段时间里只算部分正确。Manticore 确实可以在 systemd 下运行，只是两者的配合一直有点别扭。这个守护进程模型来自更早的 Unix 世界；systemd 出现得更晚，并且对服务有不同的要求。所以这套配置能用，但从来谈不上多让人满意。

变化其实很简单：Manticore 现在支持原生的 systemd 通知。

为什么要在意？因为几个小烦人的运维问题会一下子改善：

- `systemctl status` 会讲出更真实的情况
- 启动和重载更容易追踪
- 日志能自然地进入 `journalctl`
- 当实时表正在刷盘时，关闭会更安全
- PID 文件不再承担那么多重任

最后这一点比很多人想的影响更小，直到它真正变得很重要的那天。

## 先从关闭说起，因为事情通常就是在这里开始变得认真

这次变化最好的部分，并不是最显眼的部分，而是关闭行为。

如果 Manticore 正在把数据从实时表刷到磁盘，关闭可能会花一段时间。旧方案通常会退回到 `searchd --stopwait`，再加上一点运气。有时候这就够了。有时候真的不够。

失败模式既平淡又糟糕：systemd 认为服务卡住了，等待足够长时间后发送 `SIGKILL`。如果 Manticore 正在刷盘中，这大概是强行杀掉进程的最糟时机。

新行为则是协作式的。只要 Manticore 还在刷盘，它就会告诉 systemd 进度仍在继续，而且还需要更多时间。具体来说，它会每 15 秒发送一次超时延长通知，每次再申请 30 秒。

如果要给文中几乎所有收益排个优先级，我会把这个放在最前面。更好的状态输出当然不错。更干净的重载也不错。但在 RT 刷盘过程中把关闭路径搞坏，要糟得多。

现在，慢停止不再天然可疑了。有时候它只是说明服务器正在完成真正重要的那部分工作。

## 先看看旧方案长什么样

从历史上看，Manticore 使用的是标准的 Unix 守护进程模式：从终端分离，双重 fork 到后台，写入 PID 文件，然后继续运行。这并不是什么奇怪的设计选择。只要你在乎可移植性，很快就会走到这一步。

摩擦来自把这个模型和 systemd 混在一起。

虽然支持存在，但很有限。unit 必须相信这个守护进程 fork 得正确，而且 PID 文件确实存在并且还指向正确的进程。当一切都正常时，没问题。只要稍微偏一点，监督就会变得模糊。

典型的薄弱点很容易预测：

- 进程跟踪依赖外部文件
- 状态报告是间接的
- 启动进度对 systemd 来说大多不可见
- 重载和关闭更难被干净地监控

把这些冷静写出来并不显得多戏剧化。但在实际操作中，它会带来运维人员最讨厌的那类问题：这个服务到底是真的健康，还是只是当前还活着。

有一个具体例子值得记住。如果 Manticore 的内部 watchdog 在崩溃后重启了 `searchd`，systemd 可能仍然会继续监督原始进程关系，而不是新复活的守护进程。像 `Supervising process ... which is not our child` 这样的警告就是这么来的。

另一个更小但很常见的失败模式是配置漂移：在 Manticore 里改了 `pid_file` 路径，却忘了同步更新 unit，结果 `systemd` 追踪到了错误的位置。守护进程可能已经起来了，但 `systemctl status` 却讲着一个没那么有帮助的故事。

![systemd_searchd_pid](./systemd_integration/systemd_searchd_pid.png)

这张截图很好地说明了旧方案。systemd 能看到进程树，但不一定能准确看到你真正关心的服务生命周期。

## 基于 notify 的 unit

新方案使用 `Type=notify`，这样 Manticore 就能直接报告自己的状态，而不是强迫 systemd 只靠 PID 文件和猜测来推断太多东西。

现在的 systemd unit 大致如下：

```systemd
[Unit]
Description=Manticore Search Engine
...

[Service]
Type=notify
...

ExecStart=/usr/bin/searchd --config /etc/manticoresearch/manticore.conf --nodetach $_ADDITIONAL_SEARCHD_PARAMS
...

Restart=on-failure
...
```

这里有几个细节很重要。

`Type=notify` 表示 systemd 期望直接收到 Manticore 发来的状态更新。

`--nodetach` 让 `searchd` 保持在前台。在 systemd 下，这才是正确的默认方式。其他做法都属于额外的仪式感。这个标志本身并不新；变化在于，它现在已经成为打包好的 systemd 配置的一部分，而不只是用于调试的选项。

`PIDFile` 不再是主要的监督机制，但如果你还有依赖它的旧工具，它依然可能有用，也仍然可以手动添加。

有了这套配置，Manticore 可以报告如下状态：

- 启动中
- 正在加载表
- 正在重载
- 就绪

这听起来并不华丽。不过，和旧的“有个进程，大概没问题”模型相比，这已经是实质性的改进了。

## 我喜欢的一个小变化：重载不再那么别扭

重载 Manticore 传统上意味着向 `searchd` 发送 `SIGHUP`。在 systemd 下也还是这样。打包后的 unit 使用：

```systemd
ExecReload=/bin/kill -HUP $MAINPID
```

因此 `systemctl reload manticore` 并不会重启守护进程。它会向正在运行的 `searchd` 发送 `SIGHUP`，从而启动表轮换：Manticore 会重新打开 [普通表](https://manual.manticoresearch.com/Read_this_first#Real-time-table-vs-plain-table)，并切换到新构建的表文件，而不是完整重启守护进程。

这个区别很重要。`reload` 是在不先停服务的前提下触发表轮换的低风险方式。根据 `seamless_rotate` 设置，在轮换窗口期间，新查询可能会短暂停顿，客户端也可能看到临时错误。`restart` 则完全是另一回事：它会先停止服务，然后重新启动。

现在好的地方在于可见性更强了。当轮换开始时，Manticore 会向 systemd 报告 `RELOADING=1`；当轮换完成时，再次报告 `READY=1`。因此 `systemctl status manticore` 可以显示守护进程正在主动轮换，而不是只停留在一个笼统的运行状态。

## `--nodetach` 也修正了日志故事

这个选项并不新。变化的是它所处的环境。

当 Manticore 不是钻到后台里消失，而是保持与 systemd 连接时，日志就会进入 journal，而且主进程也是 systemd 真正认识的那个进程。中间层更少了。这个好处通常是在之后才显现，而不是立刻见效。以前在 `--nodetach` 之前，启动信息可能会先出现在 `journalctl`，然后在 detach 之后，后续内容又转到 Manticore 自己的日志文件里。

所以对很多部署来说，常用工具就已经够了：

```bash
journalctl -u manticore
journalctl -u manticore -f
journalctl -u manticore --since "1 hour ago"
```

我喜欢平淡无奇的日志方案。只看一个最先要找的地方，这件事其实很值。

如果你乐意把 systemd journal 当作主日志目标，那么你可能不再需要 `manticore.conf` 里那套旧日志配置。通常这意味着少操心一件事。

## 关于内部 watchdog

如果 Manticore 运行在 systemd 下，我会推荐最简单的方案：让 systemd 监督这个服务，除非你确实需要，否则不要启用 Manticore 的内部 watchdog。

能同时开两者吗？可以。
正常部署里我会选这个吗？不会。

如果你明确启用了内部 watchdog，监督模型就会变得不那么直接，而且你可能会看到这样的警告：

`Supervising process ... which is not our child`

这个配置是受支持的。但对大多数人来说，它只是多了些移动部件。

## 几条补充说明

在基于通知的监督下，PID 文件对 systemd 自身的重要性会小得多。你也许可以从配置中移除 `pid_file`，但前提是你环境里没有别的东西还依赖它。旧脚本往往比任何人预期的都活得更久。

如果你使用可执行配置文件，包括像 `#!/usr/bin/env python` 这样的基于 shebang 的配置，这套方案通常会比一些旧的服务管理方式更自然。

## 你真正会用到的命令

```bash
sudo systemctl start manticore
sudo systemctl stop manticore
sudo systemctl restart manticore
sudo systemctl reload manticore
sudo systemctl status manticore
sudo systemctl enable manticore
```

再看日志：

```bash
sudo journalctl -u manticore
sudo journalctl -u manticore -f
sudo journalctl -u manticore --since "1 hour ago"
```

基本就是这样。

实际结果并不炫目：Manticore 更像一个现代 Linux 服务，而不再像一个来自更早年代、大家都学着绕着走的守护进程。但这类基础设施改动往往最有价值。它们消除了那些你几乎已经不再注意到的摩擦。
