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

posted on 2021-12-06 08:46  jueyuanfengsheng  阅读(645)  评论(0编辑  收藏  举报