译者:祝祥

kubernetes链路分析(KubernetnsLB无需云厂商的动态DNS和负载均衡)(1)

我们经常谈论托管Kubernetes或在云中运行的Kubernetes,但我们也在非云的环境(例如VMware或裸机服务器)上运行Kubernetes。

您可能还会听到很多有关云供应商集成的经典案例:您可以获取无密码凭据来访问托管服务,无需手动干预即可配置云负载均衡器,自动创建DNS条目等。

在本地运行时,通常无法使用这些集成功能,除非您使用的是受支持的云平台(如 OpenStack)。那么当在裸机或VM上运行时,如何获得Cloud Native环境的自动化优势?

所以让我们一步一步去看我们所想实现的功能。

本文中使用的所有清单均可在github项目中(https://github.com/clusterfrak-dynamics/gitops-template)获取。

GitOps

与往常一样,我们使用GitOps和FluxCD将我们的资源部署到集群中,无论它们是在云上还是在本地。你可以参考跟多我们关于Flux的文章。

首先,您可以使用我们的GitOps模板,并根据需要对其进行自定义。kubectl如果更合适,您也可以直接部署清单 。

以下让我们深入研究我们的组件。

负载均衡

在云上运行kubernetns时,通常可以立即使用Load Balancer。在裸机或VM上运行时,负载均衡器保持pending不可用状态。

因此,首先,我们希望我们的服务类型LoadBalancer不处于pending不可用状态,并且能够在需要时提供动态负载平衡器,而无需手动配置haproxy或其他类似的服务。

metallb可以提供两种模式的虚拟负载均衡器的实现:

后者更简单,因为它可以在几乎任何二层网络上工作,而无需进一步配置。

在ARP模式下,metallb的配置非常简单。您只需要给它提供一些可以使用的IP就可以了。

配置清单可在此处或官方文件中找到。要配置所需的IP地址,可以使用ConfigMap完成。

metallb-config.yaml:

apiVersion:v1 kind:ConfigMap metadata: namespace:metallb-system name:config data: config:| address-pools: -name:default protocol:layer2 addresses: -10.10.39.200-10.10.39.220

您还需要生成一个密钥来加密Metallb组件通信,您可以使用以下脚本来生成Kubernetes secret yaml:

kubectlcreatesecretgeneric-nmetallb-systemmemberlist--from-literal=secretkey="$(opensslrand-base64128)"-oyaml--dry-run=client>metallb-secret.yaml

部署完所有内容后,您应该在metallb-system namespace内看到相应的pods :

NAMEREADYSTATUSRESTARTSAGE controller-57f648cb96-tvr9q1/1Running02d1h speaker-7rd8p1/1Running02d1h speaker-7t7rg1/1Running02d1h speaker-8qm2t1/1Running02d1h speaker-bks4s1/1Running02d1h speaker-cz6bc1/1Running02d1h speaker-h8b541/1Running02d1h speaker-j6bss1/1Running02d1h speaker-phvv71/1Running02d1h speaker-wdwjc1/1Running02d1h speaker-xj25p1/1Running02d1h

现在,我们准备测试负载均衡器。为此,我们直接进入下一个主题。

Ingress controller

在云上运行时,除了经典的4层负载均衡器以外,您有时还可以在GCP和AWS上获得7层负载均衡器(例如,应用程序负载均衡器)。但是它们的功能有限,而且成本效益不高,而且您经常需要一个ingress controller来管理来自Kubernetes集群的流量。

这个ingress controller通常通过服务类型为LoadBalancer在外部发布。这就是为什么我们以前的metallb部署会派上用场。

第一个也是最常用的ingress controller之一是nginx-ingress,它可以轻松地与Helm一起部署。

由于我们将Flux与Helm Operator结合使用,因此根据我们使用的Helm,您可以参考以下values.yaml配置:

因为我们将Flux与Helm Operator结合使用,所以我们使用了一个Helm Release 的版本,您可以从中得到values.yaml,以下展示了我们的配置:

apiVersion:helm.fluxcd.io/v1 kind:HelmRelease metadata: name:nginx-ingress namespace:nginx-ingress spec: releaseName:nginx-ingress chart: repository:https://kubernetes-charts.storage.googleapis.com version:1.36.3 name:nginx-ingress values: controller: publishService: enabled:true kind:"DaemonSet" service: enabled:true externalTrafficPolicy:Local daemonset: hostPorts: http:80 https:443 defaultBackend: replicaCount:2 podSecurityPolicy: enabled:true

没有什么特别之处,我们使用的是DaemonSet,默认情况下是使用的服务类型为LoadBalancer。

如果我们检查新部署的版本:

$kubectl-nnginx-ingressgethelmreleases.helm.fluxcd.io NAMERELEASEPHASESTATUSMESSAGEAGE nginx-ingressnginx-ingressSucceededdeployedReleasewassuccessfulforHelmrelease'nginx-ingress'in'nginx-ingress'.2d1h or $helm-nnginx-ingressls NAMENAMESPACEREVISIONUPDATEDSTATUSCHARTAPPVERSION nginx-ingressnginx-ingress22020-05-1215:06:25.832403094 0000UTCdeployednginx-ingress-1.36.30.30.0 $kubectl-nnginx-ingressgetsvc NAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)AGE nginx-ingress-controllerLoadBalancer10.108.113.21210.10.39.20080:31465/TCP,443:30976/TCP2d1h nginx-ingress-default-backendClusterIP10.102.217.148<none>80/TCP2d1h

