新しい Red Hat Universal Base Images の OpenJDK ランタイムイメージで、無駄のない Java コンテナを構築

Red Hat で Solution Architect として OpenJDK を担当している伊藤ちひろ(@chiroito)です。

この記事は、Red Hat Developerのブログ記事、Build lean Java containers with the new Red Hat Universal Base Images OpenJDK runtime images | Red Hat Developer の翻訳記事です。


https://developers.redhat.com/sites/default/files/styles/article_feature/public/ST-java1_1x%20%281%29.png?itok=HSEPV0HL

Red Hat Universal Base Images (UBI) には、OpenJDK の完全な Red Hat ビルドが含まれています。Universal Base Images は UBI のエンドユーザライセンス契約の下で誰でも利用でき、Red Hat の顧客には完全にサポートされています。これらの「ビルダ」イメージは、特に Red Hat OpenShift 環境で使用する場合に、広範な Java ベースのアプリケーションの構築と実行に適しているように設計されています。これらのイメージには、開発ツール、Java コンパイラ、Maven、および関連するビルドツールを含む完全な Java Development Kit(JDK)が含まれています。

OpenShiftのS2I(source-to-image)プロセスでは、アプリケーションのソースコードをOpenShiftクラスタ内で簡単に構築・更新できます。また、基盤となるイメージやアプリケーションのソースが更新されるたびに、デプロイメントが更新されます。このワークフローでは、アプリケーションはビルダイメージの上に重ねられているため、デプロイメントには完全なJDKとMavenツールが含まれています。開発者の中には、よりスリムなベースイメージに基づいてデプロイメントを行いたいと考える人もいます。その場合、Maven を使用しないか、JDK ツールを完全に使用しないか、またはその両方を使用します。

このニーズに対応するため、Red Hat は新しい OpenJDK ランタイム・コンテナ・イメージをリリースしました。 このイメージには、完全なJDKやその他のビルドツールは含まれていません。この記事では、S2Iワークフローを使用してアプリケーションを自動的にデプロイするために、OpenShift環境内でこれらの新しいイメージを使用する方法を説明します。ここでは、Quarkusのクイックスタートをアプリケーションソースとして使用します。

S2IとQuarkusのクイックスタートでビルダイメージを使う

Quarkusのクイックスタートを開始するには、OpenShiftインスタンスにログインしていることを確認し、以下のコマンドを実行します。

# OpenShiftでのイメージ構築
$ oc new-app  --context-dir=getting-started --name=quarkus-quickstart \
 'registry.access.redhat.com/ubi8/openjdk-11~https://github.com/quarkusio/quarkus-quickstarts.git#1.13.3.Final'

# ビルドの完成を見守る
$ oc logs -f bc/quarkus-quickstart

# ルートを作成
$ oc expose svc/quarkus-quickstart

# ルートのURIを取得
$ export URI="http://$(oc get route | grep quarkus-quickstart | awk '{print $2}')"

# アプリをテスト!
$ curl $URI/hello/greeting/quarkus

最初のコマンドである oc new-app は、クイックスタートアプリケーションの構築とデプロイに必要なすべての OpenShift コンポーネントを作成します。すなわち、BuildConfigubi8/openjdk-11 ベースイメージと結果のアプリケーションイメージの両方のImageStreamsDeploymentConfigServiceです。また,このコマンドはビルドとデプロイメントのインスタンスを作成します。oc exposeコマンドは、デプロイメントへのRouteを作成し、実行中のWebアプリケーションをテストできます。

この設定の優れた点は、アプリケーションソースがGitリポジトリで更新されたときや、基盤となるビルダイメージが更新されたときに、新しいビルドを起動できることです。

ランタイムイメージ

さて、新しい部分です。ここでは、無駄のないランタイムイメージのために、新しい ImageStream を定義します。このイメージの一部として、前のステップで作成されたアプリケーションの ImageStream からアプリケーションの成果物をコピーします。また、初期のクイックスタートで自動的に行われたように、アプリケーションを稼働させるための他のOpenShiftの部品を定義する必要があります。それは"DeploymentConfig"、"Service"、"Route "の3つです。図1は私たちのプロセスを示しています。

