はてなブログをHugo+AWSに移行しました

長らくはてなブログで運営していた当ブログですが、この度AWSにHugoで移行しました。

動機

色々考えるとこはあるんですが、つまるところ以下の要因。

  • はてなブログがHTTP2Sに未対応
  • はてなで独自ドメインかつHTTPS、さらにHTTP2化できる未来が直近で見えなかった
  • マネージドだとそもそも自由度に限界がある。色々自由にやりたくなった

お品書き

手順としては以下の通り。

  • はてなブログからブログデータをエクスポート
  • はてなブログデータをHugo用に変換
  • HugoをCIでビルドし、Amazon S3にアップロード
  • S3のStatic Web HostをCloudFrontで配信
  • AWS Certificate Managerで証明書を作成
  • CloudFrontを設定
  • 旧ブログのURLから新ブログのURLにリダイレクトできるようにする
  • Route53でDNS変更

はてなブログからブログデータをエクスポート

image

はてなブログデータをHugo用に変換する

移行なので、当然既存記事をHugo用に変換する必要がある。というわけでよしなにツールを書いてみた。

はてなブログはMovable Type形式なので、これをHugoのマークダウンに変換する。MTを単純にコンバートするツールは他にもありそうだが、単純に変換するだけだとはてなキーワードのリンクが残ってしまうのでリンクを排除したり。はてなフォトライフにアップロードしてある画像を引き続き使うことになってしまう(これはCDN泥棒になってしまう)。

hateblo2hugoでは、はてなキーワードのリンクを除去したりしていたり、はてなフォトライフの画像をダウンロードしてきてsrcを貼り直す等、はてなから移行に必要そうなことをやってる。とりあえず自分のブログで使ってる機能だけ実装したので、ツールとしてはまだ不備があるかもしれない。

$ hateblo2hugo migrate -i ~/your_path/your_hatenablog.export.txt -o ~/your_path/your_hugo_blog/blog/

このように、入力にはブログデータファイル、出力先にhugoのディレクトリを指定すればよしなに移行ができる。

HugoをCIでビルドし、Amazon S3にアップロード

今回、Hugo化したブログのホスト先にはAWSを選んだ。GitHub Pagesに置くのが一番(金銭的)コストのかからない方法であるが、多少はお金をかけてもいいなという気になったのでより自由なAWSに置くことにした。

ストレージはもちろんAmazon S3を選択。ブログ記事はGitHubにホストしておいて、CircleCIと連携し、masterにpush(またはマージ)された際にビルドして、成果物をS3にあげるようにしている。

抜粋だがcircle.ymlはこのようになる。ownerとバケット名は適当に各自に合わせて欲しい。

dependencies:
  pre:
    - go get -v github.com/gohugoio/hugo 
  override:
    - hugo -t casper:
        pwd: blog

test:
  override:
    - echo noops

deployment:
  master:
    branch: master
    owner: stormcat24 
    commands:
      - aws s3 sync blog/public s3://your_hugo_bucket/

S3のStatic Web HostをCloudFrontで配信

S3をOriginとしてCloudFrontで配信する。CloudFrontで配信すればHTTPSとHTTP2の恩恵をうけることができる。

まずはOriginとなるバケットにStatic website hostingの設定を施す。

image

次にバケットポリシーを設定する。

