InfoGrab DocsInfoGrab Docs

GitLab CI/CD 캐싱

요약

캐시는 Job이 다운로드하고 저장하는 하나 이상의 파일입니다. .gitlab-ci.yml 파일에서 캐시를 정의하는 방법은 cache 참조를 참조합니다. 고급 캐시 키 전략을 위해 다음을 사용할 수 있습니다: 더 많은 사용 사례와 예시는 CI/CD 캐싱 예시를 참조합니다.

캐시는 Job이 다운로드하고 저장하는 하나 이상의 파일입니다. 동일한 캐시를 사용하는 후속 Job은 파일을 다시 다운로드할 필요가 없으므로 더 빠르게 실행됩니다.

.gitlab-ci.yml 파일에서 캐시를 정의하는 방법은 cache 참조를 참조합니다.

고급 캐시 키 전략을 위해 다음을 사용할 수 있습니다:

더 많은 사용 사례와 예시는 CI/CD 캐싱 예시를 참조합니다.

캐시와 아티팩트의 차이점#

인터넷에서 다운로드하는 패키지와 같은 의존성에는 캐시를 사용합니다. 캐시는 GitLab Runner가 설치된 위치에 저장되며 분산 캐시가 활성화된 경우 S3에 업로드됩니다.

Stage 간에 중간 빌드 결과를 전달하는 데는 아티팩트를 사용합니다. 아티팩트는 Job에 의해 생성되고 GitLab에 저장되며 다운로드할 수 있습니다.

아티팩트와 캐시 모두 프로젝트 디렉터리를 기준으로 경로를 정의하며, 그 외부의 파일에는 링크할 수 없습니다.

캐시#

  • cache 키워드를 사용하여 Job별로 캐시를 정의합니다. 정의하지 않으면 비활성화됩니다.
  • 후속 파이프라인은 캐시를 사용할 수 있습니다.
  • 의존성이 동일하면 같은 파이프라인의 후속 Job도 캐시를 사용할 수 있습니다.
  • 서로 다른 프로젝트는 캐시를 공유할 수 없습니다.
  • 기본적으로 보호된 브랜치와 보호되지 않은 브랜치는 캐시를 공유하지 않습니다. 하지만 이 동작을 변경할 수 있습니다.

아티팩트#

  • Job별로 아티팩트를 정의합니다.
  • 같은 파이프라인의 이후 Stage에 있는 후속 Job은 아티팩트를 사용할 수 있습니다.
  • 아티팩트는 기본적으로 30일 후 만료됩니다. 사용자 지정 만료 시간을 정의할 수 있습니다.
  • 최신 아티팩트 유지가 활성화된 경우 최신 아티팩트는 만료되지 않습니다.
  • 어떤 Job이 아티팩트를 가져올지 제어하려면 dependencies를 사용합니다.

좋은 캐싱 관행#

캐시의 가용성을 최대한 보장하려면 다음 중 하나 이상을 수행합니다:

러너가 캐시를 효율적으로 사용하려면 다음 중 하나를 수행해야 합니다:

  • 모든 Job에 러너 하나만 사용합니다.
  • 캐시가 S3 버킷에 저장되는 분산 캐싱을 지원하는 여러 러너를 사용합니다. GitLab.com의 인스턴스 러너는 이 방식으로 동작합니다. 이러한 러너는 자동 확장 모드일 수 있지만 반드시 그래야 하는 것은 아닙니다. 캐시 객체를 관리하려면 일정 기간이 지난 후 캐시 객체를 삭제하는 수명 주기 규칙을 적용합니다. 수명 주기 규칙은 오브젝트 스토리지 서버에서 사용할 수 있습니다.
  • 동일한 아키텍처의 여러 러너를 사용하고, 이 러너들이 캐시를 저장할 공통 네트워크 마운트 디렉터리를 공유하도록 합니다. 이 디렉터리는 NFS나 이와 유사한 것을 사용해야 합니다. 이러한 러너는 자동 확장 모드여야 합니다.

여러 캐시 사용#

Job당 최대 4개의 캐시를 가질 수 있습니다:

test-job:
  stage: build
  cache:
    - key:
        files:
          - Gemfile.lock
      paths:
        - vendor/ruby
    - key:
        files:
          - yarn.lock
      paths:
        - .yarn-cache/
  script:
    - bundle config set --local path 'vendor/ruby'
    - bundle install
    - yarn install --cache-folder .yarn-cache
    - echo Run tests...

여러 캐시를 폴백 캐시 키와 함께 사용하면, 캐시를 찾지 못할 때마다 전역 폴백 캐시를 가져옵니다.

폴백 캐시 키 사용#

캐시별 폴백 키#

각 캐시 항목은 fallback_keys 키워드를 사용하여 최대 5개의 폴백 키를 지원합니다. Job이 캐시 키를 찾지 못하면 대신 폴백 캐시를 가져오려고 시도합니다. 폴백 키는 캐시를 찾을 때까지 순서대로 검색됩니다. 캐시를 찾지 못하면 Job은 캐시 없이 실행됩니다. 예를 들면 다음과 같습니다:

test-job:
  stage: build
  cache:
    - key: cache-$CI_COMMIT_REF_SLUG
      fallback_keys:
        - cache-$CI_DEFAULT_BRANCH
        - cache-default
      paths:
        - vendor/ruby
  script:
    - bundle config set --local path 'vendor/ruby'
    - bundle install
    - echo Run tests...

