Docker 超入門
CAMPHOR- DAY 2020
Koki Makita @km_conner
自己紹介
• Twitter: @km_conner
• GitHub: KMConner
• 春から京都大学大学院 M1
• 普段はバイトで C# & 研究で Python
このトークについて
話すこと
• モジュール間の関係性 (dockerd, containerd,…)
• runc がコンテナ間で環境を分離している仕組み
質問などあれば YouTube Live のコメント or Twitter の
#camphor_day まで!
Docker 使ってますか?
Docker とは?
もともとは港湾作業者 (コンテナの船からの積み下ろしなどをする
人) の意味
特徴としては
• コンテナごとに環境を分離することが可能
• Dockerfile をもとに作成されるイメージによりホスト OS に関わら
ずほぼ同じ環境を再現できる
Dockefile を書くだけで Docker を理解したと
思っていませんか?
それよりももっと奥深いもの
このトークのゴール
Docker の中身をざっくり
と理解する
このトークで扱う範囲
• Linux 上の docker に限定 (Windows は今回は扱わない)
• Docker はランタイムとして runc を使用していると仮定
• これ以降に示すコマンドはすべて Ubuntu 18.04 において実行
Docker に含まれるモジュール群
モジュール概観
Docker CLI
dockerd
containerd
RunC
Registry
REST API
gRPC
ランタイムとして使用
イメージの
Push/Pull
Docker CLI
いわゆる docker コマンド
• docker run …
• docker build …
dockerd の REST API で通信
• TCP ソケット、 Unix ソケットなどを通じて通信
dockerd
• docker cli が使用する REST API を公開する daemon
• containerd と gRPC を通じて通信
Docker CLI
dockerd
containerd
RunC
Registry
REST API
gRPC
ランタイムとして使用
イメージの
Push/Pull
containerd
• gRPC Endpoint で待ち受ける daemon
• コンテナのライフサイクル管理
• イメージの push / pull
• runc などのランタイムのコマンドを実行する
Docker CLI
dockerd
containerd
RunC
Registry
REST API
gRPC
ランタイムとして使用
イメージの
Push/Pull
runc
• containerd が使用するランタイム
• 実際にコンテナを作成するのはココ!
• OCI (Open Container Initiative) の規格に準拠
Docker CLI
dockerd
containerd
RunC
Registry
REST API
gRPC
ランタイムとして使用
イメージの
Push/Pull
OCI とは
コンテナイメージやコンテナランタイムの
仕様を定めている団体
Runc が環境分離を実現している
仕組み
名前空間
• PID などがコンテナ外から見えないようにするための仕掛け
• 基本的に名前空間の外は見えない
• 分離するもの
• PID (名前空間が違えば同じプロセス ID のプロセスが存在できる)
• MNT (ファイルシステムのツリーを完全に分離できる)
• NET (IP アドレス、ルーティングなどのネットワークスタックを分離)
• IPC (共有メモリ空間の分離)
• UTS (ホスト名、ドメインの分離)
• etc…
名前空間とコンテナの関係
• コンテナは (特に何も指定しなければ) 別の名前空間で実行され
るプロセス
• PID, MNT などの名前空間を新しく作ってその中でプロセスを実
行
名前空間の確認方法
• /proc/[pid] ディレクトリに、 各プロセスの情報が入っている
• /proc/self は特殊なリンクで、現在実行中のプロセスの情報にリ
ンクしている
• 例
• /proc/self/cwdはカレントディレクトリへのリンク
• /proc/self/exeは実行中のファイル
$ ls -la /proc/self/cwd /proc/self/exe
lrwxrwxrwx1root root 0 3月 28 11:41 /proc/self/cwd ->/root
lrwxrwxrwx1root root 0 3月 28 11:41 /proc/self/exe -> /bin/ls
名前空間の確認方法
• /proc/self/ns ディレクトリにある!
$ls -l /proc/self/ns
total0
lrwxrwxrwx1rootroot0 3月 2811:48cgroup->'cgroup:[4026531835]'
lrwxrwxrwx1rootroot0 3月 2811:48ipc ->'ipc:[4026531839]'
lrwxrwxrwx1rootroot0 3月 2811:48mnt-> 'mnt:[4026531840]'
lrwxrwxrwx1rootroot0 3月 2811:48net-> 'net:[4026531992]'
lrwxrwxrwx1rootroot0 3月 2811:48pid-> 'pid:[4026531836]'
lrwxrwxrwx1rootroot0 3月 2811:48pid_for_children-> 'pid:[4026531836]'
lrwxrwxrwx1rootroot0 3月 2811:48user->'user:[4026531837]'
lrwxrwxrwx1rootroot0 3月 2811:48uts->'uts:[4026531838]'
名前空間を見てみる
一番上はホスト側のシェル、下の 2 つはそれぞれ別のコンテナで
PID namespace を確認した結果
すべてバラバラ
$ls -l /proc/self/ns/pid
lrwxrwxrwx1rootroot0 3月 2812:07/proc/self/ns/pid-> 'pid:[4026531836]’
$ls -l /proc/self/ns/pid
lrwxrwxrwx1rootroot0Mar2803:10/proc/self/ns/pid-> 'pid:[4026532149]’
$ls -l /proc/self/ns/pid
lrwxrwxrwx1rootroot0Mar2803:11/proc/self/ns/pid-> 'pid:[4026532281]'
名前空間だけで十分ですか?
名前空間以外のテクニック
ただ単に名前空間で分離しただけでは不十分!
• システムコールの制限
• ファイルアクセスの制限
• etc…
システムコールの制限
• システムコールの中にはコンテナの外に影響を及ぼすものもある
• 例: SYS_TIME
• システムの時刻を設定する
• date–s“2020-07-24” (要 root 権限) でシステムの時計のセット
• デフォルトではコンテナ内から使用できない
• docker run --cap-add SYS_TIME … で使用可能に
システムコールの制限
コンテナ内から
何の (予定だった) 日付でしょう??
$ date –s “2020-07-24”
Fri Jul 24 00:00:00 UTC 2020
ファイルのアクセス制限
ファイルへのアクセスを制限したいファイルがある
例
$ echo c > /proc/sysrq-trigger
とするとシステムがクラッシュする (要 root 権限)
$ echo b > /proc/sysrq-trigger
とするとシステムが強制的に再起動(要 root 権限)
☞ 読み取り専用にする or 表示しないなどの対策
まとめ
• Docker は 1 つのアプリケーションではなくいくつかのモジュール
が連携して動いている
• runc ではコンテナ仮想化を実現するために名前空間をはじめと
する Linux カーネルの機能を使用している
• 他にも docker にまつわる Linux カーネルの機能はたくさんある
ので気になる人は以下の単語で調べてみてください
• Overlay File System
• Cgroup
• Etc…

