使用外部 TCP 服务

描述一个基于 Istio Bookinfo 示例的简单场景。

2018 年 2 月 6 日 | 作者:Vadim Eisenberg

在我之前的博文 使用外部 Web 服务 中,我描述了网格内 Istio 应用程序如何通过 HTTPS 使用外部服务。在本博文中,我演示了如何通过 TCP 使用外部服务。您将使用 Istio Bookinfo 示例应用程序,该版本的图书评分数据存储在 MySQL 数据库中。您在集群外部部署此数据库,并配置 ratings 微服务以使用它。您定义一个 服务条目 以允许网格内应用程序访问外部数据库。

具有外部评分数据库的 Bookinfo 示例应用程序

首先,您设置一个 MySQL 数据库实例以在 Kubernetes 集群外部存储图书评分数据。然后,您修改 Istio Bookinfo 示例应用程序 以使用您的数据库。

设置评分数据的数据库

对于此任务,您设置一个 MySQL 实例。您可以使用任何 MySQL 实例;我使用的是 Compose for MySQL。我使用 mysqlsh (MySQL Shell) 作为 MySQL 客户端来提供评分数据。

  1. 设置 MYSQL_DB_HOSTMYSQL_DB_PORT 环境变量

    $ export MYSQL_DB_HOST=<your MySQL database host>
    $ export MYSQL_DB_PORT=<your MySQL database port>
    

    对于具有默认端口的本地 MySQL 数据库,值分别为 localhost3306

  2. 要初始化数据库,请运行以下命令,并在提示时输入密码。该命令使用 Compose for MySQL 默认创建的 admin 用户的凭据执行。

    $ curl -s https://raw.githubusercontent.com/istio/istio/release-1.24/samples/bookinfo/src/mysql/mysqldb-init.sql | mysqlsh --sql --ssl-mode=REQUIRED -u admin -p --host $MYSQL_DB_HOST --port $MYSQL_DB_PORT
    

    或者

    使用 mysql 客户端和本地 MySQL 数据库时,请运行

    $ curl -s https://raw.githubusercontent.com/istio/istio/release-1.24/samples/bookinfo/src/mysql/mysqldb-init.sql | mysql -u root -p --host $MYSQL_DB_HOST --port $MYSQL_DB_PORT
    
  3. 创建名为 bookinfo 的用户,并授予它对 test.ratings 表的 SELECT 权限

    $ mysqlsh --sql --ssl-mode=REQUIRED -u admin -p --host $MYSQL_DB_HOST --port $MYSQL_DB_PORT -e "CREATE USER 'bookinfo' IDENTIFIED BY '<password you choose>'; GRANT SELECT ON test.ratings to 'bookinfo';"
    

    或者

    对于 mysql 和本地数据库,命令为

    $ mysql -u root -p --host $MYSQL_DB_HOST --port $MYSQL_DB_PORT -e "CREATE USER 'bookinfo' IDENTIFIED BY '<password you choose>'; GRANT SELECT ON test.ratings to 'bookinfo';"
    

    在这里,您应用了 最小权限原则。这意味着您不会在 Bookinfo 应用程序中使用 admin 用户。相反,您为 Bookinfo 应用程序创建了一个名为 bookinfo 的专用用户,该用户具有最小权限。在本例中,bookinfo 用户仅对单个表具有 SELECT 权限。

    运行创建用户的命令后,您可能希望通过检查最后一个命令的编号并运行 history -d <创建用户的命令编号> 来清除您的 bash 历史记录。您不希望新用户的密码存储在 bash 历史记录中。如果您使用的是 mysql,请从 ~/.mysql_history 文件中删除最后一个命令。在 MySQL 文档 中阅读有关新创建用户的密码保护的更多信息。

  4. 检查创建的评分,以确保一切按预期工作

    $ mysqlsh --sql --ssl-mode=REQUIRED -u bookinfo -p --host $MYSQL_DB_HOST --port $MYSQL_DB_PORT -e "select * from test.ratings;"
    Enter password:
    +----------+--------+
    | ReviewID | Rating |
    +----------+--------+
    |        1 |      5 |
    |        2 |      4 |
    +----------+--------+
    

    或者

    对于 mysql 和本地数据库

    $ mysql -u bookinfo -p --host $MYSQL_DB_HOST --port $MYSQL_DB_PORT -e "select * from test.ratings;"
    Enter password:
    +----------+--------+
    | ReviewID | Rating |
    +----------+--------+
    |        1 |      5 |
    |        2 |      4 |
    +----------+--------+
    
  5. 将评分临时设置为 1,以在 Bookinfo ratings 服务使用我们的数据库时提供视觉线索

    $ mysqlsh --sql --ssl-mode=REQUIRED -u admin -p --host $MYSQL_DB_HOST --port $MYSQL_DB_PORT -e "update test.ratings set rating=1; select * from test.ratings;"
    Enter password:
    
    Rows matched: 2  Changed: 2  Warnings: 0
    +----------+--------+
    | ReviewID | Rating |
    +----------+--------+
    |        1 |      1 |
    |        2 |      1 |
    +----------+--------+
    

    或者

    对于 mysql 和本地数据库

    $ mysql -u root -p --host $MYSQL_DB_HOST --port $MYSQL_DB_PORT -e "update test.ratings set rating=1; select * from test.ratings;"
    Enter password:
    +----------+--------+
    | ReviewID | Rating |
    +----------+--------+
    |        1 |      1 |
    |        2 |      1 |
    +----------+--------+
    

    您在最后一个命令中使用了 admin 用户(以及本地数据库的 root),因为 bookinfo 用户没有对 test.ratings 表的 UPDATE 权限。

