GKEからAWSにブログとNextcloudを移行した

あけおめです、2022年ですね。今年は仕事よりもプライベートを大事にしたいですね

前回、GKEからAWSに移行する計画を立ててみたものの、色々なことで詰まりまくりながら移行が終わったので気づいたことを残しておきます

CNAMEで新しいドメインに転送させるとHTTPS接続ができなくなった

現在のブログのドメインはblog.morifuji-is.ninjaで、ブログの新しいドメインはblog.aws.morifuji-is.ninjaにする予定でした(route53で管理するホストゾーンはaws.morifuji-is.ninjaのみにしたいので)

ただし、ブログの旧ドメインを変えるとseo的にリセットされるので旧ドメインにアクセスされた場合にはCNAMEを使って新ドメインの情報を転送させることで旧ドメインと新ドメインの両方が利用できると考えていました。 また、SSL証明書はAWSのACMで新ドメインとして取得したものを利用するよう設定していました。

この構成でやってみたところ、旧ドメインにブラウザでアクセスするとSSLに関するエラーが発生しました😭 よく考えたらそうなんですが、ブラウザが旧ドメインにアクセスする際、CNAMEで転送しても、ブラウザ側としてはURLは旧ドメインのままであり、その状態でSSL証明書を取得したら新ドメインの証明書が取得されるのでドメインが一致しません。 なのでブラウザ側がエラーを吐いていたということでした。

webサイトのドメイン移管時ってCNAMEを使えば簡単にいけるやん!と思ってましたけどそう簡単ではないようですね

結局、route53に旧ドメインのホストゾーンを作成し新ドメインは一切使わないように変更しました。(Route53のホストゾーンそんなに高くなかったので助かりました)

S3+CloudFrontでtrailing slashが解釈されなかった

ブログは、S3にブログのコンテンツを配置しその前段にcloudfrontを噛ませる構成を取りました。CDKでデプロイ後にブログにアクセスしてみるものの、cloudfrontからファイルが存在しないという旨のエラーが吐かれてしまいました😭 ブログ内のリンクは全て/aaaaa/というフォーマットで出力されるのですが、これがS3バケットの/aaaaa/index.htmlを探し当てられないということみたいでした。本来であればS3のwebsite hosting機能を利用すればこの問題は発生しないはずです、S3にwebsite hostingの設定を入れているにもかかわらずなんでそうなるんだろう…と思ってましたけどどうやらCDKの設定が一部間違っていたみたいでした😭

CDKでは以下のようにCloudfrontのオリジンにはs3OriginSourceを利用していたのですが、これではS3のwebsite hosting機能は利用されずに生のS3バケットのデータを見に行くようです。 公式のcdkのサンプルを参考にしたのですがそれを鵜呑みにしたのが良くなかったですね…

https://github.com/aws-samples/aws-cdk-examples/blob/master/typescript/static-site/static-site.ts

 const distribution = new cloudfront.CloudFrontWebDistribution(this, `${id}-'SiteDistribution`, {
      viewerCertificate,
      originConfigs: [
        {
          s3OriginSource: {  // here
            s3BucketSource: blogBucket,
            originAccessIdentity: cloudfrontOAI
          },
          behaviors: [{
            isDefaultBehavior: true,
            compress: true,
            allowedMethods: cloudfront.CloudFrontAllowedMethods.GET_HEAD_OPTIONS,
          }],
        }
      ],
      viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS
    });

正しくは、以下のようにoriginConfigsを利用してS3のbucketWebsiteDomainNameをドメインとして使うべきでした。また、originProtocolPolicyもデフォルトだとHTTPS_ONLYになっていてS3にアクセスできない(S3のwebsite機能ではHTTPSは提供されない)のでHTTP_ONLYに変更しなければなりません。Redditで回答してくれたndjstaサンに感謝ですね

