学习笔记
分享学习经验,记录生活点滴

服务:让客户端发现pod并与之通信

service

1 介绍服务

​ Kubernetes服务是一种为一组功能相同的pod提供单一不变的接入点的资源。当服务存在时,它的IP地址和端口不会改变。客户端通过IP地址和端口号建立连接,这些连接会被路由到提供该服务的任意一个pod上。通过这种方式,客户端不需要知道每个单独的提供服务的pod的地址,这样这些pod就可以在集群中随时被创建或移除。

1.1 创建服务

  • 通过kubectl expose创建服务
  • 通过YAML描述文件来创建服务

代码清单 服务的定义:kubia-svc.yaml

apiVersion:v1
kind:Service
metadata:
    name:kubia
spec:
    ports:
        -port:80   #该服务的可用端口
        targetPort:8080  #服务将连接转发到的容器端口
    selector:      #具有app=kubia标签的pod
        app:kubia  #都属于该服务
  • 接下来通过使用kubectl create发布文件来创建服务

  • 检测新的服务

    $ kubectl get svc

  • 从内部集群测试服务

    • 显而易见的方法是创建一个pod,它将请求发送到服务的集群IP并记录响应。可以通过查看pod日志检查服务的响应。
    • 使用ssh远程登录到其中一个Kubernetes节点上,然后使用curl命令。
  • 可以通过kubectl exec命令在一个已经存在的pod中执行curl命令。

  • 在运行的容器中远程执行命令

    $ kubectl exec kubia-7nog1 --curl -s http://10.111.249.153

  • 配置服务上的会话亲和性

    设置服务的sessionAffinity属性为ClientIP(而不是None,None是默认值)

apiversion:v1
kind:Service
spec:
    sessionAffinity:ClientIP
    ...

​ 这种方式将会使服务代理将来自同一个client IP的所有请求转发至同一个pod上。

​ Kubernetes仅仅支持两种形式的会话亲和性服务:None和ClientIP。不支持基于cookie的会话亲和性的选项,这是因为Kubernetes服务不是在HTTP层面上工作。服务处理TCP和UDP包,并不关心其中的载荷内容。因为cookie是HTTP协议中的一部分,服务并不知道它们,这就解释了为什么会话亲和性不能基于cookie。

  • 同一个服务暴露多个端口

    创建的服务可以暴露一个端口,也可以暴露多个端口。比如,你的pod监听两个端口,比如HTTP监听8080端口、HTTPS监听8443端口,可以使用一个服务从端口80和443转发至pod端口8080和8443。在这种情况下,无须创建两个不同的服务。通过一个集群IP,使用一个服务就可以将多个端口全部暴露出来。

    apiVersion:v1
    kind:Service
    metadata:
    name:kubia
    spec:
    ports:
    - name:http
        port:80         #pod的8080端口
        targetPort:8080 #映射成80端口
    - name:https      
        port:443        #pod的8443端口
        targetPort:8443 #映射成443端口
    selector:        #标签选择器适用于
        app:kubia    #整个服务

1.2 服务发现

  • 通过环境变量发现服务
  • 通过DNS发现服务

2 连接集群外部的服务

​ 希望通过Kubernetes服务特性暴露外部服务的情况。不要让服务将连接重定向到集群中的pod,而是让它重定向到外部IP和端口。这样做可以充分利用服务负载平衡和服务发现。

2.1 服务endpoint

Endpoint 资源就是暴露一个服务的IP地址和端口的列表,Endpoint资源和其他Kubernetes资源一样,所以可以使用kubectl info来获取它的基本信息。

$ kubectl get endpoints kubia

2.2 手动配置服务的endpoint

​ 如果创建了不包含pod选择器的服务,Kubernetes将不会创建Endpoint资源(毕竞,缺少选择器,将不会知道服务中包含哪些pod)。这样就需要手动创建Endpoint资源来指定该服务的endpoint列表。
​ 要使用手动配置endpoint的方式创建服务,需要创建服务和Endpoint资源。

  • 创建没有选择器的服务

    首先为服务创建一个YAML文件

    代码清单 不含pod选择器的服务:external-service.yaml

    apiVersion:v1
    kind:Service
    metadata:
    name:external-service  #服务的名字必须和Endpoint对象的名字相匹配
    spec:
    ports:     #服务中没有定义选择器
    -port:80
  • 为没有选择器的服务创建 Endpoint 资源

    代码清单 手动创建 Endpoint 资源:external-service-endpoints.yaml

    apiVersion:v1
    kind:Endpoints 
    metadata: 
    name:external-service  #Endpoint的名称必须和服务的名称相匹配(见前一个代码清单)
    subsets:
    -addresses:                            
        -ip:11.11.11.11    #服务将连接重定向
        -ip:22.22.22.22    #到endpoint的IP地址
        ports:
        -port:80        #endpoint的目标端口

    ​ Endpoint对象需要与服务具有相同的名称,并包含该服务的目标IP地址和端口列表。服务和Endpoint资源都发布到服务器后,这样服务就可以像具有pod选择器那样的服务正常使用。在服务创建后创建的容器将包含服务的环境变量,并且与其IP:port对的所有连接都将在服务端点之间进行负载均衡。

