Ponz Dev Log

ゆるくてマイペースな開発日記

OpenLiberty with MicroProfile TelemetryでNew Relicにトレース情報を送る

Qiitaにタイトルの記事を投稿しました。 MicroProfileを使うためのアプリサーバにOpen Libertyを利用しています。

qiita.com

以前の記事で「トレース情報を送信するならSaaSもいいよね」的なことを書いたので、せっかくなら試してみようと記事にしました。 New Relicを選んだのはたまたま無料枠で使えるアカウントが手元にあったからです。 今後はInstanaで試してみたいです。

JaegerとかOSSだと使い始めるまでに自分で構築するのが面倒なのに加えて、実運用の場面では高可用性やパフォーマンスなど注意すべきことが出てきます。 SaaSだとこういった面倒ごとをSaaS側に任せる、もしくは負担軽減できるのでいいですね。


以上。

OpenLiberty with MicroProfile Telemetryで手軽に分散トレーシング

Qiitaにタイトルの記事を投稿しました。

qiita.com

過去にOpen LibertyからJaegerにOtelでトレース情報を送信する記事を投稿しましたが、あちらの記事ではJava Agentを使用しています。 内部的にはOpenTelemetry Java SDKを使っているはずですが取得できる情報が異なっているのが面白いですね。

ponzmild.hatenablog.com


以上。

TomcatのHealth Check Valveでヘルスチェックを有効化する

Apache TomcatはHealth Check Valveを使用することでヘルスチェックを有効化できます。 ヘルスチェック設定方法とOpenShift上のJWSコンテナで利用する方法を書きます。

Health Check Valveを利用可能なTomcatバージョン

Changelogを見る限り、Health Check Valveは比較的新しい機能のようです。 本記事執筆時点(2023/02)ではTomcat 8以下では利用できず、Tomcat 9以上が必要です。

Tomcat 9を利用する場合、ヘルスチェックの機能が揃っているバージョン9.0.39以上を利用することをおすすめします。 Red Hatが提供する商用TomcatであるJBoss Web Serverの場合、バージョン5.5.0以上に相当します。

参考リンク:

検証環境

コンポーネント バージョン
JDK 11
Tomcat 9.0.50 (*1)
OpenShift 4.12.0 (*2)
  • *1 Red HatTomcatであるJBoss Web Server (以下、JWS) を使用します。
  • *2 OpenShift Local (crc 2.13.1) を利用します。

Health Check Valveの設定方法

server.xml タグを1行を足すだけでHealth Check Valveを有効化できます。

<Server ...>
  <Service name="Catalina">
    <Engine name="Catalina" defaultHost="localhost">
      <Host name="localhost"  appBase="webapps" ...>

        <!-- 以下の1行を追記 -->
        <Valve className="org.apache.catalina.valves.HealthCheckValve" />

      </Host>
    </Engine>
  </Service>
</Server>

デフォルトのヘルスチェックエンドポイントのURLは http[s]://<ホスト名>:<ポート番号>/health となります。 パス部分 /health を変更したい場合はValveに path 属性を指定すればOKです。

参考リンク:

OpenShift上のTomcatコンテナでヘルスチェックを利用する

以前のブログ記事でOpenShiftのS2Iを利用したTomcatコンテナのビルドとデプロイを試しました。 この記事ではヘルスチェックを設定していなかったので、ヘルスチェックを有効化してみます。

ponzmild.hatenablog.com

しかし上記のような server.xml への設定追加は不要です。 なぜならJWSのベースイメージで提供される server.xml に既にHealth Check Valveの設定が入っているからです。 稼働中のJWS Podに配置された server.xml の中身を確認した結果は次のとおりです。

$ oc exec -i <Pod名> -- cat /opt/jws-5.6/tomcat/conf/server.xml | grep Valve
...
<Valve className="org.apache.catalina.valves.RemoteIpValve" ... />
<Valve className="org.apache.catalina.valves.AccessLogValve" ... />
<Valve className="org.apache.catalina.valves.ErrorReportValve" ... />
<Valve className="org.apache.catalina.valves.HealthCheckValve" />

Podに対してヘルスチェックエンドポイントを呼び出してみます。 Tomcatが正常に起動していればレスポンスのJSON内に UP と表示されます。 checks プロパティに追加のチェック結果を指定できそうな匂いがしますが、ドキュメントで設定方法を見つけることができませんでした。

$ oc exec -i <Pod名> -- curl -s http://localhost:8080/health
{
  "status": "UP",
  "checks": []
}

