Kubernetes 控制器深度剖析 概述 Kubernetes 的控制器是实现声明式 API 的核心机制。控制器通过”reconcile”(调和)模式,持续将期望状态与实际状态进行调和,确保集群始终维持在期望状态。Kubernetes 提供了众多内置控制器,每个控制器负责特定资源的管理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 ┌─────────────────────────────────────────────────────────────────────────┐ │ Kubernetes 控制器架构图 │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────────┐ │ │ │ kube-controller- │ │ │ │ manager │ │ │ └──────────┬──────────┘ │ │ │ │ │ ┌───────────────────────────┼───────────────────────────┐ │ │ │ │ │ │ │ ┌──▼────────┐ ┌──────────────▼────────────┐ ┌────────────▼────────┐ │ │ │ Node │ │ Deployment │ │ ReplicaSet │ │ │ │ Controller│ │ Controller │ │ Controller │ │ │ └───────────┘ └───────────────────────────┘ └────────────────────┘ │ │ │ │ │ │ ┌──▼────────┐ ┌──────────────┐ ┌────────────┐ ┌──────────────▼───┐ │ │ │ Route │ │ Service │ │ Endpoint │ │ Job │ │ │ │ Controller│ │ Controller │ │ Controller│ │ Controller │ │ │ └───────────┘ └──────────────┘ └────────────┘ └──────────────────┘ │ │ │ │ │ │ ┌──▼────────┐ ┌──────────────┐ ┌────────────┐ ┌──────────────▼───┐ │ │ │ PV/ PVC │ │ Namespace │ │ Garbage │ │ CronJob │ │ │ │ Controller│ │ Controller │ │ Collector │ │ Controller │ │ │ └───────────┘ └──────────────┘ └────────────┘ └──────────────────┘ │ │ │ │ │ │ ┌──▼────────┐ ┌──────────────┐ ┌────────────┐ ┌──────────────▼───┐ │ │ │ Token │ │ PVC │ │ DaemonSet │ │ StatefulSet │ │ │ │ Controller│ │ Protection │ │ Controller│ │ Controller │ │ │ └───────────┘ └──────────────┘ └────────────┘ └──────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────┘
1. 控制器通用模式 1.1 Reconcile 模式 所有 Kubernetes 控制器都遵循相同的 reconcile 模式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 ┌─────────────────────────────────────────────────────────────────────────┐ │ Reconcile 模式流程 │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ 期望状态 (Spec) 实际状态 (Status) │ │ │ │ │ │ │ │ │ │ └──────────────┬─────────────────────────┘ │ │ │ │ │ ▼ │ │ ┌─────────────────────┐ │ │ │ Controller │ │ │ │ Reconcile │ │ │ └──────────┬──────────┘ │ │ │ │ │ ┌─────────────┼─────────────┐ │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ 观察 │ │ 分析 │ │ 执行 │ │ │ │ 当前 │ │ 差异 │ │ 操作 │ │ │ │ 状态 │ │ │ │ │ │ │ └──────────┘ └──────────┘ └──────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────┘
1.2 控制器实现模板 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 type Controller interface { Run(ctx context.Context, workers int ) } type ControllerImpl struct { client clientset.Interface queue workqueue.RateLimitingInterface informer cache.SharedIndexInformer reconciler func (ctx context.Context, key string ) error } func (c *ControllerImpl) Run(ctx context.Context, workers int ) { go c.informer.Run(ctx.Done()) if !cache.WaitForCacheSync(ctx.Done(), c.informer.HasSynced) { return } for i := 0 ; i < workers; i++ { go wait.Until(c.worker, time.Second, ctx.Done()) } } func (c *ControllerImpl) worker() { for c.processNextItem() { } } func (c *ControllerImpl) processNextItem() bool { key, quit := c.queue.Get() if quit { return false } defer c.queue.Done(key) if err := c.reconciler(context.TODO(), key.(string )); err != nil { c.queue.AddRateLimited(key) } return true }
2. Deployment 控制器 2.1 Deployment 控制器架构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 ┌─────────────────────────────────────────────────────────────────────────┐ │ Deployment 控制器流程 │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ Deployment ReplicaSet Pod │ │ │ │ │ │ │ │ 1. 创建/更新 │ │ │ │ │ ───────────────────────────► │ │ │ │ │ │ 2. 创建 Pod │ │ │ │ │ ───────────────────────────► │ │ │ │ │ │ │ │ │ 3. 滚动更新策略 │ │ │ │ │ ───────────────────────────► │ │ │ │ │ │ 4. 创建新 ReplicaSet │ │ │ │ │ ───────────────────────────► │ │ │ │ │ │ │ │ │ │ 5. 逐步替换 Pod │ │ │ │ │ ───────────────────────────► │ │ │ │ │ │ │ │ │ 6. 清理旧 ReplicaSet │ │ │ │ │ ───────────────────────────► │ │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────────────────┘
2.2 滚动更新策略 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 type RollingUpdateDeployment struct { MaxUnavailable *int32 MaxSurge *int32 } func (d *Deployment) calculateAvailableReplicas( availableReplicas int32 , maxUnavailable int32 , ) int32 { return availableReplicas - maxUnavailable } func (d *Deployment) calculateNewReplicas( allRSs []*apps.ReplicaSet, newRSAvailableReplicas int32 , ) (int32 , error ) { maxSurge := maxUnavailable := int32 (0 ) if d.Spec.Strategy.RollingUpdate != nil { maxSurge = *d.Spec.Strategy.RollingUpdate.MaxSurge maxUnavailable = *d.Spec.Strategy.RollingUpdate.MaxUnavailable } desiredReplicas := d.Spec.Replicas newReplicas := desiredReplicas - newRSAvailableReplicas totalAvailable := newRSAvailableReplicas + availableRS.Replicas if totalAvailable < desiredReplicas-maxUnavailable { newReplicas = min(newReplicas+maxSurge, desiredReplicas) } return newReplicas, nil }
2.3 Deployment 状态机 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 func (dc *DeploymentController) checkRollingUpdateConditions(d *apps.Deployment) error { if d.Spec.ProgressDeadlineSeconds != nil { if d.Status.ObservedGeneration < d.Generation { } } }
3. ReplicaSet 控制器 3.1 ReplicaSet 控制器逻辑 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 func (rsc *ReplicaSetController) reconcile( ctx context.Context, rs *apps.ReplicaSet, ) error { pods, err := rsc.getPodMapForRS(rs) if err != nil { return err } manageReplicasDiff := len (pods) - int (*rs.Spec.Replicas) filteredPods := filterActivePods(pods) if manageReplicasDiff > 0 { deletionCount := manageReplicasDiff rsc.deletePods(filteredPods, deletionCount) } if manageReplicasDiff < 0 { wantReplicas := -manageReplicasDiff rsc.createPods(rs, filteredPods, wantReplicas) } return rsc.updateReplicaSetStatus(rs, filteredPods) }
3.2 Pod 选择与匹配 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 func (rsc *ReplicaSetController) getPodMapForRS(rs *apps.ReplicaSet) ([]*v1.Pod, error ) { pods, err := rsc.podLister.Pods(rs.Namespace).List(rs.Spec.Selector) if err != nil { return nil , err } filtered := make ([]*v1.Pod, 0 ) for _, pod := range pods { if rs.Spec.Selector.Matches(labels.Set(pod.Labels)) { filtered = append (filtered, pod) } } return filtered, nil }
4. StatefulSet 控制器 4.1 StatefulSet 控制器核心逻辑 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 type StatefulSetControl interface { UpdateStatefulSet( ctx context.Context, set *apps.StatefulSet, pods []*v1.Pod, ) (*apps.StatefulSetStatus, error ) } func (ssc *defaultStatefulSetControl) UpdateStatefulSet( ctx context.Context, set *apps.StatefulSet, pods []*v1.Pod, ) (*apps.StatefulSetStatus, error ) { if err := ssc.pvcListerSynced(); err != nil { return nil , err } pods = sortPods(pods, set) for i := range pods { if isTerminating(pods[i]) { continue } if isPending(pods[i]) { } } if len (pods) < int (*set.Spec.Replicas) { return ssc.createPod(set, ordinal) } return ssc.calculateStatus(set, pods), nil }
4.2 有序部署与终止 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 ┌─────────────────────────────────────────────────────────────────────────┐ │ StatefulSet 有序部署流程 │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ 部署顺序 (Pod-0 -> Pod-1 -> Pod-2 -> ...): │ │ │ │ ┌────────────────────────────────────────────────────────────────┐ │ │ │ │ │ │ │ Pod-0 ┌─────┐ │ │ │ │ ─────────►│Running│ ─────────────────────────► Ready │ │ │ │ └─────┘ │ │ │ │ │ │ │ │ │ ▼ │ │ │ │ Pod-1 启动 (等待 Pod-0 Ready) │ │ │ │ │ │ │ │ │ ▼ │ │ │ │ Pod-2 启动 (等待 Pod-1 Ready) │ │ │ │ ... │ │ │ │ │ │ │ └────────────────────────────────────────────────────────────────┘ │ │ │ │ 终止顺序 (Pod-N -> Pod-(N-1) -> ... -> Pod-0): │ │ │ │ ┌────────────────────────────────────────────────────────────────┐ │ │ │ │ │ │ │ Pod-N 终止 ──► Pod-(N-1) 终止 ──► ... ──► Pod-0 终止 │ │ │ │ │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ │ │ 先删除 再删除 最后删除 │ │ │ │ │ │ │ └────────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────┘
5. DaemonSet 控制器 5.1 DaemonSet 控制器逻辑 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 func (dsc *DaemonSetController) Run(ctx context.Context, workers int ) { } func (dsc *DaemonSetController) worker() { for dsc.processNextWorkItem() { } } func (dsc *DaemonSetController) syncDaemonSet( ctx context.Context, key string , ) error { namespace, name, err := cache.SplitMetaNamespaceKey(key) ds, err := dsc.dsLister.DaemonSets(namespace).Get(name) nodes, err := dsc.getNodesForDaemonSet(ds) pods, err := dsc.getPodDaemonSetMap(ds) return dsc.manageDaemonSet(ctx, ds, nodes, pods) }
5.2 节点选择算法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 func (dsc *DaemonSetController) getNodesForDaemonSet( ds *apps.DaemonSet, ) ([]*v1.Node, error ) { allNodes, err := dsc.nodeLister.List(labels.Everything()) if err != nil { return nil , err } readyNodes := filterReadyNodes(allNodes) if ds.Spec.Template.Spec.NodeSelector != nil { selectedNodes := make ([]*v1.Node, 0 ) for _, node := range readyNodes { if labels.Selected(node.Labels, ds.Spec.Template.Spec.NodeSelector) { selectedNodes = append (selectedNodes, node) } } return selectedNodes, nil } return readyNodes, nil }
6. Job 控制器 6.1 Job 控制器逻辑 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 func (jm *JobController) syncJob(ctx context.Context, key string ) error { ns, name, _ := cache.SplitMetaNamespaceKey(key) job, _ := jm.jobLister.Jobs(ns).Get(name) pods, _ := jm.getPodsForJob(job) active := countActivePods(pods) failed := countFailedPods(pods) succeeded := countSucceededPods(pods) if active > *job.Spec.Parallelism { jm.deleteExtraPods(pods, active-*job.Spec.Parallelism) } if active < *job.Spec.Parallelism { if !isJobSuspended(job) { jm.createNewPod(job, pods) } } return jm.updateJobStatus(job, active, failed, succeeded) }
6.2 Job 完成判断 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 func isJobFinished (job *batch.Job) (bool , *batch.JobCondition) { if job.Spec.Completions != nil { if succeeded >= *job.Spec.Completions { return true , &batch.JobCondition{ Type: batch.JobComplete, Status: v1.ConditionTrue, } } } else { if succeeded > 0 && active == 0 { return true , &batch.JobCondition{ Type: batch.JobComplete, Status: v1.ConditionTrue, } } } if job.Spec.BackoffLimit != nil { if failed >= *job.Spec.BackoffLimit { return true , &batch.JobCondition{ Type: batch.JobFailed, Status: v1.ConditionTrue, } } } return false , nil }
7. CronJob 控制器 7.1 CronJob 控制器架构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 ┌─────────────────────────────────────────────────────────────────────────┐ │ CronJob 控制器流程 │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌──────────────┐ ┌──────────────┐ │ │ │ CronJob │ │ CronJob │ │ │ │ Informer │ │ Controller │ │ │ └──────┬───────┘ └──────┬───────┘ │ │ │ │ │ │ │ Watch │ │ │ └─────────────────────────────────│──────────────────────────► │ │ │ │ │ ▼ │ │ ┌────────────────────────┐ │ │ │ 检查调度时间 │ │ │ │ (每 10 秒检查一次) │ │ │ └───────────┬────────────┘ │ │ │ │ │ ┌──────────────┴──────────────┐ │ │ │ │ │ │ ▼ ▼ │ │ 应该运行? 已经运行? │ │ (是) (否) │ │ │ │ │ │ ▼ │ │ │ 创建 Job 跳过 │ │ │ │ │ │ ▼ │ │ │ 创建 Pod(s) ◄────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────┘
7.2 CronJob 调度逻辑 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 func (jm *CronJobController) syncAll() { cronJobs, _ := jm.cjLister.List(labels.Everything()) for _, cj := range cronJobs { jm.syncOne(cj) } } func (jm *CronJobController) syncOne(cj *batch.CronJob) error { if cj.Spec.Suspend != nil && *cj.Spec.Suspend { return nil } times, err := getRecentScheduledTimes(cj) if err != nil { return err } js, _ := jm.jobLister.Jobs(cj.Namespace).List(labels.Everything()) recentJob := getRecentJob(cj, js) for _, t := range times { if t.After(recentJob.Created) && t.Before(now) { job, err := jm.createJob(cj, t) if err != nil { return err } jm.recorder.Eventf(cj, v1.EventTypeNormal, "SuccessfulCreate" , "Created job %s" , job.Name) } } if cj.Spec.SuccessfulJobsHistoryLimit != nil { cleanOldJobs(cj, js) } return nil }
8. Service 控制器 8.1 Service 控制器逻辑 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 func (sc *ServiceController) syncService(ctx context.Context, key string ) error { namespace, name, _ := cache.SplitMetaNamespaceKey(key) svc, err := sc.serviceLister.Services(namespace).Get(name) if err != nil { if errors.IsNotFound(err) { cachedSvc, exists := sc.cache.Get(key) if !exists { return nil } return sc.deleteEndpointSlices(cachedSvc.(*v1.Service)) } return err } if !isServiceActive(svc) { return nil } pods, err := sc.getPodsForService(svc) return sc.syncEndpoints(svc, pods) } func (sc *ServiceController) syncEndpoints( svc *v1.Service, pods []*v1.Pod, ) error { endpoints := buildEndpoints(svc, pods) slices, _ := sc.endpointSliceLister.Slices(svc.Namespace, "" ) return sc.updateEndpointSlices(svc, endpoints, slices) }
9. Node 控制器 9.1 Node 控制器职责 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 type NodeController struct { nodeLister cache.GenericLister nodeInformer cache.SharedIndexInformer taintManager *controller.NoExecuteTaintManager nodeHealthData map [string ]*nodeHealthData }
9.2 节点状态判断 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 func (nc *NodeController) DoEviction(ctx context.Context, nodeName string ) error { pods, _ := nc.podLister.Pods(v1.NamespaceAll).List(labels.Everything()) for _, pod := range pods { if pod.Spec.NodeName == nodeName { if !isPodEvictable(pod) { continue } nc.evictPod(pod) } } } func (nc *taintManager) Run(ctx context.Context) { }
10. 控制器通用工具 10.1 WorkQueue 实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 queue.AddRateLimited(key) queue.AddAfter(key, delay) queue.Add(key) func enqueueWithBackoff (queue workqueue.RateLimitingInterface, key string , err error ) { baseDelay := time.Second maxDelay := 5 * time.Minute delay := baseDelay * time.Duration(math.Pow(2 , retryCount)) if delay > maxDelay { delay = maxDelay } queue.AddAfter(key, delay) }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 func NewFilteredSharedIndexInformer ( client clientset.Interface, resourceVersion string , ) cache.SharedIndexInformer { return cache.NewSharedIndexInformer( &cache.ListWatch{ ListFunc: func (options metav1.ListOptions) (runtime.Object, error ) { return client.CoreV1().Namespaces().List(context.TODO(), options) }, WatchFunc: func (options metav1.ListOptions) (watch.Interface, error ) { return client.CoreV1().Namespaces().Watch(context.TODO(), options) }, }, &v1.Pod{}, resyncPeriod, indexers, ) }
11. 控制器协作关系 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 ┌─────────────────────────────────────────────────────────────────────────┐ │ 控制器协作关系图 │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────┐ │ │ │ Deployment │ │ │ │ Controller │ │ │ └──────┬──────┘ │ │ │ │ │ ▼ │ │ ┌─────────────┐ │ │ │ ReplicaSet │ │ │ │ Controller │ │ │ └──────┬──────┘ │ │ │ │ │ ┌─────────────────┼─────────────────┐ │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │ Pod-1 │ │ Pod-2 │ │ Pod-N │ │ │ └─────────┘ └─────────┘ └─────────┘ │ │ │ │ │ │ │ └─────────────────┼─────────────────┘ │ │ ▼ │ │ ┌─────────────┐ │ │ │ Endpoint │ │ │ │ Controller │ │ │ └──────┬──────┘ │ │ │ │ │ ▼ │ │ ┌─────────────┐ │ │ │ Endpoint │ │ │ │ Slice │ │ │ └─────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────┘
12. 关键源码路径
控制器
源码路径
Deployment
pkg/controller/deployment/
ReplicaSet
pkg/controller/replicaset/
StatefulSet
pkg/controller/statefulset/
DaemonSet
pkg/controller/daemonset/
Job
pkg/controller/job/
CronJob
pkg/controller/cronjob/
Service
pkg/controller/service/
Node
pkg/controller/node/
PV/PVC
pkg/controller/volume/
GC
pkg/controller/garbagecollector/
Controller Manager
cmd/kube-controller-manager/
面试题 基础题 1. Kubernetes 控制器的核心思想是什么?
Kubernetes 控制器的核心思想是”声明式 API + 状态调和(Reconcile)”。用户声明期望状态(如需要 3 个 Pod 副本),控制器持续监控实际状态与期望状态的差异,并采取行动使两者一致。这种模式的关键点:
声明式 :用户描述”做什么”而非”怎么做”
持续调和 :控制器持续运行,快速响应状态变化
最终一致 :允许短暂的不一致,控制器会逐步调和
2. Deployment 和 ReplicaSet 的区别是什么?
Deployment :更高级别的资源,管理 ReplicaSet 的创建、更新和删除,支持滚动更新、回滚等高级功能
ReplicaSet :低级别资源,确保指定数量的 Pod 副本运行,通常不直接使用,而是由 Deployment 管理
Deployment 通过管理 ReplicaSet 来间接管理 Pod,而 ReplicaSet 直接管理 Pod。
3. StatefulSet 与 Deployment 的主要区别是什么?
稳定的网络标识 :StatefulSet Pod 有稳定的唯一主机名(pod-name.statefulset-name.namespace.svc.cluster.local)
有序部署和终止 :Pod 按序号顺序部署/终止,保证数据一致性
稳定的持久存储 :使用 PVC,删除 Pod 后数据保留
独立的 PVC :每个 Pod 有自己独立的 PVC,不共享
4. DaemonSet 的典型使用场景有哪些?
日志收集器 :如 Fluentd、Filebeat
监控代理 :如 Prometheus Node Exporter、Datadog Agent
网络插件 :如 kube-proxy、Calico node
存储插件 :如 Ceph、GlusterFS 客户端
日志轮转 :如 logrotate
5. Job 和 CronJob 的区别是什么?
Job :一次性任务,执行完成后 Pod 保持成功状态
CronJob :定时任务,按 cron 表达式定期执行,基于 Job 创建
CronJob 在每个调度时间点创建一个 Job,Job 再创建 Pod 执行实际任务。
中级题 6. Deployment 的滚动更新策略是如何工作的?
Deployment 滚动更新通过 maxUnavailable 和 maxSurge 控制:
1 2 3 4 5 6 spec: strategy: type: RollingUpdate rollingUpdate: maxUnavailable: 25 % maxSurge: 25 %
工作流程:
创建新的 ReplicaSet(rs-new), replicas = 期望数
逐步从旧 ReplicaSet 转移 Pod 到新 ReplicaSet
同时保持总数不超过 期望数 + maxSurge
同时保证可用数不低于 期望数 - maxUnavailable
完成后删除旧 ReplicaSet
7. Kubernetes 如何保证 Job 至少执行一次?
Job 控制器通过以下机制保证至少执行一次:
活跃 Pod 追踪 :跟踪正在运行的 Pod 数量
Pod 失败重试 :失败的 Pod 会被重新创建(受 backoffLimit 限制)
指数退避 :连续失败时延迟增加重试间隔
优雅终止 :Pod 终止信号给予清理时间
挂起检测 :当 Job 从挂起状态恢复时,会创建缺失的 Pod
8. StatefulSet 的 PVC 生命周期是怎样的?
创建时 :StatefulSet controller 在创建 Pod 前创建 PVC
运行时 :PVC 与 Pod 绑定,Pod 使用 PVC
删除时 :默认保留 PVC(由 PVC Protection 控制器保护)
清理时 :需要手动删除 PVC 或配置 persistentVolumeClaimRetentionPolicy
1 2 3 4 spec: persistentVolumeClaimRetentionPolicy: whenDeleted: Retain whenScaled: Retain
9. 控制器如何使用 Informer 和 WorkQueue?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 type Controller struct { informer cache.SharedIndexInformer lister cache.GenericLister queue workqueue.RateLimitingInterface } func (c *Controller) Run() { go c.informer.Run(stopCh) cache.WaitForCacheSync(stopCh, c.informer.HasSynced) for i := 0 ; i < workers; i++ { go wait.Until(c.worker, time.Second, stopCh) } } func (c *Controller) worker() { for c.processItem() { } }
Informer 负责监听 API Server 变化并缓存本地副本,WorkQueue 存储待处理的任务,Worker 协程从队列获取任务执行 reconcile。
10. Node 控制器如何处理节点故障?
节点故障处理流程:
检测失败 :节点心跳超时(默认 40 秒标记为 Unknown)
标记状态 :节点状态更新为 ConditionUnknown
等待驱逐 :等待 podEvictionTimeout(默认 5 分钟)
执行驱逐 :在所有匹配节点上删除 Pod
Pod 重建 :由 Replicaset/DaemonSet 控制器在新节点重建
关键时间点:40 秒 Unknown → 5 分钟后开始驱逐 → Replicaset 重建 Pod
高级题 11. 分析 Deployment 控制器如何实现回滚机制?
Deployment 使用 ReplicaSet 版本历史实现回滚:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 const RevisionAnnotation = "deployment.kubernetes.io/revision" func (dc *DeploymentController) rollbackTo( revision int64 , ) error { oldRS, err := dc.getReplicaSetByRevision(revision) if err != nil { return err } activeRS, err := dc.getActiveReplicaSet(d) if err != nil { return err } d.Spec.Template = oldRS.Spec.Template _, err = dc.client.AppsV1().Deployments(d.Namespace).Update(ctx, d) dc.scaleReplicaSet(oldRS, *d.Spec.Replicas) dc.scaleReplicaSet(activeRS, 0 ) return nil }
每个滚动更新都会创建新的 ReplicaSet,历史 ReplicaSet 会被保留(默认保留 10 个),用户可以通过 kubectl rollout undo 回滚到任意历史版本。
12. StatefulSet 的顺序保证是如何实现的?
StatefulSet 控制器通过以下机制保证有序性:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 func getOrdinal (pod *v1.Pod) int { parts := strings.Split(pod.Name, "-" ) ordinal, _ := strconv.Atoi(parts[len (parts)-1 ]) return ordinal } func shouldLaunchPod (set *apps.StatefulSet, ordinal int ) bool { if ordinal == 0 { return true } prevPod := getPodByOrdinal(set, ordinal-1 ) return isPodReady(prevPod) } func shouldTerminatePod (set *apps.StatefulSet, ordinal int ) bool { if ordinal == getMaxOrdinal(set) { return true } for i := ordinal + 1 ; i <= getMaxOrdinal(set); i++ { pod := getPodByOrdinal(set, i) if !isPodTerminated(pod) { return false } } return true }
13. CronJob 控制器的并发策略是如何工作的?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 const ( AllowConcurrent = "" ForbidConcurrent = "Forbid" ReplaceConcurrent = "Replace" ) func (jm *CronJobController) allowStart( cj *batch.CronJob, scheduledTime time.Time, ) bool { switch cj.Spec.ConcurrencyPolicy { case batch.AllowConcurrent: return true case batch.ForbidConcurrent: runningJobs := getRunningJobs(cj, scheduledTime) return len (runningJobs) == 0 case batch.ReplaceConcurrent: stopRunningJobs(cj) return true } return true }
场景题 14. 如何排查 Deployment 的滚动更新卡住的问题?
排查步骤:
检查 Deployment 状态:kubectl describe deployment <name>
查看滚动更新事件:检查是否有错误信息
检查 ReplicaSet:kubectl get rs
常见原因:
镜像拉取失败 :检查 Pod 事件和镜像配置
资源不足 :节点资源耗尽
健康检查失败 :Liveness/Readiness Probe 配置错误
挂载卷失败 :PVC 未绑定或挂载超时
maxUnavailable=0 且 maxSurge=0 :配置冲突导致无法更新
检查历史记录:kubectl rollout history deployment <name>
15. 如果需要暂停和恢复 Deployment 滚动更新?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 kubectl rollout pause deployment/<name> kubectl rollout resume deployment/<name> kubectl rollout status deployment/<name> kubectl rollout undo deployment/<name> kubectl rollout undo deployment/<name> --to-revision=2
暂停期间对 Deployment 的修改会生效但不执行滚动更新,恢复后会按最新配置继续滚动。
16. 如何设计一个支持金丝雀发布的 Deployment 策略?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 apiVersion: apps/v1 kind: Deployment metadata: name: myapp-v1 spec: replicas: 4 strategy: type: RollingUpdate rollingUpdate: maxUnavailable: 0 maxSurge: 1 selector: matchLabels: app: myapp template: metadata: labels: app: myapp version: v1 spec: containers: - name: myapp image: myapp:v1 --- apiVersion: apps/v1 kind: Deployment metadata: name: myapp-v2-canary spec: replicas: 1 strategy: type: RollingUpdate selector: matchLabels: app: myapp template: metadata: labels: app: myapp version: v2 spec: containers: - name: myapp image: myapp:v2 --- apiVersion: v1 kind: Service metadata: name: myapp spec: selector: app: myapp ports: - port: 80
通过控制 v2 的副本数,可以控制进入金丝雀版本的流量比例。