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
76 changes: 30 additions & 46 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,9 @@ jobs:
id: vars
run: |
if [ "${{ github.ref_name }}" == "main" ]; then
echo "APP_SECRET=APPLICATION" >> $GITHUB_OUTPUT
echo "DOCKER_TAG=latest" >> $GITHUB_OUTPUT
echo "COMPOSE_FILE=docker-compose.prod.yml" >> $GITHUB_OUTPUT
else
echo "APP_SECRET=APPLICATION_STAGING" >> $GITHUB_OUTPUT
echo "DOCKER_TAG=staging" >> $GITHUB_OUTPUT
echo "COMPOSE_FILE=docker-compose.staging.yml" >> $GITHUB_OUTPUT
fi

- name: Remove existing application.yml
run: rm -f src/main/resources/application.yml

- name: Make application.yml
run: |
mkdir -p src/main/resources
if [ "${{ github.ref_name }}" == "main" ]; then
echo "${{ secrets.APPLICATION }}" > src/main/resources/application.yml
else
echo "${{ secrets.APPLICATION_STAGING }}" > src/main/resources/application.yml
fi

- name: Build with Gradle
Expand All @@ -55,39 +39,39 @@ jobs:
docker build -f Dockerfile -t ${{ secrets.DOCKER_REPO }}:${{ steps.vars.outputs.DOCKER_TAG }} .
docker push ${{ secrets.DOCKER_REPO }}:${{ steps.vars.outputs.DOCKER_TAG }}

- name: Deploy_EC2
- name: Copy files to server
uses: appleboy/scp-action@master
with:
host: ${{ secrets.HOST }}
username: ubuntu
key: ${{ secrets.KEY }}
source: "docker-compose.yml,init-db.sql,nginx/,scripts/"
target: /home/ubuntu/cockple

- name: Deploy
uses: appleboy/ssh-action@master
id: deploy
with:
host: ${{ secrets.HOST }}
username: ubuntu
key: ${{ secrets.KEY }}
envs: >-
DB_PASSWORD,
S3_BUCKET,S3_ACCESS_KEY,S3_SECRET_KEY,
KAKAO_CLIENT_ID,KAKAO_CLIENT_SECRET,KAKAO_REDIRECT_URI_PROD,KAKAO_REDIRECT_URI_STAGING,KAKAO_ADMIN_KEY,
JWT_SECRET_KEY
script: |
cd /home/ubuntu/home/monitor
echo "=== 배포 전 상태 ==="
sudo docker ps
sudo docker image prune -f
sudo docker pull ${{ secrets.DOCKER_REPO }}:${{ steps.vars.outputs.DOCKER_TAG }}

if [ "${{ github.ref_name }}" == "main" ]; then
sudo docker stop cockple-app || true
sudo docker rm -f cockple-app || true
if ! sudo docker ps | grep -q cockple-redis; then
echo "Redis(prod)가 죽었음, 재시작 중..."
sudo docker compose -f docker-compose.prod.yml up -d redis
sleep 10
fi
sudo docker compose -f docker-compose.prod.yml up -d cockple-app
else
sudo docker stop cockple-app-staging || true
sudo docker rm -f cockple-app-staging || true
if ! sudo docker ps | grep -q cockple-redis-staging; then
echo "Redis(staging)가 죽었음, 재시작 중..."
sudo docker compose -f docker-compose.staging.yml up -d redis-staging
sleep 10
fi
sudo docker compose -f docker-compose.staging.yml up -d cockple-app-staging
fi

echo "=== 배포 후 상태 ==="
sudo docker ps
chmod +x /home/ubuntu/cockple/scripts/deploy.sh
bash /home/ubuntu/cockple/scripts/deploy.sh \
${{ secrets.DOCKER_REPO }} \
${{ github.ref_name }}
env:
DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
S3_BUCKET: ${{ secrets.S3_BUCKET }}
S3_ACCESS_KEY: ${{ secrets.S3_ACCESS_KEY }}
S3_SECRET_KEY: ${{ secrets.S3_SECRET_KEY }}
KAKAO_CLIENT_ID: ${{ secrets.KAKAO_CLIENT_ID }}
KAKAO_CLIENT_SECRET: ${{ secrets.KAKAO_CLIENT_SECRET }}
KAKAO_REDIRECT_URI_PROD: ${{ secrets.KAKAO_REDIRECT_URI_PROD }}
KAKAO_REDIRECT_URI_STAGING: ${{ secrets.KAKAO_REDIRECT_URI_STAGING }}
KAKAO_ADMIN_KEY: ${{ secrets.KAKAO_ADMIN_KEY }}
JWT_SECRET_KEY: ${{ secrets.JWT_SECRET_KEY }}
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
FROM eclipse-temurin:17-jdk-jammy
FROM eclipse-temurin:17-jre-jammy

