@amsy810's Blog

~Kubernetes / Cloud Native / Docker / etc~

Kubernetesにおけるマルチクラスタ関連手法の分類

こんにちは。青山(amsy810)です。

Kubernetesには複数のクラスタを扱うための技術がいくつかあります。 今回は幾つかのパターンに分けて紹介していきたいと思います。 また、将来的にこうなったら面白いかもという話をするために、すぐにプロダクション利用できない話やWebサービスでは使わないような話も含まれています。

【目次】

  • 複数クラスタを利用する理由
  • 複数クラスタを管理するパターン
    1. GitOpsで複数クラスタを管理するパターン
    2. 複数のKubernetesクラスタをFederateするパターン
    3. Virtual kubeletパターン
  • マルチクラスタ時のロードバランサの扱い
    1. LoadBalancer の機能を利用して分散する
    2. DNS を利用して分散する
    3. Multi Cluster にまたがる Service Mesh を利用して転送する
  • マルチクラスタでPod Networkを繋ぐ
  • ストレージの扱い
  • マルチテナンシー関連の技術
  • 超個体型データセンターへの応用

複数クラスタを利用する理由

複数クラスタで構成するには下記のような理由があるのではないでしょうか。 何がしたいかによっては、選択できる手法も限られてくるでしょう。

  • 耐障害性、信頼性向上、ディザスタリカバリ
  • マルチクラウド・ハイブリッドクラウド対応
  • ジオロケーションによるパフォーマンス問題の解消
  • 機密データのローカリティを確保する
  • In-place クラスタアップグレードを避ける
  • スケーラビリティの確保
  • エッジ・フォグ環境に適用する

北山さんがマルチクラスタのメリット・デメリットでおすすめの記事をツイートしていたので、こちらもメモしておきます。

複数クラスタを管理するパターン

複数クラスタを管理する方法は主に3つあります。

1. GitOpsで複数クラスタを管理するパターン

GitOpsでは、Gitリポジトリに保存されたマニフェストファイルをKubernetesクラスタに対して同期することでKubernetesクラスタの管理を行います。 この時に、同じリポジトリを参照することで同等のクラスタを作成することができます。 また、ArgoCD では Kustomize や Helm と連携することができるため、レプリカ数や一部の設定を変更したり、一部のマイクロサービスだけデプロイするといった選択をすることも可能です。

現状プロダクションで利用するとなると、GitOpsで複数のクラスタに対してマニフェストを適用する方法を取ることが多いでしょう。

f:id:masaya_aoyama:20200616235216p:plain

2. 複数のKubernetesクラスタをFederateするパターン

Kubernetesには、複数のクラスタを束ねて管理する Federation v2 の開発が SIG-MultiClsuter によって進められています。 Federation v2では、マネジメントクラスタが複数のターゲットクラスタを管理する形になります。 マネジメントクラスタに対して、FederatedDeploymentやFederatedServiceなどのリソースを作成すると、複数のターゲットクラスタにDeploymentやServiceが作成されるようになっています。 また、CustomResoruceなどもFederetadXXXとして扱うことができるようになっています。

f:id:masaya_aoyama:20200617000243p:plain

Federationでも、FederatedDeploymentなどの定義時に特定のフィールドにパッチを当てることができます。 また、ClusterSelectorを使用して特定のクラスタのみにデプロイすることも可能です。

kind: FederatedDeployment
spec:
  overrides:
    - clusterName: cluster1
      clusterOverrides:
        - path: "/spec/replicas"
          value: 5
    - clusterName: cluster2
      clusterOverrides:
        - path: "/spec/template/spec/containers/0/image"
          value: "nginx:1.17.0-alpine"

他にも、複数のクラスタ横断でレプリカ数を維持するようなスケジューリングを行うReplicaSchedulingPreferenceなどの機能もあります。クラスタ横断でDeploymentを柔軟に展開したい場合には利用すると良いでしょう。

apiVersion: scheduling.kubefed.io/v1alpha1
kind: ReplicaSchedulingPreference
metadata:
  name: test-deployment
spec:
  targetKind: FederatedDeployment
  totalReplicas: 9
  clusters:
    A:
      weight: 1
    B:
      weight: 2

3. Virtual kubeletパターン

Virtual kubeletはKubernetesに仮想ノードを登録し、その仮想ノード上割り当てられたPodは「クラスタ外にあるコンピューティングプール」で起動する仕組みを提供します。 コンピューティングプールは、一般的にServerlessなCaaSサービスが多いですが、他にもエッジ環境におけるノードや別のコンピューティングクラスタのケースもあります。

f:id:masaya_aoyama:20200617004850p:plain

Virtual kubeletで扱えるコンピューティングプールには、さまざまなプロバイダーが提供されています。 大きく分けると3種類でしょうか。

Serverless CaaS バックエンド向けのプロバイダでは、無尽蔵なコンピューティングプール上に迅速にPodをオフロードして起動するような使い方ができたり、メインで使うことでKubernetesノードを管理する必要がなくなります。 なお、AWS Fargateプロバイダーは、EKS on Fargateでは使われていません。

別コンピューティングクラスタ向けのプロバイダでは、Serverless CaaSの代わりに、HashiCorp Nomadや別のKubernetesクラスタ(Admiraltyの場合)などのコンピューティングクラスタに対してPodをオフロードすることができます。 Admiralty Multi-clsuter scheduler はその名の通り、複数のKubernetesクラスタに対してスケジューリングする用途でも使うことができます。

エッジ環境や単一ノード向けのプロバイダでは、Virtual Nodeに割り当てられたPodはエッジ環境上のクラスタで起動します。 クラウド上に起動しているKubernetes Masterから、エッジ環境のノード郡を管理できるようになります。

これらのソリューションはいくつかしか試せていませんが、全てにおいて「通常Nodeで起動したPod」と「Virtual kubeletで起動したPod」の間のPod Networkが適切に接続されたままになるのかが気になるところです。

マルチクラスタ時のロードバランサの扱い

マルチクラスタ環境では、ロードバランサなどのサービスを提供するエンドポイントを払い出す方法が大きく分けて3種類あるでしょう。

1. LoadBalancer の機能を利用して分散する

Anthos の Multi Cluster Service / Ingress では、同一IPアドレスを利用して複数リージョンにまたがる複数のクラスタに対してリクエストを転送することができます。なので、米国と日本でマルチクラスタ構成を組み、リクエスト元の地理に応じて最適なクラスタに転送することができます。マルチリージョンでマルチクラスタ構成をするにあたって、この機能は便利だなという気持ちになります。

CyberAgent の AKE でも、単一のVIPを使って複数のクラスタ上に展開されるServiceやIngressに利用できるようになっています。主にクラスタ間移行をゼロダウンタイムで行えるようにすることが目的です。

f:id:masaya_aoyama:20200617004501p:plain

2. DNS を利用して分散する

上記のような環境が利用できない場合は、DNS Round Robin で構成するのが一般的でしょう。 Federation v2の場合は ExternalDNSと連携するようになっており、作成されたLoadBalancer Service と Ingress リソースが払い出す複数のVIPを自動的にDNSに登録する、Multi-Cluster Service DNSMulti-Cluster Ingress DNS の機能が提供されています。

f:id:masaya_aoyama:20200617004522p:plain

3. Multi Cluster にまたがる Service Mesh を利用して転送する

クラスタ間の距離がある程度近い場合は、特定のクラスタでリクエストを受けて隣のクラスタにリクエストを転送するようにするのも良いでしょう。 Istio ではMulti Cluster Service Meshの機能が用意されており、複数のクラスタにまたがってリクエストを分散させることができます。

f:id:masaya_aoyama:20200617012818p:plain

マルチクラスタでPod Networkを繋ぐ

マルチクラスタでPod Networkを構成するには、Submarinerを利用することができます。 Submarinerでは、相互接続するすべてのクラスタのノード1台以上のGateway用のAgent(Submariner Gateway Engine)を展開し、このGateway Agent間でIPSecトンネルが張られるようになっています。 また、すべてのノードにはRoute Agentが展開されており、これによりクラスタを跨いだPod間通信ができるようになっています。

https://github.com/submariner-io/submariner/raw/master/docs/img/architecture.jpg

またSubmarinerでは、複数のクラスタで利用されるクラスタDNSのSuffix(cluster.local)は異なるものを指定し、ClusterIPのCIDRも別のものを指定します。 そのため、特定のクラスタから別のクラスタのServiceに対してリクエストが送れるように、双方のDNSサーバに対してクエリをかけられるようにしておく必要があります。

f:id:masaya_aoyama:20200617015203p:plain

ストレージの扱い

マルチクラスタ・マルチテナント環境におけるストレージの扱いですが、VolumeSnapshotの機能が導入されることである程度クラスタ間移行はしやすくなるかもしれません。 一方で、CSI Driver間でのマイグレーションなどは現状はスコープ外のため、同一CSI Driver、すなわち同一のストレージバックエンドに限れば、クラスタ間でPersistentVolumeの移行もしやすくなるかもしれません。 マルチクラスタの理由にもよりますが、同一クラウドでマルチクラスタ構成であれば、シームレスな移行も夢では無いかと思います。

マルチテナンシー関連の技術

