Dockerとはどういったものなのか、めちゃくちゃ丁寧に説明してみる

### お詫びと訂正
***おまけ２*** の内容に関して誤りの情報を記載していたので、修正いたしました。(2020/6/6 22:55)

またコメントで大変丁寧にご指摘&アドバイスをくださった@souchi00さん、ありがとうございます🙇‍♂️

### 追記(コンテナについて)(2020/6/8 11:10)
@inductorさんがコンテナに関するわかりやすい資料を紹介してくださいました！
https://www.slideshare.net/zembutsu/what-isdockerdoing

コンテナについてより詳細を知りたい方は是非ご覧になってください

###　追追記(2020/6/8 12:30)
本記事で何点か誤った表記があったので、コメントで指摘くださった方々のアドバイスの元修正しております。(本文中でも修正しておりますが、コメント欄を見ていただければより詳細がわかると思います。)
以後このようなことが内容十分に気をつけるようにいたします。
誤った情報を記述してしまい申し訳ございませんでした。

### この記事はこんな人向け
- プログラミング初学者
- Dockerがどんな技術かよくわかってない人
- フロントエンドとバックエンドの違いがわからない人
- ウェブサイトの開発がどういうものかわかっていない人

### はじめに
最近プログラミングを学び始めたばかりの人向けにtwitterで情報発信を始めたのですが、***よく聞く単語だけどDockerって何？ 何ができるの？*** という声を目にする機会が多かったので, 今回は Docker について紹介します。

いきなりDockerの仕組みとメリットを書いても初学者の方にはピンとこない気がしたので、初学者の多くの方が目標としているウェブサイトの作成工程の中で、Dockerが大活躍する***サーバーのデプロイ***に絡めて解説していきたいと思います。

そもそも***サーバーとはなにか***、***フロントエンドとバックエンドとは***、***デプロイとは***
と言った関連ワードに関しても解説しており、たくさん寄り道をした分ボリュームのある記事になってしまいましたが、学びの多いものになっていると思いますので最後までお付き合いいただけると幸いです🙏

(この記事はdockerがどういったものか、概要をざっくり説明するもので、***command等はほとんど出てこない***ので悪しからず)

###サーバーとは
ウェブサイトを自分で作れるようになってエンジニアとして働きたい！　フリーランスで稼ぎたい! という方は多いかと思いますが、そもそもウェブサイトとはどういったものでしょう？(何を作ればいい？)

皆さんなんとなくリンクをクリックしたりブラウザ(ChromeやIE)の検索バーにurlを入力してウェブサイトを見ていますが、その裏では一体何が起きているのでしょうか?

urlとはUniform Resource Locatorの略で、文字通りリソースの場所を表すものです。

ここで言う***リソース***とは, htmlやcss, javascript, webページ中で表示したい画像など、***webページを表示するのに必要な材料***を表します。

皆さんがブラウザでurlを入力すると, ブラウザはその "urlが表す場所にあるリソースをください" というリクエストをインターネットに送りだします。

この***リクエストを受け取って必要なリソースを返送してあげるのがサーバー***です。

返送されてきたリソース(***レスポンス***と呼ばれます)はブラウザによって解釈され、みなさんが今見ているような画面として出力されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477617/6066a99d-ffcf-d5b5-8222-dd58fb5a41b5.png)

ちなみにサーバーの仕事は、リクエストに応じて***ただ事前に用意してあったリソースを返すだけではありません***。

例えばユーザーに似ている芸能人を機械学習で算出して教えてあげるサイトを作ったとすると、ユーザーが画像をアップロードするまで似ている芸能人を計算することはできません。

なので、リクエストが来てからその場で計算したり、機械学習用の別のサーバーに計算を依頼してユーザーに返す画像を決定します。

またデータベースと呼ばれるデータを管理するためのサーバーに、ユーザーの情報を登録してもらったり、レスポンスに必要なユーザー情報を引き出すのもサーバーが行う操作です。

このようにサーバーの役割は非常に広範囲に渡り、また役割ごとに異なる名称で呼ばれることが多いです。（データベースや機械学習サーバーなど)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477617/0c0e1738-dcd7-8509-696c-c87a29705445.png)

話が少し脱線してしまいましたが、ウェブサイトとは

***ページを表示するために必要なリソース群と、ユーザーからのリクエストに応じて適切なリソースを返してくれるサーバーのセット***

と言えそうです。

ウェブサイトのイメージ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477617/33e56a41-bb6d-1455-e36c-2cbf91c18823.png)


###フロントエンドとバックエンド
webサイトを作るときは、***各ページを表示するのに必要なリソース群***と***リクエストに応じて適切なリソースを返すサーバー***の両方を用意する必要があることがわかったと思います。

