Skip to content
Merged
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
16 changes: 11 additions & 5 deletions .github/workflows/Dev_CD.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,19 +59,25 @@ jobs:
uses: actions/download-artifact@v4
with:
name: app-artifact
path: ~/app
path: ~/app/staging

- name: Download deploy scripts
uses: actions/download-artifact@v4
with:
name: deploy-scripts
path: ~/app/scripts/

- name: Replace application to latest
run: sudo sh ~/app/scripts/replace-new-version.sh
- name: Setup log directory
run: |
sudo mkdir -p /home/ubuntu/logs
sudo chown -R ubuntu:ubuntu /home/ubuntu/logs
chmod 755 /home/ubuntu/logs

- name: Make deploy script executable
run: chmod +x ~/app/scripts/zero-downtime-deploy.sh

- name: Health Check
run: sh ~/app/scripts/health-check.sh
- name: Zero Downtime Deployment
run: sh ~/app/scripts/zero-downtime-deploy.sh

- name: Send Discord Alert on Failure
if: failure()
Expand Down
16 changes: 11 additions & 5 deletions .github/workflows/Prod_CD.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,19 +59,25 @@ jobs:
uses: actions/download-artifact@v4
with:
name: app-artifact
path: ~/app
path: ~/app/staging

- name: Download deploy scripts
uses: actions/download-artifact@v4
with:
name: deploy-scripts
path: ~/app/scripts/

- name: Replace application to latest
run: sudo sh ~/app/scripts/replace-new-version.sh
- name: Setup log directory
run: |
sudo mkdir -p /home/ubuntu/logs
sudo chown -R ubuntu:ubuntu /home/ubuntu/logs
chmod 755 /home/ubuntu/logs

- name: Make deploy script executable
run: chmod +x ~/app/scripts/zero-downtime-deploy.sh

- name: Health Check
run: sh ~/app/scripts/health-check.sh
- name: Zero Downtime Deployment
run: sh ~/app/scripts/zero-downtime-deploy.sh

- name: Send Discord Alert on Failure
if: failure()
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,12 @@ out/
### VS Code ###
.vscode/

### deploy
/letsencrypt/acme.json

### Rest Docs
/src/main/resources/static/docs/openapi3.yaml

