負荷テストツールk6をAWS CodeBuildから実行してみた

負荷テストツールk6をAWS CodeBuildから実行してみた

2025.09.05

負荷テストツールk6の実行環境としてAWS CodeBuildを使用する機会があったので記録として残します。

k6とは

k6はGrafana Labsによって開発されている負荷テストツールです。
https://k6.io/

テストシナリオをJavaScriptで作成することができ、作成したスクリプトをCLIから実行することでテストを行えます。
https://dev.classmethod.jp/articles/lets-try-k6/

AWSのドキュメントでも紹介されている負荷テストツールの1つになります。
https://docs.aws.amazon.com/ja_jp/prescriptive-guidance/latest/load-testing/tools.html

環境構築

今回は負荷をかける環境とk6を実行する環境の2つを作成します。
k6を実行するAWS CodeBuild側の設定は以下のGrafana Labsの公式ブログを参考に行います。
https://grafana.com/blog/2021/09/12/how-to-integrate-load-testing-with-ci/cd-with-aws-codebuild-and-k6/

AWSの構成としては以下のような形となります。
今回はシンプルにEC2にApacheをインストールして負荷をかけていきます。
k6_202509050938

EC2作成

EC2は以下のCloudFormationテンプレートで作成を行います。

AWSTemplateFormatVersion: "2010-09-09"

Description: test Stack

Metadata:
# ------------------------------------------------------------#
# Metadata
# ------------------------------------------------------------# 
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label: 
          default: Parameters for VPC
        Parameters:
          - VPCCIDR
      - Label: 
          default: Parameters for Subnet
        Parameters:
          - PublicSubnet01CIDR
      - Label: 
          default: Parameters for ec2
        Parameters:
          - EC2VolumeSize
          - EC2VolumeIOPS
          - EC2AMI
          - EC2InstanceType

Parameters:
# ------------------------------------------------------------#
# Parameters
# ------------------------------------------------------------# 
  VPCCIDR:
    Default: 172.30.0.0/16
    Type: String

  PublicSubnet01CIDR:
    Default: 172.30.3.0/24
    Type: String

  EC2VolumeSize:
    Default: 32
    Type: Number

  EC2VolumeIOPS:
    Default: 3000
    Type: Number

  EC2AMI:
    Default: '/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-6.1-x86_64'
    Type: 'AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>'

  EC2InstanceType:
    Default: t3.micro
    Type: String

Resources:
# ------------------------------------------------------------#
# IAM
# ------------------------------------------------------------# 
  EC2IAMRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument: 
        Version: "2012-10-17"
        Statement: 
          - Effect: Allow
            Principal: 
              Service: 
                - ec2.amazonaws.com
            Action: 
              - 'sts:AssumeRole'
      ManagedPolicyArns: 
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
      RoleName: iam-role-ec2
      Tags:
        - Key: Name
          Value: iam-role-ec2

  EC2IAMInstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      InstanceProfileName: iam-instanceprofile-ec2
      Roles: 
        - !Ref EC2IAMRole

# ------------------------------------------------------------#
# VPC
# ------------------------------------------------------------# 
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VPCCIDR
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags: 
        - Key: Name
          Value: test-vpc

# ------------------------------------------------------------#
# InternetGateway
# ------------------------------------------------------------# 
  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags: 
        - Key: Name
          Value: !Sub test-igw

  InternetGatewayAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      InternetGatewayId: !Ref InternetGateway
      VpcId: !Ref VPC

# ------------------------------------------------------------#
# Subnet
# ------------------------------------------------------------# 
  PublicSubnet01:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: ap-northeast-1a
      CidrBlock: !Ref PublicSubnet01CIDR
      MapPublicIpOnLaunch: true
      Tags: 
        - Key: Name
          Value: !Sub test-public-subnet-01
      VpcId: !Ref VPC

# ------------------------------------------------------------#
# RouteTable
# ------------------------------------------------------------# 
  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: test-public-rtb

  PublicRouteTableRoute:
    Type: AWS::EC2::Route
    Properties:
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway
      RouteTableId: !Ref PublicRouteTable

  PublicRtAssociation1:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PublicRouteTable
      SubnetId: !Ref PublicSubnet01

# ------------------------------------------------------------#
# Security Group
# ------------------------------------------------------------# 
  EC2SG:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: for ec2
      GroupName: test-sg-ec2
      SecurityGroupIngress:
        - FromPort: 80
          IpProtocol: tcp
          CidrIp: 0.0.0.0/0
          ToPort: 80
      Tags: 
        - Key: Name
          Value: test-sg-ec2
      VpcId: !Ref VPC

