Python・Redis・Nginxを使って始めるDocker Tutorial

[Docker Tutorial — Getting Started with Python, Redis, and Nginx.](https://hackernoon.com/docker-tutorial-getting-started-with-python-redis-and-nginx-81a9d740d091)という海外の記事が凄くdocker初学者向けで、人気だったので分かりやすく翻訳してみました。

#はじめに
これはDockerコンテナに関する入門チュートリアルです。この記事を読み終わると、ローカルマシン上でDockerを使う方法を知る事が出来ます。
この記事ではPython、Redis、Nginxコンテナを実行します。
今回、この記事の読者は上記の３つの技術の基本概念に精通していると仮定しています。
シェルを使った例を沢山使うので、ターミナルを用意して読み進めて下さい！

#Dockerとは何か
Dockerとは、ソフトウェアコンテナ内のアプリケーションの開発を自動化する為に作られたオープンソースツールです。
このDockerの概念を理解する一番簡単な方法は、現実世界の運送コンテナと比較することでしょう。
ここでは、あくまで一例として読んでください。

昔、運送会社は次のような問題に直面していました。
・どうやって、異なるタイプの品物（食べ物、化学薬品、ガラス製品、レンガなど）を並べて輸送すればいいのか
・同じ車両を使って、どうやって異なるサイズの荷物を運べばいいのか

しかし、コンテナを導入した後にこれらの問題を解決する事が出来ました。
レンガはガラスの上に置き、化学製品は食べ物の隣に置き、異なるサイズの荷物はあらかじめ規定されたコンテナに入れ、同じ車両を使って荷物を下ろしたり積んだりする事が出来るようになりました。

理解出来たでしょうか？それでは、ソフトウェア開発におけるコンテナに話を戻します。

アプリケーションを開発する際に、ライブラリやWebサーバー、データベースなどの関係を意識してコードを書くと思いますが、実行環境がそれぞれのコンピュータで異なる時に<b>アプリケーションが作動しなくなる経験をした事がある</b>かもしれません。

しかし、この問題は<b>アプリケーションとシステムを分離させる</b>ことにより解決する事が出来ます。

#普通の仮想化とは何が違うのか？
従来は、仮想マシンを使って上記の問題を回避していました。しかし、この仮想マシンの問題は、ホストOS上にある<b>“余分なOS”が数GBの容量を使ってしまう事</b>でした。ほとんどの場合、サーバーはさらに多くのスペースを占有する仮想マシンをホストすることになってしまいます（余談ですが今現在、大半のクラウドサーバーサービスはその余分なスペースの分を請求しています）
また、もう一つの重大な欠点は<b>起動が遅い事</b>です。

<b>DockerはホストOSの別々のプロセスとして、OSのカーネルを全てのコンテナをまたいで共有することにより、上記のような問題を全て解決してくれます。</b>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/301628/d21751ed-a2fb-5ab5-9dda-b597627f7105.png)


Dockerが唯一のコンテナ化プラットフォームではないですが、現時点では市場で最も大きく、ユーザー数も多いです。

#なぜDockerが必要なのか
下記にメリットをまとめておきました。

・開発プロセスの迅速にする事ができる
・アプリケーションを手軽にまとめられる
・local/dev/staging/production環境で同じ動作が可能になる
・簡単で分かりやすい監視が出来る
・拡張が簡単になる

###開発プロセスの迅速にする事ができる
PostgreSQL, Redis, Elasticsearchのような外部のアプリをシステム上にインストールする必要はありません。コンテナを起動させるだけで実行する事が出来ます！

Dockerを使うことによって、同じアプリの異なるバージョンを同時に実行する事も出来ます。
例えば、PostgreSQLの古いバージョンを新しいバージョンに手動で移行させる必要があるとします。最新の外部のアプリを使った新しいマイクロサービスを作りたい時に、マイクロサービスの設計でこのような状況になることは頻繁にあります。この時、１つのホストOS上で同じアプリの異なるバージョンを同時に実行するのは非常に困難です。

しかし、Dockerならこの問題を完璧に解決することが出来ます。Dockerを使えばサービスと外部のアプリをそれぞれ分離した独自の環境を構築出来るからです。

###アプリケーションを手軽にまとめられる
Dockerを使えば、アプリケーションを１つのカタマリとして運用することが出来ます。

