1. 程式人生 > 實用技巧 >Linux 從4.12核心版本開始移除了 tcp_tw_recycle 配置。

Linux 從4.12核心版本開始移除了 tcp_tw_recycle 配置。

被拋棄的tcp_recycle_小米雲技術-CSDN部落格_sysctl: cannot stat /proc/sys/net/ipv4/tcp_tw_recy https://blog.csdn.net/pengzhouzhou/article/details/85229437

1、背景

最近準備搭建一個新的kubernetes叢集,將核心從3.18更新到了4.14版本,並執行一些常規的優化操作。在執行sysctl -p操作時突然報錯如下:

sysctl: cannot stat /proc/sys/net/ipv4/tcp_tw_recycle: No such file or directory

2、問題原因

Linux 從4.12核心版本開始移除了 tcp_tw_recycle 配置。

參考:[1]tcp:remove tcp_tw_recycle 4396e460

移除sysctl.conf中關於net.ipv4.tcp_tw_recycle的配置內容,再次嘗試sysctl -p就不再提示報錯了。

3、深入解析

tcp_tw_recycle通常會和tcp_tw_reuse引數一起使用,用於解決伺服器TIME_WAIT狀態連線過多的問題。

3.1、TIME_WAIT狀態出現原因與檢視

讓我們回顧一下四次揮手的流程:

TIME_WAIT永遠是出現在主動傳送斷開連線請求的一方(下文中我們稱之為客戶),劃重點:這一點面試的時候經常會被問到。

客戶在收到伺服器端傳送的FIN(表示"我們也要斷開連線了")後傳送ACK報文,並且進入TIME_WAIT狀態,等待2MSL(MaximumSegmentLifetime 最大報文生存時間)。對於Linux,欄位為TCP_TIMEWAIT_LEN硬編碼為30秒,對於windows為2分鐘(可自行調整)。

為什麼客戶端不直接進入CLOSED狀態,而是要在TIME_WAIT等待那麼久呢,基於如下考慮:

1.確保遠端端處於關閉狀態。也就是說需要確保客戶端發出的最後一個ACK報文能夠到達伺服器。由於網路不可靠,有可能最後一個ACK報文丟失,如果伺服器沒有收到客戶端的ACK,則會重新發送FIN報文,客戶端就可以在2MSL時間段內收到這個這個重發的報文,並且重發ACK報文。但如果客戶端跳過TIME_WAIT階段進入了CLOSED,服務端始終無法得到響應,就會處於LAST-ACK狀態,此時假如客戶端發起了一個新連線,則會以失敗告終。

異常流程如下:

2.防止上一次連線中的包,迷路後重新出現,影響新連線(經過2MSL,上一次連線中所有的重複包都會消失),這一點和為啥要執行三次握手而不是兩次的原因是一樣的。

異常流程如下:

檢視方式有兩種:

(1)ss -tan state time-wait|wc -l

(2)netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'

3.2、TIME_WAIT的危害

對於一個處理大量連線的處理器TIME_WAIT是有危害的,表現如下:

1.佔用連線資源

TIME_WAIT佔用的1分鐘時間內,相同四元組(源地址,源埠,目標地址,目標埠)的連線無法建立,通常一個ip可以開啟的埠為net.ipv4.ip_local_port_range指定的32768-61000,如果TIME_WAIT狀態過多,會導致無法建立新連線。

2.佔用記憶體資源

這個佔用資源並不是很多,可以不用擔心。

3.3、TIME_WAIT的解決

可以考慮如下方式:

1.修改為長連線,代價較大,長連線對伺服器效能有影響。

2.增加可用埠範圍(修改net.ipv4.ip_local_port_range); 增加服務埠,比如採用80,81等多個埠提供服務; 增加客戶端ip(適用於負載均衡,比如nginx,採用多個ip連線後端伺服器); 增加服務端ip; 這些方式治標不治本,只能緩解問題。

3.將net.ipv4.tcp_max_tw_buckets設定為很小的值(預設是18000). 當TIME_WAIT連線數量達到給定的值時,所有的TIME_WAIT連線會被立刻清除,並列印警告資訊。但這種粗暴的清理掉所有的連線,意味著有些連線並沒有成功等待2MSL,就會造成通訊異常。

4.修改TCP_TIMEWAIT_LEN值,減少等待時間,但這個需要修改核心並重新編譯。

