CI 구성 성능

중단 가능한 파이프라인

기본적으로 모든 작업은 중단 가능합니다, 단, main에서 자동으로 실행되는 dont-interrupt-me 작업은 매뉴얼으로 실행되며, 새 커밋을 머지 요청에 푸시해도 실행을 마무리하려면 dont-interrupt-me 작업을 시작해야 합니다.

Git 페치 캐싱

GitLab.com은 팩 오브젝트 캐시를 사용하므로, 동시에 진행되는 동일한 파이프라인 ref의 Git 페치는 Gitaly 서버에서 중복 처리되고 (항상) 캐시에서 제공됩니다(사용 가능한 경우).

다음과 같은 이유로 잘 작동합니다:

  • 팩 오브젝트 캐시는 GitLab.com의 모든 Gitaly 서버에서 활성화됩니다.
  • CI/CD의 Git 전략 설정Git 클론으로 설정되어 모든 작업이 동일한 데이터를 가져오도록 하여 캐시 히트 비율을 극대화합니다.
  • 얕은 클론을 사용하여 각 작업에 대해 전체 Git 히스토리를 다운로드하지 않도록 합니다.

Gitaly에서 클론/페치하는 대신 아티팩트를 통해 리포지터리 가져오기

최근 Gitaly에서 다음과 같은 오류를 발견했습니다: (자세한 내용은 이슈를 참조하세요)

fatal: remote error: GitLab가 현재 부하로 인해 이 요청을 처리할 수 없습니다.

GitLab.com이 팩 오브젝트 캐시를 사용하더라도 때로는 여전히 Gitaly가 처리하기에 부하가 너무 심하고, 번개 떼가 그 시간에 리포지터리를 여러 개 클론하는 많은 작업이 문제가 될 수 있습니다.

Gitaly의 부하를 완화하고 줄이기 위해 몇 가지 작업을 변경하여 리포지터리를 한꺼번에 모두 Gitaly에서 클론하는 대신 작업 중 하나에서 아티팩트로부터 리포지터리를 가져오도록 했습니다.

현재 대부분의 파이프라인에서 가장 많은 동시 작업을 하는 RSpec 작업에 적용되었으며, 이로써 속도도 약간 향상되었으며 아티팩트로 가져오는 것이 클론하는 것보다 어느 정도 더 빨랐습니다. 이로써 각 파이프라인에 대해 더 많은 아티팩트를 저장하는 비용이 듭니다.

2023-12-20의 숫자를 기반으로 RSpec 작업용 아티팩트 리포지터리에서 가져오기에서, 각 파이프라인에 대한 추가 리포지터리 비용은 약 280M이었으며, 각 RSpec 작업에 대해 15초를 절약했습니다.

이를 사용하지 않는 작업에 대해서는 적용하지 않았는데, 그 이유는 다른 작업에 의존성이 없는 작업의 시작을 지연시키고 싶지 않았기 때문입니다.

이 동작은 변수 CI_FETCH_REPO_GIT_STRATEGY로 제어할 수 있습니다:

  • none으로 설정하면 .repo-from-artifacts을 사용하는 작업은 작업 clone-gitlab-repo에서 아티팩트로부터 리포지터리를 가져와야 합니다.
  • clone으로 설정하면 .repo-from-artifacts을 사용하는 작업은 보통대로 리포지터리를 클론해야 합니다. 이 경우 작업 clone-gitlab-repo는 실행되지 않습니다.

비활성화하려면 CI_FETCH_REPO_GIT_STRATEGYclone로 설정하고, 활성화하려면 CI_FETCH_REPO_GIT_STRATEGYnone으로 설정하세요.

