-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Topology Support (AKA Private Networking) #428 #694
Changes from all commits
000e847
8fba14b
8f30225
a1ca6b7
a1c5c77
9bd9e30
db693a6
0f8f2d1
d729596
e90b5fa
c1e8dbe
a3dd125
de79ca2
835e24f
cb31579
cebdde3
e962f9c
312621b
0857ed1
37f5bb7
3f4bc39
78ecdb2
712882f
cc2e920
479c778
5b81b86
c1644cc
3c92c6a
b8d2301
729598a
95a8c59
5f600eb
6f78e0c
4610325
de9baa2
b1febd9
8c41dad
bcbf3df
07eb92f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,6 +30,7 @@ import ( | |
"k8s.io/kops/upup/pkg/kutil" | ||
"os" | ||
"strings" | ||
"k8s.io/kops/pkg/apis/kops" | ||
) | ||
|
||
type UpdateClusterOptions struct { | ||
|
@@ -188,7 +189,11 @@ func RunUpdateCluster(f *util.Factory, cmd *cobra.Command, args []string, out io | |
fmt.Printf("\n") | ||
fmt.Printf("Suggestions:\n") | ||
fmt.Printf(" * list nodes: kubectl get nodes --show-labels\n") | ||
fmt.Printf(" * ssh to the master: ssh -i ~/.ssh/id_rsa admin@%s\n", cluster.Spec.MasterPublicName) | ||
if cluster.Spec.Topology.Masters == kops.TopologyPublic { | ||
fmt.Printf(" * ssh to the master: ssh -i ~/.ssh/id_rsa admin@%s\n", cluster.Spec.MasterPublicName) | ||
}else { | ||
fmt.Printf(" * ssh to the bastion: ssh -i ~/.ssh/id_rsa admin@%s\n", cluster.Spec.MasterPublicName) | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. would it be dryer to just use a var here: if cluster....... { fmt.Printf(" * ssh to the %s: ssh -i ~/.ssh/id_rsa admin@%s\n", someZestyVarName, cluster.Spec.MasterPublicName) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes that would work - but for the sake of keeping the two error messages decoupled I think it's fine to define the repeating text. The Bastion has some work that will be coming in a future PR #836 that will more than likely effect this string anyway. Thanks for the review! |
||
fmt.Printf(" * read about installing addons: https://github.com/kubernetes/kops/blob/master/docs/addons.md\n") | ||
fmt.Printf("\n") | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
# Network Topologies in Kops | ||
|
||
Kops supports a number of pre defined network topologies. They are separated into commonly used scenarios, or topologies. | ||
|
||
Each of the supported topologies are listed below, with an example on how to deploy them. | ||
|
||
# AWS | ||
|
||
Kops supports the following topologies on AWS | ||
|
||
| Topology | Value | Description | | ||
| ----------------- |----------- | ----------------------------------------------------------------------------------------------------------- | | ||
| Public Cluster | public | All masters/nodes will be launched in a **public subnet** in the VPC | | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add a column listing GA / Alpha release. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When this is merged it will go into both - we aren't calling out differences (yet) do we really need this in this PR? Maybe another PR for documenting the APIs? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no we need to release this into master as alpha supported. Not api based, but this is alpha, be warned. Lets talk about this on zoom. |
||
| Private Cluster | private | All masters/nodes will be launched in a **private subnet** in the VPC | | ||
|
||
|
||
[More information](http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_Subnets.html) on Public and Private subnets in AWS | ||
|
||
Notes on subnets | ||
|
||
##### Public Subnet | ||
If a subnet's traffic is routed to an Internet gateway, the subnet is known as a public subnet. | ||
|
||
##### Private Subnet | ||
If a subnet doesn't have a route to the Internet gateway, the subnet is known as a private subnet. | ||
|
||
Private topologies *will* have public access via the Kubernetes API and an (optional) SSH bastion instance. | ||
|
||
# Defining a topology on create | ||
|
||
To specify a topology use the `--topology` or `-t` flag as in : | ||
|
||
``` | ||
kops create cluster ... --topology public|private | ||
``` | ||
|
||
# Troubleshooting | ||
|
||
- Right now we require `-networking cni` for all private topologies. | ||
- Right now a manual install of weave is required for private topologies. | ||
- Right now upgrading from a public cluster to a private cluster is considered very **experimental** | ||
|
||
``` | ||
kubectl create -f https://git.io/weave-kube | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,7 +29,7 @@ import ( | |
|
||
type Cluster struct { | ||
unversioned.TypeMeta `json:",inline"` | ||
ObjectMeta `json:"metadata,omitempty"` | ||
ObjectMeta `json:"metadata,omitempty"` | ||
|
||
Spec ClusterSpec `json:"spec,omitempty"` | ||
} | ||
|
@@ -77,6 +77,11 @@ type ClusterSpec struct { | |
// NetworkID is an identifier of a network, if we want to reuse/share an existing network (e.g. an AWS VPC) | ||
NetworkID string `json:"networkID,omitempty"` | ||
|
||
// Topology defines the type of network topology to use on the cluster - default public | ||
// This is heavily weighted towards AWS for the time being, but should also be agnostic enough | ||
// to port out to GCE later if needed | ||
Topology *TopologySpec `json:"topology,omitempty"` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think - just as in mainline k8s, you have to copy-paste API changes into v1alpha1 now also. It's fine to add fields (as long as the default is consistent with the existing behaviour), but removing them or changing their meaning requires a new API version and we need to create custom conversion code, so let's avoid that for now if we can :-) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done and done - see the latest commit |
||
|
||
// SecretStore is the VFS path to where secrets are stored | ||
SecretStore string `json:"secretStore,omitempty"` | ||
// KeyStore is the VFS path to where SSL keys and certificates are stored | ||
|
@@ -275,7 +280,14 @@ type EtcdMemberSpec struct { | |
|
||
type ClusterZoneSpec struct { | ||
Name string `json:"name,omitempty"` | ||
CIDR string `json:"cidr,omitempty"` | ||
|
||
// For Private network topologies we need to have 2 | ||
// CIDR blocks. | ||
// 1 - Utility (Public) Subnets | ||
// 2 - Operating (Private) Subnets | ||
|
||
PrivateCIDR string `json:"privateCIDR,omitempty"` | ||
CIDR string `json:"cidr,omitempty"` | ||
|
||
// ProviderID is the cloud provider id for the objects associated with the zone (the subnet on AWS) | ||
ProviderID string `json:"id,omitempty"` | ||
|
@@ -341,10 +353,10 @@ func (c *Cluster) FillDefaults() error { | |
// OK | ||
} else if c.Spec.Networking.Kubenet != nil { | ||
// OK | ||
} else if c.Spec.Networking.External != nil { | ||
// OK | ||
} else if c.Spec.Networking.CNI != nil { | ||
// OK | ||
} else if c.Spec.Networking.External != nil { | ||
// OK | ||
} else { | ||
// No networking model selected; choose Kubenet | ||
c.Spec.Networking.Kubenet = &KubenetNetworkingSpec{} | ||
|
@@ -414,21 +426,27 @@ func FindLatestKubernetesVersion() (string, error) { | |
|
||
func (z *ClusterZoneSpec) performAssignments(c *Cluster) error { | ||
if z.CIDR == "" { | ||
cidr, err := z.assignCIDR(c) | ||
err := z.assignCIDR(c) | ||
if err != nil { | ||
return err | ||
} | ||
glog.Infof("Assigned CIDR %s to zone %s", cidr, z.Name) | ||
z.CIDR = cidr | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (z *ClusterZoneSpec) assignCIDR(c *Cluster) (string, error) { | ||
// Will generate a CIDR block based on the last character in | ||
// the cluster.Spec.Zones structure. | ||
// | ||
func (z *ClusterZoneSpec) assignCIDR(c *Cluster) error { | ||
// TODO: We probably could query for the existing subnets & allocate appropriately | ||
// for now we'll require users to set CIDRs themselves | ||
|
||
// Used in calculating private subnet blocks (if needed only) | ||
needsPrivateBlock := false | ||
if c.Spec.Topology.Masters == TopologyPrivate || c.Spec.Topology.Nodes == TopologyPrivate { | ||
needsPrivateBlock = true | ||
} | ||
|
||
lastCharMap := make(map[byte]bool) | ||
for _, nodeZone := range c.Spec.Zones { | ||
lastChar := nodeZone.Name[len(nodeZone.Name)-1] | ||
|
@@ -452,13 +470,13 @@ func (z *ClusterZoneSpec) assignCIDR(c *Cluster) (string, error) { | |
} | ||
} | ||
if index == -1 { | ||
return "", fmt.Errorf("zone not configured: %q", z.Name) | ||
return fmt.Errorf("zone not configured: %q", z.Name) | ||
} | ||
} | ||
|
||
_, cidr, err := net.ParseCIDR(c.Spec.NetworkCIDR) | ||
if err != nil { | ||
return "", fmt.Errorf("Invalid NetworkCIDR: %q", c.Spec.NetworkCIDR) | ||
return fmt.Errorf("Invalid NetworkCIDR: %q", c.Spec.NetworkCIDR) | ||
} | ||
networkLength, _ := cidr.Mask.Size() | ||
|
||
|
@@ -475,14 +493,49 @@ func (z *ClusterZoneSpec) assignCIDR(c *Cluster) (string, error) { | |
subnetIP := make(net.IP, len(ip4)) | ||
binary.BigEndian.PutUint32(subnetIP, n) | ||
subnetCIDR := subnetIP.String() + "/" + strconv.Itoa(networkLength) | ||
z.CIDR = subnetCIDR | ||
glog.V(2).Infof("Computed CIDR for subnet in zone %q as %q", z.Name, subnetCIDR) | ||
return subnetCIDR, nil | ||
glog.Infof("Assigned CIDR %s to zone %s", subnetCIDR, z.Name) | ||
|
||
if needsPrivateBlock { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, joy network math. Umm, how does this work 😀 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are you asking how we are calculating the subnet? Or how we know we need to calculate another block? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Check out lines 446-449 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How does this code block work? Just high level. Please document. |
||
m := binary.BigEndian.Uint32(ip4) | ||
// All Private CIDR blocks are at the end of our range | ||
m += uint32(index+len(c.Spec.Zones)) << uint(32-networkLength) | ||
privSubnetIp := make(net.IP, len(ip4)) | ||
binary.BigEndian.PutUint32(privSubnetIp, m) | ||
privCIDR := privSubnetIp.String() + "/" + strconv.Itoa(networkLength) | ||
z.PrivateCIDR = privCIDR | ||
glog.V(2).Infof("Computed Private CIDR for subnet in zone %q as %q", z.Name, privCIDR) | ||
glog.Infof("Assigned Private CIDR %s to zone %s", privCIDR, z.Name) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
return "", fmt.Errorf("Unexpected IP address type for NetworkCIDR: %s", c.Spec.NetworkCIDR) | ||
return fmt.Errorf("Unexpected IP address type for NetworkCIDR: %s", c.Spec.NetworkCIDR) | ||
} | ||
|
||
// SharedVPC is a simple helper function which makes the templates for a shared VPC clearer | ||
func (c *Cluster) SharedVPC() bool { | ||
return c.Spec.NetworkID != "" | ||
} | ||
|
||
// -------------------------------------------------------------------------------------------- | ||
// Network Topology functions for template parsing | ||
// | ||
// Each of these functions can be used in the model templates | ||
// The go template package currently only supports boolean | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can do In general, it might be easier to replace the network.yaml with go code. I have a prototype of it somewhere that I'll paste a link to if I find it. But the whole reliance on templates just seems pretty fragile and seems to have turned out to be harder for people to change, not easier. But completely up to you if you want to do this for this PR. Just if you're fighting templates you might find it easier to dump them :-) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should continue to use the templates and follow suit here - if we want to move away from them later we can.. But I think that is a larger effort. They work for now, so lets just keep it consistent There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
// operations, so the logic is mapped here as *Cluster functions. | ||
// | ||
// A function will need to be defined for all new topologies, if we plan to use them in the | ||
// model templates. | ||
// -------------------------------------------------------------------------------------------- | ||
func (c *Cluster) IsTopologyPrivate() bool { | ||
return (c.Spec.Topology.Masters == TopologyPrivate && c.Spec.Topology.Nodes == TopologyPrivate) | ||
} | ||
func (c *Cluster) IsTopologyPublic() bool { | ||
return (c.Spec.Topology.Masters == TopologyPublic && c.Spec.Topology.Nodes == TopologyPublic) | ||
} | ||
func (c *Cluster) IsTopologyPrivateMasters() bool { | ||
return (c.Spec.Topology.Masters == TopologyPrivate && c.Spec.Topology.Nodes == TopologyPublic) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
/* | ||
Copyright 2016 The Kubernetes Authors. | ||
|
||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
|
||
http://www.apache.org/licenses/LICENSE-2.0 | ||
|
||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
|
||
package kops | ||
|
||
const ( | ||
TopologyPublic = "public" | ||
TopologyPrivate = "private" | ||
) | ||
|
||
type TopologySpec struct { | ||
// The environment to launch the Kubernetes masters in public|private | ||
Masters string `json:"masters,omitempty"` | ||
|
||
// The environment to launch the Kubernetes nodes in public|private | ||
Nodes string `json:"nodes,omitempty"` | ||
|
||
// Controls if a private topology should deploy a bastion host or not | ||
// The bastion host is designed to be a simple, and secure bridge between | ||
// the public subnet and the private subnet | ||
BypassBastion bool `json:"bypassBastion,omitempty"` | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
/* | ||
Copyright 2016 The Kubernetes Authors. | ||
|
||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
|
||
http://www.apache.org/licenses/LICENSE-2.0 | ||
|
||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
|
||
package v1alpha1 | ||
|
||
const ( | ||
TopologyPublic = "public" | ||
TopologyPrivate = "private" | ||
) | ||
|
||
type TopologySpec struct { | ||
// The environment to launch the Kubernetes masters in public|private | ||
Masters string `json:"masters,omitempty"` | ||
|
||
// The environment to launch the Kubernetes nodes in public|private | ||
Nodes string `json:"nodes,omitempty"` | ||
|
||
// Controls if a private topology should deploy a bastion host or not | ||
// The bastion host is designed to be a simple, and secure bridge between | ||
// the public subnet and the private subnet | ||
BypassBastion bool `json:"bypassBastion,omitempty"` | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where do we add notes on what this actually means? n00b dev saying cool we have Topo but what the hell is Topo?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are notes in the struct definition, in the docs, in the cobra commands, etc :)