CoreDNS 与多集群服务 MCS

开发 前端
假如当前应用内的代码中是使用 NAME.NS​ 的方式的话,可以通过在 Pod /etc/resolv.conf​增加 clusterset.local​ 搜索域来解决。由于 Kubernetes 集群只支持一个 clusterDomain​,只能通过 pod.dnsConfig 在添加搜索域了。单又会来另一个问题:搜索域的增加势必带来性能的开销。

Kubernetes 作为一项核心技术已成为现代应用程序架构的基础,越来越多的企业使用其作为容器编排系统。Kubernetes 集群经历了 从单 Kubernetes 集群到多 Kubernetes 集群、从多 Kubernetes 集群到 Kubernetes 多集群的演进[1],集群的展现形式不断发生着变化。

为此,Kubernetes 多集群 SIG 提出了 KEP-1645: Multi-Cluster Services API[2](以下简称 MCS API)应对 Kubernetes 多集群带来的挑战,详细内容可以看之前的介绍:认识一下 Kubernetes 多集群服务 API[3] ,这篇要介绍的是多集群服务 DNS。

多集群服务 DNS

在官方建议的方案中使用了 多集群服务 DNS[4] ,并提出了 Kubernetes 基于 DNS 的多集群服务发现规范[5]用于指导多集群服务 API 的实现。该规范可以认为是 Kubernetes 的基于 DNS 的服务发现规范[6] 的扩展。

简单来说,该规范引入了新的 DNS 搜索域 clusterset.local,在 DNS 解析 <service>.<ns>.svc.clusterset.local 时,返回 ServiceImport 中的 IP 地址。

图片图片

CoreDNS 的 multicluster[7] 插件实现了该逻辑。

CoreDNS 多集群插件

插件可以说是 CoreDNS 精髓,其插件可以分为两种:核心插件[8] 和 外部插件(External Plugin)[9]。

  • 核心插件,也就是通常所说的插件,默认是编译在 CoreDNS 中的,只需在 Corefile 中添加配置即可启用该插件。
  • 外部插件,不是 CoreDNS 默认支持的,需要编译时加入到 CoreDNS 中。下面会详细介绍如何将外部插件编译到 CoreDNS 中。

multicluster 插件是众多外部插件中的一个,实现了基于 DNS 的多集群服务发现规范。

工作原理

multicluster 插件在启动时 创建一个控制器[10],用于监控 ServiceImports[11] 资源;在处理 DNS 解析请求阶段,只处理 clusterset.local 域的解析请求:从所有 ServiceImports 资源中 检索出匹配的结果[12],返回其中的 IP 地址,甚至端口。

演示

编译 CoreDNS

修改 plugin.cfg,添加:

...
kubernetes:kubernetes
multicluster: github.com/coredns/multicluster
...

执行命令 make 进行编译后,然后执行命令检查插件是否编译成功:

./coredns --plugins | grep -A1 dns.minimal      
  dns.minimal
  dns.multicluster

在结果中我们有看到 dns.multicluster 说明配置都成功了。

接下来就是构建多平台的二进制文件了了:

make build -f Makefile.release

可以再检查下编译好的二进制文件(使用对应平台的版本,我这里是 Ubuntu):

./build/linux/amd64/coredns --plugins | grep -A1 mini
  dns.minimal
  dns.multicluster

构建镜像

构建镜像之前记得执行下面的命令调整下文件目录:

cp -r build/linux build/docker
for arch in amd64 arm arm64 mips64le ppc64le s390x; do 
  cp Dockerfile build/docker/${arch} ;
done;

执行命令构建并推送镜像,这里 DOCKER=addozhang 是镜像 repository 名字,NAME=coredns-multicluster 是镜像名,按照下面的配置最终构建的镜像是 addozhang/coredns-multicluster:1.10.1:

export DOCKER_LOGIN=addozhang
export DOCKER_PASSWORD=<pass>
make docker-build docker-push DOCKER=addozhang NAME=coredns-multicluster VERSION=1.10.1 -f Makefile.docker

更新 CoreDNS

因为这次演示不会涉及 ServiceExport,可以只部署 ServiceImport CRD。

kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/mcs-api/master/config/crd/multicluster.x-k8s.io_serviceimports.yaml

修改了 ClusterRole coredns,增加 multicluster.x-k8s.io 的 serviceimports 资源的操作权限。

kubectl apply -f - <<EOF
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: system:coredns
rules:
- apiGroups:
  - ""
  resources:
  - endpoints
  - services
  - pods
  - namespaces
  verbs:
  - list
  - watch
- apiGroups:  #add serviceimports resource verbs
  - multicluster.x-k8s.io
  resources:
  - serviceimports
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - ""
  resources:
  - nodes
  verbs:
  - get
- apiGroups:
  - discovery.k8s.io
  resources:
  - endpointslices
  verbs:
  - list
  - watch
EOF

将集群 CoreDNS 的镜像改为上面构建的镜像:

kubectl patch deployment coredns -n kube-system -p '{"spec":{"template":{"spec":{"containers":[{"name":"coredns","image":"addozhang/coredns-multicluster:1.10.1"}]}}}}'

只更新 CoreDNS 还不够,还需要修改 Corefile,添加 multicluster 配置。

kubectl apply -n kube-system -f - <<EOF
apiVersion: v1
data:
  Corefile: |
    .:53 {
        errors
        health {
           lameduck 5s
        }
        ready
        multicluster clusterset.local
        kubernetes cluster.local in-addr.arpa ip6.arpa {
           pods insecure
           fallthrough in-addr.arpa ip6.arpa
           ttl 30
        }
        prometheus :9153
        forward . /etc/resolv.conf {
           max_concurrent 1000
        }
        cache 30
        loop
        reload
        loadbalance
    }
