GitLab CI/CD · 현장에서의 파이프라인 · 실습
스테이지 이름 오타 하나로 배포 잡이 조용히 사라졌다
목표
GitLab 설정 스키마로 검증하는 도구를 게이트로 감싸 팀 정책을 더하고, 커밋 전 훅·파이프라인 첫 잡·include 를 푼 저장소 전체 검증에 걸며, 병합 요청 리뷰용으로 두 커밋 사이 잡 목록의 변화를 뽑습니다.
왜 중요한가
파이프라인 설정의 오류는 대개 조용합니다. 스테이지 이름 오타나 들여쓰기 한 칸이 잡을 지우거나 파이프라인 생성을 막는데, 그 사실은 푸시한 뒤에야 드러납니다. 검증을 사람의 눈 대신 도구에 맡기되, 도구가 무엇을 잡고 무엇을 놓치는지 알아야 그 틈을 정책으로 메울 수 있습니다. 같은 검사를 커밋 전과 파이프라인 첫 잡 두 곳에 두는 이유는 로컬 훅은 건너뛸 수 있기 때문이고, 리뷰에서는 텍스트 diff 보다 '어떤 잡이 생기고 사라지고 자동이 되는가' 가 더 중요한 정보입니다.
단계
1. /root/glci-gate 을 git 저장소로 만들고(.gitignore 에 .gitlab-ci-local/) /root/glci-gate/ci-validate.sh <설정파일> 을 만드세요. 그 파일 하나를 임시 git 저장소의 .gitlab-ci.yml 로 넣고 gitlab-ci-local --list 로 검증해, 거부되면 INVALID <이유>(도구 출력에서 의미 있는 첫 줄)와 1, 통과하면 VALID 와 0 으로 끝냅니다. .gitlab-ci.yml 에는 아래 files 의 정상 설정을 두고 커밋하세요. 채점기는 없는 스테이지·허용 밖 when·없는 needs 대상·YAML 문법 오류 표본으로 확인합니다.
2. ci-validate.sh 가 도구의 검증을 통과한 파일에 팀 정책 두 가지를 더 적용하게 하세요. 한 잡에 rules 와 only/except 가 함께 있으면 POLICY <잡> rules-with-only-except, artifacts 에 paths 가 있는데 expire_in 이 없으면 POLICY <잡> artifacts-without-expire_in 을 잡 이름 순으로 한 줄씩 출력하고 2 로 끝냅니다. 정책을 모두 지키면 VALID 와 0 입니다. 숨김 잡(점으로 시작)과 예약 키(stages·variables·default·include·workflow 등)는 잡이 아닙니다.
3. /root/glci-gate/hooks/pre-commit 을 만들어 커밋하고, 같은 파일을 .git/hooks/pre-commit 으로 복사해 실행 권한을 주세요. 훅은 스테이징된 .gitlab-ci.yml 이 있을 때만 그 스테이징된 내용(git show :.gitlab-ci.yml)을 ci-validate.sh 로 검증해, 통과하지 않으면 이유를 표준 오류로 내고 커밋을 막습니다. 채점기는 사본에서 틀린 설정과 정책 위반 설정을 커밋해 보고, 설정과 무관한 파일 커밋은 막지 않는지도 봅니다.
4. /root/glci-gate/broken.yml 에 아래 files 의 틀린 설정을 그대로 두고, ci-validate.sh 로 한 번에 하나씩 드러나는 문제를 고쳐 /root/glci-gate/fixed.yml 을 만드세요. 잡 이름(build·unit·deploy)과 각 잡의 script 는 바꾸지 않습니다. fixed.yml 은 VALID 여야 하고, unit 은 build 를 기다려야 하며, deploy 는 main 에서 수동 승인(allow_failure false)으로 만들어져야 합니다. 두 파일 모두 커밋합니다(훅은 .gitlab-ci.yml 만 봅니다).
5. .gitlab-ci.yml 에 잡 ci-lint(stage .pre, bash ci-validate.sh .gitlab-ci.yml)를 더해 커밋하세요. 실행하면 ci-lint 가 VALID 로 통과하고 나머지 잡이 돌아야 합니다. 채점기는 사본에서 정책 위반(만료 없는 산출물)을 넣은 설정으로 돌려 ci-lint 가 실패하고 build 가 시작하지 않는지 봅니다.
6. /root/glci-gate/ci-validate-repo.sh <저장소> 를 만드세요. 저장소를 임시 사본으로 떠(훅은 빼고) 커밋한 뒤 gitlab-ci-local --preview 로 include 가 풀린 합쳐진 설정을 얻고, 실패하면 INVALID <이유> 와 1, 성공하면 합쳐진 설정을 ci-validate.sh 에 넘겨 그 결과(VALID 0·POLICY 2)를 그대로 냅니다. 이 저장소에 돌리면 VALID 여야 합니다. 채점기는 include 한 파일 쪽에 오류·정책 위반을 넣은 사본으로 확인합니다.
7. /root/glci-gate/ci-diff.sh <저장소> <옛커밋> <새커밋> [브랜치] 를 만드세요. 두 커밋을 각각 임시 작업 트리로 꺼내, 브랜치(기본 main)의 파이프라인으로 계산하도록 --variable CI_COMMIT_BRANCH=<브랜치> 를 준 gitlab-ci-local --list-csv-all 로 잡 이름과 when 을 얻고, 잡 이름 순으로 ADDED <잡>, REMOVED <잡>, CHANGED <잡> <옛when>-><새when> 을 출력합니다. 원래 저장소의 작업 트리와 브랜치는 건드리지 않고 임시 작업 트리는 지웁니다. 채점기는 사본에서 잡을 더하고 빼고 when 을 바꾼 커밋을 만들어 확인합니다.
참고
- 이 VM 에는 GitLab 서버와 러너가 없고, gitlab-ci-local 4.75.1 이 .gitlab-ci.yml 을 GitLab 과 같은 규칙으로 해석해 shell 로 잡을 실행합니다.
image:를 적으면 도커로 돌리려 하므로 쓰지 않습니다. 보호 변수·마스킹·CI_JOB_TOKEN·러너 태그·병합 요청 파이프라인 생성은 서버 기능이라 여기서 재현되지 않습니다. - 실행: 저장소 루트에서
gitlab-ci-local --shell-isolation --no-artifacts-to-source(잡마다 따로 된 작업 디렉터리, 산출물을 저장소에 되쓰지 않음), 잡 목록:gitlab-ci-local --list-csv-all, 해석된 설정:gitlab-ci-local --preview. gitlab-ci-local 은 git 이 추적하는 파일만 잡에 넘기므로 파일을 만들면git add하세요. 채점기는 저장소를 사본으로 떠 모든 파일을 커밋한 뒤 같은 도구로 다시 돌립니다. - gitlab-ci-local 4.75.1 의 검증(실측): when 허용 값·없는 스테이지·없는 needs·없는 extends·YAML 문법·script 없음은 거부하고, rules 와 only 혼용·만료 없는 산출물은 통과시킵니다. GitLab 의 CI Lint(프로젝트의 파이프라인 편집기·Lint API)는 서버 기능이라 여기서 부를 수 없습니다.
- 훅이 커밋을 막는지 시험할 때는 사본 저장소에서 하세요. 원래 저장소에서 막힌 커밋은 스테이징만 남습니다.
- [Validate GitLab CI/CD configuration(CI Lint)](https://docs.gitlab.com/ci/yaml/lint/) · [CI Lint API](https://docs.gitlab.com/api/lint/) · [CI/CD YAML syntax reference](https://docs.gitlab.com/ci/yaml/) · [GitLab 설정 검증 소스(processable.rb)](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/config/entry/processable.rb) · [gitlab-ci-local](https://github.com/firecow/gitlab-ci-local)
단계 7개
- 스키마와 참조를 도구에게 검증시킨다
- 도구가 통과시키는 것을 팀 정책으로 잡는다
- 틀린 설정은 커밋부터 막는다
- 네 군데 틀린 설정을 고친다
- 파이프라인의 첫 잡이 자기 설정을 검증한다
- include 한 파일까지 합쳐서 검증한다
- 리뷰어에게 파이프라인이 어떻게 바뀌는지 보여 준다