Skip to content

Commit 343dd75

Browse files
authored
Merge pull request kubernetes#3629 from shapeblue/add-acs
Adding support for Apache CloudStack as a cloudprovider
2 parents da455d5 + 2d8cd41 commit 343dd75

20 files changed

+2203
-0
lines changed

cluster-autoscaler/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ You should also take a look at the notes and "gotchas" for your specific cloud p
1616
* [Azure](./cloudprovider/azure/README.md)
1717
* [AWS](./cloudprovider/aws/README.md)
1818
* [BaiduCloud](./cloudprovider/baiducloud/README.md)
19+
* [CloudStack](./cloudprovider/cloudstack/README.md)
1920
* [HuaweiCloud](./cloudprovider/huaweicloud/README.md)
2021
* [Packet](./cloudprovider/packet/README.md#notes)
2122

@@ -144,5 +145,6 @@ Supported cloud providers:
144145
* Alibaba Cloud https://github.com/kubernetes/autoscaler/blob/master/cluster-autoscaler/cloudprovider/alicloud/README.md
145146
* OpenStack Magnum https://github.com/kubernetes/autoscaler/blob/master/cluster-autoscaler/cloudprovider/magnum/README.md
146147
* DigitalOcean https://github.com/kubernetes/autoscaler/blob/master/cluster-autoscaler/cloudprovider/digitalocean/README.md
148+
* CloudStack https://github.com/kubernetes/autoscaler/blob/master/cluster-autoscaler/cloudprovider/cloudstack/README.md
147149
* Exoscale https://github.com/kubernetes/autoscaler/blob/master/cluster-autoscaler/cloudprovider/exoscale/README.md
148150
* Packet https://github.com/kubernetes/autoscaler/blob/master/cluster-autoscaler/cloudprovider/packet/README.md

cluster-autoscaler/cloudprovider/builder/builder_all.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"k8s.io/autoscaler/cluster-autoscaler/cloudprovider/aws"
2525
"k8s.io/autoscaler/cluster-autoscaler/cloudprovider/azure"
2626
"k8s.io/autoscaler/cluster-autoscaler/cloudprovider/baiducloud"
27+
"k8s.io/autoscaler/cluster-autoscaler/cloudprovider/cloudstack"
2728
"k8s.io/autoscaler/cluster-autoscaler/cloudprovider/clusterapi"
2829
"k8s.io/autoscaler/cluster-autoscaler/cloudprovider/digitalocean"
2930
"k8s.io/autoscaler/cluster-autoscaler/cloudprovider/exoscale"
@@ -40,6 +41,7 @@ var AvailableCloudProviders = []string{
4041
cloudprovider.AzureProviderName,
4142
cloudprovider.GceProviderName,
4243
cloudprovider.AlicloudProviderName,
44+
cloudprovider.CloudStackProviderName,
4345
cloudprovider.BaiducloudProviderName,
4446
cloudprovider.MagnumProviderName,
4547
cloudprovider.DigitalOceanProviderName,
@@ -61,6 +63,8 @@ func buildCloudProvider(opts config.AutoscalingOptions, do cloudprovider.NodeGro
6163
return azure.BuildAzure(opts, do, rl)
6264
case cloudprovider.AlicloudProviderName:
6365
return alicloud.BuildAlicloud(opts, do, rl)
66+
case cloudprovider.CloudStackProviderName:
67+
return cloudstack.BuildCloudStack(opts, do, rl)
6468
case cloudprovider.BaiducloudProviderName:
6569
return baiducloud.BuildBaiducloud(opts, do, rl)
6670
case cloudprovider.DigitalOceanProviderName:
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// +build cloudstack
2+
3+
/*
4+
Copyright 2020 The Kubernetes Authors.
5+
6+
Licensed under the Apache License, Version 2.0 (the "License");
7+
you may not use this file except in compliance with the License.
8+
You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing, software
13+
distributed under the License is distributed on an "AS IS" BASIS,
14+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
See the License for the specific language governing permissions and
16+
limitations under the License.
17+
*/
18+
19+
package builder
20+
21+
import (
22+
"k8s.io/autoscaler/cluster-autoscaler/cloudprovider"
23+
"k8s.io/autoscaler/cluster-autoscaler/cloudprovider/cloudstack"
24+
"k8s.io/autoscaler/cluster-autoscaler/config"
25+
)
26+
27+
// AvailableCloudProviders supported by the cloud provider builder.
28+
var AvailableCloudProviders = []string{
29+
cloudprovider.CloudStackProviderName,
30+
}
31+
32+
// DefaultCloudProvider for cloudstack-only build is cloudstack.
33+
const DefaultCloudProvider = cloudprovider.CloudStackProviderName
34+
35+
func BuildCloudStack(opts config.AutoscalingOptions, do cloudprovider.NodeGroupDiscoveryOptions, rl *cloudprovider.ResourceLimiter) cloudprovider.CloudProvider {
36+
switch opts.CloudProviderName {
37+
case cloudprovider.CloudStackProviderName:
38+
return cloudstack.BuildCloudStack(opts, do, rl)
39+
}
40+
41+
return nil
42+
}

cluster-autoscaler/cloudprovider/cloud_provider.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ const (
3535
AwsProviderName = "aws"
3636
// BaiducloudProviderName gets the provider name of baiducloud
3737
BaiducloudProviderName = "baiducloud"
38+
// CloudStackProviderName gets the provider name of cloudstack
39+
CloudStackProviderName = "cloudstack"
3840
// DigitalOceanProviderName gets the provider name of digitalocean
3941
DigitalOceanProviderName = "digitalocean"
4042
// ExoscaleProviderName gets the provider name of exoscale
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
maintainers:
2+
- davidjumani
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Cluster Autoscaler on CloudStack
2+
The cluster autoscaler on CloudStack scales worker nodes within any specified cluster. It runs as a `Deployment` in your cluster.
3+
This README will go over some of the necessary steps required to get the cluster autoscaler up and running.
4+
5+
## Requirements
6+
Cluster Autoscaler requires Apache CloudStack 4.16 onward as well as Kubernetes v1.16.0 or greater.
7+
8+
## Deployment
9+
The CloudStack kubernetes cluster can be autoscaled via the `scaleKubernetesCluster` by passing `autoscalingenabled=true` along with
10+
the `minsize` and `maxsize` parameters API from 4.16 onwards. eg:
11+
```
12+
scaleKubernetesCluster id=<cluster-id> autoscalingenabled=true minsize=<minsize> maxsize=<maxsize>
13+
```
14+
Autoscaling on the cluster can be disabled by passing `autoscalingenabled=false`. This will delete the deployment and leave the cluster
15+
at its current size. eg:
16+
```
17+
scaleKubernetesCluster id=<cluster-id> autoscalingenabled=false
18+
```
19+
20+
To manually deploy the cluster autoscaler, please follow the guide below :
21+
22+
To configure API access to your CloudStack management server, you need to create a secret containing a `cloud-config`
23+
that is suitable for your environment.
24+
25+
`cloud-config` should look like this:
26+
```ini
27+
[Global]
28+
api-url = <CloudStack API URL>
29+
api-key = <CloudStack API Key>
30+
secret-key = <CloudStack API Secret>
31+
```
32+
The access token needs to be able to execute the `listKubernetesClusters` and `scaleKubernetesCluster` APIs.
33+
34+
To create the secret, use the following command:
35+
```bash
36+
kubectl -n kube-system create secret generic cloudstack-secret --from-file=cloud-config
37+
```
38+
39+
Finally, to deploy the autoscaler, modify the `cluster-autoscaler-standard.yaml` with the cluster id, minsize and maxsize located
40+
[here](./examples/cluster-autoscaler-standard.yaml) and execute it
41+
42+
```
43+
kubectl apply -f cluster-autoscaler-standard.yaml
44+
```
45+
46+
## Common Notes and Gotchas:
47+
- The automated deployment of the autoscaler will run with the defaults configured [here](./examples/cluster-autoscaler-standard.yaml). To change it, alter the file and deploy it again.
48+
- By default, cluster autoscaler will not terminate nodes running pods in the kube-system namespace. You can override this default behaviour by passing in the `--skip-nodes-with-system-pods=false` flag.
49+
- By default, cluster autoscaler will wait 10 minutes between scale down operations, you can adjust this using the `--scale-down-delay` flag. E.g. `--scale-down-delay=5m` to decrease the scale down delay to 5 minutes.
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
/*
2+
Copyright 2020 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package cloudstack
18+
19+
import (
20+
"fmt"
21+
"strconv"
22+
"strings"
23+
24+
"k8s.io/apimachinery/pkg/api/resource"
25+
"k8s.io/autoscaler/cluster-autoscaler/cloudprovider"
26+
"k8s.io/autoscaler/cluster-autoscaler/config"
27+
"k8s.io/autoscaler/cluster-autoscaler/utils/errors"
28+
29+
v1 "k8s.io/api/core/v1"
30+
klog "k8s.io/klog/v2"
31+
)
32+
33+
const (
34+
// GPULabel is the label added to nodes with GPU resource.
35+
GPULabel = "accelerator"
36+
)
37+
38+
var (
39+
availableGPUTypes = map[string]struct{}{
40+
"nvidia-tesla-k80": {},
41+
"nvidia-tesla-p100": {},
42+
"nvidia-tesla-v100": {},
43+
}
44+
availableMachineTypes = []string{}
45+
)
46+
47+
// cloudStackCloudProvider implements CloudProvider interface.
48+
type cloudStackCloudProvider struct {
49+
manager *manager
50+
resourceLimiter *cloudprovider.ResourceLimiter
51+
}
52+
53+
// Name returns name of the cloud provider.
54+
func (provider *cloudStackCloudProvider) Name() string {
55+
return cloudprovider.CloudStackProviderName
56+
}
57+
58+
// NodeGroups returns all node groups configured for this cloud provider.
59+
func (provider *cloudStackCloudProvider) NodeGroups() []cloudprovider.NodeGroup {
60+
asg := provider.manager.asg
61+
return []cloudprovider.NodeGroup{asg}
62+
}
63+
64+
// NodeGroupForNode returns the node group for the given node, nil if the node
65+
// should not be processed by cluster autoscaler, or non-nil error if such
66+
// occurred.
67+
func (provider *cloudStackCloudProvider) NodeGroupForNode(node *v1.Node) (cloudprovider.NodeGroup, error) {
68+
return provider.manager.clusterForNode(node)
69+
}
70+
71+
// Cleanup cleans up open resources before the cloud provider is destroyed, i.e. go routines etc.
72+
func (provider *cloudStackCloudProvider) Cleanup() error {
73+
return provider.manager.cleanup()
74+
}
75+
76+
// Refresh is called before every main loop and can be used to dynamically update cloud provider state.
77+
func (provider *cloudStackCloudProvider) Refresh() error {
78+
return provider.manager.refresh()
79+
}
80+
81+
// GetAvailableMachineTypes get all machine types that can be requested from the cloud provider.
82+
func (provider *cloudStackCloudProvider) GetAvailableMachineTypes() ([]string, error) {
83+
return availableMachineTypes, nil
84+
}
85+
86+
// GetResourceLimiter returns struct containing limits (max, min) for resources (cores, memory etc.).
87+
func (provider *cloudStackCloudProvider) GetResourceLimiter() (*cloudprovider.ResourceLimiter, error) {
88+
return provider.resourceLimiter, nil
89+
}
90+
91+
// GPULabel returns the label added to nodes with GPU resource.
92+
func (provider *cloudStackCloudProvider) GPULabel() string {
93+
return GPULabel
94+
}
95+
96+
// GetAvailableGPUTypes return all available GPU types cloud provider supports.
97+
func (provider *cloudStackCloudProvider) GetAvailableGPUTypes() map[string]struct{} {
98+
return availableGPUTypes
99+
}
100+
101+
// Pricing returns pricing model for this cloud provider or error if not available.
102+
func (provider *cloudStackCloudProvider) Pricing() (cloudprovider.PricingModel, errors.AutoscalerError) {
103+
return nil, cloudprovider.ErrNotImplemented
104+
}
105+
106+
// NewNodeGroup builds a theoretical node group based on the node definition provided. The node group is not automatically
107+
// created on the cloud provider side. The node group is not returned by NodeGroups() until it is created.
108+
func (provider *cloudStackCloudProvider) NewNodeGroup(machineType string, labels map[string]string, systemLabels map[string]string, taints []v1.Taint, extraResources map[string]resource.Quantity) (cloudprovider.NodeGroup, error) {
109+
return nil, cloudprovider.ErrNotImplemented
110+
}
111+
112+
func createClusterConfig(opts config.AutoscalingOptions) (*clusterConfig, error) {
113+
if len(opts.NodeGroups) == 0 {
114+
return nil, fmt.Errorf("Cluster details not present. Please use the --nodes=<min>:<max>:<id>")
115+
}
116+
117+
clusterDetails := strings.Split(opts.NodeGroups[0], ":")
118+
if len(clusterDetails) != 3 {
119+
return nil, fmt.Errorf("Cluster details not present. Please use the --nodes=<min>:<max>:<id>")
120+
}
121+
122+
minClusterSize, err := strconv.Atoi(clusterDetails[0])
123+
if err != nil {
124+
return nil, fmt.Errorf("Invalid value for min cluster size %s : %v", clusterDetails[0], err)
125+
}
126+
127+
maxClusterSize, err := strconv.Atoi(clusterDetails[1])
128+
if err != nil {
129+
return nil, fmt.Errorf("Invalid value for max cluster size %s : %v", clusterDetails[1], err)
130+
}
131+
132+
return &clusterConfig{
133+
clusterID: clusterDetails[2],
134+
minSize: minClusterSize,
135+
maxSize: maxClusterSize,
136+
}, nil
137+
}
138+
139+
// BuildCloudStack builds CloudProvider implementation for CloudStack
140+
func BuildCloudStack(opts config.AutoscalingOptions, do cloudprovider.NodeGroupDiscoveryOptions, rl *cloudprovider.ResourceLimiter) cloudprovider.CloudProvider {
141+
142+
config, err := createClusterConfig(opts)
143+
if err != nil {
144+
klog.Fatal(err)
145+
}
146+
147+
manager, err := newManager(config, withConfigFile(opts.CloudConfig))
148+
if err != nil {
149+
klog.Fatalf("Failed to create CloudStack Manager: %v", err)
150+
}
151+
152+
return &cloudStackCloudProvider{
153+
manager: manager,
154+
resourceLimiter: rl,
155+
}
156+
}

0 commit comments

Comments
 (0)