<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>calico | 伪架构师</title>
    <link>/tags/calico/</link>
      <atom:link href="/tags/calico/index.xml" rel="self" type="application/rss+xml" />
    <description>calico</description>
    <generator>Source Themes Academic (https://sourcethemes.com/academic/)</generator><language>zh</language><lastBuildDate>Thu, 07 Oct 2021 16:58:34 +0800</lastBuildDate>
    <image>
      <url>/img/logo-wide.png</url>
      <title>calico</title>
      <link>/tags/calico/</link>
    </image>
    
    <item>
      <title>Kubernetes Semaphore：模块化、无侵入的跨集群通信框架</title>
      <link>/post/k8s-semaphore-intro/</link>
      <pubDate>Thu, 07 Oct 2021 16:58:34 +0800</pubDate>
      <guid>/post/k8s-semaphore-intro/</guid>
      <description>

&lt;p&gt;原文：&lt;a href=&#34;https://uw-labs.github.io/blog/kubernetes,/multicluster/2021/07/21/kube-semaphore-intro.html&#34; target=&#34;_blank&#34;&gt;Kubernetes Semaphore: A modular and nonintrusive framework for cross cluster communication&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;作者：&lt;a href=&#34;https://uw-labs.github.io/blog/&#34; target=&#34;_blank&#34;&gt;UW Labs&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&#34;问题&#34;&gt;问题&lt;/h2&gt;

&lt;p&gt;我们有一个环境，其中包含分属三个不同供应商（AWS、GCP 和私有云）的三个集群，我们希望不同集群中运行的应用能够互相通信，以及：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;跨集群的 Pod 网络和加密能力；&lt;/li&gt;
&lt;li&gt;访问远端 Kubernetes Service 的能力；&lt;/li&gt;
&lt;li&gt;用策略来放行远端集群特定应用，使之能够访问本地端点。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;我们有一个跨集群的三层网络，这样三个集群的节点就能互相访问了。每个集群都是在各个供应商子网中申请的节点：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS：10.66.21.0/24&lt;/li&gt;
&lt;li&gt;GCP：10.22.20.0/24&lt;/li&gt;
&lt;li&gt;私有云：10.88.0.0/24&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;三个集群的 Pod 网络分配如下：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS：10.2.0.0/16&lt;/li&gt;
&lt;li&gt;GCP：10.4.0.0/16&lt;/li&gt;
&lt;li&gt;私有云：10.6.0.0/16&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&#34;依赖项&#34;&gt;依赖项&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Calico CNI：在所有集群中使用 Calico CNI，方案中对 Calico 具有一定依赖；&lt;/li&gt;
&lt;li&gt;CoreDNS：Semaphore-Service-Mirror 对此有依赖；&lt;/li&gt;
&lt;li&gt;跨集群的三层网络。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&#34;现有方案&#34;&gt;现有方案&lt;/h2&gt;

&lt;p&gt;我们对 Istio、Linkerd、Consul 进行了评估，还直接编写了自己的 Envoy Proxy 配置工具。这些方案都能提供上述大部分甚至全部的功能。但是我们认为他们的性价比并不完全适合我们的环境。不同集群之中运行的不同应用构成的网格结构并不总是让人有兴趣的，其中提供的大量功能也无法让所有人受益。我们希望避免使用 Sidecar Proxy，也回避随之而来的额外开销，并确保我们的程序保持尽可能的独立，不受跨集群通信解决方案的影响。&lt;/p&gt;

&lt;h2 id=&#34;设计&#34;&gt;设计&lt;/h2&gt;

&lt;p&gt;上面既然提到要避开 Sidecar 代理，我们希望用一种对运维人员和用户都较为简单的方式来解决问题。&lt;/p&gt;

&lt;p&gt;理想情况下，每个目标都应该以相互隔离的方法来达成——假设用户只是需要对 Pod 通信进行加密，那么只要进行单独的部署即可。对于新用户来说，只需要少量的配置就可以对方案进行试用，并且可以轻松回退。&lt;/p&gt;

&lt;h2 id=&#34;方案&#34;&gt;方案&lt;/h2&gt;

&lt;p&gt;Kube-Semaphore 是一个轻量级框架，为不同 Kubernetes 集群之间的应用，提供了简单安全的通信能力，并且无需对应用和清单进行修改。&lt;/p&gt;

&lt;p&gt;这不是一个服务网格方案，而是要为远端集群提供服务端点和防火墙规则。&lt;/p&gt;

&lt;p&gt;方案由三个独立的工具组成&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Semaphore-Wireguard：负责 Kubernetes 集群之间的流量加密；&lt;/li&gt;
&lt;li&gt;Semaphore-Service-Mirror：负责在无需外部负载均衡器参与的情况下，将一个集群中的服务暴露到另一个集群之中；&lt;/li&gt;
&lt;li&gt;Semaphore-Policy：负载在跨集群的 Pod 间通信里创建防火墙规则。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;为了小型、轻量和尽可能的安全，这个组件是用 Go 编写的，并使用了 Kubernetes 和 Calico 的客户端。远端集群上的部署内容很小，本地控制器仅需要一系列的 Service Account，满足 Watch 资源的权限需求即可。&lt;/p&gt;

&lt;h2 id=&#34;路由和加密&#34;&gt;路由和加密&lt;/h2&gt;

&lt;p&gt;Semaphore-Wireguard 负责在不同集群的节点之间进行加密通信，并在本地主机上加入访问远端 Pod 子网的路由。每个集群的每个节点上都会运行一个 WireGuard 管理器，负责节点之间的点到点通信。它负责生成本地密钥并发现所有远端密钥和端点，并配置与所有远程节点的对等关系。此外，它还负责更新本地路由表，以便通过主机的 WireGuard 接口将所有流量导向远程 Pod 子网。因此，Pod 可以利用所有集群中的节点之间创建的 WireGuard 网状结构，触达远程集群上的 Pod。&lt;/p&gt;

&lt;p&gt;WireGuard 和 Calico 的集群内通信管理协作，形成了所有集群所有节点之间的网状结构，WireGuard 网络承担了节点之间的通信。&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://github.com/utilitywarehouse/semaphore-wireguard/tree/main/deploy/example&#34; target=&#34;_blank&#34;&gt;Semaphore WireGuard 部署样例&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;下图表达了 WireGuard 形成的主机网格，其中的 &lt;code&gt;merit&lt;/code&gt; 就是我们的私有集群：&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/semaphore-wireguard-full-mesh-example.png&#34; alt=&#34;semaphore-wireguard-full-mesh-example&#34; /&gt;&lt;/p&gt;

&lt;h2 id=&#34;service&#34;&gt;Service&lt;/h2&gt;

&lt;p&gt;Semaphore-Service-Mirror 是一个控制器，负责在不同 Kubernetes 集群之间复制服务。此处的镜像服务代表的是一个本地服务，其端点处于远端集群。&lt;/p&gt;

&lt;p&gt;镜像控制器会在本地集群创建服务，并用远程集群中 Pod 的地址来更新端点列表，最终形成一个 ClusterIP 类型的 Service。&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://github.com/utilitywarehouse/semaphore-service-mirror/tree/master/deploy/example&#34; target=&#34;_blank&#34;&gt;控制器部署样例&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;例如 AWS 集群上有一个 Service 对象：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-command&#34;&gt;$ kubectl --context=aws --namespace=sys-log get service fluentd
NAME      TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)             AGE
fluentd   ClusterIP   10.3.88.18   &amp;lt;none&amp;gt;        8888/TCP,8889/TCP   164d
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;它包含的端点：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-command&#34;&gt;$ kubectl --context=aws --namespace=sys-log get endpoints fluentd
NAME      ENDPOINTS                                                  AGE
fluentd   10.2.3.19:8889,10.2.4.19:8889,10.2.7.18:8889 + 3 more...   164d
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;镜像控制器会在 &lt;code&gt;semaphore-service-mirror&lt;/code&gt; 所在的命名空间 &lt;code&gt;sys-semaphore&lt;/code&gt; 中创建对应的 Service 和 Endpoint：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-command&#34;&gt;$ kubectl --context=gcp --namespace=sys-semaphore get service | grep fluentd
aws-sys-log-73736d-fluentd   ClusterIP   10.5.184.192   &amp;lt;none&amp;gt;        8888/TCP,8889/TCP   25d

