開発向けのコンテナー利用
読む時間の目安: 5 分
前提条件
コンテナーとしてのイメージ実行 において、イメージビルドを行いコンテナー化アプリケーションを実行していること。
はじめに
本節では前節においてビルドしたアプリケーション用に、ローカルの開発環境を構築していきます。 イメージのビルドには Docker を利用し、全体を取り扱いやすくするために Docker Compose を利用していきます。
コンテナー内でのデータベース実行
まずはコンテナー内でデータベースを動作させます。 ボリュームやネットワークを使ってデータの保存を行い、アプリケーションとデータベースのやりとりを実現します。 そしてこのすべてをとりまとめて Compose ファイルに収めます。 こうすることで、たった 1 つのコマンド実行によってローカル開発環境を構築して実行できるようにします。 最後に、コンテナー内で動作しているアプリケーションに対してデバッガー接続を行ってみます。
MySQL をダウンロード、インストールして、MySQL データベースをサービスとして起動するようなことはしません。 そうではなく Docker の公式イメージの中から MySQL 用のイメージを利用し、コンテナーとしてこれを実行します。
MySQL をコンテナーとして実行するにあたって、いくつかボリュームを生成して Docker がデータや設定の保存ができるようにします。 バインドマウントは利用せず、管理されたボリューム機能を利用します。 ボリュームに関しては本ドキュメントの ボリュームの利用 において詳しく説明しています。
それではここでボリュームを生成します。 作り出すボリュームは 1 つは MySQL のデータ用、そしてもう 1 つは MySQL の設定用です。
$ docker volume create mysql
$ docker volume create mysql_config
またここでネットワークを生成して、アプリケーションとデータベースが互いにやりとりできるようにします。 ネットワークはユーザー定義によるブリッジネットワークであり、DNS 検索サービスが提供されるため、接続文字列を使った設定が利用可能になります。
$ docker network create mysqlnet
コンテナーとして MySQL を実行します。
そして上で生成したボリュームとネットワークをこれに結びつけます。
Docker はイメージを Docker Hub からプルして、ローカル環境において実行します。
以下のコマンドにおいては-v
オプションの指定により、ボリュームを使ってコンテナーを起動します。
詳しくは Docker ボリューム を参照してください。
$ docker run --rm -d -v mysql:/var/lib/mysql \
-v mysql_config:/etc/mysql -p 3306:3306 \
--network mysqlnet \
--name mysqldb \
-e MYSQL_ROOT_PASSWORD=p@ssw0rd1 \
mysql
MySQL データベースが起動されていて、そこに接続できることを確認します。 コンテナー内部から以下のコマンドを実行して、実行中の MySQL データベースに接続します。 なおパスワードプロンプトには「p@ssw0rd1」を入力します。
$ docker exec -ti mysqldb mysql -u root -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 8
Server version: 8.0.23 MySQL Community Server - GPL
Copyright (c) 2000, 2021, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql>
アプリケーションからのデータベース接続
上のコマンドにおいては、mysqldb
コンテナーに対してmysql
コマンドを実行して MySQL データベースにログインしました。
CTRL-D を入力して MySQL のインタラクティブターミナルから抜け出てください。
次は イメージのビルド の節において生成したサンプルアプリケーションを更新します。 Python アプリのディレクトリ構造を確認するには、Python アプリケーションのディレクトリ構造 を参照してください。
さて MySQL が動作したので、app.py
を修正しデータ保存先を MySQL とします。
さらにサーバーへの接続内容も追加することにします。
1 つはレコード取得であり、1 つはレコード挿入です。
import mysql.connector
import json
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello, Docker!'
@app.route('/widgets')
def get_widgets():
mydb = mysql.connector.connect(
host="mysqldb",
user="root",
password="p@ssw0rd1",
database="inventory"
)
cursor = mydb.cursor()
cursor.execute("SELECT * FROM widgets")
row_headers=[x[0] for x in cursor.description] #this will extract row headers
results = cursor.fetchall()
json_data=[]
for result in results:
json_data.append(dict(zip(row_headers,result)))
cursor.close()
return json.dumps(json_data)
@app.route('/initdb')
def db_init():
mydb = mysql.connector.connect(
host="mysqldb",
user="root",
password="p@ssw0rd1"
)
cursor = mydb.cursor()
cursor.execute("DROP DATABASE IF EXISTS inventory")
cursor.execute("CREATE DATABASE inventory")
cursor.close()
mydb = mysql.connector.connect(
host="mysqldb",
user="root",
password="p@ssw0rd1",
database="inventory"
)
cursor = mydb.cursor()
cursor.execute("DROP TABLE IF EXISTS widgets")
cursor.execute("CREATE TABLE widgets (name VARCHAR(255), description VARCHAR(255))")
cursor.close()
return 'init database'
if __name__ == "__main__":
app.run(host ='0.0.0.0')
ここでは MySQL モジュールを追加して、データベースサーバーに接続するようなコードに変更しました。 データベースとテーブルも生成しています。 またサーバーへのアクセス方法として、レコード保存のためのウィジェット、レコード取得のためのウィジェットを生成しました。 そこでこの変更を含むイメージを再ビルドします。
まずは pip を使ってアプリケーションにmysql-connector-python
モジュールを追加します。
$ pip3 install mysql-connector-python
$ pip3 freeze | grep mysql-connector-python >> requirements.txt
そしてイメージをビルドします。
$ docker build --tag python-docker-dev .
ではこのコンテナーをデータベースのネットワークに追加して実行します。 これによってデータベースへのアクセスは、コンテナー名を使って行えるようになります。
$ docker run \
--rm -d \
--network mysqlnet \
--name rest-server \
-p 8000:5000 \
python-docker-dev
アプリケーションがデータベースに接続されていて、メモ書き(note)が追加できることを確認します。
$ curl http://localhost:8000/initdb
$ curl http://localhost:8000/widgets
サービスからは以下のような JSON データが返ってくるはずです。
[]
Compose を使ったローカル開発環境
ここでは Compose ファイル を生成して、1 つのコマンド実行から python-docker と MySQL を起動できるようにします。
なおpython-docker-dev
はデバッグモードで起動するような Compose ファイルとして、稼働するノードプロセスに対してデバッガーを接続できるようにしておきます。
IDE あるいはテキストエディターにおいてpython-docker
ディレクトリを開いて、docker-compose.dev.yml
というファイルを新規生成します。
そのファイルに以下の記述をコピーおよび貼りつけます。
version: '3.8'
services:
web:
build:
context: .
ports:
- 8000:5000
volumes:
- ./:/app
mysqldb:
image: mysql
ports:
- 3306:3306
environment:
- MYSQL_ROOT_PASSWORD=p@ssw0rd1
volumes:
- mysql:/var/lib/mysql
- mysql_config:/etc/mysql
volumes:
mysql:
mysql_config:
この Compose ファイルは非常に便利なものです。
docker run
コマンドの実行にあたって、パラメーターすべてを記述する必要はありません。
それは Compose ファイル内に宣言という形で行います。
ポート 8000 を公開することで、コンテナー内部にある開発ウェブサーバーにアクセスできるようにします。 またローカルにあるソースコードを実行コンテナー内にもマッピングして、テキストエディター上での変更を可能とし、その変更をコンテナー内に反映されるようにします。
Compose ファイルを利用する理由となるもう一つ優れているのは、サービス名を使ってサービス設定の解決を行うことができる点です。 だから接続文字列の中で「mysqldb」という名称を用いることができるのです。 「mysqldb」という名称をなぜ用いるかと言えば、Compose ファイル内にて MySQL サービスに対してそのような名前づけを行ったからです。
アプリケーションを起動して正常に動作していることを確認するために、以下のコマンドを実行します。
$ docker-compose -f docker-compose.dev.yml up --build
ここでは--build
フラグを指定しました。
これによって Docker はイメージをコンパイルした上で起動を行います。
では API エンドポイントを確認します。 新たな端末を開き、curl コマンドを使って、サーバーへ GET リクエストを投げてみます。
$ curl http://localhost:8000/initdb
$ curl http://localhost:8000/widgets
以下のようなレスポンスが返ってくるはずです。
[]
この結果になったのは、データベースが空であるからです。
次のステップ
本節では汎用的な開発イメージを生成しました。 このイメージは、ふだんのコマンドラインツールと何ら変わらずに実行できました。 また Compose ファイルを生成してソースコードを実行コンテナー内にマッピングし、デバッグ用のポートを公開しました。
次節では GitHub アクションを使って CI/CD パイプラインを設定する方法を見ていきます。 以下を参照してください。
フィードバック
本トピック改善のためにフィードバックをお寄せください。 お気づきの点があれば Docker Docs の GitHub リポジトリに issue をあげてください。 あるいは PR の生成 により変更の提案を行ってください。
python, local, development, run