AWS LambdaでPython基礎

2022/4/6(水)に行われたAPNパートナー限定セミナー「Pythonの基礎から学ぶ!サーバーレス開発はじめの一歩」で学んだLambda部分の復習。

AWS Lambdaと Amazon Translate を使ってテキスト翻訳するアプリケーションを作成する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1723719/e23bb7ce-521c-da78-714d-d6f9589b28e0.png)

# 1.Lambda 関数を実行する

## 1-1. Lambda 関数の作成

マネジメントコンソールで Lambda を開き「関数の作成」をクリックする。
- 「一から作成」を選択する。
- 関数名：適当な名前を付ける。目的がわかるようにしたいので今回は`translate_lambda`とした。
- ランタイム：python3.9

その他項目はデフォルト値のまま「関数の作成」をクリックする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1723719/b2a6fff8-0fc5-717d-480c-9cb452d346cf.png)

lambda 関数が作成できたら、画面下部のコードを書き換える。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1723719/bdd175b7-dc26-a208-c542-428466d15b7f.png)

```py:lambda_function.py
def lambda_handler(event, context):
    # ログを出力する
    print("event:",event)
    print("context:",context)
```

コードを書き換えたら「Deploy」をクリックしてコードを保存する。
Deployすると「Changes not deployed」が消える。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1723719/c82cb3d1-7ef0-564a-7cdd-1132eee611c7.png)

テストタブに移動し、「テスト」をクリックする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1723719/a449487f-2c5a-3876-403b-5723dfcdd8b5.png)

テストを実行すると、実行結果の「詳細」を展開してログを確認できる。
「ここをクリックし」のリンクをクリックすると、CloudWatch のロググループ画面が開き、該当のログの詳細を確認できる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1723719/7e5cea40-ed3c-1c21-92a7-72bb8f9b16ad.png)

ClousWatch ロググループ画面には実行したLambda のログが出力されている。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1723719/b3f535bf-3c74-39d4-951c-3f83ff60dc5b.png)
`event`、`context`から続くログを見ると、Lambda関数にどんな引数が渡されたのか確認できる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1723719/2dea1353-5fba-ee02-629c-082384118df5.png)
- event：Lambda関数の呼び出し元の情報
- context：Lambda で関数が実行されると、[コンテキストオブジェクト](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/python-context.html)が[ハンドラー](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/python-handler.html)に渡される。このオブジェクトは、呼び出し、関数、および実行関数に関する情報を示すメソッドおよびプロパティを提供する。

今回は`event`を変更せず実行したため、初期値で設定されていた`{'key1': 'value1', 'key2': 'value2', 'key3': 'value3'}`が引数としてLambda 関数に渡されていたことがわかる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1723719/aa540344-1b09-da43-af49-6bac370b751d.png)

[Lambda 関数のハンドラー](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/python-handler.html)は、イベントを処理する関数コード内のメソッドのこと。関数が呼び出されると、Lambda はハンドラーメソッドを実行し、ハンドラーによってレスポンスが終了するか、レスポンスが返ったら、別のイベントを処理できるようになる。
今回もハンドラーによってハンドラーメソッドが実行され、レスポンスが終了している状態となる。

今実行したコードはハンドラー（`lambda_handler()`）を呼ぶ文を記述していないが、`lambda_handler()`が実行されているのは、Lambda関数のハンドラーとして`lambda_functions.py`の`lambda_handler()`がLambda関数のコードタブの画面下部で設定されているため。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1723719/6b180df1-70ee-1a64-a7f7-6e2af6d474f1.png)

# 2.Lambda 関数でAmazon Translate を使う

## 2-1.AWS Cloud9 で書いたコードを Lambda 関数用に書き換える

