こんにちは!SREのid:cw-ozakiです。
SREでは運用にあたって、できる限り利用しているコマンドをDockerizeするようにしています。 そうすることによって、メンバーのマシンやCIのように各環境に依存せずにコマンドを実行できるからです。
この活動を始めてからコマンドが動かないという問題がなくなり、かなり快適になりました。 が、一つだけ大きな問題が発生しました。

_人人人人人人人人人人人人_
> helmfileの更新早すぎ <
 ̄YYYYYYYYYYYY^ ̄
更新が早いことは良いことなのですが、さすがに日に3回も更新が入るとDockerfileを更新するのも一苦労です。 それに、ただバージョン番号を書き換えてPRを作成するという作業はあまりにもインテリジェンスが足りていません。
そこでvariantdev/modとmergify.ioを使ってDockerfileの更新を自動化してインテリジェンスな作業の集中できるようにします。
variantdev/mod
variantdev/modはChatworkの技術顧問の@mumoshuさんの作成されたパッケージ管理ツールで言語に関係なく依存を管理することができます。 Pull Requestを作成する機能もあるので、定期的に実行することで依存の更新とPRの作成まで行うことができます。
また、手前味噌ですが最近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を作成します。

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が通ったら、特定のファイルが更新されていたらなど条件を追加したい場合はドキュメントを参考に追加してください。

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