-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrss.xml
More file actions
579 lines (561 loc) · 87.1 KB
/
Copy pathrss.xml
File metadata and controls
579 lines (561 loc) · 87.1 KB
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
<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Mingxin's Blog RSS Feed]]></title><description><![CDATA[A personal website of Mingxin]]></description><link>https://mingxinwei.github.io</link><generator>GatsbyJS</generator><lastBuildDate>Mon, 11 May 2026 14:44:14 GMT</lastBuildDate><item><title><![CDATA[一次http debug经历]]></title><description><![CDATA[最近有同事反馈,调用服务A的内部接口会偶现超时错误。于是开始查出错的原因。 首先了解了具体情况,调用方的设置的超时时间是2s;错误是偶发的;超时之后重试一般都会成功。 再往服务里加了一些debug日志,最后查出来是这个服务A调用另一个服务B…]]></description><link>https://mingxinwei.github.io/An HTTP Debug Experience/</link><guid isPermaLink="false">https://mingxinwei.github.io/An HTTP Debug Experience/</guid><pubDate>Fri, 22 May 2020 18:20:31 GMT</pubDate><content:encoded><p>最近有同事反馈,调用服务A的内部接口会偶现超时错误。于是开始查出错的原因。</p>
<p>首先了解了具体情况,调用方的设置的超时时间是2s;错误是偶发的;超时之后重试一般都会成功。</p>
<p>再往服务里加了一些debug日志,最后查出来是这个服务A调用另一个服务B的时候会超时。这个调用设置的超时时间是5s,所以这个请求超时之前,那边2s的请求就已经先报超时错误了。这是错误的表象,深层的原因还需要去服务B里查看。</p>
<p>在服务B里查看对应的日志,发现这些超时的请求都会报出一个<code class="language-text">Error: Parse Error</code>:</p>
<div class="gatsby-highlight" data-language="text"><pre class="language-text"><code class="language-text">Error: Parse Error
at socketOnEnd (_http_server.js:455:20)
at Socket.emit (events.js:203:15)
at Socket.EventEmitter.emit (domain.js:448:20)
at endReadableNT (_stream_readable.js:1129:12)
at process._tickCallback (internal/process/next_tick.js:63:19)</code></pre></div>
<p>查看对应的代码<a href="https://github.com/nodejs/node/blob/v10.16.0/lib/_http_server.js#L455-L461">_http_server.js</a>,这个错误是由client超时关闭socket,server在处理socket end事件时释放HTTP parser出错导致的。</p>
<p>这说明HTTP请求的解析失败,可能是server收到了一个错误格式的HTTP请求。不过这里我有了另一个疑问,server在一个无法解析的HTTP请求上为什么没有马上报错,而是hang up直到5s之后超时?</p>
<p>所以怀疑是A服务的HTTP请求库 <a href="https://github.com/request/request">request</a>会偶尔发送一个错误格式的HTTP请求过去。在github找到一个可能相关的request的<a href="https://github.com/request/request/issues/2047">issue</a>,尤其是里面有一个<a href="https://github.com/request/request/issues/2047#issuecomment-471077041">comment</a>和我遇到的现象很相似。不过这些都是猜测,要证明是request的问题,还是需要找到一个错误格式的HTTP请求的例子。</p>
<p>因为server在接受到HTTP请求的时候无法解析hang up了,不能在HTTP协议这一层去获取到数据。所以尝试在TCP层去输出数据。多亏Node.js是事件驱动的特性,输出socket的数据只需要在socket上增加一个data事件的listener:</p>
<div class="gatsby-highlight" data-language="js"><pre class="language-js"><code class="language-js"><span class="token keyword">const</span> Koa <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'koa'</span><span class="token punctuation">)</span>
<span class="token keyword">const</span> app <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Koa</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token keyword">const</span> server <span class="token operator">=</span> http<span class="token punctuation">.</span><span class="token function">createServer</span><span class="token punctuation">(</span>app<span class="token punctuation">.</span><span class="token function">callback</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">listen</span><span class="token punctuation">(</span>config<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'port'</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
server<span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span><span class="token string">'connection'</span><span class="token punctuation">,</span> <span class="token parameter">socket</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
socket<span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span><span class="token string">'data'</span><span class="token punctuation">,</span> <span class="token parameter">d</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'%d socket data: %s'</span><span class="token punctuation">,</span> Date<span class="token punctuation">.</span><span class="token function">now</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span>d<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// stringify是为了输出日志到一行</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre></div>
<p>注:测试时发现这种做法会与koa-morgan middleware冲突,会让之后的handler无法读取到body。所以如果使用了morgan需要暂时禁用。</p>
<p>加了日志之后觉得终于可以抓到错误格式的请求了,没想到这个bug就玩消失了-_-(可能是具有量子力学不可观测性的bug)。一周之后终于找到几个日志,错误的http请求是长这样的:</p>
<div class="gatsby-highlight" data-language="text"><pre class="language-text"><code class="language-text">POST /list HTTP/1.1\r\ncontent-type: application/json\r\naccept-encoding: gzip, deflate\r\naccept: application/json\r\ncontent-length: 117\r\nConnection: close\r\n\r\n
</code></pre></div>
<p>这个请求应该传递117字节的数据,但是却没有传对应的body,所以解析失败。</p>
<p>这也解释了为什么server为什么会hang up到超时,因为header里指定了content-length,但是却没有传对应的body,server认为还有数据没有传过来,于是一直在等待后续的数据。在client超时关闭连接后,server的HTTP parser就报错了。</p>
<p>由于request<a href="https://github.com/request/request#deprecated">已经不在维护</a>,最后换用了<a href="https://github.com/sindresorhus/got">got</a>来解决这个问题。</p></content:encoded></item><item><title><![CDATA[VXLAN & Flannel]]></title><description><![CDATA[VXLAN 是什么 Virtual Extensible LAN(VXLAN)是一个在三层网络上模拟二层网络的网络虚拟化技术。可以解决云计算场景下子网不够用的问题。 有一个和 VXLAN 名字和解决的问题都很相似的技术:VLAN,VXLAN 和 VLAN…]]></description><link>https://mingxinwei.github.io/VXLAN&Flannel/</link><guid isPermaLink="false">https://mingxinwei.github.io/VXLAN&Flannel/</guid><pubDate>Mon, 23 Dec 2019 11:51:30 GMT</pubDate><content:encoded><h3>VXLAN 是什么</h3>
<p>Virtual Extensible LAN(VXLAN)是一个在三层网络上模拟二层网络的网络虚拟化技术。可以解决云计算场景下子网不够用的问题。</p>
<p>有一个和 VXLAN 名字和解决的问题都很相似的技术:VLAN,VXLAN 和 VLAN 相比的优点是什么?</p>
<p>传统 VLAN 只能支持 4000 个子网,VXLAN 可以支持 1600 万个子网。</p>
<h3>VXLAN 原理</h3>
<p>使用三层网络的 udp 包来封装二层的数据包。..</p>
<h4>包结构</h4>
<p><span
class="gatsby-resp-image-wrapper"
style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; "
>
<a
class="gatsby-resp-image-link"
href="/static/8eebca9b5c20497d223e8f0e3c856c81/5f6dd/vnet-vxlan-20191107165758344.png"
style="display: block"
target="_blank"
rel="noopener"
>
<span
class="gatsby-resp-image-background-image"
style="padding-bottom: 47.2972972972973%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAAsTAAALEwEAmpwYAAACgUlEQVR42h2Q20vTARTHf/RkvdZT9AcUQWAPUT0GPRRBRiqVVpbYhSIjK8pY8+40l2BhN1NzlqUGIU7nZebll81d3H6XOZ2b6bwM1xyGhknap58e+PD5cuAcDkdwOF24XG7cskpfhwUlM4GZrJM4bidRUlBIsaGMJ3l5VD66S2V2FtW52TQ9NdBkLKZZc70hly8GHTaHC1X1Igy7PdjtTmxOD6LFTDh5D6Ttw5EYz9aP82yxQtwrL8ev3efU9Ts8fvGO0cVV7NNRRiK/aAnGqMzR4XI4Ub0+hCGbfXOhy6PQY27Fee4gU2mHGLh4hFvZ+aTrSsl8oEd35RL6jDSePcyiwViEqSSfhrJCXufr+aC7w/chB7IkI0SjCwQCQcbG/ITnwoSmZ5BlBf+Il7Bkxy9amZUcTMjDuMR+bL09SEM2xiQ3o8MOxoft+GRJm5smFoshrK2tsUEkMk80GmX59zK/V1dZARY0Ty8u8UvLf/79gzWtv7jAciwCf1dYX19no5aWljSWN7Pg842iKOrmVZIkIWt/8PV2MllfwcT75wQ0B2qMzPZbqFXm0ItBCsQAOZoHPCM0NjTQ2NSMyWTCarUiiOIgra1ttLSYadPc2t2Ht7qcUNJeJhL3Mnk2nuCxXURf53K0O4JQNc622gA7Ps1Q1TnI5dQUUi+kkZBwGoOhFOHb4Hfa2jswm9uxtFswW/tQayuYS9lP6PwBQpcOM3V6NwvVRZz4GiWuboLtHybZ+TlMTbeNGxkZpGdcJTn5DEZjOYLf78ft8SArCooso476Cdl6mXpTyORbg0YJwUo9sW8WXqo/udkT4F7/D+6KIUTVj6mmhrq6eqqqqunq6uY/gdAsTYhtk+wAAAAASUVORK5CYII='); background-size: cover; display: block;"
></span>
<img
class="gatsby-resp-image-image"
alt="img"
title=""
src="/static/8eebca9b5c20497d223e8f0e3c856c81/fcda8/vnet-vxlan-20191107165758344.png"
srcset="/static/8eebca9b5c20497d223e8f0e3c856c81/12f09/vnet-vxlan-20191107165758344.png 148w,
/static/8eebca9b5c20497d223e8f0e3c856c81/e4a3f/vnet-vxlan-20191107165758344.png 295w,
/static/8eebca9b5c20497d223e8f0e3c856c81/fcda8/vnet-vxlan-20191107165758344.png 590w,
/static/8eebca9b5c20497d223e8f0e3c856c81/efc66/vnet-vxlan-20191107165758344.png 885w,
/static/8eebca9b5c20497d223e8f0e3c856c81/5f6dd/vnet-vxlan-20191107165758344.png 1139w"
sizes="(max-width: 590px) 100vw, 590px"
style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"
loading="lazy"
decoding="async"
/>
</a>
</span></p>
<p>(图片来源:<a href="https://cizixs.com/2017/09/25/vxlan-protocol-introduction/">vxlan 协议原理简介</a>)</p>
<p>从最内层往外看:最内层的 Original L2 Frame 是虚拟子网的二层数据包,在前面加上 VXLAN 的 header。之后就被当作一个普通的 udp 包的 payload,在真正的物理子网上发送出去。</p>
<p>这里可以注意一下 VXLAN Header 中的 VNID 字段,共占 24bit,所以 VXLAN 能够支持的不同子网共有<code class="language-text">2^24 = 16777216</code>个。</p>
<h4>组网原理</h4>
<p><span
class="gatsby-resp-image-wrapper"
style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 584px; "
>
<a
class="gatsby-resp-image-link"
href="/static/b9243aa32a333bf609d27ccd5bc2c876/e05eb/5652c940898f4.png"
style="display: block"
target="_blank"
rel="noopener"
>
<span
class="gatsby-resp-image-background-image"
style="padding-bottom: 52.02702702702703%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAAB70lEQVR42oWSW08TURSF+2d8881X/4sP/gW8hHgJGjXRRlEiUV80itREJIVS0GCBoBFTWkul9EJbeqMzpVM6vQ7ttDOfp1PSiknjTk7OPre111pn2xgRhmlac1TWmPiU5PFKllanv2cyOmyjDjpdw5q/7FU5fyfMxYcxVK3bBzT/A9hjY/5zq2v0105fgXPXf3FhIkgyd4TePqF7WswcqBkqOsPQHBxCvd5EVWRWvTGuOcLcnd8nk5OoV1Wq1RpNrYVhjmDoTTU4UFp9Zr3qeoNKWaFY1dCEb5JSo9zLheJyvUVNVaiXJDp623oTljRCeW0I+Oa7wlaiLmQa2JczjM+lWPMlkNZfE3e/wDf/kq2P04Tm7CJ/jsOzy5X3ezxwpS2QpR2VxYDaB9RbJ9xwHuIMlIVmg2frCuOuIhs7aQ5XpvjtuId31s6PmUcE3t4itDjNaqjETdcRk2sly6hX34pMegoYgrEtGMmRSSaoHJesCrqapynvi3v6qa/DVjEGP6bRyMfo1ArW8rggk4oniR4U+pLf+TP8TJdotXXue2JcdYcIShULxjCEccIKUwwrF7GRKDK2tMuTzbilyh2RWQjLQw+fxjf5KkVpCvljwQUubzvwl7NnGvzv/LMc4ZJ3ltuhZdGwBh8yfmay2xbgHwZb4OU9AMsQAAAAAElFTkSuQmCC'); background-size: cover; display: block;"
></span>
<img
class="gatsby-resp-image-image"
alt="img"
title=""
src="/static/b9243aa32a333bf609d27ccd5bc2c876/e05eb/5652c940898f4.png"
srcset="/static/b9243aa32a333bf609d27ccd5bc2c876/12f09/5652c940898f4.png 148w,
/static/b9243aa32a333bf609d27ccd5bc2c876/e4a3f/5652c940898f4.png 295w,
/static/b9243aa32a333bf609d27ccd5bc2c876/e05eb/5652c940898f4.png 584w"
sizes="(max-width: 584px) 100vw, 584px"
style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"
loading="lazy"
decoding="async"
/>
</a>
</span></p>
<p>(图片来源:<a href="https://cizixs.com/2017/09/25/vxlan-protocol-introduction/">vxlan 协议原理简介</a>)</p>
<p>一个 VXLAN 网络中有几个重要的部分:</p>
<ol>
<li>VTEP,VXLAN Tunnel Endpoint。这个是每个机器上都会有一个 VXLAN 网络的 endpoint,负责对数据包进行封包和解包。可以是软件实现,也可以是专有的硬件。</li>
<li>VNI,区分不同 VXLAN 网络的标识符,这个主要是设置在每个 VTEP 上,VNI 相同的 VTEP 在一个子网里。</li>
</ol>
<p>几台机器想要组成一个 VXLAN,需要哪些信息?换句话说,假设已经有了一个 VXLAN 网络,在这个网络内的机器 A 想要给机器 B 发个数据包,有哪些地址字段需要填写?</p>
<p>需要填写的字段有 4 个:</p>
<ol>
<li>内层(虚拟子网)的 IP 地址</li>
<li>内层的 MAC 地址</li>
<li>外层(物理子网)的 IP 地址</li>
<li>外层的 MAC 地址</li>
</ol>
<p>A 上的应用要给 B 发消息,肯定是知道 B 的 IP 地址(内层 IP 地址);并且如果知道外层的 IP 地址,通过物理子网的 arp 可以知道外层的 MAC。所以还缺少的地址有:</p>
<ol>
<li>内层(虚拟子网)的 MAC 地址</li>
<li>外层(物理子网)的 IP 地址</li>
</ol>
<p>想要得到这两个信息,一般有组播和控制中心两种办法。</p>
<ol>
<li>组播是通过在组播地址上组播,VTEP 自动学习着两个信息。这里先不展开介绍</li>
<li>控制中心是通过一个中心数据库来提供这两个信息,比如在 k8s 中可以在创建 pod 的时候,把 1. pod 的 MAC 地址 2. pod 所在机器的 IP 地址存储到 etcd 里,在后面别的机器(上的 pod)需要和这个 pod 通信的时候,可以通过像 etcd 发起查询得到这两个信息,就可以通信了。</li>
</ol>
<h3>在 linux 中实践 VXLAN</h3>
<h4>目标</h4>
<p>在 2 台机器上构建一个 VXLAN,使机器&#x3C;==>机器,机器&#x3C;==>容器,容器&#x3C;==>容器之间都可以通信。</p>
<h4>网络拓扑</h4>
<p><span
class="gatsby-resp-image-wrapper"
style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; "
>
<a
class="gatsby-resp-image-link"
href="/static/12be2088c186b3768405cb0713bbedc6/1ac66/expected-network-topography-vxlan.png"
style="display: block"
target="_blank"
rel="noopener"
>
<span
class="gatsby-resp-image-background-image"
style="padding-bottom: 77.02702702702703%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAYAAADkmO9VAAAACXBIWXMAAAsTAAALEwEAmpwYAAADKUlEQVR42oWUz28bRRTHp04TJcAF9ZRDGwEBVFfqgahS/gH+BJ8r9ZAD/0SOcETiwP9gpEIhbaJGyHY36/01Mzuzu17bu7b3h3+mLlCrEAje5DGzTYqVIjHS077ZmffR973RewghdO3CLldhe3t7TTr79fgz1R29+v4pfnGohxnpzr6U/+/e/fxd8bmO/msBwLVLK5VKS+vr6+9sbW0ty7NKFK2ayZ8f4uTlR14Km4f+7MZlzMbGxmqxWFy5wijI/ZKw1Qu+vFBA/7/WLuKWr2SHkNZVqBaoHhtSzexpFhvYpjO2NT62uZnqD3KlANcx4Fy1lepfqy0lpbFlkcQ0cGxazsjW3THjuK9/i9q/+me9Vx0IpwH4cQPCF20Q0KzzMgQ2oo8kZHd3t1CpVPKatX/z95/DGHqz7rw9bEEwDsAdcuB9G6y0/hcS9Hnj2Mvc1MlYYGfuwMlwZJ16IxdwZPx4FciGZE8KaEwap7zLMqfnZCQimdUxz9Swdor40AYBBNZjYHoGCCBYXTNzRg7Q1HoDFAWXNUP2gBzEf0Tgjb0MNy2wQwo0FgXpWqB3VUBsQM4lkMcC2BDA/mugP/Ek8K2U7YH5WAL9SWNO2hhoDiSAexj0jjoXQHreeO6B3aOg8TqwhAGJcdaaNoEm1sO3gfhJciIUTry54etg+Ia8D6zPBPDoBNl9csYjBlQAnREHGhGhlmfJ7zHwIf0pB8K/QDrAe9GsB6RDMi4gzoCJctnQHPlAByYgd8TPVaqC6RoyDaABBdVSs3Aagn0JXFDoTfnjcBKAitW5LFPz2AcZb7imKJH5N8KpGViRCUdN5Sx/euEr/rN5a+qDM+H5o5ShvFQul1deKzS/cSdc3slkqt7EBbV1dK4HGtQ7z+bI8A9v0FgvPlK+u6P1qx97x2RzT3t420iUO7Xw4ObVFkmhvvYz3/+06j69zWbGJ/vCf6L/UJRxlfBgU/Thm9ZZHBJ5+0lViqLcqtVqN4V9IPz3F88X4hanA8qbOh8O5dJSsVRcub97X/Z2oVqt3kuS5IQQ8kur1YIoir6SIaUvSu+JQbKys7OzLB9s0f4BQqylpplfPB4AAAAASUVORK5CYII='); background-size: cover; display: block;"
></span>
<img
class="gatsby-resp-image-image"
alt="img"
title=""
src="/static/12be2088c186b3768405cb0713bbedc6/fcda8/expected-network-topography-vxlan.png"
srcset="/static/12be2088c186b3768405cb0713bbedc6/12f09/expected-network-topography-vxlan.png 148w,
/static/12be2088c186b3768405cb0713bbedc6/e4a3f/expected-network-topography-vxlan.png 295w,
/static/12be2088c186b3768405cb0713bbedc6/fcda8/expected-network-topography-vxlan.png 590w,
/static/12be2088c186b3768405cb0713bbedc6/1ac66/expected-network-topography-vxlan.png 651w"
sizes="(max-width: 590px) 100vw, 590px"
style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"
loading="lazy"
decoding="async"
/>
</a>
</span></p>
<p>(图片来源: <a href="https://github.com/yangyuqian/k8s-the-hard-way/blob/master/network/vxlan-flannel-cn.md">VXLAN 在 Flannel 中的 Overlay 网络的实现</a>)</p>
<p>需要搭建的 overlay 子网为 192.1.0.0/16。</p>
<h4>步骤</h4>
<ol>
<li>创建 VXLAN interface(VTEP),给 VXLAN 分配 ip</li>
<li>路由表新增一项,把虚拟子网的流量交给 VTEP 处理</li>
<li>上面提到要打包缺少的地址:「内层(虚拟子网)的 MAC 地址」,这里通过手动修改 ARP 表实现。ARP 表新增几项,把 overlay 网络上需要访问的容器(机器)ip 地址都指向<strong>该机器的 VTEP 的 MAC 地址</strong>。</li>
<li>另一个缺少的地址:「外层(物理子网)的 IP 地址」,通过修改转发表来实现。转发表新增一项,把发往 VTEP MAC 地址的流量都指向另一台机器的物理子网 ip</li>
</ol>
<h4>一个数据包的奥德赛</h4>
<p>经过这样的配置之后,一个数据包怎么从一个机器/容器路由到另一个机器/容器呢?这里选择讲解一个最长的路由路径:容器&#x3C;==>容器,其他情况都包含在这个路径里。
假设 node1 上的容器 A 192.1.78.2 想要发一个数据包给 node2 上的容器 B 192.1.87.2</p>
<ol>
<li>
<p>数据包从 A 发出,通过 docker bridge 来到 docker0 interface,docker0 需要知道该把这个数据包发给谁,于是他查询了路由表,发现命中了这条规则<code class="language-text">192.1.0.0/16</code>,于是发往 vxlan.1 interface。</p>
</li>
<li>
<p>vxlan.1 收到数据包之后开始打包,需要填写 4 个地址:</p>
<blockquote>
<ol>
<li>内层(虚拟子网)的 IP 地址</li>
<li>内层的 MAC 地址</li>
<li>外层(物理子网)的 IP 地址</li>
<li>外层的 MAC 地址</li>
</ol>
</blockquote>
</li>
</ol>
<p>1 已知;2 会通过 arp 请求去查找,命中前面手动维护的本地 arp 缓存,得到 node2 VTEP 的 MAC 地址;3 会命中手动维护的 fdb 转发表,得到 node2 的物理子网 IP 地址;4 通过物理子网 arp 得到。</p>
<ol start="3">
<li>
<p>打包完成后,得到一个物理子网的普通 udp 包发往 node2 的 4789 端口。node2 的 vxlan.1 interface 监听该端口,接到 udp 包之后解开封装得到内层的数据包。</p>
</li>
<li>
<p>vxlan.1 需要知道数据包该发往哪里,同样查询本机的路由表,命中<code class="language-text">192.1.87.0/24</code>,发往 docker0 interface。docker0 再将数据包发给容器 B。</p>
</li>
</ol>
<h4>实践</h4>
<h5>1. 实现机器&#x3C;==>机器通信</h5>
<p>将以上步骤写到一起,方便执行:</p>
<div class="gatsby-highlight" data-language="bash"><pre class="language-bash"><code class="language-bash"><span class="token comment"># step1</span>
<span class="token builtin class-name">export</span> <span class="token assign-left variable">IP</span><span class="token operator">=</span> <span class="token comment"># 本机ip</span>
<span class="token builtin class-name">export</span> <span class="token assign-left variable">DEST_IP</span><span class="token operator">=</span> <span class="token comment"># 另一台机器ip</span>
<span class="token builtin class-name">export</span> <span class="token assign-left variable">VTEP_IP</span><span class="token operator">=</span> <span class="token comment"># 本机VTEP ip</span>
<span class="token builtin class-name">export</span> <span class="token assign-left variable">PREFIX</span><span class="token operator">=</span>vxlan
<span class="token builtin class-name">export</span> <span class="token assign-left variable">PORT</span><span class="token operator">=</span><span class="token number">4789</span>
<span class="token builtin class-name">export</span> <span class="token assign-left variable">VNI</span><span class="token operator">=</span><span class="token number">1</span>
<span class="token builtin class-name">export</span> <span class="token assign-left variable">SUBNET</span><span class="token operator">=</span><span class="token number">192</span>.<span class="token variable">$VNI</span>.0.0/16
<span class="token builtin class-name">export</span> <span class="token assign-left variable">DEVICE_NAME</span><span class="token operator">=</span><span class="token variable">$PREFIX</span><span class="token builtin class-name">.</span><span class="token variable">$VNI</span>
<span class="token function">ip</span> <span class="token function">link</span> <span class="token function">add</span> <span class="token variable">$DEVICE_NAME</span> <span class="token builtin class-name">type</span> vxlan <span class="token function">id</span> <span class="token variable">$VNI</span> dev eth0 dstport <span class="token variable">$PORT</span> nolearning
<span class="token function">ip</span> address <span class="token function">add</span> <span class="token variable">$VTEP_IP</span> dev <span class="token variable">$DEVICE_NAME</span>
<span class="token function">ip</span> <span class="token function">link</span> <span class="token builtin class-name">set</span> <span class="token variable">$DEVICE_NAME</span> up
<span class="token function">ip</span> route <span class="token function">add</span> <span class="token variable">$SUBNET</span> dev <span class="token variable">$DEVICE_NAME</span> scope global
<span class="token comment"># step2 需要拿到step1的结果后在再进行</span>
<span class="token builtin class-name">export</span> <span class="token assign-left variable">ANOTHER_VTEP_MAC</span><span class="token operator">=</span> <span class="token comment"># 需要等另一台机器的VXLAN设备创建之后,查看其MAC地址</span>
bridge fdb <span class="token function">add</span> <span class="token variable">$ANOTHER_VTEP_MAC</span> dev <span class="token variable">$DEVICE_NAME</span> dst <span class="token variable">$DEST_IP</span>
<span class="token builtin class-name">export</span> <span class="token assign-left variable">ANOTHER_NODE_IP</span><span class="token operator">=</span> <span class="token comment"># 需要访问的另一台机器上的(容器或机器)ip</span>
<span class="token function">ip</span> neighbor <span class="token function">add</span> <span class="token variable">$ANOTHER_NODE_IP</span> lladdr <span class="token variable">$ANOTHER_VTEP_MAC</span> dev <span class="token variable">$DEVICE_NAME</span></code></pre></div>
<p>Node1:</p>
<p><span
class="gatsby-resp-image-wrapper"
style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; "
>
<a
class="gatsby-resp-image-link"
href="/static/b75473b2711743edf07bca9b402ad511/00e09/image-20191223172234150.png"
style="display: block"
target="_blank"
rel="noopener"
>
<span
class="gatsby-resp-image-background-image"
style="padding-bottom: 89.86486486486487%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAIAAADUsmlHAAAACXBIWXMAAAsTAAALEwEAmpwYAAACaUlEQVR42l1TiXKbMBDlV9okjg1GnEKcEiAsLmMnTtNp+v8f0ocUO05ndjSC4e07drGCj6P/NpKTSttZdGda9lF+iAtFS+XR2o3F057d1/Oeba53y/81kkW584E1E5fn+vBSNkvAZMDaMJOojZveg+meuvvkwYCDjxnM7kllcm771256Z2XvJ80+rB5s+mMbPTrJf3VrZHlX5qhSsRbMqiEVI2QDv/OLLcm2Xo7a+flOX25arODv0XsbyMtAxQDPlTyp+fdhei/bJa/nlI9QYczr4iQWzyS7Ml9Gb+nJpMLiAJ9eUoPfZ63B4NFnjR2URi04Hx36JTtsVTmd+fG1GS9gTsoeYGQOWmMeYHCiEdHnfX7WjuQ5n3IxG81GZ5hKaHsGj00fdEg4kZ+pu8BoDXv8cG77C5enTEzgpIWCBaJ9ElN0pXUjjtbIzLiw8AxC4Gv1ykHeLpVccKJLAH4vg877gqKvtJ2wKpojakXKBSfu6IWQMCd8ivGsJ2b2fU56w1irYad2eEPB+frYncEM/2HWQRoIiNbs6jnB9qdsOMF3APPuhPU0tMY2kJ+YiCNCch018nfCdXiWG3AhV+amvyAzkJvMATAiNzjhE6eXP+k7dm5DUj2qjKdqTuUETmBAi/VkfIRn/Q8ljk1dO/btmNixZ0cbm/7UA1uZ/cvoTp07rbudFCpv1pVE5TU8D2lYCMqLmJdxxSlvE8FYs49FhCxiYXnH3paNIxs/k3iFwpLBcJR38IltCwvlZ12QKwdpBeVWj8CskOX/mcP3iSw9foxcQPxc6lGDGV3WsbcY5JxBTjXgPdrdfsx/qjjuewYtBlQAAAAASUVORK5CYII='); background-size: cover; display: block;"
></span>
<img
class="gatsby-resp-image-image"
alt="image-20191223172234150"
title=""
src="/static/b75473b2711743edf07bca9b402ad511/fcda8/image-20191223172234150.png"
srcset="/static/b75473b2711743edf07bca9b402ad511/12f09/image-20191223172234150.png 148w,
/static/b75473b2711743edf07bca9b402ad511/e4a3f/image-20191223172234150.png 295w,
/static/b75473b2711743edf07bca9b402ad511/fcda8/image-20191223172234150.png 590w,
/static/b75473b2711743edf07bca9b402ad511/efc66/image-20191223172234150.png 885w,
/static/b75473b2711743edf07bca9b402ad511/c83ae/image-20191223172234150.png 1180w,
/static/b75473b2711743edf07bca9b402ad511/00e09/image-20191223172234150.png 1854w"
sizes="(max-width: 590px) 100vw, 590px"
style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"
loading="lazy"
decoding="async"
/>
</a>
</span></p>
<p>Node2 :</p>
<p><span
class="gatsby-resp-image-wrapper"
style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; "
>
<a
class="gatsby-resp-image-link"
href="/static/594f065dc1585d6f878c52683ea095b8/2093e/image-20191223172531204.png"
style="display: block"
target="_blank"
rel="noopener"
>
<span
class="gatsby-resp-image-background-image"
style="padding-bottom: 87.16216216216216%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAIAAABSJhvpAAAACXBIWXMAAAsTAAALEwEAmpwYAAACWklEQVR42l1TCa6jMBTjKP3tny6UfQkQQtgDgbZ0+3P/o4wJ1bQzkhVFKM6zHaPZv0fAvPZRfcrbW5QPSXmKuIz54NHWS5pvI/rE5mOvWXdpPaQxiqCUWT3l4kbLk+5mB5eZQW4E+S8z/mT6RmAZ4eZFfg72YzCmjtQjJoNMuPRTYUfVWg9Xe39zJJ/YAstdR6LZIP8M5tR7eRekwmcirc4ArnCTxial7mVHn0OCGRbQovv8pQVk8y7Ne2+cBci0OEFzJZ9F/4D5SEmYCV62tZIFeyd9k53n4P6MzlWSakRIGM7qC8l6kONi5rNm8mm70PY2hfLvv7KPHofIorvDbd7dUzC5TIoxzoeAdbPUsMBkyIZ44J08yDub4npanrNmwkwQcM4KiyWer0MAYIPwFvxDtkjJ5heaJ/P2ChVY4ZxWZ1jAWEsNx4rwcC/W3SIeZDiJ85FWFwgGYV7LE3qCp0JUsPpfSd7PPqcdFoiEi1vZP+Aca9ZcoQVDDJ/DrancWmosDmPsWicvMj4hHhCU5wmysYGEuBgJ7524RtUA8JfN3qEAFG2O4TwZ55bhinxBSXFdkHYotqEaCgkOXKiSLCpwy0YPtZ2ZpOWZN1dkBsMqrble8LbaB19oqB5+Kax0hD9vltiRmXbghSt6v5VROSIqeOaq4SHrXNq6Lo3sOHFiakfMibmb2B7b2hRZwLxmX6Qu64OorLQmrPdZh2cHyLKmLUXb0pakIogry2NWOLcVbwH9Gv5k69obg3AzgRribaN8ZuJnxo9B8iHkMmC9lwoIwd5U/VmrCv0BnxfhQipPukgAAAAASUVORK5CYII='); background-size: cover; display: block;"
></span>
<img
class="gatsby-resp-image-image"
alt="image-20191223172531204"
title=""
src="/static/594f065dc1585d6f878c52683ea095b8/fcda8/image-20191223172531204.png"
srcset="/static/594f065dc1585d6f878c52683ea095b8/12f09/image-20191223172531204.png 148w,
/static/594f065dc1585d6f878c52683ea095b8/e4a3f/image-20191223172531204.png 295w,
/static/594f065dc1585d6f878c52683ea095b8/fcda8/image-20191223172531204.png 590w,
/static/594f065dc1585d6f878c52683ea095b8/efc66/image-20191223172531204.png 885w,
/static/594f065dc1585d6f878c52683ea095b8/c83ae/image-20191223172531204.png 1180w,
/static/594f065dc1585d6f878c52683ea095b8/2093e/image-20191223172531204.png 1858w"
sizes="(max-width: 590px) 100vw, 590px"
style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"
loading="lazy"
decoding="async"
/>
</a>
</span></p>
<p>查看一下这样执行之后机器上的路由表、arp 表、和 fdb 转发表的内容,这里以 node1 为例:</p>
<p>路由表:</p>
<div class="gatsby-highlight" data-language="bash"><pre class="language-bash"><code class="language-bash">root@VM-0-7-ubuntu:~<span class="token comment"># ip route</span>
default via <span class="token number">172.17</span>.0.1 dev eth0 proto static
<span class="token number">172.17</span>.0.0/20 dev eth0 proto kernel scope <span class="token function">link</span> src <span class="token number">172.17</span>.0.7
<span class="token number">172.18</span>.0.0/16 dev docker0 proto kernel scope <span class="token function">link</span> src <span class="token number">172.18</span>.0.1 linkdown
<span class="token number">192.1</span>.0.0/16 dev vxlan.1</code></pre></div>
<p><code class="language-text">192.1.0.0/16 dev vxlan.1</code>这条规则指定了<code class="language-text">192.1.0.0/16</code>这个子网的流量都会通过<code class="language-text">vxlan.1</code>这个设备来处理。</p>
<p>arp 表:</p>
<div class="gatsby-highlight" data-language="bash"><pre class="language-bash"><code class="language-bash">root@VM-0-7-ubuntu:~<span class="token comment"># ip neighbor</span>
<span class="token number">192.1</span>.87.0 dev vxlan.1 lladdr d2:04:98:13:a7:aa PERMANENT
<span class="token number">172.17</span>.0.1 dev eth0 lladdr fe:ee:00:2e:16:9a REACHABLE
<span class="token number">172.17</span>.0.4 dev eth0 lladdr fe:ee:00:2e:16:9a STALE</code></pre></div>
<p>在 arp 表中添加了我们需要访问的 ip <code class="language-text">192.1.87.0</code>的 MAC 地址(因为我们需要 ping 的 node2 的机器 ip)。需要注意的是,如果等下需要访问 node2 容器的 ip 地址(比如<code class="language-text">192.1.87.2</code>),这里还需要添加一条记录。</p>
<p>fdb 转发表:</p>
<div class="gatsby-highlight" data-language="bash"><pre class="language-bash"><code class="language-bash">root@VM-0-7-ubuntu:~<span class="token comment"># bridge fdb</span>
<span class="token number">33</span>:33:00:00:00:01 dev eth0 self permanent
01:00:5e:00:00:01 dev eth0 self permanent
<span class="token number">33</span>:33:ff:7b:75:85 dev eth0 self permanent
01:80:c2:00:00:00 dev eth0 self permanent
01:80:c2:00:00:03 dev eth0 self permanent
01:80:c2:00:00:0e dev eth0 self permanent
<span class="token number">33</span>:33:00:00:00:01 dev docker0 self permanent
01:00:5e:00:00:01 dev docker0 self permanent
02:42:c0:52:69:49 dev docker0 vlan <span class="token number">1</span> master docker0 permanent
02:42:c0:52:69:49 dev docker0 master docker0 permanent
d2:04:98:13:a7:aa dev vxlan.1 dst <span class="token number">172.17</span>.0.4 self permanent</code></pre></div>
<p>指定了发往<code class="language-text">d2:04:98:13:a7:aa</code>这个 MAC 地址(node2 VTEP 的 MAC 地址)的流量都发向<code class="language-text">172.17.0.4</code>这个 ip。</p>
<p>这样就可以 ping 通两台机器了:</p>
<p><span
class="gatsby-resp-image-wrapper"
style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; "
>
<a
class="gatsby-resp-image-link"
href="/static/d97c5144bd758c68eee7f44af30edb81/b6e50/image-20191223172718418.png"
style="display: block"
target="_blank"
rel="noopener"
>
<span
class="gatsby-resp-image-background-image"
style="padding-bottom: 18.243243243243242%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAj0lEQVR42o2PSRKDMAwE+U1YsrDEOzYQMBgq///ORPYH4kPX6KLWqBA+gC0ecj5g7Qk3XbDuhJAb+mHCq3WoG4Gy4lkUWu3Q+oCilNrDjCERhTHjAak8biXLExoTqNGV2sXF2I6LNTG8F7SdRdc7muc8oaSX1UZS/4UjMeOfJIsNGV/p5RHNXeHx1Khq8ZcfKWiBeav+TdQAAAAASUVORK5CYII='); background-size: cover; display: block;"
></span>
<img
class="gatsby-resp-image-image"
alt="image-20191223172718418"
title=""
src="/static/d97c5144bd758c68eee7f44af30edb81/fcda8/image-20191223172718418.png"
srcset="/static/d97c5144bd758c68eee7f44af30edb81/12f09/image-20191223172718418.png 148w,
/static/d97c5144bd758c68eee7f44af30edb81/e4a3f/image-20191223172718418.png 295w,
/static/d97c5144bd758c68eee7f44af30edb81/fcda8/image-20191223172718418.png 590w,
/static/d97c5144bd758c68eee7f44af30edb81/efc66/image-20191223172718418.png 885w,
/static/d97c5144bd758c68eee7f44af30edb81/c83ae/image-20191223172718418.png 1180w,
/static/d97c5144bd758c68eee7f44af30edb81/b6e50/image-20191223172718418.png 1862w"
sizes="(max-width: 590px) 100vw, 590px"
style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"
loading="lazy"
decoding="async"
/>
</a>
</span></p>
<h5>2. 实现容器&#x3C;==>容器通信</h5>
<p>有了前面机器&#x3C;==>机器的通信基础之后,容器&#x3C;==>容器就比较简单了。</p>
<p>首先需要改一下 docker 的子网,修改 docker 的启动参数并重启 docker:</p>
<div class="gatsby-highlight" data-language="bash"><pre class="language-bash"><code class="language-bash"><span class="token function">cat</span> <span class="token operator">></span> /etc/docker/daemon.json <span class="token operator">&lt;&lt;</span> <span class="token string">EOF
{"bip":"192.1.78.1/24"}
EOF</span>
systemctl restart <span class="token function">docker</span></code></pre></div>
<p>然后启动 docker 容器:</p>
<div class="gatsby-highlight" data-language="bash"><pre class="language-bash"><code class="language-bash"><span class="token function">docker</span> run <span class="token parameter variable">-it</span> <span class="token parameter variable">--rm</span> busybox</code></pre></div>
<p>查看一下容器的 ip 地址:</p>
<div class="gatsby-highlight" data-language="bash"><pre class="language-bash"><code class="language-bash">/ <span class="token comment"># ip a</span>
<span class="token number">1</span>: lo: <span class="token operator">&lt;</span>LOOPBACK,UP,LOWER_UP<span class="token operator">></span> mtu <span class="token number">65536</span> qdisc noqueue qlen <span class="token number">1000</span>
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet <span class="token number">127.0</span>.0.1/8 scope <span class="token function">host</span> lo
valid_lft forever preferred_lft forever
<span class="token number">6</span>: eth0@if7: <span class="token operator">&lt;</span>BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN<span class="token operator">></span> mtu <span class="token number">1500</span> qdisc noqueue
link/ether 02:42:c0:01:4e:02 brd ff:ff:ff:ff:ff:ff
inet <span class="token number">192.1</span>.78.2/24 brd <span class="token number">192.1</span>.78.255 scope global eth0
valid_lft forever preferred_lft forever</code></pre></div>
<p>在 node2 上面重复以上步骤(对应的子网地址需要改一下)。</p>
<p>可以看到 node1 上 container 的 ip 是<code class="language-text">192.1.78.2</code>,node2 上的 container 的 ip 是<code class="language-text">192.1.87.2</code>,所以需要在 2 台机器上分别添加这两个 ip 的 arp 记录:</p>
<p>Node1:</p>
<div class="gatsby-highlight" data-language="bash"><pre class="language-bash"><code class="language-bash"><span class="token builtin class-name">export</span> <span class="token assign-left variable">ANOTHER_NODE_IP</span><span class="token operator">=</span><span class="token number">192.1</span>.87.2 <span class="token comment"># 需要访问的另一台机器上的(容器或机器)ip</span>
<span class="token function">ip</span> neighbor <span class="token function">add</span> <span class="token variable">$ANOTHER_NODE_IP</span> lladdr <span class="token variable">$ANOTHER_VTEP_MAC</span> dev <span class="token variable">$DEVICE_NAME</span></code></pre></div>
<p>Node2:</p>
<div class="gatsby-highlight" data-language="bash"><pre class="language-bash"><code class="language-bash"><span class="token builtin class-name">export</span> <span class="token assign-left variable">ANOTHER_NODE_IP</span><span class="token operator">=</span><span class="token number">192.1</span>.78.2 <span class="token comment"># 需要访问的另一台机器上的(容器或机器)ip</span>
<span class="token function">ip</span> neighbor <span class="token function">add</span> <span class="token variable">$ANOTHER_NODE_IP</span> lladdr <span class="token variable">$ANOTHER_VTEP_MAC</span> dev <span class="token variable">$DEVICE_NAME</span></code></pre></div>
<p>这样就可以 ping 通容器…了吗?并没有,这时候 ping 会发现是 ping 不通的。</p>
<p>经过很长时间的摸索,我发现是 docker 设置的 iptables 的规则阻止了 ping 包:</p>
<div class="gatsby-highlight" data-language="bash"><pre class="language-bash"><code class="language-bash">root@VM-0-4-ubuntu:~<span class="token comment"># iptables -L -v</span>
Chain INPUT <span class="token punctuation">(</span>policy ACCEPT <span class="token number">1322</span> packets, 897K bytes<span class="token punctuation">)</span>
pkts bytes target prot opt <span class="token keyword">in</span> out <span class="token builtin class-name">source</span> destination
Chain FORWARD <span class="token punctuation">(</span>policy DROP <span class="token number">10</span> packets, <span class="token number">840</span> bytes<span class="token punctuation">)</span>
pkts bytes target prot opt <span class="token keyword">in</span> out <span class="token builtin class-name">source</span> destination
<span class="token number">10</span> <span class="token number">840</span> DOCKER-<span class="token environment constant">USER</span> all -- any any anywhere anywhere
<span class="token number">10</span> <span class="token number">840</span> DOCKER-ISOLATION-STAGE-1 all -- any any anywhere anywhere
<span class="token number">0</span> <span class="token number">0</span> ACCEPT all -- any docker0 anywhere anywhere ctstate RELATED,ESTABLISHED
<span class="token number">10</span> <span class="token number">840</span> DOCKER all -- any docker0 anywhere anywhere
<span class="token number">0</span> <span class="token number">0</span> ACCEPT all -- docker0 <span class="token operator">!</span>docker0 anywhere anywhere
<span class="token number">0</span> <span class="token number">0</span> ACCEPT all -- docker0 docker0 anywhere anywhere
Chain OUTPUT <span class="token punctuation">(</span>policy ACCEPT <span class="token number">1294</span> packets, 147K bytes<span class="token punctuation">)</span>
pkts bytes target prot opt <span class="token keyword">in</span> out <span class="token builtin class-name">source</span> destination
Chain DOCKER <span class="token punctuation">(</span><span class="token number">1</span> references<span class="token punctuation">)</span>
pkts bytes target prot opt <span class="token keyword">in</span> out <span class="token builtin class-name">source</span> destination
Chain DOCKER-ISOLATION-STAGE-1 <span class="token punctuation">(</span><span class="token number">1</span> references<span class="token punctuation">)</span>
pkts bytes target prot opt <span class="token keyword">in</span> out <span class="token builtin class-name">source</span> destination
<span class="token number">0</span> <span class="token number">0</span> DOCKER-ISOLATION-STAGE-2 all -- docker0 <span class="token operator">!</span>docker0 anywhere anywhere
<span class="token number">10</span> <span class="token number">840</span> RETURN all -- any any anywhere anywhere
Chain DOCKER-ISOLATION-STAGE-2 <span class="token punctuation">(</span><span class="token number">1</span> references<span class="token punctuation">)</span>
pkts bytes target prot opt <span class="token keyword">in</span> out <span class="token builtin class-name">source</span> destination
<span class="token number">0</span> <span class="token number">0</span> DROP all -- any docker0 anywhere anywhere
<span class="token number">0</span> <span class="token number">0</span> RETURN all -- any any anywhere anywhere
Chain DOCKER-<span class="token environment constant">USER</span> <span class="token punctuation">(</span><span class="token number">1</span> references<span class="token punctuation">)</span>
pkts bytes target prot opt <span class="token keyword">in</span> out <span class="token builtin class-name">source</span> destination
<span class="token number">10</span> <span class="token number">840</span> RETURN all -- any any anywhere anywhere</code></pre></div>
<p>iptables 的<code class="language-text">FORWARD</code>chain 阻止了包从<code class="language-text">vxlan.1</code> 到<code class="language-text">docker0</code>interface 的 forward,所以导致 ping 不通。这里不细究<code class="language-text">FORWARD</code>chain 下的 iptables 规则,先简单直接地把<code class="language-text">FORWARD</code>chain 的默认 policy 设置为<code class="language-text">ACCEPT</code>:</p>
<div class="gatsby-highlight" data-language="text"><pre class="language-text"><code class="language-text">iptables -P FORWARD ACCEPT</code></pre></div>
<p>这样 2 个容器之间就可以 ping 通了:</p>
<p><span
class="gatsby-resp-image-wrapper"
style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; "
>
<a
class="gatsby-resp-image-link"
href="/static/43cf0a876f7d5381e7b9f1d2dc7b1fb5/cc6fe/image-20191223192249290.png"
style="display: block"
target="_blank"
rel="noopener"
>
<span
class="gatsby-resp-image-background-image"
style="padding-bottom: 21.62162162162162%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAiklEQVR42o3PWQ7CMAwE0N6GAqKsaZzF0CVpmqoI7n+ZIY04QD6sma+ncfUKb3BYoYcIMhOIHEQ7QP7z1BjU+7b4Kjd+YHSAtRFKT5DKgTnC8oyuX8HPBYcjYVeLMpASIOSYoW0VKY+H6HG5Mm73LucGli/037QmwtgAnV5W2qc+Z1iSz2BztsXgD+jjgO2gBHvDAAAAAElFTkSuQmCC'); background-size: cover; display: block;"
></span>
<img
class="gatsby-resp-image-image"
alt="image-20191223192249290"
title=""
src="/static/43cf0a876f7d5381e7b9f1d2dc7b1fb5/fcda8/image-20191223192249290.png"
srcset="/static/43cf0a876f7d5381e7b9f1d2dc7b1fb5/12f09/image-20191223192249290.png 148w,
/static/43cf0a876f7d5381e7b9f1d2dc7b1fb5/e4a3f/image-20191223192249290.png 295w,
/static/43cf0a876f7d5381e7b9f1d2dc7b1fb5/fcda8/image-20191223192249290.png 590w,
/static/43cf0a876f7d5381e7b9f1d2dc7b1fb5/efc66/image-20191223192249290.png 885w,
/static/43cf0a876f7d5381e7b9f1d2dc7b1fb5/c83ae/image-20191223192249290.png 1180w,
/static/43cf0a876f7d5381e7b9f1d2dc7b1fb5/cc6fe/image-20191223192249290.png 1860w"
sizes="(max-width: 590px) 100vw, 590px"
style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"
loading="lazy"
decoding="async"
/>
</a>
</span></p>
<h3>Flannel and VXLAN</h3>
<p>Flannel 和 VXLAN 有什么关系?</p>
<p>Flannel 采用搭建 overlay 网络的方法来满足<a href="https://kubernetes.io/docs/concepts/cluster-administration/networking/#the-kubernetes-network-model">k8s 的网络模型</a>,它提供了几个不通的 backend 来搭建 overlay 网络,其中 VXLAN 是最推荐的一个 backend。VXLAN 在 linux 内核中有良好支持,性能好。</p>
<p>Flannel 利用 VXLAN 来构建 overlay 网络,本身只维护几个表,其余工作都是 VXLAN 完成。基本原理和上面的手工操作是一样的,只是 flannel 把这个过程自动化了。</p>
<p>Flannel 一共迭代了 3 个版本:</p>
<h4>第一版</h4>
<p>每个机器上的 flannel daemon 会创建 VXLAN interface,并把自己注册为 L2MISS 和 L3MISS 事件的 handler。</p>
<p>L2MISS 是在 2 层网络的 miss,也就是从 IP 查找 MAC 这一步的 miss(对应上面的地址 2)。Flannel daemon 收到 miss 后会查找存储的数据来给出对应的 MAC 地址,并将其加入 arp 缓存。</p>
<p>L3MISS 是 host 不知道某个 MAC 地址应该发往哪里时产生的 miss(对应上面的地址 3)。收到 miss 后给出 ip 地址,并加入 fdb。</p>
<p>这个版本每个机器上需要有:</p>
<ul>
<li>1 条路由表记录(指向 VXLAN interface)</li>
<li>每个最近通信过的 container1 条 arp 表记录</li>
<li>每个主机 1 条 fdb 表记录</li>
</ul>
<h4>第二版</h4>
<p>去掉 L3MISS handler。每次新机器加入集群时,直接添加在所有主机上添加好所需的 fdb 记录。</p>
<h4>第三版</h4>
<p>去掉 L2MISS handler。需要添加 n-1 条路由表记录,这些路由表记录和前面不同的是,除了将路由指向 VXLAN interface,还指定了通过 VXLAN 之后下一跳的地址(对应主机的 ip 地址)。这样就只需要每个主机 1 条 arp 记录,以及每个主机 1 条 fdb 记录。</p>
<p>这样 flannel daemon 就不需要监听任何内核的事件,只需要在机器加入、退出集群的时候维护好这些记录。即使 flannel daemon 挂了整个 overlay 网络还是能(在很长一段时间内)正常运行。</p>
<h3>Reference</h3>
<ol>
<li><a href="https://github.com/yangyuqian/k8s-the-hard-way/blob/master/network/vxlan-flannel-cn.md">vxlan 在 Flannel 中的 Overlay 网络的实现</a></li>
<li><a href="https://cizixs.com/2017/09/25/vxlan-protocol-introduction/">vxlan 协议原理简介</a></li>
<li><a href="https://www.sdnlab.com/21143.html">容器 Flannel vxlan 基本原理和验证</a></li>
<li><a href="https://tools.ietf.org/html/rfc7348">rfc7348</a></li>
<li><a href="https://github.com/coreos/Flannel/blob/master/backend/vxlan/vxlan.go#L18">Flannel design notes and history</a></li>
</ol></content:encoded></item><item><title><![CDATA[Kubernetes 101]]></title><description><![CDATA[Kubernetes 101 Notion Kubeadm 安装 k8s Notion]]></description><link>https://mingxinwei.github.io/Kubernenes 101/</link><guid isPermaLink="false">https://mingxinwei.github.io/Kubernenes 101/</guid><pubDate>Mon, 21 Oct 2019 10:51:30 GMT</pubDate><content:encoded><h3>Kubernetes 101</h3>
<p><a href="https://www.notion.so/shuataren/Kubernetes-101-d1cf0021fbc846c9a846a7656f6a4da5">Notion</a></p>
<h2>Kubeadm 安装 k8s</h2>
<p><a href="https://www.notion.so/shuataren/Kubeadm-k8s-1ba0fc8ee5954255b25b7315b0bf0aa3">Notion</a></p></content:encoded></item><item><title><![CDATA[重新定义cd命令]]></title><description><![CDATA[折腾vim…]]></description><link>https://mingxinwei.github.io/Redefine cd/</link><guid isPermaLink="false">https://mingxinwei.github.io/Redefine cd/</guid><pubDate>Sat, 15 Jun 2019 10:20:30 GMT</pubDate><content:encoded><p>折腾vim的时候发现一位插件作者的有趣小工具:<a href="https://zhuanlan.zhihu.com/p/35015221">想跳转到一个目录,却只记得几个字母</a>。</p>
<p>按照文章里面的思路操作之后使用了几天,果然比原来方便一些。不过也注意到有一些可以改进的地方。</p>
<p>首先是这个工具使用了另外一个名字<code class="language-text">j</code>,在每次需要目录跳转的时候需要和<code class="language-text">cd</code>命令做区分:当需要正常跳转的时候用<code class="language-text">cd</code>;模糊跳转的时候使用<code class="language-text">j</code>。这也是我使用<code class="language-text">z</code>时候的一个痛点,对于<code class="language-text">cd</code>这样一个高频命令每次输入前需要在脑子里做个if还挺累的。</p>
<p>所以我往前再进一步,直接把覆盖<code class="language-text">cd</code>命令,在不不改变<code class="language-text">cd</code>原有功能的前提下,增加了对历史访问过的目录的模糊跳转。(使用zsh)</p>
<div class="gatsby-highlight" data-language="bash"><pre class="language-bash"><code class="language-bash"><span class="token keyword">function</span> <span class="token function-name function">cd</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token builtin class-name">builtin</span> <span class="token builtin class-name">cd</span> <span class="token variable">$1</span> <span class="token operator"><span class="token file-descriptor important">2</span>></span>/dev/null
<span class="token keyword">if</span> <span class="token punctuation">[</span> <span class="token variable">$?</span> <span class="token parameter variable">-ne</span> <span class="token number">0</span> <span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token keyword">then</span>
<span class="token assign-left variable">dir</span><span class="token operator">=</span><span class="token variable"><span class="token variable">$(</span>desc_z_history <span class="token operator">|</span> fzy <span class="token parameter variable">--query</span> $1<span class="token variable">)</span></span>
<span class="token keyword">if</span> <span class="token punctuation">[</span> <span class="token string">"<span class="token variable">$dir</span>"</span> <span class="token operator">=</span> <span class="token string">"<span class="token variable">$1</span>"</span> <span class="token punctuation">]</span> <span class="token operator">||</span> <span class="token punctuation">[</span> <span class="token parameter variable">-z</span> <span class="token variable">$dir</span> <span class="token punctuation">]</span>
<span class="token keyword">then</span>
<span class="token builtin class-name">return</span>
<span class="token keyword">else</span>
<span class="token builtin class-name">builtin</span> <span class="token builtin class-name">cd</span> <span class="token variable">$dir</span>
<span class="token keyword">fi</span>
<span class="token keyword">fi</span>
<span class="token punctuation">}</span></code></pre></div>
<p>还有一个可优化的点是性能,在使用js脚本时会感知到命令启动有一点卡顿。</p>
<p>使用js脚本当然很方便,不过他的启动时间相比小runtime的编译型语言来说还是差很多。而在<code class="language-text">cd</code>这样的命令里,延时是比较重要的。</p>
<p>测量一下:</p>
<div class="gatsby-highlight" data-language="text"><pre class="language-text"><code class="language-text">-> time descZHistory
descZHistory 0.06s user 0.02s system 95% cpu 0.091 total</code></pre></div>
<p>执行时间接近100ms。再看一下Node自己启动的时间:</p>
<div class="gatsby-highlight" data-language="text"><pre class="language-text"><code class="language-text">-> time node -e ''
node -e '' 0.06s user 0.02s system 95% cpu 0.080 total</code></pre></div>
<p>Node自己启动的时间就达到80-90ms左右,在这个场景下是不可忍受的。</p>
<p>所以换用Rust实现:</p>
<div class="gatsby-highlight" data-language="rust"><pre class="language-rust"><code class="language-rust"><span class="token keyword">use</span> dirs<span class="token punctuation">;</span>
<span class="token keyword">use</span> regex<span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token namespace">std<span class="token punctuation">::</span></span>fs<span class="token punctuation">;</span>
<span class="token keyword">use</span> <span class="token namespace">std<span class="token punctuation">::</span>io<span class="token punctuation">::</span></span><span class="token punctuation">{</span><span class="token keyword">self</span><span class="token punctuation">,</span> <span class="token class-name">Write</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">fn</span> <span class="token function-definition function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">if</span> <span class="token keyword">let</span> <span class="token class-name">Some</span><span class="token punctuation">(</span>home_dir<span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token namespace">dirs<span class="token punctuation">::</span></span><span class="token function">home_dir</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">let</span> data <span class="token operator">=</span> <span class="token namespace">fs<span class="token punctuation">::</span></span><span class="token function">read_to_string</span><span class="token punctuation">(</span>home_dir<span class="token punctuation">.</span><span class="token function">as_path</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span><span class="token string">".z"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">expect</span><span class="token punctuation">(</span><span class="token string">"Open file"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> re <span class="token operator">=</span> <span class="token namespace">regex<span class="token punctuation">::</span></span><span class="token class-name">Regex</span><span class="token punctuation">::</span><span class="token function">new</span><span class="token punctuation">(</span><span class="token string">r"\d+$"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">unwrap</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> <span class="token keyword">mut</span> lines<span class="token punctuation">:</span> <span class="token class-name">Vec</span><span class="token operator">&lt;</span><span class="token class-name">Vec</span><span class="token operator">&lt;</span><span class="token operator">&amp;</span><span class="token keyword">str</span><span class="token operator">>></span> <span class="token operator">=</span> data
<span class="token punctuation">.</span><span class="token function">lines</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token closure-params"><span class="token closure-punctuation punctuation">|</span>l<span class="token closure-punctuation punctuation">|</span></span> re<span class="token punctuation">.</span><span class="token function">is_match</span><span class="token punctuation">(</span>l<span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token closure-params"><span class="token closure-punctuation punctuation">|</span>l<span class="token closure-punctuation punctuation">|</span></span> <span class="token punctuation">{</span>
<span class="token keyword">let</span> arr<span class="token punctuation">:</span> <span class="token class-name">Vec</span><span class="token operator">&lt;</span><span class="token operator">&amp;</span><span class="token keyword">str</span><span class="token operator">></span> <span class="token operator">=</span> l<span class="token punctuation">.</span><span class="token function">split</span><span class="token punctuation">(</span><span class="token char">'|'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">collect</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
arr
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">collect</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
lines<span class="token punctuation">.</span><span class="token function">sort_by</span><span class="token punctuation">(</span><span class="token closure-params"><span class="token closure-punctuation punctuation">|</span>a<span class="token punctuation">,</span> b<span class="token closure-punctuation punctuation">|</span></span> <span class="token punctuation">{</span>
<span class="token keyword">let</span> score_a <span class="token operator">=</span> a<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">unwrap</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">parse</span><span class="token punctuation">::</span><span class="token operator">&lt;</span><span class="token keyword">f64</span><span class="token operator">></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">unwrap</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> score_b <span class="token operator">=</span> b<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">unwrap</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">parse</span><span class="token punctuation">::</span><span class="token operator">&lt;</span><span class="token keyword">f64</span><span class="token operator">></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">unwrap</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
score_b<span class="token punctuation">.</span><span class="token function">partial_cmp</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>score_a<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">unwrap</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> stdout <span class="token operator">=</span> <span class="token namespace">io<span class="token punctuation">::</span></span><span class="token function">stdout</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> <span class="token keyword">mut</span> handle <span class="token operator">=</span> <span class="token namespace">io<span class="token punctuation">::</span></span><span class="token class-name">BufWriter</span><span class="token punctuation">::</span><span class="token function">new</span><span class="token punctuation">(</span>stdout<span class="token punctuation">.</span><span class="token function">lock</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">for</span> l <span class="token keyword">in</span> lines<span class="token punctuation">.</span><span class="token function">iter</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token macro property">writeln!</span><span class="token punctuation">(</span>handle<span class="token punctuation">,</span> <span class="token string">"{}"</span><span class="token punctuation">,</span> l<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">unwrap</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">unwrap</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
<span class="token macro property">eprintln!</span><span class="token punctuation">(</span><span class="token string">"Can not get home dir"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div>
<p>Rust的结果:</p>
<div class="gatsby-highlight" data-language="text"><pre class="language-text"><code class="language-text">-> time desc_z_history
desc_z_history 0.00s user 0.00s system 34% cpu 0.010 total</code></pre></div>
<p>整个程序的时间能控制在10-20ms左右,达到要求了。</p>
<p>这样这个命令就比较令人满意了,在这个命令的基础上可以做一些小拓展,例如我加了一些打开常用编辑器的辅助命令,实现模糊查询并使用编辑器打开某个项目/文件:</p>
<div class="gatsby-highlight" data-language="bash"><pre class="language-bash"><code class="language-bash"><span class="token keyword">function</span> <span class="token function-name function">code_editor_base</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">if</span> <span class="token punctuation">[</span> <span class="token parameter variable">-z</span> <span class="token variable">$2</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment"># if not passing parameter, open current directory</span>
<span class="token keyword">then</span>
<span class="token variable">$1</span> <span class="token builtin class-name">.</span>
<span class="token builtin class-name">return</span>
<span class="token keyword">fi</span>
<span class="token keyword">if</span> <span class="token punctuation">[</span> <span class="token parameter variable">-f</span> <span class="token variable">$2</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment"># if file exists, open the file directly</span>
<span class="token keyword">then</span>
<span class="token variable">$1</span> <span class="token variable">$2</span>
<span class="token keyword">else</span>
<span class="token builtin class-name">cd</span> <span class="token variable">$2</span>
<span class="token variable">$1</span> <span class="token builtin class-name">.</span>
<span class="token keyword">fi</span>
<span class="token punctuation">}</span>
<span class="token keyword">function</span> <span class="token function-name function">c</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
code_editor_base code <span class="token variable">$1</span>
<span class="token punctuation">}</span>
<span class="token keyword">function</span> <span class="token function-name function">v</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
code_editor_base <span class="token function">vim</span> <span class="token variable">$1</span>
<span class="token punctuation">}</span>
<span class="token keyword">function</span> <span class="token function-name function">gol</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
code_editor_base goland <span class="token variable">$1</span>
<span class="token punctuation">}</span>
<span class="token keyword">function</span> <span class="token function-name function">cha</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
code_editor_base charm <span class="token variable">$1</span>
<span class="token punctuation">}</span>
<span class="token keyword">function</span> <span class="token function-name function">i</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
code_editor_base idea <span class="token variable">$1</span>
<span class="token punctuation">}</span></code></pre></div></content:encoded></item></channel></rss>