Kubernetes 服务发现

释放双眼,带上耳机,听听看~!
服务发现在微服务架构里,服务之间经常进行通信,服务发现就是解决不同服务之间通信的问题。比如一个nginx的pod,要访问一个mysql服务,就需要知道mysql服务的ip和port,获取ip和port的过程就是服务发现。
🤖 由 ChatGPT 生成的文章摘要

服务发现方式

1.环境变量

Pod创建的时候,服务的ip和port会以环境变量的形式注入到pod里,比如pod创建时有一个redis-master服务,服务ip地址是10.4.82.11,port是6379,则会把下面一系列环境变量注入到pod里,通过这些环境变量访问redis-master服务。

REDIS_MASTER_SERVICE_HOST=10.4.82.11
REDIS_MASTER_SERVICE_PORT=6379
REDIS_MASTER_PORT=tcp://10.4.82.11:6379

2. DNS服务

K8s集群会内置一个dns服务器,service创建成功后,会在dns服务器里导入一些记录,想要访问某个服务,通过dns服务器解析出对应的ip和port,从而实现服务访问

Kubenetes 1.13.5 集群二进制安装

常见的DNS解析服务有CoreDNS和Kube-DNS,我们先简单介绍一下这两个DNS区别

kube-dns是将多个容器kubedns、dnsmasq、sidecar放入同一个POD中来实现

  • kubedns 基于 SkyDNS 库,通过 apiserver 监听 Service 和 Endpoints 的变更事件同时也同步到本地 Cache,实现了一个实时的 Kubernetes 集群内 Service 和 Pod 的 DNS服务发现
  • dnsmasq dsnmasq 容器则实现了 DNS 的缓存功能(在内存中预留一块默认大小为 1G 的地方,保存当前最常用的 DNS 查询记录,如果缓存中没有要查找的记录,它会到 kubedns 中查询,并把结果缓存起来),通过监听 ConfigMap 来动态生成配置
  • sider sidecar 容器实现了可配置的 DNS 探测,并采集对应的监控指标暴露出来供 prometheus 使用

Kubernetes 服务发现

使用kube-dns 使用过程中可能会导致如下问题

安全问题。在过去,dnsmasq的安全缺陷已经导致过K8S需要发布安全补丁来修复了
由于dnsmasq实现的stub domian,但External Service是由kubedns来实现的,所以你就不能在External Service中使用stub domain,这是一种巨大的限制

CoreDNS 在Kubernetes 1.11中,CoreDNS已经实现了基于DNS的服务发现的GA,可作为kube-dns插件的替代品。这意味着CoreDNS将作为各种安装工具未来发布版本中的一个选项来提供。事实上,kubeadm团队选择将其作为Kubernetes 1.11的默认选项

重要的一点,CoreDNS是只有一个容器的存在

在kube-dns中,你可以修改ConfigMap以改变服务发现的行为。这允许添加诸如提供服务存根域、修改上游名称服务器以及启用联合之类的功能。

在CoreDNS中,你同样可以修改CoreDNS Corefile的ConfigMap以更改服务发现的工作方式。Corefile配置提供了比kube-dns更多的选项,因为它是CoreDNS用于配置其所有功能(甚至是那些与Kubernetes无关的功能)的主要配置文件。

使用kubeadm从kube-dns升级到CoreDNS时,现有的ConfigMap将用于为你生成自定义Corefile,包括存根域、联合和上游名称服务器的所有配置。


环境演示

首先我们使用环境变量进行演示

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: nginx-deploy
  labels:
    k8s-app: nginx-demo
spec:
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80

---
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
  labels:
    name: nginx-service
spec:
  ports:
  - port: 18080
    targetPort: 80
  selector:
    app: nginx


[root@k8s-01 nginx-test]# kubectl apply -f nginx-demo.yaml
deployment.apps/nginx-deploy created
service/nginx-service created

这里我们检查一下创建pod的状态,这里我们可以看到已经创建了一个端口为18080的svc

[root@k8s-01 nginx-test]# kubectl get pod
NAME                                      READY   STATUS    RESTARTS   AGE
nginx-deploy-56db997f77-74qfj             1/1     Running   0          52s
nginx-deploy-56db997f77-khv26             1/1     Running   0          52s

