Lambda から EC2 インスタンス内でコマンドを実行する
Amazon SSM を利用することで、Lambda を使って、EC2インスタンス内で任意のコマンドを実行することができます。
これを CloudWatch Events と組み合わせると、従来CRONによって行っていたような定期実行タスクを、特定のEC2インスタンスをSPOFにすることなく実装することができ、オートスケーリングを有効にしたEC2クラスタ環境などで便利です。
Amazon EC2 Simple Systems Manager (SSM)
対象のEC2インスタンスには事前にSSMエージェントをインストールしておく必要があります。 手動や、cloud-initなどでインストールしておきます。
CloudFormation テンプレート(抜粋)
Parameters: Command: Type: String # 実行したい Lambda Function # 起動中のEC2インスタンスから1件を取り出してコマンドを実行する Function: Type: AWS::Lambda::Function Properties: Code: ZipFile: |+ const AWS = require('aws-sdk'); const ssm = new AWS.SSM({apiVersion: '2014-11-06'}); const ec2 = new AWS.EC2({apiVersion: '2016-11-15'}); const { Command } = process.env; const sleep = msec => new Promise(resolve => setTimeout(resolve, msec)); const debug = (key, object) => { console.log(`DEBUG: ${key}\n`, JSON.stringify(object)); } class InstanceNotFoundError extends Error { constructor(message) { super(message); this.name = 'InstanceNotFoundError'; } } exports.handler = async (event, context) => { console.log("INFO: request Recieved.\nEvent:\n", JSON.stringify(event)); const describeInstancesParams = { Filters: [ { Name: "tag:Service", Values: [ "app" ] }, { Name: "instance-state-name", Values: [ "running" ] } ] }; debug("describeInstancesParams", describeInstancesParams); const describeInstancesResult = await ec2.describeInstances(describeInstancesParams).promise(); debug("describeInstancesResult", describeInstancesResult); const reservation = describeInstancesResult.Reservations[0]; if (!reservation) { throw new InstanceNotFoundError("App is Not Found"); } const instanceStatus = reservation.Instances[0]; const sendCommandParams = { DocumentName: 'AWS-RunShellScript', InstanceIds: [instanceStatus.InstanceId], Parameters: { commands: [Command], executionTimeout: ['3600'] }, MaxConcurrency: '1', MaxErrors: '0', TimeoutSeconds: 3600, }; debug("sendCommandParams", sendCommandParams); const sendCommandResult = await ssm.sendCommand(sendCommandParams).promise(); debug("sendCommandResult", sendCommandResult); const results = { ec2InstanceId: instanceStatus.InstanceId, sendCommandParams: sendCommandParams, sendCommandResult: sendCommandResult }; debug("results", results); return results; }; Environment: Variables: Command: !Ref Command Handler: index.handler Role: !GetAtt LambdaExecutionRole.Arn Runtime: "nodejs10.x" MemorySize: 128 Timeout: 60 # Lambda の実行ロール # 標準の AWSLambdaBasicExecutionRole サービスロールポリシーに加えて、 # SSMコマンド実行用の ssm:SendCommand # EC2インスタンス一覧取得用の ec2:describeInstances # をインラインポリシーに追加 LambdaExecutionRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - "sts:AssumeRole" Path: / ManagedPolicyArns: - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" Policies: - PolicyName: run-command PolicyDocument: Statement: - Effect: Allow Action: - ssm:SendCommand - ec2:describeInstances Resource: "*"