Skip to content

Commit

Permalink
Add IPv6 support
Browse files Browse the repository at this point in the history
This commits adds allows kind to create IPv6 Kubernetes clusters
and makes the code ready to implement dual stack support. For simplicity,
only one address of each ip family is considered.

It adds 2 new options to the v1alpha3 API: ipFamily and serviceSubnet
  • Loading branch information
aojea committed Jun 21, 2019
1 parent a237adf commit 765c313
Show file tree
Hide file tree
Showing 15 changed files with 234 additions and 40 deletions.
22 changes: 20 additions & 2 deletions pkg/cluster/config/default.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions pkg/cluster/config/fuzzer/fuzzer.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ func fuzzConfig(obj *config.Cluster, c fuzz.Continue) {
}}
obj.Networking.APIServerAddress = "127.0.0.1"
obj.Networking.PodSubnet = "10.244.0.0/16"
obj.Networking.ServiceSubnet = "10.96.0.0/12"
obj.Networking.IPFamily = "ipv4"
}

func fuzzNode(obj *config.Node, c fuzz.Continue) {
Expand Down
15 changes: 15 additions & 0 deletions pkg/cluster/config/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ const (

// Networking contains cluster wide network settings
type Networking struct {
// IPFamily is the network cluster model, currently it can be ipv4 or ipv6
IPFamily ClusterIPFamily
// APIServerPort is the listen port on the host for the Kubernetes API Server
// Defaults to a random port on the host
APIServerPort int32
Expand All @@ -99,7 +101,20 @@ type Networking struct {
// PodSubnet is the CIDR used for pod IPs
// kind will select a default if unspecified
PodSubnet string
// ServiceSubnet is the CIDR used for services VIPs
// kind will select a default if unspecified
ServiceSubnet string
// If DisableDefaultCNI is true, kind will not install the default CNI setup.
// Instead the user should install their own CNI after creating the cluster.
DisableDefaultCNI bool
}

// ClusterIPFamily defines cluster network IP family
type ClusterIPFamily string

const (
// IPv4Family sets ClusterIPFamily to ipv4
IPv4Family ClusterIPFamily = "ipv4"
// IPv6Family sets ClusterIPFamily to ipv6
IPv6Family ClusterIPFamily = "ipv6"
)
22 changes: 20 additions & 2 deletions pkg/cluster/config/v1alpha3/default.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions pkg/cluster/config/v1alpha3/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ const (

// Networking contains cluster wide network settings
type Networking struct {
// IPFamily is the network cluster model, currently it can be ipv4 or ipv6
IPFamily ClusterIPFamily `json:"ipFamily,omitempty"`
// APIServerPort is the listen port on the host for the Kubernetes API Server
// Defaults to a random port on the host
APIServerPort int32 `json:"apiServerPort,omitempty"`
Expand All @@ -99,7 +101,20 @@ type Networking struct {
// PodSubnet is the CIDR used for pod IPs
// kind will select a default if unspecified
PodSubnet string `json:"podSubnet,omitempty"`
// ServiceSubnet is the CIDR used for services VIPs
// kind will select a default if unspecified for IPv6
ServiceSubnet string `json:"serviceSubnet,omitempty"`
// If DisableDefaultCNI is true, kind will not install the default CNI setup.
// Instead the user should install their own CNI after creating the cluster.
DisableDefaultCNI bool `json:"disableDefaultCNI,omitempty"`
}

// ClusterIPFamily defines cluster network IP family
type ClusterIPFamily string

const (
// IPv4Family sets ClusterIPFamily to ipv4
IPv4Family ClusterIPFamily = "ipv4"
// IPv6Family sets ClusterIPFamily to ipv6
IPv6Family ClusterIPFamily = "ipv6"
)
4 changes: 4 additions & 0 deletions pkg/cluster/config/v1alpha3/zz_generated.conversion.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions pkg/cluster/config/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ func (c *Cluster) Validate() error {
if _, _, err := net.ParseCIDR(c.Networking.PodSubnet); err != nil {
errs = append(errs, errors.Wrapf(err, "invalid podSubnet"))
}
// serviceSubnet should be a valid CIDR
if _, _, err := net.ParseCIDR(c.Networking.ServiceSubnet); err != nil {
errs = append(errs, errors.Wrapf(err, "invalid serviceSubnet"))
}

if len(errs) > 0 {
return util.NewErrors(errs)
Expand Down
18 changes: 15 additions & 3 deletions pkg/cluster/internal/create/actions/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,19 @@ func (a *Action) Execute(ctx *actions.ActionContext) error {
return errors.Wrap(err, "failed to get kubernetes version from node")
}

// get the control plane endpoint
controlPlaneEndpoint, err := nodes.GetControlPlaneEndpoint(allNodes)
// get the control plane endpoint, in case the cluster has an external load balancer in
// front of the control-plane nodes
controlPlaneEndpoint, controlPlaneEndpointIPv6, err := nodes.GetControlPlaneEndpoint(allNodes)
if err != nil {
// TODO(bentheelder): logging here
return err
}

// configure the right protocol addresses
if ctx.Config.Networking.IPFamily == "ipv6" {
controlPlaneEndpoint = controlPlaneEndpointIPv6
}

// create kubeadm init config
fns := []func() error{}

Expand All @@ -83,7 +89,9 @@ func (a *Action) Execute(ctx *actions.ActionContext) error {
APIServerAddress: ctx.Config.Networking.APIServerAddress,
Token: kubeadm.Token,
PodSubnet: ctx.Config.Networking.PodSubnet,
ServiceSubnet: ctx.Config.Networking.ServiceSubnet,
ControlPlane: true,
IPv6: ctx.Config.Networking.IPFamily == "ipv6",
}

fns = append(fns, func() error {
Expand Down Expand Up @@ -193,12 +201,16 @@ func setPatchNames(patches []string, jsonPatches []kustomize.PatchJSON6902) ([]s
// writeKubeadmConfig writes the kubeadm configuration in the specified node
func writeKubeadmConfig(cfg *config.Cluster, data kubeadm.ConfigData, node *nodes.Node) error {
// get the node ip address
nodeAddress, err := node.IP()
nodeAddress, nodeAddressIPv6, err := node.IP()
if err != nil {
return errors.Wrap(err, "failed to get IP for node")
}

data.NodeAddress = nodeAddress
// configure the right protocol addresses
if cfg.Networking.IPFamily == "ipv6" {
data.NodeAddress = nodeAddressIPv6
}

kubeadmConfig, err := getKubeadmConfig(cfg, data)

Expand Down
16 changes: 14 additions & 2 deletions pkg/cluster/internal/create/actions/loadbalancer/loadbalancer.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ func (a *Action) Execute(ctx *actions.ActionContext) error {
return nil
}

// obtain IP family
ipv6 := false
if ctx.Config.Networking.IPFamily == "ipv6" {
ipv6 = true
}

// otherwise notify the user
ctx.Status.Start("Configuring the external load balancer ⚖️")
defer ctx.Status.End(false)
Expand All @@ -71,17 +77,23 @@ func (a *Action) Execute(ctx *actions.ActionContext) error {
return err
}
for _, n := range controlPlaneNodes {
controlPlaneIP, err := n.IP()
controlPlaneIPv4, controlPlaneIPv6, err := n.IP()
if err != nil {
return errors.Wrapf(err, "failed to get IP for node %s", n.Name())
}
backendServers[n.Name()] = fmt.Sprintf("%s:%d", controlPlaneIP, kubeadm.APIServerPort)
if controlPlaneIPv4 != "" && !ipv6 {
backendServers[n.Name()] = fmt.Sprintf("%s:%d", controlPlaneIPv4, kubeadm.APIServerPort)
}
if controlPlaneIPv6 != "" && ipv6 {
backendServers[n.Name()] = fmt.Sprintf("[%s]:%d", controlPlaneIPv6, kubeadm.APIServerPort)
}
}

// create loadbalancer config data
loadbalancerConfig, err := loadbalancer.Config(&loadbalancer.ConfigData{
ControlPlanePort: loadbalancer.ControlPlanePort,
BackendServers: backendServers,
IPv6: ipv6,
})
if err != nil {
return errors.Wrap(err, "failed to generate loadbalancer config data")
Expand Down
17 changes: 16 additions & 1 deletion pkg/cluster/internal/create/nodes.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,16 @@ func createNodeContainers(
desiredNode := desiredNode // capture loop variable
fns = append(fns, func() error {
// create the node into a container (~= docker run -d)
_, err := desiredNode.Create(clusterLabel)
node, err := desiredNode.Create(clusterLabel)
if err != nil {
return err
}
if desiredNode.IPv6 {
err = node.EnableIPv6()
}
return err
})

}
if err := concurrent.UntilError(fns); err != nil {
return err
Expand All @@ -125,6 +132,7 @@ type nodeSpec struct {
// TODO(bentheelder): replace with a cri.PortMapping when we have that
APIServerPort int32
APIServerAddress string
IPv6 bool
}

func nodesToCreate(cfg *config.Cluster, clusterName string) []nodeSpec {
Expand Down Expand Up @@ -154,6 +162,11 @@ func nodesToCreate(cfg *config.Cluster, clusterName string) []nodeSpec {
}
}
isHA := controlPlanes > 1
// obtain IP family
ipv6 := false
if cfg.Networking.IPFamily == "ipv6" {
ipv6 = true
}

// add all of the config nodes as desired nodes
for _, configNode := range configNodes {
Expand All @@ -173,6 +186,7 @@ func nodesToCreate(cfg *config.Cluster, clusterName string) []nodeSpec {
ExtraMounts: configNode.ExtraMounts,
APIServerAddress: apiServerAddress,
APIServerPort: apiServerPort,
IPv6: ipv6,
})
}

Expand All @@ -186,6 +200,7 @@ func nodesToCreate(cfg *config.Cluster, clusterName string) []nodeSpec {
ExtraMounts: []cri.Mount{},
APIServerAddress: cfg.Networking.APIServerAddress,
APIServerPort: cfg.Networking.APIServerPort,
IPv6: ipv6,
})
}

Expand Down
Loading

0 comments on commit 765c313

Please sign in to comment.