转载自: keithmork From testerhome

netfilter/conntrack 相关内核参数往往是用 Linux 服务器的互联网小公司业务量上去之后遇到的第 3 个“新手怪”。(第 1 位:进程可用的 FD 不足,第 2 位:IP 临时端口不足 + TIME_WAIT 状态的连接过多导致无法建立新连接)

很多人以为 Linux 经过这么多年优化,默认参数应该“足够好”,其实不是。默认参数面向“通用”服务器,不适用于连接数和访问量比较多的场景。


症状

服务器负载正常,但请求大量超时,服务器/应用访问日志看不到相关请求记录。

在 dmesg 或 /var/log/messages 看到大量以下记录:

kernel: nf_conntrack: table full, dropping packet.


原因

服务器访问量大,内核 netfilter 模块 conntrack 相关参数配置不合理,导致 IP 包被丢掉,连接无法建立。

详细

nf_conntrack 模块在 kernel 2.6.15(2006-01-03 发布) 被引入,支持 IPv4 和 IPv6,取代只支持 IPv4 的 ip_connktrack,用于跟踪连接的状态,供其他模块使用。

需要 NAT 的服务都会用到它,例如防火墙、Docker 等。以 iptables 的 nat 和 state 模块为例:

  • nat:根据转发规则修改 IP 包的源/目标地址,靠 conntrack 记录才能让返回的包能路由到发请求的机器。
  • state:直接用 conntrack 记录的连接状态(NEW/ESTABLISHED/RELATED/INVALID 等)来匹配防火墙过滤规则。

nf_conntrack 跟踪所有网络连接,记录存储在 1 个哈希表里。首先根据五元组算出哈希值,分配一个桶,如果有冲突就在链表上遍历,直到找到一个精确匹配的。如果没有匹配的则新建。

即使来自客户端的访问量不多,内部请求多的话照样会塞满哈希表,例如 ping 本机也会留下这么一条记录:

ipv4     2 icmp     1 29 src=127.0.0.1 dst=127.0.0.1 type=8 code=0 id=26067 src=127.0.0.1 dst=127.0.0.1 type=0 code=0 id=26067 mark=0 use=1

连接记录会在哈希表里保留一段时间,根据协议和状态有所不同,直到超时都没有收发包就会清除记录。如果服务器比较繁忙,新连接进来的速度远高于释放的速度,把哈希表塞满了,新连接的数据包就会被丢掉。此时 netfilter 变成了一个黑洞, 这发生在3层(网络层),应用程序毫无办法。

如果有人 DDoS 攻击的话情况更糟,无论是空连接攻击还是简单地用短连接发大量请求都能轻易塞满哈希表。或者更隐蔽点,研究了计算 conntrack hash 值的算法后,构造很多 hash 一致的不同五元组的数据包,让大量记录堆在同一个桶里,使得遍历超长的冲突链表的开销大得难以接受。在当前的内核 conntrack 模块实现中,这是无法避免的(除非关掉不用),因为所有鸡蛋都在一个篮子里面。


诊断

netfilter 相关内核参数一览

sudo sysctl -a | grep conntrack# 如果找不到,恭喜,不用操心这问题了

查看超时相关参数

sudo sysctl -a | grep conntrack | grep timeout

所谓超时是清除 conntrack 记录的秒数,从某个连接收到最后一个包后开始倒计时, 倒数到 0 就会清除记录,中间收到包会重置。

不同协议的不同状态有不同的超时时间。(注意记录里的状态只是个标识,跟连接本身的状态不一定是一一映射的关系,跟协议的标准或实现更是完全没有关系。)

哈希表设置

查看哈希表大小(桶的数量)

sudo sysctl net.netfilter.nf_conntrack_buckets# 只读

查看最大跟踪连接数

进来的连接数超过这个值时,新连接的包会被丢弃。

sudo sysctl net.netfilter.nf_conntrack_max# 默认 nf_conntrack_buckets * 4# max 是 bucket 的多少倍决定了每个桶里的链表有多长,因此默认链表长度为 4

比较现代的系统(Ubuntu 16+, CentOS 7+)里,64 位,8G 内存的机器,max 通常默认为 262144,bucket 为 65536。随着内存大小翻倍这 2 个值也翻倍。

【注意】云服务厂商可能有不同的默认设置:

  • AWS 8G 以上这 2 个值似乎不再增加,64G 内存的机器和 8G 内存的机器一样。
  • 阿里云目前(2018年)CentOS 7+ 的机器上似乎还在用 07 年 CentOS 5 时代的默认配置:max 为 65536,bucket 为 16384。因此如果生产环境用阿里云服务器又没人了解这块的话,陷阱会来得特别早。

查看 netfilter 模块加载时的默认值

sudo dmesg | grep conntrack# 找类似这样的记录:# nf_conntrack version 0.5.0 (65536 buckets, 262144 max)

哈希表使用情况

sudo sysctl net.netfilter.nf_conntrack_count# 只读# 这个值跟 sudo conntrack -L 或 /proc/net/nf_conntrack (如果有这文件)里的条目数一致

