oceanusではPythonで書いたアプリケーションをDockerで動かしています。
Dockerを使ったアプリケーションでは、コンテナごとに変わる可能性がある変数は、DockerfileでENVを使って、環境変数にセットして、プログラム側でそれを読み込んで使えます。
しかし、Pythonで書かれた設定ファイルで、dict型だったり入れ子になったリストやタプル、CPUのコア数な動的に変更されるような変数は、環境変数での管理は難しくなります。
そこで、どの設定ファイル読み込むか、ENVで切り替えられるように作る必要があります。
もう少し詳しく言うと設定ファイルを、取得した環境変数を使って動的に読み込んで、さらに内容を展開する必要があります。
Djangoのsettings.pyを起動時に指定できるのも同じでしょうか(未確認)。
共通に読み込む設定ファイルを作る
当たり前ですが設定ファイルは複数のアプリケーション、モジュールから呼び出されます。
もし、設定ファイルを変更する度にモジュール名の変更が必要になってしまったり、動的に切り替える処理をそれぞれに書いてしまうとDRYではありません。
そこで、一つ不変な設定ファイルを作り、アプリケーションは常にそれを読み込めばいい形にします。
例として、下記のように、settings.pyを読み込む不変なファイルとし、読み込む選択肢は例として、my_settngs.py、other_settngs.pyとします。
- app - app.py - common - settings.py - my_settngs.py - other_settngs.py
app.pyから読み込む場合は下記のようにモジュールとして必要な変数(DATABASE_NAME)を読み込みます
from common.settings import DATABASE_NAME
もちろん、DATABASE_NAMEのような設定はdefault_settings.py、other_settings.pyに宣言し、settings.pyには無し、もしくはデフォルト値を書いておきます。
例として下記のような設定です。
my_settngs.py
DATABASE_NAME = "my-oceanus"
other_settngs.py
DATABASE_NAME = "other-oceanus"
settings.pyから動的に別ファイルを読み込む
まず読み込むモジュール名を環境変数からstr型として取得するようにします。
モジュール名なので、ディレクトリは.で区切り、ファイル名の.pyは不要です。
importが慣れない人は、まずコマンドラインなどで正しく読めるかを試したほうがいいかもしれません。
環境変数名をOCEANUS_SETTINGSとし、これにモジュールパス(例:common.default_settings)を入れます。
Dockerfileでは下記のように書いておき
ENV OCEANUS_SETTINGS common.default_settings
runする時に、-eをつかって
docker run -e OCEANUS_SETTINGS=common.other_settings
pythonで同名の変数に代入。
デフォルトが必要な場合はgetの第2引数でセットしておきます。
次にその文字列を使って動的にimportします。
動的にimportを行うには、__import__()でも可能ですが、importlib.import_moduleが推奨されています。
31.5. importlib — import の実装 — Python 3.6.1 ドキュメント
from os import environ OCEANUS_SETTINGS = environ.get("OCEANUS_SETTINGS", "common.default_settings") import_module(OCEANUS_SETTINGS)
これだけはまだdefault_settingsを読み込んだだけで、中身の展開はできていないので
from common.settings import DATABASE_NAME
という形では使えません。
読み込んだ設定ファイルの中身を展開する
そして、その読み込んだファイルにある変数などをすべて展開します。
ここはどうやるかしばらくハマりましたが、下記のページを参考にglobals()を使って、現在のモジュール(common.settings)のグローバル変数に追加することで、うまくいきました。
31.5. importlib — import の実装 — Python 3.6.1 ドキュメント
最終的には下記のような形になります。
settings.py
from os import environ from importlib import import_module OCEANUS_SETTINGS = environ.get("OCEANUS_SETTINGS", "common.default_settings") globals().update(import_module(OCEANUS_SETTINGS).__dict__)
これで、環境変数OCEANUS_SETTINGSを変更して、
from common.settings import DATABASE_NAME
のように読み込めば、common.default_settingsや、common.other_settingsの設定ファイルを読みこむことができます。
この動的読み込み&展開は設定ファイルに限らずいろんな所で応用がききそうです。
ゼロから作るDeep Learning ―Pythonで学ぶディープラーニングの理論と実装
- 作者: 斎藤康毅
- 出版社/メーカー: オライリージャパン
- 発売日: 2016/09/24
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (12件) を見る
- 作者: クジラ飛行机
- 出版社/メーカー: マイナビ出版
- 発売日: 2016/10/26
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る