###概要
サーバたるもの、仮想マシンであろうがコンテナであろうが、冗長性を兼ね備えていなければなりません。
そこで、Dockerを利用したサービス用サーバについても、別筐体間で冗長化・負荷分散を試みました。
Dockerのネイティブな機構でも、High Availability(以下、HA)が(ある|サポートされる)のかも知れませんが、今回はコンテナのホストサーバ間で技術的にも枯れたKeepalived + LVSを用いて、コンテナへのアクセスを冗長化・負荷分散する構成を取りました。
構成の概要は以下の図の通りとなります。
![ka_lvs_docker.png](https://qiita-image-store.s3.amazonaws.com/0/42848/e2371c43-4233-d694-12b5-3c77559a83d6.png)
コンテナ・ホストのInbondインタフェイスと、DockerコンテナであるWebサーバweb-01. web-02のゲートウェイとして、VRRP(Keepalived)によるVIPを設定しました。
図中で左のCentOS7がMasterで、InbondインタフェイスもしくはDocker向けゲートウェイのいずれかがDownした場合は、右のBackup CentOS7にフェイルオーバします。
また、各CentOS内で稼働するWebサーバをLVS(IP-LVS)によって負荷分散冗長化しており、HTTPヘルスモニタリングで死活監視をし、Webサーバプロセス(Nginx)がDownしたコンテナに対して、リクエストを回さないように設定しました。
この構成の結果、ホストCentOSへのHTTPリクエスト(TCP 80)はVIP 192.168.1.220に到達した時点でVRRP MasterがNATし、web-01 or web-02のIPアドレスに宛先NATの後にL3転送されます。
###Keepalivedの準備
Keepalivedの設定ファイル /etc/keepalived/keepalived.conf を掲載します。
(※予め、ipvsdam, keepalivedをインストールし、ip_vs.koをmodprobeしている前提)
! Configuration File for keepalived
global_defs {
router_id LVS_DOCKER
}
vrrp_instance WEB-HA-01 {
state MASTER
interface eth0
virtual_router_id 100
priority 100
advert_int 3
authentication {
auth_type AH
auth_pass docker-web
}
virtual_ipaddress {
192.168.1.220 dev eth0
192.168.100.254 dev ha-01
}
}
virtual_server 192.168.1.220 80 {
delay_loop 5
lb_algo rr
lb_kind NAT
protocol TCP
real_server 192.168.100.221 80 {
weight 1
HTTP_GET {
url {
path /ka
status_code 200
}
connect_timeout 5
nb_get_retry 3
delay_before_retry 3
}
}
real_server 192.168.100.222 80 {
weight 1
HTTP_GET {
url {
path /ka
status_code 200
}
connect_timeout 5
nb_get_retry 3
delay_before_retry 3
}
}
}
! Configuration File for keepalived
global_defs {
router_id LVS_DOCKER
}
vrrp_instance WEB-HA-01 {
state BACKUP
interface eth0
virtual_router_id 100
priority 90
advert_int 3
authentication {
auth_type AH
auth_pass docker-web
}
virtual_ipaddress {
192.168.1.220 dev eth0
192.168.100.254 dev ha-01
}
}
virtual_server 192.168.1.220 80 {
delay_loop 5
lb_algo rr
lb_kind NAT
protocol TCP
real_server 192.168.100.221 80 {
weight 1
HTTP_GET {
url {
path /ka
status_code 200
}
connect_timeout 5
nb_get_retry 3
delay_before_retry 3
}
}
real_server 192.168.100.222 80 {
weight 1
HTTP_GET {
url {
path /ka
status_code 200
}
connect_timeout 5
nb_get_retry 3
delay_before_retry 3
}
}
}
###Dockerの準備
続いて、Webサービスを提供するDockerコンテナのイメージを作成します。
CentOS7の正系・待機系で、同じimageを用意するため、以下のDockerfileを作成してください。
#
# Nginx Dockerfile
#
FROM centos:centos7
ADD ./init.sh /usr/local/bin/init.sh
RUN yum -y update \
&& yum -y install epel-release \
&& yum -y install nginx \
&& chmod +x /usr/local/bin/init.sh
# Define default command.
CMD ["/usr/local/bin/init.sh"]
このファイルと同じディレクトリ内に、CMDで実行指定しているinit.shを作成します。
#!/bin/sh
/usr/sbin/nginx && /bin/bash
ここまできたら、この2つのファイルがあるディレクトリにて、
docker build -t TAGNAME .
(※TAGNAMEは任意のimage tag名称)
上のコマンドで、CentOS7のbase imageからnginxをインストール済み&事後にdocker attach
可能なイメージを構築します。
こうして作成したWebサーバ用コンテナを、次のように起動させます。
docker run --name web-01 -ti myimg/nginx-c7:v4
###HA用ネットワークの準備
Linuxコンテナ向けのネットワークブリッジ・ユーティリティであるpipeworkを用いて、コンテナにHAネットワーク側のNICを増設します。
(pipework : https://github.com/jpetazzo/pipework)
[root@cent7-01 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9f0e6ede85ca myimg/nginx-c7:v4 /usr/local/bin/init. About a minute ago Up About a minute web-01
[root@cent7-01 ~]# pipework ha-01 9f0e6ede85ca 192.168.100.221/24
(※"ha-01"は任意のブリッジ名)
[root@cent7-02 nginx-c7]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
937e2a2038e2 myimg/nginx-c7:v4 /usr/local/bin/init. 9 seconds ago Up 8 seconds web-02
[root@cent7-02 nginx-c7]# pipework ha-01 937e2a2038e2 192.168.100.222/24
次いで、コンテナWebサーバを外部ネットワークからのHTTPリクエストに応答するために、Routing情報を変更します。
Dockerは新規コンテナ起動時に、デフォルトでは172.17.0.0/16内の空きIPをアサインし、コンテナホストに作成済みのブリッジ docker0のIP 172.17.42.1をデフォルトルートとして設定します。
ただ、このデフォルトルート情報はdocker attach
したbashセッションからは変更が禁止されているようです。
そこで、Dockerコンテナに割り当てられた Linux Namespaceを操作可能にし、そのNamespaceを指定したip netns exec
コマンドによって、コンテナ内のルーティングを変更する事にしました。
(※Linux NamespaceはDockerがホストのネットワークとコンテナ(ゲスト)のネットワークを隔離するために、内部的に利用されています。)
手順としては、
- Dockerコンテナの実行プロセスPIDを調べる
- 上で調べたPIDが属するNamespaceを
ip netns
コマンドで操作可能とするため、/var/run/netns/ディレクトリにリンクを作成する
といった要領です。
[root@cent7-01 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9f0e6ede85ca myimg/nginx-c7:v4 /usr/local/bin/init. 15 minutes ago Up 15 minutes web-01
[root@cent7-01 ~]# docker inspect web-01 | grep -i pid
"Pid": 17917,
[root@cent7-01 ~]# ln -s /proc/17917/ns/net /var/run/netns/web-01
[root@cent7-01 ~]# ip netns exec web-01 ip route sh
default via 172.17.42.1 dev eth0
172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.28
192.168.100.0/24 dev eth1 proto kernel scope link src 192.168.100.221
[root@cent7-01 ~]# ip netns exec web-01 ip route del default
[root@cent7-01 ~]# ip netns exec web-01 ip route add default via 192.168.100.254
[root@cent7-01 ~]# ip netns exec web-01 ip route sh
default via 192.168.100.254 dev eth1
172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.28
192.168.100.0/24 dev eth1 proto kernel scope link src 192.168.100.221
[root@cent7-02 ~]# docker inspect web-02 | grep -i pid
"Pid": 14886,
[root@cent7-02 ~]# ln -s /proc/14886/ns/net /var/run/netns/web-02
[root@cent7-02 ~]# ip netns exec web-02 ip route sh
default via 172.17.42.1 dev eth0
172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.17
192.168.100.0/24 dev eth1 proto kernel scope link src 192.168.100.222
[root@cent7-02 ~]# ip netns exec web-02 ip route del default
[root@cent7-02 ~]# ip netns exec web-02 ip route add default via 192.168.100.254
[root@cent7-02 ~]# ip netns exec web-02 ip route sh
default via 192.168.100.254 dev eth1
172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.17
192.168.100.0/24 dev eth1 proto kernel scope link src 192.168.100.222
以上の設定・手順を終えた時点で、
- 2台のホストに跨るコンテナ2台間で、LVSの形成(Round-Robin負荷分散)
- サービスを受け付けるホストの eth0側で、VIP 192.168.1.220が稼働
- 実際のWebサーバとなるコンテナを繋いだ内部ネットワーク ha-01側で、VIP 192.168.100.254が稼働
これらの環境が出来上がりました。
次の投稿では、上述のLVS・Keepalivedの状態確認の方法について述べたいと思います。
To be continued !