๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
DevOps

GitHub-Actions๋กœ CI/CD ๊ตฌ์ถ•ํ•˜๊ธฐ(AWS, Docker, SpringBoot)

by ์ฃผ๋ฐœ2 2022. 5. 3.
๋ฐ˜์‘ํ˜•

GitHub-Actions๋กœ CI/CD ๊ตฌ์ถ•ํ•˜๊ธฐ(AWS, Docker, SpringBoot)

 

์•ˆ๋…•ํ•˜์„ธ์š”, ์ด๋ฒˆ ์‹œ๊ฐ„์—๋Š” GitHub-Actions๋กœ CI/CD๋ฅผ ๊ตฌ์ถ•ํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

ํ•ด๋‹น ํฌ์ŠคํŒ…์ด CI/CD๋ฅผ ์ „์ฒด์ ์œผ๋กœ ํฌํ•จํ•˜๊ณ  ์žˆ๊ธฐ๋Š” ํ•˜์ง€๋งŒ, ๊ณผ๊ฑฐ Maven ๊ธฐ๋ฐ˜์œผ๋กœ CI๋ฅผ ๊ตฌ์ถ•ํ•œ ํฌ์ŠคํŒ…์ด ์žˆ๋Š”๋ฐ์š”, ๊ถ๊ธˆํ•˜์‹œ๋ฉด ์ฐธ๊ณ ํ•˜์…”๋„ ์ข‹์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค ๐Ÿ˜ƒ

 

์ €๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ™˜๊ฒฝ์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ตฌ์ถ•ํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.

  • GitHub-Actions
  • AWS EC2
  • Docker, Docker-Compose
  • Spring Boot
  • Spring profiles(prod, dev)
  • Gradle

GitHub-Actions์™€ Docker, Docker-Compose๋ฅผ ํ†ตํ•ด Spring Boot์˜ ํ™˜๊ฒฝ๋ณ„(prod, dev) ํŒŒ์ผ์„ AWS EC2 ์ธ์Šคํ„ด์Šค์— ์ž๋™๋ฐฐํฌ ํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

(์ฝ”๋“œ๋Š” ๊นƒํ—ˆ๋ธŒ ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.)

 

๊ธฐ๋ณธ์ ์œผ๋กœ AWS ๊ณ„์ •์„ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‚˜ ์Šคํ”„๋ง ๋ถ€ํŠธ ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ, Spring profiles์— ๋Œ€ํ•ด ๋”ฐ๋กœ ์„ค๋ช…์€ ์ƒ๋žตํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

Spring profiles์˜ ๊ฒฝ์šฐ Maven ๊ธฐ๋ฐ˜์ด๊ธด ํ•˜์ง€๋งŒ ๊ณผ๊ฑฐ ํฌ์ŠคํŒ…์„ ์ฐธ๊ณ ํ•ด์ฃผ์„ธ์š” :)

 

 

 

๐Ÿ“Ž AWS EC2 ์ธ์Šคํ„ด์Šค ์ƒ์„ฑํ•˜๊ธฐ

๋จผ์ € AWS ์‚ฌ์ดํŠธ์—์„œ EC2 ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

 

๊ณผ๊ฑฐ EC2 ์ธ์Šคํ„ด์Šค ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ํฌ์ŠคํŒ…์„ ํ•œ ์ ์ด ์žˆ์ง€๋งŒ, ํ•ด๋‹น ํฌ์ŠคํŒ…์—์„œ๋Š” ๊ณผ๊ฑฐ ๋ฒ„์ „์„ ๊ธฐ์ค€์œผ๋กœ ์ž‘์„ฑ๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ํ˜„์žฌ UI์™€๋Š” ๋‹ค๋ฆ…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ํ˜„์žฌ์˜ UI๋Š” ๋งค์šฐ ๋‹จ์ˆœํ•ด์กŒ๊ธฐ ๋•Œ๋ฌธ์— ์†์‰ฝ๊ฒŒ ์ƒ์„ฑ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

์ด๋ฆ„๊ณผ ํ‚ค ํŽ˜์–ด๋ฅผ ์ƒ์„ฑํ•œ ํ›„ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

ํ‚ค ํŽ˜์–ด๋Š” ์ค‘์š”ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ƒˆ๋กœ์šด ํ‚ค ํŽ˜์–ด๋ฅผ ์ƒ์„ฑํ•œ ํ›„ ๋ฐ˜๋“œ์‹œ ๋ณด๊ด€ํ•˜๊ณ , ๋ถ„์‹ค๋˜์ง€ ์•Š๋„๋ก ์œ ์˜ํ•ฉ๋‹ˆ๋‹ค.

 

๊ทธ ์™ธ ๋”ฐ๋กœ ์†๋Œ„๋ถ€๋ถ„์€ ์—†์œผ๋ฉฐ, ์ถ”ํ›„ ์šฉ๋Ÿ‰์ด ๋ถ€์กฑํ•  ๊ฒฝ์šฐ๋„ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ํ™”๋ฉด ์•„๋ž˜์—์„œ ์Šคํ† ๋ฆฌ์ง€ ๊ตฌ์„ฑ์—์„œ ํ”„๋ฆฌํ‹ฐ์–ด ๊ธฐ์ค€ ์ตœ๋Œ€ ์šฉ๋Ÿ‰(30GB)์„ ๊ธฐ์ค€์œผ๋กœ ์ ๋‹นํžˆ ๋Š˜๋ ค์ฃผ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

๋˜ํ•œ ๋ณธ์ธ์˜ ์ƒํ™ฉ์— ๋งž๊ฒŒ ํ•„์š”ํ•œ ๋ณด์•ˆ๊ทธ๋ฃน(์ธ๋ฐ”์šด๋“œ ๊ทœ์น™)์„ ์ ์ ˆํžˆ ์„ค์ •ํ•ด์ฃผ์„ธ์š”.

์ €๋Š” 8080(Tomcat), 27017(MongoDB), 5431(PostgreSQL) ๋“ฑ๋“ฑ์— ๋Œ€ํ•ด ์ธ๋ฐ”์šด๋“œ ๊ทœ์น™์„ ํ—ˆ์šฉํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.

 

 

 

๐Ÿ“Ž AWS EC2 ์ธ์Šคํ„ด์Šค ๋„์ปค ์„ค์น˜ํ•˜๊ธฐ

EC2 ์ธ์Šคํ„ด์Šค์—์„œ ์ž‘์—…์— ํ•„์š”ํ•  Docker์™€ Docker-Compose๋ฅผ ์„ค์น˜ํ•ด์ฃผ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

๊ด€๋ จํ•˜์—ฌ ์ž๋ฃŒ๊ฐ€ ๋งŽ๊ธฐ ๋•Œ๋ฌธ์— ์ƒ์„ธํ•œ ์„ค๋ช…์€ ์ƒ๋žตํ•˜๊ณ , ์•„๋ž˜์™€ ๊ฐ™์ด ๊ฐ„๋‹จํžˆ ๊ตฌ์ถ•ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

