2016年10月05日

コンテナによるLVS-Web構成

はてなブックマークに登録

はじめに

KLabさんの協力会社として一緒にお仕事をさせて頂いておりますクラスターコンピューティングと申します。今回はコンテナを利用したLVS-Web構成の構築を試してみました。

従来の物理サーバを仮想マシンやコンテナで置き換え、1つの物理システム上に複数のシステムを構築ししたり、必要に応じて動的にシステムを構築するなどのことは最近とはいわずよくおこなわれています。コンテナはそのフットワークの軽さからこれらの用途にとくに有効です。

コンテナはNamespaceやcgroupなどの技術を利用して独立したリソースをコンテナ毎に確保することにより仮想化環境を提供する一方、カーネル自体はホストのカーネルをホストの上のすべてのコンテナで共用しています。今回は、ipvsやiptablesなどカーネル空間の機能がコンテナ内でも利用できるのかの確認とともに、それらの機能を利用したコンテナによるシステム構成について試してみました。

そもそもコンテナ上てipvsやiptablesが使えるの?

結論から言えばコンテナ上でも通常のLinux Box上と同じ感覚で利用できます。コンテナ毎、正確言えばネットワークネームスペース毎に独立したIPVSのバランステーブルやiptablesのルーティングテーブルが用意されるので同じホスト上で複数のコンテナを起動した場合でも問題ありません。ただ、多少注意が必要な点があります。

必要なモジュールはホスト上で事前に読み込んで置く必要がある

ipvsやiptablesの利用に必要なカーネルモジュールはコンテナ上利用する前にホスト側で読み込まれている必要があります。root権限でコンテナを実行していたとしてもコンテナ上で各コマンドを実行したときに自動で読み込んではくれません。

ipvsの利用にはコンテナ上でもroot権限が必要。iptablesは非特権コンテナでも利用できる

root権限でコンテナを実行している場合、ipvsもiptablesも利用に問題はありません。非特権コンテナ、つまり一般ユーザ権限でコンテナを実行している場合、iptablesは利用できますが、ipvsについては無理なようです。ただし、非特権コンテナでiptablesを利用する場合でも先ほどの注意点にあるように、事前にホスト側でモジュールを読み込んでおく必要があり、これには当然root権限が必要になります

iptablesのLOGターゲットに注意

細かいことですが、コンテナ上でLOGターゲットを利用してIptablesのログを記録しようとしても上手くいきません。こはiptablesというよりも一般的なカーネル空間のログの問題のようです。今のところカーネル空間のログをそれぞれのコンテナに関連するログごとに分けて記録するような仕組みがありません。その関係上、コンテナ上てiptablesを利用してLOGターゲットでカーネル空間にログを出力することは今のところ意図的にできないようになっているようです。ログを記録するには、代わりにユーザ空間にログを出力するULOGやNFLOGターゲットを利用する必要があります。

LVS-Web構成

コンテナを利用して複数のLVS-Web構成を1つの物理システム上に構成してみます。LVSコンテナは通常の特権コンテナ、Webのコンテナには非特権コンテナを利用します。ちょっとした工夫としてVLANを利用してLVS-Webホスト間のネットワークをコンテナのグループごとに独立させます。

非特権コンテナの利用

Webコンテナに非特権コンテナを利用することによって、Web管理ユーザにホスト上でroot権限をあたえることなく、Webコンテナ内でのroot権限を与えることができます。Web管理ユーザはコンテナ内ではアプリケーションのインストールや各種設定の変更が可能になります。

VLANの利用

各コンテナは仮想インターフェースおよびブリッジを介して物理インターフェースに接続されていますが間にVLANを挟むことによってコンテナのネットワークをVLAN毎に独立させます。ネットワークを分離することによって安全性や利便性が高くなります。例えば、案件ごとにVLANを割り当てることによって、各案件のコンテナ間での通信が不可能になります。またVLAN内でのアドレス空間の設定は自由にすることができ他の案件と同じアドレスを利用することもできます。例えば、コンテナの役割でアドレスを固定してそのコンテナをそれぞれの案件にデプロイして利用することいったことも可能です。

NAT構成

lvs_r
図1

