<?xml version='1.0' encoding='UTF-8'?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <id>http://lernfunk.de/media/654321</id>
  <title>double12gzh - Posts in Kubernetes</title>
  <updated>2020-11-08T14:11:58.693783+00:00</updated>
  <link href="double12gzhblogs.readthedocs.io"/>
  <link href="double12gzhblogs.readthedocs.io/blogs/category/kubernetes/atom.xml"/>
  <generator uri="https://ablog.readthedocs.org" version="0.10.9">ABlog</generator>
  <entry>
    <id>double12gzhblogs.readthedocs.io/blogs/Kubernetes/2020-09-26-%E4%BD%BF%E7%94%A8client-go%E8%AE%BF%E9%97%AEk8s%E9%9B%86%E7%BE%A4%E4%B8%AD%E7%9A%84CRD.html</id>
    <title>如何使用client-go访问k8s crd</title>
    <updated>2020-09-26T00:00:00+00:00</updated>
    <author>
      <name>大海星</name>
    </author>
    <content type="html">&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-26-k8s-logo.png" /&gt;
&lt;/div&gt;
&lt;div class="section" id="client-gok8s-crd"&gt;

&lt;p&gt;摘要：介绍如何使用clinet-go手动编写程序来访问k8s中的crd。&lt;/p&gt;
&lt;div class="section" id="id1"&gt;
&lt;h2&gt;1. 写在前面&lt;/h2&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;微信公众号：&lt;strong&gt;[double12gzh]&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;个人主页: &lt;a class="reference external" href="https://gzh.readthedocs.io"&gt;https://gzh.readthedocs.io&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;关注容器技术、关注&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Kubernetes&lt;/span&gt;&lt;/code&gt;。问题或建议，请公众号留言。&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;Kubernetes架构的设计模式，我们可以很方便的使用&lt;a class="reference external" href="https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/"&gt;CRD(Custom Resource
Definitions)&lt;/a&gt;对k8s
API进行扩展。但是问题，通过&lt;a class="reference external" href="https://github.com/kubernetes/client-go"&gt;client-go&lt;/a&gt;来获取这些CRD或开发用户自定义控制器，那是比较麻烦的一件事情，除此之外，市面上对于&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;client-go&lt;/span&gt;&lt;/code&gt;的介绍并不是很多。&lt;/p&gt;
&lt;p&gt;本文将会通过一个示例，简单介绍一下如何通过client-go获取CRD。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id2"&gt;
&lt;h2&gt;2. 写作动机&lt;/h2&gt;
&lt;p&gt;我在PaaS平台的日常开发工作中，想要将第三方存储厂商集成到Kubernetes集群中时，遇到了这个挑战。计划是使用自定义资源定义来定义诸如文件系统池和文件系统。然后，一个自定义的Operator可以监听这些资源的创建和删除，并负责这些资源的生命周期的管理
。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="cr-custom-resource"&gt;
&lt;h2&gt;3. 定义CR(Custom Resource)&lt;/h2&gt;
&lt;p&gt;在本文中，我们将以一个简单的例子来进行演示。使用kubectl可以很容易地创建自定义资源定义，对于这个例子，我们将从一个简单的资源定义开始做起：&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="test-yaml"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;test.yaml&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-yaml notranslate"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt; &lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;apiextensions.k8s.io/v1beta1&amp;quot;&lt;/span&gt;
 &lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;CustomResourceDefinition&amp;quot;&lt;/span&gt;
 &lt;span class="nt"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
     &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;projects.examples-gzh.com&amp;quot;&lt;/span&gt;
 &lt;span class="nt"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
     &lt;span class="nt"&gt;group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;examples-gzh.com&amp;quot;&lt;/span&gt;
     &lt;span class="nt"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;v1alpha1&amp;quot;&lt;/span&gt;
     &lt;span class="nt"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Namespaced&amp;quot;&lt;/span&gt;
     &lt;span class="nt"&gt;names&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
     &lt;span class="nt"&gt;plural&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;projects&amp;quot;&lt;/span&gt;
     &lt;span class="nt"&gt;singular&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;project&amp;quot;&lt;/span&gt;
     &lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Project&amp;quot;&lt;/span&gt;
     &lt;span class="nt"&gt;validation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
     &lt;span class="nt"&gt;openAPIV3Schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
         &lt;span class="nt"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p p-Indicator"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;spec&amp;quot;&lt;/span&gt;&lt;span class="p p-Indicator"&gt;]&lt;/span&gt;
         &lt;span class="nt"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
         &lt;span class="nt"&gt;replicas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
             &lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;integer&amp;quot;&lt;/span&gt;
             &lt;span class="nt"&gt;minimum&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;确定&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Group&lt;/span&gt;&lt;/code&gt;的名字。&lt;/p&gt;
&lt;p&gt;在定义CRD时，我们首先需要定义它所在的&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Group&lt;/span&gt;&lt;/code&gt;(在上面的代码中，其``Group``为：&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;examples-gzh.com&lt;/span&gt;&lt;/code&gt;)。对于Group的定义，为了避免命名冲突，通常会使用一些比较特别的字符串(如：你的个人主页的地址、你公司的域名等)，&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Group&lt;/span&gt;&lt;/code&gt;名&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/home/docs/checkouts/readthedocs.org/user_builds/gzh/checkouts/latest/blogs/Kubernetes/2020-09-26-使用client-go访问k8s集群中的CRD.rst&lt;/span&gt;, line 70)&lt;/p&gt;
&lt;p&gt;Bullet list ends without a blank line; unexpected unindent.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;字确定了之后，由于CRD的名字是按&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;&amp;lt;plural-resource-name&amp;gt;.&amp;lt;api-group-name&amp;gt;&lt;/span&gt;&lt;/code&gt;这个格式进行命名的，所以这里我们的CRD的名字为&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;projects.example-gzh.com&lt;/span&gt;&lt;/code&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;确定&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;version&lt;/span&gt;&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;这里的&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;version&lt;/span&gt;&lt;/code&gt;，即&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;spec.version&lt;/span&gt;&lt;/code&gt;。如果你的代码没有开发完成，或者还在快速迭代中，那么，建议你使用&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;alpha&lt;/span&gt;&lt;/code&gt;这样的命名规则。这样的好处是，如果别人想使用你的代码去使用，那么，他单从版本号上就可以很方便的快速知道，你这&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/home/docs/checkouts/readthedocs.org/user_builds/gzh/checkouts/latest/blogs/Kubernetes/2020-09-26-使用client-go访问k8s集群中的CRD.rst&lt;/span&gt;, line 75)&lt;/p&gt;
&lt;p&gt;Bullet list ends without a blank line; unexpected unindent.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;是一个不稳定的版本。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;schema校验&lt;/p&gt;
&lt;p&gt;在上面我们的CRD中，我们引入了&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;spec.validation.openAPIV3Schema&lt;/span&gt;&lt;/code&gt;，它的作用是对其中的字段进行校验，如果用户在使用我们的CRD时，提供了一个不符合要求的字段后，validation可以很方便的对其进行校验。除了在这里引用validation之外，我们还&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/home/docs/checkouts/readthedocs.org/user_builds/gzh/checkouts/latest/blogs/Kubernetes/2020-09-26-使用client-go访问k8s集群中的CRD.rst&lt;/span&gt;, line 80)&lt;/p&gt;
&lt;p&gt;Bullet list ends without a blank line; unexpected unindent.&lt;/p&gt;
&lt;/div&gt;
&lt;dl class="simple"&gt;
&lt;dt&gt;可以选择在admintion&lt;/dt&gt;&lt;dd&gt;&lt;p&gt;controller中通过&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Validate&lt;/span&gt;&lt;/code&gt;阶段进行验证，不过样是需要开启admission
webhook的。&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;将上面的代码保存到一个文件中之后，我们就可以通过&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;kubectl&lt;/span&gt; &lt;span class="pre"&gt;apply&lt;/span&gt; &lt;span class="pre"&gt;-f&lt;/span&gt; &lt;span class="pre"&gt;demo.yaml&lt;/span&gt;&lt;/code&gt;进行部署了。我在本机通过minikue启动了一个K8S集群：&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;PS C:&lt;span class="se"&gt;\U&lt;/span&gt;sers&lt;span class="se"&gt;\g&lt;/span&gt;uanzenghui&amp;gt; kubectl get po -A
NAMESPACE              NAME                                        READY   STATUS    RESTARTS   AGE
kube-system            coredns-f9fd979d6-7h2b7                     &lt;span class="m"&gt;1&lt;/span&gt;/1     Running   &lt;span class="m"&gt;1&lt;/span&gt;          9h
kube-system            etcd-minikube                               &lt;span class="m"&gt;0&lt;/span&gt;/1     Running   &lt;span class="m"&gt;2&lt;/span&gt;          9h
kube-system            kube-apiserver-minikube                     &lt;span class="m"&gt;1&lt;/span&gt;/1     Running   &lt;span class="m"&gt;2&lt;/span&gt;          9h
kube-system            kube-controller-manager-minikube            &lt;span class="m"&gt;0&lt;/span&gt;/1     Running   &lt;span class="m"&gt;2&lt;/span&gt;          9h
kube-system            kube-proxy-p8zb7                            &lt;span class="m"&gt;1&lt;/span&gt;/1     Running   &lt;span class="m"&gt;1&lt;/span&gt;          9h
kube-system            kube-scheduler-minikube                     &lt;span class="m"&gt;0&lt;/span&gt;/1     Running   &lt;span class="m"&gt;2&lt;/span&gt;          9h
kube-system            storage-provisioner                         &lt;span class="m"&gt;1&lt;/span&gt;/1     Running   &lt;span class="m"&gt;1&lt;/span&gt;          9h
kubernetes-dashboard   dashboard-metrics-scraper-c95fcf479-gvhpd   &lt;span class="m"&gt;1&lt;/span&gt;/1     Running   &lt;span class="m"&gt;1&lt;/span&gt;          9h
kubernetes-dashboard   kubernetes-dashboard-5c448bc4bf-lpwqh       &lt;span class="m"&gt;1&lt;/span&gt;/1     Running   &lt;span class="m"&gt;1&lt;/span&gt;          9h

PS C:&lt;span class="se"&gt;\U&lt;/span&gt;sers&lt;span class="se"&gt;\g&lt;/span&gt;uanzenghui&amp;gt; kubectl version
Client Version: version.Info&lt;span class="o"&gt;{&lt;/span&gt;Major:&lt;span class="s2"&gt;&amp;quot;1&amp;quot;&lt;/span&gt;, Minor:&lt;span class="s2"&gt;&amp;quot;16+&amp;quot;&lt;/span&gt;, GitVersion:&lt;span class="s2"&gt;&amp;quot;v1.16.6-beta.0&amp;quot;&lt;/span&gt;, GitCommit:&lt;span class="s2"&gt;&amp;quot;e7f962ba86f4ce7033828210ca3556393c377bcc&amp;quot;&lt;/span&gt;, GitTreeState:&lt;span class="s2"&gt;&amp;quot;clean&amp;quot;&lt;/span&gt;, BuildDate:&lt;span class="s2"&gt;&amp;quot;2020-01-15T08:26:26Z&amp;quot;&lt;/span&gt;, GoVersion:&lt;span class="s2"&gt;&amp;quot;go1.13.5&amp;quot;&lt;/span&gt;, Compiler:&lt;span class="s2"&gt;&amp;quot;gc&amp;quot;&lt;/span&gt;, Platform:&lt;span class="s2"&gt;&amp;quot;windows/amd64&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
Server Version: version.Info&lt;span class="o"&gt;{&lt;/span&gt;Major:&lt;span class="s2"&gt;&amp;quot;1&amp;quot;&lt;/span&gt;, Minor:&lt;span class="s2"&gt;&amp;quot;19&amp;quot;&lt;/span&gt;, GitVersion:&lt;span class="s2"&gt;&amp;quot;v1.19.2&amp;quot;&lt;/span&gt;, GitCommit:&lt;span class="s2"&gt;&amp;quot;f5743093fd1c663cb0cbc89748f730662345d44d&amp;quot;&lt;/span&gt;, GitTreeState:&lt;span class="s2"&gt;&amp;quot;clean&amp;quot;&lt;/span&gt;, BuildDate:&lt;span class="s2"&gt;&amp;quot;2020-09-16T13:32:58Z&amp;quot;&lt;/span&gt;, GoVersion:&lt;span class="s2"&gt;&amp;quot;go1.15&amp;quot;&lt;/span&gt;, Compiler:&lt;span class="s2"&gt;&amp;quot;gc&amp;quot;&lt;/span&gt;, Platform:&lt;span class="s2"&gt;&amp;quot;linux/amd64&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;部署我们的CRD:&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;PS C:&lt;span class="se"&gt;\U&lt;/span&gt;sers&lt;span class="se"&gt;\g&lt;/span&gt;uanzenghui&lt;span class="se"&gt;\D&lt;/span&gt;ocuments&amp;gt; kubectl apply -f .&lt;span class="se"&gt;\U&lt;/span&gt;ntitled-2.yaml
customresourcedefinition.apiextensions.k8s.io/projects.examples-gzh.com created

PS C:&lt;span class="se"&gt;\U&lt;/span&gt;sers&lt;span class="se"&gt;\g&lt;/span&gt;uanzenghui&lt;span class="se"&gt;\D&lt;/span&gt;ocuments&amp;gt; kubectl get crd
NAME                        CREATED AT
projects.examples-gzh.com   &lt;span class="m"&gt;2020&lt;/span&gt;-09-25T10:40:01Z
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;如果需要查看其详情，可以使用命令:
&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;kubectl&lt;/span&gt; &lt;span class="pre"&gt;describe&lt;/span&gt; &lt;span class="pre"&gt;crd&lt;/span&gt; &lt;span class="pre"&gt;projects.examples-gzh.com&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;既然CRD已经创建完成了，接下来我们看一下如何使用这个CRD来创建与之相对应的CR。CR相关的文件内容如下：&lt;/p&gt;
&lt;div class="highlight-yaml notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;examples-gzh.com/v1alpha1&amp;quot;&lt;/span&gt;
&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;Project&lt;/span&gt;
&lt;span class="nt"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;gzh-cr&lt;/span&gt;
  &lt;span class="nt"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;default&lt;/span&gt;
&lt;span class="nt"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="nt"&gt;replica&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;2&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;创建CR&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;PS C:&lt;span class="se"&gt;\U&lt;/span&gt;sers&lt;span class="se"&gt;\g&lt;/span&gt;uanzenghui&lt;span class="se"&gt;\D&lt;/span&gt;ocuments&amp;gt; kubectl apply -f cr.yaml
project.examples-gzh.com/gzh-cr created

PS C:&lt;span class="se"&gt;\U&lt;/span&gt;sers&lt;span class="se"&gt;\g&lt;/span&gt;uanzenghui&lt;span class="se"&gt;\D&lt;/span&gt;ocuments&amp;gt; kubectl get Project
NAME     AGE
gzh-cr   39s
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;接下来，我们将使用client-go来获取这个CR。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="golang-client"&gt;
&lt;h2&gt;4. 创建golang client&lt;/h2&gt;
&lt;p&gt;在进行本节前，我假设您已经对client-go、k8s控制器机制有所理解，并且有一定的GoLang的开发经验。&lt;/p&gt;
&lt;p&gt;另外，与其它一些讲解Operator的文章不同的是，这些使用CRD的文档会假设你正在使用某种代码生成器来自动生成客户端库。然而，对于这个过程的文档很少，而且从阅读Github上的一些激烈的讨论中，我们可以看出，它仍然是一个正在进行中的工作。&lt;/p&gt;
&lt;p&gt;本文中，我将坚持使用（大部分）手动实现的客户端的方式给大家展示。&lt;/p&gt;
&lt;p&gt;首先，您可以创建一个自己的项目路径，并安装依赖:&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;mkdir github.com/double12gzh/k8s-crd-demo
go get k8s.io/client-go@v0.17.0
go get k8s.io/apimachinery@v0.17.0
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="id3"&gt;
&lt;h3&gt;4.1 定义类型&lt;/h3&gt;
&lt;div class="literal-block-wrapper docutils container" id="id6"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;example.go&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt; &lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nx"&gt;v1alpha1&lt;/span&gt;

 &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;metav1&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;k8s.io/apimachinery/pkg/apis/meta/v1&amp;quot;&lt;/span&gt;

 &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ProjectSpec&lt;/span&gt; &lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;Replicas&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="s"&gt;`json:&amp;quot;replicas&amp;quot;`&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;

 &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Project&lt;/span&gt; &lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;metav1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TypeMeta&lt;/span&gt;   &lt;span class="s"&gt;`json:&amp;quot;,inline&amp;quot;`&lt;/span&gt;
   &lt;span class="nx"&gt;metav1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ObjectMeta&lt;/span&gt; &lt;span class="s"&gt;`json:&amp;quot;metadata,omitempty&amp;quot;`&lt;/span&gt;
   &lt;span class="nx"&gt;Spec&lt;/span&gt; &lt;span class="nx"&gt;ProjectSpec&lt;/span&gt; &lt;span class="s"&gt;`json:&amp;quot;spec&amp;quot;`&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;

 &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ProjectList&lt;/span&gt; &lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;metav1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TypeMeta&lt;/span&gt; &lt;span class="s"&gt;`json:&amp;quot;,inline&amp;quot;`&lt;/span&gt;
     &lt;span class="nx"&gt;metav1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ListMeta&lt;/span&gt; &lt;span class="s"&gt;`json:&amp;quot;metadata,omitempty&amp;quot;`&lt;/span&gt;
     &lt;span class="nx"&gt;Items&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="nx"&gt;Project&lt;/span&gt; &lt;span class="s"&gt;`json:&amp;quot;items&amp;quot;`&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;metav1.ObjectMeta&lt;/span&gt;&lt;/code&gt;中包含了一个比较重要的类型&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;metadata&lt;/span&gt;&lt;/code&gt;，k8s中所有的资源有都这个属性，这里面可以定义诸如：&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;name&lt;/span&gt;&lt;/code&gt;，&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;namespace&lt;/span&gt;&lt;/code&gt;，&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;label&lt;/span&gt;&lt;/code&gt;等的属性。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="deepcopy"&gt;
&lt;h3&gt;4.2 定义DeepCopy方法&lt;/h3&gt;
&lt;p&gt;Kubernetes API 所服务的每个类型（在本例中，Project 和
ProjectList）都需要实现 k8s.io/apimachinery/pkg/runtime.Object
接口。这个接口定义了两个方法GetObjectKind()和DeepCopyObject()。第一个方法已经由内嵌的metav1.TypeMeta结构提供了；第二个方法你必须自己实现。&lt;/p&gt;
&lt;p&gt;DeepCopyObject方法的目的是生成一个对象的深度拷贝。由于这涉及到大量的模板代码，所以这些方法通常是自动生成的。在本文中，我们将手动进行。继续在同一个包中添加第二个文件
deepcopy.go。&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id7"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;deepcopy.go&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt; &lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nx"&gt;v1alpha1&lt;/span&gt;

 &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;k8s.io/apimachinery/pkg/runtime&amp;quot;&lt;/span&gt;

 &lt;span class="c1"&gt;// DeepCopyInto 把一个对象的所有属性复制给此对象类型的指针&lt;/span&gt;
 &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;in&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;Project&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;DeepCopyInto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;out&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;Project&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TypeMeta&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;in&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TypeMeta&lt;/span&gt;
     &lt;span class="nx"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ObjectMeta&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;in&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ObjectMeta&lt;/span&gt;
     &lt;span class="nx"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Spec&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ProjectSpec&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="nx"&gt;Replicas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;in&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Spec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Replicas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;

 &lt;span class="c1"&gt;// DeepCopyObject 返回一个对象类型&lt;/span&gt;
 &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;in&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;Project&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;DeepCopyObject&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Object&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;out&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;Project&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
     &lt;span class="nx"&gt;in&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DeepCopyInto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;out&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;out&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;

 &lt;span class="c1"&gt;// DeepCopyObject 返回一个对像类型&lt;/span&gt;
 &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;in&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;ProjectList&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;DeepCopyObject&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Object&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;out&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;ProjectList&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
     &lt;span class="nx"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TypeMeta&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;in&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TypeMeta&lt;/span&gt;
     &lt;span class="nx"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ListMeta&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;in&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ListMeta&lt;/span&gt;

     &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;in&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Items&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="nx"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Items&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="nx"&gt;Project&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;in&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Items&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
         &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="nx"&gt;in&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Items&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
             &lt;span class="nx"&gt;in&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;DeepCopyInto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
         &lt;span class="p"&gt;}&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;

     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;out&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;上面这个DeepCopy是我们手动来生成的，你可能已经注意到，定义所有这些不同的
DeepCopy
方法并不是一件很有趣的事情。有很多不同的工具和框架可以自动生成这些方法（所有的文档和整体成熟度都有很大的不同）。我发现效果最好的是控制器生成工具，它是&lt;a class="reference external" href="https://github.com/kubernetes-sigs/kubebuilder"&gt;Kubebuilder&lt;/a&gt;框架的一部分。&lt;/p&gt;
&lt;p&gt;下面我们就来看一下：&lt;/p&gt;
&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;go&lt;/span&gt; &lt;span class="pre"&gt;get&lt;/span&gt; &lt;span class="pre"&gt;-u&lt;/span&gt; &lt;span class="pre"&gt;github.com/kubernetes-sigs/controller-tools/cmd/controller-gen&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;为了能够使用&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;controller-gen&lt;/span&gt;&lt;/code&gt;，我们需要在CRD类型上面的添加一个annotation，如下：&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id8"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;test.go&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;1
2
3
4
5
6
7
8
9&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt; &lt;span class="c1"&gt;// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object&lt;/span&gt;
 &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Project&lt;/span&gt; &lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="c1"&gt;// ...&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;

 &lt;span class="c1"&gt;// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object&lt;/span&gt;
 &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ProjectList&lt;/span&gt; &lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="c1"&gt;// ...&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="admonition tip"&gt;
&lt;p class="admonition-title"&gt;Tip&lt;/p&gt;
&lt;p&gt;说明：对于这些annotation我们没有必要去全部记住，只有当使用到的时候再去查阅一下就行，根据二八原则，只需要记住一些常用的就可以了，其它那些不常用的只需要了解一下。&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;写好了上述代码，我们运行一下命令&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;controller-gen&lt;/span&gt; &lt;span class="pre"&gt;object&lt;/span&gt; &lt;span class="pre"&gt;paths=./api/types/v1alpha1/project.go&lt;/span&gt;&lt;/code&gt;即可生成需要代码。&lt;/p&gt;
&lt;p&gt;为了更加的简化，你甚至可以在代码文件的前面加一个声明&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;go:generate&lt;/span&gt;&lt;/code&gt;，具体请&lt;a class="reference external" href="https://blog.golang.org/generate"&gt;参考&lt;/a&gt;。如：&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id9"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;test.go&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;1
2
3
4
5
6
7&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt; &lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nx"&gt;v1alpha1&lt;/span&gt;

 &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;metav1&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;k8s.io/apimachinery/pkg/apis/meta/v1&amp;quot;&lt;/span&gt;

 &lt;span class="c1"&gt;//go:generate controller-gen object paths=$GOFILE&lt;/span&gt;

 &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;然后只需要在代码的根路径中执行&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;go&lt;/span&gt; &lt;span class="pre"&gt;generate&lt;/span&gt; &lt;span class="pre"&gt;./...&lt;/span&gt;&lt;/code&gt;即可。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id4"&gt;
&lt;h3&gt;4.3 注册类型&lt;/h3&gt;
&lt;p&gt;接下来，你需要让客户端库知道你的新类型。这将允许客户端在与API服务器通信时（或多或少）自动处理你的新类型。&lt;/p&gt;
&lt;p&gt;为此，在你的包中添加一个新文件 register.go。&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id10"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;register.go&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt; &lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nx"&gt;v1alpha1&lt;/span&gt;

 &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
     &lt;span class="nx"&gt;metav1&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;k8s.io/apimachinery/pkg/apis/meta/v1&amp;quot;&lt;/span&gt;
     &lt;span class="s"&gt;&amp;quot;k8s.io/apimachinery/pkg/runtime&amp;quot;&lt;/span&gt;
     &lt;span class="s"&gt;&amp;quot;k8s.io/apimachinery/pkg/runtime/schema&amp;quot;&lt;/span&gt;
 &lt;span class="p"&gt;)&lt;/span&gt;

 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;GroupName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;example-gzh.com&amp;quot;&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;GroupVersion&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;v1alpha1&amp;quot;&lt;/span&gt;

 &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;SchemeGroupVersion&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GroupVersion&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;GroupName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;GroupVersion&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

 &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
     &lt;span class="nx"&gt;SchemeBuilder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NewSchemeBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;addKnownTypes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="nx"&gt;AddToScheme&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;SchemeBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AddToScheme&lt;/span&gt;
 &lt;span class="p"&gt;)&lt;/span&gt;

 &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nx"&gt;addKnownTypes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scheme&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Scheme&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;scheme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AddKnownTypes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;SchemeGroupVersion&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;Project&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;
         &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;ProjectList&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;
     &lt;span class="p"&gt;)&lt;/span&gt;

     &lt;span class="nx"&gt;metav1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AddToGroupVersion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scheme&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;SchemeGroupVersion&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;正如你所注意到的，这段代码还没有真正做任何事情（除了创建一个新的runtime.SchemeBuilder实例）。重要的部分是AddToScheme函数（第16行），它是第15行创建的runtime.SchemeBuilder类型的导出结构成员。只要Kubernetes客户端被初始化以注册你的类型定
