diff --git a/.github/workflows/apiDocs.yml b/.github/workflows/apiDocs.yml index 611e3c85..a62a6726 100644 --- a/.github/workflows/apiDocs.yml +++ b/.github/workflows/apiDocs.yml @@ -1,4 +1,4 @@ -name: API Docs Integration +name: api-docs-integration on: push: diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml new file mode 100644 index 00000000..be800e1e --- /dev/null +++ b/.github/workflows/cd.yml @@ -0,0 +1,143 @@ +name: Backend CI/CD with Gradle + +on: + # workflow_dispatch: + # inputs: + # service: + # description: 'Choose Service to deploy. Default is all.' + # required: true + # default: 'all' + # type: choice + # options: + # - all + # - api-gateway + # - noti-service + # - user-service + # - weather-service + push: + branches: + - prod + +jobs: + build: + runs-on: ubuntu-latest + + #Checkout : 코드 가져오기 + steps: + - name: Checkout + uses: actions/checkout@v3 + + #Setting JDK + - name: Set up JDK 17 + uses: actions/setup-java@v2 + with: + java-version: '17' + distribution: 'temurin' + + #gradlew chmod + - name: Grant execute permission for gradlew + run: | + chmod +x ./apiGateway-service/gradlew + chmod +x ./config-service/gradlew + chmod +x ./Eureka/gradlew + chmod +x ./noti-service/gradlew + chmod +x ./user-service/gradlew + chmod +x ./weather-service/gradlew + + # DockerHub Login + - name: Docker Hub Login + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + #Config-Service Build & Docker Push + - name: Build with Gradle - config + run: | + cd config-service + ./gradlew clean build -x test + docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/waither-config . + docker push ${{ secrets.DOCKERHUB_USERNAME }}/waither-config + env: + CONFIG_GIT_URI: ${{ secrets.CONFIG_GIT_URI }} + CONFIG_PASSPHRASE: ${{ secrets.CONFIG_PASSPHRASE }} + CONFIG_PRIVATE_KEY: ${{ secrets.CONFIG_PRIVATE_KEY }} + + #Config-Service Run + - name: Run config-service + run: | + ./gradlew bootRun & + cd .. + #Wait 5 sec + - name: Wait for config-service to start + run: sleep 5 + + #apiGateway-Service Build & Docker Push + - name: Build with Gradle - apiGateway + run: | + cd apiGateway-service + ./gradlew clean build -x test + docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/waither-gateway . + docker push ${{ secrets.DOCKERHUB_USERNAME }}/waither-gateway + cd .. + + #Eureka Build & Docker Push + - name: Build with Gradle - Eureka + run: | + cd Eureka + ./gradlew clean build -x test + docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/waither-eureka . + docker push ${{ secrets.DOCKERHUB_USERNAME }}/waither-eureka + cd .. + + #Noti-Service Build & Docker Push + - name: Build with Gradle - noti-service + run: | + cd noti-service + ./gradlew clean build -x test + docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/waither-noti . + docker push ${{ secrets.DOCKERHUB_USERNAME }}/waither-noti + cd .. + + #User-Service Build & Docker Push + - name: Build with Gradle - user-service + run: | + cd user-service + ./gradlew clean build -x test + docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/waither-user . + docker push ${{ secrets.DOCKERHUB_USERNAME }}/waither-user + cd .. + + #Weather-Service Build & Docker Push + - name: Build with Gradle - weather-service + run: | + cd weather-service + ./gradlew clean build -x test + docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/waither-weather . + docker push ${{ secrets.DOCKERHUB_USERNAME }}/waither-weather + cd .. + + deploy: + name : Connect to EC2 and re-run container + needs : build + runs-on : ubuntu-latest + steps: + - name: Deploy to Server + uses: appleboy/ssh-action@v0.1.6 + env: + SERVICE_NAME: all + with: + host: ${{ secrets.EC2_HOST }} + username: ${{ secrets.EC2_USERNAME }} + password: ${{ secrets.EC2_PASSWORD }} + port: ${{ secrets.EC2_SSH_PORT }} + timeout: 60s + script: | + export CONFIG_GIT_URI=${{ secrets.CONFIG_GIT_URI }} + export CONFIG_PASSPHRASE=${{ secrets.CONFIG_PASSPHRASE }} + export CONFIG_PRIVATE_KEY=${{ secrets.CONFIG_PRIVATE_KEY_DIR }} + export DOCKERHUB_USERNAME=${{ secrets.DOCKERHUB_USERNAME }} + + bash /home/docker_init.sh + bash /home/common.sh + bash /home/deploy.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..8be6819b --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,63 @@ +name: Backend CI + +on: + pull_request: + branches: + - prod + - develop + +jobs: + build-test: + runs-on: ubuntu-latest + + steps: + - name: action checkout + uses: actions/checkout@v3 + + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'temurin' + + - name: Grant execute permission for gradlew + run: | + chmod +x ./config-service/gradlew + chmod +x ./noti-service/gradlew + chmod +x ./user-service/gradlew + chmod +x ./weather-service/gradlew + + - name: Build with Gradle - config + run: | + cd config-service + ./gradlew clean build -x test + env: + CONFIG_GIT_URI: ${{ secrets.CONFIG_GIT_URI }} + CONFIG_PASSPHRASE: ${{ secrets.CONFIG_PASSPHRASE }} + CONFIG_PRIVATE_KEY: ${{ secrets.CONFIG_PRIVATE_KEY }} + + - name: Run config-service + run: | + ./gradlew bootRun & + cd .. + + - name: Wait for config-service to start + run: sleep 5 + + - name: Build with Gradle - noti-service + run: | + cd noti-service + ./gradlew clean build -x test + cd .. + + - name: Build with Gradle - user-service + run: | + cd user-service + ./gradlew clean build -x test + cd .. + + - name: Build with Gradle - weather-service + run: | + cd weather-service + ./gradlew clean build -x test + cd .. diff --git a/Eureka/Dockerfile b/Eureka/Dockerfile new file mode 100644 index 00000000..119a7cac --- /dev/null +++ b/Eureka/Dockerfile @@ -0,0 +1,5 @@ +FROM openjdk:17-jdk + +ARG JAR_FILE=build/libs/*.jar +COPY ${JAR_FILE} app.jar +ENTRYPOINT ["java", "-Dspring.profiles.active=prod", "-Duser.timezone=Asia/Seoul" , "-jar", "app.jar"] diff --git a/Eureka/build.gradle b/Eureka/build.gradle index 5e825c0e..b15bb695 100644 --- a/Eureka/build.gradle +++ b/Eureka/build.gradle @@ -11,6 +11,10 @@ java { sourceCompatibility = '17' } +jar { + enabled = false +} + repositories { mavenCentral() } diff --git a/apiGateway-service/Dockerfile b/apiGateway-service/Dockerfile new file mode 100644 index 00000000..119a7cac --- /dev/null +++ b/apiGateway-service/Dockerfile @@ -0,0 +1,5 @@ +FROM openjdk:17-jdk + +ARG JAR_FILE=build/libs/*.jar +COPY ${JAR_FILE} app.jar +ENTRYPOINT ["java", "-Dspring.profiles.active=prod", "-Duser.timezone=Asia/Seoul" , "-jar", "app.jar"] diff --git a/apiGateway-service/build.gradle b/apiGateway-service/build.gradle index 09ccb560..a394c8d4 100644 --- a/apiGateway-service/build.gradle +++ b/apiGateway-service/build.gradle @@ -11,6 +11,10 @@ java { sourceCompatibility = '17' } +jar { + enabled = false +} + repositories { mavenCentral() } diff --git a/apiGateway-service/src/main/java/com/waither/apigatewayservice/ApiGatewayServiceApplication.java b/apiGateway-service/src/main/java/com/waither/apigatewayservice/ApiGatewayServiceApplication.java index b8616054..633a6ddd 100644 --- a/apiGateway-service/src/main/java/com/waither/apigatewayservice/ApiGatewayServiceApplication.java +++ b/apiGateway-service/src/main/java/com/waither/apigatewayservice/ApiGatewayServiceApplication.java @@ -2,11 +2,9 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Bean; -import org.springframework.security.config.web.server.ServerHttpSecurity; -import org.springframework.security.web.server.SecurityWebFilterChain; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; -@SpringBootApplication +@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) public class ApiGatewayServiceApplication { public static void main(String[] args) { diff --git a/apiGateway-service/src/main/resources/bootstrap.yml b/apiGateway-service/src/main/resources/bootstrap.yml index 62f93fcd..0e3fbdf2 100644 --- a/apiGateway-service/src/main/resources/bootstrap.yml +++ b/apiGateway-service/src/main/resources/bootstrap.yml @@ -1,5 +1,5 @@ server: - port: 8000 + port: 80 spring: application: diff --git a/config-service/Dockerfile b/config-service/Dockerfile new file mode 100644 index 00000000..119a7cac --- /dev/null +++ b/config-service/Dockerfile @@ -0,0 +1,5 @@ +FROM openjdk:17-jdk + +ARG JAR_FILE=build/libs/*.jar +COPY ${JAR_FILE} app.jar +ENTRYPOINT ["java", "-Dspring.profiles.active=prod", "-Duser.timezone=Asia/Seoul" , "-jar", "app.jar"] diff --git a/config-service/build.gradle b/config-service/build.gradle index 0a43bd2f..58164f4d 100644 --- a/config-service/build.gradle +++ b/config-service/build.gradle @@ -11,6 +11,10 @@ java { sourceCompatibility = '17' } +jar { + enabled = false +} + repositories { mavenCentral() } diff --git a/config-service/src/main/resources/bootstrap.yml b/config-service/src/main/resources/bootstrap.yml index 82864fe0..f312c907 100644 --- a/config-service/src/main/resources/bootstrap.yml +++ b/config-service/src/main/resources/bootstrap.yml @@ -5,4 +5,12 @@ spring: profiles: active: dev application: - name: config-service \ No newline at end of file + name: config-service + cloud: + config: + server: + git: + uri: ${CONFIG_GIT_URI} + ignore-local-ssh-settings: true + passphrase: ${CONFIG_PASSPHRASE} + private-key: ${CONFIG_PRIVATE_KEY} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..55c98432 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,89 @@ +version: '3' + +services: + +# nginx: +# container_name: nginx +# image: nginx:latest +# ports: +# - "80:80" +# volumes: +# - /home/ec2-user/nginx.conf:/etc/nginx/nginx.conf +# depends_on: +# - "db" +# restart: always #항상 재실행 + + api-gateway: + container_name: api-gateway + image: ${DOCKERHUB_USERNAME}/waither-gateway + expose: + - "8000" + restart: always + volumes: + - /home/ec2-user/logs/api-gateway:/logs + + config: + container_name: config + image: ${DOCKERHUB_USERNAME}/waither-config + expose: + - "8888" + environment: + CONFIG_GIT_URI : ${CONFIG_GIT_URI} + CONFIG_PASSPHRASE : ${CONFIG_PASSPHRASE} + CONFIG_PRIVATE_KEY : ${CONFIG_PRIVATE_KEY} + restart: always + volumes: + - /home/ec2-user/logs/config:/logs + + eureka: + container_name: eureka + image: ${DOCKERHUB_USERNAME}/waither-eureka + expose: + - "8761" + restart: always + volumes: + - /home/ec2-user/logs/eureka:/logs + + user-service: + container_name: user-service + image: ${DOCKERHUB_USERNAME}/waither-user + expose: + - "8080" + restart: unless-stopped #수동으로 중지되지 않는 이상 항상 재실행 + volumes: + - /home/ec2-user/logs/user-service:/logs + + weather-service: + container_name: weather-service + image: ${DOCKERHUB_USERNAME}/waither-weather + expose: + - "8081" + restart: unless-stopped + volumes: + - /home/ec2-user/logs/weather-service:/logs + + noti-service: + container_name: noti-service + image: ${DOCKERHUB_USERNAME}/waither-noti + expose: + - "8082" + restart: unless-stopped + volumes: + - /home/ec2-user/logs/noti-service:/logs + + zookeeper: + image: wurstmeister/zookeeper:latest + container_name: zookeeper + ports: + - "2181:2181" + - + kafka: + image: wurstmeister/kafka:latest + container_name: kafka + ports: + - "9092:9092" + environment: + KAFKA_ADVERTISED_HOST_NAME: 127.0.0.1 + KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 + volumes: + - /var/run/docker.sock:/var/run/docker.sock diff --git a/noti-service/Dockerfile b/noti-service/Dockerfile new file mode 100644 index 00000000..119a7cac --- /dev/null +++ b/noti-service/Dockerfile @@ -0,0 +1,5 @@ +FROM openjdk:17-jdk + +ARG JAR_FILE=build/libs/*.jar +COPY ${JAR_FILE} app.jar +ENTRYPOINT ["java", "-Dspring.profiles.active=prod", "-Duser.timezone=Asia/Seoul" , "-jar", "app.jar"] diff --git a/noti-service/build.gradle b/noti-service/build.gradle index ab43a93a..5f7e2ca0 100644 --- a/noti-service/build.gradle +++ b/noti-service/build.gradle @@ -12,6 +12,10 @@ java { sourceCompatibility = '17' } +jar { + enabled = false +} + repositories { mavenCentral() } diff --git a/script/common.sh b/script/common.sh new file mode 100644 index 00000000..7fef6ff7 --- /dev/null +++ b/script/common.sh @@ -0,0 +1,39 @@ +# 현재 실행중인 도커 컨테이너중 단어가 포함 컨테이너를 검색 +RUNNING_EUREKA=$(docker ps | grep eureka) +RUNNING_CONFIG=$(docker ps | grep config) +RUNNING_ZOOKEEPER=$(docker ps | grep zookeeper) +RUNNING_KAFKA=$(docker ps | grep kafka) + +# Eureka 검색 +if [ -z "$RUNNING_EUREKA" ]; then + echo "Starting Eureka ..." + docker compose -f /home/docker-compose.yml up -d eureka + sleep 5 #Eureka 실행 5초 대기 +else + echo "Eureka is already running" +fi + +# Config 검색 +if [ -z "$RUNNING_CONFIG" ]; then + echo "Starting Config Service ..." + docker compose -f /home/docker-compose.yml up -d config + sleep 5 #Config 실행 5초 대기 +else + echo "Config Service is already running" +fi + +# Zookeeper 검색 +if [ -z "$RUNNING_ZOOKEEPER" ]; then + echo "Starting Zookeeper ..." + docker compose -f /home/docker-compose.yml up -d zookeeper +else + echo "Zookeeper is already running" +fi + +# Kafka 검색 +if [ -z "$RUNNING_ZOOKEEPER" ]; then + echo "Starting Kafka ..." + docker compose -f /home/docker-compose.yml up -d kafka +else + echo "Kafka is already running" +fi \ No newline at end of file diff --git a/script/deploy.sh b/script/deploy.sh new file mode 100644 index 00000000..5a87944c --- /dev/null +++ b/script/deploy.sh @@ -0,0 +1,19 @@ +#!/bin/bash +#셔뱅(shebang) + + +# 타겟 서비스 재배포 시작 +#echo "$TARGET_SERVICE Deploy..." +# docker compose 를 실행함. +# -f 옵션 : docker-compose 명령에서 사용할 compose 파일의 경로를 지정. (file) +# up : docker-compose 컨테이너를 시작하는 명령. 지정된 서비스의 컨테이너를 빌드, 생성 및 시작 (버전 최신화) +# -d : detached 모드. 컨테이너를 백그라운드에서 실행 + +echo "api-gateway server start..." +docker compose -f /home/docker-compose.yml up -d api-gateway +echo "noti-service server start..." +docker compose -f /home/docker-compose.yml up -d noti-service +echo "user-service server start..." +docker compose -f /home/docker-compose.yml up -d user-service +echo "weather-service server start..." +docker compose -f /home/docker-compose.yml up -d weather-service diff --git a/script/docker_init.sh b/script/docker_init.sh new file mode 100644 index 00000000..6751a9cb --- /dev/null +++ b/script/docker_init.sh @@ -0,0 +1,46 @@ +#!/bin/bash +#셔뱅(shebang) : 이 스크립트가 Bash Shell 에서 실행되어야 함을 나타냄. + +# Docker install +#Docker 명령어가 존재하는지 확인함. +# -v 옵션 : 명령어 실행 전 명령어와 인자 출력. +# &> 명령어 출력과 오류 메세지를 리다이렉션. /dev/null 디바이스로 리다이렉션함. +# docker 명령어의 경로를 출력하고(stdout), 발생할 수 있는 오류 메세지(stderr)와 함께 /dev/null 디바이스로 리다이렉션. +# /dev/null 은 특수한 디바이스 파일. 쓰기 작업을 수행할 수 있지만 모든 데이터를 버림. +# 즉, 명령어의 출력과 오류 메세지를 모두 숨길 수 있음. +# 결론적으로 명령어의 존재 여부를 확인. 또는 명령어 실행 결괄르 사용하지 않고 성공/실패 여부만 확인하게 됨. +if ! command -v docker &> /dev/null; then + # docker 명령어가 실패했을 경우 -> docker 가 설치되어 있지 않음. docker 설치를 진행함. + echo "Docker is not installed..." + echo "Docker install start..." + # yum : Red Hat 기반의 Linux 배포판에서 사용되는 패키지 관리 도구. 업데이트. + # 원래는 dnf 사용했지만 yum 으로 대체되고 있음. + sudo yum update -y + # docker 설치 + sudo yum install -y docker + # docker 권한 설정 + sudo usermod -aG docker ec2-user + sudo systemctl enable --now docker + echo "Docker install complete" +else + # Docker 명령어가 성공했을 경우. Docker 가 이미 설치됨을 출력 + echo "Docker is already installed" +fi + +# Docker-compose install +# Docker-compose 설치 +if ! command -v docker compose &> /dev/null; then + echo "Docker-compose is not installed..." + echo "Docker-compose install start..." + + #Docker compose plugin 설치 - Amazon Linux 2023 + sudo mkdir -p /usr/local/lib/docker/cli-plugins/ + sudo curl -SL "https://github.com/docker/compose/releases/latest/download/docker-compose-linux-$(uname -m)" -o /usr/local/lib/docker/cli-plugins/docker-compose + sudo chmod +x /usr/local/lib/docker/cli-plugins/docker-compose + + docker compose version + + echo "Docker-compose install complete!" +else + echo "Docker-compose is already installed" +fi \ No newline at end of file diff --git a/user-service/Dockerfile b/user-service/Dockerfile new file mode 100644 index 00000000..119a7cac --- /dev/null +++ b/user-service/Dockerfile @@ -0,0 +1,5 @@ +FROM openjdk:17-jdk + +ARG JAR_FILE=build/libs/*.jar +COPY ${JAR_FILE} app.jar +ENTRYPOINT ["java", "-Dspring.profiles.active=prod", "-Duser.timezone=Asia/Seoul" , "-jar", "app.jar"] diff --git a/user-service/build.gradle b/user-service/build.gradle index 76cda7f2..6889f5ea 100644 --- a/user-service/build.gradle +++ b/user-service/build.gradle @@ -12,6 +12,10 @@ java { sourceCompatibility = '17' } +jar { + enabled = false +} + configurations { compileOnly { extendsFrom annotationProcessor diff --git a/weather-service/Dockerfile b/weather-service/Dockerfile new file mode 100644 index 00000000..119a7cac --- /dev/null +++ b/weather-service/Dockerfile @@ -0,0 +1,5 @@ +FROM openjdk:17-jdk + +ARG JAR_FILE=build/libs/*.jar +COPY ${JAR_FILE} app.jar +ENTRYPOINT ["java", "-Dspring.profiles.active=prod", "-Duser.timezone=Asia/Seoul" , "-jar", "app.jar"] diff --git a/weather-service/build.gradle b/weather-service/build.gradle index b8f2ebb3..37100a13 100644 --- a/weather-service/build.gradle +++ b/weather-service/build.gradle @@ -12,6 +12,10 @@ java { sourceCompatibility = '17' } +jar { + enabled = false +} + repositories { mavenCentral() }