๋ฐ˜์‘ํ˜•

1. Github Secrets ์„ค์ •

 

 

Github Actions๋ฅผ ์ ์šฉํ•  Repo์˜ Setting์—์„œ Secrets and variables์— ๋“ค์–ด๊ฐ€์ฃผ๋ฉด

New repository secret๋กœ ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Œ

 

  • DOCKERHUB_USERNAME

    => Docker Hub ์‚ฌ์šฉ์ž ์ด๋ฆ„

  • DOCKERHUB_PASSWORD

    => Docker Hub ๋น„๋ฐ€๋ฒˆํ˜ธ

  • EC2_HOST

    => EC2 ํผ๋ธ”๋ฆญ IP

  • EC2_USER

    => EC2์— SSH๋กœ ์ ‘์†ํ•  ๋•Œ ์‚ฌ์šฉํ•  ์‚ฌ์šฉ์ž ์ด๋ฆ„์ž„
    (์šฐ๋ถ„ํˆฌ EC2๋ฅผ ์‚ฌ์šฉ์ค‘์ด๊ธฐ์— ubuntu๋กœ ์ง€์ •ํ•˜์˜€์Œ)

  • EC2_SSH_KEY


https://hanjungyo.tistory.com/127

 

Jenkins์„ ์ด์šฉํ•œ Spring Boot ๋ฐฐํฌ ์ž๋™ํ™”

๋กœ์ปฌ์—์„œ Spring Boot ๋นŒ๋“œํ•˜๊ณ  docker image ๋งŒ๋“ค์–ด์„œ docker hub์— ์˜ฌ๋ฆฌ๊ณ ..EC2 ๋“ค์–ด๊ฐ€์„œ docker hub์— ์žˆ๋Š” ์ด๋ฏธ์ง€ pull ๋ฐ›์•„์„œ run ์‹œํ‚ค๊ธฐ... ๊ฐœ๋ฐœ์„ ํ•˜๋ฉด ํ• ์ˆ˜๋ก CI/CD์˜ ์ค‘์š”์„ฑ์ด ๊นŠ๊ฒŒ ๋Š๊ปด์ง€๋Š” ๊ฒƒ ๊ฐ™๋‹ค... 

hanjungyo.tistory.com

 

EC2_SSH_KEY์— ๋Œ€ํ•ด์„œ๋Š” ์œ„ ํฌ์ŠคํŒ…์˜ SSHํ‚ค ์„ค์ • ๋ถ€๋ถ„์„ ์ฐธ๊ณ ํ•˜๋ฉด ์•Œ ์ˆ˜ ์žˆ์Œ

์ฃผ์˜ํ•ด์•ผํ•  ๋ถ€๋ถ„์€

1. ์ ‘๊ทผํ•˜๋Š”์ชฝ์ธ GitHub์— ๊ณต๊ฐœํ‚ค๊ฐ€ ์•„๋‹Œ ๋น„๋ฐ€ํ‚ค๋ฅผ ๋„ฃ์–ด์ค˜์•ผํ•œ๋‹ค๋Š”์ 

2. EC2 ์ธ์Šคํ„ด์Šค์˜ authorized_keys์—๋Š” GitHub Actions์—์„œ ์‚ฌ์šฉ๋˜๋Š” ๋น„๋ฐ€ํ‚ค์— ํ•ด๋‹นํ•˜๋Š” ๊ณต๊ฐœํ‚ค (id_rsa.pub) ๊ฐ€ ๋“ฑ๋ก๋˜์–ด์•ผ SSH ์—ฐ๊ฒฐ์ด ๊ฐ€๋Šฅํ•˜๋‹ค๋Š”์ 

 

  • APPLICATION

 

application.yml ํŒŒ์ผ๋„ ์„ค์ •์„ ํ•ด์•ผํ•˜๋Š”๋ฐ (๋ณด์•ˆ์ƒ์˜ ์ด์Šˆ๋กœ gitignore ์ฒ˜๋ฆฌ ๋˜์–ด ์žˆ์Œ)

 

