MicroProfile Metricsを利用したコンテナ環境での水平オートスケール手法 ~ MicroProfileとKubernets Horizaontal Pod Autoscalerの連携 ~
富士通技術者ブログ~Javaミドルウェア~

2022年8月5日 初版
数村 憲治

マイクロサービスアーキテクチャーで構築されたシステムの利点の一つとして、負荷に応じて柔軟にスケールできることが挙げられます。コンテナ環境で標準的に使用されているKubernetesでは、さまざまなメトリクスに基づいてアプリケーションをオートスケールさせる機能を提供しています。デフォルトでは、CPU使用量やメモリー使用量のようなPodやClusterに紐づいたメトリクスを使用できますが、それだけでは柔軟なスケールを実現できない場合があります。例えば、スレッドプールの使用量、データベースコネクションの使用量など、アプリケーション固有のメトリクスに応じたオートスケールを実施したい場合があります。一方で、MicroProfile Metricsは、アプリケーション固有のメトリクスを定義・公開することができます。本稿では、MicroProfile Metricsと連携し、アプリケーション固有のメトリクスに基づいて、水平オートスケールを実現する手法について解説します。

なお、ここで紹介するプログラムの完全なソースコードや手順は、以下で参照できます。

前提知識

本稿を読むにあたっては以下の前提知識があると、より理解に役立ちます。

  • Kubernetes、Docker、Prometheusの基本的概念・操作方法
  • MicroProfile、Jakarta RESTful Web Servicesの基本的な知識

Kubernetesのオートスケール

Kubernetesにおけるスケールの方法には、水平スケールと垂直スケールの2種類があります。

  • 水平スケール
    負荷に応じて、Podの数を増減させる方法です。この方法では、ノードを追加するなど比較的簡単にスケールさせることができる一方で、一つ一つのPodが、他のPodに影響を受けず動作するようなアプリケーション構成にする必要があります。
  • 垂直スケール
    Podの数は変えずに、Podに割り当てるCPU数やメモリー量を増減させる方法です。この方法では、既存のアプリケーション構成を気にせずスケールさせることができる一方で、一つのPodが単一障害点になり安定稼働のリスクとなります。

図1:水平スケールと垂直スケール
図1:水平スケールと垂直スケール

Kubernetesではどちらの方法も提供されていますが、本稿では、水平スケールを用いた方法を紹介します。

MicroProfile Metricsの使用方法

MicroProfile Metricsは、アプリケーションのメトリクスを外部に伝えるためのフォーマットや外部からアクセスするためのエンドポイントなどを仕様として定義したものです。MicroProfile Metricsでは、3種類のスコープ(Base、Application、Vendor)が定義されています。基本となるメトリクスはBaseと呼ばれるスコープで、Java VMの情報(GCの統計情報など)や、システム情報(CPU数など)等が該当し、アプリケーション側で何も設定しなくても採取することができます。アプリケーション固有のメトリクスを作成した場合は、Applicationスコープとして採取されます。MicroProfile Metricsが提供するAPIを使うことで、簡単にアプリケーション固有のメトリクスを作成することができます。MicroProfile Metricsの詳細については、仕様書を参照してください。

アプリケーション固有のメトリクス定義

以下に、Jakarta RESTful Web ServicesのプログラムでMicroProfile Metricsを使用し、アプリケーション固有のメトリクスを定義する例を示します。

@Path("hello")
@RequestScoped
public class HelloService {
  @Inject
  @Metric(absolute=true, name="demo")・・・(1)
  Counter counter;

  @GET
  @Path("inc")
  public String increment() {・・・(2)
    counter.inc();
    return "Hello +1. Total = " + counter.getCount() + "¥n";
  }

  @GET
  @Path("dec")
  public String decrement() {・・・(3)
    counter.inc(-1);
    return "Hello -1. Total = " + counter.getCount() + "¥n";
  }

}

(1)demoという名前のCounter型メトリクスを定義します。
(2)demoカウンターをインクリメントするメソッドを定義します。
(3)demoカウンターをデクリメントするメソッドを定義します。

このプログラムでは、エンドポイント"/hello/inc"にアクセスするとdemoカウンターがインクリメントされ、"/hello/dec"にアクセスするとdemoカウンターがデクリメントされます。

上記プログラムを実行するために、MicroProfileの実装の一つである、Launcherを用意します。Launcherは、以下よりダウンロードして利用します。

Launcherの使い方は、ダウンロードしたlauncher-4.0.jarを任意の場所に置いて、javaコマンドの-jarオプションに指定するだけです(インストール作業は不要です)。詳細なLauncherの使用方法は、ドキュメントを参照してください。

$ java -jar launcher-4.0.jar --deploy hpa.war

Launcherはデフォルトで8080番ポートを使用しますので、このWebアプリケーションにアクセスするには、curlコマンドなどで以下のように実行します。

