LabHub
学习 学习路径 课程

GitLab CI/CD

一个慢 e2e 让文档发布白等了 8 秒

在 LabHub 中继续学习

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

목표

느린 잡이 섞인 파이프라인을 실제로 돌려 스테이지와 needs 가 잡의 출발 시각을 어떻게 바꾸는지 재고, 산출물 전달·조건부 잡 기다리기·실패 허용 방식을 실행 결과로 확인합니다.

왜 중요한가

스테이지 방식은 이해하기 쉽지만 가장 느린 잡이 모든 뒤 잡의 출발을 붙잡습니다. needs 로 필요한 것만 기다리게 하면 파이프라인이 빨라지는 대신, 무엇을 기다리고 무엇을 받는지를 잡마다 정확히 적어야 합니다. rules 로 빠질 수 있는 잡을 기다리면 파이프라인이 아예 안 만들어지고, 실패를 너무 넓게 허용하면 진짜 사고가 초록색으로 지나갑니다. 이 선택들은 문서를 읽을 때보다 시각과 결과를 볼 때 분명해집니다.

단계

  1. /root/glci-dag 을 git 저장소로 만들고 .gitlab-ci.yml 에 stages [build, test, deploy] 와 잡 넷을 두세요: compile(build, sleep 3bin/app 파일을 만들고 artifacts 로 bin/ 을 올림), unit(test, test -f bin/appecho unit-ok), e2e(test, sleep 8echo e2e-ok), publish-docs(deploy, test -f bin/appecho docs-published). gitlab-ci-local --shell-isolation --no-artifacts-to-source --timestamps 로 실행해 publish-docs 가 느린 e2e 가 끝난 뒤에야 시작하는 것을 보세요.
  2. publish-docsneeds: [compile] 을 더하세요. 실행하면 publish-docs 가 e2e 가 끝나기 전에 시작하고, compile 의 산출물(bin/app)은 여전히 받아야 합니다.
  3. lint(stage test, sleep 1echo lint-ok)를 needs: [] 로 더하세요. 실행하면 lint 가 compile 이 끝나기 전에 시작해야 합니다.
  4. audit(stage test)를 needs 긴 형태로 job: compile, artifacts: false 로 두고 script 를 test ! -e bin/app && echo no-artifact 로 하세요. 실행하면 audit 는 성공하고(bin/app 이 없음), unit 은 여전히 bin/app 을 받아야 합니다.
  5. integration(stage test)은 $RUN_INTEGRATION == "yes" 일 때만 만들어지게 rules 를 두고 sleep 2echo integration-ok 를 실행합니다. 잡 release(stage deploy)는 needs 에 unitjob: integration, optional: true 를 두고 echo release 를 실행합니다. 변수 없이 실행하면 integration 없이 release 가 돌고, --variable RUN_INTEGRATION=yes 로 실행하면 integration 이 끝난 뒤 release 가 시작해야 합니다.
  6. 잡 셋을 더하세요. flaky(test)는 echo flaky-runexit 1 하지만 allow_failure: true 입니다. cleanup(deploy)은 when: alwaysecho cleanup, notify-failure(deploy)는 when: on_failureecho notify 를 실행합니다. 실행하면 flaky 는 경고로 끝나고 파이프라인은 성공하며 cleanup 은 돌고 notify-failure 는 돌지 않아야 합니다. 채점기는 사본에서 flaky 의 allow_failure 를 끈 뒤에도 돌려 봅니다.
  7. check-config 를 stage .pre 에 더하세요(stages 목록에는 적지 않음). sleep 2test ! -e STOP 으로 저장소에 STOP 파일이 있으면 실패하고, 없으면 echo config-ok 를 실행합니다. 실행하면 check-config 가 끝난 뒤에 compile 이 시작해야 합니다. 채점기는 사본에 STOP 파일을 넣어, 사전 검사가 실패하면 compile 이 아예 돌지 않는지도 확인합니다.

참고

스테이지는 앞 스테이지 전부를 기다린다

/root/glci-dag 을 git 저장소로 만들고 .gitlab-ci.yml 에 stages [build, test, deploy] 와 잡 넷을 두세요: compile(build, sleep 3bin/app 파일을 만들고 artifacts 로 bin/ 을 올림), unit(test, test -f bin/appecho unit-ok), e2e(test, sleep 8echo e2e-ok), publish-docs(deploy, test -f bin/appecho docs-published). gitlab-ci-local --shell-isolation --no-artifacts-to-source --timestamps 로 실행해 publish-docs 가 느린 e2e 가 끝난 뒤에야 시작하는 것을 보세요.

