最近经常遇到因为执行了某条命令,导致系统的资源吃满,ssh 都连不上了,只能被迫重启机子。本文将提供一个脚本和其配置办法,从 CPU,RAM,网络带宽等多个维度分别确保 ssh 在极端负载情况下仍然可用。
使用方法
下面给出的脚本是针对我的云机配置设置的,请你参考下文的脚本说明根据你的机子情况调整,懒得管就直接照抄。
我的云机配置:
cpu:4核;ram:8G;带宽:3M;系统:ubuntu 22 LTS。
1. 创建 SSH 资源保障脚本
我命名为 ssh_resource_guard.sh
1 2 3
| vim /usr/local/bin/ssh_resource_guard.sh
|
脚本内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
| #!/bin/bash
if [ "$(id -u)" -ne 0 ]; then echo "此脚本需要root权限运行" exit 1 fi
NIC="eth0" if ! ip link show $NIC &>/dev/null; then echo "网卡 $NIC 不存在,脚本将退出" exit 1 fi
echo "=== 开始配置资源保障 ==="
echo "正在配置网络优先级..."
tc qdisc del dev $NIC root 2>/dev/null
tc qdisc add dev $NIC root handle 1: htb default 9999
tc class add dev $NIC parent 1: classid 1:1 htb rate 1mbps ceil 1mbps prio 0
iptables -F OUTPUT -t mangle 2>/dev/null iptables -A OUTPUT -t mangle -p tcp --sport 22 -j MARK --set-mark 0x1 tc filter add dev $NIC parent 1: protocol ip handle 0x1 fw flowid 1:1
tc class add dev $NIC parent 1: classid 1:9999 htb rate 2mbps ceil 2mbps prio 7
echo "正在配置CPU优先级..."
if [ -d "/sys/fs/cgroup" ] && [ ! -d "/sys/fs/cgroup/memory" ]; then echo "检测到使用cgroups v2" if [ ! -d "/sys/fs/cgroup/ssh_reserved" ]; then mkdir -p /sys/fs/cgroup/ssh_reserved fi echo "100" > /sys/fs/cgroup/ssh_reserved/cpu.weight 2>/dev/null echo "正在配置systemd服务属性..." systemctl set-property sshd.service CPUWeight=100 for pid in $(pgrep -f "/usr/sbin/sshd"); do echo $pid > /sys/fs/cgroup/ssh_reserved/cgroup.procs 2>/dev/null done else echo "检测到使用cgroups v1或混合模式" if [ -d "/sys/fs/cgroup/cpu" ]; then mkdir -p /sys/fs/cgroup/cpu/ssh_reserved echo 1024 > /sys/fs/cgroup/cpu/ssh_reserved/cpu.shares for pid in $(pgrep -f "/usr/sbin/sshd"); do echo $pid > /sys/fs/cgroup/cpu/ssh_reserved/cgroup.procs 2>/dev/null done fi fi
for pid in $(pgrep -f "/usr/sbin/sshd"); do chrt -rr 50 -p $pid 2>/dev/null done
echo "正在配置内存保护..."
for pid in $(pgrep -f "/usr/sbin/sshd"); do echo -1000 > /proc/$pid/oom_score_adj 2>/dev/null done
if [ -d "/sys/fs/cgroup/memory" ]; then echo "配置cgroups v1内存保护..." mkdir -p /sys/fs/cgroup/memory/other_processes MEM_TOTAL=$(free -b | grep "Mem:" | awk '{print $2}') MEM_LIMIT=$(echo "$MEM_TOTAL * 0.8" | bc | cut -d. -f1) echo $MEM_LIMIT > /sys/fs/cgroup/memory/other_processes/memory.limit_in_bytes for pid in $(ps -eo pid --no-headers); do if ! pgrep -f "sshd|systemd|init|journald|udevd|bash" | grep -q "$pid"; then echo $pid > /sys/fs/cgroup/memory/other_processes/cgroup.procs 2>/dev/null fi done elif [ -d "/sys/fs/cgroup" ]; then echo "配置cgroups v2内存保护..." mkdir -p /sys/fs/cgroup/other_processes echo "+memory" > /sys/fs/cgroup/cgroup.subtree_control 2>/dev/null MEM_TOTAL=$(free -b | grep "Mem:" | awk '{print $2}') MEM_LIMIT=$(echo "$MEM_TOTAL * 0.8" | bc | cut -d. -f1) echo $MEM_LIMIT > /sys/fs/cgroup/other_processes/memory.max 2>/dev/null for pid in $(ps -eo pid --no-headers); do if ! pgrep -f "sshd|systemd|init|journald|udevd|bash" | grep -q "$pid"; then echo $pid > /sys/fs/cgroup/other_processes/cgroup.procs 2>/dev/null fi done fi
echo "=== 资源保障配置完成 ==="
echo "" echo "当前配置状态:" echo "网络带宽配置:" tc -s qdisc show dev $NIC tc -s class show dev $NIC
echo "" echo "SSH进程的OOM分数:" for pid in $(pgrep -f "/usr/sbin/sshd"); do echo "PID $pid: $(cat /proc/$pid/oom_score_adj)" done
echo "" echo "SSH进程的调度策略:" for pid in $(pgrep -f "/usr/sbin/sshd"); do chrt -p $pid done
echo "" echo "已完成所有配置。"
|
2. 设置执行权限:
1
| chmod +x /usr/local/bin/ssh_resource_guard.sh
|
3. 创建systemd服务,使脚本在系统启动时自动运行:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| cat > /etc/systemd/system/ssh-resource-guard.service << 'EOF' [Unit] Description=SSH Resource Guard Service After=network.target sshd.service
[Service] Type=oneshot ExecStart=/usr/local/bin/ssh_resource_guard.sh RemainAfterExit=yes StandardOutput=journal StandardError=journal
[Install] WantedBy=multi-user.target EOF
|
4. 启用并启动服务:
1 2 3
| systemctl daemon-reload systemctl enable ssh-resource-guard.service systemctl start ssh-resource-guard.service
|
5. 检查systemd服务状态:
1
| systemctl status ssh-resource-guard.service
|
如果需要检查运行日志:
1
| journalctl -u ssh-resource-guard.service
|
如果需要手动启动脚本,直接执行:
1
| bash /usr/local/bin/ssh_resource_guard.sh
|
脚本说明
以下是对上述所有命令的详细解释,按功能模块分类说明其作用及实现原理:
一、网络带宽预留 (tc
+ iptables
)
1. 创建根队列
1
| tc qdisc add dev eth0 root handle 1: htb default 9999
|
- 作用:在网卡
eth0
上创建流量控制根队列(qdisc
),使用 HTB(Hierarchical Token Bucket) 算法。
- 参数解析:
root
: 表示根队列。
handle 1:
: 队列的标识符,格式为 主ID:子ID
。
htb
: 使用 HTB 算法实现带宽分层管理。
default 9999
: 未分类的流量默认发送到 1:9999
类。
2. 为 SSH 预留带宽
1
| tc class add dev eth0 parent 1: classid 1:1 htb rate 1mbps ceil 1mbps prio 0
|
- 作用:创建子类
1:1
,为 SSH 预留 1Mbps 固定带宽(最低保障 + 上限)。
- 参数解析:
parent 1:
: 父队列为根队列 1:
。
classid 1:1
: 子类的标识符。
rate 1mbps
: 保证带宽(必须满足的最低速率)。
ceil 1mbps
: 最大带宽(硬性上限)。
prio 0
: 最高优先级(数值越小优先级越高)。
3. 标记 SSH 流量
1
| iptables -A OUTPUT -t mangle -p tcp --sport 22 -j MARK --set-mark 0x1
|
- 作用:使用
iptables
在 OUTPUT
链的 mangle
表中标记所有源端口为 22
(SSH 默认端口)的流量,设置标记值 0x1
。
- 关键点:
tc
根据此标记识别 SSH 流量。
4. 将标记流量分配到预留类
1
| tc filter add dev eth0 parent 1: protocol ip handle 0x1 fw flowid 1:1
|
- 作用:将标记为
0x1
的流量(SSH)分配到预留的 1:1
类。
- 参数解析:
handle 0x1
: 匹配 iptables
设置的标记值。
flowid 1:1
: 流量指向 1:1
类。
5. 其他流量分配
1
| tc class add dev eth0 parent 1: classid 1:9999 htb rate 1000mbps ceil 1000mbps prio 7
|
- 作用:创建默认子类
1:9999
,用于处理非 SSH 流量。
- 参数解析:
prio 7
: 最低优先级(数值越大优先级越低)。
rate
和 ceil
设为网卡最大带宽(假设为 1Gbps)。
二、CPU 优先级保障
1. cgroups v2 硬性隔离
1 2
| mkdir /sys/fs/cgroup/ssh_reserved echo "cpu.weight: 100" > /sys/fs/cgroup/ssh_reserved/cpu.weight
|
- 作用:创建 cgroup
ssh_reserved
,设置 CPU 权重为 100
(默认值为 100
,更高权重会分配更多 CPU 时间)。
- 原理:在 cgroups v2 中,
cpu.weight
控制 CPU 时间片的分配比例。例如,若其他进程总权重为 100
,SSH 的 100
权重将确保其至少获得 100/(100+100)=50%
的 CPU 时间。
1
| systemctl set-property sshd.service CPUWeight=100
|
- 作用:通过
systemd
直接修改 sshd
服务的 CPU 权重,使其始终运行在 ssh_reserved
cgroup 中。
2. 实时调度策略(RT Priority)
1
| chrt -rr 99 /usr/sbin/sshd -D
|
- 作用:强制
sshd
以实时调度策略运行,优先级为 99
(最高为 99
)。
- 参数解析:
-rr
: 使用 SCHED_RR
调度策略(时间片轮转实时调度)。
- 风险提示:实时进程可能阻塞系统关键任务,需谨慎使用。
三、内存保护
1. OOM Killer 调整
1
| echo -1000 > /proc/$(pgrep -f "/usr/sbin/sshd")/oom_score_adj
|
- 作用:将
sshd
进程的 OOM(Out-Of-Memory)评分调整为 -1000
,使其成为 OOM Killer 最后杀死的进程。
- 原理:
oom_score_adj
范围是 -1000
到 1000
,值越低越不易被杀死。
2. cgroups 内存硬限制
1 2
| mkdir /sys/fs/cgroup/memory/limited_group echo 8G > /sys/fs/cgroup/memory/limited_group/memory.limit_in_bytes
|
- 作用:创建 cgroup
limited_group
,限制其内存使用上限为 8GB
。
1 2 3
| for pid in $(pgrep -v -f "sshd|systemd"); do echo $pid > /sys/fs/cgroup/memory/limited_group/cgroup.procs done
|
- 作用:将所有非
sshd
和 systemd
进程移入 limited_group
,限制它们的内存使用,从而为 SSH 预留内存。
四、Systemd 服务强化
1 2 3 4 5 6
| [Service] CPUWeight=100 MemoryHigh=2G OOMScoreAdjust=-1000 IOSchedulingClass=realtime
|
- 作用:通过 systemd 覆盖配置为
sshd
服务设置:
CPUWeight=100
: CPU 权重。
MemoryHigh=2G
: 内存使用软限制(允许短暂超出,但会触发回收)。
OOMScoreAdjust=-1000
: 免疫 OOM Killer。
IOSchedulingClass=realtime
: 磁盘 I/O 优先级为实时。
五、终端响应优化
1. SSH 保活配置
1 2 3 4
| ClientAliveInterval 30 ClientAliveCountMax 3 TCPKeepAlive yes
|
- 作用:
ClientAliveInterval 30
: 每 30 秒向客户端发送保活消息。
ClientAliveCountMax 3
: 连续 3 次无响应后断开连接。
TCPKeepAlive yes
: 启用 TCP 层保活机制。
2. TTY 资源隔离
1 2 3
| cgcreate -g cpu,memory:/tty_priority cgset -r cpu.shares=1024 /tty_priority cgset -r memory.limit_in_bytes=512M /tty_priority
|
- 作用:创建
tty_priority
cgroup,为 TTY 分配独立的 CPU 和内存资源。
1
| cgexec -g cpu,memory:/tty_priority /bin/bash
|
- 作用:启动一个 Shell 进程,并将其移入
tty_priority
cgroup。
六、持久化配置
1. 固化 tc
规则
将 tc
命令写入 /etc/rc.local
或 NetworkManager 脚本,确保重启后规则仍生效。
2. cgroups 固化
1 2
| echo "d /sys/fs/cgroup/ssh_reserved 0755 root root" > /etc/tmpfiles.d/ssh-reserved.conf
|
3. 内核参数调整
1
| echo "vm.overcommit_memory=2" >> /etc/sysctl.conf
|
- 作用:设置内存分配策略为
2
(严格模式),禁止超额分配内存。
注意事项
- 内核支持:需确认内核支持
cgroups v2
、HTB
算法等特性。
- 硬件资源:预留资源需根据实际带宽、CPU 核心数、内存大小调整。
- 实时调度风险:
SCHED_RR
可能阻塞系统关键进程,建议仅在极端场景下启用。
- 兼容性:部分命令在不同发行版中可能有差异(如
systemd
版本、tc
语法)。
通过以上配置,SSH 服务将在网络、CPU、内存等多层资源竞争中获得最高优先级,确保其在极端负载下仍可用。