AWS Graviton でお得にコンテナを動かす

2024-08-01
デベロッパーのためのクラウド活用方法

Author : 苅野 秀和

コンテナが好きな皆さんこんにちは。ソリューションアーキテクトの苅野です。

コンテナを AWS 上で動かす時にコストが気になることが多いと思います。AWS Graviton プロセッサ は ARM 系のアーキテクチャを採用したプロセッサで、これを搭載したインスタンスは同世代のインスタンスに比べてコスパよくワークロードを動かすことができます。そして、 この Graviton のインスタンスはコンテナでも利用できます。

AWS Fargate も Graviton を利用した ARM のプロセッサをサポートしており、料金ページによれば東京リージョンで x86 に比べて ARM だと vCPU 単価は 80% の価格です。一方で、AWS Graviton でしか動かないコンテナというのは可搬性が少し足りない気がしますよね。どこにでも ship できるように ARM とは異なる x86 系のアーキテクチャでも動かせるようにしておくことは重要です。

そこで本記事では ARM でも x86 でも動くコンテナイメージ (マルチアーキテクチャコンテナイメージ) がどのような仕組みで、ビルドからデプロイまでの流れはどのようになるかを解説します。


コンテナイメージのおさらい

そもそもコンテナイメージは レイヤー が積み重なったものです。Dockerfile で定義されたベースイメージに COPY コマンドでファイルをインストールしたり、RUN コマンドでソースコードをビルドしたりする操作一つ一つがレイヤーとなって、最終的にはコンテナイメージになります。

この時のコンテナイメージの構造 (イメージマニフェスト) は以下のような json で表されます。

{
  "schemaVersion": 2,
  "mediaType": "application/vnd.oci.image.manifest.v1+json",
  "config": {
    "mediaType": "application/vnd.oci.image.config.v1+json",
    "digest": "sha256:b5b2b2c507a0944348e0303114d8d93aaaa081732b86451d9bce1f432a537bc7",
    "size": 7023
  },
  "layers": [
    {
      "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
      "digest": "sha256:9834876dcfb05cb167a5c24953eba58c4ac89b1adf57f28f2f9d09af107ee8f0",
      "size": 32654
    },
    {
      "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
      "digest": "sha256:3c3a4604a545cdc127456d94e421cd355bca5b528f4a9c1905b15da2eb4a4c6b",
      "size": 16724
    },
  ],
  "subject": {
    "mediaType": "application/vnd.oci.image.manifest.v1+json",
    "digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270",
    "size": 7682
  },
}

この中の config の中身は OCI 仕様で定められており、例えば このように なります。

{
    "created": "2015-10-31T22:22:56.015925234Z",
    "author": "Hidekazu Karino <hidekari@amazon.co.jp>",
    "architecture": "amd64",
    "os": "linux",
    // 以下省略
}

architecture の項目に CPU アーキテクチャが指定されています。つまりコンテナイメージとそのコンテナが動く CPU アーキテクチャが 1:1 で対応しているわけです。そしてこのコンテナイメージを pull して動かす場合の流れは以下のようになります

  1. コンテナホストはイメージマニフェスト (hoge:amd64) をダウンロードする
  2. マニフェストの各レイヤーを次々にダウンロードしていく
  3. コンテナを実行する。この時ホストが amd64 ならマニフェストの config と合うので問題なく動くが、ホストが ARM など異なるアーキテクチャの場合動かない


コンテナイメージと CPU アーキテクチャが 1:1 で対応しているので、ARM で動かしたい場合は ARM 用のコンテナイメージを、x86 で動かしたい場合は x86 用のコンテナイメージを用意する必要があります。

普段 ARM でコンテナを動かしている場合でも、スケーリングが発生した場合に ARM インスタンスの需要が逼迫した時には、一時的にでも x86 でコンテナを動かす時が出てくるでしょう。その時に備えて例えアプリケーションコードが同一であったとしても、複数種類のコンテナイメージを用意する必要があるとなると、運用が手間になってくるでしょう。

こうした課題を解決するための選択肢の一つとなるのがマルチアーキテクチャコンテナイメージです。


マルチアーキテクチャコンテナイメージ

マルチアーキテクチャコンテナイメージに関する Docker のドキュメントは こちら です。詳細が気になる方はこちらのドキュメントを参照いただくとして、ここでは概要をお話しします。