// ๋„์ปค ์„ค์น˜
sudo yum install docker -y

// ๋„์ปค ์‹คํ–‰
sudo service docker start

// ๋„์ปค ์ƒํƒœ ํ™•์ธ
systemctl status docker.service

// Docker ๊ด€๋ จ ๊ถŒํ•œ ์ถ”๊ฐ€
sudo chmod 666 /var/run/docker.sock
docker ps

// ๋„์ปค ์ปดํฌ์ฆˆ ์„ค์น˜
sudo curl \
-L "https://github.com/docker/compose/releases/download/1.26.2/docker-compose-$(uname -s)-$(uname -m)" \
-o /usr/local/bin/docker-compose

// ๊ถŒํ•œ ์ถ”๊ฐ€
sudo chmod +x /usr/local/bin/docker-compose

// ๋ฒ„์ „ ํ™•์ธ
docker-compose --version

 

 

 

๐Ÿ“Ž GitHub-Actions ์Šคํฌ๋ฆฝํŠธ ํŒŒ์ผ ์ƒ์„ฑํ•˜๊ธฐ

๋‹ค์Œ์œผ๋กœ ๋ฐฐํฌ์— ์‚ฌ์šฉํ•  GitHub-Actions ์Šคํฌ๋ฆฝํŠธ ํŒŒ์ผ์„ ์ƒ์„ฑํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 

GitHub ๋ ˆํฌ์ง€ํ† ๋ฆฌ - Actions - Continuous integration์—์„œ Java with Gradle์˜ Configure๋ฅผ ํด๋ฆญํ•ฉ๋‹ˆ๋‹ค.

 

๊ทธ๋Ÿผ ์•„๋ž˜ ์‚ฌ์ง„๊ณผ ๊ฐ™์ด ~/.github/workflow/gradle.yml ์ด๋ผ๋Š” ํŒŒ์ผ์ด ์ƒ์„ฑ์ด ๋  ํ…๋ฐ์š”, ์•„๋ž˜์ฒ˜๋Ÿผ ์ƒ์„ฑํ•ด์ฃผ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

gradle.yml ๋ผ๋Š” ํŒŒ์ผ์€ ํ˜„์žฌ gradle build ์ „์šฉ ์Šคํฌ๋ฆฝํŠธ ํŒŒ์ผ์ด๊ณ , ํŒŒ์ผ๋ช…์€ ์ž์œ ๋กญ๊ฒŒ ๋ณ€๊ฒฝํ•˜์…”๋„ ๋ฉ๋‹ˆ๋‹ค.

(์ €๋Š” github-actions.yml ๋กœ ๋ณ€๊ฒฝํ–ˆ์Šต๋‹ˆ๋‹ค ๐Ÿ˜ƒ)

 

 

๐ŸŒˆ github-actions.yml

github-actions.yml(gradle.yml) ํŒŒ์ผ์˜ ์ „์ฒด ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€๋ฐ์š”, ํ•˜๋‚˜์”ฉ ์„ค๋ช…๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค.

์ด ๋ถ€๋ถ„์€ ์ €์˜ ์ƒํ™ฉ ๋ฐ ํ™˜๊ฒฝ์— ๋งž๊ฒŒ ์ปค์Šคํ…€ ํ•œ ๊ฒฐ๊ณผ์ด๊ธฐ ๋•Œ๋ฌธ์—, ๋ณธ์ธ์˜ ํ™˜๊ฒฝ์— ๋งž๊ฒŒ ์ˆ˜์ •์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

๋˜ํ•œ ์ €๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๊ฐœ๋ฐœํ™˜๊ฒฝ(dev)์„ ๊ธฐ์ค€์œผ๋กœ ํฌ์ŠคํŒ…์„ ํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.

# github repository Actions ํŽ˜์ด์ง€์— ๋‚˜ํƒ€๋‚ผ ์ด๋ฆ„
name: CI/CD

# event trigger
on:
  push:
    branches:
      - main
      - develop

permissions:
  contents: read

jobs:
  CI-CD:
    runs-on: ubuntu-latest
    steps:

    ## jdk setting
    - uses: actions/checkout@v3
    - name: Set up JDK 11
      uses: actions/setup-java@v3
      with:
        java-version: '11'
        distribution: 'temurin' # https://github.com/actions/setup-java

    ## gradle caching
    - name: Gradle Caching
      uses: actions/cache@v3
      with:
        path: |
          ~/.gradle/caches
          ~/.gradle/wrapper
        key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
        restore-keys: |
          ${{ runner.os }}-gradle-

    ## create application-dev.properties
    - name: make application-dev.properties
      if: contains(github.ref, 'develop')
      run: |
        cd ./src/main/resources
        touch ./application-dev.properties
        echo "${{ secrets.PROPERTIES_DEV }}" > ./application-dev.properties
      shell: bash

    ## create application-prod.properties
    - name: make application-prod.properties
      if: contains(github.ref, 'main')
      run: |
          cd ./src/main/resources
          touch ./application-prod.properties
          echo "${{ secrets.PROPERTIES_PROD }}" > ./application-prod.properties
      shell: bash

    ## create firebase-key.json
    - name: create firebase key
      run: |
        cd ./src/main/resources
        ls -a .
        touch ./firebase-service-key.json
        echo "${{ secrets.FIREBASE_KEY }}" > ./firebase-service-key.json
      shell: bash

    ## gradle build
    - name: Build with Gradle
      run: ./gradlew build -x test -x ktlintCheck -x ktlintTestSourceSetCheck -x ktlintMainSourceSetCheck -x ktlintKotlinScriptCheck

    ## docker build & push to production
    - name: Docker build & push to prod
      if: contains(github.ref, 'main')
      run: |
          docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
          docker build -f Dockerfile-prod -t ${{ secrets.DOCKER_REPO }}/dotoriham-prod .
          docker push ${{ secrets.DOCKER_REPO }}/dotoriham-prod

    ## docker build & push to develop
    - name: Docker build & push to dev
      if: contains(github.ref, 'develop')
      run: |
          docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
          docker build -f Dockerfile-dev -t ${{ secrets.DOCKER_REPO }}/dotoriham-dev .
          docker push ${{ secrets.DOCKER_REPO }}/dotoriham-dev

    ## deploy to production
    - name: Deploy to prod
      uses: appleboy/ssh-action@master
      id: deploy-prod
      if: contains(github.ref, 'main')
      with:
          host: ${{ secrets.HOST_PROD }}
          username: ec2-user
          key: ${{ secrets.PRIVATE_KEY }}
          envs: GITHUB_SHA
          script: |
              sudo docker rm -f $(docker ps -qa)
              sudo docker pull ${{ secrets.DOCKER_REPO }}/dotoriham-prod
              docker-compose up -d
              docker image prune -f

    ## deploy to develop
    - name: Deploy to dev
      uses: appleboy/ssh-action@master
      id: deploy-dev
      if: contains(github.ref, 'develop')
      with:
        host: ${{ secrets.HOST_DEV }}
        username: ${{ secrets.USERNAME }}
        password: ${{ secrets.PASSWORD }}
        port: 22
        #key: ${{ secrets.PRIVATE_KEY }}
        script: |
            sudo docker rm -f $(docker ps -qa)
            sudo docker pull ${{ secrets.DOCKER_REPO }}/dotoriham-dev
            docker-compose up -d
            docker image prune -f

  ## time
  current-time:
    needs: CI-CD
    runs-on: ubuntu-latest
    steps:
      - name: Get Current Time
        uses: 1466587594/get-current-time@v2
        id: current-time
        with:
          format: YYYY-MM-DDTHH:mm:ss
          utcOffset: "+09:00" # ๊ธฐ์ค€์ด UTC์ด๊ธฐ ๋•Œ๋ฌธ์— ํ•œ๊ตญ์‹œ๊ฐ„์ธ KST๋ฅผ ๋งž์ถ”๊ธฐ ์œ„ํ•ด +9์‹œ๊ฐ„ ์ถ”๊ฐ€

      - name: Print Current Time
        run: echo "Current Time=${{steps.current-time.outputs.formattedTime}}" # current-time ์—์„œ ์ง€์ •ํ•œ ํฌ๋งท๋Œ€๋กœ ํ˜„์žฌ ์‹œ๊ฐ„ ์ถœ๋ ฅ
        shell: bash

  ## slack
  action-slack:
    needs: CI-CD
    runs-on: ubuntu-latest
    steps:
        - name: Slack Alarm
          uses: 8398a7/action-slack@v3
          with:
              status: ${{ job.status }}
              author_name: GitHub-Actions CI/CD
              fields: repo,message,commit,author,ref,job,took
          env:
              SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} # required
          if: always() # Pick up events even if the job fails or is canceled.

