水平スケーリングについて説明します。 プロジェクトが1台のサーバーが負荷に対応できないサイズまで成長し、リソースの垂直成長の機会がないとしましょう。
この場合、プロジェクトインフラストラクチャのさらなる開発は、通常、同じタイプのサーバーの数が増加し、それらの間で負荷が分散されるために発生します。 このアプローチにより、リソースの問題を解決できるだけでなく、プロジェクトの信頼性も向上します。1つまたは複数のコンポーネントに障害が発生しても、全体的なパフォーマンスが損なわれることはありません。
このスキームで大きな役割を果たしているのは、リクエスト/トラフィックを分散するシステムであるバランサーです。 設計段階では、次の重要な要件を満たすことが重要です。
- 耐障害性。 リクエスト/トラフィックを配信するタスクに同時に関与する少なくとも2つのサーバーが必要です。 リードとバックアップの役割を明確に分離しません。
- スケーリング。 システムに新しいサーバーを追加すると、それに比例してリソースが増加します。
実際、これはノードがサーバーのバランスを取っているクラスターの説明です。
この記事では、シンプルで控えめなリソース向けのクラスターのレシピを共有します。このコンセプトは、コントロールパネルのサーバー、内部DNSサーバー、 Galeraクラスター、さまざまなマイクロサービスへのリクエストのバランスをとるために独自のインフラストラクチャでうまく使用されています。
条件に同意します。
-クラスターを構成するサーバーは、ノードまたはバランサーと呼ばれます。
-最終サーバーは、トラフィックがクラスターを介してプロキシされるホストになります。
-仮想IPは、すべてのノード間で「浮動」するアドレスであり、DNSのサービス名が指すアドレスです。
必要なもの:
-クラスターを構成するには、それぞれに2つのネットワークインターフェイスを持つ少なくとも2つのサーバー(または仮想マシン)が必要です。
-最初のインターフェイスは、外部との通信に使用されます。 ここで、実際のIPアドレスと仮想IPアドレスが構成されます。
-2番目のインターフェイスは、サービストラフィック、相互の通信ノードに使用されます。 ここで、プライベート(「グレー」)ネットワーク172.16.0.0/24からのアドレスが設定されます。
各ノードの2番目のインターフェイスは、同じネットワークセグメントにある必要があります。
使用されるテクノロジー:
VRRP、仮想ルーター冗長プロトコル -この記事のコンテキストでは、クラスター内のノード間で浮動する仮想IPアドレスの実装。 ある時点で、そのようなアドレスは、MASTERと呼ばれる任意の1つのノードで発生させることができます。 2番目のノードはBACKUPと呼ばれます。 両方のノードは、常に特別なハートビートメッセージを交換します。 指定された間隔内でそのようなメッセージを受信または受信しないと、仮想IPを「ライブ」サーバーに再割り当てする根拠が与えられます。 プロトコルの詳細については、 こちらをご覧ください 。
LVS、Linux Virtual Server -IPVSモジュールとしてLinuxカーネルに組み込まれたトランスポート/セッションレベルバランサー。 LVS機能の詳細な説明は、 こことここにあります 。
作業の本質は、「IP +ポート」の特定のペアがあり、それが仮想サーバーであることを示すことです。 このペアには、リクエストの処理を担当する実サーバーのアドレスが割り当てられ、バランシングアルゴリズムが設定され、リクエスト転送モードが設定されます。
このシステムでは、トラフィックをプロキシする必要があるLVSとエンドサーバー間の中間としてNginxを使用します。 Nginxはすべてのノードに存在します。
VRRPの設定とIPVSとの対話には、Linux Virtual Serverプロジェクトの一部として作成されたKeepalivedデーモンを使用します。
コンセプト
システムは、LVSテクノロジーとVRRPプロトコルを使用してクラスターに結合された、互いに独立した2つの均等にバランスの取れたバランスノードです。
トラフィックのエントリポイントは、1つまたは2番目のノードで発生した仮想IPアドレスになります。
LVSは、受信リクエストを、実行中のNginxインスタンスの1つ(ローカルまたは隣接ノード)にリダイレクトします。 このアプローチにより、クラスターのすべてのノード間でリクエストを均等に分散できます。 各バランサーのリソースをより有効に活用します。
Nginxの仕事は、要求を宛先サーバーにプロキシすることです。 バージョン1.9.13以降、プロキシ機能はtcpおよびudpトランスポートプロトコルレベルで使用できます。
各vhost /ストリームは、近隣のバランサーからのサービスインターフェイスと仮想IPへの着信の両方の要求を受け入れるように構成されます。 また、仮想IPアドレスがこのバランサーで物理的に発生していない場合でも(KeepalivedはサーバーにBACKUPロールを割り当てました)。
したがって、バランサーの状態(MASTERまたはBACKUP)に応じたトラフィックパターンは次のようになります。
マスター:
- 要求は仮想IPに送信されます。 IPVSはパケットをローカルサーバーにルーティングします。
- ローカルNginxは仮想IPをリッスンするため、リクエストを受信します。
- この仮想IPのプロキシ設定に従って、Nginxはリストされたアップストリームのいずれかに要求を送信します。
- 受信した応答はクライアントに送信されます。
バックアップ:
- 要求は仮想IPに送信されます。 IPVSは、パケットを隣接サーバーにルーティングします。
- 近隣のバランサーでは、この仮想IPは発生しません。 したがって、パッケージのdst_ipは、現在のバランサーのネットワークからの対応するグレーのIPに置き換える必要があります。 これには、DNATが使用されます。
- その後、ローカルのNginxはグレーのIPアドレスのリクエストを受け取ります。
- この仮想IPのプロキシ設定に従って、Nginxはリストされたアップストリームのいずれかに要求を送信します。
- 受信した応答は、src_ipが仮想IPに等しい(conntrackの参加により)クライアントに直接送信されます
実装:
オペレーティングシステムとして、 バックポートリポジトリが接続されたDebian Jessieを使用します 。
各バランサーノードに、操作に必要なソフトウェアクラスターを含むパッケージをインストールし、いくつかのシステム全体の設定を行います。
apt-get update apt-get install -t jessie-backports nginx apt-get install keepalived ipvsadm
eth1
インターフェースで、グレーネットワーク172.16.0.0/24
からアドレスを構成します。
allow-hotplug eth1 iface eth1 inet static address 172.16.0.1
eth0
インターフェイスに仮想IPアドレスを登録する必要はありません。 これは維持されます。
/etc/sysctl.d/local.conf
ファイルに次のディレクティブを追加します。
net.ipv4.ip_nonlocal_bind = 1 net.ipv4.vs.drop_entry = 1 net.nf_conntrack_max = 4194304
最初のものには、ローカルで発生しないIPをリッスンする機能が含まれます(これはNginxが機能するために必要です)。 2番目には、IPVSバランサーのレベルでのDDoSに対する自動保護が含まれます(セッションテーブルに十分なメモリがない場合、一部のレコードの自動クリーニングが開始されます)。 3番目は、conntrackテーブルのサイズを増やします。
/etc/modules
システムの起動時にIPVSモジュールのロードを有効にし/etc/modules
。
ip_vs conn_tab_bits=18
conn_tab_bits
パラメーターは、接続を持つテーブルのサイズを決定します。 その値は2の累乗です。 最大許容値は20です。
ところで、Keepalivedの開始前にモジュールがロードされない場合、Keepalivedはsegfaltを開始します。
次に、両方のバランスノードを再起動します。 したがって、起動時に構成全体が正しく上昇することを確認します。
一般設定が完了しました。 次の2つのタスクのコンテキストでさらにアクションを実行します。
- 3つのWebサーバー間でhttpトラフィックのバランスをとります。
- 2つのDNSサーバーのポート53でのudpトラフィックのバランス。
入力データ:
- 仮想IPアドレスとして
192.168.0.100
を使用します。 - Webサーバーは、シリアル番号に従って
192.168.0.102
および192.168.0.103
アドレスを持ちます。 - DNSサーバーには
192.168.0.201
と192.168.0.202
ます。
Nginxの設定から始めましょう。
ストリームセクションの説明を/etc/nginx/nginx.conf
追加し/etc/nginx/nginx.conf
。
stream { include /etc/nginx/stream-enabled/*; }
そして、適切なディレクトリを作成します。
mkdir /etc/nginx/stream-enabled
Webサーバーの設定をファイル/etc/nginx/sites-enabled/web_servers.conf
追加します
upstream web_servers { server 192.168.0.101:80; server 192.168.0.102:80; server 192.168.0.103:80; } server { listen 172.16.0.1:80 default_server;
DNSサーバーの設定をファイル/etc/nginx/stream-enabled/dns_servers.conf
ます
upstream dns_servers { server 192.168.0.201:53; server 192.168.0.202:53; } server { listen 172.16.0.1:53 udp reuseport;
その後、Keepalived(VRRP + LVS)を構成します。 バランシングノードがMASTER / BACKUP状態の間を移動するときに実行される特別なスクリプトを記述する必要があるため、これはもう少し複雑です。
すべての/etc/keepalived/keepalived.conf
設定は、1つのファイル/etc/keepalived/keepalived.conf
必要があります。 したがって、VRRPおよびLVS構成の次のブロックはすべて、このファイルに順番に保存する必要があります。
VRRP設定:
vrrp_instance 192.168.0.100 { interface eth1 # VRRP track_interface { # eth0 # , eth1 # FAULT, .. IP # LVS } virtual_router_id 1 # nopreempt # BACKUP # priority 102 # . authentication { auth_type PASS auth_pass secret # } virtual_ipaddress { 192.168.0.100/24 dev eth0 } notify /usr/local/bin/nat-switch }
上記のスクリプトは/usr/local/bin/nat-switch
です。 現在のVRRPインスタンスの状態が変わるたびに開始されます。 そのタスクは、BACKUP状態のバランサーが仮想IP宛てのパケットを正しく処理できるようにすることです。 この状況を解決するために、DNAT機能が使用されます。 つまり、次の形式のルール:
-A PREROUTING -d 192.168.0.100/32 -i eth1 -j DNAT --to-destination ${IP_on_eth1}
MASTER状態に移行すると、スクリプトはこのルールを削除します。
ここでは、この例のために書かれたnat-switch
スクリプトのバリアントを見つけることができます。
WebサーバーのグループのLVS設定:
virtual_server 192.168.0.100 80 { lb_algo wlc
DNSサーバーのグループのLVS設定:
virtual_server 192.168.0.100 53 { lb_algo wlc lb_kind DR protocol UDP delay_loop 6 real_server 172.16.0.1 53 { weight 1 MISC_CHECK { connect_timeout 2 misc_path "/bin/nc -zn -u 172.16.0.1 53" } } real_server 172.16.0.2 53 { weight 1 MISC_CHECK { connect_timeout 2 misc_path "/bin/nc -zn -u 172.16.0.2 53" } } }
最後に、NginxとKeepalivedの構成を再読み込みします。
nginx -s reload && /etc/init.d/keepalived reload
テスト:
バランサーがどのようにリクエストを宛先サーバーに配信するかを見てみましょう。 これを行うには、各Webサーバーで、簡単なコンテンツを含むindex.php
を作成します。
<?php sleep(rand(2, 8)); echo("Hello from ".gethostname()." !"); ?>
そして、httpを介して仮想IP 192.168.0.100
いくつかのリクエストを行います。
for i in $(seq 10); do printf 'GET / HTTP/1.0\n\n\n' | nc 192.168.0.100 80 | grep Hello done
結果:
Hello from server-1 ! Hello from server-2 ! Hello from server-2 ! Hello from server-3 ! Hello from server-3 ! Hello from server-1 ! Hello from server-1 ! Hello from server-2 ! Hello from server-2 ! Hello from server-3 !
このサイクルの実行中に(MASTERノードでの)LVS操作の統計を見ると、次の図を見ることができます。
ipvsadm -Ln
結果:
IP Virtual Server version 1.2.1 (size=262144) Prot LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forward Weight ActiveConn InActConn TCP 192.168.0.100:80 wlc -> 172.16.0.1:80 Route 1 1 3 -> 172.16.0.2:80 Route 1 1 3 UDP 192.168.0.100:53 wlc -> 172.16.0.1:53 Route 1 0 0 -> 172.16.0.2:53 Route 1 0 0
ここでは、クラスターのノード間で要求がどのように分散されるかを確認できます。現在処理中のアクティブな接続が2つ、処理済みの接続が6つあります。
LVSを通過するすべての接続の統計は、次のように表示できます。
ipvsadm -Lnc
結果:
IPVS connection entries pro expire state source virtual destination TCP 14:57 ESTABLISHED 192.168.0.254:59474 192.168.0.100:80 172.16.0.1:80 TCP 01:49 FIN_WAIT 192.168.0.254:59464 192.168.0.100:80 172.16.0.1:80 TCP 01:43 FIN_WAIT 192.168.0.254:59462 192.168.0.100:80 172.16.0.1:80 TCP 14:59 ESTABLISHED 192.168.0.254:59476 192.168.0.100:80 172.16.0.2:80 TCP 01:56 FIN_WAIT 192.168.0.254:59468 192.168.0.100:80 172.16.0.1:80 TCP 01:57 FIN_WAIT 192.168.0.254:59472 192.168.0.100:80 172.16.0.2:80 TCP 01:50 FIN_WAIT 192.168.0.254:59466 192.168.0.100:80 172.16.0.2:80 TCP 01:43 FIN_WAIT 192.168.0.254:59460 192.168.0.100:80 172.16.0.2:80
ここでは、それぞれ同じことがわかります。2つのアクティブな接続と6つの非アクティブな接続です。
あとがき:
提案された構成は、独自の要件と機能を備えた特定のプロジェクトの特定のソリューションの設計の出発点として機能します。
記事について何か質問がある場合、または物議をかもしているように思われるものがある場合は、コメントを残してください。喜んで話し合います。