이 예시에서는 다음과 같이 동작합니다:

  1. Job은 cache-$CI_COMMIT_REF_SLUG 캐시를 찾습니다.
  2. cache-$CI_COMMIT_REF_SLUG를 찾지 못하면 Job은 폴백 옵션으로 cache-$CI_DEFAULT_BRANCH를 찾습니다.
  3. cache-$CI_DEFAULT_BRANCH도 찾지 못하면 Job은 두 번째 폴백 옵션으로 cache-default를 찾습니다.
  4. 아무것도 찾지 못하면 Job은 캐시 없이 모든 Ruby 의존성을 다운로드하지만, Job이 완료되면 cache-$CI_COMMIT_REF_SLUG에 대한 새 캐시를 생성합니다.

폴백 키는 cache:key와 동일한 처리 로직을 따릅니다:

전역 폴백 키#

cache:key를 지정하려면 사전 정의된 변수인 $CI_COMMIT_REF_SLUG를 사용할 수 있습니다. 예를 들어 $CI_COMMIT_REF_SLUG가 test이면, test로 태그된 캐시를 다운로드하도록 Job을 설정할 수 있습니다.

이 태그가 지정된 캐시를 찾지 못하면 CACHE_FALLBACK_KEY를 사용하여 캐시가 없을 때 사용할 캐시를 지정할 수 있습니다.

다음 예시에서는 $CI_COMMIT_REF_SLUG를 찾지 못하면 Job이 CACHE_FALLBACK_KEY 변수로 정의된 키를 사용합니다:

variables:
  CACHE_FALLBACK_KEY: fallback-key

job1:
  script:
    - echo
  cache:
    key: "$CI_COMMIT_REF_SLUG"
    paths:
      - binaries/

캐시 추출 순서는 다음과 같습니다:

  1. cache:key에 대한 검색 시도
  2. fallback_keys의 각 항목에 대해 순서대로 검색 시도
  3. CACHE_FALLBACK_KEY에 지정된 전역 폴백 키에 대한 검색 시도

캐시 추출 프로세스는 첫 번째로 캐시를 성공적으로 가져오면 중단됩니다.

특정 Job에 대해 캐시 비활성화#

캐시를 전역으로 정의하면 모든 Job이 동일한 정의를 사용합니다. Job별로 이 동작을 재정의할 수 있습니다.

Job에 대해 캐시를 완전히 비활성화하려면 빈 목록을 사용합니다:

job:
  cache: []

전역 구성을 상속하지만 Job별로 특정 설정 재정의#

앵커를 사용하면 전역 캐시를 덮어쓰지 않고도 캐시 설정을 재정의할 수 있습니다. 예를 들어 한 Job에 대해 policy를 재정의하려면 다음과 같이 합니다:

default:
  cache: &global_cache
    key: $CI_COMMIT_REF_SLUG
    paths:
      - node_modules/
      - public/
      - vendor/
    policy: pull-push

job:
  cache:
    # inherit all global cache settings
    <<: *global_cache
    # override the policy
    policy: pull

자세한 내용은 cache: policy를 참조합니다.

캐시 키 이름#

히스토리

전역 폴백 캐시 키를 제외하고 캐시 키에 접미사가 추가됩니다.

파이프라인이 다음에 해당하면 캐시 키는 -protected 접미사를 받습니다:

  • 보호된 브랜치나 태그에 대해 실행됩니다. 사용자는 보호된 브랜치에 머지할 권한이나 보호된 태그를 생성할 권한이 있어야 합니다.
  • Maintainer 또는 Owner 권한을 가진 사용자가 시작했습니다.

다른 파이프라인에서 생성된 키는 non_protected 접미사를 받습니다.

예를 들어 다음과 같은 경우입니다:

  • cache:key가 $CI_COMMIT_REF_SLUG로 설정되어 있습니다.
  • main이 보호된 브랜치입니다.
  • feature가 보호되지 않은 브랜치입니다.
브랜치 Developer 권한 캐시 키 Maintainer 권한 캐시 키
main main-protected main-protected
feature feature-non_protected feature-protected

또한 태그에 대한 파이프라인의 경우, 파이프라인이 실행되는 브랜치가 아니라 태그의 보호 상태가 접미사에 우선합니다. 이 동작은 트리거하는 참조가 캐시 액세스 권한을 결정하므로 일관된 보안 경계를 보장합니다.

예를 들어 다음과 같은 경우입니다:

  • cache:key가 $CI_COMMIT_TAG로 설정되어 있습니다.
  • main이 보호된 브랜치입니다.
  • feature가 보호되지 않은 브랜치입니다.
  • 1.0.0이 보호된 태그입니다.
  • 1.1.1-rc1이 보호되지 않은 태그입니다.
태그 브랜치 Developer 권한 캐시 키 Maintainer 권한 캐시 키
1.0.0 main 1.0.0-protected 1.0.0-protected
1.0.0 feature 1.0.0-protected 1.0.0-protected
1.1.1-rc1 main 1.1.1-rc1-non_protected 1.1.1-rc1-protected
1.1.1-rc1 feature 1.1.1-rc1-non_protected 1.1.1-rc1-protected

모든 브랜치에 동일한 캐시 사용#

캐시 키 이름을 사용하지 않으려면 모든 브랜치(보호된 브랜치와 보호되지 않은 브랜치)가 동일한 캐시를 사용하도록 설정할 수 있습니다.

캐시 키 이름을 이용한 캐시 분리는 보안 기능이며, Developer 권한을 가진 모든 사용자를 매우 신뢰할 수 있는 환경에서만 비활성화해야 합니다.

모든 브랜치에 동일한 캐시를 사용하려면 다음과 같이 합니다:

  1. 상단 바에서 검색 또는 이동을 선택하고 프로젝트를 찾습니다.
  2. 왼쪽 사이드바에서 Settings > CI/CD를 선택합니다.
  3. General pipelines를 펼칩니다.
  4. Use separate caches for protected branches 체크박스를 선택 해제합니다.
  5. Save changes를 선택합니다.