์Šคํฌ๋ฆฝํŠธ ํŒŒ์ผ์ด ํ•œ ๋ˆˆ์— ๋“ค์–ด์˜ค์ง€ ์•Š์œผ๋ฏ€๋กœ ๋ถ€๋ถ„์ ์œผ๋กœ ๋‚˜๋ˆ„์–ด์„œ ์„ค๋ช…๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค.

๋จผ์ € ์ „์ฒด ํ”„๋กœ์„ธ์Šค๋Š” GitHub ๋ ˆํฌ์ง€ํ† ๋ฆฌ์— main ๋ธŒ๋žœ์น˜(prod) ๋ฐ develop ๋ธŒ๋žœ์น˜(dev)๋กœ push๊ฐ€ ๋  ๋•Œ ๊ฐ ํ™˜๊ฒฝ๋ณ„๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ณผ์ •์„ ๊ฑฐ์ณ AWS EC2 ์ธ์Šคํ„ด์Šค์— ๋ฐฐํฌ๊ฐ€ ๋˜๋Š” ๊ณผ์ •์ž…๋‹ˆ๋‹ค.

  • Gradle Build - ๊ฐ ํŒŒ์ผ ์ƒ์„ฑ - Docker build & push - AWS EC2 Deploy(ssh) - Slack ์•Œ๋ฆผ

๋‹ค๋งŒ ๋…ธ์ถœ๋˜์–ด์„œ๋Š” ์•ˆ๋˜๋Š” properties๊ณผ ๊ฐ™์€ ํŒŒ์ผ์€ ๊นƒํ—ˆ๋ธŒ์— ์˜ฌ๋ ค๋‘๋ฉด ์•ˆ๋˜๊ธฐ ๋•Œ๋ฌธ์— GitHub-Actions ์Šคํฌ๋ฆฝํŠธ์—์„œ ์ง์ ‘ ์ƒ์„ฑํ•ด์ฃผ์–ด์•ผ ํ•˜๊ณ , ํ™˜๊ฒฝ๋ณ„ ๋ถ„๋ฆฌ๊ฐ€ ํ•„์š”ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ฝ”๋“œ์˜ ๊ธธ์ด๊ฐ€ 2๋ฐฐ๊ฐ€ ๋œ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๐Ÿ˜‚

 

๊ทธ๋Ÿผ ์œ„ ์Šคํฌ๋ฆฝํŠธ ํŒŒ์ผ์— ๋Œ€ํ•ด ํ•˜๋‚˜์”ฉ ์„ค๋ช…๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค.

(์ฐธ๊ณ ๋กœ ๊ณต์‹๋ฌธ์„œ์— ์ƒ์„ธํžˆ ์„ค๋ช…๋˜์–ด ์žˆ์œผ๋‹ˆ, ๋” ๊ถ๊ธˆํ•˜์‹  ๋ถ€๋ถ„์€ ์ฐพ์•„๋ณด์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.)

 

 

๐ŸŒˆ  on: push: branches:

  • ์•„๋ž˜ ์ฝ”๋“œ๋Š” main, develop ๋ธŒ๋žœ์น˜์— push๊ฐ€ ๋˜์—ˆ์„ ๋•Œ Workflow๋ฅผ Trigger๋ฅผ ์‹คํ–‰ํ•œ๋‹ค๋Š” ์˜๋ฏธ์ž…๋‹ˆ๋‹ค.
# github repository Actions ํŽ˜์ด์ง€์— ๋‚˜ํƒ€๋‚ผ ์ด๋ฆ„
name: Dotoriham CI/CD

# event trigger
on:
  push:
    branches:
      - main
      - develop

 

 

๐ŸŒˆ  Jdk Setting

๋‹ค์Œ์œผ๋กœ jobs ๋ถ€๋ถ„์ธ๋ฐ์š”, GitHub-Actions์˜ Workflow๋Š” ๋‹ค์–‘ํ•œ Job์œผ๋กœ ๊ตฌ์„ฑ๋˜๋ฉฐ Job๋Š” ๋‹ค์‹œ Steps๋กœ ๊ตฌ์„ฑ์ด ๋ฉ๋‹ˆ๋‹ค.

  • GitHub-Actions์—์„œ ์‚ฌ์šฉ๋  JDK๋ฅผ ์„ธํŒ…ํ•ฉ๋‹ˆ๋‹ค. (ํ”„๋กœ์ ํŠธ๋‚˜ AWS์˜ Java ๋ฒ„์ „๊ณผ๋Š” ๋‹ฌ๋ผ๋„ ๋ฌด๋ฐฉํ•ฉ๋‹ˆ๋‹ค.)
  • Java-Version์œผ๋กœ 11์„ ์‚ฌ์šฉํ•˜๊ณ , distribution์œผ๋กœ 'temurin'์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
    • ๊ณผ๊ฑฐ์—๋Š” adopt๋ฅผ ์‚ฌ์šฉ์„ ํ–ˆ์—ˆ๋Š”๋ฐ, ํ˜„์žฌ ๊ณต์‹๋ฌธ์„œ์— ๋”ฐ๋ฅด๋ฉด adopt ๋Œ€์‹  temurin์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ๊ฐ•๋ ฅํžˆ ๊ถŒ์žฅํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ adopt๋Š” ๋”์ด์ƒ ์—…๋ฐ์ดํŠธ ๋˜์ง€์•Š๋Š”๋‹ค๊ณ  ํ•˜๊ธฐ์— ํŠน๋ณ„ํ•œ ์ด์œ ๊ฐ€ ์—†๋‹ค๋ฉด temurin์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