マルチクラスターと類似したものとして、マルチテナンシーの考え方もあります。 Kubernetesコミュニティにも、SIG-MultiClsuterMulti-Tenancy WG の 2 種類があります。

下記に Virtual Cluster や Hierarchical Namespace の incubating project のDesign Docsや実装が公開されています。 GitHub - kubernetes-sigs/multi-tenancy: A working place for multi-tenancy related proposals and prototypes.

また、Hierarchical Namespace Controller については、以前検証した結果をブログにまとめてあるのでご興味があれば参考にしてみてください。

amsy810.hateblo.jp

また先週末の KubeFest でぞえとろさんがちらっと話題に出してた、kioskやloft あたりも気になっています。 この辺りも時間があれば調べて、マルチクラスタ編もまとめるかもしれません。

github.com github.com

超個体型データセンターへの応用

客員研究員として参加しているさくらインターネット研究所では、ビジョンとして「超個体型データセンター」を掲げています。 私の理解でざっくりと説明すると、「中小規模のデータセンター」や「エッジ・フォグ環境」などが相互に有機的に接続され、自律分散的に協調動作するだろうというビジョンです。

research.sakura.ad.jp

今回は備忘録的にKubernetesにおけるマルチクラスタ周りの技術の分類や手法をまとめてみました。 ベースの技術としてKubernetesを使うべきというわけではありませんが、技術検証やPoCなどではKubernetesやCRD/Controllerによる拡張性を利用できると良いかと思っています。

今回紹介したマルチクラスタ関連の技術を組み合わせることで、少しそれらしいものはできるかもしれませんが、超個体的(相互に有機的に接続され、自立分散的に協調動作)というよりかは、中央集権的な手法が多いと感じています。

そのため理想としては、各Kubernetesクラスタを作成後にAgentを各クラスタにデプロイするだけで、Agentが相互に情報を交換しあうことで最適なワークロードの配置を行ってくれるような、超個体的なクラスタを作れると面白いのかなと思っています。 Kubernetesの場合には、このAgentの実装はCustom Controllerを実装すればよいので、あとはロジックをどうするかをもう少し明確に考える必要があると思っています。 ロジックやメトリクスがPlugableにできるように設計できれば、最初は局所解に陥るような単純なものでも良いのかもしれません。

f:id:masaya_aoyama:20200617025904p:plain

おしまい。 Twitter: amsy810

Flexispotで最大サイズの昇降机を自作した話

こんにちは。 青山(amsy810)です。

皆さん在宅勤務が長引いて大変な日々をお過ごしでしょうか。 かくいう私も仕事場所を作ってたんですが、長期かつ運動もしなくなる状況では耐えられなかったため昇降デスクを作ることにしました。 最大サイズで作った例が見当たらなかったので、まとめておきます。

最近の昇降デスクはかなり安くなってるのですね。 出来上がりはこんな感じです。

どの昇降デスクを選んだのか

今回選んだのは Flexispot の E3 シリーズです。 デスクの脚だけでも売っていて、天板は自由に選択することが可能です。

天板の最大サイズは 200cm x 80cm合計100kgまで耐えられるようです。 (ページによって80kgと書いてあるところもあるので、どちらが正しいのかは微妙) また、昇降高も60cm-123cmなのでかなり幅広く対応できます。

https://flexispot.jp/media/catalog/layer/cache/x560/e/3/e3b-s.jpg

この脚自体が35kgくらいあるので、なかなかしっかりしています。 個人差はあるでしょうが、この高性能な脚だけで5万を切るのはなかなか安いなと思っています。 私自身中学時代からゲームばっかりしてたので、その頃から腰痛とは付き合ってますし。

木材の購入

次に木材の選定ですが、やはり目指すは最大サイズです。 でかいホームセンターなどでよく売られているお手頃な一般的な木材の多くは、大きくても 180cm x 60cm とかが多いイメージです。 豊洲のビバホームでもこのくらいのサイズが最大のようでした。

そのため、このサイズの木材に関しては無垢材(横接ぎ材)や集積材などから切り出してもらうしかありません。

今回は下記で木材を発注しました。 4/10に入金して4/17には到着したので、納期も5営業日程度とかなりスピーディーでした。

http://sakuma-mokuzai.com

今回は木材はベイマツ集積材(米松)にしました。 一番風合いが自然だなと思ったのと直感です。

長く使うならウォルナットの無垢材に奮発しようと思っていたんですが、こちらは納期が2-3週間かかり、かつ今回はGWも跨ぐとのことだったので見送りました。 一時しのぎでベイマツ集積材にしたのですが、後述するオイルフィニッシュの出来が良かったのでこのままでも良いかなと思っています。 https://shop.woodworks-marutoku.com

強度と耐荷重について

机を作るに当たって一番気にしなければならないのは強度です。 200cm x 80cm ともなると、2cm程度の板だと流石に心許ないでしょう。 今回は 4cm の厚さで作りました。 4cmくらいあると結構しっかりしている感じですが、見た目的には3cmくらいの気分です。

200cm x 80cm x 4cm の米松の木材となると、大体45kgくらいの重さになります。 4.5cmか悩んだのですが、一人で持てる重さを考えて4cmにしたというのもあります。

ちなみにこのサイズであれば、多少力持ちであれば一人で倉庫からピックアップして車に積んで、台車を使えば家に運び込むこともできます。(個人の見解です)

現時点で床への重さは35kg(脚)+45kg(板)です。 床に対する耐荷重はここに人や椅子、モニタやモニタアームなどが加わります。 気になる方は床への耐荷重も調べておくと良いかもしれません。 調べた感じだと1m2あたり180kgまで耐えられるようなので、大丈夫かなと判断しました。

www.jsca.or.jp

机自体の耐荷重は45kgだけなので、55kg分はモニターなどで利用することが可能です。

加工について

佐久間木材さんの場合糸面取りは無料でやってくれます。 それ以外の加工はヤスリとかを買って自分でやれば30分くらいで終わるのでおすすめです。 納品された時点で木材自体は240-400番台のヤスリでヤスリがけされていたような感じがするので、そのまま使っても大丈夫な気がします。 ただし糸面取りだけだと角が結構痛いので、多少の加工はしておいたほうが良いと思います。

NT(エヌティー) ドレッサー 中目 L-30P

NESHEXST ハンドサンダー サンドペーパー ヤスリ ホルダー 紙やすり

オイルフィニッシュについて

今回は塗装ではなく、オイルフィニッシュを行いました。 塗装ってムラができやすいのでかなり真面目にやらないとダメなんですよね… その点オイルフィニッシュであれば刷毛とキムワイプさえあればどうにかなります。

手順としても、下記のステップを踏むだけなので比較的簡単です。 塗る作業についても、サラダ油くらいのオイルを木に染み込ませて木の防御をする感じなので、ムラは出づらく楽チンでした。 私は片面塗り終わった後、もう片面は1時間位たってすぐにひっくり返して塗り始めまてしまいましたが、大丈夫そうでした。 ちなみに片面しか塗らないのは、湿気の吸い具合にむらが出て木の反りの原因になるとのことなので、両面塗りは必須です。

  • 400番でヤスリがけをする
  • たっぷり片面を塗る
  • 30分待つ
  • 二度塗りする
  • 600番の耐水ペーパーで濡れたままヤスリがけ
  • キムワイプで水分を完全に拭き取る(可燃性なので廃棄に注意)
  • 12-24時間放置

オイルフィニッシュは着色されたやつもあります。 今回はウォルナット調のものを利用しましたが、木目がいい感じに出てくれてよかったです。

ワトコオイル ダークウォルナット W-13 1L ハンディ・クラウン 差し口ベロ 40mm

組み立てについて

脚のパーツは簡単に組み立てられます。 脚を180cmに広げた状態でも一人でも持ち上げられます。

木材と脚はネジでつなげますが、木材を付けた脚を一人でひっくり返すのは正直難しいと思います。 なので今回は木材を脚の上に載せた状態でネジ止めをしました。 今回は木材自体が重いのもあってか、この状態でもネジ止めができました。 ネジ止めするときも昇降して高い位置にしておけば、下からでも作業がしやすかったです。

感想

スタンディング、思ったより最高です。意外と疲れないんですね。 ただ思ったより足裏は痛くなるので、たしかに下に敷く厚手のマットは必要だなぁという気持ちになりました。

その他のWFH環境

品目 商品名 オススメ理由
椅子 アーロンチェア 12年保証?があるので月額換算すれば安い。というか腰痛すぎて高校生のときに貯金で買いました。
モニター 328P6AUBREB/11 32インチ4Kくらいがちょうどいい。type-C 接続可
モニターアーム エルゴトロン デザインよし
キーボード REALFORCE 自作したいけど、今は慣れ親しんだコレで…
マウス MX Master 言わずとしれた定番
マウスパッド steelseriaesのやつ さらさら
ウェブカメラ Logicool c922 画質よし
マイク MPM-1000U コスパよし
オットマン Amazonで適当に コスパ良し

後は配線周りを工夫して、モニタの片側をiPadで利用したりなどもできるようにしています。

あと、椅子の下にはなにか敷いたほうがいいです。長い間使ってると床を結構傷つけます。ニトリのタイルマットが結構オススメで、汚れても交換簡単、安い、厚みがあるので安心です。

