Python 2.7 から最新の Python 3 ランタイムに移行する

このページでは、第 1 世代から第 2 世代の Python ランタイムに移行する手順について説明します。サポートされている最新バージョンの Python を使用するように第 2 世代のアプリをアップグレードするには、既存のアプリケーションをアップグレードするをご覧ください。

Python 2.7 は、2024 年 1 月 31 日にサポートを終了しました。既存の Python 2.7 アプリケーションは引き続き実行され、トラフィックを受信します。ただし、サポート終了日を過ぎると、ランタイムを使用するアプリケーションの再デプロイが App Engine によってブロックされることがあります。このページのガイドラインに沿って、サポートされている最新バージョンの Python に移行することをおすすめします。

Python 3 ランタイムに移行すると、最新の言語機能を使用するだけでなく、慣用的なコードでより移植性の高いアプリを構築できます。Python 3 ランタイムでは、Python Software Foundation が提供する最新バージョンのオープンソース Python インタープリタが使用されます。Python 3 ランタイムで構築したアプリでは、requirements.txt ファイルに依存関係を宣言することで、Python の豊富なパッケージやフレームワークを使用できます(C コードを使用するものも利用できます)。

ランタイム移行プロセスの概要

ランタイムを移行する場合は、次のような段階的なアプローチをおすすめします。これにより、プロセス全体を通じてアプリケーションを機能させ、テスト可能な状態を維持できます。

  1. Python 3 に対応するようにアプリをアップグレードします。

    このアップグレードをサポートするソリューションがいくつかあります。たとえば、SixPython-FuturePython-Modernize などを使用します。

    移行プロセスの詳細な手順については、Python Software Foundation ドキュメントのサイトで Porting Python 2 Code to Python 3 をご覧ください。

  2. アプリで使用する App Engine バンドル サービスに対して、次のいずれかの実装戦略を選択します。

    1. Python 2 アプリの以前のバンドル サービスをバンドルされていない Google Cloud サービス、サードパーティのサービス、その他のおすすめの代替サービスに移行します。

    2. 引き続き Python 3 アプリで以前のバンドル サービスを使用します。このアプローチでは、移行サイクルの後半でバンドルされていないサービスに移行できます。

    各サービスを移行した後で、必ずアプリをテストしてください。

  3. Python 3 ランタイム用に App Engine 構成ファイルを準備します。app.yaml の構成設定に影響する重要な変更がいくつかあります。

    • アプリはスレッドセーフとみなされるようになりました。アプリケーションがスレッドセーフでない場合は、app.yamlmax_concurrent_requests を 1 に設定する必要があります。この設定により、スレッドセーフなアプリで必要な数を上回る数のインスタンスが作成され、不要なコストが発生する可能性があります。
    • app.yaml ファイルでは、スクリプトへのリクエストがルーティングされなくなりました。代わりに、アプリ内ルーティングでウェブ フレームワークを使用し、app.yaml のすべての script ハンドラを更新または削除する必要があります。この処理を Flask フレームワークで行う方法については、GitHub の App Engine 移行ガイドのコードサンプルをご覧ください。

      この構成ファイルや他の構成ファイルを変更する方法については、構成ファイルのセクションをご覧ください。

  4. 第 2 世代ランタイムでは、リクエストログ内にアプリログがネストされなくなります。ログ エクスプローラにリクエストログとアプリログのネストされたビューを表示するには、追加の手順が必要です。詳細については、Cloud Logging に移行するをご覧ください。

  5. アップグレードしたアプリを Python 3 環境でテストしてデプロイします。

    すべてのテストに合格したら、アップグレードしたアプリを App Engine にデプロイします。ただし、トラフィックが新しいバージョンに自動的にルーティングされないようにします。トラフィック分割を使用して、Python 2 ランタイムのアプリから Python 3 ランタイムのアプリにトラフィックを段階的に移行します。問題が発生した場合は、問題が解決するまですべてのトラフィックを安定バージョンに転送します。

Python 2 アプリを Python 3 に変換する方法については、その他のリソースをご覧ください。

Python 2 ランタイムと Python 3 ランタイムの主な違い

移行中に行う必要がある変更のほとんどは、Python 2 ランタイムと Python 3 ランタイムの次のような違いによるものです。

メモリ使用量の違い

第 2 世代のランタイムでは、第 1 世代のランタイムよりもメモリ使用量のベースラインが高くなります。これは、ベースイメージのバージョンの違いや、2 つの世代でのメモリ使用量の計算方法の違いなど、複数の要因によるものです。

第 2 世代のランタイムでは、アプリプロセスで使用されるものとメモリに動的にキャッシュされるアプリケーション ファイルの数の合計としてインスタンスのメモリ使用量が計算されます。メモリ使用量の多いアプリケーションで、メモリの上限を超えてインスタンスがシャットダウンしないようにするには、より多くのメモリを備えているインスタンス クラスにアップグレードします。

CPU 使用率の違い

第 2 世代のランタイムでは、インスタンスのコールド スタート時の CPU 使用率のベースラインが高くなります。アプリケーションのスケーリング構成によっては、CPU 使用率に基づいてスケーリングするようにアプリケーションを構成すると、インスタンス数が想定よりも多くなるなど、意図しない結果が生じることがあります。この問題を回避するには、アプリケーションのスケーリング構成をテストして、インスタンス数が許容範囲内であることを確認します。

リクエスト ヘッダーの違い

第 1 世代のランタイムでは、アンダースコア付きのリクエスト ヘッダー(X-Test-Foo_bar など)をアプリケーションに転送できます。第 2 世代のランタイムでは、ホスト アーキテクチャに Nginx が導入されています。この変更の結果、第 2 世代のランタイムはアンダースコア(_)付きのヘッダーを自動的に削除するように構成されています。アプリケーションでの問題を回避するため、アプリケーションのリクエスト ヘッダーにアンダースコアを使用しないでください。

