アプリケーションを自動的にスケーリングする必要があるのはなぜでしょうか? SRE として、アプリケーションの弾力性と高可用性を確保する必要があります。したがって、自動スケーリングは必須の機能です。自動的にスケーリングすることで、ワークロードがビジネス トラフィックを効率的に処理できるようになります。 この記事では、KEDA を使用して Kubernetes アプリケーションをイベント駆動型で自動的にスケーリングする方法について詳しく説明します。 KEDAとは何ですか? KEDA は、DevOps、SRE、および Ops チームが外部イベントまたはトリガーに基づいて Pod を水平方向にスケーリングするために使用する、軽量のオープンソース Kubernetes イベント駆動型オートスケーラーです。 KEDA は、CPU やメモリなどの標準リソース メトリックに依存するネイティブ Kubernetes 自動スケーリング ソリューションの機能を拡張するのに役立ちます。 KEDA を Kubernetes クラスターにデプロイし、カスタム リソース定義 (CRD) を使用してポッドのスケーリングを管理できます。 KEDA は Kubernetes HPA 上に構築されており、AWS SQS、Kafka、RabbitMQ などのイベントソースからの情報に基づいてポッドをスケーリングします。これらのイベント ソースはスケーラーを使用して監視され、設定されたルールに基づいてデプロイメントをアクティブ化または非アクティブ化します。 KEDA Scalerは特定のイベントソースのカスタムメトリクスも提供し、DevOpsチームがそれらに関連するメトリクスを観察するのに役立ちます。 私たちがしなければならないことは、アプリケーションを自動的にスケーリングするために使用するスケーラーといくつかのパラメータを選択して ScaledObject (KEDA CRD) を構成することだけです。あとは KEDA が行います。 - イベントソースの監視
- HPAライフサイクルの作成と管理
現在、62 個の内蔵スケーラーと 4 個の外部スケーラーが利用可能です。 KEDA が優れている理由は、軽量コンポーネントと HorizontalPodAutoscaler などのネイティブ Kubernetes コンポーネントを使用していること、そしてさらに重要なのは「プラグ アンド プレイ」であることです。 KEDAを展開する次に、KEDA をデプロイする最も簡単な方法は、次のようにインストールできる公式 Helm を使用することです。 helm repo add kedacore helm repo update helm install keda kedacore/keda --namespace keda --create-namespace ⚠️ ArgoCD を使用して KEDA をデプロイすると、CRD コメントの長さに関する問題が発生する可能性があります。この問題は、 template.sped.syncPolicy.syncOptions でオプション ServerSideApply=true を使用することで解決できます。あるいは、helm でパラメータを設定して CRD デプロイメントを無効にすることもできますが、この場合は KEDA CRD をデプロイする別の方法を見つける必要があります。 ネイティブの Cron Scaler に基づいて Web アプリケーションを自動的にスケーリングしますKEDAを詳しく見てみましょう! ウェブアプリケーションをデプロイするデモでは、ビルドされた Golang Web アプリケーションが使用されます。次のマニフェストを使用してデプロイします。 --- apiVersion: apps/v1 kind: Deployment metadata: labels: app: go-helloworld name: go-helloworld spec: selector: matchLabels: app: go-helloworld template: metadata: labels: app: go-helloworld spec: containers: - image: rg.fr-par.scw.cloud/novigrad/go-helloworld:0.1.0 name: go-helloworld resources: requests: cpu: "50m" memory: "64Mi" limits: memory: "128Mi" cpu: "100m" --- apiVersion: v1 kind: Service metadata: name: go-helloworld spec: selector: app: go-helloworld ports: - protocol: TCP port: 8080 name: http --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: kubernetes.io/ingress.class: nginx cert-manager.io/cluster-issuer: letsencrypt name: go-helloworld spec: rules: - host: helloworld.jourdain.io http: paths: - path: / pathType: Prefix backend: service: name: go-helloworld port: number: 8080 tls: # < placing a host in the TLS config will indicate a certificate should be created - hosts: - helloworld.jourdain.io secretName: go-helloworld-tls-cert KEDA を設定して、営業時間中にのみ Web アプリを自動的にスケーリングします。アプリケーションを勤務時間中にのみ利用できるようにしたいとします。なぜこれが必要なのかについては、いくつかのシナリオが考えられます。たとえば、開発環境では、アプリケーションを 24 時間稼働させておく必要はありません。クラウド環境では、アプリケーション/コンピューティング インスタンスの数に応じて、大幅なコスト削減が可能です。これを実現するには、KEDA のネイティブ Cron スケーラーを使用します。 Cron スケーラーは Linux スタイルの cron をサポートしているため、就業時間中にアプリケーションをスケーリングすることもできます。 Cron スケーラーを構成するには、[ScaledObject] CRD を次のように使用します。 apiVersion: keda.sh/v1alpha1 kind: ScaledObject metadata: name: go-helloworld spec: scaleTargetRef: name: go-helloworld triggers: - type: cron metadata: timezone: Europe/Paris start: 00 08 * * 1-5 end: 00 18 * * 1-5 desiredReplicas: "2" ⚠️ ScaledObject はアプリケーションと同じ名前空間に存在する必要があります。この構成を詳しく見てみましょう。 - spec.scaleTargetRefはKubernetes Deployment/StatefulSetまたはその他のカスタムリソースへの参照です
name (必須): Kubernetes リソースの名前 kind (オプション): Kubernetes リソースのタイプ。デフォルト値はデプロイメントです。 - spec.triggersはターゲットリソースのスケーリングをアクティブにするために使用されるトリガーのリストです
- タイプ(必須): スケーラー名
- メタデータ (必須): Cron スケーラーに必要な構成パラメータ。この構成では、アプリケーションは月曜日から金曜日までの毎日 08:00 から 18:00 の間に起動し、2 つのレプリカを実行します。
HTTPイベントに基づいてWebアプリケーションを自動的にスケーリングする(KEDA HTTP 外部スケーラーを使用) KEDA のすべてのスケーラーを使用すると、AMQP キュー内のメッセージに基づいてアプリケーションをスケーリングするなど、さまざまな方法で Web アプリケーションを自動的にスケーリングできます。 これで、KEDA がどのように機能するかがわかりました。ここでは、HTTP イベントに基づいてアプリケーションを自動的にスケーリングすることで、KEDA がトラフィックの急増に対処するのにどのように役立つかについて説明します。これを行うには、2 つのオプションがあります。 - Prometheusスケーラーの使用
- アドオンと同様に動作する KEDA HTTP 外部スケーラーを使用します。デモ クラスターには Prometheus がインストールされていないため、KEDA HTTP 外部スケーラーを使用します。
💡 KEDA HTTP プラグインは現在ベータ版です。主に KEDA チームによってメンテナンスされています。 ソリューションの概要KEDA HTTP スケーラーは、KEDA コア上に構築されたアドオンであり、オペレーター、スケーラー、インターセプターという独自のコンポーネントを備えています。これらが何をするのかについて詳しく知りたい場合は、公式ドキュメントをお読みください。つまり、仕組みをよりよく理解できるように、小さな例を示します。 写真 KEDA HTTPアドオンをインストールするこのスケーラーは組み込まれていないため、手動でインストールする必要があります。公式ドキュメントによると、Helm を使用して以下をインストールできます。 helm install http-add-on kedacore/keda-add-ons-http --namespace keda インストールが正常に完了すると、次のポッドが表示されます。 ❯ k get pods -l app=keda-add-ons-http -o name pod/keda-add-ons-http-controller-manager-5c8d895cff-7jsl8 pod/keda-add-ons-http-external-scaler-57889786cf-r45lj pod/keda-add-ons-http-interceptor-5bf6756df9-wwff8 pod/keda-add-ons-http-interceptor-5bf6756df9-x8l58 pod/keda-add-ons-http-interceptor-5bf6756df9-zxvw Webアプリケーション用に「HTTPScaledObject」を構成する前述したように、KEDA HTTP アドオンにはオペレーターを含む独自のコンポーネントが付属しており、独自の CRD も付属しています。 HTTPScaledObject は、KEDA HTTP アドオンによって管理される CRD です。ここで設定する必要があるのはこれです。 Web アプリケーション用の HTTPSscaledObject リソースを作成しましょう: ⚠️ HTTPScaleObject リソースは、Web アプリケーションと同じ名前空間に作成する必要があります。 kind: HTTPScaledObject apiVersion: http.keda.sh/v1alpha1 metadata: name: go-helloworld spec: host: "helloworld.jourdain.io" targetPendingRequests: 10 scaledownPeriod: 300 scaleTargetRef: deployment: go-helloworld service: go-helloworld port: 8080 replicas: min: 0 max: 10 ここでは、アプリケーションのデプロイメントを 0 から 10 のレプリカにスケーリングするように HTTPScaledObject アプリケーションを構成しました。インターセプターで保留中のリクエスト (アプリケーションがまだ受信していないリクエスト) が 10 件ある場合、KEDA はポッドを追加します。 ウェブアプリケーションのサービスとイングレスを調整する上の図をよく見ると、Web アプリケーションのイングレスは、Web アプリケーションのインターセプター サービスではなく、KEDA HTTP アドオンのインターセプター サービスを参照する必要があることがわかります。 Ingress は別の名前空間のサービスを参照できないため、keda 名前空間からインターセプター サービスを参照する Web アプリケーションと同じ名前空間に、external タイプのサービスを作成します。 kind: Service apiVersion: v1 metadata: name: keda-add-ons-http-interceptor-proxy spec: type: ExternalName externalName: keda-add-ons-http-interceptor-proxy.keda.svc.cluster.local ここで、新しく作成されたサービスを参照するように、Web アプリケーションのエントリ ポイントを再構成する必要があります。 apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: kubernetes.io/ingress.class: nginx cert-manager.io/cluster-issuer: letsencrypt name: go-helloworld spec: rules: - host: helloworld.jourdain.io http: paths: - path: / pathType: Prefix backend: service: name: keda-add-ons-http-interceptor-proxy port: number: 8080 tls: # < placing a host in the TLS config will indicate a certificate should be created - hosts: - helloworld.jourdain.io secretName: go-helloworld-tls-cert ⚠️ 新しいサービスの名前を入力する必要がありますが、インターセプターのサービスによって置き換えられるポートにも注意してください。 テストしてみましょう構成が正常であることを確認するために、負荷テスト ツールである [k6] ツールを使用します。 k6 についてさらに詳しく知りたい場合は、Padok のブログからの紹介をご覧ください。 - [K6 と Kubernetes を使用して分散負荷テストを行う方法] (https://www.padok.fr/en/blog/k6-load-testing)
- [Padok テックレーダーからの k6 の説明] (https://www.padok.fr/en/tech-radar-resilient?category=resilient&rank=6)
今回使用される k6 スクリプトは次の通りで、以降のテストにも使用されます。 import { check } from 'k6'; import http from 'k6/http'; export const options = { scenarios: { constant_request_rate: { executor: 'constant-arrival-rate', rate: 100, timeUnit: '1s', // 100 iterations per second, ie 100 RPS duration: '30s', preAllocatedVUs: 50, // how large the initial pool of VUs would be maxVUs: 50, // if the preAllocatedVUs are not enough, we can initialize more }, }, }; export function test(params) { const res = http.get(''); check(res, { 'is status 200': (r) => r.status === 200, }); } export default function () { test(); } まず、100 RPS で何が起こるか見てみましょう。 ❯ k6 run k6/script.js /\\ |‾‾| /‾‾/ /‾‾/ /\\ / \\ | |/ / / / / \\/ \\ | ( / ‾‾\\ / \\ | |\\ \\ | (‾) | / __________ \\ |__| \\__\\ \\_____/ .io execution: local script: k6/script.js output: - scenarios: (100.00%) 1 scenario, 50 max VUs, 1m0s max duration (incl. graceful stop): * constant_request_rate: 100.00 iterations/s for 30s (maxVUs: 50, gracefulStop: 30s) ✓ is status 200 checks.........................: 100.00% ✓ 3001 ✗ 0 data_received..................: 845 kB 28 kB/s data_sent......................: 134 kB 4.5 kB/s http_req_blocked...............: avg=792.54µs min=0s med=1µs max=137.85ms p(90)=2µs p(95)=2µs http_req_connecting............: avg=136.6µs min=0s med=0s max=17.67ms p(90)=0s p(95)=0s http_req_duration..............: avg=11.38ms min=7.68ms med=10.68ms max=100.96ms p(90)=12.78ms p(95)=14.33ms { expected_response:true }...: avg=11.38ms min=7.68ms med=10.68ms max=100.96ms p(90)=12.78ms p(95)=14.33ms http_req_failed................: 0.00% ✓ 0 ✗ 3001 http_req_receiving.............: avg=89.68µs min=8µs med=64µs max=6.35ms p(90)=112µs p(95)=134µs http_req_sending...............: avg=152.31µs min=14µs med=137µs max=2.57ms p(90)=274µs p(95)=313µs http_req_tls_handshaking.......: avg=587.62µs min=0s med=0s max=74.46ms p(90)=0s p(95)=0s http_req_waiting...............: avg=11.14ms min=7.62ms med=10.48ms max=100.92ms p(90)=12.47ms p(95)=13.96ms http_reqs......................: 3001 99.983105/s iteration_duration.............: avg=12.37ms min=7.73ms med=10.88ms max=194.89ms p(90)=13.07ms p(95)=14.99ms iterations.....................: 3001 99.983105/s vus............................: 1 min=1 max=1 vus_max........................: 50 min=50 max=50 running (0m30.0s), 00/50 VUs, 3001 complete and 0 interrupted iterations constant_request_rate ✓ [======================================] 00/50 VUs 30s 100.00 iters/s 💡 インターセプター キューにあるリクエストの数をリアルタイムで確認したい場合は、2 つのターミナル ペインで次のコマンドを実行します。 ❯ kubectl proxy Starting to serve on 127.0.0.1:8001 同様に: ❯ watch -n '1' curl --silent localhost:8001/api/v1/namespaces/keda/services/keda-add-ons-http-interceptor-admin:9090/proxy/queue {"default/go-helloworld":0} 100 RPS テストでは、インターセプター キュー内の保留中の要求の数が 1 を超えなかったため、アプリケーションはスケールアップしませんでした。念のため、targetPendingRequests を 10 に設定しました。すべて正常に動作しています😁 次に、RPS を 10 倍に増やして、何が起こるか見てみましょう。 ❯ k6 run k6/script.js /\\ |‾‾| /‾‾/ /‾‾/ /\\ / \\ | |/ / / / / \\/ \\ | ( / ‾‾\\ / \\ | |\\ \\ | (‾) | / __________ \\ |__| \\__\\ \\_____/ .io execution: local script: k6/script.js output: - scenarios: (100.00%) 1 scenario, 50 max VUs, 1m0s max duration (incl. graceful stop): * constant_request_rate: 1000.00 iterations/s for 30s (maxVUs: 50, gracefulStop: 30s) ✗ is status 200 ↳ 99% — ✓ 11642 / ✗ 2 checks.........................: 99.98% ✓ 11642 ✗ 2 data_received..................: 2.6 MB 86 kB/s data_sent......................: 446 kB 15 kB/s dropped_iterations.............: 18356 611.028519/s http_req_blocked...............: avg=1.07ms min=0s med=0s max=408.06ms p(90)=1µs p(95)=1µs http_req_connecting............: avg=43.12µs min=0s med=0s max=11.05ms p(90)=0s p(95)=0s http_req_duration..............: avg=120.09ms min=8.14ms med=74.77ms max=6.87sp(90)=189.49ms p(95)=250.21ms { expected_response:true }...: avg=120.01ms min=8.14ms med=74.76ms max=6.87sp(90)=189.41ms p(95)=249.97ms http_req_failed................: 0.01% ✓ 2 ✗ 11642 http_req_receiving.............: avg=377.61µs min=5µs med=32µs max=27.32ms p(90)=758.1µs p(95)=2.49ms http_req_sending...............: avg=61.57µs min=9µs med=45µs max=9.99ms p(90)=102µs p(95)=141µs http_req_tls_handshaking.......: avg=626.79µs min=0s med=0s max=297.82ms p(90)=0s p(95)=0s http_req_waiting...............: avg=119.65ms min=7.95ms med=74.32ms max=6.87sp(90)=188.95ms p(95)=249.76ms http_reqs......................: 11644 387.60166/s iteration_duration.............: avg=121.26ms min=8.32ms med=74.87ms max=7.07sp(90)=189.62ms p(95)=250.28ms iterations.....................: 11644 387.60166/s vus............................: 44 min=25 max=50 vus_max........................: 50 min=50 max=50 running (0m30.0s), 00/50 VUs, 11644 complete and 0 interrupted iterationsconstant_request_rate ✓ [======================================] 00/50 VUs 30s 1000.00 iters/s 結果は悪くなく、2 つのリクエストが KO になっています。これは、アプリケーションがコールド スタート (0 から開始) され、各リクエストが 1/2 秒以上待機しないためです。展開履歴は次のとおりです。 ❯ k get deployments.apps -w NAME READY UP-TO-DATE AVAILABLE AGE go-helloworld 0/0 0 0 36m go-helloworld 0/1 0 0 36m go-helloworld 1/1 1 1 36m go-helloworld 1/4 1 1 36m go-helloworld 2/4 4 2 36m go-helloworld 3/4 4 3 36m go-helloworld 4/4 4 4 36m go-helloworld 4/5 4 4 37m go-helloworld 5/5 5 5 37m アプリケーションは 0 から 5 個のレプリカに拡張されました。 Web アプリケーションの保留中の要求の数が 10 未満になるまで。 スケーリング命令は非常に高速で、アプリケーションはすぐに 5 つのレプリカに到達しました。 以下は、100 RPS テストと 1k RPS テスト間の http_req_duration k6 メトリックの比較です。 # 100 RPS http_req_duration: avg=11.38ms min=7.68ms med=10.68ms max=100.96ms p(90)=12.78ms p(95)=14.33ms # 1k RPS http_req_duration: avg=120.09ms min=8.14ms med=74.77ms max=6.87sp(90)=189.49ms p(95)=250.21ms ニーズ (SLO、SLA など) に応じて、Web アプリケーションの targetPendingRequestsHTTPScaledObject パラメータを少し調整できる可能性があります。 ゼロまでズーム!この記事で紹介した2つのケースを通じて、ゼロにスケールする方法を検証しました。しかし、それがどのように機能するかを本当に知っていますか? KEDA はイベントに基づいてアプリケーションを自動的にスケーリングするため、イベントを受信した瞬間から、KEDA はアプリケーションを最小のレプリカにスケーリングします。たとえば、HTTP アドオンを例にとると、KEDA は最初にリクエストを受信したときに最小のレプリカにスケーリングします。 要約するKEDA は、FaaS に似たイベント対応のスケーリング モデルを提供します。これにより、Kubernetes デプロイメントは、データとコンテキストを失うことなく、需要とインテリジェンスに基づいてゼロから動的にスケーリングできます。業務要求量が増えると、アプリケーションは自動的に容量を拡張し、業務要求量が少ないと、アプリケーションは自動的に容量を縮小します。これにより、多くの運用環境での手動による拡張/縮小操作が軽減され、ユーザーのサービス エクスペリエンスが保証されます。 KEDA は、Kubernetes にさらに多くのイベント ソースをもたらします。今後さらに多くのトリガーが追加されることで、KEDA は実稼働レベルの Kubernetes デプロイメントに不可欠なものとなり、アプリケーションの自動スケーリングをアプリケーション開発の組み込みコンポーネントにする可能性が高くなります。 |