# 配置

# sysctl

:一个内置命令,用于读取、配置系统运行时的内核参数。

  • 用法:
    sysctl
            <var>               # 读取指定参数的值
            [-w] <var>=<value>  # 设置指定参数的值
            -a                  # 显示所有可修改的内核参数
            -p <file>           # 加载指定文件中的配置参数(默认是 /etc/sysctl.conf 文件)
    
  • 执行 sysctl -a 会从 /proc/sys/ 目录下的各个文件读取内核参数(超过一千个),然后打印到终端。
    • 例如,参数 net.ipv4.tcp_keepalive_time 对应的文件是 /proc/sys/net/ipv4/tcp_keepalive_time
  • 用 sysctl 命令可修改运行时参数,但是在主机重启时会复原。
    • 将配置参数保存到 /etc/sysctl.conf 文件中才能永久生效。系统每次重启时会自动加载该文件,用户也可以执行 sysctl -p 主动加载。
  • sysctl.conf 配置文件采用 INI 格式。
    • 键值对用 = 分隔,中间的空格会被忽略。
    • 值通常是字符串型、整型或布尔型。
      • 其中,布尔型用 1、0 分别表示 True、False 。

# 配置参数

  • 参考文档:

  • 关于进程:

    kernel.pid_max        = 32768   # 整个主机中,同时存在 PID 的最大数量。该参数的默认值可能较小,建议主动调大,比如 10 万
    kernel.threads-max    = 126935  # 整个主机中,同时存在线程的最大数量。该参数的默认值与主机内存成正比,通常不需要修改
    
    • 如果达到上述限制,则大部分 Linux 命令会执行失败,报错:fork:Cannot allocate memory
    • 整个主机可运行的进程数远小于 pid_max ,因为 Linux 的线程属于 LWP 进程,创建线程也会占用 PID 。
      • 如果想准确限制最大进程数,则可使用 ulimit -u
      • 可执行 ps -eT | wc -l ,统计当前主机的线程总数。
    • 单个进程可以创建的线程数,还受内存限制,不能超过 virtual_memory_size / stack_size
  • 关于文件:

    fs.file-max = 791098            # 整个主机中,同时打开文件描述符的最大数量
        # 每个文件的 inode、dcache 大概占用 1k 内存。默认设置的 fs.file-max 会允许文件占用可用内存的 10%
    

# memory

  • 关于内存:
    vm.max_map_count      = 65530   # 每个进程在虚拟内存空间中,最多创建多少个 VMA 区域
    vm.swappiness         = 30      # 使用 swap 内存的推荐度,取值范围为 0~100 ,0 表示禁用。Linux 会根据该参数,自动决定使用多少 swap 内存
    
    vm.overcommit_memory  = 0       # 是否允许内核分配给所有进程的内存量,超过物理内存的总量。因为分配的内存不一定会被进程实际使用
        # 0 :根据启发式算法适量允许
        # 1 :始终允许,此时可能因为物理内存不足而触发 OOM
        # 2 :始终不允许超过物理内存的 vm.overcommit_ratio 百分比,此时不会触发 OOM ,但可能因为内存不足而不能创建新进程
    vm.overcommit_ratio   = 50
    vm.oom_kill_allocating_task = 0
        # 执行 OOM 的策略。取值如下:
        # 0 :扫描全部进程,选出 oom_score 最大的进程然后杀死
        # 1 :杀死当前申请内存而导致内存不足的进程
    vm.panic_on_oom       = 0       # 是否不执行 OOM ,而是让系统重启
    
    vm.dirty_expire_centisecs = 3000  # 每隔多久同步一次脏页到磁盘,单位为 0.01 秒
    vm.dirty_background_bytes = 0     # 当脏页的体积超过该值时,同步一次。默认为 0 ,表示忽略
    vm.dirty_background_ratio = 10    # 当脏页在总物理内存的占比,超过该百分比时,同步一次
    
    vm.nr_hugepages = 0             # 让内核创建多少个 Huge Page (包括已使用的、未使用的)
    vm.nr_overcommit_hugepages = 0  # 如果 nr_hugepages 全被使用,允许超额创建多少个 Huge Page
        # 通过 nr_hugepages 创建的 Huge Page ,即使未被进程使用,也会保留在物理内存中
        # 通过 nr_overcommit_hugepages 创建的 Huge Page ,一旦不被进程使用,就会被释放,变成普通 page
    

