在 kubernetes 中,Pod 是有生命周期的,如果 Pod 重启它的 IP 很有可能会发生变化。如果我们的服务都是将 Pod 的 IP 地址写死,Pod 挂掉或者重启,和刚才重启的 pod 相关联的其他服务将会找不到它所 关联的 Pod,为了解决这个问题,在 kubernetes 中定义了 service 资源对象,Service 定义了一个服务访问的入口,客户端通过这个入口即可访问服务背后的应用集群实例,service 是一组 Pod 的逻辑集合, 这一组 Pod 能够被 Service 访问到,通常是通过 Label Selector 实现的。
如下图:
pod ip经常变化,service是pod的代理,客户端访问,只需要访问service,就会把请求代理到Pod
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 决定)
pod 虽然定义了容器端口,但是不会使用调度到该节点上的 80 端口,也不会使用任何特定的 NAT 规则去路由流量到 Pod 上。 这意味着可以在同一个节点上运行多个 Pod,使用相同的容器端口,并且可以从集群中任何其他的 Pod 或节点上使用 IP 的方式访问到它们。
创建完yaml文件后,我们要更新资源清单文件
[root@master tmp]# kubectl apply -f pod_test.yaml
看到如下红色报错信息,我们就要去检查文件是否正确修改后再次更新。
查看我们刚刚创建的Pod
[root@master tmp]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINA TED NODE READINESS GATES
my-nginx-9db6b8945-j2tqr 1/1 Running 0 52s 10.244.1.3 node1
my-nginx-9db6b8945-mp6ls 1/1 Running 0 52s 10.244.1.2 node1
[root@master ~]# curl 10.244.0.6
Welcome to nginx!
如果我们误操作删除了Pod。
例如我们删除pod: my-nginx-9db6b8945-j2tqr
[root@master tmp]# kubectl delete pods my-nginx-9db6b8945-j2tqr
pod "my-nginx-9db6b8945-j2tqr" deleted
[root@master tmp]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOM INATED NODE READINESS GATES
my-nginx-9db6b8945-mp6ls 1/1 Running 0 5m12s 10.244.1.2 node1
my-nginx-9db6b8945-s79lj 1/1 Running 0 24s 10.244.0.6 master
通过上面可以看到重新生成了一个 pod :my-nginx-9db6b8945-s79lj,ip 是 10.244.0.6,在 k8s 中创建 pod,如果 pod 被删除了,重新生成的 pod ip 地址会发生变化,所以需要在 pod 前端加一个 固定接入层。接下来创建 service:
[root@master tmp]# kubectl apply -f service_test.yaml
service/my-nginx created
[root@master tmp]# kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 443/TCP 78d
my-nginx ClusterIP 10.101.211.226 80/TCP 24s
[root@master tmp]# kubectl get svc -l gg=my-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-nginx ClusterIP 10.101.211.226 80/TCP 59s
在 k8s 控制节点访问 service 的 ip:端口就可以把请求代理到后端 pod
[root@master tmp]# curl 10.101.211.226:80
Welcome to nginx!
通过上面可以看到请求 service IP:port 跟直接访问 pod ip:port 看到的结果一样,这就说明 service 可以把请求代理到它所关联的后端 pod
注意:上面的 10.101.211.226:80 地址只能是在 k8s 集群内部可以访问,在外部无法访问,比方说我们想要通过浏览器访问,那么是访问不通的,如果想要在 k8s 集群之外访问,是需要把 service type 类型改成 nodePort 的
service 可以对外提供统一固定的 ip 地址,并将请求重定向至集群中的 pod。其中“将请求重定向 至集群中的 pod”就是通过 endpoint 与 selector 协同工作实现。
selector 是用于选择 pod,由 selector 选择出来的 pod 的 ip 地址和端口号,将会被记录在 endpoint 中。endpoint 便记录了所有 pod 的 ip 地址和端口号。当一个请求访问到 service 的 ip 地址时,就会从 endpoint 中选择出一个 ip地址 和端口号,然后将请求重定向至 pod 中。
[root@master tmp]# kubectl explain service
KIND: Service
VERSION: v1
DEscriptION:
Service is a named abstraction of software service (for example, mysql)
consisting of local port (for example 3306) that the proxy listens on, and
the selector that determines which pods will answer requests sent through
the proxy.
.........
spec
[root@master tmp]# kubectl explain service.spec
KIND: Service
VERSION: v1
RESOURCE: spec
DEscriptION:
Spec defines the behavior of a service.
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
ServiceSpec describes the attributes that a user creates on a service.
FIELDS:
clusterIP
clusterIP is the IP address of the service and is usually assigned randomly
by the master. If an address is specified manually and is not in use by
others, it will be allocated to the service; otherwise, creation of the
service will fail. This field can not be changed through updates. Valid
values are "None", empty string (""), or a valid IP address. "None" can be
specified for headless services when proxying is not required. only applies
to types ClusterIP, NodePort, and LoadBalancer. Ignored if type is
ExternalName. More info:
https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies
externalIPs <[]string>
externalIPs is a list of IP addresses for which nodes in the cluster will
also accept traffic for this service. These IPs are not managed by
Kubernetes. The user is responsible for ensuring that traffic arrives at a
node with this IP. A common example is external load-balancers that are not
part of the Kubernetes system.
externalName
externalName is the external reference that kubedns or equivalent will
return as a CNAME record for this service. No proxying will be involved.
Must be a valid RFC-1123 hostname (https://tools.ietf.org/html/rfc1123) and
requires Type to be ExternalName.
externalTrafficPolicy
externalTrafficPolicy denotes if this Service desires to route external
traffic to node-local or cluster-wide endpoints. "Local" preserves the
client source IP and avoids a second hop for LoadBalancer and Nodeport type
services, but risks potentially imbalanced traffic spreading. "Cluster"
obscures the client source IP and may cause a second hop to another node,
but should have good overall load-spreading.
healthCheckNodePort
healthCheckNodePort specifies the healthcheck nodePort for the service. If
not specified, HealthCheckNodePort is created by the service api backend
with the allocated nodePort. Will use user-specified nodePort value if
specified by the client. only effects when Type is set to LoadBalancer and
ExternalTrafficPolicy is set to Local.