MINCS – containers in the shell script
-
Upload
masami-hiramatsu -
Category
Engineering
-
view
3.591 -
download
3
Transcript of MINCS – containers in the shell script
第8回 コンテナ型仮想化の情報交換会@東京1
MINCS – containers in the shell script
@mhiramatGithub.com/mhiramat/
2
スピーカーについて
@mhiramatLinux カーネルハッカーだが
最近はコードをいじれていない (><)o
Perftools や Ftrace のメンテナンスをしています
3
はじめに
今日の発表はほぼ100% シェルスクリプトの話ですが、
発表者はUSP とは特に関係ありません。
でも興味があるのでいつでも呼んでください
4
コンテナとは ?
コンテナ=Docker という風潮
他にも様々なOSS 実装が・・・
LXC
Runc
OpenVZ
Etc…
そもそもコンテナって何?
5
Docker
Docker は多くの機能を提供
コンテナ機能
ソフトウェアのパッケージ化
レイヤーの管理
ソフトウェアのカタログ化
REST API
Etc…
どうやって動いてる?
6
Docker は素晴らしい、でも・・・
ちょっと大きすぎないですか?
全機能が一つになっていて個々の機能が試しにくい
シングルバイナリ全部入り
Unix 哲学を思い出そう
Keep It Simple, Stupid既存のツールで実現できそう
7
Let's mimic it!
最小限の実行環境分離を試そう
名前空間を使ってみよう
デバイスファイルなどをバインドしてみよう
Chroot/pivot_root で rootfs を変えてみよう
Capabilities や CPUSET も使ってみよう
ファイルシステムのレイヤリングもしてみよう
Overlayfs がある!( 3.18 以降)
コンテナイメージの管理もしてみよう
8
MINCS
Minimum Container Shell-scriptshttps://github.com/mhiramat/mincs基本機能
PID/Net/UTS/Mount 名前空間
Overlayfs によるレイヤリング
その他 Capabilities 、 CPUSET など
POSIX shell script (not bash script)busybox shell/dash などでも動作可能
9
MINCS
フロントエンド(パーサー)
minc
marten
polecat
バックエンド
minc-exec
minc-coat
minc-leash
minc-farm
minc-trapper
10
フロントエンドスクリプト
MINCS のフロントエンドコマンド
Minc : 指定したコマンドをコンテナ内で動かす
Marten : マルチレイヤのコンテナイメージを管理する
Polecat : 自己実行形式のシングルバイナリコンテナアプリを作る
フロントエンドコマンドは基本的にパラメタのパースのみ
環境変数に変換してバックエンドを呼び出す
Marten/minc-farm の関係だけはまだちゃんとできていない
11
minc
MINCS のメインツール
コマンドをコンテナの中で動かす
Chroot コマンドのようなもの(ただしちょっと制限がある)
あるいは Docker run? :)
デフォルトで名前空間分離と overlayfs による作業空間分離を行う
Docker のようなコンテナイメージは不要
Chroot のような rootfs ディレクトリも不要( / に直接 overlayfs を被せられる)
ネットワークはそのまま見える
[mhiramat@localhost mincs]$ sudo ./minc ps -efUID PID PPID C STIME TTY TIME CMDroot 1 0 0 10:58 ? 00:00:00 ps -ef
12
minc: Usage
Usage: minc [options] [command]Options:
-r/--root ROOTDIR rootfs とするディレクトリを指定。省略時は / を使う。
-t/--temp TEMPDIR 作業ディレクトリを指定( -k 有効)。省略すると mktemp で作成。
-k mktemp で作った作業ディレクトリを消さない
--name UTSNAME コンテナ内で使うホスト名を指定する
--debug デバッグ用にログを出す
Command は省略すると (root ユーザの )$SHELL を実行
13
Dive into the shell script
minc コマンドがどうやってコンテナを作るか覗いてみよう
ステップ1 :コマンドライン処理と環境変数の設定
ステップ2 :minc-exec の実行
netns と cpumask の設定
新しい名前空間への移行
PID の保存と uts の設定
コンテナ用 rootfs のセットアップ
デバイスファイルのバインド
不要なマウントポイントの削除
新しい rootfs への移行と capabilities の設定
14
Minc: コマンドライン処理
シンプルなcase 文とwhile ループで処理
getopts は使いにくいので使っていない
While と shift コマンドでコマンドラインを処理するだけ
主に環境変数の設定
知らない引数が出てきたら取り敢えずコマンドと解釈 :)
ループ後の処理
必要なら後述する minc-farm を使って UUID からイメージを取り出す
trap コマンドで後処理を設定
Minc-exec を呼び出す
15
Minc-exec(1) :概要
基本的には自分で自分を呼び出すシェルスクリプト
名前空間を作る unshare コマンドは何かを実行しないといけない→自分自身を呼び出す(昔の名残)
以前は chns という 1 ファイルのスクリプトだった
1 度目の実行はコンテナ外部の処理
netns と cpuset の設定
2 度めの実行はコンテナ内部の処理
PID=1 かどうかを調べて切り替え
コンテナ内部から色んな物を隠す
デバイスファイル
procfs の再マウント
16
Minc-exec(2) : netns/cpuset
netns の設定
MINC_NETNS で指定された名前の netns を ip netns コマンドで作る
終了時に Trap コマンドで削除
一応仮想 eth pair だけは作成(でも IP アドレスは割り当てていない)
Docker のような iptables 対応はしない
pipework などがあるため
CPUSET でCPU マスクを設定
taskset コマンドで実行する CPU を指定する
cgroups のような細かい設定はなし
17
閑話休題: Trap コマンド
Trap コマンドは偉大
シグナルによる割り込みとシェルスクリプト終了をハンドリングできる
スクリプト内の関数も呼び出せる
→後処理関数を作れる
minc では積極的に trap を活用
一時ファイル /PID ファイルの削除
終了時の info 表示
Ctrl+c の抑制
18
Minc-exec(3) : unshare による名前空間移動
Unshare コマンドで名前空間を移動
自分自身を指定して起動
Pid, mount, ipc, uts の名前空間を分離
unshare -iumpf $0 “$@”
netns だけは ip netns exec を使う
ip netns exec $MINC_NETNS unshare -iumpf $0 “$@”
19
Minc-exec(4) : PID と utsname の処理
PID の取得
後でコンテナ外から PID を知るために使う
Unshare は fork してしまうのでコンテナ内からしか分からない :)
実は procfs を remount しない限り /proc はコンテナ外と同じ
/proc/self が参照できるのだ!
utsname の設定
MINC_UTSNAME で設定した名前を hostname に指定
20
Minc-exec(5): マウント名前空間とオーバレイ
mount 名前空間の完全隔離
unshare で mount 名前空間を隔離しても、他の名前空間に操作が伝搬する!
何だこの仕様・・・。
Mount --make-rprivate / を実行
/ 以下でのマウント操作を他の名前空間に通知しない
Minc-coat でオーバレイを行う
Minc-coat バックエンド(後述)で rootfs に作業ディレクトリを被せる
この後の作業が元になる rootfs に変更をしないようにする
rootfs を壊していい場合は --direct オプションを付ける
21
Minc-coat :オーバレイの実装
一時的ディレクトリに root, storage, work を作成
root : overlayfs をマウントするマウントポイント → $RD
storage: コンテナ内の作業結果が記録されるディレクトリ → $UD
work: overlayfs 用のWorkdir (ファイルシステムが使う) → $WD
Overlay ファイルシステムで新しい rootfs を作る
名前空間の分離だけでなく、レイヤを作ってストレージを分離
カーネルバージョンによって少しオプションが違うので注意が必要
Upstream カーネルに対応するマウントコマンド
mount -t overlay -o upperdir=$UD,lowerdir=$BASEDIR,workdir=$WD overlayfs $RD
Ubuntu14.10 用のoverlayfs に対応するマウントコマンド
mount -t overlayfs -o upperdir=$UD,lowerdir=$BASEDIR overlayfs $RD
22
Minc-exec(6) :特殊ファイルの処理
特殊ファイル・ディレクトリの処理
まず /etc, /dev, /sys, /proc をminc-coat で作ったディレクトリに作成
/dev 以下のバインドマウント
ダミーファイルを作ってmount --bind で実体とバインドする( symlink みたいなもの)
/dev/console, /dev/null, /dev/zero, /dev/random, /dev/urandom, /dev/mqueue正直このあたりは使う人が勝手に書き換えてほしい
/dev/pts だけはnewinstanceオプションで新規作成
/proc のマウント
元の /proc はリードオンリーにしておく
此処から先は他のプロセスPID が見えなくなる
/proc/sys など書き込まれたくないものは、元の /proc (既に ro 化済)の物を見せる
/sys のバインドマウント
バインドマウントを使ってマウント(これも ro 化が必要かも)
23
閑話休題:デバッグ
デバッグはどうするの?
動作確認だけなら、 --debugオプションを minc に渡します
set -x が有効になりログが吐かれます
いろいろコマンドラインから確認したい→ブレークポイントに bash と書く
その処理の時点で bash が起動するので適当に楽しんでください
その時の状態だけを確認したい
コマンドを書いてください
MINCS は単なるシェルスクリプト
変えたいように変えればいい
24
Minc-exec(7) : mountpoint の後処理
不要なmountpoint を消す
残しておくと chroot 後にも見えてしまう
umount 出来ないものがあるので pivot_root で処理を行う
動作を見るためにdf -h を挟んでモニタしてみるとよくわかる
25
Minc-exec(7) : mountpoint の後処理
不要なmountpoint を消す
残しておくと chroot 後にも見えてしまう
umount 出来ないものがあるので pivot_root で処理を行う
Filesystem Size Used Avail Use% Mounted ondevtmpfs 740M 0 740M 0% /devtmpfs 748M 0 748M 0% /dev/shmtmpfs 748M 8.5M 740M 2% /runtmpfs 748M 0 748M 0% /sys/fs/cgroup/dev/sda2 15G 8.6G 6.5G 58% /
minc実行前
26
Minc-exec(7) : mountpoint の後処理
不要なmountpoint を消す
残しておくと chroot 後にも見えてしまう
umount 出来ないものがあるので pivot_root で処理を行う
Filesystem Size Used Avail Use% Mounted on/dev/sda2 15G 8.6G 6.5G 58% /devtmpfs 740M 0 740M 0% /devtmpfs 748M 0 748M 0% /dev/shmtmpfs 748M 0 748M 0% /sys/fs/cgrouptmpfs 748M 8.5M 740M 2% /runoverlayfs 15G 8.6G 6.5G 58% /tmp/minc1012-NpuyIA/roottmpfs 748M 0 748M 0% /tmp/minc1012-NpuyIA/root/devdevtmpfs 740M 0 740M 0% /tmp/minc1012-NpuyIA/root/dev/consoledevtmpfs 740M 0 740M 0% /tmp/minc1012-NpuyIA/root/dev/nulldevtmpfs 740M 0 740M 0% /tmp/minc1012-NpuyIA/root/dev/zerodevtmpfs 740M 0 740M 0% /tmp/minc1012-NpuyIA/root/dev/randomdevtmpfs 740M 0 740M 0% /tmp/minc1012-NpuyIA/root/dev/urandom
特殊ファイルを用意
27
Minc-exec(7) : mountpoint の後処理
不要なmountpoint を消す
残しておくと chroot 後にも見えてしまう
umount 出来ないものがあるので pivot_root で処理を行う
Filesystem Size Used Avail Use% Mounted on/dev/sda2 15G 8.6G 6.5G 58% /.origdevtmpfs 740M 0 740M 0% /.orig/devtmpfs 748M 0 748M 0% /.orig/dev/shmtmpfs 748M 0 748M 0% /.orig/sys/fs/cgrouptmpfs 748M 8.5M 740M 2% /.orig/runoverlayfs 15G 8.6G 6.5G 58% /tmpfs 748M 0 748M 0% /devdevtmpfs 740M 0 740M 0% /dev/consoledevtmpfs 740M 0 740M 0% /dev/nulldevtmpfs 740M 0 740M 0% /dev/zerodevtmpfs 740M 0 740M 0% /dev/randomdevtmpfs 740M 0 740M 0% /dev/urandom
1回目のpivot_rootではこの辺りが残る
28
Minc-exec(7) : mountpoint の後処理
不要なmountpoint を消す
残しておくと chroot 後にも見えてしまう
umount 出来ないものがあるので pivot_root で処理を行う
Filesystem Size Used Avail Use% Mounted on/dev/sda2 15G 8.6G 6.5G 58% /.origoverlayfs 15G 8.6G 6.5G 58% /tmpfs 748M 0 748M 0% /devdevtmpfs 740M 0 740M 0% /dev/consoledevtmpfs 740M 0 740M 0% /dev/nulldevtmpfs 740M 0 740M 0% /dev/zerodevtmpfs 740M 0 740M 0% /dev/randomdevtmpfs 740M 0 740M 0% /dev/urandom
古い/devなどを削除する
29
Minc-exec(7) : mountpoint の後処理
不要なmountpoint を消す
残しておくと chroot 後にも見えてしまう
umount 出来ないものがあるので pivot_root で処理を行う
Filesystem Size Used Avail Use% Mounted onoverlayfs 15G 8.6G 6.5G 58% /tmpfs 748M 0 748M 0% /devdevtmpfs 740M 0 740M 0% /dev/consoledevtmpfs 740M 0 740M 0% /dev/nulldevtmpfs 740M 0 740M 0% /dev/zerodevtmpfs 740M 0 740M 0% /dev/randomdevtmpfs 740M 0 740M 0% /dev/urandom
古い/にpivotしなおした後新しい/にchrootした
30
Minc-leash
Leash 関数= “Least capabilities shell”Linux ケーパビリティの制限と chroot を行う
capsh(libcap) を利用
実行ユーザも変更
gid と uid で変更
ケーパビリティを変更しないなら Chroot だけ実行
Wash 関数
シェルスクリプト間でやり取りしてきた環境変数を消す
env と grep でMINC_* を取り出して unset
31
MINCS のユースケース
小さいことはいいことだ(確信
コンテナ機能を学ぶ教材としての利用
Docker も裏でいろいろしている
なんでこんな制約があるの?→実際作るとわかる
プロトタイプやテストのお供に
小さいことはいいことだ(重要
組み込みデバイスにコンテナを求めるのは間違っているだろうか
Docker(14MB~) vs MINCS+Busybox(~4MB, シェルや基本コマンド込 )
→Boot2MINC
32
Boot2minc
自家製最小 ISO イメージ+MINCShttps://github.com/mhiramat/boot2minc
Minimal Linux Live ( https://github.com/ivandavidov/minimal )から fork
boot2minc に含まれるもの
Linux kernel
Busybox(+unshare パッチ )
MINCS
以上 :)
カーネル含めて 8MBぐらいのイメージ (Qemu-kvm で起動可能 )Busybox とカーネルの config の絞り込みを行えば更に小さくできる
33
Marten: コンテナイメージの管理
コンテナは実行環境の分離
minc はコンテナ機能のみを提供
-rオプションで rootfs の指定が可能
毎回 debootstrap とかで再構築するの? Fedora とか CentOS はどうする?
-tオプションで指定した作業結果の再利用が面倒
今回作成した作業結果を反映させたい・・・
Overlayfs ベースのコンテナイメージ管理をしよう
Docker っぽく UUID っぽいもので識別
イメージ間の依存関係をシェルスクリプトで整理しよう
Docker export/save したイメージの再利用
34
デモ
Minc
Marten
Boot2minc
35
TODO
mincTTY を screen/tmux経由でサポート
pipework とのコラボ (netns なんとかしよう )
cgroups によるメモリや CPU利用量の制限 (minc-cage)
btrfs や dm-thin のサポート (plugin方式? )
martenコンテナの export と署名添付機能の追加( O.C.I.互換?)
UUIDベースのコンテナ起動機能追加(より Docker っぽく)
36
課題
Testcasesシェルスクリプトで書けるだろう
Capsh の問題
Capabilities の設定のために Capsh を使うと” sh -c”経由になってしまう
実行コマンドにエスケープ文字列などが渡せない問題・・・
37
まとめ
今日伝えたかったこと
「Linux 特有の機能を組み合わせたらコンテナができる」
特殊な機能を使っているわけではなく基本的に全部対応するコマンドがある
つまり
「Linux +シェルスクリプト最強!」
39
Docker 連携の例
# docker save centos | gzip - > centos.tar.gz # marten import centos.tar.gzImporting image: centos511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c1585b12ef8fd57065237a6833039acc0e7f68e363c15d8abb5cacce7143a1f7de8a8efe422e6104930bd0975c199faa15da985b6694513d2e873aa2da9ee402174c # marten imagesID SIZE NAME511136ea3c5a 4.0K (noname)5b12ef8fd570 4.0K (noname)8efe422e6104 224M centos # minc -r centos /bin/bash