制約の実装方法

高橋拓也氏(以下、高橋):ここまではある意味夢というか、「ぼくがかんがえたさいきょうのShopSet」という話でしたが、ではこれをどう実装していくかのお話をしていきます。

まずこの制約をどう実装していくかと、実装したあとに「絶対これKubernetes抜けられなくなるやん」と考えたので、それの対応も入れてあります。

制約は先ほどお話したとおり、Custom ResourceとControllerを実装して、KubernetesのAPIを介して動かすことにします。これを選択した理由としては、自律的に動作できることと、人間からわかりやすい単位、kubectl get shopsetのような感じでAPIを使える点と、Kubebuilderなどのフレームワークやscaffoldに乗れば、実装も簡単にできるというところで採用します。

ロックイン問題と解決方法

これらのロックイン問題。Kubernetesのロックインはありますが、Custom Controllerのロックインがけっこう重いなぁと考えています。

Custom Controllerでしかオペレーションできない。例えばショップを参加させることができないとか、ShopSetを作れないとか、復旧作業ができないような状態になると、Controllerを頻繁に更新することが、まずできなくなります。

さらに、Controllerの障害時にショップが全断したような状態に、なにもできないような状態になってしまうと、これは非常につらい。つらつらのつらの状態になってしまうことなので、対応します。

これは、Controllerからロジックを分離する方法をとることで、解決を試みています。どういうことかというと、Controllerでしかできないようなオペレーションをすべてなくす、ということです。人間の手作業で、多少大変だったとしても、Controllerがやっていることを容易に再現可能な状態にするのを目指しています。

解決方法として、GitOpsをベースにマニフェストの生成をControllerが自動で行うことで解決しようとしています。聞き慣れない言葉だと思いますが、GitOpsはGitのリポジトリを唯一の成果物とするデプロイ方式のことで、例えばメインブランチにあるマニフェストなどを、Kubernetesクラスタに同期するような動きをしてくれるソフトウェアを用いて実現します。

ArgoCDというものが、有名なプロジェクトとしてけっこうあります。あとはFluxなど、いろいろあるみたいです。

ArgoCDが結局何をしているのかというと、ずっとgit pullしてkubectl apply-fしているだけです。本当はもっとありますが、基本的にこれだけ。

ArgoCDがアプライできるということは、人間が手動でアプライできるということです。ArgoCDを中間レイヤーにすれば、複雑性はけっこう吸収できるのではと考え、こちらを中間レイヤーとしてArgoCDがアプライできるリポジトリの構成を、Controllerで自動生成する道を選びました。

リポジトリの構成

構成としてはこんな感じです。このニコちゃんが開発者だとして、apply-f tierなどをするとshopset-controllerがその中身を見て、GitHubにpushし続けます。そうすると、ArgoCDがその変更をfetchしてきて、Kubernetes APIにapplyする構成です。

こうすることによって、GitHubを分岐点に、shopset-controllerとArgoCDの責任分界点が生まれるため、当初の目的は達成できるかと思います。

shopset-controllerが実際にやることは、Kustomizationで使う差分ファイルの生成とプッシュ、base manifestsの更新。そしてArgoCD Applicationリソースの作成とapplyの3つです。ここでしか言っていませんが、暗にKustomizeを使います。

さらに、shopset-controllerだけがいじるリポジトリと開発者がいじるリポジトリに分割しました。system-repositoryというものを用意しました。これはKustomizeの文脈でいう、overlaysに相当するファイルを保存するところです。

すべてのファイルをshopset-controllerが生成して、プッシュし続けます。ArgoCDはそこの変更を参照して、クラスタにアプライする動作をし続けます。

一方、app-repositoryは人間が作成したデプロイメントや、サービスマニフェストのbase manifestsとKustomizeの文脈で呼ぶものを保存する場所です。人間が作成して、プルリク出してマージするという、ギットフローにのっとった開発を行います。system-repositoryからは、git submoduleとして参照されるようにします。

Tierのリソースのマニフェスト

これが実際のTierのリソースのマニフェストです。このようにappsとsystemのurlを指定してあげると、kustomization.yamlとpatchファイルをガーッと生成して、プッシュしてくれます。

