V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Recommended Services
Amazon Web Services
LeanCloud
New Relic
ClearDB
PureWhite
V2EX  ›  云计算

Kubernetes 中的 Service

  •  
  •   PureWhite · 2018-01-24 01:00:26 +08:00 · 3735 次点击
    这是一个创建于 2256 天前的主题,其中的信息可能已经有所发展或是发生改变。

    Service 是 kubernetes 中一个很重要的,也是很有用的概念,我们可以通过 service 来将 pod 进行分组,并提供外网的访问 endpoint。在这个过程中还有比如kube-proxy提供了对 service 的访问。

    Connecting Users to Pods

    如果我们要让一个用户能够使用应用程序,用户需要能访问到 pod,但是 pod 是一个短暂存在的东西,很可能突然挂了然后重启,这时候 ip 地址就会改变,所以 pod 的 ip 地址并不是静态的。比如说:

    E3FACD27-1617-40FE-A1EC-2099FD6AFF35

    用户在这张图里面通过 ip 地址访问到了 4 个 pod,突然其中有一个 pod 挂了,然后 controller 又起了一个 pod:

    BE6386EA-2846-4AD8-ACD3-80A6163AD935

    这时候用户就访问不到了,因为用户不知道新的 ip 地址是多少。

    kubernetes 为了解决这个问题,提供了一个高层的抽象,叫做 Service。Service 从逻辑上把 pod 进行分组,并且设置访问的策略。一般我们是通过 label 和 selector 来达到分组的目的的。

    Services

    比如,我们用 app 作为 key,db 和 frontend 作为 value 来区分 pod:

    23B63A0F-40BC-4FF7-AAEC-4D1C11C54970

    通过 selector ( app=frontend 和 app=db ),我们就可以把这些 pod 分为两个逻辑组了。

    这个时候,我们再给这两个逻辑组加上一个名称,比如frontend-svcdb-svc,就是 service 了:

    F67805E2-CE2D-40A8-9CFD-DE79CFD832E4

    Service 对象模型

    一个 service 对象模型大致如下:

    kind: Service
    apiVersion: v1
    metadata:
      name: frontend-svc
    spec:
      selector:
        app: frontend
      ports:
        - protocol: TCP
          port: 80
          targetPort: 5000
    

    在这个对象模型中,我们创建了一个叫做frontend-svc的 Service,这个 service 选择了所有的app=frontend的 pod。在默认情况下,每个 service 都会有一个 cluster 内部可以访问到的 ip 地址,也被称为ClusterIP

    3232AECF-82CA-45DF-B971-1EB278EFD552

    用户现在可以通过 service 的 ip 地址来访问到 pod 了,service 会负责做负载均衡。

    当转发请求的时候,我们可以选择 pod 上的目标端口,比如在我们的例子里面,frontend-svc 通过 80 端口来接受用户的请求,然后转发到 pod 的 5000 端口。如果目标端口没有被显式声明,那么会默认转发到 service 接受请求的端口(和 service 端口一样)。

    一个 pod、ip 地址和目标端口的元组代表了一个 service 的 endpoint,比如在这个例子里面,frontend-svc 有 3 个 endpoints,分别是10.0.1.3:5000, 10.0.1.4:500010.0.1.5:5000

    kube-proxy

    所有的 worker node 都有一个后台任务,叫做kube-proxy。这个 kube-proxy 会检测 API Server 上对于 service 和 endpoint 的新增或者移除。对于每个新的 service,在每个 node 上,kube-proxy 都会设置相应的 iptables 的规则来记录应该转发的地址。当一个 service 被删除的时候,kube-proxy 会在所有的 pod 上移除这些 iptables 的规则。

    0608F78E-6A96-403A-A9E1-3095AE3625F3

    服务发现

    我们已经知道,Service 是和 kubernetes 进行沟通的主要方式,那么我们就需要有一个办法来在运行的时候能够对已有的服务进行发现。Kubernetes 提供了两种方法:

    环境变量

    每个 pod 在 worker node 上启动的时候,kubelet 都会通过环境变量把所有目前可用的 service 的信息传进去。举个例子,我们有一个叫做redis-master的 service,这个 service expose 了 6379 的端口,并且 ClusterIP 是 172.17.0.6,那么在一个新创建的 pod 上,我们可以看到以下环境变量:

    REDIS_MASTER_SERVICE_HOST=172.17.0.6
    REDIS_MASTER_SERVICE_PORT=6379
    REDIS_MASTER_PORT=tcp://172.17.0.6:6379
    REDIS_MASTER_PORT_6379_TCP=tcp://172.17.0.6:6379
    REDIS_MASTER_PORT_6379_TCP_PROTO=tcp
    REDIS_MASTER_PORT_6379_TCP_PORT=6379
    REDIS_MASTER_PORT_6379_TCP_ADDR=172.17.0.6
    

    如果使用这个解决方案,我们必须非常小心启动服务的顺序,因为 pod 不会获得自己启动之后的 service 的 env。

    DNS

    kubernetes 有一些 dns 的 addon,这些 addon 会自动为所有 service 创建一个类似my-svc.my-namespace.svc.cluster.local的 dns 解析,并且在同一个 namespace 里面的 service 可以直接用 service name 进行访问。这是最为推荐的方法。

    Service 类型

    当我们定义一个 service 的时候,我们可以选择可访问的范围,比如:

    • 是否只能在 cluster 内部访问
    • 是否同时可以被 cluster 内部和外部访问
    • 是否是映射到一个集群外的 entity 上

    可访问的范围由 service 的类型决定,service 的类型可以在创建 service 的时候声明。

    ClusterIP 和 NodePort

    ClusterIP 是默认的 service type,一个 service 通过 ClusterIP 来获取自己的 Virtual IP,这个 IP 是用来和别的 service 通信的,只能在集群内部被访问。

    NodePort 的 service type 除了会创建一个 ClusterIP 之外,还会把所有 worker node 上的一个 30000-32767 之间的端口映射到这个 service,比如假设32233端口映射到了frontend-svc,那么不管我们连接到哪个 worker node,我们都会被转发到 service 分配的 ClusterIP —— 172.17.0.4。

    默认情况下,当 expose 到有一个 nodeport 的时候,kubernetes master 会自动随机选择一个 30000-32767 之间的 port,当然,我们自己也可以手动指定这个 port。

    A943E99B-5473-4177-B8AF-543939949F0B

    NodePort 的这个 service type 在我们想要让外网访问我们服务的时候非常有用,用户通过访问 node 上指定的 port 就可以访问到这个 service。管理员可以在 kubernetes 集群外再搭一个反向代理就可以更方便地进行访问了。

    LoadBalancer

    对于 LoadBalancer 这个 Servicetype:

    • NodePort 和 ClusterIP 会被自动创建,外部的 load balancer 会自动路由上去
    • service 会在一个静态的端口上被暴露
    • 通过底层的 cloud provider 提供的 load balancer 来暴露到外网

    8C099E38-B51E-4688-B693-1FF2F46FCE0B

    LoadBalancer 这个 service type 只有在底层的基础架构支持了自动创建 load balancer 的时候 kubernetes 才支持,比如 Google Cloud Platform 和 aws。

    ExternalIP

    如果一个 service 可以路由到一个或者多个 worker node 上,那么它可以被映射到一个 ExternalIP 地址。通过这个 ExternalIP 进入到集群的流量会被路由到其中一个 endpoint 上。

    1B599B5F-0EA5-46A5-B505-2FC2658AC55C

    需要注意的是,ExternalIP 并不是由 k8s 自动管理的,是由管理员手动设置路由到其中的一个 node 上的。

    ExternalName

    ExternalName 是一个特定的 service type,这种 service type 没有任何的 selector 也没有任何声明的 endpoint。当在集群中访问到这个 service 的时候,会返回一个外部服务的 CNAME。

    这个 service 一般是用来让一个外部的服务在集群内部可以访问到的,比如我们有一个外部服务叫做my-database.example.com,那么我们可以通过设置 ExternalName 类型的 Service,让内部的其它 service 通过my-database之类的名字访问到这个服务。

    原文出自:我的博客

    5 条回复    2018-01-24 15:33:27 +08:00
    artandlol
        1
    artandlol  
       2018-01-24 02:24:08 +08:00 via iPhone
    感谢
    问下博主,微 deis 的 workflow 怎么样?目前国内没有这方面的资料,自己折腾了解的不多
    PureWhite
        2
    PureWhite  
    OP
       2018-01-24 09:37:00 +08:00
    @artandlol 这玩意已经不维护了吧。。。。
    artandlol
        3
    artandlol  
       2018-01-24 15:13:34 +08:00 via iPhone
    @PureWhite 被微软收购了
    desi.comdesi.io 还很活跃
    artandlol
        4
    artandlol  
       2018-01-24 15:14:45 +08:00 via iPhone
    PureWhite
        5
    PureWhite  
    OP
       2018-01-24 15:33:27 +08:00
    @artandlol
    但是你说的 workflow 已经是 deprecated 了,马上就不维护了。

    现在 deis 是挺活跃的,不过主要是在做 helm
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   950 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 31ms · UTC 21:13 · PVG 05:13 · LAX 14:13 · JFK 17:13
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.