MINCS – containers in the shell script

39
第8回 コンテナ型仮想化の情報交換会@東京 1 MINCS – containers in the shell script @mhiramat Github.com/mhiramat/

Transcript of MINCS – containers in the shell script

Page 1: MINCS – containers in the shell script

第8回 コンテナ型仮想化の情報交換会@東京1

MINCS – containers in the shell script

@mhiramatGithub.com/mhiramat/

Page 2: MINCS – containers in the shell script

2

スピーカーについて

@mhiramatLinux カーネルハッカーだが

最近はコードをいじれていない (><)o

Perftools や Ftrace のメンテナンスをしています

Page 3: MINCS – containers in the shell script

3

はじめに

今日の発表はほぼ100% シェルスクリプトの話ですが、

発表者はUSP とは特に関係ありません。

でも興味があるのでいつでも呼んでください

Page 4: MINCS – containers in the shell script

4

コンテナとは ?

コンテナ=Docker という風潮

他にも様々なOSS 実装が・・・

LXC

Runc

OpenVZ

Etc…

そもそもコンテナって何?

Page 5: MINCS – containers in the shell script

5

Docker

Docker は多くの機能を提供

コンテナ機能

ソフトウェアのパッケージ化

レイヤーの管理

ソフトウェアのカタログ化

REST API

Etc…

どうやって動いてる?

Page 6: MINCS – containers in the shell script

6

Docker は素晴らしい、でも・・・

ちょっと大きすぎないですか?

全機能が一つになっていて個々の機能が試しにくい

シングルバイナリ全部入り

Unix 哲学を思い出そう

Keep It Simple, Stupid既存のツールで実現できそう

Page 7: MINCS – containers in the shell script

7

Let's mimic it!

最小限の実行環境分離を試そう

名前空間を使ってみよう

デバイスファイルなどをバインドしてみよう

Chroot/pivot_root で rootfs を変えてみよう

Capabilities や CPUSET も使ってみよう

ファイルシステムのレイヤリングもしてみよう

Overlayfs がある!( 3.18 以降)

コンテナイメージの管理もしてみよう

Page 8: MINCS – containers in the shell script

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 などでも動作可能

Page 9: MINCS – containers in the shell script

9

MINCS

フロントエンド(パーサー)

minc

marten

polecat

バックエンド

minc-exec

minc-coat

minc-leash

minc-farm

minc-trapper

Page 10: MINCS – containers in the shell script

10

フロントエンドスクリプト

MINCS のフロントエンドコマンド

Minc : 指定したコマンドをコンテナ内で動かす

Marten : マルチレイヤのコンテナイメージを管理する

Polecat : 自己実行形式のシングルバイナリコンテナアプリを作る

フロントエンドコマンドは基本的にパラメタのパースのみ

環境変数に変換してバックエンドを呼び出す

Marten/minc-farm の関係だけはまだちゃんとできていない

Page 11: MINCS – containers in the shell script

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

Page 12: MINCS – containers in the shell script

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 を実行

Page 13: MINCS – containers in the shell script

13

Dive into the shell script

minc コマンドがどうやってコンテナを作るか覗いてみよう

ステップ1  :コマンドライン処理と環境変数の設定

ステップ2  :minc-exec の実行

netns と cpumask の設定

新しい名前空間への移行

PID の保存と uts の設定

コンテナ用 rootfs のセットアップ

デバイスファイルのバインド

不要なマウントポイントの削除

新しい rootfs への移行と capabilities の設定

Page 14: MINCS – containers in the shell script

14

Minc: コマンドライン処理

シンプルなcase 文とwhile ループで処理

getopts は使いにくいので使っていない

While と shift コマンドでコマンドラインを処理するだけ

主に環境変数の設定

知らない引数が出てきたら取り敢えずコマンドと解釈 :)

ループ後の処理

必要なら後述する minc-farm を使って UUID からイメージを取り出す

trap コマンドで後処理を設定

Minc-exec を呼び出す

Page 15: MINCS – containers in the shell script

15

Minc-exec(1) :概要

基本的には自分で自分を呼び出すシェルスクリプト

名前空間を作る unshare コマンドは何かを実行しないといけない→自分自身を呼び出す(昔の名残)

以前は chns という 1 ファイルのスクリプトだった

1 度目の実行はコンテナ外部の処理

netns と cpuset の設定

2 度めの実行はコンテナ内部の処理

PID=1 かどうかを調べて切り替え

コンテナ内部から色んな物を隠す

デバイスファイル

procfs の再マウント

Page 16: MINCS – containers in the shell script

16

Minc-exec(2) : netns/cpuset

netns の設定

MINC_NETNS で指定された名前の netns を ip netns コマンドで作る

終了時に Trap コマンドで削除

一応仮想 eth pair だけは作成(でも IP アドレスは割り当てていない)

Docker のような iptables 対応はしない

pipework などがあるため

CPUSET でCPU マスクを設定

taskset コマンドで実行する CPU を指定する

cgroups のような細かい設定はなし

Page 17: MINCS – containers in the shell script

17

閑話休題: Trap コマンド

Trap コマンドは偉大

シグナルによる割り込みとシェルスクリプト終了をハンドリングできる

スクリプト内の関数も呼び出せる

→後処理関数を作れる

minc では積極的に trap を活用

一時ファイル /PID ファイルの削除

