Red Hat - Open Innovation Labsのブログ - TECH PLAY

TECH PLAY

Red Hat  - Open Innovation Labs

Red Hat - Open Innovation Labs の技術ブログ

全900件

Red Hatの小島です。 OpenShift Container Platform(以下、OpenShiftと記述)にはアプリケーションの開発支援機能が備わっており、Gitリポジトリにあるアプリケーションのソースコードをもとに、OpenShiftでのコンテナアプリケーションのビルドとデプロイ、および外部公開用のURL作成を自動化することができます。そうした機能の具体的な利用例を記載したワークショップコンテンツがありますので、本記事ではそれらのコンテンツをご紹介していきます。 サンプルアプリケーションのデプロイ Web IDEを利用した開発 Amazon Bedrockアプリケーションの実行 まとめ サンプルアプリケーションのデプロイ OpenShiftでは、標準提供している様々な言語/ミドルウェア/データベースなどのカタログにあるサンプルソースコードを利用して、コンソールからアプリケーションを簡単にデプロイできるようになっています。下記のコンテンツではNode.jsのサンプルアプリケーションの作成や削除方法に加えて、OpenShiftで実行するNode.jsアプリケーションの開発ガイドへのリンクもご紹介しています。 また、アプリケーションのコンソール接続テストのための、OpenShift環境のドメイン名を利用したアプリケーションURLの変更方法もご紹介しています。 rh-open.github.io Web IDEを利用した開発 OpenShiftでは、Dev SpacesというVisual Studio CodeのようなWeb IDEを標準で利用できるようになっています。下記のコンテンツではWeb IDEのJavaに関するカタログを利用して、Javaアプリケーションの開発方法をご紹介しています。 また、開発したJavaアプリケーションのソースコードが置かれたGitHubリポジトリのURLを指定して、OpenShiftへのアプリケーションの自動ビルドとデプロイ方法も紹介しています。この例では、以下のファイルが置かれたGitHubリポジトリを指定します。 Javaアプリケーションのソースコード pom.xml GitHubリポジトリに置かれたこれらのファイルにはOpenShift特有のものは含まれておらず、OpenShiftの標準機能によってJavaアプリケーションの自動ビルドとデプロイが実行されます。 rh-open.github.io Amazon Bedrockアプリケーションの実行 サンプルのPythonアプリケーションによる、Amazon BedrockというAWSの生成AIサービスの利用方法をご紹介しています。Dockerfileに記載された手順に従ってPythonアプリケーションの自動ビルドとデプロイをOpenShiftで実行するために、以下のファイルが置かれたGitHubリポジトリを利用しています。 app.py (Pythonアプリケーションのメインファイル) requirements.txt Dockerfile rh-open.github.io まとめ ここまでご紹介してきたコンテンツは、アプリケーションの自動ビルドとデプロイの実行を体感していただくと共に、「自動ビルドとデプロイの実行時に、ソースコードとして最低限必要なものは何か」を理解していただくためのコンテンツともなっています。これらのコンテンツを、OpenShiftでのアプリケーション開発の基礎を学習する際の参考にしてください。
Red Hat で Quarkus を担当している伊藤ちひろです。 この記事では、PC 上で LLM に接続するアプリケーションの開発の流れを紹介します。 今回は、LLM としてGraniteを使用し、そこへアクセスするアプリケーションは Quarkus を使用して開発します。 このGraniteのモデルはInstructLabでチューニングされたものを使用しています。 Graniteのモデルは Podman Desktop 上に構築しています。 最終的に完成するアプリはこちらです。 質問すると、AIっぽい返答がもらえます。 Podman の AI 拡張機能をインストール まずは、Podman Desktop に AI の拡張機能をインストールします。 左のメニューのパズルのポースのようなアイコンを押して、Catalog (カタログ) を選択すると、 Podman AI Lab というのが見つかります。 Podman AI Lab の右側にある下向き矢印(↓)をクリックして、 Podman AI Lab をインストールします。 Podman AI Lab のインストールが終わると、左側のメニューに Podman AI Lab のアイコンが表示されます。 Graniteをインストール 次に、Graniteをインストールします。 Podman AI Lab の Catalog を選択すると、使用可能なモデルの一覧が表示されます。 今回は一番への instructlab/granite を使用します。 右側にある下向き矢印(↓)をクリックしてダウンロードしましょう。 ダウンロードが終わると、 instructlab/granite のアイコンが黄緑になります。 それでは instructlab/granite を起動しましょう。 右側のロケットのアイコンをクリックすると、モデルのサービスを作成する画面に移動します。 設定できる項目はコンテナのポートのみです。 Create service ボタンをクリックすると、モデルのサービスが作成されます。 ここまでで、Podman 上に モデルが構築できました。 最後に、実際に、モデルへ問い合わせしてみましょう。 モデルのサービスが作成されると、そのサービスへアクセスする方法が表示されます。 デフォルトではcURLを使ったアクセス方法が表示されています。 cURL のある環境から次のコマンドを実行してください。 レスポンスは JSON 形式のため、最後に jq コマンドを追加すると良いでしょう。 > curl --location 'http://localhost:35000/v1/chat/completions' --header 'Content-Type: application/json' --data '{ "messages": [ { "content": "東京の首都はどこですか?", role": "user" } ] }' | jq レスポンスは以下のようになります。結果の文は実行のたびに異なります。 { " id ": " chatcmpl-c6572f33-52bc-41bb-a200-27cc7d88f73c ", " object ": " chat.completion ", " created ": 1718946977 , " model ": " /models/granite-7b-lab-Q4_K_M.gguf ", " choices ": [ { " index ": 0 , " message ": { " content ": " 東京は日本の首都です。また、世界の最大の都市です。 ", " role ": " assistant " } , " logprobs ": null , " finish_reason ": " stop " } ] , " usage ": { " prompt_tokens ": 66 , " completion_tokens ": 34 , " total_tokens ": 100 } } Quarkus からモデルへ接続 Podman AI Lab は、cURLだけでなく、Quarkus からモデルへ接続する方法も提供します。 ドロップダウンリストを Java と Quarkus Langchain4J に変更します。 これによって、モデルへ接続する為に必要な設定やコードが出力されます。 ここで変更するファイルは以下の 3 つです。 pom.xml src/main/resources/application.properties src/main/java/io/podman/desktop/quarkus/langchain4j/AiService.java 最初の 2 つはすでにあるものに追記します。 最後のファイルは新たに追加します。 これでQuarkus がモデルへ接続する準備はできました。 Quarkus アプリとの統合 最後に、Quarkusアプリからモデルへ接続してみましょう。 今回は、Qute を使用して画面を作成します。 pom.xml に Qute の依存関係を追加します。 pom.xml <dependency> <groupId> io.quarkus </groupId> <artifactId> quarkus-rest-qute </artifactId> </dependency> 画面を作成します。 src/resources/templates/ai.html <!DOCTYPE html> < html > < head > < meta charset = "UTF-8" > < title > Quarkus + Granite </ title > </ head > < body > < h1 > Quarkus + Granite Demo </ h1 > < form action = "/ai/query" method = "get" > < div > < label for = "question" > 質問を入力してください </ label >< br /> < input type = "text" style = "width: 500px" name = "question" id = "question" value = "{question ?: ''}" required /> < input type = "submit" value = "送信" /> </ div > </ form > < p > {answer ?: ''} </ p > </ body > </ html > 最後に、画面とモデルの橋渡しをする部分を作成します。 src/main/java/ai/AiResource.java package ai; import io.podman.desktop.quarkus.langchain4j.AiService; import io.quarkus.qute.Template; import io.quarkus.qute.TemplateInstance; import jakarta.inject.Inject; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import jakarta.ws.rs.QueryParam; import jakarta.ws.rs.core.MediaType; @Path ( "/ai" ) public class AiResource { @Inject Template ai; @Inject AiService aiService; @GET @Produces (MediaType.TEXT_HTML) public TemplateInstance index() { return ai.instance(); } @GET @Path ( "/query" ) @Produces (MediaType.TEXT_HTML) public TemplateInstance get( @QueryParam ( "question" ) String question) { String answer = aiService.request(question); return ai.data( "question" , question).data( "answer" , answer); } } これで完成です。 きちんと動くか試してみましょう。 Quarkus アプリケーションを起動して、 https://localhost:8080/ai にアクセスします。 テキストフィールドに、質問を入力して「送信」ボタンを押してみましょう。 返事が下に表示されれば成功です。 10秒以上かかるとタイムアウトします。 タイムアウトの時間を延ばすにはapplication.propertiesに quarkus.langchain4j.openai.timeout=30s のように追加してください。
Red Hatでソリューションアーキテクトをしている田中司恩( @tnk4on )です。 前回記事の続きになります。前回記事ではRed Hat build of Keycloak(RHBK)のサポート構成やPodmanを使ったテスト実行について紹介しました。 今回はPodmanで実行したコンテナレジストリのユーザー認証基盤としてRed Hat build of Keycloakを使う方法を紹介します。 (前回記事はこちら) rheb.hatenablog.com -目次- Distribution Registryとは Distribution Registryのコンテナイメージ Red Hat build of KeycloakをDistribution Registryの認証基盤として利用する Red Hat build of Keycloakの実行 コンテナレジストリ向けの認証設定 Distribution Registryの実行 PodmanからDistribution Registryを利用する ログイン/ログアウト コンテナイメージのPushとPull まとめ Distribution Registryとは Distributionプロジェクトのロゴ かつてPythonで書かれた「Docker Registry」 *1 というOSSプロジェクトがありました。 Docker Hubやその他のコンテナレジストリの基礎となるOSSプロジェクトでしたが、その後のコンテナエコシステムの標準化の流れでCNCFに寄贈されました。 詳細はDocker社のブログ記事を参照ください。 Donating Docker Distribution to the CNCF | Docker このプロジェクトはCNCFに寄贈された以降は「Distribution」に名称が変更されました。このDistributionプロジェクトの中のコンポーネントの1つが「Distribution Registry」で、現在はGoで書き直されています。このDistribution RegistryはOCI Distribution spec *2 に基づいて実装が行われています。 DistributionプロジェクトのWebサイトとGitHubリポジトリ CNCF Distribution GitHub - distribution/distribution: The toolkit to pack, ship, store, and deliver container content ネットのさまざまなところではDocker Registryという名前を見ることが多いですが、その情報はすでに古く、Distribution Registryまたは単にRegistryと呼ぶのが正しいです。この際に覚えておきましょう。 Distribution Registryのコンテナイメージ このDistribution Registryはコンテナイメージが公開されていますので、すぐにコンテナ環境で実行できます。 なお、Docker HubにあるRegistryのDocker公式イメージはDistribution Registryのコンテナイメージです。 これは「Docker Hubにホストされている Distribution Registry 」であり「 Docker Registry 」ではありませんのでお間違えないように。 registry - Official Image | Docker Hub podman run コマンドで実行する場合はこんな感じです。これだけでローカル環境にシンプルなコンテナレジストリが立ち上がります。 % podman run -d -p 5000:5000 --name registry docker.io/library/registry:2 registry:2 の2となっているのは現在のDistribution Registryのメジャーバージョンのv2をタグとして指定しています。なお現時点のDistribution Registryの最新版はv2.8.3ですが、2、2.8、2.8.3はすべて同じイメージを指すようにタグ付けされています。 保管するコンテナイメージを永続化したい場合はコンテナ内の /var/lib/registry にバインドマウントします。 % mkdir registry % podman run -d -p 5000:5000 --name registry -v ./registry:/var/lib/registry:Z docker.io/library/registry ローカル環境でテスト的に使いたい場合ははこれくらいを覚えておけば基本的には十分です。もう少し高度に使いたい場合は環境変数を追加して実行できます。 Distribution Registryのドキュメントにいくつか例があるので参考にしてください Deploy a registry server | CNCF Distribution 次のセクションではRed Hat build of Keycloakを認証基盤として利用する方法を紹介します。 Distribution Registryは別の手段で実行しますので、テストで起動したコンテナは削除しておいてください。 % podman rm -f -t 0 registry Red Hat build of KeycloakをDistribution Registryの認証基盤として利用する Distribution Registry自体にはユーザーを作成したり管理する機能はありません。その代わりにsilly、token、htpasswdの認証プロバイダをサポートしています。 今回はRed Hat build of Keycloakをtoken方式の認証基盤として利用します。 Red Hat build of KeycloakをDistribution Registryの認証基盤として利用する 実行するコンテナと公開ポートの構成はこちらです。 実行するコンテナと公開ポートの構成 Red Hat build of Keycloakの実行 ここから構築方法について解説します。製品ドキュメントは下記を元にしていますが、ドキュメントの内容だけでは情報が足りないので本記事の内容をしっかりと確認してください。 第4章 Red Hat build of Keycloak を使用するための Docker レジストリー設定 | Red Hat Product Documentation 作業の順番は下記の通りです 管理者ユーザーでログインする 新規のレルムを作成する 新規のクライアントを作成する Docker Compose YAMLをダウンロードする 新規ユーザーを作成する 不要な属性の必須設定をオフにする まずは前回と同じく podman run コマンドでRed Hat build of Keycloakを起動します。今回の構築用に若干オプションを変更しています。 Red Hat build of Keycloakのコンテナイメージの取得にはregistry.redhat.ioへのログインが必要です。 Red Hat build of Keycloakが含まれる製品の有効なサブスクリプションをお持ちのRed Hatアカウント、または評価版を使用中のRed Hatアカウント、等が必要です % podman login registry.redhat.io % podman run -d -p 8080:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin --name keycloak -e KC_FEATURES=docker keycloak-rhel9:24-10 start-dev -d :デタッチモードでバックグラウンド実行する --name keyclaok :コンテナに keycloak という名前をつける -e KC_FEATURES=docker :コンテナ実行時に環境変数「 KC_FEATURES 」を追加し、パラーメーターを docker にセットする 今回使用するDocker認証はデフォルトでは無効化されています。そのため、Red Hat build of Keycloakの起動時に有効化するオプションを追加します。 この環境変数 KC_FEATURES については製品ドキュメントにチラッとだけ出てきます。製品ドキュメントではほとんど解説がないのでご注意ください。 3.8. 関連するオプション | Red Hat Product Documentation podman run コマンドで実行した後に podman logs コマンドでコンテナ名を指定してRed Hat build of Keycloakが起動したかどうか確認します。 正常に起動すると下記のようなログが出力されます。 % podman run -d -p 8080:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin --name keycloak -e KC_FEATURES=docker keycloak-rhel9:24-10 start-dev % podman logs -f keycloak ... 2024-06-14 01:35:44,461 INFO [io.quarkus] (main) Keycloak 24.0.5.redhat-00001 on JVM (powered by Quarkus 3.8.4.redhat-00002) started in 16.594s. Listening on: http://0.0.0.0:8080 2024-06-14 01:35:44,462 INFO [io.quarkus] (main) Profile dev activated. 2024-06-14 01:35:44,462 INFO [io.quarkus] (main) Installed features: [agroal, cdi, hibernate-orm, jdbc-h2, keycloak, narayana-jta, reactive-routes, resteasy-reactive, resteasy-reactive-jackson, smallrye-context-propagation, vertx] 2024-06-14 01:35:44,468 WARN [org.keycloak.quarkus.runtime.KeycloakMain] (main) Running the server in development mode. DO NOT use this configuration in production. コンテナレジストリ向けの認証設定 ここからはRed Hat build of Keycloakの管理画面での操作になります。 まず、Red Hat build of Keycloakのコンテナ起動時に指定した管理者ユーザーのユーザー名とパスワードでログインします。ブラウザで localhost:8080 へアクセスします。 管理者ユーザーでログインする 管理者ユーザーでログイン直後は master レルムが表示されます。レルムとはKeycloakにおけるマルチテナント機能のようなものです。 master はサーバー自体の管理ができるレルムになります。 アプリケーション用途には別のレルムを作ってそちらを使います。 ここではServer infoのタブを開きDocker機能が有効化されているか確認します。Enable featuresにDockerが見つかれば正常に有効化できています。 masterレルム Server infoでDocker機能の有効化を確認する 次に新規でレルムを作成します。Red Hat build of Keycloakと書いてあるプルダウンメニューを選択しCreate realmを押します。 新規でレルムの作成を実行する レルムの作成画面ではレルム名を入力してCreateを押します。ここではレルム名は「registry」にします。 レルム名を入力して作成を実行する レルムの作成が成功するとプルダウンメニューの表示が作成したレルム名(registry)に変更されます。 左のClientsメニューを選択します。 Clientsの一覧画面 新規でクライアントを作成します。Client typeはプルダウンから「docker-v2」を選択します。Client IDには任意の名前を入力します。ここでは「docker-auth」とします。「Next」を押します。 クライアントとはユーザーの認証を要求できるエンティティーのことでKeycloakに接続するアプリケーションの管理単位です。コンテナレジストリにログインする際のユーザー(後ほど作成)とは別の管理単位になりますので混同しないようにご注意ください クライアントの作成。Client typeはdocker-v2を選択する。 次のページのCapability configは空白なのでそのまま「Save」を押します。 Capability configは空白のままSaveを押す さらに次のLogin settingsもそのまま「Save」を押します。 Login SettingsもそのままSaveを押す クライアント「docker-auth」が作成されました。 新規クライアント「docker-auth」の作成完了 クライアント「docker-auth」のActionプルダウンから「Download adapter config」を選択します。 Download adapter configを選択する Download adaptor configsの画面でFormat optionが「Docker Compose YAML」になっていることを確認し「Download」を押します。 Docker Compose YAMLをダウンロードする ダウンロードしたファイルは「keycloak-docker-compose-yaml.zip」として保存されます。これは後でDistribution Registryの実行時に使用します。 次はコンテナレジストリへログインするときに使用するユーザーを作成します。 左のメニューからUsersを選択し、「Create new user」を押します。 新規ユーザーの作成 ユーザー名を入力し「Create」を押します。ここではユーザー名「tnk4on」を指定します。 ユーザー名を入力しユーザーの作成を完了する ユーザーのパスワードを作成します。User datailsの画面からCredentialsタブを選択し、「Set password」選択します。 Credentialsの作成 パスワードを入力し、Temporaryのチェックを外し「Save」を押します。 パスワードを入力しTemporaryのチェックを外す もう一度確認画面が出てくるので「Save password」押します。これでユーザーのパスワードがセットされました。 再度確認画面でSave passwordを押す コンテナレジストリの認証に不要な email , firstName , lastName 要素の必須設定をオフにします。 左のメニューからRealm settingsを選択し、User Profileタブを選択します。ますはemailのリンクを選択します。 不要な属性の必須設定をオフにする Required fieldをオフにし「Save」を押します。残りの firstName , lastName も同様に行います。この設定が email , firstName , lastName すべてでオフになっていないと podman login を実行したときにエラーになります。ご注意ください。 Required fieldをオフにして保存する Distribution Registryの実行 先ほどの手順でダウンロードしたファイル「keycloak-docker-compose-yaml.zip」を展開すると下記のファイル一覧が出てきます。 % tree . ├── README.md ├── certs │ ├── localhost.crt │ ├── localhost.key │ └── localhost_trust_chain.pem ├── data └── docker-compose.yaml 3 directories, 5 files docker-compose.yamlを使ってDistribution Registryを起動するのですが、このままではComposeの実行時でエラーになります。 <ダウンロードしたdocker-compose.yaml> registry: image: registry:2 ports: - 127.0.0.1:5000:5000 environment: REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data REGISTRY_HTTP_TLS_CERTIFICATE: /opt/certs/localhost.crt REGISTRY_HTTP_TLS_KEY: /opt/certs/localhost.key REGISTRY_AUTH_TOKEN_REALM: http://localhost:8080/realms/registry/protocol/docker-v2/auth REGISTRY_AUTH_TOKEN_SERVICE: docker-auth REGISTRY_AUTH_TOKEN_ISSUER: http://localhost:8080/realms/registry REGISTRY_AUTH_TOKEN_ROOTCERTBUNDLE: /opt/certs/localhost_trust_chain.pem volumes: - ./data:/data:z - ./certs:/opt/certs:z 正常に起動する修正を加えたのが下記です。 <修正を加えたdocker-compose.yaml>> version: '3' services: registry: image: registry:2 ports: - 127.0.0.1:5000:5000 environment: REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY: /data REGISTRY_HTTP_TLS_CERTIFICATE: /opt/certs/localhost.crt REGISTRY_HTTP_TLS_KEY: /opt/certs/localhost.key REGISTRY_AUTH_TOKEN_REALM: http://localhost:8080/realms/registry/protocol/docker-v2/auth REGISTRY_AUTH_TOKEN_SERVICE: docker-auth REGISTRY_AUTH_TOKEN_ISSUER: http://localhost:8080/realms/registry REGISTRY_AUTH_TOKEN_ROOTCERTBUNDLE: /opt/certs/localhost_trust_chain.pem volumes: - ./data:/data:z - ./certs:/opt/certs:z ではこのdocker-compose.yamlファイルを使ってDistribution Registryのコンテナを実行します。 Podman v4.7.0以降は podman compose コマンドが実装されています。 podman compose コマンドは、ローカルホストにインストールされた docker-compose コマンドを参照実行するラッパーコマンドです。 本家の docker-compose コマンドをインストールし、 docker.sock から podman.sock へシンボリックリンクを作成するだけでPodmanで docker-compose がそのまま使えます。 詳細は拙作の著書「 Podman Advanced Pod-01:DockerからPodmanへの移行 」を参照ください。 podman compose コマンドを -d オプションをつけて実行します。 % cd keycloak-docker-compose-yaml % podman compose up -d >>>> Executing external compose provider "/usr/local/bin/docker-compose". Please refer to the documentation for details. <<<< [+] Running 1/1 ✔ Container keycloak-docker-compose-yaml-registry-1 Started podman ps コマンドを実行し、 keycloak-docker-compose-yaml-registry-1 というコンテナが正常に起動していれば成功です。 % podman ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES e3ed196f344b registry.redhat.io/rhbk/keycloak-rhel9:24-10 start-dev 15 minutes ago Up 15 minutes 0.0.0.0:8080->8080/tcp, 8080/tcp, 8443/tcp keycloak 54f837e4c325 docker.io/library/registry:2 /etc/docker/regis... 12 seconds ago Up 13 seconds 127.0.0.1:5000->5000/tcp, 5000/tcp keycloak-docker-compose-yaml-registry-1 PodmanからDistribution Registryを利用する コンテナレジストリを利用する環境が整ったのでPodmanを使って各種操作を行います。 ログイン/ログアウト コンテナレジストリにログインするには podman login コマンドを使います。注意点としては、実稼働環境のコンテナレジストリでは https:// でアクセスを行いますが今回はテスト環境なので http:// でアクセスを行います。 http:// でアクセスする場合は podman login コマンドに --tls-verify=false オプションを追加して実行します。 % podman login localhost:5000 --tls-verify=false ユーザー名とパスワードを入力し、 Login Succeeded! が出ればログイン成功です。 % podman login localhost:5000 --tls-verify=false Username: tnk4on Password: Login Succeeded! ログイン済みのコンテナレジストリからログアウトをする場合は podman logout コマンドを使います。ログアウトの場合 --tls-verify=false オプションは不要です。 Removed login credentials for が表示されればログアウト成功です。ローカルに保存されていた認証情報は削除されます。 % podman logout localhost:5000 Removed login credentials for localhost:5000 コンテナイメージのPushとPull コンテナレジストリにログインし、そのユーザー専用のリポジトリとしてコンテナをPushしてみます。 サンプルでビルドしたコンテナをコンテナレジストリにPushします。 podman push コマンドの実行の場合も --tls-verify=false をつけて実行します。 % cat > Containerfile <<EOF FROM ubi9 RUN echo "hello" | tee hello.txt EOF % podman build -t registry-test . % podman login localhost:5000 --tls-verify=false Username: tnk4on Password: Login Succeeded! % podman push registry-test localhost:5000/tnk4on/registry-test --tls-verify=false Getting image source signatures Copying blob sha256:32c72c1609374470f063a4a8d190512eedd824b0aa644575c3a75d0e466a6426 Copying blob sha256:0abe35b0d2bf06d260396dfc129715dae9736b6c91f3d861492982a0f7daa5c2 Copying config sha256:d7d811c318f6237a9dbc97dc15fc31cc92605b0c2166a4b66b4bdfbde727ea1f Writing manifest to image destination コンテナがコンテナレジストリに登録されたかを確認します。Skopeoを使ってコンテナレジストリ上のコンテナイメージの詳細を確認します。 skopeo inspect コマンドを実行します。コンテナレジストリ上のコンテナを直接調べる場合は docker:// (Dockerトランスポート)を指定します。またこの場合も --tls-verify=false オプションを追加します。 Linux以外の環境で skopeo inspect を実行して対象のOSが見つからないエラーになる場合は --override-os=linux を追加してください。 今回のローカル環境のテストの場合はmacOS版Skopeoを使ってもエラーにならなかったのでオプションは追加していません % skopeo inspect docker://localhost:5000/tnk4on/registry-test --tls-verify=false { "Name": "localhost:5000/tnk4on/registry-test", "Digest": "sha256:cd56949fd637e728a6145d7fb45717baac5dabd52cf440b95b4afcd3b140540b", "RepoTags": [ "latest" ], "Created": "2024-06-14T02:05:17.827927425Z", ... コンテナイメージが登録されているのが確認できました。今度はこのイメージをPullします。 ローカルのコンテナストレージ上のコンテナイメージを削除し、コンテナレジストリからPullします。Pullの際も --tls-verify=false オプションを追加します。 % podman images REPOSITORY TAG IMAGE ID CREATED SIZE localhost/registry-test latest d7d811c318f6 13 minutes ago 238 MB registry.access.redhat.com/ubi9 latest a13af6f8eaac 8 days ago 238 MB registry.redhat.io/rhbk/keycloak-rhel9 24-10 559c3ef84129 2 weeks ago 461 MB docker.io/library/registry 2 27031dfcd859 8 months ago 25.5 MB % podman rmi registry-test:latest Untagged: localhost/registry-test:latest Deleted: d7d811c318f6237a9dbc97dc15fc31cc92605b0c2166a4b66b4bdfbde727ea1f % podman pull localhost:5000/tnk4on/registry-test --tls-verify=false Trying to pull localhost:5000/tnk4on/registry-test:latest... Getting image source signatures Copying blob sha256:e4c264f9fa3197924054e658c77b17936ecf85f6dec74962138da485d7d7b86b Copying blob sha256:e44751f30cbb5bdc2d04fc32e3df7b09765f21c1db7682159c33cc3398c0e583 Copying config sha256:d7d811c318f6237a9dbc97dc15fc31cc92605b0c2166a4b66b4bdfbde727ea1f Writing manifest to image destination 6a9eef918b31d10db8fdb774f23a6ef27f91c29cecacd79c227196000cfa481f % podman images REPOSITORY TAG IMAGE ID CREATED SIZE localhost:5000/tnk4on/registry-test latest 6a9eef918b31 14 minutes ago 238 MB registry.access.redhat.com/ubi9 latest a13af6f8eaac 8 days ago 238 MB registry.redhat.io/rhbk/keycloak-rhel9 24-10 559c3ef84129 2 weeks ago 461 MB docker.io/library/registry 2 27031dfcd859 8 months ago 25.5 MB podman logout でローカルの認証情報を削除し、再度コンテナレジストリ上のコンテナイメージをPullしてみます。 今度はコンテナレジストリの認証でエラーになりPullできません。パブリックなコンテナレジストリと同様に、認証の仕組みが機能していることが確認できます。 % podman rmi registry-test:latest Untagged: localhost:5000/tnk4on/registry-test:latest Deleted: 6a9eef918b31d10db8fdb774f23a6ef27f91c29cecacd79c227196000cfa481f % podman logout localhost:5000 Removed login credentials for localhost:5000 % podman pull localhost:5000/tnk4on/registry-test --tls-verify=false Trying to pull localhost:5000/tnk4on/registry-test:latest... Error: initializing source docker://localhost:5000/tnk4on/registry-test:latest: Requesting bearer token: invalid status code from registry 400 (Bad Request) まとめ Red Hat build of Keycloakを認証基盤として利用するケースの1つとして、コンテナレジストリの認証環境を構築してみました。 Red Hat build of KeycloakはGUIの管理コンソールがあるため簡単に認証の設定を行うことができます。また、ユーザーの追加なども管理コンソール上で行うことができます。 Distribution Registryはローカル環境で簡単に実行できるコンテナレジストリのソリューションですが、ユーザー認証基盤を持たないためRed Hat build of Keycloakと組み合わせることで簡単に認証の仕組みを追加できます。 Red Hat build of Keycloakの基本機能を確認する目的でもローカルのコンテナ環境でサクッとお試しできますので、まずはPodmanを使って手を動かして体験してみることをオススメします。 *1 : GitHubリポジトリはアーカイブ済み: https://github.com/docker-archive/docker-registry *2 : https://github.com/opencontainers/distribution-spec
Red Hatでソリューションアーキテクトをしている田中司恩( @tnk4on )です。 PodmanではじめるRed Hatのミドルウェア製品シリーズ、今回取り上げる製品は「Red Hat build of Keycloak(RHBK)」です。 (追記:続きの記事を書きました。合わせてお読みください) rheb.hatenablog.com -目次- Red HatがサポートするRed Hat build of Keycloakの構成 Red Hat build of Keycloakのサポート構成 Red Hat build of Keycloakのサポートされない構成 Red Hat build of Keycloakの商業的に合理的なサポート構成 Podmanを使ってRed Hat build of Keycloakでできること カスタムコンテナイメージの作成 開発またはテスト目的でコンテナ実行する Red Hat build of Keycloakのコンテナイメージの確認 Armホスト上でpullする場合 PodmanでRed Hat build of Keycloakのコンテナイメージを実行する まとめ Red HatがサポートするRed Hat build of Keycloakの構成 はじめに確認するのはRed Hat build of Keycloakのサポート構成です。しかしこの構成が多岐に渡るので少しややこしいです。 まずは製品ドキュメントを確認するのですが、Red Hat build of Keycloakのサポート構成の詳細についてはKCS(ArticlesやSolutions)に記載があります。 チェック頂きたいのがこの2つのリンクです。 Red Hat build of Keycloak でサポートされる構成 - Red Hat Customer Portal Red Hat build of Keycloak support on 3rd-party Kubernetes environments (EKS, AKS, GKE, xKS, ...) - Red Hat Customer Portal 「Red Hat build of Keycloak support on 3rd-party...」の方は日本語版が古い内容になっているので英語版をリンクしておきます サポートされる構成をザックリ分けるとこの2つのパターンになります。 RHELまたはWindowsのホストの上で直接Red Hat build of Keycloakを実行するパターン OpenShiftの上でコンテナとして実行するパターン ここから話しが込み入ってくるのですが、その原因となるのが「OpenShift以外の3rdパーティ製のコンテナ/Kubernetes環境」です。 Red Hatは公式のRed Hat build of Keycloakコンテナイメージを提供していますが、これを使ってサポートされる構成は「OpenShift」で実行する場合のみです。 OpenShift以外の3rdパーティ製のコンテナ/Kubernetes環境でRed Hat提供のRed Hat build of Keycloakコンテナイメージを実行する場合はサポートされません。 また、このRed Hat提供のRed Hat build of KeycloakコンテナイメージをRHEL上のPodmanで動かしてもサポートされません。 え?このシリーズは「Podmanではじめる〜」なんですが、どうすんの?という点は後ほど回答します。 兎にも角にも「Red Hat提供のRed Hat build of KeycloakコンテナイメージがサポートされるのはOpenShiftのみ」と覚えておきましょう。 では、3rdパーティー製のコンテナ/Kuberentes環境ではRed Hat build of Keycloakが使う方法はないのか、というとそうではないのです。 3rdパーティー製のコンテナ/Kuberentes環境については、条件を合わせることで限定的なサポートを受けることができます。 要約すると下記のようなポイントがあります。 3rdパーティ環境にデプロイされたRed Hatミドルウェア製品のサポートガイドラインに沿ってcommercially reasonably support(商業的に合理的なサポート)を受けることが可能 サポート対象のバージョンのRed Hat build of KeycloakのZIPアーカイブ、サポートされるJVM、UBIを使用してカスタムコンテナイメージをセルフビルドしたものを使用する Red Hatサポートはカスタム コンテナ イメージの作成と保守を支援またはサポート行わない(必要に応じて有償のコンサルティングサービスをご購入いただけます) Red Hat build of Keycloakのサポート構成、サポートされない構成、商業的に合理的なサポート構成をまとめると下記の図のようになります。(各コンポーネントやOSのバージョンなどは省略しています) Red Hat build of Keycloakのサポート構成 Red Hat build of Keycloakのサポート構成 Red Hat build of Keycloakのサポートされない構成 Red Hat build of Keycloakのサポートされない構成 Red Hat build of Keycloakの商業的に合理的なサポート構成 Red Hat build of Keycloakの商業的に合理的なサポート構成 本記事ではこれ以上細かい内容については解説しませんが、導入の検討において詳細な確認が必要な場合は担当のRed Hatの営業までお尋ねください。 Podmanを使ってRed Hat build of Keycloakでできること さて、PodmanではRed Hat build of Keycloakのコンテナイメージの実行がサポートされないという衝撃の事実があったのですが、製品ドキュメントを読み進めるとできることがだんだん分かってきました。 カスタムコンテナイメージの作成 製品ドキュメントの「3.1. カスタマイズおよび最適化されたコンテナーイメージの作成」には下記の記載があります。 第3章 Red Hat build of Keycloak をコンテナー内で実行する | Red Hat Product Documentation デフォルトの Red Hat build of Keycloak コンテナーイメージは、すぐに設定および最適化できる状態で出荷されます。 Red Hat build of Keycloak コンテナーを最適に起動するには、コンテナーのビルド中に build ステップを実行してイメージをビルドします。この手順を実行することで、後に続くコンテナーイメージの各起動フェーズで時間を節約できます。 ざっくり要約すると、Red Hat build of Keycloakのコンテナイメージをそのまま使うとPodの実行のたびに時間がかかるので、事前にプリビルドしたカスタムコンテナイメージを使用することで起動時間を短縮できる、ということのようです。 製品ドキュメントには下記のContainerfileのサンプルがあります。 FROM registry.redhat.io/rhbk/keycloak-rhel9:24 as builder # Enable health and metrics support ENV KC_HEALTH_ENABLED=true ENV KC_METRICS_ENABLED=true # Configure a database vendor ENV KC_DB=postgres WORKDIR /opt/keycloak # for demonstration purposes only, please make sure to use proper certificates in production instead RUN keytool -genkeypair -storepass password -storetype PKCS12 -keyalg RSA -keysize 2048 -dname "CN=server" -alias server -ext "SAN:c=DNS:localhost,IP:127.0.0.1" -keystore conf/server.keystore RUN /opt/keycloak/bin/kc.sh build FROM registry.redhat.io/rhbk/keycloak-rhel9:24 COPY --from=builder /opt/keycloak/ /opt/keycloak/ # change these values to point to a running postgres instance ENV KC_DB=postgres ENV KC_DB_URL=<DBURL> ENV KC_DB_USERNAME=<DBUSERNAME> ENV KC_DB_PASSWORD=<DBPASSWORD> ENV KC_HOSTNAME=localhost ENTRYPOINT ["/opt/keycloak/bin/kc.sh"] シンプルなマルチステージビルドの内容で、Red Hat提供のRed Hat build of Keycloakのコンテナイメージをベースイメージとし使用し、証明書やDBの設定を行う内容のようです。 元のイメージのまま起動した場合とカスタムコンテナイメージを使った場合でどれくらい時間短縮ができるのかについては、また別の機会に試してみたいと思います。 このカスタムコンテナイメージのビルドおよびテスト起動にPodmanを使うことがドキュメントに書かれています。 ちゃんとPodmanの出番があって安心しました。 開発またはテスト目的でコンテナ実行する もうひとつのPodmanの利用用途がRed Hat build of Keycloakの開発またはテスト目的での実行です。これは製品ドキュメントの「3.3. 開発モードで Red Hat build of Keycloak を試用する」に記載があります。 3.3. 開発モードで Red Hat build of Keycloak を試用する | Red Hat Product Documentation Red Hat提供のRed Hat build of Keycloakのコンテナイメージを使った実稼働環境での使用はサポートされませんが、開発またはテスト目的においてのみ使うことができる内容です。 podman run コマンドでいくつかのオプションをつけてRed Hat build of Keycloakのコンテナイメージを実行するだけのとても簡単なものです。 OpenShiftを用意するまでもなく、Red Hat build of Keycloakのコンテナイメージの実行を試したい場合には手軽な手段です。 では、実際にPodmanでRed Hat build of Keycloakのコンテナイメージを動かしてその内容を確認してみましょう。 繰り返しますが、PodmanでのRed Hat提供のRed Hat build of Keycloakのコンテナイメージの実行はサポートされる構成ではない点ご注意ください Red Hat build of Keycloakのコンテナイメージの確認 Ecosystem Catalog上のRed Hat build of Keycloakのコンテナイメージ Red Hat build of Keycloakのコンテナイメージは registry.redhat.io でホストされており、コンテナイメージをダウンロードするにはコンテナレジストリにログインする必要があります。 Red Hat build of Keycloakが含まれる製品の有効なサブスクリプションをお持ちのRed Hatアカウント、または評価版を使用中のRed Hatアカウント、等が必要です podman login コマンドでコンテナレジストリにログインします。 $ podman login registry.redhat.io Red Hat build of Keycloakのコンテナイメージは必ずタグまで指定してpullする必要があります。本記事執筆時点での最新版は「keycloak-rhel9:24-10」です。 $ podman pull registry.redhat.io/rhbk/keycloak-rhel9:24-10 タグ指定なし(latest指定)の場合はイメージが見つからずエラーになります。 $ podman pull registry.redhat.io/rhbk/keycloak-rhel9 Trying to pull registry.redhat.io/rhbk/keycloak-rhel9:latest... WARN[0000] Failed, retrying in 1s ... (1/3). Error: initializing source docker://registry.redhat.io/rhbk/keycloak-rhel9:latest: reading manifest latest in registry.redhat.io/rhbk/keycloak-rhel9: unsupported: This repository does not use the "latest" tag to track the most recent image and must be pulled with an explicit version or image reference. For more information, see: https://access.redhat.com/articles/4301321 WARN[0002] Failed, retrying in 1s ... (2/3). Error: initializing source docker://registry.redhat.io/rhbk/keycloak-rhel9:latest: reading manifest latest in registry.redhat.io/rhbk/keycloak-rhel9: unsupported: This repository does not use the "latest" tag to track the most recent image and must be pulled with an explicit version or image reference. For more information, see: https://access.redhat.com/articles/4301321 WARN[0003] Failed, retrying in 1s ... (3/3). Error: initializing source docker://registry.redhat.io/rhbk/keycloak-rhel9:latest: reading manifest latest in registry.redhat.io/rhbk/keycloak-rhel9: unsupported: This repository does not use the "latest" tag to track the most recent image and must be pulled with an explicit version or image reference. For more information, see: https://access.redhat.com/articles/4301321 Error: initializing source docker://registry.redhat.io/rhbk/keycloak-rhel9:latest: reading manifest latest in registry.redhat.io/rhbk/keycloak-rhel9: unsupported: This repository does not use the "latest" tag to track the most recent image and must be pulled with an explicit version or image reference. For more information, see: https://access.redhat.com/articles/4301321 OpenJDKの回 *1 *2 で行ったようにイメージの確認をしてみます。 podman images 、 podman image tree 、 podman history を使います。 $ podman images REPOSITORY TAG IMAGE ID CREATED SIZE registry.redhat.io/rhbk/keycloak-rhel9 24-10 559c3ef84129 13 days ago 461 MB $ podman image tree registry.redhat.io/rhbk/keycloak-rhel9:24-10 Image ID: 559c3ef84129 Tags: [registry.redhat.io/rhbk/keycloak-rhel9:24-10] Size: 460.7MB Image Layers ├── ID: 7a99878a1748 Size: 23.52MB └── ID: 690b8207333e Size: 437.2MB Top Layer of: [registry.redhat.io/rhbk/keycloak-rhel9:24-10] $ podman history registry.redhat.io/rhbk/keycloak-rhel9:24-10 ID CREATED CREATED BY SIZE COMMENT 559c3ef84129 13 days ago /bin/sh -c #(nop) USER 1000 437MB FROM registry.redhat.io/ubi9/ubi-micro@sha256:826cf6250899228070dcd4eb0abd8667d0468a5fe0148d54bb513c912b06cee4 <missing> 13 days ago /bin/sh -c mv -fZ /tmp/ubi.repo /etc/yum.r... 0B <missing> 13 days ago /bin/sh -c #(nop) USER root 0B <missing> 13 days ago /bin/sh -c #(nop) USER 1000 0B <missing> 13 days ago /bin/sh -c rm -f /tmp/tls-ca-bundle.pem 0B <missing> 13 days ago /bin/sh -c rm -f '/etc/yum.repos.d/odcs-31... 0B <missing> 13 days ago /bin/sh -c #(nop) USER root 0B ... Red Hat build of KeycloakのベースイメージはUBI-microが使われているようです。 UBI-microについては以前書いた記事がありますので、こちらを参考にしてください。 【UBI】Red Hatの新しい最軽量コンテナーイメージ:UBI Microの紹介 - 赤帽エンジニアブログ Armホスト上でpullする場合 Red Hat build of Keycloakのコンテナイメージはamd64、ppc64le、s390xイメージしか提供されておらず、arm64版は提供されていません。 Apple Silicon Macの場合、そのままではpullできずエラーになります。 % podman pull registry.redhat.io/rhbk/keycloak-rhel9:24-10 Trying to pull registry.redhat.io/rhbk/keycloak-rhel9:24-10... Error: choosing an image from manifest list docker://registry.redhat.io/rhbk/keycloak-rhel9:24-10: no image found in manifest list for architecture "arm64", variant "v8", OS "linux" --arch オプションに続けてアーキテクチャーを指定することでpullが可能になります。 % podman pull --arch amd64 registry.redhat.io/rhbk/keycloak-rhel9:24-10 PodmanでRed Hat build of Keycloakのコンテナイメージを実行する ここからは普段使っているApple Silicon Mac上のPodman machineで実行してみます。 ドキュメントに記載の podman run コマンドは下記のとおりです % podman run -p 8080:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin keycloak-rhel9:24-10 start-dev -e KEYCLOAK_ADMIN :管理ユーザーのユーザー名を指定 -e KEYCLOAK_ADMIN_PASSWORD :管理ユーザーのパスワードを指定 start-dev :開発モードで実行するためのスイッチ 上記のコマンドを実行した場合、Podman machineのメモリ設定がデフォルトの2GBのままだとエラーになり起動しません。Podman machine以外の環境でエラーになる場合も空きメモリに余裕があるかどうか確認ください。 % podman run -p 8080:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin keycloak-rhel9:24-10 start-dev WARNING: image platform (linux/amd64) does not match the expected platform (linux/arm64) Updating the configuration and installing your custom providers, if any. Please wait. 2024-06-11 20:30:36,636 WARN [io.qua.dep.ind.IndexWrapper] (build-68) Failed to index org.apache.tools.ant.Task: Class does not exist in ClassLoader QuarkusClassLoader:Deployment Class Loader: PROD for keycloak@3ecedf21 2024-06-11 20:30:36,676 WARN [io.qua.dep.ind.IndexWrapper] (build-68) Failed to index org.springframework.core.io.DefaultResourceLoader: Class does not exist in ClassLoader QuarkusClassLoader:Deployment Class Loader: PROD for keycloak@3ecedf21 2024-06-11 20:30:36,677 WARN [io.qua.dep.ind.IndexWrapper] (build-68) Failed to index org.springframework.core.io.ResourceLoader: Class does not exist in ClassLoader QuarkusClassLoader:Deployment Class Loader: PROD for keycloak@3ecedf21 2024-06-11 20:30:36,679 WARN [io.qua.dep.ind.IndexWrapper] (build-68) Failed to index org.springframework.core.io.Resource: Class does not exist in ClassLoader QuarkusClassLoader:Deployment Class Loader: PROD for keycloak@3ecedf21 2024-06-11 20:30:36,703 WARN [io.qua.dep.ind.IndexWrapper] (build-68) Failed to index io.mashona.logwriting.ArrayStore: Class does not exist in ClassLoader QuarkusClassLoader:Deployment Class Loader: PROD for keycloak@3ecedf21 2024-06-11 20:30:36,705 WARN [io.qua.dep.ind.IndexWrapper] (build-68) Failed to index org.apache.activemq.artemis.core.journal.RecordInfo: Class does not exist in ClassLoader QuarkusClassLoader:Deployment Class Loader: PROD for keycloak@3ecedf21 2024-06-11 20:30:36,705 WARN [io.qua.dep.ind.IndexWrapper] (build-68) Failed to index org.apache.activemq.artemis.core.journal.Journal: Class does not exist in ClassLoader QuarkusClassLoader:Deployment Class Loader: PROD for keycloak@3ecedf21 2024-06-11 20:30:36,711 WARN [io.qua.dep.ind.IndexWrapper] (build-68) Failed to index jakarta.jms.XAConnection: Class does not exist in ClassLoader QuarkusClassLoader:Deployment Class Loader: PROD for keycloak@3ecedf21 2024-06-11 20:30:36,711 WARN [io.qua.dep.ind.IndexWrapper] (build-68) Failed to index jakarta.jms.XASession: Class does not exist in ClassLoader QuarkusClassLoader:Deployment Class Loader: PROD for keycloak@3ecedf21 2024-06-11 20:30:36,712 WARN [io.qua.dep.ind.IndexWrapper] (build-68) Failed to index jakarta.jms.XAConnectionFactory: Class does not exist in ClassLoader QuarkusClassLoader:Deployment Class Loader: PROD for keycloak@3ecedf21 2024-06-11 20:30:36,747 WARN [io.qua.dep.ind.IndexWrapper] (build-68) Failed to index jakarta.jms.Connection: Class does not exist in ClassLoader QuarkusClassLoader:Deployment Class Loader: PROD for keycloak@3ecedf21 /opt/keycloak/bin/kc.sh: line 161: 76 Killed 'java' -Dkc.config.build-and-exit=true '-XX:MetaspaceSize=96M' '-XX:MaxMetaspaceSize=256m' '-Dfile.encoding=UTF-8' '-Dsun.stdout.encoding=UTF-8' '-Dsun.err.encoding=UTF-8' '-Dstdout.encoding=UTF-8' '-Dstderr.encoding=UTF-8' '-XX:+ExitOnOutOfMemoryError' '-Djava.security.egd=file:/dev/urandom' '-XX:+UseParallelGC' '-XX:GCTimeRatio=4' '-XX:AdaptiveSizePolicyWeight=90' '-XX:FlightRecorderOptions=stackdepth=512' '-XX:MinHeapFreeRatio=10' '-XX:MaxHeapFreeRatio=20' '-Xms64m' '-Xmx512m' '--add-opens=java.base/java.util=ALL-UNNAMED' '--add-opens=java.base/java.util.concurrent=ALL-UNNAMED' '--add-opens=java.base/java.security=ALL-UNNAMED' -Dkc.home.dir='/opt/keycloak/bin/..' -Djboss.server.config.dir='/opt/keycloak/bin/../conf' -Djava.util.logging.manager=org.jboss.logmanager.LogManager -Dpicocli.disable.closures=true -Dquarkus-log-max-startup-records=10000 -cp '/opt/keycloak/bin/../lib/quarkus-run.jar' io.quarkus.bootstrap.runner.QuarkusEntryPoint --profile=dev start-dev Podman machineのメモリの割り当てを増やすには、一度Podman machineを止めて設定を変更する必要があります。 Podman machineのメモリの割り当てメモリの変更は podman machine set コマンドに続けて --memory オプションを使います。指定する数値は MiB で指定します。 % podman machine ls NAME VM TYPE CREATED LAST UP CPUS MEMORY DISK SIZE podman-machine-default* applehv 9 days ago Currently running 5 2GiB 100GiB % podman machine stop % podman machine set --memory 10240 % podman machine ls NAME VM TYPE CREATED LAST UP CPUS MEMORY DISK SIZE podman-machine-default* applehv 9 days ago Currently running 5 10GiB 100GiB Podman machineの割り当てメモリを変更後、再度 podman run コマンドを実行します。今度は無事にコンテナが起動しました。 無事に起動することが確認できれば podman run コマンドに -d オプションをつけてバックグラウンドで起動するように変更してもよいでしょう $ podman run -p 8080:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin keycloak-rhel9:24-10 start-dev WARNING: image platform (linux/amd64) does not match the expected platform (linux/arm64) Updating the configuration and installing your custom providers, if any. Please wait. 2024-06-11 20:36:15,582 WARN [io.qua.dep.ind.IndexWrapper] (build-40) Failed to index org.springframework.core.io.DefaultResourceLoader: Class does not exist in ClassLoader QuarkusClassLoader:Deployment Class Loader: PROD for keycloak@3ecedf21 2024-06-11 20:36:15,593 WARN [io.qua.dep.ind.IndexWrapper] (build-40) Failed to index org.springframework.core.io.ResourceLoader: Class does not exist in ClassLoader QuarkusClassLoader:Deployment Class Loader: PROD for keycloak@3ecedf21 2024-06-11 20:36:15,612 WARN [io.qua.dep.ind.IndexWrapper] (build-40) Failed to index org.apache.tools.ant.Task: Class does not exist in ClassLoader QuarkusClassLoader:Deployment Class Loader: PROD for keycloak@3ecedf21 2024-06-11 20:36:15,631 WARN [io.qua.dep.ind.IndexWrapper] (build-40) Failed to index org.springframework.core.io.Resource: Class does not exist in ClassLoader QuarkusClassLoader:Deployment Class Loader: PROD for keycloak@3ecedf21 2024-06-11 20:36:15,640 WARN [io.qua.dep.ind.IndexWrapper] (build-40) Failed to index jakarta.jms.XAConnection: Class does not exist in ClassLoader QuarkusClassLoader:Deployment Class Loader: PROD for keycloak@3ecedf21 2024-06-11 20:36:15,641 WARN [io.qua.dep.ind.IndexWrapper] (build-40) Failed to index jakarta.jms.XASession: Class does not exist in ClassLoader QuarkusClassLoader:Deployment Class Loader: PROD for keycloak@3ecedf21 2024-06-11 20:36:15,641 WARN [io.qua.dep.ind.IndexWrapper] (build-40) Failed to index jakarta.jms.XAConnectionFactory: Class does not exist in ClassLoader QuarkusClassLoader:Deployment Class Loader: PROD for keycloak@3ecedf21 2024-06-11 20:36:15,643 WARN [io.qua.dep.ind.IndexWrapper] (build-40) Failed to index jakarta.jms.Connection: Class does not exist in ClassLoader QuarkusClassLoader:Deployment Class Loader: PROD for keycloak@3ecedf21 2024-06-11 20:36:15,647 WARN [io.qua.dep.ind.IndexWrapper] (build-40) Failed to index io.mashona.logwriting.ArrayStore: Class does not exist in ClassLoader QuarkusClassLoader:Deployment Class Loader: PROD for keycloak@3ecedf21 2024-06-11 20:36:15,648 WARN [io.qua.dep.ind.IndexWrapper] (build-40) Failed to index org.apache.activemq.artemis.core.journal.RecordInfo: Class does not exist in ClassLoader QuarkusClassLoader:Deployment Class Loader: PROD for keycloak@3ecedf21 2024-06-11 20:36:15,649 WARN [io.qua.dep.ind.IndexWrapper] (build-40) Failed to index org.apache.activemq.artemis.core.journal.Journal: Class does not exist in ClassLoader QuarkusClassLoader:Deployment Class Loader: PROD for keycloak@3ecedf21 2024-06-11 20:37:14,933 INFO [io.qua.dep.QuarkusAugmentor] (main) Quarkus augmentation completed in 72776ms 2024-06-11 20:37:18,271 INFO [org.keycloak.quarkus.runtime.hostname.DefaultHostnameProvider] (main) Hostname settings: Base URL: <unset>, Hostname: <request>, Strict HTTPS: false, Path: <request>, Strict BackChannel: false, Admin URL: <unset>, Admin: <request>, Port: -1, Proxied: false 2024-06-11 20:37:20,669 WARN [io.quarkus.agroal.runtime.DataSources] (JPA Startup Thread) Datasource <default> enables XA but transaction recovery is not enabled. Please enable transaction recovery by setting quarkus.transaction-manager.enable-recovery=true, otherwise data may be lost if the application is terminated abruptly 2024-06-11 20:37:21,420 WARN [org.infinispan.CONFIG] (keycloak-cache-init) ISPN000569: Unable to persist Infinispan internal caches as no global state enabled 2024-06-11 20:37:21,629 INFO [org.infinispan.CONTAINER] (keycloak-cache-init) ISPN000556: Starting user marshaller 'org.infinispan.jboss.marshalling.core.JBossUserMarshaller' 2024-06-11 20:37:22,566 INFO [org.keycloak.broker.provider.AbstractIdentityProviderMapper] (main) Registering class org.keycloak.broker.provider.mappersync.ConfigSyncEventListener 2024-06-11 20:37:22,599 INFO [org.keycloak.connections.infinispan.DefaultInfinispanConnectionProviderFactory] (main) Node name: node_120547, Site name: null 2024-06-11 20:37:24,429 INFO [org.keycloak.quarkus.runtime.storage.legacy.liquibase.QuarkusJpaUpdaterProvider] (main) Initializing database schema. Using changelog META-INF/jpa-changelog-master.xml UPDATE SUMMARY Run: 124 Previously run: 0 Filtered out: 0 ------------------------------- Total change sets: 124 2024-06-11 20:37:29,282 INFO [org.keycloak.services] (main) KC-SERVICES0050: Initializing master realm 2024-06-11 20:37:32,370 INFO [org.keycloak.services] (main) KC-SERVICES0009: Added user 'admin' to realm 'master' 2024-06-11 20:37:32,662 INFO [io.quarkus] (main) Keycloak 24.0.5.redhat-00001 on JVM (powered by Quarkus 3.8.4.redhat-00002) started in 17.107s. Listening on: http://0.0.0.0:8080 2024-06-11 20:37:32,664 INFO [io.quarkus] (main) Profile dev activated. 2024-06-11 20:37:32,664 INFO [io.quarkus] (main) Installed features: [agroal, cdi, hibernate-orm, jdbc-h2, keycloak, narayana-jta, reactive-routes, resteasy-reactive, resteasy-reactive-jackson, smallrye-context-propagation, vertx] 2024-06-11 20:37:32,674 WARN [org.keycloak.quarkus.runtime.KeycloakMain] (main) Running the server in development mode. DO NOT use this configuration in production. コンテナの起動後は、ブラウザで http://localhost:8080 にアクセスし、管理者ユーザーのIDとパスワードでログインします。 Red Hat build of Keycloakのログイン画面 Red Hat build of Keycloakの管理コンソール Red Hat build of Keycloakの画面にアクセスできました。 余談ですが、Apple Silicon MacでRed Hat build of Keycloakのコンテナイメージ(amd64)を実行する場合はエミュレーションモードでの実行になります。 Podman v5.1以降はmacOS版のPodman machineのエミュレーションモードはデフォルトでRosettaが使用されます。 Red Hat build of Keycloakの起動時間を比べたところ、 Rosetta使用の場合:1分程度 qemu-user-static使用の場合:2分程度 と2倍くらいの時間の差があります。起動後の負荷テストなどは行っていませんが、実際の稼働のパフォーマンスなどにも違いは出てくると思われます。 このRosetta機能は私がPull Requestを書いて実装したのですが、こうやって実際に役に立つ場面があると頑張った甲斐があるなと感動します。 PodmanのRosettaについての解説は下記の記事を参照ください。 rheb.hatenablog.com まとめ Red Hat build of Keycloakのコンテナイメージを使ってテスト目的でのPodmanの実行を行うことができました。 実稼働目的ではないとはいえ、まずはお試しでRed Hat build of Keycloak実行してみる手段としてはとても有用です。 この次のステップとしては、外部データーベースを同じくコンテナで起動してRed Hat build of Keycloakから繋いでみるとか、Podmanの環境で高可用性のテストなんかもできるかもしれません(あくまで妄想ですが)。 アップストリームのKeycloakではPodmanを使ったスタートガイドなども実はあります。 www.keycloak.org 「Podmanではじめる〜」本シリーズはRed Hat製品を対象とするのでRed Hat build of Keycloakに対象を絞って紹介しました。 OpenShiftを使わずにRed Hat build of Keycloakをコンテナで実行できることが分かったので、さらに使い込んでRed Hat build of Keycloakのことを理解していきたいと思います。 *1 : https://rheb.hatenablog.com/entry/podman-mw-openjdk *2 : https://rheb.hatenablog.com/entry/podman-mw-openjdk-2
Red Hatでソリューションアーキテクトをしている田中司恩( @tnk4on )です。 前回記事の続きになります。前回記事ではOpenJDKのコンテナイメージでjarファイルが実行される仕組みなどを深掘りしました。 今回はOpenJDKのコンテナイメージのサイズの確認やコンテナイメージを自作した場合との比較などを行います。 (前回記事はこちら) rheb.hatenablog.com -目次- OpenJDKのコンテナイメージとベースイメージとの差分を確認する OpenJDK入りの自作コンテナイメージを作成する UBI-microを使ってOpenJDKのコンテナイメージを作成する まとめ OpenJDKのコンテナイメージとベースイメージとの差分を確認する OpenJDK 21のコンテナイメージ 前回の記事でOpenJDKのコンテナイメージはUBI-minimalがベースイメージに使用されていることが分かりました。 では、元のUBI-minimalとの差分を確認してみましょう。UBI-minimalをダウンロードし podman image tree を使ってレイヤーの差分を確認します。 今回もOpenJDKのコンテナイメージは registry.access.redhat.com/ubi9/openjdk-21 を使用します。手元にない方は podman pull でコンテナイメージを入手してください。 $ podman pull registry.access.redhat.com/ubi9/openjdk-21 $ podman pull registry.access.redhat.com/ubi9/ubi-minimal $ podman images REPOSITORY TAG IMAGE ID CREATED SIZE registry.access.redhat.com/ubi9/openjdk-21 latest 985daf3b31fb 10 days ago 422 MB registry.access.redhat.com/ubi9/ubi-minimal latest d254c4a73500 11 days ago 104 MB $ podman image tree openjdk-21:latest Image ID: 985daf3b31fb Tags: [registry.access.redhat.com/ubi9/openjdk-21:latest] Size: 422.4MB Image Layers ├── ID: 256cf40975c5 Size: 104.3MB └── ID: 25d8d0ec0c6b Size: 318.1MB Top Layer of: [registry.access.redhat.com/ubi9/openjdk-21:latest] ベースとなるUBI-minimalが104MB、その差分の318MB程がOpenJDK用として追加されたとわかります。 podman diff コマンドで2つのコンテナイメージを比較します。 $ podman diff openjdk-21 ubi-minimal A /help.1 C /home A /home/default A /home/default/.bash_logout A /home/default/.bash_profile A /home/default/.bashrc A /home/default/.m2 A /home/default/.m2/settings.xml C /var C /var/run C /var/cache A /var/cache/ldconfig A /var/cache/ldconfig/aux-cache A /var/cache/libX11 A /var/cache/libX11/compose C /var/lib C /var/lib/rpm C /var/lib/rpm/.rpm.lock C /var/lib/rpm/rpmdb.sqlite C /var/lib/rpm/rpmdb.sqlite-shm C /var/lib/rpm/rpmdb.sqlite-wal C /var/lib/alternatives A /var/lib/alternatives/java_sdk_21 A /var/lib/alternatives/java_sdk_21_openjdk A /var/lib/alternatives/java_sdk_openjdk A /var/lib/alternatives/javac A /var/lib/alternatives/jre_21 A /var/lib/alternatives/jre_21_openjdk A /var/lib/alternatives/jre_openjdk C /var/lib/alternatives/libnssckbi.so A /var/lib/alternatives/java A /var/lib/alternatives/mvn ... すごい量のログが出力されます。7,000行以上の差分があるようです。 $ podman diff openjdk-21 ubi-minimal | wc -l 7096 Red Hatが作成したコンテナイメージではありますが、UBI-minimalのベースイメージに対してかなりの追加ファイルがあるようです。 Javaを動かすだけなのにここまでの追加が必要なのか?とちょっと疑ってしまいます。 そこで、OpenJDK入りのコンテナイメージを自作してできる限りスリムにしたものと、Red Hat提供のものとを比較してみようと思います。 OpenJDK入りの自作コンテナイメージを作成する Red Hatが提供するOpenJDKのコンテナイメージではなく、自分でビルドしたOpenJDK入りのコンテナイメージの作成を行います。 まず、UBI-minimalをベースイメージとして使用し、OpenJDKのみをインストールするContainerfileを作成します。 UBI-minimalのパッケージマネージャーは dnf コマンドではなく microdnf コマンドを使用します。 $ cat > Containerfile <<EOF FROM registry.access.redhat.com/ubi9/ubi-minimal RUN microdnf install java-21-openjdk --setopt install_weak_deps=0 --nodocs -y \ && microdnf clean all EOF OpenJDKはUBIリポジトリで配布されているパッケージのためサブスクライブされたRHELホスト以外でもインストール可能です 可能な限りサイズを小さくしたいので、依存関係の少ないパッケージを除外する --setopt install_weak_deps=0 オプションと、ドキュメント類のインストールを行わない --nodocs オプションを追加します ubi-openjdk というタグを指定して新しいコンテナイメージをビルドします。ビルド後にイメージのリストを表示します。OpenJDKのコンテナイメージよりもサイズを減らすことができました。 $ podman build -t ubi-openjdk . $ podman images REPOSITORY TAG IMAGE ID CREATED SIZE localhost/ubi-openjdk latest 4e2b302567f1 16 seconds ago 394 MB registry.access.redhat.com/ubi9/openjdk-21 latest 985daf3b31fb 10 days ago 422 MB registry.access.redhat.com/ubi9/ubi-minimal latest d254c4a73500 11 days ago 104 MB 作成したコンテナイメージのレイヤーを確認するとレイヤーが2つあります。 $ podman image tree ubi-openjdk Image ID: 4e2b302567f1 Tags: [localhost/ubi-openjdk:latest] Size: 394.1MB Image Layers ├── ID: 69d96fccdf6f Size: 104.3MB Top Layer of: [registry.access.redhat.com/ubi9/ubi-minimal:latest] └── ID: 3a36d880c1ec Size: 289.9MB Top Layer of: [localhost/ubi-openjdk:latest] これらを1つにまとめてさらに容量を減らしてみます。ビルド時に --squash-all を追加します。 $ podman build --squash-all -t ubi-openjdk-squash . $ podman images REPOSITORY TAG IMAGE ID CREATED SIZE localhost/ubi-openjdk-squash latest 70909257c076 4 seconds ago 384 MB localhost/ubi-openjdk latest 4e2b302567f1 About a minute ago 394 MB registry.access.redhat.com/ubi9/openjdk-21 latest 985daf3b31fb 10 days ago 422 MB registry.access.redhat.com/ubi9/ubi-minimal latest d254c4a73500 11 days ago 104 MB 先ほど作ったコンテナイメージより容量を減らすことができました。 さらにベースイメージを変更して容量の削減を目指してみます。 UBI-microを使ってOpenJDKのコンテナイメージを作成する UBI-minimalよりサイズの小さいUBI-microを使ってみます。 UBI-microについては以前書いた記事がありますので、こちらを参考にしてください。 【UBI】Red Hatの新しい最軽量コンテナーイメージ:UBI Microの紹介 - 赤帽エンジニアブログ なお、ここからはローカルホストにRHELを使用します。PodmanとBuildahが導入された環境を用意ください。 UBI-microを入手しサイズを確認します。UBI-minimalよりも小さいサイズであることが分かります。 $ cat /etc/redhat-release Red Hat Enterprise Linux release 9.4 (Plow) $ podman pull registry.access.redhat.com/ubi9/ubi-micro $ podman images REPOSITORY TAG IMAGE ID CREATED SIZE localhost/ubi-openjdk-squash latest 70909257c076 27 seconds ago 384 MB localhost/ubi-openjdk latest 4e2b302567f1 About a minute ago 394 MB registry.access.redhat.com/ubi9/openjdk-21 latest 985daf3b31fb 10 days ago 422 MB registry.access.redhat.com/ubi9/ubi-minimal latest d254c4a73500 11 days ago 104 MB registry.access.redhat.com/ubi9/ubi-micro latest 2d8598608a5d 2 weeks ago 24.7 MB BuildahでUBI-microにパッケージを追加する流れ Buildahでマウントしたコンテナイメージに対話的にパッケージを追加します。ルートレスで実行する場合はユーザーネームスペースに入る必要があるため buildah unshare または podman unshare を実行します。 $ buildah unshare # container=$(buildah from registry.access.redhat.com/ubi9/ubi-micro) # mnt=$(buildah mount $container) # dnf install --installroot $mnt --setopt install_weak_deps=false --nodocs java-21-openjdk -y # dnf clean all --installroot $mnt # buildah commit $container ubi-micro-openjdk buildah from registry.access.redhat.com/ubi9/ubi-micro :作業用コンテナの元イメージを指定します buildah mount $container :作業用コンテナをファイルシステムにマウントします dnf install --installroot $mnt :マウントした領域に対して dnf コマンドでパッケージのインストールを行います buildah commit $container : buildah commit コマンドを使ってコンテナイメージをコミットします。 作成したコンテナイメージを確認するとUBI-minimalベースの自作コンテナイメージよりも小さいイメージができました。 # podman images REPOSITORY TAG IMAGE ID CREATED SIZE localhost/ubi-micro-openjdk latest 0aef77730e2e 7 seconds ago 378 MB localhost/ubi-openjdk-squash latest 70909257c076 About a minute ago 384 MB localhost/ubi-openjdk latest 4e2b302567f1 2 minutes ago 394 MB registry.access.redhat.com/ubi9/openjdk-21 latest 985daf3b31fb 10 days ago 422 MB registry.access.redhat.com/ubi9/ubi-minimal latest d254c4a73500 11 days ago 104 MB registry.access.redhat.com/ubi9/ubi-micro latest 2d8598608a5d 2 weeks ago 24.7 MB イメージレイヤーを確認すると2つのレイヤーがあります。これを1つにまとめて(squash)してさらに容量を削減します。 # podman image tree ubi-micro-openjdk:latest Image ID: 0aef77730e2e Tags: [localhost/ubi-micro-openjdk:latest] Size: 378.3MB Image Layers ├── ID: b7c370fe2e29 Size: 24.64MB Top Layer of: [registry.access.redhat.com/ubi9/ubi-micro:latest] └── ID: 7377f93815e2 Size: 353.7MB Top Layer of: [localhost/ubi-micro-openjdk:latest] buildah commit に --squash オプションをつけて実行します。レイヤーが1つにまとめられ、さらに容量が減りました。 # buildah commit --squash $container ubi-micro-openjdk-squash # podman images REPOSITORY TAG IMAGE ID CREATED SIZE localhost/ubi-micro-openjdk-squash latest 507bfdf69b15 7 seconds ago 370 MB localhost/ubi-micro-openjdk latest 0aef77730e2e 39 seconds ago 378 MB localhost/ubi-openjdk-squash latest 70909257c076 2 minutes ago 384 MB localhost/ubi-openjdk latest 4e2b302567f1 3 minutes ago 394 MB registry.access.redhat.com/ubi9/openjdk-21 latest 985daf3b31fb 10 days ago 422 MB registry.access.redhat.com/ubi9/ubi-minimal latest d254c4a73500 11 days ago 104 MB registry.access.redhat.com/ubi9/ubi-micro latest 2d8598608a5d 2 weeks ago 24.7 MB # podman image tree ubi-micro-openjdk-squash Image ID: 507bfdf69b15 Tags: [localhost/ubi-micro-openjdk-squash:latest] Size: 369.7MB Image Layers └── ID: afb99d712887 Size: 369.7MB Top Layer of: [localhost/ubi-micro-openjdk-squash:latest] ビルド作業が終わったので作業用コンテナを削除してユーザーネームスペースから抜けます。 # buildah rm -a # exit exit $ 前回の記事と同様に、自作したコンテナイメージをベースイメージとして使ったjarファイルを実行するコンテナイメージをビルドします。 hello.jar は前回作成したものを流用します。 $ cat > Containerfile <<EOF FROM ubi-micro-openjdk-squash COPY hello.jar /hello.jar EOF $ podman build -t java-test . $ podman images REPOSITORY TAG IMAGE ID CREATED SIZE localhost/java-test latest 8e82cd787a48 8 seconds ago 370 MB localhost/ubi-micro-openjdk-squash latest 507bfdf69b15 About a minute ago 370 MB localhost/ubi-micro-openjdk latest 0aef77730e2e About a minute ago 378 MB localhost/ubi-openjdk-squash latest 70909257c076 3 minutes ago 384 MB localhost/ubi-openjdk latest 4e2b302567f1 4 minutes ago 394 MB registry.access.redhat.com/ubi9/openjdk-21 latest 985daf3b31fb 10 days ago 422 MB registry.access.redhat.com/ubi9/ubi-minimal latest d254c4a73500 11 days ago 104 MB registry.access.redhat.com/ubi9/ubi-micro latest 2d8598608a5d 2 weeks ago 24.7 MB 作成したコンテナイメージを実行します。 java コマンドに続けて -jar オプションと実行するファイル(コンテナ内のパスを指定)を指定してコンテナを実行します。 $ podman run --rm java-test java -jar /hello.jar Hello, World! 期待通りにHello Worldが実行されました。 このようにシンプルなjarファイルを1つ実行するだけなら自作のコンテナイメージを作るのとOpenJDKのコンテナイメージを使うのとでは大した差はありません。 むしろRed Hat提供のOpenJDKのコンテナイメージは決められたディレクトリに配置するだけでjarファイルを読み込んでくれたり、S2Iが実行できたり、チューニング用の環境変数がいろいろ使えたりなど、より実務で使うために必要な機能が豊富に盛り込まれています。 その機能の豊富さとコンテナイメージを自作しメンテナンスする手間を比べた場合のメリットがどれほどのものかをよく考える必要があります。 まとめ OpenJDKのコンテナイメージについて、使用されているベースイメージやイメージのレイヤー、イメージのサイズという観点で深堀りしてみました。 イメージのサイズが小さいということはコンテナのライフサイクルにおけるPushやPull(それらに伴う圧縮解凍)といった作業時間の軽減に寄与します。 また、コンテナレジストリやローカルストレージの保存領域の観点でも容量削減が見込めますが、昨今のストレージの規模から考えたらそこまで気にする点でもありません。 さらに、コンテナプロセスのリソース消費やコンテナの起動速度は実行するアプリに依存するため、コンテナサイズが直接的に影響するものではありません。 コンテナのサイズは利便性とのバランスになります。UBI-microはサイズが小さく、またパッケージの追加更新作業は手間がかかりますが、静的リンクでコンパイルされた実行バイナリをコピーするだけの用途ではとても有用です。Red Hatが提供するOpenJDKのコンテナイメージはS2IやJavaアプリの自動実行に対応し、さらにRed Hatがコンテナイメージのメンテナンスもして容量もそこそこ小さいです。 自作のコンテナイメージと比較するとRed Hatが提供するOpenJDKのコンテナイメージを活用する利点は十分あると考えます。 Red Hatが提供するOpenJDKのコンテナイメージはよく考えられて作られているので、みなさんどんどん活用しましょう!
Red Hatでソリューションアーキテクトをしている田中司恩( @tnk4on )です。 Red Hatの製品にはミドルウェア製品が多数あるのですが、数も種類も多くて細かい機能までは把握しきれていません。 個人の観点ではこれまでIT業界の仕事はインフラ畑でやってきたのでミドルウェアについて深く触れることもありませんでした。 そこで、 Podman とコンテナの力を借りて、Red Hatのミドルウェアと仲良くなろうというのがこの企画の趣旨です。 今回はOpenJDKについて深堀していきたいと思います。 (追記:続きの記事を書きました。合わせてお読みください) rheb.hatenablog.com -目次- OpenJDKとは Red Hatが提供するOpenJDKのコンテナイメージ OpenJDKのコンテナイメージの入手 OpenJDKのコンテナイメージでjarファイルが実行される仕組み OpenJDKのコンテナイメージでjarファイルを実行する まとめ 参考:/opt/jboss/container/java/run/run-java.sh OpenJDKとは OpenJDKについてはRed HatのOpenJDKの製品 *1 ドキュメントに下記のように記載されています。 Open Java Development Kit (OpenJDK) は、Java Platform Standard Edition (Java SE) のオープンソース実装です。Red Hat build of OpenJDK は、8u、11u、17u、21u の 4 つのバージョンで利用できます。 Red Hat build of OpenJDK 向けパッケージは、Red Hat Enterprise Linux および Microsoft Windows で利用でき、Red Hat Ecosystem Catalog の JDK および JRE として同梱されています。 Red Hatが製品として提供しているOpenJDKは「Red Hat build of OpenJDK」が正式名称です。本記事執筆時点では「21.0.3」が最新です。 Red Hatが提供するOpenJDKのコンテナイメージ Red Hatが提供するOpenJDKのコンテナイメージにはいくつか種類があります。 Ecosystem Catalogを検索していくつか出てきますが、関連度で上位に上がってくるのはUBIベースのものです。 Red Hat Ecosystem Catalogの検索結果 https://catalog.redhat.com/search?gs&q=openjdk&searchType=containers このUBIベースのOpenJDKイメージは、ベースイメージのUBIと同じくUBI EULAに基づいて無償で使えます。ただし、このOpenJDKのイメージをベースに作成した新たなイメージを再配布する場合は、UBIと同じライセンス上の制限がある点はご注意ください。 詳細はUBIのFAQを参照 Universal Base Images FAQ | Red Hat Developer PodmanやSkopeoを使ってコンテナイメージ中に設定されているライセンス条項を確認できます。 (Podmanについては後述) $ skopeo inspect docker://registry.access.redhat.com/ubi9/openjdk-21 --format '{{ index .Labels "com.redhat.license_terms" }}' https://www.redhat.com/en/about/red-hat-end-user-license-agreements#UBI エコシステムカタログを検索するとOpenJDKのコンテナイメージにはいろいろバリエーションがありますが、完全に無償で使えるのは registry.access.redhat.com でホストされているものです。(例:registry.access.redhat.com/ubi9/openjdk-21) registry.redhat.io/openjdk の使用には適格なサブスクリプションが必要です(コンテナレジストリへのログインが必要) OpenJDKのコンテナイメージの入手 まずはOpenJDKのコンテナイメージを入手します。 registry.access.redhat.com はログイン不要でコンテナイメージをpullできます。ここでは最新の openjdk-21 を利用します。 $ podman pull registry.access.redhat.com/ubi9/openjdk-21 $ podman images openjdk-21:latest REPOSITORY TAG IMAGE ID CREATED SIZE registry.access.redhat.com/ubi9/openjdk-21 latest 985daf3b31fb 10 days ago 422 MB イメージのレイヤーを確認します。 $ podman image tree openjdk-21:latest Image ID: 985daf3b31fb Tags: [registry.access.redhat.com/ubi9/openjdk-21:latest] Size: 422.4MB Image Layers ├── ID: 256cf40975c5 Size: 104.3MB └── ID: 25d8d0ec0c6b Size: 318.1MB Top Layer of: [registry.access.redhat.com/ubi9/openjdk-21:latest] podman inspect ( podman image inspect )でイメージの詳細を確認します。ポイントだけピックアップします。 $ podman inspect openjdk-21:latest ... "Config": { "User": "185", ... "Cmd": [ "/usr/local/s2i/run" ], "WorkingDir": "/home/default", ... User:UID=185でユーザーが作成されている Cmd:コンテナのデフォルトコマンドは /usr/local/s2i/run が実行される WorkingDir: /home/default がデフォルトの作業ディレクトリ ライセンス条項を確認するには下記のようにフィルタすることで確認できます。 $ podman inspect registry.access.redhat.com/ubi9/openjdk-21 --format '{{ index .Config.Labels "com.redhat.license_terms" }}' https://www.redhat.com/en/about/red-hat-end-user-license-agreements#UBI コンテナイメージの履歴を確認します(出力が多いので一部のみ表示)。 $ podman history openjdk-21:latest ID CREATED CREATED BY SIZE COMMENT 985daf3b31fb 10 days ago /bin/sh -c #(nop) USER 185 318MB FROM registry.access.redhat.com/ubi9/ubi-minimal@sha256:ef6fb6b3b38ef6c85daebeabebc7ff3151b9dd1500056e6abc9c3295e4b78a51 <missing> 10 days ago /bin/sh -c mv -fZ /tmp/ubi.repo /etc/yum.r... 0B <missing> 10 days ago /bin/sh -c #(nop) USER root 0B <missing> 10 days ago /bin/sh -c #(nop) USER 185 0B <missing> 10 days ago /bin/sh -c rm -f /tmp/tls-ca-bundle.pem 0B <missing> 10 days ago /bin/sh -c rm -f '/etc/yum.repos.d/odcs-31... 0B ... コメントに FROM registry.access.redhat.com/ubi9/ubi-minimal とあり、このOpenJDKのコンテナイメージはUBI-minimalをベースとして構築されていることがわかります。 OpenJDKのコンテナイメージでjarファイルが実行される仕組み OpenJDKの製品ドキュメント *2 には下記のような記載があります。 Red Hat build of OpenJDK イメージには、アプリケーション JAR ファイルを自動的に検出し、Java を起動するデフォルトの起動スクリプトがあります。 OpenJDK イメージの /deployments ディレクトリーの Java アプリケーションは、イメージの読み込み時に実行されます。 これがどのように実行されるのか調べてみます。まず、コンテナの実行コマンドファイル /usr/local/s2i/run を調べます。 podman image mount コマンドを使いコンテナイメージをファイルシステムにマウントしてファイルの中身を確認します。 ルートレスで実行している場合はユーザーネームスペースの中で podman image mount を実行する必要があるので、 podman unshare を実行します。 (macOSやWindowsで実行している場合はPodman machineにログインして podman unshare を実行する必要があります) $ podman unshare # mnt=$(podman image mount openjdk-21) # cat $mnt/usr/local/s2i/run #!/bin/bash # Command line arguments given to this script args="$*" source "${JBOSS_CONTAINER_UTIL_LOGGING_MODULE}/logging.sh" source "${JBOSS_CONTAINER_S2I_CORE_MODULE}/s2i-core" # include our s2i_core_*() overrides/extensions source "${JBOSS_CONTAINER_JAVA_S2I_MODULE}/s2i-core-hooks" # Global S2I variable setup s2i_core_env_init export JAVA_OPTS if [ -f "${S2I_TARGET_DEPLOYMENTS_DIR}/bin/run.sh" ]; then echo "Starting the application using the bundled ${S2I_TARGET_DEPLOYMENTS_DIR}/bin/run.sh ..." exec ${DEPLOYMENTS_DIR}/bin/run.sh $args ${JAVA_ARGS} else echo "Starting the Java application using ${JBOSS_CONTAINER_JAVA_RUN_MODULE}/run-java.sh $args..." exec "${JBOSS_CONTAINER_JAVA_RUN_MODULE}/run-java.sh" $args ${JAVA_ARGS} fi /usr/local/s2i/run ファイルをざっくり眺めると、 ${S2I_TARGET_DEPLOYMENTS_DIR}/bin/run.sh ファイルの有無で実行モードがS2IモードになるかJavaアプリの実行モードになるかの選択があるようです。つまり、このOpenJDKのコンテナイメージはS2Iのビルド用途にもJavaアプリの実行用途にもどちらでも使える共通イメージとして提供されているようです。 今回はS2Iについては深入りしないのでJavaアプリの実行用途についてもう少し探っていきます。 run-java.sh がどこにあるか探します。 # cd $mnt # find . -name run-java.sh ./opt/jboss/container/java/run/run-java.sh /opt/jboss/container/java/run/run-java.sh にファイルがあるようです。 run-java.sh ファイルの中身を確認します。 # cd ~ # cat $mnt/opt/jboss/container/java/run/run-java.sh ファイルの行数が多いので全文は本記事の最後に載せておきます。 まず、確認する点がこちら。 JAVA_APP_DIR 環境変数に /deployments がセットされています。 # Default the application dir to the S2I deployment dir if [ -z "$JAVA_APP_DIR" ] then JAVA_APP_DIR=/deployments fi 製品ドキュメントに書いてあった /deployments のJavaアプリケーションが読み込まれるというのはここの設定のようです。 /opt/jboss/container/java/run/run-java.sh の中でjarファイルの検出が行われ、最終的には ${args} にファイルがセットされて実行される仕組みです。 # Start JVM startup() { ... exec -a "${procname}" java $(get_java_options) -cp "$(get_classpath)" ${args} $* } OpenJDKのコンテナイメージを引数なしで実行するとエラーメッセージの内容でここまでに確認したことがなんとなくわかります。 $ podman run --rm openjdk-21:latest Starting the Java application using /opt/jboss/container/java/run/run-java.sh ... ERROR Neither $JAVA_MAIN_CLASS nor $JAVA_APP_JAR is set and 0 JARs found in /deployments (1 expected) INFO exec -a "java" java -XX:MaxRAMPercentage=80.0 -XX:+UseParallelGC -XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=20 -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -XX:+ExitOnOutOfMemoryError -cp "." -jar INFO running in /deployments Error: -jar requires jar file specification ... 作業が終わったら podman image unmount コマンドでマウントを解除します。 -a オプションで全てのマウントを解除します。 最後に exit でユーザーネームスペースを抜けます。 # podman image unmount -a 985daf3b31fb3a88c12a0f7e374bc2e29c5a6718c836f876eca99c0d91ff2ea8 # exit exit $ OpenJDKのコンテナイメージでjarファイルを実行する jarファイルの実行の仕組みが分かったのでサンプルのjarファイルを作成してコンテナを実行してみます。 まず、Hello Worldを実行するJavaソースファイルを作成します。適当な作業ディレクトリを作ってその中で作業してください。後ほどコンテナにバインドマウントを行うため、HOME以外のカレントディレクトリに余計なファイルがない状態にするためです。 $ mkdir work $ cd work $ cat > hello.java <<EOF public class hello { public static void main(String[] args) { System.out.println("Hello, World!"); } } EOF 次に、OpenJDKのコンテナイメージを使ってコンパイルします。コンテナを使うことでローカルホストに直接OpenJDKをインストールする必要はありません。 OpenJDKのコンテナ内でJavaのコンパイルに必要なコマンドは揃っているようです。 $ podman run --rm -it openjdk-21 bash [default@2281f04e1b49 ~]$ ja jar jarsigner java javac javadoc javap カレントディレクトリをバインドマウントしてコンパイルを実行します。 $ podman run --rm -it -v $PWD:$PWD:Z -w $PWD --user root openjdk-21 javac hello.java --user root :OpenJDKのコンテナイメージはUID=185の default というユーザーで実行されます。そのままバインドマウントを行うとローカルホスト上のUIDと異なりファイルの書き込みができません。また、実行ユーザーが固定されているので --userns=keep-id も使えません。そのため実行ユーザーを root で上書きしてシンプルなコマンドで実行できるようにしています。 続けてjarファイルにパッケージ化します $ podman run --rm -it -v $PWD:$PWD:Z -w $PWD --user root openjdk-21 jar cfe hello.jar hello hello.class でき上がったファイルを確認します。 $ ls -l total 12 -rw-r--r--. 1 core core 417 Jun 11 02:22 hello.class -rw-r--r--. 1 core core 738 Jun 11 02:22 hello.jar -rw-r--r--. 1 core core 119 Jun 11 02:14 hello.java jarファイルを指定して実行します。 $ podman run --rm -it -v $PWD:$PWD:Z -w $PWD openjdk-21 java -jar hello.jar Hello, World! jarファイルができたのでOpenJDKのコンテナイメージに組み込んで新しいコンテナイメージを作成します。 jarファイルの実行の流れはこのようになります。 OpenJDKコンテナのJavaアプリ実行の流れ まず、下記のContainerfileを作成してビルドします。ベースイメージにOpenJDKコンテナを指定し、hello.jarを /deployments にコピーします。 $ cat > Containerfile <<EOF FROM registry.access.redhat.com/ubi9/openjdk-21 COPY hello.jar /deployments/hello.jar EOF $ podman build -t java-test . STEP 1/2: FROM registry.access.redhat.com/ubi9/openjdk-21 STEP 2/2: COPY hello.jar /deployments/hello.jar COMMIT java-test --> 3b11c122787a Successfully tagged localhost/java-test:latest 3b11c122787a0cd14d50ff2ef595a59e239c5731818a56b5f2f2328319a069c1 ビルドしたjava-testコンテナイメージを実行します。 $ podman run --rm java-test Starting the Java application using /opt/jboss/container/java/run/run-java.sh ... INFO exec -a "java" java -XX:MaxRAMPercentage=80.0 -XX:+UseParallelGC -XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=20 -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -XX:+ExitOnOutOfMemoryError -cp "." -jar /deployments/hello.jar INFO running in /deployments Hello, World! 期待通りにHello Worldが実行されました。 まとめ OpenJDKコンテナイメージを使うことで、コンテナ上でJavaのコンパイルを行ったりjarファイルの実行ができます。 また、OpenJDKコンテナイメージをベースイメージとして指定し、コンテナ内の /deployments にjarファイルを配置するだけでコンテナの実行時に自動で実行してくれます。 ベースのUBIイメージはRed Hatが定期的に更新しており、RHELの信頼性とセキュリティを担保された安心して使用できるコンテなイメージです。 UBIをベースイメージとしたOpenJDKのコンテナイメージは誰でも無償で使用できますので、Javaを使う方にはどなたにでもオススメのコンテナイメージです。 Podmanとコンテナを使うことでミドルウェアの理解がさらに深まりそうです。 参考:/opt/jboss/container/java/run/run-java.sh run-java.shの出力結果を開く root@localhost:~# cat $mnt/opt/jboss/container/java/run/run-java.sh #!/bin/bash # Fail on a single failed command set -eo pipefail export JBOSS_CONTAINER_UTIL_LOGGING_MODULE="${JBOSS_CONTAINER_UTIL_LOGGING_MODULE-/opt/jboss/container/util/logging}" export JBOSS_CONTAINER_JAVA_RUN_MODULE="${JBOSS_CONTAINER_JAVA_RUN_MODULE-/opt/jboss/container/java/run}" # Default the application dir to the S2I deployment dir if [ -z "$JAVA_APP_DIR" ] then JAVA_APP_DIR=/deployments fi source "$JBOSS_CONTAINER_UTIL_LOGGING_MODULE/logging.sh" # ========================================================== # Generic run script for running arbitrary Java applications # # This has forked (and diverged) from: # at https://github.com/fabric8io/run-java-sh # # ========================================================== # Error is indicated with a prefix in the return value check_error() { local msg=$1 if echo ${msg} | grep -q "^ERROR:"; then log_error ${msg} exit 1 fi } # detect Quarkus fast-jar package type (OPENJDK-631) is_quarkus_fast_jar() { if test -f quarkus-app/quarkus-run.jar; then log_info "quarkus fast-jar package type detected" echo quarkus-app/quarkus-run.jar return 0 else return 1 fi } # Try hard to find a sane default jar-file auto_detect_jar_file() { local dir=$1 # Filter out temporary jars from the shade plugin which start with 'original-' local old_dir=$(pwd) cd ${dir} if [ $? = 0 ]; then if quarkus="$(is_quarkus_fast_jar)"; then echo "$quarkus" return fi local nr_jars=`ls *.jar 2>/dev/null | grep -v '^original-' | wc -l | tr -d '[[:space:]]'` if [ ${nr_jars} = 1 ]; then ls *.jar | grep -v '^original-' exit 0 fi log_error "Neither \$JAVA_MAIN_CLASS nor \$JAVA_APP_JAR is set and ${nr_jars} JARs found in ${dir} (1 expected)" cd ${old_dir} else log_error "No directory ${dir} found for auto detection" fi } # Check directories (arg 2...n) for a jar file (arg 1) get_jar_file() { local jar=$1 shift; if [ "${jar:0:1}" = "/" ]; then if [ -f "${jar}" ]; then echo "${jar}" else log_error "No such file ${jar}" fi else for dir in $*; do if [ -f "${dir}/$jar" ]; then echo "${dir}/$jar" return fi done log_error "No ${jar} found in $*" fi } load_env() { # Configuration stuff is read from this file local run_env_sh="run-env.sh" # Load default default config if [ -f "${JBOSS_CONTAINER_JAVA_RUN_MODULE}/${run_env_sh}" ]; then source "${JBOSS_CONTAINER_JAVA_RUN_MODULE}/${run_env_sh}" fi # Check also $JAVA_APP_DIR. Overrides other defaults # It's valid to set the app dir in the default script if [ -f "${JAVA_APP_DIR}/${run_env_sh}" ]; then source "${JAVA_APP_DIR}/${run_env_sh}" fi export JAVA_APP_DIR # JAVA_LIB_DIR defaults to JAVA_APP_DIR export JAVA_LIB_DIR="${JAVA_LIB_DIR:-${JAVA_APP_DIR}}" if [ -z "${JAVA_MAIN_CLASS}" ] && [ -z "${JAVA_APP_JAR}" ]; then JAVA_APP_JAR="$(auto_detect_jar_file ${JAVA_APP_DIR})" check_error "${JAVA_APP_JAR}" fi if [ "x${JAVA_APP_JAR}" != x ]; then local jar="$(get_jar_file ${JAVA_APP_JAR} ${JAVA_APP_DIR} ${JAVA_LIB_DIR})" check_error "${jar}" export JAVA_APP_JAR=${jar} else export JAVA_MAIN_CLASS fi } # Combine all java options get_java_options() { local jvm_opts local debug_opts local proxy_opts local opts if [ -f "${JBOSS_CONTAINER_JAVA_JVM_MODULE}/java-default-options" ]; then jvm_opts=$(${JBOSS_CONTAINER_JAVA_JVM_MODULE}/java-default-options) fi if [ -f "${JBOSS_CONTAINER_JAVA_JVM_MODULE}/debug-options" ]; then debug_opts=$(${JBOSS_CONTAINER_JAVA_JVM_MODULE}/debug-options) fi if [ -f "${JBOSS_CONTAINER_JAVA_PROXY_MODULE}/proxy-options" ]; then source "${JBOSS_CONTAINER_JAVA_PROXY_MODULE}/proxy-options" proxy_opts="$(proxy_options)" fi opts=${JAVA_OPTS-${debug_opts} ${proxy_opts} ${jvm_opts} ${JAVA_OPTS_APPEND}} # Normalize spaces with awk (i.e. trim and eliminate double spaces) echo "${opts}" | awk '$1=$1' } # Read in a classpath either from a file with a single line, colon separated # or given line-by-line in separate lines # Arg 1: path to claspath (must exist), optional arg2: application jar, which is stripped from the classpath in # multi line arrangements format_classpath() { local cp_file="$1" local app_jar="$2" local wc_out=`wc -l $1 2>&1` if [ $? -ne 0 ]; then log_error "Cannot read lines in ${cp_file}: $wc_out" exit 1 fi local nr_lines=`echo $wc_out | awk '{ print $1 }'` if [ ${nr_lines} -gt 1 ]; then local sep="" local classpath="" while read file; do local full_path="${JAVA_LIB_DIR}/${file}" # Don't include app jar if include in list if [ x"${app_jar}" != x"${full_path}" ]; then classpath="${classpath}${sep}${full_path}" fi sep=":" done < "${cp_file}" echo "${classpath}" else # Supposed to be a single line, colon separated classpath file cat "${cp_file}" fi } # Fetch classpath from env or from a local "run-classpath" file get_classpath() { local cp_path="." if [ "x${JAVA_LIB_DIR}" != "x${JAVA_APP_DIR}" ]; then cp_path="${cp_path}:${JAVA_LIB_DIR}" fi if [ -z "${JAVA_CLASSPATH}" ] && [ "x${JAVA_MAIN_CLASS}" != x ]; then if [ "x${JAVA_APP_JAR}" != x ]; then cp_path="${cp_path}:${JAVA_APP_JAR}" fi if [ -f "${JAVA_LIB_DIR}/classpath" ]; then # Classpath is pre-created and stored in a 'run-classpath' file cp_path="${cp_path}:`format_classpath ${JAVA_LIB_DIR}/classpath ${JAVA_APP_JAR}`" else # No order implied cp_path="${cp_path}:${JAVA_APP_DIR}/*" fi elif [ "x${JAVA_CLASSPATH}" != x ]; then # Given from the outside cp_path="${JAVA_CLASSPATH}" fi echo "${cp_path}" } # Mask secrets before printing mask_passwords() { local content="$1" local result="" IFS=' ' read -r -a key_value_pairs <<< "$content" for pair in "${key_value_pairs[@]}"; do key=$(echo "$pair" | cut -d '=' -f 1) value=$(echo "$pair" | cut -d '=' -f 2-) if [[ $key =~ [Pp][Aa][Ss][Ss][Ww][Oo][Rr][Dd] ]]; then result+="$key=***** " else result+="$pair " fi done echo "${result% }" } # Start JVM startup() { # Initialize environment load_env local args cd ${JAVA_APP_DIR} if [ "x${JAVA_MAIN_CLASS}" != x ] ; then args="${JAVA_MAIN_CLASS}" else args="-jar ${JAVA_APP_JAR}" fi local procname="${JAVA_APP_NAME-java}" local masked_opts=$(mask_passwords "$(get_java_options)") log_info "exec -a \"${procname}\" java ${masked_opts} -cp \"$(get_classpath)\" ${args} $*" log_info "running in $PWD" exec -a "${procname}" java $(get_java_options) -cp "$(get_classpath)" ${args} $* } # ============================================================================= # Fire up startup $* *1 : https://docs.redhat.com/ja/documentation/red_hat_build_of_openjdk/21/html/release_notes_for_red_hat_build_of_openjdk_21.0.3/pr01 *2 : https://docs.redhat.com/ja/documentation/red_hat_build_of_openjdk/21/html/packaging_red_hat_build_of_openjdk_21_applications_in_containers/openjdk-apps-in-containers
皆さんこんにちは、Red Hat の岡野です。 今回は、運用面から見たシステム稼働率の最大化と RHEL のパッチ適用について考えてみたいと思います。実は最近、セキュリティに対する意識の高まりを背景に、『RHEL のパッチ管理をやりたいんだけど』という問い合わせを沢山いただくようになりました。ただその一方で、既にサポート外となっている古いバージョンを継続利用していて不具合が発生し、表現はちょっとよくないですが、営業経由で『泣きの依頼』が来るようなことも、残念ながら発生しています。 そんな中、社内にあった古い資料の中にこんな文言を見つけました。 " 85% of critical issues raised to Red Hat support are already known to Red Hat or our partners " サポートに来る重大なトラブルのうち85%は既知の問題。 つまり、きちんと設定をし、パッチさえ当てていれば問題は起きなかった、ということです。これって結構重いメッセージですよね。 ちょうど RHEL 7 が来月、7月より ELS フェーズに入ります。システム更新に関して考える良い時期かなとも思いますので、そのあたりも含めて考えていただければと思います。 access.redhat.com パッチ管理ベストプラクティス 一般的な RHEL のパッチ管理では、Red Hat が提供するリポジトリである CDN (Content Delivery Network) に接続し、必要なパッケージの入手やパッチの適用を行います。オンラインの場合は直接、オフラインの場合は CDN に接続可能な端末で入手し適用を行います。CDN の特徴として、提供されるパッケージは時とともにアップデートされていきます。 一方、システムの安定稼働の観点からは動作確認が取れた特定のバージョンで稼働させたい、というのが一般的かと思います。また、先ほどの『85%問題』を回避するには、特定のバージョンで動作テストを行った上で継続的にバージョンアップしていく必要があります。 このため、刻々と変化する CDN のある時点でのスナップショットを複数用意し、利用するリポジトリを柔軟に変更できるような機能が求められるのですが、これを実現するのが Red Hat Satellite です。 Red Hat Satellite を利用すると CDN からダウンロードしたパッケージの中から任意のバージョンのパッケージのみが含まれるカスタムリポジトリを作成し、コンテンツビューという形で提供することが可能となります。コンテンツビューは複数作成することが可能で、RHEL 単体やグループ単位でどのコンテンツビューを利用するかを柔軟に設定・変更することが可能です。例えば、以下の例では、App1、App2に対して本番環境とテスト環境を準備、そのそれぞれが、Satelliteから提供されている特定のパッケージを含むコンテンツビューを参照しています。 各システムがアクセス可能なパッケージは Satellite から変更しない限り同じものとなりますので確実に同一パッケージでの運用が可能となります。この機能を利用し、App1、App2をテスト環境を使って徹底的に動作確認を行い、問題ないことが確認出来たら本番環境をそちらに移行、つまり、Satellite 上でコンテンツビューの割り当てを変更し、本番環境のシステム更新を行う事が可能となります。 このコンテンツビューの作成とシステムへの割り当て作業を Ansible Automation Platform を使って自動化、サービス化するとさらに大きな効果をもたらします。・・・、と申しますのも、実は、RHEL のシステム更新が進まない大きな要因の一つに、 『アプリケーションが動かなくなると困るので、 パッチ管理者側 では勝手にシステム更新が行えない』 というものがあります。この理由、本当に多いのですが、Ansible Automation Platform を利用すると、どのバージョンをテスト環境及び本番環境で利用するか、さらにはパッチの適用自体をアプリケーション管理者側にオフロードすることが出来るようになります。アプリケーション責任者に各システムを稼働させるパッチレベルを決定させ、システムアップデートも実行させることが出来るということです。イメージは以下の通りです。 パッチ管理者は CDN で提供されるパッチ収集と時に発生するクリティカルなバグに対するHotFixの収集、そして、それらをコンテンツビューとして適切なタイミングで公開する作業に集中します。一方、アプリケーション管理者は各システムでどのバージョンを利用するかを決定します。実際の紐づけ作業には、Satellite と連携した Ansible Automation Platform を利用します。この紐づけの変更やパッチ適用を行う Playbook やジョブテンプレートはパッチ管理者が作成し、アプリケーション管理者はそれをサービスとして利用するイメージです。 アプリケーション管理者は各システムをテストするためのコードも開発します。さらには、このテストコードの実行も Ansible Automation Platform で自動化します。このテストコードを使ってテスト環境で徹底的に事前検証を行い、問題なければ本番環境に適用を考えるという具合です。また、万が一本番環境に適用した後、問題が発生した時のために、環境のバックアップとリストアの仕組みを、仮想環境やクラウド環境の管理者などに依頼してあらかじめAnsible Automation Platform のジョブテンプレートして作成してもらい、それを実際のパッチ適用のワークフローの中に組み込んでおくと、さらに安心して作業が可能となります。 まとめ 如何でしたでしょうか?触らぬ神にたたりなし。動いているものを触るのは怖い、というのはよくわかります。クリティカルなシステムであればなおさらです。しかしながら、重大障害の多くは既知のバグ。これを心にとめてシステムアップデート前提で仕組みを考えてみてください。このブログがその一助になれば幸いです。 RHEL の定期的なアップデートに関してはこちらもご参照ください。 bit.ly
Red Hat で Java や Quarkus を担当している伊藤ちひろです。 今回は、Quarkus で将来的に使えるようになる新機能を紹介します。 QuarkusはJDK Flight Recorderを quarkus-jfr として統合しました。 JFRは、さまざまな情報をイベントとして記録します。 この統合により、各スレッドで発生したイベントを時系列に見られます。 この図では、Quarkusの処理の中核である vert.x-eventloop-thread がいつ何を処理しているのかを表しています。 スレッド名右側の四角は、左から右に時間が流れています。 緑はスレッドに何もイベントがありません。 オレンジは REST の処理をしていることを示しています。 quarkus-jfr は、このようにJFRに独自のイベントを提供し、アプリケーションの問題解決を助けます。 quarkus-jfr はJVMモードだけではなく、ネイティブモードでも使用できます。 quarkus-jfr の詳細はこちらをご覧ください。 Using JDK Flight Recorder - main - Quarkus 本記事では、その機能を抜粋して紹介します。 quarkus-jfr は、リクエストIDを提供します。 リクエストIDは、 quarkus-jfr が提供する全てのイベントはこれを活用します。 このリクエストIDは、同一HTTPリクエスト内で記録される全てのイベントで同じIDが使用されます。 これは、さまざまな箇所で記録されるイベントを関連付け、問題箇所の切り分けが容易になります。 たとえば、アプリケーションは提供するREST APIの中でデータストアへアクセスするとします。 このとき、「REST APIの処理」と「その処理内で行われるデータストアへのアクセス」は、同一のリクエストIDを持ち、それぞれで作成されるイベントは関連付けられます。 もし、REST APIの処理時間が長期化した場合に、みなさんはその処理内で行われたデータストアへのアクセスが長期化したせいでREST APIの処理時間が長期化したのかどうかを簡単に確認できます。 quarkus-jfr はOpenTelemetryとも連携します。 Quarkusが OpenTelemetry を使用している場合、 quarkus-jfr は Opentelemetry の Trace IDとSpan IDをリクエストIDとして使用します。 これにより、OpenTelemetry でシステム全体を確認し、問題があった場合に、特定のQuarkusアプリケーションへのドリルダウンを容易にします。 [NOTE] 今はREST APIのイベントのみを記録します。 quarkus-jfr はQuarkusの標準のエクステンションです。 そのため、依存関係を追加するだけで使用できます。 <dependency> <groupId> io.quarkus </groupId> <artifactId> quarkus-jfr </artifactId> </dependency> アプリケーションの修正は必要ありません。 依存関係を追加するだけで、自動的に情報を記録します。 Quarkusは、その下で動いているJava仮想マシンでJFRを起動しなければなりません。 JFRを起動する方法はさまざまあります。 ここでは、Javaアプリケーションの起動時にJFRを起動する方法を紹介します。 起動時にJFRも起動するには、JVM起動引数に -XX:StartFlightRecording を追加します。 JFRが初めての場合は以下のように設定すると良いでしょう。 export JAVA_OPTS =-XX:StartFlightRecording = name =quarkus, dumponexit =true, filename =myrecording.jfr 上のコマンドを実行したら、次に、アプリケーションを起動します。 mvn と java を使う方法はそれぞれ以下のようになります。 どちらかを実行すればアプリケーションが JFR の記録を開始して起動します。 mvn -Djvm . args = " ${JAVA_OPTS} " quarkus:dev java ${JAVA_OPTS} -jar quarkus-app.jar あとは、これまで同様にアプリケーションを使うだけです。 先ほどの起動引数では、Javaアプリケーション終了時にJFRの情報を myrecording.jfr というファイルにダンプするようになっています。 このダンプファイルは、専用のビューアであるJDK Mission Control (JMC) や、 jfr コマンドで開けます。 JMCでダンプファイルを開き、スレッドの情報を見るビューを開いてみましょう。 そうすると、最初に紹介した図が見られるようになります。 気になるイベントが見つかれば、そのイベントのTrace IDやSpan IDにフォーカスすることで、同一のIDを持つイベントだけが表示されます。 これによって、同一リクエスト内でどのような処理がどのくらい時間を要しているかを簡単に特定できます。 さいごに quarkus-jfr は開発中のリポジトリに統合されました。 先日 Quarkus 3.11 のタグ付けがされたことから、おそらく Quarkus 3.12 で使用できるようになるでしょう。 Red Hat が提供する Red Hat build of Quarkus では、新たな LTS が登場すると使用できると思いますが、サポートされるかどうかは今のところ分かりません。 新たな情報が分かり次第この場で共有させていただきます。 興味のある方は開発中のリポジトリをクローンすることで今すぐ試せますので、ぜひリポジトリをクローンしてみて下さい。
Red Hatでソリューションアーキテクトをしている田中司恩( @tnk4on )です。 Podman v5.1.0がリリースされました! github.com リリースノートの先頭にあるRosettaのサポートは私がPull Requestを書いて機能を実装したものです。 github.com 本記事ではこの「PodmanのRosettaのサポート」について紹介します。 (※本文の初出は執筆中のPodman Advanced Pod-02からの先出しで、そこから内容を抜粋したものとなります。) (2024年6月28日、追記)Podman Desktop v1.11でGUIからRosettaの設定ができるようになりました。 -目次- Rosettaとは Podmanにおけるエミュレーションの利用 PodmanにおけるRosettaサポートの概要 実行方法 動作確認 Rosettaの無効化 Rosettaのアンインストール 既知の問題 まとめ 参考:Sysbenchの結果 Rosettaとは AppleのRosettaは、異なるプロセッサアーキテクチャ間の互換性を提供するためのバイナリトランスレーション技術です。初代Rosettaは *1 、2006年にAppleがPowerPCからIntelのx86アーキテクチャに移行する際の支援技術として導入されました。 その後、Rosetta 2は *2 、2020年にAppleがIntel x86から自社製のApple Silicon(ARMベース)への移行の際に導入されました。初代Rosetta、Rosetta 2のどちらもアプリケーションの移行を支援し、Mac上でのアプリケーションの実行を前提としています。 (初代)Rosetta:2006年リリース。PowerPC向けアプリをIntelチップのMac上で実行する目的 Rosetta 2:2020年リリース。Intel向けアプリをApple SiliconチップのMac上で実行する目的 その後リリースされたmacOS 13(macOS Ventura)では、Virtualization FrameworkがRosetta 2(以後、Rosetta)をサポートしました。 これによりApple Silicon搭載MacにおけるLinux仮想マシン(ARMアーキテクチャーのLinux)上でx86_64バイナリを実行できるようになりました *3 。 Podman v5.1.0で追加されたRosettaのサポートはこの技術を利用します。 Podmanにおけるエミュレーションの利用 QEMUを使ったPodmanでのエミュレーションのアーキテクチャー Podmanはコンテナの実行時やビルド時に --arch や --platform をオプションを付けることで、実行元ホストのCPUアーキテクチャーとは異なるコンテナイメージを実行できます。 Podman machineでは、デフォルトで qemu-user-static がインストールされているため、利用者はその内部の仕組みを意識することなくエミュレーションを使うことができます。 % arch arm64 % podman run --rm --arch amd64 ubi9 uname -m x86_64 % podman build -t arch-test --platform linux/amd64 . % podman inspect arch-test --format {{.Architecture}} amd64 他にも、たとえばamd64のイメージしかないコンテナイメージを実行した場合に、アーキテクチャーの指定をしなくても自動でエミュレーションが行われる機能もあります。この場合は実行元ホストのCPUアーキテクチャーと異なっている警告が表示されます。 % podman run -d --name mssql mcr.microsoft.com/mssql/rhel/server WARNING: image platform (linux/amd64) does not match the expected platform (linux/arm64) a7f77aa2262fef96dbed32cf311ed2ab0327a4393dcdd8b2dc0c4ea3606838cb このようにこれまでもエミュレーションの利用はできましたが、QEMU *4 の機能を使ったCPUエミュレーションのためネイティブな環境での実行に比べて性能面での課題が多くあります。 ARMベースのApple Silicon MacにおけるRosettaのサポートは、この性能面の課題を解決する技術となります。 PodmanにおけるRosettaサポートの概要 Rosettaを使ったPodmanでのエミュレーションのアーキテクチャー Podman v5.1.0で実装されたRosettaのサポートにより、qemu-user-staticの代わりにRosettaを使用した高性能なx86_64エミュレーションができるようになりました。 このRosettaを使用したx86_64エミュレーションにより、ネイティブなx86_64環境で実行した場合と遜色ない性能をApple Silicon Mac上で利用できます。 手元でのSysbenchを使った簡易ベンチマークの結果では、Rosettaを使った場合はネイティブ環境と比較して85-6%程度の性能が出ました(QEMUの場合は4%程度)。QEMU vs Rosettaの場合は22倍以上の性能差となりました(Sysbenchのベンチマーク結果は本記事の最後に載せておきます)。 エミュレーション性能が向上したことでコンテナイメージのビルド時の dnf update や dnf install で大量のパッケージのインストールなども、I/O処理性能の大幅な向上に伴い短時間で終了できるようになります。 またその他の利点として、QEMUのエミュレーションでは実行できないコンテナイメージを実行できる場合があります。ケースバイケースではありますが、QEMUではうまく実行できないコンテナイメージをRosettaで起動できる場合があります。ただし、その逆のケースもありますので、Rosetta経由で実行できない場合は後述するRosettaを無効化して対応ください。Rosettaを使うか使わないかはグローバルな設定となるため、個別のコンテナ単位で選択できない点はご注意ください。 実行方法 RosettaのサポートはPodman v5.1.0以降ではデフォルトで有効化されていますがいくつか前提条件があります。 前提条件: Apple Silicon搭載Mac(M1、M2、M3チップのMacシリーズ、※執筆時点) macOS 13(macOS Ventura)以降 Podman v5.1.0以降 Rosettaに対応するためには、Podman v5.1.0以降の podman machine コマンドを使用してPodman machineを作成します。 既存のPodman v5.0のPodman machineがある場合は一度削除し再作成します。 なお、Podman v5.0のPodman machineをアップデートする方法は既知の問題として後述します。 既存のPodman machineを削除します。 -f オプションをつけると確認画面を出さず即座に削除します。 ※ Podman machine内のすべてのデータは削除されますのでご注意ください % podman machine rm -f Podman machineを作成します。 --now オプションを付けると作成と同時にPodman machineを起動できます。 % podman machine init --now この時点でmacOS上にRosettaがインストールされていない場合は、Rosettaのインストールを促すウィンドウが表示されます。 インストールするファイルは軽量で(数百KB程度)、インストール自体は一瞬で完了します。 その後続けてPodman machineの起動が実行されます。 Rosetta 2のインストール画面 --arch amd64 を付けてコンテナを実行しエミュレーションが動作しているか確認します。 % uname -m arm64 % podman run --rm --arch amd64 ubi9 uname -m x86_64 Rosettaを手動でインストールする場合は softwareupdate コマンドを使用します。 --agree-to-license オプションを付けることでライセンス契約同意のインタラクティブな手順を省略できます。 % softwareupdate --install-rosetta --agree-to-license 動作確認 Rosettaが使用できるかどうかはいくつかの確認方法があります。 podman machine inspect を使いPodman machineの設定を確認します。デフォルトで有効化されているので true が自動で設定されます。 % podman machine inspect [ { "ConfigDir": { "Path": "/Users/shtanaka/.config/containers/podman/machine/applehv" }, ... "Rootful": false, "Rosetta": true } ] または、 --format オプションを使うことでJSONデータをパースできます % podman machine inspect --format {{.Rosetta}} true podman machine inspect で表示されるのはあくまで設定上のフラグなので、Podman machine内で使用できる状態かどうかを確認します。 podman machine ssh コマンドでPodman machineにログインしてコマンドを実行します。 /mnt/ 配下にrosettaとrosettadファイルがマウントされているか確認します。 % podman machine ssh ... core@localhost:~$ ls -ld /mnt/rosetta* -rwxr-xr-x. 1 core core 1660888 Feb 15 11:04 /mnt/rosetta -rwxr-xr-x. 1 core core 298792 Feb 15 11:04 /mnt/rosettad 次に、binfmt_misc *5 にrosettaが登録されているか確認します core@localhost:~$ ls -ld /proc/sys/fs/binfmt_misc/rosetta -rw-r--r--. 1 root root 0 May 21 09:30 /proc/sys/fs/binfmt_misc/rosetta Podman machineにはデフォルトでqemu-user-staticがインストールされており、Rosetta以外のエミュレーションにも対応します。 core@localhost:~$ podman run --rm --arch s390x ubi9 uname -m s390x 登録されているエミュレーション方式は /proc/sys/fs/binfmt_misc リストすると確認できます core@localhost:~$ ls -l /proc/sys/fs/binfmt_misc total 0 -rw-r--r--. 1 root root 0 May 21 09:30 qemu-aarch64_be -rw-r--r--. 1 root root 0 May 21 09:30 qemu-alpha -rw-r--r--. 1 root root 0 May 21 09:30 qemu-armeb -rw-r--r--. 1 root root 0 May 21 09:30 qemu-hexagon -rw-r--r--. 1 root root 0 May 21 09:30 qemu-hppa -rw-r--r--. 1 root root 0 May 21 09:30 qemu-i386 -rw-r--r--. 1 root root 0 May 21 09:30 qemu-i486 -rw-r--r--. 1 root root 0 May 21 09:30 qemu-loongarch64 -rw-r--r--. 1 root root 0 May 21 09:30 qemu-m68k -rw-r--r--. 1 root root 0 May 21 09:30 qemu-microblaze -rw-r--r--. 1 root root 0 May 21 09:30 qemu-microblazeel -rw-r--r--. 1 root root 0 May 21 09:30 qemu-mips -rw-r--r--. 1 root root 0 May 21 09:30 qemu-mips64 -rw-r--r--. 1 root root 0 May 21 09:30 qemu-mips64el -rw-r--r--. 1 root root 0 May 21 09:30 qemu-mipsel -rw-r--r--. 1 root root 0 May 21 09:30 qemu-mipsn32 -rw-r--r--. 1 root root 0 May 21 09:30 qemu-mipsn32el -rw-r--r--. 1 root root 0 May 21 09:30 qemu-or1k -rw-r--r--. 1 root root 0 May 21 09:30 qemu-ppc -rw-r--r--. 1 root root 0 May 21 09:30 qemu-ppc64 -rw-r--r--. 1 root root 0 May 21 09:30 qemu-ppc64le -rw-r--r--. 1 root root 0 May 21 09:30 qemu-riscv32 -rw-r--r--. 1 root root 0 May 21 09:30 qemu-riscv64 -rw-r--r--. 1 root root 0 May 21 09:30 qemu-s390x -rw-r--r--. 1 root root 0 May 21 09:30 qemu-sh4 -rw-r--r--. 1 root root 0 May 21 09:30 qemu-sh4eb -rw-r--r--. 1 root root 0 May 21 09:30 qemu-sparc -rw-r--r--. 1 root root 0 May 21 09:30 qemu-sparc32plus -rw-r--r--. 1 root root 0 May 21 09:30 qemu-sparc64 -rw-r--r--. 1 root root 0 May 21 09:30 qemu-xtensa -rw-r--r--. 1 root root 0 May 21 09:30 qemu-xtensaeb --w-------. 1 root root 0 May 21 09:30 register -rw-r--r--. 1 root root 0 May 21 09:30 rosetta -rw-r--r--. 1 root root 0 May 21 09:30 status Rosettaが有効化されている場合、排他的にqemu-x86_64は無効化されておりbinfmt_miscディレクトリには存在しません。逆にRosettaが無効化された場合はqemu-x86_64が登録されてQEMUによるx86_64バイナリの実行がエミュレーションに使用されます。 その他の確認方法としてプロセス上でRosettaが動作していることを確認する方法もあります。 Rosettaを使ったコンテナを実行し、 podman top コマンドを使用してプロセスの状況を確認します。 podman top *6 はpsコマンド互換のAIXフォーマットで出力されます。 % podman run --rm -d --name rosetta --arch amd64 ubi9 sleep inf % podman top rosetta USER PID PPID %CPU ELAPSED TTY TIME COMMAND root 1 0 0.000 11.303605881s ? 0s /mnt/rosetta /usr/bin/coreutils --coreutils-prog-shebang=sleep /usr/bin/sleep inf Rosettaの無効化 Rosettaを無効化するには containers.conf のmachineセクションに rosetta=false を追加します。 macOSの場合ユーザー固有の設定ファイルは $HOME/.config/containers に配置します。 containers.conf にファイルがまだない場合は新規でファイルを作成してください。 % cat > ~/.config/containers/containers.conf <<EOF [machine] rosetta=false EOF または、 $HOME/.config/containers.conf.d ディレクトリに、*.confの任意のファイル名で特定の設定のみを分割管理できます。 containers.confにすでに既存の設定があり、変更を加えたくない場合などには便利な方法です。 % mkdir -p ~/.config/containers.conf.d % cat > ~/.config/containers/containers.conf.d/machine.conf <<EOF [machine] rosetta=false EOF containers.conf に変更を加えた場合はPodman machineを再起動して設定を反映します。 % podman machine stop % podman machine start % podman machine inspect --format {{.Rosetta}} false Rosettaを無効化すると/mnt/rosetta*のマウントはされなくなり、binfmt_miscにはrosettaの代わりにqemu-x86_64が登録されます % podman machine ssh ... core@localhost:~$ ls -ld /mnt/rosetta* ls: cannot access '/mnt/rosetta*': No such file or directory core@localhost:~$ ls -ld /proc/sys/fs/binfmt_misc/rosetta ls: cannot access '/proc/sys/fs/binfmt_misc/rosetta': No such file or directory core@localhost:~$ ls -ld /proc/sys/fs/binfmt_misc/qemu-x86_64 -rw-r--r--. 1 root root 0 May 22 03:07 /proc/sys/fs/binfmt_misc/qemu-x86_64 QEMUのエミュレーションを使った場合の podman top コマンド出力では qemu-x86_64-static が使用されていることが確認できます。 % podman run --rm -d --name qemu --arch amd64 ubi9 sleep inf % podman top qemu USER PID PPID %CPU ELAPSED TTY TIME COMMAND root 1 0 0.000 29.883195833s ? 0s /usr/bin/qemu-x86_64-static /usr/bin/coreutils --coreutils-prog-shebang=sleep /usr/bin/sleep inf containers.confを使ったRosettaの設定については、containers/commonのドキュメントに記載がありますので参考にしてください。 common/docs/containers.conf.5.md at main · containers/common PodmanのGUIツールであるPodman DesktopにはRosettaを有効化/無効化設定する機能がまだありません。 機能追加のリクエストは上げたので、もしかしたら今後はGUIを通じて設定ができるようになるかもしれません。 github.com (2024年6月28日、追記) Podman Desktop v1.11でGUIでRosettaの有効化/無効化する機能が追加されました。 podman-desktop.io Settings > Resources > Podman 歯車マーク(Setup Podman)を押すとPodman拡張機能の設定画面内にRosettaの設定項目があります。 Podman DesktopにおけるRosettaの設定画面 設定の変更後はCLIの場合と同じで、適用するにはPodman machineの再起動が必要です。 Rosettaのアンインストール RosettaをアンインストールするにはmacOSのシステム整合性保護(SIP) *7 *8 を無効化して、対象のファイルを手動で削除する必要があります。 SIPを無効化するには下記の手順を実行します。 *9 再起動しリカバリーモードで起動します ユーティリティーメニューからターミナルを起動します csrutil disable コマンドを実行します 再起動します SIPを無効化した状態でターミナルを起動し、該当のファイルを削除します。 Rosettaの関連ファイルのインストール場所を確認します。 % ls -l /Library/Apple/usr/lib/libRosettaAot.dylib % ls -l /Library/Apple/usr/libexec % ls -l /Library/Apple/usr/share/rosetta sudo付きでファイルとフォルダの削除を実行します。 % sudo rm -rf /Library/Apple/usr/lib/libRosettaAot.dylib % sudo rm -rf /Library/Apple/usr/libexec % sudo rm -rf /Library/Apple/usr/share/rosetta Rosettaのファイルを削除したらSIPを有効化します。SIPを有効化する手順は無効化の手順と同様です。 再起動しリカバリーモードで起動します ユーティリティーメニューからターミナルを起動します csrutil enable コマンドを実行します 再起動します 既知の問題 Rosettaサポートを利用するためには、Podman CLI v5.1.0以上とPodman machine v5.1以上の組み合わせが必要です。 Podman v5.0で作成したPodman machineはPodman machine v5.0のイメージを使用しており、Rosettaには対応していません。 また、Podman machineの機能でv5.0からv5.1への移行機能は現時点でありません。 Podman machineはFedora CoreOS(FCOS)をベースとしており、FCOSの標準機能の rpm-ostree upgrade コマンドは使えるものの、 参照先のイメージが適切に設定されておらず、コマンドの実行が失敗します。 この問題はすでにIssueとして作成されています。 Can't upgrade core machine · Issue #22678 · containers/podman 現時点でのワークアラウンドとしては、 podman machine os apply コマンドを使用し、Podman machine v5.1のイメージを指定することで更新は可能です。 ただし、この方法は公式な更新手段として提供されているわけではないため、あくまで一時的な処置としてご利用ください。 推奨方法としては、本記事で紹介したPodman machineの再作成となります。 まとめ Podman v5.1.0で追加されたRosettaサポートについて解説しました。 macOSユーザー待望の機能がようやく正式に使えるようになりました。 これまでエミュレーションやマルチアーキテクチャーイメージに触れることがなかった方も、ぜひこの機会にいろいろ試してこの便利さを体感してください。 また、本記事では紹介できなかったPull Requestの作業を通じて実装したRosettaサポート周りの技術や苦労話などを、別の機会にディープダイブして解説したいと思います。 参考:Sysbenchの結果 arm64コンテナイメージで実行 出力結果を開く [root@9a0ccd4c2d7f /]# sysbench cpu run sysbench 1.0.20 (using system LuaJIT 2.1.0-beta3) Running the test with following options: Number of threads: 1 Initializing random number generator from current time Prime numbers limit: 10000 Initializing worker threads... Threads started! CPU speed: events per second: 8406.67 General statistics: total time: 10.0002s total number of events: 84075 Latency (ms): min: 0.12 avg: 0.12 max: 0.52 95th percentile: 0.13 sum: 9982.11 Threads fairness: events (avg/stddev): 84075.0000/0.00 execution time (avg/stddev): 9.9821/0.00 amd64コンテナイメージをRosettaを使って実行 出力結果を開く [root@a693e721cbd3 /]# sysbench cpu run sysbench 1.0.20 (using system LuaJIT 2.1.0-beta3) Running the test with following options: Number of threads: 1 Initializing random number generator from current time Prime numbers limit: 10000 Initializing worker threads... Threads started! CPU speed: events per second: 7241.41 General statistics: total time: 10.0006s total number of events: 72425 Latency (ms): min: 0.14 avg: 0.14 max: 0.28 95th percentile: 0.14 sum: 9989.74 Threads fairness: events (avg/stddev): 72425.0000/0.00 execution time (avg/stddev): 9.9897/0.00 amd64コンテナイメージをqemu-user-staticを使って実行 出力結果を開く [root@1ef656b6323c /]# sysbench cpu run sysbench 1.0.20 (using system LuaJIT 2.1.0-beta3) Running the test with following options: Number of threads: 1 Initializing random number generator from current time Prime numbers limit: 10000 Initializing worker threads... Threads started! CPU speed: events per second: 323.51 General statistics: total time: 10.0037s total number of events: 3237 Latency (ms): min: 3.05 avg: 3.09 max: 3.93 95th percentile: 3.19 sum: 9996.01 Threads fairness: events (avg/stddev): 3237.0000/0.00 execution time (avg/stddev): 9.9960/0.00 *1 : Apple - Rosetta *2 : Appleシリコン搭載MacのRosetta 2 - Apple サポート (日本) *3 : Running Intel Binaries in Linux VMs with Rosetta | Apple Developer Documentation *4 : QEMU / QEMU · GitLab *5 : Kernel Support for miscellaneous Binary Formats (binfmt_misc) — The Linux Kernel documentation *6 : podman-top — Podman documentation *7 : システム整合性保護 - Apple サポート (日本) *8 : Mac のシステム整合性保護について - Apple サポート (日本) *9 : Disabling and Enabling System Integrity Protection | Apple Developer Documentation
Red Hatの織です。RHELとOpenShiftをFIPS準拠モードについて解説する記事... に見せかけて、Red HatがGoアプリケーションをFIPS対応するためにGo言語ツールチェインを改造していることについて調べた話です。 zenn.dev
レッドハットのソリューションアーキテクトの森です。 Red Hat build of Debezium 2.5.4 が先日リリースされました。 Debezium を使うと、データベース内のデータやスキーマの変更をチェンジイベントとして取得し、AMQ Streams(Apache Kafka)のトピックに格納し、他のターゲットシステムにニアリアルタイムで変更内容を連携する事ができます。 access.redhat.com 今回の2.5.4リリースでは、今までテクニカルプレビューだった、JDBC Sink Connector がGAになりました。 この JDBC Sink Connector は、Debeziumを通じてキャプチャしたチェンジイベントを、JDBC ドライバーを使用してリレーショナルデータベースに書き込むことができます。 今回は、OpenShift上にMySQLのデータベースを構築し、Debeziumでチェンジイベントをキャプチャしているという想定で、 このチェンジイベントを、JDBC Sink Connector を使ってPostgreSQLに反映し、データベースの同期を行う仕組みを作っていきたいと思います。 OpenShift上にMySQLをデプロイし、Debezium Source Connectorを設定しました。 MySQLには以下のようなサンプルのデータベースを作成し、5件のレコードをInsertしています。 Debeziumがこれをキャプチャし、 debezium.sampledb.order_data というトピックに、チェンジイベントが5件格納されています。 { "schema": { "type": "struct", "fields": [ 〜 中略 〜 (イベントメッセージのスキーマ情報) ], "optional": false, "name": "debezium.sampledb.order_data.Envelope", "version": 1 }, "payload": { "before": null, "after": { "orderid": "2e6d79c6-dea8-4f7a-9ae1-0120c3e6d4dc", "orderitemname": "Orange Mango", "firstname": "Aubrey", "lastname": "Schamberger", "quantity": 177, "price": "AVU=", "shipmentaddress": "P.O. Box 724, 731 Pharetra Avenue", "zipcode": 11570, "creditcardnumber": "3812-7582-9602-6055", "creditcardexpiryDate": "2023/06", "creditcardtype": "solo" }, "source": { "version": "2.5.4.Final-redhat-00001", "connector": "mysql", "name": "debezium", "ts_ms": 1715921665000, "snapshot": "false", "db": "sampledb", "sequence": null, "table": "order_data", "server_id": 1, "gtid": null, "file": "binlog.000002", "pos": 3236, "row": 0, "thread": 58, "query": null }, "op": "c", "ts_ms": 1715921665681, "transaction": null } } "payload"以下の、"before"には更新前のレコードの状態、"after"には更新後のレコードの状態があります。 "op"は操作のタイプを示しており、"c"はCREATE(新規)、"d"はDELETE(削除)、"u"はUPDATE(更新)、"r"はREAD(参照)です。 それでは、同期先のPostgreSQLへ更新を反映するSink Connectorを実装していきます。 PostgreSQLは、OpenShiftのテンプレートを使ってデプロイしました。Sink Connectorに関する設定は特に必要ありません。 (PostgreSQLのデプロイについての詳細は省略します) まだデプロイしたのみで、テーブルなどは作成していない状態です。 Debezium JDBC Sink Connector のアーカイブを Kafka Connect に追加します。 OpenShift Web Console の AMQ Streams Operator から、Kafka Connect の設定を開き、 spec.build.plugins.artifacts にJDBC Sink Connector のリポジトリのURLと名称を追加します。 spec: build: plugins: - artifacts: - type: zip url: 'https://maven.repository.redhat.com/ga/io/debezium/debezium-connector-jdbc/2.5.4.Final-redhat-00001/debezium-connector-jdbc-2.5.4.Final-redhat-00001-plugin.zip' name: debezium-connector-jdbc Sink Connector の 設定を Kafka Connector に追加して、起動します。 OpenShift Web Console の AMQ Streams Operator から、Kafka Connect のタブを選択し、 Create Kafka Connector で新しい Kafka Connector を作成します。 apiVersion: kafka.strimzi.io/v1beta2 kind: KafkaConnector metadata: labels: strimzi.io/cluster: debezium-kafka-connect-cluster # JDBCコネクタのURLを追加した KafkaConnect のNameと合わせる name: debezium-connector-jdbc spec: name: debezium-connector-jdbc tasksMax: 1 # コネクタで使用するタスクの最大数 class: io.debezium.connector.jdbc.JdbcSinkConnector # Debezium JDBC コネクタの場合は、io.debezium.connector.jdbc.jdbcSinkConnector を指定 config: connection.url: jdbc:postgresql://postgresql.postgresql-dev.svc.cluster.local:5432/sampledb # データベースへの接続に使用するJDBC接続URL connection.username: "demo" # コネクタがデータベースへの接続に使用するusername connection.password: "demo" # コネクタがデータベースへの接続に使用するpassword topics: debezium.sampledb.order_data # 受信するKafkaのトピックのリスト、複数ある場合はカンマ区切りで指定 insert.mode: upsert # データベースへのイベント処理に関する設定。 insert, update, upsertの3種類から選択。 delete.enabled: true # DELETE イベントを処理して、対応する行をデータベースから削除するかどうかを指定。 primary.key.mode: record_key # コネクタがイベントからプライマリキーを取得する方法。DELETE イベントを処理する場合は record_key に設定。 primary.key.fields: "orderid" # プライマリキーのカラム名 schema.evolution: basic # コネクタが対象のテーブルのスキーマの変更への対応の有無を指定。 database.time_zone: UTC # 時間値を挿入する際に使用するタイムゾーンを指定します。 コンフィグパラメータの詳細については、 製品ドキュメント を参照ください。 しばらく待って、 Condition: Ready になったら設定完了です。 PosgreSQLのテーブルを確認してみます。 先ほどは無かった、 debezium_sampledb_order_data というテーブルが作成されています。 中身を見ると、MySQLのサンプルテーブルのカラムと同じ構成になっています。 レコードを確認すると、MySQLのサンプルテーブルと同じものが5件格納されています。 MySQL 側に変更(INSERT, UPDATE, DELETE)を行ってみます。 insert into order_data (orderid, orderitemname, firstname, lastname, quantity, price, shipmentaddress, zipcode, creditcardnumber, creditcardexpirydate, creditcardtype) VALUES ('12139cb8-e5fa-4019-a743-42e6792e3c8a', 'Blueberry', 'Lester', 'Mraz', 10, 5.2, '415-2575 Integer Road', '97462', '1234-5678-9012-3456', '2028/12', 'american_express'); update order_data set creditcardexpirydate='2032/03' where orderid='d6fda7ea-0ab6-4857-aed2-0cd3ae3bd401'; delete from order_data where orderid='55de4cb6-7759-49c0-8872-88a4350e0475'; Query OK, 1 row affected (0.02 sec) INSERT("orderid":"12139cb8-e5fa-4019-a743-42e6792e3c8a"のレコードを追加) [MySQL] [PostgreSQL] UPDATE("orderid":"d6fda7ea-0ab6-4857-aed2-0cd3ae3bd401"のレコードの"creditcardexpirydate"を変更) [MySQL] [PostgreSQL] DELETE("orderid":"55de4cb6-7759-49c0-8872-88a4350e0475"のレコードを削除) [MySQL] [PostgreSQL] MySQLに対する操作が、PostgreSQLにも反映されていることが確認できました。 いかがでしょうか? Debezium JDBC Sink Connector を使うことで、チェンジイベントからデータベースの同期を図る仕組みの構築がより簡単になりました。 Debeziumによるチェンジデータキャプチャは、データのレプリケーションや、マイクロサービス間のデータ連携、参照モデルの更新など、様々なユースケースで活用する事ができます。 Debeziumに関して興味がある・もう少し詳しい内容が聞きたい、などありましたら、是非弊社までお問合せ頂ければと思います。
ソリューションアーキテクトの瀬戸です。 左を見ても右を見てもAIと言われている今日この頃、ChatGPTなどのサービスを動かしている方々がほとんどではないかと思います。 その中でLLMは別にクラウドじゃないと動かないわけではなく、ローカルで動かすことも可能なのですが、Pythonのインストールが難しそうとかの理由で避けている人もいるのではないかと思います。 Podman AI Labを使用する事で簡単にローカルでLLMを動作させて、チャットのやり取りを楽しむことができるので、ちょっとやってみましょう。 注意: この記事は管理者権限を持ったx64 Windows 11 Professionalと十分なメモリが積まれたPCを使用しています。8G程度のメモリしか積まれていない場合は動作が難しい可能性があるので、ご注意ください。32G以上の余裕を持ったPCを使用する事を推奨します。また、モデルはそれぞれ数ギガバイト程度の大きさがあります。ディスクスペースにも十分な空きがあることを確認してください。 Podman のダウンロード及びにインストール 注意:wslを使用したことがない場合は、インストール前にwslを有効にする必要があります。 管理者権限のあるコマンドプロンプトから以下のコマンドを実行して再起動します。 > wsl --install --no-distribution podman-desktop.io Download Now を押すことでダウンロードができます。 Podman Desktopダウンロード ダウンロードしたexeをクリックすると、そのままインストールが始まります。 Podman Desktopインストール インストールが終わったら起動します。Podmanのコマンドラインツールをインストールしていない場合は、ここでインストールします。 Podman Desktop起動画面 Podman CLI インストール インストール開始、基本的にデフォルトのままインストールしていけばOK インストール後にはPodman CLIの初期化が必要になります。Initialize and startを押します。 初期化も必要 ボタンを押すと、すぐにPodmanが起動中になることを確認できます。 Podmanの初期化完了 注意: Podman CLIのインストールの詳細については Podman for Windowsのインストールガイド を参照してください。 Podman AI Lab拡張機能のインストール Podman Desktopがセットアップされたら、次は拡張機能をインストールします。 左の ジグソーパズルのようなアイコン をクリックして、 検索欄にAI を入力、 Catalog をクリックすることでPodman AI Labが表示されるので、そのままインストールボタンを押します。 Podman AI Lab Extentionのインストール セットアップが完了すると左のメニューに、Podman AI Labのアイコンが増えていることを確認できます。 AI labのアイコン AI Labを実際に起動してみる ここまで来たらあとちょっとです。左のアイコンをクリックすることで、AI Labの初期画面に移ります。 AI Lab起動画面 Recipes Catalogを選択することで、動かしたいアプリの種類を選択することができます。今回は、ChatBotを選択します。 注意: 他のアプリを選択することもできます。 レシピの選択 そのあとすぐにStart AI Appを押したくなりますが、ぐっとこらえて、Modelsを選択します。 Modelsの選択 すると、下の方で動かしたいLLMモデルを選択できるので、好きなものを選択します。今回は ibm/merlinite-7b-GGUFを選択しています。 modelの選択 Start AI Appを押すことでチャットボットが起動します。モデルのダウンロードに時間がかかりますので少々お待ちください。 チャットボットの起動 チャットボットの起動中 注意: モデルを選択しなかった場合、デフォルトではinstructlab/granite-7b-lab-GGUFが選択されているはずです。 AI アプリを使ってみる 起動に成功すると以下のような画面になるので、右側のOpen AI Appボタンをクリックします。 チャットボット起動完了 すると、画面が開いて質問し放題です。対応している言語や回答内容等、AIの性能については選択した言語モデルに依存します。 チャットボット画面 まとめ 駆け足ながらPodman AI Labを使用してローカルでAI環境を構築するところまで行いました。 Podman AI Labではすでに用意されているLLMだけではなく、自分自身で構築したものやRed Hat以外が準備したLLMを使用する事もできます。 そういったPodman AI Labのその他の機能に興味がある場合は、developers.redhat.comの記事、 Getting started with Podman AI Lab を参照してみてください。 Getting started with Podman AI Lab | Red Hat Developer Introducing Podman AI Lab: Developer tooling for working with LLMs | Red Hat Developer Podman Red HatがPodman AI Labを発表
皆様はEvent-Driven Ansible(以下EDA)をご存知でしょうか?通常のAnsibleでは人がPlaybookをコマンドラインやAutomation Controllerから実行します。一方で、EDAでは他のソフトウェア(監視システム等)からメッセージを受取り、そのメッセージの内容に応じて決められたジョブテンプレート・ワークフローを実行するというものになります。 EDAに関しての詳細は以下の記事をご参照ください。 Event-Driven Ansible の始め方 Event-Driven Ansible と IBM Turbonomic / Instana の連携 Ansible Automation Platform 2.4 注目の新機能紹介 今回の記事では、このEDAを用いて、日本で人気の高い Zabbix と連携して障害対応を自動化する例についてご紹介いたします。 EDA と Zabbix の連携には以下の特徴があり、非常に簡単に導入することができるのが嬉しい点になっています。 Zabbix には EDA連携の機能が含まれている 既存の通知設定を変更するだけでEDAと連携可能になる(メール通知しているところにEDA連携を追加する、など) ここからはZabbixとEDAの連携について動画を交えつつ、簡単に導入方法を紹介していきます。大まかな流れは以下になります。 EDA環境を構築する → Event-Driven Ansible の始め方 をご参照ください ZabbixにEDA連携の設定を行う Zabbixからのメッセージを処理するルールブックと、起動するPlaybookを準備して、EDAでアクティベーションする EDA環境の構築については、先の記事で紹介されておりますので、ZabbixにEDA連携の設定を行う部分から紹介していきます。 Zabbix の設定 Zabbixから設定方法のドキュメントが準備されていますので、そちらを参考に設定してきましょう。 https://www.zabbix.com/jp/integrations/ansible#event_driven_ansible まずはEDA連携用のメディアタイプを追加します。 次に通知先のユーザーを作成し、連絡先をEDAサーバーに設定します。この時、EDAサーバーのポート番号はこのあとで作成するEDAのルールブックの中で決めることになります。あらかじめ、どのポートを使って連携するかを決めておきましょう。 最後にトリガーアクションを設定します。トリガーアクションは監視に対して、エラーが起きた場合のアクションを設定することができます。ここにトリガーが起動されたさいにユーザーへのメッセージ通知を行うように設定します。そして通知先のユーザーは先に作成したEDA用のユーザーになります。 Zabbix 側の準備はこれで終了となります。これでトリガーが起動されると、指定したEDAサーバーにメッセージが送信されるようになります。 ルールブックの設定 EDAではZabbixから送信されるメッセージに対応するルールを記述します。 執筆時点での Zabbix は以下のような形式のメッセージを送信してきます。 {'event': {'meta': {'endpoint': 'endpoint', (省略) 'payload': {'HTTPProxy': '', 'acknowledged': 'No', 'endpoint': '/endpoint', 'event_date': '2023.10.26', 'event_datetime_timestamp': 1698281140, 'event_id': 35, 'event_name': 'VM2死活監視', 'event_nseverity': 4, 'event_object': 0, 'event_severity': 'High', 'event_source': 0, 'event_tags': {}, 'event_time': '00:45:40', 'event_value': 1, 'host_groups': ['Linux servers'], 'host_host': 'demo-hostB', 'host_id': 10602, 'host_ip': '172.31.0.155', 'host_port': '10050', 'monitoring_source': 'Zabbix sever', 'operation_data': '0', 'send_to': '11.22.33.44:8888', 'subject': 'Event ID: 35, Host: ' 'demo-hostB, Problem: VM2死活監視', 'trigger_description': '', 'trigger_id': 23415, 'trigger_name': 'VM2死活監視エラー'}}}} ルールブックでは、このデータを使って判定を行い、対応するジョブテンプレートの起動を行うことになります。 例えば以下のようなルールブックになります。 --- - name: Listen for webhook 8888 and Debug hosts: all sources: - ansible.eda.webhook: host: 0.0.0.0 port: 8888 rules: - name: Trigger action of scenario 1 condition: >- event.payload.host_host == 'demo-agent1' and event.payload.trigger_name == 'monitoring1_trigger1' and event.payload.event_severity == 'High' action: run_job_template: name: scenario1_action organization: Default job_args: extra_vars: z_host_name: "{{ event.payload.host_host }}" z_host_id: "{{ event.payload.host_id }}" z_event_id: "{{ event.payload.event_id }}" z_event_severity: "{{ event.payload.event_severity }}" z_trigger_name: "{{ event.payload.trigger_name }}" - name: Trigger action of scenario 2 condition: >- event.payload.host_host == 'demo-agent1' and event.payload.trigger_name == 'monitoring2_trigger2' and event.payload.event_severity == 'High' (省略) ルールブックの全体像 この例では、Zabbixからのメッセージをポート8888で待ち受け、メッセージに含まれる event.payload.host_host 障害が発生したホストの名前 event.payload.trigger_name Zabbix側のトリガー名 event.payload.event_severity Zabbix側で設定した重要度 この3つを使って識別を行っています。 そして、ルールに合致した場合は、ジョブテンプレート scenario1_action をZabbixから受け取ったデータを引数に起動するようにしています。 逆にルールにマッチしない場合はEDAは何もおこないませんので、既存のZabbix監視に対して徐々に自動対応を行う設定を加えていくことができます(一気にすべてを作らなくてもよい) あとは、このルールブックをアクティベーションし、対応するジョブテンプレート側を準備すれば完了です。 では実際に動かす例をみてみましょう。 EDA & Zabbix の動作デモ動画 このデモ動画では、EDAが動作した場合にRedmineにチケットを起こして記録するようにしてあります。 設定の確認 自動復旧の例 このデモは以下のような流れで動いています。 Zabbix が障害を検知 → EDA へ通知を行う EDA が Zabbix からの受け取ったデータをルールブックで判定して、復旧するジョブテンプレートを実行 Automation Controller から復旧を実行して、最後にRedmineへ結果を記載 承認したら自動復旧する例 このデモは以下のような流れで動いています。 Zabbix が障害を検知 → EDA へ通知を行う EDA が Zabbix からの受け取ったデータをルールブックで判定して、復旧するジョブテンプレートを実行 Automation Controller から復旧を実行するが、状態確認の段階で問題を検知 Automation Controler は問題を検知したため、Redmine へ通知を行い、処理を「実行承認待ち」の状態で待機 Redmine で通知されたチケットを確認する そのまま復旧を実行して良いと判断し「承認」を行い、停止していたジョブが再開し、障害が復旧 結果が Redmine へ通知 まとめ ご紹介した内容の通り、EDA と Zabbix を連携させることで、監視システムが上げるアラートに対する自動対応を実現することが可能となります。 EDAを用いたアラート対応の自動化には、構築していく上で以下の利点があります。 監視システム(Zabbix)と、自動化対応(Ansible)を疎結合にすることができる(監視をする組織と対処する組織の責任分界点を明確にでき、互いのシステムの影響を受けにくい状態を作れる) 既に稼働している既存の監視システムにはほどんど影響がない(通知先にEDAを追加するだけ) 徐々に自動対応の仕組みを作っていくことができる(小さく始められる) またEDAはWebhookさえ発信してもらえれば連携可能なため、応用範囲がとても広い技術となります。 今回の記事ではZabbixとの連携を紹介しておりますが、他の監視ソフトウェアや、セキュリティ系の製品とも幅広くインテグレーションすることが可能です。 ネットワーク機器のコンフィグが変更されたらEDAを起動してAnsibleでNW機器の最新コンフィグを自動でバックアップする セキュリティのアラートを検知したらEDAを起動してAnsibleで特定のIPを遮断する 新しい機器のネットワーク接続を検知したらEDAを起動してAnsibleでCMDBへ登録する といった活用も可能です。 ぜひともEvent-Driven Ansibleを活用した「自動対応」の仕組みにチャレンジしてみてください。
Red Hat のソリューションアーキテクトの瀬戸です。 この記事はRed Hat Developerのブログ記事 How JBoss EAP 8.0 makes deployment on OpenShift easier | Red Hat Developer を許可をうけて翻訳したものです。 最近 リリース された Red Hat JBoss Enterprise Application Platform (EAP) 8.0 では、 Red Hat OpenShift上の JBoss EAP アプリケーションイメージのプロビジョニングと設定の方法に変更が導入されました。JBoss EAP Maven プラグインが採用されており、OpenShift 上での JBoss EAP の設定がより簡単かつ柔軟になる大幅な改善が行われています。 この記事ではJBoss EAP Maven プラグインを既存の JBoss EAP 8.0 アプリケーションに追加するために必要な手順を説明します。また、プラグインをローカルでテストし、OpenShift UI と Helm CLI の両方から Helm を使用してアプリケーションをビルドして OpenShift にデプロイします。 JBoss EAP Maven プラグインを使う事で得られるメリット JBoss EAP Maven プラグインは次の機能を持っています。 wildfly-ee-galleon-packと eap-cloud-galleon-pack Galleon feature-packを使用し、サーバー設定ファイルをカスタマイズするためのレイヤーを選択します。 CLI スクリプト コマンドをサーバーに適用します。これらの CLI スクリプトはアプリケーションのソースコードの入ったリポジトリに簡単に追加できます。 サーバーにインストールを行うキーストアのファイルなどのファイルの追加をサポートします。 詳細については ドキュメント を参照してください。実際のプロジェクトに適用するための動作するサンプルが欲しい場合は JBoss EAP 8.0 のクイックスタート を参照してください。 JBoss EAP 7.Xの時には、OpenShift 用のイメージをビルドするときにOpenShift ビルダーイメージが JBoss EAP サーバーインスタンスをプロビジョニングしていました。設定はランタイム変数、設定スニペット、および JBoss CLI スクリプトの組み合わせによって行われていました。 JBoss EAP 8.0 では、JBoss EAP Maven プラグインがサーバーをプロビジョニングし、Maven の実行中にパッケージ化されたアプリケーションをデプロイします。このビルドプロセスのすべての設定はMaven pom.xml ファイルで記載されます。これにより開発者と運用チームはローカル環境で EAPのプロビジョニング を制御およびテストできるようになるという大きなメリットが得られます。 JBoss EAP Maven プラグインは Galleon を使用してデプロイされたアプリケーションを動作させることのできる最小限の機能セットで設定された JBoss EAP サーバーをプロビジョニングします。 Galleon はMaven リポジトリにアクセスしながら動作するプロビジョニング ツールです。 Galleon はリリースされた JBoss EAP Maven アーティファクトを自動的に取得し、ユーザーの設定に従って JBoss EAP ベースのアプリケーションサーバーのソフトウェアディストリビューションを構成します。 前提条件 Helm CLI. Version 3.5+ (OpenShift Dev Console の代わりに Helm CLI を使用する場合にのみ必要です) OpenShift cluster. Version 4.11+ Maven. Version 3.8.5+ 訳注: この記事の動作確認を訳者はWindows 11上でOpenShift Local 4.14.12を使用して行いました。自由に使えるOpenShift環境がなく、手元にインストールして試したい場合はOpenShift Local を使用する事ができます。 また、Helm CLIはWindowsでも動作します。 developers.redhat.com 訳注おわり プロジェクトのセットアップ シンプルな「hello world」JBoss EAP 8.0 アプリケーションから始めます。 GitHub からソースをチェックアウトできます。チェックアウトしたフォルダで次のコマンドを実行することで、このプロジェクトをビルドしてデプロイする対象を生成できます。 mvn clean package これでアプリケーションが ./target/helloworld.warに生成されます。このアプリケーションを JBoss EAP 8.0 でテストする場合は次の手順に従って実行できます。 ここから JBoss EAP 8.0 ディストリビューションをダウンロードします。 ディストリビューションをローカルフォルダ (例: ~/jboss-eap-8) に解凍、もしくはインストールします。 EAP_HOME 環境変数をインストール先に設定します。 export EAP_HOME=~/jboss-eap-8 ※訳注: Windowsの場合はsetコマンドを使用するか、コントロールパネルから環境変数を設定してください。 次のコマンドを使用して JBoss EAP 8.0 を起動します。 $EAP_HOME/bin/standalone.sh ※訳注: Windowsの場合は %EAP_HOME%\bin\standalone で起動できます。これ以降のコマンドも同様です。 次のコマンドを実行して、コードを複製したフォルダからJBoss CLI を使用して EAP に接続します。 $EAP_HOME/bin/jboss-cli.sh --connect JBoss CLI上からは以下のようにコマンドを実行することでデプロイできます。 [standalone@localhost:9990 /] deploy ./target/helloworld.war これで、 http://localhost:8080/helloworld/HelloWorld にあるサンプル アプリケーションにアクセスできるようになります 。 JBoss EAP 8.0 サーバーをシャットダウンします。 OpenShift サポートの追加 OpenShift サポートを追加するためにJBoss EAP Maven プラグインをプロジェクトに追加してOpenShift用のプロファイルを作成します。pom.xml ファイルの <profiles> セクションに以下を追加します。 <profile> <id> openshift </id> <build> <plugins> <plugin> <groupId> org.jboss.eap.plugins </groupId> <artifactId> eap-maven-plugin </artifactId> <version> 1.0.0.Final-redhat-00014 </version> <configuration> <channels> <channel> <manifest> <groupId> org.jboss.eap.channels </groupId> <artifactId> eap-8.0 </artifactId> </manifest> </channel> </channels> <feature-packs> <feature-pack> <location> org.jboss.eap:wildfly-ee-galleon-pack </location> </feature-pack> <feature-pack> <location> org.jboss.eap.cloud:eap-cloud-galleon-pack </location> </feature-pack> </feature-packs> <layers> <layer> cloud-server </layer> </layers> <filename> ROOT.war </filename> </configuration> <executions> <execution> <goals> <goal> package </goal> </goals> </execution> </executions> </plugin> </plugins> </build> </profile> このプロファイルは次のような意味を持ちます。 「package」ゴールを持つ「openshift」プロファイルを作成します。 eap-maven-pluginプラグインを追加します。 機能パックを使用して eap-maven-plugin を構成します。機能パックは ZIP アーカイブであり、通常はアーティファクト リポジトリ (Maven など) にデプロイされ、Galleon ツールで使用できます。これらの機能は、インストール構成の作成に使用できる事前定義された構成レイヤーで定義されます。機能パックのレイヤーを使用すると、EAP インスタンスの展開が可能になり、展開されたアプリケーションをサポートする最小限の機能が提供されます。このプロジェクトでは次の2つの機能パックを追加します。 org.jboss.eap:wildfly-ee-galleon-pack wildfly-ee-galleon-pack には、JBoss EAP のインスタンスを構築するために必要な機能が含まれています。この機能パックには、いくつかのレイヤー (jaxrs-server および cloud-server など) が含まれています。 org.jboss.eap.cloud:eap-cloud-galleon-pack org.jboss.eap.cloud:eap-cloud-galleon-pack Galleon 機能パックは、クラウド上で実行するように JBoss EAP サーバーを設定できるようにする追加機能をプロビジョニングします。 クラウドサーバー層の組み込み。クラウドサーバー層は、OpenShift にデプロイされたアプリケーションに最低限必要な層です。この層は、ヘルスやメトリクスなどのサブシステムを提供します。 ファイル名を ROOT.war に設定して、アプリケーションがルート コンテキストにデプロイされるようにしています。 JBoss EAP Maven プラグインのテスト OpenShift プロファイルと eap-maven-plugin を使ったJBoss EAP 8.0 インスタンスのプロビジョニングをテストできます。次のコマンドを実行します。 mvn package -Popenshift このコマンドは次のタスクを実行します。 /target/serverフォルダを作成してJBoss EAP のインスタンスをデプロイします。 JBoss EAP インスタンスにはpom.xml ファイルの セクションで定義された機能(この場合はクラウドサーバーレイヤー) のみが含まれます。 プロジェクトコードをパッケージ化してビルドしたファイルにROOT.warという名前を付けます。 /target/serverフォルダから JBoss EAP インスタンスを開始してROOT.warをデプロイします。 コマンドの出力は次のようになります。 [INFO] Deploying ROOT.war [disconnected /] embed-server --server-config=standalone.xml [standalone@embedded /] deploy ROOT.war --name=ROOT.war --runtime-name=ROOT.war [standalone@embedded /] stop-embedded-server 次のコマンドを実行してJBoss EAP Maven プラグインによってプロビジョニングされた JBoss EAP のインスタンスを実行できます。 ./target/server/bin/standalone.sh このコマンドは JBoss EAP 8.0 を起動します。ログにはいくつか重要な点が出力されています。 INFO [org.jboss.as.server] (Controller Boot Thread) WFLYSRV0010: Deployed "ROOT.war" (runtime-name : "ROOT.war") パッケージ化されたアプリケーションは ROOT.war としてデプロイされています。これによりルート コンテキストで実行されます。 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0054: Admin console is not enabled 管理コンソールは有効になっていません。OpenShift にデプロイされた JBoss EAP アプリケーションでは管理コンソールを有効にしないことを推奨されています。 http://localhost:8080 に移動します (図 1)。 図1: localhost上のJBoss EAPでHello Worldが動いている eap-maven-plugin が正しく設定されていることが確認できたので、 OpenShift へのデプロイに進みます。 OpenShift へのデプロイ アプリケーションを OpenShift にデプロイするには、OpenShift UIか、Helm CLI ツールからHelm チャートを利用します。 まず、次の手順に従って Helmの設定を作成します: eap8-on-ocp コードを複製したフォルダに「charts」というフォルダを作成します。 このフォルダにhelm.yaml という名前で次の内容のファイルを作成します。 build : uri : https://github.com/deewhyweb/eap8-on-ocp.git ref : 8.0.x deploy : replicas : 1 この例では、eap-maven-plugin を使用するように更新されたソース コードを含む、事前に準備されたブランチを使用しています。この yaml ファイル内の情報を置き換えることによってあなたのアプリケーションの含まれたリポジトリを使用する事もできます。 あなたのリポジトリでビルドを正しく実行するには上で説明した pom.xmlに対する変更をプッシュする必要があります。 これでアプリケーションを OpenShift にデプロイする準備が整いました。 OpenShift UI を使用した Helm チャートのデプロイ OpenShift UI に Helm チャートをデプロイするには、次の手順に従います。 OpenShift にログインします。 開発者 UI を開きます。 ※訳注: 必要に応じてデプロイを行うプロジェクトを作成してください。 左側のメニューから Helm をクリックします。 開発者カタログから Install a Helm Chart from the developer catalog をクリックします。これにより、 Helm Charts カタログ ページが表示されます。 (図 2) Filter by keyword フィールドに 「eap」と入力します。 図 2: OpenShift の Helm チャート カタログ Helm チャートのリストから eap8 を選択します。 Install Helm Chart をクリックします。 YAML view に切り替えます。 図 3 に示すように charts/helm.yaml ファイルの内容を貼り付けます。 Install をクリックして Helm チャートをデプロイします。 図 3: Helm カタログから OpenShift に EAP Helm チャートをインストールする画面 Helm CLI を使用した Helm チャートのデプロイ 次にHelm CLIを使用してローカルからHelm チャートをデプロイしていきます。次の手順を実行します。 訳注:この手順ではローカルにHelm CLIとocコマンドがインストールされており、実行できる必要があります。コマンドラインでのOpenShift操作に慣れていない場合は読み飛ばしてください。 oc loginを使用してOpenShiftにログインします。 oc new-projectを使用してプロジェクトを作成します。 jboss-eap/eap8 Helm チャートを以下のコマンドで使用します。 helm install helloworld -f charts/helm.yaml --repo https://jbossas.github.io/eap-charts/ eap8 このコマンドを実行すると次のような内容が表示されます: NAME: helloworld LAST DEPLOYED: Fri Dec 16 12:52:27 2022 NAMESPACE: eap8-helm STATUS: deployed REVISION: 1 TEST SUITE: None NOTES: バックグラウンドではEAP 8 アプリケーションがデプロイされています。ビルド状況を追跡するには、次のコマンドを実行します。(Ctrl-cで終了します。) oc get build -w (注:ビルドが完了するまでデプロイ中にErrImagePull とImagePullBackOffが報告されますが、問題ありません。ビルドが完了すると、イメージは自動的に展開されます。) アプリケーションのデプロイ状況を追跡するには次のコマンドを実行します。(Ctrl-cで終了します。) oc get deployment helloworld -w アプリケーションのデプロイメントのテスト OpenShiftのUI からは、OpenShiftのUIを使ってデプロイした場合とHelm CLIを使った場合の2つのビルドが実行されて完了していることが確認できます。その後、JBoss EAP インスタンスとデプロイされたアプリケーションを含むポッドがデプロイされます。 (図 4) 訳注: OpenShiftにUIからログインして左のメニューからTopologyを開いて、デプロイに使用したプロジェクトを選択してください。 図 4: OpenShift にデプロイされた JBoss EAP 8.0 アプリケーション アプリケーションをテストするには、ルートアイコン *1 をクリックします。OpenShift CLI を使用してルートからURLを確かめるには次のコマンドを入力します。 oc get routes 次のような出力が表示されます: NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD helloworld helloworld-eap8-helm.apps.cluster-xxx.xxx.hostname.com helloworld <all> edge/Redirect None ブラウザから http:// + host *2 にアクセスしてください。 前にデプロイしたローカル ホストのインスタンスと同様に Hello World! が表示されるはずです。アプリケーションは正常に構築され、OpenShift にデプロイされました。 要約 JBoss EAP 8.0 ではeap-maven-plugin を使用して OpenShift イメージ用の EAP をプロビジョニングおよび設定します。この記事ではeap-maven-plugin を既存の JBoss EAP 8.0 アプリケーションに追加する方法と利点を説明した後に、このプラグインを使用して JBoss EAP 8.0 サーバーのプロビジョニングをローカルでテストし、EAP 8 Helm チャートを使用してこのアプリケーションを OpenShift にデプロイしました。 *1 : 訳注: podの右上にある四角から飛び出している矢印のボタン *2 : 訳注: 上の例だと http://helloworld-eap8-helm.apps.cluster-xxx.xxx.hostname.com OpenShiftの設定によってはhttpsの場合もあります。
Red Hatの織です。Red Hat Summit 2024で発表されたImage mode for Red Hat Enterprise Linuxの仕組みについて調べた内容を、下記にまとめました。 zenn.dev
画像提供:もりわかさん OpenShift SSA の瀬戸です。 JBoss EAP 8がリリースされてから4ヶ月がたちました。8.0.1パッチも提供され、そろそろ移行をお考えの方もいらっしゃるのではないでしょうか。 以前、 JBoss EAP 7.x から JBoss EAP 8-Beta への移行方法 という翻訳記事でベータバージョンでの移行方法を紹介しましたが、1年以上の時間が経ち、状況が変わってきてしまっているのであらためて移行方法をまとめたいと思います。 この時に使用したMigration Toolkit for Runtimes(以下MTR)の廃止が決まっており、更新がされなくなってしまっており、最新のアップストリームの更新がとりこまれていません。そのため、最新の情報を扱えなくなっています。 そこで、今回はアップストリームのWindupを使用して修正個所の特定をしていきます。 windup.github.io さらに、JBoss EAPは標準のJakarta EE仕様に準拠しているため、Red Hatが関与していないOSSを使う事でも移行を行うことができます。今回は、Eclipse Transformer を使って、ユーザーアプリケーションを移行します。 projects.eclipse.org この記事では以下の順番で移行の説明をしていきます。 JBoss EAP 7.4環境のセットアップ JBoss サーバー移行ツール(JBoss Server Migration Tool)を使ってのサーバー構成の移行 Windupを使用しての移行箇所の特定 Eclipse Transformerを使ってのユーザーアプリケーションの修正 この記事では以下のツールを使用しています。 Windows 11 Professional 23H2 Openjdk 17.0.10 2024-01-16(Temurin) https://adoptium.net/temurin/releases/ Maven 3.9.6 https://maven.apache.org/ Podman Desktop https://podman-desktop.io/downloads Git 2.44.0 https://git-scm.com/download/win また、Red Hatアカウントを必要とします。Red Hatアカウントを持っていない場合は、Red Hat Developer Programに参加することで作成できますので、先に参加してください。 rheb.hatenablog.com 注意: Red HatではアップストリームのOSSの直接のサポートは行っておりません。ただし、今回の内容で移行した後の本番環境でのサポートなどは承っています。また、移行に対するご不明点がある場合は弊社営業もしくはパートナー企業にお問い合わせください。 JBoss EAP 7.4環境のセットアップ サーバー構成(standalone.xmlなど)の移行についてはベータ版から変更はありません。引き続き同じ方法が使用できます。ここでは、古いEAP7.4用のサンプルアプリケーションのデプロイも兼ねてEAP7.4のサーバーをセットアップし、その内容をそのままEAP 8.0へ移行します。 PostgreSQL のセットアップ 今回のアプリケーションではPostgreSQLを使用しているので、PodmanでPosgtreSQLを実行します。 コマンドプロンプトを開いて以下のコマンドを入力します。 podman image pull docker.io/postgres podman run --name postgres -e POSTGRES_PASSWORD=postgres -p 5432:5432 -d postgres ※PostgreSQLのユーザー名、パスワードとしてそれぞれpostgresを、待ち受けポートとして5432が使用されています。 ※今回起動したPostgreSQLは最後で削除します。削除しない場合はリソースを消費し続けます。 また、PostgreSQLのJDBCドライバをダウンロードして、ローカルに保存しておきます。 以下からダウンロードできる最新版を使用してください。 jdbc.postgresql.org JBoss EAPサーバーのセットアップ JBoss EAP 7.4 zip ディストリビューションをダウンロードします。ディストリビューションをローカルフォルダー(例: C:\apps\jboss\jboss-eap-7.4) に解凍します。 developers.redhat.com EAP 7.4ダウンロード コマンドプロンプトからEAP_HOME 環境変数を設定して、JBoss EAP 7.4を起動します。サーバー構成ファイルとしてはstandalone-full.xmlを指定します。 set EAP_HOME=C:\apps\jboss\jboss-eap-7.4 %EAP_HOME%\bin\standalone.bat --server-config=standalone-full.xml 十分なCPU、メモリが積まれている場合は10秒以内に起動します。 このあと、このコマンドプロンプトは開きっぱなしにしておいてください。 もう一つコマンドプロンプトを開いて、管理CLI (jboss-cli)を起動します。 set EAP_HOME=C:\apps\jboss\jboss-eap-7.4 %EAP_HOME%\bin\jboss-cli --connect 管理CLI からtopicの追加とJDBCドライバのインストールを行います。 ※<PATH_TO_JDBC_DRIVER>\postgresql-XX.X.X.jar はpostgresの設定時にダウンロードしたpostgresqlのドライバを指定してください。 jms-topic add --topic-address=orders --entries=[/topic/orders] module add --name=org.postgres --resources=<PATH_TO_JDBC_DRIVER>\postgresql-XX.X.X.jar --dependencies=javax.api,javax.transaction.api /subsystem=datasources/jdbc-driver=postgres:add(driver-module-name=org.postgres, driver-name=postgres) data-source add --name=postgresDS --jndi-name=java:jboss/datasources/CoolstoreDS \ --driver-name=postgres \ --connection-url=jdbc:postgresql://localhost:5432/postgres \ --user-name=postgres --password=postgres これでサーバーの設定が終わったので、アプリケーションのデプロイを行います。 以下のリポジトリからソースコードをダウンロードします。 github.com このリポジトリにはRed Hatがワークショップで使用するcoolstoreというアプリケーションをJBoss EAP 7.4とJBoss EAP 8.0で動作できるように変更したものが含まれています。 今回はそのうちJBoss EAP 7.4で動作するものを使用します。 コマンドプロンプトを使用して mtr-example-eap7.4to8.0\eap7.4\coolstoreに移動してMavenを使用してコンパイル、デプロイを行います。 mvn package mvn wildfly:deploy ※コンパイルに失敗する場合は、インターネットにつながっているか、OpenJDKのバージョンは17を使用しているかを確認してみてください。 ※デプロイに失敗する場合はPostgreSQLが起動しているか、topic、jdbcドライバの設定を行っているかを確認してください。 デプロイに成功すると、 http://localhost:8080/ にアクセスすることで、以下のような画面を表示することができます。 EAP7.4 coolstore これでEAP7.4上で動くアプリケーションのセットアップは終了です。 管理CLIを起動していたコマンドプロンプトを閉じます。 JBoss EAP 7.4を起動した最初のコマンドプロンプトでCTRL+Cを押してサーバーを停止します。そのあとバッチジョブを終了するか聞かれるのでYを押してください。 JBoss EAPを起動していたコマンドプロンプトとコンパイルを行ったコマンドプロンプトはこの後も使用するため、そのままにしておきます。 JBoss サーバー移行ツール(JBoss Server Migration Tool)を使ってのサーバー構成の移行 最初にJBoss EAP 8.0本体とJBoss サーバー移行ツール(JBoss Server Migration Tool)をダウンロードしてきます。 JBoss EAP 8.0.0 zip ディストリビューションをダウンロードします。ディストリビューションをローカルフォルダー(例: C:\apps\jboss\jboss-eap-8.0) に解凍します。 jboss-eap-8.0.0-server-migration zip ファイルをダウンロードします。ファイルをローカルフォルダー(例: C:\apps\jboss\jboss-eap-8.0\jboss-server-migration) に解凍します。Server Migration と書かれたリンクからダウンロードすることが可能です。 developers.redhat.com JBoss EAP 8.0ダウンロード ※注意: JBoss Server Migration Toolは以前においてはJBoss EAP本体と一緒に配布されていましたが、このバージョンから別個に配布されるようになりました。 移行を始めるために、移行前の環境と移行後の環境がわかるように環境変数を設定します。 前の手順で設定したEAP7.4のフォルダをEAP_PREVIOUS_HOMEにし、EAP_HOME にEAP8.0のフォルダを設定します。 # set EAP_HOME=C:\apps\jboss\jboss-eap-7.4 set EAP_PREVIOUS_HOME=%EAP_HOME% set EAP_HOME=C:\apps\jboss\jboss-eap-8.0 JBoss Server Migration Toolを使って先ほどstandalone-full.xmlを使って起動したJBoss EAP7.4の設定を移行します。設定した内容はstandalone-full.xmlに含まれています。これを使う事でデータベース用のドライバや、topicの設定などが移行されます。 ※JBoss Server Migration ToolはEAP 7.4にも同梱されていますが、EAP 8.0のものを使用してください。EAP 7.4に同梱されているものはEAP 8.0への移行がサポートされていません。 ※今回はJDBCドライバをそのまま移行していますが、実運用環境ではデータベースの移行を伴う場合があり、その場合はJDBCドライバのバージョンアップが必要になります。 %EAP_HOME%\jboss-server-migration\jboss-server-migration -s %EAP_PREVIOUS_HOME% -t %EAP_HOME% サーバー移行ツールは、移行プロセス中にいくつかの質問をします。 ---------------------------------------------------------- ---- JBoss Server Migration Tool ----------------------- ---------------------------------------------------------- Retrieving servers... INFO SOURCE server name: JBoss EAP, version: 7.4.0.GA. INFO TARGET server name: JBoss EAP, version: 8.0.0.GA. ---------------------------------------------------------- ---------------------------------------------------------- Server migration starting... INFO --- Migrating modules requested by environment... INFO No modules to migrate. Migrate the source's standalone server? yes/no? standalone-full.xmlはstandalone serverに含まれるので [yes] を指定します。 INFO --- Migrating standalone server... INFO Source's standalone content migrated. INFO Source's standalone configurations found: [standalone-full-ha.xml, standalone-full.xml, standalone-ha.xml, standalone-load-balancer.xml, standalone.xml] Migrate all configurations? yes/no? standalone-full.xmlのみを移行したいので [no] を指定します。 Migrate configuration standalone-full-ha.xml ? yes/no? standalone-full.xmlのみを移行したいので [no] を指定します。 Migrate configuration standalone-full.xml ? yes/no? standalone-full.xmlを移行したいので [yes] を指定します。 INFO Migrating standalone configuration standalone-full.xml... WARN Management Interface http-interface is secured by legacy security realm ManagementRealm, and its configuration will be changed to use the target's server Elytron matching functionally configured by default, which may require additional Elytron manual configuration. INFO Legacy security XML configuration retrieved. INFO Unsupported extensions removed: [org.jboss.as.jsr77, org.jboss.as.security] INFO Unsupported subsystems removed: [urn:jboss:domain:jsr77:1.0, urn:jboss:domain:security:2.0] INFO Referenced paths migrated. INFO Legacy security realms removed from XML configuration. WARN Migrated Remoting subsystem's http connector resource /subsystem/remoting/http-connector/http-remoting-connector using a legacy security-realm, to Elytron's default application SASL Authentication Factory migration-defaultApplicationSaslAuthenticationFactory. Please note that further manual Elytron configuration may be needed if the legacy security realm being used was not the source server's default Application Realm configuration! WARN Migrated Undertow subsystem https-listener resource /subsystem/undertow/server/default-server/https-listener/https using a legacy security-realm, to Elytron's default TLS ServerSSLContext migration-defaultTLSServerSSLContext. Please note that further manual Elytron configuration may be needed if the legacy security realm being used was not the source server's default Application Realm configuration! WARN Migrated Undertow subsystem http-invoker resource /subsystem/undertow/server/default-server/host/default-host/setting/http-invoker using a legacy security-realm, to Elytron's default Application HTTP AuthenticationFactory migration-defaultApplicationHttpAuthenticationFactory. Please note that further manual Elytron configuration may be needed if the legacy security realm being used was not the source server's default Application Realm configuration! INFO Legacy security realms migrated to Elytron. WARN Migrated management interface http-interface secured by a legacy security-realm, to Elytron's default Management HTTP AuthenticationFactory migration-defaultManagementHttpAuthenticationFactory. Please note that further manual Elytron configuration may be needed if the legacy security realm being used was not the source server's default Application Realm configuration! WARN Migrated ejb3 subsystem resource /subsystem/ejb3/application-security-domain/other using legacy security domain other, to Elytron's default application Security Domain. Please note that further manual Elytron configuration may be needed if the legacy security domain being used was not the source server's default Application Domain configuration! WARN Migrated undertow subsystem resource /subsystem/undertow/application-security-domain/other using legacy security domain other, to Elytron's default application Security Domain. Please note that further manual Elytron configuration may be needed if the legacy security domain being used was not the source server's default Application Domain configuration! WARN Migrated messaging-activemq subsystem server resource /subsystem/messaging-activemq/server/default, to Elytron's default application Security Domain. Please note that further manual Elytron configuration may be needed if the legacy security domain being used was not the source server's default Application Domain configuration! WARN Migrated iiop-openjdk subsystem resource using legacy security domain to Elytron defaults. Please note that further manual Elytron configuration should be needed! INFO Persistent deployments found: [ROOT.war] This tool is not able to assert if persistent deployments found are compatible with the target server, skip persistent deployments migration? yes/no? 永続化のデプロイメント(persistent deployments)の互換性が確認できないと表示されますが、今回は [no] を指定して無理やり移行します。これに関しては後程アプリケーションの修正時に解消します。 INFO Persistent deployment ROOT.war migrated. Migrate configuration standalone-ha.xml ? yes/no? standalone-full.xmlのみを移行したいので [no] を指定します。 Migrate configuration standalone-load-balancer.xml ? yes/no? standalone-full.xmlのみを移行したいので [no] を指定します。 Migrate configuration standalone.xml ? yes/no? standalone-full.xmlのみを移行したいので [no] を指定します。 Migrate the source's managed domain? yes/no? standalone-full.xmlのみを移行したいので [no] を指定します。 Server migration done. INFO ---------------------------------------------------------------------------------------------------------- Task Summary ---------------------------------------------------------------------------------------------------------- server .......................................................................................... SUCCESS standalone ..................................................................................... SUCCESS contents.standalone.migrate-content-dir ....................................................... SUCCESS contents.standalone.migrate-content(path=8a\f9d271e002eb6e298dffb3aace5d40bf8a0395\content) .. SUCCESS standalone-configurations ..................................................................... SUCCESS standalone-configuration(source=standalone-full.xml) ......................................... SUCCESS ---------------------------------------------------------------------------------------------------------- Migration Result: SUCCESS ---------------------------------------------------------------------------------------------------------- 続行するには何かキーを押してください . . . SUCCESSが表示されたら、サーバー構成の移行は完了です。JBoss EAP 8.0のサーバーを起動します。 %EAP_HOME%\bin\standalone.bat --server-config=standalone-full.xml ※EAP_HOMEにはJBoss EAP 8.0のフォルダが入っているはずです。 起動中にいくつかのスタックトレースが表示されたのが確認できると思います。 実際にアプリケーションにアクセスするとエラーになります。 現時点ではエラー ※リロードをしてもエラーにならない場合はJBoss EAP 7.4のサーバーが正しくシャットダウンされていることと、JBoss EAP 8.0のサーバーを起動していることを確認してください。 ちなみに、このサーバーマイグレーションツールでは、keycloakサブシステムの置き換えも行ってくれます。 EAP 7.4までは実はこのサーバーマイグレーションツールを使うだけで移行が完了してしまう場合もありました。しかし、EAP 7.4からEAP 8.0のアップデートではそれだけで移行できることはほぼありません。 EAP 7.4からEAP 8.0の間でパッケージ名の変更が行われています。Java EE仕様のOracleからEclipse Foundationへの移管に伴う影響です。 そのためアプリケーションの変更が必要になります。 このあとはアプリケーションの変更をしていきます。 最後にEAP 8.0 を起動しているコマンドプロンプト上でCTRL-Cを押して、サーバーをシャットダウンします。 Windupを使用しての移行箇所の特定 EAP 7.4からEAP 8.0に移行する際にアプリケーションで変更するべき点については、広く知られています。 Red Hatにおいても記事を作成しており 、どういう修正をすればいいのかを見つけ出すのは容易です。また、この後使用するEclipse Transformerは多くの修正点を自動的に直してしまうこともできます。 しかしながら、自動的には直せない修正点があったり、作業前にどれくらいの作業が必要なのかを見積もる必要がある場合があったりする場合もあります。 そのようなときにRed Hatでは Migration Toolkit for Applications を用意しています。 しかし、実は現時点ではMigration Toolkit for Applicationsは現在大きく改修中で、最新版の状況が取り込まれていません。 *1 そのため、今回はUpstreamであるWindupを使用して修正個所の洗い出しを行います。 ※単純に移行をしたいだけの場合はこの手順は必須ではありません。 WindupのCLI(windup-cli-X.X.X.Final-offline.zip)をダウンロードします。 https://windup.github.io/downloads/ Windupダウンロード zipファイルを解凍したら、binの下にある windup-cli.bat に3つの引数を付けて起動します。 --input ソースコードのおかれたフォルダ --output 修正個所の洗い出し結果を保存するフォルダ --target 移行する対象のアーキテクチャとバージョン(今回はeap:8固定) 例): windup-cli --input C:\Users\megas\git\mtr-example-eap7.4to8.0\eap7.4\coolstore --output C:\Users\megas\git\mtr-example-eap7.4to8.0\output --target eap:8 実行すると少し時間がかかった後に修正個所の一覧が出力されたURLが表示されます。 windup実行結果 URLを開くことで、修正が必要な個所の一覧が表示されます。 Windup画面 アプリケーションの名前(今回はcoolstore) → Issuesを順に選択することで修正が必要な個所の詳細を一覧表示することもできます。 修正が必要な個所の一覧 ここでは具体的にどのような修正が必要なのかの詳細については説明しません。ほとんどの修正はこの後のEclipse Transformerの実行で解決できてしまうからです。 現時点ではすべての修正個所を漏れなく検出、移行してくれるツールは存在しないので、いくつかの移行ツールを併用することをお勧めします。 その中で、Windup(もしくはMTA)は検出のみを行い、対象への変更をしないようにすることもできるため、併用がしやすくなっています。 Eclipse Transformerを使ってのユーザーアプリケーションの修正 ここからユーザーアプリケーションの修正を行っていきます。EAP 7.4からEAP8.0への移行はサードパーティの依存ライブラリを使用していない場合は一部を除いて自動的に行えるようになっています。 ここでは、Eclipse Transformerを使ってほとんどの修正を行った後に、移行できなかった場所を個別で変更していきます。 Eclipse TransformerはEAP 7.4が準拠しているJakarta EE 8からEAP 8.0が準拠しているJakarta EE 10の間に発生したパッケージ名、ネームスペースの変更に対応してくれます。すべてのケースに対応できるわけではありませんが、おおよそ変換してくれます。 1から全部を対応するよりはずっと楽になりますので、こういったツールを使うようにしてください。なお、パッケージ名の変更としてjavax->jakartaへの変更が行われたことはよく知られていますが、変更が必要ないパッケージを変更してしまう可能性があるので、手動で対応するのはお勧めできません。 JBoss EAPが準拠しているJakarta EE(Java EE)は多くのベンダー製アプリケーションサーバーが準拠しており、多くのサードパーティ製ツールが存在します。それらをうまく組み合わせていくことで、やりたいことが簡単に出来るようになります。 Eclipse Transformerをダウンロードします。現在の最新版は0.5.0です。 projects.eclipse.org Downloadsから追っていくことでダウンロードできますが、複雑なので、mavenリポジトリから直接ファイルをダウンロードします。 https://repo.maven.apache.org/maven2/org/eclipse/transformer/org.eclipse.transformer.cli/0.5.0/org.eclipse.transformer.cli-0.5.0-distribution.jar これは拡張子がjarとなっていますが、実態はzipファイルで、一度解凍する必要があります。 *2 解凍すると次のようなフォルダ構成になっています。 eclipse transformerのフォルダ構成 org.eclipse.transformer.cli-0.5.0.jarに移行するプロジェクトのフォルダと変換されたファイルの出力先を引数として渡して実行します。 java -jar <PATH_TO_CLI>\org.eclipse.transformer.cli-0.5.0.jar <INPUT> <OUTPUT> 例) java -jar C:\apps\org.eclipse.transformer.cli-0.5.0-distribution\org.eclipse.transformer.cli-0.5.0.jar C:\Users\megas\git\mtr-example-eap7.4to8.0\eap7.4\coolstore C:\Users\megas\git\mtr-example-eap7.4to8.0\output 実行すると変換が行われ、すぐに終了します。 Copyright (c) Contributors to the Eclipse Foundation org.eclipse.transformer.cli.JakartaTransformerCLI Version [ 0.5.0 ] [main] INFO Transformer - Input [ C:\Users\megas\git\mtr-example-eap7.4to8.0\eap7.4\coolstore ] [main] INFO Transformer - Output [ C:\Users\megas\git\mtr-example-eap7.4to8.0\output ] [main] INFO Transformer - Properties [ RULES_SELECTIONS ] URL [ jar:file:/C:/apps/org.eclipse.transformer.cli-0.5.0-distribution/libs/org.eclipse.transformer.jakarta-0.5.0.jar!/org/eclipse/transformer/jakarta/jakarta-selection.properties ] [main] INFO Transformer - Properties [ RULES_RENAMES ] URL [ jar:file:/C:/apps/org.eclipse.transformer.cli-0.5.0-distribution/libs/org.eclipse.transformer.jakarta-0.5.0.jar!/org/eclipse/transformer/jakarta/jakarta-renames.properties ] [main] INFO Transformer - Properties [ RULES_VERSIONS ] URL [ jar:file:/C:/apps/org.eclipse.transformer.cli-0.5.0-distribution/libs/org.eclipse.transformer.jakarta-0.5.0.jar!/org/eclipse/transformer/jakarta/jakarta-versions.properties ] [main] INFO Transformer - Properties [ RULES_BUNDLES ] URL [ jar:file:/C:/apps/org.eclipse.transformer.cli-0.5.0-distribution/libs/org.eclipse.transformer.jakarta-0.5.0.jar!/org/eclipse/transformer/jakarta/jakarta-bundles.properties ] [main] INFO Transformer - Properties [ RULES_DIRECT ] URL [ jar:file:/C:/apps/org.eclipse.transformer.cli-0.5.0-distribution/libs/org.eclipse.transformer.jakarta-0.5.0.jar!/org/eclipse/transformer/jakarta/jakarta-direct.properties ] [main] INFO Transformer - Properties [ RULES_MASTER_TEXT ] URL [ jar:file:/C:/apps/org.eclipse.transformer.cli-0.5.0-distribution/libs/org.eclipse.transformer.jakarta-0.5.0.jar!/org/eclipse/transformer/jakarta/jakarta-text-master.properties ] [main] INFO Transformer - Package renames are in use [main] INFO Transformer - Package versions will be updated [main] INFO Transformer - Bundle identities will be updated [main] INFO Transformer - Properties [ Substitutions matching [ application-client.xml ] ] URL [ jar:file:/C:/apps/org.eclipse.transformer.cli-0.5.0-distribution/libs/org.eclipse.transformer.jakarta-0.5.0.jar!/org/eclipse/transformer/jakarta/jakarta-application-xml.properties ] [main] INFO Transformer - Properties [ Substitutions matching [ application-client.xml ] ] URL [ jar:file:/C:/apps/org.eclipse.transformer.cli-0.5.0-distribution/libs/org.eclipse.transformer.jakarta-0.5.0.jar!/org/eclipse/transformer/jakarta/jakarta-direct.properties ] [main] INFO Transformer - Properties [ Substitutions matching [ application-client.xml ] ] URL [ jar:file:/C:/apps/org.eclipse.transformer.cli-0.5.0-distribution/libs/org.eclipse.transformer.jakarta-0.5.0.jar!/org/eclipse/transformer/jakarta/jakarta-renames.properties ] [main] INFO Transformer - Properties [ Substitutions matching [ persistence.xml ] ] URL [ jar:file:/C:/apps/org.eclipse.transformer.cli-0.5.0-distribution/libs/org.eclipse.transformer.jakarta-0.5.0.jar!/org/eclipse/transformer/jakarta/jakarta-persistence-xml.properties ] [main] INFO Transformer - Properties [ Substitutions matching [ persistence.xml ] ] URL [ jar:file:/C:/apps/org.eclipse.transformer.cli-0.5.0-distribution/libs/org.eclipse.transformer.jakarta-0.5.0.jar!/org/eclipse/transformer/jakarta/jakarta-direct.properties ] [main] INFO Transformer - Properties [ Substitutions matching [ persistence.xml ] ] URL [ jar:file:/C:/apps/org.eclipse.transformer.cli-0.5.0-distribution/libs/org.eclipse.transformer.jakarta-0.5.0.jar!/org/eclipse/transformer/jakarta/jakarta-renames.properties ] [main] INFO Transformer - Properties [ Substitutions matching [ web.xml ] ] URL [ jar:file:/C:/apps/org.eclipse.transformer.cli-0.5.0-distribution/libs/org.eclipse.transformer.jakarta-0.5.0.jar!/org/eclipse/transformer/jakarta/jakarta-web-xml.properties ] [main] INFO Transformer - Properties [ Substitutions matching [ web.xml ] ] URL [ jar:file:/C:/apps/org.eclipse.transformer.cli-0.5.0-distribution/libs/org.eclipse.transformer.jakarta-0.5.0.jar!/org/eclipse/transformer/jakarta/jakarta-direct.properties ] [main] INFO Transformer - Properties [ Substitutions matching [ web.xml ] ] URL [ jar:file:/C:/apps/org.eclipse.transformer.cli-0.5.0-distribution/libs/org.eclipse.transformer.jakarta-0.5.0.jar!/org/eclipse/transformer/jakarta/jakarta-renames.properties ] [main] INFO Transformer - Properties [ Substitutions matching [ beans.xml ] ] URL [ jar:file:/C:/apps/org.eclipse.transformer.cli-0.5.0-distribution/libs/org.eclipse.transformer.jakarta-0.5.0.jar!/org/eclipse/transformer/jakarta/jakarta-beans-xml.properties ] [main] INFO Transformer - Properties [ Substitutions matching [ beans.xml ] ] URL [ jar:file:/C:/apps/org.eclipse.transformer.cli-0.5.0-distribution/libs/org.eclipse.transformer.jakarta-0.5.0.jar!/org/eclipse/transformer/jakarta/jakarta-direct.properties ] [main] INFO Transformer - Properties [ Substitutions matching [ beans.xml ] ] URL [ jar:file:/C:/apps/org.eclipse.transformer.cli-0.5.0-distribution/libs/org.eclipse.transformer.jakarta-0.5.0.jar!/org/eclipse/transformer/jakarta/jakarta-renames.properties ] [main] INFO Transformer - Properties [ Substitutions matching [ *.faces-config.xml ] ] URL [ jar:file:/C:/apps/org.eclipse.transformer.cli-0.5.0-distribution/libs/org.eclipse.transformer.jakarta-0.5.0.jar!/org/eclipse/transformer/jakarta/jakarta-faces-config-xml.properties ] [main] INFO Transformer - Properties [ Substitutions matching [ *.faces-config.xml ] ] URL [ jar:file:/C:/apps/org.eclipse.transformer.cli-0.5.0-distribution/libs/org.eclipse.transformer.jakarta-0.5.0.jar!/org/eclipse/transformer/jakarta/jakarta-direct.properties ] [main] INFO Transformer - Properties [ Substitutions matching [ *.faces-config.xml ] ] URL [ jar:file:/C:/apps/org.eclipse.transformer.cli-0.5.0-distribution/libs/org.eclipse.transformer.jakarta-0.5.0.jar!/org/eclipse/transformer/jakarta/jakarta-renames.properties ] [main] INFO Transformer - Properties [ Substitutions matching [ *.taglib.xml ] ] URL [ jar:file:/C:/apps/org.eclipse.transformer.cli-0.5.0-distribution/libs/org.eclipse.transformer.jakarta-0.5.0.jar!/org/eclipse/transformer/jakarta/jakarta-renames.properties ] [main] INFO Transformer - Properties [ Substitutions matching [ application.xml ] ] URL [ jar:file:/C:/apps/org.eclipse.transformer.cli-0.5.0-distribution/libs/org.eclipse.transformer.jakarta-0.5.0.jar!/org/eclipse/transformer/jakarta/jakarta-application-xml.properties ] [main] INFO Transformer - Properties [ Substitutions matching [ application.xml ] ] URL [ jar:file:/C:/apps/org.eclipse.transformer.cli-0.5.0-distribution/libs/org.eclipse.transformer.jakarta-0.5.0.jar!/org/eclipse/transformer/jakarta/jakarta-direct.properties ] [main] INFO Transformer - Properties [ Substitutions matching [ application.xml ] ] URL [ jar:file:/C:/apps/org.eclipse.transformer.cli-0.5.0-distribution/libs/org.eclipse.transformer.jakarta-0.5.0.jar!/org/eclipse/transformer/jakarta/jakarta-renames.properties ] [main] INFO Transformer - Properties [ Substitutions matching [ *.tld ] ] URL [ jar:file:/C:/apps/org.eclipse.transformer.cli-0.5.0-distribution/libs/org.eclipse.transformer.jakarta-0.5.0.jar!/org/eclipse/transformer/jakarta/jakarta-direct.properties ] [main] INFO Transformer - Properties [ Substitutions matching [ *.tld ] ] URL [ jar:file:/C:/apps/org.eclipse.transformer.cli-0.5.0-distribution/libs/org.eclipse.transformer.jakarta-0.5.0.jar!/org/eclipse/transformer/jakarta/jakarta-renames.properties ] [main] INFO Transformer - Properties [ Substitutions matching [ ra.xml ] ] URL [ jar:file:/C:/apps/org.eclipse.transformer.cli-0.5.0-distribution/libs/org.eclipse.transformer.jakarta-0.5.0.jar!/org/eclipse/transformer/jakarta/jakarta-ra-xml.properties ] [main] INFO Transformer - Properties [ Substitutions matching [ ra.xml ] ] URL [ jar:file:/C:/apps/org.eclipse.transformer.cli-0.5.0-distribution/libs/org.eclipse.transformer.jakarta-0.5.0.jar!/org/eclipse/transformer/jakarta/jakarta-direct.properties ] [main] INFO Transformer - Properties [ Substitutions matching [ ra.xml ] ] URL [ jar:file:/C:/apps/org.eclipse.transformer.cli-0.5.0-distribution/libs/org.eclipse.transformer.jakarta-0.5.0.jar!/org/eclipse/transformer/jakarta/jakarta-renames.properties ] [main] INFO Transformer - Properties [ Substitutions matching [ *.tag ] ] URL [ jar:file:/C:/apps/org.eclipse.transformer.cli-0.5.0-distribution/libs/org.eclipse.transformer.jakarta-0.5.0.jar!/org/eclipse/transformer/jakarta/jakarta-direct.properties ] [main] INFO Transformer - Properties [ Substitutions matching [ *.tag ] ] URL [ jar:file:/C:/apps/org.eclipse.transformer.cli-0.5.0-distribution/libs/org.eclipse.transformer.jakarta-0.5.0.jar!/org/eclipse/transformer/jakarta/jakarta-renames.properties ] [main] INFO Transformer - Properties [ Substitutions matching [ faces-config.xml ] ] URL [ jar:file:/C:/apps/org.eclipse.transformer.cli-0.5.0-distribution/libs/org.eclipse.transformer.jakarta-0.5.0.jar!/org/eclipse/transformer/jakarta/jakarta-faces-config-xml.properties ] [main] INFO Transformer - Properties [ Substitutions matching [ faces-config.xml ] ] URL [ jar:file:/C:/apps/org.eclipse.transformer.cli-0.5.0-distribution/libs/org.eclipse.transformer.jakarta-0.5.0.jar!/org/eclipse/transformer/jakarta/jakarta-direct.properties ] [main] INFO Transformer - Properties [ Substitutions matching [ faces-config.xml ] ] URL [ jar:file:/C:/apps/org.eclipse.transformer.cli-0.5.0-distribution/libs/org.eclipse.transformer.jakarta-0.5.0.jar!/org/eclipse/transformer/jakarta/jakarta-renames.properties ] [main] INFO Transformer - Properties [ Substitutions matching [ ejb-jar.xml ] ] URL [ jar:file:/C:/apps/org.eclipse.transformer.cli-0.5.0-distribution/libs/org.eclipse.transformer.jakarta-0.5.0.jar!/org/eclipse/transformer/jakarta/jakarta-ejb-jar-xml.properties ] [main] INFO Transformer - Properties [ Substitutions matching [ ejb-jar.xml ] ] URL [ jar:file:/C:/apps/org.eclipse.transformer.cli-0.5.0-distribution/libs/org.eclipse.transformer.jakarta-0.5.0.jar!/org/eclipse/transformer/jakarta/jakarta-direct.properties ] [main] INFO Transformer - Properties [ Substitutions matching [ ejb-jar.xml ] ] URL [ jar:file:/C:/apps/org.eclipse.transformer.cli-0.5.0-distribution/libs/org.eclipse.transformer.jakarta-0.5.0.jar!/org/eclipse/transformer/jakarta/jakarta-renames.properties ] [main] INFO Transformer - Properties [ Substitutions matching [ web-fragment.xml ] ] URL [ jar:file:/C:/apps/org.eclipse.transformer.cli-0.5.0-distribution/libs/org.eclipse.transformer.jakarta-0.5.0.jar!/org/eclipse/transformer/jakarta/jakarta-web-xml.properties ] [main] INFO Transformer - Properties [ Substitutions matching [ web-fragment.xml ] ] URL [ jar:file:/C:/apps/org.eclipse.transformer.cli-0.5.0-distribution/libs/org.eclipse.transformer.jakarta-0.5.0.jar!/org/eclipse/transformer/jakarta/jakarta-direct.properties ] [main] INFO Transformer - Properties [ Substitutions matching [ web-fragment.xml ] ] URL [ jar:file:/C:/apps/org.eclipse.transformer.cli-0.5.0-distribution/libs/org.eclipse.transformer.jakarta-0.5.0.jar!/org/eclipse/transformer/jakarta/jakarta-renames.properties ] [main] INFO Transformer - Properties [ Substitutions matching [ permissions.xml ] ] URL [ jar:file:/C:/apps/org.eclipse.transformer.cli-0.5.0-distribution/libs/org.eclipse.transformer.jakarta-0.5.0.jar!/org/eclipse/transformer/jakarta/jakarta-permissions-xml.properties ] [main] INFO Transformer - Properties [ Substitutions matching [ permissions.xml ] ] URL [ jar:file:/C:/apps/org.eclipse.transformer.cli-0.5.0-distribution/libs/org.eclipse.transformer.jakarta-0.5.0.jar!/org/eclipse/transformer/jakarta/jakarta-direct.properties ] [main] INFO Transformer - Properties [ Substitutions matching [ permissions.xml ] ] URL [ jar:file:/C:/apps/org.eclipse.transformer.cli-0.5.0-distribution/libs/org.eclipse.transformer.jakarta-0.5.0.jar!/org/eclipse/transformer/jakarta/jakarta-renames.properties ] [main] INFO Transformer - Text files will be updated [main] INFO Transformer - Java direct string updates will be performed [main] INFO Transformer - All resources will be selected [main] INFO Transformer - Action selected for input [ C:/Users/megas/git/mtr-example-eap7.4to8.0/eap7.4/coolstore ]: Directory Action [main] INFO Transformer - Input [ C:/Users/megas/git/mtr-example-eap7.4to8.0/eap7.4/coolstore ] as [ C:\Users\megas\git\mtr-example-eap7.4to8.0\eap7.4\coolstore ] [main] INFO Transformer - Output [ C:/Users/megas/git/mtr-example-eap7.4to8.0/output ] as [ C:\Users\megas\git\mtr-example-eap7.4to8.0\output ] [main] INFO Transformer - [ All Resources ] [ 8593 ] Unaccepted [ 0 ] Accepted [ 8593 ] [main] INFO Transformer - [ All Unchanged ] [ 8497 ] Failed [ 0 ] Duplicated [ 0 ] [main] INFO Transformer - [ All Changed ] [ 96 ] Renamed [ 0 ] Content [ 96 ] [main] INFO Transformer - Transformer Return Code [ 0 ] [ Success ] すると、ほとんどの移行が終了しているので、引き続きライブラリのバージョンアップを行っていきます。 mavenの設定ファイル、pom.xmlに記載されているライブラリをJakarta EE 8以前のもの(Java EE/J2EEのもの)から、Jakarta EE 10のものに変更します。 このアプリケーションではjavax.javaee-web-apiとjavax.javaee-apiのバージョン7が定義されているので、整理して変更します。このアプリケーションでは使用していませんが、ほかにユーザーライブラリでJakarta EE 8以前のものがある場合は、すべてJakarta EE 10対応版に更新する必要があります。 <dependency> <groupId> javax </groupId> <artifactId> javaee-web-api </artifactId> <version> 7.0 </version> <scope> provided </scope> </dependency> <dependency> <groupId> javax </groupId> <artifactId> javaee-api </artifactId> <version> 7.0 </version> <scope> provided </scope> </dependency> ↓ <dependency> <groupId> jakarta.platform </groupId> <artifactId> jakarta.jakartaee-api </artifactId> <version> 10.0.0 </version> <scope> provided </scope> </dependency> 注意: 使用しているライブラリによっては、製造元が更新を止めてしまい、Jakarta EE 10対応版が存在しない場合があります。その場合は、自分たちでライブラリをフォークして、Jakarta EE 10対応版を作成する必要があります。 *3 実はEclipse Transformerはjarファイルやclassファイルのバイナリを直接変更してパッケージ名をjakartaに書き換える機能も持っていますが、完全に動作することは保証されませんし、実際によく知られたいくつかのパターンでは動作しません。 また、ライブラリによってはバージョンアップ後に使用方法が変更されており、新しいバージョンに合わせてソースコードを修正する必要がある場合もあります。 JBoss EAP 8.0では Hibernateのバージョンアップ が行われており、いくつかのデフォルト値が変更されているなどの非互換が発生しています。 今回はすでに動いているアプリケーションなので、前のバージョンに動作を合わせる設定を追加します。 persistence.xmlのpropertiesに以下の記述を追加します。 <property name = "hibernate.id.db_structure_naming_strategy" value = "single" /> さて、ここまで来たら移行は完了です。EAP 8.0を起動して、修正の終わったアプリケーションをデプロイします。 コマンドプロンプトを開いてEAP 8.0を起動します。起動中に前回のアプリケーションの起動エラーが出ますが、無視してください。 set EAP_HOME=C:\apps\jboss\jboss-eap-8.0 %EAP_HOME%\bin\standalone.bat --server-config=standalone-full.xml 起動が終わったら、修正したアプリケーションをコンパイルしてデプロイします。最初にデプロイした方法と同じです。 mvn package mvn wildfly:deploy デプロイが成功し、EAP 8.0上でアプリケーションを実行することができました。 EAP 8.0上のcoolstore まとめ JBoss EAP 7.4(Jakarta EE 8)からJBoss EAP 8.0(Jakarta EE 10)への移行を行ってきました。このバージョンではパッケージ名の変更が入っているため、いつもよりも移行が大変になっております。いくつかのライブラリはすでにメンテナンスが終了しており、移行のためには使用するライブラリを大幅に変更する必要がある場合もあります。 しかしながら、メンテナンスが終了しているライブラリを使い続けることはセキュリティー上の問題が発生する場合もあります。 少し前にはすでに開発がストップしていたlog4jの脆弱性である Log4Shell が猛威を振るいました。この機会に刷新するのはいかがでしょうか。 なお、Red Hatでは移行に関する資料を準備しております。必要に応じてそちらを参照してください。 参考資料 Product Documentation for Red Hat JBoss Enterprise Application Platform 8.0 - 特に 移行ガイド と JBoss サーバー移行ツールの使用 KCS: Jakarta EE 8 から EE 10 への Red Hat JBoss EAP アプリケーションの移行 ブログ: JBoss EAP 7.x から JBoss EAP 8-Beta への移行方法 Windup ウェブサイト Eclipse Transformer ウェブサイト 富士通ブログ: Eclipse Transformerのご紹介と、javaxパッケージ名前空間からjakartaパッケージ名前空間への変換例 今回使ったサンプルアプリのリポジトリ Tomcat Migration Tool for Jakarta EE ウェブサイト : Apache Tomcatが提供しているjavax-> jakartaのマイグレーションツール OpenRewrite ウェブサイト : Java SEのバージョンアップの変換もしてくれるツール *1 : 近日中に今はやりのアレを取り込んだバージョンが公開される予定なので、こうご期待! *2 : mavenリポジトリの本来の使い方を逸脱している気がする。 *3 : この記事を執筆した2024年4月時点ではSeaser 2やApache Velocityなど。
こんにちは、レッドハットサポートの クリス です。この記事の 英語版 があります。 前回の記事 の続きになります。今回も、システム2台があります: 測定用の制御システム 消費電力を測定しながらワークロードを実行するシスU(System Under Test, SUT) SUTはpmcdとpmda-denkiを実行し、測定データを提供する。SUTはpmcdとpmda-denkiを実行してメトリクデータを提供し、制御システムはPCPを実行してデータを収集し、後で計算するためにアーカイブファイルに保存します。Ansible playbookを使って、両方のシステムに必要なパッケージをインストールする。 このセットアップを使用して、さまざまなLinuxシステムがどれだけ速く計算タスクを実行できるか、また、どれだけエネルギー効率よくタスクを実行できるかを比較する。もしかしたら、遅いシステムの方がエネルギー効率が高いかもしれない。 今回の用語: 電力 とは、ある時点で消費されるエネルギーのことで、単位はワットまたはVAである。例えば、「このシステムは現在20ワットを消費している」ということ です。 エネルギー は「時間経過に伴う電力」であり、ある時間スで使用されるエネルギー量です。例えば、ワット時(英文:Watthour)またはジュール(Joule)で測定されます。 pmda-denkiの電力関連のメトリクスを簡単にまとめます: RAPL: x86システム上で、CPU、RAM、オンボードGPUによって消費される電力を提供します 電池: 電池の放電を監視することで消費電力を計算します スマートプラグ: ネットワーク/APIを使って、消費電力を測定する機能を持つデバイス。この記事では、Tasmotaファームウェアを搭載したSwitchBotスマートプラグを使用する。代わりに他の多くのデバイスを使用することができる。 テストに最適なワークロードを探そう この記事では、作業負荷の例を使用し、複数のシステムを比較しながら消費/エネルギー効率を測定する。例えば、SPEC ベンチマーク・スイートを実行したら、非常に時間がかかる。特に、ARM や RISC-V CPU の小型システムでも測定したいので、もっと軽い負荷を探そう。 システムに接続されたストレージへのI/Oを比較する代わりに、CPUとメモリ操作に焦点を当てる。そのために、僕は「一つの用事を何回も実行する」事に決めました。例えば、10分の間、以前圧縮したファイルを何回も解凍する。解凍の頻度やかかった電気量を測定する。この記事では、ファイルを解凍する操作: bzcat httpd-2.4.57.tar.bz2 >/dev/null それを一つか複数のスレッドで実行すると、スレッドをそれぞれのCPUコアに分散させることができる。終了してから、それぞれのコアで実行したコマンドの回数を数える。その後、10分間で消費された電力を調べ、1回の解凍でシステムが消費したエネルギーを計算することができます。圧縮されたファイルはメモリーに保存され、解凍されたデータは/dev/nullに送られ、ストレージサブシステムからの影響を減らします。 計算ジョブの基本的な流れは以下の通り: uml program flow フローは左から始まり、job_httpd_extract_cpu.sh スクリプトが実行される。スクリプトでは、関数 extract() が 3 回実行され、3つのスレッドを生成される。これらのスレッドはそれぞれ、ファイルを解凍し、終了した解凍の回数を計算する。これらのスレッドはバックグラウンドで実行され、もともとのスレッドが実行されている間、「sleep 10分間」と実行する。その後、スレッドが終了し、成功した解凍ジョブの全体数が計算されます。PCPから電力メトリクスを読んだ後、エネルギー消費と効率を計算することができます。 正当な結果を得るために、テスト・ジョブはどれくらいの実行時間すればいいのか? ファイルの解凍というワークロードを決定した。正当な結果を得るためには、消費電力を測定しながら、どれくらいの時間、連続的に解凍を行う必要があるでしょうか? 例えば、ファイルの解凍に60秒かかるのであれば、30秒だけジョブを実行しても結果はでません。しかし、連続解凍ジョブを5分間実行したとしても、その結果は複数回の実行でどの程度異なるのだろうか?意味のある結果を得るためには、2分か2時間か実行する必要があるのでしょうか? 次のグラフでは、あるジョブが例えば60秒間実行され、そのジョブは4つのスレッドを起動し、それぞれがループ内で可能な限り多くのファイル解凍を実行した。左側では、ジョブをわずか3秒間実行しただけで、複数の実行の間に大きな違いが見られます。右側の60秒の実行時間では、測定された消費量は複数の実行の間で安定している。紫色のマークはRAPL("Running Average Power Limit")の測定値で、CPUとメモリの消費量を測定している。紫色のマークは、電池のマークより安定です。このデータは、Steam Deckというシステムで測定したものです。 graph of job lenth comparison これらの結果に基づき、私は以下のテストのほとんどを10分間実行することを決めました。 smartplugのどの電力メトリクスが正しいのか? RAPLと電池は単純のメトリクスになるので、使いやすい。それに対して、タスモタ・パワープラグは複数のメトリクスが出る: $ http://192.168.0.2/cm?cmnd=Status%208 { "StatusSNS": { "Time": "2024-02-12T10:28:25", "ENERGY": { "TotalStartTime": "2023-10-27T13:19:18", "Total": 12.599, "Yesterday": 0.015, "Today": 0.006, "Power": 3, "ApparentPower": 4, "ReactivePower": 3, "Factor": 0.67, "Voltage": 101, "Current": 0.037 } } } 上記の出力されたデータで、4つのメトリクスが読める: Power (電力) ApparentPower ReactivePower 「電圧」と「電流」の乗算 最初の3つはスマートプラグから読める。4番目は電圧と電流から計算されます。どれが正しいのか?その答えを見つけるために、私はワークロードを実行して、上記の4つのメトリクスとRAPLと電池のメトリクスを比較しました。Thinkpad L480システムで測ったデータを図形にして: power metric comparison 左のグラフでは、システムが使っている消費電力を示しています。それぞれのメトリクスは違うデータを流出するのは見えます。左のグラフでは、スレッドが「0」の場合、システムはアイドル状態ですが、電力を使っています。3スレッドの場合、システムの消費電力は2スレッドの場合よりもわずかに少なくなっています。グラフには示していないが、CPUのクロック速度を並行して見ると、負荷が1つまたは2つのコアの場合、Intel Turbo BoostでCPUのクロックは3.4Ghzであることがわかる。3スレッド以上の場合、CPUのクロックは3.2Ghzまたは3Ghzとなり、消費電力も若干少なくなります。代替、電池のメトリクスは一番安定だ。 右のグラフは電池を「0」にして、他のメトリクスとの違えを示しています。ここでは、Tasmotaスマートプラグからの「Power」メトリクスが、電池メトリクスに最も類似したメトリクスであることが分かる。 最もエネルギー効率の高いシステムは? 様々なアーキテクチャでジョブを実行してみましょう! energy consumption per job このグラフは、各システムが1回の解凍ジョブで使っているエネルギー消費を示している。システムの詳細: Thinkpad L480 、アーキテクチャx86_64、2018年から販売するモデル。第8世代インテルi5-8250U CPU(14nm)、ハイパースレッディングなしの4コア。このシステムでは、消費電力を測定するための3つのメトリクスすべてが使用可能である。 Macbook pro Asahi Fedora remix 、10コアのAppleSilicon M2 CPU(5nm)、aarch64、2023年のモデル。コア数が多いため、最大10スレッドを別々のコアで実行できる。 Steam Deck 、これは4コア/8スレッドのAMD製CPU(7nm)、2022年にリリースした。 Raspberry Pi 4 、4コア(16nm)のaarch64システム、2019年発売 Star 64 、4コアのRISC-Vシステム、2023年発売 すべてのシステムには複数のコアがある。すべてのシステムにおいて、使用するコアが増えると、ジョブあたりの消費量は減少する。負荷が3コア以上になると、Raspi4は調査したインテル・システムよりもジョブあたりの効率が高くなる。LinuxカーネルのApple Siliconのサポートが比較的新しいのに、今回の解凍ジョブでは、Macbookのエネルギー効率は最高でした。カーネルのStar64/RISC-Vのサポートもかなりに新しい。 総消費電力 SUTの総消費電力の比較: total power draw 左側は、実行のジョブが0の状態で、システムがアイドルの状態である。Macbookはアイドリング状態ではそれほど消費しておらず、負荷が入ると54Wになる。 次のグラフは、各システムが実行した1秒あたりの解凍ジョブの総数: graph with total completed extract jobs per second ほとんどのシステムは4コアなので、スレッド数を4まで増やすと解凍ジョブ数が増える。Macbookは10コアなので、10並列スレッドまで解凍ジョブ数が増えていることが分かります。Raspi4とStar64の1秒あたりの解凍ジョブ数はほぼ同じなので、両者の図形はほぼ重なっている。 まとめ ある計算ジョブに対するさまざまなシステムのエネルギー効率を比較した。他の作業負荷を使うと、結果も異なる可能性が高い。ここで使用した方法により、いくつかの他の調査もできる。例えば: 顧客のアプリケーション/ワークロードにとって最もエネルギー効率の高いシステムは? 「powertop」を使うと、システムのエネルギー効率にどのような影響を与えますか? Spectre/Meltdownのような回避策がエネルギー効率に与える影響は? ハイパースレッディングは、ワークロードの性能とエネルギー効率にどのような影響を与えているか? テストスクリプトを編集したら、CPUコアのクロック速度を追加記録することもできる。このようなプロジェクトを立ち上げ、より多くのスクリプトと計算ジョブを収集し、さまざまなシステムから測定結果を収集することも検討する。 PCPやpmda-denkiはRHELの一部であり、RHEL製品のサポート範囲内である。今回の測定データやスクリプトは ここのリポジトリ のdenki-jobrunnerディレクトリにあります。そのスクリプトはレッドハットサポートの範囲に入っています。グラフの元となった生データもレポにあります。 この記事をレビューし、協力してくれたレッドハットのTAMやスペシャリストの仲間に感謝している。 巨人の肩の上に立っています。
Red Hat の古市です。 今後主力製品としてサポートされる Red Hat Build of Apache Camel 4.x 向け開発ツールを、こちらのブログでご紹介しています。ツールの概要と簡単な使い方、最後にフィードバック方法について順に記載していますので、ぜひご覧ください。 https://playintegration.blogspot.com/2024/04/red-hat-buld-of-apache-camel-4-vscode.html
森若です。 Red Hat Enterprise Linux 8(RHEL 8)は、セキュリティを強化するために多くの新機能を導入しましたが、その中でも特に運用の手間削減、セキュリティの一貫性向上、従来からの手順変更という点で運用管理への影響が大きいものがcrypto-policies(システム全体の暗号化ポリシー)です。この記事では、crypto-policiesがどのように拡張されてきたかを紹介します。 Crypto-Policiesとは? 背景としては、システムに含まれる多数のアプリケーション全体について暗号化などのレベルを揃えることが重要です。一部のサービスだけが強い暗号化を使っていても他のサービスが脆弱な暗号化を使っていると、システム全体としては脆弱です。 一言で「揃える」と言っても、実際にシステムに多数存在する暗号化ライブラリやアプリケーションの設定を実施するには必要な知識が非常に幅広く難しいです。crypto-policiesはこの困難な作業を容易にするために開発されました。 Crypto-policiesは、システム全体の暗号化ポリシーを一元管理するためのフレームワークです。管理者は複数のアプリケーションやライブラリにわたって一貫した暗号化設定を簡単に適用できます。 ソフトウェアの設定方法が多数のソフトウェアで従来とは変わりますから、構築手順や運用管理への影響が大きくなります。 Crypto-Policiesで設定を管理されるソフトウェアは何? Crypto-policiesは、RHELに同梱されるOpenSSL、GnuTLS、OpenSSH、OpenJDKなど、多くのセキュリティライブラリやツールの設定を管理します。これにより、システム全体でセキュリティ基準を統一することが可能になります。crypto-policiesパッケージで設定を生成し、暗号化機能を持つ他のパッケージではcrypto-policiesの設定を読みこむ実装がおこなわれています。 管理対象とならないソフトウェアとしては、RHEL同梱以外のサードパーティソフトウェアのうち、RHEL同梱の暗号化ライブラリを使わず独自にもつものや、独自に明示的な設定を行うもの、Go言語で記述されたプログラムのほとんどです。(RHEL同梱のPodmanではcrypto-policiesの設定を反映するopensslを利用する修正がおこなわれています。) RHEL 8.0でのCrypto-Policies RHEL 8.0がリリースされた当初、crypto-policiesはDEFAULT、LEGACY、FUTURE、およびFIPSの4種類の事前定義されたポリシーから選択することしかできませんでした。これらのポリシーはカスタマイズすることができません。 以下のようなコマンドでポリシーを設定します。 # update-crypto-policies --set DEFAULT RHEL 8.2でのカスタマイズ導入 RHEL 8.2から、ポリシー言語を使用してcrypto-policiesをのポリシーをユーザが定義する機能が導入されました。以下の例のような簡単な記述で、管理者はポリシーを自作したり、特定の要件に合わせてポリシーを細かく調整できるようになりました。ポリシー言語は「オプション = 暗号化スイート名(オプションによっては複数)」「オプション = 値」のような記述を列挙するものです。 典型的にはポリシーをゼロから新規に書かず、もとのポリシーからの変更部分をサブポリシーとして定義して、既存のポリシーとくみあわせて利用します。サブポリシーでは通常のポリシーの記述のほかに、もとのポリシー差分を定義して、「オプション = +値」「オプション = -値」のような記述で一部の値を追加・削除もできます。サブポリシーは任意の数だけ指定できます。 たとえば NO-SHA1.pmod でSHA-1の利用を禁止するサブポリシーを記述します。この例ではハッシュ関数、署名、証明書でのSHA-1の利用を禁止しています。ポリシー言語の詳細は man crypto-policies の CRYPTO POLICY DEFINITION FORMAT に説明があります。 hash = -SHA1 sign = -*-SHA1 sha1_in_certs = 0 以下のようにくみあわせて、基本はDEFAULTポリシーですが、SHA-1の利用を禁止するポリシーを指定できます。 # update-crypto-policies --set DEFAULT:NO-SHA1 RHEL 8.5でのスコープ指定導入 RHEL 8.4まではシステム全体で統一されたポリシーしか扱えませんでした。本来の目的からすると妥当なのですが、問題となるケースが存在しました。「古いシステムから接続をうけつけるためsshだけ脆弱な暗号化を使いたい」「古いActive Directoryを利用するためKerberosだけ脆弱なハッシュ関数を使いたい」などのケースです。 RHEL 8.5からは、 ポリシー言語の中で cipher@ssh のように特定のスコープを指定して、一部のプログラムにだけ影響するポリシーを設定できるようになりました。これにより、たとえばSSH接続に対する暗号化要件だけを他のサービスとは独立して管理できるようになります。 他の例で AD-SUPPORT.pmod では、古い設定のActive Directoryとの互換性のため RC4などをKerberosだけで許可するため以下のようなサブポリシーを定義します。 cipher@kerberos = RC4-128+ hash@kerberos = MD5+ RHEL 8 と RHEL 9 でのssh サーバ実装の違い すこし話が変わりますが、RHEL 8とRHEL 9では実装の違いがあります。ポリシー言語はそのままなのですが、sshサーバへ設定を反映させる方法が変わっています。 RHEL 8, 9 のどちらも問題なく動作するのですが、インプレースアップグレードを行うと問題が発生する場合があります。 RHEL 8の時にcrypto-policiesの設定を無視する設定をおこなった状態で、RHEL 9に更新すると、(8の時のまま動作してほしい期待に反して)crypto-policiesの設定に従った設定が利用されるので、意図しない動作の違いが発生します。インプレースアップグレードでなくても、設定の使いまわしができませんので注意が必要です。 RHEL 8とRHEL 9の実装の違いはこのようになっています。 RHEL 8では、sshd起動時のコマンドラインオプションとして crypto-policies が生成したポリシーを指定することで設定をおこなっていました。 RHEL 9では、設定のincludeにより、crypto-policiesが生成したポリシーを設定ファイルの一部として読み込み、コマンドラインの指定は行いません。 利用上の注意 /etc/crypto-policies/back-ends の直接編集はNG - このディレクトリ内のファイル update-crypto-policies コマンドの実行やパッケージの更新で上書きされます。特にデフォルトだとシンボリックリンクで /usr/share/crypto-policies/ 以下のファイルを指しており、管理者が直接編集することは想定されていません。間違えて編集してしまった場合は、 yum reinstall crypto-policies としてパッケージを再インストールすることで元にもどせます。 最新バージョンの利用を推奨 Crypto-policiesは、ここで紹介した以外にもポリシー言語の拡張など、RHELの各マイナーリリースのタイミングで拡張が行われています。常に最新バージョンの利用を推奨します。 変更内容についてまとまったドキュメントは存在しませんが、 rpm -q --changelog crypto-policies としてパッケージの更新履歴を確認できます。 参考 第3章 システム全体の暗号化ポリシーの使用 Red Hat Enterprise Linux 8 redhat-crypto / fedora-crypto-policies · GitLab
OpenShiftの再現環境作成のコツ - 第3回 ランタイムイメージの利用 こんにちは、Red Hat Partner Technical Account Managerのイアンです。 シリーズの前回 では、ローカルのソースコードをビルドし、OpenShiftにデプロイする方法を紹介しました。 しかし、ビルダーイメージをそのまま使っていましたので、ビルド用のツール(Maven、s2i)が残って、イメージのサイズが大きくなりました。 当記事では、ビルダーイメージとランタイムイメージの両方を使って、アプリのビルドとデプロイを行います。 当シリーズ OpenShiftの再現環境作成のコツ - 第1回 Quickstarts紹介 OpenShiftの再現環境作成のコツ - 第2回 一からビルド OpenShiftの再現環境作成のコツ - 第3回 ランタイムイメージの利用 目次 当シリーズ 目次 事前準備 ビルドの流れ デプロイの流れ ImageStreamの作成 BuildConfigの作成 中間イメージ用 Runtimeイメージ用 ビルド開始 DeploymentConfig作成 デプロイの確認 Pod上の稼働確認 イメージサイズの確認 再デプロイ まとめ 事前準備 前回 の記事を参照して、 helloworld-rs アプリを用意してください。 また、前回の sandbox のプロジェクトがあれば、一旦削除してから再作成してください。 当記事では同じプロジェクト名を使います。 ビルドの流れ 今までのビルドの流れは次です: JBoss EAP Builder Image の中にアプリのビルドを行い、eap-appという ImageStream にプッシュしました。 これからは、 JBoss EAP Runtime Image を加えて、ビルドの流れを次のように変更します。 eap-app のイメージを中間イメージとして使って、ランタイムイメージを使っているもう一つのビルドを実行し、最終イメージを作ります。 デプロイの流れ 大まかな流れは次です: ImageStreamと2つのBuildConfigを作成する ビルドを行う DeploymentConfigを作成する ServiceとRouteを作成し、アプリを公開する ImageStreamの作成 中間イメージを管理するため、2つの ImageStream を作ります。 $ oc create imagestream eap-app $ oc create imagestream eap-app-build-artifacts 前回のように、分かりやすいラベルも付けます。 $ oc label is/eap-app application=eap-app $ oc label is/eap-app-build-artifacts application=eap-app BuildConfigの作成 中間イメージ用 中間イメージの BuildConfig は前回とほぼ変わっていませんが、保存先を eap-app-build-artifacts:latest の ImageSteam に変更しました。 YAMLを buildconfig-artifacts.yaml に保存し、 oc create -f buildconfig-artifacts.yaml コマンドで BuildConfig を作成します。 kind : BuildConfig apiVersion : build.openshift.io/v1 metadata : name : eap-app-artifacts labels : application : eap-app spec : runPolicy : Serial source : type : Binary strategy : sourceStrategy : env : - name : GALLEON_PROVISION_LAYERS value : jaxrs-server from : kind : ImageStreamTag name : jboss-eap74-openjdk11-openshift:latest namespace : sandbox type : Source output : to : kind : ImageStreamTag name : eap-app-build-artifacts:latest Runtimeイメージ用 次は、ランタイムイメージ用の BuildConfig を作ります。 内容は eap74-basic-s2i のテンプレートを参照して作りました。 YAMLを buildconfig-runtime.yaml に保存し、 oc create -f buildconfig-runtime.yaml コマンドで BuildConfig を作成します。 kind : BuildConfig apiVersion : build.openshift.io/v1 metadata : name : eap-app labels : application : eap-app spec : runPolicy : Serial source : dockerfile : | FROM jboss-eap74-openjdk11-runtime-openshift:latest COPY /server /opt/eap USER root RUN chown -R jboss:root /opt/eap && chmod -R ug+rwX /opt/eap USER jboss CMD /opt/eap/bin/openshift-launch.sh images : - from : kind : ImageStreamTag name : eap-app-build-artifacts:latest paths : - destinationDir : . sourcePath : /s2i-output/server type : Dockerfile strategy : dockerStrategy : from : kind : ImageStreamTag name : jboss-eap74-openjdk11-runtime-openshift:latest namespace : sandbox type : Docker output : to : kind : ImageStreamTag name : eap-app:latest triggers : - imageChange : from : kind : ImageStreamTag name : eap-app-build-artifacts:latest type : ImageChange - type : ConfigChange この BuildConfig は中間イメージの eap-app-build-artifacts:latest の ImageStream が更新されたら、自動的にビルドを開始するためのTriggerを使っています。 ビルド開始 helloworld-rs ディレクトリに移動し、次の oc start-build... を実行します。 中間イメージのビルド名 eap-app-build-artifacts を使っていることにご注意ください。 $ oc start-build eap-app-build-artifacts --from-dir=. -F Uploading directory "." as binary input for the build ... Uploading finished build.build.openshift.io/eap-app-1 started Receiving source from STDIN as archive ... ... Writing manifest to image destination Storing signatures Successfully pushed image-registry.openshift-image-registry.svc:5000/sandbox/eap-app-build-artifacts@sha256:c2886f188bf30e9909ad7b2055640050f0e7ed42aa44a467fcdec9435131de51 Push successful eap-app-build-artifacts のビルドが終わると、 eap-app のビルドが動きます。 oc status コマンドで状況を確認できます。 DeploymentConfig作成 ランタイムイメージを使って、イメージを作成しました。 次はそのイメージを使ってアプリをデプロイします。 次のYAMLを deploymentconfig.yaml に保存し、 oc create -f deploymentconfig.yaml コマンドで DeploymentConfig を作成します。 kind : DeploymentConfig apiVersion : apps.openshift.io/v1 metadata : name : eap-app labels : application : eap-app spec : replicas : 1 selector : deploymentconfig : eap-app template : metadata : labels : application : eap-app deploymentconfig : eap-app spec : containers : - env : - name : ENABLE_ACCESS_LOG value : "true" image : sandbox/eap-app imagePullPolicy : Always name : eap-app ports : - containerPort : 8080 protocol : TCP resources : {} restartPolicy : Always securityContext : {} terminationGracePeriodSeconds : 30 triggers : - type : ConfigChange - imageChangeParams : automatic : true containerNames : - eap-app from : kind : ImageStreamTag name : eap-app:latest namespace : sandbox type : ImageChange デプロイの確認 DeploymentConfig が作成されたら、すぐアプリのデプロイが開始され、Podが起動されます。 $ oc get pods -l deploymentconfig=eap-app NAME READY STATUS RESTARTS AGE eap-app-1-f5v29 1/1 Running 1 50s Pod上の稼働確認 第2回 には、 Service と Route の作成とアプリの公開方法を説明しましたので、今回は割愛します。 Podを起動しましたので、Podに入り、EAPアプリにアクセスしてみます。 $ oc rsh eap-app-1-f5v29 sh-4.4$ curl -w "\n" http://localhost:8080/rest/json {"result":"Hello World!"} sh-4.4$ exit $ イメージサイズの確認 前回 の最後の方に、ビルダーイメージのイメージサイズが 680MB ほどだったことを伝えました 1 。 今回作ったランタイムイメージのサイズは 440MB ほどになりました 2 。 200MBぐらい小さくなりました。 再デプロイ アプリを再デプロイしたい場合は、 eap-app-build-artifacts のビルドの start-build をします。 eap-app ビルドと DeploymentConfig にイメージ変更時の Trigger を設定したため、自動的に再ビルドとデプロイが走ります。 まとめ ビルダーイメージとランタイムイメージを合わせて、お客様が実際にご利用する s2i の仕組みに近づくことができました。 このシリーズで紹介したやり方を用いて、OpenShiftにサンプルアプリを簡単にデプロイすることができました。トラブルシュートするときは役に立てば幸いです。 skopeo を使ってビルダーイメージの中の各レイヤーのサイズを合計します。 skopeo inspect --tls-verify=false docker://default-route-openshift-image-registry.apps-crc.testing/sandbox/eap-app-build-artifacts | jq '[.LayersData[]|.Size]|add' 683121920 ↩ skopeo を使ってランタイムイメージの中の各レイヤーのサイズを合計します。 skopeo inspect --tls-verify=false docker://default-route-openshift-image-registry.apps-crc.testing/sandbox/eap-app | jq '[.LayersData[]|.Size]|add' 446811269 ↩