์ฝ”๋“œ๋ฅผ ๊ทธ๋ƒฅ ๋„ฃ์–ด๋„ ๋˜์ง€๋งŒ ๊ณต๋ฐฑ์ด๋‚˜ ํŠน์ˆ˜๋ฌธ์ž ๋“ฑ์—์„œ ์˜ค๋ฅ˜๊ฐ€ ๋‚  ์ˆ˜ ์žˆ๋‹ค๊ณ ํ•ด์„œ Base64๋กœ ์ธ์ฝ”๋”ฉํ•œ ๊ฐ’์„ APPLICATION์œผ๋กœ ์ €์žฅํ•˜์˜€์Œ

 

https://www.convertstring.com/ko/EncodeDecode/Base64Encode

 

Base64๋กœ ์ธ์ฝ”๋”ฉ - ์˜จ๋ผ์ธ Base64๋กœ ์ธ์ฝ”๋”

 

www.convertstring.com

 

์œ„ ์‚ฌ์ดํŠธ์—์„œ application.yaml ํŒŒ์ผ์„ ๋„ฃ๊ณ  Base64๋กœ ์ธ์ฝ”๋”ฉํ•  ์ˆ˜ ์žˆ์Œ

 

 

2.  CI ํ…Œ์ŠคํŠธ (Java with Gradle)

 

 

CI / CD๋ฅผ ์ ์šฉํ•  Github Repo์˜ ์ƒ๋‹จ๋ฐ”์—์„œ Actions๋ฅผ ํด๋ฆญํ•˜๊ณ  Java with Gradle์„ Configureํ•ด์•ผํ•จ

 

name: Java CI with Gradle

on:
  push:
    branches: [ "backend/main", "backend/feature/cd-setup" ]
  pull_request:
    branches: [ "backend/main", "backend/feature/cd-setup" ]
  workflow_dispatch:

jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      contents: read

    steps:
      - uses: actions/checkout@v4

      - name: Set up JDK 17
        uses: actions/setup-java@v4
        with:
          java-version: '17'
          distribution: 'temurin'

      - name: Setup Gradle
        uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0

      - name: Grant execute permission for gradlew
        run: chmod +x backend/gradlew

      - name: Create application.yml from GitHub Secrets
        run: |
          mkdir -p backend/src/main/resources
          echo "${{ secrets.APPLICATION }}" | base64 --decode > backend/src/main/resources/application.yml

      - name: Build with Gradle Wrapper
        working-directory: backend
        run: ./gradlew build

 

ํ•„์š”์—†๋Š” ๋ถ€๋ถ„๋„ ์žˆ๊ธฐ์— ์œ„์™€ ๊ฐ™์ด ์ˆ˜์ •ํ•ด์คฌ์Œ

 

์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณด๋ฉด

 

  • Java CI with Gradle ์ด๋ผ๋Š” ์ด๋ฆ„์˜ GitHub Actions ์›Œํฌํ”Œ๋กœ์šฐ๋ฅผ ๋งŒ๋“ฌ

  • backend/main ๊ณผ  backend/feature/cd-setup ๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋ธŒ๋žœ์น˜์— push ๋˜๋Š” pull request ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์ด ์›Œํฌํ”Œ๋กœ์šฐ๊ฐ€ ์‹คํ–‰๋˜๋„๋ก ํŠธ๋ฆฌ๊ฑฐ ์กฐ๊ฑด์„ ๊ฑธ์—ˆ์Œ

    => workflow_dispatch๋ฅผ ํ†ตํ•ด ์ด workflow ํŒŒ์ผ์„ ๊ฐ€์ง„ ๋ธŒ๋žœ์น˜์—์„œ ํŠธ๋ฆฌ๊ฑฐ ์™ธ์— ์ˆ˜๋™์œผ๋กœ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Œ

 

