24
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

DockerでNGINX + NGINX Unit + MySQLの環境を構築

Posted at

Dockerの導入

以前、NGINX + NGINX Unit + Flaskの環境を構築しました。

NGINX + NGINX Unit + Flask で PythonのWeb アプリを動かす

前回はVagrantとVirtualBoxで構築しましたが、今回はDockerで構築していこうと思います。
前回と同様に、WebサーバーにNGINXを、APサーバーにNGINX Unitを、フレームワークにFlaskを使用します。
更に今回はデータベースとしてMySQLを追加し、WEB <-> AP <-> DBの環境を構築をしていきます。

以下の図のようなイメージです。(本来コンテナはホスト内で起動しますが、わかりやすように分けています)

docker1.png

順を追って構築していくため、できたものをみたい方はGitHubに上げてありますのでそちらをどうぞ。

https://github.com/txkxyx/docker-web

環境

以下の環境で構築していきます。

  • ホスト
    • OS : macOS Catalina 10.15.3
    • Docker : 19.03.5
  • コンテナ
    • Python : 3.7.3
    • Flask : 1.1.1
    • NGINX : 1.17.7
    • NGINX Unit : 1.14.0
    • MySQL : 8.0.18
    • Flask SQLAclchemy : 2.4.1

ディレクトリ構成は以下のようにします。

./web
    |- db                   // DB用
    |- nginx                // NGINX用
    |- python               // NGINX Unit・ソースファイル用
    |    |- src
    |- docker-compose.yml

では始めていきます。

Dockerfiletとdocker-composeの設定値

Dokerfileとdocker-composeで使用する設定値を簡単にまとめておきます。

Dockerfileの設定値

詳しくは公式のDockerfileリファレンスを参照してください。

https://docs.docker.com/engine/reference/builder/

設定値 概要
FROM 使用するイメージを指定する。
WORKDIR 作業ディレクトリを指定する。この宣言以降はコンテナ内の指定したパスで作業を行う。
COPY ホストからコンテナに、指定したディレクトリやファイルをコピーする。ホスト コンテナの順で指定する。.dockerignoreで指定したファイルは対象外となる。
RUN 指定したコマンドを現時点のコンテナ内で実行する。(ビルド時に実行するコマンド)
CMD コンテナ起動時に実行するコマンドを指定する。(起動時に実行するコマンド)

docker-composeの設定値

詳しくは公式のリファレンスを参照してください。

https://docs.docker.com/compose/compose-file/

設定値 概要
version Docker Engineが対応するファイルフォーマットのバージョン
services アプリケーションを構成する各要素
build 起動するコンテナのDockerfileがあるディレクトリを指定。子要素で、context(DockerfileのあるディレクトリまたはGithubURL)args(Dockerfileに渡す引数)などを指定できる 。
image 起動するコンテナが使用するイメージを指定する。
command docker-compose up を実行した際に実行されるコマンド
ports コンテナが公開するポートを指定します。ホスト:コンテナもしくはコンテナのポートのみ指定します。
expose リンクするコンテナのみに公開するコンテナのポートを指定します。ホストには公開されません。
environment 起動するコンテナの環境変数を指定します。
volumes コンテナにマウントするホストのディレクトリを指定します。ホスト:コンテナの形式でパスを指定します。
container_name 起動するコンテナのコンテナ名を指定します。
depends_on サービス間の依存関係を指定します。指定したサービス名が先に起動します。

DBコンテナの構築

まずはMySQLのコンテナを構築していきます。
イメージはこんな感じです。

docker2.png

docker-compose.ymlを作成します。

web/docker-compose.yml
version: "3"

services:
    db:
        image: mysql
        command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
        ports:
            - "33306:3306"
        expose:
            - "3306"
        environment:
            MYSQL_ROOT_PASSWORD: root
            MYSQL_USER: test
            MYSQL_PASSWORD: test
        volumes:
            - ./db/init:/docker-entrypoint-initdb.d
        container_name: app_db

コンテナの初期起動時にデータベースを作成するように、dbディレクトリ内にinitディレクトリを作成し、createdatabase.sqlを作成します。

web/db/init/createdatabase.sql
CREATE DATABASE app;
USE app;

CREATE TABLE users(
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(255),
    email VARCHAR(255)
);

INSERT INTO users(name,email) VALUES('sample','sample@sample.com');
INSERT INTO users(name,email) VALUES('test','test@test.com');
INSERT INTO users(name,email) VALUES('app','app@app.com');

GRANT ALL ON app.* TO test;

