๐Ÿง‘โ€๐Ÿ’ป ์งง์€ํ˜ธํก/React

ํ”„๋ก ํŠธ์—”๋“œ ๋ชจ๋…ธ๋ ˆํฌ ๊ตฌ์ถ• ์‚ฝ์งˆ๊ธฐ (3) - CICD ๋ฐฐํฌ, Docker, Github Actions

ํ•œ๊ทœ์ง„ 2023. 1. 18. 14:42

์‚ฌ์‹ค ํ•œ๋ฒˆ์— ๋๋‚  ์ค„ ์•Œ๊ณ  dev ๋ธŒ๋žœ์น˜์— ๋ฐ”๋กœ ํ…Œ์ŠคํŠธ๋ฅผ ํ–ˆ๋‹ค. ๊ทธ ๊ฒฐ๊ณผ ์•„์ง ์ œ๋Œ€๋กœ ์‹œ์ž‘๋„ ์•ˆํ–ˆ๋Š”๋ฐ ์ปค๋ฐ‹์ด 100๊ฐœ๊ฐ€ ๋„˜์–ด๊ฐ€๋Š” ๋ถˆ์ƒ์‚ฌ๊ฐ€ ์ƒ๊ฒผ๋‹ค. ์–ด์จŒ๋“  ๋ชจ๋…ธ๋ ˆํฌ ์‹œ๋ฆฌ์ฆˆ ๋งˆ์ง€๋ง‰์€ Docker์™€ Github Actions๋ฅผ ์ด์šฉํ•œ CI/CD ๊ตฌ์ถ• ์‚ฝ์งˆ๊ธฐ. ๋ณ„๋กœ ์–ด๋ ค์šด ๋‚ด์šฉ์ด ์•„๋‹ˆ๊ณ , ๊ทธ๋ ‡๊ธฐ์— ๊ฑฐ์ฐฝํ•œ ์ •๋ณด ๊ณต์œ  ๋ชฉ์ ์€ ๊ฒฐ์ฝ” ์•„๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋‚˜์ฒ˜๋Ÿผ ์ŒฉํŒ ์ฒ˜์Œ ๋ชจ๋…ธ๋ ˆํฌ๋ฅผ ์‹œ๋„ํ•ด๋ณด๋Š” ์‚ฌ๋žŒ๋“ค์ด ์‰ฝ๊ฒŒ ์ฐธ๊ณ ํ•  ์ˆ˜ ์žˆ์„๋งŒํ•œ ๊ธ€์ด ๋ณ„๋กœ ์—†์–ด ํž˜๋“ค์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ๊พธ์—ญ๊พธ์—ญ ์“ฐ๋Š” ๊ธ€.

 


 

๊ธฐ๋ณธ์ ์œผ๋กœ ์ด์ „์— ๊ตฌ์ถ•ํ•ด ์‚ฌ์šฉํ•˜๋˜ ์„ธํŒ…์„ ๋ฐ”ํƒ•์œผ๋กœ ํ•˜๊ณ  ์žˆ๋‹ค. (1ํŽธ, 2ํŽธ) ํฌ๊ฒŒ ๋‹ค๋ฅผ๊ฑด ์—†์ง€๋งŒ, ๊ฑฐ๊ธฐ์„œ๋ถ€ํ„ฐ ํ•œ์ค„ํ•œ์ค„์”ฉ ๋ฐ”๊ฟ”๊ฐ€๋Š” ๊ณผ์ •์ด๋ผ๊ณ  ๋ณด๋ฉด ๋  ๊ฒƒ ๊ฐ™์Œ. ๋”ฐ์ง€๊ณ  ๋ณด๋ฉด ํ•œ์ค„ํ•œ์ค„์”ฉ ๊ณ ์ณ๋‚˜๊ฐ€๋Š” ๊ณผ์ •..

 

  "scripts": {
    // ...
    "admin:build": "yarn workspace admin build",
    "ticket:build": "yarn workspace ticket build",
    "ticket:start": "yarn workspace ticket start",
  },

