🐷

Shared Runner , self-Managed Runner 間キャッシュ共有の方法

2023/09/22に公開

GitLab CI で self-Managed Runner を構築していたら、キャッシュの受け渡しが上手く動かなかったので、模索した話

tl;dr

  1. Shared,self-Managed 間はcacheではなく artifactsを使おう
  2. artifactsをやりとりするjobは必ず後に実行されるようにしよう
  3. 受け取り側のjobはdependenciesを使おう。stageを後ろにしても良い
  4. Runner 間のデータのやり取りのTopicはマニュアルには書いてなさそう

やりたいこと

Shared Runner で ビルドした生成物を キャッシュの受け渡しで self-Managed Runnerに渡してデプロイしたい

背景

元々開発環境にはJenkinsを入れて、ビルド&デプロイをしていたんですが...

  1. 開発環境側でビルドする必要もない
  2. Node.js系のビルドは回数を重ねるとメモリを圧迫して定期的な再起動が必要

ってところが課題に上がっていてCIツール側でビルドしてからデプロイしたい、
GitLab CIに寄せたいと言うことで、
開発環境のインスタンスに Runner Install して、既に組んであるパイプラインに開発環境のデプロイフローを追加したくなりました。

とりあえずデプロイしてみる

と言うわけでとりあえずRunner登録して、デプロイしてみる

Shared Runner のCacheが取れない

config.tomlがデフォルトの状態の実行ログ
Checking cache for caches-feature/ci/change_ci_path-36148020-npm-18-nuxt3_output-non_protected...
No URL provided, cache will not be downloaded from shared cache server. Instead a local version of cache will be extracted. 
  • Shared Runner にあった、Google Storageへのリンクが取れない
Shared Runnerの場合
Checking cache for caches-feature/ci/change_ci_path-36148020-npm-18-nuxt3-non_protected...
Downloading cache from https://storage.googleapis.com/gitlab-com-runners-cache/project/36148020/caches-feature/ci/change_ci_path-36148020-npm-18-nuxt3-non_protected 
Successfully extracted cache

どうもマニュアルを調べたところ self-Managed Runner のconfig.toml の設定をShared Runner のGCS設定に揃える必要がある様子。

参考:The [runners.cache.gcs] section

config.toml 例
[runners.cache]
  Type = "gcs"
  Path = "path/to/prefix"
  Shared = false
  [runners.cache.gcs]
    AccessID = "cache-access-account@test-project-123456.iam.gserviceaccount.com"
    PrivateKey = "-----BEGIN PRIVATE KEY-----\nXXXXXX\n-----END PRIVATE KEY-----\n"
    BucketName = "runners-cache"

ということで取得できないのでcache指定は使えない となると、他の選択肢は

  1. artifactsを使う
  2. 自前でHost する

ぐらいかなと思う。
S3/R2などにHostしてもいいけど、どうせならGitLabで完結したいので、artifactsを使う方向で考える

APIでartifactsを取得してみる

まず、Job Artifacts APIがあるので、試してみる。

sample
artifact_download:
  stage: test
  script:
    - 'curl --location --output artifacts.zip --header "JOB-TOKEN: $CI_JOB_TOKEN" "https://gitlab.example.com/api/v4/projects/1/jobs/42/artifacts"'

JOB-TOKENしてもあり、これが正式な方法なのかな?って思いつつハードコーディングされているところを変数に落とし込んで、

実際に叩いてみたcurlコマンド
curl --verbose\
     --location\
     --output artifacts.zip\
     --header "JOB-TOKEN: $CI_JOB_TOKEN"\
     "$CI_SERVER_URL/api/v4/projects/$CI_PROJECT_ID/jobs/$CI_JOB_ID/artifacts"

こんな感じで、やってみる。

上手くいかない・・・

どうも

artifactsを作るJobと受け取るJobで、IDが別なのでJob IDを指定できない

ってことで他の手がないかちょっと考える

ChatGPT先生に聞いてみる

ChatGPT先生に確認してみたら、この手法が良さそうだったので試してみる

  1. アーティファクトを使用する方法:

    1. 最初のジョブで、必要な情報を含むファイルを生成してアーティファクトとして保存します。
    2. 次のジョブで、アーティファクトを取得し、その情報を使用します。

    例えば、最初のジョブでファイルを生成し、アーティファクトとして保存するコマンドは以下のようになります:

    job1:
      script:
        - echo "ジョブID: $CI_JOB_ID" > job_info.txt
      artifacts:
        paths:
          - job_info.txt
    

    次のジョブでアーティファクトを取得するコマンドは以下のようになります:

    job2:
      script:
        - cat job_info.txt
      dependencies:
        - job1
    

参考:dependencies

In this example, two jobs have artifacts: build osx and build linux. When test osx is executed,
the artifacts from build osx are downloaded and extracted in the context of the build.

The same thing happens for test linux and artifacts from build linux.

The deploy job downloads artifacts from all previous jobs because of the stage precedence.

  1. dependenciesartifacts を生成しているjobを指定するとダウンロードして展開する
  2. 後段のstage は 前stageまでに作られたartifacts全てをダウンロードして展開する

dependenciesを使ってみる

結論から言うと上手くいった。

実際に使ったCIスクリプト
generate:
  stage: build
  needs: [ "prepare" ]
  script:
    - npx nuxt generate
  cache:
    - key: caches-$CI_COMMIT_REF_NAME-$CI_PROJECT_ID-$JS_PKG-$NODE_VER-node_modules
      paths:
        - node_modules
      policy: pull
    - key: caches-$CI_COMMIT_REF_NAME-$CI_PROJECT_ID-$JS_PKG-$NODE_VER-nuxt3
      paths:
        - .nuxt
      policy: pull
  artifacts:
    name: caches-$CI_COMMIT_REF_NAME-$CI_PROJECT_ID-$JS_PKG-$NODE_VER-nuxt3_output
    paths:
      - .output

deploy:instance:dev:
  stage: deploy
  needs: [ "generate"]
  variables:
    GIT_STRATEGY: none
  dependencies:
    - generate
  before_script:
    - rm -rf /var/www/git-vue
    - mkdir /var/www/git-vue
  script:
    - cp -r ./.output /var/www/git-vue/
    - ls -la /var/www/git-vue/
  tags:
    - $CI_DEPLOY_DEV_TAG
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
      when: manual

手順

  1. generate ジョブで .outputartifacts
  2. dependenciesgenerateに設定し、artifactsをダウンロードできるようにする

ログ

Jobのログ
Downloading artifacts for generate (5139095646)...
Downloading artifacts from coordinator... ok        host=cdn.artifacts.gitlab-static.net id=5139095646 responseStatus=200 OK token=64_GGSVJ

Executing "step_script" stage of the job script

$ rm -rf /var/www/git-vue
$ mkdir /var/www/git-vue
$ cp -r ./.output /var/www/git-vue/
$ ls -la /var/www/git-vue/
合計 12
drwxrwxr-x  3 www-data www-data 4096  9月 21 23:40 .
drwxr-xr-x 17 www-data www-data 4096  9月 21 23:40 ..
drwxr-xr-x  4 www-data www-data 4096  9月 21 23:40 .output
  • .outputが正しくコピーされている

後課題

artifactsでキャッシュを渡せるようになりましたが、artifactsは最新以外は破棄される設定になっていたり、cacheより少し使い勝手が悪いので、
開発時にエラーが出ないような設定を模索する必要があるので、その辺りの運用も答えが出たら追記したいと思います。

GitHubで編集を提案

Discussion