概述

需要解决的问题:东西流量的访问
1 pod的访问地址存在动态变化,需要实现服务发现与注册(Eureka 或者nacos)
2 pod的访问策略(负载均衡)

Pod和Service关系

由于 Kubernetes 集群中每个 Pod(容器组)都有一个唯一的 IP 地址(即使是同一个 Node 上的不同 Pod),我们需要一种机制,为前端系统屏蔽后端系统的 Pod(容器组)在销毁、创建过程中所带来的 IP 地址的变化。
Service(服务) 提供了这样的一个抽象层,它选择具备某些特征的 Pod(容器组)并为它们定义一个访问方式。Service(服务)使 Pod(容器组)之间的相互依赖解耦(原本从一个 Pod 中访问另外一个 Pod,需要知道对方的 IP 地址)。
一个 Service(服务)和 Pod(容器组)建立关联的方式通过 Label和Selector(标签选择器)。

Kubernetes——Service - 图1

查看service

  1. $ kubectl get service -o wide
  2. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
  3. kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 6d21h <none

Service定义

service的通用yaml资源配置文件如下

  1. apiVersion: v1
  2. kind: Service
  3. metadata: # 元数据
  4. name: string #服务名称
  5. namespace: string # 如果不填写的话,默认为 default
  6. labels: # 自定义标签属性列表
  7. - name: string
  8. annotations: # 自定义注解属性列表
  9. - name: string
  10. spec: # 详细描述
  11. selector: [] # 标签选择器
  12. type: string # 可选有: ClusterIP(默认)/NodePort/LoadBalancer(外接负载均衡器时选择这个)
  13. clusterIP: string # 不指定的话系统自动分配 IP,当 type=LoadBalancer 时必须指定
  14. sessionAffinity: string # 是否支持 Session,默认值为空,可选值为 ClientIP,ClientIP 表示将同一个客户端的访问请求都转发到同一个后端 Pod
  15. ports: # 需要暴露的端口列表
  16. - name: string # 端口名称
  17. protocols: string # 端口协议,支持 TCP 和 UDP,默认值为 TCP
  18. port: int # 服务端口号,也就是service自己的端口
  19. targetPort: int # 需要后端Pod的端口号
  20. nodePort: int # 指定映射到Node节点的端口号
  21. status: # 当 spec.type=LoadBalancer 时,设置外部负载均衡器的地址,用于公有云环境
  22. loadBalancer:
  23. ingress: # 外部负载均衡器
  24. ip: string # 外部负载均衡器的 IP 地址
  25. hostname: string # 外部负载均衡器的主机名

eg:

  1. kind: Service
  2. apiVersion: v1
  3. metadata:
  4. name: my-service
  5. spec:
  6. selector:
  7. app: MyApp
  8. ports:
  9. - protocol: TCP
  10. port: 8080
  11. targetPort: 80

上述配置将创建一个名称为 “my-service” 的 Service 对象,它会将请求代理到使用 TCP 端口 80,并且具有标签 “app=MyApp” 的 Pod 上。这个 Service 将被指派一个 IP 地址(通常称为 “Cluster IP”)。

Type类型

1.ClusterIP

ClusterIP是 kubernetes 的默认方式,只能集群内部访问,分为普通 Service 和 Headless Service。
- 普通Service:创建的 Service 会分配一个集群内部可访问的固定虚拟 IP,这是最常使用的方式。
- Headless Service:创建的 Service 不会分配固定的虚拟 IP,同样也不会通过 kube-proxy 做反向代理和负载均衡,主要通过 DNS 提供稳定的网络 ID 进行访问,通常用于 StatefulSet 中。

2.NodePort

NodePort:拥有并且使用 ClusterIP,并且将 Service 的 port 端口映射到集群中每个 Node 节点的相同端口 port,这样在集群外部访问服务可以直接使用 nodeIP:nodePort 进行访问。

3.LoadBalancer

LoadBalancer:在 NodePort 的基础上(也就是拥有 ClusterIP 和 nodePort),还会向所处的公有云申请负载均衡器 LB(负载均衡器的后端直接映射到各 Node 节点的 nodePort 上),这样就实现了通过外部的负载均衡器访问服务。

4.ExternalName

ExternalName:这是 Service 的一种特例形式。主要用于解决运行在集群外部的服务问题,这种方式下会返回外部服务的别名来为集群内部提供服务。上述提到的 3 种模式主要依赖于 kube-proxy,而这种模式依赖于 kube-dns 的层级。

端口Port

nodePort