๋จผ์ € ๋ฃจํŠธ์˜ package.json์—์„œ ์œ„์˜ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค. ์›Œํฌ์ŠคํŽ˜์ด์Šค์— ์žˆ๋Š” ํ•œ app์˜ package.json์— ์žˆ๋Š” ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

Dockerfile.admin

FROM node:16 AS builder
# set working directory
WORKDIR /app
# install app dependencies
COPY package.json ./
COPY yarn.lock ./
# Installs all node packages
RUN npm install yarn --global --force
RUN yarn install --immutable --immutable-cache --check-cache

# Copies everything over to Docker environment
COPY . ./
RUN yarn install --immutable
RUN yarn admin:build

#Stage 2
#######################################
#pull the official nginx:1.19.0 base image
FROM nginx:1.19.0
COPY ./nginx/admin.conf /etc/nginx/conf.d/default.conf
# Remove default nginx static resources
RUN rm -rf ./usr/share/nginx/html/*
# Copies static resources from builder stage
COPY --from=builder /app/apps/admin/dist /usr/share/nginx/html/
EXPOSE 3100
ENTRYPOINT ["nginx", "-g", "daemon off;"]

์–ด๋“œ๋ฏผ ๋ ˆํฌ์ง€ํ† ๋ฆฌ์— ๋Œ€ํ•œ ๋„์ปคํŒŒ์ผ์ด๋‹ค. ๊ธฐ์กด 9์›”๊ณต์—ฐ ์„œ๋น„์Šค์—์„œ ์“ฐ๋˜๊ฑธ ํ† ๋Œ€๋กœ ๋ฐ”๊พธ๋‹ค ๋ณด๋‹ˆ yarn install์„ ๋‘๋ฒˆ์— ๋‚˜๋ˆ„์–ด์„œ ํ•˜๊ฒŒ ๋˜์—ˆ๋Š”๋ฐ, ์ง€๊ธˆ ์ƒ๊ฐํ•˜๋‹ˆ๊นŒ ๊ทธ๋Ÿด ํ•„์š”๊ฐ€ ์—†์—ˆ๋„ค.

 

yarn์„ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— npm์—์„œ ๋จผ์ € yarn์„ ์„ค์น˜ํ•œ ํ›„์— ์˜์กด์„ฑ ํŒจํ‚ค์ง€๋“ค์„ ์„ค์น˜ํ•ด์ค€๋‹ค. CI ํ™˜๊ฒฝ์—์„œ๋Š” yarn.lockํŒŒ์ผ์„ ์ƒ์„ฑํ•˜์ง€ ์•Š๊ณ  ์—…๋ฐ์ดํŠธ๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ ์‹คํŒจํ•˜๋„๋ก ํ•ด์ฃผ๋Š”๊ฒŒ ์•ˆ์ „ํ•˜๋‹ค ํ•œ๋‹ค. --frozen-lockfile ์˜ต์…˜์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค. ์ด์ œ๋Š” ๊ทธ๋ณด๋‹ค --immutable ์˜ต์…˜์„ ์‚ฌ์šฉํ•˜๋ผ๊ณ  ๊ถŒ์žฅํ•˜๊ณ  ์žˆ๋‹ค. --immutable-cache์™€ --check-cache ์˜ต์…˜์€ zero-install ํ™˜๊ฒฝ์—์„œ ์‚ฌ์šฉํ•œ๋‹ค๊ณ  ํ•˜๋Š”๋ฐ,, ์ง€๊ธˆ์€ zero-install์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ์žˆ์œผ๋‹ˆ๊นŒ ์ˆ˜์ •ํ•ด์•ผ๊ฒ ๋‹ค.

 

์˜์กด์„ฑ ์„ค์น˜ ํ›„์—๋Š” ์œ„์— ์Šคํฌ๋ฆฝํŠธ์— ์ž‘์„ฑํ•ด๋†“์•˜๋˜ yarn admin:build ๋ช…๋ น์œผ๋กœ ์–ด๋“œ๋ฏผ ํ”„๋กœ์ ํŠธ๋ฅผ ๋นŒ๋“œํ•œ๋‹ค. ๊ทธ ๋’ค๋ก  nginx ์„ค์ •ํŒŒ์ผ์˜ ๊ฒฝ๋กœ ์™ธ์—๋Š” ๋ณ€๊ฒฝ์‚ฌํ•ญ์ด ์—†๋‹ค.

 

Dockerfile.ticket

๋‹ค์Œ์€ Next ํ”„๋กœ์ ํŠธ ๊ด€๋ จ ๋„์ปคํŒŒ์ผ์ด๋‹ค. ๋„ฅ์ŠคํŠธ๋ฅผ ์“ฐ๋Š” ํ”„๋กœ์ ํŠธ๊ฐ€ ์ฒ˜์Œ์ด๋ผ ๋น„๊ต์  ์‚ฝ์งˆ์ด ๋งŽ์•˜๋‹ค.

 

FROM node:16-alpine AS builder
# set working directory
WORKDIR /app
# install app dependencies
COPY package.json ./
COPY yarn.lock ./
# Installs all node packages
RUN npm install yarn --global --force
RUN yarn install --immutable --immutable-cache --check-cache


# Copies everything over to Docker environment
COPY . ./
RUN rm -rf apps/admin

RUN yarn install
RUN yarn ticket:build
RUN rm -rf apps/ticket/.next/cache

#Stage 2
EXPOSE 3000
CMD ["yarn", "ticket:start"]

๋ฆฌ์•กํŠธ ํ”„๋กœ์ ํŠธ ๋ฐฐํฌํ• ๋•Œ์™€ ํฌ๊ฒŒ ๋‹ค๋ฅธ๊ฑด ์—†๋‹ค. ๋‹ค๋งŒ Nginx๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  Next ์„œ๋ฒ„๋ฅผ ์‹คํ–‰์‹œ์ผœ ์‚ฌ์šฉํ•œ๋‹ค. ๋ชจ๋…ธ๋ ˆํฌ ๊ด€๋ จ ์„ธํŒ… ์‚ฝ์งˆ์„ ํ•˜๋ฉด์„œ ์–ด๋”˜๊ฐ€ ๊ผฌ์˜€๋Š”์ง€, yarn install ์— --immutable ์˜ต์…˜์„ ์ฃผ๋ฉด yarn.lock์ด ๋ณ€๊ฒฝ๋  ์ˆ˜ ์žˆ๋‹ค๊ณ  ์˜ค๋ฅ˜๋‚ด๊ณ  ๋๋‚ด๋ฒ„๋ฆฌ๋”๋ผ. ์˜ต์…˜์„ ์ œ๊ฑฐํ•ด์ฃผ์–ด๋„ ์ผ๋‹จ ๋นŒ๋“œ ์ž˜๋˜๊ณ  ๋ฐฐํฌ๋„ ์ž˜ ๋˜๋Š”๊ฒƒ์„ ๋ณด๊ณ ... ์ผ๋‹จ์€ ๊ทธ๋ ‡๊ฒŒ ํ–ˆ๋‹ค. ๋ชจ๋ž˜ ์œ„์— ๊ฑด๋ฌผ์„ ์ง“๋Š” ๋Š๋‚Œ์ด๋‹ค.

 

์ฒ˜์Œ ๋”ฑ ๋„์ปค ์ด๋ฏธ์ง€๋ฅผ ํ—ˆ๋ธŒ์— ์˜ฌ๋ ธ๋”๋‹ˆ ๋ฌด๋ ค 750MB์˜ ์šฉ๋Ÿ‰์„ ๊ฐ–๊ณ  ์žˆ์—ˆ๋‹ค. ์ด๊ฒŒ ๋ง์ด ๋˜๋‚˜?? ์ตœ๋Œ€ํ•œ ์ด๋ฏธ์ง€์˜ ํฌ๊ธฐ๋ฅผ ์ค„์—ฌ๋ณด๋ ค๊ณ  ํ–ˆ๋‹ค.

node ์ด๋ฏธ์ง€๋ฅผ ๊ฐ€์ ธ์˜ฌ ๋•Œ ์šฉ๋Ÿ‰์ด ๊ฐ€์žฅ ์ž‘์€ alpine ๋ฒ„์ „์„ ์‚ฌ์šฉํ•˜๋„๋ก ๋ฐ”๊ฟจ๋‹ค. ์ด๊ฒƒ ํ•˜๋‚˜๋งŒ์œผ๋กœ ๊ฑฐ์˜ 200MB ๊ฐ€๊นŒ์ด ์ค„์–ด๋“ค์—ˆ๋‹ค. ์ถ”๊ฐ€๋กœ ์–ด๋“œ๋ฏผ ๊ด€๋ จ ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ์ง€์› ๊ณ , ๋นŒ๋“œ ํ›„ ๋‚˜์˜จ .next์˜ cache ํŒŒ์ผ๋“ค๋„ ์‚ญ์ œํ•˜๋„๋ก ํ–ˆ๋‹ค. ๊ทธ ๊ฒฐ๊ณผ 450MB์ •๋„๋กœ ์ค„์ผ ์ˆ˜ ์žˆ์—ˆ๋‹ค. ํ•˜์ง€๋งŒ ์—ฌ์ „ํžˆ ํฌ๋‹ค..! ์›๋ž˜ ์ด๋Ÿฐ๊ฑด๊ฐ€.

 

GitHub Actions

์•ก์…˜ ์›Œํฌํ”Œ๋กœ์šฐ ํŒŒ์ผ์€ ํ‹ฐ์ผ“๊ณผ ์–ด๋“œ๋ฏผ์ด ๊ฑฐ์˜ ๋™์ผํ•˜๋‹ค. ์ค‘์š”ํ•˜๊ฒŒ ๋ณผ ๋ถ€๋ถ„์€ ํƒœ๊ทธ ๋ถ€๋ถ„์ด๋‹ค.

๊นƒํ—ˆ๋ธŒ์—์„œ ์ƒˆ๋กœ์šด ํƒœ๊ทธ๋ฅผ ๋‹ฌ๊ณ  ๋ฆด๋ฆฌ์ฆˆ๋ฅผ ํ•˜๋ฉด ์•ก์…˜์ด ํŠธ๋ฆฌ๊ฑฐ๋˜๊ณ  ๋„์ปค ์ด๋ฏธ์ง€๊ฐ€ ํ—ˆ๋ธŒ๋กœ ์˜ฌ๋ผ๊ฐ€๋„๋ก ํ•˜๋Š”, ๊ทธ๋Ÿฐ ํ๋ฆ„์„ ๊ฐ–๊ณ  ์žˆ๋‹ค. ํ‹ฐ์ผ“๊ณผ ์–ด๋“œ๋ฏผ ๋ ˆํฌ ๊ฐ๊ฐ ๋ณ„๊ฐœ๋กœ ์‹คํ–‰๋˜์–ด์•ผ ํ•œ๋‹ค. ๋ฆด๋ฆฌ์ฆˆ๋ฅผ ํ• ๋•Œ ํƒœ๊ทธ๋ช…์— 'Ticket-\*', 'Admin-\*'๊ณผ ๊ฐ™์ด ์•ž์— ๋ ˆํฌ๋ช…์„ ๋ถ™์—ฌ์„œ ์˜ฌ๋ฆฐ๋‹ค. ๊ทธ๋Ÿผ ๊ทธ ํƒœ๊ทธ ๋ช…์— ๋งž๋Š” ์•ก์…˜์ด ์‹คํ–‰๋˜๋„๋ก ํ–ˆ๋‹ค.

 

name: Build & Docker Push - Ticket
on:
  push:
    # branches: [dev]
    # paths: ['apps/ticket/pages/**']
    # Publish semver tags as releases.
    tags:
      - Ticket-v*.*.*

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2

      # ๋„์ปค ์ด๋ฏธ์ง€์— env ํŒŒ์ผ ํฌํ•จ
      - name: Create .env file
        run: |
          touch .env
          echo "${{ secrets.ENV_VARS_TICKET }}" >> .env
      # ๋„์ปค ์ด๋ฏธ์ง€ ๋ฒ„์ „ ๊ฐ€์ ธ์˜ค๊ธฐ
      - name: Get the version
        id: get_version
        run: |
          RELEASE_VERSION_WITHOUT_V="$(cut -d'v' -f2 <<< ${GITHUB_REF#refs/*/})"
          echo ::set-output name=VERSION::$RELEASE_VERSION_WITHOUT_V

      # ๋„์ปค ๋นŒ๋“œ ๊ด€๋ จ ์…‹์—…
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v1
      # ๋‚ด ๋„์ปคํ—ˆ๋ธŒ ๋กœ๊ทธ์ธ
      - name: Login to DockerHub
        uses: docker/login-action@v1
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}
      # ๋„์ปค์ด๋ฏธ์ง€ ๋นŒ๋“œํ•˜๊ณ  ํ—ˆ๋ธŒ๋กœ ํ‘ธ์‰ฌ
      - name: Build and push
        uses: docker/build-push-action@v2
        with:
          context: .
          file: ./Dockerfile.ticket
          platforms: linux/amd64
          push: true
          tags: david0218/dudoong-ticket:${{ steps.get_version.outputs.VERSION }}

