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対応にしてみたい。

2015/09/25

lxc上でlxdを動かしたり・・・

シングルマシン1台でopenstackやらdevstackやらnovaのlxd版やら、色々試してみたものの動いたり動かなかったり、動いてもすごいリソース食いだったり(当たり前か)を一通り体感したので、原点に立ち戻った。

方針
* ホストはUbuntu15.04をクリーンインストールしたところに、openssh-serverと lxc だけ入れる
* lxdはlxcコンテナの中で動かす(仮想の仮想)
* 構築した録画システムは、lxcコンテナ上で稼働させる


インストール関係は過去の記事で書いたので、ここでは上のプランを実施する上で行ったことを書く。

lxcコンテナ作成と日本語化
ぼやぼやしているうちに、lxcもバージョンアップしたのか15.04だからか不明だけれど、テンプレートがスクリプトになったため、テンプレートからコンテナ作成するとものすごく遅くなった。日本語化も同時にやってくれるので面倒な無いけど、余計なものも入ってしまう。
ということでイメージダウンロードでコンテナ作成する。

$ sudo lxc-create -n test -t download -d ubuntu -r vivid -a amd64
前はこんな感じで出来た気がするのだけれど、-dが理解してくれない。仕方ないので

$ sudo lxc-create -t download  -n
で、対話式でやった。

このクラウドイメージは素の状態なので、日本語化とか、いくつか手を加える
#!/bin/bash
lxc-start -n $1
lxc-attach -n $1 -- adduser ubuntu
lxc-attach -n $1 -- passwd ubuntu
lxc-attach -n $1 -- apt-get install language-pack-ja -y
lxc-attach -n $1 -- apt-get install emacs -y
lxc-attach -n $1 -- update-locale LANG=ja_JP.UTF-8
lxc-attach -n $1 -- dpkg-reconfigure tzdata
lxc-stop -n $1
こんなスクリプトを書いてそのコンテナに対して実行すると、デフォルトのubuntuユーザと日本語化を一気にやる。といってもubuntuパスワード設定と最後のタイムゾーン設定は対話になる。

毎回 lxc-create するのは面倒なので、lxc-createしたものをマスターにして、実際使うコンテナはlxc-cloneして使うようにする。それから、マスターにはopenssh-serverは入れないようにする。
入れちゃうとクローンしてもちゃんと動かない。MACアドレスが変わるので認証コードも変える必要があるが、作りなおさせるのも面倒なので、最初から入れない。

sshサーバーはクローンして入れる
#!/bin/bash
lxc-clone $1 $2
lxc-start -n $2
lxc-attach -n $2 -- apt-get update
lxc-attach -n $2 -- apt-get install openssh-server -y
何度もやるのは面倒なので、クローンしてsshを入れるところまでのスクリプトを書いておく。

lxdを動かすlxcコンテナのNesting設定
lxcコンテナのコンフィグファイルに以下の設定を追記する
# Nesting
lxc.mount.auto = cgroup
lxc.aa_profile = lxc-container-default-with-nesting
これで、lxcコンテナにlxdをインストールして普通にlxdコンテナを稼働させることができるようになる。
自分の理解では、lxcならば仮想の仮想になってもCPUコストはほとんど変わらない。ただしネットワークがブリッジのブリッジになるのでその分通信パフォーマンスは落ちるか。
この辺は仮想ネットワーク構成を工夫すれば改善できるんじゃないかな。

何でこんなことするかというと、ホストに直接lxdを入れても動くが、いざlxcを使おうと思ってもlxd用のlxcになっているらしく思うように使えなかったため。dnsmasqも変。なので一番思い通りに動くlxcを親にした。いずれ lxdの方が一般的になってくるだろうから、その時に再考する。

コンテナ上でdevstack動くかな?
性懲りもなくやってみたが、当然というべきなのか動かなかった。
/lib/modulesがなかったり、ebtablesでエラーになったり。。コンテナ自体がカーネルを持っていないからだろうね。独立したカーネル上で動くKVMベースだったら動かせるのかなあ。

録画環境のコンテナ移設は次回。

2015/09/16

devstackにlxdコンテナ組み込み やってみたが・・


先の記事では、Shuttle DS57Uというスモール環境にopenstackを構築してみた話を書きました。

インスタンスをkvmからlxdに切り替えてみたいと思って色々試したのですが、残念ながら最後のインスタンス作成で失敗します。
手順は間違っていないと思っているので、手順自体は記録しておこうと思って書きました。

lxd は、lxcとopenstack/novaを繋ぐハイパバイザーってことらしい。
nova-compute-lxd をdevstackに組み込めば良いだけ。
Introduction to nova-compute-lxd が全てを教えてくれています。

Ubuntu 15.04 vividをクリーンインストールする(opensshだけ入れる)

$ sudo apt-get update
$ sudo apt-get install ntp git -y
$ sudo apt-get upgrade -y

ネットワークを固定IPにして、ipv6をディスエーブルする。お好みで。



パスワード要求無しでsudo出来るスーパーユーザを作る

$ sudo adduser stack
「stack ALL=(ALL) NOPASSWD:ALL」 を /etc/sudoers に追記

以降は全てstackユーザでの操作

$ su - stack

最初にnova-compute-lxdを/opt/stackにクローンする
別の場所にすると適応時に面倒なので、素直にここに置く

$ git clone https://github.com/lxc/nova-compute-lxd /opt/stack/nova-compute-lxd
nova-compute-lxd/contrib/devstack/README.first に書かれている通りにdevstackをクローン

$ git clone https://github.com/openstack-dev/devstack /opt/stack/devstack

