Ну че типа разберемся с кэшом…

В качестве основы возьмем статью GitLab CI speeding up your pipeline with caching

Чуток модифицируем это дело вообщем поехали…

Пару слов о конфигурации

Нам понадобится Gitlab Runner + minio это нужно для использования fallback_keys в конфигурации кэша (ниже будет подробнее).
В доке к сожалению подробностей нет но можно почитать тут Fallback cache key only works for distributed cache.
Тут писать что-то о настройке gitlab-runer + minio мы не будем просто приведу конфиг

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
listen_address = "runner.local:9252"
concurrent = 20
check_interval = 2

[session_server]
  session_timeout = 1800

[[runners]]
  name = "runner"
  url = "https://gitlab.local/"
  limit = 20
  request_concurrency = 10
  id = 521
  token = "xdfsFGcvdfsvffdsF"
  token_obtained_at = 2023-09-19T14:51:02Z
  token_expires_at = 0001-01-01T00:00:00Z
  tls-ca-file = "/media/data/gitlab-runner/config/certs/gitlab.pem"
  executor = "shell"
  environment = ["GIT_SSL_NO_VERIFY=1"]
  [runners.cache]
    MaxUploadedArchiveSize = 0
    Type = "s3"
    Shared = true
    [runners.cache.s3]
      AccessKey = "xzgxgdfgdfg"
      SecretKey = "dxdgdxgGfxzdhf"
      BucketName = "runner"
      Insecure = true
      ServerAddress = "runner.local:80"

Тестовый конфиг для GitLab CI

Тут стоит у помянуть что у нас будут “псевдошаги” те по факту будем просто писать и выводить файл. В краце опишем что мы хотим

Скажем у нас есть 2 ветки:

  • master
  • dev

Stage + Variables

Фиг знает че тут сказать вообще то нечего… ну разве что по переменным.

DEFAULT_CACHE_KEY_PREFIX - эт типа для fallback кэша т.е. если мы не будем собирать кэш то от куда его брать.
CACHE_KEY_PREFIX - тут просто префикс для кэша.
DEPS_FOLDER - директория которую будем кэшировать.

1
2
3
4
5
6
7
8
9
stages:
  - prepare
  - build
  - test

variables:
  DEFAULT_CACHE_KEY_PREFIX: ${CI_DEFAULT_BRANCH}
  CACHE_KEY_PREFIX: ${CI_COMMIT_REF_SLUG}
  DEPS_FOLDER: ${CI_PROJECT_DIR}/.deps

Когда собирать кэш?

Собирать кэш будем в 2х случаях.

  1. В случае билда из ветки мастер
  2. Если мы билдим любую другую ветку при этом у нас файл my-referent-file.lock изменился в сравнии с файлом из ветки master doc.

За это дело у нас будут отвечать следующий кусок конфига

1
2
3
4
5
6
7
8
9
10
11
.deps_update_rules:
  rules:
    - if: $CI_COMMIT_BRANCH != "master"
      changes:
        paths:
          - my-referent-file.lock
        compare_to: refs/heads/master
    - if: $CI_COMMIT_BRANCH == "master"
      when: always
    - if: $CI_COMMIT_TAG != null
      when: always

Anchor для сборки кэша

Тут у нас пару кэшей будет формироваться.

  1. ${CACHE_KEY_PREFIX}-deps-dev
  2. ${CACHE_KEY_PREFIX}-deps-run

(Их 2 не потому что тут что-то хитрожопое просто в изначальной статье их зачем то сделали 2, ну и для наглядности пойдет)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.deps_dev_cache:
  cache: &deps_dev_cache
    key: ${CACHE_KEY_PREFIX}-deps-dev
    fallback_keys:
      - ${DEFAULT_CACHE_KEY_PREFIX}-deps-dev
    paths:
      - ${DEPS_FOLDER}
    policy: pull

.deps_run_cache:
  cache: &deps_run_cache
    key: ${CACHE_KEY_PREFIX}-deps-run
    fallback_keys:
      - ${DEFAULT_CACHE_KEY_PREFIX}-deps-run
    paths:
      - ${DEPS_FOLDER}
    policy: pull

Собственно че тут написанно про сами anchors (&deps_dev_cache/&deps_run_cache) писать не буду можно почитать тут. Тут просто нужно знать что anchors пишутся для того чтобы можно бы ло переиспользовать данный контент где то ниже.

Соответственно тут у нас “контент” для формирования кэша:

  • key: ${CACHE_KEY_PREFIX}-deps-dev - название кэша
  • fallback_keys: - ${DEFAULT_CACHE_KEY_PREFIX}-deps-dev - если не найден кэш ${CACHE_KEY_PREFIX}-deps-dev то попробовать взять кэш из fallback_keys
  • paths: ${DEPS_FOLDER} - путь который будем кэшировать
  • policy - забирать пулять вообщем типа че делать с кэшом, данное свойство будем переопредлять по ходу написания конфига.

