Galleonを使ってWildFlyのイメージサイズを小さくする
GalleonはWildFly16から対応した、WildFlyのバイナリを作るためのプロビジョニングツールです。詳細についてはWildFly News - WildFly 16 and Galleon, towards a cloud native EE application serverにまとめられていますが、Galleonにより以下が実現できます。
- JAX-RS/CDI/JPAなど、よく使うAPIだけ含んだサイズの小さなWildFlyサーバを作る
- WildFlyから使わない機能を削ってDockerイメージサイズを小さくする
- Galleonで作った小さなWildFlyは従来のWildFlyと同じようにAPサーバとしての使い勝手で使える。ThorntailのようなUber-jarではない。
Galleonを使う
実際に使ってみる方がイメージが湧きやすいので、使い方から紹介します。
GalleonはWildFlyコミュニティで開発されているツールですが、WildFlyにはバンドルされておらず、GitHubのGalloenのリポジトリからダウンロードします。このブログを書いた時点の最新バージョンは4.0.3.Finalです。
ダウンロードしたツールを任意のディレクトリに展開します
$ mkdir work $ cp ~/Downloads/galleon-4.0.3.Final.zip . $ unzip galleon-4.0.3.Final.zip Archive: galleon-4.0.3.Final.zip creating: galleon-4.0.3.Final/ creating: galleon-4.0.3.Final/bin/ inflating: galleon-4.0.3.Final/LICENSE inflating: galleon-4.0.3.Final/bin/galleon.sh inflating: galleon-4.0.3.Final/bin/galleon-cli-logging.properties inflating: galleon-4.0.3.Final/bin/galleon-cli.jar inflating: galleon-4.0.3.Final/bin/galleon.bat
まずは早速GalleonでWildFlyサーバを作成してみます。対象のWildFlyのバージョンはこの記事を書いている時点で最新の17.0.1.Final、--layersにはサーバに含める機能を指定するオプションでcloud-profile
とはJAX-RS/CDI/JPA/JTAなどのよく使われるAPI実装に加えて、WildFly自体の動作に必要なサーバのコア機能のみ持つことを示します。--dirは作成したWildFlyサーバの出力先です。
Maven経由で必要なサブシステムをダウンロードしてWildFlyを組み立てるため、初回実行時は数分かかる場合もあります。
$ cd galleon-4.0.3.Final $ bin/galleon.sh install wildfly:current#17.0.1.Final --layers=cloud-profile --dir=cloud-profile Feature-packs resolved. Feature-packs resolved. Packages installed. JBoss modules installed. Configurations generated. Feature pack installed. ======= ============ ============== Product Build Update Channel ======= ============ ============== wildfly 17.0.1.Final current
Galleonによって生成された機能がそぎ落とされたWildFlyサーバの構成を確認すると一見して同じようなディレクトリ構造に見えます。GalleonはThorntailのように、Uber-jarに必要な機能をまとめるのではなく、通常のWildFlyと同じ構成を保ったまま、サブシステムを削ることで小サイズ化を実現しています。
$ cd cloud-profile $ ls LICENSE.txt bin jboss-modules.jar standalone README.txt copyright.txt modules
$WILDFLY_HOME/bin の中身を見ると、WildFlyを使ったことがある人なら違和感を感じると思います。jboss-cli.sh、add-user.shや、LinuxのOS起動時にWildFlyを起動させるinit.dスクリプト、jboss-client.jarが含まれていません。
これらの$WILDFLY_HOME/binに含まれていたスクリプトを含んだWildFlyサーバを生成したい場合は、--layersにcore-tools
を追加します。
$ bin/galleon.sh install wildfly:current#17.0.1.Final --layers=cloud-profile,core-tools --dir=cloud-profile-with-tools
他にもどんなlayerがあるかは、WildFly Admin Guideの12.4. WildFly Galleon layersに解説があります。
ユースケース例として、WildFlyにSpring Frameworkを使ったアプリケーションをデプロイしたいので、GalleonによってEJBやremotingサブシステム(JMSサーバ接続やリモートEJBに使われるWildFly固有の機能)を削って、欲しい機能だけ持つWildFlyを作ることが考えられます。
Galleonが生まれた背景
コンテナのイメージサイズの削減がGalleonの目的です。
WildFlyには、元々@Statelessが付与されたコードがデプロイされてからEJBサブシステムをクラスロードするなど、アプリケーションで使われている機能をデプロイ処理で検知して必要な機能だけロードして余分なMetaspaceを消費しないようにする仕組みが備わっています。このため、Galleonによって使われないサブシステムを削除しても、メモリ削減にはあまり効果は期待できません。
前述のcloud-profileの場合、通常のWildFlyと比較しても起動直後のJavaヒープメモリのフットプリント削減の効果は手元で計測する限りでは10MB程度です。
APサーバとJDK、S2Iツール群など含んだJBossのコンテナイメージはどうしてもサイズが大きくなります。JBoss EAP7の場合、最新のイメージでは940MBと大きなイメージです。
$ docker pull registry.redhat.io/jboss-eap-7/eap72-openjdk11-openshift-rhel8:1.0-4 $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE registry.redhat.io/jboss-eap-7/eap72-openjdk11-openshift-rhel8 1.0-4 cbc9919e54f5 3 weeks ago 940MB
Dockerイメージのアプリのレイヤだけ別で、全て最新の同一バージョンのJBoss EAPが同一のワーカーノードで起動している場合はDockerによるイメージレイヤの共有が期待できます。しかし、別バージョンのJBoss EAPが複数起動している場合はディスクサイズをそれなりに消費します。
Web管理コンソールやリモートJMXサーバなど、EAP自体にもコンテナ環境ではあまり使わない機能が含まれています。このモノリスなAPサーバから使わない機能を削ってイメージサイズをなるべく小さくすることがGalleonの目的です。
必要な機能だけアーカイブに含める考え方はThorntailでも実現できていましたが、Quarkusの登場に伴い、Thorntailは今後メジャーバージョンアップせずにメンテナンスモードとなる*1ため、WildFlyとしてもイメージサイズ削減に手を打っています。
OpenShiftにGalleonで作ったWildFlyをデプロイする
Galloenを使ったWildFlyコンテナをデプロイするためのテンプレートやImageStreamはwildfly-s2iリポジトリで配布されています。テンプレートの使い方などの詳しいドキュメントは READMEに書かれています。
Galleonには前述のCLIの他にもMavenプラグインのgalleon-maven-pluginがあり、WildFlyのS2Iビルダイメージでは、BuildConfigによるイメージのビルド時にMavenプラグインのGalleonを動かして小さなWildFlyバイナリを生成します。
単純にS2Iビルド時にGalleonでWildFlyをビルドしても、元々のコンテナイメージのレイヤのサイズは小さくならず、ただレイヤを重ねてビルダイメージよりも大きくなるだけです。このためS2Iのツール群やJDK、WildFly本体を含んだビルダイメージquay.io/jfdenise/wildfly-centos7:latestと、WildFly本体やS2Iツール群を削ってJDKと便利スクリプトのみ含められたランタイムイメージquay.io/jfdenise/wildfly-runtime-centos7:latestの2種類のコンテナイメージが配布されています。
テンプレートには2つのBuildConfigが含まれています。1つはWildFlyにビルダイメージとソースコードをインプットに従来と同等のアプリケーションイメージを作成するBuildConfigと、生成されたアプリケーションイメージからGalleonで生成したディレクトリを抜き取って、ランタイムイメージにマージしたサイズの小さなアプリケーションイメージを生成するBuildConfigです。絵にすると以下のようなイメージです。
"抜き取る"とは、具体的には以下のようにBuildConfigを定義して、アプリケーションイメージの /s2i-output/server/ の内容をランタイムイメージにコピーしています。
- apiVersion: build.openshift.io/v1 kind: BuildConfig ... spec: output: ... source: dockerfile: |- FROM wildfly-runtime-centos7:latest COPY /server $JBOSS_HOME USER root RUN chown -R jboss:root $JBOSS_HOME && chmod -R ug+rwX $JBOSS_HOME RUN ln -s $JBOSS_HOME /wildfly USER jboss CMD $JBOSS_HOME/bin/openshift-launch.sh images: - from: kind: ImageStreamTag name: ${APPLICATION_NAME}-build-artifacts:latest paths: - sourcePath: /s2i-output/server/ destinationDir: "."
minishiftで実際に試してみます。
# プロジェクトの作成 $ oc new-project wildfly-galleon # ImageStreamとTemplateのロード $ oc create -f https://raw.githubusercontent.com/wildfly/wildfly-s2i/wf-17.0/templates/wildfly-builder-imagestream.yml $ oc create -f https://raw.githubusercontent.com/wildfly/wildfly-s2i/wf-17.0/templates/wildfly-runtime-imagestream.yml $ oc create -f https://raw.githubusercontent.com/wildfly/wildfly-s2i/wf-17.0/templates/wildfly-s2i-chained-build-template.yml # テンプレートwildfly-s2i-chained-build-templateによるイメージのビルド $ oc new-app --template=wildfly-s2i-chained-build-template -p APPLICATION_NAME=cloudprofile-app -p GIT_REPO=https://github.com/nagetsum/eap-debug.git -p GIT_CONTEXT_DIR=cloudprofile-app -p IMAGE_STREAM_NAMESPACE=wildfly-galleon -p GALLEON_PROVISION_SERVER=cloud-profile-h2
テンプレートのパラメータGALLEON_PROVISION_SERVER
がGalleonに渡すパラメータで、この例ではJAX-RS/CDI/JPA + H2インメモリDBを示すcloud-profile-h2
を指定します。その他のパラメータは前述のREADMEに言及があります。
イメージのサイズは従来のアプリケーションイメージが1.63GBに対し、小さいなアプリケーションイメージは568MBと約3分の1となりました。
$ minishift ssh [docker@minishift ~]$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE 172.30.1.1:5000/wildfly-galleon/cloudprofile-app latest 43b73a025880 28 minutes ago 568 MB 172.30.1.1:5000/wildfly-galleon/cloudprofile-app-build-artifacts latest a18f05787add 29 minutes ago 1.63 GB quay.io/jfdenise/wildfly-centos7 <none> 6983f22f7f32 6 weeks ago 1.22 GB quay.io/jfdenise/wildfly-runtime-centos7 <none> c449eca1cc04 6 weeks ago 436 MB
テンプレートを実行しただけではイメージがビルドされるだけなので、作ったイメージをデプロイします。
$ oc new-app cloudprofile-app:latest --> Found image 43b73a0 (37 minutes old) in image stream "wildfly-galleon/cloudprofile-app" under tag "latest" for "cloudprofile-app:latest" Tags: wildfly, wildfly17 * This image will be deployed in deployment config "cloudprofile-app" * Ports 8080/tcp, 8778/tcp will be load balanced by service "cloudprofile-app" * Other containers can access this service through the hostname "cloudprofile-app" --> Creating resources ... deploymentconfig.apps.openshift.io "cloudprofile-app" created service "cloudprofile-app" created --> Success Application is not exposed. You can expose services to the outside world by executing one or more of the commands below: 'oc expose svc/cloudprofile-app' Run 'oc status' to view your app. $ oc expose svc/cloudprofile-app route.route.openshift.io/cloudprofile-app exposed
このアプリケーションではJAX-RSのエンドポイントにアクセスすると、JPAを使ってインメモリのH2データベースから本情報を取り出しています。
$ oc get route NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD cloudprofile-app cloudprofile-app-wildfly-galleon.192.168.64.7.nip.io cloudprofile-app 8080-tcp None $ curl cloudprofile-app-wildfly-galleon.192.168.64.7.nip.io/cloudprofile-app/api/books/1; echo {"author":"Joshua Bloch","id":1,"title":"Effective Java"}