エムスリーテックブログ

エムスリー(m3)のエンジニア・開発メンバーによる技術ブログです

GitLab CIのテンプレート基盤を構築した

エムスリーエンジニアリンググループ AI・機械学習チームでソフトウェアエンジニアをしている中村(po3rin) です。検索とGoが好きです。

今回はチームで利用するGitLab CIのテンプレート基盤を構築してGitLab CIの運用改善を行ったのでご紹介します。

抱えていた課題

エムスリーのAI・機械学習チームではGitLab CIを使ったKubernetesへのデプロイや脆弱性のスキャンを行っています。これらの処理はcookiecutter templateを利用してプロジェクト作成時に組み込まれるため、これらの処理を0から準備しなくてもすぐに開発が可能です。

cookiecutterを使ったプロジェクトテンプレートの作成に関しては下記のブログで紹介しています。

www.m3tech.blog

すぐにプロジェクトを始めることができるようになったことで、AI・機械学習チームは多くのプロジェクトを立ち上げることができました。しかし、結果として1つのCircle CI JOBの変更する際にcookiecutterで作成した沢山のプロジェクトに変更が及ぶことがありました。例えば、GitLab CI JOBのベースイメージのバージョン追従などを行う際にcookiecutterで作成した全てのリポジトリを調査して、バージョンアップをしていかなければなりません。

そこで、今回、GitLab CIの共通処理をテンプレートとして1つのリポジトリで管理するという解決策を取りました。

GitLab CI テンプレート

GitLab CIではinclude機能で、他のプロジェクトに存在するymlを読み込んで利用できます。これを利用すればGitLab CIのテンプレート基盤が作れます。

図にするとこのようになります。

GitLab CI テンプレートを使った共通JOB管理

例えば、workflowの終了などをSlack通知する機能をテンプレート化したい場合、テンプレート保管用リポジトリに下記のようなymlを配置しておきます。

# SlackにWebHookで通知を送ります。
#
# --- ENV ---
# * SLACK_CHANNEL_WEBHOOK_URL
#
# --- REQUIRED TOOLS ---
# * curl

.notify_slack_after_script:
  after_script:
    - text="${CI_PROJECT_NAME} ${CI_JOB_NAME} ${CI_JOB_STATUS} ${CI_JOB_URL} by ${CI_COMMIT_AUTHOR}"
    - icon_emoji="$([[  ${CI_JOB_STATUS} == "success" ]] && echo ':rocket:' || echo ':boom:')"
    - username=${CI_PROJECT_NAME}
    - >
      curl -s -X POST -H 'Content-type: application/json'
      -d "{\"text\": \"$text\", \"icon_emoji\": \"$icon_emoji\", \"username\": \"$username\"}"
      ${SLACK_CHANNEL_WEBHOOK_URL}

上記のように必要な環境変数やツールをコメントでファイルの先頭に書いておくと親切です。これを下記のように別レポジトリのyamlからincludeして利用できるようになります。

stages:
  - test

include:
  - project: 'ai/gitlab-ci-yaml' # group名/project名
    file: '/slack/.notify_slack.yml' # projectからのpath

slack_test:
  stage: test
  image: curlimages/curl:latest
  script:
    - echo 'hello gitlab!'
  extends: .notify_slack_after_script

このようにextendsキーワードで展開して利用が可能です。 includeを利用することで各プロジェクトのリポジトリからslack通知スクリプトの定義が消え、gitlab-ci.ymlもシンプルに保つことができるようになります。

include方法には4種類(local, file, remote, template)あります。file指定の方法はGitLabインスタンスの配下の別のプライベートプロジェクトのファイルを認証なしで参照できるので、弊社ではfileによるinclude方法を採用しています。

テストの実装

GitLab CIテンプレート基盤を構築するときは、もしバグが混入した場合、全てのリポジトリに影響が出るため、事前にテストをしておくと良いでしょう。弊チームでは、新しいtemplateが追加された場合、共通基盤リポジトリのgitlab-ci.ymlにテストを書くことにしています。先ほどのSlack通知の例だと、下記のようにJOBを定義することで、GitLab CI上から手動で通知をテストできます。

stages:
  - test

include:
  - local: 'slack/.notify_slack.yml'

slack_test:
  stage: test
  image: curlimages/curl:latest
  script:
    - echo 'slack test!'
  extends: .notify_slack_after_script
  when: manual

下記のようにGitLab CI/CD PiplinesのUIからテストを起動できます。

このようにgitlab-ci.ymlにテストを買いておくことで、テスト自体がテンプレートを使いたい時のexampleとなり、使い方がすぐに理解できるようになります。

弊チームでGitLab CIテンプレート化したJOB

弊チームでは現在は下記のJOBのテンプレートを共通化しています。

  • Slack通知
  • Skaffoldを使ったk8sへのデプロイ
  • 脆弱性スキャン

特にSkaffoldを使ったk8sへのデプロイはYAMLの記述量が多いため、gitlab-ci.ymlの記述量が多く、見にくかったのがかなりスッキリしました。 今後必要に応じてDBマイグレーションJOBやElasticsearchのIndex管理JOBなんかも共通化していく予定です。

まとめ

今回はGitLab CIのテンプレートJOB基盤を構築した話を紹介しました。GitLab CIのテンプレート機能に関する詳細は下記のドキュメントを参照すると良いと思います。

docs.gitlab.com

We're hiring !!!

エムスリーでは、開発しやすい環境をガンガン作って医療に貢献していきたいメンバーを募集しています。「ちょっと話を聞いてみたいかも」という人はこちらから! jobs.m3.com