03-K8S之service工作方式及使用
目录
service
1.service工作模式
1.userspace k8s1.1-
2.iptables k8s1.8-
3.ipvs k8s1.8+
kubernetes中的3类IP地址
node network(节点网络):配置在节点的接口之上
pod network(pod 网络):配置在pod资源之上
service network(cluster network):virtual IP,没有配置在某个接口当中,只是出现在service的规则当中。
每个节点之上都有一个kube-proxy组件,它会始终监视(通过watch请求方法实现)着apiserver当中有关service资源的变动信息,当有service资源内容发生变动(创建),kube-proxy组件都将把它转化为当前节点之上的能够实现service资源调度(包括将用户请求调度到后端pod之上的规则,规则是iptables或者ipvs,取决于service的实现方式,)
2.kubernetes的3种service实现模型
1.userspace
1. userspace: Client Pod(客户端)请求到达service以后,先把service转化为监听在某个套接字上的用户空间的kube-proxy,由它负责处理,之后再转给Service IP,service IP再转给其代理的Pod。
对于客户端的请求,经过Service IP,还要回到监听在某个端口上的kube-proxy,由kube-proxy再进行分发,而kube-proxy是工作在用户空间的进程,这种方式就叫做userspace。但是工作效率低,其需要经过内核转给用户空间的kube-proxy进行报文封装,再次回到内核由iptables规则进行分发。
2.iptables
iptables:Client Pod(客户端)请求直接由内核iptables规则所截取,直接调度到相关联的pod。
3.ipvs
ipvs:Client Pod(客户端)请求直接由内核ipvs规则所截取,直接调度到相关联的pod。
3.service使用iptables和ipvs规则实现的区别
参考文章:https://blog.csdn.net/qq_36807862/article/details/106068871
4.sevcie常用字段
kubectl explain svc.spec
selector <map[string]string> # 选择关联的pod的标签
ports <[]Object> # 定义service监听端口(port),暴露外部可以访问的端口(nodePort,前提type:NodePort),以及目标端口(targetPort,)
loadBalancerIP <string>
clusterIP <string> # 手动指定集群内部访问IP,不指定此字段,默认自动随机分配IP
type <string> # 指定集群访问类型
sessionAffinity # 可以设置成ClientIP,把来至同一客户端的请求调度到同一个pod中。默认是none不使用;
kubectl explain svc.spec.type
# svc类型
ExternalName:把集群外部的服务引入到集群内部使用
ClusterIP: 创建service时,指定集群内部通信ClusterIP。
NodePort:接入集群外部的流量,使用NodePort
LoadBalancer:把kubernetes部署在虚拟机上,虚拟机部署在云环境中,而云环境支持LBAS(负载均衡及服务,)的一键调用,自动在外部创建一个软负载均衡器,创建软负载均衡器时使用。
kubectl explain svc.spec.ports
ports <[]Object>
name <string> # 端口名称
port <integer> -required- # service对外提供服务的端口
nodePort <integer> # type=NodePort,LoadBalancer,才能定义nodePort;service将在每个节点上暴露此端口,可以在集群外部,通过集群任意节点IP:nodePort访问此service关联的一组后端pod服务。
protocol <string> # 不指定的话,默认是TCP
targetPort <string> # 容器端口
1.定义为ClusterIP时使用的ports下使用字段:
port: # service上的端口,k8s集群内部服务之间访问service的入口,要与镜像暴露的端口一致。
targetPort: #可以不写, podIP上的端口 可以是端口名称,也可以是端口;如果是端口名称,要和pod中容器定义的port端口名称一致。
2.定义为NodePort时使用的字段:
type: NodePort
ports:
- port: # 要与镜像暴露的端口一致
nodePort: # service对外暴露的端口
3.loadbalancerIP:公有云主机底层支持创建LoadbalacerIP # 下图1
type: LoadBalancer
loadBalancerIP: 52.130.86.47
ports:
- port: 80
selector:
app:nginx
4. ExternalName:集群内pod访问一个服务,此服务在集群之外,又想让pod访问到这个服务,如何做?pod网络都是内网地址,即使请求能路由出去,外部的响应报文也无法回来。
spec:
type: ExternalName # 下图2
externalName: my.database.example.com # cname必须能被DNS服务解析
图1:
图2:
5.nodePort、port、targetPort、hostPort解释
1.nodePort
外部流量访问k8s集群中service入口的一种方式(另一种方式是LoadBalancer),即nodeIP:nodePort是提供给外部流量访问k8s集群中service的入口。比如外部用户要访问k8s集群中的一个Web应用,那么我们可以配置对应service的type=NodePort,nodePort=30001。其他用户就可以通过浏览器http://node:30001访问到该web服务。而数据库等服务可能不需要被外界访问,只需被内部服务访问即可,那么我们就不必设置service的NodePort。
apiVersion: v1
kind: Service
metadata:
name: myapp
namespace: default
spec:
selector:
app: myapp
release: canary
type: NodePort
ports:
- port: 80 # myapp svc 监听端口
targetPort: http # pod端口,这个名称必须是在containers.ports中有定义(containers.ports.name)
#targetPort: 80
nodePort: 30080
2.Port
k8s集群内部服务之间访问service的入口。即clusterIP:port是service暴露在clusterIP上的端口。mysql容器暴露了3306端口,集群内其他容器通过33306端口访问mysql服务,但是外部流量不能访问mysql服务,因为mysql服务没有配置NodePort。对应的service.yaml如下:
apiVersion: v1
kind: Service
metadata:
name: mysql-service
namespace: default
spec:
selector:
name: mysql-pod
ports:
- port: 33306
targetPort: 3306
3.targetPort
容器的端口(最终的流量端口)。targetPort是pod上的端口,从port和nodePort上来的流量,经过kube-proxy流入到后端pod的targetPort上,最后进入容器。
制作容器时暴露的端口一致(使用DockerFile中的EXPOSE),例如官方的nginx(参考DockerFile)暴露80端口。 对应的service.yaml如下:
apiVersion: v1
kind: Service
metadata:
name: nginx-service
namespace: default
spec:
selector:
name: nginx-pod
type: NodePort # 有配置NodePort,外部流量可访问k8s中的服务
ports:
- port: 30080 # 服务访问端口(service监听端口)
targetPort: 80 # 容器端口
nodePort: 30001 # NodePort 如果不给值,docker会随机给默认值
4.hostport
hostPort这是一种直接定义Pod网络的方式。hostPort是直接将容器的端口与所调度的节点上的端口路由,这样用户就可以通过宿主机的IP加上来访问Pod了,如:
apiVersion: v1
kind: Pod
metadata:
name: influxdb
spec:
containers:
- name: influxdb
image: influxdb
ports:
- containerPort: 8086
hostPort: 8086
5.总结
总的来说,port和nodePort都是service的端口,前者暴露给k8s集群内部服务访问,后者暴露给k8s集群外部流量访问。从上两个端口过来的数据都需要经过反向代理kube-proxy,流入后端pod的targetPort上,最后到达pod内的容器。
6.创建redis service示例
1.创建ClusterIP类型的service:
vim redis-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: redis
namespace: default
spec:
selector:
app: redis
role: logstor
# 不指定service类型,默认创建在集群内部使用的service,不手动指定ClusterIP,会随机分配一个10.96.0.0/12网络的IP,如果手动指定,一定要确认这个IP没被分配,不和原有的service冲突。
clusterIP: 10.97.97.97 # 一旦被创建成功,无法再次apply修改。除非删除svc重建。
type: ClusterIP
ports:
- port: 6379
# targetPort: redis #
targetPort: 6379
redis deployment:
[root@master01 service]# cat ../controll/pod-daemonset-controll.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: redis
role: logstor
template:
metadata:
labels:
app: redis
role: logstor
spec:
containers:
- name: redis
image: redis:6.0.3
ports:
- name: redis
containerPort: 6379
当一个service创建完成以后,会自动在集群的DNS中动态添加一个A记录,我们可以通过这个服务名进行解析,格式为:
SVC_NAME>NS_NAME_DOMAIN.LTD.
集群默认的域名后缀:
svc.cluster.local.
redis.default.svc.cluster.local. # 对于redis service的域名
2.创建NodePort类型的service:
apiVersion: v1
kind: Service
metadata:
name: myapp
namespace: default
spec:
selector:
app: myapp
release: canary
# 不指定service类型,默认创建在集群内部使用的service,不手动指定ClusterIP,会随机分配一个10.96.0.0/12网络的IP,如果手动指定,一定要确认这个IP没被分配,不和原有的service冲突。
clusterIP: 10.99.99.99 # 自定义集群内部service IP
type: NodePort
ports:
- port: 80 # myapp svc 监听端口
targetPort: http # pod端口,这个名称必须是在containers.ports中有定义(containers.ports.name)
#targetPort: 80
nodePort: 30080
myapp deployment:
[root@master01 service]# cat ../controll/pod-deploy-controll.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-deploy-controll
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: myapp
release: canary
template:
metadata:
name: myapp-deploy-controll
labels:
app: myapp
release: canary
spec:
containers:
- name: myapp
image: nginx:1.20.2
ports:
- name: http
containerPort: 80
7.使用ipvs模型的配置补充
编辑kubelet配置文件/etc/sysconfig/kubelet,设置其忽略Swap启用的状态错误,内容如下:
KUBE_EXTRA_ARGS="--fail-swap-on=false"
KUBE_PROXY_MODE=ipvs
# 安装ipvs调度算法相关模块
yum intall ip_vs,ip_vs_rr,ip_vs_wrr,ip_vs_sh,nf_conntrack_ipv45
HeadLess Service
有时不需要或不想要负载均衡,以及单独的 Service IP。 遇到这种情况,可以通过指定 Cluster IP(spec.clusterIP)的值为 "None" 来创建 Headless Service。您可以使用headless Service 与其他服务发现机制进行接口,而不必与 Kubernetes 的实现捆绑在一起。典型应用就是Ingress。
vim headless-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: headless-svc
namespace: default
spec:
selector:
app: myapp
release: canary
clusterIP: None
ports:
- port: 80
targetPort: 80
kubectl apply -f headless-svc.yaml
kubectl get svc -o wide
dig -t A headless-svc.default.svc.cluster.local. @10.96.0.10
dig -t A myapp.default.svc.cluster.local. @10.96.0.10