これらは分離して開発可能で、一般的にhtmlやjavascriptなどのwebページの表示に必要なリソースを開発する仕事(スマホアプリのUI開発なども)を***フロントエンド開発***、サーバープログラムを開発する仕事を***バックエンド開発***と呼びます。

ユーザーから見た時にユーザーにより近いところ(ユーザーの端末上)で動くので、htmlやjavascriptなどはフロントエンド、その対比でサーバーはバックエンド、と覚えるといいでしょう。

主にユーザーが目にするUIを扱いたい方はフロントエンドを、それ以外の裏側の仕組みを扱いたい方はバックエンドを勉強するといいでしょう。(それぞれどんな言語があるのかについては割愛します。)

###デプロイ
さて、めでたく皆さんのPCの中でサーバープログラムとユーザーに渡すためのリソースが用意できました、サイトの完成です!

が、せっかく作ったサイトもこのままでは他の人に見てもらえません。

***他の人がアクセスできる場所(主にインターネット上)に公開する必要があるのです。***
ユーザーがサイトやアプリなどを利用可能な状態にする作業のことを、***デプロイ***と呼びます。

***Dockerはこのデプロイ作業時に発生しうる様々な問題点を解消してくれるのです。***

ここからはデプロイの歴史を見ていきます。

###デプロイの歴史 (@souchi00さんにコメントでご教授いただき加筆いたしました。 2020/06/06/23:30)
ひと昔前までは([こちらの記事によると](https://mag.executive.itmedia.co.jp/executive/articles/0801/09/news036.html)1990年代頃までは)サーバーと言うと大型マシン一台がすべてのリクエストをさばいていました。(そもそもこの頃はインターネットは普及しておらず、社内システムがメイン)
しかし、これだとPCメーカーによる囲い込み(簡単に違うPCメーカーに乗り換えられなくなる)やその結果の高コスト、柔軟性や拡張性欠如の問題がありました。

その後技術が進歩し、小型PCの性能がup & 価格も安くなってくると、小型PCをつなぎ合わせて、システムや機能を分散配置する構築手法が広がりました。

この大型機から小型機を組み合わせた分散システムへ移行することはダウンサイジングと呼ばれ、当時のトレンドとなりました。
これ以降は、小型PCに適材適所に環境を構築することが主流となっていきます。

しかしこの変化は新たな別の問題を発生させました。

###管理の複雑化・リソースの無駄遣い
まずサーバーを構成するPCが増えると管理と運用が大変なのは、想像に難くないですよね。
たとえばもし100台の小型PCを組み合わせる運用を考えた場合、当然***100台分の環境構築や設定が必要***になります。

一度設定してしまえば終わりというものでも有りません。プログラムのアップデートが入るとまた100台分プログラムをインストールし直す必要があります。(そして数が増えるほどミスが発生し、アップデートし忘れたサーバーなどが出てくる)
これは大変な手間ですね。

また、この複数のPCが常に100%稼働しているわけではなく、***時間によっては遊んでいる(リクエストが全然来ない)PCが沢山出てきます***。

「暇なら一台のPCでたくさんのサーバープログラムを動かせばいいじゃん」とお思いになる方もいらっしゃると思います。

もちろんひとつのPCで複数のプログラムを同時に実行することは可能なのですが、いくつか懸念点が出てきます。

まずリソース(CPUやメモリなど)を共有するため、***単純に各プログラムの処理速度が遅くなります。***

沢山の人が同じ場所に詰めかけると渋滞が起こりますよね？ 
あれと同じでメモリなどに複数のプログラムが同時にアクセスしようとすると、待ち時間が発生するようになります。

またセキュリティや可用性(サーバーが安定して稼働し続ける能力のこと)に関しても気になる点があります。

例えばある1台のPCが10個のサーバープログラムを動かしていたとします。
仮にこのPCが悪意のあるユーザーから攻撃を受け、PCの管理者権限を乗っ取られてしまった場合、この10個のサーバープログラムでやり取りされたすべての情報が攻撃者に渡ってしまうことになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477617/fc257903-119a-007f-a3f5-03830a0d116a.png)

1つのサーバープログラムで100人分のユーザーのリクエストをさばいていた場合、100×10=1000人分の個人情報が流出することになります。

もしこれを10台のPCで分散して実行していたなら100*1=100人分の個人情報の流出ですむわけです。(十分アウトですが)

また攻撃だけではなく、プログラムのバグや大量のリクエストによる処理落ちでPCがフリーズした際、同じPCで10個のプログラムを実行しているとその全てが停止してしまうことになります。

###実行環境の多様化
またPCの数が増えるにつれ、システムを構成する環境が複雑かつ多様化しました。
これにより各PCの環境の違いにも気を使う必要が出てきます。

開発時に用いたPC上では正しく動くプログラムでも、実行環境(OSやライブラリのバージョン)が異なるPC上では動かないことが多々あります。

オペレーティング・システム(Operation System、OS) ・・・ アプリやデバイスを動作させるための基本となるソフトウェアのこと。
ソフトウェア(みなさんが開発してるプログラムなど)とハードウェア(CPUやメモリなどの器械)の連携を司る中枢的な役割を果たす。
プログラムを実行する際、プログラムの実行に必要なCPUやRAMメモリなどがOSによって確保、割り当てられる。
OSの助けがないとプログラムはほぼ何もできない。(OSの機能を使わないとプログラムは標準出力機構にアクセスできないので、Hello Worldを画面に出力することさえできません!)
有名どころを上げるとwindowsやmacOS、Linuxなどが該当する。

実行環境 ・・・ 作成したプログラム、アプリケーションを実行するための環境のこと。
OSの種類や、ライブラリのversion、環境変数などが該当する。
上述したとおり実行環境が異なると、同じプログラムでも動かなくなったりする。

プログラムはOSの機能に依存しているのにそのOSの種類が開発時と異なったら、動かなくなるのはなんとなく想像つきますよね。

異なる環境でプログラムを動かそうとすると...
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477617/69711570-5fe2-0898-b4fc-be4e05983d53.png)