nodePort提供了集群外部客户端访问service的一种方式,:nodePort提供了集群外部客户端访问service的端口,即nodeIP:nodePort提供了外部流量访问k8s集群中service的入口。nodePort 的范围是 30000-32767,设置端口时不能够与已经在使用的有冲突。
比如外部用户要访问k8s集群中的一个Web应用,那么我们可以配置对应service的type=NodePort,nodePort=30001。其他用户就可以通过浏览器http://node:30001访问到该web服务。
而数据库等服务可能不需要被外界访问,只需被内部服务访问即可,那么我们就不必设置service的NodePort。

port

port是暴露在cluster ip上的端口,:port提供了集群内部客户端访问service的入口,即clusterIP:port
mysql容器暴露了3306端口(参考DockerFile),集群内其他容器通过33306端口访问mysql服务,但是外部流量不能访问mysql服务,因为mysql服务没有配置NodePort。对应的service.yaml如下:

  1. apiVersion: v1
  2. kind: Service
  3. metadata:
  4. name: mysql-service
  5. spec:
  6. ports:
  7. - port: 33306
  8. targetPort: 3306
  9. selector:
  10. name: mysql-pod

targetPort

targetPort是pod上的端口,从port/nodePort上来的数据,经过kube-proxy流入到后端pod的targetPort上,最后进入容器。
与制作容器时暴露的端口一致(使用DockerFile中的EXPOSE),例如官方的nginx(参考DockerFile)暴露80端口。 对应的service.yaml如下:

  1. apiVersion: v1
  2. kind: Service
  3. metadata:
  4. name: nginx-service
  5. spec:
  6. type: NodePort // 配置NodePort,外部流量可访问k8s中的服务
  7. ports:
  8. - port: 30080 // 服务访问端口
  9. targetPort: 80 // pod控制器中定义的端口
  10. nodePort: 30001 // NodePort
  11. selector:
  12. name: nginx-pod

总的来说,port和nodePort都是service的端口,前者暴露给k8s集群内部服务访问,后者暴露给k8s集群外部流量访问。从这两个端口到来的数据都需要经过反向代理kube-proxy,流入后端pod的targetPort上,最后到达pod内容器的containerPort

Hello world

创建nginx.yaml文件

  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: nginx-service
  5. spec:
  6. selector:
  7. matchLabels:
  8. app: nginx
  9. replicas: 1
  10. template:
  11. metadata:
  12. labels:
  13. app: nginx
  14. spec:
  15. containers:
  16. - name: nginx
  17. image: nginx:1.14
  18. ports:
  19. - containerPort: 80
  20. ---
  21. apiVersion: v1
  22. kind: Service
  23. metadata:
  24. name: nginx-service
  25. spec:
  26. type: NodePort # 指定服务类型为 NodePort
  27. ports:
  28. - port: 8080 # 指定集群内部 service 的端口
  29. targetPort: 80 #指定 Pod 的端口
  30. nodePort: 30000 # 指定外部连接的端口
  31. selector:
  32. app: nginx # 标签选择器,选择带有 app=nginx 的 Pod

执行

  1. $ kubectl apply -f nginx-service.yaml
  2. deployment.apps/nginx-service created
  3. service/nginx-service created

查看

  1. $ kubectl get pods
  2. NAME READY STATUS RESTARTS AGE
  3. nginx-service-54f669bb6c-7vzmq 1/1 Running 0 59s

查看severice

  1. $ kubectl get svc
  2. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  3. kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 284d
  4. nginx-service NodePort 10.103.155.57 <none> 8080:30000/TCP 2m26s

