포스트

CI/CD 기초 (3) - CD: Delivery and Deployment

프로젝트별 배포의 의미부터 플랫폼 연동, needs와 if로 만드는 deploy job, staging 구분까지 검증을 통과한 코드를 자동으로 내보내는 CD를 정리합니다.

CI/CD 기초 (3) - CD: Delivery and Deployment

2편에서 push마다 자동으로 검사가 도는 CI를 만들었습니다. 이번 편은 그 다음, 검사를 통과한 코드를 자동으로 내보내는 CD입니다.

배포란 무엇인가?

CD를 만들기 전에 “배포”라는 말부터 구체화해야 합니다. 프로젝트 종류에 따라 배포가 가리키는 작업이 다르기 때문입니다.

프로젝트 종류배포가 뜻하는 것
정적 사이트 (블로그, 문서)빌드된 HTML 파일들을 호스팅 서버에 올리는 것
서버 앱 (API, 웹 서비스)새 코드를 서버에 올리고 프로세스를 재시작하는 것
컨테이너 앱새 이미지를 만들어 registry에 올리고, 서버가 새 이미지로 다시 뜨게 하는 것
라이브러리PyPI, npm 같은 패키지 저장소에 새 버전을 올리는 것

작업 내용은 다르지만 공통점이 있습니다. 전부 명령어의 나열로 적을 수 있다는 것입니다. 명령어로 적을 수 있으면 2편에서 배운 step으로 만들 수 있고, step으로 만들 수 있으면 자동화할 수 있습니다. CD는 결국 “배포 명령들을 워크플로에 옮겨 적는 일”입니다.

방법 1: 배포 플랫폼 연동

가장 쉬운 CD는 워크플로를 한 줄도 쓰지 않는 방식입니다. Vercel, Netlify, GitHub Pages 같은 배포 플랫폼에 저장소를 연결해 두면, 플랫폼이 main 브랜치를 지켜보다가 새 커밋이 올라올 때마다 알아서 빌드하고 배포합니다.

이 블로그가 그 방식입니다. GitHub 저장소가 Vercel에 연결되어 있어서, main에 push하는 순간 Vercel이 Jekyll 사이트를 빌드해 내보냅니다. 글을 쓰는 사람 입장에서 배포라는 별도 행위는 없고, “main에 push”가 곧 배포입니다.

정적 사이트나 프런트엔드 앱이라면 이 방식이 정답에 가깝습니다. 배포 대상이 플랫폼이 지원하는 형태를 벗어날 때, 예를 들어 내 서버에 올려야 하거나 배포 전후에 특수한 절차가 필요할 때 방법 2로 갑니다.

방법 2: deploy job 추가

2편의 CI 워크플로에 deploy job을 하나 더 붙입니다. 뼈대는 이렇습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      # ... 2편의 린트와 테스트 step ...

  deploy:
    needs: test                            # test가 성공해야만 시작
    if: github.ref == 'refs/heads/main'    # main 브랜치일 때만
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      # ... 빌드하고 서버로 내보내는 step ...

새로 나온 두 줄이 CD의 뼈대입니다.

needs: test는 job 사이의 순서를 정합니다. 원래 job들은 서로 독립적으로 동시에 도는데, needs를 걸면 지목한 job이 성공한 뒤에만 시작됩니다. test가 실패하면 deploy는 시작조차 하지 않습니다. 이 한 줄이 “검증을 통과한 것만 내보낸다”를 강제하는 장치이고, CI와 CD를 잇는 연결부입니다.

if: github.ref == 'refs/heads/main'는 조건입니다. 이 워크플로는 PR에서도 도는데, PR 단계에서는 검사만 하고 배포는 하면 안 됩니다. 이 조건이 “main에 실제로 합쳐졌을 때만 배포”를 보장합니다.

flowchart LR
    A[PR] --> B[test job]
    B -->|검사만 하고 끝| A
    C[main에 merge] --> D[test job]
    D -->|성공| E[deploy job]
    D -->|실패| F[배포 안 됨]

deploy job의 step 내용은 위에서 본 표처럼 프로젝트마다 다릅니다. 정적 사이트면 빌드 후 업로드, 서버 앱이면 SSH 접속 후 pull과 재시작, 컨테이너면 이미지 빌드와 push입니다. 마지막 형태는 5편에서 실제로 만듭니다.

배포에는 거의 항상 비밀값이 필요합니다. 서버 접속 키, 클라우드 자격 증명, 배포 토큰 같은 것들입니다. 이런 값을 YAML에 직접 쓰면 저장소를 볼 수 있는 모두에게 공개되므로 절대 안 되고, GitHub의 Secrets 기능에 넣고 참조합니다. 사용법은 4편에서 다룹니다.

Continuous Delivery와 Continuous Deployment

1편에서 Continuous Delivery는 마지막 결정을 사람이, Continuous Deployment는 기계가 한다고 정리했습니다. 위의 워크플로는 merge되면 끝까지 자동으로 나가므로 Continuous Deployment입니다.

사람의 승인을 한 번 끼워 넣고 싶으면 GitHub의 environment 기능을 씁니다. deploy job에 environment: production을 지정하고 저장소 설정에서 그 environment에 승인자를 걸어두면, test까지는 자동으로 돌고 deploy는 지정된 사람이 승인 버튼을 눌러야 시작됩니다. 워크플로는 그대로 두고 설정만으로 Delivery와 Deployment 사이를 오갈 수 있습니다.

staging 환경

서비스 규모가 커지면 배포 환경을 둘로 나눕니다.

  • staging: production과 같은 구성으로 만들어 둔 리허설 환경. 사용자는 안 보고 팀만 봅니다.
  • production: 실제 사용자가 쓰는 환경입니다.

흐름은 “main에 merge되면 staging에 자동 배포, staging에서 확인한 뒤 production으로”가 일반적입니다. 테스트가 아무리 촘촘해도 실제와 같은 환경에서만 드러나는 문제가 있기 때문에, 그걸 사용자보다 먼저 볼 자리를 만드는 것입니다. 개인 프로젝트에서는 없어도 되지만, 회사의 파이프라인을 읽을 때 staging이라는 단어는 반드시 만나게 되므로 개념은 알아두는 것이 좋습니다.

CD 이후의 main 브랜치

CD 이전의 main은 “코드가 모이는 곳”이지만, CD 이후의 main은 “여기 들어오면 사용자에게 나간다”는 뜻이 됩니다. 깨진 코드가 main에 들어오면 몇 분 안에 사용자가 겪게 된다는 뜻이기도 합니다. 2편에서 만든 branch protection이 CD 이후에는 권장이 아니라 필수가 되는 이유입니다. CI 없이 CD부터 붙이는 것은 순서가 틀린 자동화입니다.

정리

개념한 줄 요약
배포프로젝트마다 다르지만 전부 명령어 나열로 적을 수 있는 작업
플랫폼 연동Vercel 등에 저장소를 연결, 워크플로 없이 main push가 곧 배포
직접 배포deploy job을 추가, needs와 if로 조건을 건다
needs: test검증 통과가 배포의 전제 조건이 되게 하는 연결부
environment 승인설정만으로 Delivery(사람 승인)와 Deployment(전자동) 전환
stagingproduction과 같은 구성의 리허설 환경

다음 편에서는 실무 파이프라인에 반드시 등장하는 부품들, 캐시, Secrets, artifact, 수동 실행 버튼을 다룹니다.

다음 글: CI/CD 기초 (4) - Cache, Secrets, and Artifacts

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.