<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Xinzhao&#39;s Blog</title>
  <subtitle></subtitle>
  <link href="https://ia.jifangcheng.com/rss.xml" rel="self"/>
  <link href="https://ia.jifangcheng.com/"/>
  <updated>2022-11-20T00:00:00Z</updated>
  <id>https://ia.jifangcheng.com/</id>
  <author>
    <name>Xinzhao Xu</name>
  </author>
  
  <entry>
    <title>Karmada Resource Interpreter Webhook 解析</title>
    <link href="https://ia.jifangcheng.com/posts/guide-to-karmada-resource-interpreter-webhook/"/>
    <updated>2022-11-20T00:00:00Z</updated>
    <id>https://ia.jifangcheng.com/posts/guide-to-karmada-resource-interpreter-webhook/</id>
    <content type="html">&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;table-of-contents&quot;&gt;&lt;div class=&quot;toc-container-header&quot;&gt;索引&lt;/div&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/guide-to-karmada-resource-interpreter-webhook/#karmada-%E4%BB%8B%E7%BB%8D&quot;&gt;Karmada 介绍&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/guide-to-karmada-resource-interpreter-webhook/#%E4%B8%80%E4%B8%AA%E4%BE%8B%E5%AD%90%EF%BC%9A%E5%88%9B%E5%BB%BA%E4%B8%80%E4%B8%AA-nginx-%E5%BA%94%E7%94%A8&quot;&gt;一个例子：创建一个 nginx 应用&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/guide-to-karmada-resource-interpreter-webhook/#resource-interpreter-webhook&quot;&gt;Resource Interpreter Webhook&lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/guide-to-karmada-resource-interpreter-webhook/#interpretreplica&quot;&gt;InterpretReplica&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/guide-to-karmada-resource-interpreter-webhook/#revisereplica&quot;&gt;ReviseReplica&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/guide-to-karmada-resource-interpreter-webhook/#workload-%E5%AE%9E%E7%8E%B0%E5%89%AF%E6%9C%AC%E6%95%B0%E8%B0%83%E5%BA%A6&quot;&gt;Workload 实现副本数调度&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/guide-to-karmada-resource-interpreter-webhook/#retain&quot;&gt;Retain&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/guide-to-karmada-resource-interpreter-webhook/#aggregatestatus&quot;&gt;AggregateStatus&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/guide-to-karmada-resource-interpreter-webhook/#%E5%8F%82%E8%80%83%E9%93%BE%E6%8E%A5&quot;&gt;参考链接&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;
&lt;h2 id=&quot;karmada-%E4%BB%8B%E7%BB%8D&quot; tabindex=&quot;-1&quot;&gt;Karmada 介绍&lt;/h2&gt;
&lt;p&gt;在开始讲 Resource Interpreter Webhook 之前需要对 Karmada 的基础架构以及如何分发应用等有一定的了解，但那一部分在之前的博客中已经提到过了，所以这篇文章就不再赘述了，如果需要的话可以移步到 &lt;a href=&quot;https://ia.jifangcheng.com/posts/kubernetes-multi-cluster-projects/#karmada&quot;&gt;Kubernetes 多集群项目介绍&lt;/a&gt; 了解。&lt;/p&gt;
&lt;h2 id=&quot;%E4%B8%80%E4%B8%AA%E4%BE%8B%E5%AD%90%EF%BC%9A%E5%88%9B%E5%BB%BA%E4%B8%80%E4%B8%AA-nginx-%E5%BA%94%E7%94%A8&quot; tabindex=&quot;-1&quot;&gt;一个例子：创建一个 nginx 应用&lt;/h2&gt;
&lt;p&gt;让我们先从一个最简单的例子开始，在 Karmada 中创建并分发一个 nginx 应用；首先是准备 nginx 的资源模板，这个就是原生的 K8s &lt;code&gt;Deployment&lt;/code&gt;，不需要任何改变：&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; apps/v1&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Deployment&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;replicas&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;matchLabels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token key atrule&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token key atrule&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token key atrule&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token key atrule&quot;&gt;containers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx&lt;br /&gt;        &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;再准备一个 &lt;code&gt;PropagationPolicy&lt;/code&gt;，用来控制 nginx 分发到哪些集群：&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; policy.karmada.io/v1alpha1&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; PropagationPolicy&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;propagation&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;resourceSelectors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; apps/v1&lt;br /&gt;      &lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Deployment&lt;br /&gt;      &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;placement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;clusterAffinity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token key atrule&quot;&gt;clusterNames&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; member1&lt;br /&gt;        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; member2&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里我们就直接将它分发到 &lt;code&gt;member1&lt;/code&gt; 和 &lt;code&gt;member2&lt;/code&gt; 集群。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ia.jifangcheng.com/img/guide-to-karmada-resource-interpreter-webhook/kubectl.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;member1&lt;/code&gt; 和 &lt;code&gt;member2&lt;/code&gt; 集群分别有一个副本数为 2 的 nginx &lt;code&gt;Deployment&lt;/code&gt;，所以该资源一共存在 4 个 Pod。&lt;/p&gt;
&lt;p&gt;上面的例子非常简单，直接在 member 集群根据模板原封不动创建 &lt;code&gt;Deployment&lt;/code&gt; 就行了，但是大家知道 Karmada 是支持一些更高级的副本数调度策略的，比如下面这个例子：&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;replicaScheduling&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;replicaDivisionPreference&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Weighted&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;replicaSchedulingType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Divided&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;weightPreference&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;staticWeightList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;targetCluster&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;          &lt;span class=&quot;token key atrule&quot;&gt;clusterNames&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;            &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; member1&lt;br /&gt;        &lt;span class=&quot;token key atrule&quot;&gt;weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;targetCluster&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;          &lt;span class=&quot;token key atrule&quot;&gt;clusterNames&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;            &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; member2&lt;br /&gt;        &lt;span class=&quot;token key atrule&quot;&gt;weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;应用了该规则之后，会涉及到针对每个集群上资源副本数的动态调整，之后 Karmada 在 member 集群创建 &lt;code&gt;Deployment&lt;/code&gt; 的时候就需要增加一个修改副本数的步骤。&lt;/p&gt;
&lt;p&gt;针对 &lt;code&gt;Deployment&lt;/code&gt; 这类 K8s 核心资源，因为其结构是确定的，我们可以直接编写修改其副本数的代码，但是如果我有一个功能类似 &lt;code&gt;Deployment&lt;/code&gt; 的 CRD 呢？我也需要副本数调度，Karmada 能正确地修改它的副本数吗？答案是否定的，也正因此，Karmada 引入了一个新的特性来使其能深度支持自定义资源（CRD）。&lt;/p&gt;
&lt;h2 id=&quot;resource-interpreter-webhook&quot; tabindex=&quot;-1&quot;&gt;Resource Interpreter Webhook&lt;/h2&gt;
&lt;p&gt;为了解决上面提到的问题，Karmada 引入了 Resource Interpreter Webhook，通过干预从 &lt;code&gt;ResourceTemplate&lt;/code&gt; 到 &lt;code&gt;ResourceBinding&lt;/code&gt; 到 &lt;code&gt;Work&lt;/code&gt; 到 &lt;code&gt;Resource&lt;/code&gt; 的这几个阶段来实现完整的自定义资源分发能力：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ia.jifangcheng.com/img/guide-to-karmada-resource-interpreter-webhook/resource-interpreter-webhook.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;从一个阶段到另一个都会经过我们预定义的一个或多个接口，我们会在这些步骤中实现修改副本数等操作；用户需要增加一个单独的实现了对应接口的 webhook server，Karmada 会在执行到相应步骤时通过配置去调用该 server 来完成操作。&lt;/p&gt;
&lt;p&gt;下面我们将选四个具有代表性的 hook 点来逐一介绍，接下来都使用以下 CRD 作为示例：&lt;/p&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Workload is a simple Deployment.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; Workload &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	metav1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;TypeMeta   &lt;span class=&quot;token string&quot;&gt;`json:&quot;,inline&quot;`&lt;/span&gt;&lt;br /&gt;	metav1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ObjectMeta &lt;span class=&quot;token string&quot;&gt;`json:&quot;metadata,omitempty&quot;`&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// Spec represents the specification of the desired behavior.&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// +required&lt;/span&gt;&lt;br /&gt;	Spec WorkloadSpec &lt;span class=&quot;token string&quot;&gt;`json:&quot;spec&quot;`&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// Status represents most recently observed status of the Workload.&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// +optional&lt;/span&gt;&lt;br /&gt;	Status WorkloadStatus &lt;span class=&quot;token string&quot;&gt;`json:&quot;status,omitempty&quot;`&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// WorkloadSpec is the specification of the desired behavior of the Workload.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; WorkloadSpec &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// Number of desired pods. This is a pointer to distinguish between explicit&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// zero and not specified. Defaults to 1.&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// +optional&lt;/span&gt;&lt;br /&gt;	Replicas &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;int32&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;`json:&quot;replicas,omitempty&quot;`&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// Template describes the pods that will be created.&lt;/span&gt;&lt;br /&gt;	Template corev1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PodTemplateSpec &lt;span class=&quot;token string&quot;&gt;`json:&quot;template&quot; protobuf:&quot;bytes,3,opt,name=template&quot;`&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// Paused indicates that the deployment is paused.&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// Note: both user and controllers might set this field.&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// +optional&lt;/span&gt;&lt;br /&gt;	Paused &lt;span class=&quot;token builtin&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;`json:&quot;paused,omitempty&quot;`&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// WorkloadStatus represents most recently observed status of the Workload.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; WorkloadStatus &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// ReadyReplicas represents the total number of ready pods targeted by this Workload.&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// +optional&lt;/span&gt;&lt;br /&gt;	ReadyReplicas &lt;span class=&quot;token builtin&quot;&gt;int32&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;`json:&quot;readyReplicas,omitempty&quot;`&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;它和 &lt;code&gt;Deployment&lt;/code&gt; 很像，我们用来演示 Karmada 如何支持这类资源来进行副本数调度等高级特性。&lt;/p&gt;
&lt;h3 id=&quot;interpretreplica&quot; tabindex=&quot;-1&quot;&gt;InterpretReplica&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://ia.jifangcheng.com/img/guide-to-karmada-resource-interpreter-webhook/InterpretReplica.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;该 hook 点发生在从 &lt;code&gt;ResourceTemplate&lt;/code&gt; 到 &lt;code&gt;ResourceBinding&lt;/code&gt; 这个过程中，针对有 replica 功能的资源对象，比如类似 &lt;code&gt;Deployment&lt;/code&gt; 的自定义资源，实现该接口来告诉 Karmada 对应资源的副本数。&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; workload.example.io/v1alpha1&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Workload&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;replicas&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token key atrule&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token key atrule&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token key atrule&quot;&gt;containers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx&lt;br /&gt;          &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;针对我们示例的 &lt;code&gt;Workload&lt;/code&gt; 资源，实现方式也非常简单，直接在 webhook server 中返回副本数的值即可：&lt;/p&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;workloadInterpreter&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;responseWithExploreReplica&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;workload &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;workloadv1alpha1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Workload&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; interpreter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Response &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	res &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; interpreter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Succeeded&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;	res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Replicas &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; workload&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Spec&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Replicas&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; res&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;注：所有的示例均来自 Karmada 官方文档，可以通过文章最后的 &lt;a href=&quot;https://ia.jifangcheng.com/posts/guide-to-karmada-resource-interpreter-webhook/#%E5%8F%82%E8%80%83%E9%93%BE%E6%8E%A5&quot;&gt;参考链接&lt;/a&gt; 来查看完整的示例和代码。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;revisereplica&quot; tabindex=&quot;-1&quot;&gt;ReviseReplica&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://ia.jifangcheng.com/img/guide-to-karmada-resource-interpreter-webhook/ReviseReplica.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;该 hook 点发生在从 &lt;code&gt;ResourceBinding&lt;/code&gt; 到 &lt;code&gt;Work&lt;/code&gt; 这个过程中，针对有 replica 功能的资源对象，需要按照 Karmada 发送的 request 来修改对象的副本数。Karmada 会通过调度策略把每个集群需要的副本数计算好，你需要做的只是把最后计算好的值赋给你的 CR 对象（因为 Karmada 并不知道该 CRD 的结构）：&lt;/p&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;workloadInterpreter&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;responseWithExploreReviseReplica&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;workload &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;workloadv1alpha1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Workload&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; req interpreter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; interpreter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Response &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	wantedWorkload &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; workload&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;DeepCopy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;	wantedWorkload&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Spec&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Replicas &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DesiredReplicas&lt;br /&gt;	marshaledBytes&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; json&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Marshal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;wantedWorkload&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; interpreter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Errored&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;StatusInternalServerError&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; interpreter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;PatchResponseFromRaw&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Raw&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; marshaledBytes&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;核心代码也只有赋值那一行。&lt;/p&gt;
&lt;h3 id=&quot;workload-%E5%AE%9E%E7%8E%B0%E5%89%AF%E6%9C%AC%E6%95%B0%E8%B0%83%E5%BA%A6&quot; tabindex=&quot;-1&quot;&gt;Workload 实现副本数调度&lt;/h3&gt;
&lt;p&gt;回到我们最初的那个问题，在了解了 &lt;code&gt;InterpretReplica&lt;/code&gt; 和 &lt;code&gt;ReviseReplica&lt;/code&gt; 两个 hook 点之后，就能够实现自定义资源按副本数调度了，实现 &lt;code&gt;InterpretReplica&lt;/code&gt; hook 点以告知 Karmada 该资源的副本总数，实现 &lt;code&gt;ReviseReplica&lt;/code&gt; hook 点来修改对象的副本数，再配置一个 &lt;code&gt;PropagationPolicy&lt;/code&gt; 就可以了，配置方法和 &lt;code&gt;Deployment&lt;/code&gt; 等资源一样：&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; policy.karmada.io/v1alpha1&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; PropagationPolicy&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;workload&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;propagation&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;resourceSelectors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; workload.example.io/v1alpha1&lt;br /&gt;      &lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Workload&lt;br /&gt;      &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;placement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;clusterAffinity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token key atrule&quot;&gt;clusterNames&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; member1&lt;br /&gt;        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; member2&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;replicaScheduling&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token key atrule&quot;&gt;replicaDivisionPreference&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Weighted&lt;br /&gt;      &lt;span class=&quot;token key atrule&quot;&gt;replicaSchedulingType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Divided&lt;br /&gt;      &lt;span class=&quot;token key atrule&quot;&gt;weightPreference&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token key atrule&quot;&gt;staticWeightList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;          &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;targetCluster&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;              &lt;span class=&quot;token key atrule&quot;&gt;clusterNames&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;                &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; member1&lt;br /&gt;            &lt;span class=&quot;token key atrule&quot;&gt;weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;br /&gt;          &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;targetCluster&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;              &lt;span class=&quot;token key atrule&quot;&gt;clusterNames&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;                &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; member2&lt;br /&gt;            &lt;span class=&quot;token key atrule&quot;&gt;weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;效果如下：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ia.jifangcheng.com/img/guide-to-karmada-resource-interpreter-webhook/workload-replica.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h3 id=&quot;retain&quot; tabindex=&quot;-1&quot;&gt;Retain&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://ia.jifangcheng.com/img/guide-to-karmada-resource-interpreter-webhook/Retain.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;该 hook 点发生在从 &lt;code&gt;Work&lt;/code&gt; 到 &lt;code&gt;Resource&lt;/code&gt; 这个过程中，针对 &lt;code&gt;spec&lt;/code&gt; 内容会在 member 集群单独更新的情况，可以通过该 hook 告知 Karmada 保留某些字段的内容。&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; workload.example.io/v1alpha1&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Workload&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;replicas&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;paused&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;false&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;以 &lt;code&gt;paused&lt;/code&gt; 为例，该字段的功能是暂停 workload，member 集群的 controller 会单独更新该字段，&lt;code&gt;Retain&lt;/code&gt; hook 就是为了能更好地和 member 集群的 controller 协作，可以通过该 hook 来告知 Karmada 哪些字段是需要不用更新、需要保留的。&lt;/p&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;workloadInterpreter&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;responseWithExploreRetaining&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;desiredWorkload &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;workloadv1alpha1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Workload&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; req interpreter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; interpreter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Response &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ObservedObject &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		err &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; fmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Errorf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;nil observedObject in exploreReview with operation type: %s&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Operation&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; interpreter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Errored&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;StatusBadRequest&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;	observerWorkload &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;workloadv1alpha1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Workload&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;	err &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;decoder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;DecodeRaw&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ObservedObject&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; observerWorkload&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; interpreter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Errored&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;StatusBadRequest&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// Suppose we want to retain the `.spec.paused` field of the actual observed workload object in member cluster,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// and prevent from being overwritten by karmada controller-plane.&lt;/span&gt;&lt;br /&gt;	wantedWorkload &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; desiredWorkload&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;DeepCopy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;	wantedWorkload&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Spec&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Paused &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; observerWorkload&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Spec&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Paused&lt;br /&gt;	marshaledBytes&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; json&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Marshal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;wantedWorkload&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; interpreter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Errored&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;StatusInternalServerError&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; interpreter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;PatchResponseFromRaw&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Raw&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; marshaledBytes&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;核心代码只有一行，更新 &lt;code&gt;wantedWorkload&lt;/code&gt; 的 &lt;code&gt;Paused&lt;/code&gt; 字段为之前版本的内容。&lt;/p&gt;
&lt;h3 id=&quot;aggregatestatus&quot; tabindex=&quot;-1&quot;&gt;AggregateStatus&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://ia.jifangcheng.com/img/guide-to-karmada-resource-interpreter-webhook/AggregateStatus.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;该 hook 点发生在从 &lt;code&gt;ResourceBinding&lt;/code&gt; 到 &lt;code&gt;ResourceTemplate&lt;/code&gt; 这个过程中，针对需要将 &lt;code&gt;status&lt;/code&gt; 信息聚合到 Resource Template 的资源类型，可通过实现该接口来更新 Resource Template 的 &lt;code&gt;status&lt;/code&gt; 信息。&lt;/p&gt;
&lt;p&gt;Karmada 会将各个集群 Resouce 的状态信息统一收集到 &lt;code&gt;ResourceBinding&lt;/code&gt; 中：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ia.jifangcheng.com/img/guide-to-karmada-resource-interpreter-webhook/nginx-rb.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;AggregateStatus&lt;/code&gt; hook 需要做的事情就是将 &lt;code&gt;ResourceBinding&lt;/code&gt; 中 &lt;code&gt;status&lt;/code&gt; 信息更新到 Resource Template 中：&lt;/p&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;workloadInterpreter&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;responseWithExploreAggregateStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;workload &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;workloadv1alpha1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Workload&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; req interpreter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; interpreter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Response &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	wantedWorkload &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; workload&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;DeepCopy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; readyReplicas &lt;span class=&quot;token builtin&quot;&gt;int32&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; item &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;range&lt;/span&gt; req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;AggregatedStatus &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Status &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token keyword&quot;&gt;continue&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;		status &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;workloadv1alpha1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;WorkloadStatus&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; json&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Unmarshal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Status&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Raw&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; status&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; interpreter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Errored&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;StatusInternalServerError&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;		readyReplicas &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; status&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ReadyReplicas&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;	wantedWorkload&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Status&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ReadyReplicas &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; readyReplicas&lt;br /&gt;	marshaledBytes&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; json&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Marshal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;wantedWorkload&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; interpreter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Errored&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;StatusInternalServerError&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; interpreter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;PatchResponseFromRaw&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Raw&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; marshaledBytes&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;逻辑也非常简单，根据 &lt;code&gt;ResourceBinding&lt;/code&gt; 中的 &lt;code&gt;status&lt;/code&gt; 信息来计算（聚合）出该资源总的 &lt;code&gt;status&lt;/code&gt; 信息再更新到 Resource Template 中；效果和 &lt;code&gt;Deployment&lt;/code&gt; 类似，可以直接查询到该资源在所有集群汇总后的状态信息：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ia.jifangcheng.com/img/guide-to-karmada-resource-interpreter-webhook/deployment-status.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;%E5%8F%82%E8%80%83%E9%93%BE%E6%8E%A5&quot; tabindex=&quot;-1&quot;&gt;参考链接&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/karmada-io/karmada/tree/master/docs/proposals/resource-interpreter-webhook&quot;&gt;Resource Interpreter Webhook&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/karmada-io/karmada/tree/master/examples#resource-interpreter&quot;&gt;custom resource interpreter example&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  
  <entry>
    <title>Kubernetes 多集群项目介绍</title>
    <link href="https://ia.jifangcheng.com/posts/kubernetes-multi-cluster-projects/"/>
    <updated>2021-11-03T00:00:00Z</updated>
    <id>https://ia.jifangcheng.com/posts/kubernetes-multi-cluster-projects/</id>
    <content type="html">&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;table-of-contents&quot;&gt;&lt;div class=&quot;toc-container-header&quot;&gt;索引&lt;/div&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/kubernetes-multi-cluster-projects/#%E4%B8%BA%E4%BB%80%E4%B9%88%E8%A6%81%E4%BD%BF%E7%94%A8%E9%9B%86%E7%BE%A4%E8%81%94%E9%82%A6&quot;&gt;为什么要使用集群联邦&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/kubernetes-multi-cluster-projects/#federation-v1&quot;&gt;Federation v1&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/kubernetes-multi-cluster-projects/#federation-v2&quot;&gt;Federation v2&lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/kubernetes-multi-cluster-projects/#cluster-configuration&quot;&gt;Cluster Configuration&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/kubernetes-multi-cluster-projects/#type-configuration&quot;&gt;Type Configuration&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/kubernetes-multi-cluster-projects/#federated-resource-crd&quot;&gt;Federated Resource CRD&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/kubernetes-multi-cluster-projects/#scheduling&quot;&gt;Scheduling&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/kubernetes-multi-cluster-projects/#karmada&quot;&gt;Karmada&lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/kubernetes-multi-cluster-projects/#cluster&quot;&gt;Cluster&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/kubernetes-multi-cluster-projects/#propagation-policy&quot;&gt;Propagation Policy&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/kubernetes-multi-cluster-projects/#override-policy&quot;&gt;Override Policy&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/kubernetes-multi-cluster-projects/#demo&quot;&gt;Demo&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/kubernetes-multi-cluster-projects/#%E5%8F%82%E8%80%83%E9%93%BE%E6%8E%A5&quot;&gt;参考链接&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;