https://www.reddit.com/r/aws/comments/bg46g5/cloudfront_s3_returning_404_when_no_trailing_slash/elj3d0r/

    const distribution = new cloudfront.CloudFrontWebDistribution(this, `${id}-'SiteDistribution`, {
      viewerCertificate,
      originConfigs: [
        {
          customOriginSource: {
            domainName: blogBucket.bucketWebsiteDomainName, 
            originProtocolPolicy: cloudfront.OriginProtocolPolicy.HTTP_ONLY,
          },
          behaviors: [{
            isDefaultBehavior: true,
            compress: true,
            allowedMethods: cloudfront.CloudFrontAllowedMethods.GET_HEAD_OPTIONS,
          }],
        },
      ],
      viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS
    });

当初はこの解決方法がわからなく、CloudFrontFunctionを設定して自前でURLのパスを加工していました。これでも動くことは動くんですが無駄な設定ですしCloudFrontFunctionの実行にお金もかかってしまいますのでお勧めできません。CDKではCloudFrontFunctionがLambdaと同程度簡単に定義できることが分かったことは幸いです

// CloudFrontFunction
function handler(event) {
    var request = event.request;
    var uri = request.uri;

    // Check whether the URI is missing a file name.
    if (uri.endsWith('/')) {
        request.uri += 'index.html';
    }
    return request;
}

NextcloudをLightsailのコンテナを使ってデプロイしようとしたが、バックアップができないため使えなかった

Nextcloudは、大量のファイルをローカルに保存して動作しているため、定期的なバックアップが必要でした。AWSのLightsailにはバックアップ機能があるので利用する予定だったのですが、Lightsailのインスタンスの場合と異なりコンテナの場合にはバックアップ機能がないということが判明したため、コンテナを使うことをなくなく諦めました😭

EBSのようなストレージ機能がlightsailに存在し、そちらにバックアップ機能があったため、NextCloudのコンテナにストレージをアタッチできればバックアップ可能だと考えて方向転換してみたものの、調べるとコンテナにストレージはアタッチできませんでした。

結局、そりゃコンテナなんだからステートレスで運用しろよ、ということだと思うので、大人しくインスタンスにデプロイするように変更しました

NextCloudのURLをCloudFrontが解釈できずHTTPS化できなかった

NextCloudを動かしているlightsailのインスタンスへHTTPS接続でアクセスできるようにするには、lightsailのALBかDistribution(=CloudFront)のどちらかを利用する必要がありました。ALBは固定で結構な料金が発生するのでDistributionを選択したのですが、ここでもハマりました😭…

インスタンスにnextcloudを入れて(結局dockerを入れて起動させた)、ブラウザからアクセスするとうまくHTTPS接続できていたのですが、なぜかログインしても何もファイルが表示されない…

データの移行に失敗した!?と思って元データとlightsailインスタンスの移行後データのファイルサイズを確認しましたがほぼ一致してたのでそれが原因ではなかったです

原因は、Nextcloudが画面でファイルを表示する際には /remote.php/dav/files/aaaaa/bbbb.pdfのようなリクエストがajaxによって送信されるんですが、そのリクエストが400系のエラーでレスポンスされているためでした 色々試行錯誤してみたものの明確な原因はわからずでしたが、推測ですが~~/remote.php/~~のようにURLのパスの中に拡張子が存在するとcloudfrontが何か誤作動を起こすのかもしれません。

結局HTTPS接続は諦めてIPベースのHTTP接続でアクセスするようにしました。個人で閲覧するぐらいでヤバいデータはないので別にいいかと考えてます(よくない)

まとめ

  • AWSのことまだまだ知らないことに気づいた
  • 個人で触る程度の規模であれば、CDKは変にスタックやコンストラクトを分割しないほうが良いことが多いことに気づいた
    • 分割されてると見通し悪くなる
  • OrganizationとCDKを使うと、最悪リソースを全て吹っ飛ばせるのでとても気が楽

See also