Pro Yearly is on sale from $80 to $50! »

Linux Namespaces

Linux Namespaces

457c3c757b4fae74c7cdc79ad67a5645?s=128

Masami Ichikawa

September 26, 2015
Tweet

Transcript

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

  2. 目次 • Readme • Namespaces? • System calls • Kernel

    Implementation • FAQ
  3. @masami256 • Linuxカーネルのメモリーリークを直したり ◦ ackされたりされなかったり • Linuxカーネルもくもく会を開催したり ◦ at 秋葉原

    • ラズパイ向けのdockerイメージ作ってたり ◦ Arch Linuxのラズパイ • Arch LinuxのAURにPKGBUILDを公開したり • Fedora ProjectでQAをやったり ◦ masami@fedoraproject.org • きらら、きららミラク、きららキャラット、きらら MAXは欠かさず購入 ◦ ゆゆ式は哲学(`・ω・´)キリッ • 大家さんは思春期!も良いですね(*´ω`*) ◦ まんがタイム・まんがタイムファミリーで連載中 ◦ アニメ化企画進行中ヽ(=´▽`=)ノ
  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. Linuxのコンテナで使われる技術 • プロセス・リソース管理 ◦ Namespaces ◦ cgroup • ストレージバックエンド ◦

    btrfs ◦ overlayfs ◦ aufs
  6. Namespaces? • Linuxの名前空間については@TenForwardさ んの資料を読むのが確実です ◦ 今さら聞けない Linux コンテナの基礎 ▪ https://speakerdeck.com/tenforward/jin-

    sarawen-kenai-linux-kontenafalseji- chu-2015-06-20
  7. Linux 4.1でサポートしている名前空間 名前空間 概要 IPC System Vのプロセス間通信、POSIXメッセー ジキュー Net ネットワークデバイス、

    IPv4・IPv6プロトコルス タック、ルーティングテーブル等々 Mount マウントポイント PID PID User UID、GID UTS ホスト名
  8. 名前空間の機能概要 • リソースを管理する ◦ メモリ、cpuなどのリソースとは別 ◦ IPC、ネットワーク、ホスト名など管理する仕組み ▪ この仕組みをごそっと入れ替えることで名前空間の 分離が実現できる

  9. 名前空間の機能概要(Cont’d) • 名前空間を分離したときの挙動 ◦ 元の名前空間のコピーを作る ▪ e.g. Mount名前空間 ◦ データがなく完全に新規の状態

    ▪ e.g. Net名前空間
  10. 主な登場人物 • 名前空間 ◦ UTS、Net名前空間等 • NSProxy ◦ カーネルで各名前空間を管理している構造体 •

    参照カウンタ ◦ 名前空間(個々の名前空間)の参照カウンタ ◦ NSProxyの参照カウンタ
  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. プロセスと名前空間 • プロセスの親子関係と名前空間の関係は別 ◦ 基本は親プロセスと同じ名前空間に所属 ▪ デフォルト値をどうするかというところの話 ◦ 親プロセスの名前空間からの独立は何時でも可能 ▪

    ただし、例外あり • 後述します
  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. • 見ての通りシンボリックリンク • 4026531839は名前空間のinode番号 ◦ このファイル自体はユーザ空間からは見えない • setns(2)ではこのファイルのfdを使用する ユーザ空間からの見え方 lrwxrwxrwx.

    1 masami masami 0 Jul 15 23:39 ipc -> ipc:[4026531839]
  15. システムコール - 名前空間の操作 • 名前空間に対してできる操作 ◦ 親プロセスの名前空間を共有 ◦ 親プロセスの名前空間から分離 ◦

    別のプロセスの名前空間へ移動
  16. 名前空間に関連するシステムコール • 3つあります ◦ clone(2) ◦ setns(2) ◦ unshare(2) •

    setns(2)は純粋に名前空間操作用 • その他の2関数は名前空間も操作できる
  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. clone(2)? • 子プロセスを作成する ◦ fork(2)の仲間 ▪ フラグを色々設定して細かい制御が可能 ▪ スレッドを作る場合にも使う •

    カーネル内ではdo_fork()をfork/cloneの共通処理とし て使用
  19. clone(2)と名前空間の操作 • プロセス起動時から新しい名前空間で 動かしたい ◦ clone(2)を使う • CLONE_NEWXXXフラグで指定しな かった名前空間 ◦

    親プロセスと共有
  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. Linuxのプロセス作成(ざっくりと) • ざっくりと言ってしまうと、fork(2)を実行したプロセスのコピーを作 る ◦ ファイルディスクリプタやメモリ空間などはコピーではなくて同 じデータを共有します ▪ Copy on

    Write ▪ 詳しく知りたいなら以下の本が良いと思います ▪ なるほどUnixプロセス ― Rubyで学ぶUnixの基礎 • http://tatsu-zine.com/books/naruhounix • このデータのコピー/共有というところで、clone(2)よる共有の設 定、unshare(2)による分離が必要に
  22. unshare(2)? • 他のプロセスと共有しているデータを分離 ◦ 名前空間各種 ▪ 所属している名前空間から離れ、新規に作成 ◦ ファイルディスクリプタテーブル ◦

    ファイルシステム属性 ▪ ルートディレクトリ ▪ umask
  23. unshare(2)の制限 • PID名前空間は分離できない ◦ 当初サポートされていたけど以下のコミットで削除 ▪ pidns: Don't have unshare(CLONE_NEWPID)

    imply CLONE_THREAD • https://github. com/torvalds/linux/commit/6e556ce209b09528dbf 1931cbfd5d323e1345926
  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. setns(2)の制限 • 別のPID名前空間に所属させた場合 ◦ そのプロセス自身のPID名前空間は変わらない ▪ 子プロセスから名前空間が切り替わる

  26. システムコールと名前空間の操作 親プロセスと名前 空間を共有 親プロセスの名 前空間から分離 別プロセスの名 前空間へ移動 clone(2) ◦ ◦

    unshare(2) ◦ setns(2) ◦ fork(2)/vfork(2)は必ず 親プロセスの名前空間を使用
  27. 名前空間を操作するタイミング プロセス生成時 プロセス起動後 clone(2) ◦ unshare(2) ◦ setns(2) ◦ 名前空間によってはどちらを使う

    かで挙動に差がある
  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. User Namspaceの特殊なところ • 基本的に各名前空間同士はデータ的に独立 ◦ User Namespaceは例外 ▪ User名前空間以外は、その構造体にUser Namespaceのポインタを持つ

    ▪ 名前空間を操作する際にケーパビリティがあるか チェックするために使用
  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. プロセスと名前空間 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. プロセスと名前空間 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. プロセスと名前空間 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. ここまでのまとめ • PID名前空間はちょっと扱いが特殊 • User名前空間は他の名前空間と独立ではない

  35. Linux Kernelでの実装 • nsproxy構造体 ◦ 名前空間の一元管理 ▪ User名前空間は除く • cred構造体

    ◦ User名前空間を管理 • nsfs ◦ 各名前空間をユーザ空間にエクスポート ▪ /proc/<pid>/ns配下のファイル
  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. nsproxy in task_struct • 1プロセス/1スレッド(1 task_struct)につき1つ存 在 ◦ include/linux/sched.h struct

    task_struct { ~略~ /* namespaces */ struct nsproxy *nsproxy; ~略~
  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. 各名前空間が持つ基本的なデータ • 参照カウンタ ◦ 名前空間の分離をしない場合は参照数を増やして、プロセス間で共有する ため ▪ カウントするのは参照しているNSProxyの数 • struct

    ns_common構造体 ◦ procfsに関連するデータを持つ ▪ inode ▪ struct proc_ns_operations • setns(2)が/proc/<pid>/ns以下のファイルを使用するの覚えてま すよね(・∀・) • struct user_namespace ◦ User Namespace以外の名前空間はuser_namespaceのポインタをデー タとして持つ
  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. NSProxyの作成/参照数を増やす条件 参照数を増やす NSProxyを新規作成 clone(2) 名前空間を分離しない場 合 (CLONE_NEWXXXが設 定されいない場合) 1つでも名前空間を分離す る場合

    setns(2) 常に新規作成 unshare(2) 常に新規作成 setns(2)とunshare(2)の場合、元の NSProxyの参照数を減らす処理も行 う
  42. ユーザ空間で名前空間の使用状況確認 • NSProxyは見れない • 個々の名前空間に関しては確認できる ◦ 自分で数えれば • unshare(2)とsetns(2)をした時にどう変わるか 見てみます

  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. 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. struct ns_common • 主なデータ ◦ proc_ns_operation構造体 ◦ /proc/<pid>/<namespace>のinode番号 • 使用目的

    ◦ ユーザ空間にexportしたファイルのinodeから名前空間 の構造体にアクセスする ▪ container_ofマクロを使うとできる • 各名前空間はns_common構造体のポインタをメンバ変数 に持っている
  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. struct proc_ns_operations • ユーザ空間から名前空間を操作するために使 用する構造体 • 主に使用するもの ◦ ファイル名 ▪

    /proc/self/ns/netなどのファイル名 ◦ 名前空間を操作する関数への関数ポインタ
  48. proc_ns_operations構造体の関数 • get() ◦ 参照数を増やす • put() ◦ 参照数を減らす •

    install() ◦ 名前空間に参加する memo Linuxカーネルでget/putという名称は参照数を増や す/減らすという意味で使うことが多いです。
  49. 名前空間の初期化 • 名前空間は基本的にコンパイル時に初期化 ▪ すべてのプロセスの先祖にあたるinit_taskの初期化 タイミングでもある ▪ Mount名前空間は実行時に初期化 • 初期化対象

    ◦ init_nsproxy ▪ これはuts、ipcなどの名前空間の初期化も含む ◦ init_cred ▪ user名前空間
  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. 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. 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. 各名前空間の初期値 • 個々の名前空間固有の話なので今回は省略し ますm(_ _)m

  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. clone(2)の処理 • 名前空間に関する部分は2点 ◦ User名前空間の処理 ▪ User名前空間の参照カウンタを増やすか新規に作 る ◦ NSProxyの処理

    ▪ NProxyの参照カウンタを増やすか新規に作る ▪ 各名前空間の処理 • 対象の名前空間の参照カウンタを増やすか新規 に作る
  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. clone(2)時のNSProxyの処理 • NSProxyを親プロセスと共有 or 新規作成する • 入り口になるのはcopy_namespaces() • この関数が名前空間の分離や参照数の更新を行う ◦

    以下の流れで呼ばれる -> do_fork() -> copy_process() -> copy_namespaces()
  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. craete_new_namespaces()の処理 • NSProxy構造体を新規に作成 ◦ 作成はcreate_nsproxy()で実施 ◦ 主な処理 ▪ kmem_cache_alloc()でメモリを確保 ▪

    参照数を1に設定 ▪ 各名前空間を処理 • 各名前空間の関数を呼び出していく ◦ copy_xxxという名前
  60. copy_xxx()全般共通の処理 • フラグをチェックし、自身が分離の対象になって いなければ既存の名前空間の参照カウンタ数 を増やす • /proc/<pid>/ns以下にexportするファイルの inodeを確保 • 参照カウンタの設定

  61. setns(2)による名前空間の移動 • ファイルディスクリプタからinodeを取得 • そこからns_common構造体取得 • NSProxy構造体のインスタンスを作成 • 移動対象の名前空間が持つinstall()を呼び、その名前 空間固有の処理を実施

    ◦ UTS名前空間なら参照数を増やす程度 • 作成したNSProxyをプロセスの既存のNSProxyと交換 ◦ ここで既存のNSProxyの参照数を減らす
  62. unshare(2)による名前空間の分離 • setns(2)の場合と基本的に同じ ◦ install()は呼ばない ▪ unshare(2)は既存の名前空間に移動しないので

  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. nsfsの機能 • ns_get_path() ◦ /proc/<pid>/ns/uts等の名前空間のファイルのdentryを 返す ◦ 初回実行時はファイル自体がないのでinode割り当てか ら実施 ▪

    slow path • proc_ns_fget() ◦ fdからfile構造体を返す
  65. NSProxy構造体へのアクセス struct task_struct NSProxy /proc/<pid>/<namespace> カーネル空間 ユーザ空間 setns(2)の実行

  66. NSProxy構造体へのアクセス • struct task_structからアクセス ◦ clone(2)、unshare(2)の場合 ▪ カーネル空間からのアクセス • /proc/<pid>/ns/のファイルからアクセス

    ◦ setns(2)の場合 ▪ ユーザ空間からのアクセス
  67. カーネル空間でのNSProxyへのアクセス • お手軽 struct task_struct *p = current; pr_info("nsproxy ->

    0x%p\n", p->nsproxy);
  68. ユーザ空間からNSProxyにアクセス • /proc/<pid>/<namespace>をopen ◦ あとはnsfsの出番 ▪ ファイルに関する操作は何もない • file_operations構造体はllseekに対してno_llseek()を設定 ◦

    -ESPIPEを返すだけの関数
  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. ユーザ空間から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. 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. ns_operations構造体の取得 • PROC_I()を使いproc_inodeを取得 ◦ そこからns_operations構造体にアクセスできる • PROC_I()は大方の予想に反してマクロではなくて関数 ▪ task_struct構造体を取得する時にも使ってます

  73. FAQ • オーバーヘッドはどう? ◦ dockerやlxcなどではなくて、Namespaceだけ分離した 場合はどうか?

  74. Namespaceのオーバーヘッドを調べてみる • Mount Namespace • Net Namespace

  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. 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. 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. Random Deleteの結果 中央値だと unshared : 404 not unshared : 411

  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. 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. 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. Net名前空間テスト内容 • iperf3の対抗機でサーバとして起動 ◦ iperf3 -s • kvmゲストでは ◦ netns(名前はtestns)を作って、そこから対抗機とiperf3

    でテスト ◦ netnsを作らずに対抗機iperf3でテスト
  83. socket(2)での名前空間へのアクセス sys_socket() -> sock_create() return __sock_create(current->nsproxy->net_ns, family, type, protocol, res,

    0); これと言った処理はなくて、普通に net名前空間にアクセス。 Mount名前空間と違い、ピンポイントでデータにアクセスできるので カーネルとしてはオーバーヘッドは無いはず
  84. iperf3 テスト結果 中央値 netnsあり(sender) : 942Mbits/sec netnsなし(sender) : 943Mbits/sec netnsあり(receiver)

    : 939Mbits/sec netnsなし(receiver) : 940Mbits/sec
  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. まとめ • 名前空間はカーネルのリソースを管理 • 名前空間の操作 ◦ 親プロセスと共有 ◦ 親プロセスから分離 ◦

    他の名前空間に所属 • 名前空間分離時の挙動 ◦ 元の名前空間のコピーを作る ◦ データが全く無い空の状態 • オーバーヘッドはものによりけり ◦ ポインタの差し替えで済むもの ◦ リストを辿るもの