服务发现在微服务架构里,服务之间经常进行通信,服务发现就是解决不同服务之间通信的问题。比如一个nginx的pod,要访问一个mysql服务,就需要知道mysql服务的ip和port,获取ip和port的过程就是服务发现。
服务发现方式
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中来实现
使用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服务中设置的域名
前面也说了,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 标志来指定这些策略
注意:Default不是默认的 DNS 策略。如果没有显式地指定dnsPolicy,将会使用 ClusterFirst
查询首先被发送到 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,这里就简单说一下域名格式是如何建立
举个栗子,例如我们有个mysql服务,service名称为mysql-test1,命名空间为rrfq,那么我们调用的时候可以直接写
mysql-test1.rrfq.svc.cluster.local 也可以简写为mysql-test1.rrfq
#这里使用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
除了上面的格式,也可以缩写
/ # wget -q -O- nginx-service.default:18080 / # wget -q -O- nginx-service:18080 #因为在一个命名空间下,所以不加namespace也是可以的
相关文章:
- Kubernetes 1.14 二进制集群安装
- Kuerbernetes 1.11 集群二进制安装
- Kubenetes 1.13.5 集群二进制安装
- CentOS 7 ETCD集群配置大全