&lt;h2 id=&quot;%E4%B8%BA%E4%BB%80%E4%B9%88%E8%A6%81%E4%BD%BF%E7%94%A8%E9%9B%86%E7%BE%A4%E8%81%94%E9%82%A6&quot; tabindex=&quot;-1&quot;&gt;为什么要使用集群联邦&lt;/h2&gt;
&lt;p&gt;Kubernetes 从 1.8 版本起就声称单集群最多可支持 5000 个节点和 15 万个 Pod，实际上应该很少有公司会部署如此庞大的一个单集群，很多情况下因为各种各样的原因我们可能会部署多个集群，但是又想将它们统一起来管理，这时候就需要用到集群联邦（Federation）。&lt;/p&gt;
&lt;p&gt;集群联邦的一些典型应用场景：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;高可用：在多个集群上部署应用，可以最大限度地减少集群故障带来的影响&lt;/li&gt;
&lt;li&gt;避免厂商锁定：可以将应用负载分布在多个厂商的集群上并在有需要时直接迁移到其它厂商&lt;/li&gt;
&lt;li&gt;故障隔离：拥有多个小集群可能比单个大集群更利于故障隔离&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;federation-v1&quot; tabindex=&quot;-1&quot;&gt;&lt;a href=&quot;https://github.com/kubernetes-retired/federation&quot;&gt;Federation v1&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;最早的多集群项目，由 K8s 社区提出和维护。&lt;/p&gt;
&lt;p&gt;Federation v1 在 K8s v1.3 左右就已经着手设计（Design Proposal），并在后面几个版本中发布了相关的组件与命令行工具（kubefed），用于帮助使用者快速建立联邦集群，并在 v1.6 时，进入了 Beta 阶段；但 Federation v1 在进入 Beta 后，就没有更进一步的发展，由于灵活性和 API 成熟度的问题，在 K8s v1.11 左右正式被弃用。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ia.jifangcheng.com/img/kubernetes-multi-cluster-projects/federation-v1.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;v1 的基本架构如上图，主要有三个组件：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Federation API Server：类似 K8s API Server，对外提供统一的资源管理入口，但只允许使用 &lt;a href=&quot;https://github.com/kubernetes-retired/federation/tree/master/pkg/federatedtypes&quot;&gt;Adapter&lt;/a&gt; 拓展支持的 K8s 资源&lt;/li&gt;
&lt;li&gt;Controller Manager：提供多个集群间资源调度及状态同步，类似 kube-controller-manager&lt;/li&gt;
&lt;li&gt;Etcd：储存 Federation 的资源&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在 v1 版本中我们要创建一个联邦资源的大致步骤如下：把联邦的所有配置信息都写到资源对象 annotations 里，整个创建流程与 K8s 类似，将资源创建到 Federation API Server，之后 Federation Controller Manager 会根据 annotations 里面的配置将该资源创建到各子集群；下面是一个 ReplicaSet 的示例：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ia.jifangcheng.com/img/kubernetes-multi-cluster-projects/federation-v1-replicaset.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这种架构带来的主要问题有两个：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不够灵活，每当创建一种新资源都要新增 &lt;a href=&quot;https://github.com/kubernetes-retired/federation/tree/master/pkg/federatedtypes&quot;&gt;Adapter&lt;/a&gt;（提交代码再发版）；并且对象会携带许多 Federation 专属的 Annotation&lt;/li&gt;
&lt;li&gt;缺少独立的 API 对象版本控制，例如 Deployment 在 Kubernetes 中是 GA，但在 Federation v1 中只是 Beta&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;federation-v2&quot; tabindex=&quot;-1&quot;&gt;&lt;a href=&quot;https://github.com/kubernetes-sigs/kubefed&quot;&gt;Federation v2&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;有了 v1 版本的经验和教训之后，社区提出了新的集群联邦架构：Federation v2；Federation 项目的演进也可以参考 &lt;a href=&quot;https://kubernetes.io/blog/2018/12/12/kubernetes-federation-evolution/&quot;&gt;Kubernetes Federation Evolution&lt;/a&gt; 这篇文章。&lt;/p&gt;
&lt;p&gt;v2 版本利用 CRD 实现了整体功能，通过定义多种自定义资源（CR），从而省掉了 v1 中的 API Server；v2 版本由两个组件构成：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;admission-webhook 提供了准入控制&lt;/li&gt;
&lt;li&gt;controller-manager 处理自定义资源以及协调不同集群间的状态&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在 v2 版本中要创建一个联邦资源的大致流程如下：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ia.jifangcheng.com/img/kubernetes-multi-cluster-projects/federation-v2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;将 Federated Resource 创建到 Host 集群的 API Server 中，之后 controller-manager 会介入将相应资源分发到不同的集群，分发的规则等都写在了这个 Federated Resource 对象里面。&lt;/p&gt;
&lt;p&gt;在逻辑上，Federation v2 分为两个大部分：configuration（配置）和 propagation（分发）；configuration 主要包含两个配置：Cluster Configuration 和 Type Configuration。&lt;/p&gt;
&lt;h3 id=&quot;cluster-configuration&quot; tabindex=&quot;-1&quot;&gt;Cluster Configuration&lt;/h3&gt;
&lt;p&gt;用来保存被联邦托管的集群的 API 认证信息，可通过 &lt;code&gt;kubefedctl join/unjoin&lt;/code&gt; 来加入/删除集群，当成功加入时，会建立一个 &lt;code&gt;KubeFedCluster&lt;/code&gt; CR 来存储集群相关信息，如 API Endpoint、CA Bundle 和 Token 等。后续 controller-manager 会使用这些信息来访问不同 Kubernetes 集群。&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; core.kubefed.io/v1beta1&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; KubeFedCluster&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;creationTimestamp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2019-10-24T08:05:38Z&quot;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;generation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; cluster1&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; kube&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;federation&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;system&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;resourceVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;647452&quot;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;selfLink&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; /apis/core.kubefed.io/v1beta1/namespaces/kube&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;federation&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;system/kubefedclusters/cluster1&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;uid&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 4c5eb57f&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;5ed4&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;4cec&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;89f3&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;cfc062492ae0&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;apiEndpoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; https&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;//172.16.200.1&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;6443&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;caBundle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; LS&lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt;.Qo=&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;secretRef&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; cluster1&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;shb2x&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;conditions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;lastProbeTime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2019-10-28T06:25:58Z&quot;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;lastTransitionTime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2019-10-28T05:13:47Z&quot;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; /healthz responded with ok&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;reason&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ClusterReady&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;True&quot;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Ready&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;region&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://ia.jifangcheng.com/img/kubernetes-multi-cluster-projects/federation-v2-sync-controller.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h3 id=&quot;type-configuration&quot; tabindex=&quot;-1&quot;&gt;Type Configuration&lt;/h3&gt;
&lt;p&gt;定义了哪些 Kubernetes API 资源要被用于联邦管理；比如说想将 &lt;code&gt;ConfigMap&lt;/code&gt; 资源通过联邦机制建立在不同集群上时，就必须先在 Host 集群中，通过 CRD 建立新资源 &lt;code&gt;FederatedConfigMap&lt;/code&gt;，接着再建立名称为 &lt;code&gt;configmaps&lt;/code&gt; 的 Type configuration（&lt;code&gt;FederatedTypeConfig&lt;/code&gt;）资源，然后描述 &lt;code&gt;ConfigMap&lt;/code&gt; 要被 &lt;code&gt;FederatedConfigMap&lt;/code&gt; 所管理，这样 Kubefed controller-manager 才能知道如何建立 Federated 资源，一个示例如下：&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; core.kubefed.k8s.io/v1beta1&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; FederatedTypeConfig&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; configmaps&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; kube&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;federation&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;system&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;federatedType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; types.kubefed.k8s.io&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; FederatedConfigMap&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;pluralName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; federatedconfigmaps&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;scope&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Namespaced&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; v1beta1&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;propagation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Enabled&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;targetType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ConfigMap&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;pluralName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; configmaps&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;scope&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Namespaced&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; v1&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;federated-resource-crd&quot; tabindex=&quot;-1&quot;&gt;Federated Resource CRD&lt;/h3&gt;
&lt;p&gt;其中还有一个关键的 CRD：Federated Resource，如果想新增一种要被联邦托管的资源的话，就需要建立一个新的 FederatedXX 的 CRD，用来描述对应资源的结构和分发策略（需要被分发到哪些集群上）；Federated Resource CRD 主要包括三部分：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Templates 用于描述被联邦的资源&lt;/li&gt;
&lt;li&gt;Placement 用来描述将被部署的集群，若没有配置，则不会分发到任何集群中&lt;/li&gt;
&lt;li&gt;Overrides 允许对部分集群的部分资源进行覆写&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;一个示例如下：&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; types.kubefed.k8s.io/v1beta1&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; FederatedDeployment&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; test&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;deployment&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; test&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;namespace&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# 定义 Deployment 的所有內容，可理解成 Deployment 与 Pod 之间的关联。&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token key atrule&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token key atrule&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;placement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;clusters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; cluster2&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; cluster1&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;overrides&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;clusterName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; cluster2&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;clusterOverrides&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; spec.replicas&lt;br /&gt;      &lt;span class=&quot;token key atrule&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这些 FederatedXX CRD 可以通过 &lt;code&gt;kubefedctl enable &amp;lt;target kubernetes API type&amp;gt;&lt;/code&gt; 来创建，也可以自己生成/编写对应的 CRD 再创建。&lt;/p&gt;
&lt;p&gt;结合上面介绍了的 Cluster Configuration、Type Configuration 和 Federated Resource CRD，再来看 v2 版本的整体架构和相关概念就清晰很多了：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ia.jifangcheng.com/img/kubernetes-multi-cluster-projects/federation-v2-concepts.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h3 id=&quot;scheduling&quot; tabindex=&quot;-1&quot;&gt;Scheduling&lt;/h3&gt;
&lt;p&gt;Kubefed 目前只能做到一些简单的集群间调度，即手工指定，对于手工指定的调度方式主要分为两部分，一是直接在资源中制定目的集群，二是通过 &lt;code&gt;ReplicaSchedulingPreference&lt;/code&gt; 进行比例分配。&lt;/p&gt;
&lt;p&gt;直接在资源中指定可以通过 &lt;code&gt;clusters&lt;/code&gt; 指定一个 &lt;code&gt;cluster&lt;/code&gt; 列表，或者通过 &lt;code&gt;clusterSelector&lt;/code&gt; 来根据集群标签选择集群，不过有两点要注意：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果 &lt;code&gt;clusters&lt;/code&gt; 字段被指定，&lt;code&gt;clusterSelector&lt;/code&gt; 将会被忽略&lt;/li&gt;
&lt;li&gt;被选择的集群是平等的，该资源会在每个被选中的集群中部署一个无差别副本&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;placement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;clusters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; cluster2&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; cluster1&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;clusterSelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token key atrule&quot;&gt;matchLabels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token key atrule&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bar&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果需要在多个集群间进行区别调度的话就需要引入 &lt;code&gt;ReplicaSchedulingPreference&lt;/code&gt; 进行按比例的调度了：&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; scheduling.kubefed.io/v1alpha1&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ReplicaSchedulingPreference&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; test&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;deployment&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; test&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;ns&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;targetKind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; FederatedDeployment&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;totalReplicas&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;9&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;clusters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token key atrule&quot;&gt;minReplicas&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token key atrule&quot;&gt;maxReplicas&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token key atrule&quot;&gt;weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token key atrule&quot;&gt;minReplicas&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token key atrule&quot;&gt;maxReplicas&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token key atrule&quot;&gt;weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;totalReplicas&lt;/code&gt; 定义了总副本数，&lt;code&gt;clusters&lt;/code&gt; 描述不同集群的最大/最小副本以及权重。&lt;/p&gt;
&lt;p&gt;目前 ReplicaSchedulingPreference 只支持 &lt;a href=&quot;https://github.com/kubernetes-sigs/kubefed/blob/224fe9501b0709fb2999451339d7e756a8a94e9f/pkg/schedulingtypes/replicascheduler.go#L47-L54&quot;&gt;deployments 和 replicasets 两种资源&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id=&quot;karmada&quot; tabindex=&quot;-1&quot;&gt;&lt;a href=&quot;https://github.com/karmada-io/karmada&quot;&gt;Karmada&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Karmada 是由华为开源的多云容器编排项目，这个项目是 Kubernetes Federation v1 和 v2 的延续，一些基本概念继承自这两个版本。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ia.jifangcheng.com/img/kubernetes-multi-cluster-projects/karmada-architecture.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Karmada 主要有三个组件：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Karmada API Server：本质就是一个普通的 K8s API Server，绑定了一个单独的 etcd 来存储那些要被联邦托管的资源&lt;/li&gt;
&lt;li&gt;Karmada Controller Manager：多个 controller 的集合，监听 Karmada API Server 中的对象并与成员集群 API server 进行通信&lt;/li&gt;
&lt;li&gt;Karmada Scheduler：提供高级的多集群调度策略&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;和 Federation v1 类似，我们下发一个资源也是要写入到 Karmada 自己的 API Server 中，之前 controller-manager 根据一些 policy 把资源下发到各个集群中；不过这个 API Server 是 K8s 原生的，所以支持任何资源，不会出现之前 Federation v1 版本中的问题，然后联邦托管资源的分发策略也是由一个单独的 CRD 来控制的，也不需要配置 v2 中的 Federated Resource CRD 和 Type Configure。&lt;/p&gt;
&lt;p&gt;Karmada 的一些基本概念：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;资源模板（Resource Template）：Karmada 使用 K8s 原生 API 定义作为资源模板，便于快速对接 K8s 生态工具链&lt;/li&gt;
&lt;li&gt;分发策略（Propagation Policy）：Karmada 提供独立的策略 API，用来配置资源分发策略&lt;/li&gt;
&lt;li&gt;差异化策略（Override Policy）：Karmada 提供独立的差异化 API，用来配置与集群相关的差异化配置，比如配置不同集群使用不同的镜像&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://ia.jifangcheng.com/img/kubernetes-multi-cluster-projects/karmada-resource-relation.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h3 id=&quot;cluster&quot; tabindex=&quot;-1&quot;&gt;Cluster&lt;/h3&gt;
&lt;p&gt;Cluster 资源记录的内容和 Federation v2 类似，就是访问被纳管集群的一些必要信息：API Endpoint、CA Bundle 和访问 Token。&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;apiEndpoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; https&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;//172.31.165.66&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;55428&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;secretRef&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; member1&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; karmada&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;cluster&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;syncMode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Push&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但是有一个不一样的点是，Karmada 的 &lt;code&gt;Cluster&lt;/code&gt; 资源有两种 sync 模式：&lt;code&gt;Push&lt;/code&gt; 和 &lt;code&gt;Pull&lt;/code&gt;；&lt;code&gt;Push&lt;/code&gt; 就是最普通、最常见的方式，host 集群的 Karmada 组件会负责同步并更新这类集群的状态；&lt;code&gt;Pull&lt;/code&gt; 模式的 member 集群上会运行一个 &lt;code&gt;karmada-agent&lt;/code&gt; 组件，这个组件会负责收集自己的状态并且更新 host 集群的相应的 &lt;code&gt;Cluster&lt;/code&gt; 资源状态。&lt;/p&gt;
&lt;h3 id=&quot;propagation-policy&quot; tabindex=&quot;-1&quot;&gt;Propagation Policy&lt;/h3&gt;
&lt;p&gt;在 Karmada 中分发资源到 member 集群需要配置这个单独 &lt;code&gt;PropagationPolicy&lt;/code&gt; CR；以下面的 nginx 应用为例，首先是 Resource Template，这个就是普通的 K8s &lt;code&gt;Deployment&lt;/code&gt;：&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; apps/v1&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Deployment&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;replicas&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;matchLabels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token key atrule&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token key atrule&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token key atrule&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token key atrule&quot;&gt;containers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx&lt;br /&gt;        &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之后配置一个 &lt;code&gt;PropagationPolicy&lt;/code&gt; 来控制这个 nginx &lt;code&gt;Deployment&lt;/code&gt; 资源的分发策略即可，在下面的示例中，会将 nginx 应用按 1:1 的权重比分发到 member1 和 member2 集群中：&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; policy.karmada.io/v1alpha1&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; PropagationPolicy&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;propagation&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;resourceSelectors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; apps/v1&lt;br /&gt;      &lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Deployment&lt;br /&gt;      &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;placement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;clusterAffinity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token key atrule&quot;&gt;clusterNames&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; member1&lt;br /&gt;        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; member2&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;replicaScheduling&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token key atrule&quot;&gt;replicaDivisionPreference&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Weighted&lt;br /&gt;      &lt;span class=&quot;token key atrule&quot;&gt;replicaSchedulingType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Divided&lt;br /&gt;      &lt;span class=&quot;token key atrule&quot;&gt;weightPreference&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token key atrule&quot;&gt;staticWeightList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;          &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;targetCluster&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;              &lt;span class=&quot;token key atrule&quot;&gt;clusterNames&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;                &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; member1&lt;br /&gt;            &lt;span class=&quot;token key atrule&quot;&gt;weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;br /&gt;          &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;targetCluster&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;              &lt;span class=&quot;token key atrule&quot;&gt;clusterNames&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;                &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; member2&lt;br /&gt;            &lt;span class=&quot;token key atrule&quot;&gt;weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在 Karmada API Server 中创建这两个资源后，可以通过 Karmada API Server 查询到该资源的状态：&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ kubectl get deploy&lt;br /&gt;NAME    READY   UP-TO-DATE   AVAILABLE   AGE&lt;br /&gt;nginx   2/2     2            2           51s&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但是注意，这并不代表应用运行在 Karmada API Server 所在的集群上，实际上这个集群上没有任何工作负载，只是存储了这些 Resource Template，实际的工作负载都运行在上面 &lt;code&gt;PropagationPolicy&lt;/code&gt; 配置的 member1 和 member2 集群中，切换到 member1/member2 集群中可以看到：&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ kubectl get deploy&lt;br /&gt;NAME    READY   UP-TO-DATE   AVAILABLE   AGE&lt;br /&gt;nginx   1/1     1            1           6m26s&lt;br /&gt;&lt;br /&gt;$ kubectl get pod&lt;br /&gt;NAME                     READY   STATUS    RESTARTS   AGE&lt;br /&gt;nginx-6799fc88d8-7cgfz   1/1     Running   0          6m29s&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;分发策略除了上面最普通的指定集群名称外也支持 &lt;code&gt;LabelSelector&lt;/code&gt;、&lt;code&gt;FieldSelector&lt;/code&gt; 和 &lt;code&gt;ExcludeClusters&lt;/code&gt;，如果这几个筛选项都设置了，那只有全部条件都满足的集群才会被筛选出来；除了集群亲和性，还支持 &lt;code&gt;SpreadConstraints&lt;/code&gt;：对集群动态分组，按 &lt;code&gt;region&lt;/code&gt;、&lt;code&gt;zone&lt;/code&gt; 和 &lt;code&gt;provider&lt;/code&gt; 等分组，可以将应用只分发到某类集群中。&lt;/p&gt;
&lt;p&gt;针对有 &lt;code&gt;replicas&lt;/code&gt; 的资源（比如原生的 &lt;code&gt;Deployment&lt;/code&gt; 和 &lt;code&gt;StatefulSet&lt;/code&gt;），支持在分发资源到不同集群的时候按要求更新这个副本数，比如 member1 集群上的 nginx 应用我希望有 2 个副本，member2 上的只希望有 1 个副本；策略有很多，首先是 &lt;code&gt;ReplicaSchedulingType&lt;/code&gt;，有两个可选值：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Duplicated&lt;/code&gt;：每个候选成员集群的副本数都是一样的，从原本资源那里面复制过来的，和不设置 &lt;code&gt;ReplicaSchedulingStrategy&lt;/code&gt; 的效果是一样的&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Divided&lt;/code&gt;：根据有效的候选成员集群的数量，将副本分成若干部分，每个集群的副本数由 &lt;code&gt;ReplicaDivisionPreference&lt;/code&gt; 决定&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;而这个 &lt;code&gt;ReplicaDivisionPreference&lt;/code&gt; 又有两个可选值：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Aggregated&lt;/code&gt;：在考虑集群可用资源的情况下，将这些副本调度到尽量少的集群上，如果一个集群就能容纳所有副本，那只会调度到这一个集群上，其它集群上也会存在相应的资源，不过副本数是 0&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Weighted&lt;/code&gt;：根据 &lt;code&gt;WeightPreference&lt;/code&gt; 来划分副本数，这个 &lt;code&gt;WeightPreference&lt;/code&gt; 就很简单了，直接指定每个集群的权重是多少&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;完整和详细的结构可以参考 &lt;code&gt;Placement&lt;/code&gt; 的 &lt;a href=&quot;https://github.com/karmada-io/karmada/blob/085fdd23c0390f30009d446ef96a688e8fae1b4d/pkg/apis/policy/v1alpha1/propagation_types.go#L89&quot;&gt;API 定义&lt;/a&gt;。&lt;/p&gt;
&lt;h3 id=&quot;override-policy&quot; tabindex=&quot;-1&quot;&gt;Override Policy&lt;/h3&gt;
&lt;p&gt;Override Policy 就很简单了，通过增加 &lt;code&gt;OverridePolicy&lt;/code&gt; 这个 CR 来配置不同集群的差异化配置，直接看一个例子：&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; policy.karmada.io/v1alpha1&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; OverridePolicy&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; example&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;override&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; default&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;resourceSelectors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; apps/v1&lt;br /&gt;      &lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Deployment&lt;br /&gt;      &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; nginx&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;targetCluster&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;clusterNames&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; member1&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;labelSelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token key atrule&quot;&gt;matchLabels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token key atrule&quot;&gt;failuredomain.kubernetes.io/region&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; dc1&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;overriders&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;plaintext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; /spec/template/spec/containers/0/image&lt;br /&gt;        &lt;span class=&quot;token key atrule&quot;&gt;operator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; replace&lt;br /&gt;        &lt;span class=&quot;token key atrule&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;dc-1.registry.io/nginx:1.17.0-alpine&#39;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; /metadata/annotations&lt;br /&gt;        &lt;span class=&quot;token key atrule&quot;&gt;operator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; add&lt;br /&gt;        &lt;span class=&quot;token key atrule&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;          &lt;span class=&quot;token key atrule&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bar&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;demo&quot; tabindex=&quot;-1&quot;&gt;Demo&lt;/h3&gt;
&lt;p&gt;最后看一个 Karmada 官方的例子：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://ia.jifangcheng.com/img/kubernetes-multi-cluster-projects/karmada-sample-nginx.svg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;%E5%8F%82%E8%80%83%E9%93%BE%E6%8E%A5&quot; tabindex=&quot;-1&quot;&gt;参考链接&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.ihypo.net/15716465002689.html&quot;&gt;Kubernetes 多集群管理：Kubefed（Federation v2）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.ihypo.net/15718231244282.html&quot;&gt;使用 Kubernetes 联邦（Kubefed）进行多集群管理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://jimmysong.io/kubernetes-handbook/practice/federation.html&quot;&gt;集群联邦（Cluster Federation）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://support.huaweicloud.com/productdesc-mcp/mcp_productdesc_0001.html&quot;&gt;什么是多云容器平台&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  
  <entry>
    <title>Kubernetes 组件单元测试指南</title>
    <link href="https://ia.jifangcheng.com/posts/k8s-unittest-guide/"/>
    <updated>2021-07-15T00:00:00Z</updated>
    <id>https://ia.jifangcheng.com/posts/k8s-unittest-guide/</id>
    <content type="html">&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;table-of-contents&quot;&gt;&lt;div class=&quot;toc-container-header&quot;&gt;索引&lt;/div&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/k8s-unittest-guide/#%E5%89%8D%E8%A8%80&quot;&gt;前言&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/k8s-unittest-guide/#%E4%BD%BF%E7%94%A8-fake-client&quot;&gt;使用 fake client&lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/k8s-unittest-guide/#%E5%8E%9F%E7%94%9F-client&quot;&gt;原生 client&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/k8s-unittest-guide/#generic-client&quot;&gt;generic client&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/k8s-unittest-guide/#%E6%9E%84%E9%80%A0%E8%BD%BB%E9%87%8F%E7%BA%A7%E9%9B%86%E7%BE%A4&quot;&gt;构造轻量级集群&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/k8s-unittest-guide/#%E5%B8%B8%E7%94%A8%E5%9C%BA%E6%99%AF%E5%AF%B9%E6%AF%94&quot;&gt;常用场景对比&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;
