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

This is the last part in the series of blog posts showing how to set up and optimize a containerized Python development environment. The first part covered how to containerize a Python service and the best development practices for it. The second part showed how to easily set up different components that our Python application needs and how to easily manage the lifecycle of the overall project with Docker Compose.

この最後のパートでは、プロジェクトの開発サイクルを確認し、コードの更新を適用し、コンテナー化された Python サービスのエラーをデバッグする方法について詳しく説明します。 目標は、開発プロセスのこれらの繰り返しフェーズをスピードアップする方法を分析して、ローカル開発と同様のエクスペリエンスを得ることです。

コード更新の適用

一般に、コンテナ化された開発サイクルは、コードの記述/更新、ビルド、実行、デバッグで構成されます。

Python 開発パート 3

ビルドと実行のフェーズでは、ほとんどの場合実際に待たなければならないため、これらのフェーズを非常に迅速に実行して、コーディングとデバッグに集中する必要があります。

次に、開発中にビルドフェーズを最適化する方法を分析します。 ビルドフェーズは、Python ソースコードを変更するときのイメージビルド時間に対応します。 コンテナーを起動する前に、コンテナーで Python コードの更新を取得するには、イメージを再構築する必要があります。

ただし、イメージをビルドしなくてもコードの変更を適用できます。 これは、ローカルソースディレクトリをコンテナ内のパスにバインドマウントするだけで実行できます。 このために、作成ファイルを次のように更新します。

docker-compose.yaml

...
  app:
    build: app
    restart: always
    volumes:
      - ./app/src:/code
...

これにより、更新されたコードに直接アクセスできるため、イメージのビルドをスキップし、コンテナーを再起動してPythonプロセスをリロードできます。

さらに、ファイルの変更を監視し、変更が検出されるとPythonプロセスの再起動をトリガーするリローダープロセスをコンテナ内で実行すると、コンテナの再起動を回避できます。 前述のように、Composeファイルにソースコードがバインドマウントされていることを確認する必要があります。

この例では、デバッグモードでリローダーと呼ばれる非常に便利なモジュールを実行するFlaskフレームワークを使用します。 リローダーはすべてのソース コード ファイルを監視し、ファイルが変更されたことを検出したときにサーバーを自動的に再起動します。 デバッグモードを有効にするには、デバッグパラメータを以下のように設定するだけです。

server.py

server.run(debug=True, host='0.0.0.0', port=5000)

アプリコンテナのログを確認すると、フラスコサーバーがデバッグモードで実行されていることがわかります。

$ docker-compose logs app
Attaching to project_app_1
app_1 | * Serving Flask app "server" (lazy loading)
app_1 | * Environment: production
app_1 | WARNING: This is a development server. Do not use it in a production deployment.
app_1 | Use a production WSGI server instead.
app_1 | * Debug mode: on
app_1 | * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
app_1 | * Restarting with stat
app_1 | * Debugger is active!
app_1 | * Debugger PIN: 315-974-099

ソースコードを更新して保存すると、ログに通知が表示され、リロードされます。

$ docker-compose logs app
Attaching to project_app_1
app_1 | * Serving Flask app "server" (lazy loading)
...
app_1 | * Debugger PIN: 315-974-099
app_1 | * Detected change in '/code/server.py', reloading
app_1 | * Restarting with stat
app_1 | * Debugger is active!
app_1 | * Debugger PIN: 315-974-099

コードのデバッグ

主に2つの方法でコードをデバッグできます。 

1つ目は、オブジェクト/変数の実行時値をチェックするために、コード全体にprintステートメントを配置する昔ながらの方法です。 これをコンテナ化されたプロセスに適用するのは非常に簡単で、 docker-compose logs コマンドを使用して出力を簡単に確認できます。

第 2 に、より深刻なアプローチは、デバッガを使用することです。 コンテナー化されたプロセスがある場合は、コンテナー内でデバッガーを実行し、そのリモート デバッガーに接続して、インスタンス データを検査できるようにする必要があります。

例として、Flaskアプリケーションを再び取り上げます。 デバッグモードで実行する場合、リローダーモジュールとは別に、インタラクティブなデバッガーも含まれています。 例外を発生させるようにコードを更新すると仮定すると、Flask サービスは例外を含む詳細な応答を返します。

Python 開発パート 3 1

もう 1 つの興味深いケースは、コードにブレークポイントを配置してライブ検査を行う対話型デバッグです。 このためには、PythonとリモートデバッグをサポートするIDEが必要です。 コンテナーで実行されている Python コードをデバッグする方法を示すために Visual Studio Code に依存することを選択した場合は、VSCode から直接リモート デバッガーに接続するために、次の手順を実行する必要があります。 

まず、デバッガーへの接続に使用するポートをローカルにマップする必要があります。 これは、ポートマッピングをComposeファイルに追加することで簡単に実行できます。

docker-compose.yaml

...
  app:
    build: app
    restart: always
    volumes:
      - ./app/src:/code
    ports:
      - 5678:5678

...

次に、デバッガーモジュールをソースコードにインポートし、Composeファイルで定義したポートでリッスンさせる必要があります。 依存関係ファイルにも追加し、アプリ サービスのイメージを再構築してデバッガー パッケージをインストールすることを忘れないでください。 この演習では、VS Code でサポートされている ptvsd デバッガー パッケージを使用することを選択します。

server.py

...
import ptvsd
ptvsd.enable_attach(address=('0.0.0.0', 5678))
...

requirements.txt

Flask==1.1.1
mysql-connector==2.2.9


ptvsd==4.3.2

作成ファイルに加えた変更については、作成コマンドを実行して現在のコンテナー設定を削除してから、docker-compose upを実行して、作成ファイル内の新しい構成で再デプロイする必要があることを覚えておく必要があります。

最後に、デバッグ モードを起動するために、VS Code で "リモート アタッチ" 構成を作成する必要があります。

プロジェクトの launch.json は次のようになります。

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Python: Remote Attach",
            "type": "python",
            "request": "attach",
            "port": 5678,
            "host": "localhost",
            "pathMappings": [
                {
                    "localRoot": "${workspaceFolder}/app/src",
                    "remoteRoot": "/code"
                }
            ]
        }
    ]
}

パスマップをローカルとコンテナ内で更新する必要があります。 

これを行うと、IDEにブレークポイントを簡単に配置し、作成した構成に基づいてデバッグモードを開始し、最後にコードをトリガーしてブレークポイントに到達できます。

結論

This series of blog posts showed how to quickly set up a containerized Python development environment, manage project lifecycle and apply code updates and debug containerized Python services.  Putting in practice all we discussed should make the containerized development experience identical to the local one. 

リソース

フィードバック

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