🎉

Pythonプロジェクトの環境変数によるデプロイ環境管理の一例

2021/09/04に公開

定義

目的

開発環境および本番環境を区別するために、環境変数を取り入れて行うのが一般的である。例えば Node.js + npm の場合、多くのフレームワークは NODE_ENV という変数を使っている。しかし Python では、現時点筆者が知る限り、こうした慣習が普及されていない。よって、そういった需要があった場合、開発者自らこれを実装しなければならない。

確かにpipenvといったツールでは、便利に開発時以下のように依存関係を指定できる。しかし、NODE_ENV のような環境変数まで管理されていない。

pipenv install --dev <package>

そのため、本稿ではこれの処理方法の一例をテンプレートとして提供したい所存である。

実装

ファイルを作成

__environ__.pyというファイルを作成する。ファイル名は他に_env.py__env__.pyなど色々ある。標準では規定されていないため、自由でよい。

DeploymentEnvironment の Enum

まず、内部で str のみで種類を分けることは望ましくないため、Enum型を使ってこれを管理する。例としてPRODUCTION```(本番)、DEVELOPMENT(開発)、STAGING``(デモ)の三種類を出しているが、実際の需要に応じて追加・削除していただければと思う。

ここで__str__を上書きしたのは、デフォルトで例えばstr(DeploymentEnvironment.PRODUCTION)の場合、DeploymentEnvironment.PRODUCTIONと長く帰ってくるので、valueを使って、'prod'とだけ帰ってくるようにするためである。故に、なくてもいい場合もあるから、こちらも自由にしてよい。

from enum import Enum
class DeploymentEnvironment(Enum):
    PRODUCTION = 'prod'
    DEVELOPMENT = 'dev'
    STAGING = 'stage'
    
    def __str__(self) -> str:
        return str(self.value)

環境変数の読み込み

それから、環境変数を指定して読み込むこと。まず、環境変数名も定例がないため、仮に DEPLOY_ENV としたが、自由に指定して構わない(一応環境変数の慣例に従うべきである)
)。

環境変数は存在しないことやあまり綺麗な値にならない場合があるので、get(..., default='').strip().lower()等の対応を施して、同じフォーマットに基準化する。

import os
DEPLOY_ENV_VAR_NAME = 'DEPLOY_ENV'
DEPLOY_ENV_VAR_VALUE = os.environ.get('DEPLOY_ENV', default='').strip().lower()

環境変数の値を Enum にマッチング

まず、あり得る表示方法の一覧をDEPLOYMENT_ENVIRONMENT_REPRESENTATIONSというdictに格納し、イテレーションを行って環境変数の値と比較する。マッチングができた時Enumの値をDEPLOYMENT_ENVIRONMENTに付与する。できなかった場合は開発環境(DeploymentEnvironment.DEVELOPMENT)にフォールバックする。

DEPLOYMENT_ENVIRONMENT_REPRESENTATIONS = {
    DeploymentEnvironment.DEVELOPMENT: {'dev', 'development', 'trunk'},
    DeploymentEnvironment.STAGING: {'stage','staging', 'demo'},
    DeploymentEnvironment.PRODUCTION: {'prod','production', 'live'},
}
DEPLOYMENT_ENVIRONMENT = None
for deploy_env in DeploymentEnvironment:
    if DEPLOY_ENV_VAR_VALUE in DEPLOYMENT_ENVIRONMENT_REPRESENTATIONS[deploy_env]:
        DEPLOYMENT_ENVIRONMENT = deploy_env
        break
else:
    DEPLOYMENT_ENVIRONMENT = DeploymentEnvironment.DEVELOPMENT

便宜上のbool値

一一Enumの値と比較しなくて済むようにするため、便宜上比較しやすいために三種類のbool変数を作る。

DEVELOPMENT_MODE = DEPLOYMENT_ENVIRONMENT is DeploymentEnvironment.DEVELOPMENT
PRODUCTION_MODE = DEPLOYMENT_ENVIRONMENT is DeploymentEnvironment.PRODUCTION
STAGING_MODE = DEPLOYMENT_ENVIRONMENT is DeploymentEnvironment.STAGING

利用方法

プロジェクト内の利用方法

以下のように直接DEPLOYMENT_ENVIRONMENTを引用して使うことができる。

from __environ__ import DEPLOYMENT_ENVIRONMENT
print("Current Deployment Environment is:", DEPLOYMENT_ENVIRONMENT)

また、Enumの値と比較することもできる。

from __environ__ import DeploymentEnvironment, DEPLOYMENT_ENVIRONMENT
if DEPLOYMENT_ENVIRONMENT is DeploymentEnvironment.PRODUCTION:
    # Do something productive!
    print("I'm in production mode! Yay!")
else:
    print("I'm not in production mode :(")

さらに先ほど作っておいたbool値を使って便利に比較することができる。

from __environ__ import DEVELOPMENT_MODE
if DEVELOPMENT_MODE:
    # Do something for development
    print("I will do something specific for development!")
else:
    print("I'm not in development mode so I will do nothing :(")

VScode の環境変数設定の方法

実際に効果を検証するために、VSCodeのデバッグ機能で、それぞれ異なる環境変数を指定し、その結果を見る。

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Python (dev)",
            "type": "python",
            "request": "launch",
            "program": "usage.py",
            "console": "integratedTerminal",
            "env": {
                "DEPLOY_ENV": "dev"
            }
        }, {
            "name": "Python (prod)",
            "type": "python",
            "request": "launch",
            "program": "usage.py",
            "console": "integratedTerminal",
            "env": {
                "DEPLOY_ENV": "prod"
            }
        }, 
    ]
}

結果

ソースコード

Discussion