$ kubectl --context=gcp --namespace=sys-semaphore get endpoints | grep fluentd
aws-sys-log-73736d-fluentd   10.2.3.19:8889,10.2.4.19:8889,10.2.7.18:8889 + 3 more...   17d
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我们会发现，这个 Service 指向是远程集群：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-command&#34;&gt;$ kubectl --context=gcp --namespace=sys-semaphore describe service aws-sys-log-73736d-fluentd | grep Endpoints
Endpoints:         10.2.3.19:8888,10.2.4.19:8888,10.2.7.18:8888
Endpoints:         10.2.3.19:8889,10.2.4.19:8889,10.2.7.18:8889
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;控制器会监控远端资源，并根据事件进行更新，这样镜像服务就会及时地指向可用的端点。&lt;/p&gt;

&lt;p&gt;最后对 CoreDNS &lt;a href=&#34;https://github.com/utilitywarehouse/semaphore-service-mirror#coredns-config-example&#34; target=&#34;_blank&#34;&gt;进行配置&lt;/a&gt;，就能够更好地进行解析了：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-command&#34;&gt;$ drill fluentd.sys-log.svc.cluster.aws
;; -&amp;gt;&amp;gt;HEADER&amp;lt;&amp;lt;- opcode: QUERY, rcode: NOERROR, id: 51067
;; flags: qr aa rd ; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 
;; QUESTION SECTION:
;; fluentd.sys-log.svc.cluster.aws.     IN      A

