Image Builder は、カスタマイズしたクラウドイメージを構築するために、Red Hat が提供する新しいホスト型サービスです。このブログ記事では、コマンドラインで Image Builder を使用する方法を説明します。

Image Builder とは

Image Builder は Red Hat Hybrid Cloud Console のホスト型サービスで、カスタマイズイメージを素早く組み立てることができます。このサービスを使用することで、AWS、Azure、Google Cloud など、最も一般的に使用されているハイパースケーラーのいずれにも、イメージを同時にアップロードできるため、ハイブリッドクラウドソリューションを迅速に開発できます。Image Builder サービスの詳細は、こちらの ブログ記事 を参照してください。このサービスにアクセスするには、無料の開発者向けサブスクリプションを利用できます。このサブスクリプションについては、こちらの ブログ記事 を参照してください。

Image Builder をプログラムで使用する理由

Red Hat のグラフィカルインタフェース (図 1) はできるだけ簡単に使用できるようにしていますが、数十件または数千件のイメージを作成する予定の場合は、Image Builder API を使用して、イメージ構築で必要な作業をすべて自動化することをお勧めします。

Image builder user interface

図 1: Image Builder のユーザーインタフェース

認証

Image Builder は、認証に OAuth 2.0 を使用します。まず、このページ で Red Hat API のオフライントークを生成する必要があります。このトークンは Image Builder で直接使用できないため、第 2 段階として、このトークンをアクセストークンに交換する必要があります。「curl」を使用するだけで、アクセストークンの交換が可能です。ただし、このコマンドを実行する前に、OFFLINE_TOKEN という名前の変数に、オフライントークンを保存することを忘れないでください。

$ OFFLINE_TOKEN=”YOUR_OFFLINE_TOKEN”
$ curl --silent \
    --request POST \
    --data grant_type=refresh_token \
    --data client_id=rhsm-api \
    --data refresh_token=$OFFLINE_TOKEN \
    https://sso.redhat.com/auth/realms/redhat-external/protocol/openid-connect/token \
  | jq .

このコマンドを実行すると、以下のような出力が表示されます。

{
  "access_token": "oiZjo1Mjhk...",
  "expires_in": 900,
  "refresh_expires_in": 0,
  "refresh_token": "eyJhbG...",
  "token_type": "bearer",
  "not-before-policy": 0,
  "session_state": "f0dbb8d4-4e4e-4654-844c-6f3704c84422",
  "scope": "offline_access"
}

jq (jq がインストールされていない場合は、sudo dnf install -y jq を実行) を使用して JSON ペイロードから実際のアクセストークンを取得し、このトークンを以下のスニペットを使って変数に保存します。

$ access_token=$( \
    curl --silent \
      --request POST \
      --data grant_type=refresh_token \
      --data client_id=rhsm-api \
      --data refresh_token=$OFFLINE_TOKEN \

https://sso.redhat.com/auth/realms/redhat-external/protocol/openid-connect/token \
    | jq -r .access_token \
  )

アクセストークンには有効期限があることに注意してください。Image Builder が認証エラーを返す場合には、コマンドを再度実行して新しいトークンを取得します。

Red Hat API および OAuth の詳細は、このページ を参照してください。

API ドキュメントの取得

イメージを構築するには、Image Builder API を使用します。便利なことに、Red Hat Hybrid Console にはすべての API のドキュメントが組み込まれています。Image Builder のドキュメントは、このページ で確認できます。

ただし、このブログ記事では CLI に焦点を当てているため、簡単な curl コマンドを使用して、コマンドラインから API ドキュメントを取得する方法についても紹介します。

$ curl --silent \
    --header "Authorization: Bearer $access_token" \
    https://console.redhat.com/api/image-builder/v1/openapi.json \
  | jq .

このコマンドを使用すると、JSON でエンコードされた OpenAPI 3 仕様形式の API ドキュメントが出力されます。

