kubernetes版本:1.13.2

接上一节:kubernetes垃圾回收器GarbageCollector Controller源码分析(一)

主要步骤

GarbageCollector Controller源码主要分为以下几部分:

  1. monitors作为生产者将变化的资源放入 graphChanges队列;同时 restMapper定期检测集群内资源类型,刷新 monitors

  2. runProcessGraphChanges从 graphChanges队列中取出变化的 item,根据情况放入 attemptToDelete队列;

  3. runProcessGraphChanges从 graphChanges队列中取出变化的 item,根据情况放入 attemptToOrphan队列;

  4. runAttemptToDeleteWorker从 attemptToDelete队列取出,尝试删除垃圾资源;

  5. runAttemptToOrphanWorker从 attemptToDelete队列取出,处理该孤立的资源; kubernetes垃圾回收器Garbage Collector Controller源码分析(二)_kubernetes 代码较复杂,便于讲的更清楚,调整了下讲解顺序。上一节分析了第1部分,本节分析第2、3部分。

runProcessGraphChanges处理主流程

来到源码k8s.io\kubernetes\pkg\controller\garbagecollector\graph_builder.go中,runProcessGraphChanges中一直死循环处理变化的资源对象:

  1. func (gb *GraphBuilder) runProcessGraphChanges() {

  2. for gb.processGraphChanges() {

  3. }

  4. }

一个协程一直循环从graphChanges队列中获取变化的资源对象,更新图形,填充dirty_queue。(graphChanges队列里数据来源于各个资源的monitors监听资源变化回调addFunc、updateFunc、deleteFunc)

  1. // Dequeueing an event from graphChanges, updating graph, populating dirty_queue.

  2. //从graphChanges中获取事件,更新图形,填充dirty_queue。(graphChanges队列里数据来源于各个资源的monitors监听资源变化回调addFunc、updateFunc、deleteFunc)

  3. func (gb *GraphBuilder) processGraphChanges() bool {

  4. item, quit := gb.graphChanges.Get()

  5. if quit {

  6. return false

  7. }

  8. defer gb.graphChanges.Done(item)

  9. event, ok := item.(*event)

  10. if !ok {

  11. utilruntime.HandleError(fmt.Errorf("expect a *event, got %v", item))

  12. return true

  13. }

  14. obj := event.obj

  15. //获取该变化资源obj的accessor

  16. accessor, err := meta.Accessor(obj)

  17. if err != nil {

  18. utilruntime.HandleError(fmt.Errorf("cannot access obj: %v", err))

  19. return true

  20. }

  21. klog.V(5).Infof("GraphBuilder process object: %s/%s, namespace %s, name %s, uid %s, event type %v", event.gvk.GroupVersion().String(), event.gvk.Kind, accessor.GetNamespace(), accessor.GetName(), string(accessor.GetUID()), event.eventType)

  22. // Check if the node already exists

  23. // 检查节点是否已存在

  24. //根据该变化资源obj的UID

  25. //uidToNode维护着资源对象依赖关系图表结构

  26. existingNode, found := gb.uidToNode.Read(accessor.GetUID())

  27. if found {

  28. // this marks the node as having been observed via an informer event

  29. // 1. this depends on graphChanges only containing add/update events from the actual informer

  30. // 2. this allows things tracking virtual nodes' existence to stop polling and rely on informer events

  31. //这标志着节点已经通过informer事件

  32. // 1.进行了观察。这取决于仅包含来自实际informer的添加/更新事件的graphChange

  33. // 2.这允许跟踪虚拟节点的存在以停止轮询和依赖informer事件

  34. existingNode.markObserved()

  35. }

  36. switch {

  37. //gc第一次运行时,uidToNode尚且没有初始化资源对象依赖关系图表结构,所以found为false,会新增节点

  38. case (event.eventType == addEvent || event.eventType == updateEvent) && !found:

  39. newNode := &node{

  40. identity: objectReference{

  41. OwnerReference: metav1.OwnerReference{

  42. APIVersion: event.gvk.GroupVersion().String(),

  43. Kind: event.gvk.Kind,

  44. UID: accessor.GetUID(),

  45. Name: accessor.GetName(),

  46. },

  47. Namespace: accessor.GetNamespace(),

  48. },

  49. dependents: make(map[*node]struct{}),

  50. owners: accessor.GetOwnerReferences(),

  51. deletingDependents: beingDeleted(accessor) && hasDeleteDependentsFinalizer(accessor),

  52. beingDeleted: beingDeleted(accessor),

  53. }

  54. gb.insertNode(newNode)

  55. // the underlying delta_fifo may combine a creation and a deletion into

  56. // one event, so we need to further process the event.

  57. //底层delta_fifo可以将创建和删除组合成一个事件,因此我们需要进一步处理事件。

  58. gb.processTransitions(event.oldObj, accessor, newNode)

  59. //uidToNode已经初始化资源对象依赖关系图表结构,所以found为true

  60. case (event.eventType == addEvent || event.eventType == updateEvent) && found:

  61. // handle changes in ownerReferences

  62. //处理ownerReferences中的更改

  63. added, removed, changed := referencesDiffs(existingNode.owners, accessor.GetOwnerReferences())

  64. if len(added) != 0 || len(removed) != 0 || len(changed) != 0 {

  65. // check if the changed dependency graph unblock owners that are

  66. // waiting for the deletion of their dependents.

  67. //检查更改的依赖关系图是否取消阻止等待删除其依赖项的所有者。

  68. gb.addUnblockedOwnersToDeleteQueue(removed, changed)

  69. // update the node itself

  70. //更新node的owner

  71. existingNode.owners = accessor.GetOwnerReferences()

  72. // Add the node to its new owners' dependent lists.

  73. //给新owner添加依赖资源列表

  74. gb.addDependentToOwners(existingNode, added)

  75. // remove the node from the dependent list of node that are no longer in

  76. // the node's owners list.

  77. //从不再属于该资源owner列表中删除该节点。

  78. gb.removeDependentFromOwners(existingNode, removed)

  79. }

  80.  

  81. // 该对象正在被删除中

  82. if beingDeleted(accessor) {

  83. existingNode.markBeingDeleted()

  84. }

  85. gb.processTransitions(event.oldObj, accessor, existingNode)

  86. //处理资源对象被删除的场景,涉及垃圾。比如,owner被删除,其依赖资源(从资源)也需要被删除掉,除非设置了Orphan

  87. case event.eventType == deleteEvent:

  88. if !found {

  89. klog.V(5).Infof("%v doesn't exist in the graph, this shouldn't happen", accessor.GetUID())

  90. return true

  91. }

  92. // 从图标中移除item资源,同时遍历owners,移除owner下的item资源

  93. gb.removeNode(existingNode)

  94. existingNode.dependentsLock.RLock()

  95. defer existingNode.dependentsLock.RUnlock()

  96. //如果该资源的从资源数大于0,则将该资源被删除信息加入absentOwnerCache缓存

  97. if len(existingNode.dependents) > 0 {

  98. gb.absentOwnerCache.Add(accessor.GetUID())

  99. }

  100. //遍历该资源的从资源加到删除队列里

  101. for dep := range existingNode.dependents {

  102. gb.attemptToDelete.Add(dep)

  103. }

  104. for _, owner := range existingNode.owners {

  105. ownerNode, found := gb.uidToNode.Read(owner.UID)

  106. //owner没发现 或者 owner的从资源不是正在被删除(只有该资源对象的终结器为foregroundDeletion Finalizer时deletingDependents被设为true,因为后台删除owner直接被删除,不会被其从资源block,故这里都不需要去尝试删除owner了)

  107. if !found || !ownerNode.isDeletingDependents() {

  108. continue

  109. }

  110.  

  111. // 这是让attempToDeleteItem检查是否删除了owner的依赖项,如果是,则删除所有者。

  112. gb.attemptToDelete.Add(ownerNode)

  113. }

  114. }

  115. return true

  116. }

