はじめに
これから開発するアプリケーションをKubernetes/OpenShiftにデプロイした際、アプリケーションのPodでどの程度のCPU・メモリが使用されるかを見積もるにはどうすればよいでしょうか。最も確実な方法は、実環境で動作させて検証することだと思います。ただし、アプリケーションはこれから開発するため、実際には何らかのサンプルアプリケーションを作成してパフォーマンス検証を実施することになると思います。
当記事では、IBM CloudのマネージドOpenShift(Red Hat OpenShift on IBM Cloud)に、JavaフレームワークのQuarkusで作成したサンプルAPIアプリケーションをデプロイし、そのアプリケーションのパフォーマンス検証(スループット、PodのCPU・メモリ使用量の確認)を実施したときの手順を示します。
パフォーマンス検証のための負荷ツールはLocustを使用します。
前提環境
- IBM Cloudが使用可能であること
- Red Hat OpenShift on IBM Cloud 4.12
- Quarkus 3.2.4 Final
- Locust 2.16.1
QuarkusでのサンプルAPIアプリケーションの作成
まず、サンプルのAPIアプリケーションをQuarkusで作成します。
QuarkusのCLIをインストールします。
Quarkus CLIインストール手順
Quarkus CLIでアプリケーションの雛形を作成します。
quarkus create app openshift-performance-sample
cd openshift-performance-sample
src/main/java/org/acme/GreetingResource.javaを以下のように編集します。コードの内容は以下の通りです。
-
/greeting/heavyというエンドポイントを用意(executeHeavyProcessing()メソッド) -
executeHeavyProcessing()メソッドでは、インスタンス変数processingTimeナノ秒の間、ループを回す(各ループでは10ミリ秒未満のスリープを入れる) - かかった処理時間を含むメッセージを戻り値として返す
ループを入れているのは、何も処理が無ければAPIが一瞬で終了してしまうためです。スリープだけさせるとCPU負荷も全くかからないため、指定時間だけループさせています(この部分は改善の余地あり)。
package org.acme;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import java.util.Random;
@Path("/greeting")
public class GreetingResource {
@ConfigProperty(name = "processing.time", defaultValue = "100000000")
long processingTime;
@GET
@Produces(MediaType.TEXT_PLAIN)
@Path("/heavy")
public String executeHeavyProcessing() throws Exception {
long startTime = System.nanoTime();
Random random = new Random();
while (System.nanoTime() - startTime < processingTime) { // 100 ms
Thread.sleep(random.nextInt(10));
}
long endTime = System.nanoTime();
double duration = (endTime - startTime) / 1_000_000.0; // ms
String message = "処理時間 : " + duration + " ms";
System.out.println(message);
return message;
}
}
上記のコードのインスタンス変数processingTimeでは、@ConfigPropertyを用いて、値を設定ファイルや環境変数から変更可能にしています。
Quarkusの設定ファイルであるsrc/main/resources/application.propertiesで、値を設定します(デフォルト値と同じなのであまり意味はないですが)。実際には、OpenShift上で環境変数を設定し、値を変更できるようにすることを目的としています。
processing.time=100000000
では、アプリケーションの稼働を確認してみます。以下のコマンドで開発モードで起動します。
quarkus dev
標準でポート8080で起動するため、APIのエンドポイントである/greeting/heavyにcurlコマンドでアクセスしてみます。
❯ curl http://localhost:8080/greeting/heavy
処理時間 : 104.354125 ms
正常にアクセスできることが確認できました。
OpenShiftへのデプロイ
次に、作成したAPIアプリケーションをコンテナ化し、OpenShiftにデプロイします。
コンテナイメージを作成するため、QuarkusのExtension container-image-docker を追加します。
quarkus extension add 'container-image-docker'
コンテナイメージをビルドします。
# 通常はこちらのコマンドでOK
./mvnw clean package -Dquarkus.container-image.build=true -DskipTests=true
# M1/M2 Macなど、arm64 CPUを使用している場合は以下のコマンドを実行
# アプリケーションはビルド済みであることが前提
docker image build --platform linux/amd64 -f src/main/docker/Dockerfile.jvm .
作成されたイメージを確認し、イメージの repository, tag を確認します。
❯ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
imanishi/openshift-performance-sample 1.0.0-SNAPSHOT 57b1af7ae5a6 24 hours ago 434MB
IBM CloudのコンテナレジストリであるIBM Cloud Container Registryに、作成したイメージをpushします。
# IBM Cloud Container Registryにpushするために、IBM Cloudにログイン
# リージョンは東京、リソースグループはDefaultを指定
ibmcloud login -a https://cloud.ibm.com -r jp-tok -g Default
# IBM Cloud Container Registryのリージョンをap-north(東京)に設定
ibmcloud cr region-set ap-north
# Container Registryにログイン
ibmcloud cr login
# Container Registryに名前空間「openshift-performance-sample」を作成
# この名前空間にイメージをpushする
ibmcloud cr namespace-add openshift-performance-sample
# docker tagコマンドで、作成したQuarkusのコンテナイメージを、Container Registryにpushできるようにタグ付け
# 「jp.icr.io/<名前空間>(先ほど作成したopenshift-performance-sample)/<イメージ名>:<タグ>」
# 「jp.icr.io」は東京リージョンのContainer Registryのホスト名
docker tag <OSユーザ名>/openshift-performance-sample:1.0.0-SNAPSHOT jp.icr.io/openshift-performance-sample/openshift-performance-sample:0.1
# Container Registryにpush
docker push jp.icr.io/openshift-performance-sample/openshift-performance-sample:0.1
# openshift-performance-sample名前空間にpushされていることを確認する
ibmcloud cr image-list --restrict openshift-performance-sample
次はOpenShiftへのデプロイの準備を実施します。OpenShift上に、Quarkusアプリケーションをデプロイするプロジェクト(名前空間)を作成します(ocコマンドが使えるように oc login が実行済みであることが前提)。
# プロジェクトを作成
oc new-project openshift-performance-sample
# 作成したプロジェクトを処理対象プロジェクトとして設定
oc project openshift-performance-sample
作成したプロジェクトでIBM Cloud Container Registryからイメージをpullできるよう、defaultプロジェクトからimagePullSecret(all-icr-io)をコピーします。
oc get secret all-icr-io -n default -o yaml | sed 's/default/openshift-performance-sample/g' | oc create -n openshift-performance-sample -f -
次に、コンテナをデプロイするためのマニフェストファイルを作成します。
Deploymentでは、PodにCPU・メモリの上限を設定するため、resources.limits.cpuは500m、resources.limits.memoryは256Miを設定します。また、APIの処理時間を環境変数PROCESSING_TIMEとして与えます。設定値はConfigMapから取得しています。なお、@ConfigPropertyを使用した値の設定については、Quarkus設定リファレンスガイドも適宜参照ください。
apiVersion: apps/v1
kind: Deployment
metadata:
name: openshift-performance-sample
spec:
selector:
matchLabels:
app: openshift-performance-sample
replicas: 1
template:
metadata:
labels:
app: openshift-performance-sample
spec:
containers:
- name: openshift-performance-sample
image: jp.icr.io/openshift-performance-sample/openshift-performance-sample:0.1
resources:
limits:
memory: "256Mi"
cpu: "500m"
ports:
- containerPort: 8080
env:
- name: PROCESSING_TIME
valueFrom:
configMapKeyRef:
name: openshift-performance-sample
key: processing-time
imagePullSecrets:
- name: all-icr-io
ConfigMapのマニフェストファイルです。環境変数PROCESSING_TIMEに与える値を設定します。
apiVersion: v1
kind: ConfigMap
metadata:
name: openshift-performance-sample
data:
processing-time: "500000000"
Serviceのマニフェストファイルです。ポート8080で公開されているQuarkusアプリを、ポート80で公開するようにします。
apiVersion: v1
kind: Service
metadata:
name: openshift-performance-sample
spec:
selector:
app: openshift-performance-sample
ports:
- protocol: TCP
port: 80
targetPort: 8080
type: NodePort
各マニフェストファイルをoc applyし、さらにServiceをoc exposeしてRouteを作成し、外部からアクセスできるようにします。
oc apply -f manifest/configmap.yaml
oc apply -f manifest/deployment.yaml
oc apply -f manifest/service.yaml
oc expose svc/openshift-performance-sample
作成したRouteの情報を確認します。openshift-performance-sample-xxxxxxxxx.jp-tok.containers.appdomain.cloud のようなホスト名が取得できます。
oc get route
取得したホスト名に、APIエンドポイント/greeting/heavyを付けてcurlでアクセスします。なお、oc exposeではHTTPのRouteが作成されます。
❯ curl -w "\n" http://openshift-performance-sample-xxxxxxxxx.jp-tok.containers.appdomain.cloud/greeting/heavy
処理時間 : 502.130118 ms
ここまでで、QuarkusのAPIアプリケーションを、処理時間を500ms(0.5秒)に設定してOpenShiftにデプロイすることができました。
Locustによる負荷
ここからはLocustを使って負荷をかけていきます。locustfile.pyというファイルを作成し、以下のシナリオを記述します。単純に/greeting/heavyにアクセスして終了するというものになります。ユーザ数やホスト名はLocustの実行時に設定するため、シナリオ中には含めていません。
from locust import HttpUser, task
class OpenShiftPerformanceSample(HttpUser):
@task
def greeting_heavy(self):
self.client.get("/greeting/heavy")
Locustをポート3000で起動します。-fオプションで上記のlocustfile.pyを指定しています。
locust --web-port 3000 -f locustfile.py
http://localhost:3000にブラウザでアクセスすると以下のような画面が表示されます。各項目に以下の値を入力し、「Start swarming」ボタンをクリックすることで実行が開始されます。
- Number of users:負荷をかけるユーザ数
- Spawn rate:1秒あたりのユーザ増加数。「Number of users」で設定したユーザ数まで、1秒ごとにここで設定したユーザ数が増えていく
- Host:負荷をかける対象のホスト名(
http://、または、https://を含めて記述)
負荷をかけ始めると、全体のサマリーとして以下のような表形式で状況が表示されます。リクエスト総数、エラー数、レスポンスタイム、スループット(RPS)などが表示されます。

「Charts」タブを選択すると、スループットやレスポンスタイムの状況がグラフとして表示されます。

OpenShiftでのパフォーマンスモニタリング
それでは最後に、Locustで負荷をかけた状態で、Podがどの程度のCPU・メモリを使用しているかを確認します。
OpenShiftのWebコンソールで、Developer Pespective(以下のスクリーンショットでは「開発者向け表示」)を選択し、「モニタリング」をクリックすると、CPU・メモリなどのリソース状況を確認することができます。
「ダッシュボード」で「Kubernetes / Compute Resources / Workload」を選択し、デプロイしたDeploymentを選択します。

以下ではDeploymentと紐づくPodのCPUの使用状況が表示されています。500m(0.5)の上限を設定しましたが、実際に使用されているのは0.034程度で、上限に対して10%未満であることが分かります。

メモリの使用状況も同様に表示されています。上限256Miに対して、100MB程度(40%)が使用されていることが分かります。

今回は単純なサンプルAPIアプリケーションに対して単純な負荷をかけましたが、サンプルAPIの処理を変えたり、Podのレプリカ数を変えたり、Locustのユーザ数などを変えることで、様々なケースの負荷を試すことができます。