龙空技术网

动手动脑学Kubernetes系列教程之服务发现介绍

爱编程的查理 35

前言:

当前我们对“centos待机后如何唤醒”都比较讲究,咱们都需要剖析一些“centos待机后如何唤醒”的相关文章。那么小编也在网上汇集了一些有关“centos待机后如何唤醒””的相关文章,希望小伙伴们能喜欢,大家一起来学习一下吧!

在之前的文章中, 介绍了搭建好的#minikube#环境,如果你现在还没有一个可用的minikube环境, 那么可以去该篇文章中直接下载;

在之前的文章中, 先后介绍了如何从源代码开始构建一个Node.js应用和Spring Boot 应用, 并且部署到Kubernetes 中(这个Kubernetes 环境主要是之前建好的#minikube#) , 现在我们开始进一步深入的学习Kubernetes, 用一个个可以实际运行的例子的形式, 深入理解#Kubernetes#的概念以及原理.

在#动手动脑学Kubernetes#系列教程中, 我们展示了Kubernetes的基本用法

在第一篇里, 学习了Pod的基本知识;在第二篇里, 学习了标签(Label)的语法, 使用Label来选择和过滤Kubernetes 资源;在第三篇, 介绍了Deployment的使用, 介绍了Deployment与Replica Set、Pod的关系, 并展示了如何进行应用的版本回滚;在第四篇中, 介绍了Service的使用,使用Replication Controller创建Pod, 并创建Service, 展示了从Service 调用应用的方法; 随后又展示了扩展 Pod的数量为2, 比较了Service和之前的不同, 基本展示了Cluster IP 类型的Service的基本用法.在第五篇中, 介绍了Namespace的使用, 除了创建,列出系统的Namespace之外, 还说明Namespace 如何对资源进行隔离, 建立Development, Staging, Production等环境的方法.

在这篇中,我们将介绍Service Discovery的基本用法, 继续愉快地学习吧!

动手动脑学Kubernetes

引子

Service Discovery是Kubernetes如何连接到服务的过程。 尽管存在基于可用环境变量(environment variables)的服务发现选项,但是基于DNS的服务发现是更可取的。

众所周知,Kubernetes Master 节点存储所有Service定义和更新, Service同时也负责Frontend Pod和backend Pod的通信和负载平滑, 但是首先frontend Pod 就需要知道它们的请求首先要发到哪里。Pod 既可以把网络信息存储到环境变量(environment variables)中, 但是从长远来看这是不可行的。 如果网络详细信息和一组后端Pod将来发生更改,则客户端Pod将无法与其通信。

为了解决这个问题, Kubernetes 引入了内置的DNS 系统, Kube-DNS和CoreDNS是可选的Kubernetes DNS 模块,用于定义DNS命名规则(Naming rules),并把Pod和Service 的DNS解析为其相应的群集IP。

借助DNS,backend Pod 就可以通过引用Service的DNS 来引用这些Service, 而且不限数量。 DNS的命名方案也遵循可预测的模式,从而使各种Service的地址更容易记忆。 Service可以通过完全限定域名(FQDN)进行引用(如果对FQDN不太理解, 可以到上一篇文章中学习),也可以仅仅通过Service 名字本身进行引用.

Fully Qualified Domain Name:完全合格的域名, 绝对域名

完全合格的域名(通常称为FQDN)是我们所谓的绝对域名。 DNS系统中的域可以相对彼此指定,因此可能有些模棱两可。 FQDN是绝对名称,用于指定其相对于域名系统绝对根的位置。

这意味着它将指定每个顶级域,包括TLD。正确的FQDN以点结尾,指示DNS层次结构的根。

FQDN的一个示例是“ mail.baidu.com.”。有时,要求FQDN的软件不需要结尾点,但是需要结尾点才能符合ICANN标准。

为了展示Service Discovery, 还是先分别创建Replication Controller 和Service.

从镜像开始,创建Replication Controller和Service

让我们创建一个名为thesvc的服务,并创建一个RC来监督这些Pod.

创建Replication Controller :

Github 地址:

yaml 文件内容:

apiVersion: v1kind: ReplicationControllermetadata:  name: rcsisespec:  replicas: 2  selector:    app: sise  template:    metadata:      name: somename      labels:        app: sise    spec:      containers:      - name: sise        image: quay.io/openshiftlabs/simpleservice:0.5.0        ports:        - containerPort: 9876

这个RC 的内容还是跟之前一样, 先创建.

因为这篇的内容跟上一篇的操作差不多, 在创建之前,先检查和清理一下, 先删除Service, 再删除Replication Controller:

$ kubectl get svcNAME                  TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGEecho-service          NodePort    10.111.46.73     <none>        80:30080/TCP     8dhello-minikube        NodePort    10.109.238.121   <none>        8080:21724/TCP   9dhello-springwebflux   ClusterIP   10.107.17.10     <none>        8080/TCP         7d19hkubernetes            ClusterIP   10.96.0.1        <none>        443/TCP          42dnode-service          NodePort    10.107.42.162    <none>        8080:33557/TCP   9dsimpleservice         ClusterIP   10.108.216.135   <none>        80/TCP           24h$ kubectl get rcNAME     DESIRED   CURRENT   READY   AGErcsise   2         2         2       24h$ kubectl delete svc simpleserviceservice "simpleservice" deleted$ kubectl delete rc rcsisereplicationcontroller "rcsise" deleted

创建:

$ kubectl apply -f  created

这个RC 将创建两个Pod副本:

$ kubectl get pods -l app=sise --show-labelsNAME           READY   STATUS    RESTARTS   AGE     LABELSrcsise-29jjs   1/1     Running   0          2m42s   app=sisercsise-nfd25   1/1     Running   0          2m42s   app=sise

查看这些Pod的详情:

$ kubectl describe pods -l app=siseName:         rcsise-29jjsNamespace:    defaultPriority:     0Node:         localhost.localdomain/10.0.2.15Start Time:   Wed, 10 Mar 2021 04:17:12 +0000Labels:       app=siseAnnotations:  <none>Status:       RunningIP:           172.17.0.12IPs:  IP:           172.17.0.12Controlled By:  ReplicationController/rcsiseContainers:  sise:    Container ID:   docker://f673f32a9f7b14be46ab9e4a46d4109acf487afc8d49e71a88a780eacf3a7fcb    Image:          quay.io/openshiftlabs/simpleservice:0.5.0    Image ID:       docker-pullable://quay.io/openshiftlabs/simpleservice@sha256:72bfe1acc54829c306dd6683fe28089d222cf50a2df9d10c4e9d32974a591673    Port:           9876/TCP    Host Port:      0/TCP    State:          Running      Started:      Thu, 11 Mar 2021 03:26:20 +0000    Last State:     Terminated      Reason:       Error      Exit Code:    137      Started:      Wed, 10 Mar 2021 12:11:46 +0000      Finished:     Wed, 10 Mar 2021 14:52:45 +0000    Ready:          True    Restart Count:  3    Environment:    <none>    Mounts:      /var/run/secrets/kubernetes.io/serviceaccount from default-token-q77th (ro)Conditions:  Type              Status  Initialized       True   Ready             True   ContainersReady   True   PodScheduled      True Volumes:  default-token-q77th:    Type:        Secret (a volume populated by a Secret)    SecretName:  default-token-q77th    Optional:    falseQoS Class:       BestEffortNode-Selectors:  <none>Tolerations:     node.kubernetes.io/not-ready:NoExecute op=Exists for 300s                 node.kubernetes.io/unreachable:NoExecute op=Exists for 300sEvents:          <none>  ......

注意这个Pod 的ip 是172.17.0.12.

下面来创建Service, 名称为thesvc

Github 地址:

yaml 文件内容:

apiVersion: v1kind: Servicemetadata:  name: thesvcspec:  ports:    - port: 80      targetPort: 9876  selector:    app: sise

创建:

$ kubectl apply -f  createdkubectl get svc thesvcNAME     TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGEthesvc   ClusterIP   10.103.72.152   <none>        80/TCP    28s

可以看到, 已经成功了.

现在,我们要从群集内(例如,从另一个服务)连接到thesvc服务。 为了模拟这一点,我们在相同的名称空间中创建了一个跳转Pod(Jump Pod), 这个Pod 没有什么特别的功能,就是在centos 上启动shell,执行一个sleep, 间歇性唤醒.

Github 地址:

kubectl apply -f

yaml 文件内容:

apiVersion:   v1kind:         Podmetadata:  name:       jumpodspec:  containers:  - name:     busybox    image:    busybox:1.28    command:      - "bin/bash"      - "-c"      - "sleep 10000"

注意: 我们在创建这个jump pod的时候, 使用的镜像是busybox, 这是Docker 社区维护的'官方' 镜像, 号称是Linux的瑞士军刀, 体积小而且功能强大, 携带了多种强大的诊断工具.

BusyBox将许多常见UNIX实用程序的微型版本组合到一个小型可执行文件中,大小在1M~5M之间。 它代替了通常在GNU fileutils,shellutils等中发现的大多数实用程序。 但是,所包含的选项提供了预期的功能,并且其行为与GNU对应项非常相似。 BusyBox为任何小型或嵌入式系统提供了一个相当完整的环境。

执行:

$ kubectl apply -f  created$ kubectl get pods jumpodNAME     READY   STATUS             RESTARTS   AGEjumpod   0/1     CrashLoopBackOff   6          6m24s

Jump Pod创建成功!

检验Kubernetes DNS 是否工作

先来看看Kube DNS的解析功能是否正常:

$ kubectl exec -it jumpod -- busybox nslookup kubernetes.defaultServer:    10.96.0.10Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.localName:      kubernetes.defaultAddress 1: 10.96.0.1 kubernetes.default.svc.cluster.local

能解析出kubernetes.default,说明kube dns 是正常工作的.

另外多提一点, Kubernetes.default 是一个存在于default 命名空间下面的一个Service, 会把所有的请求信息都转发到Kubernetes Master 节点, 也就是Kubernetes API Server. 因此, 所有从Cluster 到Kubernetes.default的请求信息, 都会被路由并转发到Kubernetes Master的IP地址.

验证一下,先来看kubernetes服务的详情:

$ kubectl describe  svc kubernetesName:              kubernetesNamespace:         defaultLabels:            component=apiserver                   provider=kubernetesAnnotations:       <none>Selector:          <none>Type:              ClusterIPIP Families:       <none>IP:                10.96.0.1IPs:               10.96.0.1Port:              https  443/TCPTargetPort:        8443/TCPEndpoints:         10.0.2.15:8443Session Affinity:  NoneEvents:            <none>

可以看出, kubernetes这个Service 的Endpoint 端口是10.0.2.15, 这恰恰就是我们的Cluster的IP地址:

$ kubectl cluster-info Kubernetes control plane is running at  is running at 

再来检查一下Kubernetes DNS 是否已经运行:

$ kubectl get pods  --namespace=kube-system -l k8s-app=kube-dns --show-labelsNAME                      READY   STATUS    RESTARTS   AGE   LABELScoredns-74ff55c5b-fs5jr   1/1     Running   23         43d   k8s-app=kube-dns,pod-template-hash=74ff55c5b

可以看到, 在我目前使用的minikube 环境中, 这个kube-dns的Pod 名称是coredns-74ff55c5b-fs5jr, label是k8s-app:kube-dns, 这就很有意思, 名称中带有coredns, 但是label 是kube-dns. 这个等以后的中专门来讲解Kubernete DNS再来解释.

检查一下Service的 IP 地址是否已经分配:

kubectl get services --namespace=kube-system -l k8s-app=kube-dnsNAME       TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)                  AGEkube-dns   ClusterIP   10.96.0.10   <none>        53/UDP,53/TCP,9153/TCP   43d

