CI/CD 캐싱 예시
CI/CD 캐싱 예시 (CI/CD caching examples)
캐싱을 쓰면 job이 실행될 때마다 의존성과 빌드 산출물을 다시 내려받지 않아도 돼요. 이전에 받아 둔 콘텐츠를 재사용해서 CI/CD 파이프라인을 빠르게 만들어 주는 것이죠. 더 많은 예시는 GitLab CI/CD 템플릿을 참고하세요.
출처: 문서
본문
캐시 전략 (Cache strategies)
이 예시들은 job과 브랜치 사이에서 캐시를 공유하는 여러 접근 방식을 보여줘요.
같은 브랜치의 job들 사이에서 캐시 공유 (Share caches between jobs in the same branch)
각 브랜치의 job들이 같은 캐시를 쓰게 하려면 key: $CI_COMMIT_REF_SLUG으로 캐시를 정의하세요.
cache:
key: $CI_COMMIT_REF_SLUG
이 구성은 실수로 캐시를 덮어쓰는 걸 막아줘요. 다만 MR의 첫 파이프라인은 느립니다. 브랜치에 커밋을 다시 푸시하면 그다음부터 캐시가 재사용되어 job이 빨라져요. job별·브랜치별 캐싱을 활성화하려면 이렇게 합니다.
cache:
key: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
스테이지별·브랜치별 캐싱을 활성화하려면 이렇게 합니다.
cache:
key: "$CI_JOB_STAGE-$CI_COMMIT_REF_SLUG"
다른 브랜치의 job들 사이에서 캐시 공유 (Share caches across jobs in different branches)
모든 브랜치·모든 job에 캐시를 공유하려면 모든 곳에서 같은 키를 쓰면 돼요.
cache:
key: one-key-to-rule-them-all
브랜치 사이에는 캐시를 공유하되 job마다 고유한 캐시를 쓰려면 이렇게 합니다.
cache:
key: $CI_JOB_NAME
변수로 job의 캐시 정책 제어 (Use a variable to control a job's cache policy)
pull 정책만 다른 중복 job을 줄이려면 CI/CD 변수를 사용할 수 있어요. 예를 들면 이렇습니다.
conditional-policy:
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
variables:
POLICY: pull-push
- if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH
variables:
POLICY: pull
stage: build
cache:
key: gems
policy: $POLICY
paths:
- vendor/bundle
script:
- echo "This job pulls and pushes the cache depending on the branch"
- echo "Downloading dependencies..."
이 예시에서 job의 캐시 정책은:
- 기본 브랜치 변경에는
pull-push. - 다른 브랜치 변경에는
pull.
의존성 캐시 (Cache dependencies)
이 예시들은 프로그래밍 언어별로 일반적인 의존성 캐싱 방법을 보여줘요.
Node.js
프로젝트가 npm으로 Node.js 의존성을 설치한다면, 다음 예시처럼 모든 job이 상속받는 기본 cache를 정의하면 돼요. 기본적으로 npm은 홈 폴더(~/.npm)에 캐시 데이터를 저장해요. 하지만 프로젝트 디렉터리 밖은 캐시할 수 없어요. 대신 npm이 ./.npm을 쓰도록 하고 브랜치별로 캐시하세요.
default:
image: node:latest
cache: # Cache modules in between jobs
key: $CI_COMMIT_REF_SLUG
paths:
- .npm/
before_script:
- npm ci --cache .npm --prefer-offline
test_async:
script:
- node ./specs/start.js ./specs/async.spec.js
잠금 파일에서 캐시 키 계산 (Compute the cache key from the lock file)
cache:key:files를 쓰면 package-lock.json이나 yarn.lock 같은 잠금 파일로 캐시 키를 계산해서 여러 job에서 재사용할 수 있어요.
default:
cache: # Cache modules using lock file
key:
files:
- package-lock.json
paths:
- .npm/
오프라인 미러와 함께 쓰는 Yarn (Yarn with offline mirror)
Yarn을 쓴다면 yarn-offline-mirror로 압축된 node_modules tarball을 캐시할 수 있어요. 압축해야 할 파일이 더 적어서 캐시 생성이 더 빨라집니다.
job:
script:
- echo 'yarn-offline-mirror ".yarn-cache/"' >> .yarnrc
- echo 'yarn-offline-mirror-pruning true' >> .yarnrc
- yarn install --frozen-lockfile --no-progress
cache:
key:
files:
- yarn.lock
paths:
- .yarn-cache/
PHP
프로젝트가 Composer로 PHP 의존성을 설치한다면, 다음 예시처럼 모든 job이 상속받는 기본 cache를 정의하면 돼요. PHP 라이브러리는 vendor/에 설치되고 브랜치별로 캐시됩니다.
default:
image: php:latest
cache: # Cache libraries in between jobs
key: $CI_COMMIT_REF_SLUG
paths:
- vendor/
before_script:
# Install and run Composer
- curl --show-error --silent "https://getcomposer.org/installer" | php
- php composer.phar install
test:
script:
- vendor/bin/phpunit --configuration phpunit.xml --coverage-text --colors=never
Python
프로젝트가 pip으로 Python 의존성을 설치한다면, 다음 예시처럼 모든 job이 상속받는 기본 cache를 정의하면 돼요. pip의 캐시는 .cache/pip/ 아래에 정의되고 브랜치별로 캐시됩니다.
default:
image: python:latest
cache: # Pip's cache doesn't store the python packages
paths: # https://pip.pypa.io/en/stable/topics/caching/
- .cache/pip
before_script:
- python -V # Print out python version for debugging
- pip install virtualenv
- virtualenv venv
- source venv/bin/activate
variables: # Change pip's cache directory to be inside the project directory because GitLab can only cache local items.
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
test:
script:
- python setup.py test
- pip install ruff
- ruff --format=gitlab .
Ruby
프로젝트가 Bundler로 gem 의존성을 설치한다면, 다음 예시처럼 모든 job이 상속받는 기본 cache를 정의하면 돼요. gem은 vendor/ruby/에 설치되고 브랜치별로 캐시됩니다.
default:
image: ruby:latest
cache: # Cache gems in between builds
key: $CI_COMMIT_REF_SLUG
paths:
- vendor/ruby
before_script:
- ruby -v # Print out ruby version for debugging
- bundle config set --local path 'vendor/ruby' # The location to install the specified gems to
- bundle install -j $(nproc) # Install dependencies into ./vendor/ruby
rspec:
script:
- rspec spec
다른 gem이 필요한 job들이 있다면 전역 cache 정의에서 prefix 키워드를 사용하세요. 이 구성은 job마다 다른 캐시를 만들어 줍니다. 예를 들어 테스트 job은 프로덕션에 배포하는 job과 다른 gem이 필요할 수 있어요.
default:
cache:
key:
files:
- Gemfile.lock
prefix: $CI_JOB_NAME
paths:
- vendor/ruby
test_job:
stage: test
before_script:
- bundle config set --local path 'vendor/ruby'
- bundle install --without production
script:
- bundle exec rspec
deploy_job:
stage: production
before_script:
- bundle config set --local path 'vendor/ruby' # The location to install the specified gems to
- bundle install --without test
script:
- bundle exec deploy
Go
프로젝트가 Go Modules로 Go 의존성을 설치한다면, 다음 예시처럼 어떤 job이든 확장할 수 있는 go-cache 템플릿에 cache를 정의해요. Go 모듈은 ${GOPATH}/pkg/mod/에 설치되고 모든 go 프로젝트에 대해 캐시됩니다.
.go-cache:
variables:
GOPATH: $CI_PROJECT_DIR/.go
before_script:
- mkdir -p .go
cache:
paths:
- .go/pkg/mod/
test:
image: golang:latest
extends: .go-cache
script:
- go test ./... -v -short
빌드 산출물과 다운로드 캐시 (Cache build artifacts and downloads)
이 예시들은 빌드를 빠르게 하기 위해 컴파일된 객체와 내려받은 파일을 캐시하는 방법을 보여줘요.
Ccache로 C/C++ 컴파일 캐시 (Cache C/C++ compilation using Ccache)
C/C++ 프로젝트를 컴파일한다면 Ccache로 빌드 시간을 단축할 수 있어요. Ccache는 이전 컴파일을 캐시하고 같은 컴파일이 다시 수행되는 걸 감지해서 재컴파일을 빠르게 합니다. Linux 커널 같은 큰 프로젝트를 빌드할 때 상당히 빨라지는 걸 기대할 수 있어요. 만들어진 캐시를 job 사이에서 재사용하려면 cache를 사용하면 됩니다. 예를 들면 이렇습니다.
job:
cache:
paths:
- ccache
before_script:
- export PATH="/usr/lib/ccache:$PATH" # Override compiler path with ccache (this example is for Debian)
- export CCACHE_DIR="${CI_PROJECT_DIR}/ccache"
- export CCACHE_BASEDIR="${CI_PROJECT_DIR}"
- export CCACHE_COMPILERCHECK=content # Compiler mtime might change in the container, use checksums instead
script:
- ccache --zero-stats || true
- time make # Actually build your code while measuring time and cache efficiency.
- ccache --show-stats || true
단일 저장소에 여러 프로젝트가 있다면 각각에 별도 CCACHE_BASEDIR를 둘 필요는 없어요.
cURL로 다운로드 캐시 (Cache downloads with cURL)
프로젝트가 cURL로 의존성이나 파일을 내려받는다면 다운로드한 콘텐츠를 캐시할 수 있어요. 더 새로운 다운로드가 가능해지면 파일이 자동으로 갱신됩니다.
job:
script:
- curl --remote-time --time-cond .curl-cache/caching.md --output .curl-cache/caching.md "https://docs.gitlab.com/ci/caching/"
cache:
paths:
- .curl-cache/
이 예시에서 cURL은 웹서버에서 파일을 내려받아 .curl-cache/의 로컬 파일로 저장해요. --remote-time 플래그는 서버가 보고한 마지막 수정 시간을 저장하고, cURL은 --time-cond로 캐시된 파일의 타임스탬프와 비교합니다. 원격 파일의 타임스탬프가 더 최신이면 로컬 캐시가 자동으로 갱신돼요.
더 알아보기
캐시 키를 어떻게 잡느냐가 캐시 전략의 핵심이에요. $CI_COMMIT_REF_SLUG로 브랜치별, $CI_JOB_NAME으로 job별로 공유 범위를 나눌 수 있고, 잠금 파일(cache:key:files)로 의존성 변경 시점에만 캐시가 갱신되게 할 수도 있어요. 언어별로 npm·Composer·pip·Bundler·Go 모듈의 기본 캐시 경로를 맞춰 보고, Ccache나 cURL처럼 산출물 캐싱도 함께 적용해 보세요.