캐시 가용성#

캐싱은 최적화이지만 항상 작동하는 것이 보장되지는 않습니다. 캐시가 필요한 각 Job에서 캐시된 파일을 재생성해야 할 수도 있습니다.

.gitlab-ci.yml에서 캐시를 정의한 후 캐시 가용성은 다음에 따라 달라집니다:

  • 러너의 실행기 유형.
  • Job 간에 캐시를 전달하는 데 서로 다른 러너를 사용하는지 여부.

캐시가 저장되는 위치#

Job에 대해 정의된 모든 캐시는 하나의 cache.zip 파일로 아카이브됩니다. 러너 구성이 이 파일이 저장되는 위치를 정의합니다. 기본적으로 캐시는 GitLab Runner가 설치된 머신에 저장됩니다. 저장 위치는 실행기 유형에 따라서도 달라집니다.

러너 실행기 캐시의 기본 경로
Shell 로컬, gitlab-runner 사용자의 홈 디렉터리 아래: /home/gitlab-runner/cache/<user>/<project>/<cache-key>/cache.zip.
Docker 로컬, Docker 볼륨 아래: /var/lib/docker/volumes/<volume-id>/_data/<user>/<project>/<cache-key>/cache.zip.
Docker Machine (자동 확장 러너) Docker 실행기와 동일합니다.

Job에서 캐시와 아티팩트로 동일한 경로를 저장하면, 아티팩트보다 캐시가 먼저 복원되기 때문에 캐시가 덮어쓰여질 수 있습니다.

아카이빙 및 추출 워크플로#

이 예시는 연속된 두 Stage에 속한 두 Job을 보여줍니다:

stages:
  - build
  - test

default:
  cache:
    key: build-cache
    paths:
      - vendor/
  before_script:
    - echo "Hello"

job A:
  stage: build
  script:
    - mkdir vendor/
    - echo "build" > vendor/hello.txt
  after_script:
    - echo "World"

job B:
  stage: test
  script:
    - cat vendor/hello.txt

프로젝트가 한 머신에 설치된 단일 러너를 사용한다면 모든 Job이 해당 호스트에서 실행됩니다:

  1. 파이프라인이 시작됩니다.
  2. job A가 실행됩니다.
  3. 캐시가 있으면 추출됩니다.
  4. before_script가 실행됩니다.
  5. script가 실행됩니다.
  6. after_script가 실행됩니다.
  7. cache가 실행되고 vendor/ 디렉터리가 cache.zip으로 압축됩니다. 이 파일은 러너의 설정과 cache: key를 기준으로 하는 디렉터리에 저장됩니다.
  8. job B가 실행됩니다.
  9. 캐시가 있으면 추출됩니다.
  10. before_script가 실행됩니다.
  11. script가 실행됩니다.
  12. 파이프라인이 완료됩니다.

단일 머신에서 단일 러너를 사용하면 job B가 job A와 다른 러너에서 실행되는 문제가 발생하지 않습니다. 이 설정은 Stage 간에 캐시를 재사용할 수 있음을 보장합니다. 이는 동일한 러너/머신에서 build Stage에서 test Stage로 실행이 이어질 때만 작동합니다. 그렇지 않으면 캐시를 사용할 수 없을 수도 있습니다.

이러한 제한이 있는 이유는 한 러너를 위해 만들어진 캐시가 다른 러너에서 사용될 때 유효하지 않은 경우가 많기 때문입니다. 다른 러너는 다른 아키텍처를 사용할 수 있으므로 캐시된 바이너리 파일이 동작하지 않을 수 있습니다. 러너는 서로 다른 머신에서 실행될 수도 있으므로, GitLab은 캐시의 신뢰성을 유지하기 위해 기본적으로 같은 러너를 사용합니다.

Mermaid 다이어그램 (8줄)
소스 코드 보기
flowchart LR
    accTitle: Cache archive and extraction across two jobs
    accDescr: Job A archives the cache to the runner's storage, and job B can only extract it if both jobs run on the same runner.
A[Job A runs] --&gt; B[Cache archived to cache.zip]
B --&gt; C{Job B on same runner?}
C --&gt;|Yes| D[Cache extracted for job B]
C --&gt;|No| E[Cache not available for job B]</code></pre></details></div>

캐싱 프로세스 중에는 다음 사항도 고려해야 합니다:

  • 다른 캐시 구성을 가진 다른 Job이 같은 zip 파일에 캐시를 저장한 경우 덮어쓰여집니다. S3 기반 공유 캐시를 사용하는 경우 파일은 캐시 키를 기준으로 한 객체로 S3에도 업로드됩니다. 따라서 경로는 다르지만 캐시 키가 같은 두 Job은 서로의 캐시를 덮어씁니다.
  • cache.zip에서 캐시를 추출할 때 zip 파일의 모든 내용이 Job의 작업 디렉터리(보통 내려받은 리포지터리)에 추출되며, 러너는 job A의 아카이브가 job B의 아카이브에 있는 내용을 덮어쓰는 것에 관여하지 않습니다.

캐시 지우기#

러너는 기존 데이터를 재사용하여 Job 실행 속도를 높이기 위해 캐시를 사용합니다. 이로 인해 때때로 일관되지 않은 동작이 발생할 수 있습니다.

새로운 캐시로 시작하려면 cache:key를 변경하거나 캐시를 수동으로 지우세요.

cache:key를 변경하여 캐시 지우기#

.gitlab-ci.yml 파일에서 cache: key 값을 변경합니다. 다음번에 파이프라인이 실행되면 캐시는 다른 위치에 저장됩니다.

