diff --git a/.ebextensions-dev/00-set-timezone.config b/.ebextensions-dev/00-set-timezone.config deleted file mode 100644 index 86838d75..00000000 --- a/.ebextensions-dev/00-set-timezone.config +++ /dev/null @@ -1,3 +0,0 @@ -commands: - set_time_zone: - command: ln -f -s /usr/share/zoneinfo/Asia/Seoul /etc/localtime diff --git a/.github/workflows/cd_gradle.yml b/.github/workflows/cd_gradle.yml index 04e9e4ac..7f8e6b07 100644 --- a/.github/workflows/cd_gradle.yml +++ b/.github/workflows/cd_gradle.yml @@ -10,8 +10,12 @@ on: permissions: contents: read +env: + DOCKER_REPOSITORY: sponus + APPLICATION_SECRET_PATH: ./api/src/main/resources + jobs: - cd: + deploy-with-docker: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -21,7 +25,7 @@ jobs: java-version: '17' distribution: 'temurin' - ## gradle 캐싱 + # Gradle caching - name: Gradle Caching uses: actions/cache@v4 with: @@ -32,15 +36,12 @@ jobs: restore-keys: | ${{ runner.os }}-gradle- - ## create application.yml - - name: make application-secret.yml + # Create application-secret.yaml + - name: Make application-secret.yml run: | - mkdir -p ./api/src/main/resources - touch ./api/src/main/resources/application-secret.yml - shell: bash - - name: deliver application-secret.yml - run: echo "${{ secrets.APPLICATION_SECRET }}" > ./api/src/main/resources/application-secret.yml - shell: bash + mkdir -p $APPLICATION_SECRET_PATH + touch $APPLICATION_SECRET_PATH/application-secret.yml + echo "${{ secrets.APPLICATION_SECRET }}" > $APPLICATION_SECRET_PATH/application-secret.yml ## firebase-key 설정 - name: Set FCM @@ -50,64 +51,47 @@ jobs: mkdir -p ./core/core-infra-firebase/src/main/resources/firebase echo $DATA > ./core/core-infra-firebase/src/main/resources/firebase/firebase-key.json - # 빌드 및 테스트 단계. + # Grant execute permission for gradlew - name: Grant execute permission for gradlew - run: chmod +x ./gradlew + run: chmod +x gradlew - # gradle build + # Build with Gradle - name: Build with Gradle - run: ./gradlew clean build -x test + run: ./gradlew build -x test - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v1 + # Docker Hub 로그인 + - name: Docker Hub 로그인 + uses: docker/login-action@v3 with: - aws-access-key-id: ${{ secrets.AWS_ACTION_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_ACTION_SECRET_ACCESS_KEY }} - aws-region: ap-northeast-2 - - - name: Login to Amazon ECR - id: login-ecr - uses: aws-actions/amazon-ecr-login@v1 + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} - - name: Build, tag, and push image to Amazon ECR - id: build-image - env: - ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} - ECR_REPOSITORY: beanstalk - IMAGE_TAG: latest + # Docker build & Push + - name: Docker build & Push run: | - docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . - docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG - echo "::set-output name=image::$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" - + docker build -f Dockerfile -t ${{ secrets.DOCKER_USERNAME }}/${{ env.DOCKER_REPOSITORY }} . + docker push ${{ secrets.DOCKER_USERNAME }}/${{ env.DOCKER_REPOSITORY }} - - name: Get current time - uses: 1466587594/get-current-time@v2 - id: current-time + # Send deploy.sh & docker-compose.yaml + - name: Send deploy.sh + uses: appleboy/scp-action@master with: - format: YYYY-MM-DDTHH-mm-ss - utcOffset: "+09:00" - - - name: Show Current Time - run: echo "CurrentTime=${{ steps.current-time.outputs.formattedTime }}" - shell: bash - - - name: Generate deployment package - run: | - mkdir -p deploy - cp -r .ebextensions-dev deploy/.ebextensions - cp -r .platform deploy/.platform - cp Dockerrun.aws.json deploy/Dockerrun.aws.json - cd deploy && zip -r deploy.zip . - - - name: Beanstalk Deploy - uses: einaregilsson/beanstalk-deploy@v20 + username: ubuntu + host: ${{ secrets.EC2_HOST }} + key: ${{ secrets.EC2_PRIVATE_KEY }} + port: 22 + source: "./deploy/*" + target: "~" + + # Deploy to Dev + - name: Deploy to Dev + uses: appleboy/ssh-action@master with: - aws_access_key: ${{ secrets.AWS_ACTION_ACCESS_KEY_ID }} - aws_secret_key: ${{ secrets.AWS_ACTION_SECRET_ACCESS_KEY }} - application_name: project-prod-study - environment_name: Project-prod-study-env - version_label: github-action-${{ steps.current-time.outputs.formattedTime }} - region: ap-northeast-2 - deployment_package: deploy/deploy.zip - wait_for_deployment: false + username: ubuntu + host: ${{ secrets.EC2_HOST }} + key: ${{ secrets.EC2_PRIVATE_KEY }} + script: | + cd deploy + chmod 777 ./deploy.sh + ./deploy.sh + docker image prune -f diff --git a/Dockerfile b/Dockerfile index 2fa349b2..90a353a7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,19 +1,17 @@ -# Eclipse Temurin OpenJDK 17 이미지를 사용 -FROM eclipse-temurin:17-jdk -ARG JAR_FILE=api/build/libs/*.jar - -COPY ${JAR_FILE} app.jar +FROM eclipse-temurin:17-jdk AS builder +WORKDIR application +ARG JAR_FILE=./api/build/libs/*.jar +COPY ${JAR_FILE} application.jar +RUN java -Djarmode=layertools -jar application.jar extract -# Redis 및 supervisord 설치 -RUN apt-get update && \ - apt-get install -y redis-server supervisor && \ - rm -rf /var/lib/apt/lists/* - -# supervisord 설정 파일 복사 -COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf +FROM eclipse-temurin:17-jdk +WORKDIR application +COPY --from=builder application/dependencies/ ./ +COPY --from=builder application/spring-boot-loader/ ./ +COPY --from=builder application/snapshot-dependencies/ ./ +COPY --from=builder application/application/ ./ -# 포트 노출 -EXPOSE 8080 6379 +# 애플리케이션 JAR 파일이 이미 압축이 해제 되었으므로, JarLauncher를 사용하여 애플리케이션을 시작 +ENV TZ=Asia/Seoul +ENTRYPOINT ["java", "-Dspring.profiles.active=prod", "-Duser.timezone=Asia/Seoul", "org.springframework.boot.loader.launch.JarLauncher"] -# supervisord를 사용하여 애플리케이션과 Redis 실행 -CMD ["/usr/bin/supervisord"] diff --git a/Dockerrun.aws.json b/Dockerrun.aws.json deleted file mode 100644 index 0135ad94..00000000 --- a/Dockerrun.aws.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "AWSEBDockerrunVersion": "1", - "Image": { - "Name": "891377099059.dkr.ecr.ap-northeast-2.amazonaws.com/beanstalk", - "Update": "true" - }, - "Ports": [ - { - "ContainerPort": "8080", - "HostPort": "8080" - }, - { - "ContainerPort": "6379", - "HostPort": "6379" - } - ] -} diff --git a/HELP.md b/HELP.md deleted file mode 100644 index 2146b765..00000000 --- a/HELP.md +++ /dev/null @@ -1,33 +0,0 @@ -# Read Me First -The following was discovered as part of building this project: - -* The original package name 'com.sponus.sponus-be' is invalid and this project uses 'com.sponus.sponusbe' instead. - -# Getting Started - -### Reference Documentation -For further reference, please consider the following sections: - -* [Official Gradle documentation](https://docs.gradle.org) -* [Spring Boot Gradle Plugin Reference Guide](https://docs.spring.io/spring-boot/docs/3.2.1/gradle-plugin/reference/html/) -* [Create an OCI image](https://docs.spring.io/spring-boot/docs/3.2.1/gradle-plugin/reference/html/#build-image) -* [Spring Web](https://docs.spring.io/spring-boot/docs/3.2.1/reference/htmlsingle/index.html#web) -* [Spring Security](https://docs.spring.io/spring-boot/docs/3.2.1/reference/htmlsingle/index.html#web.security) -* [Spring Data JPA](https://docs.spring.io/spring-boot/docs/3.2.1/reference/htmlsingle/index.html#data.sql.jpa-and-spring-data) - -### Guides -The following guides illustrate how to use some features concretely: - -* [Building a RESTful Web Service](https://spring.io/guides/gs/rest-service/) -* [Serving Web Content with Spring MVC](https://spring.io/guides/gs/serving-web-content/) -* [Building REST services with Spring](https://spring.io/guides/tutorials/rest/) -* [Securing a Web Application](https://spring.io/guides/gs/securing-web/) -* [Spring Boot and OAuth2](https://spring.io/guides/tutorials/spring-boot-oauth2/) -* [Authenticating a User with LDAP](https://spring.io/guides/gs/authenticating-ldap/) -* [Accessing Data with JPA](https://spring.io/guides/gs/accessing-data-jpa/) - -### Additional Links -These additional references should also help you: - -* [Gradle Build Scans – insights for your project's build](https://scans.gradle.com#gradle) - diff --git a/build.gradle b/build.gradle index 80915243..e0ed6488 100644 --- a/build.gradle +++ b/build.gradle @@ -13,7 +13,7 @@ jar { enabled = true } allprojects { group = 'com.sponus' - version = '0.0.1-SNAPSHOT' + version = '0.0.1' java { sourceCompatibility = '17' diff --git a/core/core-infra-redis/src/main/resources/application-redis.yml b/core/core-infra-redis/src/main/resources/application-redis.yml index 15079fdb..8d5810d2 100644 --- a/core/core-infra-redis/src/main/resources/application-redis.yml +++ b/core/core-infra-redis/src/main/resources/application-redis.yml @@ -1,3 +1,13 @@ +spring.config.activate.on-profile: local + +spring: + data: + redis: + host: localhost + port: 6379 +--- +spring.config.activate.on-profile: prod + spring: data: redis: diff --git a/deploy/deploy.sh b/deploy/deploy.sh new file mode 100644 index 00000000..403c24be --- /dev/null +++ b/deploy/deploy.sh @@ -0,0 +1,72 @@ +#!/bin/bash + +IS_GREEN=$(docker ps | grep green) # 현재 실행중인 App이 green인지 확인합니다. +DEFAULT_CONF=" /etc/nginx/nginx.conf" + +BLUE_PORT=8081 +GREEN_PORT=8082 + +send_failure_message() { + echo "Health check failed after 10 attempts, sending failure message and exiting." + exit 1 +} + +if [ -z $IS_GREEN ]; then # blue인 경우 + + echo "### BLUE => GREEN ###" + + echo "1. get green image" + docker compose pull green # green으로 이미지를 내려받습니다. + + echo "2. green container up" + docker compose up -d green # green 컨테이너 실행 + + for i in {1..15}; do + echo "3. green health check attempt $i..." + sleep 3 + + REQUEST=$(curl http://127.0.0.1:$GREEN_PORT) # green으로 request + if [ -n "$REQUEST" ]; then # 서비스 가능하면 health check 중지 + echo "health check success" + break + elif [ $i -eq 15 ]; then + send_failure_message + fi + done + + echo "4. reload nginx" + sudo cp /etc/nginx/nginx.green.conf /etc/nginx/nginx.conf + sudo nginx -s reload + + echo "5. blue container down" + docker compose stop blue +else + echo "### GREEN => BLUE ###" + + echo "1. get blue image" + docker compose pull blue + + echo "2. blue container up" + docker compose up -d blue + + for i in {1..15}; do + echo "3. blue health check attempt $i..." + sleep 3 + + REQUEST=$(curl http://127.0.0.1:$BLUE_PORT) # blue로 request + + if [ -n "$REQUEST" ]; then # 서비스 가능하면 health check 중지 + echo "health check success" + break + elif [ $i -eq 15 ]; then + send_failure_message + fi + done + + echo "4. reload nginx" + sudo cp /etc/nginx/nginx.blue.conf /etc/nginx/nginx.conf + sudo nginx -s reload + + echo "5. green container down" + docker compose stop green +fi diff --git a/deploy/docker-compose.yaml b/deploy/docker-compose.yaml new file mode 100644 index 00000000..12b76fd5 --- /dev/null +++ b/deploy/docker-compose.yaml @@ -0,0 +1,12 @@ +services: + blue: + image: dhxl50/sponus:latest + container_name: blue + ports: + - "8081:8080" + + green: + image: dhxl50/sponus:latest + container_name: green + ports: + - "8082:8080" diff --git a/supervisord.conf b/supervisord.conf deleted file mode 100644 index 74ceea01..00000000 --- a/supervisord.conf +++ /dev/null @@ -1,12 +0,0 @@ -[supervisord] -nodaemon=true - -[program:redis] -command=/usr/bin/redis-server -autostart=true -autorestart=true - -[program:app] -command=java -Dspring.profiles.active=prod -jar /app.jar -autostart=true -autorestart=true