<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://binpan.me/feed.xml" rel="self" type="application/atom+xml" /><link href="https://binpan.me/" rel="alternate" type="text/html" /><updated>2025-09-05T10:58:34+08:00</updated><id>https://binpan.me/feed.xml</id><title type="html">小潘</title><subtitle>小潘</subtitle><author><name>Maoxiang Pan</name></author><entry><title type="html">毕业这8年 aka 35岁复盘</title><link href="https://binpan.me/2024/09/28/35-years/" rel="alternate" type="text/html" title="毕业这8年 aka 35岁复盘" /><published>2024-09-28T00:00:00+08:00</published><updated>2024-09-28T00:00:00+08:00</updated><id>https://binpan.me/2024/09/28/35-years</id><content type="html" xml:base="https://binpan.me/2024/09/28/35-years/"><![CDATA[<h1 id="毕业这8年35岁复盘">毕业这8年/35岁复盘</h1>

<p>说好的每两年复盘一次，距上次复盘已是4年前，生活像是在深踩油门，催促着一直往前冲，没有精力去好好思考，过往的收获是什么、缺陷在哪里，如何打破瓶颈，借35岁这天，从上帝视角来复盘下。</p>

<h2 id="复盘">复盘</h2>

<p>19年底，加入一家当时很有潜力的互联网公司，那时候全公司都在为3亿DAU冲刺着，每个人都有机会通过自己本领来领导项目/架构的演进，那是我最有激情的4年，那几年，从零到一 构建出公司级别的项目，开发维护超20万+的服务器，维护着全球最大的镜像仓库，当然也收获了不少白头发。</p>

<h3 id="项目收益">项目收益</h3>

<ul>
  <li>维护着一线K8S集群，包括二次开发，日常运维等等，一些技能业界领先，也深入了解一些Linux的内核技能</li>
  <li>从0到1的一个项目(保密)，桥接着容器与用户入口，来到下家才发现 这是一个团队的工作量</li>
  <li>全球最大的镜像仓库，打磨出当时体量最大的镜像中心，单仓库XXPB，至今有朋友来取经</li>
  <li>。。。</li>
</ul>

<h3 id="个人成长">个人成长</h3>

<ul>
  <li>自信，不再会质疑自己能不能做到，而是想 如何才能做到，别人如何做的，是不是最优的，我该如何做</li>
  <li>收益，公司上市，套现了一波、收获一波年终奖</li>
  <li>情绪，稳定保持心跳在80左右，波澜不惊，遇事不慌</li>
  <li>感悟，只要你认定自己是一个什么样的人，就会慢慢成为你想的样子</li>
</ul>

<h2 id="现状">现状</h2>

<ul>
  <li>新领域熟悉中，静默蓄力</li>
  <li>注射干细胞，希望体能/精力增强</li>
  <li>其他敬请期待</li>
</ul>

<h2 id="缺陷">缺陷</h2>

<ul>
  <li>有点眼高手低，很多事情不愿意自己干，需保持激情与好奇心</li>
  <li>英语水平不高，未来瓶颈之一</li>
</ul>

<h2 id="未来">未来</h2>

<ul>
  <li>强者不抱怨环境、不权衡利弊、不彷徨、不迷茫、</li>
  <li>积累经验、增强变现、早日脱离打工人身份</li>
  <li>特殊的618</li>
  <li>2年后再会</li>
</ul>]]></content><author><name>Maoxiang Pan</name></author><category term="成长" /><summary type="html"><![CDATA[毕业系列]]></summary></entry><entry><title type="html">kubernetes 跨版本升级</title><link href="https://binpan.me/2020/10/21/kubernetes-update/" rel="alternate" type="text/html" title="kubernetes 跨版本升级" /><published>2020-10-21T00:00:00+08:00</published><updated>2020-10-21T00:00:00+08:00</updated><id>https://binpan.me/2020/10/21/kubernetes-update</id><content type="html" xml:base="https://binpan.me/2020/10/21/kubernetes-update/"><![CDATA[<blockquote>
  <p>本文概述 跨版本升级涉及kubelet 的修改，也是升级流程中一个重要环节。
阅读本文需要:</p>
  <ul>
    <li>了解kubernetes，以及各个组件的功能</li>
    <li>golang语言</li>
    <li>了解go-spew</li>
  </ul>
</blockquote>

<h1 id="为什么要升级版本">为什么要升级版本</h1>
<p>集群主要版本是&lt;=1.12 落后于社区版本太多(升级时，最新1.17已经release)，kubernetes社区更新迭代速度较快，跟进社区高版本，可以保证集群更可靠稳定,跟上社区的发展利大于弊。</p>
<ul>
  <li>新版本 bug fix</li>
  <li>新feature的应用</li>
</ul>

<h1 id="升级难点">升级难点</h1>

<p>生产集群的升级，不能影响业务容器的运行，k8s组件较多，升级过程中，任何一个组件出现问题，都可能影响到业务，对于核心业务，如果批量重启，可能造成灾难，因此，
集群的升级需要，业务容器无感知，基于社区的升级方案主要有几个问题:</p>
<ul>
  <li>版本的升级，不能跨大版本，只能从小版本逐步升级，升级繁琐</li>
  <li>升级过程中，出现问题，不能回滚</li>
  <li>升级会造成容器重建，影响业务的运行</li>
</ul>

<h2 id="难点分析">难点分析</h2>
<p>跨版本升级其中一个最大的问题是kubelet会重建业务容器，我们源码分析，为什么升级kubelet，会造成容器重建</p>