Gunicorn ワーカーの違い

Python 3 以降のランタイムの場合、Gunicorn ワーカーの数はメモリ使用量に直接影響します。メモリ使用量が増加すると、それに比例してワーカーの数も増加します。メモリ消費量を削減するには、Gunicorn ワーカーの数を減らすことを検討してください。Gunicorn ワーカー数の構成手順については、エントリポイントのベスト プラクティスをご覧ください。

Python 2 と Python 3 の互換性の問題

Python 3 が 2008 年に初めて発表された時点で、Python に対して下位互換性のない複数の変更が行われています。print ステートメントから print() 関数に変更するなど、コードの軽微な更新だけで対処できる変更もありますが、バイナリデータ、テキスト、文字列を扱う方法の更新など、コードに大幅な更新が必要な変更もあります。

Python 標準ライブラリを含む一般的な多くのオープンソース ライブラリも、Python 2 から Python 3 への移行に伴い変更されています。

Python 3 ランタイムでの App Engine バンドル サービス

移行の労力と複雑さを軽減するため、App Engine スタンダード環境では Python 3 ランタイムで以前の多くのバンドル サービスと API にアクセスできます(Memcache など)。Python 3 アプリは、言語固有のライブラリを介してバンドル サービスの API を呼び出すことができ、Python 2 ランタイムとほぼ同じ機能にアクセスできます。

以前のバンドル サービスと同様の機能を備えた Google Cloud プロダクトを使用することもできます。バンドルされていない Google Cloud プロダクトへの移行を検討することをおすすめします。これにより、継続的な改善と新機能を活用できるようになります。

Google Cloud で個別のプロダクトとして提供されないバンドル サービス(画像処理、検索、メッセージングなど)については、推奨のサードパーティ プロバイダまたはその他の回避策を使用できます。

構成ファイル

App Engine スタンダード環境の Python 3 ランタイムでアプリを実行する前に、App Engine で使用する構成ファイルの一部を変更する必要があります。

動的コンテンツのリクエストを転送するために必要なウェブ フレームワーク

Python 2 ランタイムでは、app.yaml ファイルで URL ハンドラを作成して、特定の URL または URL パターンがリクエストされたとき実行するアプリを指定できます。

Python 3 ランタイムでは、アプリが app.yaml で URL ハンドラを使用するのでなく、代わりに Flask や Django などのウェブ フレームワークを使用して動的コンテンツのリクエストを転送する必要があります。静的コンテンツの場合は、アプリの app.yaml ファイルで引き続き URL ハンドラを作成できます。

静的コンテンツのみを使用するアプリ

App Engine で静的ウェブアプリをホストする場合は、app.yaml ファイルにハンドラを指定して、URL を静的ファイルにマッピングします。

Python 2 では、app.yaml ファイルで指定されたハンドラのいずれにもリクエストが一致しない場合、App Engine は 404 エラーコードを返します。

Python 3 では、ハンドラのいずれにもリクエストが一致しない場合、App Engine は main.py ファイルを検索し、main.py ファイルが見つからない場合は 5xx エラーを返します。静的コンテンツのみを使用する App Engine アプリは main.py ファイルを必要としないため、ほとんどのユーザーには、このエラーが表示され、さらにアプリのログにインスタンス起動エラーが表示されます。

どの静的ハンドラにも一致しない場合に 404 エラーを返す動作を維持し、ログのエラーを回避するには、次のいずれかを行います。

  • app.yaml ファイルに空のディレクトリを指定するキャッチオール静的ハンドラを追加する。
  • 404 エラーを返す main.py ファイルにシンプルな動的アプリを追加する。

両方の選択肢の使用例:

app.yaml

ルートアプリ ディレクトリに空のディレクトリ(empty/ など)を作成します。app.yaml ファイルのハンドラ セクションで、他のすべての URL パターンをキャッチするため、最後に新しいハンドラを作成し、static_filesupload 要素empty ディレクトリを指定します。

  handlers:
  - url:
    .
    .
    .
  - url: /(.*)$
    static_files: empty/\1
    upload: empty/.*$

main.py

main.py ファイルを作成し、404 エラーを返す次のコードを追加します。

  def app(env, start_response):
    start_response('404 Not Found', [('Content-Type','text/html')])
    return [b"Not Found"]

テスト

dev_appserver に依存するよりも、Python に対して汎用的なテスト方法を使用することをおすすめします。たとえば venv を使用すれば、分離されたローカル Python 3 環境を作成できます。標準的な任意の Python テスト フレームワークを使用して、単体テスト、統合テスト、システムテストを作成できます。サービスのデベロッパー版を設定するか、多くの Google Cloud プロダクトで用意されているローカル エミュレータを使用することもできます。

必要に応じて、Python 3 をサポートするプレビュー版の dev_appserver を使用できます。このテスト機能の詳細については、ローカル開発用サーバーの使用をご覧ください。

デプロイ

appcfg.py を使用したデプロイは、Python 3 でサポートされていません。アプリのデプロイには gcloud コマンドライン ツールを使用します

ロギング

Python 3 ランタイムでのロギングは、Cloud Logging のロギング標準に従います。Python 3 ランタイムでは、アプリログがリクエストログにバンドルされなくなり、別のレコードに分離されます。Python 3 ランタイムにおけるログの読み取りと書き込みの詳細については、ロギングガイドをご覧ください。

移行に関するその他のリソース

App Engine アプリをスタンドアロンの Cloud サービスまたは Python 3 ランタイムに移行する方法については、次の App Engine リソースをご覧ください。