ちょっと細かいですが図1のように、2台のLVSサーバ、2台のWebサーバからなる物理システム上に2系統のLVS2台-Web2台のNAT構成を構築します。LVSコンテナは冗長化を実行し、それぞれLVSの上流側(外側)、および下流側(Webサーバ側)のゲートウェイアドレスをVRRPで管理します。それぞれ系統のLVS-Webコンテナ間のネットワークはVLANにより分離されており、各コンテナのアドレスは同じものを利用しています。

各ホストに作成したVLANインターフェースにはブリッジが接続され、そこにそれぞれの系統のコンテナが接続されています。ここでは1つのホスト上に1つの系統のコンテナは1つしかありませんが、ブリッジにコンテナを複数接続するとによって同一ホスト上で同じ系統のコンテナを複数利用することは可能です。

点線で囲まれた枠内がコンテナで分離されている範囲です。ブリッジやVLANインターフェースはその外側にありますので、非特権コンテナによりコンテナ内の特権があたえられていたとしてもWebコンテナからはVLANの外側にアクセスすることはできません。

LVSの上流側ではLVSコンテナは同じブリッジに接続されています。LVSホストのネットワークもここに接続されています。

下流側のLVS-Webホスト間のネットワーク(図1のuntagged)もコンテナのネットワークからは分離されていますが、ホストのroot権限があればブリッジ(図1でのlxcbr0など)を介してコンテナのネットワークにアクセスすることは可能です。

ここではとくに記述していませんが、LVS-Webホスト間の物理ネットワーク機器(スイッチなど)は各VLANパケットを通すようにしておきます。

実際にコンテナで構成してみる。

WebホストではUbuntu、LVSホストではDebianを利用しています。コンテナの実装としてはLXCを利用します。それぞれのホストにLXCをパッケージを利用してインストールしています。UbuntuであればLXCをインストール時点で非特権コンテナを実行する準備が整ってるはずです。各アドレスは図のとおりに設定します。非特権コンテナを実行するユーザはVLAN10側がuser10、VLAN20側がuser20とします。

構成の手順としては、まずホスト側で必要なブリッジやVLANインターフェースを用意します。そのあとコンテナを起動してブリッジに接続します。全てのホストでコンテナを起動しコンテナのクラスタが構成されたら、あとは通常のLinux boxの場合と同様に各コンテナ内でのルーティングの設定や、アプリケーションの起動などを実行するだけです。

各種ブリッジやVLANインターフェースの準備、コンテナの起動などすべては手動で行いますが、基本的にLXCとLinuxの基本コマンドしか利用しません。コンテナのクラスタの構成は比較的簡単に可能です。

コンテナクラスタの構成

Webホスト側の準備

まずVLAN10系統のセットアップを行います。コンテナ接続用のブリッジおよびVLANインターフェースを作成し、VLANインターフェースとブリッジを接続します。
root@web1 # brctl addbr lxcbr10
root@web1 # ip link add link eth0 name eth0.10 type vlan id 10
root@web1 # brctl addif lxcbr10 eth0.10
root@web1 # ip link set lxcbr10 up
root@web1 # ip link set eth0.10 up


コンテナを管理するユーザ(user10)を作成します。
root@web1 # groupadd -g 1010 user10
root@web1 # useradd -u 1010 -g 1010 -m user10

ユーザ(user10)のコンテナのブリッジ(lxcbr10)への接続を許可します。
root@web1 # echo "user10 veth lxcbr10 10" > /etc/lxc/lxc-usernet

Webコンテナ(非特権コンテナ)の準備

以下の作業は管理ユーザ(user10)で可能です。Ubuntuで非特権コンテナを実行する場合、LXCの設定ファイルおよびコンテナのディレクトリがユーザのホーム以下になっているようです。
  • デフォルトの設定ファイル:HOME/.config/lxc/default.conf
  • 各コンテナのファイル:HOME/.local/share/lxc/以下
システムのデフォルトの設定ファイルをユーザの設定ファイル場所(HOME/.config/lxc/)にコピーし、コンテナ管理ユーザ(user10)のsubuid/subgidを設定ファイルに記述します。ユーザのsubuid/subgidは/etc/subuid,/etc/subgidに記述されてます。先ほど作成したブリッジを設定ファイルに記述します。一般ユーザの実行する非特権コンテナは/etc/lxc/lxc-usernetで許可されているブリッジにのみ接続できます。コンテナのアドレスは今回はLXC側で設定するようにしました。
user10@web1 $ mkdir -p .config/lxc
user10@web1 $ cp /etc/lxc/default.conf .config/lxc
user10@web1 $ vi .config/lxc/default.conf

