Docker学習 - docker-composeでnginx+Flaskのwebアプリを構築する方法

2020年2月29日Docker,Flask

以前に以下の記事でdockerで簡単なFlaskで作成したWebアプリを作成しました。

今回は、dockerコンテナを単体で動作するのではなく、複数のコンテナを利用するようなシステムを作成していきたいと思います。

では、docker-composeを利用してFlaskのWebアプリとnginxを組み合わせたシステムを構築してみましょう。

docker-composeで構築するシステムの構成

システムの構成図

docker-composeで全部で3つのdockerコンテナを配置し、ユーザからのアクセスはnginxで受付てURLにより対応する2台のWebサーバに振り分ける構成となっています。

コンテナ機能
nginx・ユーザからのアクセスを80ポートで待ち受ける
・http://localhost/web1/の場合はweb1サーバへ振り分ける
・http://localhost/web2/の場合はweb2サーバへ振り分ける
web1(Flask)・web1サーバのindex.htmlファイルの内容を出力
web2(Flask)・web2サーバのindex.htmlファイルの内容を出力

処理フロー例

システム構成図の通りですが、処理フローを順に追っていくとこうなります。

「http://localhost/web1」にアクセスした場合

  • ユーザが「http://localhost/web1/」にアクセスする
  • nginxは「http://localhost/web1/」にアクセスされた場合にweb1サーバの5001ポートへアクセスする
  • web1サーバはHTMLデータをnginxへ返却する
  • ユーザのブラウザにnginxから返却されたHTMLが表示される

 

「http://localhost/web2」にアクセスした場合

  • ユーザが「http://localhost/web2/」にアクセスする
  • nginxは「http://localhost/web2/」にアクセスされた場合にweb2サーバの5002ポートへアクセスする
  • web2サーバはHTMLデータをnginxへ返却する
  • ユーザのブラウザにnginxから返却されたHTMLが表示される

フォルダ/ファイル構成

Visual Studio Codeのスクリーンショットですが、以下のような構成になっています。

「docker_compose_demo」フォルダをルートとして「nginx」「web1」「web2」は各コンテナのフォルダです。

docker-composeの設計情報を記載するファイルである「docker-compose.yml」は「docker_compose_demo」フォルダの直下に配置しておきましょう。

それでは、ファイルのサンプルを順に説明します。

web1コンテナのファイル

app.py

web1コンテナのメインファイルです。

from flask import Flask, render_template, request

app = Flask(__name__)

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/<name>')
def hello(name):
    return render_template('hello.html', title='webサーバ1', name=name)

if __name__ == "__main__":
    app.run(debug=True, host='0.0.0.0', port=5000)

前回作成したFlaskアプリを流用しています。

ポイントだけ説明すると、

  • 「ルート(/)」にアクセスすると「index.html」の内容を返却する
  • 「/xxx」にアクセスすると「hello.html」の内容を返却する
  • 5000ポートで待ち受ける

です。

index.html

<html>
<title>docker demo flask</title>

<head></head>

<body>
    <h1>Webサーバ1</h1>
    <div>トップページ</div>

</body>

</html>

web1であることがわかるようなHTMLファイルであればどのような内容でも大丈夫です。

サンプルでは<h1>タグで「Webサーバ1」としました。

hello.html

<html>

<head>
    <title>{{ title }}</title>
</head>

<body>
    <h1>Web1サーバ</h1>
    <div>Hello!!</div>
    <div>{{ name }}さん</div>
</body>

</html>

時間がなければ、index.htmlだけでも大丈夫です。

requirements.txt

dockerコンテナを作成する際にインストールするpythonのパッケージを記載します。

「pip install -r requirements.txt」で指定するファイルです。

今回はFlaskを指定しておきましょう。

Flask==1.1.1

Dockerfile

web1のコンテナを作成するためのDockerfileです。

公開するポートは5000としておきます。

# python v3.6 をベースにDockerイメージを作成
FROM python:3.6

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

# カレントディレクトリのファイルをDockerコンテナの「/app」 ディレクトリにコピー
ADD . /app

# インストール
RUN pip install -r requirements.txt

# 外部に公開するポートを指定
EXPOSE 5000

# コンテナの実行コマンドを指定
CMD ["python", "app.py"]

web2コンテナのファイル

web1と全く別のWebアプリを作成してもよかったのですが、今回はdocker-composeを使ってnginxで振り分けることを木邸としているので、web2コンテナ用のファイルはweb1とほとんど同じにしました。

ただ、URLでWebサーバが振り分けされていることがわかるように以下の3つのファイルの「Webサーバ1」の部分を「Webサーバ2」に変更はしておいてください。

  • app.py
  • index.html
  • hello.html

