์Šคํ”„๋ง Docker, githubaction ์‚ฌ์šฉํ•˜์—ฌ ๋ฐฐํฌ ์ž๋™ํ™”(CI/CD) ํŒŒ์ดํ”„๋ผ์ธ ๊ตฌ์ถ•

์ด์ „์— ์ง„ํ–‰ํ–ˆ๋˜ ํ”„๋กœ์ ํŠธ๋•Œ๋„ ๊ทธ๋ ‡๊ณ  ์ด๋ฒˆ ํ”„๋กœ์ ํŠธ์—๋„ ci-cd ํŒŒ์ดํ”„๋ผ์ธ์„ ๊ตฌ์ถ•ํ•˜๋Š”๋ฐ github action, AWS S3, AWS CODEDEPLOY ๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค.  

 

์†Œํ”„ํŠธ์›จ์–ด ์•„ํ‚คํ…์ฒ˜๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ๊ตฌ์„ฑํ•˜์˜€๋‹ค. 

 

ํ•˜์ง€๋งŒ ์ด๋Š” ์„ธํŒ… ๊ณผ์ •์ด ๋„ˆ๋ฌด ๊ธธ๊ณ  ๋ณต์žกํ•˜๋‹ค๋Š” ์ƒ๊ฐ์ด ๋“ค์—ˆ๋‹ค. 

 

 

๋” ์ข‹์€ ๋ฐฉ๋ฒ•์ด ์—†์„๊นŒ ์ฐพ์•„๋ณด๋‹ค Docker๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ๋กœ ํ–ˆ๊ณ  ์ด์ „๊ณผ ๋‹ฌ๋ฆฌ ๋„ˆ๋ฌด ํŽธํ•ด์ง„ ํŒŒ์ดํ”„๋ผ์ธ ๊ตฌ์ถ• ๊ณผ์ •์„ ๊ธฐ๋กํ•˜๊ธฐ ์œ„ํ•ด ๊ธ€์„ ์“ด๋‹ค. 

 

 

 

๊ทธ ์ „์— cicd์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์ž

 

 

CICD ๋ž€ ๋ฌด์—‡์ธ๊ฐ€?

CI/CD๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ฐœ๋ฐœ ๋‹จ๊ณ„๋ถ€ํ„ฐ ๋ฐฐํฌ ๋•Œ๊นŒ์ง€์˜ ๋ชจ๋“  ๋‹จ๊ณ„๋ฅผ ์ž๋™ํ™”๋ฅผ ํ†ตํ•ด์„œ ์ข€ ๋” ํšจ์œจ์ ์ด๊ณ  ๋น ๋ฅด๊ฒŒ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋นˆ๋ฒˆํžˆ ๋ฐฐํฌํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์„ ๋งํ•œ๋‹ค.

 

 

CI (Continuous Integration)

CI (Continuous Integration)๋Š” "์ง€์†์ ์ธ ํ†ตํ•ฉ"์ด๋ผ๋Š” ์˜๋ฏธ์ด๋‹ค. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋ฒ„๊ทธ ์ˆ˜์ •์ด๋‚˜ ์ƒˆ๋กœ์šด ์ฝ”๋“œ ๋ณ€๊ฒฝ์ด ์ฃผ๊ธฐ์ ์œผ๋กœ ๋นŒ๋“œ ๋ฐ ํ…Œ์ŠคํŠธ๋˜๋ฉด์„œ ๊ณต์œ ๋˜๋Š” ๋ ˆํŒŒ์ง€ํ† ๋ฆฌ์— ํ†ตํ•ฉ(merge)๋˜๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค.

 

์ฃผ๊ธฐ์ ์œผ๋กœ ๋นˆ๋ฒˆํ•˜๊ฒŒ ๊ณต์œ ํ•˜์ง€ ์•Š์œผ๋ฉด ์™œ ์•ˆ ๋ ๊นŒ??

 

