LabHub
배우기 러닝패스 코스

GitLab CI/CD

I inherited before_script, and one line disappeared

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

목표

include·extends·!reference·YAML 앵커·default·spec:inputs 로 설정의 중복을 걷어내고, 각각의 병합 규칙이 잡에 실제로 무엇을 남기는지 실행 로그와 합쳐진 설정으로 확인합니다.

왜 중요한가

파이프라인 파일이 커지면 공통 부분을 떼어 재사용하게 되는데, 재사용 장치마다 합치는 규칙이 다릅니다. 해시는 합쳐지고 배열은 바뀌며, 앵커는 파일 안에서만, default 는 아무도 정하지 않았을 때만 쓰입니다. 이 규칙을 모르면 '물려받았다고 믿은 준비 명령이 한 잡에서만 빠지는' 사고가 오류 없이 납니다. 입력을 받는 템플릿은 같은 잡을 환경마다 복사하지 않게 해 주지만, 허용 범위를 정해 두지 않으면 오타 난 환경 이름도 그대로 잡이 됩니다.

단계

  1. /root/glci-reuse 을 git 저장소로 만드세요. /root/glci-reuse/ci/templates.yml 에 숨김 잡 .base(before_script 는 echo base-setup 한 줄, 변수 LOG_LEVEL: info)를 두고, .gitlab-ci.ymlinclude: - local: ci/templates.yml 로 그 파일을 가져온 뒤 stages [build, test] 와 잡 build(stage build, extends: .base, script echo "LOG=$LOG_LEVEL")를 둡니다. gitlab-ci-local --shell-isolation --no-artifacts-to-source 로 실행해 build 로그에 base-setupLOG=info 가 찍히는지 보세요.
  2. 잡 둘을 더하세요. test(stage test)는 .base 를 extends 하면서 변수를 LOG_LEVEL: debug, PYTEST: "1" 로 적고 script echo "LOG=$LOG_LEVEL PYTEST=$PYTEST" 를 둡니다. lint(stage test)는 .base 를 extends 하면서 자기 before_script(echo lint-setup)와 script echo lint 를 둡니다. 실행해 test 로그에 base-setupLOG=debug PYTEST=1 이, lint 로그에는 lint-setup 만 있고 base-setup 은 없는지 확인하세요.
  3. package(stage build)를 더하세요. extends 를 쓰지 않고 before_script 를 !reference [.base, before_script]echo package-setup 두 항목으로 적어, 실행하면 base-setup 다음에 package-setup 이 찍혀야 합니다. script 는 echo package 입니다.
  4. /root/glci-reuse/broken-anchor.yml 에 ci/templates.yml 을 include 하고, 이 파일에 정의되지 않은 앵커 *base_vars 를 variables 에 <<: *base_vars 로 쓰는 잡 build 를 두세요. gitlab-ci-local --file broken-anchor.yml --list 의 출력을 /root/glci-reuse/anchor-error.txt 에 저장합니다. 그리고 .gitlab-ci.yml 에는 같은 파일 안에서 앵커 &docs_vars(DOCS_OUT: public)를 정의해 잡 docs(stage test)의 variables 에 <<: *docs_varsDOCS_FMT: html 을 두고 script echo "$DOCS_OUT/$DOCS_FMT"public/html 이 찍히게 하세요.
  5. .gitlab-ci.yml 최상위에 default: 로 before_script echo default-setup 을 두세요. 그리고 잡 report(stage test, script echo report)는 inherit: default: false 로 기본값을 물려받지 않게 합니다. 실행해 docs 로그에는 default-setup 이, report 로그에는 아무 준비 줄도 없고, build·test 로그에는 여전히 base-setup 이 찍히는지 확인하세요.
  6. /root/glci-reuse/ci/deploy.ymlspec:inputs 헤더가 있는 템플릿으로 만드세요. 입력 envstaging·production 중 하나만 허용하고, replicas 는 숫자이며 기본 1 입니다. 헤더 뒤(---)에는 잡 deploy-$[[ inputs.env ]](stage deploy)가 echo "deploy <env> replicas=<replicas>" 를 실행합니다. .gitlab-ci.yml 은 stages 에 deploy 를 더하고 이 파일을 두 번 include 합니다 — staging(기본 replicas), production(replicas 3).
  7. gitlab-ci-local --preview 의 출력을 /root/glci-reuse/expanded.yml 에 저장하세요. 이 파일에는 include·extends·!reference·앵커·default·inputs 가 모두 풀린 결과가 있어야 합니다. 채점기는 저장소 사본에서 같은 명령을 돌려 내용이 같은지와 몇 가지 값을 확인합니다.

참고

템플릿 파일을 include 하고 extends 로 물려받는다

/root/glci-reuse 을 git 저장소로 만드세요. /root/glci-reuse/ci/templates.yml 에 숨김 잡 .base(before_script 는 echo base-setup 한 줄, 변수 LOG_LEVEL: info)를 두고, .gitlab-ci.ymlinclude: - local: ci/templates.yml 로 그 파일을 가져온 뒤 stages [build, test] 와 잡 build(stage build, extends: .base, script echo "LOG=$LOG_LEVEL")를 둡니다. gitlab-ci-local --shell-isolation --no-artifacts-to-source 로 실행해 build 로그에 base-setupLOG=info 가 찍히는지 보세요.