Red Hat のコードも、Image Builder もオープンであるため、 アップストリームのリポジトリ から YAML 形式の仕様を取得することもできます。

単純な KVM ゲストイメージの構築

これで、イメージの構築を開始する準備が整いました。では、基本的な内容から始めます。x86_64 CPU アーキテクチャ用の最新の RHEL 9.0 ゲストイメージを libvirt や OpenStack で利用できるように構築します。

まず、compose 要求を作成する必要があります。

$ cat >request.json <<END
{
  "image_name": "My up-to-date guest image",
  "distribution": "rhel-90",
  "image_requests": [
    {
      "architecture": "x86_64",
      "image_type": "guest-image",
      "upload_request": {
        "type": "aws.s3",
        "options": {}
      }
    }
  ]
}
END

最も重要なフィールドは、ディストリビューション、CPU、アーキテクチャ、イメージタイプです。これらの情報を指定して、上述の RHEL 9.0 x86_64 ゲストイメージを構築します。アップロード要求のタイプは、API で必要とされるとおり aws.s3 に設定します。イメージには任意の名前を付けることもできますが、この例ではイメージに「My up-to-date guest image」という名前を指定します。

これで要求ボディの作成が完了しました。次に、要求を Image Builder API に送信します。

$ curl --silent \
    --request POST \
    --header "Authorization: Bearer $access_token" \
    --header "Content-Type: application/json" \
    --data @request.json \
    https://console.redhat.com/api/image-builder/v1/compose

すべてが正常に進むと、以下のような出力が表示されるはずです。

{"id":"fd4ecf3c-f0ce-43dd-9fcc-6ad11208b939"}

イメージ構築のステータス確認にこの ID が必要となるため、jq を活用して、変数に直接、この ID を投入することをお勧めします。

$ compose_id=$( \
    curl --silent \
      --request POST \
      --header "Authorization: Bearer $access_token" \
      --header "Content-Type: application/json" \
      --data @request.json \
      https://console.redhat.com/api/image-builder/v1/compose \
    | jq -r .id \
  )

Red Hat Hybrid Cloud Console を見ると、図 2 に示されているように、イメージが構築されていることを確認できるはずです。

Red Hat Hybrid Cloud Console showing a in-progress image build

図 2: イメージが構築中であることを示す Red Hat Hybrid Cloud Console

イメージの構築には通常数分かかるため、以下の呼び出しを使用してステータスを確認できます。

$ curl \
    --silent \
    --header "Authorization: Bearer $access_token" \
    "https://console.redhat.com/api/image-builder/v1/composes/$compose_id" \
  | jq .

compose の作成直後にこの呼び出しを実行すると、出力として以下のメッセージが表示されます。

{
  "image_status": {
    "status": "building"
  }
}

しばらくすると、返されるメッセージは以下のように変わります。

{
  "image_status": {
    "status": "success",
    "upload_status": {
      "options": {
        "url": "https://image-builder-service-production.s3.amazonaws.com/composer-api-76...-disk.qcow2?e42..."
      },
      "status": "success",
      "type": "aws.s3"
    }
  }
}

イメージの準備ができました。これで、以下のコマンドを使用してイメージをダウンロードできるようになりました。

$ curl --location --output guest-image.qcow2  \
    “https://image-builder-service-production.s3.amazonaws.com/composer-api-76...-disk.qcow2?e42...”

libvirt を使用してローカルで実行したり、OpenStack にアップロードしたりできます。S3 リンクの有効期限は現在 6 時間であるため、次の日にリンクが機能しなくなっていても心配しないでください。

AWS のカスタマイズイメージの構築

前述の例は、常に新しい最新のイメージを提供するので便利です。次は、より複雑な内容を試してみましょう。AWS を対象にして、イメージへの別のパッケージの追加、パーティションレイアウトのカスタマイズ、アクティベーションキーの埋め込みを行い、サブスクリプションについて心配する必要がないようにします。

