FORCIA CUBEフォルシアの情報を多面的に発信するブログ

GitLab CI/CD 導入の手引き

2019.12.08

アドベントカレンダー2019 テクノロジー 開発事例

この記事はCI/CD Advent Calendar 2019 8日目の記事です。

こんにちは。8日目のアドベントカレンダー記事を書かせていただきます、エンジニアの山門です!
現在は旅行プラットフォーム事業部で大手旅行会社のシステム開発を担当しています。

突然ですが皆さん、CI/CD しているでしょうか?

社内ではレポジトリ管理にGitLabを使用しているのですが、これまでGitLab CI/CD は使用していませんでした。

ここ最近になり、GitLab CI/CD の魅力に気づいた社内の各所で使われ始め、私自身も使い始めてみたので、そもそもどうやって使うんだ?という導入部分の紹介ができればと思います。

そもそも CI/CD って?

ここ最近 CI/CD というワードをよく耳にするようになりましたが、実際どういったものなのでしょう。

ざっくり以下のような役割を果たしており、開発段階の効率をあげるプロセスがCIで、運用段階の効率をあげるプロセスがCDのようなイメージです。

  • CI(Continuous Integration): 継続的インテグレーション
    • ビルド
    • 単体テスト
    • 結合テスト
  • CD(Continuous Delivery): 継続的デプロイ
    • ソースコードレビュー
    • ステージング環境へのデプロイ
    • 本番環境へのデプロイ

GitLab CI/CDはこのどちらの機能も有している便利ツールなのです。
今回はCIにフォーカスして、その導入を実践していただければと思っています。

GitLab CI/CDとは

GitLab CI/CDは GitLabの機能の一機能で、アプリケーションのビルドや単体テストのオペレーションを自動化し、品質維持を手助けしてくれるツールです。

GitLab CI/CDで動作する各jobはプロジェクトに関連しているため、プロジェクトの特定のブランチ更新や、mergeをトリガーにしてjobを呼び出します。

  • こんな感じのイメージ

pic_2.png

参照元

レポジトリにpushした後にGitLabがjobをキックし、テスト等々実行してくれるものだと思っておいていただけると良いかと思います。

メリット

フォルシアでは以下のような点が良いと感じ、GitLab CI/CDの利用を開始しました。

  • 社内のレポジトリ管理にGitLabを使っているため、GitLab上でbuildやtestが完結し、使うツールをまとめることができる
  • レポジトリと結びついているので、branchやMRとの連携が容易
  • 設定を .gitlab-ci.ymlというファイルでコード管理することができる

GitLab Runner

GitLab CI/CDを利用するにあたってGitLab Runnerの存在が不可欠です。GitLab RunnerはGitLabがmerge等のトリガーを検知した際に、実際にjobを実行し、結果をGitLabに返してくれるツールです。中身はGoで書かれており、GNU/Linux, macOS, Windowsといった様々な環境で動作可能なのが特徴です。

また、Executorというjobの実行形式を変えることで、各人の環境に合わせた選択をすることが可能です。

Runnerの種類

RunnerにはShered RunnersとSpecific Runnersの2種類の利用方法があります。

  • Shared Runners
    • 複数のプロジェクトのjob実行を共有のRunnerで処理する方式
    • 現在実行されているjobの数が最も少ないプロジェクトから処理が実行される
  • Specific Runners
    • 特定のプロジェクトのjobのみを実行する方式
    • 基本的に実行リクエストが来た順に処理が実行される

Executorの種類

Runnerはjobを実行するプラットフォームに応じてExecutorとよばれるjobの実行形式を選択します。
主なExecutorとして、以下のようなものがあり、フォルシアでは推奨でもあるDocker Executorを利用しています。

  • Shell Executor
    • Runnerが導入されているサーバー上で、build,test等を実行
  • Docker Executor
    • Docker APIを通してDocker Engineと接続することによりコンテナから各build,test等を実行
  • Virtual Box Executor
    • VM上のSSHを経由してbuild,test等を実行
  • SSH Executor
    • RunnerからSSH接続可能なサーバに対して、コマンドをSSH経由で送信してbuild,test等を実行
  • Kubernetes Executor
    • Kubernetes API経由でクラスタ上のPodを作成してbuild,test等を実行

準備

Runnerのinstall

フォルシアではオンプレでGitLabを利用しており、そこに新しくCI/CDの実行環境を用意しました。
ここでは事前準備としてRunnerのinstallをします。

ここでは、以下の前提の上で話を進めます。

  • GitLab が乗っているサーバがある -- A
  • Runnerを動かすサーバ(Dockerがinstallされている)がある -- B
  • A,Bがネットワーク的につながっている

RunnerのContainerを実行

今回はRunnerをDocker imageでinstallしていきます。

こちらの公式ドキュメントが参考になるかと思います。

まずはRunnerのContainerを起動。

docker run -d --name gitlab-runner --restart always \
-v /srv/gitlab-runner/config:/etc/gitlab-runner \
-v /var/run/docker.sock:/var/run/docker.sock \
gitlab/gitlab-runner:latest
Runnerの登録