캐시를 수동으로 지우기#

사전 요구 사항:

  • 프로젝트에 대한 Developer 이상의 권한이 있어야 합니다.
Note

캐시를 지우면 보호된 브랜치의 파이프라인을 포함해 다음 파이프라인이 의존성을 다시 가져옵니다. Job이 의존성 버전을 고정하지 않으면 이 과정에서 아무도 검토하지 않은 더 새로운 업스트림 릴리스로 해석될 수 있습니다. 캐시를 지워도 파이프라인의 콘텐츠가 바뀌지 않도록 의존성 버전을 고정합니다.

GitLab UI에서 캐시를 지울 수 있습니다:

  1. 상단 바에서 검색 또는 이동을 선택하고 프로젝트를 찾습니다.
  2. 왼쪽 사이드바에서 Build > Pipelines를 선택합니다.
  3. 오른쪽 상단 모서리에서 Clear runner caches를 선택합니다.

다음 커밋부터 CI/CD Job은 새 캐시를 사용합니다.

Note

캐시를 수동으로 지울 때마다 내부 캐시 이름이 업데이트됩니다. 이름은 cache-<index> 형식을 사용하며 인덱스는 하나씩 증가합니다. 이전 캐시는 삭제되지 않습니다. 러너 스토리지에서 이 파일들을 수동으로 삭제할 수 있습니다.

문제 해결#

캐시 불일치#

캐시 불일치가 발생하면 다음 단계에 따라 문제를 해결합니다.

캐시 불일치의 원인 해결 방법
공유 캐시 없이 하나의 프로젝트에 연결된 여러 독립 실행형 러너(자동 확장 모드가 아닌)를 사용하는 경우. 프로젝트에 러너 하나만 사용하거나 분산 캐시가 활성화된 여러 러너를 사용합니다.
분산 캐시가 활성화되지 않은 자동 확장 모드의 러너를 사용하는 경우. 자동 확장 러너가 분산 캐시를 사용하도록 구성합니다.
러너가 설치된 머신의 디스크 공간이 부족하거나, 분산 캐시를 설정한 경우 캐시가 저장되는 S3 버킷의 공간이 충분하지 않은 경우. 새 캐시를 저장할 수 있도록 공간을 확보합니다. 이를 자동으로 수행하는 방법은 없습니다.
서로 다른 경로를 캐시하는 Job에 동일한 key를 사용하는 경우. 캐시 아카이브가 다른 위치에 저장되어 잘못된 캐시를 덮어쓰지 않도록 서로 다른 캐시 키를 사용합니다.
러너에서 분산 러너 캐싱을 활성화하지 않은 경우. Shared = false로 설정하고 러너를 다시 프로비저닝합니다.

캐시 불일치 예시 1#

프로젝트에 러너가 하나만 할당된 경우, 캐시는 기본적으로 해당 러너의 머신에 저장됩니다.

두 Job이 동일한 캐시 키를 가지지만 경로가 다르면 캐시가 덮어쓰여질 수 있습니다. 예를 들면 다음과 같습니다:

stages:
  - build
  - test

job A:
  stage: build
  script: make build
  cache:
    key: same-key
    paths:
      - public/

job B:
  stage: test
  script: make test
  cache:
    key: same-key
    paths:
      - vendor/
  1. job A가 실행됩니다.
  2. public/이 cache.zip으로 캐시됩니다.
  3. job B가 실행됩니다.
  4. 이전 캐시가 있으면 압축이 풀립니다.
  5. vendor/가 cache.zip으로 캐시되어 이전 캐시를 덮어씁니다.
  6. 다음에 job A가 실행될 때는 job B의 캐시를 사용하게 되는데, 이는 서로 달라서 효과가 없습니다.

이 문제를 해결하려면 각 Job에 서로 다른 keys를 사용합니다.

캐시 불일치 예시 2#

이 예시에서는 프로젝트에 러너가 두 개 이상 할당되어 있고 분산 캐시가 활성화되어 있지 않습니다.

두 번째로 파이프라인이 실행될 때 job A와 job B가 (이 경우 서로 다른) 캐시를 재사용하기를 원합니다:

stages:
  - build
  - test

job A:
  stage: build
  script: build
  cache:
    key: keyA
    paths:
      - vendor/

job B:
  stage: test
  script: test
  cache:
    key: keyB
    paths:
      - vendor/

key가 다르더라도, 후속 파이프라인에서 Job이 서로 다른 러너에서 실행되면 각 Stage 이전에 캐시된 파일이 "정리"될 수 있습니다.

동시 실행 러너에서 로컬 캐시가 누락되는 경우#

Docker 실행기로 여러 개의 동시 실행 러너를 구성한 경우, 동시에 실행 중인 Job에서 예상과 달리 로컬에 캐시된 파일이 없을 수 있습니다. 캐시 볼륨의 이름은 러너 인스턴스마다 고유하게 생성되므로, 한 러너 인스턴스가 캐시한 파일을 다른 러너 인스턴스의 캐시에서는 찾을 수 없습니다.

동시 실행 러너 간에 캐시를 공유하려면 다음 중 하나를 수행합니다:

  • 러너의 config.toml에 있는 [runners.docker] 섹션을 사용하여, 각 컨테이너의 /cache에 매핑되는 호스트의 단일 마운트 지점을 구성합니다(예: volumes = ["/mnt/gitlab-runner/cache-for-all-concurrent-jobs:/cache"]). 이 방법은 러너가 동시 실행되는 Job마다 고유한 볼륨 이름을 만드는 것을 방지합니다.
  • 분산 캐시를 사용합니다.