<h3 id="kubelet-是如何同步pod的">kubelet 是如何同步pod的</h3>
<p>kubelet整个处理流程比较复杂，核心处理逻辑在 <code class="language-plaintext highlighter-rouge">kuberuntime_manager.go</code> 中 <code class="language-plaintext highlighter-rouge">SyncPod</code> 方法，kubelet会定时或者有event变更调用该方法</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// SyncPod syncs the running pod into the desired pod by executing following steps:</span>
<span class="c">//</span>
<span class="c">//  1. Compute sandbox and container changes.</span>
<span class="c">// ...</span>
<span class="k">func</span> <span class="p">(</span><span class="n">m</span> <span class="o">*</span><span class="n">kubeGenericRuntimeManager</span><span class="p">)</span> <span class="n">SyncPod</span><span class="p">(</span><span class="n">pod</span> <span class="o">*</span><span class="n">v1</span><span class="o">.</span><span class="n">Pod</span><span class="p">,</span> <span class="n">podStatus</span> <span class="o">*</span><span class="n">kubecontainer</span><span class="o">.</span><span class="n">PodStatus</span><span class="p">,</span> <span class="n">pullSecrets</span> <span class="p">[]</span><span class="n">v1</span><span class="o">.</span><span class="n">Secret</span><span class="p">,</span> <span class="n">backOff</span> <span class="o">*</span><span class="n">flowcontrol</span><span class="o">.</span><span class="n">Backoff</span><span class="p">)</span> <span class="p">(</span><span class="n">result</span> <span class="n">kubecontainer</span><span class="o">.</span><span class="n">PodSyncResult</span><span class="p">)</span> <span class="p">{</span>
	<span class="c">// Step 1: Compute sandbox and container changes.</span>
	<span class="n">podContainerChanges</span> <span class="o">:=</span> <span class="n">m</span><span class="o">.</span><span class="n">computePodActions</span><span class="p">(</span><span class="n">pod</span><span class="p">,</span> <span class="n">podStatus</span><span class="p">)</span>
    <span class="o">...</span>
</code></pre></div></div>

<p>我们重点关注Step.1 <code class="language-plaintext highlighter-rouge">computePodActions</code>，该方法判断容器是否发生了改变，方法逻辑判断比较多，通过分析，我们直接跳到问题代码</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	<span class="k">if</span> <span class="n">_</span><span class="p">,</span> <span class="n">_</span><span class="p">,</span> <span class="n">changed</span> <span class="o">:=</span> <span class="n">containerChanged</span><span class="p">(</span><span class="o">&amp;</span><span class="n">container</span><span class="p">,</span> <span class="n">containerStatus</span><span class="p">);</span> <span class="n">changed</span> <span class="p">{</span>
		<span class="n">message</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Sprintf</span><span class="p">(</span><span class="s">"Container %s definition changed"</span><span class="p">,</span> <span class="n">container</span><span class="o">.</span><span class="n">Name</span><span class="p">)</span>
    <span class="o">...</span>
</code></pre></div></div>
<p>替换kubelet 二进制后，日志打印  <code class="language-plaintext highlighter-rouge">Container %s definition changed</code>，看来<code class="language-plaintext highlighter-rouge">containerChanged</code> 问题出在这里的<code class="language-plaintext highlighter-rouge">HashContainer</code>，我们对比下1.12 1.17 版本差异</p>

<p>v1.12</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="n">HashContainer</span><span class="p">(</span><span class="n">container</span> <span class="o">*</span><span class="n">v1</span><span class="o">.</span><span class="n">Container</span><span class="p">)</span> <span class="kt">uint64</span> <span class="p">{</span>
	<span class="n">hash</span> <span class="o">:=</span> <span class="n">fnv</span><span class="o">.</span><span class="n">New32a</span><span class="p">()</span>
	<span class="n">hashutil</span><span class="o">.</span><span class="n">DeepHashObject</span><span class="p">(</span><span class="n">hash</span><span class="p">,</span> <span class="o">*</span><span class="n">container</span><span class="p">)</span>
	<span class="k">return</span> <span class="kt">uint64</span><span class="p">(</span><span class="n">hash</span><span class="o">.</span><span class="n">Sum32</span><span class="p">())</span>
<span class="p">}</span>
</code></pre></div></div>

<p>v1.17</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="n">HashContainer</span><span class="p">(</span><span class="n">container</span> <span class="o">*</span><span class="n">v1</span><span class="o">.</span><span class="n">Container</span><span class="p">)</span> <span class="kt">uint64</span> <span class="p">{</span>
	<span class="n">hash</span> <span class="o">:=</span> <span class="n">fnv</span><span class="o">.</span><span class="n">New32a</span><span class="p">()</span>
	<span class="n">containerJson</span><span class="p">,</span> <span class="n">_</span> <span class="o">:=</span> <span class="n">json</span><span class="o">.</span><span class="n">Marshal</span><span class="p">(</span><span class="n">container</span><span class="p">)</span>
	<span class="n">hashutil</span><span class="o">.</span><span class="n">DeepHashObject</span><span class="p">(</span><span class="n">hash</span><span class="p">,</span> <span class="n">containerJson</span><span class="p">)</span>
	<span class="k">return</span> <span class="kt">uint64</span><span class="p">(</span><span class="n">hash</span><span class="o">.</span><span class="n">Sum32</span><span class="p">())</span>
<span class="p">}</span>
</code></pre></div></div>

<p>对比我们发现，1.12 版本的计算hash方法和1.17发生了改变，一个是通过struct ，一个是直接json string 计算hash，所以node容器hash和计算出来的hash不匹配导致容器重建</p>

<h2 id="几种升级方式">几种升级方式</h2>
<p>最终我们找到了，跨版本升级造成容器重建的原因，通过上面分析，我们总结出几种跨版本升级的方案</p>

<ul>
  <li>
    <p>替换二进制升级
直接将kubelet 二进制换成1.17版本，相对简单粗暴，会造成容器hash比对失败，业务容器重建</p>
  </li>
  <li>
    <p>修改容器hash升级
将k8s组件暂停，升级kubelet之前，更新每个node 容器的hash，再升级kubelet二进制到新版</p>
  </li>
</ul>

<p>以上两种方案都不太能满足我们内部平滑升级策略</p>
<ul>
  <li>第一种方案 完全不考虑，集群升级会造成业务容器重启，业务方无法接受</li>
  <li>第二种方案 暂且可以接受，不过升级流程繁琐，需要保证每个环节原子性，且不可回滚</li>
</ul>

<h3 id="探索新的升级方式">探索新的升级方式</h3>
<p>既然我们知道了1.12 的hash计算方式，那么我们是否可以 增强hash对比逻辑呢。</p>

<h5 id="增加二次判断">增加二次判断</h5>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="n">containerChanged</span><span class="p">(</span><span class="n">container</span> <span class="o">*</span><span class="n">v1</span><span class="o">.</span><span class="n">Container</span><span class="p">,</span> <span class="n">containerStatus</span> <span class="o">*</span><span class="n">kubecontainer</span><span class="o">.</span><span class="n">ContainerStatus</span><span class="p">)</span> <span class="p">(</span><span class="kt">uint64</span><span class="p">,</span> <span class="kt">uint64</span><span class="p">,</span> <span class="kt">bool</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">expectedHash</span> <span class="o">:=</span> <span class="n">kubecontainer</span><span class="o">.</span><span class="n">HashContainer</span><span class="p">(</span><span class="n">container</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">containerStatus</span><span class="o">.</span><span class="n">Hash</span> <span class="o">!=</span> <span class="n">expectedHash</span> <span class="c">// and more {</span>
		<span class="n">expectedHash</span> <span class="o">=</span> <span class="n">kubecontainer</span><span class="o">.</span><span class="n">HashContainerWith12Version</span><span class="p">(</span><span class="n">container</span><span class="p">,</span> <span class="no">false</span><span class="p">)</span>       <span class="c">// 如果不等 进行1.12 计算hash方法比较</span>
		<span class="k">if</span> <span class="n">containerStatus</span><span class="o">.</span><span class="n">Hash</span> <span class="o">==</span> <span class="n">expectedHash</span> <span class="p">{</span>
			<span class="k">return</span> <span class="n">expectedHash</span><span class="p">,</span> <span class="n">containerStatus</span><span class="o">.</span><span class="n">Hash</span><span class="p">,</span> <span class="n">containerStatus</span><span class="o">.</span><span class="n">Hash</span> <span class="o">!=</span> <span class="n">expectedHash</span>
		<span class="p">}</span>
    <span class="p">}</span>
    <span class="o">...</span>

</code></pre></div></div>
<p>找到方案了，我们验证下自己结论，很遗憾，用了1.12 的计算方法，hash没有匹配上</p>

<h5 id="go-spew-">go-spew !</h5>
<p>容器hash是通过go-spew (不了解的同学可以了解下该项目)计算而来，我们先看下这个module，看下go-spew 怎么hash的。
最终计算逻辑在format 中</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="p">(</span><span class="n">f</span> <span class="o">*</span><span class="n">formatState</span><span class="p">)</span> <span class="n">format</span><span class="p">(</span><span class="n">v</span> <span class="n">reflect</span><span class="o">.</span><span class="n">Value</span><span class="p">)</span> <span class="p">{</span>
    <span class="o">...</span>
	<span class="k">case</span> <span class="n">reflect</span><span class="o">.</span><span class="n">Struct</span><span class="o">:</span>
		<span class="n">numFields</span> <span class="o">:=</span> <span class="n">v</span><span class="o">.</span><span class="n">NumField</span><span class="p">()</span>
    <span class="o">...</span>

</code></pre></div></div>
<p>既然是反射拿到每个字段，value又没变化，那么可以确定是pod 的struct 发生了变化</p>

<h5 id="适配它">适配它</h5>
<p>最终我们对比v1.12 v1.17 pod strcut 发现  v1.17增加了几个字段</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code>	<span class="s">"SubPathExpr"</span><span class="p">,</span>
	<span class="s">"StartupProbe"</span><span class="p">,</span>
	<span class="s">"WindowsOptions"</span><span class="p">,</span>
</code></pre></div></div>
<p>只要spew计算时，忽略这三个字段就ok，最终，我们给spew扩展了一个方法， 支持忽略指定字段计算hash，k8s适配代码</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">func</span> <span class="n">DeepHashObjectWithIgnore</span><span class="p">(</span><span class="n">hasher</span> <span class="n">hash</span><span class="o">.</span><span class="n">Hash</span><span class="p">,</span> <span class="n">ignoreMap</span> <span class="k">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="kt">bool</span><span class="p">,</span><span class="n">objectToWrite</span> <span class="k">interface</span><span class="p">{})</span> <span class="p">{</span>
	<span class="n">hasher</span><span class="o">.</span><span class="n">Reset</span><span class="p">()</span>
	<span class="n">printer</span> <span class="o">:=</span> <span class="n">spew</span><span class="o">.</span><span class="n">ConfigState</span><span class="p">{</span>
		<span class="n">Indent</span><span class="o">:</span>         <span class="s">" "</span><span class="p">,</span>
		<span class="n">SortKeys</span><span class="o">:</span>       <span class="no">true</span><span class="p">,</span>
		<span class="n">DisableMethods</span><span class="o">:</span> <span class="no">true</span><span class="p">,</span>
		<span class="n">SpewKeys</span><span class="o">:</span>       <span class="no">true</span><span class="p">,</span>
	<span class="p">}</span>
	<span class="n">printer</span><span class="o">.</span><span class="n">FprintfWithIgnoreFields</span><span class="p">(</span><span class="n">hasher</span><span class="p">,</span> <span class="s">"%#v"</span><span class="p">,</span> <span class="n">ignoreMap</span><span class="p">,</span> <span class="n">objectToWrite</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="再升级">再升级</h2>
<p>细心的同学可能觉察到，修改源码，下一个版本升级，岂不是脱离社区了?其实，每次大版本升级都会需要人力去跟进，况且</p>
<ul>
  <li>老容器随着业务的升级，会使用新的hash计算逻辑</li>
  <li>下次跨版本升级前，可以扫描历史容器，kubelet也做了相关逻辑记录，判断能否直接丝滑升级</li>
  <li>为适配业务场景，维护着自己的版本，每次升级都会评估业务影响</li>
  <li>没有最好的方案，只有更合适场景的方案</li>
</ul>

<h2 id="集群现状">集群现状</h2>
<p>截止到9月1号，升级node数超过6w+，涉及 17 个k8s 集群稳定升级</p>]]></content><author><name>Maoxiang Pan</name></author><category term="技术" /><summary type="html"><![CDATA[集群主要版本是<=1.12 落后于社区版本太多(升级时，最新1.17已经release)，kubernetes社区更新迭代速度较快，跟进社区高版本，可以保证集群更可靠稳定,跟上社区的发展利大于弊。]]></summary></entry><entry><title type="html">golang的debug 方法 以及pprof的使用</title><link href="https://binpan.me/2020/09/08/go-pprof/" rel="alternate" type="text/html" title="golang的debug 方法 以及pprof的使用" /><published>2020-09-08T00:00:00+08:00</published><updated>2020-09-08T00:00:00+08:00</updated><id>https://binpan.me/2020/09/08/go-pprof</id><content type="html" xml:base="https://binpan.me/2020/09/08/go-pprof/"><![CDATA[<h2 id="docker-的pprof-真的需要reload吗">docker 的pprof 真的需要reload吗</h2>

<p>看下源码 <code class="language-plaintext highlighter-rouge">api/server/server.go</code></p>
<div class="language-golang highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// InitRouter initializes the list of routers for the server.</span>
<span class="c">// This method also enables the Go profiler.</span>
<span class="k">func</span> <span class="p">(</span><span class="n">s</span> <span class="o">*</span><span class="n">Server</span><span class="p">)</span> <span class="n">InitRouter</span><span class="p">(</span><span class="n">routers</span> <span class="o">...</span><span class="n">router</span><span class="o">.</span><span class="n">Router</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">s</span><span class="o">.</span><span class="n">routers</span> <span class="o">=</span> <span class="nb">append</span><span class="p">(</span><span class="n">s</span><span class="o">.</span><span class="n">routers</span><span class="p">,</span> <span class="n">routers</span><span class="o">...</span><span class="p">)</span>

	<span class="n">m</span> <span class="o">:=</span> <span class="n">s</span><span class="o">.</span><span class="n">createMux</span><span class="p">()</span>
	<span class="n">s</span><span class="o">.</span><span class="n">routerSwapper</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">routerSwapper</span><span class="p">{</span>
		<span class="n">router</span><span class="o">:</span> <span class="n">m</span><span class="p">,</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>在 <code class="language-plaintext highlighter-rouge">s.createMux()</code> 中</p>
<div class="language-golang highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">debugRouter</span> <span class="o">:=</span> <span class="n">debug</span><span class="o">.</span><span class="n">NewRouter</span><span class="p">()</span>
<span class="n">s</span><span class="o">.</span><span class="n">routers</span> <span class="o">=</span> <span class="nb">append</span><span class="p">(</span><span class="n">s</span><span class="o">.</span><span class="n">routers</span><span class="p">,</span> <span class="n">debugRouter</span><span class="p">)</span>
<span class="k">for</span> <span class="n">_</span><span class="p">,</span> <span class="n">r</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">debugRouter</span><span class="o">.</span><span class="n">Routes</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">f</span> <span class="o">:=</span> <span class="n">s</span><span class="o">.</span><span class="n">makeHTTPHandler</span><span class="p">(</span><span class="n">r</span><span class="o">.</span><span class="n">Handler</span><span class="p">())</span>
	<span class="n">m</span><span class="o">.</span><span class="n">Path</span><span class="p">(</span><span class="s">"/debug"</span> <span class="o">+</span> <span class="n">r</span><span class="o">.</span><span class="n">Path</span><span class="p">())</span><span class="o">.</span><span class="n">Handler</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>既然默认都是有router的，我们把sock文件绑到端口上就可以使用pprof了</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>wget http://www.dest-unreach.org/socat/download/socat-1.7.3.3.tar.gz
socat <span class="nt">-d</span> <span class="nt">-d</span> TCP-LISTEN:5555,fork,bind<span class="o">=</span>0.0.0.0 UNIX:/var/run/docker.sock
go tool pprof http://127.0.0.1:5555/debug/pprof/heap
</code></pre></div></div>
<p>还有没有更丝滑的方法呢</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl --unix-socket /var/run/docker.sock http://./debug/pprof/goroutine?debug=2
</code></pre></div></div>]]></content><author><name>Maoxiang Pan</name></author><category term="技术" /><summary type="html"><![CDATA[关于golang程序的debug方法，监控，pprof 分析等]]></summary></entry><entry><title type="html">30岁这年</title><link href="https://binpan.me/2020/03/21/30-years-old/" rel="alternate" type="text/html" title="30岁这年" /><published>2020-03-21T00:00:00+08:00</published><updated>2020-03-21T00:00:00+08:00</updated><id>https://binpan.me/2020/03/21/30-years-old</id><content type="html" xml:base="https://binpan.me/2020/03/21/30-years-old/"><![CDATA[<p>同类文章<br />
16年毕业的思考  <a href="http://binpan.me/2016/02/09/hello/">脉脉迁移文章</a><br />
18年工作的思考  <a href="http://binpan.me/2018/01/23/hello-world/">毕业这两年</a></p>

<p>记录下过去的趣事，和未来1-2年的计划吧，小学作文水平，又开始秀下限了</p>

<blockquote>
  <p>30岁，像一根分割线，催你快速成熟，在提升技术的能力的同时，更需要快速提升变现能力</p>
</blockquote>

<p>从几个方面记录吧，<code class="language-plaintext highlighter-rouge">工作</code>，<code class="language-plaintext highlighter-rouge">生活</code>，<code class="language-plaintext highlighter-rouge">情感</code></p>

<h2 id="生活">生活</h2>
<blockquote>
  <p>合租实在太痛苦了，整租又不符合现在的收入水平
  18年初，从北京的大北边，搬到了最南面<br />
  亦庄-经海路，一直以为这是一个很偏僻的地方，生活不是很方便，后来到 后厂村，我发现我还是太年轻<br />
  经海路附近的大族广场，配套设施不要太好，7fresh的蔬菜水果，刷工牌能直接抵扣现金<br />
  那些年吃的太好了，水果一年吃了上万块钱的</p>
</blockquote>

<p><img src="/images/IMG_5673.jpg" alt="jd" /></p>

<h3 id="神奇的舍友">神奇的舍友</h3>

<p>在亦庄这两年，感觉租房生活被下了诅咒 <br />
  遇到了2个神奇的隔壁，隔壁01房间应该被诅咒过<br />
  经海路的房子是要抢的，某企业房子放出来就被订光了，因为东哥总部的人是太多了，刚好目标小区<br />
  作为一枚程(diao)序(si)员，怎么能抢不过别人，
  启动，写代码，一晚上，抓包，编码，测试，验证码识别，提交订单，一气呵成，可把我牛x坏了，启动了N台云主机，监控放出时间，下单，通知等等
  机会总留给有准备的人，一系列的sao操作，房子当然是我的，随后隔壁舍友入住了，我的三观就开始刷新了。</p>

<h4 id="no0-女生">No.0 女生</h4>
<p>专业租房好几年，某ru上的级别信用都200多分了，从来没遇上这样的大姐姐(94年，讨厌的一律大姐姐，其他都统称都是女孩，小仙女)
  说下这个大姐姐的日常吧：</p>
<ol>
  <li>散养喵<br />
  某如明文规定不能养宠物，大姐明目张胆放在客厅养，后来散养，个人以为，只要不影响别人，养也没关系<br />
  来诠释下什么叫散养：晚上撵出去，让猫撒野，白天回来吃饭睡觉，一直以为这是虐待动物，大姐美其名曰:给ta自由<br />
  你自由个锤子，散养带回来病毒细菌怎么办，挠到别人家小孩怎么办，全然不管，小区有小孩的住户说，让看好自己家的猫，防止挠到自己baby，答复是：我家的猫从来不挠人<br />
  不挠个锤子啊老子情人节晚上被挠了，大晚上打车去医院打疫苗，人家回复：我猫打疫苗了，是美国进口的<br />
  不讲理的人，真是没办法。</li>
  <li>私生活<br />
  自称从英国留学回来，说自己家庭都是清华北大，怎么怎么厉害，我就不明白了，这么优秀的家庭，怎么没教导好，什么是 教养<br />
  大姐五一的男朋友是英国人，在家里住了一周后，十一的男朋友换成北美了<br />
  从这件是我总结出来，北美的男朋友体力好，带着耳机掩盖不掉声音<br />
  十一过去，大姐一周没出门，我不太清楚怎么吃东西的，我就知道，客厅散落着内条内衣内裤，下班回家我都快吐了。</li>
</ol>

<p>迷一般的大姐，哈哈哈哈哈哈哈哈 <br />
  小朋友有很多问号</p>

<h4 id="no1-情侣">No.1 情侣</h4>
<p>盼望着，隔壁大姐走了，来了一对东北情侣，大哥是80后，夫人应该是90后的样子<br />
  很和谐了，客厅明亮，干净了，大家都保持的很好<br />
  11月，我在群里说，要搬家了，要换工作了<br />
  隔壁大哥在这种情况下，报警，我是隔断，甩锅给小区物业，小区物业管理人员给你我说，隔壁报警那哥们报警干嘛，你不是要搬走了吗 <br />
  真是给大东北丢脸啊，说好的，不服就当面刚呢</p>

<p>小朋友有很多问号again</p>

<h3 id="生活上的朋友">生活上的朋友</h3>

<p>排除上面两件事，这两年生活还是挺美好<br />
  在JD，比较有生活了，收获了一群好友<br />
  可乐达人轩总，成熟大叔乔总，人气少年康哥，二货中年雷总，美丽少女秋秋<br />
  还有会说韩语的光哥等等，时常在一起互损，冷笑话，自嘲等等，快乐的不行<br />
  同时也结实了南京团队的一群大佬，每个人都是我的标杆，感谢曾经打造的JDOS<br />
  以上朋友均有对象或者有娃，就剩我一个人单身了，求介绍的可以退下了，哈哈</p>

<p>回忆这两年的生活，特别平淡，从一个周五看夜场电影的少年，到10点以后的电影院不考虑的中年<br />
  其实夜场电影院真的特别舒服，人不多，可以安心的看电影</p>

<h2 id="工作">工作</h2>

<blockquote>
  <p>基础架构，底层基础设施<br />
  很多其他行业的朋友说，什么时候才能搞到中高层，哈哈哈哈 快了快了，容我在摸爬滚打几年可好<br />
  换工作的原因，其实很简单，想用公积金hold住房贷，领导也挺支持，毕竟公司涨薪能力有限，后来我做到了，哈哈哈，算是一件开心的事吧。</p>
</blockquote>

<h3 id="2019年的计划">2019年的计划</h3>
<object data="/images/plan-2019.svg" type="image/svg+xml"></object>

<p>2019年的计划完成度并不是很高，主要原因是，没有做好笔记与总结<br />
  主要完成的是:</p>
<ul>
  <li>开始刷leetcode</li>
  <li>k8s 源码阅读了大半</li>
  <li>apue开始分章节刷</li>
  <li>SDN一部分知识</li>
</ul>

<p>pending的一些任务:</p>
<ul>
  <li>CKA 考试 原因价值不如之前了</li>
  <li>TCP/IP协议</li>
  <li>英语</li>
</ul>

<h3 id="为什么要一直打基础底层原理">为什么要一直打基础/底层原理</h3>
<p>之前有学弟，或者其他一些学生问我一个问题：操作系统重要吗，数据结构重要吗<br />
  重要：因为这是地基，决定了记得高度，很多问题的排查需要 基础/底层 的知识支撑<br />
  不重要： 如果你是一个业务的开发，很多时候并用不到，不用的东西，遗忘是最快的<br />
  如果你想在这个行业走的更长，还是建议多学习下，leetcode能预防老年痴呆，cs课程能预防35岁中年危机，何乐不为</p>

<h3 id="工作上的收获">工作上的收获</h3>

<p>18年开始，有幸见证了，多次的双11，618<br />
  我也看到了，基础架构的能力，从紧张，到喝着咖啡看监控，这期间付出了多大的努力，可能业务研发同学不曾看多过<br />
  这2年，收获了太多，从只写代码，到要考虑整体架构设计，落地方案<br />
  荣幸在TIG奋斗过，从处理问题，到帮研发同时排查问题<br />
  也荣幸在 业界所谓的全球最大的k8s集群中，贡献一点力量<br />
  与之前相比，学习，理解能力更快，更强了</p>

<h3 id="2020-上半年的计划">2020 上半年的计划</h3>
<p>因为疫情，2020 上半年基本已经没了，好心疼</p>

<object data="/images/2020上.svg" type="image/svg+xml"></object>

<h2 id="情感">情感</h2>
<blockquote>
  <p>2020的计划是 提升变现技能，脱单<br />
  希望有个女孩能见证我的成长，如果她还不出现，那就下一个山顶见</p>
</blockquote>

<h3 id="提升变现技能">提升变现技能</h3>
<p>如果是2年前，我认为技术是一切，现在我认为，技术是一部分<br />
  技术是重要的指标，打铁还需自身硬，但是，项目管理，落地能力也同样重要<br />
  2020-2022 这1-2年的时间，在提升基础技能的同时，提升项目管理，技术落地能力<br />
  欠下的债总是要还的，之前没有打好的基础，没有填补的技能，躲不过，还是要啃下来，像刷leetcode，咬牙坚持<br />
  在开源/自建项目中，需要有快速落地与推广的能力，并扩大业界影响力，包装自己同样很重要</p>

<h3 id="脱单">脱单</h3>

<h4 id="理想的女孩是什么样的">理想的女孩是什么样的</h4>
<ol>
  <li>
    <p>有终生学习的思想
    不是结婚了，就不学习不进取了，我有能力养的起你，但是希望你有坚持提升自己的想法<br />
    提升任何都可以，不光有工作，还有生活，其他技能，甚至是 衣品等等</p>
  </li>
  <li>
    <p>收入相当或者不能差太多(少于二分之一)
    希望我们差距不太大，这样有利于生活的稳定</p>
  </li>
  <li>
    <p>可以比我小，但是不能差太大
    接受比我大的，之前一个同事40了，看起来像是25，6岁的女孩<br />
    但是太小的不行，不稳定</p>
  </li>
  <li>
    <p>遇见更好的你
    如果还是遇见不到你<br />
    要么你站在原地别动，我开导航来找你<br />
    要么，我们下一个山峰见</p>
  </li>
</ol>

<h2 id="写在最后">写在最后</h2>
<h3 id="2020的计划">2020的计划</h3>
<ul>
  <li>脱单</li>
  <li>提升技能</li>
  <li>英语</li>
</ul>

<p>往事作序,来日为章</p>

<p>我们2年后再见<br />
我们32岁再见～</p>]]></content><author><name>Maoxiang Pan</name></author><category term="职业生涯" /><summary type="html"><![CDATA[又是2年后的总结]]></summary></entry><entry><title type="html">给openwrt安装quagga</title><link href="https://binpan.me/2019/05/10/openwrt-quagga/" rel="alternate" type="text/html" title="给openwrt安装quagga" /><published>2019-05-10T00:00:00+08:00</published><updated>2019-05-10T00:00:00+08:00</updated><id>https://binpan.me/2019/05/10/openwrt-quagga</id><content type="html" xml:base="https://binpan.me/2019/05/10/openwrt-quagga/"><![CDATA[<p>我们有一部分集群是使用BGP的网络模式,基于quagga + calico 进行了一部分功能的改造，由于本地没有物理机，测试起来比较麻烦。<br />
  今天灵机一动，尝试在花了35块钱买的路由器上安装quagga，过程比较曲折，搞了一天才把这玩意搞定。</p>