[2-4-1.位置引数](https://qiita.com/emiki/items/65eb26492be5315d929d#2-4-1%E4%BD%8D%E7%BD%AE%E5%BC%95%E6%95%B0)で練習した以下のpythonコードを書き換える。

```py:positional_arguments.py (書き換え前)
 # boto3パッケージを使うように指定する
import boto3

 # honyaku関数の宣言
def honyaku(text, source_language_code, target_language_code): #位置引数をここで定義
    # boto3パッケージ内のどのサービスを使用するか指定する
    client = boto3.client('translate') #関数宣言前に書いてもいいし、関数内に書いてもいいらしい
    # translate_text()メソッド項目の「Request Syntax（リクエスト構文）」を参考に、パラメータを指定する
    response = client.translate_text(
        Text=text, # 位置引数text
        SourceLanguageCode=source_language_code, # 位置引数source_language_code
        TargetLanguageCode=target_language_code  # 位置引数target_language_code
        )
    # honyaku()関数はここまで
    
    # honyaku()関数の中にある変数 response の内容を出力 
    print(response)

 # honyaku()関数の呼び出し
honyaku('I want to be able to use Python!', 'en', 'ja') # 指定した順序で引数の値を書く
```

これを、以下の方針で　Lambda関数に書き換える。
- honyaku関数`honyaku()`はそのままLambda関数内のプログラムへ移行する
- Lambda 関数が実行する`lambda_handler()`関数で`honyaku()`関数を呼び出す

先ほど作成した「translate_lambda」に戻り、コードを書き換える。
`lambda_handler()`関数の上に、`honyaku()`関数と`import boto3`をコピーして貼り付け。

```py:「translate_lambda」lambda_function.py 編集中1
import boto3

 # honyaku関数の宣言
def honyaku(text, source_language_code, target_language_code): #位置引数をここで定義
    client = boto3.client('translate')
    response = client.translate_text(
        Text=text, # 位置引数text
        SourceLanguageCode=source_language_code, # 位置引数source_language_code
        TargetLanguageCode=target_language_code  # 位置引数target_language_code
        )

def lambda_handler(event, context):
    # ログを出力する
    print("event:", event)
    print("context:", context)
```

`lambda_handler()`関数内のprint文を削除し、`honyaku()`関数を実行する呼び出し文を追記する。
ここではユーザー定義の`honyaku()`関数に直接文字列を渡すのではなく、変数にそれぞれ値を格納してから`honyaku()`関数にその変数を渡す。

```py:「translate_lambda」lambda_function.py 編集中2
import boto3

 # honyaku関数の宣言
def honyaku(text, source_language_code, target_language_code): #位置引数をここで定義
    client = boto3.client('translate')
    response = client.translate_text(
        Text=text, 
        SourceLanguageCode=source_language_code, 
        TargetLanguageCode=target_language_code 
        )
      
    # honyaku()関数の中にある変数 response の内容を出力 
    print(response)

def lambda_handler(event, context):
    # honyaku()関数に渡す文字列を変数に格納する
    text="I want to be able to use Python!"
    source_language_code="en"
    target_language_code="ja"
    # honyaku()関数の呼び出し
    honyaku(text, source_language_code, target_language_code) 
```

ここまで編集したら「Deploy」をクリックしてコードを保存し、テストタブから先ほどと同じように「テスト」をクリックしてコードを実行する。「実行結果: 失敗(ログ)」と表示され、詳細を開くと以下のようにエラーログが表示される。

```json
{
  "errorMessage": "An error occurred (AccessDeniedException) when calling the TranslateText operation: User: arn:aws:sts::xxxxxxxxxxxx:assumed-role/translate_lambda-role-cd41u5ay/translate_lambda is not authorized to perform: translate:TranslateText because no identity-based policy allows the translate:TranslateText action",
  "errorType": "ClientError",
  "requestId": "a3201585-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "stackTrace": [
    "  File \"/var/task/lambda_function.py\", line 21, in lambda_handler\n    honyaku(text, source_language_code, target_language_code)\n",
    "  File \"/var/task/lambda_function.py\", line 6, in honyaku\n    response = client.translate_text(\n",
    "  File \"/var/runtime/botocore/client.py\", line 391, in _api_call\n    return self._make_api_call(operation_name, kwargs)\n",
    "  File \"/var/runtime/botocore/client.py\", line 719, in _make_api_call\n    raise error_class(parsed_response, operation_name)\n"
  ]
}
```

エラーログを読むと`translate_lambda is not authorized to perform: translate:TranslateText because no identity-based policy allows the translate:TranslateText action`とあり、translate_lambdaがAmazon Translateを実行する権限がないことがわかる。

## 2-2.Lambda関数に AmazonTranslateを操作する権限を付与する
「設定」タブで「アクセス権限」を開き、実行ロールのロール名をクリックする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1723719/8c9d6af2-2a5e-ea83-badd-9286887c96f0.png)

IAM の画面に飛ぶので、このロールに AmazonTranslate の権限を付与する。「アクセス許可を追加」から「ポリシーをアタッチ」を選択する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1723719/a17669a0-2a60-79b4-7d6d-a5b594fe3cd5.png)

`TranslateReadOnly`を選択し、「ポリシーをアタッチ」をクリックする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1723719/1601f1e5-e4d9-6063-c7d8-d26f5107ae30.png)

## 2-3.Lambda 関数を再度実行する
「translate_lambda」の実行画面に戻り、テストタブから再度実行する。
成功し、翻訳結果がログに出力されているのが確認できる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1723719/d1fb8e0b-f768-7e04-3d9f-11ca2497cd7f.png)

```:translate_lambda 実行結果ログ
START RequestId: cb4380b9-xxxx-xxxx-xxxx-xxxxxxxxxxxx Version: $LATEST
{'TranslatedText': 'Pythonを使えるようになりたい！', 'SourceLanguageCode': 'en', 'TargetLanguageCode': 'ja', 'ResponseMetadata': {'RequestId': '5e18059e-xxxx-xxxx-xxxx-xxxxxxxxxxxx', 'HTTPStatusCode': 200, 'HTTPHeaders': {'x-amzn-requestid': '5e18059e-xxxx-xxxx-xxxx-xxxxxxxxxxxx', 'cache-control': 'no-cache', 'content-type': 'application/x-amz-json-1.1', 'content-length': '115', 'date': 'Sat, 16 Apr 2022 10:22:53 GMT'}, 'RetryAttempts': 0}}
END RequestId: cb4380b9-xxxx-xxxx-xxxx-xxxxxxxxxxxx
REPORT RequestId: cb4380b9-xxxx-xxxx-xxxx-xxxxxxxxxxxx	Duration: 1247.15 ms	Billed Duration: 1248 ms	Memory Size: 128 MB	Max Memory Used: 66 MB	Init Duration: 251.46 ms	
```

# 参考

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/python-context.html

https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/python-handler.html

[AWS Cloud9でPython基礎](https://qiita.com/emiki/items/65eb26492be5315d929d)
[AWS Cloud9でPython基礎～ファイルの読み書き](https://qiita.com/emiki/items/f93bddb4a1fd76b102b5)
[AWS LambdaでPython基礎](https://qiita.com/emiki/items/5daa0e31b8a8fa7c680a)
[AWS Cloud9でPython基礎～Amazon S3を利用する](https://qiita.com/emiki/items/7433971c9f1ee03dd9ea)
[AWS Cloud9でPython基礎～Amazon S3とAmazon Translateを利用する](https://qiita.com/emiki/items/b95fad815d9f8e360dd0)
[AWS LambdaでPython基礎～Amazon S3とAmazon Translateを利用する](https://qiita.com/emiki/items/0f9bbddd6f4cd475f12b)