下面看一下具体的Pod 和Service 的DNS 信息

查看Service 和Pod的DNS信息

Kubernetes会为Service和Pod创建DNS记录。 通过DNS, Pod就可以使用一致的DNS名称而不再需要使用IP地址来联系和访问Service。

此我们先来看看Pod 的DNS 记录, Pod把DNS 记录在文件 /etc/resolv.conf中:

$ kubectl exec rcsise-29jjs -- cat /etc/resolv.conf nameserver 10.96.0.10search default.svc.cluster.local svc.cluster.local cluster.local minikube.internaloptions ndots:5 

resolv.conf文件内容:为

nameserver 10.96.0.10search default.svc.cluster.local svc.cluster.local cluster.local minikube.internaloptions ndots:5 

可以看到, Name Server(名称服务器) 的ip 地址为10.96.0.10, 这跟我们上面查询到的Kube-dns的ip地址是一致的.

名称服务器是用于将域名转换为IP地址的计算机。 这些服务器在DNS系统中完成大部分工作。 由于任何一台服务器的域名转换总数太多,因此每台服务器都可能将请求重定向到其他名称服务器,或将责任委托给它们负责的子域的子集。

名称服务器可以是“权威性的”,这意味着它们可以为在其控制下的域查询提供答案。 否则,它们可能指向其他服务器,或提供其他名称服务器数据的缓存副本。