<h2 id="刷pandora固件">刷pandora固件</h2>
<p>未加电情况下，按下rest，进入breek web,选择固件升级即可</p>

<h2 id="准备编译环境">准备编译环境</h2>
<p>对于跨平台编译的环境，官方给出了一个安装步骤  <code class="language-plaintext highlighter-rouge">https://oldwiki.archive.openwrt.org/doc/devel/crosscompile</code> 之前按照这个方式，比较麻烦，网上有人准备了docker环境，一键启动。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker run -it --network=host -d -v/root/quagga-0.99.22.1:/root/quagga-0.99.22.1 openwrtio/openwrt-sdk-gee-ralink  bash
</code></pre></div></div>
<p>把本地的quagga源码映射到容器中，方便修改，拷贝文件</p>

<p>直接开始编译</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>export STAGING_DIR=/root/openwrt/hc5761/staging_dir/toolchain-mipsel_r2_gcc-4.6-linaro_uClibc-0.9.33.2/bin 
STAGING_DIR=/root/openwrt/hc5761/staging_dir/toolchain-mipsel_r2_gcc-4.6-linaro_uClibc-0.9.33.2/bin \
CC=/root/openwrt/hc5761/staging_dir/toolchain-mipsel_r2_gcc-4.6-linaro_uClibc-0.9.33.2/bin/mipsel-openwrt-linux-gcc \
./configure \
--with-gnu-ld=mipsel-openwrt-linux-ld \
--build=x86_64-unknown-linux-gnu  \
-host=mips-openwrt-linux-uclibc \
--libdir=/opt/lib  \
--enable-zebra \
--enable-bgpd   \
--enable-user=root  \
--enable-group=root