workflow_dispatch ์„ค์ • ์‹œ Actions์— ๋“ค์–ด๊ฐ€์„œ ์ขŒ์ธก์˜ ์‹คํ–‰ํ•  workflow ์ด๋ฆ„์„ ์„ ํƒํ•˜๊ฒŒ๋˜๋ฉด ์˜ค๋ฅธ์ชฝ ๋นจ๊ฐ„ ๋ฐ•์Šค์— ์žˆ๋Š” Run workflow๋ฅผ ๋ณผ ์ˆ˜ ์žˆ์Œ

์ด ๋•Œ ์‹คํ–‰ํ•  branch๋ฅผ ์„ ํƒํ•˜๊ฒŒ ๋˜๋ฉด ์ˆ˜๋™์œผ๋กœ workflow ์ˆ˜ํ–‰์ด ๊ฐ€๋Šฅํ•œ๋ฐ ์ด๋ฅผ ์œ„ํ•ด์„  ํ•ด๋‹น branch์˜ .github/workflows์— ํ•ด๋‹น workflow ๊ด€๋ จ yml ํŒŒ์ผ์ด ์กด์žฌํ•ด์•ผํ•จ

 

  • build๋ผ๋Š” job์„ ์ •์˜ํ•˜๊ณ  ์‹คํ–‰ ํ™˜๊ฒฝ์„ ubuntu ์ตœ์‹  ํ™˜๊ฒฝ์œผ๋กœ ์ง€์ •ํ•˜์˜€์Œ
    (์ €์žฅ์†Œ์˜ ๋‚ด์šฉ์„ ์ฝ์„ ์ˆ˜ ์žˆ๋„๋ก ์ตœ์†Œํ•œ์˜ ๊ถŒํ•œ์„ ์„ค์ •)

    • ํ˜„์žฌ ์ €์žฅ์†Œ์˜ ์ฝ”๋“œ๋ฅผ checkoutํ•˜์—ฌ runner ํ™˜๊ฒฝ์— ๋‹ค์šด๋กœ๋“œ

    • JDK 17์„ ์„ค์น˜ (Adoptium์—์„œ ์ œ๊ณตํ•˜๋Š” OpenJDK ๋ฐฐํฌํŒ์ธ temurin)

    • ๊ณต์‹ Gradle GitHub Action์„ ์‚ฌ์šฉํ•˜์—ฌ Gradle์„ ์„ค์ •

    • gradlew(Gradle Wrapper) ํŒŒ์ผ์ด ์‹คํ–‰๋  ์ˆ˜ ์žˆ๋„๋ก ๊ถŒํ•œ ๋ถ€์—ฌ

      => ํ˜„์žฌ github repo์˜ ๋””๋ ‰ํ† ๋ฆฌ ๊ตฌ์กฐ๊ฐ€ fornt ์™€ backend ํด๋”๊ฐ€ ํ•จ๊ป˜ ์กด์žฌํ•˜๊ธฐ์— gradlew์˜ ์œ„์น˜๋ฅผ backend/gradelw๋กœ ํ•ด์ค˜์•ผ backend ํด๋” ์•ˆ์— ์žˆ๋Š” gradlew๋ฅผ ์ œ๋Œ€๋กœ ์ฐพ์„ ์ˆ˜ ์žˆ์Œ

    • github secrets์— ์ €์žฅํ•œ base64 ์ธ์ฝ”๋”ฉ๋œ application.yml ํŒŒ์ผ์„ (๋ณ€์ˆ˜๋ช… APPLICATION) ๋””์ฝ”๋”ฉ ํ•˜์—ฌ ์ง€์ •ํ•ด๋‘” ์œ„์น˜์— ์ €์žฅ
    • Gradle์„ ์‚ฌ์šฉํ•˜์—ฌ ํ”„๋กœ์ ํŠธ ๋นŒ๋“œ

      => working directory๋ฅผ backend๋กœ ์„ค์ •ํ•˜์˜€์Œ
      (์ด์ „์— ๋งํ•œ๋Œ€๋กœ ํ˜„์žฌ ๋ฃจํŠธ์—๋Š” front ์™€ backend ๋‘๊ฐœ์˜ ๋””๋ ‰ํ† ๋ฆฌ๊ฐ€ ๋ชจ๋‘ ์กด์žฌํ•˜๊ธฐ์—)

 

 

 

