Lambdaでお手軽サーバーレス入門（CloudWatch Events編）

前回：[Lambdaでお手軽サーバーレス入門（API Gatewayデプロイ編）](https://qiita.com/kakken1988/items/c864e8eecccdf877c612)

今回は別のトリガー、CloudWatch Eventsについて試してみます。

# 今回やりたいこと

* 毎時5分（7時〜19時）に起動する  
  7:05, 8:05, 9:05, ..., 19:05
* 天気予報を外部サービスから取得
* 何かしらの方法で知りたい

# イベントソースは2種類

## Event Pattern

![cw01.png](https://qiita-image-store.s3.amazonaws.com/0/143240/899351ab-5ce2-cd2a-c551-00a315503879.png)

例では、EC2のすべてのインスタンスが起動状態（running）になったとき、を指定しています。
このように、数あるAWSのサービスのイベントをトリガーにできます。

## Schedule

![cw02.png](https://qiita-image-store.s3.amazonaws.com/0/143240/907c35c0-5af8-d7b0-12a6-b54cd59a8798.png)

○分毎、○時間毎、のような指定方法や、Cronパターンを指定しての細かい設定も可能な、定期的なトリガーです。

今回は、Scheduleを使ってみます。

# 天気予報を知りたい

いろいろAPIはありますが、今回は私が今住んでいるホーチミンシティの天気を取りたかったので、 [OpenWeatherMap](https://openweathermap.org/)のAPIを使ってみます。

## API登録

今回はフリーアカウントで十分なので、[こちら](https://openweathermap.org/appid)のページから、登録していきました。
![cw03.png](https://qiita-image-store.s3.amazonaws.com/0/143240/e49860a2-69d8-3c1f-376f-b4c221e43b77.png)

APIは、JSONでこのように返ってきます。

<img width="915" alt="cw04.png" src="https://qiita-image-store.s3.amazonaws.com/0/143240/1a57c6de-2cfe-f4b8-f685-1faa17f72bc5.png">

```json
{
  "coord": {
    "lon": 106.7,
    "lat": 10.78
  },
  "weather": [
    {
      "id": 802,
      "main": "Clouds",
      "description": "scattered clouds",
      "icon": "03n"
    }
  ],
  "base": "stations",
  "main": {
    "temp": 28,
    "pressure": 1008,
    "humidity": 83,
    "temp_min": 28,
    "temp_max": 28
  },
  "visibility": 10000,
  "wind": {
    "speed": 2.1,
    "deg": 250
  },
  "clouds": {
    "all": 40
  },
  "dt": 1538314200,
  "sys": {
    "type": 1,
    "id": 7985,
    "message": 0.0058,
    "country": "VN",
    "sunrise": 1538260921,
    "sunset": 1538304244
  },
  "id": 1566083,
  "name": "Ho Chi Minh City",
  "cod": 200
}
```

# ソースコード

では、Pythonで呼び出してみます。

```python:lambda_function.py
import os
import json
from urllib.parse import urlencode
from urllib.request import urlopen
from datetime import datetime

API_ID = os.getenv('OWM_API_ID')
DATETIME_FORMAT = '%Y-%m-%d %H:%M:%S'


def lambda_handler(event: dict, context):
    if not API_ID:
        print('ERROR: No API ID')
        return

    # get whether information from OpenWeatherMap
    url = 'https://api.openweathermap.org/data/2.5/weather?{}'.format(urlencode({
        'q': 'Ho Chi Minh City,VN',
        'units': 'metric',
        'appid': API_ID
    }))
    with urlopen(url) as f:
        raw_data = json.loads(f.read().decode('utf-8'))

    print('Location: {}, {}'.format(raw_data['name'], raw_data['sys']['country']))
    print('Time: {}'.format(datetime.fromtimestamp(raw_data['dt']).strftime(DATETIME_FORMAT)))
    print('Weather: {}, {}'.format(raw_data['weather'][0]['main'], raw_data['weather'][0]['description']))
    print('Temperature: {}℃'.format(raw_data['main']['temp']))
    print('Pressure: {:,}hPa'.format(raw_data['main']['pressure']))
    print('Humidity: {}%'.format(raw_data['main']['humidity']))
```

このような出力結果を期待します。

```
Location: Ho Chi Minh City, VN
Time: 2018-09-30 20:30:00
Weather: Clouds, scattered clouds
Temperature: 28℃
Pressure: 1,008hPa
Humidity: 83%
```

なお、Lambda関数を作成時、環境変数に以下を設定してください。

* OWM_API_ID  
  OpenWeatherMapで取得したAPI ID

実行時間は、余裕を見て1分としました。

![cw05.png](https://qiita-image-store.s3.amazonaws.com/0/143240/d05b97c6-cf2d-34c8-9aaa-bc857ad0c499.png)

# CloudWatchのイベントを設定する

[CloudWatch management console](https://ap-northeast-1.console.aws.amazon.com/cloudwatch/home?region=ap-northeast-1#rules:action=create)
左ツリーのEvents→Rulesを押して、画面のCreate Ruleをクリックします。

## ソースの設定

Scheduleを選んだあと、Cron expressionを選ぶと、Cron式を入力できます。
![cw06.png](https://qiita-image-store.s3.amazonaws.com/0/143240/6ea39fd7-5ce2-30ce-fe60-21cf2b8c0dc5.png)

スペース区切りで、6つの指定が必要です。
`分 時 日 月 曜日 年`

* 分  
  * 値：0-59
  * ワイルドカード
      * `,`カンマ（複数指定）
      * `-`ハイフン（範囲指定）
      * `*`アスタリスク（全指定）
      * `/`スラッシュ（増分指定）
* 時  
  * 値：0-23
  * ワイルドカード
      * `,`カンマ（複数指定）
      * `-`ハイフン（範囲指定）
      * `*`アスタリスク（全指定）
      * `/`スラッシュ（増分指定）
* 日  
  * 値：1-31
  * ワイルドカード
      * `,`カンマ（複数指定）
      * `-`ハイフン（範囲指定）
      * `?`クエスチョン（任意値指定）
      * `*`アスタリスク（全指定）
      * `/`スラッシュ（増分指定）
      * `L`（最終日指定）
      * `W`（平日指定）
* 月  
  * 値：1-12 または JUN-DEC
  * ワイルドカード
      * `,`カンマ（複数指定）
      * `-`ハイフン（範囲指定）
      * `*`アスタリスク（全指定）
      * `/`スラッシュ（増分指定）
* 曜日  
  * 値：1-7 または SUN-SAT
  * ワイルドカード
      * `,`カンマ（複数指定）
      * `-`ハイフン（範囲指定）
      * `?`クエスチョン（任意値指定）
      * `L`（最終日指定）
      * `#`シャープ（何週目指定）
* 年  
  * 値：1970-2199
  * ワイルドカード
      * `,`カンマ（複数指定）
      * `-`ハイフン（範囲指定）
      * `*`アスタリスク（全指定）
      * `/`スラッシュ（増分指定）

**UTCで指定しますので注意してください**
ここでは、インドシナ時間(ICT, UTC+7)で7:05〜19:05まで、毎日実行したいので、 `5 0-12 * * ? *`と指定しました。
日本時間(JST, UTC+9)だと、`5 2-14 * * ? *`となりますね。

テキストボックスの下には、直近のトリガー10個が表示されるので確認にも使えます。

## ターゲットの設定

右側はターゲットの設定で、何を実行するかを指定します。
ここでは、先程作ったLambdaを指定します。
※Lambda以外にもいろいろ実行できます。

![cw07.png](https://qiita-image-store.s3.amazonaws.com/0/143240/ffb0af41-ee10-c657-ac92-3cd6aaf50719.png)

右下のConfigure detailsをクリックします。

![cw08.png](https://qiita-image-store.s3.amazonaws.com/0/143240/aa07ef8a-1a39-9eae-48e7-3ed006ef9af3.png)

名前と説明、Stateを選択します。
StateのEnabledにチェックを入れておくと、すぐに有効になります。
しばらく実行したくない場合は、このチェックを外します。
あとから有効・無効は切り替えられます。

すべて終わったら、Create ruleをクリックします。

![cw09.png](https://qiita-image-store.s3.amazonaws.com/0/143240/531c7d0f-1358-8950-ffe4-9383ef77c847.png)

作成されると、一覧に表示されます。

ここで有効・無効を切り替えたい場合は、緑アイコン（無効時はグレーアイコン）左にあるラジオボタンをチェックして、Actions→Disable(Enable)を選びます。

![cw10.png](https://qiita-image-store.s3.amazonaws.com/0/143240/1fe4af3c-4966-a79c-5764-3a16f92854bb.png)

# ログを見てみる

さて翌日、きちんと動いたかを見てみます。

[CloudWatch Logs](https://ap-northeast-1.console.aws.amazon.com/cloudwatch/home?region=ap-northeast-1#logs:)

![cw11.png](https://qiita-image-store.s3.amazonaws.com/0/143240/29650086-409c-8567-0bfe-da43c5e3d658.png)

きちんとLog Groupができています。
Log Groupは、`/aws/lambda/関数名` となっています。
ここをクリックすると、

![cw12.png](https://qiita-image-store.s3.amazonaws.com/0/143240/6add66b2-fa36-d1f8-6b1c-03882c26a956.png)

Log Streamを確認できます。
Log Streamが新しくなる理由は、関数を更新したり、一定時間以上空いた場合になります（と思います、自信ないです）。

ストリーム名をクリックすると、
![cw13.png](https://qiita-image-store.s3.amazonaws.com/0/143240/d9a93c74-7848-362a-9a32-f74b185b2ad6.png)
無事に、希望通りの出力があります！

# 次回予告

今回、ログに出力しただけなので、わざわざ見に行かないといけないです。
面倒くさいので、Twitterに通知を飛ばしてみます。
素でTwitter APIを叩いてもいいのですが、ここは世の中便利なライブラリがたくさんあるので、Lambdaでどのようにライブラリを使うのか、をメインに、Twitterでこの情報を通知できるようにします。