⇧ う~む、システムに影響が出てきてるってのが、モチベーションを下げてくれますよね...
Linuxのシェルスクリプトで環境毎に設定ファイルを読み込むようにしたい
Webアプリケーションとかだと、
- ローカル環境
- テスト環境(結合テスト環境)
- ステージング環境
- 本番環境
のように、環境毎に設定ファイルを切り替えること、あるあるだと思うのだけど、Webフレームワークのような存在の無さそうな、Linuxのシェルスクリプトのような世界で、環境毎に設定ファイルを切り替えるのって、どうするのが良いんだろうかと。
まぁ、考えられる方法としては、
- ホスト名
- IPアドレス
のどちらかを、リスト化しておいて、各環境で、「ホスト名」ないしは「IPアドレス」を取得して、突き合わせて判定するってことになるんかなと。
で、クラウド環境とかだと、IPアドレスが動的に変わることもあるあるだと思うので、「ホスト名」で判定するのが良いのかなと。
ただ、
⇧「ホスト名」については、重複してるものがある環境で運用してる場合もあるっぽいので、シェルスクリプトの配置先のマシンを間違えると影響がありそう...
ちなみに「ホスト名」については、ネットの情報を漁っていたら、
⇧ 上記サイト様によりますと、
uname -n
⇧ 上記のコマンドで、「ホスト名」が取得できるらしいですと。
そして、
⇧ stackoverflowで、シェルスクリプトで、環境毎の話が出てました。
とりあえず、シェルスクリプトを利用して、環境毎に設定ファイルを変えるってことを実現しようとする際に、ハマりどころとしては、
- シェル変数は別プロセスには引き継げない
- 環境変数は親プロセスから子プロセスへ引き継げる
- 子プロセスから親プロセスへ引継ぐ場合は、親プロセス側で「source」コマンドを実施する
結論をまとめると
- exportした環境変数は子シェルには引き継がれるます。自分を呼び出した親には引き継がれません。
- シェル変数は 自身のシェル内のみで有効です。
⇧ といったところでしょうか。
つまり、複数のシェルスクリプトのファイルがあって、別のシェルスクリプトのファイルに処理を委譲しているような場合は、大元のシェルスクリプトのファイルで環境変数を定義しておいてあげる必要があるようです。
肝心の設定ファイルから読み込む方法は、
⇧ 上記サイト様がまとめて下さっていました。
というわけで、試してみました。
例の如く、動作環境は「WSL 2(Windows Subsystem for Linux 2)」の「Ubuntu 22.04.2 LTS (GNU/Linux 5.15.133.1-microsoft-standard-WSL2 x86_64)」です。
■/home/ts0818/work/test_environment/env/host_list.txt
dev.host=dev.ubuntuhost stg.host=stg.ubuntuhost prod.host=ubuntuhost
■/home/ts0818/work/test_environment/env/.env.development
# development environment # 外部サーバーX X_SERVER_HOST=dev.x_server # 外部サーバーY Y_SERVER_HOST=dev.y_server # 外部サーバーZ Z_SERVER_HOST=dev.z_server
■/home/ts0818/work/test_environment/env/.env.staging
# stg environment # 外部サーバーX X_SERVER_HOST=stg.x_server # 外部サーバーY Y_SERVER_HOST=stg.y_server # 外部サーバーZ Z_SERVER_HOST=stg.z_server
■/home/ts0818/work/test_environment/env/.env.production
# production environment # 外部サーバーX X_SERVER_HOST=prod.x_server # 外部サーバーY Y_SERVER_HOST=prod.y_server # 外部サーバーZ Z_SERVER_HOST=prod.z_server
■/home/ts0818/work/test_environment/test_setting_environment.sh
#!/bin/bash # ホスト名を取得する host_name=$(uname -n) echo "[host_name]${host_name}" # 開発環境 env_dev=.env.development # ステージング環境(検証環境) env_stg=.env.staging # 本番環境 env_prod=.env.production env_dir=./env # デフォルトは、開発環境 env=${env_dir}/${env_dev} # ホストのリストを取得する host_list_file="${env_dir}/host_list.txt" declare -A host_list_map # 連想配列を作成する while IFS='=' read -r key value; do # 空白行は無視する if [ "${#key}" -eq 0 ] || [ "${#value}" -eq 0 ]; then continue; fi host_list_map["$key"]="$value" done < "${host_list_file}" # 環境毎の設定ファイルを読み込む for key in "${!host_list_map[@]}" do # ホスト名が、本番環境 if [ "${key}" = "prod.host" ] && [ "${host_name}" = "${host_list_map[${key}]}" ]; then echo "prod environment setting done." env=${env_dir}/${env_prod} break; fi # ホスト名が、ステージング環境(検証環境) if [ "${key}" = "stg.host" ] && [ "${host_name}" = "${host_list_map[${key}]}" ]; then echo "staging environment setting done." env=${env_dir}/${env_stg} break; fi # ホスト名が、開発環境 if [ "${key}" = "dev.host" ] && [ "${host_name}" = "${host_list_map[${key}]}" ]; then echo "development environment setting done." env=${env_dir}/${env_dev} break; fi done # 環境変数をexport export $(cat ${env}| grep -v "#" | xargs) echo "environment setting finish."
■/home/ts0818/work/test_environment/test_env04.sh
#!/bin/bash # 処理開始 start_date=$(date +"%F %T") echo "[${start_date}][start]test_04.sh is start" echo "test_04.sh is done." echo "[test_04.sh][X_SERVER_HOST]${X_SERVER_HOST}" echo "[test_04.sh][Y_SERVER_HOST]${Y_SERVER_HOST}" echo "[test_04.sh][Z_SERVER_HOST]${Z_SERVER_HOST}" # 処理終了 finish_date=$(date +"%F %T") echo "[${finish_date}][finished]test_04.sh is finished."
■/home/ts0818/work/test_environment/test_env03.sh
#!/bin/bash # 処理開始 start_date=$(date +"%F %T") echo "[${start_date}][start]test_03.sh is start" echo "test_03.sh is done." echo "[test_03.sh][X_SERVER_HOST]${X_SERVER_HOST}" echo "[test_03.sh][Y_SERVER_HOST]${Y_SERVER_HOST}" echo "[test_03.sh][Z_SERVER_HOST]${Z_SERVER_HOST}" # 処理終了 finish_date=$(date +"%F %T") echo "[${finish_date}][finished]test_03.sh is finished."
■/home/ts0818/work/test_environment/test_env02.sh
#!/bin/bash # 処理開始 start_date=$(date +"%F %T") echo "[${start_date}][start]test_02.sh is start" echo "test_02.sh is done." echo "before setting environment." echo "[test_02.sh][X_SERVER_HOST]${X_SERVER_HOST}" echo "[test_02.sh][Y_SERVER_HOST]${Y_SERVER_HOST}" echo "[test_02.sh][Z_SERVER_HOST]${Z_SERVER_HOST}" # 環境変数を読み込み source ./test_setting_environment.sh echo "after setting environment." echo "[test_02.sh][X_SERVER_HOST]${X_SERVER_HOST}" echo "[test_02.sh][Y_SERVER_HOST]${Y_SERVER_HOST}" echo "[test_02.sh][Z_SERVER_HOST]${Z_SERVER_HOST}" # 処理を委譲する if [ "$?" -eq 0 ]; then echo "delegate test_env03.sh" bash ./test_env03.sh fi # 処理を委譲する if [ "$?" -eq 0 ]; then echo "delegate test_env04.sh" bash ./test_env04.sh fi echo "delegate test_env03.sh test_env04.sh." echo "[test_02.sh][X_SERVER_HOST]${X_SERVER_HOST}" echo "[test_02.sh][Y_SERVER_HOST]${Y_SERVER_HOST}" echo "[test_02.sh][Z_SERVER_HOST]${Z_SERVER_HOST}" # 処理終了 finish_date=$(date +"%F %T") echo "[${finish_date}][finished]test_02.sh is finished."
■/home/ts0818/work/test_environment/test_env01.sh
#!/bin/bash # 処理開始 start_date=$(date +"%F %T") echo "[${start_date}][start]test_01.sh is start" echo "test_01.sh is done." echo "before test_02.sh is done." echo "[test_01.sh][X_SERVER_HOST]${X_SERVER_HOST}" echo "[test_01.sh][Y_SERVER_HOST]${Y_SERVER_HOST}" echo "[test_01.sh][Z_SERVER_HOST]${Z_SERVER_HOST}" # 処理を委譲 bash ./test_env02.sh echo "after test_02.sh is done." echo "[test_01.sh][X_SERVER_HOST]${X_SERVER_HOST}" echo "[test_01.sh][Y_SERVER_HOST]${Y_SERVER_HOST}" echo "[test_01.sh][Z_SERVER_HOST]${Z_SERVER_HOST}" # 処理終了 finish_date=$(date +"%F %T") echo "[${finish_date}][finished]test_01.sh is finished."
で、実行してみる。
⇧ test_env02.shで環境変数を読み込むようにしたので、test_env01.sh以外では、環境変数の値が参照できているようです。
シェルスクリプトで利用する環境変数が、他の環境変数を上書きしないように注意は必要ですかね。
環境変数については、
⇧ 上記サイト様によりますと、「printenv」コマンドを実行すれば、現在有効な環境変数の一覧が確認できるようです。
結論としては、環境変数の汚染に気を付けることが前提になりますが、一応、頑張れば、環境毎に利用する設定ファイルを変えることはできそうということですかね。
以下、参考にさせていただいたサイト様
■key=valueの形式のファイル読み込みについて
■連想配列について
実際の現場だと、どうしてるのかは気になるところですな。
毎度モヤモヤ感が半端ない...
今回はこのへんで。