運営者:そうすけ
30代のブロガーとエンジニア。
工場勤務→情シス→ソフトウェアエンジニア
主にフロントエンド開発を行っています。
趣味はガジェットと植物。
本記事ではNuxt3で作成したアプリケーションを簡単にAWS上にデプロイする方法としてCDKを利用したので、解説していきます。
前回の準備編記事はこちら
ファイル構成
構成の説明
cdk init app –language typescript を押すと下記フォルダ構成なっています。
プロジェクト名cdk/
┣ bin ━ プロジェクト名cdk.ts
┣ lib ━ プロジェクト名cdk-stack.ts
┣ test
その他configファイル等
bin/〇〇-cdk.ts
このフォルダはCDKアプリケーションのエントリーポイントになります。
要はインスタンスをAWSに投げる場所です。
このnuxtのcdk用プロジェクトやっていることは2つ です。
- CDKアプリケーションのインスタンス作成 (bin)
- CDKインスタンスを引き渡す形での〇〇CDKStack作成 (lib)
CDKには大きい順にアプリケーション、スタック、リソースの3つの3大要素で構成されています。
これら3つはCDKでは、基本的にすべて「コンストラクト」というオブジェクトで定義されていきます。
コンストラクトの作成は引数は第1引数にスコープ、第2引数に任意のID、第3引数にオプションになります。
今回のアプリケーションの作成は引数は第1引数にスコープ、第2引数に任意のID、第3引数にオプションになります。
libで作成したスタックをimportして、それをインスタンス化して自分自身を【app】と定義しているのがコードからわかります。
ここではNeoReshipiStackCDKがアプリケーションに所属するということを示しています。
このファイルは以上です。今回はこのファイルは変更しないままで大丈夫です。
複数のスタックに分割したり、異なるリージョンにリソースを定義するときは手を加える必要があります。
lib/○○cdkStack.ts
ここではbinフォルダにあるファイルへ出力するスタックを定義してます。
このフォルダでリソース定義を担うスタックという存在を作成しています。
import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
export class 〇〇CdkStack extends cdk. Stack {
constructor( scope: Construct, id: string, props?: cdk. StackProps )
{ super( scope, id, props); } }
constructにcdkクラスをextendで継承して、親クラスのcdk.Stackのメソッドやプロパティを自分のものとして扱うようにします。
constructor(コンストラクター)はクラス内でインスタンス化するときに初めに実行される初期化処理メソッドです。
継承を用いて定義したクラスは、コンストラクター内で『 継承した親クラスのコンストラクター』 をコールするsuper()を実行する必要があります。
そのために今回のCdkStackクラスのコンストラクターは、 super( scope, id, props); を実行しています
superでは親クラスでのコンストラクタの引数に準じているので、親クラスcdk.Stackの引数3つを定義しています。
ここにガンガンスタックとリソースを作成して、Binフォルダ内ファイルのアプリケーションに継承できるスタックとリソースをもつクラスを作っていきます。
実装
S3に静的コンテンツを置くためのスタックを準備
まずインポート分を書きます。
import {
//後で使うものもまとめてインポート
CfnOutput,
Duration,
Fn,
RemovalPolicy,
Stack,
StackProps,
}from 'aws-cdk-lib';
import { RetentionDays } from "aws-cdk-lib/aws-logs";
import { Bucket } from "aws-cdk-lib/aws-s3";
import { BucketDeployment,Source } from "aws-cdk-lib/aws-s3-deployment";
クラス内に下記のように宣言します。
//--S3のバケット作成
const nuxt3Bucket = new Bucket(this, "nuxt3Bucket" ,{
//リソース削除時の挙動を制御。スタックを削除するとバケットも削除
//だだしバケットの中身があるときはautoDeleteObjectsがtrue出ないと削除できない
removalPolicy:RemovalPolicy.DESTROY,
autoDeleteObjects:true
});
//--S3へ静的コンテンツをdeployment
new BucketDeployment(this,"nuxt3BucketDeployment",{
//バケットにアップする対象を指定
sources:[Source.asset("../neo-reshipi/.output/public")],
//デプロイ先のバケットを指定
destinationBucket:nuxt3Bucket,
//ログを保持する期間を指定
logRetention: RetentionDays,ONE_MONTH,
});
スタックの作成は引数は第1引数にスコープ、第2引数に任意のID、第3引数にオプションになります。
スタック生成時の第一引数はスコープと呼ばれる「自分が所属する構造体」を指定します。
Binでは〇〇CDKstackがアプリケーションに所属するという関係性を提示していたので【app】と記載しました。
対して、今回はこのクラスのスタック自身を指すので【this】と記載します。
第二引数はIDを記載します(そのオブジェクトの役割を書けばわかりやすい)。
第三引数のオプションは今回awsのcdkライブラリからインポートしたプロパティやメソッドを使って、AWS上のオプションを書いていきます。
デプロイメントのポイント
- バケット作成後に実装(デプロイ先がない)
- sourcesには.output/publicを指定
→Node.jsでコンパイルされた静的コンテンツ(HTML)が入っているのがpublicフォルダ - ファイルパスは相対パスに
S3は以上です。
LambdaにNUXT3のサーバーサイドをデプロイする
Lamdba関数URLというサービスを利用すると、URL一つでlambda関数を呼び出すと他サービスを使用せず起動できます。プログラム
Lamdba関数にNuxt3のNitroのサーバーサイドを担わせ、URLを指定することでServerサイドのプログラムを起動することができます。
AddFunctionUrlメソッドを利用する
//lambda関数の定義
const lambda = new Function(this,"nitro",{
//実行環境を指定
runtime:Runtime.NODEJS_18_X,
//lambda関数としてアップロードする関数のディレクトリを指定
code:Code.fromAsset("../neo-reshipi/.output/server"),
//実行させる関数の名前をファイル名込みで指定
handler:"index.handler",
//タイムアウト時間を指定
timeout:Duration.seconds(5),
//メモリーサイズを指定
memorySize:2048,
//ログの保持を指定
logRetention:RetentionDays.ONE_MONTH,
});
//定義したコンストラクトをaddFunctonUrlでlambda関数URLを定義
//この段階では実際のURLではなく、デプロイ時にURLとし置き換えて解釈されるトークン
const functionUrl = lambda.addFunctionUrl({
//認証を必要としないのでNONEを指定
authType:FunctionUrlAuthType.NONE,
});
//CloudFrontにLambda関数URLを紐づけるための『Httporigin』
//HTTPでアクセス可能なオリジンを紐づけることができるので
//Lambda関数URL以外でも使うことができる
const lambdaOrigin = new HttpOrigin(
//Lambda関数のURLからドメイン部分だけを抽出する
//なので後述するFnメソッドでドメイン部分を抽出する
Fn.select(2,Fn.split("/",functionUrl.url)),
{
//CloudFrontからLambda関数URLsへ渡すカスタムヘッダー
//下記Refereを用いてCloudFront以外のアクセスを制限
customHeaders:{referer},
}
);
このままではS3に配置しただけで配信していないのと、Lambda関数とS3は同じドメインでアクセスしないといけない
そのためCloudFrontを利用して、そこにアクセスした後Lamdba関数か静的コンテンツかを判断してもらう。
CloudFrontにS3とLambdaを紐づけるコード
//----CloudFront------
const distribution = new Distribution(this,"cdn",{
//価格クラス。設定ごとに金と地域が異なる
priceClass:PriceClass.PRICE_CLASS_200,
//デフォルトのビヘイビア。もっとも優先度が低い
defaultBehavior:{
//オリジンにLambda関数URLを紐づけたHttpOriginを指定
origin:lambdaOrigin,
//すべてのHTTPメソッドを許可
allowedMethods:AllowedMethods.ALLOW_ALL,
//オリジンへのリクエスト制御リソース
originRequestPolicy: new OriginRequestPolicy(
this,
"lambdaOriginRequestPolicy",
{
//リクエストヘッダを送らない
headerBehavior:OriginRequestHeaderBehavior.none(),
//クッキーはすべて送る(ダークモード判定で使用している)
cookieBehavior:OriginRequestCookieBehavior.all(),
//クエリパラメータも送る
queryStringBehavior:OriginRequestQueryStringBehavior.all(),
}
),
//キャッシュ制御リソース
cachePolicy:new CachePolicy(this,"lambdaCachePolicy",{
//サーバー処理はキャッシュさせないのでキャッシュに関するすべてのTTLをゼロにする
//TTLとはTime to Liveで保持期間のこと
minTtl:Duration.seconds(0),
defaultTtl:Duration.seconds(0),
maxTtl:Duration.seconds(0),
}),
},
//デフォルト以外のビヘイビア。今回は拡張子のある場合にヒットするバスパターン一つのみ定義
additionalBehaviors:{
//オブジェクトのキーにパスパターンを指定する
//拡張子を含むパスにヒットさせる意図
"/*.*":{
//オリジンにS3バケットのオリジンを指定
origin:nuxt3BucketOrigin,
//静的コンテンツの取得なのでPOST等のHTTPメソッドは不要
allowedMethods:AllowedMethods.ALLOW_GET_HEAD,
//オリジンへのリクエスト制御リソース
originRequestPolicy: new OriginRequestPolicy(
this,
"nuxt3OriginRequestPolicy",
{
//単なる静的コンテンツ取得が目的なので、
//ヘッダやクッキー、クエリパラメータは不要
headerBehavior:OriginRequestHeaderBehavior.none(),
cookieBehavior:OriginRequestCookieBehavior.none(),
queryStringBehavior:OriginRequestQueryStringBehavior.none(),
}
),
//Lambda側とは異なり、S3の静的コンテンツはキャッシュさせる
cachePolicy:new CachePolicy(this,"nuxt3CachePolicy",{
cookieBehavior:OriginRequestCookieBehavior.none(),
minTtl:Duration.seconds(1),
defaultTtl:Duration.seconds(2),
maxTtl:Duration.seconds(3),
}),
},
},
});
コメント