캐싱 전략

  1. 모든 작업은 기본적으로 캐시를 가져와야 합니다.
  2. 모든 작업은 빈 캐시로도 통과해야 합니다. 즉, 캐시는 작업을 가속하기 위해서만 있는 것입니다.
  3. 우리는 현재 .gitlab/ci/global.gitlab-ci.yml에 정의된 여러 다른 캐시 정의가 있습니다.
    • .setup-test-env-cache
    • .ruby-cache
    • .static-analysis-cache
    • .rubocop-cache
    • .ruby-gems-coverage-cache
    • .ruby-node-cache
    • .qa-cache
    • .yarn-cache
    • .assets-compile-cache (키에 ${NODE_ENV}가 포함되어 있어 실제로 두 가지 다른 캐시입니다).
  4. 이러한 캐시 정의는 다중 원자적 캐시로 구성되어 있습니다.
  5. 2시간마다 실행되는 maintenance 예약 파이프라인에서만 실행되는 다음 작업들이 캐시에 푸시(즉, 업데이트)하는 유일한 작업입니다:
    • [.gitlab/ci/rails.gitlab-ci.yml](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/rails.gitlab-ci.yml)에 정의된 update-setup-test-env-cache`
    • [.gitlab/ci/rails.gitlab-ci.yml](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/rails.gitlab-ci.yml)에 정의된 update-gitaly-binaries-cache`
    • [.gitlab/ci/rails.gitlab-ci.yml](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/rails.gitlab-ci.yml)에 정의된 update-rubocop-cache`
    • [.gitlab/ci/qa.gitlab-ci.yml](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/qa.gitlab-ci.yml)에 정의된 update-qa-cache`
    • [.gitlab/ci/frontend.gitlab-ci.yml](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/frontend.gitlab-ci.yml)에 정의된 update-assets-compile-production-cache`
    • [.gitlab/ci/frontend.gitlab-ci.yml](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/frontend.gitlab-ci.yml)에 정의된 update-assets-compile-test-cache`
    • [.gitlab/ci/frontend.gitlab-ci.yml](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/ci/frontend.gitlab-ci.yml)에 정의된 update-storybook-yarn-cache`
  6. 이러한 작업들은 또한 pipeline:update-cache 레이블을 가진 Merge Request에서 실행되도록 강제할 수 있습니다(캐시 키를 업데이트하는 MR에서 캐시를 미리 로드하는 데 유용합니다).

아티팩트 전략

업로드/다운로드 시간 및 비용, 그리고 아티팩트 리포지터리를 줄이기 위해 작업이 저장하고 검색하는 아티팩트를 최소화하도록 제한합니다.

컴포넌트 캐싱

GitLab의 일부 외부 컴포넌트(GitLab Workhorse 및 프론트엔드 자산)는 테스트를 실행하기 전에 소스에서 빌드해야 합니다.

cache-workhorse

이 MR에서, 그리고 이 MR에서, 우리는 다음과 같은 새로운 cache-workhorse 작업을 소개했습니다:

  • GitLab.com의 gitlab-org/gitlab 예약 파이프라인에 자동으로 실행됩니다.
  • master에 커밋하는 경우에는 workhorse/ 폴더에 영향을 미치는 커밋에 대해 자동으로 실행됩니다.
  • gitlab-org의 MRs에서는 캐시 관련 파일을 손대는 경우 매뉴얼으로 실행됩니다.

이 작업은 GitLab 테스트 슈트에서 필요한 GitLab Workhorse 이진 파일을 포함하는 일반 패키지를 다운로드하려고 합니다(예: tmp/tests/gitlab-workhorse).

  • 패키지 URL이 404를 반환하면:
    1. scripts/setup-test-env를 실행하여 GitLab Workhorse 바이너리를 빌드합니다.
    2. 그런 다음 바이너리를 포함하는 아카이브를 생성하고 일반 패키지로 업로드합니다.
  • 그렇지 않은 경우, 패키지가 이미 있는 경우에는 작업을 성공적으로 종료합니다.

또한 setup-test-env 작업을 다음과 같이 변경했습니다:

  1. 먼저 cache-workhorse에 의해 빌드 및 업로드된 GitLab Workhorse 일반 패키지를 다운로드합니다.
  2. 패키지가 성공적으로 검색된 경우 해당 내용을 올바른 폴더에 배치하여(tmp/tests/gitlab-workhorse 등) 나중에 scripts/setup-test-env가 실행될 때 바이너리를 빌드하도록 합니다.
  3. 패키지 URL이 404를 반환하면 현재와 동일한 동작입니다: scripts/setup-test-env의 일부로 GitLab Workhorse 바이너리를 빌드합니다.
note
패키지 버전은 workhorse 트리 SHA입니다(예: git rev-parse HEAD:workhorse).

cache-assets

이 MR에서, 우리는 새로운 cache-assets:test, cache-assets:test as-if-foss, cache-assets:production 작업 세 개를 소개했습니다. 이 작업들은 다음과 같습니다:

  • $CACHE_ASSETS_AS_PACKAGE == "true" 일 때만 실행되며
  • 모든 GitLab.com gitlab-org/gitlab 예약된 파이프라인에 대해 자동으로 실행되며
  • master 커밋이 assets 관련 폴더를 건드리는 경우에도 자동으로 실행되며
  • gitlab-org의 MR에서 캐싱 관련 파일을 건드리는 경우 매뉴얼으로 실행됩니다.

이 작업은 GitLab 테스트 스위트에서 필요로 하는 GitLab 컴파일된 assets가 포함된 일반 패키지를 다운로드하려고 시도합니다 (app/assets/javascripts/locale/**/app.jspublic/assets).

  • 패키지 URL이 404를 반환하는 경우:
    1. bin/rake gitlab:assets:compile을 실행하여 GitLab assets를 컴파일합니다.
    2. assets를 포함하는 아카이브를 생성하고 일반 패키지로 업로드합니다. 패키지 버전은 assets 폴더의 해시 합으로 설정됩니다.
  • 그렇지 않은 경우, 패키지가 이미 존재하는 경우 작업을 성공적으로 종료합니다.

compile-*-assets

또한 compile-test-assets, compile-test-assets as-if-foss, compile-production-assets 작업을 다음과 같이 변경했습니다:

  1. 먼저 “native” 캐시 assets를 다운로드합니다.
    • 컴파일된 assets
    • cached-assets-hash.txt 파일을 포함하여, assets가 의존하는 모든 소스 파일의 SHA256 해시 다이제스트를 포함합니다. 이 파일은 비관적인 디렉터리이며 assets가 이러한 파일에 의존하지 않을 수 있습니다. 최악의 경우, assets를 더 자주 컴파일하지만, 오래된 assets를 사용하는 것보다는 더 나은 방법입니다. 이 파일은 assets가 컴파일된 후에 생성됩니다.
  2. 그런 다음, 현재 체크아웃된 브랜치에 의존하는 assets의 모든 소스 파일의 SHA256 해시 다이제스트를 계산합니다. 계산된 해시 다이제스트를 GITLAB_ASSETS_HASH 변수에 저장합니다.
  3. $CACHE_ASSETS_AS_PACKAGE == "true"이면, cache-assets:*에 의해 빌드되고 업로드된 일반 패키지를 다운로드합니다.
    • 체크아웃된 브랜치에 대해 cache가 최신 상태인 경우, native 캐시 캐시 패키지를 다운로드합니다. 우리는 일반 패키지를 다운로드하는 대신 native cache를 다운로드함으로써 최적화할 수 있습니다. 그러나 native 캐시는 실제로 매우 자주 오래된 상태일 수 있습니다. 왜냐하면 2시간마다만 재구성하기 때문입니다.
  4. 우리는 assets_compile_script 함수를 실행합니다; 이 함수는 자체적으로 assets:compile Rake task를 실행합니다.

    이 작업은 assets의 컴파일 여부를 결정하는 데 책임이 있습니다. 이 작업은 $GITLAB_ASSETS_HASHHEAD SHA256 해시 다이제스트를 cached-assets-hash.txtmaster 해시 다이제스트와 비교합니다.

  5. 해시가 동일하면, 아무것도 컴파일하지 않습니다. 다르다면, assets를 컴파일합니다.

스트립된 바이너리

기본적으로, setup-test-env저장 공간을 절약하고 이후의 CI 작업의 artifact 다운로드를 가속화하기 위해 스트립된 바이너리를 포함하는 artifact를 생성합니다.

스트립된 바이너리에서의 크래시를 디버깅하기 쉽게 하려면 setup-test-job 작업에서 strip_executable_binaries와 같은 라인을 주석 처리하고, 새로운 파이프라인을 시작하세요.