make
</code></pre></div></div>

<h2 id="问题记录">问题记录</h2>
<ul>
  <li>–enable-vtysh
目前如果开启vtysh报错
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>checking for main in -lreadline... no
configure: error: vtysh needs libreadline but was not found and usable on your system.
</code></pre></div>    </div>
    <p>找不到依赖，查看<code class="language-plaintext highlighter-rouge">toolchain-mipsel_r2_gcc-4.6-linaro_uClibc-0.9.33.2</code>存在readline的lib<br />
有时间需要确认下，不过没啥太大关系，使用telnet一样配置quagga</p>
  </li>
  <li>
    <p>openwrt报错提示找不到libzebra.so<br />
设置–libdir=/opt/lib 然后把编译出来的so文件复制到/opt/lib即可</p>
  </li>
  <li>
    <p>缺少依赖的排查<br />
使用ldd命令，可以查看可执行文件的so依赖，以及缺少的会<code class="language-plaintext highlighter-rouge">=&gt; not found</code></p>
  </li>
  <li>运行bgpd没任何提示信息<br />
第一次编译的时候没有指定<code class="language-plaintext highlighter-rouge">enable-user</code>导致在root下启不来，路由器并没有报错</li>
</ul>]]></content><author><name>Maoxiang Pan</name></author><summary type="html"><![CDATA[networking]]></summary></entry><entry><title type="html">容器化跨主机通讯的一些思考与经验</title><link href="https://binpan.me/2019/05/02/container-networking/" rel="alternate" type="text/html" title="容器化跨主机通讯的一些思考与经验" /><published>2019-05-02T00:00:00+08:00</published><updated>2019-05-02T00:00:00+08:00</updated><id>https://binpan.me/2019/05/02/container-networking</id><content type="html" xml:base="https://binpan.me/2019/05/02/container-networking/"><![CDATA[<p>在大规模的kubernetes集群情况下，网络的拓扑设计显的尤为的重要，业界的很多参考，也有很多成熟的解决方案，例如使用flannel，calicao，以及quagga等现有的解决方案。<br />
  现有的一些解决方案有时并不能解决我们的业务场景，所以会根据现有成熟方案进行一些略微的改造<br />
  我们重点分析BGP的决绝方案</p>

