WEBBOOK CHAPTER

CI/CD, 서버실에서 클라우드까지: 40장. 출간·출시 최종 판정

40장. 출간·출시 최종 판정

독자는 다음을 실제로 수행할 수 있어야 한다.

  • Jenkins, GitHub Actions, GitLab CI의 문법보다 공통 전달 계약을 설계한다.
  • build once와 artifact digest 승격을 지킨다.
  • runner를 신뢰 구역별로 분리하고 OIDC로 장기 cloud key를 줄인다.
  • SBOM·provenance·scan을 digest에 묶고 배포 때 검증한다.
  • 온프레미스 rolling, Kubernetes rollout, GitOps의 실패 차이를 설명한다.
  • canary SLO와 업무 invariant로 promote/rollback을 결정한다.
  • 비밀 노출, schema, controller 장애까지 복구 훈련한다.

자동 QA는 일관성 증거이지 운영 환경 적합성 보증이 아니다. 상업 출간 전 Jenkins/GitHub/GitLab 운영자, platform engineer, SRE, application security, 기술 교정, 법무·상표 담당자가 독립 감수해야 한다. 실제 cloud·registry·Kubernetes에서 clean-room 실습과 rollback 시간을 측정한다.

저작권·상표·정책

ParcelTrack, ReleaseDesk, 모든 예제·도해·실습 UI는 이 책을 위해 새로 만들었다. 공식 문서는 사실 확인과 추가 학습 링크로 사용하고 문장·제품 화면·로고를 복제하지 않았다. Jenkins, GitHub, GitLab, Kubernetes, Argo CD와 cloud 명칭은 각 권리자의 상표일 수 있으며 제휴·승인을 암시하지 않는다. 실제 credential, private URL, 고객 데이터는 포함하지 않는다.

GitHub Actions 예시는 저장소 code의 lint, test, build, security check, packaging, deployment에만 한정한다. 외부 사이트/API를 schedule·loop·backfill로 수집하거나 모니터링하는 workflow를 만들지 않는다. 그런 작업은 명시적으로 승인된 별도 운영 infrastructure에서 수행하고 CI는 준비된 integrity-checked artifact만 소비한다.

공식 자료 확인표

부록 A. 증상별 첫 10분 진단

CI가 시작되지 않는다. trigger/rules, branch protection, skipped condition, queue, runner label·tag, concurrency cancellation을 본다. YAML을 바꾸기 전에 생성된 pipeline graph와 event payload 범주를 확인한다.

CI는 성공했는데 artifact가 없다. build output 경로, working directory, ignore, artifact upload의 no-files policy, expiry를 본다. upload가 빈 경로에서도 성공하는 옵션을 금지한다.

staging과 production 동작이 다르다. 두 환경의 digest, config revision, feature flag, migration version, downstream endpoint를 비교한다. source branch 이름만 비교하지 않는다.

Kubernetes rollout이 멈췄다. rollout status, desired/current/available replica, pod event, readiness, image pull, resource quota, PDB를 본다. pod 하나를 무작정 지워 증거를 없애지 않는다.

Argo CD가 OutOfSync다. diff, ignored field, mutating webhook, HPA가 소유한 replica, Helm render, target revision을 본다. ignoreDifferences를 넓혀 실제 drift를 숨기지 않는다.

rollback했는데 회복되지 않는다. current digest와 traffic route, config, DB, cache, queue consumer, external API를 확인한다. 이전 code가 새 data를 읽지 못하는지 본다.

부록 B. Production 변경 요청서


RELEASE-____
서비스 / owner:
source SHA:
artifact digest:
SBOM / provenance verification:
config revision / migration:
test·security evidence:
변경 위험과 사용자 영향:
rollout 전략·단계:
promote threshold:
abort·rollback threshold:
previous digest:
DB·queue compatibility:
관측 dashboard/query:
승인자:
시작·종료·실제 결과:

빈칸을 “해당 없음”으로 닫을 때도 이유를 남긴다. 같은 양식이 온프레미스와 cloud에서 통하면 도구가 바뀌어도 운영 언어가 유지된다.

부록 C. 릴리스 성숙도 사다리

1단계는 수동 명령과 담당자 기억, 2단계는 versioned build/test, 3단계는 immutable artifact와 자동 staging, 4단계는 identity·supply-chain evidence와 progressive delivery, 5단계는 platform self-service와 지속적인 recovery drill이다. 높은 단계가 모든 팀의 목표는 아니다. 위험과 비용에 맞는 최소 충분 단계를 택한다.

도구를 새로 사기 전에 현재 단계에서 가장 큰 단절 하나를 고친다. source와 artifact가 연결되지 않으면 GitOps보다 digest manifest가 먼저다. rollback을 못 하면 배포 빈도를 올리기보다 schema compatibility와 artifact retention을 먼저 고친다. 좋은 CI/CD는 화려한 pipeline graph가 아니라 두려움 없이 작은 변경을 전달하고 실제로 되돌릴 수 있는 팀의 능력이다.

