这篇文章上次修改于 899 天前,可能其部分内容已经发生变化,如有疑问可询问作者。

1 现象

# -w 表示只输出 HTTP 状态码及总时间,
# -o表 示将响应重定向到 /dev/null
# --connect-timeout 表示连接超时时间

$ curl -w 'Http code: %{http_code}\nTotal time:%{time_total}s\n' -o /dev/null --connect-timeout 10 http://192.168.0.30

...
Http code: 000Total time:10.001scurl: (28) Connection timed out after 10000 milliseconds

客户端连接超时,且没有收到服务端的响应。

2 排查

查看网络:

$ sar -n DEV 1
08:55:49        IFACE   rxpck/s   txpck/s    rxkB/s    txkB/s   rxcmp/s   txcmp/s  rxmcst/s   %ifutil
08:55:50      docker0      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
08:55:50         eth0  22274.00    629.00   1174.64     37.78      0.00      0.00      0.00      0.02
08:55:50           lo      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00

网络接收的 PPS 已经达到了 20000 多,但是 BPS 却只有 1174 kB,这样每个包的大小就只有 54(1174*1024/22274=54)。

用 tcpdump 查看小包是什么:

# -i eth0 只抓取eth0网卡,
# -n 不解析协议名和主机名
# tcp port 80 表示只抓取 tcp 协议并且端口号为 80 的网络帧

$ tcpdump -i eth0 -n tcp port 80

09:15:48.287047 IP 192.168.0.2.27095 > 192.168.0.30: Flags [S], seq 1288268370, win 512, length 
009:15:48.287050 IP 192.168.0.2.27131 > 192.168.0.30: Flags [S], seq 2084255254, win 512, length 
009:15:48.287052 IP 192.168.0.2.27116 > 192.168.0.30: Flags [S], seq 677393791, win 512, length 
009:15:48.287055 IP 192.168.0.2.27141 > 192.168.0.30: Flags [S], seq 1276451587, win 512, length 
009:15:48.287068 IP 192.168.0.2.27154 > 192.168.0.30: Flags [S], seq 1851495339, win 512, length 0
...

都是 SYN 包,可见是受到了 SYN Flood 攻击。

查看 SYN 包的数量:

$ netstat -n -p | grep SYN_REC | wc -l
193

3 缓解 DDos 攻击

限制单个攻击源:

# 限制syn并发数为每秒1次
$ iptables -A INPUT -p tcp --syn -m limit --limit 1/s -j ACCEPT

# 限制单个IP在60秒新建立的连接数为10
$ iptables -I INPUT -p tcp --dport 80 --syn -m recent --name SYN_FLOOD --update --seconds 60 --hitcount 10 -j REJECT

对 TCP 优化:

$ cat /etc/sysctl.conf

# 降低连接每个 SYN_RECV 失败时的默认的重试次数
sysctl -w net.ipv4.tcp_synack_retries=1

# 开启 SYN Cookies,不需要维护半开连接状态
sysctl -w net.ipv4.tcp_syncookies=1

要使 /etc/sysctl.conf 生效,需执行 sysctl -p 命令。写到 /etc/sysctl.conf 中后,重启仍然会生效。

参考

倪朋飞. Linux 性能优化实战.