くりにっき

フルスタックキュアエンジニアです

GitHub-native DependabotでもAuto mergeをやりたかった

tl;dr;

いくつかやり方はあるんだけどどれも一長一短で決定打はなかった

前置き

Dependabot Previewの終了がアナウンスされました。

github.blog

Auto-merge: We always recommend verifying your dependencies before merging them; therefore, auto-merge will not be supported for the foreseeable future. For those of you who have vetted your dependencies, or are only using internal dependencies, we recommend adding third-party auto-merge apps, or setting up GitHub Actions to merge.

と書いてるようにGitHub-native Dependabot(新しい方)ではAuto mergeがサポートされないようです。

Dependabot Preview(古い方)では automerged_updates を使ってたので本腰入れて調べてみました。

ちなみに自動マージをやりたいモチベーションは個人的な理由なので大抵の人には当てはまらないと思います。(20個以上の個人アプリでdependabotを入れてると大量にPRきた時にマージするだけでも大変だし一気にマージするとデプロイのqueueが詰まる(CircleCIのjobの同時実行数はアカウント単位)ので、開発系のライブラリや本番系のライブラリでもパッチバージョンのバージョンアップ時は自動マージを許したい)

GitHub Actionsでやる方法

https://github.com/marketplace?type=actions&query=dependabot+merge で調べてStar数の多いものや定期的に更新されてるものをピックアップしました

Dependabot Auto Merge

github.com

pull_requestイベントかpull_request_targetイベントで実行される。執筆時点での最新版は2.4.0

メリット

  • Dependabot Previewautomerged_updates 相当の設定がある
  • APIから @dependabot merge みたいなコメントをして実現してるのでGitHub-native Dependabotと100%の互換性がある

デメリット

  • 前述の仕様のためPersonal Access Token(PAT)が必要
    • publicリポジトリだけなら public_repo スコープだけでいいのでギリギリ許容できなくはないが、トークン漏洩時のリスクを考えるとなるべくスコープを狭くしたい
    • ためしにApp Tokenを使ってみたらコメント自体は成功したけどDependabot側でエラーになったので、おそらくPATのユーザにリポジトリのWrite権限があるかをチェックしてるように見える

f:id:sue445:20210504181545p:plain

備考

GitHub-native Dependabotの仕様としてpublicリポジトリのpull_requestイベントだとsecretsが取れないのでPATの受け渡しができずにエラーになるのでpull_request_targetイベントを使う必要があるのですが、pull_request_targetイベントだと今度はセキュリティリスクがあるので注意する必要があります。

github.com

https://github.com/ahmadnassri/action-dependabot-auto-merge/issues/60#issuecomment-806170152 みたいに github.actor でチェックするしかなさそう。

Merge me!

github.com

workflow_runイベントによって別のジョブの終了時に実行される。執筆時点での最新版は1.2.62

メリット

  • secrets.GITHUB_TOKEN が使える

デメリット

  • presetで DEPENDABOT_MINORDEPENDABOT_PATCH はあるものの現時点ではDependabot Previewほどの柔軟さはない
  • workflow_runの仕様上リポジトリ上はGitHub Actionsで統一していないと使えない
    • CircleCIのジョブ終了時にGitHub Actionsを発火させるのはできなくはないが大変そう

Dependabot Pull Request Action

github.com

scheduleイベントで定期実行する。執筆時点での最新版は1.0.0

メリット

  • secrets.GITHUB_TOKEN が使える

デメリット

  • 設定は豊富だが現時点ではDependabot Previewautomerged_updates を完全に置き換えるのは難しそう
  • dependabotを実行する時間にあわせてscheduleの時間も調整する必要がある
    • 寝てる間にいい感じにマージされていればいいのであれば雑に30分後とか1時間後にセットすればいいけど、そのラグが許容できないとストレスかも

番外編

Renovateを使う

github.com

Renovateであればauto merge対応してるのでDependabotから移行するのも1つの手なのだが、DependabotはGitHub公式という強みがあるので移行するかどうかはかなり悩ましい

docs.renovatebot.com

近況