# net

  • 关于网络:

    net.ipv4.icmp_echo_ignore_all = 0         # 是否忽略所有 icmp 包,这样本机就不能被 ping 到
    net.ipv4.icmp_echo_ignore_broadcasts = 1  # 是否忽略发向广播地址的 icmp 包
    net.ipv4.ip_forward           = 1         # 当网口收到一个数据包时,如果数据包的目标 IP 不匹配当前网口,是否转发给其它网口、其它主机,从而实现路由转发功能。建议设置为 1
    net.ipv4.ip_local_port_range  = 10000  65535   # 允许 Socket 绑定的本地端口范围。默认值为 10000 65535 或 32768 60999
    
    # Linux 会自动调整每个 Socket 的接收缓冲区、发送缓冲区占用的内存容量
    net.ipv4.tcp_moderate_rcvbuf = 1          # 是否自动调整每个 Socket 接收缓冲区的容量
    net.core.rmem_default = 212992            # 每个 Socket 接收缓冲区的默认容量,默认为 208 KB
    net.core.wmem_default = 212992            # 每个 Socket 发送缓冲区的最大容量
    net.core.rmem_max     = 212992            # 最大容量
    net.core.wmem_max     = 212992
    net.ipv4.tcp_rmem     = 4096    87380   6291456   # 每个 TCP Socket 接收缓冲区的容量,分别指定最小值、默认值、最大值,单位 bytes 。默认为 4KB、86KB、6MB
    net.ipv4.tcp_wmem     = 4096    16384   4194304   # 每个 TCP Socket 发送缓冲区的容量。默认为 4KB、16KB、4MB
    net.ipv4.tcp_mem      = 88500   118001  177000    # 整个主机中,全部 TCP Socket 缓冲区的容量,分别指定最小值、压力值、最大值,单位为内存页 pages 。默认值与主机内存成正比
        # 全部容量超过压力值时,Linux 开始节省给 Socket 分配的内存。此时每个 TCP Socket 缓冲区的容量可能降低,但不会低于每个的最小值
        # 全部容量超过最大值时,Linux 禁止给 Socket 分配新的内存。此时不能建立新的 TCP 连接
    net.ipv4.udp_mem      = 574224  765634  1148448
    net.ipv4.udp_rmem_min = 4096
    net.ipv4.udp_wmem_min = 4096
    
  • 关于 ARP :

    # 默认配置
    net.ipv4.neigh.default.base_reachable_time_ms = 30000   # 每条 ARP 记录大概缓存多少毫秒。如果一个 ARP 记录长时间未被使用,则从 reachable 状态改为 stale 状态
    net.ipv4.neigh.default.gc_stale_time = 60   # 每隔多久,将长时间未使用的 ARP 记录改为 stale 状态。此时 stale 记录尚未删除,但已失效,访问其 IP 时需要重新通过 ARP 协议查询 Mac 地址
    net.ipv4.neigh.default.gc_interval = 30     # 每隔多久 GC 一次,删除 stale 状态的 ARP 记录
    net.ipv4.neigh.default.gc_thresh1 = 128     # 至少缓存多少条 ARP 记录时,才开始 GC
    net.ipv4.neigh.default.gc_thresh3 = 1024    # 最多缓存多少条 ARP 记录
    
    # 每个网口有一份配置,继承默认配置
    net.ipv4.neigh.eth0.gc_interval = 30
    
  • 关于 TCP 通信:

    # 建立 TCP 连接
    net.core.somaxconn            = 128     # 限制调用 listen() 函数时,backlog 的最大值
    net.ipv4.tcp_max_syn_backlog  = 128     # SYN 队列的最大长度,默认值与内存成正比
    net.ipv4.tcp_syn_retries      = 6
        # TCP 握手的第一步,client 发送 SYN 包之后,如果超过一定时间未收到 server 的回复,则认为丢包了,于是重传 SYN 包
        # 第一次发送时,重传超时 RTO 为 1 秒。第 n 次发送时,RTO 为 2^(n-1) 秒,比如 1、2、4、8、16 秒
        # 最大重传次数为 tcp_syn_retries(不包括第一次发送),超过则放弃 TCP 握手
    net.ipv4.tcp_synack_retries   = 5
        # TCP 握手的第二步,server 发送 SYN+ACK 包之后,如果超过一定时间未收到 client 的回复,则认为丢包了,于是重传 SYN+ACK 包
        # 重传超时 RTO 与 tcp_syn_retries 一致
        # 最大重传次数为 tcp_synack_retries(不包括第一次发送),超过则放弃 TCP 握手
    net.ipv4.tcp_syncookies       = 1
        # 是否启用 SYN Cookies 功能。取值如下:
        # 0 :不启用
        # 1 :默认值,当 SYN 队列满了时启用
        # 2 :总是启用
    net.ipv4.tcp_abort_on_overflow= 0
        # 当 accept 队列满了时,server 怎么处理新的 ESTABLISHED 连接。取值如下:
        # 0 :默认值,丢弃 client 发来的 ACK 包
        # 1 :回复一个 RST 包给 client ,表示拒绝连接
    
    # 关闭 TCP 连接
    net.ipv4.tcp_fin_timeout      = 60      # 如果 Socket 保持在 FIN-WAIT-2 状态超过该时长,则关闭 Socket 。通常设置为 2*MSL 时长
    net.ipv4.tcp_max_orphans      = 65536   # 限制 orphan Socket 的最大数量,超过限制则自动回收。默认值与主机内存成正比
    net.ipv4.tcp_max_tw_buckets   = 65536   # 限制 TIME-WAIT 状态的 Socket 的数量,超过限制则会立即关闭 Socket 。默认值与内存成正比
    
    # 一些 TCP 配置参数
    net.ipv4.tcp_window_scaling = 1         # 是否启用 Window scale 功能,默认启用
    net.ipv4.tcp_congestion_control = cubic # 采用哪种 TCP 拥塞控制算法
    net.ipv4.tcp_slow_start_after_idle = 1  # 是否启用慢启动重启(Slow-Start Restart,SSR),当 TCP 连接空闲一段时间之后,重置拥塞窗口 cwnd ,重新慢启动
    
    # TCP 长连接:
    # 如果本机的一个 TCP 连接长达 tcp_keepalive_time 秒没有用于数据通信,则探测一下对方主机是否仍然在线。每隔 tcp_keepalive_intvl 秒探测一次,最多探测 tcp_keepalive_probes 次
    # 因此一个 TCP 连接空闲时长达到 tcp_keepalive_time + tcp_keepalive_intvl * tcp_keepalive_probes 之后会被关闭
    # 减小这些参数,可以尽早关闭无用的 Socket ,比如 CLOSE_WAIT 状态的 Socket
    net.ipv4.tcp_keepalive_time   = 7200
    net.ipv4.tcp_keepalive_intvl  = 75
    net.ipv4.tcp_keepalive_probes = 9
    
    # 复用 TCP 连接:如果同一对 client、server 多次建立 TCP 连接,旧连接的 Socket 处于 TIME-WAIT 状态而尚未关闭,则建立新连接时,是否允许复用旧 Socket ,而不是创建新 Socket
    net.ipv4.tcp_tw_reuse         = 0       # 当 client 多次向同一个 server 建立 TCP 连接时,是否允许复用 TIME-WAIT 状态的 Socket 。该参数只能作用于 client 即主动连接方,对 server 无效
    net.ipv4.tcp_tw_recycle       = 0       # 当 server 多次被同一个 client 建立 TCP 连接时,是否允许复用 TIME-WAIT 状态的 Socket 。该行为可能搞混在同一 NAT 网关之后的不同 client ,因此从 Linux 4.12 开始弃用
    
    # TFO(TCP Fast Open):Linux 3.7 开始提供的一个功能,能将 TCP 三次握手缩短一个 RTT 耗时,但存在安全隐患,因此默认禁用
    # 原理:如果同一对 client、server 多次建立 TCP 连接,则 client 可在本机记录 TFO cookie ,等到重新建立连接时,在握手的第一步发送 SYN 包,并在 TCP Options 中包含 cookie 来证明自己的身份。使得 server 在握手的第二步之后,就可以开始向 client 传输数据,然后依然进行握手的第三步
    # client、server 需要同时启用 TFO 功能,才能生效
    net.ipv4.tcp_fastopen = 0
    