https://github.com/actions/setup-java

jobs:
  CI-CD:
    runs-on: ubuntu-latest
    steps:

    ## jdk setting
    - uses: actions/checkout@v3
    - name: Set up JDK 11
      uses: actions/setup-java@v3
      with:
        java-version: '11'
        distribution: 'temurin' # https://github.com/actions/setup-java

 

 

๐ŸŒˆ  Gradle Caching

๋‹ค์Œ์œผ๋กœ Gradle์„ ์บ์‹ฑํ•˜๋Š” ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.

์ด ๋ถ€๋ถ„์€ ์ถ”๊ฐ€ํ•˜์ง€ ์•Š์•„๋„ ๋ฌด๋ฐฉํ•˜๋‚˜ ๊ฐœ์ธ์ ์œผ๋กœ ์ ์šฉํ–ˆ์„ ๋•Œ ๋นŒ๋“œ ์‹œ๊ฐ„์ด 20~30% ๊ฐ€๋Ÿ‰ ํ–ฅ์ƒ๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ถ”๊ฐ€ํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.

๋” ์ž์„ธํ•œ ์„ค๋ช…์€ ์•„๋ž˜ ๊ณต์‹๋ฌธ์„œ๋ฅผ ์ฐธ๊ณ ํ•ด์ฃผ์„ธ์š” :)

    ## gradle caching
    - name: Gradle Caching
      uses: actions/cache@v3
      with:
        path: |
          ~/.gradle/caches
          ~/.gradle/wrapper
        key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
        restore-keys: |
          ${{ runner.os }}-gradle-

 

 

๐ŸŒˆ  application-<dev, prod>.properties

๋‹ค์Œ์œผ๋กœ ํ™˜๊ฒฝ ๋ณ„ properties ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜๋Š” ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค.

์ด ํŒŒ์ผ์€ ๊ฐœ์ธ ์ •๋ณด(DB ์ •๋ณด, AWS ์ •๋ณด ๋“ฑ๋“ฑ)๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๊นƒํ—ˆ๋ธŒ์—๋Š” ์˜ฌ๋ ค๋‘์ง€ ์•Š๊ณ , GitHub-Actions์—์„œ ์ง์ ‘ ํŒŒ์ผ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

 

์ €๋Š” ./src/main/resources๊ฐ€ ์•„๋‹Œ ./src/main/resource ๋กœ ๊ฒฝ๋กœ๋ฅผ ์ž˜๋ชป์žก์•„์ค˜์„œ ๊ฝค๋‚˜ ์‚ฝ์งˆ์„ ํ–ˆ๋Š”๋ฐ์š”.. ์ €์™€ ๊ฐ™์€ ์‹ค์ˆ˜๋ฅผ ๋ฒ”ํ•˜์ง€ ๋งˆ์„ธ์š”. ๐Ÿ˜ญ๐Ÿ˜ญ

    ## create application-dev.properties
    - name: make application-dev.properties
      if: contains(github.ref, 'develop') # branch๊ฐ€ develop์ผ ๋•Œ
      run: |
      	# spring์˜ resources ๊ฒฝ๋กœ๋กœ ์ด๋™
        cd ./src/main/resources 
        
        # application-dev.properties ํŒŒ์ผ ์ƒ์„ฑ
        touch ./application-dev.properties 
        
        # GitHub-Actions์—์„œ ์„ค์ •ํ•œ ๊ฐ’์„ application-dev.properties ํŒŒ์ผ์— ์“ฐ๊ธฐ
        echo "${{ secrets.PROPERTIES_DEV }}" > ./application-dev.properties 
      shell: bash

    ## create application-prod.properties
    - name: make application-prod.properties
      if: contains(github.ref, 'main') # branch๊ฐ€ main ์ผ ๋•Œ, ๋‚˜๋จธ์ง€๋Š” ์œ„์™€ ๋™์ผ
      run: |
          cd ./src/main/resources
          touch ./application-prod.properties
          echo "${{ secrets.PROPERTIES_PROD }}" > ./application-prod.properties
      shell: bash

์œ„ ๊ณผ์ •์— ๋Œ€ํ•œ ๋‚ด์šฉ์€ ์ฃผ์„์œผ๋กœ ์„ค๋ช…ํ•ด๋†“์•˜์Šต๋‹ˆ๋‹ค.

ํŒŒ์ผ ๋‚ด์šฉ์„ ์“ฐ๊ธฐ ์œ„ํ•ด์„œ๋Š” GitHub-Actions์˜ secrets์— ๊ฐ’์„ ์„ค์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

 

ํ•ด๋‹น ๋‚ด์šฉ์€ ๊นƒํ—ˆ๋ธŒ ๋ ˆํฌ์ง€ํ† ๋ฆฌ์˜ Settings > Secrets > Actions > New repository secret ๋ฒ„ํŠผ์„ ํ†ตํ•ด ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

โ€ป secrets ๋‚ด์šฉ๋“ค์€ ์•”ํ˜ธํ™”๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ํ•œ ๋ฒˆ ๊ฐ’์„ ์„ค์ •ํ•˜๋ฉด ๋‹ค์‹œ ํ™•์ธํ•  ์ˆ˜ ์—†์ด ๋ณ€๊ฒฝ๋งŒ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค(ํŒจ์Šค์›Œ๋“œ์ฒ˜๋Ÿผ)

  • DOCKER_PASSWORD: ๋„์ปค ๊ณ„์ • ํŒจ์Šค์›Œ๋“œ
  • DOCKER_REPO: ๋„์ปค ๋ ˆํฌ์ง€ํ† ๋ฆฌ
  • DOCKER_USERNAME: ๋„์ปค ID
  • FIREBASE_KEY: ํŒŒ์ด์–ด๋ฒ ์ด์Šค key
  • HOST_DEV: dev ํ™˜๊ฒฝ์˜ EC2 ์ธ์Šคํ„ด์Šค ip
  • HOST_PROD: prod ํ™˜๊ฒฝ์˜ EC2 ์ธ์Šคํ„ด์Šค ip
  • PASSWORD: EC2 ์ธ์Šคํ„ด์Šค ํŒจ์Šค์›Œ๋“œ
  • PRIVATE_KEY
  • PROPERTIES_DEV: application-dev.properties ํŒŒ์ผ ๋‚ด์šฉ
  • PROPERTIES_PROD: application-prod.properties ํŒŒ์ผ ๋‚ด์šฉ
  • SLACK_WEBHOOK_URL: ์Šฌ๋ž™ ์—ฐ๋™์„ ์œ„ํ•œ ์›นํ›…URL
  • USERNAME: EC2 ์ธ์Šคํ„ด์Šค ๊ณ„์ • ID(ec2-user)

