いっしきまさひこBLOG

AI・機械学習関連、Web制作関連、プログラミング関連、旅行記録などなど。一色政彦。

CMSサイト(動的)→S3(静的)移行を自動化! Pythonスクリプト公開中

「CMSで管理されている動的なWebサイトを静的サイトに移行したい」と考えている人向けの記事です。CMSでサイトを頻繁に更新する必要がなくなった場合や、運用コストを削減したい場合などで、静的サイトへの移行は大きなメリットをもたらします。以下では、私が実践した手法と、そのために作成したPythonスクリプトを紹介します。

課題と解決策

CMSを利用した動的サイトには以下の課題があります:

  • サーバーコストが高い
  • CMSのメンテナンスが手間

これらを解決するため、私はCMSサイトを静的化し、Amazon S3+CloudFrontでホスティングする方法を選びました。この作業を効率化するため、自作のPythonスクリプトを開発しました。

このスクリプトは、以下の機能を実現します:

  • 静的化:CMSからローカル環境にHTMLファイルを取得
  • リンク修正(オプション):内部リンクの変換や、一部HTML内容の変更
  • ホスティング設定:S3にアップロード

このあと、CloudFrontでHTTPS対応のホスティングを設定する作業は説明資料に従って進めてください。

スクリプトはGitHubで公開しています:

本スクリプトおよび資料は、現状のまま(AS IS)で提供されており、いかなる保証も行いません。本スクリプトの使用により発生したいかなる損害や問題についても、作成者は一切の責任を負いかねます。ご利用は自己責任でお願いいたします。

説明資料

詳細な手順はREADME.mdに記載しています。以下にもその内容を転載しておきます。


Dynamic Web to S3 Migration

このプロジェクトは、CMS管理のWebサイトからファイルをダウンロードし、Amazon S3にアップロードするためのPythonスクリプトを提供します。

また、アップロードした後に、Amazon S3をスタティック(静的)なWebサイトホスティングサービスとして構成し、Amazon CloudFrontによりCDNで配信されるようにし、AWS Certificate Manager(ACM)により生成し自動更新されるHTTPS用のSSL証明書を設定するまでの作業手順も、このREADME.mdファイルに掲載しています。

ただし、スクリプトはWindows環境で実行しているため、それ以外の環境での動作は保証できません。細かい調整が必要な場合は、スクリプトを修正することで対応してください。

※本スクリプトの実行は自己責任で行ってください。いかなる責任も負いません。また、本スクリプトにPR(プルリクエスト)やIssue(問題報告)を送られても、一切対応しませんので、あしからずご了承ください。

構成

  • requirements.txt: 必要なPythonパッケージのリスト。
  • config.ini: 認証情報を含む設定ファイル。
  • WebHtmlDownload.py: CMS管理のWebサイトから全てのHTMLファイルをダウンロードします。
  • AwsS3Upload.py: ダウンロードされたファイルをAmazon S3にアップロードします。

ログファイル

  • <トップのローカルフォルダ名>_download_log.txt: Webサイトからのダウンロードに関するログ。
  • <トップのローカルフォルダ名>_processed_log.txt: 処理済みのURLに関するログ。
  • <トップのローカルフォルダ名>_skipped_log.txt: スキップしたURLに関するログ。
  • <トップのローカルフォルダ名>_download_retry.txt: ダウンロードに失敗したURLに関するリトライ用のログ。
  • <S3名>_upload_log.txt: Amazon S3へのアップロードに関するログ。
  • <S3名>_compare_log.txt: WebサイトとAmazon S3の内容比較に関するログ。

<トップのローカルフォルダ名>(Webサイト管理用の名前)は、S3バケット名と同じ名前にします。例:wwwexamplecom

リトライ機能

※本プロジェクト開発者が試した限りでは、失敗が発生したことがないため、リトライ機能は実装済みですが、正常に稼働するかは検証されていません。

  • ダウンロードやアップロードに失敗したファイルは、リトライ用のファイルに記録されます。
  • 次回実行時にリトライ用のファイルを読み込み、前回失敗した処理を再試行します。成功した処理は再試行しません。
  • 全てのファイルが成功した場合、リトライ用のファイルは削除されます。

前提条件

CMS管理のWebサイトをWebサーバとして活用してダイナミックなWebページを配信していることを前提としています(※ただし、未検証ですがスタティックWebサイトでも有効ななずです)。また、Azure CDNなどでCDNとSSLの自動更新を対応している場合もカバーしています。

