TIME_WAIT状態のTCPコネクションを早く終了させるべくKernelをリビルド

by Dalo_Pix2



以前、一度やったはずなのですが、すっかり忘れてしまっていて、結局調べることになったので、今回はここに作業ログを残しておきます。

TIME_WAITコネクションの増殖

一般的にネットワークアクセス数が極端に多いサーバでは、TIME_WAIT状態のコネクションが残留しがちです。
TIME_WAITの滞留時間が、Linuxデフォだと60秒になっているため、下記のエントリにも書きましたが、60秒の間に数十万レベルのリクエストが来るとあっという間にコネクションテーブルが埋まっていってしまうわけです。


で、別にTIME_WAITコネクションが多くなってしまうこと自体は、完全な悪というわけでもなく、 "net.ipv4.tcp_max_tw_buckets" あたりでキャップもできるし、それなりに制御して付き合っていけばいいわけですが、ローカルのTCPポートを使い切るようなケースだと、使えるローカルポートレンジは限られているので困ったりするパターンもあります。

そういえば昔、↑のエントリのような状況に出くわして、この時は諸事情で(NATしている構成ではなかったので) "net.ipv4.tcp_tw_recycle" を設定することで、TIME_WAITとなったTCPコネクションを再利用することで事なきを得たのですが、今回は検証の都合上、Linux Kernelをビルドして、TIME_WAITのコネクションが解放される時間(デフォルト: 60秒)を短くしてみます。

OS(今回の環境例)

今回使った検証環境でのサーバのOSは、LinuxのCentOS 6.4でした。

# cat /proc/version
Linux version 2.6.32-358.23.2.el6.x86_64 (mockbuild@c6b9.bsys.dev.centos.org) (gcc version 4.4.7 20120313 (Red Hat 4.4.7-3) (GCC) ) #1 SMP Wed Oct 16 18:37:12 UTC 2013

こんな感じ。

カーネルのソースパッケージをゲットする

せっかくCentOSを使っていることもあって、公開されているカーネルのソースパッケージを取得します。

上記サイトから、該当するバージョンのリリースノートを読むと、ソースパッケージの記載場所が書いてありますので、そこからダウンロードしましょう。


CentOS 6.4だと、以下のリリースノート箇所に書いてあります。

以下のUpdateリポジトリあたりから、自分のカーネルバージョンと一致しているソースパッケージを入手します。


今回の例だと、↑の通り "/proc/version" でKernelのバージョンが "2.6.32-358.23.2.el6.x86_64" とわかりますので、以下コマンドみたいな感じでダウンロードして展開します。

# wget http://vault.centos.org/6.4/updates/Source/SPackages/kernel-2.6.32-358.23.2.el6.src.rpm
# rpm -ivh kernel-2.6.32-358.23.2.el6.src.rpm

Kernelのソースコードをちょこっと修正

本来であれば、きちんとパッチ(patch)を作成すべきなのですが、今回は検証が目的だったこともあって(面倒くさいので)、tarballを展開して直接変更を加えることにします。。。

# cd ~/rpmbuild/SOURCES/
# cp linux-2.6.32-358.23.2.el6.tar.bz2 /tmp

ソースパッケージをインストールすると、"~/rpmbuild/SOURCES/"にtarファイルが展開されるので、それを/tmpにコピーしちゃいます。

# cd /tmp
# tar xjvf linux-2.6.32-358.23.2.el6.tar.bz2

/tmpに移動して展開。

mv linux-2.6.32-358.23.2.el6 linux-2.6.32-358.23.3.namibuild.el6

ディレクトリファイル名を適当に変えてみました。
(更新番号をインクリメントして、野良ビルドしたので適当に文字列をくっつけました。)

cd linux-2.6.32-358.23.3.namibuild.el6

カレントディレクトリを変えて、以下のような感じでファイルを修正。

# diff -c Makefile{.bak,}
*** Makefile.bak        2013-11-19 16:56:31.272668572 +0900
--- Makefile    2013-11-19 16:57:03.889668546 +0900
***************
*** 1,7 ****
  VERSION = 2
  PATCHLEVEL = 6
  SUBLEVEL = 32
! EXTRAVERSION =
  NAME = Man-Eating Seals of Antiquity
  RHEL_MAJOR = 6
  RHEL_MINOR = 4