&lt;h2 id=&quot;%E5%89%8D%E8%A8%80&quot; tabindex=&quot;-1&quot;&gt;前言&lt;/h2&gt;
&lt;p&gt;单元测试相关概念和基础内容这里不过多介绍，可以参考 go 官方的一些指南和网上的其它资料：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/golang/go/wiki/LearnTesting&quot;&gt;LearnTesting&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/golang/go/wiki/TableDrivenTests&quot;&gt;TableDrivenTests&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;针对需要操作 k8s 的组件，单测的关键在于如何在单测的函数中构造一个 k8s 集群出来供业务函数对相应资源进行 CRUD，构造 k8s 集群的大致思路主要分为两类：使用 fake client 构造一个假的和在单测的过程中构造一个真的、轻量级的 k8s 集群；下面将逐一介绍这两种方法。&lt;/p&gt;
&lt;p&gt;&lt;em&gt;注：根据不同的测试对象，选择合适的、能达到测试目的方法即可，不必强行使用某一种方法&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;%E4%BD%BF%E7%94%A8-fake-client&quot; tabindex=&quot;-1&quot;&gt;使用 fake client&lt;/h2&gt;
&lt;p&gt;fake client 基本只能用来 CRUD 各种资源（但其实这能覆盖到大部分场景了），一些其它的操作比如触发 informer 的 callback 事件等它是实现不了的，所以如果测试代码也想覆盖这类场景，需要使用下面的构造真正集群的方法；使用 fake client 测试步骤大致如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;构造测试数据
&lt;ul&gt;
&lt;li&gt;即各种测试 case 中需要的（原生和自定义）资源对象&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;使用上面的测试数据生成 fake client
&lt;ul&gt;
&lt;li&gt;把这些测试对象 append 到 fake client 中&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;替换业务函数使用的 client 为 fake client
&lt;ul&gt;
&lt;li&gt;这个具体看业务函数是怎么实现的，怎么获取 k8s client 的&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;fake client 可以再细分成下面两类：&lt;/p&gt;
&lt;h3 id=&quot;%E5%8E%9F%E7%94%9F-client&quot; tabindex=&quot;-1&quot;&gt;原生 client&lt;/h3&gt;
&lt;p&gt;指原生的 &lt;a href=&quot;https://github.com/kubernetes/client-go&quot;&gt;client-go&lt;/a&gt; 和使用 code-generator 生成的各 CR 的 typed client，这些 client 都提供了相应的 fake client 方法，fake client 很好构造，只用一个函数，把测试需要用到的对象都加进去就行：&lt;/p&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;client &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; fake&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;NewSimpleClientset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;objects&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;一个简单示例如下，首先业务函数定义如下：&lt;/p&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Add adds or updates the given Event object.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;kubeClient kubernetes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Interface&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; eventObj &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;corev1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Event&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我需要测试的场景就两种：增加一个事件和更新已有的事件，针对这两个场景构造测试数据：&lt;/p&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;tests &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	name             &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;br /&gt;	objects          &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;runtime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Object&lt;br /&gt;	event            &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;corev1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Event&lt;br /&gt;	isErr            &lt;span class=&quot;token builtin&quot;&gt;bool&lt;/span&gt;&lt;br /&gt;	wantedEventCount &lt;span class=&quot;token builtin&quot;&gt;int32&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;             &lt;span class=&quot;token string&quot;&gt;&quot;exist test&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;		objects&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;          &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;runtime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Object&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;test&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;GetEvent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;		event&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;            test&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;GetEvent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;		isErr&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;            &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;		wantedEventCount&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;             &lt;span class=&quot;token string&quot;&gt;&quot;not exist test&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;		event&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;            test&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;GetEvent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;		isErr&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;            &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;		wantedEventCount&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;根据 case 的测试数据生成 fake client 并执行测试：&lt;/p&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; tt &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;range&lt;/span&gt; tests &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	t&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;t &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;testing&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;T&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token comment&quot;&gt;// 生成 fake client，把需要 CRUD 的资源加入进去&lt;/span&gt;&lt;br /&gt;		client &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; fake&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;NewSimpleClientset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;objects&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;		&lt;span class=&quot;token comment&quot;&gt;// 执行函数&lt;/span&gt;&lt;br /&gt;		err &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;client&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; tt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; tt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isErr &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			t&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Errorf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;%s Add() unexpected error: %v&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; tt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;		&lt;span class=&quot;token comment&quot;&gt;// 校验结果是否符合预期&lt;/span&gt;&lt;br /&gt;		eventObj&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; client&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;CoreV1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Events&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Namespace&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;TODO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; tt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; metav1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GetOptions&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			t&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Errorf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;%s unexpected error: %v&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; tt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; eventObj&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Count &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; tt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;wantedEventCount &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			t&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Errorf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;%s event Count = %d, want %d&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; tt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; eventObj&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Count&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; tt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;wantedEventCount&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;注：测试函数不一定非得像示例这样写，重点了解下流程和构造 fake client 的方法即可&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;针对 controller 的测试，如果你有用到 lister 的话，还需要往对应资源的 informer 中增加需要的资源，这样业务代码里面 lister 才能读到相应的资源，简单示例如下：&lt;/p&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 创建 fake client&lt;/span&gt;&lt;br /&gt;f&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;client &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; fake&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;NewSimpleClientset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;f&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;objects&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// 创建基于 fake client 的 informer&lt;/span&gt;&lt;br /&gt;informer &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; informers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;NewSharedInformerFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;f&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;client&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// 往 informer indexer 中添加对应的资源对象&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; s &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;range&lt;/span&gt; f&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;storageClasses &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	informer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Native&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Storage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;V1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;StorageClasses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Informer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;GetIndexer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之后也是替换 client 和 informer 再运行测试即可。&lt;/p&gt;
&lt;h3 id=&quot;generic-client&quot; tabindex=&quot;-1&quot;&gt;generic client&lt;/h3&gt;
&lt;p&gt;指 controller-runtime 提供的 &lt;a href=&quot;https://github.com/kubernetes-sigs/controller-runtime/tree/master/pkg/client&quot;&gt;generic client&lt;/a&gt;，和上面的 typed client 不同的是，该 client 是一个通用的 client，可用于 CRUD 任何资源，测试方法基本和上面一样，只是构造 fake client 方法稍有不同，其它流程都一样，下面就只介绍构造 fake client 的方法，其它的内容就不再赘述了。&lt;/p&gt;
&lt;p&gt;构造 generic client 的 fake client 的方法最大的一个不同点是它需要包含要 CRUD 的所有资源的 scheme：&lt;/p&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 配置 scheme 和需要添加的 objects&lt;/span&gt;&lt;br /&gt;client &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; fake&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;NewClientBuilder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WithScheme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scheme&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Scheme&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WithObjects&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;objs&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;scheme 的一般构造方法如下：&lt;/p&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// Scheme contains all types of custom clientset and kubernetes client-go clientset&lt;/span&gt;&lt;br /&gt;	Scheme &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; runtime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;NewScheme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// 添加你需要的资源的 scheme&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token boolean&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; clientgoscheme&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;AddToScheme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Scheme&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token boolean&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; cosscheme&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;AddToScheme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Scheme&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token boolean&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; apiextensionsv1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;AddToScheme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Scheme&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;%E6%9E%84%E9%80%A0%E8%BD%BB%E9%87%8F%E7%BA%A7%E9%9B%86%E7%BE%A4&quot; tabindex=&quot;-1&quot;&gt;构造轻量级集群&lt;/h2&gt;
&lt;p&gt;针对使用 fake client 不能覆盖的场景可使用这种方法进行测试，在单元测试中启动一个真实集群一般使用 controller-runtime 提供的 &lt;a href=&quot;https://github.com/kubernetes-sigs/controller-runtime/tree/master/pkg/envtest&quot;&gt;envtest&lt;/a&gt; 库，非常方便，由于是启动了一个真实的集群，该方法适用于任何 client。&lt;/p&gt;
&lt;p&gt;测试步骤大致如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;准备集群相关配置并启动集群
&lt;ul&gt;
&lt;li&gt;一般不需要额外配置，一个函数即可启动，如果有想要预注册 CRD 等才需要配置，配置项较多，可以参考 &lt;a href=&quot;https://github.com/kubernetes-sigs/controller-runtime/blob/master/pkg/envtest/server.go#L105&quot;&gt;https://github.com/kubernetes-sigs/controller-runtime/blob/master/pkg/envtest/server.go#L105&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;使用上面创建的集群的 kube config 来生成各种 client&lt;/li&gt;
&lt;li&gt;创建测试数据（如果需要的话）&lt;/li&gt;
&lt;li&gt;进行测试，完成后销毁集群&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;启动集群方法如下：&lt;/p&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 生成 env，这里面有很多配置项，可以根据需要配置&lt;/span&gt;&lt;br /&gt;testEnv &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;envtest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Environment&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// 启动环境，返回值是环境的 rest.Config，可用于生成各 kube client&lt;/span&gt;&lt;br /&gt;config&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; testEnv&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	t&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Fatal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// 销毁集群&lt;/span&gt;&lt;br /&gt;testEnv&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Stop&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意，该方法需要提前安装 kubebuilder，它依赖 kubebuilder 包提供的 apiserver 那几个 binary 文件，本地的话自己&lt;a href=&quot;https://book.kubebuilder.io/quick-start.html#installation&quot;&gt;下载&lt;/a&gt;安装就好了，如果是 CI 环境需要的话，可以在基础镜像里面增加这些文件，一个示例：&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;RUN mkdir -p /usr/local &amp;&amp; \&lt;br /&gt;	wget https://go.kubebuilder.io/dl/2.3.1/linux/amd64 &amp;&amp; \&lt;br /&gt;	tar xvf amd64 &amp;&amp; \&lt;br /&gt;	mv kubebuilder_2.3.1_linux_amd64 /usr/local/kubebuilder &amp;&amp; \&lt;br /&gt;	rm amd64&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;下面是针对普通 client 的一个简单示例，业务函数定义如下：&lt;/p&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Create creates the given CRD objects or updates them if these objects already exist in the cluster.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;client clientset&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Interface&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; crds &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;apiextensionsv1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;CustomResourceDefinition&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;测试准备：&lt;/p&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 启动测试集群&lt;/span&gt;&lt;br /&gt;testEnv &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;envtest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Environment&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;config&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; testEnv&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	t&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Fatal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;defer&lt;/span&gt; testEnv&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Stop&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// 生成需要的 apiextension client&lt;/span&gt;&lt;br /&gt;apiextensionClient&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; apiextensionsclient&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;NewForConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之后再使用上面的 &lt;code&gt;apiextensionClient&lt;/code&gt; 去执行测试即可。&lt;/p&gt;
&lt;p&gt;针对 generic client 的一个简单示例如下：&lt;/p&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 启动测试集群&lt;/span&gt;&lt;br /&gt;testEnv &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;envtest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Environment&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;config&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; testEnv&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	t&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Fatal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;defer&lt;/span&gt; testEnv&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Stop&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// 生成 generic client&lt;/span&gt;&lt;br /&gt;cli&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; client&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;New&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; client&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Options&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	Scheme&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; scheme&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	t&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Fatal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// 准备测试数据&lt;/span&gt;&lt;br /&gt;sc1 &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; test&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;GetStorageClass&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;sc1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;sc1&quot;&lt;/span&gt;&lt;br /&gt;sc1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Provisioner &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;example.com/test&quot;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// 创建测试数据&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; cli&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;TODO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sc1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	t&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Fatal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// 开始执行测试...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;%E5%B8%B8%E7%94%A8%E5%9C%BA%E6%99%AF%E5%AF%B9%E6%AF%94&quot; tabindex=&quot;-1&quot;&gt;常用场景对比&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;fake client&lt;/th&gt;
&lt;th&gt;真实集群&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;资源 CRUD&lt;/td&gt;
&lt;td&gt;支持&lt;/td&gt;
&lt;td&gt;支持&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;使用 lister&lt;/td&gt;
&lt;td&gt;支持，需要额外处理&lt;/td&gt;
&lt;td&gt;支持，无需额外处理&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;informer 事件&lt;/td&gt;
&lt;td&gt;不支持，无法触发&lt;/td&gt;
&lt;td&gt;支持&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;运行依赖&lt;/td&gt;
&lt;td&gt;无&lt;/td&gt;
&lt;td&gt;需要安装 kubebuilder&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;fake client 使用简单，非常轻量级，执行速度快，但是它基本只能覆盖资源 CRUD 场景，其它操作的业务代码无法覆盖，还有一些场景能覆盖到但是需要一些额外的操作，稍微麻烦一点；构造真实集群完全能模拟组件运行在线上集群中的情况，几乎所有的业务代码只要想测都能覆盖到，但是执行速度较慢，对环境有特殊要求；选择哪种方案的一个简单判断方法是，用 fake client 测试能覆盖到的用 fake client，fake client 覆盖不到的再用构造真实集群的方法。&lt;/p&gt;
</content>
  </entry>
  
  <entry>
    <title>Kubernetes CRD v1 介绍</title>
    <link href="https://ia.jifangcheng.com/posts/crd-v1-introduction/"/>
    <updated>2021-07-06T00:00:00Z</updated>
    <id>https://ia.jifangcheng.com/posts/crd-v1-introduction/</id>
    <content type="html">&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;table-of-contents&quot;&gt;&lt;div class=&quot;toc-container-header&quot;&gt;索引&lt;/div&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/crd-v1-introduction/#%E5%89%8D%E8%A8%80&quot;&gt;前言&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/crd-v1-introduction/#metadata&quot;&gt;metadata&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/crd-v1-introduction/#versions&quot;&gt;versions&lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/crd-v1-introduction/#schema&quot;&gt;schema&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/crd-v1-introduction/#subresource&quot;&gt;subresource&lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/crd-v1-introduction/#status-subresource&quot;&gt;status subresource&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/crd-v1-introduction/#scale-subresource&quot;&gt;scale subresource&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/crd-v1-introduction/#%E5%8F%82%E8%80%83%E9%93%BE%E6%8E%A5&quot;&gt;参考链接&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;