needs 가 없는 잡은 앞 스테이지의 모든 잡이 끝나야 출발하고, 앞 스테이지들의 산출물을 모두 받습니다. --timestamps 를 붙이면 각 줄 앞에 시각이 찍혀 시작(starting shell)과 끝(finished in)을 비교할 수 있습니다.

문서 게시는 컴파일만 기다리면 된다

publish-docsneeds: [compile] 을 더하세요. 실행하면 publish-docs 가 e2e 가 끝나기 전에 시작하고, compile 의 산출물(bin/app)은 여전히 받아야 합니다.

needs 는 기다릴 잡을 직접 지정합니다. 적은 잡의 산출물만 받고, 스테이지 순서는 더 이상 출발 시각을 정하지 않습니다. 한 번 needs 를 쓰면 적지 않은 잡은 기다리지도, 산출물을 받지도 않습니다.

needs: [] 는 파이프라인이 시작하자마자 출발한다

lint(stage test, sleep 1echo lint-ok)를 needs: [] 로 더하세요. 실행하면 lint 가 compile 이 끝나기 전에 시작해야 합니다.

빈 needs 는 '아무도 기다리지 않는다' 입니다. 스테이지가 test 여도 build 가 끝나기를 기다리지 않습니다. 소스만 보면 되는 검사를 이렇게 앞당기면 실패를 몇 분 일찍 알 수 있습니다.

순서는 기다리되 산출물은 받지 않는다

audit(stage test)를 needs 긴 형태로 job: compile, artifacts: false 로 두고 script 를 test ! -e bin/app && echo no-artifact 로 하세요. 실행하면 audit 는 성공하고(bin/app 이 없음), unit 은 여전히 bin/app 을 받아야 합니다.

needs 의 긴 형태는 잡마다 산출물을 받을지 끌 수 있습니다. 순서만 필요하고 파일은 필요 없는 잡이 큰 산출물을 내려받느라 느려지는 것을 막습니다.

있을 수도 없을 수도 있는 잡을 기다린다

integration(stage test)은 $RUN_INTEGRATION == "yes" 일 때만 만들어지게 rules 를 두고 sleep 2echo integration-ok 를 실행합니다. 잡 release(stage deploy)는 needs 에 unitjob: integration, optional: true 를 두고 echo release 를 실행합니다. 변수 없이 실행하면 integration 없이 release 가 돌고, --variable RUN_INTEGRATION=yes 로 실행하면 integration 이 끝난 뒤 release 가 시작해야 합니다.

rules 로 빠질 수 있는 잡을 그냥 needs 에 적으면 GitLab 은 그 잡이 없을 때 파이프라인 자체를 만들지 않습니다. optional: true 는 '있으면 기다리고, 없으면 넘어간다' 는 뜻입니다.

실패를 허용할 잡, 실패해도 도는 잡, 실패해야 도는 잡

잡 셋을 더하세요. flaky(test)는 echo flaky-runexit 1 하지만 allow_failure: true 입니다. cleanup(deploy)은 when: alwaysecho cleanup, notify-failure(deploy)는 when: on_failureecho notify 를 실행합니다. 실행하면 flaky 는 경고로 끝나고 파이프라인은 성공하며 cleanup 은 돌고 notify-failure 는 돌지 않아야 합니다. 채점기는 사본에서 flaky 의 allow_failure 를 끈 뒤에도 돌려 봅니다.

allow_failure 는 실패를 '경고' 로 바꿔 뒤 스테이지를 막지 않게 합니다. when: on_failure 는 앞에서 실패한 잡이 있을 때만, always 는 결과와 관계없이 돕니다. 허용된 실패는 on_failure 를 부르지 않습니다.

모든 스테이지보다 먼저 도는 사전 검사

check-config 를 stage .pre 에 더하세요(stages 목록에는 적지 않음). sleep 2test ! -e STOP 으로 저장소에 STOP 파일이 있으면 실패하고, 없으면 echo config-ok 를 실행합니다. 실행하면 check-config 가 끝난 뒤에 compile 이 시작해야 합니다. 채점기는 사본에 STOP 파일을 넣어, 사전 검사가 실패하면 compile 이 아예 돌지 않는지도 확인합니다.

.pre 는 stages 에 적지 않아도 늘 맨 앞에 있는 예약 스테이지입니다(맨 뒤에는 .post). 스테이지 목록을 건드리지 않고 파이프라인 전체의 사전 검사를 붙일 때 씁니다. 앞 스테이지가 실패하면 뒤 스테이지의 보통 잡은 만들어지기만 하고 돌지 않습니다.