目次
ひさびさに 1 週間毎朝 30 分ウォーキングしただけで股関節を痛めました、那須です。 ちょっとした運動は毎日継続することが大切だと気づきました。
自社業務とはまったく関係ないところで、Amazon Elasticsearch Service (以下 ES ) の運用をしています。 この ES、諸事情により日々インデックスがたまっていきます。
先日までこの ES の EBS ボリュームサイズも都度手作業で拡張していました。 それがさすがにつらい…ということで自動拡張する仕組みを考えて実装しました。 今のところはうまく動いているので、似たような悩みを持っている方向けにどうやったかをご紹介します。
構成
以下のような構成で作成してきます。
さあやってみよう!
実際にやってみた手順をご紹介しますね。 基本は CloudFormation でのデプロイです。
すべてを CloudFormation でデプロイしたかったのですが、SSM Automation ドキュメントの作成だけは AWS CLI v2 で作成しています。 理由は、CloudFormation でSSM Automation ドキュメントのテンプレートを更新すると別のドキュメントに置換されてしまって過去の内容が旧バージョンとして残らないのが気に入らなかったから、だけです。
そんなの関係ない!って方は CloudFormation でデプロイしてもいいと思います。
CloudFormation でデプロイしても新バージョン作成して更新できるよ!って方がいれば教えてください。 もしできるならめっちゃ嬉しいです!
まずは IAM ロールを準備
SSM Automation ドキュメント実行のための IAM ロールを作成します。 サービス es の参照アクションと UpdateElasticsearchDomainConfig アクションだけを許可するだけで十分です。
ファイル名は SSM-AutomationServiceRole.yml にしました。
AWSTemplateFormatVersion:
'2010-09-09'
Description:
AWS CloudFormation template IAM Roles for Systems Manager | Automation for ES EBS size update
Resources:
EBSSizeUpdateForESAutomationRole:
Type:
AWS
:
:
IAM
:
:
Role
Properties:
AssumeRolePolicyDocument:
Version:
'2012-10-17'
Statement:
-
Effect
:
Allow
Principal:
Service:
-
ssm.amazonaws.com
Action:
sts
:
AssumeRole
Path:
"/"
RoleName:
EBSSizeUpdateForESAutomationRole
EsEbsAutomationPolicy:
Type:
AWS
:
:
IAM
:
:
ManagedPolicy
Properties:
PolicyDocument:
Version:
"2012-10-17"
Statement:
-
Effect
:
"Allow"
Action:
-
"es:Describe*"
-
"es:UpdateElasticsearchDomainConfig"
-
"es:Get*"
-
"es:List*"
Resource:
"*"
ManagedPolicyName:
EBSSizeUpdateForESAutomationPolicy
Roles:
-
Ref
:
EBSSizeUpdateForESAutomationRole
Outputs:
Role:
Value:
!GetAtt EBSSizeUpdateForESAutomationRole.Arn
Export:
Name:
EBSSizeUpdateForESAutomationRoleArn
この yaml ファイルを使って CloudFormation スタックを作成しましょう。
aws cloudformation create-stack --stack-name CreateESEBSUpdateAutomationRole --template-body
file
:
//SSM-AutomationServiceRole
.yml --capabilities CAPABILITY_NAMED_IAM
aws cloudformation wait stack-create-complete --stack-name CreateESEBSUpdateAutomationRole
RoleArn=`aws cloudformation describe-stacks --stack-name CreateESEBSUpdateAutomationRole | jq -r
'.Stacks[].Outputs[].OutputValue'
`
RoleArn 変数を準備している理由は、この後の SSM Automation ドキュメント作成で IAM ロールの ARN を指定する箇所があるので、そこで動的に指定したいためです。
IAM ロールの準備はこれで OK です。
SSM Automation ドキュメントを作成する
実際に ES の EBS ボリュームを拡張する仕組みである SSM Automation のドキュメントのコンテンツを準備します。 ファイル名は ESEBSUpdateAutomation_source.yml にしました。
ステップは以下の 4 つあります。
1. ES の EBS ボリューム拡張を行う時刻と拡張サイズを決める(本番環境は 30GB、それ以外は 10GB 追加するようにしています)
2. EBS ボリューム拡張時刻まで待つ
3. ES EBS ボリューム拡張を行う
4. 結果を確認する(タイムアウトは 2 時間にしています)
description:
schemaVersion:
'0.3'
assumeRole:
EBSSizeUpdateForESAutomationRole.Arn
outputs:
-
Schedule.Schedule
-
UpdateES.ChangedSize
parameters:
DomainName:
type:
String
mainSteps:
-
name
:
Schedule
action:
'aws:executeScript'
inputs:
Runtime:
python3.7
Handler:
script_handler
Script:
|
def script_handler(events
,
context)
:
from datetime import datetime
,
timedelta
,
timezone
import boto3
JST = timezone(timedelta(hours=+9)
,
'JST'
)
now = datetime.now(JST)
if now.hour >= 2
:
schedule = now + timedelta(days=1)
else:
schedule = now
schedule = schedule.replace(hour=2
,
minute=0
,
second=0
,
microsecond=0)
client = boto3.client(
'es'
)
response = client.describe_elasticsearch_domain_config(
DomainName = events
[
'DomainName'
]
)
CurrentEBSSize = int(response
[
'DomainConfig'
]
[
'EBSOptions'
]
[
'Options'
]
[
'VolumeSize'
]
)
if events
[
'DomainName'
]
==
'production'
:
increase_size =
30
else:
increase_size =
10
TargetEBSSize = CurrentEBSSize + increase_size
return
{
'schedule'
:
schedule.isoformat()
,
'CurrentEBSSize'
:
CurrentEBSSize
,
'TargetEBSSize'
:
TargetEBSSize
}
InputPayload:
DomainName:
'{{ DomainName }}'
outputs:
-
Selector
:
$.Payload.schedule
Type:
String
Name:
Schedule
-
Type
:
Integer
Name:
CurrentEBSSize
Selector:
$.Payload.CurrentEBSSize
-
Name
:
TargetEBSSize
Selector:
$.Payload.TargetEBSSize
Type:
Integer
isEnd:
false
nextStep:
WaitForExecute
-
name
:
WaitForExecute
action:
'aws:sleep'
inputs:
Timestamp:
'{{ Schedule.Schedule }}'
nextStep:
UpdateES
-
name
:
UpdateES
action:
'aws:executeAwsApi'
inputs:
Service:
es
Api:
UpdateElasticsearchDomainConfig
DomainName:
'{{ DomainName }}'
EBSOptions:
VolumeSize:
'{{ Schedule.TargetEBSSize }}'
outputs:
-
Name
:
ChangedSize
Selector:
$.DomainConfig.EBSOptions.Options.VolumeSize
Type:
Integer
nextStep:
WaitForProcessing
-
name
:
WaitForProcessing
action:
'aws:waitForAwsResourceProperty'
inputs:
Service:
es
Api:
DescribeElasticsearchDomain
DomainName:
'{{ DomainName }}'
PropertySelector:
$.DomainStatus.Processing
DesiredValues:
-
'False'
timeoutSeconds:
7200
3 行目に assumeRole を指定しているのですが、ここで先ほど作成した IAM ロールの ARN を指定します。
IAM ロールの ARN が指定できれば、あとはその内容の通りに SSM Automation ドキュメントを作成するコマンドを実行するだけです。 ドキュメント名は ESEBSUpdateAutomation にしました。
DocumentName=ESEBSUpdateAutomation
sed -e "s;EBSSizeUpdateForESAutomationRole.Arn;'${RoleArn}';" ./ESEBSUpdateAutomation_source.yml > ESEBSUpdateAutomation.yml
aws ssm create-document --content file://ESEBSUpdateAutomation.yml --name $DocumentName --document-format YAML --document-type Automation
これで SSM Automation ドキュメントの準備はOKです。
EventBridge でひっかける
仕組みが準備できたので、あとは実行するための EventBridge ルールを作成するための CloudFormation テンプレートを作成します。 実行トリガーは、特定の CloudWatch Alarm でアラーム状態になった時としています。
テンプレート名は ESEBSUpdateEvents.yml としました。
AWSTemplateFormatVersion:
"2010-09-09"
Description:
A template for EventBridge
Parameters:
AutomationDocumentName:
Type:
String
Description:
Specify the automation document name for execution.
Resources:
AlarmToAutomationRole:
Type:
AWS
:
:
IAM
:
:
Role
Properties:
AssumeRolePolicyDocument:
Version:
"2012-10-17"
Statement:
-
Effect
:
Allow
Principal:
Service:
-
events.amazonaws.com
Action:
sts
:
AssumeRole
Path:
"/"
RoleName:
AlarmToAutomationRole
AlarmToAutomationPolicy:
Type:
AWS
:
:
IAM
:
:
ManagedPolicy
Properties:
PolicyDocument:
Version:
"2012-10-17"
Statement:
-
Effect
:
"Allow"
Action:
-
"ssm:StartAutomationExecution"
Resource:
!Sub
"arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:automation-definition/ESEBSUpdateAutomation:$DEFAULT"
ManagedPolicyName:
AlarmToAutomationPolicy
Roles:
-
!Ref AlarmToAutomationRole
AlarmToAutomationEventRule:
Type:
AWS
:
:
Events
:
:
Rule
Properties:
Description:
Execute SSM Automation from CloudWatch Alarm
EventPattern:
source:
-
aws.cloudwatch
detail-type:
-
CloudWatch Alarm State Change
detail:
alarmName:
-
ES_FreeStorageSpace_production
-
ES_FreeStorageSpace_staging
-
ES_FreeStorageSpace_development
state:
value:
-
ALARM
Name:
ExecuteSSMAutomation-ESEBSIncrease
State:
ENABLED
Targets:
-
Arn
:
!Sub arn
:
aws
:
ssm
:
$
{
AWS
:
:
Region
}
:
$
{
AWS
:
:
AccountId
}
:
automation-definition/$
{
AutomationDocumentName
}
Id:
AlarmToAutomationEventRule
InputTransformer:
InputPathsMap:
DomainName:
$.detail.configuration.metrics
[
0
]
.metricStat.metric.dimensions.DomainName
InputTemplate:
'{"DomainName": [<DomainName>]}'
RoleArn:
!GetAtt AlarmToAutomationRole.Arn
このテンプレートで CloudFormation スタックを作成しましょう。
aws cloudformation create-stack --stack-name ESEBSUpdateEvents --template-body
file
:
//ESEBSUpdateEvents
.yml --parameters ParameterKey=AutomationDocumentName,ParameterValue=$DocumentName --capabilities CAPABILITY_NAMED_IAM
aws cloudformation wait stack-create-complete --stack-name ESEBSUpdateEven
ちょっと原因がわかってないのですが、この手順で EventBridge ルールがうまく動かないことがあります。 その場合は、イベントパターンがカスタムで定義されていると思うので、AWS サービスから選択する形で定義し直してください。 出来上がりはまったく同じイベントパターンができあがりますが、これで大丈夫です。
さあ、これで準備完了です!
いざテスト!
CloudWatch Alarm でアラーム状態にすれば、ES EBS ボリュームを拡張するために SSM Automation がアップを始めます。 アラーム状態にするには、以下のようなコマンド一発実行すれば OK です。
aws cloudwatch
set
-alarm-state --alarm-name ES_FreeStorageSpace_staging --state-reason
"test"
--state-value ALARM
指定時刻になれば ES EBS ボリュームサイズが拡張されていると思います!
さいごに
ES に限らず EBS ボリュームの拡張っていろんな場面であると思います。 そのたびに手作業でやっていると大変なので、もし気軽に拡張できるシステムなのであればこのように自動化してしまいましょう。 これで EBS ボリュームの空き容量のことを気にしておく必要はなくなりますよ!(不要なデータがあるならそれを削除した方がいいに越したことはないです