Dockerについて学んでみよう、使ってみよう

# はじめに
かなり前にDockerについて勉強した時に社内向けに書いた記事ですが、
少しまとめて修正したり、見やすい形に書き直してみました。
以下技術者で初学者向けに、Dockerとは何か、Dockerの使い方についてざっくり書いていきます。

# Dockerとは何ぞ？
>Dockerとは、コンテナ仮想化を用いてアプリケーションを開発・配置・実行するための
オープンソースソフトウェアあるいはオープンプラットフォームである(Wikipediaから引用)

これだけだとよく分からないですね。
といいますか、そもそも、コンテナ仮想化って何なのでしょう？
まずは、コンテナについて軽く説明していきたいと思います。

## コンテナとは？
コンテナは端的に言えば、仮想環境の一種になります。
以下のような特徴があります。

* 特定のアプリケーションを内包する
* 実行環境だけを物理環境(ホストマシン)から独立させる
* コンテナ同士などで連携ができる機能を持つ

すなわち、必要なライブラリ・ソフトウェアだけを持ち、
必要な機能(仮想ネットワーク)を持つ仮想的な実行環境、ということです。
ある特定のアプリを実行するためだけの箱庭的なものと言えるかもしれません。

## 仮想マシンとの違いは？
仮想環境といえば、他にはVirtualBox,VMWareなどの仮想マシン（VM）などがあります。
上記の物はおおまかにホスト型やハイパーバイザー型とか、言ったりしますが、
コンテナはそれらとはシステム的な部分で違います。違う部分は以下の２つ。

* 仮想ハードウェアを持たない
* ゲストOSを持たない（※場合によっては一つ持つ場合もある）

例えば、複数のアプリケーションを一つのOS上でそれぞれ独立して動かす、となった時、
仮想マシンで構成すると、そのアプリケーションの数だけ仮想ハードウェアやゲストOSが必要になります。

仮想メモリがどうとか仮想CPUがどうとか、ゲストOSはwindowsだとかubuntuだとか...
それぞれ適切に設定して、ホストマシンのメモリや演算力を割り振る必要性があります。

しかし、どれも同じ構成でも良ければそこは共通でもいいはずです。
実際それぞれを連携させるのであれば同じOSにするのが運用・開発もやりやすい。
共通にした方が複数のゲストOSや仮想ハードウェアに費やしているメモリや演算力も使わなくて済みます。
複数の仮想OSの起動時間を待つ必要もありません。

コンテナ仮想化は、ハイパーバイザーやゲストOSを、
コンテナエンジンへと概念的に置き換える(*1)ことでそれらの問題をクリアしています。
このコンテナエンジンこそまさに、これから紹介するDockerになる訳です。

その他詳細は下記URLの記事を読んでみてください。
図も交えながらの詳しくわかり易い説明になっています。
https://thinkit.co.jp/article/17301

加えてこちらも
https://qiita.com/ko_koyama_m/items/0e65849b91a329529675

※1.特に非Linux上でのコンテナエンジンの構成には、
　ハイパーバイザーやゲストOS、仮想マシンが関与していることが多いです。
　そのため、概念的な置き換え、と表現しています。

# Dockerを導入する
やっと本題ですが、Dockerを導入していきます。
以下のURLより使うOSにあったDockerをダウンロード・インストールしてください。
https://www.docker.com/products/docker-desktop

インストールが終わり、Dockerが起動したら”docker is running”という通知が出るはずです。
その次にターミナル・PowerShell(以下ターミナルで統一)で以下のコマンドを打ちます。

```
docker --version
docker-compose --version
```
それぞれバージョンが出ることを確認してください。
出ない場合はターミナルを再起動して、もう一度トライしてください。


※なお、Windows版はWSL2の導入が推奨されますので下記の手順を元にインストールしてください。
　　他のプラットフォームはDockerをインストールするだけで大丈夫です。

**1.　PCの仮想マシンプラットフォームの有効化**
Windowsの設定＞アプリ＞関連設定の「プログラムと機能」
「プログラムと機能」
画面左の「Windowsの機能の有効化または無効化」
「仮想マシン プラットフォーム」をOnにして、OKクリック

**2.　WSL2のインストール/アップデート**
①PowerShellを管理者権限で起動
ウィンドウズメニュー(田)を右クリック
「Windows PowerShell(管理者)」
を選択して起動
	
①以下をPowerShellで実行

```
dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart
```
③WSL2 Linuxカーネルの更新
以下から「x64 マシン用の最新の WSL2 Linux カーネル更新プログラム パッケージをダウンロードしてください。」のリンク先をクリック。
https://docs.microsoft.com/ja-jp/windows/wsl/wsl2-kernel
「wsl_update_x64.msi」のダウンロードが終わったら実行。

④Ubuntuのインストール
McrosoftStoreからUbuntu 18.04LTSをインストールする

**3.　Docker Desktopのインストール**
①以下からダウンロード
https://hub.docker.com/editions/community/docker-ce-desktop-windows
「Get Docker Desktop for Windows (stable)」をクリックしてダウンロード後、実行。

# コンテナを使ってみよう
ターミナルを起動してください。
まず、コンテナの元になるdockerイメージをダウンロードします。
以下のコマンドを実行します。

```
docker pull nginx:latest
```

次にコンテナを作成して起動します。

```
docker run -d -p 8080:80  nginx:latest
```

無事作成されればハッシュ値が表示されます。
作成されたらブラウザでURLに"localhost:8080"と打ってください。

Nginxの標準ページが表示されれば成功です！
ちなみにコンテナを止めるたり起動するときは

