k8s service的基本用法
一般来说,对外提供服务的应用程序需要通过某种机制来实现,对于容器应用最简便的方式就是通过TCP/IP机制及监听IP和端口号来实现。例如,定义一个提供Web服务的RC,由两个Tomcat容器副组成,每个容器都通过containerPort设置提供服务的端口号为8080:webapp-rc.yamlapiVersion: v1kind: ReplicationControllermetadata:na
通过以前的学习,我们已经能够通过ReplicaSet来创建一组Pod来提供具有高可用性的服务。虽然每个Pod都会分配一个单独的Pod IP,然而却存在如下两问题:
- Pod IP仅仅是集群内可见的虚拟IP,外部无法访问。
- Pod IP会随着Pod的销毁而消失,当ReplicaSet对Pod进行动态伸缩时,Pod IP可能随时随地都会变化,这样对于我们访问这个服务带来了难度.
因此,Kubernetes中的Service对象就是解决以上问题的实现服务发现核心关键。
Service可以看作是一组提供相同服务的Pod对外的访问接口。
借助Service,应用可以方便地实现服务发现和4层负载均衡。和其他Controller对象一样,通过Label Selector来确定一个Service将要使用哪些Pod。一个简单的Service定义如下:
例如,定义一个提供Web服务的RC,由两个Tomcat容器副组成,每个容器都通过containerPort设置提供服务的端口号为8080:
webapp-rc.yaml
apiVersion: v1
kind: ReplicationController
metadata:
name: webapp
spec:
replicas: 2
template:
metadata:
name: webapp
labels:
app: webapp
spec:
containers:
- name: webapp
image: tomcat
ports:
- containerPort: 8080
[root@bogon ~]# kubectl get rc
NAME DESIRED CURRENT READY AGE
webapp 2 2 2 17m
[root@bogon ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
webapp-jsmkp 1/1 Running 0 118s
webapp-s7b6t 1/1 Running 0 118s
创建该RC之后获取该Pod的IP地址:
[root@bogon ~]# kubectl get pod -l app=webapp -o yaml|egrep -w "podIP|nodeName"
nodeName: server01
podIP: 172.17.0.2
nodeName: server01
podIP: 172.17.0.3
去server01 节点上直接通过这两个Pod的IP地址和端口号访问Tomcat服务:
[root@server01 ~]# curl 172.17.0.2:8080
<!doctype html><html lang="en"><head><title>HTTP Status 404 – Not Found</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 404 – Not Found</h1><hr class="line" /><p><b>Type</b> Status Report</p><p><b>Description</b> The origin server did not find a current representation for the target resource or is not willing to disclose that one exists.</p><hr class="line" /><h3>Apache Tomcat/9.0.37</h3></body></html>
直接通过Pod的IP地址和端口号可以访问到容器应用内的服务,但是Pod的IP地址是不可靠的。
例如当Pod所在的Node发生故障时,Pod将被Kubernetes重新调度到另一个Node,Pod的IP地址将发生变化。
更重要的是,如果容器应用本身是分布式的部署方式,通过多个实例共同提供服务,就需要在这些实例的前端设置一个负载均衡器来实现请求的分发。
Kubernetes中的Service就是用于解决这些问题的核心组件。
kubectl expose快速创建service
以前面创建的webapp应用为例,为了让客户端应用访问到两个Tomcat Pod实例,需要创建一个Service来提供服务。
Kubernetes提供了一种快速的方法,即通过kubectl expose命令来创建Service:
[root@bogon ~]# kubectl expose rc webapp
service/webapp exposed
查看新创建的Service,可以看到系统为它分配了一个虚拟的IP地址(ClusterIP),Service所需的端口号则从Pod中的containerPort复制而来:
[root@bogon ~]# kubectl get svc -l app=webapp
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
webapp ClusterIP 169.169.122.239 <none> 8080/TCP 3m
接下来就可以通过Service的IP地址和Service的端口号访问该Service了:
[root@server01 ~]# curl 169.169.122.239:8080
这里,对Service地址169.169.122.239:8080的访问被自动负载分发到了后端两个Pod之一。
配置文件定义Service
除了使用kubectl expose命令创建Service,我们也可以通过配置文件定义Service,再通过kubectl create命令进行创建。
例如对于前面的webapp应用,我们可以设置一个Service,代码如下:
webapp-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: webapp1
spec:
ports:
- port: 8081
targetPort: 8080
selector:
app: webapp
[root@bogon ~]# kubectl create -f webapp-svc.yaml
service/webapp1 created
[root@bogon ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
webapp1 ClusterIP 169.169.33.76 <none> 8081/TCP 5s
[root@server01 ~]# curl 169.169.33.76:8081
Service定义中的关键字段是ports和selector。
本例中ports定义部分指定了Service所需的虚拟端口号为8081,由于与Pod容器端口号8080不一样,所以需要再通过targetPort来指定后端Pod的端口号。
selector定义部分 设置的是后端Pod所拥有的label:app=webapp。
负载分发策略
目前Kubernetes提供了两种负载分发策略:RoundRobin和SessionAffinity,具体说明如下:
- RoundRobin:轮询模式,即轮询将请求转发到后端的各个Pod上。
- SessionAffinity:基于客户端IP地址进行会话保持的模式。
即第1次将某个客户端发起的请求转发到后端的某个Pod上,之后从相同的客户端发起的请求都将被转发到后端相同的Pod上。
在默认情况下,Kubernetes采用RoundRobin模式对客户端请求进行负载分发,但我们也可以通过设置service.spec.sessionAffinity=ClientIP来启用SessionAffinity策略。这样,同一个客户端IP发来的请求就会被转发到后端固定的某个Pod上了。
通过Service的定义,Kubernetes实现了一种分布式应用统一入口的定义和负载均衡机制。
多端口service
有时一个容器应用也可能提供多个端口的服务,那么在Service的定义中也可以相应地设置为将多个端口对应到多个应用服务。
在下面的例子中,Service设置了两个端口号,并且为每个端口号都进行了命名:
[root@bogon ~]# vim manyport.yaml
apiVersion: v1
kind: Service
metadata:
name: webapp2
spec:
ports:
- port: 8080
targetPort: 8080
name: web
- port: 8005
targetPort: 8005
name: management
selector:
app: webapp
[root@bogon ~]# kubectl create -f manyport.yaml
service/webapp2 created
[root@bogon ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
webapp2 ClusterIP 169.169.19.150 <none> 8080/TCP,8005/TCP 11s
多端口,不同协议的service
另一个例子是两个端口号使用了不同的4层协议—TCP和UDP:
[root@bogon ~]# vim utp-tcp-webapp.yaml
apiVersion: v1
kind: Service
metadata:
name: webapp3
spec:
ports:
- port: 8080
protocol: UDP
name: udp
- port: 8080
protocol: TCP
name: tcp
selector:
app: webapp
[root@bogon ~]# kubectl create -f utp-tcp-webapp.yaml
service/webapp3 created
[root@bogon ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
webapp3 ClusterIP 169.169.215.151 <none> 8080/UDP,8080/TCP 6s
外部服务service
在某些环境中,应用系统需要将一个外部数据库作为后端服务进行连接,或将另一个集群或Namespace中的服务作为服务的后端,这时可以通过创建一个无Label Selector的Service来实现:
[root@bogon ~]# vim no-lable-selector.yaml
kind: Service
apiVersion: v1
metadata:
name: my-service
spec:
ports:
- protocol: TCP
port: 80
targetPort: 80
[root@bogon ~]# kubectl create -f no-lable-selector.yaml
service/my-service created
[root@bogon ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-service ClusterIP 169.169.239.213 <none> 80/TCP 5s
通过该定义创建的是一个不带标签选择器的Service,即无法选择后端的Pod,系统不会自动创建Endpoint。
因此需要手动创建一个和该Service同名的Endpoint,用于指向实际的后端访问地址。
创建Endpoint的配置文件内容如下:
[root@bogon ~]# vim endpoint.yaml
kind: Endpoints
apiVersion: v1
metadata:
name: my-service
subsets:
- addresses:
- ip: 1.2.3.4
ports:
- port: 8080
[root@bogon ~]# kubectl create -f endpoint.yaml
endpoints/my-service created
[root@bogon ~]# kubectl get ep
NAME ENDPOINTS AGE
my-service 1.2.3.4:8080 12s
webapp 172.17.0.2:8080,172.17.0.3:8080 40m
访问没有标签选择器的Service和带有标签选择器的Service一样,请求将会被路由到由用户手动定义的后端Endpoint上。
更多推荐
所有评论(0)