Ubernetes Lite: Volumes can dictate zone scheduling

For AWS EBS, a volume can only be attached to a node in the same AZ.
The scheduler must therefore detect if a volume is being attached to a
pod, and ensure that the pod is scheduled on a node in the same AZ as
the volume.

So that the scheduler need not query the cloud provider every time, and
to support decoupled operation (e.g. bare metal) we tag the volume with
our placement labels.  This is done automatically by means of an
admission controller on AWS when a PersistentVolume is created backed by
an EBS volume.

Support for tagging GCE PVs will follow.

Pods that specify a volume directly (i.e. without using a
PersistentVolumeClaim) will not currently be scheduled correctly (i.e.
they will be scheduled without zone-awareness).
This commit is contained in:
Justin Santa Barbara
2015-11-29 14:00:49 -05:00
parent 7743a4ca89
commit f9a6ac077e
15 changed files with 536 additions and 4 deletions

View File

@@ -45,6 +45,7 @@ import (
"k8s.io/kubernetes/pkg/util/sets"
"github.com/golang/glog"
"k8s.io/kubernetes/pkg/api/unversioned"
)
const ProviderName = "aws"
@@ -158,6 +159,9 @@ type Volumes interface {
// Create a volume with the specified options
CreateVolume(volumeOptions *VolumeOptions) (volumeName string, err error)
DeleteVolume(volumeName string) error
// Get labels to apply to volume on creation
GetVolumeLabels(volumeName string) (map[string]string, error)
}
// InstanceGroups is an interface for managing cloud-managed instance groups / autoscaling instance groups
@@ -510,6 +514,16 @@ func isRegionValid(region string) bool {
return false
}
// Derives the region from a valid az name.
// Returns an error if the az is known invalid (empty)
func azToRegion(az string) (string, error) {
if len(az) < 1 {
return "", fmt.Errorf("invalid (empty) AZ")
}
region := az[:len(az)-1]
return region, nil
}
// newAWSCloud creates a new instance of AWSCloud.
// AWSProvider and instanceId are primarily for tests
func newAWSCloud(config io.Reader, awsServices AWSServices) (*AWSCloud, error) {
@@ -527,7 +541,10 @@ func newAWSCloud(config io.Reader, awsServices AWSServices) (*AWSCloud, error) {
if len(zone) <= 1 {
return nil, fmt.Errorf("invalid AWS zone in config file: %s", zone)
}
regionName := zone[:len(zone)-1]
regionName, err := azToRegion(zone)
if err != nil {
return nil, err
}
valid := isRegionValid(regionName)
if !valid {
@@ -1274,6 +1291,32 @@ func (aws *AWSCloud) DeleteVolume(volumeName string) error {
return awsDisk.deleteVolume()
}
// Implements Volumes.GetVolumeLabels
func (c *AWSCloud) GetVolumeLabels(volumeName string) (map[string]string, error) {
awsDisk, err := newAWSDisk(c, volumeName)
if err != nil {
return nil, err
}
info, err := awsDisk.getInfo()
if err != nil {
return nil, err
}
labels := make(map[string]string)
az := aws.StringValue(info.AvailabilityZone)
if az == "" {
return nil, fmt.Errorf("volume did not have AZ information: %q", info.VolumeId)
}
labels[unversioned.LabelZoneFailureDomain] = az
region, err := azToRegion(az)
if err != nil {
return nil, err
}
labels[unversioned.LabelZoneRegion] = region
return labels, nil
}
func (v *AWSCloud) Configure(name string, spec *api.NodeSpec) error {
return nil
}