HPAの外部メトリクスにNew Relicを使ってみた

はじめに

1年ぶりです。おとです。 2022年12月に、New Relic を使った Kubernetes (以下、「k8s」という。) の監視についての記事を書きました。

blog.ecbeing.tech

今回もNew Relicとk8sを絡めたお話をしたいと思います。

k8sはコンテナオーケストレーションツールであり、自動スケーリング機能があります。 リソースの効率的な利用とアプリケーションのパフォーマンス維持に不可欠な機能です。

この記事では、k8sの自動スケーリング機能の1つであるHorizontal Pod Autoscaler (HPA) の基本から、New Relicを利用した外部メトリクスによるスケーリングの実装までを紹介します。

HPAの基本

HPAは、指定したメトリクスに基づいてPodの数を自動で調整するk8sの機能です。

(PodやDeploymentといったk8sの用語についてはリンク先のk8s公式サイトをご確認ください。)

水平Pod自動スケーリング | Kubernetes

k8sの標準メトリクスでは、CPUやメモリの使用率が閾値を超えたときにPodの数を増やし、逆に使用率が下がったら減らす、ということができます。

しかし、標準メトリクスだけでは、アプリケーションのパフォーマンス維持に十全に対応できるわけではありません。

カスタムメトリクスの必要性

例えば、アプリケーションのアクセス数が急増した際に適切にスケーリングするには、CPUやメモリ以外の指標が必要です。

このような場合、カスタムメトリクスに基づいてスケーリングできるようにする必要がありますが、これを実現するためにはメトリクスアダプターが必要になります。

メトリクスソリューションの選択

カスタムメトリクスの収集・管理には多くのツールがありますが、k8s公式から提供されているものはなく、よく使われているのはPrometheusのようです。

しかし、今回はせっかくなのでNew Relicが提供しているメトリクスアダプターを使ってみました。

New Relic メトリクスアダプターの導入と設定

New Relicのメトリクスアダプターをk8sに導入する過程は比較的シンプルです。

New Relicの公式ドキュメントに導入手順が用意されています。

Kubernetes を使用してインフラストラクチャを自動スケールする | New Relic Documentation

以下のコードはk8sクラスターとNew Relicエージェントをデプロイするためのマニフェストファイルの該当箇所を一部抜粋したものです。

---
apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
  name: newrelic-bundle
  namespace: newrelic
spec:
  releaseName: newrelic-bundle
  chart:
    spec:
      chart: nri-bundle
      reconcileStrategy: ChartVersion
      sourceRef:
        kind: HelmRepository
        name: newrelic
  values:
    newrelic-k8s-metrics-adapter:
      lowDataMode: true
      personalAPIKey: "New RelicのAPIキー"
      config:
        accountID: "New RelicのアカウントID"
        externalMetrics:
          requests_count:
            query: "SELECT COUNT(*) FROM Log SINCE 5 minutes ago"

「requests_count」という名前で外部メトリクスを定義しています。

次に、HPA側の設定をします。

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: api-front-hpa
spec:
  minReplicas: 1
  maxReplicas: 3
  metrics:
  - type: External
    external:
        metric:
          name: requests_count
          selector:
            matchLabels:
              proxy_upstream_name: demo1-api-service
        target:
          type: AverageValue
          averageValue: 50
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: demo1-api-deployment

メトリック名に先ほど定義した「requests_count」を指定します。

matchLabelsに指定したKeyValueは、NRQLのWHERE条件として適用されるので、New Relicに発行されるクエリは

SELECT COUNT(*) FROM Log WHERE proxy_upstream_name = 'demo1-api-service' SINCE 5 minutes ago

となり、この取得結果をアクセス数とします。

(今回はIngress-Nginxを使用してPodへの通信をルーティングさせており、Ingress-NginxのログもNew Relicに連携しています。 proxy_upstream_nameはIngress-Nginxのログの属性の1つであり、Podに紐づくServiceの名前が入っています。 'demo1-api-service'を指定することで、demo1-api-deploymentのPodへのアクセスログの件数をアクセス数として取得しています。)

targetをAverageValueとしているので、アクセス数をPodの数で除算した値を閾値と比較して、いくつまでスケールするかを決めることができます。

今回の例でいうと、averageValueが50でmaxReplicasが3なので以下のような挙動となります。

  • 現在のPod数=1、アクセス数>50 ⇒ 50÷1=50となり、Pod数が2に増える
  • 現在のPod数=2、アクセス数=50 ⇒ 50÷2=25となり、Pod数は2のまま
  • 現在のPod数=2、アクセス数>100 ⇒ 100÷2=50となり、Pod数が3に増える
  • 現在のPod数=3、アクセス数=100 ⇒ 100÷3=33となり、Pod数は3のまま
  • 現在のPod数=3、アクセス数>150 ⇒ 150÷3=50となるが、Max=3なのでPod数は3のまま

実装結果

アクセス数の増加に伴い、自動的にPodの数を増減させることができました。

APIResponseが閾値を超えたところでCPUとMemoryに新しい線が引かれる=Podが増えた

今回は実験として閾値を50としましたが、実際の運用ではアプリケーションのアクセス状況をもとに閾値を試行錯誤する必要があります。

おわりに

今回は、New Relicを利用した外部メトリクスによるHPAの実装例を紹介しました。

HPAを利用することで予期せぬアクセス集中が発生したときも、自動でスケールすることができるのでアプリケーションのパフォーマンスを維持することができます。

機会があればPrometheusも試してみたいと思います。

careers.ecbeing.tech