Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 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
6 changes: 6 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
.git
.github
.gradle
.env
secrets
build
out
logs
*.jfr
*.hprof
gc.log*
Comment on lines 7 to +12
*.iml
.idea
**/.DS_Store
Expand Down
116 changes: 41 additions & 75 deletions .github/workflows/cicd.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
# CI/CD에 필요한 GitHub Secrets (Repo Settings > Secrets and variables > Actions)
# - Docker Hub: DOCKERHUB_USERNAME, DOCKERHUB_TOKEN
# - Server: SERVER_HOST, SERVER_USER, SERVER_SSH_KEY, SERVER_SSH_PORT
# - GitHub repo/submodule access: GITHUB_TOKEN

name: cicd

Expand Down Expand Up @@ -118,18 +121,6 @@ jobs:
IMAGE: ${{ secrets.DOCKERHUB_USERNAME }}/dorumdorum-be:pinpoint
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
FIREBASE_SERVICE_ACCOUNT_B64: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_B64 }}
RDB_USERNAME: ${{ secrets.RDB_USERNAME }}
RDB_URL: ${{ secrets.RDB_URL }}
RDB_PASSWORD: ${{ secrets.RDB_PASSWORD }}
MYSQL_ROOT_PASSWORD: ${{ secrets.MYSQL_ROOT_PASSWORD }}
MYSQL_DATABASE: ${{ secrets.MYSQL_DATABASE }}
JWT_KEY: ${{ secrets.JWT_KEY }}
JWT_ACCESS_EXPIRATION: ${{ secrets.JWT_ACCESS_EXPIRATION }}
JWT_REFRESH_EXPIRATION: ${{ secrets.JWT_REFRESH_EXPIRATION }}
SMTP_USERNAME: ${{ secrets.SMTP_USERNAME }}
SMTP_PASSWORD: ${{ secrets.SMTP_PASSWORD }}
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
CONTAINER_NAME: dorumdorum-be
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO_FULL_NAME: ${{ github.repository }}
Expand All @@ -138,56 +129,11 @@ jobs:
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SERVER_SSH_KEY }}
port: ${{ secrets.SERVER_SSH_PORT }}
envs: IMAGE,DOCKERHUB_USERNAME,DOCKERHUB_TOKEN,FIREBASE_SERVICE_ACCOUNT_B64,RDB_USERNAME,RDB_URL,RDB_PASSWORD,MYSQL_ROOT_PASSWORD,MYSQL_DATABASE,JWT_KEY,JWT_ACCESS_EXPIRATION,JWT_REFRESH_EXPIRATION,SMTP_USERNAME,SMTP_PASSWORD,CONTAINER_NAME,GITHUB_TOKEN,REPO_FULL_NAME,DISCORD_WEBHOOK
envs: IMAGE,DOCKERHUB_USERNAME,DOCKERHUB_TOKEN,CONTAINER_NAME,GITHUB_TOKEN,REPO_FULL_NAME
script_stop: true
script: |
set -e
echo ">>> [Deploy] SSH 연결됨, 환경 변수 정규화 중..."

RDB_USERNAME="$(printf '%s' "$RDB_USERNAME" | tr -d '\r\n')"
RDB_URL="$(printf '%s' "$RDB_URL" | tr -d '\r\n')"
RDB_PASSWORD="$(printf '%s' "$RDB_PASSWORD" | tr -d '\r\n')"
MYSQL_ROOT_PASSWORD="$(printf '%s' "$MYSQL_ROOT_PASSWORD" | tr -d '\r\n')"
MYSQL_DATABASE="$(printf '%s' "$MYSQL_DATABASE" | tr -d '\r\n')"
JWT_KEY="$(printf '%s' "$JWT_KEY" | tr -d '\r\n')"
JWT_ACCESS_EXPIRATION="$(printf '%s' "$JWT_ACCESS_EXPIRATION" | tr -d '\r\n')"
JWT_REFRESH_EXPIRATION="$(printf '%s' "$JWT_REFRESH_EXPIRATION" | tr -d '\r\n')"
SMTP_USERNAME="$(printf '%s' "$SMTP_USERNAME" | tr -d '\r\n')"
SMTP_PASSWORD="$(printf '%s' "$SMTP_PASSWORD" | tr -d '\r\n')"

