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

TECH PLAY

Red Hat  - Open Innovation Labs

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

全900件

※本記事は OpenShift Virtualization アドベントカレンダーの 15 日目の記事です。 qiita.com 皆さんこんにちは、OpenShift Virtualization とストレージを生業にしている Red Hat のうつぼ(宇都宮)です。 唐突ですが昨日私 VMware の資格試験を受けてきました。結果は無事合格。多分一番簡単な試験だったんですが、胸を撫で下ろしています。 なんで Red Hat のオマエが VMware の資格取ってんだよと突っ込むところですが、やっぱ VMware からの移行を検討しているお客様が圧倒的に多いんですね。ほんとに圧倒的で20馬身以上あります。 なので、「さすがに Virt 特命係のうつぼは移行元の VMware の勉強せにゃならんでしょう、せっかく勉強するならついでに資格も取ってしまえ、受験費用は会社が出すから。」 ということになり、ホヤホヤの VCF9 対応の VMware vSphere Foundation Administrator を取ったということです。※落ちたら自腹だからかなり怖かった。 さて、VMware からの移行でしばしば挙がってくる話題が、RDM(Raw Device Mapping)ディスクはどう移行するのか?です。RDM はシンプルに容量が大きいことが多いので、移行は大変そうですよね。 ということで今日のテーマは RDM の移行です。 VMware 上で物理互換モードの RDM を使う VM から RDM を外して付け替える OpenShift で LUN を PV として定義する Virt 上で VM に PVC を追加する。 VM でディスクをみてみる まとめ OpenShift Virtualization(以下 Virt)では、RDM のように 外部ストレージの LUN をそのまま VM に見せる 方法があります。 本稿では、vSphere 上の VM に見せていた RDM ディスクを Virt 上の VM に見せるにはどうするのか & どうなるのか?を実際にやってみて紹介します。 VMware 上で物理互換モードの RDM を使う RDM とはなんぞや?については Red Hatter の私が語るのはおこがましいし、先人たちの様々な記事があるので省略します。 Virt で RDM の出元の LUN を認識するためには、 物理互換モードの RDM(p-RDM) であることが必要です。 「互換モード : 物理」になってること RDM の多くは p-RDM で使われてると思うので、ここは大抵問題ないと思います。 上の図は、RHEL の VM に iSCSI ストレージから切り出した 10GiB の RDM ディスクを追加しているところです。互換モードに『物理』が選ばれていることを確認します。 この VM を起動すると当然ながら 10GiB のディスクが見えます。RDM は SCSI なので /dev/sdX と認識されます。 [root@mig-test-rhel ~]# lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 0 316G 0 disk ├─sda1 8:1 0 600M 0 part /boot/efi ├─sda2 8:2 0 1G 0 part /boot └─sda3 8:3 0 314.4G 0 part ├─rhel-root 253:0 0 312.8G 0 lvm / └─rhel-swap 253:1 0 1.6G 0 lvm [SWAP] sdb 8:16 0 10G 0 disk   # RDM ディスク このディスクをファイルシステムで使うケースと Raw で使うケースがありますが、どちらでも同じです。ここでは簡単のため XFS でフォーマットしてファイルシステムとして使います。 フォーマットしたらマウントして適当なファイルを突っ込みます。複数のディレクトリを掘ってその中にファイルを1つずつ格納します。 [root@mig-test-rhel ~]# mkfs.xfs /dev/sdb specified blocksize 4096 is less than device physical sector size 16384 switching to logical sector size 512 meta-data=/dev/sdb isize=512 agcount=4, agsize=655361 blks = sectsz=512 attr=2, projid32bit=1 = crc=1 finobt=1, sparse=1, rmapbt=0 = reflink=1 data = bsize=4096 blocks=2621444, imaxpct=25 = sunit=0 swidth=0 blks naming =version 2 bsize=4096 ascii-ci=0, ftype=1 log =internal log bsize=4096 blocks=2560, version=2 = sectsz=512 sunit=0 blks, lazy-count=1 realtime =none extsz=4096 blocks=0, rtextents=0 Discarding blocks...Done. [root@mig-test-rhel ~]# mount /dev/sdb /mnt ... ファイルを生成 ... [root@mig-test-rhel ~]# find /mnt -maxdepth 2 -type f | sort /mnt/A-alpha/Adele.txt /mnt/B-bravo/Brad_Pitt.txt /mnt/C-charlie/Charlie_Chaplin.txt /mnt/D-delta/Denzel_Washington.txt /mnt/E-echo/Emma_Stone.txt /mnt/F-foxtrot/Frank_Sinatra.txt /mnt/G-golf/George_Clooney.txt /mnt/H-hotel/Hugh_Jackman.txt /mnt/I-india/Ian_McKellen.txt /mnt/J-juliett/Jennifer_Lawrence.txt /mnt/K-kilo/Keanu_Reeves.txt /mnt/L-lima/Leonardo_DiCaprio.txt /mnt/M-mike/Morgan_Freeman.txt /mnt/N-november/Natalie_Portman.txt /mnt/O-oscar/Oprah_Winfrey.txt /mnt/P-papa/Paul_McCartney.txt /mnt/Q-quebec/Queen_Latifah.txt /mnt/R-romeo/Robert_De_Niro.txt /mnt/S-sierra/Scarlett_Johansson.txt /mnt/T-tango/Tom_Hanks.txt /mnt/U-uniform/Usain_Bolt.txt /mnt/V-victor/Victoria_Beckham.txt /mnt/W-whiskey/Will_Smith.txt /mnt/X-xray/Xavier_Dolan.txt /mnt/Y-yankee/Yoko_Ono.txt /mnt/Z-zulu/Zendaya.txt [root@mig-test-rhel ~]# cat /mnt/A-alpha/Adele.txt A - alpha : Adele VM から RDM を外して付け替える はい下準備は終わり。ここからが本番です。データが入った RDM を VM から取り除きます。 VM 上でファイルシステムをアンマウントしてから、vSphere Client などで VM の設定から RDM ディスクを削除すればよいです。 vmdk ファイルも削除するか?のオプションについては正直どちらでも構いません。中のデータまで消えることはありません。 [root@mig-test-rhel ~]# umount /mnt ... RDM ディスクを削除 ... [root@mig-test-rhel ~]# lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 0 316G 0 disk ├─sda1 8:1 0 600M 0 part /boot/efi ├─sda2 8:2 0 1G 0 part /boot └─sda3 8:3 0 314.4G 0 part ├─rhel-root 253:0 0 312.8G 0 lvm / └─rhel-swap 253:1 0 1.6G 0 lvm [SWAP] iSCSI の場合は ESXi が認識しているターゲットを削除して、iSCSI セッションを切ります。 これで LUN がフリーになりました。ここからはストレージ側の操作で、LUN を認識させているホストを ESXi ホストから OpenShift クラスタのノードに切り替えます。 ストレージ装置ごとに操作が異なるので手順は割愛します。 OpenShift で LUN を PV として定義する ここからは OpenShift 側の操作です。 いま LUN が OpenShift のノードから見えている状態になっているはずです。さっそく PV を定義してみましょう。 通常の iSCSI LUN の PV の定義で OK です。こちらのドキュメントを参考にして下さい。 https://docs.redhat.com/ja/documentation/openshift_container_platform/4.20/html-single/storage/index#persistent-storage-using-iscsi Virt の仮想ディスクとして使ううえで重要なのは、 accessModes: ReadWriteMany と volumeMode: Block を指定することです。 問題がなければ Available になるはずです。 apiVersion: v1 kind: PersistentVolume metadata: name: iscsi-rdm-pv spec: capacity: storage: 10Gi accessModes: - ReadWriteMany persistentVolumeReclaimPolicy: Retain volumeMode: Block iscsi: targetPortal: 192.168.34.3:3260 iqn: iqn.2005-10.org.freenas.ctl:rdm-vmware lun: 0 readOnly: false それでは次に PVC を作成してこの PV にバインドします。 storageClassName: "" と明示しないと default StorageClass から PV を見つけようとしてしまうので注意。 apiVersion: v1 kind: PersistentVolumeClaim metadata: name: iscsi-rdm-pvc spec: accessModes: - ReadWriteMany resources: requests: storage: 10Gi volumeName: iscsi-rdm-pv storageClassName: "" PVC が Bound になっていれば OK です。 [root@bastion ~]# oc get pvc iscsi-rdm-pvc -n demo NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE iscsi-rdm-pvc Bound iscsi-rdm-pv 10Gi RWX <unset> 2m53s Virt 上で VM に PVC を追加する。 それではこの PVC を指定して VM に追加してみます。MTV で移行した VMware にいた RHEL VM でもよいですし、全然違う VM でもどちらでも構いません。 ここでは全然違う VM につけてみます。 Virt の画面で VM を選んでディスクの追加を行います。ディスクの追加のメニューで Volume を選びます。 Volume を選ぶ 次に、先ほど作成した PVC を選択して、 タイプ : LUN 、 インターフェース : SCSI を指定します。これが Virt で LUN を RDM 的にそのまま使うための設定です。 タイプとインターフェース ※ディスクのタイプとかインターフェースの特徴については以前の投稿をご覧下さい。 rheb.hatenablog.com VM でディスクをみてみる さあさあさあ、VM にログインしてこのディスクをみてみましょう。 [cloud-user@rhel9-jade-wildfowl-88 ~]$ lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS sda 8:0 0 10G 0 disk   # iSCSI LUN vda 252:0 0 30G 0 disk ├─vda1 252:1 0 1M 0 part ├─vda2 252:2 0 200M 0 part /boot/efi ├─vda3 252:3 0 1G 0 part /boot └─vda4 252:4 0 28.8G 0 part / vdb 252:16 0 1M 0 disk [cloud-user@rhel9-jade-wildfowl-88 ~]$ blkid /dev/vdb: UUID="2025-12-16-01-58-59-00" LABEL="cidata" TYPE="iso9660" /dev/sda: UUID="c55d3c33-fad2-4d91-be70-ae97d712d540" TYPE="xfs" /dev/vda4: LABEL="root" UUID="00ec0807-5182-4552-9cc4-a3b59fad9934" TYPE="xfs" PARTUUID="6f44decf-0866-4dfb-85df-7022a4c53d47" /dev/vda2: SEC_TYPE="msdos" LABEL_FATBOOT="ESP" LABEL="ESP" UUID="236D-D209" TYPE="vfat" PARTUUID="57cd19ce-1c2f-456b-9426-e9c25944977d" /dev/vda3: LABEL="boot" UUID="2c158aeb-eddc-4161-a951-104986383cbc" TYPE="xfs" PARTUUID="eb823f54-c299-467b-b5c2-c4c159c33e45" /dev/sda という名前で SCSI ディスクとして認識できていますし、xfs タイプのパーティションも認識できています。このままマウントできそうですね。 じゃあマウントして中身を見てみましょう。 [cloud-user@rhel9-jade-wildfowl-88 ~]$ sudo mount /dev/sda /mnt [cloud-user@rhel9-jade-wildfowl-88 ~]$ find /mnt -maxdepth 2 -type f | sort /mnt/A-alpha/Adele.txt /mnt/B-bravo/Brad_Pitt.txt /mnt/C-charlie/Charlie_Chaplin.txt /mnt/D-delta/Denzel_Washington.txt /mnt/E-echo/Emma_Stone.txt /mnt/F-foxtrot/Frank_Sinatra.txt /mnt/G-golf/George_Clooney.txt /mnt/H-hotel/Hugh_Jackman.txt /mnt/I-india/Ian_McKellen.txt /mnt/J-juliett/Jennifer_Lawrence.txt /mnt/K-kilo/Keanu_Reeves.txt /mnt/L-lima/Leonardo_DiCaprio.txt /mnt/M-mike/Morgan_Freeman.txt /mnt/N-november/Natalie_Portman.txt /mnt/O-oscar/Oprah_Winfrey.txt /mnt/P-papa/Paul_McCartney.txt /mnt/Q-quebec/Queen_Latifah.txt /mnt/R-romeo/Robert_De_Niro.txt /mnt/S-sierra/Scarlett_Johansson.txt /mnt/T-tango/Tom_Hanks.txt /mnt/U-uniform/Usain_Bolt.txt /mnt/V-victor/Victoria_Beckham.txt /mnt/W-whiskey/Will_Smith.txt /mnt/X-xray/Xavier_Dolan.txt /mnt/Y-yankee/Yoko_Ono.txt /mnt/Z-zulu/Zendaya.txt [cloud-user@rhel9-jade-wildfowl-88 ~]$ cat /mnt/A-alpha/Adele.txt A - alpha : Adele OK!ばっちり RDM のファイルを持ってこれましたね! まとめ こんな感じで VMware の RDM ディスクを付け外しで Virt に持ってこれました。 RDM や SCSI LUN タイプは仮想化レイヤーを極力通さずオーバーヘッドが小さいため、ストレージ側のパフォーマンスを最大限活かすことができます。 また、最初にも書きましたが RDM は大容量で使われるパターンがあるので、普通にデータコピーして移行するのはかなり骨が折れます。 そんな場合は、こんなやり方でも移行(移動)できるよという紹介でした。 あ、もちろんですが、この方法はいきなり本番で試すのではなくて、きちんと検証して手順を確立させてからやってくださいね。ストレージ装置ごとに手順が違ったりするので。 ということで今日はここまで。
Apache Camel Advent Calendar 15日目の記事は、サポート担当古市が担当します。 テーマは、「Deploy camel to k8s/OpenShift」 Camelを使った Java アプリケーションを k8s/OpenShift 環境へデプロイする方法は複数あり、現時点ではサーチエンジンやAIなどで回答を求めた場合、製品サポート対象ではない方法が提示されることもあり鬼門のトピックです。 赤帽ブログの記事ですので、Red Hat Build of Apache Camel(RHBAC) 製品サポート対象となるデプロイ方法をご紹介します。 現在、RHBAC 4.x では2つの runtime をサポートしており、runtime 毎でサポートされるデプロイ方法が異なります。 Quarkus (Red Hat build of Quarkus(RHBQ) と同じ方法) Spring Boot (openshift-maven-plugin を使用) なぜ runtime によってデプロイ方法が違うのか不思議に思う方が多いかと思います。自分もその1人です。。 話が脱線しますが、異なるデプロイ方法になった経緯を自分なりに想像してみたので、共有します。(公式情報ではありません。あくまでも個人の推測です。) RHBAC のバージョンは Apache Camel のバージョンに準拠しています。 4.8 や、4.10 といったように。 しかし、RHBAC for Quarkus の場合、製品番号がガラッと変わります。4.8 相当は 3.15 で、4.10 相当は 3.20といったように。 access.redhat.com このバージョンはどこから来ているかというと、RHBQ です。 access.redhat.com Quarkus runtime の拡張機能として各種 extension が提供されておりますが、camel extension もサポート対象となる RHBQ が RHBAC for Quarkus であり、あくまでも主語は Quarkusです。 なので、RHBAC for Quarkus のバージョンは RHBQ のバージョンが使われており、RHBQユーザーにはわかりやすいでしょう。(逆に、Quarkus未経験の Camelユーザーが混乱する理由でもあります。) 「あくまでも主語は Quarkus」なので、k8s/OpenShift環境へのデプロイ方法が RHBQと同じなのも腹落ちします。 RHBAC for Quarkus の製品ドキュメントでデプロイ方法を確認すると、RHBQドキュメントのリンクが紹介されています。デプロイ方法の詳細については、こちらをご確認ください。 docs.redhat.com 次に Spring Bootについて。 RHBAC の全身となる Red Hat Fuse 7.x では、「Fuse on Openshift」も提供しており、サポート対象 runtime(karaf, EAP, SpringBoot)は maven plugin を使いデプロイする方法を提供しておりました。 その maven pluginこそが、openshift-maven-plugin でした。 docs.redhat.com karaf runtime と EAP runtime が deprecated となり、最終的に RHBAC では Spring Boot runtime サポートが残り、Quarkus runtime が追加された状況となります。 Red Hat Fuse(Fuse on OpenShift) からの流れもあり、k8s/OpenShift環境へのデプロイ方法に openshift-maven-plugin が使われているのだと考えられます。 「Fuse on Openshift」を使われていたユーザーには馴染みがある一方、OpenShiftを使った運用経験者が初めて RHBAC を取り扱う際には困惑されるようです。なぜ、openshift-maven-plugin を強要するのだと。。 RHBAC for Spring Boot の製品ドキュメントでは、openshift-maven-plugin を使ったデプロイ方法の紹介がされていますが、かなりシンプルです。 docs.redhat.com mvn clean -DskipTests oc:deploy -Popenshift 細かなオプションなどはコミュニティードキュメントをご確認いただくか、製品サポート窓口のご利用をご検討ください。 eclipse.dev 番外編として、もう1つ方法があります。 camel-jbang も k8s plugin による連携が可能です。 camel.apache.org camel-jbang は開発用ツールの位置付けであり、簡単に動作確認する際にご利用いただけます。こんな方法もあるんだといった知識としてお持ちいただく程度で、プロジェクトのデプロイは前述した2つの方法をご利用いただくようご検討ください。 明日の advent calendar 16日目では、「Camel で header/variable を使い分ける」についてご紹介します。 アドベントカレンダーの一覧はこちらです。 qiita.com
Apache Camel Advent Calendar 14日目の記事は、サポート担当古市が担当します。 テーマは、「Camel kamelet」 昨日ご紹介した route Template をベースに、各種 camel component をコネクターとして用意し、ユーザーはコネクターを並べてサービスを定義するという kamelet が開発されました。製品提供は終了しましたが、Openshift上のオペレーター(Camel-K Operator)を使いインテグレーションのライフサイクルを制御し、ブラウザー上の GUI を使いインテグレーションの作成やデプロイなどを管理する Fuse Onlineが登場した当時、ローコードのインテグレーションプラットフォームとして注目されました。 syndesis.io 製品提供は終了されましたが、各種製品要素は Red Hat Build of Apache Camel や後日紹介する kaotoや DataMapper に受け継がれています。 本番運用でのお問い合わせがグローバルの中でも多かった日本では、kamelet ベースの資産をお持ちであったり、route Template ではなく kamelet を使ったテンプレート方法を好まれる方がいらっしゃるかもしれません。そんな方々向けに Red Hat Build of Apache Camel では、camel-kamelet コンポーネントを使いそれら kamelet をご利用いただけます。 camel.apache.org こちらのリポジトリーから、サンプルプロジェクト(Camel for SpringBoot)を確認してみましょう。 github.com pom.xml から、camel-kamelet コンポーネントへの依存を確認できます。 <dependency> <groupId>org.apache.camel.springboot</groupId> <artifactId>camel-kamelet-starter</artifactId> </dependency> yamlで定義された kamelet は kamelet-chucknorris/src/main/resources/kamelets/chuck-norris-source.kamelet.yaml で確認できます。 (kamelet は yamlで定義します。kaotoを使えば、GUIで編集することも可能です。) kamelet を 1 から学びたいという方は、こちらをご確認ください。 camel.apache.org chuck-norris-source.kamelet.yaml の template はこのような内容で定義されています。 template: from: uri: "timer:chuck" parameters: period: "{{period}}" steps: - to: "https://api.chucknorris.io/jokes/random" - setBody: jsonpath: "$.value" - to: "kamelet:sink" 末尾の "kamelet:sink" が実行されると、camel-kamelet コンポーネントから実行された kamelet integrationの制御が camel-kamelet に戻ります。 camel-kamemelt エンドポイントを含む camel ルートはこちらに定義されています。 kamelet-chucknorris/src/main/java/sample/camel/MyRoute.java from("kamelet:chuck-norris-source") .delay(500) .log("${body}"); consumer endpoint "kamelet:" に続く "chuck-norris-source" は、 chuck-norris-source.kamelet.yaml に定義された名前です。 kind: Kamelet metadata: name: chuck-norris-source プロパティ値 "period"が指定されていませんが、今回のサンプルではデフォルト値が適用されます。 properties: period: title: Period description: The interval (msec) to wait before getting the next joke type: integer default: 10000 camel-kamelet エンドポイントでルートやテンプルレートのプロパティ設定も可能ですので、詳細については camel-kafka componentの説明ページをご確認ください。 camel.apache.org サンプルの実行方法については readme.adoc をご確認いただくとして、camel-kamelet により kameletを使ったテンプレートもご活用いただけるイメージがついたかと思います。 明日の advent calendar 15日目では、「deploy camel to k8s/OpenShift」 についてご紹介します。 アドベントカレンダーの一覧はこちらです。 qiita.com
※本記事は OpenShift Virtualization アドベントカレンダーの 13日目の記事です。 qiita.com 目次 はじめに 【積極採用中!】Red Hat 採用に関するお知らせ この記事のゴール Q. Red Hat OpenShift環境では、ストレージはどのように使われるのか? Q. Red Hat OpenShift Virtualizationに対応しているストレージはどのように調べれば良いのか?? Q. どういうこと? Q. CSIドライバーのバージョン管理のコツは? 補足 ちょっと上級者向けの情報 あとがき はじめに 皆さんこんにちわ、Red Hat Global Learning ServicesでLearning Solution Architectを担当している坂井 大和( @lab8010 )です。 主に X.com 上でRed Hat製品やサービスについて配信をしているので、もしよければフォローくださると幸いです。 この記事では、 『Red Hat OpenShift Virtualizationで使えるストレージ製品ってどう調べるの?』 という疑問に対して解説します。 なお、本記事で想定するストレージ=基本的に物理ストレージ製品を想定します。 【積極採用中!】Red Hat 採用に関するお知らせ Red Hatでは事業拡大に伴い人材を募集しています。 本記事でご紹介している『OpenShift Virtualization』に関するコンサルタントを募集しています。もし少しでも興味がある方は、ご応募または本記事を執筆している私坂井までお気軽にDMをください。( X / Linkedin ) redhat.wd5.myworkdayjobs.com この記事のゴール この記事を読み終わった頃には、読者様が次のことが出来る状態になって頂きたいと考えています。 Red Hat OpenShift Virtualizationで使用可能なストレージの調べ方 それでは、早速本編に入っていきましょう。 Q. Red Hat OpenShift環境では、ストレージはどのように使われるのか? A. 次の記事に詳細を記述していますので、ご一読いただけると幸いです。 本記事の内容は、以下の記事内容をご理解いただいた上で読めるようにしています。 rheb.hatenablog.com Q. Red Hat OpenShift Virtualizationに対応しているストレージはどのように調べれば良いのか?? A. 次の主要なストレージメーカーは、Red Hat OpenShiftのようなKubernetesベースの環境においてストレージ製品と連携するためのCSIドライバーを提供しています。 特にRed Hat製品と互換性があるCSIドライバーは Red Hat Ecosystem Catalog に掲載されます。 以下のリンクは、『OpenShift Virtualizationで使用できる』『Storage関連』の製品でフィルターをしています。 catalog.redhat.com もし、Ecosystem Catalogのトップページから自身でフィルターをかけたい場合は、次の箇所のフィルターを使用してください。 始めにタイプはSoftwareを選択します。 Type - Softwareを選択 次にカテゴリはStorageを選択します。 Category - Storageを選択 最後に、Infrastructure featuresはOpenShift Virtualizationを選択します。 なお、以下の選択肢からもわかる通り、 仮想マシン環境を込みとするかどうかで使用可能なストレージ製品は微妙に異なります! Infrastructure features - OpenShift Virtualizationを選択 これにより残った検索結果に表示されているものが、OpenShift Virtualizationで使用可能なCSIドライバー達です。ここを見るだけで、少なくともどこのメーカーがOpenShift Virtualizationに対応したストレージを出しているかはわかります。 2025年12月5日時点の検索結果 ここで一つ注意点ですが、 『ここに表示されたストレージベンダーのあらゆるストレージがOpenShift Virtualizationで使えるわけではない』 ということです。 Q. どういうこと? A. 上記の検索結果は『OpenShift Virtualizationで使用可能なストレージを含むCSIドライバー』が表示されますが、『そのCSIドライバーに対応するあらゆるストレージ製品』がOpenShiftVirtualizationに対応しているかどうかは別の話です。(この点については最後に図解を用意していますのでそちらで最後にまとめます。) 実際の例を用いて解説します。 例えばDell TechnologiesのCSIドライバーである 『Dell Container Storage Modules(略称CSM)』 について調べてみます。 catalog.redhat.com 以下のページ内の Supported Platform にアクセスします。 各ベンダーのCSIドライバーのページ内のレイアウトは異なります 移動後のページは、Dell TechnologiesがメンテナンスしているGitHubページが表示されます。以下のページでは、このCSIドライバーとRed Hat OpenShiftのバージョン互換性情報が掲載されています。 このページの情報では、Dell TechnologiesのCSM 1.15.1ではRed Hat OpenShift 4.19までが互換性があることがわかります。 そして、画面右上には OpenShift Virtualization の記載があります。 Supported Platformでは様々な視点でのサポート構成情報が確認できます 上図内のOpenShift Virtualizationの項をクリック後、次の情報が表示されました。 以下によれば一部のストレージ(PowerScaleとUnity XT)はOpenShift Virtualizationとの互換性がないようです。 本図キャプチャ時点では PowerStore/PowerFlex/PowerMaxが OpenShift Virtualizationと互換性があります つまり、ここまでの内容を図解してみるとこうなります。 2025年12月時点のDell CSM側の情報です。 今後は製品のアップデートによりこの情報は変化する可能性もあります。 常に公式情報を確認するようにしましょう。 ちなみにで言うと、Dell Technologiesが提供するストレージ製品の中に PowerVault ME5 というシリーズがあるのですが、それについてはそもそもDell CSMでそもそも制御対象とはされておらず、またRed HatのKBにおいても、このストレージ製品がOpenShift Virtualizationに対応しているかどうかはベンダーに尋ねるようにという記載がなされています。 access.redhat.com さらに余談で言えば、Dell PowerVault ME5はSeagate社の筐体を使用したOEM製品であるため、CSIドライバーはSeagate社のものを使用するという案内があります。 www.dell.com 上記の 『(Dell PowerScaleとUnityXTが)OpenShift Virtualizationに非対応』 という情報を見ると、 『(仮想マシンではなく)コンテナのためのOpenShift環境ではサポートされるのか?』 という疑問も生まれますが、ストレージ提供元であるDell Technologiesのいくつかの資料を読むとコンテナのためのOpenShift環境での利用はできそうです。 OpenShiftのための各種ストレージのインストール手法紹介ページ https://dell.github.io/csm-docs/docs/getting-started/installation/openshift/ Dell Technologiesが提供する新しいインフラストラクチャ - Dell Private Cloud https://www.delltechnologies.com/asset/en-us/solutions/infrastructure-solutions/briefs-summaries/private-cloud-red-hat-openshift-brief.pdf ここで強調したいポイントは、 『Red Hat OpenShiftに対する動作サポートは、ストレージ提供者側が決定する』 ということです。 KubernetesやOpenShiftはCSIという仕組みは提供する が、 CSIドライバーから物理ストレージまではストレージ提供者側の範疇 という責任分解的な考え方が生まれます。 よって、セットアップやトラブルシュートなどが必要な場面ではこれらのレイヤーごとに問い合わせ先が変化するため、運用者は双方の知識を有していることが望ましいといえます。 Q. CSIドライバーのバージョン管理のコツは? A. Red Hat OpenShiftとCSIドライバーと対象ストレージ製品の3種はセットで要確認である。 以下のリンク内にある情報(CSM v 1.15.1の場合、2025年12月上旬時点)では以下の互換性となっている。 Support Matrix | Dell Technologies ある箇所のアップデートを計画する場合、そのアップデートがストレージやOpenShiftにも互換性があることを確認しておく必要がある もし、Red Hat OpenShiftを4.20にアップデートしたくとも、Dell CSMが1.15.1では(少なくとも本投稿時点)以下のように非対応であるため、Dell CSM側の対応を待つ必要があります。 当該のサイト閲覧時には掲載情報がアップデートされている可能性もあるため、 常に最新の公式サイトを確認するようお願い致します。 結論としては、アップデートを計画する場合は相互関与する要素を意識しつつ、計画的にアップデートの順番やアップデート先を検討する必要があるという点を押さえておきましょう。 補足 ちょっと上級者向けの情報 ストレージメーカーによっては、ストレージのマウント先となるサーバーやサーバーが持つストレージアダプター(FC HBAやiSCSI通信用のNICなど)に対しても厳格に製品型番やファームウェアやデバイスドライバーレベルに至るまで互換性があるバージョンを定義しているケースがあります。 この領域にまで至ってくると、Red Hat側ではなくサーバーやストレージメーカー側での情報提供になってきますので、そうした情報があるかどうかも調べてみると良いかもしれません。 あとがき 今回の記事は、Red Hat OpenShift Virtualizationに対応しているストレージについて調べるというものでしたが、Red Hatの情報とストレージメーカーの情報を同程度参照する必要があるということを解説させていただきました。 私の経験値上Dell Technologiesの機材をベースに紹介致しましたが、皆様におかれましてもご自身が運用管理されているメーカーのサイトを是非確認をすることをお勧めいたします。必要に応じてメーカーへのお問い合わせもしてみましょう。 以上の内容をもち、本記事の締めとしたいと思います。 この記事の内容が、少しでもRed Hat OpenShift Virtualizationを活用した組織づくりのお役に立てば幸いです。
Apache Camel Advent Calendar 13日目の記事は、サポート担当古市が担当します。 テーマは、「Camel routeTemplate」 サポートでたまにあるお問い合わせには、「コード量が多くどの箇所が問題になっているかわからない。とりあえずソースコードは全て添付する。」といったものです。 開発と運用が分かれているからといった理由の他には、見本となる camel routeを複製することで業務対応しているケースもありました。(数百もの camel ルートがほぼ複製されたものといったケースも) 厳密にはリファクタリングを通して2つか 3つの camelルートへ集約することが可能であったりしますが、開発コストや技術者の育成/引き継ぎを懸念されていらっしゃるのかもしれません。 そんな現場の皆さんへぜひ知っておいてほしいのが、camel routeのテンプレート機能となる、「camel routeTemplate」です。 camel.apache.org その名の通り、雛形の camelルートを作成し、利用者は虫食いとなっているパラメーターに必要な値を設定しながらルートを複製するだけです。 これならば、雛形の camelルートを修正したい場合、修正箇所は1つとなります。(camelルートを全てコピーされていた場合、修正箇所はコピーした分だけ膨れ上がります。もちろん、コピーした camelルートをどのように修正したかそれぞれ確認する必要もあるため、メンテナンスコストは膨大に膨れ上がります。) コミュニティードキュメントを一通り読んで試していただきたいのですが、ここでは基本となるサンプルを試してみます。 こちらが、雛形の camel ルートと、雛形を使った2つの camel ルートです。 @Override public void configure() throws Exception { routeTemplate("myTemplate") .templateParameter("name") .templateParameter("greeting") .templateParameter("myRepeatCount", "1") .from("timer:{{name}}?repeatCount={{myRepeatCount}}") .setBody(simple("{{greeting}} ${body}")) .log("${body}"); templatedRoute("myTemplate") .routeId("route1") .parameter("name", "one") .parameter("greeting", "Hello"); templatedRoute("myTemplate") .routeId("route2") .parameter("name", "two") .parameter("greeting", "Bonjour") .parameter("myRepeatCount", "2"); } もちろん、雛形の camelルートを定義したクラスと、雛形を使った camelルートを定義するクラスを分けることも可能です。分けたサンプルはこちらにあります。 https://github.com/apache/camel-examples/tree/main/routetemplate/src/main/java/org/apache/camel/example camel-timer consumer の repeatCount オプションにはデフォルト値(1)も設定してあるため、雛形を使ったクラスがパラメーターを指定しなかった場合、デフォルト値が使われます。 実行結果はこのようになります。 2025-09-24 16:39:53.907 INFO 43715 --- [ main] e.camel.impl.engine.AbstractCamelContext : Apache Camel 4.14.0 (ObserveRouteTemplate) is starting 2025-09-24 16:39:53.951 INFO 43715 --- [ main] org.apache.camel.main.BaseMainSupport : Property-placeholders summary 2025-09-24 16:39:53.951 INFO 43715 --- [ main] org.apache.camel.main.BaseMainSupport : [LocalProperties] name = two 2025-09-24 16:39:53.951 INFO 43715 --- [ main] org.apache.camel.main.BaseMainSupport : [LocalProperties] myRepeatCount = 2 2025-09-24 16:39:53.951 INFO 43715 --- [ main] org.apache.camel.main.BaseMainSupport : [LocalProperties] greeting = Bonjour 2025-09-24 16:39:54.005 INFO 43715 --- [ main] e.camel.impl.engine.AbstractCamelContext : Routes startup (total:2 templates:2) 2025-09-24 16:39:54.005 INFO 43715 --- [ main] e.camel.impl.engine.AbstractCamelContext : Started route1 (timer://one) 2025-09-24 16:39:54.005 INFO 43715 --- [ main] e.camel.impl.engine.AbstractCamelContext : Started route2 (timer://two) 2025-09-24 16:39:54.005 INFO 43715 --- [ main] e.camel.impl.engine.AbstractCamelContext : Apache Camel 4.14.0 (ObserveRouteTemplate) started in 98ms (build:0ms init:0ms start:98ms boot:595ms) 2025-09-24 16:39:54.959 INFO 43715 --- [5 - timer://one] route1 : Hello 2025-09-24 16:39:54.959 INFO 43715 --- [6 - timer://two] route2 : Bonjour 2025-09-24 16:39:55.954 INFO 43715 --- [6 - timer://two] route2 : Bonjour ルートIDも指定してある為、ログの視認性も良いです。 これならば、camel ルートを安全に複製していただき、メンテナンス性も飛躍的に向上するはずです。ぜひお試しください。 雛形を使うのにコーディングをしたくないといった場合には、application.properties を使い雛形を使ったルートを作成することもできます。 camel.route-template[route1].template-id=myTemplate camel.route-template[route1].name=one camel.route-template[route1].greeting=Hello camel.route-template[route2].template-id=myTemplate camel.route-template[route2].name=two camel.route-template[route2].greeting=Bonjour camel.route-template[route2].myRepeatCount=2 []の中には、routeId を指定しています。 クラスでは、雛形の定義しかしていません。 @Override public void configure() throws Exception { routeTemplate("myTemplate") .templateParameter("name") .templateParameter("greeting") .templateParameter("myRepeatCount", "1") .from("timer:{{name}}?repeatCount={{myRepeatCount}}") .setBody(simple("{{greeting}} ${body}")) .log("${body}"); 実行結果はこのようになります。 2025-09-24 14:35:28.616 INFO 33800 --- [ main] e.camel.impl.engine.AbstractCamelContext : Apache Camel 4.14.0 (ObserveRouteTemplate) is starting 2025-09-24 14:35:28.659 INFO 33800 --- [ main] org.apache.camel.main.BaseMainSupport : Property-placeholders summary 2025-09-24 14:35:28.660 INFO 33800 --- [ main] org.apache.camel.main.BaseMainSupport : [LocalProperties] name = two 2025-09-24 14:35:28.660 INFO 33800 --- [ main] org.apache.camel.main.BaseMainSupport : [LocalProperties] myRepeatCount = 2 2025-09-24 14:35:28.660 INFO 33800 --- [ main] org.apache.camel.main.BaseMainSupport : [LocalProperties] greeting = Bonjour 2025-09-24 14:35:28.712 INFO 33800 --- [ main] e.camel.impl.engine.AbstractCamelContext : Routes startup (total:2 templates:2) 2025-09-24 14:35:28.712 INFO 33800 --- [ main] e.camel.impl.engine.AbstractCamelContext : Started route1 (timer://one) 2025-09-24 14:35:28.712 INFO 33800 --- [ main] e.camel.impl.engine.AbstractCamelContext : Started route2 (timer://two) 2025-09-24 14:35:28.712 INFO 33800 --- [ main] e.camel.impl.engine.AbstractCamelContext : Apache Camel 4.14.0 (ObserveRouteTemplate) started in 96ms (build:0ms init:0ms start:96ms boot:591ms) 2025-09-24 14:35:29.668 INFO 33800 --- [6 - timer://two] route2 : Bonjour 2025-09-24 14:35:29.668 INFO 33800 --- [5 - timer://one] route1 : Hello 2025-09-24 14:35:30.668 INFO 33800 --- [6 - timer://two] route2 : Bonjour camel-jbang コマンドで、2つの camel ルートが稼働していることも確認できます。 % camel get route PID NAME ID FROM REMOTE STATUS AGE COVER MSG/S TOTAL FAIL INFLIGHT MEAN MIN MAX LAST DELTA SINCE-LAST 33800 ObserveRouteTemplate route1 timer://one?repeatCount=1 Started 20s 2/2 0.00 1 0 0 3 3 3 3 +3 20s/20s/- 33800 ObserveRouteTemplate route2 timer://two?repeatCount=2 Started 20s 2/2 0.00 2 0 0 1 0 3 0 -3 19s/19s/- % 明日の advent calendar 14日目では、「kamelet」 についてご紹介します。 アドベントカレンダーの一覧はこちらです。 qiita.com
OpenShift Virtualization Advent Calender 2025 12日目の記事です。 OpenShift v4.20 (もしくはv4.19.14) 以降で、CNIプラグイン OVN-Kubernetes がBGP対応しました。OVN-KubernetesのBGPサポートでは、 デフォルトPodネットワークのデフォルトVRFからの経路広告 プライマリネットワークとして定義したCluster User Defined NetworkのデフォルトVRFからの経路広告 プライマリネットワークとして定義したCluster User Defined NetworkをVRFを切って経路広告 EgressIPの経路広告 ができます。外部から受け取った経路をカーネルのルーティングテーブルにインポートすることもできます。 以下の記事に、2.の詳細を書きました。Cluster User Defined Networkに接続するVMのIPアドレスに外から直接(NodePortやIngressを経由せずに)接続する内容となっております。 zenn.dev 1.〜4.のそれぞれを解説したスライドもありますので、もしよかったら見てみてください。 speakerdeck.com
こんにちは、Ansibleテクニカルサポートの八木澤(ひよこ大佐)です。この記事は、「 Ansible Advent Calendar 2025 」12日目の記事です。 qiita.com 前回は、「 AnsibleのWinRMとPSRPの違いをみてみよう 」と題して、WinRMとPSRPそれぞれのプロトコルの違いについて解説しました。ざっくり振り返ると、「WinRMは毎回プロセスを作るから重い」「PSRPはRunspace(実行空間)を使い回すから速い」 という話をしたと思います。今回は、それを踏まえて実際にPlaybookを実行した場合にどの程度差が発生するのか、実際の環境で検証していきましょう。 検証環境 今回は、Fedora 43にインストールしたansible-core 2.18.11から、KVMの仮想マシン上のWindows Server 2025へ接続を行います。仮想マシンへホストOSのFedoraから直接接続しますので、ネットワークによる遅延の影響は排除できる環境となります。 検証にあたって、以下のようなタスクをそれぞれWinRMおよびPSRPで実行します。 WinRMの弱点である「プロセス生成のオーバーヘッド」を浮き彫りにするため、以下の「軽い処理を大量に繰り返す」シナリオを用意しました。 win_ping 100回ノック: 処理自体は一瞬で終わるモジュールを100回ループ実行し、純粋な接続オーバーヘッドを比較します。 win_file 100フォルダ作成: 実際にファイルシステムを操作するタスクを100回ループ実行します。 以下が、検証に使用したPlaybookの全文です。 --- - name : WinRM vs PSRP Performance Benchmark hosts : all gather_facts : false vars : loop_count : 100 tasks : # ------------------------------------------------------- # 1. PSRPでの計測 # ------------------------------------------------------- - name : "[PSRP] Set connection to PSRP" set_fact : ansible_connection : psrp ansible_psrp_cert_validation : ignore - name : "[PSRP] Warmup (Establish Connection)" ansible.windows.win_ping : - name : "[PSRP] Benchmark: win_ping x {{ loop_count }}" ansible.windows.win_ping : loop : "{{ range(0, loop_count) | list }}" register : psrp_ping_result - name : "[PSRP] Benchmark: Create {{ loop_count }} directories" ansible.windows.win_file : path : "C: \\ Temp \\ benchmark_psrp_{{ item }}" state : directory loop : "{{ range(0, loop_count) | list }}" # クリーンアップ - name : "[PSRP] Cleanup directories" ansible.windows.win_file : path : "C: \\ Temp \\ benchmark_psrp_{{ item }}" state : absent loop : "{{ range(0, loop_count) | list }}" # ------------------------------------------------------- # 2. WinRMでの計測 # ------------------------------------------------------- - name : "[WinRM] Set connection to WinRM" set_fact : ansible_connection : winrm ansible_winrm_server_cert_validation : ignore - name : "[WinRM] Warmup (Establish Connection)" ansible.windows.win_ping : - name : "[WinRM] Benchmark: win_ping x {{ loop_count }}" ansible.windows.win_ping : loop : "{{ range(0, loop_count) | list }}" register : winrm_ping_result - name : "[WinRM] Benchmark: Create {{ loop_count }} directories" ansible.windows.win_file : path : "C: \\ Temp \\ benchmark_winrm_{{ item }}" state : directory loop : "{{ range(0, loop_count) | list }}" # クリーンアップ - name : "[WinRM] Cleanup directories" ansible.windows.win_file : path : "C: \\ Temp \\ benchmark_winrm_{{ item }}" state : absent loop : "{{ range(0, loop_count) | list }}" 検証結果 以下が、実際に実行した結果です。計測には profile_tasks コールバックプラグインを使用し、タスクの実行時間を集計しました。 =============================================================================== [WinRM] Benchmark: win_ping x 100 ----------------------------------------------------------- 205.36s [WinRM] Cleanup directories ----------------------------------------------------------------- 191.19s [WinRM] Benchmark: Create 100 directories --------------------------------------------------- 186.23s [PSRP] Benchmark: win_ping x 100 ------------------------------------------------------------- 21.17s [PSRP] Cleanup directories ------------------------------------------------------------------- 14.75s [PSRP] Benchmark: Create 100 directories ----------------------------------------------------- 13.38s [WinRM] Warmup (Establish Connection) --------------------------------------------------------- 2.42s [PSRP] Warmup (Establish Connection) ---------------------------------------------------------- 1.97s [PSRP] Set connection to PSRP ----------------------------------------------------------------- 0.01s [WinRM] Set connection to WinRM --------------------------------------------------------------- 0.01s これをグラフにまとめると、以下のようになります。 WinRM vs PSRP 驚くべきことに、約10倍の速度差が出ました。 WinRMでは1回win_pingを実行するのに毎回2秒かかっています。100回繰り返すと、それだけで3分以上の待ち時間が発生してしまいます。一方、PSRPはわずか20秒強で完了しました。 実際の作業に近いフォルダ作成タスクでは、さらに差が開き約14倍の高速化を記録しました。 WinRMが3分かかっている処理を、PSRPは13秒で終わらせています。 今回の検証は意図的に差が出やすい「短時間で完了するタスクを大量に実行する」という手法で行ったため想定以上の差が出ていますが、実際のタスクでも無視できない差になることは間違いありません。 まとめ 今回の検証結果から、WinRMの仕組み上発生する「1タスクごとのプロセス起動コスト」が、タスク数に比例して全体の実行時間に影響を与えていることが確認できました。 もちろん、WinRMはWindows管理の標準プロトコルであり、多くの環境で追加設定なしに利用できる高い信頼性と互換性を持っています。しかし、Ansibleのように「短期間にコマンド実行と切断を繰り返す」アーキテクチャにおいては、今回のようなパフォーマンスの差として現れることがあります。 もし、現在のPlaybook実行においてパフォーマンスの課題を感じている場合は、PSRPの利用も検討してみてください。 Happy Automation!
こんにちは、Technical Account Manager (TAM) の鈴村圭史です。 前回の記事 rheb.hatenablog.com ではローカル環境(Podman)での可観測性環境の構築方法を紹介しました。今回は、 OpenShift Container Platform (OCP) 上に JBoss EAP XP 6.0 と OpenTelemetry (OTEL) を連携させた可観測性基盤を構築する方法を解説します。 JBoss EAP XP 6.0 は JBoss EAP 8.1 をベースとしており、MicroProfile Telemetry 2.0 をサポートします。これにより、クラウドネイティブ環境でも標準技術でアプリケーション内部の状態を可視化できるようになります。 この記事では、 Helm Chart を使ったデプロイ手法 と、OTEL 連携時に発生しがちな 通信エラー(gRPC/HTTP 不整合問題)を防ぐ設定ポイント をわかりやすく整理して紹介します。 アーキテクチャ構成 今回の構成は以下のとおりです。 アプリケーション:JBoss EAP XP 6.0(S2I ビルド / Helm Chart デプロイ) サンプルアプリ:Quickstart opentelemetry-tracing 観測基盤:OpenTelemetry Collector(Deployment モード) 通信プロトコル:OTLP/HTTP(protobuf) 前提条件 oc と helm が利用可能であること 手順 1: プロジェクトの作成 まずは検証用のプロジェクトを作成します。 oc new-project xp6-demo 手順 2: OpenTelemetry Collector のデプロイ OTLP/HTTP でトレースデータを受信する Collector を Deployment としてデプロイします。 otel-collector.yaml apiVersion : opentelemetry.io/v1beta1 kind : OpenTelemetryCollector metadata : name : otel spec : mode : deployment config : receivers : otlp : protocols : http : endpoint : 0.0.0.0:4318 processors : batch : {} memory_limiter : check_interval : 1s limit_percentage : 50 spike_limit_percentage : 30 exporters : debug : verbosity : detailed service : pipelines : traces : receivers : [ otlp ] processors : [ memory_limiter, batch ] exporters : [ debug ] logs : receivers : [ otlp ] processors : [ memory_limiter, batch ] exporters : [ debug ] metrics : receivers : [ otlp ] processors : [ memory_limiter, batch ] exporters : [ debug ] ポイント endpoint: 0.0.0.0:4318 → HTTP/OTLP で受信するための設定 verbosity: detailed → Collector のログでトレース内容を確認するためのデバッグ設定 適用します。 oc apply -f otel-collector.yaml oc get service otel-collector 手順 3: Helm Chart 設定ファイルの作成(重要) JBoss EAP XP 6.0 をデプロイするための helm.yaml を作成し、OTEL の環境変数を定義します。 helm.yaml build : uri : https://github.com/jboss-developer/jboss-eap-quickstarts.git ref : xp-6.0.x contextDir : opentelemetry-tracing mode : s2i deploy : replicas : 1 env : - name : OTEL_TRACES_EXPORTER value : otlp - name : OTEL_EXPORTER_OTLP_ENDPOINT value : http://otel-collector:4318 - name : OTEL_EXPORTER_OTLP_PROTOCOL value : http/protobuf - name : OTEL_SERVICE_NAME value : eap-xp6-tracing-demo ここが特に重要! OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf XP 6.0 の OTEL エクスポーターはデフォルト gRPC。 Collector 側は HTTP。 手順 4: アプリケーションのデプロイ Helm Chart を使って XP 6.0 アプリケーションをデプロイします。 helm repo add jboss-eap https://jbossas.github.io/eap-charts/ helm install otel-demo -f helm.yaml jboss-eap/eap8 しばらくすると、otel-demo Pod が起動します。 # oc get pod NAME READY STATUS RESTARTS AGE otel-collector-69577ccd59-69bl4 1/1 Running 0 13m otel-demo-2-build 0/1 Completed 0 27h otel-demo-586f5d7fc4-dpfx2 1/1 Running 0 17m otel-demo-build-artifacts-1-build 0/1 Completed 0 27h 手順 5: 動作確認 1. アプリケーションへアクセスしトレースを生成 URL=$(oc get route otel-demo --template='{{ .spec.host }}') curl -k https://$URL/opentelemetry-tracing 2. Collector のログでトレースを確認 oc logs otel-collector-69577ccd59-69bl4 -f 以下のようなログが表示されれば成功です。 service.name: eap-xp6-tracing-demo url.path: /opentelemetry-tracing/ 2025-12-12T05:22:27.515Z info Traces {"resource": {"service.instance.id": "7cd11e01-c2c0-43b1-9edc-a3c1689664c1", "service.name": "otelcol", "service.version": "0.135.0"}, "otelcol.component.id": "debug", "otelcol.component.kind": "exporter", "otelcol.signal": "traces", "resource spans": 1, "spans": 1} 2025-12-12T05:22:27.515Z info ResourceTraces #0 service.name=eap-xp6-tracing-demo telemetry.sdk.language=java telemetry.sdk.name=opentelemetry telemetry.sdk.version=1.42.1.redhat-00001 ScopeTraces #0 io.smallrye.opentelemetry@2.9.2.redhat-00001 GET /opentelemetry-tracing/ d1d86a289c3ea92f9077d291889b218f 177921993954f6a6 http.response.status_code=200 url.scheme=https network.transport=tcp http.request.method=GET http.route=/opentelemetry-tracing/ server.address=otel-demo-xp6-demo.apps.test.lab.local client.address=172.16.0.1 user_agent.original=curl/8.12.1 url.path=/opentelemetry-tracing {"resource": {"service.instance.id": "7cd11e01-c2c0-43b1-9edc-a3c1689664c1", "service.name": "otelcol", "service.version": "0.135.0"}, "otelcol.component.id": "debug", "otelcol.component.kind": "exporter", "otelcol.signal": "traces"} まとめ 今回は、OpenShift 上で JBoss EAP XP 6.0 と OpenTelemetry を連携させる方法を紹介しました。 XP 6.0 の持つ最新の可観測性機能を、ぜひ OpenShift 環境でも活用してみてください。
こんにちは、Technical Account Manager (TAM) の鈴村圭史です。 2025年、エンタープライズ Java 開発の現場では 「可観測性(Observability)」 の重要性がこれまで以上に高まっています。 今回は、最新の Red Hat JBoss Enterprise Application Platform Expansion Pack 6.0(以下、JBoss EAP XP 6.0) を利用して、 OpenTelemetry(OTEL) による可視化環境を構築する方法をハンズオン形式で紹介します。 インフラ基盤のセットアップから、JBoss EAP XP 6.0 の構成、そしてアプリケーションのトレース/ログ/メトリクスの可視化まで、順に手を動かしながら確認していきます。 用語解説 JBoss EAP XP 6.0 とは? JBoss EAP XP(Expansion Pack) は、安定した JBoss EAP をベースに、最新の MicroProfile 仕様やクラウドネイティブ機能を提供する拡張パックです。 XP 6.0 は JBoss EAP 8.1 上で動作し、 MicroProfile 7.0 に準拠 しています。 これにより、 Jakarta EE 10 の安定性 と MicroProfile の先進機能 の両方を活用できます。 特に XP 6.0 では MicroProfile Telemetry 2.0 がサポートされ、OpenTelemetry との統合が大幅に強化されています。 OpenTelemetry (OTEL) とは? OpenTelemetry は、 トレース・メトリクス・ログ といったテレメトリデータを生成・収集し、外部にエクスポートするための標準フレームワークです。 従来の「監視(Monitoring)」が “壊れていないかを見る” ものであるのに対し、 可観測性(Observability)は “なぜそうなっているのか” を理解するためのものです。 マイクロサービスや分散システムのトラブルシューティングにおいて、今や欠かせない技術となっています。 検証環境の構成 今回は、ローカル環境で構築します。 基盤: Podman (専用ネットワーク otel-net) APサーバー: JBoss EAP 8.1 + XP 6.0 可視化基盤: OTEL Collector: テレメトリデータの収集と転送 Jaeger: トレース情報の表示(オプション) ステップ 1: インフラ基盤の構築 (Podman) まず、Jaeger と OTEL Collector を立ち上げます。 1. 専用ネットワークの作成 podman network create otel-net 2. Jaeger (All-in-One) の起動 podman run -d --name jaeger \ --network otel-net \ -e COLLECTOR_OTLP_ENABLED=true \ -p 16686:16686 \ docker.io/jaegertracing/all-in-one:latest 3. OTEL Collector 設定ファイル (otel-config.yaml) の作成 トレース、ログ、メトリクスのすべてに対応した設定ファイルを作成します。 receivers : otlp : protocols : grpc : endpoint : "0.0.0.0:4317" http : endpoint : "0.0.0.0:4318" prometheus : config : scrape_configs : - job_name : 'jboss-eap' scrape_interval : 10s static_configs : - targets : [ 'host.containers.internal:9990' ] exporters : debug : verbosity : detailed otlp : endpoint : "jaeger:4317" tls : insecure : true service : pipelines : traces : receivers : [ otlp ] exporters : [ debug, otlp ] logs : receivers : [ otlp ] exporters : [ debug ] metrics : receivers : [ prometheus ] exporters : [ debug ] 4. OTEL Collector の起動 podman run -d --name otel-collector \ --network otel-net \ -p 4317:4317 \ -p 4318:4318 \ --add-host host.containers.internal:host-gateway \ -v $(pwd)/otel-config.yaml:/etc/otelcol/config.yaml:Z \ docker.io/otel/opentelemetry-collector:latest ステップ 2: JBoss EAP XP 6 のインストールと設定 次に、JBoss EAP XP 6.0 をセットアップし、OpenTelemetry サブシステムを有効化します。 1. インストール JBoss EAP Installation Manager を使用して XP 6.0 プロファイルをインストールします。 弊社製品のダウンロードサイトから、EAP のInstallation Manager (もしくは、EAP 8.1 などの)の zip 版をダウンロードし展開します。 Installation Manager を使用すると、EAP と XP の依存モジュールが正しく配置されたディレクトリ構成を自動で作成できます。 eap-xp-6 ディレクトリに移動してから、EAP を起動します。 ./bin/jboss-eap-installation-manager.sh install --profile eap-xp-6.0 --dir eap-xp-6 cd eap-xp-6 ./bin/standalone.sh 2. OpenTelemetry サブシステムの設定 起動したターミナルとは別のターミナルを開いて、管理CLIを使用して追加の設定を行います。 ./bin/jboss-cli.sh --connect # 以下のコマンドを順に実行 /extension=org.wildfly.extension.opentelemetry:add /subsystem=opentelemetry:add( \ service-name=eap-xp6-server \ ) reload ステップ 3: アプリケーションの準備 XP 6 上で動作させるための簡単な JAX-RS アプリケーションを用意します。プロジェクト構成は次の通りです。 サンプルコード構成: jaxrs-test/ ├── pom.xml └── src └── main ├── java │   └── com │   └── example │   ├── HelloResource.java │   └── RestApplication.java ├── resources │   └── META-INF │   └── microprofile-config.properties └── webapp └── WEB-INF └── beans.xml サンプルのコードは、この記事の一番最後に記載します。 アプリケーションをコンパイルして、EAP にデプロイします。 mvn clean package cp target/jax-rs.war <PATHTO>/eap-xp-6/standalone/deployment/jax-rs.war ステップ 4: 実行と確認 1. XP 6 の起動 ./bin/standalone.sh -bmanagement 0.0.0.0 2. トラフィックの生成 デプロイしたアプリケーションにアクセスしてトレースデータを生成します。 curl http://localhost:8080/jax-rs/api/hello 3. 可視化の確認 Collector ログの確認: podman logs -f otel-collector を実行し、Trace, Log, Metrics のデータが流れてきていることを確認します。 Collector のログには、EAP から送られてきたトレースやログがリアルタイムで表示されます。アプリケーションのアクセスした結果として次のようなログが出力されます。 podman logs otel-collector -f 2025-12-12T03:37:34.203Z info Logs {"resource": {"service.instance.id": "e8273ca9-94e6-451b-b0f3-250a1cd1cc58", "service.name": "otelcol", "service.version": "0.141.0"}, "otelcol.component.id": "debug", "otelcol.component.kind": "exporter", "otelcol.signal": "logs", "resource logs": 1, "log records": 1} 2025-12-12T03:37:34.203Z info ResourceLog #0 Resource SchemaURL: Resource attributes: -> service.name: Str(eap-xp6-server) -> telemetry.sdk.language: Str(java) -> telemetry.sdk.name: Str(opentelemetry) -> telemetry.sdk.version: Str(1.42.1.redhat-00001) ScopeLogs #0 ScopeLogs SchemaURL: InstrumentationScope io.smallrye.opentelemetry LogRecord #0 ObservedTimestamp: 2025-12-12 03:37:34.006242847 +0000 UTC Timestamp: 2025-12-12 03:37:34.006012341 +0000 UTC SeverityText: INFO SeverityNumber: Info(9) Body: Str(--- JAX-RS HelloResource Accessed ---) Trace ID: 231c425c58a933394ba848b42b3d8f9b Span ID: 0ced270d06823f4e Flags: 1 {"resource": {"service.instance.id": "e8273ca9-94e6-451b-b0f3-250a1cd1cc58", "service.name": "otelcol", "service.version": "0.141.0"}, "otelcol.component.id": "debug", "otelcol.component.kind": "exporter", "otelcol.signal": "logs"} また、Jaeger UI での確認には、ブラウザで http://localhost:16686 にアクセスします。 Service で eap-xp6-server を選択して、trace を確認します。 まとめ 今回は、ローカル環境で JBoss EAP XP 6.0 を利用して、 OpenTelemetry(OTEL) を試してみました。 ぜひ試してみてください。 サンプルコード pom.xml <project xmlns = "http://maven.apache.org/POM/4.0.0" xmlns : xsi = "http://www.w3.org/2001/XMLSchema-instance" xsi : schemaLocation = "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" > <modelVersion> 4.0.0 </modelVersion> <groupId> com.example </groupId> <artifactId> jaxrs-test </artifactId> <version> 1.0.0-SNAPSHOT </version> <packaging> war </packaging> <properties> <maven . compiler . source> 17 </maven . compiler . source> <maven . compiler . target> 17 </maven . compiler . target> <failOnMissingWebXml> false </failOnMissingWebXml> <version . bom . expansion> 6.0.0.GA-redhat-00016 </version . bom . expansion> <version . bom . ee> 8.1.2.GA-redhat-00004 </version . bom . ee> </properties> <repositories> <repository> <id> redhat-ga-maven-repository </id> <name> Red Hat GA Maven Repository </name> <url> https://maven.repository.redhat.com/ga/ </url> <releases> <enabled> true </enabled> </releases> <snapshots> <enabled> false </enabled> </snapshots> </repository> <repository> <id> jboss-public-maven-repository </id> <name> JBoss Public Maven Repository </name> <url> https://repository.jboss.org/nexus/content/groups/public/ </url> <releases> <enabled> true </enabled> </releases> <snapshots> <enabled> false </enabled> </snapshots> </repository> </repositories> <pluginRepositories> <pluginRepository> <id> redhat-ga-maven-repository </id> <name> Red Hat GA Maven Repository </name> <url> https://maven.repository.redhat.com/ga/ </url> <releases> <enabled> true </enabled> </releases> <snapshots> <enabled> false </enabled> </snapshots> </pluginRepository> </pluginRepositories> <dependencyManagement> <dependencies> <dependency> <groupId> org.jboss.bom </groupId> <artifactId> jboss-eap-expansion </artifactId> <version> ${version.bom.expansion} </version> <type> pom </type> <scope> import </scope> </dependency> <dependency> <groupId> org.jboss.bom </groupId> <artifactId> jboss-eap-ee-with-tools </artifactId> <version> ${version.bom.ee} </version> <type> pom </type> <scope> import </scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId> jakarta.ws.rs </groupId> <artifactId> jakarta.ws.rs-api </artifactId> <scope> provided </scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId> org.apache.maven.plugins </groupId> <artifactId> maven-war-plugin </artifactId> <version> 3.4.0 </version> <configuration> <failOnMissingWebXml> false </failOnMissingWebXml> </configuration> </plugin> </plugins> </build> </project> HelloResource.java package com.example; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; @Path ( "/hello" ) public class HelloResource { @GET @Produces ( "text/plain" ) public String sayHello() { System.out.println( "--- JAX-RS HelloResource Accessed ---" ); return "Hello, OpenTelemetry via JAX-RS!" ; } } RestApplication.java package com.example; import jakarta.ws.rs.ApplicationPath; import jakarta.ws.rs.core.Application; @ApplicationPath ( "/api" ) public class RestApplication extends Application {} microprofile-config.properties otel.sdk.disabled=false otel.traces.exporter=otlp otel.exporter.otlp.protocol=http/protobuf otel.exporter.otlp.endpoint=http://localhost:4318 otel.service.name=my-jaxrs-app beans.xml <beans xmlns = "https://jakarta.ee/xml/ns/jakartaee" bean-discovery-mode = "all" ></beans>
Red Hat のソリューションアーキテクトの瀬戸です。 これは Jakarta EE / Java EE Advent Calendar 2025 の12日目の記事です。 この記事はQuarkusでのSpringアノテーションの使用を薦めるものではありません。適切にご使用ください。 と、最初に不穏な文章を書きましたが、Quarkusでは実はSpringのアノテーションや機能を一部、制限付きで使用する事ができます。 ただ、サポートされるものについてもSpringの全機能が使えるわけではありません。 例えばQuarkusのライブラリとしてquarkus-spring-diを追加する事で、SpringのDI系のアノテーションが有効になります。 <dependency> <groupId> io.quarkus </groupId> <artifactId> quarkus-spring-di </artifactId> </dependency> これを使う事で @Autowired などのSpringでおなじみのアノテーションがQuarkusでも使用できるようになります。 つまり、次のようなコードもQuarkus上で動作します。Springのコードに見えるでしょう? package org.acme.spring.di; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component public class GreeterBean { private final MessageProducer messageProducer; @Autowired @Qualifier ( "noopFunction" ) StringFunction noopStringFunction; @Autowired @Qualifier ( "capitalizeFunction" ) StringFunction capitalizerStringFunction; @Value ( "${greeting.suffix:!}" ) String suffix; public GreeterBean(MessageProducer messageProducer) { this .messageProducer = messageProducer; } public String greet(String name) { final String initialValue = messageProducer.getPrefix() + " " + name + suffix; return noopStringFunction.andThen(capitalizerStringFunction).apply(initialValue); } } しかし、内部でSpringのDIコンテナが動くわけではなく、QuarkusのCDI上で動きます。SpringもQuarkusもDIコンテナではあるので、大きな問題になることはないとは少ないと思いますが、 ApplicationContext や BeanPostProcessor などは動作せず、Spring AOPについても動作しないため、すべてにおいて完全な互換性が必要な場合には不適なものとなっています。 また、実行時DIではなく、ビルド時DIとなるので、実行時にDIを使っていろいろなことをやってる場合も多くの場合に動作しません。 実際にquarkus-spring-diの依存関係を確認したところ、QuarkusのDI実装であるarcにのみ依存が張られており、Spring本体への依存はありませんでした。 >mvn dependency:tree [INFO] Scanning for projects... [INFO] [INFO] -------------------< org.acme:spring-di-quickstart >-------------------- [INFO] Building spring-di-quickstart 1.0.0-SNAPSHOT [INFO] from pom.xml [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- dependency:3.7.0:tree (default-cli) @ spring-di-quickstart --- [INFO] org.acme:spring-di-quickstart:jar:1.0.0-SNAPSHOT [INFO] +- io.quarkus:quarkus-spring-di:jar:3.30.2:compile [INFO] | +- io.quarkus:quarkus-spring-core-api:jar:6.2.SP1:compile [INFO] | +- io.quarkus:quarkus-spring-context-api:jar:6.2.SP1:compile [INFO] | +- io.quarkus:quarkus-spring-beans-api:jar:6.2.SP1:compile [INFO] | \- io.quarkus:quarkus-arc:jar:3.30.2:compile [INFO] | +- io.quarkus.arc:arc:jar:3.30.2:compile [INFO] | | +- jakarta.transaction:jakarta.transaction-api:jar:2.0.1:compile [INFO] | | \- io.smallrye.reactive:mutiny:jar:3.0.3:compile [INFO] | | \- org.jctools:jctools-core:jar:4.0.5:compile [INFO] | \- org.eclipse.microprofile.context-propagation:microprofile-context-propagation-api:jar:1.3:compile (以下省略) SpringでのAOP的な何かを追加したい場合はCDIの仕組みを使う必要がありますのでご注意ください。 後ろ側で共通機能とかを作る人はCDIの拡張ポイントでがんばれ。 また、この記事ではspring-diの説明をしましたが、Spring Web、Data JPAなど、いくつかのアノテーションや機能の一部を使用することができます。興味がある方は次のURLからご確認ください。。 ja.quarkus.io
Apache Camel Advent Calendar 12日目の記事は、サポート担当古市が担当します。 テーマは、「Camel Split/Aggregate」 既に使われている方もいらっしゃるかと思いますが、しっかりと理解して使うとパフォーマンスの向上が見込める EIP です。 Split はその名の通り、メッセージを分割して後段へ流すコンポーネントです。 camel.apache.org 気を付けるべきポイントはこちら。 分割する際、メッセージボディーを全てメモリーへ読み込むか。ストリーミングを使い逐次読み込み処理するか。 分割したメッセージを並列で処理するかどうか。 分割する際、メッセージボディーを全てメモリーへ読み込むか。ストリーミングを使い逐次読み込み処理するか。 たまにGB単位のテキストメッセージを分割処理されたいといったお問い合わせがあります。同時に何件処理するか状況にもよりますが、ストリーミング処理をした方が少ないメモリーフットプリントで対応できます。 .split().tokenize("\n", 1000).streaming() 分割したメッセージを並列で処理するかどうか。 オリジナルメッセージの先頭から順序を担保し逐次処理をされたい場合、直列で作業する必要があります。逆に、順序を気にせず高速に処理したい場合、並列処理をご検討ください。(マシンリソースのサイジングもお忘れなく。) .split(body()).parallelProcessing() Camel 4.11 からは、ちょっとした変更も発生しています。 camel.apache.org RECIPIENT LIST, SPLIT AND MULTICAST EIP In parallel processing mode, you can also enable synchronous=true to force these EIPs to process the sub-tasks using the upper bounds of the thread-pool. If using synchronous=false then Camel will allow its reactive routing engine to use as many threads as possible, which may be available due to sub-tasks using other thread-pools such as CompletableFuture.runAsync or others. Setting synchronous=true is the same behavior as in Camel 2 which did not have the reactive routing engine. 百聞は一見に如かず。早速、3パターン試してみましょう。(コミュニティー版 camel 4.14.0を使用) 以下のコードをベースとし、split のオプションを変更しつつ出力結果に注目してみます。 from("timer:java?repeatCount=1") .setBody() .simple("Hello\nHello\nHello") .split(body().tokenize("\n")) .to("direct:input") .end(); from("direct:input") .log("${body}") .process(exchange -> { System.out.println(Thread.currentThread().getName()); }); パターン1: 並列処理設定なし Camel (camel-1) thread #5 - timer://java 2025-09-12 16:19:16.759 INFO 16493 --- [ - timer://java] ObserveSplit.java:18 : Hello Camel (camel-1) thread #5 - timer://java 2025-09-12 16:19:16.760 INFO 16493 --- [ - timer://java] ObserveSplit.java:18 : Hello Camel (camel-1) thread #5 - timer://java 2025-09-12 16:19:16.760 INFO 16493 --- [ - timer://java] ObserveSplit.java:18 : Hello パターン2: .parallelProcessing().synchronous(true) Camel (camel-1) thread #11 - Split Camel (camel-1) thread #9 - Split Camel (camel-1) thread #7 - Split 2025-09-12 16:14:12.736 INFO 15946 --- [ead #11 - Split] ObserveSplit.java:18 : Hello 2025-09-12 16:14:12.736 INFO 15946 --- [read #9 - Split] ObserveSplit.java:18 : Hello 2025-09-12 16:14:12.736 INFO 15946 --- [read #7 - Split] ObserveSplit.java:18 : Hello パターン3: .parallelProcessing().synchronous(false) Camel (camel-1) thread #11 - Split Camel (camel-1) thread #7 - Split Camel (camel-1) thread #9 - Split 2025-09-12 16:16:56.809 INFO 16239 --- [ead #11 - Split] ObserveSplit.java:18 : Hello 2025-09-12 16:16:56.809 INFO 16239 --- [read #7 - Split] ObserveSplit.java:18 : Hello 2025-09-12 16:16:56.809 INFO 16239 --- [read #9 - Split] ObserveSplit.java:18 : Hello 並列処理を行う際、スレッドプールを使った処理にするか、reactive処理にするかの判断が必要になります。それぞれのパターンで想定する最大の負荷をかけ、リソース逼迫状況やパフォーマンスをもとに判断されると良いでしょう。 分割処理後、処理結果をどう集約するかも注意すべきポイントです。 camel.apache.org 豊富なオプションと集約戦略(aggregation strategy)が提供されており、もちろん aggregate()メソッドを使い独自のマージ処理を実装可能です。(サンプルなどは、上記コミュニティーページをご確認ください。) aggregate()メソッドはあくまでもメッセージの集約ですので、不必要に重い処理やメソッドの目的以外のビジネスロジックを組み込まないようご注意ください。 明日の advent calendar 13日目では、「Camel routeTemplate」 についてご紹介します。 アドベントカレンダーの一覧はこちらです。 qiita.com
※本記事は OpenShift Virtualization アドベントカレンダーの 11 日目の記事です。 qiita.com 皆さんこんにちは、OpenShift Virtualization とストレージを生業にしている Red Hat のうつぼ(宇都宮)です。 実は私けん玉を趣味にしておりまして、休日には息子と一緒に公園でずっとけん玉してたり、イベントに行ったりしています。先月開催された KWC(Kendama World Cup)も、現地には行けなかったんですがずっと YouTube Live で観てました。 今年の KWC は昨年に続いて同郷の川本凌雅選手の二連覇になりました。別のけん玉イベントで無理矢理川本選手にサインしてもらった1本のけん玉は私の宝物です。 大事に保管しておきたい気持ちもありますが、やっぱけん玉は振ってなんぼです。だから宝物でもバリバリ使ってます。落として傷も入ってますし、汚れも剥がれもしてます。 ですがけん玉の傷はそれだけ振ったという勲章なので、ボロッボロまで使い込んだけん玉を川本選手に持っていって、また新しいけん玉にサインしてもらおうと思います。 大皿に書いてもらった Ryoga 選手のサイン さて、使っていれば壊れていくのはけん玉もマシンも一緒。2つあれば1つ壊れても安心ですが、1つしかないと心もとない。"Two is one and one is none." ほんとに金言ですね。 ということで今日のテーマは High Availability です。 OpenShift 上の Pod と VM の HA の違い VM の HA を実現するコンポーネント 1. Node Health Check (NHC) Operator 2. Remediation Operator どっちを使うとよいのよ? NHC と Remediation Operator の連携フロー インストールとデプロイ まとめ OpenShift Virtualization(以下 Virt)に限らず、全ての仮想化基盤において「ホストに障害が起きても、VM が自動で健全な別のホストで再起動する」仕組み、つまり High Availability (HA) の実装は超クリティカルなポイントですね。 本稿では、OpenShift 環境での HA の基本的な考え方から、Virt で VM の HA をどう実現するか、その具体的なコンポーネントである Node Health Check (NHC) Operator と Remediation Operator について解説します。 OpenShift 上の Pod と VM の HA の違い OpenShift は、コンテナのアプリケーション(Pod)の HA は Kubernetes の自己修復機能を適用します。 つまり、Compute ノードがダウンしても、OpenShift のコントローラーが自動的に別の健全なノードに Pod を作り直してサービスを再開する仕組みを標準で持っています。 ところが、 Virt の VM はこの標準の HA の仕組みではカバーできません。 Virt では、実行中の VM は VirtualMachineInstance (VMI) というカスタムリソースで宣言され、この VMI が virt-launcher Pod を呼び出して稼働します。 参考 : OpenShift Virtualization ( Kubevirt ) でVM管理もCloud Nativeに (1) - 赤帽エンジニアブログ 「じゃあ Compute ノードが落ちたらこの virt-launcher Pod が別のノードに移って、それでいいんじゃないの?」と思うかも知れませんが、残念。そうはなりません。実際は、落ちたノードでもう一度 Pod を上げようと試みます。 その理由は virt-launcher Pod は VMI で管理されているからです。VMI が Pod が稼働するノードの情報を持っており、同じノードで再び上げようとするのです。 ゆえに、VM を別のノードで起動するには、 VMI 自身を削除して再作成する ことが必要になります。しかし OpenShift は標準では自動で VMI を再作成してくれません。 だから、Compute ノードが落ちても VM が自動で別のノードに上がってくるという機能を実現するには、OpenShift の標準機能とは別に専用の仕組みが必要になるわけです。 VM を failover させるには VM の HA を実現するコンポーネント Virt で VM の HA を実現するためは以下の2つがキーとなります。 Node Health Check (NHC) Operator Remediation Operator この2つの Operator が連携することで、ノード障害の検知から VM の自動再起動までの一連の流れが完成します。 1. Node Health Check (NHC) Operator NHC Operator は、名前の通り Compute ノードが元気かどうかをずーっと監視 するものです。 ノードが事前に決めた健全性の基準(ヘルスチェック)を満たしているかをしげしげとチェックし、例えばノードが一定時間「NotReady」のステータスを続けたら障害(Unhealthy)だと判定する、のような動きをします。 そして、NHC が Unhealthy なノードを見つけると、そのノードを修復するために Remediation Operator を呼び出します。 2. Remediation Operator Remediation Operator は、NHC から障害の通知を受け取ったノードに対して、 修復措置(Remediation)を実行 するものです。 具体的には障害ノードを再起動することでクラスタから一時的に切り離し、そのノードで動いていた VM が健全なノードで再起動するように VMI を再作成します。 Remediation Operator には、よく使われる2種類があります。 Operator 名 特徴 Self Node Remediation (SNR) Operator 障害ノード自身に自己修復(再起動など)を試みさせる。設定はシンプルだけど、深刻な障害だと対処できないことも。 Fence Agents Remediation (FAR) Operator 別のノードから外部の電源管理機能(IPMI / Redfish など)を使って、障害ノードを強制的にシャットダウンする。確実に切り離せるが外部設定が必要。 2段構えで実装 どっちを使うとよいのよ? SNR と FAR はどっちを使うかは環境や要件次第で選べますし、両方組み合わせて使うことも可能です。 といっても、どっちを使うとよいのよ?と思われるかたもいると思います。 Self Node Remediation (SNR) Operator Fence Agents Remediation (FAR) Operator 動作原理 障害ノード 自身 が再起動などの自己修復を試みる。 クラスタ内の 別のノード から外部の電源管理を使って強制的に電源を切る/入れ直す。 メリット 外部の機器設定が不要で、構成がシンプル。 ノードが完全にフリーズしても、外部から 確実に電源を落として切り離せる (データ破損防止)。 デメリット 完全なハングアップなど、深刻な障害には対応できない可能性がある。 物理的な電源管理インターフェースの設定とアクセス設定が必要で、構成が少し複雑。 Remediation 速度 比較的遅い 。ノードOS内での処理が必要なため、ノードの状態によって処理開始に時間がかかる。 比較的速い 。外部インターフェース経由で直接電源操作を行うため、確実かつ迅速にノードを切り離せる。 どれだけ VM のダウンタイムを短くできるか?という点に興味が寄せられることが多いですが、一般的に FAR (Fence Agents Remediation) の方が SNR (Self Node Remediation) よりも迅速に Remediation を完了させられる 傾向があります。 FAR は、健全なノードから外部の電源管理インターフェースに対して直接コマンドを送り、障害ノードの電源を強制的に切断します。これは OS がどんな状態であろうともマシンをハードリセットするので少々危険ですが、非常に速くかつ確実にノードをクラスタから切り離せます。 一方で SNR は、障害ノード自身が watchdog を使って OS レベルで再起動などの処理を行うため、ノードの状態によっては処理開始までに時間がかかったり、深刻な障害ではそもそも処理が実行できなかったりする可能性があります。ただ、ソフトリセットなので graceful で安全です。 なるべく復旧時間を短くしたい場合は FAR 、時間より OS のデータインテグリティを重視する場合は SNR という方針がよいと思います。 NHC と Remediation Operator の連携フロー VM の HA が実現するまでの流れは、こんな感じです。 ノード障害が発生: Compute ノード A がダウンして「NotReady」になる。 NHC が障害を検出: NHC Operator が、ノード A がヘルスチェックの基準を満たさないことを検知し、"Unhealthy" とみなす。 Remediation Operator を呼び出し: NHC Operator が、設定されている Remediation Operator(SNR または FAR)を起動する。 ノードを修復(切り離し): SNR の場合: ノード A が自己再起動を試みる。 FAR の場合: 外部インターフェース経由でノード A を再起動。 VM が自動再起動: ノード A がクラスタから完全に切り離されると、そのノードで動いていた VMI は「ホストを失った」状態とみなされます。Virt はこれを検知して、 健全な Compute ノード B で自動的に VMI を作り直して、VM を起動 する。 この連携のおかげで、ノード障害から VM の自動復旧までが実現し、VM の HA が確保されるというわけです。 インストールとデプロイ NHC Operator、SNR Operator、FAR Operator は、いずれも OperatorHub から簡単にインストールできます。 デフォルトでは、これらの Operator は openshift-workload-availability という専用の Namespace に展開されます。 インストールしたら、あとはノードのヘルスチェックの条件や、どの Remediation Operator を使うかといった詳細設定をカスタムリソースとして適用すれば、HA 機能が使えるようになります。 一例として、NHC と FAR を使う場合は次のような YAML を作成します。 apiVersion: remediation.medik8s.io/v1alpha1 kind: NodeHealthCheck metadata: name: nhc-worker-vms namespace: openshift-workload-availability spec: selector: matchLabels: node-role.kubernetes.io/worker: ""    # 監視対象のノードラベル unhealthyConditions:    # Unhealthy とみなす条件。Ready 状態が "Unknown" or "False" = NotReady が 30s 続くと Unhealthy - type: Ready status: "Unknown" timeout: 30s - type: Ready status: "False" timeout: 30s minHealthy: 51%    # クラスタノードの過半数が Healthy でなければ remediation は実行しない maxUnhealthy: 1    # 一時点で Unhealthy とみなすノード数は1つまで remediationTemplate:    # 呼び出す FAR のテンプレート apiVersion: fence-agents-remediation.medik8s.io/v1alpha1 kind: FenceAgentsRemediationTemplate name: fence-ipmilan-template apiVersion: fence-agents-remediation.medik8s.io/v1alpha1 kind: FenceAgentsRemediationTemplate metadata: name: fence-ipmilan-template namespace: openshift-workload-availability spec: template: spec: agent: fence_ipmilan remediationStrategy: OutOfServiceTaint nodeparameters:    # 対象ノードごとの別々のパラメータ --ip: worker-01: '192.168.10.11' worker-02: '192.168.10.12' worker-03: '192.168.10.13' sharedParameters:    # 対象ノードで共有のパラメータ retryCount: 2 retryInterval: 10 timeout: 15 login: "<bmc-user>" passwd: "<bmc-password>" NHC の spec.unhealthyConditions で timeout を短くすればするほど、VM は速く再起動します。 ただしあまりに極端に短くすると、ちょっとしたネットワークの遅延でノードが再起動されてしまうリスクもあるため、注意して設定しましょう。 まとめ Virt で Compute ノード障害時に VM の HA を確保するには、NHC と SNR / FAR のような Remediation Operator を導入して連携させることで実現できます。 OpenShift は Kubernetes だから放っておけば復活するんでしょ、と思っている人はいると思います。私もはじめはそうでした。 これから機能検証や PoC を始められるかたは、ぜひこれらの Operator をちゃんと設定すれば、VMware vSphere HA と同じくらい信頼できる VM の可用性を Virt で実現できると思っていただけると幸いです。 ということで今日はここまで。
Apache Camel Advent Calendar 11日目の記事は、サポート担当古市が担当します。 テーマは、「Camel SEDA エンドポイント」 Camel 3.x から、従来のルーティングエンジンに加え、リアクティブエンジンが追加されました。 リアクティブに処理をするのであれば、敢えて asynchronous な処理を提供する sedaエンドポイントを使用するメリットがあるのか、改めて振り返ってみましょう。 camel.apache.org まずは、direct エンドポイントを使ったこちらのルート。 @Override public void configure() throws Exception { from("timer:java?repeatCount=1") .routeId("timer") .setBody() .simple("Hello Camel from ${routeId}") .to("direct:input") .log("${body}"); from("direct:input") .routeId("input") .log("Sleep for 3sec") .process(e -> { try { Thread.sleep(3000); } catch (InterruptedException ex) { throw new IOException(ex); } }) .log("Woke up") .setBody() .simple("Bye Camel from ${routeId}"); } 実行結果はこのようになります。 2025-09-11 08:59:52.158 INFO 8365 --- [ - timer://java] ObserveThreading.java:18 : Sleep for 3sec 2025-09-11 08:59:55.159 INFO 8365 --- [ - timer://java] ObserveThreading.java:26 : Woke up 2025-09-11 08:59:55.162 INFO 8365 --- [ - timer://java] ObserveThreading.java:14 : Bye Camel from input ポイントは、Camel ルーティングエンジンによる個々のコンポーネント実行がリアクティブであっても、実行順序はリニアです。(request/reply) 次に directエンドポイントを sedaエンドポイントに置き換えて実行します。 @Override public void configure() throws Exception { from("timer:java?repeatCount=1") .routeId("timer") .setBody() .simple("Hello Camel from ${routeId}") .to("seda:input") .log("${body}"); from("seda:input") .routeId("input") .log("Sleep for 3sec") .process(e -> { try { Thread.sleep(3000); } catch (InterruptedException ex) { throw new IOException(ex); } }) .log("Woke up") .setBody() .simple("Bye Camel from ${routeId}"); } } 実行結果はこちら。 2025-09-11 08:57:23.490 INFO 7972 --- [ - timer://java] ObserveThreading.java:14 : Hello Camel from timer 2025-09-11 08:57:23.490 INFO 7972 --- [ - seda://input] ObserveThreading.java:18 : Sleep for 3sec 2025-09-11 08:57:26.493 INFO 7972 --- [ - seda://input] ObserveThreading.java:26 : Woke up sedaエンドポイントから始まる "input"ルートの実行結果を待たずに、"timer"ルートの処理が終了しています。(fire/forget) つまり、"fire/forget"パターンのデザインを使うべきか、"request/reply"パターンのデザインを使うべきかを踏まえて、directエンドポイントもしくは sedaエンドポイントの使い分けをするのが良いでしょう。 sedaエンドポイント自体もオプションが豊富です。独自にスレッドプールを使いリクエストを処理していますので、キューサイズや同時処理数など、ご利用される際はスレッドプールのチューニングもお忘れなく。 camel.apache.org スレッド調整に興味を持たれた方は、こちらの記事もご確認ください。 camel.apache.org 明日の advent calendar 12日目では、「Camel Split/Aggrergate」 についてご紹介します。 アドベントカレンダーの一覧はこちらです。 qiita.com
こんにちは。レッドハットコンサルティングチームの木嶋です。 「アプリケーションモダナイゼーション(以下、モダナイゼーション)」について解説する連載第8回目の今回からは、6Rのうちのリファクター (refactor) に焦点を当てます。 リファクターは『既存の振る舞いを保ちながら、内部構造を改善する』行為です。これ自体はユーザーに目新しいものはなにも生み出さないので、一見ただの「作り直し」に見えてしまいます。 実はこの内部構造の改善は、最終的に開発者の時間の使われ方や組織全体のコスト構造を大きく変えます。見た目以上に「経済の話」なんです。 6Rとは 本シリーズ1回目の記事 をご参照ください。 シリーズ1回目では、リファクターを以下のように説明しています。 アプリケーションのコードやアーキテクチャを抜本的に見直し、クラウドネイティブ化やマイクロサービス化といった変化に対応するものです。中長期的な開発・運用の柔軟性を重視します。 クラウドネイティブとは、クラウドコンピューティングのメリットを最大限に活用し、アプリケーションを迅速かつ高頻度で構築・デプロイ・運用するための設計およびアプローチのコンセプトです。 一般的にコンテナベースで実装されたマイクロサービスアーキテクチャという設計パターンで実装されます。 本記事では、このシリーズ定義の「リファクター」を開発者(Dev)視点で見直し、なぜ今リファクタリングが効果を持つのか、その根拠を整理していきます。 はじめに DVSに時間がかかる理由 プログラミングではない作業 既存実装の調査 トラブル対応 プログラミングの作業 緊急のバグ修正 新しい機能のために「過去の構造を修正する」作業 良い設計の経済学 リファクタリングがDevにもたらす具体的なメリット 変更すべき場所をすぐに特定できる バグ調査・トラブルシュートが短時間で済む 新機能に着手するまでの「助走距離」が短くなる レビューやペアプロがやりやすくなる 新メンバーの立ち上がりが早くなる まとめ Red Hat コンサルティングのご紹介:カスタム・ソリューションの構築をエキスパートがサポート  はじめに さまざまな組織が市場での競争力を獲得するため、顧客への価値提供をより早く、より確実に実施したいと考えています。 たとえば住宅ローンなどの金融商品では、顧客がローンを必要として相談し、申し込み、銀行側で内容を確認・審査し、承認されてはじめて、顧客は必要な資金を受け取ることができます。 このように、顧客が「必要だ」と思った瞬間から価値を受け取るまでの一連の流れをValue Stream (価値の流れ) と呼びます。 当然ながら、顧客にとってこの Value Stream は短ければ短いほど嬉しいものです。 しかし実際には、Value Stream に含まれる業務の側面を見ると、手作業や部門間の引き継ぎ、多数のExcelファイルや調整作業といった、さまざまなムダやボトルネックが潜んでいます。 これらをデジタルの力で効率化することを、一般に「デジタルトランスフォーメーション (DX)」と呼びます。 では次に、この DX を支える “裏側” に焦点を当ててみましょう。 業務の効率化を実現するには、その仕組みを実際に作り上げる人たちが必要です。つまり、開発者です。開発者は、業務側が必要とする仕組みを設計し、開発し、テストし、デプロイし、業務の現場に届けます。 実はここにも、もう一つの Value Stream が存在します。 業務の要件定義から設計、実装、テスト、デリバリーまでの流れ。これがもう一つのValue Streamです。 この2種類の Value Stream には、次のような関係があります。 Operational Value Stream (OVS): 実際の業務が顧客に価値を届ける流れ Development Value Stream (DVS): 業務が必要とするデジタル機能を開発して届ける流れ この関係性から、次のように言うことができます。 Operational Value Stream の改善スピードは、Development Value Stream の軽さ・速さによって決まる。 DVSに時間がかかる理由 しかし現場に目を向けると、この Development Value Stream の1サイクルは決して軽くありません。 小さな改修でも1〜2週間、大きな機能になると数ヶ月、基幹系の入れ替えとなれば3年、5年というプロジェクトも珍しくありません。 このサイクルタイムが長くなるほど、OVS 側で「こう変えたい」というアイデアを試せる頻度は下がり、市場の変化に合わせて学習するスピードも落ちていきます。 DVSが遅い顧客向けシステムでは、より俊敏な競合サービスへ顧客が流れてしまうきっかけにもなります。 社内業務システムでは、待ちきれない現場が Excel や Access、VBA、RPA といった “影のIT(シャドーIT)” を増殖させてしまうこともあります。 ではなぜDVSが遅くなってしまうのでしょうか。 さまざまな組織で調査をさせていただくと、開発工程で行っていることは、プログラムを書くことだけではないことが浮かび上がっています。 例えばとあるプロジェクトは、「開発」工程の時間の使い方を分析した結果、プログラムを書くことに実際に使える時間は、割り当てられている時間のおおよそ1/3程度であることがわかりました。 さらにプログラミングをしている時間の中で、本当に欲しい新機能のプログラミングに割り当てられる時間は、さらに1/3です。 DVSの時間配分 では他の時間は何をしているのでしょうか。分析してみると、開発者が日々行なっている作業は大きく次のように分類されます。 プログラミングではない作業 既存実装の調査 必要な機能に対して、どこにどう手を加えれば良いかを判断するために、関連するコードや設定、データフローなどを読み解く作業です。 トラブル対応 「動かしたら別のところが壊れた」「昨日まで動いていた処理が動かない」といった現象を追いかけ、原因を探し、対処する作業です。 プログラミングの作業 緊急のバグ修正 障害時にはOps (運用) が復旧後、暫定対応などの「パッチ」を開発することがあります。これは全てに優先して実施されるため、新しい機能の開発中にも対応が必要です。 新しい機能のために「過去の構造を修正する」作業 本当は新しい機能を作りたいのですが、その前に「過去の実装で手を入れないと進めない部分」を修正する必要がある場合があります。 こうした作業はどのアプリケーションでも一定は発生しますが、いわゆる「スパゲッティコード」状態であればあるほど頻繁に発生したり、頻度も工数も増大する傾向があります。 そして、この “構造に起因する時間のロス” こそが、DVS(開発のバリューストリーム)を重くし、結果として OVS(業務側のバリューストリーム)のスピードをも左右してしまいます。 良い設計の経済学 では、こうした問題が発生するのは人為的ミスなのでしょうか。 実は、そうではありません。 問題の多くは、開発者個人の能力や努力ではなく、良い設計の基準そのものが時代と共に大きく変化したことに由来します。 かつては「正しい判断」だった設計方針が、今では開発速度を下げる構造になってしまっている。そうした“歴史的な前提の違い”が、現場の時間配分に歪みを生んでいるのです。 かつて「プログラミング」が広まってきた頃は、CPUやメモリー、ディスクスペースといったコンピューティングリソースが非常に高価でした。 総務省の情報通信白書 (2015年版) では、1985年ごろのディスクスペースは1GBあたり50000ドル程度だったことが記録されています。 GBあたりのストレージ価格 私自身も、約20年前に所属していた組織でTB単位のストレージを導入した際に、1,000万円を超える見積もりが出ていたことをよく覚えています。 当時の「良い設計」とは、限られたコンピューティングリソースでも効率よく動かせる設計でした。 そのため、アプリケーションが動きさえすれば十分であり、「より速く、より軽く動かす」ために、開発者がどれだけ時間を費やしても、それ自体がコストメリットのある投資でした。 これは、当時のアプリケーション規模が現在と比べて小さかったことにも支えられていました。 しかし現在は状況が大きく異なります。 複雑で巨大化したアプリケーションを維持・更新する上では、「人をあまり使わなくてすむ設計」こそが良い設計と言われています。その背景には、 コンピューティングリソースが劇的に安価になったこと 一方で、開発者の時間 (人的リソース) が相対的に高価になったこと というコスト構造の逆転があります。 この状況に適応するために、オブジェクト指向、 ドメイン駆動設計 (DDD) 、そしてその流れを継いだマイクロサービスアーキテクチャが普及していきました。 これらの手法が広く採用されているのは、コードを理解しやすくし、変更しやすくし、人的リソースの節約につながるという経済的合理性があるためです。 「DVSに時間がかかる理由」で触れた調査やバグ対応に追われる状況は、まさに当時の当たり前で設計されたシステムを、今の当たり前の規模とスピードで運用しようとした結果生まれた副産物です。 当時は妥当だった設計判断も、アプリの巨大化や人件費の高騰という世界的な変化を経て、維持するだけで高コストになってしまったのが現在のレガシーシステムと言えます。 ただし、すべてのシステムが今の当たり前に合わせて再設計されるべき、というわけではありません。 求められる更新頻度やビジネスの変化スピードによって、リファクタリングの必要性も、取り組むべきサイクルも異なります。 ここで重要なのが、OVS(業務の価値の流れ)とDVS(開発の価値の流れ)の関係です。 どれほど OVS にスピードが求められるかによって、DVS を軽くするための投資=リファクタリングの重要度が決まります。 市場変化の速い領域ほど OVS に求められる改善速度が高まり、その時間的レバレッジを支えるために、DVSへの構造的投資(リファクタ)は戦略的な必要投資になるのです。 リファクタリングがDevにもたらす具体的なメリット ここまで見てきたように、リファクタリングは「新機能を作る時間を少しでも増やすための構造投資」です。 では、その投資がうまくいったとき、開発者にはどのような変化が起きるのでしょうか。 代表的なものをいくつか挙げてみます。 変更すべき場所をすぐに特定できる 機能ごとに責務が分かれ、命名やレイヤ構造が整理されていると、「この変更はこのコンポーネント」と当たりが付けやすくなります。 結果として、既存コードを読み解く時間や、影響範囲調査に費やす時間が減ります。 バグ調査・トラブルシュートが短時間で済む 責務の境界や依存関係が明確になることで、「この不具合はどの層で起きていそうか」「どのコンポーネントまでは正常か」といった切り分けがしやすくなります。 夜間・週末の障害対応や、原因調査に追われる時間を減らすことができます。 新機能に着手するまでの「助走距離」が短くなる 既存の構造を理解し、壊さないように慎重にコードを読んでからでないと実装に入れない、という状況が減ります。 小さな変更を安全に積み重ねやすくなるため、短いサイクルでのリリースにも適した状態になります。 レビューやペアプロがやりやすくなる 設計意図や責務の分割がコードから読み取りやすくなると、レビューアが「何を見ればよいか」を判断しやすくなります。 特定の個人だけが理解している「暗黙知の領域」が減り、チームとして品質を支えやすくなります。 新メンバーの立ち上がりが早くなる ドメインごとにモジュールが分かれている、テストが揃っている、といった状態は、オンボーディングにも効いてきます。 新しく参加した開発者が、短期間で安全にコードを触れるようになることで、チーム全体の開発余力が増えます。 これらの効果を合計すると、「もともと開発に割り当てていた時間のうち、1/9 しかなかった“新しい価値を生むためのコーディング時間”」を少しずつ広げていくことができます。 リファクタリングは、単に「きれいなコードにする」ためではなく、この 1/9 をどれだけ広げられるかを意識した、Dev視点の経済的な投資と言えます。 まとめ 今回は、開発者視点からリファクタリングを“良い設計の経済学”の観点で捉え直し、設計の良し悪しが DVS(開発の価値の流れ)のスピードにどれほど影響するのかを見てきました。 ただし、リファクタリングの価値は開発だけに留まりません。 日本の大規模システムでは、むしろ DBコネクション枯渇・バッチ処理の逼迫・スレッド増加 といった “キャパシティ課題(インフラ負荷)” が引き金になるケースの方が多く見られます。 本来はアプリケーション構造や運用基盤の改善で解消できる問題を、長年夜間対応、手動運用、再起動ナレッジといった人手オペレーションで肩代わりしてきた結果、その場しのぎのコストが恒常的な負荷となり、リファクタリングが「恒久的なリスクと運用コストの削減」という文脈で必要になるのです。 次回はこのような運用 (Ops) 側文脈を、同じ良い設計の経済学というレンズで紐解いてみたいと思います。 Red Hat コンサルティングのご紹介:カスタム・ソリューションの構築をエキスパートがサポート  「技術的負債を解消し、ビジネスの変革を加速する第一歩を踏み出しませんか?  Red Hatが提供するベストプラクティスと実践的なコラボレーションにより、お客様に最適化された『戦略的ロードマップの作成』から、最終的な『全社規模への拡張』までを伴走支援します。」  ご興味のある方は、Red Hat コンサルティングへぜひお問い合わせください。  www.redhat.com
Apache Camel Advent Calendar 10日目の記事は、サポート担当古市が担当します。 テーマは、「Camel による Exception/Error Handling」 基本的な使い方と componentの調べ方/試し方がわかった次に気になるのは、例外処理ではないでしょうか。 Camel を使う上で知っておくべき例外処理は、3つあります。 ErrorHandler OnException DoTry/DoCatch コミュニティードキュメントに、詳細解説があります。 camel.apache.org camel.apache.org camel.apache.org camel.apache.org Camelユーザーにとってバイブルと言える書籍「Camel in Action 2nd」では例外処理について網羅的に解説されており、とてもわかりやすいです。 www.manning.com より深く Camelについて学びたい方にはお勧めの書籍ではありますが、まずはざっくりと確認されたい方には、以前、Japan Camel User Groupで読書会を開催した際のまとめ資料があります。 フェンさんのまとめ資料(日本語)が素晴らしいので、こちらをご確認ください。 www.slideshare.net www.slideshare.net www.slideshare.net まずは Exception/Error Handling の挙動を確認するための簡単な camel route を作成し、動かしながら動作を理解するのをお勧めします。 hfuruich@hfuruich-mac camel % cat Hello.java import org.apache.camel.builder.RouteBuilder; import java.io.IOException; public class Hello extends RouteBuilder { @Override public void configure() throws Exception { from("timer:java?period=1000") .setBody() .simple("Hello Camel from ${routeId}") .process(e -> { throw new IOException("Simulated Exception"); }) .log("${body}"); } } doTry/DoCatch, OnException, ErrorHandler の使い分けについて、Geminiへアドバイスを求めてみました。ご参考までに。 errorHandler は、ルートやシステム全体で一貫したフォールトトレランス戦略(「失敗したらリトライし、ダメなら隔離する」など)を定義するために使用します。個別のビジネスロジックではなく、アプリケーション全体の信頼性に関わる設定と考えるのが適切です。 onException は、errorHandler よりも具体的で、特定の例外クラスに対して個別の処理ルールを定義します。errorHandler の設定を上書きする形で動作します。 「SQLException が発生した場合はリトライし、管理者にメールを送信する」「ValidationException が発生した場合は、エラーメッセージをボディに設定して正常終了させる」といった、特定の業務エラーに応じた詳細なハンドリングを行いたい場合に使用します。 doTry/doCatchは、「外部 API を呼び出す部分で、もし接続に失敗したら、代わりにデフォルトの値を返す」「特定のデータ変換処理でエラーが起きたら、その部分だけスキップして処理を続ける」といった、ルート全体の流れを止めずに、部分的な失敗をその場で回復・代替処理したい場合に最適です。 結論 これらの3つのメカニズムは競合するものではなく、レイヤー(層)のように組み合わせて使用するのがベストプラクティスです。 1: まず、errorHandler (特に DeadLetterChannel) でアプリケーション全体の基本的な信頼性を確保します。 2: 次に、ビジネス要件に応じて**onException** を使い、特定の例外に対する共通の処理ルールを追加します。 3: そして、ルート内の個別の処理ステップで発生しうる局所的なエラーは doTry/doCatch で柔軟にハンドリングします。 このように役割分担させることで、Camel ルートは非常に堅牢で、メンテナンス性の高いものになります。 明日の advent calendar 11日目では、「Camel SEDA エンドポイント」についてご紹介します。 アドベントカレンダーの一覧はこちらです。 qiita.com
※本記事は OpenShift Virtualization アドベントカレンダーの 9日目の記事です。 qiita.com こんにちは、OpenShift のプリセールスを担当している山川です。OpenShift Virtualization が日本で普及することを切実に願い続けています。 OpenShift Virtualization の導入を検討いただく際、最初に突き当たる壁の一つがネットワークの設計です。 GUI も有用なのですが、今回は慣れると設定が楽で、かつより柔軟な設定が可能な Yaml を利用して設定してみたいと思います。 OpenShift をインストールしたてのクラスターには、Pod Network、Service Network、Machine Networkといった OpenShift 内部にデプロイされたアプリケーションを外部のネットワークへ簡単に公開することをサポートするネットワークが存在しています。 この方法を用いる場合、OpenShift 内部で IP アドレス変換が発生します。 ただし、VMware 等のハイパーバイザーを利用してきたユーザーを移行しようとすると、ほとんどのワークロードにおいて基盤側での強制的な IP アドレス変換は好まれません。 そこで、本ブログでは、OpenShift 内部と外部をタグ VLAN により L2 ネットワークで接続する方法を見ていきたいと思います。 OpenShift と VLAN ネットワークの接続 最初に結論を申し上げると、Secondary network interface を用いることで、OpenShift Virtualization 上にデプロイされた VM を外部の L2 ネットワークと通信させることが可能となります。 最初から存在するのが下記の図の青で描かれたネットワークです。また、今回注目したいのが緑で描かれた Secondary network です。 Secondary network はデフォルトネットワークとは異なる IP アドレスレンジを用意いただくことが必要です。 Secondary network interface の種類 OpenShift 4.20 の公式ドキュメントによると、Secondary network interface には「OVN-Kubernetes セカンダリーネットワークへの仮想マシンの接続」「仮想マシンの SR-IOV ネットワークへの接続」「Linux ブリッジネットワークへの仮想マシンの接続」の 3 つの方法が解説されており、さらに「OVN-Kubernetes セカンダリーネットワークへの仮想マシンの接続」には layer 2 トポロジーと localnet トポロジーが記載されています。 第10章 ネットワーク | Virtualization | OpenShift Container Platform | 4.20 | Red Hat Documentation 4 つの方式の特徴を見てみましょう。 OVN-Kubernetes layer 2 Network Geneve (Generic Network Virtualization Encapsulation) プロトコルを使用して、OpenShift のノード間にオーバーレイネットワークを作成します。追加の物理ネットワークを設定することなく、OpenShift 内で同一サブネットによる接続が可能になります。OpenShift 外部のクライアントやアプリケーションとの通信では利用しません。また、後述の 3 つとは異なり、必ず OpenShift をインストールする時に設定されるデフォルトの物理 NIC を経由します。 OVN-Kubernetes localnet Network OVN-Kubernetes 管理下の元、OpenShift のノード間だけではなく、外部の L2 ネットワークとの通信も可能にします。利用する場合、内部で利用される Open vSwitch (OVS) を利用するための追加設定が必要です。 SR-IOV Network 高帯域幅または低レイテンシーを必要とする仮想ネットワークアプライアンスや GPU アプリケーション向けに Single Root I/O Virtualization (SR-IOV) を用いて外部の L2 ネットワークとの通信を可能にします。利用するには、SR-IOV が利用可能なハードウェアを用意する必要があります。 Linux Bridge Network Linux カーネル標準のブリッジ機能を使用して、OpenShift 上のアプリケーションを OpenShift 外部の L2 ネットワークと通信する際に利用します。 通常の用途であれば、OpenShift 外部の L2 ネットワークと直接通信する際には OVN-Kubernetes localnet Network、Linux Bridge Network、SR-IOV Network のいずれかを利用可能です。 仮想ネットワークアプライアンス等、ネットワークのパフォーマンスを意識する必要がある場合は SR-IOV Network の利用も検討しますが、今回は 用途が限定的な SR-IOV Network は対象外とします。 OVN-Kubernetes localnet / Linux Bridge どちらを使う? OpenShift 4.20の公式ドキュメントには、「OVN-Kubernetes の localnet トポロジーが推奨される」、と明記されています。OVN-Kubernetes は、OpenShift のデフォルトのネットワークでも使われており今後の拡張も楽しみな技術です。 ただし、サポートされる機能には差があるので、実装されている機能を比較してみましょう。 OpenShift 4.20 のドキュメントには、2 つの方式の比較表が存在しています。 第10章 ネットワーク | Virtualization | OpenShift Container Platform | 4.20 | Red Hat Documentation OVN-Kubernetes localnet と Linux Bridge Network の比較表を見ると、大きな違いは MultiNetworkPolicy の利用可否 (表のネットワークポリシーの利用可否に該当)、仮想アプライアンス等を利用する際に Tagged Packet を VM まで通すかどうか (表のレイヤー 2 トランクアクセスの利用可否に該当) です。 MultiNetworkPolicy を使いたい場合は、OVN-Kubernetes localnet が必須、L2 トランクアクセスが必要な場合は Linux Bridge CNI を利用しましょう。 また、VMware の vSwitch でも設定が存在するプロミスキャスモードや偽装転送 (OpenShift だと Mac spoof filtering が該当) のオン/オフ設定があるかどうかについてよくご質問いただきますが、Linux Bridge CNI ではオン/オフ設定が可能です。 OVN-Kubernetes localnet に関しては、OpenShift 4.20 時点だと設定が固定値となっています。 OVN-Kubernetes を推したいところではあるのですが、サポートされる機能に差分があるため、特徴を理解した上で仮想化基盤のネットワーク要件に基づいて決めていくことを推奨します。 OpenShift Secondary network の設定方法 OpenShift Secondary network の設定には、NodeNetworkConfigulationPolicy (NNCP) と NetworkAttachmentDefinition (NAD) を用います。 NNCP は、Kubernetes NMState Operator を使用して、OpenShift クラスター内の物理ノード(ホスト)のネットワーク設定を宣言的に管理するための仕組みです。 OpenShift Virtualization ユーザーは NMState がほぼ必須となると言っても過言ではないため、早急に Kubernetes NMState Operator をインストールしてください。 5.3. インストール後のネットワーク設定 | Virtualization | OpenShift Container Platform | 4.20 | Red Hat Documentation NodeNetworkConfigulationPolicy (NNCP) NNCP を用いることで、物理 NIC の設定、物理 NIC 冗長化(OpenShift だと Bond の作成)、VLAN インターフェースの作成、Bridge の構成などが可能です。 Yaml ファイル (一部 GUI も可能) を書くだけで、全ノード(または特定の役割のノード)に対して一貫したネットワーク設定を自動適用できます。 なお、OpenShift ユーザーは、各ノードの Red Hat Enterprise Linux CoreOS に SSH でログインして nmcli / nmtui を用いてネットワーク設定をするのではなく、必ず NNCP を使いましょう。 NetworkAttachmentDefinition (NAD) NAD は、Multus CNI (マルチネットワークを可能にするプラグイン) を利用して、VM に追加のネットワークインターフェースを定義するためのカスタムリソース (CRD) です。 「どのインターフェース (NNCP で作ったもの) を使って、どの IP 管理方式 (DHCP や Static など) で VM をネットワークで繋ぐか」という定義を保持します。 また、Open vSwitch (OVS) / Linux Bridge へ任意の設定をするためにも利用します。 VMware に慣れたユーザーには、よく Port Group の役割に近いリソースと説明しています。 それでは、NNCP や NAD を駆使して、Secondary network を設定しましょう。 今回は、OVS bridge や Linux Bridge で タグ VLAN ID を付与するための設定を入れます。 VMware だと 仮想スイッチタギング (VST:Virtual Switch Tagging) に相当します。 OVN-Kubernetes localnet Network の設定 インストールした OpenShift Virtualization に、OVN-Kubernetes localnet Network を設定します。 まずは、冗長化したいインタフェースの bond を作成するための yaml ファイルを用意し、oc apply -f .yaml コマンドを実行します。 OpenShift 4.20 では、3 種類 (active-backup/balance-xor/802.3ad) の bond モードの設定がサポートされています。 対向の L2 スイッチと設定を合わせて設定ください。 1.10. 異なるインターフェイスのポリシー設定の例 | Kubernetes NMState | OpenShift Container Platform | 4.20 | Red Hat Documentation なお、NIC の冗長化が不要な場合は本手順をスキップ可能です。 本ブログでは、以後、下記のような設定例を掲載しますが、環境に合わせて適宜チューニングください。 apiVersion: nmstate.io/v1 kind: NodeNetworkConfigurationPolicy metadata: name: bond1 # 任意 spec: nodeSelector: node-role.kubernetes.io/worker: '' # 全ての Worker Node に設定 desiredState: interfaces: - name: bond1 type: bond # type を bond と指定 state: up ipv4: enabled: false # インタフェースに IPv4 アドレスを指定しない dhcp: false # インタフェースに外部の DHCP サーバーから IPv4 の取得を拒否 link-aggregation: mode: 802.3ad # 802.3ad (LACP) mode の設定例 options: miimon: '100' # リンク監視 (故障検知) の間隔を指定 port: - ens2f0 # 冗長化したい物理 NIC の1 つ目を指定 - ens3f0 # 冗長化したい物理 NIC の2 つ目を指定 続いて、Open vSwitch (OVS) を作成するためのyamlファイルを用意し、oc apply -f .yaml コマンドを実行します。 apiVersion: nmstate.io/v1 kind: NodeNetworkConfigurationPolicy metadata: name: br-data spec: nodeSelector: node-role.kubernetes.io/worker: '' # 全ての Worker Node に設定。設定する Worker Node を指定する場合は値を指定。 desiredState: interfaces: - name: br-data type: ovs-bridge # type を ovs-bridge と指定 state: up bridge: allow-extra-patch-ports: true # kubernetes-nmstate pod がリスタートをした際のネットワーク切断を回避するための必須設定項目 options: stp: false # ovs bridge 上で STP (ループ検知プロトコル) を無効 port: - name: bond1 # ovs bridge (br-data) に紐付ける物理ポートやbond (ここでは bond1) を指定 ovn: bridge-mappings: # ovs bridge と 後述の NAD を紐づけるために必須 - localnet: trunk-net # 後述の localnet の NAD と紐づけるために必須 bridge: br-data # localnet のNAD と紐付けるために自身の bridge 名を指定 state: present 最後に、NAD を作成するための yaml ファイルを用意し、oc apply -f .yaml コマンドを実行します。 apiVersion: k8s.cni.cncf.io/v1 kind: NetworkAttachmentDefinition metadata: name: vlan20-net namespace: default # 展開する必要があるnamespace。defaultにすると全namesapeから利用可能 spec: config: |- { "cniVersion": "0.4.0", "name": "vlan20-net", "topology": "localnet", # localnet を指定 "netAttachDefName": "default/vlan20-net", # 自身の metadata.namespace と metadata.name の内容と一致 "physicalNetworkName": "trunk-net", # 前述の NNCP の bridge-mappings の値と一致 "type": "ovn-k8s-cni-overlay", # 固定 "disableContainerInterface": true, # 固定 "vlanID": 20 # ovs bridge で付与するタグ LAN ID を指定 } これで、全ての設定が完了しました。 あとは、VirtualMachine カスタムリソースで作成した NAD を指定ください。 Linux Bridge Network の設定 インストールした OpenShift Virtualization に、Linux Bridge Network を設定します。 まずは、冗長化したいインタフェースの bond を作成するための yaml ファイルを用意し、oc apply -f .yaml コマンドを実行します。 ここは、localnet と同様です。 なお、NIC の冗長化が不要な場合は本手順をスキップ可能です。 apiVersion: nmstate.io/v1 kind: NodeNetworkConfigurationPolicy metadata: name: bond2 # 任意 spec: nodeSelector: node-role.kubernetes.io/worker: '' # 全ての Worker Node に設定 desiredState: interfaces: - name: bond2 type: bond # type を bond と指定 state: up ipv4: enabled: false # インタフェースに IPv4 アドレスを指定しない dhcp: false # インタフェースに外部の DHCP サーバーから IPv4 の取得を拒否 link-aggregation: mode: 802.3ad # 802.3ad (LACP) mode の設定例 options: miimon: '100' # リンク監視 (故障検知) の間隔を指定 port: - ens4f0 # 冗長化したい物理 NIC の1 つ目を指定 - ens5f0 # 冗長化したい物理 NIC の2 つ目を指定 続いて、Linux Bridge を作成するための yaml ファイルを用意し、oc apply -f .yaml コマンドを実行します。 apiVersion: nmstate.io/v1 kind: NodeNetworkConfigurationPolicy metadata: name: br-data2 spec: nodeSelector: node-role.kubernetes.io/worker: '' # 全ての Worker Node に設定。設定する Worker Node を指定する場合は値を指定。 desiredState: interfaces: - name: br-data2 type: linux-bridge # type を linux-bridge と指定 state: up bridge: options: stp: enabled: false # linux bridge 上で STP (ループ検知プロトコル) を無効 port: - name: bond2 # linux bridge (br-data2) に紐付ける物理ポートやbond (ここでは bond2) を指定 なお、Linux Bridge の場合は bridge-mappings は不要です。 最後に、NAD を作成するための yaml ファイルを用意し、oc apply -f .yaml コマンドを実行します。 apiVersion: k8s.cni.cncf.io/v1 kind: NetworkAttachmentDefinition metadata: name: vlan30-net namespace: default spec: config: |- { "cniVersion": "0.4.0", "name": "vlan30-net", "type": "bridge", # bridge を指定 "bridge": "br-data2", # 前述の NNCP の bridge 名の値と一致 "promiscMode": false, # Promiscuous Mode の設定。デフォルトは false。 "macspoofchk": false, # Mac spoof filtering の設定。デフォルトは false。 "disableContainerInterface": true, # 固定 "vlan": 30 # linux bridge で付与するタグ LAN ID を指定 } これで、全ての設定が完了しました。 あとは、VirtualMachine カスタムリソースで作成した NAD を指定ください。 NNCP や NAD の削除方法 NAD は oc delete により削除が可能です。 NNCPを削除する際には、NNCPの interfaces.state セクションに state: absent を付与した後、oc delete で NNCP のリソースを削除ください。 1.4. NodeNetworkConfigurationPolicy マニフェストファイル | Kubernetes NMState | OpenShift Container Platform | 4.20 | Red Hat Documentation まとめ NNCP や NAD を使いこなすことで OpenShift Virtualization のネットワークを効率的に設定、管理可能です。 ぜひ色々な設定を入れて楽しんでみてください。
こんにちは、Ansibleテクニカルサポートの八木澤(ひよこ大佐)です。この記事は、「 Ansible Advent Calendar 2025 」9日目の記事です。 qiita.com AnsibleでWindowsを自動化する際に、現在は以下の3つのコネクションプラグインを利用できます。 WinRM PSRP SSH おそらくWindowsに接続するうえで最も利用率が高いのはWinRMでしょう。SSHは比較的最近導入されたコネクションプラグインですが、Linuxで馴染み深い方も多いはずです。ただ、WinRMやPSRPはWindows独自のプロトコルのため「具体的に何がどう違うの?」と思っている方も多いのではないでしょうか。この記事では、これらのプロトコルの違いについて詳しく見ていきましょう。 WinRM (Windows Remote Management) WinRMは、その名の通りWindowsのリモート管理のために作られた仕組みです。実体は「WS-Management プロトコル」を使用し接続先のWindowsとやり取りし、CUIベースでのリモート管理を実現します。 WS-Management WS-Managementは、基本的に「SOAP」と呼ばれるXMLベースのメッセージを使用します。HTTP/HTTPSをトランスポート層とし、Windows側の「リソースURI(操作対象)」と「キー(対象の特定)」を指定して、アクション(Get/Put/Enumerate等)を投げます。 詳細な仕様は Microsoft社のドキュメント にありますが、Ansibleのwinrmコネクションプラグイン(pywinrm)は以下のようなフローで接続を行います。 (1) シェルの作成: Create メッセージを送信し、リモートで cmd.exe や powershell.exe のプロセスを立ち上げます。 (2)コマンドの実行: 確立されたシェルに対し、Command メッセージを送信します。Ansibleは実行したいPowerShellスクリプトをBase64エンコードして送り込みます。 (3)出力の取得: コマンドの標準出力・標準エラー出力を取得するため、クライアントから能動的に Receive メッセージをポーリングします。 (4)シェルの削除: タスク完了後、Delete メッセージでプロセスを破棄します。 つまり、WinRM接続は 「都度プロセスを起動し、テキストベースのコマンドを流し込み、テキストの出力を受け取る」 という、従来のSSHに近い手続きをXMLメッセージベースで行っています。ここでのポイントは、「常にpowershell.exeなどのプロセスを新たに立ち上げる」点です。 PSRP(PowerShell Remoting Protocol) PSRPは、PowerShellのコマンドライン(パイプライン)を送信し、実行させるために特化したプロトコルです。 プロトコルの階層構造 重要な点は、PSRPは先ほどWinRMの項で紹介したWS-Managementの上で動作するアプリケーションプロトコルであるということです。 WS-Managementが「接続と認証、データの運搬」を担当し、そのペイロードとしてPSRP独自のメッセージが格納されます。 AnsibleでPSRP(pypsrp)を使用する場合、WinRMとは全く異なるアプローチで接続が行われます。 (1) RunspacePoolの作成とネゴシエーション: PSRPでは単なるシェルプロセスではなく、「RunspacePool(実行空間のプール)」 を作成します。 最初の接続時(wxf:Create)に、SESSION_CAPABILITY メッセージを含めることで、クライアントとサーバー間でプロトコルバージョンやシリアライズのバージョンをネゴシエーションします。 (2) パイプラインの作成と実行: コマンド実行は 「Pipeline(パイプライン)」 単位で行われます。Ansibleは CREATE_PIPELINE メッセージを構築し、サーバー上でPowerShellコマンドレットやスクリプトブロックを実行可能状態にします。 (3)オブジェクトの入出力: データのやり取りには「PIPELINE_INPUT / PIPELINE_OUTPUT」というPSRP独自メッセージが使われ、データは 「シリアライズされた.NETオブジェクト(XML形式)」 として転送されます。 WinRMとPSRPの違い Ansibleでこれらのプロトコルを使う上での違いは、以下の2点に集約されます。 ① テキスト vs オブジェクト WinRM: 基本的に「テキストストリーム(Stdout/Stderr)」です。受信したデータをAnsible側で再度解釈する必要があります。 PSRP: PowerShellの 「オブジェクト」 をXMLで構造化して転送します。型情報やオブジェクト構造が維持されるため、データの欠損やパース時のエラーが起きにくくなります。また、巨大なデータはプロトコルレベルで断片化して転送されるため、WinRMのパケットサイズ制限の影響を受けにくいという利点もあります 。 ② オーバーヘッドとパフォーマンス WinRM: タスクごとに「プロセス起動(powershell.exe等)→実行→終了」を繰り返します。Windowsのプロセス生成コストはLinuxと比較して高いため、タスク数が多いとオーバーヘッドが無視できなくなります。 PSRP: 最初に作成した RunspacePool を再利用します。Runspaceは「Opened(開放)」状態を維持し、そこに次々とコマンド(パイプライン)を投入して使い回します。「100個の小さな設定変更を行う」ようなPlaybookでは、PSRPの方がWinRMより有意に高速になる可能性が高くなります。 まとめ AnsibleにおいてWinRMとPSRPは似たようなものと思われがちですが、内部的な仕組みは大きく異なります。特にPSRPはパフォーマンス改善にも有効ですので、ぜひ一度試してみてください!
Red Hat のソリューションアーキテクトの瀬戸です。 これは Jakarta EE / Java EE Advent Calendar 2025 の9日目の記事です。 QuarkusはJBoss EAPや他のJavaアプリケーションサーバーに比べると起動が早いです。実行に必要なモジュールが少なく、最小限のリソース読み込みで起動するという事もあるのですが、もう一つ、DIをビルド時に行うからという事もあります。 DIとは DIは依存性注入(Dependency Injection)のことで、コンポーネント間の結合度を低減し、テスタビリティと保守性を向上させるために使用されています。 古くはSpringで実装され、広まり、Jakarta EE/MicroProfileではCDI(Contexts and Dependency Injection)として仕様化されており、実装されています。 アプリケーションの依存関係を直接ソースコードに記載するのではなく、DIコンテナにまとめて管理させることで、コンポーネント間の結合度を低減させることができます。 GoFデザインパターンにおける Abstract Factoryパターン を高度に洗練させたものと言っていいでしょう。 Springや多くのJakarta EEサーバーでは実行時DIという仕組みが使われており、アプリケーションサーバーの実行時に依存性の解決が行われていました。しかしながら、Quarkusではサーバーの起動時ではなく、ビルド(コンパイル)時にDIが行われます。 Quarkusのビルド時DI 実行時DIとビルド時DIの比較図(ja.quarkus.ioより) 実行時DIはサーバーの実行時に行われますが、これは利点が多かった仕組みでした。サーバーは一度起動してしまえば基本的にシャットダウンすることはなく長く動き続けます。また、サーバーの環境に合わせて依存性を自由に変更する事ができたので、本番環境と開発環境でほぼ同じライブラリだけど一部だけ構成を切り替えるようなこともできました。 しかしながら、そのあとにコンテナの考え方が出てきたことで、サーバーは必要に応じて増減させるもの、開発環境も本番環境も原則として同じものを動かそう。という形が主流となりました。 そのため、サーバーの起動の回数が多くなったために、サーバーの起動毎に依存性を解決するよりも、ビルド時に依存性を解決することにして、起動時の速度をあげようという形になりました。 Quarkusではその考え方を採用し、ビルド時に依存性を解決するようにしています。 具体例でどうなるのかを見てみる ビルド時にDIをするということは、ビルド時にソースコードへの何かしらの変更が行われているという事になります。 Javaのデコンパイラである jadx を使って、実際にどうなっているのかを確認してみたいと思います。 Quarkusでは最小限のDIを行っているGetting-Startedがあるのでそちらを使って、どういうようなコードが出てきたのかを見てみます。 コンパイル前 二つのクラスが含まれています。 GreetingResource.java package org.acme.getting.started; import jakarta.inject.Inject; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.MediaType; @Path ( "/hello" ) public class GreetingResource { @Inject GreetingService service; @GET @Produces (MediaType.TEXT_PLAIN) @Path ( "/greeting/{name}" ) public String greeting(String name) { return service.greeting(name); } @GET @Produces (MediaType.TEXT_PLAIN) public String hello() { return "hello" ; } } GreetingService .java package org.acme.getting.started; import jakarta.enterprise.context.ApplicationScoped; @ApplicationScoped public class GreetingService { public String greeting(String name) { return "hello " + name; } } JBoss EAP用にコンパイル これらのコードはJAX-RSとCDIを使っているだけなのでJBoss EAPで動かすためにJakarta EEのwarとしてコンパイルすることができます。 jadxでデコンパイルしたところ全く同じものが復元されました。 クラス構造(war) GreetingService Quarkus用にコンパイル Quarkusで実行時jarを生成し、それをデコンパイルしたところ、いくつかのソースコードが追加で生成されていました。 クラス構造(Quarkus) 中身としては、ClientProxyなどが生成され、CDIたるコンテキスト(スコープ)の解決が行えるようなコードが追加されています。 ClientProxy なお、GreetingServiceやGreetingResrouce自体は特に変更はされていませんでした。 GreetingService まとめ ということで、Quarkusではビルド時DIという仕組みを使って起動を高速化しているという話と、その中でどういうコードが生成されているのかを見てきました。 QuarkusはGraalVMと組み合わせたネイティブコンパイルが注目されることが多いですが、実はネイティブコンパイル以外にも速度向上のための機能が含まれています。 SpringやJakarta EEサーバーよりも軽量なアプリケーションサーバーが必要な場合はぜひお試しください。
Apache Camel Advent Calendar 9日目の記事は、サポート担当古市が担当します。 テーマは、「Camel Kafka コンポーネント」 Openshiftなどのクラウド環境では camel-kafka コンポーネントをご利用されているお客様がとても多い印象です。 クライアントや他サービスといった前段からのリクエストをルーティングするといった用途ではなく、クラウド上で生成されるログなどをルーティングする事例が増えています。 ご利用するにあたり、今回も製品ドキュメントもしくはコミュニティードキュメントを確認していただく方法を強くお勧めします。 ここでは、コミュニティードキュメントを見ていきます。 camel.apache.org オープンソースコミュニティーならではといった感じですが、camel 2.13 から提供開始された camel-kafka コンポーネントも非常に豊富なオプションを提供しています。 "Example"セクションには、多数サンプルが記述されています。きっとイメージしている使用例が見つかるはずですので、一通り見ていただくと良いです。 検証する際には docker で kafka broker インスタンスを用意するのが一般的な方法かと思います。 https://hub.docker.com/r/apache/kafka せっかくなので、今回は camel-jbang を使い kafka broker インスタンスを用意してみます。 infra コマンドを使いコンテナの起動などの管理を行えます。 list オプションを使い、kafka に対応しているか確認します。 % camel infra list | grep kafka kafka confluent, redpanda, strimzi Apache Kafka, Distributed event streaming platform % strimzi 実装の kafka broker を起動します % camel infra run kafka strimzi Starting service kafka with implementation strimzi { "brokers" : "localhost:9092", "getBootstrapServers" : "localhost:9092" } 別ターミナルで、稼働中のサービスリストを表示します。 % camel infra ps ALIAS IMPLEMENTATION DESCRIPTION kafka confluent, redpanda, strimzi Apache Kafka, Distributed event streaming platform % getオプションで、サービス接続用のURLを確認可能です。 % camel infra get kafka { "brokers" : "localhost:9092", "getBootstrapServers" : "localhost:9092" } % kafka broker のログを確認することも可能です。 camel infra log kafka kafka broker が立ち上がったので、疎通確認をしてみます。 camel-jbang では簡単にメッセージを確認できるよう、camel route を用意せずに consumer endpoint だけを指定し、メッセージを受信することも可能です。 camel cmd send --poll --endpoint='kafka:test?brokers=localhost:9092' 別ターミナルで、kafka broker へメッセージを送信します。メッセージ送信も camel-jbang で実現できます。 % camel cmd send --body='Hello World' --uri='kafka:test?brokers=localhost:9092' 2025-08-26 13:43:47.921 29144 --- kafka://test?brokers=localhost:9092 : Sent (success) (261ms) % メッセージ送信後、camel cmd send -poll を実行していたターミナルでは、このようなログが出力されるはずです。 2025-08-26 13:43:44.905 29120 --- kafka://test?brokers=localhost:9092 : Poll received (success) (3s282ms) Endpoint kafka://test?brokers=localhost:9092 Exchange (DefaultExchange) InOnly 526363B4E3B0BA0-0000000000000000 Message (DefaultMessage) Header (Long) CamelMessageTimestamp 1756183428168 (2025-08-26 13:43:48 / 0s ago) Header (RecordHeaders) kafka.HEADERS RecordHeaders(headers = [], isReadOnly = false) Header (Long) kafka.OFFSET 12 Header (Integer) kafka.PARTITION 0 Header (Long) kafka.TIMESTAMP 1756183428168 Header (String) kafka.TOPIC test Body (String) (bytes: 11) Hello World % これで kafka broker の疎通確認ができました。 前回用意した camel-ftp コンポーネントを使った camel route を改修してみます。 % cat Hello.java import org.apache.camel.builder.RouteBuilder; import org.apache.camel.model.dataformat.JsonLibrary; public class Hello extends RouteBuilder { @Override public void configure() throws Exception { from("file:data?charset=utf-8") .split(body().tokenize("\n")) .unmarshal().csv() .marshal().json(JsonLibrary.Jackson) .to("kafka:test?brokers=localhost:9092"); from("kafka:test?brokers=localhost:9092") .log("received: ${body}"); } } CSVファイルを読み込んだ後、改行コードを使いメッセージ分割し、それぞれ kafka broker へ jsonフォーマットで送信します。 それとは別に kafka broker からメッセージを読み込み、ログ出力する camel route も用意しました。 実行すると、メッセージを出力するログを確認できます。 % camel run Hello.java ~割愛~ 2025-08-26 11:25:22.764 INFO 7312 --- [aConsumer[test]] Hello.java:15 : received: [["1","Bob","23"]] 2025-08-26 11:25:22.769 INFO 7312 --- [aConsumer[test]] Hello.java:15 : received: [["2","Nancy","30"]] 2025-08-26 11:25:22.773 INFO 7312 --- [aConsumer[test]] Hello.java:15 : received: [["3","David","18"]] 検証が終了したら、infra コマンドでサービスを停止します。 % camel infra stop kafka Shutting down service kafka (PID: 3540) このサンプルは、業務シナリオとかけ離れているので退屈に感じるかもしれません。ポイントとしては以下になります。 ファイルデータを取り込む (camel が提供する豊富な component に置き換え可能です) 取り込んだデータを分割や加工 (フォーマット変換はもちろん、データ修正、条件式を使った分岐も可能です) kafka broker へ送信 camel-jbang を使い、簡単に検証 camel-file / camel-kafka コンポーネントの記事を通して camel component を試す際、コミュニティードキュメントを確認後、 camel-jbang を使い試す流れを体感できたかと思います。 皆さんが興味のある camel component を見つけ、camel-jbang を使い試していただけば幸いです。 そうそう、各component にはそれぞれ専用オプションが豊富に用意されています。簡易版とはなりますが、camel-jbang でそれらオプション内容を確認することも可能です。 % camel doc kafka | grep autoOffsetRese ║ autoOffsetReset │ What to do when there is no initial offset in ZooKeeper or if an offset is out │ latest │ String ║ % 明日の advent calendar 10日目では、「Camel による Exception/Error Handling」についてご紹介します。 アドベントカレンダーの一覧はこちらです。 qiita.com
(2026-08-19 追記) VolumeMode(Filesystem / Block)の説明と、VMware 構成との対応表を追記しました。 ※本記事は OpenShift Virtualization アドベントカレンダーの 8 日目の記事です。 qiita.com 皆さんこんにちは、OpenShift Virtualization とストレージを生業にしている Red Hat のうつぼ(宇都宮)です。 アドベントカレンダーも 1/3 くらいになって、いい具合に個人的にネタ切れ感が襲ってきました。いくらでも書くべきことや問われることはあるはずなんですが、いざ記事にしようと思うと浮かんでこない。 こないだのブラックフライデーでも、セールが始まる前には「これ今じゃなくてブラックフライデーで買おう」と何回も思ったんですが、いざセールが始まると「俺は何がほしかったんだっけ」となって呆然としました。 覚えてないってことは結局そんなに欲しいものじゃなかったってことですかね。欲を抑えて合理的に生きるというのはなかなか難しいものです。非合理でも刹那的に生きよう。 ということで今日のテーマはディスクの種類です。 仮想ディスクのタイプ Disk(一般的な仮想ディスク) LUN(Logical Unit Number) 仮想ディスクのインターフェース VirtIO SCSI SATA タイプとインターフェースの組み合わせ (追記)VolumeMode: Filesystem と Block タイプ(Disk / LUN)との関係 VMware の構成との対応 まとめ(と言いつつブツブツ言う) Virt で VM を作成する際には必ずディスクが必要になります。VM で利用できる仮想ディスクには タイプ と インターフェース を指定します。それぞれに種類があってこれらを組み合わせます。 本稿では、主要な仮想ディスクのタイプとインターフェース、それぞれの特徴について紹介します。 何を使えばよいのか。 仮想ディスクのタイプ OpenShift Virtualization において、仮想ディスクとして設定できる主要なタイプは以下の2種類です。 Disk(一般的な仮想ディスク) 特徴: VM が利用する一般的な仮想ディスク形式です。Storage Class に PVC を作ることで CSI plugin によってストレージシステムで動的にプロビジョニングされた領域(ブロックデバイスまたはファイル・システム)にディスクが作られることが大半です。 用途: ほとんどの OS 用のルートディスク、アプリケーションデータ用ディスクなど、標準的な仮想化環境内で仮想ディスクを作る場合に適しています。大抵はこれでよいです。 LUN(Logical Unit Number) 特徴: 基盤となるストレージで切った論理ボリューム(Logical Volume とか Logical Drive とかいろんな言い方があります)を VM に直接ブロックデバイスとして見せる特殊な形式です。ストレージの特性(例:高度なストレージ機能、低遅延)を活かしやすくなります。 用途: かなり高速な I/O 性能が求められるデータベースや、ストレージの機能(Snapshot, Clone, QoS 等)を VM 側ではなくストレージ側で管理したい場合など、特定のワークロードで使われます。vSphere の RDM(Raw Device Mapping)と同じイメージで構いません。 もう一つ CD-ROM というタイプがありますが、これはちょっと仮想ディスクとは毛色が違うのでここでは省きます。 仮想ディスクのインターフェース 仮想ディスクを VM に接続するインターフェースは以下の3種類があります。 VirtIO 特徴: KVM/QEMU 環境で最高の I/O 性能を発揮するために設計された、準仮想化インターフェースです。ホスト OS とゲスト OS 間で効率的に通信し、仮想化による性能オーバーヘッドを最小限に抑えることができます。 用途: 概ね全てのワークロードで使える形式で、 OpenShift Virtualization における標準的かつ推奨されるインターフェース です。Linux VM からは /dev/vdX というデバイス名で見えます。 SCSI 特徴: 仮想化環境以外でも広く普及しているインターフェースで、多くのOSでサポートされています。1つの SCSI バスに複数のデバイスをぶら下げるため、多くのディスクを同時に接続できたり、SCSI コマンドが話せるを使えるなど、エンタープライズ環境で求められる機能を提供します。仮想化環境では、通常、 virtio-scsi という準仮想化ドライバでエミュレートされます。 用途: ストレージと SCSI で話す場合に使います。複数 VM でクラスタリングする場合に SCSI-3 Persistent Reserve に対応する共有ディスクとして使う場合や、先の LUN タイプで仮想化環境外の論理ボリュームを直接見せる場合、ゲスト OS 上で直で SCSI UNMAP などの SCSI コマンドを打ちたい場合などで使います。Linux VM からは /dev/sdX というデバイス名で見えます。 SATA 特徴: 比較的古い規格であり、主に CD-ROM や古いストレージデバイスのエミュレーションで使います。完全仮想化のインターフェースで、オーバーヘッドのため他と比べて I/O 性能は劣ります。 用途: 対応する VirtIO ドライバがない古い OS のディスクや、特定の互換性要件がある場合にのみ使用されます。 タイプとインターフェースの組み合わせ 大半の用途では 3 つ、または 4 つで事足ります。 特別な理由がない限りは、まずは VirtIO Disk の組み合わせを第一選択としておくのがよいでしょう。 仮想ディスクの種類 タイプ インターフェース 適した用途 VirtIO Disk Disk VirtIO ほとんどの用途で安定して使え、大抵はこれで OK 大丈夫。 SCSI Disk Disk SCSI SCSI3-PR 対応の共有ディスクとして使う場合や、大量に仮想ディスクを接続する場合。 SCSI LUN LUN SCSI 外部ストレージの LUN を直接アタッチしたい場合。 SATA Disk Disk SATA VirtIO ドライバがない古い OS 向けのみ。 (追記)VolumeMode: Filesystem と Block ここまで仮想ディスクのタイプ(Disk / LUN)とインターフェース(VirtIO / SCSI / SATA)を紹介しましたが、もう一つ重要な軸があります。それが PersistentVolumeClaim(PVC)の VolumeMode です。 VolumeMode は PVC がストレージをどのような形で Pod(VM)に提供するかを決めるフィールドで、以下の 2 つの値を取ります。 Filesystem : Pod が PV をファイルシステムとしてマウントします。VM の場合はその上にディスクイメージファイル(通常は disk.img )を置く形となります。NAS のストレージではこちらが自然です。 Block : Pod が PV をブロックデバイスとして認識します。VM の場合は PV がそのままブロックデバイスとして渡されます。ファイルシステムを噛まさないので、SAN のストレージではこちらがブロックデバイスの特性を活かせます。 タイプ(Disk / LUN)との関係 ここで注意したいのが、 VolumeMode と仮想ディスクのタイプ(Disk / LUN)は別の軸 であるということです。 VolumeMode は「PVC がストレージを VM にどう渡すか」(ファイルシステム経由か、ブロックデバイスか) タイプ は「VM がそのディスクをどう認識するか」(一般的な仮想ディスクか、SCSI LUN として直接見せるか) この 2 つは独立しています。なので「VolumeMode: Block だから LUN タイプ」というわけではなく、組み合わせで異なる構成を実現できます。 VMware の構成との対応 VMware から移行を検討する際に「vSphere でいうアレは Virt だとどれ?」という疑問が出がちです。VolumeMode とタイプの組み合わせを VMware の構成と対比すると、以下のように整理できます。 VMware の構成 OpenShift Virtualization で近い構成 説明 通常の VMDK VolumeMode: Filesystem + disk VM イメージがファイルとして格納される、最も一般的な構成。 vRDM に近い VolumeMode: Block + disk ブロックデバイスを VM に渡すが、VM からは通常のディスクとして見える。 pRDM に近い VolumeMode: Block + lun + bus: scsi ブロックデバイスを SCSI LUN として VM に直接公開する。SCSI コマンドがストレージまで透過する。 あくまで「近い」構成であり、VMware の VMDK / vRDM / pRDM と完全に同じものではありません。 仮想化基盤のアーキテクチャが異なるため、I/O パスやストレージ側の振る舞いには差異があります。移行の際は「同じもの」ではなく「目的が同じ構成」として捉えるのがよいでしょう。 まとめ(と言いつつブツブツ言う) 普通の VM ディスクの用途としては、VirtIO Disk, SCSI Disk のどちらかを使えばほとんどの場合 OK です。 DB のテーブル領域とか低レイテンシが欲しい領域で、今は vSphere の RDM で使ってる、という場合は SCSI LUN です。 物理互換モードの RDM だったら、そのまま vSphere から外して SCSI LUN としてアタッチできると思います。これはやってみて次回以降に報告しようと思います。 VirtIO (virtio-blk) も SCSI (virtio-scsi) もどちらも準仮想化インターフェースです。前者はブロックデバイスを仮想化するコントローラで、後者は SCSI インターフェースを仮想化するコントローラです。 virtio-blk はこれ自身が PCI 直のデバイスとして扱われますが、virtio-scsi は PCI に乗った SCSI コントローラ として扱われます。 例として適しているかはわからないですが、virtio-blk は PCI スロットに直挿しする SSD で、virtio-scsi は PCI スロットに挿す RAID コントローラみたいな感じです。 こうすると、virtio-scsi のほうが構成の自由度は高いと感じやすいかなと思います。実際 virtio-scsi だと数百のディスクを付けることができます。 さらに SCSI コマンドが使えるので、制御も行いやすい。やる人居ないと思うけど、テープとかまで繋げられるんじゃないかな。 しかし、性能においては VirtIO の方が SCSI レイヤーを噛まさず直で繋ぐので VirtIO の方がレイテンシは小さいです。 ただこれは理論的な話であって、現実的にはほとんど気にならない程度の性能差じゃないの?とか思います。 もしそうだったら SCSI Disk の方がいいですよね。デバイス名が sdX なんて嫌だ vdX じゃないと違和感で夜しか寝られない、みたいな人で無い限り SCSI を選ぶべきなのではとか思います。 次回以降に VirtIO Disk タイプと SCSI Disk タイプについて深堀りしていきたいと思います。 というわけで今日はここまで。