開発向けのコンテナー利用

読む時間の目安: 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 パイプラインを設定する方法を見ていきます。 以下を参照してください。

CI/CD の設定

フィードバック

本トピック改善のためにフィードバックをお寄せください。 お気づきの点があれば Docker Docs の GitHub リポジトリに issue をあげてください。 あるいは PR の生成 により変更の提案を行ってください。

python, local, development, run