什么是服务发现?

服务发现就是一种提供服务发布和查找的服务,是基于服务架构(SOA)应用的核心服务,需具备以下关键特性:

  1. 注册(Registration),新增服务到服务列表;
  2. 目录(Directory),即服务列表;
  3. 查找(Lookup),通过服务名找到服务。

服务发现的关键在于服务元数据(metadata)的存储,包括服务名、服务 IP、服务端口等信息。

Kubernetes 支持两种服务发现方式,环境变量和 DNS。

环境变量

当 Pod 创建时,Kubernetes 会将每个活跃的 Service 的相关环境变量设置到 Pod 中。值得注意的是,这些环境变量不会因为相关 Service 改变而改变(笔者亲手试验过)。

Kubernetes 会设置两类环境变量,分别是:

  1. Kubernetes Service 环境变量
  2. Docker Link 环境变量

Kubernetes Service 环境变量形如(假定服务名为 latte,且访问端口为 8080):

LATTE_SERVICE_HOST=10.100.251.57
LATTE_SERVICE_PORT=8080

Docker Link 环境变量形如(假定服务名为 latte,且访问端口为 8080):

LATTE_PORT_8080_TCP_ADDR=10.100.251.57
LATTE_PORT_8080_TCP_PORT=8080
LATTE_PORT_8080_TCP_PROTO=tcp
LATTE_PORT=tcp://10.100.251.57:8080
LATTE_PORT_8080_TCP=tcp://10.100.251.57:8080

可以通过进入 Pod 的终端,执行 env 命令查看设置的环境变量验证。

kubectl exec -ti <pod-name> env --namespace=<my-namespace>

此种方式的服务发现缺点很明显:

  1. 依赖的服务必须先运行起来,否则 Pod 无法发现;
  2. 如依赖的服务宕机或绑定新地址,Pod 无法发现,仍然持有旧的地址。

幸好,我们还有另一种服务发现机制。

DNS 服务

在讲述 Kubernetes 中使用 DNS 进行服务发现之前,我们不得不先了解下 Linux 中是如何进行 DNS 查询的。

Linux 中 DNS 查询原理

