<?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 by 大海星</title>
  <updated>2020-11-08T14:11:57.952530+00:00</updated>
  <link href="double12gzhblogs.readthedocs.io"/>
  <link href="double12gzhblogs.readthedocs.io/blogs/author/大海星/atom.xml"/>
  <generator uri="https://ablog.readthedocs.org" version="0.10.9">ABlog</generator>
  <entry>
    <id>double12gzhblogs.readthedocs.io/blogs/GoLang/2020-09-21-goroutine%E5%8F%91%E7%94%9F%E5%88%87%E6%8D%A2%E6%97%B6%E8%83%8C%E5%90%8E%E5%8F%91%E7%94%9F%E4%BA%86%E4%BB%80%E4%B9%88.html</id>
    <title>goroutine切换背后那些事儿</title>
    <updated>2020-09-21T00: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-21-go-switch/0.png" /&gt;
&lt;/div&gt;
&lt;div class="section" id="goroutine"&gt;

&lt;p&gt;本文基于于GoLang 1.13。&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;Goroutine很轻量，从资源消耗方面来看，它只需要一个2Kb的内存栈就可以运行；从运行时来看，它的运行成本也很低，将一个goroutine切换到另一个goroutine并不需要很多操作。&lt;/p&gt;
&lt;p&gt;在进行讲解golang的切换之前，我们先High
Level的看一下goroutine切换的相关内容。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id2"&gt;
&lt;h2&gt;2. 案例&lt;/h2&gt;
&lt;p&gt;golang会根据两种断点将goroutine调度到线程上：&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;当一个goroutine阻塞了。如：系统调用，mutex，或者通道。被阻塞的goroutine会进入睡眠模式/队列，让Go调度并运行一个等待的goroutine。被阻塞的goroutine进入睡眠模式/队列，允许Go调度&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/GoLang/2020-09-21-goroutine发生切换时背后发生了什么.rst&lt;/span&gt;, line 36)&lt;/p&gt;
&lt;p&gt;Bullet list ends without a blank line; unexpected unindent.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;和运行一个等待的goroutine。&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;在函数调用过程中，假如goroutine必须增长它的栈。这个断点允许Go调度另一个goroutine，避免正在运行的那个goroutine占用CPU。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在这两种情况下，运行调度器的g0会用另一个准备运行的goroutine替换当前的goroutine。然后，被选中的goroutine取代g0，从而在线程上运行。&lt;/p&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;如果您想了解更多关于&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;g0&lt;/span&gt;&lt;/code&gt;的内容，请参考&lt;a class="reference external" href="https://www.cnblogs.com/double12gzh/p/13661777.html"&gt;g0&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;将一个运行中的goroutine切换到另一个运行中的goroutine涉及到两个切换。&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;g&lt;/span&gt;&lt;/code&gt;-&amp;gt; &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;g0&lt;/span&gt;&lt;/code&gt;。&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-21-go-switch/1.png" /&gt;
&lt;/div&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;g0&lt;/span&gt;&lt;/code&gt;-&amp;gt; 另一个&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;g&lt;/span&gt;&lt;/code&gt;。&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-21-go-switch/2.png" /&gt;
&lt;/div&gt;
&lt;p&gt;在GoLang中，groutine真的非常轻量。为了保存，它只需要两个东西：&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;goroutine是在哪一行停止的。即：在被调度前，goroutine是在哪一行停止的，当前要运行的指令被记录在程序计数器（PC）中。goroutine稍后将在同一点恢复。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;存放goroutine的堆栈。这个堆栈的目的是为了方便再次运行时恢复其局部变量。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;下面我们深入看一下。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="pc"&gt;
&lt;h2&gt;3. PC（程序记数器）&lt;/h2&gt;
&lt;p&gt;为了便于举例，我将使用一个通过channel进行通信的goroutine来说明，这两个goroutine中，一个可以产生数据的，其它的用于消费数据。代码如下：&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="main-go"&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&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;fmt&amp;quot;&lt;/span&gt;
     &lt;span class="s"&gt;&amp;quot;sync&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;COUNT&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&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;wg&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;WaitGroup&lt;/span&gt;

     &lt;span class="nx"&gt;c&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;chan&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&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;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

     &lt;span class="c1"&gt;// 生产数据&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="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="mi"&gt;0&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;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&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;&amp;lt;-&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;
         &lt;span class="p"&gt;}&lt;/span&gt;

         &lt;span class="nb"&gt;close&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;wg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Done&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="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="mi"&gt;0&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;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;{&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;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&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="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;v&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;c&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;v&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&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;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="nx"&gt;v&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="p"&gt;}()&lt;/span&gt;
     &lt;span class="p"&gt;}&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="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;消费者基本上会打印0到99的偶数，我们将重点关注第一个goroutine–生产者–向缓冲区添加数字。当缓冲区满了，它将在发送消息时阻塞。此时，Go要切换到g0，调度另一个goroutine。&lt;/p&gt;