GitLab CI/CD 캐싱

GitLab v19.5
Tier: Free, Premium, Ultimate
Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
원문 보기

요약

캐시는 Job이 다운로드하고 저장하는 하나 이상의 파일입니다. .gitlab-ci.yml 파일에서 캐시를 정의하는 방법은 cache 참조를 참조합니다. 고급 캐시 키 전략을 위해 다음을 사용할 수 있습니다: 더 많은 사용 사례와 예시는 CI/CD 캐싱 예시를 참조합니다.

캐시는 Job이 다운로드하고 저장하는 하나 이상의 파일입니다. 동일한 캐시를 사용하는 후속 Job은 파일을 다시 다운로드할 필요가 없으므로 더 빠르게 실행됩니다.

.gitlab-ci.yml 파일에서 캐시를 정의하는 방법은 cache 참조를 참조합니다.

고급 캐시 키 전략을 위해 다음을 사용할 수 있습니다:

더 많은 사용 사례와 예시는 CI/CD 캐싱 예시를 참조합니다.

캐시와 아티팩트의 차이점#

인터넷에서 다운로드하는 패키지와 같은 의존성에는 캐시를 사용합니다. 캐시는 GitLab Runner가 설치된 위치에 저장되며 분산 캐시가 활성화된 경우 S3에 업로드됩니다.

Stage 간에 중간 빌드 결과를 전달하는 데는 아티팩트를 사용합니다. 아티팩트는 Job에 의해 생성되고 GitLab에 저장되며 다운로드할 수 있습니다.

아티팩트와 캐시 모두 프로젝트 디렉터리를 기준으로 경로를 정의하며, 그 외부의 파일에는 링크할 수 없습니다.

캐시#

  • cache 키워드를 사용하여 Job별로 캐시를 정의합니다. 정의하지 않으면 비활성화됩니다.
  • 후속 파이프라인은 캐시를 사용할 수 있습니다.
  • 의존성이 동일하면 같은 파이프라인의 후속 Job도 캐시를 사용할 수 있습니다.
  • 서로 다른 프로젝트는 캐시를 공유할 수 없습니다.
  • 기본적으로 보호된 브랜치와 보호되지 않은 브랜치는 캐시를 공유하지 않습니다. 하지만 이 동작을 변경할 수 있습니다.

아티팩트#

  • Job별로 아티팩트를 정의합니다.
  • 같은 파이프라인의 이후 Stage에 있는 후속 Job은 아티팩트를 사용할 수 있습니다.
  • 아티팩트는 기본적으로 30일 후 만료됩니다. 사용자 지정 만료 시간을 정의할 수 있습니다.
  • 최신 아티팩트 유지가 활성화된 경우 최신 아티팩트는 만료되지 않습니다.
  • 어떤 Job이 아티팩트를 가져올지 제어하려면 dependencies를 사용합니다.

좋은 캐싱 관행#

캐시의 가용성을 최대한 보장하려면 다음 중 하나 이상을 수행합니다:

러너가 캐시를 효율적으로 사용하려면 다음 중 하나를 수행해야 합니다:

  • 모든 Job에 러너 하나만 사용합니다.
  • 캐시가 S3 버킷에 저장되는 분산 캐싱을 지원하는 여러 러너를 사용합니다. GitLab.com의 인스턴스 러너는 이 방식으로 동작합니다. 이러한 러너는 자동 확장 모드일 수 있지만 반드시 그래야 하는 것은 아닙니다. 캐시 객체를 관리하려면 일정 기간이 지난 후 캐시 객체를 삭제하는 수명 주기 규칙을 적용합니다. 수명 주기 규칙은 오브젝트 스토리지 서버에서 사용할 수 있습니다.
  • 동일한 아키텍처의 여러 러너를 사용하고, 이 러너들이 캐시를 저장할 공통 네트워크 마운트 디렉터리를 공유하도록 합니다. 이 디렉터리는 NFS나 이와 유사한 것을 사용해야 합니다. 이러한 러너는 자동 확장 모드여야 합니다.

여러 캐시 사용#

Job당 최대 4개의 캐시를 가질 수 있습니다:

test-job:
  stage: build
  cache:
    - key:
        files:
          - Gemfile.lock
      paths:
        - vendor/ruby
    - key:
        files:
          - yarn.lock
      paths:
        - .yarn-cache/
  script:
    - bundle config set --local path 'vendor/ruby'
    - bundle install
    - yarn install --cache-folder .yarn-cache
    - echo Run tests...

여러 캐시를 폴백 캐시 키와 함께 사용하면, 캐시를 찾지 못할 때마다 전역 폴백 캐시를 가져옵니다.

폴백 캐시 키 사용#

캐시별 폴백 키#

각 캐시 항목은 fallback_keys 키워드를 사용하여 최대 5개의 폴백 키를 지원합니다. Job이 캐시 키를 찾지 못하면 대신 폴백 캐시를 가져오려고 시도합니다. 폴백 키는 캐시를 찾을 때까지 순서대로 검색됩니다. 캐시를 찾지 못하면 Job은 캐시 없이 실행됩니다. 예를 들면 다음과 같습니다:

test-job:
  stage: build
  cache:
    - key: cache-$CI_COMMIT_REF_SLUG
      fallback_keys:
        - cache-$CI_DEFAULT_BRANCH
        - cache-default
      paths:
        - vendor/ruby
  script:
    - bundle config set --local path 'vendor/ruby'
    - bundle install
    - echo Run tests...

