カーネルモジュールことはじめ #7
今日は ip_vs_test_schedule() 関数に手をいれます。(コードはこちら)
この関数は、IPVSが新しい接続を受け入れる時に呼び出されます。そして接続すべきリアルサーバを指す ip_vs_dest 構造体のポインタを返します。リアルサーバのリストは、引数で渡される ip_vs_service 構造体の destinations に格納されています。このコードでは、リストの先頭から以下の条件を満たすサーバを検索します。
- weightが0じゃないサーバ
- 接続数がしきい値(u_threshold)を越えていないサーバ
動作確認
# ipvsadm -A -t 10.211.55.3:21 -s test # ipvsadm -a -t 10.211.55.3:21 -r 10.211.55.5:21 -x 4 -y 1 # ipvsadm -a -t 10.211.55.3:21 -r 10.211.55.6:21 -x 4 -y 1 # ipvsadm -a -t 10.211.55.3:21 -r 10.211.55.7:21 -x 4 -y 1 # ipvsadm -L -n --thresholds IP Virtual Server version 1.2.1 (size=4096) Prot LocalAddress:Port Uthreshold Lthreshold ActiveConn InActConn -> RemoteAddress:Port TCP 10.211.55.3:21 test -> 10.211.55.253:21 4 1 0 0 -> 10.211.55.252:21 4 1 0 0 -> 10.211.55.251:21 4 1 0 0 -> 10.211.55.250:21 4 1 0 0
接続確認
client:$ telnet 10.211.55.3 21 Connected to 10.211.55.3. Escape character is '^]'. 220 ProFTPD 1.3.0 Server (Debian) [10.211.55.253]
これを5個動かすとIPVSの状態は以下のようになります。
# ipvsadm -L -n --thresholds IP Virtual Server version 1.2.1 (size=4096) Prot LocalAddress:Port Uthreshold Lthreshold ActiveConn InActConn -> RemoteAddress:Port TCP 10.211.55.3:21 test -> 10.211.55.253:21 4 1 5 0 -> 10.211.55.252:21 4 1 0 0 -> 10.211.55.251:21 4 1 0 0 -> 10.211.55.250:21 4 1 0 0
リストの先頭のサーバに5セッション張られました。
さらにもうひとつ接続すると、、
# ipvsadm -L -n --thresholds IP Virtual Server version 1.2.1 (size=4096) Prot LocalAddress:Port Uthreshold Lthreshold ActiveConn InActConn -> RemoteAddress:Port TCP 10.211.55.3:21 test -> 10.211.55.253:21 4 1 5 0 -> 10.211.55.252:21 4 1 1 0 -> 10.211.55.251:21 4 1 0 0 -> 10.211.55.250:21 4 1 0 0
今度は2番目のサーバに繋ぎにいきました。
では、一番最初に起動した telnet をいったん止めて再接続するとどうなるでしょう。
# ipvsadm -L -n --thresholds IP Virtual Server version 1.2.1 (size=4096) Prot LocalAddress:Port Uthreshold Lthreshold ActiveConn InActConn -> RemoteAddress:Port TCP 10.211.55.3:21 test -> 10.211.55.253:21 4 1 4 1 -> 10.211.55.252:21 4 1 2 0 -> 10.211.55.251:21 4 1 0 0 -> 10.211.55.250:21 4 1 0 0
接続が切れても 10.211.55.253 は OverLoadのままなので 10.211.55.252 に繋がります。
どうやら想定通りの動きをしてくれているようです。
ソースコード(ip_vs_test.c)
#include <linux/module.h> #include <linux/kernel.h> #include <net/ip_vs.h> static int ip_vs_test_init_svc(struct ip_vs_service *svc) { IP_VS_DBG(7, "ip_vs_test: init_svc\n"); return 0; } static int ip_vs_test_done_svc(struct ip_vs_service *svc) { IP_VS_DBG(7, "ip_vs_test: done_svc\n"); return 0; } static int ip_vs_test_update_svc(struct ip_vs_service *svc) { IP_VS_DBG(7, "ip_vs_test: update_svc\n"); return 0; } static struct ip_vs_dest * ip_vs_test_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) { struct ip_vs_dest *dest; list_for_each_entry(dest, &svc->destinations, n_list){ if(atomic_read(&dest->weight)){ if(dest->flags & IP_VS_DEST_F_OVERLOAD){ /* OverLoad */ }else{ return(dest); } } } return(NULL) ; } static struct ip_vs_scheduler ip_vs_test_scheduler = { .name = "test", .refcnt = ATOMIC_INIT(0), .module = THIS_MODULE, .init_service = ip_vs_test_init_svc, .done_service = ip_vs_test_done_svc, .update_service = ip_vs_test_update_svc, .schedule = ip_vs_test_schedule, }; static int __init ip_vs_test_init(void) { INIT_LIST_HEAD(&ip_vs_test_scheduler.n_list); return register_ip_vs_scheduler(&ip_vs_test_scheduler) ; } static void __exit ip_vs_test_cleanup(void) { unregister_ip_vs_scheduler(&ip_vs_test_scheduler); } module_init(ip_vs_test_init); module_exit(ip_vs_test_cleanup); MODULE_LICENSE("GPL");