Kubernetes 服务发现

目录

  • 什么是服务发现?
  • 环境变量
  • DNS 服务
    • Linux 中 DNS 查询原理
    • Kubernetes 中 DNS 查询原理
    • 调试 DNS 服务
    • 存根域及上游 DNS

什么是服务发现?

服务发现就是一种提供服务发布和查找的服务,是基于服务架构(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 中常见的配置项有:

所有查询中,如果.
的个数少于给定的数值,则会根据search
中配置的列表依次在对应域中先进行搜索,如果没有返回,则最后再直接查询域名本身。阈值为 15。

笔者在本地试验时发现,文件 /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"]

写在后面: 1. 子曰:「学而不思则罔,思而不学则殆」。 2. 站点地图。 2. 本作品作者为 Lshare,采用知识共享署名 4.0 国际许可协议进行许可。

本文分享自微信公众号 - DotNet技术平台(DotNetCore_Mements)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-08-16

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏从ORACLE起航,领略精彩的IT技术。

3.1 解压GI的安装包

Linux平台 Oracle 19c RAC安装指导: Part1:Linux平台 Oracle 19c RAC安装Part1:准备工作 Part2:Lin...

11620
来自专栏左手编程右手文化

Jenkins常见问题集锦(一)

参考:Jenkins和Docker结合可以将容器作为Jenkins的slave节点,有很多优点。比如实现执行环境的统一,slave的自动创建和销毁,免去了人工维...

19550
来自专栏从ORACLE起航,领略精彩的IT技术。

4.1 解压DB的安装包

Linux平台 Oracle 19c RAC安装指导: Part1:Linux平台 Oracle 19c RAC安装Part1:准备工作 Part2:Lin...

11410
来自专栏Java编程指南

程序人生:初学者最常问的几个问题

隔行如隔山,初学编程往往不知道从何入手,非常迷茫,以下几个问题是我经常被问到的,总结出来分享给读者。

9140
来自专栏Linux内核及编程语言底层相关技术研究

对又一个epoll问题的全面分析

该方法最主要的目的是,当客户端建立tcp连接到服务端时,服务端立即调用shutdown方法,关闭其send方向。

11130
来自专栏数据分析1480

大数据之脚踏实地学03--Linux的常用文件级命令

众所周知,大数据应用工具(如Hadoop、Storm、Hive、Spark等)都是运行于Linux系统中。所以,第一步要做的就是认识并操作Linux系统(正如你...

9620
来自专栏数据分析1480

大数据之脚踏实地学01--虚拟机和Linux系统的安装

曾记得我在读研的时候,参加了中国统计年会(2013年),在会上很多领域内的专家都谈及了大数据一词,然而那个时候的我并没有那么敏感。短短5年过去了,大数据行业发展...

17020
来自专栏有困难要上,没有困难创造困难也要上!

Singularity入门之运行图形应用

要在 Singularity 中运行图形程序需要重新制作一个镜像,使其包含执行图形应用程序需要的环境和程序,这里还是通过 sandbox 的方式来制作一个可以运...

16530
来自专栏算法工程师之路

Linux下GDB调试指令总结

之前写C++的一些程序都是在windows下,直接使用VS2017的傻瓜式编译器,最近尝试摸索在linux进行C++程序的编译,有了一些成果!特此总结!

36520
来自专栏指尖下的Android

Linux中IO多路复用机制

之前的面试有问到主线程在 ActivityThread 里初始化 Looper 后调用了 Looper.loop() 这个死循环为什么不会阻塞主线程,当时回答因...

17820

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励