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

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

0n1shi
October 08, 2019

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

0n1shi

October 08, 2019
Tweet

More Decks by 0n1shi

Other Decks in Technology

Transcript

  1. 自己紹介
 2
 大西 和貴 (@_k_onishi_)
 
 
 2017年度新卒
 SVOPチーム(運用) →

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

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

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

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


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

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


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


    
 名前空間にはいくつかの種類が存在し、
 どの名前空間(複数組み合わせることが
 可能)を作成するかで
 隔離する対象を決定する。
 
 参考: https://gihyo.jp/admin/serial/01/linux_containers/0002
  6. コンテナの実現 ~ 名前空間 ~
 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
  7. コンテナの実現 ~ 名前空間 ~
 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;
  8. コンテナの実現 ~ cgroups ~
 16
 cgroupsでは制限を設ける対象として以下のようなものが存在する。 • cpu CPUへのアクセス •

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

    デバイスへのアクセス • net_cls ネットワークパケットへのタグ付け • net_prio ネットワークトラフィックの優先度を動的に設定 • freezer タスクを一時停止または再開 参考: https://gihyo.jp/admin/serial/01/linux_containers/0002
  10. コンテナの実現 ~ 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
  11. コンテナの実現 ~ 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) を 参照のこと)
  12. コンテナの実現 ~ Linux Capabilities ~ 
 22
 • CAP_SYS_NICE 任意のプロセスの

    nice 値の変更を行う • CAP_SYS_PTRACE ptrace(2) を使って任意のプロセスをトレースする • CAP_SYS_TIME システムクロックを変更する (settimeofday(2), stime(2), adjtimex(2))
  13. コンテナの実現 ~ 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
  14. コンテナの実装
 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
  15. コンテナの実装
 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
  16. コンテナの実装
 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
  17. コンテナの実装
 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
  18. コンテナの実装
 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
  19. コンテナの実装
 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
  20. コンテナの実装
 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"
  21. おまけ(自作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 # ここからコンテナ内部
  22. おまけ(自作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