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
158 changes: 95 additions & 63 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
name: cd

#on:
# push:
# branches:
# - develop
on:
push:
branches:
- develop

permissions:
contents: read
Expand All @@ -29,76 +29,108 @@ jobs:
# Docker 이미지 build 및 push
- name: docker build and push
run: |
echo ${{secrets.APPLICATION_SECRET}} | base64 -d > src/main/resources/application-secret.yml
docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
docker build -t ${{ secrets.DOCKER_USERNAME }}/jolupbisang:latest .
docker push ${{ secrets.DOCKER_USERNAME }}/jolupbisang:latest
docker build -t ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_IMAGE_NAME }}:latest .
docker push ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_IMAGE_NAME }}:latest
docker image prune -a

deploy:
needs: build
runs-on: ubuntu-latest
steps:
# 현재 서버의 상태와 포트 확인 및 업스트림 설정
- name: Set target IP
run: |
STATUS=$(curl -o /dev/null -w "%{http_code}" "http://${{ secrets.EC2_IP }}/env")
echo "STATUS: $STATUS"
if [ $STATUS = 200 ]; then
CURRENT_UPSTREAM=$(curl -s "http://${{ secrets.EC2_IP }}/env")
else
CURRENT_UPSTREAM=green
fi
echo CURRENT_UPSTREAM=$CURRENT_UPSTREAM >> $GITHUB_ENV
if [ $CURRENT_UPSTREAM = blue ]; then
echo "CURRENT_PORT=8080" >> $GITHUB_ENV
echo "STOPPED_PORT=8081" >> $GITHUB_ENV
echo "TARGET_UPSTREAM=green" >> $GITHUB_ENV
else
echo "CURRENT_PORT=8081" >> $GITHUB_ENV
echo "STOPPED_PORT=8080" >> $GITHUB_ENV
echo "TARGET_UPSTREAM=blue" >> $GITHUB_ENV
fi

# Docker 이미지 pull 및 compose up
- name: Docker compose
uses: appleboy/ssh-action@master
with:
username: ${{ secrets.EC2_USERNAME }}
host: ${{ secrets.EC2_IP }}
key: ${{ secrets.EC2_SSH_KEY }}
script_stop: true
script: |
sudo docker pull ${{ secrets.DOCKER_USERNAME }}/jolupbisang:latest
sudo docker-compose -f docker-compose-${{env.TARGET_UPSTREAM}}.yml up -d

# 실행한 서버 Health Check
- name: Check deploy server URL
uses: jtalk/url-health-check-action@v3
with:
url: http://${{ secrets.EC2_IP }}:${{env.STOPPED_PORT}}/env
max-attempts: 5
retry-delay: 10s

# Nginx upstream 변경
- name: Change nginx upstream
uses: appleboy/ssh-action@master
with:
username: ${{ secrets.EC2_USERNAME }}
host: ${{ secrets.EC2_IP }}
key: ${{ secrets.EC2_SSH_KEY }}
script_stop: true
script: |
sudo docker exec -i nginx_server bash -c 'echo "set \$service_url ${{ env.TARGET_UPSTREAM }};" > /etc/nginx/conf.d/service-env.inc && nginx -s reload'

# 기존 서버 중지
- name: Stop current server
# 무중단 배포 실행
- name: Blue-Green Deployment
uses: appleboy/ssh-action@master
with:
username: ${{ secrets.EC2_USERNAME }}
host: ${{ secrets.EC2_IP }}
key: ${{ secrets.EC2_SSH_KEY }}
script_stop: true
script: |
sudo docker stop ${{env.CURRENT_UPSTREAM}}
sudo docker rm ${{env.CURRENT_UPSTREAM}}
# 프로젝트 디렉토리 찾기
if [ -d "/home/${{ secrets.EC2_USERNAME }}/silrok/docker" ]; then
cd /home/${{ secrets.EC2_USERNAME }}/silrok/docker
elif [ -d "~/silrok/docker" ]; then
cd ~/silrok/docker
elif [ -d "/opt/silrok/docker" ]; then
cd /opt/silrok/docker
else
echo "Error: docker directory not found"
exit 1
fi

# 현재 실행 중인 서버 확인
BLUE_RUNNING=$(sudo docker ps --filter "name=blue" --filter "status=running" --format "{{.Names}}" | grep -q "^blue$" && echo "true" || echo "false")
GREEN_RUNNING=$(sudo docker ps --filter "name=green" --filter "status=running" --format "{{.Names}}" | grep -q "^green$" && echo "true" || echo "false")

# 타겟 서버 결정
if [ "$BLUE_RUNNING" = "true" ]; then
CURRENT_UPSTREAM="blue"
CURRENT_PORT=8080
TARGET_UPSTREAM="green"
TARGET_SERVICE="green"
TARGET_PORT=8081
elif [ "$GREEN_RUNNING" = "true" ]; then
CURRENT_UPSTREAM="green"
CURRENT_PORT=8081
TARGET_UPSTREAM="blue"
TARGET_SERVICE="blue"
TARGET_PORT=8080
else
CURRENT_UPSTREAM="none"
CURRENT_PORT=8080
TARGET_UPSTREAM="blue"
TARGET_SERVICE="blue"
TARGET_PORT=8080
fi

echo "Current upstream: $CURRENT_UPSTREAM"
echo "Target upstream: $TARGET_UPSTREAM"
echo "Target port: $TARGET_PORT"