DEPLOY_PATH="${HOME}/dorumdorum"
mkdir -p "$DEPLOY_PATH"
cd "$DEPLOY_PATH"
echo ">>> [Deploy] DEPLOY_PATH=$DEPLOY_PATH"

# 1) Firebase 서비스 계정 파일 생성 (VM에 저장)
printf '%s' "$FIREBASE_SERVICE_ACCOUNT_B64" | base64 -d > firebase-service-account.json
chmod 600 firebase-service-account.json

# 2) 컨테이너에서 참조할 경로를 env로 고정
FIREBASE_SERVICE_ACCOUNT_PATH="/app/firebase-service-account.json"

# 3) .env 생성
# Pinpoint Web reads Spring datasource keys directly.
printf '%s\n' \
"SPRING_PROFILES_ACTIVE=prod" \
"RDB_USERNAME=$RDB_USERNAME" \
"RDB_URL=$RDB_URL" \
"RDB_PASSWORD=$RDB_PASSWORD" \
"MYSQL_ROOT_PASSWORD=$MYSQL_ROOT_PASSWORD" \
"MYSQL_DATABASE=$MYSQL_DATABASE" \
"SPRING_DATASOURCE_URL=$RDB_URL" \
"SPRING_DATASOURCE_USERNAME=$RDB_USERNAME" \
"SPRING_DATASOURCE_PASSWORD=$RDB_PASSWORD" \
"JWT_KEY=$JWT_KEY" \
"JWT_ACCESS_EXPIRATION=$JWT_ACCESS_EXPIRATION" \
"JWT_REFRESH_EXPIRATION=$JWT_REFRESH_EXPIRATION" \
"SMTP_USERNAME=$SMTP_USERNAME" \
"SMTP_PASSWORD=$SMTP_PASSWORD" \
"DISCORD_WEBHOOK=$DISCORD_WEBHOOK" \
"FIREBASE_SERVICE_ACCOUNT_PATH=$FIREBASE_SERVICE_ACCOUNT_PATH" > .env
chmod 600 .env
echo ">>> [Deploy] .env, firebase-service-account.json 생성 완료"
echo ">>> [Deploy] SSH 연결됨"