;; ANSWER SECTION:
fluentd.sys-log.svc.cluster.aws.        5       IN      A       10.5.184.192
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这样我们的 Pod 在无需关注镜像细节的情况下使用友好的服务名称了。&lt;/p&gt;

&lt;h2 id=&#34;policy&#34;&gt;Policy&lt;/h2&gt;

&lt;p&gt;Semaphore-Policy 是一个用于创建防火墙策略的组件，用于限制来自远端集群的访问。这个组件会创建用于 Calico 网络策略的 IP 组，来定义允许发起的流量。控制器的唯一任务就是根据标签选择器来监控远端集群的 Pod，然后根据监控结果在本地创建 NetworkSets。接下来，可以使用简单的标签来描述 Calico Network Policy，方便地实现跨集群防火墙规则。&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://github.com/utilitywarehouse/semaphore-policy/tree/main/deploy&#34; target=&#34;_blank&#34;&gt;策略控制器部署样例&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;假设 GCP 集群中存在如下部署：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-command&#34;&gt;$ kubectl --context=gcp --namespace=sys-log get po -o wide -l policy.semaphore.uw.io/name=forwarder
NAME              READY   STATUS    RESTARTS   AGE     IP          NODE                                      NOMINATED NODE   READINESS GATES
forwarder-4jdm6   1/1     Running   0          3d20h   10.4.1.3    worker-k8s-exp-1-4l87.c.uw-dev.internal   &amp;lt;none&amp;gt;           &amp;lt;none&amp;gt;
forwarder-6ztl4   1/1     Running   0          3d20h   10.4.0.13   worker-k8s-exp-1-2868.c.uw-dev.internal   &amp;lt;none&amp;gt;           &amp;lt;none&amp;gt;
forwarder-klxdc   1/1     Running   0          4h27m   10.4.4.2    master-k8s-exp-1-j5f8.c.uw-dev.internal   &amp;lt;none&amp;gt;           &amp;lt;none&amp;gt;
forwarder-m9k27   1/1     Running   0          4h27m   10.4.5.2    master-k8s-exp-1-fc0b.c.uw-dev.internal   &amp;lt;none&amp;gt;           &amp;lt;none&amp;gt;
forwarder-n6nsn   1/1     Running   0          4h27m   10.4.3.3    master-k8s-exp-1-31rv.c.uw-dev.internal   &amp;lt;none&amp;gt;           &amp;lt;none&amp;gt;
forwarder-n8vnj   1/1     Running   0          3d20h   10.4.2.4    worker-k8s-exp-1-mdd7.c.uw-dev.internal   &amp;lt;none&amp;gt;           &amp;lt;none&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这是一个 &lt;code&gt;sys-log&lt;/code&gt; 命名空间里名为 &lt;code&gt;forwarder&lt;/code&gt; 的 DaemonSet。为了让控制器在远程集群中创建所需资源，需要给这个 Daemonset 中的 Pod 加入标签 &lt;code&gt;policy.semaphore.uw.io/name=forwarder&lt;/code&gt;。加入标签之后，AWS 集群中的控制器就会创建对应的 &lt;code&gt;GlobalNetworkSet&lt;/code&gt;：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-command&#34;&gt;$ kubectl --context=aws describe GlobalNetworkSet gcp-sys-log-forwarder
Name:         gcp-sys-log-forwarder
Namespace:
Labels:       managed-by=semaphore-policy
              policy.semaphore.uw.io/cluster=gcp
              policy.semaphore.uw.io/name=forwarder
              policy.semaphore.uw.io/namespace=sys-log
Annotations:  projectcalico.org/metadata: {&amp;quot;uid&amp;quot;:&amp;quot;c7569765-a47d-424c-9533-80e4a7c201d6&amp;quot;,&amp;quot;creationTimestamp&amp;quot;:&amp;quot;2021-04-09T15:04:43Z&amp;quot;}
API Version:  crd.projectcalico.org/v1
Kind:         GlobalNetworkSet
Spec:
  Nets:
    10.4.5.2/32
    10.4.4.2/32
    10.4.1.3/32
    10.4.0.13/32
    10.4.3.3/32
    10.4.2.4/32
