Blog株式会社マネーフォワード公式開発者向けブログです。技術や開発手法、イベント登壇などを発信します。サービスに関するご質問は、各サービス窓口までご連絡ください。2021-02-19Kubernetesクラスタ上で動かすSidekiqに対してヘルスチェックを導入した話こんにちは。クラウドのアカウント基盤のプラットフォームをプロダクトオーナー兼バックエンドエンジニアとして開発しているkamillleです。manifest、Terraformなどを書いています。今回はKubernetesクラスタ上で動かしているSidekiq(※)プロセスに対してヘルスチェックを導入して、より安全にデプロイが行えるようになった話をご紹介しようと思います。今回ヘルスチェックで満たしたかったゴールは下記の2つです。デプロイ時に新しいSidekiq用のPodが起動したが内部でプロセスが立ち上がらなかった場合は既存Podのterminateを行わない(いわゆるゼロダウンタイムデプロイを高いレベルで実現したい)Sidekiqのプロセスは生きているがなんらかの理由でジョブを捌けなくなったら検知して再起動するゴール実現のためにそれぞれ以下の状態を検知できるようにすることが必要と考えました。Probe)に当てはめると下記になると考えました。Probe、どちらを使うべきかは後述します。下記の2つを一つのコードで検知しようと思います。Sidekiqが起動し、Redisのキューを捌けるようになったSidekiqのプロセスは生きているが処理が止まってしまっているSidekiqのプロセスは5秒に一度、自身のホスト名やプロセスIDなどの情報をRedisに書き込んでおり(以後、ハートビート)、起動時も同様の挙動が実行され続けます(FYI)。満たしているとみなして良いと考えました。このハートビートのレコードにはEXPIREが設定されており、60秒で揮発するようになっています(FYI)。いう状態が起きた際は60秒後にハートビートのレコードが消え、Sidekiqのプロセスはあるがハートビートが存在しない状態ができます。気づくことが可能になります。実際に60秒後にハートビートが揮発することは自分の環境でも確かめてみました。上記に書いてきたように今回のヘルスチェックのゴールを満たすにはSidekiqがRedisに書き込むハートビートを検証するのが良いと思います。注意しなければならないのは下記図のようにSidekiqを複数プロセスで動かすケースです。下記図のようにハートビートはSidekiqのプロセスごとにRedisに書き込まれます。そのためヘルスチェックをしたいSidekiqのプロセスが書き込んだハートビートのレコードがRedisに存在するかどうかを確認する必要があります。ハートビートのレコードは下記のような構造になっていて、Sidekiqが動いているサーバーのhostnameが書き込まれるようになっているため、ヘルスチェックしたいSidekiqのプロセスが動いているサーバーのhostnameが含まれたハートビートレコードがあるかどうかをチェックすることが大事です。https://medium.com/kubernetes-tutorials/kubernetes-dns-for-services-and-pods-664804211501とあるように、KubernetesのPodのホスト名はデフォルトではPod名と同じになるようになっています。いう形式で同じ名前のPodが作成されないようにしてくれます。そのため各Sidekiq用のPodごとにハートビートのレコードがRedisに存在することになります。Probeはコマンドを使ったヘルスチェックをサポートしています。SidekiqはHTTPリクエストを受けることはできないので今回はハートビートのレコードの存在有無を検証するコマンドを用意し、それを実行しようと思います。Deploymentの定義は下記のようになりました。ヘルスチェックで利用するコマンドを記載します。ヘルスチェック用のコマンドで、これはSidekiqで起動するRailsアプリケーションのリポジトリに実装しました。Probeを使わない理由ヘルスチェックを実装した2021/01では、自分が開発しているアプリケーションはEKSのKubernetesクラスタで動いていました。startupProbeの両方をmanifestに記載した場合、startupProbeの定義がPodに当たらないというバグがあり(FYI)、これによってstartupProbeが使えない状態であったため代わりにreadinessProbeを使っています。2021/02にEKSがKubernetes1.19に対応したためバージョンアップが完了したらstartupProbeを使おうと考えています。runnerを利用してヘルスチェック用のRubyのコードを実行しています。taskで実行するでもいいと思います。当該Podのhostnameが書き込まれたハートビートがRedisに存在するかどうかをチェックしています。以外の場合はunhealthyと判断してくれます。インスタンスをバインドしています。インスタンスはRedisから全てのハートビートのレコードを取得し、それをEnumerableなオブジェクトとして提供してくれます。インスタンスが返したハートビートのレコードの中に当該サーバーのhostnameを含んだレコードが存在するかどうかを検証しています。そのためexitを実行するメソッドと終了コードを決めるメソッドを分けている。endendまとめ今回紹介したヘルスチェックを導入したことでデプロイによって作られた新しいPodが起動はしたがSidekiqのプロセスが立ち上がらないまま古いPodがterminateされジョブを捌けるプロセスがいない状態になってしまったり、なんらかの理由で処理がハングしたSidekiqのプロセスがあれば自動で再起動がかかるようになりました。これによって安心してデプロイを行うことができるようになったり、何かあった時に手動で復旧させる必要性が低くなったので運用面のコストが下げられてよかったと感じています。引き続き開発者が安心安全に運用を行える仕組みや運用コストの削減に取り組み、自分だけではなく周囲のエンジニアメンバーがユーザーの課題解決に取り組むための時間を増やせるような活動をしていければと思っています。マネーフォワードでは、エンジニアを募集しています。ご応募お待ちしています。SREグループの佐々木と申します。読者になるこのブログについてはてなブログをはじめよう!moneyforwardDevさんは、はてなブログを使っています。ブログを報告する引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。