2.3 为服务创建别名

​ 除了手动配置服务的Endpoint来代替公开外部服务方法,有一种更简单的方法,就是通过其完全限定域名(FQDN)访问外部服务

  • 创建ExternalName类型的服务

    ​ 要创建一个具有别名的外部服务的服务时,要将创建服务资源的一个type字段设置为ExternalName。例如,设想一下在someapi.somecompany.com上有公共可用的API,可以定义一个指向它的服务,如下面的代码清单所示。

    代码清单 ExternalName类型的服务:extemal-service-extemalname.yaml

    apiVersion:v1
    kind:Service
    metadata:
    name:external-service
    spec:
    type:ExternalName  #代码的type被设置成ExternalName
    externalName:someapi.somecompany.com  #实际服务的完全限定域名
    ports:
    -port:80

3 将服务暴露给外部客户端

有几种方式可以在外部访问服务:

  • 将服务的类型设置成NodePort
  • 将服务的类型设置成LoadBalance(NodePort类型的一种扩展)
  • 创建一个Ingress资源,这是一个完全不同的机制,通过一个IP地址公开多个服务一—它运行在HTTP层(网络协议第7层)上。

3.1 使用NodePort类型的服务

创建 NodePort类型的服务

代码清单 NodePort 服务定义kubia-svc-nodeport.yaml

apiVersion:v1
kind:Service
metadata:
    name:kubia-nodeport
spec:
    type:NodePort  #为NodePort设置服务类型
    ports:
    -port:80      #服务集群IP的端口号
        targetPort:8080  #背后pod的目标端口号
        nodePort:30123  #通过集群节点的30123端口可以访问该服务
    selector:
        app:kubia

查看NodePort类型的服务

$ kubectl get svc kubia-nodeport

更改防火墙规则,让外部客户端访问我们的NodePort服务(谷歌云平台)

$ gcloud compute firewall -rules create kubia-svc-rule --allow=tcp:30123

3.2 通过负载均衡器将服务暴露出来

创建 LoadBalance服务

代码清单 Load Balancer类型的服务:kubia-svc-loadbalancer.yaml

apiversion:v1
kind:Service
metadata:
    name:kubia-loadbalancer
spec:
    type:LoadBalancer        #该服务从Kubernetes集群的基础架构获取负载平衡器
    ports:
    -port:80
        targetPort:8080
    selector:
        app:kubia

通过负载均衡器连接服务

​ 创建服务后,云基础架构需要一段时间才能创建负载均衡器并将其IP地址写入服务对象。一旦这样做了,IP地址将被列为服务的外部IP地址:

$ kubectl get svc kubia-loadbalancer

假设负载均衡器的IP地址(EXTERNAL-IP)为130.211.53.173,现在可以通过该IP地址访问该服务:

$ curl http://130.211.53.173

4 pod就绪后发出信号

4.1 就绪指针

像存活探针一样,就绪探针有三种类型:

  • Exec探针,执行进程的地方。容器的状态由进程的退出状态代码确定。
  • HTTP GET探针,向容器发送HTTP GET请求,通过响应的HTTP状态代码判断容器是否准备好。
  • TCP socket探针,它打开一个TCP连接到容器的指定端口。如果连接已建立,则认为容器已准备就绪。

4.2 向pod 添加就绪指针

​ 通过修改Replication Controller的pod模板来为现有的pod添加就绪探针。

向pod template添加就绪探针

​ 可以通过kubectl edit命令来向已存在的ReplicationController中的pod模板添加探针。

$ kubectl edit rc kubia

​ 当在文本编辑器中打开ReplicationControllerYAML时,在pod模板中查找容器规格,并将以下就绪探针定义添加到spec.template.spec.containers下的第一个容器。YAML看起来应该就像下面的代码清单。

代码清单 RC创建带有就绪探针的pod:kubia-rc-readinessprobe.yaml

apiVersion:v1
kind:ReplicationController
...
spec:
    ...
    template:
        ...
        spec:
            containers:
            - name:kubia
                image:luksa/kubia
                readinessProbe:        #
                    xec:               #pod中的每个容器都
                        command:       #会有一个就绪探针
                        - ls           #
                        - /var/ready   #
                ...

5 使用headless服务来发现独立的pod

5.1 创建 headless服务

​ 将服务spec中的clusterIP字段设置为None会使服务成为headless服务,因为Kubernetes不会为其分配集群IP,客户端可通过该IP将其连接到支持它的pod。
​ 现在将创建一个名为kubia-headlessheadless服务。以下代码清单显示了它的定义。

代码清单 一个headless 服务:kubia-svc-headless.yaml

apiVersion:v1
kind:Service
metadata:
    name:kubia-headless
spec:
    clusterIP:None   #这使得服务成为headless的
    ports:
    - port:80
        targetPort:8080
    selector:
        app:kubia

​ 在使用kubectl create创建服务之后,可以通过kubectl getkubectl describe来查看服务,你会发现它没有集群IP,并且它的后端包含与pod 选择器匹配的(部分)pod。“部分”是因为pod包含就绪探针,所以只有准备就绪的pod会被列出作为服务的后端文件来确保至少有两个pod报告已准备就绪。

5.2 通过DNS发现pod

不通过YAML文件运行pod:

$ kubectl run dnsutils --image=tutum/dnsutils --generator=run-pod/v1 --command--sleep infinity

​ 诀窍在--generator=run-pod/v1选项中,该选项让kubectl直接创建pod,而不需要通过ReplicationController之类的资源来创建。

使用新创建的pod执行DNS查找:

$ kubectl exec dnsutils nslookup kubia-headless

5.3 发现所有的pod—包括未就绪的pod

​ 只有准备就绪的pod能够作为服务的后端。但有时希望即使pod没有准备就绪,服务发现机制也能够发现所有匹配服务标签选择器的pod。

​ 幸运的是,不必通过查询KubernetesAPI服务器,可以使用DNS查找机制来查找那些未准备好的pod。要告诉Kubernetes无论pod的准备状态如何,希望将所有pod添加到服务中。必须将以下注解添加到服务中:

kind:Service
metadata:
    annotations:
        service.alpha.kubernetes.io/tolerate-unready-endpoints:"true"

6 排除服务故障

如果无法通过服务访问pod,应该根据下面的列表进行排查:

  • 首先,确保从集群内连接到服务的集群IP,而不是从外部。
  • 不要通过ping服务IP来判断服务是否可访问(请记住,服务的集群IP是虚拟IP,是无法ping通的)。
  • 如果已经定义了就绪探针,请确保它返回成功;否则该pod不会成为服务的一部分。
  • 要确认某个容器是服务的一部分,请使用kubectl get endpoints来检查相应的端点对象。
  • 如果尝试通过FQDN或其中一部分来访问服务(例如,myservice.mynamespace.svc.cluster.localmyservice.mynamespace),但并不起作用,请查看是否可以使用其集群IP而不是FQDN来访问服务。
  • 检查是否连接到服务公开的端口,而不是目标端口。
  • 尝试直接连接到podIP以确认pod正在接收正确端口上的连接。
  • 如果甚至无法通过podIP访问应用,请确保应用不是仅绑定到本地主机。

7 小结

  • 在一个固定的IP地址和端口下暴露匹配到某个标签选择器的多个pod
  • 服务在集群内默认是可访问的,通过将服务的类型设置为NodePortLoadBalancer,使得服务也可以从集群外部访问
  • pod能够通过查找环境变量发现服务的IP地址和端口
  • 允许通过创建服务资源而不指定选择器来发现驻留在集群外部的服务并与之通信,方法是创建关联的Endpoint 资原
  • 为具有ExternalName服务类型的外部服务提供DNS CNAME别名
  • 通过单个Ingress公开多个HTTP服务(使用单个IP
  • 使用pod容器的就绪探针来确定是否应该将pod包含在服务endpoints
  • 通过创建 headless服务让DNS发现pod IP
  • 通过kubectl execpod容器中执行命令
  • 通过kubectl apply命令修改Kubernetes资源
  • 使用kubectl run --generator=run-pod/v1运行临时的pod
赞(6) 打赏
未经允许不得转载:ABCLearning » 服务:让客户端发现pod并与之通信
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

阿里云限时红包 助力一步上云

了解详情领取红包

觉得文章有用就打赏一下文章作者

微信扫一扫打赏