エムスリーテックブログ

エムスリー(m3)のエンジニア・開発メンバーによる技術ブログです

IaCを意識したCLI開発のエッセンス

f:id:abctail30:20201121185846p:plain

エムスリーエンジニアリンググループ AI・機械学習チームの中村(@po3rin) です。 好きな言語はGo。仕事では主に検索周りを担当しています。

エムスリーの検索基盤ではElasticsearchを利用しています。社内で積極的に検索改善が行われており複数のIndexが管理がしづらいという問題がありました。 そこで定義ファイルからIndexの状態を冪等性を持って同期させるeskeeperというOSSを作りました。

この経験から「定義ファイルで〇〇を宣言的に管理する系のツール」を作る時のちょっとしたコツを紹介します。タイトルの通り今回はIaCツールを作るのではなくIaCのプラクティスを意識してCLIを作るお話になるのでご了承ください。

なぜeskeeperを作るに至ったか

本題に入る前に、IaCを意識したCLI開発の具体例として、僕が作ったeskeeperというOSSが出来た経緯を詳しく紹介します。リンクは下記です。

github.com

チームでのElasticsearchの運用と課題

エムスリーの検索基盤チームではElasticsearchを利用しています。現在、積極的に検索改善しているので、Indexの変更が頻繁に発生していました。Index の変更時にはIndexの新規作成 & Alias 切り替えで対応していました。

f:id:abctail30:20201121180900p:plain

また、これらのオペレーションにはGitLabからKickしたk8s Jobにより実行されていました。オペレーションの数だけスクリプトが用意され、非常に複雑な運用でした。

f:id:abctail30:20201121181014p:plain

Index更新が稀なら良いですが、エムスリーではガンガン検索改善が行われているので毎回下記のような悩みが発生していました。

f:id:abctail30:20201121181116p:plain

そこでこれらの管理上の問題を解決するために作ったのがeskeeperです。

eskeeperとは

github.com

冪等性を保ちながら定義ファイルで Elasticsearch Index & alias を宣言的に管理してくれる君です。Goで書かれています。先ほどあげた問題を解決するためにInfrastructure as Codeの思想に影響を受けています(対処がインフラではなく永続ミドルウェアなので完全なIaCではない)。下記のように定義ファイルでElasticsearchの状態を宣言できます。

index:
  - name: test-v1
    mapping: testdata/test.json

  - name: close-v1
    mapping: testdata/test.json
    status: close

alias:
  - name: alias1
    index:
      - test-v1

永続レイヤーなので操作は現在 write only に絞っています。つまり現在の実装は完全な同期を妥協する事でデータを誤って消すリスクを避けていますeskeeperの導入により冪等性を担保したオペレーションを確立し、複雑化していたスクリプトやk8s Jobを一掃しました。

IaCを意識したCLI開発のエッセンス

ここからはeskeeperの実装を例にIaCを意識したCLI開発のエッセンスを紹介していきます。ここで参考にしたのはかの有名なオライリー・ジャパンの「Infrastructure as Code」という本です。

www.amazon.co.jp

この本の「第3章 インフラストラクチャ定義ツール」という章にIaCを実現するための技術選定について書かれています。ここからはeskeeperでも参考にした要件をピックアップしてご紹介します。

コマンドラインツールの無人モード

他のツールと合わせて使いやすいように入力を取り出せることが重要です。eskeeperでは標準入力などから情報を取り出せるようにしています。

eskeeper < es.yaml

標準入力にするメリットはyamlを出力するツールと一緒に使いやすくするためです。ファイルパス指定がないと実行できないインターフェースだと、例えばyamlを標準出力として生成するツールを使う時に一度ファイルI/Oを挟むことになってしまいます。下記の例ではyqというyaml操作ツールを使ってQA用の設定だけ切り出して標準入力としてeskeeperに渡す例です。

cat sample.yaml | yq -y '.qa' | eskeeper

また直接的なタイピングを要求しないといけないインターフェースは排除できるようにしておきます。直接的なタイピングを要求するインターフェースだと、人間が実行することを前提にしており、自動化できなくなってしまうためです。

設定の外在化

設定を外在化できることは言うまでもなく重要です。これにより反復可能性が担保され、Gitなどによるバージョン管理も可能になります。また、定義ファイルとして既存のテキストフォーマットをサポートすればあらゆる既成ツール(jqyqなど)で読み書きできるメリットも享受できます。下記はeskeeperの定義ファイルの例です。yamlだとコメントも書けて便利です、

index:
  - name: test-v1
    mapping: testdata/test.json

  - name: close-v1
    mapping: testdata/test.json
    status: close  // close index

alias:
  - name: alias1
    index:
      - test-v1

無人実行のサポート

ここが個人的には一番重要なテーマでした。「Infrastructure as Code」という本では 「無人実行はコマンドとして自動で実行できるだけでは不十分であり、人間が関与しなくても高い信頼性で実行できることが重要」と記しています。反対にこれが達成できない場合、オートメーションツールの結果に自信がなくて、自動化に任せっきりであることが怖くなってしまう「オートメーション恐怖症」と呼ばれる状態になってしまいます。「オートメーション恐怖症」に関してはオライリー・ジャパンの「Infrastructure as Code」で詳しく言及されています。

本では高い信頼性で無人実行を行うための要素を5つ挙げています。

  • 冪等性
  • 事前チェック
  • 事後チェック
  • 目に見える形での失敗
  • パラメータ化

eskeeperでは上から4つをサポートしています。eskeeperの処理は4つのステージに分類されています。下記はeskeeper-vオプションを使った時の出力です。 f:id:abctail30:20201121184549p:plain

設定ファイルのvalidationだけでなく事前チェック、事後チェックを行うことで高い信頼性を持って実行が出来ます。

また、定義ファイルと状態を同期させる系のツールでは、同期の途中でエラーになってしまい、定義ファイルと実体に管理が生じる可能性があるので、失敗のほとんどを手前で検知する必要があります。事前確認の順番はテストピラミッドの観点からなるべく実行コストの低いものから順番に行いましょう。

まとめ

今回紹介したようにIaCを意識したCLIでは無人実行の信頼性を上げることや、他のツールやスクリプトと合わせて使えるようにしておくとGoodです。IaCツールの持つべき要素は全てを紹介したわけではないので、興味ある方は今回紹介した「Infrastructure as Code」を是非読んでみてください。

www.amazon.co.jp

eskeeperを本番導入して、index管理用シェルスクリプト×5、k8s ConfigMap×1、Alias管理用のjsonファイル×3、環境変数設定ファイル×6 を粉砕できました。運用がかなりシンプルになったはず。本番導入から日が浅いので今後のeskeeperの活躍を観察していきます。

f:id:abctail30:20201121185449p:plain

We're hiring !!!

エムスリーではIaCやクラウドが好きなSREや、検索基盤の開発&改善を通して医療を前進させるエンジニアを募集しています! 「ちょっと話聞いてみたいかも」という人はこちらから!

jobs.m3.com

参考文献

Infrastructure as Code ―クラウドにおけるサーバ管理の原則とプラクティス www.amazon.co.jp

画像利用元

Web vector created by stories - www.freepik.com