该方法功能主要将对象、owner、从资源加入到attemptToDelete或attemptToOrphan。

1、 出队

从graphChanges队列取出资源对象,从GraphBuilder.uidToNode中读取该资源节点(uidToNode维护着资源对象依赖关系图表结构),found为true时表示图表存在该资源节点;

2、switch的第一个case

如果该资源是新增或者更新触发,且该资源对象不存在于图表中,gb.uidToNode.Write(n)会将其写入图标;gb.insertNode(newNode)中的gb.addDependentToOwners(n, n.owners)方法则会遍历该资源的owner,如果其owner不存在于图标中,则新增owner的虚拟节点到图标中,并将该资源和owner产生关联。如果owner不存在时,则尝试将owner加入到attemptToDelete队列中去;

  1. // addDependentToOwners将n添加到所有者的从属列表中。如果所有者不存在于gb.uidToNode中,则将创建"虚拟"节点以表示

  2. // 所有者。"虚拟"节点将入队到attemptToDelete,因此

  3. // attemptToDeleteItem()将根据API服务器验证所有者是否存在。

  4. func (gb *GraphBuilder) addDependentToOwners(n *node, owners []metav1.OwnerReference) {

  5. //遍历owner

  6. for _, owner := range owners {

  7. //获取owner node如果不存在于图中,则加虚拟owner节点

  8. ownerNode, ok := gb.uidToNode.Read(owner.UID)

  9. if !ok {

  10. // Create a "virtual" node in the graph for the owner if it doesn't

  11. // exist in the graph yet.

  12. //如果图形中尚未存在,则在图表中为所有者创建“虚拟”节点。

  13. ownerNode = &node{

  14. identity: objectReference{

  15. OwnerReference: owner,

  16. Namespace: n.identity.Namespace,

  17. },

  18. dependents: make(map[*node]struct{}),

  19. virtual: true,

  20. }

  21. klog.V(5).Infof("add virtual node.identity: %s\n\n", ownerNode.identity)

  22. gb.uidToNode.Write(ownerNode)

  23. }

  24. //给owner加该资源作为依赖

  25. ownerNode.addDependent(n)

  26. //owner不存在于图中时,才往删除队列添加

  27. if !ok {

  28. // Enqueue the virtual node into attemptToDelete.

  29. // The garbage processor will enqueue a virtual delete

  30. // event to delete it from the graph if API server confirms this

  31. // owner doesn't exist.

  32. //将虚拟节点排入attemptToDelete。

  33. // 如果API服务器确认owner不存在,垃圾处理器将排队虚拟删除事件以将其从图中删除。

  34. gb.attemptToDelete.Add(ownerNode)

  35. }

  36. }

  37. }