Events:  &amp;lt;none&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这个组合包含了远端对象的 IP 地址，可以用于 Calico 的网络策略，定义允许进入的流量：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;apiVersion: crd.projectcalico.org/v1
kind: NetworkPolicy
metadata:
  name: allow-to-fluentd
spec:
  selector: app.kubernetes.io/name == &#39;fluentd&#39;
  types:
    - Ingress
  ingress:
    - action: Allow
      protocol: TCP
      source:
        selector: &amp;gt;-
          policy.semaphore.uw.io/name == &#39;forwarder&#39; &amp;amp;&amp;amp;
          policy.semaphore.uw.io/namespace == &#39;sys-log&#39; &amp;amp;&amp;amp;
          policy.semaphore.uw.io/cluster == &#39;gcp&#39;
        namespaceSelector: global()
      destination:
        ports:
          - 8889
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上边的规则允许来自远端 &lt;code&gt;forwarder&lt;/code&gt; 到本地 &lt;code&gt;fluentd&lt;/code&gt; 的访问。&lt;/p&gt;

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

&lt;p&gt;这套东西对我们来说很有用，但是它的局限性也是显而易见的——如果恰巧适用于读者的环境和需求，那自然很好；如果不是，也希望读者在本文中得到一点启发。&lt;/p&gt;

&lt;p&gt;没有 Calico 和 WireGuard，这个方案可能就难于落地了，方案的复杂部分主要是由这两个项目完成的，也正因为此，我们对我们的方案充满信心，这里对他们致以崇高敬意。&lt;/p&gt;

&lt;h2 id=&#34;相关链接&#34;&gt;相关链接&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Semaphore WireGuard：&lt;code&gt;https://github.com/utilitywarehouse/semaphore-wireguard&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Semaphore WireGuard 部署示例：&lt;code&gt;https://github.com/utilitywarehouse/semaphore-wireguard/tree/main/deploy/example&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Semaphore Service Mirror：&lt;code&gt;https://github.com/utilitywarehouse/semaphore-service-mirror&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Semaphore Service Mirror 部署示例：&lt;code&gt;https://github.com/utilitywarehouse/semaphore-service-mirror/tree/master/deploy/example&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Semaphore Policy：&lt;code&gt;https://github.com/utilitywarehouse/semaphore-policy&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Semaphore Policy 部署示例：&lt;code&gt;https://github.com/utilitywarehouse/semaphore-policy/tree/main/deploy&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>Linux Conntrack 的短板</title>
      <link>/post/when-linux-conntrack-is-no-longer-your-friend/</link>
      <pubDate>Thu, 02 May 2019 16:27:45 +0800</pubDate>
      <guid>/post/when-linux-conntrack-is-no-longer-your-friend/</guid>
      <description>

&lt;p&gt;原文：&lt;a href=&#34;https://www.tigera.io/blog/when-linux-conntrack-is-no-longer-your-friend/&#34; target=&#34;_blank&#34;&gt;When Linux conntrack is no longer your friend&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;作者：&lt;a href=&#34;https://www.linkedin.com/in/alexpollitt&#34; target=&#34;_blank&#34;&gt;Alex Pollitt&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Linux 内核中，&lt;code&gt;conntrack&lt;/code&gt; 的是网络栈的核心功能之一。它使得内核能够跟踪所有逻辑网络连接或者流量，因此能够鉴别构成流量的数据包，从而对每个数据流进行一致的处理。&lt;/p&gt;

&lt;p&gt;Conntrack 是一个重要的内核功能，是一些关键用例的基础：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;NAT 能够根据 Conntrack 的信息，对构成数据流的所有数据包进行翻译。例如当 Pod 访问 Kubernetes 服务的时候，kube-proxy 的负载均衡功能会使用 NAT 把连接重定向给指定的后端 Pod。Conntrack 负责对指定连接进行记录，发送到 Service IP 的数据包会被发送给同一个后端 Pod，从后端 Pod 返回的数据包会反向送回源 Pod。&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Calico 这样的有状态防火墙，依赖 Conntrack 的信息来精确的将响应流量纳入白名单。用户如果编写了一个“允许 Pod 连接所有远端 IP”的策略，无需进行其它工作就能够允许所有的返回流量（如果没有这种功能，就必须加入一个不那么安全的规则：允许所有目标是该 Pod 的流量）。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;另外 Conntrack 还能够提高性能（降低 CPU 和延迟），这是因为只有第一个数据包需要完成整个网络栈的处理，参见 &lt;a href=&#34;https://www.tigera.io/blog/comparing-kube-proxy-modes-iptables-or-ipvs/&#34; target=&#34;_blank&#34;&gt;Comparing kube-proxy modes&lt;/a&gt; 一文，其中包含了这方面的例子。&lt;/p&gt;