现在,您已准备好部署将使用您的数据库的 Bookinfo 应用程序版本。

Bookinfo 应用程序的初始设置

为了演示使用外部数据库的场景,您从具有 安装 Istio 的 Kubernetes 集群开始。然后,您部署 Istio Bookinfo 示例应用程序应用默认目标规则,并 将 Istio 更改为默认阻止出口策略

此应用程序使用 ratings 微服务来获取图书评分,评分范围为 1 到 5。评分以星形显示在每个评论下方。ratings 微服务有几个版本。有些使用 MongoDB,有些使用 MySQL 作为其数据库。

本博文中的示例命令适用于 Istio 0.8+,无论是否启用了 双向 TLS

提醒一下,这是 Bookinfo 示例应用程序 中应用程序的端到端架构。

The original Bookinfo application
原始 Bookinfo 应用程序

在 Bookinfo 应用程序中使用评分数据的数据库

  1. 修改使用 MySQL 数据库的 ratings 微服务版本的部署规范,以使用您的数据库实例。该规范位于 Istio 发行版存档的 samples/bookinfo/platform/kube/bookinfo-ratings-v2-mysql.yaml 中。编辑以下几行

    - name: MYSQL_DB_HOST
      value: mysqldb
    - name: MYSQL_DB_PORT
      value: "3306"
    - name: MYSQL_DB_USER
      value: root
    - name: MYSQL_DB_PASSWORD
      value: password
    

    替换上面代码段中的值,指定数据库主机、端口、用户和密码。请注意,在 Kubernetes 中使用容器环境变量处理密码的正确方法是 使用密钥。仅针对此示例任务,您可能希望将密码直接写入部署规范中。不要在真实环境中这样做!我还假设每个人都意识到 "password" 不应用作密码…

  2. 应用修改后的规范以部署将使用您的数据库的 ratings 微服务版本 v2-mysql

    压缩
    $ kubectl apply -f @samples/bookinfo/platform/kube/bookinfo-ratings-v2-mysql.yaml@
    deployment "ratings-v2-mysql" created
    
  3. 将所有发往 reviews 服务的流量路由到其 v3 版本。这样做是为了确保 reviews 服务始终调用 ratings 服务。此外,将所有发往 ratings 服务的流量路由到使用您的数据库的 ratings v2-mysql

    通过添加两个 虚拟服务 来指定上面两个服务的路由。这些虚拟服务在 Istio 发行版存档的 samples/bookinfo/networking/virtual-service-ratings-mysql.yaml 中指定。重要:确保您在运行以下命令之前 应用了默认目标规则

    压缩
    $ kubectl apply -f @samples/bookinfo/networking/virtual-service-ratings-mysql.yaml@
    

