（僕にとって）はじめてのDocker

## 今回書かないこと

* NW周り（コンテナ間通信とか外部との通信とか）
  - K8SのNW周りとあわせて別途調査したい

## Dockerイメージとは

* ReadOnlyのイメージレイヤーの積層体
* Dockerイメージの概念（[超入門Docker](http://www.atmarkit.co.jp/ait/articles/1701/30/news037.html) から引用）

![01.png](https://qiita-image-store.s3.amazonaws.com/0/33137/7a5827db-12d8-c9ef-5599-5795c44547e4.png)


* 例）APサーバのDockerイメージ
  - レイヤー４：・・・
  - レイヤー３：Tomcat8
  - レイヤー２：openjdk8
  - レイヤー１：OS（CentOS7）←DockerHubから取得したベースイメージ
* PJでは、ベースイメージにカスタマイズをくわえたイメージを自分で作って使う
  - 大抵はDockerHubに公開されているイメージ（CentOS等）をベースイメージにスタートする？
* イメージは何度も作成可能
  - 変更を確定→ReadOnlyのレイヤーを積み上げ→イメージを作成するコマンド（docker commit）
  - 繰り返していくうちに変更のレイヤーが積み重なっていく
* 変更に失敗しても、最新のレイヤーを捨てて過去のレイヤーに戻れる


## Dockerコンテナとは

* Linuxのプロセス単位で動作するアプリケーション実行環境

Dockerエンジンのインストール例

```bash

sudo yum -y install docker-io
```

* 以下、動作環境イメージ（[超入門Docker](http://www.atmarkit.co.jp/ait/articles/1701/30/news037.html) から引用）

![02.png](https://qiita-image-store.s3.amazonaws.com/0/33137/7a159c79-8c7c-f328-11ab-0791bba5198f.png)


* Dockerコンテナは複数起動可能で、各コンテナ同士は不可視
* Dockerエンジンの動くホストは、Linuxであれば何でも（物理でもVMでもEC2でも）可


## Dockerコンテナの起動

* Dockerイメージを使って起動する
  - Dockerイメージがローカル（DockerエンジンのあるLinuxホスト）にないとき
     - Dockerレジストリからダウンロードする
     - デフォルトのDockerレジストリはDockerHub
  - 初回起動時は、Dockerコンテナ作成と起動（docker run）
     - Dockerコンテナ作成時に、新たに書き込み可能なイメージレイヤーが用意される
  - 二回目以降の起動は、既存のコンテナIDもしくはコンテナ名で起動できる（docker start）
* Dockerコンテナ起動中に加えられた変更は書き込み可能レイヤーに反映される
* Dockerコンテナを停止（docker stop）しても変更内容は残る
* Dockerコンテナを破棄（docker rm）してしまうと変更内容も消滅する
  - なのでログ等はコンテナ外に逃がすようにする
  - 設定変更を保存したい場合はイメージ化する（docker commit）


## Dockerイメージの作り方

* **手動**
  - ベースイメージからDockerコンテナを起動（docker run/docker start）
  - コンテナにターミナルでログインして変更を実施
  - ターミナルをexit
  - 変更を確定し、新しいDockerイメージ作成（docker commit）
* **自動**
  - 使用するベースイメージ、起動後のコンテナ内で実行するコマンド等をDockerfileに記述
  - Dockerイメージ作成（docker build）


以下、[効率的に安全な Dockerfile を作るには](https://qiita.com/pottava/items/452bf80e334bc1fee69a) から引用

```bash
FROM centos:6    #ベースイメージを指定
RUN set -x && \  #コンテナ起動後に実行するコマンドを記述
    yum install -y epel-release && \
    yum install -y nginx && \
    sed -i -e "s/index  index.html index.htm/proxy_pass http:\/\/ip-api.com\/json/" \
        /etc/nginx/conf.d/default.conf && \
    ln -sf /dev/stdout /var/log/nginx/access.log && \
    ln -sf /dev/stderr /var/log/nginx/error.log
EXPOSE 80  #コンテナがリッスンするポートを記述
CMD ["nginx", "-g", "daemon off;"]
```

* **コツ**
  - まず手動で手さぐり的に変更を実施・確認し、うまくいったらDockerfile作って自動化、が確実


## 複数のDockerコンテナ起動

* Docker Composeを使う
  - docker build（+Dockerfile）、docker runをまとめてやるようなツール
  - コンテナの起動・停止の順序設定ができる
  - 各イメージ用のDockerfileは用意する必要がある
  - 設定ファイル（yaml）を書き、コマンド実行（docker-compose up）
* より高度な制御をしたければオーケストレーションツール（K8S等）を使うイメージ？


## DevOps、CI/CD観点でのDockerの利点

* DockerイメージはReadOnlyなのでimmutability（不変性）が保証される
  - 同一の設定済イメージを各環境に配布する（コンテナ単位ではない）
* とにかく軽量（物理/VMとの比較）
  - 起動が速い、フットプリントが小さい、イメージサイズが小さいので可搬性高い
  - ベースイメージをより軽量なものにすればさらに軽量化が図れる
     - Alpine Linux等


## 課題

* **環境差分の注入方法**
  - 環境差分なしのイメージをベースイメージに各環境ごとのDockerイメージを作る
     - Dockerfileに環境差分設定を記述しておく
  - 環境差分ファイルをホスト側に置きDockerコンテナにマウントする
  - 別の仕組み（SpringConfig等）
* **アプリのデプロイ方法**
  - jar/warはイメージに焼かずにDockerコンテナにマウントする
  - アプリのビルドごとにイメージ作成をしなくてすむ


## 参考

* [超入門Docker](http://www.atmarkit.co.jp/ait/articles/1701/30/news037.html)
* [Docker ハンズオン - 基本コマンド編](https://qiita.com/hihihiroro/items/6dda871dc2566801a6da)
* [Dockerイメージの理解とコンテナのライフサイクル](https://www.slideshare.net/zembutsu/docker-images-containers-and-lifecycle0)
* [Dockerのライフサイクルがよくわからなかった](https://qiita.com/chroju/items/ce9cae248cc016745c66)
* [効率的に安全な Dockerfile を作るには](https://qiita.com/pottava/items/452bf80e334bc1fee69a)
* [Docker compose ことはじめハンズオン](https://qiita.com/TsutomuNakamura/items/7e90e5efb36601c5bc8a)