kubernetes实践-内部服务依赖外部服务

原创
2019/05/08 19:05
阅读数 336

大多数 Kubernetes 用户都有可能用到集群外部的服务。例如,您可能使用 Twillio API 发送短信。

如果位于不同环境中的应用连接相同的外部端点,并且您不打算将外部服务引入 Kubernetes 集群,那么在代码中直接使用外部服务的端点是完全可以的。然而,很多时候情况并非如此。

端点即Endpoint,表示访问服务的唯一地址,不同类型的服务有着不同的表达,例如http服务一般是:http://domain:port/api/function, mysql 服务是jdbc:mysql://domain:port/db_name,不难看出同种服务的Endpoint,变化的核心就是域名/IP+端口

例如,在我们使用云数据库时,需要创建数据库实例,并且它会自动为我们的数据库实例创建一个唯一的域名(即访问的Endpoint)。

在不同的部署环境中,相同的外部服务使用不同的Endpoint非常常见。

就查找端点而言,ConfigMap 是个不错的解决方案。只需将端点地址存储在 ConfigMap 中,并将其作为环境变量用于代码中。此解决方案的确有效,但也存在一些缺点。您需要修改部署以包含 ConfigMap 并编写额外的代码以从环境变量中读取。但最重要的是,如果端点地址发生变化,您可能需要重启所有正在运行的容器以获取更新后的端点地址。

如何将 Kubernetes 内置服务发现机制运用于集群外部运行的服务,像使用集群内的服务一样使用外部服务?通过这种方式,您可以在开发环境和生产环境中实现相同的功能,如果最终将服务移入集群内,则不需要更改任何代码。

场景一: 具有 IP 地址的集群外服务

使用 Cloud Launcher 创建了一个 MongoDB 服务器。由于此服务器在与 Kubernetes 集群相同的网络(或 VPC)中创建,因此可以使用高性能的内部 IP 地址访问。

有了IP,那么我们就可以创建服务了:

kind: Service
apiVersion: v1
metadata:
    name: mongo
Spec:
    type: ClusterIP
    ports:
      – port: 27017
        targetPort: 27017

该服务没有Pod选择器,它不知道将流量发到何处,此时我们就可以手动创建一个Endpoint:

kind: Endpoints
apiVersion: v1
metadata:
    name: mongo
subsets:
    – addresses:
        – ip: 10.240.0.4
      ports:
        – port: 27017

Endpoints 手动定义了数据库的 IP 地址,并且Endpoint的名称与Service名称相同。Kubernetes 将 Endpoints 中定义的所有 IP 地址视为与常规 Kubernetes Pod 一样。

现在用一个简单的连接字符串访问数据库:

mongodb://mongo

根本不需要在代码中使用 IP 地址!如果以后 IP 地址发生变化,您可以为端点更新 IP 地址,而应用无需进行任何更改。

场景二: 具有 URI 的集群外部服务

如果使用的是来自第三方的托管数据库服务,它们可能会为您提供可用于连接的统一资源标识符 (URI)。如果它们为您提供 IP 地址,则可以使用场景一种的方法,但是如果没有提供,则需要想别的办法。

例如使用 mLab 上托管了两个 MongoDB 数据库。一个是我的开发数据库,另一个是生产数据库。

数据库连接字符串如下:

mongodb://<dbuser>:<dbpassword>@ds149763.mlab.com:49763/dev
mongodb://<dbuser>:<dbpassword>@ds145868.mlab.com:45868/prod

云数据库提供了动态 URI 和动态端口。我们来使用 Kubernetes 基于这些差异创建一个抽象层,屏蔽相关的差异。

可以创建一个 ExternalName Kubernetes 服务,此服务为您提供将流量重定向到外部服务的静态 Kubernetes 服务。此Kubernetes Service在内核级别执行简单的 CNAME 重定向,因此对性能的影响非常小。

kind: Service
apiVersion: v1
metadata:
    name: mongo
spec:
    type: ExternalName
    externalName: ds149763.mlab.com

那么可以使用如下的方式访问数据库:

mongodb://<dbuser>:<dbpassword>@mongo:<port>/dev

由于 ExternalName 使用 CNAME 重定向,因此无法执行端口重映射。对于使用静态端口的服务来说,这可能不成问题,然而本例中使用的是动态端口。这意味着您需要对开发和生产数据库使用其他连接字符串。

但如果您可以获取 IP 地址,就可以执行端口重映射。

场景三:具有 URI 和端口重映射功能的集群外部服务

CNAME 重定向对于每个环境均使用相同端口的服务非常有效,但如果每个环境的不同端点使用不同的端口,CNAME 重定向就略显不足。

第一步是从 URI 获取 IP 地址。URI 运行 nslookup、hostname 或 ping 命令即可获取数据库的 IP 地址。例如IP:35.188.8.12

此时就转化成第一种类型:

kind: Service
apiVersion: v1
metadata:
    name: mongo
spec:
    ports:
      – port: 27017
        targetPort: 49763
---
kind: Endpoints
apiVersion: v1
metadata:
    name: mongo
subsets:
    – addresses:
        – ip: 35.188.8.12
      ports:
        – port: 49763

⚠️注意:

  1. URI 可以使用 DNS 在多个 IP 地址之间进行负载平衡,因此,如果 IP 地址发生变化,这个方法可能会有风险!
  2. 如果您通过上述命令获取多个 IP 地址,则可以将所有这些地址都包含在 Endpoints YAML 中,并且 Kubernetes 会在所有 IP 地址之间进行流量的负载平衡。

通过这种方式,您无需指定端口即可连接到远程数据库。Kubernetes 服务重映射端口的过程完全透明!

mongodb://<dbuser>:<dbpassword>@mongo/dev

注意此处mongodb://<dbuser>:<dbpassword>@mongo/dev中Endpoint没有带端口是因为支持Endpoint没有指定端口就是用默认端口,如果Endpoint不支持默认端口,则需要带上端口,但此时端口号是不需要修改的。

--Posted from Rpc

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
0 评论
0 收藏
0
分享
返回顶部
顶部