[root@k8s-01 nginx-test]# kubectl get svc
NAME            TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)       AGE
kubernetes      ClusterIP   10.254.0.1               443/TCP       3d4h
nginx-service   ClusterIP   10.254.159.126           18080/TCP     62s

接下来,我们创建一个普通的Pod,查看Pod环境变量是否有我们刚刚创建svc一些信息

apiVersion: v1
kind: Pod
metadata:
  name: test-pod
spec:
  containers:
  - name: test-service
    image: nginx
    command: ["/bin/sh", "-c", "env"]


[root@k8s-01 nginx-test]# kubectl apply -f test-demo.yaml
pod/test-pod created

上面在Pod文件中,我们只让Pod执行了一条环境变量,接下来我们就可以直接看pod的日志查看状态

[root@k8s-01 nginx-test]# kubectl get pod
NAME                                      READY   STATUS             RESTARTS   AGE
...
test-pod                                  0/1     CrashLoopBackOff   3          99s
...

[root@k8s-01 nginx-test]# kubectl logs test-pod|grep NGINX
...
NGINX_SERVICE_PORT=tcp://10.254.159.126:18080
NGINX_SERVICE_SERVICE_PORT=18080
NGINX_SERVICE_PORT_18080_TCP_ADDR=10.254.159.126
NGINX_DS_SERVICE_HOST=10.254.86.215
NGINX_SERVICE_PORT_18080_TCP_PORT=18080
NGINX_SERVICE_PORT_18080_TCP_PROTO=tcp

#这里我们可以看到在Pod默认的环境就已经有刚刚创建svc的相关信息,这时候如果我们test-pod的应用想访问之前创建nginx的svc就可以直接访问它的环境变量就可以访问到。但是前提必须是nginx-svc服务必须提前创建,也可以使用initContainer等待nginx-svc创建完成后,才进行通信,或者是通过DNS来解决

Kube-DNS、CoreDNS

这里我们使用的是CoreDNS,比Kube-DNS更强大。关于安装这里不再细说,需要安装请参考下面文章,包含kube-dns和coredns选择一个就行

Kubenetes 1.13.5 集群二进制安装

DNS Pod具有静态IP并作为Kubernetes服务暴露出来。该静态 IP 被分配后,kubelet 会将使用--cluster-dns = 参数配置的 DNS 传递给每个容器。DNS 名称也需要域名,本地域可以使用参数--cluster-domain = 在 kubelet中配置。如果使用的是coredns则是下面的配置clusterDomain

cluster_dns为DNS服务的ClusterIP地址
cluster_domain为DNS服务中设置的域名

Kubernetes 服务发现

前面也说了,DNS实际上就是通过configmap进行配置监听的,所以我们可以看到在kube-system命名空间下有一个名称为coredns的configmap

[root@k8s-01 kubelet]# kubectl get cm -n kube-system coredns -o yaml
apiVersion: v1
data:
  Corefile: |
    .:53 {
        errors
        health
        kubernetes cluster.local in-addr.arpa ip6.arpa {
            pods insecure
            upstream
            fallthrough in-addr.arpa ip6.arpa
        }
        prometheus :9153
        forward . /etc/resolv.conf
        cache 30
        loop
        reload
        loadbalance
    }
kind: ConfigMap
metadata:
  creationTimestamp: "2020-01-30T18:10:18Z"
  labels:
    addonmanager.kubernetes.io/mode: EnsureExists
  name: coredns
  namespace: kube-system
  resourceVersion: "2592"
  selfLink: /api/v1/namespaces/kube-system/configmaps/coredns
  uid: c58bed8b-438b-11ea-af72-000c29eeccce

如果我们想自定义dns,可以参考下面的例子

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

举个栗子

当访问kubernetes.default.svc.cluster.local DNS服务器为CoreDNS && Kube-DNS

当访问为*.abc.local DNS解析服务为1.2.3.4

当访问i4t.com DNS解析地址为Google的8.8.8.8和8.8.4.4其中之一

除了修改coredns或者kube-dns的configmap以外,还可以通过修改deployment文件定义dns解析服务器

当前Kubernetes支持三种 Pod 特定的 DNS 策略:“ClusterFirstWithHostNet”、“Default” 和 “ClusterFirst”。 可以通过 dnsPolicy 标志来指定这些策略

Kubernetes 服务发现