gb.processTransitions方法:新item正在被删,旧item没开始被删除,且终结器为Orphan Finalizer加入到attemptToOrphan队列;新item正在被删,旧item没开始被删除,且终结器为foregroundDeletion Finalizer,则加入到attemptToDelete队列。

  1. func (gb *GraphBuilder) processTransitions(oldObj interface{}, newAccessor metav1.Object, n *node) {

  2. //新的正在被删,旧的没开始被删除,且终结器为Orphan Finalizer

  3. if startsWaitingForDependentsOrphaned(oldObj, newAccessor) {

  4. klog.V(5).Infof("add %s to the attemptToOrphan", n.identity)

  5. //加入到Orphan队列

  6. gb.attemptToOrphan.Add(n)

  7. return

  8. }

  9.  

  10. //新的正在被删,旧的没开始被删除,且终结器为foregroundDeletion Finalizer

  11. if startsWaitingForDependentsDeleted(oldObj, newAccessor) {

  12. klog.V(2).Infof("add %s to the attemptToDelete, because it's waiting for its dependents to be deleted", n.identity)

  13. // if the n is added as a "virtual" node, its deletingDependents field is not properly set, so always set it here.

  14. n.markDeletingDependents()

  15. for dep := range n.dependents {

  16. gb.attemptToDelete.Add(dep)

  17. }

  18. gb.attemptToDelete.Add(n)

  19. }

  20. }

3、switch的第二个case

如果该资源是新增或者更新触发,且该资源对象存在于图表中。对比owneReferences是否有变更,referencesDiffs方法里会根据uid对比,added表示新owner里有,旧owner里没有的, removed表示旧owner里有,新owner里没有的, changed表示相同uid的owner不deepEqual的。

  1. func referencesDiffs(old []metav1.OwnerReference, new []metav1.OwnerReference) (added []metav1.OwnerReference, removed []metav1.OwnerReference, changed []ownerRefPair) {

  2. //key为uid, value为OwnerReference

  3. oldUIDToRef := make(map[string]metav1.OwnerReference)

  4. for _, value := range old {

  5. oldUIDToRef[string(value.UID)] = value

  6. }

  7. oldUIDSet := sets.StringKeySet(oldUIDToRef)

  8.  

  9. //key为uid, value为OwnerReference

  10. newUIDToRef := make(map[string]metav1.OwnerReference)

  11. for _, value := range new {

  12. newUIDToRef[string(value.UID)] = value

  13. }

  14. newUIDSet := sets.StringKeySet(newUIDToRef)

  15.  

  16. //新的里有,旧的里没有的为新增(根据uid判断)

  17. addedUID := newUIDSet.Difference(oldUIDSet)

  18.  

  19. //旧的里有,新的里没有的为删除(根据uid判断)

  20. removedUID := oldUIDSet.Difference(newUIDSet)

  21.  

  22. //取交集, 旧的和新的里都有的owner(根据uid判断)

  23. intersection := oldUIDSet.Intersection(newUIDSet)

  24.  

  25. for uid := range addedUID {

  26. added = append(added, newUIDToRef[uid])

  27. }

  28. for uid := range removedUID {

  29. removed = append(removed, oldUIDToRef[uid])

  30. }

  31.  

  32. //根据uid判断,两个uid相等的OwnerReference是否deepEqual,不等则加到changed

  33. for uid := range intersection {

  34. if !reflect.DeepEqual(oldUIDToRef[uid], newUIDToRef[uid]) {

  35. changed = append(changed, ownerRefPair{oldRef: oldUIDToRef[uid], newRef: newUIDToRef[uid]})

  36. }

  37. }

  38. return added, removed, changed

  39. }

