Nuxt.jsのv2系でTypeScriptを利用しつつ、AWS Lambda+αにデプロイできるか確認してみました。
手順
GitHubにいい感じのコードをアップいただけている御方がおられたので、利用させてもらいます。
jeehyukwon/nuxt-serverless: Nuxt.js Serverless SSR Starter on AWS (Lambda + API Gateway + S3) with Serverless Framework
https://github.com/jeehyukwon/nuxt-serverless
静的ファイルはS3、AWS LambdaのNode.jsでExpressを立ち上げて、SSRをして、API Gatewayがエンドポイント、ドメインはカスタムドメインを利用する構成です。
カスタムドメインを利用する前提でしたので、それを外してデプロイするようにしてみます。
Githubに上記リポジトリをフォークしたものがありますので、よければご参考ください。
https://github.com/kai-kou/nuxt-serverless
ソースの取得と環境構築
ローカル環境で構築します。
node
とnpm
がインストールされている前提です。
> node -v v10.11.0 > npm -v 6.4.1
> git clone https://github.com/jeehyukwon/nuxt-serverless.git > cd nuxt-serverless
コンテナ内
> npm install
動作確認
ローカルで動作するか確認します。
> npm run dev (略) ✔ success Builder initialized ✔ success Nuxt files generated READY Listening on http://localhost:3000
ブラウザでアクセスします。
http://localhost:3000
はい。
ソースを確認するとSSRしてますね。
デプロイ準備
API Gatewayのステージ対応
AWS Lambdaへデプロイする場合、HTTPアクセスにはAWS API Gatewayを利用することになります。
カスタムドメインを利用しない場合、API GatewayのURLはhttps://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/
のように、ステージ名dev
が含まれます。
Nuxt.jsでこのステージ名をBaseURL
として設定してやる必要があります。
詳細は下記記事で詳しく解説されています。感謝!
Nuxt.js on AWS Lambda with Serverless Framework – mya-ake com
https://mya-ake.com/posts/nuxtjs-on-aws-lambda/
nuxt.config.js
にrouter.base
を追加します。
nuxt.config.js(一部抜粋)
module.exports = { (略) router: { base: '/dev/' }, (略) }
Nuxt.jsの設定以外にExpressでも設定が必要となります。リクエストのURLにステージ名dev
が含まれないため、強制的に含めてやります。BaseURLはNuxtの設定nuxtConfig.router.base
から取得しています。
handler.js
app.use((req, res) => ( setTimeout(() => { req.url = `${nuxtConfig.router.base}${req.url}`.replace('//', '/') nuxt.render(req, res)} , 0) ))
カスタムドメインを利用しない設定
カスタムドメインを利用しないので、package.json
のscripts
に指定されているsls create_domain
とsls delete_domain
を削除します。
package.json(一部抜粋)
"scripts": { "dev": "nuxt", "build": "cross-env NODE_ENV=production nuxt build", "start": "nuxt start", "sls:local": "sls offline", "sls:create": "npm run build && sls deploy", "sls:delete": "sls remove", "sls:deploy": "npm run build && sls deploy" },
serverless.yml
でもカスタムドメイン作成の定義を削除します。(ここではコメントアウトしています。)service
やregion
、BUCKET_NAMESPACE
は任意で変更してください。
serverless.yml
service: nuxt-serverless # 1. Edit whole service name provider: name: aws runtime: nodejs8.10 stage: ${opt:stage, 'dev'} region: ap-northeast-1 # 2. Edit AWS region name environment: NODE_ENV: production BUCKET_NAMESPACE: nuxt-serverless # 3. Specify a new AWS S3 bucket namespace for bundled assets and static assets (should be unique) ASSETS_BUCKET_NAME: ${self:provider.environment.BUCKET_NAMESPACE}-assets-${opt:stage, 'dev'} STATIC_BUCKET_NAME: ${self:provider.environment.BUCKET_NAMESPACE}-static-${opt:stage, 'dev'} ASSETS_BUCKET_URL: https://s3.${self:provider.region}.amazonaws.com/${self:provider.environment.ASSETS_BUCKET_NAME} STATIC_BUCKET_URL: https://s3.${self:provider.region}.amazonaws.com/${self:provider.environment.STATIC_BUCKET_NAME} custom: # customDomain: # domainName: service.mydomain.io # 4. Specify a new domain name to be created # stage: ${opt:stage, 'dev'} # certificateName: mydomain.io # 5. Enter the certificate name in AWS Certificate Manager (us-east-1) for https connection # createRoute53Record: true serverless-offline: port: 4000 s3Sync: - bucketName: ${self:provider.environment.ASSETS_BUCKET_NAME} localDir: .nuxt/dist/client - bucketName: ${self:provider.environment.STATIC_BUCKET_NAME} localDir: static package: exclude: - src/** include: - serverless.yml functions: nuxt-renderer: handler: handler.render memorySize: 512 timeout: 30 events: - http: path: / method: ANY cors: true - http: path: /{proxy+} method: ANY cors: true resources: Resources: AssetsBucket: Type: AWS::S3::Bucket Properties: BucketName: ${self:provider.environment.ASSETS_BUCKET_NAME} AssetsBucketPolicy: Type: AWS::S3::BucketPolicy Properties: Bucket: Ref: AssetsBucket PolicyDocument: Version: "2012-10-17" Statement: [ { Action: ['s3:GetObject'], Effect: 'Allow', Resource: { Fn::Join: ['', ['arn:aws:s3:::', { Ref: 'AssetsBucket' }, '/*']], }, Principal: '*' }, ] StaticBucket: Type: AWS::S3::Bucket Properties: BucketName: ${self:provider.environment.STATIC_BUCKET_NAME} StaticBucketPolicy: Type: AWS::S3::BucketPolicy Properties: Bucket: Ref: StaticBucket PolicyDocument: Version: "2012-10-17" Statement: [ { Action: ['s3:GetObject'], Effect: 'Allow', Resource: { Fn::Join: ['', ['arn:aws:s3:::', { Ref: 'StaticBucket' }, '/*']], }, Principal: '*' }, ] plugins: - serverless-offline - serverless-s3-sync # - serverless-domain-manager
デプロイする
npm run sls:create
またはnpm run sls:deploy
することで、Nuxt.jsのビルドと、AWSへのデプロイがされます。
> npm run sls:create (略) Entrypoint app = 55e8f003a83a598dd113.js 5e45cc8df25adbaac94b.js 1d1bc122ddb9c7b7f271.css 6a943e90e669a1de7258.js [13:27:00] Compiling server [13:27:04] Compiled server in 4s Hash: 2c7c34f6c1ea4aefd63d Version: webpack 4.25.1 Time: 3924ms Built at: 2018-11-13 13:27:04 Asset Size Chunks Chunk Names server-bundle.json 213 KiB [emitted] Entrypoint app = server-bundle.js ✨ Done in 114.07s. (略) Serverless: Stack update finished... Service Information service: nuxt-serverless stage: dev region: ap-northeast-1 stack: nuxt-serverless-dev api keys: None endpoints: ANY - https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/ ANY - https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/{proxy+} functions: nuxt-renderer: nuxt-serverless-dev-nuxt-renderer S3 Sync: Syncing directories and S3 prefixes... .... S3 Sync: Synced.
デプロイができたら、ブラウザでアクセスしてみます。xxxxxxxxxx
はご自身の環境に合わせてください。
https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/
なんとか動きました^^
はまりポイント
nuxt dev
でホスト名に0.0.0.0
が指定できない
当初Dockerコンテナ内で実行させようとしたのですが、ホスト名に0.0.0.0
が指定できなかったので、ローカル環境で構築しました。
Host and Port – Nuxt.js
https://nuxtjs.org/faq/host-port
0.0.0.0
を指定すると、ランダムっぽいIPが振られてしまいます。。。
環境変数HOST
もNUXT_HOST
も package.json
で指定も --hostname
もだめ。むむぅ。なんでしょう。。。
ドキュメントには
Note: If port is assigned the string value of ‘0’ (not 0, which is falsy), a random port will be assigned to your Nuxt application.
とありますので、ポートはわかるのですが、ホスト名でも同じような挙動です。。。ローカルでもDockerコンテナ内でも同じ挙動でした。
ローカル
INFO Building project ✔ success Builder initialized ✔ success Nuxt files generated READY Listening on http://192.168.34.25:3001
Dockerコンテナ
INFO Building project ✔ success Builder initialized ✔ success Nuxt files generated READY Listening on http://172.28.0.2:3001
API Gatewayのパスが面倒
Nuxt.jsとExpressと別でBaseURL
の設定が必要だったのが非常にわかりにくかったです。今回Serverless-httpを利用していたのですが、いくつかIssueが上がっていました。どうにかならないものでしょうか^^
Correct path for non-custom endpoints by bsdkurt · Pull Request #42 · dougmoscrop/serverless-http
https://github.com/dougmoscrop/serverless-http/pull/42
Include baseUrl in request object · Issue #35 · dougmoscrop/serverless-http
https://github.com/dougmoscrop/serverless-http/issues/35
まとめ
ローカル環境の構築がさくっと終わったのでAWS Lambdaにもさくっと構築できるだろうと思って取り組んだら、思いの外ハマって時間がかかりました。Nuxt.jsもv2がリリースされたこともあり、かつTypeScriptを利用だったので、正しい情報・設定を見極めるのに試行錯誤することになりました。
デプロイできることは確認できましたので、ひとまずは
やったぜ^^
参考
jeehyukwon/nuxt-serverless: Nuxt.js Serverless SSR Starter on AWS (Lambda + API Gateway + S3) with Serverless Framework
https://github.com/jeehyukwon/nuxt-serverless
Nuxt.js on AWS Lambda with Serverless Framework – mya-ake com
https://mya-ake.com/posts/nuxtjs-on-aws-lambda/
Host and Port – Nuxt.js
https://nuxtjs.org/faq/host-port
Correct path for non-custom endpoints by bsdkurt · Pull Request #42 · dougmoscrop/serverless-http
https://github.com/dougmoscrop/serverless-http/pull/42
Include baseUrl in request object · Issue #35 · dougmoscrop/serverless-http
https://github.com/dougmoscrop/serverless-http/issues/35