2015/09/30

lxcコンテナへのNAT設定もsystemdで

昨夜久しぶりにホストをリブートした。そしたらlxcコンテナへのNAT設定が消滅した。永続化してなかったので、当然です。そうかと軽く考えて

iptable設定永続化パッケージインストール
$ sudo apt-get install iptables-persistent

で、コンテナへのNAT設定を保存し、ホスト再起動してみたら、あらあら、
Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination
MASQUERADE  all  --  10.0.3.0/24         !10.0.3.0/24
MASQUERADE  all  --  10.0.3.0/24         !10.0.3.0/24
lxcbr0の設定がダブってしまうじゃありませんか。なるほど。lxcのネットワーク設定とiptables-persistentの設定の両方が効いてしまうからでしょうか。

Enable LXC neworking in Debian Jessie, Fedora 21 and others
には、/usr/lib/x86_64-linux-gnu/lxc/lxc-net ファイルに追加設定する方法があったり

Tips for LXC: Creation, Autostart, OpenVPN and Port Forwarding to Containersには、iptables-persistentでいいが、起動に問題があるのでちょっとゴニョゴニョするとか

それぞれのアプローチ方法は参考にはなるが、ちょっと好きになれない感じ。
自分なりの解決方法としては、iptables-persistentをやめて、前記事と同じsystemdを使って設定することにした。

ただ、今回はホストでの設定だし、ユーザモードで実行するものでもないので、普通のsystemdサービスユニット。それと追加変更がし易い形にはしようと思う。

サービスユニットファイル
~/.config/systemd/system/lxc-nat.service として作成。どこでもいいと思うが、ユーザモードの場合 ~/.config/systemd/user以下に置く流れに沿ってみた。内容は以下のとおり
[Unit]
Description=LXC NAT network setup
After=lxc-net.service

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/home/ubuntu/bin/lxc-nat start
ExecStop=/home/ubuntu/bin/lxc-nat stop

[Install]
WantedBy=multi-user.target
登録 (/etc/systemd/systemへシンボリックリンク作成)
$ sudo systemctl enable ~/.config/systemd/system/lxc-nat.service

自分でインストール先へ直接ln -sしてもいいかもしれないけど、systemctlにやってもらいます。

確認
$ sudo systemctl list-unit-files | grep lxc
lxc-nat.service                        enabled
lxc-net.service                        enabled
lxc.service                            enabled
lxcfs.service                          enabled

/home/ubuntu/bin/lxc-nat の中身の例
#!/bin/sh -

nat()
{
    iptables -t nat $1 PREROUTING -p tcp -d 192.168.1.10/32 --dport 80 -j DNAT --to 10.0.3.10:80
    iptables -t nat $1 PREROUTING -p tcp --dport 8000 -j DNAT --to 10.0.3.11:80
}

case "$1" in
    start)
        nat -A
    ;;
    stop)
        nat -D
    ;;
    *)
        echo "Usage: $0 {start|stop}"
        exit 2
esac
exit $?
nat()関数で-A/-D切り替えできるように(start/stop両方に書かないで済むように)。
後は、nat()内のiptables記述を増やすなり編集するなりするだけ。

例の場合の、10.0.3.10 のコンテナも自動起動するようにコンテナ設定ファイルに
lxc.start.auto = 1
としておくと尚良し。

2015/09/28

unicornとsidekiqをユーザレベルのsystemdで自動起動

lxcコンテナでRailsアプリである録画システムを動かすことに成功したので、永続化のためにコンテナの再起動時に自動起動するようにしたいわけです。

今までどおりなら、/etc/init.dに複雑な起動と停止のスクリプトを書くか、/etc/init.にupstart用のスクリプトファイルを置くのでしょうが、せっかく Ubuntu15.04 から systemd がデフォルトになったので、これを期にRailsアプリのデーモンをsystemdで扱えるようにしようと。

systemd/ユーザ
こちらを読むと、ユーザがログインした時に起動して、ログアウトで停止するようなサービスを定義できるらしい。更にそれをブート時に起動して、ユーザのログイン・ログアウトに依存しない永続化ができるらしい。これだぁ。

lxcコンテナでも、systemd --user デーモンがちゃんと稼働している。

今回デーモン起動したいサービスは、Railsのunicornとsidekiq。
これをsystemdのユーザサービスとして起動できるようにする。

systemdのサービスUnit定義ファイルを、ユーザのホームディレクトリの所定の場所に記述

SidekiqサービスUnitファイル .config/systemd/user/sidekiq.service

[Unit]
Description=Recman Sidekiq

