diff --git a/cmd/kubelet/app/options/options.go b/cmd/kubelet/app/options/options.go index ee77b118cd3..cea23f9cf2d 100644 --- a/cmd/kubelet/app/options/options.go +++ b/cmd/kubelet/app/options/options.go @@ -166,7 +166,7 @@ func (s *KubeletServer) AddFlags(fs *pflag.FlagSet) { fs.BoolVar(&s.RegisterNode, "register-node", s.RegisterNode, "Register the node with the apiserver (defaults to true if --api-servers is set)") fs.StringVar(&s.ClusterDomain, "cluster-domain", s.ClusterDomain, "Domain for this cluster. If set, kubelet will configure all containers to search this domain in addition to the host's search domains") fs.StringVar(&s.MasterServiceNamespace, "master-service-namespace", s.MasterServiceNamespace, "The namespace from which the kubernetes master services should be injected into pods") - fs.StringVar(&s.ClusterDNS, "cluster-dns", s.ClusterDNS, "IP address for a cluster DNS server. This value is used for containers' DNS server in case of Pods with \"dnsPolicy=ClusterFirst\"") + fs.StringSliceVar(&s.ClusterDNS, "cluster-dns", s.ClusterDNS, "Comma-separated list of DNS server IP address. This value is used for containers DNS server in case of Pods with \"dnsPolicy=ClusterFirst\". Note: all DNS servers appearing in the list MUST serve the same set of records otherwise name resolution within the cluster may not work correctly. There is no guarantee as to which DNS server may be contacted for name resolution.") fs.DurationVar(&s.StreamingConnectionIdleTimeout.Duration, "streaming-connection-idle-timeout", s.StreamingConnectionIdleTimeout.Duration, "Maximum time a streaming connection can be idle before the connection is automatically closed. 0 indicates no timeout. Example: '5m'") fs.DurationVar(&s.NodeStatusUpdateFrequency.Duration, "node-status-update-frequency", s.NodeStatusUpdateFrequency.Duration, "Specifies how often kubelet posts node status to master. Note: be cautious when changing the constant, it must work with nodeMonitorGracePeriod in nodecontroller. Default: 10s") s.NodeLabels = make(map[string]string) diff --git a/pkg/apis/componentconfig/types.go b/pkg/apis/componentconfig/types.go index ed81643f8f9..4b47685b52b 100644 --- a/pkg/apis/componentconfig/types.go +++ b/pkg/apis/componentconfig/types.go @@ -239,10 +239,10 @@ type KubeletConfiguration struct { // masterServiceNamespace is The namespace from which the kubernetes // master services should be injected into pods. MasterServiceNamespace string - // clusterDNS is the IP address for a cluster DNS server. If set, kubelet - // will configure all containers to use this for DNS resolution in - // addition to the host's DNS servers - ClusterDNS string + // clusterDNS is a list of IP address for a cluster DNS server. If set, + // kubelet will configure all containers to use this for DNS resolution + // instead of the host's DNS servers + ClusterDNS []string // streamingConnectionIdleTimeout is the maximum time a streaming connection // can be idle before the connection is automatically closed. StreamingConnectionIdleTimeout metav1.Duration diff --git a/pkg/apis/componentconfig/v1alpha1/types.go b/pkg/apis/componentconfig/v1alpha1/types.go index 4fbc631ff1e..6ef3e439236 100644 --- a/pkg/apis/componentconfig/v1alpha1/types.go +++ b/pkg/apis/componentconfig/v1alpha1/types.go @@ -293,10 +293,10 @@ type KubeletConfiguration struct { // masterServiceNamespace is The namespace from which the kubernetes // master services should be injected into pods. MasterServiceNamespace string `json:"masterServiceNamespace"` - // clusterDNS is the IP address for a cluster DNS server. If set, kubelet - // will configure all containers to use this for DNS resolution in - // addition to the host's DNS servers - ClusterDNS string `json:"clusterDNS"` + // clusterDNS is a list of IP address for the cluster DNS server. If set, + // kubelet will configure all containers to use this for DNS resolution + // instead of the host's DNS servers + ClusterDNS []string `json:"clusterDNS"` // streamingConnectionIdleTimeout is the maximum time a streaming connection // can be idle before the connection is automatically closed. StreamingConnectionIdleTimeout metav1.Duration `json:"streamingConnectionIdleTimeout"` diff --git a/pkg/apis/componentconfig/v1alpha1/zz_generated.conversion.go b/pkg/apis/componentconfig/v1alpha1/zz_generated.conversion.go index 9cfd1349009..d2c09106672 100644 --- a/pkg/apis/componentconfig/v1alpha1/zz_generated.conversion.go +++ b/pkg/apis/componentconfig/v1alpha1/zz_generated.conversion.go @@ -376,7 +376,7 @@ func autoConvert_v1alpha1_KubeletConfiguration_To_componentconfig_KubeletConfigu } out.ClusterDomain = in.ClusterDomain out.MasterServiceNamespace = in.MasterServiceNamespace - out.ClusterDNS = in.ClusterDNS + out.ClusterDNS = *(*[]string)(unsafe.Pointer(&in.ClusterDNS)) out.StreamingConnectionIdleTimeout = in.StreamingConnectionIdleTimeout out.NodeStatusUpdateFrequency = in.NodeStatusUpdateFrequency out.ImageMinimumGCAge = in.ImageMinimumGCAge @@ -548,7 +548,7 @@ func autoConvert_componentconfig_KubeletConfiguration_To_v1alpha1_KubeletConfigu } out.ClusterDomain = in.ClusterDomain out.MasterServiceNamespace = in.MasterServiceNamespace - out.ClusterDNS = in.ClusterDNS + out.ClusterDNS = *(*[]string)(unsafe.Pointer(&in.ClusterDNS)) out.StreamingConnectionIdleTimeout = in.StreamingConnectionIdleTimeout out.NodeStatusUpdateFrequency = in.NodeStatusUpdateFrequency out.ImageMinimumGCAge = in.ImageMinimumGCAge diff --git a/pkg/apis/componentconfig/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/componentconfig/v1alpha1/zz_generated.deepcopy.go index e1bc1cd5c6d..65a8e9511dc 100644 --- a/pkg/apis/componentconfig/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/componentconfig/v1alpha1/zz_generated.deepcopy.go @@ -219,6 +219,11 @@ func DeepCopy_v1alpha1_KubeletConfiguration(in interface{}, out interface{}, c * *out = new(bool) **out = **in } + if in.ClusterDNS != nil { + in, out := &in.ClusterDNS, &out.ClusterDNS + *out = make([]string, len(*in)) + copy(*out, *in) + } if in.ImageGCHighThresholdPercent != nil { in, out := &in.ImageGCHighThresholdPercent, &out.ImageGCHighThresholdPercent *out = new(int32) diff --git a/pkg/apis/componentconfig/zz_generated.deepcopy.go b/pkg/apis/componentconfig/zz_generated.deepcopy.go index 3f2d9215f8d..244f49b03b9 100644 --- a/pkg/apis/componentconfig/zz_generated.deepcopy.go +++ b/pkg/apis/componentconfig/zz_generated.deepcopy.go @@ -193,6 +193,11 @@ func DeepCopy_componentconfig_KubeletConfiguration(in interface{}, out interface *out = make([]string, len(*in)) copy(*out, *in) } + if in.ClusterDNS != nil { + in, out := &in.ClusterDNS, &out.ClusterDNS + *out = make([]string, len(*in)) + copy(*out, *in) + } if in.RegisterWithTaints != nil { in, out := &in.RegisterWithTaints, &out.RegisterWithTaints *out = make([]api.Taint, len(*in)) diff --git a/pkg/generated/openapi/zz_generated.openapi.go b/pkg/generated/openapi/zz_generated.openapi.go index 05f99d18fd0..678db3e7843 100644 --- a/pkg/generated/openapi/zz_generated.openapi.go +++ b/pkg/generated/openapi/zz_generated.openapi.go @@ -10008,9 +10008,16 @@ func GetOpenAPIDefinitions(ref openapi.ReferenceCallback) map[string]openapi.Ope }, "clusterDNS": { SchemaProps: spec.SchemaProps{ - Description: "clusterDNS is the IP address for a cluster DNS server. If set, kubelet will configure all containers to use this for DNS resolution in addition to the host's DNS servers", - Type: []string{"string"}, - Format: "", + Description: "clusterDNS is a list of IP address for the cluster DNS server. If set, kubelet will configure all containers to use this for DNS resolution instead of the host's DNS servers", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, }, }, "streamingConnectionIdleTimeout": { diff --git a/pkg/kubelet/kubelet.go b/pkg/kubelet/kubelet.go index 9f951eb7e32..8ffee1ddd7e 100644 --- a/pkg/kubelet/kubelet.go +++ b/pkg/kubelet/kubelet.go @@ -422,6 +422,16 @@ func NewMainKubelet(kubeCfg *componentconfig.KubeletConfiguration, kubeDeps *Kub oomWatcher := NewOOMWatcher(kubeDeps.CAdvisorInterface, kubeDeps.Recorder) + clusterDNS := make([]net.IP, 0, len(kubeCfg.ClusterDNS)) + for _, ipEntry := range kubeCfg.ClusterDNS { + ip := net.ParseIP(ipEntry) + if ip == nil { + glog.Warningf("Invalid clusterDNS ip '%q'", ipEntry) + } else { + clusterDNS = append(clusterDNS, ip) + } + } + klet := &Kubelet{ hostname: hostname, nodeName: nodeName, @@ -436,7 +446,7 @@ func NewMainKubelet(kubeCfg *componentconfig.KubeletConfiguration, kubeDeps *Kub registerSchedulable: kubeCfg.RegisterSchedulable, standaloneMode: standaloneMode, clusterDomain: kubeCfg.ClusterDomain, - clusterDNS: net.ParseIP(kubeCfg.ClusterDNS), + clusterDNS: clusterDNS, serviceLister: serviceLister, nodeLister: nodeLister, nodeInfo: nodeInfo, @@ -860,7 +870,7 @@ type Kubelet struct { clusterDomain string // If non-nil, use this for container DNS server. - clusterDNS net.IP + clusterDNS []net.IP // masterServiceNamespace is the namespace that the master service is exposed in. masterServiceNamespace string @@ -1277,7 +1287,7 @@ func (kl *Kubelet) GetClusterDNS(pod *v1.Pod) ([]string, []string, error) { } } useClusterFirstPolicy := pod.Spec.DNSPolicy == v1.DNSClusterFirst - if useClusterFirstPolicy && kl.clusterDNS == nil { + if useClusterFirstPolicy && len(kl.clusterDNS) == 0 { // clusterDNS is not known. // pod with ClusterDNSFirst Policy cannot be created kl.recorder.Eventf(pod, v1.EventTypeWarning, "MissingClusterDNS", "kubelet does not have ClusterDNS IP configured and cannot create Pod using %q policy. Falling back to DNSDefault policy.", pod.Spec.DNSPolicy) @@ -1308,8 +1318,12 @@ func (kl *Kubelet) GetClusterDNS(pod *v1.Pod) ([]string, []string, error) { // for a pod with DNSClusterFirst policy, the cluster DNS server is the only nameserver configured for // the pod. The cluster DNS server itself will forward queries to other nameservers that is configured to use, // in case the cluster DNS server cannot resolve the DNS query itself - dns := []string{kl.clusterDNS.String()} + dns := make([]string, len(kl.clusterDNS)) + for i, ip := range kl.clusterDNS { + dns[i] = ip.String() + } dnsSearch := kl.formDNSSearch(hostSearch, pod) + return dns, dnsSearch, nil } diff --git a/pkg/kubelet/kubelet_pods_test.go b/pkg/kubelet/kubelet_pods_test.go index c5b1d2bba1c..93a2df25b85 100644 --- a/pkg/kubelet/kubelet_pods_test.go +++ b/pkg/kubelet/kubelet_pods_test.go @@ -174,7 +174,7 @@ func TestGenerateRunContainerOptions_DNSConfigurationParams(t *testing.T) { clusterNS := "203.0.113.1" kubelet.clusterDomain = "kubernetes.io" - kubelet.clusterDNS = net.ParseIP(clusterNS) + kubelet.clusterDNS = []net.IP{net.ParseIP(clusterNS)} pods := newTestPods(2) pods[0].Spec.DNSPolicy = v1.DNSClusterFirst diff --git a/pkg/kubemark/hollow_kubelet.go b/pkg/kubemark/hollow_kubelet.go index c80386c5506..3a1b8a4495c 100644 --- a/pkg/kubemark/hollow_kubelet.go +++ b/pkg/kubemark/hollow_kubelet.go @@ -128,7 +128,7 @@ func GetHollowKubeletConfig( c.EvictionPressureTransitionPeriod.Duration = 5 * time.Minute c.MaxPods = int32(maxPods) c.PodsPerCore = int32(podsPerCore) - c.ClusterDNS = "" + c.ClusterDNS = []string{} c.DockerExecHandlerName = "native" c.ImageGCHighThresholdPercent = 90 c.ImageGCLowThresholdPercent = 80