docker info >/dev/null 2>&1 || {
echo "Docker socket permission denied for current deploy user."
Expand All @@ -198,8 +144,8 @@ jobs:
echo "$DOCKERHUB_TOKEN" | docker login -u "$DOCKERHUB_USERNAME" --password-stdin
echo ">>> [Deploy] Docker Hub 로그인 완료"

echo ">>> [Deploy] MySQL 이미지 pull 중..."
docker pull mysql:8.0
echo ">>> [Deploy] PostgreSQL 이미지 pull 중..."
docker pull postgres:16-alpine
echo ">>> [Deploy] Redis 이미지 pull 중..."
docker pull redis:7-alpine
echo ">>> [Deploy] Backend 이미지 pull 중..."
Expand All @@ -215,9 +161,14 @@ jobs:
echo ">>> [Deploy] COMPOSE_PATH=$COMPOSE_PATH, Git clone 중..."
rm -rf temp_repo

# Git에서 production 브랜치로 docker-compose.yml, monitoring 가져오기
# Git에서 production 브랜치로 docker-compose.yml, monitoring, secrets submodule 가져오기
git clone --depth 1 -b production "https://x-access-token:${GITHUB_TOKEN}@github.com/${REPO_FULL_NAME}.git" temp_repo
echo ">>> [Deploy] Clone 완료, docker-compose.yml · monitoring 복사 중..."
(
cd temp_repo
git -c url."https://x-access-token:${GITHUB_TOKEN}@github.com/".insteadOf="https://github.com/" \
submodule update --init --recursive --depth 1
)
echo ">>> [Deploy] Clone 완료, docker-compose.yml · monitoring · secrets 복사 중..."

if [ -f "temp_repo/BE/docker-compose.yml" ]; then
cp temp_repo/BE/docker-compose.yml .
Expand Down Expand Up @@ -248,6 +199,19 @@ jobs:
mkdir -p monitoring/prometheus monitoring/grafana/provisioning/dashboards/json monitoring/grafana/provisioning/datasources
fi

rm -rf secrets
if [ -d "temp_repo/BE/secrets" ]; then
cp -R temp_repo/BE/secrets .
echo ">>> [Deploy] BE/secrets 복사 완료"
elif [ -d "temp_repo/secrets" ]; then
cp -R temp_repo/secrets .
echo ">>> [Deploy] secrets 복사 완료"
else
echo "ERROR: secrets submodule directory not found in temp_repo/"
exit 1
fi
chmod 600 secrets/.env secrets/firebase-service-account.json

rm -rf temp_repo
echo ">>> [Deploy] temp_repo 삭제, compose 파일 확인 중..."

Expand All @@ -257,21 +221,23 @@ jobs:
ls -la
exit 1
fi

# .env 파일과 firebase-service-account.json 복사
cp "$DEPLOY_PATH/.env" .
cp "$DEPLOY_PATH/firebase-service-account.json" .
echo ">>> [Deploy] .env, firebase-service-account.json 복사 완료"

if [ ! -f "secrets/.env" ] || [ ! -f "secrets/firebase-service-account.json" ]; then
echo "ERROR: required secret files are missing"
ls -la secrets || true
exit 1
fi

# docker-compose로 모든 서비스 실행
export BACKEND_IMAGE="$IMAGE"
export CONTAINER_NAME="$CONTAINER_NAME"
COMPOSE="docker compose --env-file secrets/.env -f docker-compose.yml"
echo ">>> [Deploy] 기존 컨테이너 정리 (down + 이름으로 강제 제거)..."
docker compose -f docker-compose.yml down --remove-orphans >/dev/null 2>&1 || true
$COMPOSE down --remove-orphans >/dev/null 2>&1 || true
docker rm -f dorumdorum-redis dorumdorum-be dorumdorum-prometheus dorumdorum-grafana zoo1 pinpoint-hbase pinpoint-collector pinpoint-web 2>/dev/null || true

echo ">>> [Deploy] [1/6] ZooKeeper 기동..."
docker compose -f docker-compose.yml up -d zoo1
$COMPOSE up -d zoo1
for i in $(seq 1 30); do
if docker ps --format '{{.Names}}' | grep -q '^zoo1$'; then
echo ">>> [Deploy] ZooKeeper up"
Expand All @@ -281,7 +247,7 @@ jobs:
done

echo ">>> [Deploy] [2/6] HBase 기동..."
docker compose -f docker-compose.yml up -d pinpoint-hbase
$COMPOSE up -d pinpoint-hbase
echo ">>> [Deploy] HBase 초기화 대기..."
HBASE_READY=0
for i in $(seq 1 300); do
Expand All @@ -305,7 +271,7 @@ jobs:
fi

echo ">>> [Deploy] [3/6] Pinpoint Collector 기동..."
docker compose -f docker-compose.yml up -d pinpoint-collector
$COMPOSE up -d pinpoint-collector
COLLECTOR_READY=0
for i in $(seq 1 60); do
if docker logs pinpoint-collector 2>&1 | grep -Eq "Started .*CollectorApp|Started CollectorApp"; then
Expand All @@ -322,7 +288,7 @@ jobs:
fi

echo ">>> [Deploy] [4/6] Pinpoint Web 기동..."
docker compose -f docker-compose.yml up -d pinpoint-web
$COMPOSE up -d pinpoint-web
WEB_READY=0
for i in $(seq 1 60); do
if docker logs pinpoint-web 2>&1 | grep -Eq "Started .*WebApp|Started WebApp|Started .*PinpointWebApplication"; then
Expand All @@ -339,10 +305,10 @@ jobs:
fi

echo ">>> [Deploy] [5/6] Backend 기동..."
docker compose -f docker-compose.yml up -d backend
$COMPOSE up -d backend

echo ">>> [Deploy] [6/6] Redis/Monitoring 기동..."
docker compose -f docker-compose.yml up -d redis prometheus grafana
$COMPOSE up -d redis prometheus grafana

echo ">>> [Deploy] 오래된 이미지 정리 중..."
docker image prune -af --filter "until=168h"
Expand Down
11 changes: 10 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,16 @@ pinpoint-agent/tools/
.env
/analysis/

### JVM profiling artifacts ###
logs/
*.jfr
*.hprof
gc.log*

### k6 load test artifacts ###
load-testing/results/

# macOS artefacts
.DS_Store

docs/
docs/
Comment thread
KoungQ marked this conversation as resolved.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "secrets"]
path = secrets
url = https://github.com/DorumDorum/secrets
4 changes: 3 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ WORKDIR /app

