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
を追加します。
module.exports = {
(略)
router: {
base: '/dev/'
},
(略)
}
Nuxt.jsの設定以外にExpressでも設定が必要となります。リクエストのURLにステージ名dev
が含まれないため、強制的に含めてやります。BaseURLはNuxtの設定nuxtConfig.router.base
から取得しています。
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
を削除します。
"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
は任意で変更してください。
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
はご自身の環境に合わせてください。
なんとか動きました^^
はまりポイント
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
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