<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title>Posts on YochaLyc&#39;s Blog</title>
		<link>https://yochalyc.com/posts/</link>
		<description>Recent content in Posts on YochaLyc&#39;s Blog</description>
		<generator>Hugo -- gohugo.io</generator>
		<language>zh-hans</language>
		<lastBuildDate>Sat, 17 Feb 2024 22:46:06 +0800</lastBuildDate>
		<atom:link href="https://yochalyc.com/posts/index.xml" rel="self" type="application/rss+xml" />
		
		<item>
			<title>Conversion of Docker related Artifacts</title>
			<link>https://yochalyc.com/2024/02/conversion-of-docker-related-artifacts/</link>
			<pubDate>Sat, 17 Feb 2024 22:46:06 +0800</pubDate>
			
			<guid>https://yochalyc.com/2024/02/conversion-of-docker-related-artifacts/</guid>
			<description>在 OCI Image Specification 介绍 和 OCI Runtime Specification 介绍 我们介绍了 OCI 关于镜像实现和容器运行的标准。但是这两部分内容涉及的产物离大部分</description>
			<content type="html"><![CDATA[<p>在 <a href="https://yochalyc.com/2024/02/oci-image-specification-%E4%BB%8B%E7%BB%8D/">OCI Image Specification 介绍</a> 和 <a href="https://yochalyc.com/2024/02/oci-runtime-specification-%E4%BB%8B%E7%BB%8D/">OCI Runtime Specification 介绍</a> 我们介绍了 OCI 关于镜像实现和容器运行的标准。但是这两部分内容涉及的产物离大部分同学的日常操作可能比较遥远，因此补充这篇文章，以 Docker 为例大致说明 Docker 与前文提到的 OCI 标准产物是如何交互的。</p>
<blockquote>
<p>免责声明:</p>
<ul>
<li>本文内容有一定程度上请教 ChatGPT(4.0)，而 &ldquo;ChatGPT可能會出錯。請考慮核對重要資訊。&rdquo;</li>
<li>OCI 的制定晚于 Docker 早期版本，因此部分历史镜像或历史 Docker 版本的逻辑可能与本文论述的不一致</li>
</ul>
</blockquote>
<p><img src="index.assets/image-20240217225146308.png" alt="image-20240217225146308"></p>
<p>如上图，描述了主要 Docker 指令对应的产物关系。</p>
<br />
<h1 id="关键实体说明">关键实体说明</h1>
<ul>
<li>Internal Docker Image: 指的是 Docker 在本地构建完成后的镜像， 是内部 Docker 镜像，它是基于 Dockerfile 通过 <code>docker build</code> 命令构建的。它存储在 Docker 守护进程的内部存储中，(Linux 下)通常位于 <code>/var/lib/docker/</code>，遵循的是 Docker 内部的镜像存储结构。</li>
<li>OCI Runtime Bundle: 创建容器所依赖的 config 和 rootfs 文件, 参考 <a href="https://yochalyc.com/2024/02/oci-runtime-specification-%E4%BB%8B%E7%BB%8D/#filesystem-bundle">Filesystem Bundle</a></li>
</ul>
<br />
<h1 id="行为说明">行为说明</h1>
<h2 id="docker-build">docker build</h2>
<ol>
<li>Docker 从 Dockerfile 读取指令，构建镜像层，生成镜像配置</li>
<li>Docker 将镜像直接按照 Docker 内部格式进行存储</li>
</ol>
<h2 id="docker-push">docker push</h2>
<p><code>docker push</code> 会将镜像以符合 OCI Image Spec 的格式推送到远程仓库</p>
<ol>
<li>
<p>Docker 生成符合 OCI Image Spec 的 Docker Image</p>
<ol>
<li>
<p>准备镜像清单 (manifest)</p>
</li>
<li>
<p>生成镜像配置 (config)</p>
</li>
</ol>
</li>
<li>
<p>Docker 推送 Docker Image</p>
<ol>
<li>上传 layers</li>
<li>推送 manifest &amp; config</li>
<li>tag 推送</li>
</ol>
</li>
</ol>
<h2 id="docker-save">docker save</h2>
<p>docker save 会将镜像以符合 OCI Image Spec 格式的 Docker Image 保存下来为 tarball</p>
<h2 id="docker-run">docker run</h2>
<p>docker run 会 调用 runtime(如 runc) 来创建和操作 OCI Runtime Bundle, 并创建容器并执行</p>
<ol>
<li>Docker 从镜像中提取元数据和配置信息</li>
<li>Docker 准备 rootfs</li>
<li>Docker 生成配置</li>
<li>Docker 调用 Runtime 管理容器生命周期</li>
<li>Docker 启动容器</li>
</ol>
]]></content>
		</item>
		
		<item>
			<title>OCI Runtime Specification 介绍</title>
			<link>https://yochalyc.com/2024/02/oci-runtime-specification-%E4%BB%8B%E7%BB%8D/</link>
			<pubDate>Sat, 17 Feb 2024 22:43:43 +0800</pubDate>
			
			<guid>https://yochalyc.com/2024/02/oci-runtime-specification-%E4%BB%8B%E7%BB%8D/</guid>
			<description>在上次的 文章 OCI Image Specification 介绍 中我们介绍了 OCI Image Specification 定义的关于镜像如何实现的标准。这篇文章我们接着介绍一下 OCI Runtime S</description>
			<content type="html"><![CDATA[<p>在上次的 文章 <a href="https://yochalyc.com/2024/02/oci-image-specification-%E4%BB%8B%E7%BB%8D/">OCI Image Specification 介绍</a> 中我们介绍了 OCI Image Specification 定义的关于镜像如何实现的标准。这篇文章我们接着介绍一下 OCI Runtime Specification。</p>
<p>OCI Runtime Specification 的出发点和 Image Spec 基本一致，它通过制定容器执行的标准规范，使容器的行为和状态与具体的上层软件无关，实现容器的便携性和更好地自动化支持。</p>
<p>满足 OCI Runtime 的标准容器有五个基本的原则(<a href="https://github.com/opencontainers/runtime-spec/blob/main/principles.md">runtime-spec/principles.md at main · opencontainers/runtime-spec</a>):</p>
<ul>
<li>Standard Operations</li>
<li>Content-agnostic</li>
<li>Infra-agnostic</li>
<li>Designed for automation</li>
<li>Industrial-grade delivery</li>
</ul>
<h1 id="术语">术语</h1>
<p>为了尽可能准确地明确文章中使用术语的上下文，我们着重介绍一下 Runtime Spec 中涉及到的两个和其上下文较紧密相关的术语, 详细的列表大家可以参考 <a href="https://github.com/opencontainers/runtime-spec/blob/main/glossary.md">runtime-spec/glossary.md at main · opencontainers/runtime-spec</a></p>
<ul>
<li>Bundle: OCI Runtime Filesystem Bundle, 是符合 OCI Runtime Spec 定义规范的，用于创建容器的文件夹</li>
<li>Runtime: OCI Runtime Spec 的具体实现，我们下文举例会用到的 <code>runc</code> 就术语 runtime, 其他例子见 <a href="https://github.com/opencontainers/runtime-spec/blob/main/implementations.md">runtime-spec/implementations.md at main · opencontainers/runtime-spec</a></li>
</ul>
<h1 id="filesystem-bundle">Filesystem Bundle</h1>
<p><img src="index.assets/image-20240217204703368.png" alt="image-20240217204703368"></p>
<p>如图所示，Runtime Bundle 包含两部分文件内容。</p>
<br />
<p><code>config.json</code> 文件</p>
<p>Config 文件 是和容器运行相关的配置文件，必须放在 Bundle 根目录下。它包含的典型内容如环境变量信息、挂载(Mounts) 信息、容器进程(<code>process</code>) 信息、和容器生命周期相关的 hooks 等。</p>
<p>Config 文件的生成来源主要有两种:</p>
<ul>
<li>通过 Runtime 指令生成默认的 config.json 文件: 如 <code>runc spec</code> , 通常用于开发、测试或是定制化配置的场景，生成默认配置后可以在其上进行修改来适配容器场景。</li>
<li>通过 OCI Image config 转化: 转化逻辑可以参考 <a href="https://github.com/opencontainers/image-spec/blob/main/conversion.md">image-spec/conversion.md at main · opencontainers/image-spec</a>， 我们日常接触的其实主要是这种来源，它具有一致性和可复现，通常与容器生态系统进行了集成（如 Docker 实际上帮我们做了这层转化)。</li>
</ul>
<br/>
<p><code>rootfs</code> 文件夹</p>
<p>rootfs 即容器的 filesystem, 工业使用场景中通过 <a href="https://yochalyc.com/2024/02/oci-image-specification-%E4%BB%8B%E7%BB%8D/#layers">Image Layers</a> unpack 得到， <code>rootfs</code> 的路径和名称可以在 <code>config.json</code> 文件的 <code>root.path</code> 中进行指定。</p>
<h2 id="demo">Demo</h2>
<p>Runtime Bundle 的文件结构相较于 Image Spec 要简单许多，我们不妨尝试按一下步骤动手尝试构建一个 Bundle 。</p>
<ol start="0">
<li>
<p>准备 Linux 环境(runc 只支持 Linux 环境执行)并进行 <code>runc</code> 准备， 见 <a href="https://github.com/opencontainers/runc">runc</a></p>
</li>
<li>
<p>通过 docker export 指令获得容器的 rootfs 并放置在指定文件夹, 这里以 busybox 镜像为例</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>$ mkdir rootfs
</span></span><span style="display:flex;"><span>$ docker export <span style="color:#66d9ef">$(</span>docker create busybox:latest<span style="color:#66d9ef">)</span> | tar -C rootfs -xvf -
</span></span></code></pre></div></li>
<li>
<p>创建默认的 config.json 文件</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>$ runc spec
</span></span></code></pre></div></li>
</ol>
<p>Over, 最终文件结构如下</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>$ tree -L <span style="color:#ae81ff">2</span>
</span></span><span style="display:flex;"><span>.
</span></span><span style="display:flex;"><span>├── config.json
</span></span><span style="display:flex;"><span>└── rootfs
</span></span><span style="display:flex;"><span>    ├── bin
</span></span><span style="display:flex;"><span>    ├── dev
</span></span><span style="display:flex;"><span>    ├── etc
</span></span><span style="display:flex;"><span>    ├── home
</span></span><span style="display:flex;"><span>    ├── lib
</span></span><span style="display:flex;"><span>    ├── lib64 -&gt; lib
</span></span><span style="display:flex;"><span>    ├── proc
</span></span><span style="display:flex;"><span>    ├── root
</span></span><span style="display:flex;"><span>    ├── sys
</span></span><span style="display:flex;"><span>    ├── tmp
</span></span><span style="display:flex;"><span>    ├── usr
</span></span><span style="display:flex;"><span>    └── var
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ae81ff">13</span> directories, <span style="color:#ae81ff">1</span> file
</span></span></code></pre></div><h1 id="lifecycle">Lifecycle</h1>
<p>除了文件结构 Bundle， OCI Runtime Spec 还定义了容器状态机及相关的 (Runtime 需要支持的) Operation 操作。</p>
<p><img src="index.assets/image-20240217211241523.png" alt="image-20240217211241523"></p>
<p>上图展示了容器的生命周期、对应触发指令及容器的响应行为，上图涉及的相关行为说明可以在 <a href="https://github.com/opencontainers/runtime-spec/blob/main/runtime.md#lifecycle">runtime-spec/runtime.md at main · opencontainers/runtime-spec</a> 这里找到， 图上涉及的 Env 信息、Hooks 信息、用户制定的容器进程 process 都是在 <code>config.json</code> 文件中指定的。</p>
<p>我们可以在上文初始化好的 bundle 文件夹中创建容器并观察 Lifecycle：</p>
<ol>
<li>
<p>执行 <code>runc create &lt;container-id&gt;</code>, container-id 可以任意指定， 这里我们指定为 testid</p>
</li>
<li>
<p>执行 <code>runc list</code> 观察状态, 这时我们容器的状态为 created</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>$ runc list
</span></span><span style="display:flex;"><span>ID          PID         STATUS      BUNDLE                                              CREATED                          OWNER
</span></span><span style="display:flex;"><span>testid      <span style="color:#ae81ff">7094</span>        created     .../busybox_fs   2024-02-17T13:20:13.231362865Z   root
</span></span></code></pre></div></li>
<li>
<p>执行 <code>runc start testid</code> 后再执行 <code>runc list</code>, 会发现状态变为了 <code>stopped</code> （我们没有指定 process)</p>
</li>
<li>
<p>可以通过 <code>runc state testid</code> 获得 <a href="https://github.com/opencontainers/runtime-spec/blob/main/runtime.md#state">runtime-spec/runtime.md at main · opencontainers/runtime-spec</a> 描述的状态元数据信息， 如</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;ociVersion&#34;</span>: <span style="color:#e6db74">&#34;1.1.0+dev&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;id&#34;</span>: <span style="color:#e6db74">&#34;testid&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;pid&#34;</span>: <span style="color:#ae81ff">0</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;status&#34;</span>: <span style="color:#e6db74">&#34;stopped&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;bundle&#34;</span>: <span style="color:#e6db74">&#34;...&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;rootfs&#34;</span>: <span style="color:#e6db74">&#34;...&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;created&#34;</span>: <span style="color:#e6db74">&#34;2024-02-17T14:19:51.642906094Z&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;owner&#34;</span>: <span style="color:#e6db74">&#34;&#34;</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div></li>
</ol>
]]></content>
		</item>
		
		<item>
			<title>第二十条影评</title>
			<link>https://yochalyc.com/2024/02/%E7%AC%AC%E4%BA%8C%E5%8D%81%E6%9D%A1%E5%BD%B1%E8%AF%84/</link>
			<pubDate>Fri, 16 Feb 2024 12:27:20 +0800</pubDate>
			
			<guid>https://yochalyc.com/2024/02/%E7%AC%AC%E4%BA%8C%E5%8D%81%E6%9D%A1%E5%BD%B1%E8%AF%84/</guid>
			<description>rate: ⭐️⭐️⭐️⭐️ 第二十条是今年(2024)年的贺岁档电影，看着上面这张海报喜气洋洋，C 位由雷佳音和</description>
			<content type="html"><![CDATA[<img src="index.assets/image-20240216000315641.png" alt="image-20240216000315641" style="zoom:30%;" />
<p>rate: ⭐️⭐️⭐️⭐️</p>
<hr>
<p>第二十条是今年(2024)年的贺岁档电影，看着上面这张海报喜气洋洋，C 位由雷佳音和马丽两个（先前）主演喜剧的演员担任左右护法，左下角还明目张胆写着“我爱我家❤️️”，谁能想到这是一部内核颇为严肃的法律教育片。作为2024年我第一部在影院观影的电影，虽然从主题上来说它可能不满足贺岁片该有的喜庆和轻松，但是在我看来是一部质量相当在线的电影。</p>
<p>先来说说人物。从海报上就可以看出来，第二十条的主要演员阵容和 23 年颇火的 《狂飙》 重合度很高，以至于我一直期待着张颂文老师的出现。主演们在我这外行看来演技都是在线的，几个老戏骨自不必说，刘耀文和赵丽颖两个年轻演员在电影中也很放得开，不做作，在被各种装逼的小鲜肉恶心的几年里这已经足够让我感到欣慰了。以及，大嫂真漂亮。</p>
<br />
<p>节奏上整体也让人感到比较合适，严肃的情节里穿插着还比较合时宜而不尴尬的喜剧桥段调节气氛。不同故事线之间的情节推进和衔接自然，不突兀，也能充分反映在主角的情绪变化上。</p>
<p>稍有点让人感到疲惫的是频繁的情侣式的斗嘴片段，相似的桥段可能出现两次会觉得有趣，多了就有点生厌。不知道是不是错觉，这种斗嘴的片段似乎随着剧情的紧张也有在变得频繁，我斗胆猜测一下，张艺谋导演或许是想借这种争吵之间的频率以及严肃程度来侧面反应剧情紧张，相关人员的心理压力也在逐渐增大，反映昧着本心做着守法但是合乎清理的决定最终只会让自己在乎的人难受？</p>
<br />
<p>最后回到剧情上。</p>
<p>第二十条指的是 《刑法》第二十条关于 “正当防卫” 的法条，电影主要也围绕着三个和正当防卫有关的故事展开，电影的整体逻辑和《不止不休》类似，主角（团）面对着种种现实因素的阻挠和惯例的约束，从畏手畏脚到最终觉醒，推动不合理制度/现象的改变。（会不会是因为因为剧情逻辑太一致所以张颂文老师没有出演🤔）。</p>
<p>电影中涵盖了三条不同的正当防卫的故事线，通过主角韩明串在了一起。三条线有着或明或暗的相同和对比，我懒得展开具体描述电影的情节，就结合我个人主观揣测一下这些相同和对比点的意图和个人感受吧。</p>
<br />
<p>相同点一: 在三个故事线中，侵害人都是正当防卫事件里的弱势者，生活中的强势者，这大概也是现实中正当防卫相关案件的普遍情况吧。</p>
<p>王勇强案中的高利贷债主和债权人，韩雨辰故事中的教导主任儿子和寄托全家希望的借读生，单亲的张贵生司机和流氓，这些人在生活中都有着自己的软肋，软肋也很可能恰恰被抓在侵害人手中。受到反抗的侵害人地展现和利用自己在正当防卫事件中的弱势形象，同时又靠着自己在生活中的强势地位施加压力，伟光正地为自己争取“合理”的诉求。</p>
<p>他们的诉求合理吗？电影里展现的诉求是合“理”的，无非是杀人偿命，无非是打人的先道个歉，他们有时甚至不为自己申请实际的利益。但是这种合理的诉求是可怕的，他们的诉求释放的信号是对正在受着压迫的当事人和非当事人的警告: 反抗我们是要付出代价的。不管是正当防卫还是见义勇为，大多是心里压抑的强烈情感的爆发，不断地接收这种信号也是在不断地提升反抗的阈值，也就有着更多和更长久的人被持续压迫着。</p>
<p>值得注意的是，在王勇强和韩雨辰的故事中，站出来博取同情的并不是当事人，他们可能并没有参与侵害，他们不会心虚，他们有着无比正当的理由。以韩雨辰和教导主任来说，教导主任想要的是一个为打人事件的道歉，他也愿意为霸凌的事单独处理单独追究，同是站在局外人的视角看，在观影的时候我一度感觉这种分治的处理方法是思路清晰的，一个道歉也只是动动嘴皮的事。</p>
<p>但是这种理中客视角的小代价和处理方法是对防卫人的服从性测试，你接受了道歉，你也就接受了压迫，你也就会在下次被霸凌的时候选择忍耐。</p>
<p>相同点二: 举证的困难，防卫人需要证明自己是受到威胁的，是在被持续侵犯的，是在侵害人作为生活的强势人掌控舆论甚至掌控自己软肋的情况下抱着鱼死网破的决心的情况下去证明自己为什么忍无可忍了。</p>
<p>王勇强案的一大段篇幅都在等着一把刀的出现，一把侵害人口中的放在车上的说要砍死王勇强的刀。故事的结局是好的，刀找到了，但是如果侵害人只是口舌之快，或是今天出门忘了带刀，那是不是只能认罪。</p>
<p>说到底那把刀重要吗，侵害人就是刀，一刀下去可能是家破人亡和三年霸凌，一辈子的心理阴影。</p>
<br />
<p>接下来说说不同的地方。</p>
<p>三个故事线的严重程度不同，阐述视角不同。</p>
<p>韩雨辰事件是中学里发生的事，是主角身边的事，也可能是我们身边的事，也可能就是我。</p>
<p>王勇强案是重大事件，侵害程度最大，但即使是这么恶劣的侵害，他仍然需要一把刀去证明自己的正当性。</p>
<p>张贵生是一个几年的历史老案，是一个判决后不断上访但无法翻案的老案。这条线告诉我们，法是全国一样的法，环境和惯例就是如此。</p>
<br />
<p>有趣的是在观影的时候坐我身后的小屁孩在不断地顶着我的椅背，而我也担心刺头小孩吵闹影响了其他观众而选择了忍耐，属于是即时情景体验了。</p>
]]></content>
		</item>
		
		<item>
			<title>OCI Image Specification 介绍</title>
			<link>https://yochalyc.com/2024/02/oci-image-specification-%E4%BB%8B%E7%BB%8D/</link>
			<pubDate>Thu, 15 Feb 2024 23:20:29 +0800</pubDate>
			
			<guid>https://yochalyc.com/2024/02/oci-image-specification-%E4%BB%8B%E7%BB%8D/</guid>
			<description>The Open Container Initiative is an open governance structure for the express purpose of creating open industry standards around container formats and runtimes. ── About the Open Container Initiative - Open Container Initiative OCI(Open Container Initiative) 是在 2015年 由 Linux Foundation 支持，</description>
			<content type="html"><![CDATA[<blockquote>
<p>The <strong>Open Container Initiative</strong> is an open governance structure for the express purpose of creating open industry standards around container formats and runtimes. ── <a href="https://opencontainers.org/about/overview/">About the Open Container Initiative - Open Container Initiative</a></p>
</blockquote>
<p>OCI(Open Container Initiative) 是在 2015年 由 Linux Foundation 支持，由 Docker, CoreOS 等其他容器行业内的领导者共同发起的 <strong>容器镜像实现的格式标准</strong>， 旨在推广容器格式(format) 和运行时(runtime) 的开放治理结构(open governance structure), 创建开放的工业标准。OCI 的成立主要是为了解决容器技术在快速发展过程中出现的标准化问题，确保容器的可移植性和互操作性，让容器能够在各种环境中无缝运行。</p>
<p>OCI 规定了关于容器的两部分标准:</p>
<ul>
<li>Image Spec: 关于容器镜像如何实现(或者说打包) 的标准</li>
<li>Runtime Spec: 关于容器如何运行的标准</li>
</ul>
<p>本文的内容聚焦于 Image Spec 相关介绍，主要来源为 <a href="https://github.com/opencontainers/image-spec/blob/main/config.md">image-spec/config.md at main · opencontainers/image-spec</a> ，尝试结合实际例子以及个人认为更符合逻辑的理解顺序来梳理 Image Spec 的主要概念。</p>
<h1 id="demo">Demo</h1>
<p>在有条件的情况下读者可以先按如下步骤进行相关 demo 的准备，结合 Demo 进行 Image Spec 相关实体的理解更能增强体感。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#75715e"># 拉取 nginx:latest 镜像到本地，nginx:latest 是本文选择的符合 OCI image 的镜像</span>
</span></span><span style="display:flex;"><span>$ docker pull nginx:latest
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 以 tarball 形式保存 nginx 镜像</span>
</span></span><span style="display:flex;"><span>$ docker image save -o nginx.tar nginx:latest
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 创建并解压保存下来的 tarball 到指定文件夹</span>
</span></span><span style="display:flex;"><span>$ mkdir nginx <span style="color:#f92672">&amp;&amp;</span> tar -xvf nginx.tar -C ./nginx
</span></span></code></pre></div><h1 id="总览">总览</h1>
<p><img src="index.assets/image-20240214161500085.png" alt="image-20240214161500085"></p>
<p>在开始深入介绍前我们先对 OCI Image Spec 有个大概的认识，为了方便理解我们不妨先将 OCI Image Spec 分为两个部分</p>
<ul>
<li>Spec Context: 对 Image 的结构、格式、描述方法所做出的约束和规范。又包括
<ul>
<li>Layout: 对 Image 实现的文件夹结构的规范</li>
<li>Media Type: 对 Image 设计的文件(或者叫 component) 的类型的约定</li>
<li>Descriptor: 定义了 Image 中 component 之间进行关联的结构体</li>
</ul>
</li>
<li>Spec components: Image 实现中需要包含的具体组成实体, 有各自对应的文件。</li>
</ul>
<p>接下来本文将结合上文 nginx tarball 例子，先从 Spec Context 开始对 OCI Image 建立规范上的认识，再分别介绍各个 Spec Components， 每一块内容标题下都提供了对应的原始文档链接，有兴趣的读者可以点击进入深入了解。</p>
<h1 id="spec-context">Spec Context</h1>
<h2 id="layout">Layout</h2>
<p><a href="https://github.com/opencontainers/image-spec/blob/main/image-layout.md">image-spec/image-layout.md at main · opencontainers/image-spec</a></p>
<p>OCI Image Layout 规定了 Image 的文件夹组织方式，通过遵守 Layout， 相关的工具可以对一个镜像按照一致的路径处理成符合 OCI Runtime Spec 的 bundle。</p>
<p>Layout 规定了一个镜像需要包含 <code>blobs</code> 文件夹, <code>oci-layout</code> 文件，<code>index.json</code> 文件， 这三种文件类型在我们的 demo 中也可以看到:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>$ ls
</span></span><span style="display:flex;"><span>blobs         index.json    manifest.json     oci-layout    repositories
</span></span></code></pre></div><h3 id="blobs-文件夹"><code>blobs</code> 文件夹</h3>
<p>blob 是 <em>Binary Large Object</em> 的缩写，通常指的是镜像中的二进制数据块。在 OCI Image Spec 的上下文中，我们可以直接将所有通过内容进行 hash 索引的文件理解成是 blob。</p>
<p><code>blobs</code> 文件夹存储的就是使用特定 Hash 算法计算过的文件，它的子目录的结构为 <code>&lt;alg&gt;/&lt;encoded&gt;</code>, <code>&lt;alg&gt;</code> 为对应的 Hash 算法 ，<code>&lt;encoded&gt;</code> 为该 Hash 算法对内容进行过 Hash 后的值。</p>
<p>比如在我们的 demo 中， <code>blobs</code> 包含如下文件</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>$ tree
</span></span><span style="display:flex;"><span>.
</span></span><span style="display:flex;"><span>├── blobs
</span></span><span style="display:flex;"><span>│   └── sha256
</span></span><span style="display:flex;"><span>│       ├── 02eecd1b8ebb27cc1f576804168486c4c5c3180d22c50048fdaa546b581adec9
</span></span><span style="display:flex;"><span>│       ├── 047c9d07f2a3abf9e7c546307dc69cb5147b1cb8d2f81b58d36d2be57fea6257
</span></span><span style="display:flex;"><span>│       ├── 0655e46321e4ed76b200f52007cb59fcc3801faccd391f6795712028d7536c6f
</span></span><span style="display:flex;"><span>│       ├── 11deb55301007d6bf1db2ce20cb5d12e447541969990af4a03e2af8141ebdbed
</span></span><span style="display:flex;"><span>│       ├── 1adb92f0b5e98c8db798082a7877732ad1f5e273577b63ac9c1a505f3e8ed61c
</span></span><span style="display:flex;"><span>│       ├── 31eaf3687d312550bf26d9d649c95283ebc46b03aeda439952d4fba2fd3187e6
</span></span><span style="display:flex;"><span>│       ├── 37be4081a37453c9e9f11af799eb037c47664a0549ddfa236b3e57bab95ba64b
</span></span><span style="display:flex;"><span>│       ├── 46dca80a0cef221684a9da329176ad5bc6d6f77c379ad234a9e5931795f975d3
</span></span><span style="display:flex;"><span>│       ├── 5c23503565e9eb6c31f511199a6f80721b2750d00d174011bcc80248b2b896ff
</span></span><span style="display:flex;"><span>│       ├── 8790436822597f6bbc1de71c6e9d25636fd687fb8a60dfc2c69e0bf3d330349a
</span></span><span style="display:flex;"><span>│       ├── 8d80ad266f0138f5eedd03c130279b1059fed471346f1656c5bf0bfa1d3d12ed
</span></span><span style="display:flex;"><span>│       ├── bb23c2def74f2ccbf902543ca6675687e5f662e3d991fbd9938dbea06c5faefc
</span></span><span style="display:flex;"><span>│       ├── c356d79b264683ba6151321749af21974030d674d1a607e2c888c4598017620b
</span></span><span style="display:flex;"><span>│       ├── ced4d951e1bfe83757aa885f51c2b48d081e0c23a87af7d01386a62cec11824c
</span></span><span style="display:flex;"><span>│       ├── df91f10d317c00ef9f4ce4359c893a96e621ae6ee532649af2e42b774315e528
</span></span><span style="display:flex;"><span>│       ├── f2b5466eadaed1957e9c445669168b5a90e9e259a2dd5990af5f3ebb7ad50df0
</span></span><span style="display:flex;"><span>│       └── f63419ada981fd98f7a67208d219167f82c6097de0e747c9f42c6c86924afc47
</span></span></code></pre></div><p>即一系列由 sha256 对内容进行过 Hash 的文件。</p>
<p>对于这些文件，我们可以通过如下方式验证其内容与 Hash 值是匹配的</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>$ shasum -a <span style="color:#ae81ff">256</span> ./blobs/sha256/02eecd1b8ebb27cc1f576804168486c4c5c3180d22c50048fdaa546b581adec9
</span></span><span style="display:flex;"><span>02eecd1b8ebb27cc1f576804168486c4c5c3180d22c50048fdaa546b581adec9  ./blobs/sha256/02eecd1b8ebb27cc1f576804168486c4c5c3180d22c50048fdaa546b581adec9
</span></span></code></pre></div><p>因此这些 blobs 是 <code>content-addressable</code> 的，即是由他们的内容决定存储位置的 blob, 这在 OCI Image Spec 中是一个相对比较重要的概念: 除了可以根据 Hash 值进行标识符进行基本的 ref 和 索引， 还可以起到校验的作用。</p>
<p>OCI Image 的 blobs 包含了镜像的大部分内容，在我们总览的 Spec Components 中，除了 Layout 里单独提到的 index 文件，其他 component 都保存在 blobs 文件夹下， 除此之外 blobs 也可能包含一些各个工具自身实现需要的(在 OCI Image Spec 之外的)额外的文件。</p>
<h3 id="oci-layout-文件"><code>oci-layout</code> 文件</h3>
<p><code>oci-layout</code> 文件的包含的内容很简单，通常只包含 <code>imageLayoutVersion</code> 字段信息，表明镜像所适配的 layout 版本号。</p>
<p>如我们 demo 中的 <code>oci-layout</code> 内容如下</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{ <span style="color:#f92672">&#34;imageLayoutVersion&#34;</span>: <span style="color:#e6db74">&#34;1.0.0&#34;</span> }
</span></span></code></pre></div><h3 id="indexjson-文件"><code>index.json</code> 文件</h3>
<p>对应 spec components 中的 index 文件，index.json 文件是 OCI Image 的入口。我们在后文的 components 中会展开介绍，这里不再赘述。</p>
<h2 id="media-type">Media Type</h2>
<p><a href="https://github.com/opencontainers/image-spec/blob/main/media-types.md">image-spec/media-types.md at main · opencontainers/image-spec</a></p>
<p>Media Type 定义了 OCI Image 各个组件的类型，对应到组件里的 <code>mediaType</code> 字段， 根据这个字段我们便可以知道某个文件对应的 component 及一些基础的元数据信息。</p>
<p>Media Type 基本是用户可读的， 比如如果一个 Image 文件对应的 <code>mediaType</code> 是 <code>application/vnd.oci.image.layer.v1.tar+gzip</code> 从字面上我们就可以看出这个 文件对应的是 OCI v1 版本下的 layer 组件，通过 <code>tar</code> 进行了打包并且通过 <code>gzip</code> 进行了压缩。</p>
<p>除了定义类型，Media Type 还起到识别冲突和判断兼容性的作用。</p>
<ul>
<li>识别冲突: 当工具请求获得 blob 的信息时，返回的元数据信息中的类型与工具实现所期望的 <code>mediaType</code> 可能不匹配，这种情况下工具需要遵守 mediaType 对冲突的响应规范进行处理或报错。</li>
<li>兼容性判断: OCI Image Spec 会尽可能地向前及向后兼容。同时对历史(OCI 之前)的 Image 配置类型，Spec 中也给出了明确的兼容性矩阵帮助进行判断。当出现兼容性问题时，就可能发生如镜像拉取/推送失败，或者容器启动失败等问题。</li>
</ul>
<h2 id="descriptor">Descriptor</h2>
<p><a href="https://github.com/opencontainers/image-spec/blob/main/descriptor.md">image-spec/descriptor.md at main · opencontainers/image-spec</a></p>
<p>如上文所说，OCI Image 包含了多个不同的 component, 这些 component 需要进行相互关联，这些 component 的关联关系就以 <em>Content Descriptors(=Descriptor)</em> 表示。</p>
<p>Descriptor 通常包含如下内容:</p>
<ul>
<li><code>mediaType</code> : 即上文介绍的 <code>mediaType</code> 内容</li>
<li><code>digest</code>: 记得我们上面提到 <code>blobs</code> 是 <code>content-addressable</code> 的吗？ digest 记代表了引用的组件的标识符，同时我们也能根据这个标识符在 <code>blobs</code> 文件夹中找到对应的资源</li>
<li><code>size</code>： <code>blobs</code> 原始内容的大小(bytes)</li>
<li>&hellip;</li>
</ul>
<h1 id="spec-components">Spec Components</h1>
<h2 id="index">Index</h2>
<p><a href="https://github.com/opencontainers/image-spec/blob/main/image-index.md">image-spec/image-index.md at main · opencontainers/image-spec</a></p>
<p>index 是 OCI Image 的入口(entry point), 也是多架构镜像中指向多个单一平台镜像的更高视角的索引入口。</p>
<p>index 通常对应的文件便是上文 Layout 中提到的 <code>index.json</code>, 我们结合 nginx Demo 看一下 <code>index.json</code> 文件中包含的具体内容</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;schemaVersion&#34;</span>: <span style="color:#ae81ff">2</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;mediaType&#34;</span>: <span style="color:#e6db74">&#34;application/vnd.oci.image.index.v1+json&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;manifests&#34;</span>: [
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;mediaType&#34;</span>: <span style="color:#e6db74">&#34;application/vnd.oci.image.manifest.v1+json&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;digest&#34;</span>: <span style="color:#e6db74">&#34;sha256:f2b5466eadaed1957e9c445669168b5a90e9e259a2dd5990af5f3ebb7ad50df0&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;size&#34;</span>: <span style="color:#ae81ff">1356</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;annotations&#34;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;io.containerd.image.name&#34;</span>: <span style="color:#e6db74">&#34;docker.io/library/nginx:latest&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;org.opencontainers.image.ref.name&#34;</span>: <span style="color:#e6db74">&#34;latest&#34;</span>
</span></span><span style="display:flex;"><span>      },
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;platform&#34;</span>: { <span style="color:#f92672">&#34;architecture&#34;</span>: <span style="color:#e6db74">&#34;arm64&#34;</span>, <span style="color:#f92672">&#34;os&#34;</span>: <span style="color:#e6db74">&#34;linux&#34;</span> }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  ]
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>如 Demo 所示，典型的 <code>index.json</code> 包含的 properties 有以下内容:</p>
<ul>
<li>
<p><code>schemaVersion</code> int: 标识了 image 的 <code>manifest</code> 的 schema version， 对于当前(20240214) 的 OCI Image Spec 版本，这个 property 的值为 2</p>
</li>
<li>
<p><code>mediaType</code> string: 标识 index 的 mediaType 的字段， 值为 <code>application/vnd.oci.image.index.v1+json</code></p>
</li>
<li>
<p><code>manifests</code> array of objects: 包含特定 platform 对应的 manifest descriptor。结合 descriptor 的字段定义，我们对其中的关键字段可以进一步展开:</p>
<ul>
<li><code>mediaType</code> : <code>application/vnd.oci.image.manifest.v1+json</code> 表示引用的是 manifest.</li>
<li><code>digest</code>: manifest 的 digest, 可以在 <code>blobs/sha256</code> 中找到对应的文件</li>
</ul>
</li>
<li>
<p><code>platform</code>: 表示对应的 manifest 需要满足的最小 runtime 要求</p>
</li>
</ul>
<h2 id="manifest">Manifest</h2>
<p><code>manifest</code> 组件提供了单一架构/操作系统下的镜像所需要的配置和 layers 信息，允许工具如 registry 和运行时确定需要拉取的配置内容和各 layers 层信息。</p>
<p>根据 <code>index.json</code> 文件的 <code>manifests[*].digest</code> , 我们可以在 <code>blobs</code> 文件夹中找到对应的manifest（可能有些工具为自身实现的需要会在根目录下直接冗余 manifest.jon 文件，OCI image spec 的定义中 manifest 是在 blobs 中保存的）。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#75715e"># f2b5466eadaed1957e9c445669168b5a90e9e259a2dd5990af5f3ebb7ad50df0 是在 index.json 文件中获得的 manifest 的 digest 值</span>
</span></span><span style="display:flex;"><span>$  cat blobs/sha256/f2b5466eadaed1957e9c445669168b5a90e9e259a2dd5990af5f3ebb7ad50df0 | jq
</span></span></code></pre></div><p>文件内容如下</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;schemaVersion&#34;</span>: <span style="color:#ae81ff">2</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;mediaType&#34;</span>: <span style="color:#e6db74">&#34;application/vnd.oci.image.manifest.v1+json&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;config&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;mediaType&#34;</span>: <span style="color:#e6db74">&#34;application/vnd.oci.image.config.v1+json&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;digest&#34;</span>: <span style="color:#e6db74">&#34;sha256:11deb55301007d6bf1db2ce20cb5d12e447541969990af4a03e2af8141ebdbed&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;size&#34;</span>: <span style="color:#ae81ff">7016</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;platform&#34;</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;architecture&#34;</span>: <span style="color:#e6db74">&#34;arm64&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;os&#34;</span>: <span style="color:#e6db74">&#34;linux&#34;</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;layers&#34;</span>: [
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;mediaType&#34;</span>: <span style="color:#e6db74">&#34;application/vnd.oci.image.layer.v1.tar&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;digest&#34;</span>: <span style="color:#e6db74">&#34;sha256:02eecd1b8ebb27cc1f576804168486c4c5c3180d22c50048fdaa546b581adec9&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;size&#34;</span>: <span style="color:#ae81ff">100168192</span>
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;mediaType&#34;</span>: <span style="color:#e6db74">&#34;application/vnd.oci.image.layer.v1.tar&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;digest&#34;</span>: <span style="color:#e6db74">&#34;sha256:ced4d951e1bfe83757aa885f51c2b48d081e0c23a87af7d01386a62cec11824c&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;size&#34;</span>: <span style="color:#ae81ff">95961088</span>
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// ....
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    {
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;mediaType&#34;</span>: <span style="color:#e6db74">&#34;application/vnd.oci.image.layer.v1.tar&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;digest&#34;</span>: <span style="color:#e6db74">&#34;sha256:047c9d07f2a3abf9e7c546307dc69cb5147b1cb8d2f81b58d36d2be57fea6257&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;size&#34;</span>: <span style="color:#ae81ff">5120</span>
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;mediaType&#34;</span>: <span style="color:#e6db74">&#34;application/vnd.oci.image.layer.v1.tar&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;digest&#34;</span>: <span style="color:#e6db74">&#34;sha256:8d80ad266f0138f5eedd03c130279b1059fed471346f1656c5bf0bfa1d3d12ed&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;size&#34;</span>: <span style="color:#ae81ff">7168</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  ]
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>我们来看看其中涉及的具体的 properties:</p>
<ul>
<li><code>schemaVersion</code> 和 <code>mediaType</code> 基本和上文 index 文件中所述一致</li>
<li><code>config</code> descriptor: 指向对应镜像的配置文件，后文对 config 本身会有具体介绍。</li>
<li><code>layers</code> array of descriptors: 指向构成镜像的 layers 列表，layers 之间存在以下关系
<ul>
<li>index 0 位置的 layer 是 image 的 base layer</li>
<li>layers 的顺序符合镜像打包的堆叠顺序</li>
<li>image 最终构成的 filesystem 匹配在一个空的文件夹下 apply 所有的 layers</li>
</ul>
</li>
</ul>
<h2 id="config">Config</h2>
<p><a href="https://github.com/opencontainers/image-spec/blob/main/config.md">image-spec/config.md at main · opencontainers/image-spec</a></p>
<p>对于一个 OCI Image, 我们可以认为它是在特定容器运行时环境下的 layers/changesets 和 运行时参数的组合。</p>
<p>config 文件描述了 OCI Image 运行时所需要的信息，包括环境变量、入口(Entrypoint) 等，主要作用就是告诉工具如何运行指定镜像。 除此之外 config 文件也包含了一些镜像自身的元数据信息，如 author、构建历史等。</p>
<p>我们从上文的 manifest 文件中可以获得对应的 config blobs 的 sha值， 在 blobs 文件夹中查找对应的文件</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#75715e"># 11deb55301007d6bf1db2ce20cb5d12e447541969990af4a03e2af8141ebdbed 是在 manifest blob 中获得的 config 的 digest 值</span>
</span></span><span style="display:flex;"><span>$  cat blobs/sha256/11deb55301007d6bf1db2ce20cb5d12e447541969990af4a03e2af8141ebdbed | jq
</span></span></code></pre></div><p>文件内容如下</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;architecture&#34;</span>: <span style="color:#e6db74">&#34;arm64&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;config&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;ExposedPorts&#34;</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;80/tcp&#34;</span>: {}
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;Env&#34;</span>: [
</span></span><span style="display:flex;"><span>      <span style="color:#e6db74">&#34;PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#e6db74">&#34;NGINX_VERSION=1.25.3&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#e6db74">&#34;NJS_VERSION=0.8.2&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#e6db74">&#34;PKG_RELEASE=1~bookworm&#34;</span>
</span></span><span style="display:flex;"><span>    ],
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;Entrypoint&#34;</span>: [<span style="color:#e6db74">&#34;/docker-entrypoint.sh&#34;</span>],
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;Cmd&#34;</span>: [<span style="color:#e6db74">&#34;nginx&#34;</span>, <span style="color:#e6db74">&#34;-g&#34;</span>, <span style="color:#e6db74">&#34;daemon off;&#34;</span>],
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;Labels&#34;</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;maintainer&#34;</span>: <span style="color:#e6db74">&#34;NGINX Docker Maintainers &lt;docker-maint@nginx.com&gt;&#34;</span>
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;StopSignal&#34;</span>: <span style="color:#e6db74">&#34;SIGQUIT&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;ArgsEscaped&#34;</span>: <span style="color:#66d9ef">true</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;OnBuild&#34;</span>: <span style="color:#66d9ef">null</span>
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;created&#34;</span>: <span style="color:#e6db74">&#34;2023-10-24T22:44:45Z&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;history&#34;</span>: [
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;created&#34;</span>: <span style="color:#e6db74">&#34;2023-10-24T22:44:45Z&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;created_by&#34;</span>: <span style="color:#e6db74">&#34;/bin/sh -c #(nop) ADD file:ef6f078c1e72fcfafb9bfeeff0c1c771219dc2efe34650963106f63d32183b49 in / &#34;</span>
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;created&#34;</span>: <span style="color:#e6db74">&#34;2023-10-24T22:44:45Z&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;created_by&#34;</span>: <span style="color:#e6db74">&#34;/bin/sh -c #(nop)  CMD [\&#34;bash\&#34;]&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;empty_layer&#34;</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// ....
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    {
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;created&#34;</span>: <span style="color:#e6db74">&#34;2023-10-24T22:44:45Z&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;created_by&#34;</span>: <span style="color:#e6db74">&#34;STOPSIGNAL SIGQUIT&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;comment&#34;</span>: <span style="color:#e6db74">&#34;buildkit.dockerfile.v0&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;empty_layer&#34;</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;created&#34;</span>: <span style="color:#e6db74">&#34;2023-10-24T22:44:45Z&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;created_by&#34;</span>: <span style="color:#e6db74">&#34;CMD [\&#34;nginx\&#34; \&#34;-g\&#34; \&#34;daemon off;\&#34;]&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;comment&#34;</span>: <span style="color:#e6db74">&#34;buildkit.dockerfile.v0&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;empty_layer&#34;</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  ],
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;os&#34;</span>: <span style="color:#e6db74">&#34;linux&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;rootfs&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;type&#34;</span>: <span style="color:#e6db74">&#34;layers&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;diff_ids&#34;</span>: [
</span></span><span style="display:flex;"><span>      <span style="color:#e6db74">&#34;sha256:02eecd1b8ebb27cc1f576804168486c4c5c3180d22c50048fdaa546b581adec9&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#e6db74">&#34;sha256:ced4d951e1bfe83757aa885f51c2b48d081e0c23a87af7d01386a62cec11824c&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#e6db74">&#34;sha256:5c23503565e9eb6c31f511199a6f80721b2750d00d174011bcc80248b2b896ff&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#e6db74">&#34;sha256:df91f10d317c00ef9f4ce4359c893a96e621ae6ee532649af2e42b774315e528&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#e6db74">&#34;sha256:1adb92f0b5e98c8db798082a7877732ad1f5e273577b63ac9c1a505f3e8ed61c&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#e6db74">&#34;sha256:047c9d07f2a3abf9e7c546307dc69cb5147b1cb8d2f81b58d36d2be57fea6257&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#e6db74">&#34;sha256:8d80ad266f0138f5eedd03c130279b1059fed471346f1656c5bf0bfa1d3d12ed&#34;</span>
</span></span><span style="display:flex;"><span>    ]
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>这里不再逐个对 <code>config</code> 文件的每个 property 展开描述，对于大部分 properties 大家可以直接根据 key 值推测出准确的含义。只单独说明一下 <code>rootfs</code> 字段。</p>
<p><code>rootfs</code> 字段中的 <code>diff_ids</code>描述了 image 各层次的 layer hash 值，并且也是从 base layer 往上层 layer 的顺序排列。大家可能会发现这个列表中的内容和上文 <code>manifest</code> 中的 layers digest 是一致的，实际上这两者在内容上存在区别: manifest layer digest 可以是根据 layer blob 的 压缩或原始内容进行hash 计算，而 <code>diff_ids</code> 只能是根据未压缩的 tarball 内容进行的计算。通过在 <code>rootfs</code> 中也保存 <code>diff_ids</code> ，除了基本的校验能力，也能使 config blob 本身依赖于 layers, 增强镜像整体数据的一致性。</p>
<h2 id="layers">Layers</h2>
<p>Layers 是构成镜像具体文件系统的 tarball 集合，也是镜像打包的主体内容。通过按 <code>manifest</code> 中 <code>layers</code> 所展示的顺序解压各个 layer blob, 我们需要能够获得与镜像容器<strong>大致</strong>匹配的目录结构。</p>
<p>我们仍然以 Demo 所示的 layers 进行操作演示，对比按顺序解压各个 layer blob 所获得的最终的文件结构和进入容器的文件结构情况。</p>
<p>解压各个 layer blob 到特定文件夹:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>$ mkdir stack_layers
</span></span><span style="display:flex;"><span>$ tar -xvf blobs/sha256/02eecd1b8ebb27cc1f576804168486c4c5c3180d22c50048fdaa546b581adec9 -C stack_layers
</span></span><span style="display:flex;"><span>$ tar -xvf blobs/sha256/ced4d951e1bfe83757aa885f51c2b48d081e0c23a87af7d01386a62cec11824c -C stack_layers
</span></span><span style="display:flex;"><span>$ tar -xvf blobs/sha256/5c23503565e9eb6c31f511199a6f80721b2750d00d174011bcc80248b2b896ff -C stack_layers
</span></span><span style="display:flex;"><span>$ tar -xvf blobs/sha256/df91f10d317c00ef9f4ce4359c893a96e621ae6ee532649af2e42b774315e528 -C stack_layers
</span></span><span style="display:flex;"><span>$ tar -xvf blobs/sha256/1adb92f0b5e98c8db798082a7877732ad1f5e273577b63ac9c1a505f3e8ed61c -C stack_layers
</span></span><span style="display:flex;"><span>$ tar -xvf blobs/sha256/047c9d07f2a3abf9e7c546307dc69cb5147b1cb8d2f81b58d36d2be57fea6257 -C stack_layers
</span></span><span style="display:flex;"><span>$ tar -xvf blobs/sha256/8d80ad266f0138f5eedd03c130279b1059fed471346f1656c5bf0bfa1d3d12ed -C stack_layers
</span></span></code></pre></div><p>export container tar &amp; extract</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>$ docker export <span style="color:#66d9ef">$(</span>docker create nginx:latest<span style="color:#66d9ef">)</span> -o container_fs.tar
</span></span><span style="display:flex;"><span>$ mkdir container_fs <span style="color:#f92672">&amp;&amp;</span> tar -xvf container_fs.tar -C container_fs
</span></span></code></pre></div><p>简单对比两者的文件树差异</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>$ tree ./stack_layers &gt; stack_tree
</span></span><span style="display:flex;"><span>$ tree ./container_fs &gt; container_tree
</span></span><span style="display:flex;"><span>$ diff stack_tree container_tree
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>输出如下
</span></span><span style="display:flex;"><span>1c1
</span></span><span style="display:flex;"><span>&lt; ./stack_layers
</span></span><span style="display:flex;"><span>---
</span></span><span style="display:flex;"><span>&gt; ./container_fs
</span></span><span style="display:flex;"><span>4a5,7
</span></span><span style="display:flex;"><span>&gt; │   ├── console
</span></span><span style="display:flex;"><span>&gt; │   ├── pts
</span></span><span style="display:flex;"><span>&gt; │   └── shm
</span></span><span style="display:flex;"><span>144a148
</span></span><span style="display:flex;"><span>&gt; │   ├── hosts
</span></span><span style="display:flex;"><span>167a172
</span></span><span style="display:flex;"><span>&gt; │   ├── mtab -&gt; /proc/mounts
</span></span><span style="display:flex;"><span>5774c5779
</span></span><span style="display:flex;"><span>&lt; <span style="color:#ae81ff">658</span> directories, <span style="color:#ae81ff">5113</span> files
</span></span></code></pre></div><p>可以看到就 nginx 镜像来说，完全基于 layers tar 包解压缩获得的文件树与 container export tar 解压的文件树存在少量文件差异。这些差异主要是由于容器运行时动态创建或挂载机制生成的，如 <code>hosts</code> 文件是用于容器内部的 DNS 解析使用的， 而 <code>mtab</code> 是一个符号链接，指向挂载的文件系统信息。</p>
<p>当然除此之外还有其他因为容器打包逻辑或是容器运行时造成的内容差异，在这里我们不(从 diff 分析角度) 展开，这里的主要目的是证明layers tar 包与容器 fs 之间的一致性。</p>
<p>接下来我们(以官方文档中的例子)介绍一下如何 from scratch 基于 fs 构造 OCI Image 各层的 tar 包，这有助于帮助我们理解镜像的运行机制。</p>
<h3 id="layer-tarballs-creation">Layer Tarballs Creation</h3>
<h4 id="1-initial-root-filesystem--populate-initial-filesystem">1. Initial Root Filesystem &amp; Populate Initial Filesystem</h4>
<p>先定义好一个基本的 base layer，并且构建好基本的文件结构， 如下</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#75715e"># base layer 包含的文件信息</span>
</span></span><span style="display:flex;"><span>rootfs-c9d-v1/
</span></span><span style="display:flex;"><span>  etc/
</span></span><span style="display:flex;"><span>    my-app-config
</span></span><span style="display:flex;"><span>  bin/
</span></span><span style="display:flex;"><span>    my-app-binary
</span></span><span style="display:flex;"><span>    my-app-tools
</span></span></code></pre></div><p>将 <code>rootfs-c9d-v1</code> 打成 tar 包，便是我们的 base layer tarball, 即 layers 中 index 0 位置的 layer。</p>
<h4 id="2-populate-a-comparison-filesystem">2. Populate a Comparison Filesystem</h4>
<p>准备好 base layer tarball 之后，便是重复往上堆叠 changeset， 每个 changeset 在 OCI Image 中对应一层 layer，在我们使用 Docker 进行镜像打包的过程中即对应 Dockerfile 里的文件变更。</p>
<p>基于上一层进行文件变更的方式如下:</p>
<ol>
<li>
<p>创建一个新的文件夹， copy/snapshot 上一步构建好的 filesystem 到这个文件夹中。这一步有两个要求:</p>
<ol>
<li>使用的 copy/snapshot 工具需要能保留上一层文件的 attributes</li>
<li>对于 copy/snapshot 后的文件的修改不能影响上一层的文件内容</li>
</ol>
<p>这一操作镜像打包工具通常使用如 CoW(copy-on-write)技术或 UnionFS 来更高效地实现。</p>
</li>
</ol>
<h4 id="3-determining-changes">3. Determining Changes</h4>
<p>进行完成变更后，镜像构建工具需要进行修改的文件集合及修改内容的确定。最终确定的信息大概如下</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>Added:      /etc/my-app.d/
</span></span><span style="display:flex;"><span>Added:      /etc/my-app.d/default.cfg
</span></span><span style="display:flex;"><span>Modified:   /bin/my-app-tools
</span></span><span style="display:flex;"><span>Deleted:    /etc/my-app-config
</span></span></code></pre></div><h4 id="4-representing-changes">4. Representing Changes</h4>
<p>以上步骤对应的变更即 changeset 文件, 会被单独打成该 layer 对应的 tarball， 打包的逻辑如下</p>
<ul>
<li>Add/Modify 操作: 对对应的文件及文件夹进行打包</li>
<li>Delete: 对对应的文件/文件夹 标记为 <code>whiteout</code> , <code>whiteout</code> 存在两种标记方法
<ul>
<li>单文件的 whiteout 形式，以 <code>.wh.</code> 作为文件前缀的空文件</li>
<li>标识同文件夹下所有文件都被一处的 opaque whiteout 形式: 被删除文件内容的文件夹下存在 <code>.wh..wh..opq</code> 文件</li>
</ul>
</li>
</ul>
<p>在 apply layer 的时候，<code>whiteout</code> 文件会先被进行处理，即被标记 whiteout 的文件会先进行删除，再应用 layer 的其他变更， 通过顺序保证了镜像打包的时候不会将修改的内容删除。</p>
<p>从这里也可以推导出，如果镜像 layer 中存在删除操作，那么直接根据 tarball 解压获得的文件夹内容会比从容器中 export 的文件内容多出 <code>whiteout</code> 文件</p>
<h1 id="summary">Summary</h1>
<p>以上差不多就是 OCI Image Spec 的大致内容，总的来说就是 OCI 对镜像打包在描述格式、文件结构、内容规范定义了标准，通过遵循这些标准使各个工具的镜像打包得以保持一致。</p>
<p>除了 Image Spec， OCI 还对 Runtime Spec 做出了规范，这部分内容后续在单独的文章中介绍。</p>
]]></content>
		</item>
		
		<item>
			<title>2023 年终总结</title>
			<link>https://yochalyc.com/2024/01/2023-%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93/</link>
			<pubDate>Thu, 25 Jan 2024 21:28:06 +0800</pubDate>
			
			<guid>https://yochalyc.com/2024/01/2023-%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93/</guid>
			<description>&lt;p&gt;2024 的第一个月里，日本发生了地震、海啸，中东地区胡塞武装和美国杠上了，太阳黑子似乎在今年会格外活跃，而我也在接近二月的时候开始动手写起 2023 的年终总结。&lt;/p&gt;