<h1 id="使用bgp">使用BGP</h1>
<p>整体拓扑图和解决方案如下:</p>

<object data="/images/bdp.svg" type="image/svg+xml"></object>
<p>第三层是node节点，在每个node节点运行bdpd<br />
每个容器的ip会通过bdpd汇报给tor swatich，从而打到整个集群容器互通的目的</p>]]></content><author><name>Maoxiang Pan</name></author><summary type="html"><![CDATA[networking]]></summary></entry><entry><title type="html">使用Quagga实现跨机器通讯</title><link href="https://binpan.me/2019/04/28/docker-network/" rel="alternate" type="text/html" title="使用Quagga实现跨机器通讯" /><published>2019-04-28T00:00:00+08:00</published><updated>2019-04-28T00:00:00+08:00</updated><id>https://binpan.me/2019/04/28/docker-network</id><content type="html" xml:base="https://binpan.me/2019/04/28/docker-network/"><![CDATA[<h2 id="整体拓扑图">整体拓扑图</h2>

<object data="/images/quagga-docker.svg" type="image/svg+xml"></object>

<p>两台安装docker的centos(虚拟机)、docker0网段已经修改 /etc/docker/daemon.json <code class="language-plaintext highlighter-rouge">{"bip":"172.2.0.1/24"}</code><br />
目前云主机不能模拟出来，后面会研究下，应该是系统做了限制</p>