이 예시에서는 다음과 같이 동작합니다:

  1. Job은 cache-$CI_COMMIT_REF_SLUG 캐시를 찾습니다.
  2. cache-$CI_COMMIT_REF_SLUG를 찾지 못하면 Job은 폴백 옵션으로 cache-$CI_DEFAULT_BRANCH를 찾습니다.
  3. cache-$CI_DEFAULT_BRANCH도 찾지 못하면 Job은 두 번째 폴백 옵션으로 cache-default를 찾습니다.
  4. 아무것도 찾지 못하면 Job은 캐시 없이 모든 Ruby 의존성을 다운로드하지만, Job이 완료되면 cache-$CI_COMMIT_REF_SLUG에 대한 새 캐시를 생성합니다.

폴백 키는 cache:key와 동일한 처리 로직을 따릅니다:

전역 폴백 키#

cache:key를 지정하려면 사전 정의된 변수인 $CI_COMMIT_REF_SLUG를 사용할 수 있습니다. 예를 들어 $CI_COMMIT_REF_SLUG가 test이면, test로 태그된 캐시를 다운로드하도록 Job을 설정할 수 있습니다.

이 태그가 지정된 캐시를 찾지 못하면 CACHE_FALLBACK_KEY를 사용하여 캐시가 없을 때 사용할 캐시를 지정할 수 있습니다.

다음 예시에서는 $CI_COMMIT_REF_SLUG를 찾지 못하면 Job이 CACHE_FALLBACK_KEY 변수로 정의된 키를 사용합니다:

variables:
  CACHE_FALLBACK_KEY: fallback-key

job1:
  script:
    - echo
  cache:
    key: "$CI_COMMIT_REF_SLUG"
    paths:
      - binaries/

캐시 추출 순서는 다음과 같습니다:

  1. cache:key에 대한 검색 시도
  2. fallback_keys의 각 항목에 대해 순서대로 검색 시도
  3. CACHE_FALLBACK_KEY에 지정된 전역 폴백 키에 대한 검색 시도

캐시 추출 프로세스는 첫 번째로 캐시를 성공적으로 가져오면 중단됩니다.

특정 Job에 대해 캐시 비활성화#

캐시를 전역으로 정의하면 모든 Job이 동일한 정의를 사용합니다. Job별로 이 동작을 재정의할 수 있습니다.

Job에 대해 캐시를 완전히 비활성화하려면 빈 목록을 사용합니다:

job:
  cache: []

전역 구성을 상속하지만 Job별로 특정 설정 재정의#

앵커를 사용하면 전역 캐시를 덮어쓰지 않고도 캐시 설정을 재정의할 수 있습니다. 예를 들어 한 Job에 대해 policy를 재정의하려면 다음과 같이 합니다:

default:
  cache: &global_cache
    key: $CI_COMMIT_REF_SLUG
    paths:
      - node_modules/
      - public/
      - vendor/
    policy: pull-push

job:
  cache:
    # inherit all global cache settings
    <<: *global_cache
    # override the policy
    policy: pull

자세한 내용은 cache: policy를 참조합니다.

캐시 키 이름#

히스토리

전역 폴백 캐시 키를 제외하고 캐시 키에 접미사가 추가됩니다.

파이프라인이 다음에 해당하면 캐시 키는 -protected 접미사를 받습니다:

  • 보호된 브랜치나 태그에 대해 실행됩니다. 사용자는 보호된 브랜치에 머지할 권한이나 보호된 태그를 생성할 권한이 있어야 합니다.
  • Maintainer 또는 Owner 권한을 가진 사용자가 시작했습니다.

다른 파이프라인에서 생성된 키는 non_protected 접미사를 받습니다.

예를 들어 다음과 같은 경우입니다:

  • cache:key가 $CI_COMMIT_REF_SLUG로 설정되어 있습니다.
  • main이 보호된 브랜치입니다.
  • feature가 보호되지 않은 브랜치입니다.
브랜치 Developer 권한 캐시 키 Maintainer 권한 캐시 키
main main-protected main-protected
feature feature-non_protected feature-protected

또한 태그에 대한 파이프라인의 경우, 파이프라인이 실행되는 브랜치가 아니라 태그의 보호 상태가 접미사에 우선합니다. 이 동작은 트리거하는 참조가 캐시 액세스 권한을 결정하므로 일관된 보안 경계를 보장합니다.

예를 들어 다음과 같은 경우입니다:

  • cache:key가 $CI_COMMIT_TAG로 설정되어 있습니다.
  • main이 보호된 브랜치입니다.
  • feature가 보호되지 않은 브랜치입니다.
  • 1.0.0이 보호된 태그입니다.
  • 1.1.1-rc1이 보호되지 않은 태그입니다.
태그 브랜치 Developer 권한 캐시 키 Maintainer 권한 캐시 키
1.0.0 main 1.0.0-protected 1.0.0-protected
1.0.0 feature 1.0.0-protected 1.0.0-protected
1.1.1-rc1 main 1.1.1-rc1-non_protected 1.1.1-rc1-protected
1.1.1-rc1 feature 1.1.1-rc1-non_protected 1.1.1-rc1-protected

모든 브랜치에 동일한 캐시 사용#

캐시 키 이름을 사용하지 않으려면 모든 브랜치(보호된 브랜치와 보호되지 않은 브랜치)가 동일한 캐시를 사용하도록 설정할 수 있습니다.

캐시 키 이름을 이용한 캐시 분리는 보안 기능이며, Developer 권한을 가진 모든 사용자를 매우 신뢰할 수 있는 환경에서만 비활성화해야 합니다.

모든 브랜치에 동일한 캐시를 사용하려면 다음과 같이 합니다:

  1. 상단 바에서 검색 또는 이동을 선택하고 프로젝트를 찾습니다.
  2. 왼쪽 사이드바에서 Settings > CI/CD를 선택합니다.
  3. General pipelines를 펼칩니다.
  4. Use separate caches for protected branches 체크박스를 선택 해제합니다.
  5. Save changes를 선택합니다.

