{"msg":"操作成功","code":200,"data":{"createBy":"admin","createTime":"2021-08-21 17:50:16","updateBy":"admin","updateTime":"2021-08-21 17:50:16","remark":null,"id":69,"articleTitle":"Kubernetes（七）Service","articleUrl":"k8s_service","articleThumbnail":"https://www.asumimoe.com/imgfiles/20220906/f93daad129a04b8db74eed70cd45263b.png","articleFlag":"0","draftStatus":"1","reprintStatement":"1","articleSummary":"Kubernetes Service 从逻辑上代表了一组 Pod，具体是哪些 Pod 则是由 label 来挑选。Service 有自己  IP，而且这个 IP 是不变的。客户端只需要访问 Service 的 IP，Kubernetes 则负责建立和维护 Service 与 Pod  的映射关系。无论后端 Pod 如何变化，对客户端不会有任何影响，因为 Service 没有变。","articleContent":"## Service介绍\n\n每个 Pod 都有自己的 IP 地址。当 controller 用新 Pod 替代发生故障的 Pod 时，新 Pod 会分配到新的 IP 地址。这样就产生了一个问题：\n\n如果一组 Pod 对外提供服务（比如 HTTP），它们的 IP 很有可能发生变化，那么客户端如何找到并访问这个服务呢？\n\nKubernetes 给出的解决方案是 Service。\n\nKubernetes Service 从逻辑上代表了一组 Pod，具体是哪些 Pod 则是由 label 来挑选。Service 有自己  IP，而且这个 IP 是不变的。客户端只需要访问 Service 的 IP，Kubernetes 则负责建立和维护 Service 与 Pod  的映射关系。无论后端 Pod 如何变化，对客户端不会有任何影响，因为 Service 没有变。\n\n## Service使用\n\n### 1.创建一组Pod\n\n定义一个提供Web服务的Deployment，由两个nginx副本组成，每个容器都通过containerPort设置提供服务的端口为80。\n\n```yaml\n# nginx.yml\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: mynginx\nspec:\n  replicas: 2\n  selector:\n    matchLabels:\n      app: nginx\n  template:\n    metadata:\n      labels:\n        app: nginx\n    spec:\n      containers:\n      - name: nginx\n        image: nginx\n        ports:\n        - containerPort: 80\n```\n\n```shell\n[root@master ~]# kubectl apply -f nginx.yml \n[root@master ~]# kubectl get po -o wide\nNAME                       READY   STATUS    RESTARTS   AGE   IP            NODE    NOMINATED NODE   READINESS GATES\nmynginx-7848d4b86f-hpt6m   1/1     Running   0          66s   10.244.2.9    node1   <none>           <none>\nmynginx-7848d4b86f-jcw7q   1/1     Running   0          66s   10.244.1.11   node2   <none>           <none>\n```\n\n根据这两个Pod的IP来访问nginx的服务。\n\n```shell\n[root@master ~]# curl 10.244.2.9\n<!DOCTYPE html>\n<html>\n<head>\n<title>Welcome to nginx!</title>\n<style>\n......\n</body>\n</html>\n\n[root@master ~]# curl 10.244.1.11\n<!DOCTYPE html>\n<html>\n<head>\n<title>Welcome to nginx!</title>\n<style>\n......\n</body>\n</html>\n```\n\n但是在K8s集群中，利用Pod的IP访问服务的方式是十分不可靠的，当其中一个节点发生故障，Pod被调度到其他节点是上时，它的IP就会发生变化。而且如果容器本身是分布式部署的，就需要一个负载均衡器来实现请求分发。Service就可以解决这个问题。\n\n### 2.创建Service\n\n1）命令行直接暴露端口形式，可以看到系统为Service分配了一个虚拟的IP地址，Service所需要的端口号则从Pod中的containerPort复制而来。\n\n```shell\n[root@master ~]# kubectl expose deploy mynginx\nservice/mynginx exposed\n[root@master ~]# kubectl get svc\nNAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE\nmynginx      ClusterIP   10.105.240.121   <none>        80/TCP    20s\n# 我们只需访问http://10.105.240.121就可以访问到我们部署的nginx服务\n#也可以在命令行中指定端口\nkubectl expose deploy mynginx --port=82 --target-port=80\n```\n\n2）我们同样可以使用yaml文件的方式创建一个svc。\n\n```yaml\napiVersion: v1\nkind: Service\nmetadata:\n  name: nginx-svc\nspec:\n  selector:\n    app: nginx # 具有此标签的Pod可由此service访问\n  ports:\n  - port: 81  #svc的port\n    targetPort: 80  #是pod的port\n```\n\n```shell\n[root@master ~]# kubectl get svc\nNAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE\nnginx-svc    ClusterIP   10.103.218.218   <none>        81/TCP    7s\n[root@master ~]# curl 10.103.218.218:81\n<!DOCTYPE html>\n<html>\n<head>\n<title>Welcome to nginx!</title>\n......\n```\n\nKubernetes提供了两种负载分发策略：\n\n- RoundRobin：轮询模式，即轮询将请求发送到后端各个Pod上。\n- SessionAffinity：基于客户端的IP地址进行会话保持的模式，来自相同客户端的请求都发送到同一Pod中。\n\n默认使用轮询方式，也可以设置`service.spec.sessionAffinity=ClientIP`来启用会话保持策略。\n\n### 3.DNS方式访问Service\n\n在集群中的Pod中，我们可以通过DNS的方式来访问某个Service。命名方式为`<SERVICE_NAME>.<NAMESPACE_NAME>.svc`。\n\n```shell\n[root@master ~]# kubectl exec mynginx-7848d4b86f-fwcn4 -it -- /bin/bash\nroot@mynginx-7848d4b86f-fwcn4:/# curl 10.96.73.67:81\n<!DOCTYPE html>\n<html>\n<head>\n<title>Welcome to nginx!</title>\n\nroot@mynginx-7848d4b86f-fwcn4:/# curl nginx-svc.default:81\n<!DOCTYPE html>\n<html>\n<head>\n<title>Welcome to nginx!</title>\n\nroot@mynginx-7848d4b86f-fwcn4:/# curl nginx-svc.default.svc:81\n<!DOCTYPE html>\n<html>\n<head>\n<title>Welcome to nginx!</title>\n# 在容器内部，这三种方式都可以访问到Service\n```\n\n## Service运行方式\n\n### 1.ClusterIP\n\n默认类型，每个Node分配一个集群内部的Ip，内部可以互相访问，外部无法访问集群内部。上述例子中我们创建的Service就是ClusterIP类型的。只能在集群内部访问及集群中的Pod中访问。\n\n### 2.NodePort\n\n基于ClusterIP，另外在每个Node上开放一个端口，可以从所有位置访问这个地址。\n\n```shell\nkubectl expose deploy mynginx --port=83 --target-port=80 --type=NodePort\n# 不加type选项就是默认的ClusterIP模式\n\n[root@master ~]# kubectl get svc\nNAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE\nmynginx      NodePort    10.96.180.111   <none>        83:30821/TCP   9s\n```\n\n在集群外部我们就可以通过192.168.52.211:30821来访问这个Service。\n\n```yaml\napiVersion: v1\nkind: Service\nmetadata:\n  name: nginx-svc\nspec:\n  type: NodePort # 指定Service类型\n  selector:\n    app: nginx\n  ports:\n  - port: 84\n    targetPort: 80\n    nodePort: 30001 # NodePort生成的端口范围是30000-32767，如果我们不指定就会随机生成一个在此范围内的端口\n    protocol: TCP\n```\n\n```shell\n[root@master ~]# kubectl get svc\nNAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE\nmynginx      NodePort    10.96.180.111   <none>        83:30821/TCP   6m5s\nnginx-svc    NodePort    10.96.73.67     <none>        84:30001/TCP   38m\n```\n\n### 3.LoadBalancer\n\n分配一个内部集群IP地址，并在每个节点上启用一个端口来暴露服务。 除此之外，Kubernetes会请求底层云平台上的负载均衡器，将每个Node（[NodeIP]:[NodePort]）作为后端添加进去。\n\n### 4.ExternalName\n\n类型为 ExternalName 的service将服务映射到 DNS 名称，而不是典型的选择器，例如`my-service`或者`cassandra`。 可以使用`spec.externalName`参数指定这些服务。\n\n```yaml\nkind: Service\napiVersion: v1\nmetadata:\n  name: service-python\nspec:\n  ports:\n  - port: 3000\n    protocol: TCP\n    targetPort: 443\n  type: ExternalName\n  externalName: remote.server.url.com\n# ExternalName service 也可以用于从其他名称空间访问服务\nspec:\n  type: ExternalName\n  externalName: service-2.namespace-b.svc.cluster.local\n```\n\n## 外部服务映射\n\nService中的ExternalName为我们提供了CNAME访问集群外部的方式。如果我们仅有外部集群的IP加端口。比如我们想访问部署在集群外部的MySQL服务。就可以通过EndPoint的方式。\n\n```yaml\napiVersion: v1\nkind: Service\nmetadata:\n  name: mysql-a\nspec:\n  ports:\n  - port: 3309\n    protocol: TCP\n---\napiVersion: v1\nkind: Endpoints\nmetadata:\n  name: mysql-a # 此名称需要与Service的名称相同\nsubsets:\n  - address: \n    - 155.155.155.155\n    ports:\n      - port: 3306\n```\n\n这样我们通过访问IP:3309即可路由到115.115.115.115:3306。即实现了访问外部集群。这样当外部服务的地址修改后我们直接修改Endpoint中的IP就可以继续访问了。\n\n如果我们以ConfigMap的方式将配置挂载到Pod中，那么我们每修改一次configMap就要重启Pod，显然不如自定义外部服务的方式方便。\n\n## Headless Service\n\n在某些应用场景中，开发人员希望自己控制负载均衡策略，不使用Service提供的默认负载均衡功能。或者应用程序希望知道属于同组服务的其他实例。Kubernetes提供了Headless Service实现这种功能，即不为Service设置ClusterIP，仅通过Label Selector将后端的Pod列表返回给调用的客户端。\n\n```yaml\napiVersion: v1\nkind: Service\nmetadata:\n  name: nginx-hlsvc\nspec:\n  selector:\n    app: nginx\n  ports:\n  - port: 90\n    targetPort: 80\n    protocol: TCP\n  clusterIP: None\n```\n\n查看此服务的EndPoint：\n\n```shell\n[root@master ~]# kubectl describe svc nginx-hlsvc\nName:              nginx-hlsvc\nNamespace:         default\nLabels:            <none>\nAnnotations:       <none>\nSelector:          app=nginx\nType:              ClusterIP\nIP Families:       <none>\nIP:                None\nIPs:               None\nPort:              <unset>  90/TCP\nTargetPort:        80/TCP\nEndpoints:         10.244.1.2:80,10.244.1.3:80 # 返回的地址为PodIP:port\nSession Affinity:  None\nEvents:            <none>\n```\n\n对于去中心化的集群，Headless将非常有用。我们之后部署redis三主三从集群就会用到它。","categoryId":10,"viewCount":981,"categoryName":"Kubernetes","author":"球接子","authorAvatar":null,"tagIds":[16],"tagNames":["Kubernetes"]}}