Skip to content
This repository has been archived by the owner on Oct 24, 2023. It is now read-only.

Commit

Permalink
add support for single stack IPv6
Browse files Browse the repository at this point in the history
  • Loading branch information
aramase committed Feb 25, 2020
1 parent 85f5bd2 commit dd24886
Show file tree
Hide file tree
Showing 20 changed files with 129 additions and 29 deletions.
Expand Up @@ -9,6 +9,15 @@ data:
kubeconfig: /var/lib/kubelet/kubeconfig
clusterCIDR: "{{ContainerConfig "cluster-cidr"}}"
mode: "{{ContainerConfig "proxy-mode"}}"
{{- if ContainerConfig "bind-address"}}
bindAddress: "{{ContainerConfig "bind-address"}}"
{{end -}}
{{- if ContainerConfig "healthz-bind-address"}}
healthzBindAddress: "{{ContainerConfig "healthz-bind-address"}}"
{{end -}}
{{- if ContainerConfig "metrics-bind-address"}}
metricsBindAddress: "{{ContainerConfig "metrics-bind-address"}}"
{{end -}}
featureGates:
{{ContainerConfig "featureGates"}}
metadata:
Expand Down
3 changes: 3 additions & 0 deletions parts/k8s/addons/coredns.yaml
Expand Up @@ -206,6 +206,9 @@ spec:
- all
readOnlyRootFilesystem: true
dnsPolicy: Default
{{- if ContainerConfig "use-host-network"}}
hostNetwork: {{ContainerConfig "use-host-network"}}
{{end -}}
volumes:
- name: config-volume
configMap:
Expand Down
2 changes: 1 addition & 1 deletion parts/k8s/cloud-init/artifacts/cse_config.sh
Expand Up @@ -303,7 +303,7 @@ ensureKMS() {
}
{{end}}

{{if IsIPv6DualStackFeatureEnabled}}
{{if IsIPv6Enabled}}
ensureDHCPv6() {
wait_for_file 3600 1 {{GetDHCPv6ServiceCSEScriptFilepath}} || exit $ERR_FILE_WATCH_TIMEOUT
wait_for_file 3600 1 {{GetDHCPv6ConfigCSEScriptFilepath}} || exit $ERR_FILE_WATCH_TIMEOUT
Expand Down
4 changes: 2 additions & 2 deletions parts/k8s/cloud-init/artifacts/cse_main.sh
Expand Up @@ -199,8 +199,8 @@ if [[ -n "${MASTER_NODE}" && "${KMS_PROVIDER_VAULT_NAME}" != "" ]]; then
fi
{{end}}

{{/* configure and enable dhcpv6 for dual stack feature */}}
{{- if IsIPv6DualStackFeatureEnabled}}
{{/* configure and enable dhcpv6 for ipv6 features */}}
{{- if IsIPv6Enabled}}
time_metric "EnsureDHCPv6" ensureDHCPv6
{{end}}

Expand Down
2 changes: 1 addition & 1 deletion parts/k8s/cloud-init/masternodecustomdata.yml
Expand Up @@ -127,7 +127,7 @@ write_files:
{{CloudInitData "aptPreferences"}}
{{end}}

{{if IsIPv6DualStackFeatureEnabled}}
{{if IsIPv6Enabled}}
- path: {{GetDHCPv6ServiceCSEScriptFilepath}}
permissions: "0644"
encoding: gzip
Expand Down
2 changes: 1 addition & 1 deletion parts/k8s/cloud-init/nodecustomdata.yml
Expand Up @@ -113,7 +113,7 @@ write_files:
{{CloudInitData "aptPreferences"}}
{{end}}

{{if IsIPv6DualStackFeatureEnabled}}
{{if IsIPv6Enabled}}
- path: {{GetDHCPv6ServiceCSEScriptFilepath}}
permissions: "0644"
encoding: gzip
Expand Down
15 changes: 15 additions & 0 deletions pkg/api/addons.go
Expand Up @@ -677,6 +677,13 @@ func (cs *ContainerService) setAddonsConfig(isUpgrade bool) {
},
}

