はじめに
本記事の対象となる読者
特定のトリガーでLambdaを起動して、EC2内のシェルスクリプトを実行したい
システム概要
上記のやりたかったことを実現するために使用したサービスは以下です。
- AWS Lambda(以後Lambda)
- AWS System Manager(以後SSM)
- AWS Elastic Computer Cloud(以後EC2)
書かないこと
- EC2インスタンスの構築方法
EC2
Amazon Linux2を使用して、無料利用枠でインスタンスを用意します。
EC2インスタンスを作成
EC2インスタンスを作成する前に以下のポリシーをアタッチしたIAMロールを作成しておきます。仮に名前はAWSServiceSSMRole
としておきます。
- AmazonEC2RoleforSSM
- AmazonSSMFullAccess
上記で作成したロールをインスタンスに割り当てインスタンスを作成します。
ssm-agentのインストールは必要ありません。
インスタンス内にbashスクリプトを作成
ssh接続をして以下を実行していきます。
# 自身で設定した情報を使ってSSH接続
$ ssh ec2-user@host -i ~~.pem
# 現在地の確認
$ pwd
# 結果:/home/ec2-user
# test.txtを作成
$ touch test.txt
# 実行するshファイルを作成
$ vi test.sh
vimでtest.sh
を開いたら以下を入力します。
#!/bin/bash
echo hoge >> test.txt
入力したら、保存します。
$ sh test.sh
と実行するとtest.txt
にhoge
という文字列が書き込まれているかと思います。
Lambda
今回はAPI Gatewayを使用して、そのエンドポイントにPOSTがあったらLambdaを実行するように作成します。
serverless frameworkを使用して、lambdaを作成します。
$ sls create -t aws-nodejs -p myfunc
$ cd myfunc
上記を実行すると、 handler.js
と serverless.yml
が作成されます。
serverless.yml
API Gatewayを使用するので、eventsを設定します。
service: myfunc
provider:
name: aws
runtime: nodejs12.x
functions:
hello:
handler: handler.hello
events:
- http:
path: hoge
method: post
integration: lambda
handler.js
AWS-SDK SSMクラスで用意されているsendCommand
メソッドを使用します。
詳しくはこちらを参照してください。
sendCommandはSSMにあらかじめ登録されているドキュメントのコマンドを実行します。
今回はデフォルトで用意されているAWS-RunShellScript
ドキュメントを使用します。
AWS-RunShellScript
はただEC2のシェルスクリプトを実行するだけですが、もちろんSSMに自分でドキュメントを作成してそれを実行することも可能です。
lambdaではaws-sdkは標準でインストールされていますので、npm installなどでインストールする必要はありません。
'use strict'
const AWS = require('aws-sdk')
const SSM = new AWS.SSM({region: 'ap-northeast-1'})
const REMOTE_WORKING_DIR = '/home/ec2-user'
module.exports.main = async event => {
try {
// 実行したいコマンド
let command = 'sh exec.sh'
let params = {
DocumentName: 'AWS-RunShellScript',
InstanceIds: ['作成したEC2のインスタンスID'],
Parameters: {
commands: [command], // 配列で指定するので複数実行も出来る
workingDirectory: [REMOTE_WORKING_DIR] // どの階層で実行するかを指定
},
// SSMの実行結果をCloudWatchにロギング
CloudWatchOutputConfig: {
CloudWatchLogGroupName: 'SSMLogs',
CloudWatchOutputEnabled: true
},
// タイムアウト設定
TimeoutSeconds: 3600 // 1 hour
}
SSM.sendCommand(params, function(err, data){
if(err){
console.log(err, err.stack)
} else {
console.log(data)
}
})
} catch(e){
console.log(e);
}
}
上記を保存後エンドポイントにPOSTして実行します。
$ curl -X POST "serverlessで作成されたエンドポイント" -d "{}"
以上となります。