はじめに
インフラGの@yjszkです。先日は青森競輪と盛岡競馬に行ってきて負けました、盛岡のジャンボ焼き鳥が美味しかったです。
さて、前回の記事ではCronitorというサービスのコード化と、CIの構築を行ったことを書きました。 それを実際に運用してみたところ、いくつかの問題が発生しました。今回は、それに対して、現在進行形で苦労している話を書きます。
CIの概要
前回の記事にあるように、CIを構築しました。 GitHub Actionsを使用し、PRにコミットが積まれるとDry-Runが走り、マージされると本番反映が走るという形です。詳しくは以下になります。
- 変更点をYAMLに記述し、GitHubにPush
- 単一YAMLだと3000行くらいになったので、可読性のため、サービスごとにYAMLを分割しています、これを一つのYAMLにまとめる処理をします
- 例えば、マッハバイトであれば、mb-hoge-staging.yaml、mb-hoge-production.yamlという形で分割しています
- 単一YAMLだと3000行くらいになったので、可読性のため、サービスごとにYAMLを分割しています、これを一つのYAMLにまとめる処理をします
- CIの中で現在の設定をYAMLにエクスポート
- 変更したYAMLと現在の設定のYAMLを突合して差分を出力
- dyffというOSSツールを採用しました、当初YAMLをDiffで比較するのに、RAWに
diff -u
でやろうとしたのですが、dyffはよりいい形で出力してくれます - 差分はPRにコメントで追記するActionを使用しました
- dyffというOSSツールを採用しました、当初YAMLをDiffで比較するのに、RAWに
- YAMLの差分をPython上で処理するためにDeepDiffを採用し、差分を取得
- 取得した差分を元に、CronitorのAPIを叩き、設定を更新
- 新規作成と更新はライブラリが対応していましたが、削除は対応していなかったため、自作しました。
出てきた課題と対策
ライブラリのtimeout値が固定値な上に短い
運用を開始してしばらくすると、以下のエラーで設定の適用がされていませんでした。
requests.exceptions.ConnectionError: HTTPSConnectionPool(host='cronitor.io', port=443): Max retries exceeded with url: /api/monitors.yaml (Caused by ReadTimeoutError("HTTPSConnectionPool(host='cronitor.io', port=443): Read timed out. (read timeout=10)"))
確かめたところ、公式ライブラリのtimeout値が10秒でした。特に引数が指定できるとかでもない。仕方ないのでリポジトリをforkしてtimeout値を変更しました。
地味にCIを実行するリポジトリから、forkしたPrivateのリポジトリを参照するのにハマってしまい、時間を食ってしまいました。これに関してはGitHubのDeploy KeyがRSAだとエラーになり、Ed25519だと問題なかっただけでした。
ドキュメントにないパラメータがダマで増えた
ある時、すべてのモニターに差分が発生しました。見たことない、ドキュメントに書いてないパラメータconsecutive_alert_threshold
が設定されていることになっていました。
これは、Cronitorのサービス側で、発表はしていないがアップデートしたものらしく、サポートに問い合わせてから判明しました。
再度設定を全てエクスポートし、現状と合わせる作業が必要になりました。機能追加は嬉しいですが、突然の追加は流石に目がぐるぐるマークになりました。
これに関して、発表の有無は考えないとして、例えばTerraformのアップデートでも同一の作業が発生するので、仕方ないことだと思います。
モニターのゾンビ化
手動で作ったモニターを削除し、同じIDでAPI経由で作成したところ、モニターが削除も使用もできなくなりました。
サポートに問い合わせたところ、
復元が必要な場合に備えてジョブデータを保持しているため、同じキーで新しいジョブを作成することができない
とありました。回復処理を行っていただき、使用可能になりました。これに関しては同じIDで作らなければいいだけなので、運用で回避可能です。
想定したように設定が反映されずに手動で変更
paused
という通知を一時的に抑制するパラメータがあるのですが、コードから変更しようとしたところ、適用されませんでした。
調べたところ、これはRead Only Attributes
であり、名前の通りコードからの変更に対応していませんでした。
ドキュメントは穴が開くほど見つめるべきです。
YAMLのdiffツール(dyff)の自己主張が激しい
- dyffというOSSのDIFFツールを使っています、例えばhogeというモニターを削除した場合、差分が以下のように出力されます。
jobs - one map entry removed: hoge: name: hoge consecutive_alert_threshold: 10 grace_seconds: 180 group: hoge-group platform: "linux cron" realert_interval: "every 8 hours" schedule: "30 1 * * *" timezone: Asia/Tokyo environments: - production notify: - dev
- このツール、デフォルトだとAAの自己主張がすごいです。
$ dyff between remote_config.yaml local_config.yaml _ __ __ _| |_ _ / _|/ _| between remote_config.yaml / _' | | | | |_| |_ and local_config.yaml | (_| | |_| | _| _| \__,_|\__, |_| |_| returned no differences |___/
--omit-header -c OFF
オプションを渡すと止めることができます。
$ dyff between --omit-header -c OFF remote_config.yaml local_config.yaml
結局CI化するべきだったのか?
得られたメリット
インフラGではIaC関連のリポジトリはモノリポとして運用しています。例えばECSのバッチの設定とcronitorのモニターの設定も両方入っているので、バッチの追加の際に一つのPRでCronitorによる監視の設定も追加できるようになりました。これですとレビュワーも楽です。
また、CI化の前に棚卸しをしたのですが、全く使用していないモニターが100近くあり、これらを大幅に減らすことができたのもメリットでした。
正直な感想と今後
作っておいてアレですが、長期の使用に耐えうるかというと微妙と言わざるを得ません。上記にあげたようにデメリットが多く、結局CI化するべきだったのか?という疑問が残りました。これに関しては継続して運用し、判断したいと考えています。
私自身はCI化とは別の方法で管理する考えに傾きつつあります。 頻繁に変更するものでもないし、定期的に棚卸しすればいいのでは?と思うようになりました。
例えば、日次でバッチの一覧をGASを使ってスプレッドシートに一覧化するなど、CI以外にもAPIを使った管理方法もあるかなと思っています。ただこれも、問題を先送りにしているだけなので、なかなか難しいです。継続して良い方法を考えていきたいと思います。ありがとうございました。