更新后的架构如下所示。请注意,网格内的蓝色箭头标记了根据我们添加的虚拟服务配置的流量。根据虚拟服务,流量将发送到 reviews v3ratings v2-mysql

The Bookinfo application with ratings v2-mysql and an external MySQL database
具有 ratings v2-mysql 和外部 MySQL 数据库的 Bookinfo 应用程序

请注意,MySQL 数据库位于 Istio 服务网格外部,或者更准确地说是 Kubernetes 集群外部。服务网格的边界用虚线标记。

访问网页

确定入口 IP 和端口 后,访问应用程序的网页。

您遇到一个问题… 在每个评论下方,当前显示的不是评分星级,而是消息 “评分服务当前不可用”

The Ratings service error messages
评分服务错误消息

使用外部 Web 服务 一样,您体验到优雅的服务降级,这是好事。由于 ratings 微服务中的错误,应用程序没有崩溃。应用程序的网页正确显示了图书信息、详细信息和评论,只是没有评分星级。

您遇到的问题与 使用外部 Web 服务 中的问题相同,即所有在 Kubernetes 集群外部的流量(包括 TCP 和 HTTP)默认情况下都被边车代理阻止。要为 TCP 启用此类流量,必须定义一个网格外部服务条目,用于 TCP。

用于外部 MySQL 实例的网格外部服务条目

TCP 网格外部服务条目可以帮助我们解决问题。

  1. 获取 MySQL 数据库实例的 IP 地址。您可以选择使用 host 命令

    $ export MYSQL_DB_IP=$(host $MYSQL_DB_HOST | grep " has address " | cut -d" " -f4)
    

    对于本地数据库,将 MYSQL_DB_IP 设置为包含您的机器的 IP,该 IP 可从您的集群访问。

  2. 定义 TCP 网格外部服务条目

    $ kubectl apply -f - <<EOF
    apiVersion: networking.istio.io/v1alpha3
    kind: ServiceEntry
    metadata:
      name: mysql-external
    spec:
      hosts:
      - $MYSQL_DB_HOST
      addresses:
      - $MYSQL_DB_IP/32
      ports:
      - name: tcp
        number: $MYSQL_DB_PORT
        protocol: tcp
      location: MESH_EXTERNAL
    EOF
    
  3. 查看您刚刚创建的服务条目,并检查它是否包含正确的值

    $ kubectl get serviceentry mysql-external -o yaml
    apiVersion: networking.istio.io/v1alpha3
    kind: ServiceEntry
    metadata:
    ...
    

请注意,对于 TCP 服务条目,您将 tcp 指定为条目的端口的协议。还要注意,您必须在地址列表中指定外部服务的 IP,作为带有后缀 32CIDR 块。

我将在 下面 详细介绍 TCP 服务条目。现在,验证我们添加的服务条目是否解决了问题。访问网页,查看是否已恢复星级。

它起作用了!访问应用程序的网页会显示评分,不会出现错误

Book Ratings Displayed Correctly
正确显示的图书评分

请注意,您会看到两个显示的评论的评分都为一颗星,符合预期。您将评分更改为一颗星,以便提供视觉线索,证明我们的外部数据库确实正在使用。

与 HTTP/HTTPS 的服务条目一样,您可以使用 kubectl 动态删除和创建 TCP 的服务条目。

出口 TCP 流量控制的动机

一些网格内 Istio 应用程序必须访问外部服务,例如传统系统。在许多情况下,访问不是通过 HTTP 或 HTTPS 协议进行的。而是使用其他 TCP 协议,例如数据库特定协议,如 MongoDB 线程协议MySQL 客户端/服务器协议 来与外部数据库通信。

