体系化！Dockerまとめ

</br>
トレンドのコンテナ技術、Dockerについて学習したことを一直線の知識としてわかるように自分なりにまとめてみたので、参考になればぜひ！


</br>
#Dockerそもそも論
「Infrastructure as Code」が詠われる世の中で代表的なトレンドがDockerです。個人的にDockerの凄みは開発環境をコードで定義することによって、環境構築や本番環境へのデプロイをスムーズに行える点にあると感じます。

コンテナ技術はよく仮想環境と比較されることが多いようですが、ウェブサーバー、アプリケーションサーバー、データベースサーバーの３階層システムで考えるとわかりやすいかと思います。
<img width="849" alt="image.png" src="https://qiita-image-store.s3.amazonaws.com/0/122992/afd68acf-bc36-40dd-feb9-9bbce4d448fa.png">

システムを構築するのに従来は、各サーバーごとにインフラ、OS、ミドルウェア、アプリと構築していましたが、仮想化の登場で１台のサーバーで３階層を構築することが可能になりました。そして、Dockerの登場で仮想化よりも管理するレイヤーが減り、ミドルウェアとアプリの２層だけになりました。この２層をコンテナとしてDockerEngineは管理しています。さらに先ほども言った通り、Dockerはこのコンテナをコードで管理しているのです。

###Immutable Infrastructure
一度セットアップしたサーバーには二度と変更を加えないというDockerの運用概念です。この概念のおかげでコンテナ内は不変のサーバー基盤環境として動作するので、開発者にとっては利便性が増します。

</br>
</br>
#コンテナの具体像
コンテナとはいわば***「サンドボックス化されたプロセスの集合体」***です。
(サンドボックスは本来外部プログラムの安全性を確かめるための保護された領域のことを指します。)
コンテナ内は隔離されており、その内部でプロセスが実行されています。

<img width="300" alt="image.png" src="https://qiita-image-store.s3.amazonaws.com/0/122992/354a76b5-ae9e-ecad-0a36-26dc838e1ec4.png">



実際にコンテナの中身を覗いてみると、ファイルシステムが存在しています。

```shell

$ docker exec -it コンテナ名 ls /

#実行結果
bin    etc    lib    mnt    root   sbin   sys    usr
dev    home   media  proc   run    srv    tmp    var
```



</br>
</br>
#コンテナができるまで
<img width="850" alt="image.png" src="https://qiita-image-store.s3.amazonaws.com/0/122992/326dcd99-deeb-b49e-10ff-6d8d0a9b7666.png">
コンテナの生成には大きく分けて２パターンあります。
</br>
一つは、**DockerHubからイメージを引っ張る**方法。ApcheやMySQLなど、有名どころのミドルウェア・ソフトウェアの環境をローカルにイメージとしてダウンロードして、docker runのコマンドでコンテナを作成することができます。Hubなので、世界中の人々が作ったコンテナ環境が公開されていたりもします。

もう一つは、自分でオリジナルな環境を構築したい(たとえば、自分でパッケージを厳選してダウンロードしたり、コンテナ起動時にはあるコマンドを実行したり)という場合に**Dockerfileを記述**して、それをdocker buildのコマンドでイメージにしてコンテナを作る方法です。