https://developers.redhat.com/sites/default/files/styles/article_floated/public/blog/2021/01/moo.png?itok=5YC-PsYx

DIYランタイムイメージを生成するためのOpenShiftのプロセス

まず、無駄のないベースイメージが必要です。ここではRed Hat UBI8 minimal base imageを使用します。これを ImageStream として OpenShift にインポートする必要があります。それは以下のコマンドで簡単にできます。

$ oc create -n openshift -f https://raw.githubusercontent.com/jboss-container-images/openjdk/release/templates/runtime-image-streams.json

ここからは、無駄のないアプリケーション用のOpenShiftコンポーネントを定義していきます。まず、無駄のないランタイムアプリケーションのイメージを公開するためのImageStreamを作成します。

$ oc create imagestream quarkus-quickstart-runtime

次に、イメージを構築するための BuildConfig が必要です。これはより複雑なので、コマンドラインではなくYAMLファイルで定義します。この記事の次のすべてのYAMLの断片については、その断片をファイルに保存して、OpenShiftに次のように読み込みます。

$ oc create -f snippet.yaml

BuildConfigは以下の通りです。

apiVersion: v1
kind: BuildConfig
metadata:
  name: quarkus-quickstart-runtime
spec:
  output:
    to:
      kind: ImageStreamTag
      name: quarkus-quickstart-runtime:latest
  source:
    images:
    - from:
        kind: ImageStreamTag
        name: quarkus-quickstart:latest
      paths:
      - sourcePath: /deployments
        destinationDir: ./deployments
    dockerfile: |-
      FROM -
      COPY deployments /
      CMD  java -jar /deployments/quarkus-run.jar
  strategy:
    type: Docker
    dockerStrategy:
      from:
        kind: ImageStreamTag
        name: ubi8-openjdk-11-runtime:latest
  triggers:
  - type: ConfigChange
  - type: ImageChange
    imageChange:
      automatic: true
      from:
        kind: ImageStreamTag
        name: quarkus-quickstart:latest
  - type: ImageChange
    imageChange:
      automatic: true
      from:
        kind: ImageStreamTag
        name: ubi8-openjdk-11-runtime:latest

Note: イメージのDockerfileはYAMLの中にインラインで記述されています。ここでは"-"を代理の FROM イメージとして使用しています。YAMLのdockerStrategyセクションは、FROM行をubi8-openjdk-11-runtimeイメージストリームの最新イメージを表す値でオーバーライドするようOpenShiftに指示します。

Quarkus のクイックスタートイメージから /deployments ディレクトリを新しい無駄のないイメージにコピーします。Quarkusのクイックスタートアプリケーション全体がこのディレクトリに存在することがわかっているので、コピーする必要があるのはこれだけです。特定のアプリケーションに応じて、さらなるステップが必要になるかもしれません。 最後に、無駄のないコンテナのCMDjavaを直接呼び出すように設定します。そして、これはアプリケーションのJARをコマンドラインで渡しています。

この構成では、基盤となる quarkus-quickstart イメージ、またはベースの ubi8-openjdk-11-runtime イメージに変更があった場合は、このイメージの再構築が行われます。

この断片を一時ファイルに保存し、前述のようにOpenShiftに読み込みます。これで、OpenShift はイメージの構築をスケジュールして開始するはずです。先ほどと同様に、ログを見て進行状況を確認できます。

$ oc logs -f bc/quarkus-quickstart-runtime

イメージサイズ

ビルドが完了したら、結果を確認してそのサイズをチェックできます。まず、ビルダイメージの上に重ねられた、通常のクイックスタート ImageStream のサイズを取得します。

$ oc get istag quarkus-quickstart:latest -o json | jq .image.dockerImageMetadata.Size
308976772
=> 295 MiB

比較のために、新しいランタイムイメージを入手してください:

