はじめに
先日開催された技術書典7の 「GoとAWS CDKで作る本格SlackBot入門」 という本の内容を受けて自分でもやってみました!本の内容に従いつつ、自分で少しアレンジしています。今回はツイートしたようにEC2のディスク使用量をSlack通知するBotを作ってみました。
AWS CDKの勉強かねて、友達のこの本参考に実践してみた。せっかくだからqiitaにアウトプットする https://t.co/Akw5CQkmHe
— ogady@アニオタエンジニア (@gadyma) September 30, 2019
現在は、下記サイトから、電子版を購入することできます、ぜひ今回の記事を読んで興味を持っていただけたら、購入してみてください。
AWS CDK、Goなどの技術を用いて、ChatOps(Slackなどのチャットを用いて快適に開発・運用していこー的なやつ)を導入することの素敵さが存分に味わえる本です。
- 購入先(BOOTH):GoとAWS CDKで作る本格SlackBot入門
前提
- SlackAPIの用意ができていること
- AWSアカウントとAWS CLIの設定ができていること
- nodeの開発環境ができていること
- Goの開発環境ができていること
技術スタック
- Go 1.13
- node 12.10.0
- npm 6.11.3
- typescript 3.6.3
- AWS CLI 1.16.234
- AWS(Lambda、SNS、CloudWatch)
- AWS CDK(TypeScript) 1.9.0
AWS CDKとは
AWS CDK(Cloud Development Kit)は、CloudForamationテンプレートを、TypeScriptやJavaScript、Java、Python、.NETで生成することができます。
AWS CDKのメリット
- 生のCloudForamationで書くよりも記述量を減らすことができる
- コンポーネント分割が用意なので、保守性の高いインフラ設計が可能
- アプリケーション開発で使っている便利ツールをインフラのコードでも使用できる
AWS CDKの導入
npm install -g aws-cdk
// プロジェクトのディレクトリに移動して
cdk init app --language=typescript
これを実行すると、以下のようにディレクトリが作成されます。
.
├── README.md
├── bin
│ └── alert_ec2_disk_used.ts
├── cdk.json
├── jest.config.js
├── lib
│ └── alert_ec2_disk_used-stack.ts
├── node_modules
├── package-lock.json
├── package.json
├── test
│ └── alert_ec2_disk_used.test.ts
└── tsconfig.json
また、CDKのソースがCloudFormationテンプレートとなるため、S3バケットが必要ですが、これは事前にcdkの用意されたコマンドを実行することで CDK Toolkit StackとしてAWS環境にデプロイすることができます。
cdk bootstrap
CDKを使うにあたって使用するAWSサービスに応じたライブラリをインストールします。
npm install @types/node@8 @aws-cdk/aws-lambda @aws-cdk/aws-sns @aws-cdk/aws-sns-subscriptions
1. CDKを書いてみる
lib/alert_ec2_disk_used-stack.tsを編集する
ここでは
- Stack・・・デプロイの単位、リージョン、AWSアカウント
- Constracts・・・デプロイするリソースの定義
を定義します。
Lambda周りの定義
// Lambda Function定義
import { Function, Runtime, Code } from "@aws-cdk/aws-lambda"
import { Topic, Subscription, SubscriptionProtocol } from '@aws-cdk/aws-sns';
import { LambdaSubscription } from '@aws-cdk/aws-sns-subscriptions';
/*
中略
*/
const lambdaFunction: Function = new Function(this,
"alert_ec2_disk_used_func", {
functionName: "alert_ec2_disk_used_func",
runtime: Runtime.GO_1_X,
code: Code.asset("./lambdaSrc"),
handler: "handler",
memorySize: 128,
timeout: cdk.Duration.seconds(10),
environment: {
"BOT_USER_TOKEN": "{SlackのBot User OAuth Access Token}",
"CANNEL_ID": "{送信対象のSlackチャンネルID}",
"MESSAGE": "ディスク使用量が75%を超えています。",
},
})
// Lambdaサブスクリプションの定義
const lambdaSub: LambdaSubscription = new LambdaSubscription(lambdaFunction)
lambdaSub.bind(snsTopic_Over75)
SNS周りの定義
// SNSトピック定義
const snsTopic_Over75: Topic = new Topic(this,
"ec2_disk_used_over_75", {
displayName: "alert_ec2_disk_used_over_75",
topicName: "alert_ec2_disk_used_over_75",
})
// SNSサブスクリプション定義
const snsSub_Over75: Subscription = new Subscription(this,
"ec2_disk_used_over_75_sub", {
endpoint: lambdaFunction.functionArn,
topic: snsTopic_Over75,
protocol: SubscriptionProtocol.LAMBDA,
})
bin/alert_ec2_disk_used.tsを編集する
ここでは、デプロイの定義(Lambdaソースのコンパイル、実行コマンドなど)を記載します。ここはほとんど本の内容を参考にしています。
#!/usr/bin/env node
import 'source-map-support/register';
import cdk = require('@aws-cdk/core');
import { AlertEc2DiskUsedStack } from '../lib/alert_ec2_disk_used-stack';
const util = require('util');
const exec = util.promisify(require('child_process').exec);
async function deploy() {
//Go のソースを build する
await exec('go get -v -t -d ./lambdaSrc/... && ' +
'GOOS=linux GOARCH=amd64 ' +
'go build -o ./lambdaSrc/handler ./lambdaSrc/**.go');
const app = new cdk.App();
new AlertEc2DiskUsedStack(app, 'AlertEc2DiskUsedStack');
app.synth();
//build 結果のバイナリを消去する
await exec('rm ./lambdaSrc/handler');
}
deploy()
2. LambdaのGoソース
今回は特定のSlackチャンネルに「Incomming Webhooks」を利用して通知を投げます。今回はCDKの勉強がしたかったので、ここは割と雑。。。
package main
import (
"context"
"encoding/json"
"os"
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
"github.com/labstack/gommon/log"
"github.com/pkg/errors"
)
type SNSMessages struct {
AlarmName string `json:"AlarmName"`
AlarmDescription string `json:"AlarmDescription"`
AWSAccountId string `json:"AWSAccountId"`
NewStateValue string `json:"NewStateValue"`
NewStateReason string `json:"NewStateReason"`
StateChangeTime string `json:"StateChangeTime"`
Region string `json:"Region"`
OldStateValue string `json:"OldStateValue"`
}
const (
EC2_DISK_USED_OVER_75_ALERT = "alert_ec2_disk_used_over_75"
)
const (
GOOD = "good"
WARNING = "warning"
DANGER = "danger"
)
func main() {
lambda.Start(noticeHandler)
}
func noticeHandler(ctx context.Context, snsEvent events.SNSEvent) (e error) {
cannelID := os.Getenv("CANNEL_ID")
footer := os.Getenv("FOOTER")
var snsMessages SNSMessages
for _, record := range snsEvent.Records {
snsEvent := record.SNS
snsMessage := snsEvent.Message
err := json.Unmarshal([]byte(snsMessage), &snsMessages)
if err != nil {
log.Error(err)
return err
}
switch snsMessages.AlarmName {
case EC2_DISK_USED_OVER_75_ALERT:
color := WARNING
if err := PostToSlack(os.Getenv("MESSAGE"), color, cannelID, footer); err != nil {
log.Error(err)
return err
}
return nil
default:
return errors.New("想定するTopicではない")
}
}
return nil
}
package main
import (
"encoding/json"
"fmt"
"os"
"strconv"
"time"
"github.com/nlopes/slack"
)
func PostToSlack(message string, color string, channel string, footer string) error {
api := slack.New(os.Getenv("BOT_USER_TOKEN"))
attachment := slack.Attachment{
Color: color,
Text: message,
Footer: footer,
Ts: json.Number(strconv.FormatInt(time.Now().Unix(), 10)),
}
_, _, err := api.PostMessage(channel, slack.MsgOptionAttachments(attachment))
if err != nil {
fmt.Println(err)
}
return nil
}
3. ビルドとデプロイ
//.tsファイルを実行可能な.js ファイルにコンパイルします。
npm run build
cdk deploy
〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜
7/7 | 0:04:09 | CREATE_COMPLETE | AWS::CloudFormation::Stack | AlertEc2DiskUsedStack
✅ AlertEc2DiskUsedStack
Stack ARN:
arn:aws:cloudformation:ap-northeast-1:434353216964:stack/AlertEc2DiskUsedStack/8a3a46f0-e45c-11e9-b8b3-06680f8688ee
cdk destroy {stack名}
4. 確認してみる
ちゃんとCloudFormation上でデプロイが成功していることが確認できます。
やってみて
AWS CDKはすごいですね。CloudFormationでシコシコ書いていたのが、こんな簡単にIaCを実現できるとは。。。
ただ、CloudFormationとCDKを書く言語の両方の知識が必要なため、知識ゼロで始めるとなると学習コストが低いわけではないかな、と思います。
とはいえ、アプリ開発と同じ感覚でIDE等の恩恵を受けることができるので、快適に作業することができる点などが本当に魅力的です。マジで。
今回参考にした**「GoとAWS CDKで作る本格SlackBot入門」では、AWSアカウント登録から、EC2の起動停止を行うSlackBot構築までを一つ一つ進めていくハンズオン形式になっています。
ここら辺の技術をあまり触ったことがない人でもAWS CDK、Go、ChatOpsの素晴らしさを体験することができるので、ここら辺の技術を使ってみたい方にはかなりオススメ**です!実案件にもすぐに導入することができる!
これからやってみたいこと
今回は、すでに構築してある EC2 と CloudWatchAlarm に対し、SNSTopic と Lambda をCDKで構築したので、次はカスタムメトリクス含めてイチからやってみたいです。(やり方はこれから調べる。。。)
あとは、これで一通りのサーバレスアーキテクチャを構築できるようになるところまでやろうと思います。