-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.xml
1457 lines (1409 loc) · 204 KB
/
index.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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml-stylesheet href="/rss.xsl" type="text/xsl"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>套路猿</title>
<link>/</link>
<description>Recent content on 套路猿</description>
<generator>Hugo -- gohugo.io</generator>
<language>zh</language>
<lastBuildDate>Fri, 22 Sep 2023 00:33:34 +0800</lastBuildDate>
<atom:link href="/index.xml" rel="self" type="application/rss+xml" />
<item>
<title>golang 结合 cobra 使用 chatgpt qdrant 实现 ai知识库 cli</title>
<link>/posts/kbai/</link>
<pubDate>Fri, 22 Sep 2023 00:33:34 +0800</pubDate>
<guid>/posts/kbai/</guid>
<description>套路猿 /posts/kbai/ -<h1 id="golang-结合-cobra-使用-chatgpt--qdrant-实现-ai知识库-cli">golang 结合 cobra 使用 chatgpt qdrant 实现 ai知识库 cli</h1>
<h2 id="流程">流程</h2>
<p><img src="https://qiniu.taoluyuan.com/2023/blog20230527115805.png?imagemogr2/auto-orient/thumbnail/!70p/blur/9x0/quality/75" alt=""></p>
<ol>
<li>将数据集 通过 openai embedding 得到向量+组装payload,存入 qdrant</li>
<li>用户进行问题搜索,通过 openai embedding 得到向量,从 qdrant 中搜索相似度大于0.8的数据</li>
<li>从 qdrant 中取出数据得到参考答案</li>
<li>将问题标题+参考答案,组装成promot 向gpt进行提问,得到偏向于 已有知识库设定的扩展知识回答</li>
</ol>
<h2 id="kabi-知识库的导入和搜索">kabi 知识库的导入和搜索</h2>
<p>仓库地址:<a href="%22https://github.com/webws/embedding-knowledge-base%22">https://github.com/webws/embedding-knowledge-base</a></p>
<p>kabi 是使用 golang 基于 openai chatgpt embedding + qdrant 实现知识库的导入和问答</p>
<pre tabindex="0"><code>❯ kabi -h
a local knowledge base, based on chatgpt and qdrant
usage:
kbai [flags]
kbai [command]
available commands:
completion generate the autocompletion script for the specified shell
help help about any command
import import data to vector database
search ask the knowledge base example: kbai ask --msg &#39;first, the chicken or the egg&#39;
flags:
--apikey string openai apikey:default from env apikey
--collection string qdrant collection name default: kubernetes (default &#34;kubernetes&#34;)
-h, --help help for kbai
--proxy string http client proxy default:socks5://127.0.0.1:1080 (default &#34;socks5://127.0.0.1:1080&#34;)
--qdrant string qdrant address default: 127.0.0.1:6334 (default &#34;127.0.0.1:6334&#34;)
--vectorsize uint qdrant vector size default: 1536 (default 1536)
use &#34;kbai [command] --help&#34; for more information about a command.
</code></pre><h5 id="启动向量数据库">启动向量数据库</h5>
<p>qdrant 是一个开源的向量搜索引擎,支持多种向量距离计算方式</p>
<p>docker 运行 qdrant</p>
<pre tabindex="0"><code>docker run --rm -p 6334:6334 qdrant/qdrant
</code></pre><h5 id="kbai库导入数据到知识库">kbai库导入数据到知识库</h5>
<p>clone 源码运行(后续提供二进制文件)</p>
<pre tabindex="0"><code>git clone https://github.com/webws/embedding-knowledge-base.git
cd ./embedding-knowledge-base
</code></pre><p>这里使用的测试数据是k8s相关的知识库,真实数据需自己准备</p>
<p>1.设置 openai apikey</p>
<pre tabindex="0"><code>export apikey=xxx
</code></pre><p>2.导入知识库(源码运行)</p>
<pre tabindex="0"><code>go run ./ import --datafile ./example/data.json
</code></pre><p>data.json 数据格式如下,为 真实数据需自己准备</p>
<pre tabindex="0"><code>[
{
&#34;questions&#34;: &#34;这是问题&#34;,
&#34;answers&#34;: &#34;这是答案&#34;
},
]
</code></pre><p>说明:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>默认的 代理 是 &#34;socks5://127.0.0.1:1080&#34; 自定义 可使用 --proxy 指定
</span></span></code></pre></div><h5 id="kbai-搜索数据">kbai 搜索数据</h5>
<p>搜索问题(源码执行)</p>
<pre tabindex="0"><code> go run ./ search --msg &#34;网关是什么&#34;
</code></pre><p>回答</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>the answer to the knowledge base:
</span></span><span style="display:flex;"><span>在kubernetes中,网关通常指的是ingress(入 口)资源对象。ingress是一种kubernetes api对象,用于配置和管理集群中的http和https流量入口。它充当了从集群外部访问集群内部服务的入口点
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>results of chatgpt answers with reference answers:
</span></span><span style="display:flex;"><span>,同时提供负载均衡、ssl/tls终止和基于域名的路由等功能。ingress资源对象定义了一组规则,这些规则指定了通过特定http路径或主机名将请求路由到后端服务的方式。可以使用不同的ingress控制器实现这些规则,如nginx、traefik等。这样就可以在集群中创建多个ingress资源对象来管理不同的流量入口。
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>only chatgpt answers:
</span></span><span style="display:flex;"><span>网关是一种网络设备,用于连接两个或多个不同类型的网络,以便实现数据以不同协议进行传递和转换。网关起到了连接不同网络之间的桥梁作用,将两个或多个网络互相连接起来,并负责数据的路由和转发。网关可以是硬件设备,如路由器,也可以是软件程序,如互联网网关。网关通常用于连接本地网络与互联网,使得局域网中的计算机能够访问互联网上的资源。除了连接不同网络的功能,网关还可以实现安全性、负载均衡、数据过滤等功能。
</span></span></code></pre></div><ol>
<li>第一个是知识库的回答(the answer to the knowledge base):</li>
<li>第二个 是结合知识库 chatgpt 的回答(results of chatgpt answers with reference answers)</li>
<li>第三个 仅chatgpt 回答</li>
</ol>
<p>可以看出 直接问chatgpt,得到的答案可能跟k8s无关,结合k8s本地知识库,可以让回答偏向 数据集设定的主题</p>
<p>如果直接搜索 与知识库无关或违规问题,将搜索不到任务数据</p>
<pre tabindex="0"><code>go run ./ search --msg &#34;苹果不洗能吃吗&#34;
rearch term violation or exceeding category
</code></pre><h2 id="kabi-golang-实现-ai知识库导入原理">kabi golang 实现 ai知识库导入原理</h2>
<h4 id="导入">导入</h4>
<ol>
<li>接入 qdrant 和 openai cleint</li>
<li>解释原始知识库数据 为 q(问) a(答)</li>
<li>将 问题 经过 openai embedding 得到向量+答案存入 qdrant</li>
</ol>
<p>以下是 <a href="%22https://github.com/webws/embedding-knowledge-base%22">kbai</a> go 导入逻辑代码</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-golang" data-lang="golang"><span style="display:flex;"><span> <span style="color:#a6e22e">qdrantclient</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">qdrant</span>.<span style="color:#a6e22e">newqdrantclient</span>(<span style="color:#a6e22e">configflags</span>.<span style="color:#a6e22e">qdrant</span>, <span style="color:#a6e22e">configflags</span>.<span style="color:#a6e22e">collection</span>, <span style="color:#a6e22e">configflags</span>.<span style="color:#a6e22e">vectorsize</span>)
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">qdrantclient</span>.close()
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">aiclient</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">ai</span>.<span style="color:#a6e22e">newaiclient</span>(<span style="color:#a6e22e">configflags</span>.<span style="color:#a6e22e">proxy</span>, <span style="color:#a6e22e">configflags</span>.<span style="color:#a6e22e">apikey</span>)
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> = <span style="color:#a6e22e">qdrantclient</span>.<span style="color:#a6e22e">createcollection</span>(<span style="color:#a6e22e">configflags</span>.<span style="color:#a6e22e">collection</span>, <span style="color:#a6e22e">configflags</span>.<span style="color:#a6e22e">vectorsize</span>); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">qas</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">converttoqas</span>(<span style="color:#a6e22e">datafile</span>)
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">points</span> <span style="color:#f92672">:=</span> []<span style="color:#f92672">*</span><span style="color:#a6e22e">pb</span>.<span style="color:#a6e22e">pointstruct</span>{}
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">logger</span>.<span style="color:#a6e22e">infow</span>(<span style="color:#e6db74">&#34;import&#34;</span>, <span style="color:#e6db74">&#34;data&#34;</span>, <span style="color:#a6e22e">qas</span>)
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">qpslenth</span> <span style="color:#f92672">:=</span> len(<span style="color:#a6e22e">qas</span>)
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span>, <span style="color:#a6e22e">qa</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">qas</span> {
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">embedding</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">aiclient</span>.<span style="color:#a6e22e">simplegetvec</span>(<span style="color:#a6e22e">qa</span>.<span style="color:#a6e22e">questions</span>)
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">logger</span>.<span style="color:#a6e22e">errorw</span>(<span style="color:#e6db74">&#34;simplegetvec&#34;</span>, <span style="color:#e6db74">&#34;err&#34;</span>, <span style="color:#a6e22e">err</span>, <span style="color:#e6db74">&#34;question&#34;</span>, <span style="color:#a6e22e">qa</span>.<span style="color:#a6e22e">questions</span>, <span style="color:#e6db74">&#34;index&#34;</span>, <span style="color:#a6e22e">i</span>, <span style="color:#e6db74">&#34;total&#34;</span>, <span style="color:#a6e22e">qpslenth</span>)
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">point</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">buildpoint</span>(<span style="color:#a6e22e">qa</span>.<span style="color:#a6e22e">questions</span>, <span style="color:#a6e22e">qa</span>.<span style="color:#a6e22e">answers</span>, <span style="color:#a6e22e">embedding</span>)
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">points</span> = append(<span style="color:#a6e22e">points</span>, <span style="color:#a6e22e">point</span>)
</span></span><span style="display:flex;"><span> }
</span></span></code></pre></div><h3 id="搜索">搜索</h3>
<ol>
<li>问题搜索,通过 openai embedding 得到向量</li>
<li>根据向量 从 qdrant 中搜索相似度大于0.8的数据</li>
<li>根据 qdrant 里的知识库答案(参考答案) + 从 chatgpt 提问 得到扩展知识</li>
</ol>
<p>以下是 <a href="%22https://github.com/webws/embedding-knowledge-base%22">kbai</a> go 搜索代码逻辑</p>
<pre tabindex="0"><code> qdrantclient := qdrant.newqdrantclient(configflags.qdrant, configflags.collection, configflags.vectorsize)
defer qdrantclient.close()
aiclient, err := ai.newaiclient(configflags.proxy, configflags.apikey)
if err != nil {
return err
}
vector, err := aiclient.simplegetvec(msg)
if err != nil {
return err
}
points, err := qdrantclient.search(vector)
if err != nil {
logger.errorw(&#34;qdrant search fail&#34;, &#34;err&#34;, err)
return err
}
if len(points) == 0 {
fmt.println(&#34;rearch term violation or exceeding category&#34;)
return nil
// return errors.new(&#34;rearch term violation or exceeding category&#34;)
}
// score less than 0.8, rearch term violation or exceeding category
if points[0].score &lt; 0.8 {
fmt.println(&#34;rearch term violation or exceeding category&#34;)
return nil
// return errors.new(&#34;rearch term violation or exceeding category&#34;)
}
</code></pre>- /posts/kbai/ - </description>
</item>
<item>
<title>golang 使用 viper 加载配置文件 自动反序列化到结构</title>
<link>/posts/golang-viper-load/</link>
<pubDate>Sun, 27 Aug 2023 20:59:44 +0800</pubDate>
<guid>/posts/golang-viper-load/</guid>
<description>套路猿 /posts/golang-viper-load/ -<h1 id="heading"></h1>
<p>文章博客地址:<a href="https://blog.taoluyuan.com/posts/golang-viper-load/">golang 使用 viper 加载配置 自动反序列化到结构</a></p>
<ul>
<li>golang使用 viper 无需设置 mapstructure tag 根据配置文件后缀 自动返序列化到结构</li>
<li>解决结构有下划线的字段解析不成功问题</li>
</ul>
<h3 id="viper-正常加载配置文件">viper 正常加载配置文件</h3>
<p>golang viper 其中可以用来 查找、加载和反序列化JSON、TOML、YAML、HCL、INI、envfile和格式的配置文件</p>
<p>配置文件 test_toml.toml</p>
<pre tabindex="0"><code>http_addr = &#34;:8082&#34;
grpc_addr = &#34;:8083&#34;
jaeger_url= &#34;http://localhost:14268/api/traces&#34;
tracing= true
</code></pre><p>golang代码</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-golang" data-lang="golang"><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">ConfigTest</span> <span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">HttpAddr</span> <span style="color:#66d9ef">string</span> <span style="color:#e6db74">`json:&#34;http_addr&#34; toml:&#34;http_addr&#34; yaml:&#34;http_addr&#34;`</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">GrpcAddr</span> <span style="color:#66d9ef">string</span> <span style="color:#e6db74">`json:&#34;grpc_addr&#34; toml:&#34;grpc_addr&#34; yaml:&#34;grpc_addr&#34;`</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">JaegerUrl</span> <span style="color:#66d9ef">string</span> <span style="color:#e6db74">`json:&#34;jaeger_url&#34; toml:&#34;jaeger_url&#34; yaml:&#34;jaeger_url&#34; mapstructure:&#34;jaeger_url&#34;`</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">Tracing</span> <span style="color:#66d9ef">bool</span> <span style="color:#e6db74">`toml:&#34;tracing&#34; json:&#34;tracing&#34; yaml:&#34;tracing&#34; `</span> <span style="color:#75715e">// opentelemetry tracing
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// jaeger 加载配置文件
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">TestSourceFile_Unmarshal</span>(<span style="color:#a6e22e">t</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">testing</span>.<span style="color:#a6e22e">T</span>) {
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">filePath</span> <span style="color:#f92672">:=</span> <span style="color:#e6db74">&#34;./test_toml.toml&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">viper</span>.<span style="color:#a6e22e">SetConfigFile</span>(<span style="color:#a6e22e">filePath</span>)
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">viper</span>.<span style="color:#a6e22e">ReadInConfig</span>(); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">t</span>.<span style="color:#a6e22e">Error</span>(<span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">c</span> <span style="color:#f92672">:=</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">ConfigTest</span>{}
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">viper</span>.<span style="color:#a6e22e">Unmarshal</span>(<span style="color:#a6e22e">c</span>); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">t</span>.<span style="color:#a6e22e">Error</span>(<span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">logger</span>.<span style="color:#a6e22e">Infow</span>(<span style="color:#e6db74">&#34;Unmarshal file sucess&#34;</span>, <span style="color:#e6db74">&#34;v&#34;</span>, <span style="color:#a6e22e">c</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>打印返序列化的配置结构</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#f92672">{</span><span style="color:#e6db74">&#34;level&#34;</span>:<span style="color:#e6db74">&#34;info&#34;</span>,<span style="color:#e6db74">&#34;ts&#34;</span>:<span style="color:#e6db74">&#34;2023-08-27T21:35:27.041+0800&#34;</span>,<span style="color:#e6db74">&#34;caller&#34;</span>:<span style="color:#e6db74">&#34;config/source_file_test.go:31&#34;</span>,<span style="color:#e6db74">&#34;msg&#34;</span>:<span style="color:#e6db74">&#34;Unmarshal file sucess&#34;</span>,<span style="color:#e6db74">&#34;v&#34;</span>:<span style="color:#f92672">{</span><span style="color:#e6db74">&#34;http_addr&#34;</span>:<span style="color:#e6db74">&#34;&#34;</span>,<span style="color:#e6db74">&#34;grpc_addr&#34;</span>:<span style="color:#e6db74">&#34;&#34;</span>,<span style="color:#e6db74">&#34;jaeger_url&#34;</span>:<span style="color:#e6db74">&#34;http://localhost:14268/api/traces&#34;</span>,<span style="color:#e6db74">&#34;tracing&#34;</span>:true<span style="color:#f92672">}}</span>
</span></span></code></pre></div><p>可以看到带下划线的字段,不加 mapstructure 标签,是不会反序列化</p>
<h3 id="不加-mapstructure-tag实现自动反序列化">不加 mapstructure tag实现自动反序列化</h3>
<h4 id="查看viper-unmarshal-代码">查看viper Unmarshal 代码</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-golang" data-lang="golang"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">v</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">Viper</span>) <span style="color:#a6e22e">Unmarshal</span>(<span style="color:#a6e22e">rawVal</span> <span style="color:#66d9ef">interface</span>{}, <span style="color:#a6e22e">opts</span> <span style="color:#f92672">...</span><span style="color:#a6e22e">DecoderConfigOption</span>) <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">decode</span>(<span style="color:#a6e22e">v</span>.<span style="color:#a6e22e">AllSettings</span>(), <span style="color:#a6e22e">defaultDecoderConfig</span>(<span style="color:#a6e22e">rawVal</span>, <span style="color:#a6e22e">opts</span><span style="color:#f92672">...</span>))
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">decode</span>(<span style="color:#a6e22e">input</span> <span style="color:#66d9ef">interface</span>{}, <span style="color:#a6e22e">config</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">mapstructure</span>.<span style="color:#a6e22e">DecoderConfig</span>) <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">decoder</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">mapstructure</span>.<span style="color:#a6e22e">NewDecoder</span>(<span style="color:#a6e22e">config</span>)
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">decoder</span>.<span style="color:#a6e22e">Decode</span>(<span style="color:#a6e22e">input</span>)
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">NewDecoder</span>(<span style="color:#a6e22e">config</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">DecoderConfig</span>) (<span style="color:#f92672">*</span><span style="color:#a6e22e">Decoder</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">config</span>.<span style="color:#a6e22e">TagName</span> <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;&#34;</span> {
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">config</span>.<span style="color:#a6e22e">TagName</span> = <span style="color:#e6db74">&#34;mapstructure&#34;</span>
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span> <span style="color:#75715e">// ...
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>}
</span></span></code></pre></div><ul>
<li>从代码看出 Viper使用的是 github.com/mitchellh/mapstructure来解析值</li>
<li>mapstructure 用于将通用的map[string]interface{}解码到对应的 Go 结构体中</li>
<li>默认情况下,mapstructure 使用结构体中字段的名称做这个映射,不区分大小写,比如 Name 字段可以映射到 name、NAME、NaMe 等等</li>
<li>如果没有指定 tagName ,则默认为 mapstructure,这也是为什么带下划线的字段不加 mapstructure 标签无法解析的原因</li>
<li>viper 中Unmarshal的第二个参数是可以指定 DecoderConfigOption 的,从而可以指定 tagName</li>
</ul>
<h4 id="viper根据文类型件自动解码到结构">viper根据文类型件自动解码到结构</h4>
<ol>
<li>读取文件后缀比如 toml</li>
<li>根据后缀设置 tagName</li>
<li>调用 viper.Unmarshal解析</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-golang" data-lang="golang"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">TestSourceFile_Unmarshal1</span>(<span style="color:#a6e22e">t</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">testing</span>.<span style="color:#a6e22e">T</span>) {
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">filePath</span> <span style="color:#f92672">:=</span> <span style="color:#e6db74">&#34;./test_toml.toml&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">c</span> <span style="color:#f92672">:=</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">ConfigTest</span>{}
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">viperUnmarshal</span>(<span style="color:#a6e22e">c</span>, <span style="color:#a6e22e">filePath</span>); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">t</span>.<span style="color:#a6e22e">Error</span>(<span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">logger</span>.<span style="color:#a6e22e">Infow</span>(<span style="color:#e6db74">&#34;Unmarshal file sucess&#34;</span>, <span style="color:#e6db74">&#34;v&#34;</span>, <span style="color:#a6e22e">c</span>)
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">viperUnmarshal</span>(<span style="color:#a6e22e">v</span> <span style="color:#66d9ef">interface</span>{}, <span style="color:#a6e22e">configPath</span> <span style="color:#66d9ef">string</span>) <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">tagName</span> <span style="color:#66d9ef">string</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">ext</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">filepath</span>.<span style="color:#a6e22e">Ext</span>(<span style="color:#a6e22e">configPath</span>)
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> len(<span style="color:#a6e22e">ext</span>) &gt; <span style="color:#ae81ff">1</span> {
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">tagName</span> = <span style="color:#a6e22e">ext</span>[<span style="color:#ae81ff">1</span>:]
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span> <span style="color:#75715e">// set decode tag_name, default is mapstructure
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#a6e22e">decoderConfigOption</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">c</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">mapstructure</span>.<span style="color:#a6e22e">DecoderConfig</span>) {
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">TagName</span> = <span style="color:#a6e22e">tagName</span>
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">cViper</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">viper</span>.<span style="color:#a6e22e">New</span>()
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">cViper</span>.<span style="color:#a6e22e">SetConfigFile</span>(<span style="color:#a6e22e">configPath</span>)
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">cViper</span>.<span style="color:#a6e22e">ReadInConfig</span>(); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">cViper</span>.<span style="color:#a6e22e">Unmarshal</span>(<span style="color:#a6e22e">v</span>, <span style="color:#a6e22e">decoderConfigOption</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#f92672">{</span><span style="color:#e6db74">&#34;level&#34;</span>:<span style="color:#e6db74">&#34;info&#34;</span>,<span style="color:#e6db74">&#34;ts&#34;</span>:<span style="color:#e6db74">&#34;2023-08-27T21:35:34.553+0800&#34;</span>,<span style="color:#e6db74">&#34;caller&#34;</span>:<span style="color:#e6db74">&#34;config/source_file_test.go:40&#34;</span>,<span style="color:#e6db74">&#34;msg&#34;</span>:<span style="color:#e6db74">&#34;Unmarshal file sucess&#34;</span>,<span style="color:#e6db74">&#34;v&#34;</span>:<span style="color:#f92672">{</span><span style="color:#e6db74">&#34;http_addr&#34;</span>:<span style="color:#e6db74">&#34;:8082&#34;</span>,<span style="color:#e6db74">&#34;grpc_addr&#34;</span>:<span style="color:#e6db74">&#34;:8083&#34;</span>,<span style="color:#e6db74">&#34;jaeger_url&#34;</span>:<span style="color:#e6db74">&#34;http://localhost:14268/api/traces&#34;</span>,<span style="color:#e6db74">&#34;tracing&#34;</span>:true<span style="color:#f92672">}}</span>
</span></span></code></pre></div><p>我已将viper加载配置集成进自己的项目,完整example 代码可以查看 <a href="https://github.com/webws/go-moda/blob/main/config/source_file_test.go">source_file_test.go</a></p>
- /posts/golang-viper-load/ - </description>
</item>
<item>
<title>k8s + docker 基于 kubeadm 多节点集群部署</title>
<link>/posts/install-k8s/</link>
<pubDate>Thu, 08 Jun 2023 21:36:22 +0800</pubDate>
<guid>/posts/install-k8s/</guid>
<description>套路猿 /posts/install-k8s/ -<h1 id="k8s--docker-基于-kubeadm-多节点集群部署">k8s + docker 基于 kubeadm 多节点集群部署</h1>
<p>博客文章地址:<a href="https://blog.taoluyuan.com/posts/install-k8s/">https://blog.taoluyuan.com/posts/install-k8s/</a></p>
<h2 id="各个节点环境准备">各个节点环境准备</h2>
<p>[环境准备] 这章的操作都要在两台机器上分别执行,我准备了两台机器,如下:</p>
<ol>
<li>一台master,一台node</li>
<li>主机1(master) ip:192.168.31.122,主机2 192.168.31.166</li>
</ol>
<h3 id="1-安装-docker">1. 安装 docker</h3>
<p>如已经安装好docker 可跳过
docker 官方安装 <a href="https://docs.docker.com/engine/install/ubuntu/">https://docs.docker.com/engine/install/ubuntu/</a> 有点慢
清华大学 镜像安装方法 <a href="https://mirrors.tuna.tsinghua.edu.cn/help/docker-ce/">https://mirrors.tuna.tsinghua.edu.cn/help/docker-ce/</a></p>
<p>安装依赖</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>sudo apt-get install ca-certificates curl gnupg
</span></span><span style="display:flex;"><span>sudo install -m <span style="color:#ae81ff">0755</span> -d /etc/apt/keyrings
</span></span><span style="display:flex;"><span>sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
</span></span><span style="display:flex;"><span>echo <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span> <span style="color:#e6db74">&#34;deb [arch=</span><span style="color:#66d9ef">$(</span>dpkg --print-architecture<span style="color:#66d9ef">)</span><span style="color:#e6db74"> signed-by=/etc/apt/keyrings/docker.gpg] https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/ubuntu \
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74"> &#34;</span><span style="color:#66d9ef">$(</span>. /etc/os-release <span style="color:#f92672">&amp;&amp;</span> echo <span style="color:#e6db74">&#34;</span>$VERSION_CODENAME<span style="color:#e6db74">&#34;</span><span style="color:#66d9ef">)</span><span style="color:#e6db74">&#34; stable&#34;</span> | sudo tee /etc/apt/sources.list.d/docker.list &gt; /dev/null
</span></span></code></pre></div><p>安装 docker-ce</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>sudo apt-get update
</span></span><span style="display:flex;"><span>sudo apt-get install docker-ce
</span></span></code></pre></div><p>docker组授予用户根级权限,让当前登陆也可以使用docker</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>sudo groupadd docker
</span></span><span style="display:flex;"><span>sudo usermod -aG docker $USER
</span></span><span style="display:flex;"><span>newgrp docker
</span></span></code></pre></div><p>镜像加速器</p>
<p>通过修改daemon配置文件/etc/docker/daemon.json修改 registry,我使用的是上海交大</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>sudo mkdir -p /etc/docker
</span></span><span style="display:flex;"><span>sudo tee /etc/docker/daemon.json <span style="color:#e6db74">&lt;&lt;-&#39;EOF&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">{
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74"> &#34;registry-mirrors&#34;: [&#34;https://docker.mirrors.sjtug.sjtu.edu.cn/&#34;]
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">}
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">EOF</span>
</span></span><span style="display:flex;"><span>sudo systemctl daemon-reload
</span></span><span style="display:flex;"><span>sudo systemctl restart docker
</span></span></code></pre></div><h3 id="2-安装-kubeadm-kubelet-和-kubectl">2. 安装 kubeadm, kubelet 和 kubectl</h3>
<p><a href="https://developer.aliyun.com/mirror/kubernetes?spm=a2c6h.13651102.0.0.560a1b11o3aTbI">阿里云官方推荐源</a></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>apt-get update <span style="color:#f92672">&amp;&amp;</span> apt-get install -y apt-transport-https
</span></span><span style="display:flex;"><span>curl https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | apt-key add -
</span></span><span style="display:flex;"><span>cat <span style="color:#e6db74">&lt;&lt;EOF &gt;/etc/apt/sources.list.d/kubernetes.list
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">deb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">EOF</span>
</span></span><span style="display:flex;"><span>sudo apt-get update
</span></span></code></pre></div><p>安装1.22.0版本</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>apt-get install -y kubelet<span style="color:#f92672">=</span>1.22.0-00 kubeadm<span style="color:#f92672">=</span>1.22.0-00 kubectl<span style="color:#f92672">=</span>1.22.0-00
</span></span></code></pre></div><p>查看版本</p>
<pre tabindex="0"><code>kubelet --version
kubeadm version
kubectl version
</code></pre><p>kubelet 开机自启</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>systemctl enable kubelet
</span></span></code></pre></div><h3 id="3-使用-systemd-作为-docker-cgroup-驱动程序">3. 使用 systemd 作为 docker cgroup 驱动程序</h3>
<p>从 v1.22 开始,在使用 kubeadm 创建集群时,kubeadm 默认使用 systemd,而 docker 默认使用 cgroupfs,所以需要修改 docker 的 cgroup 驱动程序为 systemd,<a href="https://kubernetes.io/zh-cn/docs/setup/production-environment/container-runtimes/#cgroup-drivers">k8s cgroup-drivers说明</a>
打开 /etc/docker/daemon.json 文件,追加以下配置</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#f92672">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;exec-opts&#34;</span>: <span style="color:#f92672">[</span><span style="color:#e6db74">&#34;native.cgroupdriver=systemd&#34;</span><span style="color:#f92672">]</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">}</span>
</span></span></code></pre></div><p>重启docker</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>systemctl daemon-reload
</span></span><span style="display:flex;"><span>systemctl restart docker
</span></span><span style="display:flex;"><span>systemctl enable docker
</span></span></code></pre></div><p>查看cgroup驱动,必须是systemd,才行</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>docker info|grep Cgroup
</span></span></code></pre></div><h3 id="4-swapoff-设置">4. swapoff 设置</h3>
<p>设置 swapoff</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>sudo swapoff -a
</span></span></code></pre></div><p>永久设置 swapoff,注释掉swap那一行</p>
<pre tabindex="0"><code>vim /etc/fstab
</code></pre><p>查看swapon,必须是空的,不然接下来的kubeadm init会报错</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>swapon -s
</span></span></code></pre></div><h3 id="kubeadm-主节点-安装-k8s">kubeadm 主节点 安装 k8s</h3>
<h4 id="kubeadm-init-安装-k8s">kubeadm init 安装 k8s</h4>
<ol>
<li>可以先拉取镜像,这样kubeadm init的时候就不会拉取镜</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>sudo kubeadm config images pull --kubernetes-version<span style="color:#f92672">=</span>v1.22.0 --image-repository registry.aliyuncs.com/google_containers
</span></span></code></pre></div><ol start="2">
<li>执行init</li>
</ol>
<ul>
<li>&ndash;kubernetes-version 指定k8s 版本为1.22.0,</li>
<li>&ndash;image-repository 指定镜像仓库为阿里云:registry.aliyuncs.com/google_containers,因为 k8s 默认的镜像仓库是 gcr.io,国内访问不了</li>
<li>&ndash;pod-network-cidr 指定pod的网段,需要与cni插件的网段一致,否则会出现pod无法通信的问题 ,flannel的网段是 10.244.0.0/16</li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>sudo kubeadm init --kubernetes-version<span style="color:#f92672">=</span>v1.22.0 --image-repository registry.aliyuncs.com/google_containers --pod-network-cidr<span style="color:#f92672">=</span>10.244.0.0/16
</span></span></code></pre></div><ol start="3">
<li>kubectl get nodes 查看节点状态</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>kubectl get nodes
</span></span></code></pre></div><p>应该会 出现 localhost:8080 was refused - did you specify the right host or port? 错误
将 /etc/kubernetes/admin.conf 拷贝到 $HOME/.kube/config</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>mkdir -p $HOME/.kube
</span></span><span style="display:flex;"><span>sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
</span></span><span style="display:flex;"><span>sudo chown -R $USER:$USER $HOME/.kube
</span></span></code></pre></div><p>再查看nodes状态</p>
<h4 id="安装网络插件">安装网络插件</h4>
<p>此时获取节点状态会发现有一个节点是 NotReady 状态,
而且,查看pod状态会发现,coredns 也是 Pending 状态</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>kubectl get pods --all-namespaces
</span></span></code></pre></div><p>这是因为还没有安装网络插件,这里我选择安装 flannel</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
</span></span></code></pre></div><p>网络不好可以使用ghproxy</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>kubectl apply -f https://ghproxy.com/https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
</span></span></code></pre></div><p>等待会再次查看pod状态,coredns状态是 Running,节点状态是 Ready</p>
<h2 id="子节点加入集群">子节点加入集群</h2>
<ol>
<li>master节点生成token,并且指定master节点的ip,生成join命令</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>kubeadm token create --print-join-command --ttl <span style="color:#ae81ff">0</span> --kubeconfig /etc/kubernetes/admin.conf
</span></span></code></pre></div><p>会出现 类似以下 join 命令</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>kubeadm join 192.168.31.122:6443 --token kzmdey.dk0tcgyg4ivr8y87 --discovery-token-ca-cert-hash sha256:bc2e3252080ba81e342933955682ae119decc948fef2180e5135b0dd891e5891
</span></span></code></pre></div><ol start="2">
<li>在子节点执行上面的join命令,加入集群</li>
<li>在master节点查看节点状态</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span> k get nodes -o wide
</span></span></code></pre></div><p>可以看到两个节点都是 Ready 状态</p>
<pre tabindex="0"><code>NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
song Ready control-plane,master 45h v1.22.0 192.168.31.122 &lt;none&gt; Ubuntu 22.10 5.19.0-21-generic docker://24.0.2
song2 Ready &lt;none&gt; 77m v1.22.0 192.168.31.166 &lt;none&gt; Ubuntu 22.10 5.19.0-21-generic docker://24.0.2
</code></pre><h2 id="安装相关问题排查">安装相关问题排查</h2>
<ol>
<li>container runtime is not running: output: time=&ldquo;2023-06-08T14:09:02Z&rdquo; level=fatal msg=&ldquo;validate service connection: CRI v1 runtime API is not implemented for endpoint &quot;unix:///var/run/containerd/containerd.sock&quot;: rpc error: code = Unimplemented desc = unknown service runtime.v1.RuntimeService&rdquo;
解决方法</li>
</ol>
<pre tabindex="0"><code>sudo rm /etc/containerd/config.toml
sudo systemctl restart containerd
</code></pre><ol start="2">
<li>[WARNING Swap]: swap is enabled; production deployments should disable swap unless testing the NodeSwap feature gate of the kubelet
ubuntu系统</li>
</ol>
<pre tabindex="0"><code>swapoff -a
</code></pre><ol start="3">
<li>查看pod出现错误,要是出现类似 pod cidr not assigned,如果是flannel网络插件,那么就是没有设置pod-network-cidr</li>
<li>排查kubelet 日志</li>
</ol>
<pre tabindex="0"><code>sudo systemctl status kubelet.service
sudo journalctl -xu kubelet.service
</code></pre>- /posts/install-k8s/ - </description>
</item>
<item>
<title>k8s istio 集成 多版本应用服务 和 网格监测</title>
<link>/posts/istio-getting-started/</link>
<pubDate>Sun, 04 Jun 2023 15:37:03 +0800</pubDate>
<guid>/posts/istio-getting-started/</guid>
<description>套路猿 /posts/istio-getting-started/ -<h1 id="说明">说明</h1>
<p>博客文章地址:<a href="https://blog.taoluyuan.com/posts/istio-getting-started/">https://blog.taoluyuan.com/posts/istio-getting-started/</a>
本主要是内容:</p>
<ol>
<li>使用 istioctl 安装 istio</li>
<li>采用 istio 官方提供 的 应用bookinfo,实现多版本的服务应用部署</li>
<li>istio 网关 gateway,vs,dr 的基本使用</li>
<li>利用监测工具 prometheus,grafana,jaeger 查看 istio 的监控数据</li>
</ol>
<p>文章提到的yaml,也是istio官方提供的,整理后单独放到github <a href="https://github.com/webws/k8s-istio-practice">github k8s-istio-practice</a>
根目录 makefile 集成了相关命令,你们可以直接通过 makefile 安装 service,gateway,vs,dr,监控,以实现跟文章一样的效果</p>
<h1 id="istio">istio</h1>
<p>官方文档:https://istio.io/latest/zh/docs/</p>
<h3 id="安装">安装</h3>
<p>参考官方安装文档:<a href="https://istio.io/latest/zh/docs/setup/getting-started/">官方demo配置组合安装文档</a></p>
<ol>
<li>采用 demo 配置组合,它包含了一组专为测试准备的功能集合</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span> curl -L https://istio.io/downloadIstio | ISTIO_VERSION<span style="color:#f92672">=</span>1.17.2 sh - <span style="color:#f92672">&amp;&amp;</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span> cd istio-1.17.2 <span style="color:#f92672">&amp;&amp;</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span> export PATH<span style="color:#f92672">=</span>$PWD/bin:$PATH <span style="color:#f92672">&amp;&amp;</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span> istioctl install --set profile<span style="color:#f92672">=</span>demo
</span></span></code></pre></div><ol>
<li>给命名空间添加标签,指示 在 default命名空间 部署应用的时候,自动注入 Envoy</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>kubectl label namespace default istio-injection<span style="color:#f92672">=</span>enabled
</span></span></code></pre></div><ol start="3">
<li>安装的istio相关资源在 istio-system 命名空间下,可以通过以下查看安装的资源</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>k get all -n istio-system
</span></span></code></pre></div><h2 id="bookinfo-应用程序">bookinfo 应用程序</h2>
<h3 id="bookinfo-架构及介绍">bookinfo 架构及介绍</h3>
<p>bookinfo 由四个单独的微服务构成 ,以下是官方对bookinfo的介绍,也可以直接看官方文档 <a href="https://istio.io/latest/zh/docs/examples/bookinfo/">istio-bookinfo</a></p>
<blockquote>
<p>这个应用模仿在线书店的一个分类,显示一本书的信息。 页面上会显示一本书的描述,书籍的细节(ISBN、页数等),以及关于这本书的一些评论。</p>
</blockquote>
<p>Bookinfo 应用分为四个单独的微服务:</p>
<ul>
<li>productpage. 这个微服务会调用 details 和 reviews 两个微服务,用来生成页面。</li>
<li>details. 这个微服务中包含了书籍的信息。</li>
<li>reviews. 这个微服务中包含了书籍相关的评论。它还会调用 ratings 微服务。</li>
<li>ratings. 这个微服务中包含了由书籍评价组成的评级信息。</li>
</ul>
<p>reviews 微服务有 3 个版本:</p>
<ul>
<li>
<p>v1 版本不会调用 ratings 服务</p>
</li>
<li>
<p>v2 版本会调用 ratings 服务,并使用 1 到 5 个黑色星形图标来显示评分信息。</p>
</li>
<li>
<p>v3 版本会调用 ratings 服务,并使用 1 到 5 个红色星形图标来显示评分信息。</p>
<p><img src="https://qiniu.taoluyuan.com/2023/blog20230604105059.png?imageMogr2/auto-orient/thumbnail/!70p/blur/9x0/quality/75" alt=""></p>
</li>
</ul>
<h3 id="安装bookinfo">安装bookinfo</h3>
<p>官方提示安装 bookinfo的命令</p>
<pre tabindex="0"><code>kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml
</code></pre><p>bookinfo.yaml 的istio 源码地址在 <a href="https://github.com/istio/istio/tree/release-1.17/samples/bookinfo/platform/kube/bookinfo.yaml">bookinfo</a></p>
<p>我将同名文件 bookinfo.yaml 下载后, 放到了本仓库的根目录 可以在 本地
执行</p>
<pre tabindex="0"><code>kubectl apply -f ./bookinfo.yaml
</code></pre><p>或者 make</p>
<pre tabindex="0"><code>make install-bookinfo
</code></pre><p>文章后面关于 yaml 的文件,我都会将官方的yaml下载到本地,演示也是用本地的yaml,方便大家查看</p>
<h3 id="验证安装的服务">验证安装的服务</h3>
<ol>
<li>上面的命令会启动全部的四个服务,其中也包括了 reviews 服务的三个版本(v1、v2 以及 v3)</li>
<li>确认 service和pod 都启动成功</li>
</ol>
<pre tabindex="0"><code>kubectl get services
</code></pre><pre tabindex="0"><code>kubectl get pods
</code></pre><ol start="3">
<li>确认bookinfo 接口服务 是否正常</li>
</ol>
<pre tabindex="0"><code>kubectl exec -it $(kubectl get pod -l app=ratings -o jsonpath=&#39;{.items[0].metadata.name}&#39;) -c ratings -- curl productpage:9080/productpage | grep -o &#34;&lt;title&gt;.*&lt;/title&gt;&#34;
</code></pre><p>上面命令步骤为:</p>
<ol>
<li>通过 kubectl get pod -l app=ratings -o jsonpath=&rsquo;{.items[0].metadata.name}&rsquo; 获取到 ratings 服务的pod名称</li>
<li>通过 kubectl exec -it $(&hellip;) -c ratings 进入到 ratings 容器中</li>
<li>通过 curl productpage:9080/productpage 来访问 productpage 服务,并且通过 grep -o &ldquo;<!-- raw HTML omitted -->.*<!-- raw HTML omitted -->&rdquo; 来查看返回的页面的title,如果返回有值,说明服务正常</li>
<li>bookinfo.yam 的 service port 里面定义了 9080,所以可以通过 productpage:9080/productpage 来访问 productpage 服务</li>
</ol>
<p>注意:</p>
<blockquote>
<p>源码,bookinfo里面调用是用的短名称,建议您在生产环境中指定完全限定的主机名,比如 productpage.default.svc.cluster.local</p>
</blockquote>
<h2 id="对外开放bookinfo-服务">对外开放bookinfo 服务</h2>
<p>Service 的默认类型是 ClusterIP,目前 bookinfo 只是集群内部的服务,外部无法访问
需要创建一个 Gateway 和 VirtualService 来对外开放服务</p>
<h3 id="安装-gateway-和-virtualservice">安装 Gateway 和 VirtualService</h3>
<pre tabindex="0"><code>kubectl apply -f ./bookinfo-gateway.yaml
</code></pre><p>或者 make</p>
<pre tabindex="0"><code>make install-bookinfo-gateway
</code></pre><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">apiVersion</span>: <span style="color:#ae81ff">networking.istio.io/v1alpha3</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">kind</span>: <span style="color:#ae81ff">Gateway</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">metadata</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">name</span>: <span style="color:#ae81ff">bookinfo-gateway</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">spec</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">selector</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">istio</span>: <span style="color:#ae81ff">ingressgateway</span> <span style="color:#75715e"># use istio default controller</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">servers</span>:
</span></span><span style="display:flex;"><span> - <span style="color:#f92672">port</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">number</span>: <span style="color:#ae81ff">80</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">name</span>: <span style="color:#ae81ff">http</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">protocol</span>: <span style="color:#ae81ff">HTTP</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">hosts</span>:
</span></span><span style="display:flex;"><span> - <span style="color:#e6db74">&#34;*&#34;</span>
</span></span><span style="display:flex;"><span>---
</span></span><span style="display:flex;"><span><span style="color:#f92672">apiVersion</span>: <span style="color:#ae81ff">networking.istio.io/v1alpha3</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">kind</span>: <span style="color:#ae81ff">VirtualService</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">metadata</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">name</span>: <span style="color:#ae81ff">bookinfo</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">spec</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">hosts</span>:
</span></span><span style="display:flex;"><span> - <span style="color:#e6db74">&#34;*&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">gateways</span>:
</span></span><span style="display:flex;"><span> - <span style="color:#ae81ff">bookinfo-gateway</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">http</span>:
</span></span><span style="display:flex;"><span> - <span style="color:#f92672">match</span>:
</span></span><span style="display:flex;"><span> - <span style="color:#f92672">uri</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">exact</span>: <span style="color:#ae81ff">/productpage</span>
</span></span><span style="display:flex;"><span> - <span style="color:#f92672">uri</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">prefix</span>: <span style="color:#ae81ff">/static</span>
</span></span><span style="display:flex;"><span> - <span style="color:#f92672">uri</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">exact</span>: <span style="color:#ae81ff">/login</span>
</span></span><span style="display:flex;"><span> - <span style="color:#f92672">uri</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">exact</span>: <span style="color:#ae81ff">/logout</span>
</span></span><span style="display:flex;"><span> - <span style="color:#f92672">uri</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">prefix</span>: <span style="color:#ae81ff">/api/v1/products</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">route</span>:
</span></span><span style="display:flex;"><span> - <span style="color:#f92672">destination</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">host</span>: <span style="color:#ae81ff">productpage</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">port</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">number</span>: <span style="color:#ae81ff">9080</span>
</span></span></code></pre></div><p>通过以下命令查看 Gateway 和 VirtualService 是否创建成功</p>
<pre tabindex="0"><code>kubectl get gateway
</code></pre><pre tabindex="0"><code>kubectl get virtualservice
</code></pre><p>可以看到 有一个 bookinfo-gateway 和 bookinfo 的 VirtualService</p>
<h3 id="修改-istio-ingressgateway-为-nodeport">修改 istio-ingressgateway 为 NodePort</h3>
<p>通过以下命令获取到 ingressgateway 的 ip</p>
<pre tabindex="0"><code>kubectl get svc istio-ingressgateway -n istio-system
</code></pre><p>istio-ingressgateway默认的服务类型是LoadBalancer,我是本地机器安装的k8s,修改 istio-ingressgateway type LoadBalancer 为 NodePort,port: 80 映射为 port: 30080
可以通过 nodeip:30080/productpage 来访问 bookinfo 服务</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>kubectl edit svc istio-ingressgateway -n istio-system
</span></span></code></pre></div><p>上面命令 会打开一个vim 编辑器,你需要修改 type 为 NodePort,并且将 修改 port: 80 映射为 port: 30080
查看修改结果</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>kubectl get svc istio-ingressgateway -n istio-system
</span></span></code></pre></div><p>显示类似为</p>
<pre tabindex="0"><code>NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
istio-ingressgateway NodePort 10.43.249.207 &lt;none&gt; 15021:30177/TCP,80:30080/TCP,443:30068/TCP,31400:30163/TCP,15443:30184/TCP 43d
</code></pre><h3 id="访问-bookinfo-服务">访问 bookinfo 服务</h3>
<p>上面已经将 istio-ingressgateway 的type 设置为 NodePort,并且将 port: 80 映射为 port: 30080
可以直接通过 k8s 节点的 ip 和 port 来访问 bookinfo 服务了
我的集群ip是 192.168.31.180,所以可以通过 http://192.168.31.180:30080/productpage 访问bookinfo 服务,需要将ip,prot 替换成自己的
<img src="https://qiniu.taoluyuan.com/2023/blog20230604130140.png?imageMogr2/auto-orient/thumbnail/!70p/blur/9x0/quality/75" alt="">
如官网所说,多刷新几次应用的页面,就会 看到 productpage 页面中会随机展示 reviews 服务的不同版本的效果(红色、黑色的星形或者没有显示)。reviews 服务出现这种情况是因为还没有使用 Istio 来控制版本的路由。</p>
<h2 id="可视化网格监控">可视化网格监控</h2>
<h3 id="安装-istio-telemetry-addons">安装 istio Telemetry Addons</h3>
<p>istion 和几个遥测应用都做了集成:kiali,jaeger,prometheus,grafana ,可以通过这些工具来监控 istio 的网格</p>
<p>istio Telemetry Addons相关源码位置在 <a href="https://github.com/istio/istio/tree/release-1.17/samples/addons">istio Telemetry Addons</a></p>
<p>我将 istio Telemetry Addons 其中的 jaeger,prometheus,grafana,kiali 相关的yaml文件放在 仓库 addons 目录下,可以通过以下命令安装</p>
<pre tabindex="0"><code>kubectl apply -f ./addons
</code></pre><p>或者</p>
<pre tabindex="0"><code>make install-telemetry-addons
</code></pre><p>等待安装完成后,通过以下命令查看kiali 是否部署完成</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>kubectl rollout status deployment/kiali -n istio-system
</span></span></code></pre></div><p>istio 官方文档说,采样率默认是 1%,所以要想查看追踪数据在第一个跟踪可见之前,您需要发送至少 100 个请求。 使用以下命令向 productpage 服务发送 100 个请求,需要注意将ip,prot 替换成自己的</p>
<pre tabindex="0"><code>for i in `seq 1 100`; do curl -s -o /dev/null http://192.168.31.180:30080/productpage; done
</code></pre><p>或者 make,需要将makefile 中的GATEWAY_URL 替换成自己的</p>
<pre tabindex="0"><code>make send-100-request
</code></pre><h3 id="使用-kiali-监控网格">使用 kiali 监控网格</h3>
<p>使用以下命令打开 kiali,会自动打开浏览器</p>
<pre tabindex="0"><code>istioctl dashboard kiali
</code></pre><p><img src="https://qiniu.taoluyuan.com/2023/blog20230604134656.png?imageMogr2/auto-orient/thumbnail/!70p/blur/9x0/quality/75" alt=""></p>
<h3 id="使用-jaeger-监控网格">使用 jaeger 监控网格</h3>
<p>使用以下命令打开 jaeger,会自动打开浏览器</p>
<pre tabindex="0"><code>istioctl dashboard jaeger
</code></pre><p><img src="https://qiniu.taoluyuan.com/2023/blog20230604134825.png?imageMogr2/auto-orient/thumbnail/!70p/blur/9x0/quality/75" alt=""></p>
<h3 id="使用-grafana-监控网格">使用 grafana 监控网格</h3>
<p>使用以下命令打开 prometheus,会自动打开浏览器</p>
<pre tabindex="0"><code>istioctl dashboard grafana
</code></pre><p><img src="https://qiniu.taoluyuan.com/2023/blog20230604135016.png?imageMogr2/auto-orient/thumbnail/!70p/blur/9x0/quality/75" alt=""></p>
<h2 id="总结">总结</h2>
<p>istio 部署多版本应用 和 可视化网格监控 已完成
文字有点多,但是操作起来很简单,我将所有的 yaml 文件都放在了仓库中<a href="https://github.com/webws/k8s-istio-practice">github k8s-istio-practice</a>,将所有的命令都写在了 makefile 中,只需要执行以下:</p>
<ol>
<li>安装 istio</li>
</ol>
<pre tabindex="0"><code>make install-istio
</code></pre><ol start="2">
<li>部署 bookinfo 应用</li>
</ol>
<pre tabindex="0"><code>make install-bookinfo
</code></pre><ol start="3">
<li>部署网关和路由</li>
</ol>
<pre tabindex="0"><code>make install-bookinfo-gateway
</code></pre><ol start="4">
<li>修改 istio-ingressgateway 为 NodePort
执行以下命令 修改 istio-ingressgateway 为 NodePort,并且将 port: 80 映射为 port: 30080</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>kubectl edit svc istio-ingressgateway -n istio-system
</span></span></code></pre></div><ol start="5">
<li>访问 bookinfo 服务
通过 k8s 节点的 ip 和 port 来访问 bookinfo 服务了,需要将ip,prot 替换成自己的</li>
</ol>
<pre tabindex="0"><code> http://192.168.31.180:30080/productpage
</code></pre><ol start="6">
<li>通过 监测工具监控网格</li>
</ol>
<ul>
<li>安装 istio Telemetry Addons</li>
</ul>
<pre tabindex="0"><code>make install-telemetry-addons
</code></pre><ul>
<li>打开 kiali</li>
</ul>
<pre tabindex="0"><code>istioctl dashboard kiali
</code></pre>- /posts/istio-getting-started/ - </description>
</item>
<item>
<title>通过 Github workflows CI/CD 自动化部署 Github Pages hugo 免费博客</title>
<link>/posts/github-workflows/</link>
<pubDate>Tue, 30 May 2023 10:29:04 +0800</pubDate>
<guid>/posts/github-workflows/</guid>
<description>套路猿 /posts/github-workflows/ -<h1 id="通过-github-workflows-cicd-自动化部署-github-pages-hugo-免费博客">通过 Github workflows CI/CD 自动化部署 Github Pages hugo 免费博客</h1>
<p>文章博客地址:<a href="https://blog.taoluyuan.com/posts/github-workflows/">https://blog.taoluyuan.com/posts/github-workflows/</a></p>
<h2 id="github-workflows-介绍">Github Workflows 介绍</h2>
<h3 id="github-actions-介绍">GitHub Actions 介绍</h3>
<ul>
<li>GitHub 文档:https://docs.github.com/zh/actions/learn-github-actions/understanding-github-actions</li>
<li>官方介绍:<code>GitHub Actions</code> 是一种持续集成和持续交付 (CI/CD) 平台,可用于自动执行生成、测试和部署管道。 您可以创建工作流程来构建和测试存储库的每个拉取请求,或将合并的拉取请求部署到生产环境</li>
</ul>
<h3 id="流程及原理介绍">流程及原理介绍</h3>
<ul>
<li>本文主要介绍使用GitHub Actions 来实现自动化部署博客网站 ,静态网站生成使用的是Hugo,部署使用的是Github pages,并且使用自定义域名。
<img src="https://qiniu.taoluyuan.com/2023/github-workflows.jpg?imageMogr2/auto-orient/thumbnail/!70p/blur/9x0/quality/75" alt=""></li>
</ul>
<ol>
<li>本地写hugo-blog 博客,hugo-blog 是一个hugo的博客模板,使用<code>hugo new site hugo-blog</code>命令创建,可以在里面写markdown文件</li>
<li>写好后推送到github hugo-blog 仓库,触发github actions ci/cd,执行hugo命令生成静态网站,并且推送到github-pages 仓库</li>
<li>github-pages 仓库接收到推送后,会自动部署到github pages,公网可以通过 github pages 域名 访问,也可以通过CNAME配置自定义域名访问</li>
</ol>
<h2 id="github-pages-介绍">Github Pages 介绍</h2>
<ul>
<li>Github Pages 是一个静态网站托管服务,可以通过github pages 托管静态网站,并且可以通过自定义域名访问</li>
<li>创建github pages 仓库,仓库名必须是<code>username.github.io</code>格式,username是你的github用户名,仓库名必须是这个,否则无法部署成功 访问地址就是 <a href="https://username.github.io">https://username.github.io</a></li>
<li>自定义域名访问,例如<code>www.abc.com</code>,在域名服务商添加CNAME记录,指向<code>username.github.io</code>, 然后在github pages 仓库设置中添加自定义域名, 这样通过www.abc.com 就能访问github pages</li>
<li>下面的 Actions 部分会介绍如何自动化部署到github pages,并且配置自定义域名</li>
</ul>
<h2 id="hugo-介绍">Hugo 介绍</h2>
<ul>
<li>Hugo 是一个静态网站生成器,可以通过markdown文件生成静态网站,官网:https://gohugo.io/</li>
<li>写好markdown文件后,执行hugo命令,在public目录生成静态网站,然后 将public目录推送到github pages 仓库</li>
<li>github actions工作流 就是通过hugo命令生成静态网站,并且推送到github pages 仓库</li>
</ul>
<h2 id="使用-github-actions-自动化部署">使用 Github Actions 自动化部署</h2>
<h3 id="创建-github-actions">创建 Github Actions</h3>
<p>在github 仓库中(hugo-blog)创建.github/workflows目录,并且在目录中创建deploy.yml文件,文件名可以自定义,但是后缀必须是yml,例如deploy.yml,这样就创建了一个github actions,并且会自动执行,下面介绍我的deploy.yml文件</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yml" data-lang="yml"><span style="display:flex;"><span><span style="color:#f92672">name</span>: <span style="color:#ae81ff">deploy</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">on</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">push</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">workflow_dispatch</span>:
</span></span><span style="display:flex;"><span><span style="color:#f92672">jobs</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">build</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">runs-on</span>: <span style="color:#ae81ff">ubuntu-latest</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">steps</span>:
</span></span><span style="display:flex;"><span> - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Checkout</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">uses</span>: <span style="color:#ae81ff">actions/checkout@v2</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">with</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">submodules</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">fetch-depth</span>: <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Setup Hugo</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">uses</span>: <span style="color:#ae81ff">peaceiris/actions-hugo@v2</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">with</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">hugo-version</span>: <span style="color:#e6db74">&#34;latest&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Build Web</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">run</span>: <span style="color:#ae81ff">hugo</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Deploy Web</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">uses</span>: <span style="color:#ae81ff">peaceiris/actions-gh-pages@v3</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">with</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">PERSONAL_TOKEN</span>: <span style="color:#ae81ff">${{ secrets.BLOG_TOKEN }}</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">EXTERNAL_REPOSITORY</span>: <span style="color:#ae81ff">webws/webws.github.io</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">PUBLISH_BRANCH</span>: <span style="color:#ae81ff">master</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">PUBLISH_DIR</span>: <span style="color:#ae81ff">./public</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">commit_message</span>: <span style="color:#ae81ff">${{ github.event.head_commit.message }}</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">cname</span>: <span style="color:#ae81ff">${{ secrets.DOMAIN }}</span>
</span></span></code></pre></div><p>上面 GitHub Actions配置文件用于自动部署Hugo博客到我的 GitHub Pages。以下是每个步骤的功能和解释:</p>
<h4 id="步骤1checkout">步骤1:Checkout</h4>
<p>此步骤使用 <code>actions/checkout</code> 插件来检出 GitHub 仓库,具体使用文档地址是 <a href="https://github.com/marketplace/actions/checkout">checkout</a>
<code>submodules: true</code> 参数用于同时检出子模块,<code>fetch-depth: 0</code> 用于完整地检出所有历史记录。</p>
<h4 id="步骤2setup-hugo">步骤2:Setup Hugo</h4>
<p>此步骤使用 <code>peaceiris/actions-hugo</code> 插件来安装最新版本的 Hugo。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Setup Hugo</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">uses</span>: <span style="color:#ae81ff">peaceiris/actions-hugo@v2</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">with</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">hugo-version</span>: <span style="color:#e6db74">&#34;latest&#34;</span>
</span></span></code></pre></div><h4 id="步骤3build-web">步骤3:Build Web</h4>
<p>此步骤在运行时调用 Hugo 构建静态网站,并在 <code>public</code> 目录中生成静态html文件</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Build Web</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">run</span>: <span style="color:#ae81ff">hugo</span>
</span></span></code></pre></div><h4 id="步骤4deploy-web">步骤4:Deploy Web</h4>
<p>此步骤使用 <code>peaceiris/actions-gh-pages</code> 插件将静态网站部署到 GitHub Pages 上。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>- <span style="color:#f92672">name</span>: <span style="color:#ae81ff">Deploy Web</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">uses</span>: <span style="color:#ae81ff">peaceiris/actions-gh-pages@v3</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">with</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">PERSONAL_TOKEN</span>: <span style="color:#ae81ff">${{ secrets.BLOG_TOKEN }}</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">EXTERNAL_REPOSITORY</span>: <span style="color:#ae81ff">webws/webws.github.io</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">PUBLISH_BRANCH</span>: <span style="color:#ae81ff">master</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">PUBLISH_DIR</span>: <span style="color:#ae81ff">./public</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">commit_message</span>: <span style="color:#ae81ff">${{ github.event.head_commit.message }}</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">cname</span>: <span style="color:#ae81ff">${{ secrets.DOMAIN }}</span>
</span></span></code></pre></div><p>参数的含义:</p>
<ul>
<li><code>PERSONAL_TOKEN</code>: GitHub Personal Access Tokens 用于访问 GitHub 仓库,需要 到<img src="https://github.com/settings/tokens" alt="github Personal access tokens设置"> ,添加权限 并将Token存储在仓库的 Secrets 中以供 Workflow 使用</li>
<li><code>EXTERNAL_REPOSITORY</code>: 部署到的 GitHub Pages 仓库,webws/webws.github.io 是我的github pages 仓库,需要修改为你的github pages 仓库</li>
<li><code>PUBLISH_BRANCH</code>: 要在其上部署站点的分支名称(通常为master)。</li>
<li><code>PUBLISH_DIR</code>: hugo 静态html文件目录。(在此例中,Hugo 输出位于 <code>./public</code> 目录中)。</li>
<li><code>commit_message</code>: 提交更改时使用的提交消息,从上游分支获取。</li>
<li><code>cname</code>: 自定义域名,CNAME记录,我自己的是 blog.taoluyuan.com,需要修改为你的自定义域名,如果没有,可以删除这个参数,使用默认的github pages域名也访问 webws.github.io</li>
</ul>
<p>设置 Secrets 变量,对应 yml 文件中的 PERSONAL_TOKEN 和 DOMAIN ,具体设置 在 仓库中(hugo-blog) 的 Settings -&gt; secrets and variables-&gt;actions 中,hugo-blog 要换成你自己的仓库名</p>
<ul>
<li><code>BLOG_TOKEN</code>: GitHub Personal Access Token。</li>
<li><code>DOMAIN</code>: 你的自定义域名。</li>
</ul>
<h3 id="触发-github-actions">触发 Github Actions</h3>
<ul>
<li>在github 仓库中(hugo-blog)创建.md文件,并且提交到github,这样就会触发github actions,自动部署到github pages 仓库,并且可以通过自定义域名访问了</li>
<li>可以通过 仓库中 Actions 标签查看部署状态</li>
</ul>
<h3 id="访问-github-pages">访问 Github Pages</h3>
<ol>
<li>通过github pages域名访问, <a href="https://webws.github.io">https://webws.github.io</a>,因为我设置了自定义域名,所以这个域名会自动跳转到 <a href="https://blog.taoluyuan.com">https://blog.taoluyuan.com</a></li>
<li>通过自定义域名访问, <a href="https://blog.taoluyuan.com">https://blog.taoluyuan.com</a></li>
</ol>
- /posts/github-workflows/ - </description>
</item>
<item>
<title> 使用golang 基于 OpenAI Embedding + qdrant 实现k8s本地知识库</title>
<link>/posts/embedding-openai/</link>
<pubDate>Fri, 26 May 2023 01:03:00 +0800</pubDate>
<guid>/posts/embedding-openai/</guid>
<description>套路猿 /posts/embedding-openai/ -<h1 id="使用golang-基于-openai-embedding--qdrant-实现k8s本地知识库">使用golang 基于 OpenAI Embedding + qdrant 实现k8s本地知识库</h1>
<p>文章博客地址:<a href="https://blog.taoluyuan.com/posts/embedding-openai/">套路猿-使用golang 基于 OpenAI Embedding + qdrant 实现k8s本地知识库</a></p>
<h2 id="流程">流程</h2>
<p><img src="https://qiniu.taoluyuan.com/2023/blog20230527115805.png?imageMogr2/auto-orient/thumbnail/!70p/blur/9x0/quality/75" alt=""></p>
<ol>
<li>将数据集 通过 openai embedding 得到向量+组装payload,存入 qdrant</li>
<li>用户进行问题搜索,通过 openai embedding 得到向量,从 qdrant 中搜索相似度大于0.8的数据</li>
<li>从 qdrant 中取出相似度高的数据</li>
<li>将获取到的QA,组装成 prompt 向chatgpt进行提问,得到回答</li>
</ol>
<h2 id="向量数据库-qdrant">向量数据库 qdrant</h2>
<ul>
<li>qdrant 是一个开源的向量搜索引擎,支持多种向量距离计算方式</li>
<li>官方文档:https://qdrant.tech/documentation/quick_start/</li>
<li>本节 介绍 qdrant 都是基于官方文档的例子,如已熟悉可以直接阅读下一节 [数据导入k8s知识库]</li>
</ul>
<h3 id="安装-qdrant">安装 qdrant</h3>
<p>docker 安装</p>
<pre tabindex="0"><code>docker pull qdrant/qdrant &amp;&amp; \
docker run -p 6333:6333 -p 6334:6334 qdrant/qdrant
</code></pre><h3 id="collection-说明">collection 说明</h3>
<p>collection 是 qdrant 中的一个概念,类似于 mysql 中的 database,用于区分不同的数据集合
官方文档:https://qdrant.tech/documentation/collections/#collections
collection 下面是 collection 字段说明,以创建 collection 为例</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>PUT /collections/<span style="color:#f92672">{</span>collection_name<span style="color:#f92672">}</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;name&#34;</span>: <span style="color:#e6db74">&#34;example_collection&#34;</span>,
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;vectors&#34;</span>: <span style="color:#f92672">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;size&#34;</span>: 300,
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;distance&#34;</span>: <span style="color:#e6db74">&#34;Cosine&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">}</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">}</span>
</span></span></code></pre></div><p>name: collection 名称
vectors: 向量的配置<br>
size: 向量的维度
distance: 向量的距离计算方式,Cosine(余弦距离), Euclidean(欧式距离),Dot product(点积)<br>
如果需要将 openai embedding 后 存入 qdrant,需要将 size 设置为 1536<a href="https://openai.com/blog/new-and-improved-embedding-model">openai embedding</a></p>
<h3 id="插入数据">插入数据</h3>
<p>这个是官网 http add point 的例子,可以看到 payload 是可以存储任意的 json 数据,这个数据可以用于后续的过滤</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -L -X PUT <span style="color:#e6db74">&#39;http://localhost:6333/collections/test_collection/points?wait=true&#39;</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span> -H <span style="color:#e6db74">&#39;Content-Type: application/json&#39;</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span> --data-raw <span style="color:#e6db74">&#39;{
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74"> &#34;points&#34;: [
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74"> {&#34;id&#34;: 1, &#34;vector&#34;: [0.05, 0.61, 0.76, 0.74], &#34;payload&#34;: {&#34;city&#34;: &#34;Berlin&#34; }},
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74"> {&#34;id&#34;: 2, &#34;vector&#34;: [0.19, 0.81, 0.75, 0.11], &#34;payload&#34;: {&#34;city&#34;: [&#34;Berlin&#34;, &#34;London&#34;] }},
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74"> {&#34;id&#34;: 3, &#34;vector&#34;: [0.36, 0.55, 0.47, 0.94], &#34;payload&#34;: {&#34;city&#34;: [&#34;Berlin&#34;, &#34;Moscow&#34;] }},
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74"> {&#34;id&#34;: 4, &#34;vector&#34;: [0.18, 0.01, 0.85, 0.80], &#34;payload&#34;: {&#34;city&#34;: [&#34;London&#34;, &#34;Moscow&#34;] }},
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74"> {&#34;id&#34;: 5, &#34;vector&#34;: [0.24, 0.18, 0.22, 0.44], &#34;payload&#34;: {&#34;count&#34;: [0] }},
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74"> {&#34;id&#34;: 6, &#34;vector&#34;: [0.35, 0.08, 0.11, 0.44]}
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74"> ]
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74"> }&#39;</span>
</span></span></code></pre></div><ul>
<li>id:唯一</li>
<li>vector:向量,可在HuggingFace 找相应的模型训练,获取,也可以 openai embedding 得到</li>
<li>payload:任意的自定义 json 数据</li>
</ul>
<h3 id="搜索数据">搜索数据</h3>
<p>这是 qdrant 官方搜索数据的例子</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -L -X POST <span style="color:#e6db74">&#39;http://localhost:6333/collections/test_collection/points/search&#39;</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span> -H <span style="color:#e6db74">&#39;Content-Type: application/json&#39;</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span> --data-raw <span style="color:#e6db74">&#39;{
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74"> &#34;vector&#34;: [0.2,0.1,0.9,0.7],
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74"> &#34;limit&#34;: 3
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74"> }&#39;</span>
</span></span></code></pre></div><p>vector:向量,通过 openai embedding 得到
limit:返回的数据条数</p>
<h2 id="数据导入k8s知识库">数据导入k8s知识库</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-golang" data-lang="golang"><span style="display:flex;"><span><span style="color:#75715e">// 模拟数据集 question:answer
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">questions</span> = []<span style="color:#66d9ef">string</span>{
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;什么是Kubernetes中的Deployment?&#34;</span>,
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;Kubernetes中的Service有什么作用?&#34;</span>,
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">answers</span> = []<span style="color:#66d9ef">string</span>{
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;Deployment是Kubernetes中用于管理应用程序副本的资源对象。它提供了副本的声明性定义,可以实现应用程序的部署、扩展和更新。&#34;</span>,
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;Service用于定义一组Pod的访问方式和网络策略。它为Pod提供了一个稳定的网络地址,并可以实现负载均衡、服务发现和内部通信。&#34;</span>,
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span><span style="color:#75715e">// 第一步:自己创建 一个collection: kubernetes
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">err</span> <span style="color:#66d9ef">error</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">err</span> = <span style="color:#a6e22e">qdrant</span>.<span style="color:#a6e22e">Collection</span>(<span style="color:#e6db74">&#34;kubernetes&#34;</span>).<span style="color:#a6e22e">Create</span>(<span style="color:#ae81ff">1536</span>)
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Fatalln</span>(<span style="color:#e6db74">&#34;创建collection出错:&#34;</span>, <span style="color:#a6e22e">err</span>.<span style="color:#a6e22e">Error</span>())
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">points</span> <span style="color:#f92672">:=</span> []<span style="color:#f92672">*</span><span style="color:#a6e22e">pb</span>.<span style="color:#a6e22e">PointStruct</span>{}
</span></span><span style="display:flex;"><span> <span style="color:#75715e">// 批量 进行BuildQdrantPoint
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">index</span>, <span style="color:#a6e22e">question</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">questions</span> {
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">index</span> &lt; <span style="color:#ae81ff">9</span> {
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">continue</span>
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">p</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">BuildQdrantPoint</span>(<span style="color:#a6e22e">question</span>, <span style="color:#a6e22e">answers</span>[<span style="color:#a6e22e">index</span>])
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Fatalln</span>(<span style="color:#e6db74">&#34;创建point出错:&#34;</span>, <span style="color:#a6e22e">err</span>.<span style="color:#a6e22e">Error</span>())
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#a6e22e">p</span>.<span style="color:#a6e22e">Id</span>)
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">points</span> = append(<span style="color:#a6e22e">points</span>, <span style="color:#a6e22e">p</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">err</span> = <span style="color:#a6e22e">qdrant</span>.<span style="color:#a6e22e">FastQdrantClient</span>.<span style="color:#a6e22e">CreatePoints</span>(<span style="color:#e6db74">&#34;kubernetes&#34;</span>, <span style="color:#a6e22e">points</span>)
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Fatalln</span>(<span style="color:#e6db74">&#34;批量创建point出错:&#34;</span>, <span style="color:#a6e22e">err</span>.<span style="color:#a6e22e">Error</span>())
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><ul>
<li>模拟数据集,将数据集导入到 k8s 知识数据库中</li>
<li>BuildQdrantPoint 函数是将问题和答案转换成 qdrant 的 point</li>
<li>其中 vector 是通过 openai embedding 得到的,这里使用的是 <a href="https://openai.com/blog/new-and-improved-embedding-model">openai embedding</a></li>
</ul>
<h2 id="搜索数据-1">搜索数据</h2>
<h3 id="代码实现">代码实现</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-golang" data-lang="golang"><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">myai</span> <span style="color:#e6db74">&#34;embedding-knowledge-base/ai&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;embedding-knowledge-base/qdrant&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">prompt</span> <span style="color:#f92672">:=</span> <span style="color:#e6db74">&#34;什么是Kubernetes中的DaemonSet?&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e">// prompt := &#34;苹果不削皮能吃吗&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#a6e22e">p_vec</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">myai</span>.<span style="color:#a6e22e">SimpleGetVec</span>(<span style="color:#a6e22e">prompt</span>)
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span> panic(<span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">points</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">qdrant</span>.<span style="color:#a6e22e">FastQdrantClient</span>.<span style="color:#a6e22e">Search</span>(<span style="color:#e6db74">&#34;kubernetes&#34;</span>, <span style="color:#a6e22e">p_vec</span>)
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span> panic(<span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;用户的问题是:%s\n&#34;</span>, <span style="color:#a6e22e">prompt</span>)
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">points</span>[<span style="color:#ae81ff">0</span>].<span style="color:#a6e22e">Score</span> &lt; <span style="color:#ae81ff">0.8</span> {
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;违规问题或者超纲问题&#34;</span>)
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span>
</span></span><span style="display:flex;"><span> }
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">answer</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">points</span>[<span style="color:#ae81ff">0</span>].<span style="color:#a6e22e">Payload</span>[<span style="color:#e6db74">&#34;answers&#34;</span>].<span style="color:#a6e22e">GetStringValue</span>()
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;知识库答案是:%s\n&#34;</span>, <span style="color:#a6e22e">answer</span>)
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">tmpl</span> <span style="color:#f92672">:=</span> <span style="color:#e6db74">&#34;question: %s\n&#34;</span> <span style="color:#f92672">+</span> <span style="color:#e6db74">&#34;reference answer: %s\n&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">finalPrompt</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Sprintf</span>(<span style="color:#a6e22e">tmpl</span>, <span style="color:#a6e22e">prompt</span>, <span style="color:#a6e22e">points</span>[<span style="color:#ae81ff">0</span>].<span style="color:#a6e22e">Payload</span>[<span style="color:#e6db74">&#34;question&#34;</span>].<span style="color:#a6e22e">GetStringValue</span>(), <span style="color:#a6e22e">answer</span>)
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;------------------------&#34;</span>)
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;结合知识库参考答案:chatgpt的回答是:%s\n&#34;</span>, <span style="color:#a6e22e">myai</span>.<span style="color:#a6e22e">K8sChat</span>(<span style="color:#a6e22e">finalPrompt</span>))
</span></span><span style="display:flex;"><span> <span style="color:#75715e">// 不结合知识库参考答案
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;不依赖本地知识库, chatgpt的直接回答是:%s\n&#34;</span>, <span style="color:#a6e22e">myai</span>.<span style="color:#a6e22e">K8sChat</span>(<span style="color:#a6e22e">prompt</span>))
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><ul>
<li>通过 prompt 搜索qdrant 知识库,如果相似度小于 0.8,有可能是用户乱提问,或问知识库无关的问题,直接返回</li>
<li>取相似度度大于 0.8,则取第一条数据,组装成promot向gpt进行提问,得到回答</li>
<li>具体的实现可以参考 main.go 的代码</li>
</ul>
<h3 id="示例">示例</h3>
<ol>
<li>问无关的问题,比如:苹果不削皮能吃吗
<img src="https://qiniu.taoluyuan.com/2023/blog20230526002303.png?imageMogr2/auto-orient/interlace/1/blur/1x0/quality/70%7Cwatermark/2/text/YmxvZy50YW9sdXl1YW4uY29t/font/5a6L5L2T/fontsize/500/fill/I0E4QTBBMA==/dissolve/100/gravity/NorthWest/dx/10/dy/10" alt="">
可以看到 相似度太低,提示违规问题或者超纲问题</li>
<li>问k8s 本地知识库的问题,比如:什么是Kubernetes中的Deployment?
<img src="https://qiniu.taoluyuan.com/2023/blog20230526002640.png?imageMogr2/auto-orient/interlace/1/blur/1x0/quality/70%7Cwatermark/2/text/YmxvZy50YW9sdXl1YW4uY29t/font/5a6L5L2T/fontsize/500/fill/I0E4QTBBMA==/dissolve/100/gravity/NorthWest/dx/10/dy/1" alt=""></li>
<li>问k8s本地知识库的问题,但单独向chatgpt提问,得到的答案 并不是已定与k8s相关,比如问 网关是什么<br>
<img src="http://qiniu.taoluyuan.com/2023/blog20230526003436.png?imageMogr2/auto-orient/interlace/1/blur/1x0/quality/70%7Cwatermark/2/text/YmxvZy50YW9sdXl1YW4uY29t/font/5a6L5L2T/fontsize/500/fill/I0E4QTBBMA==/dissolve/100/gravity/NorthWest/dx/10/dy/10" alt=""></li>
</ol>
<ul>
<li>可以看到,红线部分,是直接将用户问题 向 chatgpt 请求 得到的答案,跟k8s无关</li>
<li>红线前面的回答:是正确的,结合k8s本地知识库,可以让回答偏向 数据集设定的主题</li>
</ul>
<h2 id="示例源码地址及使用">示例源码地址及使用</h2>
<p>源码地址:<a href="https://github.com/webws/embedding-knowledge-base">embedding-knowledge-base</a>
进入根目录,将目录 ai/common.go 的 以下 const改成自己的</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-golang" data-lang="golang"><span style="display:flex;"><span> <span style="color:#a6e22e">SocksProxy</span> = <span style="color:#e6db74">&#34;socks5://127.0.0.1:1080&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">AIKey</span> = <span style="color:#e6db74">&#34;your api key&#34;</span>
</span></span></code></pre></div><h3 id="docker-安装-qdrant">docker 安装 qdrant</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>make install-qdrant
</span></span></code></pre></div><h3 id="数据集导入qdrant">数据集导入qdrant</h3>
<ul>
<li>导入 adrant,我这边就是模拟 了十几条k8s相关的问题,在 prebuild/prebuild.go</li>
<li>更多的数据集,需要自己用脚本抓取,然后导入qdrant</li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>make import-qdrant
</span></span></code></pre></div><h3 id="搜索">搜索</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>make search
</span></span></code></pre></div>- /posts/embedding-openai/ - </description>
</item>
<item>
<title>frp 和 nginx 搭建一个内网穿透服务器</title>
<link>/posts/frp-server/</link>
<pubDate>Wed, 17 May 2023 22:15:05 +0800</pubDate>
<guid>/posts/frp-server/</guid>
<description>套路猿 /posts/frp-server/ -<p>文章博客地址: <a href="https://blog.taoluyuan.com/posts/frp-server/">https://blog.taoluyuan.com/posts/frp-server/</a></p>
<h3 id="相关资料">相关资料</h3>
<ul>
<li>frp下载 :<a href="https://github.com/fatedier/frp">https://github.com/fatedier/frp</a></li>
<li>相关文档: <a href="https://github.com/fatedier/frp">https://github.com/fatedier/frp</a></li>
</ul>
<h3 id="下载">下载</h3>
<p>下载地址:https://github.com/fatedier/frp/releases
选择对应的版本进行下载</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>wget https://github.com/fatedier/frp/releases/download/v0.21.0/frp_0.21.0_linux_386.tar.gz
</span></span></code></pre></div><p>如果是windows需要下载windos版本</p>
<pre tabindex="0"><code>wget https://github.com/fatedier/frp/releases/download/v0.21.0/frp_0.21.0_darwin_amd64.tar.gz
</code></pre><p>下载后、我的服务端是centos 客户端是windows</p>
<blockquote>
<p>服务端需要关注的文件是 frps、frps.ini
客户端需要关注的文件是 frpc(或者是frpc.exe)、frpc.ini</p>
</blockquote>
<p>注意,如果运行的环境是windows就要运行windows版本的,也就是exe后缀的</p>
<h3 id="配置服务端">配置服务端</h3>