์ €๋Š” ์œ„์™€ ๊ฐ™์€ ์ •๋ณด๋“ค์ด ํ•„์š”ํ•ด์„œ ์„ค์ •์„ ํ•ด ์ฃผ์—ˆ๋Š”๋ฐ์š”, ๋ณธ์ธ์˜ ์ƒํ™ฉ์— ๋”ฐ๋ผ ํ•„์š”ํ•œ ๊ฐ’๋“ค๋งŒ ์„ค์ •ํ•ด์ฃผ์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค ๐Ÿคฏ

 

 

Name์— ๋ณ€์ˆ˜ ๋ช…์„, Value์— ๊ฐ’์„ ๋ณต์‚ฌํ•ด์„œ ์„ค์ •ํ•ด์ฃผ์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

 

 

๐ŸŒˆ  firebase-key.json 

๋‹ค์Œ์€ ํŒŒ์ด์–ด๋ฒ ์ด์Šค์˜ ํ‚ค ํŒŒ์ผ์„ ์„ค์ •ํ•˜๋Š” ๋ถ€๋ถ„์ธ๋ฐ์š”, ์ €๋Š” FCM์„ ์‚ฌ์šฉํ•˜๊ธฐ ์ด ํŒŒ์ผ๋„ ๋งŒ๋“ค์–ด ์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.

    ## create firebase-key.json
    - name: create firebase key
      run: |
        cd ./src/main/resources
        touch ./firebase-service-key.json
        echo "${{ secrets.FIREBASE_KEY }}" > ./firebase-service-key.json
      shell: bash

 