まとめ

備忘録+メモ書き的な感じなので乱文ですが、まとめておきました。 引き続きひきこもるぞ。

Conftest で CI 時に Rego で記述したテストを行う

Conftest で CI 時に Rego を用いたテストを行う

こんにちは。青山(@amsy810)です。 実は少しだけ PLAID さんでお手伝いをしており、CI に Conftest を組み込んで Kubernetes マニフェストのポリシーチェックを行うようにしたので、その時の備忘録を書いておきます。 PLAID さんでも GKE を基盤として選定して開発しています。

Conftest とは?

Conftest は Rego 言語で記述したポリシーを用いて、JSONYAML などがポリシーに合致しているかをチェックする OSS です。 今回は Kubernetesマニフェストがポリシーに合致しているかどうかを判別するために利用します。

例えば下記の例では、Deployment や StatefulSet などの Workloads リソースの Selector や起動してくる Pod のラベルに app ラベルが付与されるかをチェックしています。このように、比較的記述しやすい言語を利用してポリシーを定義していくことが可能です。

deny[msg] {
  input.kind == workload_resources[_]
  not (input.spec.selector.matchLabels.app == input.spec.template.metadata.labels.app)
  msg = sprintf("Pod Template 及び Selector には app ラベルを付与してください(spec.template.metadata.labels.app、spec.selector.matchLabels.app): [Resource=%s, Name=%s, Selector=%v, Labels=%v]", [input.kind, input.metadata.name, input.spec.selector.matchLabels, input.spec.template.metadata.labels])
}

ポリシーチェックの実行は下記のコマンドを実行するだけなため、容易に CI に組み込むことが可能です。

