故事背景:某台VPN服务器在会话并发高峰期时出现低延迟+大量丢包现象,通过扩容增配+监控分析,已排除硬件性能和网络瓶颈问题,故深入排查

现象分析

  • 故障出现时性能指标显示正常,且通过硬件增配和网络扩容手段,并无明显改善

  • 故障复现:

    会话数小于某一阈值时,延迟正常,丢包率<1%

    会话数超过某一阈值时,故障复现,延迟正常,丢包率陡增至30%

定位问题

  • 延迟正常但丢包率增加,推测是TCP连接被ACK RESET,阻断连接联想到文件描述符限制,通过查看文件描述符和limit限制,发现端倪

    # 每建立一个TCP连接,操作系统就得分配一个文件描述符,linux 对可打开的文件描述符的数量分别作了三个方面的限制。
    # 系统默认限制1024,这里是在部署VPN服务时手动调整到了一百万
    [root@SSHVL26 ~]# cat /proc/sys/fs/file-max         # 系统限制
    1000000
    [root@SSHVL26 ~]# cat /proc/sys/fs/nr_open          # 进程限制
    1000000
    [root@SSHVL26 ~]# ulimit -n                         # 用户限制
    1000000 
    [root@SSHVL26 ~]# lsof |grep -i vpnserver |wc -l    # 当前vpn进程文件描述符连接数
    1118963
    ​
    # 可以明显看到文件描述符已经到达瓶颈甚至超出限制(至于为什么会超出限制暂时先不深究)

内核调优

  • 编辑/etc/sysctl.confsysctl -p 应用更改:

    # 指定系统中所有进程可以打开的文件描述符的最大数量
    fs.file-max = 6469558
    ​
    # 指定系统中文件描述符(file descriptor)的最大数量
    fs.nr_open = 6469558
    ​
    # 启用SYN cookies,用于防范SYN洪水攻击
    net.ipv4.tcp_syncookies = 1
    ​
    # 允许将TIME-WAIT状态的套接字重新用于新的连接
    net.ipv4.tcp_tw_reuse = 1
    ​
    # 控制是否启用TCP连接的TIME-WAIT快速回收功能,已被弃用
    net.ipv4.tcp_tw_recycle = 0
    ​
    # 指定了TCP连接中最后一个ACK之后等待套接字关闭的时间
    net.ipv4.tcp_fin_timeout = 30
    ​
    # 设置了TCP保活(keepalive)探测的时间间隔,单位为秒
    net.ipv4.tcp_keepalive_time = 1200
    ​
    # 设置了系统内核中用于存储TIME-WAIT套接字的最大数量
    net.ipv4.tcp_max_tw_buckets = 5000
    ​
    # 启用TCP Fast Open功能,并设置了服务器支持TFO的连接队列的最大长度
    net.ipv4.tcp_fastopen = 3
    ​
    # 指定了本地端口范围,用于分配本地端口号
    net.ipv4.ip_local_port_range = 10240 65535
    ​
    # 设置了TCP接收缓冲区的最大大小
    net.core.rmem_max=16777216
    ​
    # 设置了TCP发送缓冲区的最大大小
    net.core.wmem_max=16777216
    ​
    # 设置了TCP接收缓冲区的最小值、默认值和最大值
    net.ipv4.tcp_rmem=4096 87380 16777216
    ​
    # 设置了TCP发送缓冲区的最小值、默认值和最大值
    net.ipv4.tcp_wmem=4096 65536 16777216
    ​
    # 启用TCP连接的TIME-WAIT快速回收功能,已被弃用
    net.ipv4.tcp_tw_recycle = 1
    ​
    # 禁用TCP时间戳选项,可以提高系统的安全性
    net.ipv4.tcp_timestamps = 0
    ​
    # 禁用TCP窗口扩大选项,也可以提高系统的安全性
    net.ipv4.tcp_window_scaling = 0
    ​
    # 禁用TCP选择确认选项,也可以提高系统的安全性
    net.ipv4.tcp_sack = 0
    ​
    # 设置了网络设备队列的最大长度,用于存储尚未被处理的数据包
    net.core.netdev_max_backlog = 30000
    ​
    # 禁用TCP性能监控,可提高性能
    net.ipv4.tcp_no_metrics_save=1
    ​
    #net.core.somaxconn = 262144  # 注释掉,这是设置TCP连接队列的最大长度的参数,通常不建议修改它
    ​
    # 禁用SYN cookies,可根据实际需求调整
    net.ipv4.tcp_syncookies = 0
    ​
    # 设置了TCP最大孤儿连接数,用于处理在连接建立阶段客户端突然关闭的情况
    net.ipv4.tcp_max_orphans = 262144
    ​
    # 设置了TCP SYN队列的最大长度,用于存储未完成的半连接请求
    net.ipv4.tcp_max_syn_backlog = 262144
    ​
    # 设置了TCP SYNACK数据包的重传次数
    net.ipv4.tcp_synack_retries = 2
    ​
    # 设置了TCP SYN数据包的重传次数
    net.ipv4.tcp_syn_retries = 2

    文件描述符修改后,还需要修改limit限制/etc/security/limits.conf,当前会话注销重新登录后生效

    (注意:limit限制不能大于fs.file-max和fs.nr_open限制,否则SSH和TTY会无法登录)

    * soft nofile 6469558
    * hard nofile 6469558

    检查各项配置

    [root@SSHVL26 ~]# cat /proc/sys/fs/file-max
    6469558
    [root@SSHVL26 ~]# cat /proc/sys/fs/nr_open
    6469558
    [root@SSHVL26 ~]# ulimit -n
    6469558

    重新登录,重启VPN进程,观察连接数和故障复现情况

    [root@SSHVL26 ~]# cat /proc/{$PID}/limits
    Max open files            6469558              6469558              files
    
    [root@SSHVL26 ~]# lsof |wc -l
    1963070
    # 文件描述符连接已突破限制

    检查故障情况,此时丢包率稳定在<1%,VPN高并发故障排除。

关于Limit

  • /etc/security/limits.conf限制仅针对会话创建进程,系统service进程不受控制,因此如果是通过systemctl之类启动的服务,还是会被默认限制到1024,此时可以通过修改/etc/systemd/system.conf/etc/systemd/user.conf解除限制,重启系统后生效

    DefaultLimitNOFILE=6553560
    DefaultLimitNPROC=6553560