オペレータの実装はつらい
本記事はRookと仲間たち、クラウドネイティブなストレージの Advent Calendar 2020 24日目を後からこっそり埋めたものです。
最初に言っておくと本記事はRookを題材にしていますが別にRook固有の話ではなく、Kubernetesのオペレータの実装全般がつらいことをRookを題材に紹介するというものです[1]。
まずはオペレータの管理対象となるソフトウェア(以降"管理対象"と記載)のつくりに依存して泥臭いハックをしなければならないことが多々あるという話です。例としてCephにおいてS3互換オブジェクトストレージを提供するRGWというサービスの管理の話をします。このサービスはradosgw-admin
というコマンドを介して操作します。
このコマンドはオブジェクトストレージのユーザなどの情報を取得するときには標準出力にJSONを出力して0を返します。失敗するとエラーログを標準エラー出力に出した上で1を返します。このため、かつてオペレータの実装は次のようになっていました。
- コマンドを実行して標準出力と標準エラー出力をまとめてバッファに出力
- コマンドが異常終了したら、つまり1を返したらバッファの中身を出力して処理を失敗させる
- コマンドが正常終了したらバッファの中身をJSONとして解釈する
ところが、このコマンドはシステムの負荷が高い時などに、成功しても標準エラー出力にログを出すことがありました。このような問題を解決する方法はいろいろあるのですが、この件については、上記の処理3を「コマンドが正常終了したらバッファの中身をJSONっぽい文字列の先頭まで飛ばしてからパースする」ように変更しました。
radosgw-admin
における例をもう一つ挙げておきましょう。このコマンドが失敗した場合、エラーの原因によって対処を分けたいときがあります。このようなときにエラーメッセージが構造化されていたりコマンドの戻り値が原因によって変わったりしていると楽なのですが残念ながらそんなことにはなっていません。このためRookではエラー出力から特定の文字列を探し出して原因を判別するという泥臭いことをしています。
さらに将来的にエラーメッセージが変わるときは判定文をまた増やす必要があります。この手の変更はわざわざリリースノートに書かれたりしないので、とくにテストが不十分な場合には対処コストが大きいです。
上記の例は氷山の一角であり、この手の細かいハックのようなものを入れなければならないことは多々あります。手間がかかるとともに、ちゃんとコメントを残しておかないと何のためのコードかわからないということがしばしば起きます。
これらの問題に対して「Cephを修正してオペレータにやさしい作りにすればいいじゃん」と思われるかもしれません。それは事実であって、実際Cephにもそういう動きは多々あります。ただ、それはそれとして、のちに問題が修正されたとしてもオペレータは古いバージョンもサポートしなければいけないがゆえに、バージョンごとに判定を分けるというダサいコードがそこかしこに出てきます。
Rookでいうとたとえばこういうものがあります。これは機能サポート有無の判定なので、この話とは微妙に違うのですが、雰囲気は感じていただけるかと思います。長年使いこまれてきたオペレータのコードは技術的な難易度がどうこうとは別の意味でなかなか読むのが大変です。筆者もRookのコードを最初に眺めたときは意図がわからないコードが山ほどあって困惑したのを覚えています。
オペレータを使っていて管理対象に問題がありそうとわかった場合もつらいです。このようなときはオペレータの開発者たちはもちろん管理対象の開発者たちともやりとりしながら落としどころを見つける、多くの場合は自ら修正する、というなかなかタフな作業が必要になります。これについては筆者が最近そういうことをしたときのことを記事にまとめてあるので、興味のあるかたは参考にしてください。このときはRookとCephの2つのコミュニティの間を数か月にわたって行き来していました。
ここまでボロクソに書いてきましたが、わたしはオペレータもその実装をするのも好き。もちろん実装が大変なことは大変なのですが、膨大な運用マニュアルがコードやコメントに、大変な目に遭うのがユーザからオペレータ開発者にかわっただけでも大きな前進だと思います。これからも頑張ってRookをよりよいオペレータに育てていきたいと思います。
-
もっというとKubernetesのオペレータに限らず任意のオーケストレータすべてに言えることかもしれません。 ↩︎
Discussion