此文为学习《Kubernetes权威指南》和阿里云原生公开课的相关笔记
学习笔记:
Pod作为K8s中调度和使用的基本单位,承担了在集群中建立容器、部署基本功能的任务,而RC/RS/Delpoymet/DaemonSet/Job/StatefulSet等类型的资源对象担当控制器,负责Pod的创建和生命周期的管理,上述的这些工作使得一个K8s集群能够有序的被部署进行设计者想要安排的任务,然而一个集群的最终目的应当是面向用户提供服务,包装容器功能为用户提供服务入口这项任务是由Service这一资源对象来完成的。
Service解决的问题有:
-
K8s应用之间互相调用较为困难,Pod生命周期较短、IP地址变化快。
-
单单是副本控制器管理下的Pod群无法统一对外提供接口、无法实现负载均衡。
K8s中的每一个Service与微服务架构中的一个微服务所对应,每个Service定义了一个服务的访问入口地址,外部网络或者前端的应用(其他Pod)通过这个入口访问K8s集群中一组由Pod副本组成的集群实例,而Service与其后端的Pod副本则通过Label Selector来实现无缝对接,从这个视角看,Service才是K8s的真正核心,Pod的建立是为了集群具备对外提供某一Service的能力,而副本控制器则是保证Service的服务能力和服务质量始终符合预期的标准。
最终,系统将由多个提供不同业务能力而又彼此独立的微服务单元组成,服务之间通过TCP/IP进行通信,从而形成强大的分布式能力、弹性扩展能力、容错能力,程序架构也变得简单和直观。
画了一张逻辑关系图:
下面通过几个实例学习Service的基本用法:
一、通过Service实现服务提供和自动负载
1、创建一个提供Web服务的Deployment
该Deployment包含两个Tomcat容器副本,通过containerPort开放8080端口
apiVersion: apps/v1
kind: Deployment
metadata:
name: webapp
spec:
replicas: 2
selector:
matchLabels:
app: webapp
template:
metadata:
name: webapp
labels:
app: webapp
spec:
containers:
- name: webapp
image: tomcat
ports:
- containerPort: 8080
2、创建并查看部署情况
# kubectl create -f webapp-deployment.yaml
deployment.apps/webapp created# kubectl get pods
NAME READY STATUS RESTARTS AGE
webapp-57ff49ddcf-kms92 1/1 Running 0 41s
webapp-57ff49ddcf-r9p26 1/1 Running 0 41s
通过下面的命令可以到具有指定label的Pod对应的PodIP
kubectl get pods -l <label-key>=<label-value> -o yaml | grep <field-name>
# kubectl get pods -l app=webapp -o yaml |grep podIP:
podIP: 10.44.0.1
podIP: 10.44.0.2
可以通过PodIP:containerPort的方式访问到容器应用提供的服务
# curl 10.44.0.1:8080
<!DOCTYPE html>
<html lang="en">
<head>......
# curl 10.44.0.2:8080
<!DOCTYPE html>
<html lang="en">
<head>......
3、部署Service对外提供服务入口
在不提供spec.type时,默认值为ClusterIP,Service有自己的簇地址,可以用于内部Pod访问。
在Node上的kube-proxy通过设置的iptables规则进行转发。
# vim webapp-service.yaml
apiVersion: v1
kind: Service
metadata:
name: webapp
spec: #开放8081端口,映射到后台容器的8080端口,即web服务提供端口
ports:
- port: 8081
targetPort: 8080
# type: ClusterIp #默认就是ClusterIp域
selector:
app: webapp
# kubectl create -f webapp-service.yaml
service/webapp created
通过kubectl get svc命令可以看到系统为其自动创建的ClusterIP
当spec.type=ClusterIP,可以设置spec.clusterIP指定该Service的ClusterIP,不再由K8s生成
# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
......
webapp ClusterIP 10.97.130.15 <none> 8081/TCP 10s......
4、访问Service的ClusterIP:8081,可以看到成功访问到了web服务
# curl 10.97.130.15:8081
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />......
在这个过程中,Service通过Label Selector找到其后台的Pod副本列表,自动负载分发到两个Pod之一,实现了服务的统一接口
K8s提供了两种负载分发策略:
- RoundRobin:轮询模式,即轮询将请求转发到后端的各个Pod副本上,默认是该模式。
- SessionAffinity:基于客户端IP地址进行会话保持的模式,同一个客户端发来的请求由同一个Pod处理。
5、以NodePort模式建立Service
指定spec.tpye为NodePort,将使用宿主机的端口,使能够访问各Node的外部客户端通过Node的Ip地址和端口号就能访问服务
K8s限制使用宿主机的端口号范围为30000-32767
apiVersion: v1
kind: Service
metadata:
name: webapp-nodeport
spec:
ports:
- port: 8081
targetPort: 8080
nodePort: 31234 #30000-32767
type: NodePort
selector:
app: webapp
创建该Service
# kubectl create -f webapp-service-nodeport.yaml
service/webapp-nodeport created
可以看到,在NodePort模式下,K8s同样会给Service提供一个ClusterIP用于内部Pod的访问
# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
......
webapp-nodeport NodePort 10.108.207.69 <none> 8081:31234/TCP 12s......
这时,我们即可以通过ClusterIP从内部访问web服务,也可以通过宿主机端口从外部访问
# curl 10.108.207.69:8081
<!DOCTYPE html>
<html lang="en">
<head>......
# curl 127.0.0.1:31234
<!DOCTYPE html>
<html lang="en">
<head>......
二、多端口Service的实现
一个容器在某些情况下可能提供多个端口的服务,一个Service出于某些原因也可能提供多个端口映射到同一个Pod端口,于此同时,Service也可以对外提供不同四层协议(TCP和UDP)的访问方式。
1、开放两个端口的Service
apiVersion: v1
kind: Service
metadata:
name: webapp
spec:
ports:
- port: 8081
targetPort: 8081
name: port1
- port: 8082
targetPort: 8082
name: port2
type: ClusterIp
selector:
app: webapp
2、端口使用不同协议的Service
apiVersion: v1
kind: Service
metadata:
name: webapp
spec:
ports:
- port: 8081
targetPort: 8080
protocal: TCP
- port: 8081
targetPort: 8081
protocal: UDP
type: ClusterIP
selector:
app: webapp
三、外部服务Service的定义
在某些环境中,应用系统需要将一个外部数据库作为后端服务进行连接,或者将另一个集群或Namespace中的服务作为服务的后端,这时可以通过创建一个无Label Selector的Service实现,成为外部服务Service。
1、创建一个不带有Label Selector的Service
apiVersion: v1
kind: Service
metadata:
name: external-svc
spec:
type: ClusterIP
ports:
- name: web
port: 80
protocol: TCP
targetPort: 80
2、创建一个与之同名的Endpoint
apiVersion: v1
kind: Endpoints
metadata:
name: external-svc
subsets:
- addresses:
- ip: 1.2.3.4 #用于指向实际的后端访问地址
ports:
- port: 80 #后端访问的端口
访问没有标签选择器的Service和带有标签选择器的Service一样,请求将会被路由到用户手动定义的后端Endpoint上
# kubectl get endpoints
NAME ENDPOINTS AGE
external-svc 1.2.3.4:80 7m10s
......
# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
external-svc ClusterIP 10.111.62.195 <none> 80/TCP 2m4s
......