Skip to content

Commit 644f705

Browse files
committed
data/aws/vpc: Only create subnet infrastucture for zones with Machine(Set)s
This commit updates our Terraform variables to include the worker subnets, and then switches on that (and the master zones) in Terraform to avoid creating subnet infrastructure (NAT gateways, routes, etc.) in zones that have no Machine(Set)s. This helps address limit issues in high-zone regions like us-east-1, as seen in the limits.md change. Note that without a reduction in our default MachineSet creation, the installer defaults will still not work on us-east-1 without a limit bump. The drawback is that users are now on the hook to provision their own subnets in other zones if they decide that they want to grow into a new zone as a day-2 Machine(Set) operation. For now, they'll have to provide their own infrastructure for that, and our user-provided-infrastructure docs should give them sufficient grounding to do so. It's possible that in the future the machine-API or other infrastructure operator could dynamically provision subnets in zones that were not populated at install-time, but I can't hazard a guess as to how likely that will be. The HCL functions for combining the zone lists are documented in [1,2]. [1]: https://www.terraform.io/docs/configuration-0-11/interpolation.html#concat-list1-list2- [2]: https://www.terraform.io/docs/configuration-0-11/interpolation.html#distinct-list-
1 parent 6da4cbe commit 644f705

File tree

10 files changed

+82
-54
lines changed

10 files changed

+82
-54
lines changed

data/data/aws/main.tf

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,10 @@ module "dns" {
7272
module "vpc" {
7373
source = "./vpc"
7474

75-
cidr_block = "${var.machine_cidr}"
76-
cluster_id = "${var.cluster_id}"
77-
region = "${var.aws_region}"
75+
cidr_block = "${var.machine_cidr}"
76+
cluster_id = "${var.cluster_id}"
77+
region = "${var.aws_region}"
78+
availability_zones = "${distinct(concat(var.aws_master_availability_zones, var.aws_worker_availability_zones))}"
7879

7980
tags = "${local.tags}"
8081
}

data/data/aws/variables-aws.tf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,8 @@ variable "aws_master_availability_zones" {
6262
type = "list"
6363
description = "The availability zones in which to create the masters. The length of this list must match master_count."
6464
}
65+
66+
variable "aws_worker_availability_zones" {
67+
type = "list"
68+
description = "The availability zones to provision for workers. Worker instances are created by the machine-API operator, but this variable controls their supporting infrastructure (subnets, routing, etc.)."
69+
}

data/data/aws/vpc/common.tf

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,10 @@
11
# Canonical internal state definitions for this module.
22
# read only: only locals and data source definitions allowed. No resources or module blocks in this file
3-
// Fetch a list of available AZs
4-
data "aws_availability_zones" "azs" {
5-
state = "available"
6-
}
73

8-
// Only reference data sources which are gauranteed to exist at any time (above) in this locals{} block
4+
// Only reference data sources which are guaranteed to exist at any time (above) in this locals{} block
95
locals {
10-
// List of possible AZs for each type of subnet
11-
new_subnet_azs = "${data.aws_availability_zones.azs.names}"
12-
136
// How many AZs to create subnets in
14-
new_az_count = "${length(local.new_subnet_azs)}"
7+
new_az_count = "${length(var.availability_zones)}"
158

169
// The VPC ID to use to build the rest of the vpc data sources
1710
vpc_id = "${aws_vpc.new_vpc.id}"

data/data/aws/vpc/outputs.tf

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ output "vpc_id" {
33
}
44

55
output "az_to_private_subnet_id" {
6-
value = "${zipmap(local.new_subnet_azs, local.private_subnet_ids)}"
6+
value = "${zipmap(var.availability_zones, local.private_subnet_ids)}"
77
}
88

99
output "az_to_public_subnet_id" {
10-
value = "${zipmap(local.new_subnet_azs, local.public_subnet_ids)}"
10+
value = "${zipmap(var.availability_zones, local.public_subnet_ids)}"
1111
}
1212

1313
output "public_subnet_ids" {

data/data/aws/vpc/variables.tf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
variable "availability_zones" {
2+
type = "list"
3+
description = "The availability zones in which to provision subnets."
4+
}
5+
16
variable "cidr_block" {
27
type = "string"
38
}

data/data/aws/vpc/vpc-private.tf

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ resource "aws_route_table" "private_routes" {
33
vpc_id = "${data.aws_vpc.cluster_vpc.id}"
44

55
tags = "${merge(map(
6-
"Name","${var.cluster_id}-private-${local.new_subnet_azs[count.index]}",
6+
"Name","${var.cluster_id}-private-${var.availability_zones[count.index]}",
77
), var.tags)}"
88
}
99