lxc.network.type = veth
lxc.network.link = lxcbr10
lxc.network.flags = up
lxc.network.ipv4 =192.168.10.50/24

lxc.id_map = u 0 493216 65536
lxc.id_map = g 0 493216 65536

非特権コンテナとして実行するコンテナを作成します。コンテナの中身はDebian/Jessieで作成しました。

user10@web1 $ lxc-create -t download  -n web10a 

コンテナを起動します。非特権コンテナ内ではrootとしての作業が可能です。ここではデフォルトゲートウェイの設定だけをおきます。アドレスはVRRPで冗長化されているVIPのアドレスを設定します。
アプリケーションなどの設定はあとでまとめておこないます。
user10@web1 $ lxc-start -n web10a
user10@web1 $ lxc-attach -n web10a

root@web10a:/# ip route del defalut via 192.168.10.10 dev eth0


同様にしてもう一つのWebホスト(web2)上でもセットアップおこないます。また同様にしてweb1,web2上でVLAN20系統のためセットアップを行います。基本的にブリッジ(lxcbr20)、VLANインターフェース(eth0.20)、管理ユーザ(user20)などの名前を読み替えるだけです。ただ、図にもありますようにVLAN20系統のLVS-Webコンテナ間のアドレスはVLAN10系統と同じアドレスを設定しています。

LVSホストの準備

LVSコンテナの上流側のブリッジlxcbr0-inは物理インターフェースeth0にバインドします。そのためLVSホストのアドレス(10.10.31.211)はlxcbr0-inのほうに設定する必要があります。
lvs1# brctl addbr lxcbr0-in
lvs1# brctl addif lxcbr0-in eth0
lvs1# ip addr add 10.10.31.211/24 dev lxcbr0-in
lvs1# ip link set lxcbr0-in up

 
LVSコンテナの下流側のブリッジおよびVLANインターフェースを作成します。
lvs1# brctl addbr lxcbr10
lvs1# ip link add link eth1 name eth1.10 type vlan id 10
lvs1# brctl addif lxcbr10 eth1.10
lvs1# ip link set lxcbr10 up
lvs1# ip link set eth1.10 up

LVSコンテナの準備

LVSのコンテナはroot権限で実行するので非特権コンテナのような事前準備は必要ありません。コンテナを作成します。
root@lvs1 $ lxc-create -t download  -n lvs10a

Webコンテナ同様 、今回はIPアドレスはLXCの設定ファイルで直接設定しました。
# Network configuration
lxc.network.type = veth
lxc.network.flags = up
lxc.network.link = lxcbr10
lxc.network.ipv4 =192.168.10.11/24
lxc.network.name = eth1

lxc.network.type = veth
lxc.network.flags = up
lxc.network.link = lxcbr0-in
lxc.network.ipv4 =10.10.31.11/24
lxc.network.name = eth0

コンテナを起動します。ここでもゲートウェイアドレスだけを設定して残りの中身の設定はあとでまとめておこないます。ここで10.10.31.210は上流側のゲードウェイアドレスです。

root@lvs1:/# lxc-start -n lvs10a
root@lvs1:/# lxc-attach -n lvs10a

root@lvs10a:/# ip route add default via 10.10.31.210 dev eth0


同様にしてもう一つのLVSホスト(lvs2)の方もセットアップを実行します。また同様にしてVLAN20系統のセットアップも実行します。Webホストの場合もそうでしたが、新しい系統のVLANを追加する場合、名前を読み替えての同じことの繰り返しです。

それぞれの系統でLVS-Web構成を構築する

これで各VLAN上でコンテナが接続されたました。あとは基本的にコンテナ内で通常のLinux boxと同様なセットアップを行うだけです。

Webコンテナ内でのセットアップ

apacheなどのインストールなどを行います。さらっとapt-getしていますが、ここでは上流側できちんと設定がなされ ていてWebコンテナから外側にアクセスできるようになっていると思ってください。
user10@web1 $ lxc-attach -n web10a