์ฃผ์˜ํ•˜์‹ค ๋ถ€๋ถ„์€, GitHub-Actions์—์„œ๋Š” double-quote(์Œ๋”ฐ์˜ดํ‘œ ")๋ฅผ ์ธ์‹ํ•˜๋„๋ก ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” escape ๋ฌธ์ž(๋ฐฑ์Šฌ๋ž˜์‹œ \)๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ถ€๋ถ„ ๋•Œ๋ฌธ์—๋„ ์•ฝ๊ฐ„ ์‚ฝ์งˆ์„ ํ–ˆ๋„ค์š” ^^;

## ๊ธฐ์กด firebase-key.json ๋‚ด์šฉ
{
  type: service_account,
  project_id: dotoriham-dfee3,
  private_key_id: 85796c2b2645b94eb354eedf718e81ae2fa514dd,
  
  ...
}


## GitHub-Actions์—์„œ ์ธ์‹ํ•˜๊ธฐ ์œ„ํ•œ escape character \
{
  \"type\": \"service_account\",
  \"project_id\": \"dotoriham-dfee3\",
  \"private_key_id\": \"85796c2b2645b94eb354eedf718e81ae2fa514dd\",
  
  ...
}

 

 

๐ŸŒˆ  Gradle Build - Docker Build & Push

๋‹ค์Œ์œผ๋กœ ๊ทธ๋ ˆ์ด๋“ค ๋นŒ๋“œ ๋ฐ ๋„์ปค ๋นŒ๋“œ, ํ‘ธ์‰ฌ ๊ณผ์ •์ž…๋‹ˆ๋‹ค.

์œ„์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ secrets. ๋ณ€์ˆ˜๋“ค์€ ๋ชจ๋“œ ๊นƒํ—ˆ๋ธŒ์— ๋ฏธ๋ฆฌ ์„ค์ •์„ ํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.

(๋„์ปค์˜ ๊ณผ์ •์— ๋Œ€ํ•ด์„œ๋Š” ๋”ฐ๋กœ ์„ค๋ช…์€ ์ƒ๋žตํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๋„์ปค์™€ ์Šคํ”„๋ง๋ถ€ํŠธ์˜ ๋ฐฐํฌ ๊ณผ์ •์— ๋Œ€ํ•ด ๊ถ๊ธˆํ•˜์‹  ๋ถ„์€ ํฌ์ŠคํŒ…์„ ์ฐธ๊ณ ํ•ด์ฃผ์„ธ์š”.)

    ## gradle build
    - name: Build with Gradle
      run: ./gradlew build -x test -x ktlintCheck -x ktlintTestSourceSetCheck -x ktlintMainSourceSetCheck -x ktlintKotlinScriptCheck

    ## docker build & push to production
    - name: Docker build & push to prod
      if: contains(github.ref, 'main')
      run: |
          docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
          docker build -f Dockerfile-prod -t ${{ secrets.DOCKER_REPO }}/dotoriham-prod .
          docker push ${{ secrets.DOCKER_REPO }}/dotoriham-prod

    ## docker build & push to develop
    - name: Docker build & push to dev
      if: contains(github.ref, 'develop')
      run: |
          docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
          docker build -f Dockerfile-dev -t ${{ secrets.DOCKER_REPO }}/dotoriham-dev .
          docker push ${{ secrets.DOCKER_REPO }}/dotoriham-dev

 

prod, dev๋ณ„๋กœ spring profiles๋ฅผ ์ ์šฉํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— Dockerfile์„ ๋ถ„๋ฆฌํ•ด์ฃผ์—ˆ๋Š”๋ฐ์š”, ๋„์ปค๋ฅผ ๋นŒ๋“œ ํ•  ๋•Œ ํŠน์ • ํŒŒ์ผ์„ ๊ฐ•์ œ์ ์œผ๋กœ ์ง€์ •ํ•˜๊ณ ์ž ํ•˜๋ฉด -f ์˜ต์…˜์„ ์ง€์ •ํ•ด์ฃผ์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

(๋””ํดํŠธ๋Š” ์ตœ์ƒ์œ„ ๋””๋ ‰ํ† ๋ฆฌ์˜ Dockerfile์„ ํ† ๋Œ€๋กœ ๋นŒ๋“œ๋ฅผ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค)

 

 

Dockerfile- ํŒŒ์ผ์˜ ๊ฒฝ๋กœ ๋ฐ ๋‚ด์šฉ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.(ํ”„๋กœ์ ํŠธ ์ตœ์ƒ์œ„ ๋””๋ ‰ํ† ๋ฆฌ์ž…๋‹ˆ๋‹ค.)

## Dockerfile-dev
FROM openjdk:14-jdk-slim
EXPOSE 8080
ARG JAR_FILE=/build/libs/Web-Team-2-Backend-0.0.1-SNAPSHOT.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","-Dspring.profiles.active=dev","/app.jar"]


## Dockerfile-prod
FROM openjdk:14-jdk-slim
EXPOSE 8080
ARG JAR_FILE=/build/libs/Web-Team-2-Backend-0.0.1-SNAPSHOT.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","-Dspring.profiles.active=prod","/app.jar"]

 

 

๐ŸŒˆ  Deploy to AWS EC2(using ssh)

๋งˆ์ง€๋ง‰์œผ๋กœ SSH ์—ฐ๊ฒฐ์„ ํ†ตํ•ด EC2 ์ธ์Šคํ„ด์Šค์— ๋„์ปค์˜ ์ด๋ฏธ์ง€๋ฅผ ๊ฐ€์ ธ์™€ ๋ฐฐํฌํ•˜๋Š” ๊ณผ์ •์ž…๋‹ˆ๋‹ค.

ssh-action์€ ๋‹ค์Œ ์‚ฌ์ดํŠธ๋ฅผ ์ฐธ๊ณ ํ•˜์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

    ## deploy to production
    - name: Deploy to prod
      uses: appleboy/ssh-action@master
      id: deploy-prod
      if: contains(github.ref, 'main')
      with:
          host: ${{ secrets.HOST_PROD }}
          username: ec2-user
          key: ${{ secrets.PRIVATE_KEY }}
          envs: GITHUB_SHA
          script: |
              sudo docker rm -f $(docker ps -qa)
              sudo docker pull ${{ secrets.DOCKER_REPO }}/dotoriham-prod
              docker-compose up -d
              docker image prune -f

    ## deploy to develop
    - name: Deploy to dev
      uses: appleboy/ssh-action@master
      id: deploy-dev
      if: contains(github.ref, 'develop')
      with:
        host: ${{ secrets.HOST_DEV }}
        username: ${{ secrets.USERNAME }}
        password: ${{ secrets.PASSWORD }}
        port: 22
        #key: ${{ secrets.PRIVATE_KEY }}
        script: |
            sudo docker rm -f $(docker ps -qa)
            sudo docker pull ${{ secrets.DOCKER_REPO }}/dotoriham-dev
            docker-compose up -d
            docker image prune -f

์œ„์—์„œ HOST_PROD ๋ฐ HOST_DEV์˜ ๊ฐ’์€ EC2 ์ธ์Šคํ„ด์Šค์˜ IP๋ฅผ ์„ค์ •ํ•˜์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

์ €๊ฐ™์€ ๊ฒฝ์šฐ๋Š” ์•„๋ž˜ ํŒŒ๋ž€์ƒ‰ ๋„ค๋ชจ์นœ ๊ฐ’์„ ๋ณต์‚ฌํ•ด์„œ ๋ถ™์—ฌ๋„ฃ์–ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.

  • ~~~.ap-northeast-2.compute.amazonaws.com

 

secrets.USERNAME, secrets.PASSWORD ์˜ ๊ฒฝ์šฐ EC2์—์„œ ๋”ฐ๋กœ ์„ค์ •์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

// ec2 ๊ณ„์ • ๋น„๋ฐ€๋ฒˆํ˜ธ ์„ค์ •
sudo passwd [ec2 ์‚ฌ์šฉ์ž ๊ณ„์ •]

// ssh config ์ˆ˜์ • -> PasswordAuthentication๋ฅผ yes๋กœ ๋ณ€๊ฒฝ
sudo vi /etc/ssh/sshd_config
PasswordAuthentication yes

// ์žฌ๊ธฐ๋™
sudo service sshd restart

// ec2 ์ธ์Šคํ„ด์Šค ๋กœ๊ทธ์ธ
ssh ec2-user@[EC2 ์ธ์Šคํ„ด์Šค ip]

 

 

๐ŸŒˆ  time ์ถœ๋ ฅ ๋ฐ slack ์—ฐ๋™

๋งˆ์ง€๋ง‰์€ ํ˜„์žฌ ์‹œ๊ฐ„ ์ถœ๋ ฅ ๋ฐ ์Šฌ๋ž™ ์—ฐ๋™์„ ํ†ตํ•ด GitHub-Actions์˜ Workflow์˜ ๊ฒฐ๊ณผ์— ๋Œ€ํ•ด ์•Œ๋ฆผ์„ ๋ฐ›๊ธฐ ์œ„ํ•œ ์ž‘์—…์ž…๋‹ˆ๋‹ค.

ํ•„์ˆ˜์ ์ธ ๋ถ€๋ถ„์€ ์•„๋‹ˆ๋ฉฐ, ์ถ”ํ›„ ์Šฌ๋ž™์— ์—ฐ๋™ํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด์„œ๋Š” ๋”ฐ๋กœ ํฌ์ŠคํŒ…์œผ๋กœ ์ž‘์„ฑํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค..๐Ÿ˜‚

  ## time
  current-time:
    needs: CI-CD
    runs-on: ubuntu-latest
    steps:
      - name: Get Current Time
        uses: 1466587594/get-current-time@v2
        id: current-time
        with:
          format: YYYY-MM-DDTHH:mm:ss
          utcOffset: "+09:00" # ๊ธฐ์ค€์ด UTC์ด๊ธฐ ๋•Œ๋ฌธ์— ํ•œ๊ตญ์‹œ๊ฐ„์ธ KST๋ฅผ ๋งž์ถ”๊ธฐ ์œ„ํ•ด +9์‹œ๊ฐ„ ์ถ”๊ฐ€

      - name: Print Current Time
        run: echo "Current Time=${{steps.current-time.outputs.formattedTime}}" # current-time ์—์„œ ์ง€์ •ํ•œ ํฌ๋งท๋Œ€๋กœ ํ˜„์žฌ ์‹œ๊ฐ„ ์ถœ๋ ฅ
        shell: bash

  ## slack
  action-slack:
    needs: CI-CD
    runs-on: ubuntu-latest
    steps:
        - name: Slack Alarm
          uses: 8398a7/action-slack@v3
          with:
              status: ${{ job.status }}
              author_name: GitHub-Actions CI/CD
              fields: repo,message,commit,author,ref,job,took
          env:
              SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} # required
          if: always() # Pick up events even if the job fails or is canceled.

 

