Ruby × AWS Lambda × CloudWatch Eventsで定期実行プログラムを作成する

## 本記事で目指す構成

![rubyLambdaClowdWach.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/d99775fe-346d-aec0-7832-b9246015f6b9.png)
CloudWatch EventsをトリガーにLambdaを起動し、Slackへメッセージを飛ばす。
※ CloudWatch EventsにはあらかじめCron式で実行スケジュールを設定しておく。

![スクリーンショット 2020-10-10 17.25.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/3e14c710-160e-e5fb-bb9b-e6f32ff9f73c.png)

↑こんな感じで自動で定期実行してくれるようになる。

## 対象読者

- Rubyで定期実行プログラムを作成してみたい人
- Lambdaに触れてみたい人

## 仕様

言語: Ruby2.5系
インフラ: Lambda、ClowdWatch Events

※ SlackBotは各自あらかじめ作成しておいてください。

参照: [ワークスペースで利用するボットの作成](https://slack.com/intl/ja-jp/help/articles/115005265703-%E3%83%AF%E3%83%BC%E3%82%AF%E3%82%B9%E3%83%9A%E3%83%BC%E3%82%B9%E3%81%A7%E5%88%A9%E7%94%A8%E3%81%99%E3%82%8B%E3%83%9C%E3%83%83%E3%83%88%E3%81%AE%E4%BD%9C%E6%88%90)

## 完成形

[periodic-slack-bot-on-aws-lambda](https://github.com/kazama1209/periodic-slack-bot-on-aws-lambda)

※ 一から作るのが面倒な方は↑からgit cloneしてください。

## プログラムを作成

Slackへメッセージを飛ばすためのプログラムを作成していく。

### ディレクトリを作成

```
$ mkdir periodic-slack-bot-on-aws-lambda
$ cd periodic-slack-bot-on-aws-lambda
```

### Gitを設定

```
$ git init
$ touch .gitignore
```
```:./.gitignore
.bundle
/vendor/bundle
```

Gitで管理したくないものを記述。

### Rubyのバージョンを指定

```
$ rbenv local 2.5.1 
# 2.5系を推奨
```

### Gemをインストール

```
$ bundle init
```

↑のコマンドでGemfileを生成し、以下のように編集する。

```ruby:./Gemfile
# frozen_string_literal: true

source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

ruby '2.5.1'

gem 'async-websocket'
gem 'slack-ruby-bot'
```

その後、必要なGemをインストール。

```
$ bundle install --path vendor/bundle
```

※ 後ほどGemも含めてzipファイルにパッケージングする必要があるため、グローバルではなく「vendor/bundle」以下（ローカル）にインストールしなければならない。

### app.rbを作成

```
$ touch app.rb
```
```ruby:./app.rb
require 'slack-ruby-client'

Slack.configure do |conf|
  conf.token = ENV['SLACK_BOT_TOKEN']
end

def post_to_slack(event:, context:)
  client = Slack::Web::Client.new
  client.chat_postMessage(channel: ENV['SLACK_CHANNEL_NAME'], text: 'テスト送信 from AWS Lambda', as_user: true)
end
```

※ 引数に「(event:, context:)」を渡さないと動かないので注意。
参照記事: [Python の AWS Lambda 関数ハンドラー](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/python-handler.html)

## Lambdaへデプロイ

プログラムを作成したら、Lambdaへデプロイするための準備を行う。

### AWS CLIをインストール

今回は「AWS CLI」と呼ばれるツールを使いながらデプロイしていくので、まだインストールできてないない場合はインストールしておく。

```
$ brew install awscli
```

### IAMユーザーを作成

デプロイを行うためのIAMユーザーを作成していく。
![スクリーンショット 2020-09-21 22.11.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/f924488a-df8b-b871-598e-bba9d148a806.png)
まずは「IAM」→「ポリシー」→「ポリシーの作成」へと進み、JSONタブから以下の文を貼り付ける。
![スクリーンショット 2020-09-21 22.13.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/4e0fce57-3d4e-bdf8-9c8c-62bdd89f54b2.png)

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "apigateway:*",
                "cloudformation:*",
                "dynamodb:*",
                "events:*",
                "iam:*",
                "lambda:*",
                "logs:*",
                "route53:*",
                "s3:*"
             ],
            "Resource": [
                "*"
            ]
        }
    ]
}
```

参照: [Minimal Deploy IAM Policy](https://rubyonjets.com/docs/extras/minimal-deploy-iam/)

![スクリーンショット 2020-09-21 22.15.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/d78cf088-8020-ec07-84a7-d035ac7dc5e3.png)
適当にポリシー名や説明を記述し、「ポリシーの作成」をクリック。

![スクリーンショット 2020-09-21 22.17.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/c2ddcc5a-f768-4409-b6a3-7c7c3477a3ce.png)
次に「IAM」→「ユーザー」→「ユーザーの作成」へと進み、適当な名前を付けた後「プログラムによるアクセス」にチェックを入れて次へ進む。

![スクリーンショット 2020-09-21 22.17.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/66858ba0-eebb-1342-9bfa-64dba9e4097b.png)
「既存のポリシーを直接アタッチ」から先ほど作成した「MinimalDeployIAMPolicy」を選択し、次へ進む。

![スクリーンショット 2020-09-21 22.17.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/f9d0e4cf-ce0c-31d6-d371-e19c0ae36d8a.png)

（タグは任意でOK）最後に確認画面が表示されるので、問題無ければ「ユーザーの作成」をクリック。

![スクリーンショット 2020-09-21 22.18.13_censored.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/86083659-0cb8-be5b-637f-3e0cfccd266f.jpeg)
すると「アクセスキーID」と「シークレットアクセスキー」の2つが発行されるので、csvファイルをダウンロードするなりメモするなり大事に保管しておく。

### AWS CLIの設定

```
$ aws configure --profile lambda（任意のプロフィール名でOK）

