<?xml version="1.0" encoding="utf-8"?>
    <feed xmlns="http://www.w3.org/2005/Atom">
      <title>Lawrence</title>
      <subtitle>Blog</subtitle>
      <link href="https://lawrenceli.me/atom.xml" rel="self" type="application/atom+xml" />
      <link href="https://lawrenceli.me/"/>
      <id>https://lawrenceli.me/blog</id>
      <updated>2022-08-28T03:18:32.088Z</updated>
      <author>
          <name>Lawrence</name>
          <email>lonor@live.com</email>
      </author>
     
      <entry>
        <title>基于 Go 的 ChatOps 实践</title>
        <id>https://lawrenceli.me/blog/chat-ops</id>
        <link href="https://lawrenceli.me/blog/chat-ops"/>
        <updated>2021-02-06T00:00:00.000Z</updated>
        <summary type="html">&lt;p&gt;聊着天就把活干了。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;公司的运维一直由我来帮助实施。去年将所有微服务容器化后，运维的成本面临新的挑战。本着解放生产力的目标，顺带回忆不太熟悉的 Go 基本语法，笔者写了一个钉钉机器人来帮助我做一些运维的活。效果也不错，就把大体做法在这里讲讲。&lt;/p&gt;
&lt;p&gt;做这个之前有一系列的必要（也可替换的）基础设施和概念以及 HTTP 服务端开发的认知，能实现这套聊天运维离不开每一个环节以及诸多细节。简单概括一下我是怎么实践的。&lt;/p&gt;
&lt;h3 id=&quot;技术栈依赖&quot;&gt;&lt;a href=&quot;#%E6%8A%80%E6%9C%AF%E6%A0%88%E4%BE%9D%E8%B5%96&quot; style=&quot;text-decoration: none; margin-left: -17px;&quot;&gt;&lt;span style=&quot;color: rgba(153, 153, 153, 0.5); font-weight: 430;&quot;&gt;&lt;span style=&quot;padding: 0 4px 0 0&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;技术栈依赖&lt;/h3&gt;
&lt;p&gt;实践 ChatOps 至少需要的基础设施：一个 CI / CD 服务，一个暴露在公网的 HTTP 服务，一个远程 VCS 和一个能提供收发消息 API 的即时通信工具。&lt;/p&gt;
&lt;p&gt;本项目采用：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.jenkins.io/&quot;&gt;Jenkins&lt;/a&gt; (CI / CD，经典的持续集成工具，提供公网可访问的 REST API)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://git-scm.com/&quot;&gt;Git&lt;/a&gt; (VCS，本项目使用公司正在用的 &lt;a href=&quot;https://gitee.com&quot;&gt;Gitee.com&lt;/a&gt; - 可使用 &lt;a href=&quot;http://github.com&quot;&gt;GitHub.com&lt;/a&gt; 或 GitLab，提供 REST API 或 SDK)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://vercel.com/docs/serverless-functions/introduction&quot;&gt;Serverless&lt;/a&gt; (用来暴露核心的 HTTP 端点，只是无需运维而已；也不是必须，否则需要有公网服务器)&lt;/li&gt;
&lt;li&gt;DingTalk (限制&lt;a href=&quot;https://ding-doc.dingtalk.com/document/app/develop-enterprise-internal-robots&quot;&gt;企业内部机器人&lt;/a&gt;，能提供 WebHook 和收发消息的 SDK 即可，或其他类似 Slack 的聊天应用: 企业微信、飞书等等)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;系统拓扑&quot;&gt;&lt;a href=&quot;#%E7%B3%BB%E7%BB%9F%E6%8B%93%E6%89%91&quot; style=&quot;text-decoration: none; margin-left: -17px;&quot;&gt;&lt;span style=&quot;color: rgba(153, 153, 153, 0.5); font-weight: 430;&quot;&gt;&lt;span style=&quot;padding: 0 4px 0 0&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;系统拓扑&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;/images/chat-ops/1.png&quot; alt=&quot;ChatOPS&quot;&gt;&lt;/p&gt;
&lt;p&gt;懒汉只会和机器人以聊天的形式交互，&lt;a href=&quot;/blog/the-world-as-i-see-it&quot;&gt;其余的脏活累活重复劳动交给机器人做&lt;/a&gt;。大致流程如图，重点实现：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;迎接聊天机器人的 HTTP 请求&lt;/li&gt;
&lt;li&gt;根据请求文本触发行为
&lt;ul&gt;
&lt;li&gt;Git Workflow
&lt;ul&gt;
&lt;li&gt;Approve Pull Request&lt;/li&gt;
&lt;li&gt;Test Pull Request&lt;/li&gt;
&lt;li&gt;Merge Pull Request&lt;/li&gt;
&lt;li&gt;Show PR list&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;CI / CD
&lt;ul&gt;
&lt;li&gt;CI Test&lt;/li&gt;
&lt;li&gt;Deploy to Production&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;很显然，核心的开发工作的都在 Message Handler 那里。根据聊天机器人提供的 Event Hook（每次聊天机器人收到消息时会自动向某个服务器发送 HTTP 请求，会携带发送消息的用户、消息内容等 Payload），可以设计一个 HTTP 端点，在服务端解析这些请求（钉钉需要按照企业内部机器人开发，&lt;a href=&quot;https://ding-doc.dingtalk.com/document/app/develop-enterprise-internal-robots&quot;&gt;参考这篇开发文档&lt;/a&gt;）。&lt;/p&gt;
&lt;p&gt;我们按照 &lt;a href=&quot;/blog/prow&quot;&gt;Kubernetes Prow 的设计语言&lt;/a&gt;，用一个  &lt;code&gt;/&lt;/code&gt;  来作为 Tag，形式如同 &lt;code&gt;/test xxx&lt;/code&gt; .&lt;/p&gt;
&lt;p&gt;因此这里必然需要做字符串处理了。除了判断 Tag 的存在之外，要取 Tag 后面的参数。项目用 Go 实现，很简单。贴其中一工具函数的代码，各位猜猜这是做什么的：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-go&quot;&gt;&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;StringIndexOf&lt;/span&gt;&lt;span class=&quot;hljs-params&quot;&gt;(originalArray []&lt;span class=&quot;hljs-keyword&quot;&gt;string&lt;/span&gt;, wordToFind &lt;span class=&quot;hljs-keyword&quot;&gt;interface&lt;/span&gt;{})&lt;/span&gt; []&lt;span class=&quot;hljs-title&quot;&gt;int&lt;/span&gt;&lt;/span&gt; {
	length := &lt;span class=&quot;hljs-built_in&quot;&gt;len&lt;/span&gt;(originalArray)
	interfaceArray := &lt;span class=&quot;hljs-built_in&quot;&gt;make&lt;/span&gt;([]&lt;span class=&quot;hljs-keyword&quot;&gt;interface&lt;/span&gt;{}, length)
	&lt;span class=&quot;hljs-keyword&quot;&gt;for&lt;/span&gt; i, v := &lt;span class=&quot;hljs-keyword&quot;&gt;range&lt;/span&gt; originalArray {
		interfaceArray[i] = v
	}
	&lt;span class=&quot;hljs-keyword&quot;&gt;var&lt;/span&gt; indexArray []&lt;span class=&quot;hljs-keyword&quot;&gt;int&lt;/span&gt;
	&lt;span class=&quot;hljs-keyword&quot;&gt;for&lt;/span&gt; i:=&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt; ; i &amp;#x3C; length; i++ {
		&lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; strings.Compare(wordToFind.(&lt;span class=&quot;hljs-keyword&quot;&gt;string&lt;/span&gt;), originalArray[i]) == &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt; {
			indexArray = &lt;span class=&quot;hljs-built_in&quot;&gt;append&lt;/span&gt;(indexArray, i)
		}
	}
	&lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; indexArray
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;拿到参数后继续判断，比如机器人收到了这个指令：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/merge 233&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;即合并第  #233 个 Pull Request。要通过代码来合并，那操作 Git 就需要 SDK 或接口了。考虑到之前写 &lt;a href=&quot;https://github.com/Lonor/OpsBot&quot;&gt;Issue Ops Bot&lt;/a&gt; 的经验，Google 开源的 GitHub 的 &lt;a href=&quot;https://github.com/google/go-github&quot;&gt;Go-GitHub&lt;/a&gt; 写起来很方便，Gitee 应该也有人搞吧？果然搜到了华为的 OpenEuler 团队的仓库：&lt;a href=&quot;http://gitee.com/openeuler/go-gitee&quot;&gt;Go-Gitee&lt;/a&gt;。看来只有大厂才会给这些基础设施写 SDK 啊！果断加依赖:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-sh&quot;&gt;go get -u gitee.com/openeuler/go-gitee&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;版本下载下来给的时间戳居然就是这两天的，虽然没有任何测试代码，但看似更新蛮活跃，大胆直接用上了。后面写了一些接口，发现 SDK 提供的一些 API 也不全，比如没有审核通过 PR 和测试通过 PR 的 API，就自己顺带拿 &lt;code&gt;net/http&lt;/code&gt; 参考着 &lt;a href=&quot;https://gitee.com/api/v5/swagger&quot;&gt;Gitee 的 REST API&lt;/a&gt; 自己实现了，但是代码太丑陋（本来想模仿项目的写法去写，但是一心只想把功能完成），也没给华为提 issue，等回头有机会写下给它提个 PR 嘿嘿（除此之外我还发现 Gitee 的合并方式没有 &lt;code&gt;rebase&lt;/code&gt; 选项，只提供 &lt;code&gt;merge&lt;/code&gt; 和 &lt;code&gt;squash&lt;/code&gt; ，在所谓的社区企鹅群里和他们提了一下建议，理都不理)。&lt;/p&gt;
&lt;p&gt;如果机器人收到类似这样的指令:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/jenkins test myservice&lt;/code&gt; 或 &lt;code&gt;/deploy myservice&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;那就直接对接 &lt;a href=&quot;https://www.jenkins.io/doc/book/using/remote-access-api/&quot;&gt;Jenkins 的 REST API&lt;/a&gt; 了。可以直接凭状态码判断响应，安全方面 Jenkins 自身提供了  HTTP BASIC 认证。就不细说了。&lt;/p&gt;
&lt;p&gt;之前没有用 Go 写过 HTTP Server，这次也没有写——如果运维一套服务已经很麻烦了，开发出的一套帮助运维的辅助服务本身也需要运维（公网服务器、域名解析、持续的测试和部署等服务器资源以及&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E6%B3%A8%E6%84%8F%E5%8A%9B%E7%B6%93%E6%BF%9F&quot;&gt;注意力资源&lt;/a&gt;），我就没有使用传统的后端服务，而是使用了自己熟悉的 &lt;a href=&quot;https://vercel.com/docs/runtimes#official-runtimes/go&quot;&gt;Vercel Serveless for Go&lt;/a&gt;。公开一个 Handler 方法即可，&lt;code&gt;*http.Request&lt;/code&gt; 结构体指针的 &lt;code&gt;Body&lt;/code&gt; 就是机器人发来的消息了。&lt;/p&gt;
&lt;p&gt;下面是整个 Handler 的大致逻辑，伪代码：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-go&quot;&gt;&lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Handler&lt;/span&gt;&lt;span class=&quot;hljs-params&quot;&gt;(w http.ResponseWriter, r *http.Request)&lt;/span&gt;&lt;/span&gt; {
  &lt;span class=&quot;hljs-keyword&quot;&gt;defer&lt;/span&gt; fmt.Fprintf(w, &lt;span class=&quot;hljs-string&quot;&gt;&quot;ok&quot;&lt;/span&gt;)
  chatMessage, _ := ioutil.ReadAll(r.Body)
  &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; strings.Contains(chatMessage, &lt;span class=&quot;hljs-string&quot;&gt;&quot;/deploy&quot;&lt;/span&gt;) {
     serverName := getServerNameFromMessage(chatMessage)
     jenkinsSDK.deploy(serverName)
  }
  &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; strings.Contains(chatMessage, &lt;span class=&quot;hljs-string&quot;&gt;&quot;/merge&quot;&lt;/span&gt;) {
     pullRequestNumber := getPullRequestNumberFromMessage(chatMessage)
     giteeSDK.mergePullRequest(pullRequestNumber)
  }
  ...
 }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;期待某天 Vercel 能够支持 JVM 语言甚至是 Spring 的 Serverless。希望 &lt;a href=&quot;https://rauchg.com/&quot;&gt;@rauchg&lt;/a&gt; 大佬能看到。&lt;/p&gt;
&lt;h3 id=&quot;效果&quot;&gt;&lt;a href=&quot;#%E6%95%88%E6%9E%9C&quot; style=&quot;text-decoration: none; margin-left: -17px;&quot;&gt;&lt;span style=&quot;color: rgba(153, 153, 153, 0.5); font-weight: 430;&quot;&gt;&lt;span style=&quot;padding: 0 4px 0 0&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;效果&lt;/h3&gt;
&lt;p&gt;比如让机器人展示下仓库目前的 Pull Request，然后测试这个 PR，通过后批准这个 PR，最终合并 PR，上线生产，聊天记录会是这样的：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-txt&quot;&gt;Users 命令: /pr
Robot 回复: 当前仓库的 PullRequest 列表...
          [#709] fix: typo
          [author] username

Users 命令: /jenkins test micro-server 709
Robot 回复: 开始测试 PullRequest 709
CIBot 回复: 
          [jenkins] micro-server ci
          -------------------------
          任务：#666
          状态：开始
          持续时间：0 分 1 秒
          执行人：Host 76.76.21.21

Users 命令: /approve 709
Robot 回复: 审核通过 PullRequest #709

Users 命令: /merge 709
Robot 回复: 合并 PullRequest #709
Gitee 回复: User 接受了 Owner/repo 的 Pull Request !709 fix: typo

Users 命令: /deploy micro-server
Robot 回复: 生产发布 micro-server
CDBot 回复:
          [jenkins] micro-server cd
          -------------------------
          任务：#233
          状态：开始
          持续时间：0 分 1 秒
          执行人：Host 76.76.21.21

# 友好地提供帮助
Users 命令: /help
Robot 回复: 当前支持指令列表, 带 * 需要特定人员发起
          /pr - 展示仓库发起中的 PR
          /jenkins &amp;#x3C;action&gt; &amp;#x3C;servername&gt; &amp;#x3C;pr-number&gt; - 在指定 PR 下发起后端测试
          /pass &amp;#x3C;pr-number&gt; - * 测试通过指定 PR
          /approve &amp;#x3C;pr-number&gt; - * 审核通过指定 PR  
          /merge &amp;#x3C;pr-number&gt; - * 合并指定 PR
          /test &amp;#x3C;servername&gt; - 在仅有一个 PR 的状态下发起后端服务测试
          /deploy &amp;#x3C;servername&gt; - * 发布服务至生产环境
          /help - 显示此帮助内容&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;代码写的很丑陋，没脸往外放了，而且也是企业内部使用，就不开源了（所有变量，十个左右，都配置在环境变量中，尽量没有 Hard Code）。&lt;/p&gt;
&lt;p&gt;社区也有不少钉钉机器人的 SDK，阿里没有提供 Go 版本的，但写起来也不复杂，顺手提供自己写的&lt;a href=&quot;https://github.com/Lonor/dingtalkbot-sdk&quot;&gt;钉钉群机器人 Go 语言的 SDK&lt;/a&gt;，目前就只用来发文本消息。&lt;/p&gt;
&lt;p&gt;最近一次更新，让机器人支持了多个仓库，直接在 &lt;code&gt;/tag&lt;/code&gt; 最后加一个可选参数 &lt;code&gt;[repo]&lt;/code&gt;，然后 SDK 的参数做出相应的变动就实现了。&lt;/p&gt;
&lt;p&gt;相关链接：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://devops.phodal.com/pattern#chatops&quot;&gt;Pattern#ChatOps from Ledge —— DevOps knowledge learning platform&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</summary>
        <author><name>Lawrence</name></author>
      </entry>
      <entry>
        <title>WebSocket 集群方案</title>
        <id>https://lawrenceli.me/blog/websocket-cluster</id>
        <link href="https://lawrenceli.me/blog/websocket-cluster"/>
        <updated>2020-12-22T00:00:00.000Z</updated>
        <summary type="html">&lt;p&gt;基于 Spring Cloud 使用一致性哈希算法实现分布式 WebSocket. / 基于 RabbitMQ 广播实现分布式 WebSocket.&lt;/p&gt;
&lt;h3 id=&quot;websocket-协议&quot;&gt;&lt;a href=&quot;#websocket-%E5%8D%8F%E8%AE%AE&quot; style=&quot;text-decoration: none; margin-left: -17px;&quot;&gt;&lt;span style=&quot;color: rgba(153, 153, 153, 0.5); font-weight: 430;&quot;&gt;&lt;span style=&quot;padding: 0 4px 0 0&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;WebSocket 协议&lt;/h3&gt;
&lt;p&gt;参考 &lt;a href=&quot;https://tools.ietf.org/html/rfc6455&quot;&gt;RFC&lt;/a&gt; 标准规范。&lt;/p&gt;
&lt;h3 id=&quot;思路&quot;&gt;&lt;a href=&quot;#%E6%80%9D%E8%B7%AF&quot; style=&quot;text-decoration: none; margin-left: -17px;&quot;&gt;&lt;span style=&quot;color: rgba(153, 153, 153, 0.5); font-weight: 430;&quot;&gt;&lt;span style=&quot;padding: 0 4px 0 0&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;思路&lt;/h3&gt;
&lt;p&gt;核心问题在于具备 WebSocket 服务端的 SpringBoot 或 SpringCloud 等微服务应用是&lt;strong&gt;有状态&lt;/strong&gt;的。服务端的内存中存在维持连接的
Session。客户端连接服务器时，只能和集群中的唯一一个服务实例来连接，而且数据传输过程中也直接和此实例通信。&lt;/p&gt;
&lt;p&gt;解决方案有很多。&lt;/p&gt;
&lt;h4 id=&quot;方案一：将状态放到应用之外的存储中，但不可行&quot;&gt;&lt;a href=&quot;#%E6%96%B9%E6%A1%88%E4%B8%80%EF%BC%9A%E5%B0%86%E7%8A%B6%E6%80%81%E6%94%BE%E5%88%B0%E5%BA%94%E7%94%A8%E4%B9%8B%E5%A4%96%E7%9A%84%E5%AD%98%E5%82%A8%E4%B8%AD%EF%BC%8C%E4%BD%86%E4%B8%8D%E5%8F%AF%E8%A1%8C&quot; style=&quot;text-decoration: none; margin-left: -17px;&quot;&gt;&lt;span style=&quot;color: rgba(153, 153, 153, 0.5); font-weight: 430;&quot;&gt;&lt;span style=&quot;padding: 0 4px 0 0&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;方案一：将状态放到应用之外的存储中，但不可行&lt;/h4&gt;
&lt;p&gt;这个方案非常类似于 HTTP 协议的 Token 实现（非 JWT），Java 中具体的例子就是 Spring Session 以及 Spring Security 的 Token Store，比如 RedisTokenStore 实现了将
OAuth2 Token 存放到 Redis 的逻辑。此方案的核心前提是可序列化。WebSocket 协议和 HTTP 协议并不同，一个 WebSocket Session 对应于一次 TCP Socket
连接而非数据，新的连接将会基于新的四元组，不可能做到序列化。&lt;/p&gt;
&lt;h4 id=&quot;方案二：一致性哈希&quot;&gt;&lt;a href=&quot;#%E6%96%B9%E6%A1%88%E4%BA%8C%EF%BC%9A%E4%B8%80%E8%87%B4%E6%80%A7%E5%93%88%E5%B8%8C&quot; style=&quot;text-decoration: none; margin-left: -17px;&quot;&gt;&lt;span style=&quot;color: rgba(153, 153, 153, 0.5); font-weight: 430;&quot;&gt;&lt;span style=&quot;padding: 0 4px 0 0&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;方案二：一致性哈希&lt;/h4&gt;
&lt;p&gt;像微服务注册中心一样，维护一个全局映射关系 —— 当前客户端的 WebSocket
连接是和哪一个实例/节点保持的。每次通信都经过此注册中心或哈希，查询到具体的服务实例，将连接交给它。同样会遇到一些问题，比如：哈希环的实现、如何解决一致性问题（单节点故障的 Session 迁移重连将代价降到最低）、CAP
取舍、实例的区分依据是什么（IP ? 实例
ID？）、可能与注册中心构成强依赖、以及部署环境的变更带来运维上的挑战等难题。这个方案实施有一定的成本和技术限制，可以参考&lt;a href=&quot;https://segmentfault.com/a/1190000017307713&quot;&gt;这篇文章&lt;/a&gt;的大体思路，理解了核心的哈希环的概念(如下图)，再配合注册中心、服务上下线消息，就能完成基本的实现。我利用
Nacos、Redis、RabbitMQ 基于 Spring Cloud 写了一个通过一致性哈希来实现的全栈项目，前端和后端都已开源 ——
&lt;a href=&quot;https://github.com/Lonor/websocket-cluster&quot;&gt;WebSocket 集群的一致性哈希实践(Spring Cloud 后端) - GitHub&lt;/a&gt;
, &lt;a href=&quot;https://github.com/Lonor/websocket-cluster-front&quot;&gt;WebSocket 客户端模拟与服务列表(React 前端) - GitHub&lt;/a&gt;,
最近刚刚写好基本的功能，参考 &lt;a href=&quot;https://github.com/ufiredong&quot;&gt;@ufiredong&lt;/a&gt;
的方式利用 &lt;a href=&quot;https://github.com/docker-java/docker-java&quot;&gt;Java 语言的 Docker SDK&lt;/a&gt; 实现模拟 WebSocket 实例的上下线。未来还会稍微做细微的优化。欢迎提交 issue 或
pull request。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/websocket-cluster/hashring.png&quot; alt=&quot;HashRing:虚拟节点上线后部分客户端需要迁移节点&quot;&gt;&lt;/p&gt;
&lt;p&gt;另外，Nacos 作为微服务的注册中心是国内比较流行的选择。但有人早就向 Nacos 社区提出了&lt;a href=&quot;https://github.com/alibaba/nacos/issues/2114&quot;&gt;维护哈希环的议题&lt;/a&gt;；姑且不说此议题是否合理，但
Nacos 的维护人员竟然没有能够理解这样的需求，还是有点令人诧异。&lt;/p&gt;
&lt;p&gt;最终效果如下, 上线一个新的服务实例后，客户端会自动根据最新哈希环重新路由到最新节点上，收发消息一切正常：&lt;/p&gt;
&lt;video controls preload=&apos;metadata&apos;&gt;
    &lt;source src=&quot;https://gh-proxy.lawrenceli.me/https://raw.githubusercontent.com/Lonor/nextjs-blog/main/public/videos/demo.mp4&quot; type=&quot;video/mp4&quot;&gt;
&lt;/video&gt;
&lt;h4 id=&quot;方案三：广播队列&quot;&gt;&lt;a href=&quot;#%E6%96%B9%E6%A1%88%E4%B8%89%EF%BC%9A%E5%B9%BF%E6%92%AD%E9%98%9F%E5%88%97&quot; style=&quot;text-decoration: none; margin-left: -17px;&quot;&gt;&lt;span style=&quot;color: rgba(153, 153, 153, 0.5); font-weight: 430;&quot;&gt;&lt;span style=&quot;padding: 0 4px 0 0&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;方案三：广播队列&lt;/h4&gt;
&lt;p&gt;每次连接都需要通知到所有实例，实例各自都判断连接状态在不在自己这里，不在的话直接忽略，在就处理。类似于发布订阅模型，可以通过 MQ（RabbitMQ、Kafka、RocketMQ 等）或 Redis
做到。此方案实现简单，适用于集群规模不大的场景，因为需要所有的节点进行判断或计算。我愿称之为：分布式事件驱动。&lt;/p&gt;
&lt;p&gt;以下基于 RabbitMQ 做了简单实现。&lt;/p&gt;
&lt;h5 id=&quot;简单广播实现-websocket-集群&quot;&gt;&lt;a href=&quot;#%E7%AE%80%E5%8D%95%E5%B9%BF%E6%92%AD%E5%AE%9E%E7%8E%B0-websocket-%E9%9B%86%E7%BE%A4&quot; style=&quot;text-decoration: none; margin-left: -17px;&quot;&gt;&lt;span style=&quot;color: rgba(153, 153, 153, 0.5); font-weight: 430;&quot;&gt;&lt;span style=&quot;padding: 0 4px 0 0&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;简单广播实现 WebSocket 集群&lt;/h5&gt;
&lt;p&gt;&lt;a href=&quot;/blog/declarative-programming&quot;&gt;声明式 API&lt;/a&gt; 的好处就体现在这里：短短几行就直接利用了 RabbitMQ 的 Java SDK 创建好交换机和队列以及绑定关系。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-java&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;package&lt;/span&gt; me.lawrenceli.websocket.server.configuration;

&lt;span class=&quot;hljs-keyword&quot;&gt;import&lt;/span&gt; lombok.extern.slf4j.Slf4j;
&lt;span class=&quot;hljs-keyword&quot;&gt;import&lt;/span&gt; org.springframework.amqp.core.AnonymousQueue;
&lt;span class=&quot;hljs-keyword&quot;&gt;import&lt;/span&gt; org.springframework.amqp.core.Binding;
&lt;span class=&quot;hljs-keyword&quot;&gt;import&lt;/span&gt; org.springframework.amqp.core.BindingBuilder;
&lt;span class=&quot;hljs-keyword&quot;&gt;import&lt;/span&gt; org.springframework.amqp.core.FanoutExchange;
&lt;span class=&quot;hljs-keyword&quot;&gt;import&lt;/span&gt; org.springframework.context.annotation.Bean;
&lt;span class=&quot;hljs-keyword&quot;&gt;import&lt;/span&gt; org.springframework.context.annotation.Configuration;

&lt;span class=&quot;hljs-meta&quot;&gt;@Slf4j&lt;/span&gt;
&lt;span class=&quot;hljs-meta&quot;&gt;@Configuration&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hljs-title hljs-class&quot;&gt;MqConfig&lt;/span&gt; {

    &lt;span class=&quot;hljs-keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;hljs-type&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;hljs-variable&quot;&gt;FANOUT_EXCHANGE&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;websocket-exchange&quot;&lt;/span&gt;;

    &lt;span class=&quot;hljs-meta&quot;&gt;@Bean&lt;/span&gt;
    &lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; FanoutExchange &lt;span class=&quot;hljs-title hljs-function&quot;&gt;fanoutExchange&lt;/span&gt;&lt;span class=&quot;hljs-params&quot;&gt;()&lt;/span&gt; {
        log.info(&lt;span class=&quot;hljs-string&quot;&gt;&quot;创建广播交换机 [{}]&quot;&lt;/span&gt;, FANOUT_EXCHANGE);
        &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;hljs-title hljs-class&quot;&gt;FanoutExchange&lt;/span&gt;(FANOUT_EXCHANGE);
    }

    &lt;span class=&quot;hljs-meta&quot;&gt;@Bean&lt;/span&gt;
    &lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; AnonymousQueue &lt;span class=&quot;hljs-title hljs-function&quot;&gt;queueForWebSocket&lt;/span&gt;&lt;span class=&quot;hljs-params&quot;&gt;()&lt;/span&gt; {
        log.info(&lt;span class=&quot;hljs-string&quot;&gt;&quot;创建用于 WebSocket 的匿名队列&quot;&lt;/span&gt;);
        &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;hljs-title hljs-class&quot;&gt;AnonymousQueue&lt;/span&gt;();
    }

    &lt;span class=&quot;hljs-comment&quot;&gt;/**
     * &lt;span class=&quot;hljs-doctag&quot;&gt;@param&lt;/span&gt; fanoutExchange    交换机
     * &lt;span class=&quot;hljs-doctag&quot;&gt;@param&lt;/span&gt; queueForWebSocket 队列
     * &lt;span class=&quot;hljs-doctag&quot;&gt;@return&lt;/span&gt; Binding
     */&lt;/span&gt;
    &lt;span class=&quot;hljs-meta&quot;&gt;@Bean&lt;/span&gt;
    &lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; Binding &lt;span class=&quot;hljs-title hljs-function&quot;&gt;bindingSingle&lt;/span&gt;&lt;span class=&quot;hljs-params&quot;&gt;(FanoutExchange fanoutExchange, AnonymousQueue queueForWebSocket)&lt;/span&gt; {
        log.info(&lt;span class=&quot;hljs-string&quot;&gt;&quot;把匿名队列 [{}] 绑定到广播交换器 [{}]&quot;&lt;/span&gt;, queueForWebSocket.getName(), fanoutExchange.getName());
        &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; BindingBuilder.bind(queueForWebSocket).to(fanoutExchange);
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后是生产者和消费者。生产者将消息发送到队列中进行广播，集群的消费者监听队列，判断此 WebSocket Session 是否在当前消费节点再做进一步处理。&lt;/p&gt;
&lt;p&gt;消息生产方，一般是外层接收到消息通信请求，然后调用进行集群内广播：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-java&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;package&lt;/span&gt; me.lawrenceli.websocket.server.configuration;

&lt;span class=&quot;hljs-keyword&quot;&gt;import&lt;/span&gt; lombok.extern.slf4j.Slf4j;
&lt;span class=&quot;hljs-keyword&quot;&gt;import&lt;/span&gt; org.springframework.amqp.core.FanoutExchange;
&lt;span class=&quot;hljs-keyword&quot;&gt;import&lt;/span&gt; org.springframework.amqp.rabbit.core.RabbitTemplate;
&lt;span class=&quot;hljs-keyword&quot;&gt;import&lt;/span&gt; org.springframework.beans.factory.annotation.Autowired;
&lt;span class=&quot;hljs-keyword&quot;&gt;import&lt;/span&gt; org.springframework.stereotype.Component;

&lt;span class=&quot;hljs-meta&quot;&gt;@Slf4j&lt;/span&gt;
&lt;span class=&quot;hljs-meta&quot;&gt;@Component&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hljs-title hljs-class&quot;&gt;FanoutSender&lt;/span&gt; {

    &lt;span class=&quot;hljs-meta&quot;&gt;@Autowired&lt;/span&gt;
    &lt;span class=&quot;hljs-keyword&quot;&gt;private&lt;/span&gt; FanoutExchange fanoutExchange;

    &lt;span class=&quot;hljs-meta&quot;&gt;@Autowired&lt;/span&gt;
    &lt;span class=&quot;hljs-keyword&quot;&gt;private&lt;/span&gt; RabbitTemplate rabbitTemplate;

    &lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title hljs-function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;hljs-params&quot;&gt;(Object message)&lt;/span&gt; {
        log.info(&lt;span class=&quot;hljs-string&quot;&gt;&quot;开始发送广播: [{}]&quot;&lt;/span&gt;, message.toString());
        rabbitTemplate.convertAndSend(fanoutExchange.getName(), &lt;span class=&quot;hljs-string&quot;&gt;&quot;&quot;&lt;/span&gt;, message);
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;消息消费方，多节点共同消费同一消息，区分是否需要自己来处理：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-java&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;package&lt;/span&gt; me.lawrenceli.websocket.server.configuration;

&lt;span class=&quot;hljs-keyword&quot;&gt;import&lt;/span&gt; lombok.extern.slf4j.Slf4j;
&lt;span class=&quot;hljs-keyword&quot;&gt;import&lt;/span&gt; org.springframework.amqp.rabbit.annotation.RabbitListener;
&lt;span class=&quot;hljs-keyword&quot;&gt;import&lt;/span&gt; org.springframework.stereotype.Component;

&lt;span class=&quot;hljs-meta&quot;&gt;@Slf4j&lt;/span&gt;
&lt;span class=&quot;hljs-meta&quot;&gt;@Component&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hljs-title hljs-class&quot;&gt;FanoutReceiver&lt;/span&gt; {

    &lt;span class=&quot;hljs-meta&quot;&gt;@RabbitListener(queues = &quot;#{queueForWebSocket.name}&quot;)&lt;/span&gt;
    &lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title hljs-function&quot;&gt;singleReceiver&lt;/span&gt;&lt;span class=&quot;hljs-params&quot;&gt;(Object message)&lt;/span&gt; {
        log.info(&lt;span class=&quot;hljs-string&quot;&gt;&quot;队列接收到了消息: [{}]&quot;&lt;/span&gt;, message.toString());
        &lt;span class=&quot;hljs-comment&quot;&gt;// 判断 WebSocket Session 是否在当前节点&lt;/span&gt;
        &lt;span class=&quot;hljs-comment&quot;&gt;// 一般维护在一个 ConcurrentHashMap 静态变量中，这里叫 sessionMap&lt;/span&gt;
        &lt;span class=&quot;hljs-comment&quot;&gt;// message 中有类似 SessionId 的字段&lt;/span&gt;
        &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (sessionMap.contains(sessionId)) {
            log.info(&lt;span class=&quot;hljs-string&quot;&gt;&quot;WebSocket Session 在当前节点&quot;&lt;/span&gt;);
            &lt;span class=&quot;hljs-comment&quot;&gt;// 执行相应的流程&lt;/span&gt;
        } &lt;span class=&quot;hljs-keyword&quot;&gt;else&lt;/span&gt; {
            log.info(&lt;span class=&quot;hljs-string&quot;&gt;&quot;当前节点无此 WebSocket Session&quot;&lt;/span&gt;);
            &lt;span class=&quot;hljs-comment&quot;&gt;// 什么都不用做，直接忽略&lt;/span&gt;
        }
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意，方案 2、方案 3 都没有解决单节点故障问题，因为从本质上讲，没有实现 WebSocket Session
的全局共享。但是方案二能够依赖哈希环的更新，最大程度上地降低迁移连接的代价，客户端会有重试的机制来针对单节点故障问题做容错。如果规模较大，项目复杂，且客户端连接数多，推荐使用一致性哈希的方案二，当然下面也有其他的社区方案。&lt;/p&gt;
&lt;h3 id=&quot;其他案例&quot;&gt;&lt;a href=&quot;#%E5%85%B6%E4%BB%96%E6%A1%88%E4%BE%8B&quot; style=&quot;text-decoration: none; margin-left: -17px;&quot;&gt;&lt;span style=&quot;color: rgba(153, 153, 153, 0.5); font-weight: 430;&quot;&gt;&lt;span style=&quot;padding: 0 4px 0 0&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;其他案例&lt;/h3&gt;
&lt;p&gt;即刻 App 后端基于 Node.js 使用了 &lt;a href=&quot;http://socket.io&quot;&gt;socket.io&lt;/a&gt; 实现多端实时通信，Joway
的&lt;a href=&quot;https://blog.joway.io/posts/socket-io/&quot;&gt;这篇文章&lt;/a&gt;交代的一些细节也涉及了分布式部署的问题。&lt;/p&gt;
&lt;p&gt;socket.io 官网也提供了&lt;a href=&quot;https://socket.io/docs/v3/using-multiple-nodes/index.html&quot;&gt;多节点的使用方法&lt;/a&gt;，不外乎 IP Hash 之类的方案。&lt;/p&gt;
&lt;h3 id=&quot;kubernetes-内的-websocket-集群&quot;&gt;&lt;a href=&quot;#kubernetes-%E5%86%85%E7%9A%84-websocket-%E9%9B%86%E7%BE%A4&quot; style=&quot;text-decoration: none; margin-left: -17px;&quot;&gt;&lt;span style=&quot;color: rgba(153, 153, 153, 0.5); font-weight: 430;&quot;&gt;&lt;span style=&quot;padding: 0 4px 0 0&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;Kubernetes 内的 Websocket 集群&lt;/h3&gt;
&lt;p&gt;如果部署环境基于 Kubernetes 内的 WebSocket
集群，也可以参考&lt;a href=&quot;https://www.qikqiak.com/post/socketio-multiple-nodes-in-kubernetes/&quot;&gt;阳明的这篇文章&lt;/a&gt;来实践 （利用 Service
的 &lt;code&gt;sessionAffinity&lt;/code&gt; 会话亲和确保每次将来自特定客户端的连接传递到同一 Pod：使用 &lt;code&gt;ClientIP&lt;/code&gt; 声明）。&lt;/p&gt;
</summary>
        <author><name>Lawrence</name></author>
      </entry>
      <entry>
        <title>在 Kubernetes 中部署 Prow</title>
        <id>https://lawrenceli.me/blog/prow</id>
        <link href="https://lawrenceli.me/blog/prow"/>
        <updated>2020-11-29T00:00:00.000Z</updated>
        <summary type="html">&lt;p&gt;GitHub CI 🤖️ by Kubernetes&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;prow--praʊ-&quot;&gt;&lt;a href=&quot;#prow--pra%CA%8A-&quot; style=&quot;text-decoration: none; margin-left: -17px;&quot;&gt;&lt;span style=&quot;color: rgba(153, 153, 153, 0.5); font-weight: 430;&quot;&gt;&lt;span style=&quot;padding: 0 4px 0 0&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;strong&gt;Prow&lt;/strong&gt; | praʊ |&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;noun&lt;/em&gt;. &lt;em&gt;the pointed front part of a ship; the bow. 船首.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;了解 Prow 可以参考这篇知乎专栏：&lt;a href=&quot;https://zhuanlan.zhihu.com/p/65545774&quot;&gt;Kubernetes 原生 CI/CD 系统 Prow 简介&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;本文重点参考了以下文章，并做了一些问题的修复：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/kubesphere/prow-tutorial&quot;&gt;Prow Tutorial - KubeSphere‘s Github Repo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.chenshaowen.com/blog/prow-of-devops-tool-chain.html&quot;&gt;DevOps 工具链之 Prow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我们并不一定需要使用 Bazel 来构建配置。笔者使用的 Kubernetes 版本为 2020 年发布的 1.19.3。一切的前提是拥有一个 Kubernetes 集群，最好在里面也部署一个 Ingress Controller
用来暴露我们的 WebHook 以及 Prow Deck 前端 Dashboard 服务。&lt;/p&gt;
&lt;p&gt;在 K8s 集群中部署 Prow 的具体步骤如下：&lt;/p&gt;
&lt;p&gt;首先注册一个 GitHub 账号专门用于作为机器人账号，用此账号前往 &lt;a href=&quot;https://github.com/settings/tokens&quot;&gt;GitHub Developer 页面&lt;/a&gt;创建一个 Token，权限参考上文，并邀请此账号作为
Collaborators 管理仓库。&lt;/p&gt;
&lt;p&gt;随机生成 H-MAC 作为 WebHook 密码安全验证。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-yaml&quot;&gt;&lt;span class=&quot;hljs-string&quot;&gt;openssl&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;rand&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;-hex&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;20&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;hmac&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;将他们保存在 &lt;code&gt;/etc/github/oauth&lt;/code&gt; 和 &lt;code&gt;/etc/webhook/hmac&lt;/code&gt; 文件下。路径不能错误，因为此处和下面的 Prow manifest 字段匹配。&lt;/p&gt;
&lt;p&gt;纳入集群对象：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-bash&quot;&gt;kubectl create secret generic hmac-token --from-file=hmac=/etc/webhook/hmac
kubectl create secret generic oauth-token --from-file=oauth=/etc/github/oauth&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;将&lt;a href=&quot;https://gist.github.com/Lonor/79ba7d87cb8a3a44916162d4dece0c66&quot;&gt;以下 Prow 的 manifest&lt;/a&gt; 做简单修改，注意 Ingress 的域名，namespace 为
default：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-yaml&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;# This file contains Kubernetes YAML files for the most important prow&lt;/span&gt;
&lt;span class=&quot;hljs-comment&quot;&gt;# components. Don&apos;t edit resources in this file. Instead, pull them out into&lt;/span&gt;
&lt;span class=&quot;hljs-comment&quot;&gt;# their own files.&lt;/span&gt;
&lt;span class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;ConfigMap&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;namespace:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;default&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;plugins&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;data:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;plugins.yaml:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;ConfigMap&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;namespace:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;default&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;config&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;data:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;config.yaml:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;|
    prowjob_namespace: default
    pod_namespace: test-pods
    periodics:
    - interval: 10m
      agent: kubernetes
      name: echo-test
      spec:
        containers:
        - image: alpine
          command: [&quot;/bin/date&quot;]
&lt;/span&gt;&lt;span class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;apiextensions.k8s.io/v1beta1&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;CustomResourceDefinition&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;prowjobs.prow.k8s.io&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;spec:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;group:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;prow.k8s.io&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;version:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;v1&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;names:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;ProwJob&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;singular:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;prowjob&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;plural:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;prowjobs&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;scope:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Namespaced&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;validation:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;openAPIV3Schema:&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;properties:&lt;/span&gt;
        &lt;span class=&quot;hljs-attr&quot;&gt;spec:&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;properties:&lt;/span&gt;
            &lt;span class=&quot;hljs-attr&quot;&gt;max_concurrency:&lt;/span&gt;
              &lt;span class=&quot;hljs-attr&quot;&gt;type:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;integer&lt;/span&gt;
              &lt;span class=&quot;hljs-attr&quot;&gt;minimum:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;
            &lt;span class=&quot;hljs-attr&quot;&gt;type:&lt;/span&gt;
              &lt;span class=&quot;hljs-attr&quot;&gt;type:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;string&lt;/span&gt;
              &lt;span class=&quot;hljs-attr&quot;&gt;enum:&lt;/span&gt;
                &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;presubmit&quot;&lt;/span&gt;
                &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;postsubmit&quot;&lt;/span&gt;
                &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;periodic&quot;&lt;/span&gt;
                &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;batch&quot;&lt;/span&gt;
        &lt;span class=&quot;hljs-attr&quot;&gt;status:&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;properties:&lt;/span&gt;
            &lt;span class=&quot;hljs-attr&quot;&gt;state:&lt;/span&gt;
              &lt;span class=&quot;hljs-attr&quot;&gt;type:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;string&lt;/span&gt;
              &lt;span class=&quot;hljs-attr&quot;&gt;enum:&lt;/span&gt;
                &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;triggered&quot;&lt;/span&gt;
                &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;pending&quot;&lt;/span&gt;
                &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;success&quot;&lt;/span&gt;
                &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;failure&quot;&lt;/span&gt;
                &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;aborted&quot;&lt;/span&gt;
                &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;error&quot;&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;anyOf:&lt;/span&gt;
            &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;not:&lt;/span&gt;
                &lt;span class=&quot;hljs-attr&quot;&gt;properties:&lt;/span&gt;
                  &lt;span class=&quot;hljs-attr&quot;&gt;state:&lt;/span&gt;
                    &lt;span class=&quot;hljs-attr&quot;&gt;type:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;string&lt;/span&gt;
                    &lt;span class=&quot;hljs-attr&quot;&gt;enum:&lt;/span&gt;
                      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;success&quot;&lt;/span&gt;
                      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;failure&quot;&lt;/span&gt;
                      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;error&quot;&lt;/span&gt;
                      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;aborted&quot;&lt;/span&gt;
            &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;required:&lt;/span&gt;
                &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;completionTime&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;additionalPrinterColumns:&lt;/span&gt;
    &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Job&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;type:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;string&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;description:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;The&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;job&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;being&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;run.&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;JSONPath:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;.spec.job&lt;/span&gt;
    &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;BuildId&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;type:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;string&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;description:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;The&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;ID&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;job&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;being&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;run.&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;JSONPath:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;.status.build_id&lt;/span&gt;
    &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Type&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;type:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;string&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;description:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;The&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;job&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;being&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;run.&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;JSONPath:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;.spec.type&lt;/span&gt;
    &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Org&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;type:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;string&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;description:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;The&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;org&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;which&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;job&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;running.&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;JSONPath:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;.spec.refs.org&lt;/span&gt;
    &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Repo&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;type:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;string&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;description:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;The&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;repo&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;which&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;job&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;running.&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;JSONPath:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;.spec.refs.repo&lt;/span&gt;
    &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Pulls&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;type:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;string&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;description:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;The&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;pulls&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;which&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;job&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;running.&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;JSONPath:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;.spec.refs.pulls[*].number&quot;&lt;/span&gt;
    &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;StartTime&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;type:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;date&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;description:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;When&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;job&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;started&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;running.&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;JSONPath:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;.status.startTime&lt;/span&gt;
    &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;CompletionTime&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;type:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;date&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;description:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;When&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;job&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;finished&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;running.&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;JSONPath:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;.status.completionTime&lt;/span&gt;
    &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;State&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;description:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;The&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;job.&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;type:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;string&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;JSONPath:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;.status.state&lt;/span&gt;
&lt;span class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;apps/v1&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Deployment&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;namespace:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;default&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;hook&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;labels:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;app:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;hook&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;spec:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;replicas:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;2&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;strategy:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;type:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;RollingUpdate&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;rollingUpdate:&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;maxSurge:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;maxUnavailable:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;selector:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;matchLabels:&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;app:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;hook&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;template:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;labels:&lt;/span&gt;
        &lt;span class=&quot;hljs-attr&quot;&gt;app:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;hook&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;spec:&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;serviceAccountName:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;hook&quot;&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;terminationGracePeriodSeconds:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;180&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;containers:&lt;/span&gt;
        &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;hook&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;image:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;magicsong/hook:v20190711-664ef040d&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;args:&lt;/span&gt;
            &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;--dry-run=false&lt;/span&gt;
            &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;--config-path=/etc/config/config.yaml&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;ports:&lt;/span&gt;
            &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;http&lt;/span&gt;
              &lt;span class=&quot;hljs-attr&quot;&gt;containerPort:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;8888&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;volumeMounts:&lt;/span&gt;
            &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;hmac&lt;/span&gt;
              &lt;span class=&quot;hljs-attr&quot;&gt;mountPath:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;/etc/webhook&lt;/span&gt;
              &lt;span class=&quot;hljs-attr&quot;&gt;readOnly:&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;
            &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;oauth&lt;/span&gt;
              &lt;span class=&quot;hljs-attr&quot;&gt;mountPath:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;/etc/github&lt;/span&gt;
              &lt;span class=&quot;hljs-attr&quot;&gt;readOnly:&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;
            &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;config&lt;/span&gt;
              &lt;span class=&quot;hljs-attr&quot;&gt;mountPath:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;/etc/config&lt;/span&gt;
              &lt;span class=&quot;hljs-attr&quot;&gt;readOnly:&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;
            &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;plugins&lt;/span&gt;
              &lt;span class=&quot;hljs-attr&quot;&gt;mountPath:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;/etc/plugins&lt;/span&gt;
              &lt;span class=&quot;hljs-attr&quot;&gt;readOnly:&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;
          &lt;span class=&quot;hljs-comment&quot;&gt;# livenessProbe:&lt;/span&gt;
          &lt;span class=&quot;hljs-comment&quot;&gt;#   httpGet:&lt;/span&gt;
          &lt;span class=&quot;hljs-comment&quot;&gt;#     path: /healthz&lt;/span&gt;
          &lt;span class=&quot;hljs-comment&quot;&gt;#     port: 8081&lt;/span&gt;
          &lt;span class=&quot;hljs-comment&quot;&gt;#   initialDelaySeconds: 3&lt;/span&gt;
          &lt;span class=&quot;hljs-comment&quot;&gt;#   periodSeconds: 3&lt;/span&gt;
          &lt;span class=&quot;hljs-comment&quot;&gt;# readinessProbe:&lt;/span&gt;
          &lt;span class=&quot;hljs-comment&quot;&gt;#   httpGet:&lt;/span&gt;
          &lt;span class=&quot;hljs-comment&quot;&gt;#     path: /healthz/ready&lt;/span&gt;
          &lt;span class=&quot;hljs-comment&quot;&gt;#     port: 8081&lt;/span&gt;
          &lt;span class=&quot;hljs-comment&quot;&gt;#   initialDelaySeconds: 10&lt;/span&gt;
          &lt;span class=&quot;hljs-comment&quot;&gt;#   periodSeconds: 3&lt;/span&gt;
          &lt;span class=&quot;hljs-comment&quot;&gt;#   timeoutSeconds: 600&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;volumes:&lt;/span&gt;
        &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;hmac&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;secret:&lt;/span&gt;
            &lt;span class=&quot;hljs-attr&quot;&gt;secretName:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;hmac-token&lt;/span&gt;
        &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;oauth&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;secret:&lt;/span&gt;
            &lt;span class=&quot;hljs-attr&quot;&gt;secretName:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;oauth-token&lt;/span&gt;
        &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;config&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;configMap:&lt;/span&gt;
            &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;config&lt;/span&gt;
        &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;plugins&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;configMap:&lt;/span&gt;
            &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;plugins&lt;/span&gt;
&lt;span class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Service&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;namespace:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;default&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;hook&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;spec:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;selector:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;app:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;hook&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;ports:&lt;/span&gt;
    &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;port:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;8888&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;type:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;NodePort&lt;/span&gt;
&lt;span class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;apps/v1&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Deployment&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;namespace:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;default&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;plank&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;labels:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;app:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;plank&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;spec:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;selector:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;matchLabels:&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;app:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;plank&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;replicas:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;hljs-comment&quot;&gt;# Do not scale up.&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;strategy:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;type:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Recreate&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;template:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;labels:&lt;/span&gt;
        &lt;span class=&quot;hljs-attr&quot;&gt;app:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;plank&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;spec:&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;serviceAccountName:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;plank&quot;&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;containers:&lt;/span&gt;
        &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;plank&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;image:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;magicsong/plank:v20190711-664ef040d&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;args:&lt;/span&gt;
            &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;--dry-run=false&lt;/span&gt;
            &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;--config-path=/etc/config/config.yaml&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;volumeMounts:&lt;/span&gt;
            &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;oauth&lt;/span&gt;
              &lt;span class=&quot;hljs-attr&quot;&gt;mountPath:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;/etc/github&lt;/span&gt;
              &lt;span class=&quot;hljs-attr&quot;&gt;readOnly:&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;
            &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;config&lt;/span&gt;
              &lt;span class=&quot;hljs-attr&quot;&gt;mountPath:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;/etc/config&lt;/span&gt;
              &lt;span class=&quot;hljs-attr&quot;&gt;readOnly:&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;volumes:&lt;/span&gt;
        &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;oauth&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;secret:&lt;/span&gt;
            &lt;span class=&quot;hljs-attr&quot;&gt;secretName:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;oauth-token&lt;/span&gt;
        &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;config&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;configMap:&lt;/span&gt;
            &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;config&lt;/span&gt;
&lt;span class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;apps/v1&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Deployment&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;namespace:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;default&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;sinker&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;labels:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;app:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;sinker&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;spec:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;selector:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;matchLabels:&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;app:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;sinker&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;replicas:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;template:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;labels:&lt;/span&gt;
        &lt;span class=&quot;hljs-attr&quot;&gt;app:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;sinker&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;spec:&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;serviceAccountName:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;sinker&quot;&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;containers:&lt;/span&gt;
        &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;sinker&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;image:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;magicsong/sinker:v20190711-664ef040d&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;args:&lt;/span&gt;
            &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;--config-path=/etc/config/config.yaml&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;volumeMounts:&lt;/span&gt;
            &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;config&lt;/span&gt;
              &lt;span class=&quot;hljs-attr&quot;&gt;mountPath:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;/etc/config&lt;/span&gt;
              &lt;span class=&quot;hljs-attr&quot;&gt;readOnly:&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;volumes:&lt;/span&gt;
        &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;config&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;configMap:&lt;/span&gt;
            &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;config&lt;/span&gt;
&lt;span class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;apps/v1&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Deployment&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;namespace:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;default&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;deck&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;labels:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;app:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;deck&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;spec:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;replicas:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;2&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;strategy:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;type:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;RollingUpdate&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;rollingUpdate:&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;maxSurge:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;maxUnavailable:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;selector:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;matchLabels:&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;app:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;deck&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;template:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;labels:&lt;/span&gt;
        &lt;span class=&quot;hljs-attr&quot;&gt;app:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;deck&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;spec:&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;serviceAccountName:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;deck&quot;&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;terminationGracePeriodSeconds:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;30&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;containers:&lt;/span&gt;
        &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;deck&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;image:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;magicsong/deck:v20190711-664ef040d&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;args:&lt;/span&gt;
            &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;--config-path=/etc/config/config.yaml&lt;/span&gt;
            &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;--tide-url=http://tide/&lt;/span&gt;
            &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;--hook-url=http://hook:8888/plugin-help&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;ports:&lt;/span&gt;
            &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;http&lt;/span&gt;
              &lt;span class=&quot;hljs-attr&quot;&gt;containerPort:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;8080&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;volumeMounts:&lt;/span&gt;
            &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;config&lt;/span&gt;
              &lt;span class=&quot;hljs-attr&quot;&gt;mountPath:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;/etc/config&lt;/span&gt;
              &lt;span class=&quot;hljs-attr&quot;&gt;readOnly:&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;livenessProbe:&lt;/span&gt;
            &lt;span class=&quot;hljs-attr&quot;&gt;httpGet:&lt;/span&gt;
              &lt;span class=&quot;hljs-attr&quot;&gt;path:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;/healthz&lt;/span&gt;
              &lt;span class=&quot;hljs-attr&quot;&gt;port:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;8081&lt;/span&gt;
            &lt;span class=&quot;hljs-attr&quot;&gt;initialDelaySeconds:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;3&lt;/span&gt;
            &lt;span class=&quot;hljs-attr&quot;&gt;periodSeconds:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;3&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;readinessProbe:&lt;/span&gt;
            &lt;span class=&quot;hljs-attr&quot;&gt;httpGet:&lt;/span&gt;
              &lt;span class=&quot;hljs-attr&quot;&gt;path:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;/healthz/ready&lt;/span&gt;
              &lt;span class=&quot;hljs-attr&quot;&gt;port:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;8081&lt;/span&gt;
            &lt;span class=&quot;hljs-attr&quot;&gt;initialDelaySeconds:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;10&lt;/span&gt;
            &lt;span class=&quot;hljs-attr&quot;&gt;periodSeconds:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;3&lt;/span&gt;
            &lt;span class=&quot;hljs-attr&quot;&gt;timeoutSeconds:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;600&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;volumes:&lt;/span&gt;
        &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;config&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;configMap:&lt;/span&gt;
            &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;config&lt;/span&gt;
&lt;span class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Service&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;namespace:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;default&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;deck&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;spec:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;selector:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;app:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;deck&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;ports:&lt;/span&gt;
    &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;port:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;80&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;targetPort:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;8080&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;type:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;NodePort&lt;/span&gt;
&lt;span class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;apps/v1&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Deployment&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;namespace:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;default&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;horologium&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;labels:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;app:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;horologium&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;spec:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;replicas:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;hljs-comment&quot;&gt;# Do not scale up.&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;strategy:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;type:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Recreate&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;selector:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;matchLabels:&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;app:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;horologium&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;template:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;labels:&lt;/span&gt;
        &lt;span class=&quot;hljs-attr&quot;&gt;app:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;horologium&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;spec:&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;serviceAccountName:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;horologium&quot;&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;terminationGracePeriodSeconds:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;30&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;containers:&lt;/span&gt;
        &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;horologium&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;image:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;magicsong/horologium:v20190711-664ef040d&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;args:&lt;/span&gt;
            &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;--config-path=/etc/config/config.yaml&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;volumeMounts:&lt;/span&gt;
            &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;config&lt;/span&gt;
              &lt;span class=&quot;hljs-attr&quot;&gt;mountPath:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;/etc/config&lt;/span&gt;
              &lt;span class=&quot;hljs-attr&quot;&gt;readOnly:&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;volumes:&lt;/span&gt;
        &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;config&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;configMap:&lt;/span&gt;
            &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;config&lt;/span&gt;
&lt;span class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;apps/v1&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Deployment&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;namespace:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;default&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;tide&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;labels:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;app:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;tide&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;spec:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;replicas:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;hljs-comment&quot;&gt;# Do not scale up.&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;strategy:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;type:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Recreate&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;selector:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;matchLabels:&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;app:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;tide&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;template:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;labels:&lt;/span&gt;
        &lt;span class=&quot;hljs-attr&quot;&gt;app:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;tide&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;spec:&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;serviceAccountName:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;tide&quot;&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;containers:&lt;/span&gt;
        &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;tide&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;image:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;magicsong/tide:v20190711-664ef040d&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;args:&lt;/span&gt;
            &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;--dry-run=false&lt;/span&gt;
            &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;--config-path=/etc/config/config.yaml&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;ports:&lt;/span&gt;
            &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;http&lt;/span&gt;
              &lt;span class=&quot;hljs-attr&quot;&gt;containerPort:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;8888&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;volumeMounts:&lt;/span&gt;
            &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;oauth&lt;/span&gt;
              &lt;span class=&quot;hljs-attr&quot;&gt;mountPath:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;/etc/github&lt;/span&gt;
              &lt;span class=&quot;hljs-attr&quot;&gt;readOnly:&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;
            &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;config&lt;/span&gt;
              &lt;span class=&quot;hljs-attr&quot;&gt;mountPath:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;/etc/config&lt;/span&gt;
              &lt;span class=&quot;hljs-attr&quot;&gt;readOnly:&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;volumes:&lt;/span&gt;
        &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;oauth&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;secret:&lt;/span&gt;
            &lt;span class=&quot;hljs-attr&quot;&gt;secretName:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;oauth-token&lt;/span&gt;
        &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;config&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;configMap:&lt;/span&gt;
            &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;config&lt;/span&gt;
&lt;span class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Service&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;namespace:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;default&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;tide&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;spec:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;selector:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;app:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;tide&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;ports:&lt;/span&gt;
    &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;port:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;80&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;targetPort:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;8888&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;type:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;NodePort&lt;/span&gt;
&lt;span class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;extensions/v1beta1&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Ingress&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;namespace:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;default&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;ing&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;annotations:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;nginx.ingress.kubernetes.io/ssl-redirect:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;false&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;spec:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;rules:&lt;/span&gt;
    &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;host:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;prow.your-domain.com&lt;/span&gt; &lt;span class=&quot;hljs-comment&quot;&gt;# 这里换成你的域名&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;http:&lt;/span&gt;
        &lt;span class=&quot;hljs-attr&quot;&gt;paths:&lt;/span&gt;
          &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;path:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;hljs-comment&quot;&gt;# Correct for GKE, need / on many other distros&lt;/span&gt;
            &lt;span class=&quot;hljs-attr&quot;&gt;backend:&lt;/span&gt;
              &lt;span class=&quot;hljs-attr&quot;&gt;serviceName:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;deck&lt;/span&gt;
              &lt;span class=&quot;hljs-attr&quot;&gt;servicePort:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;80&lt;/span&gt;
          &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;path:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;/hook&lt;/span&gt;
            &lt;span class=&quot;hljs-attr&quot;&gt;backend:&lt;/span&gt;
              &lt;span class=&quot;hljs-attr&quot;&gt;serviceName:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;hook&lt;/span&gt;
              &lt;span class=&quot;hljs-attr&quot;&gt;servicePort:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;8888&lt;/span&gt;
&lt;span class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;apps/v1&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Deployment&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;statusreconciler&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;namespace:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;default&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;labels:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;app:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;statusreconciler&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;spec:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;replicas:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;selector:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;matchLabels:&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;app:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;statusreconciler&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;template:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;labels:&lt;/span&gt;
        &lt;span class=&quot;hljs-attr&quot;&gt;app:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;statusreconciler&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;spec:&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;serviceAccountName:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;statusreconciler&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;terminationGracePeriodSeconds:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;180&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;containers:&lt;/span&gt;
        &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;statusreconciler&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;image:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;magicsong/status-reconciler:v20190711-664ef040d&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;args:&lt;/span&gt;
            &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;--dry-run=false&lt;/span&gt;
            &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;--continue-on-error=true&lt;/span&gt;
            &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;--plugin-config=/etc/plugins/plugins.yaml&lt;/span&gt;
            &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;--config-path=/etc/config/config.yaml&lt;/span&gt;
            &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;--github-token-path=/etc/github/oauth&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;volumeMounts:&lt;/span&gt;
            &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;oauth&lt;/span&gt;
              &lt;span class=&quot;hljs-attr&quot;&gt;mountPath:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;/etc/github&lt;/span&gt;
              &lt;span class=&quot;hljs-attr&quot;&gt;readOnly:&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;
            &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;config&lt;/span&gt;
              &lt;span class=&quot;hljs-attr&quot;&gt;mountPath:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;/etc/config&lt;/span&gt;
              &lt;span class=&quot;hljs-attr&quot;&gt;readOnly:&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;
            &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;plugins&lt;/span&gt;
              &lt;span class=&quot;hljs-attr&quot;&gt;mountPath:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;/etc/plugins&lt;/span&gt;
              &lt;span class=&quot;hljs-attr&quot;&gt;readOnly:&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;volumes:&lt;/span&gt;
        &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;oauth&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;secret:&lt;/span&gt;
            &lt;span class=&quot;hljs-attr&quot;&gt;secretName:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;oauth-token&lt;/span&gt;
        &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;config&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;configMap:&lt;/span&gt;
            &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;config&lt;/span&gt;
        &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;plugins&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;configMap:&lt;/span&gt;
            &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;plugins&lt;/span&gt;
&lt;span class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Namespace&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;test-pods&lt;/span&gt;
&lt;span class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;ServiceAccount&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;namespace:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;default&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;deck&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;RoleBinding&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;rbac.authorization.k8s.io/v1beta1&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;namespace:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;default&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;deck&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;roleRef:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;apiGroup:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;rbac.authorization.k8s.io&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Role&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;deck&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;subjects:&lt;/span&gt;
  &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;ServiceAccount&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;deck&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;RoleBinding&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;rbac.authorization.k8s.io/v1beta1&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;namespace:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;test-pods&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;deck&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;roleRef:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;apiGroup:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;rbac.authorization.k8s.io&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Role&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;deck&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;subjects:&lt;/span&gt;
  &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;ServiceAccount&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;deck&quot;&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;namespace:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;default&lt;/span&gt;
&lt;span class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Role&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;rbac.authorization.k8s.io/v1beta1&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;namespace:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;default&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;deck&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;rules:&lt;/span&gt;
  &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;apiGroups:&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;prow.k8s.io&quot;&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;resources:&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;prowjobs&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;verbs:&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;get&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;list&lt;/span&gt;
      &lt;span class=&quot;hljs-comment&quot;&gt;# Required when deck runs with `--rerun-creates-job=true`&lt;/span&gt;
      &lt;span class=&quot;hljs-comment&quot;&gt;# **Warning:** Only use this for non-public deck instances, this allows&lt;/span&gt;
      &lt;span class=&quot;hljs-comment&quot;&gt;# anyone with access to your Deck instance to create new Prowjobs&lt;/span&gt;
      &lt;span class=&quot;hljs-comment&quot;&gt;# - create&lt;/span&gt;
&lt;span class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Role&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;rbac.authorization.k8s.io/v1beta1&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;namespace:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;test-pods&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;deck&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;rules:&lt;/span&gt;
  &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;apiGroups:&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;resources:&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;pods/log&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;verbs:&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;get&lt;/span&gt;
&lt;span class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;ServiceAccount&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;namespace:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;default&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;horologium&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Role&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;rbac.authorization.k8s.io/v1beta1&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;namespace:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;default&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;horologium&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;rules:&lt;/span&gt;
  &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;apiGroups:&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;prow.k8s.io&quot;&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;resources:&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;prowjobs&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;verbs:&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;create&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;list&lt;/span&gt;
&lt;span class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;RoleBinding&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;rbac.authorization.k8s.io/v1beta1&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;namespace:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;default&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;horologium&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;roleRef:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;apiGroup:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;rbac.authorization.k8s.io&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Role&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;horologium&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;subjects:&lt;/span&gt;
  &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;ServiceAccount&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;horologium&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;ServiceAccount&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;namespace:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;default&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;plank&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Role&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;rbac.authorization.k8s.io/v1beta1&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;namespace:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;default&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;plank&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;rules:&lt;/span&gt;
  &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;apiGroups:&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;prow.k8s.io&quot;&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;resources:&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;prowjobs&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;verbs:&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;get&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;create&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;list&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;update&lt;/span&gt;
&lt;span class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Role&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;rbac.authorization.k8s.io/v1beta1&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;namespace:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;test-pods&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;plank&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;rules:&lt;/span&gt;
  &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;apiGroups:&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;resources:&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;pods&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;verbs:&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;create&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;delete&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;list&lt;/span&gt;
&lt;span class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;RoleBinding&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;rbac.authorization.k8s.io/v1beta1&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;namespace:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;default&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;plank&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;roleRef:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;apiGroup:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;rbac.authorization.k8s.io&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Role&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;plank&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;subjects:&lt;/span&gt;
  &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;ServiceAccount&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;plank&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;RoleBinding&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;rbac.authorization.k8s.io/v1beta1&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;namespace:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;test-pods&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;plank&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;roleRef:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;apiGroup:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;rbac.authorization.k8s.io&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Role&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;plank&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;subjects:&lt;/span&gt;
  &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;ServiceAccount&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;plank&quot;&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;namespace:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;default&lt;/span&gt;
&lt;span class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;ServiceAccount&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;namespace:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;default&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;sinker&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Role&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;rbac.authorization.k8s.io/v1beta1&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;namespace:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;default&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;sinker&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;rules:&lt;/span&gt;
  &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;apiGroups:&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;prow.k8s.io&quot;&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;resources:&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;prowjobs&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;verbs:&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;delete&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;list&lt;/span&gt;
&lt;span class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Role&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;rbac.authorization.k8s.io/v1beta1&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;namespace:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;test-pods&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;sinker&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;rules:&lt;/span&gt;
  &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;apiGroups:&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;resources:&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;pods&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;verbs:&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;delete&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;list&lt;/span&gt;
&lt;span class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;RoleBinding&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;rbac.authorization.k8s.io/v1beta1&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;namespace:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;default&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;sinker&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;roleRef:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;apiGroup:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;rbac.authorization.k8s.io&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Role&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;sinker&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;subjects:&lt;/span&gt;
  &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;ServiceAccount&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;sinker&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;RoleBinding&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;rbac.authorization.k8s.io/v1beta1&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;namespace:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;test-pods&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;sinker&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;roleRef:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;apiGroup:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;rbac.authorization.k8s.io&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Role&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;sinker&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;subjects:&lt;/span&gt;
  &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;ServiceAccount&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;sinker&quot;&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;namespace:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;default&lt;/span&gt;
&lt;span class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;ServiceAccount&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;namespace:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;default&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;hook&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Role&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;rbac.authorization.k8s.io/v1beta1&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;namespace:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;default&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;hook&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;rules:&lt;/span&gt;
  &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;apiGroups:&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;prow.k8s.io&quot;&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;resources:&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;prowjobs&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;verbs:&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;create&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;get&lt;/span&gt;
  &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;apiGroups:&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;resources:&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;configmaps&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;verbs:&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;create&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;get&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;update&lt;/span&gt;
&lt;span class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;RoleBinding&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;rbac.authorization.k8s.io/v1beta1&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;namespace:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;default&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;hook&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;roleRef:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;apiGroup:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;rbac.authorization.k8s.io&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Role&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;hook&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;subjects:&lt;/span&gt;
  &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;ServiceAccount&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;hook&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;ServiceAccount&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;namespace:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;default&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;tide&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Role&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;rbac.authorization.k8s.io/v1beta1&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;namespace:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;default&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;tide&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;rules:&lt;/span&gt;
  &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;apiGroups:&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;prow.k8s.io&quot;&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;resources:&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;prowjobs&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;verbs:&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;create&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;list&lt;/span&gt;
&lt;span class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;RoleBinding&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;rbac.authorization.k8s.io/v1beta1&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;namespace:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;default&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;tide&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;roleRef:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;apiGroup:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;rbac.authorization.k8s.io&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Role&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;tide&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;subjects:&lt;/span&gt;
  &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;ServiceAccount&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;tide&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;ServiceAccount&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;namespace:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;default&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;statusreconciler&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Role&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;rbac.authorization.k8s.io/v1beta1&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;namespace:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;default&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;statusreconciler&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;rules:&lt;/span&gt;
  &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;apiGroups:&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;prow.k8s.io&quot;&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;resources:&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;prowjobs&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;verbs:&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;create&lt;/span&gt;
&lt;span class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;RoleBinding&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;rbac.authorization.k8s.io/v1beta1&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;namespace:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;default&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;statusreconciler&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;roleRef:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;apiGroup:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;rbac.authorization.k8s.io&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Role&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;statusreconciler&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;subjects:&lt;/span&gt;
  &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;ServiceAccount&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;statusreconciler&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;apply 上述 manifest。过程可能持续几分钟。上面用到的镜像都来自 DockerHub，国内可以直接访问。出现问题尝试打印 Pod 日志或者 describe，用好 Google 和 GitHub 能解决几乎 99% 的技术问题。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-bash&quot;&gt;curl -L https://git.io/JIJ4c -o prow.yaml
vi prow.yaml
kubectl apply -f prow.yaml&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;deck--dɛk-&quot;&gt;&lt;a href=&quot;#deck--d%C9%9Bk-&quot; style=&quot;text-decoration: none; margin-left: -17px;&quot;&gt;&lt;span style=&quot;color: rgba(153, 153, 153, 0.5); font-weight: 430;&quot;&gt;&lt;span style=&quot;padding: 0 4px 0 0&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;strong&gt;Deck&lt;/strong&gt; | dɛk |&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;noun.&lt;/em&gt; 甲板&lt;/p&gt;
&lt;p&gt;等 Pod 全都 Running 后，项目就成功了一半！现在直接访问 Prow 的 Ingress 地址，可以看到 Prow 的 Deck 前端
Dashboard，类似于：&lt;a href=&quot;https://prow.k8s.io/&quot;&gt;https://prow.k8s.io/&lt;/a&gt; 。&lt;/p&gt;
&lt;p&gt;这时，可以在 GitHub 的项目设置里去添加 WebHook:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Payload URL 是你的 Prow Ingress 域名后面直接加 &lt;code&gt;/hook&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Content Type 选择 &lt;code&gt;application/json&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Secrets 是上面随机生成的 HMAC&lt;/li&gt;
&lt;li&gt;勾选 &lt;code&gt;Send me everything&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;激活并保存。&lt;/p&gt;
&lt;p&gt;接着在集群机器上创建一个 &lt;code&gt;plugins.yaml&lt;/code&gt;  文件和 &lt;code&gt;config.yaml&lt;/code&gt; 文件来配置 Prow。&lt;/p&gt;
&lt;p&gt;插件的声明：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-yaml&quot;&gt;&lt;span class=&quot;hljs-attr&quot;&gt;plugins:&lt;/span&gt;
  &lt;span class=&quot;hljs-string&quot;&gt;GitHub账号/项目名:&lt;/span&gt;
    &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;size&lt;/span&gt;
    &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;cat&lt;/span&gt;
    &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;dog&lt;/span&gt;
    &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;pony&lt;/span&gt;
    &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;shrug&lt;/span&gt;
    &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;yuks&lt;/span&gt;
    &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;label&lt;/span&gt;
    &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;trigger&lt;/span&gt;
    &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;approve&lt;/span&gt;
    &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;lgtm&lt;/span&gt;
    &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;welcome&lt;/span&gt;
    &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;help&lt;/span&gt;
    &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;assign&lt;/span&gt;
    &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;branchcleaner&lt;/span&gt;
    &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;lifecycle&lt;/span&gt;
    &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;wip&lt;/span&gt;
    &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;heart&lt;/span&gt;
    &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;milestone&lt;/span&gt;
    &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;milestonestatus&lt;/span&gt;
    &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;project&lt;/span&gt;
    &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;stage&lt;/span&gt;
    &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;skip&lt;/span&gt;
    &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;blockade&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tide 的配置：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-yaml&quot;&gt;&lt;span class=&quot;hljs-attr&quot;&gt;prowjob_namespace:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;default&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;tide:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;merge_method:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;kubeflow/community:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;squash&lt;/span&gt;

  &lt;span class=&quot;hljs-attr&quot;&gt;target_url:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;http://你的域名/tide&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;queries:&lt;/span&gt;
    &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;repos:&lt;/span&gt;
        &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;GitHub账号/项目名&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;labels:&lt;/span&gt;
        &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;lgtm&lt;/span&gt;
        &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;approved&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;missingLabels:&lt;/span&gt;
        &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;do-not-merge&lt;/span&gt;
        &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;do-not-merge/hold&lt;/span&gt;
        &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;do-not-merge/work-in-progress&lt;/span&gt;
        &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;needs-ok-to-test&lt;/span&gt;
        &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;needs-rebase&lt;/span&gt;

  &lt;span class=&quot;hljs-attr&quot;&gt;context_options:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;from-branch-protection:&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;skip-unknown-contexts:&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;orgs:&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;org:&lt;/span&gt;
        &lt;span class=&quot;hljs-attr&quot;&gt;required-contexts:&lt;/span&gt;
          &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;check-required-for-all-repos&quot;&lt;/span&gt;
        &lt;span class=&quot;hljs-attr&quot;&gt;repos:&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;repo:&lt;/span&gt;
            &lt;span class=&quot;hljs-attr&quot;&gt;required-contexts:&lt;/span&gt;
              &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;check-required-for-all-branches&quot;&lt;/span&gt;
            &lt;span class=&quot;hljs-attr&quot;&gt;branches:&lt;/span&gt;
              &lt;span class=&quot;hljs-attr&quot;&gt;branch:&lt;/span&gt;
                &lt;span class=&quot;hljs-attr&quot;&gt;from-branch-protection:&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;false&lt;/span&gt;
                &lt;span class=&quot;hljs-attr&quot;&gt;required-contexts:&lt;/span&gt;
                  &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;required_test&quot;&lt;/span&gt;
                &lt;span class=&quot;hljs-attr&quot;&gt;optional-contexts:&lt;/span&gt;
                  &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;optional_test&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;应用更改：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-bash&quot;&gt;kubectl create configmap plugins \
--from-file=plugins.yaml=/root/cluster/prow/plugins.yaml --dry-run -o yaml \
| kubectl replace configmap plugins -f -

kubectl create configmap config \
--from-file=config.yaml=/root/cluster/prow/config.yaml --dry-run -o yaml \
| kubectl replace configmap config -f -&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可能需要两分钟生效。等一切就绪后，就可以创建 Pull Request 来测试了。效果可以参考 &lt;a href=&quot;http://github.com/kubernetes/kubernetes&quot;&gt;Kubernetes 的官方仓库&lt;/a&gt;或&lt;a href=&quot;https://github.com/Lonor/Tranclite/pull/5&quot;&gt;我的这个 node
命令行翻译项目&lt;/a&gt;。&lt;/p&gt;
&lt;h3 id=&quot;使用-prow&quot;&gt;&lt;a href=&quot;#%E4%BD%BF%E7%94%A8-prow&quot; style=&quot;text-decoration: none; margin-left: -17px;&quot;&gt;&lt;span style=&quot;color: rgba(153, 153, 153, 0.5); font-weight: 430;&quot;&gt;&lt;span style=&quot;padding: 0 4px 0 0&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;使用 Prow&lt;/h3&gt;
&lt;p&gt;在 GitHub 的 Issue 或 Pull Request 页面下你可以用一个简单的 &lt;code&gt;/meow&lt;/code&gt; 命令让机器人随机发送一张 🐱 的图片。无需任何刷新，GitHub 页面会自动地更新此页面（后来发现是基于 WebSocket）：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/prow/1.png&quot; alt=&quot;cat-in-issue&quot;&gt;&lt;/p&gt;
&lt;p&gt;我们注意到， Kubernetes 维护人员会使用 &lt;code&gt;/test&lt;/code&gt; 命令来让集群内的 Pod 执行测试并将结果告诉 GitHub，这就是 ProwJob 实现的了。&lt;/p&gt;
&lt;p&gt;ProwJob 主要用于 PR 流程的 CI 测试，而国内网络原因无法使用 Google 的 GCS 服务，ProwJob 中 plank
依赖的 &lt;a href=&quot;https://github.com/kubernetes/test-infra/blob/master/prow/jobs.md#pod-utilities&quot;&gt;Pod Utilities&lt;/a&gt;，其中定义 GCS Bucket
用于存放构建产物、日志等内容，因此，我们似乎只能自己用自定义的镜像来跑临时 CI 任务了。注意定义 presubmit 字段下的 decorate 为
false，并利用好 &lt;a href=&quot;https://github.com/kubernetes/test-infra/blob/master/prow/jobs.md#job-environment-variables&quot;&gt;Prow 提供的一些环境变量&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;下面是我的一个简单的 Spring Boot 项目利用 &lt;a href=&quot;https://hub.docker.com/r/lawrence2018/aliyun-maven&quot;&gt;Maven 镜像（配合阿里云的 Maven Repo）&lt;/a&gt;来做的
presubmits，同样在 config.yaml 中添加：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-yaml&quot;&gt;&lt;span class=&quot;hljs-attr&quot;&gt;presubmits:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;Lonor/kubernetes-springboot-demo:&lt;/span&gt;
    &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;spring&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;decorate:&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;false&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;always_run:&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;false&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;spec:&lt;/span&gt;
        &lt;span class=&quot;hljs-attr&quot;&gt;containers:&lt;/span&gt;
          &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;image:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;lawrence2018/maven:3.6-jdk-8&lt;/span&gt;
            &lt;span class=&quot;hljs-attr&quot;&gt;command:&lt;/span&gt; [ &lt;span class=&quot;hljs-string&quot;&gt;&quot;/bin/sh&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&quot;-c&quot;&lt;/span&gt; ]
            &lt;span class=&quot;hljs-attr&quot;&gt;args:&lt;/span&gt;
              &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;|
                mkdir -p /${REPO_NAME}
                cd /${REPO_NAME}
                git init
                git remote add origin https://github.com/${REPO_OWNER}/${REPO_NAME}.git
                git fetch origin pull/${PULL_NUMBER}/head:pr-${PULL_NUMBER}
                git checkout pr-${PULL_NUMBER}
                mvn test
&lt;/span&gt;            &lt;span class=&quot;hljs-attr&quot;&gt;resources:&lt;/span&gt;
              &lt;span class=&quot;hljs-attr&quot;&gt;requests:&lt;/span&gt;
                &lt;span class=&quot;hljs-attr&quot;&gt;cpu:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;2000m&quot;&lt;/span&gt;
                &lt;span class=&quot;hljs-attr&quot;&gt;memory:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;1536Mi&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;重新配置 config 后并应用更改，在 Pull Request 下评论 &lt;code&gt;/test all&lt;/code&gt; 或者 &lt;code&gt;/test spring&lt;/code&gt;
来触发测试。&lt;a href=&quot;https://github.com/Lonor/kubernetes-springboot-demo/pull/4&quot;&gt;参考此 PR&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/test&lt;/code&gt; 后面加 presubmits 的 name 名可触发指定的测试 Pod，all 则测试全部。测试若失败，可使用 &lt;code&gt;/retest&lt;/code&gt; 来重新测试。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/prow/2.png&quot; alt=&quot;Prow-in-Pull-Request-Test.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;如果测试通过，可使用 &lt;code&gt;/lgtm&lt;/code&gt; 触发 tide 让机器人合并代码（注意自己不能 &lt;code&gt;/lgtm&lt;/code&gt;）。&lt;/p&gt;
&lt;h3 id=&quot;自研-issuepr-bot&quot;&gt;&lt;a href=&quot;#%E8%87%AA%E7%A0%94-issuepr-bot&quot; style=&quot;text-decoration: none; margin-left: -17px;&quot;&gt;&lt;span style=&quot;color: rgba(153, 153, 153, 0.5); font-weight: 430;&quot;&gt;&lt;span style=&quot;padding: 0 4px 0 0&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;自研 Issue/PR Bot&lt;/h3&gt;
&lt;p&gt;GitHub 大规模使用 WebSocket 来动态修改 Issue 或 PR 页面的 DOM，在视觉上非常令人享受。笔者后来也参考 Prow 用 Go
写了一个简单了 &lt;a href=&quot;https://github.com/Lonor/OpsBot&quot;&gt;OpsBot&lt;/a&gt; 帮助自己的仓库做一些简单的 PR、Issue 的管理，现已开源，欢迎任何 Issue / PR！&lt;/p&gt;
</summary>
        <author><name>Lawrence</name></author>
      </entry>
      <entry>
        <title>Long Live RSS</title>
        <id>https://lawrenceli.me/blog/long-live-rss</id>
        <link href="https://lawrenceli.me/blog/long-live-rss"/>
        <updated>2020-09-30T00:00:00.000Z</updated>
        <summary type="html">&lt;p&gt;RSS: A family of Web feed formats used to publish frequently updated works (blog entries, news headlines, audio, and video) in a standardized format. &lt;a href=&quot;http://www.aaronsw.com/&quot;&gt;Aaron Swartz&lt;/a&gt; helped create it at the age of 14 and committed suicide at 26.&lt;/p&gt;
&lt;p&gt;Here are some good feed I subscribed for reading:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.yitianshijie.net/feed/&quot;&gt;https://blog.yitianshijie.net/feed/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://byvoid.com/zhs/feed.xml&quot;&gt;https://byvoid.com/zhs/feed.xml&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://martinfowler.com/feed.atom&quot;&gt;https://martinfowler.com/feed.atom&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://sspai.com/feed&quot;&gt;https://sspai.com/feed&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://www.qdaily.com/feed.xml&quot;&gt;http://www.qdaily.com/feed.xml&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.solidot.org/index.rss&quot;&gt;https://www.solidot.org/index.rss&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.shuziyimin.org/feed&quot;&gt;https://blog.shuziyimin.org/feed&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.thetype.com/feed/&quot;&gt;https://www.thetype.com/feed/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://www.aaronsw.com/2002/feeds/pgessays.rss&quot;&gt;http://www.aaronsw.com/2002/feeds/pgessays.rss&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Some apps for RSS were removed from the Chinese App Store in the autumn of 2020.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://twitter.com/reederapp/status/1310963891539709952&quot;&gt;https://twitter.com/reederapp/status/1310963891539709952&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://twitter.com/FieryFeeds/status/1310553289545658369&quot;&gt;https://twitter.com/FieryFeeds/status/1310553289545658369&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://twitter.com/Inoreader/status/922363626329133057&quot;&gt;https://twitter.com/Inoreader/status/922363626329133057&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://twitter.com/yitianshijieipn/status/1311127240713711616&quot;&gt;https://twitter.com/yitianshijieipn/status/1311127240713711616&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;There&apos;s a great web app for everyone to build the RSS feed with JavaScript: RSSHub.&lt;/p&gt;
&lt;p&gt;You can visit the &lt;a href=&quot;https://docs.rsshub.app/&quot;&gt;RSSHub doc&lt;/a&gt; to use it.&lt;/p&gt;
&lt;p&gt;The author of RSSHub deployed it on &lt;a href=&quot;https://rsshub.app/&quot;&gt;https://rsshub.app&lt;/a&gt; .&lt;/p&gt;
&lt;p&gt;As a contributor for RSSHub, I&apos;m also glad to share my self-hosted RSSHub for you:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://feed.lawrenceli.me/&quot;&gt;https://feed.lawrenceli.me&lt;/a&gt; .&lt;/p&gt;
&lt;p&gt;I&apos;ll try to keep &lt;a href=&quot;https://github.com/Lonor/RSSHub&quot;&gt;my repository&lt;/a&gt; in sync with &lt;a href=&quot;https://github.com/DIYgod/RSSHub&quot;&gt;the official RSSHub git repository&lt;/a&gt; every week.&lt;/p&gt;
&lt;p&gt;Enjoy reading.&lt;/p&gt;
</summary>
        <author><name>Lawrence</name></author>
      </entry>
      <entry>
        <title>Solidot 机器人</title>
        <id>https://lawrenceli.me/blog/solidot-robot</id>
        <link href="https://lawrenceli.me/blog/solidot-robot"/>
        <updated>2020-09-05T00:00:00.000Z</updated>
        <summary type="html">&lt;h2 id=&quot;奇客的资讯，重要的东西-️&quot;&gt;&lt;a href=&quot;#%E5%A5%87%E5%AE%A2%E7%9A%84%E8%B5%84%E8%AE%AF%EF%BC%8C%E9%87%8D%E8%A6%81%E7%9A%84%E4%B8%9C%E8%A5%BF-%EF%B8%8F&quot; style=&quot;text-decoration: none; margin-left: -17px;&quot;&gt;&lt;span style=&quot;color: rgba(153, 153, 153, 0.5); font-weight: 430;&quot;&gt;&lt;span style=&quot;padding: 0 4px 0 0&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;奇客的资讯，重要的东西 🤖️&lt;/h2&gt;
&lt;p&gt;这是中国社交网络&lt;a href=&quot;https://fanfou.com/jayonit&quot;&gt;饭否上的一个机器人&lt;/a&gt;。它是一个基于 &lt;a href=&quot;https://now.sh&quot;&gt;Vercel&lt;/a&gt; 、mongoDB
的免费数据库、以及 &lt;a href=&quot;https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions#onschedule&quot;&gt;GitHub Action 提供的声明式定时任务&lt;/a&gt;一起运行来实现的
Serverless 实践。&lt;a href=&quot;https://lawrenceli.me/contact&quot;&gt;作者&lt;/a&gt;充分利用互联网现代基础设施~~(白嫖)~~，让它每隔一段时间（目前为 30 分钟）爬取 solidot.org
网站的 &lt;a href=&quot;https://www.solidot.org/index.rss&quot;&gt;RSS&lt;/a&gt;，比较新旧的数据后，将新的内容通过 &lt;a href=&quot;https://github.com/fanfoujs/fanfou-sdk-node&quot;&gt;饭否 Node SDK&lt;/a&gt; 发布。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/solidot-robot/solidot.png&quot; alt=&quot;solidot&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/Lonor/SolidotRobot&quot;&gt;源代码&lt;/a&gt;以 MIT 协议开放，写的很简陋，凑合着能用。&lt;/p&gt;
&lt;p&gt;你也可以帮我调用&lt;a href=&quot;https://post-solidot-news-to-fanfou.now.sh/api/start&quot;&gt;这个 REST API&lt;/a&gt; 来帮助触发机器人行动：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-bash&quot;&gt;curl -XGET -L https://post-solidot-news-to-fanfou.now.sh/api/start&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;若返回空数组则 Robot 🤖️ 没有抓取到新闻，反之则返回饭否 API 的响应。&lt;/p&gt;
</summary>
        <author><name>Lawrence</name></author>
      </entry>
      <entry>
        <title>机械同理心</title>
        <id>https://lawrenceli.me/blog/mechanical-empathy</id>
        <link href="https://lawrenceli.me/blog/mechanical-empathy"/>
        <updated>2019-12-29T00:00:00.000Z</updated>
        <summary type="html">&lt;blockquote&gt;
&lt;p&gt;Martin Thompson 和 Dave Farley 多年来一直在讨论软件开发的「机械同理心」。他们的灵感来自一级方程式赛车手 Jackie Stewart 的名言，「要想成为一名赛车手，你不必成为一名工程师，但你必须有机械同理心。」了解赛车的运作方式会让你成为更好的赛车手，同样，程序员如果理解计算机硬件的工作原理也会有助于编程。你不一定需要获得计算机科学学位或者成为一名硬件工程师，但是你确实需要了解硬件的工作原理，并在设计软件时考虑这一点。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;前段时间阅读了《&lt;a href=&quot;https://book.douban.com/subject/34872165/&quot;&gt;Java 持续交付&lt;/a&gt;》，这里分享分享心得。&lt;/p&gt;
</summary>
        <author><name>Lawrence</name></author>
      </entry>
      <entry>
        <title>声明式编程</title>
        <id>https://lawrenceli.me/blog/declarative-programming</id>
        <link href="https://lawrenceli.me/blog/declarative-programming"/>
        <updated>2019-12-29T00:00:00.000Z</updated>
        <summary type="html">&lt;p&gt;写 YAML 上瘾了。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;命令式编程注重过程，声明式编程注重结果。前者强调代码本身的逻辑，需要开发者重视程序的上下文，以一步一步地实现应用功能。后者基于完善的 API / SDK，告诉（声明）程序需要达到的状态，程序会主动向其驱动以达到声明的目标，有时甚至无需在意底层实现。&lt;/p&gt;
&lt;p&gt;优雅的 API 往往是声明式的，通常也非常简洁明了。&lt;/p&gt;
&lt;p&gt;这里我举一个电风扇和空调的例子：&lt;/p&gt;
&lt;p&gt;电风扇：为了实现体表温度的控制，人类利用电动机发明了电风扇，一个命令式风速调节客户端 —— 大部分你需要自己手动地调节电风扇的风向、风速，让身体接受风扇的水平气压梯度力，以让体表流体流动速度上升从而达到吸热的目的。你可能已经间接地知道了它的实现&lt;strong&gt;原理&lt;/strong&gt;，尽管这个流程并不复杂。&lt;/p&gt;
&lt;p&gt;空调：它是声明式的。你&lt;strong&gt;无需关注&lt;/strong&gt;空调自身的实现原理，只需要通过遥控器&lt;strong&gt;声明&lt;/strong&gt;它应当达到的温度即可，空调就会驱动自身来实现密闭空间的恒温调节到达期望值 —— 哪怕空间的温度骤变。&lt;/p&gt;
&lt;p&gt;虽然栗子并不够恰当，但是我们拥有了倾向意识：空调调节起来多简单方便！&lt;/p&gt;
&lt;p&gt;软件开发亦如此。&lt;/p&gt;
&lt;p&gt;我们其实早已经频繁地和声明式编程打交道了：其实 HTML、CSS、SQL 这些语言自己就是声明式语言；纯函数编程语言也被认为是声明式语言，这非常有趣。另外，前端流行的 React、Vue 都利用了 Virtual DOM 和 diff 算法实现了声明式的 JavaScript 用户界面框架，再也不需要前端开发者去使用 jQuery 或原生写法来操作 DOM。&lt;/p&gt;
&lt;p&gt;Java 开发者在开发阶段使用声明式 API 的最典型例子就是运用注解。比如 Spring 提供 &lt;code&gt;@Autowired&lt;/code&gt; 注解实现 Bean 的依赖注入，其实现是基于 Java 语言的反射。Spring 更是提供了大量内置的注解简化了开发人员的开发: &lt;code&gt;@Transactional&lt;/code&gt; 实现声明式事务, &lt;code&gt;@Component&lt;/code&gt; , &lt;code&gt;@Value&lt;/code&gt;, &lt;code&gt;@Data&lt;/code&gt; ...&lt;/p&gt;
&lt;p&gt;首个毕业于 CNCF 的项目 Kubernetes 中也在充分地让 DevOps 拥抱声明式。它提供了给予 YAML 语法的配置文件（用以描述容器集群的拓扑状态），自动地向配置的声明驱动。云原生应用的一个必要特性就是声明式。&lt;/p&gt;
&lt;p&gt;GraphQL 让原本的 REST 更加声明式。这是一个非常适合全栈开发的 HTTP 接口规范，热度最近有在攀升，能否颠覆 REST 不得而知。&lt;/p&gt;
&lt;p&gt;Apple 在 WWDC 2019 上发布的 &lt;a href=&quot;https://developer.apple.com/cn/xcode/swiftui/&quot;&gt;SwiftUI&lt;/a&gt; 和 Google 开源的 &lt;a href=&quot;https://flutter.cn/docs/get-started/flutter-for/declarative&quot;&gt;Flutter&lt;/a&gt; 一样都是移动端的原生声明式 UI 框架。&lt;/p&gt;
&lt;p&gt;Maven、Gradle、npm、yarn、pip、go module 等提供了 Java、JavaScript / TypeScript、Python、GoLang 开发者声明式依赖管理工具，现代的应用开发几乎离不开它们。&lt;/p&gt;
&lt;p&gt;GitHub、GitLab、Jenkins Pipeline 提供了基于声明式配置文件（大部分为 YAML）的持续集成流水线，可以让开发人员也能低成本地学习实践 CI/CD。&lt;/p&gt;
&lt;p&gt;开发声明式 API 是很困难的，往往需要开发人员对语言以及框架的拥有深刻认知，并且是一种「将复杂留给自己，将方便留给别人」的美德。&lt;/p&gt;
&lt;p&gt;单纯作为用户而言，效率、便利、无副作用是声明式 API 的目的。而作为软件开发者，去意识到这些声明式应用或框架的背后原理往往会更加明白创作者的真实意图，也能更清晰自己所做的声明、或是充分优化应用层代码，而避免陷入&lt;a href=&quot;https://lawrenceli.me/blog/path-dependence-and-cargo-cult&quot;&gt;路径依赖与货物崇拜编程&lt;/a&gt;的怪圈。&lt;/p&gt;
&lt;p&gt;Links:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://spring.io/guides/gs/rest-service/&quot;&gt;https://spring.io/guides/gs/rest-service/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.infoq.cn/article/rest-apis-are-rest-in-peace-apis-long-live-graphql&quot;&gt;https://www.infoq.cn/article/rest-apis-are-rest-in-peace-apis-long-live-graphql&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://kubernetes.io/zh/docs/tasks/manage-kubernetes-objects/declarative-config/&quot;&gt;https://kubernetes.io/zh/docs/tasks/manage-kubernetes-objects/declarative-config/&lt;/a&gt;&lt;/p&gt;
</summary>
        <author><name>Lawrence</name></author>
      </entry>
      <entry>
        <title>Kubernetes Notes</title>
        <id>https://lawrenceli.me/blog/k8s-note</id>
        <link href="https://lawrenceli.me/blog/k8s-note"/>
        <updated>2019-10-01T00:00:00.000Z</updated>
        <summary type="html">&lt;p&gt;大部分内容皆来源于 &lt;a href=&quot;https://k8s.io&quot;&gt;Kubernetes 官网&lt;/a&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Kubernetes &lt;a href=&quot;https://kubernetes.io/zh/docs/concepts/overview/what-is-kubernetes/#%E4%B8%BA%E4%BB%80%E4%B9%88%E9%9C%80%E8%A6%81-kubernetes-%E5%AE%83%E8%83%BD%E5%81%9A%E4%BB%80%E4%B9%88&quot;&gt;中文概述&lt;/a&gt;（是什么以及不是什么）。&lt;/p&gt;
&lt;p&gt;实践时建议使用 &lt;a href=&quot;https://kubernetes.io/zh/docs/reference/kubectl/cheatsheet/#kubectl-%E8%87%AA%E5%8A%A8%E8%A1%A5%E5%85%A8&quot;&gt;kubectl 自动补全&lt;/a&gt;，极大提升交互效率。&lt;/p&gt;
&lt;h3 id=&quot;pod&quot;&gt;&lt;a href=&quot;#pod&quot; style=&quot;text-decoration: none; margin-left: -17px;&quot;&gt;&lt;span style=&quot;color: rgba(153, 153, 153, 0.5); font-weight: 430;&quot;&gt;&lt;span style=&quot;padding: 0 4px 0 0&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;Pod&lt;/h3&gt;
&lt;p&gt;Pod 是比容器更高一层次的抽象，同一 Pod 中的所有容器共享&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;同一网络 namespace&lt;/li&gt;
&lt;li&gt;存储&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;controller&quot;&gt;&lt;a href=&quot;#controller&quot; style=&quot;text-decoration: none; margin-left: -17px;&quot;&gt;&lt;span style=&quot;color: rgba(153, 153, 153, 0.5); font-weight: 430;&quot;&gt;&lt;span style=&quot;padding: 0 4px 0 0&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;Controller&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Deployment&lt;/li&gt;
&lt;li&gt;ReplicaSet&lt;/li&gt;
&lt;li&gt;DaemonSet&lt;/li&gt;
&lt;li&gt;StatefulSet&lt;/li&gt;
&lt;li&gt;Job&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;service&quot;&gt;&lt;a href=&quot;#service&quot; style=&quot;text-decoration: none; margin-left: -17px;&quot;&gt;&lt;span style=&quot;color: rgba(153, 153, 153, 0.5); font-weight: 430;&quot;&gt;&lt;span style=&quot;padding: 0 4px 0 0&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;Service&lt;/h3&gt;
&lt;p&gt;是一个&lt;a href=&quot;https://lawrenceli.me/blog/load-balancing&quot;&gt;四层负载均衡器&lt;/a&gt;，为 Pod 提供负载均衡。&lt;/p&gt;
&lt;h3 id=&quot;minikube&quot;&gt;&lt;a href=&quot;#minikube&quot; style=&quot;text-decoration: none; margin-left: -17px;&quot;&gt;&lt;span style=&quot;color: rgba(153, 153, 153, 0.5); font-weight: 430;&quot;&gt;&lt;span style=&quot;padding: 0 4px 0 0&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;MiniKube&lt;/h3&gt;
&lt;p&gt;国内建议自定义镜像仓库，默认是 &lt;a href=&quot;http://gcr.io&quot;&gt;gcr.io&lt;/a&gt; ，似乎自带了 PV。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-bash&quot;&gt;minikube start --vm-driver=none --image-repository=registry.cn-hangzhou.aliyuncs.com/google_containers --cpus 4 --memory 6144&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;感觉 Docker Desktop 内置的 Kubernetes 有点类似这个 minikube （master 可以直接运行 Pod，也就是单节点集群）。当然了，它们都只能用于开发/测试环境。&lt;/p&gt;
&lt;h3 id=&quot;demo&quot;&gt;&lt;a href=&quot;#demo&quot; style=&quot;text-decoration: none; margin-left: -17px;&quot;&gt;&lt;span style=&quot;color: rgba(153, 153, 153, 0.5); font-weight: 430;&quot;&gt;&lt;span style=&quot;padding: 0 4px 0 0&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;Demo&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/Lonor/kubernetes-springboot-demo&quot;&gt;用 Spring Boot 创建了一个 HTTP 服务端&lt;/a&gt;，可以尝试用 Kubernetes 部署它。&lt;/p&gt;
&lt;h3 id=&quot;公网服务器集群初始化常用脚本&quot;&gt;&lt;a href=&quot;#%E5%85%AC%E7%BD%91%E6%9C%8D%E5%8A%A1%E5%99%A8%E9%9B%86%E7%BE%A4%E5%88%9D%E5%A7%8B%E5%8C%96%E5%B8%B8%E7%94%A8%E8%84%9A%E6%9C%AC&quot; style=&quot;text-decoration: none; margin-left: -17px;&quot;&gt;&lt;span style=&quot;color: rgba(153, 153, 153, 0.5); font-weight: 430;&quot;&gt;&lt;span style=&quot;padding: 0 4px 0 0&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;公网服务器集群初始化常用脚本&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-sh&quot;&gt;cat &amp;#x3C;&amp;#x3C;&lt;span class=&quot;hljs-string&quot;&gt;EOF &gt; /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-sh&quot;&gt;yum install -y kubeadm-1.19.13-0 kubelet-1.19.13-0 kubectl-1.19.13-0&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-sh&quot;&gt;kubeadm config images pull --image-repository registry.aliyuncs.com/google_containers --kubernetes-version v1.19.3&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-sh&quot;&gt;kubeadm init --image-repository registry.aliyuncs.com/google_containers --kubernetes-version v1.19.13 --apiserver-advertise-address $(hostname -i) --pod-network-cidr=10.96.0.0/16 --service-cidr=10.97.0.0/16&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-sh&quot;&gt;mkdir -p &lt;span class=&quot;hljs-variable&quot;&gt;$HOME&lt;/span&gt;/.kube
cp -i /etc/kubernetes/admin.conf &lt;span class=&quot;hljs-variable&quot;&gt;$HOME&lt;/span&gt;/.kube/config
chown $(id -u):$(id -g) &lt;span class=&quot;hljs-variable&quot;&gt;$HOME&lt;/span&gt;/.kube/config&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Flannel:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-bash&quot;&gt;wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml](https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml)&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-bash&quot;&gt;kubectl apply -f kube-flannel.yaml&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;运行 Master 作为 Node:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-bash&quot;&gt;kubectl taint node &lt;span class=&quot;hljs-variable&quot;&gt;$HOSTNAME&lt;/span&gt; node-role.kubernetes.io/master-&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;恢复 Master 不可调度：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-bash&quot;&gt;kubectl taint node &lt;span class=&quot;hljs-variable&quot;&gt;$HOSTNAME&lt;/span&gt; node-role.kubernetes.io/master=&lt;span class=&quot;hljs-string&quot;&gt;&quot;&quot;&lt;/span&gt;:NoSchedule&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;重置：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-bash&quot;&gt;kubeadm reset
systemctl stop kubelet
rm -rf /var/lib/cni/
rm -rf /var/lib/etcd/
rm -rf /var/lib/kubelet/
rm -rf /etc/cni/
rm -rf &lt;span class=&quot;hljs-variable&quot;&gt;$HOME&lt;/span&gt;/.kube
ifconfig cni0 down
ifconfig flannel.1 down
ifconfig docker0 down
ip link delete cni0
ip link delete flannel.1
systemctl restart kubelet
systemctl stop docker
systemctl restart docker&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pod 无法访问外网，尝试：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-bash&quot;&gt;iptables -P FORWARD ACCEPT
iptables --flush
iptables -tnat --flush

iptables -F &amp;#x26;&amp;#x26; iptables -t nat -F &amp;#x26;&amp;#x26; iptables -t mangle -F &amp;#x26;&amp;#x26; iptables -X
&lt;span class=&quot;hljs-comment&quot;&gt;# POD 无法访问外网络，但是可以互相 ping 或 ping 宿主。CIDR 为 POD CIDR&lt;/span&gt;
&lt;span class=&quot;hljs-comment&quot;&gt;# 有可能是 SNAT 时出了问题，使用动态 SNAT （即 MASQUERADE） 来配置 nat 表路由规则：&lt;/span&gt;
iptables -t nat -I POSTROUTING -s 10.96.0.0/16 -j MASQUERADE
&lt;span class=&quot;hljs-comment&quot;&gt;# 再尝试重新运行 core dns 服务&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/rancher/rancher/issues/6139&quot;&gt;https://github.com/rancher/rancher/issues/6139&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/coredns/coredns/issues/2693&quot;&gt;https://github.com/coredns/coredns/issues/2693&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;网络调试：使用 BusyBox:1.28.3，不建议使用 latest 镜像.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-yaml&quot;&gt;&lt;span class=&quot;hljs-attr&quot;&gt;apiVersion:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;apps/v1&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;kind:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Deployment&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;busybox&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;namespace:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;default&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;labels:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;app:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;busybox&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;spec:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;selector:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;matchLabels:&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;app:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;busybox&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;replicas:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;template:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;metadata:&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;labels:&lt;/span&gt;
        &lt;span class=&quot;hljs-attr&quot;&gt;app:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;busybox&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;spec:&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;containers:&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;busybox&lt;/span&gt;
        &lt;span class=&quot;hljs-attr&quot;&gt;image:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;busybox:1.28.3&lt;/span&gt;
        &lt;span class=&quot;hljs-attr&quot;&gt;command:&lt;/span&gt;
          &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;sleep&lt;/span&gt;
          &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;3600&quot;&lt;/span&gt;
        &lt;span class=&quot;hljs-attr&quot;&gt;imagePullPolicy:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;IfNotPresent&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;restartPolicy:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Always&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;基于 DNS 的服务发现： Kubernetes Service / Pod 对象资源可以使用集群内部 FQDN 来访问，而不需要在意其扁平空间中的 Cluster IP。
Core DNS 会将 FQDN 动态地指向最新的 Cluster IP。参考 &lt;a href=&quot;https://kubernetes.io/zh/docs/concepts/services-networking/dns-pod-service&quot;&gt;Pod 与 Service 的 DNS&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-bash&quot;&gt;kubectl &lt;span class=&quot;hljs-built_in&quot;&gt;exec&lt;/span&gt; busybox -it -- nslookup kubernetes&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;返回内容：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-bash&quot;&gt;Server:    10.97.0.10
Address 1: 10.97.0.10 kube-dns.kube-system.svc.cluster.local

Name:      kubernetes
Address 1: 10.97.0.1 kubernetes.default.svc.cluster.local&lt;/code&gt;&lt;/pre&gt;
</summary>
        <author><name>Lawrence</name></author>
      </entry>
      <entry>
        <title>The World As I See It</title>
        <id>https://lawrenceli.me/blog/the-world-as-i-see-it</id>
        <link href="https://lawrenceli.me/blog/the-world-as-i-see-it"/>
        <updated>2019-08-18T00:00:00.000Z</updated>
        <summary type="html">&lt;p&gt;爱因斯坦的美国观。&lt;/p&gt;
&lt;hr&gt;
&lt;blockquote&gt;
&lt;p&gt;What first strikes the visitor with amazement is the superiority of this country, in matters of technics and organization. Objects of everyday use are more solid than in Europe, houses infinitely more convenient in arrangement. Everything is designed to save human labour. Labour is expensive, because the country is sparsely inhabited in comparison with its natural resources. The high price of labour was the stimulus which evoked the marvelous development of technical devices and methods of work.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这段话由爱因斯坦在《 &lt;a href=&quot;https://book.douban.com/subject/2058202/&quot;&gt;The World As I See It&lt;/a&gt; 》中提到。&lt;/p&gt;
&lt;p&gt;美国高昂的人力成本促使技术设备、工作方式的惊人发展。现在想想一个案例：中国之所以至今仍有很多挨家挨户抄水电表的人员，不是因为技术上实现不了自动化，而是中国人口太多，社会就业压力大，宁可牺牲效率来换稳定。&lt;/p&gt;
&lt;p&gt;站着说话不腰疼的我自己一直奉行一个原则：拒绝机械重复劳作，应当充分发挥机器的优势和人类的主观能动，即将有规则的、确定的流程交给机器去执行，而把富有创造性的工作交给人类。&lt;/p&gt;
&lt;p&gt;BLM 运动此起彼伏，许多开源项目在其官网首页顶部加上了醒目的 Slogan，对于美国系统性种族主义此文不作讨论。Kubernetes
官网谈了它的 &lt;a href=&quot;https://git.k8s.io/community/values.md#automation-over-process&quot;&gt;社区的价值观&lt;/a&gt;，我认为这也是多数开放源码项目社区的价值观：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We value time spent automating repetitive work more highly than toil.&lt;/p&gt;
&lt;/blockquote&gt;
</summary>
        <author><name>Lawrence</name></author>
      </entry>
      <entry>
        <title>路径依赖与货物崇拜</title>
        <id>https://lawrenceli.me/blog/path-dependence-and-cargo-cult</id>
        <link href="https://lawrenceli.me/blog/path-dependence-and-cargo-cult"/>
        <updated>2019-03-12T00:00:00.000Z</updated>
        <summary type="html">&lt;p&gt;警惕、避免。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;路径依赖原本是经济学中的一个名词，我并不深知其具体领域的定义，但用通俗的话来解释起来非常简单：过去的决策将会限制、缩小未来的可选择性。&lt;/p&gt;
&lt;p&gt;笔者首次了解「路径依赖」是源于 BYVoid 的博客文章—— &lt;a href=&quot;https://byvoid.com/zhs/blog/4-years-at-google-4/&quot;&gt;在 Google 的这四年&lt;/a&gt;。类比成贪心算法的局限，他提出的解决方案是模拟退火:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;模拟退火算法的理念是在初期引入随机性，随着迭代次数的增加而减小随机因子，这样最终收敛到全局最优解的机率更大。跟随这个理念来考虑职业的选择，我决定跳出现有的路径，哪怕是这个路径的前方一片光明，现在只是为了体验不同的选择。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;再讲一个宗教故事。&lt;/p&gt;
&lt;p&gt;二战期间，美军在太平洋的塔纳岛上建了一个临时基地。岛上原住民看到美军的战斗机，以为是「大铁鸟」。美军还会送一些有用的物资给岛上原住民。原住民没见过世面，都以为美军是神。后来，二战结束，美军撤走，还留了一点物资在岛上。岛上居民认为这些物资很神奇，相信这些美国大兵还会送货物过来，纷纷信仰、崇拜起了美军军服和物资（以吸引「大铁鸟」降临）。基于此，原住民发展出了自己的宗教—— 约翰弗鲁姆教。这就是货物崇拜（&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E8%B2%A8%E7%89%A9%E5%B4%87%E6%8B%9C&quot;&gt;Cargo Cult&lt;/a&gt;）。&lt;/p&gt;
&lt;p&gt;货物崇拜在软件工程领域延伸出了货物崇拜软件工程与货物崇拜编程。&lt;/p&gt;
&lt;p&gt;货物崇拜软件工程是货物崇拜科学的一个实例。货物崇拜科学是诺奖得主 Richard Feynman 于 1974 年提出的一门伪科学概念。这种类似科学的方法论从道德上就背叛了科学精神。&lt;/p&gt;
&lt;p&gt;以下为货物崇拜软件工程的主观实践：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;盲目模仿成功开发团队的表面现象&lt;/li&gt;
&lt;li&gt;机械套用软件开发过程却不知其由（即货物崇拜编程）&lt;/li&gt;
&lt;li&gt;把 996 当作福报&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;货物崇拜和路径依赖存在交集。具体表现在开发者的机械式开发。最常见的是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;jQuery 一把梭&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我发现有前端开发者会在使用 Vue 的情况下竟然依旧使用 jQuery 操作 DOM。这是典型的货物崇拜编程实践，因为这样的开发人员完全不理解 Vue 是做什么的，因为 Ta 已经习惯了 $ 的 API，哪管 DOM Virtual 不 Virtual；姑且撇开 Virtual DOM 的性能不说，这样做的开发人员往往也没能够理解&lt;a href=&quot;/blog/declarative-programming&quot;&gt;声明式&lt;/a&gt; UI 框架的内涵。&lt;/p&gt;
&lt;p&gt;上文提到的「模拟退火」是破解路径依赖困境的法则之一。除此之外，笔者认为最适合大多数人的做法是对事物保持强烈的好奇心。正如 Aaron Swartz 所云：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Be curious. Read widely. Try new things. What people call intelligence just boils down to curiosity.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;一些链接：&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.zhihu.com/question/56085124&quot;&gt;如何「评价老夫写代码就用 jQuery 」[图]&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E8%B4%A7%E7%89%A9%E5%B4%87%E6%8B%9C%E7%BC%96%E7%A8%8B&quot;&gt;维基百科：货物崇拜编程&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.goodreads.com/quotes/709288-be-curious-read-widely-try-new-things-what-people-call&quot;&gt;Quote from Aaron Swartz&lt;/a&gt;&lt;/p&gt;
</summary>
        <author><name>Lawrence</name></author>
      </entry>
      <entry>
        <title>四层负载均衡和七层负载均衡</title>
        <id>https://lawrenceli.me/blog/load-balancing</id>
        <link href="https://lawrenceli.me/blog/load-balancing"/>
        <updated>2018-10-09T00:00:00.000Z</updated>
        <summary type="html">&lt;p&gt;对于初次了解负载均衡的人来说，可能会被理解成「四次负载均衡」、「七次负载均衡」，这就完全错了。这里要理解的话直接参考英文：layer 4 load-balancing &amp;#x26; layer 7 load-balancing. 其实应当理解为工作在 OSI 第四层、第七层的负载均衡。&lt;/p&gt;
&lt;p&gt;所以，所谓的层指的是 &lt;a href=&quot;https://zh.wikipedia.org/wiki/OSI%E6%A8%A1%E5%9E%8B&quot;&gt;OSI 网络模型&lt;/a&gt;中的层次，分别对应于传输层（比如 TCP）和应用层（具体的 Application）。&lt;/p&gt;
&lt;p&gt;因此，基于 OSI 模型，先讲一个三层负载均衡的例子，DNS 解析，同一个域名可以解析到多个 IP 上，但轮询策略不在自己手上；四层负载均衡其实是在三层基础上加上了端口，也就是通过 IP + Port 的形式进行转发，而七层负载均衡在四层的基础上考虑应用的 URL 等方式做转发。&lt;/p&gt;
&lt;p&gt;由于三、四层负载均衡直接偏底层一些，其代理的效率会比七层的更高，比如单从 TCP 的开销上来看：四层负载均衡时，服务端收到来自客户端的请求，根据负载均衡器的策略，对目标 IP/Port 做修改，转发到对应的后端服务，TCP 三次握手直接一次建立。而七层负载均衡工作在应用层，客户端必须首先与负载均衡器进行一次 TCP 握手，然后负载均衡器再与目标后端服务建立第二次 TCP 握手。相比之下，七层负载均衡的性能就弱了。&lt;/p&gt;
&lt;p&gt;一个常见的现象：效率和安全（功能）往往不可兼得。七层负载均衡效率不高，其优势却在于可以植入一些应用上的逻辑，做流量压缩、鉴权、服务迁移、降级、熔断等处理，其可塑性会更好一点，且相对四层可以做一些安全上的设计，因为四层的负载均衡相当于直接间接地暴露了后端服务却难有应用层的调度。四层负载均衡器可以通过硬件实现，比如 F5，常见的软件实现是 lvs、nginx 中的 stream block，&lt;a href=&quot;https://kubernetes.io/zh/docs/concepts/services-networking/service/&quot;&gt;Kubernetes 中的 Service 对象&lt;/a&gt;。七层负载均衡器一般是一些高性能网关，比如 &lt;a href=&quot;https://openresty.org/cn/&quot;&gt;OpenResty&lt;/a&gt;，&lt;a href=&quot;https://kubernetes.io/zh/docs/concepts/services-networking/ingress/&quot;&gt;Kubernetes 中的 Ingress 对象&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;参考链接：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://jaminzhang.github.io/lb/L4-L7-Load-Balancer-Difference/&quot;&gt;四层、七层负载均衡的区别 - Jamin Zhang&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</summary>
        <author><name>Lawrence</name></author>
      </entry>
      <entry>
        <title>Invert binary tree</title>
        <id>https://lawrenceli.me/blog/invert-binary-tree</id>
        <link href="https://lawrenceli.me/blog/invert-binary-tree"/>
        <updated>2017-12-22T00:00:00.000Z</updated>
        <summary type="html">&lt;p&gt;A problem that Homebrew&apos;s author complained.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Invert a binary tree. Problem from leetcode.&lt;/p&gt;
&lt;p&gt;Here&apos;s the input:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-txt&quot;&gt;     4
   /   \
  2     7
 / \   / \
1   3 6   9&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and the out put looks like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-txt&quot;&gt;     4
   /   \
  7     2
 / \   / \
9   6 3   1&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This problem was inspired by this tweet:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://twitter.com/mxcl/status/608682016205344768&quot;&gt;https://twitter.com/mxcl/status/608682016205344768&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Here&apos;s the solution implemented in Java.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-java&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hljs-title hljs-class&quot;&gt;Solution&lt;/span&gt; {
    &lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; TreeNode &lt;span class=&quot;hljs-title hljs-function&quot;&gt;invertTree&lt;/span&gt;&lt;span class=&quot;hljs-params&quot;&gt;(TreeNode root)&lt;/span&gt; {
        &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt;(&lt;span class=&quot;hljs-literal&quot;&gt;null&lt;/span&gt; != root){
            &lt;span class=&quot;hljs-type&quot;&gt;TreeNode&lt;/span&gt; &lt;span class=&quot;hljs-variable&quot;&gt;leftChild&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; root.left;
            root.left = root.right;
            root.right = leftChild;
            root.left = invertTree(root.left);
            root.right = invertTree(root.right);
        }
        &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; root;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There&apos;s much different logic behind software engineering with computer science. &lt;a href=&quot;https://bit.ly/2HqaTe5&quot;&gt;Max replied this question about it on quora.com after 2 years.&lt;/a&gt;&lt;/p&gt;
</summary>
        <author><name>Lawrence</name></author>
      </entry>
      <entry>
        <title>没有银弹</title>
        <id>https://lawrenceli.me/blog/no-silver-bullet</id>
        <link href="https://lawrenceli.me/blog/no-silver-bullet"/>
        <updated>2017-09-01T00:00:00.000Z</updated>
        <summary type="html">&lt;p&gt;软件工程的一般规律。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;维基： &lt;a href=&quot;https://zh.wikipedia.org/wiki/%E6%B2%A1%E6%9C%89%E9%93%B6%E5%BC%B9&quot;&gt;https://zh.wikipedia.org/wiki/没有银弹&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;文献：Brooks, F. P.，&quot;No silver bullet—essence and accidents of software engineering, &quot; in Information Processing 86, H. J. Kugler, ed. Amsterdam: Elsevier Science (North Holland), 1986, pp. 1069-1076.&lt;/p&gt;
&lt;p&gt;结论：不会有任何单一的软件工程上的突破（银弹），能够让程序设计的生产力得到一个数量级的提升。&lt;/p&gt;
&lt;p&gt;「&lt;strong&gt;人月神话&lt;/strong&gt;」&lt;/p&gt;
&lt;p&gt;维基：&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E4%BA%BA%E6%9C%88%E7%A5%9E%E8%AF%9D&quot;&gt;https://zh.wikipedia.org/wiki/人月神话&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;豆瓣：&lt;a href=&quot;https://book.douban.com/subject/1102259/&quot;&gt;https://book.douban.com/subject/1102259/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;结论：人力和时间并不呈现线性关系。以大量人员和较短的时间，并不能缩短软件的开发进度。&lt;/p&gt;
&lt;p&gt;公式：&lt;/p&gt;
&lt;p&gt;沟通管道数量（n 为一个项目团队中的人数，显然，人数越多，沟通成本越大）：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-txt&quot;&gt;p = n(n-1)/2&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Brooks 法则：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Adding manpower to a late software project makes it later.&lt;/p&gt;
&lt;/blockquote&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;p&gt;评价标准：功能概念复杂度比。功能多不见得是好事。&lt;/p&gt;
&lt;p&gt;避免第二系统过度设计。&lt;/p&gt;
</summary>
        <author><name>Lawrence</name></author>
      </entry>
      <entry>
        <title>Pull Request</title>
        <id>https://lawrenceli.me/blog/pull-request</id>
        <link href="https://lawrenceli.me/blog/pull-request"/>
        <updated>2017-01-01T00:00:00.000Z</updated>
        <summary type="html">&lt;p&gt;What and How.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Pull Request 是 GitHub 发明的用于 Git 多人协作开发的模式。&lt;/p&gt;
&lt;p&gt;官方文档里有不少详细的定义： &lt;a href=&quot;https://docs.github.com/en/free-pro-team@latest/github/collaborating-with-issues-and-pull-requests&quot;&gt;GitHub Docs&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;我画了一个简单的示意图解释具体的行为：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/pull-request/workflow.png&quot; alt=&quot;Pull-Request&quot;&gt;&lt;/p&gt;
&lt;p&gt;以下是我自己的认识：&lt;/p&gt;
&lt;p&gt;首先，存在一个上游（upstream）仓库，仓库地址一般的 URL 都是类似 REST 样式的路径，比如：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;github.com/kubernetes/website&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;第一个路由为仓库的拥有者，第二个路由为项目名。上面的 URL 可以非常形象地理解为：&lt;/p&gt;
&lt;p&gt;托管在 GitHub 上面的由 Kubernetes 作者开发的 website 项目。&lt;/p&gt;
&lt;p&gt;然后，我们采取 &lt;code&gt;fork&lt;/code&gt; （复刻）动作来将 upstream 仓库此刻的代码复制到自己的仓库中，于是产生了源（origin）仓库，地址类似于：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;github.com/Lonor/website&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;现在，可以使用如下命令来获取到 origin 代码到本地：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-bash&quot;&gt;git &lt;span class=&quot;hljs-built_in&quot;&gt;clone&lt;/span&gt; git@github.com:Lonor/website.git&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;OK! 我们已经一步一步拿到了代码，用 IDE 打开这个仓库，不着急更改内容，先看一下本地仓库关联了哪些远程仓库，使用如下命令查看：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-bash&quot;&gt;git remote -v&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;它至少会返回我们 clone 的仓库地址。但是并没有呈现出 upstream 地址，我们需要手动添加：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-bash&quot;&gt;git remote add upstream git@github.com:kubernetes/website.git&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这时候就关联好了，这一步很关键：因为会牵涉到多人协作时发生的同步复刻 (Sync fork)。&lt;/p&gt;
&lt;p&gt;现在尝试修改代码，比如修复某个臭虫（俗称 bug），或者增加某种需要的功能。然后，以 master 分支为例，push 到 origin：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-bash&quot;&gt;git add .
git commit -m &lt;span class=&quot;hljs-string&quot;&gt;&quot;bug fixed&quot;&lt;/span&gt;
git push -u origin master&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;现在，刷新远程仓库 Web 端页面，可以发现代码已经提交好了。接下来，创建 Pull Request （拉取请求）。&lt;/p&gt;
&lt;h3 id=&quot;如何理解-pull-request？&quot;&gt;&lt;a href=&quot;#%E5%A6%82%E4%BD%95%E7%90%86%E8%A7%A3-pull-request%EF%BC%9F&quot; style=&quot;text-decoration: none; margin-left: -17px;&quot;&gt;&lt;span style=&quot;color: rgba(153, 153, 153, 0.5); font-weight: 430;&quot;&gt;&lt;span style=&quot;padding: 0 4px 0 0&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;如何理解 Pull Request？&lt;/h3&gt;
&lt;p&gt;我们所做的是向 upstream 仓库贡献自己写的代码，为什么叫 Pull Request 而不叫 Merge Request 呢？这样理解，本质是对的。GitLab 就采用了 Merge Request 的叫法。但是 GitHub
认为，origin 仓库开发者并没有权限直接向 upstream 仓库直接提交代码，但是，origin 仓可以向 upstream 发一个请求，让 upstream 仓库主动拉取 origin 仓库的变更代码，以此实现合并的目的。&lt;/p&gt;
&lt;p&gt;欲了解更多，可以参考&lt;a href=&quot;https://www.zhihu.com/question/21682976&quot;&gt;知乎上有关 Pull Request 的讨论&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;现在 Pull Request 创建好了！它会在 upstream 仓库的 Pull Requests 页面下出现，URL 一般长这样：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-txt&quot;&gt;github.com/kubernetes/website/pull/21198&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个过程非常有用！尤其对于持续集成能产生很大的效率。&lt;/p&gt;
&lt;p&gt;我理解的 Pull Request
本质上是一个分支，归到底还是&lt;a href=&quot;https://git-scm.com/book/zh/v2/Git-%E5%86%85%E9%83%A8%E5%8E%9F%E7%90%86-Git-%E5%BC%95%E7%94%A8&quot;&gt;一个指向某一系列提交之首的指针或引用&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;使用如下命令可以在本地仓库切到 GitHub 上 233 的 Pull Request 合并前或合并后的 commit Head:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-bash&quot;&gt;git config remote.origin.fetch &lt;span class=&quot;hljs-string&quot;&gt;&apos;+refs/pull/*:refs/remotes/origin/pull/*&apos;&lt;/span&gt;
git fetch
git show-ref
&lt;span class=&quot;hljs-comment&quot;&gt;# Pull Request 合并之前&lt;/span&gt;
git checkout origin/pull/123/head
&lt;span class=&quot;hljs-comment&quot;&gt;# Pull Request 合并之后&lt;/span&gt;
git checkout origin/pull/123/merge&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样就能够在某个 PR 上做一些集成测试了。&lt;/p&gt;
&lt;h3 id=&quot;merge&quot;&gt;&lt;a href=&quot;#merge&quot; style=&quot;text-decoration: none; margin-left: -17px;&quot;&gt;&lt;span style=&quot;color: rgba(153, 153, 153, 0.5); font-weight: 430;&quot;&gt;&lt;span style=&quot;padding: 0 4px 0 0&quot;&gt;#&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;Merge&lt;/h3&gt;
&lt;p&gt;在 GitHub 上合并 Pull Request 有三种方式：Merge、Squash 以及 Rebase. 主要区别：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Merge: 除了 PR 上所有的 commit，merge 时会再 commit 一次&lt;/li&gt;
&lt;li&gt;Squash: 将 PR 上所有的 commit 压缩成一次 commit&lt;/li&gt;
&lt;li&gt;Rebase: 执行变基操作后追加到被合并分支&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;直接 Merge 是大多数人的选择，但是我自己偏好使用 Rebase，这种做法会保持主分支的干净简洁。&lt;/p&gt;
&lt;p&gt;今天先到这里，有空继续写。&lt;/p&gt;
</summary>
        <author><name>Lawrence</name></author>
      </entry>
      <entry>
        <title>When to Use Static Generation v.s. Server-side Rendering</title>
        <id>https://lawrenceli.me/blog/ssg-ssr</id>
        <link href="https://lawrenceli.me/blog/ssg-ssr"/>
        <updated>1970-01-02T00:00:00.000Z</updated>
        <summary type="html">&lt;p&gt;We recommend using &lt;strong&gt;Static Generation&lt;/strong&gt; (with and without data) whenever possible because your page can be built once
and served by CDN, which makes it much faster than having a server render the page on every request.&lt;/p&gt;
&lt;p&gt;You can use Static Generation for many types of pages, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Marketing pages&lt;/li&gt;
&lt;li&gt;Blog posts&lt;/li&gt;
&lt;li&gt;E-commerce product listings&lt;/li&gt;
&lt;li&gt;Help and documentation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You should ask yourself: &quot;Can I pre-render this page &lt;strong&gt;ahead&lt;/strong&gt; of a user&apos;s request?&quot; If the answer is yes, then you
should choose Static Generation.&lt;/p&gt;
&lt;p&gt;On the other hand, Static Generation is &lt;strong&gt;not&lt;/strong&gt; a good idea if you cannot pre-render a page ahead of a user&apos;s request.
Maybe your page shows frequently updated data, and the page content changes on every request.&lt;/p&gt;
&lt;p&gt;In that case, you can use &lt;strong&gt;Server-Side Rendering&lt;/strong&gt;. It will be slower, but the pre-rendered page will always be
up-to-date. Or you can skip pre-rendering and use client-side JavaScript to populate data.&lt;/p&gt;
</summary>
        <author><name>Lawrence</name></author>
      </entry>
      <entry>
        <title>Two Forms of Pre-rendering</title>
        <id>https://lawrenceli.me/blog/pre-rendering</id>
        <link href="https://lawrenceli.me/blog/pre-rendering"/>
        <updated>1970-01-01T00:00:00.000Z</updated>
        <summary type="html">&lt;p&gt;Next.js has two forms of pre-rendering: &lt;strong&gt;Static Generation&lt;/strong&gt; and &lt;strong&gt;Server-side Rendering&lt;/strong&gt;. The difference is in &lt;strong&gt;when&lt;/strong&gt; it generates the HTML for a page.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Static Generation&lt;/strong&gt; is the pre-rendering method that generates the HTML at &lt;strong&gt;build time&lt;/strong&gt;. The pre-rendered HTML is then &lt;em&gt;reused&lt;/em&gt; on each request.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Server-side Rendering&lt;/strong&gt; is the pre-rendering method that generates the HTML on &lt;strong&gt;each request&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Importantly, Next.js lets you &lt;strong&gt;choose&lt;/strong&gt; which pre-rendering form to use for each page. You can create a &quot;hybrid&quot; Next.js app by using Static Generation for most pages and using Server-side Rendering for others.&lt;/p&gt;
</summary>
        <author><name>Lawrence</name></author>
      </entry>
    </feed>