🌱

EKSで始めるk6 Operatorの導入とサンプルテストの分散実行

2024/09/14に公開

https://github.com/moko-poi/eks-k6-operator-example

前準備:

  1. iamユーザーの作成
  2. aws configure でcredentialを設定

1. EKSクラスターの作成(Terraform)

このModuleを使用する
Terraform module to create AWS Elastic Kubernetes (EKS) resources

https://github.com/terraform-aws-modules/terraform-aws-eks

main.tf

# Local Values
locals {
  project    = "eks-k6-operator"
  region     = "ap-northeast-1"
  profile    = # FIXME: Set your AWS profile
  account_id = # FIXME: Set your AWS account ID
  cidrs      = # FIXME: Set your CIDERS
}

# VPC
module "vpc" {
  source = "terraform-aws-modules/vpc/aws"

  name = "${local.project}-vpc"
  cidr = "10.0.0.0/16"

  azs             = ["${local.region}a", "${local.region}c"]
  private_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
  public_subnets  = ["10.0.101.0/24", "10.0.102.0/24"]

  enable_nat_gateway = true
}

# EKS
module "eks" {
  source = "terraform-aws-modules/eks/aws"

  cluster_name    = "${local.project}-eks"
  cluster_version = "1.30"

  vpc_id                   = module.vpc.vpc_id
  subnet_ids               = module.vpc.private_subnets
  control_plane_subnet_ids = module.vpc.public_subnets

  cluster_endpoint_public_access           = true
  cluster_endpoint_public_access_cidrs     = local.cidrs
  enable_cluster_creator_admin_permissions = true

  eks_managed_node_groups = {
    main = {
      desired_size   = 2
      instance_types = ["t3.medium"]
    }
  }
}

provider.tf

provider "aws" {
  default_tags {
    tags = {
      ManagedBy = "terraform"
    }
  }

  region              = local.region
  profile             = local.profile
  allowed_account_ids = [local.account_id]
}

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.60.0"
    }
  }
}

作成されるリソース

VPC関連

  • VPC (${local.project}-vpc)
    • CIDR: 10.0.0.0/16
    • アベイラビリティゾーン:ap-northeast-1aap-northeast-1c
    • NATゲートウェイが有効
  • サブネット
    • プライベートサブネット:10.0.1.0/24ap-northeast-1a)、10.0.2.0/24ap-northeast-1c
    • パブリックサブネット:10.0.101.0/24ap-northeast-1a)、10.0.102.0/24ap-northeast-1c
  • インターネットゲートウェイ
    • VPCに接続されるインターネットゲートウェイ
  • NATゲートウェイ
    • パブリックサブネット内に配置され、プライベートサブネットのインスタンスにインターネットアクセスを提供
  • ルートテーブル
    • パブリックおよびプライベートサブネット用のルートテーブルが作成されます。パブリックサブネットにはインターネットゲートウェイへのルートが追加され、プライベートサブネットにはNATゲートウェイへのルートが追加されます。

EKS関連

  • EKSクラスタ
    • クラスタ名: ${local.project}-eks
    • Kubernetesバージョン: 1.30
    • VPC: 上記のVPCに配置されます。
    • サブネット: プライベートサブネットにEKSコントロールプレーンが配置されます。コントロールプレーンのENIはパブリックサブネットに作成されます。
    • 公開アクセスエンドポイント: 有効 (cluster_endpoint_public_access = true)
    • 公開アクセスのCIDR制限: local.cidrs
  • EKS Managed Node Groups
    • ノードグループ名: main
    • インスタンスタイプ: t3.medium
    • デザイアードサイズ: 2台
    • ノードが自動的にスケールアップ/ダウンします(最小サイズと最大サイズはデフォルトの設定によります)。
  • IAMロールとポリシー
    • EKSクラスタとノードグループのためのIAMロールとポリシーが作成されます。
  • セキュリティグループ
    • EKSクラスタとノードグループのために、必要なセキュリティグループが作成されます。
  • その他のクラスタリソース
    • KubernetesのControl Plane Logsの有効化
    • OIDCプロバイダー設定(IRSAのため)

2. Kubernetesにk6 Operatorをデプロイ

https://grafana.com/docs/k6/latest/testing-guides/running-distributed-tests/

1. EKSクラスターの設定

~/.kube/configファイルが更新され、kubectlでEKSクラスターにアクセスできるようにする

aws eks --region <region> update-kubeconfig --name <cluster_name>

kubectl でEKSクラスターに接続を確認

kubectl get nodes

2. k6-operatorのインストール

git clone https://github.com/grafana/k6-operator && cd k6-operator
make deploy

status確認

kubectl get pod -n k6-operator-system

3. テストスクリプト

test.js

import http from 'k6/http';
import { sleep } from 'k6';

export const options = {
  vus: 10,
  duration: '10s',
};

export default function () {
  http.get('https://test.k6.io/');
  sleep(1);
}

動作確認

k6 run test.js

4. ConfigMapとしてテストスクリプトを追加

kubectl create configmap my-test --from-file test.js

5. カスタムリソースの作成

run-k6-from-configmap.yamlという名前で以下のYAMLファイルを作成し、ConfigMapからスクリプトを実行する設定を行います

run-k6-from-configmap.yaml

apiVersion: k6.io/v1alpha1
kind: TestRun
metadata:
  name: run-k6-from-configmap
spec:
  parallelism: 4
  script:
    configMap:
      name: my-test
      file: test.js

6. テストの実行

  • 以下のコマンドを実行してテストを開始します:

    kubectl apply -f run-k6-from-configmap.yaml
    
