③１日１０分で理解するコンテナ技術入門 - シンプルなWebアプリのDocker化 -

[前回の記事](https://qiita.com/goroneko/items/c9b3e7d5292128f72e13 "")では、Dockerfileの基本的な書き方を紹介しました。
今回は、シンプルなWebアプリをコンテナで起動してみて、Dockerの使われ方をイメージしていただければと思います。

# 今回使用するWebアプリ
今回使用するWebアプリは、下のようなシンプルな記事投稿サイトです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532202/8e78ecbb-b326-405c-4126-cfe95276e907.png)
ホームページに記事一覧が表示され、記事のタイトルをクリックすると、以下の様に記事が表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532202/4d96ffd1-2a5e-2561-061f-9e1912ccb00f.png)

実装にはFlaskとmysqlを使っています。コードは[ここ](https://github.com/sogawa-yk/simple-knowledge-sharing-platform/tree/3-article "")にあります。

# Flask側のコンテナ化
Flask側のDockerfileは以下のようになっています。
```Dockerfile
# ベースイメージとして公式のPythonイメージを使用
FROM python:3.9-slim

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

# 必要なファイルをコンテナにコピー
COPY requirements.txt requirements.txt
COPY app.py app.py
COPY custom_markdown.py custom_markdown.py
COPY templates/ templates/
COPY static/ static/
COPY uploads/ uploads/

# 依存関係をインストール
RUN pip install -r requirements.txt

# アップロードディレクトリのパーミッションを設定
RUN chmod -R 777 /app/uploads

# コンテナの起動時に実行されるコマンド
CMD ["python", "app.py"]
```
前回の記事で紹介した基本的なコマンドのみ使用しています。

# MySQLのコンテナ化
MySQLは公式で配布されているイメージをそのまま使用します。そのため、Dockerfileは不要です。

# それぞれのコンテナをビルドして起動
コンテナとして動かすには、以下の手順を踏む必要があります。
1. コンテナ同士が通信できるように、ネットワークを作成
1. MySQLのコンテナを作成
1. Flaskのコンテナを作成

## docker networkの作成
FlaskのコンテナとMySQLのコンテナは通信する必要があるので、そのためのネットワークを作成し、それぞれのコンテナをそのネットワークに配置します。
まずは、ネットワークを`docker network`コマンドで作成します。
```bash
docker network create flask-net
```
このコマンドでネットワークが作成されました。コンテナをdocker networkに配置するのは、`docker run`時に行うため、この後説明します。

## MySQLのコンテナを起動
MySQLありきでFlask側は実装されているので、MySQLのコンテナから起動します。MySQLのコンテナは、以下のコマンドで起動します。
```bash
docker run --name mysql -e MYSQL_ROOT_PASSWORD=password -e MYSQL_DATABASE=flask_app --network flask-net -d mysql:8.0
```
MySQLのコンテナは、データベースのパスワードなどを環境変数に設定して使用するので、`docker run`時に指定しています。また、`--network flask-net`をオプションとして加えて、先ほど作成したdocker networkに配置します。

## Flaskのコンテナを起動
以下のコマンドでDockerfileからFlaskアプリのイメージをビルドし、実行します。
```bash
docker build -t simple-knowledge-sharing-platform .
docker run -d -p 8080:5000 --network flask-net -v $(pwd)/uploads:/app/uploads simple-knowledge-sharing-platform
```
flaskアプリはポート5000で待ち受けているので、`-p 8080:5000`オプションを使って、ホストの8080にポートフォワーディングします。また、MySQLのコンテナと同様に、`--network flask-net`オプションを使って作成したdocker networkにコンテナを配置します。

今回のWebアプリは画像のアップロードにも対応しています。画像の保存はとりあえずFlaskのコンテナ側に保存するので、`-v $(pwd)/uploads:/app/uploads`オプションを用いて、コンテナの`/app/uploads`をホストの`$(pwd)/uploads`にマウントしています。この設定によって、flaskアプリ側では`/app/uploads`に画像を保存しますが、その実態はホストの`$(pwd)/uploads`に保存されています。

## コンテナ同士の通信について
同じdocker networkに所属しているコンテナ同士は、コンテナ名をホスト名として通信することができます。そのため、Flask側では以下のようにDBのホスト名を`mysql`として指定していますが、これで通信ができるのはMySQLのコンテナを作成する際にコンテナ名を`mysql`としているためです。
```python:app.pyの一部
# データベースの初期化
def init_db():
    conn = mysql.connector.connect(
        host='mysql',
        port=3306,
        user='root',
        password='password',
        database='flask_app'
    )
    cursor = conn.cursor()
    cursor.execute('CREATE TABLE IF NOT EXISTS articles (id INT AUTO_INCREMENT PRIMARY KEY, title VARCHAR(255), content TEXT)')
    conn.commit()
    cursor.close()
    conn.close()
```
また、ポート番号はコンテナのポート番号を指定して通信します。そのため、MySQLのコンテナを作成する際にポートフォワーディングをする必要はなく、MySQLのコンテナでデフォルトで待ち受けている3306番を使用して通信が可能です。

# まとめ
以上の手順を踏むことで、簡単にウェブアプリをデプロイすることができます。ただし、コンテナの作成順序を考慮して起動しないといけないなど、多少手間になる部分が見えたと思います。こういった部分を解決するために、docker composeというものがあるのですが、それはまた後日紹介します。

現状では、ローカルにウェブアプリが立ち上がっただけの状態です。インターネット上に公開するためには、リモートのレポジトリにコンテナイメージをプッシュして、インターネット上にウェブアプリを公開する必要があります。[次回の記事](https://qiita.com/goroneko/items/413f8477e449da712137)で、その手順を説明したいと思います。