![見出し画像](https://assets.st-note.com/production/uploads/images/93194561/rectangle_large_type_2_a4c1cb9496563be37128d366d8b6bebd.png?width=1200)
【AWS】cronの代わりにSystems Managerを使ってコマンドを定期実行する方法のご紹介
Japan Digital Design株式会社(以下JDD)でインフラエンジニアをしている村田と申します。今回はcronを使わずにEC2インスタンスでコマンドを定期実行させる方法を調べたのでご紹介したいと思います。
背景
JDDでは運用負荷を考慮し、できる限りサーバレスな仕組みを使うことを推奨しています。
しかし、どうしてもEC2を使わなければならない場合が出てくるため、その場合でも可能な限りマネージドサービスを使った運用ができるように工夫しています。
今回はその工夫の一つとしてサーバ内部の定期実行タスクをcronを使わずに実現する方法をご紹介します。
やりたいこと
EC2内部で定期的にコマンドを実行させたい
失敗時にはslackに通知させたい
できる限りIaC化したいのでcronは使いたくない
実現方法
構成図
今回構築したシステムの全体像は以下の通りです。
![](https://assets.st-note.com/img/1668757256672-s0ONNVj4lh.png?width=1200)
利用するAWSサービスとその役割
AWS Systems Manager(SSM) RunCommand:EC2インスタンス内で任意のコマンドを実行させる
Amazon EventBridge Rule:定期的に RunCommandを実行する
Amazon Simple Notification Service(SNS):失敗した時などにSlackに通知させる
前提条件
SNSは事前に作成した上でARNを取得しておいてください。
コマンドを実行するインスタンスはマネージドノードにしておく(適切なIAMロールを付与する、SSMエージェントを設定するなど)必要があります。詳細はこちらをご確認ください。
実装
CloudFormation Template例
上記構成のうちEventBridgeとSSMを構築するためのCloudFormation Templateの例を記載します。
AWSTemplateFormatVersion: "2010-09-09"
Description: "EventBridge rule for execute SSM RunCommand"
Parameters:
Schedule:
Type: String
Description: Process Check Schedule
Default: "cron(0 * * * ? *)"
AlarmSnsTopicArn:
Description: Alarm SNS ARN
Type: String
TargetInstanceId:
Description: Target Instance of run command
Type: String
Command:
Description: Command executed inside instance
Type: String
Resources:
# 1. コマンドを定期実行するEventBridge
CronEvent:
Type: AWS::Events::Rule
Properties:
Description: Execute command periodically
Name: CronEvent
ScheduleExpression: !Ref Schedule
State: ENABLED
Targets:
- Arn: !Sub arn:aws:ssm:${AWS::Region}::document/AWS-RunShellScript
Id: RunShellCommand
RoleArn: !GetAtt [EventBridgeForRunCommandRole, Arn]
Input: !Sub '{ "commands": ["${Command}"] }'
RunCommandParameters:
RunCommandTargets:
- Key: InstanceIds
Values:
- !Ref TargetInstanceId
# 2. 1のEventBridgeに設定するロール
EventBridgeForRunCommandRole:
Type: AWS::IAM::Role
Properties:
RoleName: EventBridgeForRunCommandRole
Path: "/"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action: sts:AssumeRole
Principal:
Service: events.amazonaws.com
Policies:
- PolicyName: AllowRunCommand
PolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: AllowSendCommand
Effect: Allow
Action:
- "ssm:sendCommand"
Resource:
- !Sub "arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:instance/*"
- !Sub "arn:aws:ssm:${AWS::Region}:*:document/AWS-RunShellScript"
# 3. 1のコマンド実行結果を参照しFailedかTimeoutだったらアラームをあげるルール
CronMonitorEvent:
Type: AWS::Events::Rule
Properties:
Description: Monitoring execute command result
Name: CronMonitorEvent
State: ENABLED
RoleArn: !GetAtt [EventBridgeForPublishSnsRole, Arn]
EventPattern:
source:
- "aws.ssm"
detail-type:
- "EC2 Command Invocation Status-change Notification"
detail:
instance-id:
- !Ref TargetInstanceId
document-name:
- !Sub arn:aws:ssm:${AWS::Region}::document/AWS-RunShellScript
status:
- "Failed"
- "Timedout"
Targets:
- Arn: !Ref AlarmSnsTopicArn
Id: 'AlarmSnsTopicCronEventMonitor'
# 4. 3のEventBridgeに設定するロール
EventBridgeForPublishSnsRole:
Type: AWS::IAM::Role
Properties:
RoleName: EventBridgeForPublishSnsRole
Path: "/"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action: sts:AssumeRole
Principal:
Service: events.amazonaws.com
Policies:
- PolicyName: AllowPublishSns
PolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: AllowSendAlarm
Effect: Allow
Action:
- "sns:Publish"
Resource:
- !Sub "arn:aws:sns:${AWS::Region}:${AWS::AccountId}:*"
解説
今回作成した4つのリソースについて解説します。
1. コマンドを定期実行するEventBridge
今回の主な目的であるコマンド実行を行うリソースです。
このEventBridgeからSSMドキュメント(今回は "AWS-RunShellScript"というドキュメント)を実行します。
このドキュメントに渡す引数を "Input"で指定します。この引数が今回は実行するコマンドとなります。
詳細は以下のリファレンスをご参照ください。
2. 1に設定するIAM Role
今回はEventBridgeからコマンドを実行するためロールによりAWSのAPI実行権限を付与してあげる必要があります。
AWSでは大まかに言うと「誰に」「何の権限」を付与するかを設定する必要がありますが、Roleでは「誰に」の部分を、Policyでは「何の権限」を規定します。
今回はEventBridgeが実行主体なので「誰に」にあたる Principalには "Service: events.amazonaws.com" を指定します。
また今回は "EC2に" "コマンドを実行させる" ことが目的ですので、「何の権限」をの部分には Resource: EC2インスタンス, Action: "ssm:sendCommand" を指定します。
詳細は以下のリファレンスをご参照ください。
3. 1のコマンド実行結果を参照しアラームをあげるルール
1のEventBridgeが起動すると、「イベント」が生成されます。このイベントには様々な情報が含まれるのですが、1のEventBridgeのイベントはコマンドの実行結果が含まれます。
このコマンドの実行結果が「Timedout」か「Failed」だった場合にはSNSを通じてアラームを発報します。
AWSでは常に様々なイベントが生成されているので、どのイベントかを特定する情報を "EventPattern" に記載します。今回は以下の条件でイベントを特定します。
(以下cloudformationのコード再掲です)
EventPattern:
source:
- "aws.ssm"
detail-type:
- "EC2 Command Invocation Status-change Notification"
detail:
instance-id:
- !Ref TargetInstanceId
document-name:
- !Sub arn:aws:ssm:${AWS::Region}::document/AWS-RunShellScript
status:
- "Failed"
- "Timedout"
source: イベントの発行主体(EventBridgeではないことに注意)
detail-type: イベントの種類
(イベントは種類毎に格納される情報が異なるためここで指定する)detail: イベントを特定するための詳細情報
instance-id:コマンドを実行したEC2のID
document-name: 実行されたSSMドキュメントのARN
status: 実行結果
イベントについては以下をご参照ください。
4. 3に設定するIAM Role
3のEventBridgeはコマンド実行が失敗 or Timeoutの場合にSNSを通じてアラームを発報する役割を持つためSNSの権限を付与しています。
感想
オンプレミスやプライベートクラウドの時代にはcronを使ってコマンドを定期実行するのが主流でしたが、エラー時の処理のためにメールを設定したりする必要があり、ちゃんと設計すると面倒だったイメージがありました。
今回、SSMやEventBridgeを活用することで意外と簡単に且つ運用が楽な方法でこれらを実現できてしまうので改めてAWSの利便性を感じました。
EC2の運用に課題をお持ちの方は是非お試し頂ければと思います。
最後に
JDDでは各種エンジニアを募集しております。少しでも気になることがありましたら、カジュアル面談も実施しておりますのでお気軽にご連絡下さい。
この記事に関するお問い合わせはこちらまでお願いします。
Japan Digital Design 株式会社
Technology & Development Division
Engineer
Ken Murata