diff --git a/.github/workflows/core-application-prod-ci-cd-flow.yml b/.github/workflows/core-application-prod-ci-cd-flow.yml new file mode 100644 index 000000000..b5f8ecf6f --- /dev/null +++ b/.github/workflows/core-application-prod-ci-cd-flow.yml @@ -0,0 +1,149 @@ +name: core-prod-ci/cd + +on: + push: + branches: + - prod_back_core + - 'hotfix/[0-9a-zA-z]+-B-prod-core-#[0-9a-zA-z]+' + pull_request: + branches: + - prod_back_core + +jobs: + build_and_test: + name: build and test + if: ${{github.event_name == 'pull_request' }} + runs-on: ubuntu-latest + + defaults: + run: + working-directory: ./backend/core + + steps: + - name: set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'liberica' + + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: true + token: ${{ secrets.CI_PAT }} + + - name: formatting + uses: axel-op/googlejavaformat-action@v3 + with: + args: "--replace" + skip-commit: true + + - name: grant execute permission for gradlew + run: chmod +x ./gradlew + + - name: test + run: ./gradlew clean test + + deploy-core: + name: core application deploy + if: ${{ github.event_name == 'push' }} + runs-on: ubuntu-latest + + defaults: + run: + working-directory: ./backend/core + + permissions: + id-token: write + contents: read + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: true + token: ${{ secrets.CI_PAT }} + + - name: set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'liberica' + cache: gradle + + - name: grant execute permission for gradlew + run: chmod +x ./gradlew + + - name: build gradle + run: ./gradlew clean build + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: ${{ secrets.AWS_ROLE_ARN }} + aws-region: ${{ secrets.AWS_REGION }} + + - name: Login to Amazon ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@v2 + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: | + ${{ steps.login-ecr.outputs.registry }}/${{ secrets.AWS_PROD_ECR_CORE_REPOSITORY_NAME }} + tags: | + type=raw,value={{date 'YYYYMMDD-HHmmss'}} + + - name: Build and push + uses: docker/build-push-action@v5 + with: + context: ./backend/core + push: true + tags: ${{ steps.meta.outputs.tags }} + provenance: false + + - name: Get Github action IP + id: ip + uses: haythem/public-ip@v1.3 + + - name: Add Github Actions IP to Security group + run: | + aws ec2 authorize-security-group-ingress --group-id ${{ secrets.AWS_PROD_CORE_NOTI_SG_ID }} --protocol tcp --port 22 --cidr ${{ steps.ip.outputs.ipv4 }}/32 + + - name: Connect ec2 and Run Docker Container + uses: appleboy/ssh-action@v0.1.6 + env: + AWS_ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} + with: + host: ${{ secrets.SSH_PROD_CORE_NOTI_HOST }} + username: ${{ secrets.SSH_USERNAME }} + key: ${{ secrets.SSH_PROD_CORE_NOTI_PRIVATE_KEY }} + port: ${{ secrets.SSH_PORT }} + script: | + docker ps -q --filter "name=core" | xargs -r docker stop + docker ps -aq --filter "name=core" | xargs -r docker rm + aws ecr get-login-password --region ${{ secrets.AWS_REGION }} | docker login --username ${{ secrets.AWS_DOCKER_USER }} --password-stdin ${{ secrets.AWS_USER_ID }}.dkr.ecr.${{ secrets.AWS_REGION }}.amazonaws.com + docker image prune -f + docker pull ${{ steps.meta.outputs.tags }} + docker run -d -p 8080:8080 -e ENVIRONMENT=prod --name core --network ec2-user_backend ${{ steps.meta.outputs.tags }} + + - name: Remove Github Actions IP from security group + if: always() + run: | + aws ec2 revoke-security-group-ingress --group-id ${{ secrets.AWS_PROD_CORE_NOTI_SG_ID }} --protocol tcp --port 22 --cidr ${{ steps.ip.outputs.ipv4 }}/32 + + - uses: sarisia/actions-status-discord@v1 + if: success() + with: + webhook: ${{ secrets.DISCORD_WEBHOOK }} + status: ${{ job.status }} + content: "여러분 <@384742716933668867> <@1084774841460215839> <@545902166842408960> <@1081452554149449748>\n 배포 완료했습니다!!" + title: "코어 운영 서버 배포 완료 알림" + description: "백엔드 개발 브랜치에서 운영 환경으로 깃허브 액션으로 배포 완료" + image: ${{ secrets.EMBED_IMAGE }} + color: 0x0000ff + url: "https://github.com/tukcomCD2024/DroidBlossom/actions" + username: GitHub Actions Bot + avatar_url: ${{ secrets.AVATAR_URL }} \ No newline at end of file diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/global/config/rabbitmq/RabbitmqConfig.java b/backend/core/src/main/java/site/timecapsulearchive/core/global/config/rabbitmq/RabbitmqConfig.java index 599915c2b..9933cf400 100644 --- a/backend/core/src/main/java/site/timecapsulearchive/core/global/config/rabbitmq/RabbitmqConfig.java +++ b/backend/core/src/main/java/site/timecapsulearchive/core/global/config/rabbitmq/RabbitmqConfig.java @@ -1,5 +1,7 @@ package site.timecapsulearchive.core.global.config.rabbitmq; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; import lombok.RequiredArgsConstructor; import org.springframework.amqp.core.Binding; import org.springframework.amqp.core.BindingBuilder; @@ -11,6 +13,7 @@ import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import site.timecapsulearchive.core.global.error.exception.InternalServerException; @Configuration @RequiredArgsConstructor @@ -19,6 +22,8 @@ public class RabbitmqConfig { private static final int MAX_RETRY_COUNT = 3; private static final String RETRY_HEADER = "x-retry-count"; + private final RabbitmqProperties rabbitmqProperties; + @Bean public Queue capsuleSkinQueue() { return new Queue(RabbitmqComponentConstants.CAPSULE_SKIN_QUEUE.getSuccessComponent(), true); @@ -153,7 +158,21 @@ public RabbitTemplate publisherConfirmsRabbitTemplate() { public CachingConnectionFactory publisherConfirmsConnectionFactory() { CachingConnectionFactory connectionFactory = new CachingConnectionFactory(); - connectionFactory.afterPropertiesSet(); + connectionFactory.setHost(rabbitmqProperties.host()); + connectionFactory.setPort(rabbitmqProperties.port()); + connectionFactory.setUsername(rabbitmqProperties.userName()); + connectionFactory.setPassword(rabbitmqProperties.password()); + connectionFactory.setVirtualHost(rabbitmqProperties.virtualHost()); + connectionFactory.setPublisherConfirmType(rabbitmqProperties.publisherConfirmType()); + connectionFactory.setPublisherReturns(rabbitmqProperties.publisherReturns()); + + if (rabbitmqProperties.isSslEnabled()) { + try { + connectionFactory.getRabbitConnectionFactory().useSslProtocol(); + } catch (NoSuchAlgorithmException | KeyManagementException e) { + throw new InternalServerException(e); + } + } return connectionFactory; } diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/global/config/rabbitmq/RabbitmqProperties.java b/backend/core/src/main/java/site/timecapsulearchive/core/global/config/rabbitmq/RabbitmqProperties.java new file mode 100644 index 000000000..a8e11e847 --- /dev/null +++ b/backend/core/src/main/java/site/timecapsulearchive/core/global/config/rabbitmq/RabbitmqProperties.java @@ -0,0 +1,28 @@ +package site.timecapsulearchive.core.global.config.rabbitmq; + +import org.springframework.amqp.rabbit.connection.CachingConnectionFactory.ConfirmType; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.NestedConfigurationProperty; + +@ConfigurationProperties(prefix = "spring.rabbitmq") +public record RabbitmqProperties( + String host, + int port, + String userName, + String password, + String virtualHost, + ConfirmType publisherConfirmType, + boolean publisherReturns, + @NestedConfigurationProperty + SSL ssl +) { + protected record SSL( + boolean enabled + ) { + + } + + public boolean isSslEnabled() { + return ssl != null && ssl.enabled; + } +} \ No newline at end of file diff --git a/backend/core/src/main/resources/logback-spring.xml b/backend/core/src/main/resources/logback-spring.xml index 61e3d86a5..f9ba2a0ca 100644 --- a/backend/core/src/main/resources/logback-spring.xml +++ b/backend/core/src/main/resources/logback-spring.xml @@ -31,7 +31,7 @@ - + @@ -43,7 +43,7 @@ - +