Slide 1

Slide 1 text

コンテナエンジンの作り方
 さくらの夕べ ~ ヤンジェネバトル ~
 さくらインターネット 株式会社 技術本部 大西 和貴 (C) Copyright 1996-2016 SAKURA Internet Inc 2019/10/08

Slide 2

Slide 2 text

自己紹介
 2
 大西 和貴 (@_k_onishi_)
 
 
 2017年度新卒
 SVOPチーム(運用) → レンタルサーバチーム(開発)
 
 サービスのコントロールパネルやメールソフトの
 バックエンドやフロントエンドを担当( Go / Typescript )
 
 
 Tech: 
 Linux / Unix / Kernel / File System / 仮想化 / コンテナ
 エミュレータ / バイナリ
 
 Lang: 
 C / Assembly / Perl
 
 Blog:
 レガシーガジェット研究所


Slide 3

Slide 3 text

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


Slide 4

Slide 4 text

ならば作ろう!!
 概要
 4


Slide 5

Slide 5 text

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


Slide 6

Slide 6 text

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


Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

コンテナと仮想化
 8
 参考: https://www.public.ne.jp/2018/12/10/docker-1/ • ソフトウェアで作成したハードウェアエミュ レータ
 • 各VMが完全に分離されている
 • 物理マシンの上にOS、その上に仮想化 したハードウェア、その上にVMのOSが動 作しているのでオーバーヘッドが大きい


Slide 9

Slide 9 text

コンテナと仮想化
 9
 参考: https://www.public.ne.jp/2018/12/10/docker-1/ • ただのプロセス
 • リソース(CPU, メモリ)管理やファイルシス テムの分離を行う
 • プロセスなので仮想マシンよりもはるか にオーバーヘッドが小さい
 • カーネルに依存するため、カーネルがク ラッシュした際などの影響は大きい


Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

コンテナの概要
 11
 コンテナはLinuxカーネルのサブシステムを組み合わせ実現されている
 
 • 名前空間(namespace)
 PIDやUIDなどをホストとは別空間で管理
 
 • cgroups
 CPUやメモリなどのリソース制御
 
 • chroot
 ルートファイルシステムを切り替え
 
 • Linux capabilities
 権限を管理する
 参考: https://access.redhat.com/ja/articles/1459113

Slide 12

Slide 12 text

コンテナの実現 ~ 名前空間 ~
 12
 名前空間とはPIDやホスト名を管理する単位。
 
 新たに名前空間を作成し、その空間にプロセスを移動させることでホスト側のプロセスやホスト 名を当該プロセスから隠蔽し、PIDやホスト名などを隔離対象プロセスに再割り当てすることが 可能。
 
 名前空間にはいくつかの種類が存在し、
 どの名前空間(複数組み合わせることが
 可能)を作成するかで
 隔離する対象を決定する。
 
 参考: https://gihyo.jp/admin/serial/01/linux_containers/0002

Slide 13

Slide 13 text

コンテナの実現 ~ 名前空間 ~
 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

Slide 14

Slide 14 text

コンテナの実現 ~ 名前空間 ~
 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;

Slide 15

Slide 15 text

コンテナの実現 ~ cgroups ~
 15
 プロセスグループのリソース(CPU、メモリ、ディスクI/Oなど)の利用を制限するLinuxカーネル の機能。 
 参考: https://gihyo.jp/admin/serial/01/linux_containers/0002

Slide 16

Slide 16 text

コンテナの実現 ~ cgroups ~
 16
 cgroupsでは制限を設ける対象として以下のようなものが存在する。 • cpu CPUへのアクセス • cpuacct CPUについての自動レポートを生成 • cpuset マルチコアCPUのコア単位およびメモリノードを割り当て • memory メモリに対する制限設定と自動レポートの生成 参考: https://gihyo.jp/admin/serial/01/linux_containers/0002

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

コンテナの実現 ~ cgroups ~
 18
 参考: https://gihyo.jp/admin/serial/01/linux_containers/0002

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

コンテナの実現 ~ 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

Slide 21

Slide 21 text

コンテナの実現 ~ 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) を 参照のこと)

Slide 22

Slide 22 text

コンテナの実現 ~ Linux Capabilities ~ 
 22
 ● CAP_SYS_NICE 任意のプロセスの nice 値の変更を行う ● CAP_SYS_PTRACE ptrace(2) を使って任意のプロセスをトレースする ● CAP_SYS_TIME システムクロックを変更する (settimeofday(2), stime(2), adjtimex(2))

Slide 23

Slide 23 text

コンテナの実現 ~ 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

Slide 24

Slide 24 text

コンテナの実装
 24
 https://github.com/k-onishi/runb

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

コンテナの実装
 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

Slide 27

Slide 27 text

コンテナの実装
 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

Slide 28

Slide 28 text

コンテナの実装
 28
 https://github.com/k-onishi/runb # cgroup cgclassify -g "$CGROUP_CONTROLLERS:$CONTAINER_NAME" $$

Slide 29

Slide 29 text

コンテナの実装
 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

Slide 30

Slide 30 text

コンテナの実装
 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

Slide 31

Slide 31 text

コンテナの実装
 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

Slide 32

Slide 32 text

コンテナの実装
 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

Slide 33

Slide 33 text

コンテナの実装
 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"

Slide 34

Slide 34 text

まとめ
 34
 • コンテナはLinux上で動作するただのプロセス。 • カーネル内サブシステムを組み合わせて実現。 • シェルでもコンテナは作成可能。もちろんC言語でも。

Slide 35

Slide 35 text

おまけ(自作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 # ここからコンテナ内部

Slide 36

Slide 36 text

おまけ(自作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