Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions .github/workflows/load-test-run.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
name: Load Test Run

on:
workflow_dispatch:
inputs:
vus:
description: "k6 VUs"
required: true
default: "10"
type: string
iterations:
description: "k6 iterations per VU"
required: true
default: "10"
type: string
max_duration:
description: "k6 max duration"
required: true
default: "15m"
type: string
target_base_url:
description: "Target base URL. Empty uses Terraform default."
required: false
default: ""
type: string
prometheus_remote_write_url:
description: "Prometheus remote-write URL. Empty uses Terraform default."
required: false
default: ""
type: string

permissions:
id-token: write
contents: read

concurrency:
group: load-test-environment
cancel-in-progress: false

env:
TF_VERSION: "1.10.5"

jobs:
run:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
token: ${{ secrets.GH_PAT }}
persist-credentials: false

- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ vars.AWS_LOAD_TEST_ROLE_ARN }}
aws-region: ap-northeast-2

- uses: hashicorp/setup-terraform@v3
with:
terraform_version: ${{ env.TF_VERSION }}
terraform_wrapper: false

- name: Install jq
run: |
sudo apt-get update
sudo apt-get install -y jq

- name: Run k6 on load generator
run: |
args=(
--vus "${{ inputs.vus }}"
--iterations "${{ inputs.iterations }}"
--max-duration "${{ inputs.max_duration }}"
)

if [ -n "${{ inputs.target_base_url }}" ]; then
args+=(--target-base-url "${{ inputs.target_base_url }}")
fi

if [ -n "${{ inputs.prometheus_remote_write_url }}" ]; then
args+=(--prometheus-remote-write-url "${{ inputs.prometheus_remote_write_url }}")
fi

bash scripts/load_test/run_k6.sh "${args[@]}"
76 changes: 76 additions & 0 deletions .github/workflows/load-test-start.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
name: Load Test Start

on:
workflow_dispatch:
inputs:
switch_stage_to_loadtest:
description: "Restart stage app with dev,loadtest profiles"
required: true
default: true
type: boolean
copy_prod_data:
description: "Copy prod RDS data to load test RDS"
required: true
default: true
type: boolean
load_generator_instance_type:
description: "k6 load generator EC2 instance type"
required: true
default: "c7i.xlarge"
type: string

permissions:
id-token: write
contents: read

concurrency:
group: load-test-environment
cancel-in-progress: false

env:
TF_VERSION: "1.10.5"

jobs:
start:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
token: ${{ secrets.GH_PAT }}
persist-credentials: false

- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ vars.AWS_LOAD_TEST_ROLE_ARN }}
aws-region: ap-northeast-2

- uses: hashicorp/setup-terraform@v3
with:
terraform_version: ${{ env.TF_VERSION }}
terraform_wrapper: false

- name: Install jq
run: |
sudo apt-get update
sudo apt-get install -y jq

- name: Start load test environment
run: |
export TF_VAR_load_generator_instance_type="${{ inputs.load_generator_instance_type }}"

if [ -n "${{ vars.LOAD_GENERATOR_INSTANCE_PROFILE_NAME }}" ]; then
export TF_VAR_load_generator_instance_profile_name="${{ vars.LOAD_GENERATOR_INSTANCE_PROFILE_NAME }}"
fi

args=()

if [ "${{ inputs.switch_stage_to_loadtest }}" = "true" ]; then
args+=(--switch-stage-to-loadtest)
fi

if [ "${{ inputs.copy_prod_data }}" != "true" ]; then
args+=(--skip-data-copy)
fi

bash scripts/load_test/start.sh "${args[@]}"
65 changes: 65 additions & 0 deletions .github/workflows/load-test-stop.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
name: Load Test Stop

on:
workflow_dispatch:
inputs:
restore_stage_dev:
description: "Restart stage app with dev profile"
required: true
default: true
type: boolean
destroy_rds:
description: "Destroy load test Terraform stack"
required: true
default: true
type: boolean

permissions:
id-token: write
contents: read

concurrency:
group: load-test-environment
cancel-in-progress: false

env:
TF_VERSION: "1.10.5"

jobs:
stop:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
token: ${{ secrets.GH_PAT }}
persist-credentials: false

- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ vars.AWS_LOAD_TEST_ROLE_ARN }}
aws-region: ap-northeast-2

- uses: hashicorp/setup-terraform@v3
with:
terraform_version: ${{ env.TF_VERSION }}
terraform_wrapper: false

- name: Install jq
run: |
sudo apt-get update
sudo apt-get install -y jq

- name: Stop load test environment
run: |
args=()

if [ "${{ inputs.restore_stage_dev }}" = "true" ]; then
args+=(--restore-stage-dev)
fi

if [ "${{ inputs.destroy_rds }}" != "true" ]; then
args+=(--skip-terraform-destroy)
fi

bash scripts/load_test/stop.sh "${args[@]}"
7 changes: 7 additions & 0 deletions config/load-test/k6/createPost.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"boardCode": "FREE",
"postCategory": "자유",
"title": "수강신청 어떻게 하나요?",
"content": "수강신청 방법을 잘 모르겠어요.",
"isQuestion": false
}
64 changes: 64 additions & 0 deletions config/load-test/k6/script/set-load-test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#!/usr/bin/env bash
set -euo pipefail

# 작업 디렉터리 설정
WORKDIR="/home/ubuntu"

#######################################################################
# set-load-test.sh
# 사용 예: SQL_FILE_BASENAME=regular.sql ./set-load-test.sh
#######################################################################

