feat: allow creation of dualstack Windows clusters (#4176)

* feat: allow creation of dualstack Windows clusters

* Apply suggestions from code review

Co-authored-by: James Sturtevant <jsturtevant@gmail.com>

* Disable IPv6-only on Windows

* fix: restart hns for Windows dual-stack

Co-authored-by: Varun Venkatesh <vavenk@microsoft.com>
Co-authored-by: James Sturtevant <jsturtevant@gmail.com>
This commit is contained in:
Varun Venkatesh 2021-03-02 22:36:26 +05:30 коммит произвёл GitHub
Родитель 30bb3381fd
Коммит 9047a452a0
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
5 изменённых файлов: 118 добавлений и 16 удалений

Просмотреть файл

@ -113,10 +113,8 @@ nginx-ipv6 LoadBalancer fd00::6283 2603:1030:805:3::3 80:31140/TCP
- Dual-stack clusters are supported only with kubenet and azurecni.
- Dual-stack cluster with azurecni are only supported with `bridge` network mode.
- Dual-stack clusters are supported only with Linux.
- Dual-stack clusters with Windows is not supported at this time because it requires
- Kubernetes version 1.19+ and
- [backport to 2004 to support dual-stack containers](https://github.com/Azure/aks-engine/issues/3568).
- Dual-stack clusters are supported on Windows from version 2004 (kernel version 10.0.19041.610) and Kubernetes version 1.19
- https://kubernetes.io/docs/setup/production-environment/windows/intro-windows-in-kubernetes/#ipv4-ipv6-dual-stack
- Dual-stack clusters are supported with
- `ipvs` kube-proxy mode (Kubernetes version 1.16+)
- `iptables` kube-proxy mode (Kubernetes version 1.18+).

Просмотреть файл

@ -0,0 +1,60 @@
{
"apiVersion": "vlabs",
"properties": {
"featureFlags": {
"enableIPv6DualStack": true
},
"orchestratorProfile": {
"orchestratorRelease": "1.19",
"kubernetesConfig": {
"apiServerConfig": {
"--feature-gates": "IPv6DualStack=true"
},
"kubeletConfig": {
"--feature-gates": "IPv6DualStack=true"
},
"controllerManagerConfig": {
"--feature-gates": "IPv6DualStack=true"
},
"kubeProxyMode": "ipvs",
"networkPlugin": "azure",
"networkMode": "bridge",
"networkPolicy": "",
"useManagedIdentity": false
}
},
"masterProfile": {
"count": 1,
"dnsPrefix": "",
"vmSize": "Standard_D2_v3"
},
"agentPoolProfiles": [
{
"name": "windowspool2",
"count": 1,
"vmSize": "Standard_D2_v3",
"availabilityProfile": "VirtualMachineScaleSets",
"osType": "Windows",
"osDiskSizeGB": 128
}
],
"windowsProfile": {
"windowsPublisher": "MicrosoftWindowsServer",
"windowsOffer": "WindowsServer",
"windowsSku": "Datacenter-Core-2004-with-Containers-smalldisk",
"imageVersion": "latest",
"adminUsername": "azureuser",
"adminPassword": "replacepassword1234$"
},
"linuxProfile": {
"adminUsername": "azureuser",
"ssh": {
"publicKeys": [
{
"keyData": ""
}
]
}
}
}
}

Просмотреть файл

@ -435,7 +435,12 @@ func (a *Properties) validateAgentPoolProfiles(isUpdate bool) error {
// validate os type is linux if dual stack feature is enabled
if a.FeatureFlags.IsIPv6DualStackEnabled() || a.FeatureFlags.IsIPv6OnlyEnabled() {
if agentPoolProfile.OSType == Windows {
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 a.FeatureFlags.IsIPv6DualStackEnabled() && !common.IsKubernetesVersionGe(a.OrchestratorProfile.OrchestratorVersion, "1.19.0") {
return errors.Errorf("Dual stack IPv6 feature is supported on Windows only from Kubernetes version 1.19, but OrchestratorProfile.OrchestratorVersion is '%s'", a.OrchestratorProfile.OrchestratorVersion)
}
if a.FeatureFlags.IsIPv6OnlyEnabled() {
return errors.Errorf("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 == Flatcar {
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)

Просмотреть файл

@ -4146,20 +4146,30 @@ func TestValidateProperties_OrchestratorSpecificProperties(t *testing.T) {
}
})
t.Run("Should not support os type other than linux for single stack ipv6 and dual stack feature", func(t *testing.T) {
t.Run("Should not support os type other than linux for single stack ipv6 and versions less than 1.19 for dual stack feature", func(t *testing.T) {
t.Parallel()
cs := getK8sDefaultContainerService(true)
masterProfile := cs.Properties.MasterProfile
masterProfile.Distro = Ubuntu
agentPoolProfiles := cs.Properties.AgentPoolProfiles
agentPoolProfiles[0].OSType = Windows
cs.Properties.FeatureFlags = &FeatureFlags{EnableIPv6DualStack: true}
cs.Properties.OrchestratorProfile.OrchestratorVersion = "1.18"
expectedMsg := fmt.Sprintf("Dual stack IPv6 feature is supported on Windows only from Kubernetes version 1.19, but OrchestratorProfile.OrchestratorVersion is '%s'", cs.Properties.OrchestratorProfile.OrchestratorVersion)
if err := cs.Properties.validateAgentPoolProfiles(false); err.Error() != expectedMsg {
t.Errorf("expected error with message : %s, but got %s", expectedMsg, err.Error())
}
cs.Properties.FeatureFlags = &FeatureFlags{EnableIPv6Only: true}
expectedMsg = fmt.Sprintf("Single stack IPv6 feature is supported only with Linux, but agent pool '%s' is of os type %s", agentPoolProfiles[0].Name, agentPoolProfiles[0].OSType)
if err := cs.Properties.validateAgentPoolProfiles(false); err.Error() != expectedMsg {
t.Errorf("expected error with message : %s, but got %s", expectedMsg, err.Error())
}
for _, featureFlags := range []FeatureFlags{{EnableIPv6DualStack: true}, {EnableIPv6Only: true}} {
cs.Properties.FeatureFlags = &featureFlags
masterProfile := cs.Properties.MasterProfile
masterProfile.Distro = Ubuntu
agentPoolProfiles := cs.Properties.AgentPoolProfiles
agentPoolProfiles[0].OSType = Windows
expectedMsg := fmt.Sprintf("Dual stack and single stack IPv6 feature is supported only with Linux, but agent pool '%s' is of os type %s", agentPoolProfiles[0].Name, agentPoolProfiles[0].OSType)
if err := cs.Properties.validateAgentPoolProfiles(false); err.Error() != expectedMsg {
t.Errorf("expected error with message : %s, but got %s", expectedMsg, err.Error())
}
agentPoolProfiles[0].OSType = Linux
agentPoolProfiles[0].Distro = Flatcar
@ -4168,7 +4178,6 @@ func TestValidateProperties_OrchestratorSpecificProperties(t *testing.T) {
t.Errorf("expected error with message : %s, but got %s", expectedMsg, err.Error())
}
}
})
}

Просмотреть файл

@ -15,6 +15,7 @@ $global:NetworkMode = "L2Bridge"
$global:NetworkPlugin = $Global:ClusterConfiguration.Cni.Name
$global:ContainerRuntime = $Global:ClusterConfiguration.Cri.Name
$UseContainerD = ($global:ContainerRuntime -eq "containerd")
$IsDualStackEnabled = $Global:ClusterConfiguration.Kubernetes.Kubeproxy.FeatureGates -contains "IPv6DualStack=true"
filter Timestamp { "$(Get-Date -Format o): $_" }
@ -45,6 +46,35 @@ if ($global:EnableHostsConfigAgent) {
Stop-Service hosts-config-agent
}
# Due to a bug in hns there is a race where it picks up the incorrect IPv6 address from the node in some cases.
# Hns service has to be restarted after the node internal IPv6 address is available when dual-stack is enabled.
# TODO Remove this once the bug is fixed in hns.
function Restart-HnsService {
do {
Start-Sleep -Seconds 1
$nodeInternalIPv6Address = (Get-NetIPAddress | Where-Object {$_.PrefixOrigin -eq "Dhcp" -and $_.AddressFamily -eq "IPv6"}).IPAddress
} while ($nodeInternalIPv6Address -eq $null)
Write-Log "Got node internal IPv6 address: $nodeInternalIPv6Address"
$hnsManagementIPv6Address = (Get-HnsNetwork | Where-Object {$_.IPv6 -eq $true}).ManagementIPv6
Write-Log "Got hns ManagementIPv6: $hnsManagementIPv6Address"
if ($hnsManagementIPv6Address -ne $nodeInternalIPv6Address) {
Restart-Service hns
Write-Log "Restarted hns service"
$hnsManagementIPv6Address = (Get-HnsNetwork | Where-Object {$_.IPv6 -eq $true}).ManagementIPv6
Write-Log "Got hns ManagementIPv6: $hnsManagementIPv6Address after restart"
}
else {
Write-Log "Hns network has correct IPv6 address, not restarting"
}
}
if ($IsDualStackEnabled) {
Restart-HnsService
}
#
# Perform cleanup
#