コンテナ化された Python 開発 – パート 2

これは、Python 開発をコンテナー化する方法に関する ブログ投稿シリーズの 第 2 部です。 パート 1 では、Python サービスをコンテナー化する方法とそのベスト プラクティスを既に示しました。 このパートでは、他のコンポーネントを設定してコンテナ化されたPythonサービスに接続する方法について説明します。 プロジェクト ファイルとデータを整理する優れた方法と、Docker Compose を使用してプロジェクト全体の構成を管理する方法を示します。 また、コンテナー化された開発プロセスを高速化するための Compose ファイルを作成するためのベスト プラクティスについても説明します。

Docker Composeを使用したプロジェクト構成の管理

例として、マイクロサービス アーキテクチャに従って機能を 3 層に分けるアプリケーションを取り上げましょう。 これは、マルチサービス アプリケーションではかなり一般的なアーキテクチャです。 サンプル アプリケーションは、次のもので構成されています。

  • UI 層 – nginx サービスで実行される
  • ロジック 層 – 私たちが焦点を当てているPythonコンポーネント
  • データ 層 – MySQLデータベースを使用して、ロジック層に必要なデータを保存します
コンテナ化された python 開発 2 1

アプリケーションを階層に分割する理由は、プロジェクト全体を作り直すことなく、新しい階層を簡単に変更または追加できるためです。

プロジェクト ファイルを構造化する良い方法は、各サービスのファイルと構成を分離することです。 これは、プロジェクト内のサービスごとに専用のディレクトリを用意することで簡単に実行できます。 これは、コンポーネントを明確に表示し、各サービスを簡単にコンテナー化する場合に非常に便利です。 また、他のサービスファイルを誤って変更する可能性があることを心配することなく、サービス固有のファイルを操作するのに役立ちます。

サンプルアプリケーションには、次のディレクトリがあります。

Project
├─── web
└─── app
└─── db

Python コンポーネントをコンテナー化する方法については、このブログ投稿シリーズの最初の部分で既に説明しました。 同じことが他のプロジェクトコンポーネントにも当てはまりますが、ここで説明する構造を実装するサンプルに簡単にアクセスできるため、詳細は省略します。 awesome-compose リポジトリによって提供される nginx-flask-mysql の例はそれらの1つです。 

これは、Dockerfile が配置された更新されたプロジェクト構造です。 Webコンポーネントとdbコンポーネントに同様の設定があるとします。

Project
├─── web
├─── app
│ ├─── Dockerfile
│ ├─── requirements.txt
│ └─── src
│ └─── server.py
└─── db

これで、コンテナー化されたすべてのプロジェクト コンポーネントに対してコンテナーを手動で開始できるようになりました。 ただし、それらを通信させるには、ネットワークの作成を手動で処理し、コンテナをそれにアタッチする必要があります。 これはかなり複雑で、頻繁に行う必要がある場合は貴重な開発時間がかかります。

ここで、Docker Composeは、ローカル環境でコンテナを調整し、サービスをスピンアップおよび停止するための非常に簡単な方法を提供します。このために必要なのは、プロジェクトのサービスの構成を含むComposeファイルを作成することだけです。 それができたら、1つのコマンドでプロジェクトを実行できます。

ファイルの作成

Composeファイルの構造と、それを使用してプロジェクトサービスを管理する方法を見てみましょう。

以下は、プロジェクトのサンプルファイルです。 ご覧のとおり、サービスのリストを定義します。 dbセクションでは、適用する特定の構成がないため、基本イメージを直接指定します。 一方、私たちのWebおよびアプリサービスは、Dockerファイルからイメージを構築します。 サービスイメージを取得できる場所に応じて、ビルドフィールドまたはイメージフィールドを設定できます。 ビルド フィールドには、内部に Dockerfile を含むパスが必要です。

docker-compose.yaml

version: "3.7"
services:
  db:
    image: mysql:8.0.19
    command: '--default-authentication-plugin=mysql_native_password'
    restart: always
    environment:
      - MYSQL_DATABASE=example
      - MYSQL_ROOT_PASSWORD=password

  app:
    build: app
    restart: always

  web:
    build: web
    restart: always
    ports:
      - 80:80

データベースを初期化するには、環境変数をDB名とパスワードで渡すことができますが、Webサービスの場合は、プロジェクトのWebインターフェイスにアクセスできるようにコンテナポートをローカルホストにマップします。

Docker Composeを使用してプロジェクトをデプロイする方法を見てみましょう。 

ここで行う必要があるのは、プロジェクトのルートディレクトリにdocker-compose.yamlを配置してから、docker-composeを使用してデプロイするためのコマンドを発行することだけです。

Project
├─── docker-compose.yaml
├─── web
├─── app
└─── db

Docker Composeは、Docker Hubからmysqlイメージをプルしてdbコンテナを起動し、Webおよびアプリサービスの場合は、イメージをローカルでビルドしてから、それらからコンテナを実行します。また、デフォルトのネットワークを作成し、その中にすべてのコンテナを配置して、相互に到達できるようにします。

コンテナ化された Python 開発 2 2

これはすべて、1つのコマンドでトリガーされます。

$ docker-compose up -d
Creating network "project_default" with the default driver
Pulling db (mysql:8.0.19)…

Status: Downloaded newer image for mysql:8.0.19
Building app
Step 1/6 : FROM python:3.8
---> 7f5b6ccd03e9
Step 2/6 : WORKDIR /code
---> Using cache
---> c347603a917d
Step 3/6 : COPY requirements.txt .
---> fa9a504e43ac
Step 4/6 : RUN pip install -r requirements.txt
---> Running in f0e93a88adb1
Collecting Flask==1.1.1