# 0. 필수 값 검증
SQL_BASENAME="${SQL_FILE_BASENAME:-regular.sql}"
if [[ -z "$SQL_BASENAME" ]]; then
echo "❌ 사용할 덤프 파일이 입력되지 않았습니다." >&2
echo "SQL_FILE_BASENAME={dump 파일 이름} ./set-load-test.sh 형식으로 인자를 전달해야합니다." >&2
exit 1
fi

SQL_SRC="$WORKDIR/load-test-setting/db/${SQL_BASENAME}"
if [[ ! -f "$SQL_SRC" ]]; then
echo "❌ 덤프 파일이 파일 시스템에 존재하지 않습니다: $SQL_SRC" >&2
exit 2
fi

# 1. 기존 어플리케이션, DB 중지
docker compose -f "$WORKDIR/solid-connection-dev/docker-compose.dev.yml" down \
|| { echo "❌ 어플리케이션 도커 중지 실패: $WORKDIR/solid-connection-dev/docker-compose.dev.yml" >&2; exit 3; }
docker compose -f "$WORKDIR/mysql/docker-compose.mysql.yml" down \
|| { echo "❌ MySQL 도커 중지 실패: $WORKDIR/mysql/docker-compose.mysql.yml" >&2; exit 4; }

# 2. 부하 테스트용 DB 실행
docker compose -f "$WORKDIR/load-test-setting/docker-compose.load-test.yml" up -d \
|| { echo "❌ 부하 테스트용 DB 실행 실패: docker-compose.load-test.yml" >&2; exit 5; }

# 3. MySQL 준비 대기 (최대 30초)
CONTAINER_NAME="load-test-db"
echo "⏳ MySQL이 준비될 때까지 대기 중 (최대 30초)..."
start_time=$(date +%s)
while ! docker exec "$CONTAINER_NAME" sh -c 'mysqladmin ping -h "127.0.0.1" --silent'; do
elapsed=$(( $(date +%s) - start_time ))
if [[ $elapsed -ge 30 ]]; then
echo "❌ MySQL 준비 시간 초과 (30초)" >&2
exit 1
fi
printf "."
sleep 1
done
echo "✔️ MySQL 준비 완료."

# 4. dump 주입
echo "📥 dump 파일 복사 → 컨테이너: $CONTAINER_NAME"
docker cp "$SQL_SRC" "${CONTAINER_NAME}:/tmp/dump.sql" \
|| { echo "❌ dump 복사 실패: $SQL_SRC → $CONTAINER_NAME:/tmp/dump.sql" >&2; exit 6; }

echo "⚙️ dump 이식"
docker exec -i "$CONTAINER_NAME" sh -c 'mysql -u root -proot < /tmp/dump.sql' \
|| { echo "❌ dump 이식 실패: 컨테이너 $CONTAINER_NAME" >&2; exit 7; }

# 5. 어플리케이션 다시 실행
docker compose -f "$WORKDIR/solid-connection-dev/docker-compose.dev.yml" up -d \
|| { echo "❌ 어플리케이션 재시작 실패: $WORKDIR/solid-connection-dev/docker-compose.dev.yml" >&2; exit 8; }

echo "✅ 부하 테스트용 DB에 연결된 어플리케이션 실행 완료!"
53 changes: 53 additions & 0 deletions config/load-test/k6/set_up_xk6.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#!/bin/bash

set -euo pipefail

trap 'echo "xk6 setup failed" >&2' ERR

export GO_VERSION=1.22.2
export BASE_DIR=/home/ubuntu/solid-connection-load-test/k6
export GOROOT=${BASE_DIR}/go
export GOPATH=${BASE_DIR}/go-workspace
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
export XK6_BIN=${GOPATH}/bin/xk6
export K6_OUT=xk6-prometheus-rw
export K6_PROMETHEUS_RW_SERVER_URL=${K6_PROMETHEUS_RW_SERVER_URL:-http://132.145.83.182:9090/api/v1/write}
export K6_PROMETHEUS_RW_TREND_STATS="${K6_PROMETHEUS_RW_TREND_STATS:-p(90),p(95),p(99),avg,min,max}"

{
echo "export BASE_DIR=${BASE_DIR}"
echo "export GOROOT=${GOROOT}"
echo "export GOPATH=${GOPATH}"
echo "export PATH=\$PATH:\$GOROOT/bin:\$GOPATH/bin"
echo "export XK6_BIN=${GOPATH}/bin/xk6"
echo "export K6_OUT=xk6-prometheus-rw"
echo "export K6_PROMETHEUS_RW_SERVER_URL=${K6_PROMETHEUS_RW_SERVER_URL}"
echo "export K6_PROMETHEUS_RW_TREND_STATS=\"${K6_PROMETHEUS_RW_TREND_STATS}\""
} >> ~/.bashrc

echo "Create and enter ${BASE_DIR}"
mkdir -p "$BASE_DIR"
cd "$BASE_DIR"

echo "Download Go ${GO_VERSION}"
curl -OL "https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz"

echo "Extract Go"
tar -xzf "go${GO_VERSION}.linux-amd64.tar.gz"
rm "go${GO_VERSION}.linux-amd64.tar.gz"

echo "Go version: $(go version)"

echo "Install xk6"
go install go.k6.io/xk6/cmd/xk6@latest

echo "xk6 installed: ${XK6_BIN}"
"$XK6_BIN" --help > /dev/null && echo "xk6 executable is available"

echo "Build k6 with Prometheus remote-write output"
"$XK6_BIN" build --with github.com/grafana/xk6-output-prometheus-remote@latest

echo "Build complete: $(pwd)/k6"
ls -lh ./k6

echo "xk6 setup completed"
5 changes: 5 additions & 0 deletions config/load-test/k6/updatePost.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"postCategory": "자유",
"title": "수강신청 어떻게 하나요?",
"content": "수강신청 방법을 잘 알겠어요."
}
Loading
Loading