40.[CI/CD]

문도연·2022년 8월 8일
0

서비스가 규모가 복잡해지고 커질수록, 서비스를 배포하는 과정이 복잡해지고 소요되는 시간이 늘어나게 된다.
서비스의 변경 사항이 생길 때마다 배포 과정을 매번 수동으로 진행한다면, 배포 과정에 많은 시간을 허비해야 할 것임. 버튼 하나만 클릭하면 전체 배포 과정이 '자동'으로 진행되게 만들 수는 없을까?

Chapter1. 배포 자동화
Chapter1-1. 지속적 통합
Chapter1-2. 지속적 배포
Chapter2. Vercel
과제1. Vercel로 클라이언트 CI/CD 구축하기
Chapter3. 클라이언트&서버 배포 (AWS)
과제2. AWS로 서버 배포하기


Chapter1. 배포 자동화

배포 자동화란 한번의 클릭 혹은 명령어 입력을 통해 전체 배포 과정을 자동으로 진행하는 것을 뜻한다. 배포 자동화가 왜 필요할까?

  • 수동적이고 반복적인 배포 과정을 자동화함으로써 시간이 절약된다.
  • 휴먼 에러(Human Error)를 방지할 수 있다.
    • 여기서 휴먼 에러란 사람이 수동적으로 배포 과정을 진행하는 중에 생기는 실수들을 뜻한다. 그 전에 했던 배포 과정과 비교하여 특정 과정을 생략하거나 다르게 진행하여 오류가 발생하는 것이 휴먼 에러의 예로 볼 수 있다.

배포 자동화를 통해 전체 배포 과정을 매번 일관되게 진행하는 구조를 설계하여 휴먼 에러 발생 가능성을 낮출 수 있다.

배포 자동화 파이프라인

배포에서 파이프라인(Pipeline)이란 용어는 소스 코드의 관리부터 실제 서비스로의 배포 과정을 연결하는 구조를 뜻한다.

파이프라인은 전체 배포 과정을 여러 단계(Stages)로 분리한다. 각 단계는 파이프라인 안에서 순차적으로 실행되며, 각 단계마다 주어진 작업(Actions)들을 수행한다.

파이프라인을 여러 단계로 분리할 때, 대표적으로 쓰이는 세 가지 단계가 존재한다.

  • Source 단계: Source 단계에서는 원격 저장소에 관리되고 있는 소스 코드에 변경 사항이 일어날 경우, 이를 감지하고 다음 단계로 전달하는 작업을 수행한다.
  • Build 단계: Build 단계에서는 Source 단계에서 전달받은 코드를 컴파일, 빌드, 테스트하여 가공한다. 또한 Build 단계를 거쳐 생성된 결과물을 다음 단계로 전달하는 작업을 수행한다.
  • Deploy 단계: Deploy 단계에서는 Build 단계로부터 전달받은 결과물을 실제 서비스에 반영하는 작업을 수행한다.

파이프라인의 단계는 필요에 따라 더 세분화되거나 간소화될 수 있다. DevOps를 전문으로 학습하는 경우 아래와 같이 파이프라인의 단계를 세분화해서 나누기도 한다. 또한, 해당 툴을 소개하는 업체에 따라 용어를 미묘하게 다르게 사용하기도 한다.

CI/CD 파이프라인

Chapter1-1. 지속적 통합

지속적 통합 (CI; continuous integration)

팀 구성원이 각자의 작업을 자주 통합하는 소프트웨어 개발 방식. CI는 크게 세 가지 단계로 나뉜다.

  • Code: 개발자가 코드를 코드 저장소에 Push한다.
  • Build: 코드 저장소로부터 코드를 가져와서 (유닛 테스트 후) 빌드한다.
  • (Test): 코드 빌드의 결과물이 다른 컴포넌트와 잘 통합되는지 확인한다.

빌드는 개발자만 읽을수 있는 소스 코드를 실행 가능한 코드 및 프로그램으로 변환하는 과정이다. 번들링도 브라우저가 소스 코드를 더 잘 읽을 수 있게 도와줌으로, 빌드의 과정 중 하나로 볼 수 있다. 종종 두 용어 모두 "빌드"로 통용되기도 한다.

지속적 통합은 모든 코드 변화를 하나의 리포지토리에서 관리하는 것 부터 시작한다. 모든 개발팀이 코드의 변화를 확인할 수 있기 때문에, 투명하게 문제점을 파악할 수 있다. 그리고 잦은 풀 리퀘스트(pull request)와 머지(merge)로 코드를 자주 통합한다. 이 때, 기본적인 테스트도 작동시킬 수 있다. 이렇게 지속적 통합을 통해 개발팀은 각자 개발한 코드를 이른 시점에, 자주 합치고, 자주 테스트 해볼 수 있다.