[Service]
Type=forking
WorkingDirectory=%h/recman
ExecStart=/usr/local/bin/sidekiq -C ./config/sidekiq.yml -d

[Install]
WantedBy=default.target

UnicornサービスUnitファイル .config/systemd/user/unicorn.service

[Unit]
Description=Recman Unicorn

[Service]
Type=forking
WorkingDirectory=%h/recman
ExecStart=/usr/local/bin/unicorn_rails -D -c ./config/unicorn.rb -E development

[Install]
WantedBy=default.target

ExecStartは、デーモン起動したらすぐに終わるコマンドなので、Typeはforking。
Railsアプリのディレクトリに移ってから実行する必要があるので、WorkingDirectoryで指定する。
%hはユーザのホームディレクトリを示す変数。
インストール先は、default.target。systemd --userによって、defaut.targetユニットが予め存在するらしい。

とにかく、すごいシンプル!こんなでいいんだ。って感じ。

これらを登録して自動起動設定するまでの流れ
以降の操作はすべてユーザ権限で行える。systemctlに'--user'を付ける。

ユーザUnitファイルを読みこませる
$ systemctl --user daemon-reload

ちゃんと認識したか確認

$ systemctl --user list-unit-files

sidekiq.serviceとunicorn.serviceが、disableの状態でリストされる
ここまで行けば、手動での起動と停止ができるようになる

手動起動
$ systemctl --user start sidekiq
$ systemctl --user start unicorn

停止
$ systemctl --user stop sidekiq
$ systemctl --user stop unicorn

ExecStopを記述しなくても、停止させることが出来る。楽ちん。

自動起動するように有効化する

$ systemctl --user enable sidekiq
$ systemctl --user enable unicorn

再度確認してみた時の結果
ubuntu@recman:~$ systemctl --user list-unit-files
UNIT FILE            STATE   
sidekiq.service      enabled 
systemd-exit.service static  
unicorn.service      enabled 
basic.target         static  
bluetooth.target     static  
default.target       static  
exit.target          disabled
paths.target         static  
printer.target       static  
shutdown.target      static  
smartcard.target     static  
sockets.target       static  
sound.target         static  
timers.target        static

ubuntu@recman:~$ systemctl --user status unicorn
unicorn.service - Recman Unicorn
   Loaded: loaded (/home/ubuntu/.config/systemd/user/unicorn.service; enabled; vendor preset: enabled)
   Active: active (running) since 日 2015-09-27 17:59:14 JST; 15min ago
 Main PID: 1575 (ruby2.1)
   CGroup: /lxc/recman-1/user.slice/user-1000.slice/user@1000.service/unicorn.service

ubuntu@recman:~$ systemctl --user status sidekiq
sidekiq.service - Recman Sidekiq
   Loaded: loaded (/home/ubuntu/.config/systemd/user/sidekiq.service; enabled; vendor preset: enabled)
   Active: active (running) since 日 2015-09-27 17:59:12 JST; 15min ago
 Main PID: 1562 (ruby2.1)
   CGroup: /lxc/recman-1/user.slice/user-1000.slice/user@1000.service/sidekiq.service


永続化

ここまでだと、ログアウトと同時にユーザサービスは停止してしまうので、ブート起動して、ログイン/ログアウトに依存しない状態にする

$ sudo loginctl enable-linger

これでログアウトしても停止しなくなるし、lxcコンテナ起動時にサービスが開始される。
解除したい場合は、disable-lingerする。

$ sudo loginctl disable-linger

正直なところ、このloginctlとlingerがよく理解できていない。
enable-lingerするとセッションが壊れる危険性?があるということだが、どういうことなのか・・・

だけれど、とにかく、こうしたいっていう目的は達成である。

2015/09/26

lxcコンテナに録画システムを移設する

今年リニューアル構築した録画システムをlxcコンテナ上で動くようにしたいわけです。
なぜなら、面白そうだから。ですが、独立したクリーンな環境で動かせることが一番の魅力です。
色々実験していると環境がぐちゃぐちゃになっていきます。
いつ何を入れたのか、どこに何を設定したのか、次第にあやふやになってくる。etckeeperだけじゃキープできない。
「ちょっと試したい時にコンテナをクローンして試して失敗したら消す!」をしたい。

録画システム用コンテナ作成後に、以下ゴニョゴニョする

ビルド環境

$ sudo apt-get install build-essential
(for recfsusb2n)
$ sudo apt-get install libboost-filesystem-dev libboost-thread-dev -y
(for epgdump)
$ sudo apt-get install cmake -y
(for FFmpeg)
$ sudo apt-get install yasm libx264-dev libfaac-dev
$ sudo apt-get install pkg-config