AWS Access Key ID # 先ほど作成したアクセスキーID
AWS Secret Access Key # 先ほど作成したシークレットアクセスキー
Default region name # ap-northeast-1 
Default output format # json 
```

ターミナルで「aws configure」と打ち込むと対話形式で色々聞かれるので、それぞれ必要な情報を入力していく。

### 関数を作成

![スクリーンショット 2020-10-10 18.12.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/119b4d75-602b-292b-7123-eb936c987b57.png)

AWSのコンソール画面から「AWS Lambda」を開き、「関数の作成」をクリック。

- 関数名（任意の名前）
- ランタイム（Ruby2.5）

を入力して関数を作成する。（その他はデフォルトの値もしくは空欄でOK）

![スクリーンショット 2020-10-10 18.17.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/5427447f-0656-6d74-b8c4-51fdb378626b.png)

「環境変数の編集」から

- SLACK_BOT_TOKEN
- SLACK_CHANNEL_NAME

それぞれ環境変数をセット。

![スクリーンショット 2020-12-13 6.49.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/83e9e25a-ee43-a73a-c09e-e89dd3536656.png)


「ランタイム設定を編集」からハンドラ名を「app.post_to_slack（ファイル名.メソッド名）」に変更。

### デプロイ

作成したプログラムをzipファイルとしてパッケージング。

```
$ zip -r function.zip app.rb vendor
```

aws cliを使いデプロイ。

```
$ aws lambda update-function-code --function-name PeriodicSlackBotOnAwsLambda --zip-file fileb://function.zip --profile lambda

{
    "FunctionName": "PeriodicSlackBotOnAwsLambda",
    "FunctionArn": "arn:aws:lambda:ap-northeast-1:**************:function:PeriodicSlackBotOnAwsLambda",
    "Runtime": "ruby2.5",
    "Role": "arn:aws:iam::**************:role/service-role/PeriodicSlackBotOnAwsLambda-role-**************",
    "Handler": "app.post_to_slack",
    "CodeSize": 12941753,
    "Description": "",
    "Timeout": 3,
    "MemorySize": 128,
    "LastModified": "2020-10-09T21:08:11.888+0000",
    "CodeSha256": "**************/**************=",
    "Version": "$LATEST",
    "Environment": {
        "Variables": {
            "SLACK_CHANNEL_NAME": "#**************",
            "SLACK_BOT_TOKEN": "xoxb-**************-**************-**************"
        }
    },
    "TracingConfig": {
        "Mode": "PassThrough"
    },
    "RevisionId": "********-****-****-****-********",
    "State": "Active",
    "LastUpdateStatus": "Successful"
}
```

※ --function-nameには先ほど作成した関数名をセット。

### テスト実行

![スクリーンショット 2020-10-10 18.34.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/0fbe03ef-6d19-557a-fb7d-568348764b75.png)
「テストイベントの設定」から適当にテストを作成。（ハッシュは空欄でOK）

![スクリーンショット 2020-10-10 18.38.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/2ad298e0-1897-c1f2-c8f8-7ce9dc7b6d79.png)
「テスト」をクリックし、指定したSlackチャンネルへメッセージが飛んでいれば成功。
![スクリーンショット 2020-10-10 18.40.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/f8dd9b28-f579-cc77-4893-765d869c7e67.png)

## CloudWatch Eventsでスケジュール管理

### トリガーの設定

![スクリーンショット 2020-10-10 18.43.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/3da84a4c-deeb-7915-54ea-e61c0613fce4.png)

「トリガーを追加」をクリック。

![スクリーンショット 2020-10-10 18.47.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/d0091960-5418-2532-52ae-dd5e1a39922e.png)

- トリガー名: CloudWatch Events
- ルール: 新規ルールの作成
- ルール名: test_30minutes（任意）
- ルールの説明: 30分に1回実行（任意）
- ルールタイプ: スケジュール式
- スケジュール式: cron(*/30 * * * ? *)

※ 実行間隔はそれぞれお好みで設定。

cron式の書き方の説明については今回省略。
参照: [クーロン(cron)をさわってみるお](https://qiita.com/katsukii/items/d5f90a6e4592d1414f99)

![スクリーンショット 2020-10-10 17.25.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/3e14c710-160e-e5fb-bb9b-e6f32ff9f73c.png)
時間の経過を待ち、しっかりと定期実行されていれば成功。

![スクリーンショット 2020-10-10 18.54.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/0692a046-44fe-4921-3259-0df7c6090456.png)

もし上手く行かなかった場合はCloudWatchのロググループ内にログが出力されているはずなので適宜デバッグ。

## あとがき

お疲れ様でした！もし記事通りに進めて上手く動作しない箇所があったらコメント蘭などで指摘していただけると幸いです。