system-repositoryのトップにはbaseがSubmoduleとして定義されていて、このSubmoduleのバージョンを切り替えることによって、baseのアップデートを行います。

そしてArgoCDのCustom Resourceを自動生成して、常にshopset-controllerがアプライし続けるようにして、GitOpsを実現する感じのアーキテクチャになっています。

このshopset-controller+GitOpsで、Tierの制約を実装しました。どのように実装したかは、ブランチを利用しました。TierごとにそのTierが利用するブランチを生やして、最下位のTierで変更をプルリク(プルリクエスト)、マージします。動作確認ができたら、そのブランチを上位ブランチにプッシュするようなことで、Tierの分離を図りました。

言葉だとわかりづらいので、図で説明するとこんな感じです。ブランチごとにTierを分け、tier-0 branchや、tier-1 branchを作ります。app-repositoryとsystem-repositoryにも同じ名前でブランチを自動生成します。

一番下のTier branchにプルリクを出してマージされると自動でデプロイされるので、そちらで動作確認をします。動作確認が終わるまで、tier-1に関してはなにも変更がされないので、デプロイされない状態になります。

動作確認ができたら次のTier branch変更を反映して、そうするとシステムのリポジトリのbaseが自動で更新されて、自動デプロイが走るようなリポジトリの関係性になっています。

Configurationの実装です。ConfigurationはこちらもgitのリポジトリとTier branchの構成を取るようにしています。こうすることでConfiguration自体もTier分けされるので、高いTierのConfiguration変更も低いTierのConfigurationで実験できるようにします。

そして最後に、ShopSetはこのTierとConfigurationを指定するだけでポンポンとたくさん生せるようにしました。Failure Domainはこれから実装します。まだです。

現状の課題

では最後に、現状と課題について説明したいと思います。ShopSetの開発体制をまず軽く説明します。プロジェクトとして、現在少数精鋭のチームで開発をしています。僕は主にShopSet自体の設計や実装などを担当していて、そのほかカラーミーショップの開発運用をずっとやってきた、プロフェッショナルの人が3人参加してくれています。

アプリケーションやミドルウェアのk8s-convertをやってくれています。さらにクラスタの管理やログ転送基盤、セキュリティなど、僕が見る能力のないものをすべてやってくれている、とてもありがたいチームになっています。

現状ShopSetがどういうところまでいっているかは、検証環境で半年くらい動かして、ShopSet起因による、なにかしらのトラブルは起こっていない状態です。当初制約につけ加えた設計は、おおむねワークしている状態になります。

さらに、ShopSetで構築したカラーミーショップの環境を、プロダクションに一部導入済みです。一部導入済みというのは、13日の午前中にたまたまリリースが行われ、今一息ついている状態です。

その構成は、今のところは単一クラスタ、単一ShopSet構成という、かなりシンプルです。こからどんどんShopSetを増やしたり、クラスタ数を増やしたりしていって、この恩恵にどんどん与れるようにしたいと考えています。

ShopSet開発の課題

ここからはShopSet開発の課題に関して言及します。まず最初に挙げられるのが、k8s-convertが非常に大変だったということです。既存のサービスたちをk8sで動くようにすることが、非常に大変でした。

ドメイン知識が豊富なプロフェッショナルでも、1アプリに対して1ヶ月くらいはかかっていました。最初だから大変、というのはもちろんそうなので、ある程度の構成の共通化や、cluster-wideな機能を提供して、それを使ってクラスタに機能を委譲したりなどで工数を削減できたらいいかなとは思っていますが……。ずっと大変なのは、「それはそう、っていう感じかなぁ」と正直思っています(笑)。

プロダクト開発体制の構築も、課題に思っています。ここでいうプロダクト開発は、カラーミーショップのプロダクトの開発です。k8sに慣れた人だけがプロダクト開発をするわけではなく、k8sを触ったことがないRailsエンジニアなども開発に参加しています。

そのため、バックやリリースを補助するインターフェースが、おそらく必要になってくると思っています。月並みですが、shopctlコマンドを現在設計して、それを経由して、いろいろとCI/CDだとかもできるようにしようと考えています。

