1. 前言
轉(zhuǎn)載請說明原文出處, 尊重他人勞動成果!
源碼位置: https://github.com/nicktming/kubernetes/blob/tming-v1.13/pkg/controller/controller_ref_manager.go
分支: tming-v1.13 (基于v1.13版本)
本文將分析
controller
中的一些公共結(jié)構(gòu)體, 因?yàn)楹芏嗟?code>controller會共用這些結(jié)構(gòu)體, 所以在分析各種controller
之前了解這些結(jié)構(gòu)體的作用很有必要. 本文將主要涉及pkg/controller/controller_ref_manager.go
.
2. 接口
architecture.png
這里主要關(guān)注一下
PodControllerRefManager
, 該類是為ReplicaSet
服務(wù)的, 另外具有類似功能的ReplicaSetControllerRefManager
是為Deployment
服務(wù)的. 所以主要介紹PodControllerRefManager
即可.
這兩個類都繼承
BaseControllerRefManager
,定義了一些共同的邏輯.
由于PodControllerRefManager
需要操作pod
, 所以它有一個PodControllerInterface
類型的成員變量.
3. BaseControllerRefManager
type BaseControllerRefManager struct {
// 當(dāng)前這個controller
Controller metav1.Object
Selector labels.Selector
canAdoptErr error
canAdoptOnce sync.Once
CanAdoptFunc func() error
}
// 判斷是否可以接收
func (m *BaseControllerRefManager) CanAdopt() error {
m.canAdoptOnce.Do(func() {
if m.CanAdoptFunc != nil {
m.canAdoptErr = m.CanAdoptFunc()
}
})
return m.canAdoptErr
}
// ClaimObject是為m.Controller尋找屬于它的obj
// 1. 如果孤兒obj與m.Controller匹配 則接收并返回true
// 2. 如果該obj以前屬于m.Controller, 但是現(xiàn)在不匹配了(有可能是obj發(fā)生改變也有可能是m.Controller某些屬性比如label發(fā)生改變),
// 則需要為該obj與m.Controller解綁
// 只有兩種情況下會返回true:
// 1. 孤兒obj與m.Controller匹配 則接收并返回true
// 2. 該obj以前屬于m.Controller并且現(xiàn)在也匹配 返回true
func (m *BaseControllerRefManager) ClaimObject(obj metav1.Object, match func(metav1.Object) bool, adopt, release func(metav1.Object) error) (bool, error) {
// 獲得該obj所屬的controller
controllerRef := metav1.GetControllerOf(obj)
if controllerRef != nil {
// 說明該obj已經(jīng)屬于另外一個controller
if controllerRef.UID != m.Controller.GetUID() {
// Owned by someone else. Ignore.
return false, nil
}
// 說明該obj屬于當(dāng)前這個controller
if match(obj) {
// 如果匹配得上
// We already own it and the selector matches.
// Return true (successfully claimed) before checking deletion timestamp.
// We're still allowed to claim things we already own while being deleted
// because doing so requires taking no actions.
return true, nil
}
// 說明該obj屬于當(dāng)前這個controller 但是沒有匹配上
// (因?yàn)槿绻薷牧薱ontroller的Match Labels然后apply到集群中, 那就有可能不匹配)
// 此時需要讓該obj與當(dāng)前controller解綁
// Owned by us but selector doesn't match.
// Try to release, unless we're being deleted.
// 如果當(dāng)前controller正處于刪除 就不用解綁了
if m.Controller.GetDeletionTimestamp() != nil {
return false, nil
}
// 讓該obj與當(dāng)前controller解綁
if err := release(obj); err != nil {
// If the pod no longer exists, ignore the error.
// 如果該obj已經(jīng)不存在了
if errors.IsNotFound(err) {
return false, nil
}
// Either someone else released it, or there was a transient error.
// The controller should requeue and try again if it's still stale.
return false, err
}
// Successfully released.
// 成功解綁
return false, nil
}
// 說明當(dāng)前這個obj是個孤兒obj, 不屬于任何controller
// It's an orphan.
// 1. 如果該controller或者當(dāng)前obj正在刪除或者與該obj與當(dāng)前controller不匹配 直接返回
if m.Controller.GetDeletionTimestamp() != nil || !match(obj) {
// Ignore if we're being deleted or selector doesn't match.
return false, nil
}
if obj.GetDeletionTimestamp() != nil {
// Ignore if the object is being deleted
return false, nil
}
// 2. 嘗試將該obj與當(dāng)前controller綁定在一起
// Selector matches. Try to adopt.
if err := adopt(obj); err != nil {
// If the pod no longer exists, ignore the error.
// 如果該obj已經(jīng)不存在了 返回
if errors.IsNotFound(err) {
return false, nil
}
// Either someone else claimed it first, or there was a transient error.
// The controller should requeue and try again if it's still orphaned.
return false, err
}
// 3. 成功綁定
// Successfully adopted.
return true, nil
}
ClaimObject
的作用是為m.Controller
尋找屬于它的obj
.
1. 如果孤兒obj
與m.Controller
匹配, 則接收并返回true
.
2. 如果該obj
以前屬于m.Controller
, 但是現(xiàn)在不匹配了(有可能是obj
發(fā)生改變也有可能是m.Controller
某些屬性比如label
發(fā)生改變), 則需要為該obj
與m.Controller
解綁.
claimObject.png
可以看到只有兩種情況下會返回
true
:
1. 孤兒obj
與m.Controller
匹配, 則接收并返回true
.
2. 該obj
以前屬于m.Controller
并且現(xiàn)在也匹配, 返回true
.
4. PodControllerRefManager
type PodControllerRefManager struct {
BaseControllerRefManager
controllerKind schema.GroupVersionKind
podControl PodControlInterface
}
func NewPodControllerRefManager(
podControl PodControlInterface,
controller metav1.Object,
selector labels.Selector,
controllerKind schema.GroupVersionKind,
canAdopt func() error,
) *PodControllerRefManager {
return &PodControllerRefManager{
BaseControllerRefManager: BaseControllerRefManager{
Controller: controller,
Selector: selector,
CanAdoptFunc: canAdopt,
},
controllerKind: controllerKind,
podControl: podControl,
}
}
這里很常規(guī), 傳入了
controller
和podControl
, 那說明就是該controller
要來接受pod
.
4.1 方法
AdoptPod 和 ReleasePod
func (m *PodControllerRefManager) AdoptPod(pod *v1.Pod) error {
// 在接收前最后一次再判斷一下是否可以接收
if err := m.CanAdopt(); err != nil {
return fmt.Errorf("can't adopt Pod %v/%v (%v): %v", pod.Namespace, pod.Name, pod.UID, err)
}
// Note that ValidateOwnerReferences() will reject this patch if another
// OwnerReference exists with controller=true.
addControllerPatch := fmt.Sprintf(
`{"metadata":{"ownerReferences":[{"apiVersion":"%s","kind":"%s","name":"%s","uid":"%s","controller":true,"blockOwnerDeletion":true}],"uid":"%s"}}`,
m.controllerKind.GroupVersion(), m.controllerKind.Kind,
m.Controller.GetName(), m.Controller.GetUID(), pod.UID)
// 發(fā)的Patch請求 局部更新 比如用ReplicaSet中的Template創(chuàng)建的pod在metadata都有一個ownerReferences表示它屬于哪一個replicaset實(shí)例
return m.podControl.PatchPod(pod.Namespace, pod.Name, []byte(addControllerPatch))
}
func (m *PodControllerRefManager) ReleasePod(pod *v1.Pod) error {
klog.V(2).Infof("patching pod %s_%s to remove its controllerRef to %s/%s:%s",
pod.Namespace, pod.Name, m.controllerKind.GroupVersion(), m.controllerKind.Kind, m.Controller.GetName())
deleteOwnerRefPatch := fmt.Sprintf(`{"metadata":{"ownerReferences":[{"$patch":"delete","uid":"%s"}],"uid":"%s"}}`, m.Controller.GetUID(), pod.UID)
// 發(fā)Patch請求局部更新metadata.ownerReferences字段中的內(nèi)容
err := m.podControl.PatchPod(pod.Namespace, pod.Name, []byte(deleteOwnerRefPatch))
if err != nil {
if errors.IsNotFound(err) {
return nil
}
if errors.IsInvalid(err) {
return nil
}
}
return err
}
AdoptPod
: 用來綁定某個pod
與controller
, 也就是這個pod
是屬于該controller
的. 注意podController
發(fā)的是Patch
請求, 因?yàn)樵?code>pod實(shí)際上是已經(jīng)運(yùn)行了的, 這是只是更新它的metadata.ownerReferences
字段, 用以辨別該pod
是屬于某個controller
的.
ReleasePod
: 很顯然該方法是用來解綁的, 也是更新metadata.ownerReferences
字段, 只是里面的內(nèi)容顯示是被某個controller
接綁的.
ClaimPods
func (m *PodControllerRefManager) ClaimPods(pods []*v1.Pod, filters ...func(*v1.Pod) bool) ([]*v1.Pod, error) {
var claimed []*v1.Pod
var errlist []error
// 分別定義match, adopt, release方法
match := func(obj metav1.Object) bool {
pod := obj.(*v1.Pod)
// Check selector first so filters only run on potentially matching Pods.
if !m.Selector.Matches(labels.Set(pod.Labels)) {
return false
}
for _, filter := range filters {
if !filter(pod) {
return false
}
}
return true
}
adopt := func(obj metav1.Object) error {
return m.AdoptPod(obj.(*v1.Pod))
}
release := func(obj metav1.Object) error {
return m.ReleasePod(obj.(*v1.Pod))
}
// 分別嘗試每個pod
for _, pod := range pods {
ok, err := m.ClaimObject(pod, match, adopt, release)
if err != nil {
errlist = append(errlist, err)
continue
}
if ok {
// 如果成功
claimed = append(claimed, pod)
}
}
return claimed, utilerrors.NewAggregate(errlist)
}
ClaimPods
的作用是從傳入的pods
選出屬于當(dāng)前m.Controller
的pods
. 調(diào)用的方法都已經(jīng)在上面介紹過了.
5. 總結(jié)
理解了
PodControllerRefManager
之后,ReplicaSetControllerRefManager
就很好理解了, 明顯是為Deployment
準(zhǔn)備的, 用來接收屬于它的ReplicaSet
.