AWS EC2起動時に パラメータストアの値 を 環境変数 に 設定する 方法

0 件のコメント

今回は「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階層。

ユーザーデータで実行するスクリプトでは以下の機能を利用します。 以下の機能が利用可能なポリシーをロールに与え、そのロールを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起動時に パラメータストアの値 を 環境変数 に 設定する 方法」についてまとめました。 参考になったでしょうか? 本記事がお役に立っていると嬉しいです!!

参考記事

最後に… このブログに興味を持っていただけた方は、 ぜひ 「Facebookページ に いいね!」または 「Twitter の フォロー」 お願いします!!