次にRunnerの登録を行うのですが、登録にはあらかじめurlとtokenが必要になるので、各自取得する必要があります。登録するRunnerの種類によって以下の流れでurlとtokenを知ることができます。

  • Specific Runners登録の場合
    • 各自追加したいレポジトリの CI/CD -> Ruuners
  • Shared Runners 登録の場合
    • 管理者権限で Admin Area -> Runners
  • Specific Runners登録の場合のurlとtokenの記載場所例
    • モザイク箇所にurlとtokenが記載されています

pic_3.png

情報が揃った方は実際に登録してみましょう。下記コマンドを打つと、対話ベースでRunnerの登録が行えます。

docker run --rm -t -i -v /srv/gitlab-runner/config:/etc/gitlab-runner gitlab/gitlab-runner register
Runtime platform                                    arch=amd64 os=linux pid=6 revision=577f813d version=12.5.0
Running in system-mode.                            
												
Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com/):
<取得したGitLab url>
Please enter the gitlab-ci token for this runner:
<取得したtoken>
Please enter the gitlab-ci description for this runner:
[3b2bf8a2a755]: <このrunnerを表す名称(gitlab-runner01)>
Please enter the gitlab-ci tags for this runner (comma separated):
<カンマ区切りでタグを設定(docker,sample)>
Registering runner... succeeded                     runner=snZhRtXu
Please enter the executor: docker-ssh, ssh, docker-ssh+machine, kubernetes, docker, parallels, shell, virtualbox, docker+machine, custom:
<使用するexecutorを入力(docker)>
Please enter the default Docker image (e.g. ruby:2.6):
<使用するexecutorを入力(alpine:latest)>
Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded! 

問題なく完了すれば、無事Runnerの登録完了です!
登録したレポジトリのCI/CD -> Ruunersを見ると、登録したRunnerが以下のように画面に表示されているはずなので、早速動かしていきましょう!

pic_4.png

早速始めてみる

社内ではShared Runnerを使用しているため、以下Shared Runnersのケースで進めます。
.gitlab-ci.yml の違いはtagsの設定くらいなので、Specific Runnersの方は、公式を参考にtagsの設定を適宜入れていただければと思います。

まずは自身がいじれるレポジトリでissueなどを使って適当にbranchを作成します。
その後、レポジトリルートに .gitlab-ci.ymlを作成し、以下コードを追加してみましょう。

image: node

test1:
    script:
        - npm install
        - npm run test

(testがない方は echo "this is test1"のようにechoするだけでも問題ありません)
追加したらadd&commit&pushでレポジトリに反映しましょう。

自分のレポジトリの CI/CD -> Pipelinesを見てみると、以下のようにgit上でtestが実行されるはずです。

pic_5.png pic_6.png

注意

  • submodule周りで怒られているとなった場合、以下記述をimageプロパティと同じ高さに記載するとうまくいくかもしれません
variables:
        GIT_SUBMODULE_STRATEGY: recursive
    • GitLab CI/CDはデフォルトではsubmoduleを引っ張ってこないため、対象のレポジトリがsubmoduleを持っている場合に必要になるプロパティです
設定 内容
none(デフォルト) submoduleは引っ張ってこない
normal トップレベルのsubmoduleのみ引っ張ってくる
recursive submoduleの中にあるsubmoduleといった入れ子構造も引っ張ってくる
参考

やったこと

今書いていただいたのは、ミニマムでGitLab CI/CDを利用する方法です。
GitLab CI/CDは .gitlab-ci.ymlという設定ファイルを、レポジトリルートに配置することで実行することができます。

  • image
    • 使用するDocker イメージを指定
      • 今回はnpmを使う関係でnodeのイメージを利用しています
  • test1
    • job名
      • job名は.gitlab-ci.ymlの中で一意になっている必要があります
  • script
    • Runner上で実行されるスクリプトやコマンドを指定
    • scriptはjobの中で必須

stage, pipelineを追加してみる

お次はstageという考えを導入し、pipelineを組んでみましょう。
先ほどの.gitlab-ci.ymlに少し記述を追加します。

image: node

stages:
    - build
    - test

build:
    stage: build
    script:
        - echo "this is build stage"

test1:
    stage: test
    script:
        - npm install
        - npm run test

ここでは新たにstagesパラメータを導入し、build,testの2つのステージを追加します。
また、jobにstageパラメータを追加し、どのjobがどのstageに属するかを指定しています。

  • stages
    • ビルド、テスト、デプロイといった大きな塊を表す定義の一覧
  • stage
    • 各jobをどのステージに割り当てるかを指定
    • 同じステージのjobは並列で実行される
    • 基本的に前のステージのjobが全て成功しないと、次のステージのjobは実行されない

この状態で更新してpushしてみると、GitLab上でpipelineが自動的に組まれます。簡単ですね。これにより、ビルドが成功したらテストをやってデプロイまで、といった流れを簡単に組むことができます。

pic_7.png

注意

  • 1jobに対して1コンテナが立ち上がるので、各jobは独立していることに注意しましょう
    • ES6やTSのプロジェクトで、build jobで npm run buildしたからといって、testのjobでbuildせずにtestを走らせると失敗してしまいます!