这个值跟 net.netfilter.nf_conntrack_buckets 的值比较。

当哈希表大部分桶不为空时(计算 得出约 69%,Python 的 dict 用 2/3,Java 的 HashMap 用 75%)哈希冲突的概率会增大,性能从 O(1) 退化为读链表的 O(n),建议及时扩容。

网上有说法 “nf_conntrack_count 的值持续超过 nf_conntrack_max 的 20% 就该考虑扩容”也是这原因。因为 bucket 的值默认是 max 的 25%,用了 max 的 20% 也就是 80% 的桶都有元素了(假设没冲突)。

跟踪连接记录

# Ubuntu 通常没有 /proc/net/nf_conntrack 文件,用 conntrack 命令代替,输出一样sudo conntrack -L -o extended | tail -n 50# CentOS:sudo tail -n 50 /proc/net/nf_conntrack# 输出例:# ipv4     2 tcp      6 431999 ESTABLISHED src=10.0.13.67 dst=10.0.13.109 sport=63473 dport=22 src=10.0.13.109 dst=10.0.13.67 sport=22 dport=63473 [ASSURED] mark=0 secctx=system_u:object_r:unlabeled_t:s0 zone=0 use=2# 记录格式:# 网络层协议名、网络层协议编号、传输层协议名、传输层协议编号、记录失效前剩余秒数、连接状态(不是所有协议都有)# 之后都是 key=value 或 flag 格式,1 行里最多 2 个同名 key(如 src 和 dst),第 1 次出现的来自请求,第 2 次出现的来自响应# flag:# [ASSURED]  请求和响应都有流量# [UNREPLIED]  没收到响应,哈希表满的时候这些连接先扔掉

四层协议类型和连接数:

sudo conntrack -L -o extended | awk '{sum[$3]++} END {for(i in sum) print i, sum[i]}'# 或:sudo cat /proc/net/nf_conntrack | awk '{sum[$3]++} END {for(i in sum) print i, sum[i]}'

TCP 连接各状态对应的条数:

sudo conntrack -L -o extended | awk '/^.*tcp.*$/ {sum[$6]++} END {for(i in sum) print i, sum[i]}'# 或:sudo cat /proc/net/nf_conntrack | awk '/^.*tcp.*$/ {sum[$6]++} END {for(i in sum) print i, sum[i]}'

三层协议类型和连接数:

sudo conntrack -L -o extended | awk '{sum[$1]++} END {for(i in sum) print i, sum[i]}'# 或:sudo cat /proc/net/nf_conntrack | awk '{sum[$1]++} END {for(i in sum) print i, sum[i]}'

连接数最多的 10 个 IP 地址:

sudo conntrack -L -o extended | awk '{print $7}' | cut -d "=" -f 2 | sort | uniq -c | sort -nr | head -n 10# 或:sudo cat /proc/net/nf_conntrack | awk '{print $7}' | cut -d "=" -f 2 | sort | uniq -c | sort -nr | head -n 10

stackoverflow - details of /proc/net/ip_conntrack / nf_conntrack


配置

A. 关闭使用 NAT 的程序

最常见的是防火墙,目前第 2 常见的可能是 docker。依赖 netfilter 模块的服务关掉之后,通常 sudo sysctl -a | grep conntrack 就找不到相关的参数了。

对不直接暴露在公网,也不使用 NAT 转发的服务器来说,关闭 Linux 防火墙是最简单的办法,还避免了防火墙/netfilter 成为网络瓶颈。使用公有云的话可以用厂商提供的安全服务,通常是独立于你租的云服务器的,不消耗资源,比自己用系统防火墙设一大堆规则好得多。

Ubuntu 防火墙

sudo ufw disable

firewalld

CentOS 7.x 默认安装。

sudo systemctl stop firewalldsudo systemctl disable firewalld

iptables

CentOS 6.x 默认安装。

# 使用 SystemV init 管理的旧系统:sudo service iptables stopsudo chkconfig --del iptables# 网上有些老文章说关了 iptables 之后,用 "iptables -L -n" 等命令查看防火墙规则也会导致 nf_conntrack 重新加载,实测并不会# 使用 systemd 管理的新系统:sudo systemctl stop iptablessudo systemctl disable iptables

dockerd

系统是最小安装的话应该不会自带。如果发现系统里有 docker 的网卡在,又确定没有地方用到 docker 的话就关掉:

sudo systemctl stop dockersudo systemctl disable docker

如果 conntrack 相关参数还没消失,看看模块是不是还在:

lsmod | egrep "Module|ip_table|iptable|ip6|ipt|nat|conntrack"# 有可能会匹配到不相关的,最好对照一下这里find /lib/modules/$(uname -r) -type f -name '*.ko*' | grep netfilter# 查看模块详细信息modinfo <module>

禁用模块:

sudo modprobe [-f] -r <module> [<module2> ...]# 或:sudo rmmod [-f] <module># 未使用(Used by 栏为 0)的模块才能禁用。# 如果 Used by 不为 0,先禁用后面列出的模块。# 如果后面没模块名,就是被进程使用。# 没有简单的方法能查到调用这些模块的都是什么进程,基本靠猜。# 查看启动信息,看有没有有用的线索(多半没有)dmesg | egrep "ip_table|netfilter|conn"

B. 调整内核参数

如果调用 netfilter 的进程不能关,或查不出什么进程在用,就要靠调整参数来尽量推迟出问题的时间。

主要设置项:

  • 哈希表扩容(nf_conntrack_bucketsnf_conntrack_max
  • 让里面的元素尽快释放(超时相关参数)

nf_conntrack_buckets 和 nf_conntrack_max 的默认值怎么来的

根据这篇 08 年的 wikinf_conntrack_max 的默认值算法为:

CONNTRACK_MAX = RAMSIZE (in bytes) / 16384 / (ARCH / 32) 
  • 其中 ARCH 为 CPU 架构,值为 32 或 64。
  • 即:32 位系统使用内存的 1/16384,64 位系统再减半。
  • 对于 64 位 8G 内存的机器:(8 * 1024^3) / 16384 / (64 / 32) = 262144

nf_conntrack_buckets 默认值算法为:

HASHSIZE = CONNTRACK_MAX / 4# 比较早的版本是除以 8# 这里的 4 或 8 就是每个桶里的链表最大长度
  • 对于 64 位 8G 内存的机器:262144 / 4 = 65536

给哈希表扩容的影响

主要是内存使用增加。32 位系统还要关心内核态的地址空间够不够。

netfilter 的哈希表存储在内核态的内存空间,这部分内存不能 swap,操作系统为了兼容 32 位,默认值往往比较保守。

  • 32 位系统的虚拟地址空间最多 4G,其中内核态最多 1G,通常能用的只有前 896M。
    • 给 netfilter 分配太多地址空间可能会导致其他内核进程不够分配。1 条跟踪记录 300 字节左右,因此当年 nf_conntrack_max 默认 65535 条,占 20多MB。
  • 64 位系统的虚拟地址空间有 256TB,内核态能用一半,只需要关心物理内存的使用情况。

计算内存使用的公式还是来自上面的 wiki:

size_of_mem_used_by_conntrack (in bytes) = CONNTRACK_MAX * sizeof(struct ip_conntrack) + HASHSIZE * sizeof(struct list_head)
  • sizeof(struct ip_conntrack) 在不同架构、内核版本、编译选项下不一样。这里按 352 字节算。
    • 老文章说模块启动时会在 syslog 里打印这个值,但现在没有。
  • sizeof(struct list_head) = 2 * size_of_a_pointer(32 位系统的指针大小是 4 字节,64 位是 8 字节)
  • 64 位系统,8G 内存的机器,按默认 CONNTRACK_MAX 为 262144,HASHSIZE 为 65536 时:262144 * 352 + 65536 * 8 = 92798976(88.5 MB)

互联网公司的服务器通常内存没那么紧张,可以放开点:

  • CONNTRACK_MAX 为 1048576,HASHSIZE 为 262144 :1048576 * 352 + 262144 * 8 = 371195904(354 MB)

等业务发展到 nf_conntrack_count 经常保持在 18万(bucket 的 2/3)以上时再考虑翻倍。

(测试方法:压测工具不用 keep-alive 发请求,调大 nf_conntrack_tcp_timeout_time_wait,单机跑一段时间就能填满哈希表。观察响应时间的变化和服务器内存的使用情况。)

调整哪些超时时间

如果你的程序需要读取 conntrack 记录,或者服务器设了复杂的 iptables 规则(同样需要读取 conntrack 记录),超时时间的设置需要非常谨慎:

iptables的nf_conntrack相关参数引起两个问题 , 2015-03

dog250 - Operation not permitted引发的惊魂72小时, 2013-07 (前面全是错误的排查方向,拉到第 6 点开始入正题)

dog250 - 再次深入到ip_conntrack的conntrack full问题, 2012-02

如果 conntrack 记录对你不重要,用之前的命令查一下哪种协议哪种状态的连接最多,尝试把对应的超时参数调小。占比很少或根本用不到的可以不管。

例如 Nginx 服务器上可能会看到 90% 以上的记录都是 TIME_WAIT 状态(Nginx 连后端服务默认用短连接)。

对于通外网的服务器,考虑调整以下参数,减少 DDoS 的危害:

  • net.netfilter.nf_conntrack_tcp_timeout_established:默认 432000 (5天)
    • 这个值对应的场景是 “双方建立了连接后一直不发包,直到 5 天后才发” ……
    • 但默认 keep-alive 超时时间只有 2 小时 11 分(net.ipv4.tcp_keepalive_time + net.ipv4.tcp_keepalive_intvl * net.ipv4.tcp_keepalive_probes),由于超时关 socket 不发包,conntrack 无法根据包头的标识知道状态的变化,记录会一直处于 ESTABLISHED 状态,直到 5 天后倒计时结束才删掉。
    • 空连接攻击的最佳目标。攻击者把 IP 包头的源地址改成随机 IP,握完手就关 socket,用一台机发请求就能把你的哈希表填满。
  • net.netfilter.nf_conntrack_tcp_timeout_syn_recv:默认 60
    • 类似,故意不发握手的 ACK 即可。但这个超时时间没那么夸张,系统也有 syn cookie 机制来缓解 syn flood 攻击。

其他值得注意的参数:

  • net.netfilter.nf_conntrack_tcp_timeout_syn_sent:默认 120
    • 你的程序的 connect timeout 有这么长吗?
  • net.netfilter.nf_conntrack_tcp_timeout_fin_wait:默认 120
    • net.ipv4.tcp_fin_timeout 默认 60 秒,通常还会参考 BSD 和 macOS 设成更小的值。这里往往也没必要这么大。
  • net.netfilter.nf_conntrack_icmp_timeout:默认 30
    • 哪里的 ping 会等 30 秒才超时?

这几个倒是比较合理,小于等于可能遇到的极端情况,但如果不想半关闭的连接的记录继续占着宝贵的哈希表,提早清了似乎也没什么问题:

  • net.netfilter.nf_conntrack_tcp_timeout_time_wait:默认 120
    • Linux 里的 MSL 写死 60 秒(而不是 TCP 标准里拍脑袋的 120 秒),TIME_WAIT 要等 2MSL,这里 120 算是个合理的值。
    • 但现在默认有 PAWS(net.ipv4.tcp_timestamps),不会出现标准制定时担心的迷途报文回来碰巧污染了序列号相同的新连接的数据的情况, 互联网公司基本都开 net.ipv4.tcp_tw_reuse,既然半连接都不留这么久,记录似乎也不需要留这么久。
  • net.netfilter.nf_conntrack_tcp_timeout_close_wait:默认 60
    • CLOSE_WAIT 状态是让被动关闭方把该传的数据传完。如果程序写得不好,这里抛了未捕捉的异常,也许就走不到发 FIN 那步了,一直停在这里。
  • net.netfilter.nf_conntrack_tcp_timeout_last_ack:默认 30
    • 被动关闭方发 FIN 后如果一直收不到对面的 ACK 或 RST,会不断重发,直到超时才 CLOSE。net.ipv4.tcp_retries2 的默认值是 15,最多要等 924.6 秒……不过一般都会调小这个值。

TL;DR

除了有关联的参数,尽量一次只改一处,记录下默认值和上次改的值,效果不明显或更差就还原。修改完要多观察一段时间,确保不会影响业务。

net.netfilter.nf_conntrack_buckets 参数是只读的,不能直接改,需要修改模块的设置:

# 改为 262144echo 262144 | sudo tee /sys/module/nf_conntrack/parameters/hashsize# 再查看,此时 bucket 已经变成刚才设置的值sudo sysctl net.netfilter.nf_conntrack_buckets

net.netfilter.nf_conntrack_max 参考默认值,设为桶的 4 倍:

sudo sysctl net.netfilter.nf_conntrack_max=1048576# 改完可以看到 net.netfilter.nf_conntrack_max 和 net.nf_conntrack_max 都变了

超时的值要根据业务和网络环境设置,这里只是举例,不要照抄(参考了 这个做路由器的公司的设置):

sudo sysctl net.netfilter.nf_conntrack_icmp_timeout=10sudo sysctl net.netfilter.nf_conntrack_tcp_timeout_syn_recv=5sudo sysctl net.netfilter.nf_conntrack_tcp_timeout_syn_sent=5sudo sysctl net.netfilter.nf_conntrack_tcp_timeout_established=600sudo sysctl net.netfilter.nf_conntrack_tcp_timeout_fin_wait=10sudo sysctl net.netfilter.nf_conntrack_tcp_timeout_time_wait=10sudo sysctl net.netfilter.nf_conntrack_tcp_timeout_close_wait=10sudo sysctl net.netfilter.nf_conntrack_tcp_timeout_last_ack=10

用 sysctl [-w] 或 echo xxx > /pro/sys/net/netfilter/XXX 做的修改在重启后会失效。如果测试过没问题,在 /etc/sysctl.d/ 下新建配置文件,这里以 90-conntrack.conf 为例(CentOS 6 等旧系统编辑 /etc/sysctl.conf),系统启动时会加载里面的设置:

# 格式:<参数>=<值>,等号两边可以空格,支持 # 注释net.netfilter.nf_conntrack_max=1048576net.netfilter.nf_conntrack_icmp_timeout=10net.netfilter.nf_conntrack_tcp_timeout_syn_recv=5net.netfilter.nf_conntrack_tcp_timeout_syn_sent=5net.netfilter.nf_conntrack_tcp_timeout_established=600net.netfilter.nf_conntrack_tcp_timeout_fin_wait=10net.netfilter.nf_conntrack_tcp_timeout_time_wait=10net.netfilter.nf_conntrack_tcp_timeout_close_wait=10net.netfilter.nf_conntrack_tcp_timeout_last_ack=10

如果修改了配置文件,要马上应用配置文件里的设置:

sudo sysctl -p /etc/sysctl.d/90-conntrack.conf# 不传文件路径默认加载 /etc/sysctl.conf

C. 设置不跟踪连接的规则

对需要防火墙的机器,可以设置 NOTRACK 规则,减少要跟踪的连接数。

(注意:以下未经仔细测试,当时我们生产环境选择直接关防火墙。)

以 iptables 为例,查看所有规则:

sudo iptables-save

这个必须插在第1条,凡是不跟踪的肯定是你想放行的:

sudo iptables -I INPUT 1 -m state --state UNTRACKED -j ACCEPT# 设置成不跟踪的连接无法拿到状态,包含状态(-m state --state)的规则统统失效。# iptables 处理规则的顺序是从上到下,如果这条加的位置不对,可能导致请求无法通过防火墙。

不跟踪本地连接:

sudo iptables -t raw -A PREROUTING -i lo -j NOTRACKsudo iptables -t raw -A OUTPUT -o lo -j NOTRACK# 假如 Nginx 和应用部署在同一台机子上,增加这规则的收益极为明显。# Nginx 连各种 upstream 使得连接数起码翻了倍,不跟踪本地连接一下干掉一大半。
  • -t raw 会加载 iptable_raw 模块(kernel 2.6+ 都有)
  • raw 表基本就干一件事,通过 -j NOTRACK 给不需要被连接跟踪的包打标记(UNTRACKED 状态),告诉 nf_conntrack 不要跟踪连接
  • raw 的优先级大于 filtermanglenat,包含 PREROUTING(针对进入本机的包) 和 OUTPUT(针对从本机出去的包) 链

不跟踪某些端口的连接:

sudo iptables -t raw -A PREROUTING -p tcp -m multiport --dports 80,443 -j NOTRACKsudo iptables -t raw -A OUTPUT -p tcp -m multiport --sports 80,443 -j NOTRACK

配完防火墙规则记得留意后台服务还能不能连得上、响应时间有没有异常、某些 TCP 状态有没有异常增加……

确定没问题就保存规则(否则重启服务后失效):

# CentOS 6 等使用 SystemV init 的旧系统:sudo service iptables save# 其实就是把 iptables-save 的内容存到 /etc/sysconfig/iptables

博客也是好久没更新了,说说最近的折腾吧。

原有服务器是E3-1245 v2 + P8B-C/SAS/2L,16GB 纯ECC,千兆局域网。由于担心NAS数据安全,没做 RAID 5,板载RAID控制器不支持RAID 6,索性刷了直通,各个单盘独自运行,重要数据通过 powershell + fastcopy 定期备份。机械硬盘速度平均也就100+MB,千兆局域网可正好够用。期间也使用过两块SSD给分别给机械硬盘做读,写缓存。用以提升4K读写性能。

但也只是够用,也限制了进一步的折腾空间,想要提升NAS的数据读写速度,就得考虑两个问题:1,硬盘速度 2,网速

  1. 要提升硬盘速度,固态硬盘是别想了,太贵,而且容量也不大,还有磁盘快满时的性能严重下降、擦写加剧的问题,还是考虑机械硬盘,而机械硬盘速度提升也还是组RAID模式,(ZFS及各种文件系统比较小众,出问题也不好解决)组RAID 5数据又不放心,组RAID 6还得换硬件。
  2. 要提升网速,也有两个思路,(1)千兆网卡链路聚合(2)上万兆。链路聚合的话还需要交换机支持,而且特定情况也不一定有效,而且NAS和PC分别还要再加网卡,两个千兆口汇聚也还才最高250MB,这时就已经是4块网卡了,继续加网卡3口汇聚,成效不大,而且网卡越多,坏的几率也更高。

既然硬盘速度提升要换硬件,网速提升也要换硬件,干脆直接一步到位吧。于是。。。

以下是新入手的装备:

  1. ASUS XG-C100C 万兆网卡
  2. NETGEAR Nighthawk SX10(GS810EMX) 双万兆交换机,
  3. 二手Dell R720xd + E5 2690v2 + 16GB 1866 REG ECC *4 + H710P + 原装万兆网卡 +后置硬盘模块 + SD卡模块
  4. 当然还有两块SD卡

上万兆对到底用光口还是电口也纠结了好久。光口的话有好多便宜的垃圾可捡,无论是网卡、光模块还是交换机,但是光模块分厂家和类别,非常麻烦,而且二手交换机噪音功耗也比较大。电口的话,几乎没有二手交换机可捡,全新的又好贵。

决定用电口时,最初交换机的选择是华硕的XG-U2008,但是阉割了端口聚合、组播控制功能,售价2000,这就有点不厚道了,我可能不用,但你不能没有,因为谁知道我以后用不用得到呢?最终在亚马逊上海淘了现在的 SX10 ,有上述的功能,而且算上海关税也才1600+,算是厚道了。到手后全金属外壳,很厚重。一直以为是塑料的,用了几天才知道为什么是全金属外壳了,发热好高,摸上去都烫手。万兆网卡也是如此,拆下散热片,重新涂了硅脂,换了新的导热贴。不过就有有点纳闷为什么散热片和芯片中间还有个导热贴,不是直接接触呢?

前前后后折腾了20几天,也碰到了很多烦心事:

  1. 后置硬盘模块到手有问题不能识别
  2. 服务器到手不能识别内存,而且风扇直接跑到一万转,真的是飞机起飞,妥妥的有问题
  3. 发回来的服务器CPU散热器,每个都有3个坑洼,联系客服发回来的还是有坑洼,最后直接让我从别的地方买吧。也是够了。

换了服务器风扇转速明显正常了,开机也就5、6千的转速,虽说噪音还是大,那些说开机风扇起飞的,你们是没碰到100%风扇转速的情况

Anyway,不管怎么样,东西到齐了,也算是搞得差不多了,除了预期的4块4TB机械硬盘还没入手。双十一入手4块西数4TB NAS硬盘,这盘网上反馈质量问题不少,我也是冲着希捷去的,无良商家商品详情改成了西数硬盘,到手才发现,也懒着换了,RAID6安全性高些,也就不纠结了

一共12个3.5英寸盘位 + 2个2.5英寸盘位。

新服务器 ESXi 装在了SD卡模块上,两块SD卡做了镜像,这样的好处是更加方便了,(1、节省硬盘位;2、vmfs升级时不能升级ESXI系统本身所在的硬盘)。

然后用两块 128GB 的 SSD 组了 RAID 1,作为ESXi的数据盘。 直写,关闭预读。

剩下几块容量不等的硬盘,不好组RAID,暂时还是单盘使用,因为还是单盘,索性直接打开了磁盘缓存。回写,自适应预读

然后用一块 512GB 的 SSD 做了 CachaCade,用于加速机械硬盘,H710P 最大只支持 512GB 的 CacheCade。

测试数据盘的顺序读写可到 600MB ,这还是单盘没组 RAID 的情况下,当然随机读写就低了,也是没办法的事。

机器到手后一步一步将各个固件升到最新,没想到电源都有固件。

感觉默认的自动风扇转速还是高,于是网上找了下 ipmi 控制风扇转速的脚本,都是放到 cron 里执行的, 因为 cron 最小执行粒度是一分钟,对于突然升高的负载 温度急剧升高的情况根本没有办法,要么直接过热关机,要么就有损寿命。于是就改了下,作为守护进程启动,轮询,自动注册成 systemd 的服务,自动开机启动,当然还保留了 cron 执行作为备用, cron 执行时会检测服务进程是否运行,运行则退出,没有运行的话再进行温度控制。

另外也写了脚本自动上传ssl秘钥和证书到idrac(因为是 let'sencrypt 签发),再也不用看到idrac的不安全警告了。

更新:看了B站翼王的NAS视频,后悔没上DIY 4U服务器,主要槽点还是噪音问题,看着12cm机箱散热风扇和更大更自由的的CPU散热器,以及多一倍的硬盘位,真香,毕竟为了这服务器我都配套上了小型服务器机柜了

像往常一样,偶尔想起看看本站的访问情况,发现点击量高的可怕,并且不断再升高。

查看nginx日志,发现全都是来自不同地址的同一个请求,看来是遭到CC攻击了。最早的攻击来自于2月20日上午10点47分50秒,然后便像洪水一样持续到了现在。

Nginx日志在26日凌晨自动进行了分割,单单对26日的部分日志进行过滤就得到了1万3千多个攻击IP,这还是去重之后的。

令我自己都吃惊的是,遭受到攻击后本站一直正常运行,甚至访问速度都未减慢,这几天照常编辑了几篇文章,都没感觉到异常。看来平时的优化没白做。

在采取了多种不同的措施来缓解了攻击压力之后,CPU从百分之5/6十,下降到了正常水平。

然而攻击还在持续。本人在网上也没什么仇人,甚至都不怎么评论,是谁手里有这么多的肉鸡呢?或者是伪造的TCP数据包?这就无从得知了。

先贴上链接吧:https://github.com/frostnotfall/DoubanMovieTool

恩,这是一个关于电影的bot。基于豆瓣的数据实现。

过年一堆琐碎闲事,愣是拖到了今天才再次更一下博客。

最初的版本功能只有一个,根据电影的影评生成词云图片;半年后在此基础上添加了一些其他的功能,使之成为了一个便捷的电影工具。

基于Python实现,提供目前有“正在上映”、“即将上映”、“新片榜”‘“快捷搜索”等功能,还有一个基于旧接口实现的搜索(不太准,以后可能遗弃),通过CustomKeyboardButton、InlineKeyboardButton,InlineQuery,Instant View 方式呈现输出。

逻辑代码基本通过爬虫实现获取数据,少数数据通过接口获取。比较头疼的是这个框架并没有详尽的的文档,只有最简单的功能示例。碰到问题磕磕碰碰,最终还是完成了。

比较遗憾的是工具本身基于聊天平台,该聊天工具的数据呈现方式只有单一的text,image,vedio等,并不支持两种以上的复合呈现(尽管如此,还是比微信要好用多了)。所以尽可能通过CustomKeyboardButton、InlineKeyboardButton,InlineQuery呈现输出,避免交互方式单调,尽管如此,还是有些掣肘。

比如,api 提供图片+文字的数据发送方式,并且文字支持markdown或html解析,但是只支持最基本的粗体、斜体、url插入等方式,甚至不支持字号的设置。

目前已添加了 Instant View,电影详情以及影人详情经由 Instant View 输出,规避了之前的问题,数据呈现方式比原先好了不少。

开始的几个版本中,电影、演员的搜索方式通过返回文本+字符串截取的方式来实现,想来想去还是不太方便了,于是开始加入“快捷搜索”功能,(效果图见下方)通过InlineQuery返回结果,所见所得(之前的搜索功能依然保留,后期可能会弃之不用了)。

其他效果图就不贴了。

目前还想加入一项 top250 的功能,但是以当前使用的交互方式并不合适,所以可能会添加 Instant View 来改善一下数据的呈现方式。除此之外,目前想不到其它可添加的功能了。

2.17更新:关于 top250 的功能,目前考虑的方式是使用内联键盘更新按钮的方式。目前待定吧。

2.25更新:已添加。 采用列表分页, 通过内联键盘更新。

关于性能优化:

目前按照 python-telegram-bot开发者的建议,使用了 run_async、 wekhook 和ujson 库,但问题并不在这里,而是网络延迟,目前服务器到豆瓣的 ping 值 有250+ms,单纯请求一次http就要花费1.5s的时间,与 telegram 服务器之间的延迟也有130ms左右,真正留给程序的时间并不多,虽然对获取评论这种需要多次url请求的地方做了异步,(开始是使用多线程,但考虑到单核CPU多线程的创建、切换、销毁的开销......so),但对单个的请求使用异步并不能带来速度的提升。

所以如何缩短这部分的时间一直在考虑中:

  • 加缓存或许是个不错的方案,但使用的人少,命中率也低。
  • 加CDN也行,但也只降低了程序到telegram服务器的延迟。与豆瓣服务器的延迟没有改观。毕竟域名不是自己的。
  • 走国内代理,但免费代理很少有国际线路优化的。

非常抱歉回复晚了

原荒野无灯的脚本修改返回值,脚本如下:


#!/bin/sh

#################################################
# AnripDdns v5.08
# 基于DNSPod用户API实现的动态域名客户端
# 作者: 若海[mail@anrip.com]
# 介绍: http://www.anrip.com/ddnspod
# 时间: 2016-02-24 16:25:00
# Mod: 荒野无灯 http://ihacklog.com  2016-03-16
#################################################

# 使用Token认证(推荐) 请去 https://www.dnspod.cn/console/user/security 获取
arToken="*****,***************"
# 使用邮箱和密码认证
arMail=""
arPass=""

# 获得外网地址
arIpAdress() {
    local inter=`nvram get wan0_ipaddr`
    echo $inter
}

# 查询域名地址
# 参数: 待查询域名
arNslookup() {
    local inter="http://119.29.29.29/d?dn="
    wget --quiet --output-document=- $inter$1
}

# 读取接口数据
# 参数: 接口类型 待提交数据
arApiPost() {
    local agent="AnripDdns/5.07(mail@anrip.com)"
    local inter="https://dnsapi.cn/${1:?'Info.Version'}"
    if [ "x${arToken}" = "x" ]; then # undefine token
        local param="login_email=${arMail}&login_password=${arPass}&format=json&${2}"
    else
        local param="login_token=${arToken}&format=json&${2}"
    fi
    wget --quiet --no-check-certificate --output-document=- --user-agent=$agent --post-data $param $inter
}

# 更新记录信息
# 参数: 主域名 子域名
arDdnsUpdate() {
    local domainID recordID recordRS recordCD
    # 获得域名ID
    domainID=$(arApiPost "Domain.Info" "domain=${1}")
    domainID=$(echo $domainID | sed 's/.\+{"id":"\([0-9]\+\)".\+/\1/')
    # 获得记录ID
    recordID=$(arApiPost "Record.List" "domain_id=${domainID}&sub_domain=${2}")
    recordID=$(echo $recordID | sed 's/.\+\[{"id":"\([0-9]\+\)".\+/\1/')
    # 更新记录IP
    recordRS=$(arApiPost "Record.Ddns" "domain_id=${domainID}&record_id=${recordID}&sub_domain=${2}&record_line=默认")
    recordCD=$(echo $recordRS | sed 's/.\+{"code":"\([0-9]\+\)".\+/\1/')
    # 输出记录IP
    if [ "$recordCD" == "1" ]; then
        echo $recordRS | sed 's/.\+,"value":"\([0-9\.]\+\)".\+/\1/'
        return 1
    fi
    # 输出错误信息
    echo $recordRS | sed 's/.\+,"message":"\([^"]\+\)".\+/\1/'
}

# 动态检查更新
# 参数: 主域名 子域名
arDdnsCheck() {
    local postRS
    local hostIP=$(arIpAdress)
    local lastIP=$(arNslookup "${2}.${1}")
    echo "hostIP: ${hostIP}"
    echo "lastIP: ${lastIP}"
    if [ "$lastIP" != "$hostIP" ]; then
        postRS=$(arDdnsUpdate $1 $2)
        echo "postRS: ${postRS}"
        if [ $? -ne 1 ]; then
            return 1
        fi
    fi
    return 0
}

###################################################
# 检查更新域名

arDdnsCheck "frostfall.cn" ""

if [ $? -eq 1 ]; then
    /sbin/ddns_custom_updated 1
else
    /sbin/ddns_custom_updated 0
fi


先附上脚本地址 https://github.com/frostnotfall/DNSPodDdns

==================================================

目前联通已可获得ipv6地址,遂将原荒野无灯基于 anrip 的脚本添加了ipv6的动态解析支持
荒野无灯原帖:http://koolshare.cn/thread-37553-1-1.html

主要做了以下几点修改:
1. arNslookup (查询域名已解析的IP地址 )修改为 dnspod api post方式
2. 梅林固件重启后不会立刻获取到公网 ipv6 地址,当脚本检测不到 ipv6 地址时会等待 5 分钟后重新获取,还是没有则继续等待 5 分钟 ,以此类推,期间梅林控制台页面会显示叹号,但此时 ipv4 动态解析已经可用
3. 域名,二级域名 的传入参数作为全局变量使用,相关函数的传入参数做了简化

使用此脚本,需:
1.指定 arToken ,Token请去 https://www.dnspod.cn/console/user/security 获取
2.指定 arDdnsCheck "域名" "二级域名(可为空)" 如:
arDdnsCheck "baidu.com" ""  会动态解析 baidu.com 域名
arDdnsCheck "baidu.com" "www"  会动态解析 www.baidu.com 域名
3.因为 ipv4 与 ipv6 使用不同的recordID,使用脚本前先在 dnspod 官方控制台页面添加该域名(或二级域名)的 AAAA 记录,以生成 ipv6 的recordID

提醒:一般用作动态解析的域名,不会做 dns 负载均衡,所以为避免可能出现的问题,尽量只保持一个A记录和一个AAAA记录,避免解析错误,当然CNAME或NS等其他不受影响

1.恢复时 gitlab 版本应与备份时版本一致

2.默认卸载不完全,最好同时删除以下目录


# 同时删除备份目录,注意将备份文件保存至别处,/etc/gitlab/目录最好也备份下

rm -r /opt/gitlab/sv /opt/gitlab/service /opt/gitlab/init /opt/gitlab/etc /opt/gitlab/embedded/ssl/certs /opt/gitlab/embedded/service/gitlab-shell /opt/gitlab/embedded/service/gitlab-rails/public /opt/gitlab/embedded/service/gitlab-rails/config/initializers /opt/gitlab/embedded/cookbooks /var/opt/gitlab /opt/gitlab /etc/gitlab

# 删除 systemd 启动文件,否则安装新版本后执行 gitlab-ctl reconfigure 会卡住

rm /usr/lib/systemd/system/gitlab-runsvdir.service

3. gitlab 恢复后如果进入项目 返回500错误,db_key_base 问题,将之前的 /etc/gitlab/gitlab-secrets.json 文件拿过来替换(最好备份时一同备份),然后执行 gitlab-ctl reconfigure

迅雷是越来越不能用了,卸了好多次又装,这次是终于下决心不再用了,年费超级会员就当白扔了,祝早亡。

作为替代的下载工具,qbittorrent 着实不错,代码开源,一般的做种功能就不说了。有 Web UI,可根据 Client IP 设定白名单免密码访问,觉得原版 Web UI 丑的话,甚至可以替换成自己的 Web UI,支持动态域名,提供 WebAPI,支持导入 TLS 证书,支持RSS订阅,下载完成后可以电子邮件通知,也可执行外部程序(这里我使用windows 局域网通知功能),另外Wiki 也很详细。作为 NAS 上的下载工具非常不错。

qbittorrent 支持导入外部 Tracker 功能,玩BT下载的都知道 Tracker 的重要性,但是 Tracker 地址的维护却一直是个问题,Github 上一直在维护的的 trackerslist 数目终归是太少。

想要提高下载速度,只能自己维护了。

于是自己写了个 Tracker 维护脚本,通过遍历目录下的 Torrent 文件,提取出 Tracker 地址,做连通性测试,记录有效的地址到 txt 文件中,然后去重。

代码不多,100来行,做了多线程优化,速度也还行。放在了 Github 上。

可惜的是 qbittorrent 本身的 API 不支持永久导入 Tracker 功能,只能手动,也不支持插件的方式来实现,目前只支持搜索功能的插件,当然因为开源的原因,可以理解。

当然也不是没办法,配置文件是明文,可以通过替换特定行的方式来实现,以后再说了。