github.com/zhyoulun/cilium@v1.6.12/pkg/nodediscovery/nodediscovery.go (about) 1 // Copyright 2016-2019 Authors of Cilium 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package nodediscovery 16 17 import ( 18 "context" 19 "time" 20 21 "github.com/cilium/cilium/pkg/aws/metadata" 22 "github.com/cilium/cilium/pkg/cidr" 23 "github.com/cilium/cilium/pkg/controller" 24 "github.com/cilium/cilium/pkg/datapath" 25 "github.com/cilium/cilium/pkg/defaults" 26 "github.com/cilium/cilium/pkg/k8s" 27 ciliumv2 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2" 28 "github.com/cilium/cilium/pkg/logging" 29 "github.com/cilium/cilium/pkg/logging/logfields" 30 "github.com/cilium/cilium/pkg/mtu" 31 "github.com/cilium/cilium/pkg/node" 32 "github.com/cilium/cilium/pkg/node/addressing" 33 nodemanager "github.com/cilium/cilium/pkg/node/manager" 34 nodestore "github.com/cilium/cilium/pkg/node/store" 35 "github.com/cilium/cilium/pkg/option" 36 "github.com/cilium/cilium/pkg/source" 37 cnitypes "github.com/cilium/cilium/plugins/cilium-cni/types" 38 39 "k8s.io/apimachinery/pkg/api/errors" 40 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 41 ) 42 43 const ( 44 // AutoCIDR indicates that a CIDR should be allocated 45 AutoCIDR = "auto" 46 47 nodeDiscoverySubsys = "nodediscovery" 48 maxRetryCount = 5 49 ) 50 51 var log = logging.DefaultLogger.WithField(logfields.LogSubsys, nodeDiscoverySubsys) 52 53 // NodeDiscovery represents a node discovery action 54 type NodeDiscovery struct { 55 Manager *nodemanager.Manager 56 LocalConfig datapath.LocalNodeConfiguration 57 Registrar nodestore.NodeRegistrar 58 LocalNode node.Node 59 Registered chan struct{} 60 } 61 62 func enableLocalNodeRoute() bool { 63 return !option.Config.IsFlannelMasterDeviceSet() && option.Config.IPAM != option.IPAMENI 64 } 65 66 // NewNodeDiscovery returns a pointer to new node discovery object 67 func NewNodeDiscovery(manager *nodemanager.Manager, mtuConfig mtu.Configuration) *NodeDiscovery { 68 auxPrefixes := []*cidr.CIDR{} 69 70 if option.Config.IPv4ServiceRange != AutoCIDR { 71 serviceCIDR, err := cidr.ParseCIDR(option.Config.IPv4ServiceRange) 72 if err != nil { 73 log.WithError(err).WithField(logfields.V4Prefix, option.Config.IPv4ServiceRange).Fatal("Invalid IPv4 service prefix") 74 } 75 76 auxPrefixes = append(auxPrefixes, serviceCIDR) 77 } 78 79 if option.Config.IPv6ServiceRange != AutoCIDR { 80 serviceCIDR, err := cidr.ParseCIDR(option.Config.IPv6ServiceRange) 81 if err != nil { 82 log.WithError(err).WithField(logfields.V6Prefix, option.Config.IPv6ServiceRange).Fatal("Invalid IPv6 service prefix") 83 } 84 85 auxPrefixes = append(auxPrefixes, serviceCIDR) 86 } 87 88 return &NodeDiscovery{ 89 Manager: manager, 90 LocalConfig: datapath.LocalNodeConfiguration{ 91 MtuConfig: mtuConfig, 92 UseSingleClusterRoute: option.Config.UseSingleClusterRoute, 93 EnableIPv4: option.Config.EnableIPv4, 94 EnableIPv6: option.Config.EnableIPv6, 95 EnableEncapsulation: option.Config.Tunnel != option.TunnelDisabled, 96 EnableAutoDirectRouting: option.Config.EnableAutoDirectRouting, 97 EnableLocalNodeRoute: enableLocalNodeRoute(), 98 AuxiliaryPrefixes: auxPrefixes, 99 EnableIPSec: option.Config.EnableIPSec, 100 EncryptNode: option.Config.EncryptNode, 101 IPv4PodSubnets: option.Config.IPv4PodSubnets, 102 IPv6PodSubnets: option.Config.IPv6PodSubnets, 103 }, 104 LocalNode: node.Node{ 105 Source: source.Local, 106 }, 107 Registered: make(chan struct{}), 108 } 109 } 110 111 // Configuration is the configuration interface that must be implemented in 112 // order to manage node discovery 113 type Configuration interface { 114 // GetNetConf must return the CNI configuration as passed in by the 115 // user 116 GetNetConf() *cnitypes.NetConf 117 } 118 119 // start configures the local node and starts node discovery. This is called on 120 // agent startup to configure the local node based on the configuration options 121 // passed to the agent. nodeName is the name to be used in the local agent. 122 func (n *NodeDiscovery) StartDiscovery(nodeName string, conf Configuration) { 123 n.LocalNode.Name = nodeName 124 n.LocalNode.Cluster = option.Config.ClusterName 125 n.LocalNode.IPAddresses = []node.Address{} 126 n.LocalNode.IPv4AllocCIDR = node.GetIPv4AllocRange() 127 n.LocalNode.IPv6AllocCIDR = node.GetIPv6AllocRange() 128 n.LocalNode.ClusterID = option.Config.ClusterID 129 n.LocalNode.EncryptionKey = node.GetIPsecKeyIdentity() 130 131 if node.GetExternalIPv4() != nil { 132 n.LocalNode.IPAddresses = append(n.LocalNode.IPAddresses, node.Address{ 133 Type: addressing.NodeInternalIP, 134 IP: node.GetExternalIPv4(), 135 }) 136 } 137 138 if node.GetIPv6() != nil { 139 n.LocalNode.IPAddresses = append(n.LocalNode.IPAddresses, node.Address{ 140 Type: addressing.NodeInternalIP, 141 IP: node.GetIPv6(), 142 }) 143 } 144 145 if node.GetInternalIPv4() != nil { 146 n.LocalNode.IPAddresses = append(n.LocalNode.IPAddresses, node.Address{ 147 Type: addressing.NodeCiliumInternalIP, 148 IP: node.GetInternalIPv4(), 149 }) 150 } 151 152 if node.GetIPv6Router() != nil { 153 n.LocalNode.IPAddresses = append(n.LocalNode.IPAddresses, node.Address{ 154 Type: addressing.NodeCiliumInternalIP, 155 IP: node.GetIPv6Router(), 156 }) 157 } 158 159 n.Manager.NodeUpdated(n.LocalNode) 160 161 go func() { 162 log.Info("Adding local node to cluster") 163 for { 164 if err := n.Registrar.RegisterNode(&n.LocalNode, n.Manager); err != nil { 165 log.WithError(err).Error("Unable to initialize local node. Retrying...") 166 time.Sleep(time.Second) 167 } else { 168 break 169 } 170 } 171 close(n.Registered) 172 }() 173 174 go func() { 175 select { 176 case <-n.Registered: 177 case <-time.NewTimer(defaults.NodeInitTimeout).C: 178 log.Fatalf("Unable to initialize local node due to timeout") 179 } 180 }() 181 182 if k8s.IsEnabled() { 183 // Creation or update of the CiliumNode can be done in the 184 // background, nothing depends on the completion of this. 185 go n.UpdateCiliumNodeResource(conf) 186 } 187 188 if option.Config.KVStore != "" { 189 go func() { 190 <-n.Registered 191 controller.NewManager().UpdateController("propagating local node change to kv-store", 192 controller.ControllerParams{ 193 DoFunc: func(ctx context.Context) error { 194 err := n.Registrar.UpdateLocalKeySync(&n.LocalNode) 195 if err != nil { 196 log.WithError(err).Error("Unable to propagate local node change to kvstore") 197 } 198 return err 199 }, 200 }) 201 }() 202 } 203 } 204 205 // Close shuts down the node discovery engine 206 func (n *NodeDiscovery) Close() { 207 n.Manager.Close() 208 } 209 210 // UpdateCiliumNodeResource updates the CiliumNode resource representing the 211 // local node 212 func (n *NodeDiscovery) UpdateCiliumNodeResource(conf Configuration) { 213 if !option.Config.AutoCreateCiliumNodeResource { 214 return 215 } 216 217 ciliumClient := k8s.CiliumClient() 218 219 for retryCount := 0; retryCount < maxRetryCount; retryCount++ { 220 performUpdate := true 221 nodeResource, err := ciliumClient.CiliumV2().CiliumNodes().Get(node.GetName(), metav1.GetOptions{}) 222 if err != nil { 223 performUpdate = false 224 nodeResource = &ciliumv2.CiliumNode{ 225 ObjectMeta: metav1.ObjectMeta{ 226 Name: node.GetName(), 227 }, 228 } 229 } 230 231 n.mutateNodeResource(conf, nodeResource) 232 233 if performUpdate { 234 if _, err := ciliumClient.CiliumV2().CiliumNodes().Update(nodeResource); err != nil { 235 if errors.IsConflict(err) { 236 log.WithError(err).Warn("Unable to update CiliumNode resource, will retry") 237 continue 238 } 239 log.WithError(err).Fatal("Unable to update CiliumNode resource") 240 } else { 241 return 242 } 243 } else { 244 if _, err = ciliumClient.CiliumV2().CiliumNodes().Create(nodeResource); err != nil { 245 if errors.IsConflict(err) { 246 log.WithError(err).Warn("Unable to create CiliumNode resource, will retry") 247 continue 248 } 249 log.WithError(err).Fatal("Unable to create CiliumNode resource") 250 } else { 251 log.Info("Successfully created CiliumNode resource") 252 return 253 } 254 } 255 } 256 log.Fatal("Could not create or update CiliumNode resource, despite retries") 257 } 258 259 func (n *NodeDiscovery) mutateNodeResource(conf Configuration, nodeResource *ciliumv2.CiliumNode) { 260 // Tie the CiliumNode custom resource lifecycle to the lifecycle of the 261 // Kubernetes node 262 if k8sNode, err := k8s.GetNode(k8s.Client(), node.GetName()); err != nil { 263 log.WithError(err).Warning("Kubernetes node resource representing own node is not available, cannot set OwnerReference") 264 } else { 265 nodeResource.ObjectMeta.OwnerReferences = []metav1.OwnerReference{{ 266 APIVersion: "v1", 267 Kind: "Node", 268 Name: node.GetName(), 269 UID: k8sNode.UID, 270 }} 271 } 272 273 nodeResource.Spec.Addresses = []ciliumv2.NodeAddress{} 274 for _, address := range n.LocalNode.IPAddresses { 275 nodeResource.Spec.Addresses = append(nodeResource.Spec.Addresses, ciliumv2.NodeAddress{ 276 Type: address.Type, 277 IP: address.IP.String(), 278 }) 279 } 280 281 nodeResource.Spec.IPAM.PodCIDRs = []string{} 282 if cidr := node.GetIPv4AllocRange(); cidr != nil { 283 nodeResource.Spec.IPAM.PodCIDRs = append(nodeResource.Spec.IPAM.PodCIDRs, cidr.String()) 284 } 285 286 if cidr := node.GetIPv6AllocRange(); cidr != nil { 287 nodeResource.Spec.IPAM.PodCIDRs = append(nodeResource.Spec.IPAM.PodCIDRs, cidr.String()) 288 } 289 290 nodeResource.Spec.Encryption.Key = int(node.GetIPsecKeyIdentity()) 291 292 nodeResource.Spec.HealthAddressing.IPv4 = "" 293 if ip := n.LocalNode.IPv4HealthIP; ip != nil { 294 nodeResource.Spec.HealthAddressing.IPv4 = ip.String() 295 } 296 297 nodeResource.Spec.HealthAddressing.IPv6 = "" 298 if ip := n.LocalNode.IPv6HealthIP; ip != nil { 299 nodeResource.Spec.HealthAddressing.IPv6 = ip.String() 300 } 301 302 nodeResource.Spec.ENI = ciliumv2.ENISpec{} 303 if option.Config.IPAM == option.IPAMENI { 304 instanceID, instanceType, availabilityZone, vpcID, err := metadata.GetInstanceMetadata() 305 if err != nil { 306 log.WithError(err).Fatal("Unable to retrieve InstanceID of own EC2 instance") 307 } 308 309 nodeResource.Spec.ENI.VpcID = vpcID 310 nodeResource.Spec.ENI.FirstInterfaceIndex = 1 311 nodeResource.Spec.ENI.DeleteOnTermination = true 312 nodeResource.Spec.ENI.PreAllocate = defaults.ENIPreAllocation 313 314 if c := conf.GetNetConf(); c != nil { 315 if c.ENI.MinAllocate != 0 { 316 nodeResource.Spec.ENI.MinAllocate = c.ENI.MinAllocate 317 } 318 319 if c.ENI.PreAllocate != 0 { 320 nodeResource.Spec.ENI.PreAllocate = c.ENI.PreAllocate 321 } 322 323 if c.ENI.FirstInterfaceIndex != 0 { 324 nodeResource.Spec.ENI.FirstInterfaceIndex = c.ENI.FirstInterfaceIndex 325 } 326 327 if len(c.ENI.SecurityGroups) > 0 { 328 nodeResource.Spec.ENI.SecurityGroups = c.ENI.SecurityGroups 329 } 330 331 if len(c.ENI.SubnetTags) > 0 { 332 nodeResource.Spec.ENI.SubnetTags = c.ENI.SubnetTags 333 } 334 335 if c.ENI.VpcID != "" { 336 nodeResource.Spec.ENI.VpcID = c.ENI.VpcID 337 } 338 339 nodeResource.Spec.ENI.DeleteOnTermination = c.ENI.DeleteOnTermination 340 } 341 342 nodeResource.Spec.ENI.InstanceID = instanceID 343 nodeResource.Spec.ENI.InstanceType = instanceType 344 nodeResource.Spec.ENI.AvailabilityZone = availabilityZone 345 } 346 }