手持ちのリポジトリ全部移行する前にDependabot Auto MergeとMerge me!を1リポジトリずつ入れて試験運用してみた結果、下記の理由でDependabot Auto Mergeを採用しました

  • PATを各リポジトリにコピペしてまわる手間はあるものの automerged_updates 対応してるのは強い
  • 手持ちのリポジトリだとCircleCIも使っているのでMerge me!が使えない

gitpanda v0.9.5をリリースした

github.com

https://github.com/sue445/gitpanda/blob/master/CHANGELOG.md#v095

gitpanda的にはこれといって新機能は無いのですが GitHub Container Registryが secrets.GITHUB_TOKEN に対応した ためDockerイメージをGitHub Container Registryにホスティングするようにしてみました。

https://github.com/users/sue445/packages/container/package/gitpanda

ビルド済のDockerイメージがあるのでCloud Run辺りで動かしやすいかもしれません。

Go 1.16以降はGitHub版Dependabotを使った方がよさそう

前提

一言でDependabotと言っても実は2種類あります

3rd party版Dependabot

dependabot.com *1

GitHub版Dependabot

docs.github.com

github.blog

どうしてGo.1.16以降はGitHub版Dependabotを使った方がいいのか?

Go 1.16で No automatic changes to go.mod and go.sum が入った関係でmoduleのバージョンアップ時にエラーになることがあるためです。

blog.golang.org

実際に3rd party版DependabotでmoduleのバージョンアップPRを作った場合、下記のようにGo 1.16のみエラーになります

f:id:sue445:20210320073935p:plain

+ make test
go test -count=1 $***TEST_ARGS***
Error: kmsenv.go:8:2: missing go.sum entry for module providing package golang.org/x/oauth2/google (imported by github.com/sue445/gcp-kmsenv); to add:
    go get github.com/sue445/gcp-kmsenv
Error: ../../../go/pkg/mod/google.golang.org/grpc@v1.36.0/internal/channelz/types_linux.go:26:2: missing go.sum entry for module providing package golang.org/x/sys/unix (imported by google.golang.org/grpc/internal/channelz); to add:
    go get google.golang.org/grpc/internal/channelz@v1.36.0
Error: ../../../go/pkg/mod/cloud.google.com/go@v0.79.0/kms/apiv1/key_management_client.go:28:2: missing go.sum entry for module providing package google.golang.org/api/iterator (imported by cloud.google.com/go/kms/apiv1); to add:
    go get cloud.google.com/go/kms/apiv1@v0.79.0
Error: ../../../go/pkg/mod/cloud.google.com/go@v0.79.0/kms/apiv1/doc.go:43:2: missing go.sum entry for module providing package google.golang.org/api/option (imported by github.com/sue445/gcp-kmsenv); to add:
    go get github.com/sue445/gcp-kmsenv
Error: ../../../go/pkg/mod/cloud.google.com/go@v0.79.0/kms/apiv1/key_management_client.go:30:2: missing go.sum entry for module providing package google.golang.org/api/option/internaloption (imported by cloud.google.com/go/kms/apiv1); to add:
    go get cloud.google.com/go/kms/apiv1@v0.79.0
Error: ../../../go/pkg/mod/cloud.google.com/go@v0.79.0/kms/apiv1/key_management_client.go:31:2: missing go.sum entry for module providing package google.golang.org/api/transport/grpc (imported by cloud.google.com/go/kms/apiv1); to add:
    go get cloud.google.com/go/kms/apiv1@v0.79.0
Error: ../../../go/pkg/mod/cloud.google.com/go@v0.79.0/kms/apiv1/iam.go:19:2: missing go.sum entry for module providing package google.golang.org/genproto/googleapis/cloud/kms/v1 (imported by github.com/sue445/gcp-kmsenv); to add:
    go get github.com/sue445/gcp-kmsenv
Error: ../../../go/pkg/mod/cloud.google.com/go@v0.79.0/iam/iam.go:30:2: missing go.sum entry for module providing package google.golang.org/genproto/googleapis/iam/v1 (imported by cloud.google.com/go/iam); to add:
    go get cloud.google.com/go/iam@v0.79.0