CAMPHOR- day 2020 - Docker 超入門

Editor's Notes

  • #2 では、「Docker 超入門」というタイトルで話します
  • #3 簡単に自己紹介 本名は巻田、 Twitter やってるんでフォローお願いします 今年の春から京大の大学院、情報学研究科の M1 普段はバイトで C# を書きつつ研究では Python で Deep Learning
  • #4 このトークでは主に 2 つのことに関して話します モジュール間の関連性 (1 つのモノリシックなシステムではなく、複数のモジュールから構成されている) ランタイム「runc」 がコンテナ間で環境を分離している仕組み 質問などあればこのライブのコメントに書くか、 Twitter のハッシュタグでつぶやいて
  • #5 Docker 使ってますか?? 便利ですよね???
  • #6 まず、 Docker とはそもそも何か???に関して話します 英単語としての docker は港とかで作業する人、の意味で、コンテナとかの積み下ろしなどをする人です で、アプリケーションの docker はこれらの特徴 環境分離 コンテナに入れるとアプリケーション間の干渉が起こらない 同じ状況を再現 ライブラリ、ミドルウェアとかの環境がそっくりそのまま
  • #7 Docker は魔法ではなく、ちゃんとバックに動作の仕組みが存在する!
  • #8 このトークの目標は Docker が動いている仕組みをざっくりと理解する
  • #9 今回は、 Linux 上の docker に関してのみ扱う。 Windows 上の docker も存在はするが、今回は扱わない Docker ランタイム、 (これは後で説明) には runc を使用 ちなみに、今後に示すコマンドは ubuntu 18.04 で、 実行、 docker は snap で入れたもの、 あと、コマンドは基本的に root で実行してる
  • #10 まずは、 docker を構成するモジュールたちに関して解説
  • #11 全体がこんな感じ、 今回jは左側の 4 つに関して解説。 ちなみに、右にある registry は docker image を格納しておくサービスで、 Docker Hub や GitHub Packages などが有名
  • #12 まずは、 docker CLI に関して解説 いわゆる docker コマンドを叩いた時に最初に呼び出されるプログラム このプログラムは dockerd が提供する REST API のクライアントになっている REST API は TCP ソケットや Unix ソケットなどを通じて通信している
  • #13 Dockerd は cli が使用する REST API を公開している daemon ちなみに、 linux 上のアプリケーションで d が最後に着くのは daemon と言ってバックグラウンドで動き続ける、 httpd とか sshd とか REST API 内の動作は containerd と gRPC で通信して動いている
  • #14 Containerd は、 dockerd が使う gRPC インタフェースを提供 コンテナのライフサイクル管理、作成、起動、削除など イメージの push と pull コンテナ周りの具体的な機能はコンテナランタイムの runc に任せている
  • #15 Runc とは containerd が依存する、コンテナランタイムで、実際にコンテナを作ったりする ただし、 runc は OCI が定めた企画に従って作成されている。 他にもランタイムは存在するので、代替可能 OCI はコンテナ周りの仕様を決めている団体で、コンテナランタイム以外にもイメージに関する企画も決めている
  • #16 次は、 runc が環境の分離を実現している仕組みを解説
  • #17 Runc では、ホスト OS とコンテナで同じ共有されたカーネルを使用している。 で、その中でものを分離するのに使われるのが namespace これによって namespace 内から namespace 外のものが基本的に見えなくなるため環境が分離される NS にはいくつかの種類があり、分離しているリソースが異なっている 一つずつ見ていくと、 PID name space は名前空間を分けることで同じプロセス ID のプロセスが存在できるようにする MNT name space はファイルシステムのマウントポイントを他の name space から見えないようにしてファイルシステムのツリーを完全に分離する network name space は IP アドレス、 NIC などの network stack を分離する IPC name space はプロセス間通信で使用される共有メモリを、分離する UTS name space は同じホストで複数の host name, domain の利用を可能にする 他にも色々ある。 詳しくはググってみてください
  • #19 では、名前空間を実際に確認する方法を紹介 その前に、プロセスの情報を見る方法を紹介します /proc ディレクトリの中を見ると、数字のディレクトリがいっぱい! で、これの一部はプロセス ID に該当するもの 他に、 /proc/self は特別なリンクで、現在実行中のpid のディレクトリにリンクしている つまり、 /proc/self ディレクトリに、現在実行中のプロセスに関する情報が入ってる。 中身はいろいろあるが、、、 cwd, exe はそれぞれカレントディレクトリ、実行中のプログラムへのリンクが張られている。
  • #20 先ほどの /proc/self の中に、 /proc/self/ns というディレクトリがあり、その中に namespace に関する情報が入っている ここを見ると nams space の一覧が見られる
  • #21 では、実際にホスト側のシェル、コンテナ内のシェルで実際に name space を見てみる、 ここでは PID name space を見てみる 一番上がホスト側、下 2 つがそれぞれ別のコンテナ内で PID を確認した結果 赤字の部分を見てわかるが、すべて別々の名前空間内で実行されていることがわかる
  • #22 では、名前空間があればそれで充分分離できているのか??????
  • #23 そんなことはない!!!! コンテナ内の処理は、基本的に外から見ると root 権付で実行されることが多いため、結構厄介 他にもいろいろと必要な機能があるが、ここでは システムコールの制限、 ファイルアクセスの制限 を紹介。
  • #24 システムコールとは、要するに OS カーネルの機能呼び出しみたいな感じ、 で、カーネルと話すので、カーネルの動作に影響する可能性があり、それは完全なコンテナ間の分離を妨げる。 例えばシステムの時刻を設定するシステムコールを考える。 システムの時刻設定は date コマンドで行える。 例えば、 OTP とかは時刻に従って生成されているシステムも多い。 つまり、システムの時計が狂うとこれはいろいろとヤバイ デフォルトでコンテナ内からは実行できないが、 --cap-add すればコンテナ内から実行できる。
  • #25 実際に動かしてみると、 コンテナ内から、このコマンドを実行したところ、ホスト OS の時計が変わる! ちなみに、この日付は???? まあググってみてw
  • #26 他にも、ファイルシステム上にはファイルのアクセス制限が必要な場合がある 例えば、 /proc/sysrq-trigger のファイルは以下のような性質がある C を書き込むとクラッシュ、 B を書き込むと強制的な再起動 これでは困るので、読み取り専用にするとか非表示にするとかの対策。
  • #27 まとめです。 まず、 Docker はものしりっくなものではない! で、 ruunc ではいろいろと仮想化のために Linux カーネルの機能を利用している 他にも docker にまつわる linux kernel の機能は沢山あるので、興味あればこの辺の単語でググってみてください、