义，你就可以在以后从客户端代码的任何部分调用这个函数。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="http-client"&gt;
&lt;h3&gt;4.4 创建HTTP Client&lt;/h3&gt;
&lt;p&gt;在定义了类型并添加了一个方法来在全局方案构建器上注册它们之后，你现在可以创建一个能够加载你的自定义资源的HTTP客户端。&lt;/p&gt;
&lt;p&gt;为此，将以下代码添加到你的包的main.go文件中：&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id11"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;main.go&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt; &lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt;

 &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
     &lt;span class="s"&gt;&amp;quot;flag&amp;quot;&lt;/span&gt;
     &lt;span class="s"&gt;&amp;quot;log&amp;quot;&lt;/span&gt;

     &lt;span class="s"&gt;&amp;quot;ks.io/apimachinery/pkg/runtime/schema&amp;quot;&lt;/span&gt;
     &lt;span class="s"&gt;&amp;quot;ks.io/apimachinery/pkg/runtime/serializer&amp;quot;&lt;/span&gt;

     &lt;span class="s"&gt;&amp;quot;github.com/double12gzh/k8s-demo/api/types/valpha&amp;quot;&lt;/span&gt;
     &lt;span class="s"&gt;&amp;quot;ks.io/client-go/kubernetes/scheme&amp;quot;&lt;/span&gt;
     &lt;span class="s"&gt;&amp;quot;ks.io/client-go/rest&amp;quot;&lt;/span&gt;
     &lt;span class="s"&gt;&amp;quot;ks.io/client-go/tools/clientcmd&amp;quot;&lt;/span&gt;
 &lt;span class="p"&gt;)&lt;/span&gt;

 &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;kubeconfig&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;

 &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;flag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StringVar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;kubeconfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;kubeconfig&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;path to Kubernetes config file&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="nx"&gt;flag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Parse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;

 &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Config&lt;/span&gt;
     &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;

     &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;kubeconfig&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;using in-cluster configuration&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
         &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;InClusterConfig&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;using configuration from &amp;#39;%s&amp;#39;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;kubeconfig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
         &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;clientcmd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BuildConfigFromFlags&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;kubeconfig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;

     &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;

     &lt;span class="nx"&gt;valpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AddToScheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scheme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Scheme&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

     &lt;span class="nx"&gt;crdConfig&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;
     &lt;span class="nx"&gt;crdConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ContentConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GroupVersion&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GroupVersion&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;valpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GroupName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;valpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GroupVersion&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
     &lt;span class="nx"&gt;crdConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;APIPath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/apis&amp;quot;&lt;/span&gt;
     &lt;span class="nx"&gt;crdConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NegotiatedSerializer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;serializer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NewCodecFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scheme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Scheme&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="nx"&gt;crdConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;UserAgent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DefaultKubernetesUserAgent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

     &lt;span class="nx"&gt;exampleRestClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;UnversionedRESTClientFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;crdConfig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;现在你可以使用第48行创建的exampleRestClient来查询example.martin-helmich.de/v1alpha1
API组中的所有自定义资源。例如：&lt;/p&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;v1alpha1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ProjectList&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;exampleRestClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;projects&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;Do&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;Into&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;为了以一种更安全的方式使用你的API，通常情况下，我们最好在自己的clientet中封装这些操作。为此，创建一个新的子包clientet/v1alpha1。&lt;/p&gt;
&lt;p&gt;首先，实现一个定义你的API组类型的接口，并将配置设置从你的主方法移到该clientet的构造函数中（下面例子中的NewForConfig）。&lt;/p&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nx"&gt;valpha&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;&amp;quot;github.com/double12gzh/k8s-demo/api/types/valpha&amp;quot;&lt;/span&gt;
    &lt;span class="s"&gt;&amp;quot;ks.io/apimachinery/pkg/runtime/schema&amp;quot;&lt;/span&gt;
    &lt;span class="s"&gt;&amp;quot;ks.io/client-go/kubernetes/scheme&amp;quot;&lt;/span&gt;
    &lt;span class="s"&gt;&amp;quot;ks.io/client-go/rest&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ExampleVAlphaInterface&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Projects&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;namespace&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;ProjectInterface&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ExampleVAlphaClient&lt;/span&gt; &lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;restClient&lt;/span&gt; &lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Interface&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nx"&gt;NewForConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;ExampleVAlphaClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;
    &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ContentConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GroupVersion&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GroupVersion&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;valpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GroupName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;valpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GroupVersion&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;APIPath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/apis&amp;quot;&lt;/span&gt;
    &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NegotiatedSerializer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;scheme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Codecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WithoutConversion&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;UserAgent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DefaultKubernetesUserAgent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RESTClientFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;ExampleVAlphaClient&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;restClient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;ExampleVAlphaClient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;Projects&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;namespace&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;ProjectInterface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;projectClient&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;restClient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;restClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;ns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;         &lt;span class="nx"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;下面的代码还不能编译，因为它仍然缺少&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;ProjectInterface&lt;/span&gt;&lt;/code&gt;和&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;projectClient&lt;/span&gt;&lt;/code&gt;类型。我们稍后将讨论这些类型。&lt;/p&gt;
&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;ExampleV1Alpha1Interface&lt;/span&gt;&lt;/code&gt;和它的实现&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;--ExampleV1Alpha1Client&lt;/span&gt;&lt;/code&gt;结构现在是访问自定义资源的中心点。现在，你可以在&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;main.go&lt;/span&gt;&lt;/code&gt;中简单地调用&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;clientet,&lt;/span&gt; &lt;span class="pre"&gt;err&lt;/span&gt; &lt;span class="pre"&gt;:=&lt;/span&gt; &lt;span class="pre"&gt;v1alpha1.NewForConfig(config)&lt;/span&gt;&lt;/code&gt;来创建一个新的客户集。&lt;/p&gt;
&lt;p&gt;接下来，你需要实现一个特定的&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;clientset&lt;/span&gt;&lt;/code&gt;来访问Project自定义资源（注意，上面的例子已经使用了&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;ProjectInterface&lt;/span&gt;&lt;/code&gt;和&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;projectClient&lt;/span&gt;&lt;/code&gt;类型，我们仍然需要提供）。在同一个包中创建第二个文件&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;projects.go&lt;/span&gt;&lt;/code&gt;。&lt;/p&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nx"&gt;valpha&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;&amp;quot;github.com/double12gzh/k8s-demo/api/types/valpha&amp;quot;&lt;/span&gt;
    &lt;span class="nx"&gt;metav&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;ks.io/apimachinery/pkg/apis/meta/v&amp;quot;&lt;/span&gt;
    &lt;span class="s"&gt;&amp;quot;ks.io/apimachinery/pkg/watch&amp;quot;&lt;/span&gt;
    &lt;span class="s"&gt;&amp;quot;ks.io/client-go/kubernetes/scheme&amp;quot;&lt;/span&gt;
    &lt;span class="s"&gt;&amp;quot;ks.io/client-go/rest&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ProjectInterface&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;List&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt; &lt;span class="nx"&gt;metav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ListOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;valpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ProjectList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="nx"&gt;metav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GetOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;valpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Project&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;valpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Project&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;valpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Project&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;Watch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt; &lt;span class="nx"&gt;metav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ListOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;watch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Interface&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;projectClient&lt;/span&gt; &lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;restClient&lt;/span&gt; &lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Interface&lt;/span&gt;
    &lt;span class="nx"&gt;ns&lt;/span&gt;         &lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;projectClient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;List&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt; &lt;span class="nx"&gt;metav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ListOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;valpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ProjectList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;valpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ProjectList&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;restClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
        &lt;span class="nx"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;
        &lt;span class="nx"&gt;Namespace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ns&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
        &lt;span class="nx"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;projects&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
        &lt;span class="nx"&gt;VersionedParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;scheme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ParameterCodec&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
        &lt;span class="nx"&gt;Do&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;
        &lt;span class="nx"&gt;Into&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;projectClient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt; &lt;span class="nx"&gt;metav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GetOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;valpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Project&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;valpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Project&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;restClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
        &lt;span class="nx"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;
        &lt;span class="nx"&gt;Namespace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ns&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
        &lt;span class="nx"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;projects&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
        &lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
        &lt;span class="nx"&gt;VersionedParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;scheme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ParameterCodec&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
        &lt;span class="nx"&gt;Do&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;
        &lt;span class="nx"&gt;Into&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;projectClient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;project&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;valpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Project&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;valpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Project&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;valpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Project&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;restClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
        &lt;span class="nx"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;
        &lt;span class="nx"&gt;Namespace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ns&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
        &lt;span class="nx"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;projects&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
        &lt;span class="nx"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;project&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
        &lt;span class="nx"&gt;Do&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;
        &lt;span class="nx"&gt;Into&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;projectClient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;Watch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt; &lt;span class="nx"&gt;metav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ListOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;watch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Interface&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Watch&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;restClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
        &lt;span class="nx"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;
        &lt;span class="nx"&gt;Namespace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ns&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
        &lt;span class="nx"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;projects&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
        &lt;span class="nx"&gt;VersionedParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;scheme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ParameterCodec&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
        &lt;span class="nx"&gt;Watch&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;这个client显然还不完善，还缺失了删除、更新等方法。不过，这些方法可以和已有的方法类似实现。看看现有的clientset（例如，Pod
clientset）以获得灵感。&lt;/p&gt;
&lt;p&gt;在创建了clientset之后，用它来列出你现有的资源就变得非常容易了。&lt;/p&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;&amp;quot;fmt&amp;quot;&lt;/span&gt;

    &lt;span class="nx"&gt;clientValpha&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;github.com/double12gzh/k8s-demo/clientset/valpha&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;

    &lt;span class="nx"&gt;clientSet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;clientValpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NewForConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;projects&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;clientSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Projects&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;default&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;List&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ListOptions&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;projects found: %+v\n&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;projects&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="informer"&gt;
&lt;h3&gt;4.5 生成Informer&lt;/h3&gt;
&lt;p&gt;在构建Kubernetes
Operator时，您通常希望能够对新创建或更新的资源做出反应。理论上，您可以定期调用List()方法，检查是否有新资源被添加。在实践中，这是一个次优的解决方案，尤其是当您有很多这样的资源时。&lt;/p&gt;
&lt;p&gt;大多数Operator的工作方式是通过使用初始List()调用来初始加载资源的所有相关实例，然后使用Watch()调用来订阅更新。然后，初始对象列表和从Watch接收到的更新被用来构建一个本地缓存，允许快速访问任何自定义资源，而不必每次都打到API服务器。&lt;/p&gt;
&lt;p&gt;这种模式非常常见，以至于client-go库为此提供了一个助手：k8s.io/client-go/tools/cache包中的Informer。您可以为您的自定义资源构建一个新的
Informer，如下所示：&lt;/p&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;&amp;quot;time&amp;quot;&lt;/span&gt;

    &lt;span class="s"&gt;&amp;quot;github.com/double12gzh/k8s-demo/api/types/valpha&amp;quot;&lt;/span&gt;
    &lt;span class="nx"&gt;client_valpha&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;github.com/double12gzh/k8s-demo/clientset/valpha&amp;quot;&lt;/span&gt;
    &lt;span class="nx"&gt;metav&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;ks.io/apimachinery/pkg/apis/meta/v&amp;quot;&lt;/span&gt;
    &lt;span class="s"&gt;&amp;quot;ks.io/apimachinery/pkg/runtime&amp;quot;&lt;/span&gt;
    &lt;span class="s"&gt;&amp;quot;ks.io/apimachinery/pkg/util/wait&amp;quot;&lt;/span&gt;
    &lt;span class="s"&gt;&amp;quot;ks.io/apimachinery/pkg/watch&amp;quot;&lt;/span&gt;
    &lt;span class="s"&gt;&amp;quot;ks.io/client-go/tools/cache&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nx"&gt;WatchResources&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;clientSet&lt;/span&gt; &lt;span class="nx"&gt;client_valpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ExampleVAlphaInterface&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Store&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;projectStore&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;projectController&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NewInformer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ListWatch&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;ListFunc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lo&lt;/span&gt; &lt;span class="nx"&gt;metav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ListOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;clientSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Projects&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;some-namespace&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;List&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="nx"&gt;WatchFunc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lo&lt;/span&gt; &lt;span class="nx"&gt;metav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ListOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;watch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Interface&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;clientSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Projects&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;some-namespace&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;Watch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;valpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Project&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;
        &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Minute&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ResourceEventHandlerFuncs&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="nx"&gt;projectController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NeverStop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;projectStore&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;NewInformer&lt;/span&gt;&lt;/code&gt;方法返回两个对象。第二个返回值，控制器控制&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;List()&lt;/span&gt;&lt;/code&gt;和&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Watch()&lt;/span&gt;&lt;/code&gt;调用，并在第一个返回值，即存储中填充一个（或多或少）最近在API服务器上被监视的资源状态的缓存（在本例中，项目CRD）。&lt;/p&gt;
&lt;p&gt;现在，你可以使用 &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;store&lt;/span&gt;&lt;/code&gt; 来轻松访问你的 &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;CRD&lt;/span&gt;&lt;/code&gt;，要么列出所有的
CRD，要么通过名称来访问它们。请记住，存储函数返回的是通用&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;interface{}&lt;/span&gt;&lt;/code&gt;类型，所以您必须将它们类型化回您的CRD类型。&lt;/p&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;WatchResource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;clientSet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;project&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GetByKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;some-namespace/some-project&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;v1alpha1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Project&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="id5"&gt;
&lt;h2&gt;5. 总结&lt;/h2&gt;
&lt;p&gt;为Custom Resources构建客户端是（至少，目前）只有很少的文档，有时可能会有点棘手。&lt;/p&gt;
&lt;p&gt;如本文所示，为你的Custom
Resource建立一个客户端库，以及相应的Informer是一个很好的起点，可以构建你自己的Kubernetes
Operator，对Custom Resource的变化做出反应。&lt;/p&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;您可以到我的&lt;a class="reference external" href="https://github.com/double12gzh/k8s-demo.git"&gt;github&lt;/a&gt;上查看完整代码&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;hr class="docutils" /&gt;
&lt;p&gt;欢迎关注我的微信公众号：&lt;/p&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/wechat_public.jpg" /&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
</content>
    <link href="double12gzhblogs.readthedocs.io/blogs/Kubernetes/2020-09-26-%E4%BD%BF%E7%94%A8client-go%E8%AE%BF%E9%97%AEk8s%E9%9B%86%E7%BE%A4%E4%B8%AD%E7%9A%84CRD.html" rel="alternate"/>
    <published>2020-09-26T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>double12gzhblogs.readthedocs.io/blogs/Kubernetes/2020-09-27-WSL-Docker-Kubernetes-on-the-Windows-Desktop.html</id>
    <title>在windows上通过wsl来部署k8s</title>
    <updated>2020-09-27T00:00:00+00:00</updated>
    <author>
      <name>大海星</name>
    </author>
    <content type="html">&lt;div class="section" id="windowswslk8s"&gt;

&lt;p&gt;摘要：是Windows10和WSL2的新手，还是Docker和Kubernetes的新手？欢迎来到这篇博文，我们将在Docker KinD和Minikube中从头开始安装Kubernetes。&lt;/p&gt;
&lt;div class="section" id="id1"&gt;
&lt;h2&gt;1. 写在前面&lt;/h2&gt;
&lt;p&gt;为什么要在windows上使用kubernetes?&lt;/p&gt;
&lt;p&gt;在过去的几年里，Kubernetes成为了在分布式环境中运行容器化服务和应用的事实标准平台。虽然存在各种各样的发行版和安装程序来部署Kubernetes在云环境（公共的、私有的或混合的），或在裸机环境中，但仍然需要在本地部署和运行Kubernetes，例如，在开发者的工作站上。&lt;/p&gt;
&lt;p&gt;Kubernetes最初被设计为在Linux环境中部署和使用。然而，有不少用户（不仅是应用开发者）使用Windows操作系统作为日常工具。当微软揭示了WSL–Windows Subsystem for Linux时，Windows和Linux环境之间的界限变得更加不明显。&lt;/p&gt;
&lt;p&gt;同时，WSL还带来了在Windows上几乎无缝运行Kubernetes的能力!&lt;/p&gt;
&lt;p&gt;下面，我们将简要介绍如何安装和使用各种解决方案在本地运行Kubernetes。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id2"&gt;
&lt;h2&gt;2. 前提条件&lt;/h2&gt;
&lt;p&gt;尽管我们将会安装&lt;a class="reference external" href="https://kind.sigs.k8s.io/"&gt;Kind&lt;/a&gt;，但是，讲解太多关于如何安装的细节，如果您对这个比较关注，请关注我后续的文章。&lt;/p&gt;
&lt;p&gt;首先，我们先看一下我们继续进行下面的操作前，我们的最小的软件版本的需求:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;OS：Win10 version 2004, build 19041。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;WSL2开启。安装完成后，可以使用&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;wsl.exe&lt;/span&gt; &lt;span class="pre"&gt;--set-default-version&lt;/span&gt; &lt;span class="pre"&gt;2&lt;/span&gt;&lt;/code&gt;设置使用WSL2。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;WSL2可以通过win应用商店来安装，这里使用的是Ubuntu 18.04。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://hub.docker.com/editions/community/docker-ce-desktop-windows"&gt;Docker-desktop-for-windows&lt;/a&gt;。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;(可选)Microsoft Terminal。可以从windows应用商店进行安装。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-27-wsl_docker_kubernetes_win10/0.png" /&gt;
&lt;/div&gt;
&lt;div class="admonition tip"&gt;
&lt;p class="admonition-title"&gt;Tip&lt;/p&gt;
&lt;dl class="simple"&gt;
&lt;dt&gt;如何查看win10的版本信息？&lt;/dt&gt;&lt;dd&gt;&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;win+R&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;输入winver&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="wsl2"&gt;
&lt;h2&gt;3. WSL2：首次接触&lt;/h2&gt;
&lt;p&gt;上面的软件都安装完成后，我们就可以从&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;开始-&amp;gt;Ubuntu&lt;/span&gt;&lt;/code&gt;来打开wsl2了。&lt;/p&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-27-wsl_docker_kubernetes_win10/1.png" /&gt;
&lt;/div&gt;
&lt;p&gt;打开后，你完全可以把它看成是一个正常的Linux的发行，同样的，你也可以在里面设置用户等。&lt;/p&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-27-wsl_docker_kubernetes_win10/2.png" /&gt;
&lt;/div&gt;
&lt;div class="section" id="sudoers"&gt;
&lt;h3&gt;3.1 (可选)设置``sudoers``&lt;/h3&gt;
&lt;p&gt;由于我们是在本地计算机上工作，通常情况下，更新sudoers，并将%sudo组设置为无密码组，可能会更好。&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# 用visudo打开sudoer文件&lt;/span&gt;
sudo visudo

&lt;span class="c1"&gt;# 设置sudo group免密登陆&lt;/span&gt;
%sudo   &lt;span class="nv"&gt;ALL&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;ALL:ALL&lt;span class="o"&gt;)&lt;/span&gt; NOPASSWD: ALL

