You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用mock client-go时Replica状态值未同步的原因排查

背景信息

目标是编写Go语言单元测试,验证指定命名空间内的Deployment是否就绪,使用k8s.io/client-go/kubernetes/fake库实现。

计划执行以下步骤:

  • 使用client-go创建mock Kubernetes API:
kubectl := &KubeClient{
        testclient.NewSimpleClientset(),
}
  • 创建初始副本数为1的mock Deployment:
deploymentSpec := &appsv1.Deployment{
    ObjectMeta: metav1.ObjectMeta{
        Name:      RESOURCE_NAME,
        Namespace: NAMESPACE,
        Labels:    resourceLabel,
    },
    Spec: appsv1.DeploymentSpec{
        Replicas: &replicas,
        Template: v1.PodTemplateSpec{
            Spec: v1.PodSpec{
                Containers: []v1.Container{
                    {
                    Name:  RESOURCE_NAME,
                    Image: image,
                    },
                },
            },
        },
    },
}
  • 使用client-go的Update API将副本数更新为3:
var updateReplicas int32 = 3
replicaCountPtr := &updateReplicas
result.Spec.Replicas = replicaCountPtr

_, err = kubectl.AppsV1().Deployments(NAMESPACE).Update(context.TODO(), result, metav1.UpdateOptions{})
  • 运行goroutine检查Deployment是否就绪。

问题

当通过kubectl.AppsV1().Deployments(NAMESPACE).Get(...).Status.Replicas等方式查询Replicas、ReadyReplicas、AvailableReplicas、UpdatedReplicas时,这些值始终返回0而非3。

为何副本状态值未更新?使用k8s.io/client-go/kubernetes/fake是否是问题根源?

测试代码

package main

import (
    "context"
    "log"
    "time"

    appsv1 "k8s.io/api/apps/v1"
    v1 "k8s.io/api/core/v1"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    "k8s.io/client-go/kubernetes"
    testclient "k8s.io/client-go/kubernetes/fake"
)

type KubeClient struct {
    kubernetes.Interface
}

const NAMESPACE = "default"
const RESOURCE_NAME = "TestResource"

func main() {
    var replicas int32 = 1
    image := NAMESPACE
    resourceLabel := map[string]string{
        "env": "test",
    }

    kubectl := &KubeClient{
        testclient.NewSimpleClientset(),
    }

    // 创建Deployment
    deploymentSpec := &appsv1.Deployment{
        ObjectMeta: metav1.ObjectMeta{
            Name:      RESOURCE_NAME,
            Namespace: NAMESPACE,
            Labels:    resourceLabel,
        },
        Spec: appsv1.DeploymentSpec{
            Replicas: &replicas,
            Template: v1.PodTemplateSpec{
                Spec: v1.PodSpec{
                    Containers: []v1.Container{
                        {
                            Name:  RESOURCE_NAME,
                            Image: image,
                        },
                    },
                },
            },
        },
    }

    deployResult, err := kubectl.AppsV1().Deployments(NAMESPACE).Create(context.TODO(), deploymentSpec, metav1.CreateOptions{})
    if err != nil {
        panic(err)
    }
    log.Printf("Created deployment %q in namespace '%s'\n", deployResult.GetObjectMeta().GetName(), deployResult.GetNamespace())

    result, err := kubectl.AppsV1().Deployments(NAMESPACE).Get(context.TODO(), RESOURCE_NAME, metav1.GetOptions{})
    if err != nil {
        log.Printf("Failed to fetch deployment %s in the namespace %s with error: %s\n", RESOURCE_NAME, NAMESPACE, err.Error())
    }

    var updateReplicas int32 = 3
    replicaCountPtr := &updateReplicas
    result.Spec.Replicas = replicaCountPtr

    _, err = kubectl.AppsV1().Deployments(NAMESPACE).Update(context.TODO(), result, metav1.UpdateOptions{})
    if err != nil {
        log.Printf("Failed to update replica value: %v\n", err)
    }
    time.Sleep(1 * time.Second)
    log.Printf("Updated replica count of deployment %q in namespace %q : (%d)\n", RESOURCE_NAME, NAMESPACE, &replicas)

    var status bool
    channel := make(chan bool, 1)
    go func(bool) {
        for {
            getResult, getErr := kubectl.AppsV1().Deployments(NAMESPACE).Get(context.TODO(), RESOURCE_NAME, metav1.GetOptions{})
            if getErr != nil {
                log.Printf("Failed to fetch deployment %s in the namespace %s with error: %s\n", RESOURCE_NAME, NAMESPACE, getErr.Error())
                status = false
                break
            }

            log.Printf("Replicas status: %d\n", getResult.Status.ReadyReplicas)
            log.Printf("ReadyReplicas status: %d\n", getResult.Status.Replicas)
            log.Printf("AvailableReplicas status: %d\n", getResult.Status.AvailableReplicas)
            log.Printf("UpdatedReplicas status: %d\n", getResult.Status.UpdatedReplicas)
            log.Printf("Replicas spec: %d\n", *(getResult.Spec.Replicas))
            if getResult.Status.ReadyReplicas == *(getResult.Spec.Replicas) {
                status = true
                break
            }
            status = false
        }
        channel <- (status)
    }(status)

    select {
    case <-channel:
        if status {
            log.Printf("Deployment '%s' is up", RESOURCE_NAME)
        } else {
            log.Printf("Deployment '%s' is NOT up", RESOURCE_NAME)
        }
    case <-time.After(1 * time.Second):
        log.Printf("Deployment '%s' timed out", RESOURCE_NAME)
    }
}

日志

...
2022/11/25 17:46:55 UpdatedReplicas status: 0
2022/11/25 17:46:55 Replicas spec: 3
2022/11/25 17:46:55 Replicas status: 0
2022/11/25 17:46:55 ReadyReplicas status: 0
2022/11/25 17:46:55 AvailableReplicas status: 0
2022/11/25 17:46:55 UpdatedReplicas status: 0
2022/11/25 17:46:55 Replicas spec: 3
2022/11/25 17:46:55 Replicas status: 0
2022/11/25 17:46:55 ReadyReplicas status: 0
2022/11/25 17:46:55 AvailableReplicas status: 0
2022/11/25 17:46:55 UpdatedReplicas status: 0
2022/11/25 17:46:55 Replicas spec: 3
2022/11/25 17:46:55 Deployment 'TestResource' timed out
2022/11/25 17:46:55 Replicas status: 0
2022/11/25 17:46:55 ReadyReplicas status: 0
2022/11/25 17:46:55 AvailableReplicas status: 0
2022/11/25 17:46:55 UpdatedReplicas status: 0
2022/11/25 17:46:55 Replicas spec: 3

内容的提问来源于stack exchange,提问作者kelsonic

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 20:10:31