# nf_conntrack

  • 关于 nf_conntrack :

    net.netfilter.nf_conntrack_max = 65536      # nf_conntrack 表中最多记录多少个 网络连接。其默认值与主机 CPU 核数、内存成正比
    net.netfilter.nf_conntrack_buckets = 16384  # nf_conntrack 表中的 bucket 数量,需要设置为 conntrack_max / bucket_size
        # nf_conntrack 是一个哈希表,由大量 bucket 组成。每个 bucket 是一个链表结构,可以记录 bucket_size 条网络连接。每条记录大概占用 320 bytes 的内核内存
        # bucket_size 的值取决于用户设置的 `conntrack_max / conntrack_buckets` 。建议在 64 位操作系统让 bucket_size 为 8 ,然后按比例配置 conntrack_max 与 conntrack_buckets
        # 执行 echo 16384 > /sys/module/nf_conntrack/parameters/hashsize 可修改 nf_conntrack_buckets ,但重启主机时会复原
        # 需要再执行 echo 'options nf_conntrack hashsize=16384' >> /etc/modprobe.d/iptables.conf ,永久设置该参数
    
    net.netfilter.nf_conntrack_tcp_timeout_syn_recv    = 60
    net.netfilter.nf_conntrack_tcp_timeout_syn_sent    = 120
    net.netfilter.nf_conntrack_tcp_timeout_established = 432000  # 如果一个 ESTABLISHED 状态的 TCP 连接,保持空闲超过该时长,则删除该记录。默认为 432000 秒即 5 天,建议缩短为几小时
    net.netfilter.nf_conntrack_tcp_timeout_time_wait   = 120     # 默认为 120 秒,对应 2*MSL ,可改为 10 秒
    net.netfilter.nf_conntrack_tcp_timeout_close_wait  = 60
    net.netfilter.nf_conntrack_udp_timeout = 30
    net.netfilter.nf_conntrack_icmp_timeout = 30
    
  • 如果服务器被大量客户端并发访问(比如每秒访问上百次、上千次),则可能遇到性能问题,比如响应耗时增加、丢包率增加。解决方案如下:

    • 首先,监控服务器的 CPU、内存、磁盘、带宽等资源的使用率。如果某一种接近 100% ,则可能是这种资源遇到了性能瓶颈。
    • 然后,考虑降低上述资源的使用率,比如:
      • 花钱扩容服务器资源。
      • 优化服务器、客户端的程序代码、配置参数,减少占用的服务器资源。
      • 优化服务器的内核参数,支持更多并发 TCP 连接:
        • 增加 ulimit -n 参数,允许同时打开大量 Socket 文件。
        • nf_conntrack 记录满了时,会拒绝新的 TCP、UDP 等网络连接,因此需要增加 nf_conntrack_max 、减小 nf_conntrack_xx_timeout_xx 。
        • 频繁建立新的 TCP 连接时,建议调小 net.ipv4.tcp_keepalive_time ,更早关闭空闲连接。
        • 并发连接过多时,需要增加 net.ipv4.tcp_mem 的最大值、增加主机内存。因为每个 Socket 的接收缓冲区加发送缓冲区,可能占用 4KB~10MB 内存。
          • 每个 TCP Socket 默认的 tcp_rmem、tcp_wmem 一般足够使用,不需要修改,因为 RDP 不会很大。

