-
Notifications
You must be signed in to change notification settings - Fork 0
/
search.xml
70 lines (33 loc) · 102 KB
/
search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>记一次奇葩的TIDB迁移</title>
<link href="/2024/04/29/ji-yi-ci-qi-pa-de-tidb-qian-yi/"/>
<url>/2024/04/29/ji-yi-ci-qi-pa-de-tidb-qian-yi/</url>
<content type="html"><![CDATA[<h3 id="TIDB-官网文档"><a href="#TIDB-官网文档" class="headerlink" title="TIDB 官网文档"></a>TIDB 官网文档</h3><p><a href="https://docs.pingcap.com/zh/tidb/v6.5/ecosystem-tool-user-guide">https://docs.pingcap.com/zh/tidb/v6.5/ecosystem-tool-user-guide</a></p><h3 id="需求"><a href="#需求" class="headerlink" title="需求"></a>需求</h3><p>从旧的 TIDB(生产使用)迁移到新的 TIDB(后面生产使用),并且其中几张表要通过 TICDC 同步到 ES。( 9 亿数据量,同步完成时长 1 天)</p><h3 id="迁移方案"><a href="#迁移方案" class="headerlink" title="迁移方案"></a>迁移方案</h3><ol><li>先使用 BR 工具 将数据迁移到新的 TIDB 集群 (BR工具官网文档地址:<a href="https://docs-archive.pingcap.com/zh/tidb/v4.0/backup-and-restore-tool%EF%BC%89">https://docs-archive.pingcap.com/zh/tidb/v4.0/backup-and-restore-tool)</a></li><li>由于 BR 迁移是物理迁移,是触发不了 TICDC 任务的,所以我们单独对这 4 张表做一个处理,使用 Dumpling && Lightning 工具进行备份恢复,先创建好 TICDC(新的临时库)任务,然后使用 Lightning 工具进行恢复,CDC 的 sink 为 kafka,filter_rules 为临时库,通过 logstash 同步到 ES。(Dumpling && Lightning 官网文档地址:<a href="https://docs.pingcap.com/zh/tidb/v6.5/dumpling-overview">https://docs.pingcap.com/zh/tidb/v6.5/dumpling-overview</a> <a href="https://docs.pingcap.com/zh/tidb/v6.5/tidb-lightning-overview%EF%BC%89">https://docs.pingcap.com/zh/tidb/v6.5/tidb-lightning-overview)</a></li><li>等待 2 中数据同步到 ES 完成后,删除 2 中的 临时 TICDC 任务,创建新的 TICDC 任务,filter_rules 为正式库,此时在旧的 TIDB 上 创建 TICDC 任务,sink 为 新的 TIDB,start_ts 为 BR 时提示的 BackupTS,这时候就可以完成 TIDB 集群的迁移,并且 数据全部同步到 ES。</li></ol><h3 id="解决详情"><a href="#解决详情" class="headerlink" title="解决详情"></a>解决详情</h3><h4 id="全量迁移"><a href="#全量迁移" class="headerlink" title="全量迁移"></a>全量迁移</h4><p>先拉长 GC 时长,保证迁移期间 BackupTS 可用</p><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">SET</span> <span class="token keyword">GLOBAL</span> tidb_gc_life_time <span class="token operator">=</span> <span class="token string">'720h'</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>然后在上游使用 BACKUP 工具进行全量迁移<br>由于我们是在生产上去备份,为了防止影响生产,我们做了个限速,RATE_LIMIT = 40 MB/SECOND,将影响控制在最小</p><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">BACKUP</span> <span class="token keyword">DATABASE</span> <span class="token operator">*</span> <span class="token keyword">TO</span> <span class="token string">'s3://devops/tidb-prod/?region=us-west-1&access-key=xxx&secret-access-key=xxx'</span> RATE_LIMIT <span class="token operator">=</span> <span class="token number">40</span> MB<span class="token operator">/</span><span class="token keyword">SECOND</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>执行完记录 BackupTS 的值</p><p>下游集群中执行 RESTORE 语句备份数据(时间大概为BACKUP的2.5倍,建议放到后台去执行,避免中断)</p><pre class="line-numbers language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">RESTORE</span> <span class="token keyword">DATABASE</span> <span class="token operator">*</span> <span class="token keyword">FROM</span> <span class="token string">'s3://devops/tidb-prod/?region=us-west-1&access-key=xxx&secret-access-key=xxx'</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h4 id="创建-同步到-ES-的-logstash(临时库)"><a href="#创建-同步到-ES-的-logstash(临时库)" class="headerlink" title="创建 同步到 ES 的 logstash(临时库)"></a>创建 同步到 ES 的 logstash(临时库)</h4><p>logstash 配置涉及处理字段细节,略过,有想交流的可以私信我<br>重点配置(3个节点)<br>pipeline.worker = 1<br>input 配置<br>consumer_threads => 20<br>因为我的partition有 60 ,3个节点<br>所以每个节点 20 个线程,并且 worker 为 1,防止 INSERT 跟 UPDATE 之间顺序错乱<br>如果有更好的处理方法,欢迎私聊我告知我解决方案,谢谢!</p><h4 id="创建-CDC-任务-(临时库)"><a href="#创建-CDC-任务-(临时库)" class="headerlink" title="创建 CDC 任务 (临时库)"></a>创建 CDC 任务 (临时库)</h4><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">curl</span> <span class="token parameter variable">-X</span> POST <span class="token parameter variable">-H</span> <span class="token string">"Content-type: application/json"</span> http://127.0.0.1:8300/api/v1/changefeeds <span class="token parameter variable">-d</span> <span class="token string">'{"changefeed_id":"record-sync-qy-all","sink_uri":"kafka://b-1.xxx:9092,b-3.xxx:9092,b-2.xxx:9092/record-sync-qy?protocol=canal-json&kafka-version=2.8.1&partition-num=60&max-message-bytes=67108864&replication-factor=1","filter_rules":["test_record.tbsendrcd"],"sink_config":{"dispatchers":[{"matcher":["test_record.tbsendrcd"], "dispatcher":"rowid"}],"protocol":"canal-json"}}'</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h4 id="dumpling-逻辑备份"><a href="#dumpling-逻辑备份" class="headerlink" title="dumpling 逻辑备份"></a>dumpling 逻辑备份</h4><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment">### 如果有多张表,再加 -T 'database.table' 就行,由于我这里的数据比较敏感,就不放上来了</span>tiup dumpling <span class="token parameter variable">-uroot</span> -p<span class="token string">'xxx'</span> <span class="token parameter variable">-P4000</span> -h<span class="token string">'tidb-ip'</span> <span class="token parameter variable">--filetype</span> sql <span class="token parameter variable">-t</span> <span class="token number">1</span> <span class="token parameter variable">-o</span> /data1/test <span class="token parameter variable">-r</span> <span class="token number">200000</span> <span class="token parameter variable">-F256MiB</span> <span class="token parameter variable">-T</span> <span class="token string">'record.tbsendrcd'</span> <span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><h4 id="恢复数据"><a href="#恢复数据" class="headerlink" title="恢复数据"></a>恢复数据</h4><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token builtin class-name">cd</span> /data1/test<span class="token function">rename</span> record. test_record. *.sql<span class="token function">mv</span> record-schema-create.sql test_record-schema-create.sql<span class="token comment"># 再修改 创建库的 SQL 文件,将库名修改为 test_record</span><span class="token comment"># 恢复数据</span><span class="token function">nohup</span> tiup tidb-lightning <span class="token parameter variable">-config</span> tidb-lightning.toml <span class="token operator">></span> nohup.out <span class="token operator">&</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>tidb-lightning.toml</p><pre class="line-numbers language-toml" data-language="toml"><code class="language-toml"><span class="token punctuation">[</span><span class="token table class-name">lightning</span><span class="token punctuation">]</span><span class="token comment"># 日志</span><span class="token key property">level</span> <span class="token punctuation">=</span> <span class="token string">"info"</span><span class="token key property">file</span> <span class="token punctuation">=</span> <span class="token string">"tidb-lightning.log"</span><span class="token key property">max-size</span> <span class="token punctuation">=</span> <span class="token number">512</span> <span class="token comment"># MB</span><span class="token key property">max-days</span> <span class="token punctuation">=</span> <span class="token number">28</span><span class="token key property">max-backups</span> <span class="token punctuation">=</span> <span class="token number">14</span><span class="token comment"># 启动之前检查集群是否满足最低需求。</span><span class="token key property">check-requirements</span> <span class="token punctuation">=</span> <span class="token boolean">false</span><span class="token punctuation">[</span><span class="token table class-name">mydumper</span><span class="token punctuation">]</span><span class="token key property">data-source-dir</span> <span class="token punctuation">=</span> <span class="token string">"/data1/test"</span> <span class="token key property">filter</span> <span class="token punctuation">=</span> <span class="token punctuation">[</span><span class="token string">'*.*'</span><span class="token punctuation">,</span> <span class="token string">'!mysql.*'</span><span class="token punctuation">,</span> <span class="token string">'!sys.*'</span><span class="token punctuation">,</span> <span class="token string">'!INFORMATION_SCHEMA.*'</span><span class="token punctuation">,</span> <span class="token string">'!PERFORMANCE_SCHEMA.*'</span><span class="token punctuation">,</span> <span class="token string">'!METRICS_SCHEMA.*'</span><span class="token punctuation">,</span> <span class="token string">'!INSPECTION_SCHEMA.*'</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token table class-name">tikv-importer</span><span class="token punctuation">]</span><span class="token comment"># 导入模式配置,设为 tidb 即使用逻辑导入模式</span><span class="token key property">backend</span> <span class="token punctuation">=</span> <span class="token string">"tidb"</span><span class="token punctuation">[</span><span class="token table class-name">tidb</span><span class="token punctuation">]</span><span class="token comment"># 目标集群的信息。tidb-server 的地址,填一个即可。</span><span class="token key property">host</span> <span class="token punctuation">=</span> <span class="token string">"tidb-ip"</span><span class="token key property">port</span> <span class="token punctuation">=</span> <span class="token number">4000</span><span class="token key property">user</span> <span class="token punctuation">=</span> <span class="token string">"root"</span><span class="token comment"># 设置连接 TiDB 的密码,可为明文或 Base64 编码。</span><span class="token key property">password</span> <span class="token punctuation">=</span> <span class="token string">"xxx"</span><span class="token comment"># tidb-lightning 引用了 TiDB 库,并生成产生一些日志。</span><span class="token comment"># 设置 TiDB 库的日志等级。</span><span class="token key property">log-level</span> <span class="token punctuation">=</span> <span class="token string">"error"</span><span class="token punctuation">[</span><span class="token table class-name">checkpoint</span><span class="token punctuation">]</span><span class="token comment"># 启用断点续传。</span><span class="token comment"># 导入时,TiDB Lightning 会记录当前进度。</span><span class="token comment"># 若 TiDB Lightning 或其他组件异常退出,在重启时可以避免重复再导入已完成的数据。</span><span class="token key property">enable</span> <span class="token punctuation">=</span> <span class="token boolean">true</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>这时候就可以看到 ES 有数据写入了</p><h4 id="创建-CDC-任务-(正式库)"><a href="#创建-CDC-任务-(正式库)" class="headerlink" title="创建 CDC 任务 (正式库)"></a>创建 CDC 任务 (正式库)</h4><p>历史数据同步完后,先删除之前到 TICDC(临时库)</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">curl</span> <span class="token parameter variable">-X</span> DELETE http://127.0.0.1:8300/api/v1/changefeeds/record-sync-qy-all<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>再创建新的 TICDC(正式库),同时也要再创建正式库的 logstash<br>目前暂时不会有数据进来,因为我们现在新的 TIDB 是没有任何数据 CRUD 的,等到我们创建 TIDB(旧)到 TIDB(新)的 CDC 任务后,就会开始同步数据了</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">curl</span> <span class="token parameter variable">-X</span> POST <span class="token parameter variable">-H</span> <span class="token string">"Content-type: application/json"</span> http://127.0.0.1:8300/api/v1/changefeeds <span class="token parameter variable">-d</span> <span class="token string">'{"changefeed_id":"record-sync-qy","sink_uri":"kafka://b-1.xxx:9092,b-3.xxx:9092,b-2.xxx:9092/record-sync-qy?protocol=canal-json&kafka-version=2.8.1&partition-num=60&max-message-bytes=67108864&replication-factor=1","filter_rules":["record.tbsendrcd"],"sink_config":{"dispatchers":[{"matcher":["record.tbsendrcd"], "dispatcher":"rowid"}],"protocol":"canal-json"}}'</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h4 id="增量实时同步数据"><a href="#增量实时同步数据" class="headerlink" title="增量实时同步数据"></a>增量实时同步数据</h4><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># 在旧的的 tidb 操作</span><span class="token function">curl</span> <span class="token parameter variable">-X</span> POST http://127.0.0.1:8300/api/v1/changefeeds <span class="token parameter variable">-H</span> <span class="token string">'Content-Type: application/json'</span> <span class="token parameter variable">-d</span> <span class="token string">'{"changefeed_id": "upstream-to-downstream","sink_uri": "mysql://root:xxx@tidb-ip:4000","start_ts": 449317637862195222, # BR的时候记录的 BackupTS"config": {"enable-old-value": true,"force-replicate": true}}'</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># 等数据同步差不多了,恢复 GC 的时间</span>SET GLOBAL tidb_gc_life_time <span class="token operator">=</span> <span class="token string">'24h'</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>到这里就完成迁移了,有任何好的建议方案可以私聊我,谢谢大家</p>]]></content>
<tags>
<tag> TIDB </tag>
<tag> 迁移 </tag>
<tag> TIDB-lightning </tag>
<tag> TICDC </tag>
</tags>
</entry>
<entry>
<title>开发mutating webhook(修改statefulset的nodeselector)</title>
<link href="/2024/04/09/kai-fa-mutating-webhook-xiu-gai-statefulset-de-nodeselector/"/>
<url>/2024/04/09/kai-fa-mutating-webhook-xiu-gai-statefulset-de-nodeselector/</url>
<content type="html"><![CDATA[<h2 id="何为-Admission-Webhook"><a href="#何为-Admission-Webhook" class="headerlink" title="何为 Admission Webhook"></a>何为 Admission Webhook</h2><p><strong>官网定义</strong><br>Admission webhook 是一种用于接收准入请求并对其进行处理的 HTTP 回调机制。可以定义两种类型的 admission webhook,即 validating admission webhook 和 mutating admission webhook。Mutating admission webhook 会先被调用。它们可以更改发送到 API 服务器的对象以执行自定义的设置默认值操作。</p><p><img src="/images/webhook-img.png"></p><p>接下来,本文将采用 Kubernetes 提供的 Mutating Admission Webhook 这一机制,来实现 statufulset 中修改或者新增 Pod NodeSelector 的,我们每次发送请求调用 API 创建 Pod 的时候,Pod 的 spec 信息会被先修改,再存储。如此一来,工作节点上的 Kublet 创建 Pod 的时候,将会预置NodeSelector。</p><h3 id="需求"><a href="#需求" class="headerlink" title="需求"></a>需求</h3><p>将 statufulset 中 索引是单数的 Pod 设置 NodeSelector 标签,标签的 key 为 “name”,value 为 “even”。双数的 Pod 设置 NodeSelector 标签,标签的 key 为 “name”,value 为 “odd”。 custom-node-selector: ‘{“key”: “name”, “even”: “node1”, “odd”: “node2”}’</p><h3 id="思路"><a href="#思路" class="headerlink" title="思路"></a>思路</h3><p>不能影响到其他的服务,所以需要通过 Mutating Admission Webhook 机制,在创建 Pod 之前,通过 Pod 的 annotations 或者 labels 去判断是否经过 Mutating Admission Webhook,我们这里用 labels 去过滤是否启用 mutating 规则。 当 Pod 的 labels 中有 <strong>“ab-build-different: ‘true’”</strong> 的时候才去执行</p><h3 id="解决"><a href="#解决" class="headerlink" title="解决"></a>解决</h3><p>部分代码解释<br>如果没有”ab-build-different: ‘true’”标签,直接放行</p><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">if</span> pod<span class="token punctuation">.</span>Labels<span class="token punctuation">[</span>AppConfig<span class="token punctuation">.</span>PodAnnotationKey<span class="token punctuation">]</span> <span class="token operator">!=</span> <span class="token string">"true"</span> <span class="token punctuation">{</span>log<span class="token punctuation">.</span><span class="token function">Infof</span><span class="token punctuation">(</span><span class="token string">"Pod %s 没有注解 ab-build-different=true"</span><span class="token punctuation">,</span> pod<span class="token punctuation">.</span>Name<span class="token punctuation">)</span>resp<span class="token punctuation">.</span>Allowed <span class="token operator">=</span> <span class="token boolean">true</span><span class="token keyword">return</span> <span class="token boolean">nil</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>解析 json,我们这里的 annotations 是 <strong>custom-node-selector: ‘{“key”: “name”, “even”: “node1”, “odd”: “node2”}’</strong><br>所以这段代码最后输出会是 name node1 或者 name node2,即是我们想要用来新增或修改的 Pod 的 nodeSelector</p><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">func</span> <span class="token function">getNodeSelectorValue</span><span class="token punctuation">(</span>pod <span class="token operator">*</span>corev1<span class="token punctuation">.</span>Pod<span class="token punctuation">,</span> index <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token builtin">string</span><span class="token punctuation">,</span> <span class="token builtin">string</span><span class="token punctuation">,</span> <span class="token builtin">error</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>selectorJSON<span class="token punctuation">,</span> ok <span class="token operator">:=</span> pod<span class="token punctuation">.</span>Annotations<span class="token punctuation">[</span><span class="token string">"custom-node-selector"</span><span class="token punctuation">]</span><span class="token keyword">if</span> <span class="token operator">!</span>ok <span class="token punctuation">{</span><span class="token keyword">return</span> <span class="token string">""</span><span class="token punctuation">,</span> <span class="token string">""</span><span class="token punctuation">,</span> fmt<span class="token punctuation">.</span><span class="token function">Errorf</span><span class="token punctuation">(</span><span class="token string">"注解 %s 不存在"</span><span class="token punctuation">,</span> <span class="token string">"custom-node-selector"</span><span class="token punctuation">)</span><span class="token punctuation">}</span>log<span class="token punctuation">.</span><span class="token function">Debugf</span><span class="token punctuation">(</span><span class="token string">"selectorJSON: %s"</span><span class="token punctuation">,</span> selectorJSON<span class="token punctuation">)</span> <span class="token comment">// 打印查看实际的 JSON 字符串</span><span class="token keyword">var</span> customSelector CustomNodeSelectorerr <span class="token operator">:=</span> json<span class="token punctuation">.</span><span class="token function">Unmarshal</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token function">byte</span><span class="token punctuation">(</span>selectorJSON<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token operator">&</span>customSelector<span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span><span class="token keyword">return</span> <span class="token string">""</span><span class="token punctuation">,</span> <span class="token string">""</span><span class="token punctuation">,</span> fmt<span class="token punctuation">.</span><span class="token function">Errorf</span><span class="token punctuation">(</span><span class="token string">"解析自定义 nodeSelector 注解失败: %v"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">if</span> index<span class="token operator">%</span><span class="token number">2</span> <span class="token operator">==</span> <span class="token number">0</span> <span class="token punctuation">{</span><span class="token keyword">return</span> customSelector<span class="token punctuation">.</span>Key<span class="token punctuation">,</span> customSelector<span class="token punctuation">.</span>Even<span class="token punctuation">,</span> <span class="token boolean">nil</span><span class="token punctuation">}</span><span class="token keyword">return</span> customSelector<span class="token punctuation">.</span>Key<span class="token punctuation">,</span> customSelector<span class="token punctuation">.</span>Odd<span class="token punctuation">,</span> <span class="token boolean">nil</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>完整代码</strong><br>包含 具体逻辑实现, 钉钉通知</p><pre class="line-numbers language-go" data-language="go"><code class="language-go"><span class="token keyword">package</span> main<span class="token keyword">import</span> <span class="token punctuation">(</span><span class="token string">"bytes"</span><span class="token string">"encoding/json"</span><span class="token string">"fmt"</span><span class="token string">"io"</span> <span class="token string">"github.com/sirupsen/logrus"</span>v1 <span class="token string">"k8s.io/api/admission/v1"</span>corev1 <span class="token string">"k8s.io/api/core/v1"</span><span class="token string">"net/http"</span><span class="token string">"strconv"</span><span class="token string">"strings"</span><span class="token punctuation">)</span><span class="token comment">// Config 结构体存储配置信息</span><span class="token keyword">type</span> Config <span class="token keyword">struct</span> <span class="token punctuation">{</span>WebhookServerPort <span class="token builtin">string</span> <span class="token comment">// Webhook 服务器端口</span>TLSCertPath <span class="token builtin">string</span> <span class="token comment">// TLS 证书路径</span>TLSKeyPath <span class="token builtin">string</span> <span class="token comment">// TLS 密钥路径</span>DingTalkWebhookURL <span class="token builtin">string</span> <span class="token comment">// 钉钉机器人 Webhook URL</span>DingTalkSecret <span class="token builtin">string</span> <span class="token comment">// 钉钉机器人 Webhook Secret</span>DingTalkMessageType <span class="token builtin">string</span> <span class="token comment">// 钉钉消息类型</span>PodAnnotationKey <span class="token builtin">string</span> <span class="token comment">// Pod 注解键</span>DefaultStatefulSetName <span class="token builtin">string</span> <span class="token comment">// 默认的 StatefulSet 名称,如果无法从 Pod ownerReferences 中获取</span><span class="token punctuation">}</span><span class="token comment">// AppConfig 是全局配置实例</span><span class="token keyword">var</span> AppConfig <span class="token operator">=</span> Config<span class="token punctuation">{</span>WebhookServerPort<span class="token punctuation">:</span> <span class="token string">":8443"</span><span class="token punctuation">,</span>TLSCertPath<span class="token punctuation">:</span> <span class="token string">"/etc/webhook/certs/tls.crt"</span><span class="token punctuation">,</span>TLSKeyPath<span class="token punctuation">:</span> <span class="token string">"/etc/webhook/certs/tls.key"</span><span class="token punctuation">,</span>DingTalkWebhookURL<span class="token punctuation">:</span> <span class="token string">"https://oapi.dingtalk.com/robot/send?access_token=xxx"</span><span class="token punctuation">,</span>DingTalkSecret<span class="token punctuation">:</span> <span class="token string">"xxx"</span><span class="token punctuation">,</span>DingTalkMessageType<span class="token punctuation">:</span> <span class="token string">"markdown"</span><span class="token punctuation">,</span> <span class="token comment">// 可以是 "text" 或 "markdown"</span>PodAnnotationKey<span class="token punctuation">:</span> <span class="token string">"ab-build-different"</span><span class="token punctuation">,</span> <span class="token comment">// pod 打 annotations</span>DefaultStatefulSetName<span class="token punctuation">:</span> <span class="token string">"my-custom-sts"</span><span class="token punctuation">,</span> <span class="token comment">// 默认的 StatefulSet 名称,如果无法从 Pod ownerReferences 中获取</span><span class="token punctuation">}</span><span class="token keyword">type</span> CustomNodeSelector <span class="token keyword">struct</span> <span class="token punctuation">{</span>Key <span class="token builtin">string</span> <span class="token string">`json:"key"`</span>Even <span class="token builtin">string</span> <span class="token string">`json:"even"`</span>Odd <span class="token builtin">string</span> <span class="token string">`json:"odd"`</span><span class="token punctuation">}</span><span class="token keyword">type</span> DingTalkMessage <span class="token keyword">struct</span> <span class="token punctuation">{</span>Msgtype <span class="token builtin">string</span> <span class="token string">`json:"msgtype"`</span>Markdown <span class="token keyword">struct</span> <span class="token punctuation">{</span>Title <span class="token builtin">string</span> <span class="token string">`json:"title"`</span>Text <span class="token builtin">string</span> <span class="token string">`json:"text"`</span><span class="token punctuation">}</span> <span class="token string">`json:"markdown,omitempty"`</span>Text <span class="token keyword">struct</span> <span class="token punctuation">{</span>Content <span class="token builtin">string</span> <span class="token string">`json:"content"`</span><span class="token punctuation">}</span> <span class="token string">`json:"text,omitempty"`</span><span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">init</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token comment">// 设置日志输出格式为 JSON</span>log<span class="token punctuation">.</span><span class="token function">SetFormatter</span><span class="token punctuation">(</span><span class="token operator">&</span>logrus<span class="token punctuation">.</span>JSONFormatter<span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>http<span class="token punctuation">.</span><span class="token function">HandleFunc</span><span class="token punctuation">(</span><span class="token string">"/mutate"</span><span class="token punctuation">,</span> handleMutate<span class="token punctuation">)</span>log<span class="token punctuation">.</span><span class="token function">Info</span><span class="token punctuation">(</span><span class="token string">"服务启动,监听端口"</span><span class="token punctuation">,</span> AppConfig<span class="token punctuation">.</span>WebhookServerPort<span class="token punctuation">)</span><span class="token comment">// http.ListenAndServeTLS 是一个阻塞调用,持续运行直到发生错误</span>log<span class="token punctuation">.</span><span class="token function">Fatal</span><span class="token punctuation">(</span>http<span class="token punctuation">.</span><span class="token function">ListenAndServeTLS</span><span class="token punctuation">(</span>AppConfig<span class="token punctuation">.</span>WebhookServerPort<span class="token punctuation">,</span> AppConfig<span class="token punctuation">.</span>TLSCertPath<span class="token punctuation">,</span> AppConfig<span class="token punctuation">.</span>TLSKeyPath<span class="token punctuation">,</span> <span class="token boolean">nil</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">// handleMutate 处理来自 Kubernetes API 的 HTTP 请求</span><span class="token keyword">func</span> <span class="token function">handleMutate</span><span class="token punctuation">(</span>w http<span class="token punctuation">.</span>ResponseWriter<span class="token punctuation">,</span> r <span class="token operator">*</span>http<span class="token punctuation">.</span>Request<span class="token punctuation">)</span> <span class="token punctuation">{</span>log<span class="token punctuation">.</span><span class="token function">Info</span><span class="token punctuation">(</span><span class="token string">"收到 mutate 请求"</span><span class="token punctuation">)</span><span class="token comment">// 读取请求体</span>body<span class="token punctuation">,</span> err <span class="token operator">:=</span> io<span class="token punctuation">.</span><span class="token function">ReadAll</span><span class="token punctuation">(</span>r<span class="token punctuation">.</span>Body<span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>log<span class="token punctuation">.</span><span class="token function">Errorf</span><span class="token punctuation">(</span><span class="token string">"无法读取请求体: %v"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span>http<span class="token punctuation">.</span><span class="token function">Error</span><span class="token punctuation">(</span>w<span class="token punctuation">,</span> fmt<span class="token punctuation">.</span><span class="token function">Sprintf</span><span class="token punctuation">(</span><span class="token string">"无法读取请求体: %v"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token punctuation">,</span> http<span class="token punctuation">.</span>StatusInternalServerError<span class="token punctuation">)</span><span class="token keyword">return</span><span class="token punctuation">}</span><span class="token comment">// 解析 AdmissionReview 请求</span><span class="token keyword">var</span> admissionReviewReq v1<span class="token punctuation">.</span>AdmissionReview<span class="token keyword">if</span> err <span class="token operator">:=</span> json<span class="token punctuation">.</span><span class="token function">NewDecoder</span><span class="token punctuation">(</span>bytes<span class="token punctuation">.</span><span class="token function">NewReader</span><span class="token punctuation">(</span>body<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">Decode</span><span class="token punctuation">(</span><span class="token operator">&</span>admissionReviewReq<span class="token punctuation">)</span><span class="token punctuation">;</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>log<span class="token punctuation">.</span><span class="token function">Errorf</span><span class="token punctuation">(</span><span class="token string">"无法解析请求: %v"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span>http<span class="token punctuation">.</span><span class="token function">Error</span><span class="token punctuation">(</span>w<span class="token punctuation">,</span> fmt<span class="token punctuation">.</span><span class="token function">Sprintf</span><span class="token punctuation">(</span><span class="token string">"无法解析请求: %v"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token punctuation">,</span> http<span class="token punctuation">.</span>StatusBadRequest<span class="token punctuation">)</span><span class="token keyword">return</span><span class="token punctuation">}</span><span class="token comment">// 准备 AdmissionReview 响应</span>admissionReviewResp <span class="token operator">:=</span> v1<span class="token punctuation">.</span>AdmissionReview<span class="token punctuation">{</span>TypeMeta<span class="token punctuation">:</span> admissionReviewReq<span class="token punctuation">.</span>TypeMeta<span class="token punctuation">,</span>Response<span class="token punctuation">:</span> <span class="token operator">&</span>v1<span class="token punctuation">.</span>AdmissionResponse<span class="token punctuation">{</span>UID<span class="token punctuation">:</span> admissionReviewReq<span class="token punctuation">.</span>Request<span class="token punctuation">.</span>UID<span class="token punctuation">,</span><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">}</span><span class="token comment">// 处理请求并设置响应</span><span class="token keyword">if</span> err <span class="token operator">:=</span> <span class="token function">mutatePod</span><span class="token punctuation">(</span>admissionReviewReq<span class="token punctuation">.</span>Request<span class="token punctuation">,</span> admissionReviewResp<span class="token punctuation">.</span>Response<span class="token punctuation">)</span><span class="token punctuation">;</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>log<span class="token punctuation">.</span><span class="token function">Errorf</span><span class="token punctuation">(</span><span class="token string">"无法修改 Pod: %v"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token function">sendDingTalkNotification</span><span class="token punctuation">(</span>fmt<span class="token punctuation">.</span><span class="token function">Sprintf</span><span class="token punctuation">(</span><span class="token string">"无法修改 Pod: %v"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token punctuation">)</span>http<span class="token punctuation">.</span><span class="token function">Error</span><span class="token punctuation">(</span>w<span class="token punctuation">,</span> fmt<span class="token punctuation">.</span><span class="token function">Sprintf</span><span class="token punctuation">(</span><span class="token string">"无法修改 Pod: %v"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token punctuation">,</span> http<span class="token punctuation">.</span>StatusInternalServerError<span class="token punctuation">)</span><span class="token keyword">return</span><span class="token punctuation">}</span><span class="token comment">// 发送响应</span>respBytes<span class="token punctuation">,</span> err <span class="token operator">:=</span> json<span class="token punctuation">.</span><span class="token function">Marshal</span><span class="token punctuation">(</span>admissionReviewResp<span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>log<span class="token punctuation">.</span><span class="token function">Errorf</span><span class="token punctuation">(</span><span class="token string">"无法编码响应: %v"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span>http<span class="token punctuation">.</span><span class="token function">Error</span><span class="token punctuation">(</span>w<span class="token punctuation">,</span> fmt<span class="token punctuation">.</span><span class="token function">Sprintf</span><span class="token punctuation">(</span><span class="token string">"无法编码响应: %v"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token punctuation">,</span> http<span class="token punctuation">.</span>StatusInternalServerError<span class="token punctuation">)</span><span class="token keyword">return</span><span class="token punctuation">}</span>w<span class="token punctuation">.</span><span class="token function">Header</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">Set</span><span class="token punctuation">(</span><span class="token string">"Content-Type"</span><span class="token punctuation">,</span> <span class="token string">"application/json"</span><span class="token punctuation">)</span>w<span class="token punctuation">.</span><span class="token function">WriteHeader</span><span class="token punctuation">(</span>http<span class="token punctuation">.</span>StatusOK<span class="token punctuation">)</span>w<span class="token punctuation">.</span><span class="token function">Write</span><span class="token punctuation">(</span>respBytes<span class="token punctuation">)</span>log<span class="token punctuation">.</span><span class="token function">Info</span><span class="token punctuation">(</span><span class="token string">"mutate 请求处理完成"</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">// 处理请求并设置响应</span><span class="token keyword">func</span> <span class="token function">mutatePod</span><span class="token punctuation">(</span>req <span class="token operator">*</span>v1<span class="token punctuation">.</span>AdmissionRequest<span class="token punctuation">,</span> resp <span class="token operator">*</span>v1<span class="token punctuation">.</span>AdmissionResponse<span class="token punctuation">)</span> <span class="token builtin">error</span> <span class="token punctuation">{</span><span class="token comment">// 从请求中反序列化 Pod 对象</span><span class="token keyword">var</span> pod corev1<span class="token punctuation">.</span>Pod<span class="token keyword">if</span> err <span class="token operator">:=</span> json<span class="token punctuation">.</span><span class="token function">Unmarshal</span><span class="token punctuation">(</span>req<span class="token punctuation">.</span>Object<span class="token punctuation">.</span>Raw<span class="token punctuation">,</span> <span class="token operator">&</span>pod<span class="token punctuation">)</span><span class="token punctuation">;</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span>errorMsg <span class="token operator">:=</span> fmt<span class="token punctuation">.</span><span class="token function">Sprintf</span><span class="token punctuation">(</span><span class="token string">"无法反序列化 Pod 对象: %v"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span>log<span class="token punctuation">.</span><span class="token function">Error</span><span class="token punctuation">(</span>errorMsg<span class="token punctuation">)</span><span class="token function">sendDingTalkNotification</span><span class="token punctuation">(</span>errorMsg<span class="token punctuation">)</span> <span class="token comment">// 发送钉钉通知</span><span class="token keyword">return</span> err<span class="token punctuation">}</span><span class="token comment">// 检查 Pod 是否有注解 "ab-build-different: 'true'"</span><span class="token keyword">if</span> pod<span class="token punctuation">.</span>Labels<span class="token punctuation">[</span>AppConfig<span class="token punctuation">.</span>PodAnnotationKey<span class="token punctuation">]</span> <span class="token operator">!=</span> <span class="token string">"true"</span> <span class="token punctuation">{</span>log<span class="token punctuation">.</span><span class="token function">Infof</span><span class="token punctuation">(</span><span class="token string">"Pod %s 没有注解 ab-build-different=true"</span><span class="token punctuation">,</span> pod<span class="token punctuation">.</span>Name<span class="token punctuation">)</span>resp<span class="token punctuation">.</span>Allowed <span class="token operator">=</span> <span class="token boolean">true</span><span class="token keyword">return</span> <span class="token boolean">nil</span><span class="token punctuation">}</span><span class="token comment">// 从 Pod 的 ownerReferences 中提取 StatefulSet 名称</span><span class="token keyword">var</span> statefulSetName <span class="token builtin">string</span><span class="token keyword">for</span> <span class="token boolean">_</span><span class="token punctuation">,</span> ownerRef <span class="token operator">:=</span> <span class="token keyword">range</span> pod<span class="token punctuation">.</span>OwnerReferences <span class="token punctuation">{</span><span class="token keyword">if</span> ownerRef<span class="token punctuation">.</span>Kind <span class="token operator">==</span> <span class="token string">"StatefulSet"</span> <span class="token punctuation">{</span>statefulSetName <span class="token operator">=</span> ownerRef<span class="token punctuation">.</span>Name<span class="token keyword">break</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token keyword">if</span> statefulSetName <span class="token operator">==</span> <span class="token string">""</span> <span class="token punctuation">{</span><span class="token keyword">return</span> fmt<span class="token punctuation">.</span><span class="token function">Errorf</span><span class="token punctuation">(</span><span class="token string">"在 Pod 的 ownerReferences 中找不到 StatefulSet 名称"</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token comment">// 从 Pod 的名称中提取索引</span>indexStr <span class="token operator">:=</span> strings<span class="token punctuation">.</span><span class="token function">TrimPrefix</span><span class="token punctuation">(</span>pod<span class="token punctuation">.</span>Name<span class="token punctuation">,</span> statefulSetName<span class="token operator">+</span><span class="token string">"-"</span><span class="token punctuation">)</span>index<span class="token punctuation">,</span> err <span class="token operator">:=</span> strconv<span class="token punctuation">.</span><span class="token function">Atoi</span><span class="token punctuation">(</span>indexStr<span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span><span class="token keyword">return</span> fmt<span class="token punctuation">.</span><span class="token function">Errorf</span><span class="token punctuation">(</span><span class="token string">"无法从 Pod 名称中提取索引: %w"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token punctuation">}</span>log<span class="token punctuation">.</span><span class="token function">Infof</span><span class="token punctuation">(</span><span class="token string">"正在处理 Pod %s-%d 的 nodeSelector 修改请求"</span><span class="token punctuation">,</span> statefulSetName<span class="token punctuation">,</span> index<span class="token punctuation">)</span><span class="token comment">// 获取自定义 nodeSelector 键和值</span>nodeSelectorKey<span class="token punctuation">,</span> nodeSelectorValue<span class="token punctuation">,</span> err <span class="token operator">:=</span> <span class="token function">getNodeSelectorValue</span><span class="token punctuation">(</span><span class="token operator">&</span>pod<span class="token punctuation">,</span> index<span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span><span class="token keyword">return</span> err<span class="token punctuation">}</span><span class="token comment">// 创建 patch</span>patch <span class="token operator">:=</span> <span class="token function">createNodeSelectorPatch</span><span class="token punctuation">(</span>pod<span class="token punctuation">.</span>Spec<span class="token punctuation">.</span>NodeSelector<span class="token punctuation">,</span> nodeSelectorKey<span class="token punctuation">,</span> nodeSelectorValue<span class="token punctuation">)</span><span class="token comment">// 序列化 patch 并将其设置在响应中</span>patchBytes<span class="token punctuation">,</span> err <span class="token operator">:=</span> json<span class="token punctuation">.</span><span class="token function">Marshal</span><span class="token punctuation">(</span>patch<span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span><span class="token keyword">return</span> fmt<span class="token punctuation">.</span><span class="token function">Errorf</span><span class="token punctuation">(</span><span class="token string">"无法序列化 patch: %w"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token punctuation">}</span>resp<span class="token punctuation">.</span>Patch <span class="token operator">=</span> patchBytesresp<span class="token punctuation">.</span>PatchType <span class="token operator">=</span> <span class="token function">new</span><span class="token punctuation">(</span>v1<span class="token punctuation">.</span>PatchType<span class="token punctuation">)</span><span class="token operator">*</span>resp<span class="token punctuation">.</span>PatchType <span class="token operator">=</span> v1<span class="token punctuation">.</span>PatchTypeJSONPatchresp<span class="token punctuation">.</span>Allowed <span class="token operator">=</span> <span class="token boolean">true</span>log<span class="token punctuation">.</span><span class="token function">Infof</span><span class="token punctuation">(</span><span class="token string">"成功修改 Pod %s-%d 的 nodeSelector 为 %s:%s"</span><span class="token punctuation">,</span> statefulSetName<span class="token punctuation">,</span> index<span class="token punctuation">,</span> nodeSelectorKey<span class="token punctuation">,</span> nodeSelectorValue<span class="token punctuation">)</span><span class="token function">sendDingTalkNotification</span><span class="token punctuation">(</span>fmt<span class="token punctuation">.</span><span class="token function">Sprintf</span><span class="token punctuation">(</span><span class="token string">"成功修改 Pod %s-%d 的 nodeSelector 为 %s:%s"</span><span class="token punctuation">,</span> statefulSetName<span class="token punctuation">,</span> index<span class="token punctuation">,</span> nodeSelectorKey<span class="token punctuation">,</span> nodeSelectorValue<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token keyword">return</span> <span class="token boolean">nil</span><span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">getNodeSelectorValue</span><span class="token punctuation">(</span>pod <span class="token operator">*</span>corev1<span class="token punctuation">.</span>Pod<span class="token punctuation">,</span> index <span class="token builtin">int</span><span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token builtin">string</span><span class="token punctuation">,</span> <span class="token builtin">string</span><span class="token punctuation">,</span> <span class="token builtin">error</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>selectorJSON<span class="token punctuation">,</span> ok <span class="token operator">:=</span> pod<span class="token punctuation">.</span>Annotations<span class="token punctuation">[</span><span class="token string">"custom-node-selector"</span><span class="token punctuation">]</span><span class="token keyword">if</span> <span class="token operator">!</span>ok <span class="token punctuation">{</span><span class="token keyword">return</span> <span class="token string">""</span><span class="token punctuation">,</span> <span class="token string">""</span><span class="token punctuation">,</span> fmt<span class="token punctuation">.</span><span class="token function">Errorf</span><span class="token punctuation">(</span><span class="token string">"注解 %s 不存在"</span><span class="token punctuation">,</span> <span class="token string">"custom-node-selector"</span><span class="token punctuation">)</span><span class="token punctuation">}</span>log<span class="token punctuation">.</span><span class="token function">Debugf</span><span class="token punctuation">(</span><span class="token string">"selectorJSON: %s"</span><span class="token punctuation">,</span> selectorJSON<span class="token punctuation">)</span> <span class="token comment">// 打印查看实际的 JSON 字符串</span><span class="token keyword">var</span> customSelector CustomNodeSelectorerr <span class="token operator">:=</span> json<span class="token punctuation">.</span><span class="token function">Unmarshal</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token function">byte</span><span class="token punctuation">(</span>selectorJSON<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token operator">&</span>customSelector<span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span><span class="token keyword">return</span> <span class="token string">""</span><span class="token punctuation">,</span> <span class="token string">""</span><span class="token punctuation">,</span> fmt<span class="token punctuation">.</span><span class="token function">Errorf</span><span class="token punctuation">(</span><span class="token string">"解析自定义 nodeSelector 注解失败: %v"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">if</span> index<span class="token operator">%</span><span class="token number">2</span> <span class="token operator">==</span> <span class="token number">0</span> <span class="token punctuation">{</span><span class="token keyword">return</span> customSelector<span class="token punctuation">.</span>Key<span class="token punctuation">,</span> customSelector<span class="token punctuation">.</span>Even<span class="token punctuation">,</span> <span class="token boolean">nil</span><span class="token punctuation">}</span><span class="token keyword">return</span> customSelector<span class="token punctuation">.</span>Key<span class="token punctuation">,</span> customSelector<span class="token punctuation">.</span>Odd<span class="token punctuation">,</span> <span class="token boolean">nil</span><span class="token punctuation">}</span><span class="token keyword">func</span> <span class="token function">createNodeSelectorPatch</span><span class="token punctuation">(</span>existingNodeSelector <span class="token keyword">map</span><span class="token punctuation">[</span><span class="token builtin">string</span><span class="token punctuation">]</span><span class="token builtin">string</span><span class="token punctuation">,</span> nodeSelectorKey<span class="token punctuation">,</span> nodeSelectorValue <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token keyword">map</span><span class="token punctuation">[</span><span class="token builtin">string</span><span class="token punctuation">]</span><span class="token keyword">interface</span><span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token punctuation">{</span><span class="token keyword">var</span> patch <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token keyword">map</span><span class="token punctuation">[</span><span class="token builtin">string</span><span class="token punctuation">]</span><span class="token keyword">interface</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token keyword">if</span> existingNodeSelector <span class="token operator">==</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span><span class="token comment">// NodeSelector 为空,直接添加新的 NodeSelector</span>patch <span class="token operator">=</span> <span class="token function">append</span><span class="token punctuation">(</span>patch<span class="token punctuation">,</span> <span class="token keyword">map</span><span class="token punctuation">[</span><span class="token builtin">string</span><span class="token punctuation">]</span><span class="token keyword">interface</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">{</span><span class="token string">"op"</span><span class="token punctuation">:</span> <span class="token string">"add"</span><span class="token punctuation">,</span><span class="token string">"path"</span><span class="token punctuation">:</span> <span class="token string">"/spec/nodeSelector"</span><span class="token punctuation">,</span><span class="token string">"value"</span><span class="token punctuation">:</span> <span class="token keyword">map</span><span class="token punctuation">[</span><span class="token builtin">string</span><span class="token punctuation">]</span><span class="token builtin">string</span><span class="token punctuation">{</span>nodeSelectorKey<span class="token punctuation">:</span> nodeSelectorValue<span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span><span class="token comment">// NodeSelector 非空,直接替换整个 NodeSelector</span>newSelector <span class="token operator">:=</span> <span class="token keyword">map</span><span class="token punctuation">[</span><span class="token builtin">string</span><span class="token punctuation">]</span><span class="token builtin">string</span><span class="token punctuation">{</span>nodeSelectorKey<span class="token punctuation">:</span> nodeSelectorValue<span class="token punctuation">}</span>patch <span class="token operator">=</span> <span class="token function">append</span><span class="token punctuation">(</span>patch<span class="token punctuation">,</span> <span class="token keyword">map</span><span class="token punctuation">[</span><span class="token builtin">string</span><span class="token punctuation">]</span><span class="token keyword">interface</span><span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">{</span><span class="token string">"op"</span><span class="token punctuation">:</span> <span class="token string">"replace"</span><span class="token punctuation">,</span><span class="token string">"path"</span><span class="token punctuation">:</span> <span class="token string">"/spec/nodeSelector"</span><span class="token punctuation">,</span><span class="token string">"value"</span><span class="token punctuation">:</span> newSelector<span class="token punctuation">,</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">return</span> patch<span class="token punctuation">}</span><span class="token comment">// sendDingTalkNotification 发送通知到钉钉</span><span class="token keyword">func</span> <span class="token function">sendDingTalkNotification</span><span class="token punctuation">(</span>message <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token builtin">error</span> <span class="token punctuation">{</span>sign<span class="token punctuation">,</span> timestamp <span class="token operator">:=</span> <span class="token function">generateDingTalkSignature</span><span class="token punctuation">(</span><span class="token punctuation">)</span>log<span class="token punctuation">.</span><span class="token function">Infof</span><span class="token punctuation">(</span><span class="token string">"generateDingTalkSignature执行成功,%s , %s"</span><span class="token punctuation">,</span> sign<span class="token punctuation">,</span> timestamp<span class="token punctuation">)</span>msg <span class="token operator">:=</span> DingTalkMessage<span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token comment">//var content string</span><span class="token keyword">if</span> AppConfig<span class="token punctuation">.</span>DingTalkMessageType <span class="token operator">==</span> <span class="token string">"markdown"</span> <span class="token punctuation">{</span>msg<span class="token punctuation">.</span>Msgtype <span class="token operator">=</span> <span class="token string">"markdown"</span>msg<span class="token punctuation">.</span>Markdown<span class="token punctuation">.</span>Title <span class="token operator">=</span> <span class="token string">"Mutating Webhook Notification"</span>msg<span class="token punctuation">.</span>Markdown<span class="token punctuation">.</span>Text <span class="token operator">=</span> fmt<span class="token punctuation">.</span><span class="token function">Sprintf</span><span class="token punctuation">(</span><span class="token string">"###Mutating Webhook Notification \n > %s"</span><span class="token punctuation">,</span> message<span class="token punctuation">)</span><span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>msg<span class="token punctuation">.</span>Msgtype <span class="token operator">=</span> <span class="token string">"text"</span>msg<span class="token punctuation">.</span>Text<span class="token punctuation">.</span>Content <span class="token operator">=</span> message<span class="token punctuation">}</span>msgBytes<span class="token punctuation">,</span> err <span class="token operator">:=</span> json<span class="token punctuation">.</span><span class="token function">Marshal</span><span class="token punctuation">(</span>msg<span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span><span class="token keyword">return</span> err<span class="token punctuation">}</span>tokenDingTalkUrl <span class="token operator">:=</span> fmt<span class="token punctuation">.</span><span class="token function">Sprintf</span><span class="token punctuation">(</span><span class="token string">"%s&timestamp=%s&sign=%s"</span><span class="token punctuation">,</span> AppConfig<span class="token punctuation">.</span>DingTalkWebhookURL<span class="token punctuation">,</span> timestamp<span class="token punctuation">,</span> sign<span class="token punctuation">)</span>resp<span class="token punctuation">,</span> err <span class="token operator">:=</span> http<span class="token punctuation">.</span><span class="token function">Post</span><span class="token punctuation">(</span>tokenDingTalkUrl<span class="token punctuation">,</span> <span class="token string">"application/json"</span><span class="token punctuation">,</span> bytes<span class="token punctuation">.</span><span class="token function">NewBuffer</span><span class="token punctuation">(</span>msgBytes<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span><span class="token keyword">return</span> err<span class="token punctuation">}</span>fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span>resp<span class="token punctuation">.</span>Status<span class="token punctuation">,</span> resp<span class="token punctuation">.</span>Body<span class="token punctuation">)</span><span class="token keyword">defer</span> resp<span class="token punctuation">.</span>Body<span class="token punctuation">.</span><span class="token function">Close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token comment">// 读取响应体</span>body<span class="token punctuation">,</span> err <span class="token operator">:=</span> io<span class="token punctuation">.</span><span class="token function">ReadAll</span><span class="token punctuation">(</span>resp<span class="token punctuation">.</span>Body<span class="token punctuation">)</span><span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span><span class="token keyword">return</span> err<span class="token punctuation">}</span><span class="token keyword">if</span> resp<span class="token punctuation">.</span>StatusCode <span class="token operator">!=</span> http<span class="token punctuation">.</span>StatusOK <span class="token punctuation">{</span><span class="token keyword">return</span> fmt<span class="token punctuation">.</span><span class="token function">Errorf</span><span class="token punctuation">(</span><span class="token string">"发送钉钉消息失败, 状态吗: %s, 请求体: %s"</span><span class="token punctuation">,</span> resp<span class="token punctuation">.</span>Status<span class="token punctuation">,</span> <span class="token function">string</span><span class="token punctuation">(</span>body<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">return</span> <span class="token boolean">nil</span><span class="token punctuation">}</span><span class="token comment">// generateDingTalkSignature 生成钉钉签名</span><span class="token keyword">func</span> <span class="token function">generateDingTalkSignature</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token builtin">string</span><span class="token punctuation">,</span> <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>timestamp <span class="token operator">:=</span> strconv<span class="token punctuation">.</span><span class="token function">FormatInt</span><span class="token punctuation">(</span>time<span class="token punctuation">.</span><span class="token function">Now</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">UnixNano</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">/</span><span class="token function">int64</span><span class="token punctuation">(</span>time<span class="token punctuation">.</span>Millisecond<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">)</span>stringToSign <span class="token operator">:=</span> timestamp <span class="token operator">+</span> <span class="token string">"\n"</span> <span class="token operator">+</span> AppConfig<span class="token punctuation">.</span>DingTalkSecrethash <span class="token operator">:=</span> hmac<span class="token punctuation">.</span><span class="token function">New</span><span class="token punctuation">(</span>sha256<span class="token punctuation">.</span>New<span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token function">byte</span><span class="token punctuation">(</span>AppConfig<span class="token punctuation">.</span>DingTalkSecret<span class="token punctuation">)</span><span class="token punctuation">)</span>hash<span class="token punctuation">.</span><span class="token function">Write</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token function">byte</span><span class="token punctuation">(</span>stringToSign<span class="token punctuation">)</span><span class="token punctuation">)</span>signData <span class="token operator">:=</span> base64<span class="token punctuation">.</span>StdEncoding<span class="token punctuation">.</span><span class="token function">EncodeToString</span><span class="token punctuation">(</span>hash<span class="token punctuation">.</span><span class="token function">Sum</span><span class="token punctuation">(</span><span class="token boolean">nil</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token keyword">return</span> signData<span class="token punctuation">,</span> timestamp<span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="构建-Mutating-控制器镜像"><a href="#构建-Mutating-控制器镜像" class="headerlink" title="构建 Mutating 控制器镜像"></a>构建 Mutating 控制器镜像</h3><pre class="line-numbers language-Dockerfile" data-language="Dockerfile"><code class="language-Dockerfile"># 使用官方的 Go 基础镜像FROM golang:1.21 as builder# 设置工作目录WORKDIR /app# 将 Go 模块文件复制到容器中COPY go.mod go.sum ./# 下载 Go 模块依赖RUN go mod download# 将源代码复制到容器中COPY . .# 编译 Go 应用RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o webhookFROM alpine:latest# 有请求 https 请求必须安装RUN apk --no-cache add ca-certificates# 从构建阶段复制二进制文件和证书文件COPY --from=builder /app/webhook /webhook# 运行 webhookENTRYPOINT ["/webhook"]<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> buildx build <span class="token parameter variable">--platform</span> linux/amd64 <span class="token parameter variable">-t</span> xxx:v1 <span class="token parameter variable">--load</span> <span class="token builtin class-name">.</span><span class="token function">docker</span> push xxx:v1<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><h3 id="部署服务"><a href="#部署服务" class="headerlink" title="部署服务"></a>部署服务</h3><p>Webhook API 服务器需要通过 TLS 方式通信。如果想将其部署至 Kubernetes 集群内,我们还需要证书<br>这是我们直接用生成证书的脚本,会保存在secret里面,然后再deployment里面去引用<br>需要注意的是 signerName,eks 的 signerName 是 beta.eks.amazonaws.com/app-serving</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token shebang important">#!/bin/bash</span><span class="token builtin class-name">set</span> <span class="token parameter variable">-e</span><span class="token function-name function">usage</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">cat</span> <span class="token operator"><<</span><span class="token string">EOFGenerate certificate suitable for use with an sidecar-injector webhook service.This script uses k8s' CertificateSigningRequest API to a generate acertificate signed by k8s CA suitable for use with sidecar-injector webhookservices. This requires permissions to create and approve CSR. Seehttps://kubernetes.io/docs/tasks/tls/managing-tls-in-a-cluster fordetailed explantion and additional instructions.The server key/cert k8s CA cert are stored in a k8s secret.usage: <span class="token variable">${0}</span> [OPTIONS]The following flags are required. --service Service name of webhook. --namespace Namespace where webhook service and secret reside. --secret Secret name for CA certificate and server certificate/key pair.EOF</span> <span class="token builtin class-name">exit</span> <span class="token number">1</span><span class="token punctuation">}</span><span class="token keyword">while</span> <span class="token punctuation">[</span><span class="token punctuation">[</span> <span class="token variable">$#</span> <span class="token parameter variable">-gt</span> <span class="token number">0</span> <span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">do</span> <span class="token keyword">case</span> <span class="token variable">${1}</span> <span class="token keyword">in</span> --service<span class="token punctuation">)</span> <span class="token assign-left variable">service</span><span class="token operator">=</span><span class="token string">"<span class="token variable">$2</span>"</span> <span class="token builtin class-name">shift</span> <span class="token punctuation">;</span><span class="token punctuation">;</span> --secret<span class="token punctuation">)</span> <span class="token assign-left variable">secret</span><span class="token operator">=</span><span class="token string">"<span class="token variable">$2</span>"</span> <span class="token builtin class-name">shift</span> <span class="token punctuation">;</span><span class="token punctuation">;</span> --namespace<span class="token punctuation">)</span> <span class="token assign-left variable">namespace</span><span class="token operator">=</span><span class="token string">"<span class="token variable">$2</span>"</span> <span class="token builtin class-name">shift</span> <span class="token punctuation">;</span><span class="token punctuation">;</span> *<span class="token punctuation">)</span> usage <span class="token punctuation">;</span><span class="token punctuation">;</span> <span class="token keyword">esac</span> <span class="token builtin class-name">shift</span><span class="token keyword">done</span><span class="token punctuation">[</span> <span class="token parameter variable">-z</span> <span class="token variable">${service}</span> <span class="token punctuation">]</span> <span class="token operator">&&</span> <span class="token assign-left variable">service</span><span class="token operator">=</span>admission-webhook-example-svc<span class="token punctuation">[</span> <span class="token parameter variable">-z</span> <span class="token variable">${secret}</span> <span class="token punctuation">]</span> <span class="token operator">&&</span> <span class="token assign-left variable">secret</span><span class="token operator">=</span>admission-webhook-example-certs<span class="token punctuation">[</span> <span class="token parameter variable">-z</span> <span class="token variable">${namespace}</span> <span class="token punctuation">]</span> <span class="token operator">&&</span> <span class="token assign-left variable">namespace</span><span class="token operator">=</span>default<span class="token keyword">if</span> <span class="token punctuation">[</span> <span class="token operator">!</span> <span class="token parameter variable">-x</span> <span class="token string">"<span class="token variable"><span class="token variable">$(</span><span class="token builtin class-name">command</span> <span class="token parameter variable">-v</span> openssl<span class="token variable">)</span></span>"</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">then</span> <span class="token builtin class-name">echo</span> <span class="token string">"openssl not found"</span> <span class="token builtin class-name">exit</span> <span class="token number">1</span><span class="token keyword">fi</span><span class="token assign-left variable">csrName</span><span class="token operator">=</span><span class="token variable">${service}</span><span class="token builtin class-name">.</span><span class="token variable">${namespace}</span><span class="token assign-left variable">tmpdir</span><span class="token operator">=</span><span class="token variable"><span class="token variable">$(</span>mktemp <span class="token parameter variable">-d</span><span class="token variable">)</span></span><span class="token builtin class-name">echo</span> <span class="token string">"creating certs in tmpdir <span class="token variable">${tmpdir}</span> "</span><span class="token function">cat</span> <span class="token operator"><<</span><span class="token string">EOF<span class="token bash punctuation"> <span class="token operator">>></span> <span class="token variable">${tmpdir}</span>/csr.conf</span>[req]req_extensions = v3_reqdistinguished_name = req_distinguished_name[req_distinguished_name][ v3_req ]basicConstraints = CA:FALSEkeyUsage = nonRepudiation, digitalSignature, keyEnciphermentextendedKeyUsage = serverAuthsubjectAltName = @alt_names[alt_names]DNS.1 = <span class="token variable">${service}</span>DNS.2 = <span class="token variable">${service}</span>.<span class="token variable">${namespace}</span>DNS.3 = <span class="token variable">${service}</span>.<span class="token variable">${namespace}</span>.svcEOF</span>openssl genrsa <span class="token parameter variable">-out</span> <span class="token variable">${tmpdir}</span>/server-key.pem <span class="token number">2048</span>openssl req <span class="token parameter variable">-new</span> <span class="token parameter variable">-key</span> <span class="token variable">${tmpdir}</span>/server-key.pem <span class="token parameter variable">-subj</span> <span class="token string">"/CN=<span class="token variable">${service}</span>.<span class="token variable">${namespace}</span>.svc"</span> <span class="token parameter variable">-out</span> <span class="token variable">${tmpdir}</span>/server.csr <span class="token parameter variable">-config</span> <span class="token variable">${tmpdir}</span>/csr.conf<span class="token comment"># clean-up any previously created CSR for our service. Ignore errors if not present.</span>kubectl delete csr <span class="token variable">${csrName}</span> <span class="token operator"><span class="token file-descriptor important">2</span>></span>/dev/null <span class="token operator">||</span> <span class="token boolean">true</span><span class="token comment"># create server cert/key CSR and send to k8s API</span><span class="token function">cat</span> <span class="token operator"><<</span><span class="token string">EOF<span class="token bash punctuation"> <span class="token operator">|</span> kubectl create <span class="token parameter variable">-f</span> -</span>apiVersion: certificates.k8s.io/v1kind: CertificateSigningRequestmetadata: name: <span class="token variable">${csrName}</span>spec: groups: - system:authenticated request: <span class="token variable"><span class="token variable">$(</span><span class="token function">cat</span> $<span class="token punctuation">{</span>tmpdir<span class="token punctuation">}</span>/server.csr <span class="token operator">|</span> base64 <span class="token operator">|</span> <span class="token function">tr</span> <span class="token parameter variable">-d</span> <span class="token string">'\n'</span><span class="token variable">)</span></span> signerName: kubernetes.io/kube-apiserver-client usages: - digital signature - key encipherment - server authEOF</span><span class="token comment"># verify CSR has been created</span><span class="token keyword">while</span> <span class="token boolean">true</span><span class="token punctuation">;</span> <span class="token keyword">do</span> kubectl get csr <span class="token variable">${csrName}</span> <span class="token keyword">if</span> <span class="token punctuation">[</span> <span class="token string">"<span class="token variable">$?</span>"</span> <span class="token parameter variable">-eq</span> <span class="token number">0</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">then</span> <span class="token builtin class-name">break</span> <span class="token keyword">fi</span><span class="token keyword">done</span><span class="token comment"># approve and fetch the signed certificate</span>kubectl certificate approve <span class="token variable">${csrName}</span><span class="token comment"># verify certificate has been signed</span><span class="token keyword">for</span> <span class="token for-or-select variable">x</span> <span class="token keyword">in</span> <span class="token variable"><span class="token variable">$(</span><span class="token function">seq</span> <span class="token number">10</span><span class="token variable">)</span></span><span class="token punctuation">;</span> <span class="token keyword">do</span> <span class="token assign-left variable">serverCert</span><span class="token operator">=</span><span class="token variable"><span class="token variable">$(</span>kubectl get csr $<span class="token punctuation">{</span>csrName<span class="token punctuation">}</span> <span class="token parameter variable">-o</span> <span class="token assign-left variable">jsonpath</span><span class="token operator">=</span><span class="token string">'{.status.certificate}'</span><span class="token variable">)</span></span> <span class="token keyword">if</span> <span class="token punctuation">[</span><span class="token punctuation">[</span> <span class="token variable">${serverCert}</span> <span class="token operator">!=</span> <span class="token string">''</span> <span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">then</span> <span class="token builtin class-name">break</span> <span class="token keyword">fi</span> <span class="token function">sleep</span> <span class="token number">1</span><span class="token keyword">done</span><span class="token keyword">if</span> <span class="token punctuation">[</span><span class="token punctuation">[</span> <span class="token variable">${serverCert}</span> <span class="token operator">==</span> <span class="token string">''</span> <span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">then</span> <span class="token builtin class-name">echo</span> <span class="token string">"ERROR: After approving csr <span class="token variable">${csrName}</span>, the signed certificate did not appear on the resource. Giving up after 10 attempts."</span> <span class="token operator">></span><span class="token file-descriptor important">&2</span> <span class="token builtin class-name">exit</span> <span class="token number">1</span><span class="token keyword">fi</span><span class="token builtin class-name">echo</span> <span class="token variable">${serverCert}</span> <span class="token operator">|</span> openssl base64 <span class="token parameter variable">-d</span> <span class="token parameter variable">-A</span> <span class="token parameter variable">-out</span> <span class="token variable">${tmpdir}</span>/server-cert.pem<span class="token comment"># create the secret with CA cert and server cert/key</span>kubectl create secret generic <span class="token variable">${secret}</span> <span class="token punctuation">\</span> --from-file<span class="token operator">=</span>key.pem<span class="token operator">=</span><span class="token variable">${tmpdir}</span>/server-key.pem <span class="token punctuation">\</span> --from-file<span class="token operator">=</span>cert.pem<span class="token operator">=</span><span class="token variable">${tmpdir}</span>/server-cert.pem <span class="token punctuation">\</span> --dry-run <span class="token parameter variable">-o</span> yaml <span class="token operator">|</span> kubectl <span class="token parameter variable">-n</span> <span class="token variable">${namespace}</span> apply <span class="token parameter variable">-f</span> -<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><pre class="line-numbers language-yaml" data-language="yaml"><code class="language-yaml"><span class="token key atrule">apiVersion</span><span class="token punctuation">:</span> apps/v1<span class="token key atrule">kind</span><span class="token punctuation">:</span> Deployment<span class="token key atrule">metadata</span><span class="token punctuation">:</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> sts<span class="token punctuation">-</span>webhook <span class="token key atrule">namespace</span><span class="token punctuation">:</span> custom<span class="token punctuation">-</span>system<span class="token key atrule">spec</span><span class="token punctuation">:</span> <span class="token key atrule">replicas</span><span class="token punctuation">:</span> <span class="token number">1</span> <span class="token key atrule">selector</span><span class="token punctuation">:</span> <span class="token key atrule">matchLabels</span><span class="token punctuation">:</span> <span class="token key atrule">app</span><span class="token punctuation">:</span> sts<span class="token punctuation">-</span>webhook <span class="token key atrule">template</span><span class="token punctuation">:</span> <span class="token key atrule">metadata</span><span class="token punctuation">:</span> <span class="token key atrule">labels</span><span class="token punctuation">:</span> <span class="token key atrule">app</span><span class="token punctuation">:</span> sts<span class="token punctuation">-</span>webhook <span class="token key atrule">spec</span><span class="token punctuation">:</span> <span class="token key atrule">containers</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> sts<span class="token punctuation">-</span>webhook <span class="token key atrule">image</span><span class="token punctuation">:</span> xxx<span class="token punctuation">:</span>v1 <span class="token key atrule">ports</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token key atrule">containerPort</span><span class="token punctuation">:</span> <span class="token number">8443</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> http <span class="token key atrule">protocol</span><span class="token punctuation">:</span> TCP <span class="token key atrule">volumeMounts</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> webhook<span class="token punctuation">-</span>certs <span class="token key atrule">mountPath</span><span class="token punctuation">:</span> <span class="token string">"/etc/webhook/certs"</span> <span class="token key atrule">readOnly</span><span class="token punctuation">:</span> <span class="token boolean important">true</span> <span class="token key atrule">volumes</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> webhook<span class="token punctuation">-</span>certs <span class="token key atrule">secret</span><span class="token punctuation">:</span> <span class="token key atrule">secretName</span><span class="token punctuation">:</span> sts<span class="token punctuation">-</span>webhook<span class="token punctuation">-</span>certs <span class="token key atrule">items</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token key atrule">key</span><span class="token punctuation">:</span> cert.pem <span class="token key atrule">path</span><span class="token punctuation">:</span> tls.crt <span class="token punctuation">-</span> <span class="token key atrule">key</span><span class="token punctuation">:</span> key.pem <span class="token key atrule">path</span><span class="token punctuation">:</span> tls.key<span class="token punctuation">---</span><span class="token key atrule">apiVersion</span><span class="token punctuation">:</span> v1<span class="token key atrule">kind</span><span class="token punctuation">:</span> Service<span class="token key atrule">metadata</span><span class="token punctuation">:</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> sts<span class="token punctuation">-</span>webhook <span class="token key atrule">namespace</span><span class="token punctuation">:</span> custom<span class="token punctuation">-</span>system<span class="token key atrule">spec</span><span class="token punctuation">:</span> <span class="token key atrule">ports</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> http <span class="token key atrule">port</span><span class="token punctuation">:</span> <span class="token number">8443</span> <span class="token key atrule">protocol</span><span class="token punctuation">:</span> TCP <span class="token key atrule">targetPort</span><span class="token punctuation">:</span> http <span class="token key atrule">selector</span><span class="token punctuation">:</span> <span class="token key atrule">app</span><span class="token punctuation">:</span> sts<span class="token punctuation">-</span>webhook <span class="token key atrule">type</span><span class="token punctuation">:</span> ClusterIP<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>caBundle 获取<br><strong>TODO。。。</strong><br>接着创建 MutatingWebhookConfiguration<br>这里解读下,webhooks 是我们的主要配置<br>在 clientConfig 中,指定我们的 sts-webhook 的配置信息<br>rule 是我们的过滤规则,当 pods 创建的时候触发,并且需要 pods 有 labels 匹配 ab-build-different: “true”</p><pre class="line-numbers language-yaml" data-language="yaml"><code class="language-yaml"><span class="token key atrule">apiVersion</span><span class="token punctuation">:</span> admissionregistration.k8s.io/v1<span class="token key atrule">kind</span><span class="token punctuation">:</span> MutatingWebhookConfiguration<span class="token key atrule">metadata</span><span class="token punctuation">:</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> sts<span class="token punctuation">-</span>mutating<span class="token punctuation">-</span>webhook<span class="token key atrule">webhooks</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> sts<span class="token punctuation">-</span>webhook <span class="token key atrule">clientConfig</span><span class="token punctuation">:</span> <span class="token key atrule">service</span><span class="token punctuation">:</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> sts<span class="token punctuation">-</span>webhook <span class="token key atrule">namespace</span><span class="token punctuation">:</span> custom<span class="token punctuation">-</span>system <span class="token key atrule">path</span><span class="token punctuation">:</span> <span class="token string">"/mutate"</span> <span class="token key atrule">port</span><span class="token punctuation">:</span> <span class="token number">8443</span> <span class="token key atrule">caBundle</span><span class="token punctuation">:</span> <span class="token string">"xxxxx"</span> <span class="token key atrule">rules</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token key atrule">operations</span><span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token string">"CREATE"</span><span class="token punctuation">]</span> <span class="token key atrule">apiGroups</span><span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token string">""</span><span class="token punctuation">]</span> <span class="token key atrule">apiVersions</span><span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token string">"v1"</span><span class="token punctuation">]</span> <span class="token key atrule">resources</span><span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token string">"pods"</span><span class="token punctuation">]</span> <span class="token key atrule">admissionReviewVersions</span><span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token string">"v1"</span><span class="token punctuation">]</span> <span class="token key atrule">sideEffects</span><span class="token punctuation">:</span> None <span class="token key atrule">failurePolicy</span><span class="token punctuation">:</span> Fail <span class="token key atrule">objectSelector</span><span class="token punctuation">:</span> <span class="token key atrule">matchLabels</span><span class="token punctuation">:</span> <span class="token key atrule">ab-build-different</span><span class="token punctuation">:</span> <span class="token string">"true"</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>]]></content>
<tags>
<tag> mutating webhook </tag>
</tags>
</entry>
<entry>
<title>博客介绍</title>
<link href="/2024/04/09/hello-world/"/>
<url>/2024/04/09/hello-world/</url>
<content type="html"><![CDATA[<h1 id="欢迎来到我的运维开发博客!"><a href="#欢迎来到我的运维开发博客!" class="headerlink" title="欢迎来到我的运维开发博客!"></a>欢迎来到我的运维开发博客!</h1><p>这里是一个关于运维开发、DevOps 文化和技术的分享平台。我将在这里记录我在运维开发领域的学习、经验和见解,希望能够与大家一起探讨、分享,共同进步。</p><h2 id="关于我"><a href="#关于我" class="headerlink" title="关于我"></a>关于我</h2><p>我是一名热爱技术的运维开发工程师,擅长使用各种自动化工具和技术来提升系统的稳定性和效率。在工作中,我涉及到了 Docker、Kubernetes、Jenkins、GitLab 等工具和平台的使用和管理。我也关注新技术的发展和运用,希望能够将最新的技术应用到实际的工作中,提升团队的技术水平和工作效率。</p><h2 id="博客内容"><a href="#博客内容" class="headerlink" title="博客内容"></a>博客内容</h2><p>这个博客将包括以下内容:</p><ul><li>运维开发的最佳实践和经验分享</li><li>Docker、Kubernetes 等技术的教程和实践</li><li>自动化工具的介绍和使用技巧</li><li>DevOps 文化和团队管理的探讨</li><li>技术趋势和前沿技术的解读</li></ul><p>希望这个博客能够成为运维开发工程师们的一个技术交流和学习的平台,也欢迎大家提出问题和建议,共同讨论,共同进步!</p><h2 id="联系方式"><a href="#联系方式" class="headerlink" title="联系方式"></a>联系方式</h2><ul><li>邮箱:<a href="mailto:[email protected]">[email protected]</a></li><li>QQ:380937209</li><li>微信:devops-jack</li><li>Github:<a href="https://github.com/vb3328998">https://github.com/vb3328998</a></li></ul>]]></content>
<tags>
<tag> 介绍 </tag>
</tags>
</entry>
</search>