ABEJA Tech Blog

中の人の興味のある情報を発信していきます

EKSの運用を楽に安くするためにSpotinst Oceanを使って工夫してる話

こんにちは。ABEJAのインフラ管理してる村主 @rwle1221 です。 この記事は Amazon EKS Advent Calendar 2019 2日目の記事です。

1日目は AWSの中の方が re:Invent 直前のアツい想いを書く ということで、ハードルが上がってそうですがそんなことは気にせずに書いていきたいと思います。

[課題] ワーカーノードを管理したい

  • EKSのコントロールプレーンは簡単に作れる => AWS マネジメントコンソール の開始方法
  • この手順だと、単純な種類のワーカーノードをデプロイするだけに止まり、複数種類のワーカーノードを管理しようとすると自分でCloudFormationなどを作る必要がある
  • CloudFormationでワーカーノードを作るのはやぶさかではないが、CloudFormationはあくまでAutoScalingGroupを作っているだけでワーカーノードを作っているのではない
    • どういうことかというと、ASGの起動時にUserDataでKubernetesのマスターにJoinし、${BootstrapArguments} でワーカーノードのLabelやTaintを付与する仕様である。
    • 実装としては分かる。でもKubernetesのワーカーノード として管理しているの訳ではないので、なにか専用のコマンドでラップされていて作成しやすかったり、何を設定しているかやパラメータを変えやすくなっているか、などが無い。ただの裸のASGですよろしく状態。
  • これは使い勝手が悪いなー。ということで、Spotinst Oceanを紹介したいと思います。
      UserData: !Base64
        "Fn::Sub": |
          #!/bin/bash
          set -o xtrace
          /etc/eks/bootstrap.sh ${ClusterName} ${BootstrapArguments}
          /opt/aws/bin/cfn-signal --exit-code $? \
                   --stack  ${AWS::StackName} \
                   --resource NodeGroup  \
                   --region ${AWS::Region}

Spotinst Ocean の コンセプト

設定内容より Spotinst Ocean のコンセプトを紹介します。 Spotinst Ocean のワーカーノードの管理方法は EKS の Managed Node Groups や eksctl と違い、これを見た瞬間に「俺がやりたかったことはこれだわ」と思いました。

通常、EKS の Managed Node Groups や eksctl でワーカーノードを作成する場合は、次のようなコマンドを発行し ワーカーノードを作る時に「俺はこういうやつが欲しいんだ!」と様々なオプションを指定して、ワーカーノードを作成します。

aws eks create-nodegroup \
  --cluster-name -testcluster \
  --nodegroup-name worker-node1 \
  --scaling-config minSize=0,maxSize=100,desiredSize=1 \
  --disk-size 50 \
  --subnets subnet-XXXXXXXXXXXXXXXXX subnet-XXXXXXXXXXXXXXXXX subnet-XXXXXXXXXXXXXXXXX \
  --instance-types t3.medium \
  --ami-type AL2_x86_64 \
  --remote-access ec2SshKey=eks-workernodes-key \
  --node-role arn:aws:iam::123456789012:role/NodeInstanceRole

次のような形で構成が管理されるイメージになります。 ちゃんと明示したものが出来上がるので良いですね。バックではAutoScalingGroupが作成されたりとインフラ管理している人からすると分かりやすいですね。

f:id:xwkns157:20191130133358p:plain

しかし、Spotinst Ocean は少し趣向が変わっていて、Spotinst Ocean は以下のようなイメージで扱うことが可能です。

※ あくまでイメージです。ある程度細かくパラメータを指定することは可能ですが、デフォルトの設定が引き継がれたりするので、弊社では実質AMIしか変えていないため、このようなイメージ図になっています。

f:id:xwkns157:20191130134850p:plain

Spotinst Ocean という大元の部分で大半の設定は定義し、分岐するところだけ Launch Specifications で定義します。

これの利点は、AMI と UserData が違う場合に Launch Specifications を定義して、Node Label を設定します。例えば processor: gpu を Node Label に定義します。 その状態で nodeSelector で processor: gpu を指定するだけで GPUインスタンスが立ち上がります。 その際に、nodeSelector で beta.kubernetes.io/instance-type: p2.xlarge と指定すると p2.xlarge が立ち上がります。 さらに、デフォルトではスポットインスタンスで起動するのですが、PodのLabelに spotinst.io/node-lifecycle: od を指定するとオンデマンドインスタンスで起動してくれます。

弊社の場合は CPU と GPU で AMI が違うため 2種類の Launch Specifications を利用しています。

launch Specifications ではインスタンスタイプ は指定していないのに、なぜ p2.xlarge が立ち上がるかというと、Spotinst Ocean を作成するとデフォルトで以下のように全てのインスタンスタイプが 起動できる状態 になっています。そのため、nodeSelector で beta.kubernetes.io/instance-type: p2.xlarge を指定すると p2.xlarge が立ち上がるのです。

f:id:xwkns157:20191130135507p:plain

もちろん、インスタンスタイプ を絞りたい場合や、スペックを絞りたい場合は以下のように指定することもできます。 CPUやメモリのスペックの許容範囲を選ぶ形になっていて、細かくタイプを指定させるというより、その中から一番安くて最適なものを選ぶよ。という設計思想ですね。共感できます。

f:id:xwkns157:20191130135700p:plain

なお、Launch Specifications では以下のような定義ができます。

AMI と UserData はOSレベルでモノが変わるので割とセットで使うことが多いと思います。あとは少しだけ設定項目がありますが、最低限の項目になっていてシンプルですね。

f:id:xwkns157:20191130140306p:plain

設定項目が大元で共通化され、細かいインスタンスタイプやインスタンスの数などを気にせずに Kubernetes の世界から nodeSelector や toleration で色々扱えるのは良い世界感だなと思っています。

その他にも以前書いた以下のブログにある、Spotinstの細かいことを意識することなくスポットインスタンスをフル活用できる良いところも、Spotinstにはたくさん入っています。コンテナとスポットインスタンスは相性が良いので Spotinst 自体の利用もオススメです。

tech-blog.abeja.asia

Node Groupの管理負荷

さらに良いなーと思うところは、複数のNode Groupという モノ を管理していなくて、 Launch Specifications に Node Label を付与してちょっと定義する程度のモノを管理しているので、心理的管理負荷が低いんですよね。

例えば EKS Managed Node Groups や GKE Node Pool は、インスタンスタイプ が1つか選べず、ディスクサイズも1種類、GPUの種類や数も1種類、スポットインスタンスなのかオンデマンドインスタンスなのか、という and 条件で Node Group ( Node Pool ) が作られます。

実際 GKE で Node Pool を作ろうとした時に以下の方程式になり60個ほどの Node Pool が作成され、なんとも言えない顔になりました。

ゾーン数 x インスタンスタイプの種類 x GPU種類 x GPU枚数 x オンデマンド or プリエンプティブVM = 60+

そういう意味では、Spotinst Ocean は、インフラは軽い設定(1つの大元に加え軽い Launch Specifications を設定)で、Pod側で nodeSelector や toleration 、 Pod Label や requests などを設定することで柔軟な拡張ができるので、Kubernetes にあったワーカーノードの管理ができてるなぁと思っています。

ABEJAではイケてるしヤバい人材募集中です!最新の研究成果をキャッチアップしつつ実際のサービスを開発してみたい方・インターンに興味がある方がいれば、ぜひご連絡くださいー

採用情報一覧は以下↓

hrmos.co

www.wantedly.com

ABEJAの中の人と話ししたい!オフィス見学してみたいも随時受け付けておりますので、気軽にポチッとどうぞ↓↓