<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>kyverno | 伪架构师</title>
    <link>/tags/kyverno/</link>
      <atom:link href="/tags/kyverno/index.xml" rel="self" type="application/rss+xml" />
    <description>kyverno</description>
    <generator>Source Themes Academic (https://sourcethemes.com/academic/)</generator><language>zh</language><lastBuildDate>Thu, 18 Jul 2024 22:59:35 +0800</lastBuildDate>
    <image>
      <url>/img/logo-wide.png</url>
      <title>kyverno</title>
      <link>/tags/kyverno/</link>
    </image>
    
    <item>
      <title>用 Kyverno 让 Argo Workflow 单步执行</title>
      <link>/post/%E7%94%A8-kyverno-%E8%AE%A9-argo-workflow-%E5%8D%95%E6%AD%A5%E6%89%A7%E8%A1%8C/</link>
      <pubDate>Thu, 18 Jul 2024 22:59:35 +0800</pubDate>
      <guid>/post/%E7%94%A8-kyverno-%E8%AE%A9-argo-workflow-%E5%8D%95%E6%AD%A5%E6%89%A7%E8%A1%8C/</guid>
      <description>

&lt;p&gt;AWS 的 SSM Automation 中，有个有趣的特性就是单步执行，在编写自动化脚本的时候，这个功能对调试非常有帮助。Argo Workflow 也有个暂停特性，官网给出的例子是这样的：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
  generateName: pause-after-
spec:
  entrypoint: whalesay
  templates:
    - name: whalesay
      container:
        image: argoproj/argosay:v2
        env:
          - name: ARGO_DEBUG_PAUSE_AFTER
            value: &#39;true&#39;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;把他提交到 Argo 会看到暂停的情况：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-command&#34;&gt;$ argo submit --watch debug.yml
Name:                pause-after-hpvg9                                                                                                                                          [0/1455]
Namespace:           default
ServiceAccount:      unset (will run with the default ServiceAccount)
Status:              Running
Conditions:
 PodRunning          True
Created:             Thu Jul 18 23:18:46 +0800 (18 seconds ago)
Started:             Thu Jul 18 23:18:46 +0800 (18 seconds ago)
Duration:            18 seconds
Progress:            0/1

STEP                  TEMPLATE  PODNAME            DURATION  MESSAGE
 ● pause-after-hpvg9  whalesay  pause-after-hpvg9  18s
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;你会发现，这个 Workflow 会一直冻结在这个状态，&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-command&#34;&gt;$ argo list
NAME                STATUS      AGE   DURATION   PRIORITY   MESSAGE
pause-after-hpvg9   Running     11m   11m        0
...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这时候只要进入 Pod，执行一个命令，工作流就会完成：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-command&#34;&gt;$ kubectl exec -it pause-after-hpvg9 -- bash
root@pause-after-hpvg9:/# touch /proc/1/root/var/run/argo/ctr/main/after
root@pause-after-hpvg9:/# command terminated with exit code 137
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;可以看到 Argo 的 Watch 也发生了变化：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-command&#34;&gt;STEP                  TEMPLATE  PODNAME            DURATION  MESSAGE
 ✔ pause-after-hpvg9  whalesay  pause-after-hpvg9  21m
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;问题来了，正常的工作流不会只有一个步骤，要实现单步执行的效果，就需要给每个步骤加入环境变量，是不是有点麻烦？我想到一个办法——用 Kyverno 做个自动补丁。只要 Workflow 加上一个 &lt;code&gt;debug&lt;/code&gt; 标签，就给所有步骤加入暂停标志。&lt;/p&gt;

&lt;p&gt;废话不多说，上策略代码：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: add-argo-debug-env
spec:
  rules:
    - name: add-debug-env-var
      match:
        resources:
          kinds:
            - argoproj.io/v1alpha1/Workflow
          selector:
            matchLabels:
              debug: &amp;quot;true&amp;quot;
          operations:
          - CREATE
      mutate:
        foreach:
          - list: request.object.spec.templates[]
            patchesJson6902: |-
              - path: /spec/templates/{{elementIndex}}/container/env/-
                op: add
                value:
                  name: ARGO_DEBUG_PAUSE_AFTER
                  value: &amp;quot;true&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这段策略有几个要点：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;selector&lt;/code&gt; 指定，只处理带有 Debug 标签，并且操作为 &lt;code&gt;CREATE&lt;/code&gt; 的&lt;/li&gt;
&lt;li&gt;使用 &lt;code&gt;foreach&lt;/code&gt; 语法，处理工作流中出现的每一个步骤&lt;/li&gt;
&lt;li&gt;用 &lt;code&gt;patchesJson6902&lt;/code&gt; 方式，给每个步骤的容器加入 &lt;code&gt;ARGO_DEBUG_PAUSE_AFTER&lt;/code&gt; 环境变量。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;提交策略之后，用如下任务脚本测试一下：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
  generateName: debug314159-
  labels:
    debug: &amp;quot;true&amp;quot;
spec:
  entrypoint: whalesay
  templates:
    - name: whalesay
      container:
        image: argoproj/argosay:v2
    - name: whalesayagain
      container:
        image: argoproj/argosay:v2
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;提交工作流：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-command&#34;&gt;$ argo submit debug.yml
Name:                debug314159-dvqmw
Namespace:           default
ServiceAccount:      unset (will run with the default ServiceAccount)
Status:              Pending
Created:             Fri Jul 19 00:11:15 +0800 (now)
Progress:
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;查看生成的工作流：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-command&#34;&gt;apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
...
  labels:
    debug: &amp;quot;true&amp;quot;
    workflows.argoproj.io/completed: &amp;quot;false&amp;quot;
    workflows.argoproj.io/phase: Running
  name: debug314159-dvqmw
  namespace: default
...
spec:
...
  - container:
      env:
      - name: ARGO_DEBUG_PAUSE_AFTER
        value: &amp;quot;true&amp;quot;
      image: argoproj/argosay:v2
...
  - container:
      env:
      - name: ARGO_DEBUG_PAUSE_AFTER
        value: &amp;quot;true&amp;quot;
      image: argoproj/argosay:v2
      name: &amp;quot;&amp;quot;
...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;可以看到，Kyverno 给每个步骤都加入了环境变量，这样一来，就实现了单步执行的效果。&lt;/p&gt;

&lt;h2 id=&#34;后记&#34;&gt;后记&lt;/h2&gt;

&lt;p&gt;这个办法还有个问题，就是恢复太麻烦了，我打算接下来用 Shell Operator 来解决。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;不明白为什么 Argo Workflow 没有给这种步骤设置一个暂停状态。&lt;/p&gt;
&lt;/blockquote&gt;
</description>
    </item>
    
    <item>
      <title>Kubernetes 策略引擎对比：OPA/Gatekeeper vs Kyverno</title>
      <link>/post/k8s-policy-comparison/</link>
      <pubDate>Tue, 23 Feb 2021 16:57:55 +0800</pubDate>
      <guid>/post/k8s-policy-comparison/</guid>
      <description>

&lt;p&gt;Kubernetes 的 &lt;a href=&#34;https://kubernetes.io/docs/concepts/policy/pod-security-policy/&#34; target=&#34;_blank&#34;&gt;Pod Security Policy（PSP）&lt;/a&gt;即将被&lt;a href=&#34;https://github.com/kubernetes/kubernetes/pull/97171&#34; target=&#34;_blank&#34;&gt;淘汰和移除&lt;/a&gt;，所以需要找到一个替代方案来填补这个即将出现的空白。目前看来，Kubernetes 自身并没有准备相应的替代方案，因此需要在 Kubernetes 之外寻求解决之道。CNCF 的两个头部项目可能会成为首选的替代产品，它们分别是基于 &lt;a href=&#34;https://www.openpolicyagent.org/&#34; target=&#34;_blank&#34;&gt;Open Policy Agent（OPA）&lt;/a&gt;的 &lt;a href=&#34;https://github.com/open-policy-agent/gatekeeper&#34; target=&#34;_blank&#34;&gt;Gatekeeper&lt;/a&gt; 以及 &lt;a href=&#34;https://kyverno.io/&#34; target=&#34;_blank&#34;&gt;Kyverno&lt;/a&gt;，两个产品各行有千秋，但是目前还没有对这两个产品进行过正式的比较，这就让面临选择的用户无从下手了。这两个项目都是全功能的 Kubernetes 策略引擎，因此其功能不仅限于替代 PSP。本文尝试对 Gatekeeper 和 Kyverno 进行一个中立客观的比较，让用户能够据此作出决策。这里仅从 Kubernetes 的视角来对这两个项目来进行评价。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;因为本文仅仅涉及 Kubernetes，因此对后续对 OPA/Gatekeeper 项目会简称为 Gatekeeper。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;为了透明起见，我想公开说明我个人的立场。我是 Kyverno 而不是 GateKeeper 的撰稿人。我在 Kyverno 上写过几篇博客，在 Gatekeeper 上则没有。我过去还曾对 OPA Rego 提出过一些批评。然而，我的目标是把所有这些和任何个人感情放在一边，并试图以全新的方式来对待这两个项目，没有任何偏见和偏爱。&lt;/p&gt;

&lt;p&gt;在和 Kyverno 和 OPA 两个社区进行平等地沟通，让双方的管理者和贡献者公平地对比较标准和结果进行评论。在参与比较、评论等方面均没有偏向任何项目。&lt;/p&gt;

&lt;h2 id=&#34;导言&#34;&gt;导言&lt;/h2&gt;

&lt;h3 id=&#34;kubernetes-策略是什么&#34;&gt;Kubernetes 策略是什么&lt;/h3&gt;

&lt;p&gt;Kubernetes 的 Pod Security Policy，正如其名字所暗示的，仅是针对 Pod 工作的，是一种用来验证和控制 Pod 及其属性的机制。另外 PSP 只能屏蔽非法 Pod 的创建，无法执行任何补救/纠正措施。而 Gatekeeper 和 Kyverno 的作用范围就不是局限在 Pod 上，并且也有更多更深入的功能，而不只是简单的验证功能。策略引擎是一种能对整个 Kubernetes 环境进行全局控制的方法。&lt;/p&gt;

&lt;h3 id=&#34;gatekeeper-简介&#34;&gt;Gatekeeper 简介&lt;/h3&gt;

&lt;p&gt;Gatekeeper 是一个由 Google、微软等多个公司合作推出的开源项目，后来捐赠给了 CNCF。现已经历了三次迭代。Gatekeeper 是通用策略引擎 Open Policy Agent（OPA）的 Kubernetes 专用实现。由于 Open Policy Agent 与 Gatekeeper 之间的关系，该项目经常被写成“OPA Gatekeeper”来表明这层关系。Gatekeeper 实现了请求验证功能，最近还加入了变异能力。OPA 的一个主要特征是依赖于使用一种叫做 Rego 的专用编程语言，这种语言被用来实现策略决策的必要逻辑。通过 Rego，OPA 能够广泛适用于包括 Kubernetes 在内的多种不同的软件，实现高层次的逻辑操作。&lt;/p&gt;

&lt;h3 id=&#34;kyverno-简介&#34;&gt;Kyverno 简介&lt;/h3&gt;

&lt;p&gt;Kyverno 是来自 Nirmata 的开源项目，后来也捐赠给了 CNCF。和 Gatekeeper一样，Kyverno 也是一个具有验证和变异能力的 Kubernetes 策略引擎，但是它还有生成资源的功能，最近还加入了 API 对象查询的能力。与 Gatekeeper 不同，Kyverno 原本就是为 Kubernetes 编写的。和 Gatekeeper 相比，Kyverno 除了对象生成功能之外，还无需专用语言即可编写策略，从实现语言的角度上来看，Kyverno 的模型更为简洁。&lt;/p&gt;

&lt;h2 id=&#34;对比&#34;&gt;对比&lt;/h2&gt;

&lt;p&gt;下面的三个表格对两个项目的特征和质量进行分类，并试图以最客观的方式进行对比。这些维度分别是：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;特征/功能维度用于描述技术属性；&lt;/li&gt;
&lt;li&gt;社区/生态系统维度用于描述落地情况和组织属性；&lt;/li&gt;
&lt;li&gt;杂项。&lt;/li&gt;
&lt;/ul&gt;

&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;特征/功能&lt;/th&gt;
&lt;th&gt;Gatekeeper&lt;/th&gt;
&lt;th&gt;Kyverno&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;

&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;验证&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;变异&lt;/td&gt;
&lt;td&gt;✅（Alpha）&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;生成&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;原生策略对象&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;监控指标&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;OpenAPI 验证（&lt;code&gt;kubectl explain&lt;/code&gt;）&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;高可用&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;API 对象查询&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅（Alpha）&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;具备测试能力的 CLI 工具&lt;/td&gt;
&lt;td&gt;✅ 独立的客户端&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;策略审计&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;社区/生态系统&lt;/th&gt;
&lt;th&gt;Gatekeeper&lt;/th&gt;
&lt;th&gt;Kyverno&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;

&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CNCF 状态&lt;/td&gt;
&lt;td&gt;毕业（OPA）&lt;/td&gt;
&lt;td&gt;沙箱&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;合作伙伴生态系统采用（注 1）&lt;/td&gt;
&lt;td&gt;◗&lt;/td&gt;
&lt;td&gt;◔&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;Github 状态（星，分叉、版本、提交）&lt;/td&gt;
&lt;td&gt;1,543, 280, 38, 510&lt;/td&gt;
&lt;td&gt;702, 72, 60, 3,034&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;社区认同（注 1）&lt;/td&gt;
&lt;td&gt;◗&lt;/td&gt;
&lt;td&gt;◔&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;策略样本库&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;blockquote&gt;
&lt;p&gt;注 1：无精确定义，Gatekeeper 看起来比 Kyverno 采用数量更多，但是并没有具体数字。
注 2：无客观标准，Gatekeeper 历史更长，社区认可度可能更高。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;杂项&lt;/th&gt;
&lt;th&gt;Gatekeeper&lt;/th&gt;
&lt;th&gt;Kyverno&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;

&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;需要编程&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;可以在 Kubernetes 之外工作&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;诞生时间&lt;/td&gt;
&lt;td&gt;2017 年 7 月&lt;/td&gt;
&lt;td&gt;2019 年 5 月&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;创始公司&lt;/td&gt;
&lt;td&gt;Styra（OPA）&lt;/td&gt;
&lt;td&gt;Nirmata&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;文档成熟度&lt;/td&gt;
&lt;td&gt;◗（注 1）&lt;/td&gt;
&lt;td&gt;◕&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;blockquote&gt;
&lt;p&gt;注 1：并没有统一的评判标准。这里的评价基于 Gatekeeper 的功能，而不是 Rego。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&#34;分析&#34;&gt;分析&lt;/h2&gt;

&lt;p&gt;根据前面的功能对比，我做了一个简单的归纳，列出两个产品的优劣，这里只写出了标题内容，并不够详尽。&lt;/p&gt;

&lt;h3 id=&#34;gatekeeper-的优势&#34;&gt;Gatekeeper 的优势&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;能够表达非常复杂的策略；&lt;/li&gt;
&lt;li&gt;社区更为成熟；&lt;/li&gt;
&lt;li&gt;支持多副本模式，更好的可用性和伸缩性。&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&#34;gatekeeper-的劣势&#34;&gt;Gatekeeper 的劣势&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;需要编程语言支持，该语言的学习曲线较为陡峭，可能会产生大量技术债，并延长交付时间；&lt;/li&gt;
&lt;li&gt;变异能力还处在萌芽期；&lt;/li&gt;
&lt;li&gt;没有生成能力，意味着它的主要应用场景就在验证方面；&lt;/li&gt;
&lt;li&gt;策略复杂冗长，需要多个对象协同实现。&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&#34;kyverno-的优势&#34;&gt;Kyverno 的优势&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Kubernetes 风格的策略表达方式，非常易于编写；&lt;/li&gt;
&lt;li&gt;成熟的变异能力；&lt;/li&gt;
&lt;li&gt;独特的生成和同步能力，扩展了应用场景；&lt;/li&gt;
&lt;li&gt;快速交付，场景丰富。&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&#34;kyverno-的劣势&#34;&gt;Kyverno 的劣势&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;受到语言能力的限制，难以实现复杂策略；&lt;/li&gt;
&lt;li&gt;较为年轻，社区接受度不高；&lt;/li&gt;
&lt;li&gt;API 对象查询能力还很初级；&lt;/li&gt;
&lt;li&gt;没有高可用能力（还在路线图阶段）。&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;警告：下面的内容是我根据前面的对比表和优势劣势列表，再加上自己对这两个工具的体验，以及在云原生社区的走访，综合起来的意见分析。如果你没有兴趣看我的观点，文章就到此为止了。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Kubernetes 是一个声明式的系统：用户向 Kubernetes 提出对状态的要求，Kubernetes 通过各种控制器，去协调观察到的状态，以使其与用户期望的状态一致。这就是云原生平台的核心价值主张。为了实现这一目标，逻辑实现的重任从用户身上转移到了平台本身。每个资源类型都存在一些内部逻辑，这些逻辑就是协调其状态所需的能力。对于 Gatekeeper 来说，到目前为止最大的弱点是它需要一种叫做 Rego 的专门的编程语言来实现这种逻辑，这种语言在其他地方都无法使用。这是一个现实，因为 OPA 是一个通用的策略引擎。只有通过 Gatekeeper 将其改编成 Kubernetes 形式，才能利用其能力。那么实际上，用户负责描述他们希望调和的对象（策略），以及提供必要的逻辑（Rego）来调和它。使用外部 DSL 来管理 Kubernetes 策略，在很多方面都会变得繁琐和复杂，并给项目增加技术债务。作为一种权衡，其明显的优势是可以实现非常强大的策略。毕竟，当一个人需要编写一种编程语言时，他只受限于该语言的能力及其输入。不过，如果可以在其他地方利用 OPA，就可以分摊这种费用。&lt;/p&gt;

&lt;p&gt;相比 Gatekeeper 来说，Kyverno 的第一印象就是没有那么复杂的技术需求。因为它是专门为 Kubernetes 构建的，并且用声明式的方法来表达策略，所以它的心理模型与 Kubernetes 对象的描述和协调方式是相同的。执行策略决策所需的逻辑被从用户的负担中移除，成为工具本身的领域。这种模式导致策略的编写方式得到了极大的简化，全面的降低了策略引擎的使用难度。Kyverno 的编译和生成能力，使它从一个简单的准入控制器转变为一个真正的自动化工具。通过结合这三种能力，再加上最近增加的 API 查询能力，Kyverno 能够执行 Gatekeeper 所不能执行的任务，而且还能够消除可能在整个集群和/或组织中分散使用的其他和不同的工具。这种简单性加上它的自动化能力和对其他工具的整合，为新用户以及有经验的用户和操作者带来了巨大的价值。&lt;/p&gt;

&lt;p&gt;根据所介绍的信息，我认为 Kyverno 应该是应用 Kubernetes 策略的一个比较自然的选择。但如果用户符合下面两个用例中的一种或两种，就更应该选择 Gatekeeper。&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;有一种需求和具体意图，使用一致的核心工具将策略应用于组织内不同的系统（即，不仅仅是Kubernetes）。&lt;/p&gt;

&lt;p&gt;反对意见：根据我的经验，无论是在云原生社区内部还是外部，大多数组织目前已经在使用其他工具将策略应用于现有系统。这通常是因为这些系统以及为这些系统实施策略的软件在 Kubernetes 以及 OPA 和 Gatekeeper 之前就已经存在。此外，这些现有工具通常不要求使用编程语言来实现其策略。因此，考虑到现有的知识、运营和资本投资，大多数组织不太可能为了实现工具一致性带来的价值，选择放弃这些工具，转而使用技术负担较重的新工具。&lt;/p&gt;

&lt;p&gt;太长不看：如果你正在寻找一个跨 Kubernetes 和其他系统使用的单一策略引擎，Kyverno 不适合你。&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;策略的复杂度很高。&lt;/p&gt;

&lt;p&gt;反对意见：根据我的经验，大多数 Kubernetes 用户都没有使用包括 PSP 在内的任何策略支持。而 2020 年对在 AWS 上运行容器化工作负载的客户的调查也得到了类似的结果，只有 49% 的客户使用策略。这些用户中的绝大多数都在做的是重复的策略——例如“容器不应该有特权”或“确保所有命名空间都带有给定的标签”或“验证 Pods 没有使用 hostPath 卷”等。“复杂”这个词是相对的，有点主观，但这样的策略表达方式绝对不复杂。Kyverno 允许以最简单的形式编写策略，这反过来又更容易推理和维护。如果要为一个更复杂、更困难的工具支付额外的价格，就应该尽量物尽其用，否则无法获得价值。&lt;/p&gt;

&lt;p&gt;太长不看：如果无需实现高度复杂的策略，Gatekeeper 不会带来好处。&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&#34;结语&#34;&gt;结语&lt;/h2&gt;

&lt;p&gt;Gatekeeper 和 Kyverno 项目本身都是有价值、有能力的策略引擎，每个项目都有各自的优缺点。最终，用户应该根据自己的需求和限制条件进行评估并做出最明智的决定，但作为一般建议，所有生产用户都应该计划使用策略引擎来保护集群的安全并简化 Kubernetes 管理。&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>使用 Kyverno 定义 Kubernetes 策略</title>
      <link>/post/introduce-kyverno/</link>
      <pubDate>Fri, 19 Jul 2019 09:56:01 +0800</pubDate>
      <guid>/post/introduce-kyverno/</guid>
      <description>

&lt;p&gt;Kubernetes 的日常使用过程中，在对象提交给集群之前，我们会有很多机会，很多方法对资源的 Yaml 定义进行检查和处理。很多读者应该也会知道，资源提交之后，还有机会使用 Admission Controller 对资源动动手脚，这其中其实有很多可以提炼出来的标准动作，可以用统一的控制器来进行处理，&lt;a href=&#34;https://github.com/nirmata/kyverno&#34; target=&#34;_blank&#34;&gt;Kyverno&lt;/a&gt; 就是这样一个工具。有了 Kyverno 的帮助，YAML 程序员可以根据条件对资源进行筛选，符合条件的资源可以：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;验证资源：对资源定义进行检查，不符合条件的资源拒绝创建，从而保证集群资源的合规性。&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;修改资源：在资源定义中进行注入，强制资源部分行为的一致性。&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;生成资源：在资源创建时，同时创建相关的资源。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&#34;安装&#34;&gt;安装&lt;/h2&gt;

&lt;p&gt;安装过程是很简单的，安装清单文件位于 &lt;code&gt;https://github.com/nirmata/kyverno/raw/master/definitions/install.yaml&lt;/code&gt;，使用 kubectl 直接部署即可：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;$ kubectl create -f https://github.com/nirmata/kyverno/raw/master/definitions/install.yaml
customresourcedefinition.apiextensions.k8s.io/policies.kyverno.io created
namespace/kyverno created
service/kyverno-svc created
serviceaccount/kyverno-service-account created
clusterrolebinding.rbac.authorization.k8s.io/kyverno-admin created
deployment.extensions/kyverno created
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;有一点需要注意的是资源的类型范围，可以在主进程的命令行参数中设定不需要处理的资源类型，缺省设置为：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;  containers:
    - name: kyverno
      image: nirmata/kyverno:latest
      args: [&amp;quot;--filterKind&amp;quot;,&amp;quot;Node,Event,APIService,Policy,TokenReview,SubjectAccessReview&amp;quot;]
      ports:
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&#34;策略定义&#34;&gt;策略定义&lt;/h2&gt;

&lt;p&gt;安装完成后，就可以编写策略了，策略的规则不算复杂，具体格式可以从 &lt;code&gt;install.yaml&lt;/code&gt; 中的 CRD 定义里面推断出来。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;apiVersion : kyverno.io/v1alpha1
kind: Policy
metadata:
  name: sample-policy
spec:
  rules: # 规则数组，spec 的唯一下级
  - name: check-rule-1
    resource: # 定义选择条件，限制生效范围
      kinds: # 生效对象类型数组，必要字段
      - Deployment
      - StatefulSet
      namespace: default  # 命名空间
      name: &amp;quot;*&amp;quot; # 资源名称
      selector: # 用更加复杂一点的方式来定义选择方式
        matchLabels: # 精确匹配标签
          app: some-app
        matchExpressions: # 表达式匹配标签
          key: &amp;quot;operator&amp;quot;
          operator: In
          values:
          - v2
          - v3
    validate:
      ...
    mutate:
      ...
    generate:
      ...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;resource&lt;/code&gt; 部分是固定的，而 &lt;code&gt;validate&lt;/code&gt; &lt;code&gt;mutate&lt;/code&gt; &lt;code&gt;generate&lt;/code&gt; 三个动作则各有各的结构。&lt;/p&gt;

&lt;p&gt;下面用几个例子来演示一下他的功能。&lt;/p&gt;

&lt;h2 id=&#34;验证资源-validate&#34;&gt;验证资源（validate）&lt;/h2&gt;

&lt;p&gt;定义一个限制特定命名空间下镜像地址的策略如下：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;apiVersion : kyverno.io/v1alpha1
kind: Policy
metadata:
  name: check-registries
spec:
  rules:
  - name: check-registries
    resource:
      kinds:
      - Deployment
      namespace: default
    validate:
      message: &amp;quot;Registry is not allowed&amp;quot;
      pattern:
        spec:
          template:
            spec:
              containers:
              - name: &amp;quot;*&amp;quot;
                image: &amp;quot;docker.io/citizenstig/*&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这个策略文件中，&lt;code&gt;pattern&lt;/code&gt; 部分和我们要处理的 &lt;code&gt;deployment&lt;/code&gt; 文档结构一致，其中支持通配符，可以用它来对目标进行校验，这里我们要求 &lt;code&gt;default&lt;/code&gt; 命名空间中的 &lt;code&gt;Deplyment&lt;/code&gt; 对象，&lt;code&gt;containers&lt;/code&gt; 下的 &lt;code&gt;image&lt;/code&gt; 字段必须符合 &lt;code&gt;docker.io/citizenstig/*&lt;/code&gt; 的通配符要求。&lt;/p&gt;

&lt;p&gt;例如下面的的 Deployment 就无法创建：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: httpbin
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: httpbin
        version: v1
    spec:
      containers:
        - name: httpbin
          image: citizenstig/httpbin
          imagePullPolicy: IfNotPresent
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;$ kubectl apply -f httpbin.yaml
Error from server: error when creating &amp;quot;httpbin.yaml&amp;quot;: admission webhook &amp;quot;nirmata.kyverno.validating-webhook&amp;quot; denied the request:
Policy check-registries failed with following rules;rulename: check-registries;Rule check-registries: Validation has failed, err Failed to validate value citizenstig/httpbin with pattern docker.io/citizenstig/*. Path: /spec/template/spec/containers/0/image/.
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;但是如果我们换个命名空间就没问题了：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;$ kubectl create ns free
kunamespace/free created
$ kubectl apply -f httpbin.yaml -n free
deployment.extensions/httpbin created
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;又或者我们不用 Deployment，直接创建 Pod：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;apiVersion: v1
kind: Pod
metadata:
  name: static-httpbin
spec:
  containers:
    - name: httpbin
      image: citizenstig/httpbin
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;果然就能够创建成功了：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;$ kubectl apply -f pod.yaml
pod/static-httpbin created
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这样的绕过自然是我们不想要的，但是可以改变策略，把限制做到 Pod 上：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;  rules:
  - name: check-registries
    resource:
      kinds:
      - Pod
      namespace: default
    validate:
      message: &amp;quot;Registry is not allowed&amp;quot;
      pattern:
        spec:
          containers:
          - name: &amp;quot;*&amp;quot;
            image: &amp;quot;docker.io/citizenstig/*&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这样更新之后，不管是 Deployment 还是静态 Pod 都无法通过了。&lt;/p&gt;

&lt;p&gt;如果使用 &lt;code&gt;kubectl edit deploy httpbin&lt;/code&gt; 把 &lt;code&gt;image&lt;/code&gt; 字段修改为 &lt;code&gt;docker.io/citizenstig/httpbin&lt;/code&gt;，就能看到  &lt;code&gt;deployment&lt;/code&gt; 能够正常工作了。&lt;/p&gt;

&lt;p&gt;或者我们可以要求所有 Pod 都必须指定 CPU 限制：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;validate:
  message: &amp;quot;resources/limits is needed.&amp;quot;
  pattern:
    spec:
      template:
        spec:
          containers:
          - resources:
              limits:
                cpu: &amp;quot;*&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这个策略提交之后，上面的 Deploy 就再次无法部署了：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;$ kubectl apply -f httpbin.yaml
Error from server: error when creating &amp;quot;httpbin.yaml&amp;quot;: admission webhook &amp;quot;nirmata.kyverno.validating-webhook&amp;quot; denied the request:
...
Path: /spec/template/spec/containers/0/resources/limits/. Expected map[string]interface {}, found &amp;lt;nil&amp;gt;.
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;修改清单，加入资源限制，即可满足条件。&lt;/p&gt;

&lt;h2 id=&#34;修改资源-mutate&#34;&gt;修改资源（mutate）&lt;/h2&gt;

&lt;p&gt;这里也可以做类似自动注入的内容，例如我们可以要求所有 &lt;code&gt;default&lt;/code&gt; 命名空间中的 Deployment，如果 &lt;code&gt;deployment&lt;/code&gt; 标签中有 &lt;code&gt;io=heavy&lt;/code&gt;，则分配到 &lt;code&gt;ssd=true&lt;/code&gt; 的节点上。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;apiVersion : kyverno.io/v1alpha1
kind: Policy
metadata:
  name: assign-ssd
spec:
  rules:
  - name: assign-ssd
    resource:
      kinds:
      - Deployment
      namespace: default
      selector:
        matchLabels:
          io: heavy
    mutate:
      overlay:
        spec:
          template:
            spec:
              nodeSelector:
                ssd: true
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;修改一下上面的 &lt;code&gt;Deployment&lt;/code&gt;，加上标签：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: httpbin
  labels:
    io: heavy
spec:
...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;提交到集群之后，查看变更结果：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;$ kubectl get deployments httpbin -o yaml
apiVersion: extensions/v1beta1
kind: Deployment
...
    spec:
...
      dnsPolicy: ClusterFirst
      nodeSelector:
        ssd: &amp;quot;true&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;看到多出来的 &lt;code&gt;nodeSelector&lt;/code&gt; 字段，如果查看 Pod 信息，也会发现这个 Deployment 的所有 Pod 都分配到了指定的节点上。&lt;/p&gt;

&lt;h2 id=&#34;创建资源-generate&#34;&gt;创建资源（generate）&lt;/h2&gt;

&lt;p&gt;有时候我们在 Kubernetes 上创建资源的时候，可能希望同时提供一些缺省资源，例如一个新的命名空间，我们希望其中包含缺省的 Configmap 或者 SA 或者资源限制。&lt;/p&gt;

&lt;p&gt;例如我们要在新建 &lt;code&gt;test-n&lt;/code&gt; 的命名空间的同时，创建名为 &lt;code&gt;dummy&lt;/code&gt; 的 sa。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;apiVersion : kyverno.io/v1alpha1
kind: Policy
metadata:
  name: auto-sa
spec:
  rules:
  - name: auto-sa
    resource:
      kinds:
      - Namespace
      name: &amp;quot;test-*&amp;quot;
    generate:
      kind: ServiceAccount
      name: dummy
      data:
        spec: {}
        metadata:
          labels:
            source: &amp;quot;webhook&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这个策略生效后，每次我们创建形如 &lt;code&gt;test-*&lt;/code&gt; 的命名空间，其中都会生成对应的名为 &lt;code&gt;dummy&lt;/code&gt; 的 ServiceAccount，并且有标签：&lt;code&gt;source=webhook&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;Generate 还提供了复制对象的方法，例如每个新命名空间中都应该复制一个名为 &lt;code&gt;conn&lt;/code&gt; 的 &lt;code&gt;Configmap&lt;/code&gt;，就可以使用如下策略：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;apiVersion : kyverno.io/v1alpha1
kind: Policy
metadata:
  name: auto-cm
spec:
  rules:
  - name: auto-cm
    resource:
      kinds:
      - Namespace
      name: &amp;quot;test-*&amp;quot;
    generate:
        kind: ConfigMap
        name: conn
        clone:
          namespace: default
          name: conn
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;随意验证一下：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;$ kubectl create configmap conn \
    --from-literal=mysql=mysql \
    --from-literal=mongodb=mongodb
configmap/conn created
$ kubectl create ns test-6
namespace/test-6 created
$ kubectl get cm,sa -n test-6
NAME             DATA   AGE
configmap/conn   2      6s

NAME                     SECRETS   AGE
serviceaccount/default   1         7s
serviceaccount/dummy     1         6s
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这里会发现，随着新的命名空间的创建，新的 SA 和 CM 也都出现了。&lt;/p&gt;

&lt;h2 id=&#34;结论&#34;&gt;结论&lt;/h2&gt;

&lt;p&gt;相对于其他的类似工具，Kyverno 在灵活、强大和易用之间取得了一个很好的平衡，不需要太多学习时间，就能够提供相当方便的功能，官网提供了大量的针对各种场景的样例，非常值得一看。&lt;/p&gt;

&lt;h2 id=&#34;参考链接&#34;&gt;参考链接&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;项目主页：&lt;code&gt;https://kyverno.io/&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
  </channel>
</rss>