์œ„ ๊ณผ์ •์ด ๋๋‚œ ํ›„ GitHub-Actions์— ํ…Œ์ŠคํŠธ๋ฅผ ํ•ด๋ณด๋ฉด EC2 ์ธ์Šคํ„ด์Šค์— ์กด์žฌํ•˜๋Š” docker-compose.yaml ํŒŒ์ผ์„ ์ฝ์–ด์„œ ๋ฐฐํฌ๊ฐ€ ๋˜๋Š”๋ฐ์š”, ์ฐธ๊ณ ๋กœ ์ €์˜ docker-compose.yaml ํŒŒ์ผ์˜ ๋‚ด์šฉ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

version: "3"
services:
  mongodb:
    image: mongo
    restart: always
    container_name: yapp-mongo
    ports:
      - 27017:27017
    environment:
      - MONGO_INITDB_ROOT_USERNAME=yapp
      - MONGO_INITDB_ROOT_PASSWORD=yapp
      - MONGO_INITDB_DATABASE=yapp
    volumes:
      - mongodb:/var/lib/mongo

  postgresdb:
    image: postgres
    restart: always
    container_name: yapp-postgres
    ports:
      - 5432:5432
    environment:
      - POSTGRES_USER=yapp
      - POSTGRES_PASSWORD=yapp
    volumes:
      - postgresdb:/var/lib/postgres

  redisdb:
    image: redis
    restart: always
    ports:
      - 6379:6379
    container_name: yapp-redis
    volumes:
      - redisdb:/var/lib/redis

  dotoriham:
    image: juhyun419/dotoriham-dev
    restart: always
    container_name: dotoriham-dev
    ports:
      - 8080:8080
    depends_on:
      - mongodb
      - postgresdb
      - redisdb

volumes:
  mongodb:
  postgresdb:
  redisdb:

 

 

์ €๋Š” MongoDB, PostgreSQL, Redis๋ฅผ ์„œ๋ฒ„์—์„œ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— dev์˜ ๊ฒฝ์šฐ docker-compose๋ฅผ ํ™œ์šฉํ•ด ๋ชจ๋‘ ๋„์ปค๋กœ ๋„์›Œ์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.

 

์œ„์™€ ๊ฐ™์ด ์ •์ƒ์ ์œผ๋กœ ๋ฐฐํฌ๊ฐ€ ๋˜๋Š”๋ฐ์š”, ํ˜น์‹œ ๋ฐฐํฌ์— ์‹คํŒจํ•œ๋‹ค๋ฉด ๋„์ปค ๋กœ๊ทธ๋ฅผ ํ†ตํ•ด ์—๋Ÿฌ ๋กœ๊ทธ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

docker logs -f <CONTAINER ID>

 

 

๐ŸŒˆ  Spring Boot Logback์„ ํ†ตํ•ด ๋กœ๊ทธ ํŒŒ์ผ ๋‚จ๊ธฐ๊ธฐ

์ถ”๊ฐ€์ ์œผ๋กœ Spring Boot, docker-compose์—์„œ logback์„ ํ™œ์šฉํ•˜์—ฌ AWS EC2 ์„œ๋ฒ„์— ๋กœ๊ทธ ํŒŒ์ผ์„ ๋‚จ๊ธฐ๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด์„œ๋„ ๊ฐ„๋žตํžˆ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. (๊ฐœ์„ ํ•  ๋ถ€๋ถ„์ด ๊ต‰์žฅํžˆ ๋งŽ๊ธด ํ•˜์ง€๋งŒ.. ์ •์ƒ์ ์œผ๋กœ ๋กœ๊ทธ ํŒŒ์ผ์ด ๊ธฐ๋ก๋˜๋Š” ๊ฒƒ์—๋งŒ ํฌ์ปค์‹ฑ์„ ๋‘์—ˆ์Šต๋‹ˆ๋‹ค. ๐Ÿ˜‚)

 

๋จผ์ €, Dockerfile์— VOLUME ํ‚ค์›Œ๋“œ๋กœ ์ปจํ…Œ์ด๋„ˆ์—์„œ ๋กœ๊ทธ๋กœ ๋‚จ๊ธธ ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

์ €๋Š” ํ™˜๊ฒฝ๋ณ„ prod, dev๋กœ ๋ถ„๋ฆฌํ•˜์˜€์œผ๋ฏ€๋กœ ๋‘ ํŒŒ์ผ ๋ชจ๋‘ ์„ค์ •์„ ํ–ˆ์Šต๋‹ˆ๋‹ค.

// Dockerfile-prod
FROM openjdk:14-jdk-slim
EXPOSE 8080
ARG JAR_FILE=/build/libs/Web-Team-2-Backend-0.0.1-SNAPSHOT.jar
VOLUME ["/var/log"]
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","-Dspring.profiles.active=prod","/app.jar"]


// Dockerfile-dev
FROM openjdk:11-jdk-slim
EXPOSE 8080
ARG JAR_FILE=/build/libs/Web-Team-2-Backend-0.0.1-SNAPSHOT.jar
VOLUME ["/var/log"]
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","-Dspring.profiles.active=dev","/app.jar"]
  • VOLUME์€ ๋„์ปค ์ปจํ…Œ์ด๋„ˆ์—์„œ ์ƒ์„ฑํ•˜๊ณ  ์‚ฌ์šฉ๋˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•˜๋Š” ๋ฉ”์ปค๋‹ˆ์ฆ˜์ž…๋‹ˆ๋‹ค. ๋„์ปค ์ปจํ…Œ์ด๋„ˆ์— ์“ฐ์—ฌ์ง„ ๋ฐ์ดํ„ฐ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ์‚ญ์ œ๋  ๋•Œ ํ•จ๊ป˜ ์—†์–ด์ง€๋Š”๋ฐ์š”, ์ด๋Ÿฌํ•œ ๋„์ปค ์ปจํ…Œ์ด๋„ˆ์˜ ์ƒ๋ช…์ฃผ๊ธฐ์™€ ๊ด€๊ณ„์—†์ด ๋ฐ์ดํ„ฐ๋ฅผ ์˜์†์ ์œผ๋กœ ์ €์žฅํ•˜๊ณ ์ž ํ•  ๋•Œ ๋ณผ๋ฅจ(volume)์™€ ๋ฐ”์ธ๋“œ ๋งˆ์šดํŠธ(bind mount)๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์„ค์ •์€ ์œ„์™€ ๊ฐ™์ด VOLUME ["/var/log"]๋‚˜ VOLUME /var/log์™€ ๊ฐ™์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

https://docs.docker.com/storage/volumes/

https://docs.docker.com/storage/bind-mounts/

 

๋‹ค์Œ์œผ๋กœ, logback ์„ค์ • ํŒŒ์ผ์—์„œ ๋กœ๊ทธ๋ฅผ ๋‚จ๊ธธ ํŒŒ์ผ์˜ ๊ฒฝ๋กœ๋ฅผ ๋„์ปค ์ปจํ…Œ์ด๋„ˆ ๊ธฐ์ค€์œผ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