$ oc get istag quarkus-quickstart-runtime:latest -o json | jq .image.dockerImageMetadata.Size
135747392
=> 129 MiB

この新しいランタイム・イメージは、UBI8 OpenJDKの完全なビルダイメージの半分以下のサイズです。完全なビルダイメージには、完全なOpenJDKとMavenが含まれています。

最後の仕上げ

次に必要なのは、DeploymentConfigServiceRouteの定義です。これらについては特に説明することがないので、1つのYAMLスニペットにまとめました。これを保存しておけば、先ほどのように oc create -f で一度に読み込めます。

apiVersion: v1
kind: DeploymentConfig
metadata:
    name: quarkus-quickstart-runtime
spec:
  replicas: 1
  selector:
    app: quarkus-quickstart-runtime
  template:
    metadata:
      labels:
        app: quarkus-quickstart-runtime
    spec:
      containers:
      - image: ' '
        name: quarkus-quickstart-runtime
        ports:
        - containerPort: 8080
          protocol: TCP
  triggers:
  - type: ConfigChange
  - imageChangeParams:
      automatic: true
      containerNames:
      - quarkus-quickstart-runtime
      from:
        kind: ImageStreamTag
        name: quarkus-quickstart-runtime:latest
    type: ImageChange
---
apiVersion: v1
kind: Service
metadata:
  name: quarkus-quickstart-runtime
spec:
  selector:
    app: quarkus-quickstart-runtime
  ports:
  - port: 8080
    protocol: TCP
    targetPort: 8080
---
apiVersion: v1
kind: Route
metadata:
  name: quarkus-quickstart-runtime
spec:
  to:
    kind: Service
    name: quarkus-quickstart-runtime

これらが読み込まれたら、同じ手法でルートを取得し、「Hello world」のURIを取得して、すべてが順調に進んでいることを確認します。

$ export URI="http://$(oc get route | awk '/quarkus-quickstart-runtime/ {print $2}')"
curl $URI/hello/greeting/quarkus

まとめ

Universal Base ImagesのOpenJDK ビルダイメージは、OpenShift上でソフトウェアを構築してデプロイしたいと考えている様々なお客様のニーズに応えるために設計されました。しかし、このイメージの柔軟性は、イメージのサイズを犠牲にしています。開発者の中には、より小さなイメージや、ビルドツールのないイメージを望む人もいます。Red Hat は、このニーズに対応するために、新しい UBI OpenJDK ランタイムイメージを導入しました。この記事で紹介する方法では、新しいランタイムイメージに基づいてOpenShift内でDIY(do-it-yourself)アプリケーションイメージをカスタマイズして構築できます。また、この方法では、構成部品が変更されたときにイメージが再構築されるようになっています。

今後の課題

新しいランタイムイメージの主な目的は、可能な限り構成要素を少なくすることでした。既存のビルダイメージには、ランタイムイメージにはない多くの機能が含まれています。 その既存のビルダイメージは、機能的なランスクリプト(ただし、これに限定されません)、 メトリクス収集のための Prometheus エージェント、OpenShift の準備のためのプローブ、デフォルトのJVMおよびガベージコレクションのパラメータ、イメージを調整するための豊富な環境変数などを含んでいます。これらの機能を必要とする場合は、完全なビルダイメージが適しているかもしれません。逆に、できるだけ小さなランタイムイメージで、可動部が少なく、多少のDIYは気にしないという方には、ランタイムイメージの方が適しているかもしれません。

Red Hatでは、最新の技術を活用するためにコンテナを継続的に進化させています。多くの開発者にとって、イメージのサイズとデプロイに必要なサイズが重要な問題であることは承知しています。私たちは、リリースごとにOpenJDKビルダとランタイムイメージの両方のサイズを小さくするために継続的に取り組んでいます。現在、いくつかの有望な開発を進めています。どうぞご期待ください。

* 各記事は著者の見解によるものでありその所属組織を代表する公式なものではありません。その内容については非公式見解を含みます。