Сбор кэша

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
dev_dependencies:
  stage: prepare
  script:
    - echo "I am a dependency" > ./.deps/deps.txt
    - echo "I am a dev dependency" >> ./.deps/deps.txt
  cache:
    <<: *deps_dev_cache
    policy: pull-push
  rules: !reference [.deps_update_rules, rules]

run_dependencies:
  stage: prepare
  script:
    - echo "I am a dependency" > ./.deps/deps.txt
  cache:
    <<: *deps_run_cache
    policy: pull-push
  rules: !reference [.deps_update_rules, rules]

Тут мы формируем 2 кэша путем использования упомянутых выше anchors. В зависимости от rules

  • script - ну типа скрипт че тут еще сказать
  • cache - тут берем контент из anchor deps_dev_cache
  • policy - тут подменяем policy взятое из anchors на ту которая позваляет так же засывывать кэш

Вывод файла из кэша

1
2
3
4
5
6
7
8
9
10
11
12
13
test-app:
  stage: test
  script:
    - cat ./.deps/deps.txt
  cache:
    <<: *deps_dev_cache

package-app:
  stage: build
  script:
    - cat ./.deps/deps.txt
  cache:
    <<: *deps_run_cache

тут хз че сказать просто скрипт который выводит содержимое файла при этом опять использует кэши из anchors deps_dev_cache и deps_run_cache.

Full config

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
stages:
  - prepare
  - build
  - test

variables:
  DEFAULT_CACHE_KEY_PREFIX: ${CI_DEFAULT_BRANCH}
  CACHE_KEY_PREFIX: ${CI_COMMIT_REF_SLUG}
  DEPS_FOLDER: ${CI_PROJECT_DIR}/.deps

.deps_update_rules:
  rules:
    - if: $CI_COMMIT_BRANCH != "master"
      changes:
        paths:
          - my-referent-file.lock
        compare_to: refs/heads/master
    - if: $CI_COMMIT_BRANCH == "master"
      when: always
    - if: $CI_COMMIT_TAG != null
      when: always

.deps_dev_cache:
  cache: &deps_dev_cache
    key: ${CACHE_KEY_PREFIX}-deps-dev
    fallback_keys:
      - ${DEFAULT_CACHE_KEY_PREFIX}-deps-dev
    paths:
      - ${DEPS_FOLDER}
    policy: pull

.deps_run_cache:
  cache: &deps_run_cache
    key: ${CACHE_KEY_PREFIX}-deps-run
    fallback_keys:
      - ${DEFAULT_CACHE_KEY_PREFIX}-deps-run
    paths:
      - ${DEPS_FOLDER}
    policy: pull

dev_dependencies:
  stage: prepare
  script:
    - echo "I am a dependency" > ./.deps/deps.txt
    - echo "I am a dev dependency" >> ./.deps/deps.txt
  cache:
    <<: *deps_dev_cache
    policy: pull-push
  rules: !reference [.deps_update_rules, rules]

run_dependencies:
  stage: prepare
  script:
    - echo "I am a dependency" > ./.deps/deps.txt
  cache:
    <<: *deps_run_cache
    policy: pull-push
  rules: !reference [.deps_update_rules, rules]

test-app:
  stage: test
  script:
    - cat ./.deps/deps.txt
  cache:
    <<: *deps_dev_cache

package-app:
  stage: build
  script:
    - cat ./.deps/deps.txt
  cache:
    <<: *deps_run_cache

Test Runs

Попробуем позапускать это дело.

Запуск master ветки

Предположим что на старте у нас есть две абсолютно одинаковые ветки

  1. master
  2. dev

Нам полюбому надо сначала запустить pipeline по ветке master как минимум для того что бы у нас сформировался кэш. Запускаем видимо что все хорошо

img

Запуск dev ветки

Далее запускаем по ветке dev видим что кэши не собирались при этом все шаги отработали хорошо потому что исползован был fallback cache т.е в нашем случае кэш из ветки мастер:

1
2
    fallback_keys:
      - ${DEFAULT_CACHE_KEY_PREFIX}-deps-dev

img

В логе джоба можно увидеть что кэш для dev ветки не найден, и соответствено был звят кэш из ветки master (напомню что для этого надо s3 хранилище) img

Run dev branch after update file my-referent-file.lock

Попробуем поправить файл my-referent-file.lock в dev ветке собственнааа для того что бы собрался отдельный кэш для этой ветки (я просто добавил туда пару строк)… пушим… смотрим результат:

img

P.S.

Всем спасибо!)