# サンプルは  https://github.com/MasayaAoyama/conftest-demo に配置してあります
conftest test --policy ./policy --input yaml manifests/*

Rego 言語については OPA の公式サイトを見てください。

www.openpolicyagent.org

Built-in Function については下記のソースコードも参考になります。

github.com

OpenPolicyAgent / Gatekeeper との違い

類似プロダクトとして、OpenPolicyAgent / Gatekeeper もありますね。 Gatekeeper を利用したポリシーチェックでは、Kubernetesマニフェストが登録する際に AdmissionWebhook の ValidatingWebhook が実行されるタイミングで評価されます。そのため、Mutating されたあとの状態のチェックも行うことが可能です。

GitOps の場合はマニフェストリポジトリの状態がクラスタの状態と同じ状態になるように構成します。Gatekeeper を利用していて GitOps などを行っている場合には、マニフェストリポジトリに対してコミットした後、実際にクラスタに反映する際に失敗して初めてポリシーに違反していることが確認できます。

一方で Conftest の場合には、CI の際にチェックすることが可能です。そのため、マニフェストが適切かどうかはリポジトリにコミットする前、すなわちマニフェストファイルに対する更新時にもチェックするようにしましょう。

Gatekeeperクラスタへの登録時に安全に防ぐようにし、GitOps などを行っている場合には Conftest で CI でも前倒しして事前にチェックするように 2 段構えにすると良いと思います。

余談ですが、GCP の Anthos Config Management で提供されている Anthos Policy Controller も Gatekeeper を利用しているようです。 https://cloud.google.com/anthos-config-management/docs/concepts/policy-controller

設定しているポリシーについて

下記のようなポリシーを設定しています。一部は PodSecurityPolicy でも設定可能ですが、PSPGatekeeper 同様 Kubernetes API 登録時にチェックが行われるため、Conftest でもチェックすることに意味はあるでしょう。

  • 単一リソース内でのチェック
    • 特権コンテナの利用制限
    • 各種リソースや Selector に特定のラベルが付与されているかのチェック
    • コンテナのイメージタグの制限
    • TerminationGracePeriodSeconds の設定時間が既定値以下かのチェック
    • Resource Requests が設定されているかのチェック
    • Limits/Requests の差のチェック
    • Probe の failureThreshold などの設定値が既定値の範囲内かのチェック
    • Inter-pod anti-affinity が設定されているかのチェック
    • hostPath 利用時に Readonly になっているかのチェック
    • Service の ClusterIP が静的に設定されていないかのチェック
    • Ephemeral Storage の利用上限設定がされているかのチェック
    • etc
  • クラスタ全体での横断チェック
    • ラベルがバッティングしていないか
  • etc

Gatekeeper とのポリシーファイルの違い

Gatekeeper の場合には、現在既に Kubernetes に登録されているデータを data.inventory から参照することが可能です。

クラスタオブジェクトの場合: data.inventory.cluster[<groupVersion>][<kind>][<name>]
(例: data.inventory.cluster["v1"].Namespace["gatekeeper"])

Namespace オブジェクトの場合: data.inventory.namespace[<namespace>][groupVersion][<kind>][<name>]
(例:data.inventory.namespace["gatekeeper"]["v1"]["Pod"]["gatekeeper-controller-manager-d4c98b788-j7d92"])

これを利用すると、下記のように既に登録されている Ingress リソースの host 名と衝突していないかといったチェックも行うことができます。

# https://github.com/open-policy-agent/gatekeeper/blob/master/library/general/uniqueingresshost/src.rego
package k8suniqueingresshost

identical(obj, review) {
  obj.metadata.namespace == review.object.metadata.namespace
  obj.metadata.name == review.object.metadata.name
}

violation[{"msg": msg}] {
  input.review.kind.kind == "Ingress"
  re_match("^(extensions|networking.k8s.io)$", input.review.kind.group)
  host := input.review.object.spec.rules[_].host
  # 既にクラスタに登録されているデータを参照
  other := data.inventory.namespace[ns][otherapiversion]["Ingress"][name]
  re_match("^(extensions|networking.k8s.io)/.+$", otherapiversion)
  other.spec.rules[_].host == host
  not identical(other, input.review)
  msg := sprintf("ingress host conflicts with an existing ingress <%v>", [host])
}

Conftest による横断のチェック

さて、本ブログ記事で一番お伝えしておきたい内容です。 実際マイクロサービスが複雑化してくると、Conftest の段階で「Deployment に対して PodDisruptionBudget が設定されているか」「Service の Selector にマッチする Deployment 等が存在するか」「横断でラベルがバッティングしていないかのチェック」といった横断的なチェックを機械的に行わなければ予期せぬ障害が発生するかもしれません。

Conftest でも --combine オプションをつけることで複数のファイルをロードして横断でチェックすることが可能です。

conftest test --policy ./policy --input yaml ./manifests/* --combine

このとき、conftest にはリソース自体が入ってくるのではなく、「ファイル」「リソース」の配列で入ってくるため注意してください。なお、ひとつのファイルに単一リソースしか書かれていない場合は、「ファイル」の配列の後にそのままリソースが入ってきてしまい、ファイルによって構造が変わってきてしまいます。そのため、一つのファイルに単一のリソースしかない場合でも --- を 1 つ目のリソースの後に明示的に記載するようにしましょう。

deny[msg] {
  files := input[_]
  resources := files[_]
  other_files := input[_]
  other_resources := other_files[_]

  resources.kind == workload_resources[_]
  other_resources.kind == workload_resources[_]

  resources.spec.template.metadata.labels == other_resources.spec.template.metadata.labels
  resources.metadata.name != other_resources.metadata.name

  msg = sprintf("リソースのラベルが衝突しています: [%s/%s <=> %s/%s]", [resources.kind, resources.metadata.name, other_resources.kind, other_resources.metadata.name])
}

まとめ

Conftest は非常に手軽に CI にマニフェストのチェックを導入できるためおすすめです。また、OPA のポリシーファイルを使い回しやすいため、OPA と合わせて利用する際もそこまでコストを掛けずに利用することが可能です。ぜひセキュリティや組織ポリシーの徹底のためにも、OPA / Gatekeeper と合わせて Conftest の利用を検討してみてください。

今回利用したサンプルファイル類は下記に上げています。

github.com

EKS on Fargate:virtual-kubelet の違い + Network/LB 周りの調査

EKS on Fargate

こんにちは。 サイバーエージェントの青山(@amsy810)です。

この記事は Kubernetes3 Advent Calendar の 4日目の記事です(EKS #2 にもクロスポストしています)。 re:Invent で EKS 関連の何かしらの発表がされることを見越して Advent Calendar を埋めたので、書くネタが見つかってホッとしています。

KubeCon 会期中に 「Managed Worker Node for EKS」 がリリースされ歓喜の声が上がりましたが、今回は re:Invent で 「EKS on Fargate」 がリリースされ歓喜の声が上がっているようです。 今回は EKS on Fargate のアーキテクチャを見ていきたいと思います。virtual-kubelet と近いと思ってますが果たして。 (EKS on Fargate の詳細な記事はプロの誰かが書くと思うので、私は主に virtual-kubelet との対比と、大好きなネットワーク周りの軸でかければと思います。)

EKS on Fargate とは?

f:id:masaya_aoyama:20191204151318p:plain

EKS on Fargate は 12/3 PST にリリースされた、AWS Fargate をバックエンドとして、Kubernetes Pod を起動することができる機能です。Fargate がいることにより、EC2インスタンスを管理する必要がなくなります。よしなにやっておいてほしい人からすると嬉しいのではないでしょうか。 https://aws.amazon.com/jp/blogs/aws/amazon-eks-on-aws-fargate-now-generally-available/

ご存知の方もいるかと思いますが、ここまで聞くと類似プロダクトとして、OSS で開発が進められている virtual-kubelethttps://github.com/virtual-kubelet/virtual-kubelet) のようにも聞こえますね。virtual-kubelet は、Kubernetes の Node として1台の仮想的なノードを追加しておきます。その仮想ノードの後ろには、膨大なコンピューティングリソースを持ったコンテナ実行基盤(プール)のようなものが待ち構えており、その仮想ノードにスケジューリングされた Pod はそのリモートノード上で Pod が実行されるようになります。代表的な実装例としては、Microsoft Azure の ACI などでしょうか。

f:id:masaya_aoyama:20191204131908p:plain
virtual-kubeletの概要
参考:https://raw.githubusercontent.com/virtual-kubelet/virtual-kubelet/release-1.2/website/static/img/diagram.svg

virtual-kubelet のプロバイダーの中に AWS Fargate Provider もあるのを把握していたため、これが EKS on Fargate だと思っていましたが、どうやら違うようですリポジトリhttps://github.com/virtual-kubelet/aws-fargate)をみても、半年〜1年程度更新がないため、virtual-kubelet 経由のものよりかは、EKS on Fargate を使っていくべきでしょう。

EKS on Fargate の検証

検証環境は下記のとおりです。

クラスタの構築時に --fargate オプションを利用することで構築可能です。リリース時点から ap-northeast-1 でもデプロイ可能でした。

$ eksctl create cluster --name amsy810-cluster --region ap-northeast-1 --fargate

しばらくすると、EKS クラスタの構築が完了します。初期段階では coredns だけ起動している状態なため、2 Pod だけが立ち上がっている状態です。

# (一部アウトプットを省略)
$ kubectl get pods -A -o wide
NAME                      READY  STATUS   IP              NODE                                                     
coredns-6d75bbbf58-5jn27  1/1    Running  192.168.129.12  fargate-ip-192-168-129-12.ap-northeast-1.compute.internal
coredns-6d75bbbf58-96nbt  1/1    Running  192.168.166.59  fargate-ip-192-168-166-59.ap-northeast-1.compute.internal

ノードの状態を見てみると 下記の通り 2 台だけ起動している状態です。

# (一部アウトプットを省略)
$ kubectl get node -o wide
NAME                                                        STATUS   ROLES    VERSION       INTERNAL-IP      EXTERNAL-IP   OS-IMAGE         KERNEL-VERSION                  CONTAINER-RUNTIME
fargate-ip-192-168-129-12.ap-northeast-1.compute.internal   Ready    <none>   v1.14.8-eks   192-168-129-12   <none>        Amazon Linux 2   4.14.152-127.182.amzn2.x86_64   containerd://1.3.0
fargate-ip-192-168-166-59.ap-northeast-1.compute.internal   Ready    <none>   v1.14.8-eks   192-168-166-59   <none>        Amazon Linux 2   4.14.152-127.182.amzn2.x86_64   containerd://1.3.0

EKS on Fargate では、virtual-kubelet とは異なり Pod 1つにつき 1 Node が展開される ような形になっています。virtual-kubelet の場合には、「kubectl get node」した際に、1 つの大きな 仮想 Node だけが存在しており、その仮想 Node 上で複数の Pod が起動するような形になるため、異なるポイントです。また、Fargate Node の IP と Pod の IP は同じになっています。他にも、各 Node の Pod Capacity が 1 になっていたり、「eks.amazonaws.com/compute-type=fargate:NoSchedule」 taints も付与されていました。

なお、当然ですが下記のような 2 Container in 1 Pod な Pod も、1 つの Fargate Node にデプロイされます。

cat << _EOF_ | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: sample-2pod
spec:
  containers:
    - name: nginx-container
      image: nginx:1.12
    - name: redis-container
      image: redis:3.2
_EOF_
# (一部アウトプットを省略)
$ kubectl get pods sample-2pod -o wide
NAME          READY   STATUS    IP                NODE
sample-2pod   2/2     Running   192.168.151.223   fargate-ip-192-168-151-223.ap-northeast-1.compute.internal

Fargate で起動する Pod の選択

EKS on Fargate では、通常ノードではなく Fargate で起動する Pod を設定しておきます。例えば、eksctl で作られるクラスタのデフォルト では default namespace と kube-system namespace にスケジューリングされる Pod は Fargate 側で実行されるようになっています。そのため、事前に条件を設定していない Namespace や Pod Selector にマッチしていない Pod の場合には、Fargate 側で実行されない点だけ注意が必要です。現状だとすべての Namespace という設定はできなさそうですが、いずれ対応されるんじゃないかなと思います(12/4 18:00 追記:既に Tori さんから Issue 化されていると教えていただきました!流石!)。設定は EKS の管理画面または「aws eks create-fargate-profile」コマンドから設定可能です。

f:id:masaya_aoyama:20191204135043p:plain
Fargate profile

ちなみに少し細かい話です。最初は MutatingWebhook 等で scheduling Policy(nodeAffinity、taints 等)を書き換えてるのかと思っていましたが、そもそも default scheduler を利用していないようです。Pod 登録時に該当するものは spec.schedulerName だけ Mutating して後は独自スケジューラにまかせているんだと思います。

$ kubectl get pods sample-2pod -o jsonpath="{.spec.schedulerName}"
fargate-scheduler

EKS on Fargate の起動時間

EKS on Fargate での Pod の起動時間は大体 45-50 sec 程度でした(数回のみの施行)。ノードを増やしていくことと比較すると、かなり早くコンテナを起動できるといえます。この点は virtual-kubelet の利点の一つである、クラスタ外の warmpool で起動させる部分と同じような形です。

initContainer と emptyDir の使用

ちょっと発展的に initContainer や emptyDir を使用してみましたが、こちらも問題なく動作するようです。基本的に後述する注意点以外は普通の Kubernetes だなぁという使用感なのかと思います。

cat << _EOF_ | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: sample-initcontainer
spec:
  initContainers:
    - name: output-1
      image: centos:6
      command: ['sh', '-c', 'sleep 20; echo 1st > /usr/share/nginx/html/index.html']
      volumeMounts:
      - name: html-volume
        mountPath: /usr/share/nginx/html/
    - name: output-2
      image: centos:6
      command: ['sh', '-c', 'sleep 10; echo 2nd >> /usr/share/nginx/html/index.html']
      volumeMounts:
      - name: html-volume
        mountPath: /usr/share/nginx/html/
  containers:
    - name: nginx-container
      image: nginx:1.12
      volumeMounts:
      - name: html-volume
        mountPath: /usr/share/nginx/html/
  volumes:
  - name: html-volume
    emptyDir: {}
_EOF_

kubectl exec による任意のコマンド実行と kubectl port-forward

Fargate ではコンテナ内のデバッグがしづらいみたいな話をよく耳にします。kubectl exec も出来ないのだろうかと不安になりましたが、kubectl exec は通常通りできるようです。これはもはやほぼ Kubernetes ですね。

$ kubectl exec -it sample-initcontainer -- cat /usr/share/nginx/html/index.html
1st
2nd

同様に kubectl port-forward や logs なんかも動きます。kubelet が Fargate 側にいそうな霊圧を感じますね。

$ kubectl port-forward sample-2pod 30080:80


$ kubectl logs sample-2pod  -c nginx-container

現状の注意点

現状では、ここ にかかれている通り、下記の制約があるようです。まだファーストリリースなので、下記の要件で合わない方は AWS の早い機能追加に期待というところですね。

  • ポッドごとに最大 4 vCPU 30 Gb Memory の制約
  • 永続ボリュームまたはファイルシステムを必要とするステートフルワークロードは非サポート
  • DaemonSet、Privileged Pod、HostNetwork、HostPort などは利用不可
  • 使用可能なロードバランサーは、Application Load Balancer のみ

ALB Ingress

EKS on Fargate で払い出される Pod の IP Address 帯は Subnet のものになっているため、従来どおり ALB から疎通可能なのでしょう(Container-native Load Balancing となる type: ip 前提)。一方で "type: LoadBalancer" Service も仕組み的には kube-proxy(の iptables 等) で DNAT されているだけなので、遠くない将来にいずれ実現されるんじゃないかなと思います。

または、通常 Node が一台以上あれば、 LB からそのノードに転送された後、kube-proxy が Pod 宛に DNAT して Fargate 側に転送されて上手くいくのでは…?という気もしているので、実験してみましょう(偏るという話は置いておいて)。

更に NodeGroup を追加してハイブリット構成にしてみる(type: LB も試してみる)

というわけで、まず NodeGroup を追加します。

$ eksctl  create nodegroup --cluster amsy810-cluster

するとノードの一覧はこんな感じになります。

$ kubectl delete pod --all

$ kubectl get node
NAME                                                        STATUS   ROLES    AGE     VERSION
fargate-ip-192-168-129-12.ap-northeast-1.compute.internal   Ready    <none>   3h35m   v1.14.8-eks
fargate-ip-192-168-166-59.ap-northeast-1.compute.internal   Ready    <none>   3h33m   v1.14.8-eks
ip-192-168-6-3.ap-northeast-1.compute.internal              Ready    <none>   5m29s   v1.14.7-eks-1861c5
ip-192-168-72-240.ap-northeast-1.compute.internal           Ready    <none>   5m29s   v1.14.7-eks-1861c5

試しに "type: LoadBalancer" Service と Deployment をデプロイしてみましょう。

$ kubectl apply -f https://raw.githubusercontent.com/MasayaAoyama/kubernetes-perfect-guide/master/samples/chapter06/sample-deployment.yaml
$ kubectl apply -f https://raw.githubusercontent.com/MasayaAoyama/kubernetes-perfect-guide/master/samples/chapter06/sample-lb.yaml

$ kubectl get pods -o wide
NAME                                READY   STATUS    IP               NODE                        
sample-deployment-6cd85bd5f-4hwtt   1/1     Running   192.168.100.80   fargate-ip-192-168-100-80.ap
sample-deployment-6cd85bd5f-pkn6b   1/1     Running   192.168.153.79   fargate-ip-192-168-153-79.ap
sample-deployment-6cd85bd5f-xktr6   1/1     Running   192.168.130.33   fargate-ip-192-168-130-33.ap

$ kubectl describe svc sample-lb | grep Endpoints
Endpoints:                192.168.100.80:80,192.168.130.33:80,192.168.153.79:80

結論として、やはり読みどおり "type: LoadBalancer" も Worker Node があれば動くには動くようです。内部の実装上は、LB から Worker Node に転送された後、kube-proxy が DNAT で 各 Pod 宛に通信を転送するだけなので、Fargate Node まで VPC 経由で疎通する仕組み上、疎通しているのだと思います。Fargate がどこにあるか次第ですが、Hop数自体は Kubernetes の Node またぎと同等なので、そこまでレイテンシは乗らないんじゃないかなと思っています。この仕組だと、Fargate + 通常 WorkerNode 両方に振り分けることもできそうですね。この方法がサポート対象な方法なのかは分かりません。

$ kubectl get svc sample-lb
NAME        TYPE           CLUSTER-IP      EXTERNAL-IP                                                                    PORT(S)          AGE
sample-lb   LoadBalancer   10.100.69.184   xxx-yyy.ap-northeast-1.elb.amazonaws.com   8080:30082/TCP   5m15s

$ curl -I http://xxx-yyy.ap-northeast-1.elb.amazonaws.com:8080
HTTP/1.1 200 OK
Server: nginx/1.12.2

なお、同様に ClusterIP も Pod 宛に転送されるだけなので、通常通り動きます。上手く出来ていますね。

まとめ

EKS on Fargate では、virtual-kubelet とは異なる形で実装されました。virtual-kubelet を実装しようとするとベンダー毎の違いがかなり大きいはずなので、なかなか皆で足並みを揃えて進めるのは難しかったのではないかなと思います。また、virtual-kubelet の kubelet バージョン(Kubernetes API バージョン)の追従という心配もなくなり、AWS におまかせできるという大きなメリットもあります。

EKS on Fargate を少し触った所感では、Warmpool された Kubernetes Node が後ろに待機していてくれるイメージに近いので、より PaaS っぽく Kubernetes を使いやすくなるんじゃないかと思います(周辺エコシステムや CI/CD 等の整備は必要ですが)。

また、EKS は拡張性が高い反面、手放しで利用するには少しハードルがありましたが、最近は様々な機能開発で簡単に使う手段も増えてきて導入ハードルが下がってきたなと印象です。AWS Container Roadmap(https://github.com/aws/containers-roadmap)にも Managed 系の Issue がたくさん上がっているので、拡張性を重視した使い方はもちろんですが、利便性の高い使い方も今後も増えていくのではないでしょうか。

(いろんな Kubernetesアーキテクチャが出てきて楽しいぞ)

Fargate profile なくても動くのでは…?と言われたので試しました(12/4 16:50 追記)

ちゃんと Scheduler の方で Profile も見ているようですね。なんか悪いこと(抜け道探し)してる気分になってきました。

$ kubectl create ns newone

$ cat << _EOF_ | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: sample-pod-manual-fargate
  namespace: newone
spec:
  schedulerName: fargate-scheduler
  containers:
    - name: nginx-container
      image: nginx:1.12
_EOF_

$ kubectl describe pods sample-pod-manual-fargate -n newone
Events:
  Type     Reason            Age        From               Message
  ----     ------            ----       ----               -------
  Warning  FailedScheduling  <unknown>  fargate-scheduler  Misconfigured Fargate Profile: pod does not have profile label eks.amazonaws.com/fargate-profile

$ cat << _EOF_ | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: sample-pod-manual-fargate
  namespace: newone
  labels:
    eks.amazonaws.com/fargate-profile: fp-default
spec:
  schedulerName: fargate-scheduler
  containers:
    - name: nginx-container
      image: nginx:1.12
_EOF_

$ kubectl describe pods sample-pod-manual-fargate -n newone
Events:
  Type     Reason            Age        From               Message
  ----     ------            ----       ----               -------
  Warning  FailedScheduling  <unknown>  fargate-scheduler  Misconfigured Fargate Profile: pod's namespace does not match the ones listed in fargate profile fp-default

何が嬉しいのか考えてみた(追記:随時更新します)

  • ノードの種別管理とかは不要になり、ピッタリなサイズでサービスを提供できる(4core? 8core? とか)
  • ノイジーネイバーの影響も受けにくい(Fargate 側では集約しているので、そこの影響はあるかも)
  • Pod単位でノードがいるので、通常のノードでぴったりにしようとした際に発生するオートスケーラのスケールインの影響みたいなものを受けない

Hierarchical Namespace で Namespace を階層構造に管理してオブジェクトを伝搬させる

(12/4 11:00 requiredChild について追記)

こんにちは。CyberAgent の青山(@amsy810)です。

この記事は Advent Calendar 1日目の記事です。 毎回なぜ一日目にしてしまったんだろうかと後悔します。

今回は、KubeCon + CloudNativeCon NA 2019 で併設実施された Kubernetes Contributors Summit で聞いて気になっていた、Kubernetes Multi-tenancy WG で開発が進められている、Hierarchical Namespace のお話をします。

一言で言うならば、これは 「Namespace に階層構造の概念を取り入れて、ポリシーやSecretなどのリソースを伝搬させる仕組み」 です。 マイクロサービスチームや組織構造の都合上、同じオブジェクト(ConfigMap, Namespace, ResourceQuota, LimitRange, NetworkPolicy)を登録しておきたいケースは多々あると思います。 また、RBAC の設定を伝搬させることで、下位のチームに権限移譲の設定を上手く扱うことも可能です。

事前準備

事前準備として kubectl と kustomize をインストールしておいてください。 また、今回は下記のバージョンで実施しました。

  • Kubernetes: v1.16.0
  • Kustomize: v3.4.0
  • Hierarchical Namespace Controller: 7db19627f5d21a7a368beaa9d7b9be177d5bb26a (Git commit hash)

Hierarchical Namespace Controller (HNC)のインストール

Hierarchical Namespace Controller は Multi-tenancy WG のリポジトリの中にディレクトリが切られ、開発が進められています。 なお、Hierarchical Namespace Controller は現状まだ "very early software" と言われている通り、PoC 相当の Controller です。

https://twitter.com/aludwin/status/1196520861554991104?s=20

ソースコードを落としてきて、make deploy することでクラスタにインストールする事が可能です。今回は、 masayaaoyama/hnc-controller:7db1962 にビルドしたイメージを公開してあります。

cd
git clone https://github.com/kubernetes-sigs/multi-tenancy.git
# curl -o https://raw.githubusercontent.com/kubernetes-sigs/multi-tenancy/master/incubator/hnc/hack/hnc.yaml
cd multi-tenancy/incubator/hnc

IMG=masayaaoyama/hnc-controller:$(git rev-parse --short HEAD) GOPATH=~/go make deploy

階層化された Namespace の作成

Namespace を作成します。今回は特定のプロジェクトの Namespace (sample-project)を起点として、3階層で構成されている構造を作り出します。

root の Namespace を作成したら、それぞれに親子関係を作るため、 kubectl hnc コマンドを実行します。なお、このコマンドは kubectl plugins が make deploy した際にインストールされています。 実質的には後で説明する HierarchyConfiguration リソースの操作をしているだけです。

kubectl hnc set sample-project --requiredChild team-a
kubectl hnc set sample-project --requiredChild team-b

kubectl hnc set team-a --requiredChild team-a-service1
kubectl hnc set team-a --requiredChild team-a-service2

kubectl hnc set team-b --requiredChild team-b-service1
kubectl hnc set team-b --requiredChild team-b-service2
kubectl hnc set team-b --requiredChild team-b-service3

作成したあとに、Namespace の階層構造を確認してみると、下記のとおりです。 正しく3層の構造が出来ていることが確認できます。

$ kubectl hnc tree --all-namespaces
cert-manager
default
docker
hnc-system
kube-node-lease
kube-public
kube-system
sample-project
├── team-a
│   ├── team-a-service1
│   └── team-a-service2
└── team-b
    ├── team-b-service1
    ├── team-b-service2
    └── team-b-service3

--requiredChild を指定して作成した Namespace が消された場合、reconcile されて再度 Namespace が作成されます。また、「kubectl hnc set team-a --parent sample-project」というコマンドで、この Namespace 作成の reconcile を無効化した状態で階層構造を作ることも可能です。

$ kubectl delete ns team-a
namespace "team-a" deleted

$ kubectl hnc tree sample-project
sample-project
├── team-a
│   ├── team-a-service1
│   └── team-a-service2
└── team-b
    ├── team-b-service1
    ├── team-b-service2
    └── team-b-service3

ポリシーの伝搬の確認

今回は、ConfigMap リソースを作成し、正しくオブジェクトが伝搬していることを確認します。 sample-project 直下に作成した web-config オブジェクトは 8 つの Namespace(伝搬したのは 7 つ)に作られているのに対し、team-a 直下に作成した web-config2 オブジェクトは 3 つの Namespace(伝搬したのは 2 つ)に作られています。

$ kubectl -n sample-project create configmap web-config --from-literal=connection.max=100
$ kubectl -n team-a create configmap web-config2 --from-literal=connection.max=200

# 並び順は見やすいように変えています
$ kubectl get configmap -A | grep "web-config "
sample-project    web-config                           1      57s
team-a            web-config                           1      57s
team-a-service1   web-config                           1      57s
team-a-service2   web-config                           1      57s
team-b            web-config                           1      57s
team-b-service1   web-config                           1      57s
team-b-service2   web-config                           1      57s
team-b-service3   web-config                           1      56s
team-a            web-config2                          1      42s
team-a-service1   web-config2                          1      42s
team-a-service2   web-config2                          1      42s

ポリシー系のオブジェクトのみ親から子へ Sync される様になっています。

伝搬対象のオブジェクトは、下記の 7 種類です。 https://github.com/kubernetes-sigs/multi-tenancy/blob/master/incubator/hnc/pkg/config/gvk.go#L14

var GVKs = []schema.GroupVersionKind{
    {Group: "", Version: "v1", Kind: "Secret"},
    {Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "Role"},
    {Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "RoleBinding"},
    {Group: "networking.k8s.io", Version: "v1", Kind: "NetworkPolicy"},
    {Group: "", Version: "v1", Kind: "ResourceQuota"},
    {Group: "", Version: "v1", Kind: "LimitRange"},
    {Group: "", Version: "v1", Kind: "ConfigMap"},
}

なお、"type: kubernetes.io/service-account-token" の Secret は除外されています。 https://github.com/kubernetes-sigs/multi-tenancy/blob/7db19627f5d21a7a368beaa9d7b9be177d5bb26a/incubator/hnc/pkg/controllers/object_controller.go#L303-L326

個人的には ServiceAccount も sync してほしい気がしますが、何かしらの理由があるのでしょう。

階層構造の仕組み

階層構造は HierarchyConfiguration リソースを利用して実現しています。 このリソースには、 spec.parentspec.requiredChildren のフィールドが用意されており、kubectl hnc set team-a --requiredChild team-a-service1 および kubectl hnc set team-a --requiredChild team-a-service2 コマンドを実行すると、 team-a Namespace に hierarchy という名前の HierarchyConfiguration リソースが作成されます。 また、status にはその Namespace の子 Namespace が追加されるようになっています。

$ kubectl get hierarchyconfigurations -n team-a hierarchy -o yaml
apiVersion: hnc.x-k8s.io/v1alpha1
kind: HierarchyConfiguration
metadata:
  creationTimestamp: "2019-11-30T16:27:49Z"
  generation: 5
  name: hierarchy
  namespace: team-a
  resourceVersion: "20753"
  selfLink: /apis/hnc.x-k8s.io/v1alpha1/namespaces/team-a/hierarchyconfigurations/hierarchy
  uid: 29a4e049-85fa-4e95-adb3-3826a6fe1255
spec:
  parent: sample-project
  requiredChildren:
  - team-a-service1
  - team-a-service2
status:
  children:
  - team-a-service1
  - team-a-service2

なお、子 Namespace にコピーされたオブジェクトには、コピー元の親 Namespace 名がラベルとして付与されています( hnc.x-k8s.io/inheritedFrom )。

$ kubectl -n team-a-service1 get configmap web-config2 -o yaml
apiVersion: v1
kind: ConfigMap
metadata:
  annotations:
  labels:
    hnc.x-k8s.io/inheritedFrom: team-a
data:
  connection.max: "100"

なお、sample-project から伝搬されている web-config team-a のデータを消した場合は、team-a 配下の Namespace の web-config オブジェクトがすべて削除されます。

$ kubectl delete configmap web-config -n team-a
configmap "web-config" deleted

$ kubectl get configmap -A | grep "web-config "
sample-project    web-config                           1      48m
team-b-service1   web-config                           1      48m
team-b-service2   web-config                           1      48m
team-b-service3   web-config                           1      48m
team-b            web-config                           1      48m

プロポーザルには「HNCによって伝播されるオブジェクトは、変更または削除できない」と書いてありましたが、現状は削除可能になっています。 現状では、子の Namespace に登録されているリソースを削除できてしまうので削除すると、復元されないので注意してください。将来的には ValidationWebhook などが入るのだと思います。

なお、親 Namespace のオブジェクトを削除した場合は、子 Namespace のオブジェクトも再帰的に削除していきます。

もう少し DeepDive

Hierarchical Namespace Controller (HNC)は大きく分けて下記の 2 つの Controller( Hierarchy Controller , Object Controller )から構成されています。 kube-controller-manager と同じような構成ですね。

  • Hierarchy Controller
    • HierarchyConfiguration から Namespace の階層構造を status に書き込む
    • 同様に Labels の付与なども行っている
  • Object Controller
    • 親 Namespace のオブジェクトを子 Namespace にコピーするコントローラ

Hierarchy Controller

reconcile 関数はこの辺りから。 https://github.com/kubernetes-sigs/multi-tenancy/blob/master/incubator/hnc/pkg/controllers/hierarchy_controller.go#L104

実際に Label を付与したり、status を更新する処理は下記のあたりから呼び出されています。(書き込みは別途) https://github.com/kubernetes-sigs/multi-tenancy/blob/master/incubator/hnc/pkg/controllers/hierarchy_controller.go#L149

Object Controller

Recncile 関数はこの辺りから。 https://github.com/kubernetes-sigs/multi-tenancy/blob/7db19627f5d21a7a368beaa9d7b9be177d5bb26a/incubator/hnc/pkg/controllers/object_controller.go#L51

オブジェクトをコピーしてるのはこの辺り。(inst オブジェクトを dests[] namespaces にコピー) https://github.com/kubernetes-sigs/multi-tenancy/blob/7db19627f5d21a7a368beaa9d7b9be177d5bb26a/incubator/hnc/pkg/controllers/object_controller.go#L217-L252

GCP の Anthos でも階層構造の伝搬の仕組みがあるらしい?

また、Anthos Config Management にも同様に階層構造に伝搬するように、Gitリポジトリ内で階層構造を作って同様のことをしているようです。(Anthos 触ってみたい)

Google’s Anthos Config Management (ACM) supports a similar model to this proposal by modelling a namespace hierarchy in the filesystem of a monolithic Git repo, and copies objects from parent namespaces to child namespaces in a K8s apiserver. By contrast, the Hierarchical Namespace Controller (HNC) operates entirely within a K8s apiserver and makes use of CRDs in the namespaces to express hierarchy.ACM may allow the use of HNC in the future if it becomes sufficiently mature.

より詳しく知りたい方は

これ以上の詳細は下記の Design Docs に載っています。興味のある方はぜひ見てみてください。 (12/1 が過ぎてしまうので、この辺りで…) https://docs.google.com/document/d/10MZfFfbQMm33CBboMq2bfrEtXkJQQT4-UH4DDXZRrKY/edit#

Kubernetes は辛いのか?

こんにちは。 @amsy810 です。

下記のブログが出てから『Kubernetes は運用しきれない』と耳にすることが多くなってきたので、雰囲気で曲解されて Kubernetes is difficult とならないよう、マネージドでシンプルに使うだけなら難しくないよと伝えるために書きました。

Kubernetes がいいよと伝えるためではありません。

yakst.com

TL;DR

上記のブログについては概ね同意見です。

辛いのは自前で Kubernetes クラスタの管理自体を行う場合です。

GKE などのマネージド Kubernetes サービスを利用するケースでは、Kubernetes の管理は殆ど必要がなくなります。

例えば GKE の場合には、自動クラスタアップグレード・自動クラスタスケーリングなどが用意されています。他にも自動ノードプロビジョニング機能(CPU・メモリ・GPU の割り当て要求に従って、良い感じのノードを自動でクラスタに追加・削除する)を利用すると ノードの管理ですら必要なくなり 、抽象度の高い(利便性の高い)基盤として利用することが可能です。

また、アプリケーション開発者として Kubernetes を利用するだけなら実際は非常にシンプルに利用することが可能です。

マネージド Kubernetes を使えるなら、開発者として利用するだけなのでそこまで難しくありません。

前提条件

コンテナを利用するべきかや、Nomad・ECS・Kubernetes といったオーケストレータ(あるいは Fargate など)を利用するべきか否かというところについては述べません。この辺りが必要な方向けに書いています。なんだかんだ VM のワークロードが必要というケースも多々あるでしょう。また、PaaS も良いと思います。あくまでコンテナオーケストレータだけの話をしています。

この前提は、再現性・管理性・復元力・可観測性・自動化・疎結合性といった特性を組織が享受できる状態か(享受したい状態か)どうかによって異なります。

なお、すでにクラウドを利用している組織で新規プロジェクトの技術選定なのだとしたら、ステートフルなものはマネージドサービスを利用しつつステートレスなアプリケーションに利用していくことは障壁も少なくおすすめです。

どのオーケストレータを選択すべきか

国内では選択したオーケストレーターを今後も長く使っていくケースが非常に多くなるかと思います。

海外では大規模なクラウド間の移行やプラットフォームの移行をやってのけるケースも多々あるため、その気概があるなら正直何から始めてもいいと思います。

日本にはなかなか真似できない部分ですね。

なるべく塩漬けにしたい場合には、「拡張性がある」「コミュニティが活発」「エコシステムが豊富」といったあたりの長く利用できるかどうか、やりたいことが増えた際に魔改造しないで済むかどうかという点を考慮すべきだと思います。

OSS だと下火になった際の影響が大きいため、選択肢がいくつかある際は個人的にはこういったところを重視しています。

ECS などのマネージドサービスに関しては、今後もあまり心配はないと思います。

ただ忘れないで欲しいのが、Kubernetes であっても アプリケーション開発者から見ると「シンプルにも利用可能」 であるという点です(あとで紹介します)。

でも流石に PaaS の利便性には勝てないとは思います。あくまでオーケストレータを純粋に比較した場合です。

オンプレで Kubernetes を選択するべきか?

オンプレ上で Kubernetes をやろうとするのであれば、少なくとも少人数で選択することはオススメしません。

やるとしてもサポート付きの商用 Kubernetes ディストリビューションを利用するか、Kubernetes を専門に面倒をみるチームが必要でしょう。

trivago さんのケースでも 4 人チームでオンプレ上に構築して利用するというものだったので、Kubernetes はかなりオーバーだと思います。

利用者としての Kubernetes

Kubernetes を利用するということは、 GCPAWS・Azure といったパブリッククラウドを利用するのと近しいもの だと考えています。

そのため、当然パブリッククラウド自体を自前で構築して運用することを少人数で成し遂げることは困難です(例:OpenStack)。

一方で利用者から見るとパブリッククラウドは非常に便利かつ高機能ですね。

そのためアプリケーション開発者がどの環境でやるのが一番楽かという面では、マネージド Kubernetes サービスを利用するのが一番だと思います。

管理された Kubernetes 上でコンテナを起動させる場合、コンテナを起動させてくれる抽象度の高いマネージドサービスと余り差はない と思います。

例えば、シンプルに利用するだけであれば Kubernetes でも 下記のように Deployment リソース(コンテナを起動する)と Service リソース(ロードバランサ)の2つだけを使っても達成できます。

パブリッククラウドでも全ての機能を把握して選択することはないのと同様に、そのほかのリソース(機能)に関してはやりたいことが出来た際にやり方を調べれば良いと思います。その時にできることがたくさんあった方が良いとは思います。

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: sample-app
  template:
    metadata:
      labels:
        app: sample-app
    spec:
      containers:
        - name: nginx-container
          image: nginx:1.13
          ports:
            - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: sample-lb
spec:
  type: LoadBalancer
  ports:
    - name: "http-port"
      protocol: "TCP"
      port: 80
  selector:
    app: sample-app

なおCI/CD では、上記の様なマニフェストのボイラープレートを作って使い回すことが可能です。

Kubernetes を採択する規模は?

規模が大きい方が得られる恩恵は大きくなりますが、小さい規模でも恩恵は大きいと思います。

ただし、小さい規模の場合は人員も少ないため、そこを担当できる(したい)エンジニアがいるかという点につきますね。

Kubernetes は抽象度の高い環境を提供できるか?

Kubernetes も HorizontalPodAutoscaler・ClusterAutoscaler や Knative などを利用することにより、PaaS のような抽象度の高い(利便性の高い)環境を提供することは可能です。

いわゆる Fargate に近しい何かも設定次第では実現できます。

Kubernetes へ移行するべきか?

Nomad・ECS・Kubernetes など様々なコンテナオーケストレータが存在します。

ECS・Nomad のいずれも足りない機能を補う様にアップデートを重ねてきており、徐々に Kubernetes にしか無かった機能が実装されてきています。

そのため、すでに ECS や Nomad で稼働している場合には Kubernetes に移行するメリットは大きくないと思います。

既存のレガシーな環境から移行するべきかという点についても同等なのですが、もし今後もシステムが改修され続けていく状況で移行コストをかけられる様であれば移行も選択肢の一つです。

主軸としては移行しなければ開発効率が上がらないかどうかが判断材料ですね。

(余談)オンプレ上で Kubernetes をやると良いところ

基本的にオンプレ上で Database as a Service や Queue as a Service などを実現するのは運用コストや仕組みづくりが大変です。

Kubernetes の周辺エコシステムを利用することでマネージドサービスを Kubernetes 上で展開することも可能になってきています(mysql-operator、Vitess、NATS など)。

そのため、オンプレ上での Kubernetes にはプライベートクラウドの充実という可能性も感じています。

まとめ

技術選定は組織ごとに好みがあると思うので、あくまでも個人の見解です。

何を選択するにせよ、様々な情報を元に適切に判断すれば良いと思います。

雰囲気で曲解されて Kubernetes is difficult とならないよう、あくまで Kubernetes の苦手意識がなくなるためだけに、一意見として書いてみました。

まとめのまとめ(心の声)

Kubernetes、いいよ。)

追記 (4/3 15:00)

何度も言いますが、どの環境を使っても良いと思います。コードを書いたら良い感じにしてくれるのが目的です。PaaS でも ECS でも Kubernetes でもアプリケーション開発者からしたらなんでも良いでしょう。

Kubernetes も頑張って CI/CD 整備やマニフェストを書く人がいることによって PaaS の様にして利用しています。

Kubernetes は本当に素の Platform なので、ちゃんとした CI/CD がないとアプリケーション開発に集中できる環境を提供するという目的は達成できません。そのため、あくまでもインフラや SRE にある程度知見のあるメンバーは必要でしょう。

環境を整備すると PaaS も KubernetesNomad も ECS もいちアプリケーション開発者からすると透過的なのが良い形だと思います。

正直単純にオーケストレータを利用する範囲であれば、その学習コストよりもその周辺の考えなきゃいけないこの方が辛いです。

また、どのオーケストレータを選択しても従来の VM で環境を作った時にはいずれも近しい程度の学習コストはかかると思っていた方が良いです。

さくらインターネット研究所 に 客員研究員 としてジョインしました。

こんにちは。amsy810です。

春といえば新しいことを始める時期ですね。

4/1 より さくらインターネット研究所 さんに 客員研究員 としてジョインしました。

新しい経験もしてみたいと思い下記のようなツイートをしたところ、ありがたいことにお誘いを頂いたため新しい挑戦を始めることになりました。

まつもとりーさんからDMで連絡があったのですが、予想外なお話だったので最初は驚きました。 普段からまつもとりーさんのブログなどはウォッチしていたので、お話をいただいた時点で面白そうだなというのは明白でした。

ご存知の方も多いかと思いますが、さくらインターネット研究所さんでは「超個体型データセンター」についての研究を行なっております。 私もこの辺りのお話に携わらせていただく予定です。

f:id:masaya_aoyama:20190322022125p:plain
さくらインターネット研究所 中長期ビジョン

詳しくは所長の鷲北さんがまとめています。他にもまつもとりーさんや 研究所のみなさま がまとめた記事もたくさんあるので、探してみてください。(エキスパートのみなさまとお仕事をするのは緊張します。)

research.sakura.ad.jp

数年後に期待されるであろう技術を考えることや、大規模なクラウド事業者の向かう方向性に触れられるのは楽しみです。 最近では "Cloud Native" が広まってきましたが、次のプラットフォームになるようなコアコンセプトなどを考えていけたらと思っております。

また、さくらインターネットさんも CNCF(Cloud Native Computing Foundation)にシルバーメンバーとして参加しています。

再びですが、 CyberAgent を退職するわけではありません。 しっかりとメリハリをつけて本業も頑張りつつ、新しい経験を通じて CA でも引き続き頑張ります。

クリエーションラインに 技術アドバイザー としてジョインしました。

こんにちは。amsy810です。

春といえば新しいことを始める時期ですね。

3/18 より クリエーションラインさんに 技術アドバイザー としてジョインしました。 f:id:masaya_aoyama:20190325231512p:plain

今とは異なる要件で新しい経験もしてみたいと思い下記のようなツイートをしたところ、ありがたいことにお誘いを頂いたため新しい挑戦を始めることになりました。

主に Kubernetes やコンテナ関連のお仕事をさせていただく予定です。

私自身、技術が好きでこの業界に入ってきたほどなので、色々なことが楽しみです。

クリエーションラインさんは CNCF が提供する 日本で最初の KCSP (Kubernetes Certified Service Providers) で、CNCF 公式のKubernetesレーニングの提供を含む、Kubernetesやコンテナ関連の事業を展開しています。

具体的な内容についてお話しできないことも多々ありますが、普段中々経験できない業界だったりするので、とても良い経験ができそうだなとワクワクしています。

なお、CyberAgent を退職するわけではありません。

しっかりとメリハリをつけて本業も頑張りつつ、新しい考え方を学んで CA でも引き続き頑張ります。

なお、新しいことを始める話には続きがあり…数日後にまたご報告させていただきます。

【Kubernetes】GKEのBackendConfigとNetwork Endpoint Groups(NEGs)

この記事はAdvent Calendar 1日目の記事になります。

Japan Container Days v18.12の運営やshowKsやセッション資料の作成に追われ、遅れてしまい申し訳ありません。)

こんにちは。 @amsy810 です。

GKE では、最近ネットワークに関する便利な機能追加が多く行われており、この記事ではそれらについて紹介したいと思います。 主に下記の2つについて紹介します。

  • BackendConfig Resource
  • Network Endpoint Groups (NEGs)

BackendConfig Resource

BackendConfig Resource は GKE でのみ利用可能な CRD(Custom Resource Definition)で、HTTP/HTTPS Load Balancing(Ingress Resource)をする際に、下記の機能を付随して利用することが可能になります。

  • Cloud Content Delivery Network (Cloud CDN)
  • Cloud Armor
  • Cloud Identity-Aware Proxy (Cloud IAP)
  • Timeout, Connection draining timeout, Session affinity

もう少しいうと、GCE用のIngress ControllerがこのBackendConfigリソースをみて、連携を行なっているようです。Ingress ResourceのSpecは一般化されたリソースのためこのような記述をすることができず、もしできるとしてもAnnotationsで設定することになってしまうと思います。 そのため、BackendConfigとして実装する道を辿ったのだと思います。 現時点ではBackendConfigはGKE用のリソースとなっています。 実際の処理部分は下記のあたりです。

ingress-gce/pkg/backends/features at v1.4.0 · kubernetes/ingress-gce · GitHub

なお、BackendConfigのリソースは、下記のリンク先にある構造体を持っています。

ingress-gce/types.go at v1.4.0 · kubernetes/ingress-gce · GitHub

全部埋めた状態だとこんな感じですね。

apiVersion: cloud.google.com/v1beta1
kind: BackendConfig
metadata:
  name: sample-backend-config
  namespace: default
spec:
  iap:
    enabled: true
    oauthclientCredentials:
      secretName: my_secret
      # Secret で clientID と clientSecret を指定する場合は不要
      # clientID: my_client_id
      # clientSecret: my_client_secret
  cdn:
    enabled: true
    cachePolicy:
      includeHost: true
      includeProtocol: true
      includeQueryString: false
      QueryStringBlacklist:
        - ""
      QueryStringWhitelist:
        - ""
  securityPolicy:
    name: my_policy
  timeoutSec: 40
  connectionDraining:
    drainingTimeoutSec: 60
  sessionAffinity:
    affinityType: "GENERATED_COOKIE"
    affinityCookieTtlSec: 50

ちなみにCloud IAPとCloud CDNを両方とも1つのBackendConfigに設定することはできません。 また、作成したBackendConfigは、ServiceのAnnotationsに beta.cloud.google.com/backend-config を記述することで利用します。 Ingress ResourceのAnnotationsに設定するイメージでしたが、Serviceの方に設定するんですね。 おそらく、複数のIngress ObjectからServiceのNodePortを指定することができるため下流の方で設定をかけたかったか、Ingress ControllerはService Objectの情報を元に動作する(NEGsはServiceのNodePortに送られるのではなく、Serviceに紐付くPodのIPをDiscoveryしている)思想で作られたのかなと思っています。

apiVersion: v1
kind: Service
metadata:
  annotations:
    beta.cloud.google.com/backend-config:
      '{"ports": {"http":"config-http", "http2" :"config-http2"}, "default": "config-default"}'
  name: my-service
spec:
  type: NodePort
  ports:
  - name: http
    protocol: TCP
    port: 80
    targetPort: 8080
  - name: http2
    protocol: TCP
    port: 443
    targetPort: 8080
  - name: http3
    protocol: TCP
    port: 49152
    targetPort: 49152

Cloud Armor

Cloud Armorは、DDOS対策・IP Addressの Allow/Deny・Geo-based Access Controlをはじめとした様々なセキュリティ機能を利用することができます。

現状では事前に gcloud beta compute security-policies rules create コマンドでポリシーを作成しておき、そのポリシーを使うことを明示的にBackendConfigリソースに指定してあげる必要があります。

例えば、事前にポリシーとルールを作成し、

$ gcloud beta compute security-policies create sample-policy

$ gcloud beta compute security-policies rules create 1000 \
    --security-policy sample-policy \
    --description "Deny traffic from 192.0.2.0/24." \
    --src-ip-ranges "192.0.2.0/24" \
    --action "deny-404"

下記のようなBackendConfigを作るような形になっています。

apiVersion: cloud.google.com/v1beta1
kind: BackendConfig
metadata:
  name: sample-backend-config
  namespace: default
spec:
  securityPolicy:
    name: sample-policy

現状ではKubernetesの世界で設定を完結できないようになっていますが、この辺りは下記のようにいずれK8sの世界で管理できるようになってくるのではないでしょうか。

apiVersion: cloud.google.com/v1beta1
kind: BackendConfig
metadata:
  name: sample-backend-config
  namespace: default
spec:
  securityPolicy:
    - srcIPRange: 192.0.2.0/24
       action: deny-404
       priority: 1000

Configuring Cloud Armor  |  Kubernetes Engine  |  Google Cloud

Cloud IAP (Identity-Aware Proxy)

Cloud IAPは、GCP(GKE)上にデプロイされたアプリへのAccess Controlを行うサービスです。 事前にClient IDとClient SecretのデータをSecretに登録しておきます。

apiVersion: v1
kind: Secret
metadata:
  name: sample-db-auth
type: Opaque
data:
  client_id: XXXXXXX
  client_secret: XXXXXXX

Cloud IAPの設定項目としては、Client IDとClient Secret になります。Secret経由で渡している場合には、Secret名を指定すれば問題ありません。

apiVersion: cloud.google.com/v1beta1
kind: BackendConfig
metadata:
  name: sample-backend-config
  namespace: default
spec:
  iap:
    enabled: true
    oauthclientCredentials:
      secretName: my_secret
      # Secret で clientID と clientSecret を指定する場合は不要
      # clientID: my_client_id
      # clientSecret: my_client_secret

Cloud CDN (Content Delivery Network)

Cloud CDNは、GCPCDNです。 設定項目としてはキャッシュポリシーに関わるものがあります。

apiVersion: cloud.google.com/v1beta1
kind: BackendConfig
metadata:
  name: sample-backend-config
  namespace: default
spec:
  cdn:
    enabled: true
    cachePolicy:
      includeHost: true
      includeProtocol: true
      includeQueryString: false
      QueryStringBlacklist:
        - ""
      QueryStringWhitelist:
        - ""

そのほかの設定

そのほかにもタイムアウトの設定や、セッションアフィニティの設定などを行うことも可能です。

apiVersion: cloud.google.com/v1beta1
kind: BackendConfig
metadata:
  name: sample-backend-config
  namespace: default
spec:
  timeoutSec: 40
  connectionDraining:
    drainingTimeoutSec: 60
  sessionAffinity:
    affinityType: "GENERATED_COOKIE"
    affinityCookieTtlSec: 50

注意事項・参考資料

現在はまだ beta リリースなため、SLAが担保されていなかったり、後方互換のない変更が行われる可能性があるため注意してください。

BackendConfig custom resource  |  Kubernetes Engine  |  Google Cloud

Network Endpoint Groups (NEGs)

NEGsは、IPとPortのペアをネットワークエンドポイントとしてLoadBalancerの設定に利用するものです。 NEGsとPod Nativeなネットワーク(CalicoなどでPod Network / Internal Network に対してVM Networkから疎通性がある構成)を利用することで、LoadBalancerから直接Podまでリクエストを受けることができます。

通常はこのように、LoadBalancerからKubernetes Nodeに対してリクエストが送られると、Node上に到達した後に、再度負荷分散(2段階ロードバランシング)が行われます。 NodeからPodへのロードバランシング(kube-proxyの担当範囲)は、usermode・iptables・ipvsと3つのモードがありますが、いずれの場合にも一定の負荷・レイテンシの微増・分散のばらつきが生じます。

f:id:masaya_aoyama:20181206035829p:plain

一方でNEGsを利用することで、直接Pod宛てに負荷分散を行うことができるため、これらの問題を低減することが可能です。

f:id:masaya_aoyama:20181206035824p:plain

AWSの場合には、aws-vpc-cni-k8sとalb-ingress-controllerを使うと同様のことが実現できるそうです。(未検証)

Using container-native load balancing  |  Kubernetes Engine  |  Google Cloud

また、Ingressの実装の違いについては下記を参考にしてみてください。

speakerdeck.com

(あ、こんなところにKubernetes 本...)

www.amazon.co.jp