์˜ˆ๋ฅผ ๋“ค์–ด ์—ฌ๋Ÿฌ ๋ช…์ด์„œ ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๊ณ  ์žˆ๊ณ ,  ํ˜•์ƒ๊ด€๋ฆฌ ๋„๊ตฌ๋กœ git ์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค ๊ฐ€์ •ํ•˜์ž. ์ด๋“ค์ด ์ž์ฃผ mergeํ•˜์ง€ ์•Š๊ณ  2์ผ 3์ผ ํ˜น์€ ๋” ์˜ค๋žœ ๊ธฐ๊ฐ„ ๋™์•ˆ ๊ฐœ๋ฐœ์„ ์ง„ํ–‰ํ•˜๋‹ค๊ฐ€ ๋ฉฐ์น  ๋™์•ˆ ์ž‘์—…ํ•œ ๋งŽ์€ ์ฝ”๋“œ๋“ค์„ ํ•œ ๋ฒˆ์— merge ํ•œ๋‹ค๋ฉด ์–ด๋–ป๊ฒŒ ๋ ๊นŒ? ๐Ÿค”

 

๋งŽ์€ ์ถฉ๋Œ์ด ์ผ์–ด๋‚  ๊ฒƒ์ด๊ณ  ์ถฉ๋Œ ํ•ด๊ฒฐ ๊ณผ์ •์— ์Ÿ๋Š” ์‹œ๊ฐ„์ด ๋งŽ์•„์งˆ ๊ฒƒ์ด๋‹ค. ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๊ฐœ๋ฐœ ํšจ์œจ์„ฑ์€ ๋–จ์–ด์ง€๊ฒŒ ๋œ๋‹ค. 

 

๊ทธ๋ ‡๊ธฐ์—, ๊ฐ€๋Šฅํ•œ ์ž‘์€ ๋‹จ์œ„๋กœ ๋‚˜๋ˆ„์–ด์„œ ์ฃผ๊ธฐ์ ์œผ๋กœ ๋นˆ๋ฒˆํžˆ ๊ฐœ๋ฐœํ•˜๊ณ  ๊ณ„์†ํ•ด์„œ ํ†ตํ•ฉํ•˜์—ฌ ๋‚˜๊ฐ€๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•˜๋‹ค.

 

์ž์ฃผ mergeํ•˜์ž!

๋‹ค ์ข‹์€๋ฐ mergeํ•  ๋•Œ๋งˆ๋‹ค buildํ•˜๊ณ  testํ•˜๋Š” ๊ณผ์ •๋“ค์ด ๊ท€์ฐฎ๋‹ค. 

ํ•˜์ง€๋งŒ, ์—ฌ๊ธฐ์„œ ์ž๋™ํ™”๋ฅผ ์‚ฌ์šฉํ•ด ์ค€๋‹ค๋ฉด ์–ด๋–ป๊ฒŒ ๋ ๊นŒ? github์— ์ฝ”๋“œ๋งŒ ์˜ฌ๋ฆฌ๊ณ  ๋‚˜๋จธ์ง€ ์ž‘์—…์ธ ํ…Œ์ŠคํŠธ์™€ ๋นŒ๋“œ๋Š” ํ”„๋กœ๊ทธ๋žจ์ด ์ž๋™์œผ๋กœ ํ•ด์ค€๋‹ค๋ฉด ๋งŽ์€ ์ˆ˜๊ณ ๋ฅผ ๋œ ๊ฒƒ์ด๋‹ค.  

 

 

 

CD (Continuous Delivery)

CD๋Š” Continuous Delivery, ์ง€์†์ ์ธ ์ œ๊ณต์ด๋ผ๋Š” ์˜๋ฏธ์™€ Continuous Deployment, ์ง€์†์ ์ธ ๋ฐฐํฌ๋ผ๋Š” ์˜๋ฏธ๊ฐ€ ์žˆ๋‹ค.

 

์ •๋ฆฌํ•˜์ž๋ฉด, CI๊ฐ€ ์ƒˆ๋กœ์šด ์†Œ์Šค์ฝ”๋“œ์˜ ๋นŒ๋“œ, ํ…Œ์ŠคํŠธ, ๋ณ‘ํ•ฉ๊นŒ์ง€๋ฅผ ์˜๋ฏธํ•˜์˜€๋Š”๋ฐ, CD๋Š” ๊ฐœ๋ฐœ์ž์˜ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ๋ ˆํฌ์ง€ํ† ๋ฆฌ๋ฅผ ๋„˜์–ด, ๊ณ ๊ฐ์˜ ํ”„๋กœ๋•์…˜(Production) ํ™˜๊ฒฝ๊นŒ์ง€ ๋ฆด๋ฆฌ์ฆˆ ๋˜๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค.

 

 

 

๋ณธ๊ฒฉ์ ์œผ๋กœ ๊ตฌ์ถ• ๊ณผ์ •์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์ž

 

 

 

