🦊

GitLab CIをローカル環境で動かす<gitlab-ci-local>

に公開

はじめに

横浜銀行アジャイル開発チームのwatariです。
本記事では意欲的なツール、gitlab-ci-localの導入方法と活用ポイントをご紹介します。

GitLab CIをはじめ、例えばCircle CI、あるいはGithubActions。
多くの場合、まずは各サービスのCIサーバ上でジョブを実行するでしょう。
上手くいけば良いのですが、失敗した場合はエラーの調査・対応が必要です。

そのたびに、場合によって無料ではないクレジットがかかり、またコミットログが汚れていきます。
※コミットログは後でまとめられるにせよ、です。

こういった課題を解決するために、ローカル環境でCIパイプラインの挙動を再現し、効率的に検証・デバッグできる方法を検討しました。

ツール候補

  • GitLabの公式Runner(GitLab Runner)
    GitLabの公式サイトによると、ローカルで動かせるということがわかったので検証しました。
    確かにローカルで動くんですが、動かすにはpushしないといけません。
    pushした結果、Runnerに登録されたローカルのIPを探しにいってCIがローカルで実行されるものに見えます。
    ローカルのみで完結しないため、少し期待とは異なるものでした。
    ※1.16より前のrunnerだと、execというコマンドでローカル実行できたようです。

  • gitlab-ci-local
    本記事で紹介するgitlab-ci-localです。
    README冒頭の以下記載から、まさしく本課題を解決できるものと判断し、採用しました。

    firecow/gitlab-ci-local: Tired of pushing to test your .gitlab-ci.yml?

gitlab-ci-localのインストール

インストールはREADMEにある通りです。
WindowsのWSL(Ubuntu)環境を前提とし、Debianの手順でインストールします。

sudo wget -O /etc/apt/sources.list.d/gitlab-ci-local.sources https://gitlab-ci-local-ppa.firecow.dk/gitlab-ci-local.sources
sudo apt-get update
sudo apt-get install gitlab-ci-local

※WindowsやMacの手順もREADMEにあります!

ローカル実行

実行そのものはとても簡単でフレンドリーです。
gitlab-ci.ymlが置かれているディレクトリ(ほとんどの場合でプロジェクトルート)に移動して、以下を実行するのみです。

gitlab-ci-local {job}

複数実行も繋げるだけです。これはパイプラインの条件に縛られません。
例えばマージリクエスト作成時に走る、タグpush時に走る、といった条件の異なるジョブを一緒に動かせます。

gitlab-ci-local {job1} {job2}

その他の挙動についてはgitlab-ci-local --help打てばusage出るので、そこから辿れます。

実行時のポイント

はまったポイント、動作の癖などをいくつか記載します。

並列実行

パイプラインというのは(少なからずbashやElixirなどのパイプからイメージすると)順々に実行されることを期待します。
ただ、これはデフォルトで並列実行のようです。
そのため以下のオプションを指定しました。
公式のほうで説明されているわけでなく少し強引ですが、シーケンシャルに動きます。

gitlab-ci-local --concurrency 1 {job1} {job2}
  • --concurrency:並列に実行される数を指定
    1を指定することで、直列になります。

dindを使用したjobの試行

docker in dockerについてです。
※dindの詳細な解説は本記事の範囲外としますので、必要に応じて関連ドキュメントをご参照ください。

これはenvファイルが必要です。幸い、公式にがあります。

PRIVILEGED=true
ULIMIT=8000:16000
VOLUME=certs:/certs/client
VARIABLE="DOCKER_TLS_CERTDIR=/certs"

これは.gitlab-ci.ymlと同じディレクトリにgitlab-ci-local-envファイルを置けば良いです。

設定値については、dindだけの設定ならPRIVILEGEDULIMITだけで足ります。

VOLUMEVARIABLEは、tlsの設定があるときに必要で、certsは証明書です。
プロジェクトではno-tlsで扱っていたため、これら設定を入れていると逆に動きませんので消しました。

Issue?

GitLab CIで動くけれど、gitlab-ci-localだと動かない、という以下のような事象がありました。

rulesの記述だとartifactsが渡されない

例えばこのような記載の時、check-artifactsジョブのlsコマンドでhoge.txtを確認できないためartifactsは連携されません。

  • artifactsの連携
stages:
  - move
  - check
move-artifacts:
  stage: move
  image: mirror.gcr.io/cimg/node:20.19
  script:
    - mkdir test
    - touch test/hoge.txt
    - ls test/
  artifacts:
    paths:
      - test/
    expire_in: 1 hour
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
      when: always
  # only:
  #   refs:
  #     - merge_requests
check-artifacts:
  stage: check
  image: mirror.gcr.io/cimg/node:20.19
  dependencies:
    - move-artifacts
  script:
    - ls test/

ただ、yml中コメントアウトしているonlyを、rulesと置き換えるとcheck-artifactsジョブでhoge.txtを確認できます。

rulesとartifactsは、あまり相関がない(あるならdependenciesやneedなど)と思っていました。
この考えのもと、onlyを使っているほうのjobは万事問題なく動くので、dependenciesとneedsを入れ替えたり変数別で定義したりと試行錯誤が続いたため、参考までに記載します。

GitLab CIにおいては、artifactsはどちらでも引き継がれます。
この辺りは、ローカルを模したものなので完璧でない部分はあるでしょう。
※ちなみに、onlyは(exceptも)非推奨とされています。
 特段事情がなければ、rulesのほうを使いましょう。

本事象は、上記の簡単なサンプルで検証できました。
もう少し詳細を把握したのちに、必要であれば公式Issueに展開を検討します。

まとめ

本記事で紹介したgitlab-ci-localは、CI/CDの検証作業を効率化し、不要なリモートpushやコミットログの煩雑化を防ぐ上で非常に有用です。

特にlintやユニットテストなどの工程では、ローカル環境での迅速なフィードバックが可能となるため、開発効率向上に寄与します。
なお、本ツールを使ったCD・デプロイ工程のローカル実行には注意が必要です(ローカルマシンから対象にアクセスできる場合のみ可能ですが、問題ないかは都度確認が必要です)。

横浜銀行(内製担当有志)Tech Blog

Discussion