AWS構築学習記 #3｜CRUD API編：Lambda + API Gateway + DynamoDBでサーバーレス構築



AWS学習の第3弾として以下の内容で試験的にCRUD APIを構築してみました！
今回コードだらけで自分でも読む気が失せるほど、あまりにも見にくいので、
事前に作っておいたHLDとLLDを書いたスプレッドシートを貼っておきます。
https://docs.google.com/spreadsheets/d/1slvz8tODkX05iSfNwAM3VOLdQC71LUkoAqHYiKSl83Q/edit?usp=sharing
※補足：この構築記録に含まれるAWSリソースはすでに削除済みです。
　本記事内のAWSアカウントID等はすべてダミーに置き換え済みです。


## 目次
HLD
・プロジェクト名
・目的
・対象ユーザー
・全体構成概要
・使用サービス一覧
・ユースケース
・非機能要件
・想定トラフィック
・成果物

簡単な作業概要

エラー発生ポイント

感想・改善案

次回予告

## プロジェクト名
Lambda + API Gateway + DynamoDBでユーザー管理APIの作成	
	
## 目的	
クライアントとサーバー間でデータの作成、読み取り、更新、削除の操作を行うためのAPIの作成	
Lambda + API Gateway + DynamoDB	
	
## 対象ユーザー	
WebAPIの利用者（user）	
WebAPIの管理者（Administrator）	
	
## 全体構成概要	
外部ネットワーク　⇔　API Gateway　⇔　Lambda　⇔　Dynamo DB

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4066953/c939d871-d8c2-4be9-a521-ff298f484aa6.png)

	
## 使用サービス一覧	
Lambda：	
イベント（APIへのアクセス等）に対して、定められたコードを実行する
https://us-east-1.console.aws.amazon.com/lambda/home?region=us-east-1#/begin
API Gateway：	
開発者が API を作成、公開、保守、監視、保護を行うことが可能なサービス
https://us-east-1.console.aws.amazon.com/apigateway/main/welcome?region=us-east-1
DynamoDB：	
NoSQLのDB、運用保守不要でAuto Scaling機能を持ったDB、jsonファイルにて管理を行う
https://us-east-1.console.aws.amazon.com/dynamodbv2/home?region=us-east-1#service
	
## ユースケース	
1.ユーザーがAPIへアクセス	
2.リクエストに対し、Lambdaにて指定されたコードの実行	
3.実行されたコードが、ユーザーのリクエスト内容に基づいてDynamoDBに対してCRUD操作を実行	
4.実行結果をユーザーの画面へ表示	
	
## 非機能要件	
可用性：	
Lambda、Dynamo DBでのAuto Scaling機能でのマルチAZで高可用性を確保した冗長構成

コスト最適化：
読み書きが頻繁に発生するため、S3ではなくDynamo DBを利用しコスト削減

セキュリティ：
"IAMロールによりLambdaからDynamoDBへのアクセスを制御
API Gatewayに認証機構（APIキー、IAM認証、Cognito）を組み合わせてアクセス制御
必要に応じてCORSポリシーの設定を行う

拡張性：
API Gatewayにより新エンドポイントやバージョン管理が容易、Lambda関数の追加も柔軟
	
## 想定トラフィック	
低トラフィック（テスト環境）	
	
## 成果物	
ユーザー登録、確認、削除、更新が可能なAPI	

## 簡単な作成概要
①Amazon DynamoDBの作成----------
キャッシュなしでリソースに直接アクセスするため、東京リージョンに作成
※ユーザーの位置とリージョンを近くすることで、通信遅延を減らす

パーティションキーは使用するが、ソートキーは使用しない
※パーティションキーはデータのグループ分け
　データをユーザー毎にグループに分けて管理する
　フォルダみたいな立ち位置
※単一データを扱うため、ソートキーはいらない
　ユーザー管理だけでなく、注文履歴や時系列ログ等が必要であればあった方が便利
　グループ内の識別タグ
　
②IAMロールの作成      ----------
信頼ポリシー：
Lambdaにて利用ができるように設定
権限ポリシー：
指定したIAMロールにて、dynamo DBへの指定したアクションのアクセス権限を与える

③Lambdaの作成         ----------
ランタイムはpython
以下4つのLambdaをそれぞれ作成
|メソッド  |パス                    |説明                          |
|---------|----------------------|----------------------------|
|POST    |/users                 |ユーザー新規作成            |
|GET     |/users/{username}       |ログイン確認                |
|PUT     |/users/{username}       |name更新                    |
|DELETE  |/users/{username}       |削除                        |

　
④API gatewayの作成    ----------
IPアドレスタイプはIPv4
統合先は以下の通り
|メソッド  |パス                    |統合先                          |
|---------|----------------------|-------------------------------|
|POST    |/users                 |CRUDapi_Lambda_test_post      |
|GET     |/users/{username}       |CRUDapi_Lambda_test_get       |
|PUT     |/users/{username}       |CRUDapi_Lambda_test_put       |
|DELETE  |/users/{username}       |CRUDapi_Lambda_test_delete    |


⑤確認                 ----------
linuxのCLIにて以下コマンド入力
Taroを作成したのちに、TaroをJiroへ更新
最後にユーザーを削除し、コマンドにて消えたことを確認

ユーザー作成コマンド
```
curl -X POST https://<APIゲートウェイのARN>/prod/users \
  -H "Content-Type: application/json" \
  -d '{"name": "Taro"}'
```
想定レスポンス
```
{
  "message": "User created",
  "username": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
```

ユーザー確認コマンド
```
cur POST https://<APIゲートウェイのARN>/prod/users/Taro
```
想定レスポンス
```
{
  "message": "User found",
  "name": "Taro"
}'
```

