kubernetes-sigs/cluster-proportional-autoscaler
ターゲットは
deployment/*, replicationcontroller/* or replicaset/*
のみ
healthz server
サーバー起動プロセス
メイン処理
configmapはsyncしとらんやん
無かったら作っとるだけやん
(これつまり1回作ったら引数変えても内容アップデートしてくれないのでは?)
controllerは2種類
- Linear
- Ladder
うち有効化できるのは1つのみ
ここまで見た限りcluster-proportional-autoscalerは
- 1種類のターゲットに対してのみ
- 1つの設定(Linear or Ladderとそのパラメータ)
だけで動かすもののように思える
それぞれの仕様?はREADMEに詳しめに書いてある
- Linear
- Ladder
それぞれのパラメータは以下
type linearParams struct {
CoresPerReplica float64 `json:"coresPerReplica"`
NodesPerReplica float64 `json:"nodesPerReplica"`
Min int `json:"min"`
Max int `json:"max"`
PreventSinglePointFailure bool `json:"preventSinglePointFailure"`
IncludeUnschedulableNodes bool `json:"includeUnschedulableNodes"`
}
type ladderParams struct {
CoresToReplicas paramEntries `json:"coresToReplicas"`
NodesToReplicas paramEntries `json:"nodesToReplicas"`
}
clusterstatus生成方法
coreについてはnode.Status.Allocatableから算出
SchedulableNodesは!node.Spec.Unschedulableなnodeの総数
type ClusterStatus struct {
TotalNodes int32
SchedulableNodes int32
TotalCores int32
SchedulableCores int32
}
clusterStatus = &ClusterStatus{}
clusterStatus.TotalNodes = int32(len(nodes))
var tc resource.Quantity
var sc resource.Quantity
for i := range nodes {
node, ok := nodes[i].(*v1.Node)
if !ok {
glog.Errorf("Unexpected object: %#v", nodes[i])
continue
}
tc.Add(node.Status.Allocatable[v1.ResourceCPU])
if !node.Spec.Unschedulable {
clusterStatus.SchedulableNodes++
sc.Add(node.Status.Allocatable[v1.ResourceCPU])
}
}
clusterStatus.TotalCores = int32(tc.Value())
clusterStatus.SchedulableCores = int32(sc.Value())
ちなみに引数のnodelabelで対象ノードを絞り込める
LadderController
READMEの説明で十分じゃない?という気がするけど一応
要約:
data:
ladder: |-
{
"coresToReplicas":
[
[ 1, 1 ],
[ 64, 3 ],
[ 512, 5 ],
[ 1024, 7 ],
[ 2048, 10 ],
[ 4096, 15 ]
],
"nodesToReplicas":
[
[ 1, 1 ],
[ 2, 2 ]
]
}
のような設定を与えてcores/nodesのサイズを満たしたreplicasを返す
設定データの読み方は
[<cores or nodes>, <replicas>]
なので例えばnodeが2台ならnodesToReplicasの [ 2, 2 ]
がマッチしてrelicasが2となる
(実際にはcoresToReplicasの方の条件も考慮されるけど)
以下コード
コアとnodeそれぞれで計算してデカイ方を返す
func (c *LadderController) getExpectedReplicasFromParams(schedulableNodes, schedulableCores int) int {
replicasFromCore := getExpectedReplicasFromEntries(schedulableCores, c.params.CoresToReplicas)
replicasFromNode := getExpectedReplicasFromEntries(schedulableNodes, c.params.NodesToReplicas)
// Returns the results which yields the most replicas
if replicasFromCore > replicasFromNode {
return replicasFromCore
}
return replicasFromNode
}
- replicasFromCore
- replicasFromNode
それぞれの計算処理はこんなん
func getExpectedReplicasFromEntries(schedulableResources int, entries []paramEntry) int {
if len(entries) == 0 {
return 0
}
// Binary search for the corresponding replicas number
pos := sort.Search(
len(entries),
func(i int) bool {
return schedulableResources < entries[i][0]
})
if pos > 0 {
pos = pos - 1
}
return entries[pos][1]
}
sort.Searchはこちら
↑でやってる二分探索(バイナリサーチ)はこちら
この引数で渡されるパラメータはこの場所でソート済
sort.Sort(params.CoresToReplicas)
sort.Sort(params.NodesToReplicas)
entriesは
- node or core
- replica
というデータ形式なので、例えば
schedulableNode: 3
に対して
-
node: 2, replica: 2 -> x
-
node: 3, replica: 3 -> o
という感じでnode: 3のreplica: 3が返されるシンプルなもののよう
LinearController
要約:
data:
linear: |-
{
"coresPerReplica": 2,
"nodesPerReplica": 1,
"min": 1,
"max": 100,
"preventSinglePointFailure": true,
"includeUnschedulableNodes": true
}
のような設定値を元に動作させる
要は
- coresPerReplica: コア数あたりでreplica数を決める(2に対して実際のcpuが6ならreplicaは3になる)
- nodesPerReplica: node数あたりでreplica数を決める(↑に同じく)
- min: coresPerReplica/nodesPerReplicaでreplica数を決める際の最低数
- max: coresPerReplica/nodesPerReplicaでreplica数を決める際の最大数
- preventSinglePointFailure: trueにするとnode数が2以上の時に、replica数の最低値を自動的に2にする(preventSinglePointFailureの名前の通り複数台にすることで単一障害点にしないように、ということだと思う)
- includeUnschedulableNodes: trueにするとunschedulableなnodeも計算の際のコア数/node数に含める
という設定値を元に動作して、これらを元に
- コア数
- node数
それぞれでreplica数を計算して、サイズの大きい方を返す
例えば↑の設定値に対して
- コア数: 16
- node数: 4
だとしたら
- replica数(コア): 8(16/2)
- replica数(node): 4(4/1)
でコア数で算出したreplica数の方が多くなるから最終的なreplica数は8になる
コード
includeUnschedulableNodesによるcores/nodes設定箇所
nodes := schedulableNodes
cores := schedulableCores
if c.params.IncludeUnschedulableNodes {
nodes = totalNodes
cores = totalCores
}
replicasFromCore/replicasFromNode算出
replicasFromCore := c.getExpectedReplicasFromParam(cores, c.params.CoresPerReplica)
replicasFromNode := c.getExpectedReplicasFromParam(nodes, c.params.NodesPerReplica)
func (c *LinearController) getExpectedReplicasFromParam(schedulableResources int, resourcesPerReplica float64) int {
if resourcesPerReplica == 0 {
return 1
}
res := math.Ceil(float64(schedulableResources) / resourcesPerReplica)
if c.params.Max != 0 {
res = math.Min(float64(c.params.Max), res)
}
return int(math.Max(float64(c.params.Min), res))
}
PreventSinglePointFailureによるreplica数調整
// Prevent single point of failure by having at least 2 replicas when
// there are more than one node.
if c.params.PreventSinglePointFailure &&
nodes > 1 &&
replicasFromNode < 2 {
replicasFromNode = 2
}
最終的に大きい方を返す処理
// Returns the results which yields the most replicas
if replicasFromCore > replicasFromNode {
return replicasFromCore
}
return replicasFromNode