$ curl http://localhost:8080/hello/inc
Hello +1. Total = 1

$ curl http://localhost:8080/hello/inc
Hello +1. Total = 2

$ curl http://localhost:8080/hello/dec
Hello -1. Total = 1

このプログラムでは、/hello/incを実行する度にインクリメントされたdemoカウンターの値が、/hello/decを実行する度にデクリメントされたdemoカウンターの値が表示されることが分かります。

定義したメトリクス(demoカウンター)は、以下のエントリーポイントにアクセスすることで確認できます。

$ curl http://localhost:8080/metircs/application

出力は、OpenMetricsテキストフォーマットで、以下のようになります。

# TYPE application_demo_total counter
application_demo_total 1.0

#で始まる行はコメントになります。MicroProfile MetricsのAPIを使用して定義したアプリケーション固有のメトリクスは、先頭にapplicationが付加されます。その後に、@Metricで定義したdemo、最後に、Counter型なのでtotalの3パーツが、「_(アンダースコア)」で連結され、application_demo_totalという名前で表示されます。ここでは、その値が"1.0"であることを示しています。アプリケーションに何度かアクセス(/hello/incや/hello/dec)をする毎に、カウントの値が変わることが確認できます。

KubernetesとPrometheusの準備

次に、Kubernetesで水平オートスケールを実施するための準備をします。本稿では、HorizontalPodAutoscaler(HPA)、Prometheus Server、および、Prometheus Adapterを利用し、図2のような構成となるシステムを構築します。

図2:システム構成
図2:システム構成

KubernetesではメトリクスAPIが提供されており、PodのCPUやメモリーに関する情報がAPIエンドポイント(/apis/metrics.k8s.io/など)にアクセスすることで得られます。また、このAPIは拡張することが可能で、Podに紐づかない任意のメトリクスをExtenrnal APIとして登録することができます。本稿では、このExternal APIにMicroProfile Metricsを使ったメトリクスを登録し、水平オートスケールを実現します。

アプリケーションの配備

アプリケーション固有のメトリクス定義で作成したアプリケーションをKubernetes上に配備します。

Dockerイメージの作成例

Dockerfileの内容などは、README.mdを参照してください。

$ java -jar launcher-4.0.jar --deploy app/target/hpa-0.1.war --generate hpa-uber.jar
$ docker build -t hpa-demo -f docker/Dockerfile .

KubernetesのDeploymentとServiceを定義し、適用します。

$ kubectl apply -f docker/hpademo.yml

hpademo.ymlの抜粋内容は以下になります。

hpademo.ymlの抜粋

apiVersion: apps/v1
kind: Deployment

・・・

spec:

  ・・・

  template:
    metadata:
       annotations:
         prometheus.io/scrape: 'true'
         prometheus.io/path: /metrics/application・・・(4)
         prometheus.io/port: '8080'
    spec:
      containers:
        - name: hpademo
          image: hpa-demo・・・(5)

・・・

apiVersion: v1
kind: Service

・・・

spec:
  type: NodePort・・・(6)
  ports:
    - name: "http"
      protocol: "TCP"
      port: 8080
      targetPort: 8080
      nodePort: 30080・・・(6)

      ・・・

(4)後節でインストールするPrometheusでの採取対象とします。
(5)先ほど作成した、Dockerイメージを指定します。
(6)ここでは、NodePortサービスを使用し、ポート番号30080でアプリケーションにアクセスできるようにします。

アプリケーションへのアクセス方法は以下のとおりです。なお「***(アスタリスク)」の箇所には、お使いの環境に応じたIPアドレス等を指定してください。

$ curl http://***.***.***.***:30080/hello/inc
Hello +1. Total = 3
$ curl http://***.***.***.***:30080/hello/dec
Hello -1. Total = 2

これで、アプリケーションの配備は完了したので、次にPrometheusを設定します。

Prometheusの設定

Kubernetesの拡張APIを使用するために、本稿では、Prometheusサーバー、Prometheus Adapterを使用します。これらのインストールはhelm等を使い、以下のように実施します。

$ helm install prsrv prometheus-community/prometheus -n prom
$ helm install pradp prometheus-community/prometheus-adapter -n prom

Prometheusがインストールできたら、次に、前節でディプロイしたアプリケーションのメトリクスを登録します。登録には、以下のymlファイルを用意します。

rules:
  default: false

  external:
  - seriesQuery: 'application_demo_total'・・・(7)
        # - seriesQuery: '{__name__=~"^some_metric_count$"}'
    resources:
      template: <<.Resource>>
    name:
      matches: ""
      as: "hpa_demo_metric"・・・(8)
    metricsQuery: sum(<<.Series>>{<<.LabelMatchers>>}) by (<<.GroupBy>>)