// set host network to true for single stack IPv6 as the the nameserver is currently
// IPv4 only. By setting it to host network, we can leverage the host routes to successfully
// resolve dns.
if cs.Properties.FeatureFlags.IsFeatureEnabled("EnableIPv6Only") {
defaultCorednsAddonsConfig.Config["use-host-network"] = "true"
}

// If we have any explicit coredns or kube-dns configuration in the addons array
if getAddonsIndexByName(o.KubernetesConfig.Addons, common.KubeDNSAddonName) != -1 || getAddonsIndexByName(o.KubernetesConfig.Addons, common.CoreDNSAddonName) != -1 {
// Ensure we don't we don't prepare an addons spec w/ both kube-dns and coredns enabled
Expand All @@ -701,6 +708,14 @@ func (cs *ContainerService) setAddonsConfig(isUpgrade bool) {
},
}

// set bind address, healthz and metric bind address to :: explicitly for
// single stack IPv6 cluster as it is single stack IPv6 on dual stack host
if cs.Properties.FeatureFlags.IsFeatureEnabled("EnableIPv6Only") {
defaultKubeProxyAddonsConfig.Config["bind-address"] = "::"
defaultKubeProxyAddonsConfig.Config["healthz-bind-address"] = "::"
defaultKubeProxyAddonsConfig.Config["metrics-bind-address"] = "::1"
}

