さくらVPSでLXCを使って安価に複数台構成を実現する
2013年6月7日 22:04更新:
- Upstartのデフォルトの設定ファイルを書き換えない方法に変更しました。hitoさんありがとうございます。
- lxcなどのバージョンを記載しました。
はじめに
Chefを使っていると、役割やサービスごとに環境を分離したくなります。
しかし、個人レベルで大してトラフィックがない段階で、サービスごとに仮想サーバーを借りていてはお金が足りません。
そこで、安価なVPS上でLinux Container (LXC) を使うことで、複数のサーバーを作ります。 スケールしたくなったときは、コンテナを潰して、新しく仮想サーバーを借りてChefで同様の設定をすれば手軽にスケールできると考えています。
Heroku使えば?と言われるかもしれませんが、色々なミドルウェアを利用したり、バックグラウンドで処理をしようとすると、たちまちお金がかかるので、VPSをやりくりして遊びます。 *1
LXCとは
LXCはchrootと完全仮想化のいいとこ取りと言われ、以下の特徴があります。
- 各コンテナのリソースは分離されていて、例えばポートが被っていても大丈夫
- 完全仮想化に比べてオーバーヘッドが少ない
ただし、各コンテナはホストと同じカーネルで動くので、同じアーキテクチャのLinuxしか動かせません。
ホストのリソースは、基本的にホストと各コンテナで共有されますが、コンテナごとに制限することも可能です。
環境
今回は、LXCの導入の簡単さと、サポート期間の長さを考えて、Ubuntu 12.04 amd64を使います。
VPSは、さくらのVPS 2Gを利用します。以下のスペックで月額1,480円です。
- メモリ:2GB
- CPU:仮想3コア
- HDD:200GB
完成イメージ
データベースを複数プロセス立ち上げるのはリソースがもったいないので、DBサーバーとして1つのコンテナを割り当てます。
さくらVPSではグローバルIPは1つしか割り当てられません。 そこで、コンテナ内からインターネットへのアクセスはNATを使います。インターネットからコンテナへは直接接続できませんので、以下のようにします。
HTTP
ホストでnginxをリバースプロキシとして動作させ、名前ベースのバーチャルホストで、各コンテナのアプリケーションサーバーに振り分けます。
SSH
SSHは、まずホストにログインしてからコンテナにログインする多段接続を利用します。 設定でコマンド1つでログインできるようにします。
0. 前提
例えば、SSHに10022番ポートを使う場合は、以下のようにしておきます。
$ sudo ufw allow 10022/tcp $ sudo ufw enable
以降の手順でUFWの設定を変更するので、SSHで繋がらなくなった!ということがないように気をつけてください。
1. LXCのインストール
$ sudo apt-get install lxc
これだけでLXCがインストールされ、以下の設定も行われます。
- ブリッジ
lxcbr0
が作られ、IPアドレス10.0.3.1
、ネットマスク255.255.255.0
が割り当てられる。 - ネットワーク
10.0.3.0/24
から外部への通信用にNATが構成される。 - Dnsmasqを使ったDHCPで、
10.0.3.2
から10.0.3.254
のIPアドレスが割り当てられる。
これらの設定は/etc/lxc/lxc.conf
と/etc/default/lxc
で変更できます。
具体的な挙動は/etc/init/lxc-net.conf
を見ればわかります。
ifconfig
の実行結果は次のようになり、lxcbr0
ができていることがわかります。
$ ifconfig eth0 Link encap:Ethernet HWaddr 52:54:0a:00:92:27 inet addr:133.***.***.*** Bcast:133.***.***.255 Mask:255.255.254.0 inet6 addr: fe80::****:****:****:****/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:492875 errors:0 dropped:0 overruns:0 frame:0 TX packets:6954 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:32296394 (32.2 MB) TX bytes:2810653 (2.8 MB) lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 inet6 addr: ::1/128 Scope:Host UP LOOPBACK RUNNING MTU:16436 Metric:1 RX packets:36 errors:0 dropped:0 overruns:0 frame:0 TX packets:36 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:3240 (3.2 KB) TX bytes:3240 (3.2 KB) lxcbr0 Link encap:Ethernet HWaddr 22:2a:4d:97:df:ae inet addr:10.0.3.1 Bcast:10.0.3.255 Mask:255.255.255.0 inet6 addr: fe80::140b:58ff:fe1c:5ac5/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:387 errors:0 dropped:0 overruns:0 frame:0 TX packets:1591 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:33834 (33.8 KB) TX bytes:1559349 (1.5 MB)
2. DNSの設定
DHCPによってコンテナの作成時にIPアドレスを気にしなくていいのは楽ですが、IPアドレスが適当なものになってしまいます。そこで、ホスト名でアクセスできるようにDNSの設定を行います。
具体的には、このネットワークにlocal.example.com
というドメインを割り当て、db
という名前のコンテナにdb.local.example.com
やdb
というホスト名でアクセスできるようにします。
これには、/etc/init/lxc-net.conf
の中にあるdnsmasqの起動オプションを変更する必要があります。*2
しかし、パッケージに含まれるUpstartのジョブを直接書き換えると、アップデートされたときに手作業で変更する必要があるため、あまりよろしくありません。
そこでlxc-net
をコピーしたlxc-net-with-domain
というサービスを作り、元のlxc-net
は自動起動しないようにします。
$ sudo cp /etc/init/lxc-net.conf /etc/init/lxc-net-with-domain.conf $ sudo sh -c 'echo "manual" >> /etc/init/lxc-net.override' # lxc-netを自動起動させないように
コピーした/etc/init/lxc-net-with-domain.conf
を書き換えて、dnsmasq
の起動オプションを変更します。
$ sudo vim /etc/init/lxc-net-with-domain.conf dnsmasq -u lxc-dnsmasq --strict-order --bind-interfaces --pid-file=${varrun}/dnsmasq.pid --conf-file= --listen-address ${LXC_ADDR} --dhcp-range ${LXC_DHCP_RANGE} --dhcp-lease-max=${LXC_DHCP_MAX} --dhcp-no-override --except-interface=lo --interface=${LXC_BRIDGE} || cleanup ↓ dnsmasq --bogus-priv --domain-needed --local=/local.example.com/ --domain=local.example.com -u lxc-dnsmasq --strict-order --bind-interfaces --pid-file=${varrun}/dnsmasq.pid --conf-file= --listen-address ${LXC_ADDR} --dhcp-range ${LXC_DHCP_RANGE} --dhcp-lease-max=${LXC_DHCP_MAX} --dhcp-no-override --except-interface=lo --interface=${LXC_BRIDGE} || cleanup
やたら長いですが、dnsmasq
の引数に以下の引数を追加しているだけです。
--bogus-priv --domain-needed --local=/local.example.com/ --domain=local.example.com
lxc-net
を止めて、lxc-net-with-domain
を起動します。
$ sudo stop lxc-net $ sudo start lxc-net-with-domain
ちなみにlxc-netをrestartすると正常に再起動されない問題があったので、今後lxc-net-with-domain
を再起動したいときは、stop
& start
を使うのが吉です。
ホストでもこのDNSサーバーを参照し、コンテナを名前解決できるようにします。
$ sudo vim /etc/network/interfaces iface eth0 inet static (略) # dns-* options are implemented by the resolvconf package, if installed dns-nameservers 133.xxx.0.3 dns-search sakura.ne.jp ↓ dns-nameservers 10.0.3.1 133.xxx.0.3 dns-search local.example.com sakura.ne.jp
マシンを再起動して変更を反映します。
$ sudo reboot
3. ファイアウォール(UFW)の設定
デフォルトではブリッジによるフォワーディングは無効になっているので、これを許可します。
$ sudo vim /etc/default/ufw DEFAULT_FORWARD_POLICY="DROP" ↓ DEFAULT_FORWARD_POLICY="ACCEPT"
リロードして変更を反映します。
$ sudo ufw reload
また、ブリッジへの接続を許可します。
$ sudo ufw allow in on lxcbr0
4. 初回起動時に実行するスクリプトの用意
コンテナを作る前に、コンテナ作成直後の設定を行うスクリプトをホームディレクトリに用意しておきます。 コンテナは最小限の状態で作られるので、多少の処理が必要になります。
$ wget -P ~ https://gist.github.com/orangain/5692643/raw/lxc_ubuntu_bootstrap.sh $ chmod +x ~/lxc_ubuntu_bootstrap.sh
このスクリプトの内容は、以下のとおりです。
もちろんコンテナを大量に作るようであれば、テンプレートを変更しても構いません。 しかし、初期インストールパッケージを変更した場合は、キャッシュを更新しないと反映されないのでやや面倒です。
5. コンテナの作成
コンテナのテンプレートは/usr/lib/lxc/templates/
に存在し、実体はシェルスクリプトです。
lxc-****
というファイルが存在するので、****
の部分をテンプレート名として指定します。
ここでは、以下のようにubuntu
テンプレートを利用してdb
という名前のコンテナを作成します。
$ sudo MIRROR=http://jp.archive.ubuntu.com/ubuntu lxc-create -t ubuntu -n db -- --bindhome $USER
--
以降はテンプレートごとのオプションのため、テンプレートごとに異なります。ubuntu
テンプレートでは、デフォルトだとubuntu
ユーザーが作成されますが、--bindhome ユーザー名
とすると指定したユーザーが作成されます。さらに、該当ユーザーのホスト側のホームディレクトリがコンテナ側のホームディレクトリとしてマウントされるため、とても便利です。
MIRROR=http://jp.archive.ubuntu.com/ubuntu
は、aptのミラーを指定しています。この指定は初回のダウンロード時のみ有効です。
初回はOSのダウンロードが行われるため時間がかかりますが、2回目以降は/var/cache/lxc/
以下のキャッシュが使われるため数十秒で完了します。
コンテナは/var/lib/lxc/db
に作成されます。
6. コンテナの起動
作成したコンテナを起動して、用意したスクリプトを実行します。
$ sudo lxc-start -n db -d # 起動 $ sudo lxc-console -n db # コンソールに接続 # コンテナ側でブートストラップスクリプトを実行 (container) $ sudo ./lxc_ubuntu_bootstrap.sh # コンソールはCtrl+a, qで抜けられます
起動は一瞬ですが、ネットワークやファイアウォールの設定が間違っていてIPアドレスが取得できない場合は、コンソールに接続してから操作できるようになるまで1分ほどかかります。その場合は、Ctrl+a, qで抜けてホストの/var/log/syslog
などを確認しましょう。
以下のように、コンテナのconfig
ファイルへのシンボリックリンクを/etc/lxc/auto/
に作成することで、ホストのブート時に自動でコンテナを起動させることができます。
$ sudo ln -s /var/lib/lxc/db/config /etc/lxc/auto/db.conf
7. コンテナへのSSH接続
作業用マシンの.ssh/config
に以下のような記述を加えて、コマンド1つでログインできるようにします。
(workstation) $ vim .ssh/config # ホスト Host lxchost HostName wwwXXX.sakura.ne.jp Port 10022 # コンテナ Host db # スペース区切りで増やせます ProxyCommand ssh lxchost nc %h 22 2> /dev/null
作業用マシンから接続してみます。
(workstation) $ ssh db
まとめ
このようにしてコンテナを作れば後はChefで自由に設定できます。コンテナであることをあまり意識することなく複数台構成を組めると思います。
このように簡単に使えて便利なLXCですが、注意点もあります。
完全仮想化された環境や物理環境とはブートプロセスが異なるので、想定通りに動かないこともあります。 例えば、foremanでexportしたUpstartのジョブがブート時に起動しない問題にハマったことがあります。
このような点には注意しつつ、楽しいLXCライフを送りましょう。
なお、今回利用したlxcなどのバージョンは次のとおりです。
$ uname -a Linux wwwXXXX 3.2.0-45-generic #70-Ubuntu SMP Wed May 29 20:12:06 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux $ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 12.04.2 LTS Release: 12.04 Codename: precise $ lxc-version lxc version: 0.7.5