Dcoker composeで遊ぶ

+ Docker cpmposeとは
>Compose とは、複数のコンテナを定義し実行する Docker アプリケーションのためのツールです。Compose は YAML ファイルを使い、アプリケーションのサービスを設定します。コマンドを１つ実行するだけで、設定内容に基づいた全てのサービスを生成・起動します。

+ 単一ホスト上で、複数の環境を 分離
>Compose は プロジェクト名を使い、お互いの環境を 分離します。このプロジェクト名は、いくつもの異なる用途に利用できます。

+ コンテナ作成時にボリューム データを保持
>Compose は、サービスが利用するボリュームをすべて保持します。 docker-compose up の実行時、コンテナがそれ以前に実行されていたものであれば、以前のコンテナから現在のコンテナに向けてボリュームをコピーします。この処理において、ボリューム内に作成していたデータは失われません。

+ 変更のあったコンテナのみ再作成
>Compose はコンテナ生成時の設定情報をキャッシュに保存します。設定内容に変更のないサービスが再起動された場合、Compose はすでにあるコンテナを再利用します。コンテナの再利用とは、全体として環境への変更がすばやくできることを意味します。

引用：[Docker Compose 概要](https://docs.docker.jp/compose/index.html)
***
### 主要なコマンド
+ コンテナの作成、起動
~~~
docker compose up
docker compose up -d　：デタッチモードで起動
docker compose up --no-start　：イメージ作成のみで起動しない
docker compose up --build　：コンテナ開始前にイメージを構築する
~~~
参考：[docker compose up](https://docs.docker.jp/compose/reference/up.html)
+ コンテナの構築のみを行う
~~~
docker compose build
~~~
参考：[docker compose build](https://docs.docker.jp/compose/reference/build.html)
+ コンテナの停止とコンテナの削除、ボリューム、イメージを削除
~~~
docker compose down
docker compose --remove-orphans　：docker compose ps -a で表示される、docker compose downで削除されなかったコンテナを削除
~~~
参考：[docker-compose down](https://docs.docker.jp/compose/reference/down.html)
+ コンテナの停止
~~~
docker compose stop
~~~
参考：[docker compose stop](https://docs.docker.jp/compose/reference/stop.html)

+　起動したコンテナへ任意コマンドの実行
~~~
docker exec サービス名 コマンド
ex: docker exec web sh　：webに対して対話式プロンプトを実行
~~~

その他のコマンド：[コマンドライン・リファレンス](https://docs.docker.jp/compose/reference/toc.html)

+ 実際にアプリケーション環境を構築する
1. flask+redis環境：[Try Docker Compose](https://docs.docker.com/compose/gettingstarted/)
1. django+postgress環境：[Quickstart: Compose and Django](https://github.com/docker/awesome-compose/blob/master/official-documentation-samples/django/README.md)
1. rails+postgress環境：[Quickstart: Compose and Rails](https://github.com/docker/awesome-compose/blob/master/official-documentation-samples/rails/README.md)
1. wordpress(php+mariadb環境)：[Quickstart: Compose and WordPress
](https://github.com/docker/awesome-compose/blob/master/official-documentation-samples/wordpress/README.md)

ここでは一例でflask+redis環境を構築する
***
### flask+redis環境構築
+ プロジェクトディレクトリ作成
~~~
mkdir composetest
cd composetest
~~~
+ Dockerfileの作成
~~~
vim Dockerfile
~~~

<details><summary>Dcokerfileの内容</summary>

~~~docker
# syntax=docker/dockerfile:1
FROM python:3.7-alpine
WORKDIR /code
ENV FLASK_APP=app.py
ENV FLASK_RUN_HOST=0.0.0.0
RUN apk add --no-cache gcc musl-dev linux-headers
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
EXPOSE 5000
COPY . .
CMD ["flask", "run"]
~~~
</details>

参考：[Dockerfile reference](https://docs.docker.com/engine/reference/builder/)

+ Composeファイル作成
~~~
vim compose.yaml
~~~

<details><summary>compose.yamlの内容</summary>

~~~
services:
  web:
    build: .
    ports:
      - "8000:5000"
  redis:
    image: "redis:alpine"
~~~

</details>

+ pythonのソースコード(flaskで動作させるアプリ)とRequirements Fileを作成
 requirements.txtはpipを使ってインストールするライブラリの一覧を記載する。
参考：[pip User Guide](https://pip.pypa.io/en/latest/user_guide/#requirements-files)
~~~
vim app.py
vim requirements.txt
~~~

<details><summary>app.pyの内容</summary>

~~~python
import time

import redis
from flask import Flask

app = Flask(__name__)
cache = redis.Redis(host='redis', port=6379)

def get_hit_count():
    retries = 5
    while True:
        try:
            return cache.incr('hits')
        except redis.exceptions.ConnectionError as exc:
            if retries == 0:
                raise exc
            retries -= 1
            time.sleep(0.5)

@app.route('/')
def hello():
    count = get_hit_count()
    return 'Hello World! I have been seen {} times.\n'.format(count)
~~~

</details>

<details><summary>requirements.txtの中身</summary>

~~~~
flask
redis
~~~~

</details>

+ 作成した、DockerFile,Composeファイルをベースにコンテナを作成、起動
~~~
docker compose up
~~~
+ アプリの動作確認として「localhost:8000」へアクセス
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22836/205099d5-9ddc-5f2a-323d-09737c2fa76b.png)

現状、ホストOS内のアプリのソースコード(app.py)を書き換えてもコンテナ内のファイルへ反映されないため、ホストOSのプロジェクトディレクトリをコンテナ内のボリュームとしてマウントして変更を反映されるようにする。

+ Composeファイルの修正

<details><summary>修正後のcompose.yaml</summary>

~~~diff_yaml
services:
  web:
    build: .
    ports:
      - "8000:5000"
+   volumes:
+     - .:/code
+   environment:
+     FLASK_ENV: development
  redis:
    image: "redis:alpine"
~~~

</details>

+ app.pyの修正

<details><summary>修正後のapp.py</summary>

~~~diff_python
import time

import redis
from flask import Flask

app = Flask(__name__)
cache = redis.Redis(host='redis', port=6379)

def get_hit_count():
    retries = 5
    while True:
        try:
            return cache.incr('hits')
        except redis.exceptions.ConnectionError as exc:
            if retries == 0:
                raise exc
            retries -= 1
            time.sleep(0.5)

@app.route('/')
def hello():
    count = get_hit_count()
-   return 'Hello World! I have been seen {} times.\n'.format(count)
+   return 'Hello from Docker! I have been seen {} times.\n'.format(count)
~~~

</details>

+ 既存のコンテナを停止、削除
~~~
docker compose down
~~~
+ 変更を加えたコンテナの作成、起動
~~~~
docker compose up
~~~~
+ アプリの動作確認として「localhost:8000」へアクセス
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22836/2dd306c1-84a2-028e-250d-3140f8eef1e1.png)