prometheus:
  url: http://prsrv-prometheus-server.prom.svc.cluster.local・・・(9)
  port: 80

(7)クエリ対象として、前節で作成したアプリケーションのメトリクス名を記載します。
(8)Externalメトリクスとして登録する、メトリクス名を定義します。
(9)先ほど、helmでインストールしたPrometheusサーバーのアドレスを記載します。

helmを使用している場合は、以下のコマンドでExternalメトリクスを登録します。

$ helm upgrade pradp prometheus-community/prometheus-adapter -n prom -f yaml/external-metrics.yml

コマンドが成功すると、以下のようにExternal APIにアクセスできるようになり、登録したメトリクスの値が参照できるようになります。

$ kubectl get --raw /apis/external.metrics.k8s.io/v1beta1
{"kind":"APIResourceList","apiVersion":"v1","groupVersion":"external.metrics.k8s.io/v1beta1","resources":[{"name":"hpa_demo_metric","singularName":"","namespaced":true,"kind":"ExternalMetricValueList","verbs":["get"]}]}

$ kubectl get --raw "/apis/external.metrics.k8s.io/v1beta1/namespaces/default/hpa_demo_metric"
{"kind":"ExternalMetricValueList","apiVersion":"external.metrics.k8s.io/v1beta1","metadata":{},"items":[{"metricName":"hpa_demo_metric","metricLabels":{},"timestamp":"2022-06-23T01:57:02Z","value":"2"}]}

HPAの設定

HPAは、Kubernetes API serverを介し、Podのメトリクスを参照します。前節で設定した、Externalメトリクスを利用するには、以下のような設定ファイルを用意します。

hpa.yml

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: hpa-demo
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: hpa-app
  minReplicas: 1
  maxReplicas: 3
  behavior:
    scaleDown:
      stabilizationWindowSeconds: 10
    scaleUp:
      stabilizationWindowSeconds: 0
  metrics:
    - type: External・・・(10)
      external:
        metric:
          name: hpa_demo_metric・・・(11)
        target:
          type: Value
          value: "5"・・・(12)

(10)アプリケーション固有のメトリクスを使うには External を指定します。
(11)前節でPromethus Adapterに設定したメトリクスの名前を指定します(アプリケーションのメトリクス名application_demo_totalではありません)。
(12)スケールの契機となるdemoカウンターのターゲット値を指定します。

上記のhpa.ymlを、kubernetesに適用することで、HPAの設定が完了します。

$ kubectl apply -f yaml/hpa.yml

スケールの様子は、kubectl get hpaコマンドで確認できます。アプリケーション(/hello/incや/hello/dec)に何度かアクセスすると、TARGETSの値が変わるとともに、REPLICASの値が変わり、kubectl get podコマンドでPod数が増減することが確認できます。

$ kubectl get hpa
NAME       REFERENCE            TARGETS       MINPODS   MAXPODS   REPLICAS   AGE
hpa-demo   Deployment/hpa-app   <unknown>/5   1         3         0          5s

ここで、アプリケーション(/hello/inc)に2回アクセスします。

$ kubectl get hpa
NAME       REFERENCE            TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
hpa-demo   Deployment/hpa-app   2/5       1         3         1          37s

TARGETが2/5に変化していますが、REPLICASの値は1のままです。さらに、アプリケーション(/hello/inc)に4回アクセスします(合計6回)。

$ kubectl get hpa
NAME       REFERENCE            TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
hpa-demo   Deployment/hpa-app   6/5       1         3         3          7m36s

TARGETSが6/5になり、hpa.ymlで設定したtargetの値(5)を超えたため、REPLICASが3に増えスケールアウトされました。

$ kubectl get pod
NAME                       READY   STATUS    RESTARTS   AGE
hpa-app-7b458c7547-jgrxx   1/1     Running   0          110s
hpa-app-7b458c7547-r6tr4   1/1     Running   0          103m
hpa-app-7b458c7547-txlwv   1/1     Running   0          95s

まとめ

本稿では、MicroProfile Metricsを使ったアプリケーション固有のメトリクス情報を基に、KubernetesのPodの水平オートスケール方法を紹介しました。Kubernetes自体にも、CPUやメモリー情報に応じてスケール手段は提供されていますが、現実には、スケールの判断材料としてそれだけでは不十分な場合があります。アプリケーション固有のメトリクス情報を利用することで、きめ細かなスケールが可能になります。よりスケーラビリティーの高いシステム構築に、本稿で紹介した方法を参考にしてください。

本コンテンツに関するお問い合わせ

お電話でのお問い合わせ

富士通コンタクトライン(総合窓口)

0120-933-200

受付時間:9時~12時および13時~17時30分
(土曜日・日曜日・祝日・当社指定の休業日を除く)

Webでのお問い合わせ

当社はセキュリティ保護の観点からSSL技術を使用しております。

ページの先頭へ