ユーザー更新コマンド
```
curl -X PUT https://<APIゲートウェイのARN>/prod/users/Taro \
  -H "Content-Type: application/json" \
  -d '{"name": "Jiro"}'
```
想定レスポンス
```
{
  "message": "Name updated"
}
```

ユーザー削除コマンド
```
curl -X DELETE https://<APIゲートウェイのARN>/Prod/users/Jiro
```
想定レスポンス
```
{
  "message": "User deleted"
}
```

## エラー発生ポイント
②IAMロール作成中、作成していた信頼ポリシーにて保存ができないエラー
```:エラーメッセージ	
行 4, 列 4	Missing Principal: ポリシーステートメントに Principal 要素を追加します。
行 11, 列 18	Role Trust Policy Syntax Error Resource: ロール信頼ポリシーは、アタッチ先のロールに適用されます。リソースを指定することはできません。Resource または NotResource 要素を削除します。
行 13, 列 4	Missing Principal: ポリシーステートメントに Principal 要素を追加します。
行 21, 列 18	Role Trust Policy Syntax Error Resource: ロール信頼ポリシーは、アタッチ先のロールに適用されます。リソースを指定することはできません。Resource または NotResource 要素を削除します。
```	
→はじめは、信頼ポリシーに権限ポリシーと信頼ポリシーを両方書いていたが、のちに分けて書く必要あることが判明		
修正したLLDの通りに記載し、エラーが解消		

⑤Lambda関数実行後に{"message": "Not Found"}が返るエラー
```:エラーメッセージ
" curl -X POST ""https://<API-ID>.execute-api.ap-northeast-1.amazonaws.com/Prod/users"" \
  -H ""Content-Type: application/json"" \
  -d '{""name"": ""Taro""}'で確認しようとすると
{""message"":""Not Found""}"		
{"errorMessage": "name 'json' is not defined", "errorType": "NameError", "requestId": "983ef39a-e2bc-4a3c-be5b-ad1705b8343f", "stackTrace": [" File \"/var/task/lambda_function.py\", line 2, in lambda_handler\n body = json.loads(event[\"body\"])\n"]}		
```
→HTTP APIで作成していたが、REST APIで再度作成しなおし、Lambdaコードにimport jsonを追加。
jsonレスポンスを返すように修正したところ事象改善

CloudWatchで見つけた権限ポリシーのResource指定不足エラー
```:エラーメッセージ		
"[ERROR] ClientError: An error occurred (AccessDeniedException) when calling the PutItem operation: User: arn:aws:sts::<account-id>:assumed-role/CRUDapi_IAMrole_test/CRUDapi_Lambda_test_post is not authorized to perform: dynamodb:PutItem on resource: arn:aws:dynamodb:ap-northeast-1:<account-id>:table/CRUDapi_DynamoDB_test because no identity-based policy allows the dynamodb:PutItem action
Traceback (most recent call last):
  File ""/var/task/lambda_function.py"", line 18, in lambda_handler
    dynamodb.put_item(
  File ""/var/lang/lib/python3.13/site-packages/botocore/client.py"", line 569, in _api_call
    return self._make_api_call(operation_name, kwargs)
  File ""/var/lang/lib/python3.13/site-packages/botocore/client.py"", line 1023, in _make_api_call
    raise error_class(parsed_response, operation_name)"		
```
→IAMロールにて、Dynamo DBのARNを正しく入力ができておらず、アクセス許可がされていなかったことが原因		
IAMロールの権限ポリシーを正しいものに書き直し、事象改善		

DELETE実行時エラー		
```:エラーメッセージ		
"curl -X DELETE https://<APIゲートウェイのARN>/prod/users/<uuid>
{""message"": ""Internal server error""}"		
Cloudwatchより、Error Type: Runtime.ImportModuleError	
```
→Lambdaにて、pythonファイルなのに、Node.jsランタイムで実行をしていた		
Lambda - ランタイム設定より、ランタイムをNode.jsに変更し、事象改善		

## 感想
今回はHLDとLLDを事前に記載し、その通りに構築をした初めてのプロジェクトでした。
構築順に問題はなかったかなとは思いますが、それぞれのサービス理解が及んでおらず、
構築中に気づき変更を加えながら構築していきました。
今回時間かかったのが構築完了後のエラーの連続...
一つのエラーを解決後、別のエラーが姿を見せるというよくある形に、学習のためのプロジェクトも少し、複雑になってきたんだなぁとワクワクした部分があります！
エラー対処後のエラー対応は楽しいですよね？１つのエラーが複数のエラーによって引き起こされてる形よりもシンプルで好きです。
あとは前回踏み台を用意すると意気込み、実際ubuntu環境の踏み台を用意しておいたのですが、エラー対応やコマンド入力がすごい楽になりました。
自分の慣れてる環境を先に準備しておくことをお勧めします。

## 改善案
LLDを記載しているときにもう少し調べた方がよかったのかなと思いました。
ただ、今回の発生したエラー部分は実際に構築を始めなければ気づくのに時間がかかる部分が多かったイメージなので、これといった改善はなかったように思います。
単純にサービスに関する知識が増えた回なのかなと。
しいて言えば今回pythonのコードは全くわからないので、AIに作ってもらったのですが、そのコードの正誤を確認できる程度の知識があれば、事前にエラーを起きないようにできたのかなと思いました。

## 次回予告
SPAアプリホスティングを構築予定です！
S3 + CloudFront + Cognito + Lambdaという見覚えがあるようなサービスが多い...
S3 + CloudFrontは静的Webサイトの公開、Lambdaはイベントに対してコードを走らせるサービス...
Cognitoは知らないですが、何となく動的Webサイトの一部なのでしょう。
ということは...HTML作らんと....　だる...ということで頑張ります。