devstackに対してlxdの設定スクリプトを実行する

$ cd /opt/stack/nova-compute-lxd
$ contrib/devstack/prepare_devstack.sh

すると、/opt/stack/devstackに対して、さくっと色々やってくれます。

/opt/stack/devstack/localrcファイルが作られているので、そこに自分の設定を追記します。
VIRT_DRIVER=lxd
export NON_STANDARD_REQS=1

SERVICE_TOKEN=shuttle01
ADMIN_PASSWORD=admin
MYSQL_PASSWORD=$ADMIN_PASSWORD
RABBIT_PASSWORD=$ADMIN_PASSWORD
SERVICE_PASSWORD=$ADMIN_PASSWORD

LOGFILE=$DEST/logs/stack.sh.log
LOGDAYS=2
な感じ。FIXED_RANGEやFLOATING_RANGEやら追加してもいいでしょう。自分はデフォルトでいいのでこんなもんです。

devstackのインストール&開始

$ cd /opt/stack/devstack
$ ./stack.sh

lxc、lxd、その他諸々、この中で一気にインストールとセットアップが行われます。
30分ほど待つと、めでたく lxdがインテグレーションされたopenstackが稼働します。

しかし残念ながら今のところ、コンテナ起動が成功しません。



クラウドイメージの準備・やってみたものの・・


標準で入っている cirros イメージはlxdコンテナとしては起動できません。

$ wget -O vivid-server-cloudimg-amd64-root.tar.gz https://cloud-images.ubuntu.com/vivi/current/vivid-server-cloudimg-amd64-root.tar.gz

$ source /opt/stack/devstack/openrc
$ glance image-create --name='lxc' --container-format=bare --disk-format=raw < vivid-server-cloudimg-amd64-root.tar.gz

これでイメージが取り込まれます。
が、これを起動したんですが、残念ながら起動できませんでした。

No valid host was found. There are not enough hosts available

になってしまう・・・

nova-compute-lxd/contrib/images/convert-ubuntu-cloud-images.sh というのがあったので

$ sudo ./convert-ubuntu-cloud-images.sh ~/vivid-server-cloudimg-amd64-root.tar.gz ~/vivid-server-cloudimg-amd64-root2.tar.gz
$ glance image-create --name='lxc2' --container-format=bare --disk-format=raw < vivid-server-cloudimg-amd64-root2.tar.gz

でトライしてみましたが、結果は変わらず。

ログを見ると、n-condのでエラーが出ている。
ソースを軽く追いかけると、selected_hostsがない。もしくは見つけられない?状態らしい。
インスタンスが必要とするvCPUの数と用意されている数を比較して足りない場合エラーっていう
処理のところらしいけど。

イメージが同行という以前のところでコケている感じ。まあ、もう少し待って再トライです。

openstackをプアーに体感してみる

lxc使ってみていたら、仮想環境に興味が湧いてしまいまして、色々やってみました。

今まで作ったものをバックアップして、Ubuntu 15.04 (vivid) でOS入れ直し。
このバージョンから仮想化向けの機能が追加されたので、良いかなと思って。

仕事では普通にVMWareを使ってVM構築したり、サーバー立ち上げたりしているけれど、クライアントユーザとして使っているだけだったので、実はあまり仮想化を知らなかったりする。
おもちゃにして使っている Shuttle DS75U を仮想ホストにしてプライベートクラウドしたら、どうなってしまうんだろ?しかも1台だけで、、、という個人的興味でのアプローチです。

lxcが動いているので、CPUの仮想化支援機能は問題ない。動くはずだ。

まずは OpenStack Installation Guide for Ubuntu 14.04 を読んで見るものの・・・
ほー。それぞれのコンポーネント構成は何とか理解するものの、 これを手動でセットアップするのはほぼ不可能に近い。

ということで Ubuntu OpenStack Installer を試みる

嬉しいことに、このインストーラーで入れるとコンピュートノードはlxcで作られる構成にしてくれるらしい。

本格的な構成とシングルマシン用のインストーラーということらしいので Single Installer Guide を頼りに実行してみた。こには
って書いてあるが、見なかったことにしてインストールしてみた。ものすごい時間を要したが特に途中何の問題もなくインストールが完了した。

が、しかし、それぞれのコンポーネント用のインスタンスが8つぐらい稼働しようとする。かなり頑張っていたが、3つ目くらいのインスタンス起動の途中から先に進まなくなった。
それぞれのインスタンスのメモリ設定が4GBとか6GBとかになっていて、実メモリ4GBしかないDS75Uでは太刀打ちできなかった様だ。。。

DevStack を使って入れる

DevStackっていうのは最初からあるインストーラーで、開発者のテスト環境構築にも使われているようで、タイニーな環境でも動こうような構成で入れてくれるらしい。

Ubuntu15.04では途中でこけていたが・・・
実はこの記事を書く前に、やった時にはUbuntu15.04ではインストーラースクリプトが途中でこけてしまう問題があった。DevStack自体が15.04に対応していなかったため、rabibitmqのスタートアップ周りで不具合があったのだ。
やってみると確かに、この部分で失敗した。 問題の箇所に関するバグIssue https://bugs.launchpad.net/devstack/+bug/1449056 を追っていたら、つい最近解決したコミットが!ということでmasterブランチをプルし直して
All-In-One Single Machineに書いてあるとおりにやったら問題なく15.04でも成功した。 やほ~。

ほぼデフォルト状態でのセットアップなので、次は、仮想ノードタイプをkvmからlxcにしたり、ネットワーク周りをいじったりしてみようと思う。