- 環境変数は、プロセスが環境から受け取るキーと値の設定です。
- これらにより、ポートやデータベース URL などの構成がコードから除外されます。
- 値は常に文字列であるため、解析しない限り「false」は真実です。
- 実際のシークレットを含む
.envファイルを決してコミットしないでください。アプリの起動時に必要な設定を検証してください。
アプリにはデータベース URL が必要です。ラップトップではローカル データベース、CI では使い捨てデータベース、運用環境ではマネージド クラスターになります。これらのハードコーディングは、環境ごとにコードを変更することを意味します。 環境変数がこれを解決します。コードは DATABASE_URL であり、各環境は独自の値を提供します。
この考え方は Twelve-Factor App メソッドの中心であり、デプロイメントごとに異なる構成は環境に属するというものです。 Windows 上の Python 3.13 を使用して以下の動作をテストしました。表示されるすべての出力はその実行からのものです。
環境変数とは何ですか?
実行中のすべてのプロセスには、環境と呼ばれる一連の名前付きテキスト値があります。よく知られた例としては、シェルにプログラムの検索場所を指示する PATH と、HOME または USERPROFILE があります。独自のアプリでは、APP_PORT、LOG_LEVEL、DATABASE_URL などの設定に同じメカニズムを使用できます。
環境変数の設定
| シェル | このセッションについて | 1つのコマンドに対して |
|---|---|---|
| バッシュ/zsh | エクスポートAPP_PORT=8080 |
APP_PORT=8080 Python app.py |
| パワーシェル | $env:APP_PORT = "8080" |
設定して実行し、その後削除します |
| Windows コマンド | APP_PORT=8080 を設定します |
内蔵されていない |
これらは、現在の端末セッションの間のみ持続します。これらを永続的にするには、~/.bashrc などのシェル プロファイルに追加するか、Windows の場合はシステム環境設定または setx を使用します。アプリケーションの場合、通常、システム全体に設定するよりも、展開時に提供する方が適切です。
コードで読み取る
import os
port = os.environ.get("APP_PORT", "8000") # default if missing
print(port) # 8000
db = os.environ["DATABASE_URL_MISSING_DEMO"]
# KeyError: 'DATABASE_URL_MISSING_DEMO'
オプション設定のデフォルトでは .get() を使用します。アプリが動作しない設定には、KeyError を示す角括弧を使用します。他の言語も同様です。Node.js の process.env.APP_PORT、Java の System.getenv("APP_PORT")、Go の os.Getenv("APP_PORT")。
すべては文字列です
これはあらゆる言語の人々を魅了します。ポートを設定し、それに「1」を追加しました。
os.environ["APP_PORT"] = "8080"
port = os.environ["APP_PORT"]
print(type(port).__name__, repr(port), port + "1")
# str '8080' 80801
os.environ["APP_PORT"] = 8080
# TypeError: str expected, not int
値はテキストとして返され、追加すると 8081 ではなく 80801 が生成されました。int(os.environ["APP_PORT"]) のように、明示的に変換します。ブール値はさらに悪いです:
os.environ["DEBUG"] = "false"
print(bool(os.environ["DEBUG"])) # True
Python では空でない文字列はすべて真であるため、"false" は True になりました。運用環境ではデバッグ モードがオンになっているはずです。ブール値を意図的に解析します。
def env_bool(name, default=False):
value = os.environ.get(name)
if value is None:
return default
return value.strip().lower() in {"1", "true", "yes", "on"}
env_bool("DEBUG") # False
env_bool("NOT_SET", default=True) # True
プロセスがそれらを継承する方法
子プロセスは、親の環境の コピー を受け取ります。これを確認するために、子 Python プロセスを開始しました。
child sees: 8080 inherited # default: full copy of the parent's environment
child sees: 9000 None # given a custom env: only what we passed
parent sees child var: None # a child's changes never flow back
3 つの実践的なレッスンが続きます。エクスポートした変数は、作成していないスクリプトも含め、起動するすべてのプログラムに表示されますが、これはシークレットにとって重要です。カスタム環境を渡すとすべてが置き換えられるため、子が必要とするものはすべて含めます。また、スクリプトは親シェルの変数を変更できないため、ツールはファイルを実行する代わりにファイルを source 要求します。
.env ファイル
多くの変数を手で入力するのは面倒なので、ほとんどのプロジェクトではローカル開発値を .env ファイルに保存します。
# local development only
APP_PORT=8080
DATABASE_URL="postgres://dev:devonly@localhost:5432/shop"
FEATURE_SEARCH=true
python-dotenv や Node.js の dotenv などのライブラリは、起動時にこのファイルを読み込みます。 Node.js 20.6 以降では、--env-file を使用してネイティブにロードすることもできます。このアイデアを確認するために、上記のファイルを次のように変換する小さなパーサーを作成しました。
{'APP_PORT': '8080',
'DATABASE_URL': 'postgres://dev:devonly@localhost:5432/shop',
'FEATURE_SEARCH': 'true'}
実際には実際のライブラリを使用してください。 10 行バージョンでは無視される引用符、エスケープ、コメント、および複数行の値を処理します。デフォルトでは、ほとんどの dotenv ローダーはそうではないすでに設定されている変数をオーバーライドするため、実際の環境値がファイルよりも優先されます。それは通常、あなたが望んでいることです。
ヒント: 最初のコミットの前に .env を .gitignore に追加し、代わりにすべての変数名と安全なプレースホルダー値を含む .env.example をコミットします。新しい開発者はそれをコピーし、独自の値を入力します。
起動時に構成を検証する
変数が欠落している場合は、明確なメッセージを表示してアプリをすぐに停止する必要があり、1 時間後に紛らわしいエラーが発生することはありません。ロード時に必要な設定を確認しました。
REQUIRED = ["DATABASE_URL", "SECRET_KEY"]
def check_required(env):
missing = [k for k in REQUIRED if not env.get(k)]
if missing:
raise SystemExit(f"Missing required settings: {', '.join(missing)}")
# Missing required settings: SECRET_KEY
型指定された構成ライブラリはさらに進んでいます。 Python の Pydantic 設定、Node.js の zod または envalid、Java の Spring 構成プロパティはすべて型を変換し、すべての問題を一度に報告します。
環境変数とシークレット
環境変数は、API キーなどのシークレットをアプリに渡す一般的な方法であり、環境変数をハードコーディングするよりもはるかに優れています。彼らには、次のような弱点があります。
- テストで示されたように、子プロセスはそれらを継承します。
- これらは、クラッシュ レポート、デバッグ ページ、環境をダンプするログに表示されることがあります。
- 一部のシステムでは、同じユーザーが実行する他のプロセスがそれらを読み取ることができます。
本番環境では、多くのチームが HashiCorp Vault、AWS Secrets Manager、Azure Key Vault、Google Secret Manager などの専用マネージャーにシークレットを保存し、実行時にそれらを注入します。シークレットを変数ではなくファイルとしてマウントすることを好む人もいます。いずれにせよ、履歴から削除した後でも、ログに構成を出力したり、Git にコミットされたシークレットをローテーションしたりしないでください。
Docker と CI の場合
# Docker (not run for this article)
docker run -e APP_PORT=8080 --env-file .env.production myapp
# GitHub Actions (not run for this article)
env:
APP_PORT: 8080
API_KEY: ${{ secrets.API_KEY }}
CI プラットフォームは、シークレットを暗号化して保存し、ログにマスクします。シークレットはイメージ レイヤーで読み取り可能なままであるため、Dockerfile の ENV を使用して Docker イメージにシークレットをベイクすることは避けてください。完全なセットアップについては、Docker Compose チュートリアルおよびGitHub Actions CI/CD ガイドを参照してください。
ベストプラクティス
SHOP_DB_URLなど、プレフィックスを付けた明確な大文字の名前を使用します。- 1 つのモジュール内のすべての構成を読み取り、そこで型を変換し、起動時に検証します。
- 機密性の低い設定に対してのみ安全なデフォルトを提供します。
.envを Git から遠ざけ、.env.exampleを維持します。- 機密値については実稼働環境でシークレット マネージャーを使用します。
- すべての変数、その型、および値の例を文書化します。
環境変数の問題のデバッグ
設定が欠落しているかのようにアプリが動作する場合は、次のチェックを順番に実行してください。
- 正しい場所に設定されていますか? ある端末タブでエクスポートされた変数が、別の端末タブやシステムによって開始されたサービスには存在しません。
- アプリは再起動されましたか? 実行中のプロセスは、開始時の環境を維持します。その後変数を変更しても、再起動するまでは効果がありません。
- 名前は正確ですか? Linux と macOS では名前の大文字と小文字が区別されるため、
App_PortとAPP_PORTは異なる変数です。 Windows はこれらを同じものとして扱います。 - .env ファイルはそれをオーバーライドしていますか? それとも無視されていますか? ローダーが既存の値をオーバーライドするかどうか、および実際にどのファイルを読み取るかを確認してください。
- 余分な引用符やスペースはありますか? 末尾にスペースや余分な引用符を付けてコピーされた値は、特定が難しい形で解析に失敗したり、一致に失敗したりする可能性があります。
プロセスが実際に何を受け取ったかを確認するには、シークレットの値ではなく、変数名を出力します。 Linux と macOS では、printenv は現在のシェルの環境をリストし、PowerShell では Get-ChildItem Env: も同じことを行います。コンテナ内で env から docker exec を実行すると、コンテナのビューが表示されますが、これは多くの場合、独自のシェルのビューとは異なります。
最後のヒント: 起動時に、どのオプション設定がデフォルトを使用しているかをリストする 1 行をログに記録します。費用はかからず、「なぜそんなことをするのか?」という疑問のほとんどに答えてくれます。すぐに質問します。
まとめ
環境変数は単純ですが、いくつかの罠が隠されています。すべてが文字列であり、子はそれを継承し、それを介して秘密が漏洩する可能性があります。それらをアプリへの入力として扱います。それらを 1 か所で読み取り、早期に変換して検証し、コミットされるファイルに実際の機密情報が含まれないようにします。
よくある質問
環境変数とは何ですか?
これは、APP_PORT=8080 など、プロセスが環境から受け取る名前付きテキスト値です。アプリは、環境間で変更される設定にこれらを使用します。
.env ファイルとは何ですか?
KEY=value 行のテキスト ファイル。python-dotenv などのライブラリによって起動時にロードされます。これは主にローカル開発に使用され、実際の秘密をコミットするべきではありません。
環境変数が文字列なのはなぜですか?
オペレーティング システムは環境値をテキストとして保存します。文字列「false」は true としてカウントされるため、int() で数値を変換し、ブール値を明示的に解析します。
環境変数はシークレットに対して安全ですか?
これらはハードコーディングよりも優れていますが、子プロセスがそれらを継承し、ログによって公開される可能性があります。実稼働システムでは、多くの場合、専用のシークレット マネージャーが使用されます。
スクリプトは親シェルの変数を変更できますか?
いいえ。子プロセスは環境のコピーを取得するため、その変更は逆流しません。現在のシェルで変数を設定するには、代わりにファイルをソースします。
学び続ける
テスト、WordPress、または SEO についてサポートが必要ですか?
私たちは、チームがより優れたソフトウェアを出荷し、サイトを成長させるのを支援します。あなたが取り組んでいることを教えてください。明確な次のステップを提案します。
詳しいガイドはこちら テスト、AI、最新の開発
この記事は気に入りましたか?トピックを選択して、最新のチュートリアルに直接アクセスしてください。
AI 幻覚の説明: 言語モデルが物事をでっち上げる理由とそれを軽減する方法
AI 幻覚について説明しました。幻覚とは何か、言語モデルが幻覚を生成する理由、一般的な種類、グラウンディングとプロンプトで幻覚を軽減する方法、そして私たちが実行した簡単なチェックです。
記事を読む →DevOps環境変数の説明: .env ファイル、シークレット、および設定が正しく行われている
実行した Python の例を使用して環境変数について説明します。環境変数の読み取りと設定、すべての値が文字列である理由、.env ファイル、プロセスの継承、シークレット、検証について説明します。
記事を読む →ソフトウェア開発API レート制限の説明: アルゴリズム、ヘッダー、および 429 エラーの処理
API レート制限については、実行したシミュレーションで説明します。トークン バケット、固定ウィンドウ、スライディング ウィンドウ、429 応答、レート制限ヘッダー、バックオフによる再試行、およびテスト制限です。
記事を読む →テスト自動化Python でのモッキング:unittest.mock、patch、およびテストを嘘にする間違い
Python でのunittest.mockによるモック、テスト済み: モックとreturn_value、side_effect、名前が使用されている場所のパッチ、仕様と自動仕様、日付のモック、およびよくある間違い。
記事を読む →ソフトウェアテストコードカバレッジの説明: 回線カバレッジとブランチカバレッジ、および 100% の実際の意味
実際のcoverage.pyの実行でコードカバレッジを説明: 行、分岐、条件のカバレッジ、レポートの読み取り、100%のカバレッジでもバグを見逃す可能性がある理由、および賢明なターゲット。
記事を読む →データベースとSQLSQL における DELETE、TRUNCATE、DROP: 違い、ロールバック、およびそれぞれをいつ使用するか
DELETE、TRUNCATE、DROP について、テスト済み SQL を使用して説明します。それぞれの削除内容、ロールバック、トリガー、ID リセット、速度、PostgreSQL、MySQL、SQL Server、SQLite の違いについて説明します。
記事を読む →データベースとSQLSQL ビューの説明: 作成、使用、落とし穴の回避、例を示します
SQL ビューについて、実行したクエリとともに説明しました。ビューの作成、ライブ結果、列の非表示、パフォーマンス、更新可能なビューと具体化されたビュー、壊れた依存関係、ベスト プラクティスについて説明します。
記事を読む →ソフトウェア開発TypeScript インターフェイスと型: 実際の違い、コンパイラでテスト
TypeScript インターフェイスと型、実際のコンパイラでチェック: 両方でできること、片方だけでできること、宣言のマージ、拡張と交差、および従うべき簡単なルール。
記事を読む →ソフトウェア開発Python *args と **kwargs の説明: 柔軟な関数と明確な例
Python の *args と **kwargs について、実行したコードで説明しました。パッキングとアンパッキング、パラメーターの順序、キーワードのみの引数と位置のみの引数、デコレーター、一般的なエラーです。
記事を読む →ソフトウェア開発Python Lambda 関数: 構文、実際の用途、および使用すべきでない場合
Python のラムダ関数について、実行したコードとともに説明します。構文、キーによるソート、マップとフィルター、クロージャー、遅延バインディング トラップ、通常の def がより適切な選択である場合。
記事を読む →人工知能AIエージェントとは何ですか?シンプルなエージェント ループを実行できる仕組み
AIエージェントとは何ですか?エージェント ループ、ツール、メモリ、ガードレール、実行した小さな Python ループ、実際のリスク、およびシンプルなチャットボットがより良い選択である場合についてのわかりやすいガイドです。
記事を読む →DevOpsモニタリングと可観測性: ログ、メトリクス、トレースによる本当の違い
モニタリングと可観測性について説明: 既知および未知の障害、ログ、メトリクスとトレース、チェックアウト インシデントのウォークスルー、ゴールデン シグナル、OpenTelemetry、および便利なアラート。
記事を読む →DevOpsDocker ボリュームとバインド マウント: コンテナ データを正しい方法で保持する
Docker ボリュームとバインド マウントについて説明します。コンテナ データが消える理由、名前付きボリューム、バインド マウント、tmpfs、Compose の例、バックアップ、アクセス許可、およびそれぞれをいつ使用するかについて説明します。
記事を読む →テスト自動化TDD と BDD: 主な違い、1 つの機能が両方の方法で構築され、それぞれをいつ使用するか
TDD と BDD は、両方の方法で構築された 1 つの割引機能で説明されています。実行したコードによるレッド-グリーン-リファクタリング、Given-When-Then シナリオ、ツール、トレードオフ、およびそれぞれをいつ使用するかです。
記事を読む →ソフトウェア開発Git mergeとrebaseの違い:例と使い分け
Git mergeとrebaseの違いを、実際のログ出力、比較表、rebaseの黄金律、安全なチームワークフローで解説します。
記事を読む →ソフトウェア開発Pythonのリスト内包表記:構文・例・使うべきでない場面
Pythonのリスト内包表記を解説:構文、フィルタ、if-else、ネストしたループ、実測した処理時間、そして通常のforループの方が良い場面。
記事を読む →データベースとSQLSQL JOINを例で解説(Inner・Left・Right・Full)
サンプルのテーブルとクエリでSQL JOINを順に解説。INNER、LEFT、RIGHT、FULL、CROSS、SELFに加え、よくある間違いとパフォーマンスのコツも紹介します。
記事を読む →