처음부터 나만의 CI/CD 파이프라인 구축하기
첫 CI/CD 파이프라인을 구축하는 것은 어렵게 느껴질 수 있습니다. 특히 업무 시스템이 복잡하고 실험하기 어려운 경우 더욱 그렇습니다. 하지만 핵심 원리는 놀랍도록 간단합니다. 파이프라인은 깨끗하고 재현 가능한 환경에서 의존성을 설치하고, 테스트를 실행하며, 애플리케이션을 빌드하는 자동화된 프로세스일 뿐입니다. 이 가이드는 처음부터 CI/CD 파이프라인을 구축하는 방법에 대한 기본 개념과 실용적인 단계를 안내하여, 수동적이고 오류가 발생하기 쉬운 배포를 신뢰할 수 있는 자동화된 워크플로우로 전환해 드립니다.
CI/CD를 연습하기 위해 Kubernetes나 프로덕션 규모의 시스템이 필요하지 않습니다. 작은 프로젝트, Git 저장소, GitHub Actions, GitLab CI, Jenkins 같은 CI 도구로 시작하세요. 가장 중요한 단계는 파이프라인 설정 파일을 한 줄도 작성하기 전에 로컬에서 빌드가 재현 가능한지 확인하는 것입니다.
기초: 재현성이 핵심
첫 번째 파이프라인 파일을 작성하기 전에 프로젝트가 처음부터 깨끗하게 빌드될 수 있어야 합니다. 저장소를 새로 클론했을 때 빌드가 실패하면 CI에서도 실패합니다.
1단계: 로컬 빌드를 재현 가능하게 만들기
가장 중요한 규칙은 로컬 머신에서 깨끗한 상태로 애플리케이션이 성공적으로 빌드되는지 확인하는 것입니다. 예를 들어:
- Node.js:
npm ci && npm test && npm run build - Python:
pip install -r requirements.txt && pytest - Go:
go test ./... && go build
2단계: 잠금 파일 커밋하기
잠금 파일(package-lock.json, go.sum, Pipfile.lock 등)은 결정론적 빌드에 필수적입니다. 잠금 파일이 없으면 CI 파이프라인이 로컬에서 사용한 것과 다른 버전의 의존성을 설치하여 예측할 수 없는 테스트 실패로 이어질 수 있습니다. Node.js에서 npm ci는 잠금 파일을 정확히 적용하여 이러한 '의존성 표류'를 방지합니다.
첫 번째 파이프라인 작성하기
재현 가능한 빌드가 준비되면 이제 자동화할 차례입니다. 기본 파이프라인은 빌드, 테스트, 배포의 세 단계로 구성됩니다.
3단계: CI/CD 도구 선택하기
선택은 코드가 어디에 있는지에 따라 달라지는 경우가 많습니다. 세 가지 일반적인 옵션은 다음과 같습니다:
| 도구 | 적합한 대상 | 파이프라인 정의 | 자체 호스팅 옵션 |
|---|---|---|---|
| GitHub Actions | 이미 GitHub에 있는 프로젝트, 공개 저장소(무료) | .github/workflows/*.yml |
가능(자체 호스팅 러너) |
| GitLab CI | Git 호스팅이 포함된 올인원 DevOps 플랫폼 | .gitlab-ci.yml |
가능(GitLab CE) |
| Jenkins | 높은 사용자 정의, 성숙한 생태계, 복잡한 빌드 로직 | Jenkinsfile (Groovy) |
가능(Java 기반, 플러그인 기반) |
4단계: 최소 파이프라인 설정 만들기
파이프라인 정의는 저장소 루트에 있는 YAML 또는 Groovy 파일로, CI 러너에게 수행할 작업을 지시합니다.
예시: .gitlab-ci.yml (GitLab CI)
이 파이프라인은 Go 애플리케이션을 빌드하고, 바이너리를 아티팩트로 저장한 후, 이후 단계에서 실행합니다.
default:
image: golang
stages:
- build
- run
build go:
stage: build
script:
- go build
artifacts:
paths:
- array
run go:
stage: run
script:
- ./array
예시: Jenkinsfile (선언적 파이프라인)
동일한 작업을 수행하며, Docker 이미지를 사용하여 Node.js 앱을 실행합니다.
pipeline {
agent { docker { image 'node:20-alpine' } }
stages {
stage('Build') {
steps { sh 'npm ci && npm run build' }
}
stage('Test') {
steps { sh 'npm run test' }
}
}
}
⚠️ 보안 경고: 데이터베이스 URL이나 API 키와 같은 환경 변수를 저장소에 하드코딩하지 마세요. CI 도구의 내장 시크릿 관리자(GitLab CI Variables, Jenkins Credentials, GitHub Actions Secrets)를 사용하여 작업에 주입하세요.
단계 간 데이터 공유
최신 파이프라인의 주요 기능은 작업 간 데이터를 공유할 수 있다는 것입니다. 이 기능이 없으면 모든 단계에서 애플리케이션을 다시 빌드해야 하므로 시간과 리소스가 낭비됩니다.
5단계: 아티팩트 사용하기
아티팩트는 한 작업에서 저장되어 후속 작업에서 사용할 수 있는 파일 또는 디렉터리입니다. GitLab CI 예시에서 build go 작업은 array 바이너리를 저장합니다. 그러면 run go 작업이 해당 바이너리를 자동으로 다운로드하여 재컴파일할 필요 없이 사용합니다.
배포 자동화 (선택 사항)
CI가 작동하면 지속적 배포를 추가할 수 있습니다. 원칙은 간단합니다. 배포는 테스트 통과 후에만 이루어져야 합니다.
6단계: 기본 배포 전략 구현하기
간단한 애플리케이션의 경우 SSH 명령으로 서버에 파일을 복사할 수 있습니다.
예시: rsync로 배포하기
deploy:
stage: deploy
image: alpine:latest
only:
- main
script:
- apk add --no-cache openssh-client rsync
- rsync -avz --delete dist/ deploy@yourserver:/var/www/app/
컨테이너를 사용하는 경우 배포 단계는 일반적으로 Docker 이미지를 빌드하고, 레지스트리에 푸시한 후, SSH를 사용하여 프로덕션 서버에서 docker compose pull && docker compose up -d를 트리거합니다.
연습 및 문제 해결
CI/CD에 대해 읽는 것만으로는 충분하지 않습니다. 진정으로 배우려면 파이프라인을 직접 실행하고 의도적으로 고장 내보야 합니다.
7단계: 의도적으로 고장 내서 배우기
이것이 진정한 이해가 일어나는 곳입니다. 다음 실험을 시도해 보세요:
- 의존성 선언을 제거하세요. CI가 실패하나요? 왜 그런가요?
- 잠금 파일을 삭제하세요. 버전 표류가 빌드를 깨뜨리나요?
- 불안정한 테스트를 추가하세요. 로컬에서는 통과하지만 CI에서는 실패하나요? 이는 종종 깨끗한 CI 환경의 차이를 드러냅니다.
8단계: 테스트 보고서와 로그 사용하기
CI는 어떤 테스트가 실패했는지, 느린지, 불안정한지에 대한 가시성을 제공해야 합니다. 로그를 사용하여 오류를 찾는 것뿐만 아니라 전체 프로세스를 이해하세요. 의존성 설치에 얼마나 걸리나요? 빌드는 정확히 어디서 실패하나요?
자주 묻는 질문
첫 CI/CD 파이프라인을 구축하는 데 얼마나 걸리나요? 간단한 프로젝트의 경우 기본 파이프라인을 1시간 이내에 실행할 수 있습니다. GitLab CI 실습 랩에서는 기본 파이프라인에 15분이 소요된다고 추정합니다. 시간은 주로 설정 파일을 작성하고 재현성 문제를 해결하는 데 소요됩니다.
초보자에게 Jenkins, GitHub Actions, GitLab CI 중 어떤 것을 선택해야 하나요? 코드가 GitHub에 있다면 GitHub Actions가 자연스러운 선택입니다. 올인원 플랫폼을 원한다면 GitLab CI가 훌륭합니다. Jenkins는 매우 강력하지만 학습 곡선이 가파르고 유지 관리가 더 많이 필요하므로 일반적으로 더 크고 복잡한 설정에 적합합니다.
CI/CD에서 아티팩트와 캐시의 차이는 무엇인가요?
아티팩트는 단계 간 파일을 전달하는 데 사용됩니다(예: 빌드 단계에서 컴파일된 바이너리를 배포 단계로 전달). 캐시는 여러 파이프라인 실행에서 의존성(node_modules 등)과 같은 파일을 재사용하여 빌드 속도를 높이는 데 사용됩니다.
로컬에서는 테스트가 통과하는데 CI 파이프라인에서는 실패하는 이유는 무엇인가요? 이는 일반적으로 환경 표류의 신호입니다. 로컬 머신에 다른 소프트웨어 버전이나 환경 변수가 있을 수 있습니다. 이것이 잠금 파일을 커밋하는 것이 중요하고, 로컬에서 깨끗한 클론으로 프로젝트를 실행하는 것이 필요한 첫 번째 단계인 이유입니다.
CI/CD 파이프라인을 구축하려면 Docker가 필요한가요? 엄격히 필수는 아니지만, 강력히 권장됩니다. Docker 컨테이너 내에서 작업을 실행하면 격리와 재현성이 보장됩니다. 각 작업이 깨끗하고 알려진 환경에서 실행되어 작업이 서로 간섭하거나 호스트 서버를 오염시키는 것을 방지합니다.
— Editorial Team
아직 댓글이 없습니다.