0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

JBoss EAP 8 on OpenShiftでOpenCV 4.13.0 をTektonビルドする

0
Last updated at Posted at 2026-03-30

TL;DR

OpenCVのJavaバインディングをJBoss EAP 8(UBI8/OpenJDK 17)上で動かすには、cmakeによるソースビルドが必要になる。本記事ではTekton PipelineRun / BuildConfig でのビルドフローと、実際に踏んだ4つのハマりどころを記録する。

この記事が役に立つケース:

  • OpenCV最新版(4.x)をJBoss EAPで使いたい
  • JavaCVを検討したがRHELバージョン問題やバージョン遅れで断念した
  • OpenShift(内部ネットワーク環境)でOpenCVをビルドしている

ハマりどころ早見表:

# 症状 原因
1 libopencv_java4130.somaven package 後に消える ファイル名に java を含む .so が消失(詳細未特定)
2 起動時に UnsatisfiedLinkError / GLIBCXX not found ビルドイメージとランタイムイメージのRHELバージョン不一致
3 起動時 UnsatisfiedLinkError(libjpeg等) ランタイムイメージに画像ライブラリ未インストール
4 cmake構成フェーズが数十分かかる OpenCVDownload.cmake のタイムアウトがデフォルト600秒

背景:なぜcmakeでビルドするのか

JavaCVを使わない理由

OpenCVのJavaバインディングとして、ソースビルドの代わりに JavaCV(bytedeco/javacv)を使う選択肢がある。しかし今回の環境では以下の理由で断念した。

① 最新のOpenCVバージョンが使えない

JavaCV 1.5.12はOpenCV 4.8.0に対応しており、OpenCV 4.13.0には未対応だった(検証時点)。OpenCVの新バージョンを使うには対応するJavaCVのリリースを待つ必要がある。

javacv-platform はOpenCV以外の大量のライブラリも同梱されている

javacv-platform を依存関係に追加すると、OpenCV・FFmpeg・OpenBLAS・Leptonica・Tesseractなど、コンピュータビジョン関連のネイティブライブラリが全プラットフォーム向けに一括ダウンロードされる。必要なのはOpenCVだけでも、依存ライブラリ込みで数百MBのJARが取り込まれる。

③ Ubuntu 24でビルドされているためRHEL 8では UnsatisfiedLinkError になる

JavaCVが配布するネイティブライブラリは、公式にはUbuntu 24.04(glibc 2.39)でビルドされている。JBoss EAP 8のランタイムイメージはUBI8ベース(glibc 2.28)のため、実行時にシンボルが見つからず UnsatisfiedLinkError が発生する。

