<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>归档 on 涯余的博客</title><link>https://hangyan.github.io/post/</link><description>Recent content in 归档 on 涯余的博客</description><generator>Hugo</generator><language>zh</language><managingEditor>hang.yan@hotmail.com (涯余)</managingEditor><webMaster>hang.yan@hotmail.com (涯余)</webMaster><lastBuildDate>Fri, 21 Aug 2026 10:00:00 +0800</lastBuildDate><atom:link href="https://hangyan.github.io/post/index.xml" rel="self" type="application/rss+xml"/><item><title>把 117KB 配置塞进 64KB 限制：节点镜像元数据的压缩与裁剪实践</title><link>https://hangyan.github.io/post/2026-08-21-breaking-64kb-metadata-compression/</link><pubDate>Fri, 21 Aug 2026 10:00:00 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2026-08-21-breaking-64kb-metadata-compression/</guid><description>&lt;p&gt;最近在给云原生节点镜像打包内置组件时，遇到了一个底层的硬限制：底层基础设施的镜像管理系统对单条元数据属性（Metadata Property）设置了严格的 &lt;strong&gt;64KB（65,536 字符）长度上限&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;随着组件功能越来越丰富，里面内嵌的 CRD、OpenAPI Schema、网络拓扑模板越来越大，打包出来的配置字符串直接冲到了 &lt;strong&gt;117KB+&lt;/strong&gt;，导致镜像在注册和导入阶段直接报错拦截。&lt;/p&gt;
&lt;p&gt;由于下游控制器和模板引擎在节点初始化时必须依赖这些声明式配置，我们不能直接删减核心功能。本文记录我们如何在不破坏任何模板语法、类型校验和向后兼容的前提下，一步步把体积从 117KB 压进 64KB 安全线以内的过程。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="问题与约束"&gt;问题与约束&lt;/h2&gt;
&lt;h3 id="1-为什么配置会超标"&gt;1. 为什么配置会超标？&lt;/h3&gt;
&lt;p&gt;我们需要在节点镜像元数据中预置一整套 Kubernetes 声明式清单：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Package 扩展包定义与 CRD&lt;/strong&gt;：基础控制器与系统组件清单。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Data Values Schema&lt;/strong&gt;：用于参数校验和默认值填充的 OpenAPI v3 Schema。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;渲染模板&lt;/strong&gt;：带有动态宏（如 &lt;code&gt;#@&lt;/code&gt; 指令）的 YAML/YTT 模板。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这套配置的流转链路如下：
&lt;code&gt;原始 YAML 清单 → 内联处理与预打包 → 压缩并 Base64 编码 → 写入镜像元数据属性 → 节点启动时由控制面解压并渲染&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="2-核心限制"&gt;2. 核心限制&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;硬限制&lt;/strong&gt;：单个属性字符串长度必须 &lt;code&gt;≤ 65,536&lt;/code&gt; 字符。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;零破坏&lt;/strong&gt;：所有的优化必须是无损的，下游模板引擎的动态渲染、参数校验和默认值行为必须与优化前 100% 一致。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="第一步升级压缩算法gzip--brotli-l11"&gt;第一步：升级压缩算法（Gzip → Brotli L11）&lt;/h2&gt;
&lt;p&gt;面对最初 117KB+ 的体积，最直接的想法是换一个压缩率更高的算法。&lt;/p&gt;
&lt;p&gt;此前流水线里默认用的是标准的 &lt;code&gt;gzip (Deflate)&lt;/code&gt;。我们针对这组结构高度重复的 YAML/JSON 文本，拉了几种主流算法做基准测试：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style="text-align: left"&gt;压缩算法&lt;/th&gt;
&lt;th style="text-align: center"&gt;压缩等级&lt;/th&gt;
&lt;th style="text-align: center"&gt;构建期压缩耗时&lt;/th&gt;
&lt;th style="text-align: center"&gt;运行时解压耗时&lt;/th&gt;
&lt;th style="text-align: center"&gt;最终 Base64 字符数&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style="text-align: left"&gt;&lt;strong&gt;Gzip&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: center"&gt;6 / 9&lt;/td&gt;
&lt;td style="text-align: center"&gt;&amp;lt; 10ms&lt;/td&gt;
&lt;td style="text-align: center"&gt;&amp;lt; 1ms&lt;/td&gt;
&lt;td style="text-align: center"&gt;~107,000 - 117,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left"&gt;&lt;strong&gt;Zstd&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: center"&gt;19&lt;/td&gt;
&lt;td style="text-align: center"&gt;~15ms&lt;/td&gt;
&lt;td style="text-align: center"&gt;&amp;lt; 1ms&lt;/td&gt;
&lt;td style="text-align: center"&gt;~72,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left"&gt;&lt;strong&gt;Brotli&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: center"&gt;&lt;strong&gt;11&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: center"&gt;~90ms&lt;/td&gt;
&lt;td style="text-align: center"&gt;&amp;lt; 2ms&lt;/td&gt;
&lt;td style="text-align: center"&gt;&lt;strong&gt;66,109&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="为什么选择-brotli-l11"&gt;为什么选择 Brotli L11？&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;匹配一次构建、多次分发的场景&lt;/strong&gt;：节点镜像在构建阶段多花几十毫秒做高强度压缩是完全可以接受的，只要运行时解压足够快即可（Brotli 解压耗时小于 2ms）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;文本字典与大窗口优势&lt;/strong&gt;：Brotli 拥有更大的滑动窗口（最高 16MB），且内置了专门针对 JSON/YAML/XML 等结构化文本的静态字典。在 Kubernetes 这种大量重复 key 和缩进的场景下，Brotli 相比 Gzip 带来了接近 40% 的体积下降。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;结果&lt;/strong&gt;：Brotli L11 把体积从 117KB 压到了 &lt;strong&gt;66,109 字符&lt;/strong&gt;。虽然效果显著，但离 65,536 的上限依然超出了 &lt;strong&gt;573 个字符&lt;/strong&gt;。这说明光靠通用压缩算法已经不够了，必须对文本内容本身动手术。&lt;/p&gt;</description></item><item><title>TC 的一些有趣功能</title><link>https://hangyan.github.io/post/2021-tc-ebpf/</link><pubDate>Sat, 18 Dec 2021 19:29:35 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2021-tc-ebpf/</guid><description>&lt;p&gt;最近在看 TC 和 eBPF 相关的一些介绍。TC 本身的概念比较复杂，想要理清并不容易。加上 eBPF 的引入就更复杂了。本文用来记录与此相关的一些发现。&lt;/p&gt;
&lt;h2 id="clsact"&gt;clsact&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;clsact&lt;/code&gt; 即是用来给 TC + eBPF 是使用的一个 &lt;code&gt;qdisc&lt;/code&gt;. &lt;code&gt;qdisc&lt;/code&gt; 可以简单理解为使用某种算法(FIFO)的 Queue. &lt;code&gt;clsact&lt;/code&gt; 的特点是:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不用排队&lt;/li&gt;
&lt;li&gt;主要就是为了 attach ebpf 用的&lt;/li&gt;
&lt;li&gt;在 ingress/egress 都提供了 hook&lt;/li&gt;
&lt;li&gt;lock free&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="ingress-与-egress-的不同"&gt;ingress 与 egress 的不同&lt;/h2&gt;
&lt;p&gt;不太确定直接的原因是啥，目前的 qdisc 只在 egress 方向上有。一般的 rate-limiting 都是在 egress 上实现， ingress上的比较困难。一个原因是难以对源头进行限速。&lt;/p&gt;
&lt;h2 id="multiple-qdisc"&gt;multiple qdisc&lt;/h2&gt;
&lt;p&gt;多个 qdisc 在 egress 上是很常见的。加了 clasact 之后就更常见了。那么 clsact 和他们什么关系？&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://hangyan.github.io/images/2021-tc-ebpf/clsact-qdisc.png"&gt;&lt;/p&gt;
&lt;p&gt;这是我从另一个 Topic 摘过来的图。可以看出来， clsact 是在普通的 qdisc 之前的。&lt;/p&gt;
&lt;h2 id="direct-action-mode"&gt;direct-action mode&lt;/h2&gt;
&lt;p&gt;请参考: &lt;a href="https://qmonnet.github.io/whirl-offload/2020/04/11/tc-bpf-direct-action/"&gt;Understanding tc “direct action” mode for BPF&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Cilium 的 Host Firewall 功能</title><link>https://hangyan.github.io/post/2021-cilium-case-1/</link><pubDate>Fri, 10 Dec 2021 22:29:53 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2021-cilium-case-1/</guid><description>&lt;p&gt;Cilium CNI 之前一直看文档，功能大概了解，eBPF也大略看过。但一直没有深入从代码角度去研究。它的 bpf 相关代码做的比较全，而且比较老，跟现在网上的 BCC, CO-RW 还不太一样，而且代码量又大，所以读起来并不容易。这次借着 Host Firewall 功能的探究，深入看下。不可能面面俱到，但尽量多涉及一些。这篇文章在后面可能会继续更新，如果有新的发现的话。&lt;/p&gt;
&lt;h2 id="一些基本概念"&gt;一些基本概念&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Endpoint&lt;/code&gt;: 可以理解为跟 device 挂钩的概念(host, pod 等等)，还有特殊的 health endpoint等等。Endpoint有ID,也有对应的 Identity 和自己的 Policy. Endpoint 是 Node Scope 的概念。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Identity&lt;/code&gt;: 跟 label 挂钩。同一个deploy的两个Pod, 有不同的endpoint,但有同样的 identity. K8s Node共用同样的 Identity. Cilium 的 NetworkPolicy 即是针对于 Identity的。这个很好理解，二者主要都是使用 label 来做 filter. 跟 Endpoint 不同， Identity 是 Cluster Scope 的概念(label当然跟node无关)。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;KV Store&lt;/code&gt;: 用来存储一些映射关系。Labels, Identity, Address, Node等这些元素之间的各种映射关系。Cilium 要实现 NetworkPolicy, 那么就需要这些映射关系。比如 Address -&amp;gt; Identity 等等。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不同的 CNI 在架构方面的差别还是挺大的。比如 KV Store 的需求，有的 CNI 就完全不需要。对 Cilium 来说，当有新的 NetworkPolicy时, agent 来计算 NetworkPolicy,如果发现自己所在的 Node 上有 Endpoint 受影响，那么就需要对此 Endpoint 做一些 Update (Regenerate BPF Programs等等)，其他的 Node 则不需要。不同的 agent 则依赖于 KV Store 来互相交换一些数据。
引用一篇架构图所示:&lt;/p&gt;</description></item><item><title>eBPF Loop</title><link>https://hangyan.github.io/post/2021-ebpf-loop/</link><pubDate>Fri, 03 Dec 2021 15:54:37 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2021-ebpf-loop/</guid><description>&lt;p&gt;eBPF Loop 是个老大难问题，只要实际开发大概率都会遇到。Kernel 版本越高，loop 的支持也越多。最近在 LWN 上又看到一篇关于 bpf loop 的新实现，如果能合并进去，那么就有三种 loop 得方式了:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;bounded loop: 跟传统编程模式很像，但loop的count必须是已知的&lt;/li&gt;
&lt;li&gt;map iterator: 针对 bpf map 的迭代器，可以用来遍历 map&lt;/li&gt;
&lt;li&gt;bpf_loop 函数: 最新的提议，能让 verifier 处理的更快&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;目前的 verifier 的策略是比较激进的，因为它没法验证所有的场景，所以有可能会对一些实际正确的程序验证不通过。&lt;code&gt;bpf_loop&lt;/code&gt; 主要就是从这个角度来考虑问题，相当于对最开始的 bounded loop 做的一个优化。下面将详细介绍。&lt;/p&gt;
&lt;h2 id="bounded-loop"&gt;Bounded Loop&lt;/h2&gt;
&lt;p&gt;Kernel 5.3 之前, eBPF 不支持 loop. 要想实现类似功能，而且前提是明确 loop 的次数，那么基本上只能靠 &lt;code&gt;unroll&lt;/code&gt; 来实现。示例如下:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#pragma clang loop unroll(full)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="cm"&gt;/* Do stuff ... */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;相当于把 loop 展开。5.3 及之后，就不用这样了。&lt;/p&gt;
&lt;h2 id="map-iterator"&gt;MAP Iterator&lt;/h2&gt;
&lt;p&gt;Bounded Loop 只能解决一部分的问题。总会有 unbounded loop 的场景。5.13 引入了 map iterator, 支持对 bpf map做遍历。考虑到 bpf map 在 eBPF 程序中的普遍性，算是一个比较好的解决方案了.&lt;/p&gt;</description></item><item><title>eBPF Timeline</title><link>https://hangyan.github.io/post/2021-ebpf-timeline/</link><pubDate>Tue, 21 Sep 2021 10:42:52 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2021-ebpf-timeline/</guid><description>&lt;p&gt;bcc 的文档已经做了很多详细的介绍，关于 kernel 中 eBPF 的变化: &lt;a href="https://github.com/iovisor/bcc/blob/master/docs/kernel-versions.md"&gt;https://github.com/iovisor/bcc/blob/master/docs/kernel-versions.md&lt;/a&gt;. 本文记录的是我在测试过程中发现的几个比较重要的节点。&lt;/p&gt;
&lt;h2 id="52"&gt;5.2&lt;/h2&gt;
&lt;p&gt;这块没找到详细描述，是根据程序实测出来的。在运行包含 map 的 eBPF 程序时，有如下错误:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;failed to load objects: field Ingress: program _ingress: map .rodata: map create: read- and write-only maps not supported (requires &amp;gt;= v5.2)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;错误信息提示的比较清楚，5.2内核及以上的就可以。不太确定为啥用上了 read/write only的 maps, 猜测是 libbpf 内部的实现机制导致的。因为这个问题，我目前将我测试的 eBPF 程序支撑的最低内核设定为 5.2 了。这点挺可惜的，因为 4.x 的内核对 eBPF 的很多支持已经很完善了， ubuntu 18.04 就是 4.18 的内核。&lt;/p&gt;
&lt;h2 id="58"&gt;5.8&lt;/h2&gt;
&lt;p&gt;kernel 默认带了 BTF 文件。这个 5.8 是不准确的，但确实没查到具体在哪个 kernel 版本默认带的。ubuntu 20.04 没有， 20.10 有了，暂且将其作为开始版本。kernel 对 BTF 的支持从 4.x 就有了，只不过最开始不是默认带的，如果需要，需要自己 打开开关并重新编译内核。如果考虑生产环境，这个其实挺麻烦的。如果设定为只支持自带 BTF 文件的版本，那就省事多了。&lt;/p&gt;</description></item><item><title>eBPF, iptables, nftables</title><link>https://hangyan.github.io/post/2021-ebpf-iptables/</link><pubDate>Mon, 20 Sep 2021 19:56:17 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2021-ebpf-iptables/</guid><description>&lt;p&gt;把它们三个列在一起，是因为确实有错综复杂的关系。我也想理一理他们在网络方面的前景,比较侧重于 K8s 和 CNI 方面.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;iptables&lt;/code&gt;: kube-proxy最开始基于 iptables 来实现了 service 的负载均衡，只支持随机算法。因为 iptables 本身的局限性，目前提供了 ipvs 模式.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;nftables&lt;/code&gt;: 可以说是 iptables 的替代品，更好的API及性能，但是因为 iptables 的遗留使用率太高，暂时难以看到全面替代的可能。&lt;/li&gt;
&lt;li&gt;&lt;code&gt; eBPF&lt;/code&gt;: 影响越来越多，有可能会在很多场景下替代 iptables&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这是简要的说明。下面将根据不同侧面来详细分析一下。&lt;/p&gt;
&lt;h2 id="iptables"&gt;iptables&lt;/h2&gt;
&lt;p&gt;两个主要问题:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;规则数量线性增长&lt;/li&gt;
&lt;li&gt;更新规则需要整个替换掉，无法保证原子性。更新规则的效率也很低。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="ipset"&gt;ipset&lt;/h2&gt;
&lt;p&gt;ipset 是 iptables 的一个扩展，主要解决的问题是 iptables 的线性增长问题。考虑到 iptables 的现实中的普及性以及大家求稳的心态， ipset 在很多场景下仍是一个不错的选择。其基本思想是用 hash 等数据结构来替代原来的线性匹配，这种设计我们在很多地方都可以见到，比如 ovs, eBPF等等。&lt;/p&gt;
&lt;p&gt;nftables 作为 iptables 的继任者，很多设计已经了 ipset 里的设计。二者有一定的相似性。&lt;/p&gt;
&lt;h2 id="ipvs"&gt;ipvs&lt;/h2&gt;
&lt;p&gt;没找到太好的深入介绍的资料，但根据网上找的测试数据，比 iptables 好很多.&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://www.tigera.io/wp-content/uploads/2019/04/Picture1.png"&gt;&lt;/p&gt;
&lt;p&gt;Service 数量少时不明显，数量很大时差距就出来了。&lt;/p&gt;
&lt;h2 id="kube-proxy"&gt;kube-proxy&lt;/h2&gt;
&lt;p&gt;eBPF, iptables, nftables,ipvs, ipset这些东西，最终在 k8s 上的落脚点都是 kube-proxy. kube-proxy实现了各种 Service 的功能，底层用什么，是可能会变的，从最开始的 iptables 到现在的 ipvs, 以后真用其他的东西也说不准. 除了 iptables 和 ipvs, 我们也能找到其他方面的尝试:&lt;/p&gt;</description></item><item><title>CNI Notes: IPAM</title><link>https://hangyan.github.io/post/2021-cni-ipam/</link><pubDate>Fri, 17 Sep 2021 14:05:47 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2021-cni-ipam/</guid><description>&lt;p&gt;CNI相关笔记，本文主要记录下 IPAM 功能相关。&lt;/p&gt;
&lt;p&gt;CNI的职责包括给 Container 分配网络设备，并且分配IP. IPAM(IP Address Management)就是指IP分配相关的功能和规范。&lt;/p&gt;
&lt;p&gt;CNI本身只是一个规范，不同的实现在 IPAM 方面的功能集也不尽相同。下面将首先尝试说明下不同 CNI在这块的实现。&lt;/p&gt;
&lt;h2 id="cni实现"&gt;CNI实现&lt;/h2&gt;
&lt;h3 id="calico"&gt;Calico&lt;/h3&gt;
&lt;p&gt;calico开发的IPAM组件叫 &lt;code&gt;calico-ipam&lt;/code&gt;, 它使用 calico 的 IP pool resource来管理 IP. 它的主要优势就是可以将 pool 分割成小的 block, 并且可以动态对分配给不同的 node. node 上的 ip 利用率会更高，并且可扩展性也很好。calico 目前也支持给不同的 namespace分配不同的 ip pool. 从大的方面来看，其实最终 各个CNI 实现要解决的问题都是类似的，一般区别只是 dataplane 以及具体的 CRD的设计不太一样。所以这些功能很可能(已经有了或者以后会有)出现在其他的 CNI 上面。&lt;/p&gt;
&lt;p&gt;下面看一个实际的例子:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;projectcalico.org/v3&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;IPPool&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;external-pool&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;cidr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;172.16.0.0&lt;/span&gt;&lt;span class="l"&gt;/26&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;blockSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;29&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ipipMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Always&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;natOutgoing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nn"&gt;---&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;projectcalico.org/v3&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;IPPool&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;internal-pool&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;cidr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;192.169.0.0&lt;/span&gt;&lt;span class="l"&gt;/24&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;blockSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;29&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ipipMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Always&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;natOutgoing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里面可以创建两个 IPPool, 并且可以通过给 NS 打 label 的方式，分配给不同的 NS.&lt;/p&gt;</description></item><item><title>Rebuild Kernel 生成 BTF 文件</title><link>https://hangyan.github.io/post/2021-kernel-btf/</link><pubDate>Wed, 01 Sep 2021 11:42:02 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2021-kernel-btf/</guid><description>&lt;p&gt;在测试 golang + libbpf + ebpf 的时候发现一个问题，load ebpf object时，报错说找不到 BTF 文件。这个很奇怪，我已经 include 了 &lt;code&gt;vmlinux.h&lt;/code&gt;, 按说不应该再依赖于 BTF 文件。看了下 libbpf 的代码,大概出错的地方在:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/*
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; * Probe few well-known locations for vmlinux kernel image and try to load BTF
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; * data out of it to use for target BTF.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;btf&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;btf__load_vmlinux_btf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;path_fmt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;raw_btf&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;locations&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="cm"&gt;/* try canonical vmlinux BTF through sysfs first */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;/sys/kernel/btf/vmlinux&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="cm"&gt;/* raw BTF */&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="cm"&gt;/* fall back to trying to find vmlinux ELF on disk otherwise */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;/boot/vmlinux-%1$s&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;/lib/modules/%1$s/vmlinux-%1$s&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;/lib/modules/%1$s/build/vmlinux&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;/usr/lib/modules/%1$s/kernel/vmlinux&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;/usr/lib/debug/boot/vmlinux-%1$s&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;/usr/lib/debug/boot/vmlinux-%1$s.debug&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;/usr/lib/debug/lib/modules/%1$s/vmlinux&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;PATH_MAX&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;utsname&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;btf&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;btf&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;uname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nf"&gt;ARRAY_SIZE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;locations&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;snprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PATH_MAX&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;locations&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;path_fmt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;release&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;access&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;R_OK&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;locations&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;raw_btf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;btf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;btf__parse_raw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;btf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;btf__parse_elf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;libbpf_get_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;btf&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;pr_debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;loading kernel BTF &amp;#39;%s&amp;#39;: %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;btf&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;pr_warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;failed to find valid kernel BTF&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;libbpf_err_ptr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;ESRCH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;猜测是用到了 CO-RE 相关的功能，走到了这块逻辑。奇怪的是我已经通过 &lt;code&gt;ddeb&lt;/code&gt; repo安装了相关的 vmlinux 文件。但还是不行。这个后续还是要查查，比较快的解决方案就是 rebuild kernel 带上 BTF 先跑通。&lt;/p&gt;</description></item><item><title>如何生成 vmlinux.h</title><link>https://hangyan.github.io/post/2021-vmlinux/</link><pubDate>Wed, 25 Aug 2021 13:28:21 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2021-vmlinux/</guid><description>&lt;p&gt;eBPF 的一些开发方案里涉及到 vmlinux.h, 它包含了kernel里所有用到的data structure等等。有了它可以方便地替代其他一大堆杂七杂八的头文件。网上基本上都提到了如何用 &lt;code&gt;vmlinux&lt;/code&gt; 来生成 &lt;code&gt;vmlinux.h&lt;/code&gt;, 但最开始的
&lt;code&gt;vmlinux&lt;/code&gt; 怎么来的并没有找到比较明确的文档。本来将提供一个比较全面的如何生成 &lt;code&gt;vmlinux.h&lt;/code&gt; 的文档.&lt;/p&gt;
&lt;h2 id="环境说明"&gt;环境说明&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;OS: ubuntu 20.04.1&lt;/li&gt;
&lt;li&gt;Kernel: 5.4.0-70&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="安装-vmlinux"&gt;安装 vmlinux&lt;/h2&gt;
&lt;p&gt;需要添加 ddeb 的源&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;deb http://ddebs.ubuntu.com &lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;lsb_release -cs&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; main restricted universe multiverse&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; sudo tee -a /etc/apt/sources.list.d/ddebs.list
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; -e &lt;span class="s2"&gt;&amp;#34;deb http://ddebs.ubuntu.com &lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;lsb_release -cs&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;-updates main restricted universe multiverse\ndeb http://ddebs.ubuntu.com &lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;lsb_release -cs&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;-proposed main restricted universe multiverse&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; sudo tee -a /etc/apt/sources.list.d/ddebs.list
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;repo key相关的&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install ubuntu-dbgsym-keyring
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;安装&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;apt-get update
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt-get install linux-image-&lt;span class="k"&gt;$(&lt;/span&gt;uname -r&lt;span class="k"&gt;)&lt;/span&gt;-dbgsym
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;最后安装好的文件在 &lt;code&gt;/usr/lib/debug/boot/&lt;/code&gt; 目录下。&lt;/p&gt;</description></item><item><title>卸载UOS</title><link>https://hangyan.github.io/post/2021-uninstall-uos/</link><pubDate>Tue, 25 May 2021 10:34:33 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2021-uninstall-uos/</guid><description>&lt;p&gt;用了大半年，最近卸载了。整体来说，不如最开始的期待。优点仍在，但缺点对于重度使用者来说，难以忍受。&lt;/p&gt;
&lt;p&gt;将常见应用做到开箱即用，这是个很大的贡献，也是目前所有 Linux 发行版里做的最好的一个，这点不可抹杀。但正如其他的一些本地化分支一样，稳定性都相比于 Debian/Ubuntu 大幅度降低了。这点在长期使用中会逐渐暴露出来，并且抵消掉前者带来的优势。如果UOS想继续好好做下去，后面的着力点也应该是这个。&lt;/p&gt;
&lt;p&gt;就我的体验来说，问题主要是以下几个：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;电脑发热明显，最开始是非夏天用的感觉还不明显，到了夏天非常严重。而换了ubuntu之后好转。&lt;/li&gt;
&lt;li&gt;移除了添加repo的一些命令，让开发者很难受&lt;/li&gt;
&lt;li&gt;界面稳定性差，基本上难以做到经常不关机&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这里面部分是 Deepin 的问题，部分是 UOS 的问题了。其他的国内的发行版总体来说都是更差。&lt;/p&gt;
&lt;p&gt;从产品角度来说，用户想要什么以及该怎么做其实是很明显的。既然有这个人力物力了，就应该定好优先级，把这些核心的问题解决好，这样才能普及开来。&lt;/p&gt;
&lt;p&gt;另外从美观程度上来讲，不算差，但也不算太好。很多方面也不如 Ubuntu 的 Unity. 如何用统一美观的设计语言去做出一个美观好用的界面，应该就是下一阶段的重点了。&lt;/p&gt;</description></item><item><title>Kubernetes 的 Server-Side Apply</title><link>https://hangyan.github.io/post/2021-ssa/</link><pubDate>Thu, 18 Feb 2021 15:25:47 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2021-ssa/</guid><description>&lt;p&gt;从 Kubernetes 1.18 开始,可以看到一个明显的变化就是资源的 YAML 在 metadata 部分多了很多信息:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;v1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Namespace&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;creationTimestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;2021-02-18T03:03:40Z&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;managedFields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;v1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;fieldsType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;FieldsV1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;fieldsV1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;f:status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;f:phase&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;{}&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;manager&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;kube-apiserver&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Update&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;2021-02-18T03:03:40Z&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;default&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;resourceVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;199&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;2f4536b5-7302-4dc1-9620-052a567c917c&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;finalizers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;kubernetes&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;phase&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Active&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;比如这个 NS 的例子, 其中大部分都是 &lt;code&gt;mangedFields&lt;/code&gt;部分.简单来讲,&lt;code&gt;managedFields&lt;/code&gt; 字段是用来声明一个资源的各个字段的具体的管理者是谁.我们可以参考一个有点类似的案例, Node 的 conditions 字段:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;conditions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;lastHeartbeatTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;2021-02-18T03:05:25Z&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;lastTransitionTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;2021-02-18T03:05:25Z&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Flannel is running on this node&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;FlannelIsUp&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;False&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;NetworkUnavailable&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;lastHeartbeatTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;2021-02-18T07:34:05Z&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;lastTransitionTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;2021-02-18T03:03:36Z&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;kubelet has sufficient memory available&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;KubeletHasSufficientMemory&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;False&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;MemoryPressure&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Contaions 是一个列表,不同的 Component 负责上报自己所观察到的信息,最终汇总到 Node 的 Status 下面.然后再合并汇总出一个整体的 &lt;code&gt;Ready&lt;/code&gt; 状态. &lt;code&gt;mangedFieds&lt;/code&gt; 也是有点类似的思路, 一个资源的不同部分是由不同组件/人负责维护的,显式的声明这种维护关系有助于各方协同地维护一个资源完整的状态.更直观的,我们可以看一个 Deployment 的例子. 首先,我们使用如下的 deployment 文件来创建&lt;/p&gt;</description></item><item><title>LB系列1 - Bandaid</title><link>https://hangyan.github.io/post/2021-lb-1-bandaid/</link><pubDate>Mon, 18 Jan 2021 13:33:12 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2021-lb-1-bandaid/</guid><description>&lt;p&gt;很多大厂的LB都是自研的，而且设计考量都不相同。本文介绍 Dropbox自研的LB，名字叫 Bandaid.参考链接: &lt;a href="https://dropbox.tech/infrastructure/meet-bandaid-the-dropbox-service-proxy"&gt;Meet Bandaid, the Dropbox service proxy&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="queue设计"&gt;Queue设计&lt;/h2&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://dropbox.tech/cms/content/dam/dropbox/tech-blog/en-us/2018/03/1st.png"&gt;&lt;/p&gt;
&lt;p&gt;Bandaid 处理 Queue 中的 Request 的思路是后近先出(Last in, First out)。当系统负载不高的时候，Queue中的数据其实不多，那么先进后出或者后进先出其实区别不大。如果负载过高，考虑到先进的排队太久，可能很快就timeout了，还不如优先处理后进来的 request.&lt;/p&gt;
&lt;h2 id="user-space的-read-queue"&gt;User Space的 Read Queue&lt;/h2&gt;
&lt;p&gt;Read Requests 会被放到一个用户态的 Queue 中，而不是 kernel 里的 queue中。这样做的原因是， 可以在用户态提前关闭已经被 client close的 request, 而不是等到真正的request经由 kernel, 然后 application 在处理的时候才发现 request 已经 close 了。尤其是 client 一般还带有重试，很容易把 Kernel queue打满。User Space 的 Queue 能够通过处理只把正常的 request 传给后面。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://dropbox.tech/cms/content/dam/dropbox/tech-blog/en-us/2018/03/2nd.png"&gt;&lt;/p&gt;
&lt;h2 id="权重以及-route划分"&gt;权重以及 Route划分&lt;/h2&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://dropbox.tech/cms/content/dam/dropbox/tech-blog/en-us/2018/03/4th.png"&gt;&lt;/p&gt;
&lt;p&gt;精细程度可以做到按照 Route 将一个 Service 的重要的 Route 以及不重要的 Route 按权重定向到不同的 Backend上。不同的 Queue 可以有不同的权重，rate limit, 并发度。&lt;/p&gt;</description></item><item><title>算法笔记(1): 链表及其应用</title><link>https://hangyan.github.io/post/2020-12-03-alg-linked-list/</link><pubDate>Thu, 03 Dec 2020 16:27:39 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2020-12-03-alg-linked-list/</guid><description>&lt;p&gt;最近准备系统的过一遍算法。一是因为看的很多项目最终多多少少都会涉及到算法，如果提前了解好会理解的更深入。二是之前也没有系统的看过，最近想定一个比较长久的学习计划。看文章的话网上质量参差不齐，有些东西只能这样弄，自己花时间慢慢找，总结，记录。算法这类前两天想看 LevelDB, 然后又得先看 LSM Tree, 然后发现还是得看红黑树。然后在网上找，发现文章一大堆，但写的都不行。我觉得想要了解红黑树的基本上肯定都会有一个疑惑“为啥要两种颜色”，可惜基本上没几篇文章能把这个东西讲清楚，知其然而不知其所以然。所以最终决定第一次在网上买了一个电子课，因为发现能讲的清楚的一篇文章是从这门课里摘录的。&lt;/p&gt;
&lt;p&gt;所以后面会尝试更新一个持续的算法系列，笔记性质的。记录一些感兴趣的点。然后才是其他的一些感兴趣的项目和技术。本身作为第一篇，跳过了一些更基础的，直接从链表开始了。&lt;/p&gt;
&lt;p&gt;链表本身也比较简单，基本原理都清楚。我觉得从应用层来看更有意思一点。主要就是两个&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;约瑟夫问题&lt;/li&gt;
&lt;li&gt;LRU等算法&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="约瑟夫问题"&gt;约瑟夫问题&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;阿橋问题&lt;/strong&gt;（有时也称为&lt;strong&gt;约瑟夫斯置换&lt;/strong&gt;），是一个出现在&lt;a href="https://zh.wikipedia.org/wiki/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6"&gt;计算机科学&lt;/a&gt;和&lt;a href="https://zh.wikipedia.org/wiki/%E6%95%B0%E5%AD%A6"&gt;数学&lt;/a&gt;中的问题。在计算机&lt;a href="https://zh.wikipedia.org/wiki/%E7%BC%96%E7%A8%8B"&gt;编程&lt;/a&gt;的算法中，类似问题又称为&lt;strong&gt;约瑟夫环&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;人们站在一个等待被处决的圈子里。 计数从圆圈中的指定点开始，并沿指定方向围绕圆圈进行。 在跳过指定数量的人之后，处刑下一个人。 对剩下的人重复该过程，从下一个人开始，朝同一方向跳过相同数量的人，直到只剩下一个人，并被释放。&lt;/p&gt;
&lt;p&gt;问题即，给定人数、起点、方向和要跳过的数字，选择初始圆圈中的位置以避免被处决。&lt;/p&gt;
&lt;p&gt;或者更加数学的问法: 已知 n 个人（以编号1，2，3…n分别表示）围坐在一张圆桌周围。从编号为 k 的人开始报数，数到 m 的那个人出圈；他的下一个人又从 1 开始报数，数到 m 的那个人又出圈；依此规律重复下去，直到剩余最后一个胜利者。&lt;/p&gt;
&lt;p&gt;这个问题可能是与单项循环链表最贴近的一个问题模型了。每个人都是一个节点，然后出局的人可以用操作指针的方法将其从链表中移除即可，最后剩下一个人的时候就是答案。下面是C代码&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdlib.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/*声明一个链表节点*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;typedef&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="c1"&gt;//数据域，存储编号数值
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="c1"&gt;//指针域，指向下一个节点
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/*创建链表节点的函数*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;CreatNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Node&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;malloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="c1"&gt;//将链表节点的数据域赋值为编号
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/*创建环形链表，存放整数1到n*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;CreatJoseph&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Node&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;CreatNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="c1"&gt;//创建链表节点，并完成赋值
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="c1"&gt;//如果是头结点
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;head&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="c1"&gt;//不是头结点，则指向下一个节点
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;q&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="c1"&gt;//末尾节点指向头结点，构成循环链表
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;head&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/*模拟运行约瑟夫环，每数到一个数，将它从环形链表中删除，并打印出来*/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;RunJoseph&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Node&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;CreatJoseph&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="c1"&gt;//创建循环链表形式的约瑟夫环
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="c1"&gt;//循环条件，当前链表数目大于1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="c1"&gt;//开始计数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;//第m个人出圈
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;q&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;%d--&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="c1"&gt;//输出出圈的序号
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;free&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;n最后剩下的数为：%dn&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;scanf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;%d %d&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;RunJoseph&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="lru算法"&gt;LRU算法&lt;/h2&gt;
&lt;p&gt;不考虑各种优化算法的话，LRU场景本身也是很适合用来链表实现。需求描述如下:&lt;/p&gt;</description></item><item><title>io_uring 介绍</title><link>https://hangyan.github.io/post/2020-11-27-io_uring/</link><pubDate>Fri, 27 Nov 2020 11:18:00 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2020-11-27-io_uring/</guid><description>&lt;p&gt;这几年内核带来了很多革命性的新特性。一个是 ebpf, 现在主要被广泛应用于网络处理，性能分析等领域。另一个是 io_uring, 带来了真正的全异步IO。本文将对 io_uring 做简要介绍。&lt;/p&gt;
&lt;h2 id="总览"&gt;总览&lt;/h2&gt;
&lt;p&gt;在 io_uring 之前，只有 aio 这个异步框架。为什么要重新弄一套，是因为 aio 自身限制比较多，比如:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;只支持 &lt;code&gt;direct_io&lt;/code&gt;.而O_DIRECT要求bypass缓存和size对齐等，直接影响了很多场景的使用。而对buffered IO，其表现为同步。&lt;/li&gt;
&lt;li&gt;即使满足了所有异步IO的约束，有时候还是可能会被阻塞，例如，等待元数据IO，或者存储设备的请求槽位都正在使用等等。&lt;/li&gt;
&lt;li&gt;存在额外的开销，每个IO提交需要拷贝64+8字节，每个IO完成需要拷贝32字节，这在某些场景下影响很可观。在使用完成event的时候需要非常小心，否则容易丢事件。IO总是需要至少2个系统调用（submit + wait-for-completion)，在spectre/meltdown开启下性能下降非常严重。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;aio本身扩展性也很差，很多基于aio的开发也经常需要用dirty hack的方式来满足自己的需求。Linux 自己对它的评价也不好:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;So I think this is ridiculously ugly.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;AIO is a horrible ad-hoc design, with the main excuse being “other, less gifted people, made that design, and we are implementing it for compatibility because database people — who seldom have any shred of taste — actually use it”.&lt;/em&gt;&lt;/p&gt;</description></item><item><title>KV系列1 - LMDB</title><link>https://hangyan.github.io/post/2020-11-26-lmdb/</link><pubDate>Thu, 26 Nov 2020 14:41:44 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2020-11-26-lmdb/</guid><description>&lt;p&gt;注: 本文仅为笔记，不怎么通顺和严谨。&lt;/p&gt;
&lt;p&gt;KV系列属于一个大规模场景的必备品，而且通常很多公司会选择自研，一方面是各种不同产品均有局限，而大家的需求都有差别。另一方面社区已经有一些比较好的 building blocks，可以方便地进行组装和修改。最常见的情境是，分布式KV的开发 = 选一个底层kv存储 + 一个分布式协议。比如TiDB,ETCD等等。最近看 cloudflare 的 &lt;a href="https://blog.cloudflare.com/introducing-quicksilver-configuration-distribution-at-internet-scale/"&gt;blog&lt;/a&gt;, 也是类似的场景。他们选的是 LMDB + 自研的分布式策略(称不上算法，比较简单)，也很好的满足了自己的需求。&lt;/p&gt;
&lt;h2 id="lmdb"&gt;LMDB&lt;/h2&gt;
&lt;p&gt;全称是&lt;code&gt; Lightning Memory-Mapped Database&lt;/code&gt;, 使用内存映射文件，读写性能比较高。&lt;/p&gt;
&lt;p&gt;LMDB的一些特性:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;支持APPEND模式，提高写操作的性能&lt;/li&gt;
&lt;li&gt;支持多进程/线程同时访问。这种场景下读性能可以随着实例数增加而线性提升。&lt;/li&gt;
&lt;li&gt;单独写不block读, 读也不 block 写。&lt;/li&gt;
&lt;li&gt;不需要 transcation log, 提高了写性能。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;实现上来讲，利用内存映射是一大特色。通常的文件读取操作，通过&lt;code&gt;read&lt;/code&gt;系统调用，要先把数据从硬盘 copy 到内核，然后再拷贝到用户空间。而 &lt;code&gt;mmap&lt;/code&gt;， 不直接进行数据拷贝，而是在缺页中断时进行处理。而且是直接拷贝到用户态，所以会比 &lt;code&gt;read&lt;/code&gt; 效率高些。另外，这种内存映射是只读的，也避免了程序错误破坏存储结构。写操作则是通过 write 系统调用完成，由系统来保证数据一致性。其他细节：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用 B+ tree.&lt;/li&gt;
&lt;li&gt;LMDB只允许单个写，性能有所降低，但是不再需要WAL日志，以及其他种种并发控制的冲突及代价。&lt;/li&gt;
&lt;li&gt;LMDB中，数据的基本操作单元是页，COW也是以页为单位。&lt;/li&gt;
&lt;li&gt;如果写操作比较多，那么数据版本也会很多，旧数据会占用大量空间。LMDB会将旧的页插入到一棵B+tree当中，然后等没有事物再用到它之后就可以重复利用。这样省去了定期清理操作，但是无法保证数据可以恢复到任意时刻了。&lt;/li&gt;
&lt;li&gt;数据访问可以直接返回内存指针，避免内存拷贝。&lt;/li&gt;
&lt;li&gt;COW保证存储结构一直是合法的。系统崩溃不会导致数据库处于一个不一致的状态。最坏的情况只是丢失了一些未提交的数据。根据一些学界的研究，尚没有发现因为使用 LMDB 导致数据损坏的案例。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img loading="lazy" src="http://wiki.dreamrunner.org/public_html/C-C++/Library-Notes/Files/lmdb-arch.svg"&gt;&lt;/p&gt;
&lt;p&gt;LMDB中事物的实现思路如下:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Atom（A）: LMDB中通过txn数据结构和cursor数据结构的控制，通过将脏页列表放入 dirtylist中，当txn进行提交时再一次性统一刷新到磁盘中或者abort时都不提交保证事务要不全成功、要不全失败。对于长事务，若页面spill到磁盘，因为COW技术，这些页面未与整棵B-Tree的rootpage产生关联，因此后续的事务还是不能访问到这些页面，同样保证了事务的原子性。&lt;/li&gt;
&lt;li&gt;Consistency(C): 有如上的操作,保证其数据就是一致的，不存在因为多线程同时写数据导致数据产生错误的情况。&lt;/li&gt;
&lt;li&gt;Isolation（I）：事务隔离通过锁控制（MUTEX），LMDB支持的锁互斥是进程级别/线程级别，支持的隔离方式为锁表支持，读读之间不锁，写等待读完成之后开始，读等待写完成后开始.&lt;/li&gt;
&lt;li&gt;Duration（D）:LMDB中，没有使用WAL、undo/redo log等技术来保证系统崩溃时数据库的可用性，其保证数据持续可用的技术是COW技术和只有一线程写技术。假如LMDB或者系统崩溃时，只有读操作，那么数据本来就没有发生变化，因此数据将不可能遭到破坏。假如崩溃时，有一个线程在进行写操作，则只需要判断最后的页面号与成功提交到数据库中的页面号是否一致，若不一致则说明写操作没有完成，则最后一个事务写失败，数据在最后一个成功的页面前的是正确的，后续的属于崩溃事务的，不能用，这样就保证了数据只要序列化到磁盘则一定可用，要不其就是还没有遵循ACI原则序列化到磁盘&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;总结来看，LMDB是一个极为优秀的产品。即使作者声称它主要是为了读场景而不是写场景，但实测的结果都不错。BUG少，稳定性强。&lt;/p&gt;
&lt;h2 id="cloudflare-的实践"&gt;Cloudflare 的实践&lt;/h2&gt;
&lt;p&gt;Cloudflare 需要一个分布式的 KV Storage 来存储用户配置信息，当用户做了改动之后，能很快地分发到所有的数据中心。最开始用的是&lt;code&gt;Kyoto Tycoon datastore&lt;/code&gt;, 在使用过程中发现了不少问题，最终切换到了 LMDB.&lt;/p&gt;</description></item><item><title>C语言中的 strtok 函数</title><link>https://hangyan.github.io/post/2020-11-24-strtok/</link><pubDate>Tue, 24 Nov 2020 13:22:33 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2020-11-24-strtok/</guid><description>&lt;p&gt;在看一个项目的代码时，发现了它用到了&lt;code&gt;strtok&lt;/code&gt; 这个API, 它的作用是 split 一个字符串。这个功能在各个语言都很常见，而且接口也大同小异，比如 Golang:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-golang" data-lang="golang"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;:=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;strings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;a,b,c&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;,&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Output: [a b c]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Python:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;txt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;hello, my name is Peter, I am 26 years old&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;txt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;, &amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;而 &lt;code&gt;strtok&lt;/code&gt; 的接口就比较奇怪了，看一个例子:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Live&lt;/span&gt; &lt;span class="n"&gt;Demo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;string.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;This is - www.tutorialspoint.com - website&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;-&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="cm"&gt;/* get the first token */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;strtok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="cm"&gt;/* walk through other tokens */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nf"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s"&gt;&amp;#34; %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;strtok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;输出为:&lt;/p&gt;</description></item><item><title>OpenKruise 简介</title><link>https://hangyan.github.io/post/2020-11-21-open-kruise/</link><pubDate>Sat, 21 Nov 2020 10:51:37 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2020-11-21-open-kruise/</guid><description>&lt;p&gt;Kubernetes 作为一个 building blocks 来说，已经逐渐趋于稳定。大家对于其功能的增强，已经逐渐开始以 CRD + Controller Pattern 为主。另一方面，仍然有不少尝试是针对像 Deployment/StatefulSet/DaemonSet 这样的原始功能的。OpenKruise项目就是专注于此。原生的三种 Workload 能满足于日常的需求，但并不完善。在 OpenKruise 项目中，我们可以看到，多种 Workload 之间提供的功能是有很多共性的，我们可以提炼出他们的一些共同的功能模块, 比如:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;分批升级&lt;/li&gt;
&lt;li&gt;灰度更新&lt;/li&gt;
&lt;li&gt;原地升级&lt;/li&gt;
&lt;li&gt;SideCar管理&lt;/li&gt;
&lt;li&gt;PVC管理&lt;/li&gt;
&lt;li&gt;Pod 固定名字&lt;/li&gt;
&lt;li&gt;&amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这些原子功能有些原生的 workload 已经具有，有些没有。OpenKruise项目通过将这些原子功能排列组合，提供了一些功能增强型的 workload.下面将一一介绍。&lt;/p&gt;
&lt;h2 id="sidecarset"&gt;SidecarSet&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;SidecarSet&lt;/code&gt; 定义了一些 sidecar 模板，然后通过 webhook 的方式注入到通过标签匹配的 Pod 中，这和 istio 等的方式比较像。它比较有用的地方在于，假设你希望给很多 Pod 注入一些通用的附加功能，比如 log, metrics或者init等功能，那么通过 SidecarSet 是一种比较好的方式。下面是一个 yaml 示例:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# sidecarset.yaml&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;apps.kruise.io/v1alpha1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;SidecarSet&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;test-sidecarset&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;matchLabels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;app&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;nginx&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;strategy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;rollingUpdate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;maxUnavailable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;containers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;sidecar1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;centos:6.7&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;sleep&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;999d&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# do nothing at all&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;volumeMounts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;log-volume&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;mountPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;/var/log&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;volumes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# this field will be merged into pod.spec.volumes&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;log-volume&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;emptyDir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;{}&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;v1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Pod&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;labels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;app&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;nginx&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# matches the SidecarSet&amp;#39;s selector&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;test-pod&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;containers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;app&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;nginx:1.15.1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个示例给 &lt;code&gt;test-pod&lt;/code&gt; 注入了一个 &lt;code&gt;sidecar1&lt;/code&gt;, 同时也加入了一个 log-volume.
另外，依据对 SidecarSet 不同字段的更新，相应的 Pod 有两种更新方式&lt;/p&gt;</description></item><item><title>eBPF系列2 - XDP</title><link>https://hangyan.github.io/post/2020-11-17-ebpf-2-xdp/</link><pubDate>Tue, 17 Nov 2020 23:16:48 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2020-11-17-ebpf-2-xdp/</guid><description>&lt;p&gt;XDP是指 &lt;code&gt;eXpress data path&lt;/code&gt;, 基于 ebpf 技术上的 高性能 data path. 其主要的意图是在网络 packet 处理的早期(网卡驱动处, 在数据包到达RX queue之后, 用hook的方式)，让用户可以编写 ebpf 程序来进行一些决策。 这个hook的位置早于所有的内存分配时期(sk_buffer)，也没有上下文切换,系统调用等开销, 所以对于性能的提升是很明显的。测试数据表明，在普通的硬件上， XDP 可以 dop 大概 2600w packets per cpu.&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://hangyan.github.io/images/ebpf/xdp-1.png"&gt;&lt;/p&gt;
&lt;p&gt;一个网络包的处理流程大致如下:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;网络包到达网卡&lt;/li&gt;
&lt;li&gt;从 NIC queue 拷贝到内存(DMA-backed ring buffer)中&lt;/li&gt;
&lt;li&gt;网卡驱动使用 NAPI look 触发 soft IRQs&lt;/li&gt;
&lt;li&gt;per cpu 特定线程处理网络包&lt;/li&gt;
&lt;li&gt;分配 socket_buffer(sk_buffer), 作为网络包的基本数据结构&lt;/li&gt;
&lt;li&gt;kernel 填充 metadata, clone sk_buffer 并且交给上层网络处理层&lt;/li&gt;
&lt;li&gt;IP layer 做校验, netfilter hook 处理&lt;/li&gt;
&lt;li&gt;如果 netfilter 未 drop 此包,交给更上层网络层处理&lt;/li&gt;
&lt;li&gt;&amp;hellip;.&lt;/li&gt;
&lt;li&gt;最终数据被 copy 到 userspace (recv,read,poll等网络调用获取)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;其中, XDP hook 在数据到达 NIC RX queue 之后即触发(上面第二步之后).而 iptables 等的处理(上面第八步之后)非常靠后, 需要分配大量资源来处理网络包.&lt;/p&gt;</description></item><item><title>Linkerd系列1 - Linkerd2-proxy</title><link>https://hangyan.github.io/post/2020-11-17-linkerd2-proxy/</link><pubDate>Tue, 17 Nov 2020 18:48:42 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2020-11-17-linkerd2-proxy/</guid><description>&lt;p&gt;ServiceMesh这块属于 k8s 的一个大热门，但据我所知，落地的并不是太多。看文章大概理解为 overhead 以及部署都比较重，对一般的企业来讲，要考虑的东西比较多，所以落地难度比较大。有的建议是说，先上 envoy，其他的先搁置，这也是个思路。envoy 轻量级一些，可以先作为一个 proxy 去体验一下 ServiceMesh 架构下的 proxy 设计。然后再逐步看看怎么引入 ServiceMesh.&lt;/p&gt;
&lt;p&gt;另一个方面，Istio vs Linkerd 应该还没完全分出胜负，对于企业的选型来说，仍然是个难点。好处是，我们可以去对比二者在各个功能点上的设计思路差异，然后加深理解。在 ServiceMesh 的组件中，毫无疑问 proxy 是核心组件之一。linkderd 的 linkderd2-proxy 也是一个经过深思熟虑并且设计良好的组件，可以作为切入点研究下 linkderd 的整体设计。&lt;/p&gt;
&lt;h2 id="语言选型"&gt;语言选型&lt;/h2&gt;
&lt;p&gt;envoy 是 C++ 写的，而 linkderd2-proxy 是 Rust. 语言的选型不仅对于实现者来说很重要，对于用户来说也是。想一下 marathon 用的 scala 以及它的失败就知道了。为什么选 Rust, 这跟它最开始的目标有关&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;资源占用低。因为 ServiceMesh 场景下的 proxy, 经常是以 sidecar 方式挂载在业务组件的 Pod 里，所以一定不能有太多的资源占用。&lt;/li&gt;
&lt;li&gt;延迟低。因为要转发所有的请求，不能有太多的 overhead.&lt;/li&gt;
&lt;li&gt;安全。同上，因为要转发所有的请求，如果 proxy 本身有安全漏洞，那就影响太大了。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;因为 linkderd2-proxy 主要的设计思路是简单，只定义为 ServiceMesh 场景，那么上面几个条件几乎都是硬性的，符合上面几个条件的语言基本上就只有 Rust了。因为上面几个条件意味着:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不能有GC. GC会带来 worst case tail latency。Go/Java不符合&lt;/li&gt;
&lt;li&gt;不能太重了。JVM再经过调优也不符合。linkderd2-proxy第一版就是用 Scala 写的，被抛弃了。&lt;/li&gt;
&lt;li&gt;有内存安全保障。C/C++不符合。历史经验表明，很多组件一大半的安全漏洞都是跟内存安全有关的。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="自动协议检测及-mtls"&gt;自动协议检测及 mTLS&lt;/h2&gt;
&lt;p&gt;这点跟 envoy 的差别也比较大。envoy 的配置文件上来一看还是挺吓人的，虽然可维护性更好。而linkderd2-proxy的设计思路是尽可能的少配置，基本上能做到插入即用，不需要额外配置什么。&lt;/p&gt;</description></item><item><title>etcd 的 clock diff 问题</title><link>https://hangyan.github.io/post/2020-11-17-etcd-time-diff/</link><pubDate>Tue, 17 Nov 2020 15:19:38 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2020-11-17-etcd-time-diff/</guid><description>&lt;p&gt;最近碰到一个客户问题，最初始的现象是一个resource的处理逻辑一直不生效，看 k8s apiserver 的日志如下:&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://hangyan.github.io/images/k8s/etcd/api-log-1.jpg"&gt;&lt;/p&gt;
&lt;p&gt;这里面没有 call webhook 相关的日志。主要的错误有两类，一是超时，暂时不清楚为啥。另一类是 object conflict, 这个比较奇怪。因为一般 obejct conflict 不会这么普遍，而且一般 controller 都会对此种情况做特殊处理，会自动恢复。从这个日志中能看出来的问题不多，继续排查一下业务相关的 contrller看看线索:&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://hangyan.github.io/images/k8s/etcd/olm-log-1.jpg"&gt;&lt;/p&gt;
&lt;p&gt;也是大量的 object conflict,非常异常的场景。而且还有一个 &lt;code&gt;invalid object&lt;/code&gt;。到这只能怀疑是不是 etcd 出问题了，因为正常的 controller/apiserver 逻辑不太会触发这么多的此类错误。看 etcd 的日志:&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://hangyan.github.io/images/k8s/etcd/etcd-log-1.jpg"&gt;&lt;/p&gt;
&lt;p&gt;大量的 &lt;code&gt;clock diff&lt;/code&gt; 日志，日志意思应该是有一台节点的时间不同步。盲猜可能会导致数据存储的异常，修复时间问题后重启了 apiserver以及相应的 contorller 之后问题修复。看官方提到的 issue 里: &lt;a href="https://github.com/etcd-io/etcd/issues/9768"&gt;Is there any other side effects when the system clock differs on each node&lt;/a&gt;, 只说了会影响设置有 TTL 的 object，目前看起来影响远远大于这个。&lt;/p&gt;
&lt;p&gt;这个问题的警示是，ETCD的作用太核心了，基于 Kubernetes 的容器平台在监控层面应该尽可能收集多的 etcd 的 metrics 信息，以供管理员决策和提供告警。如果日志不好收集，apiserver 的错误码应该也能提供不少信息。&lt;/p&gt;
&lt;p&gt;另一方面，NTP等服务应该作为默认部署的组件，以确保 master 节点在进行调整，维护，扩容或者新增 master 节点的时候，保证时间同步。而不是一次性地校准一次就不管了。&lt;/p&gt;</description></item><item><title>笔记: Postgresql里的事务实现</title><link>https://hangyan.github.io/post/2020-10-27-postgres-tran/</link><pubDate>Tue, 27 Oct 2020 15:33:02 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2020-10-27-postgres-tran/</guid><description>&lt;p&gt;虽然日常工作需要涉及到数据库的底层部分并不多，但Postgresql作为一个数据库实现的范本是很值得研究的。可以通过它的实现来探索很多通用的数据库设计以及系统设计的理念。本文主要关注于事物设计方面。&lt;/p&gt;
&lt;h2 id="mvcc"&gt;MVCC&lt;/h2&gt;
&lt;p&gt;pg底层使用&lt;code&gt;MVCC&lt;/code&gt;，修改数据时会直接创建新版本，而不是直接修改旧数据。这部分有一点需要注意的是在pg中，所有的语句都是在事物中执行的，不管是不是明确地用了&lt;code&gt;BEGIN/COMMIT&lt;/code&gt;。&lt;/p&gt;
&lt;h2 id="transactions-tuples-and-snapshots"&gt;Transactions, tuples, and snapshots&lt;/h2&gt;
&lt;p&gt;先看一下 Transaction 的主要数据结构:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;typedef&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;PGXACT&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;TransactionId&lt;/span&gt; &lt;span class="n"&gt;xid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="cm"&gt;/* id of top-level transaction currently being
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; * executed by this proc, if running and XID
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; * is assigned; else InvalidTransactionId */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;TransactionId&lt;/span&gt; &lt;span class="n"&gt;xmin&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="cm"&gt;/* minimal running XID as it was when we were
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; * starting our xact, excluding LAZY VACUUM:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; * vacuum must not remove tuples deleted by
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; * xid &amp;gt;= xmin ! */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;PGXACT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;事物以&lt;code&gt;xid&lt;/code&gt;作为标识.pg针对它做了很多优化，仅在真正开始写数据的时候才分配xid,如果是只读的事物就完全不分配。
&lt;code&gt;xmin&lt;/code&gt;表示当这个事物开始的时候仍然处在运行中的事物列表中最小的那个&lt;code&gt;xid&lt;/code&gt;&lt;/p&gt;</description></item><item><title>关于 OAM 与云原生应用</title><link>https://hangyan.github.io/post/2020-10-22-oam/</link><pubDate>Thu, 22 Oct 2020 11:34:13 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2020-10-22-oam/</guid><description>&lt;p&gt;OAM 是指阿里开源的一个应用模型，主要是云原生场景。年初的时候出来到现在，断断续续地了解过一些，之前一直没有特别想清楚，其实现在也是，但多多少少有一点点感悟，在这里记录一下.&lt;/p&gt;
&lt;h2 id="云原生应用的现状"&gt;云原生应用的现状&lt;/h2&gt;
&lt;p&gt;这里主要就是说围绕 kubernetes 生态的应用现状，基本上就是一团糟。没有标准，没有好的开源产品，没有社区来推动这个事情。先看看目前有啥:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Helm: 适合作为rpm/apt类似的东西，不适合直接给 end user 用&lt;/li&gt;
&lt;li&gt;Application CRD: 社区不活跃，结构定义也不好&lt;/li&gt;
&lt;li&gt;Operator: 过于复杂，目前仅有的统一规范是 ocp 弄的 operator framework. 尚未推广开来&lt;/li&gt;
&lt;li&gt;其他林林总总的各种 ad-hoc 方案&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这就导致云厂商要么是完全自己做一套新的应用模型，要么是用上面这一类现成的，但功能受限，扩展不易。且用户需要有一定的前置知识才能用好。&lt;/p&gt;
&lt;h2 id="oam-的主要优点"&gt;OAM 的主要优点&lt;/h2&gt;
&lt;p&gt;目前看来其最主要的优点是角色分离。Kubernetes YAML 的一个主要问题就是，它是一个大而全的 cofig 方案，不宜手写，只适合运维方向。如果想要做成用户友好的模型，OAM提供了一种可能性: 将大的 config 按角色分拆开，开发人员写一部分，运维人员写一部分，各自只关心各自的部分，最终再合并成一个整体的 config. 细节问题比如具体分成几个角色，谁来合并，可以再迭代改进。但这个分离的设计可以认为是走在一个正确的方向上。&lt;/p&gt;
&lt;p&gt;其他的几个重要的方面有：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;跨平台性。从设计上来讲，一开始设计成跨平台的思路当然是对的。但实际上可能还是90%以上的人主要是用在 k8s 上，所以 spec 设计时比较贴近于 k8s 的设计。其他平台的实现估计会花不少时间，这也是导致目前没有其他平台实现的主要原因&lt;/li&gt;
&lt;li&gt;插件系统。OAM 提供的 &lt;code&gt;traits&lt;/code&gt; 能力，可以理解为对应用的一个插件系统。之前国内有厂商做过类似的东西，是一个非常好的尝试。以插件形式给应用附加能力，是一种非常自然和方便的用户体验，&lt;code&gt;traits&lt;/code&gt;可以用来提供类似的能力。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="用户体验"&gt;用户体验&lt;/h2&gt;
&lt;p&gt;即使做了职责分离，OAM 看起来仍然是非常难以直接 edit 的东西。这也是之前我一直比较纠结的一个点。因为它更像一个面向开发者的 spec, 而不是一个对 end user 的产品。我们如何让用户方便地使用这个功能呢，有下面几种可能:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;纯 UI 封装。这里需要 UI 设计的比较好，能够隐藏比较多的细节，不要暴露太多的底层名词给用户，不然用户就要去理解 OAM 是个什么东西了。而我们理想的形式就是让用户不去学习新概念。一个可能的点就是我上面说的，把 &lt;code&gt;traits&lt;/code&gt; 叫做 &lt;code&gt;插件&lt;/code&gt;等等。这里需要考虑的一个点是API怎么设计，因为一般UI/API都是必须的产品。如果直接把 OAM 用 API 暴露出来也不怎么友好，这就涉及到下面的第二点&lt;/li&gt;
&lt;li&gt;我们能否再设计一层 spec, 简单一点的，能提供便捷的API,UI也方便？ 这个诱惑力比较大，但其实又回到了远点，因为又回到了老的大的 config 上? 假设我们想做的更简单一些，那势必又无法全面覆盖 OAM 的能力。似乎唯一的可能性就是，我们换了一种 spec 格式，让用户更容易理解，但可以和 OAM 一一对应。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;因为之前有厂商放出了一些 OAM UI 的截图，比如:&lt;/p&gt;</description></item><item><title>ARM指针的一些安全功能</title><link>https://hangyan.github.io/post/arm-pointer/</link><pubDate>Mon, 19 Oct 2020 13:12:59 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/arm-pointer/</guid><description>&lt;p&gt;伴随着 ARM 平台的越来越普及，Linux 内核对其的支持也逐渐丰富。而且 ARM 的设计也有很多独特的优点，本文将介绍在 ARM 平台关于指针操作的一些安全方面的设计。&lt;/p&gt;
&lt;h2 id="pointer-authentication"&gt;Pointer Authentication&lt;/h2&gt;
&lt;p&gt;很多攻击都是通过骇客设计的不安全指针进行的。比如 &lt;code&gt;buffer-overflow&lt;/code&gt; 和 &lt;code&gt;return-oriented programming&lt;/code&gt;, 都是通过在返回地址放置一个指针来进行的。内核已经针对这个问题做了很多改善，ARM 提供了 &lt;code&gt;pointer authentication&lt;/code&gt; 来检测和拒绝这种插入的非法指针。&lt;/p&gt;
&lt;p&gt;简单来说，就是在指针上附加一个签名，要想使用一个指针，先得签名验证通过。如果是外部的攻击者，它没法伪造这种签名，那么自己就不能制造指针来进行攻击了。&lt;/p&gt;
&lt;p&gt;目前一般指针都是64位，但一般都不是所有bit都是用了。ARM64的系统，如果是三级页表，那么只有低位的40bit用来存放地址，那么其他的bit就可以用来干其他事情。 签名一般是通过以下几个参数确定:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;pointer 本身&lt;/li&gt;
&lt;li&gt;process context 里的一个 secret key, kernel生成 这个肯定是攻击者无法获取到的&lt;/li&gt;
&lt;li&gt;current stack pointer。防止签名过的pointer泄露被复用&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;使用的话，需要先重新计算一下签名，确保二者对的上，然后把签名部署的数据清理掉，这时候就是一个正常的指针了。不然的话，就认为其非法。&lt;/p&gt;
&lt;h2 id="memory-tagging"&gt;Memory Tagging&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Memory Tagging&lt;/code&gt;的做法与&lt;code&gt;Pointer Authentication&lt;/code&gt;类似，都是利用指针里未利用的 bits 去存储一些信息。&lt;code&gt;Memory Tagging&lt;/code&gt; 是利用 4bit 的信息存储了一个 key, 同时在 pointer 指向的内存地址里也存储了同样的 key. 当对指针进行解引用操作时，需要对比 pointer 里的 key 与实际内存地址里的 key 是否一致。如果不一致将会报错。这个功能有以下用处：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;被释放的内存可以修改其 key, 防止被二次引用&lt;/li&gt;
&lt;li&gt;每个 stack frame 分配一个 key, 越界访问也会报错&lt;/li&gt;
&lt;li&gt;野指针也能检测到&lt;/li&gt;
&lt;li&gt;&amp;hellip;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这些 keys 可以用应用层来管理，也可以用 CPU 来随机生成。当用应用层来管理的时候，可以提前检测出内存方便的 bug.&lt;/p&gt;</description></item><item><title>Semantic Web</title><link>https://hangyan.github.io/post/semantic-web/</link><pubDate>Sat, 17 Oct 2020 11:56:20 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/semantic-web/</guid><description>&lt;p&gt;最近开始关注&lt;code&gt;Semantic Web&lt;/code&gt;的原因是&lt;code&gt;Abstract Wikipedia&lt;/code&gt;项目的发展．作为一个很老的互联网课题，大家的愿景都是很清楚的：想要一个语义更加清晰，更方便机器处理的互联网，然后构建一个真正的&lt;code&gt;互联网&lt;/code&gt;．但是市场的行为总是与技术理想有很大偏差，想要用后期的技术来补救，其成功的概率是很低的．&lt;code&gt;Abstract Wikipedia&lt;/code&gt; 的让人期待的地方在于，它成功的可能性更高，带来的改善也可能不止局限于&lt;code&gt;Wikipedia&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;先说什么是 &lt;code&gt;Abstract Wikipedia&lt;/code&gt;, 首先目前维基百科的内容结构大家基本上也都了解:不同语言分别维护，体量差异巨大．从一个程序设计的结构来讲，这种模式是很糟糕的，因为它抽象的程度太低，大量的内容无法复用，造成的 interface 对很多用户不友好: 不同语言内容差别大，同步不及时，维护工作繁重等等．之前的低地苏格兰语被一个美国小哥&lt;code&gt;勤奋&lt;/code&gt;地填充了大量的错误内容就是一例．&lt;/p&gt;
&lt;p&gt;如何解决? 按程序设计的思路来看，需要分清&lt;code&gt;数据&lt;/code&gt;和&lt;code&gt;算法&lt;/code&gt;两部分，数据是什么，是语言无关的抽象知识，用抽象符号来表述知识．算法是什么？怎么来组织这些数据，让他们构成合理的语句．最后可能再需要一个&lt;code&gt;Render&lt;/code&gt;层，将结果最终呈现给用户．这基本上就是一个&lt;code&gt;MVC&lt;/code&gt;结构了．&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Abstract Wikipedia&lt;/code&gt;就是想做这个事情.数据部分，由&lt;code&gt;wikidata&lt;/code&gt;维护，大致如下所示的模式:
&lt;img loading="lazy" src="https://en.wikipedia.org/wiki/File:Datamodel_in_Wikidata.svg"&gt;
可以打开这个&lt;a href="https://www.wikidata.org/wiki/Q5684409"&gt;Hawaii State Public Library System&lt;/a&gt;的链接看下实际的数据是什么样子的．这部分的内容跟&lt;code&gt;RDF&lt;/code&gt;的结构很像,可以预见的是，以后&lt;code&gt;wikidata&lt;/code&gt;的内容越来越多，也能促进&lt;code&gt;Semantic Web&lt;/code&gt;本身的发展．&lt;/p&gt;
&lt;p&gt;算法部分，由&lt;code&gt;wikilambda&lt;/code&gt;维护，这部分暂时还没有现成的实现，但是已经有初步的概要设计．一个完整的示例如下:
假设原始语句为:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;San Francisco is the cultural, commercial, and financial center of Northern California. It is the fourth-most populous
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;city in California, after Los Angeles, San Diego and San Jose
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我们想把这个语句专为通用的结构化内容,那么数据部分如下:
&lt;img loading="lazy" src="https://hangyan.github.io/images/posts/semantic-web/data.png"&gt;
(这部分数据完全可以用 RDF 表示出来)
注意上面的 &lt;code&gt;Q62&lt;/code&gt;等，就代表着它们目前在&lt;code&gt;wikidata&lt;/code&gt;中的链接.有了这些数据，那么我们可以通过一个简单的函数把它们拼接成一句英语:
&lt;img loading="lazy" src="https://hangyan.github.io/images/posts/semantic-web/func.png"&gt;
这是个比较简单的示例，真实的情况肯定会比目前复杂一些．但是理论上都可以通过更丰富的数据和算法来组合出所有目前常用的句式．&lt;/p&gt;
&lt;p&gt;对维基百科来说，这是一条看起来很有希望的路．期待的场景是人类的大部分的知识，都能通过这样的方式，以不同的语言展示给读者．当然，也不会局限于此．稍微展望一下，我们就可以发现它带来的变革几乎是无限的．我们能用它来做各种各样的事:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;知识图谱．有了维基百科的数据，我们可以更方便地构建各种知识图谱&lt;/li&gt;
&lt;li&gt;&lt;code&gt;wikidata&lt;/code&gt;和&lt;code&gt;wikilambda&lt;/code&gt;的内容可以以各种方式直接给其他系统集成，提供各种编程语言的 binding.&lt;/li&gt;
&lt;li&gt;机器学习系统以及学术界可以利用维基百科的内容做各种数据研究&lt;/li&gt;
&lt;li&gt;&amp;hellip;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;期望这个美好的愿景能实现吧．&lt;/p&gt;</description></item><item><title>Kubernetes Webhook Cert 方案</title><link>https://hangyan.github.io/post/k8s-webhook-cert/</link><pubDate>Fri, 16 Oct 2020 16:53:17 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/k8s-webhook-cert/</guid><description>&lt;p&gt;Kubernetes 里的 Cert 数据是挺零散的一块，而 Webhook 中又强制要求必须是 HTTPS, 在实际使用的过程中尝试了几种方案，发现各有优劣，也都有不少问题．下面将分别详述一下:&lt;/p&gt;
&lt;h2 id="cert-manager"&gt;Cert Manager&lt;/h2&gt;
&lt;p&gt;Cert Manager 可能是最成熟的方案了，使用 Let&amp;rsquo;s Encrypt 来签发证书．只需要配置好响应的 CR (Issuer, Cert&amp;hellip;等), Cert Manager会自动生成证书，比较方便．&lt;/p&gt;
&lt;p&gt;缺点也很多，首先是引入了一个比较重的依赖，Cert Manager本身的稳定性也是一个问题．实际用的时候经常发现它的 apiservice Not Ready等问题．假设本来环境中已经有 Cert Manager,那么直接用它是一个比较好的选项，否则并不建议使用．&lt;/p&gt;
&lt;h2 id="脚本--csr-resource"&gt;脚本 + CSR Resource&lt;/h2&gt;
&lt;p&gt;这种方式是利用 Kubernetes 内置的 CSR 资源来管理证书，最终 caBundle 就可以直接用　Kubernetes 集群自带的 cert 数据，但 Server CRT 和 Server KEY 需要自己用脚本生成．Github 上有现成的项目演示了这种用法: &lt;a href="https://github.com/newrelic/k8s-webhook-cert-manager"&gt;k8s-webhook-cert-manager&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;其基本使用方式如下:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./generate_certificate.sh --service &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;WEBHOOK_SERVICE_NAME&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; --webhook
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;WEBHOOK_NAME&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; --secret &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SECRET_NAME&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; --namespace &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;WEBHOOK_NAMESPACE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我们需要提供&lt;code&gt;SVC&lt;/code&gt;的名字和&lt;code&gt;NS&lt;/code&gt;以生成&lt;code&gt;cert&lt;/code&gt;数据．脚本也可以根據自己需求進行適當裁剪.当数据生成之后，使用的 CSR 资源我们还可以通过集群看到(一段时间后会被GC):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;[&lt;/span&gt;core@bastion ~&lt;span class="o"&gt;]&lt;/span&gt;$ kubectl get csr
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;NAME AGE REQUESTOR CONDITION
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;csr-w6ldh 5m56s system:node:master3 Approved,Issued
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;需要注意的细节如下:&lt;/p&gt;</description></item><item><title>VSCode 和 Emacs</title><link>https://hangyan.github.io/post/vscode-and-emacs/</link><pubDate>Sun, 27 Sep 2020 12:28:55 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/vscode-and-emacs/</guid><description>&lt;p&gt;除了 Emacs 和 Vim 之外的 Editor 战争，现在看起来 VSCode 已经赢了。&lt;/p&gt;
&lt;p&gt;先不说现存的一些 Editor 的现状和对比，我们直接猜想一下一个比较完美的 editor 应该是什么样子的&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;modern look. 就是好看，让人想用.&lt;/li&gt;
&lt;li&gt;易上手: 不需要学太多才能才能用好&lt;/li&gt;
&lt;li&gt;易扩展: 就是 plugin 多到满足常用需求&lt;/li&gt;
&lt;li&gt;跨平台，方便配置&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这几点就差不多了，能做到这几点基本上就能做到广受欢迎了。下面一个一个细拆开讲讲.&lt;/p&gt;
&lt;h1 id="决定特性"&gt;决定特性&lt;/h1&gt;
&lt;h2 id="modern-look"&gt;Modern Look&lt;/h2&gt;
&lt;p&gt;这点最成功的案例就是 JetBrains 的一系列产品，单是其 &lt;code&gt;Darcula theme&lt;/code&gt; 就让多少人愿意试一试。Atom 和 VSCode, LightTable， Sublime 做的也都不错， Vim 和 Emacs 做的不行，太古老了。虽然可以配置好的好看，但默认配置太劝退了。&lt;/p&gt;
&lt;h2 id="易上手"&gt;易上手&lt;/h2&gt;
&lt;p&gt;这点 vim 和 Emacs 还是做的最差，虽然最终效率最高，但学习曲线太漫长了。VSCode 和 Atom 比较像，做的都不错。&lt;/p&gt;
&lt;h2 id="易扩展"&gt;易扩展&lt;/h2&gt;
&lt;p&gt;但从扩展性上来讲，Atom/VSCode/Vim/Emacs都差不多，该有的插件都有，安装也不难。不过前两个明显使用起来更方便些，是真正的&lt;code&gt;易&lt;/code&gt;扩展,直接在 UI上就可以点点安装 plugin。也有方便的介绍和使用说明。LightTable 插件太少，半死不活的。Sublime 插件也不算多。&lt;/p&gt;
&lt;h2 id="跨平台"&gt;跨平台&lt;/h2&gt;
&lt;p&gt;跨平台的能力不难做，前面提到的几个都能做到。难的是尽量保证一致性的体验和性能。这里面 VSCode 做的最好， Atom 是在哪个平台性能都不咋样.Emacs在Linux下性能最好，Mac下 UI 效果一般, Windows 下配置着更麻烦。Vim 主要是 Terminal 领域了。&lt;/p&gt;</description></item><item><title>eBPF系列1 - 介绍</title><link>https://hangyan.github.io/post/ebpf-1/</link><pubDate>Tue, 22 Sep 2020 22:39:41 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/ebpf-1/</guid><description>&lt;p&gt;一直想写(整理)一些关于 eBPF 的文章。在我眼中，它是 &lt;code&gt;未来技术&lt;/code&gt; 之一。 最近零零碎碎看了一些，感觉信息太多，怕是永远也看不完，索性开始一遍看一边记笔记吧。这一篇是介绍。&lt;/p&gt;
&lt;h2 id="ebpf-是什么"&gt;eBPF 是什么&lt;/h2&gt;
&lt;p&gt;先介绍下 eBPF 的前身: BPF -&amp;gt; &lt;code&gt;Berkeley Packet Filter&lt;/code&gt;. 看名字就知道，主要是用于网络 packet 的过滤用的。 过滤程序是运行于基于寄存器的虚拟机之上．　BPF 展示了在内核中运行用户程序的良好开端．但它的缺点也很明显，虚拟机的设计以及指令集都比较落后，跟不上现代处理器的发展(尤其是多核方面)，也无法利用现在的 64bit 寄存器．&lt;/p&gt;
&lt;p&gt;eBPF extend 了 BPF, 将其机制大大地泛化，不止用于网络数据包的处理，而是可以 hook 在任意的系统调用上, 这样它就变成了一个几乎具有无限功能的系统。 在硬件上，它跟现代的处理器的指令集更为贴近,并且能充分利用现在数量众多的 64bit 寄存器．这样就使 JIT 编译器的产生变为了可能，大大提升了性能.&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://ebpf.io/static/syscall_hook-b4f7d64d4d04806a1de60126926d5f3a.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://ebpf.io/static/hook_overview-99c69bbff092c35b9c83f00a80fed240.png"&gt;&lt;/p&gt;
&lt;p&gt;在没有　eBPF 只之前，如果我们想对内核行为进行修改，基本上就是往内核里添加代码或者通过 kernel modules 进行．这二者的门槛都不低，所以影响有限．eBPF 提供了一种新的可能性，通过普通的编程方式，去控制内核的行为，同时保证了高性能．这就是它之所以独特和具有极大潜力的主要原因．&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://img2020.cnblogs.com/blog/1334952/202008/1334952-20200806131434176-1093013946.png"&gt;&lt;/p&gt;
&lt;h2 id="程序校验"&gt;程序校验&lt;/h2&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://ebpf.io/static/loader-7eec5ccd8f6fbaf055256da4910acd5a.png"&gt;&lt;/p&gt;
&lt;p&gt;当 eBPF 程序被加载进内核之后，要先做一些程序校验工作, 比如:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;权限是否具备。 加载 eBPF 的程序是否有权限加载.一般来讲，程序必须得是 root 用户运行的或者有 &lt;code&gt;CAP_BPF&lt;/code&gt; 权限才能加载。 如果开启了 &lt;code&gt;unprivileged eBPF&lt;/code&gt;, 那么普通程序也可以加载一些功能受限的 eBPF 程序(比如不允许指针操作)。&lt;/li&gt;
&lt;li&gt;eBPF 程序是否有可能 crash&lt;/li&gt;
&lt;li&gt;在每次指令执行前后都需要校验虚拟机的状态，保证寄存器和栈的状态都是有效的. 校验器不会检查程序的每条路径，它能够知道程序的当前状态是否是已经检查过的程序的子集。由于前面的所有路径都必须是有效的(否则程序会加载失败)，当前的路径也必须是有效的，因此允许验证器“修剪”当前分支并跳过其模拟阶段。&lt;/li&gt;
&lt;li&gt;不能访问未初始化的变量或者越界访问&lt;/li&gt;
&lt;li&gt;程序大小有 limit.&lt;/li&gt;
&lt;li&gt;复杂程度有 limit. 检查程序会估算所有的执行路径以评估其复杂程度。&lt;/li&gt;
&lt;li&gt;eBPF 是否会无法终止运行(比如 loop forever)&lt;/li&gt;
&lt;li&gt;不同类型的 eBPF 程序只能访问特定类型的系统调用&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;之所以有这么多检查，就是因为它能做的事情太多了，也太有可能造成破坏了，所以在校验阶段就尽量多做一些。&lt;/p&gt;</description></item><item><title>Hugo 的优点</title><link>https://hangyan.github.io/post/fuck-hexo/</link><pubDate>Sat, 12 Sep 2020 12:30:28 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/fuck-hexo/</guid><description>&lt;p&gt;经历了 N 次在各个不同的环境里初始化 Hexo 的环境失败之后，我终于换了 Hugo.&lt;/p&gt;
&lt;p&gt;从 Jekyll -&amp;gt; Hexo -&amp;gt; Hugo 我算是发现了，依赖不好处理的平台根本不适合做 static blog generator.想在一个新的环境里部署好一个这样的软件，而且你又没有这个语言的比较多的经验的时候，大量的时间会被浪费在配置环境上。这时候，像 hugo 这样的 static binary 就是一个 killer feature, 我根本不用看它有什么优缺点，就直接切换过来了。&lt;/p&gt;
&lt;p&gt;事实上也是，从互联网的内容检索上来看，大部分的迁移路线基本上就是 Jekyll,Hexo -&amp;gt; Hugo, 基本上没有反着来的。 先不说 Golang 语言设计的怎么样，但说能如此方便地 build 出来一个 cross-platform 的 static binary, 简直是造福程序员。&lt;/p&gt;
&lt;p&gt;曾经，我以为 Hexo 的 theme 与 content 分离已经很优秀了。没想到它还是个垃圾。&lt;/p&gt;</description></item><item><title>统信 UOS 究竟咋样</title><link>https://hangyan.github.io/post/2020-09-10-uos/</link><pubDate>Thu, 10 Sep 2020 16:01:54 +0000</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2020-09-10-uos/</guid><description>&lt;p&gt;统信 UOS 是今年国产化浪潮中比较出风头的一个做国产操作系统的公司。这几天换掉了我的 Manjaro 装上了 UOS 体验了一下。总体来说，可以打七八十分吧。&lt;/p&gt;
&lt;p&gt;单说这个分数可能不是很好度量它究竟是个什么水平。可以横向对比一下，分两个方面。一个是底座上，linux 发行版里, Deepin 团队做的很出色，现在基本上是排名第十左右的发行版(按流行程度)。UOS 是基于 Deepin 改的，底座上有了不错的保证。&lt;/p&gt;
&lt;p&gt;再跟国内这一众国产操作系统比的话，那简直是碾压。红旗之类的不用说，之前中国软件的麒麟，基于 Ubuntu 来改的( Deepin 的底座也是Ubuntu), 做了这么多年，了解 Linux 的基本上没啥好感，还不如 Ubuntu。因为国产操作系统向来上先往政企方面推，像麒麟这种大概率是拿到之后用户基本上就闲置不用了，继续用 Windows了。UOS 终于让人看到一点希望，至少，看起来让人有想尝试一直用用的念头了。&lt;/p&gt;
&lt;p&gt;国产操作系统怎么做的问题，其实就是 Linux 桌面版怎么做的问题，虽然我们也自有国情在此。从操作系统内核来讲， Linux 比 Windows/MAC 并不差，问题全出在软件生态上。主要原因有以下几个:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;非商业模式。只要商业公司才能做出像 Adobe / Oracle 这样的软件。有赚钱的目标，合理的分工及 KPI, 再卖出去软件形成一个正向循环。Linux 生态里有点像现在各家的 Andriod, 还更差，基本上连个统一付钱买软件的地方都没有，再说 Linux 用户普遍还没有养成这样的习惯&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;开发者太容易分散人力开发类似的项目了。大家对项目有了争议，可以立马基于当前版本重新立一个新项目，分开开发。这就导致现在 Linux 已经有了几百个发行版，N 个桌面系统，每一类软件都有 N 个不同的产品。除了个别的项目之外，大部分都不好用。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;没有领导，群龙无首。跟上面说的两个问题相关。内核发展的好，是因为有强有力的领导。但桌面系统，基本上就是百家争鸣，谁也不服谁&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这些问题杂糅起来，就形成了一个恶性循环。 Linux 桌面不好用 -&amp;gt; 用的人少 -&amp;gt; Linux 桌面发展缓慢，程序员各自魔改 -&amp;gt; 用的人更少。所以导致现在的局面就是，你想要一个方便使用的 Linux 桌面，如果没有一点编程背景，总会有一天栽在一个无法解决的最基本的硬件问题上。蓝牙坏了，网络连不上，显示器连不上，显卡配置不好等等在 MAC/WIN 上很少碰到的问题上。&lt;/p&gt;
&lt;p&gt;假设硬件的问题解决了，那么更难的来了: 软件。国内因为更加封闭的互联网生态这个问题更加严重。让用户苹果/Wechat 二选一，大部分人会放弃苹果，Linux 也类似，没有微信这些软件，没有正常用户会喜欢用 Linux。问题摆在这，那么国产操作系统怎么做的答案也就呼之欲出了:&lt;/p&gt;</description></item><item><title>Docker 私有 Registry 的镜像 GC 问题</title><link>https://hangyan.github.io/post/2020-09-07-docker-registry-gc/</link><pubDate>Mon, 07 Sep 2020 18:13:47 +0000</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2020-09-07-docker-registry-gc/</guid><description>&lt;h2 id="需求"&gt;需求&lt;/h2&gt;
&lt;p&gt;一个轻量级的 docker registry&lt;/p&gt;
&lt;h2 id="方案讨论"&gt;方案讨论&lt;/h2&gt;
&lt;p&gt;Harbor 的功能丰富，但是过于重量级了，与其他平台集成并不方便。Portus 类似。这一类产品都比较侧重 UI 和认证，一般都带有数据库， 与K8S 的集成也比较麻烦。最终看起来还是 registry 最合适，不过就是功能太简陋了。需要考虑的东西很多:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;HTTPS&lt;/li&gt;
&lt;li&gt;暴露方式 -&amp;gt; Ingress/NodePort. 最好的方式当然是 Ingress, 但虽然 registry 支持 SubPath, 但docker client 不支持。所以只能退而求其次用 NodePort. 对外地址的暴露只能设计 CR 来做了&lt;/li&gt;
&lt;li&gt;GC -&amp;gt; 这块看起来都受限于 registry 本身的能力。它提供了命令行工具能手工清除 blob, 但其他的 metadata 没删完。导致做完 GC 还得重启下 registry. 同时还得保持 GC 时 readonly.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这里面最繁琐的就是 GC, 目前提供的工具非常的原始。harbor 集成了 gc 的功能，应该是在 admin 页面提供了按钮来手动触发。那么在 K8S + registry 的限定范围内应该怎么做，目前想好的流程是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;在 registry 的 deployment 里加一个 container 来处理 GC， 这样的好处是可以共享配置，存储和 PID&lt;/li&gt;
&lt;li&gt;gc container 和 registry container 共用同样的镜像，但 CMD 不一样&lt;/li&gt;
&lt;li&gt;gc container 的 CMD 是一个 while 循环，用来周期性地跑 GC 命令&lt;/li&gt;
&lt;li&gt;跑完 GC 之后，如果确实清理了一些 blob, 还需要重启 registry. 不然因为其他一些 metadata 没有清理， 直接 push 和 pull 会出错。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这样就组成了一个低成本的可用的 GC 方案。缺点就是没法方便地将 registry 置为 readonly. 理论上来讲, gc 的周期设置长一点，问题不大.&lt;/p&gt;</description></item><item><title>BlueTrace与健康码</title><link>https://hangyan.github.io/post/2020-07-23-blue-trace/</link><pubDate>Thu, 23 Jul 2020 16:32:10 +0000</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2020-07-23-blue-trace/</guid><description>&lt;p&gt;疫情之下，各国都在不断地调整自己的防疫政策。中国的模式已经证明了其可能是最有效的方式之一，但因为种种原因大部分国家难以效仿和接受。从技术层面来讲，基于支付宝/微信等国民APP之上的健康码模式也是如此:有效，但是别国难以效仿。新加坡最开始使用&lt;code&gt;BlueTrace&lt;/code&gt;协议并开发出了相应App让国民下载使用,部分其他国家也开始效仿，甚至有望能成为一种国际通用的防疫标准。&lt;/p&gt;
&lt;p&gt;隐私保护是 BlueTrace 所考虑的最主要特点之一。其手段包括如下方面:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;尽可能少的收集用户信息，卫生部门唯一收集的信息是手机号&lt;/li&gt;
&lt;li&gt;本地存储用户接触记录，只有当被感染者愿意分享此数据时卫生部分才能获取&lt;/li&gt;
&lt;li&gt;第三方无法通过此协议一直追踪用户。因为相关标识ID会一直更换&lt;/li&gt;
&lt;li&gt;&amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以在对用户的标识方面，主要是通过算法生成的 UserID 和 TempID 来进行。当用户用手机号进行注册时,后台服务会生成一个 UserID, 而用户在互相接触需要通过蓝牙交换信息时，则通过 TempID 进行。TempID的 大概结构如下:&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://hangyan.github.io/images/posts/bluetrace/tempid.png"&gt;&lt;/p&gt;
&lt;p&gt;可以看到其有如下特点:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;加密。只有卫生部门才有密钥能解密数据&lt;/li&gt;
&lt;li&gt;过期时间。TempID有过期时间，一般在15m左右.这样可以尽量减少恶意攻击或者伪造假数据&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;TempID的生成需要联网(加密啥的)，所以为了考虑到用户不联网的情况，所以TempID可以一次性尽量生成多个，在联网的时候一次性发给用户以备用。&lt;/p&gt;
&lt;p&gt;当装有相应APP的用户相遇时，设备会通过 Bluetooth Low Energy (BLE)协议进行通信并交换信息，其数据流程如图所示:&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://hangyan.github.io/images/posts/bluetrace/ble-handshake.png"&gt;&lt;/p&gt;
&lt;p&gt;这里面两个 Device 分别有一个角色定位，一个叫 Central ，一个叫 Peripheral。Central 主动扫描 Peripheral 并读取其信息，然后返回一些额外信息。一个手机一般是按一定时间比率轮换在两个角色之间切换。其交换的数据大概如下所示:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;id&amp;#34;: &amp;#34;FmFISm9nq3PgpLdxxYpTx5tF3ML3Va1wqqgY9DGDz1utPbw+Iz8tqAdpbxR1 nSvr+ILXPG==&amp;#34;, // TempID
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;md&amp;#34;: &amp;#34;iPhone X&amp;#34;, // Device model
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;rc&amp;#34;: -60, // Signal strength
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;o&amp;#34;: &amp;#34;IJ_HAI&amp;#34;, // Health authority identifier
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;v&amp;#34;: 2 // Protocol version
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这里面需要注意的是:&lt;/p&gt;</description></item><item><title>给 Kubernetes Lens AppImage 添加 Desktop Entry</title><link>https://hangyan.github.io/post/2020-04-29-linux-lens-icon/</link><pubDate>Wed, 29 Apr 2020 22:13:02 +0000</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2020-04-29-linux-lens-icon/</guid><description>&lt;!-- toc --&gt;
&lt;p&gt;本文记录如何为 Kuberntes Lens 的 Linux 版本的 AppImage 手动创建一个 Desktop Entry 以及生成 Icon&lt;/p&gt;
&lt;h2 id="手动创建-desktop-entry"&gt;手动创建 Desktop Entry&lt;/h2&gt;
&lt;p&gt;创建一个 &lt;code&gt;desktop&lt;/code&gt; 文件，任意位置均可。假设名字为 &lt;code&gt;lens.desktop&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ini" data-lang="ini"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Desktop Entry]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Lens&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Comment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;A full-featured Kubernetes IDE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Exec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;/home/yayu/Soft/AppImage/Lens-3.3.1.AppImage&amp;#34; %U&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Terminal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Application&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Icon&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;kube-lens&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;StartupWMClass&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Lens&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;X-AppImage-Version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1.4.1.271&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Categories&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Network;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;X-AppImage-BuildId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1N3OgzauYTeCvM55WzKgL7MIQe0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;X-Desktop-File-Install-Version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;0.24&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;X-AppImage-Comment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Create By HangYan&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;TryExec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/home/yayu/Soft/AppImage/Lens-3.3.1.AppImage&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其中 &lt;code&gt;Exec&lt;/code&gt; 以及 &lt;code&gt;TryExec&lt;/code&gt; 是 AppImage 的位置，应相应更改&lt;/p&gt;
&lt;h2 id="生成-icon"&gt;生成 ICON&lt;/h2&gt;
&lt;p&gt;这里我下载了一个黑色的 Kubernetes ICON, 背景透明，webp 格式&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://hangyan.github.io/images/kube/kube.png"&gt;&lt;/p&gt;
&lt;p&gt;这里我已经转为 png 格式，转换命令为:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;dwebp &amp;lt;origin&amp;gt;.webp -o kube.png
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后，生成相应的 Icon, 并安装到系统中&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;xdg-icon-resource install --theme Moka --size &lt;span class="m"&gt;48&lt;/span&gt; kube.png kube-lens
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;安装的 icon 可以在 &lt;code&gt;~/.icons&lt;/code&gt; 目录中看到。&lt;/p&gt;</description></item><item><title>Linux 桌面的失败</title><link>https://hangyan.github.io/post/2020-04-04-linux-desktop/</link><pubDate>Sat, 04 Apr 2020 21:29:54 +0000</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2020-04-04-linux-desktop/</guid><description>&lt;p&gt;之所以说它失败，是指从功利的角度来看。不管是市场占有率，还是软件的可用性，用户友好性，以及生态上，都是失败的。直到2020的今天，以 Ubuntu 为代表的厂商在不断地向竞争对手学习，但仍然不足以和后者相提并论。当然，从自由以及民主的角度来看，它是有成功之处的，开源软件的流行，对于用户隐私以及自由的尊重，这一切的努力都值得我们敬重。&lt;/p&gt;
&lt;p&gt;从表面上看，如果我们细数其失败的现象，很容易列出以下几点:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;生态太差，没有多少厂商愿意开发 Linux 平台的软件。而进一步的原因则是用户基数少，以及开源社区天然对商业软件的不支持态度&lt;/li&gt;
&lt;li&gt;用户不友好。偏技术化，普通用户难以方便地使用。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;造成这种现状的原因很多，不一而足。其中人和制度自然是重点因素。&lt;/p&gt;
&lt;p&gt;从 Linux 桌面的相关开发人员来看，有相当一部分人是满足于现状，并且不愿意 Linux 桌面朝着 Mac OSX 和 Windows 的方面演进，因为它是 Linux, 是自由软件的核心，似乎不应该走那样的路。这一部分人满足于目前 Linux 的生态，并沉迷于从技术角度去使用和解读 Linux，同时和普通用户区分开来。另一部分人，在努力地丰富 Linux 应用的生态，但却走入了一个怪圈。到今天，我们有几百个发行版，几十个桌面系统，几十个文件管理器，Terminal应用，音乐播放器，数种不同的 GUI SDK 框架。他们共享了一个 Kernel, 使用起来功能也大同小异，一样的不如商业软件好用。但所有人都似乎相信自己所选择的框架/方向是对的，至少相对于其他选择是有一定优势的。假设这些精力能集中起来，会不会更好？&lt;/p&gt;
&lt;p&gt;没有假设，因为没有这个可能。与当世的民主制度一样，开源软件的开发流程大体上遵循了同样的模式，也遵循了同样的制度性缺陷。它防止了最大的恶(隐私，对自由的侵犯)，但却受制于其极低的效率。作为一个开发人员，你发现了一个应用的 BUG，或者有了一个新的 Idea 或者非常好的 Feature 想要合并进去，可能目前这个项目的 Owner 太慢了没时间处理，或者暂时不认可你的想法，那么你要么搁置你的 idea 或者 bugfix, 要么狠心下来自己 fork 一个新的版本。或者你直接掠过沟通这一步，直接开始自己的新项目。这就是 Linux 应用生态的开源项目的常态。&lt;/p&gt;
&lt;p&gt;这个怪圈有没有逃出的方法，有，而且很成功。只是，他的价值观显的不那么普世罢了。这样的例子很多。GNU/Linux 二者的创始人便是。可惜的是，后面的一票人都走偏了。这个方式现在有一个专业的词汇，叫终身仁慈独裁者(Benevolent Dictator For Life).&lt;/p&gt;
&lt;p&gt;我们很熟悉日常新闻上的 Linux 本人，经常骂这个骂那个，言辞粗暴，但其地位和声誉却未受影响。因为:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;他本身水平之高有目共睹，是 Linux Kernel 和 Git 的创始人.&lt;/li&gt;
&lt;li&gt;他领导下的 Linux Kernel 项目发展顺利，效率高，质量好&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;就这，就足够了。就是这个仁慈独裁者，能保证一个产品拥有稳定的内部结构，统一的 interface, 和高效率的产出节奏。而 Linux 应用层恰恰缺少这么一个人，来决定大方向和开发流程，造成了现在的局面。&lt;/p&gt;</description></item><item><title>Kubernetes in 2020: Let's Forget About Kubernetes</title><link>https://hangyan.github.io/post/2020-01-03-kubernetes-in-2020/</link><pubDate>Fri, 03 Jan 2020 16:07:42 +0000</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2020-01-03-kubernetes-in-2020/</guid><description>&lt;p&gt;犹记得 Docker 横空出世的时候，像明星一般，吸引了无数的人去研究它，使用它，推广他。技术的狂热让我们隐隐约约觉得它似乎解决了无数的业内问题，即使它实际上只是适用于单机环境的一个容器实现。没关系，我们有 Mesos / Marathon, 或者其他类似的东西来弥补它的不足, Docker 仍然是我们关注和研究的核心，其他的都是陪衬。&lt;/p&gt;
&lt;p&gt;Kubernetes 的出现改变了这一切，与 Docker 一样，从一开始它就拥有了极好的架构基础。在它的逐步成长中，Mesos/Marathon的先天不足逐渐被显露出来，并且逐渐被推至边缘地带，Docker 也渐渐地回到了它应有的位置上，成了软件行业中，无数层级抽象中的一环，在 Linux Kernel 之上，在 Kubernetes 之下，默默工作，只有在出问题时才会被人们想起。Kubernetes 成了新的核心。&lt;/p&gt;
&lt;p&gt;而过去一年，我们看着 Kubernets 也在逐渐成熟，性能更好，功能更完善，一切都在向着更稳定的方向迈进，也在紧随着 Docker 走过的路。我们永远需要一个 Platform, 这个 Platform 是谁则是在不断变化的。 因为 Platform 的稳定，所以它应该被遗忘，因为我们在思考更高层级的东西。在这整个链条中，我们知道了起点是二进制，终点是人的需求，中间的路，我们走到了 Kubernetes 这里。那么 Kubernetes 的上面是什么？这可能是我们在今后的一年或者很多年里探索的答案。&lt;/p&gt;
&lt;p&gt;过年的一年已经给了我们很多零碎的拼图: ServiceMesh, DevOps, GitOps, Application, Helm, Servrveless, Cloud Native&amp;hellip;. 这些名词如雨后春笋般冒了出来，让人眼花撩乱。它们并不能总是解决所有的问题，但却给我们指明了一定的方向。&lt;/p&gt;
&lt;p&gt;从代码自动到产品，这一个长久而又清晰的诉求，在 DevOps 提了这么多年的情况下，依然在被呼吁，说明我们仍未达成目标，因为它本身需要的前置条件太多, 我们需要良好的 CI/CD, 能够适配各种 VCS;我们需要简明清晰的语法，来描述出了流水线的 Steps;我们需要统一的 Artifact Storage, 用来存储诸如 Test Coverage Output, Docker Image, Binary&amp;hellip;等各种各样的文件;我们需要完善的 Unit Test, Integration Test, 来保证能尽量自动化地检测产品的质量，我们需要蓝绿发布，滚动更新，自动的故障恢复&amp;hellip;等等。即使这其中的每一步都有了优质的候选技术，将他们真正地串联起来成为真正的 GitOps, 才是更难的地方。因为成功的 GitOps 也需要达到像 Docker / Kubernetes 那样既是一个稳定的核心，也是一个扩展性良好的平台。而这正是开源社区的弱项，人们更容易被技术的偏好分心而不断 &lt;code&gt;fork&lt;/code&gt; 分裂，而不像公司那样能有一个稳定的目标和节奏。Docker 和 Kubernetes 的成功历史都证明，公司 + 开源的模式才是最有效的。期待在新的一年里在此方面能有大的突破。&lt;/p&gt;</description></item><item><title>Git 与 GitOps</title><link>https://hangyan.github.io/post/2019-09-12-git/</link><pubDate>Thu, 12 Sep 2019 13:02:41 +0000</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2019-09-12-git/</guid><description>&lt;p&gt;伴随着 GitOps 的盛行， Git 本身的影响力也在逐渐增大。从最开始只是为了管理 linux kernel, 到支撑起了 Github 这个最大的软件社区，Git 本身的应用范畴早已超出了 vcs 本身，我们还用它来存储配置文件，文档，制作电子书，维护 blog (由 Github 等平台展示)等等。在 GitOps 的范畴内，它更是成为了 &lt;code&gt; the single source of truth&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;但 Git 最初的设计目标只是针对于 linux source code， 大量的小文件，而伴随着 git 应用范围的增多，明个明显的问题便显现出来&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;大文件的管理：大文件通常都是难以 diff 的二进制文件，跟 git 本身的设计理念完全背道而驰&lt;/li&gt;
&lt;li&gt;大仓库的管理: 像 Google 的 Single Repo 以及微软的 windows 等巨型仓库，在使用 git 时都都会遇到非常多的性能问题。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;对于前者来讲，目前的主流的解决方案都是 &lt;code&gt;LFS&lt;/code&gt;, 主流的托管平台都支持。但在 GitOps 的大前提下，这个问题仍然算是未解决，因为在企业场景下又多了一个 Git 大文件管理的 &lt;code&gt;dashboard&lt;/code&gt;， 而 GitOps 得了理念是为了减少 dashboard, 将所有的一切都统一起来。 一个可能的选项是用&lt;a href="https://hangyan.github.io/post/2019-08-26-cnar/"&gt;Cloud Native Artifact Registries&lt;/a&gt;的理念，用&lt;a href="https://github.com/docker/distribution"&gt;distribution&lt;/a&gt; 作为 LFS Server。&lt;/p&gt;
&lt;p&gt;大仓库的管理微软的贡献比较显著些。像 Windows 这样的巨型项目，他们必须提前做很多改善才能把 Windows的代码放到一个仓库里。最终的实现原理跟 Google 类似: 与普通的 Git 仓库不同，他们使用一个 &lt;a href="https://vfsforgit.org/"&gt;virtual filesystem&lt;/a&gt; 来作为底层存储，当用户使用 git 时，只有那些被访问的文件才会被实时从 Server pull 下来。这种方式带来的一个附带好处是也解决了大文件的管理问题。&lt;/p&gt;</description></item><item><title>GitOps</title><link>https://hangyan.github.io/post/2019-09-11-gitops/</link><pubDate>Wed, 11 Sep 2019 16:44:26 +0000</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2019-09-11-gitops/</guid><description>&lt;p&gt;Kubernetes 确定了统治地位之后，Service Mesh 和 GitOps 也渐渐火热起来。Service Mesh 领域内 , Istio 已经逐渐占了上风，但 GitOps 扔未有统治性的工具出现，尚处于早期阶段。&lt;/p&gt;
&lt;p&gt;相较而言, Service Mesh 本身技术性更强一些，而 GitOps 涉及的面比较广。对企业来说，会有很多额外因素需要考虑，一方面要考虑旧有系统的兼容性，另一方面要考虑所采用工具的可占有性(是否可以自己部署，维护，开发？)。所以即使现在社区有功能比较完善的 GitOps 工具，也很难被企业所采用。&lt;/p&gt;
&lt;p&gt;但就理念而言， GitOps 本身的前景非常广阔，最直观的改善就是对于研发人员效率的提升以及产品 Deploy 效率的提升。这些优点是以如下特性/流程为基础的:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;让工具去处理杂事&lt;/li&gt;
&lt;li&gt;研发人员使用熟悉的 Git ，专注于产品研发&lt;/li&gt;
&lt;li&gt;使用声明式语法描述环境目标状态，类似于 kubernetes&lt;/li&gt;
&lt;li&gt;使用 Pull Request 进行 Review&lt;/li&gt;
&lt;li&gt;流水线集成 lint /test, 进行较为基本的自动化 check&lt;/li&gt;
&lt;li&gt;PR 通过即可直接上线&lt;/li&gt;
&lt;li&gt;通过线上故障来总结反馈并修正自动化 check&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这个流程的效率非常高，事实上很多企业会在改善自身研发流程的过程中多多少少都实行了类似的模式，但问题就在于很多使用了 ad-hoc 或者 无法产品化的手段。如何解决社区标准化的产品与企业应用场景之间的鸿沟，是 GitOps 亟待解决的问题之一。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://miro.medium.com/max/2020/1*QNwZhE9HAJAWSi__0fnMhw.png"&gt;&lt;/p&gt;</description></item><item><title>Raft 协议笔记</title><link>https://hangyan.github.io/post/2019-09-10-raft-notes/</link><pubDate>Tue, 10 Sep 2019 14:51:05 +0000</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2019-09-10-raft-notes/</guid><description>&lt;p&gt;关注于 Raft 可能更多是因为 kubernetes/etcd 的原因，二者似乎变成了 raft 最大的&lt;code&gt;客户&lt;/code&gt;，时不时地重新理解一下这个协议还是很有必要的。&lt;/p&gt;
&lt;p&gt;之所以这样讲，是因为分布式协议本身就很难理解。即使 raft 论文中一再声称它的主要目的之一便是容易理解，比 paxos 更好懂，但对于一般从业者来说，要想完整地理解它还是有困难的。所以经常需要温故而知新。并且，在网上一搜，关于 raft 协议的 blog 也是汗牛充栋，每个人都在试着理解，记录。可能后续还需要参考下别人的笔记，互相印证。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;论文地址: &lt;a href="https://raft.github.io/raft.pdf"&gt;In Search of an Understandable Consensus Algorithm&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="基本知识"&gt;基本知识&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;leader&lt;/code&gt; 的概念是核心之一。raft 作为一个分布式系统，各个 server 通过固定的算法选举出 leader , 然后 leader 负责状态的维护，处理 client 的请求，维护整个集群的状态等等。跟民主制度里面多党选举有点像， 一旦大多数投票通过，总统就拥有至高权力。&lt;/p&gt;
&lt;p&gt;除了 leader 之外，还有另外两个角色是 &lt;code&gt;follower&lt;/code&gt;以及&lt;code&gt;candidate&lt;/code&gt;。选举状态下，大家都是&lt;code&gt;candidate&lt;/code&gt;,正常工作情况下，就分成了&lt;code&gt;leader&lt;/code&gt;和&lt;code&gt;follower&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;Raft 并没有使用系统时钟来作为同步机制，因为太不可靠了，而且维护起来很麻烦。raft 使用了一个 &lt;code&gt;term&lt;/code&gt; 的概念来代表时序。&lt;/p&gt;
&lt;h2 id="leader-election"&gt;Leader election&lt;/h2&gt;
&lt;p&gt;一般的流程如下所示:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;server 启动时，成为 follower&lt;/li&gt;
&lt;li&gt;如果 follower 在 election timeout 的时间内，没有收到 leader 发来的 heartbeat， 那么它就假定没有 leader, 准备开始选举&lt;/li&gt;
&lt;li&gt;follower 将自己变为 candidate， 然后给自己投一票， 然后发信息给其他 server 问其他 server 是否可以给自己投票。结果要么是大多数投了它，它成为 leader, 或者另外一个 candidate 成为了 leader, 或者没有 leader (splite vote,没有 server 获取到大多数 vote)，那么就进行一下次投票。&lt;/li&gt;
&lt;li&gt;在 election 的过程中，有可能其他的 candidate 已经成为了 leader, 并给当前的 candidate 发来了同步数据的请求。这时候就可以通过 term 的比较来判定，如果发过来的 term 数不小于自己的，那么就承认发送者的 leader 地位，并且把自己切换回 follower 状态。否则的话拒绝请求并且继续选举。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这里面有一个问题，所有人都在给所有人发请求要求 vote 自己，怎么能达到有一个 candidate 获取到 大多数 vote 呢? raft 采用了 randomized election timeout 来解决这个问题。不同 server 的 lection timout 不一样，避免了大部分情况下的 splite vote 情况。如果发生 splite vote, 重来一次。这种机制跟 TCP 的拥塞控制有些类似。&lt;/p&gt;</description></item><item><title>Oberon操作系统</title><link>https://hangyan.github.io/post/2019-09-04-oberon/</link><pubDate>Wed, 04 Sep 2019 21:25:35 +0000</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2019-09-04-oberon/</guid><description>&lt;p&gt;文章链接: &lt;a href="http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.90.7173&amp;amp;rep=rep1&amp;amp;type=pdf"&gt;Oberon – The Overlooked Jewel&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;很多行业以及技术在早期都呈现出了一种百花争鸣的态势，然后到了成熟期一般都只是几家公司或者技术在互相竞争。操作系统也是如此，目前的主流 OS 里基本上就是 Mac/Linux/Windows 三家。但是在互联网早期，出现了很多设计里面差别很大的操作系统，从 GNU 的 Hurd，到 Lisp Machine，还有 Oberon 等。他们之所以消失，并不是说本身技术方面不占优势，而是从商业以及用户习惯上来讲，最终使用的人数太少而逐渐销声匿迹而已。而且从设计里面上来说，相当一部分的 OS 设计是因为理念太过超前而违背了当时用户的使用习惯而被舍弃。所以即使从今日来看，探究一下这些&lt;code&gt;老旧&lt;/code&gt;的操作系统的设计理念仍然是大有益处的。&lt;/p&gt;
&lt;p&gt;Oberon 的作者也是 Pascal 的作者。他自己对 Pascal 是很矛盾的感情。Pascal 本身只是他的一个作品之一，他本身的能力是非常强的，先后设计了很多完整的操作系统，编译器等等。但是外界对他的期待都在 Pascal 上。他也一直拖着不去更新，而是专注于自己感兴趣的领域。Pascal 渐渐式微，Oberon 也淡出大众视野。他沉浸于自己的创造乐趣之中，也给后世留下了无数珍宝。&lt;/p&gt;
&lt;p&gt;文章提到了几个 Oberon 操作系统的特点，让人都非常印象深刻。&lt;/p&gt;
&lt;h2 id="系统级-gc"&gt;系统级 GC&lt;/h2&gt;
&lt;p&gt;GC 在我们一贯看来都是属于高级语言的概念，操作系统很少有人会用 GC 来做资源管理。Oberon 实现了系统级别的 GC： 进程使用文件，不需要显式地自己去关闭文件句柄等。现在操作系统的应用程序因为系统没有 GC,不管是操作系统本身还是应用程序都需要许多额外的处理此类细节的代码逻辑。而且需要设置各种文件句柄的上限等等。Oberon 所采取的策略对未来的操作系统来说很是很值得借鉴的。&lt;/p&gt;
&lt;h2 id="更少的对话框"&gt;更少的对话框&lt;/h2&gt;
&lt;p&gt;对话框这个东西，自操作系统有了图形界面不久就一直存在。软件用它来给用户提供编辑，确认等操作。但是对话框并不是一个好的设计，因为它需要会打断用户的思路，强行让用户分散注意力去关注于一个全新的界面。现代的操作系统仍然有很多的这样的或无奈或多余的设计, 比如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Mac OSX 在关机的时候，因为平时开的软件比较多。经常就是需要一个一个地点各个软件弹出的确认退出的对话框。系统并没有一个默认的忽略选项，即使用户完全不在意各个软件是否需要保存当前状态。Mac OSX 为此做了一个 &lt;code&gt;Force Quit&lt;/code&gt;的功能，用户需要一个一个地强制退出可以忽略的软件&lt;/li&gt;
&lt;li&gt;文件的更名操作既可以弹出对话框，也可以直接按 &lt;code&gt;Enter&lt;/code&gt; 去修改。后者就比前者简单好用的多&lt;/li&gt;
&lt;li&gt;Mac 安装软件的一步拖动方式就比 Windwos 的在对话框中一直点下一步好很多&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Oberon 在设计之处就考虑到了这个问题，尽量在用户界面少使用对话框。比如它的文件&lt;code&gt;另存为&lt;/code&gt;操作，就可以直接点击文件名，输入一个新的，然后点击保存命令即可。这比现在常用的弹出一个对话框让用户输入再确认好的多。&lt;/p&gt;
&lt;h2 id="校园合作"&gt;校园合作&lt;/h2&gt;
&lt;p&gt;这是最让我震惊的一点。 Oberon 操作系统在作者所在的学校里，所有的人都在用这种操作系统。它发源于本校，主要的开发者都在本校工作或者退休了，使用同一种编程语言: Oberon，有足够用的应用程序。这样一种操作系统对学生来说， 在教学上拥有无与伦比的优势：详尽的文档，近在咫尺的开发者，你可以从头到尾了解一个操作系统的实现细节，学习它，研究它，改造它， 提升它的性能，给它开发应用程序。所有的这一切，都会被记录下来，作为一个学校的令人自豪的存在，被后来的追随者延续它的生命。&lt;/p&gt;</description></item><item><title>失败博物馆 - Hadoop</title><link>https://hangyan.github.io/post/2019-08-31-hadoop/</link><pubDate>Sat, 31 Aug 2019 13:35:37 +0000</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2019-08-31-hadoop/</guid><description>&lt;p&gt;Hadoop 严格来说并不算完全失败了，不像 Mesos 那样，而是人们期待它成为一个大象， 而它却变成了一只兔子。&lt;/p&gt;
&lt;p&gt;我刚毕业就开始接触Hadoop, 当时它基本上就是大数据的代名词。在这个光辉的表象之下，研发人原面临的是一个非常不透明，UI丑陋，运行缓慢的系统。当然，技术的狂热周期会让人放弃自己的疑虑，专心地去这座 &lt;code&gt;shit mountain&lt;/code&gt; 扒拉东西。另外，也没得选。&lt;/p&gt;
&lt;p&gt;同样的情况也发生在 Jenkins, Mesos 身上，当没有对比的时候，人们很难意识到自己面对的东西的优缺点是什么。时至今日，在吃尽了很多苦头之外，我们可以回过头来想想，为什么有的软件成功了？为什么有的失败了？&lt;/p&gt;
&lt;p&gt;成功的特性几乎是不言自明的，我们可以列举如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;模块化 / 可扩展性好&lt;/li&gt;
&lt;li&gt;接口用户友好 (页面，编程语言，API)&lt;/li&gt;
&lt;li&gt;文档全面&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这些就够了。而失败软件在每一方面，几乎都没做好。Hadoop 也是如此。&lt;/p&gt;
&lt;p&gt;同时因为这些因素存在，人们从最初的狂热清醒下来之后，便开始想尽一切办法找寻可能的替代品。在数据量不大的时候尽量选择其他工具(pandas, unix tools等等)， Spark, Hive, Pig&amp;hellip;. 甚至连 Hadoop 第二代的 YARN 都被 Kubernetes 无意打残了。当然，这个现状有一个比较好的词叫&lt;code&gt;生态圈&lt;/code&gt;，但它和 Kuberentes 的生态圈还是很不同的。前者是因为太难用，被肢解，被不断替代。后者是根系稳固，枝叶繁茂。（没写完，待补。）&lt;/p&gt;
&lt;h2 id="links"&gt;Links&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://architecht.io/what-happened-to-hadoop-211aa52a297"&gt;What happened to Hadoop&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.chrisstucchio.com/blog/2013/hadoop_hatred.html"&gt;Don&amp;rsquo;t use Hadoop - your data isn&amp;rsquo;t that big&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://adamdrake.com/command-line-tools-can-be-235x-faster-than-your-hadoop-cluster.html"&gt;Command-line Tools can be 235x Faster than your Hadoop Cluster&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/pachyderm-data/lets-build-a-modern-hadoop-4fc160f8d74f"&gt;Let’s build a modern Hadoop&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.zhihu.com/question/26568496"&gt;与 Hadoop 对比，如何看待 Spark 技术？&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://zhuanlan.zhihu.com/p/76829123"&gt;Hadoop再凉凉，前大数据独角兽公司MapR被惠普企业（HPE）收购&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://zhuanlan.zhihu.com/p/71139401"&gt;驳「Hadoop 快不行了」&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://zhuanlan.zhihu.com/p/70586829"&gt;Hadoop 不再权威，开源大数据的未来何去何从？&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://computerrepairkansascity.typepad.com/blog/2012/06/the-dark-side-of-hadoop.html"&gt;The dark side of Hadoop&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>科大讯飞</title><link>https://hangyan.github.io/post/2019-08-31-iflytech/</link><pubDate>Sat, 31 Aug 2019 13:01:56 +0000</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2019-08-31-iflytech/</guid><description>&lt;p&gt;讯飞并不是一个很坏的企业，它只是太懒了。&lt;/p&gt;
&lt;p&gt;国内的互联网巨头基本上没有干净的，无不是带着一身罪恶爬到了现在的位置。讯飞是一个巨头，因为他在语音行业是老大，规模也不小。但它也不是，因为它身处合肥，似乎互联网的新潮跟它都没什么关系，也很难在新闻上见到它的身影。可以概括地来讲，它是成也合肥，败也合肥。&lt;/p&gt;
&lt;p&gt;之所以说是成也合肥，一是中科大保证了它的研发能力不弱，而是与政府互相依赖，保证了他政府方面的合作优势。败也合肥，则是因为在这么一个三线城市了，所有人都想过着朝九晚五的生活，并不想像去参与北上广的&lt;code&gt;发展竞赛&lt;/code&gt;。再加上公司的营收严重依赖于国企的订单，它自身也变得越来越像一个国企。&lt;/p&gt;
&lt;p&gt;机器学习大火之后，语音识别技术变得更加简单和常用。所有的大厂都可以投入人力物力去研发自己的语音识别技术，并且与讯飞的差距逐渐缩小，进一步去蚕食其本身的市场。在可以预见的未来里，它的日子并不会好过。&lt;/p&gt;
&lt;p&gt;还有一个更明显的问题是， 它太不会营销了。虽然说酒香不怕巷子深，但是当别人都在拼命地宣传自己的产品，而消费者又不是很在意这之间的细微差距，那么累积起来的用户习惯是很怕的。纵观互联网的历史，有太多产品好却死去的案例了。&lt;/p&gt;
&lt;p&gt;还是希望它能好好正视下自己现在的困境，好好发展下去。&lt;/p&gt;</description></item><item><title>Rick and Morty</title><link>https://hangyan.github.io/post/2019-08-29-rick-and-morty/</link><pubDate>Thu, 29 Aug 2019 11:06:59 +0000</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2019-08-29-rick-and-morty/</guid><description>&lt;p&gt;我给很多人推荐过这部动画片。可能也是唯一一个我一直在不遗余力地给很多人推荐的艺术作品。&lt;/p&gt;
&lt;p&gt;曾经我也跟很多人一样，在追《马男》，看似颓废却又奋进，真是一部优秀的剧集。后来偶然接触到了《Rick and Morty》，再去回看《马男》，不是它不好了，只是相比来说，它太平庸了。&lt;/p&gt;
&lt;p&gt;很多喜好的东西都太过个人化了，即使你再爱，也知道你没法总给别人讲。但《Rick and Morty》不同，它是一个流行于全世界的东西，是文化本身，也是文化的反面本身。&lt;/p&gt;
&lt;p&gt;很多美国的青少年如此痴迷，以至于在现实中做出种种奇怪的举动。川香酱的疯狂正是一个绝妙的对于这个剧的呼应。而对于中国的青少年来说，这个美国文化里衍生出来的动画片，包含了太多反美国文化的东西，以至于每一个大洋彼岸的年轻人都被它深刻地吸引，感同身受。它成了一个跨域东方与西方，跨越资本主义与社会主义，跨域文化的东西。&lt;/p&gt;
&lt;p&gt;从内容上来说，抛开表面的光怪陆离，看似蹩脚的宇宙设定，下面隐藏的是每一个人一生都要遇到的问题：父母，子女，家庭，婚姻，种族，文化，梦想，自我的哲学思考等等。同样是表面的虚无主义，底层的复杂深刻。不同的人看会有不同的感受，但每个人都会沉浸其中。&lt;/p&gt;
&lt;p&gt;《马男》的&lt;code&gt;失败&lt;/code&gt;在于，同样是在探讨这些问题，但是太根植于美国文化，让其他地方的人总觉得有隔阂，且太过肤浅，没有悲剧式的深刻。《Rick and Morty》里最为痛苦的设定在于，Rick 知晓自己是一个动画角色，这种痛苦一直伴随着他，自杀而不能。&lt;/p&gt;
&lt;p&gt;同样无法摆脱这种可能的我们，可能为了规避这样的痛苦而不敢去想这个问题。&lt;/p&gt;</description></item><item><title>KARL, ASLR, KASLR</title><link>https://hangyan.github.io/post/2019-08-27-karl-aslr/</link><pubDate>Tue, 27 Aug 2019 11:11:23 +0000</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2019-08-27-karl-aslr/</guid><description>&lt;!-- toc --&gt;
&lt;p&gt;前段时间在零星地看一些关于 unikerel 的文章。里面提到的一些安全相关的技术非常有趣，就又开始转向 kernel 安全这部分。本文主要探讨的是 KARL, KASLR 以及 ASLR 相关的技术。&lt;/p&gt;
&lt;p&gt;安全相关的实现技术很多，但想了想，大体的分类可以归为以下几类：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;RWX 权限检查&lt;/li&gt;
&lt;li&gt;随机化: 尽量不用确定性的内存地址&lt;/li&gt;
&lt;li&gt;暴露最少的信息: 日志，输出里尽量不暴露相关信息&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;KARL, KASLR 以及 ASLR 都属于第二类。尽量让应用程序以及内核在内存中的地址随机化，这样攻击者就难以猜出相关的地址。&lt;/p&gt;
&lt;p&gt;简单来讲， ASLR(Address-space layout randomization) 主要是指内存中用户层的地址随机化，KASLR 是指内存中内核的地址随机化， 而 KARL(kernel adress randomized link)是指内核本身的地址随机化。ASLR出现的时间更早一些，更成熟一些。&lt;/p&gt;
&lt;p&gt;注意有一个区别， 三者中都有一个 &lt;code&gt;L&lt;/code&gt;，但意义不一样。 ASLR/KASLR 中的 &lt;code&gt;L&lt;/code&gt; 是指 &lt;code&gt;layout&lt;/code&gt;，是说内存中的布局。你不可能要求大量的应用程序自己去考虑随机化，而是在应用程序无感知的情况下，由内核来选择一个随机的位置来运行应用程序。而 KARL 中的 &lt;code&gt;L&lt;/code&gt; 是指 &lt;code&gt;link&lt;/code&gt; ，是在链接阶段就做好了地址的随机化处理，这样就不需要再内存中在做了。&lt;/p&gt;
&lt;h2 id="aslr"&gt;ASLR&lt;/h2&gt;
&lt;p&gt;ASLR 理解起来相对更为简单，本身不需要再重复介绍。&lt;/p&gt;
&lt;h3 id="rng"&gt;RNG&lt;/h3&gt;
&lt;p&gt;不管是 KARL / ASLR / KASLR，其核心都是一个&lt;code&gt;随机&lt;/code&gt;的概念。明显它依赖于随机数的生成算法。当然随机程度越高越安全，但是又要考内核的性能。这个平衡很难掌握，选择哪个 RNG(random number generation) 也经过了很多讨论。&lt;/p&gt;
&lt;p&gt;一开始用的是 &lt;code&gt;get_random_int()&lt;/code&gt;,一个性能比较好但安全性不够好的方法，后来有人提出用 &lt;code&gt;get_random_bytes()&lt;/code&gt;,安全性大大提高，但是性能下降也比较多。再后来 Linux 自己对&lt;code&gt;get_random_int()&lt;/code&gt;做了一些改进, 增加了一些噪音进去，在不影响性能的情况下改进了随机性。这也是目前在用的方法。&lt;/p&gt;</description></item><item><title>Cloud Native Artifact Registries</title><link>https://hangyan.github.io/post/2019-08-26-cnar/</link><pubDate>Mon, 26 Aug 2019 13:46:42 +0000</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2019-08-26-cnar/</guid><description>&lt;p&gt;Helm3 的 design proposal 里面介绍了对于Chart Repo 融合到 Distribution 的提议。目前已经有了实验的 demo，理论上并没有什么问题。因为 Distribution的设计里并不是只能存镜像，而是一个开放的协议。我们可以想下目前企业在 DevOps 场景下，除了代码之外，有哪些文件需要存储&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;构建产物&lt;/li&gt;
&lt;li&gt;二进制程序&lt;/li&gt;
&lt;li&gt;Helm Charts&lt;/li&gt;
&lt;li&gt;镜像仓库&lt;/li&gt;
&lt;li&gt;测试报告&lt;/li&gt;
&lt;li&gt;代码分析报告&lt;/li&gt;
&lt;li&gt;Kustomize 文件&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;等等。为数众多，而且都是存储于不同系统的。构建的产物一般都暂存于构建系统内部，有失效时间。镜像在 Docker Hub 或者 Harbor 中，Helm Charts 在官方仓库或者 chartmusuem 这样的仓库中。基本上每一项都需要单独维护存储设施，分别要使用不同的客户端。Helm3 的这个迁移的决定，不止对 Helm 本身有很大的意义，对于其他的产品也具有很大的启发意义。Docker公司本身也在做这方面的 整合， Docker app 既可以用来分发 docker-compose 文件还有 CNAB 格式的应用。在可以遇见的将来，所有的 Artifacts 共用一个存储和客户端是可能的。&lt;/p&gt;
&lt;p&gt;这有一另外的一个好处是，这是一个独立于云提供商的平台。不管是在 AWS/GCE/AZURE 还是其他云平台，不管是用 S3 还是其他存储，Distribution 这样的平台可以提供统一的接口。&lt;/p&gt;
&lt;p&gt;另一方面，Kubernetes 平台上应用的分发格式也处于激烈的竞争阶段，目前常用的就有以下几个:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Docker App&lt;/li&gt;
&lt;li&gt;Docker Compose&lt;/li&gt;
&lt;li&gt;CNAB&lt;/li&gt;
&lt;li&gt;Helm&lt;/li&gt;
&lt;li&gt;Kustomize&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Distribution 这样的平台能帮助用户减少很多使用负担和迁移成本。预测来看，Helm/Kustomize 最终的赢面大些，docker 可能只能继续专注于更底层的业务。&lt;/p&gt;
&lt;h2 id="链接"&gt;链接&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://helm.sh/blog/helm-3-preview-pt3/"&gt;Helm 3 Preview: Charting Our Future – Part 3: Chart Repositories&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.docker.com/2018/12/docker-app-and-cnab/"&gt;Docker App and CNAB&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stevelasker.blog/2019/01/25/cloud-native-artifact-stores-evolve-from-container-registries/"&gt;Cloud Native Artifact Registries evolve from Docker Container Registries&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>失败博物馆 - Mesos/Marathon</title><link>https://hangyan.github.io/post/2019-08-23-mesos-marathon/</link><pubDate>Fri, 23 Aug 2019 10:28:40 +0000</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2019-08-23-mesos-marathon/</guid><description>&lt;p&gt;Mesos/Marathon 的失败，怎么说，太过典型了。如教科书一般，清晰的不能再清晰。&lt;/p&gt;
&lt;p&gt;首先，它流行的时候，是因为大家没得选。一个还在蹒跚学步阶段的 Kubernetes 就开始将它打的节节败退。到了现在，连Mesosphere 已经转向了 Kubernetes。&lt;/p&gt;
&lt;p&gt;从一开始，这就是一个学术界的产品。由一堆并不擅长编写工业化软件的研究室的学生和老师写出。这导致了 Mesos / Marathon 的致命缺陷&lt;/p&gt;
&lt;p&gt;首先，用 C++ 写 Mesos， C++可以说是学术界和工业界态度差别最大的编程语言了。对学术界来说，这是一个可以用来探究编程语言极限的编程语言，数不清的高级设计，数不清的语言学设计，你再任何其他语言里都见不到这么博大精深的体系。但对工业界来说，这是一种灾难级的编程语言。Linux 都在天天骂它，难学难用难调试，市场占有率也一直不断下滑。Mesos 选择了 C++之后，极大地减少了开源社区去贡献代码的可能性，彻底沦为了创世的开发团队自己的作品。&lt;/p&gt;
&lt;p&gt;更灾难的是，Marathon 用了 Scala，一个除了 C++之外第二复杂的语言。其同样程度的撕裂性，对研发人员的折磨，让人都开始怀疑为什么当初要选择编程这份工作。语言设计者一旦觉得自己过于聪明，会趋向出设计出来大众难以理解的东西。这并不怪大众，而是怪语言设计者。两个世界上最复杂的语言，组合起来，成了一个大部分研发人员都不愿意碰的黑盒子。然而，它又不是稳定到不出什么问题，总会有出故障需要排查的时候。这便是让所有人都痛苦的时刻。所以即使今天大家发现 Kubernetes 已经非常复杂难以理解了，但没有人会抱怨太多，因为只要你肯花点时间，分析分析代码，总能慢慢理解。而对于 Mesos/Marathon,如果你发现了一个问题，想去阅读代码，大部分时间你都在纠结：这个语法什么意思？这个语法又是什么意思？&lt;/p&gt;
&lt;p&gt;另外一种研究室出来的代码的问题在于，很少有人会考虑到架构的可扩展性。有了一个 idea,实现出来，发个 paper，大部分就完了。等到真正开始用户多的时候已经来不及了。而工业界的产品不一样，从一开始可扩展性就是所有系统在设计之初必须考虑的一个问题。所以我们看到，Mesos/Marathon 自发布之后，后续的功能扩充是极其微弱的，难以有任何实质性的功能增强。而 Kubernetes 在发布之后，新功能如井喷一般。当然这里面也有编程语言本身的功能。Golang 问题再多，简单易用本身就足够让很多人开始使用了。&lt;/p&gt;</description></item><item><title>失败博物馆 - Jenkins</title><link>https://hangyan.github.io/post/2019-08-22-fuck-jenkins/</link><pubDate>Thu, 22 Aug 2019 17:02:37 +0000</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2019-08-22-fuck-jenkins/</guid><description>&lt;p&gt;我想记录一下工作中使用过的软件里的一些在设计上比较失败的。因为它们，工作中多了很多痛苦。&lt;/p&gt;
&lt;p&gt;Jenkins 便是其中之一。跟很多软件一样，Jenkins 在商业上是成功的，成功的原因就是用户并没有什么选择。对 Jenkins 非常了解的人来说，能很容易列举出非常多的详细的jenkins 的问题。从一个普通使用者的角度来看，最直观的两个感觉就是: Jenkinsfile 难写，流水线很慢。&lt;/p&gt;
&lt;p&gt;对我来讲，Jenkinsfile 就是最大的设计败笔。从工作中的反馈来看，即使看了文档，也没人知道怎么去写一个正确的 Jenkinsfile。用户想描述好一些流水线步骤，最直观的想法应该就是贴近于 Makefile, 每个步骤几条命令，清清楚楚。再加上其他一些并发控制，产出物管理即可。用 YAML 或者 TOML 这样的配置文件即可。&lt;/p&gt;
&lt;p&gt;几乎除了 Jenkins 之外的所有 CI/CD 工具，都选择了 用YAML。而 Jenkins 使用了 Jenkinsfile, Groovy 语言，意味着用户需要首先了解 Groovy，在学习 jenkins的语法，才能写好 Jenkinsfile。一些自作聪明的开发人员在类似的设计问题时，会有一种炫技的想法，总想用 DSL 来解决问题。结果通常是不好的，既给用户带来了沉重的负担，也让系统变得难以理解。基于 JVM 的很多语言都是如此命运。&lt;/p&gt;
&lt;p&gt;即使你费劲千辛万苦，终于写出了一个能跑通的 Jenkinsfile,这时候你会发现，在 Jenkins 的流水线里面，有很多复杂的难以理解的东西。为什么多出来很多莫名其妙的步骤？系统日志和构建日志怎么这么杂乱无章地堆在一块？为什么速度总是这么慢？界面为什么这么丑？不一而足。&lt;/p&gt;
&lt;p&gt;想深刻地理解这些问题在哪，当然可以去好好探究一下它的架构，然后了解这些问题的根源。但这是在没有必要了，对于一个将死的系统来说。作为用户来讲，在已经有了替代品之后，赶快逃离就是了。&lt;/p&gt;</description></item><item><title>数据库与操作系统</title><link>https://hangyan.github.io/post/2019-08-22-db-os/</link><pubDate>Thu, 22 Aug 2019 11:21:52 +0000</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2019-08-22-db-os/</guid><description>&lt;p&gt;数据库一般认为是一种系统软件。而操作系统处于更底层的位置。这是一种通常的认知。&lt;/p&gt;
&lt;p&gt;Unix 的一切皆文件的设计思想，从一定程度上来讲，表明了操作系统内部不同组件之间有一定的结构上的一致性。比如网络接口和文件接口，都有如下的操作&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Rread&lt;/li&gt;
&lt;li&gt;Write&lt;/li&gt;
&lt;li&gt;Close/Open&lt;/li&gt;
&lt;li&gt;permission&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;等等。进程和内存管理也是类似的逻辑。所以我们现在可以看到 Linux 系统中有很多类似的尝试，用文件的形式来来作为很多内部组件对外的接口。&lt;/p&gt;
&lt;p&gt;这是 Linux 一直在被&lt;code&gt;夸赞&lt;/code&gt;的地方。一个听起来优美的设计哲学，吸引了很多人从&lt;code&gt;奇怪&lt;/code&gt;的，程序员不友好的 Windows 逃离过来，并花费大量的精力来学习和理解这个设计之下隐藏的诸多&lt;code&gt;肮脏&lt;/code&gt;的细节。&lt;/p&gt;
&lt;p&gt;工作越久，越来越多的人发现。相比较而言，平时还是 Mac 和 Linux 用起来更方便。即使是爱折腾的程序员，也大多不愿再去浪费时间去折腾 Linux，去折腾 VIM/Emacs。不是因为年纪大了，而是因为这些东西确实用户不友好，而且有设计缺陷。&lt;/p&gt;
&lt;p&gt;几十年前， &lt;Unix Haters Book&gt;已经很清楚地点评了 Linux/Unix 上的诸多问题。然而它并没有推动 Linux/Unix 去改变和解决这些问题。开源是一面美好的大旗，但它也蒙蔽了跟着的人。&lt;/p&gt;
&lt;p&gt;Linux 桌面的失败简直惨不忍睹。自由的 fork, Client-Server 的架构，无尽的口水仗。最终活下来了两个无法合作的 KDE/Gnome。Windows 自然也有很多设计的问题，但注册表现在看来相对于 Linux 的配置文件来说简直是太优秀了。多年人，有些人会想到：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不同 cmd 的 input 和 ouput 的格式都不一样，增加了很多研发的负担&lt;/li&gt;
&lt;li&gt;能否用一种统一的数据格式来表达配置以及输入输出？比如 json&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;也有人尝试过，但从来不会真正影响到社区。即使成功了，systemd 的经历也历历在目。&lt;/p&gt;
&lt;p&gt;虽然能将操作系统的诸多概念简化成文件，但文件仍然是一个相对复杂的抽象概念。除了文件系统，没有哪个其他模块能够与文件如此对齐。就数据本身而言，打开文件之后，读写数据，本质上简化为两个结构: &lt;code&gt;list&lt;/code&gt; 和 &lt;code&gt;dict&lt;/code&gt;，组合起来就是一个 &lt;code&gt;table&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;读写文件，基本上就是不断地对这个 table 做修改，增加，删除，修改行。CSV 格式的文件更是可以直接直接对应于一张表。所有其他的内部模块,其操作也是类似的；&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;创建进程： 往进程列表里添加一个元素&lt;/li&gt;
&lt;li&gt;销毁进程: 从进程列表里删除一个元素&lt;/li&gt;
&lt;li&gt;添加设备: 往设备列表里增加一个元素&lt;/li&gt;
&lt;li&gt;配置设备: 修改设备的属性&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不只是 Linux，所有操作系统面临的都是同样的问题，使用的都是同样的的机制: 不听地对Table 做各种操作。现状是，所有的操作系统在不同模块的管理上都是有差别的，因为没有统一数据模型的支持，每个模块都在不断地用不同的形式做类似的操作，其提供给用户的功能也因此而受限。&lt;/p&gt;</description></item><item><title>What is Helm Doing Wrong and How a Helm3 Controller Can Fix It</title><link>https://hangyan.github.io/post/2019-08-03-helm-controller/</link><pubDate>Sat, 03 Aug 2019 11:26:30 +0000</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2019-08-03-helm-controller/</guid><description>&lt;p&gt;Helm is big success for sure, it&amp;rsquo;s nearly the standard application package format in kubernetes.You only need to provide some metadata about your application&amp;rsquo;s name, version, description….Helm can help you package up and upload to a central or custom chart repo.Just like npm,rpm,docker image, or whatever other package management system.&lt;/p&gt;
&lt;p&gt;About a year ago, Helm3 was drafted. Since it&amp;rsquo;s still on the proposal stage, most users are still using helm2, include us. The journey we spent with helm was not a very pleasant one, we struggled very hard to make it work. In the end, we decided to create a kubernetes controller based on Helm3 proposal, it works well and we open source it on github: &lt;a href="https://github.com/alauda/captain"&gt;captain&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Sinfonia: a new paradigm for building scalable distributed systems</title><link>https://hangyan.github.io/post/2019-07-24-sinfonia-paper/</link><pubDate>Wed, 24 Jul 2019 17:22:10 +0000</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2019-07-24-sinfonia-paper/</guid><description>&lt;!-- toc --&gt;
&lt;p&gt;论文链接: &lt;a href="http://www.sosp2007.org/papers/sosp064-aguilera.pdf"&gt;Sinfonia: a new paradigm for building scalable distributed systems&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Sinfornia 像是一个构建分布式系统的 SDK 。目前的市面上分布式系统已经非常多了，比如 etcd, zookeeper, ceph 等等。但他们都是分别实现的，彼此之间的代码并无太多借鉴。Sinfonia 的实践是很难得的，可以说比大部分的分布式软件都更有指导价值一些。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://hangyan.github.io/images/sinfonia/arc.png"&gt;&lt;/p&gt;
&lt;p&gt;这个图中展示了 Sinfonia 中关键的几部分&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;user library: SDK 部分，隐藏了分布式系统处理内部细节的 SDK。用户基于 user library 来开发分布式应用&lt;/li&gt;
&lt;li&gt;minitranscations: Sinfonia 的核心概念。一个分布式的事物机制&lt;/li&gt;
&lt;li&gt;application node: 应用节点&lt;/li&gt;
&lt;li&gt;memory node: 维持分布式系统内部状态的节点。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;图中没有画出的有&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;管理节点: 用于执行一些定期的 recover 任务&lt;/li&gt;
&lt;li&gt;&lt;code&gt;directory node&lt;/code&gt;: Application Node 访问 Memory Node 使用的是逻辑 id, directory node 记录了逻辑 id 与 真实地址的映射。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="memory-node"&gt;Memory Node&lt;/h2&gt;
&lt;p&gt;Memory Node 存储了应用的状态数据，根据不同情况，可能是存在 RAM 中，也可能是在磁盘上。 user library 封装了操作 Memory Node 中数据的方式。Application Node 可以与 Memory Node 是同台机器也可以是不同机器(有些 Application 出于性能考虑会需要 Applicaiton Node 与 Memory Node 为同台机器，Sinfonia 会告知 Application 此种情况以便让其尽量把数据写在本机)。&lt;/p&gt;</description></item><item><title>Golang FAQ</title><link>https://hangyan.github.io/post/2019-07-19-golang-faq/</link><pubDate>Fri, 19 Jul 2019 00:11:33 +0000</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2019-07-19-golang-faq/</guid><description>&lt;!-- toc --&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2019-07-19-golang-faq/#%e6%96%87%e6%a1%a3%e7%9a%84%e5%8f%af%e5%be%97%e6%80%a7"&gt;文档的可得性&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2019-07-19-golang-faq/#%e8%af%ad%e8%a8%80%e8%ae%be%e8%ae%a1%e7%bb%86%e8%8a%82"&gt;语言设计细节&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2019-07-19-golang-faq/#%e5%8f%af%e8%af%bb%e6%80%a7"&gt;可读性&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2019-07-19-golang-faq/#generic-types"&gt;Generic types&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2019-07-19-golang-faq/#error-%e4%b8%8e-exception"&gt;Error 与 Exception&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2019-07-19-golang-faq/#assertions"&gt;assertions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2019-07-19-golang-faq/#static-link"&gt;static link&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2019-07-19-golang-faq/#tips"&gt;Tips&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2019-07-19-golang-faq/#unused-imports"&gt;unused imports&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;!-- tocstop --&gt;
&lt;p&gt;Golang FAQ 是一篇比较好的关于 Golang 设计的文档。比较琐碎，但是解释了很多 Golang 设计的权衡之处。没有语言是完美的，从工业界来看，Golang 是一种流行的，成功的编程语言。从语言设计来看，当然仍有欠缺与不足之处。许式伟是前者的角度，王垠是后者的角度。当然除了宏观层面，FAQ 文档仍然提供了很多有趣的细节。&lt;/p&gt;
&lt;h2 id="文档的可得性"&gt;文档的可得性&lt;/h2&gt;
&lt;p&gt;所有的语言都知道支持文档。但可得性是一个更重要的维度。 Python 有 PEP 系列详细解释了语言设计的细节， 有 read the docs 系统为各种 library 提供用户文档。 Golang有 godoc 工具以及 Golang blog, 还有像 FAQ 这样的设计 detail 解释。二者在此方面都是做的比较优秀的，也在一定程度上促进了语言的流行。&lt;/p&gt;
&lt;h2 id="语言设计细节"&gt;语言设计细节&lt;/h2&gt;
&lt;h3 id="可读性"&gt;可读性&lt;/h3&gt;
&lt;p&gt;可读性一方面是要考虑尽量贴近大部分程序员的背景以及知识结构，另一个方面语言的设定要尽量 common, 不会让人需要额外思考来确定一个语法的语义。Golang 对于大部分具有 c / python 等背景的程序员来说非常易懂，也尽量控制新的语法的数量，所以能够迅速的大范围的流行起来。&lt;/p&gt;
&lt;p&gt;另一方面，为了语言的可维护性，去掉一些便捷的语法糖也是一个重要的考量。Golang 去掉了 &lt;code&gt;?:&lt;/code&gt; , 隐式类型转换，指针操作，Goroutine 的内部结构不对外暴露等等。虽然代码不会像 python 那样简短，但在可维护性以及稳定性上都有了改善。&lt;/p&gt;</description></item><item><title>Common Index</title><link>https://hangyan.github.io/post/2019-07-16-common-index/</link><pubDate>Tue, 16 Jul 2019 16:20:10 +0000</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2019-07-16-common-index/</guid><description>&lt;p&gt;这几天在重温 《疑犯追踪》，作为一个近科幻的代表作，其描绘的社会是如此地贴近现实，让人想到时都会有一种无力感。在技术社区的种种讨论中，总会有零星的关于此话题的讨论以及解决之道。去中心化网络作为其中一个核心概念，已经有很多落地的实验性产品，虽然仍然无法撼动如今的网络结构，但总是一些希望之火。另外一个最近引起热门讨论的技术便是 Common Index。搜索引擎发展了这么多年，在给人们带来了很多便利的同时，也带来了无数的负面影响。Common Index 是一种解决途径，虽然如去中心化网络一样，看起来都很遥不可及，但仍然是值得期待的。&lt;/p&gt;
&lt;p&gt;搜索引擎是一种商业产品，其背后的公司依靠大部分依靠广告牟利。这是一种非常容易理解的商业模式，但是却造成了以下后果:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;Too Big To Fail&lt;/code&gt;: 搜索引擎技术随着互联网规模的发展，会形成越来越高的技术壁垒。没有任何新公司能进入到这个市场。Google 目前占据着绝对主流，其他几家只占有微量的比例: &lt;img loading="lazy" src="https://hangyan.github.io/images/common-index/market-share.png"&gt;&lt;/li&gt;
&lt;li&gt;搜索引擎公司可以通过一些看似 &lt;code&gt;公平&lt;/code&gt;的策略来调整不同网页的权重，从而来影响互联网的发展。比如 Google 一直在提高支持移动互联网网页的比重。这样的行为不能说是很坏，但是否应该由一家公司来做这些决定呢？&lt;/li&gt;
&lt;li&gt;搜索引擎公司可以通过人为调整搜索结果顺序来牟利。比如毫无底线的百度。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;信息时代人们对于信息检索的需求是非常庞大的，而且或许正如某些人所说，它不再是一个普通的商品，而是成为类似于水电一样的基础设施，每个人的生存都依赖于这些东西。在这种情况下，搜索技术再由某些垄断公司提供是不合适的，尤其是公司容易受到地缘政治的影响。Common Index 对此的提议是将搜索技术分为两层: 底层是通用的 index 技术，上层是各个公司的定制部分。&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://hangyan.github.io/images/common-index/owi.png"&gt;&lt;/p&gt;
&lt;p&gt;这里&lt;code&gt;OWI&lt;/code&gt; 指 &lt;code&gt;Open Web Index&lt;/code&gt;, 即本文一直说的 &lt;code&gt;Common Index&lt;/code&gt;。Common Index 作为基础设施，提供基本的互联网索引，不同的商业机构可以基于此添加自己的特定的索引，并提供不同的界面以及功能。比如我可以实现一个专门搜书的搜索引擎，或者一个专门用来搜索特定某个人的公开信息的搜索引擎等等。Common Index 让新公司加入搜索引擎这个市场变得可能，并且可以通过差异化的服务来获取用户。有了这个基础，搜索引擎市场便有可能成为一个良性竞争的市场。&lt;/p&gt;
&lt;p&gt;要想这个设想变成可能， 操作起来是很麻烦的，所以很多类似的设想都只停留在设想阶段。我能想到两个问题便是:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;谁来承担 Common Index 的成本？ 理论上可以应该让参与这个市场的公司来共同承担，想扶持本国在此方面有所发展的国家可以资助本国公司去参与这个市场。&lt;/li&gt;
&lt;li&gt;如何打破现有市场的格局? 最理想的情况是，在未来的某一个天，通过反垄断法将 Google 拆分， 分离出其 部分index 技术作为 Common Index 的基础。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;当然，希望在未来讨论这些问题的时候，百度已经不在了。&lt;/p&gt;
&lt;p&gt;相关链接:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://hub.packtpub.com/can-an-open-web-index-break-googles-stranglehold-over-the-search-engine-market/"&gt;Can an Open Web Index break Google’s stranglehold over the search engine market?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://arxiv.org/abs/1903.03846"&gt;The Web is missing an essential part of infrastructure: an Open Web Index&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>代码与注释</title><link>https://hangyan.github.io/post/2019-07-14-code-and-comments/</link><pubDate>Sun, 14 Jul 2019 21:14:49 +0000</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2019-07-14-code-and-comments/</guid><description>&lt;!-- toc --&gt;
&lt;p&gt;代码应该拥有良好的注释一直是业界共识，毕竟，在可预见的未来里，阅读代码的主要还是人。所有的语言都支持注释，有的拥有额外的注释提取工具及格式规范。但总体来说，大部分语言在这块做的都比较一般。Python有一些约定俗称的规范以及 Sphinx 这样的工具， Golang 的规范比较简单，但内置了 godoc 工具。Lisp 算是做的比较好的，示例如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-lisp" data-lang="lisp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;defun&lt;/span&gt; &lt;span class="nv"&gt;small-prime-number-p&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;&amp;#34;Return T if N, an integer, is a prime number. Otherwise, return NIL.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;cond&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nb"&gt;or&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nv"&gt;n&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nf"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;n&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="no"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;divisorp&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="nv"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;t&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;loop&lt;/span&gt; &lt;span class="nv"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;i&lt;/span&gt; &lt;span class="nv"&gt;from&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="nv"&gt;upto&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;sqrt&lt;/span&gt; &lt;span class="nv"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;by&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;never&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;divisorp&lt;/span&gt; &lt;span class="nv"&gt;i&lt;/span&gt; &lt;span class="nv"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)))))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其独特之处在于，不同于一般语言注释位于函数体之外，而是将其放置于函数 body 开始处。这样开发者更容易将其当作函数定义的一部分，也有助于养成良好的注释习惯。&lt;/p&gt;
&lt;p&gt;Golang 虽然 在注释方面做的普通，但是在官方 library 以及最佳实践方面成功地鼓励了人们尽可能地写非常详细的注释，具体到一个 struct 的各个字段上。尤其是在 kubernetes 社区里面, 其&lt;a href="https://news.ycombinator.com/item?id=18772873"&gt;Space Shuttle style&lt;/a&gt; 的 Code 对于社区的蓬勃发展可以说是一个极大的背后功臣，从如下的代码便可一窥其风格：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Adapts a ConfigMap into a projected volume.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;//
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// The contents of the target ConfigMap&amp;#39;s Data field will be presented in a&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// projected volume as files using the keys in the Data field as the file names,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// unless the items element is populated with specific mappings of keys to paths.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Note that this is identical to a configmap volume source without the default&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// mode.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;ConfigMapProjection&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;LocalObjectReference&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// If unspecified, each key-value pair in the Data field of the referenced&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// ConfigMap will be projected into the volume as a file whose name is the&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// key and content is the value. If specified, the listed keys will be&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// projected into the specified paths, and unlisted keys will not be&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// present. If a key is specified which is not present in the ConfigMap,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// the volume setup will error unless it is marked optional. Paths must be&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// relative and may not contain the &amp;#39;..&amp;#39; path or start with &amp;#39;..&amp;#39;.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// +optional&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Items&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="nx"&gt;KeyToPath&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Specify whether the ConfigMap or it&amp;#39;s keys must be defined&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// +optional&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Optional&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可读性越好的代码越容易流行。Golang 在这方面做的很好。&lt;/p&gt;</description></item><item><title>Golang 1.9 Release Note</title><link>https://hangyan.github.io/post/2019-05-27-golang-release-note/</link><pubDate>Mon, 27 May 2019 22:28:38 +0000</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2019-05-27-golang-release-note/</guid><description>&lt;!-- toc --&gt;
&lt;p&gt;今天在翻存在 Pocket 里的文章，翻到一篇 Golang 1.9 的 &lt;a href="https://golang.org/doc/go1.9"&gt;Release Note&lt;/a&gt;。不得不说，Golang 在文档化上做的相当不错。对比其他语言来说，人们关注于每个版本的更新也会更多。不过这样的对比样本比较少，可能也不是很公正。Python 因为 2/3 版本的分离，可能很多人都不太关心 3 版本的更新。C/CPP 的文档一直比较欠缺，差距太大，而且版本之间迭代的也比较慢（历史包袱也多）。&lt;/p&gt;
&lt;p&gt;现在 Golang 的版本已经到 1.13 了大概，2 的 design 也应该有不少文档。这样追下去应该有不少发现。利用语言的新特性来优化代码也是一件很有乐趣的事情。&lt;/p&gt;
&lt;h1 id="19-新特性介绍"&gt;1.9 新特性介绍&lt;/h1&gt;
&lt;h2 id="syncmap"&gt;sync.Map&lt;/h2&gt;
&lt;p&gt;之前知道有这么个东西，但是自己在写代码的时候仍然没有将他作为一个&lt;code&gt;默认的选项&lt;/code&gt;来考虑，仍然是自己加了 lock 手工实现，不得不说是一件很拙劣的事情。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-golang" data-lang="golang"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;lock&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RWMutex&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kd"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;再加上一些自己实现的&lt;code&gt;get/set&lt;/code&gt;函数。现在用一个普通的&lt;code&gt;sync.Map&lt;/code&gt;即可。注释上说了它有自己的适用场景：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;value 只写一次但是多次读&lt;/li&gt;
&lt;li&gt;不同 goroutines 读写的数据不重合&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;即使这样，适用的场景还是挺多的。更少的代码，更好的性能，值得考虑。&lt;/p&gt;
&lt;h2 id="test"&gt;Test&lt;/h2&gt;
&lt;p&gt;Golang 的 Test 包也包含了很多比较&lt;code&gt;少见&lt;/code&gt;的的功能。借助 1.9 的 Release Note 看了下，也都是很实用的。&lt;/p&gt;
&lt;h3 id="testhelper"&gt;test.Helper()&lt;/h3&gt;
&lt;p&gt;在测试中我们经常需要写一些 helper function,这些函数是服务于真正的测试函数。如果它们出错并且打印出来，只能显示到 helper function 的文件名和行号。比如我们自己实现一个 assert 函数，查看错误就很不方便。而&lt;code&gt;test.Helper()&lt;/code&gt;就是为了解决这个问题，当在 helper function 中打印具体的错误时，会显示的是调用者的文件名和行号，如下面例子所示:&lt;/p&gt;</description></item><item><title>作为公共组件的 apiserver</title><link>https://hangyan.github.io/post/2019-05-24-apiserver/</link><pubDate>Fri, 24 May 2019 15:54:47 +0000</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2019-05-24-apiserver/</guid><description>&lt;p&gt;PDF 文件在此: &lt;a href="https://hangyan.github.io/files/apiserver.pdf"&gt;apiserver&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;起因是因为一个要做一个新的项目，在综合考量 kubernetes 的各个库之后，发现 kubernetes 已经从主项目中把很多可以复用的项目都单独移出来了。而且包含了很多通用性很强的代码。作为以 Golang + Kubernetes 为核心的架构，这些代码基本上可以充当公司内部的公共组件，省去了很多重复工作。&lt;/p&gt;
&lt;p&gt;大部分公司都很少有这个意识，去尽量做好公共组件这块。业务需求本身占用了太多时间，这种属于内部架构的问题基本都是靠零星时间来改善。Kubernetes 这些项目不是说多好，而是它作为一个极其其庞大的项目，在开发的过程中自己造的轮子一般的公司都会有类似的需求。研究，拆解，复用这些公共代码，能够很大程度弥补上公司里公共技术这块的缺失。&lt;/p&gt;
&lt;p&gt;公共代码越多，项目的相似程度也越高，维护及使用也越简单方便。&lt;/p&gt;</description></item><item><title>在 Kubernetes 中安装 Gitlab</title><link>https://hangyan.github.io/post/2019-05-08-install-gitlab/</link><pubDate>Wed, 08 May 2019 13:36:33 +0000</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2019-05-08-install-gitlab/</guid><description>&lt;!-- toc --&gt;
&lt;p&gt;现在很少写这种安装类的博客了。之前在公司部署了一个 Gitlab 作为日常使用，因为步骤比较繁琐。在此把零零散散的资料汇聚一下，记录一个比较完整的安装过程&lt;/p&gt;
&lt;h2 id="环境"&gt;环境&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Kubernetes 1.13.1 三个高可用节点&lt;/li&gt;
&lt;li&gt;每个节点上一个空余磁盘&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="步骤"&gt;步骤&lt;/h2&gt;
&lt;h3 id="安装-rook"&gt;安装 Rook&lt;/h3&gt;
&lt;p&gt;Rook 提供了基于 Ceph 的分布式存储，我们利用每个节点上的空余磁盘来支撑 Kubernetes 里的 PV/StorageClass 等&lt;/p&gt;
&lt;h4 id="helm-安装"&gt;Helm 安装&lt;/h4&gt;
&lt;p&gt;首先，初始化磁盘&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mkfs.ext4 /dev/vdb
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mount /dev/vdb /var/lib/rook
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mkdir /var/lib/rook
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# TODO: add to /etc/fstab&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后通过 Helm 安装 Rook&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;helm repo add rook-stable https://charts.rook.io/stable
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;helm install --namespace rook-ceph-system rook-stable/rook-ceph
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;部署完成后可以看到&lt;code&gt;rook-ceph-system&lt;/code&gt; Namespace 下运行的 Resource:&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://hangyan.github.io/images/gitlab-install/rook.png"&gt;&lt;/p&gt;
&lt;h4 id="创建-cephcluster"&gt;创建 CephCluster&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;#################################################################################&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# This example first defines some necessary namespace and RBAC security objects.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# The actual Ceph Cluster CRD example can be found at the bottom of this example.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;#################################################################################&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;v1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Namespace&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rook-ceph&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nn"&gt;---&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;v1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ServiceAccount&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rook-ceph-osd&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rook-ceph&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nn"&gt;---&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;v1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ServiceAccount&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rook-ceph-mgr&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rook-ceph&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nn"&gt;---&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Role&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rbac.authorization.k8s.io/v1beta1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rook-ceph-osd&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rook-ceph&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="nt"&gt;apiGroups&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;configmaps&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;verbs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;get&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;list&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;watch&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;create&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;update&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;delete&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nn"&gt;---&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Aspects of ceph-mgr that require access to the system namespace&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Role&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rbac.authorization.k8s.io/v1beta1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rook-ceph-mgr-system&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rook-ceph&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="nt"&gt;apiGroups&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;configmaps&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;verbs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;get&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;list&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;watch&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nn"&gt;---&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Aspects of ceph-mgr that operate within the cluster&amp;#39;s namespace&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Role&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rbac.authorization.k8s.io/v1beta1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rook-ceph-mgr&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rook-ceph&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="nt"&gt;apiGroups&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;pods&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;services&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;verbs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;get&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;list&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;watch&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="nt"&gt;apiGroups&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;batch&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;jobs&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;verbs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;get&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;list&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;watch&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;create&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;update&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;delete&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="nt"&gt;apiGroups&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;ceph.rook.io&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="s2"&gt;&amp;#34;*&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;verbs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="s2"&gt;&amp;#34;*&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nn"&gt;---&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Allow the operator to create resources in this cluster&amp;#39;s namespace&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;RoleBinding&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rbac.authorization.k8s.io/v1beta1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rook-ceph-cluster-mgmt&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rook-ceph&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;roleRef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;apiGroup&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rbac.authorization.k8s.io&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ClusterRole&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rook-ceph-cluster-mgmt&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;subjects&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ServiceAccount&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rook-ceph-system&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rook-ceph-system&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nn"&gt;---&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Allow the osd pods in this namespace to work with configmaps&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;RoleBinding&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rbac.authorization.k8s.io/v1beta1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rook-ceph-osd&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rook-ceph&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;roleRef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;apiGroup&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rbac.authorization.k8s.io&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Role&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rook-ceph-osd&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;subjects&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ServiceAccount&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rook-ceph-osd&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rook-ceph&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nn"&gt;---&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Allow the ceph mgr to access the cluster-specific resources necessary for the mgr modules&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;RoleBinding&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rbac.authorization.k8s.io/v1beta1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rook-ceph-mgr&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rook-ceph&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;roleRef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;apiGroup&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rbac.authorization.k8s.io&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Role&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rook-ceph-mgr&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;subjects&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ServiceAccount&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rook-ceph-mgr&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rook-ceph&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nn"&gt;---&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Allow the ceph mgr to access the rook system resources necessary for the mgr modules&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;RoleBinding&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rbac.authorization.k8s.io/v1beta1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rook-ceph-mgr-system&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rook-ceph-system&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;roleRef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;apiGroup&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rbac.authorization.k8s.io&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Role&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rook-ceph-mgr-system&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;subjects&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ServiceAccount&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rook-ceph-mgr&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rook-ceph&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nn"&gt;---&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# Allow the ceph mgr to access cluster-wide resources necessary for the mgr modules&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;RoleBinding&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rbac.authorization.k8s.io/v1beta1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rook-ceph-mgr-cluster&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rook-ceph&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;roleRef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;apiGroup&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rbac.authorization.k8s.io&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ClusterRole&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rook-ceph-mgr-cluster&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;subjects&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ServiceAccount&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rook-ceph-mgr&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rook-ceph&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nn"&gt;---&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;#################################################################################&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# The Ceph Cluster CRD example&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;#################################################################################&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ceph.rook.io/v1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;CephCluster&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rook-ceph&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;rook-ceph&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;cephVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# For the latest ceph images, see https://hub.docker.com/r/ceph/ceph/tags&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ceph/ceph:v13.2.2-20181023&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;dataDirHostPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;/var/lib/rook&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;mon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;allowMultiplePerNode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;dashboard&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;useAllNodes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;useAllDevices&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;databaseSizeMB&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;1024&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;journalSizeMB&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;1024&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个 yaml 列表包含了如下的 Resource:&lt;/p&gt;</description></item><item><title>在 Gitlab 中使用 Danger</title><link>https://hangyan.github.io/post/2019-05-07-gitlab-danger/</link><pubDate>Tue, 07 May 2019 23:23:52 +0000</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2019-05-07-gitlab-danger/</guid><description>&lt;p&gt;Gitlab 社区版的 CI 功能非常好用，能够很方便的地做到代码的 lint/build/test/等等。不过社区版在多人协作上(比如 Merge Request)上阉割了不少功能，
比如将 MR assign 给多人等。通常来说，在代码合并这块，CI/CD 一般包括两部分: 代码本身以及 MR/PR 本身。Danger 这个工具正好可以补足 Gitlab 在后者的不足。&lt;/p&gt;
&lt;h2 id="功能"&gt;功能&lt;/h2&gt;
&lt;p&gt;Gitlab CI 的关注点在于提交的代码本身，而 Danger 的关注点在于 Merge Request 本身，当然也可以做到很多 Gitlab CI 能做到的事情，各种第三方插件也能极大地扩种 Danger 自身的能力。目前我觉得几个非常有用的功能是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;检查 Commit Message 的格式。这个功能是很基本的，但是很多 CI 系统本身都不支持。&lt;/li&gt;
&lt;li&gt;检查与 jira 的关联。强制让每一个 MR 都关联一个 jira,方便项目管理&lt;/li&gt;
&lt;li&gt;检查 MR 是否打标签。在 MR 非常多的时候用于给 MR 归类，在 Github 上的大项目上我们经常见到&lt;/li&gt;
&lt;li&gt;检查 MR 的大小。改动太大的 MR 是不推荐的，因为 Review 起来难度太大，推荐分裂成比较小的 MR&lt;/li&gt;
&lt;li&gt;检查是否 rebase 过了目标分支,保持一个干净的提交记录。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="安装"&gt;安装&lt;/h2&gt;
&lt;p&gt;因为 MR 本身属于 Git 系统的一个功能，所以 Danger 的一个主要能力在于与各大平台的集成性上,目前主流的 Gitlab/Github/都支持。也很容易部署。下面简要介绍与 Gitlab 的集成方法&lt;/p&gt;</description></item><item><title>少有人用的 debugger</title><link>https://hangyan.github.io/post/2019-05-06-debugger/</link><pubDate>Mon, 06 May 2019 11:32:22 +0000</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2019-05-06-debugger/</guid><description>&lt;p&gt;不知道别人是怎么认为的，我之前一直认为，Debugger 应该是一个正统的解决软件问题的方式。我们应该尽力地去多用这种方式，通过各种其他的 print 等方式是一种原始的，偷懒的，笨拙的方法。&lt;/p&gt;
&lt;p&gt;我也总是去尝试学习各种 debugger 以及在工作中去使用它们，但都是很难进行下去。真实的工作场景中也鲜有人用 debugger。是大家都在逃避问题，投机取巧吗？&lt;/p&gt;
&lt;p&gt;今天看的几篇文章让我明白了，debugger 并不是什么正统，而是一种适用范围非常狭窄，应该尽量少用的方式。用 print 之类的&lt;code&gt;笨办法&lt;/code&gt;是应该的，值得推荐的。&lt;/p&gt;
&lt;p&gt;Linux 在邮件列表里表达的对于在内核开发中使用 debugger 的看法也很适用于通用软件的场景:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;It&amp;#39;s partly &amp;#34;source vs binary&amp;#34;, but it&amp;#39;s more than that. It&amp;#39;s not that you
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;have to look at the sources (of course you have to - and any good debugger
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;will make that _easy_). It&amp;#39;s that you have to look at the level _above_
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sources. At the meaning of things. Without a debugger, you basically have
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;to go the next step: understand what the program does. Not just that
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;particular line.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;相对来说，debugger 本身更像是对问题的逃避，我们放弃对于问题以及代码更为细致的观察与思索，直接交给程序来一行一行校验代码的正确性。而在没有 debugger 的场景下，我们需要花费更多的时间来思考整个程序的设计，自上而下地考量整个问题以及解决的方式对不对，最终的困惑可以通过 print/system tools 等工具结合起来去校验。通过这种方式，最终对问题的解决可能并不是对于现有某些代码的修改，而是对于代码整体的重构与调整。&lt;/p&gt;</description></item><item><title>jekyll 的问题</title><link>https://hangyan.github.io/post/2019-05-05-jekyll/</link><pubDate>Sun, 05 May 2019 13:14:29 +0000</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2019-05-05-jekyll/</guid><description>&lt;!-- toc --&gt;
&lt;p&gt;考虑良久，决定将 Blog 迁移到 hexo。整个过程比想象的简单很多，也看了下二者的异同。相对于来讲，我觉得 hexo 在设计上比 jekyll 更为优良一些，更适合作为一个静态博客生成器。
那么，jekyll 的问题在哪?&lt;/p&gt;
&lt;h2 id="目录结构"&gt;目录结构&lt;/h2&gt;
&lt;p&gt;jekyll 的目录结构是 posts/themes/settings 等杂糅在一起，初看起来更好理解，但使用起来是很痛苦的。尤其是在更换 theme 的时候，需要小心翼翼地甄别哪些目录需要更换而哪些不需要。而 hexo 的目录结构更加清晰，每个 themes 的内容都包含在一个单独的目录中，与其他部分互不干扰。想要更换 theme 直接 clone 一个新的改一个配置参数即可。&lt;/p&gt;
&lt;p&gt;更换 theme 的次数越多，jekyll 的目录结构会越来越乱，分不清哪些文件是当前必须的。而 hexo 没这个问题。&lt;/p&gt;
&lt;h2 id="默认主题"&gt;默认主题&lt;/h2&gt;
&lt;p&gt;jekyll 没有默认主题。或者说有，但大家不这样用。通常的用法都是找一个现成的第三方 blog clone 下来，然后按自己的需求改。这样有个问题就是，总会有一种不适配的感觉。因为主题都是别人为自己的使用配置的。而 hexo 提供了默认的主题，它是这个软件自带的一部分，是理所应当的&lt;code&gt;属于&lt;/code&gt;我们自身的。而且这个默认的主题已经足够好用，同时第三方的&lt;code&gt;next&lt;/code&gt;主题也占据了相当大的份额。不是说选择少，而是在 hexo 上能够更快地完成&lt;code&gt;theme&lt;/code&gt;配置这一环而专注于内容。&lt;/p&gt;
&lt;h2 id="功能"&gt;功能&lt;/h2&gt;
&lt;p&gt;jekyll 各个 theme 的功能丰富程度差别很大，分页、评论、TAG 等等。不同主题想要添加新功能的方式不同，遇到问题处理的简易程度也不一样。而这种情况也往往导致用户会选择更换主题，然后继续恶性循环。hexo 的默认主题提供了对大多数常用功能的支持，而扩充起来也比较方便，不同主题在这块的差别也比较小。&lt;/p&gt;
&lt;h2 id="内容发布"&gt;内容发布&lt;/h2&gt;
&lt;p&gt;这块 jekyll 完全没做，必须得自己手动创建 md，添加头部的 attribute,之前还借鉴了其他人的 shell 脚本来根据模板自动生成这些内容。而 hexo 提供了&lt;code&gt;new&lt;/code&gt;命令来创建新的 post,自动补上头部的内容，这样学习成本基本上就只剩 markdown 了。&lt;/p&gt;
&lt;p&gt;目前静态博客软件的内容发布还是太偏程序员化了，可以参考 wordpress 的地方还有很多。目前已经有一些 admin 页面的项目流行起来，但还没有达到能让普通用户无学习成本使用的程度。&lt;/p&gt;
&lt;h2 id="其他"&gt;其他&lt;/h2&gt;
&lt;p&gt;目前静态博客的分化是好事，但有点像 linux distros 那样，最终也带来了很多恶性循环。大家不关注易用性去普及用户，而更关注彼此设计理念的不同。一堆程序员圈地自嗨，最终可能只是落得自娱自乐罢了。&lt;/p&gt;</description></item><item><title>如何防止代码变成 SHIT</title><link>https://hangyan.github.io/post/2019-05-01-great-code/</link><pubDate>Wed, 01 May 2019 19:03:04 +0000</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2019-05-01-great-code/</guid><description>&lt;!-- toc --&gt;
&lt;p&gt;最近发现之前写过的很多项目，不管是 python/go/cpp 的，最终回看起来代码都变的很难看，尤其是多人合作的时候。
这是一个很头疼的问题。自己平时很在意这个，但是代码最终确都变成了 shit。&lt;/p&gt;
&lt;h2 id="问题"&gt;问题&lt;/h2&gt;
&lt;p&gt;当一个人开发的时候，问题主要在于一个人的自律性不够强，不管是代码注释等都是随意写的，即使偶尔意识到了问题，但也难以保证能一直坚持下去。
而当人多的时候，问题经常处在多人风格的不同上。如果没有&lt;code&gt;工具&lt;/code&gt;来强制大家遵守一定的规则，那么大家每个人自己的风格放在一起的时候，就成了垃圾代码。
避免代码变成 shit 的唯一方法，就是让大家写的代码&lt;code&gt;看起来都一个样&lt;/code&gt;。而这个工作除了由开发人员的 Code Review 之外，还需要工具来约束。&lt;/p&gt;
&lt;h2 id="解决方法"&gt;解决方法&lt;/h2&gt;
&lt;h3 id="cicd"&gt;CI/CD&lt;/h3&gt;
&lt;p&gt;最开始接触 CI/CD，我只是认为它主要是用于部署方面的一个工具。现在发现，它对于代码质量的改进也是意义巨大的。所有代码通用规范上的约束，静态分析，复杂度分析等都可以通过自动化工具来执行。
一方面，我们可以组合尽可能多的代码静态分析工具，来约束提交代码的错误，提供代码改进的建议，形成统一的代码风格。另一方面，最于 Code Review 来说，不再需要关注基本的代码风格以及基础部分，而只用关注于具体的业务以及代码架构层面的问题。&lt;/p&gt;
&lt;h3 id="静态分析"&gt;静态分析&lt;/h3&gt;
&lt;p&gt;Python 这样的代码，在多人合作的大型项目上，如果没有非常严格的代码约束，最终出来的项目时非常难以维护的。可能经常部署出来的代码连跑都跑不起来。相对来说，静态类型语言在这方面的优势是巨大的，编译器在编译代码阶段能发现大多数比较明显的问题，最终线上的问题一般都是需要人来参与分析的。&lt;/p&gt;
&lt;p&gt;代码的静态分析是一个非常古老但是重视程度不够的技术。静态类型语言的代码分析技术能够极大地减少代码中可能存在的 bug,并给出很多的建议。以 Golang 为例，目前已经有几十款静态分析工具。
而像&lt;a href="https://github.com/golangci/golangci-lint"&gt;golangci-lint&lt;/a&gt;这样的工具，可以将多种分析工具结合起来，在各个层面给出代码改进的建议。比如：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;代码的简化写法&lt;/li&gt;
&lt;li&gt;可能的 bug&lt;/li&gt;
&lt;li&gt;Dead Code&lt;/li&gt;
&lt;li&gt;未使用的或者被覆盖的变量&lt;/li&gt;
&lt;li&gt;&amp;hellip;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;SonarQube 其实是类似的产品，只不过是 Client/Server 的架构，提供了诸如复杂度分析，单元测试覆盖率等指标。良好的 UI，以及插件式的架构，与普通的 cli 工具结合起来之后，对于代码质量的提高是非常有益的。&lt;/p&gt;
&lt;p&gt;总之，工具能处理的事情越多，人的精力就更多的能去关注更上的层面和业务层面。&lt;/p&gt;
&lt;h3 id="code-review"&gt;Code Review&lt;/h3&gt;
&lt;p&gt;Code Review 虽然成为一个共识，但真正的效率确实难以保证的。莫名其妙的改动，形式化的 approve,现有工具的局限等等问题都造成了参与的人在这方面的低效率。所以总是需要有各种规范来指导人们如何进行 Code Review。一般来讲，我们可以将代码的改动拆分为三个问题&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Why? 为什么要做这个改动&lt;/li&gt;
&lt;li&gt;How? 怎么做这个改动&lt;/li&gt;
&lt;li&gt;What? 具体做了哪些改动。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这几个问题，工具能参与的只是&lt;code&gt;What&lt;/code&gt;的一部分。其他的部分都需要人来 review。一般的 Code Review 提交的模板也都要求提交 PR 的人尽量说清楚 Why,通过文档 link 或者文字等形式。具体的 How 以及 What，则需要具体的人来参与 review。&lt;/p&gt;</description></item><item><title>JFFS3 文件系统</title><link>https://hangyan.github.io/post/2019-04-20-jffs3/</link><pubDate>Sat, 20 Apr 2019 22:35:17 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2019-04-20-jffs3/</guid><description>&lt;!-- toc --&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2019-04-20-jffs3/#%e9%97%aa%e5%ad%98%e5%af%b9%e6%96%87%e4%bb%b6%e7%b3%bb%e7%bb%9f%e7%9a%84%e5%bd%b1%e5%93%8d"&gt;闪存对文件系统的影响&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2019-04-20-jffs3/#%e9%97%aa%e5%ad%98%e8%bd%ac%e6%8d%a2%e5%b1%82"&gt;闪存转换层&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2019-04-20-jffs3/#jffs3"&gt;JFFS3&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2019-04-20-jffs3/#jffs2-%e7%9a%84%e9%97%ae%e9%a2%98"&gt;JFFS2 的问题&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2019-04-20-jffs3/#index-%e7%9a%84%e5%ad%98%e5%82%a8"&gt;Index 的存储&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2019-04-20-jffs3/#the-journal"&gt;The Journal&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2019-04-20-jffs3/#garbage-collection"&gt;Garbage collection&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2019-04-20-jffs3/#superblock"&gt;Superblock&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2019-04-20-jffs3/#links"&gt;Links&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;!-- tocstop --&gt;
&lt;p&gt;看完了&lt;a href="https://hangyan.github.io/post/2019-04-18-flash/"&gt;闪存&lt;/a&gt;,继续看文件系统。网上搜了一圈，发现都是讲 Andriod 的目录结构的，讲文件系统本身的少。目前能确认的是历史上曾经用过 ext4,yaffs,yaffs2 等。
估计目前主流的应该是 ext4。不过在搜索的时候先发现了一篇讲 jffs3 的，它在嵌入式系统上应用比较广泛，也是针对 Flash 存储特定设计的文件系统。不太确定现在的使用范围，但是拿出来研究下还是不错的。&lt;/p&gt;
&lt;h2 id="闪存对文件系统的影响"&gt;闪存对文件系统的影响&lt;/h2&gt;
&lt;p&gt;闪存跟磁盘有很多不同的地方，在设计文件系统的时候，二者有很多不同的考量。简单的对比表格如下&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style="text-align: center"&gt;&lt;/th&gt;
&lt;th style="text-align: center"&gt;闪存&lt;/th&gt;
&lt;th style="text-align: center"&gt;磁盘&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;最小寻址单位(读)&lt;/td&gt;
&lt;td style="text-align: center"&gt;字节&lt;/td&gt;
&lt;td style="text-align: center"&gt;扇区&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;最小寻址单位(写)&lt;/td&gt;
&lt;td style="text-align: center"&gt;NOR FLASH 是字节，NAND FLASH 是页，擦除是块&lt;/td&gt;
&lt;td style="text-align: center"&gt;扇区&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;寿命&lt;/td&gt;
&lt;td style="text-align: center"&gt;由擦写块的最大可擦写次数&lt;/td&gt;
&lt;td style="text-align: center"&gt;机械故障&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;闪存的这些特性，导致我们在设计文件系统的时候，必须考虑以下的问题：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;out of place update&lt;/code&gt;: 对于磁盘来讲，我们更新数据可以直接原地更新。但是闪存不行，因为无法将 bit 位从 0 -&amp;gt; 1。所以对于闪存的数据更新只能是在另外一个地方写入数据，然后将原数据标记为 dirty,再通过 GC 来定期回收.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;wear leveling&lt;/code&gt;: 磨损平衡。闪存的使用寿命是由擦写块的最大可擦写次数来决定的。超过了最大可擦写次数，这个擦写块就成为坏块(bad block)了。因此为了避免某个擦写块被过度擦写，以至于它先于其他的擦写块达到最大可擦写次数，我们应该在尽量小的影响性能的前提下，使擦写操作均匀的分布在每个擦写块上&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="闪存转换层"&gt;闪存转换层&lt;/h3&gt;
&lt;p&gt;为了能让普通的文件系统(磁盘上的)能够在闪存上正常运行，需要有一个转换层：Flash Translation Layer（FTL）。它的功能就是将底层的闪存模拟成一个具有 512 字节扇区大小的标准块设备(block device)。对于文件系统来说，就像工作在一个普通的块设备上一样，没有任何的差别。&lt;/p&gt;</description></item><item><title>闪存</title><link>https://hangyan.github.io/post/2019-04-18-flash/</link><pubDate>Thu, 18 Apr 2019 21:01:13 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2019-04-18-flash/</guid><description>&lt;!-- toc --&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2019-04-18-flash/#%e5%9f%ba%e6%9c%ac%e5%ae%9a%e4%b9%89"&gt;基本定义&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2019-04-18-flash/#nor--nand"&gt;NOR / NAND&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2019-04-18-flash/#%e5%86%99%e6%93%a6%e9%99%a4"&gt;写/擦除&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2019-04-18-flash/#emmc--ufs"&gt;eMMC / UFS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2019-04-18-flash/#links"&gt;Links&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;!-- tocstop --&gt;
&lt;p&gt;本来看华为新弄了 FS 和编译器，想学习下。但是目前似乎放出来的技术资料并不多，就先做下技术储备。先研究下他们做这个优化的历史背景。一路看下来发现链路太长，EXT4、更早的文件系统、最后到了闪存。
大学课堂上学过，但到现在真要考我，估计也说不出个所以然，所以先记录下闪存相关的笔记。&lt;/p&gt;
&lt;h2 id="基本定义"&gt;基本定义&lt;/h2&gt;
&lt;p&gt;ROM 的一种，虽然看着是&lt;code&gt;read only&lt;/code&gt;，但是闪存属于的细分类别已经可以允许重写数据(EEPROM)。比较接近的两种细分类别对比来看：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;EPROM: 需要用紫外线照射才能重写数据(注定要被淘汰的技术)&lt;/li&gt;
&lt;li&gt;EEPROM: 多的一个&lt;code&gt;E&lt;/code&gt;便是&lt;code&gt;Electrically&lt;/code&gt;，可以用电擦除数据&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;自然 EEPROM 的应用更加广泛。目前手机上的存储主要就是闪存（EEPROM)。闪存用于手机等移动设备的一些原因如下:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;动态抗震性好(没有机械部件)&lt;/li&gt;
&lt;li&gt;极端环境下也比较可靠(手机三防)&lt;/li&gt;
&lt;li&gt;在擦除数据时比一般的 EEPROM 效率更高(区块对字节)&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="nor--nand"&gt;NOR / NAND&lt;/h2&gt;
&lt;p&gt;两种 flash 的类型，直接上一个比较简单的表格对比：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style="text-align: center"&gt;&lt;/th&gt;
&lt;th style="text-align: center"&gt;NOR&lt;/th&gt;
&lt;th style="text-align: center"&gt;NAND&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;抹写速度&lt;/td&gt;
&lt;td style="text-align: center"&gt;慢&lt;/td&gt;
&lt;td style="text-align: center"&gt;快&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;抹写次数&lt;/td&gt;
&lt;td style="text-align: center"&gt;低&lt;/td&gt;
&lt;td style="text-align: center"&gt;高 (10x)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;存取方式&lt;/td&gt;
&lt;td style="text-align: center"&gt;随机&lt;/td&gt;
&lt;td style="text-align: center"&gt;区块&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;成本&lt;/td&gt;
&lt;td style="text-align: center"&gt;高&lt;/td&gt;
&lt;td style="text-align: center"&gt;低&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;面积&lt;/td&gt;
&lt;td style="text-align: center"&gt;大&lt;/td&gt;
&lt;td style="text-align: center"&gt;小&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;适用场景&lt;/td&gt;
&lt;td style="text-align: center"&gt;微处理器&lt;/td&gt;
&lt;td style="text-align: center"&gt;普通存储&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;实际使用&lt;/td&gt;
&lt;td style="text-align: center"&gt;BIOS/机顶盒&lt;/td&gt;
&lt;td style="text-align: center"&gt;U 盘/SSD&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="写擦除"&gt;写/擦除&lt;/h2&gt;
&lt;p&gt;这也是一个容易忽略但是细究起来很容易让人迷惑的东西。为什么闪存有擦除的概念?
简单来讲，由于闪存特殊的物理构造，可以理解为&lt;code&gt;写&lt;/code&gt;操作只能写入&lt;code&gt;0&lt;/code&gt;,擦除操作相当于写入&lt;code&gt;1&lt;/code&gt;。&lt;a href="https://zhuanlan.zhihu.com/p/35115499"&gt;手机闪存和固态硬盘为什么擦除多了会损坏？&lt;/a&gt;这篇文章提供了具体的电路图示意：&lt;/p&gt;</description></item><item><title>视频测试</title><link>https://hangyan.github.io/post/2019-04-15-video-test/</link><pubDate>Mon, 15 Apr 2019 10:44:44 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2019-04-15-video-test/</guid><description>&lt;iframe width="420" height="315" src="//player.bilibili.com/player.html?aid=39998869&amp;cid=70251492&amp;page=1" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true"&gt; &lt;/iframe&gt;</description></item><item><title>Google 的经验</title><link>https://hangyan.github.io/post/2019-04-14-google/</link><pubDate>Sun, 14 Apr 2019 11:30:07 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2019-04-14-google/</guid><description>&lt;p&gt;看了一篇讲谷歌公司内部的软件管理的&lt;a href="https://Arxiv.Org/Abs/1702.01715"&gt;论文&lt;/a&gt;，虽然对很多东西已经很熟悉了，但还是觉得有了一些新的启发。&lt;/p&gt;
&lt;p&gt;业内对 Google 其实大多数都是一种追随者的态度。单说最近这些年的几大热门产业，大数据由 Google 的三篇论文起始，云计算由 Kubernetes 引领热潮，
Alphago 在 AI 领域让人震撼。Google 比其他公司更早面临了很多技术发展的瓶颈之处，解决之后在某个时机将部分成果开源出来共享给业内。技术问题大家很容易采纳学习，但能解决这些问题的软件工程模式，
则不是那么容易学习的。&lt;/p&gt;
&lt;h2 id="single-repo"&gt;Single Repo&lt;/h2&gt;
&lt;p&gt;Google 在公开这种模式之后，大部分人的态度是震惊的。因为这跟大部分人的使用模式差别很大。不管是大公司小公司，不同功能的 Repo 对应不同的功能，非常便于维护，便于划清职责。Google 的单一 Repo 模式，尤其在其规模之上，初看起来是非常难以理解的。上 Tb 的仓库数据，每天上万次的 Commit,它是如何管理的呢?&lt;/p&gt;
&lt;p&gt;问题的答案自然是一套更为复杂的 CI/CD 系统，每一次的提交，自动化的构建、自动化的测试、自动化的 Review Check 等等，有了这些之后，代码的基本质量就有了保证，其他的就靠更为严格，精确的 Code Review 流程了。之所以大部分公司无法这样做，因为基础的 CI/CD 系统从来都不是一个公司在成长过程中优先考虑的问题，功能 -&amp;gt; 性能 -&amp;gt; Bug, 这些问题解决的差不多了，才有考虑其他的空闲。而往往此时，开发模式已经固定下来，没有了往 Single Repo 迁移的动力。&lt;/p&gt;
&lt;p&gt;另一方面，Single Repo 带来的优势初看起来也是比较模糊的。它更多的不是一个技术上的问题，而是一个管理模式,或者企业文化上的问题。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;是否能允许员工查看几乎所有的源码 Repo&lt;/li&gt;
&lt;li&gt;如何在 Single Repo 中划分各个项目的职责边界&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;对 Google 来讲，去掉这个边界是必须的。他希望员工能够熟悉整个系统，不受限于自己所主要维护的&lt;code&gt;组件&lt;/code&gt;的束缚，保持一个开放的心态。每个人都有创造的天性，当他发现有一个&lt;code&gt;其他项目&lt;/code&gt;的问题或者优化他很想修复，或者有更好的解决方式，没有了项目的边界，他很自然地认为可以这样做也应该这样做。这种开放的模式保证了 Google 的所有项目能受惠于公司所有人的才智,也造就了 Google 在技术和商业上的成功。&lt;/p&gt;
&lt;h2 id="personal-time"&gt;Personal Time&lt;/h2&gt;
&lt;p&gt;这也是人人熟知，但几乎没有任何经常自称有 Google 背景的管理人员愿意尝试的模式。给员工留一些时间，让他做自己想做他任何想做的事。对于 Google 来说，有很多项目和改善都是来自于员工在这个时间的 Side Project。&lt;/p&gt;</description></item><item><title>Github 与编程语言</title><link>https://hangyan.github.io/post/2019-04-12-github-language/</link><pubDate>Fri, 12 Apr 2019 15:22:54 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2019-04-12-github-language/</guid><description>&lt;p&gt;一个语言的流行程度与多种因素相关,语法的简易性，package 的数量，性能，适用的场景等等。Perl 因为语法的&lt;code&gt;怪异&lt;/code&gt;而逐渐无人问津，&lt;code&gt;ROR&lt;/code&gt;因性能等问题也用的人越来越少。
一向处于主流地位的 C/CPP 在这几年也比不上 JavaScript 以及 Golang 的热度。在 Github 上，我们可以看到热门的项目中，JS 以及 Golang 等热门语言占据了相当瞩目的位置。
语言之间的较量从来就不是在单纯的语言层面本身，而是整个生态系统的对比。而不知不觉间，Github 也成了每个语言生态体系的一部分。&lt;/p&gt;
&lt;p&gt;当代的主流程序员中，大多数都有 C/CPP 的语言背景。但在大部分的工作选择上，python 与 Golang 的数量越来越多，也越来越占主流。究其原因，一方面是在语法层面上，采用了与 C 相似而更简单的表达方式。但更重要的是，从一开始，他们就考虑到了包管理系统的重要性，因为它是语言生态系统的&lt;code&gt;发动机&lt;/code&gt;，而 C/CPP 等语言到现在仍未有一个内建的实现。&lt;/p&gt;
&lt;p&gt;对 Python 而言，在 Github 流行之前，pip 已经非常成熟。这让他成为了一个在任何需求面前都是被优先选择的语言之一。http 请求、web 框架、爬虫、图片解析等等，只要你想要的任何功能，都可以在 pip 里找到已经有的实现。有了 Github 之后，python 的 package 的数量的扩充进一步加快。代码在 github,二进制包在 pip,文档在 readthedocs,这是一个非常完善而让人信任的体系。&lt;/p&gt;
&lt;p&gt;对于 Golang 来说，它就是生于 Github,流行于 Github 的。从一开始 docker/kubernetes 项目的流行，以及整个容器生态的成熟，给了 golang 一个良好的开端。go 内置的对于 github 上代码仓库的支持，即使有很多小问题，但总体来讲，这让 golang 以极快的速度建立起来了超越于 c/cpp 甚至能赶上 python/pip 的生态体系。在常规的开发场景以及业务需求上，golang 以及没有短板了。&lt;/p&gt;
&lt;p&gt;而反观 c/cpp，在 github 之前，假设我们想要去找一个 package，只能借助于搜索引擎。也许在某些奇怪的网站上，能找到一个实现方案。但是相比于 golang/github 模式，这样的方式有明显的缺陷:&lt;/p&gt;</description></item><item><title>The useless web</title><link>https://hangyan.github.io/post/2019-04-10-the-useless-web/</link><pubDate>Wed, 10 Apr 2019 18:41:54 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2019-04-10-the-useless-web/</guid><description>&lt;p&gt;早期互联网的美妙之处在于，它真的是&lt;code&gt;万维网&lt;/code&gt;,由无数的由个人或者公司搭建的互联网。没有如此大公司固定平台的限制，这些网站充满了无数的可能性。&lt;/p&gt;
&lt;p&gt;之前工作无聊时发现的一个&lt;a href="https://theuselessweb.com/"&gt;the useless web&lt;/a&gt;, 它给了我们一个&lt;code&gt;magit button&lt;/code&gt;,每次点击都可以打开一个新的神奇的&lt;code&gt;无聊&lt;/code&gt;网站：&lt;/p&gt;
&lt;p&gt;&lt;img alt="index" loading="lazy" src="https://hangyan.github.io/images/useless-web/index.png"&gt;&lt;/p&gt;
&lt;p&gt;有最神奇的可以随鼠标晃动的&lt;a href="http://www.staggeringbeauty.com/"&gt;东西&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img alt="http://www.staggeringbeauty.com/" loading="lazy" src="https://hangyan.github.io/images/useless-web/beauty.png"&gt;&lt;/p&gt;
&lt;p&gt;一个向下拉有无限长的腿的&lt;a href="http://endless.horse/"&gt;马&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img alt="http://endless.horse/" loading="lazy" src="https://hangyan.github.io/images/useless-web/horse.png"&gt;&lt;/p&gt;
&lt;p&gt;一个不知道什么的&lt;a href="http://metaphorsofinfinity.com/"&gt;隐喻&lt;/a&gt;：&lt;/p&gt;
&lt;p&gt;&lt;img alt="http://metaphorsofinfinity.com/" loading="lazy" src="https://hangyan.github.io/images/useless-web/meta.png"&gt;&lt;/p&gt;
&lt;p&gt;被偷了 bucket 的&lt;a href="http://ihasabucket.com/"&gt;海象&lt;/a&gt;&amp;hellip;&lt;/p&gt;
&lt;p&gt;&lt;img alt="http://ihasabucket.com/" loading="lazy" src="https://hangyan.github.io/images/useless-web/bucket.jpg"&gt;&lt;/p&gt;
&lt;p&gt;这是一个几乎无限长的例子。几乎也是自由互联网的意义所在。很像&lt;code&gt;&amp;lt;Rick and Morty&amp;gt;&lt;/code&gt;里他们看的宇宙电台一样，无穷无尽的稀奇古怪的电视节目。&lt;/p&gt;
&lt;p&gt;&lt;img alt="morty" loading="lazy" src="https://hangyan.github.io/images/useless-web/morty.jpg"&gt;&lt;/p&gt;
&lt;p&gt;无尽的无趣，比有限的伪装要好太多了。&lt;/p&gt;</description></item><item><title>Docker BuildKit 介绍</title><link>https://hangyan.github.io/post/2019-04-08-buildkit/</link><pubDate>Mon, 08 Apr 2019 16:22:48 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2019-04-08-buildkit/</guid><description>&lt;!-- toc --&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2019-04-08-buildkit/#%e4%b8%ba%e4%bb%80%e4%b9%88%e9%9c%80%e8%a6%81-buildkit"&gt;为什么需要 BuildKit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2019-04-08-buildkit/#buildkit-%e9%95%bf%e4%bb%80%e4%b9%88%e6%a0%b7%e5%ad%90"&gt;BuildKit 长什么样子&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2019-04-08-buildkit/#llb"&gt;LLB&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2019-04-08-buildkit/#%e6%9c%89%e7%94%a8%e7%9a%84%e6%96%b0%e5%8a%9f%e8%83%bd"&gt;有用的新功能&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2019-04-08-buildkit/#links"&gt;Links&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;!-- tocstop --&gt;
&lt;p&gt;对于 Docker 和 Kubernetes 来说，在自身发展的壮大过程中，都会经历一个因为功能不断增加导致的软件结构庞杂的问题。对于 Kubernetes 来说，出于架构上的考量，&lt;code&gt;kubectl&lt;/code&gt; 等项目的代码都会逐渐从主项目中移除。对于 Docker 来说，事情更为复杂，它既要考虑开源，又要考虑自己的商业化，所以有了 moby 以及 &lt;code&gt;*kit&lt;/code&gt; 等一系列项目。
下图清晰地展示出了 Docker 对于相关项目的一个架构规划:&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://images.techhive.com/images/article/2017/05/moby-project-100721192-large.jpg"&gt;&lt;/p&gt;
&lt;p&gt;总体来说，Docker 希望将容器技术与容器产品分离开，核心技术是开源的，可扩展的，这样允许有其他人来基于同样的技术来构建类似于 Docker CE/Docker EE 这样的产品。
BuildKit 自 2017 年年底便已经实现，但似乎关注度不是很高(在国内)，在 CI/CD 系统中的应用也不是很广泛。本文是关于 BuildKit 的一个相关介绍。&lt;/p&gt;
&lt;h2 id="为什么需要-buildkit"&gt;为什么需要 BuildKit&lt;/h2&gt;
&lt;p&gt;除了上面说的商业以及架构上的考量，本身在构建这一块，Dockerfile 以及相应的机制也面临着一些问题&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;并发程度不够好&lt;/p&gt;
&lt;p&gt;很多步骤都是可以并行执行的，比如两部构建中的两次 pull 镜像，以及各种 run 命令&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;对 cache 的支持不好&lt;/p&gt;
&lt;p&gt;像 golang 这样的语言，每次构建都要从头开始，没法复用缓存&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;对 secret 的支持不好&lt;/p&gt;
&lt;p&gt;难以支持在 dockerfile 中通过 username-password/ssl key 等方式获取一些加密信息&lt;/p&gt;</description></item><item><title>更安全的 DNS</title><link>https://hangyan.github.io/post/2019-04-04-more-secure-dns/</link><pubDate>Thu, 04 Apr 2019 20:56:03 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2019-04-04-more-secure-dns/</guid><description>&lt;!-- toc --&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2019-04-04-more-secure-dns/#%e4%b8%bb%e8%a6%81%e9%97%ae%e9%a2%98"&gt;主要问题&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2019-04-04-more-secure-dns/#%e8%a7%a3%e5%86%b3%e6%96%b9%e6%b3%95"&gt;解决方法&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2019-04-04-more-secure-dns/#%e4%bd%bf%e7%94%a8%e5%8f%97%e4%bf%a1%e4%bb%bb%e7%9a%84dns%e8%a7%a3%e6%9e%90%e6%9c%8d%e5%8a%a1%e5%99%a8"&gt;使用受信任的 DNS 解析服务器&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2019-04-04-more-secure-dns/#doh"&gt;DoH&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2019-04-04-more-secure-dns/#qname-minimization"&gt;QNAME minimization&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2019-04-04-more-secure-dns/#%e5%85%b6%e4%bb%96%e9%97%ae%e9%a2%98"&gt;其他问题&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2019-04-04-more-secure-dns/#links"&gt;Links&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;!-- tocstop --&gt;
&lt;p&gt;HTTPS 已经越来越普及了，包括 Github Pages 都可以很方便的集成。浏览器也开始通过各种手段来促进网站都尽量使用 HTTPS。相比来说，
DNS 的安全性受重视的程度就没那么高。Firefox 写了一个一篇非常好的介绍文章(见最后 Links)，系统性地介绍了当前的问题以及解决方案。在这里整理一下。&lt;/p&gt;
&lt;h2 id="主要问题"&gt;主要问题&lt;/h2&gt;
&lt;p&gt;DNS 协议主要交流的信息就两个：ip 和 domain, 这两个东西一般来说，很不起眼，但是漏洞(非安全协议)摆在这，总会有各种不法分子来作恶。目前我能想到的比较明显的两个就是&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;运营商劫持： 一个不靠谱的运营商，在各个网站插入各种恶心的广告, 重定向网址等&lt;/li&gt;
&lt;li&gt;不安全的公共网络: 比如公共场所的 wifi, 可以伪造各种网站，收集用户的上网记录等等&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;火狐也在文章中写出了 dns 查询过程中可能出现的安全漏洞：&lt;/p&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://hacks.mozilla.org/files/2018/05/03_04-768x383.png"&gt;&lt;/p&gt;
&lt;p&gt;主要有三个&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;不可靠的本地 dns 服务器&lt;/li&gt;
&lt;li&gt;中间人攻击&lt;/li&gt;
&lt;li&gt;一些根域名服务器能追踪你的解析记录。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="解决方法"&gt;解决方法&lt;/h2&gt;
&lt;p&gt;这几个问题，火狐都列出了解决方法。&lt;/p&gt;
&lt;h3 id="使用受信任的-dns-解析服务器"&gt;使用受信任的 DNS 解析服务器&lt;/h3&gt;
&lt;p&gt;一般用户，包括互联网从业者，都不太关心本地配置的 dns 服务器是什么,一般都是运营商默认的。一般人也没有精力(能力)在出问题的时候去根运营商协调解决。火狐的思路是在用户使用浏览器的时候（不干涉其他程序），选择火狐信任的 dns 服务器来进行 dns 查询（可配置为失败时 fallback 到机器默认的）。目前火狐是和 cloudflare 合作，由 cloudflare 来提供域名解析服务，未来应该会有更多的提供商。&lt;/p&gt;
&lt;p&gt;目前的 Firefox 66 版本已经支持了此选项（不一定是最早支持版本）,可以在 &lt;code&gt;about:config&lt;/code&gt; 里面搜索 &lt;code&gt;trr&lt;/code&gt; 查询到:&lt;/p&gt;</description></item><item><title>Emacs is drugs</title><link>https://hangyan.github.io/post/2019-03-28-emacs-is-drugs/</link><pubDate>Thu, 28 Mar 2019 21:06:19 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2019-03-28-emacs-is-drugs/</guid><description>&lt;blockquote&gt;
&lt;p&gt;每个程序员都会遇到这一天，他要做一个决定，vim 还是 emacs,还是 IDE。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;近来一直主要用 Golang, 早已不再年轻的我自然是选择功能强大的 Goland, 日常工作无压力。即使没有免费的注册码了，我也毫不犹豫地买了正版。只是，它太慢了，太卡了，16GB 内存的 Mac 跑起来都不轻松。&lt;/p&gt;
&lt;p&gt;在无数次的痛苦的等待中，我终于决定换一个 Editor 试一试。Atom 也慢，Code 看起来都很不错，插件多，界面舒服，速度也快，但我面对它总有一种无所适从的感觉。我偶尔会用它来做些零碎的工作，但从来没法把它当作
一个主要的开发工具。&lt;/p&gt;
&lt;p&gt;这几天偶然翻起来一直装在电脑里确很久没有打开的 Emacs, 才明白了 Code 的问题在哪。&lt;/p&gt;
&lt;p&gt;Code 是一个优秀的 Editor, 但也仅此而以。我们用它来写代码，代码和 Editor 是两个东西。我们要学习怎么去使用这个软件，这个学习的过程和写代码是两套系统。它像 PS,像 Word，是一个精巧的软件，是编程的产物。&lt;/p&gt;
&lt;p&gt;而 Emacs 不同，它是一个 Editor, 但又和任何的 Editor 不同。它即是一个精巧的软件，也是一个操作系统，也是编程本身。&lt;/p&gt;
&lt;p&gt;首先，它基于 Elisp 编写，是 lisp 到今天少有的面向大众的软件产品。他的所有扩展也都是 lisp 代码。lisp 良好的可读性能让人很容易理解一个扩展是怎么写出来的，你可以如何修改它，照着它写一个新的。你在用它的时候，能明确地感觉到你在写的代码和编辑器不是分离的两个东西。它不是一个服务于你的黑盒子，而是一个对你透明的软体。你可以随时打开它看看他的内在，修改它，调整它，让他的形态更适用于你的需求。在很多人使用 emacs 的时候，会经常发现，你打开的文件列表经常会有 lisp 代码。好像他们属于一个项目似的。&lt;/p&gt;
&lt;p&gt;elisp 主要的功能都是面向文本编辑的，所以它比普通的 editor 有更多的概念: 缓冲区/ring buffer/minibuf/mode-line 等等。这些概念加起来，造就了最为精准丰富的文本处理和表现系统。无数在其他 editor 无法实现或者很难实现的功能，在这里都可以很容地用代码写出来。是的，就是用 elisp 代码。最终，是 elisp 代码组成了 emacs, 而对其他的 editor 来说，组成他们的是基本功能 + 扩展。&lt;/p&gt;
&lt;p&gt;Editor/IDE 的分化，是因为用常规的软件设计思路，无法作出一个满足以下功能的开发工具:&lt;/p&gt;</description></item><item><title>istio</title><link>https://hangyan.github.io/post/2019-03-25-istio/</link><pubDate>Mon, 25 Mar 2019 19:16:44 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2019-03-25-istio/</guid><description>&lt;p&gt;整个容器化浪潮诞生了三个胜利者,分别是 docker, kubernetes, istio。
一个额外的胜利者是 golang。&lt;/p&gt;
&lt;h2 id="docker"&gt;Docker&lt;/h2&gt;
&lt;p&gt;docker 的作用是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;用标准语法描述出一个服务的静态/运行时环境，无关语言与操作系统&lt;/li&gt;
&lt;li&gt;资源隔离&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;被打死的是 lxc, 它有资源隔离的功能，但是缺点太明显：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;没有上面的 docker 提供的第一个功能&lt;/li&gt;
&lt;li&gt;架构不如 docker 的 cli/server 的简洁明了，易于学习&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;受威胁的是 VM&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;太慢了&lt;/li&gt;
&lt;li&gt;性能损耗大&lt;/li&gt;
&lt;li&gt;无法 API 化&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="kubernetes"&gt;Kubernetes&lt;/h2&gt;
&lt;p&gt;真正的 DCOS，核心优势:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;统一资源模型&lt;/li&gt;
&lt;li&gt;统一 API 模型 (引导业务 API 向其靠拢)&lt;/li&gt;
&lt;li&gt;Node 横向扩展&lt;/li&gt;
&lt;li&gt;插件化&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;被打死的是 Mesos/Marathon，缺点&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;两层架构的低效率 (仁慈独裁与低效民主)&lt;/li&gt;
&lt;li&gt;C++&lt;/li&gt;
&lt;li&gt;C++ 导致的难以 debug&lt;/li&gt;
&lt;li&gt;难以扩展以增加新功能&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;另一个被打死的是 swarm/compose, 缺点&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;架构不清晰&lt;/li&gt;
&lt;li&gt;自下而上地缓慢地堆功能&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;受威胁的是 YARN, 不如 kubernetes 通用性强&lt;/p&gt;
&lt;h2 id="istio"&gt;Istio&lt;/h2&gt;
&lt;p&gt;微服务相比于单体服务的一切问题都应该在这层解决, trace, 限流, metrics 等等&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;不需要修改业务代码&lt;/li&gt;
&lt;li&gt;比手工的实现更完善，功能更强&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;受影响的：微服务业务代码&lt;/p&gt;</description></item><item><title>Namecoin</title><link>https://hangyan.github.io/post/2017-08-22-namecoin/</link><pubDate>Tue, 22 Aug 2017 00:00:00 +0000</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2017-08-22-namecoin/</guid><description>&lt;!-- toc --&gt;
&lt;p&gt;互联网到今日已经越来越趋向于中心化，用户的隐私安全以及大公司的行为也日渐引起担忧。去中心化网络是一个应对此问题的重要思想，blockchian,bitcoin,tor 等都是一些具体的技术探索，很多已经有了越来越大的影响力。Namecoin 也是其中的一个。&lt;/p&gt;
&lt;p&gt;Namecoin 想要解决的是目前 DNS 存在的一些问题。中央机构控制的 DNS 很容易因为各种原因受到干扰，用户的利益和隐私容易受到侵犯。Namecoin 借助于 bitcoin 的思想，构建了一个去中心化的 DNS 系统。&lt;/p&gt;
&lt;h1 id="zookos-triangle"&gt;Zooko&amp;rsquo;s triangle&lt;/h1&gt;
&lt;p&gt;&lt;img loading="lazy" src="https://upload.wikimedia.org/wikipedia/commons/thumb/5/5c/Zooko%27s_Triangle.svg/440px-Zooko%27s_Triangle.svg.png"&gt;&lt;/p&gt;
&lt;p&gt;类似于分布式系统中的 CAP 理论。类似 DNS 这样的协议，我们期望它具有的三个特性很难满足: 安全,可读性好,去中心化。目前的最常用的 DNS 系统不满足去中心化。DNSSec 对现有的 DNS 做了一些安全性的增强，但仍然是中心化的。&lt;code&gt;.onion&lt;/code&gt;和&lt;code&gt;bitcoin&lt;/code&gt;是去中心化的和安全的，但是可读性很差等等。&lt;/p&gt;
&lt;p&gt;Namecoin 是满足上面要求的一个系统。（&lt;code&gt;Aaron Swartz&lt;/code&gt;曾经提出过这样的设计思路)&lt;/p&gt;
&lt;h1 id="namecoin"&gt;Namecoin&lt;/h1&gt;
&lt;p&gt;Namecoin 基于 BitCoin,本身就是在 Bitcoin 的代码上实现的(大概做了 400 多行改动)。挖矿的方式跟 bitcoin 一样，只是用的是不同的 block chain.二者的目的不同,bitcoin 主要还是为了成为一种货币系统，Namecoin 是为了成为一种命名系统。所以二者的关注点不同，blockchain 分开也是最合理的。二者有一些不同的规则：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;namecoin 要求名字的唯一性(域名)，而 bitcoin 不要求&lt;/li&gt;
&lt;li&gt;经济交易以及域名注册使用的货币大小不一样，block size 设的不一样更好&lt;/li&gt;
&lt;li&gt;通货膨胀对 bitcoin 的影响很大，namecoin 则不一样。&lt;/li&gt;
&lt;li&gt;namcoin 中，注册域名需要花钱,但这些货币会被销毁(0.01NMC).因为是去中心化的系统，没有收费的中央系统。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Namecoin 里的数据主要分为两种 domain name 和 NameID&lt;/p&gt;
&lt;h2 id="domain"&gt;Domain&lt;/h2&gt;
&lt;p&gt;Domain 的格式类似于:&lt;/p&gt;</description></item><item><title>Kubernetes 笔记</title><link>https://hangyan.github.io/post/2017-08-18-kubernetes-note/</link><pubDate>Fri, 18 Aug 2017 00:00:00 +0000</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2017-08-18-kubernetes-note/</guid><description>&lt;!-- toc --&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2017-08-18-kubernetes-note/#api"&gt;API&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2017-08-18-kubernetes-note/#%e5%a3%b0%e6%98%8e%e5%bc%8f%e7%9a%84-api"&gt;声明式的 API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2017-08-18-kubernetes-note/#api-response"&gt;API Response&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2017-08-18-kubernetes-note/#%e9%94%99%e8%af%af%e5%a4%84%e7%90%86"&gt;错误处理&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2017-08-18-kubernetes-note/#resource-version"&gt;Resource Version&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2017-08-18-kubernetes-note/#version"&gt;Version&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2017-08-18-kubernetes-note/#api-group"&gt;API Group&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2017-08-18-kubernetes-note/#runtime-config"&gt;Runtime config&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2017-08-18-kubernetes-note/#regex"&gt;REGEX&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2017-08-18-kubernetes-note/#%e5%ad%97%e6%ae%b5%e6%a0%bc%e5%bc%8f"&gt;字段格式&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2017-08-18-kubernetes-note/#patch-%e4%b8%8e-put"&gt;PATCH 与 PUT&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2017-08-18-kubernetes-note/#events"&gt;Events&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2017-08-18-kubernetes-note/#%e4%ba%a4%e4%ba%92"&gt;交互&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2017-08-18-kubernetes-note/#%e8%be%93%e5%87%ba"&gt;输出&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2017-08-18-kubernetes-note/#%e8%be%93%e5%85%a5"&gt;输入&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;!-- tocstop --&gt;
&lt;h1 id="api"&gt;API&lt;/h1&gt;
&lt;h2 id="声明式的-api"&gt;声明式的 API&lt;/h2&gt;
&lt;p&gt;声明式： 结果是什么
命令式: 做什么&lt;/p&gt;
&lt;p&gt;声明式的操作，相对于命令式操作，对于重复操作的效果是稳定的，这对于容易出现数据丢失或重复的分布式环境来说是很重要的。另外，声明式操作更容易被用户使用，可以使系统向用户隐藏实现的细节，隐藏实现的细节的同时，也就保留了系统未来持续优化的可能性&lt;/p&gt;
&lt;p&gt;kubernetes 里的 API 都是声明式,我们描述好自己想要的 resource object,kubernetes 就会不断尝试去保证这个 resource object 按我们期望的方式存在.&lt;/p&gt;
&lt;h2 id="api-response"&gt;API Response&lt;/h2&gt;
&lt;p&gt;一般包含三部分&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;metadata: 元数据
&lt;ul&gt;
&lt;li&gt;annotations: 一些元信息.给第工具用来存储和解析原信息用的.&lt;/li&gt;
&lt;li&gt;labels: act as filter&lt;/li&gt;
&lt;li&gt;namespace: resource 所处的 namespace&lt;/li&gt;
&lt;li&gt;name: resource 名字&lt;/li&gt;
&lt;li&gt;uuid: 唯一标识&lt;/li&gt;
&lt;li&gt;creationTimestamp: 创建时间&lt;/li&gt;
&lt;li&gt;deletionTimestamp: 计划删除的时间(graceful deletion)&lt;/li&gt;
&lt;li&gt;resourceVersion: 每个 resource 的内部版本,可以用来确定是否发生了变化.也用于做并发控制&lt;/li&gt;
&lt;li&gt;generation&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;spec: 具体描述,不同 resource 的属性不同。spec 里通过声明式的方式表明了期望的目标状态&lt;/li&gt;
&lt;li&gt;status: resource 的当前状态&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;下面以展示一下 kubernetes node api 的 metadata 作为样例:&lt;/p&gt;</description></item><item><title>Blockchain</title><link>https://hangyan.github.io/post/2017-08-12-blockchain/</link><pubDate>Sat, 12 Aug 2017 21:37:39 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2017-08-12-blockchain/</guid><description>&lt;!-- toc --&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2017-08-12-blockchain/#%e4%bb%8b%e7%bb%8d"&gt;介绍&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2017-08-12-blockchain/#%e5%8c%ba%e5%9d%97%e7%bb%93%e6%9e%84"&gt;区块结构&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2017-08-12-blockchain/#%e5%a4%b4%e7%bb%93%e6%9e%84"&gt;头结构&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2017-08-12-blockchain/#%e5%8c%ba%e5%9d%97%e7%bb%93%e6%9e%84-1"&gt;区块结构&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2017-08-12-blockchain/#%e5%88%9b%e4%b8%96%e5%8c%ba%e5%9d%97"&gt;创世区块&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2017-08-12-blockchain/#%e5%88%86%e5%8f%89"&gt;分叉&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2017-08-12-blockchain/#proof-of-work%e5%b7%a5%e4%bd%9c%e9%87%8f%e8%af%81%e6%98%8e"&gt;Proof-of-work(工作量证明)&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2017-08-12-blockchain/#%e9%82%ae%e4%bb%b6-header"&gt;邮件 Header&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2017-08-12-blockchain/#bitcoin"&gt;Bitcoin&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2017-08-12-blockchain/#ref"&gt;REF&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;!-- tocstop --&gt;
&lt;h1 id="介绍"&gt;介绍&lt;/h1&gt;
&lt;p&gt;区块链是由一串使用密码学方法产生的数据块组成的，每一个区块都包含了上一个区块的哈希值(hash),从创始区块(genesis block)开始连接到当前区块，形成块链(类似数据结构中的链表).每一个区块都确保按照时间顺序在上一个区块之后产生，否则前一个区块的哈希值是未知的。&lt;/p&gt;
&lt;p&gt;其优势在于:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;完全分布式的，无中心节点(单点故障)&lt;/li&gt;
&lt;li&gt;任何节点都可以创建交易。在经过一段时间的确认之后，就可以合理地确认该交易是否有效。&lt;/li&gt;
&lt;li&gt;修改交易记录的成本非常高&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;区块链也是比特币等技术的基础。它也是一种非常重要的新思想，促使人们对现有的网络,社会,经济等进行重新思考。&lt;/p&gt;
&lt;h1 id="区块结构"&gt;区块结构&lt;/h1&gt;
&lt;h2 id="头结构"&gt;头结构&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;大小(bytes)&lt;/th&gt;
&lt;th&gt;字段&lt;/th&gt;
&lt;th&gt;描述&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;版本&lt;/td&gt;
&lt;td&gt;版本号&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;32&lt;/td&gt;
&lt;td&gt;父区块哈希值&lt;/td&gt;
&lt;td&gt;引用区块链中父区块的哈希值&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;32&lt;/td&gt;
&lt;td&gt;merkle 根&lt;/td&gt;
&lt;td&gt;该区块交易的 merkle 树根的哈希值&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;时间戳&lt;/td&gt;
&lt;td&gt;该区块产生的近似时间(精确到秒的 unix 时间戳)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;难度目标&lt;/td&gt;
&lt;td&gt;该区块工作量证明算法的难度目标&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Nonce&lt;/td&gt;
&lt;td&gt;用于工作量证明算法的计数器&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="区块结构-1"&gt;区块结构&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;大小(bytes)&lt;/th&gt;
&lt;th&gt;字段&lt;/th&gt;
&lt;th&gt;描述&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;区块大小&lt;/td&gt;
&lt;td&gt;该字段之后的区块大小&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;80&lt;/td&gt;
&lt;td&gt;区块头&lt;/td&gt;
&lt;td&gt;区块头结构 (上面的大小之和)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1-9&lt;/td&gt;
&lt;td&gt;可变整交易计数器&lt;/td&gt;
&lt;td&gt;交易的数量&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;可变&lt;/td&gt;
&lt;td&gt;交易&lt;/td&gt;
&lt;td&gt;记录在区块里的交易信息&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="创世区块"&gt;创世区块&lt;/h2&gt;
&lt;p&gt;比特币区块链的第一个区块，创建于 2009 年，我们称之为创世区块。它是比特币区块链里所有区块的共同祖先，这意味着你从任一区块，循链向后回溯，最终都将到达创世区块。每一个节点都“知道”创世区块的哈希值、结构、被创建的时间和里面的一个交易。因此，每个节点都把该区块作为区块链的首区块，从而构建了一个安全的、可信的区块链的根。&lt;/p&gt;</description></item><item><title>Shard</title><link>https://hangyan.github.io/post/2017-08-11-sharding/</link><pubDate>Fri, 11 Aug 2017 21:37:39 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2017-08-11-sharding/</guid><description>&lt;!-- toc --&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2017-08-11-sharding/#%e4%bb%8b%e7%bb%8d"&gt;介绍&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2017-08-11-sharding/#%e5%88%86%e7%89%87%e7%ad%96%e7%95%a5"&gt;分片策略&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2017-08-11-sharding/#the-lookup-strategy"&gt;The Lookup strategy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2017-08-11-sharding/#the-range-strategy"&gt;The Range strategy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2017-08-11-sharding/#the-hash-strategy"&gt;The Hash strategy&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2017-08-11-sharding/#%e7%9b%b8%e5%85%b3%e6%8a%80%e6%9c%af"&gt;相关技术&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://hangyan.github.io/post/2017-08-11-sharding/#brin"&gt;BRIN&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;!-- tocstop --&gt;
&lt;h1 id="介绍"&gt;介绍&lt;/h1&gt;
&lt;p&gt;Shard 指对数据的水平切分,每一个切分的部分都可以叫做一个&lt;code&gt;shard&lt;/code&gt;,它们拥有相同的 schema,但却拥有不同的数据集。对数据库来说，是指对数据库的表按行进行切分(与按列的垂直切分对应)，不同的 shard 可能位于不同的数据库服务器或者物理机器上。它的优势体现在以下几点:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;表的大小减少,索引体积减小,提升查询性能(某些方面)&lt;/li&gt;
&lt;li&gt;如果数据本身有比较明显的分区(比如国家,地区等),那么做 shard 很容易并且查询很大程度上都能落在一个 shard 上。&lt;/li&gt;
&lt;li&gt;水平扩展性好，可以通过添加新节点来扩充&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;劣势有以下几点:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;查询需要跨多个 shard 时会增加 latency&lt;/li&gt;
&lt;li&gt;因为 shard 经常只能做到某一位维度。所以在这一维度的查询的性能可能提高，但其他维度的查询的性能则可能会下降。&lt;/li&gt;
&lt;li&gt;跨 shard 的数据一致性和可用性也更加复杂和难以保障&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;shard 本身的问题让他成为一个&lt;code&gt;迫不得已&lt;/code&gt;的选择。尽量在没有其他优化方式的情况下选择 shard.理想情况下，应该有底层框架来处理 shard 而让应用层做到对 shard 无感知，不然还不如不用。&lt;/p&gt;
&lt;h2 id="分片策略"&gt;分片策略&lt;/h2&gt;
&lt;p&gt;如果将数据集进行 shard,有很多策略可以选择，常见的有&lt;/p&gt;
&lt;h3 id="the-lookup-strategy"&gt;The Lookup strategy&lt;/h3&gt;
&lt;p&gt;用 shard key 做一个映射表，包含不同 shard key 的请求会转发到相应的 shard 上。这种情况下，不同 shard key 的数据可能会落在同一个 shard 上，但相同 shard key 的数据则一定在同一个 shard 上.shard 与物理地址的映射也不能是一对一的，可以用类似于 consistent hashing 里面的那种 virtual node 的方式，设置一些&lt;code&gt;virtual shard&lt;/code&gt;，几个 virtual shard 可以对应于同样的物理位置(reblancing 的时候对上层代码的影响很小)。&lt;/p&gt;</description></item><item><title>一点 python 的经验</title><link>https://hangyan.github.io/post/2015-11-10-python-experience/</link><pubDate>Tue, 10 Nov 2015 21:37:39 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2015-11-10-python-experience/</guid><description>可能已经过时了。</description></item><item><title>Scala 笔记: 函数和闭包</title><link>https://hangyan.github.io/post/2015-08-31-scala-notes-function-and-closures/</link><pubDate>Mon, 31 Aug 2015 21:37:39 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2015-08-31-scala-notes-function-and-closures/</guid><description>&lt;h2 id="local-functions"&gt;Local functions&lt;/h2&gt;
&lt;p&gt;在函数式语言里，函数是最基本的功能块。通常为了模块清晰起见，我们需要很多 &lt;code&gt;Help Function&lt;/code&gt;，但是这些辅助函数很容易有名字冲突，暴露给外部的时候也容易引起很多问题。
Java 的主要解决方式是通过&lt;code&gt;private method&lt;/code&gt;,scala 也支持这种。但是 scala 也提供了函数
式风格的解决方式: 在函数内部定义函数(&lt;code&gt;Local functions.&lt;/code&gt;),就像局部变量一样,其作用
域仅限于外部函数内部。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-scala" data-lang="scala"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;scala.io.Source&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;LongLines&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;processFile&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;processLine&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;: &amp;#34;&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;val&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="k"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Source&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fromFile&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="k"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getLines&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;processLine&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;内部函数的一个便利之处就是它可以直接访问外部函数的变量(参数)。&lt;/p&gt;
&lt;h2 id="first-class-functions"&gt;First-class functions&lt;/h2&gt;
&lt;p&gt;函数式语言与命令式语言之间最大的区别之一便是其数据和代码的一致性。在 C/JAVA 这样
的语言里,变量、类、函数、语句等时候严格区分的，但在 Lisp 这样的语言里，所有的表
达式都有值，都是数据，都可以当做变量传给函数。&lt;/p&gt;
&lt;p&gt;scala 里有&lt;code&gt;function literal&lt;/code&gt;和&lt;code&gt;function value&lt;/code&gt;两个概念，有点像是&lt;code&gt;class&lt;/code&gt;和
&lt;code&gt;object&lt;/code&gt;之间的区别，前者都是在代码层级上而言，后者是运行时的概念。例如:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-scala" data-lang="scala"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="k"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这是一个&lt;code&gt;fucntion literal&lt;/code&gt;。你可以把它赋给一个变量并且调用它:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;increase&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;increase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;increase&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;9999&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;increase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;简单的函数一行即可描述，多行的用&lt;code&gt;{}&lt;/code&gt;包起来即可。&lt;/p&gt;</description></item><item><title>Docker 1.7 介绍</title><link>https://hangyan.github.io/post/2015-07-12-docker-1.7/</link><pubDate>Sun, 12 Jul 2015 21:37:39 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2015-07-12-docker-1.7/</guid><description>docker 1.7 features</description></item><item><title>docker 容器内多进程的管理方案</title><link>https://hangyan.github.io/post/2015-05-08-container-process-monitor/</link><pubDate>Fri, 08 May 2015 21:37:39 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2015-05-08-container-process-monitor/</guid><description>&lt;p&gt;容器生来适合的是以单进程为主的独立的微服务架构，而很多传统的组件则是体积庞大，多个进程（组件）之间难以拆分到不同的容器中，所以在单个容器内部署多个组件便成了一种
暂时的折衷方案。这便引入了一个问题：如何在容器内管理多个进程？&lt;/p&gt;</description></item><item><title>docker 容器的一些概念辨析</title><link>https://hangyan.github.io/post/2015-04-16-some-concepts-of-docker-containers/</link><pubDate>Thu, 16 Apr 2015 21:37:39 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2015-04-16-some-concepts-of-docker-containers/</guid><description>&lt;p&gt;本篇的主要内容是为了澄清 docker 容器的一些容易混淆的概念，主要分两部分，
一是容器端口的&lt;code&gt;publish&lt;/code&gt;和&lt;code&gt;expose&lt;/code&gt;,二是 Dockerfile 中&lt;code&gt;ENTRYPOINT&lt;/code&gt;和&lt;code&gt;CMD&lt;/code&gt;的区分。&lt;/p&gt;</description></item><item><title>docker 源码分析(6) -- 镜像删除</title><link>https://hangyan.github.io/post/2015-04-03-docker-source-code-part6-image-delete/</link><pubDate>Fri, 03 Apr 2015 21:37:39 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2015-04-03-docker-source-code-part6-image-delete/</guid><description>&lt;p&gt;本篇的主要内容是关于如何删除镜像的.听起来是挺简单的一件事,但是&lt;code&gt;docker&lt;/code&gt;本身的删除
策略定义并不清除，而且在实际的使用过程中,似乎总是与我们预计的结果不符,比如磁盘空间并没有被释放.所以在此单列一篇,详细解析镜
像删除的过程.&lt;/p&gt;</description></item><item><title>docker 源码分析(5) -- 镜像拉取及存储</title><link>https://hangyan.github.io/post/2015-03-30-docker-source-code-part5-images/</link><pubDate>Mon, 30 Mar 2015 21:37:39 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2015-03-30-docker-source-code-part5-images/</guid><description>docker images pulling and store</description></item><item><title>docker 源码分析(4) -- 网络设置</title><link>https://hangyan.github.io/post/2015-02-16-docker-source-code-part4-network-settings/</link><pubDate>Mon, 16 Feb 2015 21:37:39 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2015-02-16-docker-source-code-part4-network-settings/</guid><description>docker network setttings</description></item><item><title>docker 源码分析(3) -- daemon 启动流程</title><link>https://hangyan.github.io/post/2015-02-05-docker-source-code-part3-daemon-start/</link><pubDate>Thu, 05 Feb 2015 21:37:39 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2015-02-05-docker-source-code-part3-daemon-start/</guid><description>&lt;p&gt;本篇的主要内容是关于&lt;code&gt;docker daemon&lt;/code&gt;的启动流程。其主要内容均包含在
&lt;code&gt;github.com/docker/docker/docker/daemon.go&lt;/code&gt;文件中的&lt;code&gt;mainDaemon&lt;/code&gt;函数中，本文即按
其执行流程分析源码。因为所涉源码较多，所以所涉部分多是点到为止，详细分析会在后续
分专篇讲述。&lt;/p&gt;</description></item><item><title>IOPS 介绍</title><link>https://hangyan.github.io/post/2015-02-04-iops/</link><pubDate>Wed, 04 Feb 2015 21:37:39 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2015-02-04-iops/</guid><description>An introduce to IOPS</description></item><item><title>docker 源码分析(2) -- 主程序及命令行参数解析</title><link>https://hangyan.github.io/post/2015-01-30-docker-source-code-part2-start-and-args/</link><pubDate>Fri, 30 Jan 2015 21:37:39 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2015-01-30-docker-source-code-part2-start-and-args/</guid><description>&lt;p&gt;研究一个大项目的源码，最好是从&lt;code&gt;main&lt;/code&gt;函数入口，一步一步与实际程序相结合，将实际代
码与相应相印证。所以本篇的主要内容就是 docker 主程序的启动流程以及命令行参数解析的
过程。&lt;/p&gt;</description></item><item><title>现代化的开发人员实用工具</title><link>https://hangyan.github.io/post/2015-01-30-modern-dev-tools/</link><pubDate>Fri, 30 Jan 2015 21:37:39 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2015-01-30-modern-dev-tools/</guid><description>&lt;p&gt;常年混迹于 linux，对命令行程序情有独钟，平时也喜欢搜集各种实用的小工具。&lt;code&gt;github&lt;/code&gt;流行以来，越来越多的新的实用的开
发工具开源出来，有的可以用来替代一些老的工具，有的则是全新的。本文整理一些实用的
工具，希望大家能在实际开发中用到。&lt;/p&gt;</description></item><item><title>docker 源码分析(1) -- 开发环境准备</title><link>https://hangyan.github.io/post/2015-01-29-docker-source-code-part1-dev-setup/</link><pubDate>Thu, 29 Jan 2015 21:37:39 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2015-01-29-docker-source-code-part1-dev-setup/</guid><description>&lt;p&gt;之前看过一遍&lt;code&gt;docker&lt;/code&gt;的代码，但比较粗略。第二遍准备详细过一遍并且用博客的方式将其
整理一下。目前为止&lt;code&gt;docker&lt;/code&gt;的稳定版本为&lt;code&gt;1.4.1&lt;/code&gt;,所以就以此版本的代码为基础进行阅读，分
析。&lt;/p&gt;</description></item><item><title>RAID 介绍</title><link>https://hangyan.github.io/post/2015-01-25-raid/</link><pubDate>Sun, 25 Jan 2015 21:37:39 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2015-01-25-raid/</guid><description>&lt;p&gt;RAID 是一个我们经常能见到的名词。但却因为很少能在实际环境中体验,所以很难对其原理
能有很清楚的认识和掌握。本文将对 RAID 技术进行介绍和总结，以期能尽量阐明其概念。&lt;/p&gt;
&lt;p&gt;RAID 全称为独立磁盘冗余阵列(Redundant Array of Independent Disks),基本思想就是把
多个相对便宜的硬盘组合起来，成为一个硬盘阵列组，使性能达到甚至超过一个价格昂贵、
容量巨大的硬盘。RAID 通常被用在服务器电脑上，使用完全相同的硬盘组成一个逻辑扇区,
因此操作系统只会把它当做一个硬盘。&lt;/p&gt;
&lt;p&gt;RAID 分为不同的等级，各个不同的等级均在数据可靠性及读写性能上做了不同的权衡。
在实际应用中，可以依据自己的实际需求选择不同的 RAID 方案。&lt;/p&gt;</description></item><item><title>bash 各配置文件浅析</title><link>https://hangyan.github.io/post/2015-01-23-bash-config-files/</link><pubDate>Fri, 23 Jan 2015 21:37:39 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2015-01-23-bash-config-files/</guid><description>&lt;p&gt;与 bash 相关的配置文件非常之多,用户目录下的&lt;code&gt;.bashrc&lt;/code&gt;,&lt;code&gt;.profile&lt;/code&gt;,&lt;code&gt;.bash_profile&lt;/code&gt;，
系统级的&lt;code&gt;/etc/profile&lt;/code&gt;等。我们也经常会发现，在某个文件里设置好了环境变量之后，并
不能总是能在使用 bash 时正确加载。下面将对这个问题进行深入剖析，以解除疑惑。&lt;/p&gt;</description></item><item><title>CoreOS 安装及配置</title><link>https://hangyan.github.io/post/2015-01-22-coreos-install/</link><pubDate>Thu, 22 Jan 2015 21:37:39 +0800</pubDate><author>hang.yan@hotmail.com (涯余)</author><guid>https://hangyan.github.io/post/2015-01-22-coreos-install/</guid><description>&lt;p&gt;本文所遵照的步骤是官网的 Installing to disk 方法,即刻录 ISO 镜像 -&amp;gt; 启动 Coreos Live CD -&amp;gt; 安装到硬盘的步骤，与一般的桌面 Linux 安装非常类似。但 coreos 安装时也有一些需要注意的地方:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;cloud-config.yml&lt;/p&gt;
&lt;p&gt;这是 coreos 用来统一配置系统的地方，系统在每次启动时都会加载这个文件的配置，比
如系统服务、网络设定、文件修改、用户设定等。这样做的好处是在部署集群的时候可
以方便地使用相同的配置。在安装 coreos 时，需要指定好这个配置文件。实际操作时，
可以提前将这个文件写好放在别的机器上，然后用 scp / wget (利用下面的 web server) 下载到 coreos 的 Live CD 即可，或者直接存在 Live CD 里更方便。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;GFW&lt;/p&gt;
&lt;p&gt;coreos 安装时需要从官网下载镜像，但网站被墙，所以实际安装的时候可能需要用代理来解决，缺点是速度慢。更方便的方法是提前下载好需要的文件并放在局域网内并搭建一个 web server，然后修改安装脚本的 server 即可。具体方法在后面详述。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;</description></item></channel></rss>