From d0dd6c844b3cffaebbbabb72eb380da24ecec448 Mon Sep 17 00:00:00 2001 From: Ravi Sankar Penta Date: Wed, 9 Dec 2015 18:05:35 -0800 Subject: [PATCH 1/2] Allow node IP to be passed as optional config for kubelet In case of multiple IPs on the node, this will allow admin to specify desired IP to be used for the node. --- cmd/kubelet/app/server.go | 5 ++++ pkg/kubelet/kubelet.go | 52 +++++++++++++++++++++++++++++++++++-- pkg/kubelet/kubelet_test.go | 40 ++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+), 2 deletions(-) diff --git a/cmd/kubelet/app/server.go b/cmd/kubelet/app/server.go index 899916f431d..e16e7316dc4 100644 --- a/cmd/kubelet/app/server.go +++ b/cmd/kubelet/app/server.go @@ -158,6 +158,7 @@ type KubeletServer struct { // Pull images one at a time. SerializeImagePulls bool ExperimentalFlannelOverlay bool + NodeIP net.IP } // bootstrapping interface for kubelet, targets the initialization protocol @@ -349,6 +350,7 @@ func (s *KubeletServer) AddFlags(fs *pflag.FlagSet) { fs.IntVar(&s.KubeAPIBurst, "kube-api-burst", s.KubeAPIBurst, "Burst to use while talking with kubernetes apiserver") fs.BoolVar(&s.SerializeImagePulls, "serialize-image-pulls", s.SerializeImagePulls, "Pull images one at a time. We recommend *not* changing the default value on nodes that run docker daemon with version < 1.9 or an Aufs storage backend. Issue #10959 has more details. [default=true]") fs.BoolVar(&s.ExperimentalFlannelOverlay, "experimental-flannel-overlay", s.ExperimentalFlannelOverlay, "Experimental support for starting the kubelet with the default overlay network (flannel). Assumes flanneld is already running in client mode. [default=false]") + fs.IPVar(&s.NodeIP, "node-ip", s.NodeIP, "IP address of the node. If set, kubelet will use this IP address for the node") } // UnsecuredKubeletConfig returns a KubeletConfig suitable for being run, or an error if the server setup @@ -488,6 +490,7 @@ func (s *KubeletServer) UnsecuredKubeletConfig() (*KubeletConfig, error) { VolumePlugins: ProbeVolumePlugins(s.VolumePluginDir), ExperimentalFlannelOverlay: s.ExperimentalFlannelOverlay, + NodeIP: s.NodeIP, }, nil } @@ -964,6 +967,7 @@ type KubeletConfig struct { VolumePlugins []volume.VolumePlugin ExperimentalFlannelOverlay bool + NodeIP net.IP } func CreateAndInitKubelet(kc *KubeletConfig) (k KubeletBootstrap, pc *config.PodConfig, err error) { @@ -1047,6 +1051,7 @@ func CreateAndInitKubelet(kc *KubeletConfig) (k KubeletBootstrap, pc *config.Pod kc.SerializeImagePulls, kc.ContainerManager, kc.ExperimentalFlannelOverlay, + kc.NodeIP, ) if err != nil { diff --git a/pkg/kubelet/kubelet.go b/pkg/kubelet/kubelet.go index eb2e968348d..d206e0409b3 100644 --- a/pkg/kubelet/kubelet.go +++ b/pkg/kubelet/kubelet.go @@ -197,6 +197,7 @@ func NewMainKubelet( serializeImagePulls bool, containerManager cm.ContainerManager, flannelExperimentalOverlay bool, + nodeIP net.IP, ) (*Kubelet, error) { if rootDirectory == "" { @@ -313,10 +314,17 @@ func NewMainKubelet( containerManager: containerManager, flannelExperimentalOverlay: flannelExperimentalOverlay, flannelHelper: NewFlannelHelper(), + nodeIP: nodeIP, } if klet.flannelExperimentalOverlay { glog.Infof("Flannel is in charge of podCIDR and overlay networking.") } + if klet.nodeIP != nil { + if err := klet.validateNodeIP(); err != nil { + return nil, err + } + glog.Infof("Using node IP: %q", klet.nodeIP.String()) + } if plug, err := network.InitNetworkPlugin(networkPlugins, networkPluginName, &networkHost{klet}); err != nil { return nil, err } else { @@ -643,6 +651,42 @@ type Kubelet struct { // on the fly if we're confident the dbus connetions it opens doesn't // put the system under duress. flannelHelper *FlannelHelper + + // If non-nil, use this IP address for the node + nodeIP net.IP +} + +// Validate given node IP belongs to the current host +func (kl *Kubelet) validateNodeIP() error { + if kl.nodeIP == nil { + return nil + } + + // Honor IP limitations set in setNodeStatus() + if kl.nodeIP.IsLoopback() { + return fmt.Errorf("nodeIP can't be loopback address") + } + if kl.nodeIP.To4() == nil { + return fmt.Errorf("nodeIP must be IPv4 address") + } + + addrs, err := net.InterfaceAddrs() + if err != nil { + return err + } + for _, addr := range addrs { + var ip net.IP + switch v := addr.(type) { + case *net.IPNet: + ip = v.IP + case *net.IPAddr: + ip = v.IP + } + if ip != nil && ip.Equal(kl.nodeIP) { + return nil + } + } + return fmt.Errorf("Node IP: %q not found in the host's network interfaces", kl.nodeIP.String()) } func (kl *Kubelet) allSourcesReady() bool { @@ -2663,8 +2707,12 @@ func (kl *Kubelet) setNodeAddress(node *api.Node) error { } node.Status.Addresses = nodeAddresses } else { - addr := net.ParseIP(kl.hostname) - if addr != nil { + if kl.nodeIP != nil { + node.Status.Addresses = []api.NodeAddress{ + {Type: api.NodeLegacyHostIP, Address: kl.nodeIP.String()}, + {Type: api.NodeInternalIP, Address: kl.nodeIP.String()}, + } + } else if addr := net.ParseIP(kl.hostname); addr != nil { node.Status.Addresses = []api.NodeAddress{ {Type: api.NodeLegacyHostIP, Address: addr.String()}, {Type: api.NodeInternalIP, Address: addr.String()}, diff --git a/pkg/kubelet/kubelet_test.go b/pkg/kubelet/kubelet_test.go index 557b12d9ecc..327387473c6 100644 --- a/pkg/kubelet/kubelet_test.go +++ b/pkg/kubelet/kubelet_test.go @@ -787,6 +787,46 @@ func TestGetContainerInfoWithNoContainers(t *testing.T) { mockCadvisor.AssertExpectations(t) } +func TestNodeIPParam(t *testing.T) { + testKubelet := newTestKubelet(t) + kubelet := testKubelet.kubelet + tests := []struct { + nodeIP string + success bool + testName string + }{ + { + nodeIP: "", + success: true, + testName: "IP not set", + }, + { + nodeIP: "127.0.0.1", + success: false, + testName: "loopback address", + }, + { + nodeIP: "FE80::0202:B3FF:FE1E:8329", + success: false, + testName: "IPv6 address", + }, + { + nodeIP: "1.2.3.4", + success: false, + testName: "IPv4 address that doesn't belong to host", + }, + } + for _, test := range tests { + kubelet.nodeIP = net.ParseIP(test.nodeIP) + err := kubelet.validateNodeIP() + if err != nil && test.success { + t.Errorf("Test: %s, expected no error but got: %v", test.testName, err) + } else if err == nil && !test.success { + t.Errorf("Test: %s, expected an error", test.testName) + } + } +} + func TestGetContainerInfoWithNoMatchingContainers(t *testing.T) { testKubelet := newTestKubelet(t) fakeRuntime := testKubelet.fakeRuntime From a9b0696166981d70221cb1eabb30e1a28246b2aa Mon Sep 17 00:00:00 2001 From: Ravi Sankar Penta Date: Thu, 10 Dec 2015 15:23:35 -0800 Subject: [PATCH 2/2] Update auto generated docs for kubelet Add node-ip flag to know-flags.txt --- docs/admin/kubelet.md | 1 + hack/verify-flags/known-flags.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/admin/kubelet.md b/docs/admin/kubelet.md index b40d1b2d2d1..0beebf21876 100644 --- a/docs/admin/kubelet.md +++ b/docs/admin/kubelet.md @@ -113,6 +113,7 @@ kubelet --minimum-container-ttl-duration=1m0s: Minimum age for a finished container before it is garbage collected. Examples: '300ms', '10s' or '2h45m' --network-plugin="": The name of the network plugin to be invoked for various events in kubelet/pod lifecycle --network-plugin-dir="/usr/libexec/kubernetes/kubelet-plugins/net/exec/": The full path of the directory in which to search for network plugins + --node-ip=: IP address of the node. If set, kubelet will use this IP address for the node --node-label=[]: add labels when registering the node in the cluster, the flag can be used multiple times (key=value) --node-labels-file="": the path to a yaml or json file containing a series of key pair labels to apply on node registration --node-status-update-frequency=10s: 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 diff --git a/hack/verify-flags/known-flags.txt b/hack/verify-flags/known-flags.txt index fa6a074d141..bfbc0225cbb 100644 --- a/hack/verify-flags/known-flags.txt +++ b/hack/verify-flags/known-flags.txt @@ -221,6 +221,7 @@ namespace-sync-period network-plugin network-plugin-dir node-instance-group +node-ip node-monitor-grace-period node-monitor-period node-label