github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/libnetwork/service_common.go (about) 1 //go:build linux || windows 2 // +build linux windows 3 4 package libnetwork 5 6 import ( 7 "net" 8 9 "github.com/docker/libnetwork/internal/setmatrix" 10 "github.com/sirupsen/logrus" 11 ) 12 13 const maxSetStringLen = 350 14 15 func (c *controller) addEndpointNameResolution(svcName, svcID, nID, eID, containerName string, vip net.IP, serviceAliases, taskAliases []string, ip net.IP, addService bool, method string) error { 16 n, err := c.NetworkByID(nID) 17 if err != nil { 18 return err 19 } 20 21 logrus.Debugf("addEndpointNameResolution %s %s add_service:%t sAliases:%v tAliases:%v", eID, svcName, addService, serviceAliases, taskAliases) 22 23 // Add container resolution mappings 24 c.addContainerNameResolution(nID, eID, containerName, taskAliases, ip, method) 25 26 serviceID := svcID 27 if serviceID == "" { 28 // This is the case of a normal container not part of a service 29 serviceID = eID 30 } 31 32 // Add endpoint IP to special "tasks.svc_name" so that the applications have access to DNS RR. 33 n.(*network).addSvcRecords(eID, "tasks."+svcName, serviceID, ip, nil, false, method) 34 for _, alias := range serviceAliases { 35 n.(*network).addSvcRecords(eID, "tasks."+alias, serviceID, ip, nil, false, method) 36 } 37 38 // Add service name to vip in DNS, if vip is valid. Otherwise resort to DNS RR 39 if len(vip) == 0 { 40 n.(*network).addSvcRecords(eID, svcName, serviceID, ip, nil, false, method) 41 for _, alias := range serviceAliases { 42 n.(*network).addSvcRecords(eID, alias, serviceID, ip, nil, false, method) 43 } 44 } 45 46 if addService && len(vip) != 0 { 47 n.(*network).addSvcRecords(eID, svcName, serviceID, vip, nil, false, method) 48 for _, alias := range serviceAliases { 49 n.(*network).addSvcRecords(eID, alias, serviceID, vip, nil, false, method) 50 } 51 } 52 53 return nil 54 } 55 56 func (c *controller) addContainerNameResolution(nID, eID, containerName string, taskAliases []string, ip net.IP, method string) error { 57 n, err := c.NetworkByID(nID) 58 if err != nil { 59 return err 60 } 61 logrus.Debugf("addContainerNameResolution %s %s", eID, containerName) 62 63 // Add resolution for container name 64 n.(*network).addSvcRecords(eID, containerName, eID, ip, nil, true, method) 65 66 // Add resolution for taskaliases 67 for _, alias := range taskAliases { 68 n.(*network).addSvcRecords(eID, alias, eID, ip, nil, false, method) 69 } 70 71 return nil 72 } 73 74 func (c *controller) deleteEndpointNameResolution(svcName, svcID, nID, eID, containerName string, vip net.IP, serviceAliases, taskAliases []string, ip net.IP, rmService, multipleEntries bool, method string) error { 75 n, err := c.NetworkByID(nID) 76 if err != nil { 77 return err 78 } 79 80 logrus.Debugf("deleteEndpointNameResolution %s %s rm_service:%t suppress:%t sAliases:%v tAliases:%v", eID, svcName, rmService, multipleEntries, serviceAliases, taskAliases) 81 82 // Delete container resolution mappings 83 c.delContainerNameResolution(nID, eID, containerName, taskAliases, ip, method) 84 85 serviceID := svcID 86 if serviceID == "" { 87 // This is the case of a normal container not part of a service 88 serviceID = eID 89 } 90 91 // Delete the special "tasks.svc_name" backend record. 92 if !multipleEntries { 93 n.(*network).deleteSvcRecords(eID, "tasks."+svcName, serviceID, ip, nil, false, method) 94 for _, alias := range serviceAliases { 95 n.(*network).deleteSvcRecords(eID, "tasks."+alias, serviceID, ip, nil, false, method) 96 } 97 } 98 99 // If we are doing DNS RR delete the endpoint IP from DNS record right away. 100 if !multipleEntries && len(vip) == 0 { 101 n.(*network).deleteSvcRecords(eID, svcName, serviceID, ip, nil, false, method) 102 for _, alias := range serviceAliases { 103 n.(*network).deleteSvcRecords(eID, alias, serviceID, ip, nil, false, method) 104 } 105 } 106 107 // Remove the DNS record for VIP only if we are removing the service 108 if rmService && len(vip) != 0 && !multipleEntries { 109 n.(*network).deleteSvcRecords(eID, svcName, serviceID, vip, nil, false, method) 110 for _, alias := range serviceAliases { 111 n.(*network).deleteSvcRecords(eID, alias, serviceID, vip, nil, false, method) 112 } 113 } 114 115 return nil 116 } 117 118 func (c *controller) delContainerNameResolution(nID, eID, containerName string, taskAliases []string, ip net.IP, method string) error { 119 n, err := c.NetworkByID(nID) 120 if err != nil { 121 return err 122 } 123 logrus.Debugf("delContainerNameResolution %s %s", eID, containerName) 124 125 // Delete resolution for container name 126 n.(*network).deleteSvcRecords(eID, containerName, eID, ip, nil, true, method) 127 128 // Delete resolution for taskaliases 129 for _, alias := range taskAliases { 130 n.(*network).deleteSvcRecords(eID, alias, eID, ip, nil, true, method) 131 } 132 133 return nil 134 } 135 136 func newService(name string, id string, ingressPorts []*PortConfig, serviceAliases []string) *service { 137 return &service{ 138 name: name, 139 id: id, 140 ingressPorts: ingressPorts, 141 loadBalancers: make(map[string]*loadBalancer), 142 aliases: serviceAliases, 143 ipToEndpoint: setmatrix.NewSetMatrix(), 144 } 145 } 146 147 func (c *controller) getLBIndex(sid, nid string, ingressPorts []*PortConfig) int { 148 skey := serviceKey{ 149 id: sid, 150 ports: portConfigs(ingressPorts).String(), 151 } 152 c.Lock() 153 s, ok := c.serviceBindings[skey] 154 c.Unlock() 155 156 if !ok { 157 return 0 158 } 159 160 s.Lock() 161 lb := s.loadBalancers[nid] 162 s.Unlock() 163 164 return int(lb.fwMark) 165 } 166 167 // cleanupServiceDiscovery when the network is being deleted, erase all the associated service discovery records 168 func (c *controller) cleanupServiceDiscovery(cleanupNID string) { 169 c.Lock() 170 defer c.Unlock() 171 if cleanupNID == "" { 172 logrus.Debugf("cleanupServiceDiscovery for all networks") 173 c.svcRecords = make(map[string]svcInfo) 174 return 175 } 176 logrus.Debugf("cleanupServiceDiscovery for network:%s", cleanupNID) 177 delete(c.svcRecords, cleanupNID) 178 } 179 180 func (c *controller) cleanupServiceBindings(cleanupNID string) { 181 var cleanupFuncs []func() 182 183 logrus.Debugf("cleanupServiceBindings for %s", cleanupNID) 184 c.Lock() 185 services := make([]*service, 0, len(c.serviceBindings)) 186 for _, s := range c.serviceBindings { 187 services = append(services, s) 188 } 189 c.Unlock() 190 191 for _, s := range services { 192 s.Lock() 193 // Skip the serviceBindings that got deleted 194 if s.deleted { 195 s.Unlock() 196 continue 197 } 198 for nid, lb := range s.loadBalancers { 199 if cleanupNID != "" && nid != cleanupNID { 200 continue 201 } 202 for eid, be := range lb.backEnds { 203 cleanupFuncs = append(cleanupFuncs, makeServiceCleanupFunc(c, s, nid, eid, lb.vip, be.ip)) 204 } 205 } 206 s.Unlock() 207 } 208 209 for _, f := range cleanupFuncs { 210 f() 211 } 212 213 } 214 215 func makeServiceCleanupFunc(c *controller, s *service, nID, eID string, vip net.IP, ip net.IP) func() { 216 // ContainerName and taskAliases are not available here, this is still fine because the Service discovery 217 // cleanup already happened before. The only thing that rmServiceBinding is still doing here a part from the Load 218 // Balancer bookeeping, is to keep consistent the mapping of endpoint to IP. 219 return func() { 220 if err := c.rmServiceBinding(s.name, s.id, nID, eID, "", vip, s.ingressPorts, s.aliases, []string{}, ip, "cleanupServiceBindings", false, true); err != nil { 221 logrus.Errorf("Failed to remove service bindings for service %s network %s endpoint %s while cleanup: %v", s.id, nID, eID, err) 222 } 223 } 224 } 225 226 func (c *controller) addServiceBinding(svcName, svcID, nID, eID, containerName string, vip net.IP, ingressPorts []*PortConfig, serviceAliases, taskAliases []string, ip net.IP, method string) error { 227 var addService bool 228 229 // Failure to lock the network ID on add can result in racing 230 // racing against network deletion resulting in inconsistent 231 // state in the c.serviceBindings map and it's sub-maps. Also, 232 // always lock network ID before services to avoid deadlock. 233 c.networkLocker.Lock(nID) 234 defer c.networkLocker.Unlock(nID) 235 236 n, err := c.NetworkByID(nID) 237 if err != nil { 238 return err 239 } 240 241 skey := serviceKey{ 242 id: svcID, 243 ports: portConfigs(ingressPorts).String(), 244 } 245 246 var s *service 247 for { 248 c.Lock() 249 var ok bool 250 s, ok = c.serviceBindings[skey] 251 if !ok { 252 // Create a new service if we are seeing this service 253 // for the first time. 254 s = newService(svcName, svcID, ingressPorts, serviceAliases) 255 c.serviceBindings[skey] = s 256 } 257 c.Unlock() 258 s.Lock() 259 if !s.deleted { 260 // ok the object is good to be used 261 break 262 } 263 s.Unlock() 264 } 265 logrus.Debugf("addServiceBinding from %s START for %s %s p:%p nid:%s skey:%v", method, svcName, eID, s, nID, skey) 266 defer s.Unlock() 267 268 lb, ok := s.loadBalancers[nID] 269 if !ok { 270 // Create a new load balancer if we are seeing this 271 // network attachment on the service for the first 272 // time. 273 fwMarkCtrMu.Lock() 274 275 lb = &loadBalancer{ 276 vip: vip, 277 fwMark: fwMarkCtr, 278 backEnds: make(map[string]*lbBackend), 279 service: s, 280 } 281 282 fwMarkCtr++ 283 fwMarkCtrMu.Unlock() 284 285 s.loadBalancers[nID] = lb 286 addService = true 287 } 288 289 lb.backEnds[eID] = &lbBackend{ip, false} 290 291 ok, entries := s.assignIPToEndpoint(ip.String(), eID) 292 if !ok || entries > 1 { 293 setStr, b := s.printIPToEndpoint(ip.String()) 294 if len(setStr) > maxSetStringLen { 295 setStr = setStr[:maxSetStringLen] 296 } 297 logrus.Warnf("addServiceBinding %s possible transient state ok:%t entries:%d set:%t %s", eID, ok, entries, b, setStr) 298 } 299 300 // Add loadbalancer service and backend to the network 301 n.(*network).addLBBackend(ip, lb) 302 303 // Add the appropriate name resolutions 304 c.addEndpointNameResolution(svcName, svcID, nID, eID, containerName, vip, serviceAliases, taskAliases, ip, addService, "addServiceBinding") 305 306 logrus.Debugf("addServiceBinding from %s END for %s %s", method, svcName, eID) 307 308 return nil 309 } 310 311 func (c *controller) rmServiceBinding(svcName, svcID, nID, eID, containerName string, vip net.IP, ingressPorts []*PortConfig, serviceAliases []string, taskAliases []string, ip net.IP, method string, deleteSvcRecords bool, fullRemove bool) error { 312 313 var rmService bool 314 315 skey := serviceKey{ 316 id: svcID, 317 ports: portConfigs(ingressPorts).String(), 318 } 319 320 c.Lock() 321 s, ok := c.serviceBindings[skey] 322 c.Unlock() 323 if !ok { 324 logrus.Warnf("rmServiceBinding %s %s %s aborted c.serviceBindings[skey] !ok", method, svcName, eID) 325 return nil 326 } 327 328 s.Lock() 329 defer s.Unlock() 330 logrus.Debugf("rmServiceBinding from %s START for %s %s p:%p nid:%s sKey:%v deleteSvc:%t", method, svcName, eID, s, nID, skey, deleteSvcRecords) 331 lb, ok := s.loadBalancers[nID] 332 if !ok { 333 logrus.Warnf("rmServiceBinding %s %s %s aborted s.loadBalancers[nid] !ok", method, svcName, eID) 334 return nil 335 } 336 337 be, ok := lb.backEnds[eID] 338 if !ok { 339 logrus.Warnf("rmServiceBinding %s %s %s aborted lb.backEnds[eid] && lb.disabled[eid] !ok", method, svcName, eID) 340 return nil 341 } 342 343 if fullRemove { 344 // delete regardless 345 delete(lb.backEnds, eID) 346 } else { 347 be.disabled = true 348 } 349 350 if len(lb.backEnds) == 0 { 351 // All the backends for this service have been 352 // removed. Time to remove the load balancer and also 353 // remove the service entry in IPVS. 354 rmService = true 355 356 delete(s.loadBalancers, nID) 357 logrus.Debugf("rmServiceBinding %s delete %s, p:%p in loadbalancers len:%d", eID, nID, lb, len(s.loadBalancers)) 358 } 359 360 ok, entries := s.removeIPToEndpoint(ip.String(), eID) 361 if !ok || entries > 0 { 362 setStr, b := s.printIPToEndpoint(ip.String()) 363 if len(setStr) > maxSetStringLen { 364 setStr = setStr[:maxSetStringLen] 365 } 366 logrus.Warnf("rmServiceBinding %s possible transient state ok:%t entries:%d set:%t %s", eID, ok, entries, b, setStr) 367 } 368 369 // Remove loadbalancer service(if needed) and backend in all 370 // sandboxes in the network only if the vip is valid. 371 if entries == 0 { 372 // The network may well have been deleted from the store (and 373 // dataplane) before the last of the service bindings. On Linux that's 374 // ok because removing the network sandbox from the dataplane 375 // implicitly cleans up all related dataplane state. 376 // On the Windows dataplane, VFP policylists must be removed 377 // independently of the network, and they must be removed before the HNS 378 // network. Otherwise, policylist removal fails with "network not 379 // found." On Windows cleanupServiceBindings must be called prior to 380 // removing the network from the store or dataplane. 381 n, err := c.NetworkByID(nID) 382 if err == nil { 383 n.(*network).rmLBBackend(ip, lb, rmService, fullRemove) 384 } 385 } 386 387 // Delete the name resolutions 388 if deleteSvcRecords { 389 c.deleteEndpointNameResolution(svcName, svcID, nID, eID, containerName, vip, serviceAliases, taskAliases, ip, rmService, entries > 0, "rmServiceBinding") 390 } 391 392 if len(s.loadBalancers) == 0 { 393 // All loadbalancers for the service removed. Time to 394 // remove the service itself. 395 c.Lock() 396 397 // Mark the object as deleted so that the add won't use it wrongly 398 s.deleted = true 399 // NOTE The delete from the serviceBindings map has to be the last operation else we are allowing a race between this service 400 // that is getting deleted and a new service that will be created if the entry is not anymore there 401 delete(c.serviceBindings, skey) 402 c.Unlock() 403 } 404 405 logrus.Debugf("rmServiceBinding from %s END for %s %s", method, svcName, eID) 406 return nil 407 }