終了時の info 表示

Ctrl+c の抑制

Page 18: MINCS – containers in the shell script

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 “$@”

Page 19: MINCS – containers in the shell script

19

Minc-exec(4) : PID と utsname の処理

PID の取得

後でコンテナ外から PID を知るために使う

Unshare は fork してしまうのでコンテナ内からしか分からない :)

実は procfs を remount しない限り /proc はコンテナ外と同じ

/proc/self が参照できるのだ!

utsname の設定

MINC_UTSNAME で設定した名前を hostname に指定

Page 20: MINCS – containers in the shell script

20

Minc-exec(5): マウント名前空間とオーバレイ

mount 名前空間の完全隔離

unshare で mount 名前空間を隔離しても、他の名前空間に操作が伝搬する!

何だこの仕様・・・。

Mount --make-rprivate / を実行

/ 以下でのマウント操作を他の名前空間に通知しない

Minc-coat でオーバレイを行う

Minc-coat バックエンド(後述)で rootfs に作業ディレクトリを被せる

この後の作業が元になる rootfs に変更をしないようにする

rootfs を壊していい場合は --direct オプションを付ける

Page 21: MINCS – containers in the shell script

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

Page 22: MINCS – containers in the shell script

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 化が必要かも)

Page 23: MINCS – containers in the shell script

23

閑話休題:デバッグ

デバッグはどうするの?

動作確認だけなら、 --debugオプションを minc に渡します

set -x が有効になりログが吐かれます

いろいろコマンドラインから確認したい→ブレークポイントに bash と書く

その処理の時点で bash が起動するので適当に楽しんでください

その時の状態だけを確認したい

コマンドを書いてください

MINCS は単なるシェルスクリプト

変えたいように変えればいい

Page 24: MINCS – containers in the shell script

24

Minc-exec(7) : mountpoint の後処理

不要なmountpoint を消す

残しておくと chroot 後にも見えてしまう

umount 出来ないものがあるので pivot_root で処理を行う

動作を見るためにdf -h を挟んでモニタしてみるとよくわかる

Page 25: MINCS – containers in the shell script

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実行前

Page 26: MINCS – containers in the shell script

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

特殊ファイルを用意

Page 27: MINCS – containers in the shell script

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ではこの辺りが残る

Page 28: MINCS – containers in the shell script

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などを削除する

Page 29: MINCS – containers in the shell script

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した

Page 30: MINCS – containers in the shell script

30

Minc-leash

Leash 関数= “Least capabilities shell”Linux ケーパビリティの制限と chroot を行う

capsh(libcap) を利用

実行ユーザも変更

gid と uid で変更

ケーパビリティを変更しないなら Chroot だけ実行

Wash 関数

シェルスクリプト間でやり取りしてきた環境変数を消す

env と grep でMINC_* を取り出して unset

Page 31: MINCS – containers in the shell script

31

MINCS のユースケース

小さいことはいいことだ(確信

コンテナ機能を学ぶ教材としての利用

Docker も裏でいろいろしている

なんでこんな制約があるの?→実際作るとわかる

プロトタイプやテストのお供に

小さいことはいいことだ(重要

組み込みデバイスにコンテナを求めるのは間違っているだろうか

Docker(14MB~) vs MINCS+Busybox(~4MB, シェルや基本コマンド込 )

→Boot2MINC

Page 32: MINCS – containers in the shell script

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 の絞り込みを行えば更に小さくできる

Page 33: MINCS – containers in the shell script

33

Marten: コンテナイメージの管理

コンテナは実行環境の分離

minc はコンテナ機能のみを提供

-rオプションで rootfs の指定が可能

毎回 debootstrap とかで再構築するの? Fedora とか CentOS はどうする?

-tオプションで指定した作業結果の再利用が面倒

今回作成した作業結果を反映させたい・・・

Overlayfs ベースのコンテナイメージ管理をしよう

Docker っぽく UUID っぽいもので識別

イメージ間の依存関係をシェルスクリプトで整理しよう

Docker export/save したイメージの再利用

Page 34: MINCS – containers in the shell script

34

デモ

Minc

Marten

Boot2minc

Page 35: MINCS – containers in the shell script

35

TODO

mincTTY を screen/tmux経由でサポート

pipework とのコラボ (netns なんとかしよう )

cgroups によるメモリや CPU利用量の制限 (minc-cage)

btrfs や dm-thin のサポート (plugin方式? )

martenコンテナの export と署名添付機能の追加( O.C.I.互換?)

UUIDベースのコンテナ起動機能追加(より Docker っぽく)

Page 36: MINCS – containers in the shell script

36

課題

Testcasesシェルスクリプトで書けるだろう

Capsh の問題

Capabilities の設定のために Capsh を使うと” sh -c”経由になってしまう

実行コマンドにエスケープ文字列などが渡せない問題・・・

Page 37: MINCS – containers in the shell script

37

まとめ

今日伝えたかったこと

「Linux 特有の機能を組み合わせたらコンテナができる」

特殊な機能を使っているわけではなく基本的に全部対応するコマンドがある

つまり

「Linux +シェルスクリプト最強!」

Page 38: MINCS – containers in the shell script

38

おわり

ありがとうございました m(_ _)m

https://github.com/mhiramat/mincs

Page 39: MINCS – containers in the shell script

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