V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Recommended Services
Amazon Web Services
LeanCloud
New Relic
ClearDB
Tenxcloud10
V2EX  ›  云计算

[段子手专业详解] Kubernetes 如何使用 kube-dns 实现服务发现

  •  
  •   Tenxcloud10 · 2016-10-17 12:26:49 +08:00 · 4962 次点击
    这是一个创建于 2960 天前的主题,其中的信息可能已经有所发展或是发生改变。

    时速云第十五期微信群直播分享新鲜出炉,本期有时速云工程师黄鑫为小伙伴们带来了关于“ Kubernetes 如何使用 kube-dns 实现服务发现”的技术分享,关于如何发现 Pod 提供的服务、如何使用 Service 发现服务,以及如何使用 kube-dns 发现服务这些让你蓝瘦香菇的问题,统统都有完整解答,以下奉上分享的文字版!(视频回放地址: http://t.cn/RVfbQG5

    大纲:

    • Kubernetes 中如何发现服务

    • 如何发现 Pod 提供的服务

    • 如何使用 Service 发现服务

    • 如何使用 kube-dns 发现服务

    • kube-dns 原理

    • 组成

    • 域名格式

    • 配置

    注:本次分享内容基于 Kubernetes 1.2 版本!

    下面从一个简单的例子开始讲解。

    1.Kubernetes 中如何发现服务

    ◆ 发现 Pod 提供的服务

    首先使用 nginx-deployment.yaml 文件创建一个 Nginx Deployment ,文件内容如图所示:

    首先创建两个运行 Nginx 服务的 Pod :

    alt 文本

    使用 kubectl create -f nginx-deployment.yaml 指令创建,这样便可以得到两个运行 nginx 服务的 Pod 。待 Pod 运行之后查看一下它们的 IP ,并在 k8s 集群内通过 podIP 和 containerPort 来访问 Nginx 服务:

    获取 Pod IP :

    alt 文本

    在集群内访问 Nginx 服务:

    alt 文本

    看到这里相信很多人会有以下疑问:

    1. 每次收到获取 podIP 太扯了,总不能每次都要手动改程序或者配置才能访问服务吧,要怎么提前知道 podIP 呢?

    2. Pod 在运行中可能会重建, IP 变了怎么解?

    3. 如何在多个 Pod 中实现负载均衡嘞? 这些问题使用 k8s Service 就可以解决。

    ◆ 使用 Service 发现服务

    下面为两个 Nginx Pod 创建一个 Service 。使用 nginx-service.yaml 文件进行创建,文件内容如下:

    alt 文本

    创建之后,仍需要获取 Service 的 Cluster-IP ,再结合 Port 访问 Nginx 服务。

    Service 可以将 pod IP 封装起来,即使 Pod 发生重建,依然可以通过 Service 来访问 Pod 提供的服务。此外, Service 还解决了负载均衡的问题,大家可以多访问几次 Service ,然后通过 kubectl logs <pod name="">来查看两个 Nginx Pod 的访问日志来确认。

    获取 IP :

    alt 文本

    在集群内访问 Service :

    alt 文本

    虽然 Service 解决了 Pod 的服务发现和负载均衡问题,但存在着类似的问题:不提前知道 Service 的 IP ,还是需要改程序或配置啊。看到这里有没有感觉身体被掏空?

    接下来聊聊 kube-dns 是如何解决上面这个问题的。

    ◆ 使用 kube-dns 发现服务

    kube-dns 可以解决 Service 的发现问题, k8s 将 Service 的名称当做域名注册到 kube-dns 中,通过 Service 的名称就可以访问其提供的服务。

    可能有人会问如果集群中没有部署 kube-dns 怎么办?没关系,实际上 kube-dns 插件只是运行在 kube-system 命名空间下的 Pod ,完全可以手动创建它。可以在 k8s 源码( v1.2 )的 cluster/addons/dns 目录下找到两个模板( skydns-rc.yaml.inskydns-svc.yaml.in )来创建,为大家准备的完整示例文件会在分享结束后提供获取方式, PPT 中只截取了部分内容。

    通过 skydns-rc.yaml 文件创建 kube-dns Pod ,其中包含了四个 containers ,这里开始简单过一下文件的主要部分,稍后做详细介绍。

    第一部分可以看到 kube-dns 使用了 RC 来管理 Pod ,可以提供最基本的故障重启功能。

    创建 kube-dns Pod ,其中包含了 4 个 containers

    alt 文本

    接下来是第一个容器 etcd ,它的用途是保存 DNS 规则。

    alt 文本

    第二个容器 kube2sky ,作用是写入 DNS 规则。

    alt 文本

    第三个容器是 skydns ,提供 DNS 解析服务。

    alt 文本

    最后一个容器是 healthz ,提供健康检查功能。

    alt 文本

    有了 Pod 之后,还需要创建一个 Service 以便集群中的其他 Pod 访问 DNS 查询服务。通过 skydns-svc.yaml 创建 Service ,内容如下:

    alt 文本

    创建完 kube-dns Pod 和 Service ,并且 Pod 运行后,便可以访问 kube-dns 服务。

    下面创建一个 Pod ,并在该 Pod 中访问 Nginx 服务:

    创建之后等待 kube-dns 处于运行状态

    alt 文本

    再新建一个 Pod ,通过其访问 Nginx 服务

    alt 文本

    在 curl-util Pod 中通过 Service 名称访问 my-nginx Service :

    alt 文本

    只要知道需要的服务名称就可以访问,使用 kube-dns 发现服务就是那么简单。

    虽然领略了使用 kube-dns 发现服务的便利性,但相信有很多人也是一头雾水: kube-dns 到底怎么工作的?在集群中启用了 kube-dns 插件,怎么就能通过名称访问 Service 了呢?

    2.kube-dns 原理

    ◆ Kube-dns 组成

    之前已经了解到 kube-dns 是由四个容器组成的,它们扮演的角色可以通过下面这张图来理解。

    alt 文本

    其中:

    ● SkyDNS 是用于服务发现的开源框架,构建于 etcd 之上。作用是为 k8s 集群中的 Pod 提供 DNS 查询接口。项目托管于 https://github.com/skynetservices/skydns

    ● etcd 是一种开源的分布式 key-value 存储,其功能与 ZooKeeper 类似。在 kube-dns 中的作用为存储 SkyDNS 需要的各种数据,写入方为 kube2sky ,读取方为 SkyDNS 。项目托管于 https://github.com/coreos/etcd

    ● kube2sky 是 k8s 实现的一个适配程序,它通过名为 kubernetes 的 Service (通过 kubectl get svc 可以查看到该 Service ,由集群自动创建)调用 k8s 的 list 和 watch API 来监听 k8s Service 资源的变更,从而修改 etcd 中的 SkyDNS 记录。代码可以在 k8s 源码( v1.2 )的 cluster/addons/dns/kube2sky/目录中找到。

    ● exec-healthz 是 k8s 提供的一种辅助容器,多用于 side car 模式中。它的原理是定期执行指定的 Linux 指令,从而判断当前 Pod 中关键容器的健康状态。在 kube-dns 中的作用就是通过 nslookup 指令检查 DNS 查询服务的健康状态, k8s livenessProbe 通过访问 exec-healthz 提供的 Http API 了解健康状态,并在出现故障时重启容器。其源码位于 https://github.com/kubernetes/contrib/tree/master/exec-healthz

    ● 从图中可以发现, Pod 查询 DNS 是通过 ServiceName.Namespace 子域名来查询的,但在之前的示例中只用了 Service 名称,什么原理呢?其实当我们只使用 Service 名称时会默认 Namespace 为 default ,而上面示例中的 my-nginx Service 就是在 default Namespace 中,因此是可以正常运行的。关于这一点,后续再深入介绍。

    ● skydns-rc.yaml 中可以发现 livenessProbe 是设置在 kube2sky 容器中的,其意图应该是希望通过重启 kube2sky 来重新写入 DNS 规则。

    ◆ 域名格式

    接下来了解一下 kube-dns 支持的域名格式,具体为:<service_name>.<namespace>.svc.<cluster_domain>。

    其中 cluster_domain 可以使用 kubelet 的--cluster-domain=SomeDomain 参数进行设置,同时也要保证 kube2sky 容器的启动参数中--domain 参数设置了相同的值。通常设置为 cluster.local 。那么之前示例中的 my-nginx Service 对应的完整域名就是 my-nginx.default.svc.cluster.local 。看到这里,相信很多人会有疑问,既然完整域名是这样的,那为什么在 Pod 中只通过 Service 名称和 Namespace 就能访问 Service 呢?下面来解释其中原因。

    3.配置

    ◆ 域名解析配置

    为了在 Pod 中调用其他 Service , kubelet 会自动在容器中创建域名解析配置(/etc/resolv.conf ),内容为:

    alt 文本

    感兴趣的可以在网上查找一些 resolv.conf 的资料来了解具体的含义。之所以能够通过 Service 名称和 Namespace 就能访问 Service ,就是因为 search 配置的规则。在解析域名时会自动拼接成完整域名去查询 DNS 。

    刚才提到的 kubelet --cluster-domain 参数与 search 的具体配置是相对应的。而 kube2sky 容器的--domain 参数影响的是写入到 etcd 中的域名, kube2sky 会获取 Service 的名称和 Namespace ,并使用--domain 参数拼接完整域名。这也就是让两个参数保持一致的原因。

    ◆ NS 相关配置

    kube-dns 可以让 Pod 发现其他 Service ,那 Pod 又是如何自动发现 kube-dns 的呢?在上一节中的 /etc/resolv.conf 中可以看到 nameserver ,这个配置就会告诉 Pod 去哪访问域名解析服务器。

    alt 文本

    相应的,可以在之前提到的 skydns-svc.yaml 中看到 spec.clusterIP 配置了相同的值。通常来说创建一个 Service 并不需要指定 clusterIP , k8s 会自动为其分配,但 kube-dns 比较特殊,需要指定 clusterIP 使其与 /etc/resolv.conf 中的 nameserver 保持一致。

    修改 nameserver 配置同样需要修改两个地方,一个是 kubelet 的--cluster-dns 参数,另一个就是 kube-dns Service 的 clusterIP 。

    4.总结

    接下来重新梳理一下本文的主要内容:

    ● 在 k8s 集群中,服务是运行在 Pod 中的, Pod 的发现和副本间负载均衡是我们面临的问题。

    ● 通过 Service 可以解决这两个问题,但访问 Service 也需要对应的 IP ,因此又引入了 Service 发现的问题。

    ● 得益于 kube-dns 插件,我们可以通过域名来访问集群内的 Service ,解决了 Service 发现的问题。

    ● 为了让 Pod 中的容器可以使用 kube-dns 来解析域名, k8s 会修改容器的 /etc/resolv.conf 配置。 有了以上机制的保证,就可以在 Pod 中通过 Service 名称和 namespace 非常方便地访问对应的服务了。

    5.Q&A

    1. 问: 请问如果公司已有的应用接入的话,那么现有的没有接入的应用就无法访问了,因为这个服务发现是 k8s 集群内部的,外部无法访问。主要是 k8s 集群内的服务和集群外的己有的生产的域名访问如何实现,能在 kube - dns 整合吗?

    答:两个问题可以抽象为 k8s 集群内与集群外服务连通性问题,我们从两个方面讲:

    一、集群内访问集群外

    这个问题比较简单,集群内的 Pod 会继承 Node 上的 DNS 解析规则。因此只要 Node 可以访问的服务, Pod 中也可以访问到。

    另外,在 1.4 版本中, k8s 支持了一种 ExternalName 类型的 Service ,可以与一个公网域名绑定,通过该 Service 可以访问对应公网服务。

    二、集群外访问集群内

    1. 可以将 Service 设置为 NodePort 类型,这样通过任意 Node 的 IP 和 Service Port 便可以访问 Service 。适合对外的 Service 比较少的场景。

    2. 通过 kube-proxy 可以对外暴露集群内的服务。

    3. 根据实际情况在集群内自定义实现反向代理。

    4. 问:我想问下 etcd 这个容器可以省掉吗? k8s 集群有 etcd 不可以共用吗?

    答:理论上可以共用 etcd 。从隔离性的角度考虑来说还是分开好,这样 kube-dns 服务不会对整个 k8s 集群的稳定性产生影响。另外如果把 kube-dns 看做一个微服务的话,那么应该保证内部组件不依赖外部,可以独立运行。

    精彩抢先看

    下次分享将于 10 月 27 日进行,感兴趣的小伙伴赶紧添加微信号:时速云小助手(tenxcloud6)观看直播吧!

    目前尚无回复
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2786 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 07:51 · PVG 15:51 · LAX 23:51 · JFK 02:51
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.