--- 1,7 ----
  VERSION = 2
  PATCHLEVEL = 6
  SUBLEVEL = 32
! EXTRAVERSION = -358.23.3.namibuild.el6.x86_64
  NAME = Man-Eating Seals of Antiquity
  RHEL_MAJOR = 6
  RHEL_MINOR = 4
# diff -c include/net/tcp.h{.bak,}
*** include/net/tcp.h.bak       2013-11-19 16:59:19.386668435 +0900
--- include/net/tcp.h   2013-11-19 16:59:38.649668421 +0900
***************
*** 111,118 ****
                                 */


! #define TCP_TIMEWAIT_LEN (60*HZ) /* how long to wait to destroy TIME-WAIT
!                                 * state, about 60 seconds     */
  #define TCP_FIN_TIMEOUT       TCP_TIMEWAIT_LEN
                                   /* BSD style FIN_WAIT2 deadlock breaker.
                                  * It used to be 3min, new value is 60sec,
--- 111,118 ----
                                 */


! #define TCP_TIMEWAIT_LEN (2*HZ) /* how long to wait to destroy TIME-WAIT
!                                 * state, about 2 seconds      */
  #define TCP_FIN_TIMEOUT       TCP_TIMEWAIT_LEN
                                   /* BSD style FIN_WAIT2 deadlock breaker.
                                  * It used to be 3min, new value is 60sec,

上記2ファイルですね。"include/net/tcp.h"の"TCP_TIMEWAIT_LEN"の変数が、今回変更を加えたい該当箇所になります。
設定値は、ケースに応じて吟味すべきですが、今回は↑の如く、検証内容的に2秒にしました。(短いw)

# cd ..
# tar cjvf linux-2.6.32-358.23.3.namibuild.el6.tar.bz2 linux-2.6.32-358.23.3.namibuild.el6

修正後、固めなおして、、、

# cp linux-2.6.32-358.23.3.namibuild.el6.tar.bz2 ~/rpmbuild/SOURCES/

固めたtarファイルを "~/rpmbuild/SOURCES/" にコピーします。

# cd ~/rpmbuild/SPECS/
# diff -c kernel.spec{.bak,}
*** kernel.spec.bak     2013-11-19 17:02:26.605668280 +0900
--- kernel.spec 2013-11-19 17:03:02.633668255 +0900
***************
*** 19,25 ****

  %define rhel 1
  %if %{rhel}
! %define distro_build 358.23.2
  %define signmodules 1
  %else
  # fedora_build defines which build revision of this kernel version we're
--- 19,25 ----

  %define rhel 1
  %if %{rhel}
! %define distro_build 358.23.3.namibuild
  %define signmodules 1
  %else
  # fedora_build defines which build revision of this kernel version we're
***************
*** 171,177 ****
  %endif

  # The kernel tarball/base version
! %define kversion 2.6.32-358.23.2.el6

  %define make_target bzImage

--- 171,177 ----
  %endif

  # The kernel tarball/base version
! %define kversion 2.6.32-358.23.3.namibuild.el6

  %define make_target bzImage

***************
*** 562,568 ****
  %define strip_cmd strip
  %endif

! Source0: linux-2.6.32-358.23.2.el6.tar.bz2

  Source1: Makefile.common

--- 562,568 ----
  %define strip_cmd strip
  %endif

! Source0: linux-2.6.32-358.23.3.namibuild.el6.tar.bz2

  Source1: Makefile.common

更新番号とかを変更しているので、上記のような感じで、specファイルを修正。
ここまでで、修正作業は終わりです。次はビルドでございます。

独自カーネルをビルドする

さて、ビルドすべくrpmbuildコマンドを実行すると、、、

# rpmbuild -ba kernel.spec
エラー: ビルド依存性の失敗:
        redhat-rpm-config は kernel-2.6.32-358.23.3.namibuild.el6.x86_64 に必要とされています
        patchutils は kernel-2.6.32-358.23.3.namibuild.el6.x86_64 に必要とされています
        xmlto は kernel-2.6.32-358.23.3.namibuild.el6.x86_64 に必要とされています
        asciidoc は kernel-2.6.32-358.23.3.namibuild.el6.x86_64 に必要とされています
        binutils-devel は kernel-2.6.32-358.23.3.namibuild.el6.x86_64 に必要とされています
        newt-devel は kernel-2.6.32-358.23.3.namibuild.el6.x86_64 に必要とされています
        python-devel は kernel-2.6.32-358.23.3.namibuild.el6.x86_64 に必要とされています
        perl(ExtUtils::Embed) は kernel-2.6.32-358.23.3.namibuild.el6.x86_64 に必要とされています
        bison は kernel-2.6.32-358.23.3.namibuild.el6.x86_64 に必要とされています
        flex は kernel-2.6.32-358.23.3.namibuild.el6.x86_64 に必要とされています
        hmaccalc は kernel-2.6.32-358.23.3.namibuild.el6.x86_64 に必要とされています

依存関係で諸々足りないパッケージを指摘されるので、、、

yum -y install redhat-rpm-config patchutils xmlto asciidoc binutils-devel newt-devel python-devel perl-ExtUtils-Embed bison flex hmaccalc

yumでインストール。

# rpmbuild -ba kernel.spec

で、再ビルド。
実行マシンにもよりますが、カーネルのビルドは結構時間がかかりますので、気長に待ちましょう。

gpg: keyring ...のところから進まない


ひょっとしたら、以下の箇所で処理が全然すすまなくなるかもしれません。

###
### Now generating a PGP key pair to be used for signing modules.
###
### If this takes a long time, you might wish to run rngd in the background to
### keep the supply of entropy topped up.  It needs to be run as root, and
### should use a hardware random number generator if one is available, eg:
###
###     rngd -r /dev/hwrandom
###
### If one isn't available, the pseudo-random number generator can be used:
###
###     rngd -r /dev/urandom
###
+ gpg --homedir . --batch --gen-key /root/rpmbuild/SOURCES/genkey
gpg: WARNING: unsafe permissions on homedir `.'
gpg: keyring `./secring.gpg' created
gpg: keyring `./pubring.gpg' created

そんな時はですね、別シェル(ターミナル)を立ち上げて、、、

# rngd -r /dev/urandom

上記コマンドを実行してentropyを供給。

# rngd -r /dev/urandom
-bash: rngd: コマンドが見つかりません

もし、コマンドがない、って出力されたときは、、、

# yum -y install rng-tools
# rngd -r /dev/urandom

インストールして再実行してみましょう。

ビルドが終わったらインストール、、、だが

ビルドが終わると、"~/rpmbuild/RPMS/x86_64/"にrpm一式が出来ているはずです。

# cd ../RPMS/x86_64/
# rpm -Uvh kernel-2.6.32-358.23.3.namibuild.el6.x86_64.rpm kernel-devel-2.6.32-358.23.3.namibuild.el6.x86_64.rpm kernel-headers-2.6.32-358.23.3.namibuild.el6.x86_64.rpm
エラー: 依存性の欠如:
        kernel-firmware >= 2.6.32-358.23.3.namibuild.el6 は kernel-2.6.32-358.23.3.namibuild.el6.x86_64 に必要とされています

なので、こんな感じでインストールしようとするも、"kernel-firmware"がない、と(TT)

# cd ../../SPECS/
# rpmbuild -bb --with firmware kernel.spec

というわけで、再ビルド。しばし待つ。(要するに、最初から↑のコマンドを実行すると良いです。。。)

# cd ../RPMS/x86_64/
# rpm -Uvh kernel-2.6.32-358.23.3.namibuild.el6.x86_64.rpm kernel-devel-2.6.32-358.23.3.namibuild.el6.x86_64.rpm kernel-headers-2.6.32-358.23.3.namibuild.el6.x86_64.rpm kernel-firmware-2.6.32-358.23.3.namibuild.el6.x86_64.rpm
準備中...                ########################################### [100%]
   1:kernel-firmware        ########################################### [ 25%]
   2:kernel                 ########################################### [ 50%]
   3:kernel-headers         ########################################### [ 75%]
   4:kernel-devel           ########################################### [100%]

さて、やっとこさ独自ビルドしたカーネルがインストールできました。
というところで、OSを再起動します。

# cat /proc/version
Linux version 2.6.32-358.23.3.namibuild.el6.x86_64 (root@haproxy-test01) (gcc version 4.4.7 20120313 (Red Hat 4.4.7-3) (GCC) ) #1 SMP Tue Nov 19 17:35:29 JST 2013

再起動後、ちゃんと起動してきて、独自カーネルに変わっていればOKです。

動作確認

クライアントから適当に接続要求しまくって、どうなったかを netstat で確認してみます。

Before...

# netstat -anpto
・・・・・省略・・・・・

tcp        0      0 10.50.1.62:80               10.33.56.163:47769          TIME_WAIT   -                   timewait (8.40/0/0)
tcp        0      0 10.50.1.62:80               10.33.56.171:4649           TIME_WAIT   -                   timewait (9.04/0/0)
tcp        0      0 10.50.1.62:80               10.33.56.111:11322          TIME_WAIT   -                   timewait (0.00/0/0)
tcp        0      0 10.50.1.62:80               10.33.56.189:42672          TIME_WAIT   -                   timewait (25.83/0/0)
tcp        0      0 10.50.1.62:80               10.33.56.151:1175           TIME_WAIT   -                   timewait (38.93/0/0)
tcp        0      0 10.50.1.62:80               10.33.56.190:12461          TIME_WAIT   -                   timewait (25.89/0/0)
tcp        0      0 10.50.1.62:80               10.33.56.61:2089            TIME_WAIT   -                   timewait (26.19/0/0)
tcp        0      0 10.50.1.62:80               10.33.56.45:23837           TIME_WAIT   -                   timewait (25.44/0/0)
tcp        0      0 10.50.1.62:80               10.33.56.59:30782           TIME_WAIT   -                   timewait (40.56/0/0)
tcp        0      0 10.50.1.62:80               10.33.56.167:61432          TIME_WAIT   -                   timewait (53.22/0/0)

・・・・・省略・・・・・

After!!!

# netstat -anpto
・・・・・省略・・・・・

tcp        0      0 10.50.1.62:80               10.33.56.85:48327           TIME_WAIT   -                   timewait (0.71/0/0)
tcp        0      0 10.50.1.62:80               10.33.56.55:37193           TIME_WAIT   -                   timewait (0.18/0/0)
tcp        0      0 10.50.1.62:80               10.33.56.41:28099           TIME_WAIT   -                   timewait (0.34/0/0)
tcp        0      0 10.50.1.62:80               10.33.56.33:2009            TIME_WAIT   -                   timewait (0.04/0/0)
tcp        0      0 10.50.1.62:80               10.33.56.76:15310           TIME_WAIT   -                   timewait (0.38/0/0)
tcp        0      0 10.50.1.62:80               10.33.56.15:41482           TIME_WAIT   -                   timewait (0.17/0/0)
tcp        0      0 10.50.1.62:80               10.33.56.34:39958           TIME_WAIT   -                   timewait (1.76/0/0)
tcp        0      0 10.50.1.62:80               10.33.56.97:36784           TIME_WAIT   -                   timewait (0.89/0/0)
tcp        0      0 10.50.1.62:80               10.33.56.74:53628           TIME_WAIT   -                   timewait (1.95/0/0)
tcp        0      0 10.50.1.62:80               10.33.56.94:5927            TIME_WAIT   -                   timewait (0.82/0/0)

・・・・・省略・・・・・

上記の通りですが、Beforeでは53秒とか滞留しているコネクションがありましたが、Afterでは設定した2秒以内におさまっています。


と、簡単ではありますが、こんな感じでTIME_WAITコネクションの数を減らすことができました。
それでは!=͟͟͞͞(๑•̀=͟͟͞͞(๑•̀д•́=͟͟͞͞(๑•̀д•́๑)=͟͟͞͞(๑•̀д•́


詳解 Linuxカーネル 第3版

詳解 Linuxカーネル 第3版

  • 作者: Daniel P. Bovet,Marco Cesati,高橋浩和,杉田由美子,清水正明,高杉昌督,平松雅巳,安井隆宏
  • 出版社/メーカー: オライリー・ジャパン
  • 発売日: 2007/02/26
  • メディア: 大型本
  • 購入: 9人 クリック: 269回
  • この商品を含むブログ (73件) を見る