<h2 id="安装quagga">安装quagga</h2>

<p>首先安装quagga</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># yum install quagga
</code></pre></div></div>

<p>centos7 需要关闭SELinux或者允许Zebra守护进程写入它的配置目录</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># setsebool -P zebra_write_config 1 
</code></pre></div></div>
<p>启动2个守护进程：</p>
<ul>
  <li>Zebra:一个核心守护进程用于内核接口和静态路由.</li>
  <li>BGPd:一个BGP守护进程.</li>
</ul>

<h2 id="配置日志路径">配置日志路径</h2>

<p>复制zebra.conf</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># cp /usr/share/doc/quagga-?/zebra.conf.sample /etc/quagga/zebra.conf 
</code></pre></div></div>
<p>启动zebra</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># systemctl start zebra
# systemctl enable zebra 
</code></pre></div></div>
<p>通过vtysh配置2台机器</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># vtysh
qg1# configure terminal    ## 进入配置终端
qg1(config)# log file /var/log/quagga/quagga.log
qg1(config)# exit
qg1# write

</code></pre></div></div>
<h2 id="配置对等的ip地址">配置对等的IP地址</h2>
<blockquote>
  <p>因为是直接挂在了路由器上，此步骤可以忽略 2台机器都配置</p>
</blockquote>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># vtysh
qg1# show interface
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Interface docker0 is up, line protocol detection is disabled
Interface eth0 is up, line protocol detection is disabled
Interface lo is up, line protocol detection is disabled
</code></pre></div></div>

<p>配置eth0接口</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># vtysh
qg1# configure terminal         # 进入配置
qg1(config)# interface eth0     # 配置eth0
qg1(config)# ip address 192.168.15.107/32    # 设置ip
qg1(config)# description "to Router-A"       # 添加描述
qg1(config)# no shutdown
qg1(config)# exit
</code></pre></div></div>
<p>验证配置</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>qg1# show interface 
qg1# show interface description
</code></pre></div></div>
<p>验证正确保存配置</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>qg1# write
</code></pre></div></div>

<h2 id="配置bgp对等">配置BGP对等</h2>
<blockquote>
  <p>两台机器都配置</p>
</blockquote>

<p>复制sample配置,并启动</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># cp /usr/share/doc/quagga-?/bgpd.conf.sample /etc/quagga/bgpd.conf 
# systemctl start bgpd
# systemctl enable bgpd
</code></pre></div></div>
<p>查看BGP默认配置，覆盖默认配置</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># vtysh
# show running-config 
router bgp 7675
</code></pre></div></div>

<p>配置拓扑图的参数</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>qg1# configure terminal
qg1# no router bgp 7675
qg1# router bgp 100
qg1# no auto-summary
qg1# no sync
qg1(config-router)# neighbor 192.168.15.108 remote-as 200
qg1(config-router)# neighbor 192.168.15.108 description "provider B"
qg1(config-router)# exit
qg1(config)# exit
qg1# write
</code></pre></div></div>
<p>路由器都被配置好，两台路由器之间的对等将被建立。运行下面的命令确认：</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>qg1# show ip bgp summary 
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>BGP router identifier 192.168.15.107, local AS number 100
RIB entries 3, using 336 bytes of memory
Peers 1, using 4560 bytes of memory

Neighbor        V    AS MsgRcvd MsgSent   TblVer  InQ OutQ Up/Down  State/PfxRcd
192.168.15.108  4   200     336     338        0    0    0 05:33:30        0

Total number of neighbors 1
</code></pre></div></div>

<h2 id="配置前缀通告">配置前缀通告</h2>
<p>在qg1中：</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>qg1# configure terminal
qg1(config)# router bgp 100
qg1(config)# network 172.17.0.0/24
qg1(config)# exit
qg1# write
</code></pre></div></div>
<p>在qg2中：</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>qg2# configure terminal
qg2(config)# router bgp 200
qg2(config)# network 172.17.1.0/24
qg2(config)# exit
qg2# write
</code></pre></div></div>

<h2 id="验证前缀通告">验证前缀通告</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>qg1# show ip bgp summary 
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Neighbor        V    AS MsgRcvd MsgSent   TblVer  InQ OutQ Up/Down  State/PfxRcd
192.168.15.108  4   200     340     342        0    0    0 05:37:18        1
</code></pre></div></div>

<p>查看细节</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>qg1# show ip bgp neighbors 192.168.15.108 advertised-routes 
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>   Network          Next Hop            Metric LocPrf Weight Path
*&gt; 172.1.0.0/24     192.168.15.107           0         32768 i
</code></pre></div></div>

<p>前缀是我们从邻居接收到的</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>qg1# show ip bgp neighbors 192.168.15.108 routes
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>   Network          Next Hop            Metric LocPrf Weight Path
*&gt; 172.2.0.0/24     192.168.15.108           0             0 200 i
</code></pre></div></div>
<p>查看BGP路由</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>qg1# show ip bgp
   Network          Next Hop            Metric LocPrf Weight Path
*&gt; 172.1.0.0/24     0.0.0.0                  0         32768 i
*&gt; 172.2.0.0/24     192.168.15.108           0             0 200 i

qg1#  show ip route
代码: K - 内核路由, C - 已链接 , S - 静态 , R - 路由信息协议 , O - 开放式最短路径优先协议,
 
       I - 中间系统到中间系统的路由选择协议, B - 边界网关协议, &gt; - 选择路由, * - FIB 路由

