今回は「AWS EC2起動時に パラメータストアの値 を 環境変数 に 設定する 方法」についてまとめます。
起動時にパラメータストアから値とってきて環境変数に設定なんてよく聞きますが…調べても自分が欲しい情報がストレートになかったので、今回まとめてみました。
概要
EC2起動時にパラメータストアの値を取りに行き、EC2の環境変数へマッピングしていくような仕組みを作っていきます(以下の図のイメージ)。 今回は「VPC」や「EC2」につけられたタグ名をキーに「パラメータストア」の値を探してくるようなものを作ります。
「VPC」のタグには「Project」「Env」を、「EC2」のタグには「Type」を定義しておきます。
これらタグを利用して、「パラメータストア」では「/{Project}/{Env}/{Type}/*」の環境変数を取得、設定するような仕組みです。
パラメータストア
今回は以下のようなパラメータを用意するものとします。
| 名前 | 種類 |
|---|---|
| /sample-project/stg/app/MYSQL_HOST | String |
| /sample-project/stg/app/MYSQL_PORT | String |
| /sample-project/stg/app/MYSQL_DATABASE | String |
| /sample-project/stg/app/MYSQL_USERNAME | String |
| /sample-project/stg/app/MYSQL_PASSWORD | SecureString |
"/"(バックスラッシュ) で分類するとよいようなので、この方法を採用しました。 今回は以下のような階層構造を前提として設計しています。
/{VPCのProjectタグ}/{VPCのEnvタグ}/{EC2のTypeタグ}/{環境変数名}
それぞれ以下のような想定で階層をきっています。
- VPCのProjectタグ
- プロジェクト名を想定。
- VPCのEnvタグ
- リリース環境を想定。
dev,stg,prodなど。 - EC2のTypeタグ
- サーバー種別を想定。
web,app,batch,dbなど。
これを元にパスを書き直すと以下のようになります。
/{プロジェクト名}/{リリース環境名}/{サーバー種別}/{環境変数名}
VPC
タグ
おそらくVPCの名前には「プロジェクト名+環境」で設定することが多い気がします。 今回はそれを分解して以下2つのタグを設定しておきます。
Project- プロジェクト名。今回の例だと
sample-project。 パラメータストアにおける第1階層。 Env- リリース環境。
dev,stg,prodなど。 パラメータストアにおける第2階層。
EC2
EC2は「Amazon Linux」前提で記載しています。 今回設定するのは以下3か所です。
- どのパラメータストアを取り出すか指定するためのタグ
- パラメータストアなどにアクセスするための権限設定
- 起動時にパラメータストアから値をとってきて環境変数に設定するスクリプト(ユーザーデータ)
タグ
EC2は個別サーバーになります。 実際はいくつかの種類に応じて分類されると思いますので、今回は「サーバー種別」として「Type」を設定しておきこれを取得するようなサンプルにしました。
Type- サーバー種別。
web,app,batch,dbなど。 パラメータストアにおける第3階層。
IAMロール
ユーザーデータで実行するスクリプトでは以下の機能を利用します。 以下の機能が利用可能なポリシーをロールに与え、そのロールをEC2に与えておきます。
- ec2:DescribeInstances
- ec2:DescribeVpcs
- ssm:GetParametersByPath
個別に組み込んでも良いのですが、とりあえず使うのであれば以下の組み込みポリシーが使えそうでした。
- AmazonEC2ReadOnlyAccess
- AmazonSSMReadOnlyAccess
パラメータストアから情報を取ってくる際、環境ごとにアクセスできるかどうか制御したい場合、個別ポリシーとして以下のような設定を行うとさらに制限ができます。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ssm:GetParametersByPath"
],
"Resource": "arn:aws:ssm:{region}:{account-id}:parameter/{project}/{env}/{type}/*"
}
]
}
ユーザーデータ
お待たせしました…今回のメインのスクリプトです。 作成時に「ユーザーデータ」として以下のシェルスクリプトを「ファイルとして」選択、登録します。
user_data.sh
#!/bin/bash
#
# Install required modules.
#
curl -o /usr/local/bin/jq -L https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64
chmod +x /usr/local/bin/jq
#
# Initialize environment variables
#
INITENV_SHELL="/etc/rc.d/initenv.sh"
SETENV_SHELL="/etc/profile.d/setenv.sh"
# Create initenv.sh
# initialize environment variable script -START-----------------------
cat >> "$INITENV_SHELL" <<"__EOS__"
#!/bin/bash
# Environment variables file name
SETENV_SHELL="/etc/profile.d/setenv.sh"
# Load environmental variables
INSTANCE_ID=$(curl 169.254.169.254/latest/meta-data/instance-id)
REGION=$(curl 169.254.169.254/latest/meta-data/placement/region)
ZONE=$(curl 169.254.169.254/latest/meta-data/placement/availability-zone)
VPC_ID=$(aws ec2 describe-instances --region ${REGION} --instance-ids ${INSTANCE_ID} --query "Reservations[0].Instances[0].NetworkInterfaces[0].VpcId" --output text)
VPC_NAME=$(aws ec2 describe-vpcs --region ${REGION} --vpc-ids ${VPC_ID} --query "Vpcs[0].Tags[?Key=='Name'].Value" --output text)
VPC_PROJECT=$(aws ec2 describe-vpcs --region ${REGION} --vpc-ids ${VPC_ID} --query "Vpcs[0].Tags[?Key=='Project'].Value" --output text)
VPC_ENV=$(aws ec2 describe-vpcs --region ${REGION} --vpc-ids ${VPC_ID} --query "Vpcs[0].Tags[?Key=='Env'].Value" --output text)
EC2_NAME=$(aws ec2 describe-instances --region ${REGION} --instance-ids ${INSTANCE_ID} --query "Reservations[0].Instances[0].Tags[?Key=='Name'].Value" --output text)
EC2_TYPE=$(aws ec2 describe-instances --region ${REGION} --instance-ids ${INSTANCE_ID} --query "Reservations[0].Instances[0].Tags[?Key=='Type'].Value" --output text)
SSM_PARAMETER_STORE=$(aws ssm get-parameters-by-path --region ${REGION} --path "/${VPC_PROJECT}/${VPC_ENV}/${EC2_TYPE}" --with-decryption)
# Output environment initialize scripts.
cat > "${SETENV_SHELL}" <<EOF
#
# [$(date '+%Y-%m-%dT%H:%M:%S+09:00' -d '9 hour')] Initialized scripts.
#
export INSTANCE_ID=${INSTANCE_ID}
export REGION=${REGION}
export ZONE=${ZONE}
export VPC_ID=${VPC_ID}
export VPC_NAME="${VPC_NAME}"
export VPC_PROJECT="${VPC_PROJECT}"
export VPC_ENV="${VPC_ENV}"
export EC2_NAME="${EC2_NAME}"
export EC2_TYPE="${EC2_TYPE}"
EOF
for PARAMS in $(echo ${SSM_PARAMETER_STORE} | /usr/local/bin/jq -r '.Parameters[] | .Name + "=" + .Value'); do
echo "export ${PARAMS##*/}"
done >> "${SETENV_SHELL}"
chmod +x "$SETENV_SHELL"
source "$SETENV_SHELL"
__EOS__
# initialize environment variable script -END-------------------------
# Modify initenv.sh access control.
chmod +x "$INITENV_SHELL"
# Add startup service
cat > "/etc/systemd/system/initenv.service" <<EOF
[Unit]
Description=Initialize environment variables.
After=network.target
[Service]
Type=oneshot
User=root
Group=root
ExecStart="$INITENV_SHELL"
[Install]
WantedBy=default.target
EOF
systemctl daemon-reload
systemctl enable initenv
# Load environment variables.
bash "$INITENV_SHELL"
source "$SETENV_SHELL"
大まかには「initenv.sh で AWS から必要な環境変数を取得して setenv.sh を作成、作成された setenv.sh を読み込むことで環境変数に埋め込みを行う」という仕組みになっています。 initenv.sh は サービス 登録しておくことで再起動した際、読み込みなおすような仕組みになっています。
今回は「AWS EC2起動時に パラメータストアの値 を 環境変数 に 設定する 方法」についてまとめました。 参考になったでしょうか? 本記事がお役に立っていると嬉しいです!!
参考記事
- Qiita - パラメータストアからEC2に環境変数を設定する
- aws - IAM ポリシーを使用して Systems Manager パラメータへのアクセスを制限する
- aws - インスタンスメタデータの取得
- Qiita - Systemd メモ書き
- 学生たちの技術ブログ - Systemdで起動時にスクリプトを実行する
最後に… このブログに興味を持っていただけた方は、 ぜひ 「Facebookページ に いいね!」または 「Twitter の フォロー」 お願いします!!
