CodePipelineを利用したS3+API Gateway+Lambdaの簡易Webアプリケーション更新自動化

AWSの勉強として簡易的な構成で構築を実施
今回はマネジメントコンソール経由で構成

# 構成

![スクリーンショット 2025-06-01 201012.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3936824/2b01f98e-e32e-4b1d-b880-12d6b2649f25.png)

図は draw.io を利用し作成

　draw.io
　https://www.draw.io/

S3の静的ウェブサイトホスティングを利用し、バックエンドではAPI Gateway経由でlambdaにて処理を実施する簡易的な構成

GithubのリポジトリへpushすればCodePipelineが動作

CodeBuildが動いて処理後にアーティファクトをS3に格納され、
アーティファクトを利用してホスティングしているHTMLファイルとlambdaの更新を自動でやってくれる

# ソースコード

https://github.com/SH-kaw/CreateCharacter_public

・buildspec.yml      → CodeBuild用
・index.html         → S3の静的ウェブサイトホスティング用
・lambda_function.py → lambda用

※ 公開用なのでhtmlファイルのAPIエンドポイントはダミー

### lambda_function.py について
なんでもよかったので以前pythonの練習用に作成したものを流用

numpyが標準ライブラリではないので、モジュールのインポート用にnumpyを含むレイヤーを作成する必要あり

　参考:
　レイヤーによる Lambda 依存関係の管理
　<https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/chapter-layers.html>

### index.html について
試験的にChatGPTを活用して作成
ただし、作成したものをそのままローカルで動かしてみたら動かず

最終的に以下3点問題があった

(1) CORS(Cross-Origin Resource Sharing)の設定をしていなかった

API Gateway側でCORSの設定をしていなかったため実施

　参考:
　CORS とは-クロスオリジンリソース共有の説明
　https://aws.amazon.com/jp/what-is/cross-origin-resource-sharing/

なお、最初はlambda関数側にも Access-Control-Allow-Origin": "*" をヘッダーに追加していたが
今回はHTTP APIのlambda統合でAPI Gateway側でCORSのoriginを*で有効化すれば、
"Access-Control-Allow-Origin": "*"が自動的にレスポンスに追加されていた

そのため、lambda関数側でのヘッダー付与は不要と確認できたので最終的に削除

Access-Control-Allow-Origin": "*" が付与されていることの確認は
python -m http.server 8080 でローカルをHTTPサーバにして
chromeの開発者ツール(F12) → Network から確認

![スクリーンショット 2025-06-01 215323.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3936824/54bd4244-c9a0-4991-85b9-f17859fb247a.png)

※python -m http.server 8080 は実行した場所がHTTPのルートになるので
　ちゃんとindex.htmlがある階層まで移動してから実行する

(2) ローカルでHTMLファイルを開いてテストしていた