同名のAmazon S3バケットを以下の手順で作成しておいてください。

S3バケットを作る手順

作成時に以下の内容を指定することを前提とします。

  1. AWS リージョンは「アジアパシフィック (東京) ap-northeast-1」とします。
  2. AWSコンソールでS3の[汎用バケット]を開き、[バケットを作成]をクリックします。
  3. [バケットタイプ]は「汎用」を選択します。
  4. [バケット名]は<トップのローカルフォルダ名>(Webサイト管理用の名前)と同じ名前にします。例:wwwexamplecom
  5. [ACL無効]を選択してください。
  6. [パブリックアクセスをすべてブロック]のチェックを外してください。
  7. [バケットのバージョニング]は「有効にする」にしてください。
  8. [タグ - オプション]でキーは「Name」、値は「<任意のプロジェクト名(例:masahiko.info)>」でタグを追加してください。
  9. 他はデフォルトのまま[バケットを作成]をクリックしてください。
  10. これでS3バケットの作成は完了です。

必要なPythonパッケージ

以下のコマンドを使用して、必要なパッケージをインストールしてください。

pip install -r requirements.txt

認証情報の設定

AWSのアクセスキーの取得

AWSのアクセスキーはユーザーごとに取得します。セキュリティの問題もあるので、全ての作業が終わったら無効化もしくは削除するのがお勧めです。

  1. AWS Management Consoleにログインし、IAMサービスに移動します。
  2. 左側のメニューから[ユーザー]を選択します。
  3. 該当のユーザー名をクリックします。
  4. 上部の[セキュリティ認証情報]タブを開きます。
  5. [アクセスキーを作成]をクリックします。
  6. [ユースケース]は「ローカルコード」を選択します。
  7. [説明タグ値]は空のままで[アクセスキーを作成]をクリックします。
  8. これにより、アクセスキーIDとシークレットキーが発行されるので、確実にどこかに記述してください。

config.iniの編集

ご自身の環境に合わせて修正してください。

  • domainName: CMS管理Webサイトのドメイン名を指定します。例:www.example.com
  • sitemapUrlPath: Web検索用サイトマップへのルートからのパスを指定します。例:/sitemap.xml
  • topLocalName: トップのローカルフォルダ名(Webサイト管理用の名前)を指定します。S3バケット名と同じ名前にしてください。例:wwwexamplecom
  • indexFileName: インデックスドキュメント名(デフォルトのルートオブジェクト)を設定します。例:index.html
  • S3BucketName: 比較対象のS3バケット名を設定します。
  • AccessKeyId: AWSアクセスキーIDを設定します。
  • SecretAccessKey: AWSシークレットアクセスキーを設定します。
  • Region: AWSリージョンを設定します。

使用方法

WebHtmlDownload.pyの使用方法

(1) WebHtmlDownload.pyを実行して、CMS管理のWebサイトから全てのHTMLファイルをダウンロードします。

python WebHtmlDownload.py

※古い形式のサイトマップのスキーマ「http://www.google.com/schemas/sitemap/0.84」にしか対応していません。これ以外の場合、コードを修正する必要があります。

(2) ダウンロードされたファイルは、指定されたローカルディレクトリに保存されます。

AwsS3Upload.pyの使用方法

(1) AwsS3Upload.pyを実行して、ダウンロードされたファイルをAmazon S3にアップロードします。

python AwsS3Upload.py

(2) アップロードの成功または失敗はログファイルに記録されます。

全て成功すれば作業は完了です。

さらに、Favicon(Webサイト用のアイコン)もある場合は、適切な場所(例えば/favicon.ico)にAmazon S3のサイトでアップロードしてください。

S3バケットをスタティックなWebサイトホスティングサービスにするまでの手順

注意事項

  • パブリックアクセスを有効化する際はセキュリティに注意してください。不要なデータが公開されないように。
  • 公開したくないファイルはそもそもアップロードしない方がよいでしょう。このスクリプトではもともとWebサイトでパブリックに公開されていたオブジェクト(主にHTMLページ)のみを移行する仕様になっていますので、基本的には新たに不要なデータが公開されることはないはずです。

