diff --git a/.github/workflows/app-cd-dev.yml b/.github/workflows/app-cd-dev.yml index d0ff85b8..77aeb0e0 100644 --- a/.github/workflows/app-cd-dev.yml +++ b/.github/workflows/app-cd-dev.yml @@ -1,8 +1,10 @@ name: ⚙️ MAKERS-DEV-APP-DEPLOY +# 해당 스크립트를 사용하지 않고 있는 상태에서 스크립트 수정이 진행됨. 추후 스크립트 사용 시 점검 필요 on: + workflow_dispatch: push: -# branches: [ dev ] +# branches: [ 'dev' ] env: SPRING_PROFILES_ACTIVE: dev @@ -11,100 +13,117 @@ env: ECR_HOST: ${{ secrets.ECR_HOST }} jobs: - build: + build-and-deploy: name: CD Pipeline runs-on: ubuntu-latest steps: - - name: checkout + - name: Checkout Source uses: actions/checkout@v3 - - name: set up JDK 21 + - name: Set up JDK 21 uses: actions/setup-java@v3 with: distribution: 'corretto' java-version: '21' - - name: touch yml files + - name: Gradle Build run: | - touch ./src/main/resources/application-dev.yml - touch ./src/test/resources/application-test.yml - - shell: bash - - - name: copy application.yml files - run: | - echo "${{ secrets.APPLICATION_DEV_YML }}" > ./src/main/resources/application-dev.yml - echo "${{ secrets.APPLICATION_TEST_YML }}" > ./src/test/resources/application-test.yml + chmod +x ./gradlew + ./gradlew clean build -x test - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v2 + uses: aws-actions/configure-aws-credentials@v4 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_KEY }} - aws-region: ap-northeast-2 + aws-region: ${{ secrets.AWS_REGION }} - - name: 🐘Gradle Build - run: | - chmod +x ./gradlew - ./gradlew clean build -x test - - - name: Login to ECR + - name: Login to ECR Public run: | aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin $ECR_HOST - - name: Delete previous ECR images - run: | - echo "Deleting all images from ECR repository: $ECR_APP_NAME" - aws ecr-public batch-delete-image \ - --region us-east-1 \ - --repository-name $ECR_APP_NAME \ - --image-ids "$(aws ecr-public describe-images --region us-east-1 --repository-name $ECR_APP_NAME --query 'imageDetails[*].{imageDigest:imageDigest}' --output json)" \ - || echo "No images to delete or repository is empty" - - - name: 🐳Docker Image Build & Push + - name: Docker Image Build & Push + env: + GITHUB_SHA: ${{ github.sha }} run: | + IMAGE_TAG=$(echo $GITHUB_SHA | cut -c1-8) + docker build \ --no-cache \ --build-arg SPRING_PROFILES_ACTIVE=$SPRING_PROFILES_ACTIVE \ -t $ECR_APP_NAME . - docker tag $ECR_APP_NAME:latest $APP_ECR_REPO - docker push $APP_ECR_REPO + + docker tag $ECR_APP_NAME:latest $APP_ECR_REPO:latest + docker tag $ECR_APP_NAME:latest $APP_ECR_REPO:$IMAGE_TAG + + docker push $APP_ECR_REPO:latest + docker push $APP_ECR_REPO:$IMAGE_TAG + + - name: Prune old ECR Public images (keep 5 latest) + run: | + echo "Pruning old images, keeping only 5 most recent..." + ECR_REPO_NAME="${{ secrets.ECR_APP_NAME }}-dev" + + IMAGES=$(aws ecr-public describe-images \ + --region us-east-1 \ + --repository-name $ECR_REPO_NAME \ + --query 'sort_by(imageDetails,& imagePushedAt)[*].imageDigest' \ + --output json) + + COUNT=$(echo $IMAGES | jq length) + + if [ "$COUNT" -gt 5 ]; then + TO_DELETE=$(echo $IMAGES | jq -c ".[0:$((COUNT-5))] | map({imageDigest: .})") + if [ "$TO_DELETE" != "[]" ]; then + aws ecr-public batch-delete-image \ + --region us-east-1 \ + --repository-name $ECR_REPO_NAME \ + --image-ids "$TO_DELETE" + echo "✅ success delete old image" + fi + else + echo "Less than or equal to 5 images, skipping prune" + fi + + - name: Create Config Files Dynamically + env: + GITHUB_SHA: ${{ github.sha }} + run: | + mkdir -p ./config - - name: Copy docker-compose.yml - run: echo "${{ secrets.DOCKER_COMPOSE_DEV_YML }}" > ./docker-compose.yml + IMAGE_TAG=$(echo $GITHUB_SHA | cut -c1-8) - - name: Send docker-compose.yml to EC2 Instance - uses: appleboy/scp-action@master - with: - username: ec2-user - host: ${{ secrets.DEV_SERVER_IP }} - key: ${{ secrets.DEV_PEM_KEY }} - source: "./docker-compose.yml" - target: "/home/ec2-user/app/" + echo "${{ secrets.APPLICATION_DEV_YML }}" > ./config/application-dev.yml + echo "${{ secrets.DOCKER_COMPOSE_DEV_YML }}" > ./docker-compose.yml + + touch .env + echo "SPRING_PROFILES_ACTIVE=dev" >> .env + echo "AWS_ACCESS_KEY_ID=${{ secrets.AWS_ACCESS_ID }}" >> .env + echo "AWS_SECRET_ACCESS_KEY=${{ secrets.AWS_SECRET_KEY }}" >> .env + echo "AWS_REGION=${{ secrets.AWS_REGION }}" >> .env + echo "IMAGE_TAG=${IMAGE_TAG}" >> .env - - name: Send deploy script to EC2 Instance + - name: Send Files to EC2 uses: appleboy/scp-action@master with: username: ec2-user host: ${{ secrets.DEV_SERVER_IP }} key: ${{ secrets.DEV_PEM_KEY }} - source: "./script/" + source: ".env,docker-compose.yml,script/,config/" target: "/home/ec2-user/app/" - - name: Docker Container Run + - name: Execute Deployment on EC2 uses: appleboy/ssh-action@master with: username: ec2-user host: ${{ secrets.DEV_SERVER_IP }} key: ${{ secrets.DEV_PEM_KEY }} script: | - cd ~ - cd ./app - echo "Creating .env file..." - echo "AWS_ACCESS_KEY_ID=${{ secrets.AWS_ACCESS_ID }}" > .env - echo "AWS_SECRET_ACCESS_KEY=${{ secrets.AWS_SECRET_KEY }}" >> .env + cd /home/ec2-user/app + chmod 600 .env + chmod 600 ./config/application-dev.yml + chmod +x ./script/*.sh - sudo chmod +x ./script/*.sh ./script/deploy.sh docker image prune -f \ No newline at end of file diff --git a/.github/workflows/release-prod.yml b/.github/workflows/release-prod.yml index 59027729..7ad972e1 100755 --- a/.github/workflows/release-prod.yml +++ b/.github/workflows/release-prod.yml @@ -1,6 +1,7 @@ -name: 🚀MAKERS-APP-RELEASE! +name: 🚀 MAKERS-APP-RELEASE! on: + workflow_dispatch: push: branches: [ 'main' ] tags: [ 'makers-app-prod' ] @@ -12,91 +13,118 @@ env: ECR_HOST: ${{ secrets.ECR_HOST }} jobs: - build: + build-and-deploy: name: CD Pipeline runs-on: ubuntu-latest steps: - - name: checkout + - name: Checkout Source uses: actions/checkout@v3 - - name: set up JDK 21 + - name: Set up JDK 21 uses: actions/setup-java@v3 with: distribution: 'corretto' java-version: '21' - - name: touch yml files + - name: Gradle Build (No Secrets) run: | - touch ./src/main/resources/application-prod.yml - touch ./src/test/resources/application-test.yml - shell: bash - - - name: copy application.yml files - run: | - echo "${{ secrets.APPLICATION_PROD_YML }}" > ./src/main/resources/application-prod.yml - echo "${{ secrets.APPLICATION_TEST_YML }}" > ./src/test/resources/application-test.yml + chmod +x ./gradlew + ./gradlew clean build -x test - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v2 + uses: aws-actions/configure-aws-credentials@v4 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_KEY }} - aws-region: ap-northeast-2 + aws-region: ${{ secrets.AWS_REGION }} - - name: 🐘Gradle Build - run: | - chmod +x ./gradlew - ./gradlew clean build -x test - - - name: Login to ECR + - name: Login to ECR Public run: | aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin $ECR_HOST - - name: 🐳Docker Image Build & Push + - name: Docker Image Build & Push + env: + GITHUB_SHA: ${{ github.sha }} run: | + IMAGE_TAG=$(echo $GITHUB_SHA | cut -c1-8) + docker build \ --no-cache \ -f Dockerfile-prod \ --build-arg SPRING_PROFILES_ACTIVE=$SPRING_PROFILES_ACTIVE \ -t $ECR_APP_NAME . - docker tag $ECR_APP_NAME:latest $APP_ECR_REPO - docker push $APP_ECR_REPO + + docker tag $ECR_APP_NAME:latest $APP_ECR_REPO:latest + docker tag $ECR_APP_NAME:latest $APP_ECR_REPO:$IMAGE_TAG + + docker push $APP_ECR_REPO:latest + docker push $APP_ECR_REPO:$IMAGE_TAG + + - name: Prune old ECR Public images (keep 5 latest) + run: | + echo "Pruning old images, keeping only 5 most recent..." + ECR_REPO_NAME="${{ secrets.ECR_APP_NAME }}-prod" + + IMAGES=$(aws ecr-public describe-images \ + --region us-east-1 \ + --repository-name $ECR_REPO_NAME \ + --query 'sort_by(imageDetails,& imagePushedAt)[*].imageDigest' \ + --output json) - - name: Copy docker-compose.yml - run: echo "${{ secrets.DOCKER_COMPOSE_PROD_YML }}" > ./docker-compose.yml + COUNT=$(echo $IMAGES | jq length) - - name: Send docker-compose.yml to EC2 Instance - uses: appleboy/scp-action@master - with: - username: ubuntu - host: ${{ secrets.PROD_SERVER_IP }} - key: ${{ secrets.PROD_PEM_KEY }} - source: "./docker-compose.yml" - target: "/home/ubuntu/app/" + if [ "$COUNT" -gt 5 ]; then + TO_DELETE=$(echo $IMAGES | jq -c ".[0:$((COUNT-5))] | map({imageDigest: .})") + if [ "$TO_DELETE" != "[]" ]; then + aws ecr-public batch-delete-image \ + --region us-east-1 \ + --repository-name $ECR_REPO_NAME \ + --image-ids "$TO_DELETE" + echo "✅ success delete old image" + fi + else + echo "Less than or equal to 5 images, skipping prune" + fi + + - name: Create Config Files Dynamically + env: + GITHUB_SHA: ${{ github.sha }} + run: | + mkdir -p ./config + + IMAGE_TAG=$(echo $GITHUB_SHA | cut -c1-8) + + echo "${{ secrets.APPLICATION_PROD_YML }}" > ./config/application-prod.yml + echo "${{ secrets.DOCKER_COMPOSE_PROD_YML }}" > ./docker-compose.yml + + touch .env + echo "SPRING_PROFILES_ACTIVE=prod" >> .env + echo "AWS_ACCESS_KEY_ID=${{ secrets.AWS_ACCESS_ID }}" >> .env + echo "AWS_SECRET_ACCESS_KEY=${{ secrets.AWS_SECRET_KEY }}" >> .env + echo "AWS_REGION=${{ secrets.AWS_REGION }}" >> .env + echo "IMAGE_TAG=${IMAGE_TAG}" >> .env - - name: Send deploy script to EC2 Instance + - name: Send Files to EC2 uses: appleboy/scp-action@master with: username: ubuntu host: ${{ secrets.PROD_SERVER_IP }} key: ${{ secrets.PROD_PEM_KEY }} - source: "./script/" + source: ".env,docker-compose.yml,script/,config/" target: "/home/ubuntu/app/" - - name: Docker Container Run + - name: Execute Deployment on EC2 uses: appleboy/ssh-action@master with: username: ubuntu host: ${{ secrets.PROD_SERVER_IP }} key: ${{ secrets.PROD_PEM_KEY }} script: | - cd ~ - cd ./app - echo "Creating .env file..." - echo "AWS_ACCESS_KEY_ID=${{ secrets.AWS_ACCESS_ID }}" > .env - echo "AWS_SECRET_ACCESS_KEY=${{ secrets.AWS_SECRET_KEY }}" >> .env + cd /home/ubuntu/app + chmod 600 .env + chmod 600 ./config/application-prod.yml + chmod +x ./script/*.sh - sudo chmod +x ./script/*.sh ./script/deploy.sh - docker image prune -f + docker image prune -f \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 4ad23b95..edb24599 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,5 @@ FROM amazoncorretto:21 WORKDIR /app COPY ./build/libs/app-server-0.0.1-SNAPSHOT.jar /app/app-application.jar -COPY ./src/main/resources/application-dev.yml /app/application-dev.yml -ARG SPRING_PROFILES_ACTIVE -ENV SPRING_PROFILES_ACTIVE=$SPRING_PROFILES_ACTIVE - -CMD ["java", "-jar", "-Dspring.profiles.active=${SPRING_PROFILES_ACTIVE}", "-Dspring.config.location=file:/app/application-dev.yml", "app-application.jar"] +CMD ["java", "-jar", "app-application.jar"] \ No newline at end of file diff --git a/Dockerfile-prod b/Dockerfile-prod index 6e969091..edb24599 100644 --- a/Dockerfile-prod +++ b/Dockerfile-prod @@ -1,9 +1,5 @@ FROM amazoncorretto:21 WORKDIR /app COPY ./build/libs/app-server-0.0.1-SNAPSHOT.jar /app/app-application.jar -COPY ./src/main/resources/application-prod.yml /app/application-prod.yml -ARG SPRING_PROFILES_ACTIVE -ENV SPRING_PROFILES_ACTIVE=$SPRING_PROFILES_ACTIVE - -CMD ["java", "-Dspring.profiles.active=${SPRING_PROFILES_ACTIVE}", "-Dspring.config.location=file:/app/application-prod.yml", "-jar", "app-application.jar"] +CMD ["java", "-jar", "app-application.jar"] \ No newline at end of file diff --git a/script/deploy.sh b/script/deploy.sh index 5e477c7e..48ab6696 100644 --- a/script/deploy.sh +++ b/script/deploy.sh @@ -16,7 +16,7 @@ BLUE_PORT=9090 GREEN_CONTAINER_NAME="app-green" GREEN_PORT=9091 -DOCKER_PS_OUTPUT=$(docker ps --format "{{.Names}}" | grep -E "(${BLUE_CONTAINER_NAME}|${GREEN_CONTAINER_NAME})" || true) +DOCKER_PS_OUTPUT=$(docker ps --format "{{.Names}}" | grep -E "(${BLUE_CONTAINER_NAME}|${GREEN_CONTAINER_NAME})" | head -n 1 || true) RUNNING_CONTAINER_NAME="${DOCKER_PS_OUTPUT:-}" ALL_PORTS=("${BLUE_PORT}" "${GREEN_PORT}") diff --git a/script/health_check.sh b/script/health_check.sh index c048101e..22e9491a 100644 --- a/script/health_check.sh +++ b/script/health_check.sh @@ -4,10 +4,10 @@ HEALTH_CHECK_URL=/api/v2/health health_check() { local PORT=$1 - local RETRIES="${RETRIES:-5}" + local RETRIES="${RETRIES:-10}" - echo "▶️ Start health check after 20 seconds" - sleep 20 + echo "▶️ Start health check after 30 seconds" + sleep 30 for retry_count in $(seq 1 $RETRIES); do echo "🔎 Health Check on Port ${PORT} (Attempt: ${retry_count}/${RETRIES})..." diff --git a/script/stop_container.sh b/script/stop_container.sh index a35d61f2..9a155494 100644 --- a/script/stop_container.sh +++ b/script/stop_container.sh @@ -5,4 +5,7 @@ stop_container() { echo "▶️ Stopping ${CONTAINER_NAME} Container" docker-compose stop ${CONTAINER_NAME} + + echo "🗑️ Removing ${CONTAINER_NAME} Container" + docker-compose rm -f ${CONTAINER_NAME} }