COPY --from=builder /app/build/libs/*.jar app.jar

RUN mkdir -p /app/logs/jfr

EXPOSE 8080

ENTRYPOINT ["sh", "-c", "exec java ${JAVA_OPTS} -Dspring.profiles.active=${SPRING_PROFILES_ACTIVE:-prod} -jar /app/app.jar"]
ENTRYPOINT ["sh", "-c", "DEFAULT_JAVA_OPTS=\"-XX:StartFlightRecording=name=dorumdorum,settings=${JFR_SETTINGS:-profile},disk=true,dumponexit=true,filename=/app/logs/jfr/dorumdorum-${JFR_RUN_ID:-cloud-load-test}.jfr,maxage=${JFR_MAX_AGE:-30m},maxsize=${JFR_MAX_SIZE:-512m}\"; EFFECTIVE_JAVA_OPTS=\"${DEFAULT_JAVA_OPTS} ${JAVA_OPTS:-}\"; exec java ${EFFECTIVE_JAVA_OPTS} -Dspring.profiles.active=${SPRING_PROFILES_ACTIVE:-prod} -jar /app/app.jar"]
6 changes: 4 additions & 2 deletions Dockerfile.pinpoint
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ WORKDIR /app
COPY --from=builder /app/build/libs/*.jar app.jar
COPY --from=builder /app/pinpoint-agent /app/pinpoint-agent

RUN mkdir -p /app/logs/jfr

# Pinpoint Agent JVM 옵션 (환경변수로 Collector IP 등 오버라이드 가능)
ENV PINPOINT_VERSION=2.5.4
ENV PINPOINT_COLLECTOR_IP=pinpoint-collector
Expand All @@ -38,12 +40,12 @@ EXPOSE 8080

# pinpoint-root.config에서 Collector IP 오버라이드 (profiler.transport.grpc.collector.ip)
# -D 옵션으로 전달
ENTRYPOINT ["sh", "-c", "java \
ENTRYPOINT ["sh", "-c", "DEFAULT_JAVA_OPTS=\"-XX:StartFlightRecording=name=dorumdorum,settings=${JFR_SETTINGS:-profile},disk=true,dumponexit=true,filename=/app/logs/jfr/dorumdorum-${JFR_RUN_ID:-cloud-load-test}.jfr,maxage=${JFR_MAX_AGE:-30m},maxsize=${JFR_MAX_SIZE:-512m}\"; EFFECTIVE_JAVA_OPTS=\"${DEFAULT_JAVA_OPTS} ${JAVA_OPTS:-}\"; exec java \
-javaagent:/app/pinpoint-agent/pinpoint-bootstrap-${PINPOINT_VERSION}.jar \
-Dpinpoint.agentId=${PINPOINT_AGENT_ID} \
-Dpinpoint.applicationName=${PINPOINT_APPLICATION_NAME} \
-Dprofiler.transport.grpc.collector.ip=${PINPOINT_COLLECTOR_IP} \
-Dpinpoint.container=true \
${JAVA_OPTS} \
${EFFECTIVE_JAVA_OPTS} \
-Dspring.profiles.active=${SPRING_PROFILES_ACTIVE:-prod} \
-jar /app/app.jar"]
38 changes: 33 additions & 5 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,19 @@ services:
- "8080:8080"
stop_grace_period: 45s
env_file:
- .env
- ./secrets/.env
environment:
- REDIS_HOST=redis
- REDIS_PORT=6379
- PINPOINT_COLLECTOR_IP=pinpoint-collector
- PINPOINT_APPLICATION_NAME=dorumdorum
- PINPOINT_AGENT_ID=${PINPOINT_AGENT_ID:-dorumdorum-backend-${HOSTNAME}}
- PINPOINT_AGENT_ID=${PINPOINT_AGENT_ID:-dorumdorum-backend-local}
- PINPOINT_AGENT_NAME=${PINPOINT_AGENT_NAME:-dorumdorum-be}
Comment on lines +47 to 48
- JAVA_OPTS=-Xms6g -Xmx6g -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -Xlog:gc\*:file=/var/log/gc.log:time,uptime,level,tags:filecount=5,filesize=20m
- JAVA_OPTS=-Xms6g -Xmx6g -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log -XX:+ExitOnOutOfMemoryError -Xlog:gc*,safepoint:file=/var/log/gc.log:time,uptime,level,tags:filecount=10,filesize=50m
volumes:
- ./firebase-service-account.json:/app/firebase-service-account.json:ro
- ./secrets/firebase-service-account.json:/app/firebase-service-account.json:ro
- gc_logs:/var/log
- jfr_logs:/app/logs/jfr
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
depends_on:
postgres:
condition: service_healthy
Expand Down Expand Up @@ -121,7 +122,7 @@ services:
image: pinpointdocker/pinpoint-web:2.5.4
container_name: pinpoint-web
env_file:
- .env
- ./secrets/.env
depends_on:
- pinpoint-hbase
networks:
Expand Down Expand Up @@ -151,12 +152,38 @@ services:
- "--config.file=/etc/prometheus/prometheus.yml"
- "--storage.tsdb.path=/prometheus"
- "--web.enable-lifecycle"
- "--web.enable-remote-write-receiver"
Comment thread
coderabbitai[bot] marked this conversation as resolved.
extra_hosts:
- "host.docker.internal:host-gateway"
restart: unless-stopped
networks:
- dorumdorum-net

k6:
image: ${K6_IMAGE:-grafana/k6:latest}
container_name: dorumdorum-k6
profiles:
- loadtest
environment:
- BASE_URL=${K6_BASE_URL:-http://backend:8080}
- K6_OUT=${K6_OUT:-experimental-prometheus-rw}
- K6_PROMETHEUS_RW_SERVER_URL=${K6_PROMETHEUS_RW_SERVER_URL:-http://prometheus:9090/api/v1/write}
- K6_PROMETHEUS_RW_PUSH_INTERVAL=${K6_PROMETHEUS_RW_PUSH_INTERVAL:-5s}
- K6_PROMETHEUS_RW_TREND_STATS=${K6_PROMETHEUS_RW_TREND_STATS:-p(90),p(95),p(99),min,max,avg}
- K6_PROMETHEUS_RW_STALE_MARKERS=${K6_PROMETHEUS_RW_STALE_MARKERS:-true}
- K6_WEB_DASHBOARD=${K6_WEB_DASHBOARD:-true}
- K6_WEB_DASHBOARD_EXPORT=${K6_WEB_DASHBOARD_EXPORT:-/results/k6-report.html}
volumes:
- ./load-testing/k6:/scripts:ro
- ./load-testing/results:/results
working_dir: /scripts
entrypoint: ["k6"]
Comment thread
KoungQ marked this conversation as resolved.
depends_on:
- backend
- prometheus
networks:
- dorumdorum-net

grafana:
image: grafana/grafana:11.2.0
container_name: dorumdorum-grafana
Expand Down Expand Up @@ -185,6 +212,7 @@ volumes:
hbase_data:
zookeeper_data:
gc_logs:
jfr_logs:

networks:
dorumdorum-net:
Expand Down
16 changes: 16 additions & 0 deletions load-testing/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# k6 Load Testing

This directory is reserved for k6 load-test assets. Test scripts are intentionally not included.

Place scripts under `load-testing/k6/` when needed, then run:

```sh
docker compose --profile loadtest run --rm k6 run --tag testid=gc-baseline /scripts/<script>.js
```

Defaults:

- The application target for scripts is exposed as `BASE_URL=http://backend:8080`.
- k6 metrics are remote-written to Prometheus at `http://prometheus:9090/api/v1/write`.
- The optional k6 web dashboard export path is `/results/k6-report.html`.
- Use a different `testid` tag for each GC tuning experiment to separate runs in Prometheus/Grafana.
1 change: 1 addition & 0 deletions load-testing/k6/.gitkeep
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Loading
Loading