์ค‘์š”ํ•œ ๋ถ€๋ถ„์€ ๊นƒํ—ˆ๋ธŒ์—์„œ ๋ฆด๋ฆฌ์ฆˆ๋œ ๋ฒ„์ „์„ ๊ฐ€์ ธ์˜ค๊ณ , 'v'์•ž์˜ ๋‚ด์šฉ์„ ๋–ผ์„œ ๋”ฑ 'x.x.x' ๋ถ€๋ถ„๋งŒ ํƒœ๊ทธ๋กœ ๋‹ฌ์•„์ค€๋‹ค๋Š” ๊ฒƒ. ๋ฐฑ์—”๋“œ ํŒ€์žฅ์นœ๊ตฌ์˜ ์ฝ”๋“œ์ด๋‹ค!! (์ด๊ฑฐ ์•ˆ์“ฐ๋ฉด ๋ถ„๋ช… ๋ญ๋ผํ• ๊ฒƒ๊ฐ™์Œใ…Žใ…‹)

 

๋„์ปค ํ—ˆ๋ธŒ์— ๋”ฑ ์›ํ•˜๋˜ ํƒœ๊ทธ๋กœ ๋‹ฌ๋ ค์„œ ์˜ฌ๋ผ๊ฐ€๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ์ปดํฌ์ฆˆ์—์„œ ์›ํ•˜๋Š” ํƒœ๊ทธ๋กœ ๊ฐ€์ ธ์™€์„œ ๋ฐฐํฌํ•˜๋„๋ก ํ•  ์ˆ˜ ์žˆ๋‹ค.

 


 

๋ถ„๋ช…ํžˆ ์‚ฝ์งˆ์€ ๋” ๋งŽ์ด ํ–ˆ๋˜ ๊ฒƒ ๊ฐ™์€๋ฐ, ๋‚˜์ค‘์— ๊ธฐ์–ต ๋„์ง‘์–ด์„œ ์“ฐ๋ ค๋‹ˆ๊นŒ ๋น ์ง„ ๋ถ€๋ถ„์ด ๋งŽ์€ ๊ฒƒ ๊ฐ™๋‹ค. ํ•˜์’ธ.