Skip to content

Commit 458cc04

Browse files
author
Kubernetes Submit Queue
authored
Merge pull request #46254 from mtaufen/dkcfg
Automatic merge from submit-queue (batch tested with PRs 50016, 49583, 49930, 46254, 50337) Alpha Dynamic Kubelet Configuration Feature: kubernetes/enhancements#281 This proposal contains the alpha implementation of the Dynamic Kubelet Configuration feature proposed in ~#29459~ [community/contributors/design-proposals/dynamic-kubelet-configuration.md](https://github.com/kubernetes/community/blob/master/contributors/design-proposals/dynamic-kubelet-configuration.md). Please note: - ~The proposal doc is not yet up to date with this implementation, there are some subtle differences and some more significant ones. I will update the proposal doc to match by tomorrow afternoon.~ - ~This obviously needs more tests. I plan to write several O(soon). Since it's alpha and feature-gated, I'm decoupling this review from the review of the tests.~ I've beefed up the unit tests, though there is still plenty of testing to be done. - ~I'm temporarily holding off on updating the generated docs, api specs, etc, for the sake of my reviewers 😄~ these files now live in a separate commit; the first commit is the one to review. /cc @dchen1107 @vishh @bgrant0607 @thockin @derekwaynecarr ```release-note Adds (alpha feature) the ability to dynamically configure Kubelets by enabling the DynamicKubeletConfig feature gate, posting a ConfigMap to the API server, and setting the spec.configSource field on Node objects. See the proposal at https://github.com/kubernetes/community/blob/master/contributors/design-proposals/dynamic-kubelet-configuration.md for details. ```
2 parents 212928a + 3785443 commit 458cc04

File tree

98 files changed

+8047
-1566
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

98 files changed

+8047
-1566
lines changed

api/openapi-spec/swagger.json