Error: ../../../go/pkg/mod/google.golang.org/grpc@v1.36.0/status/status.go:34:2: missing go.sum entry for module providing package google.golang.org/genproto/googleapis/rpc/status (imported by google.golang.org/grpc/status); to add:
    go get google.golang.org/grpc/status@v1.36.0
Error: make: *** [Makefile:10: test] Error 1
Error: Process completed with exit code 2.

https://github.com/sue445/gcp-kmsenv/pull/53/checks?check_run_id=2152006087

対応するためにはmoduleのバージョンアップ時に go getgo mod tidy も一緒にする必要があるのですが、GitHub版DependabotだとPR作成時に自動で go mod tidy を行うためこの問題を回避できます。

github.blog

3rd party版の方は下記issueがクローズされてるのでおそらく実装の見込みは薄いと思います。

github.com

まぁ3rd party版とGitHub版があるならGitHub版を使った方がいいよね...

3rd party版 vs GitHub

GitHub版には automerged_updates が無いのを除けば今の所不満は無いです。(3rd party版の automerged_updates *4 をずっと使ってきたのでこれまで移行するモチベーションがあまりなかった)

*1:区別するために僕が勝手に3rd party版って言ってるので適切な言い方があったら知りたい

*2:https://github.com/sue445/gitpanda/pull/575

*3:https://github.com/sue445/gitpanda/pull/584

*4:https://dependabot.com/docs/config-file/#automerged_updates

Cloud FunctionsにRubyのスクリプトをデプロイする時の裏側

前置き

個人ツールをCloud Functionsで動かしてるのですがある日突然デプロイできなくなりました。

事象は下記のStack Overflowに書いて解決済みです。

stackoverflow.com

英語読みたくない人用に簡単に説明書くと、Cloud Functionsのデプロイで使われているDockerイメージにインストールされてるbundlerがバグってるバージョンにいきなり上がった *1 せいでデプロイできなかった感じです。

調べたけど gcloud functions deploy からだとデプロイ時に使うbundlerのバージョンをどうやっても変えられませんでした。

で、その調査の過程でCloud Functionsのデプロイの裏側を調べたのでメモ。

注意点

このエントリに書いてる内容は実際の処理やログを参考に調べた結果をまとめてるだけなので注意してください。公式ドキュメントのどこにも載っていなかったので公式ドキュメントへのリンクも書いてません。

Cloud Functionsのデプロイ方法

下記のように gcloud functions deploy を実行するだけで bundle install やら諸々実行されます。

gcloud functions deploy my_func --region=us-central1 --memory=128MB --runtime=ruby27

cloud.google.com

デプロイ時のログの場所

デプロイ時のログにCloud Loggingへのリンクが表示され、Cloud Loggingで詳細なログが見れます。

f:id:sue445:20210312220834p:plain

Cloud Functionsのデプロイで使われるビルド環境

f:id:sue445:20210312221239p:plain

ここで注目してほしいのはフォーカスしている箇所です。

Cloud Functionsデプロイ時のビルド環境はDockerコンテナのようです。

実際のDockerイメージは下記にあります。

実際に docker run --rm -it us.gcr.io/fn-img/buildpacks/ruby27/builder:latest bash のようにすることでCloud Functionsのビルド環境に入ることができますが、イメージ1つ2.5GBあるのでpullする時の帯域と空きストレージには注意してください。

$ docker images | grep us.gcr.io/fn-img/buildpacks
us.gcr.io/fn-img/buildpacks/ruby26/builder                          ruby26_20210228_2_6_6_RC00   3546787b4c87        41 years ago        2.48GB
us.gcr.io/fn-img/buildpacks/ruby27/builder                          ruby27_20210308_2_7_2_RC00   4eb6206a7245        41 years ago        2.44GB

Cloud Functionsのデプロイ時の処理

色々ググッた結果、デプロイ時の処理は下記にあるのを見つけました。

https://github.com/GoogleCloudPlatform/buildpacks/blob/54f3b59da3ac1fc7f9f1236bb0a6c8b1fb055e00/cmd/ruby/bundle/main.go

これを見れば gcloud functions deploy 実行時に実際にGCP側でどんな処理が実行されるのか分かると思います。

*1:Rubyのdefault gemで入ってるbundlerを使ってない

