<?xml-stylesheet href="/rss.xsl" type="text/xsl"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>翟志军 Jack Zhai</title>
    <link>https://showme.codes/</link>
    <description>Recent content on 翟志军 Jack Zhai</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <copyright>showme.codes</copyright>
    <lastBuildDate>Sat, 13 Sep 2025 00:00:00 +0000</lastBuildDate>
    
        <atom:link href="https://showme.codes/index.xml" rel="self" type="application/rss+xml" />
    
    
    
        <item>
        <title>从法庭到代码：谁主张谁举证的工程实践</title>
        <link>https://showme.codes/zh-cn/2025-09-13-applying-the-burden-of-proof-principle/</link>
        <pubDate>Sat, 13 Sep 2025 00:00:00 +0000</pubDate>
        
        <guid>https://showme.codes/zh-cn/2025-09-13-applying-the-burden-of-proof-principle/</guid>
        <description>翟志军 Jack Zhai https://showme.codes/zh-cn/2025-09-13-applying-the-burden-of-proof-principle/ -&lt;p&gt;2023年，与开发商打了不少官司。学习了不少庭审知识。发现有些实践或者原则放到软件工程中也是有效的。就比如谁主张谁举证这一基本原则。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;谁主张谁举证&lt;/strong&gt;就是当事人对自己提出的主张提供证据并加以证明。&lt;/p&gt;
&lt;p&gt;举例来说，张三说隔壁老王侵占了自己的宅基地。张三必须自己拿证据来证明自己说的事实。而不是让老王拿证据出来证明自己没有侵占。&lt;/p&gt;
&lt;p&gt;在软件工程中，服务A调用服务B，出问题了。服务A认为是服务B的问题，这时，根据谁主张谁举证原则，服务A的开发应该自己找出证据来证明自己的观点是正确的，而不应该让服务B来提供证据证明自己没有问题。&lt;/p&gt;
&lt;p&gt;因为如果让服务B证明自己没有问题，怎么证明自己没有问题呢？怎么证明，证据都不够充分。而且会浪费服务B的团队人力。&lt;/p&gt;
&lt;p&gt;所以，在团队中，让大家达成“谁主张谁举证”的共识会带来以下好处：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;节约团队的人力。如果服务A能提供有效证据，那么就不需要服务B去证明自己没有问题了。&lt;/li&gt;
&lt;li&gt;倒逼团队形成故障现场保留的习惯，比如打印良好的日志等&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;不过，我们需要注意的是，“谁主张谁举证”并不意味着，服务B团队不配合服务A一起排查问题。在一个公司里，基本的团队协作原则与底线是要有的。&lt;/p&gt;
&lt;p&gt;那是不是所有的场景都是谁主张谁举证？&lt;/p&gt;
&lt;p&gt;也有特殊情况。有些场景，是需要进行举证责任倒置的。开发商要拿证据来证明自己的转供电是合法的，而不应该让业主拿证据来证明开发商转供电不合法。
也就是证据只能是“被告方”能提供的情况下，我们就不是谁主张谁举证了。&lt;/p&gt;
&lt;p&gt;以上只是我个人的思维实验。&lt;/p&gt;
&lt;p&gt;在工作中，作为DevOps工程师，我经常遇到开发者质疑云厂商的Redis或者RDS出了问题。&lt;/p&gt;
&lt;p&gt;根据我过往的经验，只是质疑，没有证据的情况，大多是开发者自己代码的问题。通常我会自己看看监控的同时，会要求他们加日志，然后想办法复现问题。&lt;/p&gt;
&lt;p&gt;最后，一个公司一个团队里，如何低成本的达成谁主张谁举证这个共识？&lt;/p&gt;
&lt;p&gt;我目前还没有答案。&lt;/p&gt;
- https://showme.codes/zh-cn/2025-09-13-applying-the-burden-of-proof-principle/ - showme.codes</description>
        </item>
    
    
    
        <item>
        <title>An Example Implement Ansible Deployment on Github Action</title>
        <link>https://showme.codes/en/2024-04-22-github-actions-ansible/</link>
        <pubDate>Mon, 22 Apr 2024 00:00:00 +0000</pubDate>
        
        <guid>https://showme.codes/en/2024-04-22-github-actions-ansible/</guid>
        <description>翟志军 Jack Zhai https://showme.codes/en/2024-04-22-github-actions-ansible/ -&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#eed;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;&lt;/span&gt;- &lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;name&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;write secrets into json&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;run&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;|&lt;span style=&#34;color:#cd5555&#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:#cd5555&#34;&gt;    echo &amp;#34;${{ toJSON(secrets) }}&amp;#34; &amp;gt; _github_secrets.json&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;&lt;/span&gt;- &lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;name&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;write github repo vars into json&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;run&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;|&lt;span style=&#34;color:#cd5555&#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:#cd5555&#34;&gt;    echo &amp;#34;${{ toJSON(vars) }}&amp;#34; &amp;gt; _github_vars.json&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;&lt;/span&gt;- &lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;name&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;write ssh private key&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;run&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;|&lt;span style=&#34;color:#cd5555&#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:#cd5555&#34;&gt;    echo &amp;#34;${{ secrets.STAG_SSH_PRIVATE_KEY }}&amp;#34; &amp;gt; ${{ github.workspace }}/.ssh_private_key.pem
&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:#cd5555&#34;&gt;    chmod 0400 ${{ github.workspace }}/.ssh_private_key.pem&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;&lt;/span&gt;- &lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;name&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;write ssl certificate&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;run&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;|&lt;span style=&#34;color:#cd5555&#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:#cd5555&#34;&gt;    echo &amp;#34;${{ secrets.showmecodes_TLS_CERTIFICATES }}&amp;#34; &amp;gt; ${{ github.workspace }}/showmecodes.ai.pem
&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:#cd5555&#34;&gt;    echo &amp;#34;${{ secrets.showmecodes_TLS_KEY }}&amp;#34; &amp;gt; ${{ github.workspace }}/showmecodes.ai.key&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;&lt;/span&gt;- &lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;name&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;deploy showmecodes to stag&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;uses&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;dawidd6/action-ansible-playbook@v2&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;with&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;playbook&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;playbook-showmecodes.yml&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;key&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;${{ secrets.STAG_SSH_PRIVATE_KEY }}&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;options&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;|&lt;span style=&#34;color:#cd5555&#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:#cd5555&#34;&gt;      --inventory env_vars/${{env.APP_ENV}}/hosts.yaml
&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:#cd5555&#34;&gt;      --extra-vars &amp;#34;app_backend_zip_path=${{ needs.init_build_version.outputs.backendArtifactName }} app_frontend_zip_path=${{ needs.init_build_version.outputs.fontendStagArtifactName }} app_version=${{ needs.init_build_version.outputs.VERSION }} ansible_ssh_private_key_file=${{ github.workspace }}/.ssh_private_key.pem showmecodes_tls_certificate_file=${{ github.workspace }}/showmecodes.ai.pem showmecodes_tls_private_key_file=${{ github.workspace }}/showmecodes.ai.key&amp;#34; --extra-vars=@_github_vars.json --extra-vars=@_github_secrets.json&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;      
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;- https://showme.codes/en/2024-04-22-github-actions-ansible/ - showme.codes</description>
        </item>
    
    
    
        <item>
        <title>Web前端构建之依赖版本管理最佳实践</title>
        <link>https://showme.codes/zh-cn/2024-03-12-frontend-engineering/</link>
        <pubDate>Tue, 12 Mar 2024 00:00:00 +0000</pubDate>
        
        <guid>https://showme.codes/zh-cn/2024-03-12-frontend-engineering/</guid>
        <description>翟志军 Jack Zhai https://showme.codes/zh-cn/2024-03-12-frontend-engineering/ -&lt;blockquote&gt;