認証や OpenAPI 仕様についてはすべて把握してると思いますので、その点は飛ばし、直接、要求の操作に進みます。

$ cat >request.json <<EOF
{
  "image_name": "My customized AMI",
  "distribution": "rhel-86",
  "image_requests": [
    {
      "architecture": "x86_64",
      "image_type": "aws",
      "upload_request": {
        "type": "aws",
        "options": {
          "share_with_accounts": [
            "438669297799"
          ]
        }
      }
    }
  ],
  "customizations": {
    "packages": [
      "nginx"
    ],
    "filesystem": [
      {
        "mountpoint": "/var",
        "min_size": 10737418240
      }
    ],
    "subscription": {
      "organization": 123456789,
      "activation-key": "toucan",
      "server-url": "subscription.rhsm.redhat.com",
      "base-url": "http://cdn.redhat.com/",
      "insights": true
    }
  }
}
EOF

では、要求に加える変更点について確認しましょう。

まず、RHEL 8.x にも対応していることを示すため、ディストリビューションを RHEL 8.6 に切り替えました。イメージ要求でイメージのタイプが、aws に変更され、アップロード要求でもこの変更が反映されています。ホスト型の Image Builder は、イメージを Red Hat の AWS アカウントにアップロードしてからイメージを共有するため、share_with_accounts フィールドに入力し、どのアカウントを使用してイメージを共有するのかを Image Builder に指示する必要があります。なお、このイメージは 14 日後に消えるため、長期間使用する場合にはコピーを作成する必要があります。

以前の要求と比較すると、大きい customizations オブジェクトが追加されています。

  • nginx がイメージにプリインストールされます。

  • ルートパーティションのサイズは 4 GiB になります。/var は、サイズが 10 GiB の大きいパーティションに別に配置されます。

  • イメージからインスタンスを起動すると、指定のアクティベーションキーを使用して自動的にサブスクライブされます。アクティベーションキーの作成方法が分からない場合は、その作成プロセスに関する 記事 があります。

では要求を送信してみましょう。

$ compose_id=$( \
curl --silent \
   --request POST \
   --header "Authorization: Bearer $access_token" \
   --header "Content-Type: application/json" \
   --data @request.json \
   https://console.redhat.com/api/image-builder/v1/compose \
   | jq -r .id \
  )

次に、そのステータスの確認を開始します。

$ curl --silent \
      --header "Authorization: Bearer $access_token" \
      "https://console.redhat.com/api/image-builder/v1/composes/$compose_id" \
  | jq .

イメージのビルドが終了すると、以下の応答が表示されます (AWS へのインポートに数分かかる傾向があるため、時間が余分にかかる場合があります)。

{
  "image_status": {
      "status": "success",
      "upload_status": {
      "options": {
      "ami": "ami-01f2c869485288e5e",
      "region": "us-east-1"
      },
      "status": "success",
      "type": "aws"
      }
  }
}

Red Hat Hybrid Cloud Console で同じイメージを開くと、図 3 と同様の内容が表示されます。

Red Hat Hybrid Cloud Console showing a finished AWS image build

図 3:完了した AWS イメージビルドを表示する Red Hat Hybrid Cloud Console

AWS での新規イメージの起動

コンソールのリンクを使用して、新しいイメージを起動できます。ただし、これはコマンドラインに関する記事なので、AWS CLI を使ってインスタンスを起動し、そのパブリック IP アドレスを確認してみましょう。

$ aws --region us-east-1 \
    ec2 run-instances \
    --image-id ami-01f2c869485288e5e \
    --key-name KEY_NAME
$ aws --region us-east-1 \
    ec2 describe-instances \
    --instance-ids INSTANCE_ID \
    --query 'Reservations[*].Instances[*].PublicIpAddress' \
    --output text
3.83.39.87