# ------------------------------------------------------------#
# EC2
# ------------------------------------------------------------# 
  EC2:
    Type: AWS::EC2::Instance
    Properties:
      BlockDeviceMappings: 
        - DeviceName: /dev/xvda
          Ebs:
            DeleteOnTermination: true
            Encrypted: true
            Iops: !Ref EC2VolumeIOPS
            VolumeSize: !Ref EC2VolumeSize
            VolumeType: gp3
      DisableApiTermination: false
      IamInstanceProfile: !Ref EC2IAMInstanceProfile
      ImageId: !Ref EC2AMI
      InstanceType: !Ref EC2InstanceType
      SecurityGroupIds: 
        - !Ref EC2SG
      SubnetId: !Ref PublicSubnet01
      Tags:
        - Key: Name
          Value: test-ec2
      UserData: !Base64 |
        #!/bin/bash
        dnf install httpd -y
        systemctl start httpd
        systemctl enable httpd
        echo "test kobayashi" > /var/www/html/index.html

EC2起動時にユーザーデータでApacheをインストールするようにしています。
CloudFormationのデプロイは以下のAWS CLIコマンドで行います。

aws cloudformation create-stack --stack-name CloudFormationスタック名 --template-body file://CloudFormationテンプレートファイル名 --capabilities CAPABILITY_NAMED_IAM

CodeBuildの作成

今回はGitHubリポジトリにk8の使用するスクリプトとbuildspec.yamlを配置するのでCodeBuildプロジェクトを作成する前にGitHubと連携を行っておく必要があります。
以下のドキュメントの手順でGitHubへ接続を行ってください。
https://docs.aws.amazon.com/ja_jp/dtconsole/latest/userguide/connections-create-github.html

接続が完了したらCodeBuildプロジェクトの作成を行います。
CodeBuildのビルドプロジェクトから「プロジェクトを作成する」をクリックしてください。
スクリーンショット 2025-09-05 112329

ビルドプロジェクトの作成画面が開いたら「プロジェクトの設定」欄で任意の「プロジェクト名」を入力してください。
スクリーンショット 2025-09-05 112519

次に「ソース」欄で「ソースプロバイダ」と「リポジトリ」を設定します。
ソースプロバイダはGitHubを選択してリポジトリは負荷テストスクリプトとCodeBuildの使用するbuildspec.yamlを配置するリポジトリを選択してください。
スクリーンショット 2025-09-05 112753

ウェブフックは使用しないのでウェブフックのチェックは外しておきます。
スクリーンショット 2025-09-05 113231

「環境」欄はオペレーティングシステムをUbuntuに変更してそれ以外はデフォルトの設定とします。
スクリーンショット 2025-09-05 123800
スクリーンショット 2025-09-05 123912

「Buildspec」欄では「buildspec ファイルを使用する」を選択して「Buildspec 名」をbuildspec.yamlにします。
スクリーンショット 2025-09-05 124108

その他の設定はデフォルトのままにして画面下の「ビルドプロジェクトの作成」をクリックします。
CodBuildが作成できたらGitHubリポジトリにbuildspec.yamlとサンプルの負荷テストスクリプトをプッシュします。

buildspec.yaml
version: 0.2

phases:
  pre_build:
    commands:
      - echo Pull grafana/k6 image..
      - docker pull grafana/k6
  build:
    commands:
      - echo Run performance test
      - docker run -i grafana/k6 run - <scripts/test.js
  post_build:
    commands:
      - echo Performance test complete
scripts/test.js
// ./test.js
import http from 'k6/http';
import { sleep } from 'k6';

export default function () {
  const res = http.get('http://EC2のパブリックIPアドレス');
  sleep(1);
}

サンプルの負荷テストスクリプト実行

GitHubへプッシュしたらCodeBuildプロジェクト画面の「ビルドを開始」から「今すぐ始める」をクリックすると実行されます。
スクリーンショット 2025-09-05 130711

実行が完了するとログに結果が表示されます。