&lt;p&gt;然而 Conntrack 也有其限制。&lt;/p&gt;

&lt;h2 id=&#34;那么它哪里不行了&#34;&gt;那么它哪里不行了？&lt;/h2&gt;

&lt;p&gt;Conntrack 的存储表有一个可配置的最大容量，如果满了，连接通常会被拒绝和丢弃。在多数负载情况下，这个表的容量是足够的，不会出现这种问题。但是有些场景下，Conntrack 也会不够用：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;最常见的情况是，如果你的服务器需要同时处理非常大数量的活动连接。例如你的 Conntrack 表设置为 128k 的容量，但是你的并发连接超过了 128k，这肯定会出问题。&lt;/li&gt;
&lt;li&gt;另外一种情况没那么常见——每秒处理极大数量的连接。这些连接就算再短，Linux 还会在超时（通常是 120 秒）期限内对其进行跟踪。例如如果你的 Conntrack 容量设置为 128k，尝试每秒钟处理 1100 个连接，这就会超出 Conntrack 表的限制（128k/120秒 = 1092 连接/秒）。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;有个别的负载类型就符合这种条件。另外如果在一个恶劣环境中，用大量的半开连接冲击服务器，就能造成拒绝服务攻击的效果。两种情况下，Conntrack 都会成为系统中的瓶颈。有些情况下，通过提高 Conntrack 数据表容量或者降低 Conntrack 的超时时间（如果调节失误，可能会造成更多痛苦）就能解决问题。别的场景中，可能需要跨过 Conntrack 来处理这种威胁。&lt;/p&gt;

&lt;h2 id=&#34;一个真实的案例&#34;&gt;一个真实的案例&lt;/h2&gt;

&lt;p&gt;我们合作的某大型 SaaS 供应商，他们有一组运行于物理机（不是虚拟化，也不是容器化）上的 Memcached 服务器，每台服务器每秒都要处理 50k 以上的短连接。这可不是标准的 Linux 配置能够承受的。&lt;/p&gt;

&lt;p&gt;他们曾经使用提高 Conntrack 数据表容量和降低超时时间的方式进行调整，但是这种调整非常脆弱，内存占用增长极大（GB 级），超时时间过短让 Conntrack 的益处（降低 CPU 和数据包延迟）也大为减少。&lt;/p&gt;

&lt;p&gt;因此他们转向了 Calico，Calico 的网络策略允许指定部分流量绕过 Conntrack。这一措施让他们得到了期待的性能，并且还得到了 Calico 带来的安全优势。&lt;/p&gt;

&lt;h2 id=&#34;绕开-conntrack-的妥协之处&#34;&gt;绕开 Conntrack 的妥协之处&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Do-not-track&lt;/code&gt; 策略通常是对称的。上面谈到的 SaaS 供应商案例，他们的工作负载是内部的，因此他们可以非常严格的在工作负载和 Memcached 之间进行白名单设置。&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;code&gt;Do-not-track&lt;/code&gt; 策略是不知道连接的方向的。所以只要 Memcached 服务器知道 Memcached 客户端的源端口，他都可以尝试连接。但是如果为 Memcached 客户端定义了正确的策略，那么还可以在客户端拒绝这些连接。&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;code&gt;Do-not-track&lt;/code&gt; 对每个数据包生效，而通常的网络策略只对数据流中的第一个数据包生效。这样会提高 CPU 的消耗。但是在短连接环境下，网络策略造成的消耗还是低于 &lt;code&gt;Conntrack&lt;/code&gt; 过程的消耗的。例如 SaaS 供应商的例子中，每连接中的数据包都很少，所以使用策略处理每个数据包的过程中造成的多余开销也就可以接受了。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&#34;测试一下&#34;&gt;测试一下&lt;/h2&gt;

&lt;p&gt;我们测试了单一 Memcached 服务器 Pod 以及运行在远程节点上的多个客户端的场景，这种场景能方便的产生大量链接。Memcached Pod 所在的节点有 8 个 CPU 核心，Conntrack 表容量为 512k（主机的标准设置）。我们在几种方案中进行了比较：没有网络策略；Calico 通用网络策略；Calico &lt;code&gt;Do-not-track&lt;/code&gt; 策略。&lt;/p&gt;

&lt;p&gt;第一个测试中，我们限制每秒 4000 个连接，以此来方便的观察 CPU 方面的差异。无策略和正常策略的测试用例，其结果没有差异，而 &lt;code&gt;Do-not-track&lt;/code&gt; 策略降低了 20%。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/Picture2-1.png&#34; alt=&#34;CPU&#34; /&gt;&lt;/p&gt;

