ローファイ日記

出てくるコード片、ぼくが書いたものは断りがない場合 MIT License としています http://udzura.mit-license.org/

Haconiwa 近況(released 0.3.0)

Haconiwaの近況報告です。みなさんは、庭ですか?

まず、haconiwa-0.3.0 今すぐダウンロードは以下です:

次に何があったかを話します。

(サンプルコマンドの色々な操作は、rootで行う前提となっております)

haconiwa new で設定ファイルのテンプレートを作れるようにしました

ファイル名を指定するだけでOKです。コンテナ名やrootfsのロケーションはオプションで指定可能です。ヘルプを参照。

root@localhost:~/hacos# haconiwa new ./test.haco                                                                                                                                         
assign  new haconiwa name = haconiwa-2c876e2f
assign  rootfs location = /var/lib/haconiwa/2c876e2f
create  ./test.haco

以下のように、それぞれの設定項目の説明をコメントでやっていきつつ、サンプルを作成しています。編集してもいいですが、ひとまずそのまま動かせます。

なお、この時点ではコンテナ(rootfs)の作成をしていませんのでお気軽にnewできます。

# -*- mode: ruby -*-
Haconiwa.define do |config|
  # The container name and container's hostname:
  config.name = "haconiwa-2c876e2f"
  # The first process when invoking haconiwa run:
  config.init_command = "/bin/bash"
  # If your first process is a daemon, please explicitly deamonize by:
  # config.deamonize!

  # The rootfs location on your host OS
  # Pathname class is useful:
  root = Pathname.new("/var/lib/haconiwa/2c876e2f")
  config.chroot_to root
  # ...
end

haconiwa create でrootfsの作成とシェルによるプロビジョンができます

生成されたファイルには以下のような項目が存在します。

  # The bootstrap process...
  # Choose lxc or debootstrap:
  config.bootstrap do |b|
    b.strategy = "lxc"
    b.os_type  = "alpine"

    # b.strategy = "debootstrap"
    # b.variant = "minbase"
    # b.debian_release = "jessie"
  end
  # Check that the required binary is installed(lxc-create / debootstrap)

これはコンテナの初期化、具体的にはコンテナの新しいrootとなるファイルシステム(rootfs)を作成するための設定を記述する箇所です。

今のところ、以下の2つの戦略をサポートしています。

  • lxc-create で作る
  • debootstrap で作る

具体的な設定項目は README を。

なお、rootfsの作成のために対応する外部コマンドが必要ですので、事前にパッケージマネージャ等でインストールしておいてください。

また、簡単なプロビジョニングもサポートしています。今のところシェルスクリプトのみですが...(name: オプションで名前をつけて、後述する haconiwa provision でピンポイントで再実行したりもできます)

  # The provisioning process...
  # You can declare run_shell step by step:
  config.provision do |p|
    p.run_shell <<-SHELL
apk add --update bash
    SHELL
  end

プロビジョニング単体で行うための haconiwa provision も別途用意しています。

root@localhost:~/hacos# haconiwa create ./test.haco 
Start bootstrapping rootfs with lxc-create...
[bootstrap.lxc]:        Obtaining an exclusive lock... done
[bootstrap.lxc]:
[bootstrap.lxc]:        ==> Fetching and/or verifying APK keys
[bootstrap.lxc]:        alpine-devel@lists.alpinelinux.org-4a6a0840.rsa.pub: OK
[bootstrap.lxc]:        alpine-devel@lists.alpinelinux.org-4d07755e.rsa.pub: OK
[bootstrap.lxc]:        alpine-devel@lists.alpinelinux.org-5243ef4b.rsa.pub: OK
[bootstrap.lxc]:        alpine-devel@lists.alpinelinux.org-524d27bb.rsa.pub: OK
[bootstrap.lxc]:        alpine-devel@lists.alpinelinux.org-5261cecb.rsa.pub: OK
[bootstrap.lxc]:
[bootstrap.lxc]:        ==> Fetching apk-tools static binary
[bootstrap.lxc]:        tar: Ignoring unknown extended header keyword 'APK-TOOLS.checksum.SHA1'
...
Command success: /bin/sh -xe exited 0
Success!

Ubuntu Xenialでプロビジョンが失敗することがあるのを確認しています。以下のような操作で進むかもしれない)

HACO_ROOTFS=/var/lib/haconiwa/2c876e2f
sudo cp /etc/{resolv.conf,hosts} $HACO_ROOTFS/etc/

既存のnamespaceに入ることができます、すなわち ip netns 等で事前作成したNetwork namespaceを使えます

さて、コンテナを作成したら今までと同様 hacoiwa run で立ち上げることができますが...

hacoファイルにこういう記述があります。

  # The namespaces to unshare:
  config.namespace.unshare "mount"
  config.namespace.unshare "ipc"
  config.namespace.unshare "uts"
  config.namespace.unshare "pid"

  # You can use existing namespace via symlink file. e.g.:
  # config.namespace.enter "net", via: "/var/run/netns/sample001"