java.lang.UnsatisfiedLinkError: .../libjnijavacpp.so:
  /lib64/libstdc++.so.6: version `GLIBCXX_3.4.26' not found

これはJavaCVの問題ではなく、ビルド環境とランタイム環境のglibc世代が合っていないことが原因であり、JavaCPP Presetsをソースからビルドすれば解決する。ただし自前ビルドを選んだ場合、JavaCPP PresetsはOpenBLASなどの依存ライブラリも一緒にビルドするため、OpenBLASのビルドだけで数十分〜数時間かかることがある。ソースビルドの手間はOpenCVを直接cmakeでビルドする場合と変わらないため、今回はOpenCVを直接ビルドする方針をとった。


環境

項目 バージョン
OpenCV 4.13.0
JBoss EAP 8.x
ベースイメージ UBI8 / OpenJDK 17
CI Tekton(PipelineRun)
アーティファクト管理 Nexus
OpenShift 4.x

アーキテクチャ

ビルドは3つのTaskと1つのBuildConfigに分かれる。OpenCVバイナリはアプリケーションのビルドとは独立したライフサイクルを持つため、Task 1・2はOpenCVのバージョンアップ時にのみ実行し、成果物はNexusに保存して再利用する。


PipelineRun 構成

Task 1: cmakeビルド

steps:
  - name: install-tools
    image: ubi8
    script: |
      dnf install -y cmake \
        java-17-openjdk-devel \
        python3 \
        gcc \
        gcc-c++ \
        make \
        pkgconfig \
        libjpeg-turbo-devel \
        libpng-devel \
        libtiff-devel \
        --enablerepo=ubi-8-baseos-rpms \
        --enablerepo=ubi-8-appstream-rpms

  - name: patch-sources
    image: ubi8
    script: |
      # タイムアウトを5秒に短縮(外部接続できなければ即fail)
      sed -i \
        -e 's/TIMEOUT[[:space:]]\+600/TIMEOUT 5/g' \
        -e 's/INACTIVITY_TIMEOUT[[:space:]]\+60/INACTIVITY_TIMEOUT 5/g' \
        opencv/cmake/OpenCVDownload.cmake

  - name: cmake-build
    image: ubi8
    script: |
      mkdir -p build && cd build
      cmake ../opencv \
        -DBUILD_SHARED_LIBS=ON \
        -DBUILD_JAVA=ON \
        -DBUILD_LIST=core,imgcodecs,imgproc,java,java_bindings_generator \
        -DBUILD_TESTS=OFF \
        -DBUILD_PERF_TESTS=OFF \
        -DBUILD_EXAMPLES=OFF \
        -DJAVA_HOME=/usr/lib/jvm/java-17-openjdk \
        -DCMAKE_BUILD_TYPE=Release \
        -DCMAKE_INSTALL_RPATH='$ORIGIN'
      make -j$(nproc)

  - name: rename-java-binding
    image: ubi8
    script: |
      # java を含むファイル名は maven package 中に消えるためリネーム(詳細はハマり1参照)
      mv build/lib/libopencv_java4130.so build/lib/libopencv_jni4130.so

-DBUILD_LIST について: ビルド対象モジュールを core,imgcodecs,imgproc,java,java_bindings_generator に限定している。これにより不要なモジュール(video, dnn, features2dなど)のビルドが省略され、ビルド時間の短縮と外部依存ライブラリのダウンロード対象の削減を同時に実現できる。javajava_bindings_generator はJavaバインディング生成に必須。

Task 2: Nexusへプッシュ

steps:
  - name: archive-and-push
    image: ubi8
    script: |
      tar -czf opencv-natives-4.13.0.tgz --wildcards *.so*

      # ネイティブライブラリ(tgz)
      curl -u ${NEXUS_USER}:${NEXUS_PASS} \
        --upload-file opencv-natives-4.13.0.tgz \
        https://nexus.example.internal/repository/raw-hosted/opencv/opencv-natives-4.13.0.tgz

      # Javaバインディング JAR
      curl -u ${NEXUS_USER}:${NEXUS_PASS} \
        --upload-file build/bin/opencv-4130.jar \
        https://nexus.example.internal/repository/raw-hosted/opencv/opencv-4130.jar

Task 3: oc start-build(毎回)

BuildConfigを oc start-build で起動する。ビルド本体(Galleon provisioning・maven package・マルチステージDockerfile)はすべてBuildConfig内で実行され、完了後にImageStreamにpushされる。

steps:
  - name: start-build
    image: registry.redhat.io/openshift4/ose-cli
    script: |
      oc start-build ${BUILD_CONFIG_NAME} \
        --namespace=${NAMESPACE} \
        --follow \
        --wait

BuildConfig:マルチステージDockerfile

EAP 8ではGalleon feature-packを使ったマルチステージビルドが標準となる。アーティファクトビルドステージでWARをビルドしつつNexusからOpenCVライブラリを取得し、ランタイムステージでEAPイメージに組み込む。BuildConfigの output にImageStreamを指定することで、ビルド完了後に自動的にpushされる。

# ── アーティファクトビルドステージ ──────────────────────────────
FROM registry.redhat.io/jboss-eap-8/eap8-openjdk17-builder-openshift-rhel8 AS builder
# Galleon provisioning + maven package ...
 
# Nexusからライブラリを取得・解凍
RUN curl -u ${NEXUS_USER}:${NEXUS_PASS} -L -o opencv-natives.tgz \
      https://nexus.example.internal/repository/raw-hosted/opencv/opencv-natives-4.13.0.tgz && \
    tar -xzf opencv-natives.tgz -C /tmp/opencv-natives/
 
# ── ランタイムステージ ──────────────────────────────────────────
FROM registry.redhat.io/jboss-eap-8/eap8-openjdk17-runtime-openshift-rhel8 AS runtime
 
# 画像ライブラリをインストール(-devel なしのランタイム用パッケージ)
# libopencv_imgcodecs.so がこれらに動的リンクしているため必要
RUN dnf install -y libjpeg-turbo libpng libtiff \
      --enablerepo=ubi-8-baseos-rpms \
      --enablerepo=ubi-8-appstream-rpms && \
    dnf clean all
 
COPY --from=builder /tmp/opencv-natives/ ${JBOSS_HOME}/modules/com/opencv/main/lib/
COPY --from=builder /path/to/opencv-4130.jar ${JBOSS_HOME}/modules/com/opencv/main/
# Galleon extensionsでJBossモジュールとして登録(後述)

JBossモジュール登録

OpenCVのネイティブライブラリとJARをJBoss EAPから利用するには、JBossモジュールとして登録する必要がある。module.xml でJAR・ネイティブライブラリパス・JDK依存を宣言し、Galleon extensionsまたは手動で ${JBOSS_HOME}/modules/ 以下に配置する。

<!-- ${JBOSS_HOME}/modules/com/opencv/main/module.xml -->
<module xmlns="urn:jboss:module:1.9" name="com.opencv">
    <resources>
        <resource-root path="opencv-4130.jar"/>
    </resources>
    <dependencies>
        <module name="javax.api"/>
    </dependencies>
</module>

ネイティブライブラリ(.so)のロードはモジュールシステム経由ではなく、アプリケーションコード側で System.load() によるフルパス指定で行う(詳細はハマり1の対処を参照)。


ネイティブライブラリのロード方式

JNIライブラリのロードには System.loadLibrary()System.load() の2種類がある。本構成では System.load() によるフルパス指定を採用している。

方式 パス解決 本構成での採否
System.loadLibrary("opencv_jni4130") LD_LIBRARY_PATH / java.library.path から検索 ❌ 不採用
System.load("/full/path/libopencv_jni4130.so") 引数のフルパスを直接使用 ✅ 採用

System.loadLibrary() を不採用にした理由: LD_LIBRARY_PATH はプロセス全体に影響するため、JBoss EAP内の他のネイティブライブラリ(Undertow等)のロードに干渉するリスクがある。System.load() であればパス解決がアプリケーション内で完結する。

さらに、cmakeビルド時に -DCMAKE_INSTALL_RPATH='$ORIGIN' を指定しているため、libopencv_jni4130.so から libopencv_core4130.so 等への依存解決は同一ディレクトリ内で行われ、LD_LIBRARY_PATH の設定は不要となる。

static {
    // Galleon extensions が設定する環境変数からパスを取得
    String libPath = System.getenv("OPENCV_NATIVE_LIB_PATH");
    System.load(libPath + "/libopencv_jni4130.so");
}

ハマりどころ

1. libopencv_java4130.somaven package 中に消える

症状

NexusからtgzをダウンロードしてBuildConfig内で解凍しても、libopencv_java4130.so だけが target ディレクトリ内に存在しない。他の .so ファイルは正常に存在する。

原因

消失するフェーズの詳細は未特定。調査した範囲では以下が候補として残っている。

  • maven-dependency-pluginunpack 設定に excludes がある
  • maven-resources-pluginfiltering がバイナリを破損させる
  • eap-maven-plugin のコンテンツコピー処理が java を含む名前を除外する

共通しているのはファイル名に java を含む .so だけが消えるという点。libopencv_jni4130.solibopencv_core4130.so などは消えない。

対処

Task 1のビルド完了直後、Nexusプッシュ前にリネームする。

# NG: libopencv_java4130.so → maven package 中に消える
# OK: libopencv_jni4130.so  → 消えない
mv build/lib/libopencv_java4130.so build/lib/libopencv_jni4130.so

2. ビルドイメージとランタイムイメージのRHELバージョンを一致させないと UnsatisfiedLinkError

症状

java.lang.UnsatisfiedLinkError: .../libopencv_jni4130.so:
  /lib64/libstdc++.so.6: version `GLIBCXX_3.4.26' not found

