仮想化/コンテナ/Dockerについて

チーム勉強会用記事です。

# Dockerとは

Dockerとは**コンテナ型の仮想化環境を作成、配布、実行するためのオープンソースソフトウェアです**。
では**仮想化**、**コンテナ**とは?

* 仮想化とは?
* コンテナとは?

## 仮想化とは

仮想化とは、**1つのサーバ上で複数のサーバ環境を実行する技術**です。

### なぜ仮想化が必要なのか

サーバの管理、維持を簡単にするため

**仮想化前**
![仮想化前.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2703812/8baa5c87-1a77-62ba-ba3c-0f41da0f19bf.png)

* **サーバごとにPCを用意する必要がある。**
* サーバの管理、移動、廃棄が大変
* 一度PCの構成が決定すると簡単にスペックを変更できない

**仮想化後**
![仮想化後.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2703812/df32d2be-19e3-43da-8a79-88951cfcfe9e.png)

* **サーバごとにPCを用意しなくてよい**
* 実体が存在しないので複製/移動/廃棄が簡単にできる
* 必要に応じでスペックを変更できる。

## 仮想化技術の比較

仮想化の技術には主に **「VM(仮想化マシン)による仮想化」** と **「コンテナによる仮想化」** があります。

**VM(仮想化マシン)による仮想化**

![仮想化2png.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2703812/c8ef7089-ffcd-af77-390f-598b6127eee7.png)

* ハードウェア層まで含めたマシン全体を仮想化
* **他のアプリケーションの影響を受けない**
* ゲストOSを起動して実行するので、時間がかかる
* 複数のゲストOSが立つため、サーバ資源(メモリ、CPU)を食う

**コンテナによる仮想化**

![仮想化3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2703812/43aebadc-dfb9-3732-5aea-9ff50baf1e00.png)

* ソフトウェア層を仮想化
* namespace(名前空間)によってプロセスを分離
* **他のアプリケーションの影響を受けない**
* OSの立ち上げが不要のため、起動が早い
* 完全な隔離ができない(OS設定の分離) 
* **Dockerではこちらを使用**

## (補足)コンテナでの仮想化を実現する技術について

コンテナではプロセスやリソースの分路を行うためにLinuxカーネルの機能を使用している

* namespaces : プロセス、ホスト名、ネットワークなどの分離
* cgroups : メモリ、CPUなどの分離