지속적 통합으로 보안 이슈, 에러 등을 쉽게 파악할 수 있어 해당 이슈를 빠르게 개선할 수 있다. 이전에는 각자 개발자가 작성한 코드를 합치고 난 후, 모두 모여서 빌드를 시작하고 나서야 문제점을 파악할 수 있었다. 지속적 통합이 적용된 개발팀은 코드를 머지하기 전, 이미 빌드 오류나 테스트 오류를 확인하여 훨씬 더 효율적인 개발을 할 수 있게 된다.

[실습] 지속적 통합

이번 튜토리얼에서는 나만의 아고라 스테이츠 서버 레퍼런스와 Github Action을 이용하여 진행한다.
나만의 아고라 스테이츠 서버 레퍼런스

  1. 먼저, 자신의 깃허브 계정에 새로운 리포지토리를 만든다.
  2. 새로운 리포지토리에 나만의 아고라 스테이츠 서버 레퍼런스 코드를 push한다. drag & drop을 할 수도 있겠지만, 가능하면 기존 나만의 아고라 스테이츠 서버 레퍼런스를 클론받아서, 새로운 리포지토리를 원격 리포지토리로 등록하고, 코드를 push 해보자.
# 기존 나만의 아고라 스테이츠 서버 레퍼런스 클론
git clone git@github.com:codestates-seb/fe-sprint-my-agora-states-server-reference.git
# 디렉터리 이동
cd fe-sprint-my-agora-states-server-reference
# 새로운 리포지토리를 원격 리포지토리로 등록
git remote add myRepo git@github.com:{여러분의 아이디}/{새로운 리포지토리 이름}.git
# 기존 레퍼런스 코드를 새로운 리포지토리로 push
git push myRepo reference
  1. 코드가 모두 push가 되면, 커밋 기록을 보시면, 주황색 원을 확인할 수 있다.
  2. 주황색 원이 무엇인지 좀 더 자세히 파악하기 위해 Actions 탭으로 이동한다. Github Action을 코드를 설정해두면 이렇게 서버 테스트가 자동으로 작동하는 것을 확인할 수 있다

Github Action은 Github의 특정 이벤트에 맞게 다양한 작업을 시킬 수 있는 CI/CD 플렛폼이다. EC2와 같은 하나의 가상 인스턴스를 실행시켜서 원하는 작업을 시킬 수 있다.

그런데, 리포지토리를 push하기만 했는데, 왜 작동했을까? ./.github/workflows/pullRequest.yml 파일을 읽어보면, 언제 어떤 job을 할지 명시되어 있다.

name: Bare Minimum Requirements

# 언제 job을 작동시킬지
on: [push, pull_request]

# 어떤 job을 할지
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Bare Minimum Requirements
        uses: actions/setup-node@v1
        with:
          node-version: '16'
      - run: npm install
      - run: npm test

npm install은 빌드를 위한 준비과정으로 볼 수 있다. Node.js로 만든 서버 애플리케이션은 npm으로 관련 오픈소스를 모두 깔끔하게 설치해야 작동하기 때문.
npm test는 유닛 테스트 과정이다. 작성한 코드가 요구사항 충족을 위한 최소한의 조건을 만족했는지 확인한다.

github actions 공식문서

Chapter1-2. 지속적 배포

지속적 배포 (CD; continuous delivery, continuous deployment)

지속적 배포는 지속적 통합 과정이 원활하게 끝나면 바로 고객에게 배포하는 것이다.

지속적 배포는 크게 3가지 과정으로 나눌 수 있다.

  • Release: 릴리즈 단계에서는 빌드까지 모두 준비가 되었고, 어떤 기능이 개발되었는지 비즈니스 관계자들과 이야기를 나누는 단계이다. 어떤 기능을 넣을지, 해당 릴리즈는 배포를 할지 말지 결정하는 단계로 여러 의사결정이 이루어진다.
  • Deploy: 실제 배포한다.
  • Operation: 배포된 소프트웨어를 실제 운용하는 과정이다. 해당 과정에서 고객의 피드백을 충분히 받아 기획에 반영한다.

최근에는 클라우드 기술 발전과 맞물려 지속적 통합과 지속적 배포가 빠른 속도로 진행되면서 CI/CD를 하나로 묶어서 다루는 경우가 점차 증가하고 있다.

예를 들어, 이전에는 배포 자체가 상당히 오래 걸리고 힘든 일이어서 릴리즈 단계에서 많은 고민을 하곤 했다. 서버를 전부 재시작해야 한다거나, 일부 기능을 제공하지 못하는 경우도 많았기 때문. 요즘은 고객의 피드백을 빨리 받기 위해서라도, 서비스를 중단하지 않기 위해서라도 버전 릴리즈만 잘 기록해두고 바로바로 배포하는 사례가 증가하고 있다.

