Site Reliability Engineering チームの @yuya-takeyama です。
年末年始頃は React Native でのアプリ開発をやっていた気がしますが、「スキルを Web 開発から SRE の領域まで広げたい」という以前からの私自身の思いと、「Kubernetes による Microservices 基盤を作っていくメンバーがもっと必要」「Microservices を技術面だけでなく組織面でも Production Ready な形でやっていく上で Developer と SRE のつなぎ役が必要」という会社の状況が一致したので、異動して AWS, Kubernetes または MongoDB などと向き合っています。
3 行でまとめ
- Sidecar Pattern はアプリケーションのコンテナから再利用可能な部分をもう一つのコンテナとして切り出すパターン
- Ambassador Pattern は Sidecar の一種であり、プロキシとしての Ambassador を経由するこで通信にまつわる面倒な処理を切り出すパターン
- これらをうまく利用すると、アプリケーション本体はビジネスロジックに集中することができる
この記事の概要
この記事では Cloud Native なアーキテクチャにおいて重要な以下の 2 つのパターンについて紹介します。
- Sidecar Pattern
- Ambassador Pattern
これらは Kubernetes 以外のコンテナオーケストレーションエンジンにおいても有効ですし、Docker のようなコンテナランタイム単体でも実装は可能です。Sidecar や Ambassador といった名称も Kubernetes の登場以前から使われていたようです。
この記事においては Kubernetes を念頭に説明します。Kubernetes のようなオーケストレーションエンジン上であれば、これらの実装は比較的容易に行えるので、引き出しとして持っておくだけでいつか役に立つかもしれません。
また、日本でもよく聞かれるようになった Service Mesh を実現する上でも基本的な考え方となります。
対象となる読者
この記事は Kubernetes を実際に運用するインフラエンジニア・SRE よりは、そこでアプリケーションの開発を行う Web Developer を対象に書いています。
特に、Kubernetes のようなコンテナオーケストレーションエンジンの上にアプリケーションをデプロイしている、または将来そういう状況になりそうという方に読んでもらいたいと思っています。
これらのパターンを理解しておくことで、ビジネスロジックに集中しつつ、シンプルで質の高い Web アプリケーションの開発が可能になるでしょう。
Kubernetes の用語についても適宜説明を挟んでいくので、すでに Kubernetes の運用を行なっている・Kubernetes のことをよく理解しているような方には退屈に感じる部分もあると思いますが、適宜読み飛ばしてください。
Sidecar Pattern
Sidecar Pattern はアプリケーションのコンテナから再利用可能な部分をもう一つのコンテナとして切り出すパターンです。
よくある例は以下のようなものです。
- メインのコンテナでは Web アプリケーションのプロセスが動いていて、その中で適宜ログをファイルに書き出す
- ログは Volume としてマウントしている領域に書き出す
- コンテナは通常使い捨てするものなので、再起動のタイミングでファイルは揮発してしまいますが、Volume と呼ばれる領域に書き出すことで、永続化したり他のコンテナと共有したりできます
- もう一つのコンテナでもその Volume をマウントし、ログを読み出してログ集約サービスに転送する
- 例えば Fluentd のような Log Collector が BigQuery や Papertrail のようなサービスにログを送り出したり、または別の Fluentd に転送する
別名 Sidekick (相棒) とも呼ばれており、Ruby/Rails 開発者であればメインのコンテナを Rails, Sidecar コンテナを Sidekiq Worker, Volume をキューに使う Redis として考えるとイメージしやすいかもしれません。
Sidecar Pattern のメリット
Web アプリケーション本体と Sidecar のコンテナを分離することで、それぞれのコンテナを別チームでメンテナンスすることが容易になりますし、それぞれの責任が明確になるので単体でのテストもしやすくなるでしょう。
また、それぞれ独立しているので、どちらか一方のコンテナを丸ごと置き換えることも可能です。ログファイルの形式だけ約束事として決めておけば、それを書き出す Web アプリは Rails だろうと Flask だと構わないし、Log Collector についても Web アプリ側に影響することなく Fluentd から Logstash に置き換えたりすることが可能です。
個人的に一番大きいのは Sidecar の再利用性だと思います。例えば Web アプリケーションのイメージに Fluentd を追加でインストールし、foreman で両方のプロセスを起動する、ということも可能ではあると思いますが、社内の全ての Web アプリケーションに同じ変更を適用するのは非常に煩雑です。Fluentd のイメージを別途用意することで、他のどんなイメージとでも組み合わせて再利用が可能です。
どれもオブジェクト指向プログラミングの利点と共通しているところがあって面白いですね。
Sidecar Pattern を Kubernetes 上でどう実現するか
Kubernetes においては Pod と呼ばれる Object 上に複数のコンテナを定義することで実現が可能です。
Pod というのは Kubernetes にデプロイできる Object の最小単位と言えるもので、1 つの Pod 内には複数のコンテナを含むことができます。Kubernetes においてはコンテナを素のままでデプロイするということはできず、一見コンテナイメージのデプロイのように見える kubectl run
コマンドも、実行すると Pod (と、それを管理する Deployment 等) としてデプロイされます。
余談ですが、Kubernetes にはたくさんの種類の Object が登場し、その中の Deployment, ReplicaSet, DaemonSet といったものは複数の Pod をいい感じにアレするものだと思っておけば Kubernetes の最初の一歩としては大丈夫だと思います。 (ConfigMap や Secret のように Pod とは直接関係ない Object も色々あります)
Sidecar を可能にする Pod の特性
- 1 つの Pod 内のコンテナは常に同一の Node に配置される
- 1 つの Pod 内のコンテナは同一の Volume を共有することができる
- Volume は Node 上に存在する領域なので、複数の Node 間では直接アクセスできません。同一の Node に配置されるからこその特性です。
Ambassador Pattern
Ambassador Pattern は Sidecar Pattern とよく似ています。ブログ記事等によっては、Sidecar Pattern の一分類として紹介されているものもあるようです。
Microservices においては複数のサービスが RESTful API/RPC 等を使って通信することで 1 つのアプリケーションを構成しますが、その通信にプロキシとして Ambassador コンテナを利用します。
Ambassador コンテナは以下をはじめ、様々な役割を担うことができます。
- リクエストのリトライ
- モニタリング
- 特定のサービス間の通信においてエラーが多発していればそれを検知して、アラートを飛ばしたりすることが可能でしょう
- サーキットブレーカー
- エラーが稀であればリトライで信頼性を上げる方がいいこともありますが、エラーが起こり続けている状況でリトライし続けると、リクエスト先のサービスに負荷をかけ続けてしまったり、別の問題を引き起こすことがあります。一定の条件で早期にエラーを返すことができれば、そういった問題を軽減することができます。
- トレーシング
Ambassador Pattern のメリット
Ambassador Pattern も Sidecar Pattern と共通したメリットを持っていますが、その他に特有な物でいうと、プログラミングに依存しない再利用性が挙げられます。
上記のような機能は Ambassador コンテナを使わずとも、ライブラリ等によって実現することもできます。Ruby においてリトライは retriable のような gem がありますし、サーキットブレーカーを実装した有名ライブラリとしては Netflix の Hystrix (Java 製) などが挙げられます。
ですが、これらはあくまでもその言語でのみ使えるものです。Microservices においてはサービスごとに適切な言語を使い分けることも当然あり得ますし、それぞれの言語用にライブラリを選定したり実装するのは大変ですし、細かい挙動はそれぞれ異なることもあるでしょう。
それを Ambassador コンテナとして切り出すことで、Web アプリケーション本体の言語に関係なく、それらの機能を再利用することができます。
Kubernetes 上でどう実現するか
これも Pod 内に複数のコンテナを配置するという意味では Sidecar Pattern と同様となります。
Ambassador として起動するプロキシについては Envoy や Linkerd といったミドルウェアが存在します。
Quipper ではまだ本番利用はできておらず絶賛選定中ですが、リソース使用量の少なさ (Linkered が Java なのに対し Envoy は C++) と、コミュニティの活発さから考えて Envoy を使って行く可能性の方が高そう、というのが竹山の個人的な意見です。
Ambassador を可能にする Pod の特性
- Pod 内のコンテナ同士は
localhost
を通じて通信が可能である- 全ての Pod には IP アドレスが割り当てられるので、配置された Node に関係なく相互に通信することが可能ではあります。が、Sidecar と同様にコンテナ同士が常に同一の Node に配置されることで高速な通信が可能なので、プロキシとして使ってもそこの通信がボトルネックになりにくくなっています。
まとめ
このように、Sidecar Pattern や Ambassador Pattern は Kubernetes の運用を直接行わない Web Developer にとっても知っているメリットが大きいことがわかったと思います。
これらを理解していれば、Web アプリケーションを不要に複雑にすることなく、ビジネスロジックに集中できる分高速に開発でき、専用のよくテストされたミドルウェアに頼ることでより高い信頼性が得られることでしょう。
Envoy のようなミドルウェアを利用することでそういったリトライやトレーシングといったアプリ本来の機能ではないものを完全に分離できるか、というとまだまだ疑問がありますし検証も必要ではあります。(例えば、トレーシングで複数のリクエストを横串で束ねるのに必要なリクエスト ID は何かしらの方法で伝播させる必要があり、その実装はアプリケーションなりフレームワーク側に行う必要があるのでは?)
そういった問題やその解決方法についても調べていき、今後のブログでまた紹介できればと思います。
Quipper では Kubernetes 上でこのような世界を実現する SRE や、その中で高速にプロダクト開発を行う Web Engineer を募集しています。
参考文献
- The Distributed System ToolKit: Patterns for Composite Containers from Kubernetes Blog
- Kubernetes Patterns
- Design patterns for container-based distributed systems
- Sidecar pattern from Azure Architecture Center
- Ambassador pattern from Azure Architecture Center
- Service mesh data plane vs. control plane
- Service Mesh and Cookpad