我们可以看到,我们的服务是LoadBalancer类型,外部IP是我们在之前metallb的ConfigMap中定义的。

让我们创建一个demo namespace并检查创建ingress时的行为:

$kubectlcreatenamespacedemo --- apiVersion:apps/v1 kind:Deployment metadata: labels: app:nginx name:nginx namespace:demo spec: selector: matchLabels: app:nginx template: metadata: labels: app:nginx spec: containers: -image:nginx name:nginx --- apiVersion:v1 kind:Service metadata: labels: app:nginx name:nginx namespace:demo spec: ports: -port:80 protocol:TCP targetPort:80 selector: app:nginx --- apiVersion:networking.k8s.io/v1beta1 kind:Ingress metadata: annotations: kubernetes.io/ingress.class:nginx name:nginx namespace:demo spec: rules: -host:nginx.test.org http: paths: -backend: serviceName:nginx servicePort:80

nginx-ingress 能够在默认情况下发布服务,这意味着它可以向ingress对象报告负载平衡器IP地址:

$kubectl-ndemogetingress NAMECLASSHOSTSADDRESSPORTSAGE nginx<none>nginx.test.org10.10.39.20080,44347h

我们可以看到,LoadBalancer IP地址已嵌入到ingress中。这是能够使用external DNS的要求之一,这也是我们的下一个主题。

External DNS