&lt;p&gt;第二个测试中，我们尽量的提高连接数量，这样就可以观察到 Memcached 能够处理的每秒最大连接数量了。如前所述，无策略和普通策略的情况里，受到 Conntrack 数据表容量的影响，只能达到每秒 4000 多连接的能力（512k / 120s = 4,369 connections/s）。而 &lt;code&gt;Do-not-track&lt;/code&gt; 策略下，连接数达到了 60k 每秒，没有出现问题。我们相信，可以通过更多的客户端来产生更高的负载数量，但是这一数字已经证实了我们提到的观点了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/Picture1-1.png&#34; alt=&#34;CONNECTION&#34; /&gt;&lt;/p&gt;

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

&lt;p&gt;Conntrack 是一个重要的内核功能。大多数场景下，它都能发挥很好的作用。然而有些小众场景中，Conntrack 的开销会大于其收益。在这种情况下，Calico 的网络策略可以通过选择性的绕过 Conntrack，并提高网络安全性。而对其他流量，Conntrack 还是你的好朋友。&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Calico 3.5：根据节点标签分配 IP 地址</title>
      <link>/post/calico-assigning-ip/</link>
      <pubDate>Fri, 08 Feb 2019 11:15:50 +0800</pubDate>
      <guid>/post/calico-assigning-ip/</guid>
      <description>

&lt;p&gt;原文：&lt;a href=&#34;https://docs.projectcalico.org/v3.5/usage/assigning-ip-addresses-topology&#34; target=&#34;_blank&#34;&gt;Assigning IP addresses based on topology&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&#34;关于-ip-地址的分配&#34;&gt;关于 IP 地址的分配&lt;/h2&gt;

&lt;p&gt;Calico 能够进行配置，为不同拓扑指定 IP 地址池。例如可能希望某些机架、地区、或者区域能够从同一个 IP 池中获取地址。这对于降低路由数量或者配合防火墙策略的要求会很有帮助。&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://docs.projectcalico.org/v3.5/reference/cni-plugin/configuration#ipam&#34; target=&#34;_blank&#34;&gt;cni 插件配置参考中的 IP 地址管理章节&lt;/a&gt;中包含了三种分配 IP 地址的方式。Kubernetes 注解方式只能用于 Namespace 或者 Pod 一级。剩下的只有两个办法，CNI 配置或者是基于节点选择器的 IP 池，相对于 CNI 配置的方式来说，节点选择器方案省去了修改本地文件的麻烦。&lt;/p&gt;

&lt;p&gt;在更高层次上，基于节点选择器的 IP 地址分配方法就是给节点设置标签，然后用节点选择器选择对应的 IP 地址池进行分配。后面的内容中将给出一个详细的例子，用这种方式来设置一种机架亲和方式的 IP 地址分配方案。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;如果 Calico 无法根据上述顺序来决定一个 IP 地址池，或者在选定的地址池中找不到可用的 IP 地址，那么这一工作负载就不会分到 IP 地址，无法启动。为了防止这种情况的发生，我们建议所有节点至少有一个合适的地址池。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&#34;先决条件&#34;&gt;先决条件&lt;/h2&gt;

&lt;p&gt;这一功能需要 Calico 在 ETCD 模式下工作。&lt;/p&gt;

&lt;h2 id=&#34;示例-kubernetes&#34;&gt;示例：Kubernetes&lt;/h2&gt;

&lt;p&gt;本例中，我们会创建一个集群，其中包含四个节点，分布在两个机架上，每个机架各两台。示意如下：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-plain&#34;&gt;       -------------------
       |    router       |
       -------------------
       |                 |
---------------   ---------------
| rack-0      |   | rack-1      |
---------------   ---------------
| kube-node-0 |   | kube-node-2 |
- - - - - - - -   - - - - - - - -
| kube-node-1 |   | kube-node-3 |
- - - - - - - -   - - - - - - - -
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Pod IP 地址范围为 &lt;code&gt;192.168.0.0/16&lt;/code&gt;，我们进行如下设计：保留 &lt;code&gt;192.168.0.0/24&lt;/code&gt; 给 &lt;code&gt;rack-0&lt;/code&gt;, &lt;code&gt;192.168.1.0/24&lt;/code&gt; 给 &lt;code&gt;rack-1&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;要设置一个没有缺省地址池的的 Calico，首先运行 &lt;code&gt;calicoctl get ippool -o wide&lt;/code&gt;，会看到已经创建了一个 &lt;code&gt;192.168.0.0/16&lt;/code&gt; 的地址池：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-plain&#34;&gt;NAME                  CIDR             NAT    IPIPMODE   DISABLED   SELECTOR
default-ipv4-ippool   192.168.0.0/16   true   Always     false      all()
&lt;/code&gt;&lt;/pre&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;删除缺省地址池&lt;/p&gt;