@@ -22,10 +22,10 @@ resource "aws_subnet" "private_subnet" {
2222

2323
cidr_block = "${cidrsubnet(local.new_private_cidr_range, 3, count.index)}"
2424

25-
availability_zone = "${local.new_subnet_azs[count.index]}"
25+
availability_zone = "${var.availability_zones[count.index]}"
2626

2727
tags = "${merge(map(
28-
"Name", "${var.cluster_id}-private-${local.new_subnet_azs[count.index]}",
28+
"Name", "${var.cluster_id}-private-${var.availability_zones[count.index]}",
2929
"kubernetes.io/role/internal-elb", "",
3030
), var.tags)}"
3131
}

data/data/aws/vpc/vpc-public.tf

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ resource "aws_subnet" "public_subnet" {
3131

3232
cidr_block = "${cidrsubnet(local.new_public_cidr_range, 3, count.index)}"
3333

34-
availability_zone = "${local.new_subnet_azs[count.index]}"
34+
availability_zone = "${var.availability_zones[count.index]}"
3535

3636
tags = "${merge(map(
37-
"Name", "${var.cluster_id}-public-${local.new_subnet_azs[count.index]}",
37+
"Name", "${var.cluster_id}-public-${var.availability_zones[count.index]}",
3838
), var.tags)}"
3939
}
4040

@@ -49,7 +49,7 @@ resource "aws_eip" "nat_eip" {
4949
vpc = true
5050

5151
tags = "${merge(map(
52-
"Name", "${var.cluster_id}-eip-${local.new_subnet_azs[count.index]}",
52+
"Name", "${var.cluster_id}-eip-${var.availability_zones[count.index]}",
5353
), var.tags)}"
5454

5555
# Terraform does not declare an explicit dependency towards the internet gateway.
@@ -64,6 +64,6 @@ resource "aws_nat_gateway" "nat_gw" {
6464
subnet_id = "${aws_subnet.public_subnet.*.id[count.index]}"
6565

6666
tags = "${merge(map(
67-
"Name", "${var.cluster_id}-nat-${local.new_subnet_azs[count.index]}",
67+
"Name", "${var.cluster_id}-nat-${var.availability_zones[count.index]}",
6868
), var.tags)}"
6969
}

docs/user/aws/limits.md

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,25 +23,27 @@ limit.
2323

2424
## Elastic Network Interfaces (ENI)
2525

26-
The default installation creates 21 + the number of availability zones of ENIs (e.g. us-east-1 = 21 + 6 = 27 ENIs).
26+
The default installation creates 21 + the number of availability zones of ENIs (e.g. 21 + 3 = 24 ENIs for a three-zone cluster).
2727
The default limit per region is 350. Additional ENIs are created for additional machines and elastic load balancers
2828
created by cluster usage and deployed workloads. A service limit increase here may be required to satisfy the needs of
2929
additional clusters and deployed workloads.
3030

3131
## Elastic IP (EIP)
3232