include 는 여러 파일을 하나의 설정으로 합친 뒤 해석합니다. 이름이 점으로 시작하는 잡은 목록에 오르지 않고 물려주는 조각으로만 쓰입니다. gitlab-ci-local 은 git 이 추적하는 파일만 보므로 새 파일은 git add 합니다.

해시는 합쳐지고 배열은 통째로 바뀐다

잡 둘을 더하세요. test(stage test)는 .base 를 extends 하면서 변수를 LOG_LEVEL: debug, PYTEST: "1" 로 적고 script echo "LOG=$LOG_LEVEL PYTEST=$PYTEST" 를 둡니다. lint(stage test)는 .base 를 extends 하면서 자기 before_script(echo lint-setup)와 script echo lint 를 둡니다. 실행해 test 로그에 base-setupLOG=debug PYTEST=1 이, lint 로그에는 lint-setup 만 있고 base-setup 은 없는지 확인하세요.

extends 는 깊은 병합입니다. variables 같은 해시는 키 단위로 합쳐져 물려받은 키 위에 덮어쓰지만, before_script 같은 배열은 합쳐지지 않고 잡이 적은 것으로 통째로 바뀝니다.

배열을 합치고 싶을 때는 !reference

package(stage build)를 더하세요. extends 를 쓰지 않고 before_script 를 !reference [.base, before_script]echo package-setup 두 항목으로 적어, 실행하면 base-setup 다음에 package-setup 이 찍혀야 합니다. script 는 echo package 입니다.

!reference 는 다른 잡(숨김 잡 포함)의 특정 키 값을 그 자리에 끼워 넣는 태그입니다. include 한 파일의 조각도 가리킬 수 있어서, 배열이 통째로 바뀌는 extends 의 한계를 메웁니다.

앵커는 파일 경계를 넘지 못한다

/root/glci-reuse/broken-anchor.yml 에 ci/templates.yml 을 include 하고, 이 파일에 정의되지 않은 앵커 *base_vars 를 variables 에 <<: *base_vars 로 쓰는 잡 build 를 두세요. gitlab-ci-local --file broken-anchor.yml --list 의 출력을 /root/glci-reuse/anchor-error.txt 에 저장합니다. 그리고 .gitlab-ci.yml 에는 같은 파일 안에서 앵커 &docs_vars(DOCS_OUT: public)를 정의해 잡 docs(stage test)의 variables 에 <<: *docs_varsDOCS_FMT: html 을 두고 script echo "$DOCS_OUT/$DOCS_FMT"public/html 이 찍히게 하세요.

앵커와 별칭은 YAML 파서가 파일 하나를 읽을 때 처리합니다. include 는 그 뒤에 GitLab 이 합치는 단계라, 다른 파일의 앵커는 이미 사라진 뒤입니다. 파일을 넘는 재사용은 extends 나 !reference 로 합니다.

default 는 바닥값 — extends 와 inherit 에서 밀린다

.gitlab-ci.yml 최상위에 default: 로 before_script echo default-setup 을 두세요. 그리고 잡 report(stage test, script echo report)는 inherit: default: false 로 기본값을 물려받지 않게 합니다. 실행해 docs 로그에는 default-setup 이, report 로그에는 아무 준비 줄도 없고, build·test 로그에는 여전히 base-setup 이 찍히는지 확인하세요.

default 는 아무것도 정하지 않은 잡에 채워지는 전역 바닥값이라, extends 로 before_script 를 이미 받은 잡에서는 쓰이지 않습니다. inherit 는 기본값을 받을지 잡마다 끄는 스위치입니다.

입력을 받는 템플릿으로 환경별 잡을 찍어 낸다

/root/glci-reuse/ci/deploy.ymlspec:inputs 헤더가 있는 템플릿으로 만드세요. 입력 envstaging·production 중 하나만 허용하고, replicas 는 숫자이며 기본 1 입니다. 헤더 뒤(---)에는 잡 deploy-$[[ inputs.env ]](stage deploy)가 echo "deploy <env> replicas=<replicas>" 를 실행합니다. .gitlab-ci.yml 은 stages 에 deploy 를 더하고 이 파일을 두 번 include 합니다 — staging(기본 replicas), production(replicas 3).

spec 헤더는 include 할 때 넘길 수 있는 값의 형태와 허용 범위를 정합니다. 템플릿 안에서는 $[[ inputs.이름 ]] 으로 값을 씁니다. 허용 목록에 없는 값을 넘기면 파이프라인이 만들어지기 전에 거부됩니다.

GitLab 이 보게 될 합쳐진 설정을 뽑아 둔다

gitlab-ci-local --preview 의 출력을 /root/glci-reuse/expanded.yml 에 저장하세요. 이 파일에는 include·extends·!reference·앵커·default·inputs 가 모두 풀린 결과가 있어야 합니다. 채점기는 저장소 사본에서 같은 명령을 돌려 내용이 같은지와 몇 가지 값을 확인합니다.

설정이 여러 파일로 흩어질수록 '이 잡이 실제로 무엇을 실행하나' 를 파일만 보고 답하기 어렵습니다. 합쳐진 결과를 리뷰에 붙이면 병합 규칙을 머릿속으로 계산하지 않아도 됩니다. GitLab 화면의 파이프라인 편집기에도 같은 기능(전체 설정 보기)이 있습니다.