&lt;h2 id=&quot;%E5%89%8D%E8%A8%80&quot; tabindex=&quot;-1&quot;&gt;前言&lt;/h2&gt;
&lt;p&gt;CRD API 在 Kubernetes v1.16 版本 GA 了，所以打算来聊一聊关于 CRD v1 版本的一些特性和一些常见的问题；关于 CRD 基础内容可能会一笔带过，不会涉及到太多，基础内容可以在 Kubernetes 官方文档上了解一下（后面&lt;a href=&quot;https://ia.jifangcheng.com/posts/crd-v1-introduction/#%E5%8F%82%E8%80%83%E9%93%BE%E6%8E%A5&quot;&gt;参考链接&lt;/a&gt;有），可以更多地把这篇文章当作遇到问题时的速查手册。&lt;/p&gt;
&lt;p&gt;CRD 是扩展 Kubernetes 最常见和最方便的方法，除了提供定义 custom resource（CR）的基本能力外，CRD 还提供了很多扩展能力，包括 schema、多版本、版本之间的 conversion、subresource 子资源等，本文主要介绍下面四部分的内容：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;metadata: 定义了 CR 的基本信息，比如 API group 和名称&lt;/li&gt;
&lt;li&gt;versions: 定义 CR 的版本信息&lt;/li&gt;
&lt;li&gt;schema: 定义 CR 的结构和字段类型等信息&lt;/li&gt;
&lt;li&gt;subresource: 定义 CR 的子资源信息&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;metadata&quot; tabindex=&quot;-1&quot;&gt;metadata&lt;/h2&gt;
&lt;p&gt;最基础但是最重要的部分，metadata 包含的主要内容是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;自定义资源 CR 的名称&lt;/li&gt;
&lt;li&gt;该 CR 所属的 API group&lt;/li&gt;
&lt;li&gt;scope: 资源是 Namespace 级别还是 Cluster 级别&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;下面是一个比较完整的例子（注释基本是从 K8s 官方文档上摘抄的，就不翻译了），假设我们要定义一种新的 &lt;code&gt;VolumeSnapshot&lt;/code&gt; 资源：&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; apiextensions.k8s.io/v1&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; CustomResourceDefinition&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;# the name of this CR, must match the spec fields below, and be in the form: &amp;lt;plural&gt;.&amp;lt;group&gt;.&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; volumesnapshots.example.com&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;# group is the API group of the defined custom resource.&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;#	The custom resources are served under `/apis/&amp;lt;group&gt;/...`.&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;#	Must match the name of the CustomResourceDefinition (in the form `&amp;lt;names.plural&gt;.&amp;lt;group&gt;`).&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; example.com&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;names&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;# kind is normally the CamelCased singular type. Your resource manifests use this.&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; VolumeSnapshot&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;# listKind is the serialized kind of the list for this resource. Defaults to &quot;`kind`List&quot;.&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;listKind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; VolumeSnapshotList&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;# plural name to be used in the URL: /apis/&amp;lt;group&gt;/&amp;lt;version&gt;/&amp;lt;plural&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;plural&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; volumesnapshots&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;# singular name to be used as an alias on the CLI and for display&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;singular&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; volumesnapshot&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;# shortNames allow shorter string to match your resource on the CLI&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;# kubectl get volumesnapshot/vss&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;shortNames&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; vss&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;# either Namespaced or Cluster&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;scope&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Namespaced&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;其中 &lt;code&gt;group&lt;/code&gt; 必须是域名，不然创建的时候就会报下面的错：&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;The CustomResourceDefinition &quot;volumesnapshots.test&quot; is invalid: spec.group: Invalid value: &quot;test&quot;: should be a domain with at least one dot&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;versions&quot; tabindex=&quot;-1&quot;&gt;versions&lt;/h2&gt;
&lt;p&gt;定义了 CR 的版本信息，包括哪些版本是可用的，每个版本的结构信息，不同版本之间的转换策略等。&lt;/p&gt;
&lt;p&gt;目前有两种版本转换的策略：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;None: 默认的策略，只改 &lt;code&gt;apiVersion&lt;/code&gt;，其它字段不管，旧版本有，但是新版本没有的字段的数据会直接丢失，同名但是结构不匹配的会报错&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definition-versioning/#webhook-conversion&quot;&gt;Webhook&lt;/a&gt;: 可以配置自定义的转换，API Server will call to an external webhook to do the conversion&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;还是再通过一个例子来看看整体结构：&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; apiextensions.k8s.io/v1&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; CustomResourceDefinition&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;# list of versions supported by this CustomResourceDefinition&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;versions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; v1beta1&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;served&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;storage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;additionalPrinterColumns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; PVC&lt;br /&gt;      &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; string&lt;br /&gt;      &lt;span class=&quot;token key atrule&quot;&gt;jsonPath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; .spec.pvcName&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;schema&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;# The conversion section is introduced in Kubernetes 1.13+ with a default value of&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;# None conversion (strategy sub-field set to None).&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;conversion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;# None conversion assumes the same schema for all versions and only sets the apiVersion&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;# field of custom resources to the proper value&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;strategy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; None&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;关于多版本的常见问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;每个版本都可以通过 &lt;code&gt;served&lt;/code&gt; 字段启用/禁用；有且仅有一个版本能被标记为存储版本（&lt;code&gt;storage = true&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;get &lt;code&gt;served&lt;/code&gt; 为 &lt;code&gt;false&lt;/code&gt; 的版本会直接报错：&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Error from server (NotFound): Unable to list &quot;example.com/v1, Resource=volumesnapshots&quot;: the server could not find the requested resource (get volumesnapshots.example.com)&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;创建的 CR 的版本最终均为 &lt;code&gt;storage = true&lt;/code&gt; 的那个版本，创建以前的版本也会被转换成新版本的&lt;/li&gt;
&lt;li&gt;如果存在以前的版本的 object，在更新这个 object 时会转换成 &lt;code&gt;storage = true&lt;/code&gt; 的那个版本&lt;/li&gt;
&lt;li&gt;转换均是通过 &lt;code&gt;conversion&lt;/code&gt; 配置的 &lt;code&gt;strategy&lt;/code&gt; 来进行的&lt;/li&gt;
&lt;li&gt;当你获取资源时（比如使用 &lt;code&gt;kubectl get&lt;/code&gt;），如果指定的版本与对象的存储时的版本不同，k8 会处理多版本之间的转换（by conversion），返回你指定的那个版本，但是这只是显示上的转换，实际上 etcd 中的数据还是老版本的，只有更新才会真正转换；如果 conversion 出问题的话，会报错：&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Error from server: conversion webhook for example.com/v1beta1, Kind=VolumeSnapshot failed: Post &lt;https: example-conversion-webhook-server.default.svc:443=&quot;&quot; crdconvert?timeout=&quot;30s:&quot;&gt; service &quot;example-conversion-webhook-server&quot; not found&lt;/https:&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;schema&quot; tabindex=&quot;-1&quot;&gt;schema&lt;/h3&gt;
&lt;p&gt;version 的一部分，定义了 CR 的结构信息，每个版本都有自己独立的 schema 结构，描述了 CR 有哪些字段，以及每个字段的类型等；schema 使用了 &lt;a href=&quot;http://json-schema.org/specification.html&quot;&gt;JSON Schema 标准&lt;/a&gt;，一个简单的例子如下：&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; apiextensions.k8s.io/v1&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; CustomResourceDefinition&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;versions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; v1beta1&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;served&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;storage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;schema&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token key atrule&quot;&gt;openAPIV3Schema&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; object&lt;br /&gt;        &lt;span class=&quot;token key atrule&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;          &lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;            &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; object&lt;br /&gt;            &lt;span class=&quot;token key atrule&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;              &lt;span class=&quot;token key atrule&quot;&gt;pvcName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;                &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; string&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;该 CR 我们只定义了 &lt;code&gt;spec&lt;/code&gt; 并且只有一个字段 &lt;code&gt;pvcName&lt;/code&gt;，所以创建一个该 CR 的 YAML 文件如下：&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; example.com/v1beta1&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; VolumeSnapshot&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; s1beta1&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; default&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;pvcName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; pvc1&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;每个字段都可以设置默认值，这个字段是什么类型的，在 yaml 中 &lt;code&gt;default&lt;/code&gt; 的值就要是什么类型的，比如我有下面三种类型的字段：&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; array&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; string&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;test&quot;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; string&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;test&quot;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; number&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;字段还支持校验，比如 &lt;code&gt;int&lt;/code&gt; 可以规定一个最大/最小值的范围，&lt;code&gt;string&lt;/code&gt; 支持正则表达式校验和 enum 枚举。具体每种类型支持什么校验可以参考：&lt;a href=&quot;http://json-schema.org/draft/2019-09/json-schema-validation.html&quot;&gt;JSON Schema Validation&lt;/a&gt;，一个例子：&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; array&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; string&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;minItems&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; string&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;pattern&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;\\w+&#39;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; number&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;minimum&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;maximum&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;schema 的一些常见问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;所有字段必须在 &lt;code&gt;schema&lt;/code&gt; 中定义，不然会报错：&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;error: error validating &quot;snapshot-v1beta1.yaml&quot;: error validating data: ValidationError(VolumeSnapshot): unknown field &quot;spec&quot; in ...; if you choose to ignore these errors, turn validation off with --validate=false&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;如果想要存储没有事先定义的字段的话，可以给相应的父字段加上 &lt;code&gt;x-kubernetes-preserve-unknown-fields: true&lt;/code&gt; 属性&lt;/li&gt;
&lt;li&gt;定义了的字段可以不填，也可以配置 &lt;code&gt;required&lt;/code&gt; 要求某些字段必填：&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;schema&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;openAPIV3Schema&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; object&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; object&lt;br /&gt;        &lt;span class=&quot;token key atrule&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;          &lt;span class=&quot;token key atrule&quot;&gt;pvc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;            &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; object&lt;br /&gt;            &lt;span class=&quot;token key atrule&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;size&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br /&gt;            &lt;span class=&quot;token key atrule&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;            &lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;如果这个 &lt;code&gt;spec.pvc.size&lt;/code&gt; 字段没填的话就会报错&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;如果关闭校验的话（&lt;code&gt;kubectl create --validate=false ...&lt;/code&gt;），未定义的字段会被直接丢弃掉，不会存储&lt;/li&gt;
&lt;li&gt;type 没有 map 类型，可以使用 &lt;code&gt;type: object&lt;/code&gt; 配合 &lt;code&gt;x-kubernetes-map-type&lt;/code&gt; 或者 &lt;code&gt;x-kubernetes-preserve-unknown-fields&lt;/code&gt; 来充当 map 类型（允许任何字段）&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;subresource&quot; tabindex=&quot;-1&quot;&gt;subresource&lt;/h2&gt;
&lt;p&gt;支持两种类型的 subresource，一个是 &lt;a href=&quot;https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#status-subresource&quot;&gt;Status subresource&lt;/a&gt;，一个是 &lt;a href=&quot;https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#scale-subresource&quot;&gt;Scale subresource&lt;/a&gt;，下面的例子中，status 和 scale 两种 subresource 都启用了：&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# subresources describes the subresources for custom resources.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token key atrule&quot;&gt;subresources&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;# status enables the status subresource.&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;# scale enables the scale subresource.&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token key atrule&quot;&gt;scale&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;# specReplicasPath defines the JSONPath inside of a custom resource that corresponds to Scale.Spec.Replicas.&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;specReplicasPath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; .spec.replicas&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;# statusReplicasPath defines the JSONPath inside of a custom resource that corresponds to Scale.Status.Replicas.&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;statusReplicasPath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; .status.replicas&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;# labelSelectorPath defines the JSONPath inside of a custom resource that corresponds to Scale.Status.Selector.&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token key atrule&quot;&gt;labelSelectorPath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; .status.labelSelector&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;status-subresource&quot; tabindex=&quot;-1&quot;&gt;status subresource&lt;/h3&gt;
&lt;p&gt;开启 status subresource 之后，整个 CR 的内容将被分为 &lt;code&gt;spec&lt;/code&gt; 和 &lt;code&gt;status&lt;/code&gt; 两部分，分别表示资源期望的状态和资源的实际状态，然后该资源的 API 会暴露一个新的 &lt;code&gt;/status&lt;/code&gt; 接口，这个 CR 的创建/更新接口会校验整个内容（包括 &lt;code&gt;status&lt;/code&gt; 的），但是 &lt;code&gt;status&lt;/code&gt; 整个会被忽略，&lt;code&gt;status&lt;/code&gt; 只能通过 &lt;code&gt;/status&lt;/code&gt; 接口来更新，&lt;code&gt;/status&lt;/code&gt; 接口只会更新/校验 &lt;code&gt;status&lt;/code&gt; 的内容，其它的会被忽略。&lt;/p&gt;
&lt;p&gt;针对 client-go，只要 type 定义了 &lt;code&gt;Status&lt;/code&gt; 就会生成 &lt;code&gt;UpdateStatus&lt;/code&gt; 方法：&lt;/p&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// +genclient&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Snapshot is the snapshot object of the specified PVC.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; Snapshot &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	metav1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;TypeMeta &lt;span class=&quot;token string&quot;&gt;`json:&quot;,inline&quot;`&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// Standard object&#39;s metadata.&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// More info: &amp;lt;https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata&gt;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// +optional&lt;/span&gt;&lt;br /&gt;	metav1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ObjectMeta &lt;span class=&quot;token string&quot;&gt;`json:&quot;metadata,omitempty&quot;`&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	Spec   SnapshotSpec   &lt;span class=&quot;token string&quot;&gt;`json:&quot;spec,omitempty&quot;`&lt;/span&gt;&lt;br /&gt;	Status SnapshotStatus &lt;span class=&quot;token string&quot;&gt;`json:&quot;status,omitempty&quot;`&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// SnapshotInterface has methods to work with Snapshot resources.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; SnapshotInterface &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token function&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;v1beta1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Snapshot&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;v1beta1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Snapshot&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token function&quot;&gt;Update&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;v1beta1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Snapshot&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;v1beta1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Snapshot&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token function&quot;&gt;UpdateStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;v1beta1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Snapshot&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;v1beta1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Snapshot&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// UpdateStatus was generated because the type contains a Status member.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;c &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;snapshots&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;UpdateStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;snapshot &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;v1beta1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Snapshot&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;v1beta1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Snapshot&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err &lt;span class=&quot;token builtin&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;v1beta1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Snapshot&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;	err &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; c&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;client&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token function&quot;&gt;Namespace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;c&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ns&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token function&quot;&gt;Resource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;snapshots&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token function&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;snapshot&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token function&quot;&gt;SubResource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;status&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token function&quot;&gt;Body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;snapshot&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token function&quot;&gt;Do&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token function&quot;&gt;Into&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;scale-subresource&quot; tabindex=&quot;-1&quot;&gt;scale subresource&lt;/h3&gt;
&lt;p&gt;和 status subresource 类似，启用之后也会暴露一个新的 &lt;code&gt;/scale&lt;/code&gt; 接口，但是我基本没使用过这个，就不过多介绍了，可以看下官方的文档，scale subresource 启用之后还可以使用 &lt;code&gt;kubectl scale&lt;/code&gt; 来单独控制资源的副本数量：&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;kubectl scale &lt;span class=&quot;token parameter variable&quot;&gt;--replicas&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt; crontabs/my-new-cron-object&lt;br /&gt;crontabs &lt;span class=&quot;token string&quot;&gt;&quot;my-new-cron-object&quot;&lt;/span&gt; scaled&lt;br /&gt;&lt;br /&gt;kubectl get crontabs my-new-cron-object &lt;span class=&quot;token parameter variable&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;jsonpath&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;{.spec.replicas}&#39;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;%E5%8F%82%E8%80%83%E9%93%BE%E6%8E%A5&quot; tabindex=&quot;-1&quot;&gt;参考链接&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/&quot;&gt;Extend the Kubernetes API with CustomResourceDefinitions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definition-versioning/&quot;&gt;Versions in CustomResourceDefinitions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://json-schema.org/specification.html&quot;&gt;JSON Schema Specification&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  
  <entry>
    <title>controller-runtime 介绍</title>
    <link href="https://ia.jifangcheng.com/posts/controller-runtime/"/>
    <updated>2020-08-20T00:00:00Z</updated>
    <id>https://ia.jifangcheng.com/posts/controller-runtime/</id>
    <content type="html">&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;table-of-contents&quot;&gt;&lt;div class=&quot;toc-container-header&quot;&gt;索引&lt;/div&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/controller-runtime/#%E6%A6%82%E8%BF%B0&quot;&gt;概述&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/controller-runtime/#%E4%BD%BF%E7%94%A8&quot;&gt;使用&lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/controller-runtime/#reconciler&quot;&gt;Reconciler&lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/controller-runtime/#%E4%BB%8B%E7%BB%8D&quot;&gt;介绍&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/controller-runtime/#%E4%BD%BF%E7%94%A8%E7%A4%BA%E4%BE%8B&quot;&gt;使用示例&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/controller-runtime/#builder-%E5%92%8C-manager&quot;&gt;Builder 和 Manager&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/controller-runtime/#%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95&quot;&gt;单元测试&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/controller-runtime/#%E6%80%BB%E7%BB%93&quot;&gt;总结&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;
