From b355a8591250af5ecb87e6d6c0b7c58ba86c70ab Mon Sep 17 00:00:00 2001 From: kakakakakku Date: Sat, 25 Aug 2018 01:15:34 +0900 Subject: [PATCH] Updated README.md with Docker Compose --- README.md | 54 +++++++++++++++++++++ dockerfiles/composetest/Dockerfile | 5 ++ dockerfiles/composetest/app.py | 29 +++++++++++ dockerfiles/composetest/docker-compose.yml | 8 +++ dockerfiles/composetest/requirements.txt | 2 + images/composetest.png | Bin 0 -> 11324 bytes 6 files changed, 98 insertions(+) create mode 100644 dockerfiles/composetest/Dockerfile create mode 100644 dockerfiles/composetest/app.py create mode 100644 dockerfiles/composetest/docker-compose.yml create mode 100644 dockerfiles/composetest/requirements.txt create mode 100644 images/composetest.png diff --git a/README.md b/README.md index e14fdcb..58ba0c4 100644 --- a/README.md +++ b/README.md @@ -165,3 +165,57 @@ $ docker push ${DOCKER_HUB_ACCOUNT}/docker-hands-on-nginx 自分の Docker Hub を確認すると,以下のように正常にイメージが公開できています. ![](images/docker_hub.png) + +## Docker Compose で複数コンテナを実行する + +Docker Compose を使うと,複数コンテナを実行することができます.Docker Compose の設定ファイルは `dockerfiles/composetest/docker-compose.yml` です.以下のような構成になり,`services` 直下を見ると `web` コンテナと `redis` コンテナを実行していることがわかります. + +```yaml +version: '3' +services: + web: + build: . + ports: + - "5000:5000" + redis: + image: "redis:alpine" +``` + +さっそく実行してみましょう.Docker Composer では `docker-compose up` コマンドを実行します. + +```sh +$ docker-compose -f dockerfiles/composetest/docker-compose.yml up -d +Starting composetest_redis_1 ... done +Starting composetest_web_1 ... done +``` + +そして `http://0.0.0.0:5000/` に接続してみましょう.ブラウザをリロードすると,アクセス回数をカウントするアプリケーションを簡単に実行することができました. + +![](images/composetest.png) + +アクセス回数のデータはどこに保存されているのでしょう?実行中の `redis` コンテナに接続してみましょう. + +まず `docker ps` コマンドで実行中のコンテナ一覧を確認します(`CONTAINER ID` は異なります).次に `docker exec` コマンドで `redis` コンテナに接続をします.コンテナ内部で Redis に接続し,保存されている `hits` キーの値を確認することができます.このように `docker exec` コマンドを使うとコンテナに接続することができます. + +```sh +$ docker ps +CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES +1fc29424e6d0 composetest_web "python app.py" 14 minutes ago Up 14 minutes 0.0.0.0:5000->5000/tcp composetest_web_1 +f20f5ec95f34 redis:alpine "docker-entrypoint.s…" 14 minutes ago Up 14 minutes 6379/tcp composetest_redis_1 + +$ docker exec -it f20f5ec95f34 /bin/sh + +/data # redis-cli + +127.0.0.1:6379> GET hits +"10" +127.0.0.1:6379> quit +``` + +動作確認後は Docker Composer を停止しておきましょう. + +```sh +$ docker-compose -f dockerfiles/composetest/docker-compose.yml stop +Stopping composetest_web_1 ... done +Stopping composetest_redis_1 ... done +``` diff --git a/dockerfiles/composetest/Dockerfile b/dockerfiles/composetest/Dockerfile new file mode 100644 index 0000000..8e67d74 --- /dev/null +++ b/dockerfiles/composetest/Dockerfile @@ -0,0 +1,5 @@ +FROM python:3.4-alpine +ADD . /code +WORKDIR /code +RUN pip install -r requirements.txt +CMD ["python", "app.py"] diff --git a/dockerfiles/composetest/app.py b/dockerfiles/composetest/app.py new file mode 100644 index 0000000..92b243b --- /dev/null +++ b/dockerfiles/composetest/app.py @@ -0,0 +1,29 @@ +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) + +if __name__ == "__main__": + app.run(host="0.0.0.0", debug=True) diff --git a/dockerfiles/composetest/docker-compose.yml b/dockerfiles/composetest/docker-compose.yml new file mode 100644 index 0000000..899cf44 --- /dev/null +++ b/dockerfiles/composetest/docker-compose.yml @@ -0,0 +1,8 @@ +version: '3' +services: + web: + build: . + ports: + - "5000:5000" + redis: + image: "redis:alpine" diff --git a/dockerfiles/composetest/requirements.txt b/dockerfiles/composetest/requirements.txt new file mode 100644 index 0000000..1a5dc97 --- /dev/null +++ b/dockerfiles/composetest/requirements.txt @@ -0,0 +1,2 @@ +flask +redis diff --git a/images/composetest.png b/images/composetest.png new file mode 100644 index 0000000000000000000000000000000000000000..a56e3dd481cf082719f216e41601949a858dc4e6 GIT binary patch literal 11324 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 literal 0 HcmV?d00001