概述

Service为一组功能相同的pod提供统一入口并为它们提供负载均衡和自动服务发现。Service所针对的一组Pod,通常是由Selector来确定。
为什么需要Service?
直接接通过pod的ip加端口去访问应用是不稳定的,因为pod的生命周期是不断变化的,每次触发了删除的动作后Pod会重新创建,那么Pod的IP地址就会产生变化。

service类型

Service的转发后端的三种方式

  • Clusterip:通过集群的内部IP的创建,实现服务在集群内的访问。j这也是默认的 ServiceType。
  • Nodeport:通过每个 Node 上的 IP 和静态端口(NodePort)暴露服务。通过请求 <NodeIP>:<NodePort>,可以从集群的外部访问一个 Kubernetes服务。
  • Loadblance:使用云提供商的负载局衡器,可以向外部暴露服务。外部的负载均衡器可以路由到Service然后到应用。
  • Externalname:依赖于DNS,用于将集群外部的服务通过域名的方式映射到Kubernetes集群内部,通过Service的name进行访问。

Service的三种端口类型:

  • Port:Service对内暴露的端口
  • Targetport:POD暴露的端口
  • Nodeport:Service对外暴露的端口

集群外访问Service的方式:

  • Nodeport
  • Loadblance
  • ExternalIP

clusterIP示例

  1. apiVersion: v1
  2. kind: Service
  3. metadata:
  4. name: my-service
  5. spec:
  6. selector:
  7. app: MyApp # service selector的label要和pod的label对应,才会生效
  8. ports:
  9. - protocol: TCP
  10. port: 8080
  11. targetPort: 80
  1. # 还可以在yml文件里不用selector,用service和endpoint,写两个yml去使用service
  2. apiVersion: v1
  3. kind: Service
  4. metadata:
  5. name: my-service # service的名字要和endpoint的名字对应
  6. spec:
  7. ports:
  8. - protocol: TCP
  9. port: 80
  10. targetPort: 9376
  11. ------------------------
  12. # 因为此Service没有选择器,则不会创建对应的Endpoints对象,可以手工将服务映射至指定的endpoints中:
  13. apiVersion: v1
  14. kind: Endpoints
  15. metadata:
  16. name: my-service
  17. subsets:
  18. - addresses:
  19. - ip: 10.42.4.42 # 指定主机,分发流量,走iptable啥的
  20. ports:
  21. - port: 9376
  1. [rancher@master1 test]$ kubectl get endpoints
  2. NAME ENDPOINTS AGE
  3. kubernetes 172.24.102.254:6443,172.24.102.255:6443,172.24.103. 1:6443 41h
  4. my-service 10.42.3.28:80,10.42.4.39:80,10.42.4. 40:80 31s
  5. [rancher@master1 test]$ kubectl apply -f service_test.yml
  6. service/my-service created
  7. [rancher@master1 test]$ kubectl get service
  8. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  9. kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 41h
  10. my-service ClusterIP 10.43.12.238 <none> 8080/TCP 3s
  11. # 命令行实现(最好不用)
  12. [rancher@master1 ~]$ kubectl run --image=nginx test2
  13. kubectl run --generator=deployment/apps.v1 is DEPRECATED and will be removed in a future version. Use kubectl run --generator=run-pod/v1 or kubectl create instead.
  14. deployment.apps/test2 created
  15. [rancher@master1 ~]$ kubectl expose deployment test2 --port=18080 --target-port=80
  16. service/test2 exposed

NodePort

  1. apiVersion: v1
  2. kind: Service
  3. metadata:
  4. name: my-service
  5. spec:
  6. selector:
  7. app: MyApp # service selector的label要和pod的label对应,才会生效
  8. ports:
  9. - protocol: TCP
  10. nodePort:
  11. port: 8080
  12. targetPort: 80
  13. sessionAffinity: None
  14. type: NodePort
  15. status:
  16. loadBalancer: {}
  1. # 直接用命令行
  2. [rancher@master1 test]$ kubectl expose deployment test --port=18080 --target-port=80 --type=NodePort

Headless Service

在有些场景下,服务可能不需要作为负载均衡代理,而仅仅需要一个单一的cluster ip。这时就可以通过设置“spec.clusterIP”的值为None,来创建一个”headless“类型的服务。此类型的服务允许开发者减少对Kubernetes系统的依赖,开发者可以通过自己的方式实现对服务的自动发现。应用也能够使用其他的服务发现系统,进行服务的自注册和适配器,实现服务的自动发现。对于这样的服务:

  • Kubernetes未指派cluster IP;
  • kube-proxy将不处理这些服务;
  • 因此也就没有负载均衡和代理。
  • 但会依赖服务是否拥有选择器进行DNS的配置。
  1. 创建statusful
    • 典型的无头服务,而且这种通过dns解析服务,只能用于statulful,每个节点只要一个pod副本,ip不会变,所以可以这样理解,就是通过解析这个无头service找到对应的poo的IP ```yml apiVersion: v1 kind: Service metadata: name: nginx labels: app: nginx spec: ports:
    • port: 80 name: web clusterIP: None selector: app: nginx

apiVersion: apps/v1 kind: StatefulSet metadata: name: web spec: selector: matchLabels: app: nginx # has to match .spec.template.metadata.labels serviceName: “nginx” replicas: 3 # by default is 1 template: metadata: labels: app: nginx # has to match .spec.selector.matchLabels spec: terminationGracePeriodSeconds: 10 containers:

  1. - name: nginx
  2. image: nginx
  3. ports:
  4. - containerPort: 80
  5. name: web
  1. 2. 通过dns去解析statusful```yml
  2. # 创建busybox
  3. apiVersion: v1
  4. kind: Pod
  5. metadata:
  6. name: busybox
  7. spec:
  8. containers:
  9. - image: busybox:1.28
  10. command:
  11. - sleep
  12. - "3600"
  13. imagePullPolicy: IfNotPresent
  14. name: busybox
  1. [rancher@master1 test]$ kubectl create -f dns.yml
  2. # 查看无头service
  3. [rancher@master1 test]$ kubectl get service
  4. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  5. kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 46h
  6. nginx ClusterIP None <none> 80/TCP 21m
  7. # 解析
  8. [rancher@master1 test]$ kubectl exec -it busybox -- nslookup nginx
  9. Server: 10.43.0.10
  10. Address 1: 10.43.0.10 kube-dns.kube-system.svc.cluster.local
  11. Name: nginx
  12. Address 1: 10.42.4.44 web-2.nginx.default.svc.cluster.local
  13. Address 2: 10.42.3.32 web-1.nginx.default.svc.cluster.local
  14. Address 3: 10.42.4.43 web-0.nginx.default.svc.cluster.local