なので、プログラムの開発時に使用するPCの環境はサーバーとして使用するPCの環境に合わせる必要がありました。

###仮想化とは
これらの悩みを解決してくれるのが、***仮想化***という技術です。

仮想化とは、ソフトウェアによって複数のハードウェアを統合し、自由なスペックでハードウェアを再現する技術で、限られた数量の物理リソース（CPU、メモリ、ストレージ、ネットワーク等）を、実際の数量以上のリソース（論理リソース）が稼働しているかのように見せかけることです。

[引用元](https://www.fsi.co.jp/solution/vmware/knowledge/virtualization.html)

- CPU(Central Processing Unit) ・・・ コンピュータの制御や演算や情報転送をつかさどる中央演算処理装置。簡単に言うとCPUの性能によってどのくらい早く計算できるか(プログラムを動かすことができるか)を表す。CPUの性能指標について詳しくは[こちら](https://www.google.com/search?ei=nlTYXtTSNofY0gS3w4LwDQ&q=cPU+%E6%8C%87%E6%A8%99&oq=cPU+%E6%8C%87%E6%A8%99&gs_lcp=CgZwc3ktYWIQAzIHCAAQgwEQBDIECAAQBDIECAAQBDIECAAQBDIECAAQBDIECAAQBDIECAAQBDIECAAQBDoHCAAQsQMQQzoECAAQQzoFCAAQsQM6AggAOgUIABCDAToGCAAQBBAeUP8IWIwWYPAYaAFwAHgBgAGsA4gBsReSAQkwLjIuNC4yLjKYAQCgAQGqAQdnd3Mtd2l6&sclient=psy-ab&ved=0ahUKEwjUmdaJh-fpAhUHrJQKHbehAN4Q4dUDCAw&uact=5)

- メモリ(RAM、Random Access Memory) ・・・ データやプログラムを一時的に記憶する部品で、高速に読み書きできます。 プログラムやアプリケーションはこのメモリの上に読み込まれ、実行されます。簡単に言うとメモリが大きいほど同時にたくさんのアプリケーションを動かせます。(動画編集しながら、powerpointいじったり、ネットサーフィンしたり...etc)
***PCの電源を切るとメモリ(RAM)上に保持されていたデータは全て消えます。***

- ストレージ ・・・ データやプログラムを永続的に記憶する部品で、ストレージが大きいほどたくさんのデータを記憶することができます。(写真を100万枚保存できる、音楽を1万曲保存できる、など)使用されていないデータやアプリケーションは普段ストレージに保存されており、使用するとなった時にメモリ(RAM)にコピーされアプリケーションが実行されます。ストレージの種類としては[HDDやSSD](https://www.pro.logitec.co.jp/about_hdd/hddssd/0718/)などがあります。
***PCの電源を切ってもストレージに保持されているデータは消えません。***

実際には１つしかないのに複数あるように見せかける ということで、仮想化技術と呼ばれているのですね。(蛇足ですがメモリやストレージは一つのPCに複数搭載することもできます。複数搭載されているメモリやストレージを仮想化技術によって１つのメモリやストレージとして扱うこともできます。)

###仮想マシン

最初に流行となった仮想化技術は***仮想マシン***というものでした。
これはPC上に仮想的なPCを立ち上げるという技術です。

例えば仮想マシンを用いると、1台しかPCが存在しないにも関わらずあたかも4つのPCが存在しているように振る舞うことができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477617/8d8f3fc9-c150-0498-8a8e-f3645dc28598.png)

また、この際に***仮想マシンが使用するOSは自由に選択することができます***。
macOSの上でwindowsを動かす といったことができるのです。


macOS上で動くwindows
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477617/8fc22d63-511e-d95e-6a0b-c94e59462195.png)

これにより開発用のPCとサーバー用のPCが別の環境であったとしても、仮想マシンを利用することで環境を揃えるということが可能になりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477617/176dd900-4754-3c82-26c3-e42be340beb1.png)

