変数展開がされないやんけ・・・!
TL;DR
- 値をローテしない場合は Parameter Store がシンプルで使いやすい。
- systemd の EnvironmentFile は変数やコマンドは展開されない。
- 事前に systemctl set-environment で値を設定することが可能。
はじめに
あけましておめでとうございます。
新年一本目の執筆権をいただきました Bell です。
今回は AWS Systems Manager の Parameter Store という機能を利用して
環境ごとの設定値差分を吸収しようとした際のお話です。
ことの発端
お昼休憩を終えたある日の午後
Bell: ゴクゴク… (食後に黒烏龍茶飲んでるからお昼ごはんの脂肪分は帳消しだな)
長田: ベルくん, ちょっと相談してもいいかな?
Bell: はいはい, なんでしょう?
長田: AWS 上の Linux で環境変数を使って本番と検証で DB の接続先とか, 外部連携の鍵とか切り替えたいんだけど・・・
なんかいい方法知らないかな?
Bell: そうですね・・・ Parameter Store ってのがあるのでそれでどうですかね?
ということで, AWS Systems Manager の Parameter Store の使い方をまとめます。
Parameter Store から値の取得
まずは parameter store から値を取得するまでを検証しましょう。
構成としては以下のイメージです。
EC2 インスタンス の aws cli を使って Parameter Store から値を取得し環境変数に設定します。
設定した環境変数を読み込んでアプリケーションが起動します。
シンプルですね。
ここでは DB のユーザ名とパスワードを取得する場合を想定して実践します。
値の設定
まずは Parameter Store に値を設定しましょう。
マネジメントコンソールから Systems Manager (ssm) を検索して画面を表示します。
左メニューのパラメータストアからパラメータの設定画面を表示します。
パラメータの作成ボタンから登録に進みます。
パラメータの設定をしていきます.
key | val |
---|---|
名前 | dev.User |
説明 | db user name |
利用枠 | 標準 |
タイプ | 文字列 |
データ型 | text |
値 | dev-ssm-ps-db-user |
パラメータを作成で確定してもう一度パラメータの作成ボタンからパスワード情報を登録します。
key | val |
---|---|
名前 | dev.Password |
説明 | db user password |
利用枠 | 標準 |
タイプ | 文字列 |
データ型 | text |
値 | dev-ssm-ps-db-password |
パラメータの名前には 環境の識別や IAM policy でのアクセス制御のため環境毎のプレフィックスを付与しました。
これで Parameter Store の設定は完了です。 直感的でいいですね。
EC2 インスタンスの作成
値を取得する EC2 インスタンスを作成します。
Parameter Store にアクセスするための権限が必要ですので、
まずは IAM Role とそれに適用する IAM Policy を作成しましょう。
マネジメントコンソールから IAM を検索して画面を表示し左メニューのロールを選択します。
EC2 インスタンスに付与するロールを作成したいので, ロールからロールを作成を選択します。
EC2 に付与するロールのため、ユースケースの選択には EC2 を選んで 次のステップを選択します。
権限を細かく制御したいのでポリシーの作成から新しくポリシーを作成します。
新しいポリシーに ec2:descrive-instance
と ssm:GetParameters
の権限を付与します。
これで Systems Manger に設定した秘匿情報にアクセスすることができるようになります。
パラメータ個別にアクセス制限を設定することも可能なので、本番と開発で参照の可否を分けることもできます。
今回は dev.
で始めるパラメータの参照権限を付与しています。
インスタンス自身の Name タグのプレフィックスを取得するために ec2:descrive-instance
も付与しています。
ec2:descrive-instance
項目 | 値 |
---|---|
サービス | EC2 |
アクション | DescribeInstances |
ssm:GetParameters
項目 | 値 |
---|---|
サービス | Systems Manager |
アクション | GetParameters |
リソース Region | ap-northeast-1 |
リソース Account | そのまま |
リソース Parameter name… | dev.* |
設定できたら次のステップに進みましょう。
タグは Name
タグに dev-ssm-param-policy
とします。
ポリシーの確認画面の名前も同様に dev-ssm-param-policy
を設定します。
作成できましたらロールの作成画面に戻り、再読み込みをしてから dev-ssm-param-policy
をチェックして次のステップを選択します。
Name
タグに dev-ssm-param-role
を設定して次のステップを選択します。
ロール名にも dev-ssm-param-role
を設定してロールの作成をします。
ロールができたら EC2 インスタンスを作成します。
ネットワークなどのリソースは必要に応じてよしなに作成してください。
EC2 を検索してインスタンスの画面を表示します。
新しくインスタンスを作成するのでインスタンスを起動
基本的にデフォルトや無料枠の設定で大丈夫です。
インスタンスの詳細の設定の IAM ロールに先程作成した dev-ssm-param-role
を選択します。Name
タグには dev-ssm-ps-inst
を設定します。
細かい設定はご自身のAWS環境に応じて設定してください。参考までに例で設定した内容をまとめて載せておきます。
内容に問題が無いことこ確認して作成しましょう。
インスタンスの状態が 実行中
になれば作成完了です。
インスタンスに SSH 接続して値が取れるか確認します。
aws ssm get-parameters --region ap-northeast-1 --name dev.User --query "Parameters[0].Value" --output text
parameter store に設定した値が取得できていればインスタンスの作成が完了です。
systemd の設定
作成したインスタンスの systemd に parameter store の値を読み込んでサービスを起動する設定を入れましょう。
追加する設定およびスクリプトは以下の 3 つです。
- ssm-param.sh
- 起動するサービスを想定したスクリプト
- DBのユーザ名とパスワードを一定間隔でログに出力する
- ssm-param-pre-exec.sh
- parameter store から値を読み込んで設定するスクリプト
- ssm-param.sh の前に起動する
- ssm-param.service
- ssm-param.sh を起動するための systemd の定義ファイル
内容は以下のとおりです。
#!/bin/bash
LOG_FILE=$(dirname $0)/$(basename $0 .sh).log
while true; do
echo "Connection DB by ${DB_USER}/${DB_PASSWORD}" >> $LOG_FILE
sleep 10
done;
10 秒ごとにユーザ名とパスワードを出力しています。
DB 接続のヘルスチェックみたいなイメージですね。
スクリプトと同じ場所にログファイルを作成して追記しています。
#!/bin/bash
# region の設定
export AWS_DEFAULT_REGION=$(/usr/bin/curl 169.254.169.254/latest/meta-data/placement/region 2>/dev/null)
# インスタンス情報の取得
INSTANCE_ID=$(/usr/bin/curl 169.254.169.254/latest/meta-data/instance-id 2>/dev/null)
INSTANCE_NAME=$(/usr/bin/aws ec2 describe-instances --instance-ids ${INSTANCE_ID} --query "Reservations[0].Instances[0].Tags[?Key=='Name'].Value" --output text)
# パラメータの取得
DB_USER=$(/usr/bin/aws ssm get-parameters --name ${INSTANCE_NAME%%-*}.User --query "Parameters[0].Value" --output text)
DB_PASSWORD=$(/usr/bin/aws ssm get-parameters --name ${INSTANCE_NAME%%-*}.Password --query "Parameters[0].Value" --output text)
# パラメータを systemd に設定
systemctl set-environment DB_USER=${DB_USER}
systemctl set-environment DB_PASSWORD=${DB_PASSWORD}
aws cli を使って parameter store から設定値を取得して systemctl コマンドを利用し設定しています。
設定値の取得のためにリージョンの情報やインスタンスの情報が必要なのではじめに取得しています。
[Unit]
Description = load environment from aws system manager parameter strore
[Service]
User=root
ExecStartPre=/usr/bin/bash /opt/ssm-param-pre-exec.sh
ExecStart=/usr/bin/bash /opt/ssm-param.sh
ExecStartPre で値を設定して ExecStart でスクリプトを起動します。
シンプルですね。
設定が終わったら起動してログファイルを見てみましょう。
sudo systemctl start ssm-param
tail -f /opt/ssm-param.log
parameter store に保存された値が読み込めているのが確認できるかと思います。
失敗談: EnvironmentFile の設定
systemd の設定に環境変数を読み込むための EnvironmentFile が設定できるのですが、
こちらの挙動でハマったことがありました。
EnvironmentFile は変数の展開がされないのです!!
試しにサービスの設定を以下のように修正してみましょう。
[Unit]
Description = load environment from aws system manager parameter strore
[Service]
User=root
EnvironmentFile=/opt/ssm-param-pre-exec.sh
ExecStart=/usr/bin/bash /opt/ssm-param.sh
サービスを再起動して環境変数の値を確認してみましょう。
変数にコマンドが出力されているのがわかるでしょうか?
このように EnvironmentFile では変数やコマンドが展開されないので注意しましょう・・・?