Cloud9でLambdaを作る（ユーザーガイドをなぞる編）

# はじめに
久しぶりにCloud9でLambdaを作ろうとしたら、色々変わっていたので、やり方をわかった範囲で記事にしました。

# 環境
- Cloud9
    - t3.smallくらいのスペック
    - デフォルトVPC上
    - 50GBくらいのボリューム
        - そのままだと10GBなので、作成後EC2コンソールから変更してください
            - 参考：https://cmqblog.com/aws-cloud9-ebsvolume-expand/
                - ルートボリュームを増やすだけで大丈夫のはずです


必要なものはCloud9にそろっていましたので、別途インストール作業等は不要でした。
※最後に、「Python3.9をインストールしておいた方が良いかも」という話を追記しました。

# 今までの開発方法
以前はCloud9画面の右側からLambda開発ができました。現時点でも設定変更すれば利用可能です。変更方法は以下のページにありました。

https://blog.denet.co.jp/cloud9_and_lambda/

ところが使おうとすると、警告が出てきます。今後はAWS Toolkitで作るようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/cfffe33a-e0d7-7b1d-f151-efde20ce3754.png)

# AWS Tookitでの開発方法

Cloud9上のAWS Toolkitでの開発方法は、以下の公式のページにありました。

https://docs.aws.amazon.com/ja_jp/cloud9/latest/user-guide/serverless-apps-toolkit.html

ですが、記載内容と現時点のCloud9とで、結構違いがありましたので、それらを説明します。

## 注意点
Cloud9をしばらく使っていると、ExplorerからAWSのリソースが見れなくなったり、AWS SAMのデプロイができなくなったりします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/a719b358-7d2b-6e4c-c282-4c0375aa5791.png)

```
2022-02-02 23:32:11 [ERROR]: [Error: Error with child process: Error: Unable to upload artifact function/ referenced by CodeUri parameter of TargetFunction resource
```

詳細を調べきれず原因はよくわかりませんが、以下の点に関係がありそうです。

- Cloud9の権限は、基本的には接続したユーザと同等のもの
- ただし、一部IAMのアクションに制限がある

参考のページ　

https://dev.classmethod.jp/articles/aws-cloud9-introduces-features-browse-cloudwatch-logs-s3-ec2-instance-profiles/#toc-6

上記ページにあるようにIAMロールをアタッチする方法もあるようですが、私は以下の方法でやりました。

1. AWS: Explorerの左側のアイコンをクリックし、[choose AWS Profile...]をクリック。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/7342cf9f-fade-f2a3-6fe8-2ea3d9855ad8.png)

2. [profile:default]を選択

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/8e6749ae-602e-7c80-d155-118a44ae0cf1.png)

エラーが出る都度、これを行いました。デプロイや他リソースを見る機会もそう多くない場合は、こちらの方法が手軽でよいと思います。
先のページではネットワーク関連でも制限があるとのことなので、デフォルトVPC限定かもしれません。

### もっと簡単なやり方(2022.02.09追記)
使っていたら、もっと良いやり方ありました。
左下のところに、使っているプロファイルが表示されている場所があり、そこをクリックするだけで、プロファイル選択のウィザードが出てきました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/d66927cc-ff45-3572-0f49-5d8ec691c0a8.png)

こちらのほうが手数も少なくてよいと思います。

# 解説
先程のユーザーガイドに沿って説明します。

## 作成


> 1. ［AWSExplorer］でLambdaノードを右クリックし、Lambda SAMアプリケーションの作成を選択します。

実行すると、左ペイン上でウィザードが出ますので、入力や選択、決定を行っていきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/37710bc1-caab-a2ec-19d2-dfc4d7e86b68.png)

> 2.SAM アプリケーションのランタイムを選択します。この例では、［nodejs12.x］を選択します。

私は、python3.9を選択しました。
次のところではプロセッサーを選ぶ画面が出ましたので、x86_64を選びました。


> 3.サーバーレスアプリケーションには、以下のいずれかのテンプレートを選択します。

ユーザーガイドより、テンプレートの選択肢が多かったです。「AWS SAM Hello World」を選びました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/25f87f51-4284-7f9a-fa68-fc9f50cb6b54.png)

> 4.新しいプロジェクトの場所を選択します。