![コンテナにおけるカーネルの役割](https://camo.qiitausercontent.com/dea22aa99378735dea71b5414553b83bac572efd/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3536393035342f62346238383831382d323463302d316430612d323466332d6261643331326235323863302e706e67)
[引用](https://qiita.com/suzuki0430/items/b2527cfba7602f9fe3bc)

### namespaceとは

Linuxカーネルが管理するプロセスを分離して扱うための機能でうｓ。
コンテナごとにnamespaceを分離することで、他のコンテナのプロセスに影響を与えることなく、プロセスが実行ができます。

[こちらの記事](https://qiita.com/Kiyo_Karl2/items/290c78c08b04dffeee58#%E3%81%BE%E3%81%A8%E3%82%81)が分かりやすかったので引用します。

>Dockerなどのコンテナ技術はVirtualBoxなどの仮想マシンと違って、全てのコンテナがひとつのホストOSのカーネルを共有します。
>なので何も考えずに同時に2つのアプリ(プロセス)、例えばLaravelとMySQLを同じOS上で動かしたら同じパスやファイル名を取れないですし、同じUIDを取ることも出来ません。これで開発するというのはあまり現実的ではないですよね。
>
>そのため、Dockerのコンテナ技術を実現するためにはプロセスID、ファイルシステム、UIDやGID、ネットワークなどもろもろの名前空間を分離させる必要があります。
それに用いられるのが上記で引用したLinuxカーネルのnamespaceという機能です。(これは2006年に実装されたもので古くからある技術です。)
>
>このnamespace機能を使うことでプロセス空間やファイルシステムを一つのOSの中で分離できるのため、namespaceで区切られた部分は互いに影響を与えることなく全く異なるOSのように振る舞うことが可能です。
>
>そしてこの分離したnamespaceを持たせることによってほかのプロセスと実行環境が分かれているプロセスこそ、私たちが普段使コンテナと呼んでいるものの正体です。

### cgroupsとは

CPUやメモリなどの物理的なリソースへのアクセスを制御するための仕組み。
コンテナ内からアクセス可能なシステムのリソース（CPUやメモリなど）を厳密に制限することが可能。

## Dockerの特徴

Dockerとは**コンテナ型の仮想化環境を作成、配布、実行するためのオープンソースソフトウェアです**。

![Dockerのアーキテクチャ](https://learn.microsoft.com/ja-jp/dotnet/architecture/microservices/container-docker-introduction/media/docker-containers-images-registries/taxonomy-of-docker-terms-and-concepts.png)
[引用](https://learn.microsoft.com/ja-jp/dotnet/architecture/microservices/container-docker-introduction/docker-containers-images-registries)

### CLIを使った簡単操作
コマンドラインを使うことで簡単にコンテナを管理/操作できます。

```cmd
$ # 起動しているコンテナ一覧を取得
$ docker ps
$ # dockerイメージ一覧を取得
$ docker images
$ # dockerコンテナの中に入る
$ docker exec -it test-server bin/sh
```

### Dockerイメージによってコンテナが作成が容易
Dockerイメージとはコンテナを作成するために必要なOS/コマンド/メタデータをまとめたものです。
Dockerイメージを使用することで簡単にコンテナを作成することが出来ます。

```cmd
$ # dockerImangeがあればコマンド一つでコンテナを作成
$ docker run -it -d -p 8080:80 --name test-server ubuntu
```

### Dockerイメージの共有が簡単
Dockerレジストリと呼ばれるサービスからDockerイメージを配布/共有できます。
デフォルトではDockeHubと呼ばれるDocker社公式の公開レジストリにイメージが共有されます。

```cmd
$ # dockerHubからのdockerイメージの取得
$ docker pull ubuntu
$ # dockerHubへdockerイメージを共有
$ docker push test-api
```

## Dockerでの開発の流れ

公式チュートリアルをもとにnodejsアプリをDockerで起動してみよう
チュートリアルは以下のコマンドからアクセスできる

```cmd
$ # 以下のコマンドを実行してhttp://localhost/tutorial/にアクセス
$ docker run -d -p 80:80 docker/getting-started
```
### 0. nodejsアプリの準備

[公式チュートリアル](http://localhost/tutorial/our-application/#getting-our-app)のapp.zipを適当な場所に解凍する。

### 1.Dockerfileを作成する。

![コンテナ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2703812/acb253fd-3f3b-01bc-1b38-01ca5a10b938.png)

DockerイメージはDockerfileというテキストファイルから生成される。
Dockerfileは **「Dockerイメージ作成に必要なコマンドラインをまとめたもの」** である。
今回は「アプリの実行環境に必要なもの」を記述していく。

**Dockerfile**
```Dockerfile
# Dockerhubからnode:18-alpineというdockerイメージを落としてくる
# nodejsアプリなのでnodeの実行環境が必要
FROM node:18-alpine
# 作業を行うディレクトリを指定
WORKDIR /app
# ローカルのappディレクトリの中身を全てイメージの中にコピーする
COPY . .
# コンテナの中でyarn installコマンドを叩く
# nodeパッケージをインストールする
RUN yarn install --production
# docker実行時にデフォルトで行いたいコマンドを記述
# node ser/index.jsを叩いてアプリを起動する
CMD ["node", "src/index.js"]
```

### 2.DockerImageを作成する。
DockerfileからDockerイメージを作成する。

1. `0. nodejsアプリの準備`で作成したappフォルダの中に先程のDockerfileを配置。
2. Dockerfileが配置されたディレクトリ配下で以下のコマンドを実行

```cmd
$ # Dockerfileからgetting-startedという名前のコンテナを作成
$ docker build -t getting-started .
```

### 3.Dockerコンテナを作成する。
DockerイメージからDockerコンテナを作成する

```cmd
$ # getting-startedというイメージからコンテナを作成して3000番ポートに接続
$ docker run -dp 3000:3000 getting-started
```

成功していれば、[ここ](http://localhost:3000/)からアプリにアクセスできるはずです。

### Dockerを使うと一体何がよいのか

Dockerを使わない場合、自身のローカルPCにnode.js実行環境を作成し
`yarn install`などのコマンドを叩く必要がある。
また、ローカルでappを起動した場合、portの競合/他のアプリからの影響などの外部要因
についても考える必要がある。

Dockerを使ってコンテナでアプリを起動することで、**実行環境や実行コマンド、外部要因を
意識することなく、アプリを開発できる。**

# まとめ

* 仮想化とは**1つのサーバ上で複数のサーバ環境を実行する技術**
* 仮想化サーバでは**それぞれのサーバが独立して起動する(互いに影響を受けない)**
* コンテナとは仮想化技術の1つ
* Dockerを使ってアプリをコンテナ上で起動することで、**実行環境や実行コマンド、外部要因を
意識することなく、アプリを開発できる。**