はじめに
こんにちは、最近パラパラチャーハンが作れるようになりました。コツは「ご飯の量と同じくらいみじん切りにした野菜を入れて誤魔化す」です。
はい、ということで今回はDockerはどうやって作られているのか気になって調べたことをまとめていきたいと思います。実際の細かい動きはどうしても図とかないと分かりにくいと思うので、その辺は参考になった記事を紹介していきます。
前提知識
Dockerは Linux が提供するコンテナの技術を使用して実現しているため、前提知識としてLinuxの基本的な知識が必要です。今回はその説明は省きますが、以下の本がすごく分かりやすかったので是非読んでみてください。忙しい人は、自分がこの本を読んでまとめていたものがあったのでもし興味があればそちらをご覧ください。
余談ですが次に読むといい感じの本も紹介しておきます。Linuxについて調べれば調べるほどC言語と仲良くなれると思うので、C言語に苦手意識のある方はLinuxから入るといいかもしれないです。(C言語が書けるようになる訳ではないですが、確実にC言語に対するイメージが変わると思います。)
次に読むといい感じの本
コンテナの実態
コンテナは名前からしてもよく箱のようなイメージを持つことが多いですが、実際は任意の範囲でプロセスやNamespaceなどを区切って、その区切った範囲でどのくらいのリソースを利用できるのか設定しています。コンピュータ内で論理的(仮想的)に環境を区切って(隔離して)おくことで例えば以下のようなことができます。
- 自分のパソコンの環境を汚さなくて済む
- 簡単に環境を構築したり、破棄したりできる
上記で隔離という用語が出てきましたが、具体的に何を隔離していて、何を設定しているのか定番のやつを紹介します。
補足)自分のPCの環境をホストと呼び、PCの環境を論理的に区切った範囲内のことをコンテナと呼ぶようにします。あと、隔離=「(論理的に)区切る」と読み変えると分かりやすいかもしれないです。
UTS名前空間
ホスト名、ドメイン名。これを隔離することでホストから見たホスト名とコンテナから見た方ホスト名を別のものにすることができます。言い方を変えるとホスト側とコンテナ側のホスト名を別々に設定できます。
PID名前空間
PIDとはプロセスIDのことです。通常、プロセスIDはプロセスが生成されるごとにカーネルにより一意の数値が割り当てられますが、隔離をすることでホストとコンテナそれぞれでそれぞれのPIDを割り当てることができます。そのため、ホストとコンテナで重複する値を設定することができたりします。例えば、コンテナのあるプロセスのPIDが3でもホストから見るとそのプロセスのPIDは8に設定されているみたいなことになっています。
ネットワークインターフェース
基本的にコンテナごとに仮想的なネットワークインターフェースを付与しておきます。ネットワークインターフェースとはインターネットの入り口で、有線LANポートとかWi-Fiが入ってくる入り口をイメージするといいと思います。このインターフェースによってコンテナでパケットを受信(ネット接続)することができます。
コンピュータのリソース
プロセスなどを隔離したら、その隔離した範囲でどのくらいのリソース(メモリ、CPU)を使えるのか設定をします。コンテナ1ではメモリを1GB使えて、コンテナ2ではメモリを2GB使えるみたいなことができます。
ファイルシステムの分離
基本的に一つのコンテナにつき一つのルートディレクトリを持っています。docker exec -it コンテナ名 bash
とかしてdockerでコンテナに入ったときに、シェルのプロンプト変わりますよね。あれです。あれはルートが変わっていて、シェルの設定ファイルがないためにプロンプトに色とかがついていないです。コンテ内でpwd
コマンドを実行してみてもホストとは違う結果が得られるのもルートが変わっているからです。でも、Dockerファイルで指定した作業ディレクトリとかアプリケーションの動作に必要なファイル(nodejsとか)とかはありますよね。ルートを変えることでルートディレクトリの構造を必要最低限ごっそり持ってきてそこに必要なファイルのみを置いていくことができます。また、コンテナを破棄するときもそのコンテナのルートディレクトリを削除するだけで綺麗さっぱり、コンテナ内のアプリの動作環境に必要なファイルたちを消すことができます。
Linuxのコンテナ技術
コンテナの実態がなんとなく掴めたところで、次はそれを実際にどうやって実現しているのか気になってくると思います。答えは、Linuxから提供される機能を使ってコンピュータ内で環境を区切っています。
NameSpace: UTS、PID、ネットワークなどの名前空間を分離します。
ControlGroupe: リソースを設定します。
FileSystem: 必要なファイルをマウントします。
Chroot: ルートディレクトリを変更します。
具体的なLinuxの機能の説明はこちらの記事をご覧ください。(丸投げ笑)
実際にどうやって実装してくのか気になって調べたら以下の記事たちを見つけました。
Containers From Scratch • Liz Rice • GOTO 2018
ライブコーディングでGoによりdockerを作っています。これを見るとなんとなくコンテナの実装イメージができると思います。この動画の日本語の解説がありました↓
【Go言語】自作コンテナ沼。スクラッチでミニDockerを作ろう - カミナシ開発者ブログ
docker内でLinux環境を作り、そこで自作Dockerを体験することができます。
Fewbytes/rubber-docker
pythonでdockerを作るワークショップらしいいです。難しめ。がっつり作っていく感じ。
OverlayFS
OverlayFSというLinuxが提供してくれるファイルシステムをdockerでは使っているみたいです。このファイルシステムを使うことでコンテナのファイルを効率よく管理できます。OverlayFSは何者でdockerでどうやって使われているかは以下の記事がわかりやすかったです。タイトル通り世界一わかりみが深いと思います。
【連載】世界一わかりみが深いコンテナ & Docker入門 〜 その6:Dockerのファイルシステムってどうなってるの? 〜 | SIOS Tech. Lab
Network
以下の記事ではdockerコンテナがどうやってネット接続しているのか解説してくれています。仮想スイッチとか仮想NICとかはLinuxの機能で用意していることになります。僕たちが Docker for Macをインストールしたときには自動で物理NICから仮想スイッチまでの経路は用意してくれているみたいですね。またdockerでコンテナを作った時はそのコンテナにくっつける仮想NICを生成して仮想スイッチに接続するみたいなことを透過的にやってくれてるみたいです。
【連載】世界一わかりみが深いコンテナ & Docker入門 〜 その5:Dockerのネットワークってどうなってるの? 〜 | SIOS Tech. Lab
最後に
今回自分は 「docker自作したい => Linuxを調べる => C言語いいかもしれない」 みたいな感じで今まで苦手意識のあったC言語に興味を持つことができました。自分があまり興味を持てない部分は自分の知識不足で発生するパターンがあるんだなと思いました。興味を持てる範囲から少しずつ知識を増やしていこうと思います!最後に書くことが思いつかなくて大学の実験レポートくらいの真面目度になってしまいましたが、最後まで読んでいただきありがとうございました!