以上の設定でdocker-composeでMySQLのコンテナを起動します。

$ docker-compose up
$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                                NAMES
bef9a864276c        mysql               "docker-entrypoint.s…"   4 minutes ago       Up 4 minutes        33060/tcp, 0.0.0.0:33306->3306/tcp   app_db

docker psの結果にapp_dbが表示されればコンテナを起動できています。
一度コンテナ内に入って、データベースが作成されているかを確認します。

$ docker exec -it app_db bash
root@00000000000:/# mysql -u test -p
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| app                |
| information_schema |
+--------------------+
2 rows in set (0.00 sec)
mysql> use app;
mysql> select * from users;
+----+--------+-------------------+
| id | name   | email             |
+----+--------+-------------------+
|  1 | sample | sample@sample.com |
|  2 | test   | test@test.com     |
|  3 | app    | app@app.com       |
+----+--------+-------------------+
3 rows in set (0.01 sec)

appというデータベースが作成されていることが確認できます。更に、createdatabase.sqlのテーブルとデータが作成されていることが確認できます。
MySQLの構築は以上です。

APコンテナの構築

APサーバーとしてNGINX Unitを、実行環境にPython3を、フレームワークとしてFlaskを使用したコンテナを構築します。
NGINX Unitの公式ドキュメントを参考に構築していきます。
イメージはこんな感じです。

docker3.png

NGINX Unitのコンテナの起動

まずは、NGINX UnitのイメージからPython3とFlaskの環境を構築します。開発環境の最小単位はこれでいいかもしれません。
web/pythonディレクトリにDockerfileを追加します。

web/python/Dorckerfile
FROM nginx/unit:1.14.0-python3.7

WORKDIR /usr/src/app

COPY src .