+27
Original file line numberDiff line numberDiff line change
@@ -54865,6 +54865,29 @@
5486554865
}
5486654866
}
5486754867
},
54868+
"io.k8s.api.core.v1.NodeConfigSource": {
54869+
"description": "NodeConfigSource specifies a source of node configuration. Exactly one subfield (excluding metadata) must be non-nil.",
54870+
"properties": {
54871+
"apiVersion": {
54872+
"description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources",
54873+
"type": "string"
54874+
},
54875+
"configMapRef": {
54876+
"$ref": "#/definitions/io.k8s.api.core.v1.ObjectReference"
54877+
},
54878+
"kind": {
54879+
"description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds",
54880+
"type": "string"
54881+
}
54882+
},
54883+
"x-kubernetes-group-version-kind": [
54884+
{
54885+
"group": "",
54886+
"kind": "NodeConfigSource",
54887+
"version": "v1"
54888+
}
54889+
]
54890+
},
5486854891
"io.k8s.api.core.v1.NodeDaemonEndpoints": {
5486954892
"description": "NodeDaemonEndpoints lists ports opened by daemons running on the Node.",
5487054893
"properties": {
@@ -54967,6 +54990,10 @@
5496754990
"io.k8s.api.core.v1.NodeSpec": {
5496854991
"description": "NodeSpec describes the attributes that a node is created with.",
5496954992
"properties": {
54993+
"configSource": {
54994+
"description": "If specified, the source to get node configuration from The DynamicKubeletConfig feature gate must be enabled for the Kubelet to use this field",
54995+
"$ref": "#/definitions/io.k8s.api.core.v1.NodeConfigSource"
54996+
},
5497054997
"externalID": {
5497154998
"description": "External ID of the node assigned by some machine database (e.g. a cloud provider). Deprecated.",
5497254999
"type": "string"

api/swagger-spec/v1.json

+21
Original file line numberDiff line numberDiff line change
@@ -18318,6 +18318,10 @@
1831818318
"$ref": "v1.Taint"
1831918319
},
1832018320
"description": "If specified, the node's taints."
18321+
},
18322+
"configSource": {
18323+
"$ref": "v1.NodeConfigSource",
18324+
"description": "If specified, the source to get node configuration from The DynamicKubeletConfig feature gate must be enabled for the Kubelet to use this field"
1832118325
}
1832218326
}
1832318327
},
@@ -18347,6 +18351,23 @@
1834718351
}
1834818352
}
1834918353
},
18354+
"v1.NodeConfigSource": {
18355+
"id": "v1.NodeConfigSource",
18356+
"description": "NodeConfigSource specifies a source of node configuration. Exactly one subfield (excluding metadata) must be non-nil.",
18357+
"properties": {
18358+
"kind": {
18359+
"type": "string",
18360+
"description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds"
18361+
},
18362+
"apiVersion": {
18363+
"type": "string",
18364+
"description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources"
18365+
},
18366+
"configMapRef": {
18367+
"$ref": "v1.ObjectReference"
18368+
}
18369+
}
18370+
},
1835018371
"v1.NodeStatus": {
1835118372
"id": "v1.NodeStatus",
1835218373
"description": "NodeStatus is information about the current status of a node.",

cmd/hyperkube/kubelet.go

+7-3
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,12 @@ import (
2323

2424
// NewKubelet creates a new hyperkube Server object that includes the
2525
// description and flags.
26-
func NewKubelet() *Server {
27-
s := options.NewKubeletServer()
26+
func NewKubelet() (*Server, error) {
27+
s, err := options.NewKubeletServer()
28+
if err != nil {
29+
return nil, err
30+
}
31+
2832
hks := Server{
2933
name: "kubelet",
3034
SimpleUsage: "kubelet",
@@ -39,5 +43,5 @@ func NewKubelet() *Server {
3943
},
4044
}
4145
s.AddFlags(hks.Flags())
42-
return &hks
46+
return &hks, nil
4347
}

cmd/hyperkube/main.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ limitations under the License.
2020
package main
2121

2222
import (
23+
"fmt"
2324
"os"
2425

2526
_ "k8s.io/kubernetes/pkg/client/metrics/prometheus" // for client metric registration
@@ -36,7 +37,12 @@ func main() {
3637
hk.AddServer(NewKubeAPIServer())
3738
hk.AddServer(NewKubeControllerManager())
3839
hk.AddServer(NewScheduler())
39-
hk.AddServer(NewKubelet())
40+
if kubelet, err := NewKubelet(); err != nil {
41+
fmt.Fprintf(os.Stderr, "error: %v\n", err)
42+
os.Exit(1)
43+
} else {
44+
hk.AddServer(kubelet)
45+
}
4046
hk.AddServer(NewKubeProxy())
4147
hk.AddServer(NewKubeAggregator())
4248

cmd/kubelet/BUILD

+4
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,14 @@ go_library(
2323
deps = [
2424
"//cmd/kubelet/app:go_default_library",
2525
"//cmd/kubelet/app/options:go_default_library",
26+
"//pkg/apis/componentconfig:go_default_library",
2627
"//pkg/client/metrics/prometheus:go_default_library",
28+
"//pkg/features:go_default_library",
29+
"//pkg/kubelet/kubeletconfig:go_default_library",
2730
"//pkg/version/prometheus:go_default_library",
2831
"//pkg/version/verflag:go_default_library",
2932
"//vendor/github.com/spf13/pflag:go_default_library",
33+
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
3034
"//vendor/k8s.io/apiserver/pkg/util/flag:go_default_library",
3135
"//vendor/k8s.io/apiserver/pkg/util/logs:go_default_library",
3236
],

cmd/kubelet/app/BUILD

+1-2
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ go_library(
5252
"//pkg/kubelet/dockershim/remote:go_default_library",
5353
"//pkg/kubelet/eviction:go_default_library",
5454
"//pkg/kubelet/eviction/api:go_default_library",
55+
"//pkg/kubelet/kubeletconfig:go_default_library",
5556
"//pkg/kubelet/network:go_default_library",
5657
"//pkg/kubelet/network/cni:go_default_library",
5758
"//pkg/kubelet/network/kubenet:go_default_library",
@@ -100,8 +101,6 @@ go_library(
100101
"//vendor/golang.org/x/exp/inotify:go_default_library",
101102
"//vendor/k8s.io/api/core/v1:go_default_library",
102103
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
103-
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
104-
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
105104
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
106105
"//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
107106
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",

cmd/kubelet/app/options/BUILD

+2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ go_library(
1919
"//pkg/apis/componentconfig:go_default_library",
2020
"//pkg/apis/componentconfig/install:go_default_library",
2121
"//pkg/apis/componentconfig/v1alpha1:go_default_library",
22+
"//pkg/apis/componentconfig/validation:go_default_library",
23+
"//pkg/features:go_default_library",
2224
"//pkg/util/taints:go_default_library",
2325
"//vendor/github.com/spf13/pflag:go_default_library",
2426
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",

cmd/kubelet/app/options/options.go

+103-25
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ limitations under the License.
1818
package options
1919

2020
import (
21+
"fmt"
2122
_ "net/http/pprof"
2223
"strings"
2324

@@ -26,9 +27,10 @@ import (
2627
utilflag "k8s.io/apiserver/pkg/util/flag"
2728
"k8s.io/kubernetes/pkg/api"
2829
"k8s.io/kubernetes/pkg/apis/componentconfig"
29-
// Need to make sure the componentconfig api is installed so defaulting funcs work
30-
_ "k8s.io/kubernetes/pkg/apis/componentconfig/install"
30+
_ "k8s.io/kubernetes/pkg/apis/componentconfig/install" // Need to make sure the componentconfig api is installed so defaulting funcs work
3131
"k8s.io/kubernetes/pkg/apis/componentconfig/v1alpha1"
32+
componentconfigvalidation "k8s.io/kubernetes/pkg/apis/componentconfig/validation"
33+
"k8s.io/kubernetes/pkg/features"
3234
utiltaints "k8s.io/kubernetes/pkg/util/taints"
3335

3436
"github.com/spf13/pflag"
@@ -79,6 +81,72 @@ type KubeletFlags struct {
7981

8082
// Container-runtime-specific options.
8183
ContainerRuntimeOptions
84+
85+
// certDirectory is the directory where the TLS certs are located (by
86+
// default /var/run/kubernetes). If tlsCertFile and tlsPrivateKeyFile
87+
// are provided, this flag will be ignored.
88+
CertDirectory string
89+
90+
// cloudProvider is the provider for cloud services.
91+
// +optional
92+
CloudProvider string
93+
94+
// cloudConfigFile is the path to the cloud provider configuration file.
95+
// +optional
96+
CloudConfigFile string
97+
98+
// rootDirectory is the directory path to place kubelet files (volume
99+
// mounts,etc).
100+
RootDirectory string
101+
102+
// The Kubelet will use this directory for checkpointing downloaded configurations and tracking configuration health.
103+
// The Kubelet will create this directory if it does not already exist.
104+
// The path may be absolute or relative; relative paths are under the Kubelet's current working directory.
105+
// Providing this flag enables dynamic kubelet configuration.
106+
// To use this flag, the DynamicKubeletConfig feature gate must be enabled.
107+
DynamicConfigDir flag.StringFlag
108+
109+
// The Kubelet will look in this directory for an init configuration.
110+
// The path may be absolute or relative; relative paths are under the Kubelet's current working directory.
111+
// Omit this flag to use the combination of built-in default configuration values and flags.
112+
// To use this flag, the DynamicKubeletConfig feature gate must be enabled.
113+
InitConfigDir flag.StringFlag
114+
}
115+
116+
// NewKubeletFlags will create a new KubeletFlags with default values
117+
func NewKubeletFlags() *KubeletFlags {
118+
return &KubeletFlags{
119+
// TODO(#41161:v1.10.0): Remove the default kubeconfig path and --require-kubeconfig.
120+
RequireKubeConfig: false,
121+
KubeConfig: flag.NewStringFlag("/var/lib/kubelet/kubeconfig"),
122+
ContainerRuntimeOptions: *NewContainerRuntimeOptions(),
123+
CertDirectory: "/var/run/kubernetes",
124+
CloudProvider: v1alpha1.AutoDetectCloudProvider,
125+
RootDirectory: v1alpha1.DefaultRootDir,
126+
}
127+
}
128+
129+
func ValidateKubeletFlags(f *KubeletFlags) error {
130+
// ensure that nobody sets DynamicConfigDir if the dynamic config feature gate is turned off
131+
if f.DynamicConfigDir.Provided() && !utilfeature.DefaultFeatureGate.Enabled(features.DynamicKubeletConfig) {
132+
return fmt.Errorf("the DynamicKubeletConfig feature gate must be enabled in order to use the --dynamic-config-dir flag")
133+
}
134+
// ensure that nobody sets InitConfigDir if the dynamic config feature gate is turned off
135+
if f.InitConfigDir.Provided() && !utilfeature.DefaultFeatureGate.Enabled(features.DynamicKubeletConfig) {
136+
return fmt.Errorf("the DynamicKubeletConfig feature gate must be enabled in order to use the --init-config-dir flag")
137+
}
138+
return nil
139+
}
140+
141+
// NewKubeletConfiguration will create a new KubeletConfiguration with default values
142+
func NewKubeletConfiguration() (*componentconfig.KubeletConfiguration, error) {
143+
versioned := &v1alpha1.KubeletConfiguration{}
144+
api.Scheme.Default(versioned)
145+
config := &componentconfig.KubeletConfiguration{}
146+
if err := api.Scheme.Convert(versioned, config, nil); err != nil {
147+
return nil, err
148+
}
149+
return config, nil
82150
}
83151

84152
// KubeletServer encapsulates all of the parameters necessary for starting up
@@ -89,29 +157,33 @@ type KubeletServer struct {
89157
}
90158

91159
// NewKubeletServer will create a new KubeletServer with default values.
92-
func NewKubeletServer() *KubeletServer {
93-
versioned := &v1alpha1.KubeletConfiguration{}
94-
api.Scheme.Default(versioned)
95-
config := componentconfig.KubeletConfiguration{}
96-
api.Scheme.Convert(versioned, &config, nil)
97-
return &KubeletServer{
98-
KubeletFlags: KubeletFlags{
99-
// TODO(#41161:v1.10.0): Remove the default kubeconfig path and --require-kubeconfig.
100-
RequireKubeConfig: false,
101-
KubeConfig: flag.NewStringFlag("/var/lib/kubelet/kubeconfig"),
102-
ContainerRuntimeOptions: *NewContainerRuntimeOptions(),
103-
},
104-
KubeletConfiguration: config,
160+
func NewKubeletServer() (*KubeletServer, error) {
161+
config, err := NewKubeletConfiguration()
162+
if err != nil {
163+
return nil, err
105164
}
165+
return &KubeletServer{
166+
KubeletFlags: *NewKubeletFlags(),
167+
KubeletConfiguration: *config,
168+
}, nil
106169
}
107170

108-
type kubeletConfiguration componentconfig.KubeletConfiguration
171+
// validateKubeletServer validates configuration of KubeletServer and returns an error if the input configuration is invalid
172+
func ValidateKubeletServer(s *KubeletServer) error {
173+
// please add any KubeletConfiguration validation to the componentconfigvalidation.ValidateKubeletConfiguration function
174+
if err := componentconfigvalidation.ValidateKubeletConfiguration(&s.KubeletConfiguration); err != nil {
175+
return err
176+
}
177+
if err := ValidateKubeletFlags(&s.KubeletFlags); err != nil {
178+
return err
179+
}
180+
return nil
181+
}
109182

110183
// AddFlags adds flags for a specific KubeletServer to the specified FlagSet
111184
func (s *KubeletServer) AddFlags(fs *pflag.FlagSet) {
112-
var kc *kubeletConfiguration = (*kubeletConfiguration)(&s.KubeletConfiguration)
113185
s.KubeletFlags.AddFlags(fs)
114-
kc.addFlags(fs)
186+
AddKubeletConfigFlags(fs, &s.KubeletConfiguration)
115187
}
116188

117189
// AddFlags adds flags for a specific KubeletFlags to the specified FlagSet
@@ -140,10 +212,21 @@ func (f *KubeletFlags) AddFlags(fs *pflag.FlagSet) {
140212
fs.StringVar(&f.NodeIP, "node-ip", f.NodeIP, "IP address of the node. If set, kubelet will use this IP address for the node")
141213

142214
fs.StringVar(&f.ProviderID, "provider-id", f.ProviderID, "Unique identifier for identifying the node in a machine database, i.e cloudprovider")
215+
216+
fs.StringVar(&f.CertDirectory, "cert-dir", f.CertDirectory, "The directory where the TLS certs are located. "+
217+
"If --tls-cert-file and --tls-private-key-file are provided, this flag will be ignored.")
218+
219+
fs.StringVar(&f.CloudProvider, "cloud-provider", f.CloudProvider, "The provider for cloud services. By default, kubelet will attempt to auto-detect the cloud provider. Specify empty string for running with no cloud provider.")
220+
fs.StringVar(&f.CloudConfigFile, "cloud-config", f.CloudConfigFile, "The path to the cloud provider configuration file. Empty string for no configuration file.")
221+
222+
fs.StringVar(&f.RootDirectory, "root-dir", f.RootDirectory, "Directory path for managing kubelet files (volume mounts,etc).")
223+
224+
fs.Var(&f.DynamicConfigDir, "dynamic-config-dir", "The Kubelet will use this directory for checkpointing downloaded configurations and tracking configuration health. The Kubelet will create this directory if it does not already exist. The path may be absolute or relative; relative paths start at the Kubelet's current working directory. Providing this flag enables dynamic Kubelet configuration. Presently, you must also enable the DynamicKubeletConfig feature gate to pass this flag.")
225+
fs.Var(&f.InitConfigDir, "init-config-dir", "The Kubelet will look in this directory for the init configuration. The path may be absolute or relative; relative paths start at the Kubelet's current working directory. Omit this argument to use the built-in default configuration values. Presently, you must also enable the DynamicKubeletConfig feature gate to pass this flag.")
143226
}
144227

145-
// addFlags adds flags for a specific componentconfig.KubeletConfiguration to the specified FlagSet
146-
func (c *kubeletConfiguration) addFlags(fs *pflag.FlagSet) {
228+
// AddKubeletConfigFlags adds flags for a specific componentconfig.KubeletConfiguration to the specified FlagSet
229+
func AddKubeletConfigFlags(fs *pflag.FlagSet, c *componentconfig.KubeletConfiguration) {
147230
fs.BoolVar(&c.FailSwapOn, "fail-swap-on", true, "Makes the Kubelet fail to start if swap is enabled on the node. ")
148231
fs.BoolVar(&c.FailSwapOn, "experimental-fail-swap-on", true, "DEPRECATED: please use --fail-swap-on instead.")
149232
fs.MarkDeprecated("experimental-fail-swap-on", "This flag is deprecated and will be removed in future releases. please use --fail-swap-on instead.")
@@ -186,10 +269,7 @@ func (c *kubeletConfiguration) addFlags(fs *pflag.FlagSet) {
186269
"If --tls-cert-file and --tls-private-key-file are not provided, a self-signed certificate and key "+
187270
"are generated for the public address and saved to the directory passed to --cert-dir.")
188271
fs.StringVar(&c.TLSPrivateKeyFile, "tls-private-key-file", c.TLSPrivateKeyFile, "File containing x509 private key matching --tls-cert-file.")
189-
fs.StringVar(&c.CertDirectory, "cert-dir", c.CertDirectory, "The directory where the TLS certs are located. "+
190-
"If --tls-cert-file and --tls-private-key-file are provided, this flag will be ignored.")
191272

192-
fs.StringVar(&c.RootDirectory, "root-dir", c.RootDirectory, "Directory path for managing kubelet files (volume mounts,etc).")
193273
fs.StringVar(&c.SeccompProfileRoot, "seccomp-profile-root", c.SeccompProfileRoot, "Directory path for seccomp profiles.")
194274
fs.BoolVar(&c.AllowPrivileged, "allow-privileged", c.AllowPrivileged, "If true, allow containers to request privileged mode.")
195275
fs.StringSliceVar(&c.HostNetworkSources, "host-network-sources", c.HostNetworkSources, "Comma-separated list of sources from which the Kubelet allows pods to use of host network.")
@@ -227,8 +307,6 @@ func (c *kubeletConfiguration) addFlags(fs *pflag.FlagSet) {
227307
fs.Int32Var(&c.ImageGCLowThresholdPercent, "image-gc-low-threshold", c.ImageGCLowThresholdPercent, "The percent of disk usage before which image garbage collection is never run. Lowest disk usage to garbage collect to.")
228308
fs.DurationVar(&c.VolumeStatsAggPeriod.Duration, "volume-stats-agg-period", c.VolumeStatsAggPeriod.Duration, "Specifies interval for kubelet to calculate and cache the volume disk usage for all pods and volumes. To disable volume calculations, set to 0.")
229309
fs.StringVar(&c.VolumePluginDir, "volume-plugin-dir", c.VolumePluginDir, "<Warning: Alpha feature> The full path of the directory in which to search for additional third party volume plugins")
230-
fs.StringVar(&c.CloudProvider, "cloud-provider", c.CloudProvider, "The provider for cloud services. By default, kubelet will attempt to auto-detect the cloud provider. Specify empty string for running with no cloud provider.")
231-
fs.StringVar(&c.CloudConfigFile, "cloud-config", c.CloudConfigFile, "The path to the cloud provider configuration file. Empty string for no configuration file.")
232310
fs.StringVar(&c.FeatureGates, "feature-gates", c.FeatureGates, "A set of key=value pairs that describe feature gates for alpha/experimental features. "+
233311
"Options are:\n"+strings.Join(utilfeature.DefaultFeatureGate.KnownFeatures(), "\n"))
234312

0 commit comments

Comments
 (0)