&lt;h2 id=&quot;%E6%A6%82%E8%BF%B0&quot; tabindex=&quot;-1&quot;&gt;概述&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/kubernetes-sigs/controller-runtime&quot;&gt;controller-runtime&lt;/a&gt; 是 &lt;a href=&quot;https://github.com/kubernetes-sigs/kubebuilder&quot;&gt;Kubebuilder&lt;/a&gt; 的子项目，提供了一系列用于构建 controller 的库；Kubebuilder 本身也是生成了大量的使用 controller-runtime 的模板代码。controller-runtime 中的几个基本概念：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Controller：字面意思，一个 controller。&lt;/li&gt;
&lt;li&gt;Reconciler：提供 &lt;code&gt;Reconcile&lt;/code&gt; 函数，&lt;code&gt;Controller&lt;/code&gt; 的主要部分和入口函数，包含这个 controller 的所有业务逻辑（等同于普通 controller 中的 &lt;code&gt;syncHandler&lt;/code&gt; 函数），用于使我们关注的 object 的实际状态逐渐向期望状态逼近。&lt;code&gt;Reconciler&lt;/code&gt; 还有以下特点：
&lt;ul&gt;
&lt;li&gt;通常只针对一种类型的 object，不同类型的 object 使用单独的 controller。&lt;/li&gt;
&lt;li&gt;通常不关心触发 &lt;code&gt;Reconcile&lt;/code&gt; 函数的事件内容和类型；比如不管 &lt;code&gt;ReplicaSet&lt;/code&gt; 是创建还是更新，&lt;code&gt;ReplicaSet&lt;/code&gt; &lt;code&gt;Reconciler&lt;/code&gt; 总是将集群中 &lt;code&gt;Pod&lt;/code&gt; 的数量与 object 中设定的数量进行比较，再做出相应的操作。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Builder：基于一些配置，为 &lt;code&gt;Reconciler&lt;/code&gt; 生成 &lt;code&gt;Controller&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;Manager：管理和启动 &lt;code&gt;Controller&lt;/code&gt;，一个 &lt;code&gt;Manager&lt;/code&gt; 可包含多个 &lt;code&gt;Controller&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;%E4%BD%BF%E7%94%A8&quot; tabindex=&quot;-1&quot;&gt;使用&lt;/h2&gt;
&lt;p&gt;下面以官方的一个&lt;a href=&quot;https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/builder#example-Builder&quot;&gt;简单例子&lt;/a&gt;来分步介绍怎么使用 controller-runtime 构建一个 controller：首先定义 &lt;code&gt;Reconciler&lt;/code&gt;，其中包含 controller 的主要逻辑，然后使用 &lt;code&gt;Builder&lt;/code&gt; 生成 &lt;code&gt;Controller&lt;/code&gt; 并加入到 &lt;code&gt;Manager&lt;/code&gt; 中，最后启动 &lt;code&gt;Manager&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;em&gt;注：以 v0.5.0 版本为例，最新版 Reconcile 函数定义有变化&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;每一步的详细介绍如下：&lt;/p&gt;
&lt;h3 id=&quot;reconciler&quot; tabindex=&quot;-1&quot;&gt;Reconciler&lt;/h3&gt;
&lt;h4 id=&quot;%E4%BB%8B%E7%BB%8D&quot; tabindex=&quot;-1&quot;&gt;介绍&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;Reconciler&lt;/code&gt; 的定义如下，仅包含一个 &lt;code&gt;Reconcile&lt;/code&gt; 函数：&lt;/p&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; Reconciler &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// Reconciler performs a full reconciliation for the object referred to by the Request.&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// The Controller will requeue the Request to be processed again if an error is non-nil or&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// Result.Requeue is true, otherwise upon completion it will remove the work from the queue.&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token function&quot;&gt;Reconcile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Result&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Request&lt;/code&gt; 和 &lt;code&gt;Result&lt;/code&gt; 定义如下：&lt;/p&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Request contains the information necessary to reconcile a Kubernetes object.  This includes the&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// information to uniquely identify the object - its Name and Namespace.  It does NOT contain information about&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// any specific Event or the object contents itself.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; Request &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// NamespacedName is the name and namespace of the object to reconcile.&lt;/span&gt;&lt;br /&gt;	types&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;NamespacedName&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Result contains the result of a Reconciler invocation.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; Result &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// Requeue tells the Controller to requeue the reconcile key.  Defaults to false.&lt;/span&gt;&lt;br /&gt;	Requeue &lt;span class=&quot;token builtin&quot;&gt;bool&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// RequeueAfter if greater than 0, tells the Controller to requeue the reconcile key after the Duration.&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// Implies that Requeue is true, there is no need to set Requeue to true at the same time as RequeueAfter.&lt;/span&gt;&lt;br /&gt;	RequeueAfter time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Duration&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Request&lt;/code&gt; 包含本次 reconcile object 的 namespace 和 name，object 类型是生成 controller 时配置的，一个 &lt;code&gt;Reconciler&lt;/code&gt; 仅能处理一种类型的 object；&lt;code&gt;Result&lt;/code&gt; 基本不用关心，如果 &lt;code&gt;Reconcile&lt;/code&gt; 返回 error 会自动 requeue。&lt;/p&gt;
&lt;h4 id=&quot;%E4%BD%BF%E7%94%A8%E7%A4%BA%E4%BE%8B&quot; tabindex=&quot;-1&quot;&gt;使用示例&lt;/h4&gt;
&lt;p&gt;定义一个 &lt;code&gt;ReplicaSetReconciler&lt;/code&gt;，其中包含一个由 controller-runtime 提供的一个 &lt;a href=&quot;https://github.com/kubernetes-sigs/controller-runtime/tree/master/pkg/client&quot;&gt;generic client&lt;/a&gt;，功能同普通的 kubernetes client，能获取到集群的所有资源：&lt;/p&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ReplicaSetReconciler is a simple ControllerManagedBy example implementation.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; ReplicaSetReconciler &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	client&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Client&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;不过和普通 kubernetes client 不同的是，这个通用 client 是一个 client 就能 CRUD 所有类型的资源，非常方便和易于使用。&lt;/p&gt;
&lt;p&gt;然后是实现业务逻辑：&lt;/p&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Implement the business logic:&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// This function will be called when there is a change to a ReplicaSet or a Pod with an OwnerReference&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// to a ReplicaSet.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;//&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// * Read the ReplicaSet&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// * Read the Pods&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// * Set a Label on the ReplicaSet with the Pod count&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;ReplicaSetReconciler&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Reconcile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req reconcile&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;reconcile&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Result&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// Read the ReplicaSet&lt;/span&gt;&lt;br /&gt;	rs &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;appsv1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ReplicaSet&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;	err &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;TODO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;NamespacedName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; rs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; reconcile&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Result&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// List the Pods matching the PodTemplate Labels&lt;/span&gt;&lt;br /&gt;	pods &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;corev1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PodList&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;	err &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;TODO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; client&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;InNamespace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Namespace&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;MatchingLabels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;rs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Spec&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Template&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Labels&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pods&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; reconcile&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Result&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// Update the ReplicaSet&lt;/span&gt;&lt;br /&gt;	rs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Labels&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;pod-count&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; fmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Sprintf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;%v&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;pods&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Items&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;	err &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Update&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;TODO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; rs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; reconcile&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Result&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; reconcile&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Result&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;InjectClient&lt;/code&gt; 将 manager 真实的 client 赋给 &lt;code&gt;ReplicaSetReconciler&lt;/code&gt;：&lt;/p&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;ReplicaSetReconciler&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;InjectClient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;c client&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Client&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Client &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; c&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;builder-%E5%92%8C-manager&quot; tabindex=&quot;-1&quot;&gt;Builder 和 Manager&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Builder&lt;/code&gt; 用来为 &lt;code&gt;Reconciler&lt;/code&gt; 生成 &lt;code&gt;Controller&lt;/code&gt;，&lt;code&gt;Manager&lt;/code&gt; 用来管理和启动 &lt;code&gt;Controller&lt;/code&gt;，直接用例子来介绍，首先生成一个 &lt;code&gt;Manager&lt;/code&gt;：&lt;/p&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;mgr&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; manager&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;New&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; manager&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Options&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;could not create manager&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;	os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Exit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;其中 config 为 client-go 的 &lt;code&gt;rest.Config&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;为 &lt;code&gt;ReplicaSetReconciler&lt;/code&gt; 生成 &lt;code&gt;Controller&lt;/code&gt; 并加入到 &lt;code&gt;Manager&lt;/code&gt; 中：&lt;/p&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token boolean&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; builder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token function&quot;&gt;ControllerManagedBy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mgr&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// Create the ControllerManagedBy&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token function&quot;&gt;For&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;appsv1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ReplicaSet&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// ReplicaSet is the Application API&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token function&quot;&gt;Owns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;corev1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Pod&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;       &lt;span class=&quot;token comment&quot;&gt;// ReplicaSet owns Pods created by it&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token function&quot;&gt;Build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;ReplicaSetReconciler&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;could not create controller&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;	os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Exit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;其中 &lt;code&gt;For&lt;/code&gt; 函数用于指定我们要 reconcile 的 object 类型，&lt;code&gt;Owns&lt;/code&gt; 用来 watch 其 owner 是 reconcile object 类型的 object（&lt;code&gt;Owns&lt;/code&gt; 可以指定多种类型），这两种类型 object 的增/删/改事件均会触发 &lt;code&gt;Reconcile&lt;/code&gt; 函数。&lt;/p&gt;
&lt;p&gt;最后启动 &lt;code&gt;Manager&lt;/code&gt;，整个组件启动：&lt;/p&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; mgr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;stopCh&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;could not start manager&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;	os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Exit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95&quot; tabindex=&quot;-1&quot;&gt;单元测试&lt;/h2&gt;
&lt;p&gt;为普通的 controller 写单元测试主要还是依赖 client-go 提供的 fake client，将测试需要的各种 object append 到一个 fake client 中，使用这个 fake client 来完成测试，有用到 lister 的话需要手动往对应的 informer indexer 中添加相应的 object，示例：&lt;/p&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// 创建 fake client&lt;/span&gt;&lt;br /&gt;f&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;client &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; fake&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;NewSimpleClientset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;f&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;objects&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// 创建基于 fake client 的 informer&lt;/span&gt;&lt;br /&gt;informer &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; informers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;NewSharedInformerFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;f&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;client&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// 往 informer indexer 中添加 object&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; s &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;range&lt;/span&gt; f&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;storageClasses &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	informer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Native&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Storage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;V1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;StorageClasses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Informer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;GetIndexer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;controller-runtime 是使用了自己的 &lt;a href=&quot;https://github.com/kubernetes-sigs/controller-runtime/tree/master/pkg/envtest&quot;&gt;envtest&lt;/a&gt; 包在本地启动一个真正的 apiserver 和 etcd，然后连接这个 apiserver 进行测试，我们还是需要 fake objects，不过和 fake client 不同，这些 fake objects 是要创建到 controller-runtime 启动的 apiserver 中，如果是 CR 的话，还需要先往这个新的 apiserver 中注册 CRD。一个完整的示例：&lt;/p&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;TestNodeLocalStorageReconciler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;t &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;testing&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;T&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// 配置 testEnv，其中包含该项单测需要的 CRD 等&lt;/span&gt;&lt;br /&gt;	testEnv &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;envtest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Environment&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		CRDs&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;runtime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Object&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token function&quot;&gt;newNodeLocalStorageCRD&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// 启动测试环境（etcd 和 apiserver），返回该环境的 rest config&lt;/span&gt;&lt;br /&gt;	config&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; testEnv&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		t&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Fatal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;defer&lt;/span&gt; testEnv&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Stop&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// 生成 controller-runtime 需要的 client&lt;/span&gt;&lt;br /&gt;	c&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; client&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;New&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; client&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Options&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		Scheme&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; scheme&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		t&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Fatal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// 初始化 Reconciler&lt;/span&gt;&lt;br /&gt;	nlsReconciler &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;NodeLocalStorageReconciler&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		Client&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; c&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// 准备好测试数据&lt;/span&gt;&lt;br /&gt;	nls1 &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; test&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;GetNodeLocalStorage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;	nls1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;test&quot;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; c&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;TODO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; nls1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		t&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Fatal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// test cases&lt;/span&gt;&lt;br /&gt;	testCases &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		describe  &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;br /&gt;		namespace &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;br /&gt;		name      &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;br /&gt;		isErr     &lt;span class=&quot;token builtin&quot;&gt;bool&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			describe&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;  &lt;span class=&quot;token string&quot;&gt;&quot;normal test&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;			namespace&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;			name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;      &lt;span class=&quot;token string&quot;&gt;&quot;test&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;			isErr&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;     &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// 测试每个 case&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; testCase &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;range&lt;/span&gt; testCases &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token boolean&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; nlsReconciler&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Reconcile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;reconcile&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Request&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			NamespacedName&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; types&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;NamespacedName&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;				Namespace&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; testCase&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;namespace&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;				Name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;      testCase&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; testCase&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isErr &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			t&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Fatalf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;%s unexpected error: %v&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; testCase&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;describe&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;%E6%80%BB%E7%BB%93&quot; tabindex=&quot;-1&quot;&gt;总结&lt;/h2&gt;
&lt;p&gt;controller-runtime 框架本身提供了很多库来帮助构建 controller，让整个流程变得简单，屏蔽了很多通用的细节，能够让构建 controller 整个过程变得更简单，感兴趣的同学可以在不同的场景和需求下都尝试一下；即便不使用 controller-runtime 我们也可以单独使用它的 &lt;a href=&quot;https://github.com/kubernetes-sigs/controller-runtime/tree/master/pkg/client&quot;&gt;generic client&lt;/a&gt; 和 &lt;a href=&quot;https://github.com/kubernetes-sigs/controller-runtime/tree/master/pkg/envtest&quot;&gt;envtest&lt;/a&gt; 等通用库。&lt;/p&gt;
&lt;p&gt;单独说下使用 &lt;a href=&quot;https://github.com/kubernetes-sigs/controller-runtime/tree/master/pkg/envtest&quot;&gt;envtest&lt;/a&gt; 需要运行环境（你本地和 CI 等环境）安装 Kubebuilder 提供的一系列 bin 文件（主要是 &lt;code&gt;etcd&lt;/code&gt; 和 &lt;code&gt;kube-apiserver&lt;/code&gt;），本地的话自己&lt;a href=&quot;https://book.kubebuilder.io/quick-start.html#installation&quot;&gt;下载&lt;/a&gt;就好了，如果是 CI 环境需要的话，可以在基础镜像里面增加这些文件，一个示例：&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;RUN mkdir -p /usr/local &amp;&amp; \&lt;br /&gt;	wget https://go.kubebuilder.io/dl/2.3.1/linux/amd64 &amp;&amp; \&lt;br /&gt;	tar xvf amd64 &amp;&amp; \&lt;br /&gt;	mv kubebuilder_2.3.1_linux_amd64 /usr/local/kubebuilder &amp;&amp; \&lt;br /&gt;	rm amd64&lt;/code&gt;&lt;/pre&gt;
</content>
  </entry>
  
  <entry>
    <title>在使用 SharedInformerFactory 时一些很小但值得注意的问题</title>
    <link href="https://ia.jifangcheng.com/posts/some-basic-but-noteworthy-points-when-using-sharedinformerfactory/"/>
    <updated>2020-05-11T00:00:00Z</updated>
    <id>https://ia.jifangcheng.com/posts/some-basic-but-noteworthy-points-when-using-sharedinformerfactory/</id>
    <content type="html">&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;table-of-contents&quot;&gt;&lt;div class=&quot;toc-container-header&quot;&gt;索引&lt;/div&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/some-basic-but-noteworthy-points-when-using-sharedinformerfactory/#%E4%B8%8D%E8%A6%81%E4%BD%BF%E7%94%A8-goroutine-%E6%9D%A5%E5%90%AF%E5%8A%A8-sharedinformerfactory&quot;&gt;不要使用 goroutine 来启动 SharedInformerFactory&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/some-basic-but-noteworthy-points-when-using-sharedinformerfactory/#%E6%AD%A3%E7%A1%AE%E5%90%AF%E5%8A%A8-sharedinformerfactory&quot;&gt;正确启动 SharedInformerFactory&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;近期在工作中被不同的同事问了一些类似的、关于 SharedInformerFactory 的问题，有些问题非常小，很细节，虽然小但是我觉得是值得注意的问题，所以打算总结分享一下。&lt;/p&gt;