注意:Default不是默认的 DNS 策略。如果没有显式地指定dnsPolicy,将会使用 ClusterFirst

  • 如果 dnsPolicy 被设置为“ Default” 让kubelet来决定使用何种DNS策略,而kubelet默认使用宿主机的 /etc/resolv.conf(使用宿主机的DNS策略)但kubelet可以配置使用什么文件来进行DNS 策略,使用kubelet的参数:–resolv-conf=/etc/resolv.conf来决定DNS解析文件地址
  • 如果 dnsPolicy 被设置为“ClusterFirstWithHostNet” POD是用HOST模式启动的(HOST模式),用HOST模式表示POD中的所有容器,都使用宿主机的/etc/resolv.conf进行DNS查询,但如果使用了HOST模式,还继续使用Kubernetes的DNS服务,那就将dnsPolicy设置为 ClusterFirstWithHostNet
  • 如果 dnsPolicy 被设置为 “ClusterFirst”,这就要依赖于是否配置了存根域和上游 DNS 服务器
  • 1.未进行自定义配置:没有匹配上配置的集群域名后缀的任何请求,例如 “www.abcdocker.com”,将会被转发到继承自节点的上游域名服务器。(Pod所在的Node节点为主)
  • 2.进行自定义配置:如果配置了存根域和上游 DNS 服务器(类似于前面示例 配置的内容),DNS 查询将基于下面的流程对请求进行路由:
查询首先被发送到 kube-dns 中的 DNS 缓存层
  从缓存层,检查请求的后缀,并根据下面的情况转发到对应的 DNS 上:
    具有集群后缀的名字(例如 “.cluster.local”):请求被发送到 kubedns。
    具有存根域后缀的名字(例如 “.abc.local”):请求被发送到配置的自定义 DNS 解析器(例如:监听在 1.2.3.4)
    未能匹配上后缀的名字(例如 “i4t.com”):请求被转发到上游 DNS(例如:Google 公共 DNS 服务器,8.8.8.8 和 8.8.4.4)。 

域名格式

前面说了可以通过域名的格式访问svc,这里就简单说一下域名格式是如何建立

  • 普通Service
  • 普通service会生成一个service_name.namespace.svc.cluster.local会将这个域名解析到对应的ClusterIP上,如果在Pod之间调用可以简写为service_name.namespace,或者在一个命名空间下,可以只写service_name

举个栗子,例如我们有个mysql服务,service名称为mysql-test1,命名空间为rrfq,那么我们调用的时候可以直接写

mysql-test1.rrfq.svc.cluster.local
也可以简写为mysql-test1.rrfq
  • Headless Service
  • 简单的来说就是将clusterIP设置为None,这样就可以解析到某一个Pod上,可以通过pod名称进行访问podname.servicename.namespace.svc.cluster.local这种一般常见于statefulset
#这里使用mysql举例,当我们创建了3个statefulset的Pod,svc名称为mysql pod名称为mysql-web。则会产生3个名称为mysql-web01、mysql-web02、mysql-web03

这时候比如我们想通过域名的方式访问到mysql-web02,就可以使用下面的域名进行访问

mysql-web02.mysql.default.svc.cluster.local

#如果不设置命名空间,默认就是default

测试DNS

在前面的步骤我们已经创建了nginx,接下来我们使用busybox镜像测试一下是否可以解析正常

kubectl run --rm -i --tty test-dns --image=busybox /bin/sh


#从下面的svc我们可以看到,我们有一个名称为nginx-service的nginx,端口为18080,所属命名空间为default,那么接下来就看可以进入busybox进行访问测试
[root@k8s-01 nginx-test]# kubectl get svc
NAME            TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)       AGE
kubernetes      ClusterIP   10.254.0.1               443/TCP       3d4h
nginx-service   ClusterIP   10.254.159.126           18080/TCP     62s

在下面截图我们可以看到使用nginx-service.default.svc.cluster.local:18080是可以直接访问到我们的nginx

Kubernetes 服务发现

除了上面的格式,也可以缩写

/ # wget -q -O- nginx-service.default:18080
/ # wget -q -O- nginx-service:18080

#因为在一个命名空间下,所以不加namespace也是可以的

给TA打赏
共{{data.count}}人
人已打赏
Kubernetes

持久化存储 StorageClass

2020-2-1 20:28:44

Kubernetes

Kubernetes Traefik Ingress

2020-2-5 20:32:04

2 条回复 A文章作者 M管理员
  1. 学习啦

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索