COPY build/libs/cockple.demo-0.0.1-SNAPSHOT.jar app.jar

CMD ["java", "-Dspring.profiles.active=dev", "-jar", "app.jar"]
ENTRYPOINT ["java", "-jar", "app.jar"]
119 changes: 119 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
name: cockple

services:
mysql:
image: mysql:8.0
container_name: cockple-mysql
restart: always
ports:
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
TZ: Asia/Seoul
command:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
- --innodb-buffer-pool-size=256M
volumes:
- mysql-data:/var/lib/mysql
- ./init-db.sql:/docker-entrypoint-initdb.d/init-db.sql:ro
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
mem_limit: 512m
memswap_limit: 768m

redis:
image: redis:7-alpine
container_name: cockple-redis
restart: always
command:
- redis-server
- --appendonly
- "yes"
- --maxmemory
- 200mb
- --maxmemory-policy
- allkeys-lru
volumes:
- redis-data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 5
mem_limit: 256m
memswap_limit: 384m

cockple-app:
container_name: cockple-app
image: kanghana1/cockple:latest
restart: always
environment:
JAVA_TOOL_OPTIONS: "-Xms768m -Xmx768m"
SPRING_PROFILES_ACTIVE: prod
DB_PASSWORD: ${DB_PASSWORD}
S3_BUCKET: ${S3_BUCKET}
S3_ACCESS_KEY: ${S3_ACCESS_KEY}
S3_SECRET_KEY: ${S3_SECRET_KEY}
KAKAO_CLIENT_ID: ${KAKAO_CLIENT_ID}
KAKAO_CLIENT_SECRET: ${KAKAO_CLIENT_SECRET}
KAKAO_REDIRECT_URI: ${KAKAO_REDIRECT_URI_PROD}
KAKAO_ADMIN_KEY: ${KAKAO_ADMIN_KEY}
JWT_SECRET_KEY: ${JWT_SECRET_KEY}
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_healthy
mem_limit: 1200m
memswap_limit: 1536m

cockple-app-staging:
container_name: cockple-app-staging
image: kanghana1/cockple:staging
restart: always
environment:
JAVA_TOOL_OPTIONS: "-Xms128m -Xmx512m"
SPRING_PROFILES_ACTIVE: staging
DB_PASSWORD: ${DB_PASSWORD}
S3_BUCKET: ${S3_BUCKET}
S3_ACCESS_KEY: ${S3_ACCESS_KEY}
S3_SECRET_KEY: ${S3_SECRET_KEY}
KAKAO_CLIENT_ID: ${KAKAO_CLIENT_ID}
KAKAO_CLIENT_SECRET: ${KAKAO_CLIENT_SECRET}
KAKAO_REDIRECT_URI: ${KAKAO_REDIRECT_URI_STAGING}
KAKAO_ADMIN_KEY: ${KAKAO_ADMIN_KEY}
JWT_SECRET_KEY: ${JWT_SECRET_KEY}
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_healthy
mem_limit: 1024m
memswap_limit: 1280m

nginx:
image: nginx:stable-alpine
container_name: cockple-nginx
restart: always
ports:
- "80:80"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/conf.d:/etc/nginx/conf.d:ro
depends_on:
- cockple-app
- cockple-app-staging
mem_limit: 64m
memswap_limit: 128m

volumes:
mysql-data:
redis-data:

networks:
default:
name: cockple_network
2 changes: 2 additions & 0 deletions init-db.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
CREATE DATABASE IF NOT EXISTS cockple CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE DATABASE IF NOT EXISTS cockple_staging CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
12 changes: 12 additions & 0 deletions nginx/conf.d/prod.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
server {
listen 80;
server_name cockple.shop;

location / {
proxy_pass http://cockple-app:8080;
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;
}
}
12 changes: 12 additions & 0 deletions nginx/conf.d/staging.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
server {
listen 80;
server_name staging.cockple.shop;

location / {
proxy_pass http://cockple-app-staging:8080;
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;
}
}
25 changes: 25 additions & 0 deletions nginx/nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;

