香港服务器用 CentOS 7 时,我如何用 Tuned Profile 做“自动化性能优化”,把机器按工作负载一键切到合适的状态

那天夜里 1:20,我在葵涌机房的冷风里被手机报警叫醒:某跨境电商客户做直播秒杀,香港集群前端 p99 延迟从 18ms 飙到了 120ms,MySQL 的行锁堆叠像电梯口的双十一。机房灯光打在机柜门上,能看见自己呼出的白气。我没有先去翻 Nginx、内核、网卡一堆参数,而是直接在跳板机上敲了两行:
sudo tuned-adm profile latency-performance
sudo tuned-adm active
三十秒后,前端延迟掉回 25~30ms,回写队列也缓了下来。这不是魔法,是我早就把“不同工作负载用不同的 Tuned Profile”这套东西打通了,而且把自定义 profile 做成了自动化切换。那晚的后半夜,我把临时切换固化成策略:让机器“自己知道自己是谁”,在“低延迟 Web”、“数据库 IO 密集”、“跨机房吞吐备份”、“虚拟化承载”之间,一键/自动切换到最适合的状态。
下面我把这套做法完整摊开。既写给踩坑的人,也写给想把这活儿做漂亮的人。
1. 现场环境与硬件清单
我们手上的两类香港机型用得最多(真实上架批次之一):
| 项目 | 机型 A(前端/数据库) | 机型 B(备份/虚拟化) |
|---|---|---|
| 机箱/主板 | Supermicro 1U / X11DPU | Dell R740xd |
| CPU | 2 × Intel Xeon Silver 4214 (12C/24T, 2.2GHz) | 2 × Intel Xeon Gold 6130 (16C/32T, 2.1GHz) |
| 内存 | 128GB DDR4-2666(8×16GB) | 256GB DDR4-2666(16×16GB) |
| 系统盘 | 2 × SATA SSD 480GB(RAID1) | 2 × SATA SSD 480GB(RAID1) |
| 数据盘 | 2 × NVMe U.2 3.2TB(RAID1, 数据库/缓存) | 10 × SATA HDD 8TB(RAID6, 备份/对象) |
| 网卡 | Intel X710 10GbE ×2 | Mellanox ConnectX-4 25GbE ×2 |
| OS | CentOS 7.9 (3.10 内核,部分升级至 5.4 LTS) | CentOS 7.9 (3.10 内核) |
| 关键服务 | Nginx/LVS、Percona MySQL 5.7 | MinIO/rsync、KVM、Docker |
注:CentOS 7 默认 3.10 内核,如需 BBR 或更好的多队列 NVMe/网卡体验,我们会对前端节点打 ELRepo 的 kernel-lt/kernel-ml(比如 5.4/5.10)。统一变更时请评估内核 ABI 兼容。
2. Tuned 是什么?为什么适合“按场景一键优化”
Tuned 是 RHEL/CentOS 系列自带的系统调优守护进程 + 配置集,核心点:
- 提供一组工作负载导向的预置 profile(Balanced、Throughput、Latency、Network、Virtual-* 等)。
- 每个 profile 通过插件式段落([cpu] [vm] [sysctl] [disk] ...)把CPU 频率/能耗、内核 sysctl、磁盘调度、THP、IRQ、网卡等调到某种“性格”。
- 可以叠加和自定义:我们常用 include=某预置,再加上业务化的 sysctl/脚本。
- 有常用命令:tuned-adm list/recommend/profile/active/profile_info;服务名 tuned。
CentOS 7 常见内置 profile(命名略有差异):
| Profile | 适用 | 核心倾向 |
|---|---|---|
balanced |
通用默认 | 折中 |
throughput-performance |
批处理/备份/吞吐 | 提升吞吐,CPU 高性能,readahead 更大 |
latency-performance |
延迟敏感(Web/交易) | 追求低延迟,禁 THP,性能 governor |
network-latency |
网络低延迟 | 网栈偏低延迟 |
network-throughput |
网络高吞吐(大包/备份) | rmem/wmem 更大 |
virtual-host |
KVM 宿主机 | NUMA/中断/调度倾向虚拟化 |
virtual-guest |
虚机内部 | 虚机通用调优 |
powersave |
低功耗 | 省电优先 |
3. 基础安装与“手工救火”到“制度化”的最短路径
3.1 安装与启用
# 安装
sudo yum install -y tuned tuned-utils tuned-profiles-compat
# 开机自启动并马上启动
sudo systemctl enable --now tuned
# 看看系统推荐(会基于硬件/虚拟化特征给建议)
sudo tuned-adm recommend
# 列出所有可用 profile
sudo tuned-adm list
# 激活一个
sudo tuned-adm profile latency-performance
# 查看当前生效
sudo tuned-adm active
sudo tuned-adm profile_info
排错入口:/var/log/tuned/tuned.log,任何没生效的选项、冲突、脚本报错,这里都有记录。
3.2 两个“立竿见影”的开关(CentOS 7)
CPU governor:切到 performance,延迟场景肉眼可见地稳定。
THP(Transparent Huge Pages):数据库延迟敏感时常常关掉(never 或 madvise),减少抖动。
4. 我们的“场景画像”与自定义 Profile
策略:先 include 官方 profile,再用小刀精准加料。这样“稳又快”,也方便回滚。
4.1 场景 A:低延迟 Web(Nginx/LVS)
目录:/etc/tuned/custom-nginx/tuned.conf
[main]
include=latency-performance
summary=Low-latency web front profile (CentOS 7, HK POP)
[cpu]
governor=performance
energy_perf_bias=performance
force_latency=1
[vm]
transparent_hugepages=never
swapiness=10
# 主要是网络栈,保持低延迟倾向
[sysctl]
net.core.somaxconn=4096
net.core.netdev_max_backlog=65536
net.ipv4.tcp_fin_timeout=15
net.ipv4.tcp_tw_reuse=1
net.ipv4.tcp_mtu_probing=1
net.ipv4.tcp_timestamps=1
net.ipv4.tcp_sack=1
net.ipv4.tcp_low_latency=1
# 适度放大缓冲但保持低延迟优先
net.core.rmem_max=33554432
net.core.wmem_max=33554432
net.ipv4.tcp_rmem=4096 87380 33554432
net.ipv4.tcp_wmem=4096 65536 33554432
# BBR 仅当内核>=4.9且模块存在,否则保持 cubic
net.ipv4.tcp_congestion_control=cubic
[disk]
# NVMe 通常是 'none',SATA SSD/HDD 用 deadline
elevator=deadline
readahead=256
说明:这里不盲目开超大缓冲,因为直播/抢购这种流量突增时,过大的队列反而会让尾延迟拉长。
4.2 场景 B:数据库(Percona MySQL 5.7)
/etc/tuned/custom-mysql/tuned.conf
[main]
include=latency-performance
summary=MySQL OLTP profile (HK POP, NVMe RAID1)
[cpu]
governor=performance
energy_perf_bias=performance
[vm]
transparent_hugepages=never
swappiness=1
dirty_background_ratio=5
dirty_ratio=15
overcommit_memory=1
[sysctl]
vm.max_map_count=262144
fs.aio-max-nr=1048576
# InnoDB + 连接池延迟敏感
net.core.somaxconn=8192
net.ipv4.ip_local_port_range=10240 65535
net.core.rmem_max=67108864
net.core.wmem_max=67108864
net.ipv4.tcp_rmem=4096 87380 67108864
net.ipv4.tcp_wmem=4096 65536 67108864
net.ipv4.tcp_congestion_control=cubic
[disk]
# SATA SSD: deadline;NVMe: 维持 none/none 等价
elevator=deadline
readahead=128
补充:MySQL 本身要配合 innodb_flush_method=O_DIRECT、innodb_flush_neighbors=0,NVMe 更明显。
4.3 场景 C:跨机房大吞吐备份(rsync/MinIO)
/etc/tuned/custom-throughput/tuned.conf
[main]
include=throughput-performance
summary=WAN backup/object throughput profile
[cpu]
governor=performance
[vm]
transparent_hugepages=madvise
swappiness=10
dirty_background_bytes=67108864
dirty_bytes=536870912
[sysctl]
# 大吞吐,放大缓冲
net.core.netdev_max_backlog=250000
net.core.rmem_max=134217728
net.core.wmem_max=134217728
net.ipv4.tcp_rmem=4096 1048576 134217728
net.ipv4.tcp_wmem=4096 1048576 134217728
net.ipv4.tcp_mtu_probing=1
net.ipv4.tcp_congestion_control=cubic
[disk]
readahead=4096
elevator=deadline
4.4 场景 D:虚拟化宿主机(KVM)
/etc/tuned/custom-virtual-host/tuned.conf
[main]
include=virtual-host
summary=KVM host profile with IRQ/Numa hints
[cpu]
governor=performance
energy_perf_bias=performance
[vm]
transparent_hugepages=madvise
swappiness=5
[sysctl]
kernel.sched_migration_cost_ns=5000000
vm.zone_reclaim_mode=0
KVM 场景里,我们更多把 CPU 绑定、IRQ 亲和、NUMA 拆分交给 Libvirt/调度器,但用 tuned 撑住底层“基调”。
4.5 激活与回滚
sudo tuned-adm profile custom-mysql # 切到自定义
sudo tuned-adm profile balanced # 快速回滚到通用
当前生效写在 /etc/tuned/active_profile。我们还做了一个“安全网”脚本,切换前备份关键 sysctl,紧急回滚时直接 restore(见第 6 节)。
5. 自动化:让机器“自己知道选哪个 Profile”
5.1 一段极简的 Bash(无 Ansible 也能跑)
/usr/local/sbin/auto-tuned.sh
#!/usr/bin/env bash
set -euo pipefail
kernel_ver=$(uname -r | cut -d. -f1)
has_mysql=$(pgrep -x mysqld >/dev/null && echo 1 || echo 0)
has_nginx=$(pgrep -x nginx >/dev/null && echo 1 || echo 0)
is_kvm_host=$(lsmod | grep -q kvm_intel && virsh list --all >/dev/null 2>&1 && echo 1 || echo 0)
is_backup=$(systemctl is-active minio >/dev/null 2>&1 || pgrep -x rsync >/dev/null && echo 1 || echo 0)
profile="balanced"
if [[ $is_kvm_host -eq 1 ]]; then
profile="custom-virtual-host"
elif [[ $has_mysql -eq 1 ]]; then
profile="custom-mysql"
elif [[ $is_backup -eq 1 ]]; then
profile="custom-throughput"
elif [[ $has_nginx -eq 1 ]]; then
profile="custom-nginx"
fi
echo "Selecting tuned profile: $profile"
tuned-adm profile "$profile"
tuned-adm active
开机时调用:
cat >/etc/systemd/system/auto-tuned.service <<'EOF'
[Unit]
Description=Auto select tuned profile by workload
After=network-online.target tuned.service
Wants=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/auto-tuned.sh
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable auto-tuned.service
5.2 Ansible 版本(生产常用)
- hosts: hk-pop
become: yes
tasks:
- name: Install tuned
yum: { name: [tuned, tuned-utils, tuned-profiles-compat], state: present }
- name: Enable tuned
systemd: { name: tuned, enabled: yes, state: started }
- name: Deploy custom profiles
copy:
src: "files/tuned/{{ item }}/tuned.conf"
dest: "/etc/tuned/{{ item }}/tuned.conf"
mode: "0644"
with_items:
- custom-nginx
- custom-mysql
- custom-throughput
- custom-virtual-host
- name: Install auto selector
copy:
src: files/auto-tuned.sh
dest: /usr/local/sbin/auto-tuned.sh
mode: "0755"
- name: Install systemd unit
copy:
src: files/auto-tuned.service
dest: /etc/systemd/system/auto-tuned.service
- name: Reload systemd and run selector
systemd: { daemon_reload: yes, name: auto-tuned.service, enabled: yes, state: started }
6. 变更前备份 & 快速回退
我们在 /usr/local/sbin/sysctl-snapshot.sh 做了一个快照工具:
#!/usr/bin/env bash
set -e
SNAP=/var/backups/sysctl-$(date +%F-%H%M%S).conf
/sbin/sysctl -a 2>/dev/null | sed -n 's/^\([^ ]\+\) = \(.*\)$/\1=\2/p' > "$SNAP"
echo "Saved to $SNAP"
回滚时:
sudo sysctl -p /var/backups/sysctl-<timestamp>.conf
sudo tuned-adm profile balanced
7. 实测数据:切换前后,用数说话
测试方法尽量贴近业务:Web 用 wrk 压测 + tail latency,DB 用 sysbench OLTP,备份用 iperf3 + rsync 大文件。
7.1 前端(Nginx,10GbE,内核 5.4,custom-nginx)
| 指标 | 切换前 balanced |
切换后 custom-nginx |
变化 |
|---|---|---|---|
| RPS(wrk, 50 并发) | 58,200 | 64,900 | +11.5% |
| p95 延迟 | 41.3 ms | 28.7 ms | -30.5% |
| p99 延迟 | 118.9 ms | 54.2 ms | -54.4% |
| CPU 频率波动 | 明显 | 稳定在高频 | 稳定 |
7.2 MySQL(NVMe RAID1,custom-mysql)
| 指标 | balanced |
custom-mysql |
变化 |
|---|---|---|---|
| sysbench TPS(oltp_read_write, 64 线程) | 3,850 | 4,410 | +14.5% |
| p95 响应 | 29.6 ms | 22.1 ms | -25.3% |
| InnoDB fsync/s | 10.2k | 12.1k | +18.6% |
7.3 跨机房吞吐(25GbE,custom-throughput)
| 指标 | balanced |
custom-throughput |
变化 |
|---|---|---|---|
| iperf3 单流 | 9.8 Gbps | 14.7 Gbps | +50% |
| iperf3 4 流 | 17.2 Gbps | 22.9 Gbps | +33% |
| rsync 50GB 大文件 | 288 MB/s | 371 MB/s | +28.8% |
这些数字不是广告语,是我们在香港 POP 里反复做出来的中位水平;你的结果会受内核版本、线路质量、对端栈、TLS、磁盘等影响。
8. 线上常见坑与我在机房的解决手记
BBR 不是银弹(也不总能开)
CentOS 7 默认 3.10 没 BBR,贸然升级内核要评估驱动、监控 Agent、HBA 兼容。我们做法:前端节点单独灰度到 5.4/5.10,profile 里仍默认 cubic,只在探测到 BBR 可用时脚本再切(sysctl net.ipv4.tcp_available_congestion_control 检测)。
NVMe 调度器
NVMe 通常是 none(等价 noop),你强行写 deadline 也未必能改;所以我在 [disk] 写 elevator=deadline 只是照顾 SATA 盘,同时在巡检脚本里按设备类型判断。查看:cat /sys/block/nvme0n1/queue/scheduler。
THP 生效与否要看内核状态
latency-performance 会关 THP,但线上我一定跑:
cat /sys/kernel/mm/transparent_hugepage/enabled,确认 never 或 madvise 是否真正生效。
之前遇到过容器内和宿主机不一致,最后把调优做在宿主机 profile 上,并在容器入口脚本二次确认。
cpupower/IRQBalance 冲突
部分镜像里装了 cpupower 或我们早期自写的 CPU pinning 服务,会和 tuned 都想改 governor/IRQ,引发拉扯。解决:让tuned 做基线,pinning 用业务化脚本,互相避让。冲突可在 /var/log/tuned/tuned.log 看到。
手动改 /etc/sysctl.conf 被“打回原形”
很多人一边用 tuned,一边到处 echo sysctl。记住:把业务化 sysctl 放在自定义 profile 的 [sysctl] 段,别散落在 sysctl.conf,否则重启/重载后会被 profile 覆盖。
虚拟机里误用 virtual-host
我见过同事在 KVM 来宾里套了宿主机 profile,时延直接炸。虚机里请用 virtual-guest 或针对业务自定义。
BIOS 电源策略被忘了
机房换主板后 BIOS 复位,CPU 电源策略回到 Balanced,怎么调都不稳。排查 20 分钟后我进 BIOS 改成 Performance,世界瞬间安静。软件调优挡不住硬件被“捆着手脚”。
网卡队列/中断亲和要分工
Tuned 对网卡 ring、coalesce 的能力有限,细粒度优化(ethtool -G/-C、rps_cpus、irqbalance --banirq)我放在脚本插件或 Ansible role 里,而不是硬塞进 tuned。
例子(配合 custom-nginx):
/etc/tuned/custom-nginx/ethtool.sh
#!/usr/bin/env bash
ethtool -G ens3f0 rx 4096 tx 4096 || true
ethtool -C ens3f0 rx-usecs 12 rx-frames 0 || true
tuned.conf 加:
[scripts]
script=/etc/tuned/custom-nginx/ethtool.sh
9. 巡检与验收:别迷信“设了就好”,要看结果
profile 生效检查:
tuned-adm active / tuned-adm profile_info / journalctl -u tuned -n 100
CPU/频率:
cpupower frequency-info(或直接看 /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor)
THP/污页:
cat /sys/kernel/mm/transparent_hugepage/enabled / sysctl vm.dirty_*
磁盘/调度器/预读:
cat /sys/block/sdX/queue/scheduler / blockdev --getra /dev/sdX
网络栈:
sysctl net.core.rmem_max net.core.wmem_max net.core.netdev_max_backlog
基准:
- Web:wrk -t8 -c128 -d60s https://...
- DB:sysbench oltp_read_write --tables=16 --table-size=1e6 ... run
- 吞吐:iperf3 -P4 -t60 -c <peer>
把这些写成 Runbook,每次变更都留“前后对比截图/数据”,久而久之你会有自己的“香港 POP 体感库”。
10. FAQ(我常被问到的)
Q:Tuned 会覆盖我的 Docker 容器内核参数吗?
A:tuned 改的是宿主机;容器网络命名空间里的 sysctl 不会被自动覆盖,需要在容器编排层面设置(例如 docker run --sysctl 或 k8s Pod securityContext)。
Q:能否一台机既跑低延迟 Web 又跑吞吐备份?
A:能,但 profile 只能选一个。我的做法是按主业务选 profile,把次业务的关键参数通过脚本/服务粒度调,避免“拉扯”。
Q:切 profile 会不会瞬时抖动?
A:个别参数重载会有轻微抖动(比如 THP 切换),所以在窗口期做;而 somaxconn、readahead 等通常无感。
11. 收尾:从“半夜救火”到“机器自己会选路”
回想那晚,切到 latency-performance 的那个瞬间,我盯着面板上 p99 的曲线像电梯一样下行——那是我在机房里最爱看的“风景线”。如今,新的香港节点上架,我几乎不需要再守在屏幕前:Nginx 在,选 custom-nginx;MySQL 起,切 custom-mysql;周末做跨机房备份,自动把 profile 换到 custom-throughput。
我仍然会去机柜前听一听风扇的声音、摸一摸网线卡扣是不是松了——**优化不是一堆参数的堆砌,而是让机器“像人一样”知道在什么场景该做什么事。**Tuned 不是终点,但它是把“性能优化”制度化的那把扳手。
如果你也在香港机房里和我经历着同样的夜晚,拿走这套脚本和 profile 吧。先从一台灰度,跑一轮巡检,再让它自动地、可回滚地、可度量地跑起来。等你有了自己的曲线和数字,你就会明白:那并不是一次幸运的按键,而是一套可复制的工程方法。