ビルドは正常に完了しているにもかかわらず、起動時に初めて発覚する

原因

JavaからC++のコードを呼び出すJNI(Java Native Interface)では、System.load() 呼び出し時にJVMがOS上の .so ファイルをロードし、glibc・libstdc++などのシステムライブラリを動的にリンクする。このとき、.so をコンパイルした環境のglibc・libstdc++バージョンが実行環境より新しいと UnsatisfiedLinkError が発生する。「ビルドできた」はコンパイル成功であり、実行環境との互換性は別問題

対処

cmakeビルドのベースイメージを、EAPランタイムイメージと同じRHELバージョンのUBIに統一する。

ビルドイメージ ランタイムイメージ 結果
ubi9 eap8-openjdk17-...-rhel8(UBI8) 起動時にfail
ubi8 eap8-openjdk17-...-rhel8(UBI8) 正常

この不一致はビルドが成功してもランタイムまで気づけない。ビルドパイプライン設計の最初にランタイムイメージのRHELバージョンを確定させてからベースイメージを選ぶのが確実。

なお、JavaCV公式バイナリがUbuntu 24(glibc 2.39)ベースでビルドされているためUBI8上で動かない問題も、この同じ構造から来ている。


3. 画像ライブラリが不足すると UnsatisfiedLinkError

