CI/CD 개발 지침
CI/CD에 특화된 개발 가이드는 여기에 나열되어 있습니다:
- 새로운 CI/CD 템플릿을 생성하는 경우, GitLab CI/CD 템플릿 개발 가이드를 읽어보세요.
- 새로운 키워드를 추가하거나 CI 스키마를 변경하는 경우, 다음 가이드를 참조하세요:
CI/CD YAML 참조 문서 가이드를 확인하여 CI/CD YAML 구문 참조 페이지를 업데이트하는 방법을 배워보세요.
CI/CD 사용 예시
우리는 ci-sample-projects
그룹을 유지하고 있으며, GitLab CI/CD의 다양한 사용 사례에 대한 .gitlab-ci.yml
의 예시를 보여주는 프로젝트를 보유하고 있습니다. 또한 다양한 시나리오에 사용될 수 있는 특정 구문을 다루고 있습니다.
CI 아키텍처 개요
다음은 CI 아키텍처의 간소화된 다이어그램입니다. 중요한 컴포넌트에 집중하기 위해 일부 세부 정보는 생략되었습니다.
다이어그램의 왼쪽에는 사용자 또는 자동화에 의해 트리거되는 다양한 이벤트에 기반한 파이프라인을 트리거하는 이벤트들이 있습니다:
-
git push
는 가장 흔한 파이프라인을 트리거하는 이벤트입니다. - Web API.
- UI에서 “파이프라인 실행” 버튼을 선택하는 사용자.
- Merge Request이 생성되거나 업데이트될 때.
- Merge Request이 Merge Train에 추가될 때.
- 예약된 파이프라인.
- 프로젝트가 상위 프로젝트에 구독되었을 때.
- Auto DevOps가 활성화될 때.
- GitHub 통합에서 외부 풀 리퀘스트가 사용될 때.
- 상위 파이프라인이 하위 파이프라인을 트리거하는 브릿지 작업을 포함할 때.
위의 이벤트 중 하나를 트리거하면, 해당 이벤트를 통합하기 위해 CreatePipelineService
가 호출되며, 사용자 및 이벤트 데이터를 입력으로 받아 파이프라인을 생성하려고 시도합니다.
CreatePipelineService
는 주로 파이프라인의 추상 데이터 구조(단계 및 모든 작업 포함)를 입력으로 받아 이를 반환하는 YAML Processor
컴포넌트에 의존합니다. 이 컴포넌트는 YAML 덩어리를 입력으로 받아 파이프라인의 추상 데이터 구조를 반환하고(단계 및 모든 작업 포함) 해당 YAML의 구조를 처리하면서 구문 또는 의미론적 오류를 반환합니다. YAML Processor
컴포넌트는 파이프라인을 구성하기 위해 사용할 수 있는 모든 키워드를 정의하는 곳입니다.
CreatePipelineService
는 YAML Processor
에서 반환된 추상 데이터 구조를 받아들인 후, 이를 지속 모델(파이프라인, 단계 및 작업과 같은)으로 변환합니다. 그런 다음, 파이프라인은 처리할 준비가 됩니다. 파이프라인을 처리하는 것은 실행 순서(단계 또는 DAG)에 따라 진행되며 다음 중 하나가 될 때까지 계속됩니다:
- 모든 예상 작업이 실행된 경우.
- 실패로 인해 파이프라인 실행이 중단된 경우.
파이프라인을 처리하는 컴포넌트는 ProcessPipelineService
이며, 파이프라인의 모든 작업을 완료 상태로 이동하는 책임이 있습니다. 파이프라인이 생성되면, 모든 작업은 초기 상태인 created
상태입니다. 이 서비스는 파이프라인 구조에 따라 처리 가능한 created
단계의 작업을 살펴보고, 그 다음으로 해당 작업을 pending
상태로 이동시킵니다. 이는 작업이 이제 러너(runner)에 의해 선택될 수 있다는 것을 의미합니다. 작업이 실행되면 성공적으로 완료되거나 실패할 수 있습니다. 파이프라인 내 작업에 대한 모든 상태 전환은 이 서비스를 다시 트리거하며, 전체 파이프라인의 상태를 업데이트합니다.
다이어그램의 오른쪽에는 GitLab 인스턴스에 연결된 러너(runner) 디렉터리이 있습니다. 이러한 러너(runner)는 공유 러너(runner), 그룹 러너(runner) 또는 프로젝트 러너(runner)일 수 있습니다. 러너와 Rails 서버 간의 통신은 Runner API Gateway
로 그룹화된 API 엔드포인트의 집합을 통해 발생합니다.
우리는 러너(runner)를 등록, 삭제 및 확인할 수 있으며, 이는 데이터베이스에 읽기/쓰기 쿼리를 발생시킵니다. 러너가 연결된 후에는 다음 작업을 실행하도록 요청하게 됩니다. 이는 RegisterJobService
를 호출하게 되며, 다음 작업을 선택하고 러너에 할당합니다. 이 시점에서 작업은 running
상태로 전환되며, 다시 한 번 상태 변경으로 인해 ProcessPipelineService
가 트리거됩니다.
더 자세한 내용은 작업 예약을 읽어보세요.
작업이 실행되는 동안 러너는 로그와 저장해야 할 가능한 아티팩트를 서버로 전송하며, 작업은 이전 작업의 아티팩트에 종속될 수 있습니다. 이 경우 러너는 전용 API 엔드포인트를 사용하여 아티팩트를 다운로드합니다.
아티팩트는 객체 리포지터리에 저장되며, 메타데이터는 데이터베이스에 유지됩니다. 아티팩트의 중요한 예는 Merge Request에서 구문 분석하고 렌더링되는 보고서(JUnit, SAST 및 DAST와 같은)입니다.
작업 상태 전환은 모두 자동화되는 것은 아닙니다. 사용자는 매뉴얼 작업 실행, 파이프라인 취소, 특정 실패한 작업 또는 전체 파이프라인을 다시 시도할 수 있습니다. 작업이 상태를 변경하는 모든 것은 전체 파이프라인의 상태를 추적하는 책임이 있는 ProcessPipelineService
를 트리거합니다.
브릿지 작업은 서버 측에서 실행되는 특수 유형의 작업으로, pending
상태로 전환될 때 실행됩니다. 이 작업은 다운스트림 파이프라인(다중 프로젝트 또는 자식 파이프라인과 같은)을 생성하는 책임이 있습니다. 다운스트림 파이프라인이 트리거될 때마다 워크플로 루프는 다시 CreatePipelineService
에서 시작됩니다.
CI 백엔드 아키텍처 워크스루를 시청할 수 있습니다.
작업 스케줄링
파이프라인이 생성되면 모든 작업이 모든 단계에 대해 한꺼번에 created
상태로 생성됩니다. 이렇게 하면 파이프라인의 전체 내용을 시각화할 수 있습니다.
created
상태의 작업은 아직 러너에 의해 볼 수 없습니다. 작업을 러너에 할당할 수 있게 하려면 먼저 작업을 pending
상태로 전이해야 합니다. 이는 다음과 같은 경우에 발생할 수 있습니다.
- 작업이 파이프라인의 맨 처음 단계에 생성된 경우.
- 작업이 매뉴얼으로 시작되어 트리거된 경우.
- 이전 단계의 모든 작업이 성공적으로 완료된 경우. 이 경우 다음 단계의 모든 작업을
pending
상태로 전이합니다. - 작업이
needs:
를 사용하여 DAG 의존성을 지정하고 모든 종속 작업이 완료된 경우. - 작업이
Ci::PipelineCreation::DropNotRunnableBuildsService
에 의해 실행불가능한 상태로 인해 취소되지 않은 경우 (링크:Ci::PipelineCreation::DropNotRunnableBuildsService
).
러너가 연결되면 서버를 계속 폴링하여 실행할 다음 pending
작업을 요청합니다.
lib/api/ci/runner.rb
에 정의되어 있습니다.서버가 요청을 받으면 Ci::RegisterJobService
알고리즘을 기반으로 다음 pending
작업을 선택하고 러너에 할당하여 작업을 보냅니다.
러너와 GitLab 서버 간 통신
러너가 등록 토큰을 사용하여 등록하면 서버는 실행할 수 있는 작업 유형을 알 수 있습니다. 이는 다음에 따라 달라집니다.
- 등록된 러너 유형:
- 공유 러너
- 그룹 러너
- 프로젝트 러너
- 연결된 태그.
러너는 실행할 작업을 요청하기 위해 POST /api/v4/jobs/request
로 요청을 시작합니다. 몇 초마다 폴링이 발생하지만, 작업 대기열이 변경되지 않는 경우 서버 측 작업 부하를 줄이기 위해 HTTP 헤더를 통해 캐싱을 활용합니다.
이 API 엔드포인트는 Ci::RegisterJobService
를 실행하며, 이는 다음을 수행합니다.
-
pending
작업 풀에서 다음으로 실행할 작업을 선택합니다. - 러너에게 할당합니다.
- API 응답을 통해 러너에게 제공합니다.
Ci::RegisterJobService
이 서비스가 사용하는 주요 3가지 최상위 쿼리가 있으며, 이는 러너가 등록된 수준에 따라 선택됩니다.
- 공유 러너에 대한 작업 선택 (인스턴스 수준)
- 빌드 수가 적은 프로젝트를 우선시하는 공평한 스케줄링 알고리즘을 활용합니다.
- 그룹 러너에 대한 작업 선택
- 프로젝트 러너에 대한 작업 선택
이 작업 디렉터리은 작업과 러너의 태그가 일치하는 경우에 따라 더 필터링됩니다.
마지막으로 러너가 선택할 수 있는 작업만 선택하도록 남은 pending
작업을 반복하고 추가 정책에 따라 첫 번째 작업을 할당하려고 시도합니다. 예를 들어, protected
로 표시된 러너는 보호된 브랜치(예: 프로덕션 배포)에서 실행되는 작업만 선택할 수 있습니다.
러너 풀 내에서 러너 수를 늘릴수록 동일한 작업이 다른 러너에 할당되어 충돌 가능성이 증가합니다. 이를 방지하기 위해 충돌 오류를 정중히 처리하고 디렉터리에서 다음 작업을 할당합니다.
실행되지 않은 빌드 취소
빌드를 “실행되지 않은”으로 표시하고 삭제하는 두 가지 방법이 있습니다.
- 빌드가 생성된 경우,
Ci::PipelineCreation::DropNotRunnableBuildsService
는 작업을 실행할 수 없게 만드는 알려진 조건을 사전에 확인합니다. - 실행 가능한 러너가 없는 경우, 1시간 후에
Ci::StuckBuilds::DropPendingService
에 의해 삭제됩니다.- 러너가 24시간 동안 작업을 선택하지 않으면 해당 시간 이후에 처리 대기열에서 자동으로 제거됩니다.
-
stuck
상태인 보류 중인 작업이 있는 경우, 처리할 수 있는 러너가 없는 경우, 해당 시간 이후에 대기열에서 제거됩니다. - 양쪽 경우 모두 작업 상태가 적절한 실패 사유와 함께
failed
로 변경됩니다.
이 차이의 이유
CI 분 할당량 메커니즘은 대부분의 경우에 일관된 결정이기 때문에 작업이 생성될 때 조기에 처리됩니다. 프로젝트가 한도를 초과하면 다음 달이 시작될 때까지 해당하는 모든 작업에 적용될 것입니다. 물론 프로젝트 소유자는 추가 분을 구입할 수 있지만, 이는 프로젝트가 매뉴얼으로 수행해야 하는 매뉴얼 작업입니다.
동일한 메커니즘은 곧 allowed_plans
에서 사용될 것입니다(링크: 곧).
프로젝트가 필요한 계획이 없고 러너가 해당 계획을 대상으로 실행되는 작업이 있다면, 프로젝트 소유자가 구성을 변경하거나 네임스페이스를 필요한 계획으로 업그레이드할 때까지 계속해서 실패합니다.
이 두 가지 메커니즘은 SaaS에 특화되어 있으며 동시에 SaaS의 규모를 고려할 때 상당히 계산 비용이 많이 듭니다. 작업을 보류함으로써 조기에 실패하도록 함으로써 매우 합리적입니다.
보류 중인 경우 다른 경우를 조기에 처리하지 않는 이유는 무엇인가요? 어떤 경우에는 작업이 보류 중인 경우에는 러너가 작업을 처리하는 데 시간이 걸리는 경우뿐입니다. 이는 GitLab 수준에서 알 수 없는 것입니다. 러너의 구성 및 용량, GitLab의 대기열 크기에 따라 작업이 즉시 실행될 수도 있고 대기해야 할 수도 있습니다.
다른 이유도 있을 수 있습니다.
- 러너 유지 관리를 수행하고 한동안 사용 불가능한 경우,
- 구성을 업데이트하고 실수로 태그 또는 보호 플래그를 엉망으로 만든 경우(또는 SaaS 인스턴스 러너의 경우 잘못된 비용 요소 또는
allowed_plans
구성을 할당한 경우).
이 모든 것은 일시적인 문제일 수 있으며 대개 예상되지 않으며 조기에 감지 및 수정될 것으로 예상됩니다. 이러한 조건이 발생하고 조치되지 않게 되는 것을 방지하기 위해 바로 작업을 삭제하고 싶지는 않습니다. 러너(runner)가 수용량을 초과했거나 일시적으로 사용할 수 없는 상태인 경우 작업을 바로 삭제하는 것은 사용자에게 매우 유해할 수 있습니다.
GitLab CI/CD에서 “Job”의 정의
GitLab CI 문맥에서의 “Job”은 지속적 통합, 전달 및 배포를 수행하는 작업을 의미합니다. 일반적으로 하나의 파이프라인에는 여러 단계가 포함되고, 각 단계에는 여러 개의 작업이 포함됩니다.
Active Record 모델링에서 “Job”은 CommitStatus
클래스로 정의됩니다.
또한, 다음과 같은 유형의 작업이 있습니다:
-
Ci::Build
… 실행 대상인 작업(runners)입니다. -
Ci::Bridge
… 하류 파이프라인을 트리거하는 작업입니다. -
GenericCommitStatus
… 외부 CI/CD 시스템, 예를 들어 Jenkins에서 실행되는 작업입니다.
코드베이스에서 “Job” 용어를 사용할 때, 독자들은 일반적으로 위에 언급된 유형의 클래스/객체라고 가정합니다.
만약 특별히 Ci::Build
클래스를 참조하는 경우, “job”으로 객체/클래스를 명명해서 혼동을 줄 수 있습니다. 문서에서는 “Build” 대신에 일반적으로 “Job”을 사용해야 합니다.
우리의 코드베이스에는 리팩토링이 필요한 몇 가지 일관성이 없는 부분이 있습니다.
예를 들어, CommitStatus
는 Ci::Job
로 변경되어야 하고, Ci::JobArtifact
는 Ci::BuildArtifact
로 변경되어야 합니다.
전체 리팩토링 계획에 대한 자세한 내용은 이 이슈를 참조하세요.
컴퓨트 할당량
- “CI/CD minutes”가 GitLab 16.1에서 “컴퓨트 할당량” 및 “컴퓨트 분”으로 이름이 변경되었습니다.
다음 다이어그램은 컴퓨트 할당량 기능과 해당 컴포넌트의 작동 방식을 보여줍니다.
이 기능에 대한 자세한 내용은 아래 비디오에서 확인할 수 있습니다.