Day 28 – 使用 CDK 創建 CloudWatch Alarm 的含圖告警同時發送到 LINE 與 Discord

2020 12th 鐵人賽

昨天教大家怎麼簡單的在 LINE Notify 上面看到 CloudWatch 的 Alarm,不過這樣只有通知沒有目前的狀態圖對於平常判斷系統是否有問題好像少了什麼,所以今天來教大家怎麼讓 LINE Notify 上面除了可以看到 CloudWatch 的 Alarm 之外還可以看到 CloudWatch Graph

https://i1.wp.com/ithelp.ithome.com.tw/upload/images/20201012/20117701DXnf49N8qV.jpg?w=640&ssl=1

而今天的內容除了可以把訊息發到 LINE Notify 之外還加碼支援把訊息發送到 Discord

https://i1.wp.com/ithelp.ithome.com.tw/upload/images/20201012/20117701R5IkkogXCH.png?w=640&ssl=1

今日目標

  1. 更新 Lambda 程式
  2. Lambda 加入 Policy
  3. Lambda 加入 environment
  4. 加入壓測工具

創建一個 CloudWatch Alarm 的含圖告警

更新 Lambda 程式

先更新新版的 Lambda 程式,程式位置 GitHub

主要功能我都已經先寫好並且放到 GitHub 上面了,所以大家只要 pull 就可以取得新版程式了呦!

寫得很爛請大家見諒

$ cd lambda
$ git pull

Lambda 加入 Policy

我們需要讓 Lambda 可以吃到 CloudWatch 的資料,所以要修改一下它的權限,其實這邊權限還可以再給得更小不過今天就先開個 cloudwatch:Get* 吧 XD

myFunction.addToRolePolicy(
  new iam.PolicyStatement({
    actions: ["cloudwatch:Get*"],
    resources: ["*"],
  })
);

Lambda 加入 environment

還記得昨天的範例我們需要直接修改 "your-line-notify-token" 嗎?這個部分我做了一點改變,讓參數直接從 CDK 喂給 Lambda 的 environment

const myFunction = new lambda.Function(this, "Lambda", {
  handler: "index.handler",
  runtime: lambda.Runtime.NODEJS_10_X,
  code: lambda.Code.fromAsset("lambda"),
  environment: {
    LINE_NOTIFY_TOKEN: "your-line-notify-token",
    DISCORD_URL: "your-discord-webhook-url",
  },
});

取得 Discord Webhook 的方法可以參考官方文件

加入壓測工具

昨天的教學因為篇幅關係沒有介紹到如何使用人為方法給予機器負載做測試,今天來把這段補起來

我們平常使用的 AMI 為 AMAZON LINUX 2 所以以此介紹,首先我們需要先安裝 epel 不然我們沒有軟體包可以使用

$ sudo amazon-linux-extras install epel -y

如果對 epel 有問題可以參考 AWS 的文件 How do I enable the EPEL repository for my Amazon EC2 instance running CentOS, RHEL, or Amazon Linux?

安裝 stress-ng

這次我們選用 stress-ng 作為我們的測試工具,還是記得我們前面有教大家使用 User Data 嗎?我們就直接請 User Data 安裝這樣我們開啟系統的時候就可以直接使用了

$ sudo yum install stress-ng -y

製造 CPU Load

我們範例定義的 CPU Utilization threshold 為 5 基本上只要給他一點壓力 CloudWatch Alarm 就會有反應了,所以我們先給予他 CPU 40% 的壓力作為測試給予壓力的指令如:

$ stress-ng -c 1 -l 40
stress-ng: info:  [1084] defaulting to a 86400 second run per stressor
stress-ng: info:  [1084] dispatching hogs: 1 cpu
stress-ng: info:  [1084] successful run completed in 74.43s (1 min, 14.43 secs)

整理一下範例

今天的範例建立了

  • EC2
    • 放入 Default VPC 加速部署
    • 等級 t3a.nano
    • Security Group 22 全開
    • Image:AMAZON_LINUX_2
    • Subnet:PUBLIC
    • User Data:
      • amazon-linux-extras install epel -y
      • yum install stress-ng tmux htop -y
  • SNS
  • CloudWatch
  • Lambda
const vpc = ec2.Vpc.fromLookup(this, "VPC", {
  isDefault: true,
});

const securityGroup = new ec2.SecurityGroup(this, "SecurityGroup", {
  vpc,
  allowAllOutbound: true,
});
securityGroup.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(22));

const instance = new ec2.Instance(this, "Instance", {
  vpc,
  instanceType: ec2.InstanceType.of(
    ec2.InstanceClass.T3A,
    ec2.InstanceSize.NANO
  ),
  machineImage: ec2.MachineImage.latestAmazonLinux({
    generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2,
  }),
  securityGroup,
  vpcSubnets: {
    subnetType: ec2.SubnetType.PUBLIC,
  },
  keyName: "KeyPair",
});
instance.userData.addCommands(
  `amazon-linux-extras install epel -y`,
  `yum install stress-ng tmux htop -y`
);
new cdk.CfnOutput(this, "instance", {
  value: instance.instancePublicDnsName,
});

const topic = new sns.Topic(this, "Topic");

const metric = new cloudwatch.Metric({
  namespace: "AWS/EC2",
  metricName: "CPUUtilization",
  dimensions: {
    InstanceId: instance.instanceId,
  },
  period: cdk.Duration.minutes(1),
});

const alarm = new cloudwatch.Alarm(this, "Alarm", {
  metric,
  threshold: 5,
  evaluationPeriods: 1,
});

alarm.addAlarmAction(new cw_actions.SnsAction(topic));

const myFunction = new lambda.Function(this, "Lambda", {
  handler: "index.handler",
  runtime: lambda.Runtime.NODEJS_10_X,
  code: lambda.Code.fromAsset("lambda"),
  environment: {
    LINE_NOTIFY_TOKEN: "your-line-notify-token",
    DISCORD_URL: "your-discord-webhook-url",
  },
});
myFunction.addToRolePolicy(
  new iam.PolicyStatement({
    actions: ["cloudwatch:Get*"],
    resources: ["*"],
  })
);

topic.addSubscription(new subscriptions.LambdaSubscription(myFunction));

參考資料

  • https://github.com/aws-samples/aws-cloudwatch-snapshot-graphs-alert-context

今天介紹如何部署一個 CloudWatch Alarm 的含圖告警,有機會我會在修改目前的程式讓它變得更靈活使用或是支援更多的通訊程式,希望對大家有幫助 ~