From 27755ebc63e0e0d1752c504c41ccee6d33cd2f53 Mon Sep 17 00:00:00 2001 From: garden-zero Date: Sat, 15 Nov 2025 01:57:22 +0900 Subject: [PATCH 1/6] =?UTF-8?q?fix:=20Green=20=EC=BB=A8=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EB=84=88=20=ED=99=98=EA=B2=BD=EB=B3=80=EC=88=98=EB=A5=BC=20Gre?= =?UTF-8?q?en=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/docker-compose.app.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/docker-compose.app.yml b/docker/docker-compose.app.yml index 60e5f8ff..d028708a 100644 --- a/docker/docker-compose.app.yml +++ b/docker/docker-compose.app.yml @@ -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: From 9613763f655446f798a5375cadfbfaf73c6a259f Mon Sep 17 00:00:00 2001 From: garden-zero Date: Sat, 15 Nov 2025 01:57:54 +0900 Subject: [PATCH 2/6] =?UTF-8?q?chore:=20=EC=93=B0=EC=A7=80=EC=95=8A?= =?UTF-8?q?=EB=8A=94=20=EC=97=94=EB=93=9C=ED=8F=AC=EC=9D=B8=ED=8A=B8=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=20=ED=8C=8C=EC=9D=BC=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EC=A0=9C=EC=99=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/jolupbisang/demo/global/config/SecurityConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/jolupbisang/demo/global/config/SecurityConfig.java b/src/main/java/com/jolupbisang/demo/global/config/SecurityConfig.java index 2d434b65..3ff127ce 100644 --- a/src/main/java/com/jolupbisang/demo/global/config/SecurityConfig.java +++ b/src/main/java/com/jolupbisang/demo/global/config/SecurityConfig.java @@ -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/**" }; From 8cdbbf0d01db88ab74dfabd7e07d46cc4b4aa9c9 Mon Sep 17 00:00:00 2001 From: garden-zero Date: Sat, 15 Nov 2025 01:58:21 +0900 Subject: [PATCH 3/6] =?UTF-8?q?feat:=20nginx=20docker=20compose=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/docker-compose.nginx.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 docker/docker-compose.nginx.yml diff --git a/docker/docker-compose.nginx.yml b/docker/docker-compose.nginx.yml new file mode 100644 index 00000000..e398f44f --- /dev/null +++ b/docker/docker-compose.nginx.yml @@ -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} + From a1ceb88d39c93355c007074c1fa3579267ba35a3 Mon Sep 17 00:00:00 2001 From: garden-zero Date: Sat, 15 Nov 2025 01:58:38 +0900 Subject: [PATCH 4/6] =?UTF-8?q?feat:=20nginx=EC=97=90=EC=84=9C=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=ED=95=A0=20=EC=84=A4=EC=A0=95=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EC=84=A0=EC=96=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/nginx/default.conf | 51 ++++++++++++++++++++++++++++++++++++ docker/nginx/service-env.inc | 4 +++ 2 files changed, 55 insertions(+) create mode 100644 docker/nginx/default.conf create mode 100644 docker/nginx/service-env.inc diff --git a/docker/nginx/default.conf b/docker/nginx/default.conf new file mode 100644 index 00000000..9598e180 --- /dev/null +++ b/docker/nginx/default.conf @@ -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; + } +} + diff --git a/docker/nginx/service-env.inc b/docker/nginx/service-env.inc new file mode 100644 index 00000000..c5d4d330 --- /dev/null +++ b/docker/nginx/service-env.inc @@ -0,0 +1,4 @@ +# 이 파일은 cd.yml에서 동적으로 생성됩니다 +# 기본값: blue +set $service_url blue; + From 845dc20cd92fb829c5413ddedce633a90fd28bd5 Mon Sep 17 00:00:00 2001 From: garden-zero Date: Sat, 15 Nov 2025 01:59:25 +0900 Subject: [PATCH 5/6] =?UTF-8?q?feat:=20=EB=AC=B4=EC=A4=91=EB=8B=A8=20?= =?UTF-8?q?=EB=B0=B0=ED=8F=AC=20=ED=99=9C=EC=84=B1=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/cd.yml | 158 +++++++++++++++++++++++---------------- 1 file changed, 95 insertions(+), 63 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 5b924c0f..885820df 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -1,9 +1,9 @@ name: cd -#on: -# push: -# branches: -# - develop +on: + push: + branches: + - develop permissions: contents: read @@ -29,70 +29,17 @@ 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 }} @@ -100,5 +47,90 @@ jobs: 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!" From 163ab7d265944ae2e126a7d6e4ca095801ec8d88 Mon Sep 17 00:00:00 2001 From: garden-zero Date: Sat, 15 Nov 2025 02:08:14 +0900 Subject: [PATCH 6/6] =?UTF-8?q?chore:=20=ED=99=98=EA=B2=BD=20=EB=B3=80?= =?UTF-8?q?=EC=88=98=20gitignore=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 +++ docker/grafana/provisioning/datasources/loki.yml | 16 ++++++++++++++++ .../provisioning/datasources/prometheus.yml | 12 ++++++++++++ 3 files changed, 31 insertions(+) create mode 100644 docker/grafana/provisioning/datasources/loki.yml create mode 100644 docker/grafana/provisioning/datasources/prometheus.yml diff --git a/.gitignore b/.gitignore index 665a5d7e..29e06646 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,6 @@ out/ /docker/loki-config.yml /docker/promtail-config.yml +# GitHub Secrets +.github/secrets.env +/docker/scripts/* diff --git a/docker/grafana/provisioning/datasources/loki.yml b/docker/grafana/provisioning/datasources/loki.yml new file mode 100644 index 00000000..43f7a874 --- /dev/null +++ b/docker/grafana/provisioning/datasources/loki.yml @@ -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}" \ No newline at end of file diff --git a/docker/grafana/provisioning/datasources/prometheus.yml b/docker/grafana/provisioning/datasources/prometheus.yml new file mode 100644 index 00000000..4ad96bbe --- /dev/null +++ b/docker/grafana/provisioning/datasources/prometheus.yml @@ -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 \ No newline at end of file