33-
For a single, default cluster, your account will have the needed capacity limits required. There is one exception,
34-
"EC2-VPC Elastic IPs". The installer creates a public and private subnet for each
35-
[availability zone within a region][availability-zones] to provision the cluster in a highly available configuration. In
36-
each private subnet, a separate [NAT Gateway][nat-gateways] is created and requires a separate [elastic IP][elastic-ip].
37-
The default limit of 5 is sufficient for most regions and a single cluster. For the us-east-1 region, a higher limit is
38-
required. For multiple clusters, a higher limit is required. Please see [this map][az-map] for a current region map with
39-
availability zone count. We recommend selecting regions with 3 or more availability zones.
33+
By default, the installer distributes control-plane and compute machines across [all availability zones within a region][availability-zones] to provision the cluster in a highly available configuration.
34+
Please see [this map][az-map] for a current region map with availability zone count.
35+
We recommend selecting regions with 3 or more availability zones.
36+
You can [provide an install-config](../overview.md#multiple-invocations) to [configure](customization.md) the installer to use specific zones to override that default.
4037

41-
### Example: Using N. Virginia (us-east-1)
38+
The installer creates a public and private subnet for each configured availability zone.
39+
In each private subnet, a separate [NAT Gateway][nat-gateways] is created and requires a separate [EC2-VPC Elastic IP (EIP)][elastic-ip].
40+
The default limit of 5 is sufficient for a single cluster, unless you have configured your cluster to use more than five zones.
41+
For multiple clusters, a higher limit will likely be required (and will certainly be required to support more than five clusters, even if they are each single-zone clusters).
4242

43-
To use N. Virginia (us-east-1) for a new cluster, please submit a limit increase for VPC Elastic IPs similar to the
44-
following in the support dashboard (to create more than one cluster, a higher limit will be necessary):
43+
### Example: Using North Virginia (us-east-1)
44+
45+
North Virginia (us-east-1) has six availablity zones, so a higher limit is required unless you configure your cluster to use fewer zones.
46+
To support the default, all-zone installation, please submit a limit increase for VPC Elastic IPs similar to the following in the support dashboard (to create more than one cluster, a higher limit will be necessary):
4547

4648
![Increase Elastic IP limit in AWS](images/support_increase_elastic_ip.png)
4749

pkg/asset/cluster/tfvars.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ func (t *TerraformVariables) Dependencies() []asset.Asset {
6161
&bootstrap.Bootstrap{},
6262
&machine.Master{},
6363
&machines.Master{},
64+
&machines.Worker{},
6465
}
6566
}
6667