&lt;p&gt;本文需要读者懂一点点前端的构建知识：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;package.json文件的作用之一是管理外部依赖；&lt;/li&gt;
&lt;li&gt;.npmrc是npm命令默认配置，放在工程根目录。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;Web前端构建一直都是一个不难，但是非常烦人的问题，在DevOps、CI/CD领域。&lt;/p&gt;
&lt;p&gt;烦人的是偶尔发生这样的事情：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;开发在本地构建通过，但是流水构建失败。这时前端开发人员会经常报怨Pipeline不稳定；&lt;/li&gt;
&lt;li&gt;流水线构建通过，但是在生产环境上启动不了，或者出现运行错误；&lt;/li&gt;
&lt;li&gt;不使用Docker可以启动，但是打包成Docker镜像后启动就失败。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这类问题，不是今天解决了，明天就不会发生。而是你根本不知道它什么时候又发生。&lt;/p&gt;
&lt;p&gt;据我观察，绝大多数时候都是依赖版本管理没有做好导致的。&lt;/p&gt;
&lt;p&gt;Web前端的依赖版本管理包括以下几个维度：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;node的版本&lt;/li&gt;
&lt;li&gt;外部依赖的版本&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;我们需要在开发环境，构建环境，运行环境保证它们的版本是一致的。这样，在本地开发环境测试通过，那么，在其它环境就理论上也应该能通过。&lt;/p&gt;
&lt;p&gt;接下来是具体的最佳实践。&lt;/p&gt;
&lt;h2 id=&#34;保证node版本一致&#34;&gt;保证Node版本一致&lt;/h2&gt;
&lt;p&gt;要保证Node版本一致，就要保证所有的环境使用同一个版本的node。而且是要具体到某一个精确的版本，如v20.11.1，而不是20这样一个粗略版本。&lt;/p&gt;
&lt;p&gt;以下是我们以v20.11.1为例。&lt;/p&gt;
&lt;h3 id=&#34;设置开发环境&#34;&gt;设置开发环境&lt;/h3&gt;
&lt;p&gt;设置开发环境的node的版本，需要在&lt;code&gt;package.json&lt;/code&gt;中加入：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#eed;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&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:#8b008b;font-weight:bold&#34;&gt;&amp;#34;engines&amp;#34;&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:#8b008b;font-weight:bold&#34;&gt;&amp;#34;node&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#34;v20.11.1&amp;#34;&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:#8b008b;font-weight:bold&#34;&gt;&amp;#34;npm&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#34;10.2.4&amp;#34;&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这时，如果存在开发环境与配置的版本不匹配的情况，执行&lt;code&gt;npm install&lt;/code&gt;，会出现以下警告，但是命令还是会继续执行：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#eed;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;npm WARN EBADENGINE Unsupported engine {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;npm WARN EBADENGINE   package: &lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#39;gpt@0.0.1&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;npm WARN EBADENGINE   required: { node: &lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#39;v20.10.1&amp;#39;&lt;/span&gt;, npm: &lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#39;10.2.4&amp;#39;&lt;/span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;npm WARN EBADENGINE   current: { node: &lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#39;v20.11.1&amp;#39;&lt;/span&gt;, npm: &lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#39;10.2.4&amp;#39;&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;npm WARN EBADENGINE }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;希望强制要求版本一致，就在根目录的&lt;code&gt;.npmrc&lt;/code&gt;文件加入：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;engine-strict=true
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;发生版本不一致的情况，报错日志如下，且命令会停止执行：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;npm ERR! code EBADENGINE
npm ERR! engine Unsupported engine
npm ERR! engine Not compatible with your version of node/npm: gpt@0.0.1
npm ERR! notsup Not compatible with your version of node/npm: gpt@0.0.1
npm ERR! notsup Required: {&amp;#34;node&amp;#34;:&amp;#34;v20.10.1&amp;#34;,&amp;#34;npm&amp;#34;:&amp;#34;10.2.4&amp;#34;}
npm ERR! notsup Actual:   {&amp;#34;npm&amp;#34;:&amp;#34;10.2.4&amp;#34;,&amp;#34;node&amp;#34;:&amp;#34;v16.0.1&amp;#34;}
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;设置构建环境&#34;&gt;设置构建环境&lt;/h3&gt;
&lt;p&gt;我们以Github Actions为例。在设置node环境时，应设置为：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#eed;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- &lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;name&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;Setup Node&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;uses&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;actions/setup-node@v3&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;with&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;node-version&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#39;20.11.1&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;设置运行环境&#34;&gt;设置运行环境&lt;/h3&gt;
&lt;p&gt;运行环境分两种：虚拟机环境、容器运行时。&lt;/p&gt;
&lt;p&gt;在虚拟机环境下，要避免&lt;code&gt;apt install node-20&lt;/code&gt;，尽量使用能指定精确node版本的方式安装NodeJS（比如从官网上下载20.11.1的包安装）。&lt;/p&gt;
&lt;p&gt;容器运行时环境，选择的镜像的Tag要与构建环境的版本完全一致，而不是随便选一个20版本的。&lt;/p&gt;
&lt;h2 id=&#34;保证外部依赖版本的一致&#34;&gt;保证外部依赖版本的一致&lt;/h2&gt;
&lt;p&gt;由于Node的依赖管理默认配置下非常的宽松，默认情况下使用的就是自动升级策略。&lt;/p&gt;
&lt;p&gt;当开发在本地执行: &lt;code&gt;npm i @babel/core&lt;/code&gt;，npm会在&lt;code&gt;package.json&lt;/code&gt;文件中加入&lt;code&gt;&amp;quot;@babel/core&amp;quot;: &amp;quot;^7.11.6&amp;quot;,&lt;/code&gt;。&lt;code&gt;^&lt;/code&gt;代表将来再次执行&lt;code&gt;npm i&lt;/code&gt;时，npm有权自动升级它的小版本。&lt;/p&gt;
&lt;p&gt;这一行为，导致项目一开始构建是成功的，但是过一段时间又构建失败的偶尔事件。&lt;/p&gt;
&lt;p&gt;这种偶发性，不仅给构建工程带来不必要的浪费，还让软件变得不可靠。想想建设在沙子上的大厦会是怎样。&lt;/p&gt;
&lt;p&gt;所以，我们推崇以下管理方法。&lt;/p&gt;
&lt;h3 id=&#34;限制依赖下载源&#34;&gt;限制依赖下载源&lt;/h3&gt;
&lt;p&gt;限制的方法是在&lt;code&gt;.npmrc&lt;/code&gt;中加入配置：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;registry=https://registry.npmjs.org
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;从源头就控制软件供应链的一致性。&lt;/p&gt;
&lt;h3 id=&#34;默认使用准确版本&#34;&gt;默认使用准确版本&lt;/h3&gt;
&lt;p&gt;正如前文所述，在执行 &lt;code&gt;npm install &amp;lt;package&amp;gt;&lt;/code&gt; 安装依赖时，默认情况下会在package.json文件中使用&lt;code&gt;^&lt;/code&gt;符号来指定版本范围。不过，我们可以通过添加 &lt;code&gt;--save-exact&lt;/code&gt; 参数来避免这种情况，即运行 &lt;code&gt;npm install &amp;lt;package&amp;gt; --save-exact&lt;/code&gt;，这样package.json文件中就不会出现&lt;code&gt;^&lt;/code&gt;符号，而是会锁定安装的精确版本号。&lt;/p&gt;
&lt;p&gt;我们不可能让开发人员100%做到每次执行命令都加&lt;code&gt;--save-exact&lt;/code&gt;参数。&lt;/p&gt;
&lt;p&gt;所以，我们需要更改npm默认的行为，在&lt;code&gt;.npmrc&lt;/code&gt;文件中增加配置：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;save-exact=true
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;将package-lockjson加入到版本库中&#34;&gt;将package-lock.json加入到版本库中&lt;/h3&gt;
&lt;p&gt;package-lock.json文件是npm专门用于固定依赖版本的。如果你使用的是pnpm，相对应的文件就是：&lt;code&gt;pnpm-lock.yaml&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;node工程中，除了使用package-lock.json锁定版本，还可以使用npm-shrinkwrap.json。&lt;/p&gt;
&lt;p&gt;它们具有相同格式，都放在项目的根目录，目的都是为了锁定依赖版本。区别是npm-shrinkwrap.json会被发布到制品库，而package-lock.json不会。且引用它的package会忽略这个文件。&lt;/p&gt;
&lt;p&gt;而且当同一个工程根目录下，同时存在它们时，package-lock.json会被忽略。&lt;/p&gt;
&lt;h3 id=&#34;使用pnpm代替npm&#34;&gt;使用PNPM代替npm？&lt;/h3&gt;
&lt;p&gt;这篇&lt;a href=&#34;https://hackernoon.com/choosing-the-right-package-manager-npm-yarn-or-pnpm&#34;&gt;文章&lt;/a&gt;对PNPM，npm和Yarn三个依赖管理工具进行对比，读者自行判断选择相应的工具。但是，可以确定的是不要使用cnpm。&lt;/p&gt;
&lt;p&gt;不论使用哪种工具，以上的实践都是类似的。&lt;/p&gt;
&lt;h2 id=&#34;后记&#34;&gt;后记&lt;/h2&gt;
&lt;p&gt;作者作为一个Web前端的外行写下本文，有不足的或者错误的，还请补充和指正，多谢。&lt;/p&gt;
- https://showme.codes/zh-cn/2024-03-12-frontend-engineering/ - showme.codes</description>
        </item>
    
    
    
        <item>
        <title>优秀的DevOps工程师应该具有什么特质？</title>
        <link>https://showme.codes/zh-cn/2024-03-12-devops-characters/</link>
        <pubDate>Tue, 12 Mar 2024 00:00:00 +0000</pubDate>
        
        <guid>https://showme.codes/zh-cn/2024-03-12-devops-characters/</guid>
        <description>翟志军 Jack Zhai https://showme.codes/zh-cn/2024-03-12-devops-characters/ -&lt;p&gt;这是我最近面试时遇到的一个非常好的问题：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;在你的心目中，优秀的DevOps工程师应该是什么样的？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;很长一段时间里，我没有想到这个问题。所以，当HR问起时，我边思考边回答。&lt;/p&gt;
&lt;p&gt;我已经不记得原话，本文就当作从性格角度思考“优秀的DevOps工程师应该具有什么特质”。&lt;/p&gt;
&lt;p&gt;首先，优秀的DevOps工程师，TA应该是&lt;strong&gt;严谨的&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;一个严谨的特质的人才会主动考虑软件工程化过程中的各种可能。&lt;/p&gt;
&lt;p&gt;其次，TA应该对手工操作产生“生理性上的不适”，即&lt;strong&gt;追求自动化极致&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;一个再怎么严谨的人，如果是手工操作，面对复杂的线上环境的运维，TA的能力也是有限的。TA必须自动化所有能自动化的东西，并争取自动化所有的内容，TA才能有可能“驯服野兽”。&lt;/p&gt;
&lt;p&gt;然后，TA应该是一个节约的人，即&lt;strong&gt;节俭&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;因为浪费导致企业不必要损失，是否要避免这个损失，很多时间里是一个DevOps工程师的选择问题。&lt;/p&gt;
&lt;p&gt;最后，TA应该是一个&lt;strong&gt;热爱&lt;/strong&gt;这个领域的人。我常常忘记自己对这个领域的热爱。以致于我忘记回答。&lt;/p&gt;
&lt;p&gt;只有热爱，才能产生自驱力，驱动TA去成长，去不断思考。即使你再严谨，你也有考虑不到的地方，只有热爱，TA才会探索到TA不知道他不知道的。&lt;/p&gt;
&lt;p&gt;以上只是我个人的看法，欢迎一起讨论。&lt;/p&gt;
- https://showme.codes/zh-cn/2024-03-12-devops-characters/ - showme.codes</description>
        </item>
    
    
    
        <item>
        <title>Two Patterns for Rollback</title>
        <link>https://showme.codes/en/2024-03-06-two-patterns-for-rollback/</link>
        <pubDate>Wed, 06 Mar 2024 00:00:00 +0000</pubDate>
        
        <guid>https://showme.codes/en/2024-03-06-two-patterns-for-rollback/</guid>
        <description>翟志军 Jack Zhai https://showme.codes/en/2024-03-06-two-patterns-for-rollback/ -&lt;p&gt;Rollback is an operations and maintenance procedure. It usually occurs when a problem is discovered during deployment, and the target environment needs to be reverted to its pre-deployment state.&lt;/p&gt;
&lt;p&gt;In my opinion, there are two patterns for rollback. One of them is to perform a reverse operation step by step, which I call the &lt;strong&gt;Reverse Operation Pattern&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&#34;rollback-pattern-based-on-reverse-operation&#34;&gt;Rollback Pattern Based on Reverse Operation&lt;/h2&gt;
&lt;p&gt;Probably due to the inertia of the past manual operation mindset, I found that quite a few people only know this one pattern.&lt;/p&gt;
&lt;p&gt;For example, the operation of manually deploying an Nginx configuration is as follows:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;SSH into the target server&lt;/li&gt;
&lt;li&gt;Navigate to the &lt;code&gt;/etc/nginx/sites-enabled/&lt;/code&gt; directory where Nginx is stored&lt;/li&gt;
&lt;li&gt;Edit the target configuration file &lt;code&gt;vim example.443.conf&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Add a location configuration&lt;/li&gt;
&lt;li&gt;Reload Nginx to apply the configuration changes&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In the rollback scenario using the reverse-operation pattern, how should we roll back? Steps 1, 2, 3, and 5 are the same as in deployment. Step 4 requires the operator to find the configuration and remove it.&lt;/p&gt;
&lt;p&gt;At this point, the operator may make a mistake in the operation, and it may be difficult to perceive when the mistake is made because it is a manual operation.&lt;/p&gt;
&lt;p&gt;Then, some people think it would be better if the above steps could be automatically rolled back.&lt;/p&gt;
&lt;p&gt;But how can we automate the rollback? We need to design the corresponding automated rollback script at the time of deployment. When needed, trigger its automatic rollback.&lt;/p&gt;
&lt;p&gt;However, this rollback scheme is not generalizable, and it increases the cost of operations and maintenance. Because the average person maintaining an Nginx configuration change is very reluctant to write a rollback script, and they themselves may not be able to write a correct, reliable automated rollback script.&lt;/p&gt;
&lt;p&gt;Then, someone might think, &amp;ldquo;Can I implement a platform to automatically generate the rollback code?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;The answer is yes. You have to pre-define each step action, such as defining the Nginx configuration change as an action in the platform. Then define its inverse action.&lt;/p&gt;
&lt;p&gt;If you are implementing a similar platform project, you will know that the amount of work is endless because the operations are endless.&lt;/p&gt;
&lt;p&gt;You can say that the platform can provide the ability to customize the step action, but then you will also encounter the problem of &amp;ldquo;they themselves may not be able to write a correct and reliable automation rollback script.&amp;rdquo; And, since it&amp;rsquo;s a platform, it&amp;rsquo;s the platform&amp;rsquo;s responsibility to define the actions.&lt;/p&gt;
&lt;p&gt;So, this is an industry dilemma.&lt;/p&gt;
&lt;p&gt;During the interview process, the interviewer usually assumes that I will encounter the same dilemma. However, I won&amp;rsquo;t encounter this problem at all.&lt;/p&gt;
&lt;h2 id=&#34;version-based-rollback-pattern&#34;&gt;Version-based Rollback Pattern&lt;/h2&gt;
&lt;p&gt;When I was explaining this pattern, many people couldn&amp;rsquo;t understand it.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s take the case of deploying an Nginx configuration, but automate the deployment through Ansible. Assume that the following deployment script already exists:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#eed;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- &lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;hosts&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;prod-nginx&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;gather_facts&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;yes&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;become&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;true&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;vars_files&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;    &lt;/span&gt;- common_vars/nginx.yaml&lt;span style=&#34;color:#bbb&#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:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;roles&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#228b22&#34;&gt;# Nginx deployment logic, which is declarative and idempotent&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;    &lt;/span&gt;- ansible-role-nginx&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The above code means roughly: deploy Nginx to a list of prod-nginx hosts and use the configuration in the common_vars/nginx.yaml file.&lt;/p&gt;
&lt;p&gt;The configuration of nginx.yaml is as follows:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#eed;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;nginx_vhosts&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;- &lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;listen&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#34;80&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;server_name&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#34;*.example.com&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;return&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#34;301 https://{{example_domain}}$request_uri&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;filename&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#34;example.80.conf&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We version the above code (commit it to Git and build it via automation) to get version number: v1.0.1.&lt;/p&gt;
&lt;p&gt;When there&amp;rsquo;s a need to modify the configuration on the line, such as in the case of &amp;ldquo;Rollback Mode for Reverse Operations,&amp;rdquo; we accomplish this by appending the appropriate configuration to the nginx.yaml file. It will resemble the following structure:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#eed;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;nginx_vhosts&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;&lt;/span&gt;- &lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;listen&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#34;80&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;server_name&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#34;*.example.com&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;return&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#34;301 https://{{example_domain}}$request_uri&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;filename&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#34;example.80.conf&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;&lt;/span&gt;- &lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;listen&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#34;80&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;server_name&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#34;*.abc.com&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;return&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#34;301 https://{{example_domain}}$request_uri&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;filename&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#34;abc.80.conf&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Subsequently, the code is integrated into the codebase, resulting in the version number: v1.1.0.&lt;/p&gt;
&lt;p&gt;During deployment, the v1.1.0 code is simply deployed.&lt;/p&gt;
&lt;p&gt;With the version-based rollback mode, reverting is straightforward. It involves executing the code from v1.0.1 (the preceding version of v1.1.0) again.&lt;/p&gt;
&lt;p&gt;Creating a platform based on this pattern is equally straightforward. The platform isn&amp;rsquo;t concerned with the specifics of the operation; its primary objective is to select the last correct version of the code for execution, thus facilitating the rollback process without encountering various issues associated with &amp;ldquo;reverse operation based on rollback mode&amp;rdquo; implementations.&lt;/p&gt;
&lt;p&gt;This simplicity also facilitates the standardization of the platform&amp;rsquo;s deployment process.&lt;/p&gt;
&lt;p&gt;However, it&amp;rsquo;s essential to note that the version-based rollback mode necessitates idempotent, declarative deployment code execution. Idempotent implies that running the same code multiple times yields the same outcome.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;The version-based rollback model essentially constitutes deployment, utilizing an earlier version of deployment in lieu of traditional &amp;ldquo;rollback&amp;rdquo; procedures.&lt;/p&gt;
&lt;p&gt;In conclusion, I trust this article has sparked some fresh perspectives.&lt;/p&gt;
- https://showme.codes/en/2024-03-06-two-patterns-for-rollback/ - showme.codes</description>
        </item>
    
    
    
        <item>
        <title>回滚的两种模式</title>
        <link>https://showme.codes/zh-cn/2024-03-05-rollback-pattern/</link>
        <pubDate>Tue, 05 Mar 2024 00:00:00 +0000</pubDate>
        
        <guid>https://showme.codes/zh-cn/2024-03-05-rollback-pattern/</guid>
        <description>翟志军 Jack Zhai https://showme.codes/zh-cn/2024-03-05-rollback-pattern/ -&lt;p&gt;回滚是一种运维操作。通常发生在部署过程中发现问题，需要将目标环境恢复到部署前的状态。&lt;/p&gt;
&lt;p&gt;在我看来，回滚有两种模式。其中一种是一步步执行反向操作，我称之为&lt;strong&gt;反向操作模式&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id=&#34;基于反向操作的回滚模式&#34;&gt;基于反向操作的回滚模式&lt;/h2&gt;
&lt;p&gt;可能是由于过去手工运维的思维方式的惯性，我发现不少人只知道这一种模式。&lt;/p&gt;
&lt;p&gt;比如使用手工部署Nginx的配置的操作如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;SSH登录到目标服务器&lt;/li&gt;
&lt;li&gt;进入到存放Nginx的&lt;code&gt;/etc/nginx/sites-enabled/&lt;/code&gt;目录&lt;/li&gt;
&lt;li&gt;编辑目标配置文件&lt;code&gt;vim example.443.conf&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;增加一个location配置&lt;/li&gt;
&lt;li&gt;reload nginx使配置生效&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;在反向操作模式的回滚方案中，我们应该该如何回滚呢？1，2，3，5步骤与部署时一致。第4步，需要操作人员找到该配置，并删除。&lt;/p&gt;
&lt;p&gt;这时，操作人员在操作时就可能会出错，而且出错了，你可能很难觉察到。因为他是手工操作的。&lt;/p&gt;
&lt;p&gt;那么，有人就想，以上步骤能自动回滚就好了。&lt;/p&gt;
&lt;p&gt;可是，该如何自动回滚呢？我们需要在部署时就设计好相应的自动化回滚脚本。当需要时，就触发其自动回滚。&lt;/p&gt;
&lt;p&gt;然而这个回滚方案的方案是无法通用的，而且增加了运维成本。因为普通的运维人员对于一个Nginx的配置的变更，是非常不愿意写回滚的脚本的，而且，他本人也不一定能写出正确的、可靠的自动化回滚脚本。&lt;/p&gt;
&lt;p&gt;那么，有人就会想了，我能否实现自动生成回滚代码的平台？&lt;/p&gt;
&lt;p&gt;答案是可以的。你必须预先定义每一个步骤动作，比如在平台上将Nginx配置的修改作为一个动作定义。然后再定义它的反操作。&lt;/p&gt;
&lt;p&gt;如果你是实现过类似平台项目，你会知道，这工作量是无穷无尽的。因为运维的操作是无穷无尽的。&lt;/p&gt;
&lt;p&gt;你可以说平台可以提供自定义步骤动作的能力，那么你同样会遇到“他本人也不一定能写出正确的、可靠的自动化回滚脚本”的问题。而且，既然是平台了，定义操作的责任就应该是平台的责任。&lt;/p&gt;
&lt;p&gt;所以，这是一个业界的难题。&lt;/p&gt;
&lt;p&gt;在面试过程中，面试官通常假设我也会遇到同样的难题。然而，我根本不会遇到这个问题。&lt;/p&gt;
&lt;h2 id=&#34;基于版本的回滚模式&#34;&gt;基于版本的回滚模式&lt;/h2&gt;
&lt;p&gt;我在解释这个模式时，很多人无法理解。&lt;/p&gt;
&lt;p&gt;还是以部署Nginx配置为案例。但是通过Ansible来实现自动化部署。假设已经存在以下部署脚本：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#eed;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- &lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;hosts&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;prod-nginx&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;gather_facts&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;yes&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;become&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;true&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;vars_files&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;		&lt;/span&gt;&lt;span style=&#34;color:#228b22&#34;&gt;# Nginx的配置&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;    &lt;/span&gt;- common_vars/nginx.yaml&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;roles&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;	 &lt;/span&gt;&lt;span style=&#34;color:#228b22&#34;&gt;# Nginx的部署逻辑，是声明式的、幂等的。&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;    &lt;/span&gt;- ansible-role-nginx&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;以上代码含义大概是：部署Nginx到prod-nginx主机列表上，并使用common_vars/nginx.yaml文件中的配置。&lt;/p&gt;
&lt;p&gt;nginx.yaml的配置如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#eed;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;nginx_vhosts&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;&lt;/span&gt;- &lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;listen&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#34;80&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;server_name&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#34;*.example.com&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;return&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#34;301 https://{{example_domain}}$request_uri&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;filename&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#34;example.80.conf&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我们对以上代码版本化（提前到Git中，并通过自动化构建），得到版本号：v1.0.1。&lt;/p&gt;
&lt;p&gt;现在我们需要像“反向操作的回滚模式”中的案例那样修改线上的配置时，我们的做法是在nginx.yaml配置增加相应的配置，最终效果如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#eed;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;nginx_vhosts&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;&lt;/span&gt;- &lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;listen&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#34;80&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;server_name&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#34;*.example.com&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;return&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#34;301 https://{{example_domain}}$request_uri&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;filename&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#34;example.80.conf&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;&lt;/span&gt;- &lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;listen&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#34;80&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;server_name&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#34;*.abc.com&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;return&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#34;301 https://{{example_domain}}$request_uri&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;filename&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#34;abc.80.conf&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后将代码push到代码库中，经过构建，我们得到版本号：v1.1.0。&lt;/p&gt;
&lt;p&gt;部署时，使用v1.1.0的代码部署即可。&lt;/p&gt;
&lt;p&gt;基于版本的回滚模式，回滚就很简单了。就是再执行一次v1.0.1版本（v1.1.0的上一个版本）的代码就可以了。&lt;/p&gt;
&lt;p&gt;基于这种模式实现平台化，也非常简单。平台不需要关心具体运行的内容，就要选择上一个正确的版本的代码执行，就完成了回滚。不会遇到“基于反向操作的回滚模式”的实现过程遇到的各种问题。&lt;/p&gt;
&lt;p&gt;也因为这种简单化，平台实现部署的标准化，也非常简单。&lt;/p&gt;
&lt;p&gt;但是，基于版本的回滚模式是有前提的，你的部署代码的执行必须是幂等的、声明式的。幂等的，指的是同一份代码，运行多次，得到的结果是一样的。&lt;/p&gt;
&lt;h2 id=&#34;小结&#34;&gt;小结&lt;/h2&gt;
&lt;p&gt;基于版本的回滚模式准确来说，也是部署。也就是它使用老版本部署代替传统的“回滚”。&lt;/p&gt;
&lt;p&gt;最后，希望这篇文章能给读者新的启发。&lt;/p&gt;
- https://showme.codes/zh-cn/2024-03-05-rollback-pattern/ - showme.codes</description>
        </item>
    
    
    
        <item>
        <title>Jenkins kubernetes插件的原理</title>
        <link>https://showme.codes/zh-cn/2024-02-26-jenkins-kubernetes-plugin/</link>
        <pubDate>Mon, 26 Feb 2024 00:00:00 +0000</pubDate>
        
        <guid>https://showme.codes/zh-cn/2024-02-26-jenkins-kubernetes-plugin/</guid>
        <description>翟志军 Jack Zhai https://showme.codes/zh-cn/2024-02-26-jenkins-kubernetes-plugin/ -&lt;h2 id=&#34;如何使用&#34;&gt;如何使用&lt;/h2&gt;
&lt;p&gt;使用Kubernetes插件时，我们需要做三件事情：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;根据官方文档，在Jenkins上加入kubernetes配置。&lt;/li&gt;
&lt;li&gt;在Jenkinsfile中加入kubernetes agent的申明。&lt;/li&gt;
&lt;li&gt;指定容器执行你的业务脚本。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;关于第2点，kubernetes agent的申明又有两种方式。一种是脚本式的，代码样例如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#eed;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-groovy&#34; data-lang=&#34;groovy&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;podTemplate(containers: [&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;…&lt;/span&gt;]) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  node(POD_LABEL) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    stage(&lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#39;Run shell&amp;#39;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      container(&lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#39;mycontainer&amp;#39;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sh &lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#39;echo hello world&amp;#39;&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;&lt;p&gt;一种是申明式，代码样例如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#eed;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-groovy&#34; data-lang=&#34;groovy&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;pipeline {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  stages {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    stage(&lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#39;Run maven&amp;#39;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      agent {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        kubernetes {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              yaml &lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#34;&amp;#34;&amp;#34;
&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:#cd5555&#34;&gt;                apiVersion: v1
&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:#cd5555&#34;&gt;                kind: Pod
&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:#cd5555&#34;&gt;                metadata:
&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:#cd5555&#34;&gt;                  labels:
&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:#cd5555&#34;&gt;                    app: jenkins-agent
&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:#cd5555&#34;&gt;                spec:
&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:#cd5555&#34;&gt;                  containers:
&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:#cd5555&#34;&gt;                  - name: maven
&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:#cd5555&#34;&gt;                    image: maven:alpine
&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:#cd5555&#34;&gt;                    command:
&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:#cd5555&#34;&gt;                    - cat
&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:#cd5555&#34;&gt;                    tty: true
&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:#cd5555&#34;&gt;                  - name: busybox
&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:#cd5555&#34;&gt;                    image: busybox
&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:#cd5555&#34;&gt;                    command:
&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:#cd5555&#34;&gt;                    - cat
&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:#cd5555&#34;&gt;                    tty: true
&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:#cd5555&#34;&gt;                &amp;#34;&amp;#34;&amp;#34;&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;      steps {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        container(&lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#39;maven&amp;#39;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          sh &lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#39;mvn -version&amp;#39;&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;&lt;p&gt;笔者推荐使用申明式。yaml配置部分看起来并不优雅，这是另一个话题。咱们今后再讲。&lt;/p&gt;
&lt;h2 id=&#34;原理&#34;&gt;原理&lt;/h2&gt;
&lt;p&gt;我们都知道Jenkins是master/agent的架构。而master与agent之间通信方法有两种：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;通过JNLP协议：需要启动JNLP客户端主动连接master。这是Kubernetes插件使用的方式。&lt;/li&gt;
&lt;li&gt;通过SSH协议：master使用SSH主动连接agent机器。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Kubernetes插件的具体的做法就是连接到Kubernetes集群，然后启动一个Pod。Pod中包含一个JNLP客户端，容器名约定为：jnlp。jnlp 会主动连接Jenkins master。&lt;/p&gt;
&lt;p&gt;所以，当你发现Jenkins任务的日志中，一直在等待jnlp连接时，我们可以这样查问题：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;查看相应的Pod是否存活。&lt;/li&gt;
&lt;li&gt;jnlp 容器连接不上master：大概率是配置不对。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;可是，我们看到上面的示例代码中，都没有叫jnlp的容器呢。这是因为Jenkins kubernates插件在真正创建pod前，为我们混入了默认的jnlp的容器定义。也就是，最终执行的yaml其实是：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#eed;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;apiVersion&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;v1&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;kind&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;Pod&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;metadata&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;labels&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;some-label&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;some-label-value&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;spec&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;containers&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;- &lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;name&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;jnlp&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;image&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;jenkins/jnlp-slave:alpine&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;args&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;[&lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#39;\$(JENKINS_SECRET)&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#39;\$(JENKINS_NAME)&amp;#39;&lt;/span&gt;]&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;- &lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;name&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;maven&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;image&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;maven:alpine&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;command&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;    &lt;/span&gt;- cat&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;tty&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;true&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#008b45;text-decoration:underline&#34;&gt;...&lt;/span&gt;省略其它&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;最后，pod启动后，pod中的jnlp容器会连上Jenkins master。当pipeline运行到以下代码：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;container(&amp;#39;maven&amp;#39;) {
 sh &amp;#39;mvn -version&amp;#39;
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;kubernates插件会找到名为&lt;code&gt;maven&lt;/code&gt;的容器，然后将闭包内的代码发给它执行。&lt;/p&gt;
&lt;p&gt;以上基本就是kubernates插件全部。&lt;/p&gt;
&lt;h2 id=&#34;更换jnlp实现&#34;&gt;更换jnlp实现&lt;/h2&gt;
&lt;p&gt;当我们知道它的原理后，我们也就可以更换jnlp的实现镜像了。比如有些同学是在arm架构的机器上执行Kubernetes的，那么，他可以创建一个基于arm架构的jnlp镜像，然后，加入到yaml中。比如：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#eed;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;containers&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;- &lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;name&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;jnlp&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;image&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;supercom.com/jnlp-arm-agent:1.0&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;    &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;args&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;[&lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#39;\$(JENKINS_SECRET)&amp;#39;&lt;/span&gt;,&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#39;\$(JENKINS_NAME)&amp;#39;&lt;/span&gt;]&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;小结&#34;&gt;小结&lt;/h2&gt;
&lt;p&gt;总的来说，它的原理无非就是创建pod，pod中的jnlp容器连接到Jenkins master，然后Jenkins master根据需要，将需要执行的命令发送给相应的容器执行。&lt;/p&gt;
&lt;h2 id=&#34;附录&#34;&gt;附录&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Jenkins kubernetes源码：&lt;a href=&#34;https://github.com/jenkinsci/kubernetes-plugin&#34;&gt;https://github.com/jenkinsci/kubernetes-plugin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;混入jnlp容器的代码位置：&lt;code&gt;org.csanchez.jenkins.plugins.kubernetes.PodTemplateBuilder#build()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;创建pod的代码位置：&lt;code&gt;org.csanchez.jenkins.plugins.kubernetes.KubernetesLauncher#launch&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
- https://showme.codes/zh-cn/2024-02-26-jenkins-kubernetes-plugin/ - showme.codes</description>
        </item>
    
    
    
        <item>
        <title>K8s工程化：K8s中的Java应用出现OOM后怎么办？</title>
        <link>https://showme.codes/zh-cn/2024-2-26-jvm-oom-kubernetes/</link>
        <pubDate>Mon, 26 Feb 2024 00:00:00 +0000</pubDate>
        
        <guid>https://showme.codes/zh-cn/2024-2-26-jvm-oom-kubernetes/</guid>
        <description>翟志军 Jack Zhai https://showme.codes/zh-cn/2024-2-26-jvm-oom-kubernetes/ -&lt;blockquote&gt;
&lt;p&gt;完整代码在文末&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;背景&#34;&gt;背景&lt;/h3&gt;
&lt;p&gt;前段时间，线上系统出现了两次持续时间比较长的事故。这两次事故暴露我在某些方面的不足。同时，也意识到在SRE这个领域，经验的重要性。&lt;/p&gt;
&lt;p&gt;事故过程中，我们发现大量的FullGC。当时，我们想到了要dump内存出来分析，可惜发现没有加&lt;code&gt;-XX:HeapDumpPath&lt;/code&gt;参数。同时，我们也发现，如果dump出来了，我们也没法拿到dump出来的文件。因为我们的应用是跑在K8s中的。&lt;/p&gt;
&lt;h3 id=&#34;方案调研&#34;&gt;方案调研&lt;/h3&gt;
&lt;p&gt;经复盘，我们得到一个action：在Java应用出现OOM时，将内存dump出来，并持久化，并且方便分析。&lt;/p&gt;
&lt;p&gt;这个action可以细分为两个任务：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;OOM时，dump内存出来；&lt;/li&gt;
&lt;li&gt;提供一种途径方便分析。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;经过权衡，任务2的优先级是可以降低的。puvad只要把任务1做好就可以。所以，这两个任务最终变成：在Java应用出现OOM时，将内存dump到NAS中。&lt;/p&gt;
&lt;p&gt;笔者在网上搜索一通，看到的方案基本就是启动一个sidecar容器，与应用共享一个目录。然后监控这个目录，发现内容就上传到s3这类对象存储中。&lt;/p&gt;
&lt;p&gt;这种方案的问题在于：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;sidecar在传输过程，有出现问题的风险；&lt;/li&gt;
&lt;li&gt;为了OOM这个小概率事件启动一个sidecar，资源有点浪费。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;个人觉得，Java应用的Pod应该只负责将OOM时的内存dump到NAS即可，其它事情应该由其它Pod完成。&lt;/p&gt;
&lt;h3 id=&#34;具体实现&#34;&gt;具体实现&lt;/h3&gt;
&lt;p&gt;以下方案是基于Helm自动化部署。如果你使用的是其它自动化部署工具，思路大体相同。&lt;/p&gt;
&lt;h4 id=&#34;准备nfs服务&#34;&gt;准备NFS服务&lt;/h4&gt;
&lt;p&gt;这部分不是本文范畴。&lt;/p&gt;
&lt;h4 id=&#34;java应用启动时参数配置&#34;&gt;Java应用启动时参数配置&lt;/h4&gt;
&lt;p&gt;在Dockerfile中必须将变量$JAVA_OPTS加入到启动参数中。&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;FROM openjdk:11.0.12-jre-buster
COPY target/app.jar /app.jar
CMD java -jar $JAVA_OPTS /app.jar
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&#34;加入initcontainers&#34;&gt;加入InitContainers&lt;/h4&gt;
&lt;p&gt;作用：创建符合指定规则的Dump目录（注意DUMP_FOLDER变量的定义）。如下代码，在init容器启动后，它会创建目录：/nfs/dump/default/jvm-oom-example/10.233.66.38 。在应用出现OOM，内存文件会被dump在此目录下。&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;initContainers:
- name: init
  image: registry.cn-shenzhen.aliyuncs.com/aliacs-app-catalog/busybox:1.30.1
  command: [&amp;#39;sh&amp;#39;, &amp;#39;-c&amp;#39;, &amp;#39;echo $DUMP_FOLDER;mkdir -p $DUMP_FOLDER&amp;#39;]
  {{- with .Values.volumeMounts }}
  volumeMounts:
  {{- toYaml . | nindent 12 }}
  {{- end }}
  env:
  - name: MY_NODE_NAME
      valueFrom:
      fieldRef:
          fieldPath: spec.nodeName
  - name: MY_POD_NAME
      valueFrom:
      fieldRef:
          fieldPath: metadata.name
  - name: MY_POD_NAMESPACE
      valueFrom:
      fieldRef:
          fieldPath: metadata.namespace
  - name: MY_POD_IP
      valueFrom:
      fieldRef:
          fieldPath: status.podIP
  - name: DUMP_FOLDER
      value: &amp;#34;/nfs/dump/$(MY_POD_NAMESPACE)/{{ include &amp;#34;app.fullname&amp;#34; . }}/$(MY_POD_IP)&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&#34;配置应用容器&#34;&gt;配置应用容器&lt;/h4&gt;
&lt;p&gt;我们要做的，其实就是设置JAVA_OPTS环境变量。这里要注意的是JAVA_OPTS可以由三部分组成的：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;内存大小设置，比如：-Xmx640M Xms640M 这类；&lt;/li&gt;
&lt;li&gt;GC算法设置，比如：-XX:+UseSerialGC&lt;/li&gt;
&lt;li&gt;JVM日志设置，比如：-XX:ErrorFile=/dump/hs_err_pid%p.log -XX:HeapDumpPath=/dump。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;1,2部分应该是由用户决定。第3部分是由平台决定的。&lt;/p&gt;
&lt;p&gt;所以，我们的配置JAVA_OPTS分成两部分：DUMP_ARGS 和 用户的JVM配置。代码如下：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;containers:
- name: {{ .Chart.Name }}
  {{- with .Values.volumeMounts }}
  volumeMounts:
    {{- toYaml . | nindent 12 }}
  {{- end }}
  env:
    - name: MY_NODE_NAME
      valueFrom:
        fieldRef:
          fieldPath: spec.nodeName
    - name: MY_POD_NAME
      valueFrom:
        fieldRef:
          fieldPath: metadata.name
    - name: MY_POD_NAMESPACE
      valueFrom:
        fieldRef:
          fieldPath: metadata.namespace
    - name: MY_POD_IP
      valueFrom:
        fieldRef:
          fieldPath: status.podIP
    - name: DUMP_FOLDER
      value: &amp;#34;/nfs/dump/$(MY_POD_NAMESPACE)/{{ include &amp;#34;app.fullname&amp;#34; . }}/$(MY_POD_IP)&amp;#34;
    - name: DUMP_ARGS
      value: &amp;#34;-XX:ErrorFile=$(DUMP_FOLDER)/hs_err_pid.log  -XX:HeapDumpPath=$(DUMP_FOLDER) -XX:+HeapDumpOnOutOfMemoryError&amp;#34;
    - name: JAVA_OPTS
      value: &amp;#34;{{.Values.javaOpts}} $(DUMP_ARGS)&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;最终效果&#34;&gt;最终效果&lt;/h3&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[vagrant@k8s-3 jvm-oom]$ pwd
/persistentvolumes/dump/default/jvm-oom
[vagrant@k8s-3 jvm-oom]$ tree
.
├── 10.233.66.37
└── 10.233.66.38
    └── java_pid6.hprof
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;小结&#34;&gt;小结&lt;/h3&gt;
&lt;p&gt;这个方案并不是没有缺点。比如每次Pod启动都会创建一个目录，不论是否出现OOM。当然，这个缺点的解决方案也很简单，另启一个Pod负责清理就好了。&lt;/p&gt;
&lt;p&gt;完成代码地址：https://github.com/zacker330/jvm-oom-example&lt;/p&gt;
- https://showme.codes/zh-cn/2024-2-26-jvm-oom-kubernetes/ - showme.codes</description>
        </item>
    
    
    
        <item>
        <title>Kubernetes包管理器Helm的本质</title>
        <link>https://showme.codes/zh-cn/2024-2-26-theory-of-helm/</link>
        <pubDate>Mon, 26 Feb 2024 00:00:00 +0000</pubDate>
        
        <guid>https://showme.codes/zh-cn/2024-2-26-theory-of-helm/</guid>
        <description>翟志军 Jack Zhai https://showme.codes/zh-cn/2024-2-26-theory-of-helm/ -&lt;p&gt;“本质”类的文章，通常很难带流量。而且写起来非常吃力。&lt;/p&gt;
&lt;p&gt;那我为什么还要写？写作是对自己的锻炼。写作是让自己的思想更有深度的一种有效方式。&lt;/p&gt;
&lt;p&gt;如果你觉得这篇文章对你有帮助，也你麻烦你转发这篇文章，这是对我的帮助。谢谢。&lt;/p&gt;
&lt;h2 id=&#34;kubernetes-的包管理器的本质&#34;&gt;Kubernetes 的包管理器的本质&lt;/h2&gt;
&lt;p&gt;“Helm 是 Kubernetes 的包管理器”。Helm的官方网站如是说。&lt;/p&gt;
&lt;p&gt;那什么是“Kubernetes 的包管理器”？&lt;/p&gt;
&lt;p&gt;我们假设需要在没包管理器的场景下部署资源，你需要一个个文件手工地执行&lt;code&gt;kubectl apply -f abc.yaml&lt;/code&gt;，abc.yaml就是Kubernetes的资源的定义文件。&lt;/p&gt;
&lt;p&gt;文件内容如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#eed;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#008b45;text-decoration:underline&#34;&gt;---&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;apiVersion&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;apps/v1&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;kind&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;Deployment&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;metadata&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;	&lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;name&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;abc&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;	&lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;labels&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;		&lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;app.kubernetes.io/name&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;abc&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;spec&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;	&lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;replicas&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#b452cd&#34;&gt;1&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;	&lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;selector&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;		&lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;matchLabels&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;			&lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;app.kubernetes.io/name&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;abc&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;当需要卸载资源呢？你又需要手工执行&lt;code&gt;kubectl delete -f abc.yaml&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;所以每次发布，你都必须有一个发布记录，记录下哪些YAML要执行apply，哪些yaml要执行delete。而且delete后，你还要记得将那个文件从文件夹中删除。&lt;/p&gt;
&lt;p&gt;如果每次手工执行，工作量大不说，还很容易出错。所以，有人会想到使用Shell脚本或者Python脚本来解决这些问题。&lt;/p&gt;
&lt;p&gt;当你通过Shell脚本或者Python脚本能自动化解决以上问题时，实际上就等于实现了一个Kubernetes 的包管理器。&lt;/p&gt;
&lt;p&gt;当我们真正理解以上所说的Kubernetes资源的部署问题后，你就明白了Kubernetes 的包管理器其实就两个核心功能：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;自动化执行Kubenetes资源更新；&lt;/li&gt;
&lt;li&gt;跟踪Kubenetes资源更新记录（本质还是版本化）。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;我们在选择包管理器时，务必要从这两个角度考虑。像Grafana公司Tanka，并不是一开始就实现“跟踪Kubenetes资源更新记录”功能，具体可以看：https://github.com/grafana/tanka/issues/88 。&lt;/p&gt;
&lt;h2 id=&#34;helm是如何实现包管理的&#34;&gt;Helm是如何实现包管理的&lt;/h2&gt;
&lt;p&gt;注：本文讲的是Helm3。Helm2与Helm3存在较大差异。&lt;/p&gt;
&lt;h3 id=&#34;helm的包chart&#34;&gt;Helm的包：Chart&lt;/h3&gt;
&lt;p&gt;假如存在一个微服务x，我们将其部署到Kubernetes中，需要准备Deployment、HPA、Service的这三种资源的YAML文件。这三个文件，统一放在一个文件夹中。&lt;/p&gt;
&lt;p&gt;Helm本身是一个命令行工具。通过package子命令，可以将整个文件夹打包成一个tgz的压缩包。打包命令为：&lt;code&gt;helm package x-service --version 1.0&lt;/code&gt; 。打包结果是一个tgz包。如下图：
&lt;img src=&#34;https://upload-images.jianshu.io/upload_images/292372-c272b0d600a1417f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240&#34; alt=&#34;Pasted image 20221213140013.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;这个tgz包，我们称之为Chart包。本质上它就是Kubernetes的资源文件的一个集合。&lt;/p&gt;
&lt;p&gt;我们可以将Chart包上传到Nexus这类制品管理工具进行版本化控制。这涉及到Chart的管理的工程实践，不在本文范围。&lt;/p&gt;
&lt;p&gt;在有了Chart包以后，我们可以通过命令&lt;code&gt;helm install &amp;lt;release&amp;gt; &amp;lt;chart路径&amp;gt;&lt;/code&gt;将svc安装到指定的Kubernetes集群上。如x-svc的部署指令将会是：&lt;code&gt;helm install x-svc ./x-svc-1.0.tgz&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;release&lt;/code&gt;是Helm的一个概念，即发布名。每执行一次helm install，对于Helm来说就是创建一个release。通常我们使用应用名作为发布名。&lt;/p&gt;
&lt;p&gt;release这个概念在资源变更跟踪中环节非常重要。后面会反复使用此概念。&lt;/p&gt;
&lt;h3 id=&#34;使用模板技术解决chart的规模性问题&#34;&gt;使用模板技术解决Chart的规模性问题&lt;/h3&gt;
&lt;p&gt;实际工作中，我们还会有y-svc、z-svc……n个服务。我们是不是每个服务要创建一个Chart？另外，每个服务都将被部署到三个环境中，那么，是不是每个环境还要单独又创建一个Chart？最终，我们需要服务与环境两个维度进行排列组合个Chart。&lt;/p&gt;
&lt;p&gt;如果不能很好解决这个问题。Chart的数量会爆炸式增长。Helm如何解决这个问题呢？&lt;/p&gt;
&lt;p&gt;它通过模板技术解决。换句话说，就是将Chart中资源文件中的容易变化的部分配置抽离出来变成变量，不变的部分变成模板。&lt;/p&gt;
&lt;p&gt;变量部分配置统一放在Chart包中的values.yaml文件中。所以，这部分配置，我们通常也称为values配置，或者values文件。&lt;/p&gt;
&lt;p&gt;这样，我们的Chart包的结构就变成如下（实际还有一些别的文件，但是不是本文讨论范围）：&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://upload-images.jianshu.io/upload_images/292372-318d56cd0c4df44f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240&#34; alt=&#34;Pasted image 20221213151111.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;对于Chart中不变的部分，Helm使用gotemplate模板语言进行描述。就是说我们可以在deployments.yaml中直接写gotemplate模板语言了，如代码1：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#eed;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;apiVersion&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;apps/v1&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;kind&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;Deployment&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;metadata&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;	&lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;name&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;{{&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;.Values.name }}&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;	&lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;labels&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;	&lt;/span&gt;{{- include &amp;#34;demo.labels&amp;#34; . | nindent 4 }}&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#008b45;text-decoration:underline&#34;&gt;...&lt;/span&gt;篇幅有限，省略&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;		&lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;resources&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;			&lt;/span&gt;{{- toYaml .Values.resources | nindent 12 }}&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我们无意挑起模板语言的战争，我们对gotemplate没有好感。你需要小心翼翼地去维护模板文件中的空格数量。如代码1的最后一行，它的意思是指该YAML块缩进12个空格。&lt;/p&gt;
&lt;p&gt;关键问题我们该如何确定它应该是12，而不是10呢？而且，如果重构这部分代码，我又要重新算一次空格的数量！&lt;/p&gt;
&lt;p&gt;使用gotemplate作为它的模板语言是它的最大错误。我们可能需要另外写一篇文章介绍规避这个问题的方法。&lt;/p&gt;
&lt;p&gt;在写Helm的gotemplate模板时，建议不要写太复杂的逻辑，代码宁可重复，甚至另创建一个新的Chart。&lt;/p&gt;
&lt;h3 id=&#34;执行资源变更&#34;&gt;执行资源变更&lt;/h3&gt;
&lt;p&gt;当values与Chart都已经准备好之后，我们通过以下命令，即可将x-svc的所有的资源部署到指定的namespace中：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#eed;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;helm install x-svc ./svc-chart-1.0.tgz -f x-svc-value.yaml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;注意，一个不存在的服务，首次部署时是要执行install子命令。将来更新时，就只能执行upgrade子命令了。&lt;/p&gt;
&lt;p&gt;以此类推，y-svc的部署命令就是：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#eed;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;helm install y-svc ./svc-chart-1.0.tgz -f y-svc-value.yaml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在执行install成功后，如果你需要修改该release，你需要执行upgrade指令，如下：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;helm upgrade y-svc ./svc-chart-2.0.tgz -f y-svc-value.yaml
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;但是，helm是如何知道是要执行创建/变更资源，还是要执行删除资源呢？svc-chart-2.0.tgz比1.0版本可能少了deployment资源。&lt;/p&gt;
&lt;p&gt;这就涉及到资源变更的跟踪了。&lt;/p&gt;
&lt;h3 id=&#34;资源变更跟踪&#34;&gt;资源变更跟踪&lt;/h3&gt;
&lt;p&gt;在介绍“资源变更跟踪”前，我们先介绍几个重要的相关子命令：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;upgrade：更新已存在的release。如：&lt;code&gt;helm upgrade y-svc ./svc-chart-1.0.tgz -f y-svc-value.yaml&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;list：列出所有的已经安装的release；&lt;/li&gt;
&lt;li&gt;rollback: 将指定的release进行回滚。甚至可以指定回滚到某个版本，命令：&lt;code&gt;helm rollback &amp;lt;RELEASE&amp;gt; [REVISION] [flags]&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;history: 列出release的发布记录。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;有些同学可能发现问题了：执行helm命令时，即没有默认的，也没有显示指定的release持久化方式，这些release信息是记录在哪里的？&lt;/p&gt;
&lt;p&gt;同时，这又与我们上文说的“资源变更跟踪”有什么关系？&lt;/p&gt;
&lt;p&gt;它们是相关的。Helm的核心原理就在此：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;当首次部署时，使用install，这时，Helm会直接在指定命名空间（默认是default）下，创建一个helm.sh/release类型的secret。secret的名称定义为：sh.helm.release.v1.release.v1。secret的内容是这次执行的所有的Kubernetes资源的YAML内容。&lt;/li&gt;
&lt;li&gt;当使用upgrade更新时，Helm从sh.helm.release.v1.release.v1的secret取出所有的YAML资源内容与本次将要执行更新的YAML资源内容进行对比，计算出本次更新需要执行的操作，是删除，变更。源码：https://github.com/helm/helm/blob/main/pkg/action/upgrade.go#L286&lt;/li&gt;
&lt;li&gt;当upgrade执行成功，Helm会创建名为sh.helm.release.v1.release.v2的secret。当你看到这个v2的时候，你就已经知道了。Helm是通过结合secret的名称约定和secret的内容来记录下每一次发布的。当下次upgrade时，Helm会取v2的secret，然后执行更新，并创建v3的secret。以此类推。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;为了展示的更友好，Helm把这些底层都隐藏下来了，所以，当你执行history指令时，你看到的将是：&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://showme.codes/assets/images/helm-theory.png&#34; alt=&#34;helm-theory.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;截图取自Helm官网&lt;/p&gt;
&lt;p&gt;至此，整个Helm的本质，已经介绍完。剩下细节可以通过查文档学习了。&lt;/p&gt;
&lt;h2 id=&#34;小结&#34;&gt;小结&lt;/h2&gt;
&lt;p&gt;虽然本文标题写的是Helm的本质，其实写的是Kubernetes的包管理器的本质：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;自动化执行Kubenetes资源更新；&lt;/li&gt;
&lt;li&gt;跟踪Kubenetes资源更新记录（本质还是版本化）。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;你可以拿这两次去评估Kustomize或者另的包管理工具。&lt;/p&gt;
- https://showme.codes/zh-cn/2024-2-26-theory-of-helm/ - showme.codes</description>
        </item>
    
    
    
        <item>
        <title>SRE-DevOps不得不懂的：Prometheus的配置工程化</title>
        <link>https://showme.codes/zh-cn/2024-2-26-prometheus-engineering/</link>
        <pubDate>Mon, 26 Feb 2024 00:00:00 +0000</pubDate>
        
        <guid>https://showme.codes/zh-cn/2024-2-26-prometheus-engineering/</guid>
        <description>翟志军 Jack Zhai https://showme.codes/zh-cn/2024-2-26-prometheus-engineering/ -&lt;h1 id=&#34;背景&#34;&gt;背景&lt;/h1&gt;
&lt;p&gt;Prometheus有两个最基本的组件：一个是Prometheus程序，一个是Alertmanager程序。&lt;/p&gt;
&lt;p&gt;它们的职责分工很明确：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Prometheus程序负责：定时拉取监控指标数据、存储指标数据、根据告警规则发起告警通知；&lt;/li&gt;
&lt;li&gt;Alertmanager程序负责：负责告警通知的路由，即当接收到Prometheus程序的通知后，该将通知以何种方式通知给谁。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;https://showme.codes/assets/images/prometheus-config-1.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Prometheus程序的配置最核心的&lt;a href=&#34;https://prometheus.io/docs/prometheus/latest/configuration/configuration/#configuration&#34;&gt;配置&lt;/a&gt;是：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#eed;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#228b22&#34;&gt;# ... &lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#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:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#228b22&#34;&gt;# 当指标数据符合什么规则进行告警通知。&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#228b22&#34;&gt;# 在其它文件定义，这里只是引用该文件的路径。&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;rule_files&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;[&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;- &amp;lt;filepath_glob&amp;gt; ... ]&lt;span style=&#34;color:#bbb&#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:#bbb&#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:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#228b22&#34;&gt;# 从哪里，该如何拉取指标&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;scrape_configs&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;[&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;- &amp;lt;scrape_config&amp;gt; ... ]&lt;span style=&#34;color:#bbb&#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:#bbb&#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:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#228b22&#34;&gt;# ...&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Alertmanager程序的配置最核心的&lt;a href=&#34;https://prometheus.io/docs/alerting/latest/configuration/#configuration&#34;&gt;配置&lt;/a&gt;是：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#eed;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#228b22&#34;&gt;# ...&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#228b22&#34;&gt;# 告警通知路由规则&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;route&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;	&lt;/span&gt;[- &amp;lt;route_config&amp;gt;-]&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#228b22&#34;&gt;# 告警通知的接收者列表，部分监控告警平台也称之为channel&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;receivers&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;	&lt;/span&gt;[- &amp;lt;receivers&amp;gt;-]&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#228b22&#34;&gt;# ...&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在实际工作中，Prometheus和Alertmanager的配置会非常大。&lt;/p&gt;
&lt;p&gt;严谨的软件工程要求我们在真正部署这些配置前，对其进行有效性和正确性的检查。否则SRE/DevOps的工程效率就会很低，因为你需要手工调试庞大的配置。&lt;/p&gt;
&lt;p&gt;所以，我们需要有一种高效率的方式来保证配置的有效性和正确性。&lt;/p&gt;
&lt;h1 id=&#34;保证prometheus程序配置的有效性和正确性&#34;&gt;保证Prometheus程序配置的有效性和正确性&lt;/h1&gt;
&lt;h2 id=&#34;promtool&#34;&gt;promtool&lt;/h2&gt;
&lt;p&gt;Prometheus程序提供了一个叫promtool的命令行程序。解压Prometheus的程序包后，你会发现它和Prometheus程序放在一个文件夹中。&lt;/p&gt;
&lt;p&gt;promtool提供了一些子命令来保证Prometheus程序配置的有效性和正确性：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#eed;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#228b22&#34;&gt;# 校验Prometheus配置的有效性，它支持--lint=&amp;#34;duplicate-rules&amp;#34;参数，用于检查重复的rule配置&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;check config [&amp;lt;flags&amp;gt;] &amp;lt;config-files&amp;gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#228b22&#34;&gt;# 校验rule配置的有效性&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;check rules [&amp;lt;flags&amp;gt;] &amp;lt;rule-files&amp;gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#228b22&#34;&gt;# 执行rules单元测试用例&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:#658b00&#34;&gt;test&lt;/span&gt; rules &amp;lt;test-rule-file&amp;gt;...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;至于有效性检查，只需要执行check子命令即可，不需要过多说明。&lt;/p&gt;
&lt;p&gt;promtool的&lt;code&gt;test rules&lt;/code&gt;子命令可以实现rule配置的单元测试，具体命令如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#eed;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;./promtool &lt;span style=&#34;color:#658b00&#34;&gt;test&lt;/span&gt; rules test.yml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;test.yaml是单元测试描述文件。promtool是支持同时指定多个单元测试文件的，如：&lt;code&gt;./promtool test rules test.yml test1.yml test2.yml&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;单元测试描述文件内容的格式如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#eed;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#228b22&#34;&gt;# Prometheus的rule配置文件路径&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;rule_files&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;    &lt;/span&gt;- rule1.yml&lt;span style=&#34;color:#bbb&#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:#bbb&#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:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#228b22&#34;&gt;# 评估的间隔时长&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;evaluation_interval&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;1m&lt;span style=&#34;color:#bbb&#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:#bbb&#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:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#228b22&#34;&gt;# 单元测试列表&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;tests&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;&lt;/span&gt;- &lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;interval&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;1m&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;input_series&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;alert_rule_test&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;promql_expr_test&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;tests&lt;/code&gt;下的每个用例由4个字段组成：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;input_series：测试用例的测试数据，即指标的时序数据；&lt;/li&gt;
&lt;li&gt;interval：代表每个时序数据之间的间隔时长；&lt;/li&gt;
&lt;li&gt;alert_rule_test：告警规则的测试用例；&lt;/li&gt;
&lt;li&gt;promql_expr_test：promql表达式的测试用例。我们可以使用它进行调试我们的promsql。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;接下来将详细介绍它们。&lt;/p&gt;
&lt;h2 id=&#34;测试用例数据&#34;&gt;测试用例数据&lt;/h2&gt;
&lt;p&gt;测试用例数据的定义格式如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#eed;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;input_series&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;- &lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;series&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#39;up{job=&amp;#34;prometheus&amp;#34;, instance=&amp;#34;localhost:9090&amp;#34;}&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;	&lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;values&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#39;0 0 0 0 0 0 0 0 0 0 0 0 0 0 0&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;- &lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;series&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#39;up{job=&amp;#34;node_exporter&amp;#34;, instance=&amp;#34;localhost:9100&amp;#34;}&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;	&lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;values&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#39;1+0x6 0 0 0 0 0 0 0 0&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;- &lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;series&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#39;go_goroutines{job=&amp;#34;prometheus&amp;#34;, instance=&amp;#34;localhost:9090&amp;#34;}&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;	&lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;values&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#39;10+10x2 30+20x5&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;- &lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;series&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#39;go_goroutines{job=&amp;#34;node_exporter&amp;#34;, instance=&amp;#34;localhost:9100&amp;#34;}&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;	&lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;values&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#39;10+10x7 10+30x4&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;每一条input_serie由两个字段组成：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;series：指标时序数据的key&lt;/li&gt;
&lt;li&gt;values：指标的value。其中的每一个值之间的间隔时长是&lt;code&gt;interval&lt;/code&gt;的值&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;为了简化values的值的定义，你可以一种扩展符号来定义其值。语法如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;a+bxc&lt;/code&gt;代表：&lt;code&gt;a a+b a+(2*b) a+(3*b) … a+(c*b)&lt;/code&gt;；这一个a值是起始值的序列，然后a以b的(0..c)的倍数进行递增；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;a-bxc&lt;/code&gt;表示：这一个a值是起始值的序列，然后a以b的(0..c)的倍数进行递减；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;_&lt;/code&gt;下划线表示：序列中的某次指标值没有被抓取到；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;stale&lt;/code&gt;表示：过期的样本数据。
以下是一些来自官方文档的例子：&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-2+4x3&lt;/code&gt;表示：&lt;code&gt;-2 2 6 10&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1-2x4&lt;/code&gt;表示：&lt;code&gt;1 -1 -3 -5 -7&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1x4&lt;/code&gt;表示：&lt;code&gt;1 1 1 1 1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 _x3 stale&lt;/code&gt;表示：&lt;code&gt;1 _ _ _ stale&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1+0x6 0 0 0 0 0 0 0 0&lt;/code&gt;表示： &lt;code&gt;1 1 1 1 1 1 1 0 0 0 0 0 0 0 0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;10+10x2 30+20x5&lt;/code&gt;表示：10 20 30 30 50 70 90 110 130&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;测试promsql表达式&#34;&gt;测试Promsql表达式&lt;/h2&gt;
&lt;p&gt;在写Prometheus告警规则时，一个很大的痛点就是无法简单的验证自己写的promsql的正确性。我们在告警规则的单元测试中验证后，再配置到真正的rule文件中。&lt;/p&gt;
&lt;p&gt;promsql的测试用例的写法如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#eed;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;promql_expr_test&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;- &lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;expr&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;go_goroutines &amp;gt; 5 &lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;	&lt;/span&gt;&lt;span style=&#34;color:#228b22&#34;&gt;# 要测试的promsql表达式，它将会从测试数据中查询数据&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;	&lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;eval_time&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;4m&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;	&lt;/span&gt;&lt;span style=&#34;color:#228b22&#34;&gt;# 评估时长。从测试数据的第0秒开始算。&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;	&lt;/span&gt;&lt;span style=&#34;color:#228b22&#34;&gt;# 如果interval是1m，那么4m代表的是测试数据的第4个数值&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;	&lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;exp_samples&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#228b22&#34;&gt;# 执行promsql表达式后，预期得到的数据结果&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;		&lt;/span&gt;- &lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;labels&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#39;go_goroutines{job=&amp;#34;prometheus&amp;#34;,instance=&amp;#34;localhost:9090&amp;#34;}&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;		  &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;value&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#b452cd&#34;&gt;50&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;		&lt;/span&gt;- &lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;labels&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#39;go_goroutines{job=&amp;#34;node_exporter&amp;#34;,instance=&amp;#34;localhost:9100&amp;#34;}&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;		  &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;value&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#b452cd&#34;&gt;50&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;告警规则的单元测试&#34;&gt;告警规则的单元测试&lt;/h2&gt;
&lt;p&gt;在单元测试描述文件中，添加Promsql表达式的测试用例的同时，我们还可以添加告警规则的测试用例，代码样例如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#eed;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;alert_rule_test&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;&lt;/span&gt;- &lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;eval_time&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;10m&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;alertname&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;InstanceDown&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;exp_alerts&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;  &lt;/span&gt;- &lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;exp_labels&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;	  &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;severity&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;page&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;	  &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;instance&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;localhost:9090&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;	  &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;job&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;prometheus&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;	&lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;exp_annotations&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;	  &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;summary&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#34;Instance localhost:9090 down&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#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:#bbb&#34;&gt;	  &lt;/span&gt;&lt;span style=&#34;color:#8b008b;font-weight:bold&#34;&gt;description&lt;/span&gt;:&lt;span style=&#34;color:#bbb&#34;&gt; &lt;/span&gt;&lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#34;localhost:9090 of job prometheus has been down for more than 5 minutes.&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#bbb&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;eval_time：规则评估时长；&lt;/li&gt;
&lt;li&gt;alertname：告警名，要求与Prometheus的告警规则中的alertname一致；&lt;/li&gt;
&lt;li&gt;exp_labels：预期收到的告警通知中的label值；&lt;/li&gt;
&lt;li&gt;exp_annotations：预期收到的告警通知中的annotation值。&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;保证alertmanager程序配置的有效性和正确性&#34;&gt;保证Alertmanager程序配置的有效性和正确性&lt;/h1&gt;
&lt;h2 id=&#34;amtool&#34;&gt;amtool&lt;/h2&gt;
&lt;p&gt;与Prometheus程序类似，Alertmanager程序提供了一个叫&lt;a href=&#34;https://github.com/prometheus/alertmanager#amtool&#34;&gt;amtool&lt;/a&gt;的命令行程序。&lt;/p&gt;
&lt;p&gt;我们关注它的两个子命令：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;config routes test&lt;/code&gt;：验证配置的正确性&lt;/li&gt;
&lt;li&gt;&lt;code&gt;check-config &amp;lt;config.yaml&amp;gt;&lt;/code&gt;：验证配置的有效性&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;config-routes-test子命令介绍&#34;&gt;config routes test子命令介绍&lt;/h2&gt;
&lt;p&gt;amtool不像promtool那样支持在YAML文件中定义测试用例，以下是它的命令样例：
&lt;code&gt;amtool config routes test --config.file=config.yaml --verify.receivers=team-X-pager service=database owner=team-X&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;--config.file&lt;/code&gt;参数指定了配置文件的路径。&lt;/p&gt;
&lt;p&gt;除了支持指定配置的路径，还可以通过参数&lt;code&gt;--alertmanager.url&lt;/code&gt;指定使用某个运行中的Alertmanager的配置。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;--verify.receivers&lt;/code&gt;指定期望返回的receiver列表，使用逗号分隔。&lt;/p&gt;
&lt;p&gt;该子命令的最后是标签集，由key=value的格式组成，并使用空格分隔。例子中&lt;code&gt;service=database owner=team-X&lt;/code&gt;，代表的是&lt;code&gt;{service=&amp;quot;database&amp;quot;,owner=&amp;quot;team-X&amp;quot;}&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;为了更好的可视化，还可以加一个&lt;code&gt;--tree&lt;/code&gt;的参数，效果如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#eed;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;% amtool config routes &lt;span style=&#34;color:#658b00&#34;&gt;test&lt;/span&gt; --config.file=config.yaml --tree --verify.receivers=team-X-pager &lt;span style=&#34;color:#00688b&#34;&gt;service&lt;/span&gt;=database &lt;span style=&#34;color:#00688b&#34;&gt;owner&lt;/span&gt;=team-X
&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;Matching routes:
&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;└── default-route
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    └── {&lt;span style=&#34;color:#00688b&#34;&gt;service&lt;/span&gt;=&lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#34;database&amp;#34;&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:#00688b&#34;&gt;owner&lt;/span&gt;=&lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#34;team-X&amp;#34;&lt;/span&gt;}  receiver: team-X-pager
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果验证失败，该命令返回非0结果。&lt;/p&gt;
&lt;h2 id=&#34;check-config子命令介绍&#34;&gt;check-config子命令介绍&lt;/h2&gt;
&lt;p&gt;它的运行效果如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#eed;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;% amtool check-config config.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Checking &lt;span style=&#34;color:#cd5555&#34;&gt;&amp;#39;config.yaml&amp;#39;&lt;/span&gt;  SUCCESS
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Found:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; - global config
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; - route
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; - &lt;span style=&#34;color:#b452cd&#34;&gt;1&lt;/span&gt; inhibit rules
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; - &lt;span style=&#34;color:#b452cd&#34;&gt;5&lt;/span&gt; receivers
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; - &lt;span style=&#34;color:#b452cd&#34;&gt;1&lt;/span&gt; templates
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  SUCCESS
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果验证失败，该命令返回非0结果。&lt;/p&gt;
&lt;h2 id=&#34;可视化告警通知路由&#34;&gt;可视化告警通知路由&lt;/h2&gt;
&lt;p&gt;Prometheus官网提供了一个告警通知路由的&lt;a href=&#34;https://prometheus.io/webtools/alerting/routing-tree-editor/?_gl=1&#34;&gt;在线可视化编辑器&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://showme.codes/assets/images/prometheus-config-2.png&#34; alt=&#34;alertmanager-route-editor.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;将配置粘贴至编辑框中，然后在“Match Label Set”中输入告警的标签，最后下方会显示通知的路由路径。如下图，实心红点即是匹配了该label的receiver：&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://showme.codes/assets/images/prometheus-config-3.png&#34; alt=&#34;alertmanager-route-editor.png&#34;&gt;pro&lt;/p&gt;
&lt;p&gt;可视化工具在路由配置调试阶段非常有用。减小了路由配置的难度。但是，需要注意：不要将任何敏感配置上传到公网。&lt;/p&gt;
&lt;h1 id=&#34;如何集成到cicd-pipeline中&#34;&gt;如何集成到CI/CD Pipeline中&lt;/h1&gt;
&lt;p&gt;以上介绍的是两个命令最原始的使用方法，即手工运行。我们需要将其集成到CI/CD pipeline中，以实现工程化。&lt;/p&gt;
&lt;p&gt;集成方式一般有两：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;在Pipeline中增加一个执行promtool和amtool的阶段&lt;/li&gt;
&lt;li&gt;集成构建工具中，比如集成到Bazel中。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;当然有一些DevOps平台如果需要深度集成，可以将promtool的amtool的实现代码引入到自己的DevOps平台的代码中。&lt;/p&gt;
&lt;h1 id=&#34;工程化的挑战&#34;&gt;工程化的挑战&lt;/h1&gt;
&lt;p&gt;另一个工程化的挑战，就是以上的配置文件之间存在引用，如下prometheus的rule文件中的expr字段的值，实际上是被prometheus-unitesting.yml文件引用。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://upload-images.jianshu.io/upload_images/292372-26b61dfe31e0de99.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;如果不对这个引用关系进行治理，这些配置的维护成本将会非常高。&lt;/p&gt;
&lt;p&gt;由于YAML文件天生不具备变量定义的功能。可以采用类似Jsonnet、CUE这样的支持编程的配置语言代替YAML。&lt;/p&gt;
&lt;p&gt;这个话题比较大，不在本文讨论范围。&lt;/p&gt;
- https://showme.codes/zh-cn/2024-2-26-prometheus-engineering/ - showme.codes</description>
        </item>
    
    
  </channel>
</rss> 