フォルダですが、作ったばかりのCloud9なので、sampleCloud9(Cloud9作成時の名前)を選びました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/2c25f260-c48c-8c1e-e547-4e028b580e00.png)

> 5.新しいアプリケーションの名前を入力します。

デフォルトで入力されている文字列をそのまま使いました。テキストボックスを選択して　Enterキーを押して決定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/194ea088-a7d7-5b01-b896-cbfe90fd5008.png)

作成されたファイルは、フォルダマークをクリックして確認できます。この時点ではLambdaは作られていません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/4641626b-26e8-dff8-9219-50b8a7299e1c.png)

## SAMテンプレートを使用して実行

> 1.サーバーレスアプリケーションを構成するアプリケーションファイルを表示するには、［環境 ］ウィンドウに移動します。
> 2.アプリケーションフォルダ (例:my-sample-app) でtemplate.yaml ファイルを開きます。
> 3.template.yaml のエディターの上で、ドロップダウンメニューから　起動設定の編集を選択します。

上記説明が、だいぶ異なっておりました。実行やデバッグを行うための設定ファイルである`launch.json`は、`Run`の横のプルダウンの一番下"Edit Launch Configuration"を選択すると開きます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/99a52025-142d-aa6b-cce6-bdb331ddb0d7.png)

もしくは、`hello_world/app.py`の、`def lambda_handler(event, context):`の上のところに、`Add`と`Edit`のリンクが出ています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/8b5fa671-4b08-16fb-6d94-552a825ed494.png)

今回は新しく`Add`で入れてみます。
ユーザーガイドでは次で色々設定しますが、ここでは`Add`を押した後のウィザードで作成できます。

> 4.次の設定プロパティの値を編集または確認します。

同等の設定は`Add`を押した際の、`lambda-python3.9/template.yaml:HelloWorldFunction`を選択すると、`launch.json`に作成してくれます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/1aea5a08-af7c-e8db-0c68-912191868436.png)

`launch.json`に書き込まれると、そのデバッグ設定のnameが先のプルダウンの中に表示されます。設定値の内容がユーザーガイドと同じになっていることを確認しておくとよいです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/5e3891d5-363f-ed27-29b0-0ed0d126f4c3.png)

注意ですがこの`launch.json`、保存時にnameの昇順で自動ソートします。先の設定のnameに、01_と入れて保存するとファイルの先頭にきます。プルダウンも先頭になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/8d660240-7788-47cc-21b0-e39091fa00f6.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/4d94c9af-4b85-b410-3818-12d96387deab.png)

> 5.デバッグ設定に問題がなければ、launch.json を保存します。次に、デバッグをスタートするため、RUN の横の緑色の［再生］ボタンを選択します。

プルダウンにて追加した設定を選んで、[再生]ボタンを押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/bbaae6e6-2369-f0ec-4020-ded75925ffb0.png)

エラーになった場合は、Cloud9に十分なディスク領域が無いためかもしれません。EC2のディスク容量を増やして再起動してみてください。

## コードから直接実行
> 1.サーバーレスアプリケーションファイルを表示するには、エディタの横にあるフォルダアイコンを選択して、アプリケーションフォルダに移動します。
> 2.アプリケーションフォルダ (my-sample-appなど) から、関数フォルダー (この場合、hello-world) を拡張し、app.js ファイルを開きます。
> 3.適格な Lambda ハンドラー関数を識別するインラインアクションで、Add Debug Configuration を選択します。
> 4.SAM アプリケーションを実行するランタイムを選択します。

このあたりも多少異なります。先の`hello_world/app.py`の、`def lambda_handler(event, context):`の上の`Add`からウィザードで、[No Template]-[python3.9]と選択します。
どれが追加されたのか分かりにくいので、すぐにnameを変えてしまうのをお勧めします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/cb0cc17a-7e76-060d-e8bb-09a40f6a9226.png)

> 5.launch.json ファイルのエディターでは、次の設定プロパティの値を編集または確認します。
> 6.デバッグ設定が満足なものであれば、［RUN］の横の緑の再生矢印を選択して、デバッグをスタートします。

自動で設定された内容で問題ないことを確認し、実行します。

### エディタ設定
この後少しエディタでコードを修正するので、使いやすいように設定変更します。

- タブ
    - スペースの数はエディタ右下の、以下の箇所で変えられます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/42685a63-7523-8263-95df-088c714efc8a.png)

