diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..91c72cb --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,77 @@ +name: CI and Deploy to Amazon EC2 + +on: + # Triggers the workflow on push events but only for the "main" branch + push: + branches: [ "main", "develop" ] + +# 본인이 설정한 값을 여기서 채워넣습니다. +# 리전, 버킷 이름, CodeDeploy 앱 이름, CodeDeploy 배포 그룹 이름 +env: + AWS_REGION: ap-northeast-2 + S3_BUCKET_NAME: github-actions-s3-management + CODE_DEPLOY_APPLICATION_NAME: codedeploy-management-app + CODE_DEPLOY_DEPLOYMENT_GROUP_NAME: codedeploy-deployment-group + +permissions: + contents: read + +jobs: + deploy: + name: Deploy + runs-on: ubuntu-latest + environment: production + + steps: + # (1) 기본 체크아웃 + - name: Checkout + uses: actions/checkout@v3 + + # (2) JDK 17 세팅 + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + distribution: 'temurin' + java-version: '17' + + ## create application-rds.yml && application-s3.yml && application-auth.yml && forbidden-words.txt + - name: create application.properties file + run: | + touch ./src/main/resources/application-prod.yml + echo "${{ secrets.DATABASE_YML }}" | base64 --decode > src/main/resources/application-prod.yml + + #추가 + - name: Make Gradle Wrapper script executable + run: chmod +x /home/runner/work/STUDIO-EYE-MANAGEMENT-SERVICE/STUDIO-EYE-MANAGEMENT-SERVICE/gradlew + + # (3) Gradle build (Test 제외) + - name: Build with Gradle + uses: gradle/gradle-build-action@0d13054264b0bb894ded474f08ebb30921341cee + with: + arguments: clean build -x test + + # (4) AWS 인증 (IAM 사용자 Access Key, Secret Key 활용) + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ env.AWS_REGION }} + + # (5) 빌드 결과물을 S3 버킷에 업로드 + - name: Upload to AWS S3 + run: | + aws deploy push \ + --application-name ${{ env.CODE_DEPLOY_APPLICATION_NAME }} \ + --ignore-hidden-files \ + --s3-location s3://$S3_BUCKET_NAME/$GITHUB_SHA.zip \ + --source . + + # (6) S3 버킷에 있는 파일을 대상으로 CodeDeploy 실행 + - name: Deploy to AWS EC2 from S3 + run: | + aws deploy create-deployment \ + --application-name ${{ env.CODE_DEPLOY_APPLICATION_NAME }} \ + --deployment-config-name CodeDeployDefault.AllAtOnce \ + --deployment-group-name ${{ env.CODE_DEPLOY_DEPLOYMENT_GROUP_NAME }} \ + --s3-location bucket=$S3_BUCKET_NAME,key=$GITHUB_SHA.zip,bundleType=zip \ No newline at end of file diff --git a/.github/workflows/gradle-cd.yml b/.github/workflows/gradle-cd.yml deleted file mode 100644 index 8e4272b..0000000 --- a/.github/workflows/gradle-cd.yml +++ /dev/null @@ -1,77 +0,0 @@ -# This workflow uses actions that are not certified by GitHub. -# They are provided by a third-party and are governed by -# separate terms of service, privacy policy, and support -# documentation. -# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time -# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle - -# Workflow 이름 -name: Spring Boot & Gradle CD - -on: - push: - branches: [ "main" ] -# pull_request: -# branches: [ "main" ] - -permissions: - contents: read - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - # JDK 17 설치 - - name: Set up JDK 17 - uses: actions/setup-java@v3 - with: - java-version: '17' - distribution: 'temurin' - - # github secret 환경변수로 적어둔 APPLICATION_YML으로 application.yml파일을 생성합니다. - # 환경변수가 지나치게 많아짐을 방지하기 위해 APPLICATION_YML 변수를 만들었습니다. - - name: make application.yml - run: | - cd ./src/main/resources - touch ./application.yml - echo "${{ secrets.APPLICATION_YML }}" >> ./application.yml - shell: bash - - # gradlew에 실행 권한을 부여합니다. - - name: Grant execute permisson for gradlew - run: chmod +x gradlew - - # test는 CI 과정에서 수행되므로 여기서는 `-x`로 테스트를 생략했습니다. - # `--stacktrace`로 더 자세한 로그가 출력되게 해줍니다. - - name: Build with Gradle (without Test) - run: ./gradlew clean build -x test --stacktrace - - # docker hub에 로그인하고 이미지를 빌드합니다. 이후에 push를 진행합니다. - # docker_username을 적지 않으면 push 시에 요청이 거부될 수 있습니다. - - name: Docker Hub build & push - run: | - docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} - docker build -t ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_REPO }} . - docker images - docker push ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_REPO }} - - # EC2에 접속하고 배포합니다. - - name: Deploy - uses: appleboy/ssh-action@master - with: - host: ${{ secrets.EC2_HOST }} - username: ${{ secrets.EC2_USERNAME }} - key: ${{ secrets.EC2_KEY }} - port: ${{ secrets.EC2_SSH_PORT }} - - script: | - sudo docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} - sudo docker rm -f $(sudo docker ps -qa) - - sudo docker pull ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_REPO }} - sudo docker run -d -p 8080:8080 ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_REPO }}:latest - diff --git a/.github/workflows/gradle-ci.yml b/.github/workflows/gradle-ci.yml deleted file mode 100644 index 25f5e3d..0000000 --- a/.github/workflows/gradle-ci.yml +++ /dev/null @@ -1,82 +0,0 @@ -# This workflow uses actions that are not certified by GitHub. -# They are provided by a third-party and are governed by -# separate terms of service, privacy policy, and support -# documentation. -# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time -# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle - -# Workflow 이름 -name: Spring Boot & Gradle CI - -on: -# push: -# branches: [ "main" ] - pull_request: - branches: [ "main" ] - -permissions: write-all - -jobs: - test: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - # JDK 17 설치 - - name: Set up JDK 17 - uses: actions/setup-java@v3 - with: - java-version: '17' - distribution: 'temurin' - - # github secret 환경변수로 적어둔 APPLICATION_YML으로 application.yml파일을 생성합니다. - # 환경변수가 지나치게 많아짐을 방지하기 위해 APPLICATION_YML 변수를 만들었습니다. - - name: make application.yml - run: | - cd ./src/main/resources - touch ./application.yml - echo "${{ secrets.APPLICATION_YML }}" >> ./application.yml - shell: bash - - # gradlew에 실행 권한을 부여합니다. - - name: Grant execute permisson for gradlew - run: chmod +x gradlew - - # 테스트를 진행합니다. - - name: Test with Gradle - run: ./gradlew --info test - - # 테스트 후 Result를 보기위해 Publish Unit Test Results step 추가 - - name: Publish Unit Test Results - uses: EnricoMi/publish-unit-test-result-action@v2 - if: ${{ always() }} # 테스트가 실패하여도 Report를 보기 위해 `always`로 설정 - with: - files: build/test-results/**/*.xml - - # 테스트 후 코드 커버리지 결과 코멘트 등록 - - name: Add coverage to PR - id: jacoco - uses: madrapps/jacoco-report@v1.6.1 - with: - paths: ${{ github.workspace }}/build/reports/jacoco/test/jacocoTestReport.xml - token: ${{ secrets.GITHUB_TOKEN }} - title: "Coverage Report" - min-coverage-overall: 50 - min-coverage-changed-files: 50 - - # 테스트 결과 레포트를 깃허브에 업로드 - - name: Upload Test Result Report - if: ${{ always() }} - uses: actions/upload-artifact@v3 - with: - name: test-report # 결과물의 이름 - path: build/reports/tests/test # upload하고자 하는 파일 또는 디렉토리 경로 - - # 테스트 커버리지 레포트를 깃허브에 업로드 - - name: Upload Jacoco Report - uses: actions/upload-artifact@v3 - with: - name: jacoco-report # 결과물의 이름 - path: build/reports/jacoco/test/html # upload하고자 하는 파일 또는 디렉토리 경로 \ No newline at end of file diff --git a/.github/workflows/sonarqube.yml b/.github/workflows/sonarqube.yml new file mode 100644 index 0000000..bc0126f --- /dev/null +++ b/.github/workflows/sonarqube.yml @@ -0,0 +1,46 @@ +name: Build + +on: + push: + branches: + - main + - develop + +jobs: + build: + name: Build + runs-on: ubuntu-latest + permissions: read-all + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + + - name: Set up JDK 17 + uses: actions/setup-java@v1 + with: + java-version: 17 + + - name: Cache SonarQube packages + uses: actions/cache@v1 + with: + path: ~/.sonar/cache + key: ${{ runner.os }}-sonar + restore-keys: ${{ runner.os }}-sonar + + - name: Cache Gradle packages + uses: actions/cache@v1 + with: + path: ~/.gradle/caches + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} + restore-keys: ${{ runner.os }}-gradle + + #추가 + - name: Make Gradle Wrapper script executable + run: chmod +x ./gradlew + + - name: Build and analyze + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} + run: ./gradlew build -x test sonar --info \ No newline at end of file diff --git a/.gitignore b/.gitignore index e18130a..635313f 100644 --- a/.gitignore +++ b/.gitignore @@ -37,7 +37,6 @@ out/ .vscode/ ### application.yml ### -application.yml application-local.yml application-prod.yml application-test.yml diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 1faad58..0000000 --- a/Dockerfile +++ /dev/null @@ -1,4 +0,0 @@ -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/appspec.yml b/appspec.yml new file mode 100644 index 0000000..17c761f --- /dev/null +++ b/appspec.yml @@ -0,0 +1,23 @@ +version: 0.0 +os: linux + +files: + - source: / + destination: /home/ubuntu/app + overwrite: yes + +permissions: + - object: / + pattern: "**" + owner: ubuntu + group: ubuntu + +hooks: + AfterInstall: + - location: scripts/stop.sh + timeout: 60 + runas: root + ApplicationStart: + - location: scripts/start.sh + timeout: 60 + runas: root \ No newline at end of file diff --git a/build.gradle b/build.gradle index 19ef4db..69dae2a 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,6 @@ plugins { id 'java' + id "org.sonarqube" version "4.4.1.3373" id 'org.springframework.boot' version '3.1.3' id 'io.spring.dependency-management' version '1.1.3' id 'jacoco' @@ -66,6 +67,9 @@ dependencies { implementation 'io.jsonwebtoken:jjwt:0.9.1' implementation 'javax.xml.bind:jaxb-api:2.3.0' + // SonarQube + implementation 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.3' + // test container testImplementation "org.testcontainers:junit-jupiter:1.17.2" testImplementation "org.testcontainers:mysql:1.17.2" @@ -77,6 +81,20 @@ dependencies { implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE' } +sonarqube { + properties { + property "sonar.projectKey", "sonar-management" + property "sonar.projectName", "sonar-management" +// property "sonar.sources", "src" // 소스 경로 +// property "sonar.language", "java" // 언어 +// property "sonar.java.source", "17" +// property "sonar.sourceEncoding", "UTF-8" +// property "sonar.profile", "Sonar way" // SonarQube 에서 분석할 때 적용할 프로필(분석할 수준 설정) +// property "sonar.java.binaries", "${buildDir}/classes" // 자바 클래스 파일위치 +// property "sonar.test.inclusions", "**/*Test.java" // 코드 분석에 사용할 테스트 소스 + } +} + dependencyManagement { imports { mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index dfa982c..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,16 +0,0 @@ -version: '3.8' - -services: - # mysql - mysql: - container_name: IA_mysql - image: mysql:8.0 - env_file: - - .env\ - environment: - TZ: Asia/Seoul - ports: - - "33061:3306" - command: - - '--character-set-server=utf8mb4' - - '--collation-server=utf8mb4_unicode_ci' \ No newline at end of file diff --git a/scripts/start.sh b/scripts/start.sh new file mode 100644 index 0000000..126c2a2 --- /dev/null +++ b/scripts/start.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +PROJECT_ROOT="/home/ubuntu/app" +JAR_FILE="$PROJECT_ROOT/spring-webapp.jar" + +APP_LOG="$PROJECT_ROOT/application.log" +ERROR_LOG="$PROJECT_ROOT/error.log" +DEPLOY_LOG="$PROJECT_ROOT/deploy.log" + +TIME_NOW=$(date +%c) + +# build 파일 복사 +echo "$TIME_NOW > $JAR_FILE 파일 복사" >> $DEPLOY_LOG +cp $PROJECT_ROOT/build/libs/*.jar $JAR_FILE + +# jar 파일 실행 +echo "$TIME_NOW > $JAR_FILE 파일 실행" >> $DEPLOY_LOG +nohup java -jar $JAR_FILE > $APP_LOG 2> $ERROR_LOG & + +CURRENT_PID=$(pgrep -f $JAR_FILE) +echo "$TIME_NOW > 실행된 프로세스 아이디 $CURRENT_PID 입니다." >> $DEPLOY_LOG \ No newline at end of file diff --git a/scripts/stop.sh b/scripts/stop.sh new file mode 100644 index 0000000..755d347 --- /dev/null +++ b/scripts/stop.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +PROJECT_ROOT="/home/ubuntu/app" +JAR_FILE="$PROJECT_ROOT/spring-webapp.jar" + +DEPLOY_LOG="$PROJECT_ROOT/deploy.log" + +TIME_NOW=$(date +%c) + +# 현재 구동 중인 애플리케이션 pid 확인 +CURRENT_PID=$(pgrep -f $JAR_FILE) + +# 프로세스가 켜져 있으면 종료 +if [ -z $CURRENT_PID ]; then + echo "$TIME_NOW > 현재 실행중인 애플리케이션이 없습니다" >> $DEPLOY_LOG +else + echo "$TIME_NOW > 실행중인 $CURRENT_PID 애플리케이션 종료 " >> $DEPLOY_LOG + kill -15 $CURRENT_PID +fi \ No newline at end of file diff --git a/src/main/java/com/mju/management/global/config/JasyptConfig.java b/src/main/java/com/mju/management/global/config/JasyptConfig.java index ce677a1..8c490c4 100644 --- a/src/main/java/com/mju/management/global/config/JasyptConfig.java +++ b/src/main/java/com/mju/management/global/config/JasyptConfig.java @@ -1,33 +1,33 @@ -package com.mju.management.global.config; - -import org.jasypt.encryption.StringEncryptor; -import org.jasypt.encryption.pbe.PooledPBEStringEncryptor; -import org.jasypt.encryption.pbe.config.SimpleStringPBEConfig; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class JasyptConfig { - - @Value("${jasypt.encryptor.password}") - private String encryptKey; - - @Bean("jasyptStringEncryptor") - public StringEncryptor stringEncryptor() { - - PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor(); - SimpleStringPBEConfig config = new SimpleStringPBEConfig(); - - config.setPassword(encryptKey); // encrypt key - config.setAlgorithm("PBEWithMD5AndDES"); // 암호화 알고리즘 - config.setKeyObtentionIterations("1000"); // 반복할 해싱 회수 - config.setPoolSize("1"); // 인스턴스 pool - config.setProviderName("SunJCE"); - config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator"); // salt 생성 클래스 - config.setStringOutputType("base64"); //인코딩 방식 - encryptor.setConfig(config); - return encryptor; - } - -} \ No newline at end of file +//package com.mju.management.global.config; +// +//import org.jasypt.encryption.StringEncryptor; +//import org.jasypt.encryption.pbe.PooledPBEStringEncryptor; +//import org.jasypt.encryption.pbe.config.SimpleStringPBEConfig; +//import org.springframework.beans.factory.annotation.Value; +//import org.springframework.context.annotation.Bean; +//import org.springframework.context.annotation.Configuration; +// +//@Configuration +//public class JasyptConfig { +// +// @Value("${jasypt.encryptor.password}") +// private String encryptKey; +// +// @Bean("jasyptStringEncryptor") +// public StringEncryptor stringEncryptor() { +// +// PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor(); +// SimpleStringPBEConfig config = new SimpleStringPBEConfig(); +// +// config.setPassword(encryptKey); // encrypt key +// config.setAlgorithm("PBEWithMD5AndDES"); // 암호화 알고리즘 +// config.setKeyObtentionIterations("1000"); // 반복할 해싱 회수 +// config.setPoolSize("1"); // 인스턴스 pool +// config.setProviderName("SunJCE"); +// config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator"); // salt 생성 클래스 +// config.setStringOutputType("base64"); //인코딩 방식 +// encryptor.setConfig(config); +// return encryptor; +// } +// +//} \ No newline at end of file diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml deleted file mode 100644 index 4590483..0000000 --- a/src/main/resources/application-local.yml +++ /dev/null @@ -1,52 +0,0 @@ -#eureka: -# client: -# register-with-eureka: true -# fetch-registry: true -# service-url: -# defaultZone: http://localhost:8761/eureka - -spring: - config: - activate: - on-profile: local - application: - name: api - -# datasource: -# url: jdbc:mysql://localhost:33061/iceAmericano -# driver-class-name: com.mysql.cj.jdbc.Driver -# username: root -# password: iceAmericano - datasource: - driver-class-name: org.h2.Driver - url: jdbc:h2:tcp://localhost/~/manageServ;NON_KEYWORDS=USER - username: sa - password: - -# jpa: -# hibernate: -# ddl-auto: create -# show-sql: true -# properties: -# hibernate: -# format_sql: true -# dialect: org.hibernate.dialect.MySQLDialect - - #JPA 설정 - jpa: - database-platform: - hibernate: - ddl-auto: create # create or validate - - #배포 할 때는 open-in-view false 설정 - open-in-view: false - properties: - hibernate: - show_sql: true - format_sql: true - -#jwt: -# secret: ENC(acbDZy8Gz9X1OSL3fjOHVg==) - -#user-service: -# url: http://localhost:8081/user-service diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml deleted file mode 100644 index f7c7f4b..0000000 --- a/src/main/resources/application-prod.yml +++ /dev/null @@ -1,37 +0,0 @@ -spring: - config: - activate: - on-profile: prod - application: - name: Management-Service - - datasource: - url: ENC(vK3ggYCmRnJlODS6cbBXJAi+7Mw7qpO3mKVnWz9PV+Mi+aQrVJBY++5ecNXwrnQxgbCgoivoVVDJjDe554GHnyoj8f2rUbkyNbgN/QmNAZa+Cc+Gp1x7huLrQD7BCeLC) - driver-class-name: com.mysql.cj.jdbc.Driver - username: ENC(Oywg8yRSKPs0TXgE6rbNaA==) - password: ENC(R6oW03mOpylO0Fj2BJzvOeMuCiLGT568) - - jpa: - hibernate: - ddl-auto: update - show-sql: true - properties: - hibernate: - format_sql: true - dialect: org.hibernate.dialect.MySQLDialect - -eureka: - instance: - hostname : 3.35.179.234 - instance-id: ${spring.application.name}:${spring.application.instance_id:${server.port}} - client: - register-with-eureka: true - fetch-registry: true - service-url: - defaultZone: http://13.125.181.139:8761/eureka - -jwt: - secret: ENC(acbDZy8Gz9X1OSL3fjOHVg==) - -user-service: - url: http://52.78.155.253:8081/user-service diff --git a/src/main/resources/application-test.yml b/src/main/resources/application-test.yml deleted file mode 100644 index bbb42b4..0000000 --- a/src/main/resources/application-test.yml +++ /dev/null @@ -1,33 +0,0 @@ -spring: - config: - activate: - on-profile: test - application: - name: api - - datasource: - driver-class-name: org.testcontainers.jdbc.ContainerDatabaseDriver - url: jdbc:tc:mysql:8://testDB - username: root - password: 1234 - - jpa: - hibernate: - ddl-auto: create-drop - show-sql: true - properties: - hibernate: - format_sql: true - dialect: org.hibernate.dialect.MySQLDialect - -eureka: - client: - register-with-eureka: false - fetch-registry: false - -jwt: - secret: ENC(acbDZy8Gz9X1OSL3fjOHVg==) - -user-service: - url: http://localhost:8081/user-service - diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..7a16e86 --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,22 @@ +spring: + profiles: + active: prod + +--- + +# Swagger +springdoc: + default-consumes-media-type: application/json + default-produces-media-type: application/json + api-docs: + groups: + enabled: true + swagger-ui: + operations-sorter: method # alpha(알파벳 오름차순), method(HTTP메소드순) + tags-sorter: alpha # 태그 정렬 기준 + path: /swagger # html 문서 접속 경로 + disable-swagger-default-url: true + display-query-params-without-oauth2: true + doc-expansion: none # tag, operation 펼치는 방식 + paths-to-match: + - /**