kubell Creator's Note

ビジネスチャット「Chatwork」のエンジニアのブログです。

ビジネスチャット「Chatwork」のエンジニアのブログです。

読者になる

variantdev/modとmergify.ioで始める依存パッケージの自動管理

こんにちは!SREのid:cw-ozakiです。

SREでは運用にあたって、できる限り利用しているコマンドをDockerizeするようにしています。 そうすることによって、メンバーのマシンやCIのように各環境に依存せずにコマンドを実行できるからです。

この活動を始めてからコマンドが動かないという問題がなくなり、かなり快適になりました。 が、一つだけ大きな問題が発生しました。

f:id:cw-ozaki:20190927112302p:plain
Helmfileの更新通知

_人人人人人人人人人人人人_

> helmfileの更新早すぎ <

 ̄YYYYYYYYYYYY^ ̄

更新が早いことは良いことなのですが、さすがに日に3回も更新が入るとDockerfileを更新するのも一苦労です。 それに、ただバージョン番号を書き換えてPRを作成するという作業はあまりにもインテリジェンスが足りていません。

そこでvariantdev/modとmergify.ioを使ってDockerfileの更新を自動化してインテリジェンスな作業の集中できるようにします。

variantdev/mod

github.com

variantdev/modはChatworkの技術顧問の@mumoshuさんの作成されたパッケージ管理ツールで言語に関係なく依存を管理することができます。 Pull Requestを作成する機能もあるので、定期的に実行することで依存の更新とPRの作成まで行うことができます。

github.com

また、手前味噌ですが最近variantdev/modをGitHub Actionとして実行できるようにしました。 従来のCI環境ではPull Requestを作成するのにGitHubのPersonal Tokenの生成など一手間が必要でしたが、GitHub ActionではTokenが自動的に生成できるようになったため、YAMLファイルを一つコミットすればvariantdev/modをすぐに使えるようになっています。便利ですね!!!

依存を更新する

Dockerfile

FROM chatwork/helm:2.14.3

ARG HELMFILE_VERSION=

RUN apk --update --no-cache add bash

ADD https://github.com/roboll/helmfile/releases/download/v${HELMFILE_VERSION}/helmfile_linux_amd64 /bin/helmfile
RUN chmod 0755 /bin/helmfile

ENTRYPOINT ["/bin/helmfile"]

例として上記のようなDockerfileを用意して、これの依存となるhelmfileのバージョンをvariantdev/modで更新していきます。

variant.mod

dependencies:
  helmfile:
    releasesFrom:
      githubReleases:
        source: roboll/helmfile
    version: version: "> 0.1"

provisioners:
  textReplace:
    Dockerfile:
      from: "ARG HELMFILE_VERSION={{ .helmfile. previousVersion }}"
      to: "ARG HELMFILE_VERSION={{ .helmfile. version }}"

variantdev/modではvariant.modというファイルを利用してパッケージを管理します。 あまりREADME.mdが充実していないのでざっくり解説すると

依存しているパッケージを管理するdependenciesでは

  • JSONファイルを参照するjsonPath
  • Gitのタグを参照するgitTags
  • GitHubのリリースを参照するgithubReleases
  • Docker Registryのタグを参照するdockerImageTags
  • コマンドを実行して生成したバージョン一覧を参照するexec

が定義でき、それぞれで取得したバージョンの最新のバージョン番号を変数としてprovsionersに引き渡せます。

依存に合わせてファイルを生成するprovisionersでは

  • Goテンプレートからファイルを生成を生成するfiles
  • ファイルの文字列を置き換えるtextReplace

が定義でき、指定したファイルを生成、更新します。 他にもvaluesやexecutablesなどもありますが詳しい説明は割愛します。

今回の例ではgithubReleasesでhelmfileのリリースからバージョン番号を取得し、textReplaceでHELMFILE_VERSIONを書き換えていく形ですね。

$ mod up --build

variant.modファイルを用意してコマンドを実行すれば、無事に最新のhelmfileのバージョン番号が入ったDockerfileが生成できると思います。

Pull Requestを作成する

variantdev/modで依存を更新できたら、次にGitHub Actionを使って更新があったときにPull Requestを作成できるようにします。

.github/workflow/dependencies.yaml

name: Update dependencies
on:
  schedule:
  - cron: '0 */1 * * *'

jobs:
  up:
    name: mod up
    runs-on: ubuntu-18.04
    steps:
    - uses: actions/checkout@v1
    - uses: variantdev/mod-action@v0.2.2
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      with:
        args: up --build --pull-request

modはGitHub Actionに対応しているので、上記のファイルをコミットすれば1時間毎にチェックが走り、依存の更新があればPull Requestを作成します。

f:id:cw-ozaki:20190927140834p:plain
variantdev/modが作成するPull Reuqest
注意点として、mod v0.2.2では同一の変更でもPull Requestを作成します。そのため、今回の例では1時間に1度必ずマージしないと同じPull Requestが何度も送られてしまう危険性があることに注意してください。v0.2.2ではPull Requestを確認できる間隔で実行することをオススメします。

mergify.io

https://mergify.io/

mergify.ioは設定して条件に合わせてPull Requestを自動でマージしてくれるサービスです。 Github Actionにも自動マージするためのActionはありますが、マージ条件が豊富で使い勝手が良さそうだったのでmergify.ioを採用しています。

(もし時間帯を指定して自動マージするサービスなどがあれば乗り換えるかもしれないので教えてください)

Pull Requestを自動マージする

GitHub Appsでmergify.ioをインストールしたら、リポジトリにファイルをコミットして自動マージできるようにします。

.mergify.yaml

pull_request_rules:
- name: automatic merge
  conditions:
  - base=master
  - author=github-actions[bot]
  actions:
    merge:
      method: merge
- name: delete head branch after merge
  conditions: []
  actions:
    delete_head_branch: {}% 

今回はmasterブランチに向けて、github-actions[bot]がPRを作ったら自動でマージするというルールにしています。 もし、CIが通ったら、特定のファイルが更新されていたらなど条件を追加したい場合はドキュメントを参考に追加してください。

f:id:cw-ozaki:20190927142348p:plain
mergifyがPull Requestをマージしたログ

無事にPull Requestを自動マージができたら完成です。

おわりに

これでようやくインテリジェンスな作業に集中できそうです。 もう少ししたらインテリジェンスな作業の成果としてdocker、helm、helmfile、variant、mod、gitops、eksctlなどなど素敵なワードを満載したお話ができると思いますので楽しみにお待ちください。