&lt;p&gt;&lt;code&gt;default-ipv4-ippool&lt;/code&gt; 地址池已经存在，并占据了整个 &lt;code&gt;/16&lt;/code&gt; 块，因此必须删除：&lt;code&gt;calicoctl delete ippools default-ipv4-ippool&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;给 Node 打标签。&lt;/p&gt;

&lt;p&gt;要给特定节点分配地址池，节点必须用标签进行标识：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-plain&#34;&gt;kubectl label nodes kube-node-0 rack=0
kubectl label nodes kube-node-1 rack=0
kubectl label nodes kube-node-2 rack=1
kubectl label nodes kube-node-3 rack=1
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;为每个机架创建地址池&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-plain&#34;&gt;calicoctl create -f -&amp;lt;&amp;lt;EOF
apiVersion: projectcalico.org/v3
kind: IPPool
metadata:
  name: rack-0-ippool
spec:
  cidr: 192.168.0.0/24
  ipipMode: Always
  natOutgoing: true
  nodeSelector: rack == &amp;quot;0&amp;quot;
EOF
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code class=&#34;language-plain&#34;&gt;calicoctl create -f -&amp;lt;&amp;lt;EOF
apiVersion: projectcalico.org/v3
kind: IPPool
metadata:
  name: rack-1-ippool
spec:
  cidr: 192.168.1.0/24
  ipipMode: Always
  natOutgoing: true
  nodeSelector: rack == &amp;quot;1&amp;quot;
EOF
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;现在就创建了两个地址池，使用 &lt;code&gt;calicoctl get ippool -o wide&lt;/code&gt; 进行查看：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-plain&#34;&gt;NAME                  CIDR             NAT    IPIPMODE   DISABLED   SELECTOR
rack-1-ippool         192.168.0.0/24   true   Always     false      rack == &amp;quot;0&amp;quot;
rack-2-ippool         192.168.1.0/24   true   Always     false      rack == &amp;quot;1&amp;quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;检查地址池的工作状态&lt;/p&gt;

&lt;p&gt;创建一个 Nginx 的 Deployment，其中包含五个副本，保证分配到每一个节点上。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-plain&#34;&gt;kubectl run nginx --image nginx --replicas 5
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;检查新的 Pod 是否已经根据所在机架获得了应有的 IP 地址。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-plain&#34;&gt;NAME                   READY   STATUS    RESTARTS   AGE    IP             NODE          NOMINATED NODE   READINESS GATES
nginx-5c7588df-prx4z   1/1     Running   0          6m3s   192.168.0.64   kube-node-0   &amp;lt;none&amp;gt;           &amp;lt;none&amp;gt;
nginx-5c7588df-s7qw6   1/1     Running   0          6m7s   192.168.0.129  kube-node-1   &amp;lt;none&amp;gt;           &amp;lt;none&amp;gt;
nginx-5c7588df-w7r7g   1/1     Running   0          6m3s   192.168.1.65   kube-node-2   &amp;lt;none&amp;gt;           &amp;lt;none&amp;gt;
nginx-5c7588df-62lnf   1/1     Running   0          6m3s   192.168.1.1    kube-node-3   &amp;lt;none&amp;gt;           &amp;lt;none&amp;gt;
nginx-5c7588df-pnsvv   1/1     Running   0          6m3s   192.168.1.64   kube-node-2   &amp;lt;none&amp;gt;           &amp;lt;none&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;可以看到，IP 地址的是根据节点（所在的机架）来选择了对应的地址池进行分配的。&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
</description>
    </item>
    
    <item>
      <title>借助 Calico，管窥 Kubernetes 网络策略</title>
      <link>/post/network-policy-basic-calico/</link>
      <pubDate>Thu, 09 Mar 2017 20:37:41 +0800</pubDate>
      <guid>/post/network-policy-basic-calico/</guid>
      <description>

&lt;p&gt;Kubernetes 提出了一系列 CXI 的标准容器接口，其中的 CNI 以插件方式支持多种网络。新增的 networkpolicy API 对象，提供了对网络策略的支持，本文以 Calico 为例，实际操作一个网络策略的创建和测试。&lt;/p&gt;

&lt;h2 id=&#34;环境准备&#34;&gt;环境准备&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;一个 Kubernetes 集群&lt;/li&gt;
&lt;li&gt;Kubelet 和 API Server 都开启了 &lt;code&gt;--allow_privileged=true&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Kubelet 指定使用 CNI ：&lt;code&gt;--network-plugin=cni&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;为了避免某些不可描述的网络设施的影响，建议下载几个镜像

