Upgrade to Pro — share decks privately, control downloads, hide ads and more …

コンテナ起動への道

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.
Avatar for xorphitus xorphitus
August 16, 2016

 コンテナ起動への道

コンテナの要素技術を使ってみました。

Avatar for xorphitus

xorphitus

August 16, 2016
Tweet

More Decks by xorphitus

Other Decks in Programming

Transcript

  1. どんなシステムコールで操作するの? • clone(2) ◦ Namespaceの設定をした新しいプロセスを生成する • unshare(2) ◦ 実行中プロセスのNamespaceを制御する ◦

    unshare(1) もあるからシェルスクリプトからも色々できるよ • setns(2) ◦ プロセスを既存のNamespaceに紐付ける
  2. clone(2)によるPIDの隔離 Cでの実装 #define _GNU_SOURCE #include <sched.h> #include <stdio.h> #include <stdlib.h>

    #include <sys/wait.h> #include <unistd.h> #define STACK_SIZE 1024*1024 static char child_stack[STACK_SIZE]; static int child_fn() { printf("child - self pid: %ld\n", (long) getpid()); printf("child - parent pid: %ld\n", (long) getppid()); exit(0); } int main(int argc, char *argv[]) { int flags = SIGCHLD; int opt; while ((opt = getopt(argc, argv, "p")) != -1) { if (opt) { flags |= CLONE_NEWPID; } else { exit(1); } } pid_t child_pid = clone(child_fn, child_stack + STACK_SIZE, flags, NULL); printf("current - clone pid: %ld\n", (long) child_pid); printf("current - self pid: %ld\n", (long) getpid()); waitpid(child_pid, NULL, 0); exit(0); } -p option付与で PID隔離フラグ 噂のclone(2)
  3. clone(2)によるPIDの隔離 実行してみる $ gcc さっきの.c $ sudo ./a.out current -

    clone pid: 15573 current - self pid: 15572 child - self pid: 15573 child - parent pid: 15572 $ sudo ./a.out -p current - clone pid: 15635 current - self pid: 15634 child - self pid: 1 child - parent pid: 0 CLONE_NEWPIDフラ グを 渡さなければ PIDは隔離されない フラグを渡して PIDの隔離に成功 PID 1 は本来(?)initだよ! $ pgrep -fa init 1 /sbin/init 重複が許されている
  4. Namespaceの情報ってどこにあるの? /proc/[pid]/ns にある 先ほどのプログラムを少し変更して、上記のパスを見てみる static int child_fn() { printf("child -

    self pid: %ld\n", (long) getpid()); printf("child - parent pid: %ld\n", (long) getppid()); // この sleep を追記 (せっかくなのでC99スタイル) // ここで止まっている間に /proc/[pid]/ns を ls する作戦 (プログラム中で ls 呼べばいいとか言わないこと) sleep(100); exit(0); }
  5. Namespace見てみますよ / PID隔離なしver $ sudo ./a.out current - clone pid:

    20026 current - self pid: 20025 child - self pid: 20026 child - parent pid: 20026 $ sudo ls -l /proc/20025/ns 合計 0 lrwxrwxrwx 1 root root 0 7月 19 02:58 cgroup -> 'cgroup:[4026531835]' lrwxrwxrwx 1 root root 0 7月 19 02:58 ipc -> 'ipc:[4026531839]' lrwxrwxrwx 1 root root 0 7月 19 02:58 mnt -> 'mnt:[4026531840]' lrwxrwxrwx 1 root root 0 7月 19 02:58 net -> 'net:[4026531957]' lrwxrwxrwx 1 root root 0 7月 19 02:58 pid -> 'pid:[4026531836]' lrwxrwxrwx 1 root root 0 7月 19 02:58 uts -> 'uts:[4026531838]' $ sudo ls -l /proc/20026/ns 合計 0 lrwxrwxrwx 1 root root 0 7月 19 02:59 cgroup -> 'cgroup:[4026531835]' lrwxrwxrwx 1 root root 0 7月 19 02:59 ipc -> 'ipc:[4026531839]' lrwxrwxrwx 1 root root 0 7月 19 02:59 mnt -> 'mnt:[4026531840]' lrwxrwxrwx 1 root root 0 7月 19 02:59 net -> 'net:[4026531957]' lrwxrwxrwx 1 root root 0 7月 19 02:59 pid -> 'pid:[4026531836]' lrwxrwxrwx 1 root root 0 7月 19 02:59 uts -> 'uts:[4026531838]' 両者は一致
  6. Namespace見てみますよ / PID隔離ver $ sudo ./a.out -p current - clone

    pid: 20332 current - self pid: 20331 child - self pid: 1 child - parent pid: 0 $ sudo ls -l /proc/20331/ns 合計 0 lrwxrwxrwx 1 root root 0 7月 19 02:59 cgroup -> 'cgroup:[4026531835]' lrwxrwxrwx 1 root root 0 7月 19 02:59 ipc -> 'ipc:[4026531839]' lrwxrwxrwx 1 root root 0 7月 19 02:59 mnt -> 'mnt:[4026531840]' lrwxrwxrwx 1 root root 0 7月 19 02:59 net -> 'net:[4026531957]' lrwxrwxrwx 1 root root 0 7月 19 02:59 pid -> 'pid:[4026531836]' lrwxrwxrwx 1 root root 0 7月 19 02:59 uts -> 'uts:[4026531838]' $ sudo ls -l /proc/20332/ns 合計 0 lrwxrwxrwx 1 root root 0 7月 19 02:59 cgroup -> 'cgroup:[4026531835]' lrwxrwxrwx 1 root root 0 7月 19 02:59 ipc -> 'ipc:[4026531839]' lrwxrwxrwx 1 root root 0 7月 19 02:59 mnt -> 'mnt:[4026531840]' lrwxrwxrwx 1 root root 0 7月 19 02:59 net -> 'net:[4026531957]' lrwxrwxrwx 1 root root 0 7月 19 02:59 pid -> 'pid:[4026532213]' lrwxrwxrwx 1 root root 0 7月 19 02:59 uts -> 'uts:[4026531838]' PIDの所に注目 違う値に なっている
  7. 他のNamespace • IPC: System V IPC, POSIX メッセージキュー • Network:

    ネットワークデバイス、スタック、ポートなど • Mount: マウントポイント ◦ clone, unshareした内部でmountしたものの中身が外から見えなくなる • User: ユーザー ID とグループ ID • UTS: ホスト名と NIS ドメイン名 https://linuxjm.osdn.jp/html/LDP_man-pages/man7/namespaces.7.html http://gihyo.jp/admin/serial/01/linux_containers/0002?page=2
  8. chroot(2) によるルートディレクトリ隔離 #include <stdio.h> #include <stdlib.h> #include <dirent.h> #include <unistd.h>

    #define PATHNAME_SIZE 512 void show_dir(char *dirname) { struct dirent **namelist; int r = scandir(dirname, &namelist, NULL, NULL); if (r == -1) { exit(1); } printf("--- list files in directory '%s' ---\n", dirname); for (int i = 0; i < r; i++) { printf("%s\n", namelist[i]->d_name); } } int main(int argc, char *argv[]) { show_dir("/"); char current_path[PATHNAME_SIZE]; getcwd(current_path, sizeof current_path); printf("\nchange root to '%s'\n\n", current_path); int r = chroot(current_path); if (r == -1) { printf("chroot failed"); exit(1); } else { show_dir("/"); exit(0); } } 噂のchroot(2)
  9. chroot(2) によるルートディレクトリ隔離 $ sudo ./a.out --- list files in directory

    '/' --- lib boot srv bin run tmp root sys lib64 etc (以下略) (実行結果の続き) change root to '/var/tmp/my_chroot' --- list files in directory '/' --- my_chroot.c a.out . .. / の変更に成功
  10. まとめというか感想 • こんな感じで地道にやっていけばいつか自前コンテナは立ち上がる ◦ ユーザコマンドまじ有能 ◦ Dockerは甘え • 温故知新 •

    WebプログラマはWeb f/w使いになりがちなので、たまにはこういうのを書くのは充 足感がある • ちなみに今回のコードはここに置いた ◦ https://github.com/xorphitus/making-linux-container