ヘルスチェックエンドポイントを呼び出せることを確認できたので、 OpenShiftのLiveness ProbeとReadiness Probeを設定して、ダウンしている際には再起動およびリクエストを流さないようにしてみます。 YAMLマニフェストでも設定できますが、OpenShift CLIではワンライナーで設定可能です。 次のコマンドを実行してTomcatのヘルスチェックエンドポイントを叩くように設定します。

$ oc set probe deployment/<アプリ名> --liveness --readiness --get-url=http://:8080/health --initial-delay-seconds=5
deployment.apps/<アプリ名> probes updated

設定後はアクセスログを確認すると10秒間隔でヘルスチェックエンドポイント /health が呼びされていることがわかります。 Liveness ProbeとReadiness Probeで同じエンドポイントを設定しているので2回ずつ呼び出されます。

19-Feb-2023 09:05:20.262 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in [1926] milliseconds
10.21.32.45 - - [19/Feb/2023:09:05:25 +0000] - "GET /health HTTP/1.1" 200 36
10.21.32.45 - - [19/Feb/2023:09:05:25 +0000] - "GET /health HTTP/1.1" 200 36
10.21.32.45 - - [19/Feb/2023:09:05:25 +0000] - "GET /health HTTP/1.1" 200 36
10.21.32.45 - - [19/Feb/2023:09:05:35 +0000] - "GET /health HTTP/1.1" 200 36
...

以上。

TomcatアプリケーションをS2Iでコンテナ化してOpenShiftで動かす

Tomcatで動作するJavaアプリケーションをS2Iでコンテナ化してOpenShiftで動作させる方法の備忘録です。 手元にTomcatアプリケーションはあるけど、コンテナ化やOpenShift/k8sの設定が面倒だなというモチベーションで調査した結果を書き残します。

環境情報

コンポーネント バージョン
JDK 11
Tomcat 9.0.x (*1)
OpenShift 4.12.0 (*2)

アプリケーションの準備

この記事ではコンテナ化してOpenShift上で動作させることを最速で実現するため、 mvn archetype:generate で生成したシンプルなServlet/JSPアプリケーションを題材とします。

$ mvn archetype:generate \
    -DarchetypeGroupId=org.apache.maven.archetypes \
    -DarchetypeArtifactId=maven-archetype-webapp \
    -DarchetypeVersion=1.4 \
    -DgroupId=com.example \
    -DartifactId=jws-sample-webapp \
    -Dversion=1.0.0 \
    -DinteractiveMode=false

$ cd jws-sample-webapp

$ tree .
.
├── pom.xml
└── src
    └── main
        └── webapp
            ├── WEB-INF
            │   └── web.xml
            └── index.jsp

手元のJDKのバージョンと合わせるため、pom.xmlではJavaバージョンを1.7から11に変更します。

<project ...>
  <properties>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
  </properties>

  <!-- 他は変更せず -->
</project>

S2Iでコンテナ化する前に、MavenコマンドでWARファイルを生成できることを確認しておきます。

$ mvn clean package -DskipTests -B -ntp
[INFO] Scanning for projects...
...
[INFO] BUILD SUCCESS