캐시 가용성#

캐싱은 최적화이지만 항상 작동하는 것이 보장되지는 않습니다. 캐시가 필요한 각 Job에서 캐시된 파일을 재생성해야 할 수도 있습니다.

.gitlab-ci.yml에서 캐시를 정의한 후 캐시 가용성은 다음에 따라 달라집니다:

  • 러너의 실행기 유형.
  • Job 간에 캐시를 전달하는 데 서로 다른 러너를 사용하는지 여부.

캐시가 저장되는 위치#

Job에 대해 정의된 모든 캐시는 하나의 cache.zip 파일로 아카이브됩니다. 러너 구성이 이 파일이 저장되는 위치를 정의합니다. 기본적으로 캐시는 GitLab Runner가 설치된 머신에 저장됩니다. 저장 위치는 실행기 유형에 따라서도 달라집니다.

러너 실행기 캐시의 기본 경로
Shell 로컬, gitlab-runner 사용자의 홈 디렉터리 아래: /home/gitlab-runner/cache/<user>/<project>/<cache-key>/cache.zip.
Docker 로컬, Docker 볼륨 아래: /var/lib/docker/volumes/<volume-id>/_data/<user>/<project>/<cache-key>/cache.zip.
Docker Machine (자동 확장 러너) Docker 실행기와 동일합니다.

Job에서 캐시와 아티팩트로 동일한 경로를 저장하면, 아티팩트보다 캐시가 먼저 복원되기 때문에 캐시가 덮어쓰여질 수 있습니다.

아카이빙 및 추출 워크플로#

이 예시는 연속된 두 Stage에 속한 두 Job을 보여줍니다:

stages:
  - build
  - test

default:
  cache:
    key: build-cache
    paths:
      - vendor/
  before_script:
    - echo "Hello"

job A:
  stage: build
  script:
    - mkdir vendor/
    - echo "build" > vendor/hello.txt
  after_script:
    - echo "World"

job B:
  stage: test
  script:
    - cat vendor/hello.txt

프로젝트가 한 머신에 설치된 단일 러너를 사용한다면 모든 Job이 해당 호스트에서 실행됩니다:

  1. 파이프라인이 시작됩니다.
  2. job A가 실행됩니다.
  3. 캐시가 있으면 추출됩니다.
  4. before_script가 실행됩니다.
  5. script가 실행됩니다.
  6. after_script가 실행됩니다.
  7. cache가 실행되고 vendor/ 디렉터리가 cache.zip으로 압축됩니다. 이 파일은 러너의 설정과 cache: key를 기준으로 하는 디렉터리에 저장됩니다.
  8. job B가 실행됩니다.
  9. 캐시가 있으면 추출됩니다.
  10. before_script가 실행됩니다.
  11. script가 실행됩니다.
  12. 파이프라인이 완료됩니다.

단일 머신에서 단일 러너를 사용하면 job B가 job A와 다른 러너에서 실행되는 문제가 발생하지 않습니다. 이 설정은 Stage 간에 캐시를 재사용할 수 있음을 보장합니다. 이는 동일한 러너/머신에서 build Stage에서 test Stage로 실행이 이어질 때만 작동합니다. 그렇지 않으면 캐시를 사용할 수 없을 수도 있습니다.

이러한 제한이 있는 이유는 한 러너를 위해 만들어진 캐시가 다른 러너에서 사용될 때 유효하지 않은 경우가 많기 때문입니다. 다른 러너는 다른 아키텍처를 사용할 수 있으므로 캐시된 바이너리 파일이 동작하지 않을 수 있습니다. 러너는 서로 다른 머신에서 실행될 수도 있으므로, GitLab은 캐시의 신뢰성을 유지하기 위해 기본적으로 같은 러너를 사용합니다.

Mermaid 다이어그램 (8줄)
소스 코드 보기
flowchart LR
    accTitle: Cache archive and extraction across two jobs
    accDescr: Job A archives the cache to the runner's storage, and job B can only extract it if both jobs run on the same runner.
A[Job A runs] --&gt; B[Cache archived to cache.zip]
B --&gt; C{Job B on same runner?}
C --&gt;|Yes| D[Cache extracted for job B]
C --&gt;|No| E[Cache not available for job B]</code></pre></details></div>

캐싱 프로세스 중에는 다음 사항도 고려해야 합니다:

  • 다른 캐시 구성을 가진 다른 Job이 같은 zip 파일에 캐시를 저장한 경우 덮어쓰여집니다. S3 기반 공유 캐시를 사용하는 경우 파일은 캐시 키를 기준으로 한 객체로 S3에도 업로드됩니다. 따라서 경로는 다르지만 캐시 키가 같은 두 Job은 서로의 캐시를 덮어씁니다.
  • cache.zip에서 캐시를 추출할 때 zip 파일의 모든 내용이 Job의 작업 디렉터리(보통 내려받은 리포지터리)에 추출되며, 러너는 job A의 아카이브가 job B의 아카이브에 있는 내용을 덮어쓰는 것에 관여하지 않습니다.

캐시 지우기#

러너는 기존 데이터를 재사용하여 Job 실행 속도를 높이기 위해 캐시를 사용합니다. 이로 인해 때때로 일관되지 않은 동작이 발생할 수 있습니다.

새로운 캐시로 시작하려면 cache:key를 변경하거나 캐시를 수동으로 지우세요.

cache:key를 변경하여 캐시 지우기#

.gitlab-ci.yml 파일에서 cache: key 값을 변경합니다. 다음번에 파이프라인이 실행되면 캐시는 다른 위치에 저장됩니다.