부록 D. Jenkins를 처음 맡은 운영자의 30일 점검

첫 주에는 변경하지 말고 inventory를 만든다. controller version, Java, plugin과 설치 이유, update site, agent label·executor, shared library reference, credential 종류와 owner, backup 위치, 외부 webhook, queue·build duration을 수집한다. credential 값은 출력하지 않는다. UI job과 SCM Jenkinsfile의 비율을 확인한다.


controller: version / JVM / storage / HA expectation
agents    : label / OS / executor / isolation / network
plugins   : version / owner / last update / replacement
secrets   : credential ID / scope / owner / rotation (값 제외)
backup    : content / encryption key / restore date / measured RTO

둘째 주에는 신뢰 경계를 고친다. controller executor를 0으로 두고 build를 agent로 보낸다. public/untrusted change와 protected build·deploy agent를 나눈다. 모든 agent가 production network와 Docker socket에 접근하지 않게 한다. orphan process와 workspace cleanup을 합성 job으로 시험한다.

셋째 주에는 pipeline 표준을 작게 만든다. checkout, test, build once, artifact fingerprint, staging, production input, post evidence를 가진 reference Jenkinsfile 하나를 만든다. 각 서비스가 자기 test command와 artifact path를 명시하게 하고, library가 숨겨서 재build하지 않게 한다.

넷째 주에는 restore와 upgrade를 연습한다. 별 controller에 backup을 복원하고 credential decrypt, job load, agent reconnect, plugin compatibility, in-flight pipeline 기대를 확인한다. upgrade 전후 reference pipeline을 실행하고 rollback 시간도 잰다. controller 재시작을 production deploy 중에 처음 시험하지 않는다.

부록 E. 같은 전달 계약을 세 플랫폼으로 번역한다

전달 개념 Jenkins GitHub Actions GitLab CI
pipeline file Jenkinsfile workflow YAML .gitlab-ci.yml
execution agent/node runner runner/executor
단계 stage job + needs stage/job + needs
output archive/fingerprint artifact/package artifact/package
protected target input + auth/plugin environment protected environment
shared logic Shared Library reusable workflow/action include/component
short cloud auth credential/OIDC integration OIDC token ID token/workload identity

문법 변환보다 semantic 차이를 시험한다. Jenkins input이 agent를 점유하는지, GitHub environment secret은 gate 전 접근 가능한지, GitLab manual job의 allow_failure가 pipeline 성공 의미를 어떻게 바꾸는지 실제 fixture로 확인한다.


공통 acceptance
1. pull/merge request는 production credential을 얻지 못한다.
2. test 실패 source는 artifact release repository에 들어가지 못한다.
3. staging과 production은 같은 digest를 쓴다.
4. production은 승인과 rollback digest를 기록한다.
5. pipeline tool 장애 중에도 현재 service는 계속 동작한다.

migration은 빅뱅으로 하지 않는다. 먼저 artifact manifest와 registry를 공통화하고 한 low-risk service를 양쪽에서 검증하되 한 시스템만 배포 권한을 갖게 한다. 결과 digest와 test evidence를 비교한 뒤 service별로 전환한다. 이전 pipeline의 credential과 webhook을 종료하고 remote 등록 상태까지 확인한다.

부록 F. Kubernetes Production Release Day 절차

배포 전: 현재 desired/live digest, available replica, 최근 incident, SLO error budget, PDB, node capacity, pending migration, on-call과 rollback owner를 확인한다. release manifest의 source·artifact·config가 모두 존재하는지 검증한다.


kubectl -n parceltrack get deploy parceltrack 
  -o jsonpath='{.spec.template.spec.containers[0].image}{"n"}'
kubectl -n parceltrack get pods -l app=parceltrack
kubectl -n parceltrack describe deploy parceltrack

명령은 문맥 확인 뒤 승인된 cluster에서 실행한다. kubectl config current-context와 namespace를 먼저 확인하고 production context는 시각적으로 구분한다. manifest diff에는 image digest 외 unexpected permission, ServiceAccount, volume, environment change가 없는지 본다.

배포 중: rollout status만 보지 않고 desired/updated/available replica, pod event, readiness, restart, error/latency와 business synthetic을 같은 timeline에서 본다. canary 단계마다 최소 요청 수와 관찰 시간을 채운다. 다음 단계로 가는 사람과 중단 권한자를 미리 정한다.


kubectl -n parceltrack rollout status deployment/parceltrack --timeout=10m
kubectl -n parceltrack get rs -l app=parceltrack
kubectl -n parceltrack get events --sort-by=.lastTimestamp