现在我们已经有了4层负载平衡器(metallb),它们可以将流量传送到群集中的7层负载平衡器(nginx-ingress),我们如何动态管理DNS?一个常用的工具是 external-dns(https://github.com/kubernetes-sigs/external-dns)使Kubernetes Services和Ingress与DNS平台保持同步。

如果您正在使用一种广泛使用的DNS平台(AWS Route53或 Google Cloud DNS),这将非常简单易用。External DNS还支持其他DNS供应商,但是如果您没有使用直接支持的DNS供应商,您可能就会比较麻烦。

比方说,您的本地DNS由Active Directory管理,因为External DNS无法直接写入Active Directory DNS,所以最终导致DNS解析不可用。

那么我们如何才能获得动态DNS功能呢?当然,你可以使用一个通配符DNS记录并将其指向到nginx-ingress负载平衡器IP,这是一种方法。如果您只使用一个LoadBalancer作为集群的入口,但如果您希望使用HTTP或其他类型LoadBalancer服务以外的协议,则仍需要手动更新一些DNS记录。

另一个解决方案是为您的群集指定DNS zone。

External DNS支持CoreDNS作为后端,因此我们可以将active directory的DNS Zone指派给Kubernetes中运行的CoreDNS服务器。

注意事项

听起来很简单,但是当深入研究external-dns/CoreDNS部分时,我们注意到与External DNS一起使用的CoreDNS唯一受支持的后端是Etcd。所以我们需要一个Etcd集群。您可能还注意到readme依赖于etcd-operator,它现在已被弃用,而且它也不不支持加密与etcd的通信。

我们发布了最新指南。首先,我们将使用Cilium的etcd-operator来配置3个节点的etcd集群并生成TLS。

Etcd operator

首先,我们应用etcd的 Custom Resource Definition:(https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/):

--- apiVersion:apiextensions.k8s.io/v1beta1 kind:CustomResourceDefinition metadata: name:etcdclusters.etcd.database.coreos.com spec: additionalPrinterColumns: -JSONPath:.metadata.creationTimestamp description:'CreationTimestampisatimestamprepresentingtheservertimewhen thisobjectwascreated.Itisnotguaranteedtobesetinhappens-beforeorder acrossseparateoperations.Clientsmaynotsetthisvalue.Itisrepresented inRFC3339formandisinUTC.Populatedbythesystem.Read-only.Nullfor lists.Moreinfo:https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata' name:Age type:date group:etcd.database.coreos.com names: kind:EtcdCluster listKind:EtcdClusterList plural:etcdclusters shortNames: -etcd singular:etcdcluster scope:Namespaced version:v1beta2 versions: -name:v1beta2 served:true storage:true

然后我们可以部署Etcd operator。

很快我们就可以得到etcd pod和secrets了:

$kubectl-nexternal-dnsgetpods NAMEREADYSTATUSRESTARTSAGE cilium-etcd-mnphzk2tjl1/1Running02d1h cilium-etcd-operator-55d89bbff7-cw8rc1/1Running02d1h cilium-etcd-tsxm5rsckj1/1Running02d1h cilium-etcd-wtnqt22ssg1/1Running02d1h etcd-operator-6c57fff6f5-g92pc1/1Running02d1h $kubectl-nexternal-dnsgetsecrets NAMETYPEDATAAGE cilium-etcd-client-tlsOpaque32d1h cilium-etcd-operator-token-zmjclkubernetes.io/service-account-token32d1h cilium-etcd-peer-tlsOpaque32d1h cilium-etcd-sa-token-5dhtnkubernetes.io/service-account-token32d1h cilium-etcd-secretsOpaque32d1h cilium-etcd-server-tlsOpaque32d1h

CoreDNS

然后,我们可以使用官方Helm chat部署CoreDNS 。

就像以前一样,我们的资源是*HelmRelease*(https://github.com/clusterfrak-dynamics/gitops-template/blob/master/flux/resources/external-dns/coredns.yaml)。如果需要values.yaml,您可以从中获取:

apiVersion:helm.fluxcd.io/v1 kind:HelmRelease metadata: name:coredns namespace:external-dns spec: releaseName:coredns chart: repository:https://kubernetes-charts.storage.googleapis.com version:1.10.1 name:coredns values: serviceType:"NodePort" replicaCount:2 serviceAccount: create:true rbac: pspEnable:true isClusterService:false extraSecrets: -name:cilium-etcd-client-tls mountPath:/etc/coredns/tls/etcd servers: -zones: -zone:. port:53 plugins: -name:errors -name:health configBlock:|- lameduck5s -name:ready -name:prometheus parameters:0.0.0.0:9153 -name:forward parameters:./etc/resolv.conf -name:cache parameters:30 -name:loop -name:reload -name:loadbalance -name:etcd parameters:test.org configBlock:|- stubzones path/skydns endpointhttps://cilium-etcd-client.external-dns.svc:2379 tls/etc/coredns/tls/etcd/etcd-client.crt/etc/coredns/tls/etcd/etcd-client.key/etc/coredns/tls/etcd/etcd-client-ca.crt

重要的几行如下:

extraSecrets: -name:cilium-etcd-client-tls mountPath:/etc/coredns/tls/etcd and -name:etcd parameters:test.org configBlock:|- stubzones path/skydns endpointhttps://cilium-etcd-client.external-dns.svc:2379 tls/etc/coredns/tls/etcd/etcd-client.crt/etc/coredns/tls/etcd/etcd-client.key/etc/coredns/tls/etcd/etcd-client-ca.crt

我们正在安装并使用etcd secret与etcd进行TLS通信。

External DNS

最后,我们可以打包并安装external DNS。和往常一样,我们将使用官方的Helm chat(https://github.com/helm/charts/tree/master/stable/external-dns)和HelmRelease(https://github.com/clusterfrak-dynamics/gitops-template/blob/master/flux/resources/external-dns/external-dns.yaml):

apiVersion:helm.fluxcd.io/v1 kind:HelmRelease metadata: name:external-dns namespace:external-dns spec: releaseName:external-dns chart: repository:https://charts.bitnami.com/bitnami version:2.22.4 name:external-dns values: provider:coredns policy:sync coredns: etcdEndpoints:"https://cilium-etcd-client.external-dns.svc:2379" etcdTLS: enabled:true secretName:"cilium-etcd-client-tls" caFilename:"etcd-client-ca.crt" certFilename:"etcd-client.crt" keyFilename:"etcd-client.key"

这里,与以前一样,我们提供了etcd TLS的secret名称和路径,以确保通信安全,并且我们启用了coredns。

这是我们的最终external-dns namespace:

$kubectl-nexternal-dnsgetsvc NAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)AGE cilium-etcdClusterIPNone<none>2379/TCP,2380/TCP2d2h cilium-etcd-clientClusterIP10.105.37.25<none>2379/TCP2d2h coredns-corednsNodePort10.99.62.135<none>53:31071/UDP,53:30396/TCP2d1h external-dnsClusterIP10.103.88.97<none>7979/TCP2d1h $kubectl-nexternal-dnsgetpods NAMEREADYSTATUSRESTARTSAGE cilium-etcd-mnphzk2tjl1/1Running02d2h cilium-etcd-operator-55d89bbff7-cw8rc1/1Running02d2h cilium-etcd-tsxm5rsckj1/1Running02d2h cilium-etcd-wtnqt22ssg1/1Running02d2h coredns-coredns-5c86dd5979-866s21/1Running02d coredns-coredns-5c86dd5979-vq86w1/1Running02d etcd-operator-6c57fff6f5-g92pc1/1Running02d2h external-dns-96d9fbc64-j22pf1/1Running02d1h

如果您回头看一下我们的ingress:

--- apiVersion:networking.k8s.io/v1beta1 kind:Ingress metadata: annotations: kubernetes.io/ingress.class:nginx name:nginx namespace:demo spec: rules: -host:nginx.test.org http: paths: -backend: serviceName:nginx servicePort:80 $kubectl-ndemogetingress NAMECLASSHOSTSADDRESSPORTSAGE nginx<none>nginx.test.org10.10.39.20080,4432d

让我们检查此ingress是否已被external dns接收并插入etcd数据库:

$kubectl-nexternal-dnslogs-fexternal-dns-96d9fbc64-j22pf time="2020-05-12T15:23:52Z"level=infomsg="Add/setkey/skydns/org/test/nginx/4781436ctoHost=10.10.39.200,Text=\"heritage=external-dns,external-dns/owner=default,external-dns/resource=ingress/demo/nginx\",TTL=0"

External DNS似乎正在发挥作用。现在,让我们看看是否可以直接从CoreDNS解析查询,因为它应该是从同一etcd服务器读取的。

CoreDNS正在监听NodePort服务,这意味着我们可以查询该服务上的任何节点NodePort:

$kubectl-nexternal-dnsgetsvccoredns-coredns NAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)AGE coredns-corednsNodePort10.99.62.135<none>53:31071/UDP,53:30396/TCP2d1h

53/UDP端口映射到端口31071/UDP。让我们选择一个随机节点:

NAMESTATUSROLESAGEVERSIONINTERNAL-IPEXTERNAL-IPOS-IMAGEKERNEL-VERSIONCONTAINER-RUNTIME m1Readymaster15dv1.18.210.10.40.10<none>Ubuntu18.04.3LTS4.15.0-99-genericcontainerd://1.3.4 n1Ready<none>15dv1.18.210.10.40.110<none>Ubuntu18.04.3LTS4.15.0-99-genericcontainerd://1.3.4 n2Ready<none>15dv1.18.210.10.40.120<none>Ubuntu18.04.3LTS4.15.0-74-genericcontainerd://1.3.4

并尝试使用以下方式进行DNS查询dig:

root@inf-k8s-epi-m5:~#dig-p31071nginx.test.org@10.10.40.120 ;<<>>DiG9.11.3-1ubuntu1.11-Ubuntu<<>>-p31071nginx.test.org@10.10.40.120 ;;globaloptions: cmd ;;Gotanswer: ;;->>HEADER<<-opcode:QUERY,status:NOERROR,id:61245 ;;flags:qraard;QUERY:1,ANSWER:1,AUTHORITY:0,ADDITIONAL:1 ;;WARNING:recursionrequestedbutnotavailable ;;OPTPSEUDOSECTION: ;EDNS:version:0,flags:;udp:4096 ;COOKIE:ef8ff2732b2dc6fd(echoed) ;;QUESTIONSECTION: ;nginx.test.org.INA ;;ANSWERSECTION: nginx.test.org.30INA10.10.39.200 ;;Querytime:2msec ;;SERVER:10.10.40.120#31071(10.10.40.120) ;;WHEN:ThuMay1416:26:07UTC2020 ;;MSGSIZErcvd:85

我们可以看到CoreDNS正在使用MetalLB负载均衡器IP进行回复。

快速启动并运行

在本指南中,我们配置了CoreDNS,External DNS,Nginx Ingress和MetalLB,以提供与Cloud架构一样的动态体验。如果您想快速入门,请查看我们的Flux github仓库地址,其中包含用于此演示的所有清单以及更多内容(https://github.com/clusterfrak-dynamics/gitops-template/tree/master/flux)。

原文:https://particule.io/en/blog/k8s-no-cloud/

,