linux namespaces

86
Linux Namespaces 8コンテナ型仮想化の情報交換会@東京 @masami256

Upload: masami-ichikawa

Post on 24-Jan-2017

6.466 views

Category:

Technology


0 download

TRANSCRIPT

Page 1: Linux Namespaces

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

@masami256

Page 2: Linux Namespaces

目次

● Readme● Namespaces?● System calls● Kernel Implementation● FAQ

Page 3: Linux Namespaces

@masami256● Linuxカーネルのメモリーリークを直したり

○ ackされたりされなかったり● Linuxカーネルもくもく会を開催したり

○ at 秋葉原● ラズパイ向けのdockerイメージ作ってたり

○ Arch Linuxのラズパイ● Arch LinuxのAURにPKGBUILDを公開したり● Fedora ProjectでQAをやったり

[email protected]● きらら、きららミラク、きららキャラット、きららMAXは欠かさず購入

○ ゆゆ式は哲学(`・ω・´)キリッ● 大家さんは思春期!も良いですね(*´ω`*)

○ まんがタイム・まんがタイムファミリーで連載中○ アニメ化企画進行中 (ヽ=´▽`=)ノ

Page 4: Linux Namespaces

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

Page 5: Linux Namespaces

Linuxのコンテナで使われる技術

● プロセス・リソース管理○ Namespaces○ cgroup

● ストレージバックエンド○ btrfs○ overlayfs○ aufs

Page 6: Linux Namespaces

Namespaces?

● Linuxの名前空間については@TenForwardさんの資料を読むのが確実です○ 今さら聞けない Linux コンテナの基礎

■ https://speakerdeck.com/tenforward/jin-sarawen-kenai-linux-kontenafalseji-chu-2015-06-20

Page 7: Linux Namespaces

Linux 4.1でサポートしている名前空間

名前空間 概要

IPC System Vのプロセス間通信、POSIXメッセージキュー

Net ネットワークデバイス、 IPv4・IPv6プロトコルスタック、ルーティングテーブル等々

Mount マウントポイント

PID PID

User UID、GID

UTS ホスト名

Page 8: Linux Namespaces

名前空間の機能概要

● リソースを管理する○ メモリ、cpuなどのリソースとは別○ IPC、ネットワーク、ホスト名など管理する仕組み

■ この仕組みをごそっと入れ替えることで名前空間の

分離が実現できる

Page 9: Linux Namespaces

名前空間の機能概要(Cont’d)

● 名前空間を分離したときの挙動○ 元の名前空間のコピーを作る

■ e.g. Mount名前空間○ データがなく完全に新規の状態

■ e.g. Net名前空間

Page 10: Linux Namespaces

主な登場人物

● 名前空間○ UTS、Net名前空間等

● NSProxy○ カーネルで各名前空間を管理している構造体

● 参照カウンタ○ 名前空間(個々の名前空間)の参照カウンタ○ NSProxyの参照カウンタ

Page 11: Linux Namespaces

名前空間の概要: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

Page 12: Linux Namespaces

プロセスと名前空間

● プロセスの親子関係と名前空間の関係は別○ 基本は親プロセスと同じ名前空間に所属

■ デフォルト値をどうするかというところの話○ 親プロセスの名前空間からの独立は何時でも可能

■ ただし、例外あり● 後述します

Page 13: Linux Namespaces

名前空間のユーザ空間への見せ方

● 名前空間はファイルとして見える○ /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]

Page 14: Linux Namespaces

● 見ての通りシンボリックリンク

● 4026531839は名前空間のinode番号○ このファイル自体はユーザ空間からは見えない

● setns(2)ではこのファイルのfdを使用する

ユーザ空間からの見え方

lrwxrwxrwx. 1 masami masami 0 Jul 15 23:39 ipc -> ipc:[4026531839]

Page 15: Linux Namespaces

システムコール - 名前空間の操作

● 名前空間に対してできる操作○ 親プロセスの名前空間を共有○ 親プロセスの名前空間から分離○ 別のプロセスの名前空間へ移動

Page 16: Linux Namespaces

名前空間に関連するシステムコール

● 3つあります○ clone(2)○ setns(2)○ unshare(2)

● setns(2)は純粋に名前空間操作用● その他の2関数は名前空間も操作できる

Page 17: Linux Namespaces

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)で使用

Page 18: Linux Namespaces

clone(2)?

● 子プロセスを作成する○ fork(2)の仲間

■ フラグを色々設定して細かい制御が可能■ スレッドを作る場合にも使う

● カーネル内ではdo_fork()をfork/cloneの共通処理として使用

Page 19: Linux Namespaces

clone(2)と名前空間の操作

● プロセス起動時から新しい名前空間で動かしたい○ clone(2)を使う

● CLONE_NEWXXXフラグで指定しなかった名前空間○ 親プロセスと共有

Page 20: Linux Namespaces

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. */

Page 21: Linux Namespaces

Linuxのプロセス作成(ざっくりと)

● ざっくりと言ってしまうと、fork(2)を実行したプロセスのコピーを作る○ ファイルディスクリプタやメモリ空間などはコピーではなくて同

じデータを共有します■ Copy on Write■ 詳しく知りたいなら以下の本が良いと思います■ なるほどUnixプロセス ― Rubyで学ぶUnixの基礎

● http://tatsu-zine.com/books/naruhounix● このデータのコピー/共有というところで、clone(2)よる共有の設

定、unshare(2)による分離が必要に

Page 22: Linux Namespaces

unshare(2)?

● 他のプロセスと共有しているデータを分離○ 名前空間各種

■ 所属している名前空間から離れ、新規に作成○ ファイルディスクリプタテーブル○ ファイルシステム属性

■ ルートディレクトリ■ umask

Page 23: Linux Namespaces

unshare(2)の制限

● PID名前空間は分離できない○ 当初サポートされていたけど以下のコミットで削除

■ pidns: Don't have unshare(CLONE_NEWPID) imply CLONE_THREAD● https://github.

com/torvalds/linux/commit/6e556ce209b09528dbf1931cbfd5d323e1345926

Page 24: Linux Namespaces

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]

Page 25: Linux Namespaces

setns(2)の制限

● 別のPID名前空間に所属させた場合○ そのプロセス自身のPID名前空間は変わらない

■ 子プロセスから名前空間が切り替わる

Page 26: Linux Namespaces

システムコールと名前空間の操作

親プロセスと名前空間を共有

親プロセスの名前空間から分離

別プロセスの名前空間へ移動

clone(2) ○ ○

unshare(2) ○

setns(2) ○fork(2)/vfork(2)は必ず親プロセスの名前空間を使用

Page 27: Linux Namespaces

名前空間を操作するタイミング

プロセス生成時 プロセス起動後

clone(2) ○

unshare(2) ○

setns(2) ○名前空間によってはどちらを使うかで挙動に差がある

Page 28: Linux Namespaces

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()等で子プロセスを作ると、その子プロセスから移動し

た名前空間に所属する

Page 29: Linux Namespaces

User Namspaceの特殊なところ

● 基本的に各名前空間同士はデータ的に独立○ User Namespaceは例外

■ User名前空間以外は、その構造体にUser Namespaceのポインタを持つ

■ 名前空間を操作する際にケーパビリティがあるか

チェックするために使用

Page 30: Linux Namespaces

プロセスと名前空間 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

が子プロセス

Page 31: Linux Namespaces

プロセスと名前空間 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

Page 32: Linux Namespaces

プロセスと名前空間 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に所属させた場合

Page 33: Linux Namespaces

プロセスと名前空間 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

Page 34: Linux Namespaces

ここまでのまとめ

● PID名前空間はちょっと扱いが特殊● User名前空間は他の名前空間と独立ではない

Page 35: Linux Namespaces

Linux Kernelでの実装

● nsproxy構造体○ 名前空間の一元管理

■ User名前空間は除く

● cred構造体○ User名前空間を管理

● nsfs○ 各名前空間をユーザ空間にエクスポート

■ /proc/<pid>/ns配下のファイル

Page 36: Linux Namespaces

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の参照数

Page 37: Linux Namespaces

nsproxy in task_struct

● 1プロセス/1スレッド(1 task_struct)につき1つ存在○ include/linux/sched.h

struct task_struct {~略~/* namespaces */ struct nsproxy *nsproxy;~略~

Page 38: Linux Namespaces

各名前空間の構造体

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

Page 39: Linux Namespaces

各名前空間が持つ基本的なデータ

● 参照カウンタ○ 名前空間の分離をしない場合は参照数を増やして、プロセス間で共有する

ため■ カウントするのは参照しているNSProxyの数

● struct ns_common構造体○ procfsに関連するデータを持つ

■ inode■ struct proc_ns_operations

● setns(2)が/proc/<pid>/ns以下のファイルを使用するの覚えてま

すよね(・∀・)● struct user_namespace

○ User Namespace以外の名前空間はuser_namespaceのポインタをデータとして持つ

Page 40: Linux Namespaces

プロセスとNSProxyと名前空間の関係

例:PID 2000はUTS Namespaceだけ分離している

pid:1111

pid:2222

pid:2000

NSProxycount: 2

uts namespacecount: 1

pid namespacecount: 2

uts namespacecount: 1

NSProxycount: 1

Page 41: Linux Namespaces

NSProxyの作成/参照数を増やす条件

参照数を増やす NSProxyを新規作成

clone(2) 名前空間を分離しない場合(CLONE_NEWXXXが設定されいない場合)

1つでも名前空間を分離する場合

setns(2) 常に新規作成

unshare(2) 常に新規作成setns(2)とunshare(2)の場合、元のNSProxyの参照数を減らす処理も行う

Page 42: Linux Namespaces

ユーザ空間で名前空間の使用状況確認

● NSProxyは見れない● 個々の名前空間に関しては確認できる

○ 自分で数えれば

● unshare(2)とsetns(2)をした時にどう変わるか見てみます

Page 43: Linux Namespaces

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

新規に作られた

Page 44: Linux Namespaces

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

ここに移動する

増えた

Page 45: Linux Namespaces

struct ns_common● 主なデータ

○ proc_ns_operation構造体○ /proc/<pid>/<namespace>のinode番号

● 使用目的

○ ユーザ空間にexportしたファイルのinodeから名前空間

の構造体にアクセスする■ container_ofマクロを使うとできる

● 各名前空間はns_common構造体のポインタをメンバ変数に持っている

Page 46: Linux Namespaces

補足: 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のアドレス)が取れる

↓はなんとなくの例

Page 47: Linux Namespaces

struct proc_ns_operations

● ユーザ空間から名前空間を操作するために使用する構造体

● 主に使用するもの○ ファイル名

■ /proc/self/ns/netなどのファイル名○ 名前空間を操作する関数への関数ポインタ

Page 48: Linux Namespaces

proc_ns_operations構造体の関数

● get()○ 参照数を増やす

● put()○ 参照数を減らす

● install()○ 名前空間に参加する

memoLinuxカーネルでget/putという名称は参照数を増やす/減らすという意味で使うことが多いです。

Page 49: Linux Namespaces

名前空間の初期化

● 名前空間は基本的にコンパイル時に初期化■ すべてのプロセスの先祖にあたるinit_taskの初期化

タイミングでもある■ Mount名前空間は実行時に初期化

● 初期化対象○ init_nsproxy

■ これはuts、ipcなどの名前空間の初期化も含む○ init_cred

■ user名前空間

Page 50: Linux Namespaces

各名前空間の初期化タイミング

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

Page 51: Linux Namespaces

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

Page 52: Linux Namespaces

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, };

Page 53: Linux Namespaces

各名前空間の初期値

● 個々の名前空間固有の話なので今回は省略しますm(_ _)m

Page 54: Linux Namespaces

mnt_nsの初期化

start_kernel() -> vfs_cache_init() -> mnt_init() -> init_mount_tree() -> create_mnt_ns()

rootfsのvfsmount構造体取得

mnt_ns構造体をallocして、mount構造体のmnt_nsに設定

カーネルの初期化関数

Page 55: Linux Namespaces

clone(2)の処理

● 名前空間に関する部分は2点○ User名前空間の処理

■ User名前空間の参照カウンタを増やすか新規に作

る○ NSProxyの処理

■ NProxyの参照カウンタを増やすか新規に作る■ 各名前空間の処理

● 対象の名前空間の参照カウンタを増やすか新規に作る

Page 56: Linux Namespaces

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()で作ったものに置き換え

Page 57: Linux Namespaces

clone(2)時のNSProxyの処理

● NSProxyを親プロセスと共有 or 新規作成する● 入り口になるのはcopy_namespaces()● この関数が名前空間の分離や参照数の更新を行う

○ 以下の流れで呼ばれる

-> do_fork() -> copy_process() -> copy_namespaces()

Page 58: Linux 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()で作成し

たものに置き換える

Page 59: Linux Namespaces

craete_new_namespaces()の処理

● NSProxy構造体を新規に作成○ 作成はcreate_nsproxy()で実施○ 主な処理

■ kmem_cache_alloc()でメモリを確保■ 参照数を1に設定■ 各名前空間を処理

● 各名前空間の関数を呼び出していく○ copy_xxxという名前

Page 60: Linux Namespaces

copy_xxx()全般共通の処理

● フラグをチェックし、自身が分離の対象になっていなければ既存の名前空間の参照カウンタ数を増やす

● /proc/<pid>/ns以下にexportするファイルのinodeを確保

● 参照カウンタの設定

Page 61: Linux Namespaces

setns(2)による名前空間の移動

● ファイルディスクリプタからinodeを取得● そこからns_common構造体取得● NSProxy構造体のインスタンスを作成● 移動対象の名前空間が持つinstall()を呼び、その名前

空間固有の処理を実施○ UTS名前空間なら参照数を増やす程度

● 作成したNSProxyをプロセスの既存のNSProxyと交換○ ここで既存のNSProxyの参照数を減らす

Page 62: Linux Namespaces

unshare(2)による名前空間の分離

● setns(2)の場合と基本的に同じ○ install()は呼ばない

■ unshare(2)は既存の名前空間に移動しないので

Page 63: Linux Namespaces

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

Page 64: Linux Namespaces

nsfsの機能

● ns_get_path()○ /proc/<pid>/ns/uts等の名前空間のファイルのdentryを

返す

○ 初回実行時はファイル自体がないのでinode割り当てか

ら実施■ slow path

● proc_ns_fget()○ fdからfile構造体を返す

Page 65: Linux Namespaces

NSProxy構造体へのアクセス

struct task_struct NSProxy /proc/<pid>/<namespace>

カーネル空間 ユーザ空間

setns(2)の実行

Page 66: Linux Namespaces

NSProxy構造体へのアクセス

● struct task_structからアクセス○ clone(2)、unshare(2)の場合

■ カーネル空間からのアクセス● /proc/<pid>/ns/のファイルからアクセス

○ setns(2)の場合■ ユーザ空間からのアクセス

Page 67: Linux Namespaces

カーネル空間でのNSProxyへのアクセス

● お手軽

struct task_struct *p = current;

pr_info("nsproxy -> 0x%p\n", p->nsproxy);

Page 68: Linux Namespaces

ユーザ空間からNSProxyにアクセス

● /proc/<pid>/<namespace>をopen○ あとはnsfsの出番

■ ファイルに関する操作は何もない● file_operations構造体はllseekに対してno_llseek()を設定

○ -ESPIPEを返すだけの関数

Page 69: Linux Namespaces

ユーザ空間からNSProxyにアクセス

sys_open-> do_sys_open

-> do_file_open-> path_openat

-> proc_ns_follow_link-> ns_get_path

-> utsns_get

vfs

procs

nsfs

Page 70: Linux Namespaces

ユーザ空間から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を取得

Page 71: Linux Namespaces

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

Page 72: Linux Namespaces

ns_operations構造体の取得

● PROC_I()を使いproc_inodeを取得

○ そこからns_operations構造体にアクセスできる

● PROC_I()は大方の予想に反してマクロではなくて関数

■ task_struct構造体を取得する時にも使ってます

Page 73: Linux Namespaces

FAQ

● オーバーヘッドはどう?○ dockerやlxcなどではなくて、Namespaceだけ分離した

場合はどうか?

Page 74: Linux Namespaces

Namespaceのオーバーヘッドを調べてみる

● Mount Namespace

● Net Namespace

Page 75: Linux Namespaces

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

Page 76: Linux Namespaces

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を見ているので

Page 77: Linux Namespaces

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;}

Page 78: Linux Namespaces

Random Deleteの結果中央値だとunshared : 404not unshared : 411

Page 79: Linux Namespaces

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

Page 80: Linux Namespaces

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

Page 81: Linux Namespaces

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は省略

コンテナのゲスト側

Page 82: Linux Namespaces

Net名前空間テスト内容

● iperf3の対抗機でサーバとして起動○ iperf3 -s

● kvmゲストでは○ netns(名前はtestns)を作って、そこから対抗機とiperf3

でテスト○ netnsを作らずに対抗機iperf3でテスト

Page 83: Linux Namespaces

socket(2)での名前空間へのアクセス

sys_socket() -> sock_create()

return __sock_create(current->nsproxy->net_ns, family, type, protocol, res, 0);

これと言った処理はなくて、普通に net名前空間にアクセス。Mount名前空間と違い、ピンポイントでデータにアクセスできるのでカーネルとしてはオーバーヘッドは無いはず

Page 84: Linux Namespaces

iperf3 テスト結果

中央値netnsあり(sender) : 942Mbits/secnetnsなし(sender) : 943Mbits/secnetnsあり(receiver) : 939Mbits/secnetnsなし(receiver) : 940Mbits/sec

Page 85: Linux Namespaces

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

Page 86: Linux Namespaces

まとめ

● 名前空間はカーネルのリソースを管理● 名前空間の操作

○ 親プロセスと共有○ 親プロセスから分離○ 他の名前空間に所属

● 名前空間分離時の挙動○ 元の名前空間のコピーを作る○ データが全く無い空の状態

● オーバーヘッドはものによりけり○ ポインタの差し替えで済むもの○ リストを辿るもの