技术指南
VPS 初始调优:先盘点、再变更、用证据验收
把新 VPS 初始化改造成可审查的变更流程:先保存基线,再逐项验证软件包、资源、内核、日志与时间设置。
新 VPS 的第一天不适合执行一串“优化命令”。发行版、虚拟化边界、软件源、当前服务和业务目标还没有对齐时,批量升级、全局调高限制、固定启用 BBR 或替换 DNS 都可能制造新的故障。更可靠的初始化把现状快照、候选变更、受控激活、业务验收和回滚证据串成一条短闭环。
先保存一份可复查的主机基线
| 范围 | 要回答的问题 | 只读证据 |
|---|---|---|
| 身份 | 这台实例、发行版和内核是否是目标对象 | `hostnamectl`、`os-release`、`uname` |
| 虚拟化 | 是完整虚拟机、共享内核容器还是嵌套环境 | `systemd-detect-virt` |
| 容量 | CPU、内存、swap、文件系统和 inode 是否有余量 | `lscpu`、`free`、`findmnt`、`df` |
| 服务 | 谁在监听、谁失败、谁开机启动 | `ss`、`systemctl --failed`、unit 清单 |
| 时间与日志 | 时钟是否同步,日志放在哪里、占用多少 | `timedatectl`、`journalctl --disk-usage` |
stamp=$(date --utc +%Y%m%dT%H%M%SZ)
out="$HOME/vps-baseline-$stamp.txt"
{
date --utc --iso-8601=seconds
cat /etc/os-release
uname -a
systemd-detect-virt || true
lscpu
free --bytes
findmnt --real --output TARGET,SOURCE,FSTYPE,OPTIONS
df --block-size=1 --output=source,fstype,size,used,avail,pcent,target
df --inodes --output=source,itotal,iused,iavail,ipcent,target
ss --listening --tcp --udp --numeric --processes
systemctl --failed --no-pager
timedatectl
journalctl --disk-usage
} | tee "$out"`systemd-detect-virt` 能区分完整虚拟机和容器,并只报告最内层环境。共享内核容器通常不能加载模块或修改部分内核参数;完整虚拟机也不代表云平台允许所有能力。把检测结果当作能力线索,再用目标参数和真实业务探针确认,不要仅凭“KVM”或“OpenVZ”标签下结论。
每个变更先写清收益、指标与撤销条件
- 现状:哪条观测证明当前确有问题,而不是套用通用清单。
- 候选:只包含本次要改的文件、包或 unit,能够在不激活时检查。
- 成功:至少一条系统证据和一条用户可见证据,且观察窗口明确。
- 停止:出现连接、延迟、错误率、磁盘或内存退化时由谁终止。
- 回滚:旧配置、旧包策略和恢复入口已经存在,并知道哪些数据变化不可逆。
先模拟软件包计划,再安排维护窗口
`apt-get update` 刷新索引;`apt-get upgrade` 只升级不需要新增或删除包的已安装软件,而 `dist-upgrade` 可能为了依赖关系删除包。不要把 `apt update && apt upgrade -y` 当作无需审阅的初始化动作。先刷新索引、模拟目标命令并检查安装、移除、保留和配置文件影响,再决定维护窗口与重启顺序。
sudo apt-get update
apt-get --simulate --verbose-versions upgrade | tee /tmp/apt-upgrade.plan
rg '^(Inst|Remv|Conf) ' /tmp/apt-upgrade.plan
apt-mark showhold
apt-get check升级后区分进程重启与主机重启
sudo needrestart -r l
test -e /run/reboot-required && cat /run/reboot-required || true
systemctl --failed --no-pager
ss --listening --tcp --udp --numeric --processes`needrestart` 的职责是找出库升级后需要重启的 daemon,并能检查旧内核或旧库。不要自动重启所有进程后就宣布成功:连接排空、集群顺序、数据库一致性和外部健康检查仍属于应用运行手册。重启主机也必须验证实例可从控制台恢复、服务依赖按预期启动。
文件描述符按服务容量设置,不做全局常数
shell 的 `ulimit`、PAM limits 和 systemd unit 是不同生效路径。systemd 管理的服务优先在该 unit 的 drop-in 中设置 `LimitNOFILE=`,依据连接模型和监控数据确定软硬限制;修改后检查进程实际限制,而不是只看配置文件或交互 shell。
# /etc/systemd/system/example.service.d/70-resources.conf
[Service]
LimitNOFILE=16384systemd-analyze verify /etc/systemd/system/example.service
sudo systemctl daemon-reload
sudo systemctl restart example.service
pid=$(systemctl show --property=MainPID --value example.service)
cat "/proc/$pid/limits"
systemctl show example.service --property=LimitNOFILE更高的 nofile 不会自动增加应用并发能力,也不能替代 worker、上游连接池、内存和内核容量规划。把所有用户同时改成 65535 或 1048576 会扩大不可见资源消耗,也可能与应用自身限制冲突。先解决真实的 `EMFILE`/`Too many open files` 证据,再只调目标服务。
内核参数先做兼容性与单变量门禁
candidate=/tmp/70-example-tuning.conf
printf '%s\n' 'vm.swappiness = 20' > "$candidate"
sysctl vm.swappiness
sudo sysctl --dry-run --load="$candidate"
# 记录旧值后,才在维护窗口安装到 /etc/sysctl.d/
sudo install -m 0644 "$candidate" /etc/sysctl.d/70-example-tuning.conf
sudo sysctl --load=/etc/sysctl.d/70-example-tuning.conf
sysctl vm.swappiness`sysctl.d` 按目录优先级和文件名字典序合并,`/etc` 留给管理员覆盖。部分参数只有模块加载后才出现,启动早期写入可能不会生效;不存在或无权限的键还可能只被记录。`--dry-run` 能发现当前主机上的未知键而不写值,但上线后仍要读取实际值并观察业务指标。
BBR 与 DNS 都是实验,不是新机默认动作
拥塞控制收益取决于内核能力、RTT、丢包、队列、对端与套餐限速;DNS 还可能承载云内私有域名。先记录当前算法、可用算法、解析管理者和同目标业务指标。只有对照实验稳定改善且没有破坏 IPv4、IPv6、私有解析或包管理,才把单一候选安装为持久配置。
sysctl net.ipv4.tcp_available_congestion_control
sysctl net.ipv4.tcp_congestion_control
readlink -f /etc/resolv.conf
resolvectl status 2>/dev/null || true
getent ahosts example.com
# 记录同一目标、时段、并发和持续时间的应用层成功率与延迟先判断内存压力,再准备 swap 候选
swap 能为突发压力提供缓冲,但不能保证避免 OOM,也不能修复持续内存泄漏。大小和 swappiness 应由工作集、磁盘介质、延迟预算与休眠需求决定。swap 文件还受文件系统约束:稀疏文件会被拒绝,Btrfs 等写时复制文件系统需要专门步骤。
free --bytes
swapon --show --bytes --output=NAME,TYPE,SIZE,USED,PRIO
journalctl --dmesg --grep='oom|Out of memory' --since='7 days ago' --no-pager
# 候选文件优先使用发行版和文件系统文档认可的方法;格式化后先识别签名
sudo mkswap /path/to/approved-swapfile
sudo blkid -p /path/to/approved-swapfile日志预算要同时考虑持久与运行时存储
`SystemMaxUse=` 只约束持久 journal,且要求 `/var/log/journal` 可写并存在;否则实际使用的是 `/run/log/journal` 和 `RuntimeMaxUse=`。上限也不保证立即把占用压到该数字,因为清理只删除归档文件。先确认当前 storage 模式、磁盘余量与保留需求,再用 drop-in 覆盖。
# /etc/systemd/journald.conf.d/70-storage-budget.conf
[Journal]
SystemMaxUse=512M
SystemKeepFree=1G
RuntimeMaxUse=128Msystemd-analyze cat-config systemd/journald.conf
journalctl --disk-usage
df --block-size=1 /var/log /run/log
sudo systemctl restart systemd-journald
journalctl --since='5 minutes ago' --no-pager | tail时区显示与时钟同步分开验收
timedatectl
timedatectl show --property=Timezone --property=NTPSynchronized
timedatectl timesync-status 2>/dev/null || true
timedatectl show-timesync --all 2>/dev/null || true`timedatectl set-ntp true` 会启用系统识别到的第一个网络时间同步服务,不保证一定是 systemd-timesyncd。先确认当前管理者和同步证据;容器里时钟通常由宿主机控制。证书、分布式锁和事件排序依赖的是实际时间偏差,而不是屏幕显示为某个时区。
初始化结束前的验收清单
- 基线包含 UTC 时间、发行版、内核、虚拟化、容量、监听、失败 unit、时间与日志状态。
- 每个变更有单独候选、收益指标、停止条件和已验证恢复入口。
- 软件包计划经过模拟和再次复核,没有未解释的移除、hold 或配置文件决策。
- 服务限制读取自实际进程;sysctl 读取自实际键,并有业务指标对照。
- swap 目标和文件系统兼容性已证明;日志的持久/运行时预算与保留要求一致。
- 外部连接、核心业务探针、错误率、资源趋势和重启后的启动状态全部通过。
- 临时计划、测试 unit 和敏感基线已按保留策略处理,变更单记录了真实结果。
回滚配置,也撤销错误假设
- 一次只回滚一个变更:恢复旧文件或删除本次 drop-in,重新加载对应管理器。
- 读取实际生效值,重启或 reload 目标服务,再跑同一系统与用户侧探针。
- 软件包已执行的 schema、数据或配置迁移不能只靠降包;按应用恢复手册处理。
- 若回滚后指标没有恢复,停止继续套用参数,回到基线重新定位瓶颈。
- 保留失败候选、时间线和观测差异,避免下一次把同一猜测重新包装成“最佳实践”。
本次隔离演练验证了什么
VPScope 在 Ubuntu 24.04 上保存只读主机基线,运行 `apt-get --simulate upgrade` 并得到 291 个待升级、0 个新增、0 个移除和 6 个保留包;APT/dpkg 状态未变化。有效 sysctl 候选通过 dry run,故意不存在的键返回状态 1。有效 systemd service 通过 verify,缺少绝对可执行路径的候选返回状态 1。
一个自动回收的 transient service 以 `LimitNOFILE=4096` 启动,并在进程内同时读到 4096 的软硬限制。替代根目录中的 journald drop-in 被 `cat-config` 合并显示;8 MiB 临时文件写入 swap 签名但从未启用。时区与 NTP 同步状态只读检查通过,全部临时文件和 transient unit 清理完成。演练没有升级包、写入 `/etc`、修改内核值、启用 swap、重启服务或触碰 SSH/防火墙。