&lt;span class="c1"&gt;# 按 CTRL+X 退出&lt;/span&gt;
&lt;span class="c1"&gt;# 按 Y 保存&lt;/span&gt;
&lt;span class="c1"&gt;# 按 Enter 确认&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-27-wsl_docker_kubernetes_win10/3.png" /&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="ubuntu"&gt;
&lt;h3&gt;3.2 更新Ubuntu&lt;/h3&gt;
&lt;p&gt;在进行后面的操作前，我们先升级一下我们的系统。&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# 更新apt-get源&lt;/span&gt;
sudo apt update
&lt;span class="c1"&gt;# 基于最新的源更新系统。automatically&lt;/span&gt;
sudo apt upgrade -y
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-27-wsl_docker_kubernetes_win10/4.png" /&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="docker-desktop"&gt;
&lt;h2&gt;4. Docker Desktop&lt;/h2&gt;
&lt;p&gt;Docker-Windows-Desktop与WSL2更配哦。&lt;/p&gt;
&lt;p&gt;在我们进入设置之前，让我们做一个小测试，它将显示出与Docker Desktop的新集成是多么的酷。&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# 看一下docker的版本及docker是否被安装&lt;/span&gt;
docker version
&lt;span class="c1"&gt;# 看一下kubectl的版本及kubectl是否被安装&lt;/span&gt;
kubectl version
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-27-wsl_docker_kubernetes_win10/5.png" /&gt;
&lt;/div&gt;
&lt;p&gt;你有遇到错误吗？有错误就对了，所以现在让我们继续进行设置。&lt;/p&gt;
&lt;div class="section" id="docker-desktop-wsl2"&gt;
&lt;h3&gt;4.1 设置docker desktop：开启对WSL2的支持&lt;/h3&gt;
&lt;p&gt;如果还没有打开Docker-For-Windows的话，首先我们来启动它。打开Windows的开始菜单，输入”docker”，点击名称启动应用程序。&lt;/p&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-27-wsl_docker_kubernetes_win10/6.png" /&gt;
&lt;/div&gt;
&lt;p&gt;现在在你的任务栏中就能看到它的图标了。&lt;/p&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-27-wsl_docker_kubernetes_win10/7.png" /&gt;
&lt;/div&gt;
&lt;p&gt;下面我们在docker-for-windows的中设置一下，让他使用wsl2:&lt;/p&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-27-wsl_docker_kubernetes_win10/8.png" /&gt;
&lt;/div&gt;
&lt;p&gt;这个选项默认情况下并没有开启:&lt;/p&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-27-wsl_docker_kubernetes_win10/9.png" /&gt;
&lt;/div&gt;
&lt;p&gt;这个功能在幕后所做的就是在WSL2中创建了两个新的发行版，包含并运行所有需要的后端socket、守护进程以及CLI工具（如：docker和kubectl命令）。&lt;/p&gt;
&lt;p&gt;但是，第一种设置仍然不足以在我们的发行版中运行命令。如果我们尝试，我们会遇到和之前一样的错误。&lt;/p&gt;
&lt;p&gt;为了解决这个问题，并最终能够使用这些命令，我们需要告诉Docker桌面也要”附加 “到我们的发行版上。&lt;/p&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-27-wsl_docker_kubernetes_win10/10.png" /&gt;
&lt;/div&gt;
&lt;p&gt;接下来，我们再看一下命令是否正常了呢：&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# 看一下docker的版本及docker是否被安装&lt;/span&gt;
docker version
&lt;span class="c1"&gt;# 看一下kubectl的版本及kubectl是否被安装&lt;/span&gt;
kubectl version
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-27-wsl_docker_kubernetes_win10/11.png" /&gt;
&lt;/div&gt;
&lt;div class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: ERROR/3 (&lt;span class="docutils literal"&gt;/home/docs/checkouts/readthedocs.org/user_builds/gzh/checkouts/latest/blogs/Kubernetes/2020-09-27-WSL-Docker-Kubernetes-on-the-Windows-Desktop.rst&lt;/span&gt;, line 155)&lt;/p&gt;
&lt;p&gt;Figure caption must be a paragraph or empty comment.&lt;/p&gt;
&lt;div class="highlight-default notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;.. figure:: https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-27-wsl_docker_kubernetes_win10/11.png
   :alt:

    说明:

    -  如果没有发生任何事情，重启Docker
       Desktop并在Powershell中重启WSL进程。重启服务LxssManager并启动一个新的Ubuntu会话。
    -  如果您的docker-desktop-for-win中没有\ ``Use the WSL2 based engine``\ 的，请重新安装它的\ ``edge``\ 版本。
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="kind-k8s"&gt;
&lt;h2&gt;5. Kind: 让k8s方便的运行在容器中&lt;/h2&gt;
&lt;p&gt;现在，我们的Docker已经安装好了，配置好了，上次的测试也很正常。&lt;/p&gt;
&lt;p&gt;但是，如果我们仔细观察kubectl命令，它找到了”客户端版本”（1.15.5），但它没有找到任何服务器。&lt;/p&gt;
&lt;p&gt;这很正常，因为我们没有启用Docker Kubernetes集群。所以让我们安装KinD并创建我们的第一个集群。&lt;/p&gt;
&lt;p&gt;我们将按照KinD官方网站上的&lt;a class="reference external" href="https://kind.sigs.k8s.io/docs/user/quick-start/"&gt;教程（部分）&lt;/a&gt;进行操作。&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# 下载 KinD&lt;/span&gt;
curl -Lo ./kind https://github.com/kubernetes-sigs/kind/releases/download/v0.7.0/kind-&lt;span class="k"&gt;$(&lt;/span&gt;uname&lt;span class="k"&gt;)&lt;/span&gt;-amd64
&lt;span class="c1"&gt;# 增加可执行权限&lt;/span&gt;
chmod +x ./kind
&lt;span class="c1"&gt;# 移动到系统PATH中&lt;/span&gt;
sudo mv ./kind /usr/local/bin/
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-27-wsl_docker_kubernetes_win10/12.png" /&gt;
&lt;/div&gt;
&lt;div class="section" id="id3"&gt;
&lt;h3&gt;5.1 创建单节点的集群&lt;/h3&gt;
&lt;p&gt;下面，我们将会创建我们的第一个集群：&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# 检查 KUBECONFIG 是否存在&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$KUBECONFIG&lt;/span&gt;
&lt;span class="c1"&gt;# Check if the .kube directory is created &amp;gt; if not, no need to create it&lt;/span&gt;
ls &lt;span class="nv"&gt;$HOME&lt;/span&gt;/.kube
&lt;span class="c1"&gt;# Create the cluster and give it a name (optional)&lt;/span&gt;
kind create cluster --name wslkind
&lt;span class="c1"&gt;# Check if the .kube has been created and populated with files&lt;/span&gt;
ls &lt;span class="nv"&gt;$HOME&lt;/span&gt;/.kube
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-27-wsl_docker_kubernetes_win10/13.png" /&gt;
&lt;/div&gt;
&lt;div class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: ERROR/3 (&lt;span class="docutils literal"&gt;/home/docs/checkouts/readthedocs.org/user_builds/gzh/checkouts/latest/blogs/Kubernetes/2020-09-27-WSL-Docker-Kubernetes-on-the-Windows-Desktop.rst&lt;/span&gt;, line 203)&lt;/p&gt;
&lt;p&gt;Figure caption must be a paragraph or empty comment.&lt;/p&gt;
&lt;div class="highlight-default notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;.. figure:: https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-27-wsl_docker_kubernetes_win10/13.png
   :alt:

    说明：对于执行的每一步，都会有一个小图标与之显示。
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;运行完上面的命令后，k8s集群就已经创建好了，因为我们使用是docker desktop，所以，里面的网络默认使用的是&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;as&lt;/span&gt; &lt;span class="pre"&gt;is&lt;/span&gt;&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;我们可以在浏览器中打开&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;kubernetes&lt;/span&gt; &lt;span class="pre"&gt;master&lt;/span&gt;&lt;/code&gt;的页面：&lt;/p&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-27-wsl_docker_kubernetes_win10/14.png" /&gt;
&lt;/div&gt;
&lt;p&gt;而这才是Docker Desktop for Windows与WSL2后台的真正优势。Docker真的做了一个惊人的整合。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id4"&gt;
&lt;h3&gt;5.2 创建多节点的集群&lt;/h3&gt;
&lt;p&gt;上一节中，我们刚创建的那是一个单节点的集群。&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Check how many nodes it created&lt;/span&gt;
kubectl get nodes
&lt;span class="c1"&gt;# Check the services for the whole cluster&lt;/span&gt;
kubectl get all --all-namespaces
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-27-wsl_docker_kubernetes_win10/15.png" /&gt;
&lt;/div&gt;
&lt;p&gt;虽然那也已经能满足大多数人的需求了，但是，下面我们还是来继续看一下它另外一个更加酷炫的能力：创建多节点的集群。&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Delete the existing cluster&lt;/span&gt;
kind delete cluster --name wslkind
&lt;span class="c1"&gt;# Create a config file for a 3 nodes cluster&lt;/span&gt;
cat &lt;span class="s"&gt;&amp;lt;&amp;lt; EOF &amp;gt; kind-3nodes.yaml&lt;/span&gt;
&lt;span class="s"&gt;kind: Cluster&lt;/span&gt;
&lt;span class="s"&gt;apiVersion: kind.x-k8s.io/v1alpha4&lt;/span&gt;
&lt;span class="s"&gt;nodes:&lt;/span&gt;
&lt;span class="s"&gt;  - role: control-plane&lt;/span&gt;
&lt;span class="s"&gt;  - role: worker&lt;/span&gt;
&lt;span class="s"&gt;  - role: worker&lt;/span&gt;
&lt;span class="s"&gt;EOF&lt;/span&gt;
&lt;span class="c1"&gt;# Create a new cluster with the config file&lt;/span&gt;
kind create cluster --name wslkindmultinodes --config ./kind-3nodes.yaml
&lt;span class="c1"&gt;# Check how many nodes it created&lt;/span&gt;
kubectl get nodes
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-27-wsl_docker_kubernetes_win10/16.png" /&gt;
&lt;/div&gt;
&lt;div class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: ERROR/3 (&lt;span class="docutils literal"&gt;/home/docs/checkouts/readthedocs.org/user_builds/gzh/checkouts/latest/blogs/Kubernetes/2020-09-27-WSL-Docker-Kubernetes-on-the-Windows-Desktop.rst&lt;/span&gt;, line 253)&lt;/p&gt;
&lt;p&gt;Figure caption must be a paragraph or empty comment.&lt;/p&gt;
&lt;div class="highlight-default notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;.. figure:: https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-27-wsl_docker_kubernetes_win10/16.png
   :alt:

    提示：根据我们运行 &amp;quot;get nodes&amp;quot;命令的速度，可能不是所有的节点都准备好了，等几秒钟再运行，一切都应该准备好了。
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;到此，我们已经创建完了三节点的一个集群，我们通过下面的命令再来看一下，如果你仔细看你会发现，每一个服务都是三副本的了。&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Check the services for the whole cluster&lt;/span&gt;
kubectl get all --all-namespaces
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-27-wsl_docker_kubernetes_win10/17.png" /&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="k8s-dashboard"&gt;
&lt;h3&gt;5.3 查看k8s Dashboard&lt;/h3&gt;
&lt;p&gt;对于开发人员来说，在命令行上执行操作是可以接受，并且这样效率也比较高。然而，在处理Kubernetes时，我们可能希望在某些时候有一个可视化的概述。为此，我们创建了&lt;a class="reference external" href="https://github.com/kubernetes/dashboard"&gt;Kubernetes
Dashboard&lt;/a&gt;项目。安装和第一次连接测试是相当快的，所以我们来做吧。&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Install the Dashboard application into our cluster&lt;/span&gt;
kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0-rc6/aio/deploy/recommended.yaml
&lt;span class="c1"&gt;# Check the resources it created based on the new namespace created&lt;/span&gt;
kubectl get all -n kubernetes-dashboard
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-27-wsl_docker_kubernetes_win10/18.png" /&gt;
&lt;/div&gt;
&lt;p&gt;虽然kind使用&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;ClusterIP&lt;/span&gt;&lt;/code&gt;为集群创建了一个&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;service&lt;/span&gt;&lt;/code&gt;，但是由于这个IP是一个内网的IP，所以我们现在还无法从页面上直接访问&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;dashboard&lt;/span&gt;&lt;/code&gt;服务。&lt;/p&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-27-wsl_docker_kubernetes_win10/19.png" /&gt;
&lt;/div&gt;
&lt;p&gt;这个问题可以通过创建一个&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;proxy&lt;/span&gt;&lt;/code&gt;来解决：&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Start a kubectl proxy&lt;/span&gt;
kubectl proxy
&lt;span class="c1"&gt;# Enter the URL on your browser: http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-27-wsl_docker_kubernetes_win10/20.png" /&gt;
&lt;/div&gt;
&lt;p&gt;最后要登录，我们可以输入一个我们没有创建的Token，或者从我们的Cluster中输入kubeconfig文件。&lt;/p&gt;
&lt;p&gt;如果我们尝试用kubeconfig登录，我们会得到错误 “Internal error (500): Not enough data to create auth info structure”。没有足够的数据来创建Auth信息结构”。这是由于kubeconfig文件中缺乏凭证。&lt;/p&gt;
&lt;p&gt;因此，为了避免你以同样的错误结束，让我们按照推荐的RBAC方法。&lt;/p&gt;
&lt;p&gt;让我们打开一个新的WSL2会话。&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Create a new ServiceAccount&lt;/span&gt;
kubectl apply -f - &lt;span class="s"&gt;&amp;lt;&amp;lt;EOF&lt;/span&gt;
&lt;span class="s"&gt;apiVersion: v1&lt;/span&gt;
&lt;span class="s"&gt;kind: ServiceAccount&lt;/span&gt;
&lt;span class="s"&gt;metadata:&lt;/span&gt;
&lt;span class="s"&gt;  name: admin-user&lt;/span&gt;
&lt;span class="s"&gt;  namespace: kubernetes-dashboard&lt;/span&gt;
&lt;span class="s"&gt;EOF&lt;/span&gt;
&lt;span class="c1"&gt;# Create a ClusterRoleBinding for the ServiceAccount&lt;/span&gt;
kubectl apply -f - &lt;span class="s"&gt;&amp;lt;&amp;lt;EOF&lt;/span&gt;
&lt;span class="s"&gt;apiVersion: rbac.authorization.k8s.io/v1&lt;/span&gt;
&lt;span class="s"&gt;kind: ClusterRoleBinding&lt;/span&gt;
&lt;span class="s"&gt;metadata:&lt;/span&gt;
&lt;span class="s"&gt;  name: admin-user&lt;/span&gt;
&lt;span class="s"&gt;roleRef:&lt;/span&gt;
&lt;span class="s"&gt;  apiGroup: rbac.authorization.k8s.io&lt;/span&gt;
&lt;span class="s"&gt;  kind: ClusterRole&lt;/span&gt;
&lt;span class="s"&gt;  name: cluster-admin&lt;/span&gt;
&lt;span class="s"&gt;subjects:&lt;/span&gt;
&lt;span class="s"&gt;- kind: ServiceAccount&lt;/span&gt;
&lt;span class="s"&gt;  name: admin-user&lt;/span&gt;
&lt;span class="s"&gt;  namespace: kubernetes-dashboard&lt;/span&gt;
&lt;span class="s"&gt;EOF&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-27-wsl_docker_kubernetes_win10/21.png" /&gt;
&lt;/div&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Get the Token for the ServiceAccount&lt;/span&gt;
kubectl -n kubernetes-dashboard describe secret &lt;span class="k"&gt;$(&lt;/span&gt;kubectl -n kubernetes-dashboard get secret &lt;span class="p"&gt;|&lt;/span&gt; grep admin-user &lt;span class="p"&gt;|&lt;/span&gt; awk &lt;span class="s1"&gt;&amp;#39;{print $1}&amp;#39;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Copy the token and copy it into the Dashboard login and press &amp;quot;Sign in&amp;quot;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-27-wsl_docker_kubernetes_win10/22.png" /&gt;
&lt;/div&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-27-wsl_docker_kubernetes_win10/23.png" /&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="minikube-k8s"&gt;
&lt;h2&gt;6. minikube: 让k8s可任意部署&lt;/h2&gt;
&lt;p&gt;现在，我们的Docker已经安装好了，配置好了，上次的测试也很正常。&lt;/p&gt;
&lt;p&gt;但是，如果我们仔细观察kubectl命令，它找到了 “Client Version”（1.15.5），但它没有找到任何服务器。&lt;/p&gt;
&lt;p&gt;这很正常，因为我们没有启用Docker Kubernetes集群。所以让我们安装Minikube并创建我们的第一个集群。&lt;/p&gt;
&lt;p&gt;我们将按照&lt;a class="reference external" href="https://kubernetes.io/docs/tasks/tools/install-minikube/"&gt;Kubernetes.io网站上的方法（部分）&lt;/a&gt;进行操作。&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Download the latest version of Minikube&lt;/span&gt;
curl -Lo minikube https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
&lt;span class="c1"&gt;# Make the binary executable&lt;/span&gt;
chmod +x ./minikube
&lt;span class="c1"&gt;# Move the binary to your executable path&lt;/span&gt;
sudo mv ./minikube /usr/local/bin/
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-27-wsl_docker_kubernetes_win10/24.png" /&gt;
&lt;/div&gt;
&lt;div class="section" id="host"&gt;
&lt;h3&gt;6.1 更新Host&lt;/h3&gt;
&lt;p&gt;如果我们按照how-to，它提示我们应该使用–driver=none标志，以便直接在主机和Docker上运行Minikube。&lt;/p&gt;
&lt;p&gt;不幸的是，我们会得到一个关于运行Kubernetes v 1.18需要 “conntrack”的错误信息&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Create a minikube one node cluster&lt;/span&gt;
minikube start --driver&lt;span class="o"&gt;=&lt;/span&gt;none
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-27-wsl_docker_kubernetes_win10/25.png" /&gt;
&lt;/div&gt;
&lt;p&gt;根据提示，我们是缺少了一个包，下面我们就把它安装上：&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Install the conntrack package&lt;/span&gt;
sudo apt install -y conntrack
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;安装完成后，再次启动：&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Create a minikube one node cluster&lt;/span&gt;
minikube start --driver&lt;span class="o"&gt;=&lt;/span&gt;none
&lt;span class="c1"&gt;# We got a permissions error &amp;gt; try again with sudo&lt;/span&gt;
sudo minikube start --driver&lt;span class="o"&gt;=&lt;/span&gt;none
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-27-wsl_docker_kubernetes_win10/26.png" /&gt;
&lt;/div&gt;
&lt;p&gt;这次是不是没有出错呢。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="systemd"&gt;
&lt;h3&gt;6.2 启用&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;systemd&lt;/span&gt;&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;为了能够使用&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;systemd&lt;/span&gt;&lt;/code&gt;，我们需要按照这个&lt;a class="reference external" href="https://github.com/DamionGans/ubuntu-wsl2-systemd-script"&gt;脚本&lt;/a&gt;来配置一下。&lt;/p&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;说明：如果您想了解更多关于这个脚本背后的考虑，请&lt;a class="reference external" href="https://forum.snapcraft.io/t/running-snaps-on-wsl2-insiders-only-for-now/13033"&gt;参考&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;在终端中输入以下命令：&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Install the needed packages&lt;/span&gt;
sudo apt install -yqq daemonize dbus-user-session fontconfig
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-27-wsl_docker_kubernetes_win10/27.png" /&gt;
&lt;/div&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Create the start-systemd-namespace script&lt;/span&gt;
sudo vi /usr/sbin/start-systemd-namespace
&lt;span class="c1"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nv"&gt;SYSTEMD_PID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;ps -ef &lt;span class="p"&gt;|&lt;/span&gt; grep &lt;span class="s1"&gt;&amp;#39;/lib/systemd/systemd --system-unit=basic.target$&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; grep -v unshare &lt;span class="p"&gt;|&lt;/span&gt; awk &lt;span class="s1"&gt;&amp;#39;{print $2}&amp;#39;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; -z &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$SYSTEMD_PID&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$SYSTEMD_PID&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt; !&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;1&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;PRE_NAMESPACE_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
    &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt; -o posix&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        grep -v &lt;span class="s2"&gt;&amp;quot;^BASH&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        grep -v &lt;span class="s2"&gt;&amp;quot;^DIRSTACK=&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        grep -v &lt;span class="s2"&gt;&amp;quot;^EUID=&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        grep -v &lt;span class="s2"&gt;&amp;quot;^GROUPS=&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        grep -v &lt;span class="s2"&gt;&amp;quot;^HOME=&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        grep -v &lt;span class="s2"&gt;&amp;quot;^HOSTNAME=&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        grep -v &lt;span class="s2"&gt;&amp;quot;^HOSTTYPE=&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        grep -v &lt;span class="s2"&gt;&amp;quot;^IFS=&amp;#39;.*&amp;quot;&lt;/span&gt;&lt;span class="s1"&gt;$&amp;#39;\n&amp;#39;&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;#39;&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        grep -v &lt;span class="s2"&gt;&amp;quot;^LANG=&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        grep -v &lt;span class="s2"&gt;&amp;quot;^LOGNAME=&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        grep -v &lt;span class="s2"&gt;&amp;quot;^MACHTYPE=&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        grep -v &lt;span class="s2"&gt;&amp;quot;^NAME=&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        grep -v &lt;span class="s2"&gt;&amp;quot;^OPTERR=&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        grep -v &lt;span class="s2"&gt;&amp;quot;^OPTIND=&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        grep -v &lt;span class="s2"&gt;&amp;quot;^OSTYPE=&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        grep -v &lt;span class="s2"&gt;&amp;quot;^PIPESTATUS=&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        grep -v &lt;span class="s2"&gt;&amp;quot;^POSIXLY_CORRECT=&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        grep -v &lt;span class="s2"&gt;&amp;quot;^PPID=&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        grep -v &lt;span class="s2"&gt;&amp;quot;^PS1=&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        grep -v &lt;span class="s2"&gt;&amp;quot;^PS4=&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        grep -v &lt;span class="s2"&gt;&amp;quot;^SHELL=&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        grep -v &lt;span class="s2"&gt;&amp;quot;^SHELLOPTS=&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        grep -v &lt;span class="s2"&gt;&amp;quot;^SHLVL=&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        grep -v &lt;span class="s2"&gt;&amp;quot;^SYSTEMD_PID=&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        grep -v &lt;span class="s2"&gt;&amp;quot;^UID=&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        grep -v &lt;span class="s2"&gt;&amp;quot;^USER=&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        grep -v &lt;span class="s2"&gt;&amp;quot;^_=&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        cat - &amp;gt; &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.systemd-env&amp;quot;&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;PATH=&amp;#39;&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;&lt;span class="s2"&gt;&amp;#39;&amp;quot;&lt;/span&gt; &amp;gt;&amp;gt; &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.systemd-env&amp;quot;&lt;/span&gt;
    &lt;span class="nb"&gt;exec&lt;/span&gt; sudo /usr/sbin/enter-systemd-namespace &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$BASH_EXECUTION_STRING&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; -n &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$PRE_NAMESPACE_PATH&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$PRE_NAMESPACE_PATH&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Create the enter-systemd-namespace&lt;/span&gt;
sudo vi /usr/sbin/enter-systemd-namespace
&lt;span class="c1"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$UID&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt; !&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;You need to run &lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="s2"&gt; through sudo&amp;quot;&lt;/span&gt;
    &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="nv"&gt;SYSTEMD_PID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;ps -ef &lt;span class="p"&gt;|&lt;/span&gt; grep &lt;span class="s1"&gt;&amp;#39;/lib/systemd/systemd --system-unit=basic.target$&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; grep -v unshare &lt;span class="p"&gt;|&lt;/span&gt; awk &lt;span class="s1"&gt;&amp;#39;{print $2}&amp;#39;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; -z &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$SYSTEMD_PID&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    /usr/sbin/daemonize /usr/bin/unshare --fork --pid --mount-proc /lib/systemd/systemd --system-unit&lt;span class="o"&gt;=&lt;/span&gt;basic.target
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; -z &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$SYSTEMD_PID&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
        &lt;span class="nv"&gt;SYSTEMD_PID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;ps -ef &lt;span class="p"&gt;|&lt;/span&gt; grep &lt;span class="s1"&gt;&amp;#39;/lib/systemd/systemd --system-unit=basic.target$&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; grep -v unshare &lt;span class="p"&gt;|&lt;/span&gt; awk &lt;span class="s1"&gt;&amp;#39;{print $2}&amp;#39;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;done&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; -n &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$SYSTEMD_PID&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$SYSTEMD_PID&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt; !&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;1&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; -n &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt; !&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;bash --login&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt; !&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/bin/bash --login&amp;quot;&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
        &lt;span class="nb"&gt;exec&lt;/span&gt; /usr/bin/nsenter -t &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$SYSTEMD_PID&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt; -a &lt;span class="se"&gt;\&lt;/span&gt;
            /usr/bin/sudo -H -u &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$SUDO_USER&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
            /bin/bash -c &lt;span class="s1"&gt;&amp;#39;set -a; source &amp;quot;$HOME/.systemd-env&amp;quot;; set +a; exec bash -c &amp;#39;&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;%q&amp;quot;&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="nb"&gt;exec&lt;/span&gt; /usr/bin/nsenter -t &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$SYSTEMD_PID&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt; -a &lt;span class="se"&gt;\&lt;/span&gt;
            /bin/login -p -f &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$SUDO_USER&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="k"&gt;$(&lt;/span&gt;/bin/cat &lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.systemd-env&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; grep -v &lt;span class="s2"&gt;&amp;quot;^PATH=&amp;quot;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;fi&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Existential crisis&amp;quot;&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Edit the permissions of the enter-systemd-namespace script&lt;/span&gt;
sudo chmod +x /usr/sbin/enter-systemd-namespace
&lt;span class="c1"&gt;# Edit the bash.bashrc file&lt;/span&gt;
sudo sed -i 2a&lt;span class="s2"&gt;&amp;quot;# Start or enter a PID namespace in WSL2\nsource /usr/sbin/start-systemd-namespace\n&amp;quot;&lt;/span&gt; /etc/bash.bashrc
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-27-wsl_docker_kubernetes_win10/28.png" /&gt;
&lt;/div&gt;
&lt;p&gt;现在再重新打开一个wsl2的窗口，&lt;strong&gt;没有必要&lt;/strong&gt;把前一个窗口关闭。&lt;/p&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-27-wsl_docker_kubernetes_win10/29.png" /&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="id5"&gt;
&lt;h3&gt;6.3 创建集群&lt;/h3&gt;
&lt;p&gt;下面我们创建我们的第一个集群:&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Check if the KUBECONFIG is not set&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$KUBECONFIG&lt;/span&gt;
&lt;span class="c1"&gt;# Check if the .kube directory is created &amp;gt; if not, no need to create it&lt;/span&gt;
ls &lt;span class="nv"&gt;$HOME&lt;/span&gt;/.kube
&lt;span class="c1"&gt;# Check if the .minikube directory is created &amp;gt; if yes, delete it&lt;/span&gt;
ls &lt;span class="nv"&gt;$HOME&lt;/span&gt;/.minikube
&lt;span class="c1"&gt;# Create the cluster with sudo&lt;/span&gt;
sudo minikube start --driver&lt;span class="o"&gt;=&lt;/span&gt;none
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;为了不用&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;sudo&lt;/span&gt;&lt;/code&gt;就能执行&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;kubelet&lt;/span&gt;&lt;/code&gt;，&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Minikube&lt;/span&gt;&lt;/code&gt;建议执行一下&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;chown&lt;/span&gt;&lt;/code&gt;命令:&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Change the owner of the .kube and .minikube directories&lt;/span&gt;
sudo chown -R &lt;span class="nv"&gt;$USER&lt;/span&gt; &lt;span class="nv"&gt;$HOME&lt;/span&gt;/.kube &lt;span class="nv"&gt;$HOME&lt;/span&gt;/.minikube
&lt;span class="c1"&gt;# Check the access and if the cluster is running&lt;/span&gt;
kubectl cluster-info
&lt;span class="c1"&gt;# Check the resources created&lt;/span&gt;
kubectl get all --all-namespaces
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-27-wsl_docker_kubernetes_win10/30.png" /&gt;
&lt;/div&gt;
&lt;p&gt;集群成功创建了，并且&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Minikube&lt;/span&gt;&lt;/code&gt;使用的是wsl2的IP，这样以来，我们不需要新配置&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;proxy&lt;/span&gt;&lt;/code&gt;就可以直接访问dashboard了。&lt;/p&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-27-wsl_docker_kubernetes_win10/31.png" /&gt;
&lt;/div&gt;
&lt;p&gt;而WSL2集成的真正优势，&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;8443&lt;/span&gt;&lt;/code&gt;端口一旦在WSL2发行版上打开，它其实是转发到Windows上的，所以我们不需要代理这个IP地址，也可以通过localhost访问到Kubernetes的dashboard
URL。&lt;/p&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-27-wsl_docker_kubernetes_win10/32.png" /&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="dashboard"&gt;
&lt;h3&gt;6.4 访问dashboard&lt;/h3&gt;
&lt;p&gt;在命令行上工作总是好的，而且非常有见地。然而，当处理Kubernetes时，我们可能会在某些时候希望有一个可视化的概述。&lt;/p&gt;
&lt;p&gt;为此，Minikube嵌入了Kubernetes Dashboard。得益于它，运行和访问Dashboard非常简单。&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Enable the Dashboard service&lt;/span&gt;
sudo minikube dashboard
&lt;span class="c1"&gt;# Access the Dashboard from a browser on Windows side&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-27-wsl_docker_kubernetes_win10/33.png" /&gt;
&lt;/div&gt;
&lt;p&gt;该命令还创建了一个代理，这意味着一旦我们按CTRL+C键结束该命令，Dashboard将不再被访问。&lt;/p&gt;
&lt;p&gt;不过，如果我们查看命名空间kubernetes-dashboard，我们会发现服务仍然存在。&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Get all the services from the dashboard namespace&lt;/span&gt;
kubectl get all --namespace kubernetes-dashboard
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-27-wsl_docker_kubernetes_win10/34.png" /&gt;
&lt;/div&gt;
&lt;p&gt;接下来，我们更改一下&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;service&lt;/span&gt;&lt;/code&gt;的类型，让他以&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;LoadBalancer&lt;/span&gt;&lt;/code&gt;作为后端：&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Edit the Dashoard service&lt;/span&gt;
kubectl edit service/kubernetes-dashboard --namespace kubernetes-dashboard
&lt;span class="c1"&gt;# Go to the very end and remove the last 2 lines&lt;/span&gt;
status:
  loadBalancer: &lt;span class="o"&gt;{}&lt;/span&gt;
&lt;span class="c1"&gt;# Change the type from ClusterIO to LoadBalancer&lt;/span&gt;
  type: LoadBalancer
&lt;span class="c1"&gt;# Save the file&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-27-wsl_docker_kubernetes_win10/35.png" /&gt;
&lt;/div&gt;
&lt;p&gt;再次查看dashboard相关的&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;service&lt;/span&gt;&lt;/code&gt;，这次我们通过&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;LoadBalancer&lt;/span&gt;&lt;/code&gt;来访问：&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;# Get all the services from the dashboard namespace&lt;/span&gt;
kubectl get all --namespace kubernetes-dashboard
&lt;span class="c1"&gt;# Access the Dashboard from a browser on Windows side with the URL: localhost:&amp;lt;port exposed&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-27-wsl_docker_kubernetes_win10/36.png" /&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="id6"&gt;
&lt;h2&gt;7. 结论&lt;/h2&gt;
&lt;p&gt;很明显，我们还远远没有完成，因为我们可以实现一些LoadBalancing和/或其他服务（存储、入口、注册表等）。&lt;/p&gt;
&lt;p&gt;关于WSL2上的Minikube，由于它需要启用SystemD，我们可以把它看作是一个中间层来实现。&lt;/p&gt;
&lt;p&gt;那么，在两种解决方案中，什么才是 “最适合你”的呢？两者都有各自的优势和不便，所以这里仅从我们的角度来概述一下。&lt;/p&gt;
&lt;table class="docutils align-default"&gt;
&lt;colgroup&gt;
&lt;col style="width: 31%" /&gt;
&lt;col style="width: 50%" /&gt;
&lt;col style="width: 19%" /&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr class="row-odd"&gt;&lt;th class="head"&gt;&lt;p&gt;对比项&lt;/p&gt;&lt;/th&gt;
&lt;th class="head"&gt;&lt;p&gt;Kind&lt;/p&gt;&lt;/th&gt;
&lt;th class="head"&gt;&lt;p&gt;Minikube&lt;/p&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr class="row-even"&gt;&lt;td&gt;&lt;p&gt;与WSL2共同使用&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;非常简单&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;一般简单&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-odd"&gt;&lt;td&gt;&lt;p&gt;多节点&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;支持&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;不支持&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-even"&gt;&lt;td&gt;&lt;p&gt;插件&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;手动安装&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;自动安装&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-odd"&gt;&lt;td&gt;&lt;p&gt;持久化&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;支持(但这并不是它的本初设计)&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;支持&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr class="row-even"&gt;&lt;td&gt;&lt;p&gt;同类型的其它产品&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;k3s&lt;/p&gt;&lt;/td&gt;
&lt;td&gt;&lt;p&gt;MicroK8s&lt;/p&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;我们希望你能真正体验到不同组件之间的整合。WSL2 - Docker Desktop -KinD/Minikube。这给了你一些想法，甚至更好的是，给了你在Windows和WSL2上使用KinD和/或Minikube的Kubernetes工作流的一些答案。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id7"&gt;
&lt;h2&gt;8. 出处&lt;/h2&gt;
&lt;p&gt;&lt;a class="reference external" href="https://kubernetes.io/blog/2020/05/21/wsl-docker-kubernetes-on-the-windows-desktop/"&gt;WSL+Docker: Kubernetes on the Windows
Desktop&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
</content>
    <link href="double12gzhblogs.readthedocs.io/blogs/Kubernetes/2020-09-27-WSL-Docker-Kubernetes-on-the-Windows-Desktop.html" rel="alternate"/>
    <published>2020-09-27T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>double12gzhblogs.readthedocs.io/blogs/Kubernetes/2020-10-03-%E4%BD%BF%E7%94%A8gitops%E7%AE%A1%E7%90%86k8s-secret.html</id>
    <title>使用GitOps管理K8S Secret</title>
    <updated>2020-10-03T00:00:00+00:00</updated>
    <author>
      <name>大海星</name>
    </author>
    <content type="html">&lt;div class="section" id="gitopsk8s-secret"&gt;

&lt;p&gt;摘要: 介绍如何使用GitOps来管理Kubernetes Secret。&lt;/p&gt;
&lt;p&gt;&lt;img alt="image0" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-10-03-gitops-manage-k8s-secret.jpg" /&gt;&lt;/p&gt;
&lt;div class="section" id="id1"&gt;
&lt;h2&gt;1. 写在前面&lt;/h2&gt;
&lt;p&gt;GitOps是伴随着云原生产生的一个新的概念，它的核心是以一种&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;声明式的&lt;/span&gt;&lt;/code&gt;方式管理资源，表示当前的状态，让你在任何时候都能知道git中的情况，并将这种声明式的状态解析 为集群。&lt;/p&gt;
&lt;p&gt;我们在GitOps上犯的最大错误是&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;结构&lt;/span&gt;&lt;/code&gt;。仓库的结构至关重要，你选择如何在公司里组织GitOps，将决定它的成败。&lt;/p&gt;
&lt;p&gt;当你解决了结构问题之后，我们面临的下一个最大的问题就是&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;如何保证Secret的安全&lt;/span&gt;&lt;/code&gt;。一般来说，最后的结果是人们有不需要的访问权限，或者有一个共享的&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Secret&lt;/span&gt;&lt;/code&gt;，通过Slack或1Password传来传去。&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="https://github.com/bitnami-labs/sealed-secrets"&gt;Sealed
Secrets&lt;/a&gt;是Kubernetes上解决&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Secret&lt;/span&gt;&lt;/code&gt;的一种流行方式，它是一个不错的解决方案，依靠&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;PKI&lt;/span&gt;&lt;/code&gt;和&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;共享公钥&lt;/span&gt;&lt;/code&gt;来加密，并在集群上安装私钥。&lt;/p&gt;
&lt;p&gt;本文将尝试解释如何使用GitOps来管理k8s的&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Secret&lt;/span&gt;&lt;/code&gt;资源。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="secret"&gt;
&lt;h2&gt;2. 管理Secret比较好的方式&lt;/h2&gt;
&lt;p&gt;&lt;a class="reference external" href="https://github.com/mozilla/sops"&gt;Mozilla的SOPS&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;SOPS支持多种来源的密钥输入，包括AWS KMS、GCE、Vault、PGP等。它提供了使用AWS KMS的外部密钥作为输入来加密和解密秘密的能力，但它也支持&lt;cite&gt;Shamir的``Secret`&lt;/cite&gt; &amp;lt;&lt;a class="reference external" href="https://en.wikipedia.org/wiki/Shamir's_Secret_Sharing"&gt;https://en.wikipedia.org/wiki/Shamir’s_Secret_Sharing&lt;/a&gt;&amp;gt;`__共享和密钥组，本质上允 许对解密所需的密钥以及这些密钥的数量进行策略。&lt;/p&gt;
&lt;p&gt;由于SOPS让我们有能力使用AWS KMS这样的服务，我们可以控制谁有加密和解密的权限，但这也意味着我们可以利用AWS的实例配置文件，让机器做它们设计时最擅长的事情，通过自动化让我们的生活更轻松。&lt;/p&gt;
&lt;p&gt;最后一个我想提请大家注意的功能是，一旦文件被加密，所有关于如何加密的元数据都是文件本身的一部分，不需要第三方服务、数据库、隐藏目录或二级文件来知道如何解密文件。&lt;/p&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;Flux对SOPS有一流的支持。&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div class="section" id="id2"&gt;
&lt;h2&gt;3. 结构&lt;/h2&gt;
&lt;p&gt;一开始我就说过，正确架构你的仓库是至关重要的。这一点来源于个人在工程实践中总结的一些经验之谈，对于不同的场景，您可能需要权衡一下我这里的“最佳实践”。&lt;/p&gt;
&lt;p&gt;对于&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;结构化&lt;/span&gt;&lt;/code&gt;化来说，通常可以简单的分成两个部分：&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;仓库中内容的布局&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;使用仓库的数量&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在我的项目中，我通常会使用三个库： - 定义集群运行所需的每一个基础资源 -
处理工程团队负责的产品的所有发布 - 给&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Secret&lt;/span&gt;&lt;/code&gt;使用&lt;/p&gt;
&lt;p&gt;项目中我只使用了SOPS与专用的&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Secret&lt;/span&gt;&lt;/code&gt;库，使用Flux和SOPS使我们获得了这种能力，具有前所未有的灵活性。&lt;/p&gt;
&lt;div class="section" id="id3"&gt;
&lt;h3&gt;3.1 目录结构&lt;/h3&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;-- .sops.yaml
&lt;span class="p"&gt;|&lt;/span&gt;-- secrets
    &lt;span class="p"&gt;|&lt;/span&gt;-- dev &lt;span class="o"&gt;(&lt;/span&gt;environment/cluster&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;|&lt;/span&gt;-- database.yaml
    &lt;span class="p"&gt;|&lt;/span&gt;-- stg &lt;span class="o"&gt;(&lt;/span&gt;environment/cluster&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;|&lt;/span&gt;-- database.yaml
    &lt;span class="p"&gt;|&lt;/span&gt;-- prd &lt;span class="o"&gt;(&lt;/span&gt;environment/cluster&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;|&lt;/span&gt;-- database.yaml
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;这个文件布局为我们提供了使用SOPS创建规则来控制文件如何加密的基础。我们可以使用不同的密钥、密钥组和阈值。它还允许我们告诉Flux在克隆和解析集群上需要的东西时只关心一个特定的路径。下面是一个例子(注意：这不是一个完整可运行的例子)。&lt;/p&gt;
&lt;div class="highlight-yaml notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nt"&gt;creation_rules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="nt"&gt;path_regex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;secrets/dev/.*&lt;/span&gt;
  &lt;span class="nt"&gt;encrypted_regex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;^(data|stringData)$&amp;quot;&lt;/span&gt;
  &lt;span class="nt"&gt;kms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nt"&gt;arn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;kms-arn&lt;/span&gt;
    &lt;span class="nt"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;ksm-role&lt;/span&gt;
&lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="nt"&gt;path_regex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;secrets/stg/.*&lt;/span&gt;
  &lt;span class="nt"&gt;encrypted_regex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;^(data|stringData)$&amp;quot;&lt;/span&gt;
  &lt;span class="nt"&gt;kms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nt"&gt;arn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;kms-arn&lt;/span&gt;
    &lt;span class="nt"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;ksm-role&lt;/span&gt;
&lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="nt"&gt;path_regex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;secrets/prd/.*&lt;/span&gt;
  &lt;span class="nt"&gt;encrypted_regex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;^(data|stringData)$&amp;quot;&lt;/span&gt;
  &lt;span class="nt"&gt;kms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nt"&gt;arn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;kms-arn&lt;/span&gt;
    &lt;span class="nt"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;ksm-role&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="scaling"&gt;
&lt;h3&gt;3.2 Scaling&lt;/h3&gt;
&lt;p&gt;使用这个设置，我们几乎可以无限地扩展这个设置。它只受存储在版本库中的数据量和服务于它的git服务器的限制。秘密一般不会经常变化，但它们会被轮换，这就是像&lt;a class="reference external" href="https://github.com/stakater/Reloader"&gt;Reloader&lt;/a&gt;这样的额外工具的作用(这里不会作详细介绍)。&lt;/p&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;Reloader:&lt;/p&gt;
&lt;p&gt;它可以用于监视ConfigMap和Secret是否发生变化，如果发生了变化，它将触发与之相关的以下资源实现滚动更新：&lt;/p&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;DeploymentConfig&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Deployment&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Daemonset&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Statefulset&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="sops"&gt;
&lt;h2&gt;4. 设置SOPS&lt;/h2&gt;
&lt;p&gt;前面我们已经讲解了SOPS的目录结构和设计原则，下面我们通过一个小例子实践一下。&lt;/p&gt;
&lt;p&gt;有多种方法可以利用SOPS，但我只介绍其中一种：使用AWS KMS与多个键和AWS Instance Profile(Flux可以使用)。&lt;/p&gt;
&lt;div class="section" id="aws"&gt;
&lt;h3&gt;4.1 设置AWS&lt;/h3&gt;
&lt;p&gt;在真正的使用场景中，我会建议使用多个AWS账户，并假设角色权限，但对于这样的文章来说，这可能会变得不必要的复杂，所以我们要假设一个单一的AWS账户，但仍然利用使用3个密 钥。&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;aws kms create-key
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;一定要把ARN收集起来，以便以后使用。&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;接下来我们需要创建我们的&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;.sops.yaml&lt;/span&gt;&lt;/code&gt;文件与创建规则。在真正的使用场景中，你会希望你的阈值是2或更多，并且让你的密钥至少比阈值高N+1。
让我们从创建一个基础目录开始，然后创建&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;.sops.yaml&lt;/span&gt;&lt;/code&gt;文件，内容如下，确保用你的有效ARNs交换ARNs。&lt;/p&gt;
&lt;div class="highlight-yaml notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nt"&gt;creation_rules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="nt"&gt;path_regex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;secrets/dev/.*&lt;/span&gt;
  &lt;span class="nt"&gt;encrypted_regex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;^(data|stringData)$&amp;quot;&lt;/span&gt;
  &lt;span class="nt"&gt;shamir_threshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;2&lt;/span&gt;
  &lt;span class="nt"&gt;key_groups&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="nt"&gt;kms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="nt"&gt;arn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;arn:aws:kms:us-west-2:000000000000:key/b5d44bf0-7ec5-49a9-b404-bc4d8b4036fb&lt;/span&gt;
    &lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="nt"&gt;kms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="nt"&gt;arn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;arn:aws:kms:us-west-2:000000000000:key/16d44186-2393-40d9-90e1-9a2f92fd5863&lt;/span&gt;
    &lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="nt"&gt;kms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="nt"&gt;arn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;arn:aws:kms:us-west-2:000000000000:key/2120d2c1-a89e-4aeb-844f-f17ae2abd210&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;这个创建规则规定，secrets/dev目录下的所有文件都要用3个不同的密钥进行加密，解密阈值为2。&lt;/p&gt;
&lt;p&gt;接下来创建你的secrets/dev目录，并创建一个example.yaml文件，内容如下：&lt;/p&gt;
&lt;div class="highlight-yaml notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;v1&lt;/span&gt;
&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;Secret&lt;/span&gt;
&lt;span class="nt"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;example&lt;/span&gt;
&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;Opaque&lt;/span&gt;
&lt;span class="nt"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="nt"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;gzhGZHSBWUDESIN=&lt;/span&gt;
  &lt;span class="nt"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;SBALIBSBWUDESIN=&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;使用如下命令结上面的yaml文件进行加密：&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;sops -e -i secrets/dev/example.yaml
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;说明:&lt;/p&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;-e: 加密&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;-i: 支持原地修改(如果您不想修改原文件并且想看加密后的输入内容，请忽略此参数)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;上面的命令执行完成后您就可以提交到仓库中了。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id4"&gt;
&lt;h3&gt;4.2 AWS实例配置文件&lt;/h3&gt;
&lt;p&gt;这个系统的好处是，&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Flux&lt;/span&gt;&lt;/code&gt;可以简单地利用AWS实例配置文件，通过STS承担角色，并获得临时的密钥，以便在集群本身运行时能够解密。
要实现这一点，您需要设置一个角色，该角色具有&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Encrypt/Decrypt&lt;/span&gt;&lt;/code&gt;和可能的&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Generate&lt;/span&gt;&lt;/code&gt;权限，具体取决于您使用的密钥类型，并设置您的AWS实例以使用配置文件。&lt;/p&gt;
&lt;p&gt;一旦您完成了这些设置，只需使用正确的 CLI 选项将 Flux 安装到群集上，Flux就会让您的群集保持最新状态。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="flux"&gt;
&lt;h3&gt;4.3 设置Flux&lt;/h3&gt;
&lt;p&gt;你只需要做几件事情来确保&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Flux&lt;/span&gt;&lt;/code&gt;的正常运行，你需要启用&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;SOPS&lt;/span&gt;&lt;/code&gt;，这取决于你的部署方法，可能是&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;配置选项&lt;/span&gt;&lt;/code&gt;或CLI标志&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;--sops&lt;/span&gt;&lt;/code&gt;。你需要启用
&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;SOPS&lt;/span&gt;&lt;/code&gt;，根据你的部署方法，可能是一个&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;配置选项&lt;/span&gt;&lt;/code&gt;或 CLI 标志``–sops``。你还需要指示你的 &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Flux``实例只关心默认分支中的一个特定路径。\&lt;/span&gt; &lt;span class="pre"&gt;``--git-path&lt;/span&gt;&lt;/code&gt; 需要设置为
&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Secrets&lt;/span&gt;&lt;/code&gt; 下的一个目录，比如 &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;--git-path=secrets/dev&lt;/span&gt;&lt;/code&gt;。&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="id5"&gt;
&lt;h2&gt;5. 小结&lt;/h2&gt;
&lt;p&gt;使用这种方法可以让你更好地控制&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Secret&lt;/span&gt;&lt;/code&gt;及谁有访问权，另外，它也允许您让AWS处理提供&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Secret&lt;/span&gt;&lt;/code&gt;中比较难搞的部分，以通过自动化和短暂的&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Secret&lt;/span&gt;&lt;/code&gt;来访问&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Secret&lt;/span&gt;&lt;/code&gt;解密，您可以将所有的&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Secret&lt;/span&gt;&lt;/code&gt;保存在一个地方，以便于管理和轮换。&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
</content>
    <link href="double12gzhblogs.readthedocs.io/blogs/Kubernetes/2020-10-03-%E4%BD%BF%E7%94%A8gitops%E7%AE%A1%E7%90%86k8s-secret.html" rel="alternate"/>
    <published>2020-10-03T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>double12gzhblogs.readthedocs.io/blogs/Kubernetes/2020-10-11-client-go%E7%B3%BB%E5%88%97%E4%B9%8B1---client-go%E4%BB%A3%E7%A0%81%E8%AF%A6%E8%A7%A3.html</id>
    <title>client-go系列之1—client-go代码详解</title>
    <updated>2020-10-11T00:00:00+00:00</updated>
    <author>
      <name>大海星</name>
    </author>
    <content type="html">&lt;div class="section" id="client-go1-client-go"&gt;

&lt;p&gt;摘要：介绍golang-client的代码结构&lt;/p&gt;
&lt;div class="section" id="id1"&gt;
&lt;h2&gt;1. 写在前面&lt;/h2&gt;
&lt;div class="highlight-default notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;个人主页: https://gzh.readthedocs.io

关注容器技术、关注Kubernetes。
问题或建议，请公众号留言。
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;本系列内容都是基于这个版本的&lt;a class="reference external" href="https://github.com/kubernetes/client-go/tree/becbabb360023e1825a48b4db85f454e452ae249"&gt;client-go&lt;/a&gt;进行讲解，不同版本的略有差异。&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;root@77DDE94FF07FCC1-wsl /ACode/client-go&lt;span class="o"&gt;]&lt;/span&gt; git rev-parse HEAD
becbabb360023e1825a48b4db85f454e452ae249
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;下面简单罗列一下本文中可能会用提到的一些常用缩写，详细的介绍请参考后面的文章(TODO)&lt;a class="reference external" href="https://double12gzh.github.io/2020/10/11/client-go%E7%B3%BB%E5%88%97%E4%B9%8B1-client-go%E4%BB%A3%E7%A0%81%E7%BB%93%E6%9E%84%E8%AE%B2%E8%A7%A3-copy/"&gt;谈起k8s中的GVR我们实际在讲什么&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;&lt;img alt="image0" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-10-11-client-go/3-abbr.png" /&gt;&lt;/p&gt;
&lt;div class="highlight-default notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;说明：

看一下k8s的资源模型：

apiVersion: extensions/v1beta1
kind: ReplicaSet

对于Resource我们可以将之类比于编程语言中的“包”的概念，主要用于区分不同的API，
这样可以有效避免Kinds重名的问题。通常会使用公司的域名等具有独特明显区分的字符
串作为Resource的值，如：alibaba-inc.com
Version用于区分不同API的稳定程度及兼容性，如：v1beta1, v1

Kind即为API所对应的名字，如：Deployment, Service
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="image1" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-10-11-client-go/1-group-version-resource.png" /&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id2"&gt;
&lt;h2&gt;2. 代码结构&lt;/h2&gt;
&lt;p&gt;从这个包的名字可以很明显的知道，&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;client-go&lt;/span&gt;&lt;/code&gt;其实主要是提供了用户与k8s交互时使用客户端，方便大家编程。&lt;/p&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;个别目录已过滤掉。&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;root@77DDE94FF07FCC1-wsl /ACode/client-go&lt;span class="o"&gt;]&lt;/span&gt; tree -d -L &lt;span class="m"&gt;1&lt;/span&gt; -I &lt;span class="s2"&gt;&amp;quot;testing|examples|*_test*|Godeps|third_party|metadata|deprecated|restmapper&amp;quot;&lt;/span&gt;
.
├── discovery                   &lt;span class="c1"&gt;# 定义DsicoveryClient客户端。作用是用于发现k8s所支持GVR(Group, Version, Resources)。&lt;/span&gt;
├── dynamic                     &lt;span class="c1"&gt;# 定义DynamicClient客户端。可以用于访问k8s Resources(如: Pod, Deploy...)，也可以访问用户自定义资源(即: CRD)。&lt;/span&gt;
├── informers                   &lt;span class="c1"&gt;# k8s中各种Resources的Informer机制的实现。&lt;/span&gt;
├── kubernetes                  &lt;span class="c1"&gt;# 定义ClientSet客户端。它只能用于访问k8s Resources。每一种资源(如: Pod等)都可以看成是一个客端，而ClientSet是多个客户端的集合，它对RestClient进行了封装，引入了对Resources和Version的管&lt;/span&gt;
&lt;span class="p"&gt;|&lt;/span&gt;                               &lt;span class="c1"&gt;# 理。通常来说ClientSet是client-gen来自动生成的。&lt;/span&gt;
├── listers                     &lt;span class="c1"&gt;# 提供对Resources的获取功能。对于Get()和List()而言，listers提供给二者的数据都是从缓存中读取的。&lt;/span&gt;
├── pkg
├── plugin                      &lt;span class="c1"&gt;# 提供第三方插件。如：GCP, OpenStack等。&lt;/span&gt;
├── rest                        &lt;span class="c1"&gt;# 定义RestClient，实现了Restful的API。同时会支持Protobuf和Json格式数据。&lt;/span&gt;
├── scale                       &lt;span class="c1"&gt;# 定义ScalClient。用于Deploy, RS, RC等的扩/缩容。&lt;/span&gt;
├── tools                       &lt;span class="c1"&gt;# 定义诸如SharedInformer、Reflector、DealtFIFO和Indexer等常用工具。实现client查询和缓存机制，减少client与api-server请求次数，减少api-server的压力。&lt;/span&gt;
├── transport
└── util                        &lt;span class="c1"&gt;# 提供诸如WorkQueue、Certificate等常用方法。&lt;/span&gt;

&lt;span class="m"&gt;12&lt;/span&gt; directories
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="id3"&gt;
&lt;h2&gt;3. 代码使用简单示例&lt;/h2&gt;
&lt;p&gt;在对每一部分进行讲解前，先用一个图来讲解各部分之间的关系：&lt;/p&gt;
&lt;p&gt;&lt;img alt="image2" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-10-11-client-go/2-details.png" /&gt;&lt;/p&gt;
&lt;p&gt;对于图中的每一个带有标号的部分，下面给出简单的代码使用展示，
如果暂时不明白下面的代码可以先进行下一章节的学习。&lt;/p&gt;
&lt;div class="section" id="kubeconfigcontext"&gt;
&lt;h3&gt;3.1 获取kubeconfig及context&lt;/h3&gt;
&lt;p&gt;这一部分对应于序号1—tools/clientcmd。&lt;/p&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;kubeconfig&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;

        &lt;span class="c1"&gt;// 默认会从~/.kube/config路径下获取配置文件&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;home&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;homeDir&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="nx"&gt;home&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;kubeconfig&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;flag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;kubeconfig&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filepath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;home&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;.kube&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;config&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;(optional)absolute path to the kubeconfig file&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;kubeconfig&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;flag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;kubeconfig&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;absolute path to the kubeconfig file&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nx"&gt;flag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Parse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="c1"&gt;// 使用k8s.io/client-go/tools/clientcmd生成config的对象&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;clientcmd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BuildConfigFromFlags&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;kubeconfig&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="clientset"&gt;
&lt;h3&gt;3.2 创建ClientSet&lt;/h3&gt;
&lt;p&gt;这一部分对应于序号2—ClientSet。&lt;/p&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;// 使用k8s.io/client-go/kubernetes生成一个ClientSet的客户端，客户端生成后，就可以使用这个客户端与k8s API server进行交互了，如获取资源列表、Create/Update/Delete资源等&lt;/span&gt;
&lt;span class="nx"&gt;clientset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;kubenetes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NewForConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="clientsetpods"&gt;
&lt;h3&gt;3.3 使用ClientSet获取集群中的pods&lt;/h3&gt;
&lt;p&gt;这一部分对应于序号2/3/4—RestClient。&lt;/p&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// 使用ClientSet客户端获取集群中所有的Pods。其中：ListOptions的结构如下：&lt;/span&gt;
    &lt;span class="c1"&gt;// type ListOptions struct {&lt;/span&gt;
    &lt;span class="c1"&gt;//      TypeMeta `json:&amp;quot;,inline&amp;quot;`&lt;/span&gt;
    &lt;span class="c1"&gt;//      LabelSelector string `json:&amp;quot;labelSelector,omitempty&amp;quot;`&lt;/span&gt;
    &lt;span class="c1"&gt;//      FieldSelector string `json:&amp;quot;fieldSelector,omitempty&amp;quot;`&lt;/span&gt;
    &lt;span class="c1"&gt;//}&lt;/span&gt;
    &lt;span class="nx"&gt;pods&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;clientset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CoreV1&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;Pods&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;List&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metav1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ListOptions&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Number of pods are: %d\n&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pods&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Items&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="clientsetpod"&gt;
&lt;h3&gt;3.4 使用ClientSet获取指定的pod&lt;/h3&gt;
&lt;p&gt;这一部分对应于序号2/3/4—tools/clientcmd。&lt;/p&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// 在这里我们从default这个namespace中获取了名为my-pod的Pod对象&lt;/span&gt;
    &lt;span class="nx"&gt;pod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;clientset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CoreV1&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;Pods&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;default&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;my-pod&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;metav1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GetOptions&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;painc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;%v\n\n\n\n&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="clients"&gt;
&lt;h2&gt;4. 各种Clients详解&lt;/h2&gt;
&lt;p&gt;client-go中定义的比较重要的client有：&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;RestClient&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;ClientSet
(&lt;a class="reference external" href="https://github.com/kubernetes/client-go/tree/becbabb360023e1825a48b4db85f454e452ae249/examples/create-update-delete-deployment"&gt;用法示例&lt;/a&gt;)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;DiscoveryClient&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;DynamicClient
(&lt;a class="reference external" href="https://github.com/kubernetes/client-go/tree/becbabb360023e1825a48b4db85f454e452ae249/examples/dynamic-create-update-delete-deployment"&gt;用法示例&lt;/a&gt;)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;其中，RestClient是所有客户端的基础，后三者都是对RestClient的封装。RestClient它通过kubeconfig与k8s-api-server进行交互。详细结构如下图：&lt;/p&gt;
&lt;p&gt;&lt;img alt="image3" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-10-11-client-go/0-client-go-arch.png" /&gt;&lt;/p&gt;
&lt;p&gt;ClientSets使用&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;预生成的API对象&lt;/span&gt;&lt;/code&gt;,
这样的好处是当本地的API对象与k8s-api-server进行交互时会变得比较方便，方便的同时，随之也带来了版本与类型强耦合的问题。&lt;/p&gt;
&lt;p&gt;DynamicClient则使用&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;unstructured.Unstructured&lt;/span&gt;&lt;/code&gt;表示来自API
Server的所有对象值。&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Unstructured&lt;/span&gt;&lt;/code&gt;类型是一个嵌套的&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;map[string]inferface{}&lt;/span&gt;&lt;/code&gt;值的集合来创建一个内部结构，这一点类似于RESTful
API中的Json数据，这样可以解决ClientSet中出现的强耦合的问题，换句话说，当客户端的API发生变化时，DynamicClient无需重新编译。DynamicClient使所有数据实现延时绑定，即只有到运行时才会实现绑定，这意味着程序运行之前，使用&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;DynamicClient&lt;/span&gt;&lt;/code&gt;的程序将不会对对象进行Validation，这也是本client的一个缺点。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id4"&gt;
&lt;h2&gt;5. 其它组件&lt;/h2&gt;
&lt;p&gt;client-go中除了上面提到比较重要的客户端外,
本库还包含了各种机制(&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;tools/cache&lt;/span&gt;&lt;/code&gt;)。&lt;/p&gt;
&lt;p&gt;下图比较直观的展示了client-go与customer
controller及client-go各组件之间的交互关系，是我们在开发自定义控制器时经常需要使用的机制，了解这个图有助于我们更好的理解client-go及controller背后的实现逻辑。&lt;/p&gt;
&lt;p&gt;&lt;img alt="image4" src="https://raw.githubusercontent.com/kubernetes/sample-controller/master/docs/images/client-go-controller-interaction.jpeg" /&gt;&lt;/p&gt;
&lt;p&gt;如果您对client-go之前就比较了解，建议您移步&lt;a class="reference external" href="https://github.com/kubernetes/sample-controller"&gt;sample-controller&lt;/a&gt;看一下控制器实现的具体代码。&lt;/p&gt;
&lt;div class="section" id="reflector"&gt;
&lt;h3&gt;5.1 Reflector&lt;/h3&gt;
&lt;p&gt;refelector是定义在包缓存里面的&lt;a class="reference external" href="https://github.com/kubernetes/client-go/blob/becbabb360023e1825a48b4db85f454e452ae249/tools/cache/reflector.go#L49"&gt;Reflector&lt;/a&gt;结构体，可以用于监视指定资源类型（kind）的Kubernetes
API。&lt;/p&gt;
&lt;p&gt;实现这个功能的函数是&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;ListAndWatch&lt;/span&gt;&lt;/code&gt;。监视的对象可以是一个内置的资源，也可以是一个自定义的资源(CRD)。&lt;/p&gt;
&lt;p&gt;当reflector通过watch
API接收到关于新资源实例存在的通知时，它会使用相应的listing
API获取新创建的对象，并将其放在&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;watchHandler&lt;/span&gt;&lt;/code&gt;函数里面的&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;DeltaFIFO&lt;/span&gt;&lt;/code&gt;队列中。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="informer"&gt;
&lt;h3&gt;5.2 Informer&lt;/h3&gt;
&lt;p&gt;它是定义在包缓存中的一个基础控制器，它可以w使用函数&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;processLoop&lt;/span&gt;&lt;/code&gt;从&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;DeltaFIFO&lt;/span&gt;&lt;/code&gt;队列中取出对象。&lt;/p&gt;
&lt;p&gt;这个基础控制器的工作是保存对象以便以后检索，并调用我们的控制器将对象传递给它。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="indexer"&gt;
&lt;h3&gt;5.3 Indexer&lt;/h3&gt;
&lt;p&gt;提供对对象的索引功能。它被定义在&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;tools/cache&lt;/span&gt;&lt;/code&gt;包中的&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Indexer&lt;/span&gt;&lt;/code&gt;类型中。&lt;/p&gt;
&lt;p&gt;一个典型的索引用例是基于对象标签创建一个索引。Indexer可以基于几个索引函数来维护索引。Indexer使用一个线程安全的数据存储来存储对象和它们的键值。&lt;/p&gt;
&lt;p&gt;在&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;tools/cache&lt;/span&gt;&lt;/code&gt;内的&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Store&lt;/span&gt;&lt;/code&gt;类型中定义了一个名为&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;MetaNamespaceKeyFunc&lt;/span&gt;&lt;/code&gt;的默认函数，该函数为该对象生成一个对象的键，作为&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;&amp;lt;namespace&amp;gt;/&amp;lt;name&amp;gt;&lt;/span&gt;&lt;/code&gt;组合。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="workqueue"&gt;
&lt;h3&gt;5.4 WorkQueue&lt;/h3&gt;
&lt;p&gt;这是在控制器代码中创建的队列，用于将对象的分发与处理解耦。编写
&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Resource&lt;/span&gt; &lt;span class="pre"&gt;Event&lt;/span&gt; &lt;span class="pre"&gt;Handler&lt;/span&gt;&lt;/code&gt;
函数来提取所分发对象的键值并将其添加到工作队列中。&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;p&gt;欢迎关注我的微信公众号：&lt;/p&gt;
&lt;p&gt;&lt;img alt="image5" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/wechat_public.jpg" /&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
</content>
    <link href="double12gzhblogs.readthedocs.io/blogs/Kubernetes/2020-10-11-client-go%E7%B3%BB%E5%88%97%E4%B9%8B1---client-go%E4%BB%A3%E7%A0%81%E8%AF%A6%E8%A7%A3.html" rel="alternate"/>
    <published>2020-10-11T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>double12gzhblogs.readthedocs.io/blogs/Kubernetes/2020-10-13-client-go%E7%B3%BB%E5%88%97%E4%B9%8B2---%E7%AE%A1%E7%90%86kubeconfig.html</id>
    <title>client-go系列之2—管理kubeconfig</title>
    <updated>2020-10-13T00:00:00+00:00</updated>
    <author>
      <name>JeffreyGuan</name>
    </author>
    <content type="html">&lt;div class="section" id="client-go2-kubeconfig"&gt;

&lt;p&gt;摘要：分析如何通过client-go来管理kubeconfig。&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-10-13-client-go-kubeconfig/0.png" /&gt;&lt;/p&gt;
&lt;div class="section" id="id1"&gt;
&lt;h2&gt;1. 写在前面&lt;/h2&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;个人主页: https://gzh.readthedocs.io&lt;/p&gt;
&lt;p&gt;关注容器技术、关注&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Kubernetes&lt;/span&gt;&lt;/code&gt;。问题或建议，请公众号留言。&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;本系列内容都是基于这个版本的&lt;a class="reference external" href="https://github.com/kubernetes/client-go/tree/becbabb360023e1825a48b4db85f454e452ae249"&gt;client-go&lt;/a&gt;进行讲解，不同版本的略有差异。&lt;/p&gt;
&lt;p&gt;在使用client-go开发时，通常会遇到两种情况：&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;在集群内部访问kubernetes资源&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;在集群外部访问kubernetes资源&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;关于这两种方式的区别，可以分别看一下下面这段代码直观感受一下：&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://github.com/kubernetes/client-go/tree/becbabb360023e1825a48b4db85f454e452ae249/examples/in-cluster-client-configuration"&gt;in-cluster-client-configuration&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://github.com/kubernetes/client-go/tree/becbabb360023e1825a48b4db85f454e452ae249/examples/out-of-cluster-client-configuration"&gt;out-of-cluster-client-configuration&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="id2"&gt;
&lt;h2&gt;2. 集群配置管理&lt;/h2&gt;
&lt;p&gt;k8s的配置文件默认会存放在~/.kube/config中，其内容如下：&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: &lt;span class="nv"&gt;LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0xxxxxxxxCg&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;
    server: https://127.0.0.1:33629
  name: kind-kind
contexts:
- context:
    cluster: kind-kind
    user: kind-kind
  name: kind-kind
current-context: kind-kind
kind: Config
preferences: &lt;span class="o"&gt;{}&lt;/span&gt;
users:
- name: kind-kind
  user:
    client-certificate-data: &lt;span class="nv"&gt;LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1Jxxxxxxx&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;
    client-key-data: &lt;span class="nv"&gt;LS0tLS1yyyyyyyCg&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;上面这是单个配置文件情况，client-go提供了合并多个配置文件的能力，合并完成后如下：&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;apiVersion: v1

clusters:
- cluster:
    certificate-authority-data: &lt;span class="nv"&gt;LS0tLS1CRUdJTiBDRVJUSUZJQtCg&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;
    server: https://127.0.0.1:51107
  name: kind-guan
- cluster:
    certificate-authority-data: &lt;span class="nv"&gt;LS0tLS1CRUdJTiBDRVJtCg&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;
    server: https://127.0.0.1:33629
  name: kind-kind

contexts:
- context:
    cluster: kind-guan
    user: kind-guan
  name: kind-guan
- context:
    cluster: kind-kind
    user: kind-kind
  name: kind-kind


current-context: kind-kind

kind: Config

preferences: &lt;span class="o"&gt;{}&lt;/span&gt;

users:
- name: kind-guan
  user:
    client-certificate-data: &lt;span class="nv"&gt;LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUUtLS0tLQo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
    client-key-data: &lt;span class="nv"&gt;LS0tLS1CRUdJTiBxxxxxS0tCg&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;
- name: kind-kind
  user:
    client-certificate-data: &lt;span class="nv"&gt;LS0tLS1CRUdJTiBDRVJxxxxxxEUtLS0tLQo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
    client-key-data: &lt;span class="nv"&gt;LS0tLS1CRUdJTiBSU0EgUFJJVkFURSxxxxg&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;client-go中对于配置文件的管理可以简单概括为以下两点：&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;加载配置文件&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;合并配置文件&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;接下来分别从代码层面看一下。&lt;/p&gt;
&lt;div class="section" id="id3"&gt;
&lt;h3&gt;2.1 加载配置文件&lt;/h3&gt;
&lt;p&gt;加载配置文件的代码如下：&lt;/p&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;kubeconfig&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;

        &lt;span class="c1"&gt;// 默认会从~/.kube/config路径下获取配置文件&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;home&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;homeDir&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="nx"&gt;home&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;kubeconfig&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;flag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;kubeconfig&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filepath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;home&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;.kube&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;config&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;(optional)absolute path to the kubeconfig file&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;kubeconfig&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;flag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;kubeconfig&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;absolute path to the kubeconfig file&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nx"&gt;flag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Parse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="c1"&gt;// 使用k8s.io/client-go/tools/clientcmd加载配置文件并生成config的对象&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;clientcmd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BuildConfigFromFlags&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;kubeconfig&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;clientcmd.BuildConfigFromFlags&lt;/span&gt;&lt;/code&gt;加载配置文件后, 会生成一个&lt;a class="reference external" href="https://github.com/kubernetes/client-go/blob/becbabb360023e1825a48b4db85f454e452ae249/rest/config.go#L53"&gt;Config&lt;/a&gt;的对象，这个对象中会包含如：apiserver地址、用户名、密码、token等信息。&lt;/p&gt;
&lt;p&gt;进入到&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;clientcmd.BuildConfigFromFlags&lt;/span&gt;&lt;/code&gt;查看其进行配置加载相关的代码：&lt;/p&gt;
&lt;p&gt;代码位置：&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;tools/clientcmd/client_config.go&lt;/span&gt;&lt;/code&gt;
&lt;img alt="tools/clientcmd/client_config.go" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-10-13-client-go-kubeconfig/1.png" /&gt;&lt;/p&gt;
&lt;p&gt;生成一个结构体：&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;ClientConfigLoadingRules&lt;/span&gt;&lt;/code&gt;，这里面记录了当前传入的kubeconfig文件的位置。另外，在这个结构体中有一个变量&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;load&lt;/span&gt; &lt;span class="pre"&gt;ClientConfigLoader&lt;/span&gt;&lt;/code&gt;，对于kubeconfig文件的管理，就是通过其中定义的&lt;a class="reference external" href="https://github.com/kubernetes/client-go/blob/becbabb360023e1825a48b4db85f454e452ae249/tools/clientcmd/loader.go#L76"&gt;ClientConfigLoadingRules.Load()&lt;/a&gt;实现的。&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-10-13-client-go-kubeconfig/2.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-10-13-client-go-kubeconfig/3.png" /&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id4"&gt;
&lt;h3&gt;2.2 合并配置文件&lt;/h3&gt;
&lt;p&gt;上面对kubeconfig完成加载后，下一步就需要对kubeconfig做合并处理。&lt;/p&gt;
&lt;p&gt;代码位置: &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;tools/clientcmd/loader.go&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-10-13-client-go-kubeconfig/4.png" /&gt;&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;p&gt;欢迎关注我的微信公众号：&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/wechat_public.jpg" /&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
</content>
    <link href="double12gzhblogs.readthedocs.io/blogs/Kubernetes/2020-10-13-client-go%E7%B3%BB%E5%88%97%E4%B9%8B2---%E7%AE%A1%E7%90%86kubeconfig.html" rel="alternate"/>
    <published>2020-10-13T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>double12gzhblogs.readthedocs.io/blogs/Kubernetes/2020-10-14-client-go%E7%B3%BB%E5%88%97%E4%B9%8B3---restclient%E7%9A%84%E4%BD%BF%E7%94%A8.html</id>
    <title>client-go系列之3—restclient的使用</title>
    <updated>2020-10-14T00:00:00+00:00</updated>
    <author>
      <name>JeffreyGuan</name>
    </author>
    <content type="html">&lt;div class="section" id="client-go3-restclient"&gt;

&lt;p&gt;摘要：介绍如何使用client-go中的restclient，可以结合“系列之1”来看。&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-10-13-client-go-kubeconfig/0.png" /&gt;&lt;/p&gt;
&lt;div class="section" id="id1"&gt;
&lt;h2&gt;0. 背景&lt;/h2&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;个人主页: https://gzh.readthedocs.io&lt;/p&gt;
&lt;p&gt;关注容器技术、关注&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Kubernetes&lt;/span&gt;&lt;/code&gt;。问题或建议，请公众号留言。&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;首先我通过&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;kind&lt;/span&gt;&lt;/code&gt;创建了一个6节点的集群，本文章中所有的操作都是在这个集群中进行的。&lt;/p&gt;
&lt;p&gt;通过本文的讲解，希望您能了解如何使用client-go中的RESTClient来对资源进行操作，这里我只是举了最简单的例子—pod资源获取。&lt;/p&gt;
&lt;p&gt;文中用到的软件的版本如下：&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;kind&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;root@xxx-wsl ~/client-go-example&lt;span class="o"&gt;]&lt;/span&gt; kind version
kind v0.9.0 go1.15.2 linux/amd64
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="id2"&gt;
&lt;h2&gt;1. 环境准备&lt;/h2&gt;
&lt;p&gt;通过kind创建多节点的k8s集群: 3个master节点 + 3个worker节点&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;root@xxx-wsl ~/init_kind_clusters&lt;span class="o"&gt;]&lt;/span&gt; kind create cluster --config&lt;span class="o"&gt;=&lt;/span&gt;init_cluster.yaml
Creating cluster &lt;span class="s2"&gt;&amp;quot;kind&amp;quot;&lt;/span&gt; ...
 ✓ Ensuring node image &lt;span class="o"&gt;(&lt;/span&gt;kindest/node:v1.19.1&lt;span class="o"&gt;)&lt;/span&gt; 🖼
 ✓ Preparing nodes 📦 📦 📦 📦 📦 📦
 ✓ Configuring the external load balancer ⚖️
 ✓ Writing configuration 📜
 ✓ Starting control-plane 🕹️
 ✓ Installing CNI 🔌
 ✓ Installing StorageClass 💾
 ✓ Joining more control-plane nodes 🎮
 ✓ Joining worker nodes 🚜
Set kubectl context to &lt;span class="s2"&gt;&amp;quot;kind-kind&amp;quot;&lt;/span&gt;
You can now use your cluster with:

kubectl cluster-info --context kind-kind

Not sure what to &lt;span class="k"&gt;do&lt;/span&gt; next? 😅  Check out https://kind.sigs.k8s.io/docs/user/quick-start/
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;其中&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;init_cluster.yaml&lt;/span&gt;&lt;/code&gt;内容如下：&lt;/p&gt;
&lt;div class="highlight-yaml notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="p p-Indicator"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;root@xxx-wsl ~/init_kind_clusters&lt;/span&gt;&lt;span class="p p-Indicator"&gt;]&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;cat init_cluster.yaml&lt;/span&gt;
&lt;span class="l l-Scalar l-Scalar-Plain"&gt;# a cluster with 3 control-plane nodes and 3 workers&lt;/span&gt;
&lt;span class="l l-Scalar l-Scalar-Plain"&gt;kind&lt;/span&gt;&lt;span class="p p-Indicator"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;Cluster&lt;/span&gt;
&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;kind.x-k8s.io/v1alpha4&lt;/span&gt;
&lt;span class="nt"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="nt"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;control-plane&lt;/span&gt;
&lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="nt"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;control-plane&lt;/span&gt;
&lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="nt"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;control-plane&lt;/span&gt;
&lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="nt"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;worker&lt;/span&gt;
&lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="nt"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;worker&lt;/span&gt;
&lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="nt"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;worker&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="restclient"&gt;
&lt;h2&gt;2. RestClient使用示例&lt;/h2&gt;
&lt;p&gt;这段代码的作用：从namespace为&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;kube-system&lt;/span&gt;&lt;/code&gt;中获取所有的&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;pod&lt;/span&gt;&lt;/code&gt;并输出到屏幕&lt;/p&gt;
&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;main.go&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;&amp;quot;context&amp;quot;&lt;/span&gt;
        &lt;span class="s"&gt;&amp;quot;fmt&amp;quot;&lt;/span&gt;

        &lt;span class="nx"&gt;corev1&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;k8s.io/api/core/v1&amp;quot;&lt;/span&gt;
        &lt;span class="nx"&gt;metav1&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;k8s.io/apimachinery/pkg/apis/meta/v1&amp;quot;&lt;/span&gt;

        &lt;span class="s"&gt;&amp;quot;k8s.io/client-go/kubernetes/scheme&amp;quot;&lt;/span&gt;
        &lt;span class="s"&gt;&amp;quot;k8s.io/client-go/rest&amp;quot;&lt;/span&gt;
        &lt;span class="s"&gt;&amp;quot;k8s.io/client-go/tools/clientcmd&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Prepare config object.&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;// 加载k8s配置文件，生成Config对象&lt;/span&gt;
        &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;clientcmd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BuildConfigFromFlags&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/root/.kube/config&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;APIPath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;api&amp;quot;&lt;/span&gt;
        &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GroupVersion&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;corev1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SchemeGroupVersion&lt;/span&gt;
        &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NegotiatedSerializer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;scheme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Codecs&lt;/span&gt;

        &lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Init RESTClient.&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;// 定义RestClient，用于与k8s API server进行交互&lt;/span&gt;
        &lt;span class="nx"&gt;restClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RESTClientFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Get Pods in cluster.&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;// 获取pod列表。这里只会从namespace为&amp;quot;kube-system&amp;quot;中获取指定的资源(pods)&lt;/span&gt;
        &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;corev1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PodList&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;restClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
                &lt;span class="nx"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;
                &lt;span class="nx"&gt;Namespace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;kube-system&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
                &lt;span class="nx"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;pods&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
                &lt;span class="nx"&gt;VersionedParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;metav1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ListOptions&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;scheme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ParameterCodec&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
                &lt;span class="nx"&gt;Do&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TODO&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;
                &lt;span class="nx"&gt;Into&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Print all listed pods.&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;// 打印所有获取到的pods资源，输出到标准输出&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Items&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;NAMESPACE: %v NAME: %v \t STATUS: %v \n&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Namespace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Phase&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;go.mod&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;module main

go &lt;span class="m"&gt;1&lt;/span&gt;.15

require &lt;span class="o"&gt;(&lt;/span&gt;
        github.com/go-logr/logr v0.2.1 // indirect
        github.com/google/gofuzz v1.2.0 // indirect
        github.com/imdario/mergo v0.3.11 // indirect
        golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 // indirect
        golang.org/x/net v0.0.0-20201010224723-4f7140c49acb // indirect
        golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43 // indirect
        golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634 // indirect
        golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e // indirect
        k8s.io/api v0.19.2
        k8s.io/apimachinery v0.19.2
        //k8s.io/client-go v11.0.0+incompatible
        //      k8s.io/client-go v11.0.0+incompatible
        k8s.io/klog v1.0.0 // indirect
        k8s.io/klog/v2 v2.3.0 // indirect
        k8s.io/utils v0.0.0-20201005171033-6301aaf42dc7 // indirect
&lt;span class="o"&gt;)&lt;/span&gt;

require k8s.io/client-go v0.19.2
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="id3"&gt;
&lt;h2&gt;3. 输出结果&lt;/h2&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;root@xxx-wsl ~/client-go-example&lt;span class="o"&gt;]&lt;/span&gt; go run main.go
Prepare config object.
Init RESTClient.
Get Pods in cluster.
Print all listed pods.
NAMESPACE: kube-system NAME: coredns-f9fd979d6-rhzfd     STATUS: Running
NAMESPACE: kube-system NAME: coredns-f9fd979d6-whrj2     STATUS: Running
NAMESPACE: kube-system NAME: etcd-kind-control-plane     STATUS: Running
NAMESPACE: kube-system NAME: etcd-kind-control-plane2    STATUS: Running
NAMESPACE: kube-system NAME: etcd-kind-control-plane3    STATUS: Running
NAMESPACE: kube-system NAME: kindnet-bpsfl       STATUS: Running
NAMESPACE: kube-system NAME: kindnet-ks6zv       STATUS: Running
NAMESPACE: kube-system NAME: kindnet-pm6zl       STATUS: Running
NAMESPACE: kube-system NAME: kindnet-qfhqt       STATUS: Running
NAMESPACE: kube-system NAME: kindnet-s7qqn       STATUS: Running
NAMESPACE: kube-system NAME: kindnet-trk5l       STATUS: Running
NAMESPACE: kube-system NAME: kube-apiserver-kind-control-plane   STATUS: Running
NAMESPACE: kube-system NAME: kube-apiserver-kind-control-plane2          STATUS: Running
NAMESPACE: kube-system NAME: kube-apiserver-kind-control-plane3          STATUS: Running
NAMESPACE: kube-system NAME: kube-controller-manager-kind-control-plane          STATUS: Running
NAMESPACE: kube-system NAME: kube-controller-manager-kind-control-plane2         STATUS: Running
NAMESPACE: kube-system NAME: kube-controller-manager-kind-control-plane3         STATUS: Running
NAMESPACE: kube-system NAME: kube-proxy-7gz67    STATUS: Running
NAMESPACE: kube-system NAME: kube-proxy-bvbkk    STATUS: Running
NAMESPACE: kube-system NAME: kube-proxy-clf72    STATUS: Running
NAMESPACE: kube-system NAME: kube-proxy-d8zpb    STATUS: Running
NAMESPACE: kube-system NAME: kube-proxy-dsmsj    STATUS: Running
NAMESPACE: kube-system NAME: kube-proxy-fplkk    STATUS: Running
NAMESPACE: kube-system NAME: kube-scheduler-kind-control-plane   STATUS: Running
NAMESPACE: kube-system NAME: kube-scheduler-kind-control-plane2          STATUS: Running
NAMESPACE: kube-system NAME: kube-scheduler-kind-control-plane3          STATUS: Running
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
</content>
    <link href="double12gzhblogs.readthedocs.io/blogs/Kubernetes/2020-10-14-client-go%E7%B3%BB%E5%88%97%E4%B9%8B3---restclient%E7%9A%84%E4%BD%BF%E7%94%A8.html" rel="alternate"/>
    <published>2020-10-14T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>double12gzhblogs.readthedocs.io/blogs/Kubernetes/2020-10-17-client-go%E7%B3%BB%E5%88%97%E4%B9%8B4---Indexer.html</id>
    <title>client-go系列之4—Indexer</title>
    <updated>2020-10-17T00:00:00+00:00</updated>
    <author>
      <name>JeffreyGuan</name>
    </author>
    <content type="html">&lt;div class="section" id="client-go4-indexer"&gt;

