Dockerって何? って聞かれたときの解説、の解説

11 min read読了の目安(約10500字 6

TL;DR

  • Dockerは仮想化であるコンテナの実装の一種
  • ただし、広義のDockerはOCI系コンテナの総称
  • アプリの配布と実行の仕組みと思えばOK
  • コンテナによりIaCや一貫したデプロイ、H/Wの効率的な利用がしやすくなる

はじめに

おそらく過去幾度となく生み出されたであろうDocker解説記事となります。正確には解説動画の解説記事。

というのも、Dockerあるいはコンテナはもはや当たり前、と言えるほど普及してるようにもSNSやブログとかだけ見てると思えますが、実際には話題は知ってるけど良く分かってない/業務で今度使う事を検討したいけどつまり何なの? って人もまだまだ多いです。

なので私が 「Dockerって何?」 と聞かれたとき答えてる内容を動画にしてみました。技術的な詳細を解説というよりは 「そもそも何なの?」 「何で流行ってるの?」 を話しています。

動画で使っているスライドはこちら。

https://slide4vr.nklab.dev/slide/0m3ItnvCMQhbACV9rR5mkdmFOns2/c4c0f543-02c2-433a-9e69-67faecf4c982/

ただ動画の中では長さの都合で話さなかったこともあるので、もう少しだけ詳細を書いておこうと思い解説の解説としてこの記事を書きました。

Dockerとは?

DockerはDocker社によって開発されるOCI系のコンテナです。元々、Docker社はdotCloud というPaaSベンダーで、フルスクラッチでアプリを作る場合のプラットフォームとして当時人気だったのHerokuとは異なり、WordPressとか既存のアプリケーションのホスティングを簡単に出来ることを売りにしていました。その中で使われていた技術がDockerコンテナであり、こちらを公開したところ本業よりも人気になってしまい社名もDocker社に改めて本格的にコミットしてきました。

コンテナという存在はDocker以前にもありましたが、既存のものよりポータビリティに優れ、この段階では後述するVagrantの後継としてちょうど良いポジションだったのでまず普及したかな、という感じがります。もちろん他の文脈で気にしてた人も当時からいると思いますけど。

もう一つ重要な点としてDockerは具体的な実装としてのDockerとは別にOCI系コンテナの総称としても使われます。例えば本番環境でDockerを使う事はそんなに多くなく実は別のコンテナを使ってるのですが便宜上「Dockerを使ってる」と言ったりします。これは後述するOCIと呼ばれる仕組み自体がDockerをオープン化したものであり、その流れから広義にはDockerはOCI系コンテナの総称となります。このあたり普段はあまり気にしなくて良いと思いますが、たまに意識する必要もあるので 「狭義のDockerと広義のDockerがある」 といったん覚えてもらえれば良いかと思います。

Dockerは本番環境では単体ではなく k8s(Kubernetes) と組み合わせて使うれます。この組み合わせが モダンなインフラを作る基礎技術 になっているので現在メチャクチャ流行っている、という訳です。また**「Dockerって何?」** という話題になった時にやれIaCだやれ分散システムだと話が発散しがちなのですが、これは元々色々な文脈のプラクティスがDockerに合流しているためです。その点に関しては後半で説明をします。

コンテナとは?

コンテナ概要

コンテナはOS機能による仮想化の一種です。最近よく聞く名前だと思いますが特に新しい技術ではなく、古くはメインフレームから始まる何十年も前からある仕組みです。SolarisのZONE、BSDのJail、あるいはLinuxのLXCやOpenVZなど様々なコンテナがあり活用はされてきたのですが、今はコンテナといえばOCI系コンテナを指す と言っても過言ではないと思うので、特別な文脈であれば「コンテナ == OCI系コンテナ」で大丈夫です。この記事でも以降は単にコンテナと呼びます。

コンテナは OSの仮想化 とも呼ばれますが、VMWareなどのH/W仮想化 とは異なり、OSそのものは共有しているが名前空間の分離やリソースの割り当てを分離しています。そのためオーバーヘッドが非常に小さく ほとんど普通のプロセスと同様の速度で起動/実行されます。

OS(正確には狭義のOSであるカーネル)を共有するのでVMWare等のように自由にゲストOSを選ぶことは出来ません。OS機能を使ってるのでホストOSも通常は限定されます。Dockerの場合はLinuxをベースとした仕組みなので原則Linuxのみで動作します。厳密に言えばWindows ContainerというOCIに準拠したWindowsのコンテナもありますが、これは逆にWindowsでしか動かないのでそこまで利用はされていません。(OCI準拠かはさておきWindowsの内部では割とコンテナは使われてます。WSL2とかDefender Application Guardとか)。いや、うちのMacやWindowsはDocker使えてるよ? と思われるかもしれませんが、あれはDocker DesktopがVitual Boxや各種OSの仮想化機能を使ってH/W仮想化でLinuxを動かしてその上でDockerを動かして透過的につないでるだけです。とても便利な仕組みですが、仮想環境の上で動いていると知らないと偶に罠にはまります。最近はあまりないけど。

H/W仮想化とコンテナ

H/W仮想化とコンテナの違いをもう少し追っていきましょう。良くある説明の図はこんな感じ。

H/W仮想化ではまずハイパーバイザーが一番下に置かれます。その上で各種ゲストOSが動く形ですね。ハイパーバイザーとは雑に言えば「仮想マシン専用の実行基盤(=OS)」です。通常のOSよりも仮想マシンの実行に特化しているので無駄なものが無くセキュアで軽く高機能、というわけです。VMWare ESXiが一番有名だと思います。

本当はH/W仮想化もハイパーバイザー方式以外にもType2とも呼ばれるQEMUやVirtualBoxなどOSのアプリケーションとして動く仮想化ソフトウェアもあるのですが、サーバサイドでは通常はハイパーバイザー方式を採用するのでそちらと比較しています。

ハイパーバイザがその上で各マシンをエミュレーションしその上にOSをデプロイしているのに対し、コンテナの場合は同じOSの上にコンテナという箱を置いてそこでアプリ単位での仮想化を実行しています。図を見るだけでもパソコン/サーバを丸ごとエミュレーションしているH/W仮想化よりはアプリの実行のところだけ仮想化しているコンテナ の方がなんとなくオーバーヘッド小さそうですよね?

具体的な仮想化対象の違いは以下の通り。

図の通り、H/W仮想化ではCPUやメモリといったものを仮想化しています。そのため仮想マシンより上は通常のOSになりWindowsとLinuxをそれぞれGuest OSで動かしたりすることも可能です。準仮想化の話とかあるけどここでは割愛。

対してコンテナでの仮想化の基本は名前空間の分離です。これは同じOSの上にあるのにそれぞれのアプリを専用環境で動かしているように見えるという事です。例えば利用するディレクトリ及びインストールされているソフトウェアが異なっていたり、コンテナ間でプロセスが共有されなかったり、NWで同じIPやポートを使っても干渉しなかったり、そういったOS
OSの各種機能が隔離されているという事です。またCPUやメモリ、ストレージといったリソースの割り当ても制御します。

コンテナはあくまでOSの各プロセスにタグのようなものを付けコンテナ間で干渉しないようにフィルタリングしているような挙動になります。実際にDockerなどであればホストOSでPSコマンドを打てばDocker上のプロセスも通常のプロセスのように見えます。H/W仮想化でも仮想マシンの上で動くプロセスを確認は出来ますがこれは仮想マシンの上で動くOSをのぞき込んでるだけでコンテナのケースとはかなり挙動が違います。この違いがパフォーマンスやセキュリティに大きな影響を与えています。

イメージとコンテナ

Dockerの基本となるDockerfile/イメージ/コンテナについて解説します。

まず、Dockerfileですがこれはビルドスクリプトです。Dockerではマニュアル片手にコンテナにログインしてコマンドを手作業で打って環境構築、ということは通常はしません。代わりにDockerfileというBash風のビルドスクリプトを使います。これによって手順書でインフラを作るよりも誰が作っても再現性の高い環境を構築できます。このような考え方をIaC (Infrastructure as a Code) と呼びます。AnsibleとかChefとか使ったことがある人はイメージがしやすいですね。ビルド結果としてimageが出来ます。

次にimageですが、これはLinuxのディレクトリを/からtarで固めたものと考えてみてください。実際にはDockerはLayerd File Systemを採用しているので単一のファイルとして固められてるわけではないのですが/から固めたファイル全部と考えた方が想像しやすいのでまずはそう考えても良いと思います。LinuxといえばRedhatやCentOS、DebianやUbuntuなど様々なディストリビューションがありますがカーネルは同じです。そのためルートからのディレクトリを丸ごと置き換えてやれば異なるディストリビューションとして振舞います。またイメージの最も重要な点は 「配布可能」 という事です。これによりWrite once, Run anywhereをいかなる言語であっても実現する事が出来ます。

最後にContainerですがこれはImageを実行した状態と考えることが出来ます。ImageとContainerは最初はどっちがどっちだったかと混乱しがちですが、ディスクイメージのような塊がImage、実際に動いてるプロセスがContainerと考えると良いと思います。

Dockerが流行っている理由

Dockerで出来ること

簡単に構成の話が出来たので、次にDockerで出来る事を解説します。シンプルに言えば以下の3つです。

  • DockerfileでImageを作成できる
  • Docker Imageを配布できる
  • Docker Imageをコンテナとして隔離された形で実行できる

という3点です。つまるところアプリを必要なライブラリごとパッケージング して、配布して環境に依存せずに実行 できます。さて、ではなぜこの特徴でここまで流行っているのでしょうか?

端的にいえばDockerは流行っているから流行っています

DockerHubやk8sといったエコシステムの力を使いたいから流行っているのです。もちろん、そのエコシステムが出来上がったのには理由があり、それこそが最初に上げたDockerの特徴です。この特徴が有用だったでのDocker以前から存在していた様々なプラクティスがDockerに集約されていきました。

Docker前史

先ほど述べたようにDockerは様々なプラクティスが集約されています。つまり目的が分かりづらいです。なので、Dockerに集約される前の前史を振り返ってみます。

運用/インフラ/あるいはDevOpsという界隈では上記の4つの考え方があり10年くらい色んな製品が群雄割拠していました。

パッケージング/ポータビリティ

これは依存関係を含めて一つに固めてそれを配布すればどこでも動く、ということです。一番成功した例はJavaEEのwarではないでしょうか? あるいはRubyのエンジニアならばbundler, Pythonのエンジニアであればpipenvを思い浮かべても良いと思います。これらは単一ファイルまたはディレクトリに依存を固めることでポータビリティを実現しています。ただし課題もあって多くの場合はOSのグローバル環境にも依存していしまいます。/var/etcあるいはホームディレクトリなどの構成やIPアドレスにポート番号、また自前主義のJavaはともかくスクリプト言語だとOSにインストールされているパッケージも重要です。こういったところで差分が出てしまいポータビリティには課題が残る状態でした。これを解決するために仮想OSに固めてVMイメージを配布するVagrantというソフトも人気を博していましたが、Dockerに比べると起動のオーバーヘッドやイメージが大きかった状態です。

IaC (Infrastructure as a Code

先ほども説明した通り手作業ではなくスクリプトでインフラを組む考え方です。これは手順書の代わりに構築を自動化させて作業を効率化させるという観点もあるのですが、どちらかというとバージョン管理や簡単なdiffの確認、PullRequest/コードレビューなど開発のベストプラクティスをインフラにも持ち込むための考え方です。AnsibleやChefも有名ですね。Iacにより手順書やパラメータシートを疑って本番で値を確認しなくても良くなります。特にその中でもDockerは一度作った環境を変更しないImmutable Infrastructureと呼ばれる運用を実現できます。実はAnsibleやChef、あるいは特に自前のBashによる環境構築をした場合は手で誰かが後から変更してしまったり、スクリプトに不備があった時に誤った変更をしてしまい環境を壊す場合がありました。Immutable Infrastructureでは変更では無く常に新規構築なので誤った場合に元の内容に戻すのも容易です。

サーバ利用効率化

ハードウェアをより有効活用しよう、という話です。現在のサーバはスペックが高く通常時は空きリソースが出来るのは当然です。そしてクラウド時代と違いHWは貴重なので、アプリケーションを相乗りさせてHWリソースを有効活用しようという話になりました。しかしながら、素朴な相乗りでは同一のライブラリの異なるバージョンを要求して片方のアプリが壊れたり、セキュリティ設定があまいだと別チームの人間が別アプリを触れたり、そして何より片方のアプリが高負荷な時に巻き込まれて死ぬ、という事象が発生していました。これらを解決するためにOSレベルのセキュリティ/リソースの分離をすべくVMwareなどの仮想化が普及していきました。特にVMWareは2つの異なるアプリケーションが同時に高負荷になる可能性は低い、という特性に注目して単にに2で割ったリソース分配ではなく少し多めにリソースを割り当てるオーバーコミットをする事が出来ます。銀行の信用想像みたいなものです。皆が一斉に引き出す(=一斉に高負荷)と取り付け騒ぎになるのも同様ですね。

オーケストレーション

この文脈では大規模分散システムのリソース管理を指します。これはある意味ではH/W仮想化を使ったサーバ利用効率化をより推し進めたものでOSのスケジューラよりもよりたくさんの必要な情報をとれるためミドルウェアとして実装され対応したアプリケーションのスケジューリングを行っていました。有名な実装としてHadoop YARNやMesosがあります。

こういった様々な観点がDockerとK8sに集約されていき、現在は上記の4つのいずれかを実践したい場合は最新の環境がDocker + k8sになるので人気があるわけです。まあ、最新の仕組みが一番自分たちに合うかどうかは別の話だけど。

Dockerの周辺システム

DockerHub

DockerHubはDockerイメージを共有するための仕組みです。DockerにはRegistryというイメージを配布する仕組みがあり、DockerHubはその本家本元公式サイト、というわけです。DockerHubの公開済みイメージを使う事で今まではそれなりに手間がかかったMySQLやMongoDBなどのミドルウェア、あるいはそれらも利用したWordPressのようなアプリケーションが今やdocker runの一発で構築できるようになりました。これはメチャクチャ強力です。Dockerが流行った原動力の一つはこのDockerHubの豊富な充実度だと思っています。

k8s (Kubernetes)

k8sは非常に大きく複雑な仕組みなのであまり触れませんが、オーケストレーションを担当するミドルウェアになります。つまりDockerを使った分散環境です。色々機能はありますが主にスケジューリング/デプロイ/セキュリティを担当します。つまりYARNやMesosのような位置づけですね。Googleが社内で利用していたBorg/Omegaといった大規模コンテナ実行基盤をベースに、Dockerに対応できるように一から書き起こしたものです。元々コンテナの超ヘビーユーザであるGoogleによって作られただけあって拡張性が非常に高くかつスケーラブルです。

分散環境で自由度の高いスケジューリングを行おうとすると配下のどのノードにもデプロイでき依存無く実行される必要があります。これにはDockerの特徴は最適です。

余談ですがk8sは本質的にPaaSなどのクラウド環境を作る基盤という特性が強いので自力で運用しようとするとかなりのパワーを使います。そのためKNative/Cloud RunやHerokuのようなPaaS/CaaSレベルに仕立てたものかFargateやGKE Autopilotのようにフルマネージドなk8sか、せめてGKEやAmazon EKSを使うのが良いと個人的には思います。前から順番にお勧め。

Docker以外のOCIコンテナ

OCIは元々Dockerをオープン化した仕様となります。さらにリファレンス実装の多くが元々Docker由来という事もあってOCI系コンテナの総称としてDockerを使うというのはすでに述べた通りです。逆にいえばDocker以外のOCI系コンテナも存在します。

有名どころとしてはcontainerd, cri-o, そしてPodmanです。

containerdは実はDockerの内部にも使われている実装でいわばDockerから本番実行に不要な部分を外したコードです。そのためDockerとの互換性も非常に高く多くの本番環境で動作しています。

cri-oはその登場経緯からしてk8sで動かすことを目的として作られました。そのため軽量でk8sで動かすことに特化しているのでやはりこちらも本番環境で人気があります。

最後にPodmanですがRedhat社がDockerの代替を狙って開発しているコンテナです。Docker同様に開発ツールなのでビルドなどの機能も充実しています。大きな特徴として不要なデーモンプロセスを無くしたりsystemdとの連携を高めるなどセキュリティやLinuxとの統合をより進めたプロダクトとなっています。

Dockerとセキュリティ

Dockerにおいてセキュリティは重要なキーワードです。それは原理的にOSを共有しているためVMWareなどのH/W仮想化に比べると隔離レベルが低いからです。これは社内などすべてのコンテナが自社のもののケースでは問題になりにくいですが、クラウドベンダーなど複数のユーザのワークロードを混在して実行させる環境では大きな問題になることもあり得ます。

そのためいくつかのセキュリティのための実装があります。特に有名なのがAmazonのFirecrackerとGoogleのgVisorです。これらはAWSやGCPなどで既に利用されています。

Firecrackerは分かりやすくmicro-VMとも呼ばれる非常に小さなVMを起動しその上でコンテナを動かすのに必要最小限のLinuxを動かす方式です。micro-VMは1秒以下の時間で起動する非常に高速な実装を指します。WindowsのWSL2とかもそうですね。VMなので隔離レベルは高くシンプルですし、それでいて起動が高速なのは素晴らしいですよね。

gVisorはユーザ空間で動くアプリケーションとして実装されたカーネルです。コンテナ側から見えるカーネルを直接ホストのカーネルではなくコンテナ毎に専用のカーネルで実行します。最終的にはこのgVisorのカーネル自体はホストのカーネルの上で動くことになりますが直接コンテナから見えませんし強力なセキュリティとパフォーマンスを両立できます。一方でmicro-VMと比べると仕組みが複雑になりがちかと思います。

Dockerとセキュリティ、というと脆弱性スキャンの話も話題になりがちですが、今回はコンテナに特化した事情というこでカーネル周りの話をもってきました。

まとめ

なるべく技術的な詳細には踏み込まずにかつ使いたい理由/流行っている理由をまとめてみました。とりあえず一通り読むと以下のイメージが大分つきやすくなったんじゃないでしょうか?

  • Dockerは仮想化であるコンテナの実装の一種
  • ただし、広義のDockerはOCI系コンテナの総称
  • アプリの配布と実行の仕組みと思えばOK
  • コンテナによりIaCや一貫したデプロイ、H/Wの効率的な利用がしやすくなる

OCIとかRunCとかCRIとか技術的な背景が知りたいんだー! って人には物足りないと思いますし、コマンドでの操作とか実務的な内容は別でやる必要がありますけど 「そもそも何?」 って段階だと、このくらいがちょうど良いんじゃないかな、と。

ちなみにこれはかなりジェネラルな説明で相手の知識によってもちろんカスタマイズします。特にJava EEとかに慣れてる人だったら「war=docker, k8s=weblogic/glassfishととりあえず思ってください」という事が多いかな。

それではHappy Hacking!