events {
worker_connections 1024;
}

http {
include /etc/nginx/mime.types;
default_type application/octet-stream;

log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent"';

access_log /var/log/nginx/access.log main;

sendfile on;
keepalive_timeout 65;
client_max_body_size 30M;

include /etc/nginx/conf.d/*.conf;
}
42 changes: 42 additions & 0 deletions scripts/deploy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#!/bin/bash

DOCKER_REPO=$1
BRANCH=$2

cd /home/ubuntu/cockple

if [ "$BRANCH" == "main" ]; then
SERVICE="cockple-app"
TAG="latest"
else
SERVICE="cockple-app-staging"
TAG="staging"
fi

cat > .env << EOF
DB_PASSWORD=${DB_PASSWORD}
S3_BUCKET=${S3_BUCKET}
S3_ACCESS_KEY=${S3_ACCESS_KEY}
S3_SECRET_KEY=${S3_SECRET_KEY}
KAKAO_CLIENT_ID=${KAKAO_CLIENT_ID}
KAKAO_CLIENT_SECRET=${KAKAO_CLIENT_SECRET}
KAKAO_REDIRECT_URI_PROD=${KAKAO_REDIRECT_URI_PROD}
KAKAO_REDIRECT_URI_STAGING=${KAKAO_REDIRECT_URI_STAGING}
KAKAO_ADMIN_KEY=${KAKAO_ADMIN_KEY}
JWT_SECRET_KEY=${JWT_SECRET_KEY}
EOF

echo "=== 배포 전 상태 ==="
sudo docker ps

sudo docker compose up -d mysql redis nginx
sudo docker image prune -f
sudo docker pull $DOCKER_REPO:$TAG

sudo docker stop $SERVICE || true
sudo docker rm -f $SERVICE || true

sudo docker compose up -d $SERVICE

echo "=== 배포 후 상태 ==="
sudo docker ps
20 changes: 20 additions & 0 deletions scripts/tunnel.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
@echo off
:: 사용법: scripts\tunnel.bat [GCP_IP]
:: 예시: scripts\tunnel.bat 34.64.xxx.xxx

set GCP_IP=%1

if "%GCP_IP%"=="" (
set /p GCP_IP=GCP IP 입력:
)

echo 터널링 시작: %GCP_IP%
echo MySQL -^> localhost:3306 -^> cockple-mysql:3306
echo Redis -^> localhost:6379 -^> cockple-redis:6379
echo 종료: Ctrl+C

ssh -N ^
-L 3306:localhost:3306 ^
-L 6379:localhost:6379 ^
-i %USERPROFILE%\.ssh\cockple ^
ubuntu@%GCP_IP%
24 changes: 24 additions & 0 deletions scripts/tunnel.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/bin/bash

# GCP 서버를 통해 Docker MySQL/Redis 터널링
# 사용법: ./scripts/tunnel.sh [GCP_IP]
# 예시: ./scripts/tunnel.sh 34.64.xxx.xxx

GCP_IP=${1:-$(cat .tunnel-ip 2>/dev/null)}

if [ -z "$GCP_IP" ]; then
echo "GCP IP를 인자로 전달하거나 .tunnel-ip 파일에 저장하세요."
echo "사용법: ./scripts/tunnel.sh [GCP_IP]"
exit 1
fi

echo "터널링 시작: $GCP_IP"
echo " MySQL → localhost:3306 → cockple-mysql:3306"
echo " Redis → localhost:6379 → cockple-redis:6379"
echo "종료: Ctrl+C"

ssh -N \
-L 3306:localhost:3306 \
-L 6379:localhost:6379 \
-i ~/.ssh/cockple \
ubuntu@$GCP_IP
4 changes: 4 additions & 0 deletions src/main/java/umc/cockple/demo/global/config/RedisConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,13 @@ public class RedisConfig {
@Value("${spring.data.redis.port}")
private int redisPort;

@Value("${spring.data.redis.database:0}")
private int redisDatabase;

@Bean
public RedisConnectionFactory redisConnectionFactory() {
RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration(redisHost, redisPort);
configuration.setDatabase(redisDatabase);
return new LettuceConnectionFactory(configuration);
}

Expand Down
12 changes: 12 additions & 0 deletions src/main/resources/application-local.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
spring:
datasource:
url: jdbc:mysql://localhost:3306/cockple?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Seoul

data:
redis:
host: localhost
database: 0

jpa:
hibernate:
ddl-auto: update
Loading