LoginSignup
1
0

AWSのサービスのみでGitHub Pagesのような機能を実装する。

Posted at

はじめに

GitHubの便利な機能の一つに、指定したリポジトリにファイルをコミット、プッシュしたことを契機に、指定のディレクトリ配下のファイルをいい感じのWebページとして公開できるGitHub Pagesという機能があります。

ただ単純にHTMLファイルを公開するだけではなく、Markdown形式のファイルであればHTML形式に変換して公開してくれるため、Markdown形式で書いたちょっとしたメモを公開したりするのに便利な機能です。

GitHub PagesGitHubの機能となるので、GitHub以外では使用できませんが、今回はAWSのサービスだけ使用して、GitHub Pagesのように外部にWebページを公開する機能を実装してみたいと思います。

作成するAWS構成

仕組みとしては以前記事に書いた「AWSの各種パラメータを取得するツールを作りました。」の「AWSのサービスのみ使用したドキュメント生成ツール」で作った機能の流用となります。

codebuild_github_pages.png

利用サービス 用途
CodeCommit HTMLに変換したいMarkdownファイルのコミット用
EventBridge CodeCommitへのプルリクエストやマージを検知するために使用
CodeBuild CodeCommitに格納されたMarkdownファイルをHTMLに変換し、S3に格納する
S3 HTMLに変換したファイルを格納して静的Webサイトホスティングで公開する

AWSリソースの作成

以下より、必要なリソースを作成していきます。

CodeCommitの作成

CodeCommitダッシュボードより「リポジトリ」→「リポジトリを作成」からリポジトリを作成していきます。

あまり設定項目もないので、名前だけ指定して作成します。

項目 設定 備考
リポジトリ名 codecommit_pages 任意の名前を指定
説明 ※空欄 今回は未指定
タグ ※指定なし 今回は未指定
追加設定 ※デフォルトのまま
Amazon CodeGuru Reviewer for Java and Pythonを有効にする チェックなし

後ほど作成したリポジトリのARNを使用するため、作成したリポジトリを指定して「設定」から「リポジトリARN」を控えておきます。

Monosnap_20240630_152032.png

S3バケットの作成

上記構成図の順番とは異なりますが、先にS3バケットから作成していきます。

作成するS3バケットは生成されたHTMLファイルの格納以外に、格納されたファイルを公開するため、静的Webサイトホスティングも有効化します。

S3基本設定

S3ダッシュボードから「バケット」→「バケットを作成」を選択して以下のようにS3バケットを作成していきます。

今回のS3バケットは静的Webサイトホスティングで公開する必要があるため、ブロックパブリックアクセスの設定のみ、公開用の設定に変更しておきます。

項目 設定 備考
バケットタイプ 汎用
バケット名 codecommit-pages-bucket 任意の名前を指定
オブジェクト所有者 ACL無効(推奨)
このバケットのブロックパブリックアクセス設定 ※すべてチェックなし 静的Webサイトホスティングで公開するためチェックをすべて外す
現在の設定により、このバケットとバケット内のオブジェクトが公開される可能性があることを承認します。 有効化 ブロックパブリックアクセスを無効化する場合に選択が必要
バケットのバージョニング 無効にする ソースはCodeCommitで管理しているので今回は無効にする
タグ ※指定なし 今回は未指定
暗号化タイプ Amazon S3マネージドキーを使用したサーバー側の暗号化(SSE-S3)
バケットキー 有効にする
オブジェクトロック 無効にする

バケットポリシー設定

静的Webサイトホスティングを有効化する場合、ブロックパブリックアクセスを無効化してS3バケットを公開できる状態にする必要があるため、誰でもアクセスできる状態となります。

誰でもアクセスできる状態になるのはパブリックリポジトリでGitHub Pagesを有効化した場合でも同様ですが、公開する内容によってはやはりアクセスを制限したい場合もあるかと思いますので、特定のIPアドレスのみ許可するバケットポリシー設定を以下に例として記載しておきます。

設定を行う場合、作成したS3バケットの「アクセス許可」タブの「バケットポリシー」の「編集」から以下のResourceを作成したS3バケットのARN、aws:SourceIpをアドレス許可したいIPアドレスを指定して「変更の保存」で設定します。
※以下に記載しているIPアドレスは実在しないドキュメント用のIPアドレスなので、環境に合わせて変更してください。