RUN apt update && apt install -y python3-pip                               \
    && pip3 install --no-cache-dir -r ./requirements.txt                            \
    && rm -rf /var/lib/apt/lists/* 

CMD ["sleep","infinity"]

Docker Hubのnginx/unitのサイトから、NGINX UnitのPython3.7用のイメージを使用します。

次に、pipでライブラリを一括でインストールできるように、web/python/srcディレクトリにrequirements.txtを作成します。

web/python/src/requirements.txt
Flask == 1.1.1
flask-sqlalchemy == 2.4.1
PyMySQL == 0.9.3

docker-compose.ymlにNGINX Unitのコンテナの設定を追記します。

docker-compose.yml
version: "3"

services:
    db:
        image: mysql
        ports:
            - "33306:3306"
        expose:
            - "3306"
        environment:
            MYSQL_ROOT_PASSWORD: root
            MYSQL_USER: test
            MYSQL_PASSWORD: test
        volumes:
            - ./db/init:/docker-entrypoint-initdb.d
        container_name: app_db
    # ↓↓追記
    ap:
        build: ./python
        ports:
            - "8080:8080"
        environment:
            TZ: "Asia/Tokyo"
        container_name: app_ap
        depends_on:
            - db

作成した'Dockerfile'はweb/pythonのディレクトリに存在するので、buildでその場所を指定します。
サーバーのポートは8080をホストに公開するようにします。
起動しているDockerコンテナを停止してから、docker-compose buildでビルドしてからコンテナを起動します。

$ docker-compose down
$ docker-compose build --no-cache
$ docker-compose up
$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                                NAMES
daf4ddc7c11a        web_ap              "sleep infinity"         41 seconds ago      Up 40 seconds       0.0.0.0:8080->8080/tcp               app_ap
565eb32e6a39        mysql               "docker-entrypoint.s…"   43 seconds ago      Up 41 seconds       33060/tcp, 0.0.0.0:33306->3306/tcp   app_db

MySQLのコンテナのapp_dbと、NGINX Unitのコンテナのapp_apが起動していることが確認できます。
NGINX Unitのコンテナに入り、requirements.txtのライブラリがインストールされているかを確認します。

$ docker exec -it app_ap bash
root@00000000000:/# python3 -V
Python 3.7.3
root@00000000000:/# pip3 freeze
Flask==1.1.1
Flask-SQLAlchemy==2.4.1
PyMySQL==0.9.3

上記のライブラリ以外にも、SQLAlchemyJinja2などがインストールされています。
ここまでNGINX Unitのコンテナの起動は完了です。続いて、Flaskの実装を行います。

Flaskアプリケーションの実装

Flaskアプリケーションの実装をします。作成するファイルとディレクトリは以下のようになります。

./web
    |- db
    |   |- init
    |       |- createdatabase.sql
    |- nginx  
    |- python
    |   |- src
    |   |   |- app.py           ← 追加
    |   |   |- config.json      ← 追加
    |   |   |- config.py        ← 追加
    |   |   |- run.py           ← 追加
    |   |   |- users.py         ← 追加
    |   |   |- requirements.txt
    |   |   |- templates        ← 追加
    |   |       |- list.html    ← 追加
    |   |- Dockerfile           ← 更新
    |- docker-compose.yml

各ファイルは以下のように実装します。

config.py

まずは、DBの接続先などの設定クラスを実装するconfig.pyです。
ホスト先は、DBコンテナのコンテナ名app_dbで指定します。

web/python/src/config.py

class Config(object):
    '''
    Config Class
    '''
    # DB URL
    SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://test:test@app_db:3306/app?charset=utf8'

app.py

次にFlaskアプリケーションを起動するapp.pyです。
config.from_object()でアプリケーションの設定クラスを呼び込み、SQLAlchemy()でFlaskアプリケーションでSQLAchemyが使えるように初期化を行います。

web/python/src/app.py
from config import Config
from flask import Flask
from flask_sqlalchemy import SQLAlchemy

# Create Flask Application
application = Flask(__name__)

# Set Config Class
application.config.from_object(Config)

# Set DB
db = SQLAlchemy(application)

users.py

次にusersテーブルのModelクラスを作成します。
db.Modelクラスを継承したUsersクラスを作成します。

web/python/src/users.py
from app import db

class Users(db.Model):
    '''
    Users Table Model
    '''
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(255))
    email = db.Column(db.String(255))

    def __init__(self,name,email):
        self.name = name
        self.email = email

run.py

次に、Flaskアプリケーションの起動とルーティングのモジュールrun.pyです。
レスポンスにテンプレートファイルを使用するので、render_template()でテンプレートファイルとオブジェクトを指定します。

web/python/src/run.py
from app import application
from users import Users
from flask import render_template

@application.route('/list')
def index():
    users = Users.query.order_by(Users.id).all()
    return render_template('list.html', users=users)

if __name__ == '__main__':
    application.run(host='0.0.0.0', port='8080')

list.html

次にテンプレートファイルとなるlist.htmlを作成します。
render_template()usersオブジェクトが渡されるので、テンプレートエンジンであるJinja2を使用して実装します。

web/python/src/templates/list.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Flask Sample</title>
</head>
<body>
    <h1>Flask Sample</h1>
    <table border="1" style="border-collapse: collapse">
        <thead>
            <tr>
                <th >Id</th>
                <th >Name</th>
                <th >EMail</th>
            </tr>
        </thead>
        <tbody>
            {% for user in users %}
                <tr>
                    <td>{{user.id}}</td>
                    <td>{{user.name}}</td>
                    <td>{{user.email}}</td>
                </tr>
            {% endfor %}
        </tbody>
    </table>
</body>
</html>

Dockerfile

Dockerfileを更新します。

web/python/Dorckerfile
FROM nginx/unit:1.14.0-python3.7

WORKDIR /usr/src/app

COPY src .

RUN apt update && apt install -y python3-pip                               \
    && pip3 install --no-cache-dir -r ./requirements.txt                            \
    && rm -rf /var/lib/apt/lists/*
# ↓↓削除

config.json

最後にNGINX Unitの設定ファイルconfig.jsonを追加します。

web/python/src/config.json
{
  "listeners": {
    "*:8080": {
      "pass": "applications/app"
    }
  },

  "applications": {
    "app": {
      "type": "python",
      "processes": 2,
      "path": "/usr/src/app/",
      "module": "run"
    }
  }
}

実装は以上です。

ビルドしてからコンテナを起動してみましょう。

$ docker-compose down
$ docker-compose build --no-cache
$ docker-compose up
$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                                NAMES
daf4ddc7c11a        web_ap              "sleep infinity"         41 seconds ago      Up 40 seconds       0.0.0.0:8080->8080/tcp               app_ap
565eb32e6a39        mysql               "docker-entrypoint.s…"   43 seconds ago      Up 41 seconds       33060/tcp, 0.0.0.0:33306->3306/tcp   app_db

app_apのコンテナの起動後に、コンテナにアクセスしてNGINX Unitの設定ファイルを設定します。

$ docker exec -it app_ap bash
root@00000000000:/# curl -X PUT --data-binary @config.json --unix-socket /var/run/control.unit.sock http://localhost/config
{
	"success": "Reconfiguration done."
}

ブラウザで、http://localhost:8080/listにアクセスして画面が表示されます。

docker4.png

これでAPコンテナの構築は終了です。

Webコンテナの構築

最後にWEBサーバーのNGINXのコンテナを構築していきます。
これで NGINX <-> NGINX Unit <-> Flask <-> MySQLの構成になります。

docker1.png

追加、更新するファイルは以下のようになります。

./web
    |- db
    |   |- init
    |       |- createdatabase.sql
    |- nginx
    |   |- Dockerfile       ← 追加
    |   |- index.html       ← 追加
    |   |- nginx.conf       ← 追加
    |- python
    |   |- src
    |   |   |- __init__.py
    |   |   |- app.py
    |   |   |- config.json
    |   |   |- config.py
    |   |   |- run.py
    |   |   |- users.py
    |   |   |- requirements.txt
    |   |   |- templates
    |   |       |- index.html
    |   |- Dockerfile
    |- docker-compose.yml   ← 更新

まずはトップページとなるindex.htmlを作成します。

web/nginx/index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Index</title>
</head>
<body>
    <h1>Index</h1>
    <a href="/list">List</a>
</body>
</html>

次にNGINXDockerfileを作成します。

FROM nginx

WORKDIR /var/www/html

COPY ./index.html ./

CMD ["nginx", "-g", "daemon off;","-c","/etc/nginx/nginx.conf"]

次に、NGINXの設定ファイルを作成します。前回の記事で紹介した設定ファイルから、APサーバーのホストをDocker用に変更します。

nginx.conf
user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                  '$status $body_bytes_sent "$http_referer" '
                  '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;
    server_tokens off;

    keepalive_timeout  65;

    #gzip  on;

    upstream unit-python {
        server app_ap:8080; # container_nameで指定
    }
    server {
        listen 80;
        server_name localhost;

        # トップページを表示
        location  / {
            root /var/www/html;
        }

        # /listはAPコンテナにルーティング
        location  /list {
            proxy_pass http://unit-python;
            proxy_set_header Host $host;
        }
    }
}

最後にdocker-composeを更新します。

docker-compose.yml
version: "3"

services:
    db:
        image: mysql
        ports:
            - "33306:3306"
        expose:
            - "3306"
        environment:
            MYSQL_ROOT_PASSWORD: root
            MYSQL_USER: test
            MYSQL_PASSWORD: test
        volumes:
            - ./db/init:/docker-entrypoint-initdb.d
        container_name: app_db

    ap:
        build:
            context: ./python
            args:
                project_directory: "/src/"
        # ↓↓更新
        expose:
            - "8080"
        volumes:
            - "./python/src:/projects"
        environment:
            TZ: "Asia/Tokyo"
        container_name: app_ap
        depends_on:
            - db
    # ↓↓追加
    web:
        build: ./nginx
        volumes:
            - ./nginx/nginx.conf:/etc/nginx/nginx.conf
        ports:
            - "80:80"
        environment:
            TZ: "Asia/Tokyo"
        container_name: "app_web"
        depends_on:
            - ap

ビルドしてからコンテナを起動してみましょう。

$ docker-compose down
$ docker-compose build --no-cache
$ docker-compose up
$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                                NAMES
5b0f06b89db4        web_web             "nginx -g 'daemon of…"   2 minutes ago       Up 23 seconds       0.0.0.0:80->80/tcp                   app_web
625f3c025a82        web_ap              "/usr/local/bin/dock…"   2 minutes ago       Up 2 minutes        8080/tcp                             app_ap
fe5bf54411a2        mysql               "docker-entrypoint.s…"   2 minutes ago       Up 2 minutes        33060/tcp, 0.0.0.0:33306->3306/tcp   app_db

app_apのコンテナの起動後に、コンテナにアクセスしてNGINX Unitの設定ファイルを設定します。

$ docker exec -it app_ap bash
root@00000000000:/# curl -X PUT --data-binary @config.json --unix-socket /var/run/control.unit.sock http://localhost/config
{
	"success": "Reconfiguration done."
}

ブラウザで、http://localhost:80にアクセスするとトップページのindex.htmlが表示されます。(8080ポートではアクセスできません)

docker5.png

リンクとなっているListを押下すると、Flaskアプリケーションのlist.htmlが表示されます。

docker6.png

以上でNGINXの構築は終了です。

まとめ

DockerでNGINX + NGINX Unit + MySQLの環境を構築することができました。
あとはアプリケーションを作り込むだけです。

24
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
24
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?