캐시를 수동으로 지우기#

사전 요구 사항:

  • 프로젝트에 대한 Developer 이상의 권한이 있어야 합니다.
Note

캐시를 지우면 보호된 브랜치의 파이프라인을 포함해 다음 파이프라인이 의존성을 다시 가져옵니다. Job이 의존성 버전을 고정하지 않으면 이 과정에서 아무도 검토하지 않은 더 새로운 업스트림 릴리스로 해석될 수 있습니다. 캐시를 지워도 파이프라인의 콘텐츠가 바뀌지 않도록 의존성 버전을 고정합니다.

GitLab UI에서 캐시를 지울 수 있습니다:

  1. 상단 바에서 검색 또는 이동을 선택하고 프로젝트를 찾습니다.
  2. 왼쪽 사이드바에서 Build > Pipelines를 선택합니다.
  3. 오른쪽 상단 모서리에서 Clear runner caches를 선택합니다.

다음 커밋부터 CI/CD Job은 새 캐시를 사용합니다.

Note

캐시를 수동으로 지울 때마다 내부 캐시 이름이 업데이트됩니다. 이름은 cache-<index> 형식을 사용하며 인덱스는 하나씩 증가합니다. 이전 캐시는 삭제되지 않습니다. 러너 스토리지에서 이 파일들을 수동으로 삭제할 수 있습니다.

문제 해결#

캐시 불일치#

캐시 불일치가 발생하면 다음 단계에 따라 문제를 해결합니다.

캐시 불일치의 원인 해결 방법
공유 캐시 없이 하나의 프로젝트에 연결된 여러 독립 실행형 러너(자동 확장 모드가 아닌)를 사용하는 경우. 프로젝트에 러너 하나만 사용하거나 분산 캐시가 활성화된 여러 러너를 사용합니다.
분산 캐시가 활성화되지 않은 자동 확장 모드의 러너를 사용하는 경우. 자동 확장 러너가 분산 캐시를 사용하도록 구성합니다.
러너가 설치된 머신의 디스크 공간이 부족하거나, 분산 캐시를 설정한 경우 캐시가 저장되는 S3 버킷의 공간이 충분하지 않은 경우. 새 캐시를 저장할 수 있도록 공간을 확보합니다. 이를 자동으로 수행하는 방법은 없습니다.
서로 다른 경로를 캐시하는 Job에 동일한 key를 사용하는 경우. 캐시 아카이브가 다른 위치에 저장되어 잘못된 캐시를 덮어쓰지 않도록 서로 다른 캐시 키를 사용합니다.
러너에서 분산 러너 캐싱을 활성화하지 않은 경우. Shared = false로 설정하고 러너를 다시 프로비저닝합니다.

캐시 불일치 예시 1#

프로젝트에 러너가 하나만 할당된 경우, 캐시는 기본적으로 해당 러너의 머신에 저장됩니다.

두 Job이 동일한 캐시 키를 가지지만 경로가 다르면 캐시가 덮어쓰여질 수 있습니다. 예를 들면 다음과 같습니다:

stages:
  - build
  - test

job A:
  stage: build
  script: make build
  cache:
    key: same-key
    paths:
      - public/

job B:
  stage: test
  script: make test
  cache:
    key: same-key
    paths:
      - vendor/
  1. job A가 실행됩니다.
  2. public/이 cache.zip으로 캐시됩니다.
  3. job B가 실행됩니다.
  4. 이전 캐시가 있으면 압축이 풀립니다.
  5. vendor/가 cache.zip으로 캐시되어 이전 캐시를 덮어씁니다.
  6. 다음에 job A가 실행될 때는 job B의 캐시를 사용하게 되는데, 이는 서로 달라서 효과가 없습니다.

이 문제를 해결하려면 각 Job에 서로 다른 keys를 사용합니다.

캐시 불일치 예시 2#

이 예시에서는 프로젝트에 러너가 두 개 이상 할당되어 있고 분산 캐시가 활성화되어 있지 않습니다.

두 번째로 파이프라인이 실행될 때 job A와 job B가 (이 경우 서로 다른) 캐시를 재사용하기를 원합니다:

stages:
  - build
  - test

job A:
  stage: build
  script: build
  cache:
    key: keyA
    paths:
      - vendor/

job B:
  stage: test
  script: test
  cache:
    key: keyB
    paths:
      - vendor/

key가 다르더라도, 후속 파이프라인에서 Job이 서로 다른 러너에서 실행되면 각 Stage 이전에 캐시된 파일이 "정리"될 수 있습니다.

동시 실행 러너에서 로컬 캐시가 누락되는 경우#

Docker 실행기로 여러 개의 동시 실행 러너를 구성한 경우, 동시에 실행 중인 Job에서 예상과 달리 로컬에 캐시된 파일이 없을 수 있습니다. 캐시 볼륨의 이름은 러너 인스턴스마다 고유하게 생성되므로, 한 러너 인스턴스가 캐시한 파일을 다른 러너 인스턴스의 캐시에서는 찾을 수 없습니다.

동시 실행 러너 간에 캐시를 공유하려면 다음 중 하나를 수행합니다:

  • 러너의 config.toml에 있는 [runners.docker] 섹션을 사용하여, 각 컨테이너의 /cache에 매핑되는 호스트의 단일 마운트 지점을 구성합니다(예: volumes = ["/mnt/gitlab-runner/cache-for-all-concurrent-jobs:/cache"]). 이 방법은 러너가 동시 실행되는 Job마다 고유한 볼륨 이름을 만드는 것을 방지합니다.
  • 분산 캐시를 사용합니다.