✨✨ 欢迎大家来到景天科技苑✨✨
🎈🎈 养成好习惯,先赞后看哦~🎈🎈
🏆 作者简介:景天科技苑
🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。
🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi,flask等框架,云原生k8s,linux,shell脚本等实操经验,网站搭建,数据库等分享。所属的专栏:云原生K8S,零基础到进阶实战
景天的主页:景天科技苑
在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的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的ip和endpoints,pod的ip为什么能保存到防火墙规则里?是通过kube-proxy组件决定的
k8s在创建Service时,会根据标签选择器selector(lable selector)来查找Pod,
据此创建与Service同名的endpoints对象,当Pod 地址发生变化时,endpoint也会随之发生变化,
service接收前端client请求的时候,就会通过endpoint,找到转发到哪个Pod进行访问的地址。
(至于转发到哪个节点的Pod,由负载均衡kube-proxy决定)
若service定义中没有selector字段,service被创建时,endpoint controller不会自动创建endpoint。
service 负载分发策略有两种:
RoundRobin:轮询模式,即轮询将请求转发到后端的各个pod上(默认模式);
SessionAffinity:基于客户端IP地址进行会话保持的模式,第一次客户端访问后端某个pod,之后的请求都转发到这个pod上。
DNS:可以通过cluster add-on的方式轻松的创建KubeDNS来对集群内的Service进行服务发现————这也是k8s官方强烈推荐的方式。
为了让Pod中的容器可以使用kube-dns来解析域名,k8s会修改容器的/etc/resolv.conf配置。
endpoint
endpoint是k8s集群中的一个资源对象,存储在etcd中,用来记录一个service对应的所有pod的访问地址。
service配置selector,endpoint controller才会自动创建对应的endpoint对象;否则,不会生成endpoint对象.
例如,k8s集群中创建一个名为k8s-classic-1113-d3的service,就会生成一个同名的endpoint对象,如下图所示。
其中ENDPOINTS就是service关联的pod的ip地址和端口。
endpoint controller是k8s集群控制器的其中一个组件,其功能如下:
负责生成和维护所有endpoint对象的控制器
负责监听service和对应pod的变化
监听到service被删除,则删除和该service同名的endpoint对象
监听到新的service被创建,则根据新建service信息获取相关pod列表,然后创建对应endpoint对象
监听到service被更新,则根据更新后的service信息获取相关pod列表,然后更新对应endpoint对象
监听到pod事件,则更新对应的service的endpoint对象,将podIp记录到endpoint中
查看pod的ip
kubectl get pods -n kube-system -owide
Node Network和Pod network这两种网络地址是我们实实在在配置的,
其中节点网络地址是配置在节点接口之上,而pod网络地址是配置在pod资源之上的,
因此这些地址都是配置在某些设备之上的,这些设备可能是硬件,也可能是软件模拟的
kubectl get svc
查看定义Service资源需要的字段有哪些?
[root@master01 ~ ]#kubectl explain service.spec KIND: Service VERSION: v1 RESOURCE: spec
service在实现负载均衡的时候还支持sessionAffinity,sessionAffinity
什么意思?会话联系,默认是none,随机调度的(基于iptables规则调度的);
如果我们定义sessionAffinity的client ip,那就表示把来自同一客户端的IP请求调度到同一个pod上,类似于nginx的会话保持
查看定义Service.spec.type需要的字段有哪些?
[root@master01 ~ ]#kubectl explain service.spec.type KIND: Service VERSION: v1 FIELD: type DESCRIPTION: type determines how the Service is exposed. Defaults to ClusterIP. Valid options are ExternalName, ClusterIP, NodePort, and LoadBalancer. "ClusterIP" allocates a cluster-internal IP address for load-balancing to endpoints. Endpoints are determined by the selector or if that is not specified, by manual construction of an Endpoints object or EndpointSlice objects. If clusterIP is "None", no virtual IP is allocated and the endpoints are published as a set of endpoints rather than a virtual IP. "NodePort" builds on ClusterIP and allocates a port on every node which routes to the same endpoints as the clusterIP. "LoadBalancer" builds on NodePort and creates an external load-balancer (if supported in the current cloud) which routes to the same endpoints as the clusterIP. "ExternalName" aliases this service to the specified externalName. Several other fields do not apply to ExternalName services. More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types
适用于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记录。
service的FQDN是:
..svc.cluster.local my-service.prod.svc.cluster.local
如果使用externalName时,集群内部访问my-service.prod.svc.cluster.local 就相当于访问externalName配置的
my.database.example.com,这个可以是是集群之外的域名
通过k8s集群内部IP暴露服务,选择该值,服务只能够在集群内部访问,这也是默认的ServiceType。
查看系统默认的service type
kubectl get svc -n kube-system
通过每个Node节点上的IP和静态端口暴露k8s集群内部的服务。 通过请求:可以把请求代理到内部的pod。 Client----->NodeIP:NodePort----->Service Ip:ServicePort----->PodIP:ContainerPort。
适用于集群外部访问内部资源,service会在宿主机上映射出一个端口
[root@k8s-master ~ ]#kubectl get svc -n h5-web NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE new-openaccount NodePort 10.43.68.177 8898:8898/TCP 10d opencardadmin NodePort 10.43.248.155 8899:31180/TCP 14d
这种就是NodePort类型的service,会在宿主机映射出指定的端口
[root@k8s-master ~ ]#netstat -lanptu|grep 31180 tcp 0 0 0.0.0.0:31180 0.0.0.0:* LISTEN 4171449/kube-proxy
使用云提供商的负载均衡器,可以向外部暴露服务。外部的负载均衡器可以路由到NodePort服务和ClusterIP 服务。
云提供商的负载均衡器,阿里云,华为云,腾讯云等
查看service的spec.ports字段如何定义?
[root@master01 ~]# kubectl explain service.spec.ports KIND: Service VERSION: v1 RESOURCE: ports <[]Object> DESCRIPTION: The list of ports that are exposed by this service. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies ServicePort contains information on service's port. FIELDS: appProtocol name #定义端口的名字 nodePort |#宿主机上映射的端口,比如一个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 targetPort # targetPort是pod上的端口 # targetPort是pod上的端口,从port和nodePort上来的流量,经过kube-proxy流入到后端pod的targetPort上, 最后进入容器。与制作容器时暴露的端口一致(使用DockerFile中的EXPOSE),例如官方的nginx暴露80端口。
1、创建Pod
#把nginx.tar.gz上传到node01和node02,手动解压
[root@node01 ~ ]#docker load -i nginx.tar.gz [root@node02 ~ ]#docker load -i nginx.tar.gz
[root@master01 service ]#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: - name: my-nginx image: nginx imagePullPolicy: IfNotPresent ports: - containerPort: 80 #pod中的容器需要暴露的端口
#更新资源清单文件
[root@master01 service ]#kubectl apply -f pod_test.yaml deployment.apps/my-nginx created [root@master01 service ]#kubectl get pods NAME READY STATUS RESTARTS AGE my-nginx-5684588fff-2nwtp 1/1 Running 0 11s my-nginx-5684588fff-dcmgj 1/1 Running 0 11s
[root@master01 service ]#kubectl get pods -l run=my-nginx -owide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES my-nginx-5684588fff-2nwtp 1/1 Running 0 8m24s 172.21.231.129 node02 my-nginx-5684588fff-dcmgj 1/1 Running 0 8m24s 172.29.55.7 node01
请求pod ip地址,查看结果
[root@master01 service ]#curl http://172.21.231.129 Welcome to nginx!
[root@master01 service ]#curl 172.29.55.7 Welcome to nginx!
[root@master01 service ]#kubectl exec -it my-nginx-5684588fff-2nwtp -- /bin/bash root@my-nginx-5684588fff-2nwtp:/# ls -l total 12 root@my-nginx-5684588fff-2nwtp:/# curl 172.21.231.129 Welcome to nginx!
需要注意的是,pod虽然定义了容器端口,但是不会使用调度到该节点上的80端口,
也不会使用任何特定的NAT规则去路由流量到Pod上。 这意味着可以在同一个节点上运行多个 Pod,
使用相同的容器端口,并且可以从集群中任何其他的Pod或节点上使用IP的方式访问到它们。
误删除其中一个Pod:
[root@master01 service ]#kubectl delete pods my-nginx-5684588fff-2nwtp pod "my-nginx-5684588fff-2nwtp" deleted
[root@master01 service ]#kubectl get pods -owide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES my-nginx-5684588fff-c5hnr 1/1 Running 0 6s 172.21.231.130 node02 my-nginx-5684588fff-dcmgj 1/1 Running 0 122m 172.29.55.7 node01
可见有生成一个新的pod,并且ip发生了变化,所以需要在pod前端加一个固定接入层。接下来创建service:
查看pod标签:
[root@master01 service ]#kubectl get pods --show-labels NAME READY STATUS RESTARTS AGE LABELS my-nginx-5684588fff-c5hnr 1/1 Running 0 2m pod-template-hash=5684588fff,run=my-nginx my-nginx-5684588fff-dcmgj 1/1 Running 0 123m pod-template-hash=5684588fff,run=my-nginx
2、创建Service
[root@master01 service ]#vim service_test.yaml apiVersion: v1 kind: Service metadata: name: my-nginx labels: run: my-service spec: type: ClusterIP ports: - port: 80 #service的端口,暴露给k8s集群内部服务访问 protocol: TCP 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 的端口)上暴露。
[root@master01 service ]#kubectl apply -f service_test.yaml service/my-nginx created [root@master01 service ]# [root@master01 service ]# [root@master01 service ]#kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 192.168.0.1 443/TCP 8h my-nginx ClusterIP 192.168.148.246 80/TCP 5s
#在k8s控制节点访问service的ip:端口就可以把请求代理到后端pod
root@master01 service ]#curl 192.168.148.246 Welcome to nginx!
Pod 中的端口定义是有名字的,你可以在 Service 的 targetPort 属性中引用这些名称。
例如,我们可以通过以下方式将 Service 的 targetPort 绑定到 Pod 端口:
apiVersion: v1 kind: Pod metadata: name: nginx labels: app.kubernetes.io/name: proxy spec: containers: - name: nginx image: nginx:stable ports: - containerPort: 80 name: http-web-svc --- apiVersion: v1 kind: Service metadata: name: nginx-service spec: selector: app.kubernetes.io/name: proxy ports: - name: name-of-service-port protocol: TCP port: 80 targetPort: http-web-svc
1、创建一个pod资源
[root@master01 service ]#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-container image: nginx imagePullPolicy: IfNotPresent ports: - containerPort: 80
每个控制器创建的pod的标签不要一样,要区分开
2、创建service,代理pod
[root@master01 service ]#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 nodePort: 30380 selector: run: my-nginx-nodeport
[root@master01 service ]#kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 192.168.0.1 443/TCP 12d my-nginx ClusterIP 192.168.148.246 80/TCP 12d my-nginx-nodeport NodePort 192.168.158.194 80:30380/TCP 13s
这样,在外部通过集群的任意ip:30380 就可以访问pod了,这种方式只能是通过kubeadm安装的集群可以,二进制安装的集群需要通过
node节点的ip:30380才能访问
http://10.10.0.10:30380/
[root@master01 service ]#curl 10.10.0.10:30380 Welcome to nginx!