&lt;p&gt;摘要：介绍client-go中Indexer机制。&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-10-13-client-go-kubeconfig/0.png" /&gt;&lt;/p&gt;
&lt;div class="section" id="id1"&gt;
&lt;h2&gt;1. 写在前面&lt;/h2&gt;
&lt;p&gt;在本系列教程的第一篇中，我们已经对如下这张图作了简单介绍。这张图非常重要，理解这张图对我们正确理解&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;client-go&lt;/span&gt;&lt;/code&gt;及&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Controller&lt;/span&gt;&lt;/code&gt;非常有帮助(&lt;a class="reference external" href="https://github.com/kubernetes/sample-controller/blob/master/docs/controller-client-go.md"&gt;出处&lt;/a&gt;)。&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://raw.githubusercontent.com/kubernetes/sample-controller/master/docs/images/client-go-controller-interaction.jpeg" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;client-go&lt;/span&gt;&lt;/code&gt;中对其实现的代码位于&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;tools/cache&lt;/span&gt;&lt;/code&gt;，如下图：&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-10-16-custom-controller/code-path.png" /&gt;&lt;/p&gt;
&lt;p&gt;除了上图中展示的&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Indexer&lt;/span&gt;&lt;/code&gt;的定义比较重要外，我们还需要关注&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Indexer&lt;/span&gt;&lt;/code&gt;比较重要的数据结构：&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;root@xxxx-wsl /ACode/client-go&lt;span class="o"&gt;]&lt;/span&gt; cat tools/cache/index.go&lt;span class="p"&gt;|&lt;/span&gt; grep -B &lt;span class="m"&gt;1&lt;/span&gt; -A &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;