externalname

  1. apiVersion: v1
  2. kind: Service
  3. metadata:
  4. name: my-service
  5. spec:
  6. type: ExternalName
  7. externalName: www.baidu.com
  1. # 通过externalName可以对my-service在集群内部做一个解析
  2. [rancher@master1 test]$ kubectl exec -it busybox nslookup my-service
  3. Server: 10.43.0.10
  4. Address 1: 10.43.0.10 kube-dns.kube-system.svc.cluster.local
  5. Name: my-service
  6. Address 1: 240e:83:205:58:0:ff:b09f:36bf
  7. Address 2: 240e:83:205:59:0:ff:b09b:159e
  8. Address 3: 220.181.38.149
  9. Address 4: 220.181.38.150
  10. [rancher@master1 test]$ kubectl exec -it busybox nslookup www.baidu.com
  11. Server: 10.43.0.10
  12. Address 1: 10.43.0.10 kube-dns.kube-system.svc.cluster.local
  13. Name: www.baidu.com
  14. Address 1: 240e:83:205:59:0:ff:b09b:159e
  15. Address 2: 240e:83:205:58:0:ff:b09f:36bf
  16. Address 3: 220.181.38.150
  17. Address 4: 220.181.38.149
  18. ****

external ip

  1. apiVersion: v1
  2. kind: Service
  3. metadata:
  4. name: my-service
  5. spec:
  6. selector:
  7. app: nginx
  8. ports:
  9. - name: http
  10. protocol: TCP
  11. port: 18080
  12. targetPort: 80
  13. externalIPs:
  14. - 172.24.102.253
  1. # 其实觉得可以理解为主机端口映射
  2. [rancher@master1 test]$ curl 172.24.102.253:18080
  3. [rancher@master1 test]$ kubectl get service
  4. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  5. kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 47h
  6. my-service ClusterIP 10.43.91.49 172.24.102.253 18080/TCP 55s

ingress

ingress组成:

Service - 图1

ingress-controller本身是一个是一个pod,这个pod里面的容器安装了反向代理软件,通过读取添加的Service,动态生成负载均衡器的反向代理配置,你添加对应的ingress服务后,里面规则包含了对应的规则,里面有域名和对应的Service-backend。

NGINX Ingress Controller是目前使用最多也是评分最高的Ingress控制器:

  • 基于http-header 的路由
  • 基于 path 的路由
  • 单个ingress 的 timeout (不影响其他ingress 的 timeout 时间设置)
  • 请求速率limit rewrite
  • 规则 ssl

详细步骤(示例)

  1. 先创建两个服务,并暴露端口80```bash [rancher@master1 ~]$ kubectl run —image=nginx test [rancher@master1 ~]$ kubectl run —image=nginx demo

[rancher@master1 ~]$ kubectl expose deployment test —port=80 [rancher@master1 ~]$ kubectl expose deployment demo —port=80

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE demo ClusterIP 10.43.91.189 80/TCP 19m test ClusterIP 10.43.124.172 80/TCP 19m kubernetes ClusterIP 10.43.0.1 443/TCP 2d9h

  1. 2. 在/etc/hosts文件里添加域名解析```bash
  2. [rancher@master1 ~]$ cat /etc/hosts
  3. ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
  4. 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
  5. 172.24.102.255 master1
  6. 172.24.102.254 master2
  7. 172.24.103.1 master3
  8. 172.24.102.253 node1
  9. 172.24.103.0 node2 foo.bar.com
  1. 创建ingress文件```yml

    第一种,使用foo.bar.com域名访问

    apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: name: test annotations: nginx.ingress.kubernetes.io/rewrite-target: / spec: rules:
    • host: foo.bar.com # 上面使用的域名解析 http: paths:
      • path: /foo # 访问路径前缀 backend: serviceName: test servicePort: 80 # cluster端口
      • path: /bar backend: serviceName: demo servicePort: 80

第二种,使用ip地址进行访问

apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: name: test-ingress2 spec: backend: serviceName: test servicePort: 80

  1. 4. 访问
  2. ```bash
  3. # 用域名
  4. [rancher@master1 ~]$ curl foo.bar.com/foo
  5. test # 可提前更改访问内容(略)