From 2d3bd793368cdd7f5ff6e43c76c481d1b31908a7 Mon Sep 17 00:00:00 2001 From: Yejun Park <80024278+jun02160@users.noreply.github.com> Date: Sat, 30 Nov 2024 11:05:32 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20coffee=20chat=20=EC=B5=9C=EC=8B=A0?= =?UTF-8?q?=EC=88=9C=20=EC=A0=95=EB=A0=AC=20=EC=B6=94=EA=B0=80=20(#558)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit related issue #557 --- .../repository/coffeechat/CoffeeChatRepositoryCustomImpl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/sopt/makers/internal/member/repository/coffeechat/CoffeeChatRepositoryCustomImpl.java b/src/main/java/org/sopt/makers/internal/member/repository/coffeechat/CoffeeChatRepositoryCustomImpl.java index 4a3a4930..adef4e7a 100644 --- a/src/main/java/org/sopt/makers/internal/member/repository/coffeechat/CoffeeChatRepositoryCustomImpl.java +++ b/src/main/java/org/sopt/makers/internal/member/repository/coffeechat/CoffeeChatRepositoryCustomImpl.java @@ -96,6 +96,7 @@ public List findSearchCoffeeChatInfo(Long memberId, CoffeeCha .leftJoin(member).on(coffeeChat.member.id.eq(member.id)) .leftJoin(memberCareer).on(coffeeChat.member.id.eq(memberCareer.memberId)) .where(builder) + .orderBy(coffeeChat.createdAt.desc()) .fetch(); } From 8529719a29c8d10501a9cddc8524ad281550c34a Mon Sep 17 00:00:00 2001 From: Yejun Park <80024278+jun02160@users.noreply.github.com> Date: Sun, 15 Dec 2024 18:39:18 +0900 Subject: [PATCH 2/2] [Feat] update dev server deploy script (#562) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: update dev server deploy script related issue #561 * fix: aws access key 수정 related issue #561 * fix: ecr login command related issue #561 * fix: remove 'amd64' tag related issue #561 * fix: remove 'amd64' tag related issue #561 * fix: ECR_REPO env를 사용하도록 수정 related issue #561 * fix: env 할당 방식 변경 related issue #561 * fix: env 할당 방식 변경 related issue #561 * fix: change platform related issue #561 * fix: ECR_REPO env 추가 related issue #561 * chore: ecr repo rollback * fix: ECR_HOST, ECR_REPO env 변경 * fix: ECR_HOST, ECR_REPO env 변경 * fix: cli만 사용하도록 변경 * fix: directory 명시 * fix: scp debugging - directory 수정 * fix: env 수정 * fix: docker -> docker-compose로 수정 * fix: docker -> docker-compose로 수정 - scp debug mode off related issue #561 * feat: port switching 추가 related issue #561 * fix: add health check sleep related issue #561 * refactor: script 공통 로직 function으로 최소화 related issue #561 * refactor: script 파일 분리 related issue #561 * fix: health check error related issue #561 * chore: test용 trigger 제거 related issue #561 * feat: apply to production workflow related issue #561 * chore: remove appspec.yml, code deploy script related issue #561 * chore: workflow name rollback related issue #561 * chore: region 'ap-northeast-2'로 변경 related issue #561 * chore: add end line related issue #561 * test: trigger 추가 related issue #561 * fix: ap-northeast-2 -> us-east-1 로 변경 related issue #561 * chore: remove trigger for test related issue #561 --- .github/workflows/cd-to-dev.yml | 84 +++++++++++++++++++---------- .github/workflows/cd-to-prod.yml | 81 ++++++++++++++++++---------- .github/workflows/ci.yml | 5 +- Dockerfile | 7 +++ appspec.yml | 19 ------- aws/start.sh | 8 --- aws/stop.sh | 3 -- aws/valid.sh | 17 ------ docker-compose.yml | 21 ++++++++ scripts/deploy.sh | 90 ++++++++++++++++++++++++++++++++ scripts/deploy_container.sh | 13 +++++ scripts/health_check.sh | 32 ++++++++++++ scripts/nginx_reload.sh | 12 +++++ scripts/stop_container.sh | 9 ++++ 14 files changed, 297 insertions(+), 104 deletions(-) create mode 100644 Dockerfile delete mode 100644 appspec.yml delete mode 100644 aws/start.sh delete mode 100644 aws/stop.sh delete mode 100644 aws/valid.sh create mode 100644 docker-compose.yml create mode 100644 scripts/deploy.sh create mode 100644 scripts/deploy_container.sh create mode 100644 scripts/health_check.sh create mode 100644 scripts/nginx_reload.sh create mode 100644 scripts/stop_container.sh diff --git a/.github/workflows/cd-to-dev.yml b/.github/workflows/cd-to-dev.yml index da351375..df29c264 100644 --- a/.github/workflows/cd-to-dev.yml +++ b/.github/workflows/cd-to-dev.yml @@ -17,6 +17,12 @@ on: push: branches: [ develop ] +env: + SPRING_PROFILES_ACTIVE: dev + ECR_APP_NAME: ${{ secrets.ECR_APP_NAME }}-dev + ECR_REPO: ${{ secrets.ECR_HOST }}/${{ secrets.ECR_APP_NAME }}-dev + ECR_HOST: ${{ secrets.ECR_HOST }} + jobs: build: name: CD Pipeline @@ -35,12 +41,13 @@ jobs: - name: 'Check Java Version' run: | java --version + echo 'ecr repo is $ECR_REPO' - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v1 + - name: 'Configure AWS credentials' + uses: aws-actions/configure-aws-credentials@v2 with: - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID__TEAM_PLAYGROUND }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY__TEAM_PLAYGROUND }} aws-region: ap-northeast-2 - name: 'Get application.yml from AWS S3' @@ -54,37 +61,58 @@ jobs: aws s3 cp \ --region ap-northeast-2 \ s3://sopt-makers-internal/dev/deploy/${{ secrets.APPLE_KEY }} src/main/resources/static/${{ secrets.APPLE_KEY }} - - - name: Grant execute permission for gradlew - run: chmod +x ./gradlew - shell: bash - - name: Build with Gradle - run: ./gradlew clean build -x test + - name: 'Build with Gradle' + run: | + chmod +x ./gradlew + ./gradlew clean build -x test shell: bash - - name: Get current time + - name: 'Login to ECR' + run: | + aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin $ECR_HOST + + - name: 'Docker Image Build & Push' + run: | + docker build -t $ECR_APP_NAME . + docker tag $ECR_APP_NAME:latest $ECR_REPO + docker push $ECR_REPO + + - name: 'Send docker-compose.yml to EC2 Instance' + uses: appleboy/scp-action@master + with: + host: ${{ secrets.DEV_RELEASE_SERVER_IP }} + username: ${{ secrets.RELEASE_SERVER_USER }} + key: ${{ secrets.DEV_RELEASE_SERVER_KEY }} + source: "./docker-compose.yml" + target: "/home/ec2-user/app/" + + - name: 'Send deploy script to EC2 Instance' + uses: appleboy/scp-action@master + with: + host: ${{ secrets.DEV_RELEASE_SERVER_IP }} + username: ${{ secrets.RELEASE_SERVER_USER }} + key: ${{ secrets.DEV_RELEASE_SERVER_KEY }} + source: "./scripts/" + target: "/home/ec2-user/app/" + + - name: 'Get current time' uses: 1466587594/get-current-time@v2 id: current-time with: format: YYYY-MM-DDTHH-mm-ss utcOffset: "+09:00" - - name: Spring Boot env setting - run: sudo sed -i 's/-Dspring.profiles.active=dev/-Dspring.profiles.active=dev/g' ./aws/start.sh - - - name: Check env profile - run: cat ./aws/start.sh + - name: 'Docker Container Run' + uses: appleboy/ssh-action@master - - name: Make zip file - run: zip -qq -r ./$GITHUB_SHA.zip . - shell: bash - - - name: Upload to S3 - run: aws s3 cp --region ap-northeast-2 ./$GITHUB_SHA.zip s3://sopt-makers-internal/dev/deploy/jar/$GITHUB_SHA.zip - - - name: Code Deploy - run: aws deploy create-deployment --application-name internal - --deployment-config-name CodeDeployDefault.OneAtATime - --deployment-group-name dev - --s3-location bucket=sopt-makers-internal,bundleType=zip,key=dev/deploy/jar/$GITHUB_SHA.zip + with: + host: ${{ secrets.DEV_RELEASE_SERVER_IP }} + username: ${{ secrets.RELEASE_SERVER_USER }} + key: ${{ secrets.DEV_RELEASE_SERVER_KEY }} + script: | + cd ~ + sudo docker pull $ECR_REPO + sudo chmod +x ./app/scripts/*.sh + ./app/scripts/deploy.sh + docker image prune -f diff --git a/.github/workflows/cd-to-prod.yml b/.github/workflows/cd-to-prod.yml index bcd8f2ae..12f8704f 100644 --- a/.github/workflows/cd-to-prod.yml +++ b/.github/workflows/cd-to-prod.yml @@ -17,6 +17,12 @@ on: push: branches: [ main ] +env: + SPRING_PROFILES_ACTIVE: prod + ECR_APP_NAME: ${{ secrets.ECR_APP_NAME }}-prod + ECR_REPO: ${{ secrets.ECR_HOST }}/${{ secrets.ECR_APP_NAME }}-prod + ECR_HOST: ${{ secrets.ECR_HOST }} + jobs: build: name: CD Pipeline @@ -36,11 +42,11 @@ jobs: run: | java --version - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v1 + - name: 'Configure AWS credentials' + uses: aws-actions/configure-aws-credentials@v2 with: - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID__TEAM_PLAYGROUND }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY__TEAM_PLAYGROUND }} aws-region: ap-northeast-2 - name: 'Get application.yml from AWS S3' @@ -55,36 +61,57 @@ jobs: --region ap-northeast-2 \ s3://sopt-makers-internal/prod/deploy/${{ secrets.APPLE_KEY }} src/main/resources/static/${{ secrets.APPLE_KEY }} - - name: Grant execute permission for gradlew - run: chmod +x ./gradlew + - name: 'Build with Gradle' + run: | + chmod +x ./gradlew + ./gradlew clean build -x test shell: bash - - name: Build with Gradle - run: ./gradlew clean build -x test - shell: bash + - name: 'Login to ECR' + run: | + aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin $ECR_HOST + + - name: 'Docker Image Build & Push' + run: | + docker build -t $ECR_APP_NAME . + docker tag $ECR_APP_NAME:latest $ECR_REPO + docker push $ECR_REPO + + - name: 'Send docker-compose.yml to EC2 Instance' + uses: appleboy/scp-action@master + with: + host: ${{ secrets.PROD_RELEASE_SERVER_IP }} + username: ${{ secrets.RELEASE_SERVER_USER }} + key: ${{ secrets.PROD_RELEASE_SERVER_KEY }} + source: "./docker-compose.yml" + target: "/home/ec2-user/app/" + + - name: 'Send deploy script to EC2 Instance' + uses: appleboy/scp-action@master + with: + host: ${{ secrets.PROD_RELEASE_SERVER_IP }} + username: ${{ secrets.RELEASE_SERVER_USER }} + key: ${{ secrets.PROD_RELEASE_SERVER_KEY }} + source: "./scripts/" + target: "/home/ec2-user/app/" - - name: Get current time + - name: 'Get current time' uses: 1466587594/get-current-time@v2 id: current-time with: format: YYYY-MM-DDTHH-mm-ss utcOffset: "+09:00" - - name: Spring Boot env setting - run: sudo sed -i 's/-Dspring.profiles.active=dev/-Dspring.profiles.active=prod/g' ./aws/start.sh + - name: 'Docker Container Run' + uses: appleboy/ssh-action@master - - name: Check env profile - run: cat ./aws/start.sh - - - name: Make zip file - run: zip -qq -r ./$GITHUB_SHA.zip . - shell: bash - - - name: Upload to S3 - run: aws s3 cp --region ap-northeast-2 ./$GITHUB_SHA.zip s3://sopt-makers-internal/prod/deploy/jar/$GITHUB_SHA.zip - - - name: Code Deploy - run: aws deploy create-deployment --application-name internal - --deployment-config-name CodeDeployDefault.OneAtATime - --deployment-group-name prod - --s3-location bucket=sopt-makers-internal,bundleType=zip,key=prod/deploy/jar/$GITHUB_SHA.zip + with: + host: ${{ secrets.PROD_RELEASE_SERVER_IP }} + username: ${{ secrets.RELEASE_SERVER_USER }} + key: ${{ secrets.PROD_RELEASE_SERVER_KEY }} + script: | + cd ~ + sudo docker pull $ECR_REPO + sudo chmod +x ./app/scripts/*.sh + ./app/scripts/deploy.sh + docker image prune -f diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 890fa8ac..ee1bfa81 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,11 +10,12 @@ jobs: steps: - name: 'Checkout' - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: 'Set up JDK 17' - uses: actions/setup-java@v1 + uses: actions/setup-java@v3 with: + distribution: 'corretto' java-version: 17 - name: 'Grant execute permission for gradlew' diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..e535107f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,7 @@ +FROM amazoncorretto:17 +WORKDIR /app +COPY ./build/libs/internal-0.0.1-SNAPSHOT.jar /app/APPLICATION.jar + +ENV SPRING_PROFILES_ACTIVE=dev + +CMD ["java", "-Duser.timezone=Asia/Seoul", "-jar", "-Dspring.profiles.active=${SPRING_PROFILES_ACTIVE}", "APPLICATION.jar"] diff --git a/appspec.yml b/appspec.yml deleted file mode 100644 index 5203c308..00000000 --- a/appspec.yml +++ /dev/null @@ -1,19 +0,0 @@ -version: 0.0 -os: linux - -files: - - source: / - destination: /home/ec2-user/app # 인스턴스에서 파일이 저장될 위치 -hooks: - ApplicationStop: - - location: aws/stop.sh - timeout: 60 - runas: root - AfterInstall: - - location: aws/start.sh - timeout: 60 - runas: root - ValidateService: - - location: aws/valid.sh - timeout: 100 - runas: root \ No newline at end of file diff --git a/aws/start.sh b/aws/start.sh deleted file mode 100644 index 104eef93..00000000 --- a/aws/start.sh +++ /dev/null @@ -1,8 +0,0 @@ -REPOSITORY=/home/ec2-user/app -cd $REPOSITORY - -JAR_NAME=$(ls $REPOSITORY/build/libs/ | grep '.jar' | tail -n 1) -JAR_PATH=$REPOSITORY/build/libs/$JAR_NAME - -echo "> $JAR_PATH 배포" #3 -nohup java -jar -Dspring.profiles.active=dev $REPOSITORY/build/libs/internal-0.0.1-SNAPSHOT.jar >nohup.out 2>&1 /dev/null | head -n 1 | awk -F" " '{print$3}'` -if [[ "$root" = "root" ]]; then - echo "port OK_1" -else - echo "port Fail_1" - sleep 20 - - root_nxt=`sudo lsof -i -P -n | grep LISTEN | grep 8080 2>/dev/null | head -n 1 | awk -F" " '{print$3}'` - if [[ "$root_nxt" = "root" ]]; then - echo "port OK_2" - else - echo "port Fail_2" - exit 1 - fi -fi \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..f4f3f377 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,21 @@ +version: '3.8' +services: + playground-blue: + image: ${ECR_REPO} + expose: + - 8080 + ports: + - "8080:8080" + environment: + - TZ=Asia/Seoul + - SPRING_PROFILES_ACTIVE=${SPRING_PROFILES_ACTIVE} + + playground-green: + image: ${ECR_REPO} + expose: + - 8080 + ports: + - "8081:8080" + environment: + - TZ=Asia/Seoul + - SPRING_PROFILES_ACTIVE=${SPRING_PROFILES_ACTIVE} diff --git a/scripts/deploy.sh b/scripts/deploy.sh new file mode 100644 index 00000000..e131cacd --- /dev/null +++ b/scripts/deploy.sh @@ -0,0 +1,90 @@ +#!/bin/bash +cd /home/ec2-user/app + +# Source the external scripts to load their functions +source /home/ec2-user/app/scripts/health_check.sh +source /home/ec2-user/app/scripts/deploy_container.sh +source /home/ec2-user/app/scripts/nginx_reload.sh +source /home/ec2-user/app/scripts/stop_container.sh + +ALL_PORTS=("8080","8081") + +# Check the running container name +AVAILABLE_PORT=() +DOCKER_PS_OUTPUT=$(docker ps | grep playground) +RUNNING_CONTAINER_NAME=$(echo "$DOCKER_PS_OUTPUT" | awk '{print $NF}') +RUNNING_SERVER_PORT="" + +check_running_container() { + if [[ ${RUNNING_CONTAINER_NAME} =~ "playground-blue" ]]; then + echo "Running Port: playground-blue (:8080)" + RUNNING_SERVER_PORT=8080 + elif [[ ${RUNNING_CONTAINER_NAME} =~ "playground-green" ]]; then + echo "Running Port: playground-green (:8081)" + RUNNING_SERVER_PORT=8081 + else + echo "Running Port: None" + fi +} + +# Get the available ports by excluding the currently running port +get_available_ports() { + for item in "${ALL_PORTS[@]}"; do + if [ "$item" != "${RUNNING_SERVER_PORT}" ]; then + AVAILABLE_PORT+=("$item") + fi + done +} + + +### -- + +check_running_container +get_available_ports + +# If no available ports, exit the script +if [ ${#AVAILABLE_PORT[@]} -eq 0 ]; then + echo "Not exists available ports." + exit 1 +fi + + +# Green Up +if [ "${RUNNING_SERVER_PORT}" == "8080" ]; then + CURRENT_SERVER_PORT=8081 + + # function call (container name, current port) + deploy_container "playground-green" ${CURRENT_SERVER_PORT} + + if ! health_check ${CURRENT_SERVER_PORT}; then + echo "❌ Health Check failed ..." + stop_container "playground-green" + exit 1 + fi + reload_nginx ${CURRENT_SERVER_PORT} + stop_container "playground-blue" + +# Blue up +else + CURRENT_SERVER_PORT=8080 + + deploy_container "playground-blue" ${CURRENT_SERVER_PORT} + + if ! health_check ${CURRENT_SERVER_PORT}; then + echo "❌ Health Check failed ..." + stop_container "playground-blue" + exit 1 + fi + reload_nginx ${CURRENT_SERVER_PORT} + stop_container "playground-green" +fi + + +# Final health check through Nginx to confirm the server change +echo "▶️ Final health check applied nginx port switching ..." +if ! health_check ${CURRENT_SERVER_PORT}; then + echo "❌ Server change failed ..." + exit 1 +fi + +echo "✅ Server change successful 👍" diff --git a/scripts/deploy_container.sh b/scripts/deploy_container.sh new file mode 100644 index 00000000..4bcd9547 --- /dev/null +++ b/scripts/deploy_container.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# Deploy the container using docker-compose +deploy_container() { + local CONTAINER_NAME=$1 + local PORT=$2 + + echo "▶️ Switching to ${CONTAINER_NAME} at Port ${PORT} ..." + echo "docker-compose pull & up ..." + + docker-compose pull ${CONTAINER_NAME} + docker-compose up -d ${CONTAINER_NAME} +} diff --git a/scripts/health_check.sh b/scripts/health_check.sh new file mode 100644 index 00000000..b20d2489 --- /dev/null +++ b/scripts/health_check.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +HEALTH_CHECK_URL=/actuator/health + +# Health check to verify if the server is up +health_check() { + local PORT=$1 + + echo "▶️ Start health check after 15 seconds" + sleep 15 + + for retry_count in $(seq 1 $RETRIES); do + echo "Health Check on Port ${PORT} ..." + sleep 3 + + RESPONSE=$(curl -s http://localhost:${PORT}${HEALTH_CHECK_URL}) + UP_COUNT=$(echo $RESPONSE | grep 'UP' | wc -l) + echo "Health Check Response: ${RESPONSE}" + + if [ $UP_COUNT -ge 1 ]; then + echo "✅ Success Health check!" + break; + else + echo "Health check response is empty or not status 'UP'" + fi + + if [ $retry_count -eq $RETRIES ]; then + echo "Health check failed after $RETRIES attempts." + return 1 + fi + done +} diff --git a/scripts/nginx_reload.sh b/scripts/nginx_reload.sh new file mode 100644 index 00000000..03bcf196 --- /dev/null +++ b/scripts/nginx_reload.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +# Reload Nginx to apply port switching +reload_nginx() { + local PORT=$1 + + echo "▶️ Nginx Reload (Port switching applied) ..." + + echo "set \$service_url http://127.0.0.1:${PORT};" | sudo tee /etc/nginx/conf.d/service-url.inc + sudo nginx -s reload + echo "Current running Port after switching: $(sudo cat /etc/nginx/conf.d/service-url.inc)" +} diff --git a/scripts/stop_container.sh b/scripts/stop_container.sh new file mode 100644 index 00000000..1366fb14 --- /dev/null +++ b/scripts/stop_container.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +# Stop the specified container +stop_container() { + local CONTAINER_NAME=$1 + + echo "▶️ Stopping ${CONTAINER_NAME} Container" + docker-compose stop ${CONTAINER_NAME} +}