手順 1: 静的Webサイトホスティングを有効化

  1. AWSコンソールで対象のS3バケットを選択します。
  2. 上部メニューの[プロパティ]をクリックしてください。
  3. [静的Webサイトホスティング]セクションの[編集]をクリック。
  4. 「静的Webサイトホスティング」の「有効にする」を選択します。
  5. [ホスティングタイプ]jは、「静的ウェブサイトをホストする」を選択します。
  6. [インデックスドキュメント](例: index.htmldefault.html)を入力。
    • 必要なら[エラードキュメント](例: error.html)も入力。このファイルは基本的にバケットのルート(トップレベル)に配置してください。
  7. [変更の保存]をクリックします。

サンプルのerror.htmlファイルをこのプロジェクトに含めていますので、これをS3バケットのトップレベルにアップロードしてみてください。

手順 2: ブロックパブリックアクセスをオフにする

この作業は、作成時にオフにしていれば不要です。

  1. 上部メニューの[アクセス許可]をクリック。
  2. [ブロックパブリックアクセス (バケット設定)]セクションの[編集]をクリック。
  3. すべてのチェックボックスをオフにします。
  4. 保存します。
  5. 注意: この設定変更により、バケットがパブリックにアクセス可能になるリスクがあります。次のポリシー設定を適切に行いましょう。

手順 3: バケットポリシーを設定

S3バケットをパブリックにアクセス可能にするため、バケットポリシーを記載します。