nginxコンテナのファイル

default.conf

nginxのコンテナはURLによるWebサーバの振り分けを行うために、「default.conf」を準備しましょう。

server {
    listen       80;
    server_name  localhost;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }

    location /web1 {
        proxy_pass http://web1:5001/;
    }

    location /web2 {
        proxy_pass http://web2:5002/;
    }
}

設定内容は大きく4つです。

  • 80ポートで待ち受ける
  • 「http://localhost/」の場合は「/usr/share/nginx/html/index.html」を表示する
  • 「http://localhost/web1」の場合は「http://web1:5001/」へ転送する
  • 「http://localhost/web2」の場合は「http://web2:5002/」へ転送する
ポイント

web1コンテナとweb2コンテナのURLのホスト名部分は「localhost」や「127.0.0.1」ではなく、「web1」と「web2」にしておいてください。
理由については、次の「docker-compose.yml」ファイルで説明します。

docker-compose.yml

docker-compose.ymlファイルはdocker-composeコマンドで指定する設定ファイルです。

docker-composeはファイル名を指定しない場合「docker-compose.yml」をデフォルトで読み込みます。

version: "3"
services:
  nginx:
    image: nginx:latest
    container_name: nginx
    ports:
      - "80:80"
    volumes:
      - ./nginx/default.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - web1
      - web2
    networks:
      - nginx_network

  web1:
    build: ./web1
    container_name: web1
    ports:
      - 5001:5000
    networks:
      - nginx_network
  
  web2:
    build: ./web2
    container_name: web2
    ports:
      - 5002:5000
    networks:
      - nginx_network
  
networks:
  nginx_network:
    driver: bridge
ポイント

各コンテナ間の通信はサービス名で行われる
そのため、nginxからweb1コンテナへのアクセスは「http://web1:5001/」となる

nginx:

■image:docker imageとしてnginxの最新を指定
■ports:nginxのデフォルトポートの80をそのまま指定
■volumes:作成したdefault.confをコンテナへコピーします
■depends_on:コンテナ間で通信を可能にするためにサービス名を指定しますので、
 今回は、web1とweb2のコンテナを指定しましょう
■networks:指定しない場合はデフォルトのbridgeネットワークが作成されますが明示的に指定しておきます。

web1:

■build:Flaskで作成したWebアプリからコンテナイメージを作成するためBuildを指定します。
■ports:5001ポートとして公開います。※5000のままだとweb2と重複するため
■networks:nginxと同じ設定にします

web2:

webとほぼ同じ設定ですが、portsのみ5002とします。

docker-composeで実行/確認/停止する

docker-composeで実行する

早速、docker-composeでdockerコンテナ3つを稼働させましょう。

コマンドは以下の通りです。

docker-compose up -d

実行すると初回はnginxのイメージのダウンロードやweb1コンテナのビルドなどが実行されますが、2解明校は以下のように数秒で実行されます。

$ docker-compose up -d
Creating network "dockercomposedemo_nginx_network" with driver "bridge"
Creating web2 ... 
Creating web1 ... 
Creating web2
Creating web2 ... done
Creating nginx ... 
Creating nginx ... done

docker-compose でステータスを確認する

実行できたかを確認するために以下のコマンドを実行しましょう

docker-compose ps

以下のように表示されれば、無事起動しています。

$ docker-compose ps
Name          Command          State                Ports              
-----------------------------------------------------------------------
nginx   nginx -g daemon off;   Up      0.0.0.0:80->80/tcp              
web1    python app.py          Up      0.0.0.0:5001->5000/tcp, 5001/tcp
web2    python app.py          Up      0.0.0.0:5002->5000/tcp, 5002/tcp

ブラウザで確認してみましょう

3つのURLにアクセスして、

  • nginxのデフォルトトップページ
  • web1のページ
  • web2のページ

が表示されれば完成です。

 

http://localhost/

http://localhost/web1/

http://localhost/web2/

docker-composeで停止する

docker-composeでコンテナを停止する場合は以下のコマンドを実行します。

docker-compose down

正常に停止できれば以下のような表示になります

$ docker-compose down
Stopping nginx ... done
Stopping web1  ... done
Stopping web2  ... done
Removing nginx ... done
Removing web1  ... done
Removing web2  ... done
Removing network dockercomposedemo_nginx_network

まとめ

複数のコンテナを組み合わせてシステムを作成する場合、dockerのrunコマンドでも実現は可能です。

ですが、docker-composeを利用することで、複数コンテナをまとめて実行や停止できるため、docker開発には欠かせないツールですので、実際に手を動かして覚えておきたい技術ですね。

少し長くなりましたがdocker-composeの開発に役立つことができれば幸いです。

Docker,Flask

Posted by snow