我们知道, 每个Service 在创建之初, 都会被分配一个DNS 记录, 名称格式为:

<service-name>.<namespace-name>.svc.cluster.local

我们来看看我们之前创建的Service, 名称为thesvc, 而且我们用的namespace 默认的default, 那么这个FQDN就应该是thesvc.default.svc.cluster.local svc.cluster.local

尝试解析一下, 注意我们的命令使用的是busybox nslookup thesvc.default.svc.cluster.local:

kubectl exec -it jumpod  -- busybox nslookup thesvc.default.svc.cluster.localServer:    10.96.0.10Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.localName:      thesvc.default.svc.cluster.localAddress 1: 10.103.72.152 thesvc.default.svc.cluster.local

可以看到, 我们在jumpod 里面通过调用busybox 工具箱里面的nslookup, 成功解析出我们thesvc的IP和FQDN地址的映射关系

10.103.72.152 <=============================>thesvc.default.svc.cluster.local

让我们来验证一下这个thesvc的IP地址是否正确:

$ kubectl describe svc thesvcName:              thesvcNamespace:         defaultLabels:            <none>Annotations:       <none>Selector:          app=siseType:              ClusterIPIP Families:       <none>IP:                10.103.72.152IPs:               10.103.72.152Port:              <unset>  80/TCPTargetPort:        9876/TCPEndpoints:         172.17.0.11:9876,172.17.0.12:9876Session Affinity:  NoneEvents:            <none>