&lt;p&gt;如前所述，Go首先需要保存当前指令，以便在同一指令处恢复goroutine。程序计数器(PC)保存在goroutine的内部结构中。&lt;/p&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-21-go-switch/3.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;➜  hello go tool compile -N -l main.go
➜  hello ls &lt;span class="p"&gt;|&lt;/span&gt; grep main.o
main.o
&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;➜  hello go tool objdump main.o
&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-21-go-switch/4.png" /&gt;
&lt;/div&gt;
&lt;p&gt;在函数&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;runtime.chansend1&lt;/span&gt;&lt;/code&gt;上阻塞通道前，程序逐条指令执行。Go将当前的程序计数器保存到当前goroutine的内部属性中。在我们的例子中，Go保存程序计数器的地址是&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;0x4268d0&lt;/span&gt;&lt;/code&gt;，这
个地址是在&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;runtime&lt;/span&gt;&lt;/code&gt;和方法&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;runtime.chansend1&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-21-go-switch/5.png" /&gt;
&lt;/div&gt;
&lt;p&gt;然后，当&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;g0&lt;/span&gt;&lt;/code&gt;唤醒goroutine时，它将在同一指令处恢复，对数值进行循环并推入通道。&lt;/p&gt;
&lt;p&gt;下面我们来谈谈goroutine切换过程中的栈管理。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="stack"&gt;
&lt;h2&gt;4. 栈(stack)&lt;/h2&gt;
&lt;p&gt;在被阻塞之前，正在运行的goroutine有它的原始栈。这个堆栈包含临时内存，比如变量i:&lt;/p&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-21-go-switch/6.png" /&gt;
&lt;/div&gt;
&lt;p&gt;然后，当它在通道上阻塞时，goroutine将和它的堆栈一起切换到g0，这个goroutine将会有一个更大的栈。&lt;/p&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-21-go-switch/7.png" /&gt;
&lt;/div&gt;
&lt;p&gt;在切换之前，堆栈将被保存，以便在goroutine再次运行时恢复。&lt;/p&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-21-go-switch/8.png" /&gt;
&lt;/div&gt;
&lt;p&gt;我们现在已经完整地了解了goroutine切换中涉及的不同操作。现在让我们看看它是如何影响性能的。&lt;/p&gt;
&lt;p&gt;我们应该注意到，一些架构(比如arm)需要多保存一个寄存器LR(链接寄存器)。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id3"&gt;
&lt;h2&gt;5. 操作&lt;/h2&gt;
&lt;p&gt;为了测量goroutine切换可能需要的时间，我们将使用前面写的程序。然而，它并不能给出一个完美的性能视图，因为它可能取决于找到下一个要调度的goroutine所需的时间。这样goroutine的切换也会
影响性能，从函数prolog的切换比从通道上阻塞的goroutine切换要做的操作更多。&lt;/p&gt;
&lt;p&gt;我们来总结一下我们要测量的操作：&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;当前的g在通道上阻塞并切换到g0:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;PC和堆栈指针一起被保存在一个内部结构中&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;g0被设置为正在运行的goroutine。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;g0的堆栈取代了当前的堆栈。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;g0正在寻找一个新的goroutine来运行。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;g0必须与所选的goroutine进行切换。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;PC和堆栈指针被从内部结构中提取出来。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;程序跳转到获取的PC地址。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&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-21-go-switch/9.png" /&gt;
&lt;/div&gt;
&lt;p&gt;从&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;g&lt;/span&gt;&lt;/code&gt;到&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;g0&lt;/span&gt;&lt;/code&gt;或&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;g0到&lt;/span&gt;&lt;/code&gt;g的切换是最快的阶段。它们包含少量固定的指令，这一点与调度器检查许多源以寻找下一个要运行的goroutine的情况相反。根据运行程序的情况，这个阶段甚
至可能需要更多的时间。&lt;/p&gt;
&lt;p&gt;需要说明的一点是，对于以上测试的结果会因机器架构的不同而不同&lt;/p&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/GoLang/2020-09-21-goroutine%E5%8F%91%E7%94%9F%E5%88%87%E6%8D%A2%E6%97%B6%E8%83%8C%E5%90%8E%E5%8F%91%E7%94%9F%E4%BA%86%E4%BB%80%E4%B9%88.html" rel="alternate"/>
    <published>2020-09-21T00:00:00+00:00</published>
  </entry>
  <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>
</feed>