接下来,我将提供有关 TCP 流量服务条目的更多详细信息。

TCP 流量服务条目

用于启用对特定端口的 TCP 流量的服务条目必须将 TCP 指定为端口的协议。此外,对于 MongoDB 线程协议,协议可以指定为 MONGO,而不是 TCP

对于条目中的addresses 字段,必须使用 CIDR 表示法中的 IP 块。请注意,hosts 字段在 TCP 服务条目中被忽略。

要通过其主机名启用对外部服务的 TCP 流量,必须指定主机名所有 IP。每个 IP 必须由 CIDR 块指定。

请注意,并非总是知道所有外部服务的 IP。要启用出站 TCP 流量,只需指定应用程序使用的 IP。

还要注意,外部服务的 IP 并不总是静态的,例如 CDN 的情况。有时 IP 大部分时间是静态的,但可能会不时更改,例如由于基础设施更改。在这些情况下,如果已知可能 IP 的范围,则应使用 CIDR 块指定该范围。如果未知可能 IP 的范围,则无法使用 TCP 的服务条目,并且必须 直接调用外部服务,绕过 Sidecar 代理。

与虚拟机支持的关系

请注意,本文中描述的场景不同于 带虚拟机的 Bookinfo 示例。在该场景中,MySQL 实例运行在外部(集群外部)机器(裸机或 VM)上,并与 Istio 服务网格集成。MySQL 服务成为网格中的一等公民,适用于 Istio 的所有有益功能。除其他事项外,该服务可以通过本地集群域名访问,例如通过mysqldb.vm.svc.cluster.local,并且可以对其进行通信安全保护 双向 TLS 身份验证。无需创建服务条目即可访问此服务;但是,必须将该服务注册到 Istio。要启用这种集成,必须在机器上安装 Istio 组件(Envoy 代理节点代理_istio-agent_),并且 Istio 控制平面(PilotMixerCitadel)必须可以从该机器访问。有关更多详细信息,请参见 Istio VM 相关 任务。

在本例中,MySQL 实例可以在任何机器上运行,也可以由云提供商作为服务进行配置。没有要求将机器与 Istio 集成。Istio 控制平面无需从机器上访问。对于 MySQL 作为服务,MySQL 运行的机器可能不可访问,并且可能无法在其上安装所需的组件。在本例中,MySQL 实例可以通过其全局域名访问,这在使用该域名的应用程序预期使用该域名时可能会有所帮助。这在预期域名无法在使用应用程序的部署配置中更改时特别有用。

清理

  1. 删除test 数据库和bookinfo 用户

    $ mysqlsh --sql --ssl-mode=REQUIRED -u admin -p --host $MYSQL_DB_HOST --port $MYSQL_DB_PORT -e "drop database test; drop user bookinfo;"
    

    或者

    对于 mysql 和本地数据库

    $ mysql -u root -p --host $MYSQL_DB_HOST --port $MYSQL_DB_PORT -e "drop database test; drop user bookinfo;"
    
  2. 删除虚拟服务

    压缩
    $ kubectl delete -f @samples/bookinfo/networking/virtual-service-ratings-mysql.yaml@
    Deleted config: virtual-service/default/reviews
    Deleted config: virtual-service/default/ratings
    
  3. 取消部署ratings v2-mysql

    压缩
    $ kubectl delete -f @samples/bookinfo/platform/kube/bookinfo-ratings-v2-mysql.yaml@
    deployment "ratings-v2-mysql" deleted
    
  4. 删除服务条目

    $ kubectl delete serviceentry mysql-external -n default
    Deleted config: serviceentry mysql-external
    

结论

在这篇博文中,我演示了 Istio 服务网格中的微服务如何通过 TCP 使用外部服务。默认情况下,Istio 会阻止所有流量,TCP 和 HTTP,到集群外部的主机。要为 TCP 启用此类流量,必须为服务网格创建 TCP 网格外服务条目。

分享这篇文章