サーバー基盤グループで SRE として活動している工藤です。
つい先日、 Google Cloud 上で念願の Arm インスタンス T2A が利用可能になりました。まだプレビュー扱いで限られたリージョンでしか利用できませんが、既に Kubernetes Engine も動作します。
今回はそんな Google Cloud の Arm インスタンスを試してみました。
- 広がる Arm アーキテクチャの世界
- Arm for Server
- Arm インスタンスと Apple Silicon Mac
- docker buildx + QEMU によるクロスビルド
- GCE T2A を用いたネイティブビルド
- 速度・費用比較
- まとめ
広がる Arm アーキテクチャの世界
Arm アーキテクチャといえば、現在販売されているスマートフォンの大半が Arm アーキテクチャの CPU を採用しています。もちろんコロプラのゲームも Arm アーキテクチャ向けにビルドされています。
コンピュータの世界では、 Apple が Mac に採用した Apple Silicon も Arm アーキテクチャの一つです。 Windows マシンも Microsoft の Surface X が Qualcomm 製の Arm アーキテクチャ CPU 搭載チップセットを採用するなど、近年勢いを増しています。
Arm for Server
Arm の躍進は今やサーバーの世界にも波及し、 Amazon Web Services (以下: AWS) が Graviton で採用したのを皮切りにOracle Cloud や Microsoft Azure も Arm アーキテクチャ CPU のインスタンスを提供しています。Intel や AMD などの x86_64 (amd64) アーキテクチャのインスタンスに比べ、安価に利用できるのが強みです。
AWS や Microsoft Azure が対応する中、 Google Cloud は Arm サポートについて沈黙を貫いてきましたが、先日ついに T2A インスタンスとして利用できるようになりました。
Google Cloud の T2A インスタンスは Ampere Computing 社の Altera CPU を採用しています。 Oracle Cloud や Microsoft Azure と同様です。
もちろん Intel / AMD に比べ安価なのは Google Cloud でも共通しており、 Compute Engine での 1 時間あたりの価格を比べると以下のようになります。
Arch | 1 時間あたり | 1 時間あたり (spot) | |
n1-standard-8 (Intel Xeon SKL) | x86_64 | $0.379998 | $0.08 |
n2-standard-8 (Intel Xeon ICL) | x86_64 | $0.388472 | $0.09416 |
n2d-standard-8 (AMD EPYC Zen2/3) | x86_64 | $0.337968 | $0.034088 |
t2a-standard-8 (Ampere Altra) | AArch64 | $0.308 | $0.0924 |
Pricing | Compute Engine: Virtual Machines (VMs) | Google Cloud (region: us-central1, 2022-07-15 時点)
安いのはとても良いことです。現在はまだプレビューですが、将来的にはプロダクション環境のコスト削減にも活用できそうです。
Arm インスタンスと Apple Silicon Mac
前述の通りまだまだサービスに使うには早い感じのある Arm インスタンスですが、弊社ではバックエンドエンジニアのマシンとして MacBook Pro を利用していることもあり、 Apple が Apple Silicon (Arm アーキテクチャ) へ移行を進めている関係で Arm アーキテクチャ向けのイメージへの需要が高まってきました。
というのも、 Docker Desktop for Mac では QEMU (CPU エミュレータ) を利用して x86_64 向けのイメージを動作させることができますが、これが非常に遅いのです。
どのくらい遅いのか、 PHP 付属の Zend/bench.php を実行してみました。環境は以下の通りです。
- OS: macOS Monterey 12.4
- CPU: Apple M1 Pro 8core (6: firestorm, 2: icestorm)
- RAM: 32GB
- Docker Desktop: v4.9.1
amd64/php:8.1-cli (QEMU) | arm64v8/php:8.1-cli (Native) | |
simple | 0.078 | 0.012 |
simplecall | 0.030 | 0.005 |
simpleucall | 0.097 | 0.015 |
simpleudcall | 0.106 | 0.013 |
mandel | 1.020 | 0.049 |
mandel2 | 1.074 | 0.047 |
ackermann(7) | 0.134 | 0.011 |
ary(50000) | 0.019 | 0.003 |
ary2(50000) | 0.017 | 0.002 |
ary3(2000) | 0.331 | 0.019 |
fibo(30) | 0.404 | 0.034 |
hash1(50000) | 0.044 | 0.003 |
hash2(500) | 0.058 | 0.004 |
heapsort(20000) | 0.185 | 0.012 |
matrix(20) | 0.184 | 0.011 |
nestedloop(12) | 0.142 | 0.010 |
sieve(30) | 0.105 | 0.006 |
strcat(200000) | 0.035 | 0.002 |
Total | 4.063 | 0.259 |
なんと 15 倍くらいかかっています。流石につらい...
docker buildx + QEMU によるクロスビルド
ではどうするか、というと Arm アーキテクチャネイティブなイメージを作成する必要があります。幸いにも Docker は buildx を用いて異なるアーキテクチャ向けのイメージをビルドすることができるようになっているため、それを利用すれば良い...かに思えました。
実際に n1-standard-8 でイメージをビルドしてみます。今回は検証のため、 DockerHub で公開されている php:8.1-cli-alpine のイメージに対し gRPC 拡張を追加したイメージを焼くことにします (PHP 自体は既にビルドされているのでそこそこ巨大な gRPC を pecl を使ってビルドします)
環境構築手順は省略します (結構ややこしいので需要があればまた別記事で...とりあえずコマンドの羅列だけ残しておきます)
Dockerfile はこんな感じ
FROM php:8.1-cli-alpine # pecl_mt_install: pecl install 時にマルチコアでビルドするためのおまじない RUN set -eux \ && pecl_mt_install() { \ extension="${1}" \ && extension_name="${extension%-*}" \ && shift \ && temporary="/tmp/pear/temp/${extension_name}" \ && apk_delete="" \ && if [ -n "${PHPIZE_DEPS}" ] && ! apk info --installed .phpize-deps > /dev/null; then \ apk add --no-cache --virtual .phpize-deps ${PHPIZE_DEPS}; \ apk_delete='.phpize-deps'; \ fi \ && if [ -n "${HTTP_PROXY:-}" ]; then \ pear config-set http_proxy ${HTTP_PROXY}; \ fi \ && pecl install --onlyreqdeps --nobuild "${extension}" \ && cd "${temporary}" \ && phpize \ && CFLAGS="${PHP_CFLAGS:-}" CPPFLAGS="${PHP_CPPFLAGS:-}" CXXFLAGS="${PHP_CXXFLAGS:-}" LDFLAGS="${PHP_LDFLAGS:-}" ./configure \ && make -j$(nproc) \ && make install \ && rm -rf "${temporary}" \ && if [ -n "${apk_delete}" ]; then apk del --purge --no-network ${apk_delete}; fi; \ } \ && apk add --no-cache "linux-headers" "zlib-dev" \ && pecl_mt_install "grpc"
結果は以下のような感じ
$ time sudo docker buildx build --platform "linux/arm64" . (省略) real 88m59.975s user 0m0.558s sys 0m1.395s
びっくりするくらいかかりました。つらい...
GCE T2A を用いたネイティブビルド
次に t2a-standard-8 でネイティブにビルドしてみます。
環境構築手順は同様に省略
結果はこうなりました
$ time sudo docker build . (省略) Successfully built 667e8e7d7b00 real 3m33.002s user 0m0.033s sys 0m0.088s
は、速い...!さすがネイティブ...!
速度・費用比較
比較してみると差は歴然です。あまりにかかるので n1-highcpu-32 でも計測してみました。
Arch | コア数 | ビルド時間 | 概算費用 ※1 | 概算費用 ※2 | |
n1-standard-8 | x86_64 | 8 | 88m59.975s | $33.819822 (4565.67597 円) | $7.12 (961.2 円) |
n1-highcpu-32 | x86_64 | 32 | 29m28.323s | $34.007328 (4590.98928 円) | $7.159488 (966.53088 円) |
t2a-standard-8 | AArch64 | 8 | 3m33.002s | $1.232 (166.32 円) | $0.3696 (49.896 円) |
※1 : us-central1, 非プリエンプティブ, 分単位切り上げを 1USD = 135JPY で換算した場合
※2 : us-central1, プリエンプティブ, 分単位切り上げを 1USD = 135JPY で換算した場合
やはり圧倒的な差です。コストはプリエンプティブインスタンスを使えば大幅に下げられるとはいえ、それでも大差がついています。
まとめ
Arm インスタンスはサービスのインフラ費用を低減させるだけでなく、 Arm 化の進むソフトウェアエンジニアの開発環境構築においても非常に喜ばしいものだということがわかりました。
今回は Compute Engine での検証となりましたが、今後 Cloud Build でも T2A インスタンスが利用できるようになることでより利用しやすくなり、 CI / CD の選択肢も広がってくることでしょう。
また Arm インスタンスはそもそもの費用が低いということもあり、 CPU より I/O バウンドな Web アプリケーションのリソースとして非常に魅力的な選択肢になりうると思えます。 GA が待ち遠しいですね。