Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Linux Namespaces

8,388 views

Published on

第8回 コンテナ型仮想化の情報交換会@東京
Linuxの名前空間に関する発表

Published in: Technology
  • Be the first to comment

Linux Namespaces

  1. 1. Linux Namespaces 第8回 コンテナ型仮想化の情報交換会@東京 @masami256
  2. 2. 目次 ● Readme ● Namespaces? ● System calls ● Kernel Implementation ● FAQ
  3. 3. @masami256 ● Linuxカーネルのメモリーリークを直したり ○ ackされたりされなかったり ● Linuxカーネルもくもく会を開催したり ○ at 秋葉原 ● ラズパイ向けのdockerイメージ作ってたり ○ Arch Linuxのラズパイ ● Arch LinuxのAURにPKGBUILDを公開したり ● Fedora ProjectでQAをやったり ○ masami@fedoraproject.org ● きらら、きららミラク、きららキャラット、きららMAXは欠かさず購入 ○ ゆゆ式は哲学(`・ω・´)キリッ ● 大家さんは思春期!も良いですね(*´ω`*) ○ まんがタイム・まんがタイムファミリーで連載中 ○ アニメ化企画進行中ヽ(=´▽`=)ノ
  4. 4. 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
  5. 5. Linuxのコンテナで使われる技術 ● プロセス・リソース管理 ○ Namespaces ○ cgroup ● ストレージバックエンド ○ btrfs ○ overlayfs ○ aufs
  6. 6. Namespaces? ● Linuxの名前空間については@TenForwardさ んの資料を読むのが確実です ○ 今さら聞けない Linux コンテナの基礎 ■ https://speakerdeck.com/tenforward/jin- sarawen-kenai-linux-kontenafalseji- chu-2015-06-20
  7. 7. Linux 4.1でサポートしている名前空間 名前空間 概要 IPC System Vのプロセス間通信、POSIXメッセー ジキュー Net ネットワークデバイス、 IPv4・IPv6プロトコルス タック、ルーティングテーブル等々 Mount マウントポイント PID PID User UID、GID UTS ホスト名
  8. 8. 名前空間の機能概要 ● リソースを管理する ○ メモリ、cpuなどのリソースとは別 ○ IPC、ネットワーク、ホスト名など管理する仕組み ■ この仕組みをごそっと入れ替えることで名前空間の 分離が実現できる
  9. 9. 名前空間の機能概要(Cont’d) ● 名前空間を分離したときの挙動 ○ 元の名前空間のコピーを作る ■ e.g. Mount名前空間 ○ データがなく完全に新規の状態 ■ e.g. Net名前空間
  10. 10. 主な登場人物 ● 名前空間 ○ UTS、Net名前空間等 ● NSProxy ○ カーネルで各名前空間を管理している構造体 ● 参照カウンタ ○ 名前空間(個々の名前空間)の参照カウンタ ○ NSProxyの参照カウンタ
  11. 11. 名前空間の概要:UTS名前空間での例 pid:1001 ppid:1000 NSProxy uts ns pid:1002 ppid:1000 nodename:foo pid:1003 ppid:1000 NSProxy uts ns nodename: bar pid:1000 ppid:900 pid1001と1002がuname -nするとfooが返る pid1003がuname -nするとbarが返る pid1000の名前空間は この図では省略 名前空間 名前空間 struct new_utsname struct new_utsname
  12. 12. プロセスと名前空間 ● プロセスの親子関係と名前空間の関係は別 ○ 基本は親プロセスと同じ名前空間に所属 ■ デフォルト値をどうするかというところの話 ○ 親プロセスの名前空間からの独立は何時でも可能 ■ ただし、例外あり ● 後述します
  13. 13. 名前空間のユーザ空間への見せ方 ● 名前空間はファイルとして見える ○ /proc/<pid>/ns/[namespace name] masami@saga:~$ ls -la /proc/self/ns total 0 dr-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]
  14. 14. ● 見ての通りシンボリックリンク ● 4026531839は名前空間のinode番号 ○ このファイル自体はユーザ空間からは見えない ● setns(2)ではこのファイルのfdを使用する ユーザ空間からの見え方 lrwxrwxrwx. 1 masami masami 0 Jul 15 23:39 ipc -> ipc:[4026531839]
  15. 15. システムコール - 名前空間の操作 ● 名前空間に対してできる操作 ○ 親プロセスの名前空間を共有 ○ 親プロセスの名前空間から分離 ○ 別のプロセスの名前空間へ移動
  16. 16. 名前空間に関連するシステムコール ● 3つあります ○ clone(2) ○ setns(2) ○ unshare(2) ● setns(2)は純粋に名前空間操作用 ● その他の2関数は名前空間も操作できる
  17. 17. 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)で使用
  18. 18. clone(2)? ● 子プロセスを作成する ○ fork(2)の仲間 ■ フラグを色々設定して細かい制御が可能 ■ スレッドを作る場合にも使う ● カーネル内ではdo_fork()をfork/cloneの共通処理とし て使用
  19. 19. clone(2)と名前空間の操作 ● プロセス起動時から新しい名前空間で 動かしたい ○ clone(2)を使う ● CLONE_NEWXXXフラグで指定しな かった名前空間 ○ 親プロセスと共有
  20. 20. 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. */
  21. 21. Linuxのプロセス作成(ざっくりと) ● ざっくりと言ってしまうと、fork(2)を実行したプロセスのコピーを作 る ○ ファイルディスクリプタやメモリ空間などはコピーではなくて同 じデータを共有します ■ Copy on Write ■ 詳しく知りたいなら以下の本が良いと思います ■ なるほどUnixプロセス ― Rubyで学ぶUnixの基礎 ● http://tatsu-zine.com/books/naruhounix ● このデータのコピー/共有というところで、clone(2)よる共有の設 定、unshare(2)による分離が必要に
  22. 22. unshare(2)? ● 他のプロセスと共有しているデータを分離 ○ 名前空間各種 ■ 所属している名前空間から離れ、新規に作成 ○ ファイルディスクリプタテーブル ○ ファイルシステム属性 ■ ルートディレクトリ ■ umask
  23. 23. unshare(2)の制限 ● PID名前空間は分離できない ○ 当初サポートされていたけど以下のコミットで削除 ■ pidns: Don't have unshare(CLONE_NEWPID) imply CLONE_THREAD ● https://github. com/torvalds/linux/commit/6e556ce209b09528dbf 1931cbfd5d323e1345926
  24. 24. setns(2)? ● 所属したい名前空間のfdを使って、その名前空 間に移動する ○ fdは/proc/<pid>/nsにあるファイルのfd masami@saga:~$ ls -la /proc/self/ns total 0 dr-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]
  25. 25. setns(2)の制限 ● 別のPID名前空間に所属させた場合 ○ そのプロセス自身のPID名前空間は変わらない ■ 子プロセスから名前空間が切り替わる
  26. 26. システムコールと名前空間の操作 親プロセスと名前 空間を共有 親プロセスの名 前空間から分離 別プロセスの名 前空間へ移動 clone(2) ○ ○ unshare(2) ○ setns(2) ○ fork(2)/vfork(2)は必ず 親プロセスの名前空間を使用
  27. 27. 名前空間を操作するタイミング プロセス生成時 プロセス起動後 clone(2) ○ unshare(2) ○ setns(2) ○ 名前空間によってはどちらを使う かで挙動に差がある
  28. 28. PID名前空間の仕様 ● clone(2)でプロセス生成時のみ、名前空間変更対象のプロセスは新しいPID名 前空間に所属できる ● unshare(2)によるPID名前空間の分離は非サポート ○ 一時期サポートされていたが「pidns: Don't have unshare (CLONE_NEWPID) imply CLONE_THREAD」で外された ● https://github. com/torvalds/linux/commit/6e556ce209b09528dbf1931cbfd5d 323e1345926 ● setns(2)による名前空間の移動 ○ 移動したプロセスのPID名前空間は変わらない ○ このプロセスがfork()等で子プロセスを作ると、その子プロセスから移動し た名前空間に所属する
  29. 29. User Namspaceの特殊なところ ● 基本的に各名前空間同士はデータ的に独立 ○ User Namespaceは例外 ■ User名前空間以外は、その構造体にUser Namespaceのポインタを持つ ■ 名前空間を操作する際にケーパビリティがあるか チェックするために使用
  30. 30. プロセスと名前空間 fork(2)実行時 pid: 1234 ppid: 784 pid: 1192 ppid: 784 pid: 1326 ppid: 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 が子プロセス
  31. 31. プロセスと名前空間 clone(2)実行時 pid: 1234 ppid: 784 pid: 1192 ppid: 784 pid: 1326 ppid: 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
  32. 32. プロセスと名前空間 setns(2)実行時 pid: 1234 ppid: 784 pid: 1192 ppid: 784 pid: 1326 ppid: 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に所属させた場合
  33. 33. プロセスと名前空間 unshare(2)実行時 pid: 1234 ppid: 784 pid: 1192 ppid: 784 pid: 1326 ppid: 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
  34. 34. ここまでのまとめ ● PID名前空間はちょっと扱いが特殊 ● User名前空間は他の名前空間と独立ではない
  35. 35. Linux Kernelでの実装 ● nsproxy構造体 ○ 名前空間の一元管理 ■ User名前空間は除く ● cred構造体 ○ User名前空間を管理 ● nsfs ○ 各名前空間をユーザ空間にエクスポート ■ /proc/<pid>/ns配下のファイル
  36. 36. 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の参照数
  37. 37. nsproxy in task_struct ● 1プロセス/1スレッド(1 task_struct)につき1つ存 在 ○ include/linux/sched.h struct task_struct { ~略~ /* namespaces */ struct nsproxy *nsproxy; ~略~
  38. 38. 各名前空間の構造体 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
  39. 39. 各名前空間が持つ基本的なデータ ● 参照カウンタ ○ 名前空間の分離をしない場合は参照数を増やして、プロセス間で共有する ため ■ カウントするのは参照しているNSProxyの数 ● struct ns_common構造体 ○ procfsに関連するデータを持つ ■ inode ■ struct proc_ns_operations ● setns(2)が/proc/<pid>/ns以下のファイルを使用するの覚えてま すよね(・∀・) ● struct user_namespace ○ User Namespace以外の名前空間はuser_namespaceのポインタをデー タとして持つ
  40. 40. プロセスとNSProxyと名前空間の関係 例:PID 2000はUTS Namespaceだけ分離している pid:1111 pid:2222 pid:2000 NSProxy count: 2 uts namespace count: 1 pid namespace count: 2 uts namespace count: 1 NSProxy count: 1
  41. 41. NSProxyの作成/参照数を増やす条件 参照数を増やす NSProxyを新規作成 clone(2) 名前空間を分離しない場 合 (CLONE_NEWXXXが設 定されいない場合) 1つでも名前空間を分離す る場合 setns(2) 常に新規作成 unshare(2) 常に新規作成 setns(2)とunshare(2)の場合、元の NSProxyの参照数を減らす処理も行 う
  42. 42. ユーザ空間で名前空間の使用状況確認 ● NSProxyは見れない ● 個々の名前空間に関しては確認できる ○ 自分で数えれば ● unshare(2)とsetns(2)をした時にどう変わるか 見てみます
  43. 43. unshare -u /bin/bashしたとき Namespace inode(before) count(before) inode(after) inode(after) Net 4026531957 : 91 4026531957 93 UTS 4026531838 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 4026531840 4026531857 4026532110 4026532124 88 1 1 1 4026531840 4026531857 4026532110 4026532124 90 1 1 1 新規に作られた
  44. 44. nsenter --target $PID --utsしたとき Namespace inode(before) count(before) inode(after) inode(after) Net 4026531957 : 94 4026531957 96 UTS 4026531838 93 4026531838 94 4026532111 1 4026532111 2 IPC 4026531839 94 4026531839 96 PID 4026531836 94 4026531836 96 User 4026531837 94 4026531837 96 Mount 4026531840 4026531857 4026532110 4026532124 91 1 1 1 4026531840 4026531857 4026532110 4026532124 93 1 1 1 ここに移動する 増えた
  45. 45. struct ns_common ● 主なデータ ○ proc_ns_operation構造体 ○ /proc/<pid>/<namespace>のinode番号 ● 使用目的 ○ ユーザ空間にexportしたファイルのinodeから名前空間 の構造体にアクセスする ■ container_ofマクロを使うとできる ● 各名前空間はns_common構造体のポインタをメンバ変数 に持っている
  46. 46. 補足: 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のアドレス)が取れる ↓はなんとなくの例
  47. 47. struct proc_ns_operations ● ユーザ空間から名前空間を操作するために使 用する構造体 ● 主に使用するもの ○ ファイル名 ■ /proc/self/ns/netなどのファイル名 ○ 名前空間を操作する関数への関数ポインタ
  48. 48. proc_ns_operations構造体の関数 ● get() ○ 参照数を増やす ● put() ○ 参照数を減らす ● install() ○ 名前空間に参加する memo Linuxカーネルでget/putという名称は参照数を増や す/減らすという意味で使うことが多いです。
  49. 49. 名前空間の初期化 ● 名前空間は基本的にコンパイル時に初期化 ■ すべてのプロセスの先祖にあたるinit_taskの初期化 タイミングでもある ■ Mount名前空間は実行時に初期化 ● 初期化対象 ○ init_nsproxy ■ これはuts、ipcなどの名前空間の初期化も含む ○ init_cred ■ user名前空間
  50. 50. 各名前空間の初期化タイミング 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
  51. 51. init_nsproxy struct 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
  52. 52. 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, };
  53. 53. 各名前空間の初期値 ● 個々の名前空間固有の話なので今回は省略し ますm(_ _)m
  54. 54. mnt_nsの初期化 start_kernel() -> vfs_cache_init() -> mnt_init() -> init_mount_tree() -> create_mnt_ns() rootfsのvfsmount構造体取得 mnt_ns構造体をallocして、mount 構造体のmnt_nsに設定 カーネルの初期化関数
  55. 55. clone(2)の処理 ● 名前空間に関する部分は2点 ○ User名前空間の処理 ■ User名前空間の参照カウンタを増やすか新規に作 る ○ NSProxyの処理 ■ NProxyの参照カウンタを増やすか新規に作る ■ 各名前空間の処理 ● 対象の名前空間の参照カウンタを増やすか新規 に作る
  56. 56. 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()で作ったものに置き換え
  57. 57. clone(2)時のNSProxyの処理 ● NSProxyを親プロセスと共有 or 新規作成する ● 入り口になるのはcopy_namespaces() ● この関数が名前空間の分離や参照数の更新を行う ○ 以下の流れで呼ばれる -> do_fork() -> copy_process() -> copy_namespaces()
  58. 58. 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()で作成し たものに置き換える
  59. 59. craete_new_namespaces()の処理 ● NSProxy構造体を新規に作成 ○ 作成はcreate_nsproxy()で実施 ○ 主な処理 ■ kmem_cache_alloc()でメモリを確保 ■ 参照数を1に設定 ■ 各名前空間を処理 ● 各名前空間の関数を呼び出していく ○ copy_xxxという名前
  60. 60. copy_xxx()全般共通の処理 ● フラグをチェックし、自身が分離の対象になって いなければ既存の名前空間の参照カウンタ数 を増やす ● /proc/<pid>/ns以下にexportするファイルの inodeを確保 ● 参照カウンタの設定
  61. 61. setns(2)による名前空間の移動 ● ファイルディスクリプタからinodeを取得 ● そこからns_common構造体取得 ● NSProxy構造体のインスタンスを作成 ● 移動対象の名前空間が持つinstall()を呼び、その名前 空間固有の処理を実施 ○ UTS名前空間なら参照数を増やす程度 ● 作成したNSProxyをプロセスの既存のNSProxyと交換 ○ ここで既存のNSProxyの参照数を減らす
  62. 62. unshare(2)による名前空間の分離 ● setns(2)の場合と基本的に同じ ○ install()は呼ばない ■ unshare(2)は既存の名前空間に移動しないので
  63. 63. 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/e149ed2b805fefdccf7c cdfc19eca22fdd4514ac
  64. 64. nsfsの機能 ● ns_get_path() ○ /proc/<pid>/ns/uts等の名前空間のファイルのdentryを 返す ○ 初回実行時はファイル自体がないのでinode割り当てか ら実施 ■ slow path ● proc_ns_fget() ○ fdからfile構造体を返す
  65. 65. NSProxy構造体へのアクセス struct task_struct NSProxy /proc/<pid>/<namespace> カーネル空間 ユーザ空間 setns(2)の実行
  66. 66. NSProxy構造体へのアクセス ● struct task_structからアクセス ○ clone(2)、unshare(2)の場合 ■ カーネル空間からのアクセス ● /proc/<pid>/ns/のファイルからアクセス ○ setns(2)の場合 ■ ユーザ空間からのアクセス
  67. 67. カーネル空間でのNSProxyへのアクセス ● お手軽 struct task_struct *p = current; pr_info("nsproxy -> 0x%pn", p->nsproxy);
  68. 68. ユーザ空間からNSProxyにアクセス ● /proc/<pid>/<namespace>をopen ○ あとはnsfsの出番 ■ ファイルに関する操作は何もない ● file_operations構造体はllseekに対してno_llseek()を設定 ○ -ESPIPEを返すだけの関数
  69. 69. ユーザ空間からNSProxyにアクセス sys_open -> do_sys_open -> do_file_open -> path_openat -> proc_ns_follow_link -> ns_get_path -> utsns_get vfs procs nsfs
  70. 70. ユーザ空間から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を取得
  71. 71. 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
  72. 72. ns_operations構造体の取得 ● PROC_I()を使いproc_inodeを取得 ○ そこからns_operations構造体にアクセスできる ● PROC_I()は大方の予想に反してマクロではなくて関数 ■ task_struct構造体を取得する時にも使ってます
  73. 73. FAQ ● オーバーヘッドはどう? ○ dockerやlxcなどではなくて、Namespaceだけ分離した 場合はどうか?
  74. 74. Namespaceのオーバーヘッドを調べてみる ● Mount Namespace ● Net Namespace
  75. 75. 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.gz CONFIG_UTS_NS=y CONFIG_IPC_NS=y CONFIG_USER_NS=y CONFIG_PID_NS=y CONFIG_NET_NS=y
  76. 76. 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を見ているので
  77. 77. 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; }
  78. 78. Random Deleteの結果 中央値だと unshared : 404 not unshared : 411
  79. 79. 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.gz CONFIG_UTS_NS=y CONFIG_IPC_NS=y # CONFIG_USER_NS is not set CONFIG_PID_NS=y CONFIG_NET_NS=y
  80. 80. Net名前空間 テスト環境 iperf対抗機 192.168.11.7/24 KVMホスト enp9s0: 192.168.11.6/24 virbr0: 192.168.122.1/24 KVMゲスト&コンテナホスト 名前空間:設定なし(init_net_ns) br0: 192.168.122.33/24 host-veth: アドレスなし コンテナゲスト 名前空間:testns guest-veth: 192.168.122.150/24
  81. 81. kvmゲスト環境のネットワーク [root@nstest masami]# ip a 3: 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 forever 5: 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 a 4: 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は省略 コンテナのゲスト側
  82. 82. Net名前空間テスト内容 ● iperf3の対抗機でサーバとして起動 ○ iperf3 -s ● kvmゲストでは ○ netns(名前はtestns)を作って、そこから対抗機とiperf3 でテスト ○ netnsを作らずに対抗機iperf3でテスト
  83. 83. socket(2)での名前空間へのアクセス sys_socket() -> sock_create() return __sock_create(current->nsproxy->net_ns, family, type, protocol, res, 0); これと言った処理はなくて、普通に net名前空間にアクセス。 Mount名前空間と違い、ピンポイントでデータにアクセスできるので カーネルとしてはオーバーヘッドは無いはず
  84. 84. iperf3 テスト結果 中央値 netnsあり(sender) : 942Mbits/sec netnsなし(sender) : 943Mbits/sec netnsあり(receiver) : 939Mbits/sec netnsなし(receiver) : 940Mbits/sec
  85. 85. 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
  86. 86. まとめ ● 名前空間はカーネルのリソースを管理 ● 名前空間の操作 ○ 親プロセスと共有 ○ 親プロセスから分離 ○ 他の名前空間に所属 ● 名前空間分離時の挙動 ○ 元の名前空間のコピーを作る ○ データが全く無い空の状態 ● オーバーヘッドはものによりけり ○ ポインタの差し替えで済むもの ○ リストを辿るもの

×