// IndexFunc knows how to compute the &lt;span class="nb"&gt;set&lt;/span&gt; of indexed values &lt;span class="k"&gt;for&lt;/span&gt; an object.
&lt;span class="nb"&gt;type&lt;/span&gt; IndexFunc func&lt;span class="o"&gt;(&lt;/span&gt;obj interface&lt;span class="o"&gt;{})&lt;/span&gt; &lt;span class="o"&gt;([]&lt;/span&gt;string, error&lt;span class="o"&gt;)&lt;/span&gt;

--
// Index maps the indexed value to a &lt;span class="nb"&gt;set&lt;/span&gt; of keys in the store that match on that value
&lt;span class="nb"&gt;type&lt;/span&gt; Index map&lt;span class="o"&gt;[&lt;/span&gt;string&lt;span class="o"&gt;]&lt;/span&gt;sets.String

// Indexers maps a name to a IndexFunc
&lt;span class="nb"&gt;type&lt;/span&gt; Indexers map&lt;span class="o"&gt;[&lt;/span&gt;string&lt;span class="o"&gt;]&lt;/span&gt;IndexFunc

// Indices maps a name to an Index
&lt;span class="nb"&gt;type&lt;/span&gt; Indices map&lt;span class="o"&gt;[&lt;/span&gt;string&lt;span class="o"&gt;]&lt;/span&gt;Index
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;IndexFunc: 从接收的资源对象中，根据一系列的key来获取相应的值。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Index: 建立key与被索引值的对应关系并存储到&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Store&lt;/span&gt;&lt;/code&gt;中，即它里面的数据都是&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Key/Value&lt;/span&gt;&lt;/code&gt;的形式。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Indexers: 存储的是索引器的名字与索引器的实现函数。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Indices: 存储缓存数据，形式&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Key/Value&lt;/span&gt;&lt;/code&gt;，其中&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Key&lt;/span&gt;&lt;/code&gt;为缓存器的字，&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Value&lt;/span&gt;&lt;/code&gt;为缓存数据。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="custom-controller"&gt;
&lt;h2&gt;2. Custom Controller中的组件&lt;/h2&gt;
&lt;p&gt;如图中所示，&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Custom&lt;/span&gt; &lt;span class="pre"&gt;Controller&lt;/span&gt;&lt;/code&gt;是位于下半部分的内容。从图中可以很容易的看到，一个&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Custom&lt;/span&gt; &lt;span class="pre"&gt;Controller&lt;/span&gt;&lt;/code&gt;主要包含以下内容：&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Indexer&lt;/span&gt; &lt;span class="pre"&gt;Reference&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Informer&lt;/span&gt; &lt;span class="pre"&gt;Reference&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Resource&lt;/span&gt; &lt;span class="pre"&gt;Event&lt;/span&gt; &lt;span class="pre"&gt;Handler&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;WorkQueue&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;ProcessItem&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="section" id="indexer"&gt;
&lt;h3&gt;2.1 Indexer&lt;/h3&gt;
&lt;div class="section" id="id2"&gt;
&lt;h4&gt;2.1.1 简单介绍&lt;/h4&gt;
&lt;p&gt;它是一个知道如何使用CRD对象的 &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Indexer&lt;/span&gt;&lt;/code&gt; 实例的引用(reference)。当我们写&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;自定义控制器&lt;/span&gt;&lt;/code&gt;(Custom Controller)代码时，将使用这个reference去做对象检索。&lt;/p&gt;
&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Indexer&lt;/span&gt;&lt;/code&gt;是client-go中实现的一个本地存储，它可以建立索引并存储Resource的对象。&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Reflector&lt;/span&gt;&lt;/code&gt;通过&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Delta&lt;/span&gt; &lt;span class="pre"&gt;FIFO&lt;/span&gt; &lt;span class="pre"&gt;Queue&lt;/span&gt;&lt;/code&gt;将资源对象存储到&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Indexer&lt;/span&gt;&lt;/code&gt;中。&lt;/p&gt;
&lt;p&gt;需要注意的是，&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Indexer&lt;/span&gt;&lt;/code&gt;中的数据与etcd中的数据是完全一致的，这样client-go需要数据时，无须每次都从api-server获取，从而减少了请求过多造成对api-server的压力。&lt;/p&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;一句话总结：Indexer是用于存储+快速查找资源，即它的目的就是为了能够进行快速查找。&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Indexer&lt;/span&gt;&lt;/code&gt;是如何实现“存储+快速查找资源”的呢？我们来看一下下面这张图，通过这张图方便我们理解一下&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Indexer&lt;/span&gt;&lt;/code&gt;的存储结构：&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-10-17-indexer-data-structure/indexer_data_structure2.png" /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;说明： 图中的&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Indexers&lt;/span&gt;&lt;/code&gt;与&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Indices&lt;/span&gt;&lt;/code&gt;虽然都指向了相同的&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;IndexFunc&lt;/span&gt;&lt;/code&gt;，这并不是说二者的数据是相同的，而是说二者使用的&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;IndexFunc&lt;/span&gt;&lt;/code&gt;是相同的。&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;根据上面这个图，我们可以简单的规纳出与之对应的数据结构：&lt;/p&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;// 包含的所有索引器/分类以及对应的实现&lt;/span&gt;
&lt;span class="nx"&gt;Indexers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
    &lt;span class="s"&gt;&amp;quot;namespace&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NamespaceIndexFunc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;&amp;quot;nodeName&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NodeNameIndexFunc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// 包含的所有索引分类中所有的索引数据&lt;/span&gt;