배포 후: 실제 pod image ID가 expected digest인지, old ReplicaSet이 scale down됐는지, migration invariant, queue lag, duplicate notification을 확인한다. release record에 실제 시작·종료·판정과 예외를 남긴다. “관찰 예정”을 완료로 표시하지 않는다.

rollback은 먼저 promotion을 멈추고 현재 GitOps controller와 수동 명령이 싸우지 않게 한다. environment commit revert 또는 progressive controller abort 중 한 소유 경로를 택한다. old digest를 적용한 뒤 traffic과 업무 invariant가 회복됐는지 확인하고 data incompatibility를 평가한다.

부록 G. 지표가 팀을 속이지 않게 측정한다

Deployment frequency는 production에 실제 사용자 가치를 전달한 횟수로 정의하고 pipeline 재실행을 세지 않는다. Lead time은 commit 생성 또는 merge에서 production까지 어떤 기준을 쓸지 고정한다. Change failure는 rollback, hotfix, incident를 포함하고, Recovery time은 탐지·선언·완화·완전 복구 중 조직 기준을 명시한다.


release_id, source_sha, artifact_digest
merged_at, deploy_started_at, deploy_finished_at
result: success | rollback | hotfix | aborted
incident_id, service_restored_at

팀 순위를 매기면 작은 commit 쪼개기, 실패 은폐 같은 gaming이 생긴다. 지표는 병목 탐색과 학습에 사용한다. service 위험, 근무 시간, batch와 online 특성을 함께 본다. 평균만 보고 긴 꼬리를 숨기지 않는다.

pipeline 자체 SLO도 둔다. main CI queue p95, first failure feedback, artifact availability, deploy control plane availability, false-positive gate, rollback drill success를 본다. CI 장애가 application 장애로 전파되지 않게 coupling을 줄인다.

부록 H. 공급망과 Credential 리뷰 카드

Source: protected branch/tag, signed commit 정책의 실제 목적, CODEOWNERS 우회, automation account, stale deploy key를 확인한다. secret이 active ref와 release artifact에 없는지 값 자체를 출력하지 않는 scanner로 확인한다.

Dependency: registry source, lockfile, checksum, typosquat, maintainer 변화, license, lifecycle script를 본다. 자동 update PR도 test와 review를 통과해야 한다. 긴급 CVE patch와 major feature upgrade를 한 번에 섞지 않는다.

Build: runner image와 action/plugin/library version, network egress, cache poisoning, untrusted input interpolation, Docker socket, artifact upload path를 본다. pull request title이나 branch name을 shell에 안전하지 않게 넣지 않는다.

Artifact: immutable digest, signature/attestation, SBOM, scan DB 시점, retention, replication, delete permission을 확인한다. verify 명령이 실제 deployment job에서 실행되는지 본다.

Deploy: environment protection, OIDC trust subject, least privilege, approval evidence, config revision, migration, canary threshold, rollback digest를 본다. break-glass account는 평소 비활성·강한 인증·감사·사용 후 rotation을 가진다.

Remote remediation: credential 노출이 의심되면 current tree cleanup, active branch/tag cleanup, historical exposure, provider-side revoke/rotate, account status를 별도 사실로 검증한다. local에서 지운 것만으로 “완료”라고 보고하지 않는다.

부록 I. Pipeline Code Review 질문 25

  1. trigger가 의도한 event와 ref만 포함하는가?
  2. fork·untrusted change가 privileged runner를 쓰는가?
  3. top-level과 job permission이 최소인가?
  4. third-party action/plugin/library reference가 검토됐는가?
  5. 사용자 입력이 shell에 안전하게 전달되는가?
  6. secret 값이 argument·environment dump·artifact에 남는가?
  7. test command 실패가 exit code로 전파되는가?
  8. empty test/artifact가 성공 처리되는가?
  9. cache key가 lockfile·OS·tool version을 포함하는가?
  10. cache miss에서도 build가 되는가?
  11. environment마다 rebuild하지 않는가?
  12. source SHA와 artifact digest가 연결되는가?
  13. SBOM·provenance가 digest에 묶이는가?
  14. scanner suppression에 owner와 만료가 있는가?
  15. production credential은 gate 뒤에만 얻는가?
  16. OIDC issuer·audience·subject가 제한되는가?
  17. concurrent production deploy가 직렬화되는가?
  18. staging evidence가 production 승격에 연결되는가?
  19. DB migration이 old/new app과 호환되는가?
  20. rollout timeout과 abort action이 있는가?
  21. 업무 invariant를 관측하는가?
  22. 이전 digest와 config revision이 보존되는가?
  23. rollback이 실제로 시험됐는가?
  24. 알림 실패가 deploy 결과를 왜곡하는가?
  25. pipeline 변경 자체의 rollback이 가능한가?

이 질문은 한 번의 release review가 아니라 template와 regression test로 옮긴다. 답이 불명확한 항목은 자동화보다 먼저 소유자와 정책을 정한다.