&lt;p&gt;不管是对于我还是对于这个世界，2024 都算不上开了一个好头，那就先骗骗自己是在踩着农历新年的日子对过去的 2023 做一个总结吧。&lt;/p&gt;</description>
			<content type="html"><![CDATA[<p>2024 的第一个月里，日本发生了地震、海啸，中东地区胡塞武装和美国杠上了，太阳黑子似乎在今年会格外活跃，而我也在接近二月的时候开始动手写起 2023 的年终总结。</p>
<p>不管是对于我还是对于这个世界，2024 都算不上开了一个好头，那就先骗骗自己是在踩着农历新年的日子对过去的 2023 做一个总结吧。</p>
<h1 id="第一次">第一次</h1>
<p>2023 年是我虚岁 29 岁的尾巴，在这个我的人生大约已经走过 1/3 的年纪里，还是体验了不少第一次。</p>
<p>第一次做了一整年的笔记</p>
<p>大约是在2021 年中的时候，我开始沉迷于各种笔记软件，从 Notion 到 Roam Research 到 RemNote , Obsidian, LogSeq&hellip;FoMO 情绪让我在各种笔记软件里反复横跳，今年才大概算是把更多地精力放到记录本身，记录下自己一年的轨迹。</p>
<p>说是更多精力投入到记录，但是回头看看，记的多是些零碎的流水帐，除了让自己在写这篇文章的时候能诧异原来今年自己还做了这么些事，似乎也没有沉淀下来什么有价值的东西。</p>
<p>第一次上外教课</p>
<p>由于在 X 上看到某推友晒自己在 Cambly 获得的课时认证书，我也恰好有心改进一下我的哑巴口语，在 2023 年 5 月 18 日那天，一个彻彻底底的 e 人终于鼓起勇气约了第一节外教课， 并且也以每周两次的频率持续到了写下这篇文章的时候。</p>
<p>我很高兴自己在那天踏出了这一小步，尽管和外教老师们就是单纯的尬聊，没有任何系统性的教学或规划，我仍然感觉自己在这半年多的时间里有了不小的进步，值得我写下一篇 《Why you should learn speaking English by talking [to native speakers]》。</p>
<p>第一次听演唱会</p>
<p>2023 年 9 月 10 日，和畅畅到了北京鸟巢听华晨宇的演唱会。</p>
<p>演唱会的氛围很棒，但是我显得有点呆滞。</p>
<p>一是我不是很能欣赏华晨宇的歌，我可以说几乎没有任何音乐素养，单纯是觉得他的歌声不讨我耳朵的喜欢。二是因为华的粉丝们让我有种他们形成了某种奇怪宗教的感觉，我无法理解他们的狂热，也感觉自己的冷静在那个氛围下略显尴尬。</p>
<p>对了，为了赶着第二天回上海上班，我们也第一次体验了在机场的小旅舍过夜。</p>
<p><img src="2023-%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93.assets/IMG_7893.jpeg" alt="IMG_7893"></p>
<p>第一次滑雪</p>
<p>今年滑了两次雪，都是在浙江的室内滑雪场。</p>
<p>一次是参加了稻草人的周末团，到杭州的冰雪大世界体验了双板；另一次是到了乔波的滑雪场，这次尝试了单板。</p>
<p>滑雪很好玩，希望在 2024 年能学会基本的技术，到室外滑雪场过过瘾吧。</p>
<p>第一次听交响音乐会</p>
<p>2023 年的最后一天，我和畅选择了到一个小教堂听交响音乐会来迎接新的一年。</p>
<p>因为是小教堂，交响乐队和听众有不少有趣的互动，也一起在倒数声和返场中迎来了 2024。但是如果让我再做一次选择，我大概会选择在家里颓过最后一个晚上吧，因为&hellip;</p>
<p><img src="2023-%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93.assets/IMG_9114-6191921-6191932.jpeg" alt="IMG_9114"></p>
<p>第一次在现场看羽毛球赛</p>
<p>而且是世界羽联总决赛 🥳</p>
<p>现场看球的感觉确实和电视不同，有热烈到尖叫的观众，有石宇奇的迷妹粉丝在发应援物品，有更真实的球速感受，当然也有失望提前离场的观众。</p>
<p>让我们恭喜小戴拿下 2023 年总决赛女单冠军吧 🏆。</p>
<h1 id="旅游">旅游</h1>
<p>2023 年的旅游次数比起往年似乎少了一些，江浙沪的各种必要穿梭抛开不算，中长途的旅行只有两趟吧。</p>
<p>一趟是为了华晨宇的演唱会到了北京，在北京的时候恰好赶上绵绵的秋雨，只在北海公园浅浅地溜达了一圈，吃了四季民福，就也不剩下什么时间了。</p>
<p>第二趟是跟稻草人去了珠峰。畅国庆还要加班，因此这也是我第一次自己跟稻草人的团。</p>
<p>我挺骄傲自己在海拔 5400+ 的地方还能活蹦乱跳，过几年我大概还是回想念并且忘掉头几天高反地难受劲冲向西藏吧。</p>
<p><img src="2023-%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93.assets/IMG_8605.jpeg" alt="IMG_8605"></p>
<h1 id="输入">输入</h1>
<h2 id="书">书</h2>
<p>说来惭愧，2023 年初的时候雄心壮志要养成看书的习惯，一年下来看书的时间和数量都不尽如我意。</p>
<p>正在在 2023 年里看完的书大概只有 Kubernetes in Action 这本大部头，尽管是英文版，但是作者用的词汇算是亲民了。</p>
<h2 id="电影">电影</h2>
<p>2023 恰好看了 10 部电影，质量都还算不错。</p>
<ul>
<li>流浪地球2: 值回票价的商业片，特效精彩，剧情也算过硬。</li>
<li>进击的巨人/最终季/完结篇/前篇 + 后篇： 哪个天才想的命名方法。算是给这步经典动漫收了个还可以的尾巴，和鲁鲁修或者文豪野犬类似的救世主逻辑吧。</li>
<li>中国乒乓之绝地反击: 剧情没啥硬伤，也没带太多广告（我现在对电影的要求已经这么低了吗hhh）</li>
<li>不止不休: 很不错的社会电影，张颂文老师演技在线，白客也成功证明自己的可能性。</li>
<li>蜘蛛侠/纵横宇宙: 没想到在 3202 年了还有电影能有这么出彩的画面特效！漫画风、水墨风以及抽帧等手法着实是让我眼前一亮。</li>
<li>子弹列车: 所谓的巧合和误会，看起来是命运，实际上都是必然的因果交织。把多个主角的故事线很自然地衔接在一起。</li>
<li>极限职业: 来自韩国的喜剧片，（按我的偏见）我记得韩国以前常以灾难片和社会电影闻名，这部喜剧片倒是也值得推荐。</li>
<li>封神/朝歌风云: 乌尔善导演真聪明啊，妲己吸引男观众，一众质子男模们的胸肌吸引女观众，真有你的。</li>
<li>辛德勒的名单: 是外教 Tony 推荐的电影，是斯皮尔伯格拍摄的一部老电影了。讲述了二战期间德国商人如何帮助犹太人的故事，和钢琴家从两个视角讲述了那个时代的故事。</li>
</ul>
<h2 id="动漫">动漫</h2>
<p>今年看的动漫有 22 部，感觉比起以前我追的番已经不少了，没想到还能达到这个数。</p>
<p><img src="2023-%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93.assets/image-20240125224408968.png" alt="ALT"></p>
<p>数量太多，也没耐心一一介绍，硬要从里头选一部推荐的话，那么:</p>
<p>拾荒者统治 🥳 出自网飞的作品，扎实的画面和剧情，新颖的想象，如果看腻了日式动漫，那么它会是你很好地调节口味的选择。</p>
<h2 id="其他">其他</h2>
<p>今年的上下班路上一直有在坚持听博客和看一些文章。但是像前面说的，总结过少，也没有什么系统性的整理，只能说希望自己在某天遇到问题的时候还能想起来自己看过某个相关的内容吧。</p>
<h1 id="工作">工作</h1>
<p>在贵司又度过了一年。</p>
<p>事儿上，我比较欣慰自己在这年里还是做了一些实际的产出吧。经手了大约四五个模块的事情，也还在持续迭代着两个开发项目。</p>
<p>人嘛。</p>
<p>算了，就这样吧。</p>
<h1 id="生活">生活</h1>
<p>和畅畅走过了第五个年头。</p>
<p>尽管不想自己度过传统老中人的一生，戴上老中人的枷锁，但是我们也确实到了该考虑下一步的时候了。</p>
<p>或许早就应该考虑了，只是我还是会因为无法调节到满意的结果恐惧吧。</p>
<p>希望 2024 年我们都能得到自己想要的答案，希望答案里都还有我们两个人。</p>
<h1 id="2024">2024</h1>
<p>所以到了该展望一下 2024 的时候了。</p>
<p>2023 年年初我曾经想过给自己的生活也按 Q 定几个 OKR，现在一看 Q1 的 OKR 都还留着白。</p>
<p>2024 大概也还是会在相同的白日梦中假装努力吧：</p>
<ul>
<li>早睡</li>
<li>学好英语（或许还有日语）</li>
<li>看书</li>
<li>开源</li>
<li>多整理，多输出</li>
</ul>
<p>祝大家新年快乐。</p>]]></content>
		</item>
		
		<item>
			<title>2023国庆川藏游图片纯享版</title>
			<link>https://yochalyc.com/2023/10/2023%E5%9B%BD%E5%BA%86%E5%B7%9D%E8%97%8F%E6%B8%B8%E5%9B%BE%E7%89%87%E7%BA%AF%E4%BA%AB%E7%89%88/</link>
			<pubDate>Tue, 10 Oct 2023 13:55:23 +0000</pubDate>
			
			<guid>https://yochalyc.com/2023/10/2023%E5%9B%BD%E5%BA%86%E5%B7%9D%E8%97%8F%E6%B8%B8%E5%9B%BE%E7%89%87%E7%BA%AF%E4%BA%AB%E7%89%88/</guid>
			<description></description>
			<content type="html"><![CDATA[<h1 id="成都">成都</h1>
<h2 id="芳草街">芳草街</h2>
<p><img src="/uploads/2023/10/5E6E062E-2A96-4153-86EB-A248CA440A5B_1_105_c.jpeg" alt="Alt text"></p>
<p><img src="/uploads/2023/10/5E6E062E-2A96-4153-86EB-A248CA440A5B_1_105_c.jpeg" alt="Alt text"></p>
<h2 id="玉林">玉林</h2>
<p><img src="/uploads/2023/10/917C53E1-3B17-430E-9B49-B2408F8632B3_1_105_c.jpeg" alt="Alt text"></p>
<p><img src="/uploads/2023/10/6DEAACCB-EF6D-4480-9CBC-42C22DDFAC0D_1_105_c.jpeg" alt="Alt text"></p>
<p><img src="/uploads/2023/10/4465901C-2B1A-434A-9B11-0540FA7EDF32_1_105_c.jpeg" alt="Alt text"></p>
<h2 id="九眼桥">九眼桥</h2>
<p><img src="/uploads/2023/10/D8843828-AB5B-4A7D-9CDF-BDC080E270C9_1_105_c.jpeg" alt="Alt text"></p>
<p><img src="/uploads/2023/10/E697BBD3-69E5-4351-9DEE-C9922875E766_1_105_c.jpeg" alt="Alt text"></p>
<h1 id="西藏">西藏</h1>
<h2 id="拉萨">拉萨</h2>
<h3 id="大昭寺">大昭寺</h3>
<p><img src="/uploads/2023/10/3EBC48BC-C1A7-484F-A004-9B2360AE110E_1_105_c.jpeg" alt="Alt text"></p>
<p><img src="/uploads/2023/10/0BC501F6-5A77-4850-AFE0-3610F6757740_1_105_c.jpeg" alt="Alt text"></p>
<p><img src="/uploads/2023/10/F510BDFD-9E52-410B-92A6-43F5715E4B83_1_102_o.jpeg" alt="Alt text"></p>
<p><img src="/uploads/2023/10/F8BB4567-6D42-4928-86EA-ADC7E78CE5E3_1_102_o-1024.jpeg" alt="Alt text"></p>
<p><img src="/uploads/2023/10/65C6C0D3-7D5D-4E18-920A-28ED4260C558_1_102_o-1024.jpeg" alt="Alt text"></p>
<h3 id="八廓街">八廓街</h3>
<p><img src="/uploads/2023/10/B775B82F-AEE4-45BA-8E49-5F0186B7B7A5_1_102_o.jpeg" alt="Alt text"></p>
<p><img src="/uploads/2023/10/0374707E-6BE6-4536-BEB9-25A71BDAE720_1_102_o.jpeg" alt="Alt text"></p>
<h3 id="布达拉宫">布达拉宫</h3>
<p><img src="/uploads/2023/10/D1C5ACC6-7334-43F1-B7BA-B4E316FDB085_1_102_o.jpeg" alt="Alt text"></p>
<p><img src="/uploads/2023/10/e39ba93e27f6d2d331035cc933ce666a.jpg" alt="Alt text"></p>
<h2 id="羊卓雍措">羊卓雍措</h2>
<p><img src="/uploads/2023/10/96757DFC-95AC-4803-8C1F-F6CB1BE94A7A_1_102_o.jpeg" alt="Alt text"></p>
<p><img src="/uploads/2023/10/65F91F2D-59DC-44B6-A1DC-33CC6074E663_1_102_a.jpeg" alt="Alt text"></p>
<p><img src="/uploads/2023/10/278B250C-0CFF-45CC-8144-7BC9B8327A1E_1_102_o.jpeg" alt="Alt text"></p>
<p><img src="/uploads/2023/10/ab9ad3d7a0b08d2aef1f342ac2130d6f.jpg" alt="Alt text"></p>
<p><img src="/uploads/2023/10/dc05445375c8230dd162dd32eee1676e.jpg" alt="Alt text"></p>
<h2 id="卡洛拉冰川">卡洛拉冰川</h2>
<p><img src="/uploads/2023/10/27D6EF30-EF31-4E55-B590-897B94379494_1_102_o.jpeg" alt="Alt text"></p>
<h2 id="扎什伦布寺">扎什伦布寺</h2>
<p><img src="/uploads/2023/10/735128EB-CF18-4DCD-8724-0A96A40F86D6_1_102_o.jpeg" alt="Alt text"></p>
<p><img src="/uploads/2023/10/526501F2-93B8-4500-A24B-DB0D42E86F12_1_102_o.jpeg" alt="Alt text"></p>
<p><img src="/uploads/2023/10/AAE26229-0882-4CEA-86C6-E27BF694391D_1_102_o.jpeg" alt="Alt text"></p>
<p><img src="/uploads/2023/10/84b49e6221a474d6bc6a6071a47fd7ca.jpg" alt="Alt text"></p>
<p><img src="/uploads/2023/10/e7ff7b67a582624983ef2f75940c31e2.jpg" alt="Alt text"></p>
<h2 id="珠穆朗玛峰">珠穆朗玛峰</h2>
<h3 id="路上">路上</h3>
<p><img src="/uploads/2023/10/C363F21E-D170-4BB5-B664-F2F69B6B1243_1_105_c.jpeg" alt="Alt text"></p>
<p><img src="/uploads/2023/10/2FB8474F-E469-4CA7-BED8-0BB095304B7A_1_105_c.jpeg" alt="Alt text"></p>
<p><img src="/uploads/2023/10/5840D3CB-2F60-4D31-8298-532E661EF0D8_1_105_c.jpeg" alt="Alt text"></p>
<p><img src="/uploads/2023/10/8B4D3431-870B-4584-B6C5-990831422A68_1_105_c.jpeg" alt="Alt text"></p>
<p><img src="/uploads/2023/10/2f94d284591d28f28cecdd0a5caadd77-1024x683.jpg" alt="Alt text"></p>
<h3 id="大本营">大本营</h3>
<p><img src="/uploads/2023/10/E46CEF85-92EC-40CF-8DE7-A8073BEC082F_1_105_c.jpeg" alt="Alt text"></p>
<p><img src="/uploads/2023/10/1987B095-C4AD-47BF-A2E2-E650134871D3_1_105_c.jpeg" alt="Alt text"></p>
<p><img src="/uploads/2023/10/3AF2D5CE-7AB0-4455-8CFA-1E71D572E94C_1_105_c.jpeg" alt="Alt text"></p>
<p><img src="/uploads/2023/10/7A970FFE-D945-456C-B801-4192DC0AE581_1_105_c.jpeg" alt="Alt text"></p>
<p><img src="/uploads/2023/10/IMG_8587.jpeg" alt="Alt text"></p>
<p><img src="/uploads/2023/10/IMG_8563.jpeg" alt="Alt text"></p>
<h2 id="萨迦寺">萨迦寺</h2>
<p><img src="/uploads/2023/10/0F80E57D-90D8-44E3-8544-D29A0DC62D6B_1_105_c.jpeg" alt="Alt text"></p>
<p><img src="/uploads/2023/10/7555BD05-D6E3-4E51-9A0A-6E6D305037F1_1_105_c.jpeg" alt="Alt text"></p>
<p><img src="/uploads/2023/10/660AAE0A-8675-442B-BAB6-8345C9146D14_1_105_c.jpeg" alt="Alt text"></p>
<p><img src="/uploads/2023/10/7FA5F687-9A37-4657-8C05-7DBDACDDC114_1_105_c.jpeg" alt="Alt text"></p>
<p><img src="/uploads/2023/10/9228624E-40F2-4C35-86F5-C8C5DCD3AD87_1_105_c.jpeg" alt="Alt text"></p>]]></content>
		</item>
		
		<item>
			<title>AWS Savings Plans 介绍一 ──  基本知识</title>
			<link>https://yochalyc.com/2023/05/aws-savings-plans-%E4%BB%8B%E7%BB%8D%E4%B8%80-%E5%9F%BA%E6%9C%AC%E7%9F%A5%E8%AF%86/</link>
			<pubDate>Thu, 04 May 2023 14:02:41 +0000</pubDate>
			
			<guid>https://yochalyc.com/2023/05/aws-savings-plans-%E4%BB%8B%E7%BB%8D%E4%B8%80-%E5%9F%BA%E6%9C%AC%E7%9F%A5%E8%AF%86/</guid>
			<description>&lt;p&gt;最近几个月我有相当一部分精力投入在云上(主要是 AWS)成本管理相关的工作上，涵盖了从资源管理到数据分析的内容。在这过程中接触了不少新的概念和计费方法，也专门为了理清其中的关系和相关的技术支持同学开过几次会，其中比较折腾人的应该就是 AWS Savings Plans 这个收费模型。因此准备写两篇文章来理清其中的一些知识。&lt;/p&gt;