&lt;h2 id=&quot;%E4%B8%8D%E8%A6%81%E4%BD%BF%E7%94%A8-goroutine-%E6%9D%A5%E5%90%AF%E5%8A%A8-sharedinformerfactory&quot; tabindex=&quot;-1&quot;&gt;不要使用 goroutine 来启动 SharedInformerFactory&lt;/h2&gt;
&lt;p&gt;我不是说用 goroutine 来启动就一定是错的，但是用 goroutine 来启动 &lt;code&gt;SharedInformerFactory&lt;/code&gt; 没有意义，因为这个方法根本就不会 block，直接启动就好了，&lt;code&gt;Start&lt;/code&gt; 方法的源代码：&lt;/p&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Start initializes all requested informers.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;f &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;sharedInformerFactory&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;stopCh &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;chan&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	f&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lock&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Lock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;defer&lt;/span&gt; f&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lock&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Unlock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; informerType&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; informer &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;range&lt;/span&gt; f&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;informers &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;f&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;startedInformers&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;informerType&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token keyword&quot;&gt;go&lt;/span&gt; informer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;stopCh&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;			f&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;startedInformers&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;informerType&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用 goroutine 来启动还有另一个问题：它可能导致 &lt;code&gt;WaitForCacheSync&lt;/code&gt; 不再起作用，&lt;code&gt;WaitForCacheSync&lt;/code&gt; 方法必须要在 informer factory 完全启动（这个方法执行完成）之后再调用，不然它会拿到错误的已启动 informers 的数据，导致它并没有等待所有 informers 启动就返回结果了，看源代码：&lt;/p&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// WaitForCacheSync waits for all started informers&#39; cache were synced.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;f &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;sharedInformerFactory&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;WaitForCacheSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;stopCh &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;chan&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;reflect&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Type&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	informers &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;reflect&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Type&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;cache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SharedIndexInformer &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		f&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lock&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Lock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;defer&lt;/span&gt; f&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lock&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Unlock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;		informers &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;reflect&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Type&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;cache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SharedIndexInformer&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; informerType&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; informer &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;range&lt;/span&gt; f&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;informers &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; f&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;startedInformers&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;informerType&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;				informers&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;informerType&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; informer&lt;br /&gt;			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; informers&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	res &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;reflect&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Type&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; informType&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; informer &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;range&lt;/span&gt; informers &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		res&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;informType&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; cache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WaitForCacheSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;stopCh&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; informer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;HasSynced&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; res&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;WaitForCacheSync&lt;/code&gt; 需要从 &lt;code&gt;f.startedInformers&lt;/code&gt; 中获取已启动 informers 的数据，而 &lt;code&gt;f.startedInformers&lt;/code&gt; 是在执行 &lt;code&gt;Start&lt;/code&gt; 方法时填充的，使用 goroutine 来启动 &lt;code&gt;SharedInformerFactory&lt;/code&gt; 之后再正常 wait for sync 往往都是 &lt;code&gt;WaitForCacheSync&lt;/code&gt; 函数先于 &lt;code&gt;Start&lt;/code&gt; 方法执行，它会先拿到锁，等到它执行完了 &lt;code&gt;Start&lt;/code&gt; 才会开始执行，导致该函数完全没有起到 wait 的效果；可以用下面的示例代码（可自行调整用 goroutine 来启动不同的方法）来看看效果：&lt;/p&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;package&lt;/span&gt; main&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token string&quot;&gt;&quot;fmt&quot;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token string&quot;&gt;&quot;sync&quot;&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token string&quot;&gt;&quot;time&quot;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; factory &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	lock sync&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;RWMutex&lt;br /&gt;	data &lt;span class=&quot;token keyword&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;f &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;factory&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;method1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	f&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lock&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Lock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;defer&lt;/span&gt; f&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lock&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Unlock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	f&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;k1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;v1&quot;&lt;/span&gt;&lt;br /&gt;	f&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;k2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;v2&quot;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;f &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;factory&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;method2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	a &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		f&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lock&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Lock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;defer&lt;/span&gt; f&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lock&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Unlock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; k&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; v &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;range&lt;/span&gt; f&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;			fmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;k&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; v&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token function&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	f &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;factory&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		data&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;make&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;	f&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;method1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;	f&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;method2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;	time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Sleep&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Second &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Wrong&lt;/th&gt;&lt;th&gt;Correct&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;go&lt;/span&gt; informerFactory&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;stopCh&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;informerFactory&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WaitForCacheSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;stopCh&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;informerFactory&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;stopCh&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;informerFactory&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WaitForCacheSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;stopCh&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;%E6%AD%A3%E7%A1%AE%E5%90%AF%E5%8A%A8-sharedinformerfactory&quot; tabindex=&quot;-1&quot;&gt;正确启动 SharedInformerFactory&lt;/h2&gt;
&lt;p&gt;在调用 &lt;code&gt;informerFactory.Start&lt;/code&gt; 方法之前，你必须保证直接或间接调用了你想使用的 informer 的 &lt;code&gt;Informer()&lt;/code&gt; 方法，否则之后 &lt;code&gt;Start&lt;/code&gt; 方法不会起作用，它不会启动任何东西；调用示例：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;&lt;tr&gt;&lt;th&gt;直接调用&lt;/th&gt;&lt;th&gt;间接调用&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;informerFactory&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Core&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;V1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Pods&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Informer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;informerFactory&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Core&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;V1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Pods&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Lister&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;其中 &lt;code&gt;Lister&lt;/code&gt; 方法会调用 &lt;code&gt;Informer&lt;/code&gt; 方法，而 &lt;code&gt;Informer&lt;/code&gt; 方法会调用 factory 的 &lt;code&gt;InformerFor&lt;/code&gt; 方法，该方法会将当前 informer 数据写入到 factory 中，后续启动的时候会使用到：&lt;/p&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// InternalInformerFor returns the SharedIndexInformer for obj using an internal&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// client.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;f &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;sharedInformerFactory&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;InformerFor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;obj runtime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Object&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; newFunc internalinterfaces&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;NewInformerFunc&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; cache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SharedIndexInformer &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	f&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lock&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Lock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;defer&lt;/span&gt; f&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lock&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Unlock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	informerType &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; reflect&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;TypeOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;obj&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;	informer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; exists &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; f&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;informers&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;informerType&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; exists &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; informer&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	resyncPeriod&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; exists &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; f&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;customResync&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;informerType&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;exists &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;		resyncPeriod &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; f&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;defaultResync&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	informer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;newFunc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;f&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;client&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; resyncPeriod&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;	f&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;informers&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;informerType&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; informer&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; informer&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Wrong&lt;/th&gt;&lt;th&gt;Correct&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;informerFactory &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; informers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;NewSharedInformerFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;kubeClient&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;informerFactory&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;stopch&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;podLister &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; informerFactory&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Core&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;V1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Pods&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Lister&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;informerFactory &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; informers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;NewSharedInformerFactory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;kubeClient&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;podLister &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; informerFactory&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Core&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;V1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Pods&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Lister&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;informerFactory&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;stopch&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;上面错误的方法中，&lt;code&gt;informerFactory&lt;/code&gt; 不会启动也不会缓存任何东西，&lt;code&gt;podLister&lt;/code&gt; 会永远返回空结果。&lt;/p&gt;
&lt;p&gt;在一些通用框架的场景下也可以使用 &lt;code&gt;ForResource&lt;/code&gt; 方法来手动“注册”一个资源，它其实也是调用了对应资源的 &lt;code&gt;Informer&lt;/code&gt; 方法：&lt;/p&gt;
&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; SharedInformerFactory &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token function&quot;&gt;ForResource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;resource schema&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GroupVersionResource&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;GenericInformer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// ForResource gives generic access to a shared informer of the matching type&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;f &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;sharedInformerFactory&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ForResource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;resource schema&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GroupVersionResource&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;GenericInformer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; resource &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token comment&quot;&gt;// Group=snapshot.storage.k8s.io, Version=v1beta1&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; v1beta1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SchemeGroupVersion&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WithResource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;volumesnapshots&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;genericInformer&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;resource&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; resource&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;GroupResource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; informer&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; f&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Snapshot&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;V1beta1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;VolumeSnapshots&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Informer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; v1beta1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SchemeGroupVersion&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WithResource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;volumesnapshotclasses&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;genericInformer&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;resource&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; resource&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;GroupResource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; informer&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; f&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Snapshot&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;V1beta1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;VolumeSnapshotClasses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Informer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; v1beta1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SchemeGroupVersion&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WithResource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;volumesnapshotcontents&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br /&gt;		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;genericInformer&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;resource&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; resource&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;GroupResource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; informer&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; f&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Snapshot&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;V1beta1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;VolumeSnapshotContents&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Informer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; fmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Errorf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;no informer found for %v&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; resource&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
</content>
  </entry>
  
  <entry>
    <title>使用 Ceph 做持久化存储的常见问题</title>
    <link href="https://ia.jifangcheng.com/posts/common-problems-about-using-ceph-for-persistent-storage/"/>
    <updated>2018-09-30T00:00:00Z</updated>
    <id>https://ia.jifangcheng.com/posts/common-problems-about-using-ceph-for-persistent-storage/</id>
    <content type="html">&lt;p&gt;&lt;/p&gt;&lt;div class=&quot;table-of-contents&quot;&gt;&lt;div class=&quot;toc-container-header&quot;&gt;索引&lt;/div&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/common-problems-about-using-ceph-for-persistent-storage/#%E5%89%8D%E8%A8%80&quot;&gt;前言&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/common-problems-about-using-ceph-for-persistent-storage/#%E5%AE%89%E8%A3%85-ceph-%E9%9B%86%E7%BE%A4%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98&quot;&gt;安装 Ceph 集群常见问题&lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/common-problems-about-using-ceph-for-persistent-storage/#%E4%B8%8D%E8%A6%81%E7%94%A8-root-%E7%94%A8%E6%88%B7&quot;&gt;不要用 root 用户&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/common-problems-about-using-ceph-for-persistent-storage/#%E8%8A%82%E7%82%B9%E6%95%B0%E9%87%8F&quot;&gt;节点数量&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/common-problems-about-using-ceph-for-persistent-storage/#%E5%85%B3%E4%BA%8E-.ssh%2Fconfig-%E6%96%87%E4%BB%B6&quot;&gt;关于 .ssh/config 文件&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/common-problems-about-using-ceph-for-persistent-storage/#importerror%3A-no-module-named-pkg_resources&quot;&gt;ImportError: No module named pkg_resources&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/common-problems-about-using-ceph-for-persistent-storage/#runtimeerror%3A-nosectionerror%3A-no-section%3A-&#39;ceph&#39;&quot;&gt;RuntimeError: NoSectionError: No section: &#39;ceph&#39;&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/common-problems-about-using-ceph-for-persistent-storage/#%E4%BD%BF%E7%94%A8-ceph-%E7%9A%84%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98&quot;&gt;使用 Ceph 的常见问题&lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/common-problems-about-using-ceph-for-persistent-storage/#provisioner&quot;&gt;Provisioner&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/common-problems-about-using-ceph-for-persistent-storage/#error-creating-rbd-image%3A-executable-file-not-found-in-%24path&quot;&gt;Error creating rbd image: executable file not found in $PATH&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/common-problems-about-using-ceph-for-persistent-storage/#feature-set-mismatch-missing-400000000000000&quot;&gt;feature set mismatch missing 400000000000000&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/common-problems-about-using-ceph-for-persistent-storage/#%E8%AE%BF%E9%97%AE%E6%A8%A1%E5%BC%8F&quot;&gt;访问模式&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://ia.jifangcheng.com/posts/common-problems-about-using-ceph-for-persistent-storage/#%E5%85%B6%E5%AE%83%E9%97%AE%E9%A2%98&quot;&gt;其它问题&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Ceph 是一个开源的分布式对象、块和文件存储系统。Kubernetes 支持 Ceph 的块存储（Ceph RBD）和文件存储（CephFS）作为 Kubernetes 的持久存储后端。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;%E5%89%8D%E8%A8%80&quot; tabindex=&quot;-1&quot;&gt;前言&lt;/h2&gt;
