🤖

AIチャットアプリを作ってみよう (インフラ構築編)

2023/12/21に公開

この記事は某有志アドベントカレンダー 2023の 21 日目の記事です。


「AIチャットアプリを作ってみよう」シリーズ


こんにちは。あべです。

前回に引き続き、AIチャットアプリのインフラの構築をしていこうと思います。

Dockerfileを書く

現状、ディレクトリ構成はこんな感じになっています

app
`- src/index.ts
`- package.json
`- bun.lockb

appの下にDockerfileを記述していきます
oven/bun:latestというイメージが公開されていたので、今回はそれを利用します。

// app/Dockerfile
FROM oven/bun:latest AS build

WORKDIR /app

COPY package.json bun.lockb ./

RUN bun install --production

FROM oven/bun:distroless AS runtime

COPY src /app/src
COPY --from=build --chown=nonroot:nonroot /app/node_modules /app/node_modules

USER nonroot
EXPOSE 3000

CMD ["run", "--hot", "/app/src/index.ts"]

CDKでECRを作成

本当はAppRunnerまでCDKで構築してみようと思ったのですが、
正式には L1 Constract しか用意されていないようで難易度が高く、、、断念しました。

なので今回はECRとimageのデプロイまでをCDK + スクリプトで、
AppRunnerの構築はAWSコンソール上で実施しようと思います。

cdk initで初期化し、コンテナレジストリを作成していきます。
※CDKを初めて入れた環境では適宜cdk bootstrapで初期化します

// cdk/lib/cdk-stack.ts

import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as ecr from 'aws-cdk-lib/aws-ecr';

export class CdkStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const repository = new ecr.Repository(this, 'ai-chat-app-repo', {
      repositoryName: 'ai-chat-app-repo',
      removalPolicy: cdk.RemovalPolicy.DESTROY,
      autoDeleteImages: true,
    });
  }
}

cdk deployを実行すると、コンテナレジストリがデプロイされます。

コンテナイメージのデプロイ

この辺りはこちらの記事を参考にさせていただきました🙏
https://dev.classmethod.jp/articles/aws-cdk-v2-create-ecr-repo-push-image-execute-on-lambda/

現状ディレクトリ構造はこんな感じです。

/
  `- app
    `- src/index.ts
    `- package.json
    `- bun.lockb
  `- cdk
    `- lib
      `- cdk-stack.ts
    `- bin
    `- (略)

ルートディレクトリにDockerイメージをpushするスクリプトを配置します。
aws configureは適宜設定してある前提です。まだの方は先に設定が必要になります。

// image_push.sh

#!/bin/bash

ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
REGION=$(aws configure get region)
ECR_REPOSITORY_NAME=ai-chat-app-repo
ECR_REPOSITORY_URI=${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/${ECR_REPOSITORY_NAME}

echo "Account ID: $ACCOUNT_ID"
echo "Region: $REGION"
echo "ECR Repository Name: $ECR_REPOSITORY_NAME"

echo "Building docker image..."

aws ecr get-login-password --region ${REGION} | docker login --username AWS --password-stdin ${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com

(cd app && docker build --platform linux/amd64 -f ./Dockerfile -t ${ECR_REPOSITORY_NAME} .)
docker tag ${ECR_REPOSITORY_NAME}:latest ${ECR_REPOSITORY_URI}:latest
docker push ${ECR_REPOSITORY_URI}:latest

echo "Docker image built and pushed to ECR repository: ${ECR_REPOSITORY_URI}"

これでECRにイメージをpushする準備ができました。
では早速実行してみます。

chmod -x image_push.sh
./image_push.sh

AWSコンソール側で確認してみましょう。

ちゃんとpushされていますね。

App Runnerのサービスを作成する

App Runnerが参照するコンテナレジストリのURLを設定しましょう。
URIはECRから取得できます。

必要に応じて、CPUはメモリ、オートスケーリングなどの設定が必要なりますが、
今回はお試しなので何も考えずにデフォルト設定でいきます。

次に実行時環境変数の設定をします。
今回はOPENAI_API_KEYが必要になるので設定しましょう。

後述する問題ではハマったのですが、なんとかデプロイ完了してAPIにアクセスできました。
複雑な設定なしですぐアクセスできるのも嬉しいですね。

ハマりポイント

App Runnerでコンテナの実行が失敗していました。

12-19-2023 10:07:50 PM exec /usr/local/bin/bun: exec format error

理由はシンプルで、Dockerイメージのビルド時に--platformが未指定だったのが原因でした。
linux/amd64に指定して解決しました

docker build --platform linux/amd64 -f ./Dockerfile -t ${ECR_REPOSITORY_NAME} 

後片づけ

AppRunnerは停止していても課金されるようなので、削除しておきましょう。
CDKデプロイしたECRも破棄します

cdk destroy

(やっぱり後片付けが面倒なので、全部CDKでやればよかったですねー、、、)

まとめ

今回で一旦一区切りとなります(フロント構築編は気が向いたらやるかも、、、?)

App RunnerをCDKで構築したかったのですが、うまくいかなかったのが無念です、、、

L1 Constract ではなく、実験的L2 Constractの @aws-cdk/aws-apprunner-alphaを利用した実装では以下の記事が参考になりました。 (今更ですが、こちらの実装の方がスマートですね、、、)
https://zenn.dev/watany/articles/194e31331a25be#app-runnerでの構築

まぁ、周り道したなりに勉強になる点もあったのでよしとします。

お付き合いありがとうございました。
それではー。

株式会社ガラパゴス(有志)

Discussion