***※図では、開発用PCの環境に、サーバー側が仮想化技術を用いて環境を合わせるように書いておりますが、実際は開発用のPC上でサーバーの環境を作り、その上でプログラムを開発することがほとんどです。***
***また、皆様に馴染み深いだろうということでwindows10やmacOSを例に上げていますが、これらはサーバー用のOSではなく、サーバーには専用のOS(Windows Server, macOs Server, Linuxディストリビューションなど)が用意されています(Linuxディストリビューションが人気です)***
(@souchi00さん、ご指摘ありがとうございました)

また仮想化によって生成された仮想マシンは安全に分離されているので、1つの仮想マシンに異常が発生しても他の仮想マシンに影響が出るリスクを軽減できます。(実態のPCに異常が発生すると全ての仮想マシンに影響が出てしまうので、リスクを軽減できる という表現にとどめています。)

これにより、１台のPC上で安全に複数のプログラムを動かすことができるようになったのでした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477617/5ac103b1-c5dd-b201-6422-d7958796c44d.png)
(詳細は割愛しますが、仮想マシンにはホスト型とハイパーバイザー型があり、ハイパーバイザー型のほうがサクサク動くため近年主流になっています。)

###Docker
仮想マシンの登場によってデプロイ時の悩みの多くは解消されました。
しかし仮想マシンにも弱点は有りました。

それは目的のプログラムとは関係のないサービスなども多数動作することになるので、オーバーヘッド(立ち上げにかかる時間など)が大きく、リソースも無駄に多く必要になりがちだという点です。(OSに関するプログラムの分多くのメモリを消費する)

PCを起動すると、デスクトップが表示されるまである程度時間がかかるのはみなさんもよくご存知だと思います。

そこで誕生したのがコンテナ仮想化という技術でした。

コンテナ仮想化という技術はプログラムを開発・実行環境から隔離することで、安全に素早く複数のプログラムを実行することができます。

コンテナ仮想化技術は***OSを新たにインストールすることはしません。***
もともとPCに備わっているOSをそのまま利用します。
OSのもつ複数の機能を組み合わせて作り出した「コンテナ」という隔離空間上でアプリケーションを実行することで、同じOS上でも互いに隔離されたプロセスを作ることができるのです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477617/f7fab02f-b5bf-df90-dab5-b7061fab5fd8.png)

***※おまけ２の方に記載しましたが、もしもともと備わっているOSとアプリケーションを実行したい環境のOSが異なる場合(たとえばmacOS上でLinux環境を用意したい場合)、ハイパーバイザ型の仮想化技術でLinux環境の仮想マシンをたちあげています。(図でいうとOSとDOcker Engineの間にハイパーバイザが挟まります)
@okazu_dmさん、ご指摘ありがとうございました。(2020 6/7 17:30)***

[参考記事](https://qiita.com/yuki_ycino/items/cb21cf91a39ddd61f484)

だからコンテナ仮想化と呼ばれるわけですね。

新たにOSを立ち上げる必要がないため、仮想マシンに比べてアプリケーション(プログラム)の立ち上げが早く、またメモリも節約することができます。

Dockerはこの***コンテナ仮想化技術を簡単に扱えるようにしたソフトウェア***です。
本来コンテナを作成するのには複数のOSコマンドを実行しなければならないところ、Dockerコマンドを一発叩くだけでコンテナが作成できるようになっているのです。
ちなみにコンテナ仮想化技術はDocker固有のものでなく、他にもコンテナ仮想化が簡単に行えるようにと開発されたソフトウェアは多数存在します。
それぞれのソフトウェアはコンテナ仮想化技術に加えて各々特徴をもっています。
Dockerならではの特徴としてはDockerfileやDockerhubといったものが挙げられるでしょう。

***Docker != コンテナ仮想化技術 なのでご注意ください!***(自分は長らく勘違いしておりました...)

###Dockerfile
Dockerでは***Dockerfile***というファイルを元にコンテナを生成(アプリを実行)します。
Dockerfileは環境構築のレシピのようなもので、Dockerはレシピ通りに上から順番にコマンドを実行し、コンテナ内の環境を整えていきます。

Dockerfileの例
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/477617/96b53f9e-261d-bb8d-ec6c-efca1b83bfe1.png)