# 환경 변수 설정
export DOCKER_REGISTRY_URL=${{ secrets.DOCKER_USERNAME }}
export SPRING_IMAGE_NAME=${{ secrets.DOCKER_IMAGE_NAME }}
export DOCKER_IMAGE_TAG=latest
export SPRING_ENV=$TARGET_UPSTREAM
export ENV_FILE=.docker.env

# Docker 이미지 pull 및 새 서버 배포
echo "Pulling latest image..."
sudo docker pull ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_IMAGE_NAME }}:latest

echo "Starting $TARGET_SERVICE service..."
sudo docker-compose --env-file .docker.env -f docker-compose.app.yml up -d $TARGET_SERVICE

# 새 서버 Health Check
echo "Waiting for $TARGET_SERVICE to be healthy..."
MAX_ATTEMPTS=30
ATTEMPT=0
while [ $ATTEMPT -lt $MAX_ATTEMPTS ]; do
if curl -sf http://localhost:$TARGET_PORT/actuator/health > /dev/null 2>&1; then
echo "$TARGET_SERVICE is healthy!"
break
fi
ATTEMPT=$((ATTEMPT + 1))
echo "Attempt $ATTEMPT/$MAX_ATTEMPTS: Waiting for $TARGET_SERVICE..."
sleep 10
done

if [ $ATTEMPT -eq $MAX_ATTEMPTS ]; then
echo "Error: $TARGET_SERVICE failed to become healthy"
exit 1
fi

# Nginx upstream 변경 (트래픽 전환)
echo "Switching nginx upstream to $TARGET_UPSTREAM..."
sudo docker exec -i nginx_server bash -c "echo 'set \$service_url $TARGET_UPSTREAM;' > /etc/nginx/conf.d/service-env.inc && nginx -s reload"

# 기존 서버 중지 및 제거
if [ "$CURRENT_UPSTREAM" != "none" ]; then
echo "Stopping old server: $CURRENT_UPSTREAM"
sudo docker stop $CURRENT_UPSTREAM || true
sudo docker rm $CURRENT_UPSTREAM || true
echo "Old server stopped successfully"
fi

echo "Deployment completed successfully!"
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,6 @@ out/
/docker/loki-config.yml
/docker/promtail-config.yml

# GitHub Secrets
.github/secrets.env
/docker/scripts/*
2 changes: 1 addition & 1 deletion docker/docker-compose.app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ services:
container_name: green
environment:
- PROFILES=${SPRING_PROFILE:-local}
- ENV=${SPRING_ENV:-blue}
- ENV=${SPRING_ENV:-green}
env_file:
- "${ENV_FILE:-.env.dev}"
ports:
Expand Down
23 changes: 23 additions & 0 deletions docker/docker-compose.nginx.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
services:
nginx:
image: nginx:${NGINX_VERSION:-1.25-alpine}
container_name: nginx_server
ports:
- "${NGINX_PORT:-80}:80"
volumes:
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
- ./nginx/service-env.inc:/etc/nginx/conf.d/service-env.inc:ro
networks:
- app_network
restart: always
healthcheck:
test: [ "CMD", "nginx", "-t" ]
interval: 30s
timeout: 10s
retries: 3

networks:
app_network:
external: true
name: ${DOCKER_NETWORK_NAME:-combined_network}

16 changes: 16 additions & 0 deletions docker/grafana/provisioning/datasources/loki.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
apiVersion: 1

datasources:
- name: Loki
type: loki
access: proxy
url: http://my-loki:3100
isDefault: false
editable: true
jsonData:
maxLines: 1000
derivedFields:
- datasourceUid: prometheus
matcherRegex: "traceID=(\\w+)"
name: TraceID
url: "$${__value.raw}"
12 changes: 12 additions & 0 deletions docker/grafana/provisioning/datasources/prometheus.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
apiVersion: 1

datasources:
- name: Prometheus
type: prometheus
access: proxy
url: http://my-prometheus:9090
isDefault: true
editable: true
jsonData:
httpMethod: POST
timeInterval: 5s
51 changes: 51 additions & 0 deletions docker/nginx/default.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
upstream blue {
server blue:8080 max_fails=3 fail_timeout=30s;
}

upstream green {
server green:8080 max_fails=3 fail_timeout=30s;
}

# 동적으로 변경되는 upstream 변수
# 이 파일은 cd.yml에서 동적으로 생성됨
include /etc/nginx/conf.d/service-env.inc;

server {
listen 80;
server_name _;

# 로그 설정
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;

# 요청 본문 크기 제한
client_max_body_size 100M;

# 타임아웃 설정
proxy_connect_timeout 600s;
proxy_send_timeout 600s;
proxy_read_timeout 600s;
send_timeout 600s;

# 기본 upstream으로 라우팅 (service-env.inc에서 설정된 값 사용)
location / {
proxy_pass http://$service_url;
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;

# WebSocket 지원
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}

# Health check endpoint
location /health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
}

4 changes: 4 additions & 0 deletions docker/nginx/service-env.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# 이 파일은 cd.yml에서 동적으로 생성됩니다
# 기본값: blue
set $service_url blue;

Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public class SecurityConfig {

private static final String[] PUBLIC_ENDPOINTS = {
"/swagger-resources/**", "/swagger-ui/**", "/webjars/**", "/v3/api-docs/**", //Swagger
"/error", "/env",
"/error",
"/api/auth/**",
"/ws/**"
};
Expand Down
Loading