マルチアーキテクチャコンテナイメージのアイデアは複数のイメージマニフェストを参照するマニフェストリストを導入し、ホストは一旦このマニフェストリストをダウンロードして、その次にホストの CPU アーキテクチャに対応するイメージマニフェストがあればそれをダウンロードするようにしようというものです。OCI の仕様だと image index と呼ばれています。

マニフェストリストは例えば以下のような構造になっています。

{
  "schemaVersion": 2,
  "mediaType": "application/vnd.oci.image.index.v1+json",
  "manifests": [
    {
      "mediaType": "application/vnd.oci.image.manifest.v1+json",
      "size": 7143,
      "digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f",
      "platform": {
        "architecture": "arm64",
        "os": "linux"
      }
    },
    {
      "mediaType": "application/vnd.oci.image.manifest.v1+json",
      "size": 7682,
      "digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270",
      "platform": {
        "architecture": "amd64",
        "os": "linux"
      }
    }
  ],
}

この例の場合二つのイメージマニフェストを配列で持っていて、それぞれ arm64 と amd64 アーキテクチャに対応したイメージマニフェストです。このマニフェストリストを pull して動かす流れは以下のようになります

  1. コンテナホストはマニフェストリスト (hoge:v1) をダウンロードする
  2. コンテナホストの CPU アーキテクチャと同じアーキテクチャ (例えば amd64)、OS (例えば Linux) のイメージマニフェスト (hoge-image:some-id) をダウンロードする
  3. 以降は先ほどのコンテナイメージを pull して動かす流れと同じ


このような流れだと最初にダウンロードするのはマニフェストリストなので、コンテナホストの CPU アーキテクチャは関係ありません。amd64 でも arm64 でもよくて同じマニフェストリストをまずダウンロードし、その後ホストと同じアーキテクチャのイメージマニフェストをダウンロードします。ホストの CPU アーキテクチャごとに pull するイメージを見に行って分けなくても良くなりました。


マルチアーキテクチャコンテナイメージをビルドしてデプロイするまで

Docker でマルチアーキテクチャコンテナイメージをビルドするにはいくつか戦略があります。ここでは QEMU を使ったエミュレーションと異種類のホストでのビルドを紹介します。

QEMU を使ったエミュレーションは手軽に始めることができる方法です。docker buildx コマンドを使えば、以下のように二つのアーキテクチャ向けのマニフェストリストを簡単にビルド・プッシュできます。

docker buildx build 
--platform linux/arm64,linux/amd64 
--tag ${repository URI}:multi-arch-image
--push .

QEMU でホストマシンとは異なるアーキテクチャの CPU をエミュレーションし、その CPU アーキテクチャ向けにコンテナイメージをビルドすることができます。一方で異種類のホストでのビルドは ARM と x86 アーキテクチャのホストをそれぞれ用意し、それぞれでコンテナイメージをビルドした後最後にマニフェストリストを生成するやり方です。

前者は QEMU をインストールすれば手軽に始められる一方で、ホストと異なる CPU アーキテクチャをエミュレートするオーバーヘッドがかかるため、イメージビルドにかかる時間が長くなるかもしれません。後者はオーバーヘッドは小さい一方で、複数のビルド用マシンを稼働させるためランニングコストと運用コストが追加で発生します。どちらが適しているかは要件によるでしょう。後者の場合デプロイパイプラインは例えば以下のようになるでしょう。


まとめ

今回は AWS Graviton でも x86 でもコンテナを動かす方法の一つとしてマルチアーキテクチャコンテナイメージを紹介しました。普段は安く AWS Graviton でコンテナを動かし、いざとなったら Intel でも動かせるようにしておくことでお得にコンテナを動かせると思います。

皆さんも是非 Graviton でコンテナを動かす こちらのワークショップ を実施して、AWS Graviton でコンテナを動かしてみてください。


builders.flash メールメンバーへ登録することで
AWS のベストプラクティスを毎月無料でお試しいただけます

筆者プロフィール

苅野 秀和
アマゾン ウェブ サービス ジャパン合同会社 ソリューションアーキテクト

飛行機の勉強をしていたのが気づいたらなんやかんやあって AWS に入社してました。現在はウェブ系のお客様の技術支援を行いながら Rust を書いています。
週末は美味しいクロワッサンを求めてパン屋を探訪しています。

AWS を無料でお試しいただけます

AWS 無料利用枠の詳細はこちら ≫
5 ステップでアカウント作成できます
無料サインアップ ≫
ご不明な点がおありですか?
日本担当チームへ相談する