ssh ec2-user@3.83.39.87 を使用してインスタンスに SSH 接続すると、インスタンスには要求したディスクレイアウトが設定され、nginx がインストール済みで、マシンがサブスクライブされているため新しいパッケージをすぐにインストールできる状態であることが確認できます。

$ lsblk
NAME              MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
xvda              202:0    0   15G  0 disk 
|-xvda1           202:1    0    1M  0 part 
|-xvda2           202:2    0   14G  0 part 
| |-rootvg-rootlv 253:0    0    4G  0 lvm  /
| `-rootvg-varlv  253:1    0   10G  0 lvm  /var
`-xvda3           202:3    0  512M  0 part /boot
xvdc              202:32   0  896M  0 disk [SWAP]
$ rpm -q nginx
nginx-1.14.1-9.module+el8.0.0+4108+af250afe.x86_64
$ sudo dnf install tmux
Updating Subscription Management repositories.
Red Hat Enterprise Linux 8 for x86_64 - BaseOS (RPMs)                                  6.7 MB/s |  47 MB     00:07    
Red Hat Enterprise Linux 8 for x86_64 - AppStream (RPMs)                                10 MB/s |  44 MB     00:04    
Last metadata expiration check: 0:00:03 ago on Fri May 27 12:25:55 2022.
Dependencies resolved.
=======================================================================================================================
 Package             Architecture          Version                  Repository                                    Size
=======================================================================================================================
Installing:
 tmux                x86_64                2.7-1.el8                rhel-8-for-x86_64-baseos-rpms                317 k

Transaction Summary
=======================================================================================================================
Install  1 Package

Total download size: 317 k
Installed size: 770 k
Is this ok [y/N]:

デフォルトの VPC、デフォルトのサブネットまたはデフォルトのセキュリティグループがここで提示している内容と異なる場合、AWS CLI に関するこのセクションは機能しない可能性があります。

Ansible を使用した EC2 インスタンスの設定

インスタンスが実行されると、Ansible を使用して Image Builder の機能以外を設定できます。以下の例では、簡単な cron ジョブを追加して、新しいホスト名を設定します。まず、(前のセクションで取得した IP アドレスを使用して) インベントリーファイル と Playbook を作成する必要があります。

$ cat >hosts <<EOF
[webservers]
3.83.39.87
EOF
$ cat >playbook.yml <<EOF
---
- name: Set up web servers
  hosts: webservers
  remote_user: ec2-user

  tasks:
    - name: Log free space
      ansible.builtin.cron:
       name: "log free space"
       hour: "3"
       job: "df -h"

    - name: Set hostname
      ansible.builtin.hostname:
         name: nginx-webserver
      become: true
EOF

次に、以下のコマンドを使用して Playbook を実行できます。

$ ansible-playbook -i hosts playbook.yml

PLAY [Set up web servers] *******************

TASK [Gathering Facts] **********************
ok: [3.83.39.87]

TASK [Log free space] ***********************
changed: [3.83.39.87]

TASK [Set hostname] *************************
changed: [3.83.39.87]

PLAY RECAP **********************************
3.83.39.87           : ok=3 changed=2

このように、Image Builder からデプロイしたインスタンスで、Ansible の使用を簡単に開始できます。Red Hat Ansible Automation Platform に関する詳細情報が必要な場合は、こちらの ドキュメント をご覧ください。

イメージの設定方法としてもう 1 つ `cloud-init` を使用する方法があります。このツールの包括的なドキュメントは、Red Hat Enterprise Linux ドキュメント にあります。

まとめ

API を経由すると、ホスト型の Image Builder は非常に簡単に使用できます。curl コマンドを数回実行するだけで、任意のクラウドに最新のカスタマイズ可能な RHEL イメージをすばやく取得できます。ぜひ、今日から使い始めてみてください


About the author

Ondřej is a Senior Software Engineer at the image builder team at Red Hat. His mission is to make image building as simple as possible for all customers. In his free time, he enjoys cooking and running.

Read full bioIcon-Red_Hat-Directional-A-Black-RGB