Edited at

Nuxt.js(v2.2.0)+TypeScriptなアプリをAWS Lambda+αにデプロイしてみた

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


ソースの取得と環境構築

ローカル環境で構築します。

nodenpm がインストールされている前提です。

> 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

スクリーンショット 2018-11-13 11.07.17.png

スクリーンショット 2018-11-13 11.07.37.png

はい。

ソースを確認すると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.jsrouter.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.jsonscripts に指定されているsls create_domainsls 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 でもカスタムドメイン作成の定義を削除します。(ここではコメントアウトしています。)serviceregionBUCKET_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/

Hello__Nuxt_Serverless_Template.png

Hello__TypeScript.png

view-source_https___vvb2o02739_execute-api_ap-northeast-1_amazonaws_com_dev_.png

なんとか動きました^^


はまりポイント


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が振られてしまいます。。。

環境変数HOSTNUXT_HOSTpackage.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