以前こういう記事を書いた.
このときから更に便利になり,kOps 1.23で,ついにeks-pod-identity-webhookさえも自動インストールできるようになったので紹介する.
続きを読む以前こういう記事を書いた.
この当時はIssuerも自分で作らなければいけなかったが,kOps 1.21からIRSAのサポートが入り,これがだいぶ簡略化されるようになった. というわけでこれを紹介しておく.
続きを読むふだんkopsで自分用のKubernetesクラスタを立ち上げているんだけど,個人用途なのでAutoScaleはしてほしくないのでClusterAutoscalerは入れていない.だけど,その状態でも可用性はできる限り下げたくない. というわけで,ちょっと自分でノードを管理するカスタムコントローラを書いた.
こういう,あんまり需要がないけど自分のところだけでいろいろカスタマイズしたいときに,適当なコントローラを自作すると手動作業が減らせるので大変良い.
kopsを使ってAWS上に構築している.複数台構成で
くらいで運用している.nodeの数は乗せるものによって増やしたり減らしたり.
ちなみに全部SpotInstanceで運用している. Spotだと不意にTerminateされることが多いんだけど,大抵の場合はすぐに次のインスタンスが上がってきて,事なきを得る. たまに特定のFlavorの値段が高騰したりすると,新しいインスタンスをe立ち上げられなくなるんだけど,これに関してはAutoScalingGroupのLaunchTemplateとMixedInstancePolicyの組み合わせを使うことで,複数のFlavorを利用するとこができる. 例えば,t3.largeの値段が高騰したとしても,MixedInstancePolicyで
とか入れていれば,この3つの中で安いSpotInstanceを探してきて立ち上げてくれる. たしかにSpotPriceはたまに高騰するんだけど,複数Flavorを用意しておけばそれらがすべて高騰することはあまりない.
kopsで作成している時点で,基本的にAutoScalingGroupでインスタンスを立ち上げている.そのため特定のnodeが突然ししたとしても,新しいnodeは自動的に補充される.
ただし,特定のAvailabilityZoneのSpotが足らなくなったり,価格が高騰したりした場合,そのAZのインスタンスは起動できなくなる. 特に最近のkopsはAZごとにAutoScalingGroupを作成している.
こうなっていると,特定のASGだけひたすらインスタンスが起動できないような状態に陥ることがある.
このときに,可用性を下げたくないので,生きているASGで代替のインスタンスを一時的に起動しておいてほしい.
という要望はありつつ,不要なインスタンスまで起動していてほしくない.なので,例えば上記のトラブルが解消されたら元のインスンタンスに戻してほしいのである.
かつて,kopsのAutoScalingGroupでSpotInstanceを使うときに,LaunchTemplateではなくLaunchConfigurationしか使えない時代があった.このときにSpotInstanceを立ち上げると,Spotの期限がデフォルトの7dayになってしまっていた. これは編集不可能で,仕方なく7日おきにインスタンスを殺すようなLambdaを書いていた.
ただ,このタイミングでどうしてもインスタンスが死ぬと障害になってしまうので,Lambdaではなく内部のコントローラでこれを行いたかった.
最近のkopsはLaunchTemplateが使えて,Spotの期限も無期限にできるので,特にこのような問題はない. ただ,上記のようなことをやっていたため,どうしてもあまり長くインスタンスを生かしておきたくなかった.というわけで,週に一度くらいはnodeのローリングアップデートをしたかった.
少し考えてみて,これらを一つのcontroller-managerで実現したほうがよいと思った.というのも,不要なインスタンスを消す機能と,ローリングアップデートを同時に動かすのは結構難しい.ローリングアップデート時にインスタンスを突然殺す分には特に問題ない.ただ,それをやるとnode数がたらなくなり,配置できないPodが生じる可能性はある.そのため,予備のnodeを予め起動しておいてから,古いnodeのローリングアップデートをしたい.それをやろうと思うと,予備のnodeが不要なnodeと判定されないためにも,これらの動作を排他的に制御できるのが望ましく,そのために同じcontroller-managerで実現するほうが楽だと判断した.
やることが複数なので,複数CRDで複数controllerという構成になるものを,controller-managerで束ねるという形にしたいと思う.
まだだいぶ荒削りだがとりあえず動くものはできた.
今回はcontroller-runtimeを使ってしまっている.controllerとしてそこまで複雑なことをするわけではないので.
node-manager ├── aws-node-manager-master │ ├── replenisher-master │ └── refresher-master └── aws-node-manager-worker ├── replenisher-worker └── refresher-worker
現在のKubernetesのnodeを取得してきて,これがAWS上のどのAutoScalingGroupに属しているのか,instanceIDが何なのか,ということを埋めている.これをCRDのStatusに埋めることで,下位のcontrollerがこれらの情報を利用する.
また,CustomResourceの定義に応じてmaster, nodeのrefresherとreplenisherのCRを作成する.
CRに指定されたスケジュールに応じて以下のようなステップでローリングアップデートを行う.
kopsでは,cluster specにてetcdの数と,etcdを起動するinstance groupを指定している.これの定義以上のインスタンスを起動しても,masterとしてはクラスタに参加することができない(etcdの定義が被ってしまう)ため,そういったインスタンスはいつまで待っていても無駄である.そのため,これも削除するようにしている.
実際,LaunchTemplateとMixedInstancePolicyが使えるようになってから,replenisherの仕事はほぼなくなった.複数のflavorを指定しているので,特定のASGでインスタンスが起動できなくなるという事態がそもそも発生しにくくなっている.そのため,普段はほとんどrefresherの仕事を眺めるだけになっている.
Spotは中断の2分前に通知を出すことができる.
本当はこれを拾って,対象nodeのdrainができると良いのだけど,まだそこまで実装できていない. また,refreshにおいても,インスタンスを殺す前にdrainができると最高なのだが,まだ実装できていない.
ここの話です.
Issueの内容を熟知している方には余計なお世話です.
いやーこんなの自分には関係ないだろうと思ってたら見事に当たりました.
問題になるケースはおそらく以下の2パターン.
ちなみにこれは作成時にエラーになるわけではない.あくまでGCコントローラの挙動の変更なので,リソースが削除される挙動が変更になっている.
私の場合2に該当していた.
CRDを定義して,カスタムリソースをapplyするとAdmissionWebhookのConfigurationと,それに合わせたWebhookのPodとServiceを作るようなコントローラを作っていた. このとき,CRDのscopeをNamespacedにしていたんだけど,NamespacedなカスタムリソースがCluster scopeのValidating/MutatingWebhookConfigurationをChildに持つ場合,がっつり上記に該当してGCされなくなる. 結果として,カスタムリソースを削除してもWebhookConfigurationが削除されないという状態になった.
私の場合の話なので,2に該当する場合なのだけれど,OwnerがNamespacedである必要がないのであれば,OwnerをCluster scopeにしてしまえば良い.Cluster scopeなOwnerがCluster scopeなChildを持つことは特に問題ない.また,Cluster scopeなOwnerがNamespace scopeのChildを持つことも,特に問題はない.GCコントローラはちゃんとOwnerが消えたときにChildも消してくれる.
今回で言うなら,Validating/MutatingWebhookConfigurationのOwnerなので,別にCluster scopeで良くて,Cluster scopeに変更することで対応できた.合わせて作るPodとServiceのnamespaceをどこにするかは悩みどころではあったが.Cluster scopeなカスタムリソースもそれらのPodやServiceのOwnerになれるので,特に問題はなかった.
1についても同様なのだけれど,OwnerがCluster scopeで良いのであれば,Cluster scopeにすることで正しいOwnerReferenceになる.
問題はOwnerがNamespace scopeである必要がある場合なのだが,それってどんなケースなのか想像できないのでちょっと対応策も思いつかない…….
そもそもこの話は知っていたんだけど,完全に無関係だろうと思っていた.ので完全に油断していたんだけど,先日書いたkind local registryによりE2Eがそれなりの頻度でCIで走るようになったおかげで気づいた.
Kubernetes 1.20に上げたところでテストが落ちて,AdmissionWebhookが消えなくなっていたので,そこで問題が発覚した.
E2Eテスト便利や.
ちなみに記事でも紹介されてたけど kubectl-check-ownerrefefrences
を使えば一発でわかるんで,これも便利だったよ.