# ip route 
</code></pre></div></div>

<h2 id="启动一个容器测试是否能通">启动一个容器测试是否能通</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[qg1]# docker run -it -d nginx
[qg2]# curl curl 172.2.0.2
</code></pre></div></div>
<p>跨物理机的容器之间是互通的</p>]]></content><author><name>Maoxiang Pan</name></author><summary type="html"><![CDATA[networking]]></summary></entry><entry><title type="html">linux 性能分析之cpu</title><link href="https://binpan.me/2019/02/28/linux-performance-cpu/" rel="alternate" type="text/html" title="linux 性能分析之cpu" /><published>2019-02-28T00:00:00+08:00</published><updated>2019-02-28T00:00:00+08:00</updated><id>https://binpan.me/2019/02/28/linux-performance-cpu</id><content type="html" xml:base="https://binpan.me/2019/02/28/linux-performance-cpu/"><![CDATA[<h1 id="术语">术语</h1>
<h2 id="cpu架构与术语解释">CPU架构与术语解释</h2>

<p><img src="/images/cpu.png" alt="CPU架构" /></p>

<ul>
  <li>处理器(processor):  插到系统槽或主板上的物理芯片，可以是一块或者多块</li>
  <li>核(Core): 多核处理器上的一个独立的CPU实例，核是使用处理器的一种扩展方式</li>
  <li>硬件线程(Hardware thread): 在同一个核上同时能执行多个线程的CPU架构</li>
</ul>

<h3 id="cpu绑定">CPU绑定</h3>
<p>把一个进程和线程绑定到单个CPU或者CPU组上，这样可以提升内存I/O的性能
主要有两种实现方式:</p>
<ul>
  <li>进程绑定  配置一个进程跑在单个CPU上</li>
  <li>独占CPU组  将CPU分组，进程只能跑在这些分组上</li>
</ul>

<p>linux的cpuset可以实现这一功能</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># mkdir /dev/cpuset
# mount -t cpuset cpuset /dev/cpuset 
# cd /dev/cpuset
# mkdir prodset  # create a cpuset called "prodset"
# cd prodset
# echo 7-10 &gt; cpus # assign CPUs 7-10
# echo 1 &gt; cpu_exclusive # make prodset exclusive
# echo 1159 &gt; tasks # assign PID 1159 to prodset
</code></pre></div></div>

<h2 id="cpu内存缓存">CPU内存缓存</h2>

<p>为了提高内存的I/O性能，处理器提供多种硬件缓存，级数越小的速度越快，同时也引入一些线程数据共享同步问题和并发问题。</p>

<p><img src="/images/cpu-cache.png" alt="内存缓存" /></p>

<h2 id="cpu运行队列">CPU运行队列</h2>

<p><img src="/images/cpu-queue.png" alt="CPU运行队列" /></p>

<p>正在运行和就绪运行的线程数量，表示了<strong>CPU的饱和度</strong>，上图表示有四个线程和一个CPU上运行的线程。花在等待CPU运行上的时间称为<strong>运行队列延时</strong>。
内核会为每个CPU提供一个运行队列，并尽量使同一个线程运行在同一个队列中，这样避免了上下文切换，提高性能。</p>

<h1 id="常用工具及名词解释">常用工具及名词解释</h1>
<p>一些linux 下的cpu性能分析工具，使用方法</p>

<h2 id="uptime">uptime</h2>
<p>打印1，5，15分钟的平均负载</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pi@raspberrypi:~ $ uptime
 15:48:27 up 4 days,  5:37,  1 user,  load average: 0.28, 0.13, 0.09
</code></pre></div></div>
<p><strong>平均负载</strong> 
是指单位时间内，系统处于可运行状态和不可中断状态的平均进程数。
这个值不是cpu使用率，是通过正在运行的线程数和正在排队的线程数计算的。
这个值的意义：平均负载大于CPU个数，表示CPU不足以服务线程，有线程在等待；小于线程数表示有余量，线程想执行的时候执行。</p>

<p><strong>平均负载 vs CPU 使用率</strong>
平均负载和CPU使用率并不一定是一样的</p>
<ul>
  <li>CPU 密集型进程，使用大量 CPU 会导致平均负载升高，此时这两者是一致的</li>
  <li>I/O 密集型进程，等待 I/O 也会导致平均负载升高，但CPU 使用率不一定很高</li>
</ul>

<p>##</p>]]></content><author><name>Maoxiang Pan</name></author><category term="linux" /><summary type="html"><![CDATA[性能之巅读书笔记]]></summary></entry><entry><title type="html">毕业这两年</title><link href="https://binpan.me/2018/01/23/hello-world/" rel="alternate" type="text/html" title="毕业这两年" /><published>2018-01-23T00:00:00+08:00</published><updated>2018-01-23T00:00:00+08:00</updated><id>https://binpan.me/2018/01/23/hello-world</id><content type="html" xml:base="https://binpan.me/2018/01/23/hello-world/"><![CDATA[<p>时间过的真快，都说毕业三年决定了职场发展，掐指一算，毕业到现在已经一年半了，这一年半 收获了很多，技术上，人际上。</p>

<p>不知道哪来的勇气，加入一家仅有四个人的创业公司，也许是年少无知，也许是刚毕业更能接受挫折，也许是创始人以及团队成员的背景吸引了我，google，yelp，yahoo 工程师确实在某方面很优秀，羡慕他们有海外工作的经历，羡慕一口流利的口语。</p>

<p>这一年半，收到同学，朋友最多的消息是：“哥们换工作了。”，不知道是消息同步，还是为了证明自己的能力又有了进步，而我，在这家公司待了2年，2年也许不长，但对于这个浮躁社会，对于这个像拼命证明自己的时代，2年似乎也不算太短。</p>

<p>不过还好，这2年，长进了不少，当然是对于自己来说，依稀的记得认识CEO的时候，是16年2月份，可笑的是当时是为了给前任介绍工作，老板对我挺感兴趣，就这样阴差阳错，5月份终于被CEO征服加入创业团队。</p>

<p>挺感谢那段时间，初创公司没有什么技术债务，我们用的都是一些看起来当时在国内还算新的技术，容器化，云，ci/cd，devops，我们敢用刚刚release的开源框架，因为我们喜欢折腾，也经得起折腾。</p>

<p>过去的这2年，我每天都在想，我是一个什么样的人，我要成为一个什么样的人，我是java方向还是容器方向，我不知道，因为这两个都是我想做的，对于java，对于jvm，我觉得是一种挑战，对于容器，可以更深入的了解 linux。</p>

<p>在公司提升了自己的几点：</p>

<ol>
  <li>
    <p>ci/cd
  归功于公司年轻，工程开始的时候，希望标准化，所以使用了 travis + jarcoco ，整个工程在travis 编译，build image 启动，到测试，每一个pr都会有人去看（虽然只有几个人，但是每个pr都会有人看，这样保证了知识的传递）</p>
  </li>
  <li>
    <p>容器化部署
  当时选择用docker(v1.11)，是为了更好的发布部署工程，当然这也引入了很多坑，随着docker的版本的不断升级，以及提issue，最终稳定运行，平台经历过了mesos + marathon 到 swarm 最后可能要决策使用k8s(离职的时候还没有换)</p>
  </li>
  <li>
    <p>参与社区
  公司基于kafka connect 进行开发，引用了一些开源项目，debezium等，在使用过程中遇到的坑，提issue或者bug fix。参与社区，贡献社区是一件很有成就感的事情。</p>
  </li>
