LabHub

GitLab CI/CD · 실행 모델 — 순서·조건·전달 · 실습

설정으로 파이프라인의 실행 모델 만들기

LabHub 에서 이어서 보기

목표

.gitlab-ci.yml 한 장을 여덟 단계에 걸쳐 쌓아 올리면서 GitLab CI 의 실행 모델 — 스테이지 순서, needs 가 만드는 DAG, rules 가 가르는 생성 여부, artifactscache 의 차이 — 을 손으로 만든다. 마지막에는 그 모델을 계산하는 해석기를 직접 짠다.

왜 중요한가

이 파드에는 GitLab 서버도 GitLab Runner 도 없다. 그래서 이 실습은 파이프라인이 실제로 도는 흉내를 내지 않는다. 대신 러너가 없어도 정직하게 배울 수 있는 것만 다룬다 — 설정 언어와 그 실행 모델이다. 실무에서 파이프라인 때문에 막히는 순간은 대부분 명령을 몰라서가 아니라, 왜 이 잡이 안 만들어졌는지 왜 저 잡이 아직 기다리는지를 설명하지 못해서다. 그 답은 전부 이 파일의 구조에서 나온다. 채점도 파일을 눈으로 훑지 않고 YAML 파서로 읽어 구조를 확인한다 — 들여쓰기가 한 칸 어긋나 잡이 엉뚱한 키 아래 매달린 설정은 사람 눈에는 멀쩡해 보이고 실제로는 돌지 않기 때문이다.

단계

1. /root/glci/.gitlab-ci.yml 을 만든다. 최상위 stagesbuild, test, deploy 를 이 순서로 두고, build-app 잡을 stage: build 로 두어 script 를 하나 이상 적는다.
2. 잡을 더 붙여 세 스테이지를 모두 채운다. lintunit-teststage: test, deploy-stagingstage: deploy 다. 모든 잡에 script 가 있어야 하고, 모든 stage 값은 stages 목록 안에 있어야 한다.
3. .python-base 라는 숨김 잡을 만들어 imagebefore_script 를 넣고, build-app·lint·unit-test 세 잡이 extends: .python-base 로 물려받게 한다. 물려받은 잡은 image 를 다시 적지 않는다. 최상위 default 에 기본 image 를 둔다.
4. needs 를 붙여 DAG 를 만든다. lintneeds: [], unit-testneedsbuild-app, deploy-stagingneedsunit-test 를 적는다. build-app 에는 needs 를 두지 않는다.
5. deploy-stagingrules 를 붙여 $CI_COMMIT_BRANCH == "main" 일 때 when: on_success 로 두고, 마지막 항목은 조건 없는 when: never 로 닫는다. deploy-prod 잡을 stage: deploy 로 새로 만들어 needsdeploy-staging 을 적고, 같은 브랜치 조건에서 when: manual · allow_failure: false 로 둔 뒤 역시 조건 없는 when: never 로 닫는다. onlyexcept 는 쓰지 않는다.
6. build-appartifacts 를 붙여 pathsexpire_in 을 적는다. unit-testneeds 를 긴 형태로 바꿔 job: build-app · artifacts: true 로, deploy-stagingneedsartifacts: false 로 둔다.
7. /root/glci/requirements.txt 를 내용 있는 파일로 만든다. build-appunit-testcache 를 붙이되 keyfilesrequirements.txt 를 가리키게 한다. build-apppolicy: pull-push, unit-testpolicy: pull 이다. cache.pathsartifacts.paths 와 겹치지 않게 한다.
8. /root/glci/plan.py 를 만든다. 인자로 받은 설정 파일을 읽어 {"stages": [...], "waves": [[...], ...]} 를 표준출력에 JSON 으로 낸다. needs 가 있으면 그 목록만, 없으면 앞 스테이지의 모든 잡을 기다린다(기본 stagetest). 점으로 시작하는 키와 예약 키(stages·variables·default·include·workflow)는 잡이 아니다. 각 웨이브는 이름순으로 정렬한다. 아무 잡도 출발할 수 없으면 cycle 이라는 말을 출력하고 0 이 아닌 종료 코드로 끝낸다. 마지막으로 자기 설정에 대해 돌린 출력을 /root/glci/plan.json 에 저장한다.

참고

단계 8개

  1. 스테이지 순서와 첫 잡
  2. 세 스테이지를 모두 채우기
  3. 숨김 잡과 extends 로 중복 걷어내기
  4. needs 로 스테이지 벽 넘기
  5. rules 로 브랜치 조건과 수동 승인 걸기
  6. artifacts 와 받을 사람 정하기
  7. cache 키를 잠금 파일 해시로 잡기
  8. 파이프라인 해석기로 실행 순서 계산하기