&lt;span class="nx"&gt;Indices&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//namespace 这个索引分类下的所有索引数据&lt;/span&gt;
    &lt;span class="s"&gt;&amp;quot;namespace&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
         &lt;span class="c1"&gt;// Index 就是一个索引键下所有的对象键列表&lt;/span&gt;
        &lt;span class="s"&gt;&amp;quot;default&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;pod-1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;pod-2&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; 
        &lt;span class="c1"&gt;// Index&lt;/span&gt;
        &lt;span class="s"&gt;&amp;quot;kube-system&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;pod-3&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;   
    &lt;span class="p"&gt;},&lt;/span&gt;

    &lt;span class="c1"&gt;//nodeName 这个索引分类下的所有索引数据(对象键列表)&lt;/span&gt;
    &lt;span class="s"&gt;&amp;quot;nodeName&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="c1"&gt;// Index&lt;/span&gt;
        &lt;span class="s"&gt;&amp;quot;node1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;pod-1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
         &lt;span class="c1"&gt;// Index&lt;/span&gt;
        &lt;span class="s"&gt;&amp;quot;node2&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;pod-2&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;pod-3&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Indexers&lt;/span&gt;&lt;/code&gt;和&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Indices&lt;/span&gt;&lt;/code&gt;都是按照&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;IndexFunc&lt;/span&gt;&lt;/code&gt;(名字)分组， 每个&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;IndexFunc&lt;/span&gt;&lt;/code&gt;输出多个&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;IndexKey&lt;/span&gt;&lt;/code&gt;，产生相同&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;IndexKey&lt;/span&gt;&lt;/code&gt;的多个对象存储在一个集合中。&lt;/p&gt;
&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;IndexKey&lt;/span&gt;&lt;/code&gt;主要是用于快速查找&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;ObjectKey&lt;/span&gt;&lt;/code&gt;; 而&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;ObjectKey&lt;/span&gt;&lt;/code&gt;是对象存储时唯一命名的key(这个key方便在存储中快速找到相应的对象)。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id3"&gt;
&lt;h4&gt;2.1.2 代码位置&lt;/h4&gt;
&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;$GOPATH/pkg/mod/k8s.io/client-go&amp;#64;v0.19.0/tools/cache/index.go&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id4"&gt;
&lt;h4&gt;2.1.3 类图展示&lt;/h4&gt;
&lt;p&gt;从下图我们可以很容易的看到&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Indexer&lt;/span&gt;&lt;/code&gt;与&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;cache&lt;/span&gt;&lt;/code&gt;、&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Store&lt;/span&gt;&lt;/code&gt;及&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;ThreadSafeStore&lt;/span&gt;&lt;/code&gt;之间的调用关系。&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-10-16-custom-controller/indexers.png" /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;上图如果长时间无法显示，请参考&lt;a class="reference external" href="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-10-16-custom-controller/indexers.png"&gt;这里&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Store: 是一个通用对象存储和处理接口。&lt;/p&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="mi"&gt;26&lt;/span&gt; &lt;span class="c1"&gt;// Store is a generic object storage and processing interface.  A&lt;/span&gt;
&lt;span class="mi"&gt;27&lt;/span&gt; &lt;span class="c1"&gt;// Store holds a map from string keys to accumulators, and has&lt;/span&gt;
&lt;span class="mi"&gt;28&lt;/span&gt; &lt;span class="c1"&gt;// operations to add, update, and delete a given object to/from the&lt;/span&gt;
&lt;span class="mi"&gt;29&lt;/span&gt; &lt;span class="c1"&gt;// accumulator currently associated with a given key.  A Store also&lt;/span&gt;
&lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="c1"&gt;// knows how to extract the key from a given object, so many operations&lt;/span&gt;
&lt;span class="mi"&gt;31&lt;/span&gt; &lt;span class="c1"&gt;// are given only the object.&lt;/span&gt;
&lt;span class="mi"&gt;32&lt;/span&gt; &lt;span class="c1"&gt;//&lt;/span&gt;
&lt;span class="mi"&gt;33&lt;/span&gt; &lt;span class="c1"&gt;// In the simplest Store implementations each accumulator is simply&lt;/span&gt;
&lt;span class="mi"&gt;34&lt;/span&gt; &lt;span class="c1"&gt;// the last given object, or empty after Delete, and thus the Store&amp;#39;s&lt;/span&gt;
&lt;span class="mi"&gt;35&lt;/span&gt; &lt;span class="c1"&gt;// behavior is simple storage.&lt;/span&gt;
&lt;span class="mi"&gt;36&lt;/span&gt; &lt;span class="c1"&gt;//&lt;/span&gt;
&lt;span class="mi"&gt;37&lt;/span&gt; &lt;span class="c1"&gt;// Reflector knows how to watch a server and update a Store.  This&lt;/span&gt;
&lt;span class="mi"&gt;38&lt;/span&gt; &lt;span class="c1"&gt;// package provides a variety of implementations of Store.&lt;/span&gt;
&lt;span class="mi"&gt;39&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Store&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="mi"&gt;40&lt;/span&gt;
&lt;span class="mi"&gt;41&lt;/span&gt;     &lt;span class="c1"&gt;// Add adds the given object to the accumulator associated with the given object&amp;#39;s key&lt;/span&gt;
&lt;span class="mi"&gt;42&lt;/span&gt;     &lt;span class="nx"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;
&lt;span class="mi"&gt;43&lt;/span&gt;
&lt;span class="mi"&gt;44&lt;/span&gt;     &lt;span class="c1"&gt;// Update updates the given object in the accumulator associated with the given object&amp;#39;s key&lt;/span&gt;
&lt;span class="mi"&gt;45&lt;/span&gt;     &lt;span class="nx"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;
&lt;span class="mi"&gt;46&lt;/span&gt;
&lt;span class="mi"&gt;47&lt;/span&gt;     &lt;span class="c1"&gt;// Delete deletes the given object from the accumulator associated with the given object&amp;#39;s key&lt;/span&gt;
&lt;span class="mi"&gt;48&lt;/span&gt;     &lt;span class="nx"&gt;Delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;
&lt;span class="mi"&gt;49&lt;/span&gt;
&lt;span class="mi"&gt;50&lt;/span&gt;     &lt;span class="c1"&gt;// List returns a list of all the currently non-empty accumulators&lt;/span&gt;
&lt;span class="mi"&gt;51&lt;/span&gt;     &lt;span class="nx"&gt;List&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kd"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="mi"&gt;52&lt;/span&gt;
&lt;span class="mi"&gt;53&lt;/span&gt;     &lt;span class="c1"&gt;// ListKeys returns a list of all the keys currently associated with non-empty accumulators&lt;/span&gt;
&lt;span class="mi"&gt;54&lt;/span&gt;     &lt;span class="nx"&gt;ListKeys&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="mi"&gt;55&lt;/span&gt;
&lt;span class="mi"&gt;56&lt;/span&gt;     &lt;span class="c1"&gt;// Get returns the accumulator associated with the given object&amp;#39;s key&lt;/span&gt;
&lt;span class="mi"&gt;57&lt;/span&gt;     &lt;span class="nx"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="nx"&gt;exists&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;58&lt;/span&gt;
&lt;span class="mi"&gt;59&lt;/span&gt;     &lt;span class="c1"&gt;// GetByKey returns the accumulator associated with the given key&lt;/span&gt;
&lt;span class="mi"&gt;60&lt;/span&gt;     &lt;span class="nx"&gt;GetByKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="nx"&gt;exists&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;61&lt;/span&gt;
&lt;span class="mi"&gt;62&lt;/span&gt;     &lt;span class="c1"&gt;// Replace will delete the contents of the store, using instead the&lt;/span&gt;
&lt;span class="mi"&gt;63&lt;/span&gt;     &lt;span class="c1"&gt;// given list. Store takes ownership of the list, you should not reference&lt;/span&gt;
&lt;span class="mi"&gt;64&lt;/span&gt;     &lt;span class="c1"&gt;// it after calling this function.&lt;/span&gt;
&lt;span class="mi"&gt;65&lt;/span&gt;     &lt;span class="nx"&gt;Replace&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kd"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;
&lt;span class="mi"&gt;66&lt;/span&gt;
&lt;span class="mi"&gt;67&lt;/span&gt;     &lt;span class="c1"&gt;// Resync is meaningless in the terms appearing here but has&lt;/span&gt;
&lt;span class="mi"&gt;68&lt;/span&gt;     &lt;span class="c1"&gt;// meaning in some implementations that have non-trivial&lt;/span&gt;
&lt;span class="mi"&gt;69&lt;/span&gt;     &lt;span class="c1"&gt;// additional behavior (e.g., DeltaFIFO).&lt;/span&gt;
&lt;span class="mi"&gt;70&lt;/span&gt;     &lt;span class="nx"&gt;Resync&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;
&lt;span class="mi"&gt;71&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Indexer: 扩展了多个索引的 &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Store&lt;/span&gt;&lt;/code&gt;，并限制每个累加器只保存当前对象（删除后为空）。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;ThreadSafeStore: 与&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Indexer&lt;/span&gt;&lt;/code&gt;类似，是一个允许对存储后端进行并发索引访问的接口&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;cache: 根据 &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;ThreadSafeStore&lt;/span&gt;&lt;/code&gt; 和关联的 &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;KeyFunc&lt;/span&gt;&lt;/code&gt; 实现的 &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Indexer&lt;/span&gt;&lt;/code&gt;。所有的对象都缓存在内存中，这不是一个外部可以调用的对象，在包的外部不能直接调用&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;cache&lt;/span&gt;&lt;/code&gt;。它的定义也比较简单， 如下：&lt;/p&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="mi"&gt;131&lt;/span&gt; &lt;span class="c1"&gt;// 代码位置：client-go/tools/cache/store.go&lt;/span&gt;
&lt;span class="mi"&gt;132&lt;/span&gt; &lt;span class="c1"&gt;// `*cache` implements Indexer in terms of a ThreadSafeStore and an&lt;/span&gt;
&lt;span class="mi"&gt;133&lt;/span&gt; &lt;span class="c1"&gt;// associated KeyFunc.&lt;/span&gt;
&lt;span class="mi"&gt;134&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt; &lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="mi"&gt;135&lt;/span&gt;     &lt;span class="c1"&gt;// cacheStorage bears the burden of thread safety for the cache&lt;/span&gt;
&lt;span class="mi"&gt;136&lt;/span&gt;     &lt;span class="nx"&gt;cacheStorage&lt;/span&gt; &lt;span class="nx"&gt;ThreadSafeStore&lt;/span&gt;
&lt;span class="mi"&gt;137&lt;/span&gt;     &lt;span class="c1"&gt;// keyFunc is used to make the key for objects stored in and retrieved from items, and&lt;/span&gt;
&lt;span class="mi"&gt;138&lt;/span&gt;     &lt;span class="c1"&gt;// should be deterministic.&lt;/span&gt;
&lt;span class="mi"&gt;139&lt;/span&gt;     &lt;span class="nx"&gt;keyFunc&lt;/span&gt; &lt;span class="nx"&gt;KeyFunc&lt;/span&gt;
&lt;span class="mi"&gt;140&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;我们可以使用&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;NewStore&lt;/span&gt;&lt;/code&gt;或&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;NewIndexer&lt;/span&gt;&lt;/code&gt;来实例化一个&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Store&lt;/span&gt;&lt;/code&gt;或&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Indexer&lt;/span&gt;&lt;/code&gt;。&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;threadSafeMap : 是 &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;ThreadSafeStore&lt;/span&gt;&lt;/code&gt;的实现，它实现了并发安全的存储(它是一个内存中的存储)，具备&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;CRUD&lt;/span&gt;&lt;/code&gt;等操作。它其中包含了三个比较重要的属性，见代码：&lt;/p&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="mi"&gt;61&lt;/span&gt; &lt;span class="c1"&gt;// 代码位置：client-go/tools/cache/thread_safe_store.go&lt;/span&gt;
&lt;span class="mi"&gt;62&lt;/span&gt; &lt;span class="c1"&gt;// threadSafeMap implements ThreadSafeStore&lt;/span&gt;
&lt;span class="mi"&gt;63&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;threadSafeMap&lt;/span&gt; &lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="mi"&gt;64&lt;/span&gt;     &lt;span class="nx"&gt;lock&lt;/span&gt;  &lt;span class="nx"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RWMutex&lt;/span&gt;
&lt;span class="mi"&gt;65&lt;/span&gt;     &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="kd"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kd"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="c1"&gt;//用于保存数据&lt;/span&gt;
&lt;span class="mi"&gt;66&lt;/span&gt;
&lt;span class="mi"&gt;67&lt;/span&gt;     &lt;span class="c1"&gt;// indexers maps a name to an IndexFunc&lt;/span&gt;
&lt;span class="mi"&gt;68&lt;/span&gt;     &lt;span class="nx"&gt;indexers&lt;/span&gt; &lt;span class="nx"&gt;Indexers&lt;/span&gt;
&lt;span class="mi"&gt;69&lt;/span&gt;     &lt;span class="c1"&gt;// indices maps a name to an Index&lt;/span&gt;
&lt;span class="mi"&gt;70&lt;/span&gt;     &lt;span class="nx"&gt;indices&lt;/span&gt; &lt;span class="nx"&gt;Indices&lt;/span&gt;
&lt;span class="mi"&gt;71&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;对于&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;threadSafeMap&lt;/span&gt;&lt;/code&gt;中各种索引的关系，我们可以用下图来表示：&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-10-17-indexer-data-structure/indexer_indices_keys_mapping.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;threadSafeMap&lt;/span&gt;&lt;/code&gt;其实只会做两件事情：存储， 索引。存储即存储runtime.object到&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;items&lt;/span&gt;&lt;/code&gt;这个Map中； 索引即为&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;items&lt;/span&gt;&lt;/code&gt;这个Map建立三层索引：indices的Map类型的索引(如：namespace, nodeName等)；index Map的类型索引(如： namespace1, namespace2, …); runtime.object的索引。&lt;/p&gt;
&lt;p&gt;如果我们需要向&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;threadSafeMap&lt;/span&gt;&lt;/code&gt;中添加一个对象，只需要调用下以代码即可：&lt;/p&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="mi"&gt;72&lt;/span&gt; &lt;span class="c1"&gt;// 代码位置：client-go/tools/cache/thread_safe_store.go&lt;/span&gt;
&lt;span class="mi"&gt;73&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;threadSafeMap&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="mi"&gt;74&lt;/span&gt;     &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="mi"&gt;75&lt;/span&gt;     &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Unlock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="mi"&gt;76&lt;/span&gt;     &lt;span class="nx"&gt;oldObject&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="mi"&gt;77&lt;/span&gt;     &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;
&lt;span class="mi"&gt;78&lt;/span&gt;     &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;updateIndices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;oldObject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;79&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;如果想了解更多的细节，请查看&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;updateIndices()&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;另外，从图中我们也可以看出，&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Indexer&lt;/span&gt;&lt;/code&gt;实际上是对&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;threadSafeMap&lt;/span&gt;&lt;/code&gt;的封装，它继承了后者的所有方法，同时也实现了&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;IndexFunc&lt;/span&gt;&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="id5"&gt;
&lt;h3&gt;2.2 Indexer索引器实现&lt;/h3&gt;
&lt;p&gt;在kubernetes中使用的比较多的索引函数是&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;MetaNamespaceIndexFunc()&lt;/span&gt;&lt;/code&gt;（&lt;em&gt;代码位置: client-go/tools/cache/index.go&lt;/em&gt;）。&lt;/p&gt;
&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Indexer&lt;/span&gt;&lt;/code&gt;索引的实现是通过&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;index.ByIndex&lt;/span&gt;&lt;/code&gt;来完成的，&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;index.ByIndex&lt;/span&gt;&lt;/code&gt;的代码如下：&lt;/p&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="mi"&gt;178&lt;/span&gt; &lt;span class="c1"&gt;// 代码位置：client-go/tools/cache/thread_safe_store.go&lt;/span&gt;
&lt;span class="mi"&gt;179&lt;/span&gt; &lt;span class="c1"&gt;// ByIndex returns a list of the items whose indexed values in the given index include the given indexed value&lt;/span&gt;
&lt;span class="mi"&gt;180&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;threadSafeMap&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;ByIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;indexName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;indexedValue&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kd"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="mi"&gt;181&lt;/span&gt;     &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RLock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="mi"&gt;182&lt;/span&gt;     &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RUnlock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="mi"&gt;183&lt;/span&gt;
&lt;span class="mi"&gt;184&lt;/span&gt;     &lt;span class="nx"&gt;indexFunc&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;indexers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;indexName&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;// 关键代码。获取索引器函数。&lt;/span&gt;
&lt;span class="mi"&gt;185&lt;/span&gt;     &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;indexFunc&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="mi"&gt;186&lt;/span&gt;         &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;Index with name %s does not exist&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;indexName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;187&lt;/span&gt;     &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="mi"&gt;188&lt;/span&gt;
&lt;span class="mi"&gt;189&lt;/span&gt;     &lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;indices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;indexName&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;   &lt;span class="c1"&gt;// 关键代码。获取缓存器函数。&lt;/span&gt;
&lt;span class="mi"&gt;190&lt;/span&gt;
&lt;span class="mi"&gt;191&lt;/span&gt;     &lt;span class="nx"&gt;set&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;indexedValue&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;    &lt;span class="c1"&gt;// 关键代码。Index中的数据以Set类型存储在缓存中。&lt;/span&gt;
&lt;span class="mi"&gt;192&lt;/span&gt;     &lt;span class="nx"&gt;list&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kd"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;set&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Len&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="mi"&gt;193&lt;/span&gt;     &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="nx"&gt;set&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="mi"&gt;194&lt;/span&gt;         &lt;span class="nx"&gt;list&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="mi"&gt;195&lt;/span&gt;     &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="mi"&gt;196&lt;/span&gt;
&lt;span class="mi"&gt;197&lt;/span&gt;     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;
&lt;span class="mi"&gt;198&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;上述方法接收两个参数:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;indexName: 索引器的名字&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;indexedValue: 需要索引的key&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;此方法的基本思路如下：&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;根据索引器名字查找指定的索引器函数(&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;c.indexers[indexName]&lt;/span&gt;&lt;/code&gt;)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;根据索引器名字查找相应的缓存器函数(&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;c.indices[indexName]&lt;/span&gt;&lt;/code&gt;)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;根据索引key(即：&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;indexedValue&lt;/span&gt;&lt;/code&gt;)从缓存中进行数据查询，并返回查询结果&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
</content>
    <link href="double12gzhblogs.readthedocs.io/blogs/Kubernetes/2020-10-17-client-go%E7%B3%BB%E5%88%97%E4%B9%8B4---Indexer.html" rel="alternate"/>
    <published>2020-10-17T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>double12gzhblogs.readthedocs.io/blogs/Kubernetes/2020-10-21-client-go%E7%B3%BB%E5%88%97%E4%B9%8B5---Informer.html</id>
    <title>client-go系列之5—Informer</title>
    <updated>2020-10-21T00:00:00+00:00</updated>
    <author>
      <name>JeffreyGuan</name>
    </author>
    <content type="html">&lt;div class="section" id="client-go5-informer"&gt;