```terminal
docker start　[表示されたハッシュ値の前から四桁くらい]
docker stop　[表示されたハッシュ値の前から四桁くらい]
```

のコマンドで出来ます。

オプションについて、
-pはコンテナ内のポートとホストのポートをつなげてくれるコマンドです。
-p 8080:80と書けばホストの8080番とコンテナ内の80番をつなげてくれます。
nginxの標準ポートはhttpの80番になっているため、
8080番に接続するとコンテナ内で動くnginxにアクセスできる訳です。
その他にも色々オプションがありますので、こちらを確認してみて下さい。
https://docs.docker.jp/engine/reference/commandline/run.html

なお、dockerイメージファイルは、
mysqlとかnodeとか様々な種類のものが存在します。
存在するdockerイメージは以下のURLで検索出来ます。
https://hub.docker.com/search?q=node&type=image
色々検索してみるのもいいかもしれないですね。

また、上記ではコマンドを使いましたが、
最初に導入したDockerDestopはGUIがありますので、
そちらでコンテナ作成や起動などの管理・操作をした方がやりやすいかもしれません。

# Dockerfileについて
以下のコマンドで上記で紹介したrunなどで使える独自のdockerイメージを作成出来ます。

```terminal
docker build [Dockerfileがあるフォルダ]
```

Dockerfileは以下のような感じで書く

```dockerfile:Dockerfile
#　使うdockerイメージ
FROM node:12.19.0-stretch-slim

# 作業フォルダ（コンテナ内）
WORKDIR /src

# 実行するコマンド
RUN npm install && npm run dev
```

RUNには大体lsなどlinuxの標準的なコマンドが利用できます。

aptやyumなどもイメージに依っては利用できますが、
パッケージ管理システムが持つパッケージリストは最初の時点ではだいたい空です。
なので、updateをした後に各種パッケージのinstallなどを実行すると良いです。

RUNコマンドは連続して実行コマンド毎に書けますが、その分コンテナの容量が大きくなります。
DockerはRUN毎にコンテナの環境を区切っているためです。
なので、コンテナの容量削減のため、
&&を使いコマンドは上記"#実行するコマンド"のように出来る限り連結させるようにしましょう！


他のオプションはこちらを参照
https://docs.docker.jp/engine/reference/builder.html

# docker-compose.ymlについて
docker-composeというものもあります。
こちらはdocker-compose.ymlを利用してコンテナのセットを生成できます。

複数コンテナをまとめて設定できるので便利です。
Dockerで開発環境など整える場合は大体これを利用することになると思います。

実行方法はdocker-compose.ymlファイルを作って、
ターミナルやPowerShellでそのファイルが存在している場所にcdし、以下のコマンドを実行します。

```docker
docker-compose up
```

docker-compose.ymlファイルは以下のような感じで書きます。


```yml:docker-compose.yml
version: "3" # Composeファイルの書式のバージョン
services: # 以下それぞれのコンテナ・サービスの内容
  nginx:　# サービス名
    image: nginx:latest #使用するコンテナのイメージ名(≒docker pull)
    volumes: # 共有フォルダ（ホストのフォルダ：コンテナ内のフォルダ）
      - ./src/web/dist/:/usr/share/nginx/html
    ports: # ポートフォワード(ホストのポート：コンテナ内のポート)
      - "80:80"
  nuxt:
    build: ./ #指定パス内のDockerfileをcompose up時に実行する
    volumes:
      - ./src/web/:/src
    ports:
      - "3000:3000"
    command: "npm run dev" # 起動時に実行するコマンド
```
記述されている内容それぞれについて補足します。

services以下は実際に起動するコンテナ内容の記述。
上記ではnuxtとnginxの２つのコンテナが存在します。
(サービス名前は任意のアルファベットなら問題ない)
分かりにくいとは思いますが、サービス名＝コンテナ名ではないです。
コンテナ名は別にあって、
指定していなければサービス名を含んだコンテナ名が自動で生成されます。

**・サービス名以下の内容について**
最初にbuildかimageのどちらかが選択できます。
**image**は指定した名前のdockerイメージをweb上から引っ張って来てコンテナを生成します。

**build**はDockerfileという設定ファイルを元にdockerイメージを作って、それを元にコンテナを生成します。

**volumes**はホスト側(実行しているOS側)のフォルダやファイルをコンテナ内のフォルダやファイルにリンクさせることが出来ます。
例えば、

```yml:docker-compose.yml
volumes:
     - ./src/nginx/nginx.conf:/etc/nginx/nginx.conf
```

こう書くと左の指定パス内にあるnginxの設定ファイルとコンテナ内のnginxの設定ファイルが同期します。
コンテナ内は独立しているのでリアルタイムでの編集が難しくなっていますが、
簡単にホスト内で編集操作をしながらコンテナを動かすことが可能です。

**ports**は外部にコンテナ内のポートを公開します。
exposeというものもありますが、こちらはホスト内限定でポートを公開します。
例えば、上記の場合ホスト側でlocalhost:80でアクセスするとnginxが表示されます。

その他にも様々なオプションがあります。
下記ドキュメントを見ながら試行錯誤してみてください。
https://qiita.com/zembutsu/items/9e9d80e05e36e882caaa
https://docs.docker.com/compose/

# 最後に
date volumeを使ってnodejsやrubyなどのモジュールファイルを分けるだとか、
ネットワークのルーティングの仕方、コンテナの連携の仕方など色々書きたかったんですが、
執筆時間が長くなったので今回はナシにしました。
色々端折りましたがDockerについて、少しでもこの記事で理解できた！となったところがあれば幸いです。

PS.Kurbernetesとかもそのうち触ってみたいですね...その際はまた記事書きます