大半のプログラミング言語、フレームワーク、オペレーションシステムはそれぞれパッケージマネージャを持っています。たとえもしそれぞれのパッケージマネージャでアプリをまとめたとしても、他のシステムでそのアプリを使うのは難しいです。

しかし、Dockerなら異なるホストやクラウドサービスの垣根を越えて、あるフォーマットに統一されたイメージを作ることによって他のシステムでもアプリを使う事が出来ます。

###local/dev/staging/production環境で同じ動作が可能になる
どんな技術にもヒューマンファクタがあるため、Dockerはlocal/dev/staging/productionが全て同じ環境であることを保証することは出来ません。
しかし、OSやシステムの依存関係によって起こるエラーの確率をほとんどゼロに減らしてくれます。

正しい方法でDocker imageをビルドする事が出来れば、同じOSやシステム上の依存関係を持ったアプリを使う事が出来ます！

＊ヒューマンファクタ・・・人間や組織・機械・設備等で構成されるシステムが、安全かつ経済的に動作・運用できるために考慮しなければならない人間側の要因のこと。一言でいえば「人的要因」である。

###簡単で分かりやすい監視が出来る
起動しているコンテナのログファイルを読むための統一された方法がDockerにはあります。
どこにログファイルを保存しておくかも覚える必要はなく、外部のログドライバーとモニターを１つの場所にまとめておく事が出来ます。

###拡張が簡単になる
設計上Dockerは環境変数の設定、TCP/UDPポートを通した通信などの主要な原則に沿って使う事が出来ます。
正しくアプリを完成させればDockerの中だけではなく、色々な所にスケールを広げる事が簡単に出来ます。

#対応プラットフォーム
DockerのネイティブプラットフォームはLinuxです。これはLinuxカーネルに基づいてDockerが作られているからです。しかし、macOSやWindows上で使う事が出来ます。
Linuxとの唯一の違いはmacOSとWindows上のDockerは小さな仮想マシンにカプセル化されている事ですが、現在では、macOSとWindows上のDockerは本来のDockerと同じくらい使いやすくなっています。

