- 追記
- 直近の Docker 界隈について
- Linux コンテナ
- haconiwa
- haconiwa で学ぶ Linux コンテナ
- 俺は Linux コンテナについてなんにも解っていなかった(まとめ)
- 参考
追記
@udzura さんからコメントを頂きました!
なぜOK牧場かというと、Network namespaceのunshare設定が入っていないので、ホストとネット設定を共有しているだけなのであった... (そのうちveth引っ張る手順を書きます。検証レポート誠に感謝。 / “俺…” https://t.co/yuwoLfeLCo
— Uchio KONDO (@udzura) 2017年5月1日
有難うございました!
直近の Docker 界隈について
以下のようなキーワードがインターネット上を駆け巡っている気がする。
自分自身、これのらのキーワードについて中身を全く理解出来ていない。
また、Docker を触る機会も減っているのも事実で、やばいな〜、やばいな〜と思いながら、ゴールデンウィーク突入となったので、改めて、Docker というよりも Linux コンテナ技術について勉強してみたいと思う。
Linux コンテナ
参考
Linux コンテナとは
- 隔離された空間でプロセスを実行する
- プロセスに対してリソース制限を設定する
Linux コンテナを構成する主な機能
Linux コンテナはカーネルに含まれる以下のような機能を利用して実装されている。
名前 | 役割 |
---|---|
namespace | OS リソースの隔離(プロセスをグループ化して他のリソースと隔離) |
cgroup | ホストの物理リソースに対する制限(グループ化したプロセス対するリソース制限) |
capability | root 権限をプロセスやファイルに割り当てる |
上記以外にも Bind mount/chroot
や Resource limit(rlimit)
及び setuid/setgid
等の機能も利用されている。
OS リソース毎の Namespace
OS リソース毎に以下のような Namespace が提供されている。
- Mount Namespace
- UTS Namespace
- PID Namespace
- IPC Namespace
- User Namespace
- Network Namespace
- cgroup Namespace
各 Namespace はカーネルのバージョンを追う毎に追加実装されている。
cgroup サブシステム
cgroup は cgroup ファイルシステムという仮想的なファイルシステムを使って操作し、Namespace 同様に以下のようなサブシステムと呼ばれる機能でリソースを扱う。
- cpu
- cpuacct
- cpuset
- device
- freezer
- memory
- blkio
他にも hugetlb や perf_event 等が提供されている。
こんなにざっくりでは、Linux コンテナは語れないと思うけど
最低限、上記のようなキーワードだけはしっかりと覚えておきたい。
haconiwa
haconiwa とは
- @udzura さんがメインとなって実装されている mruby で実装された Linux コンテナのランタイム
- Ruby DSL で Linux コンテナの設定を記述することが出来て、自分だけの Linux コンテナを作成することが出来る
haconiwa で何が出来ると?
haconiwa 導入
haconiwa は mruby-cli でビルドされたバイナリで配布されており、packagecloud にてパッケージ配布されているので、以下のようにインストールする。
# # Ubunt/xenial に導入する # $ cat /etc/lsb-release DISTRIB_ID=Ubuntu DISTRIB_RELEASE=16.04 DISTRIB_CODENAME=xenial DISTRIB_DESCRIPTION="Ubuntu 16.04.2 LTS" # # 事前に lxc を導入済み # $ dpkg --list | grep lxc ii liblxc1 2.0.7-0ubuntu1~16.04.2 amd64 Linux Containers userspace tools (library) ii lxc 2.0.7-0ubuntu1~16.04.2 all Transitional package for lxc1 ii lxc-common 2.0.7-0ubuntu1~16.04.2 amd64 Linux Containers userspace tools (common tools) ii lxc-templates 2.0.7-0ubuntu1~16.04.2 amd64 Linux Containers userspace tools (templates) ii lxc1 2.0.7-0ubuntu1~16.04.2 amd64 Linux Containers userspace tools ii lxcfs 2.0.6-0ubuntu1~16.04.1 amd64 FUSE based filesystem for LXC ii python3-lxc 2.0.7-0ubuntu1~16.04.2 amd64 Linux Containers userspace tools (Python 3.x bindings) # # haconiwa パッケージの導入 # $ curl -s https://packagecloud.io/install/repositories/udzura/haconiwa/script.deb.sh | sudo bash $ sudo apt-get install haconiwa=0.8.5-1 # # ヘルプの確認 # ubuntu@ubuntu-xenial:~$ haconiwa haconiwa - The MRuby on Container commands: new - generate haconiwa's config DSL file template create - create the container rootfs provision - provision already booted container rootfs archive - create, provision, then archive rootfs to image start - run the container attach - attach to existing container reload - reload running container parameters, following its current config kill - kill the running container version - show version revisions - show mgem/mruby revisions which haconiwa bin uses Invoke `haconiwa COMMAND -h' for details.
はじめての haconiwa (1)
以下のように haconiwa new
を実行すると定義ファイルの雛形を生成する事が出来る。
haconiwa new \ --name=my-first-container \ --root=/var/lib/haconiwa/my-first-container my-first-container.haco
以下のように出力され、my-first-container.haco
というファイルがカレントディレクトリに生成されている。
create my-first-container.haco
.haco
ファイルの中身は以下のようになっている。
$ grep -v -e '^\s*#' -e '^\s*$' my-first-container.haco Haconiwa.define do |config| config.name = "my-first-container" config.init_command = "/bin/bash" root = Pathname.new("/var/lib/haconiwa/my-first-container") config.chroot_to root config.bootstrap do |b| b.strategy = "lxc" b.os_type = "alpine" end config.provision do |p| p.run_shell <<-SHELL apk add --update bash SHELL end config.add_mount_point "tmpfs", to: root.join("tmp"), fs: "tmpfs" config.mount_network_etc(root, host_root: "/etc") config.mount_independent "procfs" config.mount_independent "sysfs" config.mount_independent "devtmpfs" config.mount_independent "devpts" config.mount_independent "shm" config.namespace.unshare "mount" config.namespace.unshare "ipc" config.namespace.unshare "uts" config.namespace.unshare "pid" end
はじめての haconiwa (2) 〜 コンテナ作成 〜
以下のように create
オプションを指定して haconiwa を実行すると、lxc-create
が実行されて alpine linux のコンテナの作成が開始される。
sudo haconiwa create my-first-container.haco
以下のように出力される。
ubuntu@ubuntu-xenial:~$ sudo haconiwa create my-first-container.haco Creating rootfs of my-first-container... Start bootstrapping rootfs with lxc-create... [bootstrap.lxc]: Obtaining an exclusive lock... done ... Command success: lxc-create -n my-first-container -t alpine --dir /var/lib/haconiwa/my-first-container exited 0 Success! Start provisioning... Running provisioning with shell script... [provison.shell-1]: + apk add --update bash [provison.shell-1]: fetch http://mirror.yandex.ru/mirrors/alpine//v3.5/main/x86_64/APKINDEX.tar.gz [provison.shell-1]: (1/5) Installing ncurses-terminfo-base (6.0-r7) [provison.shell-1]: (2/5) Installing ncurses-terminfo (6.0-r7) [provison.shell-1]: (3/5) Installing ncurses-libs (6.0-r7) [provison.shell-1]: (4/5) Installing readline (6.3.008-r4) [provison.shell-1]: (5/5) Installing bash (4.3.46-r5) [provison.shell-1]: Executing bash-4.3.46-r5.post-install [provison.shell-1]: Executing busybox-1.25.1-r0.trigger [provison.shell-1]: OK: 14 MiB in 21 packages Command success: /bin/sh -xe exited 0 Success!
はじめての haconiwa (3) 〜 コンテナ起動 〜
以下のように run
又は start
オプションを指定して haconiwa を実行するとコンテナが起動する!!
$ sudo haconiwa run my-first-container.haco Container fork success and going to wait: pid=1893 bash-4.3# ps PID USER TIME COMMAND 1 root 0:00 /bin/bash 2 root 0:00 ps bash-4.3# exit exit Container(1893) finish detected: #<Process::Status: pid=1893,exited(0)> Container successfully exited: #<Process::Status: pid=1893,exited(0)> One of supervisors finished: 1892, #<Process::Status: pid=1892,exited(0)> $ sudo haconiwa start my-first-container.haco Container fork success and going to wait: pid=1906 bash-4.3# ps PID USER TIME COMMAND 1 root 0:00 /bin/bash 2 root 0:00 ps bash-4.3# exit exit Container(1906) finish detected: #<Process::Status: pid=1906,exited(0)> Container successfully exited: #<Process::Status: pid=1906,exited(0)> One of supervisors finished: 1905, #<Process::Status: pid=1905,exited(0)>
ちなみに、起動したコンテナからインターネットへのアクセスだって OK 牧場。
$ sudo haconiwa start my-first-container.haco Container fork success and going to wait: pid=1919 bash-4.3# bash-4.3# ping www.yahoo.com -c 3 PING www.yahoo.com (106.10.139.246): 56 data bytes 64 bytes from 106.10.139.246: seq=0 ttl=63 time=137.592 ms 64 bytes from 106.10.139.246: seq=1 ttl=63 time=158.620 ms 64 bytes from 106.10.139.246: seq=2 ttl=63 time=106.147 ms --- www.yahoo.com ping statistics --- 3 packets transmitted, 3 packets received, 0% packet loss round-trip min/avg/max = 106.147/134.119/158.620 ms bash-4.3#
haconiwa で学ぶ Linux コンテナ
.haco ファイル再掲
ここからは、以下の .haco
ファイルを利用して Linux コンテナを弄ってみたいと思う。
Haconiwa.define do |config| config.name = "my-first-container" config.init_command = "/bin/bash" root = Pathname.new("/var/lib/haconiwa/my-first-container") config.chroot_to root config.bootstrap do |b| b.strategy = "lxc" b.os_type = "alpine" end config.provision do |p| p.run_shell <<-SHELL apk add --update bash SHELL end config.add_mount_point "tmpfs", to: root.join("tmp"), fs: "tmpfs" config.mount_network_etc(root, host_root: "/etc") config.mount_independent "procfs" config.mount_independent "sysfs" config.mount_independent "devtmpfs" config.mount_independent "devpts" config.mount_independent "shm" config.namespace.unshare "mount" config.namespace.unshare "ipc" config.namespace.unshare "uts" config.namespace.unshare "pid" end
namespace を弄る前に
Namespace の機能についておさらい。
namespace | 機能 |
---|---|
mount | Namespace 内の mount / umount が他の Namespace に影響を与えないようにする |
ipc | SysV IPC オブジェクトや POSIX メッセージキューの隔離 |
uts | hostname や uname の結果を分離 |
pid | PID 空間の分離、新しい PID Namespace では PID 1 から始まる |
uts
と pid
について弄ってみる。
namespace uts
以下のように config.namespace.unshare "uts"
をコメントアウト。
ubuntu@ubuntu-xenial:~$ diff -u my-first-container.haco.bk my-first-container.haco --- my-first-container.haco.bk 2017-04-29 23:35:21.313501998 +0000 +++ my-first-container.haco 2017-04-29 23:35:32.883713999 +0000 @@ -59,7 +59,7 @@ # The namespaces to unshare: config.namespace.unshare "mount" config.namespace.unshare "ipc" - config.namespace.unshare "uts" + # config.namespace.unshare "uts" config.namespace.unshare "pid" # You can use existing namespace via symlink file. e.g.:
何が起きるのか。
ubuntu@ubuntu-xenial:~$ hostname ubuntu-xenial ubuntu@ubuntu-xenial:~$ sudo haconiwa run my-first-container.haco Container fork success and going to wait: pid=3449 bash-4.3# hostname ubuntu-xenial
uts
は hostname
や uname
の結果を隔離する namespace であるが、unshare
を行っていない為、hostname
の結果が隔離されておらず、ホストと同じ hostname
の結果を返している状態。
ちなみに、この状態でコンテナ内からホスト名を変更してみると、以下のように怒られる。
bash-4.3# hostname foo hostname: sethostname: Operation not permitted
これは、sethostname(2) の
EPERM sethostname() において、呼び出した人が CAP_SYS_ADMIN ケーパビリティ (capability) を持っていなかった。
とある事から察するに、haconiwa においてコンテナ内部からのホスト名変更は Linux capability により制御されていると思われる。
改めて、config.namespace.unshare "uts"
をコメントインして haconiwa run
してみる。
ubuntu@ubuntu-xenial:~$ hostname ubuntu-xenial ubuntu@ubuntu-xenial:~$ sudo haconiwa run my-first-container.haco Container fork success and going to wait: pid=3651 bash-4.3# hostname my-first-container
hostname
コマンドの実行結果がホスト側とコンテナ内で異なっており、コンテナに隔離された状態となっている。
尚、unshare
コマンドで uts
のみを分離した場合には以下のような挙動となり、隔離した環境において実施した hostname
コマンドの影響は限定的となっていることが解る。
# # uts namespace を隔離 # ubuntu@ubuntu-xenial:~$ sudo unshare --uts -- /bin/bash root@ubuntu-xenial:~# hostname ubuntu-xenial root@ubuntu-xenial:~# hostname foo root@ubuntu-xenial:~# hostname foo # # 別端末で hostname を確認(hostname 変更の影響は出ていない) # ubuntu@ubuntu-xenial:~$ hostname ubuntu-xenial ubuntu@ubuntu-xenial:~$ hostname ubuntu-xenial
namespace pid
以下のように config.namespace.unshare "pid"
をコメントアウト。
$ diff -u my-first-container.haco.bk my-first-container.haco --- my-first-container.haco.bk 2017-04-29 23:35:21.313501998 +0000 +++ my-first-container.haco 2017-04-29 23:56:19.213720735 +0000 @@ -60,7 +60,7 @@ config.namespace.unshare "mount" config.namespace.unshare "ipc" config.namespace.unshare "uts" - config.namespace.unshare "pid" + # config.namespace.unshare "pid" # You can use existing namespace via symlink file. e.g.: # config.namespace.enter "net", via: "/var/run/netns/sample001"
何が起きるのか…
ubuntu@ubuntu-xenial:~$ sudo haconiwa run my-first-container.haco Container fork success and going to wait: pid=3596 bash-4.3# ps aux | less PID USER TIME COMMAND 1 root 0:13 {systemd} /sbin/init 2 root 0:00 [kthreadd] 3 root 0:00 [ksoftirqd/0] ... 3596 root 0:00 /bin/bash 3602 root 0:00 [kworker/u4:2] 3605 root 0:00 ps aux 3606 root 0:00 less
ホスト側のプロセスが丸見え状態になっている。
改めて、config.namespace.unshare "pid"
をコメントインして haconiwa run
してみる。
ubuntu@ubuntu-xenial:~$ sudo haconiwa run my-first-container.haco Container fork success and going to wait: pid=3623 bash-4.3# ps aux PID USER TIME COMMAND 1 root 0:00 /bin/bash 2 root 0:00 ps aux
プロセス ID は 1
から開始され、ホスト側のプロセス ID を見ることは出来ない状態になっておりコンテナに隔離された状態となっている。
俺は Linux コンテナについてなんにも解っていなかった(まとめ)
haconiwa は
Linux コンテナ
- 自分は本当になんにも解っていなかった
- Linux コンテナとは namespace による OS リソースの隔離、cgroup によるマシンリソースの制限等の Linux カーネルの機能を寄せ集めて実装されている
- Docker は良しなに Linux コンテナをラップして使いやすくしただけ(それが凄いことなんだけど)で、コンテナを動かすのに Docker は必ずしも必要無いんだなってことが解った
次回は
cgroup や capability に触れられれば良いかなと考えている。
参考
Linux コンテナ神資料
Linux コンテナを語る上で欠かせない神資料達。
- https://speakerdeck.com/tenforward/osc2017-osaka
- https://speakerdeck.com/tenforward/kof-2016
- http://gihyo.jp/admin/serial/01/linux_containers
- https://speakerdeck.com/hayajo/tukututexue-bulinuxkontenafalseli-ce
haconiwa 神資料
haconiwa を触る上で欠かす事の出来ない神資料達。
- https://speakerdeck.com/udzura/learn-you-a-linux-namespace-for-great-good
- https://speakerdeck.com/udzura/haconiwa-fukuoka-ruby-award-presentation-2017
- https://speakerdeck.com/udzura/haconiwa-and-future-os
- https://speakerdeck.com/udzura/the-haconiwa-internals
- https://speakerdeck.com/udzura/haconiwa-on-the-pavement
- https://speakerdeck.com/udzura/mruby-on-container
- https://speakerdeck.com/udzura/haconiwa-intro