在 Linux 的 /etc/ 目录中,存在 3 个我们需要关注的文件,分别是(参考:http://man7.org/linux/man-pages/man5/host.conf.5.html):

  1. /etc/host.conf:DNS 解析器配置,包含 trim、multi、order、reorder 和 nospoof 等等配置。
  2. /etc/hosts:本地 hosts 数据库,存放本地的域名到 IP 的配置。
  3. /etc/resolv.conf:DNS 解析器配置,包含 nameserver、domain、search、sortlist 和 options 等配置。

下面是一台 Linux 服务器中 3 个相关文件的内容:

# /etc/host.conf
multi on
# /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost6 localhost6.localdomain6
# /etc/resolv.conf
; generated by /usr/sbin/dhclient-script
search us-west-2.compute.internal
options timeout:2 attempts:5
nameserver 192.168.0.2

/etc/resolv.conf 配置解释如下:

配置项 功能 备注
nameserver DNS 服务器 值必须是 IP 地址
domain 本地域名 域中的查询可以使用相对于本地域名的短名称
search 主机名查询列表 默认只包含本地域名。阈值为 6 个域名,256 个字符。
options 选项 修改内部 DNS 解析器变量值。

options 中常见的配置项有:

配置项 功能
ndots 所有查询中,如果.的个数少于给定的数值,则会根据search中配置的列表依次在对应域中先进行搜索,如果没有返回,则最后再直接查询域名本身。阈值为 15。
timeout 等待 DNS 服务器响应的超时时间,单位为秒。阈值为 30 s。
attempts 向同一个 DNS 服务器发起重试的次数,单位为次。阈值为 5。

笔者在本地试验时发现,文件 /etc/resolv.conf 是网络连接时自动生成的,依据是:

  1. 当本机处以断网状态时,cat /etc/resolv.conf 返回空文本;
  2. 当本机连上网络时,cat /etc/resolv.conf 返回以下内容:
#
# macOS Notice
#
# This file is not consulted for DNS hostname resolution, address
# resolution, or the DNS query routing mechanism used by most
# processes on this system.
#
# To view the DNS configuration used by this system, use:
# scutil --dns
#
# SEE ALSO
# dns-sd(1), scutil(8)
#
# This file is automatically generated.
#
nameserver 58.250.162.58
nameserver 8.8.8.8

第一个 DNS 服务器是中国联通的,通过访问 https://whois.domaintools.com/58.250.162.58 可知;

第二个 DNS服务器是 Google 的,通过 nslookup 8.8.8.8 或者访问 https://whois.domaintools.com/8.8.8.8 可知。

Kubernetes 中 DNS 查询原理

Kubernetes 中有两个可选的 DNS 服务插件(处在 kube-system 命名空间):

插件 说明
kube-dns 其代码已经从 kubernetes 库中分离到单独的仓库维护,见 https://github.com/kubernetes/dns
CoreDNS 支持 Kubernetes v1.9 及以上;Kubernetes v1.12 起,官方推荐使用来替换 kube-dns;Kubernetes v1.13 起,成为默认 DNS 服务;占用内存小,查询速度快。

注意:kube-dns 在 Kubernetes 中有多重含义,要注意区别。

  1. 与 CoreDNS 对比时,使用狭义,表示名为 kube-dns 的 DNS 服务;
  2. 当泛指时,表示 Kubernetes 中的 DNS 服务。

使用 kubeadm 创建 v1.11 及以后的 Kubernetes 集群,默认启用 CoreDNS(处于 GA 状态,见 Software release life cycle)。(来源

笔者通过 aws 提供的 eksctl 工具创建的 v1.15 的集群默认也是启用了 CoreDNS,查阅 eksctl 源码可以获取其默认启用的插件。

Kubernetes 通过修改每个 Pod 中每个容器的域名解析配置文件 /etc/resolv.conf 来达到服务发现的目的。在笔者创建的集群中获取其中一个容器的域名解析配置文件如下:

# /etc/resolv.conf
nameserver 10.100.0.10
search cafe.svc.cluster.local svc.cluster.local cluster.local us-west-2.compute.internal
options ndots:5

其含义是:DNS 服务器为 10.100.0.10,当查询关键词中 . 的数量少于 5 个,则根据 search 中配置的域名进行查询,当查询都没有返回正确响应时再尝试直接查询关键词本身。

例如,执行 host -v cn.bing.com 后我们将会看到:

Trying "cn.bing.com.cafe.svc.cluster.local"
Trying "cn.bing.com.svc.cluster.local"
Trying "cn.bing.com.cluster.local"
Trying "cn.bing.com.us-west-2.compute.internal"
Trying "cn.bing.com"
...

解析过程是如此缓慢,当对某些服务访问频繁时建议额外配置 DNS 记录。

注:获取过程如下

# 1) 查询指定命名空间中的所有 pod
kubectl get pods --namespace=cafe
# 2) 进入其中一个 pod 的交互终端
kubectl exec -ti macchiato-6746674bdd-5hmtw sh --namespace=cafe
# 3) 查看 /etc/resolv.conf
cat /etc/resolv.conf

DNS 服务器会监听着集群内所有 Service API,以在服务不可用时移除记录,在新服务创建时插入新记录。

这些记录存放在哪里呢?

答案是:存储在 kube-dns 插件中的 cache 也可配置到 etcd。

存储的 DNS 记录有哪些种类呢?

我们过去或多或少了解到的 DNS 记录有以下几种:

类别 作用
A Address record,域名到 IP 地址的记录
CNAME Canonical name record,别名记录,设置域名的别名
NS Name server record,域名服务器记录
MX Mail exchange record,邮件服务记录
TXT Text record,为某条记录设置说明
AAAA IPv6 address record,域名到 IPv6 地址 ( 128 = 32 * 4 )的记录

这次我们要多认识一个称之为 SRV(Service locator)的 DNS 记录,用来通用化地定位服务。

Kubernetes 的 DNS 服务(简称为 kube-dns)支持 Service 的 A 记录、 SRV 记录和 CNAME 记录

我们知道 Kubernetes 中的 Service 是 Pod 的逻辑分组,有 Cluster IP 和 Label Selector 有无之别。没有设置 Cluster IP 的我们称之为 Headless Service,否则称之为 Normal Service。设置了 Label Selector 的会同时产生一个 Endpoints 对象,声明集群内部 Service 的访问端点。

假定有一个 cafe 命名空间下名为 latte 的 Normal Service,开放了名为 http 的 TCP 端口 8080,kube-dns 会为其生成以下的 A 记录和 SRV 记录:

latte.cafe.svc.cluster.local. 5 IN A 10.100.71.56
_http._tcp.latte.cafe.svc.cluster.local. 30 IN SRV 10 100 443 latte.cafe.svc.cluster.local.

注:查询 DNS 记录的方法

(1)安装 dig 工具

将下面的部署配置保存到文件 dnsutils.yaml,然后执行 kubectl apply -f dnsutils.yaml 部署。

apiVersion: v1
kind: Pod
metadata:
name: dnsutils
namespace: default
spec:
containers:
- name: dnsutils
image: tutum/dnsutils
command:
- sleep
- "3600"
imagePullPolicy: IfNotPresent
restartPolicy: Always

(2)使用 dig 工具获取 DNS 记录

# 用法
dig @<DNS服务器> <记录类型> <域名> <可选值>
# 示例
## 1)获取 DNS 服务地址
kubectl get svc kube-dns -n kube-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-dns ClusterIP 10.100.0.10 <none> 53/UDP,53/TCP 8d
## 2)进入 dnsutils 的 shell 终端
kubectl exec -ti dnsutils sh
## 3)查询 latte 服务的 A 记录
dig @10.100.0.10 A latte.cafe.svc.cluster.local +noall +answer

假如有一个 cafe 命名空间下名为 mocha 的 Headless Service,kube-dns 会为其生成以下的 A 记录集(域名到 Pod IPs 的映射):

mocha.cafe.svc.cluster.local. 5 IN A 192.168.62.111
mocha.cafe.svc.cluster.local. 5 IN A 192.168.62.112
mocha.cafe.svc.cluster.local. 5 IN A 192.168.62.113

如若有一个 cafe 命名空间下名为 macchiato 的 Headless 但设置了以下 Endpoints 的 Service:

kind: Endpoints
apiVersion: v1
metadata:
name: macchiato
namespace: cafe
subsets:
- addresses:
- ip: 1.2.3.4
ports:
- port: 9376

kube-dns 会为其生成以下的 A 记录:

macchiato.cafe.svc.cluster.local. 4 IN A 1.2.3.4

如果有一个 cafe 命名空间下名为 cappuccino 的 Headless 但设置了以下 ExternalName 的 Service:

kind: Service
apiVersion: v1
metadata:
name: cappuccino
namespace: cafe
spec:
type: ExternalName
externalName: cappuccino.cafe.com

kube-dns 会为其生成以下的 CNAME 记录:

cappuccino.cafe.svc.cluster.local. 10 IN CNAME cappuccino.cafe.com.

Kubernetes 的 DNS 服务除了支持 Service 的 DNS 记录外,还支持 Pod 的 A 记录,使用 hostname + subdomain 方式实现。仔细阅读以下部署配置。

apiVersion: v1
kind: Service
metadata:
name: default-subdomain
spec:
selector:
name: busybox
clusterIP: None
ports:
- name: foo # Actually, no port is needed.
port: 1234
targetPort: 1234
---
apiVersion: v1
kind: Pod
metadata:
name: busybox1
labels:
name: busybox
spec:
hostname: busybox-1
subdomain: default-subdomain
containers:
- image: busybox:1.28
command:
- sleep
- "3600"
name: busybox
---
apiVersion: v1
kind: Pod
metadata:
name: busybox2
labels:
name: busybox
spec:
hostname: busybox-2
subdomain: default-subdomain
containers:
- image: busybox:1.28
command:
- sleep
- "3600"
name: busybox

我们发现其创建了:

  1. name 为 busybox1,hostname 为 busybox-1,subdomain 为 default-subdomain 的 Pod;
  2. name 为 busybox2,hostname 为 busybox-2,subdomain 为 default-subdomain 的 Pod;
  3. name 为 default-subdomain 的 Headless Service。

产生以下 A 记录:

busybox-1.default-subdomain.svc.cluster.local. 4 IN A 192.168.51.119
busybox-2.default-subdomain.svc.cluster.local. IN A
busybox-2.default-subdomain.svc.cluster.local. 4 IN A 192.168.36.188
default-subdomain.svc.cluster.local. 4 IN A 192.168.62.187
default-subdomain.svc.cluster.local. 4 IN A 192.168.62.188

这些记录是怎样的一种格式呢?

参见:https://github.com/kubernetes/dns/blob/master/docs/specification.md

调试 DNS 服务

使用 busybox 调试 DNS 服务,因为 busybox 中有 nslookup 工具可以使用。

(1)保存以下文本到文件 busybox.yaml(此处使用命名空间为 cafe )

apiVersion: v1
kind: Pod
metadata:
name: busybox
namespace: cafe
spec:
containers:
- name: busybox
image: busybox:1.28
command:
- sleep
- "3600"
imagePullPolicy: IfNotPresent
restartPolicy: Always

(2)应用 busybox.yaml,并查看状态

kubectl apply -f busybox.yaml --namespace=cafe
kubectl get pods busybox --namespace=cafe

(3)进入终端交互界面并支持 nslookup 查询服务 latte

kubectl exec -ti busybox sh --namespace=cafe
nslookup latte

存根域及上游 DNS

当无自定义配置时,不匹配的 DNS 查询(比如上文说的cn.bing.com)会使用从 Node 中继承的 nameserver 进行解析。

当有自定义的配置时,会在 DNS 缓存层查询无果后,根据查询名称后缀决定去往的 DNS 解析器:

  • 查询名称带有集群后缀的(比如 ".cluster.local"),转发到 kube-dns。
  • 查询名称带有存根域名后缀的(比如 ".acme.local"),转发到 custom DNS。
  • 查询名称不匹配的(比如 "widget.com"),转发到 upstream DNS。

以上配置使用 Kubernetes 的ConfigMap 插件,配置如下:

apiVersion: v1
kind: ConfigMap
metadata:
name: kube-dns
namespace: kube-system
data:
stubDomains: |
{"acme.local": ["1.2.3.4"]}
upstreamNameservers: |
["8.8.8.8","8.8.4.4"]

Kubernetes 服务发现的更多相关文章

  1. Docker Kubernetes 服务发现原理详解

    Docker Kubernetes  服务发现原理详解 服务发现支持Service环境变量和DNS两种模式: 一.环境变量 (默认) 当一个Pod运行到Node,kubelet会为每个容器添加一组环境 ...

  2. Kubernetes服务发现入门:如何高效管理服务?

    愈发复杂的应用程序正在依靠微服务来保持可扩展性和提升效率.Kubernetes为微服务提供了完美的环境,并能够让其与Kubernetes的工具组件和功能兼容.当应用程序的每个部分放置在一个容器中,整个 ...

  3. 一文看懂 Kubernetes 服务发现: Service

    Service 简介   K8s 中提供微服务的实体是 Pod,Pod 在创建时 docker engine 会为 pod 分配 ip,"外部"流量通过访问该 ip 获取微服务.但 ...

  4. Kubernetes服务发现之Service详解

    一.引子 Kubernetes Pod 是有生命周期的,它们可以被创建,也可以被销毁,然后一旦被销毁生命就永远结束.通过ReplicationController 能够动态地创建和销毁Pod(列如,需 ...

  5. 基于Kubernetes服务发现机制的探讨Non Service

    服务注册 注册中⼼作为一般的RPC/Web服务中的底层设施提供了服务进程元数据(IP, Port, Interface, Group,Method等)存储,被Watch的功能,每个服务进程均需接⼊同⼀ ...

  6. k8s入坑之路(11)kubernetes服务发现

    kubernetes访问场景 1.集群内部访问 2.集群内部访问外部 3.集群外部访问内部 1.集群内部访问 1.pod之间直接ip通讯(利用calico通过路由表经过三层将ip流量转发)由于容器之间 ...

  7. 从零开始入门 | Kubernetes 中的服务发现与负载均衡

    作者 | 阿里巴巴技术专家  溪恒 一.需求来源 为什么需要服务发现 在 K8s 集群里面会通过 pod 去部署应用,与传统的应用部署不同,传统应用部署在给定的机器上面去部署,我们知道怎么去调用别的机 ...

  8. Kubernetes 中的服务发现与负载均衡

    原文:https://www.infoq.cn/article/rEzx9X598W60svbli9aK (本文转载自阿里巴巴云原生微信公众号(ID:Alicloudnative)) 一.需求来源 为 ...

  9. prometheus k8s服务发现

    Prometheus的服务发现在解决什么问题? 被监控的目标(target)是整个监控体系中重要组成部分,传统监控系统zabbix通过 网络发现的机制自动创建主机到zabbix-server,进而快速 ...

随机推荐

  1. NOIP 2017 惊魂记

    考完了NOIP三周后才开始补……然后又补了一周…… DAY -1: 晚上吃了一顿送行宴散伙饭,然后默默地看了一遍之前所有考试后写的题解,再读了几遍板子,然后和QTY一起和达哥又一次在外面谈了一个小时, ...

  2. ServiceFabric极简文档-1.3删除群集

    删除群集 若要删除群集,请运行包文件夹中的 RemoveServiceFabricCluster.ps1 Powershell 脚本,并传入 JSON 配置文件的路径. 可以选择性地指定删除日志的位置 ...

  3. TigerGraph入门

    测试机器配置 1G内存,1个核,CentOS Linux release 7.4.1708 (Core)的云主机,一块50G HDD的云主机. 1. 安装 下载了目前最新的开发者版本,下载链接:htt ...

  4. js 使用ES6 实现从json中取值并返回新的数组或者字符串

    1.获取的json数据是这样的: data:[ { 'Id': '1', 'Phone': '123456', 'Name': '张三', }, { 'Id': '2', 'Phone': '7894 ...

  5. python爬虫笔记之爬取足球比赛赛程

    目标:爬取某网站比赛赛程,动态网页,则需找到对应ajax请求(具体可参考:https://blog.csdn.net/you_are_my_dream/article/details/53399949 ...

  6. TestNG的静态方法mock的步骤

    最近团队内部对程序中使用大量的`静态方法`,而公司要求要有sonar扫描覆盖率的,因为在大量使用静态方法的地方若不mock,则覆盖率达不到.于是网上很少的文章讲解对静态方法的mock,大多都是如何使用 ...

  7. HBase的优化

    HBase的优化 高可用 在 HBase 中 Hmaster 负责监控 RegionServer 的生命周期,均衡 RegionServer 的负载,如果 Hmaster 挂掉了,那么整个 HBase ...

  8. spark 源码分析之十九 -- Stage的提交

    引言 上篇 spark 源码分析之十九 -- DAG的生成和Stage的划分 中,主要介绍了下图中的前两个阶段DAG的构建和Stage的划分. 本篇文章主要剖析,Stage是如何提交的. rdd的依赖 ...

  9. 逆向MFC程序

    目录 @ 1 MFC执行流程 1.1 环境支持 1.2 分析 1.3 实践探索 1.3.1 创建一个MFC程序 1.3.2 下关键断点并调试 1.4 转向MFC库源文件中观测 2 逆向 2.1 特征码 ...

  10. ESP-8266 RTOS 环境搭建

    本节为 ESP-8266 RTOS 的环境搭建 只适合Linux环境,推荐Ubuntu.本例以Ubuntu16.04-x64为例 安装 git [dzlua@ubuntu: ~]$ sudo apt ...