プリパラのライブにおける緊急対応事例 #ノンシュガー

起きたこと

3/6(土)の NonSugar スペシャルイベント「約束のてへペロピタですわ!」byプリパラ の昼夜公演に現地勢として参加してたのですが、昼公演の楽曲中に演者の1人の山下七海さんが 歌ってる最中にダッシュでステージ裏にはけた。

現地で見てた側としてはまさにこういう心境でした。

dic.pixiv.net

昼公演終了の段階では「ペッパー *1 だしそういう演出の可能性もあるかもしれないな」という感想だった。

ちなみに夜公演だと途中退場はなかったのでガチなトラブルだったらしい。

真相

これらしい。

家に帰って配信アーカイブを見返してたんだけど、確かに山下さんのブーツの紐が片方解けてステージ上で自分で外そうとした様子が映っていた。(ステージ降りた後の再入場時にはブーツの紐っぽいのがなくなってた)

配信アーカイブを見返しての感想

昼公演の様子がむっちゃ気になったので家に帰ってから配信チケットを買ってアーカイブを見た。

山下さんが歌いながらブーツを外そうとして苦労してるのは確認。

だけど山下さんがダッシュで抜けた様子は全くカメラに映ってなくて、NonSugarが一時的に2人でライブしてる間はカメラをむっちゃズームにしてステージ全体が映らないようになってた。完全に推測だけどステージ全体が映ると3人ユニットのNonSugarが2人しかいないことに配信側が気づくので現場の一瞬の判断のカメラワークだと思う。

山下さんが抜けてる最中に山下さんのコールのパートがあったのだけど残り2人が一緒にコールしてた。その直後に山下さんが再入場して歌唱再開したのですがその時にはブーツの紐っぽいのが両足ともなくなっていた。

2曲終わってMCタイムになった時に山下さんが再度ステージから抜けて戻ってきた時にはブーツの紐っぽいのが両足ともついてたので、楽曲やってる数分間でスタッフがブーツの緊急対応をやったのだと思う。

結果としてNonSugarは歌唱中にトラブルが起きて一瞬2人になったけども楽曲を止めることなく歌いきった。

時間にしたらほんの数分間の出来事なんだけどかなり情報量が多かった。

軒並みですが演者だけでなく関係者一同対応乙という感想しかないです。

サービス継続不可能になりそうな障害時にも周囲が一瞬の判断で協力して緊急対応してダウンタイムやユーザ影響を最小にしてサービスを継続するのは日々の業務に通じるものがあると思いました。

ちなみにアーカイブ配信は3/7までで円盤にも夜公演しか収録されないのでこの緊急対応内容はお蔵入りになりそう。

*1:プリパラ内で山下七海さんが演じる野生児キャラ

ItamaeのCIをTravis CIからGitHub Actionsに移行した

tl;dr;

github.com

前置き:ItamaeのCIの式年遷宮の歴史

僕がコミッタになる前はWercker + Vagrant + DigitalOceanを利用

僕とうなすけさん( id:yu_suke1994 )がコミッタになったタイミングでTravis CI( travis-ci.org ) + Dockerに移行

sue445.hatenablog.com

先日うなすけさんが travis-ci.com に移行

blog.unasuke.com

移行のモチベーション

Itamaeのリポジトリでは週1のweekly buildを実行してるのですが、1回辺りのビルド時間が長くて無料枠のクレジットを使い切ったためです

f:id:sue445:20210217214520p:plain

https://docs.travis-ci.com/user/billing-faq/#what-if-i-am-building-open-source を読むとOSSなら無料でクレジットを付与するのでメールしてねって書いてるので連絡したのですが、1週間以上経っても返事がこなかったので諦めてGitHub Actionsに移行しました。

メール送った直後にzendeskからの自動返信メールが届いたので向こうに届いてないってことはないはずなんだけどなぁ。。。(もちろんTravisからのメールが迷惑メールに振り分けられてもないです)

ハマったこと

ItamaeのCIだと docker run -d で起動したコンテナに対してItamaeを実行しているのですが、Itamae実行中にtmpディレクトリの中身が消えたりファイルが作れないという事象のせいでビルドがコケる現象が頻繁に発生してました。