root@web10a:/# apt-get install apache2
root@web10a:/# echo "HELLO,This is web10a" > /var/www/html/index.htm


同様にしてもう一つのWebコンテナ(web10a)、VLAN20系統のコンテナ(web20a/web20b)上でもセットアップをおこないます。

LVSコンテナ内でのセットアップ

ipvsadm,keepalived等必要なものをインストールします。

root@lvs1:/# lxc-attach -n lvs10a

root@lvs10a:/# apt-get install ipvsadm,keepalived


また、Webサービスへののアクセスアドレス(10.10.31.110)はeth0のaliasで設定しておきます。
root@lvs10a# ip addr add 10.10.31.110 label eth0.110 dev eth0

keepalivedの設定をおこないます。はじめにお話しましたが、コンテナ上でkeepalived(ipvs)を利用する前にホスト上でipvsのモジュールを読み込んでおく必要があります。その際ip_vsモジュールさえ読み込んでおけばその他の必要なモジュール(ip_vs_lcなど)は必要に応じて読み込まれるようです。

root@lvs1:# modprobe ip_vs

keepalivedの設定は普通の物理サーバ構成の場合と特に変わりません。またLVS-Webサーバ間のアドレスを同じにしているので、リアルサーバの設定部分はVLAN10系統VLAN20系統で変わりません。
 
