diff --git a/cmd/kube-proxy/app/server_linux.go b/cmd/kube-proxy/app/server_linux.go index 24347ca4e9e..748096992e4 100644 --- a/cmd/kube-proxy/app/server_linux.go +++ b/cmd/kube-proxy/app/server_linux.go @@ -182,7 +182,7 @@ func (s *ProxyServer) createProxier(ctx context.Context, config *kubeproxyconfig ipts := utiliptables.NewBestEffort() logger.Info("Using ipvs Proxier") - message := "The ipvs proxier is now deprecated and may be removed in a future release. Please use 'nftables' instead." + message := "The ipvs proxier has been deprecated and will be disabled by default in Kubernetes 1.40 and removed in Kubernetes 1.43. Migrate to the 'nftables' proxier instead." logger.Error(nil, message) s.Recorder.Eventf(s.NodeRef, nil, v1.EventTypeWarning, "IPVSDeprecation", "StartKubeProxy", message) if dualStack { diff --git a/pkg/features/kube_features.go b/pkg/features/kube_features.go index ef1fdf7bd29..74372ce6af5 100644 --- a/pkg/features/kube_features.go +++ b/pkg/features/kube_features.go @@ -470,6 +470,12 @@ const ( // Allows to delegate reconciliation of a Job object to an external controller. JobManagedBy featuregate.Feature = "JobManagedBy" + // owner: @adrianmoisey @danwinship + // kep: https://kep.k8s.io/5495 + // + // Allow use of IPVS mode in kube-proxy + KubeProxyIPVS featuregate.Feature = "KubeProxyIPVS" + // owner: @marquiz // kep: http://kep.k8s.io/4033 // @@ -1494,6 +1500,10 @@ var defaultVersionedKubernetesFeatureGates = map[featuregate.Feature]featuregate {Version: version.MustParse("1.35"), Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.38 }, + KubeProxyIPVS: { + {Version: version.MustParse("1.11"), Default: true, PreRelease: featuregate.GA}, + }, + KubeletCgroupDriverFromCRI: { {Version: version.MustParse("1.28"), Default: false, PreRelease: featuregate.Alpha}, {Version: version.MustParse("1.31"), Default: true, PreRelease: featuregate.Beta}, @@ -2362,6 +2372,8 @@ var defaultKubernetesFeatureGateDependencies = map[featuregate.Feature][]feature JobManagedBy: {}, + KubeProxyIPVS: {}, + KubeletCgroupDriverFromCRI: {}, KubeletCrashLoopBackOffMax: {}, diff --git a/pkg/proxy/apis/config/validation/validation.go b/pkg/proxy/apis/config/validation/validation.go index 6ce772d2f2c..d3a7cc53df6 100644 --- a/pkg/proxy/apis/config/validation/validation.go +++ b/pkg/proxy/apis/config/validation/validation.go @@ -30,6 +30,7 @@ import ( logsapi "k8s.io/component-base/logs/api/v1" "k8s.io/component-base/metrics" apivalidation "k8s.io/kubernetes/pkg/apis/core/validation" + "k8s.io/kubernetes/pkg/features" kubeproxyconfig "k8s.io/kubernetes/pkg/proxy/apis/config" netutils "k8s.io/utils/net" ) @@ -171,10 +172,13 @@ func validateProxyMode(mode kubeproxyconfig.ProxyMode, fldPath *field.Path) fiel func validateProxyModeLinux(mode kubeproxyconfig.ProxyMode, fldPath *field.Path) field.ErrorList { validModes := sets.New[string]( string(kubeproxyconfig.ProxyModeIPTables), - string(kubeproxyconfig.ProxyModeIPVS), string(kubeproxyconfig.ProxyModeNFTables), ) + if utilfeature.DefaultFeatureGate.Enabled(features.KubeProxyIPVS) { + validModes.Insert(string(kubeproxyconfig.ProxyModeIPVS)) + } + if mode == "" || validModes.Has(string(mode)) { return nil } diff --git a/pkg/proxy/apis/config/validation/validation_test.go b/pkg/proxy/apis/config/validation/validation_test.go index 6275d4ee399..a93b7895b01 100644 --- a/pkg/proxy/apis/config/validation/validation_test.go +++ b/pkg/proxy/apis/config/validation/validation_test.go @@ -27,8 +27,11 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/validation/field" + utilfeature "k8s.io/apiserver/pkg/util/feature" componentbaseconfig "k8s.io/component-base/config" + featuregatetesting "k8s.io/component-base/featuregate/testing" logsapi "k8s.io/component-base/logs/api/v1" + "k8s.io/kubernetes/pkg/features" kubeproxyconfig "k8s.io/kubernetes/pkg/proxy/apis/config" "k8s.io/utils/ptr" ) @@ -429,8 +432,9 @@ func TestValidateProxyMode(t *testing.T) { func testValidateProxyModeLinux(t *testing.T) { newPath := field.NewPath("KubeProxyConfiguration") for name, testCase := range map[string]struct { - mode kubeproxyconfig.ProxyMode - expectedErrs field.ErrorList + mode kubeproxyconfig.ProxyMode + expectedErrs field.ErrorList + enableKubeProxyIPVS *bool }{ "blank mode should default": { mode: kubeproxyconfig.ProxyMode(""), @@ -438,12 +442,38 @@ func testValidateProxyModeLinux(t *testing.T) { "iptables is allowed": { mode: kubeproxyconfig.ProxyModeIPTables, }, - "ipvs is allowed": { + "iptables is allowed - with KubeProxyIPVS feature gate enabled": { + mode: kubeproxyconfig.ProxyModeIPTables, + enableKubeProxyIPVS: new(true), + }, + "iptables is allowed - with KubeProxyIPVS feature gate disabled": { + mode: kubeproxyconfig.ProxyModeIPTables, + enableKubeProxyIPVS: new(false), + }, + "ipvs is allowed - with KubeProxyIPVS feature gate in default state": { mode: kubeproxyconfig.ProxyModeIPVS, }, + "ipvs is allowed - with KubeProxyIPVS feature gate enabled": { + mode: kubeproxyconfig.ProxyModeIPVS, + enableKubeProxyIPVS: new(true), + }, + "ipvs is not allowed - with KubeProxyIPVS feature gate disabled": { + mode: kubeproxyconfig.ProxyModeIPVS, + enableKubeProxyIPVS: new(false), + expectedErrs: field.ErrorList{field.Invalid(newPath.Child("ProxyMode"), "ipvs", "must be iptables, nftables or blank (blank means the best-available proxy [currently iptables])")}, + }, "nftables is allowed": { mode: kubeproxyconfig.ProxyModeNFTables, }, + "nftables is allowed - with KubeProxyIPVS feature gate enabled": { + mode: kubeproxyconfig.ProxyModeNFTables, + enableKubeProxyIPVS: new(true), + }, + "nftables is allowed - with KubeProxyIPVS feature gate disabled": { + mode: kubeproxyconfig.ProxyModeNFTables, + enableKubeProxyIPVS: new(false), + }, + "winkernel is not allowed": { mode: kubeproxyconfig.ProxyModeKernelspace, expectedErrs: field.ErrorList{field.Invalid(newPath.Child("ProxyMode"), "kernelspace", "must be iptables, ipvs, nftables or blank (blank means the best-available proxy [currently iptables])")}, @@ -454,6 +484,12 @@ func testValidateProxyModeLinux(t *testing.T) { }, } { t.Run(name, func(t *testing.T) { + if testCase.enableKubeProxyIPVS != nil { + featuregatetesting.SetFeatureGatesDuringTest(t, utilfeature.DefaultFeatureGate, featuregatetesting.FeatureOverrides{ + features.KubeProxyIPVS: *testCase.enableKubeProxyIPVS, + }) + } + errs := validateProxyMode(testCase.mode, newPath) assert.Equal(t, testCase.expectedErrs, errs, "did not get expected validation errors") }) diff --git a/test/compatibility_lifecycle/reference/feature_list.md b/test/compatibility_lifecycle/reference/feature_list.md index a5d98deb073..f3ac652a3ab 100644 --- a/test/compatibility_lifecycle/reference/feature_list.md +++ b/test/compatibility_lifecycle/reference/feature_list.md @@ -99,6 +99,7 @@ | InformerResourceVersion | :ballot_box_with_check: 1.35+ | | 1.30–1.34 | | 1.35– | | | [code](https://cs.k8s.io/?q=%5CbInformerResourceVersion%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbInformerResourceVersion%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) | | JobManagedBy | :ballot_box_with_check: 1.32+ | :closed_lock_with_key: 1.35+ | 1.30–1.31 | 1.32–1.34 | 1.35– | | | [code](https://cs.k8s.io/?q=%5CbJobManagedBy%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbJobManagedBy%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) | | KMSv1 | :ballot_box_with_check: 1.0+ | | | | 1.0–1.27 | 1.28– | | [code](https://cs.k8s.io/?q=%5CbKMSv1%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbKMSv1%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) | +| KubeProxyIPVS | :ballot_box_with_check: 1.11+ | | | | 1.11– | | | [code](https://cs.k8s.io/?q=%5CbKubeProxyIPVS%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbKubeProxyIPVS%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) | | KubeletCgroupDriverFromCRI | :ballot_box_with_check: 1.31+ | :closed_lock_with_key: 1.34+ | 1.28–1.30 | 1.31–1.33 | 1.34– | | | [code](https://cs.k8s.io/?q=%5CbKubeletCgroupDriverFromCRI%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbKubeletCgroupDriverFromCRI%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) | | KubeletCrashLoopBackOffMax | :ballot_box_with_check: 1.35+ | | 1.32–1.34 | 1.35– | | | | [code](https://cs.k8s.io/?q=%5CbKubeletCrashLoopBackOffMax%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbKubeletCrashLoopBackOffMax%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) | | KubeletEnsureSecretPulledImages | :ballot_box_with_check: 1.35+ | | 1.33–1.34 | 1.35– | | | | [code](https://cs.k8s.io/?q=%5CbKubeletEnsureSecretPulledImages%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/kubernetes) [KEPs](https://cs.k8s.io/?q=%5CbKubeletEnsureSecretPulledImages%5Cb&i=nope&files=&excludeFiles=CHANGELOG&repos=kubernetes/enhancements) | diff --git a/test/compatibility_lifecycle/reference/versioned_feature_list.yaml b/test/compatibility_lifecycle/reference/versioned_feature_list.yaml index 7e516431e3e..ea12ee19a46 100644 --- a/test/compatibility_lifecycle/reference/versioned_feature_list.yaml +++ b/test/compatibility_lifecycle/reference/versioned_feature_list.yaml @@ -987,6 +987,12 @@ lockToDefault: true preRelease: GA version: "1.34" +- name: KubeProxyIPVS + versionedSpecs: + - default: true + lockToDefault: false + preRelease: GA + version: "1.11" - name: ListFromCacheSnapshot versionedSpecs: - default: false