数百のテナントが同居するマルチテナントKubernetesの世界

2019年12月06日 金曜日


【この記事を書いた人】
田口 景介

社会人生活の半分をフリーランス、半分をIIJで過ごすエンジニア。元々はアプリケーション屋だったはずが、クラウドと出会ったばかりに半身をインフラ屋に売り渡す羽目に。現在はコンテナ技術に傾倒中だが語りだすと長いので割愛。タグをつけるならコンテナ、クラウド、ロードバイク、うどん。

「数百のテナントが同居するマルチテナントKubernetesの世界」のイメージ
IIJ Engineers blog読者プレゼントキャンペーン

Twitterフォロー&条件付きツイートで「バリーくんぬいぐるみ」を抽選で20名にプレゼント!
応募期間は2019/11/29~2019/12/31まで。詳細はこちらをご覧ください。
今すぐツイートするならこちら→ フォローもお忘れなく!

IIJ 2019 TECHアドベントカレンダー 12/6(金)の記事です】

シニアテクニカルマネージャの田口です。今年はKubernetesに始まり、Kubernetesに終わる1年でした。皆さん、コンテナ化の準備は万端ですか?
IIJにおけるKubernetesの活用はまだ黎明期といったところですが、それでも急速に利用が進み、最も巨大なクラスタでは約200名のエンジニアが暮らし、数百のテナント(プロジェクト)が同居する規模にまで育ってきました。そのネタでブログを1本と思ったのですが、1年前に比べるとKubernetesはプロダクション環境での普及が進み、多くの情報が入手できるようになってきたので、ありきたりな話題はすでに十分語られている気がします。そこで、簡単にIIJのKubernetes環境であるIKE(IIJ Kubernetes Engine)を紹介した後で、いくつか特徴的なトピックをピックアップしたいと思います。

IKE(IIJ Kubernetes Engine)

IKEとは、2018年5月からIIJでサービス基盤として利用されているKubernetesディストリビューションです。現在国内3か所のデータセンターに展開されており、拠点ごとに一つのクラスタがデプロイされています。また、近々2拠点が追加される予定で、徐々に規模の拡大が図られています。これら拠点はいずれもIIJのクラウドサービス(IIJ GIO)上に構築されたものですが、その他にいくつものIIJサービスを組み合わせて実装されています。また、こうしたサービス群と連携するために、CSIプラグイン、イングレスコントローラ、CCM(Cloud Controller Manager)、監視用オペレータなどがIKEのために実装されています。

リソース サービス 用途
インフラ IIJ GIO terraform、Cluster APIでKubernetesクラスタのインフラを構築
ストレージ IIJ GIO GIO用CSIプラグインでPersistentVolumeを管理
ネットワーク SEIL BGPルータにIIJのSEILを使い、calicoと連携してクラスタネットワークを管理
ロードバランサ IIJ GIO、PulseSecure vTM GIO用クラウドコントローラからvTMを制御し、Ingress、service type LoadBalancerとして管理
DNS IIJ DNSアウトソースサービス DNSドメインの動的設定、ACME DNS-01認証などに利用

内容監視・通知

Barry prometheusが検知したアラートをBarryを利用してインシデントハンドリング

ただし、IKEの目的は自社サービスの活用ではありません。最も重要なのは、アプリケーションのポータビリティを高め(インフラを問わず同一パッケージでデプロイ)、運用を規格化し(環境に依存せず同一オペレーションで運用)、リリース速度の向上、スケーラビリティの向上、サービスの品質向上、システムリソースの低コスト化を実現することです。Kubernetesの価値を最大限に引き出すことが結果的に目的を達成することになるでしょう。

つまり、インフラ(自社他社IaaS、他社マネージドコンテナサービス、ベアメタル、etc)がなんであれ、このゴールが満たされていれば、それはIKEと呼べる環境なのです。

それでは、3つほどIKEにまつわるトピックを紹介したいと思います。

  • IKEのマルチテナンシーモデル
  • IKE Officeのネットワーク
  • IKEのモニタリングと通知