- 折り返し、フォントサイズ
    - 右下の歯車アイコンで設定できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/db87920c-5d14-2809-de96-e503ca515c5c.png)


### コードデバッグ時にeventを指定する方法

Lambda関数のハンドラに渡されるeventも設定することができます。

https://docs.aws.amazon.com/ja_jp/toolkit-for-vscode/latest/userguide/serverless-apps-run-debug-config-ref.html

上記ユーザーガイドにあるように`lambda.payload`を以下のように変更します。

```
                "payload": {
                    "json": {
                        "body": "{\"message\": \"hello world by code debug\"}",
                        "resource": "/hello"
                    }
                },
```

Lambda関数`hello_world/app.py`でも、eventの中身を見れるよう、printを入れておきます。

```
    #     raise e
    print(event)
    return {
        "statusCode": 200,
```

実行結果に、先の`lambda.payload`で指定した内容が表示されれば成功です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/5b6fb60f-a03f-becc-4e78-f1d93954ecec.png)

`events/event.json`というファイルがありますので、今度はそれを使うように`lambda.payload`を以下のように修正します。

```
                "payload": {
                    "path": "lambda-python3.9/events/event.json"
                },
```

実行結果に中身が表示されれば成功です。

## ローカル Amazon API Gateway リソースの実行

> 1.以下のいずれかのアプローチを選択し、AWS SAMAPI Gateway リソース用起動 Config を作成:
> ・オプション 1: AWS SAM プロジェクトにあるハンドラーのソースコード (.js、.cs、または.py ファイル) にアクセスし、Lambda ハンドラーの上で移動して、デバッグ設定の追加を選択します。次に、メニューで API イベントとマークされた項目を選択します。

今の画面だと、ウィザードで以下を選びます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/6713ccf4-2d73-a90f-10d5-fbcefb56a7bb.png)

追加したものがわかるように、接頭辞を入れました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/ffdabe7a-9c38-22dc-43bd-141bfc484432.png)

> 2.［Run (実行) ］ボタンの横のドロップダウンメニューで、起動設定を選択します 
> 3.(オプション) Lambda プロジェクトコードにブレークポイントを追加します。
> 4.緑の「再生」ボタンの横にある［Run (実行)］ボタンを選択します。
> 5.出力ペインで、結果を表示します。

ユーザーガイドの通りでOKですが、returnの値は実行結果に表示されませんので、`hello_world/app.py`を修正し、デバッグ設定で実行されているか見れるようにします。

```
    #     raise e
    print(event['requestContext']['httpMethod'])
    print(event)
    return {
```

実行して、GETが出てくれば成功です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/d0f18cdc-9098-8952-0b20-31bc50bc4cc5.png)


postに対応する場合は、まずtemplate.yamlにpostの定義をします。

```
      Events:
        HelloWorld:
          Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
          Properties:
            Path: /hello
            Method: get
         # 追加
        HelloWorldPost:
          Type: Api
          Properties:
            Path: /hello
            Method: post
```

保存して、新しいデバッグ設定追加ウィザードを使うと、POST用のが出てきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/c098d1ea-676e-4656-0fbf-0a58b1cd187c.png)

追加してnameを変更して、payloadに適当な内容を入れて実行します。

```
            "api": {
                "path": "/hello",
                "httpMethod": "post",
                "payload": {
                    "json": {
                        "message": "hogehoge"
                    }
                }
            }
```

POSTであることや、bodyにpayloadで指定した内容が確認できました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/da6d146f-cf49-35c8-88b6-522e32788935.png)

## デプロイ
> 1.［AWSExplorer］ウィンドウで、Lambda ノードのコンテキストメニュー (右クリック) を開き
> 2.デプロイに使用する template.yaml ファイルを選択します。
> 3.デプロイ先の AWS リージョンを今すぐ選択します。

template.yamlの右クリックでもできるのですが、AWS: Explorerの右クリックからファイルを指定して実行する方法もあります。
こちらからだと、リージョンを選ぶ際にデフォルトでリージョンがセットされていました。
ユーザーガイドの方法で行うと、すべてのリージョンから選ぶ必要があり、少し手間なので、私はこちらからデプロイしています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/6beca9d4-f964-72ca-8f2a-b75c3cb80262.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/95b00dee-84fa-5a18-521d-b3c223ab7ba8.png)