特定のIPアドレスからのアクセスのみ許可するバケットポリシー
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "PublicReadGetObject",
            "Effect": "Allow",
            "Principal": {
                "AWS": "*"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::codecommit-pages-bucket/*",
            "Condition": {
                "IpAddress": {
                    "aws:SourceIp": [
                        "203.0.113.1/32"
                    ]
                }
            }
        }
    ]
}

特にアクセス制御しなくても良い場合でも、バケットポリシーは必要となるため、その場合はCondition部分を削除し、以下のように設定してください。

IPアドレス制限を行わない場合のバケットポリシー
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "PublicReadGetObject",
            "Effect": "Allow",
            "Principal": {
                "AWS": "*"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::codecommit-pages-bucket/*"
        }
    ]
}

静的Webサイトホスティング設定

静的Webサイトホスティングを有効化するため、「プロパティ」タブの「静的ウェブサイトホスティング」の「編集」から以下のように設定します。

項目 設定 備考
静的ウェブサイトホスティング 有効にする
ホスティングタイプ 静的ウェブサイトをホストする
インデックスドキュメント index.html
エラードキュメント ※空欄 今回は特にerrorファイルは使わないため未指定
リダイレクトルール ※空欄 リダイレクトしないため未設定

CodeBuildの作成

今回の機能のメインとなるCodeBuildを作成していきます。

CodeBuild用IAMポリシーの作成

CodeBuildでIAMロールを指定する必要があるため、IAMロールを作成するためのIAMポリシーを作成しておきます。

IAMダッシュボードから「ポリシー」の「ポリシーの作成」から以下のように設定していきます。

今回は説明簡略化のため、Resource指定しない設定としていますが、実際の環境に導入する際には要調整してください。

項目 設定 備考
ポリシーエディタ JSON 今回は以下JSONを指定
ポリシー名 codecommit-pages-codebuild-policy 任意の名前を指定
説明 ※空欄 今回は未指定
タグ ※指定なし 今回は未指定
CodeBuild用IAMポリシー
{
    "Statement": [
        {
            "Action": [
                "logs:PutLogEvents",
                "logs:CreateLogStream",
                "logs:CreateLogGroup"
            ],
            "Effect": "Allow",
            "Resource": ["*"]
        },
        {
            "Action": [
                "codebuild:UpdateReport",
                "codebuild:CreateReportGroup",
                "codebuild:CreateReport",
                "codebuild:BatchPutTestCases",
                "codebuild:BatchPutCodeCoverages"
            ],
            "Effect": "Allow",
            "Resource": ["*"]
        },
        {
            "Action": [
                "s3:PutObject",
                "s3:ListBucket",
                "s3:GetObjectVersion",
                "s3:GetObject",
                "s3:GetBucketLocation",
                "s3:GetBucketAcl",
                "s3:DeleteObject"
            ],
            "Effect": "Allow",
            "Resource": ["*"]
        },
        {
            "Action": "codecommit:GitPull",
            "Effect": "Allow",
            "Resource": ["*"]
        }
    ],
    "Version": "2012-10-17"
}

CodeBuild用IAMロールの作成

IAMロールを作成します。

IAMダッシュボードから「ロール」の「ロールを作成」から以下のように設定していきます。

項目 設定 備考
信頼されたエンティティタイプ AWSのサービス
サービスまたはユースケース CodeBuild プルダウンメニューから選択
ユースケース CodeBuild
許可ポリシー codecommit-pages-codebuild-policy 先ほど作成したIAMポリシーを指定
ロール名 codecommit-pages-codebuild-role 任意の名前を指定
説明 ※空欄 今回は未指定

ビルドプロジェクト作成

ビルドプロジェクトを以下のように設定していきます。

HTMLの生成にはGitHub Pagesでも使用しているJekyllを使用するため、AWSのマネージド型イメージを使用するのではなくJekyllのDockerイメージを使用するようにします。

項目 設定 備考
プロジェクト名 codecommit-pages-build-proj 任意の名前を指定
追加設定(プロジェクトの設定) ※デフォルトのまま 今回はデフォルトのまま未設定
ソースプロバイダ AWS CodeCommit
リポジトリ codecommit_pages 先ほど作成したリポジトリを選択
リファレンスタイプ ブランチ
ブランチ ※空欄 ブランチの指定ができないためデフォルトのまま空欄とする
追加設定(ソース) ※デフォルトのまま 今回はデフォルトのまま未設定
プロビジョニングモデル オンデマンド
環境イメージ カスタムイメージ 今回はjekyllイメージを使用したいためカスタムイメージを選択
コンピューティング EC2
環境タイプ Linux EC2
イメージレジストリ その他のレジストリ 今回はjekyllイメージを使用したいためその他のレジストリを選択
外部レジストリのURL jekyll/jekyll:latest jekyllイメージのURLを指定
レジストリの認証情報 ※空欄 パブリックレジストリのため認証なし
サービスロール codecommit-pages-codebuild-role 先ほど作成したIAMロールを指定
AWS CodeBuild にこのサービスロールの編集を許可し、このビルドプロジェクトでの使用を可能にする 有効にする
追加設定(環境) 環境変数のみ設定(次表で記載) 環境変数以外はデフォルトのままにする
環境変数 ※次表参照 buildspec.ymlで使用する環境変数を指定
ビルド仕様 ビルドコマンドの挿入 今回は「エディタに切り替え」から直接以下のbuildspec.ymlの内容をコピペ
バッチ設定を定義 無効にする ※デフォルトのまま
アーティファクト1 タイプ アーティファクトなし
追加設定(アーティファクト) ※デフォルトのまま 今回はデフォルトのまま未設定
CloudWatch Logs 無効にする 今回は説明簡略化のため無効
S3ログ 無効にする 今回はデフォルトのまま未設定

buildspec.ymlで使用する環境変数は以下のように指定します。

名前 タイプ 備考
REGION ap-northeast-1 プレーンテキスト S3バケットのリージョンを指定
REPOSITORY codecommit_pages プレーンテキスト 先ほど作成したリポジトリ名を指定
GLIBC_VERSION 2.35-r1 プレーンテキスト AWS CLIインストールに必要のため指定
SRC_DIR ※空欄 プレーンテキスト ルートディレクトリ以外に生成用ファイルを格納する場合に指定
PROTOCOL http プレーンテキスト 静的Webサイトホスティングはhttpアクセスとなるため、httpを指定
S3_BUCKET_NAME codecommit-pages-bucket プレーンテキスト 先ほど作成したS3バケット名を指定

SRC_DIRは生成元のファイルがルートディレクトリではなく別のディレクトリ(例えば/docs)に存在する場合に指定してください。

SRC_DIRを指定することで、GitHub Pagesで以下のような特定のブランチ、特定のディレクトリを指定した場合と同等の処理が行われます。

Monosnap_20240630_201842.png

buildspec.ymlの設定は以下をコピペしてください。

buildspec.ymlの設定
version: 0.2

env:
  git-credential-helper: yes

phases:
  install:
    on-failure: ABORT
    commands:
      # jqとAWS CLI v2のインストール
      - apk --update add jq binutils
      - wget -O /etc/apk/keyssgerrand.rsa.pub https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub
      - wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/${GLIBC_VERSION}/glibc-${GLIBC_VERSION}.apk
      - wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/${GLIBC_VERSION}/glibc-bin-${GLIBC_VERSION}.apk
      - wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/${GLIBC_VERSION}/glibc-i18n-${GLIBC_VERSION}.apk
      - |
        apk add --no-cache --force-overwrite --allow-untrusted \
        glibc-${GLIBC_VERSION}.apk \
        glibc-bin-${GLIBC_VERSION}.apk \
        glibc-i18n-${GLIBC_VERSION}.apk
      - /usr/glibc-compat/bin/localedef -i en_US -f UTF-8 en_US.UTF-8
      - ln -sf /usr/glibc-compat/lib/ld-linux-x86-64.so.2 /lib64/ld-linux-x86-64.so.2
      - wget -O awscliv2.zip https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip
      - unzip awscliv2.zip 2>&1 > /dev/null
      - aws/install
      # 不要なファイルの削除
      - rm -rf aws awscliv2.zip glibc-*.apk
  build:
    on-failure: ABORT
    commands:
      # JekyllインストールとJekyllに必要なファイルの作成
      # Gemfileの作成
      - cd /srv/jekyll
      - |
        cat << _EOF_ > Gemfile
        source "https://rubygems.org"
        gem 'github-pages', group: :jekyll_plugins
        _EOF_
      # _config.ymlの作成
      - |
        cat << _EOF_ > _config.yml
        title: ${REPOSITORY}
        url: ${PROTOCOL}://${S3_BUCKET_NAME}.s3-website-${REGION}.amazonaws.com
        baseurl: /
        theme: jekyll-theme-primer
        _EOF_
      # Bundleのインストール
      - chown jekyll:jekyll Gemfile _config.yml
      - bundle add webrick
      # jekyll-theme-primer用default.htmlの作成
      # jekyll-theme-primerのデフォルトレイアウトの場合、GitHubのリポジトリ名を指定しないとエラーとなるためカスタムレイアウトを作成
      - mkdir -p ${CODEBUILD_SRC_DIR}/${SRC_DIR}/_layouts
      - |
        cat << _EOF_ > ${CODEBUILD_SRC_DIR}/${SRC_DIR}/_layouts/default.html
        <!DOCTYPE html>
        <html lang="{{ site.lang | default: "en-US" }}">
          <head>
            <meta charset="UTF-8">
            <meta http-equiv="X-UA-Compatible" content="IE=edge">
            <meta name="viewport" content="width=device-width, initial-scale=1">

            <link rel="stylesheet" href="{{ "/assets/css/style.css?v=" | append: site.github.build_revision | relative_url }}">
            {% include head-custom.html %}
          </head>
          <body>
            <div class="container-lg px-3 my-5 markdown-body">
              {% if site.title and site.title != page.title %}
              <h1><a href="{{ "/" | absolute_url }}">{{ site.title }}</a></h1>
              {% endif %}

              {{ content }}

            </div>
            <script src="https://cdnjs.cloudflare.com/ajax/libs/anchor-js/4.1.0/anchor.min.js" integrity="sha256-lZaRhKri35AyJSypXXs4o6OPFTbTmUoltBbDCbdzegg=" crossorigin="anonymous"></script>
            <script>anchors.add();</script>
          </body>
        </html>
        _EOF_
      - chown -R jekyll:jekyll ${CODEBUILD_SRC_DIR}/${SRC_DIR}/_layouts
  post_build:
    on-failure: ABORT
    commands:
      # ドキュメント表示用HTMLファイルの作成とS3へのアップロード
      # ドキュメントのビルド
      - |
        bundle exec jekyll build \
          --config /srv/jekyll/_config.yml \
          --source ${CODEBUILD_SRC_DIR}/${SRC_DIR} \
          --destination /srv/jekyll/_site
      # S3へのアップロード
      - aws s3 sync /srv/jekyll/_site s3://${S3_BUCKET_NAME}/ --exclude "*.md" --delete

buildspec.ymlについて

上記で記載したbuildspec.ymlについてざっくりと以下に示します。

installフェーズ

今回、AWSのマネージド型イメージではなくJekyllイメージを使用するため、JekyllイメージでもAWS CLIを使用できるようにするため、必要なパッケージ等をインストールして、AWS CLIを導入しています。

JekyllイメージはAlpineをベースにしていますが、AWS CLIインストール時に必要となるGLIBCが標準パッケージにないため、他リポジトリから持ってきたりしています。

buildフェーズ

GitHub Pagesと同じように表示できるようにするため、Jekyllの設定を色々と行っています。

GitHub Pagesで使われているjekyll-theme-primerというテーマをそのまま使おうとすると、GitHubリポジトリ名を指定しないとエラーとなることから、カスタムレイアウトを_layouts/default.htmlに作成して、AWSで使用した場合でもエラーが出ないようにしています。

buildフェーズはJekyllの設定となるため、もし色々とカスタマイズしたい方はJekyllのドキュメントをよく読んで、buildフェーズで色々と設定してみてください。

post_buildフェーズ

buildフェーズで設定したJekyllの設定に従って、HTMLファイルを生成しています。

生成したドキュメントは指定したS3バケットに格納されます。

EventBridgeの作成

CodeCommitへのコミットを契機に先ほど作成したCodeBuildを実行するためのEventBridgeを作成します。

項目 設定 備考
名前 codecommit-pages-eb-rule 任意の名前を指定
説明 ※空欄 今回は未指定
イベントバス default
選択したイベントバスでルールを有効にする 有効
ルールタイプ イベントパターンを持つルール
イベントソース AWSイベントまたはEventBridgeパートナーイベント
メソッド カスタムパターン(JSONエディタ) 直接以下のイベントパターンを指定するためカスタムパターンを指定
イベントパターン ※以下イベントパターンのJSON参照 以下イベントパターンを指定
ターゲットタイプ AWSのサービス
ターゲットを指定 CodeBuildプロジェクト
プロジェクトARN 先ほど作成したCodeBuildのARNを指定
実行ロール この特定のリソースについて新しいロールを作成 新規作成でも必要な権限が付与されるため今回はデフォルトロールで作成
追加設定(ターゲット1) ※デフォルトのまま 今回はデフォルトのまま未設定
タグ ※指定なし 今回は未指定

イベントパターンは先程控えたCodeCommitのARNを指定しつつ以下のように設定。

イベントパターン
{
  "detail": {
    "event": ["referenceCreated", "referenceUpdated"],
    "referenceName": ["master"]
  },
  "resources": ["[先ほど控えたCodeCommitのARNを指定]"],
  "source": ["aws.codecommit"]
}

動作確認

ここまでの作業で、GitHub Pagesのように、リポジトリにコミットしたらMarkdownのファイルから公開用HTMLファイルを生成して外部から閲覧できる準備ができたので、実際に動作するかを試してみます。

CodeCommitリポジトリのクローン

先ほど作成したCodeCommit画面にリポジトリのクローン作成URLが表示されているため、コマンドをコピーして自分の端末にクローンします。

Monosnap_20240630_175116.png

CodeCommitリポジトリのクローン
git clone https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/[先ほど作成したCodeCommitリポジトリ名]

リポジトリにテスト用index.mdファイルのプッシュ

きちんとHTMLファイルが生成されるかを確認するため、適当なMarkdownファイルをリポジトリにプッシュしてみようと思います。

先ほどローカルにクローンしたリポジトリに移動し、以下のようなindex.mdファイルをルートディレクトリに作成して、Markdownファイルをプッシュしてみます。

index.md(テスト用Markdownファイル)
# CodeCommit Pagesテスト

CodeCommitでGitHub Pagesのような機能を実装するテスト。

## Hello World

- 表テスト

|項目1|項目2|
|:--|:--|
|test1|test2|

- コードブロックテスト

```bash
aws s3 ls
```
リポジトリへのプッシュ
git add . && git commit -m "CodeCommit Pagesテスト" && git push -u origin master

公開HTMLファイルの確認

今まで設定した内容に間違いがなければ、リポジトリプッシュを契機にCodeBuildが動き出し、暫く経つとステータスが「成功」となるはずです。

Monosnap_20240630_183358.png

S3バケットにはindex.mdから生成されたindex.htmlとCSSファイルが格納されているassetsが存在しているかと思います。

Monosnap_20240630_183035.png

生成されたHTMLファイルの内容を確認するには、「プロパティ」タブの「静的ウェブサイトホスティング」欄に記載されている「バケットウェブサイトエンドポイント」のリンクをクリックしWebアクセスを行います。

すると以下のようにGitHub Pagesで生成した画面と同等のページが確認できるかと思います。

Monosnap_20240630_183834.png

なお、生成に使用しているジェネレータはGitHub Pagesで使用されているジェネレータと同じく「Jekyll」を使用しているため、ほぼ同等の表示となります。

ちなみに先ほどと同じindex.mdファイルをGitHub Pagesが有効なGitHubリポジトリにプッシュしてHTMLファイルを生成したときの例が以下となります。

Monosnap_20240630_191925.png

リポジトリ名をgithub_pagesというリポジトリにしたため、頭のリンク名だけは異なりますが、それ以外に違いがないことが確認できるかと思います。

おわりに

以前「AWSの各種パラメータを取得するツールを作りました。」でGitHubを用いない場合でも使えるようにした際の副産物として、今回AWSサービスのみでGitHub Pagesのような機能を実装してみました。

ドキュメント生成部分をCodeBuildで実装している関係上、どうしてもbuildspec.ymlの手入れは必要となってしまいますが、GitHubを使えない環境でも似た機能を実装したい場合には使えるのではないかと思います。

今回はGitHub Pagesの表示になるべく似せるため、ページのトップにリポジトリ名を表示させたり、GitHub Pagesで使用しているテーマを使っていますが、buildspec.ymlに記載しているJekyllの設定次第で表記を変えることもできますし、応用してJekyll以外の静的サイトジェネレータを使ってみても面白いかと思います。

また、今回のリソースとほぼ同じもの(IAMポリシー等は一部リソース指定等行うように修正)を作成できるようにしたterraformを以下に置いたので、すぐに試してみたい方はこちらを使ってみてください。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0