使用containerdns的理由
先说下我们为什么要使用containerdns,事实上该项目开源时间并不长,而且没有完善的社区,也没有丰富的文档。说白了,我们选中它,是因为它刚好切合我们的需求。
目前市面上支持kubernetes的开源dns并不多,除了containerdns,就只有skydns和官方的kubedns。
我们先说下官方的kubedns:
kubedns是kubernetes官方的,而且是官方出品,我们毫不怀疑,它一定是对kubernetes支持最好的,官方还提供了各种自动扩展的特性。但是有两点满足不了我的需求:
- 需要部署到kubernetes集群中,这在大多数应用场景下其实是优点。但我们有多套不同的kubernretes集群,这些集群中总不能各自都部署一套containerdns,然后再在外部部署一套bind来帮忙这些containerdns做转发吧?我们多套集群需要依赖一个公共的dns来做所有域名的解析。当然事实上,我们也可以把kubedns独立部署,但是k8s提供的和动扩展等功能就无法使用了。而且kubedns只能解析一个一级域名,比如cluster.local。如果我两套kubernetes集群,集群1使用cluster.local,集群2使用cluster2.local。这个时候,单套kubedns将变的无能为力。
- 在早期kubernetes默认采用skydns的时候,是将解析存储到etcd中。后来kubernetes使用kubedns将解析存储到内存中。虽然提升了解析效率,但是把所有解析一股脑放到内存中的做法并没有在大型应用中得到验证,如果有大量的解析情况下,内存的占用是个不得不考虑的问题。
然后说下skydns:
其实在上面说kubedns的时候,基本也都提到了skydns的缺点:
- 将数据直接存储到etcd中,每一次解析都是对etcd的访问,效率不高
- skydns也面临跟kubedns同样的问题,单套skydns只能解析一个一级域名,当我需要它同时解析cluster.local和cluster2.local的时候,它就会变的无能为力。
最后再回头说containerdns:
containerdns支持多个域名解析,最终的解析也会写入etcd中,同时会利用缓存来缓存一部分解析以提升解析效率。基本上解决了上面说到的kubedns和skydns的问题。
这就是我们最终选用containerdns的原因。
同时这里需要感谢下京东containerdns的开发团队,尤其是陈书刚大大,帮我解答了很多疑惑,也帮忙解决了很多相关的问题!
部署
项目地址:https://github.com/tigcode/containerdns
具体的项目介绍及containerdns相关原理,请直接参考项目文档,也可以直接参考这篇文档:
http://www.sohu.com/a/150240329_683048
我这里不再做详细说明。只是说下我们在使用过程中遇到的一些坑。
先简单的说下配置:
我们这里就用到了项目中的两个组件,分别是containerdns和containerdns-kubeapi。
安装方法也直接参考官网即可。
需要说明的是etcd的api需要使用v3版本
我这里采用将获取到的二进制打进镜像中,使用docker部署的方式,下面直接给出docker-compose配置:
version: "2"
services:
etcd:
image: dk-reg.op.douyuyuba.com/library/etcd-amd64:3.1.10
restart: always
network_mode: host
volumes:
- "/home/www/server/etcd:/data/etcd"
- "/etc/localtime:/etc/localtime"
environment:
ETCDCTL_API: "3"
command: etcd -name etcd1 -data-dir /data/etcd
containerdns:
image: dk-reg.op.douyuyuba.com/kubernetes/containerdns
restart: always
network_mode: host
volumes:
- "/etc/containerdns:/etc/containerdns"
environment:
ETCDCTL_API: "3"
containerdns-kubeapi:
image: dk-reg.op.douyuyuba.com/kubernetes/containerdns-kubeapi
restart: always
network_mode: host
volumes:
- "/etc/containerdns:/etc/containerdns"
environment:
ETCDCTL_API: "3"
如果不使用docker部署的话,启动方式如下:
nohup containerdns -config-file /etc/containerdns/containerdns.conf &> /tmp/containerdns.log & nohup containerdns-kubeapi -config-file /etc/containerdns/containerdns-api.conf &> /tmp/containerdns-kubeapi.log &
配置
containerdns.conf示例:
[Dns] dns-domains = wh01 dns-addr = 0.0.0.0:53 ex-nameservers = "114.114.114.114:53" inDomainServers = test.com@xx.xx.xx.xx:53 inDomainServers ="" cacheSize = 1000000 ip-monitor-path = /containerdns/monitor/status/ [Log] log-dir = /var/log/containerdns log-level = 0 log-to-stdio = true [Etcd] etcd-servers = http://127.0.0.1:2379 etcd-certfile = "" etcd-keyfile = "" etcd-cafile = "" [Fun] random-one = false hone-one = false [Stats] statsServer = 127.0.0.1:9600 statsServerAuthToken = 123456789
简要的说下:
- ex-nameservers:对于公网的地址解析,转发到公网的dns上
- inDomainServers:对于内网其他域名的解析(即不由containerdns解析的域名)转发到指定的内网dns上,可以指定多个,用
%
分隔,这里给个官方的示例:inDomainServers = hades.local@10.8.65.104:53%tmp.containerdns.local@192.168.169.41:53
- [Stats]是一个暴露的状态接口,后面会详细说明
containerdns-api.conf示例:
[General] host = xx.xx.xx.xx etcd-server = http://127.0.0.1:2379 etcd-certfile = "" etcd-keyfile = "" etcd-cafile = "" ip-monitor-path = /containerdns/monitor/status [Kube2DNS] kube-domain = wh01. kube-enable = YES kube-config-file = /etc/containerdns/bootstrap.kubeconfig #svc-ip-source = clusterIP #kube-enable=NO [DNSApi] api-enable = YES api-domains = wh01. api-address = 0.0.0.0:9003 containerdns-auth = abcdef123456789
简要的说下:
- host:用于标识当前进程运行在哪个服务器上,当有多个不同的服务器上同时启动该进程抓取数据到etcd时,用于区分数据来自哪个服务器。
- kube-config-file:这里指定的是我们连接kubernetes集群时,用的kubeconfig,需要通过该文件授权以后才能访问到kube-apiserver接口
- svc-ip-source:这个需要详细说下,我们在k8s上执行kubectl get svc时,可以看到如下输出:
kubectl get svc NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE infovod-stress None <none> 80/TCP 64d kubernetes 10.254.0.1 <none> 443/TCP 176d
可以看到,一个service对应的有两个IP,分别是CLUSTER-IP和EXTERNAL-IP,这两个ip具体的区别,请自行google,不属于containerdns的范畴。这里要说的是,默认containerdns-kube-api拿到的是EXTERNAL-IP的值,当然如果是使用headless service这种方式的话,解析会拿到实际pod的ip。但拿不到CLUSTER-IP的值,这样一来,默认情况下,创建kubernetes时,自动创建的kubernetes.default.svc的解析就拿不到了。如果你的应用依赖于CLUSTER-IP的话,则需要开启svc-ip-source选项,并将其值设置为clusterIP。
相关问题
1.手动写入解析的问题
上面我们说到svc-ip-source的问题,其实在之前,并不支持该项配置,这个时候像kubernetes.default.svc的解析,就只能通过手动写入。遗憾的是,containerdns手动的命令行配置已经废弃,我们这里又没有办法去直接调接口修改配置。所以采取了一种很粗暴的方式,直接按照其etcd中解析的存储格式,手动往etcd中插入一条数据,如下:
etcdctl put /containerdns/bj02/svc/default/kubernetes/ip/ '{"type":"A","source":"user","host":"10.254.0.1","ttl":30,"priority":10,"weight":10}'
需要说明的是,如果是由containerdns-kubeapi抓取的数据,source标识为svc,手动写入的数据,source需要标识为user,否则的话,会被定时冲掉。
2.监控的问题
关于containerdns中qps的监控,containerdns.conf配置文件提供了[Stats]的配置,默认监听到本地的9600端口,调用方式如下:
curl -H "Content-Type:application/json;charset=UTF-8" -X GET http://127.0.0.1:9600/containerdns/stats/payapi-pre.php.svc.wh01.?token=123456789 {"reqCount":5,"lastQueryTime":"2017-12-13T10:20:42.727212277+08:00","firstQueryTime":"2017-12-13T10:20:39.42317166+08:00"}
需要说明的是,如果指定的域名从没被请求解析过,会抛出domain not found
的异常。