https://github.com/eip-work/kuboard-press前面我们讨论了Linux和容器网络的关键组件,现在开始kubernetes网络之旅。在本文中我们将讨论pod如何从内部和外部连接到集群。介绍kubernetes的内部组件是如何连接的。
kubernetes网络主要解决以下四个方面的问题:
- Highly-coupled container-to-container communications
- Pod-to-Pod communications
- Pod-to-Service communications
- External-to-Service communications
Docker网络模型默认使用一个虚拟网桥网络,它定义在每个主机上,是一个用于连接容器的私有网络。容器的IP地址被分配了一个私有IP地址,这意味着运行在不同机器上的容器不能相互通信。开发人员必须将主机端口映射到容器端口,然后用Docker代理跨节点的通信。在这种情况下,Docker管理员要负责避免容器之间的端口冲突。kubernetes有着不同的网络处理方式。
Kubernetes网络模型
Container Network Interface、CNI- 所有容器必须在没有NAT的情况下与每个容器通信;
- 节点可以在不使用NAT的情况下与容器通信;
- 容器的IP地址与将自己视为外部容器的IP地址相同。
pod以下示例是一个最小的pod定义。省略了许多选项,kubernetes管理各种字段,比如pod的状态,并且是只读的。
apiVersion: v1 kind: Pod metadata: name: go-web namespace: default spec: containers: - name: go-web image: go-web:v0.0.1 ports: - containerPort: 8080 protocol: TCP
Custom Resource Definitions,CRDpod本身是临时的,这意味这它们会被删除,并被新版本所取代。对于熟悉半永久的、传统的物理或虚拟机的开发人员和操作人员来说,pod的短生命周期是一个主要的惊喜和挑战。在pod的生命周期中,本地磁盘状态、节点调度和IP地址都将被定期替换。
--portiptables使用自己的IP地址创建和删除pod可能会给不理解这种行为的初学者带来问题。假设我们在Kubernetes上运行一个小型服务,其部署形式是带有三个pod副本。当有人在部署中更新容器镜像时,Kubernetes执行滚动升级,删除旧的pod并使用新的容器镜像创建新的pod。这些新的pod可能会有新的IP地址,使旧的IP地址不可达。在配置或DNS记录中手动引用pod ip可能是一个常见的初学者错误,但它们无法解决。此错误是服务和端点试图解决的问题。
当显式创建pod时,可以指定IP地址。StatefulSets是一种用于数据库等工作负载的内置工作负载类型,它维护一个pod标识概念,并为一个新的pod提供与它所替代的pod相同的名称和IP地址。还有其他第三方CRD形式的示例,可以为特定的网络目的编写CRD。
每个Kubernetes节点运行一个名为Kubelet的组件,该组件管理节点上的pod。Kubelet中的网络功能来自于与节点上的CNI插件的API交互。CNI插件管理pod IP地址和单个容器网络配置。CNI定义了一个标准接口来管理容器的网络。将CNI作为接口的原因是要有一个可互操作的标准,其中有多个CNI插件实现。CNI插件负责分配pod的IP地址,并维护所有(适用的)pod之间的路由。Kubernetes没有附带默认的CNI插件,这意味着在Kubernetes的标准安装中,pod不能使用网络。
让我们开始讨论CNI和不同的网络设计如何启用pod网络。
节点和Pod网络设计
10.1.0.0/16一般来说,pod没有MAC地址。因此,L2连接pod是不可能的。CNI将为pods确定这一点。
Kubernetes中对L3与外部的连接没有要求。尽管大多数集群都有互联网连接,但出于安全原因,有些集群需要隔离。
我们将广泛讨论入口ingress(离开主机或集群的流量)和出口engress(进入主机或集群的流量)。我们在这里使用的“ingress”不应该与Kubernetes的ingress资源相混淆,它是一种将流量路由到Kubernetes服务的特定HTTP机制。
有三种不同的方法来构建集群网络:isolated network、flat network和island network
Isolated Networks
在一个孤立的集群网络中,节点在更广泛的网络上是可路由的(即,不属于集群的主机可以到达集群中的节点),但pod则不能。如图2所示。Pod不能到达集群之外的其他pod(或任何其他主机)。
由于集群不能从更广泛的网络路由,多个集群甚至可以使用相同的IP地址空间。如果外部系统或用户应该能够访问Kubernetes API,那么Kubernetes API服务器需要能够从更广泛的网络路由。许多Kubernetes供应商都有类似的“安全集群”选项,集群和互联网之间不可能有直接通信。
如果集群的工作负载允许/需要这样的设置(例如用于批处理的集群),那么与本地集群的隔离可以极大地提高安全性。然而,这对所有集群都是不合理的。大多数集群将需要访问和/或由外部系统访问,例如必须支持依赖于更广泛的internet的服务的集群。负载均衡器和代理可以用来突破这个障碍,并允许互联网流量进入或离开孤立的集群。
Flat Networks
在flat network中,所有pod都有一个IP地址,可以从更广泛的网络路由。排除防火墙规则,网络上的任何主机都可以路由到集群内部或外部的任何pod。这种配置在网络简单性和性能方面有许多优点。Pod可以直接连接到网络中的任意主机。
在图3中,两个集群中没有两个节点的pod cidr重叠,因此不会给两个pod分配相同的IP地址。因为更广泛的网络可以将每个pod的IP地址路由到该pod的节点,所以网络上的任何主机都可以与任何pod连接。
这种开放性允许任何拥有足够服务发现数据的主机决定哪个pod将接收这些数据包。集群外部的负载均衡器可以对pod进行负载平衡,比如另一个集群中的gRPC客户端。
外部pod流量(以及传入的pod流量,当连接的目的地是特定的pod IP地址时)具有低延迟和低开销。任何形式的代理或包重写都会产生延迟和处理成本,这虽然很小,但并非微不足道(特别是在涉及许多后端服务的应用程序体系结构中,每次延迟都会累加)。
不幸的是,这个模型需要为每个集群提供一个大的、连续的IP地址空间(即,一个范围内的每个IP地址都在你的控制之下的IP地址范围)。Kubernetes需要一个单独的CIDR用于pod IP地址(对于每个IP族)。该模型可以通过私有子网(如10.0.0.0/8或172.16.0.0/12)实现;然而,使用公共IP地址(尤其是IPv4地址)要困难得多,成本也高得多。管理员需要使用NAT将运行在私有IP地址空间的集群连接到互联网。
除了需要一个大的IP地址空间外,管理员还需要一个易于编程的网络。CNI插件必须分配pod的IP地址,并确保存在到给定pod节点的路由。
在云提供商环境中,在私有子网上的flat networks很容易实现。绝大多数云提供商网络将提供大型私有子网,并拥有用于IP地址分配和路由管理的API(甚至是已经存在的CNI插件)。
Island Networks
iptables共享一个IP地址,同时也使用NAT隐藏个人pod IP地址。跨集群边界的基于IP地址的防火墙和识别变得困难。在集群中,哪个IP地址是哪个pod(因此也是哪个应用程序)仍然很明显。其他集群或更广泛网络上的其他主机中的pod将不再具有这种映射。基于IP地址的防火墙和允许列表本身并不是足够的安全性,但却是一个有价值的、有时是必需的层。
kube-controller-managerkube-controller-manager配置
kube-controller-managerkube-controller-managerKube-controller-manager--allocate-node-cidrs--CIDR-allocator-type string--cluster-CIDR--allocate-node-cidrskube-controller-managerIPv6DualStack--cluster-CIDR--configure-cloud-routesallocate-node-cidrs--node-CIDR-mask-size2^(node-CIDR-mask-size)--node-CIDR-mask-size-ipv4--node-CIDR-mask-size-ipv6--service-cluster-ip-range--allocate-node-cidrskube-controller-managerIPv6DualStack--service-cluster-ip-range现在我们已经讨论了Kubernetes控制平面中的高级网络架构和网络配置,让我们进一步研究Kubernetes工作节点如何处理网络。
Kubelet
Kubelet是一个运行在集群中的每个工作节点上的二进制文件。在较高的级别上,Kubelet负责管理调度到节点的任何pod,并为其上的节点和pod提供状态更新。然而,Kubelet主要充当节点上其他软件的协调器。Kubelet管理容器网络实现(通过CNI)和容器运行时(通过CRI)。
nodeNamenode-1apiVersion: v1 kind: Pod metadata: name: example spec: nodeName: "node-1" containers: - name: example image: example:1.0
node-1kubectl get pods -w --field-selector spec.nodeName=node-1Pod Readiness and Probes
endpoint用户可以在pod规范中指定pod就绪检查。Kubelet执行指定的检查并根据成功或失败更新pod状态。
. status.PhaseCrashLoopBackofflivenessProbereadinessProbestartupProbe每个探针都有以下三种结果之一:
Success
容器通过了诊断。
Failure
容器诊断失败。
Unknown
诊断失败,因此不应采取任何操作。
failureThreshold当容器就绪探测失败时,Kubelet不会终止它。相反,Kubelet将失败写入了pod的状态。
CrashLoopBackoff启动探针可以在激活探针生效之前提供一段宽限期。在启动探测成功之前,活性探测不会终止容器。一个示例用例是允许容器花很多分钟启动,但如果启动后容器变得不正常,则快速终止容器。
8080HTTP GET/healthz/apiVersion: v1 kind: Pod metadata: labels: test: liveness name: go-web spec: containers: - name: go-web image: go-web:v0.0.1 ports: - containerPort: 8080 livenessProbe: httpGet: path: /healthz port: 8080 initialDelaySeconds: 5 periodSeconds: 5 readinessProbe: httpGet: path: / port: 8080 initialDelaySeconds: 5 periodSeconds: 5
ReplicaSetsEndpointsEndpointsSlicestartupProbestartupProbestartupProbestartupProbe探针可配置选项:
initialDelaySeconds
在启动活动或准备就绪探测之前,容器启动后的秒数。默认值0;最低0。
periodSeconds
执行探测的频率。默认10;最低1。
timeoutSeconds
探测超时的秒数。默认1;最低1。
successThreshold
探针失败后连续成功的最小值。默认1;对于活性和启动探针必须为1;最低1。
failureThreshold
当探测失败时,Kubernetes会尝试很多次,然后放弃。在activity探测的情况下放弃意味着容器将重新启动。准备就绪时,舱将被标记为未就绪。默认3;最低1。
Kubelet必须能够连接到Kubernetes API服务器。在图5中,我们可以看到集群中所有组件的所有连接:
etcdCNI规范
CNI规范本身非常简单。根据规范,CNI插件必须支持四种操作:
ADD
向网络添加一个容器。
DEL
从网络中删除容器。
CHECK
如果容器的网络有问题,返回一个错误。
VERSION
报告插件的版本信息。
stdinstdoutKubernetes一次只使用一个CNI插件,尽管CNI规范允许多重设置(例如,为一个容器分配多个IP地址)。Multus是一个CNI插件,它在Kubernetes中通过充当多个CNI插件的扇形来解决这个限制。
CNI插件
CNI插件有两个主要职责:为pod分配唯一的IP地址,并确保Kubernetes中存在到每个pod IP地址的路由。这意味着集群所在的总体网络决定了CNI插件的行为。例如,如果IP地址太少或无法为一个节点附加足够的IP地址,集群管理员将需要使用支持覆盖网络的CNI插件。使用的硬件堆栈或云提供商通常决定了哪些CNI选项是合适的。
--network-plugin=cni/etc/cni/net.d/opt/cni/bin/--CNI -config-dir=--cni-bin-dir= CNI网络模型有两大类:flat networks和overlay networks。在flat networks中,CNI驱动程序使用来自集群网络的IP地址,这通常需要许多IP地址对集群可用。在overlay networks中,CNI驱动程序在Kubernetes中创建一个次级网络,该网络使用集群的网络(称为底层网络)来发送数据包。overlay networks在集群中创建一个虚拟网络。在overlay networks中,CNI插件封装数据包。overlay networks增加了相当大的复杂性,并且不允许集群网络上的主机直接连接到pod。然而,overlay networks允许集群网络更小,因为只有节点必须分配该网络上的IP地址。
CNI插件通常还需要一种在节点之间通信状态的方法。插件采用非常不同的方法,比如将数据存储在Kubernetes API中,并存储在专用的数据库中。
CNI插件还负责调用IPAM插件进行IP寻址。
IPAM接口
stdinstdout{ "cniVersion": "0.4.0", "ips": [ { "version": "<4-or-6>", "address": "<ip-and-prefix-in-CIDR>", "gateway": "<ip-address-of-the-gateway>" (optional) }, ... ], "routes": [ (optional) { "dst": "<ip-and-prefix-in-cidr>", "gw": "<ip-of-next-hop>" (optional) }, ... ] "dns": { (optional) "nameservers": <list-of-nameservers> (optional) "domain": <name-of-local-domain> (optional) "search": <list-of-search-domains> (optional) "options": <list-of-options> (optional) } }
常用的CNI插件
Cilium是一个开源软件,用于透明地保护应用程序容器之间的网络连接。Cilium是一种支持L7/http的CNI,可以使用与网络寻址分离的基于身份的安全模型在L3-L7上执行网络策略。
Flannel专注于网络,是为Kubernetes设计的一种简单的配置三层网络结构的方法。如果集群需要网络策略等功能,管理员必须部署其他cni,如Calico。Flannel使用Kubernetes集群现有的etcd存储其状态信息,以避免提供专用的数据存储。
Calico表示,它“将灵活的网络能力与随处运行的安全强制措施相结合,提供了具有本地Linux内核性能和真正的云本地可伸缩性的解决方案。”Calico不使用overlay networks。相反,Calico配置了一个三层网络,使用BGP路由协议在主机之间路由报文。Calico还可以与Istio(服务网格)集成,在服务网格和网络基础设施层解释和执行集群内工作负载的策略。
表2给出了可供选择的主要CNI插件的简要概述。
| Name | NetworkPolicy support | Data storage | Network setup |
|---|---|---|---|
| Cilium | Yes | etcd or consul | Ipvlan(beta), veth, L7 aware |
| Flannel | No | etcd | Layer 3 IPv4 overlay network |
| Calico | Yes | etcd or Kubernetes API | Layer 3 network using BGP |
| Weave Net | Yes | No external cluster store | Mesh overlay network |
让我们在示例3中使用Golang web服务器部署Cilium进行测试。我们需要一个kubernetes群集来部署cilium。在本地部署集群进行测试的最简单的方法之一是KIND,它允许我们使用YAML配置文件创建一个集群,然后使用Helm将Cilium部署到该集群中。
kind: Cluster # Specifies that we are configuring a KIND cluster apiVersion: kind.x-k8s.io/v1alpha4 # The version of KIND’s config nodes: # The list of nodes in the cluster - role: control-plane # One control plane node - role: worker # Worker node 1 - role: worker # Worker node 2 - role: worker # Worker node 3 networking: # KIND configuration options for networking disableDefaultCNI: true # Disables the default networking option so that we can deploy Cilium
使用KIND集群配置YAML,我们可以使用下面的命令使用KIND创建该集群。
$ kind create cluster --config=kind-config.yaml Creating cluster "kind" ... ✓ Ensuring node image (kindest/node:v1.18.2) Preparing nodes ✓ Writing configuration Starting control-plane Installing StorageClass Joining worker nodes Set kubectl context to "kind-kind"You can now use your cluster with: kubectl cluster-info --context kind-kind Have a question, bug, or feature request? Let us know! https://kind.sigs.k8s.io/ Always verify that the cluster is up and running with kubectl.
现在集群已经在本地运行,我们可以开始使用Kubernetes的部署工具Helm安装Cilium。根据其文档,Helm是安装Cilium的首选方式。
$ helm repo add cilium https://helm.cilium.io/# Pre-pulling and loading container images is optional.$ docker pull cilium/cilium:v1.9.1kind load docker-image cilium/cilium:v1.9.1
--set NAME_VAR=VAR$ helm install cilium cilium/cilium --version 1.10.1 \ --namespace kube-system NAME: Cilium LAST DEPLOYED: Fri Jan 1 15:39:59 2021NAMESPACE: kube-system STATUS: deployed REVISION: 1TEST SUITE: None NOTES: You have successfully installed Cilium with Hubble. Your release version is 1.10.1. For any further help, visit https://docs.cilium.io/en/v1.10/gettinghelp/
cilium-cnicilium -agentcilium-cnikubectl -n kube-system get pods --watch$ kubectl -n kube-system get pods --watch NAME READY STATUS cilium-65kvp 0/1 Init:0/2cilium-node-init-485lj 0/1 ContainerCreating cilium-node-init-79g68 1/1 Running cilium-node-init-gfdl8 1/1 Running cilium-node-init-jz8qc 1/1 Running cilium-operator-5b64c54cd-cgr2b 0/1 ContainerCreating cilium-operator-5b64c54cd-tblbz 0/1 ContainerCreating cilium-pg6v8 0/1 Init:0/2cilium-rsnqk 0/1 Init:0/2cilium-vfhrs 0/1 Init:0/2coredns-66bff467f8-dqzql 0/1 Pending coredns-66bff467f8-r5nl6 0/1 Pending etcd-kind-control-plane 1/1 Running kube-apiserver-kind-control-plane 1/1 Running kube-controller-manager-kind-control-plane 1/1 Running kube-proxy-k5zc2 1/1 Running kube-proxy-qzhvq 1/1 Running kube-proxy-v54p4 1/1 Running kube-proxy-xb9tr 1/1 Running kube-scheduler-kind-control-plane 1/1 Running cilium-operator-5b64c54cd-tblbz 1/1 Running
现在已经部署了cilium,我们可以运行cilium连接检查,以确保它正常运行:
$ kubectl apply -n cilium-test \-f \ https://raw.githubusercontent.com/strongjz/advanced_networking_code_examples/ master/chapter-4/connectivity-check.yaml deployment.apps/echo-a created deployment.apps/echo-b created deployment.apps/echo-b-host created deployment.apps/pod-to-a created deployment.apps/pod-to-external-1111 created deployment.apps/pod-to-a-denied-cnp created deployment.apps/pod-to-a-allowed-cnp created deployment.apps/pod-to-external-fqdn-allow-google-cnp created deployment.apps/pod-to-b-multi-node-clusterip created deployment.apps/pod-to-b-multi-node-headless created deployment.apps/host-to-b-multi-node-clusterip created deployment.apps/host-to-b-multi-node-headless created deployment.apps/pod-to-b-multi-node-nodeport created deployment.apps/pod-to-b-intra-node-nodeport created service/echo-a created service/echo-b created service/echo-b-headless created service/echo-b-host-headless created ciliumnetworkpolicy.cilium.io/pod-to-a-denied-cnp created ciliumnetworkpolicy.cilium.io/pod-to-a-allowed-cnp created ciliumnetworkpolicy.cilium.io/pod-to-external-fqdn-allow-google-cnp created
连接性测试将部署一系列Kubernetes部署,这些部署将使用各种连接性路径。连接路径具有或不具有服务负载均衡,并且存在于各种网络策略组合中。
$ kubectl get pods -n cilium-test -w NAME READY STATUSecho-a-57cbbd9b8b-szn94 1/1 Runningecho-b-6db5fc8ff8-wkcr6 1/1 Runningecho-b-host-76d89978c-dsjm8 1/1 Running host-to-b-multi-node-clusterip-fd6868749-7zkcr 1/1 Running host-to-b-multi-node-headless-54fbc4659f-z4rtd 1/1 Running pod-to-a-648fd74787-x27hc 1/1 Running pod-to-a-allowed-cnp-7776c879f-6rq7z 1/1 Running pod-to-a-denied-cnp-b5ff897c7-qp5kp 1/1 Running pod-to-b-intra-node-nodeport-6546644d59-qkmck 1/1 Running pod-to-b-multi-node-clusterip-7d54c74c5f-4j7pm 1/1 Running pod-to-b-multi-node-headless-76db68d547-fhlz7 1/1 Running pod-to-b-multi-node-nodeport-7496df84d7-5z872 1/1 Running pod-to-external-1111-6d4f9d9645-kfl4x 1/1 Running pod-to-external-fqdn-allow-google-cnp-5bc496897c-bnlqs 1/1 Running
NetworkPolicyNetworkPolicykube-proxy
kube-proxyKube-proxyendpoint /EndpointSlices- 服务为一组pod定义了负载平衡器。
- Endpoint/Endpointslices列出一组就绪的pod ip。它们是从服务中自动创建的,使用与服务相同的pod选择器。
kube-proxykube-proxykube-proxykube-proxyuserspaceiptablesipvskernelspace--proxy-modeiptablesuserspace Mode
userspaceuserspacekube-proxyiptablesuserspaceiptables Mode
iptablesiptablesiptablesiptableskube-dnsiptableskube-dns10.96.0.10$ sudo iptables -t nat -L KUBE-SERVICES Chain KUBE-SERVICES (2 references) target prot opt source destination /* kube-system/kube-dns:dns cluster IP */ udp dpt:domain KUBE-MARK-MASQ udp -- !10.217.0.0/16 10.96.0.10/* kube-system/kube-dns:dns cluster IP */ udp dpt:domain KUBE-SVC-TCOU7JCQXEZGVUNU udp -- anywhere 10.96.0.10/* kube-system/kube-dns:dns-tcp cluster IP */ tcp dpt:domain KUBE-MARK-MASQ tcp -- !10.217.0.0/16 10.96.0.10/* kube-system/kube-dns:dns-tcp cluster IP */ tcp dpt:domain KUBE-SVC-ERIFXISQEP7F7OF4 tcp -- anywhere 10.96.0.10 ADDRTYPE match dst-type LOCAL /* kubernetes service nodeports; NOTE: this must be the last rule in this chain */ KUBE-NODEPORTS all -- anywhere anywhere
kube-dns10.217.0.0/16KUBE-SVC-TCOU7JCQXEZGVUNU$ sudo iptables -t nat -L KUBE-SVC-TCOU7JCQXEZGVUNU Chain KUBE-SVC-TCOU7JCQXEZGVUNU (1 references) target prot opt source destination /* kube-system/kube-dns:dns */ KUBE-SEP-OCPCMVGPKTDWRD3C all -- anywhere anywhere statistic mode random probability 0.50000000000/* kube-system/kube-dns:dns */ KUBE-SEP-VFGOVXCRCJYSGAY3 all -- anywhere anywhere
10.0.1.141$ sudo iptables -t nat -L KUBE-SEP-OCPCMVGPKTDWRD3C Chain KUBE-SEP-OCPCMVGPKTDWRD3C (1 references) target prot opt source destination /* kube-system/kube-dns:dns */ KUBE-MARK-MASQ all -- 10.0.1.141 anywhere /* kube-system/kube-dns:dns */ udp to:10.0.1.141:53DNAT udp -- anywhere anywhere
ipvs Mode
ipvsipvsiptablesipvs--ipvs -schedulerrrlcdhshsednqRound-robin (rr)iptablesiptableskernelspace Mode
kernelspaceuserspaceiptablesipvsNetworkPolicy
NetworkPolicyNetworkPolicyNetworkPolicyNetworkPolicyNetworkPolicyNetworkPolicyNetworkPolicyNetworkPolicyNetworkPolicyNetworkPolicy| CNI plugin | NetworkPolicy supported |
|---|---|
| Calico | Yes, and supports additional plugin-specific policies |
| Cilium | Yes, and supports additional plugin-specific policies |
| Flannel | No |
| Kubenet | No |
NetworkPolicyNetworkPolicyapiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: demo namespace: default spec: podSelector: matchLabels: app: demo policyTypes: - Ingress - Egress ingress: []NetworkPolicyIngressRule # Not expanded egress: []NetworkPolicyEgressRule # Not expanded
NetworkPolicydemodemo- dbNetworkPolicydemo-DBdemo-DBNetworkPolicydemo-DBapiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: demo-db namespace: default spec: podSelector: matchLabels: app: demo-db policyTypes: - Ingress - Egress
app=demodemo-dbdemoNetworkPolicyapiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: demo-db namespace: default spec: podSelector: matchLabels: app: demo-db policyTypes: - Ingress - Egress ingress: - from: - podSelector: matchLabels: app: demo
demo-dbdemodemo-dbNetworkPolicy示例:cilium
NetworkPolicyNetworkPolicyYAMLkubectl$ kubectl apply -f database.yaml service/postgres created configmap/postgres-config created statefulset.apps/postgres created
下面我们将web服务器作为Kubernetes部署到KIND集群中:
$ kubectl apply -f web.yaml deployment.apps/app created
dnsutilspingcurl$ kubectl apply -f dnsutils.yaml pod/dnsutils created
kubectl port-forwardkubectl port-forward app-5878d69796-j889q 8080:8080
现在从本地终端,我们可以到达我们的API:
$ curl localhost:8080/ Hello $ curl localhost:8080/healthz Healthy $ curl localhost:8080/data Database Connected
让我们测试从其他pod到集群内web服务器的连通性。为了做到这一点,我们需要获得web服务器pod的IP地址:
$ kubectl get pods -l app=app -o wide NAME READY STATUS RESTARTS AGE IP NODE app-5878d69796-j889q 1/1 Running 0 87m 10.244.1.188 kind-worker3
dnsutils$ kubectl exec dnsutils -- nc -z -vv 10.244.1.188 808010.244.1.188 (10.244.1.188:8080) open sent 0, rcvd 0
dnsutils$ kubectl exec dnsutils -- wget -qO- 10.244.1.188:8080/ Hello $ kubectl exec dnsutils -- wget -qO- 10.244.1.188:8080/data Database Connected $ kubectl exec dnsutils -- wget -qO- 10.244.1.188:8080/healthz Healthy
10.244.2.189kubectl$ kubectl get pods -l app=postgres -o wide NAME READY STATUS RESTARTS AGE IP NODE postgres-0 1/1 Running 0 98m 10.244.2.189 kind-worker
dnsutils$ kubectl exec dnsutils -- nc -z -vv 10.244.2.189 543210.244.2.189 (10.244.2.189:5432) open sent 0, rcvd 0
该端口对所有人开放,因为没有适当的网络策略。现在让我们用cilium网络策略来限制它。下面的命令部署网络策略,以便我们可以测试安全的网络连通性。让我们首先将对数据库pod的访问限制为仅对web服务器。应用网络策略,只允许从web服务器pod到数据库的流量:
$ kubectl apply -f layer_3_net_pol.yaml ciliumnetworkpolicy.cilium.io/l3-rule-app-to-db created
kubectlkubectl describe ciliumnetworkpolicy.cilium.io l3-rule-app-to-db$ kubectl describe ciliumnetworkpolicies.cilium.io l3-rule-app-to-db Name: l3-rule-app-to-db Namespace: default Labels: <none> Annotations: API Version: cilium.io/v2 Kind: CiliumNetworkPolicy Metadata: Creation Timestamp: 2021-01-10T01:06:13Z Generation: 1Managed Fields: API Version: cilium.io/v2 Fields Type: FieldsV1 fieldsV1: f:metadata: f:annotations: .: f:kubectl.kubernetes.io/last-applied-configuration: f:spec: .: f:endpointSelector: .: f:matchLabels: .: f:app: f:ingress: Manager: kubectl Operation: Update Time: 2021-01-10T01:06:13Z Resource Version: 47377Self Link: /apis/cilium.io/v2/namespaces/default/ciliumnetworkpolicies/l3-rule-app-to-db UID: 71ee6571-9551-449d-8f3e-c177becda35a Spec: Endpoint Selector: Match Labels: App: postgres Ingress: From Endpoints: Match Labels: App: app Events: <none>
dnsutilsdnsutils$ kubectl exec dnsutils -- nc -z -vv -w 5 10.244.2.189 5432nc: 10.244.2.189 (10.244.2.189:5432): Operation timed out sent 0, rcvd 0command terminated with exit code 1
/dataNetworkPolicy$ kubectl exec dnsutils -- wget -qO- 10.244.1.188:8080/data Database Connected $ curl localhost:8080/data Database Connected
//data/healthz$ kubectl apply -f layer_7_netpol.yml ciliumnetworkpolicy.cilium.io/l7-rule created
我们可以看到策略的应用就像API中的其他Kubernetes对象一样:
$ kubectl get ciliumnetworkpolicies.cilium.io NAME AGE l7-rule 6m54s $ kubectl describe ciliumnetworkpolicies.cilium.io l7-rule Name: l7-rule Namespace: default Labels: <none> Annotations: API Version: cilium.io/v2 Kind: CiliumNetworkPolicy Metadata: Creation Timestamp: 2021-01-10T00:49:34Z Generation: 1 Managed Fields: API Version: cilium.io/v2 Fields Type: FieldsV1 fieldsV1: f:metadata: f:annotations: .: f:kubectl.kubernetes.io/last-applied-configuration: f:spec: .: f:egress: f:endpointSelector: .: f:matchLabels: .: f:app: Manager: kubectl Operation: Update Time: 2021-01-10T00:49:34Z Resource Version: 43869 Self Link:/apis/cilium.io/v2/namespaces/default/ciliumnetworkpolicies/l7-rule UID: 0162c16e-dd55-4020-83b9-464bb625b164 Spec: Egress: To Ports: Ports: Port: 8080 Protocol: TCP Rules: Http: Method: GET Path: / Method: GET Path: /data Endpoint Selector: Match Labels: App: app Events: <none>
//data/healthzNetworkPolicy$ kubectl exec dnsutils -- wget -qO- 10.244.1.188:8080/data Database Connected$kubectl exec dnsutils -- wget -qO- 10.244.1.188:8080/ Hello $ kubectl exec dnsutils -- wget -qO- -T 5 10.244.1.188:8080/healthz wget: error getting response command terminated with exit code 1
这些小示例展示了Cilium网络策略在集群内加强网络安全方面的强大功能。强烈建议管理员选择支持网络策略的CNI,并强制开发人员使用网络策略。网络策略是有命名空间的,如果团队有类似的设置,集群管理员可以并且应该强制开发人员定义网络策略以增加安全性。
Selecting Pods
NetworkPolicyNetworkPolicyspec.policyTypesNetworkPolicyspec.podselectorNetworkPolicylabel selector.(podSelector:{})NetworkPolicyspec.podselectorNetworkPolicyapp: demoapp: demo有多种方法可以实现默认防火墙行为,包括以下几种:
NetworkPolicyNetworkPolicyNetworkPolicyDNS
DNS是任何网络基础设施的关键部分,让我们讨论一下DNS在Kubernetes内部是如何工作的。
kube-dnsdnsmasqsidecarkube-dnsdnsmasqsidecar在CoreDNS和KubeDNS之间有几个区别:
- 为了简单起见,CoreDNS作为单个容器运行。
- CoreDNS是一个Go过程,复制并增强了Kube-DNS的功能。
- CoreDNS被设计成一个通用的DNS服务器,向后兼容Kubernetes,它的可扩展插件可以做的比Kubernetes DNS规范中提供的更多。
kube-systemdnpolicyClusterFirstWithHostNetdnpolicyapiVersion: v1 kind: Pod metadata: name: busybox namespace: default spec: containers: - image: busybox:1.28 command: - sleep - "3600" imagePullPolicy: IfNotPresent name: busybox restartPolicy: Always hostNetwork: true dnsPolicy: ClusterFirstWithHostNet
dnpolicyDefaultClusterFirstClusterFirstWithHostNethostNetworkClusterFirstWithHostNetNonednsConfigNonenameservers:apiVersion: v1 kind: Pod metadata: namespace: default name: busybox spec: containers: - image: busybox:1.28 command: - sleep - "3600" imagePullPolicy: IfNotPresent name: busybox dnsPolicy: "None" dnsConfig: nameservers: - 1.1.1.1 searches: - ns1.svc.cluster-domain.example - my.dns.search.suffix
optionsnamevalueresolv.conf<service>.default.svc.cluster.local ↓ svc.cluster.local ↓ cluster.local ↓ The host search path
主机搜索路径来自pod DNS策略或CoreDNS策略。
在Kubernetes中查询DNS记录会导致许多请求,并增加等待DNS请求的应用程序的延迟。CoreDNS有一个解决方案叫做Autopath。Autopath允许服务器端搜索路径完成。通过剥离集群搜索域,在CoreDNS服务器上进行查找,缩短了客户端搜索路径解析;当它找到一个答案时,它将结果存储为CNAME并返回一个查询,而不是五个。
然而,使用Autopath确实会增加CoreDNS上的内存使用量。确保根据集群的大小扩展CoreDNS副本的内存。请确保为CoreDNS pod设置适当的内存和CPU请求。为了监控CoreDNS,它导出了它公开的几个指标:
- coredns build info
- dns request count total
- dns request duration seconds
- dns request size bytes
- coredns plugin enabled
通过结合pod指标和CoreDNS指标,插件管理员将确保CoreDNS保持健康并在集群中运行。
总结
Kubernetes网络模型是网络如何在集群中工作的基础。在节点上运行的CNI实现了Kubernetes网络模型中提出的原则。该模型没有定义网络安全;Kubernetes的可扩展性使得CNI可以通过网络策略实现网络安全。
选择正确的CNI需要从开发人员和管理员的角度进行评估。需要列出要求并测试CNIs。如果不讨论网络安全和支持它的CNI,集群是不完整的。
DNS是至关重要的;一个完整的设置和一个平稳运行的网络需要网络和集群管理员能够熟练地在他们的集群中扩展CoreDNS。异常数量的Kubernetes问题源于DNS和CoreDNS的错误配置。