IKEのマルチテナンシーモデル

現在のKubernetesには公式にマルチテナントクラスタを構成する仕組みは提供されていません。しかし、Kubernetesの魅力をリソース集約によるコスト削減と大規模な共通運用基盤による運用効率化に見出すならば、マルチテナント化は避けては通れない道です。すでに様々な実装が行われていますし、CNCFでもMultitenancy WGで活発な議論が続けられていますが、新しい提案が続いておりまだ議論には時間がかかりそうな気配です。いずれは公式に盛り込まれるであろうことはわかっていても、独自の仕組みで実現せざるをえないと判断しました。

どのようにマルチテナントを実現するのかは議論がありますが、少なくとも以下4点を考える必要があります。IKEでは今のところ複雑な作り込みは避けて、公式にマルチテナント構成が提供されたときにコンフリクトしないことを重視した保守的な設計になっています。

  • アカウント管理と認証
    • アカウントごとにクライアント証明書を発行。Kubernetes APIだけでなく、IKEのアカウント管理APIなどへのアクセスにも利用する
    • アカウントは最低一つのテナント(kind: Group)に所属し、テナントへロールを割り当てる
  • テナント間のセキュリティ
    • ネームスペースとRBACでコントロール
    • アカウントが所有するネームスペースのリソースのみアクセス可能
  • テナントとホストの間のセキュリティ
    • PodSecurityPolicyでhostNetwork、hostPathなど、ホストノードへ影響を与えうる権限を除外
    • apparmor、selinuxの活用
  • クラスタスコープリソースの管理
    • 多くのクラスタスコープリソースは読み取りのみ可能
    • CRD、StorageClassなど一部リソースの作成は申請ベースで対応
  • リソースクォータ、リソースリミット
    • 設定しない。適切なリソース量の見積もりは困難
    • 必要ならば、特定テナントがノードを占有するように設定

ただ、限界を感じる部分は少なくありません。特にクラスタスコープリソースへのアクセスをテナントに許可できないのは、非常にストレスを感じます。やはりネームスペーススコープとクラスタスコープの間にテナントスコープを設けて、複数のネームスペースへまとめて権限を与えたり、内部的に名前空間を分離してクラスタスコープを読み書きできるようにしない限り、アクセスコントロールの悩みは解決しないでしょう。

また、リソースの奪い合いは大きな問題ですが、完全な解決策はなさそうです。ワークロードごとにリスクを見定めて、安定性を求めて固定的にリソースを割り当てるのか、効率化を求めて集約率を高めるのか判断すべきと考えています。とはいえ、もっとインテリジェントな方法は考え続けたいものです。

IKE Officeのネットワーク

IKEは拠点ごとに複数のクラスタがありますが、その一つにIKE Officeと呼ばれる特殊なクラスタがあります。これはサービス基盤ではなく、開発や検証、各種ツールのホスティングを目的としてデザインされたクラスタです。基本的にはIKEと共通の設計となっていますが、IKE OfficeのPodネットワークは我々エンジニアが暮らすオフィスネットワークに接続されている点が特殊です。通常、Podネットワークはクラスタ内部に閉じており、外部からPodへアクセスするにはservice type NodePortまたはLoadBalancerを使う必要があるのですが、Kubernetesのネットワーク仕様上、つないでいけない理由はありません。

直接つながっているということは、Podへアクセスするためにいちいちserviceを作る必要がなく、以下のようにいきなりPodへアクセスできるということです。service type NodePortのようにたびたび変わるポート番号を調べる必要がなく、またservice type LoadBalanacerのようにたやすく枯渇しがちなリソースを心配する必要もありません。

$ kubectl create deploy nginx --image=nginx
$ curl http://$(kubectl get pods --selector=app=nginx -o jsonpath="{.items[].status.podIP}")