Dockerfileで環境を管理することで以下のようなメリットがえられます。

- 簡単に何度でもコンテナを生成できる
一度Dockerfileを作成してしまえば、コマンド一発で何度でもコンテナを生成できます。
なので例えばサーバーが急に100台必要になっても、リソース(CPUやメモリなど)さえあれば簡単に用意することができるのです。

- コンテナを持ち運びできる
またDockerfileはただのソースコードなので、簡単に別のPCに移植したり第三者と共有することができます。
Dockerfileを共有するためのDockerhubといったサービスも有り、手軽に他の方が生成したコンテナを利用することができます。
例えば会社に新入社員が入ってきた際の環境構築も、Dockerfileを共有するだけで済むのです。

- アプリケーションのバージョン管理が楽になる
Dockerfileはただのソースコードなので、git等で差分管理ができます。
なので例えば新しくリリースしたバージョンが何かしらの理由でバグっていた場合にも、確実に動いていた前のバージョンに戻すことができます。(プログラムのみをgitで管理していると、ライブラリのバージョンと言った環境依存のバグを解消できません。)

このような利便性からDockerは現在人気を博しているわけですね。

###まとめ
Dockerとは
他に実行しているプログラムから影響を受けない隔離された環境上で新たなプログラムを実行することを可能にする、コンテナ型の仮想環境を作成、配布、実行するためのソフトウェアです。
[引用](https://knowledge.sakura.ad.jp/13265/)

Dockerを使うことで次のようなメリットがえられる。
- １台のPC上で簡単かつ安全に独立したサーバーを複数動かすことができる。
- アプリケーションのバージョン管理が楽になる(アップデートによってアプリケーションが壊れても、すぐに動いてたバージョンに戻せる)
- 相手がDockerを使える環境であれば、確実に動かせるソフトウェアを作ることができる(環境の違いを吸収できる)

長らくお付き合いいただきありがとうございました。
なんとなくでもDockerがどういったものか理解していただけたでしょうか？

DockerはGitと並んでエンジニアにとっては習得必須の技術になりつつあります。
ぜひこの機会に腰を据えて勉強してみてください！

※誤っている箇所があればコメント等でご指摘いただけると🙇‍♂️
[twitter](https://twitter.com/buebue4632)でも歓迎です！

###おまけ1
環境の違いと言うと、フロントエンドエンジニアの方ならIEとChrome などと言ったブラウザの違いに苦しめらている方も多いと思います。
Dockerでは残念ながらこれらの違いを吸収することはできません。

DockerがカバーしてくれるのはOSや環境変数といったものの違いであるのに対して、IEとChromeの違いはブラウザの持つレンダリングエンジンやJavascriptエンジンの違いであるからです。

これらの違いを吸収してくれるソフトウェアが出たら嬉しいですね。

###おまけ2 
Dockerはもともと持っているOSを使い回すと説明した時に、
鋭い方は「あれ、これじゃOSの違いを吸収できなくね？」と思ったかもしれません。

***すいません、こちら完全に嘘を教えておりました***🙇‍♂️
***旧説明文***
~~現在代表的なOSというと大体windows、Linux、macOSの３種類が挙げらます。~~
~~この内macOSとLinuxはOSのコアの部分が非常に似ており(もしかしたら完全に同じかも)、macOSで動くものはだいたいLinux上でも動くのです。(逆もしかり)~~

***macOSとLinuxは全くの別物であり、似てもいませんでした***。(嘘を教えて申し訳ないです🙇‍♂️)

訂正した説明文
実はコンテナという機能はLinuxOSの機能であり、以前はDockerはLinuxでしか使えませんでした。
その後macOSやwindowsでもDockerが使えるようにと「Docker Desktop for Mac」や「Docker for Windows」といったソフトウェアが登場します。
これらは環境がLinuxであるコンテナを実行する際、裏側でハイパーバイザー型の仮想マシン技術を使ってLinux環境の仮想マシンを立ち上げ、その上でアプリケーションを実行しています。
[参考リンク](https://qiita.com/na-777/items/bbc07a40c9802a990abd)