실리콘밸리 테크 기업들은 다양한 모니터링 툴과 장애 대응 프로세스를 마련해 배포에 대한 자신감을 높여 조직의 비즈니스 역량을 키우고 있다. “하루에 1,000번의 배포를 할 수 있는가?” 는 조직의 기술적 확장 역량을 판단할 수 있는 중요한 지표입니다.
하루에 1000번 배포하는 조직되기-뱅크샐러드

지속적 배포 사례

지속적 배포의 가장 흔한 사례가 Github Page.

지정해둔 디렉터리에 정해진 방식에 따라 잘 커밋하기만 하면, Github Page가 알아서 해당 index.html 파일과 해당 디렉터리에 있는 파일을 잘 번들링해서 Github Page 서버에 업로드한다.

이렇게 자동으로 인터넷에 배포가 되었고, 주변 가족이나 친구들에게 쉽게 만든 결과물을 공유할 수 있다. (틀린 부분이 있다면 빠르게 피드백도 받을 수 있다.)


Chapter2. Vercel - 클라이언트 배포

Vercel은 Netlify와 함께 프론트엔드 배포를 자동화화고, 그 외 여러 추가기능을 제공하는 배포 프레임워크이다. 깃허브 아이디를 통해 쉽게 가입할 수 있다.

1.new project 클릭
2.검색어 치려고 보면 configure Github app이 뜸 -> 인증 -> select 리포지토리

이후, 프로젝트 설정 화면을 볼 수 있다.

  • Project name: 프로젝트의 이름
  • Framework preset: 사용을 원하는 프론트엔드 프레임워크
    Create React App
  • Root directory: 루트 디렉터리 위치
    my-agora-states-client -- 배포는 클라이언트 디렉토리,,
  • Build command: 빌드 스크립트
  • Output directory: 빌드 결과 디렉터리 위치
  • Install command: 설치 스크립트
    자신의 프로젝트에 맞게 수정해야 하는 부분이 어디인지 파악하고, Deploy를 누른다.

이후에는 Deploy가 시작된다. Vercel이 EC2와 같은 인스턴스를 따로 실행시켜, 빌드 및 배포를 자동으로 진행한다.

대시보드로 다시 돌아가서, Deployment 탭을 누르면, Deployment Status를 확인할 수 있다. 로그를 잘 읽어보면, 언제 어디서 에러가 났는지 쉽게 파악할 수 있고, 대략적인 Vercel의 작동 방식에 대해서 이해할 수 있다. install 스크립트, build 스크립트를 실행하고 빌드 결과를 자신의 서버에 배포했다는 것을 파악할 수 있다.


과제1. Vercel로 클라이언트 CI/CD 구축하기

이번 과제에서는 Next.js 개발사로 유명한 Vercel의 CI/CD 기능을 이용하여 간단하게 React SPA 애플리케이션을 배포해본다.

  • 자신의 깃허브에 리액트로 제작한 나만의 아고라 스테이츠 레퍼런스, 혹은 자신의 리액트 프로젝트를 배포한다.
  • 최소한 하나 이상의 클라이언트 테스트를 작성하고, 테스트가 통과되어야 배포되도록 코드를 작성한다.

문제해결과정

깃허브 액션을 이용해 지속적인 통합, 배포를 해보고자 했다.
(소스코드는 위에서 실습한 지속적인 통합 레포지토리 코드를 활용했다.)

  1. 이미 소스코드를 내 컴퓨터에 git clone한 상태였고, 원격저장소 Remote 설정을 통해 내가 PR 보낼 곳을 추가했다. PR용 branch를 생성했다.
# 기존 나만의 아고라 스테이츠 서버 레퍼런스 클론
git clone git@github.com:codestates-seb/fe-sprint-my-agora-states-server-reference.git
# 디렉터리 이동
cd fe-sprint-my-agora-states-server-reference
# 새로운 리포지토리를 원격 리포지토리로 등록 / PR 보낼 곳을 추가하는 코드
git remote add myRepo git@github.com:{여러분의 아이디}/{새로 만든 리포지토리 이름}.git
# 기존 레퍼런스 코드를 새로운 리포지토리로 push
git push myRepo reference
# PR용 branch 생성하기
git checkout -b moon
  1. 깃허브 액션 개념을 이해한 뒤, 블로그를 참고해 yml파일을 수정했다.
  • pull request 이벤트 발생시 index.test.js 테스트를 통과하지 못하는 경우, pr에 코멘트가 달리고 자동으로 Closed되도록 했다.
    즉, 다시 테스트코드를 수정하고 pr을 날려야 하는 것이다.
//pullRequest.yml
name: Bare Minimum Requirements