ํ”„๋กœ์ ํŠธ ํด๋” ๊ตฌ์กฐ๊ฐ€ ์œ„์™€ ๊ฐ™์œผ๋ฏ€๋กœ ๋นŒ๋“œํ•  ๋•Œ working-directory๋ฅผ backend๋กœ ์ง€์ •ํ•ด์ฃผ์–ด์•ผํ•จ

 

์œ„์™€ ๊ฐ™์€ CI ์›Œํฌํ”Œ๋กœ์šฐ๋ฅผ ํ†ตํ•ด PR์ด๋‚˜ ์ฝ”๋“œ ๋ณ€๊ฒฝ ์‹œ ์ž๋™์œผ๋กœ ๋นŒ๋“œ ์˜ค๋ฅ˜๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Œ

 

 

์œ„์™€ ๊ฐ™์ด ์„ฑ๊ณตํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Œ

 

์ฃผ์˜์‚ฌํ•ญ

.github/worflows/gradle.yml ์— ์œ„์—์„œ ์ž‘์„ฑํ•œ yml ํŒŒ์ผ์ด ๋“ค์–ด๊ฐ€๊ฒŒ๋ ํ…๋ฐ

 

์ด๋ฏธ ์กด์žฌํ•˜๋Š” ํŠน์ • branch๋ฅผ ํŠธ๋ฆฌ๊ฑฐํ•ด๋†จ๋”๋ผ๋„ ํ•ด๋‹น ๋ธŒ๋ Œ์น˜์— gradle.yml ํŒŒ์ผ ์ฆ‰, Github Action ํŒŒ์ผ์ด ์—†๋‹ค๋ฉด ์‹คํ–‰๋˜์ง€ ์•Š์Œ

 

๋”ฐ๋ผ์„œ, master ๋ธŒ๋žœ์น˜์— yml ํŒŒ์ผ์„ ๋งŒ๋“ค๊ณ  ํŠธ๋ฆฌ๊ฑฐ ์„ค์ •์„ ํ•ด๋†“์€ ๋ธŒ๋žœ์น˜์— merge ํ•˜๊ฑฐ๋‚˜ ์ฒ˜์Œ๋ถ€ํ„ฐ Action์„ ์‚ฌ์šฉํ•  ๋ธŒ๋žœ์น˜์— gradle.yml์„ ์ƒ์„ฑํ•˜๋ฉด ๋จ

 

 

3. CD ์ž‘์„ฑ

 

name: Java CI/CD with Gradle & Docker

on:
  push:
    branches: [ "backend/main", "backend/feature/cd-setup" ]
  pull_request:
    branches: [ "backend/main", "backend/feature/cd-setup" ]
  workflow_dispatch:

jobs:
  build-docker-image:
    runs-on: ubuntu-latest
    permissions:
      contents: read

    steps:
      # GitHub ์ €์žฅ์†Œ์—์„œ ์ฝ”๋“œ๋ฅผ ๊ฐ€์ ธ์˜ด
      - uses: actions/checkout@v4
      
      # JDK 17 ์„ค์ •
      - name: Set up JDK 17
        uses: actions/setup-java@v4
        with:
          java-version: '17'
          distribution: 'temurin'

      # Gradle ์„ค์ •
      - name: Setup Gradle
        uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0

      # Gradle wrapper ์‹คํ–‰ ๊ถŒํ•œ ๋ถ€์—ฌ
      - name: Grant execute permission for gradlew
        run: chmod +x backend/gradlew

      # GitHub Secrets์—์„œ application.yml ํŒŒ์ผ ์ƒ์„ฑ
      - name: Create application.yml from GitHub Secrets
        run: |
          mkdir -p backend/src/main/resources
          echo "${{ secrets.APPLICATION }}" | base64 --decode > backend/src/main/resources/application.yml

      # Gradle ๋นŒ๋“œ ์‹คํ–‰
      - name: Build with Gradle Wrapper
        working-directory: backend
        run: ./gradlew build

      # Docker ์ด๋ฏธ์ง€ ๋นŒ๋“œ
      - name: Build Docker Image
        working-directory: backend
        run: |
          docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/2025capstone:latest .

      # Docker Hub ๋กœ๊ทธ์ธ
      - name: Login to Docker Hub
        uses: docker/login-action@v2
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_PASSWORD }}

      # Docker Hub์— ์ด๋ฏธ์ง€ ํ‘ธ์‹œ
      - name: Push Docker Image to Docker Hub
        run: docker push ${{ secrets.DOCKERHUB_USERNAME }}/2025capstone:latest

  run-docker-image-on-ec2:
    needs: build-docker-image
    runs-on: ubuntu-latest

    steps:
      # Known Hosts ์—…๋ฐ์ดํŠธ: EC2์— SSH ์ ‘์†ํ•˜๊ธฐ ์œ„ํ•ด known_hosts ํŒŒ์ผ์— EC2 ํ˜ธ์ŠคํŠธ ์ถ”๊ฐ€
      - name: Update Known Hosts
        run: |
          mkdir -p ~/.ssh
          ssh-keyscan -H ${{ secrets.EC2_HOST }} >> ~/.ssh/known_hosts

      # EC2์— Docker ์ด๋ฏธ์ง€ ๋ฐฐํฌ
      - name: Deploy to EC2
        uses: appleboy/ssh-action@v0.1.6
        with:
          host: ${{ secrets.EC2_HOST }}
          username: ${{ secrets.EC2_USER }}
          key: ${{ secrets.EC2_SSH_KEY }}
          script: |
            # ๊ธฐ์กด ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ์žˆ์œผ๋ฉด ์ค‘์ง€ํ•˜๊ณ  ์‚ญ์ œ
            sudo docker stop $(sudo docker ps -q --filter ancestor=${{ secrets.DOCKERHUB_USERNAME }}/2025capstone)
            sudo docker rm $(sudo docker ps -aq --filter ancestor=${{ secrets.DOCKERHUB_USERNAME }}/2025capstone)
            
            # ์ตœ์‹  Docker ์ด๋ฏธ์ง€ ๊ฐ€์ ธ์˜ค๊ธฐ
            sudo docker pull ${{ secrets.DOCKERHUB_USERNAME }}/2025capstone:latest
            
            # ์ƒˆ ์ปจํ…Œ์ด๋„ˆ ์‹คํ–‰
            sudo docker run --rm -it -d -p 8080:8080 --name 2025capstone ${{ secrets.DOCKERHUB_USERNAME }}/2025capstone:latest
            
            # ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” Docker ์ด๋ฏธ์ง€ ๋ฐ ์บ์‹œ ์ •๋ฆฌ
            sudo docker system prune -f

 

 