# ulimit

:一个内置命令,用于设置 shell 终端占用的系统资源上限。

  • 命令:

    ulimit
          -a        # 显示各种资源的限制信息
            -H      # 选择硬限制
            -S      # 选择软限制
          -u        # 显示 -u 参数的值
          -u <n>    # nproc ,同时运行的进程数最多为 n 个(不能限制 root 用户)
          -m <n>    # rss   ,占用的常驻内存最多为 n KB(大部分 Linux 发行版不支持该限制)
          -v <n>    # 占用的虚拟内存最多为 n KB
          -s <n>    # stack ,stack 的最大体积为 n KB
          -t <n>    # cpu   ,每个进程累计最多占用的 CPU 时长,超过限制时则发送 SIGKILL 信号终止该进程。进程处于 Sleeping 状态时不占用 CPU 时长
          -n <n>    # nofile ,同时打开的文件描述符数量最多为 n
          -f <n>    # fsize ,创建单个文件的大小最大为 n blocks
    
  • 例:

    ulimit -a               # 显示限制(默认是软限制)
    ulimit -aH              # 显示硬限制
    ulimit -nH unlimited    # 设置硬限制
    ulimit -nS 1024         # 设置软限制
    ulimit -n  unlimited    # 同时设置硬限制和软限制
    
    • 当一个进程占用的资源超过限制时,会被立即杀死。
    • 软限制的值不能超过硬限制。
    • 非 root 用户无权调大硬限制,只能调小硬限制、改变软限制。
  • ulimit 的配置只能作用于当前 shell 终端。而 /etc/security/limits.conf 文件中的配置会对指定用户永久生效,如下:

    *    hard    nofile       unlimited
    *    soft    nofile       1024
    *    hard    maxlogins    3
    *    soft    maxlogins    3
    

    每行的格式如下:

    <domain> <type> <item> <value>
    
    • domain :用户名或组名,也可用 * 匹配所有。
    • type :取值为 hard 或 soft ,表示硬限制或软限制。
    • item、value :限制项及其值。比如:
      • maxlogins :当前用户同时登录的最大数量
      • maxsyslogins :系统同时登录的最大数量