5.開啟tcp_tw_recycle和tcp_timestamps選項。

6.開啟tcp_tw_reuse和tcp_timestamps選項。

3.4、net.ipv4.tcp_tw_{reuse,recycle}

需要明確兩個點:

解決方式已經給出,那我們需要了解一下net.ipv4.tcp_tw_reuse和net.ipv4.tcp_tw_recycle有啥區別

1.兩個選項都需要開啟對TCP時間戳的支援,即net.ipv4.tcp_timestamps=1(預設即為1)。

RFC 1323中實現了TCP拓展規範,以便保證網路繁忙的情況下的高可用。並定義了一個新的TCP選項-兩個四位元組的timestamp欄位,第一個是TCP傳送方的當前時鐘時間戳,第二個是從遠端主機接收到的最新時間戳。

2.兩個選項預設都是關閉狀態,即等於0。

3.4.1 - net.ipv4.tcp_tw_reuse:更安全的設定

將處於TIME_WAIT狀態的socket用於新的TCP連線,影響連出的連線。

[2]kernel sysctl 官方指南中是這麼寫的:

Allow to reuse TIME-WAIT sockets for new connections when it is safe from protocol viewpoint. Default value is 0.

It should not be changed without advice/request of technical experts.

協議安全主要指的是兩點:

1.只適用於客戶端(連線發起方)

net/ipv4/inet_hashtables.c

  1. static int __inet_check_established(struct inet_timewait_death_row *death_row,
  2. struct sock *sk, __u16 lport,
  3. struct inet_timewait_sock **twp)
  4. {
  5. /* ……省略…… */
  6. sk_nulls_for_each(sk2, node, &head->chain) {
  7. if (sk2->sk_hash != hash)
  8. continue;
  9. if (likely(INET_MATCH(sk2, net, acookie,
  10. saddr, daddr, ports, dif))) {
  11. if (sk2->sk_state == TCP_TIME_WAIT) {
  12. tw = inet_twsk(sk2);
  13. if (twsk_unique(sk, sk2, twp))
  14. break;
  15. }
  16. goto not_unique;
  17. }
  18. }
  19. /* ……省略…… */
  20. }

2.TIME_WAIT建立時間超過1秒才可以被複用

net/ipv4/tcp_ipv4.c

  1. int tcp_twsk_unique(struct sock *sk, struct sock *sktw, void *twp)
  2. {
  3. /* ……省略…… */
  4. if (tcptw->tw_ts_recent_stamp &&
  5. (!twp || (sock_net(sk)->ipv4.sysctl_tcp_tw_reuse &&
  6. get_seconds() - tcptw->tw_ts_recent_stamp > 1))) {
  7. /* ……省略…… */
  8. return 1;
  9. }
  10. return 0;
  11. }

滿足以上兩個條件才會被認為是"safe from protocol viewpoint"的狀況。啟用net.ipv4.tcp_tw_reuse後,如果新的時間戳比之前儲存的時間戳更大,那麼Linux將會從TIME-WAIT狀態的存活連線中選取一個,重新分配給新的連接出去的的TCP連線,這種情況下,TIME-WAIT的連線相當於只需要1秒就可以被複用了。

重新回顧為什麼要引入TIME-WAIT:

第一個作用就是避免新連線接收到重複的資料包,由於使用了時間戳,重複的資料包會因為時間戳過期被丟棄。

第二個作用是確保遠端不是處於LAST-ACK狀態,如果ACK包丟失,遠端沒有成功獲取到最後一個ACK包,則會重發FIN包。直到:

1.放棄(連線斷開)

2.收到ACK包

3.收到RST包

如果FIN包被及時接收到,並且本地端仍然是TIME-WAIT狀態,那ACK包會被髮送,此時就是正常的四次揮手流程。

如果TIME-WAIT的條目已經被新連線所複用,則新連線的SYN包會被忽略掉,並且會收到FIN包的重傳,本地會回覆一個RST包(因為此時本地連線為SYN-SENT狀態),這會讓遠端端跳出LAST-ACK狀態,最初的SYN包也會在1秒後重新發送,然後完成連線的建立,整個過程不會中斷,只是有輕微的延遲。流程如下:

需要注意,連線被複用後,TWrecycled計數器會增加(/proc/net/netstat中TWrecycled值)

