今回は「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
パラメータストアから情報を取ってくる際、環境ごとにアクセスできるかどうか制御したい場合、個別ポリシーとして以下のような設定を行うとさらに制限ができます。
1 2 3 4 5 6 7 8 9 10 11 12 | { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "ssm:GetParametersByPath" ], "Resource": "arn:aws:ssm:{region}:{account-id}:parameter/{project}/{env}/{type}/*" } ] } |
ユーザーデータ
お待たせしました…今回のメインのスクリプトです。 作成時に「ユーザーデータ」として以下のシェルスクリプトを「ファイルとして」選択、登録します。
user_data.sh
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | #!/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 の フォロー」 お願いします!!