1. Dockerfile ์ž‘์„ฑ 

 

FROM

FROM์€ ๊ธฐ๋ณธ ์ด๋ฏธ์ง€๋ฅผ ์ง€์ •ํ•ด์ค€๋‹ค.

 

์ด๋ฏธ์ง€ ์ด๋ฆ„์ด openjdk์ด๊ณ  ํƒœ๊ทธ๊ฐ€ 17-alpine์ธ ์ด๋ฏธ์ง€๋ฅผ ๊ธฐ๋ณธ์œผ๋กœ ์‚ฌ์šฉํ•˜๊ฒ ๋‹ค๋Š” ๋œป์ด๋‹ค.

 

์ž์‹ ์˜ ๋ฒ„์ „์— ๋งž๊ฒŒ ์ด๋ฏธ์ง€๋ฅผ ์„ ํƒํ•˜๋ฉด ๋œ๋‹ค.

 

 

ARG

๊ทธ๋ฆฌ๊ณ  ARG๋ฅผ ํ†ตํ•ด์„œ jar ํŒŒ์ผ์˜ ์œ„์น˜๋ฅผ ์ง€์ •ํ•ด์คฌ๋‹ค.

 

jar ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜๊ณ  ๋‚˜๋ฉด build/libs์— ์ €์žฅ๋˜๊ธฐ ๋•Œ๋ฌธ์— build/libs/*.jar๊ฐ€ ์ƒ์„ฑ๋œ jar ํŒŒ์ผ์˜ ๊ฒฝ๋กœ๊ฐ€ ๋œ๋‹ค.

 

์ฐธ๊ณ ๋กœ gradle ๋นŒ๋“œ ์‹œ ์ผ๋ฐ˜์ ์ธ jar ํŒŒ์ผ๊ณผ plain์ด ๋ถ™์€ jar ํŒŒ์ผ์ด ์ƒ์„ฑ๋œ๋‹ค๋ฉด plain jar ํŒŒ์ผ์ด ์ƒ์„ฑ๋˜์ง€ ์•Š๋„๋ก ์„ค์ •ํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

 

build.gradle์— ๋‹ค์Œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์ฃผ๋ฉด jar ํŒŒ์ผ์ด ํ•˜๋‚˜๋งŒ ์ƒ๊ธธ ๊ฒƒ์ด๋‹ค.

 

 

 

COPY

jar ํŒŒ์ผ์„ ์ปจํ…Œ์ด๋„ˆ ๋‚ด๋ถ€๋กœ ๋ณต์‚ฌํ•œ๋‹ค.

COPY [์ƒ์„ฑ๋œ ๋กœ์ปฌ jar ํŒŒ์ผ ๊ฒฝ๋กœ] [์ปจํ…Œ์ด๋„ˆ ๋‚ด๋ถ€ jar ํŒŒ์ผ ๊ฒฝ๋กœ]

 

 

 

ENTRY POINT

entry point๋ฅผ ํ†ตํ•ด ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ์ž๋™์œผ๋กœ jar ํŒŒ์ผ์„ ์‹คํ–‰ํ•˜๋„๋ก ํ•œ๋‹ค.

ENTRYPOINT ["java","-Duser.timezone=Asia/Seoul","-Dspring.profiles.active=prod", "-jar","/app.jar"]

 

๋‚˜๊ฐ™์€ ๊ฒฝ์šฐ์—” timezone์„ ์„œ์šธ๋กœ ์„ค์ •ํ•ด์คฌ๊ณ  profile ์„ prod๋กœ ์žก๊ณ  jar ํŒŒ์ผ์„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ–ˆ๋‹ค. 

์˜ต์…˜์€ ๊ฐ ์ƒํ™ฉ์— ๋งž๊ฒŒ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค. 

 

 

 

 

2. docker-compose.yml ์ž‘์„ฑ

 

DB๋Š” AWS RDS๋ฅผ ์‚ฌ์šฉํ•ด ๋”ฐ๋กœ ๋นผ๋‘์—ˆ๊ธฐ ๋•Œ๋ฌธ์— redis์™€ Spring๋งŒ ๋„์–ด์ฃผ์—ˆ๋‹ค. 

 

docker-compose๋Š” ์‹คํ–‰ํ•  ๋•Œ .envํŒŒ์ผ์„ ์ฝ๊ธฐ ๋•Œ๋ฌธ์— .envํŒŒ์ผ์— ๋”ฐ๋กœ ์„ธํŒ…ํ•ด๋‘์—ˆ๋‹ค. 

.envํŒŒ์ผ์€ ๋ฌผ๋ก  docker-compose.ymlํŒŒ์ผ๊ณผ ๊ฐ™์€ ๋ฃจํŠธ์— ๋†”๋‘ฌ์•ผ ํ•œ๋‹ค. (๋‚˜์˜ ๊ฒฝ์šฐ์—” ec2 ์˜ /home/ubuntu์— ๋‘ )

 

restart: always ๋Š” ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ์–ด๋–ค ์ด์œ ๋กœ๋“  ์ค‘์ง€๋˜๋ฉด(ex. ์˜ค๋ฅ˜), ์ž๋™์œผ๋กœ ๋‹ค์‹œ ์‹œ์ž‘ํ•˜์—ฌ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์˜ ๊ฐ€์šฉ์„ฑ์„ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ์˜ต์…˜์ด๋‹ค.

 

redis ์˜ ๋ฐ์ดํ„ฐ๋“ค์€ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์žฌ์‹œ์ž‘ํ•ด๋„ ์œ ์ง€๋˜์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— volume์„ ์žก์•„ ๋‘์—ˆ๋‹ค. 

 

logging ๋ถ€๋ถ„์€ cloud watch์— ๋กœ๊ทธ๋ฅผ ๊ธฐ๋กํ•ด๋‘๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ–ˆ๋Š”๋ฐ, ci/cd์™€๋Š” ๊ด€๋ จ ์—†๋‹ค.

version: "3.8"
services:
  redis:
    image: redis:latest
    container_name: redis
    hostname: redis
    ports:
      - "6379:6379"
    volumes:
      - ./redis/data:/data
  spring:
    container_name: spring
    image: ${DOCKERHUB_USERNAME}/jikgong
    depends_on:
      - redis
    environment:
      - DB_ENDPOINT=${DB_ENDPOINT}
      - DB_PASSWORD=${DB_PASSWORD}
      - DB_USERNAME=${DB_USERNAME}
      - SECRET_KEY=${SECRET_KEY}
      - S3_ACCESS_KEY=${S3_ACCESS_KEY}
      - S3_SECRET_ACCESS_KEY=${S3_SECRET_ACCESS_KEY}
      - FCM_PATH=${FCM_PATH}
    logging:
      driver: awslogs
      options:
        awslogs-group: ...
        awslogs-region: ...
        awslogs-stream: ...
    ports:
      - "8080:8080"
    restart: always

 

 

3. .github/workflows/deploy.yml ์ž‘์„ฑ

 

github action์—์„œ ์ˆ˜ํ–‰ํ•  ๋™์ž‘๋“ค์„ deploy.yml ํŒŒ์ผ์— ์ž‘์„ฑํ•ด์ฃผ์—ˆ๋‹ค. 

 

name: Java CI with Gradle

on:
  push:
    branches: [ "release" ]

permissions:
  contents: read

jobs:
  build-docker-image:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Set up JDK 17
        uses: actions/setup-java@v3
        with:
          java-version: '17'
          distribution: 'temurin'

      - name: Run chmod to make graldew executable
        run: chmod +x ./gradlew

      - name: Build with Gradle
        uses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1
        with:
          arguments: clean bootJar -Pspring.profiles.active=prod

      - name: docker image build
        run: docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/jikgong .

      - name: docker login
        uses: docker/login-action@v1
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: docker Hub push
        run: docker push ${{ secrets.DOCKERHUB_USERNAME }}/jikgong

  cd-pipeline:
    needs: build-docker-image
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@master
      - name: copy file via ssh password
        uses: appleboy/scp-action@master
        with:
          host: ${{ secrets.SSH_HOST }}
          username: ubuntu
          password: ${{ secrets.SSH_PASSWORD }}
          port: 22
          source: "./docker-compose.yml"
          target: "/home/ubuntu/"

      - name: executing remote ssh commands using password
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.SSH_HOST }}
          username: ubuntu
          password: ${{ secrets.SSH_PASSWORD }}
          port: 22
          script: |
            sudo docker rm -f $(docker ps -qa)
            sudo docker pull ${{ secrets.DOCKERHUB_USERNAME }}/jikgong
            sudo docker-compose up -d
            sudo docker image prune -f

 

์ฒซ๋ฒˆ์งธ JOB - build-docker-image 

uses: actions/checkout@v3

- ํ˜„์žฌ ์›Œํฌ์ŠคํŽ˜์ด์Šค(๋ ˆํฌ์ง€ํ† ๋ฆฌ)์— ๋Œ€ํ•œ ์†Œ์Šค ์ฝ”๋“œ๋ฅผ ๊ฐ€์ ธ์˜ค๊ณ  ์ž‘์—… ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ์ง€์ •๋œ ๋ธŒ๋žœ์น˜ ๋˜๋Š” ์ปค๋ฐ‹์œผ๋กœ ์„ค์ •

 

name: docker image build

- ๋„์ปค ์ด๋ฏธ์ง€ ๋นŒ๋“œ. ์œ„์—์„œ ์ž‘์„ฑํ•œ Dockerfile์„ ์—ฌ๊ธฐ์„œ ์ฝ์–ด๋“ค์ธ๋‹ค. 

 

name: docker login

- ๋„์ปค ํ—ˆ๋ธŒ์— ๋กœ๊ทธ์ธ์„ ํ•ด์ค€๋‹ค. ์ด๋•Œ username๊ณผ token์„ ์‚ฌ์šฉํ•˜์—ฌ ๋กœ๊ทธ์ธ ํ•˜๋Š”๋ฐ token์€ docker hub ์‚ฌ์ดํŠธ์—์„œ ๋ฐœ๊ธ‰ ๊ฐ€๋Šฅํ•˜๋‹ค. 

 

name: docker Hub push

- docker hub ์— push ํ•ด์ค€๋‹ค. ๋”ฐ๋กœ ํƒœ๊ทธ๋ฅผ ์ง€์ •ํ•ด์ฃผ์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์— latest๋กœ ์ž๋™ ์ง€์ •๋œ๋‹ค.

 

 

๋‘๋ฒˆ์งธ JOB - cd-pipeline

needs: build-docker-image

- build-docker-image์˜ job์ด ์™„๋ฃŒ๋  ๋•Œ ๊นŒ์ง€ ๋Œ€๊ธฐํ•œ๋‹ค. 

 

copy file via ssh password

- SSH ๋ฅผ ์ด์šฉํ•˜์—ฌ file ์„ ec2๋กœ ์ด๋™

- host: ์›๊ฒฉ ์„œ๋ฒ„์˜ ํ˜ธ์ŠคํŠธ ์ฃผ์†Œ(IP ์ฃผ์†Œ ๋˜๋Š” ๋„๋ฉ”์ธ ์ด๋ฆ„)

- username: SSH ์—ฐ๊ฒฐ์„ ์œ„ํ•ด ์‚ฌ์šฉ๋˜๋Š” ์›๊ฒฉ ์„œ๋ฒ„์˜ ๊ณ„์ • ์ด๋ฆ„ (ec2๋ฅผ ๋งŒ๋“ค ๋•Œ ubuntu ๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค๋ฉด ubuntu ์ด๋‹ค.)

- password: SSH ์—ฐ๊ฒฐ์„ ์œ„ํ•œ ๋น„๋ฐ€๋ฒˆํ˜ธ. ec2์˜ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋”ฐ๋กœ ์„ค์ •ํ•ด์•ผ ํ•จ

- port: SSH ์„œ๋ฒ„๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ํฌํŠธ ๋ฒˆํ˜ธ. ์ผ๋ฐ˜์ ์œผ๋กœ 22๋ฒˆ ํฌํŠธ๊ฐ€ ์‚ฌ์šฉ

- source: ๋กœ์ปฌ์—์„œ ๋ณต์‚ฌํ•  ํŒŒ์ผ ๋˜๋Š” ๋””๋ ‰ํ† ๋ฆฌ์˜ ๊ฒฝ๋กœ.

- target: ์›๊ฒฉ ์„œ๋ฒ„์— ๋ณต์‚ฌํ•  ๊ฒฝ๋กœ.

 

executing remote ssh commands using password

- ์ž‘์„ฑํ•œ ์Šคํฌ๋ฆฝํŠธ ๋ช…๋ น์–ด ์‹คํ–‰

- ์‹คํ–‰์ค‘์ธ ์Šคํ”„๋ง, ๋ ˆ๋””์Šค ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์ข…๋ฃŒํ•˜๊ณ  docker compose ์‹คํ–‰