3.4.2 -net.ipv4.tcp_tw_recycle:更激進的設定

啟用TIME_WAIT 狀態的sockets的快速回收,影響所有連入和連出的連線

[3]kernel sysctl 官方指南是這麼寫的

Enable fast recycling TIME-WAIT sockets. Default value is 0. It should not be changed without advice/request of technical experts.

這次表述的更加模糊,繼續翻看原始碼:

net/ipv4/tcp_input.c

  1. int tcp_conn_request(struct request_sock_ops *rsk_ops,
  2. const struct tcp_request_sock_ops *af_ops,
  3. struct sock *sk, struct sk_buff *skb)
  4. {
  5. /* ……省略…… */
  6. if (!want_cookie && !isn) {
  7. /* ……省略…… */
  8. if (net->ipv4.tcp_death_row.sysctl_tw_recycle) {
  9. bool strict;
  10. dst = af_ops->route_req(sk, &fl, req, &strict);
  11. if (dst && strict &&
  12. !tcp_peer_is_proven(req, dst, true,
  13. tmp_opt.saw_tstamp)) {
  14. NET_INC_STATS(sock_net(sk), LINUX_MIB_PAWSPASSIVEREJECTED);
  15. goto drop_and_release;
  16. }
  17. }
  18. /* ……省略…… */
  19. isn = af_ops->init_seq(skb, &tcp_rsk(req)->ts_off);
  20. }
  21. /* ……省略…… */
  22. drop_and_release:
  23. dst_release(dst);
  24. drop_and_free:
  25. reqsk_free(req);
  26. drop:
  27. tcp_listendrop(sk);
  28. return 0;
  29. }

簡單來說就是,Linux會丟棄所有來自遠端的timestramp時間戳小於上次記錄的時間戳(由同一個遠端傳送的)的任何資料包。也就是說要使用該選項,則必須保證資料包的時間戳是單調遞增的。

問題在於,此處的時間戳並不是我們通常意義上面的絕對時間,而是一個相對時間。很多情況下,我們是沒法保證時間戳單調遞增的,比如使用了nat,lvs等情況。

而這也是很多優化文章中並沒有提及的一點,大部分文章都是簡單的推薦將net.ipv4.tcp_tw_recycle設定為1,卻忽略了該選項的侷限性,最終造成嚴重的後果(比如我們之前就遇到過部署在nat後端的業務網站有的使用者訪問沒有問題,但有的使用者就是打不開網頁)。

3.5、被拋棄的tcp_tw_recycle

如果說之前核心中tcp_tw_recycle僅僅不適用於nat和lvs環境,那麼從4.10核心開始,官方修改了時間戳的生成機制。

參考:[4]tcp: randomize tcp timestamp offsets for each connection 95a22ca

在這種情況下,無論任何時候,tcp_tw_recycle都不應該開啟。故被拋棄也是理所應當的了。

4、總結

  • tcp_tw_recycle 選項在4.10核心之前還只是不適用於NAT/LB的情況(其他情況下,我們也非常不推薦開啟該選項),但4.10核心後徹底沒有了用武之地,並且在4.12核心中被移除.

  • tcp_tw_reuse 選項仍然可用。在伺服器上面,啟用該選項對於連入的TCP連線來說不起作用,但是對於客戶端(比如伺服器上面某個服務以客戶端形式執行,比如nginx反向代理)等是一個可以考慮的方案。

  • 修改TCP_TIMEWAIT_LEN是非常不建議的行為。

5、參考連結

[1]https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=4396e46187ca5070219b81773c4e65088dac50cc

[2]https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/networking/ip-sysctl.txt?h=v4.11#n648

[3]https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/networking/ip-sysctl.txt?h=v4.11#n643

[4]https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=95a22caee396cef0bb2ca8fafdd82966a49367bb

[5]Coping with the TCP TIME-WAIT state on busy Linux servers:https://vincent.bernat.ch/en/blog/2014-tcp-time-wait-state-linux

[6]net.ipv4.tcp_tw_recycle は廃止されました ― その危険性を理解する:https://qiita.com/tmshn/items/b49f1b51bfc472968b30

[7]tcp_tw_reuse、tcp_tw_recycle 使用場景及注意事項:https://www.cnblogs.com/lulu/p/4149312.html

本文首發於公眾號“小米運維”,點選檢視原文