ビルドイメージ側(気づきにくい)

cmakeビルド時に libjpeg-turbo-devellibpng-devellibtiff-devel が存在しない場合、OpenCVはJPEG/PNG/TIFFサポートなしでビルドされる。cmakeはエラーにならず警告のみで通過するため気づきにくい。

確認方法: cmake実行後の出力で Media I/O セクションを確認する。YES になっていない項目は無効化されている。

--   Media I/O:
--     ZLib:                        zlib (ver 1.2.11)
--     JPEG:                        libjpeg-turbo (ver 2.0.4-62)  ← YES であること
--     PNG:                         libpng (ver 1.6.34)           ← YES であること
--     TIFF:                        libtiff (ver 42 / 4.0.9)     ← YES であること

ランタイムイメージ側

ランタイムイメージに libjpeg-turbolibpnglibtiff-devel なし)が存在しない場合、OpenCVの .so がこれらへの動的リンク解決に失敗し、起動時に UnsatisfiedLinkError が発生する。ハマり2と同じエラー種別だが原因が異なる。

UnsatisfiedLinkError: .../libopencv_jni4130.so:
  libjpeg.so: cannot open shared object file: No such file or directory

ビルドイメージとランタイムイメージでのパッケージの使い分け

パッケージ ビルドイメージ ランタイムイメージ
libjpeg-turbo-devel ✅ 必要(ヘッダファイル)
libjpeg-turbo ✅ 必要(ランタイム)
libpng-devel
libpng
libtiff-devel
libtiff

-devel パッケージはヘッダファイルを含むビルド用パッケージであり、実行時には不要。


4. cmake構成フェーズで外部ダウンロードが1件最大10分待ってからfailする

症状

外部ネットワーク接続がない環境でcmakeを実行すると、cmake構成フェーズ(cmake ../opencv ... の実行中、Makefile生成前)で長時間フリーズしたように見える。実際には接続できない外部URLへのダウンロードを延々と待っている。

原因

cmake/OpenCVDownload.cmake にデフォルトのタイムアウト値が定義されている。

# cmake/OpenCVDownload.cmake(抜粋)
set(OPENCV_DOWNLOAD_PARAMS INACTIVITY_TIMEOUT 60 TIMEOUT 600 ...)
#                                              ^^         ^^^
#                              無通信タイムアウト60秒   全体タイムアウト600秒(10分)

OpenCVはcmake構成フェーズでippicv・adeなど複数の依存ライブラリを個別に外部からダウンロードしようとする。外部接続できない環境では1件あたり最大600秒待ってからfailするため、ライブラリ数に比例して積み重なる。

対処

cmake実行前に sedOpenCVDownload.cmake のタイムアウトを短縮する。

# タイムアウトを5秒に短縮(外部接続できなければ即fail)
sed -i \
  -e 's/TIMEOUT[[:space:]]\+600/TIMEOUT 5/g' \
  -e 's/INACTIVITY_TIMEOUT[[:space:]]\+60/INACTIVITY_TIMEOUT 5/g' \
  opencv/cmake/OpenCVDownload.cmake

これにより外部接続できないライブラリは約5秒でfailし、cmake構成フェーズが即座に次の処理に進む。なお、ダウンロードが必要な依存ライブラリについては、-DBUILD_LIST でビルド対象モジュールを必要最小限に絞ることでダウンロード対象自体を減らすアプローチを併用している。


まとめと教訓

ハマりどころ 原因 対処 教訓
.so が消える maven package 中に消失 jni にリネーム ネイティブライブラリのファイル名は疑う
GLIBCXX not found RHEL世代の不一致 UBI8で統一 ランタイムのRHELバージョンを起点に設計する
libjpeg.so not found 画像ライブラリ未インストール dnf install cmake出力の Media I/O を必ず確認
構成フェーズが遅い TIMEOUT 600 / INACTIVITY_TIMEOUT 60 sed で5秒に短縮 内部NW環境ではタイムアウト設定を確認

最も見つけにくいのはRHELバージョン不一致(ハマり2)。 ビルドが通るため見逃しやすく、起動時に初めて発覚する。エラーメッセージも GLIBCXX の話であり、一見OpenCVとの関連が見えない。

JavaCVが避けられない場合も含め、ネイティブライブラリを扱うときの原則として「ビルド環境とランタイム環境のglibc世代を一致させる」を設計時点で決めておくと、後から追いかける必要がなくなる。


参考リンク

OpenCV

JavaCV / JavaCPP Presets

JNI・ネイティブライブラリ・glibc

JBoss EAP 8 / OpenShift

Tekton

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?