$ ls target/*.war
target/jws-sample-webapp.war

S2IでTomcatアプリケーションをコンテナ化

それでは手元のソースコードをS2IでTomcatアプリケーションでコンテナ化します。 OpenShift CLIの操作例をいくつか例示しますが、 oc login コマンドでOpenShiftにログイン済みの状態を前提とします。

ビルダーイメージの選択

OpenShiftではTomcatアプリケーションをコンテナ化するビルダーイメージが用意されています。 openshift プロジェクトのImageStreamから最新のビルダーイメージを検索します。 本記事執筆時点で最新のImageStreamTagであるJBoss Web Server 5.6.2を使用します。

$ # JWSのImageStreamを検索
$ oc get is -n openshift | grep '^jboss-webserver' | cut -d ' ' -f 1 | grep 'openjdk11'
jboss-webserver53-openjdk11-tomcat9-openshift
jboss-webserver54-openjdk11-tomcat9-openshift-rhel7
jboss-webserver54-openjdk11-tomcat9-openshift-ubi8
jboss-webserver56-openjdk11-tomcat9-openshift-ubi8  # これを使う

$ # JWS 5.6のImageStreamからImageStreamTagを検索
$ oc get istag -n openshift | grep 'jboss-webserver56-openjdk11-tomcat9-openshift-ubi8'  | cut -d ' ' -f 1
jboss-webserver56-openjdk11-tomcat9-openshift-ubi8:5.6.0
jboss-webserver56-openjdk11-tomcat9-openshift-ubi8:5.6.1
jboss-webserver56-openjdk11-tomcat9-openshift-ubi8:5.6.2  # これを使う
jboss-webserver56-openjdk11-tomcat9-openshift-ubi8:latest

S2Iによるコンテナイメージのビルド

oc コマンドでBuildConfigリソースを作成し、先ほど検索したImageStreamTagを指定します。 Dockerfileを使わずソースコードからコンテナイメージを作成するため、バイナリビルドのフラグ --binary=true をコマンドに付与します。

$ oc new-build --name=tomcat-sample-app \
  --image-stream=jboss-webserver56-openjdk11-tomcat9-openshift-ubi8:5.6.2 \
  --binary=true 
--> Found image af8501c (4 months old) in image stream "openshift/jboss-webserver56-openjdk11-tomcat9-openshift-ubi8" under tag "5.6.2" for "jboss-webserver56-openjdk11-tomcat9-openshift-ubi8:5.6.2"

    JBoss Web Server 5.6 OpenJDK11 
    ------------------------------ 
    Platform for building and running web applications on JBoss Web Server 5.6 with OpenJDK11 - Tomcat v9

    Tags: builder, java, tomcat9

    * A source build using binary input will be created
      * The resulting image will be pushed to image stream tag "tomcat-sample-app:latest"
      * A binary build was created, use 'oc start-build --from-dir' to trigger a new build

--> Creating resources with label build=tomcat-sample-app ...
    imagestream.image.openshift.io "tomcat-sample-app" created
    buildconfig.build.openshift.io "tomcat-sample-app" created
--> Success

BuildConfigリソースの作成に成功したら、ソースコードディレクトリを指定してS2Iを実行します。 ビルダーイメージ内のS2Iスクリプトが実行され、コンテナイメージ生成のログが出力されます。 Push successful と表示されたらコンテナイメージのビルドは成功です。 コンテナイメージはOpenShiftの内部レジストリに格納されます。

$ oc start-build tomcat-sample-app --from-dir=. --follow
Uploading directory "." as binary input for the build ...
(...omit...)
Push successful

アプリケーションのデプロイ

コンテナイメージのビルドが無事完了したら、いよいよOpenShiftにアプリケーションをデプロイ (=コンテナを起動) します。 oc new-app コマンドで先ほどビルドしたコンテナイメージ名を指定してデプロイします。 ビルダーイメージからビルドしたTomcat (JWS) アプリケーションは、環境変数 ENABLE_ACCESS_LOG を指定することでアクセスログを出力する Access Log Valve が有効化されます。

$ oc new-app tomcat-sample-app --env=ENABLE_ACCESS_LOG=true
...
--> Creating resources ...
    deployment.apps "tomcat-sample-app" created
    service "tomcat-sample-app" created
--> Success

出力されたログから、コンテナを起動するDeploymentリソースとServiceリソースが作成されていることがわかります。 OpenShift Webコンソールのトポロジービューで参照すると、Deployment管理下のPodがRunningステータスで実行されているはずです。 (スクリーンショットで表示されているヘルスチェックの設定は別記事で取り上げる予定)

さらに外部からもアクセスできるようにRouteリソースも追加で作成します。 oc get route で外部からアクセス可能なホスト名を取得できます。

$ oc expose svc/tomcat-sample-app

$ oc get route tomcat-sample-app -o jsonpath='{.spec.host}'
<サンプルアプリのホスト名>

http://<ホスト名>/jws-sample-webappWebブラウザでアクセスするとJSPのページが表示されます。 ホスト名以降のパス (コンテキストルート) はデフォルトでWARファイル名です。

Podのログを参照するとTomcatJavaのバージョン、そしてアクセスログが出力されています。

$ oc logs tomcat-sample-app-aaaaaa
...
Server version name: Apache Tomcat/9.0.50.redhat-00007
JVM Version: 11.0.16+8-LTS
JVM Vendor: Red Hat, Inc.
CATALINA_BASE: /opt/jws-5.6/tomcat
CATALINA_HOME: /opt/jws-5.6/tomcat
...
<接続元IPアドレス> - - [20/Jan/2023:04:56:27 +0000] <ホスト名> "GET /jws-sample-webapp/ HTTP/1.1" 200 52

参考


以上。

今後のブログ記事投稿の方針

2022年は合計13本の記事を投稿しました。おおよそ毎月1本書けたことになります。 記事を読んでいただいた皆様ありがとうございます!

さて今後のブログの記事投稿についてですが、はてなブログとQiitaを使い分けることにします。 具体的には所属会社が関係するプロダクトの技術記事をQiitaに寄せます。

というのも今年後半に所属会社有志のQiita Organizationができまして、私もそちらに参加しています。

qiita.com

既に2本投稿もしています。

qiita.com

qiita.com

所属会社のプロダクトのことは多くの人に読んでいただいて知ってほしいですし、会社名なり製品名で検索しやすい(ググラビリティ)という点で、当ブログではなく有志コミュニティのページに投稿する方がが良さそうと判断しました。 自分がよく書くネタだと、Open LibertyやIBM Cloud関連ネタはQiitaに投稿となると思います。

ただ、はてなブログを購読している方にも読んでいただけると嬉しいので、Qiitaに投稿した旨の記事は当ブログにも投稿する予定です。

今後も当ブログは更新していきますので、 2023年もどうぞよろしくお願いします!

OpenShift LocalでPVのDynamic Provisioningを試す

この記事はOpenShift Advent Calender 2022の10日目の記事です。 前日は @ArakiToshihiro さんの『見かけたjarファイルをOpenShift にデプロイする』でした。

お仕事でOpenShiftを使うことが多くなり、手元で気軽にOpenShiftを試す環境としてOpenShift Local (旧称Red Hat CodeReady Containers)を使っています。 半年ぶりくらいにOpenShift Localをアップデートした際に見つけたPV周りの小ネタを書きます。

PVは事前に作成されなくなった

私が最後に試したバージョン1.39では、OpenShift LocalでOpenShiftクラスタを作成するとPVが30個事前に作成されていました。 しかしOpenShift Local バージョン2.11を使ってクラスタを構築した直後では、PVは1つしかありません。 内部レジストリ用にアサインされたものだけです。

デフォルトStorageClassはPVを動的プロビジョニングできる

OpenShiftクラスタアサイン済みPVを深掘りすると、StorageClassは crc-csi-hostpath-provisioner です。

OpenShift LocalのデフォルトStorageClass

また、StorageClassの詳細を見るとプロビジョナーは kubevirt/hostpath-provisioner という見慣れないものになっています。

デフォルトStorageClassのプロビジョナー

このプロビジョナーを使ったStorageClassおよびPVの作成方法は、以前からDynamic Provisioningの実現方法としてWikiに掲載されていたものです。 まだWikiやリリースノートに記載されていませんが、ようやくデフォルトのStorageClassでPVをDynamic Provisioningできるものになったようです。

github.com

github.com

PVをDynamic Provisioningしてワークロードに紐づけてみる

検証対象

実際にデフォルトのStorageClassでPVをDynamic Provisioningできるか、ステートフルなワークロードをデプロイして検証してみます。 Red Hat Enterprise Linux 9.1にインストールしたOpenShift Local バージョン2.11を使用して検証してみます。

$ crc version
CRC version: 2.11.0+823e40d
OpenShift version: 4.11.13
Podman version: 4.2.0

ステートフルなワークロードをデプロイ

ステートフルなワークロードの例として、Red Hat AMQ Streams Operator (2.2.0-3) でデプロイしたKafkaクラスタを使います。 事前にOperatorHubからRed Hat AMQ Streams Operatorをインストールしておきます。 デプロイ先のプロジェクト名は適当に ponz-sample-project を作成して指定しました。

Red Hat AMQ Streams Operatorをインストール

Operatorのカスタムリソース Kafka でKafkaクラスタを作成します。 このカスタムリソースの中で、Zookeeperを1台、Kafka Brokerを3台、それぞれ5GiBの容量を確保するように設定しておきます。 マニフェストspec.{kafka|zookeeper}.storage にPVの要求内容を記述します。

次のカスタムリソースを適用してKafkaクラスタを作成します。

$ cat <<EOF > kafka-dynamic-pv-sample-cluster.yaml 
apiVersion: kafka.strimzi.io/v1beta2
kind: Kafka
metadata:
  name: dynamic-pv-sample-cluster
  namespace: ponz-sample-project
spec:
  entityOperator:
    topicOperator: {}
    userOperator: {}
  kafka:
    listeners:
      - name: plain
        port: 9092
        type: internal
        tls: false
      - name: tls
        port: 9093
        type: internal
        tls: true
    storage:
      type: persistent-claim
      size: 5Gi
    version: 3.2.3
    replicas: 3
    config:
      offsets.topic.replication.factor: 3
      transaction.state.log.replication.factor: 3
      transaction.state.log.min.isr: 2
      default.replication.factor: 3
      min.insync.replicas: 2
      inter.broker.protocol.version: '3.2'
  zookeeper:
    storage:
      type: persistent-claim
      size: 5Gi
    replicas: 1
EOF

$ oc apply -f kafka-dynamic-pv-sample-cluster.yaml
kafka.kafka.strimzi.io/dynamic-pv-sample-cluster created

カスタムリソースを適用してしばらく待つと、StatefulSet経由でPodをデプロイされます。 それぞれのPodにはPVが紐づけられます。

$ oc get kafka/dynamic-pv-sample-cluster
NAME                        DESIRED KAFKA REPLICAS   DESIRED ZK REPLICAS   READY   WARNINGS
dynamic-pv-sample-cluster   3                        1                     True

$ oc get sts
NAME                                  READY   AGE
dynamic-pv-sample-cluster-kafka       3/3     105s
dynamic-pv-sample-cluster-zookeeper   1/1     2m38s

$ oc get pvc
NAME                                         STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS                   AGE
data-dynamic-pv-sample-cluster-kafka-0       Bound    pvc-780ddd84-df96-44c9-8985-36a44ac5d808   79Gi       RWO            crc-csi-hostpath-provisioner   2m2s
data-dynamic-pv-sample-cluster-kafka-1       Bound    pvc-64c19c56-3ca0-4124-bfbd-72be6584a156   79Gi       RWO            crc-csi-hostpath-provisioner   2m2s
data-dynamic-pv-sample-cluster-kafka-2       Bound    pvc-0a985451-0701-4e4e-a49d-ebc24c6e527f   79Gi       RWO            crc-csi-hostpath-provisioner   2m2s
data-dynamic-pv-sample-cluster-zookeeper-0   Bound    pvc-1f168f25-5368-4399-963f-4bfcfdb2718a   79Gi       RWO            crc-csi-hostpath-provisioner   2m56s

デフォルトのStorageClassでPVCがDynamic Provisioningされていますね。 PVCの容量は5GiBで要求しましたが、適用したマニフェストでは全て同じくホストマシンのディスク容量が指定されてしまうようです。

動作確認

Kafkaクラスタに接続およびGUIから参照するため、Kafdropをデプロイします。 oc new-app コマンドを使います。

アドベントカレンダー前日の記事がJARからコンテナイメージのビルドだったので、ここでは試しにOpenJDKビルダーイメージとKafdropのソースコードからコンテナイメージをビルドしてデプロイしています。(KafdropはDocker Hubにイメージもありますがせっかくなので)

また、KafdropはKafka Bootstrapサーバ接続先を環境変数にセットする必要があります。 Kafkaカスタムリソースで自動作成されるサービス名を指定します。 ここでは oc new-app--env フラグに Kafkaカスタムリソースのname-kafka-bootstrap.プロジェクト名.svc.cluster.local:9092 を渡します。

$ oc new-app --name kafdrop ubi8-openjdk-11:1.12~https://github.com/obsidiandynamics/kafdrop.git  \
    --env KAFKA_BROKERCONNECT="dynamic-pv-sample-cluster-kafka-bootstrap.ponz-sample-project.svc.cluster.local:9092"  \
    --env SERVER_SERVLET_CONTEXTPATH="/"  \
    --env JVM_OPTS="-Xms32M -Xmx64M"

$ oc expose deploy/kafdrop-ui --port=9000
service/kafdrop-ui exposed

$ oc expose svc kafdrop
route.route.openshift.io/kafdrop exposed

$ oc get route/kafdrop
NAME      HOST/PORT                                      PATH   SERVICES   PORT   TERMINATION   WILDCARD
kafdrop   kafdrop-ponz-sample-project.apps-crc.testing          kafdrop    9000                 None

Routeで払い出されたホスト名でブラウザからアクセスすると、PVと紐づいたKafkaに接続できています。

KafdropからDynamic ProvisioningしたPVを使ったKafkaクラスタに接続


PV事前作成の手間要らずで、以前よりも気軽にステートフルなワークロードを使えるようになりましたね。

以上!

k8s@home #1でkubesprayをお題にLTしました

2022/10/12に k8s@home #1 というイベントでLTをやりました。

自宅検証環境として使っていたk8sクラスタをkubesprayで立てては壊してを繰り返した話です。 人生初のLTだったのですが、自分の知見を話すのは良い体験でした。

k8shome.connpass.com

LTのスライドはドクセルとSpeaker Deckにアップしています。 YouTubeアーカイブも残ると聞いているので、ご興味ある方は見ていただけますと幸いです。

www.docswell.com


以上。