Help us understand the problem. What is going on with this article?

Nuxt.jsとContentfulでモダンなブログを構築してみた

68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f35383236382f66306462353438342d333037632d653839382d653931382d3534313537626139663336322e6a706567.jpg

Nuxt.jsでイマドキなブログを作ってみました。
https://mycode.rip/

ソースコードは本番運用しているものをGitHubで公開しています。

使った技術は以下の通りです。

  • Contentful(Headless CMS)
  • Nuxt.js(Vue.jsベースのフレームワーク)
  • AWS CloudFront + S3

CSSはMilligram(https://milligram.io)という2KBのCSSフレームワークを使っています。
AWSはTerraformで構築していますので、GitHubのコードをそのまま流用することもできます。

Contentfulとは

https://www.contentful.com/

Contentfulとは、HeadlessなCMS、つまりグラフィカルなUIを提供しないCMSです。
具体的に言うと、コンテンツの管理画面と、そのコンテンツの取得APIのみを提供するものです。

WordPressとは違い、Contentfulはフロント部分(ユーザーが触れる画面)は一切提供していません。
管理画面はContentfulが提供していますが、表側は自分で実装する必要があります。

なぜContentfulを採用するべきなのか

Nuxt.jsを使うならば、CMSを使わずにMarkdown形式で記事を管理すればいいのではと思うかもしれません。
しかし私は、以下の理由からContentfulを採用しました。

  • 5000記事までは無料
  • コンテンツとソースコードのバージョン管理を切り離せる
  • Git環境が無いところからも記事を投稿/編集できる
  • 画像のクロップやリサイズをWeb上で行える
  • 画像のURLに?w=680などのクエリで画像のリサイズを行える(CDNのエッジサーバーでキャッシュされるっぽい)

その他にもWorkSpaceというブランチに似た概念があるため、本番環境への影響を考えずに開発することもできます。
大規模なプロジェクトでWordPressを使っているケースであれば、有効活用できるのではないでしょうか。

またWordPressだとどうしても内部がブラックボックス化してしまいます。
拡張性やスケーラビリティは、今回の構成が圧倒的に優れています。

インフラはCloudFront + S3 + Lambda@Edge

Nuxt.jsを静的ビルドし、それをS3にアップロードしてCloudFront(+Lambda@Edge)で待ち受けている構成です。
記事ページのコンテンツがHTMLとして既に生成されているため、DB接続などの処理を挟まずに素早くレスポンスを返すことができます。

なぜLambda@Edgeを挟む必要があるのか

Nuxt.jsを静的にCloudFrontとS3で配信する場合、Lambda@Edgeを利用する必要があります。

Nuxt.jsの静的ビルド設定では、例えば/my-first-postというページを生成する場合、以下の2通りの方法があります。

  • /my-first-post/index.html
  • /my-first-post.html

しかし、CloudFrontのデフォルトルートオブジェクトにindex.htmlを指定しても、それが有効になるのはトップのみです。
つまりhttps://mycode.rip (/index.html)のみしかキレイなURL表示ができません。

全てのページを.htmlを拡張子無しで表示させるためには、以下の2通りがあります。

  • CloudFrontのオリジンタイプをS3ではなく、カスタムドメインとしてS3の静的ホスティングエンドポイントを利用する
  • /my-first-post/index.html/my-first-postのように拡張子を消してリネームし、content-typeをtext/htmlにする

後者の場合はhtmlファイルをキャッシュしたいとなった場合に拡張子を用いたワイルドカード指定ができなくなります。
前者の場合も、カスタムドメインで指定した場合はS3のURLに直接アクセスできてしまうというデメリットがあります。

こういった観点から、Lambda@Edgeでリクエストファイル名を置き換えてやるのが一番良いと判断しました。
Lambda@edgeのソースコードはGitHubで公開しています。
https://github.com/hareku/mycode.rip-terraform/blob/master/lambda_edge_function/index.js

CodePipelineとWebhookで継続的インテグレーション

CI環境はCodePipelineを用いています。
GitHubへのプッシュを検知してCodeBuildに流し、そこでNuxt.jsの静的ビルドとS3へのアップロードを行っています。

ちなみにContenfulにはwebhookが用意されているため、そこで記事の作成or編集でAPI GatewayにPOSTし、CodePipelineを起動させています。これでわざわざAWSのコンソール画面に行き、CodePipelineのリリースボタンを押す必要はなくなります。

安全なデプロイをどのようにするか

さて、デプロイについてですが、例えば以下のようにするとどうなるでしょう。

buildspec.yml
  post_build:
    commands:
      - aws s3 cp dist s3://mycode.rip/application-tmp --recursive
      - aws s3 rm s3://mycode.rip/application --recursive
      - aws s3 mv s3://mycode.rip/application-tmp s3://mycode.rip/application --recursive

※この例ではCloudFrontのS3オリジンパスをapplicationに設定しています。

  1. 新しくビルドしたコードをapplication-tmpにアップロード
  2. 古いソースコードであるapplicationを全て削除
  3. 新しくアップロードしたapplication-tmpをapplicationにリネーム

このリリースフローだと、rm(2つ目のコマンド)とmv(3つ目のコマンド)を実行中の間、アプリケーションは正常に動作しなくなります。

こういったダウンタイムが生じないよう、デプロイツールをnpmパッケージとして自作しました。
npm: https://www.npmjs.com/package/s3-safety-deploy
github: https://github.com/hareku/node-s3-safety-deploy

コマンドはこのように使用します。
s3-safety-deploy --bucket mycode.rip --uploadDir ./dist --deletePattern *.html

  1. 新しくビルドしたコードをS3にアップロード
  2. 1でアップロードした以外の.htmlファイルを削除し、他の.jsファイルなどにはShouldDeleteタグを付ける
  3. S3のライフサイクルによって、ShouldDeleteタグの付いたものは数日後に削除する

こういったフローであれば、現在閲覧中のユーザーは「jsファイルない!」などのエラーが出ることがありません。また記事を削除した場合も、htmlファイルは削除することができているので404を返すことができます。

みなさんもオリジナルの爆速ブログを作ってみましょう

このように、Nuxt.jsを使えば”イマドキ”なオリジナルブログを簡単に作ることができちゃいます。
よければ今回作成したソースコードを参考に、皆さんも作ってみましょう!

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした