背景
深刻了解Service服务的内在逻辑 node
Service
k8s中service是一个面向微服务架构的设计,它从k8s自己解决了容器集群的负载均衡,并开放式地支持了用户所须要的各类负载均衡方案和使用场景。mysql
一般,一个service被建立后会在集群中建立相应的endpoints,随后,controller-manager中的endpointsController会去检查并向该endpoints填入关于这个service,符合下述全部条件的后端端点(即pod):sql
相同的namespace;
pod的labels能知足service.Spec.selector(除非service.Spec.selector为空,这种状况下不会自动建立endpoints);
若是service开放了port,且是以字符串名字的形式(targetPort=[字符串]),则相应的pod的某个container中必须有配置同名的port;
当endpoints被更新后,kube-proxy会感知,并根据更新后的endpoints,在宿主机上作转发规则的配置,kube-proxy目前支持iptables、ipvs两种负载均衡方式,默认是iptables。数据库
DNS
绝大部分使用k8s的人都会使用kubedns作服务发现和service domain的解析。kubedns会创建长链接即时检查service的变化,一旦发现有service被建立,会根据service的类型,在数据库中构建service domain 到指定的CNAME或IP(cluster-IP)的映射,做为对该service domain 的dns解析结果。后端
service 的类型
ClusterIP
最广泛的service类型,也是默认类型。ClusterIP类型的service建立时,k8s会经过etcd从可分配的IP池中分配一个IP,该IP全局惟一,且不可修改。全部访问该IP的请求,都会被iptables转发到后端的endpoints中。架构
该类型下,service的cluster-ip会做为kube-dns的解析结果,返回给客户端。基本上这是私有云中服务内部经常使用的方案,可是也只能集群内服务互相访问。负载均衡
NodePort
经过node的IP进行地址转换实现服务的可访问性,外部访问集群中任意一个node:port便可以访问服务。
举个例子:一个单副本的deployment,其pod运行在node2上,经过nodePort方式开放服务,外部client访问node1_ip:port便可访问到容器服务。这个过程当中原理是:less
client访问node1_ip:port
node1对数据包作SNAT,未来源地址改为node1_ip
node1对数据包作DNAT,将目的地址改为node2_ip
node2收到数据包,根据port查找自身的端口,这个端口在service建立时就会与自身运行的port作映射,因此这里数据包会被转发到真实的容器中
数据响应由容器发给node1,node1再返回给client
这种方案下,node上会产生端口的占用,因此要确保端口可用性。
另外,经过指定service.spec.externalTrafficPolicy=Local能够设置node上kube-proxy不转发到其余node,参照上面的例子,因为node1没有响应的pod在运行,因此node1上会直接drop数据包。dom
使用这种方案的缘由,不外乎是:外部没法访问容器服务。ide
LoadBalancer
须要外部支持(GCP and Azure),用户访问service.spec.external-ip,该IP对应到一个外部负载均衡的vip,外部服务对这个vip的请求,会被loadbalancer经过健康检查和转发,发送到一个运行着该服务pod的node上,并一样经过nodePort里的端口映射,发送给容器。
上述是几种比较广泛的service,随着社区发展,又出现了一些新的类型,能够作更灵活的适配。
ExternalName
用户能够指定一个任意的名字,做为该service被解析的CNAME,这种类型的servcie不用指定clusterIP,所以kube-proxy不会管理这类service,这类service须要使用1.7版本以上的kubedns。好比用户想建立一个service,但要让全部容器访问该service的请求都引导到用户本身定义的外部域名服务,就能够经过这个方式实现。能够说这个是最自由的一个方式:你但愿服务被如何解析,服务就能被如何解析。你甚至能够给多个service配置同一个externalName。
headless service
headless service是一个特殊的ClusterIP类service,这种service建立时不指定clusterIP(--cluster-ip=None),由于这点,kube-proxy不会管这种service,因而node上不会有相关的iptables规则。
当headless service有配置selector时,其对应的全部后端节点,会被记录到dns中,在访问service domain时kube-dns会将全部endpoints返回,选择哪一个进行访问则是系统本身决定;
当selector设置为空时,headless service会去寻找相同namespace下与本身同名的pod做为endpoints。这一点被应用到statefulset中,当一个三副本的statefulset(mysql1,mysql2,mysql3)运行在不一样节点时,咱们能够经过域名的方式对他们分别访问。