前回の記事では、Dockerfileの基本的な書き方を紹介しました。
今回は、シンプルなWebアプリをコンテナで起動してみて、Dockerの使われ方をイメージしていただければと思います。
今回使用するWebアプリ
今回使用するWebアプリは、下のようなシンプルな記事投稿サイトです。
ホームページに記事一覧が表示され、記事のタイトルをクリックすると、以下の様に記事が表示されます。
実装にはFlaskとmysqlを使っています。コードはここにあります。
Flask側のコンテナ化
Flask側の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は不要です。
それぞれのコンテナをビルドして起動
コンテナとして動かすには、以下の手順を踏む必要があります。
- コンテナ同士が通信できるように、ネットワークを作成
- MySQLのコンテナを作成
- Flaskのコンテナを作成
docker networkの作成
FlaskのコンテナとMySQLのコンテナは通信する必要があるので、そのためのネットワークを作成し、それぞれのコンテナをそのネットワークに配置します。
まずは、ネットワークをdocker network
コマンドで作成します。
docker network create flask-net
このコマンドでネットワークが作成されました。コンテナをdocker networkに配置するのは、docker run
時に行うため、この後説明します。
MySQLのコンテナを起動
MySQLありきでFlask側は実装されているので、MySQLのコンテナから起動します。MySQLのコンテナは、以下のコマンドで起動します。
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アプリのイメージをビルドし、実行します。
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
としているためです。
# データベースの初期化
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というものがあるのですが、それはまた後日紹介します。
現状では、ローカルにウェブアプリが立ち上がっただけの状態です。インターネット上に公開するためには、リモートのレポジトリにコンテナイメージをプッシュして、インターネット上にウェブアプリを公開する必要があります。次回の記事で、その手順を説明したいと思います。