&lt;p&gt;摘要：介绍client-go informer及controller.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-10-13-client-go-kubeconfig/0.png" /&gt;&lt;/p&gt;
&lt;div class="section" id="id1"&gt;
&lt;h2&gt;1. 写在前面&lt;/h2&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;个人主页: https://gzh.readthedocs.io&lt;/p&gt;
&lt;p&gt;关注容器技术、关注&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Kubernetes&lt;/span&gt;&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;问题或建议，请公众号（&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;double12gzh&lt;/span&gt;&lt;/code&gt;）留言。&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;依然秉承本系列的传统，在文章开始都会再次上一下下面这经经典的图(足见其重要性，哈哈哈)。&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://raw.githubusercontent.com/kubernetes/sample-controller/master/docs/images/client-go-controller-interaction.jpeg" /&gt;&lt;/p&gt;
&lt;p&gt;在&lt;a class="reference external" href="https://double12gzh.github.io/2020/10/11/client-go%E7%B3%BB%E5%88%97%E4%B9%8B1-client-go%E4%BB%A3%E7%A0%81%E7%BB%93%E6%9E%84%E8%AE%B2%E8%A7%A3/"&gt;client-go系列之1—client-go代码结构讲解&lt;/a&gt;中简单介绍一个client-go中的相关的模块(即图中上半部分)，其实，在client-go中不只是有前面提到的模块，还包括上图中下半部分的内容，即自定义控制器部分，如：&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;informer reference： 是一个&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Informer&lt;/span&gt;&lt;/code&gt;的实例，主要用于处理与CRD（自定义资源）对象相关的。当我们开发自定义控制器（custom controller）时，需要这个控制器开创建相匹配的&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Informer&lt;/span&gt;&lt;/code&gt;。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;indexer reference：是一个&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Indexer&lt;/span&gt;&lt;/code&gt;的实例，主要用于处理与CRD（自定义资源）对象相关的。当我们开发自定义控制器时，需要创建&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Indexer&lt;/span&gt;&lt;/code&gt;的实例，这个实例主要作用是实现&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;存储+索引&lt;/span&gt;&lt;/code&gt;。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;WorkQueue：工作者队列。前面我们提到过&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Informer&lt;/span&gt;&lt;/code&gt;，它除了更新本地缓存之外，还要将数据同步给相应控制器，&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;WorkQueue&lt;/span&gt;&lt;/code&gt;就是为了数据同步的问题而产生的。当有资源被添加、修改或删除，&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Informer&lt;/span&gt;&lt;/code&gt;/&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;SharedInformer&lt;/span&gt;&lt;/code&gt;就会将相应的事件加入到&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;WorkQueue&lt;/span&gt;&lt;/code&gt;中。其它所有的控制器需要排队对这个queue进行读取，如果某个控制器发现这个事件与自己相关，就执行相应的操作。如果操作失败，就会把刚才取出的事件再放回到&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;WorkQueue&lt;/span&gt;&lt;/code&gt;中，等再轮到自己执行时会再去重试这次失败的操作。如果操作成功，就将该事件从队列中删除。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Resource Event Handler：这是一个回调函数，当一个&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Informer&lt;/span&gt;&lt;/code&gt;/&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;SharedInformer&lt;/span&gt;&lt;/code&gt;要分发一个对象到控制器时，会调用此函数。例如：将对象的&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Key&lt;/span&gt;&lt;/code&gt;放在&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;WorkQueue&lt;/span&gt;&lt;/code&gt;中并等待后续的处理。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Process Item：用户自定义的处理&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;WorkQueue&lt;/span&gt;&lt;/code&gt;中的相应&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Item&lt;/span&gt;&lt;/code&gt;的函数。 如，我们可以在这里面使用&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Indexer&lt;/span&gt;&lt;/code&gt;或&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Listing&lt;/span&gt; &lt;span class="pre"&gt;wrapper&lt;/span&gt;&lt;/code&gt;来根据相应的&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Key&lt;/span&gt;&lt;/code&gt;检索对象。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;Item的内容如下：&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-10-16-custom-controller/20201021-items.png" /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;queue中的内容如下：&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-10-16-custom-controller/20201021-queue.png" /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;在client-go的controller中给出了如何定义&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Indexer&lt;/span&gt;&lt;/code&gt;和&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Informer&lt;/span&gt;&lt;/code&gt;的方法，代码位置：client-go/tools/cache/controller.go，代码如下：&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;  &lt;span class="mi"&gt;344&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nx"&gt;NewIndexerInformer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="mi"&gt;345&lt;/span&gt;     &lt;span class="nx"&gt;lw&lt;/span&gt; &lt;span class="nx"&gt;ListerWatcher&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                 &lt;span class="c1"&gt;// 用于获取/监控需要Informer处理的资源&lt;/span&gt;
  &lt;span class="mi"&gt;346&lt;/span&gt;     &lt;span class="nx"&gt;objType&lt;/span&gt; &lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;           &lt;span class="c1"&gt;// 订阅的对象类型&lt;/span&gt;
  &lt;span class="mi"&gt;347&lt;/span&gt;     &lt;span class="nx"&gt;resyncPeriod&lt;/span&gt; &lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;       &lt;span class="c1"&gt;// 非0时，将会一直list我们所关心的对象； 0时，‘重新list’将会被推迟&lt;/span&gt;
  &lt;span class="mi"&gt;348&lt;/span&gt;     &lt;span class="nx"&gt;h&lt;/span&gt; &lt;span class="nx"&gt;ResourceEventHandler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;           &lt;span class="c1"&gt;// 用于处理与resources相关的事件&lt;/span&gt;
  &lt;span class="mi"&gt;349&lt;/span&gt;     &lt;span class="nx"&gt;indexers&lt;/span&gt; &lt;span class="nx"&gt;Indexers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="mi"&gt;350&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Indexer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Controller&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="mi"&gt;351&lt;/span&gt;     &lt;span class="c1"&gt;// This will hold the client state, as we know it.&lt;/span&gt;
  &lt;span class="mi"&gt;352&lt;/span&gt;     &lt;span class="nx"&gt;clientState&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;NewIndexer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;DeletionHandlingMetaNamespaceKeyFunc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;indexers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="mi"&gt;353&lt;/span&gt;
  &lt;span class="mi"&gt;354&lt;/span&gt;     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;clientState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newInformer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;objType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resyncPeriod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;clientState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="mi"&gt;355&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;ResourceEventHandler的定义如下。其代码位置: client-go/tools/cache/controller.go。这里的&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Evnet&lt;/span&gt;&lt;/code&gt;只是具有&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;通知&lt;/span&gt;&lt;/code&gt;的作用，因为，我们不应该对这里收到的对象进行任何修改。&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="mi"&gt;212&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ResourceEventHandler&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="mi"&gt;213&lt;/span&gt;     &lt;span class="nx"&gt;OnAdd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;                  &lt;span class="c1"&gt;// 当有新的对象被创建时，将会调用这个函数&lt;/span&gt;
