diff --git a/.deploy/Dockerfile b/.deploy/Dockerfile new file mode 100644 index 0000000..1faad58 --- /dev/null +++ b/.deploy/Dockerfile @@ -0,0 +1,4 @@ +FROM openjdk:17 +ARG JAR_FILE=build/libs/*.jar +COPY ${JAR_FILE} app.jar +ENTRYPOINT ["java","-jar","/app.jar"] \ No newline at end of file diff --git a/.deploy/blue-deploy.yml b/.deploy/blue-deploy.yml new file mode 100644 index 0000000..dda9fe9 --- /dev/null +++ b/.deploy/blue-deploy.yml @@ -0,0 +1,16 @@ +version: '3' + +services: + + web: + container_name: neighbors-blue + image: ${DOCKER_USERNAME}/${DOCKER_REPOSITORY} + expose: + - "8080" + ports: + - "8080:8080" + environment: + - TZ=Asia/Seoul + - DB_URL=${DATASOURCE_URL_LOCAL} + - DB_USERNAME=${DATASOURCE_USERNAME} + - DB_PASSWORD=${DATASOURCE_PASSWORD} \ No newline at end of file diff --git a/.deploy/green-deploy.yml b/.deploy/green-deploy.yml new file mode 100644 index 0000000..62513d5 --- /dev/null +++ b/.deploy/green-deploy.yml @@ -0,0 +1,16 @@ +version: '3' + +services: + + web: + container_name: neighbors-green + image: ${DOCKER_USERNAME}/${DOCKER_REPOSITORY} + expose: + - "8081" + ports: + - "8081:8081" + environment: + - TZ=Asia/Seoul + - DB_URL=${DATASOURCE_URL_LOCAL} + - DB_USERNAME=${DATASOURCE_USERNAME} + - DB_PASSWORD=${DATASOURCE_PASSWORD} \ No newline at end of file diff --git a/.github/workflows/CD.yml b/.github/workflows/CD.yml new file mode 100644 index 0000000..26c17a3 --- /dev/null +++ b/.github/workflows/CD.yml @@ -0,0 +1,86 @@ +name: Blue-Green deployment + +on: + pull_request: + branches: + - main + +jobs: + build: + runs-on: ubuntu-latest + + steps: + + # 레포지토리의 특정 브랜치, 커밋을 가져오는 설정 + # Github Action 라이브러리인 actions/checkout@v3 액션을 사용 + - uses: actions/checkout@v3 + + # JDK 셋업 + # Github Action 라이브러리인 actions/setup-java@v3 액션을 사용 + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'temurin' + + # Gradle 빌드에 필요한 데이터를 캐싱하여 빌드 속도를 향상시키는 설정 + # Github Action 라이브러리인 actions/cache 액션을 사용 + # Gradle 캐시에 의존성, 래퍼 등을 빌드할 때 저장해두었다가 나중에 재빌드할 때 재사용해서 속도를 향상 + - name: Cache Gradle packages + uses: actions/cache@v3 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }} + restore-keys: ${{ runner.os }}-gradle- + + # gradle 파일에 접근할 권한을 부여 + - name: Run chmod to make gradlew executable + run: chmod +x ./gradlew + + # build 작업 수행 + - name: Build with Gradle Wrapper + run: ./gradlew build + + # docker image를 build 하고 docker hub에 push + - name: Docker Build and Push + run: | + sudo docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} + sudo docker build -f ./.deploy/Dockerfile -t ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_REPOSITORY }} . + sudo docker push ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_REPOSITORY }} + + deploy: + needs: build + runs-on: ubuntu-latest + + steps: + # DockerHub에 로그인 + # GitHub Action 라이브러리 사용 + - name: Login to DockerHub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + # .env 파일에 환경 변수 값들 집어넣기 (Github Secret 에 저장한 정보를 .env 파일로 echo) + # 그리고 Blue-Green 배포 스크립트인 deploy.sh 실행 + - name: Run a New Version of the the application on EC2 + uses: appleboy/ssh-action@master + with: + host: ${{ secrets.EC2_HOST }} + username: ubuntu + key: ${{ secrets.EC2_KEY }} + script: | + rm -rf ./.env + touch ./.env + echo "DOCKER_USERNAME=${{ secrets.DOCKER_USERNAME }}" >> ./.env + echo "DOCKER_REPOSITORY=${{ secrets.DOCKER_REPOSITORY }}" >> ./.env + echo "DATASOURCE_URL_LOCAL=${{ secrets.DATASOURCE_URL_LOCAL }}" >> ./.env + echo "DATASOURCE_USERNAME=${{ secrets.DATASOURCE_USERNAME }}" >> ./.env + echo "DATASOURCE_PASSWORD=${{ secrets.DATASOURCE_PASSWORD }}" >> ./.env + cd ~/app/Backend + chmod +x deploy.sh + source deploy.sh + + diff --git a/.gitignore b/.gitignore index c2065bc..fc2fa08 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,5 @@ out/ ### VS Code ### .vscode/ + +.DS_Store diff --git a/deploy.sh b/deploy.sh new file mode 100644 index 0000000..c6acb4d --- /dev/null +++ b/deploy.sh @@ -0,0 +1,67 @@ +#!/bin/bash + +set -e + +ERR_MSG='' + +trap 'echo "Error occured: $ERR_MSG. Exiting deploy script."; exit 1' ERR + +if sudo docker ps --filter "name=blue" --format '{{.ID}}' | grep -E .; then + RUN_TARGET="green" + STOP_TARGET="blue" + WAS_RUN_PORT=8081 + WAS_STOP_PORT=8080 +else + RUN_TARGET="blue" + STOP_TARGET="green" + WAS_RUN_PORT=8080 + WAS_STOP_PORT=8081 +fi + +echo "The $STOP_TARGET version is currently running on the server. Starting the $RUN_TARGET version." + +DOCKER_COMPOSE_FILE="$RUN_TARGET-deploy.yml" +sudo docker-compose -f "$DOCKER_COMPOSE_FILE" pull || { ERR_MSG='Failed to pull docker image'; exit 1; } +sudo docker-compose -f "$DOCKER_COMPOSE_FILE" up -d || { ERR_MSG='Failed to start docker image'; exit 1; } +sleep 50 + +echo "Starting health check for the new version of the application." + +HEALTH_CHECK_PASSED=true +RUN_CONTAINER_IDS=$(sudo docker ps --filter "name=$RUN_TARGET" --quiet --all) + +for CONTAINER_ID in $RUN_CONTAINER_IDS; do + HEALTH_STATUS=$(sudo docker inspect --format "{{.State.Health.Status}}" $CONTAINER_ID) + if [ "$HEALTH_STATUS" != "healthy" ]; then + HEALTH_CHECK_PASSED=false + break + fi +done + +if [ "$HEALTH_CHECK_PASSED" = false ]; then + sudo docker image prune -af + ERR_MSG="Health check failed." + exit 1 +fi + +echo "Health check passed. Reloading nginx to transfer traffic from $STOP_TARGET to $RUN_TARGET." + +NGINX_ID=$(sudo docker ps --filter "name=nginx" --quiet) +NGINX_CONFIG="/etc/nginx/conf.d/default.conf" + +sudo docker exec $NGINX_ID /bin/bash -c "sed -i 's/neighbors-$STOP_TARGET:$WAS_STOP_PORT/neighbors-$RUN_TARGET:$WAS_RUN_PORT/' $NGINX_CONFIG" +sudo docker exec $NGINX_ID /bin/bash -c "nginx -s reload" || { ERR_MSG='Failed to reload nginx'; exit 1; } + +echo "Terminating the $STOP_TARGET applications." + +STOP_CONTAINER_ID=$(sudo docker ps --filter "name=$STOP_TARGET" --quiet) +if [ -n "$STOP_CONTAINER_ID" ]; then + sudo docker stop $STOP_CONTAINER_ID + sudo docker rm $STOP_CONTAINER_ID + sudo docker image prune -af +fi + +rm .env + +echo "Deployment success." +exit 0 \ No newline at end of file