在kubernetes中配置skydns

背景

kubernetes中的集群发现有两种方式,一种是环境变量,还有一种就是dns服务。对于环境变量,在使用上有一些限制,它依赖于svc和rc的启动顺序: 如果rc先于svc启动,那么pod里面就没有相关svc的环境变量,这种方式违背了kubernetes的理念,即资源之间是解耦的。推荐的方式是使用dns的服务,这篇文章主要关于部署skydns的过程以及其中遇到的问题和采用的方式。

部署

关于skydns如何部署在已有的kubernetes集群中,网上有不少资料可以参考,这里贴上一篇文章: kubernetes入门之skydns。想要部署一个toy版本可以参考一下。

问题 & 方案

在实际应用的过程中,我需要改变一些配置,其中遇到的坑就记录在此。

http vs https

网络上所有关于skydns的配置,全都是基于http的。比如下面这一条:

1
2
3
4
command:
- /kube2sky
- --kube_master_url=http://10.8.65.48:8080
- -domain=kube.local

kube-apiserver监听了两个端口,一个是对外的https,一个是对内的http,两者提供的服务是一样的。

默认的http端口是监听在127.0.0.1:8080上的,对于这个问题,有几个可行的方案:

  1. 修改apiserver的配置,让它监听在某个内网ip上,skydns通过内网ip去访问apiserver
  2. 通过外网ip访问,建立iptables路由规则,将外网的流量转发到127.0.0.1:8080
    这个方案需要注意添加ip白名单,防止外网恶意流量的入侵,由于事先并不知道调度器会把dns服务送往哪台机器,需要预先对所有集群机器的ip加白名单。
  3. 将dns服务通过label绑定在apiserver所在的机器上,直接访问本地外网ip
    (备注,不确定本地docker里面访问external_ip:8080可不可以被转发到127.0.0.1:8080,需要试试。如果不行的话,这条方案直接枪毙。)

但我所遇到的情况更苛刻,kubernetes集群中的机器分布于不同的idc机房,运营商也不全是同一个。这个时候除了自己搭建vpn内网,就没有办法使用内网ip了。由于自己维护vpn内网的成本太大,因此这里就不考虑方案一了。
对于方案二,需要预先加很多白名单。之后如果集群有新机器进来,又要加一遍,维护成本也很大,暂时放弃。
对于方案三,即使可行,但我也不想把dns服务绑死在某一台机器上,集群调度本身就应该有足够的自由度。

想了又想,要不试试使用https的端口吧。但让我失望的是,google到处搜,也没有搜到skydns用https的方案。最后还是在kube2sky的github主页找到了某个可能可用的配置选项:

1
2
--kube-master-url: URL of kubernetes master. Required if --kubecfg_file is not set.
--kubecfg-file: Path to kubecfg file that contains the master URL and tokens to authenticate with the master.

在skydns-rc.yaml中去掉--kube_master_url,然后使用--kubecfg_file,指向kubeconfig文件,这里我偷个懒使用的是kubelet的配置文件(kubelet.kubeconfig):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: v1
kind: Config
current-context: kubelet-to-cluster.local
preferences: {}
clusters:
- cluster:
certificate-authority: /etc/kubernetes/certs/ca.crt
server: https://node-1-master:
name: cluster.local
contexts:
- context:
cluster: cluster.local
user: kubelet
name: kubelet-to-cluster.local
users:
- name: kubelet
user:
token: J1j0EhcoCBZBAP20xDR72ta79vPr7j3J

目录挂载

在上一部中指定了--kubecfg_file=/etc/kubernetes/kubelet.kubeconfig还是不够的,启动的时候会报错,说找不到配置文件。原因是这个路径只是作为一个字符串传进了pod中,pod中并没有这个路径对应的文件,我们还需要做目录的挂载:

1
2
3
4
5
# 省略很多配置
volumeMounts:
- name: kubernetes-etc
mountPath: /etc/kubernetes
readOnly: true

1
2
3
4
5
# 省略很多配置
volumes:
- name: kubernetes-etc
hostPath:
path: /etc/kubernetes

在我以为大功告成的时候,结果还是给了我最后一个坑。这个时候kube2sky报了一个错,说无法解析node-1-master。
因为集群中所有机器都是以这种短名字命名的,它们对应的ip都配在所有机器的/etc/hosts里。而pod中的/etc/hosts并不是继承于宿主机的,所以自然就无法解析这些短名字了。最后解决的方法也跟上面一样,把/etc/hosts这个文件挂载到skydns的pod中就可以了。

总结

我的方案可能并不是best practice,也非常感谢广大读者可以提出更好的做法,我也会即使更新在博客中,在此谢过了。