❯ kubectl get pods                                                                                                                                                      ─╯

NAME                                      READY   STATUS      RESTARTS   AGE
run-k6-from-configmap-1-qhxv2             0/1     Completed   0          41s
run-k6-from-configmap-2-s6hwj             0/1     Completed   0          41s
run-k6-from-configmap-3-wp4vh             0/1     Completed   0          41s
run-k6-from-configmap-4-dj658             0/1     Completed   0          41s
run-k6-from-configmap-initializer-d4s8d   0/1     Completed   0          49s
run-k6-from-configmap-starter-xlqwd       0/1     Completed   0          32s
❯ kubectl logs run-k6-from-configmap-1-qhxv2                                                                                                                            ─╯

     data_received..................: 327 kB 30 kB/s
     data_sent......................: 3.7 kB 335 B/s
     http_req_blocked...............: avg=35.13ms  min=4.35µs   med=5.36µs   max=332.69ms p(90)=122.27ms p(95)=308.73ms
     http_req_connecting............: avg=16.52ms  min=0s       med=0s       max=157.2ms  p(90)=57.38ms  p(95)=144.93ms
     http_req_duration..............: avg=168.31ms min=143.63ms med=158.2ms  max=296.06ms p(90)=222.62ms p(95)=289.51ms
       { expected_response:true }...: avg=168.31ms min=143.63ms med=158.2ms  max=296.06ms p(90)=222.62ms p(95)=289.51ms
     http_req_failed................: 0.00%  ✓ 0        ✗ 27
     http_req_receiving.............: avg=16.09ms  min=76.59µs  med=115.66µs max=144.84ms p(90)=56.86ms  p(95)=143.85ms
     http_req_sending...............: avg=38.14µs  min=19.37µs  med=28.73µs  max=95.16µs  p(90)=77.73µs  p(95)=91.56µs
     http_req_tls_handshaking.......: avg=16.56ms  min=0s       med=0s       max=157.27ms p(90)=57.52ms  p(95)=145.44ms
     http_req_waiting...............: avg=152.18ms min=143.51ms med=149.19ms max=180.68ms p(90)=159.49ms p(95)=159.64ms
     http_reqs......................: 27     2.460681/s
     iteration_duration.............: avg=1.2s     min=1.14s    med=1.15s    max=1.49s    p(90)=1.35s    p(95)=1.45s
     iterations.....................: 27     2.460681/s
     vus............................: 3      min=0      max=3
     vus_max........................: 3      min=3      max=3

  • テストが終了したら、リソースをクリーンアップするために以下を実行します:

    kubectl delete -f run-k6-from-configmap.yaml
    

実行したログなどの収集

k6リソースを見ると、STAGEstartedになっている。

$ kubectl get k6 -n k6-test
NAME        STAGE     AGE   TESTRUNID
k6-sample   started   28s

Podは以下のようになっていて、parallelismで4並列で動かすよう指定していたため、テスト本体は4つ同時に動いている。

$ kubectl get pod -n k6-test
NAME                          READY   STATUS      RESTARTS   AGE
k6-sample-1-gf698             1/1     Running     0          41s
k6-sample-2-n2rbl             1/1     Running     0          41s
k6-sample-3-tbk6f             1/1     Running     0          41s
k6-sample-4-v657j             1/1     Running     0          41s
k6-sample-initializer-7cxff   0/1     Completed   0          50s
k6-sample-starter-ftdzh       0/1     Completed   0          34s

テスト終了後はSTAGEfinishedになる。

$ kubectl get k6 -n k6-test
NAME        STAGE      AGE   TESTRUNID
k6-sample   finished   89s

ログを見るとテストの結果を確認することができる

$ kubectl logs -n k6-test k6-sample-1-gf698

     ✓ http response status code is 200

     checks.........................: 100.00% ✓ 3557      ✗ 0
     data_received..................: 3.3 MB  56 kB/s
     data_sent......................: 450 kB  7.5 kB/s
     http_req_blocked...............: avg=4.01ms   min=1.8µs    med=5.56µs   max=270.01ms p(90)=6.9µs    p(95)=8.8µs
     http_req_connecting............: avg=1.92ms   min=0s       med=0s       max=120.45ms p(90)=0s       p(95)=0s
     http_req_duration..............: avg=432.43ms min=103.84ms med=124.67ms max=2.93s    p(90)=1.59s    p(95)=2.09s
       { expected_response:true }...: avg=432.43ms min=103.84ms med=124.67ms max=2.93s    p(90)=1.59s    p(95)=2.09s
     http_req_failed................: 0.00%   ✓ 0         ✗ 3557
     http_req_receiving.............: avg=125.08µs min=23.3µs   med=120.21µs max=3.12ms   p(90)=157.28µs p(95)=182.03µs
     http_req_sending...............: avg=35.59µs  min=7.79µs   med=28.69µs  max=735.26µs p(90)=51.94µs  p(95)=58.07µs
     http_req_tls_handshaking.......: avg=2.05ms   min=0s       med=0s       max=126.54ms p(90)=0s       p(95)=0s
     http_req_waiting...............: avg=432.27ms min=103.71ms med=124.54ms max=2.93s    p(90)=1.59s    p(95)=2.08s
     http_reqs......................: 3557    59.281034/s
     iteration_duration.............: avg=436.66ms min=104.08ms med=125.48ms max=2.93s    p(90)=1.59s    p(95)=2.09s
     iterations.....................: 3557    59.281034/s
     vus............................: 2       min=0       max=50
     vus_max........................: 50      min=50      max=50

References

Discussion