github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/swarmkit/manager/scheduler/nodeinfo.go (about)

     1  package scheduler
     2  
     3  import (
     4  	"context"
     5  	"time"
     6  
     7  	"github.com/docker/swarmkit/api"
     8  	"github.com/docker/swarmkit/api/genericresource"
     9  	"github.com/docker/swarmkit/log"
    10  )
    11  
    12  // hostPortSpec specifies a used host port.
    13  type hostPortSpec struct {
    14  	protocol      api.PortConfig_Protocol
    15  	publishedPort uint32
    16  }
    17  
    18  // versionedService defines a tuple that contains a service ID and a spec
    19  // version, so that failures can be tracked per spec version. Note that if the
    20  // task predates spec versioning, specVersion will contain the zero value, and
    21  // this will still work correctly.
    22  type versionedService struct {
    23  	serviceID   string
    24  	specVersion api.Version
    25  }
    26  
    27  // NodeInfo contains a node and some additional metadata.
    28  type NodeInfo struct {
    29  	*api.Node
    30  	Tasks                     map[string]*api.Task
    31  	ActiveTasksCount          int
    32  	ActiveTasksCountByService map[string]int
    33  	AvailableResources        *api.Resources
    34  	usedHostPorts             map[hostPortSpec]struct{}
    35  
    36  	// recentFailures is a map from service ID/version to the timestamps of
    37  	// the most recent failures the node has experienced from replicas of
    38  	// that service.
    39  	recentFailures map[versionedService][]time.Time
    40  
    41  	// lastCleanup is the last time recentFailures was cleaned up. This is
    42  	// done periodically to avoid recentFailures growing without any limit.
    43  	lastCleanup time.Time
    44  }
    45  
    46  func newNodeInfo(n *api.Node, tasks map[string]*api.Task, availableResources api.Resources) NodeInfo {
    47  	nodeInfo := NodeInfo{
    48  		Node:                      n,
    49  		Tasks:                     make(map[string]*api.Task),
    50  		ActiveTasksCountByService: make(map[string]int),
    51  		AvailableResources:        availableResources.Copy(),
    52  		usedHostPorts:             make(map[hostPortSpec]struct{}),
    53  		recentFailures:            make(map[versionedService][]time.Time),
    54  		lastCleanup:               time.Now(),
    55  	}
    56  
    57  	for _, t := range tasks {
    58  		nodeInfo.addTask(t)
    59  	}
    60  
    61  	return nodeInfo
    62  }
    63  
    64  // removeTask removes a task from nodeInfo if it's tracked there, and returns true
    65  // if nodeInfo was modified.
    66  func (nodeInfo *NodeInfo) removeTask(t *api.Task) bool {
    67  	oldTask, ok := nodeInfo.Tasks[t.ID]
    68  	if !ok {
    69  		return false
    70  	}
    71  
    72  	delete(nodeInfo.Tasks, t.ID)
    73  	if oldTask.DesiredState <= api.TaskStateCompleted {
    74  		nodeInfo.ActiveTasksCount--
    75  		nodeInfo.ActiveTasksCountByService[t.ServiceID]--
    76  	}
    77  
    78  	if t.Endpoint != nil {
    79  		for _, port := range t.Endpoint.Ports {
    80  			if port.PublishMode == api.PublishModeHost && port.PublishedPort != 0 {
    81  				portSpec := hostPortSpec{protocol: port.Protocol, publishedPort: port.PublishedPort}
    82  				delete(nodeInfo.usedHostPorts, portSpec)
    83  			}
    84  		}
    85  	}
    86  
    87  	reservations := taskReservations(t.Spec)
    88  	resources := nodeInfo.AvailableResources
    89  
    90  	resources.MemoryBytes += reservations.MemoryBytes
    91  	resources.NanoCPUs += reservations.NanoCPUs
    92  
    93  	if nodeInfo.Description == nil || nodeInfo.Description.Resources == nil ||
    94  		nodeInfo.Description.Resources.Generic == nil {
    95  		return true
    96  	}
    97  
    98  	taskAssigned := t.AssignedGenericResources
    99  	nodeAvailableResources := &resources.Generic
   100  	nodeRes := nodeInfo.Description.Resources.Generic
   101  	genericresource.Reclaim(nodeAvailableResources, taskAssigned, nodeRes)
   102  
   103  	return true
   104  }
   105  
   106  // addTask adds or updates a task on nodeInfo, and returns true if nodeInfo was
   107  // modified.
   108  func (nodeInfo *NodeInfo) addTask(t *api.Task) bool {
   109  	oldTask, ok := nodeInfo.Tasks[t.ID]
   110  	if ok {
   111  		if t.DesiredState <= api.TaskStateCompleted && oldTask.DesiredState > api.TaskStateCompleted {
   112  			nodeInfo.Tasks[t.ID] = t
   113  			nodeInfo.ActiveTasksCount++
   114  			nodeInfo.ActiveTasksCountByService[t.ServiceID]++
   115  			return true
   116  		} else if t.DesiredState > api.TaskStateCompleted && oldTask.DesiredState <= api.TaskStateCompleted {
   117  			nodeInfo.Tasks[t.ID] = t
   118  			nodeInfo.ActiveTasksCount--
   119  			nodeInfo.ActiveTasksCountByService[t.ServiceID]--
   120  			return true
   121  		}
   122  		return false
   123  	}
   124  
   125  	nodeInfo.Tasks[t.ID] = t
   126  
   127  	reservations := taskReservations(t.Spec)
   128  	resources := nodeInfo.AvailableResources
   129  
   130  	resources.MemoryBytes -= reservations.MemoryBytes
   131  	resources.NanoCPUs -= reservations.NanoCPUs
   132  
   133  	// minimum size required
   134  	t.AssignedGenericResources = make([]*api.GenericResource, 0, len(resources.Generic))
   135  	taskAssigned := &t.AssignedGenericResources
   136  
   137  	genericresource.Claim(&resources.Generic, taskAssigned, reservations.Generic)
   138  
   139  	if t.Endpoint != nil {
   140  		for _, port := range t.Endpoint.Ports {
   141  			if port.PublishMode == api.PublishModeHost && port.PublishedPort != 0 {
   142  				portSpec := hostPortSpec{protocol: port.Protocol, publishedPort: port.PublishedPort}
   143  				nodeInfo.usedHostPorts[portSpec] = struct{}{}
   144  			}
   145  		}
   146  	}
   147  
   148  	if t.DesiredState <= api.TaskStateCompleted {
   149  		nodeInfo.ActiveTasksCount++
   150  		nodeInfo.ActiveTasksCountByService[t.ServiceID]++
   151  	}
   152  
   153  	return true
   154  }
   155  
   156  func taskReservations(spec api.TaskSpec) (reservations api.Resources) {
   157  	if spec.Resources != nil && spec.Resources.Reservations != nil {
   158  		reservations = *spec.Resources.Reservations
   159  	}
   160  	return
   161  }
   162  
   163  func (nodeInfo *NodeInfo) cleanupFailures(now time.Time) {
   164  entriesLoop:
   165  	for key, failuresEntry := range nodeInfo.recentFailures {
   166  		for _, timestamp := range failuresEntry {
   167  			if now.Sub(timestamp) < monitorFailures {
   168  				continue entriesLoop
   169  			}
   170  		}
   171  		delete(nodeInfo.recentFailures, key)
   172  	}
   173  	nodeInfo.lastCleanup = now
   174  }
   175  
   176  // taskFailed records a task failure from a given service.
   177  func (nodeInfo *NodeInfo) taskFailed(ctx context.Context, t *api.Task) {
   178  	expired := 0
   179  	now := time.Now()
   180  
   181  	if now.Sub(nodeInfo.lastCleanup) >= monitorFailures {
   182  		nodeInfo.cleanupFailures(now)
   183  	}
   184  
   185  	versionedService := versionedService{serviceID: t.ServiceID}
   186  	if t.SpecVersion != nil {
   187  		versionedService.specVersion = *t.SpecVersion
   188  	}
   189  
   190  	for _, timestamp := range nodeInfo.recentFailures[versionedService] {
   191  		if now.Sub(timestamp) < monitorFailures {
   192  			break
   193  		}
   194  		expired++
   195  	}
   196  
   197  	if len(nodeInfo.recentFailures[versionedService])-expired == maxFailures-1 {
   198  		log.G(ctx).Warnf("underweighting node %s for service %s because it experienced %d failures or rejections within %s", nodeInfo.ID, t.ServiceID, maxFailures, monitorFailures.String())
   199  	}
   200  
   201  	nodeInfo.recentFailures[versionedService] = append(nodeInfo.recentFailures[versionedService][expired:], now)
   202  }
   203  
   204  // countRecentFailures returns the number of times the service has failed on
   205  // this node within the lookback window monitorFailures.
   206  func (nodeInfo *NodeInfo) countRecentFailures(now time.Time, t *api.Task) int {
   207  	versionedService := versionedService{serviceID: t.ServiceID}
   208  	if t.SpecVersion != nil {
   209  		versionedService.specVersion = *t.SpecVersion
   210  	}
   211  
   212  	recentFailureCount := len(nodeInfo.recentFailures[versionedService])
   213  	for i := recentFailureCount - 1; i >= 0; i-- {
   214  		if now.Sub(nodeInfo.recentFailures[versionedService][i]) > monitorFailures {
   215  			recentFailureCount -= i + 1
   216  			break
   217  		}
   218  	}
   219  
   220  	return recentFailureCount
   221  }