大多数资料由老师提供。
目录
server概念
为什么要有service?
在 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 决定)
service的四种实现类型
ExternalName:适用于k8s集群内部容器访问外部资源,它没用selector,也没用定义任何端口和Endpoint。
ClusterIP:提供一个集群内部的虚拟IP地址以供Pod访问(默认模式)
NodePort:在Node上打开一个端口以供外部访问
LoadBalancer:通过外部负载均衡器来访问 (需要额外的模组来提供负载均衡)
实验步骤
创建service:type类型为ClusterIP
创建一个文件夹用来存放yaml文件
mkdir /opt/tmp
cd /opt/tmp
1.首先创建pod
[root@master tmp]# cat pod_test.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
selector:
matchLabels:
gg: my-nginx
replicas: 2
template:
metadata:
labels:
gg: my-nginx
spec:
containers:
- name: my-nginx
image: nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80 #pod中容器需要暴露的端口
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 <none> <none>
my-nginx-9db6b8945-mp6ls 1/1 Running 0 52s 10.244.1.2 node1 <none> <none>
[root@master ~]# curl 10.244.0.6
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
如果我们误操作删除了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 <no ne> <none>
my-nginx-9db6b8945-s79lj 1/1 Running 0 24s 10.244.0.6 master <no ne> <none>
通过上面可以看到重新生成了一个 pod :my-nginx-9db6b8945-s79lj,ip 是 10.244.0.6,在 k8s 中创建 pod,如果 pod 被删除了,重新生成的 pod ip 地址会发生变化,所以需要在 pod 前端加一个 固定接入层。接下来创建 service:
[root@master tmp]# cat service_test.yaml
apiVersion: v1
kind: Service
metadata:
name: my-nginx
labels:
gg: my-nginx
spec:
type: ClusterIP
ports:
- port: 80 #service的端口,暴露给k8s集群内部服务访问
protocol: TCP
targetPort: 80 #pod容器中定义的端口
selector:
gg: my-nginx # 选择有gg=my-nginx标签的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 <none> 443/TCP 78d
my-nginx ClusterIP 10.101.211.226 <none> 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 <none> 80/TCP 59s
在 k8s 控制节点访问 service 的 ip:端口就可以把请求代理到后端 pod
[root@master tmp]# curl 10.101.211.226:80
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
通过上面可以看到请求 service IP:port 跟直接访问 pod ip:port 看到的结果一样,这就说明 service 可以把请求代理到它所关联的后端 pod
注意:上面的 10.101.211.226:80 地址只能是在 k8s 集群内部可以访问,在外部无法访问,比方说我们想要通过浏览器访问,那么是访问不通的,如果想要在 k8s 集群之外访问,是需要把 service type 类型改成 nodePort 的
查看service的详细信息
[root@master tmp]# kubectl describe svc my-nginx
Name: my-nginx
Namespace: default
Labels: gg=my-nginx
Annotations: Selector: gg=my-nginx
Type: ClusterIP
IP: 10.101.211.226
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 10.244.0.6:80,10.244.1.2:80
Session Affinity: None
Events: <none>
service 可以对外提供统一固定的 ip 地址,并将请求重定向至集群中的 pod。其中“将请求重定向 至集群中的 pod”就是通过 endpoint 与 selector 协同工作实现。
selector 是用于选择 pod,由 selector 选择出来的 pod 的 ip 地址和端口号,将会被记录在 endpoint 中。endpoint 便记录了所有 pod 的 ip 地址和端口号。当一个请求访问到 service 的 ip 地址时,就会从 endpoint 中选择出一个 ip地址 和端口号,然后将请求重定向至 pod 中。
具体把请求代理到哪个 pod,需要的就是 kube-proxy 的轮询实现的。service 不会直接到 pod,service 是直接到 endpoint 资源,就是地址加端口,再由 endpoint 关联到 pod。
创建service: type类型是NodePort
创建pod资源
这里我们看到pod的创建和之前类似。
[root@master tmp]# cat pod_nodeport.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx-nodeport
spec:
selector:
matchLabels:
gg: my-nginx-nodeport
replicas: 2
template:
metadata:
labels:
gg: my-nginx-nodeport
spec:
containers:
- name: my-nginx-nodeport
image: nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
更新并查看是否创建成功,running
[root@master tmp]# kubectl apply -f pod_nodeport.yaml
deployment.apps/my-nginx-nodeport created
[root@master tmp]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
my-nginx-nodeport-69d6b4d6ff-2ts5v 1/1 Running 0 9s 10.244.0.7 master <none> <none>
my-nginx-nodeport-69d6b4d6ff-djjwc 1/1 Running 0 9s 10.244.1.4 node 1 <none> <none>
创建service
[root@master tmp]# cat service_nodeport.yaml
apiVersion: v1
kind: Service
metadata:
name: my-nginx-nodeport
labels:
gg: my-nginx-nodeport
spec:
type: NodePort #类型变化
ports:
- port: 80
protocol: TCP
targetPort: 80
nodePort: 30380 #开放端口
selector:
gg: my-nginx-nodeport
查看service
[root@master tmp]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 78d
my-nginx-nodeport NodePort 10.96.223.93 <none> 80:30380/TCP 19m
访问没有问题
[root@master tmp]# curl 10.96.223.93
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
10.96.223.93 是 k8s 集群内部的 service ip 地址,只能在 k8s 集群内部访问,在集群外无法访问。
在集群外访问service 192.168.225.30 是你的 Node IP
[root@master tmp]# curl 192.168.225.30:30380
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
在浏览器访问
服务请求走向:Client ->192.168.225.30:30380->10.96.223.93:80->pod ip:80
如果忘记参数命令,我们可以使用explain命令去查看
[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 <Object>
Spec defines the behavior of a service.
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
status <Object>
Most recently observed status of the service. Populated by the system.
Read-only. More info:
https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
[root@master tmp]# kubectl explain service.spec
KIND: Service
VERSION: v1
RESOURCE: spec <Object>
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 <string>
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 <string>
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 <string>
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 <integer>
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.