AWS Amplifyを使っていると気になってくるのがビルドの速度です。
デフォルトで用意されているイメージは汎用的に作られているためイメージも重くビルドには時間がかかってしまいます。
しかしAmplifyでは自身で構築したDockerイメージをビルドに用いることができます。自分に必要な最小限の構成で、かつ必要なランタイムのバージョンを最初からインストールしておくことができ、ビルド時間を短縮することができそうです。
今回は実際に本サイトをビルドするためのカスタムビルドイメージ作成を例に取り、ローカルでHugoを動作させるDockerイメージを作りそれをECRのパブリックリポジトリに登録してAmplifyから使う手順を紹介します。
従来の方法
Amplifyのカスタムビルドイメージは、以前は公式のGitHubリポジトリにてテンプレートが公開されており、それを元にしてカスタムイメージを作る方法が案内されていました。しかしこの方法は現在は非推奨となっています。
以下を見ると2023年5月に非推奨としてサンプルのDockerfileは取り下げられ、Deprecated(非推奨)という扱いになったようです。
参考: Commit diff
今は以下の公式ドキュメントで別の手順が紹介されているため、今回はこちらに沿って行っていこうと思います。
Dockerイメージの作成
Docker環境の準備
まずはローカルでDockerイメージをビルドするための環境を整えます。これは各環境に依存しますので各自でふさわしい方法を探して構築してください。
Dockerfileの準備
次にDockerfileを準備します。以下は本サイトをビルドするためのHugoの環境を作るDockerfileです。軽量化のためにマルチステージビルドを行っています。
FROM --platform=linux/x86_64 public.ecr.aws/amazonlinux/amazonlinux:2023.3.20240131.0 AS builder
USER root
ENV HOME=/root
###########################
# Install OS packages #
###########################
RUN dnf -y update && \
dnf -y install \
git \
gcc-c++ \
tar \
wget && \
dnf clean all
######################
# Install golang #
######################
ENV GO_PACKAGE_NAME=go1.21.6.linux-amd64.tar.gz
RUN wget https://go.dev/dl/${GO_PACKAGE_NAME} && \
tar -C /usr/local -xzf ${GO_PACKAGE_NAME} && \
rm ${GO_PACKAGE_NAME}
ENV PATH=$PATH:/usr/local/go/bin:${HOME}/go/bin
####################
# Install Hugo #
####################
ENV CGO_ENABLED=1
RUN go install -tags extended github.com/gohugoio/hugo@v0.122.0 && \
go clean --modcache
##########################
# Second Stage Build #
##########################
FROM --platform=linux/x86_64 public.ecr.aws/amazonlinux/amazonlinux:2023.3.20240131.0-minimal
RUN dnf -y install git && \
dnf clean all
COPY --from=builder /root/go/bin/hugo /usr/local/bin/
ENTRYPOINT [ "bash", "-c" ]
まずはカスタムビルドイメージで必要なものとして以下があります。
- x86_64のLinuxイメージ
- cURL
- Git
- OpenSSH
- Bash and The Bourne Shell
参考: https://docs.aws.amazon.com/amplify/latest/userguide/custom-build-image.html
ドキュメントではNode.JS+NPMも強く推奨されていますが、今回の場合は無くても動くので入れていません。
ここで注意ですがFROMで指定したイメージにx86_64とarm64のバリエーションがある場合、ビルドする環境に応じて自動的に選ばれるようです。私はArm環境(M1 Mac)だったため以下のようなエラーが出てしまいました。
gcc: error: unrecognized command-line option '-m64'
なのでFROMに明示的に --platform=linux/x86_64
をつけています。
Hugo特有の設定
以下はHugoのビルドのために必要な内容です。違うフレームワークを使う場合はそれぞれに合わせた方法を取ってください。
まずGoのインストールは以下に手順があります。
以下より適切なパッケージを選択します。今回はx86_64のLinuxなので linux-amd64
とあるものの中から選びます。
Hugoのインストールは公式の以下の手順に従いました。
Hugoには g++
コマンドが必要です。
g++: exec: "g++": executable file not found in $PATH
AmazonLinux2013ではyumで gcc-c++
というパッケージ名を指定すれば大丈夫です。
上記の例では私の環境で使っているバージョンである0.122.0を指定していますが、実際には各環境に合わせて必要なバージョンを指定してください。インストールされたHugoのバイナリは ${HOME}/go/bin
に格納されるので忘れずにPATHに追加しておきます。
Dockerイメージのビルド
では作成したDockerfileを使ってイメージをビルドします。
ここでは amplify-build-hugo
という名前で作成しますがここは自由につけてください。
docker build -t amplify-build-hugo-test - < Dockerfile
ECRリポジトリへの登録
Dockerイメージが作れたら次はそのイメージをAmplifyから使えるようにリポジトリへ登録します。ECR (Elastic Container Registry) の Public Repository に登録することで、Amplifyでのカスタムビルドイメージとして指定ができるようになります。
以下の手順に沿っていけばよいのでここでは簡単に記載するのみとします。
ECRでパブリックリポジトリの作成
Webコンソールから作成すればOKです。
リポジトリへPush
Webコンソールの「プッシュコマンドを表示」に丁寧に手順が記載されているのでその通りに実施すればOKです。必要あればバージョンタグは適宜変えましょう。
カスタムビルドイメージの指定
あとはAmplifyのビルドの設定からカスタムイメージを指定します。
これで、今後のビルドはさきほど作成したカスタムイメージにて行われるようになりました。
結果
実際に速くなったのかどうか確認してみましょう。
まず手元のDockerイメージは313MBでした。
REPOSITORY TAG IMAGE ID CREATED SIZE
amplify-build-hugo latest afeea12d5ca4 9 minutes ago 312MB
ECRの画面から見ると199.83MBとあります。圧縮されているということでしょうか。
これだけ見てもなんとも言えないので、実際にデプロイまでの時間を見てみます。
最初はビルドイメージはAmazonLinux:2013、ライブパッケージアップデートでHugoのバージョンを指定していたところ 1分36秒 かかっていたのですが、カスタムイメージを用いたところ 35~40秒 程度まで短縮されました。約1分の短縮、約3分の1の時間になりました!(ただ、たまに1分10秒くらいかかるときもあるようです。Dockerイメージのキャッシュが効くかどうかなどコントロール外の要因がありそうです。)
NetlifyやVercelなど静的サイトホスティングに特化したサービスと比較するとまだまだ速いとは言えない感じかなと思いますが、デフォルトでかかる時間に比べれば許容範囲になってくるのではないでしょうか。
さらにミニマムなイメージを作ることでさらに速くなる可能性もありますが、作る手間もあるため一旦これぐらいにしておこうと思います。特に今回はgitを入れるのに手こずってdnfでのインストールにしてしまったのは悔やまれるポイントです。
より高速化することができた方はぜひコメントいただけると嬉しいです。
まとめ
AWS Amplifyのビルド時間を短縮するべくカスタムビルドイメージを作ることに挑戦しました。軽量なDockerイメージを作ることで約3分の1まで時間を短縮することができました。より高速化するポイントも探っていきたいですが、正直なところAWSさんにもぜひ改善を願いたいところです。