keepalived.conf
! Configuration File for keepalived
vrrp_instance VI_VLAN { state BACKUP interface eth1 garp_master_delay 5 virtual_router_id 1 priority 101 # LVS2側のコンテナには以下の設定 # priority 100 nopreempt advert_int 1 authentication { auth_type PASS auth_pass password } virtual_ipaddress { 10.10.31.10/24 dev eth0
# VLAN20系統の場合は以下の設定 # 10.10.31.20/24 dev eth0 192.168.10.10/24 dev eth1 } }

virtual_server_group VLAN {
        10.10.31.110 80

# VLAN20系統の場合は以下の設定
# 10.10.31.120 80
}

virtual_server group VLAN {
    delay_loop 6
    lvs_sched lc
    lvs_method NAT
    protocol TCP

    real_server 192.168.10.50 80 {
        weight 1
        inhibit_on_failure
        HTTP_GET {
            url {
              path /index.html
              status_code 200
            }
            connect_timeout 3
        }
    }

    real_server 192.168.10.51 80 {
        weight 1
        inhibit_on_failure
        HTTP_GET {
            url {
              path /index.html
              status_code 200
            }
            connect_timeout 3
        }
    }
}


同様にしてもう一つのLVSコンテナ(lvs10b)、またVLAN20系統のコンテナ(lvs20a/lvs20b)のセットアップもおこないます。今回はすべて手動で行っていますが、コンテナ内の設定はいわゆる構成管理ツールを利用することによりもっとスマートに行うことが可能になると思います。

Webコンテナの側の準備も完了してから、両方のLVSコンテナでkeepalivedを起動します。

root@lvs10a:/# /etc/init.d/keepalived start
root@lvs10a:/# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  10.10.31.110:80 lc
  -> 192.168.10.50:80             Masq    1      0          0        
  -> 192.168.10.51:80             Masq    1      0          0        


結果確認

適当な10.10.31.0/24上のクライアントから各VLANのサービスアドレスにアクセスします。上流側からサービスにアクセスする場合、このサービスアドレスへの経路をVRRPで冗長化する上流側のVIPアドレスに設定しておきます。
client# ip route add 10.10.31.110 via 10.10.31.10
client# ip route add 10.10.31.120 via 10.10.31.20

アクセスしてみました。
client# curl http://10.10.31.110
HELLO,This is web10a
client# curl http://10.10.31.110
HELLO,This is web10b
client# curl http://10.10.31.110
HELLO,This is web10a
client# curl http://10.10.31.110
HELLO,This is web10b

client# curl http://10.10.31.120
HELLO,This is web20a
client# curl http://10.10.31.120
HELLO,This is web20b
client# curl http://10.10.31.120
HELLO,This is web20a
client# curl http://10.10.31.120
HELLO,This is web20b

それぞれの系統でちゃんとロードバランスされていることがわかります。

LVSをコンテナ化しない場合

LVSをコンテナ化すると案件ごとに専用のLVSを設置することができます。ただ運用していく中でLVSのマスタとスレーブのコンテナの存在する物理サーバが案件ごとに異なるといった状態になることも考えられます。LVSをコンテナ化しない場合の構成として次のような構成を考えてみました。

lvs_r2
図2

LVS-Webホスト間のネットワークそのままVLANを利用した独立したものになっています。あるVLANのWebコンテナからは別のVLANのコンテナにアクセスはできません。ただ1つのLVSサーバでロードバランスする関係上、それぞれのVLAN内で同じネットワークアドレスは利用できません。この場合はVLANそれぞれに異なるネットワークセグメントを割り当てるのが妥当でしょう。ここではVLAN10に192.168.10.0/24のVLAN20に192.168.20.0/24のセグメントを割り当てています。

LVSをコンテナ化した場合と異なりLVSサーバにトラブルが発生した場合、すべての案件に影響が及んでしまいますが、これは冗長化により対処は可能です。1つのkeepalivedですべての案件のロードバランスも可能ですが、その場合は設定変更時などにトラブルが発生した場合の影響が甚大ですので、案件ごとに別々のkeepalivedを起動する必要があると思われます。

DR構成の場合

もともとはDR構成のケースを考えていました。コンテナ上でiptablesが利用可能かどうかを確認したのもそのためです(今回のNAT構成ではiptablesによるパケットの制御は必要ありません)ここでは外部にでるために上流側のネットワークのVLAN1のネットワークに接続しなくてはならないケースを考えます。このVLAN1のネットワークは上位の管理者の管轄で設定などを変更することは不可能であるとします。この条件でつぎのような構成を考えてみました。今回LVSの冗長化は省略してあります。

lvs_dr2
図3

外部からのパケットはVLAN1からLVSホストに到達し、そこで各VLANのサービスアドレス(VLAN10は10.10.1.110、VLAN20は10.10.1.120)に応じてそれぞれのVLANのLVSコンテナにルーティングされます。そしてLVSコンテナでロードバランスされたあとターゲットのWebコンテナに送られます。戻りのパケットはWebコンテナに用意されたeth1のインターフェースから直接外部に送られます。eth1のインターフェースはWebホスト上に用意されたVLAN1のインターフェースに接続されているブリッジに繋げられています。

この構成ではWebコンテナから直接パケットを返送するために、VLAN1への経路としてeth1がWebコンテナ上に設けられています。このインターフェースからはパケットが出て行くだけなので必ずしもアドレスを設定する必要はありませんが、接続されているブリッジを介して他のVLANのWebコンテナとつながってしまっています。LVS-Webホスト間のVLANネットワークの独立性を保つためにはWebホスト側でこのブリッジを介しての通信を禁止する必要があります(図3では一方通行で表しています)

まとめ

コンテナを利用して複数のLVS-Web構成を1つの物理システム上に構成することを試みてみました。その際にVLANを利用して LVS-Web間のネットワークを独立したものにしています。Webコンテナに非特権コンテナを利用することによってWeb管理者にWebホストや外部ネットワークへの権限を与えないままコンテナ内のみの権限を与えることができます。今回の構成にはLXCとLinuxのカーネルの機能および基本的なコマンドのみを利用しており、比較的簡単にコンテナによるクラスタ構成を作成することができます。またVLANによる構成はコンテナ内からはそれを全く意識することなくコンテナのグループに独立したネットワークを与えることができます。
tech_ccmp at 13:50│Comments(0)TrackBack(0)

トラックバックURL

この記事にコメントする

名前:
URL:
  情報を記憶: 評価: 顔   
 
 
 
Blog内検索
Archives
このブログについて
DSASとは、KLab が構築し運用しているコンテンツサービス用のLinuxベースのインフラです。現在5ヶ所のデータセンタにて構築し、運用していますが、我々はDSASをより使いやすく、より安全に、そしてより省力で運用できることを目指して、日々改良に勤しんでいます。
このブログでは、そんな DSAS で使っている技術の紹介や、実験してみた結果の報告、トラブルに巻き込まれた時の経験談など、広く深く、色々な話題を織りまぜて紹介していきたいと思います。
最新コメント