Successfully tagged project_app:latest
WARNING: Image for service app was built because it did not already exist. To rebuild this image you must use docker-compose build or docker-compose up --build.
Building web
Step 1/3 : FROM nginx:1.13-alpine
1.13-alpine: Pulling from library/nginx

Status: Downloaded newer image for nginx:1.13-alpine
---> ebe2c7c61055
Step 2/3 : COPY nginx.conf /etc/nginx/nginx.conf
---> a3b2a7c8853c
Step 3/3 : COPY index.html /usr/share/nginx/html/index.html
---> 9a0713a65fd6
Successfully built 9a0713a65fd6
Successfully tagged project_web:latest

Creating project_web_1 … done
Creating project_db_1 … done
Creating project_app_1 … done

実行中のコンテナーを確認します。

$ docker-compose ps
  Name         Command                        State  Ports
-------------------------------------------------------------------------
project_app_1  /bin/sh -c python server.py    Up
project_db_1   docker-entrypoint.sh --def ... Up     3306/tcp, 33060/tcp
project_web_1  nginx -g daemon off;           Up     0.0.0.0:80->80/tcp

すべてのプロジェクトコンテナを停止して削除するには、次のコマンドを実行します。

$ docker-compose down
Stopping project_db_1 ... done
Stopping project_web_1 ... done
Stopping project_app_1 ... done
Removing project_db_1 ... done
Removing project_web_1 ... done
Removing project_app_1 ... done
Removing network project-default

イメージをリビルドするには、ビルドを実行してからupコマンドを実行して、プロジェクトコンテナの状態を更新します。

$ docker-compose build
$ docker-compose up -d

ご覧のとおり、docker-composeを使用してプロジェクトコンテナのライフサイクルを管理するのは非常に簡単です。

作成ファイルを作成するためのベスト プラクティス

Composeファイルを分析して、Composeファイルを作成するためのベストプラクティスに従って最適化する方法を見てみましょう。

ネットワークの分離

複数のコンテナがある場合は、それらを配線する方法を制御する必要があります。 作成ファイルにネットワークを設定しないため、すべてのコンテナが同じデフォルトネットワークで終了することを覚えておく必要があります。

コンテナ化された Python 開発 2 3

Pythonサービスだけがデータベースに到達できるようにしたい場合、これは良いことではないかもしれません。 この問題に対処するために、作成ファイルで、コンポーネントのペアごとに個別のネットワークを実際に定義できます。 この場合、WebコンポーネントはDBにアクセスできません。

コンテナ化された Python 開発 2 4

ドッカーボリューム

コンテナを削除するたびに、コンテナを削除するため、以前のセッションで保存したデータは失われます。 これを回避し、異なるコンテナ間でDBデータを保持するために、名前付きボリュームを利用できます。 このためには、以下に示すように、Composeファイルで名前付きボリュームを定義し、dbサービスでそのマウントポイントを指定するだけです。

version: "3.7"
services:
  db:
    image: mysql:8.0.19
    command: '--default-authentication-plugin=mysql_native_password'
    restart: always
    volumes:
      - db-data:/var/lib/mysql

    networks:
      - backend-network
    environment:
      - MYSQL_DATABASE=example
      - MYSQL_ROOT_PASSWORD=password

  app:
    build: app
    restart: always
    networks:
      - backend-network
      - frontend-network

  web:
    build: web
    restart: always
    ports:
      - 80:80
    networks:
      - frontend-network
volumes:
  db-data:

networks:
  backend-network:
  frontend-network:

 必要に応じて、docker-compose downで名前付きボリュームを明示的に削除できます。

ドッカーシークレット

Composeファイルで確認できるように、 パスワードをプレーンテキストで設定します db 。これを回避するために、Dockerシークレットを悪用してパスワードを保存し、それを必要とするサービスと安全に共有できます。 以下のようにシークレットを定義し、サービスで参照することができます。 パスワードはファイルにローカルに保存されproject/db/password.txt、 の下の/run/secrets/<secret-name>コンテナにマウントされます

version: "3.7"
services:
  db:
    image: mysql:8.0.19
    command: '--default-authentication-plugin=mysql_native_password'
    restart: always
    secrets:
      - db-password

    volumes:
      - db-data:/var/lib/mysql
    networks:
      - backend-network
    environment:
      - MYSQL_DATABASE=example
      - MYSQL_ROOT_PASSWORD_FILE=/run/secrets/db-password

  app:
    build: app
    restart: always
    secrets:
      - db-password

    networks:
      - backend-network
      - frontend-network

  web:
    build: web
    restart: always
    ports:
      - 80:80
    networks:
      - frontend-network
volumes:
  db-data:
secrets:
  db-password:
    file: db/password.txt

networks:
  backend-network:
  frontend-network:

これで、ベストプラクティスに従ったプロジェクト用の明確に定義されたComposeファイルができました。 ここで説明したすべての側面を実行するサンプルアプリケーションを見つけることができます。

次は何ですか?

このブログ投稿では、Python サービスが他のサービスに配線されているコンテナー ベースのマルチサービス プロジェクトを設定する方法と、Docker Compose を使用してローカルにデプロイする方法について説明しました。

このシリーズの次の最後のパートでは、コンテナー化された Python コンポーネントを更新およびデバッグする方法を示します。

リソース

フィードバック

「コンテナ化されたPython開発–パート0」に関する2つの考え