๊ธฐ์กด์˜ CI ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์—์„œ ๋ณ€๊ฒฝ๋œ ๋ถ€๋ถ„์„ ์‚ดํŽด๋ณด๋ฉด

 

  • ./gradlew build ๋ช…๋ น์–ด๋กœ Gradle์„ ์‚ฌ์šฉํ•ด ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋นŒ๋“œํ•œ ํ›„ docker build ๋ช…๋ น์–ด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ backend ๋””๋ ‰ํ† ๋ฆฌ์—์„œ Docker ์ด๋ฏธ์ง€๋ฅผ ๋นŒ๋“œํ•˜๊ณ  USERNAME ๊ฐ’์— ๋”ฐ๋ผ 2025capstone:latest ๋กœ ํƒœ๊น…ํ•จ

    => USERNAME/2025capstone:latest ์ด๋ฏธ์ง€ ์ƒ์„ฑ

  • docekr/login-action@v2 ๋ฅผ ์‚ฌ์šฉํ•ด Docker Hub์— ๋กœ๊ทธ์ธํ•จ

    => github secrets์— ์ง€์ •ํ•ด๋‘” ์œ ์ €์•„์ด๋””์™€ ๋น„๋ฐ€๋ฒˆํ˜ธ ์‚ฌ์šฉ

  • ๋นŒ๋“œํ•œ Docker ์ด๋ฏธ์ง€๋ฅผ docker push ๋ช…๋ น์–ด๋กœ Docker Hub์— ํ‘ธ์‹œํ•จ


  • run-docker-image-oon-ec2 ์ž‘์—…

    • ssh-keyscan์„ ์‚ฌ์šฉํ•ด EC2 ํ˜ธ์ŠคํŠธ์˜ SSH ๊ณต๊ฐœ ํ‚ค๋ฅผ ~/.ssh/known_hosts์— ์ถ”๊ฐ€ํ•จ

      (์œ„์— ์ฒจ๋ถ€ํ•œ ssh ์„ค์ • ํฌ์ŠคํŒ… ์ฐธ๊ณ )

    • appleboy/ssh-action@v0.1.6์„ ์‚ฌ์šฉํ•ด EC2์— SSH๋กœ ์—ฐ๊ฒฐํ•˜๊ณ  ์ž‘์—… ์‹คํ–‰

      • docker stop ๋ฐ docker rm ๋ช…๋ น์–ด๋กœ ์ด์ „์— ์‹คํ–‰์ค‘์ธ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์ค‘์ง€ํ•˜๊ณ  ์‚ญ์ œ

      • docker pull ๋กœ Docker Hub์—์„œ ์ตœ์‹  ์ด๋ฏธ์ง€๋ฅผ ๊ฐ€์ ธ์˜ด

      • docker run ๋ช…๋ น์–ด๋กœ ์ƒˆ๋กœ์šด ์ปจํ…Œ์ด๋„ˆ๋ฅผ 8080 ํฌํŠธ์— ๋ฐฐํฌํ•จ

      • docker system prune์„ ์‚ฌ์šฉํ•ด ๋ถˆํ•„์š”ํ•œ Docker ์ด๋ฏธ์ง€์™€ ์บ์‹œ๋ฅผ ์ •๋ฆฌํ•จ

 

4. ํ…Œ์ŠคํŠธ

 

 

 

 

workflow์— ์ง€์ •ํ•ด๋‘” backend/feature/cd-setup ๋ธŒ๋žœ์น˜์— test.md ๋ฅผ push ํ•˜์ž ์œ„ ์‚ฌ์ง„๊ณผ ๊ฐ™์ด ์ฒดํฌํ‘œ์‹œ๋กœ CD ์ž‘์—…์ด ์„ฑ๊ณต์ ์œผ๋กœ ์ด๋ฃจ์–ด์ง„ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Œ

 

 

EC2์— ๋“ค์–ด๊ฐ€ sudo docker ps ๋ช…๋ น์–ด๋กœ ํ˜„์žฌ ์‹คํ–‰์ค‘์ธ ์ปจํ…Œ์ด๋„ˆ ํ™•์ธ์‹œ ์ž˜ ๋™์ž‘ํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Œ

 

 

๋ฐ˜์‘ํ˜•

'๐ŸŒŠ Infra > CI ยท CD' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

Jenkins์„ ์ด์šฉํ•œ Spring Boot ๋ฐฐํฌ ์ž๋™ํ™”  (0) 2025.03.03
Github Actions ํ†บ์•„๋ณด๊ธฐ  (0) 2025.02.26
GitHub Actions ์†Œ๊ฐœ  (0) 2024.05.01