Linux Namespaces 第8回 コンテナ型仮想化の情報交換会@東京 @masami256
Linux Namespaces第8回 コンテナ型仮想化の情報交換会@東京
@masami256
目次
● Readme● Namespaces?● System calls● Kernel Implementation● FAQ
@masami256● Linuxカーネルのメモリーリークを直したり
○ ackされたりされなかったり● Linuxカーネルもくもく会を開催したり
○ at 秋葉原● ラズパイ向けのdockerイメージ作ってたり
○ Arch Linuxのラズパイ● Arch LinuxのAURにPKGBUILDを公開したり● Fedora ProjectでQAをやったり
○ [email protected]● きらら、きららミラク、きららキャラット、きららMAXは欠かさず購入
○ ゆゆ式は哲学(`・ω・´)キリッ● 大家さんは思春期!も良いですね(*´ω`*)
○ まんがタイム・まんがタイムファミリーで連載中○ アニメ化企画進行中 (ヽ=´▽`=)ノ
Readme● kernelとlibcの実装は以下のバージョンで確認Linux kernel
version 4.1■ http://lxr.free-electrons.com/?v=4.1
○ glibc version 2.21■ http://sourceware.org/git/?p=glibc.git;a=commit;
h=4e42b5b8f89f0e288e68be7ad70f9525aebc2cff● Man
○ man 7 namespace
Linuxのコンテナで使われる技術
● プロセス・リソース管理○ Namespaces○ cgroup
● ストレージバックエンド○ btrfs○ overlayfs○ aufs
Namespaces?
● Linuxの名前空間については@TenForwardさんの資料を読むのが確実です○ 今さら聞けない Linux コンテナの基礎
■ https://speakerdeck.com/tenforward/jin-sarawen-kenai-linux-kontenafalseji-chu-2015-06-20
Linux 4.1でサポートしている名前空間
名前空間 概要
IPC System Vのプロセス間通信、POSIXメッセージキュー
Net ネットワークデバイス、 IPv4・IPv6プロトコルスタック、ルーティングテーブル等々
Mount マウントポイント
PID PID
User UID、GID
UTS ホスト名
名前空間の機能概要
● リソースを管理する○ メモリ、cpuなどのリソースとは別○ IPC、ネットワーク、ホスト名など管理する仕組み
■ この仕組みをごそっと入れ替えることで名前空間の
分離が実現できる
名前空間の機能概要(Cont’d)
● 名前空間を分離したときの挙動○ 元の名前空間のコピーを作る
■ e.g. Mount名前空間○ データがなく完全に新規の状態
■ e.g. Net名前空間
主な登場人物
● 名前空間○ UTS、Net名前空間等
● NSProxy○ カーネルで各名前空間を管理している構造体
● 参照カウンタ○ 名前空間(個々の名前空間)の参照カウンタ○ NSProxyの参照カウンタ
名前空間の概要:UTS名前空間での例
pid:1001ppid:1000
NSProxy uts ns
pid:1002ppid:1000
nodename:foo
pid:1003ppid:1000 NSProxy uts ns nodename: bar
pid:1000ppid:900
pid1001と1002がuname -nするとfooが返る
pid1003がuname -nするとbarが返るpid1000の名前空間はこの図では省略
名前空間
名前空間
struct new_utsname
struct new_utsname
プロセスと名前空間
● プロセスの親子関係と名前空間の関係は別○ 基本は親プロセスと同じ名前空間に所属
■ デフォルト値をどうするかというところの話○ 親プロセスの名前空間からの独立は何時でも可能
■ ただし、例外あり● 後述します
名前空間のユーザ空間への見せ方
● 名前空間はファイルとして見える○ /proc/<pid>/ns/[namespace name]
masami@saga:~$ ls -la /proc/self/nstotal 0dr-x--x--x. 2 masami masami 0 Jul 15 23:39 ./dr-xr-xr-x. 9 masami masami 0 Jul 15 23:39 ../lrwxrwxrwx. 1 masami masami 0 Jul 15 23:39 ipc -> ipc:[4026531839]lrwxrwxrwx. 1 masami masami 0 Jul 15 23:39 mnt -> mnt:[4026531840]lrwxrwxrwx. 1 masami masami 0 Jul 15 23:39 net -> net:[4026531969]lrwxrwxrwx. 1 masami masami 0 Jul 15 23:39 pid -> pid:[4026531836]lrwxrwxrwx. 1 masami masami 0 Jul 15 23:39 user -> user:[4026531837]lrwxrwxrwx. 1 masami masami 0 Jul 15 23:39 uts -> uts:[4026531838]
● 見ての通りシンボリックリンク
● 4026531839は名前空間のinode番号○ このファイル自体はユーザ空間からは見えない
● setns(2)ではこのファイルのfdを使用する
ユーザ空間からの見え方
lrwxrwxrwx. 1 masami masami 0 Jul 15 23:39 ipc -> ipc:[4026531839]
システムコール - 名前空間の操作
● 名前空間に対してできる操作○ 親プロセスの名前空間を共有○ 親プロセスの名前空間から分離○ 別のプロセスの名前空間へ移動
名前空間に関連するシステムコール
● 3つあります○ clone(2)○ setns(2)○ unshare(2)
● setns(2)は純粋に名前空間操作用● その他の2関数は名前空間も操作できる
CLONE_NEWXXXフラグ
名前空間 フラグ
IPC Namespace CLONE_NEWIPC
Net Namespace CLONE_NEWNET
Mount Namespace CLONE_NEWNS
PID Namespace CLONE_NEWPID
User Namespace CLONE_NEWUSER
UTS Namespace CLONE_NEWUTS
● システムコールで名前空間を操作するためのフラグ○ clone(2)、unshare(2)で使用
clone(2)?
● 子プロセスを作成する○ fork(2)の仲間
■ フラグを色々設定して細かい制御が可能■ スレッドを作る場合にも使う
● カーネル内ではdo_fork()をfork/cloneの共通処理として使用
clone(2)と名前空間の操作
● プロセス起動時から新しい名前空間で動かしたい○ clone(2)を使う
● CLONE_NEWXXXフラグで指定しなかった名前空間○ 親プロセスと共有
clone(2) ちょっとした問題
● clone(2)に渡すflagsは符号ありの32bit整数
○ 新規にNamespaceを作ろうとした場合、使えるフラグに空きがない■ 唯一の空きはこれ(/usr/include/linux/sched.h)
○ 別件でclone4(2)を提案した人はいたけどmergeはされていない■ Attaching file descriptors to processes with CLONE_FD
● http://lwn.net/Articles/638613/
int clone(int (*fn)(void *), void *child_stack,int flags, void *arg, ... /* pid_t *ptid, struct user_desc *tls, pid_t *ctid */ );
/* 0x02000000 was previously the unused CLONE_STOPPED (Start in stopped state) and is now available for re-use. */
Linuxのプロセス作成(ざっくりと)
● ざっくりと言ってしまうと、fork(2)を実行したプロセスのコピーを作る○ ファイルディスクリプタやメモリ空間などはコピーではなくて同
じデータを共有します■ Copy on Write■ 詳しく知りたいなら以下の本が良いと思います■ なるほどUnixプロセス ― Rubyで学ぶUnixの基礎
● http://tatsu-zine.com/books/naruhounix● このデータのコピー/共有というところで、clone(2)よる共有の設
定、unshare(2)による分離が必要に
unshare(2)?
● 他のプロセスと共有しているデータを分離○ 名前空間各種
■ 所属している名前空間から離れ、新規に作成○ ファイルディスクリプタテーブル○ ファイルシステム属性
■ ルートディレクトリ■ umask
unshare(2)の制限
● PID名前空間は分離できない○ 当初サポートされていたけど以下のコミットで削除
■ pidns: Don't have unshare(CLONE_NEWPID) imply CLONE_THREAD● https://github.
com/torvalds/linux/commit/6e556ce209b09528dbf1931cbfd5d323e1345926
setns(2)?
● 所属したい名前空間のfdを使って、その名前空間に移動する○ fdは/proc/<pid>/nsにあるファイルのfdmasami@saga:~$ ls -la /proc/self/nstotal 0dr-x--x--x. 2 masami masami 0 Jul 15 23:39 ./dr-xr-xr-x. 9 masami masami 0 Jul 15 23:39 ../lrwxrwxrwx. 1 masami masami 0 Jul 15 23:39 ipc -> ipc:[4026531839]lrwxrwxrwx. 1 masami masami 0 Jul 15 23:39 mnt -> mnt:[4026531840]lrwxrwxrwx. 1 masami masami 0 Jul 15 23:39 net -> net:[4026531969]lrwxrwxrwx. 1 masami masami 0 Jul 15 23:39 pid -> pid:[4026531836]lrwxrwxrwx. 1 masami masami 0 Jul 15 23:39 user -> user:[4026531837]lrwxrwxrwx. 1 masami masami 0 Jul 15 23:39 uts -> uts:[4026531838]
setns(2)の制限
● 別のPID名前空間に所属させた場合○ そのプロセス自身のPID名前空間は変わらない
■ 子プロセスから名前空間が切り替わる
システムコールと名前空間の操作
親プロセスと名前空間を共有
親プロセスの名前空間から分離
別プロセスの名前空間へ移動
clone(2) ○ ○
unshare(2) ○
setns(2) ○fork(2)/vfork(2)は必ず親プロセスの名前空間を使用
名前空間を操作するタイミング
プロセス生成時 プロセス起動後
clone(2) ○
unshare(2) ○
setns(2) ○名前空間によってはどちらを使うかで挙動に差がある
PID名前空間の仕様
● clone(2)でプロセス生成時のみ、名前空間変更対象のプロセスは新しいPID名前空間に所属できる
● unshare(2)によるPID名前空間の分離は非サポート○ 一時期サポートされていたが「pidns: Don't have unshare
(CLONE_NEWPID) imply CLONE_THREAD」で外された
● https://github.
com/torvalds/linux/commit/6e556ce209b09528dbf1931cbfd5d323e1345926
● setns(2)による名前空間の移動○ 移動したプロセスのPID名前空間は変わらない○ このプロセスがfork()等で子プロセスを作ると、その子プロセスから移動し
た名前空間に所属する
User Namspaceの特殊なところ
● 基本的に各名前空間同士はデータ的に独立○ User Namespaceは例外
■ User名前空間以外は、その構造体にUser Namespaceのポインタを持つ
■ 名前空間を操作する際にケーパビリティがあるか
チェックするために使用
プロセスと名前空間 fork(2)実行時
pid: 1234ppid: 784
pid: 1192ppid: 784
pid: 1326ppid: 1192
pid ns mount ns net ns ipc ns user ns pid ns mount ns net ns ipc ns user ns
fork(2)では親プロセスと名前空間を共有 pid:1192がforkを実行し、pid:1326
が子プロセス
プロセスと名前空間 clone(2)実行時
pid: 1234ppid: 784
pid: 1192ppid: 784
pid: 1326ppid: 1192
pid ns mount ns net ns ipc ns user ns pid ns mount ns net ns ipc ns user ns
flagsでCLONE_NEWNETを指定した場合、Net Namespace以外は親プロセスと共有
net ns
プロセスと名前空間 setns(2)実行時
pid: 1234ppid: 784
pid: 1192ppid: 784
pid: 1326ppid: 1192
pid ns mount ns net ns ipc ns user ns pid ns mount ns net ns ipc ns user ns
setns(2)でNet Namespaceをpid 1234のnamespaceに所属させた場合
プロセスと名前空間 unshare(2)実行時
pid: 1234ppid: 784
pid: 1192ppid: 784
pid: 1326ppid: 1192
pid ns mount ns net ns ipc ns user ns pid ns mount ns net ns ipc ns user ns
fork(2)で親プロセスと名前空間を共有していた状態から、Network namespaceだけを分離した状態
net ns
ここまでのまとめ
● PID名前空間はちょっと扱いが特殊● User名前空間は他の名前空間と独立ではない
Linux Kernelでの実装
● nsproxy構造体○ 名前空間の一元管理
■ User名前空間は除く
● cred構造体○ User名前空間を管理
● nsfs○ 各名前空間をユーザ空間にエクスポート
■ /proc/<pid>/ns配下のファイル
NSProxy
● include/linux/nsproxy.h○ NSProxy自体の参照数と各名前空間へのポインタを保
持struct nsproxy { atomic_t count; struct uts_namespace *uts_ns; struct ipc_namespace *ipc_ns; struct mnt_namespace *mnt_ns; struct pid_namespace *pid_ns_for_children; struct net *net_ns;};
このnsproxyの参照数
nsproxy in task_struct
● 1プロセス/1スレッド(1 task_struct)につき1つ存在○ include/linux/sched.h
struct task_struct {~略~/* namespaces */ struct nsproxy *nsproxy;~略~
各名前空間の構造体
Namespace Name File
UTS uts_namespace include/linux/utsname.h
IPC ipc_namespace include/linux/ipc_namespace.h
Mount mnt_namespace fs/mount.h
PID pid_namespace include/linux/pid_namespace.h
Net net include/net/net_namespace.h
User user_namespace include/linux/user_namespace.h
各名前空間が持つ基本的なデータ
● 参照カウンタ○ 名前空間の分離をしない場合は参照数を増やして、プロセス間で共有する
ため■ カウントするのは参照しているNSProxyの数
● struct ns_common構造体○ procfsに関連するデータを持つ
■ inode■ struct proc_ns_operations
● setns(2)が/proc/<pid>/ns以下のファイルを使用するの覚えてま
すよね(・∀・)● struct user_namespace
○ User Namespace以外の名前空間はuser_namespaceのポインタをデータとして持つ
プロセスとNSProxyと名前空間の関係
例:PID 2000はUTS Namespaceだけ分離している
pid:1111
pid:2222
pid:2000
NSProxycount: 2
uts namespacecount: 1
pid namespacecount: 2
uts namespacecount: 1
NSProxycount: 1
NSProxyの作成/参照数を増やす条件
参照数を増やす NSProxyを新規作成
clone(2) 名前空間を分離しない場合(CLONE_NEWXXXが設定されいない場合)
1つでも名前空間を分離する場合
setns(2) 常に新規作成
unshare(2) 常に新規作成setns(2)とunshare(2)の場合、元のNSProxyの参照数を減らす処理も行う
ユーザ空間で名前空間の使用状況確認
● NSProxyは見れない● 個々の名前空間に関しては確認できる
○ 自分で数えれば
● unshare(2)とsetns(2)をした時にどう変わるか見てみます
unshare -u /bin/bashしたときNamespace inode(before) count(before) inode(after) inode(after)
Net 4026531957 : 91 4026531957 93
UTS4026531838 91 4026531838 92
N/A N/A 4026532111 1
IPC 4026531839 91 4026531839 93
PID 4026531836 91 4026531836 93
User 4026531837 91 4026531837 93
Mount 4026531840402653185740265321104026532124
88111
4026531840402653185740265321104026532124
90111
新規に作られた
nsenter --target $PID --utsしたときNamespace inode(before) count(before) inode(after) inode(after)
Net 4026531957 : 94 4026531957 96
UTS4026531838 93 4026531838 94
4026532111 1 4026532111 2
IPC 4026531839 94 4026531839 96
PID 4026531836 94 4026531836 96
User 4026531837 94 4026531837 96
Mount 4026531840402653185740265321104026532124
91111
4026531840402653185740265321104026532124
93111
ここに移動する
増えた
struct ns_common● 主なデータ
○ proc_ns_operation構造体○ /proc/<pid>/<namespace>のinode番号
● 使用目的
○ ユーザ空間にexportしたファイルのinodeから名前空間
の構造体にアクセスする■ container_ofマクロを使うとできる
● 各名前空間はns_common構造体のポインタをメンバ変数に持っている
補足: container_ofの仕組み#define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );})
struct foo *p = &x;struct bar *ptr = p->b;struct foo *fp = container_of(ptr, struct bar, b);
struct foo { int n; char s[4]; struct bar *b; int x;};
typeof(((struct bar *)0)->b) __mptr = ptrこれで、struct bar * __mptr = ptrとなる
offsetof(struct bar, b)でstruct fooのメンバ変数bの先頭からのオフセットが取れる
最後に__mptrのアドレスからオフセットを引くと struct fooのインスタンスのアドレス(== pのアドレス)が取れる
↓はなんとなくの例
struct proc_ns_operations
● ユーザ空間から名前空間を操作するために使用する構造体
● 主に使用するもの○ ファイル名
■ /proc/self/ns/netなどのファイル名○ 名前空間を操作する関数への関数ポインタ
proc_ns_operations構造体の関数
● get()○ 参照数を増やす
● put()○ 参照数を減らす
● install()○ 名前空間に参加する
memoLinuxカーネルでget/putという名称は参照数を増やす/減らすという意味で使うことが多いです。
名前空間の初期化
● 名前空間は基本的にコンパイル時に初期化■ すべてのプロセスの先祖にあたるinit_taskの初期化
タイミングでもある■ Mount名前空間は実行時に初期化
● 初期化対象○ init_nsproxy
■ これはuts、ipcなどの名前空間の初期化も含む○ init_cred
■ user名前空間
各名前空間の初期化タイミング
Namespace 初期化タイミング 初期化時の変数
User コンパイル時 init_user_ns
UTS コンパイル時 init_uts_ns
IPC コンパイル時 init_ipc_ns
Net コンパイル時 init_net_ns
PID コンパイル時 init_pid_ns
Mount init_mount_tree() NULL
init_nsproxystruct nsproxy init_nsproxy = { .count = ATOMIC_INIT(1), .uts_ns = &init_uts_ns,#if defined(CONFIG_POSIX_MQUEUE) || defined(CONFIG_SYSVIPC) .ipc_ns = &init_ipc_ns,#endif .mnt_ns = NULL, .pid_ns_for_children = &init_pid_ns,#ifdef CONFIG_NET .net_ns = &init_net,#endif};
Mount Namespaceはコンパイル時に決まらないので最初はNULL
init_cred struct cred init_cred = {
.usage = ATOMIC_INIT(4),~略~
.fsuid = GLOBAL_ROOT_UID, .fsgid = GLOBAL_ROOT_GID,
.securebits = SECUREBITS_DEFAULT, .cap_inheritable = CAP_EMPTY_SET, .cap_permitted = CAP_FULL_SET, .cap_effective = CAP_FULL_SET, .cap_bset = CAP_FULL_SET, .user = INIT_USER, .user_ns = &init_user_ns, .group_info = &init_groups, };
各名前空間の初期値
● 個々の名前空間固有の話なので今回は省略しますm(_ _)m
mnt_nsの初期化
start_kernel() -> vfs_cache_init() -> mnt_init() -> init_mount_tree() -> create_mnt_ns()
rootfsのvfsmount構造体取得
mnt_ns構造体をallocして、mount構造体のmnt_nsに設定
カーネルの初期化関数
clone(2)の処理
● 名前空間に関する部分は2点○ User名前空間の処理
■ User名前空間の参照カウンタを増やすか新規に作
る○ NSProxyの処理
■ NProxyの参照カウンタを増やすか新規に作る■ 各名前空間の処理
● 対象の名前空間の参照カウンタを増やすか新規に作る
clone時のUser Namespaceの処理
● User Namespaceは認証情報のコピー処理の中で実施
do_fork() -> copy_process() -> copy_creds() -> create_user_ns() ->set_cred_user_ns()
prepare_cred()で親プロセスのuser_nsを共有
CLONE_NEWUSERがセットされている場合は、ここで親プロセスのuser_nsと分離
作成中プロセスのstruct credにあるuser_nsをcreate_user_ns()で作ったものに置き換え
clone(2)時のNSProxyの処理
● NSProxyを親プロセスと共有 or 新規作成する● 入り口になるのはcopy_namespaces()● この関数が名前空間の分離や参照数の更新を行う
○ 以下の流れで呼ばれる
-> do_fork() -> copy_process() -> copy_namespaces()
copy_namespaces()の処理
● CLONE_XXXのチェック○ 設定が無けれればNSProxyの参照を増やして終了
● 必要なケーパビリティがあるかチェック● CLONE_NEWIPCが設定されている場合
○ CLONE_SYSVSEMが設定されていたらエラーとする■ http://linuxjm.osdn.jp/html/LDP_man-pages/man2/clone.2.
html● create_new_namespaces()でNSProxyの作成と、各名前空間の処理
をする● 作成中のtask_structのnsproxyをcreate_new_namespaces()で作成し
たものに置き換える
craete_new_namespaces()の処理
● NSProxy構造体を新規に作成○ 作成はcreate_nsproxy()で実施○ 主な処理
■ kmem_cache_alloc()でメモリを確保■ 参照数を1に設定■ 各名前空間を処理
● 各名前空間の関数を呼び出していく○ copy_xxxという名前
copy_xxx()全般共通の処理
● フラグをチェックし、自身が分離の対象になっていなければ既存の名前空間の参照カウンタ数を増やす
● /proc/<pid>/ns以下にexportするファイルのinodeを確保
● 参照カウンタの設定
setns(2)による名前空間の移動
● ファイルディスクリプタからinodeを取得● そこからns_common構造体取得● NSProxy構造体のインスタンスを作成● 移動対象の名前空間が持つinstall()を呼び、その名前
空間固有の処理を実施○ UTS名前空間なら参照数を増やす程度
● 作成したNSProxyをプロセスの既存のNSProxyと交換○ ここで既存のNSProxyの参照数を減らす
unshare(2)による名前空間の分離
● setns(2)の場合と基本的に同じ○ install()は呼ばない
■ unshare(2)は既存の名前空間に移動しないので
nsfs● linux 3.19より追加
○ 3.19からは1つのfile systemとなった■ 以前も同様の処理はしていた
○ take the targets of /proc/*/ns/* symlinks to separate fs■ https://github.
com/torvalds/linux/commit/e149ed2b805fefdccf7ccdfc19eca22fdd4514ac
nsfsの機能
● ns_get_path()○ /proc/<pid>/ns/uts等の名前空間のファイルのdentryを
返す
○ 初回実行時はファイル自体がないのでinode割り当てか
ら実施■ slow path
● proc_ns_fget()○ fdからfile構造体を返す
NSProxy構造体へのアクセス
struct task_struct NSProxy /proc/<pid>/<namespace>
カーネル空間 ユーザ空間
setns(2)の実行
NSProxy構造体へのアクセス
● struct task_structからアクセス○ clone(2)、unshare(2)の場合
■ カーネル空間からのアクセス● /proc/<pid>/ns/のファイルからアクセス
○ setns(2)の場合■ ユーザ空間からのアクセス
カーネル空間でのNSProxyへのアクセス
● お手軽
struct task_struct *p = current;
pr_info("nsproxy -> 0x%p\n", p->nsproxy);
ユーザ空間からNSProxyにアクセス
● /proc/<pid>/<namespace>をopen○ あとはnsfsの出番
■ ファイルに関する操作は何もない● file_operations構造体はllseekに対してno_llseek()を設定
○ -ESPIPEを返すだけの関数
ユーザ空間からNSProxyにアクセス
sys_open-> do_sys_open
-> do_file_open-> path_openat
-> proc_ns_follow_link-> ns_get_path
-> utsns_get
vfs
procs
nsfs
ユーザ空間からNSProxyにアクセス
sys_open-> do_sys_open
-> do_file_open-> path_openat
-> proc_ns_follow_link-> ns_get_path
-> utsns_get
inodeからtask_structとns_operationsを取得
inodeからtask_structの取得
● get_proc_task()を使うことで取得できる○ inodeからstruct proc_inodeの取得
■ この構造体の先頭要素はstruct pid■ pid構造体のtasks変数の要素はtask_struct構造体のpidsメン
バ変数の要素を指す■ 後はoffset_ofでメンバ変数のオフセット位置を引けば
task_struct構造体の先頭アドレスを取得できる○ 細かい挙動は「Linux: inodeからtask_struct構造体を取得」を参照
してください■ http://kernhack.hatenablog.com/entry/2015/06/13/003928
ns_operations構造体の取得
● PROC_I()を使いproc_inodeを取得
○ そこからns_operations構造体にアクセスできる
● PROC_I()は大方の予想に反してマクロではなくて関数
■ task_struct構造体を取得する時にも使ってます
FAQ
● オーバーヘッドはどう?○ dockerやlxcなどではなくて、Namespaceだけ分離した
場合はどうか?
Namespaceのオーバーヘッドを調べてみる
● Mount Namespace
● Net Namespace
Mount名前空間 テスト環境
● Raspberry Pi B++○ Linux saturn 4.1.4-1-ARCH #1 PREEMPT Thu Aug 6 21:07:48 MDT
2015 armv6l GNU/Linux
● SDカードはなんだっけ・・?
[masami@saturn ~]$ zgrep "CONFIG_[A-Z]*_NS" /proc/config.gzCONFIG_UTS_NS=yCONFIG_IPC_NS=yCONFIG_USER_NS=yCONFIG_PID_NS=yCONFIG_NET_NS=y
Mount名前空間のテスト内容
● ツール○ bonnie++
■ bonnie++ -u 1000:1000 -d ${test_dir} -n 256:0:0:1 ● ファイル数256、最大・最小ファイルサイズ0、ディレクトリ数1
■ 上記を5回1セットにして5セット実施● unshareした場合としない場合の2パターン
● ファイルシステム○ ext4
● 結果はRandom CreateのDelete処理の部分をグラフ化○ delete(unlink)はmount namespaceを見ているので
unlinkの場合の名前空間へのアクセス
sys_unlink() ソースコード上は SYSCALL_DEFINE1(unlink, ~
-> do_unlinkat() -> vfs_unlink() -> is_local_mountpoint() -> __is_local_mountpoint()
削除対象のdentryを取得
dentryが現在のmount namespaceのものか調べる
↓のようにリストを辿るので名前空間がたくさんあればその分のオーバーヘッドはあるlist_for_each_entry(mnt, &ns->list, mnt_list) {
is_covered = (mnt->mnt_mountpoint == dentry);if (is_covered)
break;}
Random Deleteの結果中央値だとunshared : 404not unshared : 411
Net名前空間 テスト環境
● Arch Linux on KVM○ Linux nstest 4.1.6-1-ARCH #1 SMP PREEMPT Mon Aug 17 08:52:28
CEST 2015 x86_64 GNU/Linux
[root@nstest masami]# zgrep "CONFIG_[A-Z]*_NS" /proc/config.gzCONFIG_UTS_NS=yCONFIG_IPC_NS=y# CONFIG_USER_NS is not setCONFIG_PID_NS=yCONFIG_NET_NS=y
Net名前空間 テスト環境
iperf対抗機192.168.11.7/24
KVMホストenp9s0: 192.168.11.6/24virbr0: 192.168.122.1/24
KVMゲスト&コンテナホスト名前空間:設定なし(init_net_ns)br0: 192.168.122.33/24
host-veth: アドレスなし
コンテナゲスト名前空間:testnsguest-veth: 192.168.122.150/24
kvmゲスト環境のネットワーク[root@nstest masami]# ip a3: br0@NONE: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 86:dc:5e:95:d6:b3 brd ff:ff:ff:ff:ff:ff inet 192.168.122.33/24 brd 192.168.122.255 scope global dynamic br0 valid_lft 3067sec preferred_lft 3067sec inet6 fe80::84dc:5eff:fe95:d6b3/64 scope link valid_lft forever preferred_lft forever5: host-veth@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel master br0 state UP group default qlen 1000 link/ether 7e:37:ac:ec:1c:25 brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet6 fe80::7c37:acff:feec:1c25/64 scope link valid_lft forever preferred_lft forever
[root@nstest masami]# ip a4: guest-veth@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 link/ether 82:27:59:38:88:7a brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 192.168.122.150/24 scope global guest-veth valid_lft forever preferred_lft forever inet6 fe80::8027:59ff:fe38:887a/64 scope link valid_lft forever preferred_lft forever
コンテナのホスト側lo、ethは省略
コンテナのゲスト側
Net名前空間テスト内容
● iperf3の対抗機でサーバとして起動○ iperf3 -s
● kvmゲストでは○ netns(名前はtestns)を作って、そこから対抗機とiperf3
でテスト○ netnsを作らずに対抗機iperf3でテスト
socket(2)での名前空間へのアクセス
sys_socket() -> sock_create()
return __sock_create(current->nsproxy->net_ns, family, type, protocol, res, 0);
これと言った処理はなくて、普通に net名前空間にアクセス。Mount名前空間と違い、ピンポイントでデータにアクセスできるのでカーネルとしてはオーバーヘッドは無いはず
iperf3 テスト結果
中央値netnsあり(sender) : 942Mbits/secnetnsなし(sender) : 943Mbits/secnetnsあり(receiver) : 939Mbits/secnetnsなし(receiver) : 940Mbits/sec
References● Professional Linux Kernel Architecture
○ http://www.amazon.co.jp/dp/0470343435● コードリーディングのめもはblogに書いてます
○ http://kernhack.hatenablog.com/● man 7 namespaces
○ http://linuxjm.osdn.jp/html/LDP_man-pages/man7/namespaces.7.html○ 関連するmanページもここから
● Linuxファイルシステムベンチマーク第1回○ http://hesonogoma.com/linux/FileSystemBenchmarkResults-01.html
まとめ
● 名前空間はカーネルのリソースを管理● 名前空間の操作
○ 親プロセスと共有○ 親プロセスから分離○ 他の名前空間に所属
● 名前空間分離時の挙動○ 元の名前空間のコピーを作る○ データが全く無い空の状態
● オーバーヘッドはものによりけり○ ポインタの差し替えで済むもの○ リストを辿るもの