defaultPodSecurityPolicyAddonsConfig := KubernetesAddon{
Name: common.PodSecurityPolicyAddonName,
Enabled: to.BoolPtr(common.IsKubernetesVersionGe(o.OrchestratorVersion, "1.15.0") || to.Bool(o.KubernetesConfig.EnablePodSecurityPolicy)),
Expand Down
6 changes: 5 additions & 1 deletion pkg/api/const.go
Expand Up @@ -438,11 +438,15 @@ const (
// DefaultKubernetesClusterSubnet specifies the default subnet for pods.
DefaultKubernetesClusterSubnet = "10.244.0.0/16"
// DefaultKubernetesClusterSubnetIPv6 specifies the IPv6 default subnet for pods.
DefaultKubernetesClusterSubnetIPv6 = "fc00::/8"
DefaultKubernetesClusterSubnetIPv6 = "fc00::/48"
// DefaultKubernetesServiceCIDR specifies the IP subnet that kubernetes will create Service IPs within.
DefaultKubernetesServiceCIDR = "10.0.0.0/16"
// DefaultKubernetesDNSServiceIP specifies the IP address that kube-dns listens on by default. must by in the default Service CIDR range.
DefaultKubernetesDNSServiceIP = "10.0.0.10"
// DefaultKubernetesServiceCIDRIPv6 specifies the IPv6 subnet that kubernetes will create Service IPs within.
DefaultKubernetesServiceCIDRIPv6 = "fd00::/108"
// DefaultKubernetesDNSServiceIPv6 specifies the IPv6 address that kube-dns listens on by default. must by in the default Service CIDR range.
DefaultKubernetesDNSServiceIPv6 = "fd00::10"
// DefaultMobyVersion specifies the default Azure build version of Moby to install.
DefaultMobyVersion = "3.0.10"
// DefaultContainerdVersion specifies the default containerd version to install.
Expand Down
1 change: 1 addition & 0 deletions pkg/api/converterfromapi.go
Expand Up @@ -639,6 +639,7 @@ func convertFeatureFlagsToVLabs(api *FeatureFlags, vlabs *vlabs.FeatureFlags) {
vlabs.BlockOutboundInternet = api.BlockOutboundInternet
vlabs.EnableIPv6DualStack = api.EnableIPv6DualStack
vlabs.EnableTelemetry = api.EnableTelemetry
vlabs.EnableIPv6Only = api.EnableIPv6Only
}

func convertCloudProfileToVLabs(api *CustomCloudProfile, vlabsccp *vlabs.CustomCloudProfile) {
Expand Down
1 change: 1 addition & 0 deletions pkg/api/convertertoapi.go
Expand Up @@ -123,6 +123,7 @@ func convertVLabsFeatureFlags(vlabs *vlabs.FeatureFlags, api *FeatureFlags) {
api.BlockOutboundInternet = vlabs.BlockOutboundInternet
api.EnableIPv6DualStack = vlabs.EnableIPv6DualStack
api.EnableTelemetry = vlabs.EnableTelemetry
api.EnableIPv6Only = vlabs.EnableIPv6Only
}

func convertVLabsExtensionProfile(vlabs *vlabs.ExtensionProfile, api *ExtensionProfile) {
Expand Down
8 changes: 8 additions & 0 deletions pkg/api/defaults-apiserver.go
Expand Up @@ -162,6 +162,14 @@ func (cs *ContainerService) setAPIServerConfig() {
delete(o.KubernetesConfig.APIServerConfig, key)
}
}
// Set bind address to prefer IPv6 address for single stack IPv6 cluster
// Remove --advertise-address so that --bind-address will be used
if cs.Properties.FeatureFlags.IsFeatureEnabled("EnableIPv6Only") {
o.KubernetesConfig.APIServerConfig["--bind-address"] = "::"
for _, key := range []string{"--advertise-address"} {
delete(o.KubernetesConfig.APIServerConfig, key)
}
}
}

func getDefaultAdmissionControls(cs *ContainerService) (string, string) {
Expand Down
10 changes: 10 additions & 0 deletions pkg/api/defaults.go
Expand Up @@ -199,6 +199,10 @@ func (cs *ContainerService) setOrchestratorDefaults(isUpgrade, isScale bool) {
o.KubernetesConfig.ClusterSubnet = DefaultKubernetesSubnet
} else {
o.KubernetesConfig.ClusterSubnet = DefaultKubernetesClusterSubnet
// ipv6 only cluster
if cs.Properties.FeatureFlags.IsFeatureEnabled("EnableIPv6Only") {
o.KubernetesConfig.ClusterSubnet = DefaultKubernetesClusterSubnetIPv6
}
// ipv4 and ipv6 subnet for dual stack
if cs.Properties.FeatureFlags.IsFeatureEnabled("EnableIPv6DualStack") {
o.KubernetesConfig.ClusterSubnet = strings.Join([]string{DefaultKubernetesClusterSubnet, DefaultKubernetesClusterSubnetIPv6}, ",")
Expand Down Expand Up @@ -233,12 +237,18 @@ func (cs *ContainerService) setOrchestratorDefaults(isUpgrade, isScale bool) {
}
if o.KubernetesConfig.DNSServiceIP == "" {
o.KubernetesConfig.DNSServiceIP = DefaultKubernetesDNSServiceIP
if cs.Properties.FeatureFlags.IsFeatureEnabled("EnableIPv6Only") {
o.KubernetesConfig.DNSServiceIP = DefaultKubernetesDNSServiceIPv6
}
}
if o.KubernetesConfig.DockerBridgeSubnet == "" {
o.KubernetesConfig.DockerBridgeSubnet = DefaultDockerBridgeSubnet
}
if o.KubernetesConfig.ServiceCIDR == "" {
o.KubernetesConfig.ServiceCIDR = DefaultKubernetesServiceCIDR
if cs.Properties.FeatureFlags.IsFeatureEnabled("EnableIPv6Only") {
o.KubernetesConfig.ServiceCIDR = DefaultKubernetesServiceCIDRIPv6
}
}

if common.IsKubernetesVersionGe(o.OrchestratorVersion, "1.14.0") {
Expand Down
4 changes: 4 additions & 0 deletions pkg/api/types.go
Expand Up @@ -124,6 +124,7 @@ type FeatureFlags struct {
BlockOutboundInternet bool `json:"blockOutboundInternet,omitempty"`
EnableIPv6DualStack bool `json:"enableIPv6DualStack,omitempty"`
EnableTelemetry bool `json:"enableTelemetry,omitempty"`
EnableIPv6Only bool `json:"enableIPv6Only,omitempty"`
}

// ServicePrincipalProfile contains the client and secret used by the cluster for Azure Resource CRUD
Expand Down Expand Up @@ -2203,6 +2204,8 @@ func (f *FeatureFlags) IsFeatureEnabled(feature string) bool {
return f.EnableIPv6DualStack
case "EnableTelemetry":
return f.EnableTelemetry
case "EnableIPv6Only":
return f.EnableIPv6Only
default:
return false
}
Expand Down Expand Up @@ -2298,6 +2301,7 @@ func (cs *ContainerService) GetProvisionScriptParametersCommon(input ProvisionSc
"KMS_PROVIDER_VAULT_NAME": input.ClusterKeyVaultName,
"IS_HOSTED_MASTER": strconv.FormatBool(cs.Properties.IsHostedMasterProfile()),
"IS_IPV6_DUALSTACK_FEATURE_ENABLED": strconv.FormatBool(cs.Properties.FeatureFlags.IsFeatureEnabled("EnableIPv6DualStack")),
"IS_IPV6_ENABLED": strconv.FormatBool(cs.Properties.FeatureFlags.IsFeatureEnabled("EnableIPv6Only")),
"AUTHENTICATION_METHOD": cs.Properties.GetCustomCloudAuthenticationMethod(),
"IDENTITY_SYSTEM": cs.Properties.GetCustomCloudIdentitySystem(),
"NETWORK_API_VERSION": APIVersionNetwork,
Expand Down
6 changes: 6 additions & 0 deletions pkg/api/vlabs/types.go
Expand Up @@ -59,6 +59,7 @@ type FeatureFlags struct {
BlockOutboundInternet bool `json:"blockOutboundInternet,omitempty"`
EnableIPv6DualStack bool `json:"enableIPv6DualStack,omitempty"`
EnableTelemetry bool `json:"enableTelemetry,omitempty"`
EnableIPv6Only bool `json:"enableIPv6Only,omitempty"`
}

// ServicePrincipalProfile contains the client and secret used by the cluster for Azure Resource CRUD
Expand Down Expand Up @@ -926,3 +927,8 @@ func (k *KubernetesConfig) IsAddonEnabled(addonName string) bool {
func (f *FeatureFlags) IsIPv6DualStackEnabled() bool {
return f != nil && f.EnableIPv6DualStack
}

// IsIPv6OnlyEnabled checks if IPv6Only feature is enabled
func (f *FeatureFlags) IsIPv6OnlyEnabled() bool {
return f != nil && f.EnableIPv6Only
}
34 changes: 26 additions & 8 deletions pkg/api/vlabs/validate.go
Expand Up @@ -233,7 +233,7 @@ func (a *Properties) ValidateOrchestratorProfile(isUpdate bool) error {
}

if o.KubernetesConfig != nil {
err := o.KubernetesConfig.Validate(version, a.HasWindows(), a.FeatureFlags.IsIPv6DualStackEnabled())
err := o.KubernetesConfig.Validate(version, a.HasWindows(), a.FeatureFlags.IsIPv6DualStackEnabled(), a.FeatureFlags.IsIPv6OnlyEnabled())
if err != nil {
return err
}
Expand Down Expand Up @@ -386,10 +386,10 @@ func (a *Properties) validateMasterProfile(isUpdate bool) error {
if m.IsVirtualMachineScaleSets() && m.VnetSubnetID != "" && m.FirstConsecutiveStaticIP != "" {
return errors.New("when masterProfile's availabilityProfile is VirtualMachineScaleSets and a vnetSubnetID is specified, the firstConsecutiveStaticIP should be empty and will be determined by an offset from the first IP in the vnetCidr")
}
// validate os type is linux if dual stack feature is enabled
if a.FeatureFlags.IsIPv6DualStackEnabled() {
// validate distro is ubuntu if dual stack or ipv6 only feature is enabled
if a.FeatureFlags.IsIPv6DualStackEnabled() || a.FeatureFlags.IsIPv6OnlyEnabled() {
if m.Distro == CoreOS {
return errors.Errorf("Dual stack feature is currently supported only with Ubuntu, but master is of distro type %s", m.Distro)
return errors.Errorf("Dual stack and single stack IPv6 feature is currently supported only with Ubuntu, but master is of distro type %s", m.Distro)
}
}
}
Expand Down Expand Up @@ -454,12 +454,12 @@ func (a *Properties) validateAgentPoolProfiles(isUpdate bool) error {
}

// validate os type is linux if dual stack feature is enabled
if a.FeatureFlags.IsIPv6DualStackEnabled() {
if a.FeatureFlags.IsIPv6DualStackEnabled() || a.FeatureFlags.IsIPv6OnlyEnabled() {
if agentPoolProfile.OSType == Windows {
return errors.Errorf("Dual stack feature is supported only with Linux, but agent pool '%s' is of os type %s", agentPoolProfile.Name, agentPoolProfile.OSType)
return errors.Errorf("Dual stack and single stack IPv6 feature is supported only with Linux, but agent pool '%s' is of os type %s", agentPoolProfile.Name, agentPoolProfile.OSType)
}
if agentPoolProfile.Distro == CoreOS {
return errors.Errorf("Dual stack feature is currently supported only with Ubuntu, but agent pool '%s' is of distro type %s", agentPoolProfile.Name, agentPoolProfile.Distro)
return errors.Errorf("Dual stack and single stack IPv6 feature is currently supported only with Ubuntu, but agent pool '%s' is of distro type %s", agentPoolProfile.Name, agentPoolProfile.Distro)
}
}

Expand Down Expand Up @@ -1233,7 +1233,7 @@ func validatePasswordComplexity(name string, password string) (out bool) {
}

// Validate validates the KubernetesConfig
func (k *KubernetesConfig) Validate(k8sVersion string, hasWindows, ipv6DualStackEnabled bool) error {
func (k *KubernetesConfig) Validate(k8sVersion string, hasWindows, ipv6DualStackEnabled, isIPv6 bool) error {
// number of minimum retries allowed for kubelet to post node status
const minKubeletRetries = 4

Expand All @@ -1255,6 +1255,24 @@ func (k *KubernetesConfig) Validate(k8sVersion string, hasWindows, ipv6DualStack
}
}

if isIPv6 {
sv, err := semver.Make(k8sVersion)
if err != nil {
return errors.Errorf("could not validate version %s", k8sVersion)
}
minVersion, err := semver.Make("1.18.0-alpha.4")
if err != nil {
return errors.New("could not validate version")
}
if sv.LT(minVersion) {
return errors.Errorf("IPv6 single stack not available in kubernetes version %s", k8sVersion)
}
// singel stack IPv6 feature is currently only supported with kubenet
if k.NetworkPlugin != "kubenet" {
return errors.Errorf("OrchestratorProfile.KubernetesConfig.NetworkPlugin '%s' is invalid. IPv6 single stack supported only with kubenet.", k.NetworkPlugin)
}
}

if k.ClusterSubnet != "" {
clusterSubnets := strings.Split(k.ClusterSubnet, ",")
if !ipv6DualStackEnabled && len(clusterSubnets) > 1 {
Expand Down
6 changes: 3 additions & 3 deletions pkg/engine/networkinterfaces.go
Expand Up @@ -88,7 +88,7 @@ func CreateMasterVMNetworkInterfaces(cs *api.ContainerService) NetworkInterfaceA
}

// add ipv6 nic config for dual stack
if cs.Properties.FeatureFlags.IsFeatureEnabled("EnableIPv6DualStack") {
if cs.Properties.FeatureFlags.IsFeatureEnabled("EnableIPv6DualStack") || cs.Properties.FeatureFlags.IsFeatureEnabled("EnableIPv6Only") {
ipv6Config := network.InterfaceIPConfiguration{
Name: to.StringPtr("ipconfigv6"),
InterfaceIPConfigurationPropertiesFormat: &network.InterfaceIPConfigurationPropertiesFormat{
Expand Down Expand Up @@ -374,7 +374,7 @@ func createAgentVMASNetworkInterface(cs *api.ContainerService, profile *api.Agen
}
}

if cs.Properties.FeatureFlags.IsFeatureEnabled("EnableIPv6DualStack") {
if cs.Properties.FeatureFlags.IsFeatureEnabled("EnableIPv6DualStack") || cs.Properties.FeatureFlags.IsFeatureEnabled("EnableIPv6Only") {
if cs.Properties.OrchestratorProfile.KubernetesConfig.LoadBalancerSku != api.StandardLoadBalancerSku {
var backendPools []network.BackendAddressPool
if ipConfig.LoadBalancerBackendAddressPools != nil {
Expand All @@ -390,7 +390,7 @@ func createAgentVMASNetworkInterface(cs *api.ContainerService, profile *api.Agen
}

// add ipv6 nic config for dual stack
if cs.Properties.FeatureFlags.IsFeatureEnabled("EnableIPv6DualStack") {
if cs.Properties.FeatureFlags.IsFeatureEnabled("EnableIPv6DualStack") || cs.Properties.FeatureFlags.IsFeatureEnabled("EnableIPv6Only") {
ipv6Config := network.InterfaceIPConfiguration{
Name: to.StringPtr("ipconfigv6"),
InterfaceIPConfigurationPropertiesFormat: &network.InterfaceIPConfigurationPropertiesFormat{
Expand Down
3 changes: 3 additions & 0 deletions pkg/engine/template_generator.go
Expand Up @@ -653,6 +653,9 @@ func getContainerServiceFuncMap(cs *api.ContainerService) template.FuncMap {
"IsIPv6DualStackFeatureEnabled": func() bool {
return cs.Properties.FeatureFlags.IsFeatureEnabled("EnableIPv6DualStack")
},
"IsIPv6Enabled": func() bool {
return cs.Properties.FeatureFlags.IsFeatureEnabled("EnableIPv6Only")
},
"GetBase64EncodedEnvironmentJSON": func() string {
customEnvironmentJSON, _ := cs.Properties.GetCustomEnvironmentJSON(false)
return base64.StdEncoding.EncodeToString([]byte(customEnvironmentJSON))
Expand Down
22 changes: 17 additions & 5 deletions pkg/engine/templates_generated.go

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

2 changes: 1 addition & 1 deletion pkg/engine/virtualmachinescalesets.go
Expand Up @@ -537,7 +537,7 @@ func CreateAgentVMSS(cs *api.ContainerService, profile *api.AgentPoolProfile) Vi
ipconfig.VirtualMachineScaleSetIPConfigurationProperties = &ipConfigProps
ipConfigurations = append(ipConfigurations, ipconfig)

if cs.Properties.FeatureFlags.IsFeatureEnabled("EnableIPv6DualStack") {
if cs.Properties.FeatureFlags.IsFeatureEnabled("EnableIPv6DualStack") || cs.Properties.FeatureFlags.IsFeatureEnabled("EnableIPv6Only") {
ipconfigv6 := compute.VirtualMachineScaleSetIPConfiguration{
Name: to.StringPtr(fmt.Sprintf("ipconfig%dv6", i)),
VirtualMachineScaleSetIPConfigurationProperties: &compute.VirtualMachineScaleSetIPConfigurationProperties{
Expand Down

0 comments on commit dd24886

Please sign in to comment.