</ol>

<p>关于职场发展:</p>

<p>这个问题其实每天都在考虑，自己想成为哪一个领域的专家，是java方向的还是容器方向。
  特别有趣的一点是，似乎钟爱容器，记得那段时间，由此生病，我迷迷糊糊的在思考，好难受啊，不如restart自己就好了。(docker run restart)</p>

<p>从原理上来讲，容器就是操作系统隔离出来的一块资源，从宏观上来讲，是被虚拟化出来的一台机器，有时候在想，我们是不是也是被虚拟出来的，我们所有的一切也是寄宿在一个高级的物种上</p>

<p>可怕可怕。</p>

<p>毕业这两年，应该好好总结下，先占个地。</p>]]></content><author><name>Maoxiang Pan</name></author><category term="职业生涯" /><summary type="html"><![CDATA[时间过的真快，总结毕业]]></summary></entry><entry><title type="html">脉脉迁移文章</title><link href="https://binpan.me/2016/02/09/hello/" rel="alternate" type="text/html" title="脉脉迁移文章" /><published>2016-02-09T00:00:00+08:00</published><updated>2016-02-09T00:00:00+08:00</updated><id>https://binpan.me/2016/02/09/hello</id><content type="html" xml:base="https://binpan.me/2016/02/09/hello/"><![CDATA[<blockquote>
  <p>转自 本人脉脉，在今天看来，文章至今已经将近2年，文章内容一点没舍得修改，记录当年的年少无知，文章没啥营养</p>
</blockquote>

<p>写点log记录下大学的点滴，以最谦虚的姿态记叙大学的学习历程，程序员一路走来真心不容易，在任何时候都可能受到其他诱惑而放弃，游戏、兼职、各种所谓的“忙”。</p>

<p>先介绍下来大学之前：</p>

<p>在我上小学的时候，姐姐上大学，一个假期回来的时候带回来一本复印版的谭浩强c语言，并教我怎么写c计算一个加法(其实应该是完全她写的，我只是看了下输入两个数，求和的过程)，之后上学期间我按照书挨个练习玩，上初三的时候参加 市级 信息学奥林匹克竞赛并获得一等奖，在初中(04级)期间倒弄各种病毒，我记得有灰鸽子，还有什么x-man还是啥，如果看官也是从那时候走过来，应该明白，我把病毒通过软盘考回家做实验玩，当时我家电脑应该有几十种病毒吧。(现在想想，纯粹瞎捣鼓)</p>

<p>高中时候没啥大的动作，在此期间，参加过一次高中组信息学奥林匹克竞赛，获得三等奖(应该是参与奖吧，我记得那时候就有快排解决问题，读者深究可以看看NOI的介绍)。</p>

<p>ok，前提介绍完毕。下面是大学生涯。</p>

<p>log-freshman.out</p>

<p>新生报到第一天，下着小雨，听到校园广播 说北斗星网站(不是软文，看官继续)，姐夫说这个社团听起来不错，加这一个就行了，多了没用。</p>

<p>ok，报名参加，经过面试，进入技术部，从此开始了编码生涯，当时网站是php写的，于是，我开始学习(当然之前html…)php，过来人都应该记得php100这个网站，还有兄弟连，从0开始学一门知识有的时候挺费劲，尤其是在没有其他经验的情况下，期间走了很多弯路，学完smarty之后，开始迷茫不知道自己该怎么继续学习，而当时技术部门的人越来越少，没法交流学习，于是开始在网上各种搜集资料。(大一大二我宿舍莫名的bug，晚上不停电，电表也不走，使我有更充裕的时间搜索资料，学习，经常通宵学习)</p>

<p>也是在大一这一年，认识文龙兄，现在依旧是好哥们，他比我大一届，也是一个技术迷，曾大学辍学创过业，因此从学长变同学。</p>

<p>大一的总结其实就是：一直看视频自学php，还没学出啥成果，但是能写一个简陋的留言板，其实那时的迷茫应该是缺乏生产上的经验。</p>

<p>log-sophomore&amp;junior.out</p>

<p>大二这一年，是一个转折年，因为，我决定要搞java，在baidu，google(感谢我的同班同学俊源教我用某agent)，之后，发现网络上有java的一整套视频，从java到java ee 最后android。</p>

<p>定下来要搞java后，开始下视频，java入门用的是毕向东的堪称经典啊，按照搜集的java学习线路，搜集线路图上的资料，开始学习。(搜集资料浪费了好长时间，那时候好傻，某宝 1块钱搞定，不过搜索引擎倒是玩溜了)。</p>

<p>接下来的日子又是，宿舍，食堂，图书馆，能翘的课都翘了，看书，看视频，学完java基础，看完jave ee(非三大框架)时候，大二暑假要来了(应该是离放假2个月)，一个学长，来学校想找几个暑假实习，刚好的机会，但是招实习是要android，于是跳过java ee 看android视频，实习要求是要做一个android的应用，我和朋友(现在在多米)开始搞android，看视频学andorid后，为北斗星网站做了一个新闻客户端，简单的一个欢迎界面，listview，用httpclient请求的网站数据，解析xml显示文章，最后通过，暑假实习。</p>

<p>注意，其实到现在为止，所有的经验都是视频学到的，真正的经验算是从这次暑假实习，增长的挺快的，学长是架构师级别的，我们去负责参与一个android项目日记模块的完善，期间和学长交流的很多，那时候才知道设计模式等等。</p>

<p>暑假实习结束后，想做一个课程表的应用，很多人不看好，学长直接说，这个这么简单没意思，于是一个人做了，当时简易的课程表有一个比较任性化的设计，可以收藏某个班的课表，这样有想蹭课的同学可以随时客串自己的业余时间，做完之后，简单贴吧发帖宣传了下，当日下载量2000人，当时还小骄傲了一下，亲眼在上课路上看到一个同学掏出手机点开我的app看课表特别开心。</p>

<p>做android的过程中确实学到了很多，但是慢慢发现，android很多时候在设计界面，对这个不太感兴趣，而且之前的计划有java ee的，于是再此期间，把java ee的知识补充了，并在上学期间，兼职了几个小web项目。(不是简历就不在这写了)</p>

<p>log-senior.out</p>

<p>大四这年，参与各种面试，由于一些事情，错过了校招，10月底才开始投简历，到现在，也面试了11家公司，有创业公司的，也有市值60多亿的，拿到7+ offer吧，期间不断总结，补充空缺，感谢过去还算奋斗的自己。</p>

<p>大学期间，干过很多疯狂的时，曾经图书馆一座一天王忘记吃饭，曾经年三十晚上码过代码，曾经假期拖了以行李箱书回家，除了上课睡觉时间，其他时间应该都在图书馆度过了吧，我的大学远远比我想象的更要精彩，免修过，逃课过，知足了。</p>

<p>目前被活捉，有几家公司让毕业直接联系去的，要想活捉我，哼哼～～～你带上我，我带上文龙兄啊，</p>

<p>程序猿不宜，请珍爱。</p>

<p>ps:语文是体育老师教的，本文只作为分享个人经历用</p>]]></content><author><name>Maoxiang Pan</name></author><category term="职业生涯" /><summary type="html"><![CDATA[转自 本人脉脉，在今天看来，文章至今已经将近2年，文章内容一点没舍得修改，记录当年的年少无知，文章没啥营养]]></summary></entry></feed>