整体来说,owner发生变化,addUnblockedOwnersToDeleteQueue方法会判断:如果阻塞ownerReference指向某个对象被删除,或者设置为 BlockOwnerDeletion=false,则将该对象添加到attemptToDelete队列;

  1. // if an blocking ownerReference points to an object gets removed, or gets set to

  2. // "BlockOwnerDeletion=false", add the object to the attemptToDelete queue.

  3. //如果阻塞ownerReference指向某个对象被删除,或者设置为

  4. // "BlockOwnerDeletion = false",则将该对象添加到attemptToDelete队列。

  5. func (gb *GraphBuilder) addUnblockedOwnersToDeleteQueue(removed []metav1.OwnerReference, changed []ownerRefPair) {

  6. for _, ref := range removed {

  7. //被移除的OwnersReferences,BlockOwnerDeletion为true

  8. if ref.BlockOwnerDeletion != nil && *ref.BlockOwnerDeletion {

  9. //依赖图表中发现,则加入删除队列

  10. node, found := gb.uidToNode.Read(ref.UID)

  11. if !found {

  12. klog.V(5).Infof("cannot find %s in uidToNode", ref.UID)

  13. continue

  14. }

  15. //加入尝试删除队列删除这个owner

  16. gb.attemptToDelete.Add(node)

  17. }

  18. }

  19.  

  20. // Owners存在且发生变化,旧的BlockOwnerDeletion为true, 新的BlockOwnerDeletion为空或者BlockOwnerDeletion为false则删除owner(父节点)

  21. for _, c := range changed {

  22. wasBlocked := c.oldRef.BlockOwnerDeletion != nil && *c.oldRef.BlockOwnerDeletion

  23. isUnblocked := c.newRef.BlockOwnerDeletion == nil || (c.newRef.BlockOwnerDeletion != nil && !*c.newRef.BlockOwnerDeletion)

  24. if wasBlocked && isUnblocked {

  25. node, found := gb.uidToNode.Read(c.newRef.UID)

  26. if !found {

  27. klog.V(5).Infof("cannot find %s in uidToNode", c.newRef.UID)

  28. continue

  29. }

  30. gb.attemptToDelete.Add(node)

  31. }

  32. }

  33. }

更新node的owner;在依赖图表中给新owner添加该node;在依赖图表中,被删除的owner列表下删除该节点。

gb.processTransitions方法:新item正在被删,旧item没开始被删除,且终结器为Orphan Finalizer加入到attemptToOrphan队列;新item正在被删,旧item没开始被删除,且终结器为foregroundDeletion Finalizer,则加入到attemptToDelete队列。

4、switch的第三个case

如果该资源是删除时触发,从图表中移除item资源,同时遍历owners,移除owner下的item资源;如果该资源的从资源数大于0,则将该资源被删除信息(uid)加入absentOwnerCache缓存,这样处理该资源的从资源时,就知道owner不存在了。遍历该资源的从资源加到删除队列里;如果从图表中发现 owner或者 owner的从资源正在被删除,则尝试将owner加入到attemptToDelete队列中,去尝试删除owner。

整理流程

  • 当controllermanager重启时,会全量listwatch一遍所有对象,gc collector维护的uidToNode图表里各个资源对象node是不存在的,此时会走第一个switch case,构建完整关系图表,如果owner不存在则先构建虚拟owner节点,同时加入attemptToDelete队列,尝试去删除这个owner,其实即使加入到attemptToDelete队列,也不一定会被删除,还会进行一系列判断,这个下一节再分析;将正在删除的资源,同时Finalizer为Orphan的加入到attemptToOrphan队列;为foreground的资源以及其从资源加入到attemptToDelete队列,并将deletingDependents设置为true;

  • 添加或者更新事件时,且图表中存在item资源对象时,会走第二个switch case,对item的owner变化进行判断,并维护更新图表;同理将正在删除的资源,同时Finalizer为Orphan的加入到attemptToOrphan队列;Finalizer为foreground的资源以及其从资源加入到attemptToDelete队列,并将deletingDependents设置为true;

  • 如果是删除事件,则会更新图表,并处理和其相关的从资源和其owner加入到attemptToDelete队列。

参考:

k8s官方文档garbage-collection英文版:https://kubernetes.io/docs/concepts/workloads/controllers/garbage-collection/

依赖图标生成库gonum Api文档:https://godoc.org/gonum.org/v1/gonum/graph

graphviz下载:https://graphviz.gitlab.io/pages/Download/Downloadwindows.html

 

kubernetes垃圾回收器Garbage Collector Controller源码分析(二)_kubernetes_02