jobの並列実行

引き続き今度は、同じstageで複数のjobを設定し、jobを並列で実行してみましょう。

image: node

stages:
    - build
    - test

build:
    stage: build
    script:
        - echo "this is build stage"

test1:
    stage: test
    script:
        - npm install
        - npm run test

test2:
    stage: test
    script:
        - echo "test1 and test2 are run simultaneously"

そろそろ皆さん慣れてきたかもしれませんが、testステージにtest2というjobを追加しました。この状態でpushすると、buildステージが成功した後に、testステージのjobが並列で実行されるはずです。

pic_8.png

もう少しやってみる

最低限の導入という意味ではここまでで十分なのですが、せっかくなので以下2点も合わせてやってみましょう。

  • branchによる実行jobの変更
  • 各jobの前後への処理の追加
image: node

stages:
    - build
    - test

before_script:
    - npm install

after_script:
    - echo "this is executed after each job"

build:
    stage: build
    script:
        - echo "this is build stage"
    only:
        - branches
    except:
        - master

test1:
    stage: test
    script:
        - echo "this is test1"
        - npm run test
    only:
        - branches
    except:
        - master

test2:
    stage: test
    script:
        - echo "this is test2"
    only:
        - branches
    except:
        - master

test3:
    stage: test
    before_script:
        - echo "before_script is overwritten"
    script:
        - echo "this is test3"
    only:
        - master

いきなり増えましたが、追加したパラメータの説明を以下に記載します。

  • before_script
    • scriptの前に行うタスクを定義
      • 環境の設定や、npm installなどの、どのjobでも実行されるようなタスクを記載
  • after_script
    • scriptの後に行うタスクを定義
  • only
    • 指定したブランチ、およびタグの更新があった場合のみjobを実行
  • except
    • 指定した以外のブランチ、およびタグの更新があった場合のみjobを実行
      • onlyパラメータと併用することが多い

やりたいことが増えてくると、jobの数が増加し、pipeline実行にかかる時間が長くなってきてしまうのですが、こういったパラメータを利用することで、時間の短縮や、必要な場面でのみjobを実行することができるようになります。

パラメータはネストが深い方で上書きする(グローバル宣言したものよりローカル宣言したものが強い)ようになっており、test3では、グローバルに宣言されているbefore_scriptを上書きし、npm installが実行されないようになっています。

  • 補足: 特定のjobだけ別のimageを使いたいという意図で、jobの中にimage指定をして上書きすることはよくあります

この状態でpushするとbuild,test1,test2が実行され、masterにmergeされた際はtest3だけが実行されます。

pic_9.png

パラメータはこの他にもたくさんあり、例えば以下のようなパラメータを使用することもできます。

設定 内容 記述例
variables job内で利用される変数定義 variables:
      GIT_SUBMODULE_STRATEGY: recursive
tags 実行するRunerのタグを指定 tags: docker
allow_failure 失敗することを許可するか否かを指定 allow_failure: true
when 特定の条件にマッチした場合のみjobを実行 when: on_failure
retry 失敗したときのretry回数を設定 retry: 2

公式のページがかなり丁寧に書かれているので、困ったらそちらを見ると良いと思います。

MRの作成

.gitlab-ci.ymlを導入することでMRでも恩恵を受けることができます。
実際にMRを作成し、画面を見てみると、これまでなかったpipelineの表示が出てくるため、pipelineが成功してmergeという運用が新たにできるようになります。

pic_10.png

対象のMRのpipelineが実行中の場合、mergeボタンが「Merge when pipeline succeeds」という表示になり、レビュアーがOKと判断し、このボタンを押しておけば、pipeline成功時に自動でmergeをしてくれるようになります。
buildや単体テストによるチェックはCIに任せて開発効率をじゃんじゃん上げていきましょう!

pic_11.png

(ちょうどいい画像が用意できなかったため公式から引っ張ってきました)

参考までに実際に今回のMRをmergeしたときに走るpipelineです。
設定した通りmasterではtest3のみ実行されています。

pic_12.png

終わりに

駆け足でしたが、ざっとGitLab CI/CDについて理解・実践していただき、導入コストの低さと利便性を感じていただけたのではないかと思います!これを機に導入を検討されてみてはいかがでしょうか。

フォルシアでは技術の知見を共有しあうdevゼミという試みがあり、本記事もそこでの発表を元に、まとめ直したものをご紹介しております(devゼミについての紹介はこちらをご覧ください)。

GitLab CI/CD は公式ドキュメントがかなり丁寧に書かれており、そちらを見れば何かしらヒントがあるので、導入の際はぜひ読んでいただければと思います。

1push,1MR単位でCIを実施して、ソースコードの品質維持・向上を担ってくれているので、実際に開発者、レビュワーが実装に集中して業務に取り組めるようになったと感じています。

GitLab CI/CD に限らず、今後CI/CDが当たり前のものになっていくといいですね。

この記事を書いた人

山門 峻

2017年新卒入社のエンジニア。大手旅行会社のシステムを担当。
休日はおいしいものを食べたり、写真を撮ったりして楽しんでいます。