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

feat: add support for single stack IPv6 #2781

Merged
merged 4 commits into from
Mar 6, 2020
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -9,6 +9,15 @@ data:
kubeconfig: /var/lib/kubelet/kubeconfig
clusterCIDR: "{{ContainerConfig "cluster-cidr"}}"
mode: "{{ContainerConfig "proxy-mode"}}"
{{- if ContainerConfig "bind-address"}}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So go template if operates against strings according to "" = false and any non-empty string = true ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jackfrancis correct, if it's "" then it's ignored.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looked up and verified in the actual docs as well :)

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
6 changes: 6 additions & 0 deletions parts/k8s/addons/coredns.yaml
Expand Up @@ -155,6 +155,9 @@ spec:
effect: NoSchedule
nodeSelector:
beta.kubernetes.io/os: linux
{{- if ContainerConfig "use-host-network"}}
kubernetes.io/role: agent
{{end}}
containers:
- name: coredns
image: {{ContainerImage "coredns"}}
Expand Down Expand Up @@ -206,6 +209,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 @@ -201,8 +201,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
18 changes: 17 additions & 1 deletion pkg/api/addons.go
Expand Up @@ -301,7 +301,8 @@ func (cs *ContainerService) setAddonsConfig(isUpgrade bool) {
"non-masquerade-cidr": cs.Properties.GetNonMasqueradeCIDR(),
"non-masq-cni-cidr": cs.Properties.GetAzureCNICidr(),
"secondary-non-masquerade-cidr": cs.Properties.GetSecondaryNonMasqueradeCIDR(),
"enable-ipv6": strconv.FormatBool(cs.Properties.FeatureFlags.IsFeatureEnabled("EnableIPv6DualStack")),
"enable-ipv6": strconv.FormatBool(cs.Properties.FeatureFlags.IsFeatureEnabled("EnableIPv6DualStack") ||
cs.Properties.FeatureFlags.IsFeatureEnabled("EnableIPv6Only")),
},
}

Expand Down Expand Up @@ -677,6 +678,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") {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add UT coverage to addons_test.go for these ipv6 scenarios. Lemme know if you want help, it's 4k+ lines of UT coverage :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me take a stab at it, will ping you if you I need help :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jackfrancis added the tests, PTAL!

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 +709,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 @@ -454,11 +454,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
19 changes: 19 additions & 0 deletions pkg/api/defaults-apiserver_test.go
Expand Up @@ -506,3 +506,22 @@ func TestAPIServerFeatureGates(t *testing.T) {
"1.17.0", a["--feature-gates"])
}
}

func TestAPIServerIPv6Only(t *testing.T) {
cs := CreateMockContainerService("testcluster", "1.18.0", 3, 2, false)
cs.Properties.FeatureFlags = &FeatureFlags{EnableIPv6Only: true}
cs.setAPIServerConfig()

a := cs.Properties.OrchestratorProfile.KubernetesConfig.APIServerConfig
// bind address should be :: for single stack IPv6 cluster
if a["--bind-address"] != "::" {
t.Fatalf("got unexpected default value for '--bind-address' API server config: %s",
a["--bind-address"])
}
for _, key := range []string{"--advertise-address"} {
if _, ok := a[key]; ok {
t.Fatalf("got unexpected '%s' API server config value for '--advertise-address' %s",
key, a[key])
}
}
}
10 changes: 10 additions & 0 deletions pkg/api/defaults.go
Expand Up @@ -212,6 +212,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 @@ -246,12 +250,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
23 changes: 23 additions & 0 deletions pkg/api/defaults_test.go
Expand Up @@ -1482,6 +1482,29 @@ func TestMasterProfileDefaults(t *testing.T) {
t.Fatalf("OrchestratorProfile.KubernetesConfig.OutboundRuleIdleTimeoutInMinutes did not have the expected configuration, got %d, expected %d",
properties.OrchestratorProfile.KubernetesConfig.OutboundRuleIdleTimeoutInMinutes, DefaultOutboundRuleIdleTimeoutInMinutes)
}

// this validates cluster subnet default configuration for single stack IPv6 only cluster
mockCS = getMockBaseContainerService("1.18.0-alpha.1")
properties = mockCS.Properties
properties.OrchestratorProfile.OrchestratorType = Kubernetes
properties.FeatureFlags = &FeatureFlags{EnableIPv6Only: true}
mockCS.SetPropertiesDefaults(PropertiesDefaultsParams{
IsScale: false,
IsUpgrade: false,
PkiKeySize: helpers.DefaultPkiKeySize,
})
if properties.OrchestratorProfile.KubernetesConfig.DNSServiceIP != DefaultKubernetesDNSServiceIPv6 {
t.Fatalf("OrchestratorProfile.KubernetesConfig.DNSServiceIP did not have the expected configuration, got %s, expected %s",
properties.OrchestratorProfile.KubernetesConfig.DNSServiceIP, DefaultKubernetesDNSServiceIPv6)
}
if properties.OrchestratorProfile.KubernetesConfig.ServiceCIDR != DefaultKubernetesServiceCIDRIPv6 {
t.Fatalf("OrchestratorProfile.KubernetesConfig.ServiceCIDR did not have the expected configuration, got %s, expected %s",
properties.OrchestratorProfile.KubernetesConfig.ServiceCIDR, DefaultKubernetesServiceCIDRIPv6)
}
if properties.OrchestratorProfile.KubernetesConfig.ClusterSubnet != DefaultKubernetesClusterSubnetIPv6 {
t.Fatalf("OrchestratorProfile.KubernetesConfig.ClusterSubnet did not have the expected configuration, got %s, expected %s",
properties.OrchestratorProfile.KubernetesConfig.ClusterSubnet, DefaultKubernetesClusterSubnetIPv6)
}
}

func TestAgentPoolProfile(t *testing.T) {
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 @@ -2237,6 +2238,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 @@ -2332,6 +2335,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") || cs.Properties.FeatureFlags.IsFeatureEnabled("EnableIPv6DualStack")),
"AUTHENTICATION_METHOD": cs.Properties.GetCustomCloudAuthenticationMethod(),
"IDENTITY_SYSTEM": cs.Properties.GetCustomCloudIdentitySystem(),
"NETWORK_API_VERSION": APIVersionNetwork,
Expand Down