# 操作系统与主机基线

LLMS 索引： [llms.txt](/llms.txt)

---

主机调优的最大风险，不是漏掉某个神奇参数，而是不知道当前系统实际是什么，
却批量套用一份来源、版本和目标都不明的模板。

## 19.3.1 文件系统、挂载、预读与透明大页 {#item-19-3-1}

### 文件系统选择是一份兼容与恢复合同

记录：

```text
filesystem type/version
mount source and target
mount options
block/sector size
discard
inode capacity
snapshot/reflink
encryption
quota
repair tooling
backup compatibility
```

选择 XFS、ext4 或其他支持文件系统，要基于目标 OS、存储、运维能力与
PostgreSQL/Pigsty 支持矩阵，不凭论坛结论。

### 数据、WAL、日志、备份与临时文件的路径

典型职责：

```text
PGDATA
WAL (可能同盘或独立)
tablespaces
PostgreSQL logs
pgBackRest spool/repository
PgBouncer/Patroni logs
temp files (位于 relation tablespace)
monitoring/log storage
package repository
```

分盘不是目的。要问：

- 故障是否独立；
- I/O 是否真正隔离；
- capacity 是否独立；
- backup/restore 是否更复杂；
- mount 缺失时会不会写进 root；
- 监控是否覆盖每个 filesystem；
- 权限和 SELinux/AppArmor 是否一致。

### 防止“挂载没上，目录还在”

危险场景：

```text
/pg/data intended mount absent
directory exists on root filesystem
PostgreSQL starts and writes root disk
root fills
later mount hides wrong data
```

保护：

```text
systemd RequiresMountsFor
mountpoint validation before service
expected device/filesystem UUID
directory marker
capacity sanity check
monitor filesystem identity, not only path
```

本章 capture 同时记录 target、source、fstype、options 与 free，不只记录
`/pg` 存在。

### mount option 不应照抄

常见选项需要理解：

```text
noatime
discard / periodic fstrim
barrier/durability defaults
inode/allocation
network filesystem sync/cache semantics
```

现代文件系统默认行为会变化。任何影响持久性或恢复的选项，必须有官方
文档、目标版本和故障测试证据。

### 预读依赖访问形状

OS/block device read-ahead 对顺序扫描可能有益，对随机 OLTP 可能放大无效 I/O。

记录：

```bash
lsblk -o NAME,TYPE,ROTA,RA,SIZE,FSTYPE,MOUNTPOINTS
blockdev --getra /dev/...
```

然后用：

```text
OLTP random
analytics sequential
backup/restore stream
replica replay
```

分别测。不要把某 SSD 模板的 KB 值复制到所有设备。

### I/O scheduler 也要与设备匹配

物理旋转盘、NVMe、virtio、cloud block 的队列和 scheduler 不同。

采集：

```bash
cat /sys/block/<dev>/queue/scheduler
cat /sys/block/<dev>/queue/nr_requests
```

改变前后同时看 workload latency、throughput、queue 和 CPU。

### THP 与显式 huge pages

当前值：

```bash
grep . /sys/kernel/mm/transparent_hugepage/{enabled,defrag}
grep -i huge /proc/meminfo
```