</br>
以下は[チュートリアル](http://docs.docker.jp/get-started/part2.html)で使われていたDockerfileです。python:2.7-slimという親イメージをもとにオリジナルのコンテナ環境を作成しています。

```dockerfile:Dockerfile
# 公式 Python ランタイムを親イメージとして使用
FROM python:2.7-slim

# 作業ディレクトリを /app に設定
WORKDIR /app

# 現在のディレクトリの内容を、コンテナ内の /app にコピー
ADD . /app

# requirements.txt で指定された必要なパッケージを全てインストール
RUN pip install -r requirements.txt

# ポート 80 番をコンテナの外の世界でも利用可能に
EXPOSE 80

# 環境変数の定義
ENV NAME World

# コンテナ起動時に app.py を実行
CMD ["python", "app.py"]
```

</br>
詳しいDockerfileの書き方については、[Dockerfileリファレンス](http://docs.docker.jp/engine/reference/builder.html)を参照にしてください。

###~代表的なdockerコマンド~
**docker pull <イメージ名>**
DockerHubに公開されているイメージをローカルにダウンロード
**docker push <ユーザー名>/<レポジトリ名>:<タグ名>**
自分がDockerfileで作成したイメージなどをDockerHubに登録
**docker images**
リポジトリ内のイメージを表示
**docker run <イメージ名>**
イメージをもとにコンテナを作成、起動
**docker build -t <イメージ名> .**
カレントディレクトリの Dockerfile でイメージ作成
**docker container ls** or **docker ps**
全ての実行中コンテナを表示

</br>
</br>
#複数コンテナの管理には
<img width="881" alt="image.png" src="https://qiita-image-store.s3.amazonaws.com/0/122992/f29dc55d-78ea-2660-1527-00470c0f7158.png">

##Docker Compose
複数のコンテナを起動するのに、１コンテナずつ起動していては面倒なので、docker-composeコマンドを利用しましょう。docker-composeコマンドは**YAML形式で定義されたファイルをもとに**複数コンテナを起動します。

</br>
以下はDjango(Pythonのフレームワーク)アプリを起動するためのYAMLファイルです。([参照](http://docs.docker.jp/compose/django.html))
dbコンテナはイメージから直接引っ張ってきています。また、webコンテナはDockerfileをもとに起動しています。

```yaml:docker-compose.yml
version: '2'
services:
  db:
    #DockerHubからpostgresイメージからコンテナを起動
    image: postgres
  web:
    #カレントディレクトリにあるDockerfileをもとにコンテナを起動
    build: .
    #コンテナ起動時のコマンド
    command: python manage.py runserver 0.0.0.0:8000
    #カレントディレクトリを/codeにマウント
    volumes:
      - .:/code
    #ポートを公開する(ホスト:コンテナ)
    ports:
      - "8000:8000"
    #dbへのコンテナ間通信を可能にする
    links:
      - db

```

</br>
詳しいdocker-compose.ymlの書き方については、[docker-compose.ymlリファレンス](https://qiita.com/zembutsu/items/9e9d80e05e36e882caaa)を参照にしてください。

## Dockerネットワーク

> [Compose のネットワーク機能 — Docker-docs-ja 17.06.Beta ドキュメント](http://docs.docker.jp/compose/networking.html)
> [マルチホストでのDocker Container間通信 第1回: Dockerネットワークの基礎 - UZABASE Tech Blog](https://tech.uzabase.com/entry/2017/08/07/172411)
> [マルチホストでのDocker Container間通信 第2回 Port Forwarding と Overlay Network - UZABASE Tech Blog](https://tech.uzabase.com/entry/2017/08/23/175813)
> [マルチホストでのDocker Container間通信 第3回: Kubernetesのネットワーク(CNI, kube-proxy, kube-dns) - UZABASE Tech Blog](https://tech.uzabase.com/entry/2017/09/12/164756)


### コンテナ間ネットワークについて
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/122992/cb6f7ff7-b85d-1a86-16b7-5be64fc9025f.png)

IPマスカレード(NAPT)…１つのグローバルIPアドレスを複数のプライベートIPアドレスと紐づけて変換する機能。

#### 基本
* docker-composeの場合、同一ホスト上に存在するコンテナはデフォルト上のネットワーク(ディレクトリ名_default)でdocker0というブリッジ(bridge)を介して、すべてのコンテナ間で通信ができるようになリマス。ブリッジは外部通信との架け橋にもなります。ネットワーク確認コマンド`docker network ls`で見つけられる標準のネットワークは以下3つです。
1. none...外部ネットワークなし。
2. host...Dockerホストと同じIPアドレス、インタフェースを持つ。
3. bridge...Dockerホストとは異なる内部ネットワークを構築し、vethでdocker0インタフェースとDockerホストのインターフェースと接続する。
	
> [Docker network 概論 - Qiita](https://qiita.com/TsutomuNakamura/items/ed046ee21caca4a2ffd9)


* コンテナに割り振られたIPアドレスは起動するたびに異なりますが、コンテナのホスト名で指定することで名前解決して通信を行えるようになります。

* `ports`で指定した場合はDockerホスト側のネットワークインターフェースからもアクセス可能です。

* `links`を使えばコンテナのホスト名にエイリアスを割り当てることが可能です。


### ネットワークをカスタマイズする
> [Docker ネットワーク・プラグイン — Docker-docs-ja 17.06.Beta ドキュメント](http://docs.docker.jp/engine/extend/plugins_network.html)


ドライバーを適用してオリジナルのネットワークを作成することができます。
ドライバーのデフォルトは単一ホスト上ならbridge, Swarm上であればoverlayを使うと良いそうです。
(ネットワーク・プラグインを使えばDockerEngineが拡張されて、プラグインを使ったネットワークを構築することができるらしい…)


```bash:dockerネットワークを作成してそのネットワークでコンテナを動かす
# カスタムネットワークを作成
$ docker network create --driver [ドライバー名] [ネットワーク名]
# コンテナをそのネットワークで動かす
$ docker run --net=[ネットワーク名]
```


#### docker-composeでカスタム・ネットワーク設定
基本的にディレクトリ名_defaultでネットワークを作成することができます。
それでも、オリジナルでネットワークを作成した場合は以下のサンプルを参照にしてください。

```yaml
version: '2'

services:
  proxy:
    build: ./proxy
    networks:
      - front
  app:
    build: ./app
    networks:
      - front
      - back
  db:
    image: postgres
    networks:
      - back

networks:
  front:
    # Use a custom driver
    driver: custom-driver-1
  back:
    # Use a custom driver which takes special options
    driver: custom-driver-2
    driver_opts:
      foo: "1"
      bar: "2"
```

> [Composeファイル・リファレンス](http://docs.docker.jp/compose/compose-file.html#network-configuration-reference)


### マルチホストでコンテナを管理する

<img width="809" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/122992/1e7d1ebc-41e9-05b9-5ba6-6a419d116942.png">



Dockerネットワークはプライベートネットワークのため、異なるホストにあるコンテナには通常アクセスすることができません。
参考にさせていただいた記事では対応策としては以下が挙げられていました。
* Port Forwarding...**接続元のContainerが接続先のContainerのホストのIPを知っている**ことが前提
* Overlay Network...Overlay Network構築して同じネットワークに属するようにする
* [flannel](https://github.com/coreos/flannel)...**ホストに動的IPが割り振られている場合は適用不可**

ただどれもイマイチなので、
=> Kubernetes の登場へと続く…
Docker SwarmもKubernetesと同じく、複数のDocker環境を管理できるオーケストレーションツールです。環境のスケールをより効率的に行うことができますが、Kubernetesの方が優勢です。

以下の記事でDocker Swarmの具体的なイメージがつかめるかと思います。
[Docker Swarm 入門ハンズオン](https://qiita.com/TsutomuNakamura/items/6124ab7d32a58bc93ac7)

</br>
</br>
#あらゆる環境でDocker Engineを動かす
##Docker Machine
Dockerを利用するためには、各種OS、クラウド環境にDocker Engineが必要です。Docker Machineは "利用するホスト環境にDocker Engineを導入するためのツール"のことです。

***ローカルでDockerを実行したい***
![image.png](https://qiita-image-store.s3.amazonaws.com/0/122992/034f4595-4e9d-f60c-a24e-91f5869a9e0a.png)
***リモート・システム上に Docker ホストをプロビジョンしたい***
![image.png](https://qiita-image-store.s3.amazonaws.com/0/122992/d558fe21-64a7-f209-32de-2d3c02d92d88.png)


>
[Docker Machine 概要](http://docs.docker.jp/machine/overview.html)

以下はDockerが使用できるAWS EC2環境をdocker-machineコマンドを用いて作成する手順です。(前提条件:アクセスキーとシークレットアクセスキーを取得するのは必須)
[Docker Machine:Amazon Web Services (AWS) EC2 の例](http://docs.docker.jp/machine/examples/aws.html)

```shell:手順
#1,AWS EC2環境を作成
docker-machine create --driver amazonec2 --amazonec2-access-key YOUR_ACCESS_KEY --amazonec2-secret-key YOUR_SECRET_KEY  aws-test
#2,Docker Machine環境をアクティブにする
eval $(docker-machine env aws-test)
#3,Docker Machine環境を停止する
docker-machine stop aws-test
#4,Docker Machine環境を再開する
docker-machine start aws-test
# TLS証明書を発行する
docker-machine regenerate-certs aws-test
# SSH接続する
docker-machine ssh aws-test
```

</br>
</br>
</br>
以上がDockerを取り巻く基本的な技術ですが、実際に自分でコードを書いてアプリ開発環境まで取り組む余地がまだまだありそう...。