やりたい事
タイトルの通り、crondで実行されるジョブに環境変数を与えたい
前提
パラメータストアから取得した値を環境変数にセットするシステム上でcrondが動いており、定期実行のジョブがある
課題
crondから実行したジョブから見える環境変数が制限される
crontabに環境変数を列挙することも可能だがパラメータストアから取得した情報を更新する場合、都度crontabの内容を書き換える必要が出てくる
パラメータストアに設定したらできるだけ自動で反映されてほしい
解決策
あるプロセスに環境変数をセットしておきcronジョブが動くタイミングでそのプロセスのprocfsから環境変数を取り出してセットする
例えばDockerならコンテナ起動時にセットされた環境変数は PID=1
のプロセスに設定されている
なのでcronジョブが動く際にそこから環境変数を取り出してexportした上で本来実行したい処理を子プロセスとして動かせばcrondから実行するジョブも期待した環境変数を読めるようになる
procfsから環境変数を読み出す
以前書いた記事を参照
crondで実行するジョブに環境変数をセットするためのラッパーを用意する
#!/bin/bash
/path/to/process-env 1 | while read line; do
echo "export $line"
done > /path/to/loadenv-$$
source /path/to/loadenv-$$
rm /path/to/loadenv-$$
"$@" 1> /proc/1/fd/1 2> /proc/1/fd/2
Dockerの場合は PID=1
のプロセスに起動時に与えられた環境変数がセットされているので process-env
の引数に 1
を与える
KEY=VALUEの形で取得できるのでexportを付与するため一度whileで回してexport KEY=VALUEの形にした上で一時ファイルに保存する
このファイルをsourceで読み込むことで cron-wrapper.sh
のプロセスに環境変数がセットされる
そして最後の "$@"
で引数に与えられたコマンドラインが実行され、標準出力、標準エラーに吐かれた文字列は PID=1
のプロセスの標準出力、標準エラーにリダイレクトすることでDockerコンテナのログに出力される
"$@"
は引数をそれぞれ""でくくったように展開される
command arg1 arg2 arg3
の場合、 "arg1" "arg2" "arg3"
となる
このため command php a.php foo
などと実行すると "php" "a.php" "foo"
と解釈されるのでphpコマンドに a.php foo
を与えたものが実行される
cronで実行してみる
* * * * * /path/to/cron-wrapper.sh printenv
環境変数取れていれば無事成功
パラメータストア使ってたら秘匿したい情報が入っているはずなのでprintenvで全部吐き出すのはオススメしない
実際には環境変数を読み出して内容が正しいかを検証するようなスクリプトを用意すると良いと思われる
コンテナ環境以外を考えてみる
例えばEC2のサーバーでcrondを動かしており、そこで環境変数をセットしたいケースを考える
(むしろコンテナでcrondを動かそうと思うケースが稀では?)
この場合は一旦パラメータストアから情報を取得し、環境変数にセットして何らかの子プロセスを立ち上げておき、そのプロセスの環境変数を読めるようにすればDockerの場合を考えた際の PID=1
の部分が状況に応じて変わるだけで同じように処理することが可能になる
なお、systemd経由して立ち上げたプロセスのファイルディスクリプタにリダイレクトすることは出来ないのでコマンド実行結果はリダイレクトしないようにしておく必要がある
雑にsystemdのユニットファイルを作る
[Unit]
Description=パラメータストア取得
After=network.target
[Service]
Type=simple
ExecStart=/path/to/get-params.sh
WorkingDirectory=/tmp
Restart=always
User=user
Group=user
[Install]
WantedBy=multi-user.target
/path/to/get-params.shを作る
#!/bin/bash
### パラメータストアから取得した値環境変数にセットする処理を記述する
tail -f /dev/null &
TAIL_PID=$!
echo $TAIL_PID > /path/to/get-params.pid
wait $TAIL_PID
パラメータストアから取得した値を環境変数にセットする処理とその後 tail -f /dev/null
で待ち続ける処理を記載する
一旦tailコマンドをバックグラウンドで実行しておき、PIDをファイルに保存する
ちなみにsystemctl status get-params.serviceすればtailのプロセスのPIDも知ることが出来るのでファイルにPIDを保存しないでも作れるはず
雑にsystemdでデーモンを動かす
準備が出来たらsystemdのいつものやつをやる
sudo systemctl daemon-reload
sudo systemctl enable get-params.service
sudo systemctl start get-params.service
あとは $(cat /path/to/get-params.pid)
で環境変数を読み込みたいPIDを参照できるので適宜先述のcron-wrapper.shを書き換えればOKで〜す!
systemdのデーモンをリロードする際にcronジョブが起動したケースを考えて数秒の待ちを入れて数回リトライするようにしておくと良いかもしれません
パラメータストアの更新をした場合
sudo systemctl restart get-params.service
get-params.serviceが起動する際にパラメータストアを参照するようにしたのでパラメータストアの更新があった場合にはデーモンを再起動すると反映される
それでは良いLinuxライフを
採用PR
株式会社viviONでは一緒に働く仲魔仲間を募集しております
オタク文化を盛り上げたいWebエンジニアのご応募お待ちしております!
Devトークもあります!