EKS Kubernetes 構成と構想

やるといった以上、記事は書くわけなんですが、何度この下書きをゴミ箱に突っ込もうとしたかわかりませんKubernetesですよろしくお願いします。

ナニがアレかというと、構成図に全部盛り込めるはずもないくらいには色々要素があるわ、構築手順を書いていこうにも予備知識が必要な箇所がそこかしこに出現するわで、わかりやすく書けそうもないのです。なので、まずは丁寧にやりたいことから整理していくことにします。



全体図

ECSシリーズ

まず、前回のECSシリーズの復習としてこちら。
これは複雑そうに見えて、わりとオススメできる簡潔さでした。




ECSをEKSに入れ替える

ECSで説明した、ECS以外の部分は大体そのまま流用するので、薄くしたところはあまり触れず、Kubernetesクラスタの内側に重点を置いていきます。必要があれば、ECSの方の記事に都度リンク貼ったりして重複は回避していく感じで。




EKS Kubernetes 構成図

同じくWEBサービス用途として扱う前提の構成ですが……
イラつきながらも細かいのを削って主要要素で収めた図がコチラ。



   _____
  /-、 -、  \
 /  |  ・|・  | 、   \
/ / `-●-′ \   ヽ  ..。 o O ◯(何を伝えたいのかサッパリわからない)
|/ ── |  ──   ヽ  |
|. ── |  ──    |  |
| ── |  ──     |  l
ヽ (__|____  / /
 \           / /
  l━━(t)━━━━┥

ってなると思うので、見ないでいいです。全体図なんて飾りですよ。

我慢してよく見ると、実は全然たいしたことしていないんですが、これだけ構築してみるにも途中で色々つまづきポイントがあったり、選択肢があったりで、まったくスンナリいかないのが初見の感想そのままなのであります。マスターノードの管理がない分、楽なハズなんですが……どこがフルマネージドやねんって思うくらいには、ほとんど自分で苦労していく感覚になります:-)


構想

Kubernetes で何をしたいかというと、私の場合はECSの時と同じく、ごく一般的なWEBサービスを提供することが一発目の目的となります。他にも違う使い方は考えているのですが、まずはコレくらいできないと話にならないので。

で、ここではどんなことを実現しようとして、どんなことを考えたかを整理してみます。

目的

いつもどおりな内容ですが、大雑把にこんな感じ。

  • ECSで使ったECRのイメージをそのまま使う
  • ALB+ACM 経由での負荷分散
  • Node(EC2),Pod リソースの自動拡張/縮退
  • AZ以下の各個障害に対する耐障害性
  • スポットインスタンスや複数インスタンスタイプを利用可能にする
  • デプロイなど運用面を簡素かつ堅実に仕上げる
  • 監視しっかり

  • 課題と選択

    進めていく上で出てきた課題とか選択肢を思い出して書いてみます。

    ALB
    Kubernetes の機能に Service :: LoadBalancer ってあって、ペロッと作ると ELB が作成されてうまい具合にコンテナ(Pod)と繋がるのですが、いまどきELBとかありえないっしょ~キャハハ なので、ALBを使う方法を探すことになります。

    すると、ALB Ingress Controller というありがたそうなシステムがすぐ見つかるのですが、これで作成すると、Kubernetes自身がALBリソースを作成することになり、なんなら他のACMとか近しいリソースまで面倒見れるようにできるっぽかったです。

    が、完全に私見でスマンやけど、Kubernetes単体で見ればそれも良しかもだけど、パブリッククラウドでやる構成としてはダサいというか、既存のTerraformコードの流用じゃなくなったり、命名とか任意じゃなくなったり、リソース管理の面で可能な限りクラウド側に寄せたかったり、色々理由があってナシの方向にしました。

    で結局、Service で NodePort を使うと、これまでの構成をほぼそのままに活かせたので採用に至りました。

    Autoscaling
    これも最初はクラウド側に寄せたくて、AutoscalingGroup + CloudWatch で一瞬やろうとしたんですけど、速攻で意味無しだとわかるので、これはKubernetesの方に任せることにしました。

    理由は簡単で、EC2を直に扱ったり、ECS Fargateのようにノード管理がなかったりする場合は、そのインスタンスやコンテナのみを監視して増減させるだけなのですが、Kubernetesの場合は Node(EC2) と Pod(コンテナ) の2重構造になっているため、CloudWatch では Pod 管理が届かないためです。

    結果として、Node の管理は、Helm というK8sのパッケージ管理ツールにある、cluster-autoscaler に任せます。Node を増減しようとした時に AutoscalingGroup の desired を編集してくれます。増加のタイミングは、Podがリソース不足で増やせない状態になった時であり、平均CPU使用率は全く関係ないところが特徴です。

    Pod の管理は HPA (Horizontal Pod Autoscaler) に任せることに。これは Kubernetes の基本機能として組み込まれているもので、Pod群の平均CPU使用率などの指定リソースを見て、条件を満たしたら Deployment というPod管理グループを編集して台数を増減してくれます。で、そのCPU使用率を見れるようにするために、Helmパッケージである metrics-server を動かす必要がある。といった流れになります。

    耐障害性
    最近のシステムだと、何かが落ちたり機能不全になったら、破棄して可能な限り同じ条件で自動起動する、というのが主流だと思います。この辺はだいたい良くできているので、しっかり確認だけはしておきたいところです。

    Pod は異常が起きたら再起動したり、指定台数を保つようにできています。だいたい上手く動いてくれそうな分、メモリ関連など何かが起きた時にずっと再起動し続けたり、起動すら諦めたような状態に気づきづらかったりするので、Warning以上なログは Kubernetes を直接見て確認するのではなく、どこか外部に吐き出してアラートを上げる仕組みなど考える必要があります。

    次に Node ですが、AutoscalingGroup の EC2 になるので、障害が起きても台数は保ってくれます。スポットを扱う場合は、強制Terminateも考慮する必要があり、その対応は従来と変わりません。

    Pod も Node も急なダウンのときは一部のリクエストにエラーが返るのは仕方ありません。Pod は Deployment によって、Node は ALB によってヘルスチェックされているため、ダウン~ダウン判定完了までは、そのNode/Podへ転送されたリクエストがエラーになるからです。

    そういう部分は許容するのが基本であり、大事なのは各所の障害時に何分何秒程度で復旧するのか把握するのと、ログやアラートによって認知して改善していくことで、その辺はKubernetesだからナニということはありません。

    インスタンスの指定
    すぐ見つかる事例だと LaunchConfiguration を使うものばかりですが、LaunchTemplate も普通に使えるので、複数のインスタンスタイプを指定したりスポットで自動オンデマンド価格、ということは可能です。

    ただ、1Node に複数Pod となるため、CPU性能やメモリ容量の差でどうなるのか考えられる人にしかオススメできない気がします。

    Master的な機能
    スポットはいつもどおり、いつTerminateされてもおかしくない前提で組むのですが、スポットダウンに限らず、AutoscalingのDecreaseに巻き込まれてダウンしない方がよいシステムがいくつかあるため、配置について考える必要があります。

    例えば、既に出た metrics-server, cluster-autoscaler というAutoscalingに関わるHelmのPod。Helm を扱うために必要な tiller-deploy というPod。coredns というEKSが管理しているDNSサーバー。

    Pod は、ダウンしたら他のNodeで起動しますし、Nodeを落とそうとする前に移行しようとしてくれるのですが、Master的機能のPodにとってはそれらはそもそも起きない一定の環境に収めておくべきと考えました。そのため、AutoscalingGroup を scaling / master の役割に分け、いろんな設定で任意の配置をできるようにしたのですが、ややこしいので別途記事で書きます。

    スポットインスタンスの扱い
    スポットTerminate対策を調べると、Pod が落ちるとわかっていれば、事前に安全に落とす方法がありますよ。というのはいくつか見つかるのですが、そもそもスポットやDecreaseで落とされるから、安全に落としたり移動したり、しなくちゃって Pod は最初から安全な配置にすべきでしょって感想と、

    スポットはNodeの話なんだから、ALB -> Node を速やかに切断するのがシンプルな構成でしょって思ったので、Pod配置とNodeでのスポット対応を適切に行えば十分スポットも使えるなと判断しました。

    スポット対策自体はやることは同じで、メタデータ見てうんぬんするのですが、じゃあそれをどこで処理するのかと考えると、DaemonSet じゃねって結論付いたので満足しています。

    環境変数
    コンテナは環境変数の扱いが大切なのですが、これも扱い方がいくつかあるので迷いどころです。

    どのように扱えば安全なのか、そもそも安全ってどういう状態なのか、どの方法なら要件を満たせるのか、考える必要が出てきます。そのため、いくつかある方法を試しておき、それぞれの効果やメリット・デメリットを知っておくとよいです。


    まだまだ続くんぢゃ

    ここまでの内容が全てではないですが、だいたいここまでのことを考えつつ進めていって、ようやく基本的な構築が完了するくらいのイメージです。

    実際に運用するには、もっと細かいことを調査したり調整しなくては全然足りていないので、途中でぶん投げないように少しずつ続編を書いていきたいと思いますん。