動的にアサインされるPodアドレスが面倒でしょうか?そんなときはIPアドレスをDNSへ登録することもできます。本来はservice type LoadBalancerやIngressで使うために用意した仕組みですが、ヘッドレスサービス(cluster-ip=None)を利用することで、Podアドレスを登録することも可能です。こうして登録されたDNSレコードのTTLはごく短時間に設定されているため、頻繁にPodを再起動しても支障はありません(テストには向きませんが)。

$ kubectl create deploy nginx --image=nginx
$ kubectl expose deploy nginx --port=80 --target-port=80 --type=ClusterIP --cluster-ip=None
$ kubectl annotate service nginx "external-dns.alpha.kubernetes.io/hostname=nginx.example.com"
$ curl http://nginx.example.com/

また、こうしてクライアントからのアクセスが容易になるという面以外にも、何も設定することなくクラスタの外にあるリソースと連携できるのも大きなメリットです。例えば、データベースはクラスタの外に置いておき、アプリケーションサーバだけKubernetes上にデプロイしたいとしましょう。IKE Officeならばすぐつながるということです。

利便性を重視した結果、センシティブなデータを扱う場面ではNetworkPolicyの設定が欠かせないのはデメリットですが、そこはプロダクション環境版IKEと使い分けです。足元クラスタに近い使い勝手がありながら、SREチームによって運営される充実したエコシステムが利用できる環境はなかなかよいものです。

IKEのモニタリングと監視

CI/CDツールを使ってパイプラインを構成し、デプロイメントを自動化している組織は多いでしょう。しかし、アプリケーションが動いただけでデプロイ完了と言えますか?どんなシステムであれ、モニタリングされ、アラートが通知される状態でなければ、リリース完了とは言えません。リリースを自動化するのであれば、監視エージェントがデプロイされ、監視ルールが投入され、アラートが通知される状態にしたいものです。

IKEにおいては、prometheus-operatorとmonitoring-controller(IKE専用監視コントローラ)によってこれが実現されています。prometheus-operatorを利用すると、マルチテナントな環境でもPrometheusRuleリソースに設定された監視ルールを収集し、Prometheusインスタンスへ適用することが可能です。Kubernetesの監視にPrometheusを利用するならば、必携のツールと言えるでしょう。

問題は通知設定の方です。シングルテナントな環境ならばalertmanagerへ決め打ちの設定をすれば十分ですが、動的にアカウントが増減する環境で、テナントごとに通知先が異なる場合は、なんらかのコントローラが必須です。また、多くの場合、通知を送信する側だけでなく受け取る側の設定も必要になるでしょう。IKEの環境では、CRDで定義された通知設定リソースを利用することで、メールとMicrosoft Teams、それにBarryと名付けられたIIJのインシデントハンドリングシステムへの通知が設定できるようになっています(Barryについて詳しくはこちら)。

例えば、ネームスペースexampleで検知されたアラートをBarryへ通知したければ、以下のようなBarryConfigリソースを作成します。するとmonitoring-controllerがまずBarryに通知用チャネルを開設します。続いて、AlertmanagerにBarryへの通知設定を追加します。あとはPrometheusRuleに監視ルールを書けば、監視がスタートします。

apiVersion: monitoring.ike.iij.jp/v1beta1
kind: BarryConfig
metadata:
  name: test
  namespace: exmaple
spec:
  channels:
  - mute: true
    name: example-channel
  tokenSecretName: barrytoken

だいぶ駆け足になってしまいましたが、IKEの紹介をさせていただきました。今回のアドベントカレンダーではもう一枠もらっているので、そちらではIKEのネットワークアーキテクチャについてもう少し触れたいと思います。よろしければ、そちらもよろしくお願いします。

田口 景介

2019年12月06日 金曜日

社会人生活の半分をフリーランス、半分をIIJで過ごすエンジニア。元々はアプリケーション屋だったはずが、クラウドと出会ったばかりに半身をインフラ屋に売り渡す羽目に。現在はコンテナ技術に傾倒中だが語りだすと長いので割愛。タグをつけるならコンテナ、クラウド、ロードバイク、うどん。

Related
関連記事