diff --git a/.ebextensions/00-set-timezone.config b/.ebextensions/00-set-timezone.config new file mode 100644 index 0000000..4249da0 --- /dev/null +++ b/.ebextensions/00-set-timezone.config @@ -0,0 +1,3 @@ +commands: + set_time_zone: + command: ln -f -s /usr/share/zoneinfo/Asia/Seoul /etc/localtime \ No newline at end of file diff --git a/.github/workflows/dev_deploy.yml b/.github/workflows/dev_deploy.yml new file mode 100644 index 0000000..2645a85 --- /dev/null +++ b/.github/workflows/dev_deploy.yml @@ -0,0 +1,88 @@ +name: docker ci/cd + +on: + push: + branches: + - develop + pull_request: + branches: + - develop + types: [closed] + workflow_dispatch: # (2).수동 실행도 가능하도록 + +jobs: + build: + runs-on: ubuntu-latest # (3).OS환경 + if: github.ref == 'refs/heads/main' || (github.event_name == 'pull_request' && github.event.pull_request.merged == true) + + + steps: + - name: Checkout current repository + uses: actions/checkout@v2 + + - name: Set up JDK 17 + uses: actions/setup-java@v1 + with: + java-version: 17 + + - name: Grant execute permission for gradlew + run: chmod +x ./gradlew + shell: bash + + - name: Build with Gradle + run: ./gradlew clean build + shell: bash + + - 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: ap-northeast-2 + + - name: Login to Amazon ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@v1 + + - name: Build, tag, and push image to Amazon ECR + id: build-image + env: + ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} + ECR_REPOSITORY: tree-dev + IMAGE_TAG: latest + run: | + # Docker 이미지 빌드 + docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . + + # 빌드한 이미지를 Amazon ECR로 푸시 + docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG + + # 빌드된 이미지의 정보 출력 + echo "::set-output name=image::$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" + + - name: Get current time + uses: 1466587594/get-current-time@v2 + id: current-time + with: + format: YYYYMMDD_HH-mm-ss + utcOffset: "+09:00" + + - name: Generate deployment package + run: | + mkdir -p deploy + cp -r .ebextensions deploy/.ebextensions + cp Dockerrun.aws.json deploy/Dockerrun.aws.json + cp -r .platform deploy/.platform + cd deploy && zip -r deploy.zip . + + - name: Beanstalk Deploy + uses: einaregilsson/beanstalk-deploy@v14 + with: + aws_access_key: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws_secret_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + application_name: tree-dev + environment_name: Tree-dev-env + version_label: github-action-${{ steps.current-time.outputs.formattedTime }} + region: ap-northeast-2 + deployment_package: deploy/deploy.zip + wait_for_deployment: false \ No newline at end of file diff --git a/.platform/nginx.conf b/.platform/nginx.conf new file mode 100644 index 0000000..612092e --- /dev/null +++ b/.platform/nginx.conf @@ -0,0 +1,63 @@ +user nginx; +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; +worker_processes auto; +worker_rlimit_nofile 33282; + +events { + use epoll; + worker_connections 1024; + multi_accept on; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + include conf.d/*.conf; + + map $http_upgrade $connection_upgrade { + default "upgrade"; + } + + upstream springboot { + server 127.0.0.1:8080; + keepalive 1024; + } + + server { + listen 80 default_server; + listen [::]:80 default_server; + + location / { + proxy_pass http://springboot; + # CORS 관련 헤더 추가 + add_header 'Access-Control-Allow-Origin' '*'; + add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS'; + add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type'; + proxy_http_version 1.1; + proxy_set_header Connection $connection_upgrade; + proxy_set_header Upgrade $http_upgrade; + + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + + access_log /var/log/nginx/access.log main; + + client_header_timeout 60; + client_body_timeout 60; + keepalive_timeout 60; + gzip off; + gzip_comp_level 4; + + # Include the Elastic Beanstalk generated locations + include conf.d/elasticbeanstalk/healthd.conf; + } +} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..6bd839e --- /dev/null +++ b/Dockerfile @@ -0,0 +1,18 @@ +# 기본 이미지 설정 +FROM eclipse-temurin:17-jdk + +# 현재 리포지토리, api gateway의 코드를 바탕으로 JAR 생성 +ARG JAR_FILE=build/libs/*.jar +COPY ${JAR_FILE} app.jar + +# 최종 이미지 생성 +FROM eclipse-temurin:17-jdk +COPY --from=0 app.jar app.jar + +# 애플리케이션 실행 스크립트 추가 +COPY dev_start.sh start.sh +RUN chmod +x dev_start.sh + +EXPOSE 8080 + +ENTRYPOINT ["./start.sh"] diff --git a/Dockerrun.aws.json b/Dockerrun.aws.json new file mode 100644 index 0000000..57f0fc7 --- /dev/null +++ b/Dockerrun.aws.json @@ -0,0 +1,12 @@ +{ + "AWSEBDockerrunVersion": "1", + "Image": { + "Name": "851725177732.dkr.ecr.ap-northeast-2.amazonaws.com/tree-dev:latest", + "Update": "true" + }, + "Ports": [ + { + "ContainerPort": "8080" + } + ] +} \ No newline at end of file diff --git a/build.gradle b/build.gradle index e4e1a65..d2b58fb 100644 --- a/build.gradle +++ b/build.gradle @@ -22,15 +22,29 @@ repositories { } dependencies { + + // spring data jpa implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + + // spring MVC implementation 'org.springframework.boot:spring-boot-starter-web' - implementation 'io.jsonwebtoken:jjwt-api:0.11.5' + + // swagger implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0' + + // jwt + implementation 'io.jsonwebtoken:jjwt-api:0.11.5' runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5' runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5' + + // lombok compileOnly 'org.projectlombok:lombok' - runtimeOnly 'com.h2database:h2' annotationProcessor 'org.projectlombok:lombok' + + // mysql + runtimeOnly 'com.mysql:mysql-connector-j' + + runtimeOnly 'com.h2database:h2' testImplementation 'org.springframework.boot:spring-boot-starter-test' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" } diff --git a/dev_start.sh b/dev_start.sh new file mode 100644 index 0000000..bdc2ce6 --- /dev/null +++ b/dev_start.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +# 애플리케이션을 백그라운드에서 실행 +java -jar -Dspring.profiles.active=common,dev app.jar & + +# 모든 자식 프로세스가 종료될 때까지 대기 +wait diff --git a/src/main/java/org/example/tree/domain/root/controller/RootController.java b/src/main/java/org/example/tree/domain/root/controller/RootController.java new file mode 100644 index 0000000..e176ccd --- /dev/null +++ b/src/main/java/org/example/tree/domain/root/controller/RootController.java @@ -0,0 +1,13 @@ +package org.example.tree.domain.root.controller; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class RootController { + + @GetMapping("/health") + public String healthCheck(){ + return "I'm healty!"; + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..6a0994b --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,95 @@ +# default profile +spring: + application: + name: tree-server + profiles: + group: + dev: common, dev + +--- +spring: + config: + activate: + on-profile: "common" +springdoc: + swagger-ui: + tags-sorter: alpha # alpha: 알파벳 순 태그 정렬, method: HTTP Method 순 정렬 + operations-sorter: alpha # alpha: 알파벳 순 태그 정렬, method: HTTP Method 순 정렬 + cache: + disabled: true + use-fqn: true +--- +spring: + application: + name: tree-server-dev + config: + activate: + on-profile: "dev" + datasource: + username: ${aws.db.username} + password: ${aws.db.password} + url: jdbc:mysql://${aws.db.url}/${aws.db.name}?autoReconnect=true&setTimezone=Asia/Seoul + driver-class-name: com.mysql.cj.jdbc.Driver + sql: + init: + mode: never + jpa: + properties: + hibernate: + dialect: org.hibernate.dialect.MySQLDialect + show_sql: true + format_sql: true + use_sql_comments: true + hbm2ddl: + auto: update + default_batch_fetch_size: 1000 +jwt: + header: Authorization + # dev server + secret: + key: ${JWT_SECRET} + # secret : ${JWT_SECRET} + authorities-key: authoritiesKey + access-token-validity-in-seconds: 1210000000 # 30 m + refresh-token-validity-in-seconds: 1210000000 # 14 d + +--- +#spring: +# config: +# activate: +# on-profile: release +# datasource: +# username: ${aws.db.username} +# password: ${aws.db.password} +# url: ${aws.db.url} +# driver-class-name: com.mysql.cj.jdbc.Driver +# sql: +# init: +# mode: never +# jpa: +# properties: +# hibernate: +# dialect: org.hibernate.dialect.MySQLDialect +# # show_sql: true +# # format_sql: true +# use_sql_comments: true +# hbm2ddl: +# auto: update +# default_batch_fetch_size: 1000 +# data: +# redis: +# host: ${release.redis.host} +# port: 6379 +#jwt: +# header: Authorization +# # dev server +# secret: ${JWT_SECRET} +# # secret : ${JWT_SECRET} +# authorities-key: authoritiesKey +# access-token-validity-in-seconds: 1210000000 # 30 m +# refresh-token-validity-in-seconds: 1210000000 # 14 d +# +#openai: +# token: ${OPEN_API_TOKEN} +# url: +# chat: https://api.openai.com/v1/chat/completions