Kubernetns LB方案:無(wú)需云廠商的動(dòng)態(tài)DNS和負(fù)載均衡
本文轉(zhuǎn)載自微信公眾號(hào)「新鈦云服」,作者祝祥 翻譯 。轉(zhuǎn)載本文請(qǐng)聯(lián)系新鈦云服公眾號(hào)。
我們經(jīng)常談?wù)撏泄躃ubernetes或在云中運(yùn)行的Kubernetes,但我們也在非云的環(huán)境(例如VMware或裸機(jī)服務(wù)器)上運(yùn)行Kubernetes。
您可能還會(huì)聽到很多有關(guān)云供應(yīng)商集成的經(jīng)典案例:您可以獲取無(wú)密碼憑據(jù)來(lái)訪問托管服務(wù),無(wú)需手動(dòng)干預(yù)即可配置云負(fù)載均衡器,自動(dòng)創(chuàng)建DNS條目等。
在本地運(yùn)行時(shí),通常無(wú)法使用這些集成功能,除非您使用的是受支持的云平臺(tái)(如 OpenStack)。那么當(dāng)在裸機(jī)或VM上運(yùn)行時(shí),如何獲得Cloud Native環(huán)境的自動(dòng)化優(yōu)勢(shì)?
所以讓我們一步一步去看我們所想實(shí)現(xiàn)的功能。
本文中使用的所有清單均可在github項(xiàng)目中(https://github.com/clusterfrak-dynamics/gitops-template)獲取。
GitOps
與往常一樣,我們使用GitOps和FluxCD將我們的資源部署到集群中,無(wú)論它們是在云上還是在本地。你可以參考跟多我們關(guān)于Flux的文章。
首先,您可以使用我們的GitOps模板,并根據(jù)需要對(duì)其進(jìn)行自定義。kubectl如果更合適,您也可以直接部署清單 。
以下讓我們深入研究我們的組件。
負(fù)載均衡
在云上運(yùn)行kubernetns時(shí),通??梢粤⒓词褂肔oad Balancer。在裸機(jī)或VM上運(yùn)行時(shí),負(fù)載均衡器保持pending不可用狀態(tài)。
因此,首先,我們希望我們的服務(wù)類型LoadBalancer不處于pending不可用狀態(tài),并且能夠在需要時(shí)提供動(dòng)態(tài)負(fù)載平衡器,而無(wú)需手動(dòng)配置haproxy或其他類似的服務(wù)。
metallb可以提供兩種模式的虛擬負(fù)載均衡器的實(shí)現(xiàn):
- BGP協(xié)議
- ARP
后者更簡(jiǎn)單,因?yàn)樗梢栽趲缀跞魏味泳W(wǎng)絡(luò)上工作,而無(wú)需進(jìn)一步配置。
在ARP模式下,metallb的配置非常簡(jiǎn)單。您只需要給它提供一些可以使用的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
您還需要生成一個(gè)密鑰來(lái)加密Metallb組件通信,您可以使用以下腳本來(lái)生成Kubernetes secret yaml:
- kubectl create secret generic -n metallb-system memberlist --from-literal=secretkey="$(openssl rand -base64 128)" -o yaml --dry-run=client > metallb-secret.yaml
部署完所有內(nèi)容后,您應(yīng)該在metallb-system namespace內(nèi)看到相應(yīng)的pods :
- NAME READY STATUS RESTARTS AGE
- controller-57f648cb96-tvr9q 1/1 Running 0 2d1h
- speaker-7rd8p 1/1 Running 0 2d1h
- speaker-7t7rg 1/1 Running 0 2d1h
- speaker-8qm2t 1/1 Running 0 2d1h
- speaker-bks4s 1/1 Running 0 2d1h
- speaker-cz6bc 1/1 Running 0 2d1h
- speaker-h8b54 1/1 Running 0 2d1h
- speaker-j6bss 1/1 Running 0 2d1h
- speaker-phvv7 1/1 Running 0 2d1h
- speaker-wdwjc 1/1 Running 0 2d1h
- speaker-xj25p 1/1 Running 0 2d1h
現(xiàn)在,我們準(zhǔn)備測(cè)試負(fù)載均衡器。為此,我們直接進(jìn)入下一個(gè)主題。
Ingress controller
在云上運(yùn)行時(shí),除了經(jīng)典的4層負(fù)載均衡器以外,您有時(shí)還可以在GCP和AWS上獲得7層負(fù)載均衡器(例如,應(yīng)用程序負(fù)載均衡器)。但是它們的功能有限,而且成本效益不高,而且您經(jīng)常需要一個(gè)ingress controller來(lái)管理來(lái)自Kubernetes集群的流量。
這個(gè)ingress controller通常通過(guò)服務(wù)類型為L(zhǎng)oadBalancer在外部發(fā)布。這就是為什么我們以前的metallb部署會(huì)派上用場(chǎng)。
第一個(gè)也是最常用的ingress controller之一是nginx-ingress,它可以輕松地與Helm一起部署。
由于我們將Flux與Helm Operator結(jié)合使用,因此根據(jù)我們使用的Helm,您可以參考以下values.yaml配置:
因?yàn)槲覀儗lux與Helm Operator結(jié)合使用,所以我們使用了一個(gè)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,默認(rèn)情況下是使用的服務(wù)類型為L(zhǎng)oadBalancer。
如果我們檢查新部署的版本:
- $ kubectl -n nginx-ingress get helmreleases.helm.fluxcd.io
- NAME RELEASE PHASE STATUS MESSAGE AGE
- nginx-ingress nginx-ingress Succeeded deployed Release was successful for Helm release 'nginx-ingress' in 'nginx-ingress'. 2d1h
- or
- $ helm -n nginx-ingress ls
- NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
- nginx-ingress nginx-ingress 2 2020-05-12 15:06:25.832403094 +0000 UTC deployed nginx-ingress-1.36.3 0.30.0
- $ kubectl -n nginx-ingress get svc
- NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
- nginx-ingress-controller LoadBalancer 10.108.113.212 10.10.39.200 80:31465/TCP,443:30976/TCP 2d1h
- nginx-ingress-default-backend ClusterIP 10.102.217.148 <none> 80/TCP
我們可以看到,我們的服務(wù)是LoadBalancer類型,外部IP是我們?cè)谥癿etallb的ConfigMap中定義的。
讓我們創(chuàng)建一個(gè)demo namespace并檢查創(chuàng)建ingress時(shí)的行為:
- $ kubectl create namespace demo
- ---
- 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 能夠在默認(rèn)情況下發(fā)布服務(wù),這意味著它可以向ingress對(duì)象報(bào)告負(fù)載平衡器IP地址:
- $ kubectl -n demo get ingress
- NAME CLASS HOSTS ADDRESS PORTS AGE
- nginx <none> nginx.test.org 10.10.39.200 80, 443 47h
我們可以看到,LoadBalancer IP地址已嵌入到ingress中。這是能夠使用external DNS的要求之一,這也是我們的下一個(gè)主題。
External DNS
現(xiàn)在我們已經(jīng)有了4層負(fù)載平衡器(metallb),它們可以將流量傳送到群集中的7層負(fù)載平衡器(nginx-ingress),我們?nèi)绾蝿?dòng)態(tài)管理DNS?一個(gè)常用的工具是 external-dns(https://github.com/kubernetes-sigs/external-dns)使Kubernetes Services和Ingress與DNS平臺(tái)保持同步。
如果您正在使用一種廣泛使用的DNS平臺(tái)(AWS Route53或 Google Cloud DNS),這將非常簡(jiǎn)單易用。External DNS還支持其他DNS供應(yīng)商,但是如果您沒有使用直接支持的DNS供應(yīng)商,您可能就會(huì)比較麻煩。
比方說(shuō),您的本地DNS由Active Directory管理,因?yàn)镋xternal DNS無(wú)法直接寫入Active Directory DNS,所以最終導(dǎo)致DNS解析不可用。
那么我們?nèi)绾尾拍塬@得動(dòng)態(tài)DNS功能呢?當(dāng)然,你可以使用一個(gè)通配符DNS記錄并將其指向到nginx-ingress負(fù)載平衡器IP,這是一種方法。如果您只使用一個(gè)LoadBalancer作為集群的入口,但如果您希望使用HTTP或其他類型LoadBalancer服務(wù)以外的協(xié)議,則仍需要手動(dòng)更新一些DNS記錄。
另一個(gè)解決方案是為您的群集指定DNS zone。
External DNS支持CoreDNS作為后端,因此我們可以將active directory的DNS Zone指派給Kubernetes中運(yùn)行的CoreDNS服務(wù)器。
注意事項(xiàng)
聽起來(lái)很簡(jiǎn)單,但是當(dāng)深入研究external-dns/CoreDNS部分時(shí),我們注意到與External DNS一起使用的CoreDNS唯一受支持的后端是Etcd。所以我們需要一個(gè)Etcd集群。您可能還注意到readme依賴于etcd-operator,它現(xiàn)在已被棄用,而且它也不不支持加密與etcd的通信。
我們發(fā)布了最新指南。首先,我們將使用Cilium的etcd-operator來(lái)配置3個(gè)節(jié)點(diǎn)的etcd集群并生成TLS。
Etcd operator
首先,我們應(yīng)用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: 'CreationTimestamp is a timestamp representing the server time when
- this object was created. It is not guaranteed to be set in happens-before order
- across separate operations. Clients may not set this value. It is represented
- in RFC3339 form and is in UTC. Populated by the system. Read-only. Null for
- lists. More info: 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 -n external-dns get pods
- NAME READY STATUS RESTARTS AGE
- cilium-etcd-mnphzk2tjl 1/1 Running 0 2d1h
- cilium-etcd-operator-55d89bbff7-cw8rc 1/1 Running 0 2d1h
- cilium-etcd-tsxm5rsckj 1/1 Running 0 2d1h
- cilium-etcd-wtnqt22ssg 1/1 Running 0 2d1h
- etcd-operator-6c57fff6f5-g92pc 1/1 Running 0 2d1h
- $ kubectl -n external-dns get secrets
- NAME TYPE DATA AGE
- cilium-etcd-client-tls Opaque 3 2d1h
- cilium-etcd-operator-token-zmjcl kubernetes.io/service-account-token 3 2d1h
- cilium-etcd-peer-tls Opaque 3 2d1h
- cilium-etcd-sa-token-5dhtn kubernetes.io/service-account-token 3 2d1h
- cilium-etcd-secrets Opaque 3 2d1h
- cilium-etcd-server-tls Opaque 3 2d1h
CoreDNS
然后,我們可以使用官方Helm chat部署CoreDNS 。
就像以前一樣,我們的資源是*HelmRelease*(https://github.com/clusterfrak-dynamics/gitops-template/blob/master/flux/resources/external-dns/coredns.yaml)。如果需要values.yaml,您可以從中獲?。?/p>
- 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: |-
- lameduck 5s
- - 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
- endpoint https://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
- endpoint https://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進(jìn)行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 -n external-dns get svc
- NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
- cilium-etcd ClusterIP None <none> 2379/TCP,2380/TCP 2d2h
- cilium-etcd-client ClusterIP 10.105.37.25 <none> 2379/TCP 2d2h
- coredns-coredns NodePort 10.99.62.135 <none> 53:31071/UDP,53:30396/TCP 2d1h
- external-dns ClusterIP 10.103.88.97 <none> 7979/TCP 2d1h
- $ kubectl -n external-dns get pods
- NAME READY STATUS RESTARTS AGE
- cilium-etcd-mnphzk2tjl 1/1 Running 0 2d2h
- cilium-etcd-operator-55d89bbff7-cw8rc 1/1 Running 0 2d2h
- cilium-etcd-tsxm5rsckj 1/1 Running 0 2d2h
- cilium-etcd-wtnqt22ssg 1/1 Running 0 2d2h
- coredns-coredns-5c86dd5979-866s2 1/1 Running 0 2d
- coredns-coredns-5c86dd5979-vq86w 1/1 Running 0 2d
- etcd-operator-6c57fff6f5-g92pc 1/1 Running 0 2d2h
- external-dns-96d9fbc64-j22pf 1/1 Running 0 2d1h
如果您回頭看一下我們的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 -n demo get ingress
- NAME CLASS HOSTS ADDRESS PORTS AGE
- nginx <none> nginx.test.org 10.10.39.200 80, 443 2d
讓我們檢查此ingress是否已被external dns接收并插入etcd數(shù)據(jù)庫(kù):
- $ kubectl -n external-dns logs -f external-dns-96d9fbc64-j22pf
- time="2020-05-12T15:23:52Z" level=info msg="Add/set key /skydns/org/test/nginx/4781436c to Host=10.10.39.200, Text=\"heritage=external-dns,external-dns/owner=default,external-dns/resource=ingress/demo/nginx\", TTL=0"
External DNS似乎正在發(fā)揮作用?,F(xiàn)在,讓我們看看是否可以直接從CoreDNS解析查詢,因?yàn)樗鼞?yīng)該是從同一etcd服務(wù)器讀取的。
CoreDNS正在監(jiān)聽NodePort服務(wù),這意味著我們可以查詢?cè)摲?wù)上的任何節(jié)點(diǎn)NodePort:
- $ kubectl -n external-dns get svc coredns-coredns
- NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
- coredns-coredns NodePort 10.99.62.135 <none> 53:31071/UDP,53:30396/TCP 2d1h
53/UDP端口映射到端口31071/UDP。讓我們選擇一個(gè)隨機(jī)節(jié)點(diǎn):
- NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
- m1 Ready master 15d v1.18.2 10.10.40.10 <none> Ubuntu 18.04.3 LTS 4.15.0-99-generic containerd://1.3.4
- n1 Ready <none> 15d v1.18.2 10.10.40.110 <none> Ubuntu 18.04.3 LTS 4.15.0-99-generic containerd://1.3.4
- n2 Ready <none> 15d v1.18.2 10.10.40.120 <none> Ubuntu 18.04.3 LTS 4.15.0-74-generic containerd://1.3.4
并嘗試使用以下方式進(jìn)行DNS查詢dig:
- root@inf-k8s-epi-m5:~# dig -p 31071 nginx.test.org @10.10.40.120
- ; <<>> DiG 9.11.3-1ubuntu1.11-Ubuntu <<>> -p 31071 nginx.test.org @10.10.40.120
- ;; global options: +cmd
- ;; Got answer:
- ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 61245
- ;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
- ;; WARNING: recursion requested but not available
- ;; OPT PSEUDOSECTION:
- ; EDNS: version: 0, flags:; udp: 4096
- ; COOKIE: ef8ff2732b2dc6fd (echoed)
- ;; QUESTION SECTION:
- ;nginx.test.org. IN A
- ;; ANSWER SECTION:
- nginx.test.org. 30 IN A 10.10.39.200
- ;; Query time: 2 msec
- ;; SERVER: 10.10.40.120#31071(10.10.40.120)
- ;; WHEN: Thu May 14 16:26:07 UTC 2020
- ;; MSG SIZE rcvd: 85
我們可以看到CoreDNS正在使用MetalLB負(fù)載均衡器IP進(jìn)行回復(fù)。
快速啟動(dòng)并運(yùn)行
在本指南中,我們配置了CoreDNS,External DNS,Nginx Ingress和MetalLB,以提供與Cloud架構(gòu)一樣的動(dòng)態(tài)體驗(yàn)。如果您想快速入門,請(qǐng)查看我們的Flux github倉(cāng)庫(kù)地址,其中包含用于此演示的所有清單以及更多內(nèi)容(https://github.com/clusterfrak-dynamics/gitops-template/tree/master/flux)。
原文:https://particule.io/en/blog/k8s-no-cloud/