GitLab CI/CD · 설정 언어로서의 .gitlab-ci.yml · 이론
파이프라인을 파일로 쓰게 된 이유
한 줄 요약
.gitlab-ci.yml 은 빌드 서버 설정을 저장소 안으로 끌고 들어와, 코드와 같은 리뷰·같은 이력·같은 되돌리기를 받게 만든 파일이다.
왜 이게 필요했나
빌드 서버를 화면으로 설정하던 시절의 사고는 대개 한 문장으로 끝났다. "어제까지 되던 게 오늘 안 되는데 아무도 뭘 바꿨는지 모른다." 잡의 설정이 저장소 밖 데이터베이스에 있었기 때문이다. 그 설정에는 diff 가 없고, 리뷰가 없고, 브랜치가 없고, 되돌릴 커밋도 없다. 누가 체크박스 하나를 껐다는 사실은 사고가 난 뒤에야, 그것도 기억에 의존해서 밝혀진다.
더 깊은 문제는 브랜치가 없다는 쪽이었다. 코드는 브랜치마다 다른데 파이프라인은 하나뿐이니, 새 테스트 단계를 추가하려면 모든 브랜치의 빌드를 동시에 바꿔야 했다. 그래서 사람들은 파이프라인을 건드리지 않게 되고, 파이프라인은 몇 년 전 누군가가 만든 채로 굳는다. 굳은 파이프라인은 곧 아무도 믿지 않는 파이프라인이 된다.
설정을 저장소 안 파일로 옮기면 이 네 가지가 한꺼번에 해결된다. 브랜치마다 다른 파이프라인을 가질 수 있고, 병합 요청에서 파이프라인 변경 자체를 리뷰할 수 있고, 잘못됐을 때 커밋을 되돌리면 파이프라인도 함께 돌아오고, 무엇이 언제 왜 바뀌었는지가 Git 로그에 남는다.
어떻게 동작하나
GitLab 은 저장소 루트의 .gitlab-ci.yml 을 읽어 파이프라인 하나를 만든다. 여기서 중요한 것은 순서다. 커밋이 올라오면 GitLab 이 먼저 그 커밋에 있는 설정 파일을 읽고, 그 내용으로 어떤 잡을 만들지 결정한 뒤, 만들어진 잡을 러너에게 나눠 준다. 즉 설정은 잡이 시작되기 전에 이미 다 평가된다. 나중에 배울 rules 가 "이 잡을 실행할까"가 아니라 "이 잡을 만들까"를 정하는 것도 이 순서 때문이다.
파일의 문법은 YAML 매핑 하나다. 최상위 키 하나가 잡 하나이고, 예약된 이름 몇 개(stages, variables, default, include, workflow)만 잡이 아닌 전역 설정으로 쓰인다. 이 단순함이 장점이자 함정이다. 들여쓰기를 한 칸 잘못 넣으면 잡이 통째로 사라지는데, YAML 자체는 아무 오류도 내지 않는다. 파일은 멀쩡히 파싱되고, 다만 그 잡이 다른 키의 하위 항목이 되어 버린다.
이 코스에서 다루는 범위를 처음부터 분명히 해 둔다. 이 실습 환경에는 GitLab 서버도 GitLab Runner 도 없다. 그래서 파이프라인이 실제로 도는 장면은 볼 수 없고, 그런 척하는 가짜 러너도 띄우지 않는다. 대신 설정 언어와 그 실행 모델을 다룬다 — 어떤 잡이 만들어지는지, 어떤 순서로 출발하는지, 무엇이 무엇을 기다리는지. 도구를 설치하면 배울 수 있는 것은 도구뿐이지만, 실행 모델은 러너가 없어도 배울 수 있고 벤더가 바뀌어도 남는다.
현장에서 만나는 모습
설정을 파일로 옮긴 팀에서 제일 먼저 달라지는 것은 리뷰 대화다. "이 테스트를 왜 배포 뒤로 옮겼나요"라는 질문이 병합 요청 코멘트로 남고, 그 답도 함께 남는다. 반대로 아직 화면에서 설정하는 팀에서는 같은 질문이 채팅으로 오가고 사라진다.
또 하나 자주 보는 장면은 파이프라인 파일이 수백 줄로 불어난 뒤에야 include 와 템플릿을 찾기 시작하는 것이다. 파일이 저장소 안에 있으면 그 비대함이 눈에 보이기 때문에 리팩터링이 시작된다. 화면 안에 있으면 아무도 그 크기를 못 본다.
다음 글에서 볼 것
잡 하나가 실제로 무엇을 담고 있는지 키 단위로 뜯어본다. script 가 없으면 왜 잡이 아닌지, 이름 앞의 점 하나가 왜 그것을 실행 대상에서 빼는지, default 와 extends 가 어떻게 다른 방식으로 중복을 줄이는지까지 본다.