@@ -71,8 +72,9 @@ func (t *TerraformVariables) Generate(parents asset.Parents) error {
7172
bootstrapIgnAsset := &bootstrap.Bootstrap{}
7273
masterIgnAsset := &machine.Master{}
7374
mastersAsset := &machines.Master{}
75+
workersAsset := &machines.Worker{}
7476
rhcosImage := new(rhcos.Image)
75-
parents.Get(clusterID, installConfig, bootstrapIgnAsset, masterIgnAsset, mastersAsset, rhcosImage)
77+
parents.Get(clusterID, installConfig, bootstrapIgnAsset, masterIgnAsset, mastersAsset, workersAsset, rhcosImage)
7678

7779
platform := installConfig.Config.Platform.Name()
7880
switch platform {
@@ -117,7 +119,15 @@ func (t *TerraformVariables) Generate(parents asset.Parents) error {
117119
for i, m := range masters {
118120
masterConfigs[i] = m.Spec.ProviderSpec.Value.Object.(*awsprovider.AWSMachineProviderConfig)
119121
}
120-
data, err := awstfvars.TFVars(masterConfigs)
122+
workers, err := workersAsset.MachineSets()
123+
if err != nil {
124+
return err
125+
}
126+
workerConfigs := make([]*awsprovider.AWSMachineProviderConfig, len(workers))
127+
for i, m := range workers {
128+
workerConfigs[i] = m.Spec.Template.Spec.ProviderSpec.Value.Object.(*awsprovider.AWSMachineProviderConfig)
129+
}
130+
data, err := awstfvars.TFVars(masterConfigs, workerConfigs)
121131
if err != nil {
122132
return errors.Wrapf(err, "failed to get %s Terraform variables", platform)
123133
}

pkg/tfvars/aws/aws.go

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,29 +11,40 @@ import (
1111
)
1212

1313
type config struct {
14-
AMI string `json:"aws_ami"`
15-
ExtraTags map[string]string `json:"aws_extra_tags,omitempty"`
16-
BootstrapInstanceType string `json:"aws_bootstrap_instance_type,omitempty"`
17-
MasterInstanceType string `json:"aws_master_instance_type,omitempty"`
18-
AvailabilityZones []string `json:"aws_master_availability_zones"`
19-
IOPS int64 `json:"aws_master_root_volume_iops"`
20-
Size int64 `json:"aws_master_root_volume_size,omitempty"`
21-
Type string `json:"aws_master_root_volume_type,omitempty"`
22-
Region string `json:"aws_region,omitempty"`
14+
AMI string `json:"aws_ami"`
15+
ExtraTags map[string]string `json:"aws_extra_tags,omitempty"`
16+
BootstrapInstanceType string `json:"aws_bootstrap_instance_type,omitempty"`
17+
MasterInstanceType string `json:"aws_master_instance_type,omitempty"`
18+
MasterAvailabilityZones []string `json:"aws_master_availability_zones"`
19+
WorkerAvailabilityZones []string `json:"aws_worker_availability_zones"`
20+
IOPS int64 `json:"aws_master_root_volume_iops"`
21+
Size int64 `json:"aws_master_root_volume_size,omitempty"`
22+
Type string `json:"aws_master_root_volume_type,omitempty"`
23+
Region string `json:"aws_region,omitempty"`
2324
}
2425

2526
// TFVars generates AWS-specific Terraform variables launching the cluster.
26-
func TFVars(masterConfigs []*v1beta1.AWSMachineProviderConfig) ([]byte, error) {
27+
func TFVars(masterConfigs []*v1beta1.AWSMachineProviderConfig, workerConfigs []*v1beta1.AWSMachineProviderConfig) ([]byte, error) {
2728
masterConfig := masterConfigs[0]
2829

2930
tags := make(map[string]string, len(masterConfig.Tags))
3031
for _, tag := range masterConfig.Tags {
3132
tags[tag.Name] = tag.Value
3233
}
3334

34-
availabilityZones := make([]string, len(masterConfigs))
35+
masterAvailabilityZones := make([]string, len(masterConfigs))
3536
for i, c := range masterConfigs {
36-
availabilityZones[i] = c.Placement.AvailabilityZone
37+
masterAvailabilityZones[i] = c.Placement.AvailabilityZone
38+
}
39+
40+
exists := struct{}{}
41+
availabilityZoneMap := map[string]struct{}{}
42+
for _, c := range workerConfigs {
43+
availabilityZoneMap[c.Placement.AvailabilityZone] = exists
44+
}
45+
workerAvailabilityZones := make([]string, 0, len(availabilityZoneMap))
46+
for zone := range availabilityZoneMap {
47+
workerAvailabilityZones = append(workerAvailabilityZones, zone)
3748
}
3849

3950
if len(masterConfig.BlockDevices) == 0 {
@@ -60,14 +71,15 @@ func TFVars(masterConfigs []*v1beta1.AWSMachineProviderConfig) ([]byte, error) {
6071
instanceClass := defaults.InstanceClass(masterConfig.Placement.Region)
6172

6273
cfg := &config{
63-
Region: masterConfig.Placement.Region,
64-
ExtraTags: tags,
65-
AMI: *masterConfig.AMI.ID,
66-
AvailabilityZones: availabilityZones,
67-
BootstrapInstanceType: fmt.Sprintf("%s.large", instanceClass),
68-
MasterInstanceType: masterConfig.InstanceType,
69-
Size: *rootVolume.EBS.VolumeSize,
70-
Type: *rootVolume.EBS.VolumeType,
74+
Region: masterConfig.Placement.Region,
75+
ExtraTags: tags,
76+
AMI: *masterConfig.AMI.ID,
77+
MasterAvailabilityZones: masterAvailabilityZones,
78+
WorkerAvailabilityZones: workerAvailabilityZones,
79+
BootstrapInstanceType: fmt.Sprintf("%s.large", instanceClass),
80+
MasterInstanceType: masterConfig.InstanceType,
81+
Size: *rootVolume.EBS.VolumeSize,
82+
Type: *rootVolume.EBS.VolumeType,
7183
}
7284

7385
if rootVolume.EBS.Iops != nil {

0 commit comments

Comments
 (0)