今まで、network namespaceのunshareは非推奨だった(単にunshareしても使えなかった)のですが、ネットワークの分離のサポートの第一歩として、すでに作成済みのnetnsを利用できるようにしてみました。

以下のようにシュッシュと新しいnetnsを作ります。

ip netns add haco001
ip link add haco01 type veth peer name eth101
ip link set haco01 up
ip link set eth101 netns haco001 up
ip addr add 172.16.0.10/24 dev haco01
ip netns exec haco001 ip addr add 172.16.0.11/24 dev eth101
ip netns exec haco001 ip link set lo up

ホストはこんな感じに:

root@localhost:~/hacos# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 08:00:27:33:82:8a brd ff:ff:ff:ff:ff:ff
    inet 10.0.2.15/24 brd 10.0.2.255 scope global enp0s3
       valid_lft forever preferred_lft forever
3: enp0s8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 08:00:27:44:64:c9 brd ff:ff:ff:ff:ff:ff
    inet 192.168.98.202/24 brd 192.168.98.255 scope global enp0s8
       valid_lft forever preferred_lft forever
# ... 
7: haco01@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether ca:df:a0:95:a6:da brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.16.0.10/24 scope global haco01
       valid_lft forever preferred_lft forever

新しいnetns内部ではこんな感じになります:

root@localhost:~/hacos# sudo ip netns exec haco001 ip a             
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
6: eth101@if7: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state LOWERLAYERDOWN group default qlen 1000
    link/ether fa:37:14:16:6b:b4 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.16.0.11/24 scope global eth101
       valid_lft forever preferred_lft forever

ここで、設定の以下の箇所をコメントインし、こんな感じに変えます。

  # You can use existing namespace via symlink file. e.g.:
  # config.namespace.enter "net", via: "/var/run/netns/sample001"
  config.namespace.enter "net", via: "/var/run/netns/haco001"

haconiwa run すると、さっき作成したnetnsに確かに入っていると確認できます。ちなみにこのままでは外に出られませんので、より実用的にはブリッジなりIPマスカレードなりをやっていく必要があります...

root@localhost:~/hacos# haconiwa run ./test.haco
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
6: eth101@if7: <NO-CARRIER,BROADCAST,MULTICAST,UP,M-DOWN> mtu 1500 qdisc noqueue state LOWERLAYERDOWN qlen 1000
    link/ether fa:37:14:16:6b:b4 brd ff:ff:ff:ff:ff:ff
    inet 172.16.0.11/24 scope global eth101
       valid_lft forever preferred_lft forever

# ping も通る
/ # ping 172.16.0.10
PING 172.16.0.10 (172.16.0.10): 56 data bytes
64 bytes from 172.16.0.10: seq=0 ttl=64 time=0.058 ms
64 bytes from 172.16.0.10: seq=1 ttl=64 time=0.121 ms
64 bytes from 172.16.0.10: seq=2 ttl=64 time=0.073 ms
^C
--- 172.16.0.10 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.058/0.084/0.121 ms

なお、デフォルトで生成される設定で、 sysfs を新しいネームスペースでリマウントする(ここもhacoファイルでいじれたりします。READMEに書いてます)ので、親のデバイスが見えるということもありません。参考

/ # ls -l /sys/class/net/
total 0
lrwxrwxrwx    1 root     root             0 Aug 22 02:47 eth101 -> ../../devices/virtual/net/eth101
lrwxrwxrwx    1 root     root             0 Aug 22 02:47 lo -> ../../devices/virtual/net/lo

[開発] CIの高速化

開発の話なのですが、CIを高速化しました。具体的には ./mruby 配下をキャッシュしただけなんですが...

github.com

こうだったのが

f:id:udzura:20160822115506p:plain

こうなったのでだいぶ能率あがる感あります :)

f:id:udzura:20160822115513p:plain

単にmrubyをキャッシュするだけでは困る場面もあるので、ひとまず rake consistent というコマンドを打つと、 mruby_version.lock というファイルに従って、もしバージョンが変わっていればmrubyのリチェックアウト(それに伴いmrbgemsのクリンナップ)を行ってくれます。

ということでmrubyのバージョンを上げるのも簡単になっています。

NEW_HASH=22dc84f72adfd742f7079b1862d3f033e4781550
echo $NEW_HASH > mruby_version.lock
rake consistent

haconiwaの開発に興味のある方は是非頭の片隅に ;)

ということで、そろそろ割と普通にコンテナを作れるようになった気がするのですが、大事な機能とかUnixっぽい流儀とかで抜けがあるかもしれない。

PR/Issue 歓迎します(日本語でも歓迎しますが、私の方で参考英訳を添付するかもしれません)。


ところで、個人的な話ですが来るRubyKaigiのspeakerに選ばれております。

当日は、このHaconiwaのお話をする予定ですので、Rubyist、mrubyist、コンテナ人間の皆様は京都で握手しましょう!

rubykaigi.org

Enjoy handmaid container~