{
    "Version": "2008-10-17",
    "Id": "PolicyPrivate",
    "Statement": [
        {
            "Sid": "1",
            "Effect": "Allow",
            "Principal": {
                "AWS": "*"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::your_hugo_bucket/*"
        }
    ]
}

通常CloudFrontと連携する場合は、Static website hostingをせずにS3のバケットにCloudFrontからしかアクセスできないようにする制限を入れる方法が常套手段ではあるが、この場合はターゲットのディレクトリにスラッシュ終わりやindex.htmlでアクセスしたときのリライトが行われないという罠がある。この罠を回避するためにStatic web hostingにしているが、第三者からoriginへのアクセスも可能になるので s3:GetObject 以外の権限を与えないことが重要にになる。

AWS Certificate Managerで証明書を作成

CloudFrontに独自ドメインを当てて配信する前に、証明書を用意しておく必要がある(CloudFrontでDistributionを作る際に一緒に作ることも可能だが)。

そこでAWS Certificate Manager(ACM)を使って証明書を発行する。ACMを使って作成した証明書は発行が無料というコストメリットがある。CloudFrontやELBといったマネージドにしか適用できないという制限はあるが、今回の用途であれば使わない手はない。

独自ドメインの取得・Route53へのNSの設定を済ましていれば、証明書をリクエスト→アクティベーションの流れで即時に取得することが可能。

image

注意点としては証明書を発行するリージョンが重要で、CloudFrontで利用できるACMの証明書は現在はバージニア北部リージョンに限定されている。以下のドキュメントを参照してほしい。

Amazon CloudFront の使用を開始する

CloudFrontを設定

CloudFrontでブログを配信するためのディストリビューションを作成する。スクショで貼るとマスクする項目が多すぎるので省略するが、以下のポイントを抑えておくと良い。

設定のポイントは以下のところ。

  • Origin Domain Name: your_hugo_bucket.s3-website-ap-northeast-1.amazonaws.com
  • Supported HTTP Versions: HTTP/2, HTTP/1.1, HTTP/1.0
  • Viewer Protocol Policy: Redirect HTTP to HTTPS

Origin Domain Nameはバケット名ではなく、Static web hostingのドメインを設定する。証明書設定は、ACMの証明書がプルダウンで候補に出てくるので選択する。

旧ブログのURLから新ブログのURLにリダイレクトできるようにする

できれば旧ブログのリンクを踏んでも、ちゃんと新ブログにリダイレクトされるようにしたい。HTTP -> HTTPSへのリダイレクトであればCloudFrontだけの設定(Redirect HTTP to HTTPS)で可能だが、そもそもはてなブログとhugoでパスが違うという問題がある。

hatena: http://blog.stormcat.io/entry/protogen
hugo: https://blog.stormcat.io/post/entry/protogen/

hugoは頭に/postがついていて、は末尾のスラッシュが無い。hugoの方のURLはもっと工夫できるかもしれないが、今回はそこまで突っ込んで見なかった。

この問題を吸収するために、リダイレクト用のS3バケットを新規に用意した。バケットの中身は空で良く、Static web hostingとRedirect Ruleを設定すればよい。Redirect Ruleは次のようにした。

<RoutingRules>
  <RoutingRule>
    <Condition>
      <KeyPrefixEquals>entry/</KeyPrefixEquals>
    </Condition>
    <Redirect>
      <HostName>your_blog_domain</HostName>
      <ReplaceKeyPrefixWith>post/entry/</ReplaceKeyPrefixWith>
      <HttpRedirectCode>301</HttpRedirectCode>
    </Redirect>
  </RoutingRule>
  <RoutingRule>
    <Condition>
      <KeyPrefixEquals>feed</KeyPrefixEquals>
    </Condition>
    <Redirect>
      <HostName>your_blog_domain</HostName>
      <ReplaceKeyWith>index.xml</ReplaceKeyWith>
      <HttpRedirectCode>301</HttpRedirectCode>
    </Redirect>
  </RoutingRule>
  <RoutingRule>
    <Condition>
      <HttpErrorCodeReturnedEquals>404</HttpErrorCodeReturnedEquals>
    </Condition>
    <Redirect>
      <HostName>your_blog_domain</HostName>
      <ReplaceKeyWith>/</ReplaceKeyWith>
      <HttpRedirectCode>301</HttpRedirectCode>
    </Redirect>
  </RoutingRule>
</RoutingRules>
  • 1つめ: /entry が来たら /post/entry にリダイレクト
  • 2つめ: /feed が来たら index.xml にリダイレクト(atomフィードのリダイレクト)
  • 3つめ: 404対策

といったところ。ここまでできれば、作成したCloudFrontのディストリビューションからパスベースでOriginの振り分けを行えば良い。これはBehaviorsの設定で行う。

Precedence Path Pattern Origin Viewer Protocol Policy
0 /entry/* リダイレクト用バケットのStatic web hosting HTTP and HTTPS
1 /feed リダイレクト用バケットのStatic web hosting HTTP and HTTPS
2 Default (*) hugo用バケットのStatic web hosting Redirect HTTP to HTTPS

この様になっていれば、リダイレクトを含めたCloudFrontの設定は大丈夫。

Route53でDNS変更

最後はRoute53で、今までブログのドメインをはてなブログにCNAMEしていたのを変更する。

  • はてなブログ管理画面で、独自ドメインの設定を止める
  • Route53でブログのDNSをCloudFrontのドメインに変更(CNAMEじゃなくてAliasレコードで良い)

これで諸々の作業が完了で目的は達成された。

はてなブックマークは失う

めでたくHTTPS+HTTP2対応な自由度の高いブログを手に入れることができた。しかし、覚悟していたことではあるがURLが変わったことによりこれまで獲得した数千のはてブを失った。

これはもう新たに自分のメディアを作っていく気概でもう一度ゼロスタートでやっていくつもり。

というわけで今度とも当ブログを宜しくおねがいします。フィード購読は右上から💪