&lt;p&gt;这两篇文章会介绍如下内容：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AWS Savings Plans 相关的概念&lt;/li&gt;
&lt;li&gt;AWS 平台上如何更好地观察 Savings Plans 使用情况&lt;/li&gt;
&lt;li&gt;AWS Savings Plans 相关的数据结构及字段说明&lt;/li&gt;
&lt;li&gt;包含 AWS Savings Plans 的简单数据分析场景指导&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;作为第一篇，本文主要会进行 AWS Savings Plans 基本概念的介绍，以及进行相关的成本管理时可能接触到的小知识。&lt;/p&gt;</description>
			<content type="html"><![CDATA[<p>最近几个月我有相当一部分精力投入在云上(主要是 AWS)成本管理相关的工作上，涵盖了从资源管理到数据分析的内容。在这过程中接触了不少新的概念和计费方法，也专门为了理清其中的关系和相关的技术支持同学开过几次会，其中比较折腾人的应该就是 AWS Savings Plans 这个收费模型。因此准备写两篇文章来理清其中的一些知识。</p>
<p>这两篇文章会介绍如下内容：</p>
<ul>
<li>AWS Savings Plans 相关的概念</li>
<li>AWS 平台上如何更好地观察 Savings Plans 使用情况</li>
<li>AWS Savings Plans 相关的数据结构及字段说明</li>
<li>包含 AWS Savings Plans 的简单数据分析场景指导</li>
</ul>
<p>作为第一篇，本文主要会进行 AWS Savings Plans 基本概念的介绍，以及进行相关的成本管理时可能接触到的小知识。</p>
<h1 class="wp-block-heading" id="基本介绍">基本介绍</h1>
<p>AWS Savings Plans 是本文介绍的主要对象，因此我相对详细地介绍在实际成本管理中需要了解的部分。</p>
<blockquote class="wp-block-quote">
  <p>
    <cite>[AWS Savings Plans ](https://docs.aws.amazon.com/cur/latest/userguide/cur-sp.html)是一种<strong>预付费</strong>定价模型。 用户通过预先承诺一定的计算用量（按小时计算），在一定期限（通常是 1 或 3 年）内获得折扣。</cite>
  </p>
</blockquote>
<p>对于以上定义，我们拆散解释:<br>
&ldquo;预付费定价模型&rdquo; 指的是作为用户需要在使用服务之前预先支付费用，后续按照周期和用量从该预付款项中直接扣除。<br>
通常这种收费模式会给用户带来一定的折扣，但也要求用户承诺一定的用量或期限。对于 AWS Savings Plans 来说，所作出的承诺就是</p>
<ol>
<li>期限: “一定期限（通常是 1 或 3 年）”内使用相关服务</li>
<li>使用量: 在使用期限内承诺了<strong>按小时计算的使用量</strong></li>
</ol>
<p>在以上承诺范围内，对应 Savings Plans 的服务可以获得相应的折扣。<br>
Savings Plans 提供了 All Upfront(全预付)、Partial Upfront(部分预付）、No Upfront(无预付)三种选项，预付款金额由高到底，折扣力度也由大到小，基本的内涵是一样的，本文为了帮助理解仅以 All Upfront 付款选项为例进行介绍。</p>
<p>从实际交易的角度来说，实际上就是用户在指定 &ldquo;期限&rdquo; 内的相关资源 &ldquo;用量&rdquo; 按折扣后的价格计算金额后预先支付给 AWS ，AWS 按小时从该预付款中拿走该“小时费”。小时费是固定的，这意味着：</p>
<ol>
<li>如果用户实际用量不足承诺小时用量，AWS 仍会拿走固定的金额</li>
<li>如果用户实际用量超过了承诺用量，AWS 对于除了固定金额，对超出用量部分会按需收费</li>
</ol>
<p>那么这种收费模式对于用户来说分别有什么好处和坏处呢？<br>
对于用户来说最明显的好处是获得了折扣，降低了自身的计算成本，Savings Plans折扣有多种收费模式（参考 <a href="https://aws.amazon.com/savingsplans/">Cloud Cost Savings – Savings Plans – Amazon Web Services</a> 本文不展开描述）用户可以根据自身场景和架构选择合适的模式，有一定灵活性。同时 AWS 相关的技术支持人员会帮助用户制定 Savings Plans 购买方案（包括在哪个帐号上购买、购买期限，用量协商…），这也能帮助用户调整架构，提升 SavingsPlans 的利用率。<br>
但相对的，由于我们承诺了用量和期限，我们的使用也就受到了相应的限制，这个限制包括对供应商（AWS）的依赖，对实际用量调整的阻碍。以及因为预付款带来的现金流影响。</p>
<h1 class="wp-block-heading" id="收费优先级">收费优先级</h1>
<p>需要注意的是 ，Savings Plans 的所有者是 <a href="https://aws.amazon.com/account/">AWS 帐号(Account)</a> 而不是<a href="https://aws.amazon.com/organizations/">组织(org)</a>粒度的，同时 Savings Plans 是有扣款优先级的，扣款优先级主要与资源类型和帐号有关，关系如下</p>
<ol>
<li>优先使用当前帐号的 SavingsPlans, 覆盖完当前帐号的 SavingsPlans 后可以使用其他帐号的共享的 SavingsPlans</li>
<li>在满足 1 的情况下，折扣力度最大的资源类型（<a href="https://docs.aws.amazon.com/savingsplans/latest/userguide/sp-applying.html">不同类型的资源的 SavingsPlans 折扣力度不同</a>）优先使用 SavingsPlans 进行覆盖</li>
</ol>
<p>根据以上收费优先级和折扣力度差异，我们可以得到一个提高 Savings Plans 折扣力度的 tips:</p>
<blockquote class="wp-block-quote">
  <p>
    <cite>使用部分不实际运行（或运行较少）Savings Plans 相关实例的帐号来购买 Savings Plans，使 Savings Plans 能优先按照折扣力度进行覆盖。</cite>
  </p>
</blockquote>
<p>当然以上 tips 还需要结合公司实际业务情况来决策，比如是否需要优先保证某帐号的 Savings Plans 覆盖。</p>
<h1 class="wp-block-heading" id="aws-上的相关分析工具">AWS 上的相关分析工具</h1>
<p><a href="https://docs.aws.amazon.com/cost-management/latest/userguide/what-is-costmanagement.html">AWS Cost Management</a> 是专门用于提供 AWS 成本洞察的服务，其中的 <code>Savings Plans</code> 子目录下提供了 Savings Plans 相关服务，辅助我们进行分析的主要是 <code>Inventory</code>、<code>Utilization report</code>、<code>Coverage report</code> 几项<figure class="wp-block-image size-large is-resized"></p>
<p><img src="/uploads/2023/05/image-525x1024.png" alt="Alt text"> </figure></p>
<ul>
<li>Inventory: 展示了<strong>当前帐号</strong>下的 Savings Plans 情况，包括状态(Status)、Type、承诺用量(Commitment), 起止时间</li>
<li>Utilization report: 主要用于展示 Savings Plans 的使用情况(<strong>用户对 Savings Plans 的承诺成本的使用情况</strong>)和获得收益。可以结合筛选条件查看 Savings Plans 的 花费、覆盖资源对应的原始花费、节省金额（前两者相减）。使用率越高意味着 Savings Plans 得到了越有效的使用，使用率不足 100% 说明没有完全利用 Savings Plans 。<figure class="wp-block-image size-large is-resized"></li>
</ul>
<p><img src="/uploads/2023/05/image-1-1024x628.png" alt="Alt text"> </figure></p>
<ul>
<li>Coverage report: 主要展示 Savings Plans <strong>对用户使用的符合条件的资源成本</strong>的覆盖情况。提供了按 服务(Service) 和资源类型 拆解进行分析的能力。计算方式是<br>
Coverage = (按需价格计算下被 Savings Plans 覆盖的资源的价格)/(按需价格计算下被 Savings Plans 覆盖的资源的价格 + 按需价格计算下未被 Savings Plans 覆盖的价格)<br>
因此 Coverage 越高，即 Savings Plans 覆盖的资源越多，即节省了越多钱。</li>
</ul>
<p>一句话总结是，在日常分析的时候可以主要结合 Utilization report 和 Coverage report 使用，目标是使 <strong>Utilization 和 Coverage 都尽可能高</strong>，前者代表我们对 Savings Plans 使用的充分程度，后者代表 Savings Plans 为当前帐号资源的节省情况。</p></p>
<p>总结一下，本文主要介绍 AWS Savings Plans 的基本模型和常用的知识。下篇会介绍 Savings Plans 数据分析中涉及的概念、字段说明，并举几个典型场景说明用法。</p></p>
<h1 class="wp-block-heading" id="refs">Refs</h1>
<ul>
<li><a href="https://aws.amazon.com/savingsplans/">Cloud Cost Savings – Savings Plans – Amazon Web Services</a></li>
<li><a href="https://docs.aws.amazon.com/savingsplans/latest/userguide/what-is-savings-plans.html">What are Savings Plans? - Savings Plans</a></li>
<li><a href="https://docs.aws.amazon.com/savingsplans/latest/userguide/sp-applying.html">Understanding how Savings Plans apply to your AWS usage - Savings Plans</a></li>
</ul>]]></content>
		</item>
		
		<item>
			<title>2022 年终总结</title>
			<link>https://yochalyc.com/2023/01/2022-%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93/</link>
			<pubDate>Wed, 11 Jan 2023 14:25:36 +0000</pubDate>
			
			<guid>https://yochalyc.com/2023/01/2022-%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93/</guid>
			<description>&lt;p&gt;去年又是白白给 DigitalOcean 交钱的一年，就连年报都迟到两周，我大概是没救了）&lt;br&gt;
跟风做一下年度总结。&lt;br&gt;
2022 年对我来说是变化特别大的一年，尽管对自己仍然不是特别满意，但是体验到很多不一样的东西总是好的。&lt;/p&gt;</description>
			<content type="html"><![CDATA[<p>去年又是白白给 DigitalOcean 交钱的一年，就连年报都迟到两周，我大概是没救了）<br>
跟风做一下年度总结。<br>
2022 年对我来说是变化特别大的一年，尽管对自己仍然不是特别满意，但是体验到很多不一样的东西总是好的。</p>
<h1 id="工作">工作</h1>
<p>除了所负责的领域(DevOps)不变，不管是公司还是工作内容都完全不同了。</p>
<p>今年 4 月份从蚂蚁集团离职了。<br>
离职的时候所负责的是“研发洞察”相关的工作，这是一个新兴领域，还没有成熟的发展路径指导演进；这也是一个门槛“不高”的领域，大多数技术同学都能对如何做研发相关的度量发表自己的见解（虽然这个见解应该大概率是对某种度量的批判 hhh）。我也一样，我对研发洞察的发展路径没有足够的领域知识来支撑清晰的规划，能确定的是我对前司方向的不认可，再加上自己处在局中对技术的摄入选择也感到迷茫，因此犹犹豫豫的提出了离职。</p>
<p>新的公司是 PingCAP, 简称贵司。入职的时间是 4.28，本来想的是 gap 两周多少学点东西，但当时在南京需要 14 天 7 检（大概是这个数？），再加上家附近的省外核酸点效率极差，每天下午被核酸的两个多小时撕成碎片，实际上也没学多少。<br>
入职贵司前对贵司实际上是抱有极大的期待的。创业公司 + 开源 + 基础设施，可以说贵司当时就是我心中的白月光，因此虽然当时本身想换个领域但还是在纠结后选择了贵司。<br>
入职之后老实说有点失望，从个人的感觉来看贵司已经带点大公司病，比如会议多（是真的多啊，一天三四个会家常便饭），比如难以对齐但不得不看起的 OKR…</p>
<p>好在内容上有不少自己需要学习的东西，倒也不算踩了大坑。目前我在做的仍是工程效率相关的东西，负责的是某（代码跟屎一样！）的 BMS 系统和 TiDB 云上效率相关的内容。以用户视角来和云厂商打交道，K8S， github action 对于被大厂养坏的我来说都是很新奇的东西。</p>
<h1 id="生活">生活</h1>
<p>今年是和畅畅在一起的第四年，一起做了很多事，去了很多地方。<br>
拍了海马体，养了小靛（一只斗鱼，夏天时死了 😭），做了很多次菜，一起去看了只此青绿，参加了稻草人的城市活动。<br>
去了无锡、苏州的几个园林，虽然记不住 ；<br>
去了青岛冲浪，住了这辈子住过的最贵的酒店，虽然畅急性肠胃炎了；<br>
去了湖南吃了好多次茶颜悦色和黑色经典，虽然刚到张家界就因为疫情不得不跑毒（还好跑成功了）；<br>
还有常住的杭州、上海和南京，也想着法子去了几个没去的地方。</p>
<p>今年也是我从杭州到上海的第一年，找了两天房子，以为找到了梦中情房，实际住进来了发现也只是干净一些的老破小，该有的问题还是会有。</p>
<p>今年还是被疫情影响的第三年，也是体感被限制得最严的一年。因为上海被封的 3 个月先在杭州入了职，在上海也得 2 天 1 检。<br>
到了年底风向又大转弯，完全放开了，我也终于在年底成了小 🐑 人。</p>
<h1 id="其他">其他</h1>
<blockquote>
<p>“我最热衷的浪费时间的方式，就是研究各种效率软件。”</p>
</blockquote>
<p>今年花了好多时间研究效率工具，包括 RoamResearch、Obsidian、LogSeq、Readwise Reader…最终在 Obsidian 和 LogSeq 中摇摆了几个月，强迫自己在 Obsidian 定了下来。趁着这几个软件还没把我想吐槽的点完善好，或许最近我该抓紧撸几篇博客出来。</p>
<p class="has-black-color has-text-color">
  参加了贵司的 Hackathon，其实是个不太有趣的项目，火急火燎的代码写的也和设计完全不同，但是拿了二等奖，奖金给畅买了 NuPhy 键盘（其实是我自己手痒想试试哈哈）。
</p>
<p>同时开始用 NeoVim 作为主力 IDE，还是很爽的。</p>
<h1 id="4-个关键字">4 个关键字</h1>
<p>写着写着回头一看，发现自己 2022 年依然没有什么值得说道的成长，我真的太菜了。<br>
所以决定今年给自己整个新活：定下几个关键字，每天不管多少，总得做些和这些关键字（之一）搭边的内容。定的关键字是下面几个</p>
<h2 id="开源">开源</h2>
<p>尽管班还是一直在加，游戏也还是一直在玩，但是今年必须克服恐惧（被嫌菜）参与开源项目，能持续性地给开源社区做贡献。</p>
<h2 id="读书">读书</h2>
<p>2022 看了不少文章，但是书似乎只有一两本（甚至都记不准）。2023 年每天睡前看那么几分钟吧，也不限于专业书籍。</p>
<h2 id="输出">输出</h2>
<p>不能白交钱了！（不是</p>
<h2 id="英语">英语</h2>
<p>2022 我的英语口语依然垃圾，好在上下班路上坚持听 Podcast,听力还是体感上有些进步的。2023 年一定要克服社恐，想办法在口语上有自己满意的进步（至少不会因为口语再被外企拒掉或压 offer 吧…</p>
<p>最后，虽然我并不想主动把这篇文章分享出去，还是祝大家 2023 年万事顺意。</p>]]></content>
		</item>
		
		<item>
			<title>HHKB Hybrid Type-S──年轻人的第一把HHKB开箱及首日测评</title>
			<link>https://yochalyc.com/2022/06/hhkb-hybrid-type-s%E5%B9%B4%E8%BD%BB%E4%BA%BA%E7%9A%84%E7%AC%AC%E4%B8%80%E6%8A%8Ahhkb%E5%BC%80%E7%AE%B1%E5%8F%8A%E9%A6%96%E6%97%A5%E6%B5%8B%E8%AF%84/</link>
			<pubDate>Tue, 07 Jun 2022 18:12:06 +0000</pubDate>
			
			<guid>https://yochalyc.com/2022/06/hhkb-hybrid-type-s%E5%B9%B4%E8%BD%BB%E4%BA%BA%E7%9A%84%E7%AC%AC%E4%B8%80%E6%8A%8Ahhkb%E5%BC%80%E7%AE%B1%E5%8F%8A%E9%A6%96%E6%97%A5%E6%B5%8B%E8%AF%84/</guid>
			<description>&lt;/p&gt;
&lt;p&gt;其实早在年初的时候，我就给自己立下了一个 flag ── 只要我拿到offer，就奖励自己一把HHKB。现在入职P社已经过去了1个多月，我也终于咬咬牙（趁着618和公司福利）买了HHKB Hybrid Type-S。&lt;/p&gt;&lt;/p&gt;</description>
			<content type="html"><![CDATA[</p>
<p>其实早在年初的时候，我就给自己立下了一个 flag ── 只要我拿到offer，就奖励自己一把HHKB。现在入职P社已经过去了1个多月，我也终于咬咬牙（趁着618和公司福利）买了HHKB Hybrid Type-S。</p></p>
<p>我先简单说明一下我的购买价格，或许可以为同样犹豫很久的同学提供一点参考。我是在天猫618的时候蹭着优惠和满1500减120，以2444的价格买入的，附赠蓝牙接收器和数据线（除了该款其他版本似乎不附赠这两样，但部分版本价格上优惠更多）。</p></p>
<p>发货地点就在杭州，但由于端午三天及中间恰好有点事，我的HHKB在蜂巢躺了5天才被我抱回家。我本身也不是啥键盘专家，对手感也不太挑剔，这里只是趁着高兴，做一个简单的开箱和首日使用的测评。</p> <figure class="wp-block-image size-full"></p>
<p><img src="/uploads/2022/06/image-1654621893840-e1654622918412.png" alt="Alt text"> <figcaption>还算严实的包装</figcaption></figure> </p> <figure class="wp-block-image size-large"><img src="/uploads/2022/06/1654623001374-1024x576.jpg" alt="Alt text"><figcaption>赠品展示</figcaption></figure> </p> <figure class="wp-block-image size-large"><img src="/uploads/2022/06/1654623099013-1024x532.jpg" alt="Alt text"></figure> </p> <figure class="wp-block-image size-large"><img src="/uploads/2022/06/1654623184849-1024x493.jpg" alt="Alt text"></figure> </p> <figure class="wp-block-image size-large"><img src="/uploads/2022/06/1654623229309-1024x472.jpg" alt="Alt text"></figure> </p></p>
<p>正面展示，入手之前觉得这个电池仓设计很丑，本来是想买Pro2 Type-S的，奈何各家店铺都断货。实际上到手以后的感觉就是键盘整体很小巧，电池仓也没有丑的那么扎眼。除了电池仓和字体略微有点大以外，整体的感觉很&quot;复古&quot;，是我个人比较喜欢的素雅的风格。</p> <figure class="wp-block-image size-large is-resized"></p>
<p><img src="/uploads/2022/06/1654623257496-1024x476.jpg" alt="Alt text"> </figure> </p></p>
<p>背面展示，四角有防滑贴很好地固定键盘（但是支撑脚打开后还是有点滑hhhh）。电池仓下方的四方形小盖子下藏着DPI开关。</p> <figure class="wp-block-image size-large"></p>
<p><img src="/uploads/2022/06/1654623244259-1024x673.jpg" alt="Alt text"> <figcaption>各国语言说明书，看起来不太环保hhhh</figcaption></figure> </p> <figure class="wp-block-image size-large"><img src="/uploads/2022/06/1654623273718-1024x419.jpg" alt="Alt text"><figcaption>最后是工作完后盖上另外买的防尘罩，提升了一点点颜值</figcaption></figure> </p></p>
<p>外观上没有太多值得评价的点，今天使用了一天，手感上有些许体会。</p></p>
<p>在HHKB之前我主要使用两款机械键盘，一款是Filco的87键的双模忍者圣手二代红轴；另一款是WASD的青轴，同样是87键；平时直接使用MAC的蝴蝶键盘的频率也不低。我以下的测评主要是和以上几款键盘对比得到的感受。</p></p>
<p>首先是键位上，在购买之前就有小伙伴劝退我，因为我有频繁切换工作键盘的需求，可能会比较不适应。实际上搭配<a href="https://karabiner-elements.pqrs.org/">Karabiner-Elements</a>{.ek-link}进行简单(左ctl+hjkl改方向键，Esc键和~键的兼容，以及自己一直有Cap-locks和ctrl键交换的习惯)的魔改后，上手还是相对比较容易的。比较嗝应的点，一个是原本一些修饰键+方向键的用法（比如<a href="https://magnet.crowdcafe.com/?utm_source=help">Magnet – Window manager for Mac</a>的快捷键）还没法形成熟练的肌肉记忆，一个是切换回87键后Delete键容易按成 &ldquo;|&rdquo; 键。但就第一天的适应情况来说我还是比较满意的，基本有信心在短时间内能完全上手。</p></p>
<p>其次是手感上，老实说我并没有觉得有多惊艳，比较大的感觉就是“扎实”。这款HHKB按压下去的感觉，就是“按键的磨砂表面严丝合缝地紧贴着笔直地向下滑动”，但这个“紧贴”感并不会带来手感上的厚重，相反很是轻松。敲了一天HHKB再回头敲Filco和WASD，就感觉这两款机械键盘的按键很“不安分”，对比下轻微地晃动感变得明显，按下的感觉更活泼，反馈也更强烈。这点上应该也是萝卜青菜各有所爱吧。</p></p>
<p>最后是声音上，这款HHKB的声音也手感的描述的表现比较一致，每次按键按下的声音就像“塑料磨砂表面相互摩擦”带来的轻微的“沙沙”声，与机械键盘撞击及回弹产生的“哒哒”声有比较明显的区别，（比起我的红轴和轻轴）静音效果也确实是很好。</p></p>
<p>总的来说，作为奖励自己的正反馈，HHKB确实让我产生了一种“想更多地敲击键盘”的兴奋感，希望自己适应之后能明显地提升生产力吧~</p></p>]]></content>
		</item>
		
		<item>
			<title>对A厂工程效能的一点看法</title>
			<link>https://yochalyc.com/2022/05/%E5%AF%B9a%E5%8E%82%E5%B7%A5%E7%A8%8B%E6%95%88%E8%83%BD%E7%9A%84%E4%B8%80%E7%82%B9%E7%9C%8B%E6%B3%95/</link>
			<pubDate>Sun, 15 May 2022 17:15:21 +0000</pubDate>
			
			<guid>https://yochalyc.com/2022/05/%E5%AF%B9a%E5%8E%82%E5%B7%A5%E7%A8%8B%E6%95%88%E8%83%BD%E7%9A%84%E4%B8%80%E7%82%B9%E7%9C%8B%E6%B3%95/</guid>
			<description>&lt;p&gt;这段日子经历职业生涯经历了不小的变化，在纠结了许久之后选择了从(递增的)工号已逾 35 万的大厂（简称 A 厂）跳到了百&amp;quot;缺&amp;quot;待兴的贵司（简称 P 厂）。作出这个选择的很大一个原因是自己对“变化”的渴望 ── 渴望巨大的变化能给自己更多的感悟和成长。&lt;/p&gt;</description>
			<content type="html"><![CDATA[<p>这段日子经历职业生涯经历了不小的变化，在纠结了许久之后选择了从(递增的)工号已逾 35 万的大厂（简称 A 厂）跳到了百&quot;缺&quot;待兴的贵司（简称 P 厂）。作出这个选择的很大一个原因是自己对“变化”的渴望 ── 渴望巨大的变化能给自己更多的感悟和成长。</p>
<p>“遗憾”但是又“幸运”的是在 P 厂我负责的领域和在 A 厂一样，都是研发效能/工程效率领域，尽管入职还不到两周，距离离职也已快过去一个月，但如我所愿在对比中我也有一些对 A 厂研发效能的想法，趁着感觉还没完全消散，忙里偷闲地进行一些暴论。</p>
<p><strong>免责声明:</strong> 以下内容带有强烈的主观推断，有极大可能结论是错误的，都是出于个人的经验和感受进行的总结。我很欢迎大家直接地指出我的错误(如果有人看的话)，也期望能有人和我进行深入地探讨，但对于没有价值的贬低我会直接删除。</p>
<p>也不知道是从谁口中开始传的，大家都说研发效能的三要素是“人”、“流程”、“工具”，仔细一琢磨，想说的几个点大概也可以归到这三个方面上，因此我也就不打算免俗，从工具开始说起吧。</p>
<p>工具大概是明面上最容易感受到差距的地方。到了 P 厂简单了解后就不得不感慨，<strong>A 厂的效能工具确实很齐全</strong>。从需求/缺陷管理、设计、文档、IDE（及插件）、到代码托管、持续交付、发布管控、数据洞察、工单支持&hellip;这个领域内所有我能想到不能想到的边边角角都已经被插上了 A 厂自研的大旗。</p>
<p>得益于完备且相对统一的工具体系，<strong>A 厂在质量效率上确实有着很高的起点</strong>。速度上来说，（在不出错、不需要审批、且已经被工具调教熟练的前提下）A 厂的发版能享受到完整的一条龙服务。质量上来说，IDE 插件保证编码期间的错误提示，持续交付中的各种规则和组件换着花样伺候开发，线上变更三板斧配合各种应急措施（当然还有故障复盘和追则），保证了蚂蚁极低的线上故障率和极短的修复时间。上万名研发、数千个仓库、每周小几千个 MR、每分钟上百条流水线&hellip;以统一的工具体系支撑起这个量级的多种服务需求，不能算是小的成就。</p>
<p>不仅如此，“统一”一定程度上也意味着“标准”。<strong>A 厂定义了自己工具体系下的“标准”</strong>，这个“标准”即代表使用标准、流程标准，也代表评价标准、管理标准，只要适应了这个标准的语境，员工们就能很快享受到这个“标准”所带来的红利：除了上文所说的显著的质量效率提升，还有沟通术语的一致、团队切换在工作方式适应上的低成本、宏观度量数据的采集和清洗低成本&hellip;</p>
<p>这一套体系能够形成如今这么一个宏大的布局，我想最主要大概有这么几个原因：</p>
<ul>
<li>
<p>首先最直白的原因便是<strong>人多</strong>。效能部的人多，A 厂的研发效能部在巅峰时期有 180 多个人（还是 130 多个，记不清了），抛开巅峰时期不谈也从两年前的 50 余人发展到现在的 80 余人，涵盖了开发、产品、解决方案、技术支持、运营等角色，这还不谈体量更大的中间件团队和技术风险团队（作为对比 P 厂的工程效率部在本文撰写期间 2022 年 5 月中旬为 7 个人）。人多是因也是果，人多是工具体系得以全面覆盖的基础，工具体系全面带来的业务也需求并带来了更多的人。</p>
</li>
<li>
<p>其次就是<strong>公司大</strong>，公司大了<strong>业务也就广</strong>了。某个 P8 指点过我，公司也是市场黑暗森林里的一个孤立群体，保持静止最终只会带来死亡。A 厂发展到这个阶段，锁涵盖的业务范围不是我这种小 P 可以简单数清的，而 A 厂的组织方式又以中台为主，这么复杂的业务体系收束在一个部门下，确实也不是一些简单的工具可以支持的。</p>
</li>
<li>
<p>再有就是 A 厂对<strong>校招生</strong>的偏爱，这是我校招进公司时 HR 就提过的。校招生的一个特点就是好调教，一张白纸，A 厂说啥是规范流程啥就是规范流程，再加上 A 厂的校招生水平不低（我算是其中极差的了），这么些各大院校里的尖子生能快速地体会和掌握 A 厂的标准，快速地成为 A 厂的生产力。校招生的高比例和不知是否存在的青睐，也导致他们占据了 A 厂中高层的大多数。“人无法知道自己没看过的东西”，统一语境下成长起来的校招生们，大概对于标准不会有太多的敌意。</p>
</li>
<li>
<p>（KPI 导向，但实际上我入职后的 KPI 对我并没有太多约束，因此我也没有太多想法）</p>
</li>
</ul>
<p>但是，<strong>质量和效率</strong>就是研发效能的全部吗？</p>
<p>很惭愧，在 A 厂两年多的时间里我基本没有吃过我们自己的狗食，我没办法从用户的角度来评判 A 厂的研发效能并给出答案。因此我也只能从一个研发效能部研发人员的视角谈谈我的看法。</p>
<p>如上面所说，其实我代表的就是（我认为）比较典型的，A 厂效能部研发值得诟病的一方面: <strong>我们即不是自己的用户，也离业务太过遥远。</strong></p>
<p>我起先负责的是代码分析能力的建设，代码分析作为持续交付 CI 建设的一环，以“任务”的粒度进行分析，而在当时的我眼里，对这些“任务”进行优化和扩展就是我的工作，我最高频地接触用户的场景，就是在自研的对内的工单咨询系统上进行答疑。在这个阶段，也偶尔有用户跟我提出了需求，而我和 mentor 讨论的时候，往往要关注的是这些需求的“性价比”── 大部分需求是较定制化的，便找理由搪塞了过去，最终我所实现的大部分较“大”的需求，实际上都是我们自己 YY 出来的，看起来牛逼轰轰的需求。</p>
<p>我想这其中就有“统一”的原因，研发效能听起来像是服务于研发的团队或工程，但当我们开始抽象业务的复杂度，实现这个领域的统一时，我们有了很充分的拒绝服务的理由。当我们有了充足的人手，我们的分工也可以足够细，细到我这种螺丝钉始终对业务懵懵懂懂。</p>
<p>我并不知道 A 厂有多少校招生和当时的我一样对自己创造的价值感到困惑，我也不清楚当公司壮大、业务复杂后，这种大一统的中台部门是否是一个必然的选择，如果是的话这个选择带来的业务疏远感应该如何处理（异或是根本不必处理？）。就在上周我和我在 P 社的老板简单地聊到了这个话题，他提到了在他的老东家（另一个大厂）同样要面对需求的性价比问题，他会要求团队里的同学紧贴业务创造价值。这可能是一个很直接且有效的方式吧，我也希望在 P 社成长的过程中自己能有更清晰的答案。</p>
<p>我颇感不适的第二点，就是这<strong>全套自研的基础设施</strong>。</p>
<p>如上面所说，不能否认自研工具的复杂度和它们对 A 厂复杂业务的支撑价值，但<strong>自研又封闭地开发环境在我看来损失了不少成长的可能。</strong></p>
<p>一是足够活跃的社区反馈和讨论（尽管 A 厂内部也建立起了技术论坛，但在用户与产品负责人论坛上的讨论方式让我觉得更接近于“吐槽与公关”的关系），我们服务的主体是技术人员，技术人员本身对生产力工具应该是有天然的热情和兴趣的，缺少健康的沟通渠道以及足够的社区用户让自研仿佛闭门造车。</p>
<p>二是公司内的语境限制了与业界先进实践的接轨，“公司内的语境”指的是当基础设施建设得足够齐全，就相当于在公司内营造了自己的上下文，后续的很多上层建筑的建设工作都是基于这个上下文提出的。这些上层建筑累积得越厚，可能离业界的先进经验就越远，吸取地成本也就越高。</p>
<p>三是当自研工具体系覆盖了大部分领域，但又没有足够的灵活性时，A 厂的研发人员在工具选择上面临着尴尬的局面：自研的产品并不能完美地匹配团队的特性，但“又不是不能用”，这种情况下大部分团队会选择将就使用自研的产品（上面的“社区反馈产品”也是这个例子，A 厂的代码托管平台的 issue 功能鲜有人用。）。</p>
<p>这又带来了另一个问题，即<strong>流程上的不规范</strong>。是的，上文我们说到了 A 厂的流程是“统一的、标准的”，但统一和标准不同于规范。</p>
<p>**当工具不能很好地满足个人、团队的特性，而流程标准又要求使用这个工具时，工具的使用在一定程度上就变成了负担，负担的累加导致了不规范。**在我参与的第二个项目（某内部洞察项目）中，我和另一个同事曾就内部工具数据对多个（内部）团队进行数据分析工作，所得到的分析结论大部分都因“团队流程不规范”导致的脏数据不可用。</p>
<p>如果你问我，质量和效率就是研发效能的全部吗？我无法回答，我也愿意相信 A 厂仍然能够又好又快地交付产品，但作为曾经 A 厂的效能研发人员，我无法认可自己在那种工作模式下产生的价值。</p>]]></content>
		</item>
		
		<item>
			<title>2019-2021总结</title>
			<link>https://yochalyc.com/2022/04/2019-2021%E6%80%BB%E7%BB%93/</link>
			<pubDate>Sat, 16 Apr 2022 13:51:27 +0000</pubDate>
			
			<guid>https://yochalyc.com/2022/04/2019-2021%E6%80%BB%E7%BB%93/</guid>
			<description>&lt;p&gt;2022年已经过去了1/3，距离我的&lt;a href=&#34;https://yochalyc.com/2021/07/29/book/%E4%B9%A6%E7%B1%8D%E6%8E%A8%E8%8D%90%E3%80%8A%E7%BC%96%E7%A0%81%EF%BC%9A%E9%9A%90%E5%8C%BF%E5%9C%A8%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%BD%AF%E7%A1%AC%E4%BB%B6%E8%83%8C%E5%90%8E%E7%9A%84%E8%AF%AD%E8%A8%80/&#34;&gt;上一篇文章&lt;/a&gt; 也又过去了近9.5个月，我才终于开始进行年终总结。实际上这篇文章与其说是年终总结，更接近于我在蚂蚁的工作总结──昨天(2022.04.15)是我在蚂蚁集团研发效能部的lastday，只是这两年在工作之外也经历了很多，因此就一起简短地记录一下吧。&lt;/p&gt;</description>
			<content type="html"><![CDATA[<p>2022年已经过去了1/3，距离我的<a href="https://yochalyc.com/2021/07/29/book/%E4%B9%A6%E7%B1%8D%E6%8E%A8%E8%8D%90%E3%80%8A%E7%BC%96%E7%A0%81%EF%BC%9A%E9%9A%90%E5%8C%BF%E5%9C%A8%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%BD%AF%E7%A1%AC%E4%BB%B6%E8%83%8C%E5%90%8E%E7%9A%84%E8%AF%AD%E8%A8%80/">上一篇文章</a> 也又过去了近9.5个月，我才终于开始进行年终总结。实际上这篇文章与其说是年终总结，更接近于我在蚂蚁的工作总结──昨天(2022.04.15)是我在蚂蚁集团研发效能部的lastday，只是这两年在工作之外也经历了很多，因此就一起简短地记录一下吧。</p>
<h2 id="工作">工作</h2>
<p>我在蚂蚁的工作，是一段“恶始恶终”的工作: 从起错花名开始(我的花名是“黎荍(qiáo)”，荍这个字在此之前我也不认识)，在不尽人意的成果中结束。这里也就只对一些微小的工作做一些总结。往事不可谏，来者犹可追，希望我痛定思痛，痛改前非，非常成功，功在千秋，秋天还能回来看看蚂蚁的老伙计们。</p>
<h3 id="linka">LinkA</h3>
<p>LinkA是我从实习时开始接手并独立维护的代码分析系统，直到2020年交接给其他团队。在这期间进行了包括但不限于以下功能点在内的设计和实现: 多语言分析模块、远端执行任务能力、增量代码分析的能力、自动CR机器人、代码认知复杂度&hellip;</p>
<p>从结果上来看，这些功能里能实际上线的都取得了还不错的成果:</p>
<ul>
<li>多语言分析模块对系统整体进行了还算合理的重构，也对各种分析工具的执行流程进行了恰当的抽象，后续各种静态代码分析工具的接入基本能保持在3人日内完成，多语言用户对相关能力也颇有赞词。</li>
<li>远端执行能力很好地保证了系统整体的稳定性和资源池的动态扩缩容，搭配上执行模式热切换的能力，在此能力上线后系统便没出现过不可用故障。</li>
<li>&hellip;</li>
</ul>
<p>比较遗憾的是，我个人认为实现后略有成就感，也是对系统业务长期发展较重要的的几个功能因为组织架构的调整没能最终上线:</p>
<ul>
<li>自动CR机器人: 通过Git hook进行任务的触发，结合系统提供的增量分析能力在用户提交的变更代码行上标注对应的分析结果，并给出整体的总结报告。这个功能点将<a href="https://www.dynatrace.com/news/blog/what-is-shift-left-and-what-is-shift-right/">质量检测左移</a>到了代码提交阶段，能提供更精确和心智负担更小的修复指导。好在后续接管代码分析功能的团队也提供了类似的能力。</li>
<li>认知复杂度: 不同于<a href="https://zh.wikipedia.org/wiki/%E5%BE%AA%E7%92%B0%E8%A4%87%E9%9B%9C%E5%BA%A6">圈复杂度</a>实际度量的复杂度可以认为是“测试用例构造的复杂度”，认知复杂度是基于Sonar提出的<a href="https://sonarsource.com/docs/CognitiveComplexity.pdf">CognitiveComplexity.pdf</a>论文实现的，用于度量代码理解难度的指标。这个指标实际上是LinkA向整体性、系统化的代码分析平台演进的一个探索。</li>
</ul>
<p>总的来说，负责LinkA系统的开发实际上是一段蛮开心的体验，我可以完全自主地决定系统发展方向的设计和探索，相关的需求也有一定的难度; 每天都有不少的工单需要支持，不少人厌恶工单对工作节奏的影响，但工单常常能让我体会到我产出的价值，也偶尔让我对系统方向有新的感悟。</p>
<p>从反面来说，也是因为是个人维护的系统，在进行需求分析和系统设计的时候，很多情况都是由我拍脑袋决定的，这也是我期间一直觉得自己所缺失的较要紧的一个能力。</p>
<h3 id="研发数据建模">研发数据建模</h3>
<p>2020年5月，公司收购了原来的<a href="https://www.sourcebrella.com/">源伞</a>，代码分析相关的能力也都要归属到他们团队下。</p>
<p>当时老板给我提出了几个选择: 一个是跟着LinkA系统到新团队下面，另一个是跟着他做研发洞察项目，而研发洞察项目下面又有三个主要的方向: 研发洞察平台开发、效能领域的数据挖掘和研发数据建模。</p>
<p>对这几个我的考虑大概如下: 在代码分析方面实际上我并没有积累足够的底层知识，源伞团队有深厚的技术积累，也有其自带的工程平台，若跟着LinkA变更团队，我可能会有较长时间需要进行系统的交替和下线工作（况且我老板对我还不错）。而研发洞察的几个方向，就我当时的理解来看，平台开发并不是研发洞察的核心能力，底层的数据及模型才是。因此我给老板的答复是我希望能留在当前团队，进行数据相关的工作。老板表示数据建模是当前团队较缺少的能力，也愿意给我时间进行学习和落地。就这样我开始接受我负责的第二个项目: 研发数据建模，实际工作职责也从Java开发变成了数据开发。</p>
<p>研发洞察是一个相当年轻的学科，尽管相关话题热度不低，但业界并没有成熟的工程实践，已有的一些度量指标建议和方法论指导很多也缺少可靠的事实支持。尽管到我离职为止，我在这个领域也有了两年的探索和沉淀，但我仍不觉得自己能较清晰地把握住它未来的发展发向和潜在价值，我仍需要更多的思考和沉淀，而这方面的经历也值得我另外写一篇文章进行总结，在本文我也是简单陈列主要工作。</p>
<p>经我设计和实现的模型，支撑过业务进行决策的有人员产出分、人员交付质量分、人员技术影响力模型、应用质量分、效能健康度模型等。其中人员产出分相关的业务背景复杂(研发产出随岗位、时间、流程的变化)，为了适应业务情况而进行的模型开发工作量也较大(多段的分箱打分机制、线性规划计分、后分层事后分析校验分数合理性&hellip;)。</p>
<p>负责研发洞察项目期间我始终处于自我怀疑的状态，一个是怀疑自己是否有持续在学习，一个是怀疑自己的作品是否有生产出价值。我也在不断地与我负责的第一个项目进行对比，我也始终感觉，相比较于数据分析与数据开发，我还是更希望能进行开发工作。</p>
<h3 id="蚂蚁之后">蚂蚁之后</h3>
<p>在2021年年初，其实心底就萌生了跳槽的打算了，但由于当时刚提名晋升，同时也想再给自己一点时间在研发洞察这个领域上探索，因此当时并不急着准备。到了8月份左右，心里越来越意识到自己在当前的工作上并不开心，也越来越有对技术沉淀的焦虑感，便下定了要离职的决心，而实际开始准备就到了2022年春节前不久。</p>
<p>开始面试前我问了自己思考了很多次自己想要的是什么，最终我也没能给自己一个准确的答案，我不清楚自己期待的领域，不清楚自己热情的技术栈，也不知道自己最终想成为什么样的人，这让我有点沮丧。但我知道自己不想继续在国内的互联网大厂卷了，最近两年的工作让我对工作内容之外的许多学科都产生了浓厚的兴趣，我希望有时间能进行更多维度的能力提升。</p>
<p>同时我开始思考我的人生观，我开始觉得，既然最终都是要死亡，人生是无绝对意义的。所谓的意义，其实是每个人自己甘愿“被欺骗”的理由，而我自己拿来欺骗自己的人生意义就是：我想体验更多的东西。这也成为我下家选择的一个判断条件，我期望去能带给我更多体验的公司。</p>
<p>最终，我拿到了字节数据安全团队、PingCAP工程效率团队、微软Azure App Service团队的offer。在后两者中纠结了许久，基于上述的判断条件，我决定接受PingCAP的offer。尽管微软的WLB和业务对我来说很有吸引力，可能涉及的技术栈也是我很欠缺的，但我相信PingCAP能带给我更多的技术热情和“更多不同的体验”。</p>
<h2 id="生活">生活</h2>
<p>2019年的9月13日，也是当年的中秋节，我和畅畅在一起了。</p>
<p>这两年多里我们一起走过了国内的很多地方:(内蒙(海拉尔、满洲里、阿尔山、额尔古纳&hellip;)、黑龙江(哈尔滨)、天津、山西(太原、大同、朔州、繁峙县)、江苏(南京)、浙江(杭州、湖州、舟山、宁波&hellip;)、福建(泉州)、广东(深圳)、湖北(武汉)、上海&hellip;)，吃了很多美食，认识了很多人，也有不少的遗憾：对于一起经历的很多，一直没有很好地进行记录，由于疫情，我们也一直没机会一起出国旅游&hellip;</p>
<p>我也认识了很多朋友，我要前往上海最不舍得的也是他们，好在最亲密的朋友基本都是打球认识的，尽管不在蚂蚁，我们也还能保持联系(希望)。</p>]]></content>
		</item>
		
		<item>
			<title>书籍推荐《编码：隐匿在计算机软硬件背后的语言》</title>
			<link>https://yochalyc.com/2021/07/%E4%B9%A6%E7%B1%8D%E6%8E%A8%E8%8D%90%E7%BC%96%E7%A0%81%E9%9A%90%E5%8C%BF%E5%9C%A8%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%BD%AF%E7%A1%AC%E4%BB%B6%E8%83%8C%E5%90%8E%E7%9A%84%E8%AF%AD%E8%A8%80/</link>
			<pubDate>Thu, 29 Jul 2021 15:51:02 +0000</pubDate>
			
			<guid>https://yochalyc.com/2021/07/%E4%B9%A6%E7%B1%8D%E6%8E%A8%E8%8D%90%E7%BC%96%E7%A0%81%E9%9A%90%E5%8C%BF%E5%9C%A8%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%BD%AF%E7%A1%AC%E4%BB%B6%E8%83%8C%E5%90%8E%E7%9A%84%E8%AF%AD%E8%A8%80/</guid>
			<description>&lt;p&gt;介绍一本极其适合入门的计算机书籍，《编码:隐匿在计算机软硬件背后的语言》( Code:The Hidden Language of Computer Hardware and Software)，“适合入门”在这里完全没有贬意，实际上经过科班教育(尽管上学时并不认真🙈)及两年工作后再读这本书，我仍然会在阅读过程中得到启迪。&lt;/p&gt;</description>
			<content type="html"><![CDATA[<p>介绍一本极其适合入门的计算机书籍，《编码:隐匿在计算机软硬件背后的语言》( Code:The Hidden Language of Computer Hardware and Software)，“适合入门”在这里完全没有贬意，实际上经过科班教育(尽管上学时并不认真🙈)及两年工作后再读这本书，我仍然会在阅读过程中得到启迪。</p>
<h2 id="简介">简介</h2>
<p>作者真正意义上从电子以及最基础的二进制原理出发，介绍了一台真正的计算机是如何一步一步诞生的，以及之后又是如何优化成现代我们所使用的计算机及使用的自然语言。</p>
<h2 id="你能学到什么">你能学到什么</h2>
<ul>
<li>现代计算机软硬件的发展历史</li>
<li>计算机软硬件发展过程中涉及的设计原理及推导过程</li>
<li>部分现代计算机设计(如编码方式、文件系统等)的入门介绍</li>
<li>…</li>
</ul>
<h2 id="优点">优点</h2>
<ul>
<li>浅显易懂，适合纯外行了解计算机运行原理，对于更专业的内容点到即止但足够对相关概念有一定了解</li>
<li>有趣，尽管读的是译本，但也感受到了作者思路清晰，行文流畅</li>
<li>基于问题分析，全书隐隐约约在重复着：向读者抛出问题-解决问题的过程，如果在阅读过程中带着思考分析问题，对于计算机运行原理大概会有更深入了解</li>
</ul>
<h2 id="缺点">缺点</h2>
<ul>
<li>约20章之前的内容较连贯，缺乏计算机基础的人不建议跳着看</li>
<li>(对于我来说)现代计算机在硬件上的优化较没有吸引力</li>
</ul>
<h2 id="相关资料">相关资料</h2>
<ol>
<li><a href="https://www.zhihu.com/question/26829883">知乎:《编码》讲了什么</a></li>
<li><a href="https://github.com/7-sevens/Developer-Books/blob/master/Operating_System/%E7%BC%96%E7%A0%81%EF%BC%9A%E9%9A%90%E5%8C%BF%E5%9C%A8%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%BD%AF%E7%A1%AC%E4%BB%B6%E8%83%8C%E5%90%8E%E7%9A%84%E8%AF%AD%E8%A8%80.pdf">随便在github上找的资源🙈</a></li>
<li><a href="https://book.douban.com/subject/4822685//">豆瓣</a></p></li>
</ol>]]></content>
		</item>
		
		<item>
			<title>和滴滴司机的闲聊</title>
			<link>https://yochalyc.com/2021/07/%E5%92%8C%E6%BB%B4%E6%BB%B4%E5%8F%B8%E6%9C%BA%E7%9A%84%E9%97%B2%E8%81%8A/</link>
			<pubDate>Sun, 18 Jul 2021 03:41:30 +0000</pubDate>
			
			<guid>https://yochalyc.com/2021/07/%E5%92%8C%E6%BB%B4%E6%BB%B4%E5%8F%B8%E6%9C%BA%E7%9A%84%E9%97%B2%E8%81%8A/</guid>
			<description>&lt;p&gt;打车的时候偶尔会跟司机聊聊天，这周五的时候借着滴滴被下架的事情，闲聊了一下他的看法。&lt;/p&gt;</description>
			<content type="html"><![CDATA[<p>打车的时候偶尔会跟司机聊聊天，这周五的时候借着滴滴被下架的事情，闲聊了一下他的看法。</p>
<ul>
<li>滴滴下架对司机有没有影响？目前还基本没什么影响，公司也没有发针对司机们的公告。</li>
<li>司机能否签约多个打车平台？不能，或者说没必要。
<ul>
<li>签约了其他平台被发现了是会有惩罚的，比如减少派单，旺季还好，淡季就基本接不到客了。</li>
<li>大多数司机不是专职的，可能也相对没那么多精力接多个平台的单(因为平台不给交社保等等或者说交的很少。通常都有自己的主业，且不是在智力密集型公司上班，工资也不高(几千水平)，通过下班时间兼职司机每月能多几千收入，对于他们来说是很可观的。)</li>
</ul>
</li>
<li>为什么出了这个事后不选择签约其他平台？因为滴滴虽然烂(司机原话是: 我们也经常骂滴滴坑)，但是其他平台更差。这表现在几个方面:
<ul>
<li>平台活跃度，滴滴在这块属于独角兽，其他平台保不齐什么时候就没了。</li>
<li>定价，平台定价高了用户不满意，定价低了司机不满意，合理的定价能保证用户和司机的活跃度，滴滴在这块的拿捏相对比其他平台要好。</li>
<li>福利，这是对于司机来说滴滴远好于其他平台的地方，主要体现在提现上。大多数平台要求司机到30天后才能提现当前收入，这就导致他们要垫付很多油钱，房租及日常开销也会带来压力等等；而滴滴2至3天就可以(不知道理解是否有误，但总体意思是滴滴提现周期远小于其他平台)，出于信任及怕麻烦他们通常也就是一周提一次，不会过于频繁。</li>
</ul>
</li>
<li>是否比兼职外卖好？从司机的角度来看，各有优劣：
<ul>
<li>成本上，滴滴司机成本较高，兼职外卖成本低，但外卖风险相对高些。</li>
<li>收入上两者其实差不多(这个司机也有同事在兼职送外卖。</li>
<li>辛苦程度上，不好比较。外卖小哥虽然风吹日晒爬楼梯，但是时间相对自由，用餐上厕所等都比较方便，乐观点想走路爬楼梯也顺带着锻炼了身体。司机虽然坐在车内也有空调，但是因为车位问题，上厕所和吃饭都很麻烦，让停车的地方没有厕所或小餐馆，有厕所或小餐馆的地方往往不让停车。通常得随缘在合适的时间遇到合适的地方才能解决（因为打司机没法决定调度的方向，因此在固定的地方用餐和如厕也往往不可行）。</li>
</ul>
</li>
</ul>
<p>在这之前我也和其他司机聊过些零碎的话题，这里整理一两个还有印象的</p>
<ul>
<li>疫情的影响。这个话题是一个健谈的北方出租车司机和我提起的，按他的说法，在疫情前他是不屑于在平台上接单的。杭州算是一个旅游还算可以的城市，疫情前他靠着接送路边的游客，介绍一下熟悉的饭馆商店(在他看来他认为这些店确实有口皆碑，至于是不是收了商家回扣大家各自判断)，就足够他一天的生计。随着疫情的到来，游客渐渐少了，他也不得不接起了平台的生意。当然在我看来不完全是疫情的影响，手机打车确实也就在这几年发展迅速，至少对我来说优先级高于沿路拦车。(这个健谈的大哥说high了错过了转完路口，直接把表按掉了表示是他的锅，不好计价了直接按现在显示的钱算吧hhhh)</li>
<li>乘客的素质。前不久被司机吐槽了我司某员工 😥，以下简称A吧，好巧不巧他们还挺有缘老撞到一起。司机说A喜欢在车上吹牛逼，听语气多少是个小领导(司机：应该高不到哪去，是个组长吧好像)，不过这倒也不是他讨厌的点，主要是有过那么一两件事的过节。
<ul>
<li>一件事是某次送A去机场，A非要司机按他指他指定的路走（背景: 杭州近两年赶工修地铁，司机提醒过A该路可能无法通行），最后真吃了瘪只能回头跟导航走。</li>
<li>一件事是在雨天的时候接到了A的单，A一上车就将湿漉漉的伞丢在了座位上，司机又心疼车，又觉得影响了其他乘客，更觉得A素质不行了。</li>
<li>因此在之后某次司机送A到某打车不便的景区，A要求他晚上回来接他并愿意付调度费时，他直接搪塞了过去hhhhh。</li>
</ul>
</li>
</ul>
<h4 id="相关资料">相关资料:</h4>
<ol>
<li><a href="http://www.cac.gov.cn/2021-07/04/c_1627016782176163.htm">关于下架滴滴出行的通报</a></li>
</ol>]]></content>
		</item>
		
		<item>
			<title>关于思维方式的一次碎碎念</title>
			<link>https://yochalyc.com/2021/05/%E5%85%B3%E4%BA%8E%E6%80%9D%E7%BB%B4%E6%96%B9%E5%BC%8F%E7%9A%84%E4%B8%80%E6%AC%A1%E7%A2%8E%E7%A2%8E%E5%BF%B5/</link>
			<pubDate>Sun, 30 May 2021 15:18:24 +0000</pubDate>
			
			<guid>https://yochalyc.com/2021/05/%E5%85%B3%E4%BA%8E%E6%80%9D%E7%BB%B4%E6%96%B9%E5%BC%8F%E7%9A%84%E4%B8%80%E6%AC%A1%E7%A2%8E%E7%A2%8E%E5%BF%B5/</guid>
			<description>&lt;p&gt;最近这段时间一直在思考关于思维的事。&lt;/p&gt;
&lt;p&gt;做的事情多了，越来越感觉到细节之中蕴藏着宇宙。工作中偶尔需要使用一些看似浅显的手段，在调研时都能发现系统的方法论（向伟大的前人致敬）。这当然是个好事，如果能掌握实现中的每个细节和原理，在交流现有方案时就是无懈可击的；这也是很大的挑战，我现在做事还有太多想当然，每个想当然可能都意味着失败，我需要训练自己去掌握对细节的敏感/直觉，我以为这需要大量的实践才能掌握。&lt;/p&gt;</description>
			<content type="html"><![CDATA[<p>最近这段时间一直在思考关于思维的事。</p>
<p>做的事情多了，越来越感觉到细节之中蕴藏着宇宙。工作中偶尔需要使用一些看似浅显的手段，在调研时都能发现系统的方法论（向伟大的前人致敬）。这当然是个好事，如果能掌握实现中的每个细节和原理，在交流现有方案时就是无懈可击的；这也是很大的挑战，我现在做事还有太多想当然，每个想当然可能都意味着失败，我需要训练自己去掌握对细节的敏感/直觉，我以为这需要大量的实践才能掌握。</p>
<p>巧的是前不久部门里的9做了一次分享，分享的内容叫做“结构化思维”，分享只有15分钟，没有很深入，但足够介绍这个概念。那之后我也没有对这个概念进行进一步的研究(近期实在有点忙，等晋升完和手头这本书看完，是有打算系统训练一下的），只是我的瞎想又有了一些新的素材。大佬的结构化思维展现为树状且可能是深度遍历的思考方式和输出方式，以我的理解来举个例子，<a href="https://zh.wikipedia.org/wiki/%E5%85%AD%E4%BD%95%E6%B3%95">3W1H(Why，What，Where，How，也有5W1H分析法)</a>是结构化很强的分析方式，在处理需求和输出方案时能给自己和别人清晰地指导。我瞎想的结构化思维最终应该是网状的结构，效果应该是除了上述的思考方式外思维中万物有联系，为了区分，我就管我胡诌的这个思维方式叫网状思考，再举个例子，如计算机语言为什么由01组成？从能力上看01能够描述足够多的信息（参考摩斯电码和布莱叶盲文），从实现上看计算机按组成可拆解到加法器，而加法器的实现又可以关系到电路，电路的开闭正可以对应01，电路的设计离不开逻辑运算和电流（实际上逻辑运算中有先贤对哲学的探索故事），电流又涉及电解和原子结构…。这中间可能有没考虑到或者偏颇的内容，但重点是这个过程将计算机的实现和物理、数学、化学乃至政治、历史关联在了一起，这是将多个不同领域的结构化的树进行了关联。</p>
<p>话接前文我觉得对于掌握事物的所有细节困难且需要大量实践的积累，而网状思考的思维应该是能有效减少这个实践需求量的。这里的“有效”我觉得有两个内涵，一是针对“直觉”的训练有了系统的方法论指导，这个方法论就是结构化思维，使对细节的掌握不是胡乱的枚举，而是成体系的梳理；二是对事物知识的掌握更加牢靠，思维的每个点都可能和其他点之间有着联系，记忆和逻辑推理合理且自然。</p>
<p>说到这我产生了一个臆测，部分基础学科中似乎常有泰斗无法接受新兴观点的事情，比如玻尔的原子模型就不被爱因斯坦接受，甚至有这么一句玩笑话“不是新的观点被接受，而是反对的人逐渐都去世了”，可能这些“老学究”也并不是不能接受新的理论，而是其思维体系/网络过于强健且“合理”，以至于新理论可能让其一辈子构建的体系崩塌，因此但凡新理论有一点瑕疵，那么都是难以接受的。而学院的年轻人，可能知识尚未构成自洽的网络，新的理论看起来有希望拨开传统中尚不能解释的雾霾，这便是值得尝试的。</p>
<p>以上目前也还只是我的又一次瞎想，从这乱七八糟的行文也可以看出来我也并没有什么体系的思维，但据切实学习过结构化思维方式的师兄介绍，至少这部分内容是较有价值的思维工具，等真的进行部分了解了，我再回头看看这篇博客是不是又在放屁吧hhh。</p>]]></content>
		</item>
		
		<item>
			<title>FY20-FY21感悟</title>
			<link>https://yochalyc.com/2021/04/fy20-fy21%E6%84%9F%E6%82%9F/</link>
			<pubDate>Mon, 26 Apr 2021 16:17:40 +0000</pubDate>
			
			<guid>https://yochalyc.com/2021/04/fy20-fy21%E6%84%9F%E6%82%9F/</guid>
			<description>&lt;p&gt;这阵子恰逢新旧财年更替，21财年的工作收尾总结，22财年(其实现在叫CY21自然年)伊始，手上堆着不少规划要做，也忙着准备一件还算喜庆的事，再加上MHR发售(&lt;del&gt;可能这才是主要原因吧&lt;/del&gt;)，导致前阵子刚立的flag东倒西歪，心里感到有些愧疚，写点乱七八糟的感悟凑篇水文。&lt;/p&gt;
&lt;p&gt;我其实是个挺愚钝的人，也并不太擅长“感悟”东西，工作近两年其实并没有觉得自己在这种软实力方面有很大的提升。幸运的是我老板是个资历深厚且愿意和我简单聊聊这种事的人(在沟通绩效时)，因此我的为数不多的感悟大多是老板直接或间接启发得到的，也希望自己在之后的工作中能时常想起，时常遵循吧。&lt;/p&gt;</description>
			<content type="html"><![CDATA[<p>这阵子恰逢新旧财年更替，21财年的工作收尾总结，22财年(其实现在叫CY21自然年)伊始，手上堆着不少规划要做，也忙着准备一件还算喜庆的事，再加上MHR发售(<del>可能这才是主要原因吧</del>)，导致前阵子刚立的flag东倒西歪，心里感到有些愧疚，写点乱七八糟的感悟凑篇水文。</p>
<p>我其实是个挺愚钝的人，也并不太擅长“感悟”东西，工作近两年其实并没有觉得自己在这种软实力方面有很大的提升。幸运的是我老板是个资历深厚且愿意和我简单聊聊这种事的人(在沟通绩效时)，因此我的为数不多的感悟大多是老板直接或间接启发得到的，也希望自己在之后的工作中能时常想起，时常遵循吧。</p>
<h2 id="1owner意识"><a href="http://localhost:4000/2021/04/27/2019-2021-work-inspiration/#owner%E6%84%8F%E8%AF%86"></a>owner意识</h2>
<p>owner意识是在我司时常能听到，但FY20财年末我老板着重跟我提起的。虽然现在内外一致认为我司的互联网词汇只是pua或者没有营养的堆砌，但是这个词所代表的能力确实是当时的我所缺少的。</p>
<p>直白的解释，owner意识就是把自己所负责的应用(或者是产品、模型、服务…)当成自己的“财产”，而不仅仅是工作的内容。可能这对于不同的人来说有不同的理解，不同的含义，就我来说，这个词代表的是“懂得接受，懂得拒绝(需求)”。</p>
<p>懂得接受需求，指的是懂得挖掘需求背后的逻辑(可能也叫产品意识？)，如果一个需求是合理的、有价值的，还需要尽量找到它最好的实现方式，而不是做一个需求处理机器人。这里的最好的方式说的不是架构上的，当然这也重要，但只考虑架构合理也只是炫技，说的是以用户的视角+应用本身的“干净”来审视需求。我暂时没有想到特别好的例子来说明(尽量不要在应用里维护过多上下游系统的逻辑大概算一个)。</p>
<p>相对于懂得接受需求，懂得拒绝需求对于我来说更难掌握，其实两者的内涵基本一致，就是需求是否该由这个应用来承担，是否有更合适的人/应用来实现，需求开发出来有多大意义，能有多少人用它，就算是合理的需求，应该怎么评估重要性进行呢排期…都是很现实的问题，但是对于一个刚毕业不太久的人来说仍然是需要学习的技能。</p>
<h2 id="2掌握手上的资源"><a href="http://localhost:4000/2021/04/27/2019-2021-work-inspiration/#%E6%8E%8C%E6%8F%A1%E6%89%8B%E4%B8%8A%E7%9A%84%E8%B5%84%E6%BA%90"></a>掌握手上的资源</h2>
<p>想法来源于前不久我和老板及PD对新财年某个项目的规划时感受到的。事实上从去年六月开始，我在工作时能写代码的机会很少了，应老板要求搬起了数据分析的砖，而上面提到的项目就是进行模型建设的数据分析方案。我在方案制定的过程潜意识里给自己设了个限制——我只能利用现有的数据来构建需要的模型，当我阐述完我的方案后，PD给出的第一个反馈就是不要设限，我可以做问卷调研、可以发动运营同事、可以让老板给我背书…我这才感觉到了自己其实是有资源的，并且也应该积极合理利用资源。</p>
<h2 id="3但谨慎使用"><a href="http://localhost:4000/2021/04/27/2019-2021-work-inspiration/#%E4%BD%86%E8%B0%A8%E6%85%8E%E4%BD%BF%E7%94%A8"></a>但谨慎使用</h2>
<p>这条应该是上一条的前提，之所以放在后面，一个是察觉到的晚，另一个是个人感觉意识到自己手上的资源更为“高级”。</p>
<p>“谨慎使用”是说先穷尽现阶段自己能进行分析/尝试/poc的手段，“走投无路”之后再拿着充足的证据，向自己以及对你即将push的人证明你方案的必要性以及能拿到的成果。我这里用词可能有些极端，我要说明的是这其实是对工作的一种态度，也是我自己没有拿捏好的态度。“穷尽手段”代表的是你对正在负责的项目已经探索了你能力内的所有可能，在这些可能之中你选择了最好的一个，有必要的话你还应该再找能力高于你的人进行论证。   大公司内部常常会有让人诟病的一点是，很多人喜欢重复造轮子，不管是造内部的轮子还是造开源社区的轮子。我自己也见过不少这种例子，也看过心直口快地大佬在内部分享上“怼”同学“造轮子”的理由，我不清楚多少轮子是开发人员迫不得已想出来的最优解，多少轮子只是为了晋升述职的筹码，但是在看到、遇到类似的情况时多分析分析其中的可能性，大概是会对我自己颇有裨益的。</p>
<p>有点好笑的事，实际上这点是我在上文提到的方案汇报后时隔一周的第二次汇报上被老板反问时理解的，而我当时提出的新方案也正是围绕量表调研的一系列置信度、信度效度巴拉巴拉措施展开的(捂脸)。老板提出的问题是我是否调研过公司内部相关的历史问卷调研情况，以及相关平台的数据，有足够的证据能让他向上证明一次新的问卷调研是必要的。</p>
<h2 id="4定义问题的能力"><a href="http://localhost:4000/2021/04/27/2019-2021-work-inspiration/#%E5%AE%9A%E4%B9%89%E9%97%AE%E9%A2%98%E7%9A%84%E8%83%BD%E5%8A%9B"></a>定义问题的能力</h2>
<p>又是一个在绩效沟通时老板提到的能力hhhh。老板说的很直白，就是遇到问题时能很快地定位到问题本质的能力。我理解这，在写代码上就是debug的思维清晰，在接需求上就是搞清楚做不做、谁来做、怎么做的能力，在业务上就是看清楚发展方向的能力…老板说他至今(工作十余年了)也没有很好的掌握，可能是谦虚，也可能是这个能力层次过于丰富，不管怎么说，这是一个万金油能力(公司内大佬们的晋升指导也反复提到)，学而时习之吧。</p>
<h2 id="5招聘遇到的两种人"><a href="http://localhost:4000/2021/04/27/2019-2021-work-inspiration/#%E6%8B%9B%E8%81%98%E9%81%87%E5%88%B0%E7%9A%84%E4%B8%A4%E7%A7%8D%E4%BA%BA"></a>招聘遇到的两种人</h2>
<p>当然我的水平还没到足以担任面试官，是老板谈起他招聘时会有两种人让他想给出offer。</p>
<p>第一种人是研究的方向、之前的工作和目前我们正在做的业务十分match的。这种人不一定能力很强，但是你知道他一进来就能干活，就能推进事情进展，中规中矩，无功无过，只是可能缺少从零到一推动一件事落成的本事。</p>
<p>第二种人有点相反，他做的事在能力面前显得就不那么重要了，面试的时候你能感觉到他对自己做的事把控清楚，能条理清楚地和你讲清楚项目从设计到落地的细节和考虑。</p>
<p>老板说第二种人很难得，也更倾向于招第二种人。简单想想就知道这是很显然的道理，做成某件事很简单，但是老道的猎人总能嗅出来应聘的人是跟着需求按部就班地走，还是带着思考地创造。</p>
<h2 id="6data-sense"><a href="http://localhost:4000/2021/04/27/2019-2021-work-inspiration/#Data-Sense"></a>Data Sense</h2>
<p>Data Sense, 大概对于统计学或数仓出身的同学来说很熟悉，对于我这种半路出家的半吊子来说是个新词(笑。我也不清楚它代表的更多的是一种敏感，还是一种能力，或是一种直觉，简单地说它指的是记住，并清楚地了解自己相关的数字的含义，比方说你负责的业务的流量情况，流量波动幅度可能的原因等等等。</p>
<p>组里的数据同事说这是无价之宝，数字是很难记住的，只有门清数字的逻辑才能做到，向老板汇报最要紧的就是这种能力(这个也基本是原话hhh)。在我的直观感觉上，总觉得对于天天和数据打交道的人来说这大概不算难，但是仔细想想，虽然不完全在做数据，但也干了近一年和数据有关的活了，就我自己来说确实是做不到，实在惭愧。</p>
<p>抛开数据科学相关的领域不说，对于研发来说这也应该是要紧的能力。业务上PV、UV的水位，任务执行的效率和成功率，程序运行时FGCT该是多少，多少算异常，可能大体上还是基于经验，但是能有对数字敏感且能快速把握多少也需要天赋和能力吧。</p>
<p>大体来说，近一两年还记得的想法大概就这么些，实际上大多数都是最近才记起来的，乱七八糟地记个流水账，权当为了加深印象吧~</p>]]></content>
		</item>
		
		<item>
			<title>使用IDEA进行debug的一个陷阱</title>
			<link>https://yochalyc.com/2021/03/%E4%BD%BF%E7%94%A8idea%E8%BF%9B%E8%A1%8Cdebug%E7%9A%84%E4%B8%80%E4%B8%AA%E9%99%B7%E9%98%B1/</link>
			<pubDate>Mon, 29 Mar 2021 15:52:36 +0000</pubDate>
			
			<guid>https://yochalyc.com/2021/03/%E4%BD%BF%E7%94%A8idea%E8%BF%9B%E8%A1%8Cdebug%E7%9A%84%E4%B8%80%E4%B8%AA%E9%99%B7%E9%98%B1/</guid>
			<description>&lt;p&gt;事情的起因，是某天同事遇到了了一个问题，他写了如下这么一个类，为了方便理解我们把业务相关的信息都抹掉。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; java.util.Arrays;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; java.util.Collections;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; java.util.List;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;A&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; List&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;lt;String&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; unsortedList;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;@Override&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; String &lt;span style=&#34;color:#a6e22e&#34;&gt;toString&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;// 为了输出时方便查看List内容，将其排序后返回&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        Collections.&lt;span style=&#34;color:#a6e22e&#34;&gt;sort&lt;/span&gt;(unsortedList);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; unsortedList.&lt;span style=&#34;color:#a6e22e&#34;&gt;toString&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; List&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;lt;String&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;getUnsortedList&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; unsortedList;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;setUnsortedList&lt;/span&gt;(List&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;lt;String&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; unsortedList) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;unsortedList&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; unsortedList;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
			<content type="html"><![CDATA[<p>事情的起因，是某天同事遇到了了一个问题，他写了如下这么一个类，为了方便理解我们把业务相关的信息都抹掉。</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#f92672">import</span> java.util.Arrays;
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> java.util.Collections;
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> java.util.List;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">A</span> {
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">private</span> List<span style="color:#f92672">&amp;</span>lt;String<span style="color:#f92672">&gt;</span> unsortedList;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">@Override</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> String <span style="color:#a6e22e">toString</span>() {
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// 为了输出时方便查看List内容，将其排序后返回</span>
</span></span><span style="display:flex;"><span>        Collections.<span style="color:#a6e22e">sort</span>(unsortedList);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> unsortedList.<span style="color:#a6e22e">toString</span>();
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> List<span style="color:#f92672">&amp;</span>lt;String<span style="color:#f92672">&gt;</span> <span style="color:#a6e22e">getUnsortedList</span>() {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> unsortedList;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">setUnsortedList</span>(List<span style="color:#f92672">&amp;</span>lt;String<span style="color:#f92672">&gt;</span> unsortedList) {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">unsortedList</span> <span style="color:#f92672">=</span> unsortedList;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>在之后执行/调试的过程中他发现，这个类的表现很不稳定，unsortedList的结果在乱序和有序之间反复横跳，且自己在一步步调试过程中并没有涉及到给该数组排序的指令，这让他十分疑惑。<br>
   经过观察他发现以下几个现象：</p>
<ul>
<li>在正常执行的过程中unsortedList与初始化时的顺序保持一致</li>
<li>在调试过程中，如果不对A类进行断点<em>观察</em>，则unsortedList在使用时的顺序也与初始化时的一致</li>
<li>但凡设了断点_观察_A，便会发现unsortedList被排序了</li>
</ul>
<p>对此现象我们称之为薛定谔的排序(误。<br>
   聪明的读者可能很快发现了，问题出在重写的toString方法上，但是当POJO类中充满大量业务相关的字段，或者toString中的操作并没有这么明显的时候，这个陷阱就很可能被忽略掉。<br>
   实际上，在我们debug的时候，IDEA提供的展示值给我们带来了很多便利，而这个展示值就是通过调用toString方法实现的。<br>
<img src="/uploads/2021/03/29/idea-debug-tostring-trap/debug_demo1.jpg" alt="Alt text">    通过查阅官方的<a href="https://www.jetbrains.com/help/IDEA/customizing-views.html#renderers">使用文档</a>我们也可以知道，通过设置<strong>Build, Execution, Deployment/Debugger/Data Views/Java</strong>，我们按需对不同类设置启用toString展示。同时在这一个设置窗口中，我们还能设置其他debug过程中的数据展示情况，如</p>
<blockquote>
<p>Enable alternative view for Collection classes</p>
</blockquote>
<p>这栏窗口允许设置更“人性化”的Collection classes展示方式(比如对于List的实现类，勾选该栏会展示List的大小，取消勾选会适应其他数据展示勾选项——如toString)。<br>
   但事情到这还没结束，当我们在toString中打上断点，尝试进行“多余的”验证来解释debug的机制时，我们会发现始终无法进入toString方法，并且在IDEA下方会提示 <strong>Skipped breakpoint at debuggertrap.A:13 because it happened inside debugger evaluation</strong>。这行提示的意思是说，<strong>该断点发生在debugger的evaluation线程中，因此我们将其进行跳过</strong>，有趣的是在查找资料的过程中，我找到了这么<a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=12592">一页讨论</a>是关于eclipse是否该跳过evaluation阶段的toString断点的，对于是否跳过，参与讨论的人员都有认为confusing的点，显然IDEA的作者们替我们做了决定，将debugger进行evaluation时的toString进行了跳过(我没找到相关的设置，如果有大佬清楚其中的逻辑的话希望不吝赐教)，这也确实造成了部分用户在使用时对逻辑产生了误判。<br>
   关于IDEA中进行debug的toString隐式调用陷阱讲到这就算结束了。关于debug的原理部分我没有深入地进行研究，而关于其中的JDPA体系在网上有大量资料，我也就不献丑了，仅将官方资料放在👇，有兴趣的读者可以自行前往阅读。如果有时间进行探索将另开文章记录。</p>
<h2 id="1相关资料"><a href="http://localhost:4000/2021/03/29/idea-debug-tostring-trap/#%E7%9B%B8%E5%85%B3%E8%B5%84%E6%96%99"></a>相关资料:</h2>
<ol>
<li><a href="https://www.jetbrains.com/help/idea/customizing-views.html#renderers">IDEA关于debugger中的用户视图设置</a></li>
<li><a href="https://docs.oracle.com/javase/8/docs/technotes/guides/jpda/">JPDA体系官方介绍</a></li>
<li><a href="https://juejin.cn/post/6844903744266584071">一篇JDPA体系的介绍</a></li>
<li><a href="https://www.jianshu.com/p/86ec47435cfc">另一篇更深入的JDPA体系的介绍，含部分实现代码</a></li>
<li><a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=12592">eclipse论坛中关于是否开启evaluation线程中的toString断点跳过的有意思的讨论</a></li>
</ol>]]></content>
		</item>
		
		<item>
			<title>在VPS上搭建ss</title>
			<link>https://yochalyc.com/2021/03/%E5%9C%A8vps%E4%B8%8A%E6%90%AD%E5%BB%BAss/</link>
			<pubDate>Mon, 22 Mar 2021 14:28:05 +0000</pubDate>
			
			<guid>https://yochalyc.com/2021/03/%E5%9C%A8vps%E4%B8%8A%E6%90%AD%E5%BB%BAss/</guid>
			<description>&lt;p&gt;在VPS(Virtual private server)上搭建ss(Shadowsocks)本身很简单，本文不会对细节（如VPS的选择、购买及依赖安装）做过多的深入，主要会对概念和简单的原理(因为深的我也不懂😛)以及主要流程进行介绍。&lt;/p&gt;</description>
			<content type="html"><![CDATA[<p>在VPS(Virtual private server)上搭建ss(Shadowsocks)本身很简单，本文不会对细节（如VPS的选择、购买及依赖安装）做过多的深入，主要会对概念和简单的原理(因为深的我也不懂😛)以及主要流程进行介绍。</p>
<h2 id="主要概念.wp-block-heading">主要概念</h2>
<h3 id="VPS.wp-block-heading">VPS</h3>
<blockquote>
<p>虚拟专用服务器（英语：Virtual private server，缩写为VPS），是将一台服务器分割成多个虚拟专用服务器的服务。实现VPS的技术分为容器技术和虚拟机技术。在容器或虚拟机中，每个VPS都可分配独立公网IP地址、独立操作系统、实现不同VPS间磁盘空间、内存、CPU资源、进程和系统配置的隔离，为用户和应用程序模拟出“独占”使用计算资源的体验。VPS可以像独立服务器一样，重装操作系统，安装程序，单独重启服务器。VPS为用户提供了管理配置的自由，可用于企业虚拟化，也可以用于IDC资源租用。 —— <a href="https://zh.wikipedia.org/wiki/%E8%99%9A%E6%8B%9F%E4%B8%93%E7%94%A8%E6%9C%8D%E5%8A%A1%E5%99%A8">Wiki</a></p>
</blockquote>
<p>关于服务器的选择及购买我在<a href="http://localhost:4000/2021/03/11/course-of-building-hexo-blog/">其他文章</a>中有提到，有需要的可以前往或查找相关资料。</p>
<h3 id="Shadowsocks.wp-block-heading">Shadowsocks</h3>
<blockquote>
<p>Shadowsocks（简称SS）是一种基于<a href="https://zh.wikipedia.org/wiki/SOCKS">Socks5</a> 代理方式的加密传输协议，也可以指实现这个协议的各种开发包。目前包使用Python、C、C++、C#、Go语言、Rust等编程语言开发，大部分主要实现（iOS平台的除外）采用Apache许可证、GPL、MIT许可证等多种自由软件许可协议开放源代码。Shadowsocks分为服务器端和客户端，在使用之前，需要先将服务器端程序部署到服务器上面，然后通过客户端连接并创建本地代理。——<a href="https://zh.wikipedia.org/wiki/Shadowsocks">Wiki</a></p>
</blockquote>
<p>关于SS(及SSR)Wiki上有进一步的介绍，有兴趣的同学可以点击查看。其原理简单来说，就是你在境外拥有一台未被<a href="https://zh.wikipedia.org/wiki/%E5%85%A8%E5%9B%BD%E5%85%AC%E5%AE%89%E5%B7%A5%E4%BD%9C%E4%BF%A1%E6%81%AF%E5%8C%96%E5%B7%A5%E7%A8%8B">GFW</a>拉入黑名单的服务器，你就可以通过与这台服务器建立起一个加密通道躲过GFW的过滤规则，从而访问墙外资源。更深入的解释可以参考 <a href="https://www.barretlee.com/blog/2016/08/03/shadowsocks/">这篇博客</a> 和 <a href="https://tumutanzi.com/archives/13005/comment-page-1">这篇博客</a><img src="/uploads/2021/03/22/install-ss-on-vps/ss_theory_img.jpg" alt="Alt text"></p>
<h2 id="搭建流程.wp-block-heading">搭建流程</h2>
<p>作为搭建的前提，我们已经在合适的平台上购买并了VPS并进行了简单的配置，接下来便开始着手进行“见不得人的勾当”。</p>
<h3 id="ip检测.wp-block-heading">ip检测</h3>
<p>我们首先要确定的是，我们VPS的ip地址没有在过滤名单中，通过 <a href="https://www.vps234.com/ipchecker/">这个网站</a> 进行检测，若国内国外都可以正常与VPS进行ICMP/TCP连接，便可以进行之后的步骤。</p>
<h3 id="SS-SSR安装.wp-block-heading">SS/SSR安装</h3>
<p>SSR对SS在混淆和协议方面进行了改进，两者在安装方式上没有明显的不同，我们这里以SS为例。</p>
<h4 id="SS-安装及启动.wp-block-heading">SS 安装及启动</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span><span style="color:#75715e"># 安装 pip，若以安装可直接跳过</span>
</span></span><span style="display:flex;"><span>sudo apt install python-pip
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 使用pip安装shadowsocks</span>
</span></span><span style="display:flex;"><span>sudo pip install shadowsocks
</span></span></code></pre></div><p>如此shadowsocks便安装完成了，接下来以指定配置文件的方式启动ss，</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span><span style="color:#75715e"># 编辑配置文件, 不同安装方式对应的默认配置文件不同，请读者自行匹配或创建</span>
</span></span><span style="display:flex;"><span>vim /etc/shadowsocks.json
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;server&#34;</span>: <span style="color:#e6db74">&#34;0.0.0.0&#34;</span>, <span style="color:#75715e">// 服务器过滤规则，0.0.0.0表示允许所有来源ip请求
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  <span style="color:#f92672">&#34;server_port&#34;</span>: <span style="color:#ae81ff">8399</span>, <span style="color:#75715e">// 指定服务器对外开放端口
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  <span style="color:#f92672">&#34;local_address&#34;</span>: <span style="color:#e6db74">&#34;127.0.0.1&#34;</span>, <span style="color:#75715e">// 本地代理监听地址
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  <span style="color:#f92672">&#34;local_port&#34;</span>: <span style="color:#ae81ff">1080</span>, <span style="color:#75715e">// 本地代理监听端口
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  <span style="color:#f92672">&#34;password&#34;</span>: <span style="color:#e6db74">&#34;${your_pass_word}&#34;</span>, <span style="color:#75715e">//指定连接方式
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  <span style="color:#f92672">&#34;timeout&#34;</span>: <span style="color:#ae81ff">600</span>, <span style="color:#75715e">//超时设置
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>  <span style="color:#f92672">&#34;method&#34;</span>: <span style="color:#e6db74">&#34;aes-256-cfb&#34;</span> <span style="color:#75715e">//加密方式，后续客户端设置需要与这里保持一致
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>}
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span><span style="color:#75715e"># 启动ss服务器</span>
</span></span><span style="display:flex;"><span>/bin/python /usr/local/bin/ssserver -c /etc/shadowsocks.json -d start
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 关闭</span>
</span></span><span style="display:flex;"><span>/bin/python /usr/local/bin/ssserver -c /etc/shadowsocks.json -d stop
</span></span><span style="display:flex;"><span><span style="color:#75715e"># 重启</span>
</span></span><span style="display:flex;"><span>/bin/python /usr/local/bin/ssserver -c /etc/shadowsocks.json -d restart
</span></span></code></pre></div><h5 id="关于-undefined-symbol-EVP-CIPHER-CTX-cleanup.wp-block-heading">关于 <strong>undefined symbol: EVP_CIPHER_CTX_cleanup</strong></h5>
<p>可能有同学在执行时会碰到如上报错，是由于在openssl1.1.0版本中，废弃了EVP_CIPHER_CTX_cleanup函数，只需要将 ${your_python_lib}/dist-packages/shadowsocks/crypto/openssl.py 中的EVP_CIPHER_CTX_cleanup替换成EVP_CIPHER_CTX_reset即可。</p>
<h5 id="关于保姆安装方法.wp-block-heading"><a href="http://localhost:4000/2021/03/22/install-ss-on-vps/#%E5%85%B3%E4%BA%8E%E4%BF%9D%E5%A7%86%E5%AE%89%E8%A3%85%E6%96%B9%E6%B3%95"></a>关于保姆安装方法</h5>
<p>参考 <a href="https://teddysun.com/486.html">这篇文章</a>，将多种版本的ss进行打包及手把手教学安装</p>
<h3 id="本地客户端安装及配置.wp-block-heading"><a href="http://localhost:4000/2021/03/22/install-ss-on-vps/#%E6%9C%AC%E5%9C%B0%E5%AE%A2%E6%88%B7%E7%AB%AF%E5%AE%89%E8%A3%85%E5%8F%8A%E9%85%8D%E7%BD%AE"></a>本地客户端安装及配置</h3>
<h4 id="pc端.wp-block-heading"><a href="http://localhost:4000/2021/03/22/install-ss-on-vps/#pc%E7%AB%AF"></a>pc端</h4>
<ol>
<li>在<a href="https://github.com/shadowsocks">github仓库</a>下载匹配版本的shadowsocks客户端并进行安装。</li>
<li>点击 <strong>服务器 -&gt; 服务器设置…</strong><img src="/uploads/2021/03/22/install-ss-on-vps/ss_set_entry.jpg" alt="Alt text">进入服务器设置页面</li>
<li>根据VPS的配置文件(如上文的/etc/shadowsocks.json)进行配置<img src="/uploads/2021/03/22/install-ss-on-vps/ss_set.jpg" alt="Alt text"></li>
<li>完成设置，进行连接</li>
</ol>
<h4 id="手机端.wp-block-heading"><a href="http://localhost:4000/2021/03/22/install-ss-on-vps/#%E6%89%8B%E6%9C%BA%E7%AB%AF"></a>手机端</h4>
<p>这里以iphone为例</p>
<ol>
<li>登录非国区appstore账户，下载Spectre</li>
<li>通过pc端的客户端分享配置，扫描二维码或粘贴url添加服务器信息<img src="/uploads/2021/03/22/install-ss-on-vps/ss_pc_share.jpg" alt="Alt text"></li>
<li>由于某些原因不方便安装pc端ss客户端的，可以自己生成url在手机端进行粘贴配置，如下</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#75715e">// 拼接待加密路径</span>
</span></span><span style="display:flex;"><span>String uri <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;${method}:${password}@${your_ip_address}:${server_port}&#34;</span>;
</span></span><span style="display:flex;"><span>	<span style="color:#75715e">// base64编码</span>
</span></span><span style="display:flex;"><span>String base64 <span style="color:#f92672">=</span> Base64.<span style="color:#a6e22e">getEncoder</span>().<span style="color:#a6e22e">encodeToString</span>(uri.<span style="color:#a6e22e">getBytes</span>());
</span></span><span style="display:flex;"><span>      <span style="color:#75715e">// 输出url</span>
</span></span><span style="display:flex;"><span>System.<span style="color:#a6e22e">out</span>.<span style="color:#a6e22e">println</span>(<span style="color:#e6db74">&#34;ss://&#34;</span> <span style="color:#f92672">+</span> base64);
</span></span></code></pre></div><h3 id="查看服务器端访问日志.wp-block-heading">查看服务器端访问日志</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>tail -f /var/log/shadowsocks.log
</span></span></code></pre></div><h2 id="关于端口检测及开放.wp-block-heading"><a href="http://localhost:4000/2021/03/22/install-ss-on-vps/#%E5%85%B3%E4%BA%8E%E7%AB%AF%E5%8F%A3%E6%A3%80%E6%B5%8B%E5%8F%8A%E5%BC%80%E6%94%BE"></a>关于端口检测及开放</h2>
<p>可能有同学会碰到这种情况: ip监测正常, 客户端与服务器端配置也一致，但是访问日志中显示一直接收不到请求。遇到这种情况就需要对服务器端口进行监测。<a href="http://port.ping.pe/">监测网址</a></p>
<h4 id="情况一-国外服务器成功连接，国内服务器失败.wp-block-heading"><a href="http://localhost:4000/2021/03/22/install-ss-on-vps/#%E6%83%85%E5%86%B5%E4%B8%80-%E5%9B%BD%E5%A4%96%E6%9C%8D%E5%8A%A1%E5%99%A8%E6%88%90%E5%8A%9F%E8%BF%9E%E6%8E%A5%EF%BC%8C%E5%9B%BD%E5%86%85%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%A4%B1%E8%B4%A5"></a>情况一: 国外服务器成功连接，国内服务器失败</h4>
<p>这种情况是这个端口被ban了，在配置文件中修改server_port并重启ss服务尝试。</p>
<h4 id="情况二-所有服务器都显示连接失败-Connection-to-xxxx-xxxx-failed.wp-block-heading"><a href="http://localhost:4000/2021/03/22/install-ss-on-vps/#%E6%83%85%E5%86%B5%E4%BA%8C-%E6%89%80%E6%9C%89%E6%9C%8D%E5%8A%A1%E5%99%A8%E9%83%BD%E6%98%BE%E7%A4%BA%E8%BF%9E%E6%8E%A5%E5%A4%B1%E8%B4%A5-Connection-to-xxxx-xxxx-failed"></a>情况二: 所有服务器都显示连接失败(Connection to xxxx:xxxx failed)</h4>
<p><img src="/uploads/2021/03/22/install-ss-on-vps/ping_fail.jpg" alt="Alt text">    这种情况比较大的可能是防火墙未将配置的端口打开，执行</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>sudo ufw allow <span style="color:#e6db74">${</span>server_port<span style="color:#e6db74">}</span>/tcp
</span></span></code></pre></div><p>后再次进行端口检测</p>
<h2 id="参考资料.wp-block-heading"><a href="http://localhost:4000/2021/03/22/install-ss-on-vps/#%E5%8F%82%E8%80%83%E8%B5%84%E6%96%99"></a>参考资料:</h2>
<ol>
<li><a href="https://teddysun.com/486.html">Shadowsocks四合一安装</a></li>
<li><a href="https://www.digitalocean.com/community/tutorials/how-to-set-up-a-firewall-with-ufw-on-ubuntu-18-04">关于Ubuntu下防火墙的配置</a></li>
<li><a href="https://www.quchao.net/ShadowsocksR.html#Windows-SSTap">一篇比较详细的关于ss/ssr配置的博客</a></li>
<li><a href="https://tumutanzi.com/archives/13005/comment-page-1">ss原理简单介绍</a></li>
<li><a href="https://www.barretlee.com/blog/2016/08/03/shadowsocks/">胡子哥的ss原理介绍及安装说明</a> …</li>
</ol>]]></content>
		</item>
		
		<item>
			<title>Hexo博客建站历程</title>
			<link>https://yochalyc.com/2021/03/hexo%E5%8D%9A%E5%AE%A2%E5%BB%BA%E7%AB%99%E5%8E%86%E7%A8%8B/</link>
			<pubDate>Thu, 11 Mar 2021 15:48:48 +0000</pubDate>
			
			<guid>https://yochalyc.com/2021/03/hexo%E5%8D%9A%E5%AE%A2%E5%BB%BA%E7%AB%99%E5%8E%86%E7%A8%8B/</guid>
			<description>&lt;p&gt;这个博客使用Hexo框架搭建，部署在VPS上并使用Nginx作为代理服务器。博客本身从开始折腾到顺利在服务器上跑起来并没有花多少时间，只是由于本人前端小白又试图魔改主题导致到现在才写下第一篇博客。相关的每个步骤其实网上都有详尽的指导，以这篇作为实际意义上的第一篇博客一是对搭建过程进行整理方便后人，二是给自己打个气希望能由此习惯记录博客。&lt;/p&gt;</description>
			<content type="html"><![CDATA[<p>这个博客使用Hexo框架搭建，部署在VPS上并使用Nginx作为代理服务器。博客本身从开始折腾到顺利在服务器上跑起来并没有花多少时间，只是由于本人前端小白又试图魔改主题导致到现在才写下第一篇博客。相关的每个步骤其实网上都有详尽的指导，以这篇作为实际意义上的第一篇博客一是对搭建过程进行整理方便后人，二是给自己打个气希望能由此习惯记录博客。</p>
<h2 id="准备工作">准备工作</h2>
<p>由于其他的考虑，这个博客使用 <strong>独立域名 + VPS + 个人电脑</strong> 进行搭建及部署。以下是个人对于相关平台的选择：<figure class="wp-block-table"></p>
<table>
<thead>
<tr>
<th>工具</th>
<th>选择平台</th>
</tr>
</thead>
<tbody>
<tr>
<td>VPS</td>
<td><a href="https://m.do.co/c/2932cf5f2dba">DigitalOcean</a></td>
</tr>
<tr>
<td>域名</td>
<td><a href="https://www.namecheap.com/">namecheap</a></td>
</tr>
<tr>
<td>域名解析</td>
<td><a href="https://www.dnspod.cn/">DNSPOD</a></td>
</tr>
<tr>
<td>个人电脑</td>
<td>Mac</td>
</tr>
</tbody>
</table>
<h3 class="wp-block-heading" id="vps">VPS</h3>
<p>服务器的选择并没有经过特别多的纠结，国内平台购买的服务器需要进行备案，看了些知乎的评价及以前就开始关注的博主的介绍就决定在 <a href="https://m.do.co/c/2932cf5f2dba">DigitalOcean</a> 进行购买。进入网站后进行简单的注册，注册后可以进行支付方式的绑定，需要预付5刀(可作为后续购买droplet使用)，同时获得100刀的限时Promo Credit。<img src="/uploads/2021/03/11/course-of-building-hexo-blog/do_paypal_binding.png" alt="Alt text"><br>
至此注册的步骤就结束了，在开始创建项目及服务器之前，可以考虑先将个人电脑的ssh公钥进行录入方便后续操作(也可以在创建服务器后<a href="https://www.ruanyifeng.com/blog/2011/12/ssh_remote_login.html">手动添加</a>)<img src="/uploads/2021/03/11/course-of-building-hexo-blog/do_settings_ssh.png" alt="Alt text"><br>
接下来创建项目后就可以进行服务器的配置和创建。DigitalOcean把其上的每个虚拟机称为Droplet，点击Create Droplets可以根据个人需要进行配置及创建Droplet，值得注意的是region的需要根据个人情况进行选择，可以在 <a href="http://speedtest-sfo1.digitalocean.com/">DigitalOcean官方提供的测速网站</a> 选择速度较快的地区服务器，博主这里选择的节点是SF3。</p>
<h3 class="wp-block-heading" id="域名购买--域名解析">域名购买 &amp; 域名解析</h3>
<p>域名的购买我选择的是<a href="https://www.namecheap.com/">Namecheap</a>，Namecheap提提供免费的域名解析服务，听说国内Dnspod的解析速度不错因此域名解析我另外在这个平台上进行尝试(同样也是免费的)。<br>
域名解析根据<a href="https://docs.dnspod.cn/dns/5f2d4664e8320f1a740d9cab/">Dnspod官方文档</a>操作，记录添加完成后需要到Namespace上将Nameservers配置成Dnspod指定的dns服务器。</p>
<h2 class="wp-block-heading" id="搭建">搭建</h2>
<h3 class="wp-block-heading" id="本地安装安装hexo">本地安装安装Hexo</h3>
<p><a href="https://hexo.io/zh-cn/docs/">Hexo官方文档</a></p>
<h3 class="wp-block-heading" id="通过git远程远程部署hexo到服务器">通过git远程远程部署Hexo到服务器</h3>
<h4 class="wp-block-heading" id="服务器操作">服务器操作</h4>
<ol>
<li>
<p><a href="https://cloud.tencent.com/developer/article/1090679">服务器用户添加sudo权限</a></p>
</li>
<li>
<p><a href="https://www.ruanyifeng.com/blog/2011/12/ssh_remote_login.html">服务器添加本地ssh公钥</a>(如果在<a href="http://localhost:4000/2021/03/11/course-of-building-hexo-blog/#1.1">1.1</a>中已经配置了ssh可以跳过这部)</p>
</li>
<li>
<p>在指定目录下建立部署文件夹,同时也是之后Nginx配置的root路径，这里假设为blog_www</p>
</li>
<li>
<p>设置裸仓库权限及及编辑hook文件</p>
</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>cd /home/admin
</span></span><span style="display:flex;"><span>mkdir blog_receiver.git
</span></span><span style="display:flex;"><span> // 建立git裸仓库并设置文件夹权限，将$USER替换为你自己的用户
</span></span><span style="display:flex;"><span>git init --bare blog_receiver.git <span style="color:#f92672">&amp;&amp;</span> chown -R $USER:$USER blog_receiver.git  // 设置文件夹权限
</span></span><span style="display:flex;"><span>cd /home/admin/blog_receiver.git/hooks //假设/home/admin/blog_receiver.git为建立的裸仓库
</span></span><span style="display:flex;"><span>touch post-receiver
</span></span></code></pre></div><pre tabindex="0"><code>
打开post-receiver并输入如下内容

```sh
#!/bin/sh
git --work-tree=/home/admin/blog_www --git-dir=/home/admin/blog_receiver.git checkout -f
</code></pre><p><strong>编辑Nginx配置文件，在server中添加如下信息。（注意，DigitalOcean的服务器可能默认对location / 进行了配置，需要将其删除）</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>location / <span style="color:#f92672">{</span>
</span></span><span style="display:flex;"><span>    root /home/yocha/blog_www;  index index.html index.htm;
</span></span><span style="display:flex;"><span><span style="color:#f92672">}</span>
</span></span></code></pre></div><h4 class="wp-block-heading" id="本地电脑操作">本地电脑操作</h4>
<ol>
<li>在本地Hexo项目_config.yml中添加</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">deploy</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">type</span>: <span style="color:#ae81ff">git</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">repo</span>: <span style="color:#ae81ff">$USER@$host:/home/admin/blog_receiver.git //$USER替换为上文提到的用户，$host替换为你的服务器host或ip</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">branch</span>: <span style="color:#ae81ff">master</span>
</span></span></code></pre></div><p>之后便可以通过 <strong>hexo hexo generate &amp;&amp; hexo deploy</strong> 命令远程部署到服务器了</p>
<h2 class="wp-block-heading" id="完善">完善</h2>
<h3 class="wp-block-heading" id="主题更换及插件配置">主题更换及插件配置</h3>
<p>通常Hexo主题都附带有主题安装及支持插件配置教程，我这里就不再赘述，推荐几个个人较喜欢的主题：</p>
<ol>
<li><a href="https://github.com/theme-next/hexo-theme-next">Next</a>：支持三种内置风格切换，人气颇高的主题</li>
<li><a href="https://github.com/tufu9441/maupassant-hexo">maupassant</a>: 本博客最开始使用的主题，风格简约，有不少主题是基于这个进行改造的</li>
<li><a href="https://github.com/probberechts/hexo-theme-cactus">Cactos</a>: 很可爱的一个主题，简约又实用
<a href="https://hexo.voxel.site/">…排名前10的Hexo主题</a></li>
</ol>
<h3 class="wp-block-heading" id="disqus评论插件base版本去除广告">Disqus评论插件base版本去除广告</h3>
<p>Disqus是本博客依赖的评论工具，支持多种方式登录评论及自定义评论头像等功能。缺点是免费版本评论框上线会带有大篇幅的广告栏，从 <a href="https://www.javaer101.com/article/25891160.html">这篇文章</a> 中得到启发，将对应代码复制到Disqus对应的代码片段中可以去除广告。以我最开始使用的maupassant主题为例，在下面代码中插入相关函数:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-js" data-lang="js"><span style="display:flex;"><span><span style="color:#66d9ef">if</span> <span style="color:#a6e22e">theme</span>.<span style="color:#a6e22e">disqus</span>.<span style="color:#a6e22e">enable</span> <span style="color:#f92672">==</span> <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">link</span>(<span style="color:#a6e22e">rel</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#39;stylesheet&#39;</span>, <span style="color:#a6e22e">type</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#39;text/css&#39;</span>, <span style="color:#a6e22e">href</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#39;https://cdn.jsdelivr.net/gh/sukkaw/disqusjs/dist/disqusjs.css?v=&#39;</span> <span style="color:#f92672">+</span> <span style="color:#a6e22e">theme</span>.<span style="color:#a6e22e">version</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">script</span>(<span style="color:#a6e22e">type</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#39;text/javascript&#39;</span> <span style="color:#a6e22e">src</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#39;https://cdn.jsdelivr.net/gh/sukkaw/disqusjs/dist/disqus.js?v=&#39;</span> <span style="color:#f92672">+</span> <span style="color:#a6e22e">theme</span>.<span style="color:#a6e22e">version</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">script</span>(<span style="color:#a6e22e">type</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#39;text/javascript&#39;</span> <span style="color:#a6e22e">id</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#39;disqus-count-script&#39;</span>).
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">$</span>(<span style="color:#66d9ef">function</span>() {
</span></span><span style="display:flex;"><span>           ...
</span></span><span style="display:flex;"><span>           <span style="color:#a6e22e">xhr</span>.<span style="color:#a6e22e">ontimeout</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">function</span> () { <span style="color:#a6e22e">xhr</span>.<span style="color:#a6e22e">abort</span>(); };
</span></span><span style="display:flex;"><span>           <span style="color:#a6e22e">xhr</span>.<span style="color:#a6e22e">send</span>(<span style="color:#66d9ef">null</span>);
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// 插入如下函数
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    <span style="color:#75715e">// begin
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>           <span style="color:#a6e22e">setInterval</span>(() =&gt; {
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">$</span>.<span style="color:#a6e22e">each</span>(<span style="color:#a6e22e">$</span>(<span style="color:#e6db74">&#39;iframe&#39;</span>), (<span style="color:#a6e22e">arr</span>,<span style="color:#a6e22e">x</span>) =&gt; {
</span></span><span style="display:flex;"><span>                <span style="color:#66d9ef">let</span> <span style="color:#a6e22e">src</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">$</span>(<span style="color:#a6e22e">x</span>).<span style="color:#a6e22e">attr</span>(<span style="color:#e6db74">&#39;src&#39;</span>);
</span></span><span style="display:flex;"><span>                <span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">src</span> <span style="color:#f92672">&amp;&amp;</span> <span style="color:#a6e22e">src</span>.<span style="color:#a6e22e">match</span>(<span style="color:#e6db74">/(ads-iframe)|(disqusads)/gi</span>)) {
</span></span><span style="display:flex;"><span>                    <span style="color:#a6e22e">$</span>(<span style="color:#a6e22e">x</span>).<span style="color:#a6e22e">remove</span>();
</span></span><span style="display:flex;"><span>                }
</span></span><span style="display:flex;"><span>            });
</span></span><span style="display:flex;"><span>         }, <span style="color:#ae81ff">300</span>);
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// end
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>         });
</span></span><span style="display:flex;"><span>        ...
</span></span></code></pre></div><h3 class="wp-block-heading" id="设置需要的目录以下是添加各类分类的方法按需配置">设置需要的目录以下是添加各类分类的方法，按需配置</h3>
<ol>
<li><a href="http://www.hiekay.com/2018/10/17/Git%E4%B8%AA%E4%BA%BA%E5%8D%9A%E5%AE%A2hexo%E8%AE%BE%E7%BD%AE%E5%85%B3%E4%BA%8E%E3%80%81%E6%A0%87%E7%AD%BE%E3%80%81%E5%88%86%E7%B1%BB%E3%80%81%E5%BD%92%E6%A1%A3%E3%80%81%E6%97%B6%E9%97%B4%E7%BA%BF/">添加各类标签</a></li>
<li><a href="https://fontawesome.com/icons?d=gallery&amp;p=2">可以拿来寻找图标的网站</a></li>
</ol>
<h3 class="wp-block-heading" id="修改模板">修改模板</h3>
<p>在Hexo项目根目录scaffolds下可以配置各类文件的模板，为方便后续写作建议在这里先将模板配置好，配置参数可参考 <a href="https://hexo.io/zh-cn/docs/front-matter">官方文档</a>。这里给出博主的作为参考：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span>---
</span></span><span style="display:flex;"><span><span style="color:#f92672">title</span>: { { <span style="color:#ae81ff">title } }</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">date</span>: { { <span style="color:#ae81ff">date } }</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">tags</span>:
</span></span><span style="display:flex;"><span>  -
</span></span><span style="display:flex;"><span><span style="color:#f92672">categories</span>:
</span></span><span style="display:flex;"><span>  - []
</span></span><span style="display:flex;"><span><span style="color:#f92672">comments</span>: <span style="color:#66d9ef">true</span> <span style="color:#ae81ff">//允许评论</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">layout</span>: <span style="color:#ae81ff">post //布局类型</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">toc</span>: <span style="color:#66d9ef">true</span> <span style="color:#ae81ff">// 显示目录</span>
</span></span><span style="display:flex;"><span>---
</span></span></code></pre></div><h3 class="wp-block-heading" id="设置博客图标">设置博客图标</h3>
<p><a href="http://theme-next.iissnan.com/faqs.html#favicon">设置网站图标及未生效解法</a></p>
<h3 class="wp-block-heading" id="支持https">支持HTTPS</h3>
<p><a href="https://certbot.eff.org/">免费设置网站支持https</a>
之前不同系统可以通过certbot_auto统一安装，目前不支持。不过到上面这个网站也提供傻瓜式操作指引。</p>
<h2 class="wp-block-heading" id="总结">总结</h2>
<p>本篇博客是对博主自己搭建博客的过程以及过程中参考的各类文档进行简单的梳理，基本没有什么创造性的输出。希望至少能给有需要的朋友带来些许遍历。</p>
<h2 class="wp-block-heading" id="参考资料">参考资料</h2>
<ol>
<li><a href="https://hexo.io/zh-cn/docs/">Hexo官方文档</a></li>
<li><a href="https://www.ruanyifeng.com/blog/2011/12/ssh_remote_login.html">ssh远程登录服务器</a></li>
<li><a href="https://docs.dnspod.cn/dns/5f2d4664e8320f1a740d9cab/">Dnspod官方文档</a></li>
<li><a href="https://www.javaer101.com/article/25891160.html">Disqus去除广告方法</a></li>
<li><a href="https://www.zhihu.com/question/25529727">知乎:DigitalOcean选择Region问题</a></li>
<li><a href="https://blog.weearc.top/posts/6009.html">一个博主的maupassant魔改:评论、进度条、烟花效果</a></li>
<li><a href="http://theme-next.iissnan.com/faqs.html#favicon">设置网站图标及未生效解法</a></li>
</ol>
<p>…对于其他博主忘了记录但对博主搭建博客起到重要作用的文章表示歉意及衷心感谢</p>]]></content>
		</item>
		
	</channel>
</rss>