kind: ConfigMap
metadata:
  name: coredns
  namespace: kube-system
EOF

测试

手动创建 ServiceImport 资源,将服务地址配置为我本地运行的 web 服务 192.168.1.174:8080,来模拟其他集群的服务。类型设置为 ClusterSetIP:

apiVersion: multicluster.x-k8s.io/v1alpha1
kind: ServiceImport
metadata:
  name: my-svc
  namespace: default
spec:
  ips:
  - 192.168.1.174 
  type: "ClusterSetIP"
  ports:
  - name: http
    protocol: TCP
    port: 8080
  sessionAffinity: None

接下来测试下多集群服务 DNS 的解析,运行一个 pod 来模拟客户端:

kubectl run tmp-shell --rm -i --tty --image nicolaka/netshoot -n default

查询 my-svc.default.svc.clusterset.local 的解析,测试 A 记录和 SRV 记录的解析:

dig +short my-svc.default.svc.clusterset.local
192.168.1.174

dig +short SRV my-svc.default.svc.clusterset.local
0 100 8080 my-svc.default.svc.clusterset.local.

应用也可以正常访问:

curl my-svc.default.svc.clusterset.local:8080
Hi, there!

总结

这篇文章中我们通过 DNS 解决了跨集群的服务发现问题,相信看到这里大家也发现了 DNS 方案的弊端:

  1. 引入了新的 DNS 域 clusterset.local,需要改动应用代码。
  2. 两个集群间的网络要打通,对底层的网络架构要求高,如果涉及跨云跨数据中心的话这个问题会更加明显。
  3. 多集群服务 DNS 插件目前还属于 external plugin,还要像上面那样重新编译、部署 CoreDNS

其中,由于目前 KEP1645 还处于提案阶段,待正式发布之后,相信 #3 的问题待插件进入核心插件列表后可以解决。

而 #1 的问题,假如当前应用内的代码中是使用 NAME.NS 的方式的话,可以通过在 Pod /etc/resolv.conf增加 clusterset.local 搜索域来解决。由于 Kubernetes 集群只支持一个 clusterDomain,只能通过 pod.dnsConfig 在添加搜索域了。单又会来另一个问题:搜索域的增加势必带来性能的开销。

这里 #2 的可能最为棘手,需要调整底层的网络架构,使用扁平网络或者其他如 vxlan、隧道等方案。

我曾经在另一篇文章中介绍过使用 7 层网络解决 #2 的网络互通问题,有兴趣可以看 这篇。

不知道你是否有更好的方案?欢迎留言讨论。

参考资料

[1] 从单 Kubernetes 集群到多 Kubernetes 集群、从多 Kubernetes 集群到 Kubernetes 多集群的演进: https://atbug.com/kubernetes-cluster-evolution-in-multi-hybrid-cloud/

[2] KEP-1645: Multi-Cluster Services API: https://github.com/kubernetes/enhancements/tree/master/keps/sig-multicluster/1645-multi-cluster-services-api#kep-1645-multi-cluster-services-api

[3] 认识一下 Kubernetes 多集群服务 API: https://atbug.com/kubernetes-multi-cluster-api/

[4] 多集群服务 DNS: https://github.com/kubernetes/enhancements/blob/master/keps/sig-multicluster/1645-multi-cluster-services-api/README.md#dns

[5] Kubernetes 基于 DNS 的多集群服务发现规范: https://github.com/kubernetes/enhancements/blob/master/keps/sig-multicluster/1645-multi-cluster-services-api/specification.md

[6] Kubernetes 的基于 DNS 的服务发现规范: https://github.com/kubernetes/dns/blob/master/docs/specification.md

[7] multicluster: https://coredns.io/explugins/multicluster/

[8] 核心插件: https://coredns.io/plugins/

[9] 外部插件(External Plugin): https://coredns.io/explugins

[10] 创建一个控制器: https://github.com/coredns/multicluster/blob/3afa45c2996fd02d1d781b74c58da0d0e6855f18/multicluster.go#L69

[11] ServiceImports: https://github.com/coredns/multicluster/blob/3afa45c2996fd02d1d781b74c58da0d0e6855f18/controller.go#L95

[12] 检索出匹配的结果: https://github.com/coredns/multicluster/blob/3afa45c2996fd02d1d781b74c58da0d0e6855f18/controller.go#L300

责任编辑:武晓燕 来源: 云原生指北
相关推荐

2020-12-14 14:21:55

NEC

2021-03-11 12:31:33

K8sCoreDNSDNS服务器

2019-07-26 14:31:43

服务器集群部署

2022-05-24 09:00:00

云计算Kubernetes安全

2022-07-24 21:11:19

KubernetesLinux

2021-02-07 08:00:00

Kubernetes集群云原生

2021-12-24 10:47:49

Kubernetes容器化微服务

2009-06-11 09:35:47

GlassFish配置多机集群

2010-09-15 14:50:57

集群负载均衡Tomcat

2010-11-11 09:06:38

Linux集群LVS

2009-06-12 17:45:16

JMS集群JBoss集群

2019-04-02 09:01:47

CoreDNSDNS污染

2022-01-12 11:55:43

Kubernetes多集群Linux

2021-11-22 16:21:28

Kubernetes 运维开源

2014-02-21 15:21:29

集群共享运维人员

2009-06-12 14:27:49

JBoss集群配置

2019-10-31 09:03:12

Java集群微服务

2023-09-26 00:59:54

零配置服务网格

2021-05-28 17:59:37

K8sDNS解析CoreDNS

2021-08-09 10:20:04

Thanos监控架构
点赞
收藏

51CTO技术栈公众号