SIerだけど技術やりたいブログ

6年目のSIerのブログです

L7ロードバランサとL4ロードバランサ

複数のバックエンドサーバがあるときに、リクエストを分散させるための仕組み。

f:id:kimulla:20180407182724j:plain

似たような負荷分散の仕組みとしてDNSラウンドロビンがある。
これはバックエンドサーバのIPアドレスDNSレコードとして登録し、DNSの問い合わせ時にIPアドレスを順繰りに返事することで負荷分散する。DNSの問い合わせ結果はクライアント側でキャッシュされることが多いため、負荷が偏る可能性がある。
またサーバダウン時に、DNS問い合わせ結果がキャッシュされたクライアントには、即座にサーバ切り替えを行うことができない。
また、クライアントごとに同一サーバに割り振るといったパーシステンス機能は実現できない。

上記を実現したいときに、ロードバランサが使われる。

L7ロードバランサ

  • L7レベルでロードバランシングする
  • HTTPの情報を用いた、高度な制御ができる
    • Cookieを利用したスティッキーセッション
    • X-Forwarded-Forヘッダー を利用したクライアントのIPアドレス保持
  • HTTP情報を取得するために、LBでTCPコネクションが終端する
    • クライアントとLB
    • LBとバックエンドサーバ
  • パケットの流れ

f:id:kimulla:20180407181622j:plain

L4ロードバランサ

  • L4以下のレベルでロードバランシングする
  • IPレベルでのセッションパーシステンスができる
    • 接続元IPが同じなら同じバックエンドサーバに送れる
  • IPアドレス変換方式(NAT方式) と MACアドレス変換方式(DSR方式) がある
  • ロードバランサでTCPコネクションは終端しない

IPアドレス変換方式(NAT方式)

  • NATを利用してL4レベルでロードバランシングする
  • NAT変換のために全ての通信がLBを経由する
  • パケットの流れ

f:id:kimulla:20180407182128j:plain

MACアドレス変換方式(DSR方式)

  • 宛先MACアドレスを差し替えてロードバランシングする
    • ロードバランサとバックエンドサーバは同一セグメントに置く必要がある
    • クライアントから見たときの接続先IPアドレスはLBのIPアドレスになる
    • バックエンドサーバから見たときの接続元IPアドレスはクライアントのIPアドレスになる
  • バックエンドサーバの戻りパケットが、ロードバランサを経由せずにクライアントと直接通信する
  • レスポンスの接続元IPアドレスをロードバランサに見せかける必要がある
  • パケットの流れ

f:id:kimulla:20180407235625j:plain

触ってみる

L7ロードバランサ

構成図は以下のとおり。

f:id:kimulla:20180407193008j:plain

今回はLBとして、nginx を利用する。

以下を参考に、nginxの設定をする。
Using nginx as HTTP load balancer

http {
 ...
  upstream myapp1 {
    server 10.133.1.2;
    server 10.133.1.3;
  }
 ...
  server {
    ...
    location / {
      proxy_pass http://myapp1;
    }
  }
  ...
}

次に、バックエンドサーバでhttpdを起動し、クライアントからHTTPリクエストを投げる。

LBのeth0(クライアント側NIC)のパケットをキャプチャする。

# tcpdump port 80 -i eth0 -w eth0.pcap

f:id:kimulla:20180407193805p:plain

LBのeth1(バックエンド側NIC)のパケットをキャプチャする。

# tcpdump port 80 -i eth1 -w eth1.pcap

f:id:kimulla:20180407193808p:plain

上記から、コネクションが2本張られていることがわかる。また、クライアントからLBにHTTPリクエストが送られた後に、LBからバックエンドサーバにTCPコネクションを張っているのがわかる。

クライアントの接続元IPアドレスをバックエンドサーバが知りたい場合は、X-Forwarded-Forヘッダーをnginx側で付与すればよい。

https://www.nginx.com/resources/wiki/start/topics/examples/forwarded/

以下のように、HTTPヘッダーに接続元IPアドレスが付与される。

#] tcpdump -A port 80 -i eth1
...
.*.{.).
GET / HTTP/1.0
X-Forwarded-for: 192.168.11.104
Host: myapp1
Connection: close
Cache-Control: max-age=0
...

IPアドレス変換方式(NAT方式)

構成図は以下のとおり。

f:id:kimulla:20180407193008j:plain

今回はLBとして、ipvsadm を利用する。
dsas.blog.klab.org

LBでipvsadmの設定をする。

# ipvsadm -C
# ipvsadm -A -t 192.168.11.107:80 -s lc
# ipvsadm -a -t 192.168.11.107:80 -r 10.133.1.2 -m
# ipvsadm -a -t 192.168.11.107:80 -r 10.133.1.3 -m
# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  192.168.11.107:80 lc
  -> 10.133.1.2:80                Masq    1      0          0
  -> 10.133.1.3:80                Masq    1      0          0

次に、バックエンドサーバでhttpdを起動し、クライアントからHTTPリクエストを投げる。

LBのeth0(クライアント側NIC)のパケットをキャプチャする。

# tcpdump port 80 -i eth0 -w eth0.pcap

f:id:kimulla:20180407200511p:plain

LBのeth1(バックエンド側NIC)のパケットをキャプチャする。

# tcpdump port 80 -i eth1 -w eth1.pcap

f:id:kimulla:20180407200508p:plain

上記から、LBで接続元IPや接続先IPの変換はするものの、TCPコネクションが1本しか張られていないことがわかる。(接続元のエフェメラルポート番号がそのまま接続先に渡されており、また、時刻から、LBはパケットを横流ししている感じになっている)

MACアドレス変換方式(DSR方式)

構成図は以下のとおり。
f:id:kimulla:20180407235653j:plain


今回はLBとして、ipvsadm を利用する。
CentOS 6.5 で LVS (IPVS) の Direct Server Return... | CUBE SUGAR STORAGE

LBでipvsadmの設定をする。

]# iptables -L
]# ipvsadm -A -t 192.168.11.107:80 -s lc
]# ipvsadm -a -t 192.168.11.107:80 -r 192.168.11.108:80 -g
]# ipvsadm -L
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  192.168.11.107:http lc
  -> 192.168.11.108:http          Route   1      0          0

次に、バックエンドサーバでhttpdを起動し、クライアントからHTTPリクエストを投げる。

LBのパケットをキャプチャする。

# tcpdump port not 22 -w lb.pcap

戻りパケットがLBを経由しないため、Wiresharkがパケット欠けてると警告を出している。
f:id:kimulla:20180407234612p:plain

バックエンドサーバのパケットをキャプチャする。

# tcpdump port 80 -w backend.pcap

接続先IPアドレスがLBのIPアドレスになっている。また、接続元IPアドレスがクライアントのアドレスになっている。
f:id:kimulla:20180407234617p:plain

上記から、戻りパケットだけはLBを経由せずにクライアントにダイレクトに返っていることがわかる。まさにDirect Server Return…