-
Notifications
You must be signed in to change notification settings - Fork 0
/
local-search.xml
821 lines (394 loc) · 705 KB
/
local-search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
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
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>go AES CFB加解密与Java互转</title>
<link href="/post/5ddf423b.html"/>
<url>/post/5ddf423b.html</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>go 和java 的 AES CFB加密略有不同</p><h2 id="java-版本"><a href="#java-版本" class="headerlink" title="java 版本"></a>java 版本</h2><h3 id="加密"><a href="#加密" class="headerlink" title="加密"></a>加密</h3><div class="hljs"><pre><code class="hljs java"> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">byte</span>[] encrypt(<span class="hljs-keyword">byte</span>[] encoded_payload, <span class="hljs-keyword">byte</span>[] key) <span class="hljs-keyword">throws</span> NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, ShortBufferException, IllegalBlockSizeException, BadPaddingException { SecureRandom rand = <span class="hljs-keyword">new</span> SecureRandom(); <span class="hljs-comment">// or .getInstance* as you prefer</span> SecretKeySpec key_spec = <span class="hljs-keyword">new</span> SecretKeySpec(key, <span class="hljs-string">"AES"</span>); Cipher cipher = Cipher.getInstance(<span class="hljs-string">"AES/CFB/NoPadding"</span>); <span class="hljs-keyword">int</span> block_size = cipher.getBlockSize(); <span class="hljs-keyword">byte</span>[] buffer = <span class="hljs-keyword">new</span> <span class="hljs-keyword">byte</span>[block_size]; rand.nextBytes(buffer); IvParameterSpec iv = <span class="hljs-keyword">new</span> IvParameterSpec(buffer); buffer = Arrays.copyOf(buffer, block_size + encoded_payload.length); cipher.init(Cipher.ENCRYPT_MODE, key_spec, iv); cipher.doFinal(encoded_payload, <span class="hljs-number">0</span>, encoded_payload.length, buffer, block_size); <span class="hljs-keyword">return</span> buffer;}</code></pre></div><h3 id="解密"><a href="#解密" class="headerlink" title="解密"></a>解密</h3><div class="hljs"><pre><code class="hljs java"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">byte</span>[] decrypt(<span class="hljs-keyword">byte</span>[] payload, <span class="hljs-keyword">byte</span>[] key) <span class="hljs-keyword">throws</span> NoSuchPaddingException, NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, InvalidKeyException { SecretKeySpec key_spec = <span class="hljs-keyword">new</span> SecretKeySpec(key, <span class="hljs-string">"AES"</span>); Cipher cipher = Cipher.getInstance(<span class="hljs-string">"AES/CFB/NoPadding"</span>); <span class="hljs-keyword">int</span> block_size = cipher.getBlockSize(); IvParameterSpec iv = <span class="hljs-keyword">new</span> IvParameterSpec(Arrays.copyOf(payload, block_size)); <span class="hljs-keyword">byte</span>[] decryption_data = Arrays.copyOfRange(payload, block_size, payload.length); cipher.init(Cipher.DECRYPT_MODE, key_spec, iv); <span class="hljs-keyword">return</span> cipher.doFinal(decryption_data);}</code></pre></div><h2 id="go-版本"><a href="#go-版本" class="headerlink" title="go 版本"></a>go 版本</h2><h3 id="go加密"><a href="#go加密" class="headerlink" title="go加密"></a>go加密</h3><div class="hljs"><pre><code class="hljs go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">Encrypt</span><span class="hljs-params">(payload []<span class="hljs-keyword">byte</span>, key []<span class="hljs-keyword">byte</span>)</span> <span class="hljs-params">([]<span class="hljs-keyword">byte</span>, error)</span></span> {block, err := aes.NewCipher(key)<span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> { <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, err}ciphertext := <span class="hljs-built_in">make</span>([]<span class="hljs-keyword">byte</span>, aes.BlockSize+<span class="hljs-built_in">len</span>(payload))iv := ciphertext[:aes.BlockSize]<span class="hljs-keyword">if</span> _, err := io.ReadFull(rand.Reader, iv); err != <span class="hljs-literal">nil</span> { <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, err}cipher.NewCFBEncrypter(block, iv).XORKeyStream(ciphertext[aes.BlockSize:], payload)<span class="hljs-keyword">return</span> ciphertext, <span class="hljs-literal">nil</span>}</code></pre></div><h3 id="go解密"><a href="#go解密" class="headerlink" title="go解密"></a>go解密</h3><div class="hljs"><pre><code class="hljs go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">Decrypt</span><span class="hljs-params">(payload []<span class="hljs-keyword">byte</span>, key []<span class="hljs-keyword">byte</span>)</span> <span class="hljs-params">([]<span class="hljs-keyword">byte</span>, error)</span></span> { block, err := aes.NewCipher(key) <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> { <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, err } <span class="hljs-keyword">if</span> <span class="hljs-built_in">len</span>(payload) < aes.BlockSize { <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>, errors.New(<span class="hljs-string">"ciphertext too short"</span>) } iv := payload[:aes.BlockSize] payload = payload[aes.BlockSize:] cipher.NewCFBDecrypter(block, iv).XORKeyStream(payload, payload) <span class="hljs-keyword">return</span> payload, <span class="hljs-literal">nil</span>}</code></pre></div>]]></content>
<categories>
<category>编程</category>
</categories>
<tags>
<tag>java</tag>
<tag>security</tag>
<tag>go</tag>
<tag>AES</tag>
<tag>CFB</tag>
</tags>
</entry>
<entry>
<title>终端那些事</title>
<link href="/post/ed1d6ed5.html"/>
<url>/post/ed1d6ed5.html</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>最近在学习和处理终端的输入和输出以及命令拦截问题,但终端在编程里属于比较小众的领域,比较有名的类库不多,比如 go 的 <a href="https://github.com/c-bata/go-prompt" target="_blank" rel="noopener">go-prompt</a>、<a href="https://github.com/nsf/termbox-go" target="_blank" rel="noopener">termbox-go</a>、<a href="https://github.com/creack/pty" target="_blank" rel="noopener">pty</a> 以及 ts 的 <a href="https://github.com/xtermjs/xterm.js" target="_blank" rel="noopener">xterm.js</a>。</p><p>不同的终端,在处理输入和输出时,所对应的数据也不一样,以及存在跨平台等问题。总得来说跟终端打交道,就是一件苦差事,不过有这么多优秀的开源类库,让我们在了解和处理终端问题时,提供了和很好的思路。</p><h2 id="终端的介绍"><a href="#终端的介绍" class="headerlink" title="终端的介绍"></a>终端的介绍</h2><p>终端(英语:Computer terminal),是一台电脑或者计算机系统,用来让用户输入数据,及显示其计算结果的机器,简而言之就是人类用户与计算机交互的设备。终端有些是全电子的,也有些是机电的。其又名终端机,它与一部独立的电脑不同,但也是电脑组成的部分。– by <a href="https://zh.wikipedia.org/wiki/%E7%B5%82%E7%AB%AF" target="_blank" rel="noopener">wiki</a></p><h3 id="终端的种类"><a href="#终端的种类" class="headerlink" title="终端的种类"></a>终端的种类</h3><h3 id="硬件终端"><a href="#硬件终端" class="headerlink" title="硬件终端"></a>硬件终端</h3><p>终端其实就是一种输入输出设备,相对于计算机主机而言属于外设,本身并不提供运算处理功能。早期的计算机终端一般是机电的电传打字机,比如ASR33。但是对于大多数应用来说它们太慢了,在卡片或磁带等物理性的材料上标记好资料之后,放入计算机再印出结果,过程非常费工。1970年代初许多电脑公司认识到电视输入终端比穿孔卡片要好得多,而且可以使得计算机更加容易与用户互动,产生新的应用。当时的毛病在于相对于当时的小型计算机来说要显示一页文字所需要的内存太大了。在集成电路普及以前要显示电视信号所需要的速度对当时的逻辑门所提出的技术挑战也太高了。当时有一家公司宣布要生产一台价值15,000美元的视频终端,吸引了许多购户,但是最后它的工程师们决定这个计划无法完成。另一个解决方法是泰克公司发明的存储管,这台机器可以显示输出给它的信息,但是无法刷新。</p><p>约1982年左右的一台ASCII视频终端<br>后来输出端的显示功能被发展出来后,早期的视频终端使用特别的逻辑门,没有自己的中央处理器。发展微处理器的动机之一就是要简化终端里的电子组件的复杂性。大多数终端的屏幕是绿色或者橙色的,它们与大型计算机相连。典型的终端使用RS-232之类的串行数据通信与主机相连,IBM使用它自己的系统网络体系结构协议通过同轴电缆来连接其主机与终端。</p><p>最后所谓的智能终端(如VT52和VT100)被引入。今天依然有许多这两个终端的模拟软件。这些终端之所以被称为“智能”是因为它们理解转义序列,可以定位光标和控制显示位置,这样设计的终端很容易操作,已经有现代电脑的样子了。重要的非VT100终端有IBM 3270、不同的慧智模型和Tektronix 4014。1970年代里世界上有十数个终端生产商,大多数终端的指令不兼容。1970年代和1980年代初最重要的终端生产商有迪吉多、慧智、Televideo、利尔·西格勒公司和Heathkit。</p><p>早期的IBM个人计算机虽然也使用绿色的屏幕,但是它不算终端。个人计算机的屏幕不包括任何产生字母的硬件,所有的视频信号是在个人计算机的显卡里产生的。但是使用相应的模拟程序一台个人计算机可以与大型计算机相连模拟终端。使用微处理器的个人计算机大大地取消了对终端的需要,人与电脑的接触直接用图像的操作系统来代劳了。但今天大多数个人计算机的Telnet用户端仍提供最普遍的终端(一般VT100)的模拟,但这不是真正的物理终端。</p><h4 id="图形终端"><a href="#图形终端" class="headerlink" title="图形终端"></a>图形终端</h4><p>有些终端不但可以显示文字,而且可以显示矢量图形和位图。计算机向终端输出绘图指令,终端则向计算机输送用户输入(通过键盘或者定位设备)。</p><p>事实上今天过去简单的图形终端已经完全被全功能视频显示器代替了。今天在计算机中图形用户界面无处不在。大多数终端模拟程序是在图形环境内运行的。我们现在主要是透过这些辅助工具,已经很少有直接终端的存在。</p><p>X终端是专门给X窗口系统设计的图形终端,提供连接到服务器系统上运行的KDE、GNOME或其它基于X窗口系统的平台的可能性。</p><h4 id="虚拟终端"><a href="#虚拟终端" class="headerlink" title="虚拟终端"></a>虚拟终端</h4><p>由于个人电脑的普及今天已经很少有专门的计算机终端作为界面了。现代的操作系统如Linux和BSD及其派生物使用与硬件基本无关的虚拟终端。输出系统一般是屏幕,输入系统则是键盘。</p><p>在使用X窗口系统这样的图形用户界面时在屏幕上一般有多个与不同应用相关的窗口开着,而不是只有一个与一个单个过程相连的文字流。在这种情况下用户一般使用终端模拟程序。这样用户可以不必使用专门的终端设备来与计算机交换。</p><p>虚拟终端机(英语:Terminal emulator)是在个人电脑上虚拟的一个终端以及为此目的而写的软件。虚拟终端的目的是达到个人电脑及其用户能够与大型计算机的连接。一般来说需要连接的大型计算机是IBM的大型计算机或者所谓的超小型计算机(过去往往是迪吉多的VAX)。</p><p>虚拟终端使得个人电脑的用户可以直接使用他的个人电脑来与大型计算机联系,而不必使用专门的终端。</p><p>通过虚拟终端的软件虚拟终端还可以扩展大型计算机的标准终端的功能,通过虚拟终端不但可以将个人电脑上的数据传递给大型计算机,而且还可以将大型计算机的数据传递给个人电脑,并在个人电脑上继续加工。</p><p>一般大型计算机的终端是字母式的输入和输出接口,因此一个虚拟终端至少需要一个能够模拟这样的字母式(比如ASCII)输入和输出接口的能力。最常见的平台是图像式的用户表面。要使得新的、图像式的程序能够使用老的字母式的或者没有图像式输入和输出能力的程序也需要虚拟终端。</p><p>现代的大型计算机也内部使用虚拟终端,这样它们可以向老的、需要终端的程序假装一个终端,而实际上它则将程序的显示转到显卡上。比如Linux以及其它大多数基于个人电脑的类似Unix的操作系统假装有六至十个这样的“虚拟”的终端。</p><h3 id="使用go处理终端的输入和输出"><a href="#使用go处理终端的输入和输出" class="headerlink" title="使用go处理终端的输入和输出"></a>使用go处理终端的输入和输出</h3><p>通常,如果我们需要设计和使用一种终端,比如拦截终端命令,执行命令前检查文件大小,设置命令白名单拒绝高危命令。</p><h4 id="xterm"><a href="#xterm" class="headerlink" title="xterm"></a>xterm</h4><p>xterm是一个X Window System上的标准虚拟终端。用户可以在同一个显示器上开启许多xterm,每一个都为其中运行的进程提供独立的输入输出(一般来说此进程是Unix shell)。</p><p>xterm 最先是Jim Gettys的学生Mark Vandevoorde在1984年夏天为VS100写的独立虚拟终端,当时X的开发才刚刚开始。很快人们就发现它作为X的一部分比作为独立的程序更为有用,于是它开始针对X而开发。Gettys曾讲述过有关的故事 ,“xterm内部如此恐怖的部分原因,是最初想法把它作为单独进程驱动多个VS100显示窗。”(”part of why xterm’s internals are so horrifying is that it was originally intended that a single process be able to drive multiple VS100 displays.”)</p><p>在作为X参考实现的一个部分后多年,1996年左右,开发的主干转移至了XFree86(从X11R6.3版本派生出来),现在由Thomas Dickey维护。</p><p>有许多xterm变体可用。大多数的X虚拟终端都是从xterm的变体起步的。</p><p>我们目前都基于xterm处理。</p><h3 id="处理终端的输入"><a href="#处理终端的输入" class="headerlink" title="处理终端的输入"></a>处理终端的输入</h3><p>在go中,有许多方式可以获取用户的输入,通过Scan获取输入、bufio标准输入,这里我们说一个常用的方式:bufio,实时读取键盘输入的每个字符。</p><div class="hljs"><pre><code class="hljs go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">read</span><span class="hljs-params">( c <span class="hljs-keyword">chan</span> <span class="hljs-keyword">rune</span>)</span></span>{ buf := bufio.NewReader(os.Stdin) <span class="hljs-keyword">for</span> { r, n, err := buf.ReadRune() <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> { err := fmt.Errorf(<span class="hljs-string">"exit read stdin loop as err:%v"</span>err) log.Debugf(err.Error()) <span class="hljs-keyword">return</span> } log.Debugf(<span class="hljs-string">"receive input:%v, string:%s"</span>, r, <span class="hljs-keyword">string</span>(r)) <span class="hljs-keyword">if</span> n > <span class="hljs-number">0</span> { c <- r } }}</code></pre></div><p>这已经能够满足大部分场景下的需求了,但是没能真正满足终端的需求,在命令过长导致换行的时候,这种做法会导致命令被截断,而不能有效被读取。</p><p>这里提供另外一种,可以更好的控制输入的力度内容:</p><div class="hljs"><pre><code class="hljs go"> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">read</span> <span class="hljs-params">(cb <span class="hljs-keyword">chan</span> <span class="hljs-keyword">byte</span>[])</span></span> { <span class="hljs-keyword">var</span> ttystate *unix.Termios <span class="hljs-comment">//Get terminal settings</span> ttystate, err := termios.Tcgetattr(<span class="hljs-keyword">uintptr</span>(syscall.Stdin)) <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> { log.Debugf(err.Error()) <span class="hljs-keyword">return</span> } setNonCanonicalMode(ttystate) buf := <span class="hljs-built_in">make</span>([]<span class="hljs-keyword">byte</span>, <span class="hljs-number">1024</span>) <span class="hljs-keyword">for</span> { <span class="hljs-keyword">if</span> n, err := syscall.Read(syscall.Stdin, buf); err == <span class="hljs-literal">nil</span> { cb <- buf[:n] } } }<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">setNonCanonicalMode</span><span class="hljs-params">(attr *unix.Termios)</span></span> {<span class="hljs-comment">//Disable canonical mode (canonical mode disabled)&^ AND NOT)</span>attr.Lflag &^= syscall.ICANON<span class="hljs-comment">//Minimum number of characters when reading=One character</span>attr.Cc[syscall.VMIN] = <span class="hljs-number">1</span><span class="hljs-comment">//Timeout time when reading non-canonical= 0</span>attr.Cc[syscall.VTIME] = <span class="hljs-number">0</span><span class="hljs-comment">//Reflect the changed settings</span>termios.Tcsetattr(<span class="hljs-keyword">uintptr</span>(syscall.Stdin), termios.TCSANOW, attr)}</code></pre></div><p>通过实时获取用户输入,就可以实现客户端的逻辑</p><h3 id="处理终端的输出"><a href="#处理终端的输出" class="headerlink" title="处理终端的输出"></a>处理终端的输出</h3><p>终端的输出是相对比较复杂的,与输入不同,终端输出带有大量的<a href="https://zh.wikipedia.org/wiki/%E6%8E%A7%E5%88%B6%E5%AD%97%E7%AC%A6" target="_blank" rel="noopener">控制字符</a></p><p>我目前没有发现一个标准的表格对照,这也困扰了我很久,因为无法解析输出的内容,会导致无法实现命令白名单功能。无法对命令有效的拦截,经常误杀命令。</p><p>最后在github,找到这个类库,基本能够实现解析终端的输出为最终的命令 <strong><a href="https://github.com/LeeEirc/terminalparser" target="_blank" rel="noopener">terminal parse</a></strong></p><p>使用方法也很简单,在用户输入之后,将终端返回的内容缓存起来,当用户敲回车时,将缓存的内容使用这个解析器解析即可得到,这个解析器无法处理命令过长导致换行的问题,所以在处理之前需要将终端的换行符<code>\r</code>处理掉</p><div class="hljs"><pre><code class="hljs go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">parse</span><span class="hljs-params">(p []<span class="hljs-keyword">byte</span>)</span> <span class="hljs-title">string</span></span> {<span class="hljs-keyword">defer</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {<span class="hljs-keyword">if</span> r := <span class="hljs-built_in">recover</span>(); r != <span class="hljs-literal">nil</span> {log.Logger.Warnf(<span class="hljs-string">" panic: %s\n"</span>, r)}}()s := terminalparser.Screen{Rows: <span class="hljs-built_in">make</span>([]*terminalparser.Row, <span class="hljs-number">0</span>, <span class="hljs-number">1024</span>),Cursor: &terminalparser.Cursor{},}ss := s.Parse(p)<span class="hljs-keyword">return</span> strings.Join(ss, <span class="hljs-string">""</span>)}</code></pre></div><p>以上,便是我在处理终端问题时,遇到的一些棘手的事情!感谢阅读!</p>]]></content>
<categories>
<category>program</category>
</categories>
<tags>
<tag>go</tag>
<tag>terminal</tag>
</tags>
</entry>
<entry>
<title>manjaro安装记录</title>
<link href="/post/e8385125.html"/>
<url>/post/e8385125.html</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>目前在工作上使用的windows10 + wsl2 这样的一个配置,但生产上是linux,所以希望有一个一致的linux开发体验,需要一个linux的桌面开发环境。目前做的比较好的是: ubuntu和arch,最终选择了manjaro,一个基于arch的发行版,优点如下:</p><ol><li>开箱即用,驱动都能够自动适配</li><li>大部分办公需求都能满足,wps和qq和wechat</li><li>manjaro kde可以满足大部分定制需求</li></ol><h2 id="准备"><a href="#准备" class="headerlink" title="准备"></a>准备</h2><p>下载好manjaro-kde的iso文件,一个u盘, 启动盘制作程序: <a href="https://link.zhihu.com/?target=https%3A//www.balena.io/etcher/">balenaEtcher</a></p><h2 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h2><ol><li>重启,按f2,进入boot 配置,将security boot 改成false</li><li>重启,按fn+f12,选择u盘启动</li><li>按照需求安装,不再赘述</li></ol><h2 id="配置"><a href="#配置" class="headerlink" title="配置"></a>配置</h2><h3 id="仿mac配置"><a href="#仿mac配置" class="headerlink" title="仿mac配置"></a>仿mac配置</h3><p>macOS的配置还是非常不错的,我这边参考的视频是: <a href="https://www.bilibili.com/video/av927662631/" target="_blank" rel="noopener">让你的 Manjaro KDE Plasma 看起来更像 MacOS Big Sur</a></p><p>该视频也是油管搬运的</p><h3 id="安装软件"><a href="#安装软件" class="headerlink" title="安装软件"></a>安装软件</h3><h4 id="换源"><a href="#换源" class="headerlink" title="换源"></a>换源</h4><p>启动terminal,输入:</p><div class="hljs"><pre><code class="hljs bash">sudo pacman-mirrors -i -c China -m rank</code></pre></div><p>在弹出的框中选一个最快的源,一个就好,选多了会降低速度</p><h4 id="安装yay"><a href="#安装yay" class="headerlink" title="安装yay"></a>安装yay</h4><div class="hljs"><pre><code class="hljs bash">sudo pacman -S yay</code></pre></div><h4 id="安装-wps"><a href="#安装-wps" class="headerlink" title="安装 wps"></a>安装 wps</h4><div class="hljs"><pre><code class="hljs bash">yay -S wps-office-cn wps-office-mui-zh-cn ttf-wps-fonts</code></pre></div><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>优点之前说了,这里总结一下缺点:</p><ol><li>不适合没有linux基础的人玩这个desktop,不过没有需求也不会玩</li><li>细节打磨的不够,很多地方都是形像神不像mac</li><li>wechat和qq都是严格版的,linux桌面软件生态还是任重而道远</li></ol>]]></content>
<categories>
<category>OS</category>
</categories>
<tags>
<tag>OS</tag>
<tag>LINUX</tag>
<tag>manjaro</tag>
<tag>huaweimatebook14</tag>
</tags>
</entry>
<entry>
<title>我对理财的思考</title>
<link href="/post/f59de2d9.html"/>
<url>/post/f59de2d9.html</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>近日,一直在关注股市,也在思考普通人该如何理财。<br>普通人是无法依靠投资、理财而财富自由。</p><p><strong>投资有风险,投资需谨慎。</strong></p><p>风险和收益是伴随的。我这边不会推荐股票、债券和基金</p><h2 id="目的"><a href="#目的" class="headerlink" title="目的"></a>目的</h2><p>对冲风险->跑赢通胀->稳定收益</p><h3 id="固定存款"><a href="#固定存款" class="headerlink" title="固定存款"></a>固定存款</h3><p>对应人生的每一个阶段,我觉得都要固定存款池,来对冲随时的风险。</p><p>1-3年 5W<br>3-5年 8W<br>5-10年 10w<br>10-20年 20-50w</p><p>这个是活期固定存款池,存款池可以借用,但是一定要归还,且是有计划归还。<br>未达到存款池,应有计划的进行储蓄<br>存款可用于货币基金和定期。活期存款不低于固定存款池上限50%</p><h3 id="理财分配"><a href="#理财分配" class="headerlink" title="理财分配"></a>理财分配</h3><p>每月工资进行理财。除却固定存款和生活支出外,每月结余均可用于理财。</p><h4 id="基金"><a href="#基金" class="headerlink" title="基金"></a>基金</h4><p>老牌牛基,主要看基金持仓股票,你认可他的组合即可<br>黄金投资也占 1/3 仓位</p><h4 id="股票"><a href="#股票" class="headerlink" title="股票"></a>股票</h4><p>1/3 银行股,高股息,这股一般不动。持有10年8年<br>1/2 科技、医药、新能源、消费(3年-5年)<br>1/6 短期投机</p><h4 id="债券"><a href="#债券" class="headerlink" title="债券"></a>债券</h4>]]></content>
<categories>
<category>理财</category>
</categories>
<tags>
<tag>投资</tag>
<tag>理财</tag>
</tags>
</entry>
<entry>
<title>使用jib代替docker-maven构建镜像</title>
<link href="/post/d205677f.html"/>
<url>/post/d205677f.html</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>jib是google开源的纯java实现的容器构建类库,其中有jib-core核心包和maven以及gradle插件,jib可以帮助java开发者,快速构建镜像,并且无需编写dockerfile以及依赖docker环境(docker daemon和docker client),这里只介绍jib-maven-plugin如何使用,并且会讲到其中的坑点,至于核心包,我这边就不讲了,虽然笔者也有使用。详细可以到GitHub搜索jib</p><h2 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h2><p>在maven项目中的<code>pom.xml</code>文件中:</p><div class="hljs"><pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">project</span>></span> ... <span class="hljs-tag"><<span class="hljs-name">build</span>></span> <span class="hljs-tag"><<span class="hljs-name">plugins</span>></span> ... <span class="hljs-tag"><<span class="hljs-name">plugin</span>></span> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>com.google.cloud.tools<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>jib-maven-plugin<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span> <span class="hljs-tag"><<span class="hljs-name">version</span>></span>2.6.0<span class="hljs-tag"></<span class="hljs-name">version</span>></span> <span class="hljs-tag"><<span class="hljs-name">configuration</span>></span> <span class="hljs-tag"><<span class="hljs-name">to</span>></span> <span class="hljs-tag"><<span class="hljs-name">image</span>></span>myimage<span class="hljs-tag"></<span class="hljs-name">image</span>></span> <span class="hljs-tag"></<span class="hljs-name">to</span>></span> <span class="hljs-tag"></<span class="hljs-name">configuration</span>></span> <span class="hljs-tag"></<span class="hljs-name">plugin</span>></span> ... <span class="hljs-tag"></<span class="hljs-name">plugins</span>></span> <span class="hljs-tag"></<span class="hljs-name">build</span>></span> ...<span class="hljs-tag"></<span class="hljs-name">project</span>></span></code></pre></div><p>其中插件configuration支持的参数列表如下:</p><table><thead><tr><th>Field</th><th>Type</th><th>Default</th><th>Description</th></tr></thead><tbody><tr><td><code>to</code></td><td><a href="#to-object"><code>to</code></a></td><td><em>Required</em></td><td>Configures the target image to build your application to.</td></tr><tr><td><code>from</code></td><td><a href="#from-object"><code>from</code></a></td><td>See <a href="#from-object"><code>from</code></a></td><td>Configures the base image to build your application on top of.</td></tr><tr><td><code>container</code></td><td><a href="#container-object"><code>container</code></a></td><td>See <a href="#container-object"><code>container</code></a></td><td>Configures the container that is run from your image.</td></tr><tr><td><code>extraDirectories</code></td><td><a href="#extradirectories-object"><code>extraDirectories</code></a></td><td>See <a href="#extradirectories-object"><code>extraDirectories</code></a></td><td>Configures the directories used to add arbitrary files to the image.</td></tr><tr><td><code>outputPaths</code></td><td><a href="#outputpaths-object"><code>outputPaths</code></a></td><td>See <a href="#outputpaths-object"><code>outputPaths</code></a></td><td>Configures the locations of additional build artifacts generated by Jib.</td></tr><tr><td><code>dockerClient</code></td><td><a href="#dockerclient-object"><code>dockerClient</code></a></td><td>See <a href="#dockerclient-object"><code>dockerClient</code></a></td><td>Configures Docker for building to/from the Docker daemon.</td></tr><tr><td><code>skaffold</code></td><td><a href="#skaffold-integration"><code>skaffold</code></a></td><td>See <a href="#skaffold-integration"><code>skaffold</code></a></td><td>Configures the internal skaffold goals. This configuration should only be used when integrating with <a href="#skaffold-integration"><code>skaffold</code></a>.</td><td></td></tr><tr><td><code>containerizingMode</code></td><td>string</td><td><code>exploded</code></td><td>If set to <code>packaged</code>, puts the JAR artifact built at <code>${project.build.directory}/${project.build.finalName}.jar</code> (the default location where many JAR-buidiling plugins put a JAR registered as a main artifact, such as the Maven JAR Plugin) into the final image. If set to <code>exploded</code> (default), containerizes individual <code>.class</code> files and resources files.</td></tr><tr><td><code>allowInsecureRegistries</code></td><td>boolean</td><td><code>false</code></td><td>If set to true, Jib ignores HTTPS certificate errors and may fall back to HTTP as a last resort. Leaving this parameter set to <code>false</code> is strongly recommended, since HTTP communication is unencrypted and visible to others on the network, and insecure HTTPS is no better than plain HTTP. <a href="https://github.com/GoogleContainerTools/jib/tree/master/docs/self_sign_cert.md" target="_blank" rel="noopener">If accessing a registry with a self-signed certificate, adding the certificate to your Java runtime’s trusted keys</a> may be an alternative to enabling this option.</td></tr><tr><td><code>skip</code></td><td>boolean</td><td><code>false</code></td><td>If set to true, Jib execution is skipped (useful for multi-module projects). This can also be specified via the <code>-Djib.skip</code> command line option.</td></tr></tbody></table><p><code>from</code> 参数</p><table><thead><tr><th>Property</th><th>Type</th><th>Default</th><th>Description</th></tr></thead><tbody><tr><td><code>image</code></td><td>string</td><td><code>gcr.io/distroless/java</code></td><td>The image reference for the base image. The source type can be specified using a <a href="#setting-the-base-image">special type prefix</a>.</td></tr><tr><td><code>auth</code></td><td><a href="#auth-object"><code>auth</code></a></td><td><em>None</em></td><td>Specifies credentials directly (alternative to <code>credHelper</code>).</td></tr><tr><td><code>credHelper</code></td><td>string</td><td><em>None</em></td><td>Specifies a credential helper that can authenticate pulling the base image. This parameter can either be configured as an absolute path to the credential helper executable or as a credential helper suffix (following <code>docker-credential-</code>).</td></tr><tr><td><code>platforms</code></td><td>list</td><td>See <a href="#platform-object"><code>platform</code></a></td><td><em>Incubating feature</em>: Configures platforms of base images to select from a manifest list.</td></tr></tbody></table><p><code>to</code>标签配置</p><table><thead><tr><th>Property</th><th>Type</th><th>Default</th><th>Description</th></tr></thead><tbody><tr><td><code>image</code></td><td>string</td><td><em>Required</em></td><td>The image reference for the target image. This can also be specified via the <code>-Dimage</code> command line option.</td></tr><tr><td><code>auth</code></td><td><a href="#auth-object"><code>auth</code></a></td><td><em>None</em></td><td>Specifies credentials directly (alternative to <code>credHelper</code>).</td></tr><tr><td><code>credHelper</code></td><td>string</td><td><em>None</em></td><td>Specifies a credential helper that can authenticate pushing the target image. This parameter can either be configured as an absolute path to the credential helper executable or as a credential helper suffix (following <code>docker-credential-</code>).</td></tr><tr><td><code>tags</code></td><td>list</td><td><em>None</em></td><td>Additional tags to push to.</td></tr></tbody></table><p><code>auth</code>标签配置</p><table><thead><tr><th>Property</th><th>Type</th></tr></thead><tbody><tr><td><code>username</code></td><td>string</td></tr><tr><td><code>password</code></td><td>string</td></tr></tbody></table><p><code>platform</code> 标签配置</p><table><thead><tr><th>Property</th><th>Type</th><th>Default</th><th>Description</th></tr></thead><tbody><tr><td><code>architecture</code></td><td>string</td><td><code>amd64</code></td><td>The architecture of a base image to select from a manifest list.</td></tr><tr><td><code>os</code></td><td>string</td><td><code>linux</code></td><td>The OS of a base image to select from a manifest list.</td></tr></tbody></table><p><code>container</code>标签配置</p><table><thead><tr><th>Property</th><th>Type</th><th>Default</th><th>Description</th></tr></thead><tbody><tr><td><code>appRoot</code></td><td>string</td><td><code>/app</code></td><td>The root directory on the container where the app’s contents are placed. Particularly useful for WAR-packaging projects to work with different Servlet engine base images by designating where to put exploded WAR contents; see <a href="#war-projects">WAR usage</a> as an example.</td></tr><tr><td><code>args</code></td><td>list</td><td><em>None</em></td><td>Additional program arguments appended to the command to start the container (similar to Docker’s <a href="https://docs.docker.com/engine/reference/builder/#cmd" target="_blank" rel="noopener">CMD</a> instruction in relation with <a href="https://docs.docker.com/engine/reference/builder/#entrypoint" target="_blank" rel="noopener">ENTRYPOINT</a>). In the default case where you do not set a custom <code>entrypoint</code>, this parameter is effectively the arguments to the main method of your Java application.</td></tr><tr><td><code>creationTime</code></td><td>string</td><td><code>EPOCH</code></td><td>Sets the container creation time. (Note that this property does not affect the file modification times, which are configured using <code><filesModificationTime></code>.) The value can be <code>EPOCH</code> to set the timestamps to Epoch (default behavior), <code>USE_CURRENT_TIMESTAMP</code> to forgo reproducibility and use the real creation time, or an ISO 8601 date-time parsable with <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/time/format/DateTimeFormatter.html#ISO_DATE_TIME" target="_blank" rel="noopener"><code>DateTimeFormatter.ISO_DATE_TIME</code></a> such as <code>2019-07-15T10:15:30+09:00</code> or <code>2011-12-03T22:42:05Z</code>.</td></tr><tr><td><code>entrypoint</code></td><td>list</td><td><em>None</em></td><td>The command to start the container with (similar to Docker’s <a href="https://docs.docker.com/engine/reference/builder/#entrypoint" target="_blank" rel="noopener">ENTRYPOINT</a> instruction). If set, then <code>jvmFlags</code> and <code>mainClass</code> are ignored. You may also set <code><entrypoint>INHERIT</entrypoint></code> (<code><entrypoint><entry>INHERIT</entry></entrypoint></code> in old Maven versions) to indicate that the <code>entrypoint</code> and <code>args</code> should be inherited from the base image.*</td></tr><tr><td><code>environment</code></td><td>map</td><td><em>None</em></td><td>Key-value pairs for setting environment variables on the container (similar to Docker’s <a href="https://docs.docker.com/engine/reference/builder/#env" target="_blank" rel="noopener">ENV</a> instruction).</td></tr><tr><td><code>extraClasspath</code></td><td>list</td><td><em>None</em></td><td>Additional paths in the container to prepend to the computed Java classpath.</td></tr><tr><td><code>filesModificationTime</code></td><td>string</td><td><code>EPOCH_PLUS_SECOND</code></td><td>Sets the modification time (last modified time) of files in the image put by Jib. (Note that this does not set the image creation time, which can be set using <code><creationTime></code>.) The value should either be <code>EPOCH_PLUS_SECOND</code> to set the timestamps to Epoch + 1 second (default behavior), or an ISO 8601 date-time parsable with <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/time/format/DateTimeFormatter.html#ISO_DATE_TIME" target="_blank" rel="noopener"><code>DateTimeFormatter.ISO_DATE_TIME</code></a> such as <code>2019-07-15T10:15:30+09:00</code> or <code>2011-12-03T22:42:05Z</code>.</td></tr><tr><td><code>format</code></td><td>string</td><td><code>Docker</code></td><td>Use <code>OCI</code> to build an <a href="https://www.opencontainers.org/" target="_blank" rel="noopener">OCI container image</a>.</td></tr><tr><td><code>jvmFlags</code></td><td>list</td><td><em>None</em></td><td>Additional flags to pass into the JVM when running your application.</td></tr><tr><td><code>labels</code></td><td>map</td><td><em>None</em></td><td>Key-value pairs for applying image metadata (similar to Docker’s <a href="https://docs.docker.com/engine/reference/builder/#label" target="_blank" rel="noopener">LABEL</a> instruction).</td></tr><tr><td><code>mainClass</code></td><td>string</td><td><em>Inferred</em>**</td><td>The main class to launch the application from.</td></tr><tr><td><code>ports</code></td><td>list</td><td><em>None</em></td><td>Ports that the container exposes at runtime (similar to Docker’s <a href="https://docs.docker.com/engine/reference/builder/#expose" target="_blank" rel="noopener">EXPOSE</a> instruction).</td></tr><tr><td><code>user</code></td><td>string</td><td><em>None</em></td><td>The user and group to run the container as. The value can be a username or UID along with an optional groupname or GID. The following are all valid: <code>user</code>, <code>uid</code>, <code>user:group</code>, <code>uid:gid</code>, <code>uid:group</code>, <code>user:gid</code>.</td></tr><tr><td><code>volumes</code></td><td>list</td><td><em>None</em></td><td>Specifies a list of mount points on the container.</td></tr><tr><td><code>workingDirectory</code></td><td>string</td><td><em>None</em></td><td>The working directory in the container</td></tr></tbody></table><p><code>extraDirectories</code> 标签配置</p><table><thead><tr><th>Property</th><th>Type</th><th>Default</th><th>Description</th></tr></thead><tbody><tr><td><code>paths</code></td><td>list</td><td><code>[(project-dir)/src/main/jib]</code></td><td>List of <a href="#path-object"><code>path</code></a> objects and/or extra directory paths. Can be absolute or relative to the project root.</td></tr><tr><td><code>permissions</code></td><td>list</td><td><em>None</em></td><td>Maps file paths (glob patterns) on container to Unix permissions. (Effective only for files added from extra directories.) If not configured, permissions default to “755” for directories and “644” for files. See <a href="#adding-arbitrary-files-to-the-image">Adding Arbitrary Files to the Image</a> for an example.</td></tr></tbody></table><h4 id="系统构建参数"><a href="#系统构建参数" class="headerlink" title="系统构建参数"></a>系统构建参数</h4><p>(i.e. <code>-Djib.parameterName[.nestedParameter.[...]]=value</code>). Some examples are below:<br><div class="hljs"><pre><code class="hljs shell">mvn compile jib:build \ -Djib.to.image=myregistry/myimage:latest \ -Djib.to.auth.username=$USERNAME \ -Djib.to.auth.password=$PASSWORDmvn compile jib:dockerBuild \ -Djib.dockerClient.executable=/path/to/docker \ -Djib.container.environment=key1="value1",key2="value2" \ -Djib.container.args=arg1,arg2,arg3</code></pre></div></p><h2 id="maven完整使用案例"><a href="#maven完整使用案例" class="headerlink" title="maven完整使用案例"></a>maven完整使用案例</h2><p>目录结构<br>.<br>├── hello-world<br>├── jib-lib<br>├── name<br>└── pom.xml</p><p>这是一个多模块工程,其中<code>jib-lib</code>为基础依赖模块,hello-world和name都是web模块</p><p>父<code>pom.xml</code>配置如下:</p><div class="hljs"><pre><code class="hljs xml"> <span class="hljs-tag"><<span class="hljs-name">properties</span>></span> <span class="hljs-tag"><<span class="hljs-name">maven.build.timestamp.format</span>></span>yyyyMMdd-HHmmssSSS<span class="hljs-tag"></<span class="hljs-name">maven.build.timestamp.format</span>></span><span class="hljs-tag"></<span class="hljs-name">properties</span>></span><span class="hljs-tag"><<span class="hljs-name">build</span>></span> <span class="hljs-comment"><!-- Defines plugins that are used in the modules. --></span> <span class="hljs-tag"><<span class="hljs-name">pluginManagement</span>></span> <span class="hljs-tag"><<span class="hljs-name">plugins</span>></span> <span class="hljs-tag"><<span class="hljs-name">plugin</span>></span> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>com.google.cloud.tools<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>jib-maven-plugin<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span> <span class="hljs-tag"><<span class="hljs-name">version</span>></span>2.6.0<span class="hljs-tag"></<span class="hljs-name">version</span>></span> <span class="hljs-tag"></<span class="hljs-name">plugin</span>></span> <span class="hljs-tag"></<span class="hljs-name">plugins</span>></span> <span class="hljs-tag"></<span class="hljs-name">pluginManagement</span>></span> <span class="hljs-tag"></<span class="hljs-name">build</span>></span></code></pre></div><p>hello-world</p><div class="hljs"><pre><code class="hljs xml"> <span class="hljs-tag"><<span class="hljs-name">plugin</span>></span> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>com.google.cloud.tools<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>jib-maven-plugin<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span> <span class="hljs-tag"><<span class="hljs-name">configuration</span>></span> <span class="hljs-tag"><<span class="hljs-name">allowInsecureRegistries</span>></span>true<span class="hljs-tag"></<span class="hljs-name">allowInsecureRegistries</span>></span> <span class="hljs-tag"><<span class="hljs-name">from</span>></span> <span class="hljs-tag"><<span class="hljs-name">image</span>></span>localhost:5000/jre:11<span class="hljs-tag"></<span class="hljs-name">image</span>></span> <span class="hljs-tag"><<span class="hljs-name">auth</span>></span> <span class="hljs-tag"><<span class="hljs-name">username</span>></span>${env.DOCKERUSER}<span class="hljs-tag"></<span class="hljs-name">username</span>></span> <span class="hljs-tag"><<span class="hljs-name">password</span>></span>${env.DOCKERPW}<span class="hljs-tag"></<span class="hljs-name">password</span>></span> <span class="hljs-tag"></<span class="hljs-name">auth</span>></span> <span class="hljs-tag"></<span class="hljs-name">from</span>></span> <span class="hljs-tag"><<span class="hljs-name">to</span>></span> <span class="hljs-comment"><!-- make sure you already have created a project at Google Cloud Platform, see https://cloud.google.com/container-registry/ --></span> <span class="hljs-tag"><<span class="hljs-name">image</span>></span>localhost:5000/${project.artifactId}:${project.version}-${maven.build.timestamp}<span class="hljs-tag"></<span class="hljs-name">image</span>></span> <span class="hljs-tag"></<span class="hljs-name">to</span>></span> <span class="hljs-tag"><<span class="hljs-name">container</span>></span> <span class="hljs-comment"><!-- 使用当前时间构建,否则50年前的时间,可以查看:https://github.com/GoogleContainerTools/jib/blob/master/docs/faq.md#why-is-my-image-created-48-years-ago --></span> <span class="hljs-tag"><<span class="hljs-name">creationTime</span>></span>USE_CURRENT_TIMESTAMP<span class="hljs-tag"></<span class="hljs-name">creationTime</span>></span> <span class="hljs-tag"><<span class="hljs-name">jvmFlags</span>></span> <span class="hljs-tag"><<span class="hljs-name">jvmFlag</span>></span>-Xms256m<span class="hljs-tag"></<span class="hljs-name">jvmFlag</span>></span> <span class="hljs-tag"><<span class="hljs-name">jvmFlag</span>></span>-Xmx512m<span class="hljs-tag"></<span class="hljs-name">jvmFlag</span>></span> <span class="hljs-tag"></<span class="hljs-name">jvmFlags</span>></span> <span class="hljs-tag"><<span class="hljs-name">mainClass</span>></span>com.example.helloworld.HelloWorldApplication<span class="hljs-tag"></<span class="hljs-name">mainClass</span>></span> <span class="hljs-tag"><<span class="hljs-name">ports</span>></span> <span class="hljs-tag"><<span class="hljs-name">port</span>></span>8080<span class="hljs-tag"></<span class="hljs-name">port</span>></span> <span class="hljs-comment"><!-- <port>4000-4004/udp</port> --></span> <span class="hljs-tag"></<span class="hljs-name">ports</span>></span> <span class="hljs-tag"><<span class="hljs-name">format</span>></span>OCI<span class="hljs-tag"></<span class="hljs-name">format</span>></span> <span class="hljs-comment"><!-- OR <format>Docker</format> --></span> <span class="hljs-tag"></<span class="hljs-name">container</span>></span> <span class="hljs-tag"></<span class="hljs-name">configuration</span>></span><span class="hljs-tag"></<span class="hljs-name">plugin</span>></span></code></pre></div><p>依赖库<code>pom.xml</code>配置:</p><div class="hljs"><pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">plugin</span>></span> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>com.google.cloud.tools<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>jib-maven-plugin<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span> <span class="hljs-tag"><<span class="hljs-name">configuration</span>></span> <span class="hljs-comment"><!-- we don't want jib to execute on this module --></span> <span class="hljs-tag"><<span class="hljs-name">skip</span>></span>true<span class="hljs-tag"></<span class="hljs-name">skip</span>></span> <span class="hljs-tag"></<span class="hljs-name">configuration</span>></span><span class="hljs-tag"></<span class="hljs-name">plugin</span>></span></code></pre></div><p>构建命令:</p><div class="hljs"><pre><code class="hljs bash"><span class="hljs-comment">#构建全部</span>mvn clean package jib:build -Dmaven.test.skip=<span class="hljs-literal">true</span> -DsendCredentialsOverHttp=<span class="hljs-literal">true</span><span class="hljs-comment">#构建某个模块</span>mvn clean package jib:build -pl hello-world -am -Dmaven.test.skip=<span class="hljs-literal">true</span> -DsendCredentialsOverHttp=<span class="hljs-literal">true</span></code></pre></div><p>这里重点说一下<code>-DsendCredentialsOverHttp=true</code> 这个参数将允许你使用http发送账号密码,google都喜欢强迫用户使用https.</p><p>PS: <code>jvmFlag</code>用来配置jvm参数,<code>mainClass</code> 配置运行类 ,构建镜像最后文件否会放到 <code>/app</code> 目录下,该目录下拥有三个目录分别是: <code>classes</code> <code>libs</code> <code>resources</code> 源码字节码,依赖包和配置文件</p><h2 id="参考文献"><a href="#参考文献" class="headerlink" title="参考文献"></a>参考文献</h2><p><a href="https://github.com/GoogleContainerTools/jib" target="_blank" rel="noopener">jib源码</a></p><p><a href="https://github.com/GoogleContainerTools/jib/blob/master/docs/faq.md" target="_blank" rel="noopener">jib FAQ</a></p>]]></content>
<categories>
<category>java</category>
</categories>
<tags>
<tag>image</tag>
<tag>容器</tag>
<tag>jib</tag>
</tags>
</entry>
<entry>
<title>使用Java操作k8s和registry</title>
<link href="/post/153c5116.html"/>
<url>/post/153c5116.html</url>
<content type="html"><![CDATA[<h2 id="前言-👻"><a href="#前言-👻" class="headerlink" title="前言 👻"></a>前言 👻</h2><p>这段时间一直在使用java和k8s开发项目,自然就无法避免需要java客户端去操作k8s和docker registry。这里简单介绍下,基于上一篇文章<a href="/post/acd7e382.html">使用operator-sdk自定义k8s operator</a>生成的crd文件,java如何使用客户端去操作crd对象。</p><h2 id="java-client-🤔"><a href="#java-client-🤔" class="headerlink" title="java-client 🤔"></a>java-client 🤔</h2><p>由kubernetes官方维护的java客户端项目: <a href="https://github.com/kubernetes-client/java" target="_blank" rel="noopener">java-client</a>,虽然不是很完美,但是基本可以使用了,支持deployment,service,statefulSet等,甚至你可以使用它来开发java 版的operator,如果你想的话。</p><p>该项目不同版本的兼容性不同,截止到本文书写时,该项目兼容性列表如下:</p><table><thead><tr><th>client version</th><th>1.13</th><th>1.14</th><th>1.15</th><th>1.16</th><th>1.17</th><th>1.18</th></tr></thead><tbody><tr><td>5.0.0</td><td>✓</td><td>-</td><td>-</td><td>x</td><td>x</td><td>x</td></tr><tr><td>6.0.1</td><td>+</td><td>✓</td><td>-</td><td>-</td><td>x</td><td>x</td></tr><tr><td>7.0.0</td><td>+</td><td>+</td><td>✓</td><td>-</td><td>-</td><td>x</td></tr><tr><td>8.0.2</td><td>+</td><td>+</td><td>+</td><td>✓</td><td>-</td><td>-</td></tr><tr><td>9.0.2</td><td>+</td><td>+</td><td>+</td><td>+</td><td>✓</td><td>-</td></tr><tr><td>10.0.0</td><td>+</td><td>+</td><td>+</td><td>+</td><td>+</td><td>✓</td></tr></tbody></table><p>java-client 支持基于crd自动生成java crd对象代码,详情请看:<a href="https://github.com/kubernetes-client/java/blob/master/docs/generate-model-from-third-party-resources.md" target="_blank" rel="noopener">自动生成java代码</a></p><p>java-client 支持像命令行一样使用kubectl(大雾🤣,我开发的时候还没发布)。支持 <code>create</code> <code>apply</code> <code>delete</code> 等等<br>详情请看: <a href="https://github.com/kubernetes-client/java/blob/master/docs/kubectl-equivalence-in-java.md" target="_blank" rel="noopener">Kubectl Equivalence in Java</a></p><p>java-client 支持泛型,可以避免unchecked警告,简直就是强迫症福音。</p><p>更多代码demo可以参考:<a href="https://github.com/kubernetes-client/java/blob/master/examples" target="_blank" rel="noopener">kubernetes-java-client-example</a></p>]]></content>
<categories>
<category>cloud</category>
</categories>
<tags>
<tag>java</tag>
<tag>docker</tag>
<tag>kubernetes</tag>
<tag>registry</tag>
</tags>
</entry>
<entry>
<title>使用operator-sdk自定义k8s operator</title>
<link href="/post/acd7e382.html"/>
<url>/post/acd7e382.html</url>
<content type="html"><![CDATA[<h2 id="起因"><a href="#起因" class="headerlink" title="起因"></a>起因</h2><p>看到 k8s 生态中越来越多的 operator,是不是也想自己写一个?</p><p>在使用 k8s 部署业务应用时,是不是每一次都要 编写 Deployment,Service,Ingress?能不能只写一个文件就把这三种资源都部署起来呢?</p><p>大家可能想到使用 helm,helm 是个不错的东西。但我们不使用 helm,而是使用go编写类似于 k8s 内置资源的控制器。采取 crd 与 controller 来控制部署自定义资源。如果从头开始写控制器,则非常繁琐复杂。 coreOS 团队开源了一个很好的脚手架工具 operator-sdk,我们只要按照要求编写 crd 结构体和一致性逻辑即可。极大地降低了编写 controller 的难度。</p><h2 id="operator-sdk-介绍"><a href="#operator-sdk-介绍" class="headerlink" title="operator-sdk 介绍"></a>operator-sdk 介绍</h2><p>在了解 operator-sdk 之前,先了解 crd 和 operator</p><h3 id="CustomResourceDefinition"><a href="#CustomResourceDefinition" class="headerlink" title="CustomResourceDefinition"></a>CustomResourceDefinition</h3><p>在 Kubernetes 中一切都可视为资源,Kubernetes 1.7 之后增加了对 CRD 自定义资源二次开发能力来扩展 Kubernetes API,通过 CRD 我们可以向 Kubernetes API 中增加新资源类型,而不需要修改 Kubernetes 源码来创建自定义的 API server,该功能大大提高了 Kubernetes 的扩展能力。<br>当你创建一个新的 CustomResourceDefinition (CRD)时,Kubernetes API 服务器将为你指定的每个版本创建一个新的 RESTful 资源路径,我们可以根据该 api 路径来创建一些我们自己定义的类型资源。CRD 可以是命名空间的,也可以是集群范围的,由 CRD 的作用域(scpoe)字段中所指定的,与现有的内置对象一样,删除名称空间将删除该名称空间中的所有自定义对象。customresourcedefinition 本身没有名称空间,所有名称空间都可以使用。</p><h3 id="Operator"><a href="#Operator" class="headerlink" title="Operator"></a>Operator</h3><p>Operator 模式旨在捕获(正在管理一个或一组服务的)运维人员的关键目标。 负责特定应用和 service 的运维人员,在系统应该如何运行、如何部署以及出现问题时如何处理等方面有深入的了解。在 Kubernetes 上运行工作负载的人们都喜欢通过自动化来处理重复的任务。Operator 模式会封装您编写的(Kubernetes 本身提供功能以外的)任务自动化代码。Kubernetes 控制器 使您无需修改 Kubernetes 自身的代码,即可以扩展集群的行为。 Operator 是 Kubernetes API 的客户端,充当 自定义资源的控制器。</p><p>operator 可以由任何能够实现 http 的语言实现,所以是语言无关的,但是使用 golang 则更加接近 k8s 原生。</p><h3 id="operator-sdk"><a href="#operator-sdk" class="headerlink" title="operator-sdk"></a>operator-sdk</h3><p>什么是 Opearator SDK,为什么要使用它?</p><p>该项目是 Operator Framework 的组成部分,Operator Framework 是一个开放源代码工具包,用于以有效,自动化和可扩展的方式管理称为 Kubernetes 的本机应用程序。在简介博客文章中了解更多信息。</p><p>Operator 可以轻松地在 Kubernetes 上管理复杂的有状态应用程序。但是,由于诸如使用低级 API,编写样板以及缺少导致重复的模块化等挑战,今天编写 Operator 可能会很困难。</p><p>Operator SDK 是一个框架,该框架使用控制器运行时库通过提供以下功能使编写操作员更加容易:</p><ul><li>高级 API 和抽象,可以更直观地编写操作逻辑</li><li>脚手架和代码生成工具,可快速引导新项目</li><li>扩展以涵盖常见的 Operator 用例</li></ul><h2 id="阅读前提"><a href="#阅读前提" class="headerlink" title="阅读前提"></a>阅读前提</h2><ol><li>熟悉 k8s 相关概念,编写 k8s 各类 yaml 文件</li><li>本地已有一个 k8s 集群</li><li>git</li><li>mercurial version 3.9+</li><li>bazaar version 2.7.0+</li><li>go version v1.13+.</li></ol><h2 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h2><div class="hljs"><pre><code class="hljs bash">git <span class="hljs-built_in">clone</span> https://github.com/operator-framework/operator-sdk<span class="hljs-built_in">cd</span> operator-sdkgit checkout mastermake tidymake installcp <span class="hljs-variable">$GOPATH</span>/bin/operator-sdk /usr/<span class="hljs-built_in">local</span>/bin/</code></pre></div><h2 id="使用"><a href="#使用" class="headerlink" title="使用"></a>使用</h2><h3 id="初始化-project"><a href="#初始化-project" class="headerlink" title="初始化 project"></a>初始化 project</h3><div class="hljs"><pre><code class="hljs sh"><span class="hljs-built_in">cd</span> <span class="hljs-variable">$GOPATH</span>/srcoperator-sdk init --domain=bryce-huang.club<span class="hljs-comment"># 表示初始化一个operator --domain 表示api的域名, --repo表示拉取代码</span><span class="hljs-built_in">cd</span> app-operator</code></pre></div><h3 id="创建-api-生成自定义文件接口"><a href="#创建-api-生成自定义文件接口" class="headerlink" title="创建 api,生成自定义文件接口"></a>创建 api,生成自定义文件接口</h3><div class="hljs"><pre><code class="hljs sh">operator-sdk create api --group k8s --version v1 --kind App --resource=<span class="hljs-literal">true</span> --controller=<span class="hljs-literal">true</span><span class="hljs-comment">#创建一个简单的api</span></code></pre></div><h3 id="编写结构体"><a href="#编写结构体" class="headerlink" title="编写结构体"></a>编写结构体</h3><p>在 api/v1/app_types.go 中主要编写 AppSpec 和 AppStatus 两个结构体,对应 k8s 资源中的 spec 和 status</p><p>具体代码如下:</p><div class="hljs"><pre><code class="hljs go"><span class="hljs-comment">// AppSpec defines the desired state of App</span><span class="hljs-keyword">type</span> AppSpec <span class="hljs-keyword">struct</span> {<span class="hljs-comment">//容器及服务的端口</span>Port <span class="hljs-keyword">int32</span> <span class="hljs-string">`json:"svcPort"`</span><span class="hljs-comment">//镜像名称</span>Image <span class="hljs-keyword">string</span> <span class="hljs-string">`json:"image"`</span><span class="hljs-comment">// 副本数量</span>Replicas *<span class="hljs-keyword">int32</span> <span class="hljs-string">`json:"replicas"`</span><span class="hljs-comment">//ingress 域名</span>Host <span class="hljs-keyword">string</span> <span class="hljs-string">`json:"host"`</span><span class="hljs-comment">// ingress 访问上下文,如:/xxx</span>Context <span class="hljs-keyword">string</span> <span class="hljs-string">`json:"context"`</span>}<span class="hljs-keyword">type</span> AppStatus <span class="hljs-keyword">struct</span> { <span class="hljs-comment">// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster</span> <span class="hljs-comment">// Important: Run "make" to regenerate code after modifying this file</span> DeployStatus appsv1.DeploymentStatus <span class="hljs-string">`json:"deployStatus,omitempty"`</span> SvcStatus corev1.ServiceStatus <span class="hljs-string">`json:"svcStatus,omitempty"`</span> IngressStatus extv1.IngressStatus <span class="hljs-string">`json:"ingressStatus,omitempty"`</span> Pods []Pod <span class="hljs-string">`json:"pods"`</span>}<span class="hljs-comment">// +kubebuilder:object:root=true</span><span class="hljs-comment">// +kubebuilder:subresource:status</span><span class="hljs-comment">// App is the Schema for the apps API</span><span class="hljs-keyword">type</span> App <span class="hljs-keyword">struct</span> { metav1.TypeMeta <span class="hljs-string">`json:",inline"`</span> metav1.ObjectMeta <span class="hljs-string">`json:"metadata,omitempty"`</span> Spec AppSpec <span class="hljs-string">`json:"spec,omitempty"`</span> Status AppStatus <span class="hljs-string">`json:"status,omitempty"`</span>}</code></pre></div><h3 id="编写循环逻辑"><a href="#编写循环逻辑" class="headerlink" title="编写循环逻辑"></a>编写循环逻辑</h3><p>定义好数据结构,就可以根据数据结构来编写逻辑了<br>在 controllers/app_controller.go 中可以看到 Reconcile 和 SetupWithManager 两个方法,前者是定义资源一致性逻辑而后者则是定义需要监听哪些资源,当监听的资源发生变化时,则调用 Reconcile,执行逻辑。</p><div class="hljs"><pre><code class="hljs go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(r *AppReconciler)</span> <span class="hljs-title">Reconcile</span><span class="hljs-params">(req ctrl.Request)</span> <span class="hljs-params">(ctrl.Result, error)</span></span> { <span class="hljs-comment">// write your logic</span>ctx := context.Background()log := r.Log.WithValues(<span class="hljs-string">"app"</span>, req.NamespacedName)<span class="hljs-comment">//</span>app := &k8sv1.App{}err := r.Client.Get(ctx, req.NamespacedName, app)<span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> { <span class="hljs-keyword">if</span> errors.IsNotFound(err) { log.Info(<span class="hljs-string">"App resource not found. Ignoring since object must be deleted"</span>) <span class="hljs-keyword">return</span> ctrl.Result{}, <span class="hljs-literal">nil</span> } log.Error(err, <span class="hljs-string">"Failed to get App"</span>) <span class="hljs-keyword">return</span> ctrl.Result{}, err}<span class="hljs-comment">// find deployment</span>fd := &appsv1.Deployment{}err = r.Get(ctx, types.NamespacedName{Name: app.Name, Namespace: app.Namespace}, fd)<span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> && errors.IsNotFound(err) { fd = r.deployment(app) log.Info(<span class="hljs-string">"Creating a new Deployment"</span>, <span class="hljs-string">"Deployment.Namespace"</span>, fd.Namespace, <span class="hljs-string">"Deployment.Name"</span>, fd.Name) err = r.Create(ctx, fd) <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> { log.Error(err, <span class="hljs-string">"Failed to create new Deployment"</span>, <span class="hljs-string">"Deployment.Namespace"</span>, fd.Namespace, <span class="hljs-string">"Deployment.Name"</span>, fd.Name) <span class="hljs-keyword">return</span> ctrl.Result{}, err } <span class="hljs-keyword">return</span> ctrl.Result{Requeue: <span class="hljs-literal">true</span>}, <span class="hljs-literal">nil</span>} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> { log.Error(err, <span class="hljs-string">"Failed to get Deployment"</span>) <span class="hljs-keyword">return</span> ctrl.Result{}, err}<span class="hljs-comment">// check images</span>image := app.Spec.ImagefImage := fd.Spec.Template.Spec.Containers[<span class="hljs-number">0</span>].Image<span class="hljs-keyword">if</span> fImage != image { fd.Spec.Template.Spec.Containers[<span class="hljs-number">0</span>].Image = image err = r.Update(ctx, fd) <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> { log.Error(err, <span class="hljs-string">"Failed to update Deployment image"</span>, <span class="hljs-string">"Deployment.Namespace"</span>, fd.Namespace, <span class="hljs-string">"Deployment.Name"</span>, fd.Name) <span class="hljs-keyword">return</span> ctrl.Result{}, err } <span class="hljs-comment">// Spec updated - return and requeue</span> <span class="hljs-keyword">return</span> ctrl.Result{Requeue: <span class="hljs-literal">true</span>}, <span class="hljs-literal">nil</span>}<span class="hljs-comment">// check replicas</span><span class="hljs-keyword">if</span> *fd.Spec.Replicas != *app.Spec.Replicas { fd.Spec.Replicas = app.Spec.Replicas err = r.Update(ctx, fd) <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> { log.Error(err, <span class="hljs-string">"Failed to update Deployment"</span>, <span class="hljs-string">"Deployment.Namespace"</span>, fd.Namespace, <span class="hljs-string">"Deployment.Name"</span>, fd.Name) <span class="hljs-keyword">return</span> ctrl.Result{}, err } <span class="hljs-comment">// Spec updated - return and requeue</span> <span class="hljs-keyword">return</span> ctrl.Result{Requeue: <span class="hljs-literal">true</span>}, <span class="hljs-literal">nil</span>}<span class="hljs-comment">// find service</span>fs := &corev1.Service{}err = r.Get(ctx, types.NamespacedName{Name: app.Name, Namespace: app.Namespace}, fs)<span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> && errors.IsNotFound(err) { fs = r.service(app) log.Info(<span class="hljs-string">"Creating a new Service"</span>, <span class="hljs-string">"Service.Namespace"</span>, fs.Namespace, <span class="hljs-string">"Service.Name"</span>, fs.Name) err = r.Create(ctx, fs) <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> { log.Error(err, <span class="hljs-string">"Failed to create new Service"</span>, <span class="hljs-string">"Service.Namespace"</span>, fs.Namespace, <span class="hljs-string">"Service.Name"</span>, fs.Name) <span class="hljs-keyword">return</span> ctrl.Result{}, err } <span class="hljs-keyword">return</span> ctrl.Result{Requeue: <span class="hljs-literal">true</span>}, <span class="hljs-literal">nil</span>} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> { log.Error(err, <span class="hljs-string">"Failed to get Service"</span>) <span class="hljs-keyword">return</span> ctrl.Result{}, err} <span class="hljs-comment">// find ingress</span>fi := &extv1.Ingress{}err = r.Get(ctx, types.NamespacedName{Name: app.Name, Namespace: app.Namespace}, fi)<span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> && errors.IsNotFound(err) { fi = r.ingress(app) log.Info(<span class="hljs-string">"Creating a new Ingress"</span>, <span class="hljs-string">"Ingress.Namespace"</span>, fi.Namespace, <span class="hljs-string">"Ingress.Name"</span>, fi.Name) err = r.Create(ctx, fi) <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> { log.Error(err, <span class="hljs-string">"Failed to create new Ingress"</span>, <span class="hljs-string">"Ingress.Namespace"</span>, fi.Namespace, <span class="hljs-string">"Ingress.Name"</span>, fi.Name) <span class="hljs-keyword">return</span> ctrl.Result{}, err } <span class="hljs-keyword">return</span> ctrl.Result{Requeue: <span class="hljs-literal">true</span>}, <span class="hljs-literal">nil</span>} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> { log.Error(err, <span class="hljs-string">"Failed to get Ingress"</span>) <span class="hljs-keyword">return</span> ctrl.Result{}, err} <span class="hljs-keyword">return</span> ctrl.Result{}, <span class="hljs-literal">nil</span>}<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(r *AppReconciler)</span> <span class="hljs-title">SetupWithManager</span><span class="hljs-params">(mgr ctrl.Manager)</span> <span class="hljs-title">error</span></span> { <span class="hljs-keyword">return</span> ctrl.NewControllerManagedBy(mgr). For(&k8sv1.App{}). Owns(&appsv1.Deployment{}). Owns(&corev1.Service{}). Owns(&extv1.Ingress{}). Complete(r)}</code></pre></div><p>在样例中主要定义了 Deployment、Service 和 Ingress,详细代码逻辑如下:</p><div class="hljs"><pre><code class="hljs go"><span class="hljs-comment">// 构造一个deployment</span><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(r *AppReconciler)</span> <span class="hljs-title">deployment</span><span class="hljs-params">(app *k8sv1.App)</span> *<span class="hljs-title">appsv1</span>.<span class="hljs-title">Deployment</span></span> {ls := labelsForApp(app.Name, <span class="hljs-string">"deployment"</span>)deploy := &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ Name: app.Name, Namespace: app.Namespace, }, Spec: appsv1.DeploymentSpec{ Replicas: app.Spec.Replicas, Selector: &metav1.LabelSelector{ MatchLabels: ls, }, Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Labels: ls, }, Spec: corev1.PodSpec{ Containers: []corev1.Container{ { Image: app.Spec.Image, Name: app.Name, Ports: []corev1.ContainerPort{ { ContainerPort: app.Spec.Port, Name: app.Name, }, }, }, }, }, }, },}deploy.SetLabels(ls)_ = ctrl.SetControllerReference(app, deploy, r.Scheme)<span class="hljs-keyword">return</span> deploy}<span class="hljs-comment">// 构造一个Service</span><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(r *AppReconciler)</span> <span class="hljs-title">service</span><span class="hljs-params">(app *k8sv1.App)</span> *<span class="hljs-title">corev1</span>.<span class="hljs-title">Service</span></span> {ls := labelsForApp(app.Name, <span class="hljs-string">"service"</span>)svc := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: app.Name, Namespace: app.Namespace, }, Spec: corev1.ServiceSpec{ Ports: []corev1.ServicePort{ { Port: app.Spec.Port, Protocol: corev1.ProtocolTCP, TargetPort: intstr.IntOrString{Type: <span class="hljs-number">0</span>, IntVal: app.Spec.Port}, Name: app.Name, }, }, Selector: <span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]<span class="hljs-keyword">string</span>{<span class="hljs-string">"app"</span>: app.Name}, SessionAffinity: corev1.ServiceAffinityNone, Type: corev1.ServiceTypeClusterIP, },}svc.SetLabels(ls)_ = ctrl.SetControllerReference(app, svc, r.Scheme)<span class="hljs-keyword">return</span> svc}<span class="hljs-comment">// 构建一个ingress</span><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(r *AppReconciler)</span> <span class="hljs-title">ingress</span><span class="hljs-params">(app *k8sv1.App)</span> *<span class="hljs-title">extv1</span>.<span class="hljs-title">Ingress</span></span> {ingress := &extv1.Ingress{ ObjectMeta: metav1.ObjectMeta{ Namespace: app.Namespace, Name: app.Name, Annotations: <span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]<span class="hljs-keyword">string</span>{ <span class="hljs-string">"nginx.ingress.kubernetes.io/rewrite-target"</span>: <span class="hljs-string">"/"</span>, }, }, Spec: extv1.IngressSpec{ Rules: []extv1.IngressRule{ { Host: app.Spec.Host, IngressRuleValue: extv1.IngressRuleValue{ HTTP: &extv1.HTTPIngressRuleValue{ Paths: []extv1.HTTPIngressPath{ { Path: app.Spec.Context, Backend: extv1.IngressBackend{ ServicePort: intstr.IntOrString{Type: <span class="hljs-number">0</span>, IntVal: app.Spec.Port}, ServiceName: app.Name, }, }, }, }, }, }, }, },}ls := labelsForApp(app.Name, <span class="hljs-string">"ingress"</span>)ingress.SetLabels(ls)_ = ctrl.SetControllerReference(app, ingress, r.Scheme)<span class="hljs-keyword">return</span> ingress}</code></pre></div><p>这一步主要根据数据结构,生成自己想要的 deployment、Service 和 Ingress</p><h3 id="生成-crd"><a href="#生成-crd" class="headerlink" title="生成 crd"></a>生成 crd</h3><p>根据数据结构自动生成 crd 文件,文件在 config/crd 目录下</p><div class="hljs"><pre><code class="hljs sh">make generatemake manifests</code></pre></div><p>本地 k8s 集群安装 crd 并启动 operator</p><div class="hljs"><pre><code class="hljs sh">make installmake run ENABLE_WEBHOOKS=<span class="hljs-literal">false</span></code></pre></div><h3 id="编写-一个-App-CR"><a href="#编写-一个-App-CR" class="headerlink" title="编写 一个 App CR"></a>编写 一个 App CR</h3><p>在 config/samples 目下找到 k8s_v1_app.yaml,填写如下内容:</p><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">k8s.bryce-huang.club/v1</span><span class="hljs-attr">kind:</span> <span class="hljs-string">App</span><span class="hljs-attr">metadata:</span> <span class="hljs-attr">name:</span> <span class="hljs-string">app-sample</span><span class="hljs-attr">spec:</span> <span class="hljs-attr">image:</span> <span class="hljs-string">nginx</span> <span class="hljs-attr">replicas:</span> <span class="hljs-number">3</span> <span class="hljs-attr">context:</span> <span class="hljs-string">/</span> <span class="hljs-attr">host:</span> <span class="hljs-string">bryce.club</span> <span class="hljs-attr">svcPort:</span> <span class="hljs-number">80</span></code></pre></div><h3 id="创建-App-资源"><a href="#创建-App-资源" class="headerlink" title="创建 App 资源"></a>创建 App 资源</h3><p>使用: <code>kubectl apply -f config/samples/k8s_v1_app.yaml</code> 创建自定义资源</p><p>通过<code>kubectl get pod,svc,ingress -l app=app-sample</code></p><p>然后看到 operator 已经自动创建了 pod 和 service 和 ingress</p><div class="hljs"><pre><code class="hljs std">NAME READY STATUS RESTARTS AGEpod/app-sample-559f49f8d7-hzdmg 1/1 Running 0 4m45spod/app-sample-559f49f8d7-qthf5 1/1 Running 0 4m45spod/app-sample-559f49f8d7-sv894 1/1 Running 0 4m45sNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEservice/app-sample ClusterIP 10.99.211.244 <none> 80/TCP 4m44sNAME HOSTS ADDRESS PORTS AGEingress.extensions/app-sample bryce.club localhost 80 4m44s</code></pre></div><p>然后通过 <code>echo "127.0.0.1 bryce.club" >> /etc/hosts</code>就可以通过 bryce.club 访问 nginx 的欢迎页面了</p><h3 id="发布-Operator"><a href="#发布-Operator" class="headerlink" title="发布 Operator"></a>发布 Operator</h3><p>我们往往需要将 operator 提供给他人使用,可以执行下面的命令,构建并推送到 dockerhub</p><p>构建镜像</p><div class="hljs"><pre><code class="hljs sh">make docker-build IMG=docker.io/brycehuang/app-operator:v1</code></pre></div><p>推送镜像</p><div class="hljs"><pre><code class="hljs sh">make docker-push IMG=docker.io/brycehuang/app-operator:v1</code></pre></div><p>部署镜像</p><div class="hljs"><pre><code class="hljs sh"><span class="hljs-built_in">cd</span> config/default/ && kustomize edit <span class="hljs-built_in">set</span> namespace <span class="hljs-string">"default"</span> && <span class="hljs-built_in">cd</span> ../..make deploy IMG=docker.io/brycehuang/app-operator:v1</code></pre></div><p>创建 app crd</p><div class="hljs"><pre><code class="hljs sh">kubectl apply -f config/samples/k8s_v1_app.yaml</code></pre></div><p>清理</p><div class="hljs"><pre><code class="hljs bash">kubectl delete -f config/samples/ config/samples/k8s_v1_app.yamlkubectl delete deployments,service -l control-plane=controller-managerkubectl delete role,rolebinding --all</code></pre></div><h3 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h3><p><a href="https://github.com/Bryce-huang/app-operator" target="_blank" rel="noopener">app-operator 完整代码</a></p><p><a href="https://sdk.operatorframework.io/docs/building-operators/golang/tutorial/" target="_blank" rel="noopener">operator-sdk 官方文档</a></p>]]></content>
<categories>
<category>k8s</category>
</categories>
<tags>
<tag>k8s</tag>
<tag>operator</tag>
<tag>crd</tag>
</tags>
</entry>
<entry>
<title>mysql基础知识</title>
<link href="/post/451dc79a.html"/>
<url>/post/451dc79a.html</url>
<content type="html"><![CDATA[<h2 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h2><h3 id="docker安装"><a href="#docker安装" class="headerlink" title="docker安装"></a>docker安装</h3><div class="hljs"><pre><code class="hljs shell">docker run -p 3306:3306 --name mysql -v /data/mysql:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7</code></pre></div><h2 id="设计"><a href="#设计" class="headerlink" title="设计"></a>设计</h2><h3 id="设计范式"><a href="#设计范式" class="headerlink" title="设计范式"></a>设计范式</h3><h4 id="1NF"><a href="#1NF" class="headerlink" title="1NF"></a>1NF</h4><p>强调的是列的原子性,即列不能够再分成其他几列。 无重复列。</p><p>在任何一个关系数据库中,第一范式(1NF)是对关系模式的设计基本要求,一般设计中都必须满足第一范式(1NF)。不过有些关系模型中突破了1NF的限制,这种称为非1NF的关系模型。换句话说,是否必须满足1NF的最低要求,主要依赖于所使用的关系模型。</p><h4 id="2NF"><a href="#2NF" class="headerlink" title="2NF"></a>2NF</h4><p>首先是 1NF,另外包含两部分内容,一是表必须有一个主键;二是没有包含在主键中的列必须完全依赖于主键,而不能只依赖于主键的一部分。</p><h4 id="3NF"><a href="#3NF" class="headerlink" title="3NF"></a>3NF</h4><p>在1NF基础上,任何非主属性不依赖于其它非主属性[在2NF基础上消除传递依赖]。</p><p>第三范式(3NF)是第二范式(2NF)的一个子集,即满足第三范式(3NF)必须满足第二范式(2NF)</p><h2 id="索引"><a href="#索引" class="headerlink" title="索引"></a>索引</h2><h3 id="索引类型"><a href="#索引类型" class="headerlink" title="索引类型"></a>索引类型</h3><h4 id="FULLTEXT"><a href="#FULLTEXT" class="headerlink" title="FULLTEXT"></a>FULLTEXT</h4><p>即为全文索引,目前只有MyISAM引擎支持。其可以在CREATE TABLE ,ALTER TABLE ,CREATE INDEX 使用,不过目前只有 CHAR、VARCHAR ,TEXT 列上可以创建全文索引。值得一提的是,在数据量较大时候,现将数据放入一个没有全局索引的表中,然后再用CREATE INDEX创建FULLTEXT索引,要比先为一张表建立FULLTEXT然后再将数据写入的速度快很多。</p><p>全文索引的使用方法并不复杂:</p><p>创建:</p><div class="hljs"><pre><code class="hljs sql"><span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">TABLE</span> <span class="hljs-keyword">table</span> <span class="hljs-keyword">ADD</span> <span class="hljs-keyword">INDEX</span> <span class="hljs-string">`FULLINDEX`</span> <span class="hljs-keyword">USING</span> FULLTEXT(<span class="hljs-string">`cname1`</span>[,cname2…]);`</code></pre></div><p>查找:</p><div class="hljs"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> <span class="hljs-keyword">table</span> <span class="hljs-keyword">WHERE</span> <span class="hljs-keyword">MATCH</span>(cname1[,cname2…]) AGAINST (<span class="hljs-string">'word'</span> <span class="hljs-keyword">MODE</span> );</code></pre></div><p>其中, MODE为搜寻方式(IN BOOLEAN MODE ,IN NATURAL LANGUAGE MODE ,IN NATURAL LANGUAGE MODE WITH QUERY EXPANSION / WITH QUERY EXPANSION)</p><h4 id="HASH"><a href="#HASH" class="headerlink" title="HASH"></a>HASH</h4><p>由于HASH的唯一(几乎100%的唯一)及类似键值对的形式,很适合作为索引。</p><p>HASH索引可以一次定位,不需要像树形索引那样逐层查找,因此具有极高的效率。但是,这种高效是有条件的,即只在“=”和“in”条件下高效,对于范围查询、排序及组合索引仍然效率不高。</p><ol><li>Hash 索引仅仅能满足”=”,”IN”和”<=>”查询,不能使用范围查询。由于 Hash 索引比较的是进行 Hash 运算之后的 Hash 值,所以它只能用于等值的过滤,不能用于基于范围的过滤,因为经过相应的 Hash 算法处理之后的 Hash 值的大小关系,并不能保证和Hash运算前完全一样。</li><li>Hash 索引无法被用来避免数据的排序操作。由于 Hash 索引中存放的是经过 Hash 计算之后的 Hash 值,而且Hash值的大小关系并不一定和 Hash 运算前的键值完全一样,所以数据库无法利用索引的数据来避免任何排序运算;</li><li>Hash 索引不能利用部分索引键查询。对于组合索引,Hash 索引在计算 Hash 值的时候是组合索引键合并后再一起计算 Hash 值,而不是单独计算 Hash 值,所以通过组合索引的前面一个或几个索引键进行查询的时候,Hash 索引也无法被利用。</li><li>Hash 索引在任何时候都不能避免表扫描。前面已经知道,Hash 索引是将索引键通过 Hash 运算之后,将 Hash运算结果的 Hash 值和所对应的行指针信息存放于一个 Hash 表中,由于不同索引键存在相同 Hash 值,所以即使取满足某个 Hash 键值的数据的记录条数,也无法从 Hash 索引中直接完成查询,还是要通过访问表中的实际数据进行相应的比较,并得到相应的结果。</li><li>Hash 索引遇到大量Hash值相等的情况后性能并不一定就会比B-Tree索引高。对于选择性比较低的索引键,如果创建 Hash 索引,那么将会存在大量记录指针信息存于同一个 Hash 值相关联。这样要定位某一条记录时就会非常麻烦,会浪费多次表数据的访问,而造成整体性能低下。</li></ol><p>hash值即为通过特定算法由指定列数据计算出来,磁盘地址即为所在数据行存储在硬盘上的地址(也有可能是其他存储地址,其实MEMORY会将hash表导入内存。<br>这样,当我们进行WHERE age = 18 时,<em>会将18通过相同的算法计算出一个hash值==>在hash表中找到对应的储存地址==>根据存储地址取得数据</em>。<br>所以,每次查询时都要遍历hash表,直到找到对应的hash值,如(4),数据量大了之后,hash表也会变得庞大起来,性能下降,遍历耗时增加,如(5)。</p><h4 id="BTREE"><a href="#BTREE" class="headerlink" title="BTREE"></a>BTREE</h4><p>BTREE索引就是一种将索引值按一定的算法,存入一个树形的数据结构中.<br>BTREE在MyISAM里的形式和Innodb稍有不同</p><p>在 Innodb里,有两种形态:一是primary key形态,其leaf node里存放的是数据,而且不仅存放了索引键的数据,还存放了其他字段的数据。二是secondary index,其leaf node和普通的BTREE差不多,只是还存放了指向主键的信息.</p><p>而在MyISAM里,主键和其他的并没有太大区别。不过和Innodb不太一样的地方是在MyISAM里,leaf node里存放的不是主键的信息,而是指向数据文件里的对应数据行的信息</p><h4 id="RTREE"><a href="#RTREE" class="headerlink" title="RTREE"></a>RTREE</h4><p>RTREE在mysql很少使用,仅支持geometry数据类型,支持该类型的存储引擎只有MyISAM、BDb、InnoDb、NDb、Archive几种。</p><p>相对于BTREE,RTREE的优势在于范围查找.</p><h4 id="各种索引的使用情况"><a href="#各种索引的使用情况" class="headerlink" title="各种索引的使用情况"></a>各种索引的使用情况</h4><ol><li>对于BTREE这种Mysql默认的索引类型,具有普遍的适用性</li><li>由于FULLTEXT对中文支持不是很好,在没有插件的情况下,最好不要使用。</li><li>对于一些搜索引擎级别的应用来说,FULLTEXT同样不是一个好的处理方法,Mysql的全文索引建立的文件还是比较大的,而且效率不是很高,即便是使用了中文分词插件,对中文分词支持也只是一般。真要碰到这种问题,Apache的Lucene或许是你的选择</li><li>正是因为hash表在处理较小数据量时具有无可比拟的素的优势,所以hash索引很适合做缓存(内存数据库)。如mysql数据库的内存版本Memsql,使用量很广泛的缓存工具Mencached,NoSql数据库redis等,都使用了hash索引这种形式。当然,不想学习这些东西的话Mysql的MEMORY引擎也是可以满足这种需求的。</li></ol><h3 id="索引种类"><a href="#索引种类" class="headerlink" title="索引种类"></a>索引种类</h3><h4 id="主键索引"><a href="#主键索引" class="headerlink" title="主键索引"></a>主键索引</h4><p>主键是一种唯一性索引,但它必须指定为PRIMARY KEY,每个表只能有一个主键:<code>alert table tablename add primary key ('字段名')</code></p><h4 id="唯一索引"><a href="#唯一索引" class="headerlink" title="唯一索引"></a>唯一索引</h4><p>索引列的所有值都只能出现一次,即必须唯一,值可以为空: <code>alter table table_name add primary key (字段名');</code></p><h4 id="普通索引"><a href="#普通索引" class="headerlink" title="普通索引"></a>普通索引</h4><p>基本的索引类型,值可以为空,没有唯一性的限制: <code>alter table table_name add index ('字段名');</code></p><h4 id="全文索引"><a href="#全文索引" class="headerlink" title="全文索引"></a>全文索引</h4><p>全文索引的索引类型为FULLTEXT。全文索引可以在varchar、char、text类型的列上创建。可以通过ALTER TABLE或CREATE INDEX命令创建。对于大规模的数据集,通过ALTER TABLE(或者CREATE INDEX)命令创建全文索引要比把记录插入带有全文索引的空表更快。MyISAM支持全文索引,InnoDB在mysql5.6之后支持了全文索引。全文索引不支持中文需要借sphinx(coreseek)或迅搜<、code>技术处理中文。</p><p>用法:<code>alter table 表名 add FULLTEXT('字段名');</code></p><h4 id="索引总结"><a href="#索引总结" class="headerlink" title="索引总结"></a>索引总结</h4><p><strong>优点:</strong></p><p>传统的查询方法,是按照表的顺序遍历的,不论查询几条数据,mysql需要将表的数据从头到尾遍历一遍,在我们添加完索引之后,mysql一般通过BTREE算法生成一个索引文件,在查询数据库时,找到索引文件进行遍历(折半查找大幅查询效率),找到相应的键从而获取数据</p><p><strong>缺点:</strong></p><ol><li>创建索引是为产生索引文件的,占用磁盘空间</li><li>索引文件是一个二叉树类型的文件,可想而知我们的dml操作同样也会对索引文件进行修改,所以性能会下降</li></ol><p><strong>在哪些column上使用索引?</strong></p><ol><li>较频繁的作为查询条件字段应该创建索引</li><li>唯一性太差的字段不适合创建索引,尽管频繁作为查询条件,例如gender性别字段</li><li>更新非常频繁的字段不适合作为索引</li><li>不会出现在where子句中的字段不该创建索引</li></ol><p><em>总结:</em><br>a: 肯定在where条经常使用 b: 该字段的内容不是唯一的几个值 c: 字段内容不是频繁变化。</p><h2 id="备份"><a href="#备份" class="headerlink" title="备份"></a>备份</h2><p>MySQL数据库备份分为逻辑备份和物理备份两大类,犹豫到底用那种备份方式的时候先了解下它们的差异:<br>逻辑备份的特点是:直接生成SQL语句,在恢复的时候执行备份的SQL语句实现数据库数据的重现。</p><p>物理备份的特点是:拷贝相关数据文件。</p><p>这二种备份差异:逻辑备份其备份、还原慢,但备份文件占用的空间小;物理备份其备份还原快,备份文件占用空间大。</p><h3 id="mysqldump"><a href="#mysqldump" class="headerlink" title="mysqldump"></a>mysqldump</h3><p>逻辑备份成sql语句<br>mysqldump的大致实现过程是:连接 -> 初始化信息 -> 刷新表(锁表)-> 记录偏移量 -> 开启事务(一致性快照)-> 记录偏移量 -> 解锁表,因为开启了一致性读,可以得到innodb的一致性,又因为解锁表了,MyISAM表一致性得不到保证,所以尽量别使用MyISAM表。</p><div class="hljs"><pre><code class="hljs sql">mysqldump -uroot -p123 <span class="hljs-comment">--default-character-set=utf8 --single-transaction --master-data=1 -R -E --triggers -B dba_test dba_test2 > /home/dxy/dba_test.sql</span></code></pre></div><p>详细参考:<a href="https://www.cnblogs.com/zhoujinyi/p/5789465.html" target="_blank" rel="noopener">mysqldump</a></p><h3 id="xtrabackup"><a href="#xtrabackup" class="headerlink" title="xtrabackup"></a>xtrabackup</h3><p>物理备份</p><div class="hljs"><pre><code class="hljs sql">xtrabackup <span class="hljs-comment">--user=root --password=123 --datadir=/var/lib/mysql/ --backup --no-timestamp --slave-info --safe-slave-backup --parallel=5 --databases='xtra_test dba_test' --target-dir=/home/bryce/xtrabackup/</span></code></pre></div><p>还原</p><div class="hljs"><pre><code class="hljs sql">xtrabackup <span class="hljs-comment">--prepare --target-dir=/home/bryce/xtrabackup/</span></code></pre></div><p>详情参考:<a href="https://www.cnblogs.com/zhoujinyi/p/5893333.html" target="_blank" rel="noopener">xtrabackup</a></p><h2 id="MVCC"><a href="#MVCC" class="headerlink" title="MVCC"></a>MVCC</h2><p>MVCC,Multi-Version Concurrency Control,多版本并发控制。MVCC 是一种并发控制方法,通常由数据库管理系统用来提供对数据库的并发访问,并以编程语言来实现事务存储。</p><p>就是 同一份数据临时保留多版本的一种方式,进而实现并发控制。</p><p>如果有人从数据库中读数据的同时,有另外的人写入数据,有可能读数据的人会看到『半写』或者不一致的数据。有很多种方法来解决这个问题,叫做并发控制方法。最简单的方法,通过加锁,让所有的读者等待写者工作完成,但是这样效率会很差。MVCC 使用了一种不同的手段,每个连接到数据库的读者,在某个瞬间看到的是数据库的一个快照,写者写操作造成的变化在写操作完成之前(或者数据库事务提交之前)对于其他的读者来说是不可见的。</p><p>当一个 MVCC 数据库需要更一个一条数据记录的时候,它不会直接用新数据覆盖旧数据,而是将旧数据标记为过时(obsolete)并在别处增加新版本的数据。这样就会有存储多个版本的数据,但是只有一个是最新的。这种方式允许读者读取在他读之前已经存在的数据,即使这些在读的过程中半路被别人修改、删除了,也对先前正在读的用户没有影响。<strong>这种多版本的方式避免了填充删除操作在内存和磁盘存储结构造成的空洞的开销,但是需要系统周期性整理(sweep through)以真实删除老的、过时的数据</strong>。对于面向文档的数据库(Document-oriented database,也即半结构化数据库)来说,这种方式允许系统将整个文档写到磁盘的一块连续区域上,当需要更新的时候,直接重写一个版本,而不是对文档的某些比特位、分片切除,或者维护一个链式的、非连续的数据库结构。</p><p>MVCC 提供了时点(point in time)一致性视图。MVCC 并发控制下的读事务一般使用时间戳或者事务 ID去标记当前读的数据库的状态(版本),读取这个版本的数据。读、写事务相互隔离,不需要加锁。读写并存的时候,写操作会根据目前数据库的状态,创建一个新版本,并发的读则依旧访问旧版本的数据。</p><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>一般我们认为MVCC有下面几个特点:</p><ol><li><p>每行数据都存在一个版本,每次数据更新时都更新该版本</p><ul><li>修改时Copy出当前版本, 然后随意修改,各个事务之间无干扰</li><li>保存时比较版本号,如果成功(commit),则覆盖原记录, 失败则放弃copy(rollback)</li><li>就是每行都有版本号,保存时根据版本号决定是否成功,听起来含有乐观锁的味道, 因为这看来正是,在提交的时候才能知道到底能否提交成功</li></ul></li><li><p>而InnoDB实现MVCC的方式是:</p><ul><li>事务以排他锁的形式修改原始数据</li><li>把修改前的数据存放于undo log,通过回滚指针与主数据关联</li><li>修改成功(commit)啥都不做,失败则恢复undo log中的数据(rollback)</li></ul></li><li><p>二者最本质的区别是: 当修改数据时是否要排他锁定,如果锁定了还算不算是MVCC?</p><ul><li>Innodb的实现真算不上MVCC, 因为并没有实现核心的多版本共存, undo log 中的内容只是串行化的结果, 记录了多个事务的过程, 不属于多版本共存。但理想的MVCC是难以实现的, 当事务仅修改一行记录使用理想的MVCC模式是没有问题的, 可以通过比较版本号进行回滚, 但当事务影响到多行数据时, 理想的MVCC就无能为力了。</li><li>比如, 如果事务A执行理想的MVCC, 修改Row1成功, 而修改Row2失败, 此时需要回滚Row1, 但因为Row1没有被锁定, 其数据可能又被事务B所修改, 如果此时回滚Row1的内容,则会破坏事务B的修改结果,导致事务B违反ACID。 这也正是所谓的 第一类更新丢失 的情况。</li><li>也正是因为InnoDB使用的MVCC中结合了排他锁, 不是纯的MVCC, 所以第一类更新丢失是不会出现了, 一般说更新丢失都是指第二类丢失更新。</li></ul></li></ol><h2 id="log"><a href="#log" class="headerlink" title="log"></a>log</h2><p>MySQL中有六种日志文件,<br>分别是:重做日志(redo log)、回滚日志(undo log)、二进制日志(binlog)、错误日志(errorlog)、慢查询日志(slow query log)、一般查询日志(general log),中继日志(relay log)。<br>其中重做日志和回滚日志与事务操作息息相关,二进制日志也与事务操作有一定的关系,这三种日志,对理解MySQL中的事务操作有着重要的意义。</p><h3 id="重做日志(redo-log)"><a href="#重做日志(redo-log)" class="headerlink" title="重做日志(redo log)"></a>重做日志(redo log)</h3><p><strong>作用:</strong></p><ul><li><p>确保事务的持久性</p></li><li><p>防止在发生故障的时间点,尚有脏页未写入磁盘,在重启mysql服务的时候,根据redo log进行重做,从而达到事务的持久性这一特性。</p></li></ul><p><strong>内容:</strong></p><p>物理格式的日志,记录的是物理数据页面的修改的信息,其redo log是顺序写入redo log file 的物理文件中去的。</p><p><strong>什么时候产生:</strong></p><p>事务开始之后就产生redo log,redo log的落盘并不是随着事务的提交才写入的,而是在事务的执行过程中,便开始写入redo log文件中。</p><p><strong>什么时候释放:</strong></p><p>当对应的事务脏页写入到磁盘之后,redo log 的使命也就完成了,重做日志占用的空间就可以重用(被覆盖)。</p><p><strong>对应的物理文件:</strong></p><p>默认情况下,对应的物理文件位于数据库的data目录下的ib_logfile1&ib_logfule2</p><p>innodb_log_group_home_dir 指定日志文件组所在的路径,默认./ ,表示在数据库的数据目录下。<br>innodb_log_files_in_group 指定重做日志文件组中文件的数量,默认2</p><p>关于文件的大小和数量,由一下两个参数配置</p><ul><li><p>innodb_log_file_size 重做日志文件的大小。</p></li><li><p>innodb_mirrored_log_groups 指定了日志镜像文件组的数量,默认1</p></li></ul><p><strong>其他:</strong></p><p>很重要一点,redo log是什么时候写盘的?前面说了是在事物开始之后逐步写盘的。之所以说重做日志是在事务开始之后逐步写入重做日志文件,而不一定是事务提交才写入重做日志缓存,原因就是,重做日志有一个缓存区Innodb_log_buffer,Innodb_log_buffer的默认大小为8M,Innodb存储引擎先将重做日志写入innodb_log_buffer中。</p><p>然后会通过以下三种方式将innodb日志缓冲区的日志刷新到磁盘</p><ol><li>Master Thread 每秒一次执行刷新Innodb_log_buffer到重做日志文件。</li><li>每个事务提交时会将重做日志刷新到重做日志文件。</li><li>当重做日志缓存可用空间 少于一半时,重做日志缓存被刷新到重做日志文件</li></ol><div class="hljs"><pre><code class="hljs sql"><span class="hljs-keyword">show</span> <span class="hljs-keyword">variables</span> <span class="hljs-keyword">like</span> <span class="hljs-string">'innodb_log_buffer_size'</span>;</code></pre></div><p>由此可以看出,重做日志通过不止一种方式写入到磁盘,尤其是对于第一种方式,Innodb_log_buffer到重做日志文件是Master Thread线程的定时任务。因此重做日志的写盘,并不一定是随着事务的提交才写入重做日志文件的,而是随着事务的开始,逐步开始的。</p><p>另外引用《MySQL技术内幕 Innodb 存储引擎》(page37)上的原话:</p><p>即使某个事务还没有提交,Innodb存储引擎仍然每秒会将重做日志缓存刷新到重做日志文件。<br>这一点是必须要知道的,因为这可以很好地解释再大的事务的提交(commit)的时间也是很短暂的。</p><h3 id="回滚日志(undo-log)"><a href="#回滚日志(undo-log)" class="headerlink" title="回滚日志(undo log)"></a>回滚日志(undo log)</h3><p><strong>作用:</strong></p><p>保存了事务发生的之前的数据的一个版本,可以用于回滚,同时可以提供多版本并发控制下的读(MVCC),也即非锁定读</p><p><strong>内容:</strong></p><p>逻辑格式的日志,在执行undo的时候,仅仅是将数据从逻辑上恢复至事务之前的状态,而不是从物理页面上操作实现的,这一点是不同于redo log的。</p><p><strong>什么时候产生:</strong></p><p>事务开始之前,将当前的版本生成undo log,undo也会产生redo 来保证undo log 的可靠性。</p><p><strong>什么时候释放:</strong></p><p>当事务提交之后,undo log 并不能立马被删除,而是放入待清理的链表,有purge线程判断是否由其他事务在使用undo段中表的上一个事务之前的版本信息,决定是否可以清理undo log的日志空间。</p><p><strong>对应的物理文件:</strong></p><p>MySQL5.6之前,undo表空间位于共享表空间的回滚段中,共享表空间的默认的名称是ibdata,位于数据文件目录中。<br>MySQL5.6之后,undo表空间可以配置成独立的文件,但是提前需要在配置文件中配置,完成数据库初始化后生效且不可改变undo log文件的个数<br>如果初始化数据库之前没有进行相关配置,那么就无法配置成独立的表空间了。<br>关于MySQL5.7之后的独立undo 表空间配置参数如下</p><ul><li>innodb_undo_directory = /data/undospace/ –undo独立表空间的存放目录</li><li>innodb_undo_logs = 128 –回滚段为128KB</li><li>innodb_undo_tablespaces = 4 –指定有4个undo log文件</li></ul><p>如果undo使用的共享表空间,这个共享表空间中又不仅仅是存储了undo的信息,共享表空间的默认为与MySQL的数据目录下面,其属性由参数innodb_data_file_path配置。</p><div class="hljs"><pre><code class="hljs sql"><span class="hljs-keyword">show</span> <span class="hljs-keyword">variables</span> <span class="hljs-keyword">like</span> <span class="hljs-string">'%innodb_data_file_path%'</span>;</code></pre></div><p><strong>其他:</strong></p><p>undo是在事务开始之前保存的被修改数据的一个版本,产生undo日志的时候,同样会伴随类似于保护事务持久化机制的redolog的产生。<br>默认情况下undo文件是保持在共享表空间的,也即ibdatafile文件中,当数据库中发生一些大的事务性操作的时候,要生成大量的undo信息,全部保存在共享表空间中的。<br>因此共享表空间可能会变的很大,默认情况下,也就是undo 日志使用共享表空间的时候,被“撑大”的共享表空间是不会也不能自动收缩的。<br>因此,mysql5.7之后的“独立undo 表空间”的配置就显得很有必要了。</p><h3 id="二进制日志(binlog)"><a href="#二进制日志(binlog)" class="headerlink" title="二进制日志(binlog)"></a>二进制日志(binlog)</h3><p><strong>作用:</strong></p><ol><li>用于复制,在主从复制中,从库利用主库上的binlog进行重播,实现主从同步。</li><li>用于数据库的基于时间点的还原。</li></ol><p><strong>内容:</strong></p><p>逻辑格式的日志,可以简单认为就是执行过的事务中的sql语句<br>但又不完全是sql语句这么简单,而是包括了执行的sql语句(增删改)反向的信息,也就意味着delete对应着delete本身和其反向的insert;update对应着update执行前后的版本的信息;insert对应着delete和insert本身的信息。<br>因此可以基于binlog做到类似于oracle的闪回功能,其实都是依赖于binlog中的日志记录。</p><p><strong>什么时候产生:</strong></p><p>事务提交的时候,一次性将事务中的sql语句(一个事物可能对应多个sql语句)按照一定的格式记录到binlog中。</p><p>这里与redo log很明显的差异就是redo log并不一定是在事务提交的时候刷新到磁盘,redo log是在事务开始之后就开始逐步写入磁盘。</p><p>因此对于事务的提交,即便是较大的事务,提交(commit)都是很快的,但是在开启了bin_log的情况下,对于较大事务的提交,可能会变得比较慢一些。</p><p>这是因为binlog是在事务提交的时候一次性写入的造成的,这些可以通过测试验证。</p><p><strong>什么时候释放:</strong></p><p>binlog的默认是保持时间由参数expire_logs_days配置,也就是说对于非活动的日志文件,在生成时间超过expire_logs_days配置的天数之后,会被自动删除</p><div class="hljs"><pre><code class="hljs sql"><span class="hljs-keyword">show</span> <span class="hljs-keyword">variables</span> <span class="hljs-keyword">like</span> <span class="hljs-string">'%expire_logs_days%'</span>;</code></pre></div><p><strong>对应物理文件:</strong></p><p>配置文件的路径为log_bin_basename,binlog日志文件按照指定大小,当日志文件达到指定的最大的大小之后,进行滚动更新,生成新的日志文件。</p><p>对于每个binlog日志文件,通过一个统一的index文件来组织。</p><div class="hljs"><pre><code class="hljs sql"><span class="hljs-keyword">show</span> <span class="hljs-keyword">variables</span> <span class="hljs-keyword">like</span> <span class="hljs-string">'%log_bin_basename%'</span>;</code></pre></div><p><strong>其他:</strong></p><p>二进制日志的作用之一是还原数据库的,这与redo log很类似,很多人混淆过,但是两者有本质的不同</p><ol><li><p>作用不同:redo log是保证事务的持久性的,是事务层面的,binlog作为还原的功能,是数据库层面的(当然也可以精确到事务层面的),虽然都有还原的意思,但是其保护数据的层次是不一样的。</p></li><li><p>内容不同:redo log是物理日志,是数据页面的修改之后的物理记录,binlog是逻辑日志,可以简单认为记录的就是sql语句</p></li><li><p>另外,两者日志产生的时间,可以释放的时间,在可释放的情况下清理机制,都是完全不同的。</p></li><li><p>恢复数据时候的效率,基于物理日志的redo log恢复数据的效率要高于语句逻辑日志的binlog</p></li></ol><h4 id="binlog的三种模式"><a href="#binlog的三种模式" class="headerlink" title="binlog的三种模式"></a>binlog的三种模式</h4><h5 id="Statement模式"><a href="#Statement模式" class="headerlink" title="Statement模式"></a>Statement模式</h5><p>每一条修改数据的SQL都会记录到master的binlog中,slave在复制的时候,sql进程会解析成和原来在master端执行时相同的sql再执行。</p><p><strong>优点:</strong> 在 statement 模式下首先就是解决了 row 模式的缺点,不需要记录每一行数据的变化,从而减少了 binlog 的日志量,节省了 I/O 以及存储资源,提高性能。因为它只需要记录在 master 上执行的语句的细节以及执行语句的上下文信息。</p><p><strong>缺点:</strong> 在 statement 模式下,由于它是记录的执行语句,所以,为了让这些语句在 slave 端也能正确执行,那么它还必须记录每条语句在执行的时候的一些相关信息,即上下文信息,以保证所有语句在 slave 端和在 master 端执行结果相同。另外就是,由于 MySQL 现在发展比较快,很多新功能不断的加入,使 MySQL 的复制遇到了不小的挑战,自然复制的时候涉及到越复杂的内容,bug 也就越容易出现。在statement 中,目前已经发现不少情况会造成 MySQL 的复制出现问题,主要是在修改数据的时候使用了某些特定的函数或者功能才会出现,比如:sleep() 函数在有些版本中就不能被正确复制,在存储过程中使用了 last_insert_id() 函数,可能会使 slave 和 master 上得到不一致的 id 等等。由于 row 模式是基于每一行来记录变化的,所以不会出现类似的问题。</p><h5 id="Row模式"><a href="#Row模式" class="headerlink" title="Row模式"></a>Row模式</h5><p>日志中会记录每一行数据被修改的形式,然后在slave端再对相同的数据进行修改。row模式只记录要修改的数据,只有value,不会有sql多表关联的情况。</p><p><strong>优点:</strong> 在 row 模式下,binlog 中可以不记录执行的 sql 语句的上下文相关的信息,仅仅只需要记录哪一条记录被修改了,修改成什么样了,所以 row 的日志内容会非常清楚的记录下每一行数据的修改细节,非常容易理解。而且不会出现某些特定情况下的存储过程和 function,以及 trigger 的调用和触发无法被正确复制问题。<br><strong>缺点:</strong> 在 row 模式下,当所有执行语句记录到日志中的时候,都将以每行记录的修改来记录,这样可能会产生大量的日志内容。</p><h5 id="Mixed模式"><a href="#Mixed模式" class="headerlink" title="Mixed模式"></a>Mixed模式</h5><p>从官方文档中看到,之前的 MySQL 一直都只有基于 statement 的复制模式,直到 5.1.5 版本的 MySQL 才开始支持 row 复制。从 5.0 开始,MySQL 的复制已经解决了大量老版本中出现的无法正确复制的问题。但是由于存储过程的出现,给 MySQL Replication 又带来了更大的新挑战。另外,看到官方文档说,从 5.1.8 版本开始,MySQL 提供了除 Statement 和 Row 之外的第三种复制模式:Mixed,实际上就是前两种模式的结合。在 Mixed 模式下,MySQL 会根据执行的每一条具体的 SQL 语句来区分对待记录的日志形式,也就是在 statement 和 row 之间选择一种。新版本中的 statment 还是和以前一样,仅仅记录执行的语句。而新版本的 MySQL 也对 row 模式做了优化,并不是所有的修改都会以 row 模式来记录,比如遇到表结构变更的时候就会以 statement 模式来记录,如果 SQL 语句确实就是 update 或者 delete 等修改数据的语句,那么还是会记录所有行的变更。</p><h2 id="存储引擎"><a href="#存储引擎" class="headerlink" title="存储引擎"></a>存储引擎</h2><p>数据库存储引擎是数据库底层软件组织,数据库管理系统(DBMS)使用数据引擎进行创建、查询、更新和删除数据。不同的存储引擎提供不同的存储机制、索引技巧、锁定水平等功能,使用不同的存储引擎,还可以 获得特定的功能。现在许多不同的数据库管理系统都支持多种不同的数据引擎。MySql的核心就是插件式存储引擎。</p><p>查看引擎:</p><div class="hljs"><pre><code class="hljs sql"><span class="hljs-keyword">SHOW</span> <span class="hljs-keyword">ENGINES</span></code></pre></div><p>查看默认引擎:</p><div class="hljs"><pre><code class="hljs sql"><span class="hljs-keyword">SHOW</span> <span class="hljs-keyword">VARIABLES</span> <span class="hljs-keyword">LIKE</span> <span class="hljs-string">'storage_engine'</span>;</code></pre></div><h3 id="InnoDB存储引擎"><a href="#InnoDB存储引擎" class="headerlink" title="InnoDB存储引擎"></a>InnoDB存储引擎</h3><p>InnoDB是事务型数据库的首选引擎,支持事务安全表(ACID),支持行锁定和外键。</p><p>InnoDB是默认的MySQL引擎。</p><p>InnoDB 主要特点:</p><ol><li><p>为MySQL提供了具有提交、回滚和崩溃恢复能力的事务安全(ACID兼容)存储引擎。InnoDB锁定在行级并且也在SELECT语句中提供一个类似Oracle的非锁定读。这些功能增加了多用户部署和性能。在SQL查询中,可以自由地将InnoDB类型的表和其他MySQL的表类型混合起来,甚至在同一个查询中也可以混合</p></li><li><p>InnoDB存储引擎为在主内存中缓存数据和索引而维持它自己的缓冲池。InnoDB将它的表和索引在一个逻辑表空间中,表空间可以包含数个文件(或原始磁盘文件)。这与MyISAM表不同,比如在MyISAM表中每个表被存放在分离的文件中。InnoDB表可以是任何尺寸,即使在文件尺寸被限制为2GB的操作系统上</p></li><li><p>InnoDB支持外键完整性约束,存储表中的数据时,每张表的存储都按主键顺序存放,如果没有显示在表定义时指定主键,InnoDB会为每一行生成一个6字节的ROWID,并以此作为主键</p></li><li><p>InnoDB是为处理巨大数据量的最大性能设计。它的CPU效率可能是任何其他基于磁盘的关系型数据库引擎锁不能匹敌的</p></li></ol><p>使用 InnoDB存储引擎 MySQL将在数据目录下创建一个名为 ibdata1 的10MB大小的自动扩展数据文件,以及两个名为ib_logfile0和ib_logfile1的5MB大小的日志文件</p><p>虽然InnoDB也使用B+Tree作为索引结构,但具体实现方式却与MyISAM截然不同。<br>第一个重大区别是InnoDB的数据文件本身就是索引文件。MyISAM索引文件和数据文件是分离的,索引文件仅保存数据记录的地址。而在InnoDB中,表数据文件本身就是按B+Tree组织的一个索引结构,这棵树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引。</p><p>叶节点包含了完整的数据记录。这种索引叫做聚集索引。因为InnoDB的数据文件本身 要按主键聚集,所以InnoDB要求表必须有主键(MyISAM可以没有),如果没有显式指定,则MySQL系统会自动选择一个可以唯一标识数据记录的列 作为主键,如果不存在这种列,则MySQL自动为InnoDB表生成一个隐含字段作为主键,这个字段长度为6个字节,类型为长整形。</p><p>第二个与MyISAM索引的不同是InnoDB的辅助索引data域存储相应记录主键的值而不是地址。换句话说,InnoDB的所有辅助索引都引用主键作为data域.</p><p>聚集索引这种实现方式使得按主键的搜索十分高效,但是辅助索引搜索需要检索两遍索引:首先检索辅助索引获得主键,然后用主键到主索引中检索获得记录。<br>了 解不同存储引擎的索引实现方式对于正确使用和优化索引都非常有帮助,例如知道了InnoDB的索引实现后,就很容易明白为什么不建议使用过长的字段作为 主键,因为所有辅助索引都引用主索引,过长的主索引会令辅助索引变得过大。再例如,用非单调的字段作为主键在InnoDB中不是个好主意,因为 InnoDB数据文件本身是一颗B+Tree,非单调的主键会造成在插入新记录时数据文件为了维持B+Tree的特性而频繁的分裂调整,十分低效,而使用 自增字段作为主键则是一个很好的选择。</p><h3 id="MyISAM存储引擎"><a href="#MyISAM存储引擎" class="headerlink" title="MyISAM存储引擎"></a>MyISAM存储引擎</h3><p>MyISAM基于ISAM存储引擎,并对其进行扩展。它是在Web、数据仓储和其他应用环境下最常使用的存储引擎之一。MyISAM拥有较高的插入、查询速度,但不支持事物。</p><p>MyISAM主要特性:</p><ol><li>被大文件系统和操作系统支持</li><li>当把删除和更新及插入操作混合使用的时候,动态尺寸的行产生更少碎片。这要通过合并相邻被删除的块,若下一个块被删除,就扩展到下一块自动完成</li><li>每个MyISAM表最大索引数是64,这可以通过重新编译来改变。每个索引最大的列数是16</li><li>最大的键长度是1000字节,这也可以通过编译来改变,对于键长度超过250字节的情况,一个超过1024字节的键将被用上</li><li>BLOB和TEXT列可以被索引</li><li>NULL被允许在索引的列中,这个值占每个键的0~1个字节</li><li>所有数字键值以高字节优先被存储以允许一个更高的索引压缩</li><li>每个MyISAM类型的表都有一个AUTO_INCREMENT的内部列,当INSERT和UPDATE操作的时候该列被更新,同时AUTO_INCREMENT列将被刷新。所以说,MyISAM类型表的AUTO_INCREMENT列更 新比InnoDB类型的AUTO_INCREMENT更快</li><li>可以把数据文件和索引文件放在不同目录</li><li>每个字符列可以有不同的字符集</li><li>有VARCHAR的表可以固定或动态记录长度</li><li>VARCHAR和CHAR列可以多达64KB</li></ol><p>使用MyISAM引擎创建数据库,将产生3个文件。文件的名字以表名字开始,扩展名之处文件类型:frm文件存储表定义、数据文件的扩展名为.MYD(MYData)、索引文件的扩展名时.MYI(MYIndex)</p><p>MyISAM引擎使用B+Tree作为索引结构,叶节点的data域存放的是数据记录的地址。</p><p>在MyISAM中,主索引和辅助索引(Secondary key)在结构上没有任何区别,只是主索引要求key是唯一的,而辅助索引的key可以重复。</p><p>MyISAM中索引检索的算法为首先按照B+Tree搜索算法搜索索引,如果指定的Key存在,则取出其data域的值,然后以data域的值为地址,读取相应数据记录。<br>MyISAM的索引方式也叫做“非聚集”的,之所以这么称呼是为了与InnoDB的聚集索引区分。</p><h3 id="MEMORY存储引擎"><a href="#MEMORY存储引擎" class="headerlink" title="MEMORY存储引擎"></a>MEMORY存储引擎</h3><p>MEMORY存储引擎将表中的数据存储到内存中,未查询和引用其他表数据提供快速访问。</p><p>MEMORY主要特性:</p><ul><li>MEMORY表的每个表可以有多达32个索引,每个索引16列,以及500字节的最大键长度</li><li>MEMORY存储引擎执行HASH和BTREE缩影</li><li>可以在一个MEMORY表中有非唯一键值</li><li>MEMORY表使用一个固定的记录长度格式</li><li>MEMORY不支持BLOB或TEXT列</li><li>MEMORY支持AUTO_INCREMENT列和对可包含NULL值的列的索引</li><li>MEMORY表在所有客户端之间共享(就像其他任何非TEMPORARY表)</li><li>MEMORY表内存被存储在内存中,内存是MEMORY表和服务器在查询处理时的空闲中,创建的内部表共享</li><li>当不再需要MEMORY表的内容时,要释放被MEMORY表使用的内存,应该执行DELETE FROM或TRUNCATE TABLE,或者删除整个表(使用DROP TABLE)</li></ul><h3 id="存储引擎的选择"><a href="#存储引擎的选择" class="headerlink" title="存储引擎的选择"></a>存储引擎的选择</h3><p>在实际工作中,选择一个合适的存储引擎是一个比较复杂的问题。每种存储引擎都有自己的优缺点,不能笼统地说谁比谁好。但建议选择使用InnoDB</p><p>InnoDB 和 MyISAM之间的区别:</p><ul><li>InnoDB支持事物,而MyISAM不支持事物</li><li>InnoDB支持行级锁,而MyISAM支持表级锁</li><li>InnoDB支持MVCC, 而MyISAM不支持</li><li>InnoDB支持外键,而MyISAM不支持</li></ul><p>存储引擎对比:</p><table><thead><tr><th>特性</th><th>InnoDB</th><th>MyISAM</th><th>MEMORY</th></tr></thead><tbody><tr><td>事务安全</td><td>支持</td><td>无</td><td>无</td></tr><tr><td>存储限制</td><td>64TB</td><td>有</td><td>有</td></tr><tr><td>使用空间</td><td>高</td><td>低</td><td>低</td></tr><tr><td>内存使用</td><td>高</td><td>低</td><td>高</td></tr><tr><td>插入数据速度</td><td>慢</td><td>快</td><td>快</td></tr><tr><td>对外键的支持</td><td>支持</td><td>无</td><td>无</td></tr></tbody></table><p>InnoDB: 支持事务处理,支持外键,支持崩溃修复能力和并发控制。如果需要对事务的完整性要求比较高(比如银行),要求实现并发控制(比如售票),那选择InnoDB有很大的优势。如果需要频繁的更新、删除操作的数据库,也可以选择InnoDB,因为支持事务的提交(commit)和回滚(rollback)。</p><p>MyISAM: 插入数据快,空间和内存使用比较低。如果表主要是用于插入新记录和读出记录,那么选择MyISAM能实现处理高效率。如果应用的完整性、并发性要求比 较低,也可以使用。</p><p>MEMORY: 所有的数据都在内存中,数据的处理速度快,但是安全性不高。如果需要很快的读写速度,对数据的安全性要求较低,可以选择MEMOEY。它对表的大小有要求,不能建立太大的表。所以,这类数据库只使用在相对较小的数据库表。</p><p>注意:同一个数据库也可以使用多种存储引擎的表。如果一个表要求比较高的事务处理,可以选择InnoDB。这个数据库中可以将查询要求比较高的表选择MyISAM存储。如果该数据库需要一个用于查询的临时表,可以选择MEMORY存储引擎。</p><h2 id="调优"><a href="#调优" class="headerlink" title="调优"></a>调优</h2><h3 id="SQL调优"><a href="#SQL调优" class="headerlink" title="SQL调优"></a>SQL调优</h3><h4 id="建立合适的索引"><a href="#建立合适的索引" class="headerlink" title="建立合适的索引"></a>建立合适的索引</h4><p>一个最重要的原则是最左前缀原理,在提这个之前要先说下联合索引,MySQL中的索引可以以一定顺序引用多个列,这种索引叫做联合索引,一般的,一个联合索引是一个有序元组<a1, a2, …, an>,其中各个元素均为数据表的一列。另外,单列索引可以看成联合索引元素数为1的特例。</p><p>索引匹配的最左原则具体是说,假如索引列分别为A,B,C,顺序也是A,B,C:</p><ul><li>那么查询的时候,如果查询【A】【A,B】 【A,B,C】,那么可以通过索引查询</li><li>如果查询的时候,采用【A,C】,那么C这个虽然是索引,但是由于中间缺失了B,因此C这个索引是用不到的,只能用到A索引</li><li>如果查询的时候,采用【B】 【B,C】 【C】,由于没有用到第一列索引,不是最左前缀,那么后面的索引也是用不到了</li><li>如果查询的时候,采用范围查询,并且是最左前缀,也就是第一列索引,那么可以用到索引,但是范围后面的列无法用到索引</li></ul><p>因为索引虽然加快了查询速度,但索引也是有代价的:索引文件本身要消耗存储空间,同时索引会加重插入、删除和修改记录时的负担,另外,MySQL在运行时也要消耗资源维护索引,因此索引并不是越多越好</p><p>在使用InnoDB存储引擎时,如果没有特别的需要,请永远使用一个与业务无关的自增字段作为主键。如果从数据库索引优化角度看,使用InnoDB引擎而不使用自增主键绝对是一个糟糕的主意。</p><h4 id="建立索引的常用技巧"><a href="#建立索引的常用技巧" class="headerlink" title="建立索引的常用技巧"></a>建立索引的常用技巧</h4><ol><li><p>最左前缀匹配原则,非常重要的原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。</p></li><li><p>=和in可以乱序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意顺序,mysql的查询优化器会帮你优化成索引可以识别的形式</p></li><li><p>尽量选择区分度高的列作为索引,区分度的公式是count(distinct col)/count(*),表示字段不重复的比例,比例越大我们扫描的记录数越少,唯一键的区分度是1,而一些状态、性别字段可能在大数据面前区分度就是0,那可能有人会问,这个比例有什么经验值吗?使用场景不同,这个值也很难确定,一般需要join的字段我们都要求是0.1以上,即平均1条扫描10条记录</p></li><li><p>索引列不能参与计算,保持列“干净”,比如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,原因很简单,b+树中存的都是数据表中的字段值,但进行检索时,需要把所有元素都应用函数才能比较,显然成本太大。所以语句应该写成create_time = unix_timestamp(’2014-05-29’);</p></li><li><p>尽量的扩展索引,不要新建索引。比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可,当然要考虑原有数据和线上使用情况</p></li></ol><h4 id="调优方法-流程"><a href="#调优方法-流程" class="headerlink" title="调优方法/流程"></a>调优方法/流程</h4><p>一般要进行SQL调优,那么就说有慢查询的SQL,系统或者server可以开启慢查询日志,尤其是线上系统,一般都会开启慢查询日志,如果有慢查询,可以通过日志来过滤。但是知道了有需要优化的SQL后,下面要做的就是如何进行调优</p><ol><li>先运行看看是否真的很慢,注意设置SQL_NO_CACHE</li><li>where条件单表查,锁定最小返回记录表。这句话的意思是把查询语句的where都应用到表中返回的记录数最小的表开始查起,单表每个字段分别查询,看哪个字段的区分度最高</li><li>explain查看执行计划,是否与1预期一致(从锁定记录较少的表开始查询)</li><li>order by limit 形式的sql语句让排序的表优先查</li><li>了解业务方使用场景</li><li>加索引时参照建索引的几大原则</li><li>观察结果,不符合预期继续从0分析</li></ol><h4 id="执行计划explain"><a href="#执行计划explain" class="headerlink" title="执行计划explain"></a>执行计划explain</h4><p>在日常工作中,我们有时会开慢查询去记录一些执行时间比较久的SQL语句,找出这些SQL语句并不意味着完事了,我们常常用到explain这个命令来查看一个这些SQL语句的执行计划,查看该SQL语句有没有使用上了索引,有没有做全表扫描,这都可以通过explain命令来查看。所以我们深入了解MySQL的基于开销的优化器,还可以获得很多可能被优化器考虑到的访问策略的细节,以及当运行SQL语句时哪种策略预计会被优化器采用。</p><p>使用explain 只需要在原有select 基础上加上explain关键字就可以了,如下:</p><div class="hljs"><pre><code class="hljs sql">mysql> explain select * from servers;+<span class="hljs-comment">----+-------------+---------+------+---------------+------+---------+------+------+-------+</span>| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |+<span class="hljs-comment">----+-------------+---------+------+---------------+------+---------+------+------+-------+</span>| 1 | SIMPLE | servers | ALL | NULL | NULL | NULL | NULL | 1 | NULL |+<span class="hljs-comment">----+-------------+---------+------+---------------+------+---------+------+------+-------+</span>1 row in <span class="hljs-keyword">set</span> (<span class="hljs-number">0.03</span> sec)</code></pre></div><p>简要解释下explain各个字段的含义</p><p>id : 表示SQL执行的顺序的标识,SQL从大到小的执行<br>select_type:表示查询中每个select子句的类型<br>table:显示这一行的数据是关于哪张表的,有时不是真实的表名字<br>type:表示MySQL在表中找到所需行的方式,又称“访问类型”。常用的类型有: ALL, index, range, ref, eq_ref, const, system, NULL(从左到右,性能从差到好)<br>possible_keys:指出MySQL能使用哪个索引在表中找到记录,查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询使用<br>Key:key列显示MySQL实际决定使用的键(索引),如果没有选择索引,键是NULL。<br>key_len:表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度(key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的)<br>ref:表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值<br>rows: 表示MySQL根据表统计信息及索引选用情况,估算的找到所需的记录所需要读取的行数,理论上行数越少,查询性能越好<br>Extra:该列包含MySQL解决查询的详细信息<br>EXPLAIN的特性</p><p>EXPLAIN不会告诉你关于触发器、存储过程的信息或用户自定义函数对查询的影响情况<br>EXPLAIN不考虑各种Cache<br>EXPLAIN不能显示MySQL在执行查询时所作的优化工作<br>部分统计信息是估算的,并非精确值<br>EXPALIN只能解释SELECT操作,其他操作要重写为SELECT后查看执行计划。</p><h3 id="主机调优"><a href="#主机调优" class="headerlink" title="主机调优"></a>主机调优</h3><p>内存选择cpu的4倍,磁盘选择ssd,关闭内存交换Swap。</p><h3 id="服务调优"><a href="#服务调优" class="headerlink" title="服务调优"></a>服务调优</h3><h3 id="索引覆盖"><a href="#索引覆盖" class="headerlink" title="索引覆盖"></a>索引覆盖</h3><p>explain的输出结果Extra字段为Using index时,能够触发索引覆盖。</p><h4 id="什么是回表"><a href="#什么是回表" class="headerlink" title="什么是回表"></a>什么是回表</h4><p>先定位主键值,再定位行记录,它的性能较扫一遍索引树更低。需要遍历两次索引。</p><p>通过建立联合索引可以避免回表,主要是常用的查询字段,否则带来负面插入降低。</p><h4 id="覆盖索引解决场景"><a href="#覆盖索引解决场景" class="headerlink" title="覆盖索引解决场景"></a>覆盖索引解决场景</h4><ol><li>全表count()优化,如count(name);</li><li>列表查询回表优化,通过联合索引,将需要查询的字段建立索引。即可以查询出对应的字段</li><li>分页面查询,将order by升级为联合索引</li></ol><h4 id="配置"><a href="#配置" class="headerlink" title="配置"></a>配置</h4><ul><li><p>innodb_buffer_pool_size: 这是安装完InnoDB后第一个应该设置的选项。缓冲池是数据和索引缓存的地方:这个值越大越好,这能保证你在大多数的读取操作时使用的是内存而不是硬盘。典型的值是5-6GB(8GB内存),20-25GB(32GB内存),100-120GB(128GB内存)。</p></li><li><p>innodb_log_file_size:这是redo日志的大小。redo日志被用于确保写操作快速而可靠并且在崩溃时恢复。一直到MySQL 5.1,它都难于调整,因为一方面你想让它更大来提高性能,另一方面你想让它更小来使得崩溃后更快恢复。幸运的是从MySQL 5.5之后,崩溃恢复的性能的到了很大提升,这样你就可以同时拥有较高的写入性能和崩溃恢复性能了。一直到MySQL 5.5,redo日志的总尺寸被限定在4GB(默认可以有2个log文件)。这在MySQL 5.6里被提高了。如果你知道你的应用程序需要频繁的写入数据并且你使用的时MySQL 5.6,你可以一开始就把它这是成4G。</p></li><li><p>max_connections:如果你经常看到‘Too many connections’错误,是因为max_connections的值太低了。这非常常见因为应用程序没有正确的关闭数据库连接,你需要比默认的151连接数更大的值。max_connection值被设高了(例如1000或更高)之后一个主要缺陷是当服务器运行1000个或更高的活动事务时会变的没有响应。在应用程序里使用连接池或者在MySQL里使用进程池有助于解决这一问题。</p></li></ul><p>innodb参数优化</p><div class="hljs"><pre><code class="hljs ini">default-storage-engineinnodb_buffer_pool_size # 没有固定大小,50%测试值,看看情况再微调。但是尽量设置不要超过物理内存70%<span class="hljs-attr">innodb_file_per_table</span>=(<span class="hljs-number">1</span>,<span class="hljs-number">0</span>)<span class="hljs-attr">innodb_flush_log_at_trx_commit</span>=(<span class="hljs-number">0</span>,<span class="hljs-number">1</span>,<span class="hljs-number">2</span>) <span class="hljs-comment"># 1是最安全的,0是性能***,2折中</span>binlog_sync<span class="hljs-attr">Innodb_flush_method</span>=(O_DIRECT, fdatasync)innodb_log_buffer_size # 100M以下innodb_log_file_size # 100M 以下innodb_log_files_in_group # 5个成员以下,一般2-3个够用(iblogfile0-N)innodb_max_dirty_pages_pct # 达到百分之75的时候刷写 内存脏页到磁盘。log_binmax_binlog_cache_size # 可以不设置max_binlog_size # 可以不设置innodb_additional_mem_pool_size #小于2G内存的机器,推荐值是20M。32G内存以上100M</code></pre></div>]]></content>
<categories>
<category>database</category>
</categories>
<tags>
<tag>mysql</tag>
</tags>
</entry>
<entry>
<title>Ansible简明指北</title>
<link href="/post/66cb4f6f.html"/>
<url>/post/66cb4f6f.html</url>
<content type="html"><![CDATA[<h2 id="Ansible简介"><a href="#Ansible简介" class="headerlink" title="Ansible简介"></a>Ansible简介</h2><p>我是在学习部署<a href="https://docs.okd.io/" target="_blank" rel="noopener">okd</a>与ansible相遇的,我从来没有特地去学习这么一款工具,只是需要的时候查阅一下官方文档,ansible真的足够简单,可以想使用命令行一样使用ansible,当然ansible也可以做一些不简单的事情。<br><a href="https://docs.ansible.com/ansible/latest/index.html" target="_blank" rel="noopener">Ansible</a>是一种IT自动化工具。它可以配置系统,部署软件以及协调更高级的IT任务,例如连续部署或零停机滚动更新。Ansible的主要目标是简单和易用。它还非常关注安全性和可靠性,其特点是活动部件最少,使用OpenSSH进行运输(使用其他运输方式和拉动模式作为替代方案)以及一种围绕人员(即使是不熟悉的人)可审核性设计的语言。</p><h2 id="Ansible概念"><a href="#Ansible概念" class="headerlink" title="Ansible概念"></a>Ansible概念</h2><h3 id="控制节点(Control-node)"><a href="#控制节点(Control-node)" class="headerlink" title="控制节点(Control node)"></a>控制节点(Control node)</h3><p>任何装有Ansible的机器。可以从任何控制节点调用/usr/bin/ansible或来运行命令和运行playbook /usr/bin/ansible-playbook。可以将任何安装了Python的计算机用作控制节点-笔记本电脑,共享台式机和服务器都可以运行Ansible。但是,不能将Windows计算机用作控制节点。您可以有多个控制节点。</p><h3 id="受控节点(Managed-nodes)"><a href="#受控节点(Managed-nodes)" class="headerlink" title="受控节点(Managed nodes)"></a>受控节点(Managed nodes)</h3><p>使用Ansible管理的网络设备(和/或服务器)。受管节点有时也称为“主机”。未在受管节点上安装Ansible。</p><h3 id="管理清单(Inventory)"><a href="#管理清单(Inventory)" class="headerlink" title="管理清单(Inventory)"></a>管理清单(Inventory)</h3><p>受控节点的列表。清单文件有时也称为“主机文件”。您的清单可以为每个受管节点指定信息,例如IP地址。库存还可以组织受管节点,创建和嵌套组以便于扩展。默认是使用<code>/etc/ansible/hosts</code> 文件作为管理清单文件。在运行时可以通过<code>-i</code> 自定义hosts地址。详细请参考:<a href="https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html#intro-inventory" target="_blank" rel="noopener">管理清单</a></p><h3 id="模块(Modules)"><a href="#模块(Modules)" class="headerlink" title="模块(Modules)"></a>模块(Modules)</h3><p>将执行代码单元Ansible。从管理特定类型的数据库上的用户到管理特定类型的网络设备上的VLAN接口,每个模块都有特定的用途。您可以通过ansible命令调用单个模块,也可以在playbook中调用多个不同的模块。详细请参考:<a href="https://docs.ansible.com/ansible/latest/modules/modules_by_category.html#modules-by-category" target="_blank" rel="noopener">所有模块</a></p><h3 id="任务(Tasks)"><a href="#任务(Tasks)" class="headerlink" title="任务(Tasks)"></a>任务(Tasks)</h3><p>Ansible中的行动单位。您可以使用临时命令一次执行一个任务。</p><h3 id="PlayBook"><a href="#PlayBook" class="headerlink" title="PlayBook"></a>PlayBook</h3><p>已保存任务的有序列表,因此您可以按此顺序重复运行这些任务。剧本可以包括变量以及任务。剧本采用YAML编写,易于阅读,编写,共享和理解。要了解有关剧本的更多信息,请参阅<a href="https://docs.ansible.com/ansible/latest/user_guide/playbooks_intro.html#about-playbooks" target="_blank" rel="noopener">关于Playbook</a></p><h2 id="安装Ansible"><a href="#安装Ansible" class="headerlink" title="安装Ansible"></a>安装Ansible</h2><p>ansible的安装非常简单,CentOS下安装:<code>yum install -y ansible</code>,Ubantu下安装: <code>sudo apt update && sudo apt install software-properties-common && sudo apt-add-repository --yes --update ppa:ansible/ansible && sudo apt install ansible</code> 更多系统安装ansible请参考<a href="https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html#" target="_blank" rel="noopener">Installing Ansible</a></p><h3 id="配置文件"><a href="#配置文件" class="headerlink" title="配置文件"></a>配置文件</h3><p>ansible配置文件有两个<code>ansible.cfg</code> 和 <code>hosts</code>,前者为ansible软件配置,如常见的第一次访问host_key_checking,连接超时时间。后者为批量执行的受控机配置和变量设置。</p><h4 id="ansible-cfg"><a href="#ansible-cfg" class="headerlink" title="ansible.cfg"></a>ansible.cfg</h4><p>ansible.cfg 配置文件优先级:</p><ol><li>ANSIBLE_CONFIG (环境变量配置)</li><li>ansible.cfg(当前目录,存在风险,不会主动加载)</li><li>~/.ansible.cfg(当前用户主目录)</li><li>/etc/ansible/ansible.cfg</li></ol><p>ansible会安装以上列表并使用第一个找到的配置文件,可以通过<code>ansible-config list</code>和<code>ansible-config dump --only-changed</code>查看ansible配置情况</p><p>常用的<code>ansible.cfg</code>文件内容如下:</p><div class="hljs"><pre><code class="hljs ini"><span class="hljs-section">[defaults]</span><span class="hljs-attr">log_path</span> = /var/log/ansible.log<span class="hljs-attr">forks</span> = <span class="hljs-number">20</span><span class="hljs-attr">host_key_checking</span> = <span class="hljs-literal">False</span><span class="hljs-attr">retry_files_enabled</span> = <span class="hljs-literal">False</span><span class="hljs-attr">deprecation_warnings</span> = <span class="hljs-literal">False</span><span class="hljs-attr">nocows</span> = <span class="hljs-literal">True</span><span class="hljs-attr">remote_user</span> = root<span class="hljs-attr">roles_path</span> = roles/<span class="hljs-attr">gathering</span> = smart<span class="hljs-attr">fact_caching</span> = jsonfile<span class="hljs-attr">fact_caching_connection</span> = /etc/ansible/facts<span class="hljs-attr">fact_caching_timeout</span> = <span class="hljs-number">600</span><span class="hljs-attr">callback_whitelist</span> = profile_tasks<span class="hljs-attr">inventory_ignore_extensions</span> = secrets.py, .pyc, .cfg, .crt, .ini<span class="hljs-attr">timeout</span> = <span class="hljs-number">30</span><span class="hljs-section">[inventory]</span><span class="hljs-attr">unparsed_is_failed</span>=<span class="hljs-literal">true</span><span class="hljs-section">[ssh_connection]</span><span class="hljs-attr">pipelining</span> = <span class="hljs-literal">True</span><span class="hljs-attr">ssh_args</span> = -o ControlMaster=auto -o ControlPersist=<span class="hljs-number">600</span>s<span class="hljs-attr">timeout</span> = <span class="hljs-number">10</span><span class="hljs-attr">control_path</span> = %(directory)s/%%h-%%r</code></pre></div><p>以上配置文件,主要配置了日志路径、远程用户信息、超时时间等。更多信息请参考:<a href="https://github.com/ansible/ansible/blob/devel/examples/ansible.cfg" target="_blank" rel="noopener">ansible.cfg example</a>和<a href="https://docs.ansible.com/ansible/latest/reference_appendices/config.html#ansible-configuration-settings" target="_blank" rel="noopener">ansible configuration</a>。</p><h4 id="hosts"><a href="#hosts" class="headerlink" title="hosts"></a>hosts</h4><p>管理清单(host文件)默认在<code>/etc/ansible/hosts</code>,可以通过命令行 <code>-i <path></code>参数进行指定,<code>-i</code>参数可以多次指定,也就是说,ansible支持多个hosts文件同时使用。host文件支持<code>ini</code>和<code>yaml</code>两种格式。</p><p>ini文件:</p><div class="hljs"><pre><code class="hljs ini">mail.example.com<span class="hljs-section">[webservers]</span>foo.example.combar.example.com<span class="hljs-section">[dbservers]</span>one.example.comtwo.example.comthree.example.com</code></pre></div><p>yaml格式:</p><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-attr">all:</span> <span class="hljs-attr">hosts:</span> <span class="hljs-attr">mail.example.com:</span> <span class="hljs-attr">children:</span> <span class="hljs-attr">webservers:</span> <span class="hljs-attr">hosts:</span> <span class="hljs-attr">foo.example.com:</span> <span class="hljs-attr">bar.example.com:</span> <span class="hljs-attr">two.example.com:</span> <span class="hljs-attr">dbservers:</span> <span class="hljs-attr">hosts:</span> <span class="hljs-attr">one.example.com:</span> <span class="hljs-attr">two.example.com:</span> <span class="hljs-attr">three.example.com:</span></code></pre></div><p>这两个文件的内容是等价的,当你需要多级结构时,推荐使用yaml,否则ini将更加方便。host文件中存在2个默认组<code>all</code>和<code>ungrouped</code>。前者包含了每一个主机,后者则是除了<code>all</code>没有分组的。如示例中的<code>mail.expample.com</code>,一个host可以存在于多个组里面,如<code>two.example.com:</code></p><p>ansible 支持添加一个范围的host:</p><p>在INI文件中:</p><div class="hljs"><pre><code class="hljs ini"><span class="hljs-section">[webservers]</span>www[01:50].example.com</code></pre></div><p>在yaml文件中:</p><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-attr">webservers:</span> <span class="hljs-attr">hosts:</span> <span class="hljs-string">www[01:50].example.com:</span></code></pre></div><p>表示1-50的主机,此外host文件支持字符顺序如:</p><div class="hljs"><pre><code class="hljs ini"><span class="hljs-section">[databases]</span>db-[a:f].example.com</code></pre></div><p>host文件支持添加变量,如:</p><p>在INI文件中:</p><div class="hljs"><pre><code class="hljs ini"><span class="hljs-section">[atlanta]</span>host1 http_port=80 maxRequestsPerChild=808host2 http_port=303 maxRequestsPerChild=909</code></pre></div><p>在YAML文件中:</p><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-attr">atlanta:</span> <span class="hljs-attr">host1:</span> <span class="hljs-attr">http_port:</span> <span class="hljs-number">80</span> <span class="hljs-attr">maxRequestsPerChild:</span> <span class="hljs-number">808</span> <span class="hljs-attr">host2:</span> <span class="hljs-attr">http_port:</span> <span class="hljs-number">303</span> <span class="hljs-attr">maxRequestsPerChild:</span> <span class="hljs-number">909</span></code></pre></div><p>对于一些像非标准的ssh端口,可以在hostname后面添加port:</p><div class="hljs"><pre><code class="hljs ini">badwolf.example.com:5309</code></pre></div><p>但是ansible可以通过openssh的连接去找到并使用这些端口。</p><p>连接的变量也可以通过host变量描述:</p><div class="hljs"><pre><code class="hljs ini">localhost ansible_connection=localother1.example.com ansible_connection=ssh ansible_user=myuserother2.example.com ansible_connection=ssh ansible_user=myotheruser</code></pre></div><p>host文件支持别名</p><p>INI文件:</p><div class="hljs"><pre><code class="hljs ini">jumper ansible_port=5555 ansible_host=192.0.2.50</code></pre></div><p>yaml文件</p><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-attr">hosts:</span> <span class="hljs-attr">jumper:</span> <span class="hljs-attr">ansible_port:</span> <span class="hljs-number">5555</span> <span class="hljs-attr">ansible_host:</span> <span class="hljs-number">192.0</span><span class="hljs-number">.2</span><span class="hljs-number">.50</span></code></pre></div><p>在命令行或者playbook文件中可以通过别名使用对应的host,在上面的例子中指定了端口号。</p><p>host支持组变量,相当于每一个host都继承了组变量:</p><p>INI文件:</p><div class="hljs"><pre><code class="hljs ini"><span class="hljs-section">[atlanta]</span>host1host2<span class="hljs-section">[atlanta:vars]</span><span class="hljs-attr">ntp_server</span>=ntp.atlanta.example.com<span class="hljs-attr">proxy</span>=proxy.atlanta.example.com</code></pre></div><p>YAML文件:</p><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-attr">atlanta:</span> <span class="hljs-attr">hosts:</span> <span class="hljs-attr">host1:</span> <span class="hljs-attr">host2:</span> <span class="hljs-attr">vars:</span> <span class="hljs-attr">ntp_server:</span> <span class="hljs-string">ntp.atlanta.example.com</span> <span class="hljs-attr">proxy:</span> <span class="hljs-string">proxy.atlanta.example.com</span></code></pre></div><p>不过这会导致一个问题:一个host在多个组,且多个组都设置同一个变量。这将引起冲突,ansible将按照字符顺序选择最后一个(尽量避免这种问题出现),ansible支持通过<code>ansible_group_priority</code>修改优先级,默认值为1。具体规则参考 <a href="https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html#how-we-merge" target="_blank" rel="noopener">rules for merging</a></p><p>此外,host文件支持多重组变量集成,通过<code>:children</code>或<code>children:</code>分别在INI和YAML来使用:</p><p>INI文件:</p><div class="hljs"><pre><code class="hljs ini"><span class="hljs-section">[atlanta]</span>host1host2<span class="hljs-section">[raleigh]</span>host2host3<span class="hljs-section">[southeast:children]</span>atlantaraleigh<span class="hljs-section">[southeast:vars]</span><span class="hljs-attr">some_server</span>=foo.southeast.example.com<span class="hljs-attr">halon_system_timeout</span>=<span class="hljs-number">30</span><span class="hljs-attr">self_destruct_countdown</span>=<span class="hljs-number">60</span><span class="hljs-attr">escape_pods</span>=<span class="hljs-number">2</span><span class="hljs-section">[usa:children]</span>southeastnortheastsouthwestnorthwest</code></pre></div><p>YAML文件:</p><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-attr">all:</span> <span class="hljs-attr">children:</span> <span class="hljs-attr">usa:</span> <span class="hljs-attr">children:</span> <span class="hljs-attr">southeast:</span> <span class="hljs-attr">children:</span> <span class="hljs-attr">atlanta:</span> <span class="hljs-attr">hosts:</span> <span class="hljs-attr">host1:</span> <span class="hljs-attr">host2:</span> <span class="hljs-attr">raleigh:</span> <span class="hljs-attr">hosts:</span> <span class="hljs-attr">host2:</span> <span class="hljs-attr">host3:</span> <span class="hljs-attr">vars:</span> <span class="hljs-attr">some_server:</span> <span class="hljs-string">foo.southeast.example.com</span> <span class="hljs-attr">halon_system_timeout:</span> <span class="hljs-number">30</span> <span class="hljs-attr">self_destruct_countdown:</span> <span class="hljs-number">60</span> <span class="hljs-attr">escape_pods:</span> <span class="hljs-number">2</span> <span class="hljs-attr">northeast:</span> <span class="hljs-attr">northwest:</span> <span class="hljs-attr">southwest:</span></code></pre></div><p>hosts文件支持变量文件的添加,分别对应<code>host</code>变量和<code>group_vars</code>,hosts的变量文件存放位置可以在<code>/etc/ansible/group_vars/</code>和<code>/etc/ansible/host_vars/</code>,如:</p><div class="hljs"><pre><code class="hljs path">/etc/ansible/group_vars/raleigh # can optionally end in '.yml', '.yaml', or '.json'/etc/ansible/group_vars/webservers/etc/ansible/host_vars/foosball</code></pre></div><p>同样ansible支持以组名作为目录:</p><div class="hljs"><pre><code class="hljs code">/etc/ansible/group_vars/raleigh/db_settings/etc/ansible/group_vars/raleigh/cluster_settings</code></pre></div><p>ansible也支持多个hosts文件在指定目录里:</p><div class="hljs"><pre><code class="hljs code">inventory/ openstack.yml # configure inventory plugin to get hosts from Openstack cloud dynamic-inventory.py # add additional hosts with dynamic inventory script static-inventory # add static hosts and groups group_vars/ all.yml # assign variables to all hosts</code></pre></div><p>然后运行:<code>ansible-playbook example.yml -i inventory</code></p><p>ansible 有很多内置变量,如: ssh连接变量,docker,local,详见:<a href="https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html#id17" target="_blank" rel="noopener">inventory parameters</a></p><p>hosts文件支持很多插件:<a href="https://docs.ansible.com/ansible/latest/plugins/inventory.html#inventory-plugins" target="_blank" rel="noopener">Inventory Plugins</a></p><p>hosts文件同样支持动态功能:<a href="https://docs.ansible.com/ansible/latest/user_guide/intro_dynamic_inventory.html#id3" target="_blank" rel="noopener">dynamic inventory</a></p><h2 id="控制模式"><a href="#控制模式" class="headerlink" title="控制模式"></a>控制模式</h2><p>ansible 在运行相关任务的时候,即通过<code>ansible</code>或者在编写ansible-playbook的文件,指定哪些受控主机需要执行任务的时候。可以通过以下通用模式指定:</p><table><thead><tr><th style="text-align:center">描述</th><th style="text-align:center">模式</th><th style="text-align:center">目标主机</th></tr></thead><tbody><tr><td style="text-align:center">所有主机</td><td style="text-align:center">all(*)</td><td style="text-align:center"></td></tr><tr><td style="text-align:center">一个主机</td><td style="text-align:center">host1</td><td style="text-align:center"></td></tr><tr><td style="text-align:center">多个主机</td><td style="text-align:center">host1:host2(host1,host2)</td><td style="text-align:center"></td></tr><tr><td style="text-align:center">一个分组</td><td style="text-align:center">ins</td><td style="text-align:center"></td></tr><tr><td style="text-align:center">分组并集</td><td style="text-align:center">ins:nodes</td><td style="text-align:center">所有在ins加上所有在nodes的主机</td></tr><tr><td style="text-align:center">分组差集</td><td style="text-align:center">ins:!nodes</td><td style="text-align:center">所有在ins但不在nodes的主机</td></tr><tr><td style="text-align:center">分组交集</td><td style="text-align:center">ins:&nodes</td><td style="text-align:center">所有在ins也在nodes的主机</td></tr></tbody></table><p>可以像这样:<code>ins:nodes:&infra:!master</code>,表示ins和nodes并且都在infra且不是master的hosts<br>受控模式同样支持通配符:</p><div class="hljs"><pre><code class="hljs code">192.0.\*\*.example.com\*.comone*.com:dbservers</code></pre></div><p>在模式中使用变量,可以在 <code>ansible-playbook</code>使用<code>-e</code>注入变量</p><p><code>webservers:!:&</code></p><p>在模式中使用数组角标,比如存在ins:</p><div class="hljs"><pre><code class="hljs INI"><span class="hljs-section">[ins]</span>masterinfranodes</code></pre></div><p>可以通过数组角标来指定host:</p><div class="hljs"><pre><code class="hljs code">ins[0] # masterins[-1] # nodesins[0:2] # ini[0],ins[1]ins[1:] # infra nodesins[:3] # master,infra,nodes</code></pre></div><p>在模式中使用正则匹配,以<code>~</code>开头</p><p><code>~(web|db).*\.example\.com</code></p><h2 id="临时命令(命令行)"><a href="#临时命令(命令行)" class="headerlink" title="临时命令(命令行)"></a>临时命令(命令行)</h2><p>ansible拥有开箱即用的功能,只要配置好了上面两个文件运行命令行<code>ansible all -m ping</code> 即可以查看各受控主机的状态。</p><p>临时命令用于快速批量执行命令:</p><div class="hljs"><pre><code class="hljs shell">ansible [模式] -m [模块] -a "[模块参数]"</code></pre></div><p>ansible在不使用<code>-m</code> 指定模块时,默认使用command模块,默认情况下ansible使用5个并发进程,当你想指定进程数时可以通过<code>-f</code> ,通过<code>-u</code> 指定执行用户,通过<code>--become</code>提升用户权限:</p><div class="hljs"><pre><code class="hljs sh">ansible atlanta -a <span class="hljs-string">"/sbin/reboot"</span> -f 10 -u username --become 。</code></pre></div><h3 id="常见模块"><a href="#常见模块" class="headerlink" title="常见模块"></a>常见模块</h3><h4 id="管理文件"><a href="#管理文件" class="headerlink" title="管理文件"></a>管理文件</h4><ol><li><p>复制文件:<code>ansible ins -m copy -a "src=/etc/hosts dest=/tmp/hosts`</code></p></li><li><p>创建文件: <code>ansible ins -m file -a "dest=/srv/foo/b.txt mode=600 owner=bryce group=bryce"</code></p></li><li><p>创建目录:<code>ansible ins -m file -a "dest=/path/to/c mode=755 owner=bryce group=bryce state=directory"</code></p></li><li><p>删除文件:<code>ansible ins -m file -a "dest=/path/to/c state=absent"</code></p></li></ol><h4 id="管理包"><a href="#管理包" class="headerlink" title="管理包"></a>管理包</h4><ol><li>安装包:<code>ansible ins -m yum -a "name=java-1.8.0-openjdk state=present"</code></li><li>确保包为最新:<code>ansible ins -m yum -a "name=java state=latest"</code></li><li>确保未安装包:<code>ansible webservers -m yum -a "name=java state=absent"</code></li></ol><h4 id="管理用户和组"><a href="#管理用户和组" class="headerlink" title="管理用户和组"></a>管理用户和组</h4><ol><li>创建用户:<code>ansible all -m user -a "name=bryce password=<crypted password here>"</code></li><li>删除用户:<code>ansible all -m user -a "name=bryce state=absent"</code></li></ol><h4 id="管理服务"><a href="#管理服务" class="headerlink" title="管理服务"></a>管理服务</h4><ol><li>启动服务:<code>ansible ins -m service -a "name=httpd state=started"</code></li><li>停止服务: <code>ansible ins -m service -a "name=httpd state=stopped"</code></li></ol><h4 id="收集信息"><a href="#收集信息" class="headerlink" title="收集信息"></a>收集信息</h4><p><code>ansible all -m setup</code></p><h2 id="Playbooks"><a href="#Playbooks" class="headerlink" title="Playbooks"></a>Playbooks</h2><p>playbooks是ansible的配置,部署和编排语言。真正具有生产力的搬砖工具。</p><p>在介绍playbook之前,我们先来看看一个playbook的文档结构</p><p>这是我一个安装ElasticSearch的简单例子:</p><div class="hljs"><pre><code class="hljs tree">.├── elasticsearch.yml├── roles│ ├── after_install│ │ └── tasks│ │ └── main.yml│ ├── certs│ │ └── tasks│ │ └── main.yml│ ├── elasticsearch│ │ ├── tasks│ │ │ ├── config_elasticsearch.yml│ │ │ ├── install_elasticsearch.yml│ │ │ ├── main.yml│ │ │ └── redhat.yml│ │ └── templates│ │ ├── elasticsearch│ │ ├── elasticsearch.yml│ │ └── jvm.options│ └── user│ └── tasks│ └── main.yml└── vars └── elasticsearch.yml</code></pre></div><p>包含了运行主文件elasticsearch.yml、roles文件夹、vars变量文件夹。roles文件包含了多个模块的任务,其中elasticsearch用到了模板。</p><h3 id="基础"><a href="#基础" class="headerlink" title="基础"></a>基础</h3><h4 id="hosts和users"><a href="#hosts和users" class="headerlink" title="hosts和users"></a>hosts和users</h4><p>每一个playbook都需要声明hosts和user,用户可以在每一个task上指定也可以在全局指定。<code>become</code>用于提升权限</p><div class="hljs"><pre><code class="hljs yml"><span class="hljs-meta">---</span><span class="hljs-bullet">-</span> <span class="hljs-attr">hosts:</span> <span class="hljs-string">webservers</span> <span class="hljs-attr">remote_user:</span> <span class="hljs-string">root</span> <span class="hljs-attr">tasks:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">test</span> <span class="hljs-string">connection</span> <span class="hljs-attr">ping:</span> <span class="hljs-attr">remote_user:</span> <span class="hljs-string">yourname</span> <span class="hljs-attr">become:</span> <span class="hljs-literal">yes</span></code></pre></div><h4 id="tasks列表"><a href="#tasks列表" class="headerlink" title="tasks列表"></a>tasks列表</h4><p>在上面的<code>tasks:</code>表示一系列需要执行的任务,如:</p><div class="hljs"><pre><code class="hljs yml"><span class="hljs-attr">tasks:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">enable</span> <span class="hljs-string">selinux</span> <span class="hljs-attr">command:</span> <span class="hljs-string">/sbin/setenforce</span> <span class="hljs-number">1</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">run</span> <span class="hljs-string">this</span> <span class="hljs-string">command</span> <span class="hljs-string">and</span> <span class="hljs-string">ignore</span> <span class="hljs-string">the</span> <span class="hljs-string">result</span> <span class="hljs-attr">shell:</span> <span class="hljs-string">/usr/bin/somecommand</span> <span class="hljs-attr">ignore_errors:</span> <span class="hljs-literal">True</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Copy</span> <span class="hljs-string">ansible</span> <span class="hljs-string">inventory</span> <span class="hljs-string">file</span> <span class="hljs-string">to</span> <span class="hljs-string">client</span> <span class="hljs-attr">copy:</span> <span class="hljs-string">src=/etc/ansible/hosts</span> <span class="hljs-string">dest=/etc/ansible/hosts</span> <span class="hljs-string">owner=root</span> <span class="hljs-string">group=root</span> <span class="hljs-string">mode=0644</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">create</span> <span class="hljs-string">a</span> <span class="hljs-string">foo.conf</span> <span class="hljs-string">file</span> <span class="hljs-attr">template:</span> <span class="hljs-attr">src:</span> <span class="hljs-string">templates/foo.j2</span> <span class="hljs-attr">dest:</span> <span class="hljs-string">/etc/foo.conf</span></code></pre></div><h3 id="变量"><a href="#变量" class="headerlink" title="变量"></a>变量</h3><p>在playbook任何地方都可以使用两个花括号<code>{{}}</code>注入变量,但是需要注意yaml语法陷阱,在yaml使用变量时,需要将变量所在位置连续字符用<code>""</code>包裹。</p><h4 id="变量的定义"><a href="#变量的定义" class="headerlink" title="变量的定义"></a>变量的定义</h4><p>变量可以定义在hosts文件中定义在playbook中,也可以引用变量文件:</p><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-bullet">-</span> <span class="hljs-attr">hosts:</span> <span class="hljs-string">ins</span> <span class="hljs-attr">vars:</span> <span class="hljs-attr">http_port:</span> <span class="hljs-number">80</span> <span class="hljs-attr">vars_files:</span> <span class="hljs-bullet">-</span> <span class="hljs-string">/vars/external_vars.yml</span></code></pre></div><p>也可以在任务中注册变量:</p><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-bullet">-</span> <span class="hljs-attr">hosts:</span> <span class="hljs-string">ins</span> <span class="hljs-attr">tasks:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">shell:</span> <span class="hljs-string">/usr/bin/foo</span> <span class="hljs-attr">register:</span> <span class="hljs-string">foo_result</span> <span class="hljs-attr">ignore_errors:</span> <span class="hljs-literal">True</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">shell:</span> <span class="hljs-string">/usr/bin/bar</span> <span class="hljs-attr">when:</span> <span class="hljs-string">foo_result.rc</span> <span class="hljs-string">==</span> <span class="hljs-number">5</span></code></pre></div><p>ansible关键字不能定义为变量,关键字:</p><div class="hljs"><pre><code class="hljs code">add, append, as_integer_ratio, bit_length, capitalize, center, clear, conjugate, copy, count, decode, denominator, difference, difference_update, discard, encode, endswith, expandtabs, extend, find, format, fromhex, fromkeys, get, has_key, hex, imag, index, insert, intersection, intersection_update, isalnum, isalpha, isdecimal, isdigit, isdisjoint, is_integer, islower, isnumeric, isspace, issubset, issuperset, istitle, isupper, items, iteritems, iterkeys, itervalues, join, keys, ljust, lower, lstrip, numerator, partition, pop, popitem, real, remove, replace, reverse, rfind, rindex, rjust, rpartition, rsplit, rstrip, setdefault, sort, split, splitlines, startswith, strip, swapcase, symmetric_difference, symmetric_difference_update, title, translate, union, update, upper, values, viewitems, viewkeys, viewvalues, zfill</code></pre></div><h4 id="系统变量"><a href="#系统变量" class="headerlink" title="系统变量"></a>系统变量</h4><p>ansible有一些内置的变量<br><code></code></p><ol><li><p><code>ansible_facts</code>可以通过<code>ansible hostname -m setup</code> 查看,引用<code>{{ ansible_facts['devices']['xvda']['model'] }}</code></p></li><li><p><code>hostvars</code> 运行的主机变量</p></li><li><code>groups</code> 是hosts中所有组(和主机)的列表。这可用于枚举组中的所有主机。</li><li><code>group_names</code> 是当前主机所在的所有组的列表(数组)。</li><li><code>inventory_hostname</code> Ansible的hosts文件中配置的主机名的名称</li></ol><h3 id="模板"><a href="#模板" class="headerlink" title="模板"></a>模板</h3><p>ansible的模板使用的是Jinja2,具体可以参考<a href="https://docs.ansible.com/ansible/latest/user_guide/playbooks_templating.html" target="_blank" rel="noopener">模板(Jinja2)</a></p><p>使用了模板即可以,在使用template模块时,渲染模板文件。使得模板文件符合自己的要求</p><h4 id="常见的语法"><a href="#常见的语法" class="headerlink" title="常见的语法"></a>常见的语法</h4><p>循环:</p><div class="hljs"><pre><code class="hljs jinja2">[{% for host in groups[elasticsearch_hosts] %}'{{ host }}'{% if not loop.last %},{% endif %}{% endfor %}]</code></pre></div><p>判断:</p><div class="hljs"><pre><code class="hljs jinja2">{% if ip == inventory_hostname %}echo {{ loop.index - 1 }} > {{install_dir}}/{{data_dir}}/myid{% endif %}</code></pre></div><p>加减乘除:</p><div class="hljs"><pre><code class="hljs jinja2">{{(groups[elasticsearch_hosts] | length)//2+1 }} # 获取长度除2加1</code></pre></div><h3 id="条件执行"><a href="#条件执行" class="headerlink" title="条件执行"></a>条件执行</h3><p>条件执行可以在tasks上使用<code>when</code>:</p><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-attr">tasks:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">"shut down CentOS 6 and Debian 7 systems"</span> <span class="hljs-attr">command:</span> <span class="hljs-string">/sbin/shutdown</span> <span class="hljs-string">-t</span> <span class="hljs-string">now</span> <span class="hljs-attr">when:</span> <span class="hljs-string">(ansible_facts['distribution']</span> <span class="hljs-string">==</span> <span class="hljs-string">"CentOS"</span> <span class="hljs-string">and</span> <span class="hljs-string">ansible_facts['distribution_major_version']</span> <span class="hljs-string">==</span> <span class="hljs-string">"6"</span><span class="hljs-string">)</span> <span class="hljs-string">or</span> <span class="hljs-string">(ansible_facts['distribution']</span> <span class="hljs-string">==</span> <span class="hljs-string">"Debian"</span> <span class="hljs-string">and</span> <span class="hljs-string">ansible_facts['distribution_major_version']</span> <span class="hljs-string">==</span> <span class="hljs-string">"7"</span><span class="hljs-string">)</span></code></pre></div><p>when 支持多个关键字<code>in</code> <code>or</code> <code>==</code> <code>and</code> <code>is not defined</code> <code>!=</code></p><h3 id="循环"><a href="#循环" class="headerlink" title="循环"></a>循环</h3><p>用于多次重复执行任务</p><p>简单循环列表:</p><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">add</span> <span class="hljs-string">several</span> <span class="hljs-string">users</span> <span class="hljs-attr">user:</span> <span class="hljs-attr">name:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ item.name }}</span>"</span> <span class="hljs-attr">state:</span> <span class="hljs-string">present</span> <span class="hljs-attr">groups:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ item.groups }}</span>"</span> <span class="hljs-attr">loop:</span> <span class="hljs-bullet">-</span> <span class="hljs-string">{</span> <span class="hljs-attr">name:</span> <span class="hljs-string">'testuser1'</span><span class="hljs-string">,</span> <span class="hljs-attr">groups:</span> <span class="hljs-string">'wheel'</span> <span class="hljs-string">}</span> <span class="hljs-bullet">-</span> <span class="hljs-string">{</span> <span class="hljs-attr">name:</span> <span class="hljs-string">'testuser2'</span><span class="hljs-string">,</span> <span class="hljs-attr">groups:</span> <span class="hljs-string">'root'</span> <span class="hljs-string">}</span> <span class="hljs-comment"># 或者 loop: '{{vars}}'</span></code></pre></div><p>任务重试:</p><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-bullet">-</span> <span class="hljs-attr">shell:</span> <span class="hljs-string">/usr/bin/foo</span> <span class="hljs-attr">register:</span> <span class="hljs-string">result</span> <span class="hljs-attr">until:</span> <span class="hljs-string">result.stdout.find("all</span> <span class="hljs-string">systems</span> <span class="hljs-string">go")</span> <span class="hljs-string">!=</span> <span class="hljs-number">-1</span> <span class="hljs-attr">retries:</span> <span class="hljs-number">5</span> <span class="hljs-attr">delay:</span> <span class="hljs-number">10</span></code></pre></div><p>详情参考:<a href="https://docs.ansible.com/ansible/latest/user_guide/playbooks_loops.html" target="_blank" rel="noopener">loop</a></p><h3 id="阻塞块"><a href="#阻塞块" class="headerlink" title="阻塞块"></a>阻塞块</h3><p>块允许对任务进行逻辑分组以及进行中的错误处理。您可以应用于单个任务的大多数内容(循环除外)都可以应用于块级,这也使设置任务通用的数据或指令变得更加容易。这并不意味着该指令会影响块本身,而是被块所包含的任务继承。即,何时将应用于任务,而不是块本身.</p><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-attr">tasks:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Install,</span> <span class="hljs-string">configure,</span> <span class="hljs-string">and</span> <span class="hljs-string">start</span> <span class="hljs-string">Apache</span> <span class="hljs-attr">block:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">install</span> <span class="hljs-string">httpd</span> <span class="hljs-string">and</span> <span class="hljs-string">memcached</span> <span class="hljs-attr">yum:</span> <span class="hljs-attr">name:</span> <span class="hljs-bullet">-</span> <span class="hljs-string">httpd</span> <span class="hljs-bullet">-</span> <span class="hljs-string">memcached</span> <span class="hljs-attr">state:</span> <span class="hljs-string">present</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">apply</span> <span class="hljs-string">the</span> <span class="hljs-string">foo</span> <span class="hljs-string">config</span> <span class="hljs-string">template</span> <span class="hljs-attr">template:</span> <span class="hljs-attr">src:</span> <span class="hljs-string">templates/src.j2</span> <span class="hljs-attr">dest:</span> <span class="hljs-string">/etc/foo.conf</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">start</span> <span class="hljs-string">service</span> <span class="hljs-string">bar</span> <span class="hljs-string">and</span> <span class="hljs-string">enable</span> <span class="hljs-string">it</span> <span class="hljs-attr">service:</span> <span class="hljs-attr">name:</span> <span class="hljs-string">bar</span> <span class="hljs-attr">state:</span> <span class="hljs-string">started</span> <span class="hljs-attr">enabled:</span> <span class="hljs-literal">True</span> <span class="hljs-attr">when:</span> <span class="hljs-string">ansible_facts['distribution']</span> <span class="hljs-string">==</span> <span class="hljs-string">'CentOS'</span> <span class="hljs-attr">become:</span> <span class="hljs-literal">true</span> <span class="hljs-attr">become_user:</span> <span class="hljs-string">root</span> <span class="hljs-attr">ignore_errors:</span> <span class="hljs-literal">yes</span></code></pre></div><h3 id="常用技巧及模块"><a href="#常用技巧及模块" class="headerlink" title="常用技巧及模块"></a>常用技巧及模块</h3><p><a href="https://docs.ansible.com/ansible/latest/user_guide/playbooks_best_practices.html" target="_blank" rel="noopener">ansible-playbook的最佳实践</a></p><p>tasks支持很多模块如<code>command</code>、<code>shell</code>、<code>copy</code>、<code>template</code> 等<br>tasks 还有一个简单的写法:<code>action: template src=templates/foo.j2 dest=/etc/foo.conf</code></p><p>执行playbook:<code>ansible-playbook site.yml --limit datacenter2</code></p><h2 id="命令行工具"><a href="#命令行工具" class="headerlink" title="命令行工具"></a>命令行工具</h2><p>ansible 拥有多个命令行工具其中包括<code>ansible</code>和<code>ansible-playbook</code>。</p><h3 id="ansible"><a href="#ansible" class="headerlink" title="ansible"></a>ansible</h3><p>ansible参数:</p><div class="hljs"><pre><code class="hljs code">usage: ansible [-h] [--version] [-v] [-b] [--become-method BECOME_METHOD] [--become-user BECOME_USER] [-K] [-i INVENTORY] [--list-hosts] [-l SUBSET] [-P POLL_INTERVAL] [-B SECONDS] [-o] [-t TREE] [-k] [--private-key PRIVATE_KEY_FILE] [-u REMOTE_USER] [-c CONNECTION] [-T TIMEOUT] [--ssh-common-args SSH_COMMON_ARGS] [--sftp-extra-args SFTP_EXTRA_ARGS] [--scp-extra-args SCP_EXTRA_ARGS] [--ssh-extra-args SSH_EXTRA_ARGS] [-C] [--syntax-check] [-D] [-e EXTRA_VARS] [--vault-id VAULT_IDS] [--ask-vault-pass | --vault-password-file VAULT_PASSWORD_FILES] [-f FORKS] [-M MODULE_PATH] [--playbook-dir BASEDIR] [-a MODULE_ARGS] [-m MODULE_NAME] pattern</code></pre></div><p>详细信息:<a href="https://docs.ansible.com/ansible/latest/cli/ansible.html#id4" target="_blank" rel="noopener">ansible 常用选项</a></p><h3 id="ansible-config"><a href="#ansible-config" class="headerlink" title="ansible-config"></a>ansible-config</h3><p>详细信息:<a href="https://docs.ansible.com/ansible/latest/cli/ansible-config.html" target="_blank" rel="noopener">ansible-config</a></p><h3 id="ansible-console"><a href="#ansible-console" class="headerlink" title="ansible-console"></a>ansible-console</h3><p>详细信息:<a href="https://docs.ansible.com/ansible/latest/cli/ansible-console.html" target="_blank" rel="noopener">ansible-console</a></p><h3 id="ansible-doc"><a href="#ansible-doc" class="headerlink" title="ansible-doc"></a>ansible-doc</h3><p>详细信息:<a href="https://docs.ansible.com/ansible/latest/cli/ansible-doc.html" target="_blank" rel="noopener">ansible-doc</a></p><h3 id="ansible-galaxy"><a href="#ansible-galaxy" class="headerlink" title="ansible-galaxy"></a>ansible-galaxy</h3><p>用于管理共享库<br>详细信息:<a href="https://docs.ansible.com/ansible/latest/cli/ansible-galaxy.html" target="_blank" rel="noopener">ansible-galaxy</a></p><h3 id="ansible-inventory"><a href="#ansible-inventory" class="headerlink" title="ansible-inventory"></a>ansible-inventory</h3><p>用于管理hosts文件<br>详细信息:<a href="https://docs.ansible.com/ansible/latest/cli/ansible-inventory.html" target="_blank" rel="noopener">ansible-inventory</a></p><h3 id="ansible-playbook"><a href="#ansible-playbook" class="headerlink" title="ansible-playbook"></a>ansible-playbook</h3><p>用于运行ansible-playbook相关文件</p><p>常用参数:</p><div class="hljs"><pre><code class="hljs shell">usage: ansible-playbook [-h] [--version] [-v] [-k] [--private-key PRIVATE_KEY_FILE] [-u REMOTE_USER] [-c CONNECTION] [-T TIMEOUT] [--ssh-common-args SSH_COMMON_ARGS] [--sftp-extra-args SFTP_EXTRA_ARGS] [--scp-extra-args SCP_EXTRA_ARGS] [--ssh-extra-args SSH_EXTRA_ARGS] [--force-handlers] [--flush-cache] [-b] [--become-method BECOME_METHOD] [--become-user BECOME_USER] [-K] [-t TAGS] [--skip-tags SKIP_TAGS] [-C] [--syntax-check] [-D] [-i INVENTORY] [--list-hosts] [-l SUBSET] [-e EXTRA_VARS] [--vault-id VAULT_IDS] [--ask-vault-pass | --vault-password-file VAULT_PASSWORD_FILES] [-f FORKS] [-M MODULE_PATH] [--list-tasks] [--list-tags] [--step] [--start-at-task START_AT_TASK] playbook [playbook ...]</code></pre></div><p>详细信息:<a href="https://docs.ansible.com/ansible/latest/cli/ansible-playbook.html" target="_blank" rel="noopener">ansible-playbook</a></p><h3 id="ansible-pull"><a href="#ansible-pull" class="headerlink" title="ansible-pull"></a>ansible-pull</h3><p>建立远程副本<br>详细信息:<a href="https://docs.ansible.com/ansible/latest/cli/ansible-pull.html" target="_blank" rel="noopener">ansible-pull</a></p><h3 id="ansible-vault"><a href="#ansible-vault" class="headerlink" title="ansible-vault"></a>ansible-vault</h3><p>加密ansible文件<br>详细信息:<a href="https://docs.ansible.com/ansible/latest/cli/ansible-vault.html" target="_blank" rel="noopener">ansible-vault</a></p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>本文简单介绍了ansible的概念,使用方法以及相关注意事项。</p><p>参考文档:<a href="https://docs.ansible.com/" target="_blank" rel="noopener">Ansible is an IT automation tool</a></p>]]></content>
</entry>
<entry>
<title>kubernetes 安装</title>
<link href="/post/d0c3803f.html"/>
<url>/post/d0c3803f.html</url>
<content type="html"><![CDATA[<p><strong>kubernetes ubuntu安装部署</strong></p><h2 id="安装基础环境"><a href="#安装基础环境" class="headerlink" title="安装基础环境"></a><strong>安装基础环境</strong></h2><h3 id="配置系统hosts文件"><a href="#配置系统hosts文件" class="headerlink" title="配置系统hosts文件"></a>配置系统hosts文件</h3><div class="hljs"><pre><code class="hljs bash"> cat >> /etc/hosts <<EOF10.10.34.92 ukm0110.10.34.93 ukm0210.10.34.94 ukm0310.10.34.95 uki0110.10.34.96 uki0210.10.34.97 uki0310.10.34.98 ukn0110.10.34.99 ukn0210.10.34.100 ukn03EOF</code></pre></div><h3 id="配置主机名"><a href="#配置主机名" class="headerlink" title="配置主机名"></a>配置主机名</h3><div class="hljs"><pre><code class="hljs bash">hostnamectl <span class="hljs-built_in">set</span>-hostname ukm01</code></pre></div><h3 id="配置sshkey"><a href="#配置sshkey" class="headerlink" title="配置sshkey"></a>配置sshkey</h3><h4 id="创建sshkey"><a href="#创建sshkey" class="headerlink" title="创建sshkey"></a>创建sshkey</h4><div class="hljs"><pre><code class="hljs bash"> ssh-keygen -t dsa -b 1024 -C <span class="hljs-string">"[email protected]"</span>Generating public/private dsa key pair.Enter file <span class="hljs-keyword">in</span> <span class="hljs-built_in">which</span> to save the key (/root/.ssh/id_dsa):Enter passphrase (empty <span class="hljs-keyword">for</span> no passphrase):Enter same passphrase again:Your identification has been saved <span class="hljs-keyword">in</span> /root/.ssh/id_dsa.Your public key has been saved <span class="hljs-keyword">in</span> /root/.ssh/id_dsa.pub.The key fingerprint is:SHA256:LaO+CdmwvXlHaIPXvZf0s/XjKxPjVUdtvoHC0PCGEUs [email protected] key<span class="hljs-string">'s randomart image is:</span><span class="hljs-string">+---[DSA 1024]----+</span><span class="hljs-string">| E+ .|</span><span class="hljs-string">| ..=. +|</span><span class="hljs-string">| ooo .+ |</span><span class="hljs-string">| ..o . .+|</span><span class="hljs-string">| . .So... =|</span><span class="hljs-string">| *..=oo .o.o |</span><span class="hljs-string">| + +o o .o+o.|</span><span class="hljs-string">| o +. . .+oo+|</span><span class="hljs-string">| *o . .++*|</span><span class="hljs-string">+----[SHA256]-----+</span></code></pre></div><h4 id="分发key"><a href="#分发key" class="headerlink" title="分发key"></a>分发key</h4><div class="hljs"><pre><code class="hljs bash"> ssh-copy-id ukm01/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: <span class="hljs-string">"/root/.ssh/id_dsa.pub"</span>/usr/bin/ssh-copy-id: INFO: attempting to <span class="hljs-built_in">log</span> <span class="hljs-keyword">in</span> with the new key(s), to filter out any that are already installed/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- <span class="hljs-keyword">if</span> you are prompted now it is to install the new keysNumber of key(s) added: 1... ssh-copy-id ukn03...</code></pre></div><h4 id="校验"><a href="#校验" class="headerlink" title="校验"></a>校验</h4><div class="hljs"><pre><code class="hljs bash"> ssh ukm01Welcome to Ubuntu 18.04.3 LTS (GNU/Linux 4.15.0-88-generic x86_64) * Documentation: https://help.ubuntu.com * Management: https://landscape.canonical.com * Support: https://ubuntu.com/advantage System information disabled due to load higher than 4.0... ssh ukn03...</code></pre></div><h3 id="配置阿里云-repo"><a href="#配置阿里云-repo" class="headerlink" title="配置阿里云 repo"></a>配置阿里云 repo</h3><div class="hljs"><pre><code class="hljs bash"> mv /etc/apt/sources.list /etc/apt/sources.list.bak cat > /etc/apt/sources.list <<EOFdeb http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiversedeb-src http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiversedeb http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiversedeb-src http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiversedeb http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiversedeb-src http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiversedeb http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiversedeb-src http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiversedeb http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiversedeb-src http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverseEOF</code></pre></div><h3 id="安装基础包"><a href="#安装基础包" class="headerlink" title="安装基础包"></a>安装基础包</h3><p>ubuntu 18.04 默认未安装python,需要每台服务器手动安装。</p><div class="hljs"><pre><code class="hljs bash">apt update && apt install -y software-properties-common apt-transport-https ca-certificates gnupg2 python curl</code></pre></div><h2 id="安装-ansible"><a href="#安装-ansible" class="headerlink" title="安装 ansible"></a><strong>安装 ansible</strong></h2><p>只需在 master 节点安装</p><div class="hljs"><pre><code class="hljs bash">apt-add-repository --yes --update ppa:ansible/ansibleapt install -y ansible</code></pre></div><h3 id="配置-ansible-cfg"><a href="#配置-ansible-cfg" class="headerlink" title="配置 ansible.cfg"></a>配置 ansible.cfg</h3><div class="hljs"><pre><code class="hljs bash"> cat > /etc/ansible/ansible.cfg <<EOF[defaults]log_path = /var/<span class="hljs-built_in">log</span>/ansible.logforks = 20host_key_checking = Falseretry_files_enabled = Falsedeprecation_warnings = Falsenocows = Trueremote_user = rootroles_path = roles/gathering = smartfact_caching = jsonfilefact_caching_connection = /etc/ansible/factsfact_caching_timeout = 600callback_whitelist = profile_tasksinventory_ignore_extensions = secrets.py, .pyc, .cfg, .crt, .initimeout = 30[inventory]unparsed_is_failed=<span class="hljs-literal">true</span>[ssh_connection]pipelining = Truessh_args = -o ControlMaster=auto -o ControlPersist=600stimeout = 10control_path = %(directory)s/%%h-%%rEOF</code></pre></div><h3 id="配置-inventory-hosts"><a href="#配置-inventory-hosts" class="headerlink" title="配置 inventory hosts"></a>配置 inventory hosts</h3><div class="hljs"><pre><code class="hljs bash"> cat > /etc/ansible/hosts <<EOF[ins]10.10.34.92 hostname=<span class="hljs-string">'ukm01'</span>10.10.34.93 hostname=<span class="hljs-string">'ukm02'</span>10.10.34.94 hostname=<span class="hljs-string">'ukm03'</span>10.10.34.95 hostname=<span class="hljs-string">'uki01'</span>10.10.34.96 hostname=<span class="hljs-string">'uki02'</span>10.10.34.97 hostname=<span class="hljs-string">'uki03'</span>10.10.34.98 hostname=<span class="hljs-string">'ukn01'</span>10.10.34.99 hostname=<span class="hljs-string">'ukn02'</span>10.10.34.100 hostname=<span class="hljs-string">'ukn03'</span> ansible_ssh_user=<span class="hljs-string">'root'</span> ansible_ssh_pass=<span class="hljs-string">'xxxxxxx'</span>[ans]10.10.34.92[master]10.10.34.9[2:4][infra]10.10.34.9[5:7][worker]10.10.34.9[8:9]10.10.34.100EOF</code></pre></div><h3 id="验证-ansible-配置"><a href="#验证-ansible-配置" class="headerlink" title="验证 ansible 配置"></a>验证 ansible 配置</h3><div class="hljs"><pre><code class="hljs bash"> ansible ins -m ping10.10.34.92 | SUCCESS => { <span class="hljs-string">"ansible_facts"</span>: { <span class="hljs-string">"discovered_interpreter_python"</span>: <span class="hljs-string">"/usr/bin/python"</span> }, <span class="hljs-string">"changed"</span>: <span class="hljs-literal">false</span>, <span class="hljs-string">"ping"</span>: <span class="hljs-string">"pong"</span>}...</code></pre></div><h3 id="集群节点绑定主机名"><a href="#集群节点绑定主机名" class="headerlink" title="集群节点绑定主机名"></a>集群节点绑定主机名</h3><div class="hljs"><pre><code class="hljs bash"> ansible ins:\!ans -m shell -a <span class="hljs-string">'cat >> /etc/hosts <<EOF</span><span class="hljs-string">10.10.34.92 ukm01</span><span class="hljs-string">10.10.34.93 ukm02</span><span class="hljs-string">10.10.34.94 ukm03</span><span class="hljs-string">10.10.34.95 uki01</span><span class="hljs-string">10.10.34.96 uki02</span><span class="hljs-string">10.10.34.97 uki03</span><span class="hljs-string">10.10.34.98 ukn01</span><span class="hljs-string">10.10.34.99 ukn02</span><span class="hljs-string">10.10.34.100 ukn03</span><span class="hljs-string">EOF'</span> ansible ins:\!ans -m shell -a <span class="hljs-string">'cat /etc/hosts'</span></code></pre></div><h3 id="分发配置"><a href="#分发配置" class="headerlink" title="分发配置"></a>分发配置</h3><div class="hljs"><pre><code class="hljs bash">分发私钥ansible master -m copy -a <span class="hljs-string">'src=/root/.ssh/id_rsa dest=/root/.ssh/id_rsa mode=0600 owner=root'</span>分发 ansible 配置ansible master -m copy -a <span class="hljs-string">'src=/etc/ansible/hosts dest=/etc/ansible/hosts'</span>ansible master -m copy -a <span class="hljs-string">'src=/etc/ansible/ansible.cfg dest=/etc/ansible/ansible.cfg'</span></code></pre></div><h2 id="安装-Docker-CE"><a href="#安装-Docker-CE" class="headerlink" title="安装 Docker CE"></a><strong>安装 Docker CE</strong></h2><p>添加 Docker GPG key</p><div class="hljs"><pre><code class="hljs bash">ansible ins -m shell -a <span class="hljs-string">'curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -'</span></code></pre></div><h3 id="配置-Docker-源"><a href="#配置-Docker-源" class="headerlink" title="配置 Docker 源"></a>配置 Docker 源</h3><ul><li>动态识别操作系统版本 <code>add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"</code></li></ul><div class="hljs"><pre><code class="hljs bash">ansible ins -m shell -a <span class="hljs-string">'add-apt-repository "deb [arch=amd64] https://mirrors.aliyun.com/docker-ce/linux/ubuntu bionic stable"'</span></code></pre></div><h3 id="安装-Docker"><a href="#安装-Docker" class="headerlink" title="安装 Docker"></a>安装 Docker</h3><div class="hljs"><pre><code class="hljs bash">ansible ins -m shell -a <span class="hljs-string">'apt update && apt install -y docker-ce docker-ce-cli containerd.io'</span></code></pre></div><h3 id="配置-docker-daemon"><a href="#配置-docker-daemon" class="headerlink" title="配置 docker daemon"></a>配置 docker daemon</h3><div class="hljs"><pre><code class="hljs bash"> cat > /etc/docker/daemon.json <<EOF{ <span class="hljs-string">"exec-opts"</span>: [<span class="hljs-string">"native.cgroupdriver=systemd"</span>], <span class="hljs-string">"log-driver"</span>: <span class="hljs-string">"json-file"</span>, <span class="hljs-string">"log-opts"</span>: { <span class="hljs-string">"max-size"</span>: <span class="hljs-string">"100m"</span>, <span class="hljs-string">"max-file"</span>: <span class="hljs-string">"5"</span> }, <span class="hljs-string">"registry-mirrors"</span>: [<span class="hljs-string">"https://docker.mirrors.ustc.edu.cn"</span>], <span class="hljs-string">"storage-driver"</span>: <span class="hljs-string">"overlay2"</span>}EOF</code></pre></div><h3 id="分发-daemon-json"><a href="#分发-daemon-json" class="headerlink" title="分发 daemon.json"></a>分发 daemon.json</h3><div class="hljs"><pre><code class="hljs bash">ansible ins -m copy -a <span class="hljs-string">'src=/etc/docker/daemon.json dest=/etc/docker/daemon.json'</span>ansible ins -m shell -a <span class="hljs-string">'mkdir -p /etc/systemd/system/docker.service.d'</span></code></pre></div><h3 id="重启-docker-服务"><a href="#重启-docker-服务" class="headerlink" title="重启 docker 服务"></a>重启 docker 服务</h3><div class="hljs"><pre><code class="hljs bash">ansible ins -m shell -a <span class="hljs-string">'systemctl daemon-reload'</span>ansible ins -m shell -a <span class="hljs-string">'systemctl restart docker'</span>ansible ins -m shell -a <span class="hljs-string">'docker info|grep -i cgroup'</span>Cgroup Driver: systemd</code></pre></div><h3 id="daemon-json-配置详解"><a href="#daemon-json-配置详解" class="headerlink" title="daemon.json 配置详解"></a>daemon.json 配置详解</h3><div class="hljs"><pre><code class="hljs bash"> cat /etc/docker/daemon.json{ <span class="hljs-string">"authorization-plugins"</span>: [], <span class="hljs-string">"data-root"</span>: <span class="hljs-string">""</span>, <span class="hljs-string">"dns"</span>: [], <span class="hljs-string">"dns-opts"</span>: [], <span class="hljs-string">"dns-search"</span>: [], <span class="hljs-string">"exec-opts"</span>: [], <span class="hljs-string">"exec-root"</span>: <span class="hljs-string">""</span>, <span class="hljs-string">"experimental"</span>: <span class="hljs-literal">false</span>, <span class="hljs-string">"features"</span>: {}, <span class="hljs-string">"storage-driver"</span>: <span class="hljs-string">""</span>, <span class="hljs-string">"storage-opts"</span>: [], <span class="hljs-string">"labels"</span>: [], <span class="hljs-string">"live-restore"</span>: <span class="hljs-literal">true</span>, <span class="hljs-string">"log-driver"</span>: <span class="hljs-string">"json-file"</span>, <span class="hljs-string">"log-opts"</span>: { <span class="hljs-string">"max-size"</span>: <span class="hljs-string">"10m"</span>, <span class="hljs-string">"max-file"</span>:<span class="hljs-string">"5"</span>, <span class="hljs-string">"labels"</span>: <span class="hljs-string">"somelabel"</span>, <span class="hljs-string">"env"</span>: <span class="hljs-string">"os,customer"</span> }, <span class="hljs-string">"mtu"</span>: 0, <span class="hljs-string">"pidfile"</span>: <span class="hljs-string">""</span>, <span class="hljs-string">"cluster-store"</span>: <span class="hljs-string">""</span>, <span class="hljs-string">"cluster-store-opts"</span>: {}, <span class="hljs-string">"cluster-advertise"</span>: <span class="hljs-string">""</span>, <span class="hljs-string">"max-concurrent-downloads"</span>: 3, <span class="hljs-string">"max-concurrent-uploads"</span>: 5, <span class="hljs-string">"default-shm-size"</span>: <span class="hljs-string">"64M"</span>, <span class="hljs-string">"shutdown-timeout"</span>: 15, <span class="hljs-string">"debug"</span>: <span class="hljs-literal">true</span>, <span class="hljs-string">"hosts"</span>: [], <span class="hljs-string">"log-level"</span>: <span class="hljs-string">""</span>, <span class="hljs-string">"tls"</span>: <span class="hljs-literal">true</span>, <span class="hljs-string">"tlsverify"</span>: <span class="hljs-literal">true</span>, <span class="hljs-string">"tlscacert"</span>: <span class="hljs-string">""</span>, <span class="hljs-string">"tlscert"</span>: <span class="hljs-string">""</span>, <span class="hljs-string">"tlskey"</span>: <span class="hljs-string">""</span>, <span class="hljs-string">"swarm-default-advertise-addr"</span>: <span class="hljs-string">""</span>, <span class="hljs-string">"api-cors-header"</span>: <span class="hljs-string">""</span>, <span class="hljs-string">"selinux-enabled"</span>: <span class="hljs-literal">false</span>, <span class="hljs-string">"userns-remap"</span>: <span class="hljs-string">""</span>, <span class="hljs-string">"group"</span>: <span class="hljs-string">""</span>, <span class="hljs-string">"cgroup-parent"</span>: <span class="hljs-string">""</span>, <span class="hljs-string">"default-ulimits"</span>: { <span class="hljs-string">"nofile"</span>: { <span class="hljs-string">"Name"</span>: <span class="hljs-string">"nofile"</span>, <span class="hljs-string">"Hard"</span>: 64000, <span class="hljs-string">"Soft"</span>: 64000 } }, <span class="hljs-string">"init"</span>: <span class="hljs-literal">false</span>, <span class="hljs-string">"init-path"</span>: <span class="hljs-string">"/usr/libexec/docker-init"</span>, <span class="hljs-string">"ipv6"</span>: <span class="hljs-literal">false</span>, <span class="hljs-string">"iptables"</span>: <span class="hljs-literal">false</span>, <span class="hljs-string">"ip-forward"</span>: <span class="hljs-literal">false</span>, <span class="hljs-string">"ip-masq"</span>: <span class="hljs-literal">false</span>, <span class="hljs-string">"userland-proxy"</span>: <span class="hljs-literal">false</span>, <span class="hljs-string">"userland-proxy-path"</span>: <span class="hljs-string">"/usr/libexec/docker-proxy"</span>, <span class="hljs-string">"ip"</span>: <span class="hljs-string">"0.0.0.0"</span>, <span class="hljs-string">"bridge"</span>: <span class="hljs-string">""</span>, <span class="hljs-string">"bip"</span>: <span class="hljs-string">""</span>, <span class="hljs-string">"fixed-cidr"</span>: <span class="hljs-string">""</span>, <span class="hljs-string">"fixed-cidr-v6"</span>: <span class="hljs-string">""</span>, <span class="hljs-string">"default-gateway"</span>: <span class="hljs-string">""</span>, <span class="hljs-string">"default-gateway-v6"</span>: <span class="hljs-string">""</span>, <span class="hljs-string">"icc"</span>: <span class="hljs-literal">false</span>, <span class="hljs-string">"raw-logs"</span>: <span class="hljs-literal">false</span>, <span class="hljs-string">"allow-nondistributable-artifacts"</span>: [], <span class="hljs-string">"registry-mirrors"</span>: [], <span class="hljs-string">"seccomp-profile"</span>: <span class="hljs-string">""</span>, <span class="hljs-string">"insecure-registries"</span>: [], <span class="hljs-string">"no-new-privileges"</span>: <span class="hljs-literal">false</span>, <span class="hljs-string">"default-runtime"</span>: <span class="hljs-string">"runc"</span>, <span class="hljs-string">"oom-score-adjust"</span>: -500, <span class="hljs-string">"node-generic-resources"</span>: [<span class="hljs-string">"NVIDIA-GPU=UUID1"</span>, <span class="hljs-string">"NVIDIA-GPU=UUID2"</span>], <span class="hljs-string">"runtimes"</span>: { <span class="hljs-string">"cc-runtime"</span>: { <span class="hljs-string">"path"</span>: <span class="hljs-string">"/usr/bin/cc-runtime"</span> }, <span class="hljs-string">"custom"</span>: { <span class="hljs-string">"path"</span>: <span class="hljs-string">"/usr/local/bin/my-runc-replacement"</span>, <span class="hljs-string">"runtimeArgs"</span>: [ <span class="hljs-string">"--debug"</span> ] } }, <span class="hljs-string">"default-address-pools"</span>:[ {<span class="hljs-string">"base"</span>:<span class="hljs-string">"172.80.0.0/16"</span>,<span class="hljs-string">"size"</span>:24}, {<span class="hljs-string">"base"</span>:<span class="hljs-string">"172.90.0.0/16"</span>,<span class="hljs-string">"size"</span>:24} ]}</code></pre></div><h3 id="格式化查看-images"><a href="#格式化查看-images" class="headerlink" title="格式化查看 images"></a>格式化查看 images</h3><ul><li>.ID Image ID</li><li>.Repository Image 仓库地址和名称</li><li>.Tag Image 标签</li><li>.Digest Image hash 值信息</li><li>.CreatedSince Image 创建到现在的时间</li><li>.CreatedAt Image 创建的具体时间</li><li>.Size Image 大小</li></ul><div class="hljs"><pre><code class="hljs bash">docker images --format <span class="hljs-string">"table {{ .Repository }} \t {{ .Tag }} \t {{ .ID }} \t {{ .CreatedSince }} \t {{ .CreatedAt }} \t {{ .Size }}"</span> --digests</code></pre></div><h3 id="docker-pull-过程"><a href="#docker-pull-过程" class="headerlink" title="docker pull 过程"></a>docker pull 过程</h3><ul><li>Docker 客户端发送 REPOSITORY 和 TAG(或者 DIGEST)给 Registry;</li><li>Registry 根据 REPOSITORY 和 TAG(或者 DIGEST)找到相应 image manifest;</li><li>Registry 将 manifest 返回给 Docker 客户端;</li><li>Docker 客户端根据 manifest 读取 image 的 digest(sha256) , sha256 就是 IMAGE ID;</li><li>Docker 客户端根据 ID 查找本地是否存在对应的 image;</li><li>如果 image 存在则创建对应的 image REPOSITORY 和 TAG;</li><li>如果 image 不存在,则继续请求 Registry 获取 image 的配置文件;</li><li>根据 image 配置文件中的 diff_ids 在本地找对应的 layer 是否存在;</li><li>如果 layer 存在,则返回 Already exists;</li><li>如果 layer 不存在,则根据 manifest 中 layer 的 sha256 和 media type 去服务器下载对应的 layer;</li><li>下载解压之后,校验 tar 包的 sha256 是否和 Image Config 中的 diff_id 一致。</li></ul><h3 id="image本地存放信息"><a href="#image本地存放信息" class="headerlink" title="image本地存放信息"></a>image本地存放信息</h3><div class="hljs"><pre><code class="hljs bash"> 查看 image DIGEST 信息 docker images --digestsREPOSITORY TAG DIGEST IMAGE ID CREATED SIZEbusybox latest sha256:a8cf7ff6367c2afa2a90acd081b484cbded349a7076e7bdf37a05279f276bc12 be5888e67be6 2 weeks ago 1.22MB 如果是其他的文件系统的话,名字会是其他的,比如btrfs、overlay2、devicemapper等。 cat /var/lib/docker/image/overlay2/repositories.json | python -m json.tool{ <span class="hljs-string">"Repositories"</span>: { <span class="hljs-string">"busybox"</span>: { <span class="hljs-string">"busybox:latest"</span>: <span class="hljs-string">"sha256:be5888e67be651f1fbb59006f0fd791b44ed3fceaa6323ab4e37d5928874345a"</span>, <span class="hljs-string">"busybox@sha256:a8cf7ff6367c2afa2a90acd081b484cbded349a7076e7bdf37a05279f276bc12"</span>: <span class="hljs-string">"sha256:be5888e67be651f1fbb59006f0fd791b44ed3fceaa6323ab4e37d5928874345a"</span> } }} 校验 image config 的 sha256 值 sha256sum /var/lib/docker/image/overlay2/imagedb/content/sha256/be5888e67be651f1fbb59006f0fd791b44ed3fceaa6323ab4e37d5928874345abe5888e67be651f1fbb59006f0fd791b44ed3fceaa6323ab4e37d5928874345a /var/lib/docker/image/overlay2/imagedb/content/sha256/be5888e67be651f1fbb59006f0fd791b44ed3fceaa6323ab4e37d5928874345a 查看 image config cat /var/lib/docker/image/overlay2/imagedb/content/sha256/be5888e67be651f1fbb59006f0fd791b44ed3fceaa6323ab4e37d5928874345a | python -m json.tool{ <span class="hljs-string">"architecture"</span>: <span class="hljs-string">"amd64"</span>, <span class="hljs-string">"config"</span>: { <span class="hljs-string">"ArgsEscaped"</span>: <span class="hljs-literal">true</span>, <span class="hljs-string">"AttachStderr"</span>: <span class="hljs-literal">false</span>, <span class="hljs-string">"AttachStdin"</span>: <span class="hljs-literal">false</span>, <span class="hljs-string">"AttachStdout"</span>: <span class="hljs-literal">false</span>, <span class="hljs-string">"Cmd"</span>: [ <span class="hljs-string">"sh"</span> ], <span class="hljs-string">"Domainname"</span>: <span class="hljs-string">""</span>, <span class="hljs-string">"Entrypoint"</span>: null, <span class="hljs-string">"Env"</span>: [ <span class="hljs-string">"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"</span> ], <span class="hljs-string">"Hostname"</span>: <span class="hljs-string">""</span>, <span class="hljs-string">"Image"</span>: <span class="hljs-string">"sha256:b0acc7ebf5092fcdd0fe097448529147e6619bd051f03ccf25b29bcae87e783f"</span>, <span class="hljs-string">"Labels"</span>: null, <span class="hljs-string">"OnBuild"</span>: null, <span class="hljs-string">"OpenStdin"</span>: <span class="hljs-literal">false</span>, <span class="hljs-string">"StdinOnce"</span>: <span class="hljs-literal">false</span>, <span class="hljs-string">"Tty"</span>: <span class="hljs-literal">false</span>, <span class="hljs-string">"User"</span>: <span class="hljs-string">""</span>, <span class="hljs-string">"Volumes"</span>: null, <span class="hljs-string">"WorkingDir"</span>: <span class="hljs-string">""</span> }, <span class="hljs-string">"container"</span>: <span class="hljs-string">"f7e67f16a539f8bbf53aae18cdb5f8c53e6a56930e7660010d9396ae77f7acfa"</span>, <span class="hljs-string">"container_config"</span>: { <span class="hljs-string">"ArgsEscaped"</span>: <span class="hljs-literal">true</span>, <span class="hljs-string">"AttachStderr"</span>: <span class="hljs-literal">false</span>, <span class="hljs-string">"AttachStdin"</span>: <span class="hljs-literal">false</span>, <span class="hljs-string">"AttachStdout"</span>: <span class="hljs-literal">false</span>, <span class="hljs-string">"Cmd"</span>: [ <span class="hljs-string">"/bin/sh"</span>, <span class="hljs-string">"-c"</span>, <span class="hljs-string">"(nop) "</span>, <span class="hljs-string">"CMD [\"sh\"]"</span> ], <span class="hljs-string">"Domainname"</span>: <span class="hljs-string">""</span>, <span class="hljs-string">"Entrypoint"</span>: null, <span class="hljs-string">"Env"</span>: [ <span class="hljs-string">"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"</span> ], <span class="hljs-string">"Hostname"</span>: <span class="hljs-string">"f7e67f16a539"</span>, <span class="hljs-string">"Image"</span>: <span class="hljs-string">"sha256:b0acc7ebf5092fcdd0fe097448529147e6619bd051f03ccf25b29bcae87e783f"</span>, <span class="hljs-string">"Labels"</span>: {}, <span class="hljs-string">"OnBuild"</span>: null, <span class="hljs-string">"OpenStdin"</span>: <span class="hljs-literal">false</span>, <span class="hljs-string">"StdinOnce"</span>: <span class="hljs-literal">false</span>, <span class="hljs-string">"Tty"</span>: <span class="hljs-literal">false</span>, <span class="hljs-string">"User"</span>: <span class="hljs-string">""</span>, <span class="hljs-string">"Volumes"</span>: null, <span class="hljs-string">"WorkingDir"</span>: <span class="hljs-string">""</span> }, <span class="hljs-string">"created"</span>: <span class="hljs-string">"2020-04-14T19:19:53.590635493Z"</span>, <span class="hljs-string">"docker_version"</span>: <span class="hljs-string">"18.09.7"</span>, <span class="hljs-string">"history"</span>: [ { <span class="hljs-string">"created"</span>: <span class="hljs-string">"2020-04-14T19:19:53.444488372Z"</span>, <span class="hljs-string">"created_by"</span>: <span class="hljs-string">"/bin/sh -c (nop) ADD file:09a89925137e1b768ef1f0e7d1d7325eb2b4f1a0895b3aa8dfc98b0c75f3f507 in / "</span> }, { <span class="hljs-string">"created"</span>: <span class="hljs-string">"2020-04-14T19:19:53.590635493Z"</span>, <span class="hljs-string">"created_by"</span>: <span class="hljs-string">"/bin/sh -c (nop) CMD [\"sh\"]"</span>, <span class="hljs-string">"empty_layer"</span>: <span class="hljs-literal">true</span> } ], <span class="hljs-string">"os"</span>: <span class="hljs-string">"linux"</span>, <span class="hljs-string">"rootfs"</span>: { <span class="hljs-string">"diff_ids"</span>: [ <span class="hljs-string">"sha256:5b0d2d635df829f65d0ffb45eab2c3124a470c4f385d6602bda0c21c5248bcab"</span> ], <span class="hljs-string">"type"</span>: <span class="hljs-string">"layers"</span> }} layer diff_id 和 digest 的对应关系 diffid-by-digest: 存放 digest 到 diffid 的对应关系 v2metadata-by-diffid: 存放 diffid 到 digest 的对应关系 tree /var/lib/docker/image/overlay2/distribution//var/lib/docker/image/overlay2/distribution/├── diffid-by-digest│ └── sha256│ ├── e2334dd9fee4b77e48a8f2d793904118a3acf26f1f2e72a3d79c6cae993e07f0│ └── ec237e1426f0de497da8c0951d5db1d2b7f155d207cfc0df02f3fb0508a3036a└── v2metadata-by-diffid └── sha256 ├── 5b0d2d635df829f65d0ffb45eab2c3124a470c4f385d6602bda0c21c5248bcab └── f53db92a80829925f8ec12396ab68569d7a769d9ab94983b86dbe6b26ae3d1fa 根据 diffid 获取 digest~ cat /var/lib/docker/image/overlay2/distribution/v2metadata-by-diffid/sha256/5b0d2d635df829f65d0ffb45eab2c3124a470c4f385d6602bda0c21c5248bcab | python -m json.tool[ { <span class="hljs-string">"Digest"</span>: <span class="hljs-string">"sha256:e2334dd9fee4b77e48a8f2d793904118a3acf26f1f2e72a3d79c6cae993e07f0"</span>, <span class="hljs-string">"HMAC"</span>: <span class="hljs-string">""</span>, <span class="hljs-string">"SourceRepository"</span>: <span class="hljs-string">"docker.io/library/busybox"</span> }] 根据 digest 获取 diffid cat /var/lib/docker/image/overlay2/distribution/diffid-by-digest/sha256/e2334dd9fee4b77e48a8f2d793904118a3acf26f1f2e72a3d79c6cae993e07f0sha256:5b0d2d635df829f65d0ffb45eab2c3124a470c4f385d6602bda0c21c5248bcab 根据 digest 查看 layer 元数据 <span class="hljs-built_in">cd</span> /var/lib/docker/image/overlay2/layerdb/sha256/5b0d2d635df829f65d0ffb45eab2c3124a470c4f385d6602bda0c21c5248bcab/ && ls -ltotal 24-rw-r--r-- 1 root root 64 Apr 30 15:21 cache-id-rw-r--r-- 1 root root 71 Apr 30 15:21 diff-rw-r--r-- 1 root root 7 Apr 30 15:21 size-rw-r--r-- 1 root root 12159 Apr 30 15:21 tar-split.json.gz cache-id 是 docker 下载 layer 的时候随机生成 uuid,指向真正存放 layer 文件的位置。 cat cache-id486d3aacd2ee026b1e3db99b23e32a9f60d0972f7224fd53657a41943c871872 diff 文件存放 layer 的 diffid cat diffsha256:5b0d2d635df829f65d0ffb45eab2c3124a470c4f385d6602bda0c21c5248bcab size 当前 layer 的大小,单位是字节。 tar-split.json.gz 是 layer 的 split 文件,通过这个文件可以还原 layer 的 tar 包,在 docker save 导出 image 时会用到。 layer 数据,image 存储数据,使用 cache-id 访问。 <span class="hljs-built_in">cd</span> /var/lib/docker/overlay2/486d3aacd2ee026b1e3db99b23e32a9f60d0972f7224fd53657a41943c871872 tree -d diff/diff/├── bin├── dev├── etc│ └── network│ ├── <span class="hljs-keyword">if</span>-down.d│ ├── <span class="hljs-keyword">if</span>-post-down.d│ ├── <span class="hljs-keyword">if</span>-pre-up.d│ └── <span class="hljs-keyword">if</span>-up.d├── home├── root├── tmp├── usr│ └── sbin└── var ├── spool │ └── mail └── www manifest 存储对 config 和 layer 的 sha256 + media <span class="hljs-built_in">type</span> 描述,目的就是为了下载 config 和 layer。下载完成后,manifest 的使命就完成了,所以在本地没有存储 manifest 文件。</code></pre></div><h3 id="docker-空间管理"><a href="#docker-空间管理" class="headerlink" title="docker 空间管理"></a>docker 空间管理</h3><div class="hljs"><pre><code class="hljs bash"> 查看空间 docker system dfTYPE TOTAL ACTIVE SIZE RECLAIMABLEImages 11 9 1.084GB 202.9MB (18%)Containers 18 12 10.61kB 0B (0%)Local Volumes 0 0 0B 0BBuild Cache 0 0 0B 0B 清理文件系统 docker system pruneWARNING! This will remove: - all stopped containers - all networks not used by at least one container - all dangling images - all dangling build cacheAre you sure you want to <span class="hljs-built_in">continue</span>? [y/N] 强制清理,忽略依赖关系。 docker system prune -a -f 清理所有状态为 <none> 的 image docker image pruneWARNING! This will remove all images without at least one container associated to them.Are you sure you want to <span class="hljs-built_in">continue</span>? [y/N] 强制清理 image,忽略依赖关系。 docker image prune -aWARNING! This will remove all images without at least one container associated to them.Are you sure you want to <span class="hljs-built_in">continue</span>? [y/N] 清理无用数据卷 docker volume ls docker volume pruneWARNING! This will remove all <span class="hljs-built_in">local</span> volumes not used by at least one container.Are you sure you want to <span class="hljs-built_in">continue</span>? [y/N] 清理所有停止的容器 docker container pruneWARNING! This will remove all stopped containers.Are you sure you want to <span class="hljs-built_in">continue</span>? [y/N]</code></pre></div><h2 id="配置系统"><a href="#配置系统" class="headerlink" title="配置系统"></a><strong>配置系统</strong></h2><p>内核关闭 swap</p><div class="hljs"><pre><code class="hljs bash">ansible ins -m shell -a <span class="hljs-string">'echo "vm.swappiness = 0" >> /etc/sysctl.conf && sysctl -p'</span></code></pre></div><p>零时关闭 swap</p><div class="hljs"><pre><code class="hljs bash">ansible ins -m shell -a <span class="hljs-string">'swapoff -a'</span></code></pre></div><p>系统 fstab 关闭配置</p><div class="hljs"><pre><code class="hljs bash">ansible ins -m shell -a <span class="hljs-string">"sed -i 's/^[^#].*swap*/#&/g' /etc/fstab && chattr +i /etc/fstab"</span></code></pre></div><ul><li>WARNING: No swap limit support</li></ul><div class="hljs"><pre><code class="hljs bash">sed -i <span class="hljs-string">'/GRUB_CMDLINE_LINUX=/cGRUB_CMDLINE_LINUX="net.ifnames=0 biosdevname=0 console=tty0 console=ttyS0,115200 cgroup_enable=memory swapaccount=1"'</span> /etc/default/grubansible ins -m copy -a <span class="hljs-string">'src=/etc/default/grub dest=/etc/default/grub'</span>ansible ins -m shell -a <span class="hljs-string">'update-grub && shutdown -r +1 "reboot for turnoff swap"'</span></code></pre></div><h2 id="安装-kubernetes"><a href="#安装-kubernetes" class="headerlink" title="安装 kubernetes"></a><strong>安装 kubernetes</strong></h2><ul><li>激活旧版本的防火墙配置(19年之后的操作系统需要修改)</li></ul><div class="hljs"><pre><code class="hljs bash">apt install -y iptables arptables ebtablesupdate-alternatives --<span class="hljs-built_in">set</span> iptables /usr/sbin/iptables-legacyupdate-alternatives --<span class="hljs-built_in">set</span> ip6tables /usr/sbin/ip6tables-legacyupdate-alternatives --<span class="hljs-built_in">set</span> arptables /usr/sbin/arptables-legacyupdate-alternatives --<span class="hljs-built_in">set</span> ebtables /usr/sbin/ebtables-legacy</code></pre></div><h2 id="配置-kubernetes-repo"><a href="#配置-kubernetes-repo" class="headerlink" title="配置 kubernetes repo"></a>配置 kubernetes repo</h2><div class="hljs"><pre><code class="hljs bash">批量配置ansible ins -m shell -a <span class="hljs-string">'curl -fsSL https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | apt-key add -'</span>ansible ins -m shell -a <span class="hljs-string">'add-apt-repository "deb [arch=amd64] https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main"'</span></code></pre></div><h2 id="安装-kubernetes-基础工具"><a href="#安装-kubernetes-基础工具" class="headerlink" title="安装 kubernetes 基础工具"></a>安装 kubernetes 基础工具</h2><div class="hljs"><pre><code class="hljs bash">查看当前支持的版本信息apt-cache policy kubeadmansible ins -m shell -a <span class="hljs-string">'apt update && apt install -y kubelet=1.17.3-00 kubeadm=1.17.3-00 kubectl=1.17.3-00'</span></code></pre></div><h2 id="配置-driver"><a href="#配置-driver" class="headerlink" title="配置 driver"></a>配置 driver</h2><div class="hljs"><pre><code class="hljs bash"> cat > /etc/default/kubelet <<EOFKUBELET_EXTRA_ARGS=--cgroup-driver=systemdEOF cat > /etc/systemd/system/kubelet.service.d/10-kubeadm.conf <<EOF[Service]Environment=<span class="hljs-string">"KUBELET_KUBECONFIG_ARGS=--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf --cgroup-driver=systemd"</span>Environment=<span class="hljs-string">"KUBELET_CONFIG_ARGS=--config=/var/lib/kubelet/config.yaml"</span><span class="hljs-comment"># 这是 "kubeadm init" 和 "kubeadm join" 运行时生成的文件,动态地填充 KUBELET_KUBEADM_ARGS 变量</span>EnvironmentFile=-/var/lib/kubelet/kubeadm-flags.env<span class="hljs-comment"># 这是一个文件,用户在不得已下可以将其用作替代 kubelet args。</span><span class="hljs-comment"># 用户最好使用 .NodeRegistration.KubeletExtraArgs 对象在配置文件中替代。</span><span class="hljs-comment"># KUBELET_EXTRA_ARGS 应该从此文件中获取。</span>EnvironmentFile=-/etc/default/kubeletExecStart=ExecStart=/usr/bin/kubelet <span class="hljs-variable">$KUBELET_KUBECONFIG_ARGS</span> <span class="hljs-variable">$KUBELET_CONFIG_ARGS</span> <span class="hljs-variable">$KUBELET_KUBEADM_ARGS</span> <span class="hljs-variable">$KUBELET_EXTRA_ARGS</span>EOFansible ins -m copy -a <span class="hljs-string">'src=/etc/default/kubelet dest=/etc/default/kubelet'</span>ansible ins -m copy -a <span class="hljs-string">'src=/etc/systemd/system/kubelet.service.d/10-kubeadm.conf dest=/etc/systemd/system/kubelet.service.d/10-kubeadm.conf'</span></code></pre></div><h3 id="重启-kubelet"><a href="#重启-kubelet" class="headerlink" title="重启 kubelet"></a>重启 kubelet</h3><div class="hljs"><pre><code class="hljs bash">ansible ins -m shell -a <span class="hljs-string">'systemctl daemon-reload && systemctl restart kubelet'</span></code></pre></div><h2 id="开启-ipvs-支持"><a href="#开启-ipvs-支持" class="headerlink" title="开启 ipvs 支持"></a>开启 ipvs 支持</h2><h3 id="配置内核支持-IP-转发"><a href="#配置内核支持-IP-转发" class="headerlink" title="配置内核支持 IP 转发"></a>配置内核支持 IP 转发</h3><div class="hljs"><pre><code class="hljs bash">ansible ins -m shell -a <span class="hljs-string">'grep net.ipv4.ip_forward /etc/sysctl.conf && sed -i "/net.ipv4.ip_forward/c\net.ipv4.ip_forward=1" /etc/sysctl.conf || echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf'</span>ansible ins -m shell -a <span class="hljs-string">'grep net.bridge.bridge-nf-call-iptables /etc/sysctl.conf && sed -i "/net.bridge.bridge-nf-call-iptables/c\net.bridge.bridge-nf-call-iptables=1" /etc/sysctl.conf || echo "net.bridge.bridge-nf-call-iptables=1" >> /etc/sysctl.conf'</span>ansible ins -m shell -a <span class="hljs-string">'grep net.bridge.bridge-nf-call-ip6tables /etc/sysctl.conf && sed -i "/net.bridge.bridge-nf-call-ip6tables/c\net.bridge.bridge-nf-call-ip6tables=1" /etc/sysctl.conf || echo "net.bridge.bridge-nf-call-ip6tables=1" >> /etc/sysctl.conf'</span>ansible ins -m shell -a <span class="hljs-string">'sysctl -p'</span></code></pre></div><h3 id="配置内核加载-ipvs-模块"><a href="#配置内核加载-ipvs-模块" class="headerlink" title="配置内核加载 ipvs 模块"></a>配置内核加载 ipvs 模块</h3><div class="hljs"><pre><code class="hljs bash"> cat > /tmp/ipvs.modules <<EOF<span class="hljs-meta">#!/bin/bash</span>ipvs_modules=<span class="hljs-string">"ip_vs ip_vs_lc ip_vs_wlc ip_vs_rr ip_vs_wrr ip_vs_lblc ip_vs_lblcr ip_vs_dh ip_vs_sh ip_vs_fo ip_vs_nq ip_vs_sed ip_vs_ftp nf_conntrack_ipv4"</span><span class="hljs-keyword">for</span> kernel_module <span class="hljs-keyword">in</span> \<span class="hljs-variable">${ipvs_modules}</span>; <span class="hljs-keyword">do</span> /sbin/modinfo -F filename \<span class="hljs-variable">${kernel_module}</span> > /dev/null 2>&1 <span class="hljs-keyword">if</span> [ $? -eq 0 ]; <span class="hljs-keyword">then</span> /sbin/modprobe \<span class="hljs-variable">${kernel_module}</span> <span class="hljs-keyword">fi</span><span class="hljs-keyword">done</span>EOF ansible ins -m copy -a <span class="hljs-string">'src=/tmp/ipvs.modules dest=/tmp/ipvs.modules mode=0755'</span> ansible ins -m shell -a <span class="hljs-string">'/tmp/ipvs.modules'</span> 验证 ipvs 支持 ansible ins -m shell -a <span class="hljs-string">'lsmod | grep ip_vs'</span></code></pre></div><h3 id="安装-ipvsadm"><a href="#安装-ipvsadm" class="headerlink" title="安装 ipvsadm"></a>安装 ipvsadm</h3><div class="hljs"><pre><code class="hljs bash">ansible ins -m yum -a <span class="hljs-string">'name=ipvsadm state=present'</span></code></pre></div><h2 id="创建-kubernetes-集群"><a href="#创建-kubernetes-集群" class="headerlink" title="创建 kubernetes 集群"></a><strong>创建 kubernetes 集群</strong></h2><p>ukm01 上执行</p><ul><li>内网负载均衡地址 10.10.34.89</li></ul><div class="hljs"><pre><code class="hljs bash"> 配置文件创建,可以修改较多的参数。 cat > kubeadm-config.yaml <<EOFapiVersion: kubeadm.k8s.io/v1beta2kind: InitConfigurationlocalAPIEndpoint: advertiseAddress: 0.0.0.0 bindPort: 6443nodeRegistration: criSocket: /var/run/dockershim.sock kubeletExtraArgs: cgroup-driver: <span class="hljs-string">"systemd"</span> ignorePreflightErrors: - IsPrivilegedUser---apiVersion: kubeadm.k8s.io/v1beta2kind: ClusterConfigurationcontrolPlaneEndpoint: 192.168.0.113:6443certificatesDir: /etc/kubernetes/pkiclusterName: kubernetesdns: <span class="hljs-built_in">type</span>: CoreDNSapiServer: timeoutForControlPlane: 5m0s extraArgs: authorization-mode: <span class="hljs-string">"Node,RBAC"</span> certSANs: - <span class="hljs-string">"192.168.0.113"</span> - <span class="hljs-string">"192.168.0.110"</span> - <span class="hljs-string">"192.168.0.111"</span> - <span class="hljs-string">"192.168.0.112"</span> - <span class="hljs-string">"node1"</span> - <span class="hljs-string">"node2"</span> - <span class="hljs-string">"node3"</span> - <span class="hljs-string">"kubernetes"</span> - <span class="hljs-string">"kubernetes.default"</span> - <span class="hljs-string">"kubernetes.default.svc"</span> - <span class="hljs-string">"kubernetes.default.svc.cluster"</span> - <span class="hljs-string">"kubernetes.default.svc.cluster.local"</span>controllerManager: extraArgs: <span class="hljs-string">"node-cidr-mask-size"</span>: <span class="hljs-string">"20"</span>scheduler: extraArgs: address: <span class="hljs-string">"0.0.0.0"</span>dns: <span class="hljs-built_in">type</span>: CoreDNSetcd: <span class="hljs-built_in">local</span>: dataDir: /var/lib/etcdimageRepository: registry.cn-hangzhou.aliyuncs.com/google_containerskubernetesVersion: v1.20.1networking: dnsDomain: cluster.local serviceSubnet: 10.96.0.0/12 podSubnet: 192.168.0.0/16---apiVersion: kubeproxy.config.k8s.io/v1alpha1kind: KubeProxyConfigurationbindAddress: 0.0.0.0mode: ipvs ipvs: scheduler: wlc syncPeriod: 30s minSyncPeriod: 5s tcpTimeout: 0s tcpFinTimeout: 0s udpTimeout: 0s---apiVersion: kubelet.config.k8s.io/v1beta1kind: KubeletConfigurationcgroupDriver: systemdrotateCertificates: <span class="hljs-literal">true</span>EOF 验证 kubeadm-config 配置 kubeadm init --config=kubeadm-config.yaml --upload-certs 创建集群 kubeadm init --config=kubeadm-config.yaml --upload-certs 参数化创建集群 kubeadm init \ --apiserver-bind-port 8443 \ --control-plane-endpoint <span class="hljs-string">"10.10.34.89:8443"</span> \ --image-repository registry.cn-hangzhou.aliyuncs.com/google_containers \ --kubernetes-version=v1.17.3 \ --upload-certs \ --pod-network-cidr=10.100.0.0/16 \ --service-cidr=10.96.0.0/12 \...kubeadm join 10.10.34.89:8443 --token 7nf8wp.cefgemzgn5xcdmln \ --discovery-token-ca-cert-hash sha256:36b328e0052c7f8e7cac826d70b21bef0cdc4e0505fddc8200c52b8b01605da3 \ --control-plane --certificate-key bd9fc1600d2db4f2b323a17b2b7be4f4234d7ed8705bc64976dcd43d929a24a8...kubeadm join 10.10.34.89:8443 --token 7nf8wp.cefgemzgn5xcdmln \ --discovery-token-ca-cert-hash sha256:36b328e0052c7f8e7cac826d70b21bef0cdc4e0505fddc8200c52b8b01605da3</code></pre></div><h3 id="kubeadm-init-常用参数"><a href="#kubeadm-init-常用参数" class="headerlink" title="kubeadm init 常用参数"></a>kubeadm init 常用参数</h3><div class="hljs"><pre><code class="hljs bash">--apiserver-advertise-address stringAPI 服务器所公布的其正在监听的 IP 地址。如果未设置,则使用默认网络接口。--apiserver-bind-port int32 默认值:6443API 服务器绑定的端口。--apiserver-cert-extra-sans stringSlice用于 API Server 服务证书的可选附加主题备用名称(SAN)。可以是 IP 地址和 DNS 名称。--cert-dir string 默认值:<span class="hljs-string">"/etc/kubernetes/pki"</span>保存和存储证书的路径。--certificate-key string用于加密 kubeadm-certs Secret 中的控制平面证书的密钥。--config stringkubeadm 配置文件的路径。--control-plane-endpoint string为控制平面指定一个稳定的 IP 地址或 DNS 名称。--cri-socket string要连接的 CRI 套接字的路径。如果为空,则 kubeadm 将尝试自动检测此值;仅当安装了多个 CRI 或具有非标准 CRI 插槽时,才使用此选项。--dry-run不要应用任何更改;只是输出将要执行的操作。-k, --experimental-kustomize string用于存储 kustomize 为静态 pod 清单所提供的补丁的路径。--feature-gates string一组用来描述各种功能特性的键值(key=value)对。选项是:IPv6DualStack=<span class="hljs-literal">true</span>|<span class="hljs-literal">false</span> (ALPHA - default=<span class="hljs-literal">false</span>)-h, --<span class="hljs-built_in">help</span>init 操作的帮助命令--ignore-preflight-errors stringSlice错误将显示为警告的检查列表;例如:<span class="hljs-string">'IsPrivilegedUser,Swap'</span>。取值为 <span class="hljs-string">'all'</span> 时将忽略检查中的所有错误。--image-repository string 默认值:<span class="hljs-string">"k8s.gcr.io"</span>选择用于拉取控制平面镜像的容器仓库--kubernetes-version string 默认值:<span class="hljs-string">"stable-1"</span>为控制平面选择一个特定的 Kubernetes 版本。--node-name string指定节点的名称。--pod-network-cidr string指明 pod 网络可以使用的 IP 地址段。如果设置了这个参数,控制平面将会为每一个节点自动分配 CIDRs。--service-cidr string 默认值:<span class="hljs-string">"10.96.0.0/12"</span>为服务的虚拟 IP 地址另外指定 IP 地址段--service-dns-domain string 默认值:<span class="hljs-string">"cluster.local"</span>为服务另外指定域名,例如:<span class="hljs-string">"myorg.internal"</span>。--skip-certificate-key-print不要打印用于加密控制平面证书的密钥。--skip-phases stringSlice要跳过的阶段列表--skip-token-print跳过打印 <span class="hljs-string">'kubeadm init'</span> 生成的默认引导令牌。--token string这个令牌用于建立控制平面节点与工作节点间的双向通信。格式为 [a-z0-9]{6}\.[a-z0-9]{16} - 示例:abcdef.0123456789abcdef--token-ttl duration 默认值:24h0m0s令牌被自动删除之前的持续时间(例如 1 s,2 m,3 h)。如果设置为 <span class="hljs-string">'0'</span>,则令牌将永不过期--upload-certs将控制平面证书上传到 kubeadm-certs Secret。</code></pre></div><h3 id="master-节点扩容"><a href="#master-节点扩容" class="headerlink" title="master 节点扩容"></a>master 节点扩容</h3><p>在 ukm02 ukm03 上执行</p><div class="hljs"><pre><code class="hljs bash">ansible master:\!ans -m shell -a <span class="hljs-string">'kubeadm join 10.10.34.89:8443 \</span><span class="hljs-string"> --apiserver-bind-port 8443 \</span><span class="hljs-string"> --control-plane \</span><span class="hljs-string"> --token 7nf8wp.cefgemzgn5xcdmln \</span><span class="hljs-string"> --discovery-token-ca-cert-hash sha256:36b328e0052c7f8e7cac826d70b21bef0cdc4e0505fddc8200c52b8b01605da3 \</span><span class="hljs-string"> --control-plane --certificate-key bd9fc1600d2db4f2b323a17b2b7be4f4234d7ed8705bc64976dcd43d929a24a8'</span></code></pre></div><h3 id="kubeadm-join-常用参数"><a href="#kubeadm-join-常用参数" class="headerlink" title="kubeadm join 常用参数"></a>kubeadm join 常用参数</h3><div class="hljs"><pre><code class="hljs bash">--apiserver-advertise-address string如果该节点托管一个新的控制平面实例,则 API 服务器将公布其正在侦听的 IP 地址。如果未设置,则使用默认网络接口。--apiserver-bind-port int32 默认值: 6443如果节点应该托管新的控制平面实例,则为 API 服务器要绑定的端口。--certificate-key string使用此密钥可以解密由 init 上传的证书 secret。--config stringkubeadm 配置文件的路径。--control-plane在此节点上创建一个新的控制平面实例--cri-socket string要连接的 CRI 套接字的路径。如果为空,则 kubeadm 将尝试自动检测此值;仅当安装了多个 CRI 或具有非标准 CRI 插槽时,才使用此选项。--discovery-file string对于基于文件的发现,给出用于加载集群信息的文件或者 URL。--discovery-token string对于基于令牌的发现,该令牌用于验证从 API 服务器获取的集群信息。--discovery-token-ca-cert-hash stringSlice对基于令牌的发现,验证根 CA 公钥是否与此哈希匹配 (格式: <span class="hljs-string">"<type>:<value>"</span>)。--discovery-token-unsafe-skip-ca-verification对于基于令牌的发现,允许在未关联 --discovery-token-ca-cert-hash 参数的情况下添加节点。-k, --experimental-kustomize string用于存储 kustomize 为静态 pod 清单所提供的补丁的路径。-h, --<span class="hljs-built_in">help</span>join 操作的帮助命令--ignore-preflight-errors stringSlice错误将显示为警告的检查列表;例如:<span class="hljs-string">'IsPrivilegedUser,Swap'</span>。取值为 <span class="hljs-string">'all'</span> 时将忽略检查中的所有错误。--node-name string指定节点的名称--skip-phases stringSlice要跳过的阶段列表--tls-bootstrap-token string指定在加入节点时用于临时通过 Kubernetes 控制平面进行身份验证的令牌。--token string如果未提供这些值,则将它们用于 discovery-token 令牌和 tls-bootstrap 令牌。</code></pre></div><p>配置 master 管理命令</p><div class="hljs"><pre><code class="hljs bash">ansible master -m shell -a <span class="hljs-string">'echo "source <(kubectl completion bash)" >> ~/.bashrc'</span>ansible master -m shell -a <span class="hljs-string">'mkdir -p /root/.kube'</span>ansible master -m copy -a <span class="hljs-string">'src=/etc/kubernetes/admin.conf dest=/root/.kube/config mode=0600 owner=root remote_src=yes'</span></code></pre></div><h3 id="验证-master"><a href="#验证-master" class="headerlink" title="验证 master"></a>验证 master</h3><div class="hljs"><pre><code class="hljs bash"> kubectl get nodes -l node-role.kubernetes.io/masterNAME STATUS ROLES AGE VERSIONukm01 NotReady master 3m26s v1.17.3ukm02 NotReady master 1m56s v1.17.3ukm03 NotReady master 2m14s v1.17.3 kubectl get pods --all-namespacesNAMESPACE NAME READY STATUS RESTARTS AGEkube-system coredns-7f9c544f75-h9hmm 0/1 Pending 0 3m55skube-system coredns-7f9c544f75-k9pb7 0/1 Pending 0 3m55skube-system etcd-ukm01 1/1 Running 1 20skube-system etcd-ukm02 1/1 Running 0 40skube-system etcd-ukm03 1/1 Running 0 20skube-system kube-apiserver-ukm01 1/1 Running 0 20skube-system kube-apiserver-ukm02 1/1 Running 0 20skube-system kube-apiserver-ukm03 1/1 Running 0 20skube-system kube-controller-manager-ukm01 1/1 Running 3 5m17skube-system kube-controller-manager-ukm02 1/1 Running 0 74skube-system kube-controller-manager-ukm03 1/1 Running 2 3m27skube-system kube-proxy-4fkkx 1/1 Running 0 3m27skube-system kube-proxy-5vjpc 1/1 Running 0 2m9skube-system kube-proxy-plx2x 1/1 Running 0 3m55skube-system kube-scheduler-ukm01 1/1 Running 2 5m17skube-system kube-scheduler-ukm02 1/1 Running 0 75skube-system kube-scheduler-ukm03 1/1 Running 0 3m26s</code></pre></div><h3 id="确认-kube-proxy-启用了-ipvs-模式"><a href="#确认-kube-proxy-启用了-ipvs-模式" class="headerlink" title="确认 kube-proxy 启用了 ipvs 模式"></a>确认 kube-proxy 启用了 ipvs 模式</h3><div class="hljs"><pre><code class="hljs bash"> kubectl get configmaps kube-proxy -n kube-system -o yaml... iptables: masqueradeAll: <span class="hljs-literal">false</span> masqueradeBit: null minSyncPeriod: 0s syncPeriod: 0s ipvs: excludeCIDRs: null minSyncPeriod: 5s scheduler: wlc strictARP: <span class="hljs-literal">false</span> syncPeriod: 30s tcpFinTimeout: 0s tcpTimeout: 0s udpTimeout: 0s kind: KubeProxyConfiguration metricsBindAddress: <span class="hljs-string">""</span> mode: ipvs...</code></pre></div><h3 id="查看-ipvs-转发情况"><a href="#查看-ipvs-转发情况" class="headerlink" title="查看 ipvs 转发情况"></a>查看 ipvs 转发情况</h3><div class="hljs"><pre><code class="hljs bash"> ipvsadm -LnIP Virtual Server version 1.2.1 (size=4096)Prot LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forward Weight ActiveConn InActConnTCP 10.96.0.1:443 wlc -> 10.10.34.92:8443 Masq 1 0 0 -> 10.10.34.93:8443 Masq 1 0 0 -> 10.10.34.94:8443 Masq 1 0 0TCP 10.96.0.10:53 wlcTCP 10.96.0.10:9153 wlcUDP 10.96.0.10:53 wlc</code></pre></div><h3 id="查看网卡信息"><a href="#查看网卡信息" class="headerlink" title="查看网卡信息"></a>查看网卡信息</h3><div class="hljs"><pre><code class="hljs bash"> ip addr1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 link/ether fa:16:3e:cf:28:7f brd ff:ff:ff:ff:ff:ff inet 10.10.34.92/24 brd 10.10.34.255 scope global eth0 valid_lft forever preferred_lft forever inet6 fe80::f816:3eff:fecf:287f/64 scope link valid_lft forever preferred_lft forever3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default link/ether 02:42:81:5a:e2:7c brd ff:ff:ff:ff:ff:ff inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0 valid_lft forever preferred_lft forever inet6 fe80::42:81ff:fe5a:e27c/64 scope link valid_lft forever preferred_lft forever6: tunl0@NONE: <NOARP,UP,LOWER_UP> mtu 1440 qdisc noqueue state UNKNOWN group default qlen 1000 link/ipip 0.0.0.0 brd 0.0.0.046: kube-ipvs0: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN group default link/ether 6a:10:8f:81:3e:61 brd ff:ff:ff:ff:ff:ff inet 10.96.0.10/32 brd 10.96.0.10 scope global kube-ipvs0 valid_lft forever preferred_lft forever inet 10.96.0.1/32 brd 10.96.0.1 scope global kube-ipvs0 valid_lft forever preferred_lft forever</code></pre></div><h3 id="更新-etcd-集群配置"><a href="#更新-etcd-集群配置" class="headerlink" title="更新 etcd 集群配置"></a>更新 etcd 集群配置</h3><div class="hljs"><pre><code class="hljs bash">ansible master -m shell -a <span class="hljs-string">'sed -i "/--initial-cluster=/c\ - --initial-cluster=ukm01=https://10.10.34.92:2380,ukm02=https://10.10.34.93:2380,ukm03=https://10.10.34.94:2380" /etc/kubernetes/manifests/etcd.yaml'</span>apiserver 默认访问本地的 etcd 节点,如果需要访问集群所有节点可以修改配置。ansible master -m shell -a <span class="hljs-string">'sed -i "/- --etcd-servers/c\ - --etcd-servers=https://10.10.34.92:2379,https://10.10.34.93:2379,https://10.10.34.94:2379" /etc/kubernetes/manifests/kube-apiserver.yaml'</span></code></pre></div><h3 id="查看-etcd-成员状态"><a href="#查看-etcd-成员状态" class="headerlink" title="查看 etcd 成员状态"></a>查看 etcd 成员状态</h3><p>成员管理命令:</p><ul><li>add - 添加集群成员</li><li>list - 查看集群成员信息</li><li>promote - 提升集群成员权限</li><li>remove - 从集群中删除成员ID</li><li>update - 更新集群成员信息</li></ul><div class="hljs"><pre><code class="hljs bash"> kubectl <span class="hljs-built_in">exec</span> -n kube-system -it $(kubectl get pod -n kube-system -l component=etcd -o jsonpath=<span class="hljs-string">'{.items[0].metadata.name}'</span>) sh 或者下载 etcdctl curl -L -O https://github.com/etcd-io/etcd/releases/download/v3.4.3/etcd-v3.4.3-linux-amd64.tar.gz tar xzvf etcd-v3.4.3-linux-amd64.tar.gz mv etcd-v3.4.3-linux-amd64/etcd* /usr/<span class="hljs-built_in">local</span>/bin/ ETCDCTL=3 etcdctl --write-out=table \ --cert /etc/kubernetes/pki/etcd/peer.crt \ --key /etc/kubernetes/pki/etcd/peer.key \ --cacert /etc/kubernetes/pki/etcd/ca.crt \ --endpoints https://ukm01:2379,https://ukm02:2379,https://ukm03:2379 \ member list+------------------+---------+-------+--------------------------+--------------------------+------------+| ID | STATUS | NAME | PEER ADDRS | CLIENT ADDRS | IS LEARNER |+------------------+---------+-------+--------------------------+--------------------------+------------+| 7a212ff0c04f1e82 | started | ukm02 | https://10.10.34.93:2380 | https://10.10.34.93:2379 | <span class="hljs-literal">false</span> || ae758756c0c6b09c | started | ukm01 | https://10.10.34.92:2380 | https://10.10.34.92:2379 | <span class="hljs-literal">false</span> || ff90415307bdc341 | started | ukm03 | https://10.10.34.94:2380 | https://10.10.34.94:2379 | <span class="hljs-literal">false</span> |+------------------+---------+-------+--------------------------+--------------------------+------------+</code></pre></div><h3 id="验证-etcd-集群状态"><a href="#验证-etcd-集群状态" class="headerlink" title="验证 etcd 集群状态"></a>验证 etcd 集群状态</h3><div class="hljs"><pre><code class="hljs bash"> ETCDCTL=3 etcdctl --cluster --write-out=table \ --cert /etc/kubernetes/pki/etcd/peer.crt \ --key /etc/kubernetes/pki/etcd/peer.key \ --cacert /etc/kubernetes/pki/etcd/ca.crt \ --endpoints https://ukm01:2379,https://ukm02:2379,https://ukm03:2379 \ endpoint status+--------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+| ENDPOINT | ID | VERSION | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS |+--------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+| https://10.10.34.94:2379 | 4677521bc1465d7e | 3.4.3 | 1.5 MB | <span class="hljs-literal">false</span> | <span class="hljs-literal">false</span> | 9 | 1671 | 1671 | || https://10.10.34.92:2379 | ae758756c0c6b09c | 3.4.3 | 1.6 MB | <span class="hljs-literal">false</span> | <span class="hljs-literal">false</span> | 9 | 1671 | 1671 | || https://10.10.34.93:2379 | ff3c0a769521e2c2 | 3.4.3 | 1.5 MB | <span class="hljs-literal">true</span> | <span class="hljs-literal">false</span> | 9 | 1671 | 1671 | |+--------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+</code></pre></div><h3 id="查看-etcd-节点健康状况"><a href="#查看-etcd-节点健康状况" class="headerlink" title="查看 etcd 节点健康状况"></a>查看 etcd 节点健康状况</h3><div class="hljs"><pre><code class="hljs bash"> ETCDCTL=3 etcdctl --cluster --write-out=table \ --cert /etc/kubernetes/pki/etcd/peer.crt \ --key /etc/kubernetes/pki/etcd/peer.key \ --cacert /etc/kubernetes/pki/etcd/ca.crt \ --endpoints https://ukm01:2379,https://ukm02:2379,https://ukm03:2379 \ endpoint health+--------------------------+--------+-------------+-------+| ENDPOINT | HEALTH | TOOK | ERROR |+--------------------------+--------+-------------+-------+| https://10.10.34.93:2379 | <span class="hljs-literal">true</span> | 18.384799ms | || https://10.10.34.94:2379 | <span class="hljs-literal">true</span> | 18.649393ms | || https://10.10.34.92:2379 | <span class="hljs-literal">true</span> | 18.831792ms | |+--------------------------+--------+-------------+-------+</code></pre></div><h3 id="增加-node-节点"><a href="#增加-node-节点" class="headerlink" title="增加 node 节点"></a>增加 node 节点</h3><p>在 uki01 uki02 uki03 ukn01 ukn02 ukn03 上执行</p><div class="hljs"><pre><code class="hljs bash">ansible infra:worker -m shell -a <span class="hljs-string">'kubeadm join 10.10.34.89:8443 \</span><span class="hljs-string"> --token 7nf8wp.cefgemzgn5xcdmln \</span><span class="hljs-string"> --discovery-token-ca-cert-hash sha256:36b328e0052c7f8e7cac826d70b21bef0cdc4e0505fddc8200c52b8b01605da3'</span></code></pre></div><h2 id="SDN"><a href="#SDN" class="headerlink" title="SDN"></a><strong>SDN</strong></h2><h3 id="安装-calico"><a href="#安装-calico" class="headerlink" title="安装 calico"></a>安装 calico</h3><div class="hljs"><pre><code class="hljs bash">curl -L https://docs.projectcalico.org/manifests/calico.yaml -o calico.yamlkubectl apply -f calico.yaml</code></pre></div><h3 id="查看服务状态"><a href="#查看服务状态" class="headerlink" title="查看服务状态"></a>查看服务状态</h3><ul><li>node 节点处于 Ready 状态</li><li>每个节点上都有一个 pod:calico-node</li></ul><div class="hljs"><pre><code class="hljs bash"> kubectl get nodesNAME STATUS ROLES AGE VERSIONuki01 Ready infra 5m v1.17.3uki02 Ready infra 5m v1.17.3uki03 Ready infra 5m v1.17.3ukm01 Ready master 8m v1.17.3ukm02 Ready master 8m v1.17.3ukm03 Ready master 8m v1.17.3ukn01 Ready worker 5m v1.17.3ukn02 Ready worker 5m v1.17.3ukn03 Ready worker 5m v1.17.3 watch kubectl get pods -n kube-system -o wideNAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES...calico-kube-controllers-5b644bc49c-7xjzr 1/1 Running 0 30s 192.168.144.138 uki01 <none> <none>calico-node-57rns 1/1 Running 0 30s 10.10.34.98 ukn01 <none> <none>calico-node-6p8z7 1/1 Running 0 30s 10.10.34.100 ukn03 <none> <none>calico-node-77jwm 1/1 Running 0 30s 172.17.0.1 ukn02 <none> <none>calico-node-c4gxf 1/1 Running 0 30s 10.10.34.95 uki01 <none> <none>calico-node-gdb5j 1/1 Running 0 30s 10.10.34.96 uki02 <none> <none>calico-node-gmzmf 1/1 Running 0 30s 10.10.34.93 ukm02 <none> <none>calico-node-jdd2f 1/1 Running 0 30s 10.10.34.97 uki03 <none> <none>calico-node-mrr49 1/1 Running 0 30s 10.10.34.92 ukm01 <none> <none>calico-node-w5b7r 1/1 Running 0 30s 10.10.34.94 ukm03 <none> <none>... kubectl get ippool -o wideNAME AGEdefault-ipv4-ippool 2m</code></pre></div><h3 id="验证-calico"><a href="#验证-calico" class="headerlink" title="验证 calico"></a>验证 calico</h3><div class="hljs"><pre><code class="hljs bash"> 下载 calicoctl curl -L -o /usr/<span class="hljs-built_in">local</span>/bin/calicoctl https://github.com/projectcalico/calicoctl/releases/download/v3.11.2/calicoctl-linux-amd64 chmod +x /usr/<span class="hljs-built_in">local</span>/bin/calicoctl 使用 kubernetes api 存储数据 mkdir -p /etc/calico cat > /etc/calico/calicoctl.cfg <<EOFapiVersion: projectcalico.org/v3kind: CalicoAPIConfigmetadata:spec: datastoreType: kubernetes kubeconfig: /etc/kubernetes/admin.conf 使用 admin 配置 k8sAPIEndpoint: https://10.10.34.89:8443 k8sCertFile: /etc/kubernetes/pki/apiserver-kubelet-client.crt k8sKeyFile: /etc/kubernetes/pki/apiserver-kubelet-client.key k8sCAFile: /etc/kubernetes/pki/ca.crtEOF 使用独立的 etcd 存储数据 mkdir -p /etc/calico cat > /etc/calico/calicoctl.cfg <<EOFapiVersion: projectcalico.org/v3kind: CalicoAPIConfigmetadata:spec: datastoreType: etcdv3 etcdEndpoints: https://10.10.34.92:2379,https://10.10.34.93:2379,https://10.10.34.94:2379 etcdKeyFile: /etc/kubernetes/pki/etcd/peer.key etcdCertFile: /etc/kubernetes/pki/etcd/peer.crt etcdCACertFile: /etc/kubernetes/pki/etcd/ca.crtEOF 查看 node calicoctl node statusCalico process is running.IPv4 BGP status+--------------+-------------------+-------+------------+-------------+| PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO |+--------------+-------------------+-------+------------+-------------+| 10.10.34.95 | node-to-node mesh | up | 2020-04-11 | Established || 10.10.34.96 | node-to-node mesh | up | 2020-04-11 | Established || 10.10.34.97 | node-to-node mesh | up | 2020-04-11 | Established || 10.10.34.93 | node-to-node mesh | up | 2020-04-11 | Established || 10.10.34.94 | node-to-node mesh | up | 2020-04-11 | Established || 10.10.34.98 | node-to-node mesh | up | 2020-04-11 | Established || 10.10.34.99 | node-to-node mesh | up | 2020-04-11 | Established || 10.10.34.100 | node-to-node mesh | up | 2020-04-11 | Established |+--------------+-------------------+-------+------------+-------------+IPv6 BGP statusNo IPv6 peers found. calicoctl get node -o wideNAME ASN IPV4 IPV6uki01 (64512) 10.10.34.95/24uki02 (64512) 10.10.34.96/24uki03 (64512) 10.10.34.97/24ukm01 (64512) 10.10.34.92/24ukm02 (64512) 10.10.34.93/24ukm03 (64512) 10.10.34.94/24ukn01 (64512) 10.10.34.98/24ukn02 (64512) 10.10.34.99/24ukn03 (64512) 10.10.34.100/24 查看 ippool calicoctl get ippool -o wideNAME CIDR NAT IPIPMODE VXLANMODE DISABLED SELECTORdefault-ipv4-ippool 192.168.0.0/16 <span class="hljs-literal">true</span> Always Never <span class="hljs-literal">false</span> all() 查看 ip 状态 calicoctl ipam show+----------+----------------+-----------+------------+--------------+| GROUPING | CIDR | IPS TOTAL | IPS IN USE | IPS FREE |+----------+----------------+-----------+------------+--------------+| IP Pool | 192.168.0.0/16 | 65536 | 5 (0%) | 65531 (100%) |+----------+----------------+-----------+------------+--------------+ 查看已分配的服务节点信息 calicoctl get workloadendpoints -o wide -n kube-systemNAMESPACE NAME WORKLOAD NODE NETWORKS INTERFACE PROFILES NATSkube-system ukn02-k8s-calico--kube--controllers--75d56dfc47--xfdp9-eth0 calico-kube-controllers-75d56dfc47-xfdp9 ukn02 192.168.33.65/32 calif1d8798ea15 kns.kube-system,ksa.kube-system.calico-kube-controllerskube-system uki02-k8s-coredns--546565776c--mkx9n-eth0 coredns-546565776c-mkx9n uki02 192.168.225.1/32 calib399f7859db kns.kube-system,ksa.kube-system.corednskube-system ukn03-k8s-coredns--546565776c--v9wgw-eth0 coredns-546565776c-v9wgw ukn03 192.168.188.129/32 cali4fffc74a7b2 kns.kube-system,ksa.kube-system.coredns</code></pre></div><h3 id="calico-网络资源"><a href="#calico-网络资源" class="headerlink" title="calico 网络资源"></a>calico 网络资源</h3><p>官方文档: <code>https://docs.projectcalico.org/reference/resources/overview</code></p><ul><li>BGPConfiguration</li></ul><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-meta">---</span><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">projectcalico.org/v3</span><span class="hljs-attr">kind:</span> <span class="hljs-string">BGPConfiguration</span><span class="hljs-attr">metadata:</span> <span class="hljs-attr">name:</span> <span class="hljs-string">default</span><span class="hljs-attr">spec:</span> <span class="hljs-attr">logSeverityScreen:</span> <span class="hljs-string">Info</span> <span class="hljs-attr">nodeToNodeMeshEnabled:</span> <span class="hljs-literal">true</span> <span class="hljs-attr">asNumber:</span> <span class="hljs-number">63400</span> <span class="hljs-attr">serviceClusterIPs:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">cidr:</span> <span class="hljs-number">10.96</span><span class="hljs-number">.0</span><span class="hljs-number">.0</span><span class="hljs-string">/12</span> <span class="hljs-attr">serviceExternalIPs:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">cidr:</span> <span class="hljs-number">104.244</span><span class="hljs-number">.42</span><span class="hljs-number">.129</span><span class="hljs-string">/32</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">cidr:</span> <span class="hljs-number">172.217</span><span class="hljs-number">.3</span><span class="hljs-number">.0</span><span class="hljs-string">/24</span></code></pre></div><ul><li>BGPPeer</li></ul><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-meta">---</span><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">projectcalico.org/v3</span><span class="hljs-attr">kind:</span> <span class="hljs-string">BGPPeer</span><span class="hljs-attr">metadata:</span> <span class="hljs-attr">name:</span> <span class="hljs-string">some.name</span><span class="hljs-attr">spec:</span> <span class="hljs-attr">node:</span> <span class="hljs-string">rack1-host1</span> <span class="hljs-attr">peerIP:</span> <span class="hljs-number">192.168</span><span class="hljs-number">.1</span><span class="hljs-number">.1</span> <span class="hljs-attr">asNumber:</span> <span class="hljs-number">63400</span></code></pre></div><ul><li>FelixConfiguration</li></ul><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-meta">---</span><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">projectcalico.org/v3</span><span class="hljs-attr">kind:</span> <span class="hljs-string">FelixConfiguration</span><span class="hljs-attr">metadata:</span> <span class="hljs-attr">name:</span> <span class="hljs-string">default</span><span class="hljs-attr">spec:</span> <span class="hljs-attr">ipv6Support:</span> <span class="hljs-literal">false</span> <span class="hljs-attr">ipipMTU:</span> <span class="hljs-number">1400</span> <span class="hljs-attr">chainInsertMode:</span> <span class="hljs-string">Append</span></code></pre></div><ul><li>GlobalNetworkPolicy</li></ul><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-meta">---</span><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">projectcalico.org/v3</span><span class="hljs-attr">kind:</span> <span class="hljs-string">GlobalNetworkPolicy</span><span class="hljs-attr">metadata:</span> <span class="hljs-attr">name:</span> <span class="hljs-string">allow-tcp-6379</span><span class="hljs-attr">spec:</span> <span class="hljs-attr">selector:</span> <span class="hljs-string">role</span> <span class="hljs-string">==</span> <span class="hljs-string">'database'</span> <span class="hljs-attr">types:</span> <span class="hljs-bullet">-</span> <span class="hljs-string">Ingress</span> <span class="hljs-bullet">-</span> <span class="hljs-string">Egress</span> <span class="hljs-attr">ingress:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">action:</span> <span class="hljs-string">Allow</span> <span class="hljs-attr">metadata:</span> <span class="hljs-attr">annotations:</span> <span class="hljs-attr">from:</span> <span class="hljs-string">frontend</span> <span class="hljs-attr">to:</span> <span class="hljs-string">database</span> <span class="hljs-attr">protocol:</span> <span class="hljs-string">TCP</span> <span class="hljs-attr">source:</span> <span class="hljs-attr">selector:</span> <span class="hljs-string">role</span> <span class="hljs-string">==</span> <span class="hljs-string">'frontend'</span> <span class="hljs-attr">destination:</span> <span class="hljs-attr">ports:</span> <span class="hljs-bullet">-</span> <span class="hljs-number">6379</span> <span class="hljs-attr">egress:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">action:</span> <span class="hljs-string">Allow</span></code></pre></div><ul><li>GlobalNetworkSet</li></ul><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-meta">---</span><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">projectcalico.org/v3</span><span class="hljs-attr">kind:</span> <span class="hljs-string">GlobalNetworkSet</span><span class="hljs-attr">metadata:</span> <span class="hljs-attr">name:</span> <span class="hljs-string">a-name-for-the-set</span> <span class="hljs-attr">labels:</span> <span class="hljs-attr">role:</span> <span class="hljs-string">external-database</span><span class="hljs-attr">spec:</span> <span class="hljs-attr">nets:</span> <span class="hljs-bullet">-</span> <span class="hljs-number">198.51</span><span class="hljs-number">.100</span><span class="hljs-number">.0</span><span class="hljs-string">/28</span> <span class="hljs-bullet">-</span> <span class="hljs-number">203.0</span><span class="hljs-number">.113</span><span class="hljs-number">.0</span><span class="hljs-string">/24</span></code></pre></div><ul><li>HostEndpoint</li></ul><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-meta">---</span><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">projectcalico.org/v3</span><span class="hljs-attr">kind:</span> <span class="hljs-string">HostEndpoint</span><span class="hljs-attr">metadata:</span> <span class="hljs-attr">name:</span> <span class="hljs-string">some.name</span> <span class="hljs-attr">labels:</span> <span class="hljs-attr">type:</span> <span class="hljs-string">production</span><span class="hljs-attr">spec:</span> <span class="hljs-attr">interfaceName:</span> <span class="hljs-string">eth0</span> <span class="hljs-attr">node:</span> <span class="hljs-string">myhost</span> <span class="hljs-attr">expectedIPs:</span> <span class="hljs-bullet">-</span> <span class="hljs-number">192.168</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span> <span class="hljs-bullet">-</span> <span class="hljs-number">192.168</span><span class="hljs-number">.0</span><span class="hljs-number">.2</span> <span class="hljs-attr">profiles:</span> <span class="hljs-bullet">-</span> <span class="hljs-string">profile1</span> <span class="hljs-bullet">-</span> <span class="hljs-string">profile2</span> <span class="hljs-attr">ports:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">some-port</span> <span class="hljs-attr">port:</span> <span class="hljs-number">1234</span> <span class="hljs-attr">protocol:</span> <span class="hljs-string">TCP</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">another-port</span> <span class="hljs-attr">port:</span> <span class="hljs-number">5432</span> <span class="hljs-attr">protocol:</span> <span class="hljs-string">UDP</span></code></pre></div><ul><li>IPPool</li></ul><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-meta">---</span><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">projectcalico.org/v3</span><span class="hljs-attr">kind:</span> <span class="hljs-string">IPPool</span><span class="hljs-attr">metadata:</span> <span class="hljs-attr">name:</span> <span class="hljs-string">my.ippool-1</span><span class="hljs-attr">spec:</span> <span class="hljs-attr">cidr:</span> <span class="hljs-number">10.1</span><span class="hljs-number">.0</span><span class="hljs-number">.0</span><span class="hljs-string">/16</span> <span class="hljs-attr">ipipMode:</span> <span class="hljs-string">CrossSubnet</span> <span class="hljs-attr">natOutgoing:</span> <span class="hljs-literal">true</span> <span class="hljs-attr">disabled:</span> <span class="hljs-literal">false</span> <span class="hljs-attr">nodeSelector:</span> <span class="hljs-string">all()</span></code></pre></div><ul><li>NetworkPolicy</li></ul><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-meta">---</span><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">projectcalico.org/v3</span><span class="hljs-attr">kind:</span> <span class="hljs-string">NetworkPolicy</span><span class="hljs-attr">metadata:</span> <span class="hljs-attr">name:</span> <span class="hljs-string">allow-tcp-6379</span> <span class="hljs-attr">namespace:</span> <span class="hljs-string">production</span><span class="hljs-attr">spec:</span> <span class="hljs-attr">selector:</span> <span class="hljs-string">role</span> <span class="hljs-string">==</span> <span class="hljs-string">'database'</span> <span class="hljs-attr">types:</span> <span class="hljs-bullet">-</span> <span class="hljs-string">Ingress</span> <span class="hljs-bullet">-</span> <span class="hljs-string">Egress</span> <span class="hljs-attr">ingress:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">action:</span> <span class="hljs-string">Allow</span> <span class="hljs-attr">metadata:</span> <span class="hljs-attr">annotations:</span> <span class="hljs-attr">from:</span> <span class="hljs-string">frontend</span> <span class="hljs-attr">to:</span> <span class="hljs-string">database</span> <span class="hljs-attr">protocol:</span> <span class="hljs-string">TCP</span> <span class="hljs-attr">source:</span> <span class="hljs-attr">selector:</span> <span class="hljs-string">role</span> <span class="hljs-string">==</span> <span class="hljs-string">'frontend'</span> <span class="hljs-attr">destination:</span> <span class="hljs-attr">ports:</span> <span class="hljs-bullet">-</span> <span class="hljs-number">6379</span> <span class="hljs-attr">egress:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">action:</span> <span class="hljs-string">Allow</span></code></pre></div><ul><li>NetworkSet</li></ul><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-meta">---</span><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">projectcalico.org/v3</span><span class="hljs-attr">kind:</span> <span class="hljs-string">NetworkSet</span><span class="hljs-attr">metadata:</span> <span class="hljs-attr">name:</span> <span class="hljs-string">external-database</span> <span class="hljs-attr">namespace:</span> <span class="hljs-string">staging</span> <span class="hljs-attr">labels:</span> <span class="hljs-attr">role:</span> <span class="hljs-string">db</span><span class="hljs-attr">spec:</span> <span class="hljs-attr">nets:</span> <span class="hljs-bullet">-</span> <span class="hljs-number">198.51</span><span class="hljs-number">.100</span><span class="hljs-number">.0</span><span class="hljs-string">/28</span> <span class="hljs-bullet">-</span> <span class="hljs-number">203.0</span><span class="hljs-number">.113</span><span class="hljs-number">.0</span><span class="hljs-string">/24</span></code></pre></div><ul><li>Node</li></ul><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-meta">---</span><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">projectcalico.org/v3</span><span class="hljs-attr">kind:</span> <span class="hljs-string">Node</span><span class="hljs-attr">metadata:</span> <span class="hljs-attr">name:</span> <span class="hljs-string">node-hostname</span><span class="hljs-attr">spec:</span> <span class="hljs-attr">bgp:</span> <span class="hljs-attr">asNumber:</span> <span class="hljs-number">64512</span> <span class="hljs-attr">ipv4Address:</span> <span class="hljs-number">10.244</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span><span class="hljs-string">/24</span> <span class="hljs-attr">ipv6Address:</span> <span class="hljs-number">2001</span><span class="hljs-string">:db8:85a3::8a2e:370:7334/120</span> <span class="hljs-attr">ipv4IPIPTunnelAddr:</span> <span class="hljs-number">192.168</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span></code></pre></div><ul><li>Profile</li></ul><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-meta">---</span><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">projectcalico.org/v3</span><span class="hljs-attr">kind:</span> <span class="hljs-string">Profile</span><span class="hljs-attr">metadata:</span> <span class="hljs-attr">name:</span> <span class="hljs-string">dev-apps</span><span class="hljs-attr">spec:</span> <span class="hljs-attr">ingress:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">action:</span> <span class="hljs-string">Deny</span> <span class="hljs-attr">source:</span> <span class="hljs-attr">nets:</span> <span class="hljs-bullet">-</span> <span class="hljs-number">10.0</span><span class="hljs-number">.20</span><span class="hljs-number">.0</span><span class="hljs-string">/24</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">action:</span> <span class="hljs-string">Allow</span> <span class="hljs-attr">source:</span> <span class="hljs-attr">selector:</span> <span class="hljs-string">stage</span> <span class="hljs-string">==</span> <span class="hljs-string">'development'</span> <span class="hljs-attr">egress:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">action:</span> <span class="hljs-string">Allow</span> <span class="hljs-attr">labelsToApply:</span> <span class="hljs-attr">stage:</span> <span class="hljs-string">development</span></code></pre></div><ul><li>WorkloadEndpoint</li></ul><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-meta">---</span><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">projectcalico.org/v3</span><span class="hljs-attr">kind:</span> <span class="hljs-string">WorkloadEndpoint</span><span class="hljs-attr">metadata:</span> <span class="hljs-attr">name:</span> <span class="hljs-string">node1-k8s-my--nginx--b1337a-eth0</span> <span class="hljs-attr">namespace:</span> <span class="hljs-string">default</span> <span class="hljs-attr">labels:</span> <span class="hljs-attr">app:</span> <span class="hljs-string">frontend</span> <span class="hljs-attr">projectcalico.org/namespace:</span> <span class="hljs-string">default</span> <span class="hljs-attr">projectcalico.org/orchestrator:</span> <span class="hljs-string">k8s</span><span class="hljs-attr">spec:</span> <span class="hljs-attr">node:</span> <span class="hljs-string">node1</span> <span class="hljs-attr">orchestrator:</span> <span class="hljs-string">k8s</span> <span class="hljs-attr">endpoint:</span> <span class="hljs-string">eth0</span> <span class="hljs-attr">containerID:</span> <span class="hljs-number">1337495556942031415926535</span> <span class="hljs-attr">pod:</span> <span class="hljs-string">my-nginx-b1337a</span> <span class="hljs-attr">endpoint:</span> <span class="hljs-string">eth0</span> <span class="hljs-attr">interfaceName:</span> <span class="hljs-string">cali0ef24ba</span> <span class="hljs-attr">mac:</span> <span class="hljs-string">ca:fe:1d:52:bb:e9</span> <span class="hljs-attr">ipNetworks:</span> <span class="hljs-bullet">-</span> <span class="hljs-number">192.168</span><span class="hljs-number">.0</span><span class="hljs-number">.0</span><span class="hljs-string">/32</span> <span class="hljs-attr">profiles:</span> <span class="hljs-bullet">-</span> <span class="hljs-string">profile1</span> <span class="hljs-attr">ports:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">some-port</span> <span class="hljs-attr">port:</span> <span class="hljs-number">1234</span> <span class="hljs-attr">protocol:</span> <span class="hljs-string">TCP</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">another-port</span> <span class="hljs-attr">port:</span> <span class="hljs-number">5432</span> <span class="hljs-attr">protocol:</span> <span class="hljs-string">UDP</span></code></pre></div><h3 id="卸载-calico"><a href="#卸载-calico" class="headerlink" title="卸载 calico"></a>卸载 calico</h3><div class="hljs"><pre><code class="hljs bash">kubectl delete -f calico.yaml</code></pre></div><h3 id="删除-calico-配置"><a href="#删除-calico-配置" class="headerlink" title="删除 calico 配置"></a>删除 calico 配置</h3><div class="hljs"><pre><code class="hljs bash">ansible ins -m shell -a <span class="hljs-string">'rm -rf /etc/cni /opt/cni/ /var/lib/calico/'</span></code></pre></div><h2 id="集群校验"><a href="#集群校验" class="headerlink" title="集群校验"></a>集群校验</h2><h3 id="查看集群信息"><a href="#查看集群信息" class="headerlink" title="查看集群信息"></a>查看集群信息</h3><div class="hljs"><pre><code class="hljs bash"> kubectl config get-clustersNAMEshenzhen kubectl cluster-infoKubernetes master is running at https://10.10.34.89:8443KubeDNS is running at https://10.10.34.89:8443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy</code></pre></div><h3 id="查看-namespace"><a href="#查看-namespace" class="headerlink" title="查看 namespace"></a>查看 namespace</h3><div class="hljs"><pre><code class="hljs bash"> kubectl get namespacesNAME STATUS AGEdefault Active 15mkube-node-lease Active 15mkube-public Active 15mkube-system Active 15m</code></pre></div><h3 id="查看节点状态"><a href="#查看节点状态" class="headerlink" title="查看节点状态"></a>查看节点状态</h3><div class="hljs"><pre><code class="hljs bash"> kubectl get nodesNAME STATUS ROLES AGE VERSIONuki01 NotReady <none> 7m v1.17.3uki02 NotReady <none> 7m v1.17.3uki03 NotReady <none> 7m v1.17.3ukm01 NotReady master 10m v1.17.3ukm02 NotReady master 14m v1.17.3ukm03 NotReady master 13m v1.17.3ukn01 NotReady <none> 7m v1.17.3ukn02 NotReady <none> 7m v1.17.3ukn03 NotReady <none> 7m v1.17.3</code></pre></div><h3 id="添加-infra-标签"><a href="#添加-infra-标签" class="headerlink" title="添加 infra 标签"></a>添加 infra 标签</h3><div class="hljs"><pre><code class="hljs bash"> kubectl label node uki01 uki02 uki03 node-role.kubernetes.io/infra=<span class="hljs-literal">true</span>node/uki01 labelednode/uki02 labelednode/uki03 labeled kubectl get nodes -l node-role.kubernetes.io/infraNAME STATUS ROLES AGE VERSIONuki01 NotReady infra 8m v1.17.3uki02 NotReady infra 8m v1.17.3uki03 NotReady infra 8m v1.17.3</code></pre></div><h3 id="添加-worker-标签"><a href="#添加-worker-标签" class="headerlink" title="添加 worker 标签"></a>添加 worker 标签</h3><div class="hljs"><pre><code class="hljs bash"> kubectl label node ukn01 ukn02 ukn03 node-role.kubernetes.io/worker=<span class="hljs-literal">true</span>node/ukn01 labelednode/ukn02 labelednode/ukn03 labeled kubectl get nodes -l node-role.kubernetes.io/workerNAME STATUS ROLES AGE VERSIONukn01 NotReady worker 8m v1.17.3ukn02 NotReady worker 8m v1.17.3ukn03 NotReady worker 8m v1.17.3</code></pre></div><h3 id="查看所有节点标签"><a href="#查看所有节点标签" class="headerlink" title="查看所有节点标签"></a>查看所有节点标签</h3><div class="hljs"><pre><code class="hljs bash"> kubectl get nodes --show-labelsNAME STATUS ROLES AGE VERSION LABELSuki01 NotReady infra 10m v1.17.3 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=uki01,kubernetes.io/os=linux,node-role.kubernetes.io/infra=<span class="hljs-literal">true</span>uki02 NotReady infra 10m v1.17.3 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=uki02,kubernetes.io/os=linux,node-role.kubernetes.io/infra=<span class="hljs-literal">true</span>uki03 NotReady infra 10m v1.17.3 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=uki03,kubernetes.io/os=linux,node-role.kubernetes.io/infra=<span class="hljs-literal">true</span>ukm01 NotReady master 19m v1.17.3 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=ukm01,kubernetes.io/os=linux,node-role.kubernetes.io/master=ukm02 NotReady master 17m v1.17.3 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=ukm02,kubernetes.io/os=linux,node-role.kubernetes.io/master=ukm03 NotReady master 15m v1.17.3 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=ukm03,kubernetes.io/os=linux,node-role.kubernetes.io/master=ukn01 NotReady worker 10m v1.17.3 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=ukn01,kubernetes.io/os=linux,node-role.kubernetes.io/worker=<span class="hljs-literal">true</span>ukn02 NotReady worker 10m v1.17.3 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=ukn02,kubernetes.io/os=linux,node-role.kubernetes.io/worker=<span class="hljs-literal">true</span>ukn03 NotReady worker 10m v1.17.3 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=ukn03,kubernetes.io/os=linux,node-role.kubernetes.io/worker=<span class="hljs-literal">true</span></code></pre></div><h3 id="删除节点标签"><a href="#删除节点标签" class="headerlink" title="删除节点标签"></a>删除节点标签</h3><div class="hljs"><pre><code class="hljs bash">kubectl label node uki01 uki02 uki03 node-role.kubernetes.io/infra-</code></pre></div><h3 id="kubectl-简介"><a href="#kubectl-简介" class="headerlink" title="kubectl 简介"></a>kubectl 简介</h3><div class="hljs"><pre><code class="hljs docs">基础命令: create 从文件或 stdin 创建资源 expose 为 deployment、pod 创建 Service run 使用 image 去创建一个服务 set 更新resource ,比如更新env环境变量,image,resources 资源限制,selector subject等。管理命令: explain 查看资源定义(文档)。如 kubectl explain replicaset get 最基本的对象查询命令。如 kubectl get nodes/pods/deploy/rs/ns/secret等等, 加-o wide查看详细信息,-o yaml 或-o json 输出具体格式。 edit 编辑资源配置,完成对象的更新。 delete 删除指定资源,支持文件名、资源名、label selector。部署命令: rollout Deployment, Daemonset的升级过程管理(查看状态status、操作历史history、暂停升级、恢复升级、回滚等) scale 修改Deployment, ReplicaSet, Replication Controller, Job的实例数,实现一个副本集的手工扩展 autoscale 为Deploy, RS, RC配置自动伸缩规则(依赖heapster和hpa)集群管理命令: certificate 修改证书资源 cluster-info 查看集群信息 top 查看资源占用率 (CPU/Memory/Storage) cordon 标记节点为不可调度状态 uncordon 标记节点为可调度状态 drain 驱逐节点上的 pod ,准备下线维护 taint 设置节点污点隔离标记故障诊断命令: describe 查看资源详情 logs 查看 pod 内容器的日志 attach Attach 到 pod 内的一个容器 exec 进入指定容器执行命令 port-forward 为 pod 创建本地端口映射 proxy 为 Kubernetes API server 创建代理 cp 容器内外/容器间文件拷贝 auth 授权校验高级命令: diff 比较当前版本和要部署的版本 apply 从文件或stdin创建/更新资源 patch 使用strategic merge patch语法更新对象的某些字段 replace 从文件或stdin更新资源 wait 实验功能:等待一种或多种资源的特定条件。 convert 在不同API版本之间转换对象定义 kustomize 从指定目录或远程URL构建Kustomization目标。配置命令: label 给资源设置label annotate 给资源设置annotation completion 获取shell自动补全脚本 (bash or zsh)其他命令: alpha Alpha中功能的命令 api-resources 打印当前 kubernetes API 所支持的命令资源 api-versions 打印当前 kubernetes API 所支持的 api 接口以及版本信息 config 修改kubectl配置(kubeconfig文件),如context。 plugin 提供用于与插件交互的插件程序。 version 打印当前 kubernetes 的服务端和客户端版本</code></pre></div><h3 id="调度策略-Scheduling-Policies"><a href="#调度策略-Scheduling-Policies" class="headerlink" title="调度策略 Scheduling Policies"></a>调度策略 Scheduling Policies</h3><p>kube-scheduler 给一个 pod 做调度选择包含两个步骤:</p><ul><li><p>过滤 Filtering<br>过滤策略会检查候选 Node 是否满足 Pod 的创建需求;<br>在过滤之后得出一个 Node 列表,里面包含了所有可调度节点;<br>如果为空,代表这个 Pod 不可调度。</p></li><li><p>打分 Scoring<br>根据当前启用的打分规则,调度器会给每一个可调度节点进行打分。</p></li></ul><p><strong><em>过滤策略</em></strong></p><ul><li>PodFitsHostPorts:检查 Pod 请求的端口是否空闲(网络协议类型)。</li><li>PodFitsHost:检查 Pod 是否通过其主机名指定了特定的 Node。</li><li>PodFitsResources:检查节点空闲资源(例如 CPU 和内存)是否满足 Pod 的要求。</li><li>PodMatchNodeSelector:检查 Pod label 是否与 Node label 匹配。</li><li>NoVolumeZoneConflict:检查 Volume 存储卷是否可用。</li><li>NoDiskConflict:检查节点 Disk 磁盘是否冲突。</li><li>MaxCSIVolumeCount:检查节点挂载的 CSI 卷是否超出最大限制。</li><li>CheckNodeMemoryPressure:节点正在报告内存压力,并且没有配置的异常,则不会在此处安排 Pod。</li><li>CheckNodePIDPressure:节点正在报告进程 PID 稀缺,并且没有配置的异常,则不会在此处安排 Pod。</li><li>CheckNodeDiskPressure:节点正在报告存储压力,并且没有配置的异常,则不会在此处安排Pod。</li><li>CheckNodeCondition:检查节点的文件系统、网络状态或者 kubelet 是否准备好运行 Pod。</li><li>PodToleratesNodeTaints:检查 Pod 的 Tolerates 是否可以容忍节点的 Taints。</li><li>CheckVolumeBinding:检查 PVC 状态是否满足 Pod 创建需求。</li></ul><p><strong><em>打分策略</em></strong></p><ul><li>SelectorSpreadPriority:将属于同一 StatefulSet、 ReplicaSet 或 Service 的 Pod 分散在不同的主机 。</li><li>InterPodAffinityPriority:根据 Pod 亲和条目打分,匹配到给定节点的条目权重相加,结果值越大的节点得分越高。</li><li>LeastRequestedPriority:计算Pods需要的CPU和内存在当前节点可用资源的百分比,具有最小百分比的节点就是最优。</li><li>MostRequestedPriority:适用动态伸缩集群环境,会优先调度pod到使用率最高的主机节点。当伸缩集群时,就会腾出空闲机器,从而可以停机处理。</li><li>RequestedToCapacityRatioPriority:为节点上每个资源占用比例设定得分值,给资源打分函数在打分时使用。</li><li>BalancedResourceAllocation:优选那些使得资源利用率更为均衡的节点。</li><li>NodePreferAvoidPodsPriority:根据节点是否包含 scheduler.alpha.kubernetes.io/preferAvoidPods 来计算其优先级,将两个不同 Pod 运行在不同的节点上。</li><li>NodeAffinityPriority:基于 PreferredDuringSchedulingIgnoredDuringExecution 来进行节点亲和调度。</li><li>TaintTolerationPriority:基于 Pod 中对节点的污点容忍程度进行优先级评估,这个策略能够调整待选节点的排名。</li><li>ImageLocalityPriority:已经拥有 Pod 需要的 image 的节点会有较高的优先级。</li><li>ServiceSpreadingPriority:确保归属于同一给 Service 的 Pod 调度到不同的节点上,确保节点宕机之后 Service 也具有很强容灾能力。</li><li>EqualPriority:将所有的 Node 设置成相同的权重为 1。</li><li>EvenPodsSpreadPriority:实现首选的Pod拓扑扩展约束。</li></ul><h3 id="调度配置-Scheduling-Profiles"><a href="#调度配置-Scheduling-Profiles" class="headerlink" title="调度配置 Scheduling Profiles"></a>调度配置 Scheduling Profiles</h3><p>官方文档 <code>https://kubernetes.io/docs/concepts/scheduling-eviction/scheduling-framework/</code></p><p>每次调度一个Pod的尝试都分为两个阶段,调度周期 Scheduling Cycle 和 绑定周期 Binding Cycle。</p><ul><li>调度周期为Pod选择一个节点,并且绑定周期将该决定应用于集群。调度周期和绑定周期一起被称为“调度上下文”。</li><li>调度周期是串行运行的,而绑定周期可能是同时运行的。</li><li>如果确定Pod不可调度或存在内部错误,则可以中止调度或绑定周期。Pod将返回队列并重试。</li></ul><p><strong><em>扩展点 Extension points</em></strong></p><p>QueueSort - 对 Pod 的待调度队列进行排序,比较两个 Pod 谁更优先调度,同一时间点只能有一个 QueueSort 插件生效。</p><p>调度周期 Scheduling Cycle</p><ul><li>PreFilter - 对 Pod 条件信息进行预处理,如果 PreFilter 返回了 error 则调度过程终止。</li><li>Filter - 标记不符合调度条件的</li><li>PreScore - 对 Pod 进行预评分,为 Score 插件使用提供可共享的状态。如果PreScore插件返回错误,则调度周期将中止。</li><li>Score - 对符合过滤条件的节点进行计分排名,在 NormalizeScore 阶段之后,调度程序将根据权重合并所有的节点分值。</li><li>NormalizeScore - 在调度程序计算节点的最终排名之前修改分数。</li><li>Reserve - 在符合条件的节点上为 Pod 保留资源。</li><li>Permit - 用于阻止或者延迟 Pod 与节点的绑定。<ul><li>approve - 批准</li><li>deny - 拒绝</li><li>wait - 等待</li></ul></li></ul><p>绑定周期 Binding Cycle</p><ul><li>PreBind - 用于执行绑定 Pod 之前所需的任何工作。如果任何 PreBind 插件返回错误,则 Pod 被拒绝并返回到调度队列。</li><li>Bind - 用于 Pod 绑定。如果绑定插件选择处理 Pod,则会跳过其余的绑定插件。</li><li>PostBind - 成功绑定Pod后,将调用后绑定插件。绑定周期到此结束,可以用来清理关联的资源。</li></ul><p>Unreserve - 如果节点为 Pod 预留了资源,Pod 又在被绑定过程中被拒绝绑定,则执行 unreserve 释放为 Pod 预留的资源。</p><p><strong><em>调度插件 Scheduling plugins</em></strong></p><p>默认情况下启用以下插件,实现这些扩展点中的一个或多个:</p><ul><li>DefaultTopologySpread:基于 Service、ReplicaSets 和 StatefulSets 分散部署。扩展点:PreScore, Score。</li><li>ImageLocality:倾向于已经具有 Pod 运行的 image 的节点。扩展点:Score。</li><li>TaintToleration:实施污点和宽容。扩展点:Filter,Prescore,Score。</li><li>NodeName:Pod 配置的节点与节点是否匹配。扩展点:Filter。</li><li>NodePorts:节点端口是否被占用。扩展点:PreFilter,Filter。</li><li>NodePreferAvoidPods:根据节点 annotation 进行评分 scheduler.alpha.kubernetes.io/preferAvoidPods。扩展点:Score。</li><li>NodeAffinity:节点的亲和与反亲和。扩展点:Filter,Score。</li><li>PodTopologySpread:实现 Pod 拓扑传播。扩展点:PreFilter,Filter,PreScore,Score。</li><li>NodeUnschedulable:过滤掉 .spec.unschedulable 为 true 的节点。扩展点:Filter。</li><li>NodeResourcesFit:检查节点是否具有 Pod 请求的资源。扩展点:PreFilter,Filter。</li><li>NodeResourcesBallancedAllocation:使用资源利用均衡的节点。扩展点:Score。</li><li>NodeResourcesLeastAllocated:使用资源分配少的节点。扩展点:Score。</li><li>VolumeBinding:节点是否具有或可以绑定所请求卷。扩展点:Filter。</li><li>VolumeRestrictions:节点挂载的卷是否满足限制条件。扩展点:Filter。</li><li>VolumeZone:检查卷是否满足任何区域要求。扩展点:Filter。</li><li>NodeVolumeLimits:检查节点是否可以符合 CSI 限制。扩展点:Filter。</li><li>EBSLimits:检查节点是否满足 AWS EBS 限制。扩展点:Filter。</li><li>GCEPDLimits:检查节点是否满足 GCP-PD 限制。扩展点:Filter。</li><li>AzureDiskLimits:检查节点是否满足 Azure Disk 限制。扩展点:Filter。</li><li>InterPodAffinity:基于 Pod 亲和与反亲和。扩展点:PreFilter,Filter,PreScore,Score。</li><li>PrioritySort:基于默认优先级的排序。扩展点:QueueSort。</li><li>DefaultBinder:基于默认的绑定机制。扩展点:Bind。</li></ul><p>您还可以通过组件配置API启用以下默认未启用的插件:</p><ul><li>NodeResourcesMostAllocated:倾向于资源分配高的节点。扩展点:Score。</li><li>RequestedToCapacityRatio:基于节点的资源可用性比例。扩展点:Score。</li><li>NodeResourceLimits:支持满足 Pod 资源限制的节点。扩展点:PreScore,Score。</li><li>CinderVolume:检查节点是否可以满足 OpenStack Cinder 限制。扩展点:Filter。</li><li>NodeLabel:根据节点的 label 进行评分。扩展点:Filter,Score。</li><li>ServiceAffinity:检查 Service label 匹配的节点,该插件有利于在节点之间部署服务。扩展点:PreFilter,Filter,Score。</li></ul><h3 id="亲和-Affinity-与-反亲和-Anti-Affinity"><a href="#亲和-Affinity-与-反亲和-Anti-Affinity" class="headerlink" title="亲和 Affinity 与 反亲和 Anti-Affinity"></a>亲和 Affinity 与 反亲和 Anti-Affinity</h3><h4 id="亲和规则"><a href="#亲和规则" class="headerlink" title="亲和规则"></a>亲和规则</h4><ul><li><p>requiredDuringSchedulingRequiredDuringExecution<br>该规则还未正式上线;<br>在调度期间必须满足亲和或者反亲和规则,如果不能满足规则,则 pod 不能被调度到对应的主机上;<br>在之后的运行过程中,系统会持续监规则是否满足。</p></li><li><p>RequiredDuringSchedulingIgnoredDuringExecution<br>在调度期间必须满足亲和或者反亲和规则,如果不能满足规则,则 pod 不能被调度到对应的主机上;<br>在之后的运行过程中,系统不再检查这些规则是否满足。</p></li><li><p>PreferredDuringSchedulingIgnoredDuringExecution<br>在调度期间尽量满足亲和或者反亲和规则,如果不能满足规则,pod 也有可能被调度到对应的主机上;<br>在之后的运行过程中,系统不再检查这些规则是否满足。</p></li></ul><h4 id="nodeAffinity"><a href="#nodeAffinity" class="headerlink" title="nodeAffinity"></a>nodeAffinity</h4><p>nodeAffinity - 节点亲和,使用场景 :</p><ul><li>将服务的所有Pod部署到指定的符合标签规则的主机上。</li><li>将服务的所有Pod部署到除部分主机外的其他主机上。</li><li>支持的操作符: In,NotIn,Exists,DoesNotExist,Gt,Lt。</li></ul><p>节点亲和:</p><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span><span class="hljs-attr">kind:</span> <span class="hljs-string">Pod</span><span class="hljs-attr">metadata:</span> <span class="hljs-attr">name:</span> <span class="hljs-string">with-node-affinity</span><span class="hljs-attr">spec:</span> <span class="hljs-attr">affinity:</span> <span class="hljs-attr">nodeAffinity:</span> <span class="hljs-attr">requiredDuringSchedulingIgnoredDuringExecution:</span> <span class="hljs-attr">nodeSelectorTerms:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">matchExpressions:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">key:</span> <span class="hljs-string">kubernetes.io/e2e-az-name</span> <span class="hljs-attr">operator:</span> <span class="hljs-string">In</span> <span class="hljs-attr">values:</span> <span class="hljs-bullet">-</span> <span class="hljs-string">e2e-az1</span> <span class="hljs-bullet">-</span> <span class="hljs-string">e2e-az2</span> <span class="hljs-attr">preferredDuringSchedulingIgnoredDuringExecution:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">weight:</span> <span class="hljs-number">1</span> <span class="hljs-attr">preference:</span> <span class="hljs-attr">matchExpressions:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">key:</span> <span class="hljs-string">another-node-label-key</span> <span class="hljs-attr">operator:</span> <span class="hljs-string">In</span> <span class="hljs-attr">values:</span> <span class="hljs-bullet">-</span> <span class="hljs-string">another-node-label-value</span> <span class="hljs-attr">containers:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">with-node-affinity</span> <span class="hljs-attr">image:</span> <span class="hljs-string">k8s.gcr.io/pause:2.0</span></code></pre></div><h4 id="podAffinity-与-podAntiAffinity"><a href="#podAffinity-与-podAntiAffinity" class="headerlink" title="podAffinity 与 podAntiAffinity"></a>podAffinity 与 podAntiAffinity</h4><p>注意:<br>Pod 间亲和与反亲和需要大量的处理,这可能会显著减慢大规模集群中的调度。我们不建议在超过数百个节点的集群中使用它们。</p><p>podAffinity - pod 亲和,使用场景 :</p><ul><li>将某一特定的 pod 部署在同一拓扑域中,不用指定具体的拓扑域。</li><li>为了减少关联的2个服务之间的网络延迟(或其它原因),将他们部署在同一拓扑域中。</li><li>支持的操作符: In,NotIn,Exists,DoesNotExist。</li></ul><p>podAntiAffinity - pod 反亲和,使用场景 :</p><ul><li>将一个服务的 pod 分散在不同的主机或者拓扑域中,提高服务本身的稳定性。</li><li>为某一个 pod 提供节点的资源独占权限。</li><li>把可能会相互影响的 pod 分散在不同的主机上。</li><li>支持的操作符: In,NotIn,Exists,DoesNotExist。</li></ul><p>pod 亲和与反亲和:</p><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span><span class="hljs-attr">kind:</span> <span class="hljs-string">Pod</span><span class="hljs-attr">metadata:</span> <span class="hljs-attr">name:</span> <span class="hljs-string">with-pod-affinity</span><span class="hljs-attr">spec:</span> <span class="hljs-attr">affinity:</span> <span class="hljs-attr">podAffinity:</span> <span class="hljs-attr">requiredDuringSchedulingIgnoredDuringExecution:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">labelSelector:</span> <span class="hljs-attr">matchExpressions:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">key:</span> <span class="hljs-string">security</span> <span class="hljs-attr">operator:</span> <span class="hljs-string">In</span> <span class="hljs-attr">values:</span> <span class="hljs-bullet">-</span> <span class="hljs-string">S1</span> <span class="hljs-attr">topologyKey:</span> <span class="hljs-string">failure-domain.beta.kubernetes.io/zone</span> <span class="hljs-attr">podAntiAffinity:</span> <span class="hljs-attr">preferredDuringSchedulingIgnoredDuringExecution:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">weight:</span> <span class="hljs-number">100</span> <span class="hljs-attr">podAffinityTerm:</span> <span class="hljs-attr">labelSelector:</span> <span class="hljs-attr">matchExpressions:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">key:</span> <span class="hljs-string">security</span> <span class="hljs-attr">operator:</span> <span class="hljs-string">In</span> <span class="hljs-attr">values:</span> <span class="hljs-bullet">-</span> <span class="hljs-string">S2</span> <span class="hljs-attr">topologyKey:</span> <span class="hljs-string">failure-domain.beta.kubernetes.io/zone</span> <span class="hljs-attr">containers:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">with-pod-affinity</span> <span class="hljs-attr">image:</span> <span class="hljs-string">k8s.gcr.io/pause:2.0</span></code></pre></div><h3 id="污点驱逐-Taint"><a href="#污点驱逐-Taint" class="headerlink" title="污点驱逐 Taint"></a>污点驱逐 Taint</h3><ul><li>NoSchedule:表示k8s将不会将Pod调度到具有该污点的Node上</li><li>PreferNoSchedule:表示k8s将尽量避免将Pod调度到具有该污点的Node上</li><li>NoExecute:表示k8s将不会将Pod调度到具有该污点的Node上,同时会将Node上已经存在的Pod驱逐出去</li></ul><div class="hljs"><pre><code class="hljs bash">删除污点,开启 master 节点 pod 调度功能。kubectl taint node ukm03 node-role.kubernetes.io/master=:NoSchedule-添加污点,关闭 master 节点 pod 调度功能。kubectl taint node ukm03 node-role.kubernetes.io/master=:NoSchedule</code></pre></div><h3 id="容忍-Toleration"><a href="#容忍-Toleration" class="headerlink" title="容忍 Toleration"></a>容忍 Toleration</h3><p>设置了污点的节点,Pod 将在一定程度上不会被调度到节点上。 通过设置 pod 容忍(Toleration),将 pod 调度到存在污点的Node上。通过在Pod的spec中设置tolerations字段,给Pod设置上容忍点:</p><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-attr">tolerations:</span><span class="hljs-bullet">-</span> <span class="hljs-attr">key:</span> <span class="hljs-string">"key1"</span> <span class="hljs-attr">operator:</span> <span class="hljs-string">"Equal"</span> <span class="hljs-attr">value:</span> <span class="hljs-string">"value1"</span> <span class="hljs-attr">effect:</span> <span class="hljs-string">"NoSchedule"</span> <span class="hljs-attr">tolerationSeconds:</span> <span class="hljs-number">3600</span><span class="hljs-bullet">-</span> <span class="hljs-attr">key:</span> <span class="hljs-string">"key1"</span> <span class="hljs-attr">operator:</span> <span class="hljs-string">"Equal"</span> <span class="hljs-attr">value:</span> <span class="hljs-string">"value1"</span> <span class="hljs-attr">effect:</span> <span class="hljs-string">"NoExecute"</span><span class="hljs-bullet">-</span> <span class="hljs-attr">key:</span> <span class="hljs-string">"key2"</span> <span class="hljs-attr">operator:</span> <span class="hljs-string">"Exists"</span> <span class="hljs-attr">effect:</span> <span class="hljs-string">"NoSchedule"</span></code></pre></div><ul><li>key、vaule、effect要与Node上设置的taint保持一致。</li><li>当不指定key值时,表示容忍所有的污点key;当不指定effect值时,表示容忍所有的污点作用。</li><li>operator的值为Exists将会忽略value值。</li><li>tolerationSeconds用于描述当Pod需要被驱逐时可以在Pod上继续保留运行的时间。</li></ul><h3 id="create-和-apply"><a href="#create-和-apply" class="headerlink" title="create 和 apply"></a>create 和 apply</h3><p>kubectl create 命令可创建新资源。 因此再次运行该命令,则会抛出错误,因为资源名称在名称空间中应该是唯一的。</p><div class="hljs"><pre><code class="hljs bash"> kubectl create -f test.ymlpod/myapp-pod created kubectl create -f test.ymlError from server (AlreadyExists): error when creating <span class="hljs-string">"pod.xml"</span>: pods <span class="hljs-string">"myapp-pod"</span> already exists</code></pre></div><p>kubectl apply 命令将配置应用于资源。 如果资源不在那里,那么它将被创建。 apply 命令可以二次运行,因为它只是应用配置。 在这种情况下,配置没有改变, 所以 pod 没有改变。</p><div class="hljs"><pre><code class="hljs bash"> kubectl apply -f test.ymlpod/myapp-pod created kubectl apply -f test.ymlpod/myapp-pod unchanged</code></pre></div><h3 id="kubernetes-控制器"><a href="#kubernetes-控制器" class="headerlink" title="kubernetes 控制器"></a><strong>kubernetes 控制器</strong></h3><h4 id="ReplicationController"><a href="#ReplicationController" class="headerlink" title="ReplicationController"></a>ReplicationController</h4><ul><li>确保在任何时候都有特定数量的 pod 副本处于运行状态;</li><li>类似于进程管理器, ReplicationController 监控跨多个节点的多个 pod;</li><li>通常缩写为 rc ,通过 replicas 控制副本的数量。</li></ul><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-meta">---</span><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">apps/v1</span><span class="hljs-attr">kind:</span> <span class="hljs-string">ReplicationController</span><span class="hljs-attr">metadata:</span><span class="hljs-string">...</span></code></pre></div><h4 id="ReplicaSet"><a href="#ReplicaSet" class="headerlink" title="ReplicaSet"></a>ReplicaSet</h4><ul><li>ReplicaSet 是下一代的 ReplicationController;</li><li>ReplicaSet 和 ReplicationController 的唯一区别是选择器的支持,ReplicaSet 支持新的基于集合的选择器需求,而 Replication Controller 仅支持基于相等选择器的需求。</li><li>通常缩写为 rs 。</li></ul><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-meta">---</span><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">apps/v1</span><span class="hljs-attr">kind:</span> <span class="hljs-string">ReplicaSet</span><span class="hljs-attr">metadata:</span><span class="hljs-string">...</span></code></pre></div><h4 id="Deployment"><a href="#Deployment" class="headerlink" title="Deployment"></a>Deployment</h4><ul><li>Deployment 管理和描述 Pods 和 ReplicaSets 部署方式;</li><li>清理较旧的、不再需要的 ReplicaSets 。</li><li>如果 Deployment 的当前状态不稳定,回滚到较早的 Deployment 版本。</li></ul><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-meta">---</span><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">apps/v1</span><span class="hljs-attr">kind:</span> <span class="hljs-string">Deployment</span><span class="hljs-attr">metadata:</span><span class="hljs-string">...</span></code></pre></div><h4 id="StatefulSet"><a href="#StatefulSet" class="headerlink" title="StatefulSet"></a>StatefulSet</h4><ul><li>是用来管理有状态应用的对象;</li><li>稳定的、唯一的网络标识符;</li><li>稳定的、持久的存储;</li><li>有序的、优雅的部署和缩放;</li><li>有序的、自动的滚动更新。</li></ul><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-meta">---</span><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">apps/v1</span><span class="hljs-attr">kind:</span> <span class="hljs-string">StatefulSet</span><span class="hljs-attr">metadata:</span><span class="hljs-string">...</span></code></pre></div><h4 id="DaemonSet"><a href="#DaemonSet" class="headerlink" title="DaemonSet"></a>DaemonSet</h4><ul><li>DaemonSet 确保全部或者某个 label 的节点上运行一个 Pod 的实例;</li><li>在每个节点上运行集群存储 DaemonSet,例如 glusterd、ceph;</li><li>在每个节点上运行日志收集 DaemonSet,例如 fluentd、logstash;</li><li>在每个节点上运行监控 DaemonSet,例如 Prometheus Node Exporter;</li><li>在每个 infra 节点上面运行一个 ingress 服务。</li></ul><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-meta">---</span><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">apps/v1</span><span class="hljs-attr">kind:</span> <span class="hljs-string">DaemonSet</span><span class="hljs-attr">metadata:</span><span class="hljs-string">...</span></code></pre></div><h4 id="Job"><a href="#Job" class="headerlink" title="Job"></a>Job</h4><ul><li>Job负责处理任务,即仅执行一次的任务,它保证批处理任务的一个或多个Pod成功结束;</li><li>运行示例作业</li><li>编写工作规范</li><li>处理Pod和容器故障</li><li>作业终止和清理</li><li>自动清理完成的作业</li><li>CronJob</li></ul><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-meta">---</span><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">batch/v1</span><span class="hljs-attr">kind:</span> <span class="hljs-string">Job</span><span class="hljs-attr">metadata:</span> <span class="hljs-attr">name:</span> <span class="hljs-string">pi</span><span class="hljs-attr">spec:</span> <span class="hljs-attr">template:</span> <span class="hljs-attr">spec:</span> <span class="hljs-attr">containers:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">pi</span> <span class="hljs-attr">image:</span> <span class="hljs-string">perl</span> <span class="hljs-attr">command:</span> <span class="hljs-string">["perl",</span> <span class="hljs-string">"-Mbignum=bpi"</span><span class="hljs-string">,</span> <span class="hljs-string">"-wle"</span><span class="hljs-string">,</span> <span class="hljs-string">"print bpi(2000)"</span><span class="hljs-string">]</span> <span class="hljs-attr">restartPolicy:</span> <span class="hljs-string">Never</span> <span class="hljs-attr">backoffLimit:</span> <span class="hljs-number">4</span></code></pre></div><h4 id="CronJob"><a href="#CronJob" class="headerlink" title="CronJob"></a>CronJob</h4><ul><li>执行基于时间调度的任务,类似于 linux 系统的 crontab 任务。</li></ul><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-meta">---</span><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">batch/v1beta1</span><span class="hljs-attr">kind:</span> <span class="hljs-string">CronJob</span><span class="hljs-attr">metadata:</span> <span class="hljs-attr">name:</span> <span class="hljs-string">hello</span><span class="hljs-attr">spec:</span> <span class="hljs-attr">schedule:</span> <span class="hljs-string">"*/1 * * * *"</span> <span class="hljs-attr">jobTemplate:</span> <span class="hljs-attr">spec:</span> <span class="hljs-attr">template:</span> <span class="hljs-attr">spec:</span> <span class="hljs-attr">containers:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">hello</span> <span class="hljs-attr">image:</span> <span class="hljs-string">busybox</span> <span class="hljs-attr">args:</span> <span class="hljs-bullet">-</span> <span class="hljs-string">/bin/sh</span> <span class="hljs-bullet">-</span> <span class="hljs-string">-c</span> <span class="hljs-bullet">-</span> <span class="hljs-string">date;</span> <span class="hljs-string">echo</span> <span class="hljs-string">Hello</span> <span class="hljs-string">from</span> <span class="hljs-string">the</span> <span class="hljs-string">Kubernetes</span> <span class="hljs-string">cluster</span> <span class="hljs-attr">restartPolicy:</span> <span class="hljs-string">OnFailure</span></code></pre></div><h3 id="备份-images"><a href="#备份-images" class="headerlink" title="备份 images"></a>备份 images</h3><div class="hljs"><pre><code class="hljs bash"> cat images.sh!/bin/bashimagePath=<span class="hljs-string">"/root/images"</span>mkdir -p <span class="hljs-variable">${imagePath}</span><span class="hljs-function"><span class="hljs-title">save_images</span></span>() { docker images|awk <span class="hljs-string">'!/REPOSITORY/ && !/<none>/ && !/docker-registry.default.svc/ && !/jenkins/{print $1,$2}'</span> > /tmp/images.list <span class="hljs-keyword">while</span> <span class="hljs-built_in">read</span> Name Version <span class="hljs-keyword">do</span> fileName=$(<span class="hljs-built_in">echo</span> <span class="hljs-variable">$Name</span> | awk -F\/ <span class="hljs-string">'{print $NF}'</span>) <span class="hljs-keyword">if</span> [ ! -f <span class="hljs-string">"<span class="hljs-variable">${imagePath}</span>/<span class="hljs-variable">${fileName}</span>_<span class="hljs-variable">${Version}</span>.tgz"</span> ];<span class="hljs-keyword">then</span> <span class="hljs-built_in">echo</span> <span class="hljs-string">"save image <span class="hljs-variable">$Name</span>:<span class="hljs-variable">$Version</span>"</span> docker save <span class="hljs-variable">$Name</span>:<span class="hljs-variable">$Version</span> | gzip > <span class="hljs-variable">${imagePath}</span>/<span class="hljs-variable">${fileName}</span>_<span class="hljs-variable">${Version}</span>.tgz <span class="hljs-keyword">fi</span> <span class="hljs-keyword">done</span> < /tmp/images.list}<span class="hljs-function"><span class="hljs-title">load_images</span></span>() { <span class="hljs-keyword">for</span> images <span class="hljs-keyword">in</span> $(ls <span class="hljs-variable">${imagePath}</span>/*.tgz) <span class="hljs-keyword">do</span> <span class="hljs-built_in">echo</span> <span class="hljs-string">"load image <span class="hljs-variable">$images</span>"</span> docker load < <span class="hljs-variable">$images</span> <span class="hljs-keyword">done</span>}<span class="hljs-function"><span class="hljs-title">push_images</span></span>() { newRegistry=<span class="hljs-string">"https://docker-registry-default.pase-hrx-stg1.zhi-niao.com/"</span> docker images|awk <span class="hljs-string">'!/REPOSITORY/ && !/<none>/{print $1,$2}'</span> > /tmp/images.list <span class="hljs-keyword">while</span> <span class="hljs-built_in">read</span> Name Version <span class="hljs-keyword">do</span> fileName=$(<span class="hljs-built_in">echo</span> <span class="hljs-variable">$Name</span> | awk -F/ <span class="hljs-string">'sub($1,"")'</span>) <span class="hljs-built_in">echo</span> <span class="hljs-string">"<span class="hljs-variable">${Name}</span>:<span class="hljs-variable">${Version}</span> | new tag | <span class="hljs-variable">${newRegistry}</span><span class="hljs-variable">${fileName}</span>:<span class="hljs-variable">${Version}</span>"</span> docker tag <span class="hljs-variable">${Name}</span>:<span class="hljs-variable">${Version}</span> <span class="hljs-variable">${newRegistry}</span><span class="hljs-variable">${fileName}</span>:<span class="hljs-variable">${Version}</span> docker push <span class="hljs-variable">${newRegistry}</span><span class="hljs-variable">${fileName}</span>:<span class="hljs-variable">${Version}</span> docker rmi <span class="hljs-variable">${newRegistry}</span><span class="hljs-variable">${fileName}</span>:<span class="hljs-variable">${Version}</span> <span class="hljs-keyword">done</span> < /tmp/images.list}<span class="hljs-keyword">case</span> <span class="hljs-variable">$1</span> <span class="hljs-keyword">in</span> save|s) save_images ;; load|l) load_images ;; push|p) push_images ;; *) <span class="hljs-built_in">echo</span> <span class="hljs-string">"Usage: <span class="hljs-variable">$0</span> {save|load}"</span> ;;<span class="hljs-keyword">esac</span> 分发 images.sh ansible master:worker:infra -m copy -a <span class="hljs-string">'src=images.sh dest=/root/images.sh mode=0755'</span> ansible master:worker:infra -m shell -a <span class="hljs-string">'./images.sh save'</span> <span class="hljs-keyword">for</span> host <span class="hljs-keyword">in</span> ukm02 ukm03 uki01 uki02 uki03 ukn01 ukn02 ukn03;<span class="hljs-keyword">do</span> rsync -vzrtopg --progress -e ssh <span class="hljs-variable">${host}</span>:/root/images/* /root/images/;<span class="hljs-keyword">done</span></code></pre></div><h3 id="切换-namespace"><a href="#切换-namespace" class="headerlink" title="切换 namespace"></a>切换 namespace</h3><div class="hljs"><pre><code class="hljs bash">kubectl config <span class="hljs-built_in">set</span>-context --current --namespace=kube-system</code></pre></div><h3 id="获取加入集群-token"><a href="#获取加入集群-token" class="headerlink" title="获取加入集群 token"></a>获取加入集群 token</h3><div class="hljs"><pre><code class="hljs bash"> kubeadm token listTOKEN TTL EXPIRES USAGES DESCRIPTION EXTRA GROUPSgnc91d.aav2px3kb3021uxl 8h 2020-02-20T20:20:00+08:00 authentication,signing The default bootstrap token generated by <span class="hljs-string">'kubeadm init'</span>. system:bootstrappers:kubeadm:default-node-token</code></pre></div><h3 id="获取加入集群-token-hash"><a href="#获取加入集群-token-hash" class="headerlink" title="获取加入集群 token hash"></a>获取加入集群 token hash</h3><div class="hljs"><pre><code class="hljs bash"> openssl x509 -pubkey -<span class="hljs-keyword">in</span> /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | openssl dgst -sha256 -hex | sed <span class="hljs-string">'s/^.* //'</span>ae56bc3c9cabaaf04878be16436348419a0772342d5cc71c371fb5a79157b5a8</code></pre></div><h3 id="重新生成-kubeadm-certs"><a href="#重新生成-kubeadm-certs" class="headerlink" title="重新生成 kubeadm-certs"></a>重新生成 kubeadm-certs</h3><ul><li>kubeadm-certs 密钥和解密密钥会在两个小时后失效</li></ul><div class="hljs"><pre><code class="hljs bash"> kubeadm init phase upload-certs --upload-certs[upload-certs] Storing the certificates <span class="hljs-keyword">in</span> Secret <span class="hljs-string">"kubeadm-certs"</span> <span class="hljs-keyword">in</span> the <span class="hljs-string">"kube-system"</span> Namespace[upload-certs] Using certificate key:4379fa5fdc142d5e98d01a290b8490fc9a169e2a4c3a94695ec3745573b22fb3</code></pre></div><h3 id="查看-kubernetes-证书状态"><a href="#查看-kubernetes-证书状态" class="headerlink" title="查看 kubernetes 证书状态"></a>查看 kubernetes 证书状态</h3><div class="hljs"><pre><code class="hljs bash"> kubeadm alpha certs check-expiration[check-expiration] Reading configuration from the cluster...[check-expiration] FYI: You can look at this config file with <span class="hljs-string">'kubectl -n kube-system get cm kubeadm-config -oyaml'</span>CERTIFICATE EXPIRES RESIDUAL TIME CERTIFICATE AUTHORITY EXTERNALLY MANAGEDadmin.conf Apr 22, 2021 09:03 UTC 364d noapiserver Apr 22, 2021 09:03 UTC 364d ca noapiserver-etcd-client Apr 22, 2021 09:03 UTC 364d etcd-ca noapiserver-kubelet-client Apr 22, 2021 09:03 UTC 364d ca nocontroller-manager.conf Apr 22, 2021 09:03 UTC 364d noetcd-healthcheck-client Apr 22, 2021 09:03 UTC 364d etcd-ca noetcd-peer Apr 22, 2021 09:03 UTC 364d etcd-ca noetcd-server Apr 22, 2021 09:03 UTC 364d etcd-ca nofront-proxy-client Apr 22, 2021 09:03 UTC 364d front-proxy-ca noscheduler.conf Apr 22, 2021 09:03 UTC 364d noCERTIFICATE AUTHORITY EXPIRES RESIDUAL TIME EXTERNALLY MANAGEDca Apr 14, 2030 11:18 UTC 9y noetcd-ca Apr 14, 2030 11:18 UTC 9y nofront-proxy-ca Apr 14, 2030 11:18 UTC 9y no</code></pre></div><h3 id="重新签发所有-kubernetes-证书"><a href="#重新签发所有-kubernetes-证书" class="headerlink" title="重新签发所有 kubernetes 证书"></a>重新签发所有 kubernetes 证书</h3><div class="hljs"><pre><code class="hljs bash"> kubeadm alpha certs renew all[renew] Reading configuration from the cluster...[renew] FYI: You can look at this config file with <span class="hljs-string">'kubectl -n kube-system get cm kubeadm-config -oyaml'</span>certificate embedded <span class="hljs-keyword">in</span> the kubeconfig file <span class="hljs-keyword">for</span> the admin to use and <span class="hljs-keyword">for</span> kubeadm itself renewedcertificate <span class="hljs-keyword">for</span> serving the Kubernetes API renewedcertificate the apiserver uses to access etcd renewedcertificate <span class="hljs-keyword">for</span> the API server to connect to kubelet renewedcertificate embedded <span class="hljs-keyword">in</span> the kubeconfig file <span class="hljs-keyword">for</span> the controller manager to use renewedcertificate <span class="hljs-keyword">for</span> liveness probes to healthcheck etcd renewedcertificate <span class="hljs-keyword">for</span> etcd nodes to communicate with each other renewedcertificate <span class="hljs-keyword">for</span> serving etcd renewedcertificate <span class="hljs-keyword">for</span> the front proxy client renewedcertificate embedded <span class="hljs-keyword">in</span> the kubeconfig file <span class="hljs-keyword">for</span> the scheduler manager to use renewed</code></pre></div><h3 id="更新证书签名"><a href="#更新证书签名" class="headerlink" title="更新证书签名"></a>更新证书签名</h3><div class="hljs"><pre><code class="hljs bash"> 创建更新配置 cat > ca-sign.yaml <<EOFapiVersion: kubeadm.k8s.io/v1beta2kind: ClusterConfigurationapiServer: certSANs: - <span class="hljs-string">"10.10.34.89"</span> - <span class="hljs-string">"10.10.34.92"</span> - <span class="hljs-string">"10.10.34.93"</span> - <span class="hljs-string">"10.10.34.94"</span> - <span class="hljs-string">"113.108.71.77"</span> - <span class="hljs-string">"kubernetes"</span> - <span class="hljs-string">"kubernetes.default"</span> - <span class="hljs-string">"kubernetes.default.svc"</span> - <span class="hljs-string">"kubernetes.default.svc.cluster"</span> - <span class="hljs-string">"kubernetes.default.svc.cluster.local"</span>controllerManager: extraArgs: cluster-signing-cert-file: /etc/kubernetes/pki/ca.crt cluster-signing-key-file: /etc/kubernetes/pki/ca.keyEOF 更新 kubernetes 配置 kubeadm config upload from-file --config=ca-sign.yaml 确认更新配置生效 kubeadm config viewapiServer: certSANs: - 10.10.34.89 - 10.10.34.92 - 10.10.34.93 - 10.10.34.94 - 113.108.71.77 - kubernetes - kubernetes.default - kubernetes.default.svc - kubernetes.default.svc.cluster - kubernetes.default.svc.cluster.local timeoutForControlPlane: 4m0sapiVersion: kubeadm.k8s.io/v1beta2certificatesDir: /etc/kubernetes/pkiclusterName: kubernetescontrollerManager: extraArgs: cluster-signing-cert-file: /etc/kubernetes/pki/ca.crt cluster-signing-key-file: /etc/kubernetes/pki/ca.keydns: <span class="hljs-built_in">type</span>: CoreDNSetcd: <span class="hljs-built_in">local</span>: dataDir: /var/lib/etcdimageRepository: k8s.gcr.iokind: ClusterConfigurationkubernetesVersion: v1.18.2networking: dnsDomain: cluster.local serviceSubnet: 10.96.0.0/12scheduler: {} 删除原 apiserver 证书 rm -rf /etc/kubernetes/pki/apiserver.* 重新生成 apiserver 证书 kubeadm init phase certs apiserver --config=ca-sign.yamlW0429 15:59:26.534066 10196 configset.go:202] WARNING: kubeadm cannot validate component configs <span class="hljs-keyword">for</span> API groups [kubelet.config.k8s.io kubeproxy.config.k8s.io][certs] Generating <span class="hljs-string">"apiserver"</span> certificate and key[certs] apiserver serving cert is signed <span class="hljs-keyword">for</span> DNS names [ukm01 kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster.local ukm01 ukm02 ukm03 kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster kubernetes.default.svc.cluster.local] and IPs [10.96.0.1 10.10.34.92 10.10.34.89 10.10.34.92 10.10.34.93 10.10.34.94 113.108.71.77] 确认 apiserver 证书更新情况 openssl x509 -text -noout -<span class="hljs-keyword">in</span> /etc/kubernetes/pki/apiserver.crt... X509v3 extensions: X509v3 Key Usage: critical Digital Signature, Key Encipherment X509v3 Extended Key Usage: TLS Web Server Authentication X509v3 Subject Alternative Name: DNS:ukm01, DNS:kubernetes, DNS:kubernetes.default, DNS:kubernetes.default.svc, DNS:kubernetes.default.svc.cluster.local, DNS:ukm01, DNS:ukm02, DNS:ukm03, DNS:kubernetes, DNS:kubernetes.default, DNS:kubernetes.default.svc, DNS:kubernetes.default.svc.cluster, DNS:kubernetes.default.svc.cluster.local, IP Address:10.96.0.1, IP Address:10.10.34.92, IP Address:10.10.34.89, IP Address:10.10.34.92, IP Address:10.10.34.93, IP Address:10.10.34.94, IP Address:113.108.71.77... 更新所有证书 kubeadm alpha certs renew all --config=ca-sign.yamlW0429 15:50:57.792294 2944 configset.go:202] WARNING: kubeadm cannot validate component configs <span class="hljs-keyword">for</span> API groups [kubelet.config.k8s.io kubeproxy.config.k8s.io]certificate embedded <span class="hljs-keyword">in</span> the kubeconfig file <span class="hljs-keyword">for</span> the admin to use and <span class="hljs-keyword">for</span> kubeadm itself renewedcertificate <span class="hljs-keyword">for</span> serving the Kubernetes API renewedcertificate the apiserver uses to access etcd renewedcertificate <span class="hljs-keyword">for</span> the API server to connect to kubelet renewedcertificate embedded <span class="hljs-keyword">in</span> the kubeconfig file <span class="hljs-keyword">for</span> the controller manager to use renewedcertificate <span class="hljs-keyword">for</span> liveness probes to healthcheck etcd renewedcertificate <span class="hljs-keyword">for</span> etcd nodes to communicate with each other renewedcertificate <span class="hljs-keyword">for</span> serving etcd renewedcertificate <span class="hljs-keyword">for</span> the front proxy client renewedcertificate embedded <span class="hljs-keyword">in</span> the kubeconfig file <span class="hljs-keyword">for</span> the scheduler manager to use renewed 通过 api 更新所有证书,需要执行确认命令。 kubeadm alpha certs renew all --use-api --config=ca-sign.yaml &...[certs] Certificate request <span class="hljs-string">"kubeadm-cert-kubernetes-admin-8pvf8"</span> created... 批准更新 kubectl get csr | awk <span class="hljs-string">'!/Approved/ && !/NAME/{print "kubectl certificate approve "$1}'</span> | bash...certificatesigningrequest.certificates.k8s.io/kubeadm-cert-kubernetes-admin-8pvf8 approved... 查看证书更新状态 kubectl get csrNAME AGE SIGNERNAME REQUESTOR CONDITIONkubeadm-cert-front-proxy-client-tq62p 101s kubernetes.io/legacy-unknown kubernetes-admin Approved,Issuedkubeadm-cert-kube-apiserver-etcd-client-rnnjn 2m15s kubernetes.io/legacy-unknown kubernetes-admin Approved,Issuedkubeadm-cert-kube-apiserver-fk62g 2m16s kubernetes.io/legacy-unknown kubernetes-admin Approved,Issuedkubeadm-cert-kube-apiserver-kubelet-client-5cmpv 2m11s kubernetes.io/legacy-unknown kubernetes-admin Approved,Issuedkubeadm-cert-kube-etcd-healthcheck-client-7lcw9 118s kubernetes.io/legacy-unknown kubernetes-admin Approved,Issuedkubeadm-cert-kubernetes-admin-8pvf8 3m24s kubernetes.io/legacy-unknown kubernetes-admin Approved,Issuedkubeadm-cert-system:kube-controller-manager-bgsmt 2m8s kubernetes.io/legacy-unknown kubernetes-admin Approved,Issuedkubeadm-cert-system:kube-scheduler-dhj6b 96s kubernetes.io/legacy-unknown kubernetes-admin Approved,Issuedkubeadm-cert-ukm01-2bwbg 115s kubernetes.io/legacy-unknown kubernetes-admin Approved,Issuedkubeadm-cert-ukm01-ftrgn 104s kubernetes.io/legacy-unknown kubernetes-admin Approved,Issued</code></pre></div><h2 id="卸载-kubernetes-集群"><a href="#卸载-kubernetes-集群" class="headerlink" title="卸载 kubernetes 集群"></a><strong>卸载 kubernetes 集群</strong></h2><h3 id="先隔离再删除节点"><a href="#先隔离再删除节点" class="headerlink" title="先隔离再删除节点"></a>先隔离再删除节点</h3><div class="hljs"><pre><code class="hljs bash">kubectl drain uki01 uki02 uki03 ukm01 ukm02 ukm03 ukn01 ukn02 ukn03 --delete-local-data --force --ignore-daemonsetskubectl delete node uki01 uki02 uki03 ukm01 ukm02 ukm03 ukn01 ukn02 ukn03</code></pre></div><h3 id="重置节点状态"><a href="#重置节点状态" class="headerlink" title="重置节点状态"></a>重置节点状态</h3><div class="hljs"><pre><code class="hljs bash">ansible ins -m shell -a <span class="hljs-string">'kubeadm reset --force'</span>ansible ins -m shell -a <span class="hljs-string">'rm -rf /etc/cni /opt/cni/ /var/lib/calico/ ~/.kube/'</span></code></pre></div><h2 id="kubernetes-集群升级"><a href="#kubernetes-集群升级" class="headerlink" title="kubernetes 集群升级"></a><strong>kubernetes 集群升级</strong></h2><h3 id="检查-kubeadm-可用版本"><a href="#检查-kubeadm-可用版本" class="headerlink" title="检查 kubeadm 可用版本"></a>检查 kubeadm 可用版本</h3><div class="hljs"><pre><code class="hljs bash">apt update && apt-cache policy kubeadm</code></pre></div><h3 id="升级-kubeadm"><a href="#升级-kubeadm" class="headerlink" title="升级 kubeadm"></a>升级 kubeadm</h3><p>建议一台一台升级,批量操作存在无法预估的错误。</p><div class="hljs"><pre><code class="hljs bash">apt-mark unhold kubeadm && \ apt-get update && apt-get install -y kubeadm=1.18.0-00 && \ apt-mark hold kubeadm或者apt-get update && \ apt-get install -y --allow-change-held-packages kubeadm=1.18.0-00</code></pre></div><h3 id="验证-kubeadm-版本"><a href="#验证-kubeadm-版本" class="headerlink" title="验证 kubeadm 版本"></a>验证 kubeadm 版本</h3><div class="hljs"><pre><code class="hljs bash"> kubeadm versionkubeadm version: &version.Info{Major:<span class="hljs-string">"1"</span>, Minor:<span class="hljs-string">"18"</span>, GitVersion:<span class="hljs-string">"v1.18.0"</span>, GitCommit:<span class="hljs-string">"9e991415386e4cf155a24b1da15becaa390438d8"</span>, GitTreeState:<span class="hljs-string">"clean"</span>, BuildDate:<span class="hljs-string">"2020-03-25T14:56:30Z"</span>, GoVersion:<span class="hljs-string">"go1.13.8"</span>, Compiler:<span class="hljs-string">"gc"</span>, Platform:<span class="hljs-string">"linux/amd64"</span>}</code></pre></div><h3 id="隔离-ukm01"><a href="#隔离-ukm01" class="headerlink" title="隔离 ukm01"></a>隔离 ukm01</h3><div class="hljs"><pre><code class="hljs bash"> kubectl drain ukm01 --ignore-daemonsetsnode/ukm01 cordonednode/ukm01 drained</code></pre></div><h3 id="查看可升级版本"><a href="#查看可升级版本" class="headerlink" title="查看可升级版本"></a>查看可升级版本</h3><div class="hljs"><pre><code class="hljs bash"> kubeadm upgrade plan[upgrade/config] Making sure the configuration is correct:[upgrade/config] Reading configuration from the cluster...[upgrade/config] FYI: You can look at this config file with <span class="hljs-string">'kubectl -n kube-system get cm kubeadm-config -oyaml'</span>[preflight] Running pre-flight checks.[upgrade] Running cluster health checks[upgrade] Fetching available versions to upgrade to[upgrade/versions] Cluster version: v1.17.3[upgrade/versions] kubeadm version: v1.18.0[upgrade/versions] Latest stable version: v1.18.0[upgrade/versions] Latest stable version: v1.18.0[upgrade/versions] Latest version <span class="hljs-keyword">in</span> the v1.17 series: v1.17.4[upgrade/versions] Latest version <span class="hljs-keyword">in</span> the v1.17 series: v1.17.4Components that must be upgraded manually after you have upgraded the control plane with <span class="hljs-string">'kubeadm upgrade apply'</span>:COMPONENT CURRENT AVAILABLEKubelet 9 x v1.17.3 v1.17.4Upgrade to the latest version <span class="hljs-keyword">in</span> the v1.17 series:COMPONENT CURRENT AVAILABLEAPI Server v1.17.3 v1.17.4Controller Manager v1.17.3 v1.17.4Scheduler v1.17.3 v1.17.4Kube Proxy v1.17.3 v1.17.4CoreDNS 1.6.5 1.6.7Etcd 3.4.3 3.4.3-0You can now apply the upgrade by executing the following <span class="hljs-built_in">command</span>: kubeadm upgrade apply v1.17.4_____________________________________________________________________Components that must be upgraded manually after you have upgraded the control plane with <span class="hljs-string">'kubeadm upgrade apply'</span>:COMPONENT CURRENT AVAILABLEKubelet 9 x v1.17.3 v1.18.0Upgrade to the latest stable version:COMPONENT CURRENT AVAILABLEAPI Server v1.17.3 v1.18.0Controller Manager v1.17.3 v1.18.0Scheduler v1.17.3 v1.18.0Kube Proxy v1.17.3 v1.18.0CoreDNS 1.6.5 1.6.7Etcd 3.4.3 3.4.3-0You can now apply the upgrade by executing the following <span class="hljs-built_in">command</span>: kubeadm upgrade apply v1.18.0_____________________________________________________________________</code></pre></div><h3 id="升级-kubernetes"><a href="#升级-kubernetes" class="headerlink" title="升级 kubernetes"></a>升级 kubernetes</h3><div class="hljs"><pre><code class="hljs bash"> kubeadm upgrade apply v1.18.0[upgrade/config] Making sure the configuration is correct:[upgrade/config] Reading configuration from the cluster...[upgrade/config] FYI: You can look at this config file with <span class="hljs-string">'kubectl -n kube-system get cm kubeadm-config -oyaml'</span>[preflight] Running pre-flight checks.[upgrade] Running cluster health checks[upgrade/version] You have chosen to change the cluster version to <span class="hljs-string">"v1.18.0"</span>[upgrade/versions] Cluster version: v1.17.3[upgrade/versions] kubeadm version: v1.18.0[upgrade/confirm] Are you sure you want to proceed with the upgrade? [y/N]: y[upgrade/prepull] Will prepull images <span class="hljs-keyword">for</span> components [kube-apiserver kube-controller-manager kube-scheduler etcd][upgrade/prepull] Prepulling image <span class="hljs-keyword">for</span> component etcd.[upgrade/prepull] Prepulling image <span class="hljs-keyword">for</span> component kube-apiserver.[upgrade/prepull] Prepulling image <span class="hljs-keyword">for</span> component kube-controller-manager.[upgrade/prepull] Prepulling image <span class="hljs-keyword">for</span> component kube-scheduler.[apiclient] Found 0 Pods <span class="hljs-keyword">for</span> label selector k8s-app=upgrade-prepull-etcd[apiclient] Found 3 Pods <span class="hljs-keyword">for</span> label selector k8s-app=upgrade-prepull-kube-controller-manager[apiclient] Found 3 Pods <span class="hljs-keyword">for</span> label selector k8s-app=upgrade-prepull-kube-apiserver[apiclient] Found 0 Pods <span class="hljs-keyword">for</span> label selector k8s-app=upgrade-prepull-kube-scheduler[apiclient] Found 3 Pods <span class="hljs-keyword">for</span> label selector k8s-app=upgrade-prepull-etcd[apiclient] Found 3 Pods <span class="hljs-keyword">for</span> label selector k8s-app=upgrade-prepull-kube-scheduler[upgrade/prepull] Prepulled image <span class="hljs-keyword">for</span> component etcd.[upgrade/prepull] Prepulled image <span class="hljs-keyword">for</span> component kube-scheduler.[apiclient] Error getting Pods with label selector <span class="hljs-string">"k8s-app=upgrade-prepull-kube-apiserver"</span> [etcdserver: request timed out][apiclient] Error getting Pods with label selector <span class="hljs-string">"k8s-app=upgrade-prepull-kube-controller-manager"</span> [etcdserver: request timed out][upgrade/prepull] Prepulled image <span class="hljs-keyword">for</span> component kube-controller-manager.[upgrade/prepull] Prepulled image <span class="hljs-keyword">for</span> component kube-apiserver.[upgrade/prepull] Successfully prepulled the images <span class="hljs-keyword">for</span> all the control plane components[upgrade/apply] Upgrading your Static Pod-hosted control plane to version <span class="hljs-string">"v1.18.0"</span>...Static pod: kube-apiserver-ukm01 <span class="hljs-built_in">hash</span>: 7e7a0d385fce06636ac0fc223274d299Static pod: kube-controller-manager-ukm01 <span class="hljs-built_in">hash</span>: fdad41767b3b13675192402b9a840a5cStatic pod: kube-scheduler-ukm01 <span class="hljs-built_in">hash</span>: 703c43ab97818f969f780a2cbf4d24b7[upgrade/etcd] Upgrading to TLS <span class="hljs-keyword">for</span> etcd[upgrade/etcd] Non fatal issue encountered during upgrade: the desired etcd version <span class="hljs-keyword">for</span> this Kubernetes version <span class="hljs-string">"v1.18.0"</span> is <span class="hljs-string">"3.4.3-0"</span>, but the current etcd version is <span class="hljs-string">"3.4.3"</span>. Won<span class="hljs-string">'t downgrade etcd, instead just continue</span><span class="hljs-string">[upgrade/staticpods] Writing new Static Pod manifests to "/etc/kubernetes/tmp/kubeadm-upgraded-manifests739015179"</span><span class="hljs-string">W0403 09:40:19.888673 21690 manifests.go:225] the default kube-apiserver authorization-mode is "Node,RBAC"; using "Node,RBAC"</span><span class="hljs-string">[upgrade/staticpods] Preparing for "kube-apiserver" upgrade</span><span class="hljs-string">[upgrade/staticpods] Renewing apiserver certificate</span><span class="hljs-string">[upgrade/staticpods] Renewing apiserver-kubelet-client certificate</span><span class="hljs-string">[upgrade/staticpods] Renewing front-proxy-client certificate</span><span class="hljs-string">[upgrade/staticpods] Renewing apiserver-etcd-client certificate</span><span class="hljs-string">[upgrade/staticpods] Moved new manifest to "/etc/kubernetes/manifests/kube-apiserver.yaml" and backed up old manifest to "/etc/kubernetes/tmp/kubeadm-backup-manifests-2020-04-03-09-40-18/kube-apiserver.yaml"</span><span class="hljs-string">[upgrade/staticpods] Waiting for the kubelet to restart the component</span><span class="hljs-string">[upgrade/staticpods] This might take a minute or longer depending on the component/version gap (timeout 5m0s)</span><span class="hljs-string">Static pod: kube-apiserver-ukm01 hash: 7e7a0d385fce06636ac0fc223274d299</span><span class="hljs-string">Static pod: kube-apiserver-ukm01 hash: 7e7a0d385fce06636ac0fc223274d299</span><span class="hljs-string">Static pod: kube-apiserver-ukm01 hash: 7e7a0d385fce06636ac0fc223274d299</span><span class="hljs-string">Static pod: kube-apiserver-ukm01 hash: 7e7a0d385fce06636ac0fc223274d299</span><span class="hljs-string">Static pod: kube-apiserver-ukm01 hash: 7e7a0d385fce06636ac0fc223274d299</span><span class="hljs-string">Static pod: kube-apiserver-ukm01 hash: 7e7a0d385fce06636ac0fc223274d299</span><span class="hljs-string">Static pod: kube-apiserver-ukm01 hash: f9ef3245bc9735275ed5508f8bbea7e1</span><span class="hljs-string">[apiclient] Found 3 Pods for label selector component=kube-apiserver</span><span class="hljs-string">[upgrade/staticpods] Component "kube-apiserver" upgraded successfully!</span><span class="hljs-string">[upgrade/staticpods] Preparing for "kube-controller-manager" upgrade</span><span class="hljs-string">[upgrade/staticpods] Renewing controller-manager.conf certificate</span><span class="hljs-string">[upgrade/staticpods] Moved new manifest to "/etc/kubernetes/manifests/kube-controller-manager.yaml" and backed up old manifest to "/etc/kubernetes/tmp/kubeadm-backup-manifests-2020-04-03-09-40-18/kube-controller-manager.yaml"</span><span class="hljs-string">[upgrade/staticpods] Waiting for the kubelet to restart the component</span><span class="hljs-string">[upgrade/staticpods] This might take a minute or longer depending on the component/version gap (timeout 5m0s)</span><span class="hljs-string">Static pod: kube-controller-manager-ukm01 hash: fdad41767b3b13675192402b9a840a5c</span><span class="hljs-string">Static pod: kube-controller-manager-ukm01 hash: 24e6dad5d81a4c5257a7b29861a48543</span><span class="hljs-string">[apiclient] Found 3 Pods for label selector component=kube-controller-manager</span><span class="hljs-string">[upgrade/staticpods] Component "kube-controller-manager" upgraded successfully!</span><span class="hljs-string">[upgrade/staticpods] Preparing for "kube-scheduler" upgrade</span><span class="hljs-string">[upgrade/staticpods] Renewing scheduler.conf certificate</span><span class="hljs-string">[upgrade/staticpods] Moved new manifest to "/etc/kubernetes/manifests/kube-scheduler.yaml" and backed up old manifest to "/etc/kubernetes/tmp/kubeadm-backup-manifests-2020-04-03-09-40-18/kube-scheduler.yaml"</span><span class="hljs-string">[upgrade/staticpods] Waiting for the kubelet to restart the component</span><span class="hljs-string">[upgrade/staticpods] This might take a minute or longer depending on the component/version gap (timeout 5m0s)</span><span class="hljs-string">Static pod: kube-scheduler-ukm01 hash: 703c43ab97818f969f780a2cbf4d24b7</span><span class="hljs-string">Static pod: kube-scheduler-ukm01 hash: b10b96b094242086df7de02b74f10de2</span><span class="hljs-string">[apiclient] Found 3 Pods for label selector component=kube-scheduler</span><span class="hljs-string">[upgrade/staticpods] Component "kube-scheduler" upgraded successfully!</span><span class="hljs-string">[upload-config] Storing the configuration used in ConfigMap "kubeadm-config" in the "kube-system" Namespace</span><span class="hljs-string">[kubelet] Creating a ConfigMap "kubelet-config-1.18" in namespace kube-system with the configuration for the kubelets in the cluster</span><span class="hljs-string">[kubelet-start] Downloading configuration for the kubelet from the "kubelet-config-1.18" ConfigMap in the kube-system namespace</span><span class="hljs-string">[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"</span><span class="hljs-string">[bootstrap-token] configured RBAC rules to allow Node Bootstrap tokens to post CSRs in order for nodes to get long term certificate credentials</span><span class="hljs-string">[bootstrap-token] configured RBAC rules to allow the csrapprover controller automatically approve CSRs from a Node Bootstrap Token</span><span class="hljs-string">[bootstrap-token] configured RBAC rules to allow certificate rotation for all node client certificates in the cluster</span><span class="hljs-string">[addons] Applied essential addon: CoreDNS</span><span class="hljs-string">[addons] Applied essential addon: kube-proxy</span><span class="hljs-string"></span><span class="hljs-string">[upgrade/successful] SUCCESS! Your cluster was upgraded to "v1.18.0". Enjoy!</span><span class="hljs-string"></span><span class="hljs-string">[upgrade/kubelet] Now that your control plane is upgraded, please proceed with upgrading your kubelets if you haven'</span>t already <span class="hljs-keyword">done</span> so.</code></pre></div><h3 id="取消隔离"><a href="#取消隔离" class="headerlink" title="取消隔离"></a>取消隔离</h3><div class="hljs"><pre><code class="hljs bash"> kubectl uncordon ukm01node/ukm01 uncordoned</code></pre></div><h3 id="升级-kubectl-和-kubelet"><a href="#升级-kubectl-和-kubelet" class="headerlink" title="升级 kubectl 和 kubelet"></a>升级 kubectl 和 kubelet</h3><div class="hljs"><pre><code class="hljs bash">apt-mark unhold kubelet kubectl && \ apt-get update && apt-get install -y kubelet=1.18.0-00 kubectl=1.18.0-00 && \ apt-mark hold kubelet kubectl或者apt-get update && \ apt-get install -y --allow-change-held-packages kubelet=1.18.0-00 kubectl=1.18.0-00</code></pre></div><h3 id="升级其他master节点-kubeadm"><a href="#升级其他master节点-kubeadm" class="headerlink" title="升级其他master节点 kubeadm"></a>升级其他master节点 kubeadm</h3><div class="hljs"><pre><code class="hljs bash">ansible master:\!ans -m shell -a <span class="hljs-string">'apt-mark unhold kubeadm && apt-get update && apt-get install -y kubeadm=1.18.0-00 && apt-mark hold kubeadm'</span>ansible master:\!ans -m shell -a <span class="hljs-string">'apt-mark unhold kubelet kubectl && apt-get update && apt-get install -y kubelet=1.18.0-00 kubectl=1.18.0-00 && apt-mark hold kubelet kubectl'</span>ansible master:\!ans -m shell -a <span class="hljs-string">'kubeadm version'</span></code></pre></div><h3 id="升级-master-节点-kubernetes"><a href="#升级-master-节点-kubernetes" class="headerlink" title="升级 master 节点 kubernetes"></a>升级 master 节点 kubernetes</h3><div class="hljs"><pre><code class="hljs bash">kubectl drain ukm02 --ignore-daemonsetsssh ukm02kubeadm upgrade node<span class="hljs-built_in">exit</span>kubectl uncordon ukm02</code></pre></div><h3 id="升级-master-节点-kubectl-和-kubelet"><a href="#升级-master-节点-kubectl-和-kubelet" class="headerlink" title="升级 master 节点 kubectl 和 kubelet"></a>升级 master 节点 kubectl 和 kubelet</h3><div class="hljs"><pre><code class="hljs bash">ansible master -m shell -a <span class="hljs-string">'apt-mark unhold kubelet kubectl && apt-get update && apt-get install -y kubelet=1.18.0-00 kubectl=1.18.0-00 && apt-mark hold kubelet kubectl'</span></code></pre></div><h3 id="重启-master-节点-kubelet"><a href="#重启-master-节点-kubelet" class="headerlink" title="重启 master 节点 kubelet"></a>重启 master 节点 kubelet</h3><div class="hljs"><pre><code class="hljs bash">ansible master -m shell -a <span class="hljs-string">'systemctl restart kubelet'</span></code></pre></div><h3 id="确认升级成功"><a href="#确认升级成功" class="headerlink" title="确认升级成功"></a>确认升级成功</h3><div class="hljs"><pre><code class="hljs bash"> kubectl get nodesNAME STATUS ROLES AGE VERSIONuki01 Ready infra 8d v1.17.3uki02 Ready infra 8d v1.17.3uki03 Ready infra 8d v1.17.3ukm01 Ready master 8d v1.18.0ukm02 Ready master 8d v1.18.0ukm03 Ready master 8d v1.18.0ukn01 Ready worker 8d v1.17.3ukn02 Ready worker 8d v1.17.3ukn03 Ready worker 8d v1.17.3</code></pre></div><h3 id="升级-infra-和-worker-节点-kubeadm"><a href="#升级-infra-和-worker-节点-kubeadm" class="headerlink" title="升级 infra 和 worker 节点 kubeadm"></a>升级 infra 和 worker 节点 kubeadm</h3><div class="hljs"><pre><code class="hljs bash">ansible infra:worker -m shell -a <span class="hljs-string">'apt-mark unhold kubeadm && apt-get update && apt-get install -y kubeadm=1.18.0-00 && apt-mark hold kubeadm'</span></code></pre></div><h3 id="升级-infra-和-worker-节点-kubernetes"><a href="#升级-infra-和-worker-节点-kubernetes" class="headerlink" title="升级 infra 和 worker 节点 kubernetes"></a>升级 infra 和 worker 节点 kubernetes</h3><div class="hljs"><pre><code class="hljs bash">kubectl drain uki01 --ignore-daemonsetsssh uki01kubeadm upgrade node<span class="hljs-built_in">exit</span>kubectl uncordon uki01</code></pre></div><h3 id="升级-infra-和-worker-节点-kubectl-和-kubelet"><a href="#升级-infra-和-worker-节点-kubectl-和-kubelet" class="headerlink" title="升级 infra 和 worker 节点 kubectl 和 kubelet"></a>升级 infra 和 worker 节点 kubectl 和 kubelet</h3><div class="hljs"><pre><code class="hljs bash">ansible infra:worker -m shell -a <span class="hljs-string">'apt-mark unhold kubelet kubectl && apt-get update && apt-get install -y kubelet=1.18.0-00 kubectl=1.18.0-00 && apt-mark hold kubelet kubectl'</span></code></pre></div><h3 id="重启-infra-和-worker-节点-kubelet"><a href="#重启-infra-和-worker-节点-kubelet" class="headerlink" title="重启 infra 和 worker 节点 kubelet"></a>重启 infra 和 worker 节点 kubelet</h3><div class="hljs"><pre><code class="hljs bash">ansible infra:worker -m shell -a <span class="hljs-string">'systemctl restart kubelet'</span></code></pre></div><h3 id="检查-kubernetes-升级状态"><a href="#检查-kubernetes-升级状态" class="headerlink" title="检查 kubernetes 升级状态"></a>检查 kubernetes 升级状态</h3><div class="hljs"><pre><code class="hljs bash"> kubectl get nodesNAME STATUS ROLES AGE VERSIONuki01 Ready infra 8d v1.18.0uki02 Ready infra 8d v1.18.0uki03 Ready infra 8d v1.18.0ukm01 Ready master 8d v1.18.0ukm02 Ready master 8d v1.18.0ukm03 Ready master 8d v1.18.0ukn01 Ready worker 8d v1.18.0ukn02 Ready worker 8d v1.18.0ukn03 Ready worker 8d v1.18.0</code></pre></div><h2 id="安装-router"><a href="#安装-router" class="headerlink" title="安装 router"></a><strong>安装 router</strong></h2><p>router 是 openshift 开源的 ingress 服务,router 服务默认安装在 default namespace 下面。</p><h3 id="配置-rbac-文件"><a href="#配置-rbac-文件" class="headerlink" title="配置 rbac 文件"></a>配置 rbac 文件</h3><div class="hljs"><pre><code class="hljs bash"> cat > router/rbac.yaml <<EOF---apiVersion: rbac.authorization.k8s.io/v1beta1kind: ClusterRolemetadata: name: openshift-router namespace: defaultrules:- apiGroups: [<span class="hljs-string">""</span>] resources: [<span class="hljs-string">"namespaces"</span>, <span class="hljs-string">"services"</span>, <span class="hljs-string">"endpoints"</span>] verbs: [<span class="hljs-string">"get"</span>, <span class="hljs-string">"list"</span>, <span class="hljs-string">"watch"</span>]- apiGroups: [<span class="hljs-string">"route.openshift.io"</span>] resources: [<span class="hljs-string">"routes"</span>] verbs: [<span class="hljs-string">"list"</span>, <span class="hljs-string">"watch"</span>]- apiGroups: [<span class="hljs-string">"route.openshift.io"</span>] resources: [<span class="hljs-string">"routes/status"</span>] verbs: [<span class="hljs-string">"get"</span>, <span class="hljs-string">"patch"</span>, <span class="hljs-string">"update"</span>]---apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRoleBindingmetadata: name: openshift-routerroleRef: apiGroup: <span class="hljs-string">""</span> kind: ClusterRole name: openshift-routersubjects:- kind: ServiceAccount namespace: default name: router---apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRoleBindingmetadata: name: openshift-router-auth-delegatorroleRef: apiGroup: <span class="hljs-string">""</span> kind: ClusterRole name: system:auth-delegatorsubjects:- kind: ServiceAccount namespace: default name: routerEOF</code></pre></div><h3 id="配置-serviceaccount-文件"><a href="#配置-serviceaccount-文件" class="headerlink" title="配置 serviceaccount 文件"></a>配置 serviceaccount 文件</h3><div class="hljs"><pre><code class="hljs bash"> cat > router/serviceaccount.yaml <<EOF---apiVersion: v1kind: ServiceAccountmetadata: name: router namespace: defaultEOF</code></pre></div><h3 id="配置-crd-文件"><a href="#配置-crd-文件" class="headerlink" title="配置 crd 文件"></a>配置 crd 文件</h3><div class="hljs"><pre><code class="hljs bash"> cat > router/crd.yaml <<EOF---apiVersion: apiextensions.k8s.io/v1beta1kind: CustomResourceDefinitionmetadata: name must match the spec fields below, and be <span class="hljs-keyword">in</span> the form: <plural>.<group> name: routes.route.openshift.io namespace: defaultspec: group name to use <span class="hljs-keyword">for</span> REST API: /apis/<group>/<version> group: route.openshift.io list of versions supported by this CustomResourceDefinition versions: - name: v1 Each version can be enabled/disabled by Served flag. served: <span class="hljs-literal">true</span> One and only one version must be marked as the storage version. storage: <span class="hljs-literal">true</span> either Namespaced or Cluster scope: Namespaced subresources: <span class="hljs-built_in">enable</span> spec/status status: {} names: plural name to be used <span class="hljs-keyword">in</span> the URL: /apis/<group>/<version>/<plural> plural: routes singular name to be used as an <span class="hljs-built_in">alias</span> on the CLI and <span class="hljs-keyword">for</span> display singular: route kind is normally the CamelCased singular <span class="hljs-built_in">type</span>. Your resource manifests use this. kind: Route additionalPrinterColumns: - name: Host <span class="hljs-built_in">type</span>: string JSONPath: .status.ingress[0].host - name: Admitted <span class="hljs-built_in">type</span>: string JSONPath: .status.ingress[0].conditions[?(@.<span class="hljs-built_in">type</span>==<span class="hljs-string">"Admitted"</span>)].status - name: Service <span class="hljs-built_in">type</span>: string JSONPath: .spec.to.name - name: TLS <span class="hljs-built_in">type</span>: string JSONPath: .spec.tls.typeEOF</code></pre></div><h3 id="配置-router-安装文件"><a href="#配置-router-安装文件" class="headerlink" title="配置 router 安装文件"></a>配置 router 安装文件</h3><div class="hljs"><pre><code class="hljs bash"> cat > router/deployment.yaml <<EOF---apiVersion: apps/v1kind: DaemonSet Deployment DaemonSetmetadata: name: router namespace: default labels: k8s-app: routerspec: replicas: 3 如果是 DaemonSet,可以不设置副本数量。 selector: matchLabels: k8s-app: router template: metadata: labels: k8s-app: router spec: serviceAccountName: router nodeSelector: node-role.kubernetes.io/infra: <span class="hljs-string">"true"</span> containers: - env: - name: ROUTER_LISTEN_ADDR value: 0.0.0.0:1936 - name: ROUTER_METRICS_TYPE value: haproxy - name: ROUTER_SERVICE_HTTPS_PORT value: <span class="hljs-string">"443"</span> - name: ROUTER_SERVICE_HTTP_PORT value: <span class="hljs-string">"80"</span> - name: ROUTER_THREADS value: <span class="hljs-string">"4"</span> image: openshift/origin-haproxy-router:v4.0.0 imagePullPolicy: IfNotPresent livenessProbe: httpGet: host: localhost path: /healthz port: 1936 initialDelaySeconds: 10 name: router ports: - containerPort: 80 - containerPort: 443 - containerPort: 1936 name: stats protocol: TCP readinessProbe: httpGet: host: localhost path: healthz/ready port: 1936 initialDelaySeconds: 10 resources: requests: cpu: 100m memory: 256Mi hostNetwork: <span class="hljs-literal">true</span>EOF</code></pre></div><h3 id="安装-router-服务"><a href="#安装-router-服务" class="headerlink" title="安装 router 服务"></a>安装 router 服务</h3><div class="hljs"><pre><code class="hljs bash">kubectl apply -f router/serviceaccount.yamlkubectl apply -f router/rbac.yamlkubectl apply -f router/deployment.yamlkubectl apply -f router/crd.yaml</code></pre></div><h3 id="检查端口监听"><a href="#检查端口监听" class="headerlink" title="检查端口监听"></a>检查端口监听</h3><p>load blance 配置 TCP 80/443/1936 端口转发到 infra 节点服务器。</p><div class="hljs"><pre><code class="hljs bash">ansible infra -m shell -a <span class="hljs-string">'netstat -lntp|grep -E "80|443|1936"'</span></code></pre></div><h3 id="校验-route-服务"><a href="#校验-route-服务" class="headerlink" title="校验 route 服务"></a>校验 route 服务</h3><p>安装nginx</p><div class="hljs"><pre><code class="hljs bash">kubectl create deployment nginx --image nginx:alpinekubectl expose deployment nginx --port=80</code></pre></div><h3 id="创建-nginx-route"><a href="#创建-nginx-route" class="headerlink" title="创建 nginx route"></a>创建 nginx route</h3><div class="hljs"><pre><code class="hljs bash"> kubectl apply -f - <<EOF---apiVersion: route.openshift.io/v1kind: Routemetadata: name: nginx labels: app: nginxspec: host: test.mokr.cn to: kind: Service name: nginx weight: 100 wildcardPolicy: NoneEOF</code></pre></div><h3 id="访问验证"><a href="#访问验证" class="headerlink" title="访问验证"></a>访问验证</h3><div class="hljs"><pre><code class="hljs bash"> curl -ik http://test.mokr.cn --resolve <span class="hljs-string">'test.mokr.cn:80:10.10.34.89'</span>HTTP/1.1 200 OKServer: nginx/1.17.9Date: Tue, 24 Mar 2020 07:01:43 GMTContent-Type: text/htmlContent-Length: 612Last-Modified: Tue, 03 Mar 2020 17:36:53 GMTETag: <span class="hljs-string">"5e5e95b5-264"</span>Accept-Ranges: bytesSet-Cookie: 3bc5f7a8aec67b74b33e81d956f57cb9=5ffd533ac952a924e0ec8b6f1c601703; path=/; HttpOnlyCache-control: private<!DOCTYPE html><html><head><title>Welcome to nginx!</title><style> body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; }</style></head><body><h1>Welcome to nginx!</h1><p>If you see this page, the nginx web server is successfully installed andworking. Further configuration is required.</p><p>For online documentation and support please refer to<a href=<span class="hljs-string">"http://nginx.org/"</span>>nginx.org</a>.<br/>Commercial support is available at<a href=<span class="hljs-string">"http://nginx.com/"</span>>nginx.com</a>.</p><p><em>Thank you <span class="hljs-keyword">for</span> using nginx.</em></p></body></html> 或者 curl -ik http://10.10.34.89 -H <span class="hljs-string">'Host: test.mokr.cn'</span> -v</code></pre></div><h3 id="删除-router"><a href="#删除-router" class="headerlink" title="删除 router"></a>删除 router</h3><div class="hljs"><pre><code class="hljs bash">kubectl delete -f router.yaml</code></pre></div><h2 id="安装-kubernetes-dashboard-2-0"><a href="#安装-kubernetes-dashboard-2-0" class="headerlink" title="安装 kubernetes dashboard 2.0"></a><strong>安装 kubernetes dashboard 2.0</strong></h2><div class="hljs"><pre><code class="hljs bash">curl -L https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0-beta4/aio/deploy/recommended.yaml -o kubernetes-dashboard.yaml</code></pre></div><h3 id="签发-https-访问证书"><a href="#签发-https-访问证书" class="headerlink" title="签发 https 访问证书"></a>签发 https 访问证书</h3><ul><li>Can’t load /root/.rnd into RNG</li></ul><div class="hljs"><pre><code class="hljs bash"><span class="hljs-built_in">cd</span> /rootopenssl rand -writerand .rnd</code></pre></div><h3 id="dashboard-多域名配置"><a href="#dashboard-多域名配置" class="headerlink" title="dashboard 多域名配置"></a>dashboard 多域名配置</h3><div class="hljs"><pre><code class="hljs bash"> mkdir -p /certs cp /etc/ssl/openssl.cnf /certs vim /certs/openssl.cnf...[ req_distinguished_name ]countryName = Country Name (2 letter code)countryName_default = CNcountryName_min = 2countryName_max = 2stateOrProvinceName = State or Province Name (full name)stateOrProvinceName_default = GuangDonglocalityName = Locality Name (eg, city)localityName_default = ShenZhen0.organizationName = Organization Name (eg, company)0.organizationName_default = MOKR LtdorganizationalUnitName = Organizational Unit Name (eg, section)organizationalUnitName_default = OPScommonName = Common Name (e.g. server FQDN or YOUR name)commonName_max = 64emailAddress = Email AddressemailAddress_max = 64emailAddress_default = [email protected][ v3_req ]basicConstraints = CA:FALSEkeyUsage = nonRepudiation, digitalSignature, keyEnciphermentsubjectAltName = @alt_names[alt_names]DNS.1 = dashboard.mokr.cnDNS.2 = kubernetes-dashboard.kubernetes-dashboard.svcDNS.3 = kubernetes-dashboard.kubernetes-dashboard.svc.cluster.localIP.1 = 10.104.71.164IP.2 = 10.10.34.89...</code></pre></div><h3 id="签发-dashboard-密钥"><a href="#签发-dashboard-密钥" class="headerlink" title="签发 dashboard 密钥"></a>签发 dashboard 密钥</h3><div class="hljs"><pre><code class="hljs bash">openssl req -nodes -newkey rsa:2048 \ -keyout /certs/dashboard.key \ -out /certs/dashboard.csr \ -subj <span class="hljs-string">"/C=CN/ST=GuangDong/L=ShenZhen/O=Mokr LTD/OU=OPS"</span> \ -config /certs/openssl.cnf \ -extensions v3_req</code></pre></div><h3 id="签发-dashboard-证书"><a href="#签发-dashboard-证书" class="headerlink" title="签发 dashboard 证书"></a>签发 dashboard 证书</h3><div class="hljs"><pre><code class="hljs bash">私钥签发openssl x509 -req -sha256 -days 365 \ -<span class="hljs-keyword">in</span> /certs/dashboard.csr \ -signkey /certs/dashboard.key \ -out /certs/dashboard.crt \ -extfile /certs/openssl.cnf \ -extensions v3_reqca 签发openssl x509 -req -days 365 \ -<span class="hljs-keyword">in</span> /certs/dashboard.csr \ -CA /etc/kubernetes/pki/ca.crt \ -CAkey /etc/kubernetes/pki/ca.key \ -CAcreateserial \ -out /certs/dashboard.crt \ -extfile /certs/openssl.cnf \ -extensions v3_req</code></pre></div><h3 id="验证-dashboard-证书"><a href="#验证-dashboard-证书" class="headerlink" title="验证 dashboard 证书"></a>验证 dashboard 证书</h3><div class="hljs"><pre><code class="hljs bash"> openssl x509 -text -noout -<span class="hljs-keyword">in</span> /certs/dashboard.crtCertificate:... X509v3 extensions: X509v3 Basic Constraints: CA:FALSE X509v3 Key Usage: Digital Signature, Non Repudiation, Key Encipherment X509v3 Subject Alternative Name: DNS:dashboard.mokr.cn, DNS:kubernetes-dashboard.kubernetes-dashboard.svc, DNS:kubernetes-dashboard.kubernetes-dashboard.svc.cluster.local, IP Address:10.104.71.164, IP Address:10.10.34.89...</code></pre></div><h3 id="部署-dashboard"><a href="#部署-dashboard" class="headerlink" title="部署 dashboard"></a>部署 dashboard</h3><div class="hljs"><pre><code class="hljs bash">kubectl apply -f kubernetes-dashboard.yaml</code></pre></div><h3 id="为-dashboard-配置-secret-证书"><a href="#为-dashboard-配置-secret-证书" class="headerlink" title="为 dashboard 配置 secret 证书"></a>为 dashboard 配置 secret 证书</h3><div class="hljs"><pre><code class="hljs bash">kubectl create secret generic kubernetes-dashboard-certs --from-file=/certs/dashboard -n kubernetes-dashboard可不配置kubectl create secret tls kubernetes-dashboard-tls --cert=/certs/dashboard/dashboard.crt --key=/certs/dashboard/dashboard.key -n kubernetes-dashboard</code></pre></div><h3 id="校验-dashboard"><a href="#校验-dashboard" class="headerlink" title="校验 dashboard"></a>校验 dashboard</h3><div class="hljs"><pre><code class="hljs bash"> kubectl get pods,svc -n kubernetes-dashboardNAME READY STATUS RESTARTS AGEpod/dashboard-metrics-scraper-566cddb686-m5dzb 1/1 Running 0 22hpod/kubernetes-dashboard-7b5bf5d559-dbrh2 1/1 Running 0 22hNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEservice/dashboard-metrics-scraper ClusterIP 10.107.240.229 <none> 8000/TCP 22hservice/kubernetes-dashboard ClusterIP 10.96.188.203 <none> 443/TCP 22h</code></pre></div><h3 id="创建-dashboard-route"><a href="#创建-dashboard-route" class="headerlink" title="创建 dashboard route"></a>创建 dashboard route</h3><div class="hljs"><pre><code class="hljs bash"> kubectl apply -f - <<EOF---apiVersion: route.openshift.io/v1kind: Routemetadata: name: kubernetes-dashboard labels: app: kubernetes-dashboard namespace: kubernetes-dashboardspec: host: dashboard.mokr.cn tls: insecureEdgeTerminationPolicy: Redirect <span class="hljs-comment">## None Allow Redirect</span> termination: reencrypt <span class="hljs-comment">## edge passthrough reencrypt</span> to: kind: Service name: kubernetes-dashboard weight: 100 wildcardPolicy: NoneEOF</code></pre></div><h3 id="curl-访问-dashboard"><a href="#curl-访问-dashboard" class="headerlink" title="curl 访问 dashboard"></a>curl 访问 dashboard</h3><div class="hljs"><pre><code class="hljs bash">curl -ik https://dashboard.mokr.cn --resolve <span class="hljs-string">'dashboard.mokr.cn:443:10.10.34.89'</span></code></pre></div><h3 id="或者"><a href="#或者" class="headerlink" title="或者"></a>或者</h3><div class="hljs"><pre><code class="hljs bash">curl -ik https://10.10.34.89 -H <span class="hljs-string">'Host: dashboard.mokr.cn'</span></code></pre></div><h3 id="创建-dashboard-用户"><a href="#创建-dashboard-用户" class="headerlink" title="创建 dashboard 用户"></a>创建 dashboard 用户</h3><div class="hljs"><pre><code class="hljs bash"> 创建用户 kubectl create serviceaccount seanzhau -n kubernetes-dashboard 添加用户授权,配置集群 admin 权限<span class="hljs-comment">## ex: kubectl create clusterrolebinding cluster-admin --clusterrole=cluster-admin --user=user1 --user=user2 --group=group1</span> kubectl create clusterrolebinding dashboard-admin --clusterrole cluster-admin --serviceaccount kubernetes-dashboard:seanzhau 或者 kubectl apply -f - <<EOF---apiVersion: v1kind: ServiceAccountmetadata: name: seanzhau namespace: kubernetes-dashboard labels: kubernetes.io/cluster-service: <span class="hljs-string">"true"</span> addonmanager.kubernetes.io/mode: Reconcile---apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRoleBindingmetadata: name: dashboard-admin namespace: kubernetes-dashboard annotations: rbac.authorization.kubernetes.io/autoupdate: <span class="hljs-string">"true"</span>roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: cluster-adminsubjects:- kind: ServiceAccount name: seanzhau namespace: kubernetes-dashboardEOF</code></pre></div><h3 id="查看-secret-信息"><a href="#查看-secret-信息" class="headerlink" title="查看 secret 信息"></a>查看 secret 信息</h3><div class="hljs"><pre><code class="hljs bash">查看 secret 私钥或者 ca 信息kubectl -n kubernetes-dashboard get secret -o jsonpath=<span class="hljs-string">'{range .items[?(@.metadata.annotations.kubernetes\.io/service-account\.name=="seanzhau")].data}{.ca\.crt}{end}'</span> | base64 -d查看用户 tokenkubectl -n kubernetes-dashboard get secret -o jsonpath=<span class="hljs-string">'{range .items[?(@.metadata.annotations.kubernetes\.io/service-account\.name=="seanzhau")].data}{.token}{end}'</span> | base64 -d</code></pre></div><h3 id="浏览器访问"><a href="#浏览器访问" class="headerlink" title="浏览器访问"></a>浏览器访问</h3><p>浏览器打开 <code>https://dashboard.mokr.cn</code> ,选择 Token ,输入获取的用户 token。</p><h3 id="使用-kubeconfig-登录"><a href="#使用-kubeconfig-登录" class="headerlink" title="使用 kubeconfig 登录"></a>使用 kubeconfig 登录</h3><p>下载 /etc/kubernetes/admin.conf 到本地,修改配置文件,在最底部添加 token 字段。</p><div class="hljs"><pre><code class="hljs bash"> vim admin.conf...users:- name: kubernetes-admin user: client-certificate-data: xxxxxxxxxxxxxxxxxxxxx client-key-data: xxxxxxxxxxxxxxxxxxxxx token: xxxxxxxxxxxxxxxxxxxxx</code></pre></div><h2 id="安装-ceph-存储"><a href="#安装-ceph-存储" class="headerlink" title="安装 ceph 存储"></a><strong>安装 ceph 存储</strong></h2><h3 id="ceph-ansible-安装"><a href="#ceph-ansible-安装" class="headerlink" title="ceph ansible 安装"></a>ceph ansible 安装</h3><div class="hljs"><pre><code class="hljs bash">git <span class="hljs-built_in">clone</span> -b stable-5.0 https://github.com/ceph/ceph-ansible.git<span class="hljs-built_in">cd</span> ceph-ansible</code></pre></div><h3 id="安装-pip"><a href="#安装-pip" class="headerlink" title="安装 pip"></a>安装 pip</h3><div class="hljs"><pre><code class="hljs bash">apt install -y python-pip</code></pre></div><h3 id="配置-pip-源"><a href="#配置-pip-源" class="headerlink" title="配置 pip 源"></a>配置 pip 源</h3><div class="hljs"><pre><code class="hljs bash"> mkdir -p ~/.pip cat > ~/.pip/pip.conf <<EOF[global]index-url = https://mirrors.aliyun.com/pypi/simple/[install]trusted-host=mirrors.aliyun.comEOF</code></pre></div><h3 id="安装-ansible-以及依赖"><a href="#安装-ansible-以及依赖" class="headerlink" title="安装 ansible 以及依赖"></a>安装 ansible 以及依赖</h3><div class="hljs"><pre><code class="hljs bash">pip install -r requirements.txt</code></pre></div><h3 id="配置-ansible-资产文件"><a href="#配置-ansible-资产文件" class="headerlink" title="配置 ansible 资产文件"></a>配置 ansible 资产文件</h3><div class="hljs"><pre><code class="hljs bash"> cat > dummy-ansible-hosts <<EOF[mons]10.10.34.2910.10.34.3110.10.34.42[osds]10.10.34.2910.10.34.3110.10.34.42[mdss]10.10.34.2910.10.34.3110.10.34.42[rgws]10.10.34.2910.10.34.3110.10.34.42[mgrs]10.10.34.2910.10.34.3110.10.34.42[grafana-server]10.10.34.29[clients]10.10.34.2910.10.34.3110.10.34.42EOF</code></pre></div><h3 id="配置-ceph-安装文件"><a href="#配置-ceph-安装文件" class="headerlink" title="配置 ceph 安装文件"></a>配置 ceph 安装文件</h3><div class="hljs"><pre><code class="hljs bash"> docker 安装 cp site-container.yml.sample site-container.yml 二进制文件安装 cp site.yml.sample site.yml cat > group_vars/all.yml <<EOF---cluster: cephgenerate_fsid: <span class="hljs-literal">true</span>journal_size: 512ceph_origin: repositoryceph_repository: community docker 安装ceph_docker_image: <span class="hljs-string">"ceph/daemon"</span>ceph_docker_image_tag: latest-octopuscontainerized_deployment: <span class="hljs-literal">true</span>ceph_docker_registry: docker.ioceph_stable_repo: <span class="hljs-string">"{{ ceph_mirror }}/debian-{{ ceph_stable_release }}"</span> 二进制安装ceph_mirror: http://mirrors.aliyun.com/cephceph_stable_key: http://mirrors.aliyun.com/ceph/keys/release.ascceph_stable_release: octopuspublic_network: <span class="hljs-string">"10.10.34.0/24"</span>cluster_network: <span class="hljs-string">"10.10.34.0/24"</span>monitor_interface: eth0osd_objectstore: bluestoredmcrypt: <span class="hljs-literal">false</span>devices: - /dev/vdb - /dev/vdcradosgw_interface: eth0radosgw_address: 0.0.0.0radosgw_address_block: <span class="hljs-string">"10.10.34.0/24"</span>grafana_admin_user: seanzhaugrafana_admin_password: iPaaS2020dashboard_enabled: Truedashboard_admin_user: seanzhaudashboard_admin_password: iPaaS2020EOF</code></pre></div><h3 id="安装-ceph-集群"><a href="#安装-ceph-集群" class="headerlink" title="安装 ceph 集群"></a>安装 ceph 集群</h3><div class="hljs"><pre><code class="hljs bash">docker 安装ansible-playbook -i dummy-ansible-hosts site-container.yml二进制文件安装ansible-playbook -i dummy-ansible-hosts site.yml</code></pre></div><h3 id="非集群节点安装-ceph-客户端"><a href="#非集群节点安装-ceph-客户端" class="headerlink" title="非集群节点安装 ceph 客户端"></a>非集群节点安装 ceph 客户端</h3><div class="hljs"><pre><code class="hljs bash">apt install -y ceph-common</code></pre></div><h3 id="配置客户端"><a href="#配置客户端" class="headerlink" title="配置客户端"></a>配置客户端</h3><div class="hljs"><pre><code class="hljs bash"> 配置 ceph.conf cat > /etc/ceph/ceph.conf <<EOF[global]mon host = 10.10.34.29,10.10.34.31,10.10.34.42EOF 从 ceph 集群复制 admin key scp 10.10.34.29:/etc/ceph/ceph.client.admin.keyring /etc/ceph/ceph.client.admin.keyring</code></pre></div><h3 id="查看集群状态"><a href="#查看集群状态" class="headerlink" title="查看集群状态"></a>查看集群状态</h3><div class="hljs"><pre><code class="hljs bash">ceph -s cluster: id: 5ae77bc9-1831-42df-8ef2-956499ce685b health: HEALTH_WARN 1 pools have too few placement groups services: mon: 3 daemons, quorum ceph01,ceph02,ceph03 (age 15m) mgr: ceph01(active, since 14m), standbys: ceph02, ceph03 mds: cephfs:1 {0=ceph01=up:active} 2 up:standby osd: 6 osds: 6 up (since 19m), 6 <span class="hljs-keyword">in</span> (since 19m) rgw: 3 daemons active (ceph01.rgw0, ceph02.rgw0, ceph03.rgw0) task status: scrub status: mds.ceph01: idle data: pools: 7 pools, 121 pgs objects: 215 objects, 11 KiB usage: 6.1 GiB used, 2.9 TiB / 2.9 TiB avail pgs: 121 active+clean</code></pre></div><h3 id="查看磁盘状态"><a href="#查看磁盘状态" class="headerlink" title="查看磁盘状态"></a>查看磁盘状态</h3><div class="hljs"><pre><code class="hljs bash"> ceph osd treeID CLASS WEIGHT TYPE NAME STATUS REWEIGHT PRI-AFF-1 2.92978 root default-3 0.97659 host ceph01 0 hdd 0.48830 osd.0 up 1.00000 1.00000 3 hdd 0.48830 osd.3 up 1.00000 1.00000-7 0.97659 host ceph02 2 hdd 0.48830 osd.2 up 1.00000 1.00000 5 hdd 0.48830 osd.5 up 1.00000 1.00000-5 0.97659 host ceph03 1 hdd 0.48830 osd.1 up 1.00000 1.00000 4 hdd 0.48830 osd.4 up 1.00000 1.00000</code></pre></div><h3 id="挂载-ceph"><a href="#挂载-ceph" class="headerlink" title="挂载 ceph"></a>挂载 ceph</h3><div class="hljs"><pre><code class="hljs bash"> mount.ceph 10.10.34.29,10.10.34.31,10.10.34.42:/ /mnt/ -o name=admin,secret=admin-password secretfile=/root/admin.secret df -h /mnt/Filesystem Size Used Avail Use% Mounted on10.10.34.29,10.10.34.31,10.10.34.42:/ 948G 0 948G 0% /mnt</code></pre></div><h3 id="删除集群"><a href="#删除集群" class="headerlink" title="删除集群"></a>删除集群</h3><div class="hljs"><pre><code class="hljs bash">docker 卸载ansible-playbook -i dummy-ansible-hosts infrastructure-playbooks/purge-container-cluster.yml二进制文件卸载ansible-playbook -i dummy-ansible-hosts infrastructure-playbooks/purge-cluster.yml</code></pre></div><h3 id="删除磁盘分区信息"><a href="#删除磁盘分区信息" class="headerlink" title="删除磁盘分区信息"></a>删除磁盘分区信息</h3><p>RuntimeError: Unable to use device, already a member of LVM: /dev/vdb</p><div class="hljs"><pre><code class="hljs bash">ansible -i dummy-ansible-hosts osds -m shell -a <span class="hljs-string">'wipefs -a /dev/vdb --force'</span></code></pre></div><h2 id="配置-provisioner"><a href="#配置-provisioner" class="headerlink" title="配置 provisioner"></a><strong>配置 provisioner</strong></h2><h3 id="下载-ceph-images"><a href="#下载-ceph-images" class="headerlink" title="下载 ceph images"></a>下载 ceph images</h3><div class="hljs"><pre><code class="hljs bash">docker pull quay.io/external_storage/rbd-provisioner:latestdocker pull quay.io/external_storage/cephfs-provisioner:latest</code></pre></div><h3 id="创建-ceph-namespace"><a href="#创建-ceph-namespace" class="headerlink" title="创建 ceph namespace"></a>创建 ceph namespace</h3><div class="hljs"><pre><code class="hljs bash">kubectl create namespace ceph</code></pre></div><h3 id="安装-cephfs"><a href="#安装-cephfs" class="headerlink" title="安装 cephfs"></a>安装 cephfs</h3><h4 id="生成-cephfs-admin-密码"><a href="#生成-cephfs-admin-密码" class="headerlink" title="生成 cephfs admin 密码"></a>生成 cephfs admin 密码</h4><div class="hljs"><pre><code class="hljs bash">ceph auth get-key client.admin > /tmp/key</code></pre></div><h4 id="创建-cephfs-secret"><a href="#创建-cephfs-secret" class="headerlink" title="创建 cephfs secret"></a>创建 cephfs secret</h4><div class="hljs"><pre><code class="hljs bash">kubectl create secret generic cephfs-secret-admin --from-file=/tmp/key --namespace=ceph --<span class="hljs-built_in">type</span>=kubernetes.io/cephfs</code></pre></div><h4 id="创建-cephfs-安装文件"><a href="#创建-cephfs-安装文件" class="headerlink" title="创建 cephfs 安装文件"></a>创建 cephfs 安装文件</h4><div class="hljs"><pre><code class="hljs bash">mkdir -p storageClass/cephfs</code></pre></div><h3 id="重新构建-image"><a href="#重新构建-image" class="headerlink" title="重新构建 image"></a>重新构建 image</h3><p>如果有权限问题,可以按操作重新制造 image。</p><div class="hljs"><pre><code class="hljs bash"> cat > storageClass/cephfs/Dockerfile <<EOFFROM quay.io/external_storage/cephfs-provisioner:latestUSER rootRUN sed -i <span class="hljs-string">'s|0o755|0o777|g'</span> /usr/lib/python2.7/site-packages/ceph_volume_client.pyEOF docker build -t quay.io/external_storage/cephfs-provisioner:new storageClass/cephfs/</code></pre></div><h4 id="创建-cephfs-deployment-安装文件"><a href="#创建-cephfs-deployment-安装文件" class="headerlink" title="创建 cephfs deployment 安装文件"></a>创建 cephfs deployment 安装文件</h4><div class="hljs"><pre><code class="hljs bash"> cat > storageClass/cephfs/deployment.yaml <<EOF---apiVersion: apps/v1kind: Deploymentmetadata: name: cephfs-provisioner namespace: cephspec: replicas: 1 selector: matchLabels: app: cephfs-provisioner strategy: <span class="hljs-built_in">type</span>: Recreate template: metadata: labels: app: cephfs-provisioner spec: nodeSelector: node-role.kubernetes.io/infra: <span class="hljs-string">"true"</span> containers: - name: cephfs-provisioner image: <span class="hljs-string">"quay.io/external_storage/cephfs-provisioner:latest"</span> imagePullPolicy: IfNotPresent env: - name: PROVISIONER_NAME value: ceph.com/cephfs - name: PROVISIONER_SECRET_NAMESPACE value: ceph <span class="hljs-built_in">command</span>: - <span class="hljs-string">"/usr/local/bin/cephfs-provisioner"</span> args: - <span class="hljs-string">"-id=cephfs-provisioner-1"</span> serviceAccount: cephfs-provisionerEOF</code></pre></div><h4 id="创建-cephfs-serviceaccount-安装文件"><a href="#创建-cephfs-serviceaccount-安装文件" class="headerlink" title="创建 cephfs serviceaccount 安装文件"></a>创建 cephfs serviceaccount 安装文件</h4><div class="hljs"><pre><code class="hljs bash"> cat > storageClass/cephfs/serviceaccount.yaml <<EOF---apiVersion: v1kind: ServiceAccountmetadata: name: cephfs-provisioner namespace: cephEOF</code></pre></div><h4 id="创建-cephfs-rbac-安装文件"><a href="#创建-cephfs-rbac-安装文件" class="headerlink" title="创建 cephfs rbac 安装文件"></a>创建 cephfs rbac 安装文件</h4><div class="hljs"><pre><code class="hljs bash"> cat > storageClass/cephfs/rbac.yaml <<EOF---apiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata: name: cephfs-provisioner namespace: cephrules: - apiGroups: [<span class="hljs-string">""</span>] resources: [<span class="hljs-string">"secrets"</span>] verbs: [<span class="hljs-string">"create"</span>, <span class="hljs-string">"get"</span>, <span class="hljs-string">"delete"</span>] - apiGroups: [<span class="hljs-string">""</span>] resources: [<span class="hljs-string">"endpoints"</span>] verbs: [<span class="hljs-string">"get"</span>, <span class="hljs-string">"list"</span>, <span class="hljs-string">"watch"</span>, <span class="hljs-string">"create"</span>, <span class="hljs-string">"update"</span>, <span class="hljs-string">"patch"</span>]---apiVersion: rbac.authorization.k8s.io/v1kind: RoleBindingmetadata: name: cephfs-provisioner namespace: cephsubjects:- kind: ServiceAccount name: cephfs-provisionerroleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: cephfs-provisioner---kind: ClusterRoleapiVersion: rbac.authorization.k8s.io/v1metadata: name: cephfs-provisioner namespace: cephrules: - apiGroups: [<span class="hljs-string">""</span>] resources: [<span class="hljs-string">"persistentvolumes"</span>] verbs: [<span class="hljs-string">"get"</span>, <span class="hljs-string">"list"</span>, <span class="hljs-string">"watch"</span>, <span class="hljs-string">"create"</span>, <span class="hljs-string">"delete"</span>] - apiGroups: [<span class="hljs-string">""</span>] resources: [<span class="hljs-string">"persistentvolumeclaims"</span>] verbs: [<span class="hljs-string">"get"</span>, <span class="hljs-string">"list"</span>, <span class="hljs-string">"watch"</span>, <span class="hljs-string">"update"</span>] - apiGroups: [<span class="hljs-string">"storage.k8s.io"</span>] resources: [<span class="hljs-string">"storageclasses"</span>] verbs: [<span class="hljs-string">"get"</span>, <span class="hljs-string">"list"</span>, <span class="hljs-string">"watch"</span>] - apiGroups: [<span class="hljs-string">""</span>] resources: [<span class="hljs-string">"events"</span>] verbs: [<span class="hljs-string">"create"</span>, <span class="hljs-string">"update"</span>, <span class="hljs-string">"patch"</span>] - apiGroups: [<span class="hljs-string">""</span>] resources: [<span class="hljs-string">"services"</span>] resourceNames: [<span class="hljs-string">"kube-dns"</span>,<span class="hljs-string">"coredns"</span>] verbs: [<span class="hljs-string">"list"</span>, <span class="hljs-string">"get"</span>] - apiGroups: [<span class="hljs-string">""</span>] resources: [<span class="hljs-string">"endpoints"</span>] verbs: [<span class="hljs-string">"get"</span>, <span class="hljs-string">"list"</span>, <span class="hljs-string">"watch"</span>, <span class="hljs-string">"create"</span>, <span class="hljs-string">"update"</span>, <span class="hljs-string">"patch"</span>]---kind: ClusterRoleBindingapiVersion: rbac.authorization.k8s.io/v1metadata: name: cephfs-provisionersubjects: - kind: ServiceAccount name: cephfs-provisioner namespace: cephroleRef: kind: ClusterRole name: cephfs-provisioner apiGroup: rbac.authorization.k8s.ioEOF</code></pre></div><h4 id="创建-cephfs-storageclass-安装文件"><a href="#创建-cephfs-storageclass-安装文件" class="headerlink" title="创建 cephfs storageclass 安装文件"></a>创建 cephfs storageclass 安装文件</h4><div class="hljs"><pre><code class="hljs bash"> cat > storageClass/cephfs/storageclass.yaml <<EOF---kind: StorageClassapiVersion: storage.k8s.io/v1metadata: name: cephfs-provisionerprovisioner: ceph.com/cephfsparameters: monitors: <span class="hljs-string">"10.10.34.29:6789,10.10.34.31:6789,10.10.34.42:6789"</span> adminId: admin adminSecretName: cephfs-secret-admin adminSecretNamespace: ceph claimRoot: /storageEOF</code></pre></div><h4 id="安装-cephfs-provisioner"><a href="#安装-cephfs-provisioner" class="headerlink" title="安装 cephfs provisioner"></a>安装 cephfs provisioner</h4><div class="hljs"><pre><code class="hljs bash">kubectl apply -f storageClass/cephfs/serviceaccount.yamlkubectl apply -f storageClass/cephfs/rbac.yamlkubectl apply -f storageClass/cephfs/deployment.yamlkubectl apply -f storageClass/cephfs/storageclass.yaml</code></pre></div><h4 id="查看-cephfs-provisioner"><a href="#查看-cephfs-provisioner" class="headerlink" title="查看 cephfs provisioner"></a>查看 cephfs provisioner</h4><div class="hljs"><pre><code class="hljs bash"> kubectl get storageclasses cephfs-provisionerNAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGEcephfs-provisioner ceph.com/cephfs Delete Immediate <span class="hljs-literal">false</span> 17s</code></pre></div><h4 id="创建-cephfs-PVC"><a href="#创建-cephfs-PVC" class="headerlink" title="创建 cephfs PVC"></a>创建 cephfs PVC</h4><div class="hljs"><pre><code class="hljs bash"> cat > storageClass/cephfs/pvc.yaml <<EOF---apiVersion: v1kind: PersistentVolumeClaimmetadata: name: cephfs-testspec: storageClassName: cephfs-provisioner accessModes: - ReadWriteMany resources: requests: storage: 5GiEOF kubectl apply -f storageClass/cephfs/pvc.yaml -n kube-system kubectl apply -f storageClass/cephfs/pvc.yaml -n ceph kubectl apply -f storageClass/cephfs/pvc.yaml -n default</code></pre></div><h4 id="验证-cephfs-PVC"><a href="#验证-cephfs-PVC" class="headerlink" title="验证 cephfs PVC"></a>验证 cephfs PVC</h4><div class="hljs"><pre><code class="hljs bash"> kubectl get pvc --all-namespacesNAMESPACE NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGEceph cephfs-test Bound pvc-ae5ff9f8-4032-4690-9104-eeb563d45a1e 5Gi RWX cephfs-provisioner 8sdefault cephfs-test Bound pvc-85d47294-50a0-4f6c-b9ca-7a676cf3c34e 5Gi RWX cephfs-provisioner 5skube-system cephfs-test Bound pvc-759c1b48-df22-4b72-ab22-490a2f3ebd5c 5Gi RWX cephfs-provisioner 13s</code></pre></div><h3 id="创建-rbd"><a href="#创建-rbd" class="headerlink" title="创建 rbd"></a>创建 rbd</h3><h4 id="生成-rbd-admin-密码"><a href="#生成-rbd-admin-密码" class="headerlink" title="生成 rbd admin 密码"></a>生成 rbd admin 密码</h4><div class="hljs"><pre><code class="hljs bash">ceph auth get-key client.admin > /tmp/key</code></pre></div><h4 id="创建-admin-secret"><a href="#创建-admin-secret" class="headerlink" title="创建 admin secret"></a>创建 admin secret</h4><div class="hljs"><pre><code class="hljs bash">kubectl create secret generic rbd-secret-admin --from-file=/tmp/key --namespace=ceph --<span class="hljs-built_in">type</span>=kubernetes.io/rbd</code></pre></div><h4 id="创建-pool-secret"><a href="#创建-pool-secret" class="headerlink" title="创建 pool secret"></a>创建 pool secret</h4><div class="hljs"><pre><code class="hljs bash">ceph osd pool create kube 8 8ceph auth add client.kube mon <span class="hljs-string">'allow r'</span> osd <span class="hljs-string">'allow rwx pool=kube'</span>ceph auth get-key client.kube > /tmp/keykubectl create secret generic kube-secret --from-file=/tmp/key --namespace=ceph --<span class="hljs-built_in">type</span>=kubernetes.io/rbd</code></pre></div><h4 id="创建-rbd-目录"><a href="#创建-rbd-目录" class="headerlink" title="创建 rbd 目录"></a>创建 rbd 目录</h4><div class="hljs"><pre><code class="hljs bash">mkdir -p storageClass/rbd</code></pre></div><h4 id="创建-rbd-deployment-安装文件"><a href="#创建-rbd-deployment-安装文件" class="headerlink" title="创建 rbd deployment 安装文件"></a>创建 rbd deployment 安装文件</h4><div class="hljs"><pre><code class="hljs bash"> cat > storageClass/rbd/deployment.yaml <<EOF---apiVersion: apps/v1kind: Deploymentmetadata: name: rbd-provisioner namespace: cephspec: replicas: 1 selector: matchLabels: app: rbd-provisioner strategy: <span class="hljs-built_in">type</span>: Recreate template: metadata: labels: app: rbd-provisioner spec: nodeSelector: node-role.kubernetes.io/infra: <span class="hljs-string">"true"</span> containers: - name: rbd-provisioner image: <span class="hljs-string">"quay.io/external_storage/rbd-provisioner:latest"</span> imagePullPolicy: IfNotPresent env: - name: PROVISIONER_NAME value: ceph.com/rbd serviceAccount: rbd-provisionerEOF</code></pre></div><h4 id="创建-rbd-serviceaccount-安装文件"><a href="#创建-rbd-serviceaccount-安装文件" class="headerlink" title="创建 rbd serviceaccount 安装文件"></a>创建 rbd serviceaccount 安装文件</h4><div class="hljs"><pre><code class="hljs bash"> cat > storageClass/rbd/serviceaccount.yaml <<EOF---apiVersion: v1kind: ServiceAccountmetadata: name: rbd-provisioner namespace: cephEOF</code></pre></div><h4 id="创建-rbd-rbac-安装文件"><a href="#创建-rbd-rbac-安装文件" class="headerlink" title="创建 rbd rbac 安装文件"></a>创建 rbd rbac 安装文件</h4><div class="hljs"><pre><code class="hljs bash"> cat > storageClass/rbd/rbac.yaml <<EOF---apiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata: name: rbd-provisioner namespace: cephrules:- apiGroups: [<span class="hljs-string">""</span>] resources: [<span class="hljs-string">"secrets"</span>] verbs: [<span class="hljs-string">"get"</span>]- apiGroups: [<span class="hljs-string">""</span>] resources: [<span class="hljs-string">"endpoints"</span>] verbs: [<span class="hljs-string">"get"</span>, <span class="hljs-string">"list"</span>, <span class="hljs-string">"watch"</span>, <span class="hljs-string">"create"</span>, <span class="hljs-string">"update"</span>, <span class="hljs-string">"patch"</span>]---apiVersion: rbac.authorization.k8s.io/v1kind: RoleBindingmetadata: name: rbd-provisioner namespace: cephsubjects:- kind: ServiceAccount name: rbd-provisionerroleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: rbd-provisioner---kind: ClusterRoleapiVersion: rbac.authorization.k8s.io/v1metadata: name: rbd-provisioner namespace: cephrules: - apiGroups: [<span class="hljs-string">""</span>] resources: [<span class="hljs-string">"persistentvolumes"</span>] verbs: [<span class="hljs-string">"get"</span>, <span class="hljs-string">"list"</span>, <span class="hljs-string">"watch"</span>, <span class="hljs-string">"create"</span>, <span class="hljs-string">"delete"</span>] - apiGroups: [<span class="hljs-string">""</span>] resources: [<span class="hljs-string">"persistentvolumeclaims"</span>] verbs: [<span class="hljs-string">"get"</span>, <span class="hljs-string">"list"</span>, <span class="hljs-string">"watch"</span>, <span class="hljs-string">"update"</span>] - apiGroups: [<span class="hljs-string">"storage.k8s.io"</span>] resources: [<span class="hljs-string">"storageclasses"</span>] verbs: [<span class="hljs-string">"get"</span>, <span class="hljs-string">"list"</span>, <span class="hljs-string">"watch"</span>] - apiGroups: [<span class="hljs-string">""</span>] resources: [<span class="hljs-string">"events"</span>] verbs: [<span class="hljs-string">"create"</span>, <span class="hljs-string">"update"</span>, <span class="hljs-string">"patch"</span>] - apiGroups: [<span class="hljs-string">""</span>] resources: [<span class="hljs-string">"services"</span>] resourceNames: [<span class="hljs-string">"kube-dns"</span>,<span class="hljs-string">"coredns"</span>] verbs: [<span class="hljs-string">"list"</span>, <span class="hljs-string">"get"</span>] - apiGroups: [<span class="hljs-string">""</span>] resources: [<span class="hljs-string">"endpoints"</span>] verbs: [<span class="hljs-string">"get"</span>, <span class="hljs-string">"list"</span>, <span class="hljs-string">"watch"</span>, <span class="hljs-string">"create"</span>, <span class="hljs-string">"update"</span>, <span class="hljs-string">"patch"</span>]---apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRoleBindingmetadata: name: rbd-provisionersubjects: - kind: ServiceAccount name: rbd-provisioner namespace: cephroleRef: kind: ClusterRole name: rbd-provisioner apiGroup: rbac.authorization.k8s.ioEOF</code></pre></div><h4 id="创建-rbd-storageclass-安装文件"><a href="#创建-rbd-storageclass-安装文件" class="headerlink" title="创建 rbd storageclass 安装文件"></a>创建 rbd storageclass 安装文件</h4><div class="hljs"><pre><code class="hljs bash"> cat > storageClass/rbd/storageclass.yaml <<EOF---apiVersion: storage.k8s.io/v1kind: StorageClassmetadata: name: rbd-provisionerprovisioner: ceph.com/rbdparameters: monitors: <span class="hljs-string">"10.10.34.29:6789,10.10.34.31:6789,10.10.34.42:6789"</span> pool: kube adminId: admin adminSecretNamespace: ceph adminSecretName: rbd-secret-admin userId: kube userSecretNamespace: ceph userSecretName: ceph-secret imageFormat: <span class="hljs-string">"2"</span> imageFeatures: layeringEOF</code></pre></div><h4 id="安装-rbd-provisioner"><a href="#安装-rbd-provisioner" class="headerlink" title="安装 rbd provisioner"></a>安装 rbd provisioner</h4><div class="hljs"><pre><code class="hljs bash">kubectl apply -f storageClass/rbd/serviceaccount.yamlkubectl apply -f storageClass/rbd/rbac.yamlkubectl apply -f storageClass/rbd/deployment.yamlkubectl apply -f storageClass/rbd/storageclass.yaml</code></pre></div><h4 id="查看-rbd-provisioner"><a href="#查看-rbd-provisioner" class="headerlink" title="查看 rbd provisioner"></a>查看 rbd provisioner</h4><div class="hljs"><pre><code class="hljs bash"> kubectl get storageclasses rbd-provisionerNAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGErbd-provisioner ceph.com/rbd Delete Immediate <span class="hljs-literal">false</span> 7s</code></pre></div><h4 id="创建-rbd-PVC"><a href="#创建-rbd-PVC" class="headerlink" title="创建 rbd PVC"></a>创建 rbd PVC</h4><div class="hljs"><pre><code class="hljs bash"> cat > storageClass/rbd/pvc.yaml <<EOF---apiVersion: v1kind: PersistentVolumeClaimmetadata: name: rbd-testspec: accessModes: - ReadWriteOnce storageClassName: rbd-provisioner resources: requests: storage: 10GiEOF kubectl apply -f storageClass/rbd/pvc.yaml -n kube-system kubectl apply -f storageClass/rbd/pvc.yaml -n ceph kubectl apply -f storageClass/rbd/pvc.yaml -n default</code></pre></div><h4 id="验证-rbd-PVC"><a href="#验证-rbd-PVC" class="headerlink" title="验证 rbd PVC"></a>验证 rbd PVC</h4><div class="hljs"><pre><code class="hljs bash"> kubectl get pvc --all-namespacesNAMESPACE NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGEceph rbd-test Bound pvc-4d85ad48-b24c-43b6-a7c2-89aec9c661a7 10Gi RWO rbd-provisioner 19sdefault rbd-test Bound pvc-9d93ccbd-b6ed-42fe-8f57-78fbff88baeb 10Gi RWO rbd-provisioner 11skube-system rbd-test Bound pvc-5887166f-d9b4-4d40-b419-2e37ac81901d 10Gi RWO rbd-provisioner 31s</code></pre></div><h3 id="对象存储"><a href="#对象存储" class="headerlink" title="对象存储"></a>对象存储</h3><h4 id="s3-存储"><a href="#s3-存储" class="headerlink" title="s3 存储"></a>s3 存储</h4><div class="hljs"><pre><code class="hljs bash"> 配置 ceph s3 账号,保留 access_key 和 secret_key radosgw-admin user create --uid=<span class="hljs-string">"testuser"</span> --display-name=<span class="hljs-string">"test user"</span>{ <span class="hljs-string">"user_id"</span>: <span class="hljs-string">"testuser"</span>, <span class="hljs-string">"display_name"</span>: <span class="hljs-string">"test user"</span>, <span class="hljs-string">"email"</span>: <span class="hljs-string">""</span>, <span class="hljs-string">"suspended"</span>: 0, <span class="hljs-string">"max_buckets"</span>: 1000, <span class="hljs-string">"subusers"</span>: [], <span class="hljs-string">"keys"</span>: [ { <span class="hljs-string">"user"</span>: <span class="hljs-string">"testuser"</span>, <span class="hljs-string">"access_key"</span>: <span class="hljs-string">"60DD3SXN58LIRC06ILQE"</span>, <span class="hljs-string">"secret_key"</span>: <span class="hljs-string">"zgFnP3lL3uPF3WsCeQtnESKvgPYHnW75cSJfkYeZ"</span> } ], <span class="hljs-string">"swift_keys"</span>: [], <span class="hljs-string">"caps"</span>: [], <span class="hljs-string">"op_mask"</span>: <span class="hljs-string">"read, write, delete"</span>, <span class="hljs-string">"default_placement"</span>: <span class="hljs-string">""</span>, <span class="hljs-string">"default_storage_class"</span>: <span class="hljs-string">""</span>, <span class="hljs-string">"placement_tags"</span>: [], <span class="hljs-string">"bucket_quota"</span>: { <span class="hljs-string">"enabled"</span>: <span class="hljs-literal">false</span>, <span class="hljs-string">"check_on_raw"</span>: <span class="hljs-literal">false</span>, <span class="hljs-string">"max_size"</span>: -1, <span class="hljs-string">"max_size_kb"</span>: 0, <span class="hljs-string">"max_objects"</span>: -1 }, <span class="hljs-string">"user_quota"</span>: { <span class="hljs-string">"enabled"</span>: <span class="hljs-literal">false</span>, <span class="hljs-string">"check_on_raw"</span>: <span class="hljs-literal">false</span>, <span class="hljs-string">"max_size"</span>: -1, <span class="hljs-string">"max_size_kb"</span>: 0, <span class="hljs-string">"max_objects"</span>: -1 }, <span class="hljs-string">"temp_url_keys"</span>: [], <span class="hljs-string">"type"</span>: <span class="hljs-string">"rgw"</span>, <span class="hljs-string">"mfa_ids"</span>: []} 安装 s3 python 库 pip install boto 创建 s3 测试文件,使用上面创建的账号。 cat > s3.py <<EOFfrom boto.s3.connection import S3Connectionfrom boto.s3.connection import OrdinaryCallingFormat add radosgw user <span class="hljs-keyword">in</span> ceph cluster radosgw-admin user create --uid=rgwuser --subuser=rgwuser:swift --display-name=s3 --access=fullconn = S3Connection(aws_access_key_id=<span class="hljs-string">'60DD3SXN58LIRC06ILQE'</span>, aws_secret_access_key=<span class="hljs-string">'zgFnP3lL3uPF3WsCeQtnESKvgPYHnW75cSJfkYeZ'</span>, host=<span class="hljs-string">'10.10.34.29'</span>, port=8080, is_secure=False, calling_format=OrdinaryCallingFormat())create_bucket = conn.create_bucket(<span class="hljs-string">'cephfs'</span>)buckets_list = conn.get_all_buckets()<span class="hljs-keyword">for</span> bucket <span class="hljs-keyword">in</span> buckets_list: <span class="hljs-built_in">print</span>(<span class="hljs-string">'%s\t%s'</span> % (bucket.name, bucket.creation_date))delete_bucket = conn.delete_bucket(<span class="hljs-string">'cephfs'</span>)EOF 测试验证 python s3.pycephfs 2020-04-10T08:16:15.647Z</code></pre></div><h4 id="swift"><a href="#swift" class="headerlink" title="swift"></a>swift</h4><div class="hljs"><pre><code class="hljs bash"> 创建账号 radosgw-admin subuser create --uid=testuser --subuser=testuser:swift --access=full{ <span class="hljs-string">"user_id"</span>: <span class="hljs-string">"testuser"</span>, <span class="hljs-string">"display_name"</span>: <span class="hljs-string">"test user"</span>, <span class="hljs-string">"email"</span>: <span class="hljs-string">""</span>, <span class="hljs-string">"suspended"</span>: 0, <span class="hljs-string">"max_buckets"</span>: 1000, <span class="hljs-string">"subusers"</span>: [ { <span class="hljs-string">"id"</span>: <span class="hljs-string">"testuser:swift"</span>, <span class="hljs-string">"permissions"</span>: <span class="hljs-string">"full-control"</span> } ], <span class="hljs-string">"keys"</span>: [ { <span class="hljs-string">"user"</span>: <span class="hljs-string">"testuser"</span>, <span class="hljs-string">"access_key"</span>: <span class="hljs-string">"60DD3SXN58LIRC06ILQE"</span>, <span class="hljs-string">"secret_key"</span>: <span class="hljs-string">"zgFnP3lL3uPF3WsCeQtnESKvgPYHnW75cSJfkYeZ"</span> } ], <span class="hljs-string">"swift_keys"</span>: [ { <span class="hljs-string">"user"</span>: <span class="hljs-string">"testuser:swift"</span>, <span class="hljs-string">"secret_key"</span>: <span class="hljs-string">"b59Aw59eiStnhibukAm7fN3vQRtTU2BwNio2dHnl"</span> } ], <span class="hljs-string">"caps"</span>: [], <span class="hljs-string">"op_mask"</span>: <span class="hljs-string">"read, write, delete"</span>, <span class="hljs-string">"default_placement"</span>: <span class="hljs-string">""</span>, <span class="hljs-string">"default_storage_class"</span>: <span class="hljs-string">""</span>, <span class="hljs-string">"placement_tags"</span>: [], <span class="hljs-string">"bucket_quota"</span>: { <span class="hljs-string">"enabled"</span>: <span class="hljs-literal">false</span>, <span class="hljs-string">"check_on_raw"</span>: <span class="hljs-literal">false</span>, <span class="hljs-string">"max_size"</span>: -1, <span class="hljs-string">"max_size_kb"</span>: 0, <span class="hljs-string">"max_objects"</span>: -1 }, <span class="hljs-string">"user_quota"</span>: { <span class="hljs-string">"enabled"</span>: <span class="hljs-literal">false</span>, <span class="hljs-string">"check_on_raw"</span>: <span class="hljs-literal">false</span>, <span class="hljs-string">"max_size"</span>: -1, <span class="hljs-string">"max_size_kb"</span>: 0, <span class="hljs-string">"max_objects"</span>: -1 }, <span class="hljs-string">"temp_url_keys"</span>: [], <span class="hljs-string">"type"</span>: <span class="hljs-string">"rgw"</span>, <span class="hljs-string">"mfa_ids"</span>: []} 重置密码 radosgw-admin key create --subuser=testuser:swift --key-type=swift --gen-secret 安装 swift 客户端 pip install python-swiftclient 测试 swift -A http://10.10.34.29:8080/auth/1.0 -U testuser:swift -K <span class="hljs-string">'b59Aw59eiStnhibukAm7fN3vQRtTU2BwNio2dHnl'</span> list</code></pre></div><h2 id="安装-istio"><a href="#安装-istio" class="headerlink" title="安装 istio"></a><strong>安装 istio</strong></h2><h3 id="使用-demo-安装"><a href="#使用-demo-安装" class="headerlink" title="使用 demo 安装"></a>使用 demo 安装</h3><ul><li>参考 <code>https://istio.io/docs/setup/additional-setup/config-profiles/</code></li></ul><div class="hljs"><pre><code class="hljs bash">istioctl manifest apply --<span class="hljs-built_in">set</span> profile=demo --<span class="hljs-built_in">set</span> values.gateways.istio-ingressgateway.type=NodePort</code></pre></div><h3 id="自定义参数安装"><a href="#自定义参数安装" class="headerlink" title="自定义参数安装"></a>自定义参数安装</h3><ul><li>参数 <code>https://istio.io/zh/docs/reference/config/installation-options/</code></li></ul><div class="hljs"><pre><code class="hljs bash">istioctl manifest apply \ --namespace istio-system \ --<span class="hljs-built_in">set</span> values.global.hub=docker.io/istio \ --<span class="hljs-built_in">set</span> values.global.tag=1.5.0 \ --<span class="hljs-built_in">set</span> values.global.logging.level=<span class="hljs-string">"default:info"</span> \ --<span class="hljs-built_in">set</span> values.global.imagePullPolicy=IfNotPresent \ --<span class="hljs-built_in">set</span> values.global.k8sIngress.enabled=<span class="hljs-literal">false</span> \ --<span class="hljs-built_in">set</span> values.global.k8sIngress.enableHttps=<span class="hljs-literal">false</span> \ --<span class="hljs-built_in">set</span> values.global.proxy.image=proxyv2 \ --<span class="hljs-built_in">set</span> values.global.proxy.clusterDomain=<span class="hljs-string">"cluster.local"</span> \ --<span class="hljs-built_in">set</span> values.global.proxy.privileged=<span class="hljs-literal">true</span> \ --<span class="hljs-built_in">set</span> values.global.proxy.enableCoreDump=<span class="hljs-literal">true</span> \ --<span class="hljs-built_in">set</span> values.global.proxy.autoInject=enabled \ --<span class="hljs-built_in">set</span> values.global.proxy.tracer=zipkin \ --<span class="hljs-built_in">set</span> values.global.controlPlaneSecurityEnabled=<span class="hljs-literal">true</span> \ --<span class="hljs-built_in">set</span> values.global.mtls.enabled=<span class="hljs-literal">false</span> \ --<span class="hljs-built_in">set</span> values.global.outboundTrafficPolicy.mode=ALLOW_ANY \ --<span class="hljs-built_in">set</span> values.global.sds.enabled=<span class="hljs-literal">false</span> \ --<span class="hljs-built_in">set</span> values.certmanager.enabled=<span class="hljs-literal">false</span> \ --<span class="hljs-built_in">set</span> values.galley.enabled=<span class="hljs-literal">true</span> \ --<span class="hljs-built_in">set</span> values.gateways.enabled=<span class="hljs-literal">true</span> \ --<span class="hljs-built_in">set</span> values.gateways.istio-ingressgateway.enabled=<span class="hljs-literal">true</span> \ --<span class="hljs-built_in">set</span> values.gateways.istio-ingressgateway.type=NodePort NodePort, ClusterIP or LoadBalancer \ --<span class="hljs-built_in">set</span> values.gateways.istio-egressgateway.enabled=<span class="hljs-literal">true</span> \ --<span class="hljs-built_in">set</span> values.grafana.enabled=<span class="hljs-literal">true</span> \ --<span class="hljs-built_in">set</span> values.istio_cni.enabled=<span class="hljs-literal">false</span> \ --<span class="hljs-built_in">set</span> values.istiocoredns.enabled=<span class="hljs-literal">false</span> \ --<span class="hljs-built_in">set</span> values.kiali.enabled=<span class="hljs-literal">true</span> \ --<span class="hljs-built_in">set</span> values.kiali.createDemoSecret=<span class="hljs-literal">true</span> \ --<span class="hljs-built_in">set</span> values.kiali.hub=docker.io/kiali \ --<span class="hljs-built_in">set</span> values.mixer.policy.enabled=<span class="hljs-literal">false</span> \ --<span class="hljs-built_in">set</span> values.mixer.policy.autoscaleEnabled=<span class="hljs-literal">true</span> \ --<span class="hljs-built_in">set</span> values.nodeagent.enabled=<span class="hljs-literal">false</span> \ --<span class="hljs-built_in">set</span> values.pilot.enabled=<span class="hljs-literal">true</span> \ --<span class="hljs-built_in">set</span> values.pilot.sidecar=<span class="hljs-literal">true</span> \ --<span class="hljs-built_in">set</span> values.prometheus.enabled=<span class="hljs-literal">true</span> \ --<span class="hljs-built_in">set</span> values.security.enabled=<span class="hljs-literal">true</span> \ --<span class="hljs-built_in">set</span> values.sidecarInjectorWebhook.enabled=<span class="hljs-literal">true</span> \ --<span class="hljs-built_in">set</span> values.sidecarInjectorWebhook.enableNamespacesByDefault=<span class="hljs-literal">true</span> \ --<span class="hljs-built_in">set</span> values.tracing.enabled=<span class="hljs-literal">true</span> \ --<span class="hljs-built_in">set</span> values.tracing.jaeger.hub=docker.io/jaegertracing \ --<span class="hljs-built_in">set</span> values.tracing.jaeger.tag=1.14</code></pre></div><h3 id="查看-ingress-端口监听情况"><a href="#查看-ingress-端口监听情况" class="headerlink" title="查看 ingress 端口监听情况"></a>查看 ingress 端口监听情况</h3><div class="hljs"><pre><code class="hljs bash"> kubectl get svc -n istio-system | grep ingressistio-ingressgateway NodePort 10.105.16.129 <none> 15020:30796/TCP,80:32179/TCP,443:31133/TCP,15029:31338/TCP,15030:30980/TCP,15031:31675/TCP,15032:32296/TCP,31400:31821/TCP,15443:32679/TCP 30s ansible ins -m shell -a <span class="hljs-string">'netstat -lntp | grep -E "32179|31133"'</span>10.10.34.92 | CHANGED | rc=0 >>tcp6 0 0 :::32179 :::* LISTEN 21780/kube-proxytcp6 0 0 :::31133 :::* LISTEN 21780/kube-proxy10.10.34.94 | CHANGED | rc=0 >>tcp6 0 0 :::32179 :::* LISTEN 3093/kube-proxytcp6 0 0 :::31133 :::* LISTEN 3093/kube-proxy10.10.34.100 | CHANGED | rc=0 >>tcp6 0 0 :::32179 :::* LISTEN 14998/kube-proxytcp6 0 0 :::31133 :::* LISTEN 14998/kube-proxy10.10.34.99 | CHANGED | rc=0 >>tcp6 0 0 :::31133 :::* LISTEN 3031/kube-proxytcp6 0 0 :::32179 :::* LISTEN 3031/kube-proxy10.10.34.95 | CHANGED | rc=0 >>tcp6 0 0 :::32179 :::* LISTEN 23912/kube-proxytcp6 0 0 :::31133 :::* LISTEN 23912/kube-proxy10.10.34.96 | CHANGED | rc=0 >>tcp6 0 0 :::32179 :::* LISTEN 26951/kube-proxytcp6 0 0 :::31133 :::* LISTEN 26951/kube-proxy10.10.34.93 | CHANGED | rc=0 >>tcp6 0 0 :::31133 :::* LISTEN 31622/kube-proxytcp6 0 0 :::32179 :::* LISTEN 31622/kube-proxy10.10.34.97 | CHANGED | rc=0 >>tcp6 0 0 :::32179 :::* LISTEN 4244/kube-proxytcp6 0 0 :::31133 :::* LISTEN 4244/kube-proxy10.10.34.98 | CHANGED | rc=0 >>tcp6 0 0 :::31133 :::* LISTEN 8077/kube-proxytcp6 0 0 :::32179 :::* LISTEN 8077/kube-proxy</code></pre></div><h3 id="配置-load-blance"><a href="#配置-load-blance" class="headerlink" title="配置 load blance"></a>配置 load blance</h3><ul><li>LB 80 端口的 TCP 协议请求转发到集群 node 节点的 32179 端口</li><li>LB 443 端口的 TCP 协议请求转发到集群 node 节点的 31133 端口</li></ul><h3 id="应用测试"><a href="#应用测试" class="headerlink" title="应用测试"></a>应用测试</h3><p>创建 namespace 并打开自动注入</p><div class="hljs"><pre><code class="hljs bash">kubectl create namespace bookkubectl label namespace book istio-injection=enabled</code></pre></div><h3 id="创建-demo"><a href="#创建-demo" class="headerlink" title="创建 demo"></a>创建 demo</h3><div class="hljs"><pre><code class="hljs bash">kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml -n bookkubectl get services -n bookkubectl get pods -n bookkubectl <span class="hljs-built_in">exec</span> -n book \ -it $(kubectl get pod -n book -l app=ratings -o jsonpath=<span class="hljs-string">'{.items[0].metadata.name}'</span>) -c ratings \ -- curl productpage:9080/productpage | grep -o <span class="hljs-string">"<title>.*</title>"</span></code></pre></div><h3 id="创建-http-gateway"><a href="#创建-http-gateway" class="headerlink" title="创建 http gateway"></a>创建 http gateway</h3><div class="hljs"><pre><code class="hljs bash"> kubectl apply -f - <<EOF--apiVersion: networking.istio.io/v1alpha3kind: VirtualServicemetadata: name: book namespace: bookspec: hosts: - <span class="hljs-string">"book.mokr.cn"</span> gateways: - istio-system/ingressgateway http: - match: - uri: exact: /productpage - uri: prefix: /static - uri: exact: /login - uri: exact: /<span class="hljs-built_in">logout</span> - uri: prefix: /api/v1/products route: - destination: host: productpage port: number: 9080EOF</code></pre></div><h3 id="验证-http-gateway"><a href="#验证-http-gateway" class="headerlink" title="验证 http gateway"></a>验证 http gateway</h3><div class="hljs"><pre><code class="hljs bash">curl -ik http://book.mokr.cn --resolve <span class="hljs-string">'book.mokr.cn:443:10.10.34.89'</span> -v</code></pre></div><h3 id="创建-https-gateway"><a href="#创建-https-gateway" class="headerlink" title="创建 https gateway"></a>创建 https gateway</h3><div class="hljs"><pre><code class="hljs bash"> 创建根证书 openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout mokr.cn.key -out mokr.cn.crt -subj <span class="hljs-string">"/C=CN/ST=Guangdong/L=Shenzhen/O=mokr/OU=ops/CN=mokr.cn"</span> 创建域名 key openssl req -out book.mokr.cn.csr -newkey rsa:2048 -nodes -keyout book.mokr.cn.key -subj <span class="hljs-string">"/C=CN/ST=Guangdong/L=Shenzhen/O=mokr/OU=ops/CN=book.mokr.cn"</span> 创建域名证书 openssl x509 -req -days 365 -CA mokr.cn.crt -CAkey mokr.cn.key -set_serial 0 -<span class="hljs-keyword">in</span> book.mokr.cn.csr -out book.mokr.cn.crt 创建 tls secret kubectl create secret tls ingressgateway-book-certs --key book.mokr.cn.key --cert book.mokr.cn.crt -n istio-system 创建 https gateway kubectl apply -f - <<EOF--apiVersion: networking.istio.io/v1alpha3kind: Gatewaymetadata: name: book-gateway namespace: istio-systemspec: selector: istio: ingressgateway servers: - port: number: 80 name: http protocol: HTTP hosts: - book.mokr.cn tls: httpsRedirect: <span class="hljs-literal">true</span> - port: number: 443 name: https protocol: HTTPS HTTP | HTTPS | GRPC | HTTP2 | MONGO | TCP | TLS tls: mode: SIMPLE PASSTHROUGH | SIMPLE | MUTUAL | AUTO_PASSTHROUGH | ISTIO_MUTUAL credentialName: ingressgateway-book-certs hosts: - book.mokr.cnEOF 创建 https VirtualService kubectl apply -f - <<EOF--apiVersion: networking.istio.io/v1alpha3kind: VirtualServicemetadata: name: book namespace: bookspec: hosts: - <span class="hljs-string">"book.mokr.cn"</span> gateways: - istio-system/book-gateway http: - match: - uri: exact: /productpage - uri: prefix: /static - uri: exact: /login - uri: exact: /<span class="hljs-built_in">logout</span> - uri: prefix: /api/v1/products route: - destination: host: productpage port: number: 9080EOF</code></pre></div><h3 id="查看安装情况"><a href="#查看安装情况" class="headerlink" title="查看安装情况"></a>查看安装情况</h3><div class="hljs"><pre><code class="hljs bash"> kubectl get gateway -n bookNAME AGEbookinfo-gateway 23s kubectl get virtualservice -n bookNAME GATEWAYS HOSTS AGEbookinfo [bookinfo-gateway] [book.mokr.cn] 36s</code></pre></div><h3 id="验证-https-gateway"><a href="#验证-https-gateway" class="headerlink" title="验证 https gateway"></a>验证 https gateway</h3><div class="hljs"><pre><code class="hljs bash">curl -ik https://book.mokr.cn --resolve <span class="hljs-string">'book.mokr.cn:443:10.10.34.89'</span> -v</code></pre></div><h3 id="删除-demo"><a href="#删除-demo" class="headerlink" title="删除 demo"></a>删除 demo</h3><div class="hljs"><pre><code class="hljs bash">kubectl delete -f samples/bookinfo/platform/kube/bookinfo.yaml -n bookkubectl delete namespace book --force --grace-period=0</code></pre></div><h3 id="删除-istioctl-集群"><a href="#删除-istioctl-集群" class="headerlink" title="删除 istioctl 集群"></a>删除 istioctl 集群</h3><div class="hljs"><pre><code class="hljs bash">istioctl manifest generate | kubectl delete -f -</code></pre></div><h2 id="部署-registry"><a href="#部署-registry" class="headerlink" title="部署 registry"></a>部署 registry</h2><h3 id="创建-pvc-部署文件"><a href="#创建-pvc-部署文件" class="headerlink" title="创建 pvc 部署文件"></a>创建 pvc 部署文件</h3><div class="hljs"><pre><code class="hljs bash"> cat > registry/pvc.yaml <<EOF---apiVersion: v1kind: PersistentVolumeClaimmetadata: name: registry-storagespec: storageClassName: cephfs-provisioner accessModes: - ReadWriteMany resources: requests: storage: 10GiEOF</code></pre></div><h3 id="创建-service-部署文件"><a href="#创建-service-部署文件" class="headerlink" title="创建 service 部署文件"></a>创建 service 部署文件</h3><div class="hljs"><pre><code class="hljs bash"> cat > registry/service.yaml <<EOF---apiVersion: v1kind: Servicemetadata: name: docker-registry namespace: defaultspec: ports: - port: 5000 protocol: TCP targetPort: 5000 selector: k8s-app: docker-registry sessionAffinity: None <span class="hljs-built_in">type</span>: ClusterIPEOF</code></pre></div><h3 id="创建-registry-部署文件"><a href="#创建-registry-部署文件" class="headerlink" title="创建 registry 部署文件"></a>创建 registry 部署文件</h3><div class="hljs"><pre><code class="hljs bash"> cat > registry/deployment.yaml <<EOF---apiVersion: apps/v1kind: Deploymentmetadata: labels: k8s-app: docker-registry name: docker-registry namespace: defaultspec: replicas: 1 selector: matchLabels: k8s-app: docker-registry template: metadata: labels: k8s-app: docker-registry spec: nodeSelector: node-role.kubernetes.io/infra: <span class="hljs-string">"true"</span> containers: - env: - name: REGISTRY_HTTP_ADDR value: :5000 - name: REGISTRY_HTTP_TLS_CERTIFICATE value: /etc/secrets/tls.crt - name: REGISTRY_HTTP_TLS_KEY value: /etc/secrets/tls.key - name: REGISTRY_STORAGE_DELETE_ENABLED value: <span class="hljs-string">"true"</span> name: registry image: registry:2 imagePullPolicy: IfNotPresent ports: - containerPort: 5000 protocol: TCP resources: requests: cpu: 100m memory: 256Mi volumeMounts: - name: registry-data mountPath: /var/lib/registry - name: registry-certificates mountPath: /etc/secrets serviceAccountName: docker-registry volumes: - name: registry-data persistentVolumeClaim: claimName: registry-storage - name: registry-certificates secret: defaultMode: 420 secretName: registry-certificatesEOF</code></pre></div><h3 id="创建-service"><a href="#创建-service" class="headerlink" title="创建 service"></a>创建 service</h3><div class="hljs"><pre><code class="hljs bash">kubectl apply -f registry/service.yamlkubectl get svc docker-registry -o custom-columns=clusterIP:.spec.clusterIP</code></pre></div><h3 id="配置多域名"><a href="#配置多域名" class="headerlink" title="配置多域名"></a>配置多域名</h3><div class="hljs"><pre><code class="hljs bash"> mkdir -p registry/ cp /etc/ssl/openssl.cnf registry/ vim registry/openssl.cnf[ v3_req ]basicConstraints = CA:FALSEkeyUsage = nonRepudiation, digitalSignature, keyEnciphermentsubjectAltName = @alt_names[alt_names]DNS.1 = docker-registry.mokr.cnDNS.2 = docker-registry.default.svcDNS.3 = docker-registry.default.svc.cluster.local</code></pre></div><h3 id="签发-registry-密钥"><a href="#签发-registry-密钥" class="headerlink" title="签发 registry 密钥"></a>签发 registry 密钥</h3><div class="hljs"><pre><code class="hljs bash">openssl req -nodes -newkey rsa:2048 \ -keyout registry/docker-registry.key \ -out registry/docker-registry.csr \ -subj <span class="hljs-string">"/C=CN/ST=GuangDong/L=ShenZhen/O=Mokr LTD/OU=OPS"</span> \ -config registry/openssl.cnf \ -extensions v3_req</code></pre></div><h3 id="签发-registry-证书"><a href="#签发-registry-证书" class="headerlink" title="签发 registry 证书"></a>签发 registry 证书</h3><div class="hljs"><pre><code class="hljs bash">私钥签发openssl x509 -req -sha256 -days 365 \ -<span class="hljs-keyword">in</span> registry/docker-registry.csr \ -signkey registry/docker-registry.key \ -out registry/docker-registry.crt \ -extfile registry/openssl.cnf \ -extensions v3_reqca 签发openssl x509 -req -days 365 \ -<span class="hljs-keyword">in</span> registry/docker-registry.csr \ -CA /etc/kubernetes/pki/ca.crt \ -CAkey /etc/kubernetes/pki/ca.key \ -CAcreateserial \ -out registry/docker-registry.crt \ -extfile registry/openssl.cnf \ -extensions v3_req</code></pre></div><h3 id="验证-registry-证书"><a href="#验证-registry-证书" class="headerlink" title="验证 registry 证书"></a>验证 registry 证书</h3><div class="hljs"><pre><code class="hljs bash"> openssl x509 -text -noout -<span class="hljs-keyword">in</span> registry/docker-registry.crtCertificate:... X509v3 extensions: X509v3 Basic Constraints: CA:FALSE X509v3 Key Usage: Digital Signature, Non Repudiation, Key Encipherment X509v3 Subject Alternative Name: DNS:docker-registry.mokr.cn, DNS:docker-registry.default.svc, DNS:docker-registry.default.svc.cluster.local...</code></pre></div><h3 id="为-registry-配置-secret-证书"><a href="#为-registry-配置-secret-证书" class="headerlink" title="为 registry 配置 secret 证书"></a>为 registry 配置 secret 证书</h3><div class="hljs"><pre><code class="hljs bash">kubectl create secret tls registry-certificates --cert=registry/docker-registry.crt --key=registry/docker-registry.key</code></pre></div><h3 id="创建-registry"><a href="#创建-registry" class="headerlink" title="创建 registry"></a>创建 registry</h3><div class="hljs"><pre><code class="hljs bash">kubectl apply -f registry/pvc.yamlkubectl apply -f registry/deployment.yaml</code></pre></div><h3 id="创建-registry-route"><a href="#创建-registry-route" class="headerlink" title="创建 registry route"></a>创建 registry route</h3><div class="hljs"><pre><code class="hljs bash"> cat > registry/docker-registry-route.yaml <<EOFkind: RouteapiVersion: route.openshift.io/v1metadata: name: docker-registry labels: app: docker-registryspec: host: docker-registry.mokr.cn tls: insecureEdgeTerminationPolicy: Redirect <span class="hljs-comment">## None Allow Redirect</span> termination: passthrough <span class="hljs-comment">## edge passthrough reencrypt</span> to: kind: Service name: docker-registry weight: 100 wildcardPolicy: NoneEOF kubectl apply -f registry/docker-registry-route.yaml</code></pre></div><h3 id="配置-docker"><a href="#配置-docker" class="headerlink" title="配置 docker"></a>配置 docker</h3><div class="hljs"><pre><code class="hljs bash"> vim /etc/docker/daemon.json{... <span class="hljs-string">"registry-mirrors"</span>: [ <span class="hljs-string">"https://docker-registry.mokr.cn"</span>, <span class="hljs-string">"https://docker-registry.default.svc:5000"</span>, <span class="hljs-string">"https://docker-registry.default.svc.cluster.local:5000"</span> ], <span class="hljs-string">"insecure-registries"</span>: [ <span class="hljs-string">"docker-registry.mokr.cn"</span>, <span class="hljs-string">"docker-registry.default.svc:5000"</span>, <span class="hljs-string">"docker-registry.default.svc.cluster.local:5000"</span> ],...} systemctl restart docker docker info... Insecure Registries: docker-registry.default.svc.cluster.local:5000 docker-registry.default.svc:5000 docker-registry.mokr.cn 127.0.0.0/8 Registry Mirrors: https://docker-registry.mokr.cn/ https://docker-registry.default.svc:5000/ https://docker-registry.default.svc.cluster.local:5000/ Live Restore Enabled: <span class="hljs-literal">false</span></code></pre></div><h3 id="下载-image-测试"><a href="#下载-image-测试" class="headerlink" title="下载 image 测试"></a>下载 image 测试</h3><div class="hljs"><pre><code class="hljs bash">docker pull busybox:latestdocker tag busybox:latest docker-registry.default.svc:5000/busybox:latestdocker tag busybox:latest docker-registry.default.svc.cluster.local:5000/busybox:latestdocker tag busybox:latest docker-registry.mokr.cn/default/busybox:latestdocker push docker-registry.default.svc:5000/busybox:latestdocker push docker-registry.default.svc.cluster.local:5000/busybox:latestdocker push docker-registry.mokr.cn/default/busybox:latest</code></pre></div><h3 id="验证-registry"><a href="#验证-registry" class="headerlink" title="验证 registry"></a>验证 registry</h3><div class="hljs"><pre><code class="hljs bash"> curl -ik https://docker-registry.mokr.cn/v2/_catalog --resolve <span class="hljs-string">'docker-registry.mokr.cn:443:10.10.34.89'</span> -v* Added docker-registry.mokr.cn:443:10.10.34.89 to DNS cache* Hostname docker-registry.mokr.cn was found <span class="hljs-keyword">in</span> DNS cache* Trying 10.10.34.89...* TCP_NODELAY <span class="hljs-built_in">set</span>* Connected to docker-registry.mokr.cn (10.10.34.89) port 443 (0)* ALPN, offering h2* ALPN, offering http/1.1* successfully <span class="hljs-built_in">set</span> certificate verify locations:* CAfile: /etc/ssl/certs/ca-certificates.crt CApath: /etc/ssl/certs* TLSv1.3 (OUT), TLS handshake, Client hello (1):* TLSv1.3 (IN), TLS handshake, Server hello (2):* TLSv1.2 (IN), TLS handshake, Certificate (11):* TLSv1.2 (IN), TLS handshake, Server key exchange (12):* TLSv1.2 (IN), TLS handshake, Server finished (14):* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):* TLSv1.2 (OUT), TLS change cipher, Client hello (1):* TLSv1.2 (OUT), TLS handshake, Finished (20):* TLSv1.2 (IN), TLS handshake, Finished (20):* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256* ALPN, server accepted to use h2* Server certificate:* subject: C=CN; ST=GuangDong; L=ShenZhen; O=Mokr LTD; OU=OPS* start date: Apr 11 15:05:20 2020 GMT* expire date: Apr 11 15:05:20 2021 GMT* issuer: CN=kubernetes* SSL certificate verify result: unable to get <span class="hljs-built_in">local</span> issuer certificate (20), continuing anyway.* Using HTTP2, server supports multi-use* Connection state changed (HTTP/2 confirmed)* Copying HTTP/2 data <span class="hljs-keyword">in</span> stream buffer to connection buffer after upgrade: len=0* Using Stream ID: 1 (easy handle 0x556a671559a0)> GET /v2/_catalog HTTP/2> Host: docker-registry.mokr.cn> User-Agent: curl/7.58.0> Accept: */*>* Connection state changed (MAX_CONCURRENT_STREAMS updated)!< HTTP/2 200HTTP/2 200< content-type: application/json; charset=utf-8content-type: application/json; charset=utf-8< docker-distribution-api-version: registry/2.0docker-distribution-api-version: registry/2.0< x-content-type-options: nosniffx-content-type-options: nosniff< content-length: 47content-length: 47< date: Sat, 11 Apr 2020 15:57:22 GMTdate: Sat, 11 Apr 2020 15:57:22 GMT<{<span class="hljs-string">"repositories"</span>:[<span class="hljs-string">"busybox"</span>,<span class="hljs-string">"default/busybox"</span>]}* Connection 0 to host docker-registry.mokr.cn left intact</code></pre></div><h3 id="清理-registry-images"><a href="#清理-registry-images" class="headerlink" title="清理 registry images"></a>清理 registry images</h3><div class="hljs"><pre><code class="hljs bash"> 登录 docker-registry kubectl <span class="hljs-built_in">exec</span> -it $(kubectl get pods -l k8s-app=docker-registry -o jsonpath={.items[0].metadata.name}) sh/ registry garbage-collect /etc/docker/registry/config.yml --delete-untagged=<span class="hljs-literal">true</span></code></pre></div><h2 id="安装部署-cert-manager"><a href="#安装部署-cert-manager" class="headerlink" title="安装部署 cert-manager"></a>安装部署 cert-manager</h2><h3 id="下载安装脚本"><a href="#下载安装脚本" class="headerlink" title="下载安装脚本"></a>下载安装脚本</h3><div class="hljs"><pre><code class="hljs bash">mkdir -p cert-managercurl -L https://github.com/jetstack/cert-manager/releases/download/v0.14.1/cert-manager.yaml -o cert-manager/cert-manager.yamlsed -i <span class="hljs-string">'s/namespace: "cert-manager"/namespace: kube-system/g'</span> cert-manager/cert-manager.yamlsed -i <span class="hljs-string">'s/namespace: cert-manager/namespace: kube-system/g'</span> cert-manager/cert-manager.yamlsed -i <span class="hljs-string">'s/secret: "cert-manager/secret: kube-system/g'</span> cert-manager/cert-manager.yamlsed -i <span class="hljs-string">'s/secret: cert-manager/secret: kube-system/g'</span> cert-manager/cert-manager.yaml注释 5878 - 5882 行,关闭sed -i <span class="hljs-string">'5878,5882s/^//g'</span> cert-manager/cert-manager.yaml</code></pre></div><h3 id="执行安装"><a href="#执行安装" class="headerlink" title="执行安装"></a>执行安装</h3><div class="hljs"><pre><code class="hljs bash">kubectl apply -f cert-manager/cert-manager.yaml</code></pre></div><h3 id="检查安装"><a href="#检查安装" class="headerlink" title="检查安装"></a>检查安装</h3><div class="hljs"><pre><code class="hljs bash"> kubectl get pods -n kube-system | grep cert-managercert-manager-557f8d6944-zr5jh 1/1 Running 0 2m1scert-manager-cainjector-6999bdd97-nfsrn 1/1 Running 0 2m1scert-manager-webhook-c579c9f47-g6rd5 1/1 Running 0 2m1s kubectl get crd -o custom-columns=crd-name:.metadata.name|grep cert-managercertificaterequests.cert-manager.iocertificates.cert-manager.iochallenges.acme.cert-manager.ioclusterissuers.cert-manager.ioissuers.cert-manager.ioorders.acme.cert-manager.io</code></pre></div>]]></content>
</entry>
<entry>
<title>ansible 部署zk、kafka、nacos集群</title>
<link href="/post/c21ba7a3.html"/>
<url>/post/c21ba7a3.html</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>通过自动化脚本批量化部署中间件服务器是非常有用的。可以减少大量的重复性工作。</p><h2 id="步骤"><a href="#步骤" class="headerlink" title="步骤"></a>步骤</h2><ol><li><p>克隆下<a href="https://github.com/Bryce-huang/tools" target="_blank" rel="noopener">tools</a>:<code>git clone https://github.com/Bryce-huang/tools.git</code>,有intall-nacos和install-kafka-cluster</p></li><li><p>下载相关资源到/root/,按照自己的需求修改<code>XX/vars/XXx.yml</code>系统变量</p></li><li>配置ansible相关分组</li></ol><h2 id="安装zookeeper集群"><a href="#安装zookeeper集群" class="headerlink" title="安装zookeeper集群"></a>安装zookeeper集群</h2><p>进入到install-kafka-cluster,在ansible的hosts文件添加<br><div class="hljs"><pre><code class="hljs shell">[zookeeper]192.168.0.1192.168.0.2192.168.0.3</code></pre></div><br>下面介绍下变量文件的意思:</p><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-comment"># zookeeper version</span><span class="hljs-attr">zookeeper_version:</span> <span class="hljs-number">3.4</span><span class="hljs-number">.10</span><span class="hljs-comment"># zookeeper user</span><span class="hljs-attr">user:</span> <span class="hljs-string">"zookeeper"</span><span class="hljs-attr">group:</span> <span class="hljs-string">"zookeeper"</span><span class="hljs-comment"># zookeeper data path</span><span class="hljs-attr">data_dir:</span> <span class="hljs-string">zookeeper_storage</span><span class="hljs-attr">zookeeper_log_path:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{install_dir}}</span>/zookeeper_log"</span><span class="hljs-comment"># zookeeper port</span><span class="hljs-attr">leader_port:</span> <span class="hljs-number">2888</span><span class="hljs-attr">vote_port:</span> <span class="hljs-number">3888</span><span class="hljs-attr">client_port:</span> <span class="hljs-number">2181</span><span class="hljs-attr">jmx_port:</span> <span class="hljs-number">11911</span><span class="hljs-attr">random_port:</span> <span class="hljs-string">"30001-65006"</span><span class="hljs-attr">firewall_ports:</span> <span class="hljs-bullet">-</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ leader_port }}</span>"</span> <span class="hljs-bullet">-</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ vote_port }}</span>"</span> <span class="hljs-bullet">-</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ client_port }}</span>"</span> <span class="hljs-bullet">-</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ jmx_port }}</span>"</span> <span class="hljs-bullet">-</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ random_port }}</span>"</span><span class="hljs-comment"># env path</span><span class="hljs-attr">install_dir:</span> <span class="hljs-string">"/data/<span class="hljs-template-variable">{{ user }}</span>"</span><span class="hljs-attr">download_path:</span> <span class="hljs-string">"/root/"</span> <span class="hljs-comment"># your local download path</span><span class="hljs-attr">tmp_path:</span> <span class="hljs-string">"/tmp"</span><span class="hljs-comment"># host group</span><span class="hljs-attr">zk_hosts:</span> <span class="hljs-string">zookeeper</span></code></pre></div><p>需要注意的是install_dir,是文件的安装目录,安装脚本主要做了以下事情:</p><ol><li>检测是否安装jdk</li><li>自定义配置文件,如需修改zk的安装配置文件,请修改<code>install-kafka-cluster/zk/roles/zookeeper/templates</code>下的配置文件</li><li>把zookeeper注册成系统服务,开机自启</li></ol><p>最后运行<code>ansible-playbook zk/zk.yml</code></p><h2 id="安装Kafka集群"><a href="#安装Kafka集群" class="headerlink" title="安装Kafka集群"></a>安装Kafka集群</h2><p>进入到install-kafka-cluster,在ansible的hosts文件添加<br><div class="hljs"><pre><code class="hljs shell">[kafka]192.168.0.1192.168.0.2192.168.0.3</code></pre></div></p><p>下面介绍下变量文件的意思:<br><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-comment"># kafka version</span><span class="hljs-attr">kafka_version:</span> <span class="hljs-number">2.12</span><span class="hljs-number">-2.3</span><span class="hljs-number">.0</span><span class="hljs-comment"># kafka user</span><span class="hljs-attr">user:</span> <span class="hljs-string">"kafka"</span><span class="hljs-attr">group:</span> <span class="hljs-string">"kafka"</span><span class="hljs-comment"># kafka data path</span><span class="hljs-attr">data_dir:</span> <span class="hljs-string">kafka_storage</span><span class="hljs-attr">kafka_log_path:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{install_dir}}</span>/kafka_log"</span><span class="hljs-comment"># kafka port</span><span class="hljs-attr">kafka_port:</span> <span class="hljs-number">9092</span><span class="hljs-attr">firewall_ports:</span> <span class="hljs-bullet">-</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ leader_port }}</span>"</span> <span class="hljs-bullet">-</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ vote_port }}</span>"</span> <span class="hljs-bullet">-</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ client_port }}</span>"</span> <span class="hljs-bullet">-</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ jmx_port }}</span>"</span> <span class="hljs-bullet">-</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ random_port }}</span>"</span><span class="hljs-comment"># env path</span><span class="hljs-attr">install_dir:</span> <span class="hljs-string">"/data/<span class="hljs-template-variable">{{ user }}</span>"</span><span class="hljs-attr">download_path:</span> <span class="hljs-string">"/root/"</span> <span class="hljs-comment"># your local download path</span><span class="hljs-attr">tmp_path:</span> <span class="hljs-string">"/tmp"</span><span class="hljs-comment"># host group</span><span class="hljs-attr">kafka_hosts:</span> <span class="hljs-string">kafka</span> <span class="hljs-comment"># the group define in hosts/host</span><span class="hljs-attr">kafka_log4j_rootlogger:</span> <span class="hljs-string">ERROR</span> <span class="hljs-attr">zk_hosts:</span> <span class="hljs-string">zookeeper</span></code></pre></div><br>需要注意的是install_dir,是文件的安装目录,安装前需要安装zk,如zk地址与kafka地址不同,请自行修改,安装脚本主要做了以下事情:</p><ol><li>检测是否安装jdk,scala</li><li>自定义配置文件,如需修改kafka的安装配置文件,请修改<code>install-kafka-cluster/kafka/roles/kafka/templates</code>下的配置文件</li><li><p>把kafka注册成系统服务,开机自启</p><p>最后运行<code>ansible-playbook kafka/kafka.yml</code></p></li></ol><h2 id="nacos集群安装"><a href="#nacos集群安装" class="headerlink" title="nacos集群安装"></a>nacos集群安装</h2><p>进入到nacos,在ansible的hosts文件添加<br><div class="hljs"><pre><code class="hljs shell">[nacos]192.168.0.1192.168.0.2192.168.0.3</code></pre></div></p><p>下面介绍一下,nacos安装变量<br><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-meta">---</span><span class="hljs-comment"># nacos version</span><span class="hljs-attr">nacos_version:</span> <span class="hljs-number">1.2</span><span class="hljs-number">.0</span><span class="hljs-comment"># nacos user</span><span class="hljs-attr">user:</span> <span class="hljs-string">"nacos"</span><span class="hljs-attr">group:</span> <span class="hljs-string">"nacos"</span><span class="hljs-comment"># nacos service address</span><span class="hljs-attr">nacos_address:</span> <span class="hljs-bullet">-</span> <span class="hljs-number">192.168</span><span class="hljs-number">.1</span><span class="hljs-number">.1</span><span class="hljs-string">:8848</span> <span class="hljs-bullet">-</span> <span class="hljs-number">192.168</span><span class="hljs-number">.1</span><span class="hljs-number">.2</span><span class="hljs-string">:8848</span> <span class="hljs-bullet">-</span> <span class="hljs-number">192.168</span><span class="hljs-number">.1</span><span class="hljs-number">.3</span><span class="hljs-string">:8848</span><span class="hljs-comment"># mysql address</span><span class="hljs-attr">mysql:</span> <span class="hljs-attr">address:</span> <span class="hljs-number">192.168</span><span class="hljs-number">.0</span><span class="hljs-number">.103</span><span class="hljs-string">:3306</span> <span class="hljs-attr">user:</span> <span class="hljs-string">xxx</span> <span class="hljs-attr">password:</span> <span class="hljs-string">xxx</span><span class="hljs-comment"># nacos data path</span><span class="hljs-attr">data_dir:</span> <span class="hljs-string">nacos_storage</span><span class="hljs-attr">nacos_log_path:</span> <span class="hljs-string">"<span class="hljs-template-variable">{{install_dir}}</span>/nacos_access_log"</span><span class="hljs-comment"># nacos port</span><span class="hljs-attr">nacos_port:</span> <span class="hljs-number">8848</span><span class="hljs-attr">firewall_ports:</span> <span class="hljs-bullet">-</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ leader_port }}</span>"</span> <span class="hljs-bullet">-</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ vote_port }}</span>"</span> <span class="hljs-bullet">-</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ client_port }}</span>"</span> <span class="hljs-bullet">-</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ jmx_port }}</span>"</span> <span class="hljs-bullet">-</span> <span class="hljs-string">"<span class="hljs-template-variable">{{ random_port }}</span>"</span><span class="hljs-comment"># env path</span><span class="hljs-attr">install_dir:</span> <span class="hljs-string">"/data/<span class="hljs-template-variable">{{ user }}</span>"</span><span class="hljs-attr">download_path:</span> <span class="hljs-string">"/root/"</span> <span class="hljs-comment"># your local download path</span><span class="hljs-attr">tmp_path:</span> <span class="hljs-string">"/tmp"</span><span class="hljs-comment"># host group</span><span class="hljs-attr">nacos_log4j_rootlogger:</span> <span class="hljs-string">ERROR</span> <span class="hljs-attr">nacos_hosts:</span> <span class="hljs-string">nacos</span></code></pre></div></p><p>因为nacos需要依赖mysql,所以有一个mysql,这里就不再描述如何安装mysql了。此外有mysql也需要一个<a href="https://github.com/alibaba/nacos/blob/master/distribution/conf/nacos-mysql.sql" target="_blank" rel="noopener">初始化脚本</a><br>只要在mysql上创建一个nacos数据库即可。<br>需要注意的是install_dir,是文件的安装目录,安装前需要安装zk,如zk地址与kafka地址不同,请自行修改,安装脚本主要做了以下事情:</p><ol><li>检测是否安装jdk</li><li>自定义配置文件,如需修改nacos的安装配置文件,请修改<code>install-nacos/nacos/roles/nacos/templates</code>下的配置文件</li><li><p>把nacos注册成系统服务,开机自启</p><p>最后运行<code>ansible-playbook nacos/nacos.yml</code></p></li></ol><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>通过ansible的模板,非常简单就可以把中间件的集群安装好。只要修改自己需要的配置,也支持自己二次开发编写自己操作的步骤。done,有问题请提交pr或者与我联系!</p>]]></content>
</entry>
<entry>
<title>使用elastalert进行日志告警</title>
<link href="/post/ee565d7d.html"/>
<url>/post/ee565d7d.html</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>日志监控解决方案中,常用EFK作为日志解决方案,我们需要针对业务日志进行告警,让开发人员及时收到消息。<br>Elastic Search的xpack有个alert组件,但是这个是收费的,经过研究发现elastalert还是不错的。</p><h2 id="部署步骤"><a href="#部署步骤" class="headerlink" title="部署步骤"></a>部署步骤</h2><ol><li>安装ES,配置信任证书或者是账号密码</li><li>配置smtp服务器和钉钉机器人</li><li>配置<code>all_any.yaml</code>文件 自定义规则可参考<a href="https://elastalert.readthedocs.io/en/latest/" target="_blank" rel="noopener">elasalert官方文档</a></li></ol><p>参考配置文件如下,本文件模板用于报警日志级别为ERROR的日志:</p><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span><span class="hljs-attr">data:</span> <span class="hljs-attr">all_any.yaml:</span> <span class="hljs-string">></span> <span class="hljs-attr">name:</span> <span class="hljs-string">prd</span> <span class="hljs-string">log</span> <span class="hljs-string">alert</span> <span class="hljs-attr">type:</span> <span class="hljs-string">any</span> <span class="hljs-attr">index:</span> <span class="hljs-string">project.xxxxx.*</span> <span class="hljs-attr">num_events:</span> <span class="hljs-number">1</span> <span class="hljs-attr">timeframe:</span> <span class="hljs-attr">minutes:</span> <span class="hljs-number">10</span> <span class="hljs-attr">filter:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">query:</span> <span class="hljs-attr">query_string:</span> <span class="hljs-attr">query:</span> <span class="hljs-string">"level: (emerg|alert|crit|err)"</span> <span class="hljs-attr">alert:</span> <span class="hljs-bullet">-</span> <span class="hljs-string">"dingtalk"</span> <span class="hljs-attr">email:</span> <span class="hljs-bullet">-</span> <span class="hljs-string">"[email protected]"</span> <span class="hljs-attr">dingtalk_webhook_url:</span> <span class="hljs-string">"https://oapi.dingtalk.com/robot/send?access_token=3f7ce1e337441b3dfbxxxx3e7"</span> <span class="hljs-attr">dingtalk_webhook_secret:</span> <span class="hljs-string">"SEC45d20d4c113xx5479dxxxx22aa79737354384606"</span> <span class="hljs-attr">dingtalk_msgtype:</span> <span class="hljs-string">text</span> <span class="hljs-attr">realert:</span> <span class="hljs-attr">minutes:</span> <span class="hljs-number">1</span> <span class="hljs-attr">query_key:</span> <span class="hljs-bullet">-</span> <span class="hljs-string">kubernetes.namespace_name</span> <span class="hljs-bullet">-</span> <span class="hljs-string">kubernetes.labels.app</span> <span class="hljs-attr">exponential_realert:</span> <span class="hljs-attr">hours:</span> <span class="hljs-number">1</span> <span class="hljs-attr">aggregation:</span> <span class="hljs-attr">minutes:</span> <span class="hljs-number">1</span> <span class="hljs-attr">aggregate_by_match_time:</span> <span class="hljs-literal">true</span> <span class="hljs-attr">aggregation_key:</span> <span class="hljs-bullet">-</span> <span class="hljs-string">kubernetes.namespace_name</span> <span class="hljs-bullet">-</span> <span class="hljs-string">kubernetes.labels.app</span> <span class="hljs-attr">alert_subject:</span> <span class="hljs-string">"Error {} @{}"</span> <span class="hljs-attr">alert_subject_args:</span> <span class="hljs-bullet">-</span> <span class="hljs-string">name</span> <span class="hljs-bullet">-</span> <span class="hljs-string">"@timestamp"</span> <span class="hljs-attr">alert_text_type:</span> <span class="hljs-string">alert_text_only</span> <span class="hljs-attr">alert_text:</span> <span class="hljs-string">|</span> <span class="hljs-comment">### {}</span> <span class="hljs-string">></span> <span class="hljs-attr">Namespace:</span> <span class="hljs-string">{}</span> <span class="hljs-string">></span> <span class="hljs-attr">App.Labels:</span> <span class="hljs-string">{}</span> <span class="hljs-string">></span> <span class="hljs-attr">Pod:</span> <span class="hljs-string">{}</span> <span class="hljs-string">></span> <span class="hljs-attr">Host:</span> <span class="hljs-string">{}</span> <span class="hljs-string">></span> <span class="hljs-attr">Level:</span> <span class="hljs-string">{}</span> <span class="hljs-string">></span> <span class="hljs-attr">Message:</span> <span class="hljs-string">{}</span> <span class="hljs-attr">alert_text_args:</span> <span class="hljs-bullet">-</span> <span class="hljs-string">name</span> <span class="hljs-bullet">-</span> <span class="hljs-string">kubernetes.namespace_name</span> <span class="hljs-bullet">-</span> <span class="hljs-string">kubernetes.labels.app</span> <span class="hljs-bullet">-</span> <span class="hljs-string">kubernetes.pod_name</span> <span class="hljs-bullet">-</span> <span class="hljs-string">hostname</span> <span class="hljs-bullet">-</span> <span class="hljs-string">level</span> <span class="hljs-bullet">-</span> <span class="hljs-string">message</span> <span class="hljs-attr">config.json:</span> <span class="hljs-string">|</span> <span class="hljs-string">{</span> <span class="hljs-attr">"appName":</span> <span class="hljs-string">"elastalert-server"</span><span class="hljs-string">,</span> <span class="hljs-attr">"port":</span> <span class="hljs-number">3030</span><span class="hljs-string">,</span> <span class="hljs-attr">"wsport":</span> <span class="hljs-number">3333</span><span class="hljs-string">,</span> <span class="hljs-attr">"elastalertPath":</span> <span class="hljs-string">"/opt/elastalert"</span><span class="hljs-string">,</span> <span class="hljs-attr">"verbose":</span> <span class="hljs-literal">false</span><span class="hljs-string">,</span> <span class="hljs-attr">"es_debug":</span> <span class="hljs-literal">false</span><span class="hljs-string">,</span> <span class="hljs-attr">"debug":</span> <span class="hljs-literal">false</span><span class="hljs-string">,</span> <span class="hljs-attr">"rulesPath":</span> <span class="hljs-string">{</span> <span class="hljs-attr">"relative":</span> <span class="hljs-literal">true</span><span class="hljs-string">,</span> <span class="hljs-attr">"path":</span> <span class="hljs-string">"/rules"</span> <span class="hljs-string">},</span> <span class="hljs-attr">"templatesPath":</span> <span class="hljs-string">{</span> <span class="hljs-attr">"relative":</span> <span class="hljs-literal">true</span><span class="hljs-string">,</span> <span class="hljs-attr">"path":</span> <span class="hljs-string">"/rule_templates"</span> <span class="hljs-string">},</span> <span class="hljs-attr">"es_host":</span> <span class="hljs-string">"es-service"</span><span class="hljs-string">,</span> <span class="hljs-attr">"es_port":</span> <span class="hljs-number">9200</span><span class="hljs-string">,</span> <span class="hljs-attr">"writeback_index":</span> <span class="hljs-string">"elastalert_status"</span> <span class="hljs-string">}</span> <span class="hljs-attr">elastalert.yaml:</span> <span class="hljs-string">|</span> <span class="hljs-attr">rules_folder:</span> <span class="hljs-string">rules</span> <span class="hljs-attr">run_every:</span> <span class="hljs-attr">seconds:</span> <span class="hljs-number">60</span> <span class="hljs-attr">buffer_time:</span> <span class="hljs-attr">minutes:</span> <span class="hljs-number">15</span> <span class="hljs-attr">es_host:</span> <span class="hljs-string">es-service</span> <span class="hljs-attr">es_port:</span> <span class="hljs-number">9200</span> <span class="hljs-attr">use_ssl:</span> <span class="hljs-literal">True</span> <span class="hljs-attr">verify_certs:</span> <span class="hljs-literal">True</span> <span class="hljs-attr">ca_certs:</span> <span class="hljs-string">/opt/elastalert/certs/ca.crt</span> <span class="hljs-attr">client_cert:</span> <span class="hljs-string">/opt/elastalert/certs/elastalert.crt</span> <span class="hljs-attr">client_key:</span> <span class="hljs-string">/opt/elastalert/certs/elastalert.key</span> <span class="hljs-attr">writeback_index:</span> <span class="hljs-string">elastalert_status</span> <span class="hljs-attr">writeback_alias:</span> <span class="hljs-string">elastalert_alerts</span> <span class="hljs-attr">alert_time_limit:</span> <span class="hljs-attr">days:</span> <span class="hljs-number">2</span> <span class="hljs-comment">#邮箱告警可以配置在config中</span> <span class="hljs-attr">smtp_host:</span> <span class="hljs-string">smtp.126.com</span> <span class="hljs-attr">smtp_port:</span> <span class="hljs-number">25</span> <span class="hljs-comment">#保存了邮箱验证的账号密码信息</span> <span class="hljs-attr">smtp_auth_file:</span> <span class="hljs-string">/opt/elastalert/smtp_auth.yaml</span> <span class="hljs-attr">from_addr:</span> <span class="hljs-string">[email protected]</span> <span class="hljs-attr">smtp_auth.yaml:</span> <span class="hljs-string">|</span> <span class="hljs-comment">#发送邮件的邮箱</span> <span class="hljs-attr">user:</span> <span class="hljs-string">[email protected]</span> <span class="hljs-comment">##不是邮箱密码,是设置的POP3密码</span> <span class="hljs-attr">password:</span> <span class="hljs-string">xxxxxxxx</span><span class="hljs-attr">kind:</span> <span class="hljs-string">ConfigMap</span><span class="hljs-attr">metadata:</span> <span class="hljs-attr">name:</span> <span class="hljs-string">elastalert-config</span></code></pre></div><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">apps/v1</span><span class="hljs-attr">kind:</span> <span class="hljs-string">Deployment</span><span class="hljs-attr">metadata:</span> <span class="hljs-attr">annotations:</span> <span class="hljs-attr">deployment.kubernetes.io/revision:</span> <span class="hljs-string">'1'</span> <span class="hljs-attr">labels:</span> <span class="hljs-attr">app:</span> <span class="hljs-string">elastalert</span> <span class="hljs-attr">name:</span> <span class="hljs-string">elastalert</span><span class="hljs-attr">spec:</span> <span class="hljs-attr">progressDeadlineSeconds:</span> <span class="hljs-number">600</span> <span class="hljs-attr">replicas:</span> <span class="hljs-number">1</span> <span class="hljs-attr">revisionHistoryLimit:</span> <span class="hljs-number">10</span> <span class="hljs-attr">selector:</span> <span class="hljs-attr">matchLabels:</span> <span class="hljs-attr">app:</span> <span class="hljs-string">elastalert</span> <span class="hljs-attr">strategy:</span> <span class="hljs-attr">rollingUpdate:</span> <span class="hljs-attr">maxSurge:</span> <span class="hljs-number">1</span> <span class="hljs-attr">maxUnavailable:</span> <span class="hljs-number">1</span> <span class="hljs-attr">type:</span> <span class="hljs-string">RollingUpdate</span> <span class="hljs-attr">template:</span> <span class="hljs-attr">metadata:</span> <span class="hljs-attr">creationTimestamp:</span> <span class="hljs-literal">null</span> <span class="hljs-attr">labels:</span> <span class="hljs-attr">app:</span> <span class="hljs-string">elastalert</span> <span class="hljs-attr">name:</span> <span class="hljs-string">elastalert</span> <span class="hljs-attr">spec:</span> <span class="hljs-attr">containers:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">env:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">TZ</span> <span class="hljs-attr">value:</span> <span class="hljs-string">Asia/Shanghai</span> <span class="hljs-attr">image:</span> <span class="hljs-string">'brycehuang/elastalert-dingtalk:3.0.0-beta.1'</span> <span class="hljs-attr">imagePullPolicy:</span> <span class="hljs-string">IfNotPresent</span> <span class="hljs-attr">name:</span> <span class="hljs-string">elastalert</span> <span class="hljs-attr">ports:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">containerPort:</span> <span class="hljs-number">3030</span> <span class="hljs-attr">name:</span> <span class="hljs-string">tcp-3030</span> <span class="hljs-attr">protocol:</span> <span class="hljs-string">TCP</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">containerPort:</span> <span class="hljs-number">3333</span> <span class="hljs-attr">name:</span> <span class="hljs-string">tcp-3333</span> <span class="hljs-attr">protocol:</span> <span class="hljs-string">TCP</span> <span class="hljs-attr">resources:</span> <span class="hljs-attr">limits:</span> <span class="hljs-attr">cpu:</span> <span class="hljs-string">'2'</span> <span class="hljs-attr">requests:</span> <span class="hljs-attr">cpu:</span> <span class="hljs-string">'1'</span> <span class="hljs-attr">terminationMessagePath:</span> <span class="hljs-string">/dev/termination-log</span> <span class="hljs-attr">terminationMessagePolicy:</span> <span class="hljs-string">File</span> <span class="hljs-attr">volumeMounts:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">mountPath:</span> <span class="hljs-string">/opt/elastalert/config.yaml</span> <span class="hljs-attr">name:</span> <span class="hljs-string">elastalert-config</span> <span class="hljs-attr">subPath:</span> <span class="hljs-string">elastalert.yaml</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">mountPath:</span> <span class="hljs-string">/opt/elastalert/smtp_auth.yaml</span> <span class="hljs-attr">name:</span> <span class="hljs-string">elastalert-config</span> <span class="hljs-attr">subPath:</span> <span class="hljs-string">smtp_auth.yaml</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">mountPath:</span> <span class="hljs-string">/opt/elastalert-server/config/config.json</span> <span class="hljs-attr">name:</span> <span class="hljs-string">elastalert-config</span> <span class="hljs-attr">subPath:</span> <span class="hljs-string">config.json</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">mountPath:</span> <span class="hljs-string">/opt/elastalert/rules/all_any.yaml</span> <span class="hljs-attr">name:</span> <span class="hljs-string">elastalert-config</span> <span class="hljs-attr">subPath:</span> <span class="hljs-string">all_any.yaml</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">mountPath:</span> <span class="hljs-string">/opt/elastalert/certs</span> <span class="hljs-attr">name:</span> <span class="hljs-string">elastalert-certs</span> <span class="hljs-attr">readOnly:</span> <span class="hljs-literal">true</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">mountPath:</span> <span class="hljs-string">/opt/elastalert/server_data</span> <span class="hljs-attr">name:</span> <span class="hljs-string">server-data</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">mountPath:</span> <span class="hljs-string">/opt/logs</span> <span class="hljs-attr">name:</span> <span class="hljs-string">elastalert-logs</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">mountPath:</span> <span class="hljs-string">/opt/elastalert/rule_templates</span> <span class="hljs-attr">name:</span> <span class="hljs-string">rule-templates</span> <span class="hljs-attr">dnsPolicy:</span> <span class="hljs-string">ClusterFirst</span> <span class="hljs-attr">restartPolicy:</span> <span class="hljs-string">Always</span> <span class="hljs-attr">schedulerName:</span> <span class="hljs-string">default-scheduler</span> <span class="hljs-attr">securityContext:</span> <span class="hljs-string">{}</span> <span class="hljs-attr">terminationGracePeriodSeconds:</span> <span class="hljs-number">30</span> <span class="hljs-attr">volumes:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">configMap:</span> <span class="hljs-attr">defaultMode:</span> <span class="hljs-number">420</span> <span class="hljs-attr">name:</span> <span class="hljs-string">elastalert-config</span> <span class="hljs-attr">name:</span> <span class="hljs-string">elastalert-config</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">elastalert-certs</span> <span class="hljs-attr">secret:</span> <span class="hljs-attr">defaultMode:</span> <span class="hljs-number">420</span> <span class="hljs-attr">secretName:</span> <span class="hljs-string">elastalert-secret</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">emptyDir:</span> <span class="hljs-string">{}</span> <span class="hljs-attr">name:</span> <span class="hljs-string">server-data</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">emptyDir:</span> <span class="hljs-string">{}</span> <span class="hljs-attr">name:</span> <span class="hljs-string">elastalert-logs</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">emptyDir:</span> <span class="hljs-string">{}</span> <span class="hljs-attr">name:</span> <span class="hljs-string">rule-templates</span></code></pre></div>]]></content>
<categories>
<category>告警</category>
</categories>
<tags>
<tag>elastic search</tag>
<tag>日志</tag>
</tags>
</entry>
<entry>
<title>kubernetes 污点和容忍实战</title>
<link href="/post/e68bb2a1.html"/>
<url>/post/e68bb2a1.html</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>如果已经将kubernetes用于实践,常常需要k8s的一些高级特性才能满足实际需求。<br>这不,我们需要搭建性能测试平台,但是不想重新搭建一套k8s环境,但又不想原来运行在k8s的应用调度到新的节点上,影响测试结果。<br>经过一番研究,发现k8s的污点和容忍的特性满足了我们需求。下面将介绍一下污点和容忍度。</p><h2 id="污点(Taints)"><a href="#污点(Taints)" class="headerlink" title="污点(Taints)"></a>污点(Taints)</h2><p>k8s可以给node添加污点,对于添加了污点的node会和pod产生排斥,甚至会驱逐pod,使得pod无法调度到该node。<br>那么如何对node添加污点呢?使用以下命令即可对node添加污点:</p><p><code>kubectl taint nodes node1 key=value:{effect}</code></p><p>其中effect目前k8s有三个可以支持的指令:</p><ol><li>NoSchedule 如果pod不能容忍node上的污点,pod则不能被调度到包含这些污点的节点上,已有pod不受影响</li><li>PreferNoSchedule 如果pod不能容忍node上的污点,k8s将会优先将pod调度到其他节点,但仍有机会将pod调度到该node上</li><li>NoExecute 如果pod不能容忍node上的污点,pod无法调度到这个node 上,已有的无法容忍污点的pod也会被驱逐。</li></ol><p>查看污点:</p><p><code>kubectl describe nodes node1 | grep -A 5 Tain</code></p><p>删除污点:</p><p><code>kubectl taint node node1 key:NoSchedule-</code></p><h2 id="容忍(Tolerations)"><a href="#容忍(Tolerations)" class="headerlink" title="容忍(Tolerations)"></a>容忍(Tolerations)</h2><p>为保证指定pod调度到想要的node上,可以在pod上设置容忍,</p><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-attr">tolerations:</span><span class="hljs-bullet">-</span> <span class="hljs-attr">key:</span> <span class="hljs-string">"key1"</span> <span class="hljs-attr">operator:</span> <span class="hljs-string">"Equal"</span> <span class="hljs-attr">value:</span> <span class="hljs-string">"value1"</span> <span class="hljs-attr">effect:</span> <span class="hljs-string">"NoSchedule"</span> <span class="hljs-attr">tolerationSeconds:</span> <span class="hljs-number">3600</span><span class="hljs-bullet">-</span> <span class="hljs-attr">key:</span> <span class="hljs-string">"key1"</span> <span class="hljs-attr">operator:</span> <span class="hljs-string">"Equal"</span> <span class="hljs-attr">value:</span> <span class="hljs-string">"value1"</span> <span class="hljs-attr">effect:</span> <span class="hljs-string">"NoExecute"</span><span class="hljs-bullet">-</span> <span class="hljs-attr">key:</span> <span class="hljs-string">"key2"</span> <span class="hljs-attr">operator:</span> <span class="hljs-string">"Exists"</span> <span class="hljs-attr">effect:</span> <span class="hljs-string">"NoSchedule"</span></code></pre></div><p>这样就可以将我们pod调度到指定的node节点上。</p><ul><li>其中key, vaule, effect要与Node上设置的taint保持一致</li><li>operator的值为Exists将会忽略value值</li><li>tolerationSeconds用于描述当Pod需要被驱逐时可以在Pod上继续保留运行的时间。</li></ul><p>这里说明一下两个特例,参考<a href="https://blog.frognew.com/2018/05/taint-and-toleration.html" target="_blank" rel="noopener">青蛙小白</a><br>示例1: 当不指定key值时,表示容忍所有的污点key:</p><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-attr">tolerations:</span><span class="hljs-bullet">-</span> <span class="hljs-attr">operator:</span> <span class="hljs-string">"Exists"</span></code></pre></div><p>示例2:当不指定effect值时,表示容忍所有的污点作用:</p><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-attr">tolerations:</span><span class="hljs-bullet">-</span> <span class="hljs-attr">key:</span> <span class="hljs-string">"key"</span> <span class="hljs-attr">operator:</span> <span class="hljs-string">"Exists"</span></code></pre></div><h2 id="实际应用"><a href="#实际应用" class="headerlink" title="实际应用"></a>实际应用</h2><p>明白了上面的原理,实现起来就很简单了,分为以下几个步骤:</p><ol><li>对新加的node打上标签: <code>oc label nodes node1 node2 ... machine=capability</code></li><li>对新加的node打上污点:<code>oc taint nodes node1 node2 ... key=value:NoSchedule</code></li><li>修改性能测试应的编排文件,在<code>spec</code>增加pod容忍配置,如:</li></ol><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-attr">spec:</span> <span class="hljs-attr">nodeSelector:</span> <span class="hljs-attr">machine:</span> <span class="hljs-string">capability</span> <span class="hljs-attr">tolerations:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">effect:</span> <span class="hljs-string">NoSchedule</span> <span class="hljs-attr">key:</span> <span class="hljs-string">key</span> <span class="hljs-attr">operator:</span> <span class="hljs-string">Exists</span></code></pre></div><p>这样就大工告成了,收工下班。。。</p><p><strong>参考文档:</strong><br><a href="https://blog.frognew.com/2018/05/taint-and-toleration.html" target="_blank" rel="noopener">Kubernetes Pod调度进阶:Taints(污点)和Tolerations(容忍)</a><br><a href="https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/" target="_blank" rel="noopener">Taints and Tolerations</a></p>]]></content>
</entry>
<entry>
<title>Prometheus operator使用black_box监控服务连通性</title>
<link href="/post/d0bd304f.html"/>
<url>/post/d0bd304f.html</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>经常有外部服务的链接需要监控其可达性,并且k8s集群内服务连通性也需要监控。<br>发现prometheus提供的blackbox_exporter比较符合我们的需求。下面将介绍如何使用blackbox_exporter以及如何将blackbox_exporter集成进prometheus operator里面去。</p><h2 id="blackbox-exporter介绍"><a href="#blackbox-exporter介绍" class="headerlink" title="blackbox_exporter介绍"></a>blackbox_exporter介绍</h2><p>blackbox_exporter允许通过HTTP,HTTPS,DNS,TCP和ICMP对端点进行黑盒探测。<br>主要是用来检查网络的可达性,在k8s中可以用来检查服务是否可用。其安装和使用也比较简单。安装好后直接使用其中http模块,如:<br><code>http://localhost:9115/probe?target=google.com&module=http_2xx</code><br>即可获取google.com的连通性信息,更多信息请参考<a href="https://github.com/prometheus/blackbox_exporter" target="_blank" rel="noopener">black_box</a></p><h2 id="prometheus采集"><a href="#prometheus采集" class="headerlink" title="prometheus采集"></a>prometheus采集</h2><p>众所周知,prometheus采用的是pull的方式收集监控数据,blackbox_exporter也是在prometheus来采集的时候才知道有要监控什么地址,不像node_exporter,直接收集本机的信息<br>所以在prometheus的配置文件中,是需要安装如下方式配置的:</p><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-attr">scrape_configs:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">job_name:</span> <span class="hljs-string">'blackbox'</span> <span class="hljs-attr">metrics_path:</span> <span class="hljs-string">/probe</span> <span class="hljs-attr">params:</span> <span class="hljs-attr">module:</span> <span class="hljs-string">[http_2xx]</span> <span class="hljs-comment"># Look for a HTTP 200 response.</span> <span class="hljs-attr">static_configs:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">targets:</span> <span class="hljs-bullet">-</span> <span class="hljs-string">http://prometheus.io</span> <span class="hljs-comment"># Target to probe with http.</span> <span class="hljs-bullet">-</span> <span class="hljs-string">https://prometheus.io</span> <span class="hljs-comment"># Target to probe with https.</span> <span class="hljs-bullet">-</span> <span class="hljs-string">http://example.com:8080</span> <span class="hljs-comment"># Target to probe with http on port 8080.</span> <span class="hljs-attr">relabel_configs:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">source_labels:</span> <span class="hljs-string">[__address__]</span> <span class="hljs-attr">target_label:</span> <span class="hljs-string">__param_target</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">source_labels:</span> <span class="hljs-string">[__param_target]</span> <span class="hljs-attr">target_label:</span> <span class="hljs-string">instance</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">target_label:</span> <span class="hljs-string">__address__</span> <span class="hljs-attr">replacement:</span> <span class="hljs-number">127.0</span><span class="hljs-number">.0</span><span class="hljs-number">.1</span><span class="hljs-string">:9115</span> <span class="hljs-comment"># The blackbox exporter's real hostname:port.</span></code></pre></div><p>上面的yaml配置文件中,是需要配置<code>static_config</code>,更多配置请参考:<a href="https://prometheus.io/docs/prometheus/latest/configuration/configuration/#configuration-file" target="_blank" rel="noopener">prometheus-configuration</a></p><h2 id="prometheus-operator-集成"><a href="#prometheus-operator-集成" class="headerlink" title="prometheus-operator 集成"></a>prometheus-operator 集成</h2><p>因为我们的应用是部署在kubernetes集群里面的,我们需要在prometheus-operator中去集成这样的网络监控。</p><h3 id="部署blackbox-exporter"><a href="#部署blackbox-exporter" class="headerlink" title="部署blackbox_exporter"></a>部署blackbox_exporter</h3>]]></content>
</entry>
<entry>
<title>prometheus-operator监控外部服务</title>
<link href="/post/aa7f8012.html"/>
<url>/post/aa7f8012.html</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>在生产环境中,常常有部署在k8s平台外的服务,对于平台外的主机我们需要及时知道哪些机器宕机了,所以我们想把这些主机纳入Prometheus-Operator监控,并能产生告警机制。<br>采用node_exporter的形式对k8s平台外部的机器进行数据采集,并暴露metrics接口。主要有以及下步骤:</p><ol><li><a href="#为主机安装node_exporter">为主机安装node_exporter</a></li><li><a href="#创建EndPoints资源">创建EndPoints资源</a></li><li><a href="#创建ServiceMonitor">创建ServiceMonitor</a></li><li><a href="#创建告警Rule">创建告警Rule</a></li></ol><h2 id="为主机安装node-exporter"><a href="#为主机安装node-exporter" class="headerlink" title="为主机安装node_exporter"></a>为主机安装node_exporter</h2><p>为监控各云主机,需要在主机安装node_exporter,该组件可以采集主机cpu、内存、网络、磁盘等各种主机信息,并且提供metrics的web接口供promethues<br>采集。安装方式可以使用docker 或者 本地服务的方式安装,本文介绍docker方式安装。以下为初始化脚本</p><div class="hljs"><pre><code class="hljs bash"><span class="hljs-meta">#!/bin/bash</span><span class="hljs-built_in">set</span> -xregistries=<span class="hljs-variable">$1</span>yum install -y dockersystemctl <span class="hljs-built_in">enable</span> dockersystemctl start dockersed -i <span class="hljs-string">'s/{}/{ "insecure-registries":['</span><span class="hljs-variable">$registries</span><span class="hljs-string">'] }/'</span> /etc/docker/daemon.jsonsystemctl restart dockerdocker run -d --name node_exporter \ --restart=always \ --net=<span class="hljs-string">"host"</span> \ --pid=<span class="hljs-string">"host"</span> \ --privileged=<span class="hljs-literal">true</span> \ -v <span class="hljs-string">"/proc:/host/proc:ro"</span> \ -v <span class="hljs-string">"/sys:/host/sys:ro"</span> \ -v <span class="hljs-string">"/:/rootfs:ro"</span> \ <span class="hljs-variable">$registries</span>/prometheus/node-exporter:v0.18.1 \ --path.procfs=/host/proc \ --path.rootfs=/rootfs \ --path.sysfs=/host/sys \ --collector.filesystem.ignored-mount-points=<span class="hljs-string">'^/(sys|proc|dev|host|etc)($$|/)'</span></code></pre></div><p>其中安装了docker以及源的修改,部署了node_exporter 使用ansible可以批量安装 <code>ansible all -m script -a "script path" 192.168.0.9:5001</code><br>或者按照自己的想法进行修改,测试<code>ansible all -m shell curl localhost:9100/metrics</code> 能发现数据说明安装完成</p><h2 id="创建EndPoints资源"><a href="#创建EndPoints资源" class="headerlink" title="创建EndPoints资源"></a>创建EndPoints资源</h2><p>promethues-operator在k8s中定义了一系列的crd资源,其中ServiceMonitor只能对Service进行监听,而我们的服务是游离在k8s集群外部的,所以<br>需要将外部地址做成EndPoints资源。如代码:<br><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span><span class="hljs-attr">kind:</span> <span class="hljs-string">Endpoints</span><span class="hljs-attr">metadata:</span> <span class="hljs-attr">name:</span> <span class="hljs-string">node-exporter-ext</span><span class="hljs-attr">subsets:</span><span class="hljs-bullet">-</span> <span class="hljs-attr">addresses:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">ip:</span> <span class="hljs-number">10.8</span><span class="hljs-number">.122</span><span class="hljs-number">.21</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">ip:</span> <span class="hljs-number">10.8</span><span class="hljs-number">.122</span><span class="hljs-number">.21</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">ip:</span> <span class="hljs-number">10.8</span><span class="hljs-number">.122</span><span class="hljs-number">.21</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">ip:</span> <span class="hljs-number">10.8</span><span class="hljs-number">.122</span><span class="hljs-number">.21</span> <span class="hljs-attr">ports:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">http-9100</span> <span class="hljs-attr">port:</span> <span class="hljs-number">9100</span> <span class="hljs-attr">protocol:</span> <span class="hljs-string">TCP</span></code></pre></div></p><p>只需要将ip或者端口填上即可</p><h2 id="创建ServiceMonitor"><a href="#创建ServiceMonitor" class="headerlink" title="创建ServiceMonitor"></a>创建ServiceMonitor</h2><p>创建好了EndPoints,需要创建Service和ServiceMonitor</p><p>Service 文件如下:</p><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span><span class="hljs-attr">kind:</span> <span class="hljs-string">Service</span><span class="hljs-attr">metadata:</span> <span class="hljs-attr">labels:</span> <span class="hljs-attr">app:</span> <span class="hljs-string">node-exporter-ext</span> <span class="hljs-attr">name:</span> <span class="hljs-string">node-exporter-ext</span><span class="hljs-attr">spec:</span> <span class="hljs-attr">clusterIP:</span> <span class="hljs-string">None</span> <span class="hljs-attr">ports:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">http-9100</span> <span class="hljs-attr">port:</span> <span class="hljs-number">9100</span> <span class="hljs-attr">protocol:</span> <span class="hljs-string">TCP</span> <span class="hljs-attr">targetPort:</span> <span class="hljs-string">http-9100</span> <span class="hljs-attr">sessionAffinity:</span> <span class="hljs-string">None</span> <span class="hljs-attr">type:</span> <span class="hljs-string">ClusterIP</span><span class="hljs-attr">status:</span> <span class="hljs-attr">loadBalancer:</span> <span class="hljs-string">{}</span></code></pre></div><p>ServiceMonitor 是promethues-operator自定义的CRD资源,创建一个ServiceMonitor,promethues-operator就会创建一个job,去收集相关的metrics,实例如下:</p><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">monitoring.coreos.com/v1</span><span class="hljs-attr">kind:</span> <span class="hljs-string">ServiceMonitor</span><span class="hljs-attr">metadata:</span> <span class="hljs-attr">labels:</span> <span class="hljs-attr">app:</span> <span class="hljs-string">node-exporter-ext</span> <span class="hljs-attr">prometheus:</span> <span class="hljs-string">ipaas</span> <span class="hljs-attr">name:</span> <span class="hljs-string">node-exporter-ext</span><span class="hljs-attr">spec:</span> <span class="hljs-attr">endpoints:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">interval:</span> <span class="hljs-string">30s</span> <span class="hljs-attr">port:</span> <span class="hljs-string">http-9100</span> <span class="hljs-attr">scheme:</span> <span class="hljs-string">http</span> <span class="hljs-attr">jobLabel:</span> <span class="hljs-string">app</span> <span class="hljs-attr">namespaceSelector:</span> <span class="hljs-attr">matchNames:</span> <span class="hljs-bullet">-</span> <span class="hljs-string">monitoring</span> <span class="hljs-attr">selector:</span> <span class="hljs-string">{}</span></code></pre></div><p>上面代码中描述了endpoints的信息,以及抓取周期为30s,到了这步,从生成采集服务到数据录入prometheus就完成了,也可以制作dashboard,<a href="https://grafana.com/grafana/dashboards/1860" target="_blank" rel="noopener">node_exporter dashboard推荐</a></p><h2 id="创建告警Rule"><a href="#创建告警Rule" class="headerlink" title="创建告警Rule"></a>创建告警Rule</h2><p>prometheus-operator告警文件对应于k8s下kind为PrometheusRule的CRD资源,参考如下:</p><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">monitoring.coreos.com/v1</span><span class="hljs-attr">kind:</span> <span class="hljs-string">PrometheusRule</span><span class="hljs-attr">metadata:</span> <span class="hljs-attr">labels:</span> <span class="hljs-attr">prometheus:</span> <span class="hljs-string">ipaas</span> <span class="hljs-attr">role:</span> <span class="hljs-string">alert-rules</span> <span class="hljs-attr">name:</span> <span class="hljs-string">prometheus-ipaas-node-alerts</span><span class="hljs-attr">spec:</span> <span class="hljs-attr">groups:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">主机状态-监控告警</span> <span class="hljs-attr">rules:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">alert:</span> <span class="hljs-string">主机状态</span> <span class="hljs-attr">annotations:</span> <span class="hljs-attr">description:</span> <span class="hljs-string">'<span class="hljs-template-variable">{{$labels.instance}}</span>:服务器延时超过5分钟'</span> <span class="hljs-attr">summary:</span> <span class="hljs-string">'<span class="hljs-template-variable">{{$labels.instance}}</span>:服务器宕机'</span> <span class="hljs-attr">expr:</span> <span class="hljs-string">'up{job=~"node-exporter.*"} == 0'</span> <span class="hljs-attr">for:</span> <span class="hljs-string">1m</span> <span class="hljs-attr">labels:</span> <span class="hljs-attr">status:</span> <span class="hljs-string">非常严重</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">alert:</span> <span class="hljs-string">CPU使用情况</span> <span class="hljs-attr">annotations:</span> <span class="hljs-attr">description:</span> <span class="hljs-string">'<span class="hljs-template-variable">{{$labels.mountpoint }}</span> CPU使用大于60%(目前使用:<span class="hljs-template-variable">{{$value}}</span>%)'</span> <span class="hljs-attr">summary:</span> <span class="hljs-string">'<span class="hljs-template-variable">{{$labels.mountpoint}}</span> CPU使用率过高!'</span> <span class="hljs-attr">expr:</span> <span class="hljs-string">>-</span> <span class="hljs-number">100</span><span class="hljs-string">-(avg(irate(node_cpu_seconds_total{mode="idle"}[5m]))</span> <span class="hljs-string">by(instance)*</span> <span class="hljs-number">100</span><span class="hljs-string">)</span> <span class="hljs-string">></span> <span class="hljs-number">60</span> <span class="hljs-attr">for:</span> <span class="hljs-string">1m</span> <span class="hljs-attr">labels:</span> <span class="hljs-attr">status:</span> <span class="hljs-string">一般告警</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">alert:</span> <span class="hljs-string">内存使用</span> <span class="hljs-attr">annotations:</span> <span class="hljs-attr">description:</span> <span class="hljs-string">'<span class="hljs-template-variable">{{$labels.mountpoint }}</span> 内存使用大于80%(目前使用:<span class="hljs-template-variable">{{$value}}</span>%)'</span> <span class="hljs-attr">summary:</span> <span class="hljs-string">'<span class="hljs-template-variable">{{$labels.mountpoint}}</span> 内存使用率过高!'</span> <span class="hljs-attr">expr:</span> <span class="hljs-string">>-</span> <span class="hljs-number">100</span> <span class="hljs-string">-(node_memory_MemTotal_bytes</span> <span class="hljs-string">-node_memory_MemFree_bytes+node_memory_Buffers_bytes+node_memory_Cached_bytes</span> <span class="hljs-string">)</span> <span class="hljs-string">/</span> <span class="hljs-string">node_memory_MemTotal_bytes</span> <span class="hljs-string">*</span> <span class="hljs-number">100</span><span class="hljs-string">></span> <span class="hljs-number">80</span> <span class="hljs-attr">for:</span> <span class="hljs-string">1m</span> <span class="hljs-attr">labels:</span> <span class="hljs-attr">status:</span> <span class="hljs-string">严重告警</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">alert:</span> <span class="hljs-string">IO性能</span> <span class="hljs-attr">annotations:</span> <span class="hljs-attr">description:</span> <span class="hljs-string">'<span class="hljs-template-variable">{{$labels.mountpoint }}</span> 流入磁盘IO大于60%(目前使用:<span class="hljs-template-variable">{{$value}}</span>)'</span> <span class="hljs-attr">summary:</span> <span class="hljs-string">'<span class="hljs-template-variable">{{$labels.mountpoint}}</span> 流入磁盘IO使用率过高!'</span> <span class="hljs-attr">expr:</span> <span class="hljs-string">>-</span> <span class="hljs-number">100</span><span class="hljs-string">-(avg(irate(node_disk_io_time_seconds_total[1m]))</span> <span class="hljs-string">by(instance)*</span> <span class="hljs-number">100</span><span class="hljs-string">)</span> <span class="hljs-string"><</span> <span class="hljs-number">60</span> <span class="hljs-attr">for:</span> <span class="hljs-string">1m</span> <span class="hljs-attr">labels:</span> <span class="hljs-attr">status:</span> <span class="hljs-string">严重告警</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">alert:</span> <span class="hljs-string">网络</span> <span class="hljs-attr">annotations:</span> <span class="hljs-attr">description:</span> <span class="hljs-string">'<span class="hljs-template-variable">{{$labels.mountpoint }}</span>流入网络带宽持续2分钟高于100M. RX带宽使用率<span class="hljs-template-variable">{{$value}}</span>'</span> <span class="hljs-attr">summary:</span> <span class="hljs-string">'<span class="hljs-template-variable">{{$labels.mountpoint}}</span> 流入网络带宽过高!'</span> <span class="hljs-attr">expr:</span> <span class="hljs-string">>-</span> <span class="hljs-string">((sum(rate</span> <span class="hljs-string">(node_network_receive_bytes_total{device!~'tap.*|veth.*|br.*|docker.*|virbr*|lo*'}[5m]))</span> <span class="hljs-string">by</span> <span class="hljs-string">(instance))</span> <span class="hljs-string">/</span> <span class="hljs-number">100</span><span class="hljs-string">)</span> <span class="hljs-string">></span> <span class="hljs-number">102400</span> <span class="hljs-attr">for:</span> <span class="hljs-string">1m</span> <span class="hljs-attr">labels:</span> <span class="hljs-attr">status:</span> <span class="hljs-string">严重告警</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">alert:</span> <span class="hljs-string">网络</span> <span class="hljs-attr">annotations:</span> <span class="hljs-attr">description:</span> <span class="hljs-string">'<span class="hljs-template-variable">{{$labels.mountpoint }}</span>流出网络带宽持续2分钟高于100M. RX带宽使用率<span class="hljs-template-variable">{{$value}}</span>'</span> <span class="hljs-attr">summary:</span> <span class="hljs-string">'<span class="hljs-template-variable">{{$labels.mountpoint}}</span> 流出网络带宽过高!'</span> <span class="hljs-attr">expr:</span> <span class="hljs-string">>-</span> <span class="hljs-string">((sum(rate</span> <span class="hljs-string">(node_network_transmit_bytes_total{device!~'tap.*|veth.*|br.*|docker.*|virbr*|lo*'}[5m]))</span> <span class="hljs-string">by</span> <span class="hljs-string">(instance))</span> <span class="hljs-string">/</span> <span class="hljs-number">100</span><span class="hljs-string">)</span> <span class="hljs-string">></span> <span class="hljs-number">102400</span> <span class="hljs-attr">for:</span> <span class="hljs-string">1m</span> <span class="hljs-attr">labels:</span> <span class="hljs-attr">status:</span> <span class="hljs-string">严重告警</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">alert:</span> <span class="hljs-string">TCP会话</span> <span class="hljs-attr">annotations:</span> <span class="hljs-attr">description:</span> <span class="hljs-string">'<span class="hljs-template-variable">{{$labels.mountpoint }}</span> TCP_ESTABLISHED大于1000%(目前使用:<span class="hljs-template-variable">{{$value}}</span>%)'</span> <span class="hljs-attr">summary:</span> <span class="hljs-string">'<span class="hljs-template-variable">{{$labels.mountpoint}}</span> TCP_ESTABLISHED过高!'</span> <span class="hljs-attr">expr:</span> <span class="hljs-string">node_netstat_Tcp_CurrEstab</span> <span class="hljs-string">></span> <span class="hljs-number">1000</span> <span class="hljs-attr">for:</span> <span class="hljs-string">1m</span> <span class="hljs-attr">labels:</span> <span class="hljs-attr">status:</span> <span class="hljs-string">严重告警</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">alert:</span> <span class="hljs-string">磁盘容量</span> <span class="hljs-attr">annotations:</span> <span class="hljs-attr">description:</span> <span class="hljs-string">'<span class="hljs-template-variable">{{$labels.mountpoint }}</span> 磁盘分区使用大于80%(目前使用:<span class="hljs-template-variable">{{$value}}</span>%)'</span> <span class="hljs-attr">summary:</span> <span class="hljs-string">'<span class="hljs-template-variable">{{$labels.mountpoint}}</span> 磁盘分区使用率过高!'</span> <span class="hljs-attr">expr:</span> <span class="hljs-string">>-</span> <span class="hljs-number">100</span><span class="hljs-string">-(node_filesystem_free_bytes{fstype=~"ext4|xfs"}/node_filesystem_size_bytes</span> <span class="hljs-string">{fstype=~"ext4|xfs"}*100)</span> <span class="hljs-string">></span> <span class="hljs-number">80</span> <span class="hljs-attr">for:</span> <span class="hljs-string">1m</span> <span class="hljs-attr">labels:</span> <span class="hljs-attr">status:</span> <span class="hljs-string">严重告警</span></code></pre></div><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>这个流程走下来,我们总结一下,集群外部的服务,无论是主机监控还是中间件业务监控,都是一样的,就是各自需要采集的数据和告警规则不一样而已。<br>无非是以下步骤:</p><ol><li>准备相关exporter,如:kafka_exporter、zookeeper_exporter、node_exporter</li><li>安装exporter,并设置好相关权限,如:node_exporter需要主机的相关信息,kafka_exporter需要kafka的链接地址</li><li>设置EndPoints资源,让k8s集群能够获得外部exporter的地址</li><li>设置service和servicemonitor,也就是让prometheus建立采集的任务job</li><li>设置grafana的dashboard,可以到<a href="https://grafana.com/grafana/dashboards" target="_blank" rel="noopener">grafana-dashboard</a>去找找</li><li>设置告警规则,不同角色不同服务,告警规则的设置,告警路由的完善。以kafka的监控为例,系统运维只关心,机器有没有问题,kafka管理员只关心,kafka有没有正常运行,jvm怎么样,用户只关心topic消费了没?能不能发送和接收消息,消息的命中率和消息堆积情况怎么样</li></ol><p>以上就是在prometheus-operator中如何监控外部服务的内容了,感谢阅读!QAQ</p>]]></content>
<categories>
<category>监控</category>
</categories>
<tags>
<tag>prometheus</tag>
<tag>prometheus-operator</tag>
<tag>k8s</tag>
<tag>alert</tag>
</tags>
</entry>
<entry>
<title>为没有密码保护的okd应用添加oauth proxy</title>
<link href="/post/79465543.html"/>
<url>/post/79465543.html</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>很多时候为了安全起见,需要对一些控制台添加访问限制,但是很多时候,这些后端的控制台都非常简便,没有所谓的访问控制。即使就算有,倘若应用多了起来,会很多账号密码,又不统一方便,容易忘记或者遗失。为解决这个问题,引入了okd的oauth proxy,相应的web页面进行访问管理。</p><h2 id="oauth-proxy介绍"><a href="#oauth-proxy介绍" class="headerlink" title="oauth-proxy介绍"></a>oauth-proxy介绍</h2><p><a href="https://github.com/openshift/oauth-proxy" target="_blank" rel="noopener">oauth-proxys</a>是openshift专注于在OpenShift上提供最简单的安全代理,下面以安装nacos为例,将提到不少坑点</p><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span><span class="hljs-attr">kind:</span> <span class="hljs-string">Template</span><span class="hljs-attr">metadata:</span> <span class="hljs-attr">name:</span> <span class="hljs-string">nacos-template</span> <span class="hljs-attr">annotations:</span> <span class="hljs-attr">description:</span> <span class="hljs-string">nacos-template</span><span class="hljs-attr">parameters:</span><span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">NAMESPACE</span> <span class="hljs-attr">value:</span> <span class="hljs-string">midware</span><span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">STORAGECLASS</span> <span class="hljs-attr">value:</span> <span class="hljs-string">nfs-storage</span><span class="hljs-attr">objects:</span><span class="hljs-comment">#为nacos创建service account 注意annotations中的 "reference":{"kind":"Route","name":"nacos-proxy"}}',其中的name要为接下来的route的名字</span><span class="hljs-bullet">-</span> <span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span> <span class="hljs-attr">kind:</span> <span class="hljs-string">ServiceAccount</span> <span class="hljs-attr">metadata:</span> <span class="hljs-attr">namespace:</span> <span class="hljs-string">${NAMESPACE}</span> <span class="hljs-attr">name:</span> <span class="hljs-string">nacos-ipaas</span> <span class="hljs-attr">annotations:</span> <span class="hljs-attr">serviceaccounts.openshift.io/oauth-redirectreference.primary:</span> <span class="hljs-string">'{"kind":"OAuthRedirectReference","apiVersion":"v1","reference":{"kind":"Route","name":"nacos-proxy"}}'</span><span class="hljs-comment"># 为nacos代理创建route,此处注意annotations中的 service.alpha.openshift.io/serving-cert-secret-name,所有地方保持一致</span><span class="hljs-bullet">-</span> <span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span> <span class="hljs-attr">kind:</span> <span class="hljs-string">Route</span> <span class="hljs-attr">metadata:</span> <span class="hljs-attr">annotations:</span> <span class="hljs-attr">service.alpha.openshift.io/serving-cert-secret-name:</span> <span class="hljs-string">secret-nacos-ipaas-tls</span> <span class="hljs-attr">name:</span> <span class="hljs-string">nacos-proxy</span> <span class="hljs-attr">namespace:</span> <span class="hljs-string">${NAMESPACE}</span> <span class="hljs-attr">spec:</span> <span class="hljs-attr">to:</span> <span class="hljs-attr">kind:</span> <span class="hljs-string">Service</span> <span class="hljs-attr">name:</span> <span class="hljs-string">nacos-proxy</span> <span class="hljs-attr">tls:</span> <span class="hljs-attr">termination:</span> <span class="hljs-string">Reencrypt</span><span class="hljs-comment"># 为nacos代理创建service,此处注意annotations中的 service.alpha.openshift.io/serving-cert-secret-name,所有地方保持一致</span><span class="hljs-comment"># 此服务是指向proxy容器的端口,而非nacos的服务端口,我们需要用proxy帮我们做代理</span><span class="hljs-bullet">-</span> <span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span> <span class="hljs-attr">kind:</span> <span class="hljs-string">Service</span> <span class="hljs-attr">metadata:</span> <span class="hljs-attr">name:</span> <span class="hljs-string">nacos-proxy</span> <span class="hljs-attr">namespace:</span> <span class="hljs-string">${NAMESPACE}</span> <span class="hljs-attr">annotations:</span> <span class="hljs-attr">service.alpha.openshift.io/serving-cert-secret-name:</span> <span class="hljs-string">secret-nacos-ipaas-tls</span> <span class="hljs-attr">spec:</span> <span class="hljs-attr">ports:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">nacos</span> <span class="hljs-attr">port:</span> <span class="hljs-number">8443</span> <span class="hljs-attr">targetPort:</span> <span class="hljs-number">8443</span> <span class="hljs-attr">selector:</span> <span class="hljs-attr">app:</span> <span class="hljs-string">nacos</span><span class="hljs-comment"># nacos 需要依赖mysql,所以有很多变量需要添加</span><span class="hljs-bullet">-</span> <span class="hljs-attr">apiVersion:</span> <span class="hljs-string">apps/v1</span> <span class="hljs-attr">kind:</span> <span class="hljs-string">StatefulSet</span> <span class="hljs-attr">metadata:</span> <span class="hljs-attr">name:</span> <span class="hljs-string">nacos</span> <span class="hljs-attr">namespace:</span> <span class="hljs-string">${NAMESPACE}</span> <span class="hljs-attr">spec:</span> <span class="hljs-attr">podManagementPolicy:</span> <span class="hljs-string">OrderedReady</span> <span class="hljs-attr">replicas:</span> <span class="hljs-number">3</span> <span class="hljs-attr">revisionHistoryLimit:</span> <span class="hljs-number">3</span> <span class="hljs-attr">selector:</span> <span class="hljs-attr">matchLabels:</span> <span class="hljs-attr">app:</span> <span class="hljs-string">nacos</span> <span class="hljs-attr">serviceName:</span> <span class="hljs-string">nacos-headless</span> <span class="hljs-attr">template:</span> <span class="hljs-attr">metadata:</span> <span class="hljs-attr">annotations:</span> <span class="hljs-attr">pod.alpha.kubernetes.io/initialized:</span> <span class="hljs-string">'true'</span> <span class="hljs-attr">creationTimestamp:</span> <span class="hljs-literal">null</span> <span class="hljs-attr">labels:</span> <span class="hljs-attr">app:</span> <span class="hljs-string">nacos</span> <span class="hljs-attr">spec:</span> <span class="hljs-attr">serviceAccount:</span> <span class="hljs-string">nacos-ipaas</span> <span class="hljs-attr">serviceAccountName:</span> <span class="hljs-string">nacos-ipaas</span> <span class="hljs-attr">affinity:</span> <span class="hljs-attr">podAntiAffinity:</span> <span class="hljs-attr">requiredDuringSchedulingIgnoredDuringExecution:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">labelSelector:</span> <span class="hljs-attr">matchExpressions:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">key:</span> <span class="hljs-string">app</span> <span class="hljs-attr">operator:</span> <span class="hljs-string">In</span> <span class="hljs-attr">values:</span> <span class="hljs-bullet">-</span> <span class="hljs-string">nacos</span> <span class="hljs-attr">topologyKey:</span> <span class="hljs-string">kubernetes.io/hostname</span> <span class="hljs-comment"># 挂载上面创建的secret</span> <span class="hljs-attr">volumes:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">secret-nacos-ipaas-tls</span> <span class="hljs-attr">secret:</span> <span class="hljs-attr">secretName:</span> <span class="hljs-string">secret-nacos-ipaas-tls</span> <span class="hljs-attr">containers:</span> <span class="hljs-comment">#使用8443端口代理nacos的8848服务</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">args:</span> <span class="hljs-bullet">-</span> <span class="hljs-string">'-provider=openshift'</span> <span class="hljs-bullet">-</span> <span class="hljs-string">'-https-address=:8443'</span> <span class="hljs-bullet">-</span> <span class="hljs-string">'-http-address='</span> <span class="hljs-bullet">-</span> <span class="hljs-string">'-email-domain=*'</span> <span class="hljs-bullet">-</span> <span class="hljs-string">'-upstream=http://localhost:8848'</span> <span class="hljs-bullet">-</span> <span class="hljs-string">'-openshift-service-account=nacos-ipaas'</span> <span class="hljs-bullet">-</span> <span class="hljs-string">'-openshift-sar={"resource": "namespaces", "verb": "get"}'</span> <span class="hljs-bullet">-</span> <span class="hljs-string">>-</span> <span class="hljs-string">-openshift-delegate-urls={"/":</span> <span class="hljs-string">{"resource":</span> <span class="hljs-string">"namespaces"</span><span class="hljs-string">,</span> <span class="hljs-attr">"verb":</span> <span class="hljs-string">"get"</span><span class="hljs-string">}}</span> <span class="hljs-bullet">-</span> <span class="hljs-string">'-tls-cert=/etc/tls/private/tls.crt'</span> <span class="hljs-bullet">-</span> <span class="hljs-string">'-tls-key=/etc/tls/private/tls.key'</span> <span class="hljs-bullet">-</span> <span class="hljs-string">>-</span> <span class="hljs-string">-client-secret-file=/var/run/secrets/kubernetes.io/serviceaccount/token</span> <span class="hljs-bullet">-</span> <span class="hljs-string">'-cookie-secret=SECRET'</span> <span class="hljs-bullet">-</span> <span class="hljs-string">'-openshift-ca=/etc/pki/tls/cert.pem'</span> <span class="hljs-bullet">-</span> <span class="hljs-string">'-openshift-ca=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt'</span> <span class="hljs-bullet">-</span> <span class="hljs-string">'-skip-auth-regex=^/metrics'</span> <span class="hljs-attr">image:</span> <span class="hljs-string">'openshift/origin-oauth-proxy:4.5.0'</span> <span class="hljs-attr">imagePullPolicy:</span> <span class="hljs-string">IfNotPresent</span> <span class="hljs-attr">name:</span> <span class="hljs-string">nacos-ipaas-proxy</span> <span class="hljs-attr">ports:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">containerPort:</span> <span class="hljs-number">8443</span> <span class="hljs-attr">name:</span> <span class="hljs-string">https-8443</span> <span class="hljs-attr">protocol:</span> <span class="hljs-string">TCP</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">containerPort:</span> <span class="hljs-number">8888</span> <span class="hljs-attr">name:</span> <span class="hljs-string">http-8888</span> <span class="hljs-attr">protocol:</span> <span class="hljs-string">TCP</span> <span class="hljs-attr">resources:</span> <span class="hljs-string">{}</span> <span class="hljs-attr">terminationMessagePath:</span> <span class="hljs-string">/dev/termination-log</span> <span class="hljs-attr">terminationMessagePolicy:</span> <span class="hljs-string">File</span> <span class="hljs-attr">volumeMounts:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">mountPath:</span> <span class="hljs-string">/etc/tls/private</span> <span class="hljs-attr">name:</span> <span class="hljs-string">secret-nacos-ipaas-tls</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">env:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">NACOS_REPLICAS</span> <span class="hljs-attr">value:</span> <span class="hljs-string">'3'</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">SERVICE_NAME</span> <span class="hljs-attr">value:</span> <span class="hljs-string">nacos-headless</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">DOMAIN_NAME</span> <span class="hljs-attr">value:</span> <span class="hljs-string">cluster.local</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">POD_NAMESPACE</span> <span class="hljs-attr">valueFrom:</span> <span class="hljs-attr">fieldRef:</span> <span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span> <span class="hljs-attr">fieldPath:</span> <span class="hljs-string">metadata.namespace</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">MYSQL_SERVICE_DB_NAME</span> <span class="hljs-attr">value:</span> <span class="hljs-string">nacos_devtest</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">MYSQL_SERVICE_PORT</span> <span class="hljs-attr">value:</span> <span class="hljs-string">'3306'</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">MYSQL_SERVICE_USER</span> <span class="hljs-attr">value:</span> <span class="hljs-string">nacos</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">MYSQL_SERVICE_PASSWORD</span> <span class="hljs-attr">value:</span> <span class="hljs-string">nacos</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">NACOS_SERVER_PORT</span> <span class="hljs-attr">value:</span> <span class="hljs-string">'8848'</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">PREFER_HOST_MODE</span> <span class="hljs-attr">value:</span> <span class="hljs-string">hostname</span> <span class="hljs-attr">image:</span> <span class="hljs-string">'nacos/nacos-server:latest'</span> <span class="hljs-attr">imagePullPolicy:</span> <span class="hljs-string">Always</span> <span class="hljs-attr">name:</span> <span class="hljs-string">nacos</span> <span class="hljs-attr">ports:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">containerPort:</span> <span class="hljs-number">8848</span> <span class="hljs-attr">name:</span> <span class="hljs-string">client-port</span> <span class="hljs-attr">protocol:</span> <span class="hljs-string">TCP</span> <span class="hljs-attr">resources:</span> <span class="hljs-attr">requests:</span> <span class="hljs-attr">cpu:</span> <span class="hljs-string">500m</span> <span class="hljs-attr">memory:</span> <span class="hljs-string">2Gi</span> <span class="hljs-attr">limits:</span> <span class="hljs-attr">cpu:</span> <span class="hljs-number">2</span> <span class="hljs-attr">memory:</span> <span class="hljs-string">4Gi</span> <span class="hljs-attr">terminationMessagePath:</span> <span class="hljs-string">/dev/termination-log</span> <span class="hljs-attr">terminationMessagePolicy:</span> <span class="hljs-string">File</span> <span class="hljs-attr">volumeMounts:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">mountPath:</span> <span class="hljs-string">/home/nacos/plugins/peer-finder</span> <span class="hljs-attr">name:</span> <span class="hljs-string">plugindir</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">mountPath:</span> <span class="hljs-string">/home/nacos/data</span> <span class="hljs-attr">name:</span> <span class="hljs-string">datadir</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">mountPath:</span> <span class="hljs-string">/home/nacos/logs</span> <span class="hljs-attr">name:</span> <span class="hljs-string">logdir</span> <span class="hljs-attr">dnsPolicy:</span> <span class="hljs-string">ClusterFirst</span> <span class="hljs-attr">initContainers:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">image:</span> <span class="hljs-string">'nacos/nacos-peer-finder-plugin:1.0'</span> <span class="hljs-attr">imagePullPolicy:</span> <span class="hljs-string">Always</span> <span class="hljs-attr">name:</span> <span class="hljs-string">peer-finder-plugin-install</span> <span class="hljs-attr">resources:</span> <span class="hljs-string">{}</span> <span class="hljs-attr">terminationMessagePath:</span> <span class="hljs-string">/dev/termination-log</span> <span class="hljs-attr">terminationMessagePolicy:</span> <span class="hljs-string">File</span> <span class="hljs-attr">volumeMounts:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">mountPath:</span> <span class="hljs-string">/home/nacos/plugins/peer-finder</span> <span class="hljs-attr">name:</span> <span class="hljs-string">plugindir</span> <span class="hljs-attr">restartPolicy:</span> <span class="hljs-string">Always</span> <span class="hljs-attr">schedulerName:</span> <span class="hljs-string">default-scheduler</span> <span class="hljs-attr">securityContext:</span> <span class="hljs-string">{}</span> <span class="hljs-attr">terminationGracePeriodSeconds:</span> <span class="hljs-number">30</span> <span class="hljs-attr">updateStrategy:</span> <span class="hljs-attr">rollingUpdate:</span> <span class="hljs-attr">partition:</span> <span class="hljs-number">0</span> <span class="hljs-attr">type:</span> <span class="hljs-string">RollingUpdate</span> <span class="hljs-attr">volumeClaimTemplates:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">metadata:</span> <span class="hljs-attr">annotations:</span> <span class="hljs-attr">volume.beta.kubernetes.io/storage-class:</span> <span class="hljs-string">${STORAGECLASS}</span> <span class="hljs-attr">creationTimestamp:</span> <span class="hljs-literal">null</span> <span class="hljs-attr">name:</span> <span class="hljs-string">plugindir</span> <span class="hljs-attr">spec:</span> <span class="hljs-attr">accessModes:</span> <span class="hljs-bullet">-</span> <span class="hljs-string">ReadWriteMany</span> <span class="hljs-attr">resources:</span> <span class="hljs-attr">requests:</span> <span class="hljs-attr">storage:</span> <span class="hljs-string">5Gi</span> <span class="hljs-attr">status:</span> <span class="hljs-attr">phase:</span> <span class="hljs-string">Pending</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">metadata:</span> <span class="hljs-attr">annotations:</span> <span class="hljs-attr">volume.beta.kubernetes.io/storage-class:</span> <span class="hljs-string">${STORAGECLASS}</span> <span class="hljs-attr">creationTimestamp:</span> <span class="hljs-literal">null</span> <span class="hljs-attr">name:</span> <span class="hljs-string">datadir</span> <span class="hljs-attr">spec:</span> <span class="hljs-attr">accessModes:</span> <span class="hljs-bullet">-</span> <span class="hljs-string">ReadWriteMany</span> <span class="hljs-attr">resources:</span> <span class="hljs-attr">requests:</span> <span class="hljs-attr">storage:</span> <span class="hljs-string">5Gi</span> <span class="hljs-attr">status:</span> <span class="hljs-attr">phase:</span> <span class="hljs-string">Pending</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">metadata:</span> <span class="hljs-attr">annotations:</span> <span class="hljs-attr">volume.beta.kubernetes.io/storage-class:</span> <span class="hljs-string">${STORAGECLASS}</span> <span class="hljs-attr">creationTimestamp:</span> <span class="hljs-literal">null</span> <span class="hljs-attr">name:</span> <span class="hljs-string">logdir</span> <span class="hljs-attr">spec:</span> <span class="hljs-attr">accessModes:</span> <span class="hljs-bullet">-</span> <span class="hljs-string">ReadWriteMany</span> <span class="hljs-attr">resources:</span> <span class="hljs-attr">requests:</span> <span class="hljs-attr">storage:</span> <span class="hljs-string">5Gi</span> <span class="hljs-attr">status:</span> <span class="hljs-attr">phase:</span> <span class="hljs-string">Pending</span><span class="hljs-comment"># 创建mysql</span><span class="hljs-bullet">-</span> <span class="hljs-attr">apiVersion:</span> <span class="hljs-string">apps/v1</span> <span class="hljs-attr">kind:</span> <span class="hljs-string">Deployment</span> <span class="hljs-attr">metadata:</span> <span class="hljs-attr">namespace:</span> <span class="hljs-string">${NAMESPACE}</span> <span class="hljs-attr">annotations:</span> <span class="hljs-attr">deployment.kubernetes.io/revision:</span> <span class="hljs-string">'2'</span> <span class="hljs-attr">labels:</span> <span class="hljs-attr">name:</span> <span class="hljs-string">mysql</span> <span class="hljs-attr">name:</span> <span class="hljs-string">mysql</span> <span class="hljs-attr">spec:</span> <span class="hljs-attr">progressDeadlineSeconds:</span> <span class="hljs-number">600</span> <span class="hljs-attr">replicas:</span> <span class="hljs-number">1</span> <span class="hljs-attr">revisionHistoryLimit:</span> <span class="hljs-number">3</span> <span class="hljs-attr">selector:</span> <span class="hljs-attr">matchLabels:</span> <span class="hljs-attr">name:</span> <span class="hljs-string">mysql</span> <span class="hljs-attr">strategy:</span> <span class="hljs-attr">rollingUpdate:</span> <span class="hljs-attr">maxSurge:</span> <span class="hljs-number">25</span><span class="hljs-string">%</span> <span class="hljs-attr">maxUnavailable:</span> <span class="hljs-number">25</span><span class="hljs-string">%</span> <span class="hljs-attr">type:</span> <span class="hljs-string">RollingUpdate</span> <span class="hljs-attr">template:</span> <span class="hljs-attr">metadata:</span> <span class="hljs-attr">creationTimestamp:</span> <span class="hljs-literal">null</span> <span class="hljs-attr">labels:</span> <span class="hljs-attr">name:</span> <span class="hljs-string">mysql</span> <span class="hljs-attr">spec:</span> <span class="hljs-attr">containers:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">env:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">MYSQL_ROOT_PASSWORD</span> <span class="hljs-attr">value:</span> <span class="hljs-string">root</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">MYSQL_DATABASE</span> <span class="hljs-attr">value:</span> <span class="hljs-string">nacos_devtest</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">MYSQL_USER</span> <span class="hljs-attr">value:</span> <span class="hljs-string">nacos</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">MYSQL_PASSWORD</span> <span class="hljs-attr">value:</span> <span class="hljs-string">nacos</span> <span class="hljs-attr">image:</span> <span class="hljs-string">'nacos/nacos-mysql:5.7'</span> <span class="hljs-attr">imagePullPolicy:</span> <span class="hljs-string">IfNotPresent</span> <span class="hljs-attr">name:</span> <span class="hljs-string">mysql</span> <span class="hljs-attr">ports:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">containerPort:</span> <span class="hljs-number">3306</span> <span class="hljs-attr">protocol:</span> <span class="hljs-string">TCP</span> <span class="hljs-attr">resources:</span> <span class="hljs-string">{}</span> <span class="hljs-attr">terminationMessagePath:</span> <span class="hljs-string">/dev/termination-log</span> <span class="hljs-attr">terminationMessagePolicy:</span> <span class="hljs-string">File</span> <span class="hljs-attr">volumeMounts:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">mountPath:</span> <span class="hljs-string">/var/lib/mysql</span> <span class="hljs-attr">name:</span> <span class="hljs-string">mysql-data</span> <span class="hljs-attr">subPath:</span> <span class="hljs-string">mount</span> <span class="hljs-attr">dnsPolicy:</span> <span class="hljs-string">ClusterFirst</span> <span class="hljs-attr">restartPolicy:</span> <span class="hljs-string">Always</span> <span class="hljs-attr">schedulerName:</span> <span class="hljs-string">default-scheduler</span> <span class="hljs-attr">securityContext:</span> <span class="hljs-string">{}</span> <span class="hljs-attr">terminationGracePeriodSeconds:</span> <span class="hljs-number">30</span> <span class="hljs-attr">volumes:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">mysql-data</span> <span class="hljs-attr">persistentVolumeClaim:</span> <span class="hljs-attr">claimName:</span> <span class="hljs-string">mysql-data-pvc</span><span class="hljs-bullet">-</span> <span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span> <span class="hljs-attr">kind:</span> <span class="hljs-string">Service</span> <span class="hljs-attr">metadata:</span> <span class="hljs-attr">namespace:</span> <span class="hljs-string">${NAMESPACE}</span> <span class="hljs-attr">annotations:</span> <span class="hljs-attr">service.alpha.kubernetes.io/tolerate-unready-endpoints:</span> <span class="hljs-string">'true'</span> <span class="hljs-attr">labels:</span> <span class="hljs-attr">app:</span> <span class="hljs-string">nacos</span> <span class="hljs-attr">name:</span> <span class="hljs-string">nacos-headless</span> <span class="hljs-attr">spec:</span> <span class="hljs-attr">clusterIP:</span> <span class="hljs-string">None</span> <span class="hljs-attr">ports:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">server</span> <span class="hljs-attr">port:</span> <span class="hljs-number">8848</span> <span class="hljs-attr">protocol:</span> <span class="hljs-string">TCP</span> <span class="hljs-attr">targetPort:</span> <span class="hljs-number">8848</span> <span class="hljs-attr">selector:</span> <span class="hljs-attr">app:</span> <span class="hljs-string">nacos</span> <span class="hljs-attr">sessionAffinity:</span> <span class="hljs-string">None</span> <span class="hljs-attr">type:</span> <span class="hljs-string">ClusterIP</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">apiVersion:</span> <span class="hljs-string">rbac.authorization.k8s.io/v1</span> <span class="hljs-attr">kind:</span> <span class="hljs-string">ClusterRoleBinding</span> <span class="hljs-attr">metadata:</span> <span class="hljs-attr">labels:</span> <span class="hljs-attr">app:</span> <span class="hljs-string">nacos-ipaas</span> <span class="hljs-attr">name:</span> <span class="hljs-string">nacos-ipaas</span> <span class="hljs-attr">namespace:</span> <span class="hljs-string">${NAMESPACE}</span> <span class="hljs-attr">roleRef:</span> <span class="hljs-attr">apiGroup:</span> <span class="hljs-string">rbac.authorization.k8s.io</span> <span class="hljs-attr">kind:</span> <span class="hljs-string">ClusterRole</span> <span class="hljs-attr">name:</span> <span class="hljs-string">nacos-ipaas</span> <span class="hljs-attr">subjects:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">kind:</span> <span class="hljs-string">ServiceAccount</span> <span class="hljs-attr">namespace:</span> <span class="hljs-string">${NAMESPACE}</span> <span class="hljs-attr">name:</span> <span class="hljs-string">nacos-ipaas</span><span class="hljs-bullet">-</span> <span class="hljs-attr">apiVersion:</span> <span class="hljs-string">rbac.authorization.k8s.io/v1beta1</span> <span class="hljs-attr">kind:</span> <span class="hljs-string">ClusterRole</span> <span class="hljs-attr">metadata:</span> <span class="hljs-attr">name:</span> <span class="hljs-string">nacos-ipaas</span> <span class="hljs-attr">rules:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">apiGroups:</span> <span class="hljs-bullet">-</span> <span class="hljs-string">authorization.k8s.io</span> <span class="hljs-attr">resources:</span> <span class="hljs-bullet">-</span> <span class="hljs-string">subjectaccessreviews</span> <span class="hljs-attr">verbs:</span> <span class="hljs-bullet">-</span> <span class="hljs-string">create</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">apiGroups:</span> <span class="hljs-bullet">-</span> <span class="hljs-string">authentication.k8s.io</span> <span class="hljs-attr">resources:</span> <span class="hljs-bullet">-</span> <span class="hljs-string">tokenreviews</span> <span class="hljs-attr">verbs:</span> <span class="hljs-bullet">-</span> <span class="hljs-string">create</span><span class="hljs-bullet">-</span> <span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span> <span class="hljs-attr">kind:</span> <span class="hljs-string">Service</span> <span class="hljs-attr">metadata:</span> <span class="hljs-attr">namespace:</span> <span class="hljs-string">${NAMESPACE}</span> <span class="hljs-attr">labels:</span> <span class="hljs-attr">name:</span> <span class="hljs-string">mysql</span> <span class="hljs-attr">name:</span> <span class="hljs-string">mysql</span> <span class="hljs-attr">spec:</span> <span class="hljs-attr">ports:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">port:</span> <span class="hljs-number">3306</span> <span class="hljs-attr">protocol:</span> <span class="hljs-string">TCP</span> <span class="hljs-attr">targetPort:</span> <span class="hljs-number">3306</span> <span class="hljs-attr">selector:</span> <span class="hljs-attr">name:</span> <span class="hljs-string">mysql</span> <span class="hljs-attr">sessionAffinity:</span> <span class="hljs-string">None</span> <span class="hljs-attr">type:</span> <span class="hljs-string">ClusterIP</span> <span class="hljs-attr">status:</span> <span class="hljs-attr">loadBalancer:</span> <span class="hljs-string">{}</span><span class="hljs-bullet">-</span> <span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span> <span class="hljs-attr">kind:</span> <span class="hljs-string">PersistentVolumeClaim</span> <span class="hljs-attr">metadata:</span> <span class="hljs-attr">namespace:</span> <span class="hljs-string">${NAMESPACE}</span> <span class="hljs-attr">name:</span> <span class="hljs-string">mysql-data-pvc</span> <span class="hljs-attr">spec:</span> <span class="hljs-attr">accessModes:</span> <span class="hljs-bullet">-</span> <span class="hljs-string">ReadWriteOnce</span> <span class="hljs-attr">resources:</span> <span class="hljs-attr">requests:</span> <span class="hljs-attr">storage:</span> <span class="hljs-string">20Gi</span> <span class="hljs-attr">storageClassName:</span> <span class="hljs-string">${STORAGECLASS}</span><span class="hljs-bullet">-</span> <span class="hljs-attr">allowHostDirVolumePlugin:</span> <span class="hljs-literal">true</span> <span class="hljs-attr">allowHostIPC:</span> <span class="hljs-literal">false</span> <span class="hljs-attr">allowHostNetwork:</span> <span class="hljs-literal">false</span> <span class="hljs-attr">allowHostPID:</span> <span class="hljs-literal">false</span> <span class="hljs-attr">allowHostPorts:</span> <span class="hljs-literal">false</span> <span class="hljs-attr">allowPrivilegeEscalation:</span> <span class="hljs-literal">true</span> <span class="hljs-attr">allowPrivilegedContainer:</span> <span class="hljs-literal">true</span> <span class="hljs-attr">allowedCapabilities:</span> <span class="hljs-literal">null</span> <span class="hljs-attr">apiVersion:</span> <span class="hljs-string">security.openshift.io/v1</span> <span class="hljs-attr">defaultAddCapabilities:</span> <span class="hljs-literal">null</span> <span class="hljs-attr">fsGroup:</span> <span class="hljs-attr">type:</span> <span class="hljs-string">RunAsAny</span> <span class="hljs-attr">groups:</span> <span class="hljs-string">[]</span> <span class="hljs-attr">kind:</span> <span class="hljs-string">SecurityContextConstraints</span> <span class="hljs-attr">metadata:</span> <span class="hljs-attr">name:</span> <span class="hljs-string">nacos-ipaas</span> <span class="hljs-attr">priority:</span> <span class="hljs-literal">null</span> <span class="hljs-attr">readOnlyRootFilesystem:</span> <span class="hljs-literal">false</span> <span class="hljs-attr">requiredDropCapabilities:</span> <span class="hljs-literal">null</span> <span class="hljs-attr">runAsUser:</span> <span class="hljs-attr">type:</span> <span class="hljs-string">RunAsAny</span> <span class="hljs-attr">seLinuxContext:</span> <span class="hljs-attr">type:</span> <span class="hljs-string">RunAsAny</span> <span class="hljs-attr">supplementalGroups:</span> <span class="hljs-attr">type:</span> <span class="hljs-string">RunAsAny</span> <span class="hljs-attr">users:</span> <span class="hljs-bullet">-</span> <span class="hljs-string">system:serviceaccount:midware:nacos-ipaas</span> <span class="hljs-attr">volumes:</span> <span class="hljs-bullet">-</span> <span class="hljs-string">'*'</span></code></pre></div><p>使用`oc create -f nacos-template.yaml -e NAMESPACE=midware -e … 因为nacos需要特权运行,所以创建相关的资源较多。<br>主要是创建scc、serviceaccount等权限资源。</p><h3 id="访问流程"><a href="#访问流程" class="headerlink" title="访问流程"></a>访问流程</h3><p>意如其名,通过将proxy以sidecar的形式提供一个安全代理的服务,proxy可以使用okd拥有的账号登录,并且基于原有的rbac权限,用简单的方式便可以重复使用原有的账号。<br>大致访问流程如下:</p><p>浏览器–>route–>proxy service–>proxy container–>app container</p>]]></content>
</entry>
<entry>
<title>prometheus operator 接入钉钉</title>
<link href="/post/9f616579.html"/>
<url>/post/9f616579.html</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>为了及时收到告警消息,考虑了几种方案,最终选择钉钉。方案有几种:</p><ul><li>电话</li><li>短信</li><li>企业微信</li><li>钉钉</li><li>slack</li></ul><p>电话、短信需要对接相关api,并且需要收费。企业微信需要认证,手续麻烦了。slack国内不方便,钉钉对接也比较简单,有比较现成的解决方案。我们运维团队人不多。已经足够了。作为即时IM通信软件,时效性也能得到保障。</p><h2 id="安装步骤"><a href="#安装步骤" class="headerlink" title="安装步骤"></a>安装步骤</h2><h3 id="安装钉钉"><a href="#安装钉钉" class="headerlink" title="安装钉钉"></a>安装钉钉</h3><p>安装<a href="https://page.dingtalk.com/wow/dingtalk/act/download?spm=a213l2.13146415.7065056597.9.7f1518e6fxwEeD" target="_blank" rel="noopener">钉钉</a>,建立钉钉群,添加自定义机器人,通过限制ip地址请求机器人,填写公网的ip地址,改地址为SNAT的公网ip,得到一个类似于这样的:<code>https://oapi.dingtalk.com/robot/send?access_token=c7bxxxxa42f680cxxxxxc3032</code> 地址。</p><h3 id="安装dingtalk的webhook服务"><a href="#安装dingtalk的webhook服务" class="headerlink" title="安装dingtalk的webhook服务"></a>安装dingtalk的webhook服务</h3><p>由于钉钉的webhook是有格式的,所以我们采用<code>timonwong/prometheus-webhook-dingtalk:v1.4.0</code>这个webhook服务来接受alert manager的告警信息,然后将告警信息转换成钉钉所能识别的webhook的格式。以下为openshift的安装模板:</p><div class="hljs"><pre><code class="hljs yml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span><span class="hljs-attr">kind:</span> <span class="hljs-string">Template</span><span class="hljs-attr">metadata:</span><span class="hljs-attr">name:</span> <span class="hljs-string">dingtalk-template</span><span class="hljs-attr">annotations:</span> <span class="hljs-attr">description:</span> <span class="hljs-string">dingtalk-alert</span><span class="hljs-attr">parameters:</span><span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">NAMESPACE</span> <span class="hljs-attr">value:</span> <span class="hljs-string">monitoring</span><span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">DING_TALK_URL</span> <span class="hljs-attr">value:</span> <span class="hljs-string">https://oapi.dingtalk.com/robot/send?access_token=c7b19xxxf5370270b10d255934c3032</span><span class="hljs-attr">objects:</span><span class="hljs-bullet">-</span> <span class="hljs-attr">apiVersion:</span> <span class="hljs-string">apps/v1</span> <span class="hljs-attr">kind:</span> <span class="hljs-string">Deployment</span> <span class="hljs-attr">metadata:</span> <span class="hljs-attr">namespace:</span> <span class="hljs-string">${NAMESPACE}</span> <span class="hljs-attr">annotations:</span> <span class="hljs-attr">deployment.kubernetes.io/revision:</span> <span class="hljs-string">'1'</span> <span class="hljs-attr">labels:</span> <span class="hljs-attr">app:</span> <span class="hljs-string">webhook-dingtalk</span> <span class="hljs-attr">prometheus:</span> <span class="hljs-string">ipaas</span> <span class="hljs-attr">name:</span> <span class="hljs-string">webhook-dingtalk</span> <span class="hljs-attr">spec:</span> <span class="hljs-attr">progressDeadlineSeconds:</span> <span class="hljs-number">600</span> <span class="hljs-attr">replicas:</span> <span class="hljs-number">1</span> <span class="hljs-attr">revisionHistoryLimit:</span> <span class="hljs-number">3</span> <span class="hljs-attr">selector:</span> <span class="hljs-attr">matchLabels:</span> <span class="hljs-attr">app:</span> <span class="hljs-string">webhook-dingtalk</span> <span class="hljs-attr">strategy:</span> <span class="hljs-attr">rollingUpdate:</span> <span class="hljs-attr">maxSurge:</span> <span class="hljs-number">25</span><span class="hljs-string">%</span> <span class="hljs-attr">maxUnavailable:</span> <span class="hljs-number">25</span><span class="hljs-string">%</span> <span class="hljs-attr">type:</span> <span class="hljs-string">RollingUpdate</span> <span class="hljs-attr">template:</span> <span class="hljs-attr">metadata:</span> <span class="hljs-attr">creationTimestamp:</span> <span class="hljs-literal">null</span> <span class="hljs-attr">labels:</span> <span class="hljs-attr">app:</span> <span class="hljs-string">webhook-dingtalk</span> <span class="hljs-attr">prometheus:</span> <span class="hljs-string">ipaas</span> <span class="hljs-attr">spec:</span> <span class="hljs-attr">containers:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">args:</span> <span class="hljs-bullet">-</span> <span class="hljs-string">'--web.listen-address=0.0.0.0:8060'</span> <span class="hljs-bullet">-</span> <span class="hljs-string">'--web.enable-ui'</span> <span class="hljs-bullet">-</span> <span class="hljs-string">'--config.file=/etc/prometheus-webhook-dingtalk/config.yml'</span> <span class="hljs-attr">env:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">TZ</span> <span class="hljs-attr">value:</span> <span class="hljs-string">CST-8</span> <span class="hljs-attr">image:</span> <span class="hljs-string">'timonwong/prometheus-webhook-dingtalk:v1.4.0'</span> <span class="hljs-attr">imagePullPolicy:</span> <span class="hljs-string">IfNotPresent</span> <span class="hljs-attr">livenessProbe:</span> <span class="hljs-attr">failureThreshold:</span> <span class="hljs-number">3</span> <span class="hljs-attr">initialDelaySeconds:</span> <span class="hljs-number">30</span> <span class="hljs-attr">periodSeconds:</span> <span class="hljs-number">10</span> <span class="hljs-attr">successThreshold:</span> <span class="hljs-number">1</span> <span class="hljs-attr">tcpSocket:</span> <span class="hljs-attr">port:</span> <span class="hljs-number">8060</span> <span class="hljs-attr">timeoutSeconds:</span> <span class="hljs-number">1</span> <span class="hljs-attr">name:</span> <span class="hljs-string">webhook-dingtalk</span> <span class="hljs-attr">ports:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">containerPort:</span> <span class="hljs-number">8060</span> <span class="hljs-attr">name:</span> <span class="hljs-string">tcp-8060</span> <span class="hljs-attr">protocol:</span> <span class="hljs-string">TCP</span> <span class="hljs-attr">readinessProbe:</span> <span class="hljs-attr">failureThreshold:</span> <span class="hljs-number">3</span> <span class="hljs-attr">initialDelaySeconds:</span> <span class="hljs-number">30</span> <span class="hljs-attr">periodSeconds:</span> <span class="hljs-number">10</span> <span class="hljs-attr">successThreshold:</span> <span class="hljs-number">1</span> <span class="hljs-attr">tcpSocket:</span> <span class="hljs-attr">port:</span> <span class="hljs-number">8060</span> <span class="hljs-attr">timeoutSeconds:</span> <span class="hljs-number">1</span> <span class="hljs-attr">resources:</span> <span class="hljs-attr">limits:</span> <span class="hljs-attr">cpu:</span> <span class="hljs-string">500m</span> <span class="hljs-attr">memory:</span> <span class="hljs-string">500Mi</span> <span class="hljs-attr">requests:</span> <span class="hljs-attr">cpu:</span> <span class="hljs-string">100m</span> <span class="hljs-attr">memory:</span> <span class="hljs-string">100Mi</span> <span class="hljs-attr">terminationMessagePath:</span> <span class="hljs-string">/dev/termination-log</span> <span class="hljs-attr">terminationMessagePolicy:</span> <span class="hljs-string">File</span> <span class="hljs-attr">volumeMounts:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">mountPath:</span> <span class="hljs-string">/etc/prometheus-webhook-dingtalk/config.yml</span> <span class="hljs-attr">name:</span> <span class="hljs-string">webhook-dingtalk</span> <span class="hljs-attr">subPath:</span> <span class="hljs-string">config.yml</span> <span class="hljs-attr">dnsPolicy:</span> <span class="hljs-string">ClusterFirst</span> <span class="hljs-attr">restartPolicy:</span> <span class="hljs-string">Always</span> <span class="hljs-attr">schedulerName:</span> <span class="hljs-string">default-scheduler</span> <span class="hljs-attr">securityContext:</span> <span class="hljs-string">{}</span> <span class="hljs-attr">terminationGracePeriodSeconds:</span> <span class="hljs-number">30</span> <span class="hljs-attr">volumes:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">configMap:</span> <span class="hljs-attr">defaultMode:</span> <span class="hljs-number">420</span> <span class="hljs-attr">name:</span> <span class="hljs-string">webhook-dingtalk</span> <span class="hljs-attr">name:</span> <span class="hljs-string">webhook-dingtalk</span><span class="hljs-bullet">-</span> <span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span> <span class="hljs-attr">data:</span> <span class="hljs-attr">config.yml:</span> <span class="hljs-string">|</span> <span class="hljs-comment">## Request timeout</span> <span class="hljs-comment"># timeout: 5s</span> <span class="hljs-comment">## Customizable templates path</span> <span class="hljs-comment"># templates:</span> <span class="hljs-comment"># - contrib/templates/legacy/template.tmpl</span> <span class="hljs-comment">## You can also override default template using `default_message`</span> <span class="hljs-comment">## The following example to use the 'legacy' template from v0.3.0</span> <span class="hljs-comment"># default_message:</span> <span class="hljs-comment"># title: '{{ template "legacy.title" . }}'</span> <span class="hljs-comment"># text: '{{ template "legacy.content" . }}'</span> <span class="hljs-comment">## Targets, previously was known as "profiles"</span> <span class="hljs-attr">targets:</span> <span class="hljs-attr">webhook1:</span> <span class="hljs-attr">url:</span> <span class="hljs-string">${DING_TALK_URL}</span> <span class="hljs-comment"># secret for signature</span> <span class="hljs-attr">secret:</span> <span class="hljs-string">SEC000000000000000000000</span> <span class="hljs-attr">webhook2:</span> <span class="hljs-attr">url:</span> <span class="hljs-string">${DING_TALK_URL}</span> <span class="hljs-attr">webhook_legacy:</span> <span class="hljs-attr">url:</span> <span class="hljs-string">${DING_TALK_URL}</span> <span class="hljs-comment"># Customize template content</span> <span class="hljs-attr">message:</span> <span class="hljs-comment"># Use legacy template</span> <span class="hljs-attr">title:</span> <span class="hljs-string">'<span class="hljs-template-variable">{{ template "legacy.title" . }}</span>'</span> <span class="hljs-attr">text:</span> <span class="hljs-string">'<span class="hljs-template-variable">{{ template "legacy.content" . }}</span>'</span> <span class="hljs-attr">webhook_mention_all:</span> <span class="hljs-attr">url:</span> <span class="hljs-string">${DING_TALK_URL}</span> <span class="hljs-attr">mention:</span> <span class="hljs-attr">all:</span> <span class="hljs-literal">true</span> <span class="hljs-attr">webhook_mention_users:</span> <span class="hljs-attr">url:</span> <span class="hljs-string">${DING_TALK_URL}</span> <span class="hljs-attr">mention:</span> <span class="hljs-attr">mobiles:</span> <span class="hljs-string">['133xxxxx195']</span> <span class="hljs-attr">kind:</span> <span class="hljs-string">ConfigMap</span> <span class="hljs-attr">metadata:</span> <span class="hljs-attr">namespace:</span> <span class="hljs-string">${NAMESPACE}</span> <span class="hljs-attr">labels:</span> <span class="hljs-attr">app:</span> <span class="hljs-string">webhook-dingtalk</span> <span class="hljs-attr">name:</span> <span class="hljs-string">webhook-dingtalk</span></code></pre></div><h3 id="修改alertmanager-yaml"><a href="#修改alertmanager-yaml" class="headerlink" title="修改alertmanager.yaml"></a>修改alertmanager.yaml</h3><p>经过以上的步骤就已经搭建好webhook服务了,接下来需要把aler manager中的receivers和route配置一下即可,参考:</p><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-attr">global:</span> <span class="hljs-attr">resolve_timeout:</span> <span class="hljs-string">5m</span> <span class="hljs-attr">smtp_require_tls:</span> <span class="hljs-literal">false</span><span class="hljs-attr">route:</span> <span class="hljs-attr">group_wait:</span> <span class="hljs-string">30s</span> <span class="hljs-attr">group_interval:</span> <span class="hljs-string">5m</span> <span class="hljs-attr">repeat_interval:</span> <span class="hljs-string">30m</span> <span class="hljs-attr">receiver:</span> <span class="hljs-string">dingtalk</span><span class="hljs-attr">receivers:</span><span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">dingtalk</span> <span class="hljs-attr">webhook_configs:</span> <span class="hljs-bullet">-</span> <span class="hljs-attr">send_resolved:</span> <span class="hljs-literal">true</span> <span class="hljs-attr">url:</span> <span class="hljs-string">http://webhook-dingtalk.monitoring.svc:8060/dingtalk/webhook1/send</span></code></pre></div>]]></content>
<categories>
<category>监控</category>
</categories>
<tags>
<tag>prometheus</tag>
<tag>prometheus-operator</tag>
<tag>钉钉</tag>
<tag>告警</tag>
</tags>
</entry>
<entry>
<title>使用最轻量的方式进行blog集成</title>
<link href="/post/cab10e7.html"/>
<url>/post/cab10e7.html</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>因为使用挂载blog源代码的虚拟机被我整坏了,还好是有备份的。不过还是需要整一个持续集成的blog流水线,<br>但是又不想使用原来Jenkins+Gitlab方式,觉得太笨重了,又调查了一番想着使用gogs+webhook,然后又是一阵百度谷歌之旅,还是没有找到想要的webhook,<br>于是决定自己写一个钩子,需求:能接受http请求,执行shell脚本,于是20行左右js代码就写完了。</p><h2 id="webhook"><a href="#webhook" class="headerlink" title="webhook"></a>webhook</h2><p>使用nodejs编写hook.js非常简单<br><div class="hljs"><pre><code class="hljs javascript"><span class="hljs-keyword">const</span> http =<span class="hljs-built_in">require</span>(<span class="hljs-string">'http'</span>);<span class="hljs-keyword">const</span> url = <span class="hljs-built_in">require</span>(<span class="hljs-string">"url"</span>);<span class="hljs-keyword">const</span> querystring = <span class="hljs-built_in">require</span>(<span class="hljs-string">"querystring"</span>);<span class="hljs-keyword">const</span> exec = <span class="hljs-built_in">require</span>(<span class="hljs-string">'child_process'</span>).execSync;<span class="hljs-keyword">const</span> requestHandler = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">req, res</span>) </span>{<span class="hljs-keyword">const</span> arg = querystring.parse(url.parse(req.url).query); <span class="hljs-keyword">if</span> ( arg.secret !== <span class="hljs-string">'4YQcC6qy0MIiYjk'</span>) { <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'密钥不对'</span>,arg.secret); res.statusCode = <span class="hljs-number">500</span>; res.end(<span class="hljs-string">'密钥不对 error...'</span>); <span class="hljs-keyword">return</span> } <span class="hljs-keyword">try</span> { <span class="hljs-comment">// do something here,certification authorization etc</span> <span class="hljs-comment">// passing parameters ,just like:exec('bash ~/blog-ci.sh'+' '+'sss') or use execFile()</span> exec(<span class="hljs-string">'bash ~/blog-ci.sh'</span>); res.end(<span class="hljs-string">'ok'</span>); } <span class="hljs-keyword">catch</span> (e) { <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'script exec error'</span>, e); res.statusCode = <span class="hljs-number">500</span>; res.end(<span class="hljs-string">'server error...'</span>); }};<span class="hljs-comment">//create server</span><span class="hljs-keyword">const</span> web = http.createServer(requestHandler);web.listen(<span class="hljs-number">34512</span>, <span class="hljs-string">'0.0.0.0'</span>);<span class="hljs-built_in">console</span>.log(<span class="hljs-string">'http running on http://0.0.0.0:xxx'</span>);</code></pre></div></p><div class="hljs"><pre><code class="hljs go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> { router := gin.Default() <span class="hljs-comment">// github webhook method is post</span>router.POST(<span class="hljs-string">"/xxx"</span>, webhook)_ = router.Run(<span class="hljs-string">":1231"</span>)}<span class="hljs-keyword">type</span> Resp <span class="hljs-keyword">struct</span> {Msg <span class="hljs-keyword">string</span> <span class="hljs-string">`json:"msg"`</span>Data <span class="hljs-keyword">string</span> <span class="hljs-string">`json:"data"`</span>Code <span class="hljs-keyword">int</span> <span class="hljs-string">`json:"code"`</span>}<span class="hljs-keyword">const</span> SECRET = <span class="hljs-string">"xxxx"</span><span class="hljs-keyword">const</span> HOST = <span class="hljs-string">"xxx.xx:1231"</span><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">webhook</span><span class="hljs-params">(context *gin.Context)</span></span> {host := context.Request.Host<span class="hljs-keyword">if</span> host != HOST {context.JSON(<span class="hljs-number">400</span>, Resp{<span class="hljs-string">"fail"</span>, <span class="hljs-string">"host not right"</span>, <span class="hljs-number">400</span>})<span class="hljs-keyword">return</span>}secret := context.Query(<span class="hljs-string">"xxx"</span>)<span class="hljs-keyword">if</span> secret != SECRET {context.JSON(<span class="hljs-number">400</span>, Resp{<span class="hljs-string">"fail"</span>, <span class="hljs-string">"badrequest"</span>, <span class="hljs-number">400</span>})<span class="hljs-keyword">return</span>}cmd := exec.Command(<span class="hljs-string">"/bin/bash"</span>, <span class="hljs-string">"-c"</span>, <span class="hljs-string">"./blog-ci.sh"</span>)<span class="hljs-keyword">if</span> out, err := cmd.Output(); err != <span class="hljs-literal">nil</span> {log.Println(out, err)context.JSON(<span class="hljs-number">500</span>, Resp{<span class="hljs-string">"fail"</span>, <span class="hljs-string">"server error"</span>, <span class="hljs-number">500</span>})<span class="hljs-keyword">return</span>}context.JSON(<span class="hljs-number">200</span>, Resp{<span class="hljs-string">"ok"</span>, <span class="hljs-string">"success"</span>, <span class="hljs-number">200</span>})}</code></pre></div><h2 id="使用PM2管理"><a href="#使用PM2管理" class="headerlink" title="使用PM2管理"></a>使用PM2管理</h2><p>PM2是node进程管理工具,可以利用它来简化很多node应用管理的繁琐任务,如性能监控、自动重启、负载均衡等,而且使用非常简单.<br>全局安装,简直不能更简单<code>npm install -g pm2</code>,安装完毕后自动创建目录</p><ul><li>$HOME/.pm2 will contain all PM2 related files</li><li>$HOME/.pm2/logs will contain all applications logs</li><li>$HOME/.pm2/pids will contain all applications pids</li><li>$HOME/.pm2/pm2.log PM2 logs</li><li>$HOME/.pm2/pm2.pid PM2 pid</li><li>$HOME/.pm2/rpc.sock Socket file for remote commands</li><li>$HOME/.pm2/pub.sock Socket file for publishable events</li><li>$HOME/.pm2/conf.js PM2 Configuration</li></ul><p>启动webhook:<code>pm2 start app.js --watch</code> ,可通过<code>pm2 save && pm2 startup</code> 开启开机自启服务</p><p>查看我们的hook.js,使用<code>pm2 list</code><br><img src="/images/PM.jpeg" alt="Pm2 list"></p><p>这么短的代码使用45MB的内存,还是有点出乎意料的,不过已经满足了我需求,可以通过访问ip+port?secret=xxx的形式执行脚本了</p>]]></content>
<categories>
<category>blog</category>
</categories>
<tags>
<tag>blog</tag>
<tag>CI/CD</tag>
</tags>
</entry>
<entry>
<title>专业名词记录</title>
<link href="/post/37a9f748.html"/>
<url>/post/37a9f748.html</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>工作之后,发现有不少名词重复的,搞得我都不知道哪个是哪个了,这里记一下常用的名词以及相关术语,避免识词而不知其意,蛮尴尬的!</p><h2 id="术语篇"><a href="#术语篇" class="headerlink" title="术语篇"></a>术语篇</h2><h3 id="SOA"><a href="#SOA" class="headerlink" title="SOA"></a>SOA</h3><p>面向服务的体系结构(英语:service-oriented architecture)并不特指一种技术,而是一种分布式运算的软件设计方法。软件的部分组件(调用者),可以透过网络上的通用协议调用另一个应用软件组件运行、运作,让调用者获得服务。SOA原则上采用开放标准、与软件资源进行交互并采用表示的标准方式。因此应能跨越厂商、产品与技术。一项服务应视为一个独立的功能单元,可以远程访问并独立运行与更新,例如在线查询信用卡账单。</p><p><strong>SOA中的一项服务应有以下四个特性:</strong></p><ul><li>针对某特定要求的输出,该服务就是运作一项商业逻辑</li><li>具有完备的特性(self-contained)</li><li>消费者并不需要了解此服务的运作过程</li><li>可能由底层其他服务组成</li></ul><p>以下指导原则是开发,维护和使用SOA的基本原则:</p><ul><li>可重复使用、粒度、模块性、可组合型、对象化原件、构件化以及具交互操作性</li><li>匹配开放标准(通用的或行业的)</li><li>服务的识别和分类,提供和发布,监控和跟踪。</li></ul><p><strong>下面是一些特定的体系架构原则:</strong></p><ul><li>服务封装</li><li>服务松耦合(Loosely Coupled) - 服务之间的关系最小化,只是互相知道。</li><li>服务契约 - 服务按照服务描述文档所定义的服务契约行事。</li><li>服务抽象 - 除了服务契约中所描述的内容,服务将对外部隐藏逻辑。</li><li>服务的重用性 - 将逻辑分布在不同的服务中,以提高服务的重用性。</li><li>服务的可组合性 - 一组服务可以协调工作并组合起来形成一个组合服务。</li><li>服务自治 – 服务对所封装的逻辑具有控制权</li><li>服务无状态 – 服务将一个活动所需保存的信息最小化。</li><li>服务的可被发现性 – 服务需要对外部提供描述信息,这样可以通过现有的发现机制发现并访问这些服务。</li></ul><p>除此以外,在定义一个SOA实现时,还需要考虑以下因素:</p><ul><li>生命周期管理</li><li>有效使用系统资源</li><li>服务成熟度和性能</li></ul><h3 id="ACID"><a href="#ACID" class="headerlink" title="ACID"></a>ACID</h3><p>我们知道事务有4个非常重要的特性,即我们常说的(ACID)</p><p><strong>Atomicity(原子性):</strong> 是说事务是一个不可分割的整体,所有操作要么全做,要么全不做;只要事务中有一个操作出错,回滚到事务开始前的状态的话,那么之前已经执行的所有操作都是无效的,都应该回滚到开始前的状态。</p><p><strong>Consistency(一致性):</strong> 是说事务执行前后,数据从一个状态到另一个状态必须是一致的,比如A向B转账(A、B的总金额就是一个一致性状态),不可能出现A扣了钱,B却没收到的情况发生。</p><p><strong>Isolation(隔离性):</strong> 多个并发事务之间相互隔离,不能互相干扰。关于事务的隔离性,可能不是特别好理解,这里的并发事务是指两个事务操作了同一份数据的情况;而对于并发事务操作同一份数据的隔离性问题,则是要求不能出现脏读、幻读的情况,即事务A不能读取事务B还没有提交的数据,或者在事务A读取数据进行更新操作时,不允许事务B率先更新掉这条数据。而为了解决这个问题,常用的手段就是加锁了,对于数据库来说就是通过数据库的相关锁机制来保证。</p><p><strong>Durablity(持久性):</strong> 事务完成后,对数据库的更改是永久保存的,不能回滚。</p><h3 id="分布式事务"><a href="#分布式事务" class="headerlink" title="分布式事务"></a>分布式事务</h3><h4 id="TCC"><a href="#TCC" class="headerlink" title="TCC"></a>TCC</h4><h4 id="2PC"><a href="#2PC" class="headerlink" title="2PC"></a>2PC</h4>]]></content>
<categories>
<category>术语</category>
</categories>
<tags>
<tag>专业名词</tag>
<tag>计算机</tag>
</tags>
</entry>
<entry>
<title>对常用的压缩加密文件进行破解</title>
<link href="/post/a22360f1.html"/>
<url>/post/a22360f1.html</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>在工作上遇到一个客户的前供应商导出的文件是加密的,然后需要对其进行解密.我当时就懵逼了…</p><p>用notepad++打开看到是7z格式的加密文件,我感觉就可能破解不出来了,7zip使用的是AES256加密算法(Advanced Encryption Standard)高级加密标准,截至2006年,针对AES唯一的成功攻击是旁道攻击或社会工程学攻击.两者都是非暴力破解方式,难度系数较高,简而言之,前者就是绕开密码攻击密码系统或者其他漏洞,而后者就是分析人类习惯,从人的社会习惯等除法寻求密码.</p><p>虽然遇到这么一个坑事,但还是尽力去做的,就当时提升自己吧!所以就有了这一篇博文,mark一下我在解决(程序还在跑)的过程.</p><h2 id="准备破解"><a href="#准备破解" class="headerlink" title="准备破解"></a>准备破解</h2><p>解决这样的问题,对于我来说目前只能通过暴力的方式破解,社会工程学破解就是使用人类常用密码进行字典枚举,第二个方式就是限定字符的排列组合进行穷举(不推荐,效率低,浪费电)</p><p>我在破解过程中收集了几款软件,这里分别记录下相关的用法和分析下优缺点.</p><ol><li>RARcrack</li><li>7zcracker</li><li>Hashcat</li><li>自己写脚本</li><li>ARCHPR</li></ol><h2 id="使用方法"><a href="#使用方法" class="headerlink" title="使用方法"></a>使用方法</h2><h3 id="RARcrack"><a href="#RARcrack" class="headerlink" title="RARcrack"></a>RARcrack</h3><p>RARCrack是简单穷举方式暴力破解的,可以编写xml文件使得破解范围变小,可以限定使用的字符以及密码长度,它是linux环境的下软件所以需要在linux环境下运行,刚好我手头有一台服务器就用来跑这个了,超过6位数的密码,在没限定条件下几乎无法破解.(26+26+10)的六次方有56,800,235,584排列组合, 所以在不推荐用于没有任何密码提示的情况下使用,如字符长度和字符限制以及字符占位等等.</p><p>简单说下如何安装使用吧!<br>安装环境:</p><div class="hljs"><pre><code class="hljs shell">Software requirements:<span class="hljs-meta">></span><span class="bash"> glibc 2.4</span>any POSIX compatible operating system [sorry Window$ isn't]pthreadslibxml2and finally: 7zip, unrar, unzip</code></pre></div><p>安装步骤:</p><div class="hljs"><pre><code class="hljs shell"><span class="hljs-meta">$</span><span class="bash"> tar -xjf rarcrack-VERSION.tar.bz2</span><span class="hljs-meta">$</span><span class="bash"> <span class="hljs-built_in">cd</span> rarcrack-VERSION</span>// you need gcc or any C compiler (edit Makefile CC=YOUR_C_COMPILER)<span class="hljs-meta">$</span><span class="bash"> make</span>// you must be root in next step:<span class="hljs-meta">$</span><span class="bash"> make install</span></code></pre></div><p>使用命令</p><div class="hljs"><pre><code class="hljs shell">rarcrack your_encrypted_archive.ext [--threads thread_num] [--type rar|zip|7z]</code></pre></div><p>如: <code>rarcrack pojie.7z --threads 10 --type 7z</code></p><h4 id="RARcrack进阶"><a href="#RARcrack进阶" class="headerlink" title="RARcrack进阶"></a>RARcrack进阶</h4><div class="hljs"><pre><code class="hljs xml"><span class="hljs-meta"><?xml version="1.0" encoding="UTF-8"?></span><span class="hljs-tag"><<span class="hljs-name">rarcrack</span>></span><span class="hljs-tag"><<span class="hljs-name">abc</span>></span>0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ<span class="hljs-tag"></<span class="hljs-name">abc</span>></span> <span class="hljs-tag"><<span class="hljs-name">current</span>></span>uU<span class="hljs-tag"></<span class="hljs-name">current</span>></span> <span class="hljs-tag"><<span class="hljs-name">good_password</span>></span><span class="hljs-tag"></<span class="hljs-name">good_password</span>></span><span class="hljs-tag"></<span class="hljs-name">rarcrack</span>></span></code></pre></div><p>编写自定义的xml文件可以缩减密码范围,加快破解速度,标签abc是指定破解密码的字符范围,current标签是指当前密码前缀或者使用0进行占位表示密码由多少位数开始.此外xml文件名相同放在破解目录下,如破解的是test.7z那么xml文件名就是test.7z.xml</p><p><strong>优点</strong>: 就是简单,易上手,支持多线程,当你记得部分密码或者记得密码长度时是一个不错的工具.</p><p><strong>缺点</strong>: 也是很明显,就是比较笨,不支持配置字典进行破解,在没有任何配置情况下就是蛮力破解,浪费电呀!</p><p>推荐指数: ☆☆☆</p><h3 id="7zcracker"><a href="#7zcracker" class="headerlink" title="7zcracker"></a>7zcracker</h3><p>7zcracker是内置了简单的字典进行破解的,当超出字典就会采用蛮力破解,它是windows下的软件,使用cmd命令行启动.</p><p>从网上下载7zcracker,进行解压,进入目录运行7zcracker.exe 输入需要破解的文件路径.等待即可 .</p><p><strong>优点</strong>: 非常简单,不需要任何知识即可上手,可破解比较简单的密码.</p><p><strong>缺点</strong>: 不支持多线程,不支持字典配置,不支持密码配置,是简单带来的坏处.</p><p>推荐指数: ☆☆☆</p><h3 id="Hashcat"><a href="#Hashcat" class="headerlink" title="Hashcat"></a>Hashcat</h3><p>hashcat号称世界上最快的密码破解,世界上第一个和唯一的基于GPGPU规则引擎,免费多GPU(高达128个GPU),多哈希,多操作系统(Linux和Windows本地二进制文件),多平台(OpenCL和CUDA支持),多算法,资源利用率低,基于字典攻击,支持分布式破解等等,目前最新版本为4.01,<a href="https://hashcat.net/files/hashcat-4.1.0.7z" target="_blank" rel="noopener">下载地址</a>,hashcat目前支持各类公开算法高达247类,市面上面公开的密码加密算法基本都支持!hashcat系列软件在硬件上支持使用CPU、NVIDIA GPU、ATI GPU来进行密码破解。在操作系统上支持Windows、Linux平台,并且需要安装官方指定版本的显卡驱动程序,如果驱动程序版本不对,可能导致程序无法运行。NVIDIA users GPU破解驱动需要ForceWare 331.67以及<a href="http://www.geforce.cn/drivers" target="_blank" rel="noopener">更高版本</a>,AMD 用户则需要Catalyst 14.9以及更高版本,可以通过Catalyst 自动侦测和下载检测工具来检测系统应该下载那个版本,<a href="http://support.amd.com/en-us/download/auto-detect-tool" target="_blank" rel="noopener">下载地址</a>,选择合适的版本安装即可。其官方github <a href="https://github.com/hashcat/hashcat" target="_blank" rel="noopener">网站地址</a></p><p>因为我是破解7z需要下载7zhash生成工具<a href="https://github.com/philsmd/7z2hashcat/releases" target="_blank" rel="noopener">philsmd/7z2hashcat</a></p><div class="hljs"><pre><code class="hljs shell">Sample:<span class="hljs-meta">$</span><span class="bash"> 7z2hashcat64-1.2.exe <span class="hljs-string">"file.7z"</span> > hash.txt</span><span class="hljs-meta">$</span><span class="bash"> hashcat64 -h</span><span class="hljs-meta">$</span><span class="bash"> hashcat64 -m 11600 -a 0 hash.txt wordlist.txt -r rules/best64.rule</span></code></pre></div><p>首先生成7z文件hash,然后执行破解程序,wordlist.txt为字典,使用hashcat需要注意系统温度,避免高温烧坏电脑.</p><p><strong>优点</strong>: 支持复杂的密码破解,支持gpu加速,破解效率高</p><p><strong>缺点</strong>: 工具使用复杂,需要较强的专业知识,是专业的hacker工具</p><p>更多请参考:<a href="https://hashcat.net/wiki/" target="_blank" rel="noopener">hashcat wiki</a></p><p>推荐指数: ☆☆☆☆</p><h3 id="自己编写脚本"><a href="#自己编写脚本" class="headerlink" title="自己编写脚本"></a>自己编写脚本</h3><p>这个灵活性比较高,看个人code的能力,一般也是利用相关字典进行简单的破解,主要定制化程度高.</p><p>这里我贴一段Python的代码:</p><div class="hljs"><pre><code class="hljs Python"><span class="hljs-comment">#!/usr/bin/env python2</span><span class="hljs-keyword">import</span> os,sysf = open(sys.argv[<span class="hljs-number">1</span>],<span class="hljs-string">'r'</span>)lines = f.read().splitlines()<span class="hljs-keyword">for</span> line <span class="hljs-keyword">in</span> lines: x = os.system(<span class="hljs-string">'7z e {0} -p{1}'</span>.format(sys.argv[<span class="hljs-number">2</span>],line)) <span class="hljs-keyword">if</span> x == <span class="hljs-number">0</span>: <span class="hljs-keyword">print</span> (<span class="hljs-string">'[~] Password is : {0}\n\n'</span>.format(line)) exit(<span class="hljs-number">1</span>)<span class="hljs-keyword">print</span> (<span class="hljs-string">'[!] Password not found in the provided list!\n\n'</span>)</code></pre></div><div class="hljs"><pre><code class="hljs shell"><span class="hljs-meta">$</span><span class="bash">easy.py <passwords-file> <7z-file-to-bruteforce></span></code></pre></div><p>文件使用绝对路径,使用7z命令,需要先将7z加入到path路径下.</p><p><strong>优点:</strong> 定制化程度高,灵活性程度高,效率与个人code能力成正比,可随意配置字典以及修改代码</p><p><strong>缺点:</strong> 单线程,效率较低</p><p>笔者重新写了一个版本,一般破解会有多个字典,直接使用字典所在文件夹作为参数,递归自动遍历所有字典.</p><div class="hljs"><pre><code class="hljs python"><span class="hljs-comment">#!/usr/bin/env python2</span><span class="hljs-keyword">import</span> os<span class="hljs-keyword">import</span> sys<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">traversingFolder</span><span class="hljs-params">(path, crack_file)</span>:</span> files = os.listdir(path) <span class="hljs-keyword">for</span> file <span class="hljs-keyword">in</span> files: file = os.path.join(path, file) <span class="hljs-keyword">if</span> os.path.isdir(file): <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> os.listdir(file): <span class="hljs-keyword">continue</span> <span class="hljs-keyword">else</span>: traversingFolder(file, crack_file) <span class="hljs-keyword">else</span>: doCheck(file, crack_file)<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">doCheck</span><span class="hljs-params">(filepath, crack_file)</span>:</span> lines = open(filepath, <span class="hljs-string">'r'</span>).read().splitlines() <span class="hljs-keyword">for</span> line <span class="hljs-keyword">in</span> lines: x = os.system(<span class="hljs-string">'7z e {0} -p{1}'</span>.format(crack_file, line)) <span class="hljs-keyword">if</span> x == <span class="hljs-number">0</span>: print(<span class="hljs-string">'[~] Password is : {0}\n\n'</span>.format(line)) exit(<span class="hljs-number">1</span>)traversingFolder(sys.argv[<span class="hljs-number">1</span>], sys.argv[<span class="hljs-number">2</span>])print(<span class="hljs-string">'[!] Password not found in the provided list!\n\n'</span>)</code></pre></div><p>运行命令:</p><div class="hljs"><pre><code class="hljs shell"><span class="hljs-meta">$</span><span class="bash">easy.py <passwords-dir> <7z-file-to-bruteforce></span></code></pre></div><p><a href="https://github.com/a1046149428/passCrackScript" target="_blank" rel="noopener">代码地址</a></p><p>这里附一个爆破密码地址:<a href="https://github.com/rootphantomer/Blasting_dictionary" target="_blank" rel="noopener">Blasting_dictionary</a></p><h3 id="ARCHPR"><a href="#ARCHPR" class="headerlink" title="ARCHPR"></a>ARCHPR</h3><p>这里额外说一下这款破解工具,本来是不需要的,因为不支持7z,但是由于其功能强大与方便,所以在这里mark一下.</p><p>ARCHPR 全程advanced rar password recovery,支持RAR和ZiP格式压缩的文件</p><p><a href="https://advanced-archive-password-recovery.en.softonic.com/" target="_blank" rel="noopener">下载地址</a></p><p><strong>优点:</strong> 支持字典破解,支持字符长度范围调,支持明文破解</p><p><strong>缺点:</strong> 不支持7z</p>]]></content>
<categories>
<category>安全</category>
</categories>
<tags>
<tag>破解</tag>
<tag>破解工具</tag>
<tag>压缩文件</tag>
</tags>
</entry>
<entry>
<title>租房扫盲贴以及我为什么不租品牌公寓</title>
<link href="/post/5227f27b.html"/>
<url>/post/5227f27b.html</url>
<content type="html"><![CDATA[<h2 id="起因"><a href="#起因" class="headerlink" title="起因"></a>起因</h2><p>照例写个起因吧!又是一年毕业季,2019年从象牙塔走进社会的高校毕业生接近850万,这么大数量的人涌进社会中,首要的事就是租一间适居的房子!!!</p><p>主要也是好几个朋友都在找房子租,说租房子好难,我就顺势写一篇文章.</p><h2 id="为什么租房难"><a href="#为什么租房难" class="headerlink" title="为什么租房难"></a>为什么租房难</h2><p>说明一下深圳南山和宝安这边为什么租房难,因为深圳白石洲拆迁了,造就新的一批土豪的同时也让白石洲大量的租房者需要再重新租房。其中辛酸就不细说了,详查:<a href="https://news.leju.com/2019-07-15/6556420408487493193.shtml" target="_blank" rel="noopener">《白石洲里没有“深圳梦”:15万租客最迟9月底搬清房屋》</a>以及<a href="https://www.huxiu.com/article/307493.html" target="_blank" rel="noopener">《深圳最大城中村“白石洲”清租困局:被断档的学位》</a>。<br>和大量毕业生毕业和深圳吸引人才政策导致深圳这块热土租房更加紧张,此外大量资本进入长租公寓领域,导致房租上涨,原本两千就能租到不错的房子,现在就算是在桂庙新村(深大旁)的一个独卫单间也需要两千多,而且周围环境相当不好。部分长租公寓恶毒的手段就不细说了,详查:<a href="https://zhuanlan.zhihu.com/p/68124009" target="_blank" rel="noopener">《租房贷,甲醛房,长租公寓毁了年轻人》</a></p><h2 id="租房扫盲"><a href="#租房扫盲" class="headerlink" title="租房扫盲"></a>租房扫盲</h2><h3 id="什么样子的房子适居"><a href="#什么样子的房子适居" class="headerlink" title="什么样子的房子适居"></a>什么样子的房子适居</h3><p>大部分刚毕业的学生囊中羞涩,能省则省,看到58同城上便宜又好看的房子免不了心动,当你联系房东的时候就不免掉入圈套,参上《租房贷,甲醛房,长租公寓毁了年轻人》。我在这里举几个栗子:</p><p><img src="/images/租房聊天.webp" alt="聊天陷阱"><br><img src="/images/trap1.webp" alt="实际房间"></p><p>像上面主动问你是否租房的,看起来似乎很美,但其实就是那些长租公寓的招租的销售员。 没有房东会把自己的房子装饰成小清新的样子,至少我没见过装饰成这样出租的一手东!!!那不过是迎合现代年轻人喜欢的风格用廉价家具装饰出来的假象罢了!</p><p><img src="/images/trap2.webp" alt="实际房间"></p><p>上面圈出房子有经验的一看就知道是陷阱,为什么这么说呢?在大冲这个租房热门区域,如此低的价格必有妖。很多毕业生因为不熟悉地理位置常常迷路就不说了,还被各种低价吸引反而上当受骗,大学中的住宿费才多少钱?也就轻易易相信了,最后被割了韭菜不说,还耽误自己的计划。</p><p>租房前提:切莫贪心低房租,因小失大。低于市场平均的绝对有妖,高于市场平均的那是钱的香气。</p><p>那么合适的房子可以避免大部分坑的第一个条件:合理的价格,基本与市场价持平,那相应的市场价是什么样的呢?下面给出我个人认准表格,纯经验非官方,一般可以租到还可以的小区房或者比较好的农民房:</p><table><thead><tr><th style="text-align:center">行政区</th><th style="text-align:center">房型</th><th style="text-align:center">价格</th></tr></thead><tbody><tr><td style="text-align:center">南山</td><td style="text-align:center">一室独卫</td><td style="text-align:center">2000+</td></tr><tr><td style="text-align:center">宝安</td><td style="text-align:center">一室独卫</td><td style="text-align:center">1600+</td></tr><tr><td style="text-align:center">南山</td><td style="text-align:center">两室一厅</td><td style="text-align:center">5000+</td></tr><tr><td style="text-align:center">宝安</td><td style="text-align:center">两室一厅</td><td style="text-align:center">4500+</td></tr></tbody></table><p>我个人比较熟悉南山和宝安的房子,像福田应该比南山略高,与罗湖持平,龙岗和龙华不同地段差距过大,越接近市中心的越贵,相反远离市中心的越便宜。其他宜居注意事项不外乎以下几点:</p><ol><li>绿化好,空气清新,风吹来十分惬意</li><li>周围无马路无高速路,睡眠不好的朋友尤其注意,一到晚上马路呼啸声也是够受的</li><li>房屋结构好,房间采光好,房屋无暗病,下水道不易堵塞,房屋不会漏水</li><li>非新装修的房子,建议选择已被租住两年以上的房子,新装修的房子存在空气问题</li></ol><h3 id="如何选择合租室友"><a href="#如何选择合租室友" class="headerlink" title="如何选择合租室友"></a>如何选择合租室友</h3><p>很多同学因为刚毕业或者想省些钱,往往选择与别人合租.当然,好的室友会成为自己的良师益友,下班后或者节假日可以结伴出玩.这里给出几点建议:</p><ol><li>尽量选择相熟且注意卫生的人,最好是好朋友了,参考<a href="https://www.zhihu.com/question/275485204" target="_blank" rel="noopener">知乎:合租遇到极不注意卫生的室友是一种什么体验?</a></li><li>合租不要随意进出别人的房间</li><li>合租前进行约法三章,比如安排值日以及一些相关注意事项,听说过三个和尚没水喝吗?</li><li>跟志同道合的人合租,这个是非硬性条件.</li></ol><h3 id="我为什么不选择租品牌公寓"><a href="#我为什么不选择租品牌公寓" class="headerlink" title="我为什么不选择租品牌公寓"></a>我为什么不选择租品牌公寓</h3><p>我为什么不选择租品牌公寓?可以换成我为什么要租品牌公寓或者租品牌公寓对我有什么好处,我要付出什么代价?</p><p>在给出结论前,先分析一下它的好处:</p><ul><li>租房管家,24小时管理服务</li><li>定期清洁服务</li><li>看起来还不错的环境</li><li>看起来不错的家具</li></ul><p>再给出需要付出的代价:</p><ol><li>除正常的物业费外,需要交纳管理费</li><li>非整租合租人员素质参差不齐,我虽知人人平等,但每个人素质却是不一样的</li><li>廉价的家具和廉价装修往往会带来甲醛之类的空气问题</li><li>高于市场价的房租以及水电费</li><li>租住的房子并非管家的,对待房子没有房东那样尽心尽力</li><li>租房套路贷</li></ol><p>综上,品牌公寓无非就是中间商赚差价,这是一群需要租客房租养活的一群vampire,而且其中坑点防不胜防参见<a href="https://www.zhihu.com/question/35264751" target="_blank" rel="noopener">知乎:准备在自如客租房,有什么坑要注意的吗?</a>,不差钱可以直接找中介,中介手中有大量房源省时省力.</p><p>对于我个人来说,租住品牌公寓丝毫没有意义,一来无需管家,二来额外缴纳的费用不值当,三来亦可以找家政清洁,避免其他人的侵扰.</p><h3 id="更多信息"><a href="#更多信息" class="headerlink" title="更多信息"></a>更多信息</h3><p>详见:<a href="https://www.zhihu.com/question/307959368" target="_blank" rel="noopener">知乎:有什么事是你租房以后才知道的?</a></p>]]></content>
<categories>
<category>生活</category>
</categories>
<tags>
<tag>生活</tag>
<tag>租房</tag>
</tags>
</entry>
<entry>
<title>JVM学习笔记</title>
<link href="/post/28becffb.html"/>
<url>/post/28becffb.html</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>因工作上涉及到jvm内存管理,这一块内容因使用的不多,以前了解的知识差不多都忘光了,但目前需要简单地针对当前应用进行调优和内存压缩。故简单学习下jvm,基于《深入立即Java虚拟机》学习,做一部分笔记,如果对你有帮助,不胜荣幸。</p><h2 id="jvm种类和历史"><a href="#jvm种类和历史" class="headerlink" title="jvm种类和历史"></a>jvm种类和历史</h2><p>常用的jvm有sun公司的Hotspot和eba公司的JRockit(均被oracle收购了)以及IBM公司的j9。都是相当常用的虚拟机。目前大众使用的oracle的jdk使用的应该是Hotspot虚拟机。其中有许多语言建立在jvm虚拟机上,如果可以深入学习jvm虚拟机,则可以触类旁通,窥一斑而见全豹。针对kotlin和groovy以及Scala虚拟机都有一定认识。</p><h2 id="jvm内存分布"><a href="#jvm内存分布" class="headerlink" title="jvm内存分布"></a>jvm内存分布</h2><p>说到jvm对于普通开发者来说,最重要的就是内存调优。jdk1.7和jdk1.8以后内存有较大的区别。其中无论是1.7还是1.8都移除了字符串常量池。</p><h3 id="运行时数据区"><a href="#运行时数据区" class="headerlink" title="运行时数据区"></a>运行时数据区</h3><p>java虚拟机运行时,会将它所管理的内存划分若干用途不同的数据区域。如图:<img src="/images/jvmruntimearea.webp" alt="运行时数据区"></p><ul><li>程序计数器(Program Counter Register):字节码解释器工作通过改变这个计数器的值来选取下一个需要执行的字节码指令,分支、跳转、异常处理、线程恢复等基础功能都依赖此。</li><li>java虚拟机栈(Java virtual machine stacks):线程私有,生命周期与线程相同,描述的是Java方法执行的内存模型:每一个方法执行的同时都会创建一个栈帧(Stack Frame),由于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法的执行就对应着栈帧在虚拟机栈中的入栈,出栈过程。</li><li>本地方法栈(native method stack):与虚拟机栈所发挥的作用是非常相似的,他们之间的区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的Native方法服务。在虚拟机规范中对本地方法栈中方法使用的语言、使用方式与数据结构并没有强制规定,因此具体的虚拟机剋有自由实现它。甚至有的虚拟机(譬如Sun HotSpot虚拟机)直接就把本地方法栈和虚拟机栈合二为一。与虚拟机栈一样,本地方法栈区域也会抛出StackOverflowError和OutOfMemoryError异常。</li><li>Java堆(Java heap):对于大部分应用来说,Java堆是Java虚拟机所管理的内存最大的一块。Java堆被所有线程共享,此内存区域就是存放对象实例。</li><li>方法区(method area):同样被各线程共享的内存区域,用于存储已被虚拟机加载的类信息、常量、静态变量,即时编译器编译后的代码。Java虚拟机规范把方法区描述为堆的一个逻辑部分。</li><li><del>运行时常量池(runtime contant pool):已废弃不介绍了。</del></li><li>直接内存(direct memory):直接内存并不是虚拟机运行时数据区的一部分 -Xmx并无法限制字节内存。</li><li>元空间(Metaspace):元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制,但可以通过以下参数来指定元空间的大小:<code>-XX:MetaspaceSize</code>,初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过<em>MaxMetaspaceSize</em>时,适当提高该值。<br> <code>-XX:MaxMetaspaceSize</code>,最大空间,默认是没有限制的。<br>除了上面两个指定大小的选项以外,还有两个与 GC 相关的属性:<br> <code>-XX:MinMetaspaceFreeRatio</code>,在GC之后,最小的Metaspace剩余空间容量的百分比,减少为分配空间所导致的垃圾收集<br><code>-XX:MaxMetaspaceFreeRatio</code>,在GC之后,最大的Metaspace剩余空间容量的百分比,减少为释放空间所导致的垃圾收集</li></ul><h3 id="java中的引用"><a href="#java中的引用" class="headerlink" title="java中的引用"></a>java中的引用</h3><p>什么是引用? 如果reference类型的数据中存储的数值代表的是另一块内存的起始地址,就称这块内存代表着一个引用。</p><h4 id="强引用"><a href="#强引用" class="headerlink" title="强引用"></a>强引用</h4><p>指在程序代码之中普遍存在的,类似于<code>Object obj=new Object()</code>,这类引用只要还存在,垃圾收集器就不会回收掉被引用的对象。</p><h4 id="软引用"><a href="#软引用" class="headerlink" title="软引用"></a>软引用</h4><p>软引用就是用来描述一些还有用但是并非必须的对象。对于软引用关联着的对象,在系统将要发生内存溢出的异常之前,将会把这些对象列入回收范围之中进行第二次回收。如果这次回收内存仍然不足,才会抛出内存溢出的异常。jdk1.2之后引入SoftReference类来实现。</p><h4 id="弱引用"><a href="#弱引用" class="headerlink" title="弱引用"></a>弱引用</h4><p>也是用来描述非必须对象的,但是他的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集器工作时,无论内存是否够用都会收集,jdk1.2之后引入WeakReference类来实现。</p><h4 id="虚引用"><a href="#虚引用" class="headerlink" title="虚引用"></a>虚引用</h4><p>虚引用被称为幽灵引用或者幻影引用,它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的就是能在这个对象被回收时收到一个系统通知,jdk1.2后引入PhantomReference类来实现虚引用。</p><h2 id="类加载机制"><a href="#类加载机制" class="headerlink" title="类加载机制"></a>类加载机制</h2><p>虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。</p><h3 id="类加载的过程"><a href="#类加载的过程" class="headerlink" title="类加载的过程"></a>类加载的过程</h3><p>类从被加载到JVM中开始,到卸载为止,整个生命周期包括:加载、验证、准备、解析、初始化、使用和卸载七个阶段。</p><p>其中类加载过程包括加载、验证、准备、解析和初始化五个阶段。</p><p><img src="/images/classloadprocess.webp" alt="类加载过程"></p><h4 id="加载"><a href="#加载" class="headerlink" title="加载"></a>加载</h4><p>这个很简单,程序运行之前jvm会把编译完成的.class二进制文件加载到内存,供程序使用,用到的就是类加载器classLoader ,这里也可以看出java程序的运行并不是直接依 靠底层的操作系统,而是基于jvm虚拟机。如果没有类加载器,java文件就只是磁盘中的一个普通文件。</p><h4 id="连接"><a href="#连接" class="headerlink" title="连接"></a>连接</h4><p>连接是很重要的一步,过程比较复杂,分为三步 验证 >准备 >解析。</p><ol><li>验证:确保类加载的正确性。一般情况由javac编译的class文件是不会有问题的,但是可能有人的class文件是自己通过其他方式编译出来的,这就很有可能不符合jvm的编 译规则,这一步就是要过滤掉这部分不合法文件</li><li>准备:为类的静态变量分配内存,将其初始化为默认值 。我们都知道静态变量是可以不用我们手动赋值的,它自然会有一个初始值 比如int 类型的初始值就是0 ;boolean类型初始值为false,引用类型的初始值为null 。 这里注意,只是为静态变量分配内存,此时是没有对象实例的</li><li>解析:把类中的符号引用转化为直接引用。解释一下符号引用和直接引用。比如在方法A中使用方法B,A(){B();},这里的B()就是符号引用,初学java时我们都是知道这是java的引用,以为B指向B方法的内存地址,但是这是不完整的,这里的B只是一个符号引用,它对于方法的调用没有太多的实际意义,可以这么认为,他就是给程序员看的一个标志,让程序员知道,这个方法可以这么调用,但是B方法实际调用时是通过一个指针指向B方法的内存地址,这个指针才是真正负责方法调用,他就是直接引用。</li></ol><h4 id="初始化"><a href="#初始化" class="headerlink" title="初始化"></a>初始化</h4><p>为类的静态变量赋予正确的初始值,上述的准备阶段为静态变量赋予的是虚拟机默认的初始值,此处赋予的才是程序编写者为变量分配的真正的初始值</p><h4 id="类卸载"><a href="#类卸载" class="headerlink" title="类卸载"></a>类卸载</h4><p>不像类加载,Java中没有提供显式进行类卸载的API,但是如果加载类的ClassLoader对象被垃圾回收器回收的话,这个类就会被卸载。所以我们可以自己实现ClassLoader,自己加载类,然后对ClassLoader对象的引用赋值为null,等ClassLoader对象剩下的引用数量为0时会被回收,这样就达到卸载类的目的了。</p><h2 id="垃圾收集器与收集算法"><a href="#垃圾收集器与收集算法" class="headerlink" title="垃圾收集器与收集算法"></a>垃圾收集器与收集算法</h2><p>java和c++有一道关于内存回收的高墙,墙外的人想进去,墙内的人想去。世界总是平衡的。</p><h3 id="垃圾收集算法"><a href="#垃圾收集算法" class="headerlink" title="垃圾收集算法"></a>垃圾收集算法</h3><h4 id="标记-清除算法"><a href="#标记-清除算法" class="headerlink" title="标记-清除算法"></a>标记-清除算法</h4><p>最基础的收集算法是“标记-清除”(Mark-Sweep)算法,分两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。</p><p>不足:一个是效率问题,标记和清除两个过程的效率都不高;另一个是空间问题,标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能导致以后在程序运行过程需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一个的垃圾收集动作。</p><h4 id="复制算法"><a href="#复制算法" class="headerlink" title="复制算法"></a>复制算法</h4><p>为了解决效率问题,一种称为复制(Copying)的收集算法出现了,它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块内存用完了,就将还存活着的对象复制到另外一块上,然后再把已经使用过的内存空间一次清理掉。这样使得每次都是对整个半区进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。代价是内存缩小为原来的一半。</p><p>商业虚拟机用这个回收算法来回收新生代。IBM研究表明98%的对象是“朝生夕死“,不需要按照1-1的比例来划分内存空间,而是将内存分为一块较大的”Eden“空间和两块较小的Survivor空间,每次使用Eden和其中一块Survivor。当回收时,将Eden和Survivor中还存活的对象一次性复制到另外一个Survivor空间上,最后清理掉Eden和刚才用过的Survivor空间。Hotspot虚拟机默认Eden和Survivor的比例是8-1.即每次可用整个新生代的90%, 只有一个survivor,即1/10被”浪费“。当然,98%的对象回收只是一般场景下的数据,我们没有办法保证每次回收都只有不多于10%的对象存活,当Survivor空间不够时,需要依赖其他内存(老年代)进行分配担保(Handle Promotion).</p><p>如果另外一块survivor空间没有足够空间存放上一次新生代收集下来的存活对象时,这些对象将直接通过分配担保机制进入老年代。</p><p>Eden Space字面意思是伊甸园,对象被创建的时候首先放到这个区域,进行垃圾回收后,不能被回收的对象被放入到空的survivor区域。</p><p>Survivor Space幸存者区,用于保存在eden space内存区域中经过垃圾回收后没有被回收的对象。Survivor有两个,分别为To Survivor、 From Survivor,这个两个区域的空间大小是一样的。执行垃圾回收的时候Eden区域不能被回收的对象被放入到空的survivor(也就是To Survivor,同时Eden区域的内存会在垃圾回收的过程中全部释放),另一个survivor(即From Survivor)里不能被回收的对象也会被放入这个survivor(即To Survivor),然后To Survivor 和 From Survivor的标记会互换,始终保证一个survivor是空的。</p><p>为啥需要两个survivor?因为需要一个完整的空间来复制过来。当满的时候晋升。每次都往标记为to的里面放,然后互换,这时from已经被清空,可以当作to了。</p><h4 id="标记-整理算法"><a href="#标记-整理算法" class="headerlink" title="标记-整理算法"></a>标记-整理算法</h4><p>复制收集算法在对象成活率较高时就要进行较多的复制操作,效率将会变低。更关键的是,如果不想浪费50%的空间,就需要有额外的空间进行分配担保,以应对被使用的内存中所有对象都100%存活的极端情况,所以,老年代一般不能直接选用这种算法。</p><p>根据老年代的特点,有人提出一种”标记-整理“Mark-Compact算法,标记过程仍然和标记-清除一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理端边界以外的内存.</p><h4 id="分代收集算法"><a href="#分代收集算法" class="headerlink" title="分代收集算法"></a>分代收集算法</h4><p>当前商业虚拟机的垃圾收集都采用”分代收集“(Generational Collection)算法,这种算法根据对象存活周期的不同将内存划分为几块。一般把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。在新生代,每次垃圾收集时都发现大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率较高,没有额外的空间对它进行分配担保,就必须使用”标记-清理“和”标记-整理“算法来进行回收。</p><h4 id="HotSpot算法实现"><a href="#HotSpot算法实现" class="headerlink" title="HotSpot算法实现"></a>HotSpot算法实现</h4><p>在Java语言中,可作为GC Roots的对象包括下面几种:</p><ul><li>虚拟机栈(栈帧中的本地变量表)中引用的对象</li><li>方法去中类静态属性引用的对象</li><li>方法区中常量引用的对象</li><li>本地方法栈中JNI(即一般说的Native方法)引用的对象</li></ul><p>从可达性分析中从GC Roots节点找引用链这个操作为例,可作为GC Roots的节点主要在全局性的引用(例如常量或类静态属性)与执行上下文(例如栈帧中的本地变量表)中,现在很多应用仅仅方法区就有数百兆,如果要逐个检查里面的引用,必然消耗很多时间。</p><p>可达性分析对执行时间的敏感还体现在GC停顿上,因为这项分析工作必须在一个能确保一致性的快照中进行–这里”一致性“的意思是指整个分析期间整个执行系统看起来就像被冻结在某个时间点,不可以出现分析过程中对象引用关系还在不断变化的情况,该点不满足的话分析结果准确性就无法得到保证。这点是导致GC进行时必须停顿所有Java执行线程(Sun公司将这件事情称为”Stop The World“)的一个重要原因,即使是在号称(几乎)不会发生停顿的CMS收集器中,枚举根节点时也必须停顿的。</p><h3 id="垃圾收集器"><a href="#垃圾收集器" class="headerlink" title="垃圾收集器"></a>垃圾收集器</h3><h4 id="Serial-收集器"><a href="#Serial-收集器" class="headerlink" title="Serial 收集器"></a>Serial 收集器</h4><p>标记-复制。</p><p>单线程,一个CPU或一条收集线程去完成垃圾收集工作,收集时必须暂停其他所有的工作线程,直到它结束。</p><p>虽然如此,它依然是虚拟机运行在Client模式下的默认新生代收集器。简单而高效。</p><h4 id="ParNew-收集器"><a href="#ParNew-收集器" class="headerlink" title="ParNew 收集器"></a>ParNew 收集器</h4><p>ParNew是Serial收集器的多线程版本。Server模式下默认新生代收集器,除了Serial收集器之外,只有它能与CMS收集器配合工作。</p><p>并行 Parallel<br>指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态。</p><p>并发 Concurrent<br>指用户线程与垃圾收集线程同时执行(但不一定是并行的,可能会交替执行),用户程序再继续运行,而垃圾收集程序运行于另一个CPU上。</p><h4 id="Parallel-Scavenge-收集器"><a href="#Parallel-Scavenge-收集器" class="headerlink" title="Parallel Scavenge 收集器"></a>Parallel Scavenge 收集器</h4><p>Parallel Scavenge 收集器是一个新生代收集器,它也是使用复制算法的收集器。看上去来ParNew一样,有什么特别?</p><p>Parallel Scavenge 收集器的特点是它的关注点与其他收集器不同,CMS等收集器关注点是尽可能缩短垃圾收集时用户线程的停顿时间。而Parallel Scavenge收集器的目标则是达到一个可控制的吞吐量(Throughput)。所谓吞吐量就是CPU用于运行用户代码的时间和CPU总小号时间的比值,即吞吐量 = 运行用户代码时间 / (运行用户代码时间+垃圾收集时间),虚拟机总共运行了100min,其中垃圾收集花费了1min,那吞吐量就是99%.</p><p>停顿时间越短就越适合需要与用户交互的程序,良好的响应速度能提升用户体验,而高吞吐量则可以高效地利用CPU时间,主要适合在后台运算而不需要太多交互的任务。</p><p>Parallel Scavenge收集器提供了两个参数用于精确控制吞吐量,分别是控制最大垃圾收集停顿时间 -XX:MaxGCPauseMillis以及直接设置吞吐量大小的-XX:GCTimeRatio。</p><h4 id="Serial-Old收集器"><a href="#Serial-Old收集器" class="headerlink" title="Serial Old收集器"></a>Serial Old收集器</h4><p>Serial Old是Serial收集器的老年代版本,它同样是一个单线程收集器。给Client模式下的虚拟机使用。</p><p>新生代采用复制算法,暂停所有用户线程;</p><p>老年代采用标记-整理算法,暂停所有用户线程;</p><h4 id="Parallel-Old-收集器"><a href="#Parallel-Old-收集器" class="headerlink" title="Parallel Old 收集器"></a>Parallel Old 收集器</h4><p>是Parallel Scavenge收集器的老年代版本,用于老年代的垃圾回收,但与Parallel Scavenge不同的是,它使用的是“标记-整理算法”。适用于注重于吞吐量及CPU资源敏感的场合。</p><p>使用方式:-XX:+UseParallelOldGC,打开该收集器后,将使用Parallel Scavenge(年轻代)+Parallel Old(老年代)的组合进行GC。</p><h4 id="CMS-收集器"><a href="#CMS-收集器" class="headerlink" title="CMS 收集器"></a>CMS 收集器</h4><p>CMS(Concurrent Mark Sweep) 收集器是一种以获取最短回收停顿时间为目标的收集器。目前很大一部分的Java应用集中在互联网站或者B/S系统的服务端上,这类尤其重视服务的响应速度,希望系统停顿时间最短。CMS收集器就非常符合这类应用的需求。</p><p>CMS基于 标记-清除算法实现。整个过程分为4个步骤:</p><p>初始标记(CMS initial mark) -stop the world<br>并发标记(CMS concurrent mark)<br>重新标记(CMS remark) -stop the world<br>并发清除(CMS concurrent sweep)<br>初始标记,重新标记这两个步骤仍然需要Stop The World, 初始标记仅仅标记以下GC Roots能直接关联的对象,速度很快。</p><p>并发标记就是进行GC Roots Tracing的过程;</p><p>而重新标记阶段则是为了修正并发标记期间因为用户程序继续运作而导致标记产生变动的那一部分对象的标记记录。这个阶段停顿比初始标记稍微长,但远比并发标记的时间短。</p><p>整个过程耗时最长的并发标记和并发清除过程,收集器都可以与用户线程一起工作。总体上来说,CMS收集器的内存回收过程与用户线程一起并发执行的。</p><p>CMS特点:并发收集,低停顿。</p><h5 id="缺点"><a href="#缺点" class="headerlink" title="缺点"></a>缺点</h5><ol><li><p>CMS收集器对CPU资源非常敏感。默认启动的回收线程数是(CPU+3)/4. 当CPU 4个以上时,并发回收垃圾收集线程不少于25%的CPU资源。</p></li><li><p>CMS收集器无法处理浮动垃圾(Floating Garbage), 可能出现”Concurrent Mode Failure“失败而导致另一次Full GC的产生。由于CMS并发清理时,用户线程还在运行,伴随产生新垃圾,而这一部分出现在标记之后,只能下次GC时再清理。这一部分垃圾就称为”浮动垃圾“。由于CMS运行时还需要给用户空间继续运行,则不能等老年代几乎被填满再进行收集,需要预留一部分空间提供并发收集时,用户程序运行。JDK1.6中,CMS启动阈值为92%. 若预留内存不够用户使用,则出现一次Concurent Mode Failure失败。这时虚拟机启动后备预案,临时启用Serial Old收集老年代,这样停顿时间很长。</p></li><li>CMS基于”标记-清除“算法实现的,则会产生大量空间碎片,空间碎片过多时,没有连续空间分配给大对象,不得不提前触发一次FUll GC。当然可以开启-XX:+UseCMSCompactAtFullCollection(默认开),在CMS顶不住要FullGC时开启内存碎片合并整理过程。内存整理过程是无法并发的,空间碎片问题没了,但停顿时间变长。</li></ol><h5 id="面试题:CMS一共会有几次STW"><a href="#面试题:CMS一共会有几次STW" class="headerlink" title="面试题:CMS一共会有几次STW"></a>面试题:CMS一共会有几次STW</h5><p>首先,回答两次,初始标记和重新标记需要。</p><p>然后,CMS并发的代价是预留空间给用户,预留不足的时候触发FUllGC,这时Serail Old会STW.</p><p>然后,CMS是标记-清除算法,导致空间碎片,则没有连续空间分配大对象时,FUllGC, 而FUllGC会开始碎片整理, STW.</p><p>即2次或多次。</p><h5 id="CMS什么时候FUll-GC"><a href="#CMS什么时候FUll-GC" class="headerlink" title="CMS什么时候FUll GC"></a>CMS什么时候FUll GC</h5><p>除直接调用System.gc外,触发Full GC执行的情况有如下四种。</p><h6 id="旧生代空间不足"><a href="#旧生代空间不足" class="headerlink" title="旧生代空间不足"></a>旧生代空间不足</h6><p>旧生代空间只有在新生代对象转入及创建为大对象、大数组时才会出现不足的现象,当执行Full GC后空间仍然不足,则抛出如下错误:</p><p>java.lang.OutOfMemoryError: Java heap space</p><p>为避免以上两种状况引起的FullGC,调优时应尽量做到让对象在Minor GC阶段被回收、让对象在新生代多存活一段时间及不要创建过大的对象及数组。</p><h6 id="Permanet-Generation空间满"><a href="#Permanet-Generation空间满" class="headerlink" title="Permanet Generation空间满"></a><del>Permanet Generation空间满</del></h6><p>PermanetGeneration中存放的为一些class的信息等,当系统中要加载的类、反射的类和调用的方法较多时,Permanet Generation可能会被占满,在未配置为采用CMS GC的情况下会执行Full GC。如果经过Full GC仍然回收不了,那么JVM会抛出如下错误信息:</p><p>java.lang.OutOfMemoryError: PermGen space</p><p>为避免Perm Gen占满造成Full GC现象,可采用的方法为增大Perm Gen空间或转为使用CMS GC。</p><h6 id="CMS-GC时出现promotion-failed和concurrent-mode-failure"><a href="#CMS-GC时出现promotion-failed和concurrent-mode-failure" class="headerlink" title="CMS GC时出现promotion failed和concurrent mode failure"></a>CMS GC时出现promotion failed和concurrent mode failure</h6><p>对于采用CMS进行旧生代GC的程序而言,尤其要注意GC日志中是否有promotion failed和concurrent mode failure两种状况,当这两种状况出现时可能会触发Full GC。</p><p>promotionfailed是在进行Minor GC时,survivor space放不下、对象只能放入旧生代,而此时旧生代也放不下造成的;concurrent mode failure是在执行CMS GC的过程中同时有对象要放入旧生代,而此时旧生代空间不足造成的。</p><p>应对措施为:增大survivorspace、旧生代空间或调低触发并发GC的比率,但在JDK 5.0+、6.0+的版本中有可能会由于JDK的bug29导致CMS在remark完毕后很久才触发sweeping动作。对于这种状况,可通过设置-XX:CMSMaxAbortablePrecleanTime=5(单位为ms)来避免。</p><h6 id="统计得到的Minor-GC晋升到旧生代的平均大小大于旧生代的剩余空间"><a href="#统计得到的Minor-GC晋升到旧生代的平均大小大于旧生代的剩余空间" class="headerlink" title="统计得到的Minor GC晋升到旧生代的平均大小大于旧生代的剩余空间"></a>统计得到的Minor GC晋升到旧生代的平均大小大于旧生代的剩余空间</h6><p>这是一个较为复杂的触发情况,Hotspot为了避免由于新生代对象晋升到旧生代导致旧生代空间不足的现象,在进行Minor GC时,做了一个判断,如果之前统计所得到的Minor GC晋升到旧生代的平均大小大于旧生代的剩余空间,那么就直接触发Full GC。</p><p>例如程序第一次触发MinorGC后,有6MB的对象晋升到旧生代,那么当下一次Minor GC发生时,首先检查旧生代的剩余空间是否大于6MB,如果小于6MB,则执行Full GC。</p><p>当新生代采用PSGC时,方式稍有不同,PS GC是在Minor GC后也会检查,例如上面的例子中第一次Minor GC后,PS GC会检查此时旧生代的剩余空间是否大于6MB,如小于,则触发对旧生代的回收。</p><p>除了以上4种状况外,对于使用RMI来进行RPC或管理的Sun JDK应用而言,默认情况下会一小时执行一次Full GC。可通过在启动时通过- java-Dsun.rmi.dgc.client.gcInterval=3600000来设置Full GC执行的间隔时间或通过-XX:+ DisableExplicitGC来禁止RMI调用System.gc。</p><h4 id="G1垃圾收集器"><a href="#G1垃圾收集器" class="headerlink" title="G1垃圾收集器"></a>G1垃圾收集器</h4><p>G1的第一篇paper(附录1)发表于2004年,在2012年才在jdk1.7u4中可用。oracle官方计划在jdk9中将G1变成默认的垃圾收集器,以替代CMS。为何oracle要极力推荐G1呢,G1有哪些优点?</p><p><strong>首先,G1的设计原则就是简单可行的性能调优</strong><br>开发人员仅仅需要声明以下参数即可:<br><code>-XX:+UseG1GC -Xmx32g -XX:MaxGCPauseMillis=200</code></p><p>其中-XX:+UseG1GC为开启G1垃圾收集器,-Xmx32g 设计堆内存的最大内存为32G,-XX:MaxGCPauseMillis=200设置GC的最大暂停时间为200ms。如果我们需要调优,在内存大小一定的情况下,我们只需要修改最大暂停时间即可。</p><p><strong>其次,G1将新生代,老年代的物理空间划分取消了。</strong><br>这样我们再也不用单独的空间对每个代进行设置了,不用担心每个代内存是否足够。<br><img src="/images/abandoneden.PNG" alt="取消新生代老年代物理空间划分"></p><p>取而代之的是,G1算法将堆划分为若干个区域(Region),它仍然属于分代收集器。不过,这些区域的一部分包含新生代,新生代的垃圾收集依然采用暂停所有应用线程的方式,将存活对象拷贝到老年代或者Survivor空间。老年代也分成很多区域,G1收集器通过将对象从一个区域复制到另外一个区域,完成了清理工作。这就意味着,在正常的处理过程中,G1完成了堆的压缩(至少是部分堆的压缩),这样也就不会有cms内存碎片问题的存在了。</p><p><img src="/images/g1mem.webp" alt="新的g1mem"></p><p>在G1中,还有一种特殊的区域,叫Humongous区域。 如果一个对象占用的空间超过了分区容量50%以上,G1收集器就认为这是一个巨型对象。这些巨型对象,默认直接会被分配在年老代,但是如果它是一个短期存在的巨型对象,就会对垃圾收集器造成负面影响。为了解决这个问题,G1划分了一个Humongous区,它用来专门存放巨型对象。如果一个H区装不下一个巨型对象,那么G1会寻找连续的H分区来存储。为了能找到连续的H区,有时候不得不启动Full GC。</p><h5 id="对象分配策略"><a href="#对象分配策略" class="headerlink" title="对象分配策略"></a>对象分配策略</h5><p>说起大对象的分配,我们不得不谈谈对象的分配策略。它分为3个阶段:</p><ol><li>TLAB(Thread Local Allocation Buffer)线程本地分配缓冲区</li><li>Eden区中分配</li><li>Humongous区分配</li></ol><p>TLAB为线程本地分配缓冲区,它的目的为了使对象尽可能快的分配出来。如果对象在一个共享的空间中分配,我们需要采用一些同步机制来管理这些空间内的空闲空间指针。在Eden空间中,每一个线程都有一个固定的分区用于分配对象,即一个TLAB。分配对象时,线程之间不再需要进行任何的同步。</p><p>对TLAB空间中无法分配的对象,JVM会尝试在Eden空间中进行分配。如果Eden空间无法容纳该对象,就只能在老年代中进行分配空间。</p><p>最后,G1提供了两种GC模式,Young GC和Mixed GC,两种都是Stop The World(STW)的。下面我们将分别介绍一下这2种模式。</p><p><strong>G1 Young GC</strong>:<br>Young GC主要是对Eden区进行GC,它在Eden空间耗尽时会被触发。在这种情况下,Eden空间的数据移动到Survivor空间中,如果Survivor空间不够,Eden空间的部分数据会直接晋升到年老代空间。Survivor区的数据移动到新的Survivor区中,也有部分数据晋升到老年代空间中。最终Eden空间的数据为空,GC停止工作,应用线程继续执行。</p><p>这时,我们需要考虑一个问题,如果仅仅GC 新生代对象,我们如何找到所有的根对象呢? 老年代的所有对象都是根么?那这样扫描下来会耗费大量的时间。于是,G1引进了RSet的概念。它的全称是Remembered Set,作用是跟踪指向某个heap区内的对象引用。</p><p>在CMS中,也有RSet的概念,在老年代中有一块区域用来记录指向新生代的引用。这是一种point-out,在进行Young GC时,扫描根时,仅仅需要扫描这一块区域,而不需要扫描整个老年代。</p><p>但在G1中,并没有使用point-out,这是由于一个分区太小,分区数量太多,如果是用point-out的话,会造成大量的扫描浪费,有些根本不需要GC的分区引用也扫描了。于是G1中使用point-in来解决。point-in的意思是哪些分区引用了当前分区中的对象。这样,仅仅将这些对象当做根来扫描就避免了无效的扫描。由于新生代有多个,那么我们需要在新生代之间记录引用吗?这是不必要的,原因在于每次GC时,所有新生代都会被扫描,所以只需要记录老年代到新生代之间的引用即可。</p><p>需要注意的是,如果引用的对象很多,赋值器需要对每个引用做处理,赋值器开销会很大,为了解决赋值器开销这个问题,在G1 中又引入了另外一个概念,卡表(Card Table)。一个Card Table将一个分区在逻辑上划分为固定大小的连续区域,每个区域称之为卡。卡通常较小,介于128到512字节之间。Card Table通常为字节数组,由Card的索引(即数组下标)来标识每个分区的空间地址。默认情况下,每个卡都未被引用。当一个地址空间被引用时,这个地址空间对应的数组索引的值被标记为”0″,即标记为脏被引用,此外RSet也将这个数组下标记录下来。一般情况下,这个RSet其实是一个Hash Table,Key是别的Region的起始地址,Value是一个集合,里面的元素是Card Table的Index。</p><p>Young GC 阶段:</p><p>阶段1:根扫描</p><p>静态和本地对象被扫描</p><p>阶段2:更新RS</p><p>处理dirty card队列更新RS</p><p>阶段3:处理RS</p><p>检测从年轻代指向年老代的对象</p><p>阶段4:对象拷贝</p><p>拷贝存活的对象到survivor/old区域</p><p>阶段5:处理引用队列</p><p>软引用,弱引用,虚引用处理</p><p><strong>G1 Mix GC</strong> :<br>Mix GC不仅进行正常的新生代垃圾收集,同时也回收部分后台扫描线程标记的老年代分区。</p><p>它的GC步骤分2步:</p><p>1.全局并发标记(global concurrent marking)</p><p>2.拷贝存活对象(evacuation)</p><p>在进行Mix GC之前,会先进行global concurrent marking(全局并发标记)。 global concurrent marking的执行过程是怎样的呢?</p><p>在G1 GC中,它主要是为Mixed GC提供标记服务的,并不是一次GC过程的一个必须环节。global concurrent marking的执行过程分为五个步骤:</p><p>初始标记(initial mark,STW)</p><p>在此阶段,G1 GC 对根进行标记。该阶段与常规的 (STW) 年轻代垃圾回收密切相关。</p><p>根区域扫描(root region scan</p><p>G1 GC 在初始标记的存活区扫描对老年代的引用,并标记被引用的对象。该阶段与应用程序(非 STW)同时运行,并且只有完成该阶段后,才能开始下一次 STW 年轻代垃圾回收。</p><p>并发标记(Concurrent Marking)</p><p>G1 GC 在整个堆中查找可访问的(存活的)对象。该阶段与应用程序同时运行,可以被 STW 年轻代垃圾回收中断</p><p>最终标记(Remark,STW)</p><p>该阶段是 STW 回收,帮助完成标记周期。G1 GC 清空 SATB 缓冲区,跟踪未被访问的存活对象,并执行引用处理。</p><p>清除垃圾(Cleanup,STW)</p><p>在这个最后阶段,G1 GC 执行统计和 RSet 净化的 STW 操作。在统计期间,G1 GC 会识别完全空闲的区域和可供进行混合垃圾回收的区域。清理阶段在将空白区域重置并返回到空闲列表时为部分并发。</p><p>三色标记算法<br>提到并发标记,我们不得不了解并发标记的三色标记算法。它是描述追踪式回收器的一种有用的方法,利用它可以推演回收器的正确性。 首先,我们将对象分成三种类型的。</p><p>黑色:根对象,或者该对象与它的子对象都被扫描</p><p>灰色:对象本身被扫描,但还没扫描完该对象中的子对象</p><p>白色:未被扫描对象,扫描完成所有对象之后,最终为白色的为不可达对象,即垃圾对象</p><p>当GC开始扫描对象时,按照如下图步骤进行对象的扫描:</p><p>根对象被置为黑色,子对象被置为灰色。</p><p><img src="/img/blog/post/jvm/5aabba918215f.webp" alt="三色收集器"></p><p>继续由灰色遍历,将已扫描了子对象的对象置为黑色。<br><img src="/img/blog/post/jvm/5aabba91a1ccd.webp" alt="三色收集器"></p><p>遍历了所有可达的对象后,所有可达的对象都变成了黑色。不可达的对象即为白色,需要被清理。</p><p><img src="/img/blog/post/jvm/5aabba91bd8c3.webp" alt="三色收集器"></p><p>这看起来很美好,但是如果在标记过程中,应用程序也在运行,那么对象的指针就有可能改变。这样的话,我们就会遇到一个问题:对象丢失问题</p><p>我们看下面一种情况,当垃圾收集器扫描到下面情况时:<br><img src="/img/blog/post/jvm/5aabba91db437.webp" alt="三色收集器"></p><p>这时候应用程序执行了以下操作:</p><p>A.c=C<br>B.c=null<br>这样,对象的状态图变成如下情形:<br><img src="/img/blog/post/jvm/5aabba91ec8a9.webp" alt="三色收集器"></p><p>这时候垃圾收集器再标记扫描的时候就会下图成这样:</p><p><img src="/img/blog/post/jvm/5aabba92147f7.webp" alt="三色收集器"><br>很显然,此时C是白色,被认为是垃圾需要清理掉,显然这是不合理的。那么我们如何保证应用程序在运行的时候,GC标记的对象不丢失呢?有如下2中可行的方式:</p><p>1.在插入的时候记录对象</p><p>2.在删除的时候记录对象</p><p>刚好这对应CMS和G1的2种不同实现方式:</p><p>在CMS采用的是增量更新(Incremental update),只要在写屏障(write barrier)里发现要有一个白对象的引用被赋值到一个黑对象 的字段里,那就把这个白对象变成灰色的。即插入的时候记录下来。</p><p>在G1中,使用的是STAB(snapshot-at-the-beginning)的方式,删除的时候记录所有的对象,它有3个步骤:</p><p>1.在开始标记的时候生成一个快照图标记存活对象</p><p>2.在并发标记的时候所有被改变的对象入队(在write barrier里把所有旧的引用所指向的对象都变成非白的)</p><p>3.可能存在游离的垃圾,将在下次被收集</p><p>这样,G1到现在可以知道哪些老的分区可回收垃圾最多。 当全局并发标记完成后,在某个时刻,就开始了Mix GC。这些垃圾回收被称作“混合式”是因为他们不仅仅进行正常的新生代垃圾收集,同时也回收部分后台扫描线程标记的分区。混合式垃圾收集如下图:</p><p><img src="/img/blog/post/jvm/5aabba9242bdb.webp" alt="三色收集器"><br>混合式GC也是采用的复制的清理策略,当GC完成后,会重新释放空间。</p><p><img src="/img/blog/post/jvm/5aabba9268e24.webp" alt="三色收集器"></p><h2 id="双亲委派模型"><a href="#双亲委派模型" class="headerlink" title="双亲委派模型"></a>双亲委派模型</h2><h3 id="什么是双亲委派模型?"><a href="#什么是双亲委派模型?" class="headerlink" title="什么是双亲委派模型?"></a>什么是双亲委派模型?</h3><p>首先,先要知道什么是类加载器。简单说,类加载器就是根据指定全限定名称将class文件加载到JVM内存,转为Class对象。如果站在JVM的角度来看,只存在两种类加载器:</p><h4 id="启动类加载器(Bootstrap-ClassLoader)"><a href="#启动类加载器(Bootstrap-ClassLoader)" class="headerlink" title="启动类加载器(Bootstrap ClassLoader)"></a>启动类加载器(Bootstrap ClassLoader)</h4><p>由C++语言实现(针对HotSpot),负责将存放在<code><JAVA_HOME>\lib</code>目录或<code>-Xbootclasspath</code>参数指定的路径中的类库加载到内存中。</p><h4 id="其他类加载器"><a href="#其他类加载器" class="headerlink" title="其他类加载器"></a>其他类加载器</h4><p>由Java语言实现,继承自抽象类ClassLoader。如:</p><ol><li><p>扩展类加载器(Extension ClassLoader):负责加载<code><JAVA_HOME>\lib\ext</code>目录或<code>java.ext.dirs</code>系统变量指定的路径中的所有类库。</p></li><li><p>应用程序类加载器(Application ClassLoader)。负责加载用户类路径(classpath)上的指定类库,我们可以直接使用这个类加载器。一般情况,如果我们没有自定义类加载器默认就是用这个加载器。</p></li></ol><h3 id="为什么需要双亲委派模型?"><a href="#为什么需要双亲委派模型?" class="headerlink" title="为什么需要双亲委派模型?"></a>为什么需要双亲委派模型?</h3><p>为什么需要双亲委派模型呢?假设没有双亲委派模型,试想一个场景:</p><p><code>黑客自定义一个java.lang.String类,该String类具有系统的String类一样的功能,只是在某个函数稍作修改。比如equals函数,这个函数经常使用,如果在这这个函数中,黑客加入一些“病毒代码”。并且通过自定义类加载器加入到JVM中。此时,如果没有双亲委派模型,那么JVM就可能误以为黑客自定义的java.lang.String类是系统的String类,导致“病毒代码”被执行。</code></p><p>而有了双亲委派模型,黑客自定义的java.lang.String类永远都不会被加载进内存。因为首先是最顶端的类加载器加载系统的java.lang.String类,最终自定义的类加载器无法加载java.lang.String类。</p><p>或许你会想,我在自定义的类加载器里面强制加载自定义的java.lang.String类,不去通过调用父加载器不就好了吗?确实,这样是可行。但是,在JVM中,判断一个对象是否是某个类型时,如果该对象的实际类型与待比较的类型的类加载器不同,那么会返回false。</p><h3 id="如何实现双亲委派模型?"><a href="#如何实现双亲委派模型?" class="headerlink" title="如何实现双亲委派模型?"></a>如何实现双亲委派模型?</h3><p>双亲委派模型的原理很简单,实现也简单。每次通过先委托父类加载器加载,当父类加载器无法加载时,再自己加载。其实ClassLoader类默认的loadClass方法已经帮我们写好了,我们无需去写。在都集中在java.lang.ClassLoader的loadClass()方法中:</p><div class="hljs"><pre><code class="hljs java"><span class="hljs-keyword">protected</span> Class<?> loadClass(String name, <span class="hljs-keyword">boolean</span> resolve) <span class="hljs-keyword">throws</span> ClassNotFoundException { <span class="hljs-keyword">synchronized</span> (getClassLoadingLock(name)) { <span class="hljs-comment">// First, check if the class has already been loaded</span> Class<?> c = findLoadedClass(name); <span class="hljs-keyword">if</span> (c == <span class="hljs-keyword">null</span>) { <span class="hljs-keyword">long</span> t0 = System.nanoTime(); <span class="hljs-keyword">try</span> { <span class="hljs-keyword">if</span> (parent != <span class="hljs-keyword">null</span>) { c = parent.loadClass(name, <span class="hljs-keyword">false</span>); } <span class="hljs-keyword">else</span> { c = findBootstrapClassOrNull(name); } } <span class="hljs-keyword">catch</span> (ClassNotFoundException e) { <span class="hljs-comment">// ClassNotFoundException thrown if class not found</span> <span class="hljs-comment">// from the non-null parent class loader</span> } <span class="hljs-keyword">if</span> (c == <span class="hljs-keyword">null</span>) { <span class="hljs-comment">// If still not found, then invoke findClass in order</span> <span class="hljs-comment">// to find the class.</span> <span class="hljs-keyword">long</span> t1 = System.nanoTime(); c = findClass(name); <span class="hljs-comment">// this is the defining class loader; record the stats</span> PerfCounter.getParentDelegationTime().addTime(t1 - t0); PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); PerfCounter.getFindClasses().increment(); } } <span class="hljs-keyword">if</span> (resolve) { resolveClass(c); } <span class="hljs-keyword">return</span> c; } }</code></pre></div><h3 id="自定义类加载器"><a href="#自定义类加载器" class="headerlink" title="自定义类加载器"></a>自定义类加载器</h3><p>从上面代码可以明显看出,loadClass(String, boolean)函数即实现了双亲委派模型!整个大致过程如下:</p><ol><li>首先,检查一下指定名称的类是否已经加载过,如果加载过了, 就不需要再加载,直接返回。</li><li>如果此类没有加载过,那么,再判断一下是否有父加载器;如果有父加载器,则由父加载器加载(即调用parent.loadClass(name, false);).或者是调用bootstrap类加载器来加载。</li><li>如果父加载器及bootstrap类加载器都没有找到指定的类,那么调用当前类加载器的findClass方法来完成类加载。</li></ol><h4 id="findClass"><a href="#findClass" class="headerlink" title="findClass"></a>findClass</h4><p>自定义类加载器就需要实现findClass,其默认实现如下:</p><div class="hljs"><pre><code class="hljs java"><span class="hljs-keyword">protected</span> Class<?> findClass(String name) <span class="hljs-keyword">throws</span> ClassNotFoundException { <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ClassNotFoundException(name);}</code></pre></div><p>可以看出,抽象类ClassLoader的findClass函数默认是抛出异常的。而前面我们知道,loadClass在父加载器无法加载类的时候,就会调用我们自定义的类加载器中的findeClass函数,因此我们必须要在loadClass这个函数里面实现将一个指定类名称转换为Class对象。</p><p>如果是是读取一个指定的名称的类为字节数组的话,这很好办。但是如何将字节数组转为Class对象呢?很简单,Java提供了defineClass方法,通过这个方法,就可以把一个字节数组转为Class对象啦~</p><h4 id="defineClass"><a href="#defineClass" class="headerlink" title="defineClass"></a>defineClass</h4><p>将一个字节数组转为Class对象,这个字节数组是class文件读取后最终的字节数组。如,假设class文件是加密过的,则需要解密后作为形参传入defineClass函数。其默认实现如下</p><div class="hljs"><pre><code class="hljs java"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">final</span> Class<?> defineClass(String name, <span class="hljs-keyword">byte</span>[] b, <span class="hljs-keyword">int</span> off, <span class="hljs-keyword">int</span> len) <span class="hljs-keyword">throws</span> ClassFormatError { <span class="hljs-keyword">return</span> defineClass(name, b, off, len, <span class="hljs-keyword">null</span>);}<span class="hljs-keyword">protected</span> <span class="hljs-keyword">final</span> Class<?> defineClass(String name, <span class="hljs-keyword">byte</span>[] b, <span class="hljs-keyword">int</span> off, <span class="hljs-keyword">int</span> len, ProtectionDomain protectionDomain) <span class="hljs-keyword">throws</span> ClassFormatError { protectionDomain = preDefineClass(name, protectionDomain); String source = defineClassSourceLocation(protectionDomain); Class<?> c = defineClass1(<span class="hljs-keyword">this</span>, name, b, off, len, protectionDomain, source); postDefineClass(c, protectionDomain); <span class="hljs-keyword">return</span> c; }</code></pre></div><p>下面是一个classLoader简单的实现:</p><div class="hljs"><pre><code class="hljs java"> <span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyClassLoader</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ClassLoader</span></span><span class="hljs-class"></span>{ <span class="hljs-meta">@Override</span> <span class="hljs-keyword">protected</span> Class<?> findClass(String name) { String myPath = <span class="hljs-string">"data"</span> + File.separator + name.replace(<span class="hljs-string">"."</span>, File.separator) + <span class="hljs-string">".class"</span>; System.out.println(myPath); <span class="hljs-keyword">byte</span>[] cLassBytes = <span class="hljs-keyword">null</span>; Path path = <span class="hljs-keyword">null</span>; <span class="hljs-keyword">try</span> { path = Paths.get(<span class="hljs-keyword">new</span> URI(myPath)); cLassBytes = Files.readAllBytes(path); } <span class="hljs-keyword">catch</span> (IOException | URISyntaxException e) { e.printStackTrace(); } Class clazz = defineClass(name, cLassBytes, <span class="hljs-number">0</span>, cLassBytes.length); <span class="hljs-keyword">return</span> clazz; }}</code></pre></div><h2 id="jvm调优"><a href="#jvm调优" class="headerlink" title="jvm调优"></a>jvm调优</h2><h3 id="使用指定垃圾收集器指令"><a href="#使用指定垃圾收集器指令" class="headerlink" title="使用指定垃圾收集器指令"></a>使用指定垃圾收集器指令</h3><ol><li>开启Serial收集器的方式:<code>-XX:+UseSerialGC</code></li><li>开启Parallel收集器的方式: <code>-XX:+UseParallelGC -XX:+UseParallelOldGC</code></li><li>开启CMS收集器的方式: <code>-XX:+UseParNewGC -XX:+UseConcMarkSweepGC</code></li><li>开启G1收集器的方式: <code>-XX:+UseG1GC</code></li></ol><h3 id="jvm相关指令"><a href="#jvm相关指令" class="headerlink" title="jvm相关指令"></a>jvm相关指令</h3><ol><li>当虚拟机出现内存溢出时Dump出当前内存转储快照:<code>-XX:+HeapDumpOnOutOfMemoryError</code></li><li>指定虚拟机堆的最小内存为20m:<code>-Xms20m</code></li><li>指定虚拟机堆的最大内存为20m:<code>-Xmx20m</code></li><li><del>设置本地方法栈大小:<code>-Xoss</code></del></li><li>设置栈容量:<code>-Xss</code></li><li><del>设置永久代大小:<code>-XX:PermSize=10M</code> <code>-XX:MaxPermSize=10M</code></del></li><li>设置本机直接内存(DirectMemory):<code>-XX:MaxDirectMemorySize</code></li></ol><h3 id="MaxGCPauseMillis调优"><a href="#MaxGCPauseMillis调优" class="headerlink" title="MaxGCPauseMillis调优"></a>MaxGCPauseMillis调优</h3><p><code>-XX:+UseG1GC -Xmx32g -XX:MaxGCPauseMillis=200</code></p><p>该指令设置了最大内存和最大暂停时间,主要平衡长时间暂停还是吞吐量降低的问题。理论暂时时间长,吞吐量较大。暂停时间短可能导致频繁gc。</p><h3 id="其他调优参数"><a href="#其他调优参数" class="headerlink" title="其他调优参数"></a>其他调优参数</h3><p><code>-XX:G1HeapRegionSize=n</code></p><p>设置的 G1 区域的大小。值是 2 的幂,范围是 1 MB 到 32 MB 之间。目标是根据最小的 Java 堆大小划分出约 2048 个区域。</p><p><code>-XX:ParallelGCThreads=n</code></p><p>设置 STW 工作线程数的值。将 n 的值设置为逻辑处理器的数量。n 的值与逻辑处理器的数量相同,最多为 8。</p><p>如果逻辑处理器不止八个,则将 n 的值设置为逻辑处理器数的 5/8 左右。这适用于大多数情况,除非是较大的 SPARC 系统,其中 n 的值可以是逻辑处理器数的 5/16 左右。</p><p><code>-XX:ConcGCThreads=n</code></p><p>设置并行标记的线程数。将 n 设置为并行垃圾回收线程数 (ParallelGCThreads) 的 1/4 左右。</p><p><code>-XX:InitiatingHeapOccupancyPercent=45</code></p><p>设置触发标记周期的 Java 堆占用率阈值。默认占用率是整个 Java 堆的 45%。</p><p>避免使用以下参数:</p><p>避免使用 <code>-Xmn</code> 选项或 <code>-XX:NewRatio</code>等其他相关选项显式设置年轻代大小。固定年轻代的大小会覆盖暂停时间目标。</p><h3 id="触发Full-GC"><a href="#触发Full-GC" class="headerlink" title="触发Full GC"></a>触发Full GC</h3><p>在某些情况下,G1触发了Full GC,这时G1会退化使用Serial收集器来完成垃圾的清理工作,它仅仅使用单线程来完成GC工作,GC暂停时间将达到秒级别的。整个应用处于假死状态,不能处理任何请求,我们的程序当然不希望看到这些。那么发生Full GC的情况有哪些呢?</p><h4 id="并发模式失败"><a href="#并发模式失败" class="headerlink" title="并发模式失败"></a>并发模式失败</h4><p>G1启动标记周期,但在Mix GC之前,老年代就被填满,这时候G1会放弃标记周期。这种情形下,需要增加堆大小,或者调整周期(例如增加线程数<code>-XX:ConcGCThreads</code>等)。</p><h4 id="晋升失败或者疏散失败"><a href="#晋升失败或者疏散失败" class="headerlink" title="晋升失败或者疏散失败"></a>晋升失败或者疏散失败</h4><p>G1在进行GC的时候没有足够的内存供存活对象或晋升对象使用,由此触发了Full GC。可以在日志中看到(to-space exhausted)或者(to-space overflow)。解决这种问题的方式是:</p><ul><li><p>增加 <code>-XX:G1ReservePercent</code> 选项的值(并相应增加总的堆大小),为“目标空间”增加预留内存量。</p></li><li><p>通过减少<code>-XX:InitiatingHeapOccupancyPercent</code> 提前启动标记周期。</p></li><li><p>也可以通过增加 <code>-XX:ConcGCThreads</code> 选项的值来增加并行标记线程的数目。</p></li></ul><h4 id="巨型对象分配失败"><a href="#巨型对象分配失败" class="headerlink" title="巨型对象分配失败"></a>巨型对象分配失败</h4><p>当巨型对象找不到合适的空间进行分配时,就会启动Full GC,来释放空间。这种情况下,应该避免分配大量的巨型对象,增加内存或者增大-XX:G1HeapRegionSize,使巨型对象不再是巨型对象。</p>]]></content>
<categories>
<category>jvm</category>
</categories>
<tags>
<tag>java</tag>
<tag>jvm</tag>
<tag>虚拟机</tag>
</tags>
</entry>
<entry>
<title>docker常用命令</title>
<link href="/post/749ad7d8.html"/>
<url>/post/749ad7d8.html</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p> 惯例,介绍一下<strong>docker</strong></p><p> 众所周知,<strong>docker</strong>是这几年非常火爆的容器技术。</p><p> 什么是<strong>docker</strong>?</p><p> <strong>docker</strong>利用Linux核心中的资源分离机制,例如cgroups,以及Linux核心名字空间(namespaces),来创建独立的容器(containers)。这可以在单一Linux实体下运作,避免引导一个虚拟机造成的额外负担。<br>简而言之,<strong>docker</strong>利用linux资源隔离,为每一个容器创建几乎享有完整操作系统资源的空间。相比虚拟机,<strong>docker</strong>更轻量,开销更少。<br>但这还不是<strong>docker</strong>的全部,<strong>docker</strong>拥有镜像仓库<a href="https://hub.docker.com/" target="_blank" rel="noopener">docker hub</a>,极大方便<br>构造程序运行所需要的容器依赖。如果曾被ruby依赖链的噩梦所笼罩过,那么你绝对会喜欢上docker的。构建程序从未如此简单。</p><h2 id="docker基本指令"><a href="#docker基本指令" class="headerlink" title="docker基本指令"></a>docker基本指令</h2><p>使用yum进行安装</p><div class="hljs"><pre><code class="hljs bash">sudo yum updatesudo yum install docker</code></pre></div><p>服务启动</p><div class="hljs"><pre><code class="hljs bash">service docker start</code></pre></div><p>镜像检索<br><div class="hljs"><pre><code class="hljs bash">docker search 镜像名</code></pre></div></p><p>镜像下载</p><div class="hljs"><pre><code class="hljs bash">docker pull 镜像名</code></pre></div><p>镜像列表</p><div class="hljs"><pre><code class="hljs bash">docker images</code></pre></div><p>删除镜像</p><div class="hljs"><pre><code class="hljs bash">docker rmi image-id</code></pre></div><p>删除所有镜像<br><div class="hljs"><pre><code class="hljs bash">docker rmi $(docker images -q)</code></pre></div></p><p>运行镜像为容器<br><div class="hljs"><pre><code class="hljs bash">docker run --name container-name -d image-name例子: docker run --name <span class="hljs-built_in">test</span>-redis -d redis</code></pre></div></p><p>查看运行和停止状态的容器<br><div class="hljs"><pre><code class="hljs bash">docker psdocker ps -a</code></pre></div></p><p>停止容器<br><div class="hljs"><pre><code class="hljs bash">docker stop container-name/container-id</code></pre></div></p><p>启动容器<br><div class="hljs"><pre><code class="hljs bash">docker start container-name/container-id</code></pre></div></p><p>端口映射(前一个为宿主机端口,后者为容器端口)<br><div class="hljs"><pre><code class="hljs bash">docker run -d -p 6378:6379 --name port-redis redis</code></pre></div></p><p>删除容器<br><div class="hljs"><pre><code class="hljs bash">docker rm container-id</code></pre></div></p><p>删除所有容器<br><div class="hljs"><pre><code class="hljs bash">docker rm $(docker ps -a -q)</code></pre></div></p><p>容器日志<br><div class="hljs"><pre><code class="hljs bash">docker logs container-id/container-name</code></pre></div></p><p>登录容器<br><div class="hljs"><pre><code class="hljs bash">docker <span class="hljs-built_in">exec</span> -it container-id/container-name bash</code></pre></div></p><h2 id="dockerFile构建"><a href="#dockerFile构建" class="headerlink" title="dockerFile构建"></a>dockerFile构建</h2>]]></content>
<categories>
<category>docker</category>
</categories>
<tags>
<tag>docker</tag>
</tags>
</entry>
<entry>
<title>简单集成docker Jenkins gitlab 实现全自动部署blog</title>
<link href="/post/709bcd7e.html"/>
<url>/post/709bcd7e.html</url>
<content type="html"><![CDATA[<h1 id="起因"><a href="#起因" class="headerlink" title="起因"></a>起因</h1><p>  某天闲逛知乎看到一个问题 <em><a href="https://www.zhihu.com/question/29755481" target="_blank" rel="noopener">你见过的最棒的个人博客界面是什么样的?</a></em><br>当时就看了各路大神的博客,五花八门,有音乐性的,平面设计的,非常的惊艳。当时我和我的小伙伴都惊呆了。然后看到了我们广大的程序员也有很简单的博客而且颜值还很高(颜控~~)。当时就心血来潮了,下定决心自己也要搭一个。</p><h1 id="方案和技术要求"><a href="#方案和技术要求" class="headerlink" title="方案和技术要求"></a>方案和技术要求</h1><p>  经过一番调研和搜查(谷歌和百度)。制定了以下方案:</p><ol><li><strong>GithubPages</strong> 部署静态博客页面 (免费hhhhhh,毕竟我穷的很稳定╮(╯▽╰)╭ )</li><li><a href="http://link.zhihu.com/?target=https%3A//wanwang.aliyun.com/domain/">阿里云</a>购买域名(<strong>bryce-huang.fun</strong>)</li><li><strong>Hexo</strong>+<strong>NexT</strong>博客模板 高颜值和高效率,基于node.js</li><li><strong>docker</strong> +<strong>gitlab</strong> +<strong>jenkins</strong>持续集成(因为懒,写博客都懒)</li></ol><p>  由于该方案有一定的技术门槛,本文默认读者有一定的相关软件的操作知识。本文用到的相关软件如下:</p><ul><li><strong>Git</strong>和<strong>GitHub</strong>,参考<a href="http://bryce-huang.fun/post/ac774c19.html" target="_blank" rel="noopener">git的常用命令</a></li><li><strong>Hexo</strong>基本用法,参考<a href="http://bryce-huang.fun/post/33f59116.html" target="_blank" rel="noopener">hexo常用命令</a></li><li><strong>Docker</strong>基本用法,参考<a href="http://bryce-huang.fun/post/749ad7d8.html" target="_blank" rel="noopener">docker常用命令</a></li><li><strong>Linux</strong>基本命令,参考<a href="http://bryce-huang.fun/post/65cd929b.html" target="_blank" rel="noopener">linux基本命令</a></li><li><strong>Jenkins</strong>基本使用,参考<a href="https://www.yiibai.com/jenkins" target="_blank" rel="noopener">Jenkins教程</a></li></ul><p>  此外,本方案对电脑也有一定的要求,推荐16G以上内存,处理器i5八代及以上。博主使用的是虚拟机方式运行CentOS 7,再加上虚拟机运行docker 以及Gitlab和Jenkins相对较吃内存和cpu。土豪配置可以忽略,推荐使用云主机搭建。</p><p>  话不多说,有了方案就开始搭建吧!</p><h1 id="Hexo安装"><a href="#Hexo安装" class="headerlink" title="Hexo安装"></a>Hexo安装</h1><p>  博主分析过<a href="https://cn.wordpress.org/" target="_blank" rel="noopener">wordpress</a>和<a href="https://jekyllrb.com/" target="_blank" rel="noopener">jekyll</a>,由于前者太过于笨重,而后者对环境要求高(万恶的Ruby啊。。。(灬°ω°灬)),最终选择Hexo,简单,主题丰富。</p><p>  Hexo是一款基于Node.js的静态博客框架,依赖少易于安装使用,可以方便的生成静态网页托管在GitHub和Heroku上,是搭建博客的首选框架。这里博主选用的是GitHub,你没看错,全球最大的同性恋交友网站(逃……)。Hexo同时也是GitHub上的开源项目,参见:<a href="https://github.com/hexojs/hexo" target="_blank" rel="noopener">hexojs/hexo</a> ,目前该项目2.5w的star 和接近3.5k的fork如果想要更加全面的了解Hexo,可以到其官网 <a href="https://hexo.io/zh-cn/index.html" target="_blank" rel="noopener">Hexo</a> 了解更多的细节,因为Hexo的创建者是台湾人,对中文的支持很友好,可以选择中文进行查看。这里,默认各位猿/媛儿都知道GitHub就不再赘述。</p><h2 id="安装Hexo"><a href="#安装Hexo" class="headerlink" title="安装Hexo"></a>安装Hexo</h2><p>  在CentOS 7 上输入<code>npm install -g hexo-cli</code>,没有安装<strong>Node.js</strong>和<strong>npm</strong>的同学请参考<a href="https://zhuanlan.zhihu.com/p/34600831" target="_blank" rel="noopener">在 CentOS 7 上用包管理器安装 Node.js</a></p><p>  然后输入<code>hexo init blog</code> ,等待初始化blog完成</p><div class="hljs"><pre><code class="hljs bash">hexo new <span class="hljs-string">"First Blog"</span>hexo ghexo s</code></pre></div><p>  打开浏览器到输入虚拟机的ip地址加端口号4000,如:<code>192.168.1.5:4000</code><br>  well,人生的第一个博客就搭建好了,目前只能本地局域网内访问。<br>由于CentOS 7 默认开启防火墙和seLinux 安全策略,想要正常访问需要打开对应端口<br><code>firewall-cmd --zone=public --add-port=80/tcp --permanent</code><br>(–permanent永久生效,没有此参数重启后失效)后续Jenkins和Gitlab端口开放,输入此命令即可。下图为正常访问页面:</p><p><img src="/images/hexo.webp" alt="hexo 页面"></p><h2 id="安装NexT"><a href="#安装NexT" class="headerlink" title="安装NexT"></a>安装NexT</h2><p>  至此,我们搭建完了blog,但是一切都很原始,页面也不够优雅,这时我们可以选择各种各样的主题 ,可以移步<a href="https://github.com/hexojs/hexo/wiki/Themes" target="_blank" rel="noopener">主题仓库</a> ,进行选择。博主选择的主题是NexT,怎么形容呢!大概低调奢华有内涵说的就是<a href="https://theme-next.iissnan.com/" target="_blank" rel="noopener">NexT</a>吧!优雅简单,选择多,适配各种插件,语言支持度高,可以添加许多定制化功能,如:评论加载,站内搜索,打赏支持等。</p><p>  进入刚才创建的blog目录,使用<code>git clone https://github.com/iissnan/hexo-theme-next themes/next</code>命令即可安装NexT主题,此外需要在编辑blog目录下的 _config.yml文件,启用NexT主题<br><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-attr">theme:</span> <span class="hljs-string">next</span></code></pre></div><br>再重新运行<br><div class="hljs"><pre><code class="hljs bash">hexo clean hexo ghexo s</code></pre></div></p><p>浏览器刷新页面就可以看到焕然一新的博客页面,可以看到跟下面类似的页面说明安装成功</p><p><img src="/img/blog/post/other/validation-default-scheme-mac.webp" alt="NexT主题"></p><p>  更多关于个性化设置,本文就不多加描述,可以参考<a href="https://theme-next.iissnan.com/" target="_blank" rel="noopener">NexT官方文档</a><br>或者参考<a href="https://segmentfault.com/a/1190000009544924" target="_blank" rel="noopener">hexo的next主题个性化配置教程</a></p><h1 id="GitHub关联"><a href="#GitHub关联" class="headerlink" title="GitHub关联"></a>GitHub关联</h1><p>  通过上述努力,我们目前已经可以实现本地访问,美丽又好看,高雅又简洁的博客了。<br>但是想要展现给别人看,这还不够。所幸,全球最大的gay交友网站,GitHub,推出了免费且可以部署静态<br>页面的服务,利用github的服务,小伙伴们就可以轻而易举的访问到我们美丽的博客页面。</p><p>  这个是如何实现的呢?首先我们要到<a href="https://github.com/" target="_blank" rel="noopener">GitHub</a>注册一个账号,在github中<br>创建一个仓库,这个仓库名字是有规范的,仓库名应该是<code>username.github.io</code> ,如博主的就是<br><a href="https://github.com/Bryce-huang/Bryce-huang.github.io" target="_blank" rel="noopener">Bryce-huang.github.io</a>,创建完之后呢,就需要关联到咱们Hexo生成的blog上面去。首先需要生成ssh密钥上传到GitHub上,是hexo所在的机子关联哦!<br>博主就是虚拟机运行hexo的,那么就需要虚拟机生成ssh密钥。至于如何关联GitHub,这里就不在赘述,可参考<a href="https://www.bbsmax.com/A/D8547O32JE/" target="_blank" rel="noopener">生成SSH</a></p><p>然后就需要修改blog下面的_config.yml文件,修改内容参考如下:<br><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-attr">deploy:</span> <span class="hljs-attr">type:</span> <span class="hljs-string">git</span> <span class="hljs-attr">repo:</span> <span class="hljs-string">这里填入你之前在GitHub上创建仓库的完整路径,记得加上</span> <span class="hljs-string">.git</span> <span class="hljs-attr">branch:</span> <span class="hljs-string">master</span></code></pre></div></p><p>范例:<br><div class="hljs"><pre><code class="hljs yaml"><span class="hljs-attr">deploy:</span> <span class="hljs-attr">repo:</span> <span class="hljs-string">[email protected]:Bryce-huang/Bryce-huang.github.io.git</span> <span class="hljs-attr">type:</span> <span class="hljs-string">git</span> <span class="hljs-attr">branch:</span> <span class="hljs-string">master</span></code></pre></div></p><p>修改完之后就可以运行<code>hexo d</code>,这样就可以部署到gitpages上了,然后浏览器访问:username.github.io<br>如博主的<a href="https:Bryce-huang.github.io" target="_blank" rel="noopener">Bryce-huang.github.io</a> 这样我们完成github部署,小伙伴们也可以访问了我们的博客了,这不仅免费还不用备案。(✿◕‿◕✿)。如果想要显示自己定制化的域名,可以<br>购买一个自己喜欢的域名,与之绑定。<a href="http://link.zhihu.com/?target=https%3A//wanwang.aliyun.com/domain/">域名购买</a>,域名绑定参考<a href="https://juejin.im/post/5a71a4f9518825733a3105ac" target="_blank" rel="noopener">GitHub Pages自定义域名</a></p><h1 id="持续集成"><a href="#持续集成" class="headerlink" title="持续集成"></a>持续集成</h1><p>   目前为止我们已经完成了,博客安装,博客定制化页面,博客部署到GitHub。<br>可以开开心心的写博客给小伙伴们看了。可是写着写着,就发现呀!每次写完博客,都要执行一遍<br><div class="hljs"><pre><code class="hljs bash">hexo cleanhexo ghexo d</code></pre></div></p><p>  博主觉得重复的工作一直在做,都不能好好写博客了,这可不行!作为一个懒鬼,怎么可能老是重复运行这些指令呢!<br>这时候就需要我们持续集成工具箱了!Docker Jenkins GitLab,有这三款工具,我们就可以,写完代码(不对,是博客)<br>一提交就可以不用管更新博客这样重复的事情了!</p><h2 id="Docker安装"><a href="#Docker安装" class="headerlink" title="Docker安装"></a>Docker安装</h2><p>  由于docker提供镜像,避免安装过多依赖,可以一键安装相关软件。并且迁移方便,持续集成相关软件均在docker安装。<br>甚至hexo服务都可以在docker安装。由于博主一开始并没有考虑docker安装hexo,并且hexo所依赖的环境,CentOS 7本身<br>就有提供。所以对hexo在Centos 7下无需安装额外依赖。但是安装gitlab(又看到依赖狂魔Ruby了╮(╯▽╰)╭)和Jenkins确实需要额外诸多依赖,随选docker安装。</p><div class="hljs"><pre><code class="hljs bash">sudo yum updatesudo yum install dockerservice docker starts</code></pre></div><p>  只要执行上述指令,就可以安装并运行docker了,建议使用docker加速,参考:<a href="https://blog.csdn.net/u014231523/article/details/61197945" target="_blank" rel="noopener">docker国内镜像拉取和镜像加速registry-mirrors配置修改</a></p><h2 id="Docker下安装GitLab"><a href="#Docker下安装GitLab" class="headerlink" title="Docker下安装GitLab"></a>Docker下安装GitLab</h2><p>docker拉取gitlab镜像</p><p><code>docker pull gitlab/gitlab-ce</code></p><p>docker执行此条命令,其中hostname请改为自己的,若需使用本地域名访问,需要修改本地host文件,如何修改host文件请参考:<a href="https://www.cnblogs.com/mkl34367803/p/10010060.html" target="_blank" rel="noopener">centos 7 修改host文件</a><br><div class="hljs"><pre><code class="hljs bash">docker run -d \--hostname gitlab.bryce.com <span class="hljs-comment"># 指定容器域名,未知功能:创建镜像仓库的时候使用到</span>-p 443:4433 \ <span class="hljs-comment"># 将容器内443端口映射到主机4433,提供https服务</span>-p 80:8888 \ <span class="hljs-comment"># 将容器内80端口映射到主机8888,提供http服务</span>-p 22:2222 \ <span class="hljs-comment"># 将容器内22端口映射到主机2222,提供ssh服务</span>-p 9090:9090 \ <span class="hljs-comment"># 将容器内9090端口映射到主机9090,提供prometheus服务</span>--name gitlab \ <span class="hljs-comment"># 指定容器名称</span>--restart always \ <span class="hljs-comment"># 容器退出时,自动重启</span>-v /home/gitlab/config:/etc/gitlab \ <span class="hljs-comment"># 将本地/home/gitlab/config挂载到容器内/etc/gitlab</span>-v /home/gitlab/logs:/var/<span class="hljs-built_in">log</span>/gitlab \ <span class="hljs-comment"># 将本地/home/gitlab/logs挂载到容器内/var/log/gitlab</span>-v /home/gitlab/data:/var/opt/gitlab \ <span class="hljs-comment"># 将本地/home/gitlab/data挂载到容器内/var/opt/gitlab</span>gitlab/gitlab-ce:latest</code></pre></div></p><p>然后可以从浏览器访问<code>ip:8888</code>,如:<code>192.168.1.5:8888</code>访问gitlab页面,创建项目Blog项目,如下图:<br><img src="/images/gitlbab创建页面.webp" alt="创建项目"></p><p>继续将之前ssh生成的密钥添加到gitlab上,如图:<br><img src="/images/ssh.webp" alt="ssh"></p><p>到之前虚拟机blog下,初始化git,将至关联到刚才创建的项目上,并推送到远程。至此gitlab搭建完毕!</p><h2 id="Docker下安装Jenkins"><a href="#Docker下安装Jenkins" class="headerlink" title="Docker下安装Jenkins"></a>Docker下安装Jenkins</h2><p>首先执行docker拉取jenkins镜像指令</p><p><code>docker pull jenkins</code></p><p>创建jenkins主目录,为jenkins持久化</p><p><code>mkdir /home/jenkins</code></p><p>修改目录权限规则</p><p><code>chown -R 1000:1000 /home/jenkins</code></p><p>docker启动jenkins容器</p><p><code>docker run -itd -p 8080:8080 -p 50000:50000 --name jenkins --privileged=true -v /home/jenkins:/var/jenkins_home jenkins</code></p><p>其中<code>--privileged=true</code>是因为CentOS 7默认开启SELinux策略将导致jenkins无法启动,这命令赋予jenkins特权</p><p><code>-v /home/jenkins:/var/jenkins_home</code> 磁盘挂载</p><p>至此完成了docker下安装jenkins,jenkins插件需要有<code>Gitlab Hook Plugin</code>,<code>SSH plugin</code>。这两个插件是持续集成必备,其余默认安装即可。<br>更多安装jenkins疑问可参考:<a href="https://blog.csdn.net/bbwangj/article/details/80914943" target="_blank" rel="noopener">docker下安装jenkins</a></p><p>创建一个blogCI的任务,如图:<br><img src="/images/job.webp" alt="job"></p><p>进入配置页面,如图:<br><img src="/images/config.webp" alt="jenkins 配置"></p><p>我们无需托管源码,只需要jenkins帮助执行一些重复命令即可,下面配置webHook,可以自动触发jenkins构建项目<br>如图:<br><img src="/images/trigger.webp" alt="web"></p><p>点击上述箭头所指之处,生成 Secret token也就是图片中<code>260f14bd2c3505e75b5c7c143af108fa</code>这段字符串。再复制<br>GitLab webhook URL: <a href="http://192.168.1.5:8080/project/blogCI中的" target="_blank" rel="noopener">http://192.168.1.5:8080/project/blogCI中的</a> <code>http://192.168.1.5:8080/project/blogCI</code><br>点击保存job配置,到gitlab项目页面</p><p><img src="/images/Integrations.webp" alt="集成"></p><p>进入页面后,如图:</p><p><img src="/images/webhook.webp" alt="webhook"></p><p>输入刚才的GitLab webhook URL:<code>http://192.168.1.5:8080/project/blogCI</code>和Secret token:<code>260f14bd2c3505e75b5c7c143af108fa</code><br>点击test按钮,显示成功即说明配置成功!至此,就配完了gitlab,需要回到jenkins,继续编写。</p><p>直接到jenkins ,系统管理-系统配置-Publish over SSH</p><p><img src="/images/sshlinxu.webp" alt="ssh"></p><p>配置虚拟机的账号密码,然后再到刚才BlogCI任务下,进入配置,直接到 <strong>build</strong> 模块,在Exec command中输入<br><div class="hljs"><pre><code class="hljs bash"><span class="hljs-meta">#!/bin/bash</span><span class="hljs-built_in">cd</span> user/<span class="hljs-built_in">local</span>/blog <span class="hljs-comment"># 在虚拟机下的blog地址 </span>git pullhexo cleanhexo ghexo d</code></pre></div></p><p>然后点击保存即可。</p><p>至此,便完成了所有功能的配置,只需要克隆gitlab的仓库,在source/_posts,添加markdown文件,编写博客,写完后提交博客,jenkins就会自动帮助我们更新博客到github上了。<br>这下就可以安安心心的好好写博客了!</p><h1 id="坑点集合"><a href="#坑点集合" class="headerlink" title="坑点集合"></a>坑点集合</h1><p>  此处,是博主在处理相关配置的所踩的坑</p><ul><li><p><strong>docker 无法启动容器或者本身无法启动问题解决</strong> 参考:<a href="https://www.cnblogs.com/areyouready/p/9028487.html" target="_blank" rel="noopener">Error response from daemon: error creating overlay mount to /var/lib/docker/overlay2</a></p></li><li><p><strong>gitlab配置webhook,报错 url is blocked Requests to the local network are not allowed</strong> 解决办法:管理员账号登录gitlab,在Admin area中,左侧Settings -> Network -> Outbound requests,勾选Allow requests to the local network from hooks and services</p></li><li><p><strong>hexo出现中文标题,导致生成文章URL找不到问题</strong> 解决办法:<a href="https://segmentfault.com/a/1190000005799711" target="_blank" rel="noopener">hexo-abbrlink介绍</a></p></li><li><p><strong>在执行 hexo deploy 后,出现 error deployer not found:git 的错误处理</strong> 解决办法:npm install hexo-deployer-git –save</p></li></ul><p>有更多问题欢迎评论或者私信<(<em> ̄▽ ̄</em>)/</p><p><br></p><p></p><p></p><p></p><p></p>]]></content>
<categories>
<category>blog</category>
</categories>
<tags>
<tag>docker</tag>
<tag>hexo</tag>
<tag>blog</tag>
<tag>CI</tag>
<tag>jenkins</tag>
<tag>gitlab</tag>
</tags>
</entry>
<entry>
<title>oracle Sql 优化</title>
<link href="/post/7174208d.html"/>
<url>/post/7174208d.html</url>
<content type="html"><![CDATA[<h2 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h2><p>在开发初期,数据量较小,对于sql查询语句优化并不会那么急迫.但随着日积月累的数据,数据库承担的压力越来越大,数据搜索时间也越来越长.<br>对查询性能要求也变的迫在眉睫,对于数据库优化,可以有集群,分表分库,读写分离,索引等,但体现在最低端被无数次重复使用的还是程序员们写的sql,一个不成熟的程序员写sql可能极大的浪费数据库的资源,最直观的就是浪费电啊!!!所以为了环保,为了世界,编写出高效的sql是志在必得的.</p><ol><li><p>尽量不要在 where 子句里面使用 is null 和 is not null,会导致数据库弃用索引.<br>低效: (索引失效)</p><div class="hljs"><pre><code class="hljs sqL"><span class="hljs-keyword">SELECT</span> …<span class="hljs-keyword">FROM</span> DEPARTMENT<span class="hljs-keyword">WHERE</span> DEPT_CODE <span class="hljs-keyword">IS</span> <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>;</code></pre></div><div class="hljs"><pre><code class="hljs SQL">高效: (索引有效)<span class="hljs-keyword">SELECT</span> …<span class="hljs-keyword">FROM</span> DEPARTMENT<span class="hljs-keyword">WHERE</span> DEPT_CODE >=<span class="hljs-number">0</span>;</code></pre></div></li><li><p>不要连接列,如: name||’’||id=’ss 222’;</p></li><li><p>截取函数不要使用在列上 如 substr(name,2);</p></li><li><p>通配符%,不要在词首出现(%SS%),在其他位置时可以利用索引(ss%)</p></li><li><p>任何在 Order by 语句的非索引项或者有计算表达式都将降低查询速度,应避免在order by 子句中使用表达式</p></li><li><p>尽量不要使用not和 <>关键字,可以由下列改写,可以使用索引提高查询效率,<br>select * from employee where salary<>3000;<br>对这个查询,可以改写为不使用 NOT:</p></li></ol><div class="hljs"><pre><code class="hljs sql"><span class="hljs-keyword">select</span> * <span class="hljs-keyword">from</span> employee <span class="hljs-keyword">where</span> salary<<span class="hljs-number">3000</span> <span class="hljs-keyword">or</span> salary><span class="hljs-number">3000</span>;</code></pre></div><ol start="7"><li><p>优先使用<code>not exists</code> 效率比在<code>not in</code>高,原因是<code>not in</code> 无法使用索引,主表大用in,子查询大用exists</p></li><li><p>ORACLE优化器(没懂):<br>ORACLE 的优化器共有 3 种:<br>a. RULE (基于规则) b. COST (基于成本) c. CHOOSE (选择性)<br>设置缺省的优化器,可以通过对 init.ora 文件中 OPTIMIZER_MODE 参数的各种声明,如<br>RULE,COST,CHOOSE,ALL_ROWS,FIRST_ROWS . 你当然也在 SQL 句级或是会话(session)级对<br>其进行覆盖.<br>为了使用基于成本的优化器(CBO, Cost-Based Optimizer) , 你必须经常运行 analyze 命令,以增<br>加数据库中的对象统计信息(object statistics)的准确性.<br>如果数据库的优化器模式设置为选择性(CHOOSE),那么实际的优化器模式将和是否运行过<br>analyze 命令有关. 如果 table 已经被 analyze 过, 优化器模式将自动成为 CBO , 反之,数据库将采用<br>RULE 形式的优化器.<br>在缺省情况下,ORACLE 采用 CHOOSE 优化器, 为了避免那些不必要的全表扫描(full table<br>scan) , 你必须尽量避免使用 CHOOSE 优化器,而直接采用基于规则或者基于成本的优化器.</p></li></ol><ol start="9"><li><p>访问数据表,基于全表扫描,通过rowid访问表,rowid 访问表速度更快</p></li><li><p>SQl共享(存疑),一般没有特殊的访问限制,两个相同的sql可以共享,要求字符串空格都一致(严格)</p></li><li><p>oracle 表数据比较少的放在FROM子句最后,因为ORACLE处理FROM子句的时候,是从右往左进行查询的,此外多表查询(三张以上),</p></li><li><p>WHERE子句,表连接条件需要写在其他条件之前,由于ORACLE采取自下而上的顺序解析,过滤数据多的条件应该放在最后面.</p></li><li><p>禁止在列中使用<em>,这将是因为</em> 需要替换成表中各列,这个动作需要ORACLE去查询数据字典,所以是低效的</p></li><li><p>尽量在一条sql处理尽可能多的业务,当然也要分析具体场景</p></li><li><p>尽量使用decode()减少数据库重复的查询</p></li><li><p>最高效的删除重复记录方法 ( 因为使用了 ROWID)</p><div class="hljs"><pre><code class="hljs SQL"><span class="hljs-keyword">DELETE</span> <span class="hljs-keyword">FROM</span> EMP E<span class="hljs-keyword">WHERE</span> E.ROWID > (<span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">MIN</span>(X.ROWID)<span class="hljs-keyword">FROM</span> EMP X<span class="hljs-keyword">WHERE</span> X.EMP_NO = E.EMP_NO);</code></pre></div></li><li><p>尽量多使用 COMMIT,可以释放一些资源<br>COMMIT 所释放的资源:<br>a. 回滚段上用于恢复数据的信息.<br>b. 被程序语句获得的锁<br>c. redo log buffer 中的空间<br>d. ORACLE 为管理上述 3 种资源中的内部花费</p></li><li><p>和一般的观点相反, count(*) 比 count(1)稍快 , 当然如果可以通过索引检索,对索引列的计数仍旧是最快的. 例如 COUNT(EMPNO)</p></li><li><p>尽量避免使用having子句,改用where子句替代,sql 运行顺序,FROM –> WHERE –>GROUP BY –>HAVING –>SELECT –>ORDER BY</p></li><li><p>在利用子查询的时候,尽量减少对表的查询次数如:</p></li></ol><div class="hljs"><pre><code class="hljs SQL"><span class="hljs-keyword">SELECT</span> TAB_NAME<span class="hljs-keyword">FROM</span> <span class="hljs-keyword">TABLES</span><span class="hljs-keyword">WHERE</span> (TAB_NAME,DB_VER)= ( <span class="hljs-keyword">SELECT</span> TAB_NAME,DB_VER)<span class="hljs-keyword">FROM</span> TAB_COLUMNS<span class="hljs-keyword">WHERE</span> <span class="hljs-keyword">VERSION</span> = <span class="hljs-number">604</span>)<span class="hljs-keyword">UPDATE</span> EMP<span class="hljs-keyword">SET</span> (EMP_CAT, SAL_RANGE)= (<span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">MAX</span>(<span class="hljs-keyword">CATEGORY</span>) , <span class="hljs-keyword">MAX</span>(SAL_RANGE)<span class="hljs-keyword">FROM</span> EMP_CATEGORIES)<span class="hljs-keyword">WHERE</span> EMP_DEPT = <span class="hljs-number">0020</span>;</code></pre></div><ol start="21"><li><p>使用表的别名</p></li><li><p>用 EXISTS 替换 DISTINCT,当只需要外部表来过滤信息的时候.</p><div class="hljs"><pre><code class="hljs sql"><span class="hljs-keyword">SELECT</span> DEPT_NO,DEPT_NAME<span class="hljs-keyword">FROM</span> DEPT D<span class="hljs-keyword">WHERE</span> <span class="hljs-keyword">EXISTS</span> ( <span class="hljs-keyword">SELECT</span> ‘X’<span class="hljs-keyword">FROM</span> EMP E<span class="hljs-keyword">WHERE</span> E.DEPT_NO = D.DEPT_NO);</code></pre></div></li><li><p>定期的重构索引是有必要的.</p><div class="hljs"><pre><code class="hljs SQL"><span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">INDEX</span> <INDEXNAME> <span class="hljs-keyword">REBUILD</span> <TABLESPACENAME></code></pre></div></li><li><p>避免在索引列计算<br>低效:</p></li></ol><div class="hljs"><pre><code class="hljs SQL"><span class="hljs-keyword">SELECT</span> …<span class="hljs-keyword">FROM</span> DEPT<span class="hljs-keyword">WHERE</span> SAL * <span class="hljs-number">12</span> > <span class="hljs-number">25000</span>;</code></pre></div><p>高效:<br><div class="hljs"><pre><code class="hljs SQL"><span class="hljs-keyword">SELECT</span> …<span class="hljs-keyword">FROM</span> DEPT<span class="hljs-keyword">WHERE</span> SAL > <span class="hljs-number">25000</span>/<span class="hljs-number">12</span>;</code></pre></div></p><ol start="25"><li>用>=替代><br>如果 DEPTNO 上有一个索引,<br>高效:<div class="hljs"><pre><code class="hljs SQL"><span class="hljs-keyword">SELECT</span> *<span class="hljs-keyword">FROM</span> EMP<span class="hljs-keyword">WHERE</span> DEPTNO >=<span class="hljs-number">4</span></code></pre></div></li></ol><p>低效:<br><div class="hljs"><pre><code class="hljs SQL"><span class="hljs-keyword">SELECT</span> *<span class="hljs-keyword">FROM</span> EMP<span class="hljs-keyword">WHERE</span> DEPTNO ><span class="hljs-number">3</span><span class="hljs-string">``</span><span class="hljs-string">` </span><span class="hljs-string"></span><span class="hljs-string">主要区别在于第一个直接定位到4,而第二个直接定位到3,再去找比3大的数</span><span class="hljs-string"></span><span class="hljs-string"></span><span class="hljs-string">26. 索引分为单例索引和复合索引,只有复合索引的第一个列被where子句引用时,优化器才会选择该索引</span><span class="hljs-string"></span><span class="hljs-string">27. 使用提示</span><span class="hljs-string">如果一个大表没有被设定为缓存(CACHED)表而你希望它的数据在查询结束是仍然停留在</span><span class="hljs-string">SGA 中,你就可以使用 CACHE hint 来告诉优化器把数据保留在 SGA 中. 通常 CACHE hint 和</span><span class="hljs-string">FULL hint 一起使用.</span><span class="hljs-string">例如:</span><span class="hljs-string">`</span><span class="hljs-string">``</span><span class="hljs-keyword">SQL</span><span class="hljs-keyword">SELECT</span> <span class="hljs-comment">/*+ FULL(WORKER) CACHE(WORKER)*/</span> * <span class="hljs-keyword">FROM</span> <span class="hljs-keyword">WORK</span>;</code></pre></div></p><ol start="28"><li><p>用when替代order by<br>order by 子句只在两种严格的条件下使用索引<br>order by 中所有的列必须包含在相同的索引中并保持在索引的中排列顺序<br>order by 中所有的列必须定义为非空<br>where 子句使用的索引和order by子句中所使用的索引不能并列</p></li><li><p>索引只能告诉你什么在表里,不能告诉你什么不再表里,所以一般!=,<>等不要使用,这将导致索引无法生效.不要在索引列进行运算(重复)</p></li><li><p>避免使用耗费资源的操作<br>带有 DISTINCT,UNION,MINUS,INTERSECT,ORDER BY 的 SQL 语句会启动 SQL 引擎<br>执行耗费资源的排序(SORT)功能. DISTINCT 需要一次排序操作, 而其他的至少需要执行两次<br>排序.<br>例如,一个 UNION 查询,其中每个查询都带有 GROUP BY 子句, GROUP BY 会触发嵌入排序<br>(NESTED SORT) ; 这样, 每个查询需要执行一次排序, 然后在执行 UNION 时, 又一个唯一排序<br>(SORT UNIQUE)操作被执行而且它只能在前面的嵌入排序结束后才能开始执行. 嵌入的排序的深<br>度会大大影响查询的效率.<br>通常, 带有 UNION, MINUS , INTERSECT 的 SQL 语句都可以用其他方式重写</p></li><li><p>分离表和索引<br>总是将你的表和索引建立在不同的表空间内(TABLESPACES). 决不要将不属于 ORACLE 内<br>部系统的对象存放到 SYSTEM 表空间里. 同时,确保数据表空间和索引表空间置于不同的硬盘上</p></li></ol>]]></content>
<categories>
<category>database</category>
</categories>
<tags>
<tag>oracle</tag>
<tag>sql</tag>
</tags>
</entry>
<entry>
<title>Java基础笔记</title>
<link href="/post/6f706555.html"/>
<url>/post/6f706555.html</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>该文章是我在复习Java,所记录的一些知识点,主要是通过《Java编程思想》这一本书,所记录的。所记录的知识点,大都是我不知道或者是接触少容易忘记的,所以在此记录一下,以便查阅。如果对你也有帮助,那就是我莫大的荣幸了。</p><h3 id="操作符"><a href="#操作符" class="headerlink" title="操作符"></a>操作符</h3><h4 id="按位操作符"><a href="#按位操作符" class="headerlink" title="按位操作符"></a>按位操作符</h4><p>操作符(&,按位‘与’) :如果两个输入位都是1,则生成一个输出位1;</p><p>操作符(|,按位‘或’):如果两个输入位只要有一个是1,则生成一个输出位1;</p><p>操作符(^,按位‘异或’):如果输入位的某一个是1,但不全都是1,,则生成一个输出位1</p><p>操作符(~,按位‘非’):生成与输入位相反——若输入0,则输出1,若输入1,则输出0</p><h4 id="位移操作符"><a href="#位移操作符" class="headerlink" title="位移操作符"></a>位移操作符</h4><p>左移操作符(<<): 能按照操作符右侧指定的位数将操作符左边的操作数向左移动(在低位补0)</p><p>“有符号” 右移操作符(>>): 则按照操作符右侧指定的位数将操作符左边的操作数向右移动。“有符号”的意思是指:若符号为正,则在高位补0,符号为负,则在高位补1</p><p>“无符号” 右移操作符(>>>): 无论符号正负,都在高位补0</p><p>(<<=或>>=或>>>=)表示将得到的结果赋值给左边的变量(对byte和short可能出现误差,原因是需要先转为int再做位移运算)</p><h3 id="enum"><a href="#enum" class="headerlink" title="enum"></a>enum</h3><p>在switch语句中可以使用枚举类</p><h3 id="final关键字"><a href="#final关键字" class="headerlink" title="final关键字"></a>final关键字</h3><ol><li><p>final属性(域):当数据为基本类型的时候,被final声明的数据被初始化后无法被修改,当数据位对象时,final保证其引用(内存地址)无法被修改,但是对象的值仍可修改。final数据只能在定义处或者在构造器中被赋值。此举保证了数据被使用前可以得到正确初始化。</p></li><li><p>final参数:在方法参数声明中添加final,这意味着你无法在方法中修改参数引用所指向的对象。(意思就是说:参数指向一个对象,对象指向一个引用,你仍然可以通过参数修改对象的内容,但是无法修改参数指向,比如重新new 一个对象给这个参数)</p></li><li><p>final方法:将被final修饰的方法锁定,以防任何继承类修改它的含义。将final和private进行对比,private更具安全性。</p></li><li><p>final类:当final修饰类时,该类将无法被继承,且该类的方法都被隐式指定为final,String类为什么无法修改其值?原因在于底层数组用了private final,也是做过了特殊处理。<br>此外可以通过反射修改String的数组哦!</p></li></ol><h3 id="类的初始化顺序"><a href="#类的初始化顺序" class="headerlink" title="类的初始化顺序"></a>类的初始化顺序</h3><ol><li>父类静态(代码块,变量赋值二者按顺序执行)</li><li>子类静态</li><li>父类构造代码块</li><li>父类构造方法</li><li>子类构造代码块</li><li>子类构造方法</li></ol><h3 id="Foreach与迭代器"><a href="#Foreach与迭代器" class="headerlink" title="Foreach与迭代器"></a>Foreach与迭代器</h3><p>任何类只要实现了Iterator接口,就可以使用foreach语法。</p><h3 id="异常"><a href="#异常" class="headerlink" title="异常"></a>异常</h3><h4 id="修改异常的发生地"><a href="#修改异常的发生地" class="headerlink" title="修改异常的发生地"></a>修改异常的发生地</h4><p>直接抛出异常不会修改原来的信息,调用异常对象的fillInStackTrace()再抛出,可以修改异常发生的地点。</p><h4 id="异常链"><a href="#异常链" class="headerlink" title="异常链"></a>异常链</h4><p>在捕获一个异常后再抛出另外一个异常,常常想保存原来的异常信息,这就叫异常链。<br>有三个异常类的构造器可以接受cause参数,分别是Error,Exception,RuntimeException。如果其他类型则需要调用initCause().</p><h4 id="异常使用指南"><a href="#异常使用指南" class="headerlink" title="异常使用指南"></a>异常使用指南</h4><p>应该在下列情况下使用异常:</p><ul><li>在恰当的级别处理问题。(在知道该如何处理的情况下才捕获异常)</li><li>解决问题并且重新调用产生异常的方法。</li><li>进行少许修补,然后绕过异常发生的地方继续执行。</li><li>用别的数据进行计算,以代替方法预计会返回的值。</li><li>把当前的运行环境下能做的事情尽量做完,然后把相同的异常重抛到更高层。</li><li>把当前的运行环境下能做的事情尽量做完,然后把不同的异常抛到更高层。</li><li>终止程序。</li><li>进行简化。(如果你的异常模式使问题变得太复杂,那用起来会非常痛苦也烦人)</li><li>让类库和程序更安全。</li></ul><h3 id="字符串"><a href="#字符串" class="headerlink" title="字符串"></a>字符串</h3><p>String重载了操作符‘+’,在做‘+’运算时,编译器会自动使用StringBuilder进行处理。</p><h4 id="正则匹配"><a href="#正则匹配" class="headerlink" title="正则匹配"></a>正则匹配</h4>]]></content>
<categories>
<category>Java</category>
</categories>
<tags>
<tag>java</tag>
<tag>基础</tag>
</tags>
</entry>
<entry>
<title>linux基本命令</title>
<link href="/post/65cd929b.html"/>
<url>/post/65cd929b.html</url>
<content type="html"><![CDATA[<h3 id="Linux基本操作"><a href="#Linux基本操作" class="headerlink" title="Linux基本操作"></a>Linux基本操作</h3><p>查看当前目录下的所有文件(包含隐藏文件)-l 查看目录</p><div class="hljs"><pre><code class="hljs bash">ls -al ~</code></pre></div><p>退出当前登陆</p><div class="hljs"><pre><code class="hljs bash"><span class="hljs-built_in">exit</span></code></pre></div><p>显示日期 +%Y/%m/%d-%H:%M:%S<br><div class="hljs"><pre><code class="hljs bash">date</code></pre></div><br>显示目前所支持的语言<br><div class="hljs"><pre><code class="hljs bash"><span class="hljs-built_in">echo</span> <span class="hljs-variable">$LANG</span></code></pre></div><br>设置语言为英语<br><div class="hljs"><pre><code class="hljs bash">LANG=en_US</code></pre></div><br>显示日历 cal 2019 某年 cal 2 2019 某年某月<br><div class="hljs"><pre><code class="hljs bash">cal</code></pre></div><br>进入计算器程序,若没有此命令,请使用yum install bc 安装,exit退出bc,scale=3后面保留三位小数,默认显示整数。<br><div class="hljs"><pre><code class="hljs bash">bc</code></pre></div><br>命令提示,文件提示<br><div class="hljs"><pre><code class="hljs bash">Tab</code></pre></div><br>打断当前程序,退出<br><div class="hljs"><pre><code class="hljs bash">Ctrl+C</code></pre></div><br>结束输入或者离开当前登录账户<br><div class="hljs"><pre><code class="hljs bash">Ctrl+D</code></pre></div></p><p>进入指定命令的详细说明页面,如:man date。<br><div class="hljs"><pre><code class="hljs bash">man <span class="hljs-built_in">command</span></code></pre></div></p><p>向下翻一页<br><div class="hljs"><pre><code class="hljs bash">Page Dowm</code></pre></div></p><p>向上翻一页<br><div class="hljs"><pre><code class="hljs bash">Page Up</code></pre></div></p><p>回到第一页<br><div class="hljs"><pre><code class="hljs bash">Home</code></pre></div></p><p>最后一页<br><div class="hljs"><pre><code class="hljs bash">End</code></pre></div></p><p>向下查找字符串<br><div class="hljs"><pre><code class="hljs bash">String</code></pre></div></p><p>向上查找字符串<br><div class="hljs"><pre><code class="hljs bash">?String</code></pre></div></p><p>在查找字符串时,n顺向查找,N反向查找<br><div class="hljs"><pre><code class="hljs bash">n,N</code></pre></div></p><p>在man page中可以退出<br><div class="hljs"><pre><code class="hljs bash">q</code></pre></div></p><p>查询关于manual的信息<br><div class="hljs"><pre><code class="hljs bash">man -f man</code></pre></div></p><p>新建test目录 -p可以创建多层目录结构-m可以设置权限<br><div class="hljs"><pre><code class="hljs bash">mkdir <span class="hljs-built_in">test</span></code></pre></div></p><p>切换到cherbini账号<br><div class="hljs"><pre><code class="hljs bash">su cherbini</code></pre></div></p><p>新建空的文件<br><div class="hljs"><pre><code class="hljs bash">touch <span class="hljs-built_in">test</span>/<span class="hljs-built_in">test</span></code></pre></div></p><p>删除文件 -r 可以强制删除目录下的东西<br><div class="hljs"><pre><code class="hljs bash">rm <span class="hljs-built_in">test</span></code></pre></div></p><p>查看内核版本<br><div class="hljs"><pre><code class="hljs bash">uname -r</code></pre></div></p><p>查看当前发行的linux版本信息<br><div class="hljs"><pre><code class="hljs bash">lsb_release -a</code></pre></div></p><p>显示当前目录,-p可以显示完整的目录<br><div class="hljs"><pre><code class="hljs bash"><span class="hljs-built_in">pwd</span></code></pre></div></p><p>删除一个空的目录 -p可以删除上层空目录<br><div class="hljs"><pre><code class="hljs bash">rmdir</code></pre></div></p><p>查看网络状态<br><div class="hljs"><pre><code class="hljs bash">netstat -a</code></pre></div></p><p>查看ip地址<br><div class="hljs"><pre><code class="hljs bash">ifconfig -a</code></pre></div></p><p>打印,显示<br><div class="hljs"><pre><code class="hljs bash"><span class="hljs-built_in">echo</span></code></pre></div><br>添加path路径<br><div class="hljs"><pre><code class="hljs bash">PATH=<span class="hljs-string">"<span class="hljs-variable">$PATH</span>"</span>:/root</code></pre></div></p><p>将文件移动到指定目录下<br><div class="hljs"><pre><code class="hljs bash">mv /bin/ls /root</code></pre></div><br>取得最后的文件名<br><div class="hljs"><pre><code class="hljs bash">basename /etc/sysconfig/network</code></pre></div><br>取得最后的目录名<br><div class="hljs"><pre><code class="hljs bash">dirname /etc/sysconfig/network</code></pre></div><br>从第一行开始显示<br><div class="hljs"><pre><code class="hljs bash">cat</code></pre></div><br>从最后一行开始显示<br><div class="hljs"><pre><code class="hljs bash">tac</code></pre></div><br>显示文件同时显示行号<br><div class="hljs"><pre><code class="hljs bash">nl</code></pre></div><br>一页一页的显示内容<br><div class="hljs"><pre><code class="hljs bash">more</code></pre></div><br>跟more类似,可以往前翻页<br><div class="hljs"><pre><code class="hljs bash">less</code></pre></div><br>只看头几行<br><div class="hljs"><pre><code class="hljs bash">head</code></pre></div><br>只看结尾几行<br><div class="hljs"><pre><code class="hljs bash">tail</code></pre></div><br>以二进制形式读取文件内容<br><div class="hljs"><pre><code class="hljs bash">od</code></pre></div><br>修改密码<br><div class="hljs"><pre><code class="hljs bash">passwd</code></pre></div><br>显示储存使用情况<br><div class="hljs"><pre><code class="hljs bash">df -h</code></pre></div><br>查看命令<br><div class="hljs"><pre><code class="hljs bash"><span class="hljs-built_in">type</span> -tpa name</code></pre></div></p><p>查看某文件倒数的几行数据</p><div class="hljs"><pre><code class="hljs bash">tail -f <span class="hljs-built_in">log</span>/mongo.logtail filename</code></pre></div><h3 id="nano编辑器"><a href="#nano编辑器" class="headerlink" title="nano编辑器"></a>nano编辑器</h3><p>打开指定文件<br><div class="hljs"><pre><code class="hljs bash"> nano text.txt ``` 保存文件```bashCtrl+O</code></pre></div></p><h3 id="关机"><a href="#关机" class="headerlink" title="关机"></a>关机</h3><p>查看后台的程序<br><div class="hljs"><pre><code class="hljs bash">ps -aux</code></pre></div></p><p>将内存未更新的数据写入硬盘<br><div class="hljs"><pre><code class="hljs bash">sync</code></pre></div></p><p>20秒后关机<br><div class="hljs"><pre><code class="hljs bash">/sbin/shutdown -t 20 <span class="hljs-string">'i will shutdown after 20s‘</span></code></pre></div><br>立刻关机<br><div class="hljs"><pre><code class="hljs bash">shutdown -h now</code></pre></div><br>立刻重启<br><div class="hljs"><pre><code class="hljs bash">shutdown -r now</code></pre></div><br>在20 点25分关机<br><div class="hljs"><pre><code class="hljs bash">shutdown -h 20:25</code></pre></div><br>一分钟后重启<br><div class="hljs"><pre><code class="hljs bash">shutdown -r +1 ‘the system will reboot’</code></pre></div><br>取消关机计划<br><div class="hljs"><pre><code class="hljs bash">shutdown -c</code></pre></div></p><p>发出警告信息,不会关机<br><div class="hljs"><pre><code class="hljs bash">shutdown -k now ’this system will reboot‘</code></pre></div><br>断电关机<br><div class="hljs"><pre><code class="hljs bash">poweroff -f</code></pre></div><br>死机<br><div class="hljs"><pre><code class="hljs bash">halt -f</code></pre></div><br>(run level 0) 关机<br><div class="hljs"><pre><code class="hljs bash">init 0</code></pre></div><br>(run level 3)命令行模式<br><div class="hljs"><pre><code class="hljs bash">init 3</code></pre></div><br>(run level 5) 含有图形页模式<br><div class="hljs"><pre><code class="hljs bash">init 5</code></pre></div><br>(run level 6 )重启<br><div class="hljs"><pre><code class="hljs bash">init 6</code></pre></div></p><h3 id="文件权限"><a href="#文件权限" class="headerlink" title="文件权限"></a>文件权限</h3><p>将test.txt用户组改为cherbini<br><div class="hljs"><pre><code class="hljs bash">chgrp cherbini test.txt</code></pre></div><br>将test.txt所有者改为cherbini<br><div class="hljs"><pre><code class="hljs bash">chown cherbini text.txt</code></pre></div><br>复制文件<br><div class="hljs"><pre><code class="hljs bash">cp srcFile targetFile</code></pre></div><br>文件或者目录 修改文件权限<br><em>-R会将子目录文件也会执行同样的修改xyz 为数字,r:4,w:2,x:1<br>此外chmod还有另外一种修改方式<br>chmod u=rwx,g-rwx,o+rwx test.txx<br>u:user,g:group,o:other,a:all r:read,w:write,x:excute<br>=:设置,+:添加,-除去</em><br><div class="hljs"><pre><code class="hljs bash">chmod -R xyz</code></pre></div><br>设置默认的目录文件权限<br><div class="hljs"><pre><code class="hljs bash"><span class="hljs-built_in">umask</span> 002</code></pre></div><br>设置该属性后无法删除或者 修改数据,只能增加数据<br><div class="hljs"><pre><code class="hljs bash">chattr -a <span class="hljs-built_in">test</span></code></pre></div><br>无法被删除,改名,设置连接,写入或者添加数据<br><div class="hljs"><pre><code class="hljs bash">chattr -i <span class="hljs-built_in">test</span></code></pre></div><br>显示文件的隐藏属性<br><div class="hljs"><pre><code class="hljs bash">lsattr <span class="hljs-built_in">test</span></code></pre></div></p><h3 id="目录与路径"><a href="#目录与路径" class="headerlink" title="目录与路径"></a>目录与路径</h3><p>代表当前目录<br><div class="hljs"><pre><code class="hljs bash">.</code></pre></div><br>代表上层目录0<br><div class="hljs"><pre><code class="hljs bash">..</code></pre></div><br>代表上一个工作目录<br><div class="hljs"><pre><code class="hljs bash">-</code></pre></div><br>代表当前身份所在主文件夹<br><div class="hljs"><pre><code class="hljs bash">~</code></pre></div><br>代表某个账户所在的主文件夹<br><div class="hljs"><pre><code class="hljs bash">~account</code></pre></div></p><h3 id="VI与VIM编辑器"><a href="#VI与VIM编辑器" class="headerlink" title="VI与VIM编辑器"></a>VI与VIM编辑器</h3><p>vi设置行号<br><div class="hljs"><pre><code class="hljs bash">:<span class="hljs-built_in">set</span> nu</code></pre></div><br>撤销上一次编辑<br><div class="hljs"><pre><code class="hljs bash">u</code></pre></div><br>移动到58行<br><div class="hljs"><pre><code class="hljs bash">58G</code></pre></div><br>向左移动40<br><div class="hljs"><pre><code class="hljs bash">40—></code></pre></div><br>从第一行到50 行,用w2替换w1<br><div class="hljs"><pre><code class="hljs bash">:1,50s/word1/word2/gc</code></pre></div><br>向下复制九行<br><div class="hljs"><pre><code class="hljs bash">9yy</code></pre></div><br>粘贴<br><div class="hljs"><pre><code class="hljs bash">p</code></pre></div><br>光标上一行粘贴<br><div class="hljs"><pre><code class="hljs bash">P</code></pre></div><br>删除22行<br><div class="hljs"><pre><code class="hljs bash">22dd</code></pre></div><br>删除同行的15个字符<br><div class="hljs"><pre><code class="hljs bash">15x</code></pre></div><br>插入模式<br><div class="hljs"><pre><code class="hljs bash">i</code></pre></div><br>新增一行<br><div class="hljs"><pre><code class="hljs bash">o</code></pre></div><br>退出当前模式<br><div class="hljs"><pre><code class="hljs bash">esc</code></pre></div><br>写入文件并退出<br><div class="hljs"><pre><code class="hljs bash">wq</code></pre></div><br>强制写入并退出<br><div class="hljs"><pre><code class="hljs bash">w!q</code></pre></div><br>强制退出<br><div class="hljs"><pre><code class="hljs bash">q!</code></pre></div></p><p>后台执行vim,回到命令行<br><div class="hljs"><pre><code class="hljs bash">ctrl+z</code></pre></div><br>行选择<br><div class="hljs"><pre><code class="hljs bash">V</code></pre></div><br>字符选择<br><div class="hljs"><pre><code class="hljs bash">v</code></pre></div><br>复制选择<br><div class="hljs"><pre><code class="hljs bash">y</code></pre></div><br>删除选择<br><div class="hljs"><pre><code class="hljs bash">d</code></pre></div><br>编辑的文件信息<br><div class="hljs"><pre><code class="hljs bash">:files</code></pre></div><br>编辑下一个文件<br><div class="hljs"><pre><code class="hljs bash">:n</code></pre></div><br>编辑上一个文件</p><div class="hljs"><pre><code class="hljs bash">:N</code></pre></div><h3 id="bash与shell-script"><a href="#bash与shell-script" class="headerlink" title="bash与shell script"></a>bash与shell script</h3><p>使其他bash都可以使用这个变量 ,自定义变量转成环境变量<br><div class="hljs"><pre><code class="hljs bash"><span class="hljs-built_in">export</span> name</code></pre></div><br>查看环境变量<br><div class="hljs"><pre><code class="hljs bash">env</code></pre></div><br>取得10内的随机数<br><div class="hljs"><pre><code class="hljs bash"><span class="hljs-built_in">declare</span> -i number=<span class="hljs-variable">$RANDOM</span>*10/32768; <span class="hljs-built_in">echo</span> <span class="hljs-variable">$number</span></code></pre></div></p><h3 id="防火墙"><a href="#防火墙" class="headerlink" title="防火墙"></a>防火墙</h3><p>添加开放端口号<br><div class="hljs"><pre><code class="hljs bash">firewall-cmd --zone=public --add-port=80/tcp --permanent (--permanent永久生效,没有此参数重启后失效)</code></pre></div></p><p>重新载入防火墙<br><div class="hljs"><pre><code class="hljs bash">firewall-cmd --reload</code></pre></div></p><p>查看80端口号<br><div class="hljs"><pre><code class="hljs bash">firewall-cmd --zone= public --query-port=80/tcp</code></pre></div><br>移除端口号<br><div class="hljs"><pre><code class="hljs bash">firewall-cmd --zone= public --remove-port=80/tcp --permanent</code></pre></div></p><h3 id="tomcat"><a href="#tomcat" class="headerlink" title="tomcat"></a>tomcat</h3><p>开启tomcat<br><div class="hljs"><pre><code class="hljs bash">./startup.sh</code></pre></div></p><p>关闭tomcat<br><div class="hljs"><pre><code class="hljs bash">./shutdown.sh</code></pre></div></p><p>tomcat 启动日志<br><div class="hljs"><pre><code class="hljs bash">tail -f ../logs/catalina.out</code></pre></div><br>启动详情<br><div class="hljs"><pre><code class="hljs bash">ps -ef|grep tomcat</code></pre></div><br>解压缩tomcat<br><div class="hljs"><pre><code class="hljs bash">tar -zxvf apache-tomcat-8.5.30.tar.gz</code></pre></div></p><p>解压缩,一般放在 /usr/local</p>]]></content>
<categories>
<category>OS</category>
</categories>
<tags>
<tag>OS</tag>
<tag>LINUX</tag>
</tags>
</entry>
<entry>
<title>设计模式</title>
<link href="/post/364ea8cc.html"/>
<url>/post/364ea8cc.html</url>
<content type="html"><![CDATA[<h2 id="设计模式感悟"><a href="#设计模式感悟" class="headerlink" title="设计模式感悟"></a>设计模式感悟</h2><h3 id="学习经过"><a href="#学习经过" class="headerlink" title="学习经过"></a>学习经过</h3><p>博主看了《head first 设计模式》和《大话设计模式》,这两本书只是简单讲明白了什么是设计模式,并没有特别经典的实战案例,为不小遗憾。对此我这两本书并不推荐,只是入门建议参考菜鸟教程<a href="http://www.runoob.com/design-pattern/design-pattern-tutorial.html" target="_blank" rel="noopener">设计模式</a>,博主学习下面设计的模式的代码已提交<a href="https://github.com/a1046149428/basics_of_programming/tree/master/src/main/java/design/pattern" target="_blank" rel="noopener">basics_of_programming</a></p><h3 id="为什么使用设计模式?"><a href="#为什么使用设计模式?" class="headerlink" title="为什么使用设计模式?"></a>为什么使用设计模式?</h3><p><strong>模式:是在某种情境(context)下,针对某问题的某种解决方案</strong>这个是老生常谈了 ,良好的设计模式可以帮助我们,更优雅和高效的编写代码,低耦合高内聚。比如面向接口编程,可以使得我们的代码拥有更佳的扩展性。 对修改关闭,对扩展开放的设计原则,不仅使用我们的代码优雅也更安全。各种设计模式都有各自优点,这里重点推荐下,动态代理和观察者模式,在很多框架中都实现了这些设计模式,一般面试也是就这两个吧!</p><h3 id="后续学习计划"><a href="#后续学习计划" class="headerlink" title="后续学习计划"></a>后续学习计划</h3><p>仅仅在这两本书学习到的设计模式知识非常有限,并没有太多实战价值,在现实中,很多需要用到设计模式的场景是十分复杂的,需要很高的编程技艺。这个只能通过阅读大佬们的源代码,看看别人为什么这么设计了,比如jdk和spring源码,有时间还是研读一番。jdk已经使用了很多设计模式,spring框架级源码也是非常有价值的。工作中能使用设计模式进行优化的,就尽量进行优化!</p><h2 id="初步学习设计模式"><a href="#初步学习设计模式" class="headerlink" title="初步学习设计模式"></a>初步学习设计模式</h2><ol><li>面对对象Object Oriented=面向对象分析OOA+面向对象设计OOD+面向对象编程OOP</li><li>在面向对象的概念中,我们知道所有对象都是通过类来描述的,但反过来却不是,并不是所有类都是用来描述对象的,如果一个类没有包含足够的信息去描述对象,这样的类就是抽象类.</li><li>ISP Interface Segregation Principle</li><li><p>继承是 is-a的关系,接口是like-a的关系,以继承来表明拥有什么样的本质,用接口来表明有什么样的功能</p></li><li><p>适配器模式<br>类适配器:就是不愿意修改现在的接口但又有其他的功能(已存在)要加上,那一般可以通过继承原来的类的,并且实现目标接口</p></li><li><p>外观模式:外观模式定义了一个子系统的一组接口集成在一起的高层接口以提供一个一致的界面.通过这个界面,其他系统可以方便的调用子系统的功能,而<br>忽略子系统内部的变化.<br>注意事项:再设计外观模式时不需要增加额外的功能.不要从外观方法中直接返回子系统中的组件给客户.应用外观的目的是提供一个高层次的接口,不是底层细节处理.</p></li><li><p>组合模式<br>组合多个对象形成树形结构以表示具有“整体—部分”关系的层次结构。<br>组合模式对单个对象(即叶子对象)和组合对象(即容器对象)的使用具有一致性,组合模式又可以称为“整体—部分”(Part-Whole)模式,它是一种对象结构型模式。<br>简单来说就是把简单对象假想成符合对象(实际没有该功能),但是依旧提供一致的接口方法(共同实现统一的接口),分安全模式和透明模式,透明模式是所有对象行为一致,<br>但是不安全.</p></li><li><p>桥接模式<br>是一种结构型设计模式。Bridge模式基于类的最小设计原则,通过使用封装、聚合及继承等行为让不同的类承担不同的职责。<br>它的主要特点是把抽象(Abstraction)与行为实现(Implementation)分离开来,从而可以保持各部分的独立性以及应对他们的功能扩展。<br>通过解耦把功能进行简化,然后共同组成一个功能,功能可以选择不同的参数.<br>比如一只蜡笔,是有颜色+毛笔,这样颜色的大小和毛笔的大小耦合在一起了,倘若有毛笔+颜料,这样颜料就不和笔的大小耦合在一起了,需要笔的数目也少了<br>通常我们用</p></li><li><p>责任链模式<br>责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。<br>这种类型的设计模式属于行为型模式。<br>在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。<br>(找个时间实现一下)</p></li><li><p>单例模式<br>单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。<br>这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。</p></li><li><p>观察者模式<br>当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知它的依赖对象。观察者模式属于行为型模式。<br>(找个时间实现一下)</p></li><li><p>中介模式<br>中介者模式(Mediator Pattern)是用来降低多个对象和类之间的通信复杂性。这种模式提供了一个中介类,<br>该类通常处理不同类之间的通信,并支持松耦合,使代码易于维护。中介者模式属于行为型模式。</p></li><li><p>代理模式<br>顺应开闭原则(对扩展开放,对修改关闭)<br>在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。<br>在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。(Java动态代理实现原理,重点)</p></li><li><p>享元模式<br>享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式。<br>享元模式尝试重用现有的同类对象,如果未找到匹配的对象,则创建新对象。我们将通过创建 5 个对象来画出 20 个分布于不同位置的圆来演示这种模式。<br>由于只有 5 种可用的颜色,所以 color 属性被用来检查现有的 Circle 对象。(没怎么懂)</p></li><li><p>建造者模式<br>建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。<br>一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的。</p></li><li><p>简单工厂模式<br>工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。<br>在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。</p></li></ol><h2 id="Head-First-设计模式"><a href="#Head-First-设计模式" class="headerlink" title="Head First 设计模式"></a>Head First 设计模式</h2><h3 id="设计原则"><a href="#设计原则" class="headerlink" title="设计原则"></a>设计原则</h3><p><strong>总则:任何设计都要在抽象和速度之间取舍,在空间和时间之间平衡.</strong></p><ol><li><p>找出应用中可能需要变化之处,把它<br>们独立出来,不要和那些不 需要变化<br>的代码混在一起。</p></li><li><p>针对接口编程,而不是针对实现编程</p></li><li><p>多用组合,少用继承</p></li><li><p>为了交互对象之间的松耦合设计而努力。</p></li><li><p>依赖倒置原则,要依赖抽象,不依赖具体类.</p></li><li><p>最少知识原则:不管任何对象,都需要注意和它交互的有哪些类,并注意是如何交互的.不要让太多类耦合在一起.<br>即在方法调用时,只在方法内调用一层而不推荐调用多层,即getId(){user.getID()}</p></li><li><p>好莱坞原则:别调用我,我会调用你.底层组件可以挂钩在高层组件中,但是由高层组件决定调用时间以及是否调用,低级组件不得直接调用高层组件</p></li><li><p>单一责任原则:一个类应该只有一个引起变化的原因</p></li></ol><h3 id="设计模式"><a href="#设计模式" class="headerlink" title="设计模式"></a>设计模式</h3><h4 id="一、策略模式"><a href="#一、策略模式" class="headerlink" title="一、策略模式"></a>一、策略模式</h4><p>定义了算法族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户.</p><p><strong>优点:</strong></p><ol><li><p>算法可以自由切换。</p></li><li><p>避免使用多重条件判断。</p></li><li><p>扩展性良好。</p></li></ol><p><strong>缺点:</strong><br>1.策略类会增多。<br>2.所有策略类都需要对外暴露。</p><p>使用场景:<br>1.如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。<br>2.一个系统需要动态地在几种算法中选择一种。<br>3.如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。<br>注意事项:如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题。</p><h4 id="二、观察者模式"><a href="#二、观察者模式" class="headerlink" title="二、观察者模式"></a>二、观察者模式</h4><p>定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖都会收到通知并且自动更新.JDK内置了观察者模式,具体为Observer接口和Observable类</p><h4 id="三、装饰者模式"><a href="#三、装饰者模式" class="headerlink" title="三、装饰者模式"></a>三、装饰者模式</h4><p>动态地将责任附加到对象上.若要扩展功能,装饰者提供了比继承更有弹性的替代方案.<br>java IO采用装饰者模式</p><h4 id="四、工厂方法模式"><a href="#四、工厂方法模式" class="headerlink" title="四、工厂方法模式"></a>四、工厂方法模式</h4><p>定义了一个创建对象接口,但由子类决定实例化的类是哪一个.工厂方法类把实例化推迟到子类.</p><h4 id="五、抽象工厂模式"><a href="#五、抽象工厂模式" class="headerlink" title="五、抽象工厂模式"></a>五、抽象工厂模式</h4><p>提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类.</p><h4 id="六、单例模式"><a href="#六、单例模式" class="headerlink" title="六、单例模式"></a>六、单例模式</h4><p>确保一个类只有一个实例,并提供一个全局访问点.</p><h4 id="七、命令模式"><a href="#七、命令模式" class="headerlink" title="七、命令模式"></a>七、命令模式</h4><p>将”请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象.命令模式也支持可撤销的操作.</p><h4 id="八、适配器模式"><a href="#八、适配器模式" class="headerlink" title="八、适配器模式"></a>八、适配器模式</h4><p>将一个类的接口,转换成客户期望的另一个接口.适配器让原本接口不兼容的类可以合作无间.</p><h4 id="九、外观模式"><a href="#九、外观模式" class="headerlink" title="九、外观模式"></a>九、外观模式</h4><p>提供一个统一的接口,用来访问子系统中的一群接口.外观定义了一个高层接口,让子系统更容易使用.</p><h4 id="十、模板方法模式"><a href="#十、模板方法模式" class="headerlink" title="十、模板方法模式"></a>十、模板方法模式</h4><p>在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中.模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤.</p><h4 id="十一、迭代器模式"><a href="#十一、迭代器模式" class="headerlink" title="十一、迭代器模式"></a>十一、迭代器模式</h4><p>提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示.</p><h4 id="十二、组合模式"><a href="#十二、组合模式" class="headerlink" title="十二、组合模式"></a>十二、组合模式</h4><p>允许你将对象组合成树形来表现”整体/部分”层次解构.组合能让客户以一致的方式处理个别对象以及组合对象.</p><h4 id="十三、状态模式"><a href="#十三、状态模式" class="headerlink" title="十三、状态模式"></a>十三、状态模式</h4><p>允许对象在内部状态改变时改变的它的行为,对象看起来好像修改了它的类。</p><h4 id="十四、-代理模式"><a href="#十四、-代理模式" class="headerlink" title="十四、 代理模式"></a>十四、 代理模式</h4><p>为另一个对象提供一个替身或者占位符以控制对这个对象的访问。</p><h4 id="十五、-复合模式"><a href="#十五、-复合模式" class="headerlink" title="十五、 复合模式"></a>十五、 复合模式</h4><p>复合模式结合两个或以上的模式,组成一个解决方案,解决一再发生的一般性问题。</p>]]></content>
<categories>
<category>设计模式</category>
</categories>
<tags>
<tag>java</tag>
<tag>design</tag>
</tags>
</entry>
<entry>
<title>hexo常用命令</title>
<link href="/post/33f59116.html"/>
<url>/post/33f59116.html</url>
<content type="html"><![CDATA[<p>新建文章<br><div class="hljs"><pre><code class="hljs bash">hexo n <span class="hljs-string">"我的博客"</span> == hexo new <span class="hljs-string">"我的博客"</span></code></pre></div><br>生成静态文件<br><div class="hljs"><pre><code class="hljs bash">hexo g == hexo generate</code></pre></div><br>启动服务预览<br><div class="hljs"><pre><code class="hljs bash">hexo s == hexo server</code></pre></div><br>部署<br><div class="hljs"><pre><code class="hljs bash">hexo d == hexo deploy</code></pre></div><br>Hexo会监视文件变动并自动更新,无须重启服务器<br><div class="hljs"><pre><code class="hljs bash">hexo server</code></pre></div><br>静态模式<br><div class="hljs"><pre><code class="hljs bash"> hexo server -shexo server -p 5000 <span class="hljs-comment">#更改端口</span></code></pre></div></p><p>自定义 IP<br><div class="hljs"><pre><code class="hljs bash">hexo server -i 192.168.1.1</code></pre></div></p><p>清除缓存,若是网页正常情况下可以忽略这条命令hexo n “我的博客” == hexo new “我的博客” #新建文章<br><div class="hljs"><pre><code class="hljs bash">hexo clean</code></pre></div></p>]]></content>
<categories>
<category>tools</category>
</categories>
<tags>
<tag>hexo</tag>
</tags>
</entry>
<entry>
<title>git的常用命令</title>
<link href="/post/ac774c19.html"/>
<url>/post/ac774c19.html</url>
<content type="html"><![CDATA[<p>查看当前路径<br><div class="hljs"><pre><code class="hljs bash"><span class="hljs-built_in">pwd</span></code></pre></div></p><p>在当前目录下创建test文件夹<br><div class="hljs"><pre><code class="hljs bash">mkdir <span class="hljs-built_in">test</span></code></pre></div></p><p>初始化当前文件git夹,声明为空的git仓库,初始化为master版本<br><div class="hljs"><pre><code class="hljs bash">git init</code></pre></div></p><p>添加git全局邮箱<br><div class="hljs"><pre><code class="hljs bash">git config --global user.email <span class="hljs-string">"[email protected]"</span></code></pre></div><br>添加全局用户名<br><div class="hljs"><pre><code class="hljs bash">git config --global user.name <span class="hljs-string">"cherbini"</span></code></pre></div></p><p>添加文件到git仓库<br><div class="hljs"><pre><code class="hljs bash">git add readme.txt</code></pre></div><br>提交文件命令,-m是指此次提交的说明<br><div class="hljs"><pre><code class="hljs bash">git commit -m <span class="hljs-string">"提交"</span></code></pre></div><br>查看仓库当前状态<br><div class="hljs"><pre><code class="hljs bash">git status</code></pre></div><br>查看文件不同的内容变化情况<br><div class="hljs"><pre><code class="hljs bash">git diff readme.txt</code></pre></div><br>减少输出信息<br><div class="hljs"><pre><code class="hljs bash">git <span class="hljs-built_in">log</span> 查看版本更新记录 --pretty=oneline</code></pre></div><br>回退到上个版本 HEAD^^上上个 HEAD~100 往上100个版本 –hard 后接部分版本号可以恢复到指定版本二<br><div class="hljs"><pre><code class="hljs bash">git reset --hard HEAD^</code></pre></div><br>查看文件内容<br><div class="hljs"><pre><code class="hljs bash">cat readme.txt</code></pre></div><br>查看命令历史<br><div class="hljs"><pre><code class="hljs bash">git reflog</code></pre></div><br>命令可以查看工作区和版本库里面最新版本的区别<br><div class="hljs"><pre><code class="hljs bash">git diff HEAD -- readme.txt</code></pre></div><br>撤销文件在工作区的修改,或者还原文件<br><div class="hljs"><pre><code class="hljs bash">git checkout -- readme.txt</code></pre></div><br>可以把暂存区的修改撤销掉(unstage),重新放回工作区<br><div class="hljs"><pre><code class="hljs bash">git reset HEAD <file></code></pre></div><br>删除文件<br><div class="hljs"><pre><code class="hljs bash">git rm test.txt</code></pre></div><br>生成ssh秘钥<br><div class="hljs"><pre><code class="hljs bash">ssh-keygen -t rsa -C <span class="hljs-string">"[email protected]"</span></code></pre></div><br>将关联远程仓库<br><div class="hljs"><pre><code class="hljs bash">git remote add origin [email protected]:michaelliao/learngit.git</code></pre></div><br>将远程仓库同步到本地<br><div class="hljs"><pre><code class="hljs bash">git pull --rebase origin master</code></pre></div><br>将本地推送到远程仓库 -u可以关联本地和远程仓库,第一次提交<br><div class="hljs"><pre><code class="hljs bash">git push -u origin master</code></pre></div><br>查看当前目录文件列表(有什么文件)<br><div class="hljs"><pre><code class="hljs bash">ls</code></pre></div><br>从github上clone 项目<br><div class="hljs"><pre><code class="hljs bash">git <span class="hljs-built_in">clone</span> [email protected]:用户名/项目名.git</code></pre></div><br>创建并切换到dev<br><div class="hljs"><pre><code class="hljs bash">git checkout -b dev</code></pre></div><br>创建dev分支<br><div class="hljs"><pre><code class="hljs bash">git branch dev</code></pre></div><br>切换到dev分支<br><div class="hljs"><pre><code class="hljs bash">git checkout dev</code></pre></div><br>查看当前分支,会显示所有分支<br><div class="hljs"><pre><code class="hljs bash">git branch -a</code></pre></div><br>合并dev分支<br><div class="hljs"><pre><code class="hljs bash">git merge dev</code></pre></div><br>删除dev分支<br><div class="hljs"><pre><code class="hljs bash">git branch -d dev</code></pre></div><br>查看分支合并情况<br>(解决冲突就是把Git合并失败的文件手动编辑为我们希望的内容,再提交。)<br><div class="hljs"><pre><code class="hljs bash">git <span class="hljs-built_in">log</span> --graph --pretty=oneline --abbrev-commit</code></pre></div><br>禁用fast forward,会在merge时生成一个新的commit<br><div class="hljs"><pre><code class="hljs bash">git merge --no-ff -m <span class="hljs-string">"merge with no-ff"</span> dev</code></pre></div><br>将所有未提交的修改都保存起来,用于后续恢复当前工作目录<br><div class="hljs"><pre><code class="hljs bash">git stash</code></pre></div><br>恢复之前缓存的工作目录,删除git堆栈中的stash<br><div class="hljs"><pre><code class="hljs bash">git stash pop stash@{0}</code></pre></div><br>恢复指定的缓存的工作目录,但不删除git堆栈的stash<br><div class="hljs"><pre><code class="hljs bash">git stash apply stash@{0}</code></pre></div><br>查看git 堆栈中的stash列表<br><div class="hljs"><pre><code class="hljs bash">git stash list</code></pre></div><br>移除指定stash<br><div class="hljs"><pre><code class="hljs bash">git stash drop stash@{0}</code></pre></div><br>查看stash的不同<br><div class="hljs"><pre><code class="hljs bash">git stash show stash@{0}</code></pre></div><br>从stash创建分支</p><p>(默认情况下,<code>git stash</code>会缓存下列文件:<br>添加到暂存区的修改(staged changes)<br>Git跟踪的但并未添加到暂存区的修改(unstaged changes)</p><p>但不会缓存以下文件:<br>在工作目录中新的文件(untracked files)<br>被忽略的文件(ignored files)<br><code>git stash</code>命令提供了参数用于缓存上面两种类型的文件。使用<code>-u</code>或者<code>--include-untracked</code>可以<code>stash untracked</code>文件。<br>使用<code>-a</code>或者<code>--all</code>命令可以stash当前目录下的所有修改。</p><div class="hljs"><pre><code class="hljs bash">git stash branch newbranch</code></pre></div><p>至于<code>git stash</code>的其他命令建议参考<a href="https://git-scm.com/docs/user-manual.html" target="_blank" rel="noopener">Git manual</a>。)</p><p>显示远程仓库信息<br><div class="hljs"><pre><code class="hljs bash">git remote -v</code></pre></div><br>查看提交记录<br><div class="hljs"><pre><code class="hljs bash">git <span class="hljs-built_in">log</span> --graph --pretty=oneline --abbrev-commit</code></pre></div><br>形成可读性极高的标签<br><div class="hljs"><pre><code class="hljs bash">git tag v0.1 95b7600</code></pre></div><br>创建带说明的标签<br><div class="hljs"><pre><code class="hljs bash">git tag -a v0.1 -m <span class="hljs-string">"version 0.1 released"</span> 1094adb</code></pre></div><br>查看所有标签<br><div class="hljs"><pre><code class="hljs bash">git tag</code></pre></div><br>删除指定标签<br><div class="hljs"><pre><code class="hljs bash">git tag -d v0.1</code></pre></div><br>推送标签到远程<br><div class="hljs"><pre><code class="hljs bash">git push origin v0.2</code></pre></div><br>删除远程标签<br><div class="hljs"><pre><code class="hljs bash">git push origin :refs/tags/v0.2</code></pre></div><br>推送全部未推送过的标签<br><div class="hljs"><pre><code class="hljs bash">git push origin --tags</code></pre></div><br>查看忽略规则<br><div class="hljs"><pre><code class="hljs bash">git check-ignore -v sads.class</code></pre></div><br>自定义命令<br><div class="hljs"><pre><code class="hljs bash">git config --global alias.st status ( $ git config --global alias.co checkout $ git config --global alias.cm commit $ git config --global alias.br branch git config --global alias.unstage <span class="hljs-string">'reset HEAD'</span> git config --global alias.last <span class="hljs-string">'log -1'</span> git config --global alias.lg <span class="hljs-string">"log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"</span> )</code></pre></div></p><p>删除远程分支<br><div class="hljs"><pre><code class="hljs bash">git push origin --delete <branchName></code></pre></div><br>新建远程分支<br><div class="hljs"><pre><code class="hljs bash">git push --<span class="hljs-built_in">set</span>-upstream origin branch_name</code></pre></div></p>]]></content>
<categories>
<category>tools</category>
</categories>
<tags>
<tag>git</tag>
</tags>
</entry>
<entry>
<title>nginx的简单使用</title>
<link href="/post/64088825.html"/>
<url>/post/64088825.html</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>在工作上需要持续集成,采用nginx+docker+Jenkins的形势部署.这里简单介绍nginx的使用,以及着重介绍nginx的代理,转发以及重定向.</p><h2 id="NGINX介绍"><a href="#NGINX介绍" class="headerlink" title="NGINX介绍"></a>NGINX介绍</h2><p>由于NGINX良好性能,单位时间内请求访问量大,被大量公司应用,是当下非常流行的一款web服务器,开箱即用.也是非常方便,这里不介绍nginx细节.<br>我这里只介绍一般打包前端代码到nginx容器发布的流程,以及nginx的conf文件的配置.</p><h2 id="nginx容器部署流程"><a href="#nginx容器部署流程" class="headerlink" title="nginx容器部署流程"></a>nginx容器部署流程</h2><p>只需要将编译好的前端代码放到nignx运行即可,不过多追究nginx的调优以及性能问题.<br>从git仓库clone项目,编写Dockerfile与nginx.conf配置文件:</p><p>Dockerfile:</p><div class="hljs"><pre><code class="hljs shell">FROM nginxLABEL maintainer "[email protected]"ADD build/ /usr/share/nginx/htmlADD nginx.conf /etc/nginxEXPOSE 80</code></pre></div><p>nginx.conf:</p><div class="hljs"><pre><code class="hljs shell">events{ worker_connections 1024;}http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; include /etc/nginx/vhost/*.conf; index index.html; root /usr/share/nginx/html; server{ listen 80; server_name nginx; location / { root /usr/share/nginx/html; } location /bryce { proxy_pass http://192.168.1.7; } location /huang { rewrite ^/huang/(.*)$ http://192.168.1.7/$1; } }}</code></pre></div><p>需要注意Dockerfile的相对路径,上面的nginx.conf文件简单写了常用的功能: 代理和重定向.而且可以在匹配路径中使用正则表达式,这一点非常重要,如果使用灰度发布,可以使用正则表达式将小部分流量切出.</p><p>使用Dockerfile构建,使用jenkins构建过程中尽量将一些数据变量化,如Jenkins构建job的名称与镜像名称一致,镜像tag参数化,使得构建脚本尽量通用,这样就不需要一个构建任务写一份构建脚本了,就算需要修改也不用改那么多,这也是编写程序的思想,将通用的抽出来放到一处,修改的时候只需要修改一个地方就可以了.</p><p>将构建好的镜像推送到镜像仓库,删除镜像,ssh到部署机器,判断镜像和容器是否在运行,可以通过shell和grep表达式获取容器或镜像id.</p><p>存在则停止并删除容器和镜像,pull镜像并运行.就这么简单就可以实现nginx的转发和代理.下面讲下nginx常用的路径跳转.</p><h2 id="nginx常用的路径跳转以及rewrite的使用"><a href="#nginx常用的路径跳转以及rewrite的使用" class="headerlink" title="nginx常用的路径跳转以及rewrite的使用"></a>nginx常用的路径跳转以及rewrite的使用</h2><p>1、假设要把<code>bryce/static/index.html</code>访问重定向到<code>static/index.html</code></p><p>例如当我们通过浏览器访问<code>http://192.168.1.7/bryce/static/index.html</code>,实际访问的是web目录下面的<code>static/index.html</code>文件,也及去掉了bryce这个目录,使用alias,我们假设static在根目录<code>/</code>下.</p><div class="hljs"><pre><code class="hljs shell">location ^~ /bryce/ { alias /;}</code></pre></div><p><strong>注意:</strong></p><ol><li>使用alias时,目录名后面一定要加”/“。</li><li>alias可以指定任何名称。</li><li>alias在使用正则匹配时,必须捕捉要匹配的内容并在指定的内容处使用。</li><li>alias只能位于location块中,有多级目录也是可以的比如将<code>/bryce/</code> 换成<code>/bryce/huang</code>,只要访问对应的url都是可以访问到相同的资源;</li></ol><p>2、把对<code>bryce/static/index.html</code>的访问重定向到web目录下面的test目录下</p><div class="hljs"><pre><code class="hljs shell">location ~ ^/bryce/ { root /test/;}</code></pre></div><p><code>http://192.168.1.7/bryce/static/index.html</code>实际访问的是web目录下<code>/test/bryce/static/index.html</code><br>及使用root一般是把访问目录重定向到某个目录下,但是访问的路径必须在重新定位的目录下</p><p>注意区分跟alias的区别</p><h2 id="扩展内容"><a href="#扩展内容" class="headerlink" title="扩展内容"></a>扩展内容</h2><p><code>www.bryce.fun/image</code> 自动跳转到 <code>www.bryce.fun/make/image</code><br>这个如何写</p><p>这种需求有多种方法可以实现:</p><ol><li>利用Nginx rewrite 内部跳转实现:</li></ol><div class="hljs"><pre><code class="hljs shell">location /image { rewrite ^/image/(.*)$ /make/image/$1 last;}</code></pre></div><ol start="2"><li>利用alias映射</li></ol><div class="hljs"><pre><code class="hljs shell">location /image { alias /make/image; #这里写绝对路径}</code></pre></div><ol start="3"><li>利用root映射:</li></ol><div class="hljs"><pre><code class="hljs shell">location /image { root /make;}</code></pre></div><ol start="4"><li>利用nginx的permanent 301绝对跳转实现</li></ol><div class="hljs"><pre><code class="hljs shell">location /image { rewrite ^/image/(.*)$ www.bryce.fun/make/image/$1;}</code></pre></div><ol start="5"><li>判断uri实现</li></ol><div class="hljs"><pre><code class="hljs shell">if ( $request_uri ~* ^(/image)){ rewrite ^/image/(.*)$ /make/image/$1 last;}</code></pre></div>]]></content>
<categories>
<category>服务器</category>
</categories>
<tags>
<tag>NGINX</tag>
<tag>部署</tag>
<tag>服务器</tag>
</tags>
</entry>
<entry>
<title>如何通过shell和grep获取docker容器或镜像的id</title>
<link href="/post/e9568317.html"/>
<url>/post/e9568317.html</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>由于在工作上,好几次被这个命令坑了,所以在这里记录下.虽然被坑了,但是也因此对linux的命令有进一步认识,尤其打开了grep和awk的大门.</p><h2 id="什么是grep"><a href="#什么是grep" class="headerlink" title="什么是grep"></a>什么是grep</h2><p>Linux grep 命令用于查找文件里符合条件的字符串。</p><p>grep 指令用于查找内容包含指定的范本样式的文件,如果发现某文件的内容符合所指定的范本样式,预设 grep 指令会把含有范本样式的那一列显示出来。若不指定任何文件名称,或是所给予的文件名为 -,则 grep 指令会从标准输入设备读取数据。</p><p>入门参考:<a href="https://www.runoob.com/linux/linux-comm-grep.html" target="_blank" rel="noopener">菜鸟教程:grep命令</a></p><h2 id="什么是awk"><a href="#什么是awk" class="headerlink" title="什么是awk"></a>什么是awk</h2><p>awk是一个强大的文本分析工具,相对于grep的查找,sed的编辑,awk在其对数据分析并生成报告时,显得尤为强大。简单来说awk就是把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行各种分析处理。</p><p>awk有3个不同版本: awk、nawk和gawk,未作特别说明,一般指gawk,gawk 是 AWK 的 GNU 版本。</p><p>awk其名称得自于它的创始人 Alfred Aho 、Peter Weinberger 和 Brian Kernighan 姓氏的首个字母。实际上 AWK 的确拥有自己的语言: AWK 程序设计语言 , 三位创建者已将它正式定义为“样式扫描和处理语言”。它允许您创建简短的程序,这些程序读取输入文件、为数据排序、处理数据、对输入执行计算以及生成报表,还有无数其他的功能。</p><p>入门参考:<a href="http://www.ruanyifeng.com/blog/2018/11/awk.html" target="_blank" rel="noopener">awk入门教程–阮一峰</a></p><h2 id="通过grep和awk获取相关docker容器id"><a href="#通过grep和awk获取相关docker容器id" class="headerlink" title="通过grep和awk获取相关docker容器id"></a>通过grep和awk获取相关docker容器id</h2><p>通常,在编写构建脚本和运维脚本中需要获取宿主机动态运行的docker容器id和镜像id,这些id在每一个宿主机是不一样的,但是我们往往会使用<code>--name</code> 去命名容器,镜像也有名称和tag,利用这些命名,加上构建脚本的变量,我们可以很简单就写出重用性高的构建脚本.</p><p>这里再唠叨一下,通过名称匹配也会有风险,就是命名过于相似的时候,通常命名的会在特定容器上有特定的后缀,避免同shell匹配的时候出错,如果删除了不该删除的文件或者容器,尤其是生产环境,如果出错就麻烦大了,linux上可没有<code>ctrl+z</code>,所以不要过于迷恋这种通过匹配查找的脚本,上线之前需要再三测试.</p><h3 id="获得docker容器id"><a href="#获得docker容器id" class="headerlink" title="获得docker容器id"></a>获得docker容器id</h3><p>通常写法:</p><div class="hljs"><pre><code class="hljs shell">containerId= `docker ps -a| grep bryce-demo | awk '{print $1}' `</code></pre></div><p>上述代码通过先通过<code>docker ps -a</code> 获取docker容器列表,再通过grep 获取与bryce-demo相关的容器,通过awk 打印第一个字段,也就是容器id.</p><p>这个写法有些不妥就是在grep匹配出多条的时候出现问题,所以下面展示一个更细粒度的写法</p><div class="hljs"><pre><code class="hljs shell">containerId= `docker ps -a| grep bryce-demo | head -1|awk '{print $1}' `</code></pre></div><p>是的,这里就加了一个<code>head -1</code> 获取第一条数据,相反还有<code>tail -1</code>获取最后一条,当存在多条时,可以自己调整.</p><p>第二种写法,也是docker官方提供的:</p><div class="hljs"><pre><code class="hljs shell">containerId=`docker ps -aq --filter name=bryce-demo`</code></pre></div><p>这个同样会出现当name能匹配多个的实现多个容器id的问题</p><h3 id="获得docker镜像id"><a href="#获得docker镜像id" class="headerlink" title="获得docker镜像id"></a>获得docker镜像id</h3><p>修改docker容器很多时候伴随着镜像的修改</p><div class="hljs"><pre><code class="hljs shell">imageId=`docker images |grep -i $dockerHub/$namespace/$jobname:$tag | head -1 |awk '{print $3}' `</code></pre></div><p>这个跟获取容器id的方式差不多,主要是将相应的参数变量化以期能将脚本通用化。</p><p>第二种,写法也是docker 官方提供的:</p><div class="hljs"><pre><code class="hljs shell">imageId=`docker images -q --filter reference=$dockerHub/$namespace/$jobname:$tag`</code></pre></div><p>这个同样可以获取想要的镜像id</p><h2 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h2><p>这里记录一下通过awk获取maven的pom.xml的tag数据</p><div class="hljs"><pre><code class="hljs shell">tag=`awk -v RS="</*docker.image.tag>" 'NR==2{print}' pom.xml`</code></pre></div><p>其余命令详见:<a href="https://docs.docker.com/engine/reference/commandline/ps/#show-both-running-and-stopped-containers" target="_blank" rel="noopener">docker官方文档</a></p>]]></content>
<categories>
<category>shell</category>
</categories>
<tags>
<tag>docker</tag>
<tag>shell</tag>
<tag>linux</tag>
</tags>
</entry>
</search>