필사 모드: 현대 CI/CD 파이프라인 완전 분해 — GitHub Actions, GitOps, Argo CD, BuildKit, SLSA, Sigstore, SBOM (2025)
한국어> "Continuous delivery is the ability to get changes of all types into production — safely and quickly in a sustainable way." — Jez Humble (*Continuous Delivery*, 2010)
CI/CD는 지루해 보인다. "빌드 돌리고, 테스트 돌리고, 배포한다." 그러나 대형 팀과 소형 팀을 가르는 가장 큰 차이가 **CI/CD 파이프라인의 질**이라는 건 놀라운 사실이다. Google은 "커밋 → 배포"를 **분 단위**로, 대부분의 한국 기업은 **일 단위**로 한다. 이 격차는 단순한 도구 차이가 아니라, **철학과 축적된 자동화**의 차이다.
2005년의 Jenkins부터 2025년의 GitHub Actions + Argo CD + Sigstore까지. 이 글은 현대 CI/CD의 지도를 그린다.
1. CI/CD의 간단한 역사
2001 — XP와 Continuous Integration
- Kent Beck의 *Extreme Programming*이 CI를 처음 제창
- "하루 여러 번 통합"
- CruiseControl (2001) — 최초의 오픈소스 CI 서버
2005 — Hudson의 탄생
- Sun 마이크로시스템즈의 Kohsuke Kawaguchi가 개발
- Java 기반, 플러그인 천국
- 쉽고 빠르게 전 세계로 확산
2011 — Hudson → Jenkins 분할
- Oracle의 Sun 인수 후 상표권 분쟁
- 커뮤니티 대다수가 **Jenkins**로 이동
- 10년 이상 CI의 사실상 표준
2014 — 클라우드 CI 1세대
- **Travis CI** — GitHub 네이티브, YAML 설정
- **CircleCI** — 병렬화, Docker 기반 혁신
- "Jenkins 안 깔아도 된다"는 각성
2019 — GitHub Actions
- GitHub이 직접 CI를 제공
- Marketplace 액션으로 재사용 가능
- 2025년 현재 **사실상 시장 1위**
2020s — GitOps와 공급망 보안
- **Argo CD** (2018), **Flux** (2019) — Pull 기반 배포
- **Sigstore** (2021), **SLSA** (2021)
- **SBOM** 의무화 (미국 행정명령 14028, 2021)
- **AI 기반 자동 수정** (Dependabot, Renovate, Copilot)
2. Pipeline as Code — 철학의 전환
GUI의 시대
- Jenkins Freestyle 프로젝트는 **UI 클릭**으로 설정
- 재현 불가, 버전 관리 불가, 리뷰 불가
Jenkinsfile (2016) — 첫 걸음
- Groovy DSL로 파이프라인 정의
- 리포지토리에 `Jenkinsfile` 커밋
- 여전히 복잡하고 Groovy 문법
YAML의 승리
- Travis → CircleCI → GitHub Actions 모두 YAML
- 선언적, 간결, 익숙함
- `.github/workflows/*.yml` 파일이 표준
예시 — GitHub Actions
name: CI
on:
push:
branches: [main]
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- run: pnpm test
재사용성 — Composite Action, Reusable Workflow
**Composite Action**: 여러 step을 하나로 묶음.
**Reusable Workflow**: 전체 워크플로우를 다른 워크플로우에서 호출.
호출
jobs:
build:
uses: myorg/shared/.github/workflows/node-build.yml@v1
with:
node-version: 20
조직 전체에 일관된 빌드 표준을 강제할 수 있음.
모범 사례
- **pin to SHA** — `actions/checkout@8e5e7e5` (버전보다 안전, Dependabot 관리)
- **Matrix build** — 여러 Node/Python 버전 동시 테스트
- **if conditions** — skip 로직
- **concurrency group** — 같은 PR에 여러 커밋 오면 이전 취소
3. 빌드 캐시 — CI 속도의 핵심
왜 캐시인가
- 빌드는 **결정론적** — 같은 입력 → 같은 출력
- 입력 해시로 출력을 조회하면 **재빌드 불필요**
로컬 캐시
- npm/pnpm의 `node_modules` 캐시
- Maven `~/.m2`, Gradle build cache
- `actions/cache@v4`로 CI 간에 공유
원격 빌드 캐시 — 모노레포의 구원
- **Turborepo** (Vercel) — 원격 캐시로 "다른 개발자가 이미 빌드한 것"을 재사용
- **Nx** (Nrwl) — 유사한 원격 캐시 + 분산 실행
- **Bazel** — Google 내부에서 시작, 가장 강력한 Hermetic build
Bazel의 Remote Execution
- 빌드 자체를 원격 클러스터에서
- 수천 코어 병렬
- Google이 모노레포에서 하루 20억 빌드하는 비결
예시 — Turborepo
{
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
},
"test": {
"dependsOn": ["build"]
}
}
}
입력 해시 = 파일 내용 + dependsOn 결과의 해시. 변경 없으면 **0초에 완료**.
4. Container 기반 빌드 — BuildKit과 Nixpacks
Dockerfile의 진화
- 초기: 각 `RUN`이 레이어, 순서가 중요
- 현재: **BuildKit** (2018+)이 기본
- 병렬 빌드
- 캐시 마운트 (`--mount=type=cache`)
- 시크릿 마운트
- 멀티 아키텍처
Multi-stage Build
FROM node:20 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:20-slim AS runtime
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
CMD ["node", "dist/index.js"]
최종 이미지에는 소스/빌드 도구 없음. **크기 10배 감소**.
BuildKit Cache Mount
RUN --mount=type=cache,target=/root/.npm \
npm ci
레이어 간에 캐시 유지 → 재빌드 속도 ↑
Nixpacks / Buildpacks
- **Cloud Native Buildpacks** — Dockerfile 없이 언어 감지 → 빌드
- **Nixpacks** (Railway) — Rust로 쓴 빠른 빌더
- 단순 앱에는 훌륭, 복잡하면 Dockerfile로
Distroless 이미지
- `gcr.io/distroless/nodejs20-debian12`
- Shell 없음, 공격면 최소
- 구글 표준, 보안 감사에 유리
5. Test 병렬화 — CI 시간 반으로
Sharding
- 테스트를 N개 조각으로 분할
- 각 조각을 독립 runner에서 실행
strategy:
matrix:
shard: [1, 2, 3, 4]
steps:
- run: npx jest --shard=${{ matrix.shard }}/4
지능적 할당
- 이전 실행 시간 기반으로 균등 분배 (Jest `--shard`, Nx 분산 실행)
- **hot shard** 문제 방지
Flaky Test 대응
- Flaky = 같은 코드인데 때때로 실패
- 원인: 타이밍, 공유 상태, 네트워크
- 전략:
- **재시도** (선택적, 남용 금지)
- **격리** (Flaky 목록 분리 실행)
- **뿌리 수정** — 가장 중요
E2E 병렬화
- Playwright `workers` 옵션
- 독립 DB 스키마, 독립 포트
- 상태 격리가 핵심
6. Artifact Management
OCI Registry — 이미지 저장소
- **GitHub Container Registry (GHCR)** — 무료, GitHub 연동
- **Docker Hub** — 역사적, 레이트 리밋
- **AWS ECR, Google Artifact Registry, Azure ACR** — 클라우드 네이티브
- **Harbor, Artifactory** — 엔터프라이즈
OCI는 이미지 이상을 저장한다
- Helm chart
- Python wheel (oci-python)
- Terraform module
- SBOM, provenance
**OCI = 콘텐츠 주소 가능한 저장소 프로토콜**이 되고 있음.
Semantic Tagging
v1.2.3 — 고정 버전
v1.2 — minor 고정
v1 — major 고정
latest — 최신 (prod에서 금지!)
sha-abc1234 — 커밋 기반 (추적 가능)
Pull-through Cache
- 사내 registry가 외부 registry를 캐시
- Docker Hub 레이트 리밋 회피
- 네트워크 지연 감소
7. Secrets Management — OIDC의 혁명
구시대 — 장기 자격증명
- `AWS_ACCESS_KEY_ID`를 CI secrets에 저장
- 유출되면 재앙
- 갱신 부담
OIDC 기반 — Keyless
- GitHub Actions가 자신을 증명하는 **OIDC 토큰** 발급
- AWS/GCP/Azure가 그 토큰을 신뢰 → **임시 자격증명** 발급
- 장기 비밀이 **아예 존재하지 않음**
예시 — AWS
permissions:
id-token: write
steps:
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123:role/GitHubActions
aws-region: us-east-1
Vault / External Secrets
- HashiCorp Vault, AWS Secrets Manager, GCP Secret Manager
- **External Secrets Operator** — K8s에서 자동 주입
- Secret rotation이 실제로 작동
SOPS — 암호화된 시크릿 git에 저장
- Mozilla SOPS (Secrets Operations)
- KMS/PGP로 암호화된 YAML/JSON
- GitOps 친화적
8. GitOps — Pull 모델의 우아함
Push vs Pull 배포
**Push (전통)**: CI가 kubectl apply로 직접 배포
- CI가 클러스터 자격 증명을 가져야 함 (보안 위험)
- 상태 drift 탐지 어려움
**Pull (GitOps)**: 클러스터가 git을 주기적으로 확인
- 자격 증명 CI에 노출 안됨
- **Git = 단일 진실 소스**
- drift 탐지/자동 복구
4원칙 (OpenGitOps)
1. **Declarative** — 원하는 상태를 선언
2. **Versioned & Immutable** — git에 기록
3. **Pulled Automatically** — 에이전트가 적용
4. **Continuously Reconciled** — 항상 감시
Argo CD
- CNCF Graduated
- 웹 UI가 강력 — 앱별 상태, diff, sync
- Application CRD로 선언
- Kustomize/Helm 네이티브 지원
- Multi-cluster 지원
Flux
- CNCF Graduated
- CLI 중심, Argo보다 가벼움
- GitOps Toolkit로 모듈화
예시 — Argo CD Application
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-app
spec:
source:
repoURL: https://github.com/org/infra
path: apps/my-app
targetRevision: main
destination:
server: https://kubernetes.default.svc
namespace: my-app
syncPolicy:
automated:
prune: true
selfHeal: true
Argo Rollouts — Progressive Delivery와 통합
앞선 글 "Feature Flag & Progressive Delivery"와 연결:
- Canary/Blue-Green을 Kubernetes 네이티브로
- Analysis Template로 자동 rollback
9. Supply Chain Security — 2020년 이후 대격변
SolarWinds 충격 (2020)
- 해커가 **빌드 시스템**에 침투
- 악성 코드를 정상 소프트웨어에 주입
- 18,000+ 고객에 배포
- **CI/CD 자체가 공격 대상**임을 드러냄
SLSA — Supply chain Levels for Software Artifacts
Google 주도, OpenSSF로 이관. 4단계 성숙도:
| 레벨 | 요구사항 |
|---|---|
| L0 | 요구사항 없음 |
| L1 | 빌드가 자동화 + 기본 provenance |
| L2 | 버전 관리 + provenance 생성 서명 |
| L3 | Hardened build (isolated, hermetic) |
| L4 | 2-party review + hermetic, reproducible |
대부분의 기업은 L2-L3이 현실적 목표.
Provenance — 무엇을, 어떻게 빌드했나
- 어떤 소스(git SHA)
- 어떤 빌더(GitHub Actions runner)
- 어떤 의존성
- 어떤 파라미터
- **in-toto attestation** 형식
Sigstore — "무료 서명 인프라"
- Linux Foundation 프로젝트
- **Cosign** — 이미지/아티팩트 서명
- **Fulcio** — keyless 인증서 발급 (OIDC 기반)
- **Rekor** — 투명성 로그
cosign sign --yes ghcr.io/org/image:v1.0.0
cosign verify --certificate-identity "..." ghcr.io/org/image:v1.0.0
키 관리 없이도 서명 가능 — **Keyless 서명**의 혁명.
SBOM — Software Bill of Materials
- 빌드 결과물에 포함된 **모든 의존성 목록**
- 포맷: **SPDX**, **CycloneDX**
- 미국 행정명령 14028 — 연방 정부 납품 필수
- EU CRA (Cyber Resilience Act) — 2025-2027 시행
도구
- **Syft** — SBOM 생성 (Anchore)
- **Grype** — CVE 스캔 (SBOM 기반)
- **Trivy** — 통합 스캐너 (Aqua)
- **Dependency-Track** — SBOM 집중 관리
Provenance 검증 in Kubernetes
- **Kyverno / OPA Gatekeeper** 정책으로 "서명되지 않은 이미지 거부"
- 클러스터 수준에서 enforce
10. 배포 전략 — 안전하게 푸는 기술
(상세는 앞선 "Feature Flag & Progressive Delivery" 글 참조, 여기서는 CI/CD 관점)
Rolling Update
- K8s 기본값, 점진적 pod 교체
- maxSurge/maxUnavailable 튜닝
- 간단하지만 rollback이 단순 이미지 교체
Blue/Green
- 두 환경 병행, LB 스위칭
- 빠른 rollback (LB만 전환)
- 비용 2배
Canary
- 1%, 5%, 25%, 100% 단계적 노출
- 지표 확인 + 자동 진행/회귀
- Argo Rollouts, Flagger 활용
Feature Flag
- 코드는 모두 배포, **플래그로 노출 제어**
- 가장 유연, 코드 복잡도 증가
11. Dev Loop Speed — "5분 철학"
구글 내부 원칙 중 하나: "커밋 → 배포 가능 상태까지 5분 이내."
기법
1. **Incremental build** — 변경된 것만
2. **Test selection** — 관련 테스트만 (Bazel, Nx)
3. **Distributed execution** — Remote execution
4. **Parallel pipeline** — 독립 job 동시 실행
5. **Warm runner** — self-hosted + 캐시 볼륨
6. **Pre-commit hook** — 로컬에서 lint/format
측정 — DORA Metrics
2014년 Google DORA 팀이 제시한 4대 지표:
1. **Deployment Frequency** — 배포 빈도
2. **Lead Time for Changes** — 커밋 → 프로덕션 시간
3. **Change Failure Rate** — 배포 실패율
4. **Time to Restore Service** — 장애 복구 시간
Elite 팀:
- 배포: 하루 여러 번
- Lead Time: 1시간 미만
- 실패율: 15% 미만
- 복구: 1시간 미만
12. 실전 예시 — 현대적 풀스택 파이프라인
name: Full CI/CD
on:
push:
branches: [main]
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
id-token: write
packages: write
attestations: write
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- run: pnpm lint
- run: pnpm test --coverage
- uses: codecov/codecov-action@v5
build:
needs: test
runs-on: ubuntu-latest
outputs:
digest: ${{ steps.build.outputs.digest }}
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- id: build
uses: docker/build-push-action@v6
with:
push: true
tags: ghcr.io/${{ github.repository }}:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
provenance: true
sbom: true
sign:
needs: build
runs-on: ubuntu-latest
steps:
- uses: sigstore/cosign-installer@v3
- run: |
cosign sign --yes \
ghcr.io/${{ github.repository }}@${{ needs.build.outputs.digest }}
deploy:
needs: sign
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
repository: ${{ github.repository_owner }}/infra-gitops
token: ${{ secrets.GITOPS_TOKEN }}
- name: Update image tag
run: |
yq -i '.image.tag = "${{ github.sha }}"'
apps/my-app/values.yaml
- run: |
git config user.name github-actions
git config user.email github-actions@github.com
git commit -am "deploy: ${{ github.sha }}"
git push
Argo CD가 이후 자동 sync
이 한 파일에:
- 테스트 + 커버리지
- 멀티 스테이지 빌드 + GHA 캐시
- Provenance + SBOM 자동 생성
- Keyless 서명 (Cosign + Fulcio)
- GitOps 푸시 → Argo CD가 배포
13. 안티패턴 TOP 10
1. **Latest 태그 배포** — 재현 불가, 디버그 지옥
2. **CI에 장기 자격 증명 저장** — OIDC 써라
3. **메인 브랜치 직접 빌드/배포** — Branch protection 필수
4. **Flaky test 재시도로 가림** — 근본 원인 수정
5. **`npm install` (vs `npm ci`)** — lockfile 존중 안 함
6. **CI에서 `sudo apt install`** — 빌드 환경 오염, 느림
7. **Secret을 로그에 echo** — GitHub이 일부 마스킹하지만 주의
8. **Pipeline 없이 개별 CI 스크립트** — 일관성 부재
9. **서명 없는 이미지 배포** — SolarWinds 재현 가능
10. **빌드 시간 30분 이상 방치** — 개발 속도 마비
14. 현대 CI/CD 체크리스트
- [ ] **Pipeline as Code** — YAML이 리포지토리에
- [ ] **Branch protection** + **required status checks**
- [ ] **Concurrency control** — PR 중복 실행 취소
- [ ] **Build cache** — actions/cache 또는 Turborepo/Nx
- [ ] **Multi-stage Docker build** — 작은 이미지
- [ ] **OIDC 인증** — 장기 key 제거
- [ ] **테스트 병렬화** — shard + 재시도 전략
- [ ] **SBOM + Provenance** 자동 생성
- [ ] **Cosign keyless signing**
- [ ] **Argo CD/Flux** — pull 기반 배포
- [ ] **DORA 메트릭** 대시보드
- [ ] **배포 rollback 15분 이내 가능** 훈련
마치며 — CI/CD는 문화다
CI/CD는 도구 선택이 아니라 **문화**다. GitHub Actions냐 Jenkins냐보다, "커밋하면 15분 안에 staging에 가는가?" "금요일 오후 6시에도 배포할 수 있는가?" "배포 실패 시 1시간 안에 복구되는가?"가 훨씬 중요하다.
공급망 보안의 시대에는 한 걸음 더 나아가야 한다. "내가 빌드한 이것이 **정확히 어떤 소스에서, 어떤 경로로** 나왔는가?"를 서명된 증거로 남기는 것. SolarWinds 이후, 이 질문에 답하지 못하는 조직은 규제/고객 감사에서 걸러진다.
> "Slow is smooth, and smooth is fast. But in CI/CD, fast is safe — because you catch problems before they compound." — Charity Majors (Honeycomb)
다음 글 예고 — 프론트엔드 번들러의 내전 — Webpack, Vite, esbuild, Turbopack, Rspack, Rolldown (2026)
CI/CD가 배포의 혈관이라면, **번들러**는 프론트엔드 빌드의 심장이다. 다음 글에서는:
- **번들링의 역사** — Browserify → Webpack의 독점 → JS 런타임 전쟁
- **Webpack** — 왜 승자였고, 왜 내려가는가
- **esbuild** — Go의 위력, 100배 빠른 빌드
- **Vite** — Evan You의 "개발은 ESM, 빌드는 Rollup"
- **Turbopack** — Vercel의 Rust 베팅
- **Rspack** — 바이트댄스의 Rust Webpack
- **Rolldown** — Vite의 다음 진화 (2025)
- **Bun bundler** — 번들 + 런타임 + 패키지 매니저 통합
- **Tree-shaking, Code splitting, HMR** — 기저 기술
- **Module Federation** — 마이크로 프론트엔드
- **ESM in production** — import maps의 부상
"빌드 시간 3초" 시대를 가능케 한 Rust/Go 혁명을 해부하는 여정.
> "The best CI/CD pipeline is the one you don't notice. It just works, every time, for everyone." — Kelsey Hightower
현재 단락 (1/381)
CI/CD는 지루해 보인다. "빌드 돌리고, 테스트 돌리고, 배포한다." 그러나 대형 팀과 소형 팀을 가르는 가장 큰 차이가 **CI/CD 파이프라인의 질**이라는 건 놀라운 사실...