Service
Pod IP仅仅是集群内可见的虚拟IP,外部无法访问。
Pod IP会随着Pod的销毁而消失,当ReplicaSet对Pod进行动态伸缩时,Pod IP可能随时随地都会变化,这样对于我们访问这个服务带来了难度。
因此,Kubernetes中的Service对象就是解决以上问题的实现服务发现核心关键。
Service的类型
在Serivce定义时,我们需要指定spec.type字段,这个字段拥有四个选项:
● ClusterIP:默认值。给这个Service分配一个Cluster IP,它是Kubernetes系统自动分配的虚拟IP,因此只能在集群内部访问。
● NodePort:将Service通过指定的Node上的端口暴露给外部。通过此方法,访问任意一个NodeIP:nodePort都将路由到ClusterIP,
从而成功获得该服务。默认范围30000-32767。
● LoadBalancer:在 NodePort 的基础上,借助 cloud provider 创建一个外部的负载均衡器,并将请求转发到 <NodeIP>:NodePort。
此模式只能在云服务器(AWS等)上使用。
● ExternalName:将服务通过 DNS CNAME 记录方式转发到指定的域名(通过 spec.externlName 设定),
需要 kube-dns 版本在 1.7 以上。
应用场景:
集群内部访问:ClusterIP
集群外部访问:
集群访问外部:ExternalName
外部访问集群:
私有云:NodePort
公有云:LoadBalancer
Service的代理模式
● userspace 代理模式
● iptables 代理模式
● ipvs 代理模式:
ipvs模式,kube-proxy 会监视 Kubernetes Service 对象和 Endpoints ,调用 netlink 接口以相应地创建 ipvs 规则并定期与 Kubernetes Service 对象和Endpoints 对象同步 ipvs 规则,以确保 ipvs 状态与期望一 致。访问服务时,流量将被重定向到其中一个后端 Pod 。与 iptables 类似,ipvs 于 netfilter 的 hook 功能,但使用哈希表作为底层数据结构并在内核空间中工作。这意 味着 ipvs 可以更快地重定向流量,并且在同步代理规则时具有更好的性能。此外,ipvs 为负载均衡算法提供了更多选项,例如:
rr :轮询调度
lc :最小连接数
dh :目标哈希
sh :源哈希
sed :最短期望延迟
nq : 不排队调度
ClusterIp
apiVersion: v1
kind: Service
metadata:
name: myapp
namespace: default
spec:
type: ClusterIP
selector:
app: myapp
release: stabel
ports:
- name: http
port: 80
targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-deploy
namespace: default
spec:
replicas: 2
selector:
matchLabels:
app: myapp
release: stabel
template:
metadata:
labels:
app: myapp
release: stabel
env: test
spec:
containers:
- name: myapp
image: mynginx:v1
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
通过集群分配给service IP访问
$ kubectl get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
kubernetes ClusterIP 241.254.0.1 <none> 443/TCP 25d <none>
myapp ClusterIP 241.254.228.65 <none> 80/TCP 36h app=myapp,release=stabel
$ kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
myapp-deploy-6d4bc5cc76-7n5tn 1/1 Running 2 32h 241.255.0.122 master <none> <none>
myapp-deploy-6d4bc5cc76-fprjr 1/1 Running 3 32h 241.255.3.41 node1 <none> <none>
$ ipvsadm -Ln
...
TCP 241.254.228.65:80 rr
-> 241.255.0.122:80 Masq 1 0 1
-> 241.255.3.41:80 Masq 1 0 1
....
$ curl 241.254.228.65/hostname.html
myapp-deploy-6d4bc5cc76-fprjr
$ curl 241.254.228.65/hostname.html
myapp-deploy-6d4bc5cc76-7n5tn
通过集群分配给service域名访问
在k8s集群中每个Service都会有一个DNS名称,Services 包括两种,一种是普通的service服务,一种是headless services。 普通的service 的DNS会解析到一个服务的集群IP,headless services 则会直接解析到所包含的pod的IP。
service 域名命名一般是:<服务名>.<命名空间>.svc.cluster.local
将svc域名解析到coreDNS服务器,集群内部DNS解析可参考kubernetes集群内部DNS解析原理这篇文章
$ yum -y install bind-utils
#使用dig命令测试svc name是否解析到DNS服务器
$ dig -t -A myapp.default.svc.cluster.local. @241.254.0.10
......
;; ANSWER SECTION:
myapp.default.svc.cluster.local. 30 IN A 241.254.228.65
....
$ kubectl get svc -n kube-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-dns ClusterIP 241.254.0.10 <none> 53/UDP,53/TCP,9153/TCP 25d
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 241.254.0.1 <none> 443/TCP 25d
myapp ClusterIP 241.254.228.65 <none> 80/TCP 2d1h
#可使用两种访问访问
#1、在宿主机/etc/resolv.conf 添加nameserver指向kube-dns
#cat /etc/resolv.conf
#search localdomain
#nameserver 192.168.234.2
#nameserver 241.254.0.10
$ curl myapp.default.svc.cluster.local./hostname.html
myapp-deploy-6d4bc5cc76-7n5tn
$ curl myapp.default.svc.cluster.local./hostname.html
myapp-deploy-6d4bc5cc76-fprjr
#2.由于集群内pod解析默认的是指向kube-dns,所以也可随意在某个pod内访问
#由于pod内部的resolv.conf配置search域
#search域的作用就是将域名进行后缀匹配,例如myapp myapp.default.svc.cluster.local->myapp.svc.cluster.local
#->myapp.cluster.local直到匹配为止
$ kubectl exec mypod -- curl myapp.default.svc.cluster.local./hostname.html
$ kubectl exec mypod -- curl myapp/hostname.html
$ kubectl exec mypod -- curl myapp.default/hostname.html
$ kubectl exec mypod -- curl myapp.default.svc/hostname.html
#都可以访问到结果,只是效率会降低,因为多匹配一次dns查询
小贴士:ping通service域名
如果想要ping通service 域名,就必须让kube-proxy以ipvs代理模式运行。原因如下:
1、serviceIP是虚拟的地址,没有分配给任何网络接口,当数据包传输时不会把这个IP作为数据包的源IP或目的IP。
2、kube-proxy在iptables模式下,这个IP没有被设置在任何的网络设备上,ping这个IP的时候,没有任何的网络协议栈会回应这个ping请
求。在iptables模式时,clusterIp会在iptables的PREROUTING链里面用于nat转换规则中。
3、在ipvs模式下,所有的clusterIp会被设置在node上的kube-ipvs0的虚拟网卡上,这个时候去ping是可以通的
NodePort
apiVersion: v1
kind: Service
metadata:
name: myapp-nodeport
namespace: default
spec:
type: NodePort
selector:
app: myapp
release: stabel
ports:
- name: http
port: 80
targetPort: 80
nodePort: 30001
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 241.254.0.1 <none> 443/TCP 176m
myapp-nodeport NodePort 241.254.6.59 <none> 80:30001/TCP 4m10s
#通过集群内部ip访问
$ curl 241.254.6.59/hostname.html
myapp-deploy-6d4bc5cc76-lfbbh
$ curl 241.254.6.59/hostname.html
myapp-deploy2-7bddc44f9c-srgj8
#通过宿主机ip访问
$ curl 192.168.116.128:30001/hostname.html
myapp-deploy-6d4bc5cc76-hkdlr
$ curl 192.168.116.128:30001/hostname.html
myapp-deploy-6d4bc5cc76-lfbbh
ExternalName
这种类型的 Service 通过返回 CNAME 和它的值,可以将服务映射到 externalName 字段的内容( 例如:hub.hongfu.com )。ExternalName Service 是 Service 的特例,它没有 selector,也没有定义任何的端口和 Endpoint。相反的,对于运行在集群外部的服务,它通过返回该外部服务的别名这种方式来提供服务
kind: Service
apiVersion: v1
metadata:
name: myservice
namespace: default
spec:
type: ExternalName
externalName: www.baidu.com
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 241.254.0.1 <none> 443/TCP 3h43m
myservice ExternalName <none> www.baidu.com <none> 6m33s
由于CoreDNS会监测apiserver,当使用kubectl命令向apiServer提交service请求时,CoreDNS会为每一个Service创建DNS记录用于域名解析,当创建type类型为ExternalName,CoreDNS会将<服务名>.<命名空间>.svc.cluster.local**解析到externalName的值,比如上例的 myservice.default.svc.cluster.local会解析到www.baidu.com
$ nslookup myservice.default.svc.cluster.local
Server: 241.254.0.10
Address: 241.254.0.10#53
myservice.default.svc.cluster.local canonical name = www.baidu.com.
www.baidu.com canonical name = www.a.shifen.com.
Name: www.a.shifen.com
Address: 180.101.49.12
Name: www.a.shifen.com
Address: 180.101.49.11
Service与Pod与关联
两者之间通过标签关联,spec.template.metadata.labels为pod打上名为mediaplus-main的标签,Service的spec.selector需要与spec.template.metadata.labels值一致
apiVersion: v1
kind: Service
metadata:
name: mediaplus-main
labels:
app: mediaplus-main
spec:
..............
selector:
app: mediaplus-main
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: mediaplus-main
spec:
replicas: 1
selector:
matchLabels:
app: mediaplus-main
template:
metadata:
labels:
app: mediaplus-main
spec:
containers:
- name: mediaplus-main
Service与Ingress关联
Service只能实现四层代理 传输层,基于IP和端口
Ingress可以实现七层代理 应用层,基于应用协议转发,例如http协议
Ingress与Service之间通过匹配serviceName绑定,metadata.labels为Service打上mediaplus-main的标签,spec.rules.backend.serviceName的值需要与mediaplus-main保持一致
apiVersion: v1
kind: Service
metadata:
name: mediaplus-main
labels:
app: mediaplus-main
spec:
..............
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: name-virtual-host-ingress
spec:
tls:
- secretName: sign-secret
rules:
.........
- backend:
serviceName: mediaplus-main
servicePort: 3000
path: /