ビルドしてインストール

epgdump、ffmpeg は、
$ make install
recfsusb2n は Makefileにinstall記述がないので直接
$ sudo cp ./recfsusb2n /usr/local/bin

もうここは専用環境なのでバンバン/usr/localとか自由に使う。

udevdが動いてないので

どうやらコンテナではudevdが動かないらしい。前回は'video'グループ権限でアクセスできるような
ルール設定ファイルを /dev/udev/rules.d に置いたりしたけど、udevdが動いてないので意味が無い。
っていうか、後で気づいたことだけど、ホスト側で設定しておくべきことだった。
ホストに/etc/udev/rules.d/89-tuner.rulesを作成。内容は以下な感じ。
# FSUSB2N
SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="0511", ATTRS{idProduct}=="0029", MODE="0664", GROUP="video"
これでブート時にチューナデバイスのグループが'video'になる。

USBデバイス スルー

コンテナ上の録画プログラム(recfsusb2n)がチューナーデバイスを扱えるように、
ホストに接続されているUSBデバイスをlxcコンテナからアクセスできるようにする必要がある。
# usb device
lxc.cgroup.devices.allow = c 189:* rwm
# mount
lxc.mount.entry=/dev/bus/usb/001 dev/bus/usb/001 none bind,optional,create=dir
上の記述を、コンテナのコンフィグファイルに追記して、コンテナ再起動する。
ホスト側と同じ状態でUSBデバイスをシェアした状態になる
コンテナから ls -l /dev/bus/usb/001/* すると
crw-rw-r-- 1 root root  189, 0  9月 23 20:13 /dev/bus/usb/001/001
crw-rw-r-- 1 root root  189, 1  9月 23 20:13 /dev/bus/usb/001/002
crw-rw-r-- 1 root root  189, 2  9月 23 20:13 /dev/bus/usb/001/003
crw-rw-r-- 1 root root  189, 3  9月 23 20:13 /dev/bus/usb/001/004
crw-rw-r-- 1 root video 189, 4  9月 25 17:50 /dev/bus/usb/001/005
crw-rw-r-- 1 root root  189, 5  9月 23 20:13 /dev/bus/usb/001/006

rvm入れなくても大丈夫かな
ubuntu14.04の時は、パッケージインストールされるrubyが1.9.3だったり、独立したruby環境にしたかったのでrvmを入れていたけど、ubuntu15.04のrubyは2.1.2で新し目だし、コンテナ自体が独立しているので、rvmではなく、普通にパッケージインストールしたものを使う。

gemインストール
録画システムはrailsで構築しているので、システムパッケージをまるっと持ってきて
(gitで退避しておいたので、git cloneして復活)そこで

$ bundle

で必要なgemパッケージをインストール
おっと、bundleも入ってなかったか。bundleが成功するまで、色々足りていないものを入れる。

(引っかかった順)
$ sudo gem install bundler
$ sudo apt-get install ruby-dev
$ sudo apt-get install zlib1g-dev
$ sudo apt-get install mysql-server libmysqlclient-dev

bundleでgemインストールが完了したら
$ sudo mysql_secure_installation&アクセス用のmysqlユーザを作って権限設定とかして、、

DB作成とマイグレーション

$ RAILS_ENV=production bundle exec rake db:create
$ RAILS_ENV=production bundle exec rake db:migrate
$ RAILS_ENV=development bundle exec rake db:create
$ RAILS_ENV=development bundle exec rake db:migrate

NATで外と接続(ホストで)
$ sudo iptables -t nat -A PREROUTING -p tcp --dport 3000 -j DNAT --to <コンテナIP>:3000

したら、コンテナ側のrailsアプリ上で
$ rails s -b 0.0.0.0

http://ホスト:3000 へアクセスして、コンテナ上のwebrickサーバーが反応すればテストOK。

recmanとしてちゃんと稼動させる
テストは良好だったので、サーバーのデーモン駆動周りを整える
$ sudo apt-get install redis-server nginx -y

unicornとsidekiqを稼動させて、nginxからunicornへproxyするように。

最後にwheneverでcrontab更新
$ bundle exec whenever --update-crontab

NAT設定して、外からアクセスできるように
$ sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to <コンテナIP>:80


思っていたよりすんなり移設できたが、気になる点としては、Ubuntu15.04からinitデーモンが、upstartではなく、systemdになったらしいこと。upstartも使えるけど、systemd対応の方が今風なのだと。unicornとsidekiqの自動起動をsystemd対応にしてみたい。