Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Cluster Autoscaler

bells17
March 29, 2022

Cluster Autoscaler

Kubernetes Meetup Tokyo #49で発表したセッション資料です
https://k8sjp.connpass.com/event/240993/

配信URL:
https://youtu.be/KOrantQgXkI?t=2258

bells17

March 29, 2022
Tweet

More Decks by bells17

Other Decks in Programming

Transcript

  1. Cluster Autoscaler
    Kubernetes Meetup Tokyo #49(2022/03/29)
    @bells17

    View full-size slide

  2. ▶ @bells17
    ▶ Software Engineer
    ▶ 普段やってること:
    + Kubernetes 関連コンポーネントの開発
    + Kubernetes as a Service開発
    ▶ Kubernetes SIG-Docs Japanese localization reviewer
    ▶ Kubernetes Internal Organizer
    ▶ #kubenews
    ▶ @bells17_

    View full-size slide

  3. #kubenews ほぼ毎週⾦曜22:00~YouTubeで配信中
    Kubernetes/Cloud Native関連のニュースを中⼼に技術雑談してます

    View full-size slide

  4. 今⽇話すこと
    ▶ Cluster Autoscalerとは?
    ▶ Cluster Autoscalerのアーキテクチャ
    ▶ Karpenterとの⽐較

    View full-size slide

  5. このセッションでわかること
    ▶ Cluster Autoscalerがどんなものかなんとなくわかる
    ▶ Cluster AutoscalerどういうロジックでScaleUp/Downを⾏うのか?の雰囲気が掴める

    View full-size slide

  6. 注意点
    ▶ Cluster Autoscaler v1.20.2 ベースでのお話になります(諸事情で若⼲古いです)
    + https://github.com/kubernetes/autoscaler/tree/cluster-autoscaler-1.20.2
    ▶ あくまでCluster Autoscalerの実装を追った結果での理解の説明になるので、
    間違いが含まれている可能性があります
    ▶ ぶっちゃけロジックが複雑過ぎるので、全体像を掴むのに問題無いと思う
    レベルで所々ある程度説明を端折ってたりまるめてます

    View full-size slide

  7. Cluster Autoscalerとは?

    View full-size slide

  8. Cluster Autoscaler
    ▶ Kubernetesクラスター上で動作するPodの要求リソースに応じて
    Nodeの台数を⾃動で増減させるコンポーネント
    ▶ Podの要求リソース(resource request)とNodeのキャパシティを⽐較する
    ことでNode台数の増減をコントロールする
    ▶ Node台数の調整を⾏うために、Kubernetesクラスター実⾏環境に応じた
    Cloud Providerの選択もしくは実装~組み込みが必要となる
    ▶ #sig-autoscalingによって管理
    ▶ Kubernetes v1.8のタイミングでVersion 1.0 (GA)がリリースされた
    ▶ ScaleUp/ScaleDownを判定するためのロジックが本当に複雑…

    View full-size slide

  9. 公式でサポートされるCloud Provider
    ▶ AliCloud
    ▶ AWS
    ▶ Azure
    ▶ BaiduCloud
    ▶ CloudStack
    ▶ Cluster API
    ▶ DigitalOcean
    ▶ Exoscale
    ▶ GCP
    ▶ Huawei Cloud
    ▶ Ionos Cloud Managed Kubernetes
    ▶ kubemark
    ▶ OpenStack Magnum
    ▶ Packet
    この中に利用したい環境のCloud Providerが無い場合は自作する必要あり

    View full-size slide

  10. Miroに書いたメモはこれくらいの量になった..

    View full-size slide

  11. 動作イメージ(ScaleUp)

    View full-size slide

  12. ここに2台のWorker Nodeのあるk8sクラスターがあるとします
    LVCFDUMHFUOPEFTFMFDUPSOPEFSPMFLVCFSOFUFTJPDPOUSPMQMBOFPHP
    UFNQMBUF\\aO^^\\SBOHFJ JUFNJUFNT^^\\/P^^\\J^^\\OBNF^^
    \\JUFNNFUBEBUBOBNF^^\\DQV^^\\JUFNTUBUVTDBQBDJUZDQV^^\\NFNPSZ^^
    \\JUFNTUBUVTDBQBDJUZNFNPSZ^^\\aO^^\\FOE^^
    /POBNFDOMMOQEQGDDTBOECPYXPSLFSDQVNFNPSZ,J
    /POBNFDOMMOQEQGDDTBOECPYXPSLFSDQVNFNPSZ,J

    View full-size slide

  13. このクラスターでは下記のようにCPU=3のリソースを消費する
    Podが2台既に⽴ち上がっています
    LVCFDUMHFUQPEPHPUFNQMBUF\\aO^^\\SBOHFJ JUFNJUFNT^^\\/P^^\\J^^\\
    OBNF^^\\JUFNNFUBEBUBOBNF^^\\DQV^^\\ JOEFYJUFNTQFDDPOUBJOFST

    SFTPVSDFTSFRVFTUTDQV^^\\aO^^\\FOE^^
    /POBNFOHJOYDQV
    /POBNFOHJOYDQV

    View full-size slide

  14. ここに更にCPU=3のリソースを消費する1台のPodを追加します
    DBUQPEZBNM
    BQJ7FSTJPOW
    LJOE1PE
    NFUBEBUB
    OBNFOHJOY
    OBNFTQBDFEFGBVMU
    TQFD
    DPOUBJOFST
    JNBHFOHJOY
    OBNFOHJOY
    SFTPVSDFT
    SFRVFTUT
    DQV
    LVCFDUMBQQMZGQPEZBNM
    QPEOHJOYDSFBUFE

    View full-size slide

  15. 新たに⽴ち上げようとしたPodはリソース不⾜でpendingステータスに
    なってしまいます
    LVCFDUMHFUQPE
    /".&3&"%:45"5643&45"354"(&
    OHJOY3VOOJOHE
    OHJOY3VOOJOHE
    OHJOY1FOEJOHT
    LVCFDUMEFTDSJCFQPEOHJOY
    தུ

    &WFOUT
    5ZQF3FBTPO"HF'SPN.FTTBHF

    8BSOJOH'BJMFE4DIFEVMJOHTEFGBVMUTDIFEVMFSOPEFTBSFBWBJMBCMFOPEF T

    IBEUBJOU\OPEFSPMFLVCFSOFUFTJPDPOUSPMQMBOFUSVF^ UIBUUIFQPEEJEOUUPMFSBUF *OTV⒏DJFOUDQV

    View full-size slide

  16. しかしCluster Autoscalerがいると⾜りないリソースの分の
    Nodeをいつのまにか⾃動で追加してくれます
    LVCFDUMHFUOPEFTFMFDUPSOPEFSPMFLVCFSOFUFTJPDPOUSPMQMBOFPHP
    UFNQMBUF\\aO^^\\SBOHFJ JUFNJUFNT^^\\/P^^\\J^^\\OBNF^^
    \\JUFNNFUBEBUBOBNF^^\\DQV^^\\JUFNTUBUVTDBQBDJUZDQV^^\\NFNPSZ^^
    \\JUFNTUBUVTDBQBDJUZNFNPSZ^^\\aO^^\\FOE^^
    /POBNFDOMMOQEQGDDTBOECPYXPSLFSDQVNFNPSZ,J
    /POBNFDOMMOQEQGDDTBOECPYXPSLFSDQVNFNPSZ,J
    /POBNFDOMMOQEQGDDTBOECPYXPSLFSDQVNFNPSZ,J

    View full-size slide

  17. リソースが追加されたことでpendingだったPodが起動されました
    LVCFDUMEFTDSJCFQPEOHJOY
    4UBUVT3VOOJOH
    தུ

    &WFOUT
    5ZQF3FBTPO"HF'SPN.FTTBHF

    8BSOJOH'BJMFE4DIFEVMJOHNTEFGBVMUTDIFEVMFSOPEFTBSFBWBJMBCMFOPEF T
    IBEUBJOU\OPEFSPMFLVCFSOFUFTJP
    DPOUSPMQMBOFUSVF^ UIBUUIFQPEEJEOUUPMFSBUF *OTV⒏DJFOUDQV
    8BSOJOH'BJMFE4DIFEVMJOHNT YPWFSNT
    EFGBVMUTDIFEVMFSOPEFTBSFBWBJMBCMFOPEF T
    IBEUBJOU\OPEFSPMFLVCFSOFUFTJP
    DPOUSPMQMBOFUSVF^ UIBUUIFQPEEJEOUUPMFSBUF *OTV⒏DJFOUDQV
    8BSOJOH'BJMFE4DIFEVMJOHNTEFGBVMUTDIFEVMFSOPEFTBSFBWBJMBCMFOPEF T
    IBEUBJOU\OPEFSPMFLVCFSOFUFTJP
    DPOUSPMQMBOFUSVF^ UIBUUIFQPEEJEOUUPMFSBUF OPEF T
    IBEUBJOU\OPEFLVCFSOFUFTJPOPUSFBEZ^ UIBUUIFQPEEJEOUUPMFSBUF *OTV⒏DJFOUDQV
    /PSNBM4DIFEVMFENTEFGBVMUTDIFEVMFS4VDDFTTGVMMZBTTJHOFEEFGBVMUOHJOYUPDOMMOQEQGDDTBOECPYXPSLFS
    /PSNBM5SJHHFSFE4DBMF6QNTDMVTUFSBVUPTDBMFSQPEUSJHHFSFETDBMFVQ<\TPNFOPEFHSPVQ NBY
    ^>
    /PSNBM1VMMJOHNTLVCFMFU1VMMJOHJNBHFOHJOY
    /PSNBM1VMMFENTLVCFMFU4VDDFTTGVMMZQVMMFEJNBHFOHJOYJOT
    /PSNBM$SFBUFENTLVCFMFU$SFBUFEDPOUBJOFSOHJOY
    /PSNBM4UBSUFENTLVCFMFU4UBSUFEDPOUBJOFSOHJOY

    View full-size slide

  18. ScaleUp時のCluster Autoscalerのログはこんな感じ
    *TUBUJD@BVUPTDBMFSHP>4UBSUJOHNBJOMPPQ
    *LMPHYHP>1PEEFGBVMUOHJOYJTVOTDIFEVMBCMF
    *TDBMF@VQHP>6QDPNJOHOPEFT
    *TDBMF@VQHP>#FTUPQUJPOUPSFTJ[FTPNFOPEFHSPVQ
    *TDBMF@VQHP>&TUJNBUFEOPEFTOFFEFEJOTPNFOPEFHSPVQ
    *TDBMF@VQHP>'JOBMTDBMFVQQMBO<\TPNFOPEFHSPVQ NBY
    ^>
    *TDBMF@VQHP>4DBMFVQTFUUJOHHSPVQTPNFOPEFHSPVQTJ[FUP
    *FWFOU@TJOL@MPHHJOH@XSBQQFSHP>&WFOU W0CKFDU3FGFSFODF\,JOE$POpH.BQ /BNFTQBDFDMVTUFS
    BVUPTDBMFSSBODIFS /BNFDMVTUFSBVUPTDBMFSTUBUVT 6*%DEGCFGEDGCCD "1*7FSTJPOW
    3FTPVSDF7FSTJPO 'JFME1BUI^
    UZQF/PSNBMSFBTPO4DBMFE6Q(SPVQ4DBMFVQTFUUJOHHSPVQTPNFOPEFHSPVQTJ[FUP
    *FWFOU@TJOL@MPHHJOH@XSBQQFSHP>&WFOU W0CKFDU3FGFSFODF\,JOE$POpH.BQ /BNFTQBDFDMVTUFS
    BVUPTDBMFSSBODIFS /BNFDMVTUFSBVUPTDBMFSTUBUVT 6*%DEGCFGEDGCCD "1*7FSTJPOW
    3FTPVSDF7FSTJPO 'JFME1BUI^
    UZQF/PSNBMSFBTPO4DBMFE6Q(SPVQ4DBMFVQTFUUJOHHSPVQTPNFOPEFHSPVQTJ[FUP
    *FWFOU@TJOL@MPHHJOH@XSBQQFSHP>&WFOU W0CKFDU3FGFSFODF\,JOE1PE /BNFTQBDFEFGBVMU
    /BNFOHJOY 6*%EEFDEDDCGGFB "1*7FSTJPOW 3FTPVSDF7FSTJPO 'JFME1BUI^
    UZQF
    /PSNBMSFBTPO5SJHHFSFE4DBMF6QQPEUSJHHFSFETDBMFVQ<\TPNFOPEFHSPVQ NBY
    ^>

    View full-size slide

  19. 動作イメージ(ScaleDown)

    View full-size slide

  20. 逆に先程⽴ち上げたPodを削除します
    LVCFDUMEFMFUFGQPEZBNM
    QPEOHJOYEFMFUFE

    View full-size slide

  21. 削除して少しするとNodeにTaintが付与されます
    LVCFDUMEFTDSJCFOPEFDOMMOQEQGDDTBOECPYXPSLFS
    /BNFDOMMOQEQGDDTBOECPYXPSLFS
    தུ

    5BJOUT
    %FMFUJPO$BOEJEBUF0G$MVTUFS"VUPTDBMFS1SFGFS/P4DIFEVMF

    View full-size slide

  22. Cluster Autocalerのログからunneeded nodeに選ばれTaintが付与されたことがわかります
    (削除⾃体はunneeded nodeに選ばれてから時間を少し置くのでまだされません)
    *TUBUJD@BVUPTDBMFSHP>4UBSUJOHNBJOMPPQ
    *TUBUJD@BVUPTDBMFSHP>$BMDVMBUJOHVOOFFEFEOPEFT
    *QSF@pMUFSJOH@QSPDFTTPSHP>4LJQQJOHDOMMOQKEGSRTBOECPYNBTUFSOPOPEFHSPVQDPOpH
    *TDBMF@EPXOHP>/PEFDOMMOQEQGDDTBOECPYXPSLFSJTOPUTVJUBCMFGPSSFNPWBMDQVVUJMJ[BUJPOUPPCJH


    *TDBMF@EPXOHP>/PEFDOMMOQEQGDDTBOECPYXPSLFSJTOPUTVJUBCMFGPSSFNPWBMDQVVUJMJ[BUJPOUPPCJH


    *TDBMF@EPXOHP>/PEFDOMMOQEQGDDTBOECPYXPSLFSDQVVUJMJ[BUJPO
    *TUBUJD@BVUPTDBMFSHP>DOMMOQEQGDDTBOECPYXPSLFSJTVOOFFEFETJODF
    65$NEVSBUJPOT
    *TUBUJD@BVUPTDBMFSHP>4DBMFEPXOTUBUVTVOOFFEFE0OMZGBMTFMBTU4DBMF6Q5JNF
    65$NMBTU4DBMF%PXO%FMFUF5JNF65$
    NMBTU4DBMF%PXO'BJM5JNF65$NTDBMF%PXO'PSCJEEFOGBMTF
    JT%FMFUF*O1SPHSFTTGBMTFTDBMF%PXO*O$PPMEPXOGBMTF
    *TUBUJD@BVUPTDBMFSHP>4UBSUJOHTDBMFEPXO
    *TDBMF@EPXOHP>DOMMOQEQGDDTBOECPYXPSLFSXBTVOOFFEFEGPST
    *TDBMF@EPXOHP>/PDBOEJEBUFTGPSTDBMFEPXO
    *EFMFUFHP>4VDDFTTGVMMZBEEFE%FMFUJPO$BOEJEBUF5BJOUPOOPEFDOMMOQEQGDDTBOECPYXPSLFS

    View full-size slide

  23. しばらくしてNodeを確認すると3台⽬のNodeが削除されているのが確認できます
    LVCFDUMHFUOPEFTFMFDUPSOPEFSPMFLVCFSOFUFTJPDPOUSPMQMBOFPHP
    UFNQMBUF\\aO^^\\SBOHFJ JUFNJUFNT^^\\/P^^\\J^^\\OBNF^^
    \\JUFNNFUBEBUBOBNF^^\\DQV^^\\JUFNTUBUVTDBQBDJUZDQV^^\\NFNPSZ^^
    \\JUFNTUBUVTDBQBDJUZNFNPSZ^^\\aO^^\\FOE^^
    /POBNFDOMMOQEQGDDTBOECPYXPSLFSDQVNFNPSZ,J
    /POBNFDOMMOQEQGDDTBOECPYXPSLFSDQVNFNPSZ,J

    View full-size slide

  24. EventをみるとScaleDownによってnodeが削除されるのが確認できます
    LVCFDUMHFUFWFOUT
    -"454&&/5:1&3&"40/0#+&$5.&44"(&
    NT/PSNBM3FNPWJOH/PEFOPEFDOMMOQEQGDDTBOECPYXPSLFS
    /PEFDOMMOQEQGDDTBOECPYXPSLFSFWFOU3FNPWJOH/PEFDOMMOQEQGDD
    TBOECPYXPSLFSGSPN$POUSPMMFS
    NT/PSNBM4DBMF%PXOOPEFDOMMOQEQGDDTBOECPYXPSLFS
    OPEFSFNPWFECZDMVTUFSBVUPTDBMFS

    View full-size slide

  25. ScaleDown時のCluster Autoscalerのログはこんな感じ
    *TUBUJD@BVUPTDBMFSHP>DOMMOQEQGDDTBOECPYXPSLFSJTVOOFFEFETJODF
    65$NEVSBUJPONT
    *TUBUJD@BVUPTDBMFSHP>4DBMFEPXOTUBUVTVOOFFEFE0OMZGBMTFMBTU4DBMF6Q5JNF
    65$NMBTU4DBMF%PXO%FMFUF5JNF65$N
    MBTU4DBMF%PXO'BJM5JNF65$NTDBMF%PXO'PSCJEEFOGBMTFJT%FMFUF*O1SPHSFTTGBMTF
    TDBMF%PXO*O$PPMEPXOGBMTF
    *TUBUJD@BVUPTDBMFSHP>4UBSUJOHTDBMFEPXO
    *TDBMF@EPXOHP>DOMMOQEQGDDTBOECPYXPSLFSXBTVOOFFEFEGPSNT
    *TDBMF@EPXOHP>4DBMFEPXOSFNPWJOHFNQUZOPEFDOMMOQEQGDDTBOECPYXPSLFS
    *FWFOU@TJOL@MPHHJOH@XSBQQFSHP>&WFOU W0CKFDU3FGFSFODF\,JOE$POpH.BQ /BNFTQBDFDMVTUFSBVUPTDBMFSSBODIFS
    /BNFDMVTUFSBVUPTDBMFSTUBUVT 6*%DEGCFGEDGCCD "1*7FSTJPOW 3FTPVSDF7FSTJPO 'JFME1BUI^
    UZQF
    /PSNBMSFBTPO4DBMF%PXO&NQUZ4DBMFEPXOSFNPWJOHFNQUZOPEFDOMMOQEQGDDTBOECPYXPSLFS
    *FWFOU@TJOL@MPHHJOH@XSBQQFSHP>&WFOU W0CKFDU3FGFSFODF\,JOE$POpH.BQ /BNFTQBDFDMVTUFSBVUPTDBMFSSBODIFS
    /BNFDMVTUFSBVUPTDBMFSTUBUVT 6*%DEGCFGEDGCCD "1*7FSTJPOW 3FTPVSDF7FSTJPO 'JFME1BUI^
    UZQF
    /PSNBMSFBTPO4DBMF%PXO&NQUZ4DBMFEPXOSFNPWJOHFNQUZOPEFDOMMOQEQGDDTBOECPYXPSLFS
    *EFMFUFHP>4VDDFTTGVMMZBEEFE5P#F%FMFUFE5BJOUPOOPEFDOMMOQEQGDDTBOECPYXPSLFS
    *FWFOU@TJOL@MPHHJOH@XSBQQFSHP>&WFOU W0CKFDU3FGFSFODF\,JOE/PEF /BNFTQBDF /BNFDOMMOQEQGDD
    TBOECPYXPSLFS 6*%GBGCBFFBDFCCDFCGE "1*7FSTJPOW 3FTPVSDF7FSTJPO 'JFME1BUI^
    UZQF/PSNBMSFBTPO
    4DBMF%PXOOPEFSFNPWFECZDMVTUFSBVUPTDBMFS

    View full-size slide

  26. 動作イメージまとめ
    ▶ 紹介したイメージのようにPodのリソース要求に対してNode側が
    リソース不⾜に陥った場合、Cluster AutoscalerはNodeの増加を⾏い、
    リソースを補填する
    ▶ 反対にScaleDownを⾏う際は、削除可能なNodeのシミュレートを⾏い、
    unneededなNodeが⾒つかった場合は⼀定の時間を置いてから、
    uneededなNodeの削除を⾏い台数の削減を⾏う
    ▶ uneededなNodeが⾒つかった際、基本的には削除前に
    ”DeletionCandidateOfClusterAutoscaler”というTaintが付与され、
    対象NodeにPodがスケジューリングされるのを防⽌する
    ▶ 削除処理中は”ToBeDeletedTaint”というTaintが付与され、削除処理が
    実施中であることが確認できるようになっている

    View full-size slide

  27. Cluster Autoscalerのアーキテクチャ

    View full-size slide

  28. Cluster Autoscaler Overview

    View full-size slide

  29. 主なプロセス(go routineなど)
    ▶ main loop: ScaleUp/ScaleDown処理を担当
    ▶ CloudProviderNodeInstancesCache Refresh loop:
    + 各NodeGroupに所属するInstance(Node)⼀覧のキャッシュを定期的に
    リフレッシュする
    ▶ http server: 以下の2つのEndpointを提供するhttp server
    ▶ /metrics: prometheus形式のメトリクス取得⽤Endpoint
    ▶ /health-check: ヘルスチェック⽤Endpoint
    ▶ 起動するhttp serverのデフォルトポートは8085

    View full-size slide

  30. CloudProviderNodeInstancesCache Refresh loop
    以下の処理を2分毎に実⾏してるだけ
    // Refresh refreshes cache.
    func (cache *CloudProviderNodeInstancesCache) Refresh() {
    klog.Infof("Start refreshing cloud provider node instances cache")
    refreshStart := time.Now()
    nodeGroups := cache.cloudProvider.NodeGroups()
    cache.removeEntriesForNonExistingNodeGroupsLocked(nodeGroups)
    for _, nodeGroup := range nodeGroups {
    nodeGroupInstances, err := nodeGroup.Nodes()
    if err != nil {
    klog.Errorf("Failed to get cloud provider node instance for node group %v, error %v",
    nodeGroup.Id(), err)
    }
    cache.updateCacheEntryLocked(nodeGroup,
    &cloudProviderNodeInstancesCacheEntry{nodeGroupInstances, time.Now()})
    }
    klog.Infof("Refresh cloud provider node instances cache finished, refresh took %v",
    time.Now().Sub(refreshStart))
    }
    https://github.com/kubernetes/autoscaler/blob/cluster-autoscaler-1.20.2/cluster-autoscaler/clusterstate/utils/node_instances_cache.go#L154-L176

    View full-size slide

  31. ScaleUp/Down おおまかな流れ
    (簡単に説明するためにめちゃくちゃ省略してます)

    View full-size slide

  32. 1. 初期化処理
    1. Node/Pod情報の取得
    2. CloudProvider情報のRefresh
    3. ClusterStateRegistry(Cluster内のNodeGroup/Node/ScaleUp/ScaleDown/etc のデータを
    ⼊れるオブジェクト)のデータを更新
    4. ClusterSnapshot(Schedulingのシュミレーションを⾏うためのNode/Podを詰め込んだオブ
    ジェクト)のデータを構築

    View full-size slide

  33. 2. NodeGroup/Nodeの状態チェック
    1. CloudProvider側のInstance情報には存在して、k8s Nodeには存在しない
    Node(unregisteredNodes)が登録から⼀定時間経過していれば、そのNodeを
    削除して処理を中断する
    + ⻑期間unregisteredNodesである=Nodeのprovisioningに失敗したとみなされたので削除さ
    れる
    2. ClusterStateRegistryにUnreadyであると判断されたNode数が⼀定数を超えた場合はCluster
    がUnhealthyだと判断されたら処理を中断する
    3. Node作成中にCloudProviderからエラーだと判断されたInstanceがあった場合、それらの
    Instanceを削除
    4. Cloud Providerの各NodeGroupのInstance数がNodeGroupの想定サイズ内に収まっていない
    場合、Instance数をNodeGroupの想定サイズに収まるように台数削減を⾏い、処理を中断する

    View full-size slide

  34. 3. ScaleUp
    1. unschedulableなPodが存在しなければScaleUpをSkip(そもそもScaleUpする必要が無い)
    2. CloudProviderから取得できるNodeGroupをフィルタリング~unschedulablePodの
    スケジューリングシュミレーションをパスしたNodeGroupをScaleUp候補にする
    3. ScaleUp候補のNodeGroupの中からExpanderを⽤いて最終的にScaleUpを⾏うNodeGroupを
    選定する
    + 選択可能なExpanderはrandom/most-pods/least-waste/price/priorityの5つ
    4. 選定されたNodeGroupと類似のNodeGroupをScaleUpを⾏うNodeGroupに追加する
    + 類似NodeGroupの追加は“--balance-similar-node-groups”オプションをtrueに設定していた
    場合のみ
    5. ScaleUpを⾏うNodeGroup⼀覧の内、どのNodeGroupを何台増加するかの調整を⾏う
    + NodeGroupの台数増加は可能な限り均等に⾏われる
    + トータルで増加するNode台数はスケジューリングシュミレーションの際に決定される
    6. 上記の調整内容を元にScaleUpを実⾏ ~ 成功したら処理を終了(ScaleDownプロセスには移⾏しない)

    View full-size slide

  35. 4. ScaleDown準備
    1. ScaleUpが⾏われなかった場合はScaleDownに関する処理を開始
    2. Nodeリソース(GPU or CPU or Mem)の中で最も使⽤率の⾼いリソースの使⽤率が⼀定値以下
    であるNodeからScaleDown候補となるuneededNode⼀覧の⽣成を⾏う
    a. emptyNode: 以下のような条件を満たすPodを持たないNode
    1. Mirro Podではない
    2. DaemonSet Podではない
    3. ReplicaSetやJobなどに紐付いていない
    4. etc
    b. ⾮emptyNodeの中で以下のような条件をNode
    + 対象Nodeの削除をブロッキングするPodが無い
    + 対象Nodeで現在動作しているPodの中で再スケジューリングが必要なPodが全て他のNodeに
    再スケジューリングできる
    + 再スケジューリングできるかはScheduling Framework PreFilter/Filterによって検証する

    View full-size slide

  36. 5. ScaleDown
    1. unneededNodesに追加されてから⼀定時間が経過したNodeを対象にScaleDown処理を開始
    2. 対象となるunneededNodesからemptyNodesを抽出
    3. emptyNodesが存在すれば全てのemptyNodesの削除を実施
    + 削除処理はNode単位でgo routineを利⽤して⾮同期に⾏う
    + 削除前にNodeに”ToBeDeletedTaint”Taintを付与する
    4. 削除開始したemptyNodesが1つ以上あった場合はScaleDown処理を終了する
    5. emptyNodesの削除が⾏われなかった場合は、⾮emptyNodesの削除処理を⾏う
    6. ⾮emptyNodesの中から1つのNodeを取り出しemptyNodesの際と同様にgo routineで⾮同期
    での削除を開始する
    7. もし、ScaleDownを開始するNodeが1つもなかった場合はunneededNodesに対して
    "DeletionCandidateOfClusterAutoscaler"Taintの付与を⾏う

    View full-size slide

  37. 補⾜
    ▶ 説明したロジックはわかりやすさ重視のためめちゃくちゃ簡略化してます
    ▶ スライド最後のAppendixにもう少し詳しいフローを書いてますので興味ある⽅はそちらも
    ⾒てみてください

    View full-size slide

  38. Karpenterとの⽐較

    View full-size slide

  39. Karpenter
    ▶ AWSが作成した独⾃のCluster Autoscaler
    ▶ 最新バージョンは現在v0.7.3なのでまだGAしてない?
    + 今回調査したKarpenterもこのバージョン
    ▶ controller-runtimeによるOperator形式で実装されている
    ▶ autoscalingを⾏うための設定をprovisioning Custom Resourceとして定義
    することでautoscalingを実現
    ▶ Cloud Provider機能によって任意の実⾏環境でKarpenterを動作させること
    ができるが、現在の実装はAWSのみ
    ▶ コードの実装がCluster Autoscalerの1000倍くらい簡単でコードもきれい
    logo: https://github.com/aws/karpenter/blob/v0.7.3/website/static/logo.png

    View full-size slide

  40. KarpenterとCluster Autoscaler
    ▶ https://karpenter.sh/v0.7.3/concepts/ によるとCluster Autoscalerと
    ⽐べ以下のような特徴があるとのこと
    + Group-less node provisioning:
    + NodeGroupのような概念はなく、直接provisioningするNodeを管理する
    + またKarpenterによって起動された訳ではないNodeはScaleDownしない
    + Scheduling enforcement:
    + kube-schedulerを介さずにbinpackingしたPodを
    provisioningしたNodeにKarpenterが直接スケジュールする
    + なので例えばCSIStorageCapacityの情報など、kube-schedulerであればScheduling
    Frameworkがスケジューリング可能かをチェックするような厳密なチェックを⾏わずに
    スケジューリングを⾏う
    + Designed to handle the full flexibility of the cloud:
    + クラウド側インスタンスタイプの柔軟な選択が可能
    logo: https://github.com/aws/karpenter/blob/v0.7.3/website/static/logo.png

    View full-size slide

  41. Karpenter Overview

    View full-size slide

  42. Karpenter ScaleUp

    View full-size slide

  43. Karpenter ScaleDown

    View full-size slide

  44. 感想
    ▶ ScaleUp/Downのロジックがめっちゃ複雑だった…
    ▶ とはいえ計算対象からMirror PodやDaemonSet Podなどを取り除く必要があるなど、
    いろんなロジックで計算しなければいけないことを知れたのは⾯⽩かった
    ▶ また、シミュレーションの際にPreFilter/Filterを⾏ったチェックをしていたり、ScaleUpが
    必要なNode数をFirst Fit Decreasing bin-packing approximation algorithmという
    アルゴリズムでPodの箱詰めを⾏って計算しているというやり⽅も興味深かった
    ▶ Karpenterのロジックと⽐べるとだいぶ複雑なので、ある程度Karpenterで間に合うレベル
    であればこのくらいシンプルな⽅が個⼈的には良いかなという気も…
    + とはいえスケジューリング可能かのチェックがかなり簡易的だしKarpenter側で直接
    Nodeにスケジューリングしてる箇所は場合によっては問題を発⽣させる可能性が⾼く
    なるので難しいところ

    View full-size slide

  45. 参考資料
    ▶ https://github.com/kubernetes/autoscaler/tree/cluster-autoscaler-1.20.2/cluster-autoscaler
    ▶ https://github.com/kubernetes/autoscaler/tree/cluster-autoscaler-1.20.2
    ▶ https://kubernetes.io/blog/2016/07/autoscaling-in-kubernetes/
    ▶ https://docs.oracle.com/ja-jp/iaas/Content/ContEng/Tasks/contengusingclusterautoscaler.htm
    ▶ https://docs.aws.amazon.com/eks/latest/userguide/autoscaling.html
    ▶ https://cloud.google.com/kubernetes-engine/docs/concepts/cluster-autoscaler
    ▶ https://docs.microsoft.com/ja-jp/azure/aks/cluster-autoscaler
    ▶ https://en.wikipedia.org/wiki/Bin_packing_problem
    ▶ https://en.wikipedia.org/wiki/First-fit-decreasing_bin_packing
    ▶ https://github.com/aws/karpenter/tree/v0.7.3
    ▶ https://karpenter.sh/v0.7.3/concepts/
    ▶ https://karpenter.sh/v0.7.3/provisioner/
    ▶ https://karpenter.sh/v0.7.3/aws/provisioning/
    ▶ https://github.com/aws/karpenter/blob/v0.7.3/designs/v1alpha4-api.md
    ▶ https://github.com/aws/karpenter/blob/v0.7.3/designs/termination.md
    ▶ https://github.com/aws/karpenter/blob/v0.7.3/designs/limits.md
    ▶ https://github.com/aws/karpenter/blob/v0.7.3/designs/bin-packing.md

    View full-size slide

  46. Thanks / Question?
    ▶ @bells17
    ▶ Slide: https://speakerdeck.com/bells17
    ▶ @bells17_

    View full-size slide

  47. Appendix
    本番はここから!

    View full-size slide

  48. インデックス
    ▶ Cloud Provider実装について
    ▶ 主なオプション
    ▶ ClusterSnapshot
    ▶ PredicateChecker
    ▶ Estimator
    ▶ Expander⼀覧
    ▶ ClusterStateStrategy
    ▶ ScaleDown props
    ▶ ScaleUp/Down ざっくりフロー

    View full-size slide

  49. Cloud Provider実装について

    View full-size slide

  50. Cloud Provider
    ▶ Cluster Autoscalerに組み込んで実⾏される
    ▶ Goで定義されたインターフェイスのメソッドを実装して組み込むこと
    で、独⾃のCloud Provider実装をCluster Autoscalerから利⽤することが
    できる
    ▶ Goのインターフェイスであるインターフェイスは↓にある
    https://github.com/kubernetes/autoscaler/blob/cluster-autoscaler-
    1.20.2/cluster-autoscaler/cloudprovider/cloud_provider.go

    View full-size slide

  51. CloudProvider Interface
    type CloudProvider interface {
    Name() string
    NodeGroups() []NodeGroup
    NodeGroupForNode(*apiv1.Node) (NodeGroup, error)
    Pricing() (PricingModel, errors.AutoscalerError)
    GetAvailableMachineTypes() ([]string, error)
    NewNodeGroup(machineType string,
    labels map[string]string,
    systemLabels map[string]string,
    taints []apiv1.Taint,
    extraResources map[string]resource.Quantity) (NodeGroup, error)
    GetResourceLimiter() (*ResourceLimiter, error)
    GPULabel() string
    GetAvailableGPUTypes() map[string]struct{}
    Cleanup() error
    Refresh() error
    }

    View full-size slide

  52. NodeGroup Interface
    type NodeGroup interface {
    MaxSize() int
    MinSize() int
    TargetSize() (int, error)
    IncreaseSize(delta int) error
    DeleteNodes([]*apiv1.Node) error
    DecreaseTargetSize(delta int) error
    Id() string
    Debug() string
    Nodes() ([]Instance, error)
    TemplateNodeInfo() (*schedulerframework.NodeInfo, error)
    Exist() bool
    Create() (NodeGroup, error)
    Delete() error
    Autoprovisioned() bool
    }

    View full-size slide

  53. 独⾃のCloud Providerを実装する
    ▶ CloudProvider/NodeGroupの2つのインターフェイスを満たした実装を⾏う
    ▶ 上記CloudProviderインスタンスを⽣成するBuilderを実装する
    ▶ Cluster Autoscalerのmain.goをコピーして、上記Builderを利⽤してCloud Providerを
    ⽣成できるように書き換える
    (Cluster Autoscalerはライブラリとして分離されてないためこういった対応が必要)
    ▶ mainifestsファイルについては各CloudProviderのexampleにあるものが参考になる

    View full-size slide

  54. 注意点
    ▶ NodeGroup内のNode削除を⾏うDeleteNodesは実際のNodeの削除完了が成功するまで
    待機する必要がある
    ▶ Cloud Provider Interfaceの中には”NewNodeGroup”というメソッドがあり、まるで現在
    稼働中のクラスターに新たなNodeGroupを追加できるかのように思えるが、実際にはこの
    メソッドが利⽤されることは無い点に注意
    + これは現在最新のv1.23の実装でも同様
    + NewNodeGroupメソッドを利⽤した新たなNodeGroupの追加を⾏いたい場合は、
    Cluster Autoscalerの拡張ポイントの1つであるNodeGroupManager processorの独⾃実装
    が必要
    + 不要なNodeGroupを削除するNodeGroup.Deleteも同様に呼び出されることは無い

    View full-size slide

  55. 主なオプション

    View full-size slide

  56. 主なオプション
    オプション名 説明
    cloud-provider 利⽤するCloud Provider
    cloud-config
    選択したCloud Providerに渡される設定ファイルのパス
    Cloud側に接続するためのAPIキーなどを主に設定する
    scan-interval
    Cluster Autoscalerのループ間隔を設定する
    デフォルト: 10秒
    namespace
    Cluster Autoscalerを起動するNamespace
    Statusを保存するConfigMapとLeaderElectionのResourceLockに指定したNamespaceが利⽤される
    デフォルト: kube-system
    node-autoprovisioning-enabled
    本来であればtrueにすれば必要に応じて新たなNodeGroupを作成してくれるオプションなはず
    しかし実装が無いので現実には機能しない
    scale-down-enabled
    ScaleDown機能を有効にするかどうか?
    デフォルト: true
    balance-similar-node-groups trueにするとScaleUpの際に選ばれたNodeGroupと類似のNodeGroupをScaleUp処理に追加するようになる
    expander
    ScaleUpの際に候補となるNodeGroupから実際にScaleUpするNodeGroupを選ぶ際のアルゴリズムを選択する
    組み込まれてるexpanderはrandom/most-pods/least-waste/price/priorityの5つ
    デフォルト: random
    estimator
    NodeGroupをScaleUpする際に何台のNode追加すれば対象NodeGroupのNodeにスケジューリング可能なunschedulablePodを全て
    スケジューリングできるかを計算するアルゴリズムを選択する
    組み込まれてるのはbinpacking1つのみ
    max-nodes-total
    Cluster AutoscalerがScaleUpする総Node数(このNode数を超えるサイズにはScaleUpされない)
    デフォルト: 0(無制限)
    cores-total
    クラスターの最⼩:最⼤CPU数(この範囲を超えるサイズにはScaleUp/Downを⾏わない)
    デフォルト: 0: 320000 (単位: コア数 format <最⼩値>:<最⼤値>)
    memory-total
    クラスターの最⼩:最⼤メモリ数(この範囲を超えるサイズにはScaleUp/Downを⾏わない)
    デフォルト: 0: 6400000(単位: GiB format <最⼩値>:<最⼤値>)

    View full-size slide

  57. ClusterSnapshot

    View full-size slide

  58. ClusterSnapshot
    ▶ ClusterSnapshotはCluster内のNodeと各NodeにスケジューリングされたPodを管理する
    オブジェクト
    ▶ 主にPredicateCheckerによる対象Podをスケジューリング可能なNodeを探査するために利⽤
    される
    ▶ PredicateCheckerはClusterSnapshotをScheduling Frameworkのlisterとして振る舞わせるこ
    とでClusterSnapshotのデータを元にScheduling Frameworkに処理をさせる

    View full-size slide

  59. PredicateChecker

    View full-size slide

  60. PredicateChecker
    ▶ PredicateCheckerはScaleUp/Downを⾏うための以下のチェックを⾏うためのインターフェイス
    + 対象のPodを対象のNodeにスケジューリング可能か?
    + 対象のPodをスケジューリング可能なNodeはどれか?
    ▶ 実装はScheduling FrameworkのPreFilter/Filterによってチェックを⾏う
    SchedulerBasedPredicateChecker1つのみ
    + Scheduling Frameworkに設定はデフォルトでのみ使⽤可
    + 設定変更などしたい場合は独⾃実装が必要
    ▶ ScaleUpでの主な利⽤箇所:
    + 対象NodeGroupにスケジューリング可能なPodがあるかの判定に利⽤
    + Binpacking AlgorithmによるPodを詰め込めるNodeの検索に利⽤
    ▶ ScaleDownでの主な利⽤箇所:
    + ScaleDown候補のNodeで動かしている再スケジューリングが必要なPodの
    再スケジューリング先のNodeを探すために利⽤

    View full-size slide

  61. Estimator
    ▶ ScaleUpを⾏う際に対象NodeGroupのNodeを何台増やせばよいかを計算するために利⽤される
    ▶ First Fit Decreasing bin-packing approximation algorithm(bin-packing algorithm)という
    アルゴリズムを⽤いて計算される
    ▶ ⼤まかな⼿順は以下
    + Podの要求リソース(CPU+Memory)をスコアリングする
    + スコアを元にサイズの⼤きなもの順にPodをソート
    + ソートされた順番で以下のようにPodのスケジューリングをテスト
    + PredicateCheckerを使⽤してPodをスケジューリング可能なNodeを探す
    + ⾒つかればClusterSnapshot内の対象NodeにPodを追加
    + ⾒つからなければClusterSnapshot内に新規Nodeを作成し、PodをそのNodeに追加する
    + 上記テストを通して新規追加されたNode数を計算する

    View full-size slide

  62. PredicateChecker Overview

    View full-size slide

  63. Expander⼀覧

    View full-size slide

  64. Expander⼀覧
    ▶ random: ランダムにNodeGroupを選択
    ▶ most-pods: スケジューリング可能なunschedulablePodの種類が最も多いNodeGroupを選択
    ▶ least-waste: 対象NodeGroupにスケジューリング可能なunschedulablePodを全てスケジューリングした
    際に、最もNodeに無駄な空き容量が発⽣しないNodeGroupを選択
    ▶ price: 最も費⽤効果が⾼くクラスターの優先Nodeサイズと⼀致するNodeGroupを選択
    + 利⽤するにはCloudProvider側でPricingメソッドの実装が必要
    ▶ priority: ConfigMapで設定した設定ファイルに基づき最も⾼い優先度NodeGroupを選択
    # 設定ファイル例
    apiVersion: v1
    kind: ConfigMap
    metadata:
    name: cluster-autoscaler-priority-expander
    data:
    priorities: |-
    10:
    - .*t2\.large.*
    - .*t3\.large.*
    50:
    - .*m4\.4xlarge.*

    View full-size slide

  65. ClusterStateStrategy

    View full-size slide

  66. ClusterStateStrategy
    ▶ ScaleUp/ScaleDownを⾏うために必要な様々な計算されたデータが⼊ってる
    ▶ また、ScaleUp/ScaleDownの実⾏状態や失敗時のBackoffデータなども管理している

    View full-size slide

  67. ClusterStateStrategy 1
    プロパティ名 説明
    nodes
    型: []*apiv1.Node
    ⼀⾔で説明するとobtainNodeListsが返すallNodesがデータの実体
    allNodesは⼀部加⼯されることはあってもa.AllNodeLister().List()の取得結果ほぼそのまま=すべてのnode⼀覧
    unregisteredNodes
    型: map[string]UnregisteredNode
    csr.cloudProviderNodeInstancesにあってallNodesには無いnode情報
    つまり、実際のk8s nodeには存在しないCloudProviderから取得できるinstance情報
    nodeInfosForGroups
    型: map[string]*schedulerframework.NodeInfo
    core_utils.GetNodeInfosForGroupsが返すnodeInfosForGroupsがデータ実体
    CloudProviderのNodeGroup毎に1つのschedulerframework.NodeInfoを⽣成して設定している
    このNodeInfoはsanitizeNodeInfo/sanitizeTemplateNodeによって様々なtaintを除去している
    perNodeGroupReadiness
    型:map[string]Readiness
    各ステータス別のnode数をカウントしたReadinessをNodeGroup単位で保持している
    Readinessのデータ:
    Deleted: "ToBeDeletedByClusterAutoscaler" taintのあるnode数
    NotStarted: readyではないけどnode.CreationTimestamp+15分を過ぎたnode数
    Registered: allNodesに含まれているnode数
    LongUnregistered: unregisteredNodesに含まれるnodeの内、Cluster AutoscalerにUnregisteredNodeだと判断されてから15分を超
    過したnode数(オプションのmax-node-provision-timeで何分以上をLongUnregisteredに含めるかを設定できる)
    Unregistered: unregisteredNodesに含まれるnodeの内LongUnregisteredではないnode数
    totalReadiness
    型: Readiness
    perNodeGroupReadinessの(NodeGroup別でない)総合計版

    View full-size slide

  68. ClusterStateStrategy 2
    プロパティ名 説明
    cloudProviderNodeInstances
    型: map[string][]cloudprovider.Instance (map[])[]cloudprovider.Instance)
    NodeGroup単位でcache.fetchCloudProviderNodeInstancesForNodeGroupがキャッシュしたInstance⼀覧を格納したデータ
    cloudProviderNodeInstances
    型: map[string][]cloudprovider.Instance
    前回のループ時のcloudProviderNodeInstancesを保存してる
    acceptableRanges
    型: map[string]AcceptableRange
    NodeGroupのTargetSizeの直近での変動数の最⼤/最⼩値を管理するデータ
    AcceptableRangeのデータ:
    MinNodes: 現在~直近で実際に動くであろうnodeの最⼩数 - scaleup予定のノード数は実際にはまだ稼働していないかもしれない
    MaxNodes: 現在~直近で実際に動くであろうnodeの最⼤数 - scaledown予定のノード数はまだ稼働している可能性がある
    CurrentTarget: NodeGroup.TargetSize()の値そのまんま
    incorrectNodeGroupSizes
    型: map[string]IncorrectNodeGroupSize
    NodeGroup単位で、readiness.Registered(k8s nodeに登録されているnode数)がacceptableRange.MaxNodesと
    acceptableRange.MinNodesの間に収まってないものがあれば、それらをincorrectNodeGroupSizesとして保存する
    想定よりも多くなったnodeを削除するために利⽤される

    View full-size slide

  69. ClusterStateStrategy 3
    プロパティ名 説明
    scaleUpRequests
    型: map[string]*ScaleUpRequest(map[]*ScaleUpRequest)
    現在ScaleUpを実⾏中のNodeGroupと追加したnode数(Increase)を管理するデータ
    ScaleUpRequestのデータ:
    NodeGroup: NodeGroupオブジェクト
    Increase: 追加node数
    Time: 追加開始時刻
    ExpectedAddTime: node追加完了期限
    scaleUpFailures
    型: map[string][]ScaleUpFailure(map[][]ScaleUpFailure)
    ScaleUpに失敗したNodeGroupのデータを保存している
    ScaleUpFailureのデータ:
    NodeGroup: NodeGroupオブジェクト
    Reason: 失敗理由
    Time: 失敗確認時刻
    scaleDownRequests
    型: []*ScaleDownRequest
    現在のScaleDownの実⾏情報を管理するデータ
    RegisterScaleDownのデータ:
    NodeGroup: NodeGroupオブジェクト
    NodeName: Node名
    Time: 削除開始時刻
    ExpectedAddTime: node削除完了期限
    backoff
    ScaleUpのbackoff管理オブジェクト
    要はpod起動のbackoffみたいに1度scaleupに失敗したら⼀定以上の間隔を開けてリトライして、更に失敗したら更に実⾏間隔を空け
    て、という制御を⾏うためのプロパティ

    View full-size slide

  70. ScaleDown props

    View full-size slide

  71. ScaleDown props
    ▶ ScaleDownのプロパティにはunneededNodesを始めとしたScaleDownの実⾏と状態管理に
    必要な各種データが保存されている
    ▶ プロパティの多くはUpdateUnneededNodesメソッドによってアップデートされていく

    View full-size slide

  72. ScaleDown props
    プロパティ名 説明
    unneededNodes
    型: map[string]time.Time (map[<初めて追加された時刻>])
    ScaleDown候補のNode⼀覧
    simulator.FindNodesToRemoveによってnodesToRemove(削除可能node)と判断されたnode⼀覧と各nodeが初めて
    unneededNodesに追加された時刻が⼊る
    unneededNodesList
    型: []*apiv1.Node
    simulator.FindNodesToRemoveによってnodesToRemove(削除可能node)と判断されたnode⼀覧が設定される
    unremovableNodes
    型: map[string]time.Time (map[])
    何らかの理由で削除不可だと判断されたnode⼀覧とttlを管理している
    ttlを過ぎるまでは削除されず、checkNodeUtilizationでsd.unremovableNodesにあるnodeは削除不能状態にあるノードだというエ
    ラーが返る
    要するにttlを設けることで短期間で連続で対象nodeがunremovableであるかどうかを毎回チェックするのを避けるための仕組みだ
    と考えられる
    unremovableNodeReasons
    map[string]*simulator.UnremovableNode
    各種チェックによって、何らかの理由で削除不可だと判断されたnodeの⼀覧と各nodeの削除不可だと判断された理由などを管理し
    ている
    podLocationHints
    型: map[string]string (map[]<findPlaceForによって選ばれた次のスケジューリング候補node名>)
    FindNodesToRemove/findPlaceForによって対象nodeを削除した際に、そのnode内部で動いていた再スケジューリングが必要な
    podが動作する次のnode候補はどれか?というがを詰め込まれている
    nodeUtilizationMap
    型: map[string]simulator.UtilizationInfo (map[nodename]UtilizationInfo)
    node毎にcheckNodeUtilizationで計算したUtilizationInfoが⼊っている

    View full-size slide

  73. ScaleUp/Down ざっくりフロー

    View full-size slide

  74. ざっくりフロー ~ 初期化処理編 1
    1. 各種データの初期化
    2. Node⼀覧(all/ready)の取得
    3. Nodeが空なら処理中断する
    4. CloudProviderをRefresh
    5. 優先度が設定されていない or ⼀定値以下のスケジュール済Pod⼀覧を詰め込んだ
    ClusterSnapshotを⽣成(スケジューリングなどのシュミレートに使⽤)
    6. ClusterStateRegistryの各種データアップデート
    7. CloudProvider側のInstance情報には存在して、k8s Nodeには存在しない
    Node(unregisteredNodes)が登録から⼀定時間経過していれば、そのNodeを
    削除して処理中断する
    + ⻑期間unregisteredNodesである=Nodeのprovisioningに失敗したとみなされたので削除される

    View full-size slide

  75. ざっくりフロー ~ 初期化処理編 2
    8. ClusterStateRegistryにUnreadyであると判断されたNode数が⼀定数を超えた場合はCluster
    がUnhealthyだと判断された処理中断する
    9. Node作成中にCloudProviderからエラーだと判断されたInstanceがあった場合、それらの
    Instanceを削除
    10. Cloud Providerの各NodeGroupのInstance数がNodeGroupの想定サイズ内に収まっていない
    場合、Instance数をNodeGroupの想定サイズに収まるように台数削減を⾏い、処理中断する
    11. unschedulableなPod⼀覧の中で、preemptionによってNodeから追い出され、次の起動候補
    Node(nominatedNode)がされているPodをClusterSnapshot内のnominatedNodeに追加
    12. これから起動予定のNode(UpcomingNode)と、そのNode内で起動が決まってる
    Pod(Mirror PodやDaemonset Podなど)をClusterSnapshotに追加

    View full-size slide

  76. ざっくりフロー ~ ScaleUp実施判定編
    1. unschedulableなPodが無ければScaleUpをスキップ
    2. readyなNode数が”--max-nodes-total”オプションで設定した数を超過していた場合は
    ScaleUpをスキップ
    + ”—max-nodes-total”はNodeの最⼤数なので、これを超過していた場合はScaleUpしない
    3. unschedulableなPodがすべて2秒以内に作成されたものであったならScaleUpをスキップ
    + 恐らくまだSchedulerによってNodeを割り当てられていなかったりするためにスキップして
    ると思われる
    4. 上記チェックをパスした場合はScaleUp処理を開始する

    View full-size slide

  77. ざっくりフロー ~ ScaleUp編 1
    1. NodeGroup⼀覧に対して、以下のチェックを⾏いScaleUp候補となるNodeGroupの
    フィルタリングを⾏う
    a. NodeGroupがScaleUp可能な状態であるか?
    b. 現在のNodeGroupのInstance数がNodeGroupの最⼤値に達しているか?
    c. 1台以上NodeをScaleUpすることで、NodeGroupごとに消費可能なリソース量(GPU/CPU/
    Memory)の最⼤値を超過するか?
    + 消費可能なリソース量というのは”—memory-total”などのオプションで設定される

    View full-size slide

  78. ざっくりフロー ~ ScaleUp編 2
    2. フィルタを通過したNodeGroupが作成予定のNodeに対して以下のシュミレートを⾏う
    a. Scheduling FrameworkのPreFilter/Filterを利⽤して、作成予定Nodeにスケジューリング可
    能なunschedulablePod⼀覧のシュミレート
    b. First Fit Decreasing bin-packing approximation algorithmを⽤いたPodのスケジューリング
    に必要なNode数のシュミレート
    3. シュミレートした結果、1つ以上のPodがスケジューリング可能であれば、ScaleUp対象の
    NodeGroup候補に加える

    View full-size slide

  79. ざっくりフロー ~ ScaleUp編 3
    4. “--expander”オプションにより設定されたアルゴリズムにより、NodeGroup候補より1つの
    NodeGroupを選定
    + 選択可能なexpanderはrandom/most-pods/least-waste/price/priorityの5つ
    5. 選ばれたNodeGroupでシュミレートした追加Node数を追加した結果、”--max-nodes-total”
    オプションで設定した数を超過する場合にはSaleUp処理を中断する
    6. “--balance-similar-node-groups”オプションをtrueに設定していた場合、以下の条件を満たす
    NodeGroupが類似NodeGroupとしてScaleUp対象のNodeGroupに追加される
    a. 選定されたNodeGroupとリソース容量が類似してる
    b. (⼀部を除いた)ラベル情報が⼀致してる
    c. 選定されたNodeGroupにスケジューリング可能なPodをすべてスケジューリングできる
    d. NodeGroupがScaleUp可能な状態である

    View full-size slide

  80. ざっくりフロー ~ ScaleUp編 4
    7. 選定されたNodeGroup群とトータルで追加したいNode台数情報を元にどのNodeGroupにそ
    れぞれ何台のNodeを追加するのか?を決定する
    8. NodeGroup毎にScaleUp処理を実⾏する
    9. ScaleUpがすべて成功したら処理終了(ScaleDown判定処理には移⾏しない)

    View full-size slide

  81. ざっくりフロー ~ ScaleDown準備編 1
    1. ScaleDown機能が有効であれば、ScaleDown処理を開始する
    2. 全てのNodeから以下の条件を満たすScaleDown候補Nodeリストを作成する
    a. NodeGroupに所属しているNodeであり
    b. 対象NodeGroupの現在のNode数がNodeGroupの最⼩値に達していないこと
    3. ScaleDown候補Nodeリストを更に以下の条件でフィルタリングする
    a. ”ToBeDeletedTaint”Taintを持っていたら除外
    b. “cluster-autoscaler.kubernetes.io/scale-down-disabled”=true annotationを持っていたら
    除外
    c. Nodeリソース(GPU or CPU or Mem)の中で最も使⽤率の⾼いリソースの使⽤率が⼀定値を超
    えていたら除外

    View full-size slide

  82. ざっくりフロー ~ ScaleDown準備編 2
    4. フィルタリングしたScaleDown候補から以下の条件を満たすemptyNode⼀覧を⽣成
    + emptyNodeとは(おおまかにいうと)以下の条件を満たすpodが1つも無いNodeのこと
    a. Mirro Podではない
    b. DaemonSet Podではない
    c. ReplicaSetやJobなどに紐付いていない
    d. 削除中のPodではない
    e. Terminal State Podではない
    f. HostPathまたはEmptyDirのボリューム(=LocalVolume)を使⽤していない
    g. Podに紐づくPDBがある場合、紐づくPDBのpdb.Status.DisruptionsAllowedが1以上であること
    h. etc (複雑過ぎて書ききれない)
    5. ScaleDown候補NodeをemptyNodeと⾮emptyNodeに分割

    View full-size slide

  83. ざっくりフロー ~ ScaleDown準備編 3
    6. ⾮emptyNodeを更に、前回のScaleDown処理で既にunneededNodesに選ばれたものかどうかで
    2つに分割
    7. 既にunneededNodesに選ばれていた⾮emptyNodeを対象に以下の条件で更にNodeを絞り込む
    a. 対象Nodeの削除をブロッキングするPodは無いか?
    b. 対象Nodeで現在動作しているPodの中で再スケジューリングが必要なPodが全て他のNodeに
    再スケジューリングできるか?
    + 再スケジューリングできるかはScheduling Framework PreFilter/Filterによって検証する
    8. まだunneededNodesに選ばれてない⾮emptyNodeの⼀部を対象に上記と同様のNodeの絞り込み
    をする

    View full-size slide

  84. ざっくりフロー ~ ScaleDown準備編 4
    9. 以下の3種類のNodeをunneededNodesに設定する
    a. emptyNode
    b. 既にunneededNodesに選ばれていた⾮emptyNodeの中で前ページ条件で絞り込んだもの
    c. まだunneededNodesに選ばれてない⾮emptyNodeの⼀部で前ページ条件で絞り込んだもの

    View full-size slide

  85. ざっくりフロー ~ ScaleDown実施判定編
    1. unschedulableなPodがすべて2秒以内に作成されたものであったならScaleDownをスキップ
    2. 前回のScaleUp実施から⼀定時間内であればスキップ
    3. 前回のScaleDown失敗から⼀定時間内であればスキップ
    4. 前回のScaleDown処理開始から⼀定時間内であればスキップ
    5. 現在ScaleDown処理実⾏中であればスキップ
    6. それ以外ならScaleDown処理を実施

    View full-size slide

  86. ざっくりフロー ~ ScaleDown編 1
    1. unneededNodesを更に以下の条件で絞り込む
    a. ready statusでかつunneededNodesに追加されてから⼀定時間が経過したNode
    b. unready statusでかつunneededNodesに追加されてから⼀定時間が経過したNode
    c. Nodeが所属するNodeGroupの現在の数-対象NodeGroupで現在削除中のNode数が
    NodeGroupの最⼩値を下回っていないNode
    2. 絞り込んだunneededNodesからemptyNodesを⽣成
    3. emptyNodesが存在すれば全てのemptyNodesの削除を実施
    + 削除処理はNode単位でgo routineを利⽤して⾮同期に⾏う
    + 削除前にNodeに”ToBeDeletedTaint”Taintを付与する
    4. 削除開始したemptyNodesが1つ以上あった場合はScaleDown処理を終了する

    View full-size slide

  87. ざっくりフロー ~ ScaleDown編 2
    5. emptyNodesの削除が⾏われなかった場合は、⾮emptyNodesの削除処理を⾏う
    6. ⾮emptyNodesの中から1つのNodeを取り出しemptyNodesの際と同様にgo routineで⾮同期
    での削除を開始する
    7. もし、ScaleDownを開始するNodeが1つもなかった場合はunneededNodesに対して
    "DeletionCandidateOfClusterAutoscaler"Taintの付与を⾏う

    View full-size slide

  88. 補⾜
    ▶ ⼀応最低限のわかりやすさを保てる範囲でフローを書いたつもりです
    ▶ ただ実際にはこの説明の10倍くらい複雑で、⽂章で説明しようとすると
    「コード読んだ⽅が正確じゃね?」となってしまうのでこれ以上細かく説明できなさそうでした

    View full-size slide