> 4.このデプロイで使用できる Amazon S3 バケットの名前を入力します。バケットは、デプロイ先の AWS リージョンにある必要があります。
> 5.サーバーレスアプリケーションに、パッケージタイプの Image を伴う関数が含まれているのであれば、このデプロイで使用できる Amazon ECR リポジトリの名前を入力します。リポジトリは、デプロイ先のリージョンにある必要があります。
> 6.デプロイされたスタックの名前 (新しいスタック名または既存のスタック名) を入力します。

ユーザーガイド通りで問題ありません、スタック名は既存のものと重複しないような文字列を指定します。今回は"teststack001"としました。

> 7.コンソールの［AWSツールキット］タブ上のデプロイの成功を確認します。

成功すると、CloudFormationで、先に指定したスタックが実行され、リソースが作成されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/6ffdc39f-66bf-314c-0b53-8f7ee2f757ae.png)

## 更新
更新したい場合は、デプロイ時に、更新したいスタック名を指定するだけです。試しにLambdaの実行時間を延ばします。
先程デプロイされたLambda関数の実行時間は3秒になっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/577f25bf-fb8e-41ba-b024-24aa7e7c1e12.png)

`template.yaml`を修正して、タイムアウト時間を延ばします。

```
    Properties:
      CodeUri: hello_world/
      Handler: app.lambda_handler
      Runtime: python3.9
      Timeout: 60 # 追加
      Architectures:

```

デプロイした時と同じ手順、同じ名前で再デプロイします。
Lambdaの実行時間が1分になっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/f1b0395b-eed6-e2bb-c0ed-777ddfdb734f.png)

CloudFormation側でも、更新扱いになっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553680/f92b000c-afde-1dde-7995-30cd27d7ef5f.png)

## 削除
> 1.［AWSExplorer］を開きます。
> 2.［AWSExplorer］ウィンドウで、削除したいデプロイされたアプリケーションを含むリージョンを展開し、AWS CloudFormationを拡張します。
> 3.削除したいサーバーレスアプリケーションに対応する AWS CloudFormation スタック名のコンテキスト (右クリック) メニューを開き、［Delete CloudFormation Stack (CloudFormation スタックの削除)] を選択します。
> 4.選択したスタックを削除したいことを確認する場合は、［削除 ］を選択します。

ユーザーガイドの通りでもよいですし、CloudFormationのコンソールから削除しても同じように削除できます。

# おわりに
Lambdaを新しく作る必要があり、以前と大きく異なっていて戸惑ったのでわかる範囲でまとめました。
Cloud9でLambdaを開発する方の参考になるとうれしいです。


# 追記：ビルドとか、Pythonのバージョンとか(2022.02.09追記)
使っていて気づいたことを追記します。

- Cloud9は、Python3.7まで入っている。
- この状態でPython3.9のLambda関数を**外部ライブラリ未使用で**Toolkitを使ってデプロイすると、正常にデプロイされる。
- 一方、(requirements.txtに記載して)外部ライブラリを使ってデプロイをすると、以下のようなメッセージが出てビルドでエラーになります。

```
Error: PythonPipBuilder:Validation - Binary validation failed for python, searched for python in following locations  : ['/usr/bin/python'] which did not satisfy constraints for runtime: python3.9. Do you have python for runtime: python3.9 on your PATH?
```

解決方法は以下の2つがあるようです。

- Lambda関数のPythonのバージョンを、インストールされているものに併せる。
- Lambda関数のPythonと同じバージョンを、マシンにインストールする。

バージョンを下げたら、以下のようなメッセージが出て、入れられないライブラリがありました（今回は[cerberus](https://docs.python-cerberus.org/en/stable/)の例です）。

```
Error: PythonPipBuilder:ResolveDependencies - {cerberus==1.3.4(sdist)}
```
Pythonのバージョンを上げる方法は、以下のサイトを参考に行いました。

https://codechacha.com/ja/ubuntu-install-python39/

```
$ wget https://www.python.org/ftp/python/3.9.9/Python-3.9.9.tgz
$ tar -xf Python-3.9.9.tgz
$ cd Python-3.9.9
$ ./configure --enable-optimizations
$ sudo make altinstall
```

新しい環境は入れておくとよいかと思います。