【Python】開発時の環境変数を.envで管理する
1. はじめに
.envファイルは環境変数を安全かつ効率的に管理するためによく用いられます。こうすることでAPIキーやDBへの接続文字列などの機密情報をソースコードから分離することが出来、さらに開発・テスト・本番といった異なる環境ごとの設定切り替えが容易になります。
セキュリティ向上や開発効率化のために用いられることが多い.env
ファイルですが、Pythonでの扱い方を知ったので備忘録的に記事にします。
忙しい人はここだけでOK
Pythonの場合、.envファイルを扱うためにpython-dotenv[1]というライブラリが利用できます。このライブラリは、.env
ファイルを作成し、load_dotenv()でよみこむことで簡単に環境変数を管理できます。
2. 環境変数をファイル管理すると何がうれしいか
.envファイルを使用した環境変数管理は、フロント系を中心に多くの言語やフレームワークで採用されていますが、セキュリティの向上と開発プロセスの効率化が主な目的だと思います。
- セキュリティ: APIキーやデータベースの接続文字列、トークン文字列であったりをソースコードから分離し、誤って公開リポジトリにプッシュしてしまうリスクを軽減できます。
- 柔軟性:開発、テスト、本番環境等、異なる環境での設定の切り替えが簡単になります。
- 冗長な実装の排除:コードに設定が埋め込まれているとif debugのような動作環境に対する分岐を書いてしまうことがありますが、バグの温床なのでこういった実装を排除できます。
- 言語間の一貫性:JavaScript(Node.js)、Ruby(on Rails)、PHP(Laravel)など、多くの言語やフレームワークで同様の仕組みが採用されており、技術スタック間で統一的な設定管理が可能です
また、マイクロサービスアーキテクチャやコンテナ化されたアプリケーションの開発において、環境変数を一元管理でき、設定動機の容易さを提供できるという面では開発効率の向上に繋がります。
3. .envファイルの作り方
ファイル自体は
- プロジェクトのルートに
.env
という名前でファイルを作成します - エディタで
.env
ファイルを開き、環境変数を記載します
変数名=値
例)
API_CLIENT_ID='XXXXXXXXXXXXXXXXXXXXXXXXX'
API_CLIENT_SECRET='YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY'
- 複数の環境がある場合、それぞれの環境に対する
.env
ファイルを作るのが一般的です-
.env.local
(ローカル開発環境用) -
.env.dev
(開発サーバ用) -
.env.prod
(本番環境用)
-
4. Pythonでの.envファイル読み込み
python-dotenvを任意の方法でインストールしてください。pipでもuvでも何でも良いです。
.envファイルの読み込みは非常に単純です。
from dotenv import load_dotenv
load_dotenv()
load_dotenv()
関数は、python-dotenvライブラリの中核機能で、.envファイルから環境変数を読み込み、pythonの実行環境に設定します。この呼び出しにより、カレントあるいは親ディレクトリにある.env
ファイルを自動的に検索し、その内容が環境変数として設定されます。
4.1 load_dotenvの引数
load_dotenv()
は、以下のような引数を持ちます。
load_dotenv(dotenv_path=None, stream=None, verbose=False, override=False, interpolate=True, encoding='utf-8')
-
dotenv_path
:. envファイルの具体的なパスを指定できます -
stream
: StringIOなどのTextStreamを受け付けます- path or streamが有効でない場合は
find_dotenv()
が呼び出され、階層を登って探しに行きます
- path or streamが有効でない場合は
-
verbose
: ファイルが見つからない場合に警告を表示するかどうか
.envファイルが見つかり、少なくとも1つでも環境変数が設定されている場合、この関数はTrueをReturnします。
また、一時的に環境変数を設定するため、Pythonのプロセスが終了すると環境変数は消去されます。これにより、システム全体の環境を永続的に変更すること無く、特定のプログラムの実行文脈でのみ設定を適用できます。
4.2 環境変数へのアクセス
load_dotenv()
で呼び出された後は、非常に単純です。
ちなみに、os.get_env()
は環境変数が存在しない場合Noneを返しますが、デフォルト値を設定することもできます。
import os
api_key = os.getenv("API_KEY")
database_url = os.environ["DATABASE_URL"]
# デフォルトを設定
debug_mode = os.getenv("DEBUG_MODE", "False")
4.3 環境ごとの設定ファイルの切り替え
実はベストプラクティスがなにか分かっていないので教えてほしい部分でもありますが、私の設定方法を紹介します。
- プロジェクトのルートに、環境ごとの
.env
ファイルを作成します。 -
load_dotenv()
を利用してenvファイルを読み込む- 環境毎に環境変数を設定して切り替える
import os
from dotenv import load_dotenv
# OSにENVの環境変数を設定、何もなければdevのファイルを読み込む
env = os.getenv('ENV', 'dev')
load_dotenv(f'.env.{env}')
環境変数ENV
に設定された値によって読み込みに行くファイルを切り替えでき、以下の例だと、開発時と本番で参照しに行くDBを変えるようなイメージができるでしょうか。
.env.dev
DATABASE_URL=sqlite:///dev_database.db
API_KEY=dev_api_key
.env.prod
DATABASE_URL=postgresql://user:password@host:port/prod_database
API_KEY=prod_api_key
.env
を使う際の注意事項
5. .envファイルは機密情報を含むため、適切なセキュリティ対策が不可欠です。
-
バージョン管理から除外
: .gitignoreファイルに.envを追加し、機密情報が誤ってリポジトリにコミットされるリスクを軽減する -
アクセス権限の設定
: ファイルの権限を適切に設定し、最小限のユーザのみが参照できるようにする -
ハッシュ化等暗号化しておく
: DB同様そのまま持っておくのではなく、ハッシュ値として持っておく -
起動時のチェック
: 必要な設定が読み込めなかった場合に起動しないような実装を行い、設定漏れのリスクを防ぐ
この記事を読んで新しい知識を身に着けたは良いが、世の中一般的に行われていることを知らずに逆にセキュリティホールを作ってしまうおそれもあります。良い技術は正しく使ってこそ良いものなので、こういった注意事項も合わせて示しておきます。
おわりに
個人的にはprodの環境では、.env
をおかずに、環境変数として設定しておくすることが多いです。
Docker Composeやk8sなどのSecretsの機能を使うのもありですね。
だったら要らないかというと、そうでもありません。開発中にGitにAPIキーを公開してしまったというセキュリティインシデントの報告は残念ながら跡を絶ちません。チームのセキュリティ教育と同時に、こういった仕組みを規約として設定することで「知らなかったのでやってしまった」というインシデントを防ぐことにも繋がります。
AIが大変な流行を見せていますが、便利な技術は正しく使って、正しい恩恵を受けていきたいですね。
Discussion