代表例はこれ。/tmp/itamae_tmp/ が存在しなくてエラーになってそうなんだけど、Itamaeの処理の冒頭で /tmp/itamae_tmp/ を作ってるのにそうはならんやろ。。。

 INFO :   execute[echo -n 3 >> /tmp/subscribes] executed will change from 'false' to 'true'
ERROR :     stderr | mkdir: cannot create directory '/tmp/itamae_tmp/1613488120.203623': No such file or directory
ERROR :     Command `mkdir /tmp/itamae_tmp/1613488120.203623` failed. (exit status: 1)
ERROR :   remote_file[/tmp/remote_file] Failed.

https://github.com/sue445/itamae/runs/1911563358?check_suite_focus=true

/tmp がおかしそうというのはあたりがついてたのでパーミッション変えてみたりホスト側から /tmp をmountしてみたりと色々やってみたのですが、最終的には https://github.com/itamae-kitchen/itamae/pull/335/commits/69d6dfb13688609cfd96193122ae173b7c712da6 で解決しました。

良かったこと

  • GitHub Actionsの並列数で殴る構成にしたので1回あたりのビルド時間が7分から3~4分になった

悪かったこと

  • YAMLが41行から200行に増えた。(つらい)
    • いい加減アンカーとマージを使えるようにしてほしい
    • CircleCIならもっとリファクタリングできるんだけどなぁと思いながらYAMLを書いてた

VagrantとVirtualBoxは別物

Twitterを見てるとたまにVagrantVirtualBoxを混同してる発言があってその度にイラッとしてるのでメモ。

tl;dr;

  • DockerとVagrantはカバーしてる分野が異なるツールなので比較するのは間違い
  • DockerとVagrantを比較してる場合、9割くらいの確率でVagrantVirtualBoxを混同している可能性が高い

雑な説明

VirtualBoxとは

www.virtualbox.org

WikiPedia より引用

Oracle VM VirtualBox (オラクル ブイエム バーチャルボックス)とは、x86ならびにAMD64/Intel64にかかる仮想化ソフトウェアパッケージの一つ。

要はVMの実行環境です。

似たような領域をカバーするツールとしての比較対象は下記

Vagrantとは

www.vagrantup.com

WikiPedia) より引用

仮想機械の提供自体は、VirtualBoxをはじめとする仮想化ソフトウェアが行う。Vagrantは、これらソフトウェアが用意されているのを前提として、仮想機械の設定や立ち上げに特化している。

要は色々な種類のVMの操作を同一の設定ファイルとインターフェースで操作するためのソフトウェア。

当たり前ですがEC2とGCEとではAPIが全く違うので、それらをVagrantを介して同じコマンドでVMの起動や停止ができるのは便利です。

Vagrantが提供してるのは各VMの操作をするためのインターフェースの部分だけなので VirtualBoxやEC2などのVMの実行環境そのものは提供していません。

Vagrant単体で使うことはなく、

のように各VM環境に対応したproviderと組み合わせて使うことが多いです。

ここで注意してほしいのはVagrantにはDockerのproviderもある(Vagrant + Dockerの組み合わせがある)ことです。 *2

www.vagrantup.com

つまりDockerとVagrantは対立するものではないし、ツールとしての性質が違うので比較するのが間違いです。

Vagrantと似たような領域をカバーするツールを敢えて言うと(Dockerではなく)Docker ComposeやDocker Machineだと思います。(異論は認める)

Vagrant + VirtualBoxがよくある組み合わせだと思うのだけど、そのせいでVagrantVirtualBoxが混同されている気がします。

よくある間違い

  • Vagrantはオワコン、時代はDocker!
    • Vagrantが下火なのは否定しないが比較対象が違う
    • VirtualBoxとDockerを比較してるなら話が通じる
  • 開発環境をVagrantからDockerに移行した
    • 「開発環境をVagrant( + VirtualBox)からDocker( + Docker Compose)に移行した」ということを言いたいんだろうけど、なんかもにょる

*1:VMとコンテナも別物なんだけどアプリケーションの実行環境という点では共通してるので比較対象に含めています

*2:ドキュメント読む限りあまり推奨されてない感はあるが