[Container] 2025/09/05 04:16:27.676786 Running command docker run -i grafana/k6 run - <scripts/test.js

         /\      Grafana   /‾‾/  
    /\  /  \     |\  __   /  /   
   /  \/    \    | |/ /  /   ‾‾\ 
  /          \   |   (  |  (‾)  |
 / __________ \  |_|\_\  \_____/ 

     execution: local
        script: -
        output: -

     scenarios: (100.00%) 1 scenario, 1 max VUs, 10m30s max duration (incl. graceful stop):
              * default: 1 iterations for each of 1 VUs (maxDuration: 10m0s, gracefulStop: 30s)

running (00m01.0s), 1/1 VUs, 0 complete and 0 interrupted iterations
default   [   0% ] 1 VUs  00m01.0s/10m0s  0/1 iters, 1 per VU

  █ TOTAL RESULTS 

    HTTP
    http_req_duration..............: avg=2.42ms min=2.42ms med=2.42ms max=2.42ms p(90)=2.42ms p(95)=2.42ms
      { expected_response:true }...: avg=2.42ms min=2.42ms med=2.42ms max=2.42ms p(90)=2.42ms p(95)=2.42ms
    http_req_failed................: 0.00% 0 out of 1
    http_reqs......................: 1     0.993465/s

    EXECUTION
    iteration_duration.............: avg=1s     min=1s     med=1s     max=1s     p(90)=1s     p(95)=1s    
    iterations.....................: 1     0.993465/s
    vus............................: 1     min=1      max=1
    vus_max........................: 1     min=1      max=1

    NETWORK
    data_received..................: 262 B 260 B/s
    data_sent......................: 68 B  68 B/s

running (00m01.0s), 0/1 VUs, 1 complete and 0 interrupted iterations
default ✓ [ 100% ] 1 VUs  00m01.0s/10m0s  1/1 iters, 1 per VU

負荷テストスクリプトをカスタマイズしてみる

サンプルの実行ができたので、同時接続数などを調整してみたいと思います。

コードを以下のように変更してGitHubへプッシュしてください。

scripts/test.js
// ./test.js
import http from 'k6/http';
import { sleep } from 'k6';

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

export default function () {
  const res = http.get('http://EC2のパブリックIPアドレス');
  sleep(1);
}

k6ではユーザー数をVUs (仮想ユーザー) というもので表現しているらしいです。
コード内ではoptionsで指定することが可能です。
また、テストの実行時間をdurationで設定できます。
上記のコードであれば10ユーザーがアクセスして30秒間テストが実行されます。
https://grafana.com/docs/k6/latest/get-started/running-k6/

GitHubへコードをプッシュしたら同じ手順でCodeBuildを実行します。
実行後、以下のようにログが確認できます。(重要度の高い部分だけ抜粋しています)

2025/09/05 04:36:04.323974 Running command docker run -i grafana/k6 run - <scripts/test.js

         /\      Grafana   /‾‾/  
    /\  /  \     |\  __   /  /   
   /  \/    \    | |/ /  /   ‾‾\ 
  /          \   |   (  |  (‾)  |
 / __________ \  |_|\_\  \_____/ 

     execution: local
        script: -
        output: -

     scenarios: (100.00%) 1 scenario, 10 max VUs, 1m0s max duration (incl. graceful stop):
              * default: 10 looping VUs for 30s (gracefulStop: 30s)

  █ TOTAL RESULTS 

    HTTP
    http_req_duration..............: avg=2.64ms min=2.11ms med=2.62ms max=4.1ms p(90)=2.92ms p(95)=3.02ms
      { expected_response:true }...: avg=2.64ms min=2.11ms med=2.62ms max=4.1ms p(90)=2.92ms p(95)=3.02ms
    http_req_failed................: 0.00% 0 out of 300
    http_reqs......................: 300   9.966328/s

    EXECUTION
    iteration_duration.............: avg=1s     min=1s     med=1s     max=1s    p(90)=1s     p(95)=1s    
    iterations.....................: 300   9.966328/s
    vus............................: 10    min=10       max=10
    vus_max........................: 10    min=10       max=10

    NETWORK
    data_received..................: 79 kB 2.6 kB/s
    data_sent......................: 20 kB 678 B/s

running (0m30.1s), 00/10 VUs, 300 complete and 0 interrupted iterations
default ✓ [ 100% ] 10 VUs  30s

結果の見方は以下のドキュメントに記載されています。
iterationsが仮想ユーザーがスクリプトを実行した回数なので今回使用したスクリプトの場合は300回リクエストが送られていることになります。
https://grafana.com/docs/k6/latest/using-k6/metrics/reference/

実際、Apacheのアクセスログを確認すると300回アクセスが発生していることが確認できました。

grep "Grafana" access_log | grep "05/Sep/2025:04:36" | wc
    300    3900   28500

また、上記のようなGETリクエストだけでなくPOSTリクエストも行えるため、ID/PWが必要なエンドポイントへのリクエストも行えます。
https://grafana.com/docs/k6/latest/using-k6/http-requests/

さいごに

今回はCodeBuildで負荷テストツールk6を実行してみました。
普段EC2を負荷テストツールの実行環境に使うことが多いのですが、CodeBuildは実行が完了したタイミングで停止するので使い方によってはEC2よりもコストを抑えられそうです。
また、k6のように環境構築が簡単なツールであればCodeBuildの設定を行うだけで実行できるようになるのも魅力的だと思いました。

この記事をシェアする

facebookのロゴhatenaのロゴtwitterのロゴ

© Classmethod, Inc. All rights reserved.