四层负载均衡 Service
在 kubernetes 中,Pod 是有生命周期的,如果 Pod 重启它的 IP 很有可能会发生变化。如果我们的服务都是将 Pod 的 IP 地址写死,Pod 挂掉或者重启,和刚才重启的 pod 相关联的其他服务将会找不 到它所关联的 Pod,为了解决这个问题,在 kubernetes 中定义了 service 资源对象,Service 定义了 一个服务访问的入口,客户端通过这个入口即可访问服务背后的应用集群实例,service 是一组 Pod 的 逻辑集合,这一组 Pod 能够被 Service 访问到,通常是通过 Label Selector 实现的。
1、pod ip 经常变化,service 是 pod 的代理,我们客户端访问,只需要访问 service,就会把请求 代理到 Pod
2、pod ip 在 k8s 集群之外无法访问,所以需要创建 service,这个 service 可以在 k8s 集群外访 问的。
Service 概述
service 是一个固定接入层,客户端可以通过访问 service 的 ip 和端口访问到 service 关联的后端pod,这个 service 工作依赖于在 kubernetes 集群之上部署的一个附件,就是 kubernetes 的 dns 服 务(不同 kubernetes 版本的 dns 默认使用的也是不一样的,1.11 之前的版本使用的是 kubeDNs,较 新的版本使用的是 coredns),service 的名称解析是依赖于 dns 附件的,因此在部署完 k8s 之后需要再部署 dns 附件,kubernetes 要想给客户端提供网络功能,需要依赖第三方的网络插件(flannel, calico 等)。每个 K8s 节点上都有一个组件叫做 kube-proxy,kube-proxy 这个组件将始终监视着 apiserver 中有关 service 资源的变动信息,需要跟 master 之上的 apiserver 交互,随时连接到 apiserver 上获取任何一个与 service 资源相关的资源变动状态,这种是通过 kubernetes 中固有的一种 请求方法 watch(监视)来实现的,一旦有 service 资源的内容发生变动(如创建,删除),kube- proxy 都会将它转化成当前节点之上的能够实现 service 资源调度,把我们请求调度到后端特定的 pod 资源之上的规则,这个规则可能是 iptables,也可能是 ipvs,取决于 service 的实现方式。
Service 工作原理
k8s 在创建 Service 时,会根据标签选择器 selector(lable selector)来查找 Pod,据此创建与
Service 同名的 endpoint 对象,当 Pod 地址发生变化时,endpoint 也会随之发生变化,service 接 收前端 client 请求的时候,就会通过 endpoint,找到转发到哪个 Pod 进行访问的地址。(至于转发到哪 个节点的 Pod,由负载均衡 kube-proxy 决定)
kubernetes 集群中有三类 IP 地址
1、Node Network(节点网络):物理节点或者虚拟节点的网络,如 ens33 接口上的网路地址
~]# ip addr
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP
link/ether 00:0c:29:87:60:d5 brd ff:ff:ff:ff:ff:ff
inet 192.168.1.63/24 brd 192.168.1.255 scope global noprefixroute ens33
2、Pod network(pod 网络),创建的 Pod 具有的 IP 地址
]# kubectl get pods -o wide
NAME READY STATUS IP NODE
frontend-h78gw 1/1 Running
10.244.187.76 god62
Node Network 和 Pod network 这两种网络地址是我们实实在在配置的,其中节点网络地址是配
置在节点接口之上,而 pod 网络地址是配置在 pod 资源之上的,因此这些地址都是配置在某些设备之上 的,这些设备可能是硬件,也可能是软件模拟的
3、Cluster Network(集群地址,也称为 service network),这个地址是虚拟的地址(virtual ip),没有配置在某个接口上,只是出现在 service 的规则当中。
kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 21d
nginx NodePort 10.105.245.139 <none> 3000:31003/TCP 7d23h
#查看定义 Service 资源需要的字段有哪些?
kubectl explain service
FIELDS:
apiVersion <string>. #service 资源使用的 api 组
kind <string> #创建的资源类型
metadata <Object> #定义元数据
spec <Object>
#查看 service 的 spec 字段如何定义?
clusterIP <string>. #动态分配的地址,也可以自己在创建的时候指定,创建之后就改不了了
ports <[]Object> #定义 service 端口,用来和后端 pod 建立联系
publishNotReadyAddresses <boolean>
selector #通过标签选择器选择关联的 pod 有哪些
sessionAffinity <string>
sessionAffinityConfig <Object>
#service 在实现负载均衡的时候还支持 sessionAffinity,sessionAffinity 什么意思?会话联系,默认是 none,随机调度的(基于 iptables 规则调度的);如果我们定义sessionAffinity 的 client ip,那就表示把来自同一客户端的 IP 请求调度到同一个 pod 上
type #定义 service 的类型
Service 的四种类型
kubectl explain service.spec.type
DESCRIPTION:
type determines how the Service is exposed. Defaults to
ClusterIP. Valid options are ExternalName, ClusterIP, NodePort, and LoadBalancer.
1、ExternalName:
适用于 k8s 集群内部容器访问外部资源,它没有 selector,也没有定义任何的端口和Endpoint。
以下 Service 定义的是将 prod 名称空间中的 my-service 服务映射到 my.database.example.com
kind: Service
apiVersion: v1
metadata:
name: my-service
namespace: prod spec:
type: ExternalName
externalName: my.database.example.com
当查询主机 my-service.prod.svc.cluster.local 时,群集 DNS 将返回值为my.database.example.com 的 CNAME 记录。
2、ClusterIP:
通过 k8s 集群内部 IP 暴露服务,选择该值,服务只能够在集群内部访问,这也是默认的 ServiceType。
3、NodePort:
通过每个 Node 节点上的 IP 和静态端口暴露 k8s 集群内部的服务。通过请求 <NodeIP>:<NodePort>可以把请求代理到内部的 pod。Client----->NodeIP:NodePort---- ->Service Ip:ServicePort----->PodIP:ContainerPort。
4、LoadBalancer:
使用云提供商的负载局衡器,可以向外部暴露服务。外部的负载均衡器可以路由到 NodePort 服务 和 ClusterIP 服务。
Service 的端口
kubectl explain service.spec.ports
name #定义端口的名字
nodePort <integer>
|#宿主机上映射的端口,比如一个 Web 应用需要被 k8s 集群之外的其他用户访问,那么需要配置 type=NodePort,若配置 nodePort=30001,那么其他机器就可以通过浏览器访问 scheme://k8s 集 群中的任何一个节点 ip:30001 即可访问到该服务,例如 http://192.168.1.63:30001。如果在 k8s 中 部署 MySQL 数据库,MySQL 可能不需要被外界访问,只需被内部服务访问,那么就不需要设置 NodePort
port -required- #service 的端口,这个是 k8s 集群内部服务可访问的端口
protocol <string>
targetPort. <string>
# targetPort 是 pod 上的端口,从 port 和 nodePort 上来的流量,经过 kube-proxy 流入到后端 pod 的 targetPort 上,最后进入容器。与制作容器时暴露的端口一致(使用 DockerFile 中的 EXPOSE),例如官方的 nginx 暴露 80 端口。
创建 Service:type 类型是 ClusterIP
docker load -I nginx.tar.gz
cat pod_test.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
selector:
matchLabels:
run: my-nginx
replicas: 2
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- image: nginx
name: my-nginx
ports:
- containerPort: 80 #pod容器中暴露的端口
kubectl apply -f pod_test.yaml
kubectl get pods -l run=my-nginx -o wide
curl ip 看主页
Welcome to nginx!
kubectl exec -it my-nginx-5b56ccd65f-cwqbg -- /bin/bash
需要注意的是,pod 虽然定义了容器端口,但是不会使用调度到该节点上的 80 端口,也不会使用任 何特定的 NAT 规则去路由流量到 Pod 上。 这意味着可以在同一个节点上运行多个 Pod,使用相同的容 器端口,并且可以从集群中任何其他的 Pod 或节点上使用 IP 的方式访问到它们。
误删除其中一个 Pod:
kubectl delete pods my-nginx-5b56ccd65f-cwqbg
kubectl get pods -l run=my-nginx -o wide
kubectl get pods -l run=my-nginx -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
my-nginx-5b56ccd65f-d7j7v 1/1 Running 0 21s 10.244.187.103 god62 <none> <none>
my-nginx-5b56ccd65f-h2kjl 1/1 Running 0 14m 10.244.209.135 god64 <none> <none>
通过上面可以看到重新生成了一个 pod :my-nginx-5b56ccd65f-7xzr4,ip 是 10.244.187.102,在 k8s 中创建 pod,如果 pod 被删除了,重新生成的 pod ip 地址会发生变化,所 以需要在 pod 前端加一个固定接入层。接下来创建 service:
创建 Service
cat service_test.yaml
apiVersion: v1
kind: Service
metadata:
name: my-nginx
labels:
run: my-nginx
spec:
type: ClusterIP
ports:
- port: 80 #service 的端口,暴露给 k8s 集群内部服务访问
protocol: TCP #通过ip和端口代理
targetPort: 80 #pod 容器中定义的端口
selector:
run: my-nginx #选择拥有 run=my-nginx 标签的 pod
上述 yaml 文件将创建一个 Service,具有标签 run=my-nginx 的 Pod,目标 TCP 端口 80,并 且在一个抽象的 Service 端口(targetPort:容器接收流量的端口;port:抽象的 Service 端口,可 以使任何其它 Pod 访问该 Service 的端口)上暴露。
kubectl apply -f service_test.yaml
kubectl get svc -l run=my-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-nginx ClusterIP 10.102.215.115 <none> 80/TCP 3m34s
#在 k8s 控制节点访问 service 的 ip:端口就可以把请求代理到后端 pod
curl 10.102.215.115:80
Welcome to nginx!
#通过上面可以看到请求 service IP:port 跟直接访问 pod ip:port 看到的结果一样,这就说明 service 可以把请求代理到它所关联的后端 pod
注意:上面的 10.102.215.115:80 地址只能是在 k8s 集群内部可以访问,在外部无法访问,比方说
我们想要通过浏览器访问,那么是访问不通的,如果想要在 k8s 集群之外访问,是需要把 service type 类型改成 nodePort 的
#通过上面可以看到请求 service IP:port 跟直接访问 pod ip:port 看到的结果一样,这就说明 service 可以把请求代理到它所关联的后端 pod
注意:上面的 10.102.215.115:80 地址只能是在 k8s 集群内部可以访问,在外部无法访问,比方说
我们想要通过浏览器访问,那么是访问不通的,如果想要在 k8s 集群之外访问,是需要把 service type 类型改成 nodePort 的
#查看 service 详细信息
kubectl describe svc my-nginx
kubectl get ep my-nginx
NAME ENDPOINTS AGE
my-nginx 10.244.187.106:80,10.244.209.138:80 6m8s
service 可以对外提供统一固定的 ip 地址,并将请求重定向至集群中的 pod。其中“将请求重定向 至集群中的 pod”就是通过 endpoint 与 selector 协同工作实现。selector 是用于选择 pod,由 selector 选择出来的 pod 的 ip 地址和端口号,将会被记录在 endpoint 中。endpoint 便记录了所有 pod 的 ip 地址和端口号。当一个请求访问到 service 的 ip 地址时,就会从 endpoint 中选择出一个 ip地址和端口号,然后将请求重定向至 pod 中。具体把请求代理到哪个 pod,需要的就是 kube-proxy 的 轮询实现的。service 不会直接到 pod,service 是直接到 endpoint 资源,就是地址加端口,再由 endpoint 再关联到 pod。
service 只要创建完成,我们就可以直接解析它的服务名,每一个服务创建完成后都会在集群 dns 中 动态添加一个资源记录,添加完成后我们就可以解析了,资源记录格式是:
SVC_NAME.NS_NAME.DOMAIN.LTD. 服务名.命名空间.域名后缀
集群默认的域名后缀是
svc.cluster.local.
就像我们上面创建的 my-nginx 这个服务,它的完整名称解析就是 my-nginx.default.svc.cluster.local
# kubectl exec -it my-nginx-5b56ccd65f-7xzr4 -- /bin/bash
root@my-nginx-5b56ccd65f-7xzr4:/# curl my-nginx.default.svc.cluster.local
Welcome to nginx!
创建 Service:type 类型是 NodePort
cat pod_nodeport.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx-nodeport
spec:
selector:
matchLabels:
run: my-nginx-nodeport
replicas: 2
template:
metadata:
labels:
run: my-nginx-nodeport
spec:
containers:
- name: my-nginx-nodeport-contauber
image: nginx
ports:
- containerPort: 80
kubectl apply -f pod_nodeport.yaml
#查看 pod 是否创建成功
kubectl get pods -l run=my-nginx-nodeport
NAME READY STATUS RESTARTS AGE
my-nginx-nodeport-6cb5fff859-9c4lm 1/1 Running 0 54s
my-nginx-nodeport-6cb5fff859-lqdnc 1/1 Running 0 54s
创建 service,代理 pod
cat service_nodeport.yaml
apiVersion: v1
kind: Service
metadata:
name: my-nginx-nodeport
labels:
run: my-nginx-nodeport
spec:
type: NodePort
ports:
- port: 80
protocol: TCP
targetPort: 80
selector: 30380
run: my-nginx-nodeport
kubectl apply -f service_nodeport.yaml
#查看刚才创建的 service
kubectl get svc -l run=my-nginx-nodeport
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-nginx-nodeport NodePort 10.100.132.187 <none> 80:30380/TCP 2m36s
curl 10.100.132.187
<!DOCTYPE html>
<h1>Welcome to nginx!</h1> </html>
10.100.132.187是 k8s 集群内部的 service ip 地址,只能在 k8s 集群内部访问,在集群外无法访问。
#在集群外访问 service
curl 192.168.172.163:30380
过程就是192.168.172.163:30380。---> 10.100.132.187:80(kubectl get svc -l run=my-nginx-nodeport)----> pod ip:container port 随机生成的如下
创建 Service:type 类型是 ExternalName
应用场景:跨名称空间访问
需求:default 名称空间下的 client 服务想要访问 nginx-ns 名称空间下的 nginx-svc 服务
docker load -I busybox.tar.gz. 工作节点
cat client.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: client
spec:
replicas: 1
selector:
matchLabels:
app: busybox
template:
metadata:
labels:
app: busybox
spec:
containers:
- name: busybox
image: busybox
command: ["/bin/sh","-c","sleep 36000"]
kubectl apply -f client.yaml
#. cat client_svc.yaml
apiVersion: v1
kind: Service
metadata:
name: client-svc
spec:
type: ExternalName
externalName: nginx-svc.nginx-ns.svc.cluster.local
ports:
- name: http
port: 80
targetPort: 80
#该文件中指定了到 nginx-svc 的软链,让使用者感觉就好像调用自己命名空间的服务一样。 #查看 pod 是否正常运行
kubectl get pods | grep client-76b6556d97-xqkzk
client-76b6556d97-xqkzk 1/1 Running 0 64m
kubectl apply -f client_svc.yaml
kubectl apply -f client_svc.yaml
#创建名称空间
kubectl create ns nginx-ns
cat server_nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
namespace: nginx-ns
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
kubectl apply -f server_nginx.yaml
#查看 pod 是否创建成功
kubectl get pods -n nginx-ns
NAME READY STATUS RESTARTS AGE
nginx-7cf7d6dbc8-glps2 1/1 Running 0 79m
cat nginx_svc.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
namespace: nginx-ns
spec:
selector:
app: nginx
ports:
- name: http
protocol: TCP
port: 80
targetPort: 80
kubectl apply -f nginx_svc.yaml
#登录到 client pod
kubectl exec -it client-76b6556d97-xqkzk -- /bin/sh
wget -q -O - client-svc.default.svc.cluster.local
wget -q -O - nginx-svc.nginx-ns.svc.cluster.local
k8s 映射外部服务分享
场景 1:k8s 集群引用外部的 mysql 数据库
在 god62 上安装 mysql 数据库:
yum install mariadb-server.x86_64 -y
systemctl start mariadb
cat mysql_service.yaml
apiVersion: v1
kind: Service
metadata:
name: mysql
pec:kubectl apply -f mysql_service.yaml
type: ClusterIP
ports:
- port: 3306
kubectl apply -f mysql_service.yaml
kubectl get svc | grep mysql
mysql ClusterIP 10.109.176.8 <none> 3306/TCP 49s
kubectl describe svc mysql
Name: mysql
Namespace: default
Labels: <none>
Annotations: <none>
Selector: <none>
Type: ClusterIP
IP Families: <none>
IP: 10.109.176.8
IPs: 10.109.176.8
Port: <unset> 3306/TCP
TargetPort: 3306/TCP
Endpoints: <none> #还没有endpoint
Session Affinity: None
Events: <none>
cat mysql_endpoint.yaml
apiVersion: v1
kind: Endpoints
metadata:
name: mysql
subsets:
- addresses:
- ip: 192.168.172.162
ports:
- protocol: TCP
port: 3306
kubectl describe svc mysql
Name: mysql
Namespace: default
Labels: <none>
Annotations: <none>
Selector: <none>
Type: ClusterIP
IP Families: <none>
IP: 10.109.176.8
IPs: 10.109.176.8
Port: <unset> 3306/TCP
TargetPort: 3306/TCP
Endpoints: 192.168.172.162:3306 #这个 定义的外部数据库,name与之对应
Session Affinity: None
Events: <none>
上面配置就是将外部 IP 地址和服务引入到 k8s 集群内部,由 service 作为一个代理来达到能够访问
外部服务的目的。
Service 代理:kube-proxy 组件详解
Kubernetes service 只是把应用对外提供服务的方式做了抽象,真正的应用跑在 Pod 中的container 里,我们的请求转到 kubernetes nodes 对应的 nodePort 上,那么 nodePort 上的请求是如何进一步转到提供后台服务的 Pod 的呢? 就是通过 kube-proxy 实现的:
kube-proxy 部署在 k8s 的每一个 Node 节点上,是 Kubernetes 的核心组件,我们创建一个 service 的时候,kube-proxy 会在 iptables 中追加一些规则,为我们实现路由与负载均衡的功能。在 k8s1.8 之前,kube-proxy 默认使用的是 iptables 模式,通过各个 node 节点上的 iptables 规则来实 现 service 的负载均衡,但是随着 service 数量的增大,iptables 模式由于线性查找匹配、全量更新等 特点,其性能会显著下降。从 k8s 的 1.8 版本开始,kube-proxy 引入了 IPVS 模式,IPVS 模式与 iptables 同样基于 Netfilter,但是采用的 hash 表,因此当 service 数量达到一定规模时,hash 查表 的速度优势就会显现出来,从而提高 service 的服务性能。
service 是一组 pod 的服务抽象,相当于一组 pod 的 LB,负责将请求分发给对应的 pod。service 会为这个 LB 提供一个 IP,一般称为 cluster IP。kube-proxy 的作用主要是负责 service 的实现,具体 来说,就是实现了内部从 pod 到 service 和外部的从 node port 向 service 的访问。
1、kube-proxy 其实就是管理 service 的访问入口,包括集群内 Pod 到 Service 的访问和集群外访问 service。
2、kube-proxy 管理 sevice 的 Endpoints,该 service 对外暴露一个 Virtual IP,也可以称为是 Cluster IP, 集群内通过访问这个 Cluster IP:Port 就能访问到集群内对应的 serivce 下的 Pod。
kube-proxy 三种工作模式
1、Userspace 方式:
Client Pod 要访问 Server Pod 时,它先将请求发给内核空间中的 service iptables 规则,由它再将请求转给监听在指定套接字上的 kube-proxy 的端口,kube-proxy 处理完请求,并分发请求到指定 Server Pod 后,再将请求转发给内核空间中的 service ip,由 service iptables 将请求转给各个节点中 的 Server Pod。
这个模式有很大的问题,客户端请求先进入内核空间的,又进去用户空间访问 kube-proxy,由 kube-proxy 封装完成后再进去内核空间的 iptables,再根据 iptables 的规则分发给各节点的用户空间 的 pod。由于其需要来回在用户空间和内核空间交互通信,因此效率很差。在 Kubernetes 1.1 版本之 前,userspace 是默认的代理模型。
2、iptables 方式:
客户端 IP 请求时,直接请求本地内核 service ip,根据 iptables 的规则直接将请求转发到到各 pod 上,因为使用 iptable NAT 来完成转发,也存在不可忽视的性能损耗。另外,如果集群中存上万的 Service/Endpoint,那么 Node 上的 iptables rules 将会非常庞大,性能还会再打折
iptables 代理模式由 Kubernetes 1.1 版本引入,自 1.2 版本开始成为默认类型。
3、ipvs 方式:
Kubernetes 自 1.9-alpha 版本引入了 ipvs 代理模式,自 1.11 版本开始成为默认设置。客户端
请求时到达内核空间时,根据 ipvs 的规则直接分发到各 pod 上。kube-proxy 会监视 KubernetesService 对象和 Endpoints,调用 netlink 接口以相应地创建 ipvs 规则并定期与 KubernetesService 对象和 Endpoints 对象同步 ipvs 规则,以确保 ipvs 状态与期望一致。访问服务 时,流量将被重定向到其中一个后端 Pod。与 iptables 类似,ipvs 基于 netfilter 的 hook 功能,但 使用哈希表作为底层数据结构并在内核空间中工作。这意味着 ipvs 可以更快地重定向流量,并且在同步 代理规则时具有更好的性能。此外,ipvs 为负载均衡算法提供了更多选项,例如:
rr:轮询调度
lc:最小连接数
dh:目标哈希
sh:源哈希
sed:最短期望延迟
nq:不排队调度
如果某个服务后端 pod 发生变化,标签选择器适应的 pod 又多一个,适应的信息会立即反映到 apiserver 上,而 kube-proxy 一定可以 watch 到 etc 中的信息变化,而将它立即转为 ipvs 或者 iptables 中的规则,这一切都是动态和实时的,删除一个 pod 也是同样的原理。如图:
注:
以上不论哪种,kube-proxy 都通过 watch 的方式监控着 apiserver 写入 etcd 中关于 Pod 的最 新状态信息,它一旦检查到一个 Pod 资源被删除了或新建了,它将立即将这些变化,反应再 iptables 或 ipvs 规则中,以便 iptables 和 ipvs 在调度 Clinet Pod 请求到 Server Pod 时,不会出现 Server Pod 不存在的情况。自 k8s1.1 以后,service 默认使用 ipvs 规则,若 ipvs 没有被激活,则降级使用 iptables 规则. 但在 1.1 以前,service 使用的模式默认为 userspace。Service 是由 kube-proxy 组件,加上 iptables 来共同实现的。kube-proxy 通过 iptables 处理 Service 的过程,需要在宿主机上设置相 当多的 iptables 规则,如果宿主机有大量的 Pod,不断刷新 iptables 规则,会消耗大量的 CPU 资源。
kube-proxy 生成的 iptables 规则分析
在 k8s 创建的 service,虽然有 ip 地址,但是 service 的 ip 是虚拟的,不存在物理机上的,是在
iptables 或者 ipvs 规则里的。
]# kubectl get svc | grep my-nginx
my-nginx ClusterIP 10.99.198.177 80/TCP
10.99.198.177 是 kubernetes 的 service ip,这个 ip 我们在物理机上看不到,它是在 iptables 规则里。
iptables -t nat -L |grep 10.99.198.177
KUBE-MARK-MASQ tcp -- !10.244.0.0/16 10.99.198.177 /*
default/my-nginx cluster IP */ tcp dpt:http
KUBE-SVC-L65ENXXZWWSAPRCR tcp -- anywhere 10.99.198.177 /*
default/my-nginx cluster IP */ tcp dpt:http
#通过上面可以看到之前创建的 service,会通过 kube-proxy 在 iptables 中生成一个规则,来实现流量路由,有一系列目标为 KUBE-SVC-xxx 链的规则,每条规则都会匹配某个目标 ip 与端口。也 就是说访问某个 ip:port 的请求会由 KUBE-SVC-xxx 链来处理。这个目标 IP 其实就是 service ip。
Service 服务发现:coredns 组件详解
DNS 全称是 Domain Name System:域名系统,是整个互联网的电话簿,它能够将可被人理解的 域名翻译成可被机器理解 IP 地址,使得互联网的使用者不再需要直接接触很难阅读和理解的 IP 地址。域 名系统在现在的互联网中非常重要,因为服务器的 IP 地址可能会经常变动,如果没有了 DNS,那么可 能 IP 地址一旦发生了更改,当前服务器的客户端就没有办法连接到目标的服务器了,如果我们为 IP 地 址提供一个『别名』并在其发生变动时修改别名和 IP 地址的关系,那么我们就可以保证集群对外提供的 服务能够相对稳定地被其他客户端访。DNS 其实就是一个分布式的树状命名系统,它就像一个去中心化 的分布式数据库,存储着从域名到 IP 地址的映射。
CoreDNS 其实就是一个 DNS 服务,而 DNS 作为一种常见的服务发现手段,所以很多开源项目 以及工程师都会使用 CoreDNS 为集群提供服务发现的功能,Kubernetes 就在集群中使用 CoreDNS 解决服务发现的问题。 作为一个加入 CNCF(Cloud Native Computing Foundation)的服务, CoreDNS 的实现非常简单。
验证 coredns
docker load -I dig.tar.gz 工作节点解压
cat dig.yaml
apiVersion: v1
kind: Pod
metadata:
name: dig
namespace: default
spec:
containers:
- name: dig
image: god/xianchao/dig:latest
command:
- sleep
- "3600"
imagePullPolicy: IfNotPresent
restartPolicy: Always
kubectl apply -f dig.yaml
#查看默认名称空间的 kubernetes 服务
# kubectl get svc | grep kubernetes
kubectl get svc | grep kubernetes
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 23
#解析 dns,如有以下返回说明 dns 安装成功
kubectl exec -it dig -- nslookup kubernetes
Server: 10.96.0.10
Address: 10.96.0.10#53
Name: kubernetes.default.svc.cluster.local Address: 10.96.0.1
kubernetes.default.svc.cluster.local
服务名.名称空间.默认后缀
在 k8s 中创建 service 之后,service 默认的 FQDN 是<service name>.<namespace>.svc.cluster.local,那么 k8s 集群内部的服务就可以通过 FQDN 访问