TT Lab
はじめる
学ぶ 学習パス コース

GitLab CI/CD

パイプラインをファイルで書くようになった理由

TT Labで続きを見る

一言でいうと

.gitlab-ci.ymlは、ビルドサーバーの設定をリポジトリの中に引き込み、コードと同じレビュー、同じ履歴、同じリバートを受けられるようにしたファイルです。

なぜ必要なのか

ビルドサーバーを画面から設定していた時代の事故は、たいてい一言で片づきました。「昨日まで動いていたのに今日は動かない。誰が何を変えたのかわからない。」ジョブの設定がリポジトリの外のデータベースにあったからです。その設定にはdiffがなく、レビューがなく、ブランチがなく、元に戻すコミットもありません。誰かがチェックボックスを1つ外したという事実は、事故が起きたあとになって、それも記憶に頼って明らかになります。

さらに深刻なのは、ブランチがないことでした。コードはブランチごとに違うのにパイプラインは1つだけなので、新しいテストのステージを追加するには、すべてのブランチのビルドを同時に変えなければなりませんでした。そのため人々はパイプラインに手を触れなくなり、パイプラインは何年も前に誰かが作ったまま固まります。固まったパイプラインは、やがて誰も信じないパイプラインになります。

設定をリポジトリ内のファイルに移すと、この4つが一度に解決します。ブランチごとに異なるパイプラインを持てます。マージリクエストでパイプラインの変更そのものをレビューできます。間違っていたときはコミットをリバートすればパイプラインも一緒に戻ります。何がいつなぜ変わったのかがGitログに残ります。

どう動くのか

GitLabはリポジトリのルートにある.gitlab-ci.ymlを読んで、パイプラインを1つ作ります。ここで重要なのは順序です。コミットがプッシュされると、GitLabはまずそのコミットにある設定ファイルを読み、その内容でどのジョブを作るかを決めたあとで、作られたジョブをランナーに割り振ります。つまり、設定はジョブが始まる前にすべて評価されています。あとで学ぶrulesが「このジョブを実行するか」ではなく「このジョブを作るか」を決めるのも、この順序のためです。

ファイルの文法は、YAMLのマッピング1つです。トップレベルのキー1つがジョブ1つで、予約された名前がいくつか(stages、variables、default、include、workflow)だけが、ジョブではないグローバル設定として使われます。この単純さは長所であり、落とし穴でもあります。インデントを1つ間違えるとジョブがまるごと消えますが、YAML自体は何のエラーも出しません。ファイルは問題なくパースされ、ただそのジョブが別のキーの下位項目になってしまうだけです。

このコースで扱う範囲を、最初にはっきりさせておきます。このラボ環境にはGitLabサーバーもGitLab Runnerもありません。そのため、パイプラインが実際に動く場面は見られず、動いているふりをする偽のランナーも立ち上げません。代わりに、設定言語とその実行モデルを扱います。どのジョブが作られるか、どの順序で出発するか、何が何を待つのか、です。ツールをインストールして学べるのはツールだけですが、実行モデルはランナーがなくても学べますし、ベンダーが変わっても残ります。

現場での姿

設定をファイルに移したチームで最初に変わるのは、レビューでの会話です。「このテストをなぜデプロイの後ろに移したのですか」という質問がマージリクエストのコメントとして残り、その答えも一緒に残ります。反対に、まだ画面で設定しているチームでは、同じ質問がチャットでやり取りされて消えます。

もう1つよく見る場面は、パイプラインファイルが数百行に膨れ上がってから、ようやくincludeとテンプレートを探し始めることです。ファイルがリポジトリの中にあればその肥大化が目に見えるので、リファクタリングが始まります。画面の中にあれば、誰もその大きさを見られません。

次のレッスンで見ること

ジョブ1つが実際に何を含んでいるかを、キー単位で分解して見ます。scriptがないとなぜジョブではないのか、名前の前のドット1つがなぜそれを実行対象から外すのか、defaultとextendsがどのように異なる方法で重複を減らすのかまで見ます。