#インストール
Dockerのインストール方法は[ここから](https://docs.docker.com/docker-for-mac/install/)確認する事が出来ます。

(もしLinuxでDockerを実行しているなら、以下のすべてのコマンドをrootとして実行するか、ユーザーをdockerグループに追加して再ログインする必要があります。)

```
sudo usermod -aG docker $(whoami)`
```

#用語
・<b>コンテナ</b>
必要なソフトウェアをまとめたインスタンスのことです。コンテナはイメージから作られ、他のコンテナや外部と通信するためにポートとボリュームを公開することが出来ます。また、コンテナは簡単に短時間でキルしたり削除したり再構築したりすることが出来ます。
そして、コンテナは状態を保持しません。

・<b>イメージ</b>
コンテナの元となる要素のことです。イメージによって構築する時に時間がかかることがあります。一方、コンテナはイメージからすぐに構築する事が出来ます。

・<b>ポート</b>
元々の意味ではTCP / UDPポートという意味です。ですが、もっと分かりやすく理解するために、他のコンテナやホストOSから接続可能な外部のネットワークと通信する時に必要なものと考えてください。

・<b>ボリューム</b>
Dockerにおける共有フォルダのことです。コンテナを作ると初期化されます。ボリュームはデータを保持しておくために設計されており、コンテナの動作には影響されません。

・<b>レジストリ</b>
イメージを保存しているサーバーのことです。Githubと比較すると分かりやすいかもしれません。
ローカルでイメージを使う時にレジストリからイメージをpullしたり、ローカルで作ったイメージをレジストリにpushする事が出来ます。

・<b>DockerHub</b>
Docker Inc.が提供するWebインターフェイスを備えたレジストリのことです。様々なソフトウェアを含むDockerイメージを多数保存しています。
Docker Hubは、Dockerチーム、またはオリジナルのソフトウェア製造者と共同で作成された「公式の」Dockerイメージのソースです。
公式のイメージはそれらの潜在的な脆弱性をリストにまとめてあります。このリストはDockerHubにログインしているすべてのユーザーが閲覧する事が出来ます。
無料と有料の両方のアカウントがあります。
パブリックイメージは無制限に持つことができますが、プライベートイメージは1つのアカウントに1つまでです。
Docker Hubと非常によく似たサービスに、Docker Storeもあります。評価、レビューなどが掲載されています。

# Example 1: hello world
それでは、最初のコンテナを動かしてみましょう！

```terminal
docker run ubuntu /bin/echo 'Hello world'
```



```terminal:コンソールの出力結果
Unable to find image 'ubuntu:latest' locally  
latest: Pulling from library/ubuntu  
6b98dfc16071: Pull complete  
4001a1209541: Pull complete  
6319fc68c576: Pull complete  
b24603670dc3: Pull complete  
97f170c87c6f: Pull complete  
Digest:sha256:5f4bdc3467537cbbe563e80db2c3ec95d548a9145d64453b06939c4592d67b6d  
Status: Downloaded newer image for ubuntu:latest  
Hello world
```

・docker runはコンテナを実行するためのコマンドです。

・ubuntuは今実行しているイメージです。たとえば、UbuntuOSのイメージを使う時に、イメージを指定すると、Dockerは最初にDockerホスト上のイメージを探します。イメージがローカルに存在しない場合、そのイメージはパブリックイメージレジストリ=Docker Hubから取得してきます。

・```/bin/echo 'Hello world'```は作ったコンテナの中で実行されるコマンドです。このコンテナは単に「Hello world」と表示して実行を停止します。




それでは、Dockerコンテナ内でシェルを作ってみましょう！

```terminal
docker run -i -t --rm ubuntu /bin/bash
```

・```-t```は、作成するコンテナ内に疑似的なターミナルを割り当てる設定をしてます

・```-i```は、コンテナーの標準入力（STDIN）を取得し、双方向の接続をする設定をしてます。

・```-rm```は、プロセスが終了する時にコンテナを削除する設定をしてます。デフォルトだと、コンテナは削除されません。このrmの設定によって作られたコンテナは、シェルのセッションを維持する間ずっと残りますが、リモートサーバーとのssh通信などのようにセッションが無くなると削除されます。

セッション終了後もコンテナを実行し続けたい場合は、デーモン化する必要があります。

```terminal
docker run --name daemon -d ubuntu /bin/sh -c "while true; do echo hello world; sleep 1; done"
```

・```--name daemon```は「daemon」という名前でコンテナに割り当てる設定をしてます。名前を明示的に指定しない場合、Dockerは自動的に名前を生成し割り当てます。

・```-d```はバックグラウンドでコンテナを実行させる設定をしてます。（つまり、デーモン化です）


それでは今どんなコンテナを持っているのか見てみましょう。

```terminal
docker ps -a
```

```terminal:コンソールの出力結果
CONTAINER ID  IMAGE   COMMAND                 CREATED             STATUS                         PORTS  NAMES  
1fc8cee64ec2  ubuntu  "/bin/sh -c 'while..."  32 seconds ago      Up 30 seconds                         daemon  
c006f1a02edf  ubuntu  "/bin/echo 'Hello ..."  About a minute ago  Exited (0) About a minute ago         gifted_nobel
```

・```docker ps```はコンテナをリスト表示するためのコマンドです。

・```-a```は全てのコンテナは表示させるためのコマンドです。（```docker ps```だけだと起動中のコンテナしかリスト表示されません）





```docker ps```をしてみると、現在2つのコンテナを持っていることが分かります。



*```gifted_nobel```　は、「Hello,World」と出力させた、最初に作ったコンテナです。 (このコンテナの名前は自動的に生成されます。ご自身のコンピュータによって名前は異なります。)

*```daemon``` は、デーモンとして作った3番目のコンテナです。

注：2番目のコンテナ（対話型シェルを持つコンテナ）はありません。何故なら、<b>```-rm```オプションを設定しているからです。</b>そのため、2番目のコンテナは実行された時に自動的に削除されています。


それでは、ログを見てデーモンコンテナが何をしているのか見てみましょう！

```terminal
docker logs -f daemon
```

```terminal:コンソールの出力結果
...
hello world  
hello world  
hello world
```

・```docker log```を使うとdockerのログを取得することが出来ます。
・```-f```を使うとログの出力結果を追跡することが出来ます。

では、デーモンコンテナを停止してみましょう！


```terminal
docker stop　daemon
```

停止しているかどうか確かめて見てください。

```terminal
docker ps -a
```

```terminal:コンソールの出力結果
CONTAINER ID  IMAGE   COMMAND                 CREATED        STATUS                      PORTS  NAMES  
1fc8cee64ec2  ubuntu  "/bin/sh -c 'while..."  5 minutes ago  Exited (137) 5 seconds ago         daemon  
c006f1a02edf  ubuntu  "/bin/echo 'Hello ..."  6 minutes ago  Exited (0) 6 minutes ago           gifted_nobel
```

コンテナが停止していますね。もう一度、起動させてみましょう。

```terminal
docker start daemon
```

起動しているか確かめてみてください。

```terminal
docker ps -a
```

```terminal:コンソールの出力結果
CONTAINER ID  IMAGE   COMMAND                 CREATED        STATUS                    PORTS  NAMES  
1fc8cee64ec2  ubuntu  "/bin/sh -c 'while..."  5 minutes ago  Up 3 seconds                     daemon  
c006f1a02edf  ubuntu  "/bin/echo 'Hello ..."  6 minutes ago  Exited (0) 7 minutes ago         gifted_nobel
```

起動出来ていますね。では、今度はもう一度停止して、手動で消してみましょう！

```terminal
docker stop daemon  
docker rm <your first container name>  
docker rm daemon
```

全てのコンテナを消したい場合は、次のコマンドを使用します。

```terminal
docker rm -f $(docker ps -aq)
```

・```docker rm``` はコンテナを削除するためのコマンドです。
・``` -f $()``` は実行中のコンテナを停止させてます(つまり、強制的に消すという事です)。
・ ```-q```（ps用） はコンテナIDのみを表示させるためのコマンドです。


#Example 2: 環境変数とボリューム
この例を始める前に、私の[Githubレポジトリ](https://github.com/alexryabtsev/docker-workshop/) から新たなファイルを追加してください。

私のレポジトリをクローンするか、[このリンク](https://github.com/alexryabtsev/docker-workshop/archive/master.zip)をクリックしてファイルをダウンロードしてください。

それでは、ここからはNginxのような、もっと難易度の高いコンテナを作っていきましょう！

examples/nginxにカレントディレクトリを切り替えて下さい！

```terminal
docker run -d --name "test-nginx" -p 8080:80 -v $(pwd):/usr/share/nginx/html:ro nginx:latest
```

・```-p``` は、HOST PORT（8080）をCONTAINER PORT（80）としてマッピングしてます。

・```-v``` は、HOST DIRECTORY(pwd)をCONTAINER DIRECTORY（/usr/share/nginx/html:ro）にボリュームをマウントさせてます。

<b>注：このコマンドは非常にコンピュータに負荷がかかりますが、環境変数とボリュームを説明するためにあえて分かりやすくしています。実際の開発では、こんな風に手動でDockerコンテナをスタートさせたりせずにオーケストレーションサービスを使うか、カスタムスクリプトを書いてスタートさせます。</b>

```terminal:コンソールの出力結果
Unable to find image 'nginx:latest' locally  
latest: Pulling from library/nginx  
683abbb4ea60: Pull complete  
a470862432e2: Pull complete  
977375e58a31: Pull complete  
Digest: sha256:a65beb8c90a08b22a9ff6a219c2f363e16c477b6d610da28fe9cba37c2c3a2ac  
Status: Downloaded newer image for nginx:latest  
afa095a8b81960241ee92ecb9aa689f78d201cff2469895674cec2c2acdcc61c
```

重要:```run```コマンドは<b>絶対パスのみ</b>受け付けます。この例では、カレントディレクトリの絶対パスとして```$(pwd)```を使ってます。

また、```/example/nginx/index.html```（コンテナの中に```/usr/share/nginx/html```ディレクトリとしてマウントされてるファイル）を変更してページを更新することも出来ます。

それでは、test-nginxコンテナに関する情報を入手しましょう。

```terminal
docker inspect test-nginx
```

このコマンドはDockerのインストールに関するシステム全体の情報を表示します。
この情報には、カーネルのバージョン、コンテナーとイメージの数、公開ポート、マウント済みボリュームなどが含まれます。

#Example 3: 初めてのDockerfile
DockerイメージをビルドするためにはDockerfileを作る必要があります。Dockerfileとはコンテナなどの説明や実行させたい命令などを書いておくテキストファイルのことです。

今回の例で使う命令の説明はこんな感じです。

* ```FROM```：ベースとなるイメージを設定する
* ```RUN```：コンテナ内でコマンドを実行
* ```ENV```：環境変数を設定する
* ```WORKDIR```：作業ディレクトリを設定する
* ```VOLUME```：ボリュームのマウントする場所を設定する
* ```CMD```：コンテナを※実行可能コンテナとして設定する

※「実行可能コンテナ」とは、他のコンテナのベースイメージとして利用するのではなく、docker runで直接実行することを目的としたコンテナイメージのこと。

詳細は[Dockerfile reference](https://docs.docker.com/engine/reference/builder/)で見ることが出来ます。

では、URLを使ってWebサイトの中身を取得するイメージを作り、テキストファイルに保存してみましょう！

『SITE_URL』という環境変数を経由してWebサイトのURLをパスする必要があります。実行結果のファイルはボリュームとしてマウントされて、ディレクトリの中に配置されます。

Dockerfileという名前のファイルを```examples/curl```ディレクトリの中に配置して、次のような内容を書いてください。

```dockerfile:Dockerfile
FROM ubuntu:latest  
RUN apt-get update \  
    && apt-get install --no-install-recommends --no-install-suggests -y curl \
    && rm -rf /var/lib/apt/lists/*
ENV SITE_URL http://example.com/  
WORKDIR /data  
VOLUME /data  
CMD sh -c "curl -Lk $SITE_URL > /data/results"
```

Dockerfileの準備が出来ました。それでは、実際にイメージをビルドしてみましょう！
```examples/curl```ディレクトリに移動して、次のコマンドを実行してイメージをビルドしてください。

```terminal
docker build . -t test-curl
```

```terminal:コンソールの出力結果
Sending build context to Docker daemon  3.584kB  
Step 1/6 : FROM ubuntu:latest  
 ---> 113a43faa138
Step 2/6 : RUN apt-get update     && apt-get install --no-install-recommends --no-install-suggests -y curl     && rm -rf /var/lib/apt/lists/*  
 ---> Running in ccc047efe3c7
Get:1 http://archive.ubuntu.com/ubuntu bionic InRelease [242 kB]  
Get:2 http://security.ubuntu.com/ubuntu bionic-security InRelease [83.2 kB]  
...
Removing intermediate container ccc047efe3c7  
 ---> 8d10d8dd4e2d
Step 3/6 : ENV SITE_URL http://example.com/  
 ---> Running in 7688364ef33f
Removing intermediate container 7688364ef33f  
 ---> c71f04bdf39d
Step 4/6 : WORKDIR /data  
Removing intermediate container 96b1b6817779  
 ---> 1ee38cca19a5
Step 5/6 : VOLUME /data  
 ---> Running in ce2c3f68dbbb
Removing intermediate container ce2c3f68dbbb  
 ---> f499e78756be
Step 6/6 : CMD sh -c "curl -Lk $SITE_URL > /data/results"  
 ---> Running in 834589c1ac03
Removing intermediate container 834589c1ac03  
 ---> 4b79e12b5c1d
Successfully built 4b79e12b5c1d  
Successfully tagged test-curl:latest
```

* ```docker build```はローカルで新しいイメージをビルドしてます。
* ```-t```はnameタグをイメージに取り付けてます。

これで新しいイメージを持つことが出来ました。ここで、今現在持っているイメージを一覧で見てみましょう。

```terminal
docker images
```

```terminal:コンソールの出力結果
REPOSITORY  TAG     IMAGE ID      CREATED         SIZE  
test-curl   latest  5ebb2a65d771  37 minutes ago  180 MB  
nginx       latest  6b914bbcb89e  7 days ago      182 MB  
ubuntu      latest  0ef2e08ed3fa  8 days ago      130 MB
```

イメージからコンテナを作ったり、実行させる事が出来ます。デフォルトのパラメータを使ってやってみましょう。

```
docker run --rm -v $(pwd)/vol:/data/:rw test-curl
```

実行した結果がファイルに保存されたことを確認するには、次のコマンドを実行します。

```
cat ./vol/results
```

Facebook.comで試してみましょう。

```
docker run --rm -e SITE_URL=https://facebook.com/ -v $(pwd)/vol:/data/:rw test-curl
```

実行した結果がファイルに保存されたことを確認するには、次のコマンドを実行します。

```
cat ./vol/results
```

###効率よくイメージを作るためのbest practice

* <b>必要なコンテキストのみを含める</b> ： 
```.dockerignore```ファイルを使用する  （gitの.gitignoreのように）
 

* <b>不要なパッケージをインストールしない</b>　：
余分なディスク容量が消費されます。


* <b>キャッシュを使用する</b>：
Dockerfileの最後に大きな変更を加えるコンテキスト（プロジェクトのソースコードなど）を追加します。Dockerキャッシュを効果的に利用します。


* <b>ボリュームに注意する</b>：
ボリューム内のデータが何であるかを覚えておく必要があります。ボリュームは永続的であり、コンテナーと共存しないため、次のコンテナーは前のコンテナーによって作成されたボリュームのデータを使用します。


* <b>環境変数を使用する（RUN、EXPOSE、VOLUMEなど)</b>:
Dockerfileを変更しやすくなります。


###Alpineイメージについて
多くのDockerイメージ（イメージのバージョン）がAlpine Linux上に作成されています。これは容量を分散化させてDockerイメージの全体サイズを縮小するためです。

Redis, Postgresなどのサードパーティサービスには、Alpineベースのイメージを使うことをオススメします。
Appイメージには[buildpack](https://hub.docker.com/_/buildpack-deps/)ベースのイメージを使用してください。コンテナ内でデバッグするのが簡単になりますし、システム全体に必要な要素が事前にインストールされてます。

どのベースイメージを使用するかを決めるだけではなく、このように1つの基本イメージを使用することによって最大の効果を得ることができます。これは、キャッシュがより効果的に使用されるからです。

#Example 4:コンテナ同士を接続してみよう
Docker composeを使っていきます。
Docker composeとはコンテナ同士を接続するためのCLIです。
pip経由でインストールする事が出来ます。

```
sudo pip install docker-compose
```

今回の例では、PythonコンテナとRedisコンテナを接続してみたいと思います。

```
version: '3.6'  
services:  
  app:
    build:
      context: ./app
    depends_on:
      - redis
    environment:
      - REDIS_HOST=redis
    ports:
      - "5000:5000"
  redis:
    image: redis:3.2-alpine
    volumes:
      - redis_data:/data
volumes:  
  redis_data:
```


example/composeにカレントディレクトリを移動して、次のコマンドを実行してみてください。


```
docker-compose up
```


```terminal:コンソールの結果
Building app  
Step 1/9 : FROM python:3.6.3  
3.6.3: Pulling from library/python  
f49cf87b52c1: Pull complete  
7b491c575b06: Pull complete  
b313b08bab3b: Pull complete  
51d6678c3f0e: Pull complete  
09f35bd58db2: Pull complete  
1bda3d37eead: Pull complete  
9f47966d4de2: Pull complete  
9fd775bfe531: Pull complete  
Digest: sha256:cdef88d8625cf50ca705b7abfe99e8eb33b889652a9389b017eb46a6d2f1aaf3  
Status: Downloaded newer image for python:3.6.3  
 ---> a8f7167de312
Step 2/9 : ENV BIND_PORT 5000  
 ---> Running in 3b6fe5ca226d
Removing intermediate container 3b6fe5ca226d  
 ---> 0b84340fa920
Step 3/9 : ENV REDIS_HOST localhost  
 ---> Running in a4f9a1d6f541
Removing intermediate container a4f9a1d6f541  
 ---> ebe63bf5959e
Step 4/9 : ENV REDIS_PORT 6379  
 ---> Running in fd06aa65fd33
Removing intermediate container fd06aa65fd33  
 ---> 2a581c31ff4f
Step 5/9 : COPY ./requirements.txt /requirements.txt  
 ---> 671093a12829
Step 6/9 : RUN pip install -r /requirements.txt  
 ---> Running in b8ea53bc6ba6
Collecting flask==1.0.2 (from -r /requirements.txt (line 1))  
  Downloading https://files.pythonhosted.org/packages/7f/e7/08578774ed4536d3242b14dacb4696386634607af824ea997202cd0edb4b/Flask-1.0.2-py2.py3-none-any.whl (91kB)
Collecting redis==2.10.6 (from -r /requirements.txt (line 2))  
  Downloading https://files.pythonhosted.org/packages/3b/f6/7a76333cf0b9251ecf49efff635015171843d9b977e4ffcf59f9c4428052/redis-2.10.6-py2.py3-none-any.whl (64kB)
Collecting click>=5.1 (from flask==1.0.2->-r /requirements.txt (line 1))  
  Downloading https://files.pythonhosted.org/packages/34/c1/8806f99713ddb993c5366c362b2f908f18269f8d792aff1abfd700775a77/click-6.7-py2.py3-none-any.whl (71kB)
Collecting Jinja2>=2.10 (from flask==1.0.2->-r /requirements.txt (line 1))  
  Downloading https://files.pythonhosted.org/packages/7f/ff/ae64bacdfc95f27a016a7bed8e8686763ba4d277a78ca76f32659220a731/Jinja2-2.10-py2.py3-none-any.whl (126kB)
Collecting itsdangerous>=0.24 (from flask==1.0.2->-r /requirements.txt (line 1))  
  Downloading https://files.pythonhosted.org/packages/dc/b4/a60bcdba945c00f6d608d8975131ab3f25b22f2bcfe1dab221165194b2d4/itsdangerous-0.24.tar.gz (46kB)
Collecting Werkzeug>=0.14 (from flask==1.0.2->-r /requirements.txt (line 1))  
  Downloading https://files.pythonhosted.org/packages/20/c4/12e3e56473e52375aa29c4764e70d1b8f3efa6682bef8d0aae04fe335243/Werkzeug-0.14.1-py2.py3-none-any.whl (322kB)
Collecting MarkupSafe>=0.23 (from Jinja2>=2.10->flask==1.0.2->-r /requirements.txt (line 1))  
  Downloading https://files.pythonhosted.org/packages/4d/de/32d741db316d8fdb7680822dd37001ef7a448255de9699ab4bfcbdf4172b/MarkupSafe-1.0.tar.gz
Building wheels for collected packages: itsdangerous, MarkupSafe  
  Running setup.py bdist_wheel for itsdangerous: started
  Running setup.py bdist_wheel for itsdangerous: finished with status 'done'
  Stored in directory: /root/.cache/pip/wheels/2c/4a/61/5599631c1554768c6290b08c02c72d7317910374ca602ff1e5
  Running setup.py bdist_wheel for MarkupSafe: started
  Running setup.py bdist_wheel for MarkupSafe: finished with status 'done'
  Stored in directory: /root/.cache/pip/wheels/33/56/20/ebe49a5c612fffe1c5a632146b16596f9e64676768661e4e46
Successfully built itsdangerous MarkupSafe  
Installing collected packages: click, MarkupSafe, Jinja2, itsdangerous, Werkzeug, flask, redis  
Successfully installed Jinja2-2.10 MarkupSafe-1.0 Werkzeug-0.14.1 click-6.7 flask-1.0.2 itsdangerous-0.24 redis-2.10.6  
You are using pip version 9.0.1, however version 10.0.1 is available.  
You should consider upgrading via the 'pip install --upgrade pip' command.  
Removing intermediate container b8ea53bc6ba6  
 ---> 3117d3927951
Step 7/9 : COPY ./app.py /app.py  
 ---> 84a82fa91773
Step 8/9 : EXPOSE $BIND_PORT  
 ---> Running in 8e259617b7b5
Removing intermediate container 8e259617b7b5  
 ---> 55f447f498dd
Step 9/9 : CMD [ "python", "/app.py" ]  
 ---> Running in 2ade293ecb25
Removing intermediate container 2ade293ecb25  
 ---> b85b4246e9f8
Successfully built b85b4246e9f8  
Successfully tagged compose_app:latest  
WARNING: Image for service app was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.  
Creating compose_redis_1 ... done  
Creating compose_app_1   ... done  
Attaching to compose_redis_1, compose_app_1  
redis_1  | 1:C 08 Jul 18:12:21.851 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf  
redis_1  |                 _._  
redis_1  |            _.-``__ ''-._  
redis_1  |       _.-``    `.  `_.  ''-._           Redis 3.2.12 (00000000/0) 64 bit  
redis_1  |   .-`` .-```.  ```\/    _.,_ ''-._  
redis_1  |  (    '      ,       .-`  | `,    )     Running in standalone mode  
redis_1  |  |`-._`-...-` __...-.``-._|'` _.-'|     Port: 6379  
redis_1  |  |    `-._   `._    /     _.-'    |     PID: 1  
redis_1  |   `-._    `-._  `-./  _.-'    _.-'  
redis_1  |  |`-._`-._    `-.__.-'    _.-'_.-'|  
redis_1  |  |    `-._`-._        _.-'_.-'    |           http://redis.io  
redis_1  |   `-._    `-._`-.__.-'_.-'    _.-'  
redis_1  |  |`-._`-._    `-.__.-'    _.-'_.-'|  
redis_1  |  |    `-._`-._        _.-'_.-'    |  
redis_1  |   `-._    `-._`-.__.-'_.-'    _.-'  
redis_1  |       `-._    `-.__.-'    _.-'  
redis_1  |           `-._        _.-'  
redis_1  |               `-.__.-'  
redis_1  |  
redis_1  | 1:M 08 Jul 18:12:21.852 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.  
redis_1  | 1:M 08 Jul 18:12:21.852 # Server started, Redis version 3.2.12  
redis_1  | 1:M 08 Jul 18:12:21.852 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.  
redis_1  | 1:M 08 Jul 18:12:21.852 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled.  
redis_1  | 1:M 08 Jul 18:12:21.852 * The server is now ready to accept connections on port 6379  
app_1    |  * Serving Flask app "app" (lazy loading)  
app_1    |  * Environment: production  
app_1    |    WARNING: Do not use the development server in a production environment.  
app_1    |    Use a production WSGI server instead.  
app_1    |  * Debug mode: on  
app_1    |  * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)  
app_1    |  * Restarting with stat  
app_1    |  * Debugger is active!  
app_1    |  * Debugger PIN: 170-528-240
```

[このリンク](http://127.0.0.1:5000/)をクリックしてブラウザで確認してみてください。

docker-composeの使い方は今回の趣旨とはズレるので、割愛させて頂きます。
最初はDocker Hubのイメージで試してみるのがいいでしょう。もし、自分のイメージを作りたい時は上記に乗せた「効率よくイメージを作るためのbest practice」を参照してみてください。
docker-composeを使うことに関していうと、```docker-compose.yml```を書く時になるべく分かりやすい名前をボリュームにつけてください。
これをやるだけで、将来ボリュームを調べる時に様々な問題に苦しまずに済みます。

```
version: '3.6'  
services:  
  ...
  redis:
    image: redis:3.2-alpine
    volumes:
      - redis_data:/data
volumes:  
  redis_data:
```

この場合、redis_dataは``` docker-compose.yml ```ファイル内で名付けられた名前となります。
最終的なボリューム名は、プロジェクト名を接頭辞につけたものとなります。

ボリュームを見てみましょう。

```
docker volume ls
```

```terminal:コンソールの結果
DRIVER              VOLUME NAME  
local               apptest_redis_data
```


#Docker Way
Dockerにはいくつかの制限や必要条件があり、それらはシステム（コンテンの中に入っているアプリ）の構成によって変化します。もちろん、これらの制限を無視したり、一時的に対処したりする事も出来ますが、Dockerの恩恵を100%受ける事が出来なくなってしまいます。
私は下記の条件に従うことを強くオススメします。

* <b>1アプリケーション= 1コンテナ</b>

* <b>プロセスをフォアグラウンドで実行する（systemd、upstart、その他の類似のツールは使用しないでください）。</b>

* <b>データをコンテナに入れずに、ボリュームを使用してください。</b>

* <b>SSHを使用しないでください</b>（コンテナーの中に入る必要がある場合は、docker execコマンドを使用できます）。

* <b>コンテナの中で手動で設定しないで下さい</b>


# 最後に
最後にまとめると、DockerはIDEやGitのように開発者にとって必須なツールとなりました。すぐに使えて、豊富なサービスを備えているサービスと言えます。

Dockerは、プロジェクトの規模や複雑さに関係なく、色々な場面で使用できます。はじめにcomposeとSwarmから始めて、プロジェクトが拡大したらAmazon Container ServicesやKubernetesなどのクラウドサービスに移行する事が出来ます。

貨物輸送で使用される実際のコンテナと同様に、コードをDockerコンテナに入れると、より迅速で効率的なCI / CDプロセスを構築するのに役立ちます。
これは、ギーク達によって促進されている単なる技術トレンドではありません。
DockerはPayPal、Visa、Swisscom、General Electric、Splinkなどの大企業で設計時にすでに使用されています！