可以看出, IP 地址是一致的, Service 的FQDN地址解析是成功的.

同样, 我们也可以检查一下Pod 的FQDN地址, Pod的FQDN 格式, 就是把IP 作为自己的名称, thesvc 下面的两个Pod 的FQDN 地址,按照规则分布是:

172-17-0-11.thesvc.default.svc.cluster.local172-17-0-12.thesvc.default.svc.cluster.local

因此, 用nslookup 查询第一个:

$kubectl exec -it jumpod -- busybox nslookup 172-17-0-11.thesvc.default.svc.cluster.localServer:    10.96.0.10Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.localName:      172-17-0-11.thesvc.default.svc.cluster.localAddress 1: 172.17.0.11 172-17-0-11.thesvc.default.svc.cluster.local

可见, Pod 的FQDN 地址也是可以解析成功的.

而且, 因为是同一个Namespace, 所以我们不需要指定FQDN的全称, 只需要相对名称即可:

$kubectl exec -it jumpod -- busybox nslookup 172-17-0-11.thesvcServer:    10.96.0.10Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.localName:      172-17-0-11.thesvcAddress 1: 172.17.0.11 172-17-0-11.thesvc.default.svc.cluster.local

到这里位置,我们已经展示了如何查看Service 和Pod的FQDN, 了解Kubernetes DNS的解析过程, 下面来看一下理论知识吧.

Service Discovery的缘起

随着微服务和云原生应用的不断发展, 目前在Web服务开发领域,同时运行服务的多个副本已经是非常常见的模式。每个服务副本都是由公开服务API的网络端点(即某些IP和端口)表示的服务的单独实例。传统上,虚拟机或物理机已用于托管此类端点,并且在最近一段时间内已转向容器。

同时运行多个服务实例可以提高其可用性,并有助于调整服务容量以满足流量需求。另一方面,这也会使整体设置复杂化.

在访问服务之前,客户端(这里的客户端可不仅仅是指来自浏览器或者移动设备等真实的客户调用;有时候一些服务也会访问其他服务,这种frontend服务有时候也可以称之为客户端),需要弄清楚它应该使用的实际的IP地址和端口。如果我们将实例的短暂性质考虑在内,情况将变得更加棘手。由于非零故障率、上下缩放或维护、新实例部署而导致现有实例消失,这些问题, 都是Service Discovery的范畴.

Service Discovery 一般分为两类:

第一种是服务器端的Service Discovery, 客户端完全不需要知道服务器端的服务实例和数量, 一般的做法是在服务器端的负载平衡,或者反转代理, 如Nginx 或者 HAProxy, 来实现, 如图所示:

另外一种是客服端来做的服务发现, 当客户端拥有服务的副本以及相应IP信息之后, 客户端就可以依据其服务选取策略自行决定访问的服务, 现实中的例子有Netflix Eureka 以及 Ribbon 项目.

除了上述两种方法之外, 服务发现可以通过多种方式实现。 有一种众所周知的基于DNS的服务发现(DNS-SD)方法,实际上甚至早于微服务的大规模传播。

维基百科对于DNS-SD是这样描述的:

DNS-SD允许客户端发现服务实例的命名列表,并使用标准DNS查询将这些服务解析为主机名。该规范与现有的单播DNS服务器和客户端软件兼容,但在零配置环境中与mDNS兼容。使用DNS SRV 和DNS TXT 记录描述了每个服务实例。

客户端通过查询该服务类型名称的DNS PTR 记录来发现给定服务类型的可用实例列表;服务器将返回零个或多个<Service>.<Domain>形式的名称,每个名称对应一个SRV / TXT记录对。 SRV记录解析为提供实例的域名,而TXT可以包含特定于服务的配置参数。然后,客户端可以解析域名的A / AAAA记录并连接到服务。

Kubernetes 的Service Discovery 与DNS

大多数Kubernetes群集会自动配置内部DNS服务,以便为服务发现提供轻量级机制。 内置的服务发现使应用程序更容易在Kubernetes集群上相互查找和通信,即使在节点之间创建,删除和移动Pod和服务时也是如此。

在Kubernetes版本1.11之前,Kubernetes DNS服务基于kube-dns。 1.11版引入了CoreDNS来解决kube-dns的一些安全性和稳定性问题。无论处理实际DNS记录的软件如何,两种实现都以类似的方式工作:

创建名为kube-dns的服务和一个或多个Pod。kube-dns服务监听来自Kubernetes API的服务service和端点endpoint事件,并根据需要更新其DNS记录。 创建,更新或删除Kubernetes服务及其关联的pod时会触发这些事件。kubelet将每个新Pod的/etc/resolv.conf名称服务器选项设置为kube-dns服务的集群IP,并使用适当的搜索选项以允许使用更短的主机名

Kubernetes会为Service和Pod创建DNS记录。 通过DNS, Pod就可以使用一致的DNS名称而不再需要使用IP地址来联系和访问Service。

总而言之,Kubernetes通过其内置的DNS附加组件(Kube-DNS或CoreDNS)实现了高效的服务发现。 Kubernetes DNS系统将域名和子域名分配给Pod,端口和服务,这使得Kubernetes集群中的其他组件可以发现它们。 基于DNS的服务发现功能非常强大,因为您无需将IP和端口等网络参数硬编码到应用程序中。 一旦一组Pod由服务管理,您就可以使用该服务的DNS轻松访问它们。

总结

终于写完本篇了, 这一篇写得非常艰难, 一方面是网络环境是很复杂的, minikube 作为Kubernetes的一个本机版本, 网络驱动器默认是none, 导致有些调用并不是成功的;另外DNS本身也是很简单也很复杂的一个系统, DNS 说起来很简单, 但是其背后的知识相当丰富, 再加上Kubernetes DNS的灵活性, 使得Kubernetes DNS 这一块知识点非常多.

服务发现有很多种模式, 本篇算是简单介绍了最简单的一种模式, 也就是Inter-App Communication 模式, 适用于Cluster 内部的通信.

本文用到的操作:

kubectl describe svc kubernetes

查看默认空间的kubernetes服务

kubectl get pods --namespace=kube-system -l k8s-app=kube-dns --show-labels

查看kubernetes dns系统的Pod

kubectl get services --namespace=kube-system -l k8s-app=kube-dns

查看kubernetes dns系统的Service

kubectl exec rcsise-29jjs -- cat /etc/resolv.conf

查看Pod配置的解析配置文件,能看到name server

kubectl exec -it jumpod -- busybox nslookup thesvc.default.svc.cluster.local

使用busybox 里面的nslookup 来解析Service的FQDN

kubectl exec -it jumpod -- busybox nslookup 172-17-0-11.thesvc.default.svc.cluster.local

使用busybox 里面的nslookup 来解析Pod FQDN

标签: #centos待机后如何唤醒