ローカル(file://~)だとoriginは null になるらしい
"*"はnullを含まないので、Access-Control-Allow-Originが自動的に付与されても以下エラーが出る

Access to fetch at '<API Gatewayエンドポイント>' from origin 'null' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

上記の通りローカルをHTTPサーバにして動作検証することで解消

(3) lambdaから返されるjsonのパース処理誤り

今回のlambda関数は以下のようなオブジェクト形式ではなく

~~~
{
  "statusCode": 200,
  "headers": {
    "Access-Control-Allow-Origin": "*"
  },
  "body": "\<body>"
  }
~~~

以下のような配列で返してきていた

~~~
["名前=ラネコ", "element=['火']", "STR=[40]", "INT=[41]", "POW=[45]", "DEX=[55]", "HP=[54]", "MP=[62]", "APP=[61]", "family background=[69]", "gender=男", "height=[180]"]
~~~

以下のように修正して対応

修正前:
 ~~~
　const data = await response.json();
　document.getElementById("result").textContent = data.body;
~~~

修正後:
~~~
　const data = await response.json();
　document.getElementById("result").textContent = data.join("\n");
~~~

### buildspec.yml について
CodeBuildで使われる実行コマンドなどが書かれたもの
今回は特に各ビルドで実施する処理はないのでプロセスの実行時に echo でメッセージを出力させたのみ

artifactsがS3に出力される成果物の指定場所
今回はindex.htmlとlambda_function.pyをそのまま出力

# 構築
・lambda関数の作成
・API Gatewayの作成
・S3で静的ウェブサイトホスティングの設定
・CodeBuild,CodePipelineの設定

### lambda関数の作成

GUIからランタイムにPythonを指定して普通に作成

![スクリーンショット 2025-06-03 032536.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3936824/1621fecb-0d36-4a92-9a61-310fb5c6c936.png)

ただし、上述した通り、numpyがpythonの標準ライブラリではないのでレイヤー構築が必要
レイヤー構築は以下別記事で分けてまとめています

https://qiita.com/hir0202/items/cb8252aecd0098b3dd6f

### API Gatewayの作成

![スクリーンショット 2025-06-05 030227.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3936824/4ed74008-7a87-4db9-9150-d731e243c4fa.png)

![スクリーンショット 2025-06-05 030407.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3936824/90376cf0-7eab-421c-b99b-219906901a8b.png)

lambda統合で利用するlambda関数を指定し、ルートやステージなどはデフォルトで作成

### S3で静的ウェブサイトホスティングの設定
バケットを作成してから以下で有効化して

![スクリーンショット 2025-06-05 055406.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3936824/913995ca-35a9-44fe-a4f6-fe36f103280b.png)

以下でパブリックアクセスを許可

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3936824/a047f7b2-f2ec-4d29-8f17-80fb1d1b6086.png)

### CodeBuild,CodePipelineの設定
#### CodeBuild
まずCodeBuildでプロジェクトを作成する
単に更新するだけならビルドステージの指定は省略できそうだが今回は勉強がてら指定

以下の通りソースをGithubに指定(認証はSecrets Manager経由で実施済

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3936824/23f815be-20a5-404d-9c8d-10d295da55f9.png)

環境はLambdaを使用

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3936824/acf5da2d-cf89-424e-9b46-1548dfed7d18.png)

アーティファクトの格納先となるバケットを指定

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3936824/1b9d86ee-c87b-4e6a-b426-5ff15c78276e.png)

CloudWatchの部分などは一旦デフォルトのままプロジェクト作成
※CodeBuild単体で使うならpushでのウェブフック設定もここでしておく

#### CodePipeline

CodePipelineはカスタムで作成してソースをGithubに指定

![スクリーンショット 2025-06-08 181828.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3936824/0de5dbb4-d22f-4e62-9a71-463726958508.png)

今回はmainブランチにpushした際にCodePipelineが動作するようウェブフックを設定

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3936824/56221034-8bb1-47ef-ab9d-f028594078ac.png)

ビルドステージは先ほど作成したCodeBuildのプロジェクトを指定

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3936824/f2e57829-20bd-42ae-95c0-08b4977f626c.png)

デプロイステージで一旦lambda関数を指定
GUIだと複数のデプロイステージを選べなかったのでS3の更新については後で追加する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3936824/79221b67-2326-4c32-b08f-28274661fcd1.png)

パイプラインを作成後、パイプライン名を押下→編集から進みステージを追加
静的ウェブサイトホスティングの設定を実施したバケットを指定

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3936824/7935a6a7-6ec0-48e6-9463-6250771d4204.png)

 [デプロイする前にファイルを抽出する] のチェックは必須
チェックしないとzipファイルのままCodePipelineにデータが渡されてしまう

チュートリアル: Amazon S3 をデプロイプロバイダとして使用するパイプラインを作成する
https://docs.aws.amazon.com/ja_jp/codepipeline/latest/userguide/tutorials-s3deploy.html
\=======
[デプロイ前にファイルを抽出] を選択しないと、デプロイに失敗します。これは、パイプラインの AWS CodeCommit アクションがソースアーティファクトを圧縮し、ファイルが ZIP ファイルであるためです。
\=======

これで一通りの設定は終了

ここからローカルでコードを更新してPush→Pull requestしてリポジトリを更新すると
CodePipelineが動作して自動で更新してくれる