&lt;ul&gt;
&lt;li&gt;quay.io/calico/node:v1.0.2&lt;/li&gt;
&lt;li&gt;calico/cni:v1.5.6&lt;/li&gt;
&lt;li&gt;calico/kube-policy-controller:v0.5.2&lt;/li&gt;
&lt;li&gt;calico/ctl:v1.0.2&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&#34;运行-calico&#34;&gt;运行 Calico&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;下载 &lt;code&gt;http://docs.projectcalico.org/v2.0/getting-started/kubernetes/installation/hosted/calico.yaml&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;如果用私库镜像，需要修改其中的几个镜像地址&lt;/li&gt;
&lt;li&gt;修改 &lt;code&gt;data/etcd_endpoints&lt;/code&gt; 的数据为可访问的 etcd 的地址。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;kubectl create -f calico.yaml&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;这里在 kube-system 中创建了一个 DaemonSet 和一个 Deployment，分别用于提供 CNI 支持和网络策略支持。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ kubectl get deployment,daemonset,svc --all-namespaces                                          [9:55:14]
NAMESPACE     NAME                              DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
kube-system   deploy/calico-policy-controller   1         1         1            1           10h

NAMESPACE     NAME             DESIRED   CURRENT   READY     NODE-SELECTOR   AGE
kube-system   ds/calico-node   2         2         2         &amp;lt;none&amp;gt;          10h

NAMESPACE   NAME             CLUSTER-IP        EXTERNAL-IP   PORT(S)   AGE
default     svc/kubernetes   172.200.0.1       &amp;lt;none&amp;gt;        443/TCP   19h
default     svc/nginx        172.200.183.204   &amp;lt;none&amp;gt;        80/TCP    9h
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&#34;网络策略&#34;&gt;网络策略&lt;/h2&gt;

&lt;p&gt;为测试效果，我们首先创建一个 Namespace&lt;/p&gt;

&lt;p&gt;&lt;code&gt;kubectl create ns policy&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;然后是 Nginx 部署和服务：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;---
kind: ReplicationController
apiVersion: v1
metadata:
  name: nginx
  labels:
    name: nginx
spec:
  replicas: 1
  selector:
    name: nginx
  template:
    metadata:
      labels:
        name: nginx
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
          protocol: TCP

---
kind: Service
apiVersion: v1
metadata:
  name: nginx
  labels:
    name: nginx
spec:
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
  selector:
    name: nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;然后我们用 alpine 镜像测试一下对这一服务进行访问：&lt;/p&gt;

&lt;p&gt;&lt;code&gt;kubectl run alpine --rm -it --image=alpine sh&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;运行成功后，在 Alpine 的 Shell 中输入：&lt;/p&gt;

&lt;p&gt;&lt;code&gt;wget -O - -T 5 http://nginx&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;会出现 Nginx 的缺省页面的代码。&lt;/p&gt;

&lt;p&gt;接下来我们给 Default Namespace 加一个缺省拒绝访问的注解：&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$ kubectl annotate ns default &amp;quot;net.beta.kubernetes.io/network-policy={\&amp;quot;ingress\&amp;quot;: {\&amp;quot;isolation\&amp;quot;: \&amp;quot;DefaultDeny\&amp;quot;}}&amp;quot;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;重复测试过程，会发现超时错误。&lt;/p&gt;

&lt;p&gt;我们来创建一条策略：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;kind: NetworkPolicy
apiVersion: extensions/v1beta1
metadata:
  name: access-nginx
spec:
  podSelector:
    matchLabels:
      run: nginx
  ingress:
    - from:
      - podSelector:
          matchLabels:
            access: &amp;quot;true&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;很容易理解，对于符合 &amp;ldquo;run=nginx&amp;rdquo; 的 Pod，只有 &amp;ldquo;access=true&amp;rdquo; 的 Pod 能够访问&lt;/p&gt;

&lt;p&gt;给 Alpine 带上标签重新运行：&lt;/p&gt;

&lt;p&gt;&lt;code&gt;kubectl run alp --image=alpine --labels=&amp;quot;access=true&amp;quot; --rm -ti sh&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;重新 wget，会发现访问能力已经恢复。&lt;/p&gt;

&lt;p&gt;本文主要线索来自官方示例：&lt;code&gt;https://kubernetes.io/docs/getting-started-guides/network-policy/walkthrough/&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;安装方法来自 Calico 官网。&lt;/p&gt;

&lt;p&gt;这只是一个很入门的介绍，后续会有更多进一步的尝试。&lt;/p&gt;
</description>
    </item>
    
  </channel>
</rss>
