Infrastructure Composer + SAM で簡単にインフラ構築してみた

## はじめに
　今回は、Infrastructure Composerを使用して、サーバーレスアーキテクチャを構築し、ChatGPTのレスポンスを受け取る仕組みを作ります。
使用する主なAWSサービスは以下です。

**Infrastructure Composer**：AWSのインフラを可視化し、設計や他のAWSサービスとの連携、リソース管理を簡単にするツール。
**Lambda関数**：サーバーレス環境で関数を実行できるサービス。
**SAM**：サーバーレスアプリケーションを簡単にデプロイ・管理するためのフレームワーク。

## ◾️前提条件：必要なツールを準備する
### **1.AWS アカウント作成**
まず、AWSアカウントが必要です。
まだ作成されていない場合は、[AWSの公式サイト](https://aws.amazon.com/jp/free/)でアカウントから作成をお願いします。

### **2.AWS CLIのインストール**
ローカル環境からAWSと連携するために、AWS CLIをインストールします。
[AWS CLI インストールガイド](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/getting-started-install.html)

### **3.AWS SAM CLIのインストール**
AWS SAM（Serverless Application Model）CLIをインストールします。
これはLambda関数などのサーバーレスアプリケーションを簡単にデプロイするためのツールです。
[AWS SAM CLI インストール](https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/install-sam-cli.html)

### **4.AWS Infrastructure Composerへのアクセス**
AWS Management ConsoleからInfrastructure Composerを開きます。
これは、サーバーレスアプリケーションのアーキテクチャをドラッグ&ドロップで簡単に作成できるツールです。

## ◾️AWS Infrastructure Composerでアーキテクチャを作成
### **1. Infrastructure Composerの起動**
AWSコンソールで「Infrastructure Composer」を検索し、プロジェクトの作成を行います。
![スクリーンショット 2024-10-23 20.25.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748502/556a4927-5da2-e3de-525a-5b775164b607.png)

### **2. Lambda関数の追加**
画面の左側に「Lambda Function」という項目があるので、それをドラッグしてキャンバスに追加します。
右側の設定で、以下を設定します。
- Lambda関数名：ChatbotFunction
- ランタイム： nodejs20.x(typescript)
- ハンドラー：index.handler



### **3. API Gatewayの追加**
「API Gateway」をキャンバスに追加し、先ほど作成したLambda関数と接続します。
これにより、API Gatewayを経由してLambda関数が呼び出される構成が完成します。

### **1. Lambda関数の基本コード作成**
AWS Application Composerを使って、Lambda関数が作成されたら、実際にコードを書きます。
プロジェクトのルートディレクトリに、index.ts を作成します。

```index.ts
// 外部APIに対してHTTPリクエストやレスポンス取得可能
import axios from 'axios'

// Lambda関数はデフォルトでhandlerメソッドを呼び出す＝Lambdaにおける関数の実行部分
export const handler = async(event) => {
  // JSON形式の文字列　→　jsのオブジェクトに変換
  const body = JSON.parse(event.body)
  const userMessage = body.message

  const apiKey = process.env.OPENAI_API_KEY

  try {
    const response = await axios.post(
      "https://api.openai.com/v1/chat/completions",
      {
        model: "gpt-4",
        messages: [{ role: "user", content: userMessage }],
      },
      {
        headers: {
          Authorization: `Bearer ${apiKey}`,
          "Content-Type": "application/json",
        },
      }
    );
    // ChatGPT APIのレスポンスからAIの生成した返信を取得
    const botReply = response.data.choices[0].message.content

    // API Gateway経由で呼び出されているため、HTTPレスポンスとしてリターン
    return {
      statusCode: 200,
      body: JSON.stringify({ reply: botReply})
    }
  } catch(e) {
    return {
      statusCode: 500,
      body: JSON.stringify({ e: 'Error communicating with ChatGPT API'})
    }

  }
}
```
このコードは、API Gateway経由でユーザーからのメッセージを受け取り、それをChatGPT APIに送信し、応答を返すLambda関数です。
axiosを使ってChatGPT APIを呼び出しています。
`OPENAI_API_KEY`は、後で設定する環境変数です。

### **2. 環境変数の設定**
Lambda関数の環境変数に、OPENAI_API_KEYを設定します。
これは、OpenAIのAPIキーです（OpenAIのアカウントで取得できます）。
プロジェクトのルートディレクトリに、.env を作成します。
```.env
OPENAI_API_KEY=your-openai-api-key
```

Lambdaの設定を開き、環境変数に OPENAI_API_KEY を追加します。
![スクリーンショット 2024-10-25 13.49.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748502/e936ac3c-95bc-77ff-6e1d-731e5b136c24.png)

:::note info
**!Ref**
CloudFormationでは、「Ref関数」と呼ばれる組み込み関数の略記であり、テンプレート内の他のリソースやパラメータの値を参照するために使われます。この関数を使うと、テンプレート内で一度定義したリソースの値や、スタックをデプロイする際に渡されるパラメータ値を他の部分で参照できるようになります。
:::

## ◾️SAMを使ってデプロイ
### **1. SAM テンプレートの作成**
プロジェクトのルートディレクトリに、template.yaml を作成します。

```template.yaml

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
  ChatbotFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: index.handler
      Runtime: nodejs14.x
      CodeUri: .
      Environment:
        Variables:
          OPENAI_API_KEY: !Ref OpenAIKey
      Events:
        ApiEvent:
          Type: Api
          Properties:
            Path: /chatbot
            Method: post

Parameters:
  OpenAIKey:
    Type: String
    Description: "The OpenAI API key"
```

template.yaml はSAM用のテンプレートで、Lambda関数とAPI Gatewayの設定を記述しています。
Parameters でOpenAIのAPIキーをパラメータとして受け取る設定にしています。

### 2. デプロイ手順
次に、ローカル環境で sam build を実行してLambda関数をビルドします。

```
sam build
```
その後、デプロイコマンドを実行します。ここで、OpenAI APIキーを指定します。

```
sam deploy --guided
```
デプロイ時に、OpenAI APIキーを入力するように求められますので、取得済みのAPIキーを入力してください。

## ◾️ Chatbotの動作確認
### 1. API Gatewayのエンドポイント確認
❶AWSマネジメントコンソールにログイン
❷サービス検索で、「API Gateway」と検索
❸対象のAPIを選択
❹左側メニューの「Stages（ステージ）」をクリック
❺ステージの設定ページに「**URL を呼び出す**」としてエンドポイントURLをコピー

### 2. Postmanでテスト
Postmanやcurlを使って、API Gatewayのエンドポイントにリクエストを送ります。
先ほど、コピーしたエンドポイントURLにAPI Gatewayで設定したpathを追加します。

![スクリーンショット 2024-12-03 13.06.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748502/b5d9b4fe-5bb0-b408-057d-186b8affc6a1.png)


**Postmanの設定**
メソッドを POST に設定し、以下のようなJSONデータをBodyに入力して送信します。

:::note warn
OpenAIのAPIを叩く際、別途課金する必要があります。
[OpenAI Billing](https://platform.openai.com/settings/organization/billing/overview)
:::

```json
{
  "message": "こんにちは"
}
```

### 3. 結果確認
ChatGPTの応答が返ってくれば、実行成功です。

## まとめ
　今回、AWS Infrastructure Composerを使ってサーバーレスアーキテクチャを簡単に設計し、API Gateway経由でChatGPT APIを呼び出すLambda関数をデプロイしました。これにより、コードを書くだけでなく、インフラの設定も視覚的に構築できるため、複雑なサーバーレスアーキテクチャの構築が容易になります。