on: 
  pull_request:
      branches:
      - reference

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Bare Minimum Requirements
        uses: actions/setup-node@v1
        with:
          node-version: '16'
      - name: npm install
        run: npm install
      - name: run test code
        run: npm test
      - name: if fail
        uses: actions/github-script@0.2.0 
        with:
          github-token: ${{github.token}}  
          script: |                       
            const ref = "${{github.ref}}"
            const pull_number = Number(ref.split("/")[2])
            await github.pulls.createReview({
              ...context.repo,
              pull_number,
              body:"테스트코드를 다시 확인해주세요😍 ",
              event: "REQUEST_CHANGES"
            })
            await github.pulls.update({
              ...context.repo,
              pull_number,
              state: "closed"
            })
        if: failure()                 
  1. server 디렉토리의 app.js에서 1번 테스트 정답을 의도적으로 오답으로 작성한 뒤, PR용 branch에 Push하며 pull request를 보냈다.
git add app.js
git commit -m "수정"
git push myRepo moon
  1. 의도에 맞게 PR이 close되는지 확인했다.

참고

브랜치 생성 및 풀리퀘스트 보내는 법
GitHub Actions 로 풀리퀘스트 test 검증하기


과제2. 서버 배포하기 (AWS)

[그림] 서버 배포 자동화 파이프라인

  • AWS 개발자 도구 서비스를 이용해서 배포 자동화 파이프라인을 구축해야 한다.
    • CodePipeline을 이용해서 각 단계를 연결하는 파이프라인을 구축한다.
    • Source 단계에서 소스 코드가 저장된 GitHub 리포지토리를 연결한다.
    • Deploy 단계에서 CodeDeploy 서비스를 이용하여 EC2 인스턴스에 변경 사항을 실시간으로 반영한다.
  • 나중에 변경 사항을 GitHub 리포지토리에 반영했을 경우, 배포 과정이 자동으로 진행되어야 한다.
  • 배포 과정에서 오류가 생길 경우, log 파일을 참조하여 문제점을 확인할 수 있어야 한다.

순서

  1. 인스턴스 생성
  2. 인스턴스에 개발 환경 구축하기
// 패키지 매니저가 관리하는 패키지의 정보를 최신 상태로 업데이트
sudo apt update
//nvm 설치
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash

export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm

// node.js 설치
nvm install node 
// npm 설치
sudo apt install npm

3.인스턴스에 태그 및 역할 부여

  • 태그추가
  • 새 IAM 역할 생성
    • AmazonS3FullAccess
    • AmazonSSMFullAccess
    • AWSCodeDeployRole
  • 신뢰관계 편집
    • Service 부분 아래와 같이 편집
    • ["ec2.amazonaws.com", "codedeploy.ap-northeast-2.amazonaws.com"]
  • 보안그룹 확인
    • 인바운드 규칙 편집 - 포트 80, 443(위치 무관)

4.EC2를 활용한 파이프라인 구축 실습

  • 파이프라인 구축
    • yml파일추가, scripts파일 추가
    • 경로 앞부분 /home/ssm-user/
//appspec.yml
version: 0.0
os: linux
files:
  - source: /
# 아래 destination은 여러분이 해당 리포지토리를 클론한 위치로 설정해주세요.
    destination: /home/ssm-user/fe-sprint-practice-deploy

hooks:
  ApplicationStop:
    - location: scripts/stop.sh
      runas: root
  AfterInstall:
    - location: scripts/initialize.sh
      runas: root
  ApplicationStart:
    - location: scripts/start.sh
      runas: root
//initialize.sh, start.sh, stop.sh
cd /home/ssm-user/fe-sprint-practice-deploy/server
  • codeDeploy
    • 애플리케이션 생성, 배포그룹 생성
  • codePipeline
    • 파이프라인 생성, 깃허브 리포지토리 연결

결과



+codeDeploy lifecyle


나만의 타이니시꾸릿 유닛 회고

지금 내 수준에서 이런것까지 공부해야하나? 라는 물음이 들었다가
문득 코드를 잘 짜는 것 만큼이나 배포를 잘 하는 것도 매우 중요하다는 생각이 들었다.

열심히 코딩해서 대중(혹은 가족, 친구)에게 앱을 선보일 건데,
배포를 잘 못하면 세상에 내 기술을 드러내지 못하게 된다.
배포를 잘 못하면 내가 짠 코드의 기능이 어그러진다.
배포를 잘 못하면 협업도 어그러진다.

근데, 이걸 프로젝트할 때 어떻게 적용을 하지,,? 내 짧은 지식으로는 감도 잘 안온다.. 응애..ㅜ 미래의 팀원이랑 같이 잘 고민해보자,,

오늘 배포를 스무번은 한 것같은데 진짜 마지막에는 정말 기도를 했다,, ㅋㅋㅋㅋㅋ

profile
중요한건 꺾이지 않는 마음이 맞는 것 같습니다

0개의 댓글