さらに、ShopSetで動作させるカラーミーショップの環境をもっと良くしていかなければいけない課題もあります。まず1つを13日の午前中にリリースしたとは言いましたが、正直そのShopSet上で、果たしてどのくらいのリクエストを食えるのかがわからない点が課題としてあります。

さらにShopSetをどれだけ大きくして、そのShopSetでどれだけリソースを食わせるべきかも決めていません。これはShopSetのポリシーに依存するものなので、決めないといけないと思っています。まずはリクエストを食わせて、どんなものかを見るのも必要だった、という感じです。

カラーミーショップはマイクロサービスのかたちを取っている状態なので、そちらのベストプラクティスをどんどんShopSetの中に入れて、レバレッジをかけられたらいいなと思っています。

ShopSetで無限にShopSetを作れるようになると、今度は新たなボトルネックがおそらく生まれてくると思います。パッと野生の勘で思いつくのは、DB Connectionが枯渇しそうかなぁというところ。正直それ以外はあまり思いついていないので、なにか出てきたら怖いな、という気持ちです。そのときはがんばって対処すると思います。

プロダクト運用自体の簡素化も、どんどん進めていかないといけないなと思っています。ショップの追加や、ShopSetの変更、先ほどのメンテナンスモードのようなものも、スッとできるようにしないと運用は破綻すると思っています。

「どのShopSetを使えばいいの?」 とかも、人間が判断することなく、全部機械に任せられるといいと思っています。こちらも規約を用いて自動化します。

ShopSetの開発の今後の予定

ShopSetの開発の、今後予定していることを最後に紹介します。今後はShopSetに内包するサービスをどんどん増やして、ShopSetだけで動くような状態を目指していけたらと思っています。

さらに、EKSを用いてマルチクラウドの本番適用をしてクラウドの冗長化をすることで、可用性を高めていけたらと思います。

そして先ほどの図にもあったとおり、k8sクラスタが今後どんどん増えてくることが予想され、おそらく運用も線形に大変になってくると思うので、そちらの運用もうまくできるような仕組みを、ShopSet外や内に構築してやる必要があると思っています。

というところで、今回の発表はおしまいです。ご清聴ありがとうございました。

質疑応答

司会者:ありがとうございます。質問がいくつか来ているので答えてもらえればと思いますが、いいですか? まず1つ目が、「APIで作られたおもしろいアプリアイデアとか知りたい」。どういう利活用をされているのか、どういう使われ方をしているかの趣旨の質問かなと思いますが。

高橋:ここでいうAPIは、Kubernetes APIのことを指しているのでしょうか。そういうことと認識してお話すると、例えばKubernetesのAPIは、今コアリソースと呼ばれているサービスリソースなどに対しても、Controllerを書けます。

そのため、既存の今書いていて動いているものが、デプロイメントリソースがコアリソースであって。そのデプロイメントリソースの更新をフックに、Slackに「更新しました」「更新が始まりました」「終わりました」を通知するControllerを書いて動かしています。

たぶんほかにも探したらあるとは思いますが。そういったようなものを書いて遊んでいます。

司会者:ありがとうございます。もう1つ、「ShopSetの実装、大変そうだけど楽しそう。」どうですか?(笑)。楽しいっていう感じが正しいのかはわかりませんが。大変なところと楽しいところを、それぞれ聞かせてもらえるといいのかなと思います。

高橋:まず、楽しいか楽しくないかで言ったら、メチャクチャ楽しかったです。だいたい1ヶ月半くらいでガーッと書きましたが、その間は寝る間も惜しんで、みたいな感じで書きました。

大変だったところがいくつかあって、1つはまずControllerを書くこと自体がわりと大変です。Controllerは“とある理想状態に収束する”という書き方をしますが、“その理想状態から離れたものを理想状態にする”という動きを、自分で実装する必要があります。

その理想状態からの差はリソースの状態によって千差万別なので、すべてケースを書かないといけないのが大変です。テストを書いて担保するしかないようなところですが、けっこう大変です。ぜひ書いてみてください。あとは、gitにプッシュするところもあまりないので大変でした。が、楽しかったです。

司会者:ありがとうございます。ではこのあたり、で高橋さんのセッションは終了とできればと思います。ありがとうございました。

高橋:ありがとうございました。