&lt;span class="mi"&gt;214&lt;/span&gt;     &lt;span class="nx"&gt;OnUpdate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;oldObj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newObj&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;    &lt;span class="c1"&gt;// 当对象被修改时，将会调用这个函数。除此之外，当有`re-list`操作时，这个函数也会被再次调用&lt;/span&gt;
&lt;span class="mi"&gt;215&lt;/span&gt;     &lt;span class="nx"&gt;OnDelete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;               &lt;span class="c1"&gt;// 当对象被删除时，将会调用这个函数。&lt;/span&gt;
&lt;span class="mi"&gt;216&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="informer"&gt;
&lt;h2&gt;2. Informer简介&lt;/h2&gt;
&lt;p&gt;一句话背景介绍：为了减少当多个控制器对k8s-api-server进行大量访问时对api-server造成压力。&lt;/p&gt;
&lt;div class="section" id="id2"&gt;
&lt;h3&gt;2.1 产生的背景&lt;/h3&gt;
&lt;p&gt;随着Controller越来越多，如果Controller直接访问k8s-apiserver，那么将会导致其压力过大，于是在这样的背景下就有了&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Informer&lt;/span&gt;&lt;/code&gt;的概念。其发展到今天这个架构，大概可以总结出以下迭代思路：&lt;/p&gt;
&lt;p&gt;第一阶段，&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Controller&lt;/span&gt;&lt;/code&gt;直接访问k8s-api-server。存在的问题：多个控制器大量访问k8s-apiserver时会对其造成巨大的压力。&lt;/p&gt;
&lt;p&gt;第二阶段，&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Informer&lt;/span&gt;&lt;/code&gt;代替&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Controller&lt;/span&gt;&lt;/code&gt;去访问k8s-apiserver。而&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Controller&lt;/span&gt;&lt;/code&gt;的所有操作操作(如：查状态、对资源进行伸缩等）都和&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Informer&lt;/span&gt;&lt;/code&gt;进行交互。但&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Informer&lt;/span&gt;&lt;/code&gt;没有必要每次都去访问k8s-apiserver，它只要在需要的时候通过&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;ListAndWatch&lt;/span&gt;&lt;/code&gt;(即通过k8s List API获取所有资源的最新状态；通过Wath API去监听这些资源状态的变化)与k8s-apiserver交互即可。&lt;/p&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;ListAndWatch的代码位置: client-go/tools/cache/reflector.go&lt;/p&gt;
&lt;p&gt;func (r *Reflector) ListAndWatch(stopCh &amp;lt;-chan struct{}) error{
…
}&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;第三阶段， &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Informer&lt;/span&gt;&lt;/code&gt;并没有直接访问k8s-api-server，而是通过一个叫&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Reflector&lt;/span&gt;&lt;/code&gt;的对象进行api-server的访问。上面所说的 &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;ListAndWatch&lt;/span&gt;&lt;/code&gt; 事实上是由Reflector`实现的。&lt;/p&gt;
&lt;p&gt;第四阶段, 通过指定资源类型来&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Watch&lt;/span&gt;&lt;/code&gt;特定资源。&lt;/p&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;// 代码位置: client-go/tools/cache/listwatch.go&lt;/span&gt;
&lt;span class="mi"&gt;36&lt;/span&gt; &lt;span class="c1"&gt;// Watcher is any object that knows how to start a watch on a resource.&lt;/span&gt;
&lt;span class="mi"&gt;37&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Watcher&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="mi"&gt;38&lt;/span&gt;     &lt;span class="c1"&gt;// Watch should begin a watch at the specified version.&lt;/span&gt;
&lt;span class="mi"&gt;39&lt;/span&gt;     &lt;span class="nx"&gt;Watch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="nx"&gt;metav1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ListOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;watch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Interface&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;40&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;第五阶段，定义&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;SharedInformer&lt;/span&gt;&lt;/code&gt;。如果&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Controller&lt;/span&gt;&lt;/code&gt;与&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Informer&lt;/span&gt;&lt;/code&gt;是一一对应的关系，那么k8s-api-server的压力也还是挺大的。但是类似于Pod这样的资源来说，&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Deployment&lt;/span&gt;&lt;/code&gt;和&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;StatefulSet&lt;/span&gt;&lt;/code&gt;都能对它进行管理，当多个控制器同时想查Pod的状态时，实现上，只需要有一个&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Informer&lt;/span&gt;&lt;/code&gt;就能满足需求了，即: &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;SharedInformered&lt;/span&gt;&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;第六阶段，解决多个不同的控制器排除与重试问题，引入&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;DeltaFIFOQueue&lt;/span&gt;&lt;/code&gt;。每当资源被修改时，&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Reflector&lt;/span&gt;&lt;/code&gt;就会收到事件通知，并将对应的事件放入&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;DeltaFIFOQueue&lt;/span&gt;&lt;/code&gt;中。另外，&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;SharedInformer&lt;/span&gt;&lt;/code&gt;会不断从&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;DeltaFIFOQueue&lt;/span&gt;&lt;/code&gt;中读取事件并更新本地缓存(&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;ThreadSafeStore&lt;/span&gt;&lt;/code&gt;)的状态。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id3"&gt;
&lt;h3&gt;2.2 主要功能&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;通过&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;List()&lt;/span&gt;&lt;/code&gt;/&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Get()&lt;/span&gt;&lt;/code&gt;（代码位置：client-go/tools/cache/listwatch.go）获取资源对象&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;监听事件(&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;OnAdd&lt;/span&gt;&lt;/code&gt;, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;OnUpdate&lt;/span&gt;&lt;/code&gt;, &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;OnDelete&lt;/span&gt;&lt;/code&gt;)，并触发回调(&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;ResourceEventHandler&lt;/span&gt;&lt;/code&gt;)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;支持二级缓存(&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;DeltaFIFOQueue&lt;/span&gt;&lt;/code&gt;和&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;ThreadSafeStore&lt;/span&gt;&lt;/code&gt;，前者用于存储&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Watch&lt;/span&gt;&lt;/code&gt;返回的事件，后者是一个LocalStore，能够被&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Lister&lt;/span&gt;&lt;/code&gt;的&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;List()&lt;/span&gt;&lt;/code&gt;和&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Getter&lt;/span&gt;&lt;/code&gt;的&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Get()&lt;/span&gt;&lt;/code&gt;方法访问)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;需要注意的是，&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Informer&lt;/span&gt;&lt;/code&gt;与&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;k8s-apiserver&lt;/span&gt;&lt;/code&gt;之间没有同步机制，但是&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Informer&lt;/span&gt;&lt;/code&gt;内部的二级缓存之间是有同步机制的。&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;上面提到的&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;List()&lt;/span&gt;&lt;/code&gt;/&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Get()&lt;/span&gt;&lt;/code&gt;的定义位于：client-go/tools/cache/listwatch.go&lt;/p&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="mi"&gt;29&lt;/span&gt; &lt;span class="c1"&gt;// Lister is any object that knows how to perform an initial list.&lt;/span&gt;
&lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Lister&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="mi"&gt;31&lt;/span&gt;     &lt;span class="c1"&gt;// List should return a list type object; the Items field will be extracted, and the&lt;/span&gt;
&lt;span class="mi"&gt;32&lt;/span&gt;     &lt;span class="c1"&gt;// ResourceVersion field will be used to start the watch in the right place.&lt;/span&gt;
&lt;span class="mi"&gt;33&lt;/span&gt;     &lt;span class="nx"&gt;List&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="nx"&gt;metav1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ListOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;34&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; 
&lt;span class="mi"&gt;64&lt;/span&gt; &lt;span class="c1"&gt;// Getter interface knows how to access Get method from RESTClient.&lt;/span&gt;
&lt;span class="mi"&gt;65&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Getter&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="mi"&gt;66&lt;/span&gt;     &lt;span class="nx"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;restclient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Request&lt;/span&gt;
&lt;span class="mi"&gt;67&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="id4"&gt;
&lt;h3&gt;2.3 主要模块&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Reflector&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;DeltaFIFO&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;ThreadSafeStore（LocalStore）&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Controller&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Lister&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Processor&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;需要注意的是：&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;这里提到的&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Controller&lt;/span&gt;&lt;/code&gt;不是&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Kubernetes&lt;/span&gt; &lt;span class="pre"&gt;Controller&lt;/span&gt;&lt;/code&gt;（前几篇文章中我们实际上已经提到过，再次重审一下，这两个Controller并没有任何联系）&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Reflector&lt;/span&gt;&lt;/code&gt;主要用于监听与指定资源类型相关的事件&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;DeltaFIFO&lt;/span&gt;&lt;/code&gt;和&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;ThreadSafeStore（LocalStore）&lt;/span&gt;&lt;/code&gt;是&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Informer&lt;/span&gt;&lt;/code&gt;的二级缓存&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Lister&lt;/span&gt;&lt;/code&gt;主要是被调用&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;List/Get&lt;/span&gt;&lt;/code&gt;方法&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Processor&lt;/span&gt;&lt;/code&gt;中记录了所有的回调函数（即 &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;ResourceEventHandler&lt;/span&gt;&lt;/code&gt;）的实例，并负责触发之&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div class="section" id="id5"&gt;
&lt;h3&gt;2.4 类图&lt;/h3&gt;
&lt;p&gt;&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-10-11-client-go/2020-shared-informer.png" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-10-11-client-go/2020-shared-informer.png"&gt;出处&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="sharedinformer"&gt;
&lt;h3&gt;2.5 SharedInformer实现机制&lt;/h3&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;当同一个资源(如：pdod)的&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Informer&lt;/span&gt;&lt;/code&gt;被实例化多次后，将会产生多个&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Reflector&lt;/span&gt;&lt;/code&gt;，如果这些&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Reflector&lt;/span&gt;&lt;/code&gt;都去调用&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;ListAndWatch&lt;/span&gt;&lt;/code&gt;来获取资源时，将会对k8s-apiserver造成巨大的压力。&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;SharedInformer&lt;/span&gt;&lt;/code&gt;的意思是指：对于属于同一类型的资源来说，他们将会共享同一个&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Informer&lt;/span&gt;&lt;/code&gt;和&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Reflector&lt;/span&gt;&lt;/code&gt;，其实现代码如下：&lt;/p&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;// 代码位置: client-go/informers/factory.go&lt;/span&gt;
&lt;span class="mi"&gt;55&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;sharedInformerFactory&lt;/span&gt; &lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="mi"&gt;56&lt;/span&gt;     &lt;span class="nx"&gt;client&lt;/span&gt;           &lt;span class="nx"&gt;kubernetes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Interface&lt;/span&gt;
&lt;span class="mi"&gt;57&lt;/span&gt;     &lt;span class="nx"&gt;namespace&lt;/span&gt;        &lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="mi"&gt;58&lt;/span&gt;     &lt;span class="nx"&gt;tweakListOptions&lt;/span&gt; &lt;span class="nx"&gt;internalinterfaces&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TweakListOptionsFunc&lt;/span&gt;
&lt;span class="mi"&gt;59&lt;/span&gt;     &lt;span class="nx"&gt;lock&lt;/span&gt;             &lt;span class="nx"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Mutex&lt;/span&gt;
&lt;span class="mi"&gt;60&lt;/span&gt;     &lt;span class="nx"&gt;defaultResync&lt;/span&gt;    &lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Duration&lt;/span&gt;
&lt;span class="mi"&gt;61&lt;/span&gt;     &lt;span class="nx"&gt;customResync&lt;/span&gt;     &lt;span class="kd"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;reflect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Duration&lt;/span&gt;
&lt;span class="mi"&gt;62&lt;/span&gt;
&lt;span class="mi"&gt;63&lt;/span&gt;     &lt;span class="nx"&gt;informers&lt;/span&gt; &lt;span class="kd"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;reflect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SharedIndexInformer&lt;/span&gt;     &lt;span class="c1"&gt;// sharedInformer的数据都存放在这个map中。&lt;/span&gt;
&lt;span class="mi"&gt;64&lt;/span&gt;     &lt;span class="c1"&gt;// startedInformers is used for tracking which informers have been started.&lt;/span&gt;
&lt;span class="mi"&gt;65&lt;/span&gt;     &lt;span class="c1"&gt;// This allows Start() to be called multiple times safely.&lt;/span&gt;
&lt;span class="mi"&gt;66&lt;/span&gt;     &lt;span class="nx"&gt;startedInformers&lt;/span&gt; &lt;span class="kd"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;reflect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;
&lt;span class="mi"&gt;67&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="id6"&gt;
&lt;h3&gt;2.6 不同资源的Informer定义&lt;/h3&gt;
&lt;p&gt;对于不同的资源(如：pods, deployments, …)都有一个与之相对应的&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;xxxInformer&lt;/span&gt;&lt;/code&gt;存在，他们的位置为(以pod为例):&lt;/p&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;// 代码位置：clent-go/informers/core/v1/pod.go&lt;/span&gt;
   &lt;span class="mi"&gt;35&lt;/span&gt; &lt;span class="c1"&gt;// PodInformer 提供了与pod相关的shared informer及lister进行交互的途径 &lt;/span&gt;
   &lt;span class="mi"&gt;36&lt;/span&gt; &lt;span class="c1"&gt;// 每个k8s resource都会有这样一个Informer，且Informer中都有以下两个方法：Informer(), Lister()&lt;/span&gt;
   &lt;span class="mi"&gt;37&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;PodInformer&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="mi"&gt;38&lt;/span&gt;     &lt;span class="nx"&gt;Informer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SharedIndexInformer&lt;/span&gt;
   &lt;span class="mi"&gt;39&lt;/span&gt;     &lt;span class="nx"&gt;Lister&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;v1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PodLister&lt;/span&gt;
   &lt;span class="mi"&gt;40&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="mi"&gt;41&lt;/span&gt;
   &lt;span class="mi"&gt;42&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;podInformer&lt;/span&gt; &lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="mi"&gt;43&lt;/span&gt;     &lt;span class="nx"&gt;factory&lt;/span&gt;          &lt;span class="nx"&gt;internalinterfaces&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SharedInformerFactory&lt;/span&gt;
   &lt;span class="mi"&gt;44&lt;/span&gt;     &lt;span class="nx"&gt;tweakListOptions&lt;/span&gt; &lt;span class="nx"&gt;internalinterfaces&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TweakListOptionsFunc&lt;/span&gt;
   &lt;span class="mi"&gt;45&lt;/span&gt;     &lt;span class="nx"&gt;namespace&lt;/span&gt;        &lt;span class="kt"&gt;string&lt;/span&gt;
   &lt;span class="mi"&gt;46&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;调用不同资源的Informer的使用示例如下：&lt;/p&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nx"&gt;deployInformer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sharedInformer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
               &lt;span class="nx"&gt;apps&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;              &lt;span class="c1"&gt;// client-go/informers/apps&lt;/span&gt;
               &lt;span class="nx"&gt;V1&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;                &lt;span class="c1"&gt;// client-go/informers/apps/v1&lt;/span&gt;
               &lt;span class="nx"&gt;Deployments&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;       &lt;span class="c1"&gt;// client-go/informers/apps/v1/deployment.go&lt;/span&gt;
               &lt;span class="nx"&gt;Informer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;           &lt;span class="c1"&gt;// client-go/informers/apps/v1/deployment.go: type DeploymentInformer interface{}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="controller"&gt;
&lt;h2&gt;3. Controller&lt;/h2&gt;
&lt;div class="section" id="id7"&gt;
&lt;h3&gt;3.1 Controller定义&lt;/h3&gt;
&lt;p&gt;代码位置: &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;client-go/tools/cache/controller.go&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="mi"&gt;96&lt;/span&gt; &lt;span class="c1"&gt;// 这是一个Base Controller, 是其它所有控制器的`基类`。在`SharedInformer`中将会被用到。&lt;/span&gt;
&lt;span class="mi"&gt;97&lt;/span&gt; &lt;span class="c1"&gt;// &lt;/span&gt;
&lt;span class="mi"&gt;98&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Controller&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="mi"&gt;99&lt;/span&gt;      &lt;span class="c1"&gt;// Run 只做两件事情：&lt;/span&gt;
&lt;span class="mi"&gt;100&lt;/span&gt;     &lt;span class="c1"&gt;// 1. 构建并启动一个`Reflector`。把从`Config.ListerWatcher`中抛出的对象或通知发送到`Config.Queue`中，另外也可能会触发队列同步`Resync`&lt;/span&gt;
&lt;span class="mi"&gt;102&lt;/span&gt;     &lt;span class="c1"&gt;// 2. 不断的从queue中弹出item，并使用Config.ProcessFunc进行处理。&lt;/span&gt;
&lt;span class="mi"&gt;103&lt;/span&gt;     &lt;span class="c1"&gt;// &lt;/span&gt;
&lt;span class="mi"&gt;104&lt;/span&gt;     &lt;span class="c1"&gt;// 当channel `stopCh`被关闭时，上面两个操作会停止。&lt;/span&gt;
&lt;span class="mi"&gt;105&lt;/span&gt;     &lt;span class="nx"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stopCh&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="kd"&gt;chan&lt;/span&gt; &lt;span class="kd"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
&lt;span class="mi"&gt;106&lt;/span&gt;
&lt;span class="mi"&gt;107&lt;/span&gt;     &lt;span class="c1"&gt;// HasSynced Config的队列是否已经同步过了&lt;/span&gt;
&lt;span class="mi"&gt;108&lt;/span&gt;     &lt;span class="nx"&gt;HasSynced&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;
&lt;span class="mi"&gt;109&lt;/span&gt;
&lt;span class="mi"&gt;110&lt;/span&gt;     &lt;span class="c1"&gt;// LastSyncResourceVersion delegates to the Reflector when there&lt;/span&gt;
&lt;span class="mi"&gt;111&lt;/span&gt;     &lt;span class="c1"&gt;// is one, otherwise returns the empty string&lt;/span&gt;
&lt;span class="mi"&gt;112&lt;/span&gt;     &lt;span class="nx"&gt;LastSyncResourceVersion&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="mi"&gt;113&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Controller&lt;/span&gt;&lt;/code&gt;的具体实现如下：&lt;/p&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;   &lt;span class="mi"&gt;89&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt; &lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="mi"&gt;90&lt;/span&gt;     &lt;span class="nx"&gt;config&lt;/span&gt;         &lt;span class="nx"&gt;Config&lt;/span&gt;
   &lt;span class="mi"&gt;91&lt;/span&gt;     &lt;span class="nx"&gt;reflector&lt;/span&gt;      &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;Reflector&lt;/span&gt;
   &lt;span class="mi"&gt;92&lt;/span&gt;     &lt;span class="nx"&gt;reflectorMutex&lt;/span&gt; &lt;span class="nx"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RWMutex&lt;/span&gt;
   &lt;span class="mi"&gt;93&lt;/span&gt;     &lt;span class="nx"&gt;clock&lt;/span&gt;          &lt;span class="nx"&gt;clock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Clock&lt;/span&gt;
   &lt;span class="mi"&gt;94&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;从上面的实现中， 我们可以看到， 一个&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Controller&lt;/span&gt;&lt;/code&gt;实际上是一个以&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Config&lt;/span&gt;&lt;/code&gt;为参数，并将会被&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Informer&lt;/span&gt;&lt;/code&gt;使用到的一个low-level的控制器。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id8"&gt;
&lt;h3&gt;3.2 Controller关键方法&lt;/h3&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="c1"&gt;// 代码位置: client-go/tools/cache/controller.go&lt;/span&gt;
&lt;span class="c1"&gt;// Run 开始处理items，当stopCh被关闭或stopCh收到某个值是将会停止执行。&lt;/span&gt;
&lt;span class="mi"&gt;127&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stopCh&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="kd"&gt;chan&lt;/span&gt; &lt;span class="kd"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="mi"&gt;128&lt;/span&gt;     &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="nx"&gt;utilruntime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;HandleCrash&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="mi"&gt;129&lt;/span&gt;     &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="mi"&gt;130&lt;/span&gt;         &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="nx"&gt;stopCh&lt;/span&gt;
&lt;span class="mi"&gt;131&lt;/span&gt;         &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="mi"&gt;132&lt;/span&gt;     &lt;span class="p"&gt;}()&lt;/span&gt;
&lt;span class="mi"&gt;133&lt;/span&gt;     &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;NewReflector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;             &lt;span class="c1"&gt;// 创建一个Reflector， 用于ListAndWatch， 从而获取指定资源的当前状态&lt;/span&gt;
&lt;span class="mi"&gt;134&lt;/span&gt;         &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ListerWatcher&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="c1"&gt;// List/Watch指定的资源对象&lt;/span&gt;
&lt;span class="mi"&gt;135&lt;/span&gt;         &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ObjectType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="mi"&gt;136&lt;/span&gt;         &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Queue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="mi"&gt;137&lt;/span&gt;         &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FullResyncPeriod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="mi"&gt;138&lt;/span&gt;     &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;139&lt;/span&gt;     &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ShouldResync&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ShouldResync&lt;/span&gt;
&lt;span class="mi"&gt;140&lt;/span&gt;     &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WatchListPageSize&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WatchListPageSize&lt;/span&gt;
&lt;span class="mi"&gt;141&lt;/span&gt;     &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clock&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clock&lt;/span&gt;
&lt;span class="mi"&gt;142&lt;/span&gt;     &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WatchErrorHandler&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="mi"&gt;143&lt;/span&gt;         &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;watchErrorHandler&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WatchErrorHandler&lt;/span&gt;
&lt;span class="mi"&gt;144&lt;/span&gt;     &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="mi"&gt;145&lt;/span&gt;
&lt;span class="mi"&gt;146&lt;/span&gt;     &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reflectorMutex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="mi"&gt;147&lt;/span&gt;     &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reflector&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;
&lt;span class="mi"&gt;148&lt;/span&gt;     &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reflectorMutex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Unlock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="mi"&gt;149&lt;/span&gt;
&lt;span class="mi"&gt;150&lt;/span&gt;     &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;wg&lt;/span&gt; &lt;span class="nx"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Group&lt;/span&gt;
&lt;span class="mi"&gt;151&lt;/span&gt;
&lt;span class="mi"&gt;152&lt;/span&gt;     &lt;span class="nx"&gt;wg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StartWithChannel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stopCh&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// r.Run中的最主要的方法就是执行Reflector的ListAndWatch()获取资源对象的值&lt;/span&gt;
&lt;span class="mi"&gt;153&lt;/span&gt;     &lt;span class="c1"&gt;// c.ProcessLoop(client-go/tools/cache/controller.go)会从queue中取出资源Delta并进行处理&lt;/span&gt;
&lt;span class="mi"&gt;154&lt;/span&gt;     &lt;span class="nx"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Until&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;processLoop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;stopCh&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;155&lt;/span&gt;     &lt;span class="nx"&gt;wg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Wait&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="mi"&gt;156&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;c.processLoop的实现如下：&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="mi"&gt;181&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;processLoop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="mi"&gt;182&lt;/span&gt;     &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="mi"&gt;183&lt;/span&gt;         &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Pop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PopProcessFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="mi"&gt;184&lt;/span&gt;         &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="mi"&gt;185&lt;/span&gt;             &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nx"&gt;ErrFIFOClosed&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="mi"&gt;186&lt;/span&gt;                 &lt;span class="k"&gt;return&lt;/span&gt;
&lt;span class="mi"&gt;187&lt;/span&gt;             &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="mi"&gt;188&lt;/span&gt;             &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RetryOnError&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="mi"&gt;189&lt;/span&gt;                 &lt;span class="c1"&gt;// This is the safe way to re-enqueue.&lt;/span&gt;
&lt;span class="mi"&gt;190&lt;/span&gt;                 &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AddIfNotPresent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;// Queue是一个DeltaFIFO&lt;/span&gt;
&lt;span class="mi"&gt;191&lt;/span&gt;             &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="mi"&gt;192&lt;/span&gt;         &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="mi"&gt;193&lt;/span&gt;     &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="mi"&gt;194&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="id9"&gt;
&lt;h3&gt;3.3 Controller小结&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;有一个FIFO Queue的索引&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;有一个ListWatcher的索引&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;通过Pop从FIFO queue中消费资源对象(即: items)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;创建Reflector&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;提供一个proccessLoop处理资源对象以达到期望的状态&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;另外需要注意的是，不要把tools/cache/controller.go中的&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Controller&lt;/span&gt;&lt;/code&gt;与&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Cumstom&lt;/span&gt; &lt;span class="pre"&gt;Controller&lt;/span&gt;&lt;/code&gt;混为一谈，两者是完全不同的两个东西。&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div class="section" id="id10"&gt;
&lt;h3&gt;3.4 Controller示例&lt;/h3&gt;
&lt;p&gt;前面从“理论”方面对Controller/Informer做了简单的介绍，俗话说的好“纸上得来终觉浅，绝知此事要耕行”，建议学完上面的理论后，还是结合下面例子在实践中再体会一下。&lt;/p&gt;
&lt;p&gt;Controller的工作流程：
请参考: client-go/tools/cache/controller_test.go: Example()&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;p&gt;欢迎关注我的微信公众号：&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/wechat_public.jpg" /&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
</content>
    <link href="double12gzhblogs.readthedocs.io/blogs/Kubernetes/2020-10-21-client-go%E7%B3%BB%E5%88%97%E4%B9%8B5---Informer.html" rel="alternate"/>
    <published>2020-10-21T00:00:00+00:00</published>
  </entry>
  <entry>
    <id>double12gzhblogs.readthedocs.io/blogs/Kubernetes/2020-11-03-kubernetes%E7%9A%84%E5%86%85%E9%83%A8%E6%9C%BA%E5%88%B6.html</id>
    <title>kubernetes内部机制</title>
    <updated>2020-11-08T00:00:00+00:00</updated>
    <author>
      <name>JeffreyGuan</name>
    </author>
    <content type="html">&lt;div class="section" id="kubernetes"&gt;

&lt;p&gt;摘要：kubernetes内部机制简介。&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-10-13-client-go-kubeconfig/0.png" /&gt;&lt;/p&gt;
&lt;div class="section" id="id1"&gt;
&lt;h2&gt;1. 写在前面&lt;/h2&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;个人主页: https://gzh.readthedocs.io&lt;/p&gt;
&lt;p&gt;关注容器技术、关注&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Kubernetes&lt;/span&gt;&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;问题或建议，请公众号（&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;double12gzh&lt;/span&gt;&lt;/code&gt;）留言。&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;Kubernetes是一个容器编排引擎，设计用于在一组节点（通常称为集群）上托管容器化应用。使用系统建模方法，本系列旨在推进对Kubernetes及其基础概念的理解。
对于本篇博文，建议您需要提前对Kubernetes、Kubernetes对象和Kubernetes控制器有所理解。&lt;/p&gt;
&lt;p&gt;Kubernetes的特点是声明式容器编排引擎：在声明式系统中，用户向系统提供系统的期望状态的表示。然后，系统考虑当前状态和期望状态，确定从当前状态过渡到期望状态的需要执行哪些命令序列。
因此，”声明式系统 “这个术语激发了一个经过计算的、具有明确目的的协调工作的概念，其最终目的是以便从当前状态过渡到期望状态。&lt;/p&gt;
&lt;p&gt;然而，这不是Kubernetes的实际工作方式!&lt;/p&gt;
&lt;p&gt;Kubernetes并没有根据当前状态和期望状态确定一个经过计算、协调的命令执行序列。相反，Kubernetes仅根据当前状态迭代确定下一个要执行的命令。如果以及无法确定下一条命令时，Kubernetes就达到了稳定状态。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id2"&gt;
&lt;h2&gt;2. 状态转换机制&lt;/h2&gt;
&lt;p&gt;本段概述了Kubernetes的状态转换语义的抽象模型。接下来的段落概述了一个基于部署对象和部署控制器的具体示例。&lt;/p&gt;
&lt;p&gt;&lt;img alt="图1" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-11-03-k8s-mechanics/1.png" /&gt;&lt;/p&gt;
&lt;div class="highlight-als notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;fact {
    all k8s : K8s - last | let k8s&amp;#39; = k8s.next {
        some c : NextCommand[k8s] {
            command.source = k8s and command.target = k8s&amp;#39;
        }
    }
    NextCommand[k8s.last] = none
}
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;上述代码展示了Kubernetes的状态转换语义。给定下一个命令函数，系统将根据当前状态k8s确定下一个命令，将系统从当前状态k8s过渡到下一个状态k8s’。&lt;/p&gt;
&lt;p&gt;上述代码中使用到的&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;NextCommand()&lt;/span&gt;&lt;/code&gt;的实现逻辑如下：&lt;/p&gt;
&lt;div class="highlight-als notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;fun NextCommand(k8s : K8s) : set Command {
  DeploymentController.NextCommand[k8s] +
  ReplicaSetController.NextCommand[k8s] +
  ...
}
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;从概念上讲，&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;NextCommand&lt;/span&gt;&lt;/code&gt;函数是由每个Kubernetes控制器的&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;NextCommand&lt;/span&gt;&lt;/code&gt;函数的组成。&lt;/p&gt;
&lt;div class="highlight-als notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;pred Steady(k8s : K8s) { NextCommand[k8s] = none }

&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;状态序列以一个状态k8s.last结束，对于这个状态，&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;NextCommand&lt;/span&gt;&lt;/code&gt;函数没有产生下一条命令，这个状态通常称为稳态。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="k8s"&gt;
&lt;h2&gt;3. k8s对象&lt;/h2&gt;
&lt;p&gt;Kubernetes对象存储是一组Kubernetes对象。Kubernetes对象是一些有不同规格的数据记录，称为&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;kind&lt;/span&gt;&lt;/code&gt;。&lt;/p&gt;
&lt;div class="highlight-yaml notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;apps/v1&lt;/span&gt;
&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;Deployment&lt;/span&gt;
&lt;span class="nt"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;gzh-deployment&lt;/span&gt;
&lt;span class="nt"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="nt"&gt;replicas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;3&lt;/span&gt;
  &lt;span class="nt"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nt"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="nt"&gt;containers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;mydemo&lt;/span&gt;
        &lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;busybox:latest&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;如上所示即为一种k8s对象。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id3"&gt;
&lt;h2&gt;4. k8s控制器&lt;/h2&gt;
&lt;p&gt;每个Kubernetes Controller都会为下一条命令产生输入，一个Controller被实现为一个连续的过程，它根据Kubernetes的当前状态产生后续的命令。&lt;/p&gt;
&lt;div class="highlight-tla notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;process Controller = &amp;quot;Deployment Controller&amp;quot;
begin
    ControlLoop:
      while TRUE do
        \* The Deployment Controller monitors Deployment Objects
        with d ∈ {d ∈ k8s: d.kind = &amp;quot;Deployment&amp;quot;} do
          \* 1. Enabling Condition
          if Cardinality({r \in k8s: r.kind = &amp;quot;ReplicaSet&amp;quot; ∧ match(d.spec.labelSelector, r.meta.labels)}) &amp;lt; 1 then
            \* Reconciling Command
            CREATE([kind |-&amp;gt; &amp;quot;ReplicaSet&amp;quot;, spec |-&amp;gt; [replicas |-&amp;gt; d.spec.replicas, template |-&amp;gt; d.spec.template]]);
          end if;
          \* 2. Enabling Condition
          if Cardinality({r \in k8s: r.kind = &amp;quot;ReplicaSet&amp;quot; ∧ match(d.spec.labelSelector, r.meta.labels)}) &amp;gt; 1 then
            \* Reconciling Command
            with r ∈ {r \in k8s: r.kind = &amp;quot;ReplicaSet&amp;quot; ∧ match(d.spec.labelSelector, r.meta.labels)} do
               DELETE(r);
            end with;
          end if;
        end with;
      end while;
end process;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;上述代码5说明了deployment控制器。控制器监控deployment对象，并对每个对象执行一组条件语句。&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;条件&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果匹配的ReplicaSet对象少于1个&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;命令
然后，deployment控制器将产生一个&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;创建&lt;/span&gt; &lt;span class="pre"&gt;ReplicaSet&lt;/span&gt; &lt;/code&gt;命令。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;条件&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果有超过1个匹配的ReplicaSet对象。&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;命令
然后，deployment控制器将发出&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;删除&lt;/span&gt; &lt;span class="pre"&gt;ReplicaSet&lt;/span&gt; &lt;span class="pre"&gt;命令&lt;/span&gt;&lt;/code&gt;。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;从Controller的角度来看，如果Controller的条件都没有启用，Kubernetes就处于稳定状态，也就是说，Controller不会产生任何命令。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id4"&gt;
&lt;h2&gt;5. 级联指令&lt;/h2&gt;
&lt;p&gt;控制器（可以）级联地相互启用:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;给定k8s的状态，如果Kubernetes控制器C被启用，C将执行一个过渡到k8s’的命令。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;给定k8s’的状态，如果启用了Kubernetes控制器C’，C’将执行一个过渡到k8s’‘的命令。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="图2" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-11-03-k8s-mechanics/2.png" /&gt;&lt;/p&gt;
&lt;p&gt;图2显示了用户向API服务器提交deployment对象后产生的命令级联。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id5"&gt;
&lt;h2&gt;6. k8s是一个声明式系统吗？&lt;/h2&gt;
&lt;p&gt;&lt;img alt="图3" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-11-03-k8s-mechanics/3.png" /&gt;&lt;/p&gt;
&lt;div class="highlight-als notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;fact {
    all sys : Sys - last | let sys&amp;#39; = sys.next {
        some c : Command {
            command.source = sys and command.target = sys&amp;#39;
        }
    }
    Desired[sys.last]
}
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;上述代码定义了声明式系统的状态转换机制。给定一个期望状态谓词，系统将确定一个命令序列，使系统从当前状态k8s.first过渡到期望状态k8s.last。&lt;/p&gt;
&lt;p&gt;&lt;img alt="图4" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-11-03-k8s-mechanics/4.png" /&gt;&lt;/p&gt;
&lt;p&gt;如果我们不把Kubernetes对象解释为事实的记录，而是解释为意图的记录，那么我们就可以认同Kubernetes是一个声明式系统的概念。&lt;/p&gt;
&lt;div class="highlight-yaml notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;apps/v1&lt;/span&gt;
&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;Deployment&lt;/span&gt;
&lt;span class="nt"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;gzh-deployment&lt;/span&gt;
&lt;span class="nt"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="nt"&gt;replicas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;3&lt;/span&gt;
  &lt;span class="nt"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nt"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="nt"&gt;containers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="p p-Indicator"&gt;-&lt;/span&gt; &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;mydemo&lt;/span&gt;
      &lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;busybox:latest&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;例如，我们可以将代码中的deployment对象解释为存在一组3个副本对象的意图，因此对象的&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;.spec.container[0].image&lt;/span&gt;&lt;/code&gt;等于&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;busyBox:latest&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;然而，这一概念并非没有微妙之处：如果将一个对象解释为意图记录，则会出现多个选项。例如，Deployment Object 可以解释为：&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;应有一个 ReplicaSet 或&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;有一些pods&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;根据解释，当前状态可能与所需状态相匹配，也可能不相匹配:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;如果有ReplicaSet和可选的Pods或&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;如果有一个ReplicaSet，并且必须有一组Pods&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;与我们的解释无关&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;如果有ReplicaSet对象，K8s与部署对象的关系处于稳定状态（deployment控制器不会产生Commands）。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;如果有一组pod对象，K8s与ReplicaSet对象的关系处于稳定状态（ReplicaSet控制器不会产生命令）。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="id6"&gt;
&lt;h2&gt;7. 结论&lt;/h2&gt;
&lt;p&gt;Kubernetes可能被描述为一个声明式系统，而Kubernetes对象可能被描述为意向记录。
然而，当你推理Kubernetes和Kubernetes的行为时，你应该记住，Kubernetes不会做出协调的努力来过渡到所需的状态。
相反，Kubernetes会做出持续的、不协调的努力来过渡到稳定状态。&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;p&gt;欢迎关注我的微信公众号：&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/wechat_public.jpg" /&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
</content>
    <link href="double12gzhblogs.readthedocs.io/blogs/Kubernetes/2020-11-03-kubernetes%E7%9A%84%E5%86%85%E9%83%A8%E6%9C%BA%E5%88%B6.html" rel="alternate"/>
    <published>2020-11-08T00:00:00+00:00</published>
  </entry>
</feed>
