Service网络
Why:pod重启或者重建ip会发生改变,pod之间访问会有问题;
What:解耦了服务和应用。(集群内部服务之间调用填写service域名/IP即可;
How:声明一个service对象
一般常用的有两种:
k8s集群内部的service:selector指定pod,自动创建Endpoints
k8s集群外的service:手动创建Endpoints,指定外部服务的ip、端口和协议。
k8s 三种网络
node network
pod network
cluster network,也称作virtual IP虚拟网络—service
kube-proxy监听k8s-apiserver,一旦service资源发生变化,kube-proxy就会生成对应的负载调度的调整,这样就保证service的最新状态。
service 三种工作模式
userspace
iptabels
ipvs 最新版本
service类型
ExternalName
Cluster IP
Node Port
Load Balancer
资源记录
SVC_NAME.NS_NAME.DOMAIN.LTD.
DOMAIN.LTD 集群域名后缀默认为svc.cluster.local.
使用清单文件创建service资源
apiVersion: v1 kind: Service metadata: name: nginx namespace: default spec: selector: app: nginx role: logstor clusterIP: 10.97.97.97 //默认为clusterIP,不指定会自动分配 type: ClusterIP ports: - port: 80 //service端口 targetPort: 80 //容器端口
master yaml]# kubectl get svc -o wide //查看创建的SVC NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 28d <none> nginx ClusterIP 10.97.97.97 <none> 80/TCP 13m app=nginx,role=logstor
模拟匹配pod作为后端资源
master yaml]# kubectl apply -f test.yaml master yaml]# kubectl label pods nginx app=nginx master yaml]# kubectl describe svc nginx Name: nginx Namespace: default Labels: <none> Annotations: kubectl.kubernetes.io/last-applied-configuration: {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"nginx","namespace":"default"},"spec":{"clusterIP":"10.97.97.97","... Selector: app=nginx,role=logstor Type: ClusterIP IP: 10.97.97.97 Port: <unset> 80/TCP TargetPort: 80/TCP Endpoints: <none> //可以看到当svc标签与pod标签只匹配一个时,service未关联到pod资源 Session Affinity: None Events: <none> master yaml]# kubectl label pods nginx role=logstor //手动匹配所有标签 master yaml]# kubectl get svc -o wide NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 28d <none> nginx ClusterIP 10.97.97.97 <none> 80/TCP 13m app=nginx,role=logstor master yaml]# kubectl get pods --show-labels NAME READY STATUS RESTARTS AGE LABELS nginx 1/1 Running 0 10m app=nginx,role=logstor //此时标签已全匹配 master yaml]# kubectl describe svc nginx //再次查看发现已匹配后端资源 Name: nginx Namespace: default Labels: <none> Annotations: kubectl.kubernetes.io/last-applied-configuration: {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"nginx","namespace":"default"},"spec":{"clusterIP":"10.97.97.97","... Selector: app=nginx,role=logstor //svc标签 Type: ClusterIP IP: 10.97.97.97 Port: <unset> 80/TCP TargetPort: 80/TCP Endpoints: 10.244.1.84:80 //标签一致,匹配到的后端pod资源 Session Affinity: None Events: <none>
结论:servie根据SELECTOR标签选择器关联pod资源
查看svc关联的pod
]# kubectl get svc -A NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE default kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 236d kube-system kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 236d kube-system metrics-server ClusterIP 10.105.89.199 <none> 443/TCP 15d kubernetes-dashboard dashboard-metrics-scraper ClusterIP 10.109.95.129 <none> 8000/TCP 15d kubernetes-dashboard kubernetes-dashboard NodePort 10.98.147.122 <none> 443:30002/TCP 15d linux40 ng-deploy-80 NodePort 10.97.239.161 <none> 81:30019/TCP 20d ]# kubectl get pods -n linux40 -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES nginx-deployment-55fb8c9d77-9qlkp 1/1 Running 9 20d 10.244.1.98 node1 <none> <none> nginx-deployment-55fb8c9d77-sj8bs 1/1 Running 3 15d 10.244.2.29 node2 <none> <none> # 查看endpoint,svc关联的pod资源 ]# kubectl get ep -n linux40 NAME ENDPOINTS AGE ng-deploy-80 10.244.1.98:80,10.244.2.29:80 20d
资源记录
SVC_NAME.NS_NAME.DOMAIN.LTD.
集群默认后缀DOMAIN.LTD.:svc.cluster.local.
所以上面创建的服务记录为:nginx.default.svc.cluster.local
使用资源清单创建类型为nodeport的svc
apiVersion: v1 kind: Service metadata: name: myapp namespace: default spec: selector: app: myapp release: canary clusterIP: 10.99.99.99 type: NodePort ports: - port: 80 //service端口 targetPort: 80 //pod端口 nodePort: 30080 //映射节点端口,后续可以直接访问该节点端口,DNAT到service端口,再到pod端口 master yaml]# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 28d myapp NodePort 10.99.99.99 <none> 80:30080/TCP 38m //网络类型为节点网络 nginx ClusterIP 10.97.97.97 <none> 80/TCP 76m
访问servic
]# curl http://172.18.0.68:30080 //在集群外部机器访问该集群内IP加端口,servie后端须关联pod资源才可以被访问
注:此时可以在节点前面做代理服务器来访问service地址
Load Balancer(负载均衡器) 访问示意图
ExternalName(外部名称 )模式
把来自于同一个客户端的请求始终固定访问同一个pod资源
master yaml]# kubectl patch svc myapp -p ‘{“spec”:{“sessionAffinity”:“ClientIP”}}’
master yaml]# kubectl describe svc myapp //查看service详情 Name: myapp Namespace: default Labels: <none> Annotations: kubectl.kubernetes.io/last-applied-configuration: {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"myapp","namespace":"default"},"spec":{"clusterIP":"10.99.99.99","... Selector: app=myapp,release=canary Type: NodePort IP: 10.99.99.99 Port: <unset> 80/TCP TargetPort: 80/TCP NodePort: <unset> 30080/TCP Endpoints: 10.244.2.84:80 //后端pod资源 Session Affinity: ClientIP //此参数说明相同客户端访问相同pod资源;若为“none”则为负载均衡调度。 External Traffic Policy: Cluster Events: <none>
无头service:将service名称解析到后端pod ip地址来实现访问
apiVersion: v1 kind: Service metadata: name: myapp-svc namespace: default spec: selector: app: myapp release: canary clusterIP: "None" //指定service IP地址为空 ports: - port: 80 targetPort: 80
master yaml]# kubectl get svc -n kube-system NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 29d master yaml]# dig -t A myapp-svc.default.svc.cluster.local. @10.96.0.10 ...... ;; ANSWER SECTION: myapp-svc.default.svc.cluster.local. 30 IN A 10.244.1.97 //可以看到解析SVC资源记录地址为pod地址,当关联到时 myapp-svc.default.svc.cluster.local. 30 IN A 10.244.2.101 myapp-svc.default.svc.cluster.local. 30 IN A 10.244.2.100 myapp-svc.default.svc.cluster.local. 30 IN A 10.244.1.96 myapp-svc.default.svc.cluster.local. 30 IN A 10.244.2.104
......
master yaml]# kubectl get pods -o wide --show-labels //查看pod标签,与无头service 标签完全匹配
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS
myapp-deploy-f476f4fcf-jltjs 1/1 Running 3 6d1h 10.244.1.85 k8s-node1 <none> <none> app=myapp,pod-template-hash=f476f4fcf,releas=canary
myapp-deploy-f476f4fcf-nbqwv 1/1 Running 3 6d1h 10.244.2.86 k8s-node2 <none> <none> app=myapp,pod-template-hash=f476f4fcf,releas=canary
nginx 1/1 Running 1 7h26m 10.244.2.85 k8s-node2 <none> <none> app=myapp,release=canary
master yaml]# kubectl get svc -o wide //查看service标签,与pod 标签完全匹配时,可解析service name到pod地址
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 29d <none>
myapp NodePort 10.99.99.99 <none> 80:30080/TCP 7h49m app=myapp,release=canary
myapp-svc ClusterIP None <none> 80/TCP 3m23s app=myapp,release=canary //SVC地址为空
nginx ClusterIP 10.97.97.97 <none> 80/TCP 8h app=nginx,role=logstor
2.如何让集群内资源访问到互联网资源
待定
k8s ingress以及ingress Cintroller
参考文档:
https://segmentfault.com/a/1190000019908991
service的作用体现在两个方面,对集群内部,它不断跟踪pod的变化,更新endpoint中对应pod的对象,提供了ip不断变化的pod的服务发现机制,对集群外部,他类似负载均衡器,可以在集群内外部对pod进行访问。但是,单独用service暴露服务的方式,在实际生产环境中不太合适:
ClusterIP的方式只能在集群内部访问。
NodePort方式的话,测试环境使用还行,当有几十上百的服务在集群中运行时,NodePort的端口管理是灾难。
LoadBalance方式受限于云平台,且通常在云平台部署ELB还需要额外的费用。
所幸k8s还提供了一种集群维度暴露服务的方式,也就是ingress。ingress可以简单理解为service的service,他通过独立的ingress对象来制定请求转发的规则,把请求路由到一个或多个service中。这样就把服务与请求规则解耦了,可以从业务维度统一考虑业务的暴露,而不用为每个service单独考虑。
举个例子,现在集群有api、文件存储、前端3个service,可以通过一个ingress对象来实现图中的请求转发:
ingress的部署
ingress的部署,需要考虑两个方面:
ingress-controller是作为pod来运行的,以什么方式部署比较好
ingress解决了把如何请求路由到集群内部,那它自己怎么暴露给外部比较好
下面列举一些目前常见的部署和暴露方式,具体使用哪种方式还是得根据实际需求来考虑决定。
Deployment+LoadBalancer模式的Service
如果要把ingress部署在公有云,那用这种方式比较合适。用Deployment部署ingress-controller,创建一个type为LoadBalancer的service关联这组pod。大部分公有云,都会为LoadBalancer的service自动创建一个负载均衡器,通常还绑定了公网地址。只要把域名解析指向该地址,就实现了集群服务的对外暴露。
Deployment+NodePort模式的Service
同样用deployment模式部署ingress-controller,并创建对应的服务,但是type为NodePort。这样,ingress就会暴露在集群节点ip的特定端口上。由于nodeport暴露的端口是随机端口,一般会在前面再搭建一套负载均衡器来转发请求。该方式一般用于宿主机是相对固定的环境ip地址不变的场景。
NodePort方式暴露ingress虽然简单方便,但是NodePort多了一层NAT,在请求量级很大时可能对性能会有一定影响。
DaemonSet+HostNetwork+nodeSelector
用DaemonSet结合nodeselector来部署ingress-controller到特定的node上,然后使用HostNetwork直接把该pod与宿主机node的网络打通,直接使用宿主机的80/433端口就能访问服务。这时,ingress-controller所在的node机器就很类似传统架构的边缘节点,比如机房入口的nginx服务器。该方式整个请求链路最简单,性能相对NodePort模式更好。缺点是由于直接利用宿主机节点的网络和端口,一个node只能部署一个ingress-controller pod。比较适合大并发的生产环境使用。
ingress与ingress-controller
要理解ingress,需要区分两个概念,ingress和ingress-controller:
ingress对象:
指的是k8s中的一个api对象,一般用yaml配置。作用是定义请求如何转发到service的规则,可以理解为配置模板。
ingress-controller:
具体实现反向代理及负载均衡的程序,对ingress定义的规则进行解析,根据配置的规则来实现请求转发。
简单来说,ingress-controller才是负责具体转发的组件,通过各种方式将它暴露在集群入口,外部对集群的请求流量会先到ingress-controller,而ingress对象是用来告诉ingress-controller该如何转发请求,比如哪些域名哪些path要转发到哪些服务等等。
ingress Cintroller
重要:可以使用DaemonSet结合nodeselector来部署ingress-controller到特定的node(打上污点)上,然后使用HostNetwork直接把该pod与宿主机node的网络打通,直接使用宿主机的80/433端口就能访问服务。
一组独立运行的一组pod资源,可以理解为应用程序,可以实现七层调度负载均衡。
若集群中存在N多个Node节点,可以通过DaemonSet 控制器运行一个为集群接入七层调度的负载均衡pod,用于转发至后端pod。
此pod监听nNodeIP+port,可以在前面搭建四层代理转发至这几台node节点,在通过这几个pod实现七层负载均衡至后端pod。
如何对后端pod进行分组
还必须要借助service 的标签选择器来将后端代理的pod来分组,仅仅用来对后端pod资源功能分组,upstream 里面写入service地址,ingress Cintroller pod还是直接访问后端pod地址。
ingress 与 ingress Cintroller 并不是一回事
ingress资源
ingress 定义了期望 ingress Cintroller 如何建立一个前端,可能是一个虚机,也有可能是url映射。
如何实现对后端pod进行实时监控
同时定义了upstream service,可以通过service 来得到有几个主机并识别出后端pod的ip地址,并反馈注入到配置文件中。
可以保存为配置文件,一旦ingress发现serrvice 关联的pod资源发生改变(重建、修改等),会及时反应到ingress中并及时注入到 ingress Cintroller pod的配置文件中,pod中的主容器进程重新加载配置文件(因为upstream中配置的 service并不会实现负载均衡功能,仅仅用来实现分组分类,实际上还是ingress Cintroller pod还是直接访问后端pod地址)。
创建名称空间
apiVersion: v1 kind: Namespace metadata: name: ingress-nginx **定义一个ingress**
步骤
定义service 模式为Deployment+NodePort模式的Service
同样用deployment模式部署ingress-controller,并创建对应的服务,但是type为NodePort。这样,ingress就会暴露在集群节点ip的特定端口上。由于nodeport暴露的端口是随机端口,一般会在前面再搭建一套负载均衡器来转发请求。该方式一般用于宿主机是相对固定的环境ip地址不变的场景。
NodePort方式暴露ingress虽然简单方便,但是NodePort多了一层NAT,在请求量级很大时可能对性能会有一定影响。
定义 ingress。host定义了访问域名,这个域名必须可以解析
DaemonSet+HostNetwork+nodeSelector
用DaemonSet结合nodeselector来部署ingress-controller到特定的node上,然后使用HostNetwork直接把该pod与宿主机node的网络打通,直接使用宿主机的80/433端口就能访问服务。这时,ingress-controller所在的node机器就很类似传统架构的边缘节点,比如机房入口的nginx服务器。该方式整个请求链路最简单,性能相对NodePort模式更好。缺点是由于直接利用宿主机节点的网络和端口,一个node只能部署一个ingress-controller pod。比较适合大并发的生产环境使用。
自制证书