&lt;p&gt;Ceph 集群的安装过程这些就不过多阐述了，参照官网文档就行了，可以选择直接装在物理机上（我用的这种方式），也可以使用 Helm 来安装在 k8s 集群中，按需选择吧。&lt;/p&gt;
&lt;p&gt;下面是一些安装参考文档：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://docs.ceph.com/docs/master/start/quick-start-preflight/&quot;&gt;Preflight Checklist — Ceph Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://docs.ceph.com/docs/master/start/quick-ceph-deploy/&quot;&gt;Storage Cluster Quick Start — Ceph Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://docs.ceph.com/docs/master/start/kube-helm/&quot;&gt;Installation (Kubernetes + Helm) — Ceph Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;%E5%AE%89%E8%A3%85-ceph-%E9%9B%86%E7%BE%A4%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98&quot; tabindex=&quot;-1&quot;&gt;安装 Ceph 集群常见问题&lt;/h2&gt;
&lt;p&gt;先说一下安装环境，参考上面的官网文档的步骤进行安装的，共 4 台服务器，1 台用来部署，其他 3 台用作 Ceph 的节点，所有服务器的操作系统都是 CentOS 7.5。&lt;/p&gt;
&lt;h3 id=&quot;%E4%B8%8D%E8%A6%81%E7%94%A8-root-%E7%94%A8%E6%88%B7&quot; tabindex=&quot;-1&quot;&gt;不要用 root 用户&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;http://docs.ceph.com/docs/master/start/quick-start-preflight/#create-a-ceph-deploy-user&quot;&gt;官网文档&lt;/a&gt;明确指出了不要图方便就用 root 这种用户，极其不推荐。&lt;/p&gt;
&lt;h3 id=&quot;%E8%8A%82%E7%82%B9%E6%95%B0%E9%87%8F&quot; tabindex=&quot;-1&quot;&gt;节点数量&lt;/h3&gt;
&lt;p&gt;Ceph 默认需要 3 个 osd 节点，如果你机器数量不够可以使用下面的配置更改默认的设置：&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;osd pool default size = 2&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;%E5%85%B3%E4%BA%8E-.ssh%2Fconfig-%E6%96%87%E4%BB%B6&quot; tabindex=&quot;-1&quot;&gt;关于 &lt;code&gt;.ssh/config&lt;/code&gt; 文件&lt;/h3&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Host node1&lt;br /&gt;   Hostname 192.168.21.50&lt;br /&gt;   User c&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Host name 要和机器的名称匹配。&lt;/p&gt;
&lt;p&gt;顺便说一下，增加了这个文件之后还要更新一下 hosts 文件 &lt;code&gt;vim /etc/hosts&lt;/code&gt;，把 host name 对应的 IP 地址写进去：&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;192.168.21.50 node1&lt;br /&gt;...&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;importerror%3A-no-module-named-pkg_resources&quot; tabindex=&quot;-1&quot;&gt;ImportError: No module named pkg_resources&lt;/h3&gt;
&lt;p&gt;更新一下机器的 python 环境：&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;yum install gcc python-setuptools python-devel&lt;br /&gt;&lt;br /&gt;easy_install pip&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;runtimeerror%3A-nosectionerror%3A-no-section%3A-&#39;ceph&#39;&quot; tabindex=&quot;-1&quot;&gt;RuntimeError: NoSectionError: No section: &#39;ceph&#39;&lt;/h3&gt;
&lt;p&gt;检查一下 yum 的配置文件 &lt;code&gt;/etc/yum.repos.d/&lt;/code&gt; 里面 &lt;code&gt;ceph.repo&lt;/code&gt; 这个文件中有没有 &lt;code&gt;ceph&lt;/code&gt; 这一段，没有的话检查一下这个目录下面应该有一个 &lt;code&gt;ceph.repo.rpmnew&lt;/code&gt; 的文件，使用这个新的文件即可：&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;mv ceph.repo ceph.repo.old&lt;br /&gt;&lt;br /&gt;mv ceph.repo.rpmnew ceph.repo&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;%E4%BD%BF%E7%94%A8-ceph-%E7%9A%84%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98&quot; tabindex=&quot;-1&quot;&gt;使用 Ceph 的常见问题&lt;/h2&gt;
&lt;h3 id=&quot;provisioner&quot; tabindex=&quot;-1&quot;&gt;Provisioner&lt;/h3&gt;
&lt;p&gt;Kubernetes 自带 Ceph RBD 的 internal provisioner，可以配置动态存储提供（Dynamic Volume Provisioning），CephFS 则需要&lt;a href=&quot;https://github.com/kubernetes-incubator/external-storage/tree/master/ceph/cephfs&quot;&gt;外置的 provisioner&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;Kubernetes Storage Class 支持的 Provisioner 列表可以参考&lt;a href=&quot;https://kubernetes.io/docs/concepts/storage/storage-classes/#provisioner&quot;&gt;官网文档&lt;/a&gt;。&lt;/p&gt;
&lt;h3 id=&quot;error-creating-rbd-image%3A-executable-file-not-found-in-%24path&quot; tabindex=&quot;-1&quot;&gt;Error creating rbd image: executable file not found in $PATH&lt;/h3&gt;
&lt;p&gt;所有 k8s 节点的物理机上都要安装 &lt;code&gt;ceph-common&lt;/code&gt; 这个包（&lt;code&gt;yum install ceph-common&lt;/code&gt;），这个 client 的版本要和 Ceph 集群版本一致。&lt;/p&gt;
&lt;h3 id=&quot;feature-set-mismatch-missing-400000000000000&quot; tabindex=&quot;-1&quot;&gt;feature set mismatch missing 400000000000000&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;顺便说一下，如果你遇到了 &lt;code&gt;timeout expired waiting for volumes to attach/mount for pod&lt;/code&gt; 这个错误，很可能也是下面的原因&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;原因是 Linux 内核版本和 Ceph 当前版本支持的不匹配（内核版本太低了，不支持当前版本的 Ceph 需要的一些特性），可以使用下面的命令来使 Ceph 兼容当前的内核版本（这么说好像不太对，大概是这个意思）：&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;ceph osd crush tunables hammer&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;hammer&lt;/code&gt; 是对应内核支持的 Ceph 版本，对照表如下：&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&gt; TUNABLE          RELEASE   CEPH_VERSION  KERNEL&lt;br /&gt;&gt; CRUSH_TUNABLES   argonaut  v0.48.1       v3.6&lt;br /&gt;&gt; CRUSH_TUNABLES2  bobtail   v0.55         v3.9&lt;br /&gt;&gt; CRUSH_TUNABLES3  firefly   v0.78         v3.15&lt;br /&gt;&gt; CRUSH_V4         hammer    v0.94         v4.1&lt;br /&gt;&gt; CRUSH_TUNABLES5  Jewel     v10.0.2       v4.5&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当然，你也可以升级你的 Linux 内核来与之匹配。&lt;/p&gt;
&lt;h3 id=&quot;%E8%AE%BF%E9%97%AE%E6%A8%A1%E5%BC%8F&quot; tabindex=&quot;-1&quot;&gt;访问模式&lt;/h3&gt;
&lt;p&gt;还有一个注意事项：CephFS 支持 3 种访问模式：&lt;code&gt;ReadWriteOnce&lt;/code&gt;，&lt;code&gt;ReadOnlyMany&lt;/code&gt;，&lt;code&gt;ReadWriteMany&lt;/code&gt;，而 RBD 是块存储，仅支持两种：&lt;code&gt;ReadWriteOnce&lt;/code&gt;，&lt;code&gt;ReadOnlyMany&lt;/code&gt;。&lt;/p&gt;
&lt;h3 id=&quot;%E5%85%B6%E5%AE%83%E9%97%AE%E9%A2%98&quot; tabindex=&quot;-1&quot;&gt;其它问题&lt;/h3&gt;
&lt;p&gt;出现了错误仔细看一下 k8s 的报错信息，如果报错信息实在看不出什么，先检查一下各个参数有没有填对，比如 monitors 地址，密码之类的。&lt;/p&gt;
</content>
  </entry>
</feed>