本章要求 THP `never`，与 Pigsty node tuning 预期一致。PostgreSQL 官方
[Resource Consumption](https://www.postgresql.org/docs/18/runtime-config-resource.html)
区分 THP 与显式 huge pages，并指出 THP 对部分 PostgreSQL 环境会造成性能
下降。

显式 huge pages 需要：

```text
shared memory size
page size
nr_hugepages
postgres OS group/lock permissions
startup policy try/on
reboot/fragmentation behavior
monitoring
```

若 `huge_pages=on` 且数量不足，PostgreSQL 会拒绝启动；这是刻意强约束，
不能未经演练启用。

### data directory 权限

PostgreSQL 官方
[`initdb`](https://www.postgresql.org/docs/18/app-initdb.html)
要求以最终 server owner 运行，不能 root 运行。目录应由 database OS user
拥有并限制权限。

检查：

```bash
namei -l "$PGDATA"
stat -c '%U %G %a %n' "$PGDATA"
findmnt -T "$PGDATA"
```

父目录权限同样重要。不要让普通应用用户读取 data files、WAL 或 backup。

## 19.3.2 用户、目录、权限、时间同步与日志 {#item-19-3-2}

### OS 身份与数据库身份分开

OS：

```text
admin automation user
postgres service user
backup/repository user
monitoring/log agents
```

PostgreSQL：

```text
bootstrap superuser
NOLOGIN object owner
runtime roles
monitor/replication/backup/admin roles
```

同名不代表同一身份。Peer authentication 才把 OS user 映射为 database role。

### admin user 的前提要验收

Pigsty multi-node 部署需要管理用户可以：

```text
passwordless SSH to targets
passwordless sudo
run Python/Ansible modules
write expected directories
reach package sources/infra
```

这是一项强权限。要用：

```text
dedicated identity
key protection/rotation
source restriction
sudo policy
audit
break-glass
offboarding
```

本章 disposable Vagrant 用 `vagrant` + nopass sudo，只能在本地 sandbox
接受；production 必须重新设计。

### 目录清单是接口

记录每个组件：

| 项 | owner/mode | purpose | retention |
|---|---|---|---|
| source/inventory | admin | IaC | versioned/private |
| `/pg/data` | postgres | data | authoritative |
| `/pg/log` | postgres/agents | component logs | policy |
| `/pg/backup` | backup | repository/spool | backup policy |
| `/etc/patroni` | root/postgres | HA config | config |
| `/etc/pgbouncer` | service | pool config | config |
| `/etc/pgbackrest` | controlled | backup config/secret refs | config |
| `/etc/haproxy` | root | routing | config |

不要把目录路径写死进应用；通过 service/config 接口访问。

### 文件权限要从 threat model 验证

检查：

```bash
find ... -maxdepth ... -printf '%m %u %g %p\n'
systemctl cat <service>
systemctl show <service> -p User -p Group -p EnvironmentFiles
getfacl
```

特别关注：

```text
private keys
password/config files
.pgpass / service files
backup cipher pass
Patroni REST credentials
HAProxy stats credentials
MinIO keys
Ansible inventory/vault
```

本章 live inventory 必须 `0600`，projection 不能含 secret value。

### 时间同步先于集群判断

chrony/其他 NTP 服务需要：

```text
active
source reachable
synchronized
offset/jitter within policy
boot behavior
monitoring
```

只看 service active 不够；本章读取 `timedatectl NTPSynchronized`。

时间跳变可能影响日志、lease、TLS、业务 timestamp 与 incident timeline。
数据库业务时间语义仍要用第 16 章的 UTC/event-time 合同。

### hostname、machine-id 与 address

基线同时保存：

```text
declared address
live hostname
hashed /etc/machine-id
live interface addresses
```

原因：

- IP 可能被错误复用；
- hostname 可能全部相同；
- SSH config 可能把不同别名指向同一主机；
- VM clone 可能复制 machine-id；
- inventory 拼写可能连错环境。

本章准备部署时就发现，常规 SSH config 把 Vagrant 地址重写为本地转发端口；
只看命令参数不够。正式采集强制 `ssh -F /dev/null` 直连，并以 machine-id
hash 证明四个目标不同。

hash 不让 evidence 暴露原始 machine-id，但仍能检测重复。

### 日志要有来源身份

每条集中日志至少关联：

```text
timestamp + timezone
host/machine
service/component
cluster/instance
process/session
severity
request/query correlation where safe
```

不能只按 hostname，如果重装/复用会混淆。

### 日志容量与敏感性

PostgreSQL 日志可能含：

```text
SQL text
parameters/data
roles/database
client addresses
errors
file paths
```

控制：

```text
log_statement/log_min_duration choice
parameter logging/redaction
access
encryption
retention
export failure buffer
deletion/legal hold
```

“为了排障全量记录 SQL 与参数”可能制造数据泄露。

### logrotate 与磁盘故障

要验证：

```text
rotation
compression
retention
copytruncate vs reopen semantics
agent backpressure
local buffer
disk alert
component restart
```

日志不能与 PGDATA 互相填满；集中日志不可达也不应无限占用本地磁盘。

## 19.3.3 基线检查必须记录事实而非套用调优模板 {#item-19-3-3}

### 基线与目标分两列

推荐格式：

| 项 | observed | desired | status | evidence |
|---|---|---|---|---|
| OS | Ubuntu 24.04.4 | Ubuntu 24.04.x | pass | host JSON |
| THP | never | never | pass | sysfs |
| swap | 0 | 0 | pass | meminfo |
| filesystem | ext4 virtual | qualified prod storage | sandbox exception | findmnt |
| NTP | synchronized | synchronized | pass | timedatectl |

事实不符合时：

```text
fail
accepted exception with owner/expiry
planned remediation
```

不能把 desired 覆盖 observed 后再宣布通过。

### “最佳实践参数”有上下文

网络文章常给：

```text
vm.swappiness=1
vm.dirty_ratio=...
read_ahead_kb=...
noatime
hugepages=...
max_connections=...
```

每项必须问：

```text
哪个 OS/kernel/filesystem/device？
哪个 PostgreSQL/version？
哪个 workload？
解决什么测量瓶颈？
副作用？
生效范围与重启？
如何回滚？
如何证明改善？
```

答不出就先记录，不变更。

### 配置层级要可追踪

一个值可能来自：

```text
kernel default
distribution sysctl
cloud image
hypervisor
Pigsty role default
global inventory
cluster vars
host vars
manual change
runtime command
```

审计要保存最终事实和来源。只看 Git inventory 无法发现手工漂移；只看
`sysctl -a` 又无法知道下次重启会恢复成什么。

### 一次性探测命令也有风险

基线 collector 应：

```text
read-only
bounded timeout
explicit target
no secret output
stable schema
known command allowlist
stderr/exit preserved
```

避免：

```text
curl | sh
unbounded find /
dump all environment
print full config
copy /etc wholesale
run benchmark on production
```

本章 collector 不读取原始 machine-id 以外的 secret 文件；machine-id 只输出
SHA-256。

### 自动修复与验收分开

部署 playbook 可以改变主机；acceptance collector 只读。

好处：

- 验收失败不会偷偷修复；
- 能发现 automation 未覆盖的漂移；
- 证据可由不同身份运行；
- 重跑不会增加变更；
- 故障现场可先保留状态。

`task.sh all` 不会执行 `deploy.yml`。

### 运行两次不等于幂等证明

Ansible 第二次 `changed=0` 是强证据，但不是全部：

- 外部 API 可能有副作用；
- task 可能每次重启但未报告 change；
- 数据初始化可能非幂等；
- template 中动态值可漂移；
- service 行为可能改变；
- secrets/certificates 可能轮换。

需要同时比较：

```text
changed/failed counts
service state
SQL/catalog identity
endpoint behavior
files/config hashes where safe
```

### pending reboot 也是事实

包安装可能提示：

```text
new kernel installed, running kernel still old
```

不要忽略。记录：

```text
running kernel
installed candidate
reboot required
maintenance plan
post-reboot acceptance
```

是否立即 reboot 取决于授权和维护窗口，不应由 collector 自动作出。

### 例外要结构化

例外至少：

```json
{
  "id": "EX19-VIRTUAL-STORAGE",
  "reason": "...",
  "production_impact": "...",
  "owner": "...",
  "expiry_or_scope": "sandbox only",
  "compensating_control": "no production claim"
}
```

本章 requirements 先固定 ID/reason/impact；production 使用时还必须补真实
owner、expiry 与 control。

### 基线漂移比较

每次变更前后比较：

```text
host identities
OS/kernel/package
mount/storage
sysctl/limits
service versions/state
PostgreSQL init identity
topology/endpoints
exceptions
```

某些字段天然变化：

```text
capture time
uptime
LSN
metrics counters
log size
```

比较器要分类，不能要求整个 JSON byte-identical。

### 本章采集 schema

[`remote_host_facts.py`](/labs/ch19/remote_host_facts.py)
输出稳定分类：

```text
identity
kernel/OS/architecture/virtualization
CPU/NUMA
memory/THP/swap/overcommit
root and /pg mount/free
clock
network/firewall/ports
services
packages
```

动态运行指标留给第 25/26 章；本章是配置与环境基线。

### 最后的判断

主机基线的合格结论不是：

> 所有参数都等于模板。

而是：

> 对每个与服务目标有关的主机事实，我们知道 observed 值、来源、期望、
> 差异、证据和 owner；没有任何未解释差异被伪装成通过。

---

[上一节：计算、内存、存储与网络](../02/) · [返回本章目录](../) · [下一节：版本与数据库初始化契约](../04/) ·
[查看全书目录](/toc/) · [查看索引中心](/indexes/)