(logback์— ๋Œ€ํ•œ ์„ค๋ช…์€ ๋”ฐ๋กœ ์ง„ํ–‰ํ•˜์ง€ ์•Š๊ฒ ์Šต๋‹ˆ๋‹ค.)

<?xml version="1.0" encoding="UTF-8"?>

<configuration>
    <!-- ๋ณ€์ˆ˜ ์„ค์ • -->
    <property name="LOG_DIR_SERVER" value="/var/log"/>
    <property name="LOG_FILE" value="logfile.log"/>
    <property name="LOG_FILE_PROD" value="${LOG_DIR_SERVER}/${LOG_FILE}"/>
    <property name="LOG_FILE_DEV" value="${LOG_DIR_SERVER}/${LOG_FILE}"/>
    <property name="LOG_FILE_LOCAL" value="./log/${LOG_FILE}"/>

    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <layout class="ch.qos.logback.classic.PatternLayout">
            <Pattern>[%d{yyyy-MM-dd HH:mm:ss}][%thread] %highlight(%-5level) %logger{36} - %msg%n</Pattern>
        </layout>
    </appender>

    <appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">

        <springProfile name="prod">
            <file>${LOG_FILE_PROD}</file>
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <fileNamePattern>${LOG_DIR_SERVER}/${LOG_FILE}.%d{yyyy-MM-dd}.log</fileNamePattern>
                <maxHistory>180</maxHistory>
                <totalSizeCap>3GB</totalSizeCap>
            </rollingPolicy>
        </springProfile>

        <springProfile name="dev">
            <file>${LOG_FILE_DEV}</file>
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <fileNamePattern>${LOG_DIR_SERVER}/${LOG_FILE}.%d{yyyy-MM-dd}.log</fileNamePattern>
                <maxHistory>180</maxHistory>
                <totalSizeCap>5GB</totalSizeCap>
            </rollingPolicy>
        </springProfile>

        <springProfile name="local">
            <file>${LOG_FILE_LOCAL}</file>
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <fileNamePattern>./log/%d{yyyy-MM}/${LOG_FILE}.%d{yyyy-MM-dd}.log</fileNamePattern>
                <maxHistory>365</maxHistory>
                <totalSizeCap>5GB</totalSizeCap>
            </rollingPolicy>
        </springProfile>

        <encoder>
            <pattern>[%d{yyyy-MM-dd HH:mm:ss}:%-4relative][%thread] %-5level %logger{35} - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- Logging level - TRACE > DEBUG > INFO > WARN > ERROR > FATAL -->
    <!-- ๋ชจ๋“  ๋กœ๊ทธ ๋ ˆ๋ฒจ์„ INFO๋กœ ์„ค์ • : INFO, WARN, ERROR, FATAL ๋ ˆ๋ฒจ ์ถœ๋ ฅ -->
    <root level="INFO">
        <appender-ref ref="console"/>
        <appender-ref ref="file"/>
    </root>

</configuration>
  • ์œ„ ์„ค์ • ํŒŒ์ผ์€ ๊ฐœ์„ ์ด ๋งŽ์ด ํ•„์š”ํ•œ logback ํŒŒ์ผ์ด๋‹ˆ ์ฐธ๊ณ ๋งŒ ํ•ด์ฃผ์„ธ์š”.. 
  • ์œ„์—์„œ ๋กœ๊ทธ ํŒŒ์ผ์˜ ๊ฒฝ๋กœ๋Š” ์•„๋ž˜ ์ฝ”๋“œ ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค.
    • <property name="LOG_DIR_SERVER" value="/var/log"/> 

 

๋งˆ์ง€๋ง‰์œผ๋กœ, docker-compose.yaml ํŒŒ์ผ์—์„œ volumes ์„ค์ •์„ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

  // ์ƒ๋žต
  
  ...

  dotoriham:
    image: juhyun419/dotoriham-dev
    restart: always
    container_name: dotoriham-dev
    ports:
      - 8080:8080
    depends_on:
      - mongodb
      - postgresdb
      - redisdb
    environment:
      - TZ=Asia/Seoul
    volumes:
      - /home/ec2-user/log:/var/log // ์ถ”๊ฐ€๋œ ๋ถ€๋ถ„, ํ˜ธ์ŠคํŠธ ๊ฒฝ๋กœ:์ปจํ…Œ์ด๋„ˆ ๊ฒฝ๋กœ

volumes:
  mongodb:
  postgresdb:
  redisdb:
  • volumes: ํ•˜์œ„์˜ /home/ec2-user/log:/var/log ์ž…๋‹ˆ๋‹ค.

์œ„์™€ ๊ฐ™์ด ์„ค์ •ํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด /home/ec2-user/log ๊ฒฝ๋กœ์— ๋กœ๊ทธ ํŒŒ์ผ์ด ๊ธฐ๋ก์ด ๋ฉ๋‹ˆ๋‹ค.

๋‹ค๋งŒ, ๋ง์”€๋“œ๋ฆฐ ๋Œ€๋กœ ์†๋ด์•ผ ํ•  ๋ถ€๋ถ„์ด ๋งŽ๊ธฐ ๋•Œ๋ฌธ์— ๋ณธ์ธ์˜ ํ™˜๊ฒฝ์— ๋งž๊ฒŒ ์ ์ ˆํžˆ ์ˆ˜์ •ํ•˜์—ฌ ์‚ฌ์šฉํ•˜์‹œ๋Š” ๊ฒƒ์„ ์ถ”์ฒœ๋“œ๋ฆฝ๋‹ˆ๋‹ค.

 

 

 

๐Ÿ“Ž ์ •๋ฆฌ

์ด์ƒ์œผ๋กœ GitHub-Actions๋ฅผ ํ™œ์šฉํ•˜์—ฌ CI/CD๋ฅผ ๊ตฌ์ถ•ํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์•˜์Šต๋‹ˆ๋‹ค.

๊ฐœ์ธ์ ์œผ๋กœ๋Š” ์˜คํƒ€, ๊ณต๋ฐฑ, SSH ๋“ฑ ๊ต‰์žฅํžˆ ๋งŽ์€ ์‚ฝ์งˆ์„ ํ–ˆ๋Š”๋ฐ์š”..๐Ÿ˜ญ ๋งŽ์€ ์‚ฝ์งˆ์ด ์žˆ์—ˆ์ง€๋งŒ, ๋งŽ์ด ๋ฐฐ์šธ ์ˆ˜ ์žˆ๋Š” ์ข‹์€ ๊ฒฝํ—˜์ด์—ˆ์Šต๋‹ˆ๋‹ค.

 

 

 

๐Ÿ“Ž ์ฐธ๊ณ  ๋ฌธ์„œ

 

๋ฐ˜์‘ํ˜•

๋Œ“๊ธ€