コンテナエンジンの作り方 ~ さくらの夕べ ヤンジェネバトル ~

43da88bf37c0eff9e288c67039a4fa4c?s=47 Kazuki Onishi
October 08, 2019

コンテナエンジンの作り方 ~ さくらの夕べ ヤンジェネバトル ~

43da88bf37c0eff9e288c67039a4fa4c?s=128

Kazuki Onishi

October 08, 2019
Tweet

Transcript

  1. コンテナエンジンの作り方
 さくらの夕べ ~ ヤンジェネバトル ~
 さくらインターネット 株式会社 技術本部 大西 和貴

    (C) Copyright 1996-2016 SAKURA Internet Inc 2019/10/08
  2. 自己紹介
 2
 大西 和貴 (@_k_onishi_)
 
 
 2017年度新卒
 SVOPチーム(運用) →

    レンタルサーバチーム(開発)
 
 サービスのコントロールパネルやメールソフトの
 バックエンドやフロントエンドを担当( Go / Typescript )
 
 
 Tech: 
 Linux / Unix / Kernel / File System / 仮想化 / コンテナ
 エミュレータ / バイナリ
 
 Lang: 
 C / Assembly / Perl
 
 Blog:
 レガシーガジェット研究所

  3. DockerやKubernetesを使いたい!
 だけど中身がよくわからず不安...
 概要
 3


  4. ならば作ろう!!
 概要
 4


  5. コンテナの実現とその実装を完全に理解し
 コンテナエンジン自作erになる!
 概要
 5


  6. 目次
 6
 • コンテナと仮想化
 • コンテナ技術の概要
 • コンテナの実現
 • コンテナの実装


    • まとめ
 • おまけ

  7. コンテナと仮想化
 7
 参考: https://www.public.ne.jp/2018/12/10/docker-1/

  8. コンテナと仮想化
 8
 参考: https://www.public.ne.jp/2018/12/10/docker-1/ • ソフトウェアで作成したハードウェアエミュ レータ
 • 各VMが完全に分離されている
 •

    物理マシンの上にOS、その上に仮想化 したハードウェア、その上にVMのOSが動 作しているのでオーバーヘッドが大きい

  9. コンテナと仮想化
 9
 参考: https://www.public.ne.jp/2018/12/10/docker-1/ • ただのプロセス
 • リソース(CPU, メモリ)管理やファイルシス テムの分離を行う


    • プロセスなので仮想マシンよりもはるか にオーバーヘッドが小さい
 • カーネルに依存するため、カーネルがク ラッシュした際などの影響は大きい

  10. コンテナの概要
 10
 コンテナとはLinux環境で動作するプロセスのこと。
 
 通常のプロセスと異なる点は名前空間や
 ルートファイルシステムがホスト
 から隔離されているところや、
 リソース制限が可能である
 ということ。
 


    参考: https://access.redhat.com/ja/articles/1459113
  11. コンテナの概要
 11
 コンテナはLinuxカーネルのサブシステムを組み合わせ実現されている
 
 • 名前空間(namespace)
 PIDやUIDなどをホストとは別空間で管理
 
 • cgroups


    CPUやメモリなどのリソース制御
 
 • chroot
 ルートファイルシステムを切り替え
 
 • Linux capabilities
 権限を管理する
 参考: https://access.redhat.com/ja/articles/1459113
  12. コンテナの実現 ~ 名前空間 ~
 12
 名前空間とはPIDやホスト名を管理する単位。
 
 新たに名前空間を作成し、その空間にプロセスを移動させることでホスト側のプロセスやホスト 名を当該プロセスから隠蔽し、PIDやホスト名などを隔離対象プロセスに再割り当てすることが 可能。


    
 名前空間にはいくつかの種類が存在し、
 どの名前空間(複数組み合わせることが
 可能)を作成するかで
 隔離する対象を決定する。
 
 参考: https://gihyo.jp/admin/serial/01/linux_containers/0002
  13. コンテナの実現 ~ 名前空間 ~
 13
 • NS(mount)
 マウントやアンマウント操作
 
 •

    UTS
 ホスト名やドメイン名
 
 • IPC
 POSIXメッセージキューやSystem Vメッセージキュー、セマフォ、共有メモリセグメント 
 
 • NET
 IPv4及びIPv6スタック、ルーティングテーブル、ファイアウォール、/proc/net、/sys/class/net、ソケット 
 
 • PID
 プロセスに割り振るPIDのマッピング 
 
 • CGROUP
 /proc/self/cgroup
 
 • USER
 UID、GID、Capabiliies
 
 参考: https://gihyo.jp/admin/serial/01/linux_containers/0002
  14. コンテナの実現 ~ 名前空間 ~
 14
 参考: linux kernel: 5.3.1 //

    include/linux/nsproxy.h // A structure to contain pointers to all per-process 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; struct cgroup_namespace *cgroup_ns; }; extern struct nsproxy init_nsproxy;
  15. コンテナの実現 ~ cgroups ~
 15
 プロセスグループのリソース(CPU、メモリ、ディスクI/Oなど)の利用を制限するLinuxカーネル の機能。 
 参考: https://gihyo.jp/admin/serial/01/linux_containers/0002

  16. コンテナの実現 ~ cgroups ~
 16
 cgroupsでは制限を設ける対象として以下のようなものが存在する。 • cpu CPUへのアクセス •

    cpuacct CPUについての自動レポートを生成 • cpuset マルチコアCPUのコア単位およびメモリノードを割り当て • memory メモリに対する制限設定と自動レポートの生成 参考: https://gihyo.jp/admin/serial/01/linux_containers/0002
  17. コンテナの実現 ~ cgroups ~
 17
 • blkio ブロックデバイスの入出力アクセス • devices

    デバイスへのアクセス • net_cls ネットワークパケットへのタグ付け • net_prio ネットワークトラフィックの優先度を動的に設定 • freezer タスクを一時停止または再開 参考: https://gihyo.jp/admin/serial/01/linux_containers/0002
  18. コンテナの実現 ~ cgroups ~
 18
 参考: https://gihyo.jp/admin/serial/01/linux_containers/0002

  19. コンテナの実現 ~ chroot ~
 19
 現在のプロセスとその子プロセス群に対してルートディレクトリを変更する。 ルートディレクトリを別のディレクトリに変更されたプロセスは、その範囲外のファイルにはアク セスできなくなる。 参考: https://www.maketecheasier.com/how-to-run-multiple-linux-distros-without-virtualization/

  20. コンテナの実現 ~ chroot ~
 20
 // task_struct->nsproxy->mnt_namespace->mount struct mount {

    struct hlist_node mnt_hash; struct mount *mnt_parent; struct dentry *mnt_mountpoint; struct vfsmount mnt; union { struct rcu_head mnt_rcu; struct llist_node mnt_llist; }; struct mnt_pcp __percpu *mnt_pcp; struct list_head mnt_mounts; /* list of children, anchored here */ struct list_head mnt_child; /* and going through their mnt_child */ struct list_head mnt_instance; /* mount instance on sb->s_mounts */ const char *mnt_devname; /* Name of device e.g. /dev/dsk/hda1 */ : 参考: linux kernel 5.3.1
  21. コンテナの実現 ~ Linux Capabilities ~ 
 21
 Linuxには特権ユーザと非特権ユーザの二種類が存在し、特権ユーザは権限チェックを全て バイパスする。 この特権ユーザに付与されている権限をグループ化し個々に有効無効を設定できるようにした

    モノのがLinux Capabilitiesである。 • CAP_SYS_BOOT reboot(2) と kexec_load(2) を呼び出す • CAP_SYS_CHROOT chroot(2). を呼び出す • CAP_SYS_MODULE カーネルモジュールのロード、アンロードを行う (init_module(2) と delete_module(2) を 参照のこと)
  22. コンテナの実現 ~ Linux Capabilities ~ 
 22
 • CAP_SYS_NICE 任意のプロセスの

    nice 値の変更を行う • CAP_SYS_PTRACE ptrace(2) を使って任意のプロセスをトレースする • CAP_SYS_TIME システムクロックを変更する (settimeofday(2), stime(2), adjtimex(2))
  23. コンテナの実現 ~ Linux Capabilities ~ 
 23
 // task_struct->cred struct

    cred { : kernel_cap_t cap_inheritable; /* caps our children can inherit */ kernel_cap_t cap_permitted; /* caps we're permitted */ kernel_cap_t cap_effective; /* caps we can actually use */ kernel_cap_t cap_bset; /* capability bounding set */ kernel_cap_t cap_ambient; /* Ambient capability set */ : } typedef struct kernel_cap_struct { __u32 cap[_KERNEL_CAPABILITY_U32S]; } kernel_cap_t; 参考: linux kernel 5.3.1
  24. コンテナの実装
 24
 https://github.com/k-onishi/runb

  25. コンテナの実装
 25
 https://github.com/k-onishi/runb # cgroups CGROUP_CONTROLLERS="cpu,memory,pids" cgcreate -g "$CGROUP_CONTROLLERS:$CONTAINER_NAME"

  26. コンテナの実装
 26
 https://github.com/k-onishi/runb # network setting BRIDGE_NAME="runb-bridge" ip link add

    name $VETH type veth peer name $ETH brctl addif $BRIDGE_NAME $VETH ip link set $VETH up ip link set $ETH netns $CONTAINER_NET_NS ip netns exec $CONTAINER_NET_NS ip address add 10.0.0.2/24 dev $ETH ip netns exec $CONTAINER_NET_NS ip link set $ETH up ip netns exec $CONTAINER_NET_NS ip route add default via 10.0.0.1
  27. コンテナの実装
 27
 https://github.com/k-onishi/runb # make a new name space. unshare

    \ --pid \ --uts \ --ipc \ --mount \ --cgroup \ --fork \ bash $CORE $CONTAINER_DIR $CONTAINER_NAME
  28. コンテナの実装
 28
 https://github.com/k-onishi/runb # cgroup cgclassify -g "$CGROUP_CONTROLLERS:$CONTAINER_NAME" $$

  29. コンテナの実装
 29
 https://github.com/k-onishi/runb # make file system mkdir -p $ROOT_FS_DIR/proc

    && mount -t proc -o noexec,nosuid,nodev proc $ROOT_FS_DIR/proc mkdir -p $ROOT_FS_DIR/dev && mount -t tmpfs -o noexec,strictatime,mode=755 tmpfs $ROOT_FS_DIR/dev mkdir -p $ROOT_FS_DIR/dev/shm && mount -t tmpfs -o noexec,nosuid,nodev,mode=1777,size=65536k tmpfs $ROOT_FS_DIR/dev/shm
  30. コンテナの実装
 30
 https://github.com/k-onishi/runb mkdir -p $ROOT_FS_DIR/dev/mqueue && mount -t mqueue

    -o noexec,nosuid,nodev mqueue $ROOT_FS_DIR/dev/mqueue mkdir -p $ROOT_FS_DIR/dev/pts && mount -t devpts -o noexec,nosuid,newinstance,ptmxmode=0666,mode=620,gid=5 devpts $ROOT_FS_DIR/dev/pts mkdir -p $ROOT_FS_DIR/sys && mount -t sysfs -o noexec,nosuid,nodev,ro sysfs $ROOT_FS_DIR/sys
  31. コンテナの実装
 31
 https://github.com/k-onishi/runb # make special device files mknod -m

    666 $ROOT_FS_DIR/dev/null c 1 3 mknod -m 666 $ROOT_FS_DIR/dev/zero c 1 5 mknod -m 666 $ROOT_FS_DIR/dev/full c 1 7 mknod -m 666 $ROOT_FS_DIR/dev/tty c 5 0 mknod -m 666 $ROOT_FS_DIR/dev/ptmx c 5 2 mknod -m 666 $ROOT_FS_DIR/dev/random c 1 8 mknod -m 666 $ROOT_FS_DIR/dev/urandom c 1 9
  32. コンテナの実装
 32
 https://github.com/k-onishi/runb # terminal touch $ROOT_FS_DIR/dev/pts/ptmx && mount --bind

    /dev/pts/ptmx $ROOT_FS_DIR/dev/pts/ptmx # I/O ln -s /proc/self/fd $ROOT_FS_DIR/dev/fd ln -s /proc/self/fd/0 $ROOT_FS_DIR/dev/stdin ln -s /proc/self/fd/1 $ROOT_FS_DIR/dev/stdout ln -s /proc/self/fd/2 $ROOT_FS_DIR/dev/stderr
  33. コンテナの実装
 33
 https://github.com/k-onishi/runb # etc hostname runB # change rootfs

    by chroot exec \ ip netns exec $CONTAINER_NET_NS \ capsh \ --inh="$SET_CAPS" \ --drop="$DROP_CAPS" \ --uid="$UID" \ --gid="$GUID" \ --chroot="$ROOT_FS_DIR" \ -- \ -c "/bin/bash"
  34. まとめ
 34
 • コンテナはLinux上で動作するただのプロセス。 • カーネル内サブシステムを組み合わせて実現。 • シェルでもコンテナは作成可能。もちろんC言語でも。

  35. おまけ(自作erの第一歩)
 35
 # 新たなルートディレクトリを作成。 mkdir my_container; cd $_ # ライブラリとユーティリティの用意

    cp -r /bin ./ cp -r /lib ./ cp -r /lib64 ./ mkdir usr; cp -r /usr/lib ./usr/ # 名前空間の分離とルートディレクトリの変更 unshare --pid --fork chroot . /bin/bash # ここからコンテナ内部
  36. おまけ(自作erの第一歩)
 36
 # proc fsの作成 mkdir -p /proc mount -t

    proc proc /proc # psコマンドの実行 ps PID TTY TIME CMD 1 ? 00:00:00 bash 4 ? 00:00:00 ps