### application-local.yml
/src/main/resources/application-local.yml
.serena
16 changes: 10 additions & 6 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,14 @@ dependencies {
runtimeOnly 'com.mysql:mysql-connector-j'
annotationProcessor 'org.projectlombok:lombok'

// Websocket
implementation 'org.springframework.boot:spring-boot-starter-websocket'

// JWT
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
implementation 'io.jsonwebtoken:jjwt-impl:0.11.5'
implementation 'io.jsonwebtoken:jjwt-gson:0.11.5'

// Excel Export
implementation 'org.apache.poi:poi-ooxml:5.2.3'
implementation 'org.apache.poi:poi:5.2.3'

// Logging
implementation 'org.springframework.boot:spring-boot-starter-log4j2'
implementation "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml"
Expand All @@ -60,7 +59,8 @@ dependencies {

// Monitoring
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'io.micrometer:micrometer-registry-prometheus'
implementation 'io.micrometer:micrometer-registry-prometheus' // Before
implementation 'io.micrometer:micrometer-registry-statsd' // After

// DB schema manager
implementation 'org.flywaydb:flyway-mysql'
Expand All @@ -71,7 +71,7 @@ dependencies {
testImplementation("com.navercorp.fixturemonkey:fixture-monkey-starter:1.1.11")

// Rest Docs & Swagger
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0'
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.7.0'
testImplementation 'io.rest-assured:rest-assured:5.5.0'
testImplementation 'org.springframework.restdocs:spring-restdocs-restassured'
testImplementation 'com.epages:restdocs-api-spec-mockmvc:0.18.2'
Expand All @@ -82,6 +82,10 @@ bootJar {
dependsOn("openapi3")
}

jar {
enabled = false
}

jacoco {
toolVersion = '0.8.9'
}
Expand Down
17 changes: 17 additions & 0 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
FROM eclipse-temurin:17-jdk-alpine AS builder
WORKDIR /workspace
ARG JAR_FILE=build/libs/*.jar
COPY ${JAR_FILE} app.jar
RUN java -Djarmode=layertools -jar app.jar extract

FROM eclipse-temurin:17-jre-alpine
WORKDIR /workspace

ADD https://dtdg.co/latest-java-tracer ./dd-java-agent.jar

COPY --from=builder /workspace/dependencies/ ./
COPY --from=builder /workspace/spring-boot-loader/ ./
COPY --from=builder /workspace/snapshot-dependencies/ ./
COPY --from=builder /workspace/application/ ./

ENTRYPOINT ["java", "-javaagent:./dd-java-agent.jar", "org.springframework.boot.loader.launch.JarLauncher"]
54 changes: 54 additions & 0 deletions docker/application/docker-compose.application.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
services:
application:
image: debatetimer/debate_timer:${ENV:-dev}
container_name: application
environment:
- SERVER_FORWARD_HEADERS_STRATEGY=framework
- SPRING_PROFILES_ACTIVE=${ENV:-dev},monitor
- TZ=Asia/Seoul

- DD_AGENT_HOST=datadog-agent
- DD_SERVICE=debate-timer
- DD_ENV=${ENV:-dev}
- DD_VERSION=${APP_VERSION:-1.0.0}
- DD_LOGS_INJECTION=true
- DD_PROFILING_ENABLED=true
- DD_PROFILING_ALLOCATION_ENABLED=true
- DD_PROFILING_HEAP_ENABLED=true

- MANAGEMENT_STATSD_METRICS_EXPORT_ENABLED=true
- MANAGEMENT_STATSD_METRICS_EXPORT_FLAVOR=datadog
- MANAGEMENT_STATSD_METRICS_EXPORT_HOST=datadog-agent
- MANAGEMENT_STATSD_METRICS_EXPORT_PORT=8125
- MANAGEMENT_STATSD_METRICS_EXPORT_PROTOCOL=UDP

networks:
- debate-timer-net

depends_on:
traefik:
condition: service_healthy

healthcheck:
test: [ "CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8083/monitoring/health" ]
interval: 60s
retries: 10
start_period: 450s

labels:
- "traefik.enable=true"
- "traefik.http.routers.application.rule=Host(`api.${ENV:-dev}.debate-timer.com`)"
- "traefik.http.routers.application.entrypoints=websecure"
- "traefik.http.routers.application.tls=true"
- "traefik.http.routers.application.tls.certresolver=myresolver"
- "traefik.http.routers.application.service=application"
- "traefik.http.services.application.loadbalancer.server.port=8080"

- "traefik.http.routers.application-monitor.rule=PathPrefix(`/`)"
- "traefik.http.routers.application-monitor.entrypoints=monitoring"
- "traefik.http.routers.application-monitor.service=application-monitor-svc"
- "traefik.http.services.application-monitor-svc.loadbalancer.server.port=8083"

- "com.datadoghq.ad.logs=[{\"source\": \"java\", \"service\": \"debate-timer\"}]"

restart: unless-stopped
31 changes: 31 additions & 0 deletions docker/datadog/docker-compose.datadog.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
services:
datadog-agent:
image: gcr.io/datadoghq/agent:7
container_name: datadog-agent
environment:
- DD_API_KEY=${DD_API_KEY}
- DD_SITE=us5.datadoghq.com
- DD_HOSTNAME=debate-timer-${ENV:-dev}-server
- DD_APM_ENABLED=true
- DD_APM_NON_LOCAL_TRAFFIC=true
- DD_APM_IGNORE_RESOURCES=/monitoring/health
- DD_DOGSTATSD_NON_LOCAL_TRAFFIC=true
- DD_LOGS_ENABLED=true
- DD_LOGS_CONFIG_CONTAINER_COLLECT_ALL=true
- DD_PROCESS_AGENT_ENABLED=true
- DD_TAGS=env:${ENV:-dev},project:debate-timer
ports:
- "8125:8125/udp" # Metrics (StatsD)
- "8126:8126/tcp" # APM (Trace)
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro # 로그 수집 필수
- /proc/:/host/proc/:ro
- /sys/fs/cgroup/:/host/sys/fs/cgroup/:ro
networks:
- debate-timer-net
healthcheck:
test: ["CMD", "agent", "health"]
interval: 60s
timeout: 30s
retries: 5
start_period: 120s
12 changes: 12 additions & 0 deletions docker/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
name: debate-timer-server

include:
- ./application/docker-compose.application.yml
- ./datadog/docker-compose.datadog.yml
- ./traefik/docker-compose.traefik.yml
- ./webhook/docker-compose.webhook.yml

networks:
debate-timer-net:
name: debate-timer-net
driver: bridge
39 changes: 39 additions & 0 deletions docker/traefik/docker-compose.traefik.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
services:
traefik:
image: traefik:v2.11
container_name: traefik
ports:
- "80:80"
- "443:443"
- "8080:8080"
- "8083:8083"
environment:
- DOCKER_API_VERSION=1.44
command:
- "--api.insecure=true"
- "--ping=true"
- "--log.level=INFO"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"

- "--entrypoints.web.address=:80"
- "--entrypoints.web.http.redirections.entryPoint.to=websecure"
- "--entrypoints.web.http.redirections.entryPoint.scheme=https"
- "--entrypoints.websecure.address=:443"

- "--certificatesresolvers.myresolver.acme.tlschallenge=true"
- "--certificatesresolvers.myresolver.acme.email=debatetimekeeping@gmail.com"
- "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"

- "--entrypoints.monitoring.address=:8083"
healthcheck:
test: ["CMD", "wget", "--spider", "--quiet", "http://localhost:8080/ping"]
interval: 60s
timeout: 10s
retries: 5
start_period: 120s
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ../../letsencrypt:/letsencrypt
networks:
- debate-timer-net
35 changes: 35 additions & 0 deletions docker/webhook/docker-compose.webhook.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
services:
webhook:
build:
context: ..
dockerfile: ./webhook/webhook.Dockerfile
container_name: webhook
user: root
command: ["-verbose", "-hooks=/etc/webhook/hooks.json", "-hotreload"]
environment:
- ENV=${ENV:-dev}
- SECRET_TOKEN=${WEBHOOK_KEY}
labels:
- "traefik.enable=true"
- "traefik.http.routers.webhook.rule=Host(`webhook.${ENV:-dev}.debate-timer.com`)"
- "traefik.http.routers.webhook.entrypoints=websecure"
- "traefik.http.routers.webhook.tls=true"
- "traefik.http.routers.webhook.tls.certresolver=myresolver"
- "traefik.http.services.webhook.loadbalancer.server.port=9000"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /home/ubuntu/.docker:/root/.docker:ro
- ./hooks.json:/etc/webhook/hooks.json
- ../..:/home/ubuntu/debate-timer
networks:
- debate-timer-net
depends_on:
traefik:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "nc -z localhost 9000 || exit 1"]
interval: 120s
timeout: 30s
retries: 5
start_period: 30s
restart: always
38 changes: 38 additions & 0 deletions docker/webhook/hooks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
[
{
"id": "deploy",
"http-methods": ["POST"],
"execute-command": "/home/ubuntu/debate-timer/scripts/deploy/deploy-docker.sh",
"command-working-directory": "/home/ubuntu/debate-timer",
"pass-environment-to-command": [
{
"source": "header",
"name": "X-Deploy-Token",
"envname": "REQUEST_TOKEN"
}
],
"pass-arguments-to-command": [
{ "source": "entire-payload" }
]
},
{
"id": "status",
"http-methods": ["GET"],
"execute-command": "/home/ubuntu/debate-timer/scripts/deploy/check-deploy-status.sh",
"command-working-directory": "/home/ubuntu/debate-timer",
"include-command-output-in-response": true,
"response-headers": [
{
"name": "Content-Type",
"value": "application/json"
}
],
"pass-environment-to-command": [
{
"source": "header",
"name": "X-Deploy-Token",
"envname": "REQUEST_TOKEN"
}
]
}
]
7 changes: 7 additions & 0 deletions docker/webhook/webhook.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
FROM almir/webhook:2.8.3
USER root
RUN apk add --no-cache docker-cli-compose curl bash util-linux git

WORKDIR /etc/webhook

RUN git config --global --add safe.directory /home/ubuntu/debate-timer
34 changes: 34 additions & 0 deletions nginx/api.dev.debate-timer.com
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
upstream debate_timer_backend {
server 127.0.0.1:8080;
keepalive 32;
}

server {
server_name api.dev.debate-timer.com;

location / {
proxy_pass http://debate_timer_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}

listen [::]:443 ssl ipv6only=on; # managed by Certbot
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/api.dev.debate-timer.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/api.dev.debate-timer.com/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

server {
if ($host = api.dev.debate-timer.com) {
return 308 https://$host$request_uri;
} # managed by Certbot

listen 80;
listen [::]:80;
server_name api.dev.debate-timer.com;
return 404; # managed by Certbot
}
Loading
Loading