查看详细信息

  1. $ kubectl describe svc nginx-service
  2. Name: nginx-service
  3. Namespace: default
  4. Labels: <none>
  5. Annotations: kubectl.kubernetes.io/last-applied-configuration:
  6. {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"nginx-service","namespace":"default"},"spec":{"ports":[{"nodePort...
  7. Selector: app=nginx
  8. Type: NodePort
  9. IP: 10.103.155.57
  10. LoadBalancer Ingress: localhost
  11. Port: <unset> 8080/TCP
  12. TargetPort: 80/TCP
  13. NodePort: <unset> 30000/TCP
  14. Endpoints: 10.1.0.139:80,10.1.0.140:80
  15. Session Affinity: None
  16. External Traffic Policy: Cluster
  17. Events: <none>

外部访问

端口是30000

  1. $ curl http://127.0.0.1:30000

服务发现

Service 服务发现目前有两种类型:环境变量和 DNS

ENV/环境变量

kubelet 会为每个 Pod(容器)添加一组环境变量,其中就包括当前系统中已经存在的 Service 的 IP 地址和端口号。
环境变量的格式如下所示:

  1. {SVCNAME}_SERVICE_HOST=host
  2. {SVCNAME}_SERVICE_PORT=port

环境变量名都必须为大写,如果其中有连字符的会被转换为下划线。

注意的问题是:当某个特定的 Service 晚于 Pod 的创建,那么先创建的 Pod 就不会注册该 Service 的环境变量。

创建一个新的服务httpbin-service.yaml

  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: httpbin-service
  5. spec:
  6. selector:
  7. matchLabels:
  8. app: httpbin
  9. replicas: 1
  10. template:
  11. metadata:
  12. labels:
  13. app: httpbin
  14. spec:
  15. containers:
  16. - name: httpbin
  17. image: kennethreitz/httpbin
  18. ports:
  19. - containerPort: 80
  20. ---
  21. apiVersion: v1
  22. kind: Service
  23. metadata:
  24. name: httpbin-service
  25. spec:
  26. type: NodePort # 指定服务类型为 NodePort
  27. ports:
  28. - port: 8000 # 指定集群内部 service 的端口
  29. targetPort: 80 #指定 Pod 的端口
  30. nodePort: 30001 # 指定外部连接的端口
  31. selector:
  32. app: httpbin # 标签选择器,选择带有 app=nginx 的 Pod

查看启动的pod

  1. $ kubectl get pods
  2. NAME READY STATUS RESTARTS AGE
  3. httpbin-service-6754cf494b-2gprb 1/1 Running 0 5m11s
  4. nginx-service-54f669bb6c-7vzmq 1/1 Running 0 21h

查看pod的环境变量

  1. $ kubectl delete svc pioneering-raccoon-mysql
  2. service "pioneering-raccoon-mysql" deleted
  3. baxiangdeMacBook:service baxiang$ kubectl exec httpbin-service-6754cf494b-2gprb env
  4. PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
  5. HOSTNAME=httpbin-service-6754cf494b-2gprb
  6. KUBERNETES_PORT_443_TCP_PORT=443
  7. PIONEERING_RACCOON_MYSQL_PORT_3306_TCP_ADDR=10.98.92.199
  8. HTTPBIN_SERVICE_PORT=tcp://10.106.158.114:8000
  9. HTTPBIN_SERVICE_PORT_8000_TCP=tcp://10.106.158.114:8000
  10. HTTPBIN_SERVICE_PORT_8000_TCP_PROTO=tcp
  11. HTTPBIN_SERVICE_PORT_8000_TCP_PORT=8000
  12. HTTPBIN_SERVICE_PORT_8000_TCP_ADDR=10.106.158.114
  13. KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
  14. PIONEERING_RACCOON_MYSQL_PORT_3306_TCP_PORT=3306
  15. PIONEERING_RACCOON_MYSQL_PORT_3306_TCP=tcp://10.98.92.199:3306
  16. KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
  17. NGINX_SERVICE_SERVICE_HOST=10.103.155.57
  18. HTTPBIN_SERVICE_SERVICE_PORT=8000
  19. KUBERNETES_SERVICE_PORT_HTTPS=443
  20. NGINX_SERVICE_PORT=tcp://10.103.155.57:8080
  21. HTTPBIN_SERVICE_SERVICE_HOST=10.106.158.114
  22. KUBERNETES_SERVICE_HOST=10.96.0.1
  23. NGINX_SERVICE_PORT_8080_TCP_PORT=8080
  24. NGINX_SERVICE_PORT_8080_TCP_ADDR=10.103.155.57
  25. PIONEERING_RACCOON_MYSQL_SERVICE_PORT=3306
  26. PIONEERING_RACCOON_MYSQL_PORT=tcp://10.98.92.199:3306
  27. PIONEERING_RACCOON_MYSQL_PORT_3306_TCP_PROTO=tcp
  28. NGINX_SERVICE_PORT_8080_TCP_PROTO=tcp
  29. PIONEERING_RACCOON_MYSQL_SERVICE_PORT_MYSQL=3306
  30. NGINX_SERVICE_SERVICE_PORT=8080
  31. NGINX_SERVICE_PORT_8080_TCP=tcp://10.103.155.57:8080
  32. PIONEERING_RACCOON_MYSQL_SERVICE_HOST=10.98.92.199
  33. KUBERNETES_PORT=tcp://10.96.0.1:443
  34. KUBERNETES_PORT_443_TCP_PROTO=tcp
  35. KUBERNETES_SERVICE_PORT=443
  36. HOME=/root

DNS

通过在集群中部署 CoreDNS 服务(在老版本的 kubernetes 集群中使用的是 KubeDNS)来实现集群内部 Pod 通过 DNS 方式进行通讯
在 nginx Pod 中通过 DNS 解析的方式来访问名为 httpbin 的服务

  1. $ kubectl exec -it nginx-service-54f669bb6c-7vzmq bash
  2. # apt-get update
  3. # apt-get install curl
  4. # curl httpbin-service:8000/ip
  5. {
  6. "origin": "10.1.0.139"
  7. }

参考

https://www.cnblogs.com/yaohong/p/11478749.html