Dockerを使ってみよう！

---
この記事は[リンク情報システム](https://www.lis.co.jp/)の「2021新春アドベントカレンダー TechConnect」のリレー記事です。
engineer.hanzomon のグループメンバによってリレーされます。
（リンク情報システムのFacebookは[こちら](https://ja-jp.facebook.com/lis.co.jp/)から）

2021新春アドベントカレンダー Tech Connect インデックスは[こちら](https://qiita.com/dgkz/items/eabde6515457d8acec9b)

21日の記事を担当しますshigeta(hs-lis)です。よろしくお願い致します。

---------


# はじめに
ちょっと前にDockerについて勉強したので、
色々学んだ知識や知見をまとめていこうと思います。
以下技術者向けに、Dockerとは何か、Dockerの使い方についてざっくり書いていきます。

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

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

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

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

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

## 仮想マシンとの違いは？
仮想環境といえば、他にはVirtualBox,VMWareなどの仮想マシンなどがありますが、
コンテナはそれとはシステム的な部分で違います。違う部分は以下の２つ。

* 仮想ハードウェアを持たない
* ゲストOSを持たない

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

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

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

コンテナ仮想化はそれらをコンテナエンジンに置き換えることで問題をクリアしています。
（正確にはゲストOSと仮想ハードウェアの橋渡し役であるハイパーバイザーを置き換えている）
このコンテナエンジンこそがまさに、これから紹介するDockerになる訳です。

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

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

# 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とかWEBやDBなどのシステムに関して様々な種類のものが存在します。存在する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をした後にainstallなどを実行すると良い。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とかもそのうち触ってみたいですね...その際はまた記事書きます