以下は、特定のS3バケット(例: example-bucket)内のすべてのオブジェクトをパブリックにするポリシーです。arn:aws:s3:::example-bucketの部分は「バケット ARN」と呼ばれますが、編集画面上でコピーできます。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "PublicReadGetObject",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::example-bucket/*"
    }
  ]
}
  1. 引き続き[アクセス許可]にある[バケットポリシー]セクションの[編集]をクリック。
  2. 上記のJSONコードを貼り付けます(example-bucketを実際のバケット名に変更)。
  3. 保存します。

手順 4: オブジェクト所有者を確認

S3バケットの[アクセス許可]で[オブジェクト所有者]が「バケット所有者の強制」となっている場合、ACL(アクセスコントロールリスト)は無効になっています。つまり、このバケット内のすべてのオブジェクトは、このアカウントによって所有されます。このバケットとそのオブジェクトへのアクセスは、ポリシーのみを使用して指定されます。

このプロジェクトでは、ACLが無効であることを前提としています。ACLが有効な場合、バケットポリシーを設定しても、オブジェクト(ファイル)がパブリックでない場合があります。

手順 5: WebサイトのURLにアクセス

スタティックWebサイトのURLは、以下の形式になります。

http://{バケット名}.s3-website-{リージョン}.amazonaws.com
http://wwwexamplecom.s3-website-ap-northeast-1.amazonaws.com
  1. 対象のS3バケットの上部メニューの[プロパティ]をクリックしてください。
  2. 一番下の[静的ウェブサイトホスティング]にある「バケットウェブサイトエンドポイント」のURLを開いてみてください。個別のオブジェクトにアクセスできるかは後で確認します。
  3. 上部メニューの[オブジェクト]から任意のオブジェクトを開いてください。
  4. 上部メニューの[プロパティ]を開き、[オブジェクトの概要]の[オブジェクト URL]を開いて、実際にWebアクセスできるかを確認してください。
  5. これによりHTMLページや画像などが正常に表示されれば、S3のスタティックなWebサイトの公開は完了です。

S3バケットのサイトがCDN(カスタムドメインSSL証明書あり)で配信されるまでの手順

Amazon CloudFrontの手順の中で、AWS Certificate Manager(ACM)を使ってカスタムドメイン用のSSL証明書を取得するようになっています。証明書の発行時にDNS検証を選択することによって、期限切れの60日前に自動更新されます。

Amazon CloudFrontの設定手順

  1. CloudFrontディストリビューションの作成

    1. AWS Management Consoleにログインし、CloudFrontサービスに移動します。
    2. [CloudFront ディストリビューションを作成]をクリックします。
    3. [Origin domain]で、S3バケットのエンドポイントを選択し、それにより表示される[Web サイトのエンドポイントを使用]をクリックします。
      • 例: - <バケット名>.s3-website-<リージョン>.amazonaws.com
    4. [名前]は自動的に入力されます。
    5. [デフォルトのキャッシュビヘイビア]セクションで[ビューワープロトコルポリシー]を「Redirect HTTP to HTTPS」に設定します。
    6. [ウェブアプリケーションファイアウォール (WAF) ]セクションで「セキュリティ保護を有効にしないでください」を選択します。追加費用を発生させないためです。必要になったらONにすればよいと思います。
    7. [設定]セクションの[料金クラス]で、日本メインなら「北米、欧州、アジア、中東、アフリカを使用」を選択します。性能と価格のバランスがよいためです。世界中からアクセスされるなら「すべてのエッジロケーションを使用する (最高のパフォーマンス)」がよいでしょう。
    8. その下の[代替ドメイン名 (CNAME) - オプション]で、[項目を追加]をクリックして、カスタムドメイン名(例:re.masahiko.info)を入力します。
    9. その下の[Custom SSL certificate - optional]で、[証明書をリクエスト]リンクをクリックします。
      • [パブリック証明書をリクエスト]を選択して[次へ]をクリックします。
      • [完全修飾ドメイン名]にカスタムドメイン名(例:re.masahiko.info)を入力します。
      • [検証方法]は「DNS 検証 - 推奨」に必ずしてください。
      • 他はデフォルトの推奨設定のままにします。
      • リソースグループで管理しやすいように[タグ]も付けるのがお勧めです。キーは「Name」として、値は「masahiko.info」など独自のグループ名を指定します。
      • [リクエスト]をクリックします。
      • [ステータス]が「保留中の検証」となるので、検証用にDNSのCNAMEの追加が必要です。お使いのDNSの設定箇所で、[ドメイン]内の表で取得できる「CNAME名」からサブドメインまでの部分(例:_abcdef123456gh7i8j9klm01n2opq345.<subdomain>)を名前として、「CNAME値」(ドメイン名で場合によっては最後の.を除いて内容、例:_a0123bcdefghigklmnopqrst45fvwxyz.zfyfvmchrl.acm-validations.aws.)を値(正規名)として指定してください。
      • 30分もしないうちに発行済みになります。[ステータス]に「発行済み」と表示されるまで待ちます。ブラウザーでリロードすると表示が変わる場合があります。
    10. CloudFrontのディストリビューション作成画面に戻ります。
    11. [Custom SSL certificate - optional]の右端の更新ボタンをクリックした後で、先ほど作成したSSLを[Custom SSL certificate - optional]で選択します。
    12. [Default root object - optional]にルート(/)でアクセスされたときに自動表示するファイル名(例: index.htmldefault.html)を入力。
    13. [ログ配信]は必要があればオンにしてください。ただし追加費用がかかるため、私の場合はオフにしています。
    14. [ディストリビューションを作成]をクリックします。
  2. CloudFrontディストリビューションのデプロイ

    • ディストリビューションのステータスが「有効」になるまで待ちます。わたしの場合はすぐでしたが、数分かかることがあるようです。
    • ディストリビューションのステータスは、CloudFrontのトップページでも確認できます。
  3. CloudFrontディストリビューションの確認

    • CloudFrontディストリビューションの[ディストリビューションドメイン名]を使用して、S3に配置したオブジェクトのパスのWebページや画像にアクセスして、無事に表示されるのを確認します(表示されるようになるまでに数分かかります)。例: d123456abcdef8.cloudfront.net
    • [ディストリビューションドメイン名]はCDN設定で使うので、保存しておいてください。

カスタムドメインのDNS設定と確認

最後にカスタムドメインでアクセスされるように設定すれば完了です。

カスタムドメインのCNAMEの内容(ドメイン名)に[ディストリビューションドメイン名]を指定してください。[ディストリビューションドメイン名]はCloudFrontで確認できます。

念のため、依然のCNAME内容は一時的に何かに書き写してバックアップしておきましょう。

反映まで1時間ほどかかります。長い場合は72時間程度も時間がかかることがあります。反映されたかはSSL証明書をブラウザから確認すればよいでしょう。もしくは以下のコマンドで確認できます。

nslookup <カスタムドメイン名>

ここまで出来たらCMS管理のWebサイトとAzure CDNの役目も終了なので、削除しても構いません。が、トラブル回避のため、72時間ほど様子を見てからの方がよいかもしれません。

全ての作業が終わったら、AWSのアクセスキーは無効化するか、削除しておきましょう。

ライセンス

このプロジェクトはApache License 2.0の下で公開されています。詳しくはLICENSEファイルをご確認ください。