使用 Anthos Service Mesh 保护和加密 Anthos 集群之间的通信

本教程介绍如何使用 Anthos Service Mesh 出站和入站网关,通过双向传输层安全协议 (mTLS) 来保护集群间流量。本教程适用于负责网络、安全和平台方面的 Kubernetes 集群管理员。对于具有更高安全要求或需要满足监管前提条件的组织,此处所述的控制措施可能尤为有用。本教程随附配套的概念指南

本教程假定您熟悉 Kubernetes 和 Anthos Service Mesh。

目标

  • 使用 Terraform 设置基础架构:
    • 创建包含两个专用子网的自定义 VPC 网络。
    • 创建两个启用 Anthos Service Mesh 的 Kubernetes 集群:
    • 将集群注册到 GKE Hub。
  • 在 GKE 集群上部署 MySQL 客户端。
  • 在 kOps 集群上部署 MySQL 服务器。
  • 配置出站网关和入站网关以使用 mTLS 公开服务器。
  • 使用在不同集群或 VPC 中运行的 MySQL 客户端测试对 MySQL 服务器的访问。

费用

本教程使用 Google Cloud 的以下收费组件:

您可使用价格计算器根据您的预计使用量来估算费用。 Google Cloud 新用户可能有资格申请免费试用

完成本教程后,您可以删除所创建的资源以避免继续计费。如需了解详情,请参阅清理

准备工作

在本教程中,您需要一个 Google Cloud 项目。您可创建一个新项目,也可选择已创建的项目:

  1. 在 Google Cloud Console 中,转到项目选择器页面。

    转到“项目选择器”

  2. 选择或创建 Google Cloud 项目。

  3. 确保您的 Cloud 项目已启用结算功能。 了解如何确认您的项目是否已启用结算功能

  4. 在 Cloud Console 中,转到 Cloud Shell。

    转到 Cloud Shell

    Cloud Shell 会话随即会在 Cloud Console 的底部打开,并显示命令行提示。Cloud Shell 是一个已安装 Cloud SDK 的 Shell 环境,其中包括 gcloud 命令行工具。该会话可能需要几秒钟来完成初始化。

  5. 在 Cloud Shell 中,请确保您正在自己创建或选择的项目中执行操作:
    export PROJECT_ID=PROJECT_ID
    gcloud config set project ${PROJECT_ID}
    

    PROJECT_ID 替换为您的项目 ID。

  6. 为您用于 Google Cloud 的电子邮件地址创建环境变量:
    export GOOGLE_CLOUD_EMAIL_ADDRESS=GOOGLE_CLOUD_EMAIL_ADDRESS
    

    GOOGLE_CLOUD_EMAIL_ADDRESS 替换为您在 Google Cloud 中使用的电子邮件地址。

  7. 为计算资源设置地区和区域:
    export REGION=us-central1
    export ZONE=us-central1-b
    gcloud config set compute/region ${REGION}
    gcloud config set compute/zone ${ZONE}
    

    本教程使用 us-central1 作为区域,使用 us-central1-b 作为可用区。您可以部署到您选择的区域

  8. 设置所需的 Identity and Access Management (IAM) 角色。如果您是 Project Owner,则拥有完成安装所需的全部权限。否则,请让管理员在 Cloud Shell 中运行以下命令,授予您 Identity and Access Management (IAM) 角色:
    ROLES=(
    'roles/container.admin' \
    'roles/gkehub.admin' \
    'roles/iam.serviceAccountAdmin' \
    'roles/iam.serviceAccountKeyAdmin' \
    'roles/resourcemanager.projectIamAdmin' \
    'roles/compute.securityAdmin' \
    'roles/compute.instanceAdmin' \
    'roles/storage.admin' \
    'roles/serviceusage.serviceUsageAdmin'
    )
    for role in "${ROLES[@]}"
    do
     gcloud projects add-iam-policy-binding ${PROJECT_ID} \
      --member "user:${GOOGLE_CLOUD_EMAIL_ADDRESS}" \
      --role="$role"
    done
    
  9. 启用本教程所需的 API:
    gcloud services enable \
        anthos.googleapis.com \
        anthosgke.googleapis.com \
        anthosaudit.googleapis.com \
        compute.googleapis.com \
        container.googleapis.com \
        cloudresourcemanager.googleapis.com \
        serviceusage.googleapis.com \
        stackdriver.googleapis.com \
        monitoring.googleapis.com \
        logging.googleapis.com \
        cloudtrace.googleapis.com \
        meshca.googleapis.com \
        meshtelemetry.googleapis.com \
        meshconfig.googleapis.com \
        iamcredentials.googleapis.com \
        gkeconnect.googleapis.com \
        gkehub.googleapis.com
    

准备环境

  1. 在 Cloud Shell 中,克隆以下 Git 代码库:

    git clone https://github.com/GoogleCloudPlatform/anthos-service-mesh-samples
    cd anthos-service-mesh-samples/docs/mtls-egress-ingress
    
  2. 为环境更新 Terraform。默认情况下,Cloud Console 附带 Terraform 0.12。本教程假定您已安装 Terraform 0.13.5 或更高版本。您可以通过运行以下命令暂时使用其他版本的 Terraform:

    mkdir ~/bin
    curl https://releases.hashicorp.com/terraform/0.13.5/terraform_0.13.5_linux_amd64.zip -o ~/bin/terraform.zip
    unzip ~/bin/terraform.zip -d ~/bin/
    
  3. 转到 terraform 子文件夹并初始化 Terraform:

    cd terraform/
    ~/bin/terraform init
    
  4. 创建一个 terraform.tfvars 文件(基于您之前创建的环境变量):

    cat << EOF > terraform.tfvars
    project_id = "${PROJECT_ID}"
    region = "${REGION}"
    zones = ["${ZONE}"]
    EOF
    

    在下一步中,您将创建初始基础架构。为此,您需要为此配置创建并应用 Terraform 执行计划。此计划中的脚本和模块会创建以下内容:

    • 包含两个专用子网的自定义 VPC 网络
    • 两个启用 Anthos Service Mesh 的 Kubernetes 集群
    • 一个 GKE 集群
    • 一个在自定义 VPC 网络中运行的 kOps 集群

    该执行计划还会将集群注册到 GKE Hub。

  5. 运行执行计划:

    ~/bin/terraform plan -out mtls-terraform-plan
    ~/bin/terraform apply "mtls-terraform-plan"
    

    输出内容类似如下:

    Apply complete! Resources: 27 added, 0 changed, 0 destroyed.
    Outputs:
    server_token = <sensitive>
    

    <sensitive> 部分是 Terraform 输出变量,不显示在控制台中,但可以查询,例如 ~/bin/terraform output server_token

  6. terraform 目录获取您的服务器集群 kubeconfig 文件。然后,将其与 client-cluster 配置文件合并:

    cd ..
    export KUBECONFIG=client-server-kubeconfig
    cp ./terraform/server-kubeconfig $KUBECONFIG
    gcloud container clusters get-credentials client-cluster --zone ${ZONE} --project ${PROJECT_ID}
    

    client-server-kubeconfig 文件现在包含这两个集群的配置,您可以通过运行以下命令进行验证:

    kubectl config view -ojson | jq -r '.clusters[].name'
    

    输出如下所示:

    gke_PROJECT_ID_us-central1-c_client-cluster
    server-cluster.k8s.local
    
  7. 获取这两个集群的上下文供稍后使用:

    export CLIENT_CLUSTER=$(kubectl config view -ojson | jq -r '.clusters[].name' | grep client)
    export SERVER_CLUSTER=$(kubectl config view -ojson | jq -r '.clusters[].name' | grep server)
    echo -e "${CLIENT_CLUSTER}\n${SERVER_CLUSTER}"
    

    输出(再次)如下所示:

    gke_PROJECT_ID_us-central1-c_client-cluster
    server-cluster.k8s.local
    

    现在,您可以将这些集群名称用作后续 kubectl 命令的上下文。

  8. 引用客户端集群:

    kubectl --context ${CLIENT_CLUSTER} get pods -n istio-system
    
  9. 引用服务器集群:

    kubectl --context ${SERVER_CLUSTER} get pods -n istio-system
    

配置客户端

概念指南中所述,客户端要求您在 Anthos Service Mesh 中配置出站网关。

在本部分中,您将配置 Anthos Service Mesh 元素,以根据来源识别外部流量并使用自定义证书加密通信。此外,您希望仅将该流量路由到其目的地(容器中的 MySQL 数据库)。通常使用 Kubernetes 中的 Service 来实现此目的。在此示例中,您需要在网格通信内捕获该流量。要捕获流量,您需要使用 Istio 元素创建特殊的服务定义。您需要定义以下元素:

  • 出站网关
  • 服务条目
  • 虚拟服务
  • TLS 证书(作为 Secret)
  • 目标规则
  1. 在 Cloud Shell 中,使用服务器端的上下文(您之前创建的 $SERVER_CLUSTER)查询 istio-ingressgateway 服务的负载平衡器 IP 地址,从而获取服务器端入站网关的 IP 地址:

    INGRESS_HOST=$(kubectl -n istio-system --context ${SERVER_CLUSTER} get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
    

    由于 INGRESS_HOST 只是主机的 IP 地址部分,您需要创建完全限定域名 (FQDN)。此步骤是必需的,因为证书需要域名才能发挥作用。

    在本教程中,您可以使用通配符 DNS 服务 nip.io 为入站 IP 地址创建 FQDN。此服务允许您无需拥有网域也可创建 FQDN。

  2. 将 FQDN 服务网址存储在环境变量中:

    export SERVICE_URL="${INGRESS_HOST}.nip.io"
    

    SERVICE_URL 定义为 FQDN 后,您可以开始定义客户端集群的 Istio 部分。

创建出站网关

首先,您需要创建出站网关,以侦听发往外部服务的流量。

侦听发往外部服务的流量的出站网关。

  1. 在 Cloud Shell 中,创建以下 YAML 文件并将其命名为 client-egress-gateway.yaml

    cat <<EOF > client-egress-gateway.yaml
    apiVersion: networking.istio.io/v1alpha3
    kind: Gateway
    metadata:
     name: istio-egressgateway-mysql
    spec:
     selector:
       istio: egressgateway
     servers:
     - port:
         number: 15443
         name: tls
         protocol: TLS
       hosts:
       - $SERVICE_URL
       tls:
         mode: ISTIO_MUTUAL
    EOF
    
  2. 将上述 YAML 文件应用于客户端集群:

    kubectl --context ${CLIENT_CLUSTER} apply -f client-egress-gateway.yaml
    

    注意端口。在此处,您将 default 端口用于出站服务器开关,即 15443。如果要使用其他端口,则需要修改出站网关 service 对象以添加自定义端口。

    hosts 开关定义了端点,即流量的目的地。

定义服务条目

下一步是将外部服务告知服务网格。Istio 拥有自己的注册表,用于为网格存储服务端点。如果 Istio 安装在 Kubernetes 之上,集群中定义的服务会自动添加到 Istio 注册表中。通过服务条目定义,您可以将新端点添加到 Istio 注册表,如下图所示。

使用服务条目定义将端点添加到 Istio 注册表。

  1. 在 Cloud Shell 中,创建以下 YAML 文件并将其命名为 client-service-entry.yaml

    cat <<EOF > client-service-entry.yaml
    apiVersion: networking.istio.io/v1alpha3
    kind: ServiceEntry
    metadata:
     name: mysql-external
    spec:
     hosts:
       - $SERVICE_URL
     location: MESH_EXTERNAL
     ports:
       - number: 3306
         name: tcp
         protocol: TCP
       - number: 13306
         name: tls
         protocol: TLS
     resolution: DNS
     endpoints:
       - address: $SERVICE_URL
         ports:
           tls: 13306
    EOF
    
  2. 将上述 YAML 文件应用于客户端集群:

    kubectl --context ${CLIENT_CLUSTER} apply -f client-service-entry.yaml
    

    此 YAML 文件中的客户端定义会向服务指明预期的流量类型(MySQL L4 - 网络第 4 层,使用端口 3306)。您还定义通信将流向“网格外部”。在端点部分,您定义流量应流向您之前设置的 FQDN 地址 $SERVICE_URL,该地址映射到服务器集群 (kOps) 上的入站网关。

定义虚拟服务

虚拟服务是定向到主机时要应用的流量路由规则。每条路由规则为特定协议的流量定义匹配条件。如果流量匹配,则被发送到注册表中定义的指定目标服务(或其子集或某个版本)。如需了解详情,请参阅 Istio 文档

定义告诉 Istio 如何对访问外部服务的流量应用路由的虚拟服务。

虚拟服务定义告诉 Istio 如何对访问外部服务的流量应用路由。通过以下定义,您告诉网格将流量从客户端路由到端口 15443 上的出站网关。然后,从出站网关将流量路由到端口 13306(由服务器端入站网关侦听)上的主机 $SERVICE_URL

  1. 创建以下 YAML 文件并将其命名为 client-virtual-service.yaml

    cat <<EOF > client-virtual-service.yaml
    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
     name: direct-mysql-through-egress-gateway
    spec:
     hosts:
       - $SERVICE_URL
     gateways:
       - istio-egressgateway-mysql
       - mesh
     tcp:
       - match:
           - gateways:
               - mesh
             port: 3306
         route:
           - destination:
               host: istio-egressgateway.istio-system.svc.cluster.local
               subset: mysql
               port:
                 number: 15443
             weight: 100
       - match:
           - gateways:
               - istio-egressgateway-mysql
             port: 15443
         route:
           - destination:
               host: $SERVICE_URL
               port:
                 number: 13306
             weight: 100
    EOF
    
  2. 将该 YAML 定义应用于客户端集群:

    kubectl --context ${CLIENT_CLUSTER} apply -f client-virtual-service.yaml
    

    您可以通过修改 YAML 文件中的 gateways 开关来指定向哪些网关应用配置。

    此定义中的重要部分是保留字 mesh 的使用,这表示网格中的所有 Sidecar。根据 Istio 文档,如果省略此字段,则使用默认网关(网格),并将规则应用于网格中的所有 Sidecar。如果您提供网关名称列表,则规则仅应用于这些网关。要将规则应用于网关和 Sidecar,请将 mesh 指定为网关名称之一。

在下一部分中,您将定义如何处理来自客户端 prod 代理 (match.gateways.mesh) 的流量。您还将使用 match.gateways.istio-egressgateway-mysql 开关定义如何将来自出站网关的流量路由到外部服务。

定义目标规则(从客户端到出站网关)

您已定义如何将流量路由到外部服务,还需要定义适用的流量政策。您刚才定义的虚拟服务一次处理两个路由事件。一个处理从 Sidecar 代理到出站网关的流量,另一个处理从出站网关到外部服务的流量。

要将这些事件与目标规则相匹配,您需要两条单独的规则。下图显示了第一条规则,它处理从代理到出站网关的流量。在此定义中,您指示 Anthos Service Mesh 将默认证书用于 mTLS 通信。

定义如何处理从 Sidecar 代理到出站网关的流量的目标规则。

  1. 在 Cloud Shell 中,创建以下 YAML 文件并将其命名为 client-destination-rule-to-egress-gateway.yaml

    cat <<EOF > client-destination-rule-to-egress-gateway.yaml
    apiVersion: networking.istio.io/v1alpha3
    kind: DestinationRule
    metadata:
      name: egressgateway-for-mysql
    spec:
      host: istio-egressgateway.istio-system.svc.cluster.local
      subsets:
        - name: mysql
          trafficPolicy:
            loadBalancer:
              simple: ROUND_ROBIN
            portLevelSettings:
              - port:
                  number: 15443
                tls:
                  mode: ISTIO_MUTUAL
                  sni: $SERVICE_URL
    EOF
    
  2. 将上述 YAML 定义应用于客户端集群:

    kubectl --context ${CLIENT_CLUSTER} apply -f client-destination-rule-to-egress-gateway.yaml
    

    在上面的 YAML 文件中,您使用 hosts 开关来定义如何将流量从客户端代理路由到出站网关。此外,您将网格配置为在端口 15443 上使用 ISTIO_MUTUAL,该端口是出站网关上自动打开的端口之一。

创建目标规则(从出站网关到外部服务)

下图展示了第二条目标规则,它告诉网格如何处理从出站网关到外部服务的流量。

第二条目标规则,定义如何处理从出站网关到外部服务的流量。

您需要指示网格使用注入的证书与外部服务进行双向 TLS 通信。

  1. 在 Cloud Shell 中,从 anthos-service-mesh-samples/docs/mtls-egress-ingress 目录创建证书:

     ./create-keys.sh
    

    请务必在脚本要求时提供密码

  2. 将生成的文件复制到当前目录:

    cp ./certs/2_intermediate/certs/ca-chain.cert.pem ca-chain.cert.pem
    cp ./certs/4_client/private/$SERVICE_URL.key.pem client-$SERVICE_URL.key.pem
    cp ./certs/4_client/certs/$SERVICE_URL.cert.pem client-$SERVICE_URL.cert.pem
    
  3. 创建用于存储证书的 Kubernetes Secret,以便稍后在网关中引用它们:

     kubectl --context ${CLIENT_CLUSTER} create secret -n istio-system \
      generic client-credential \
      --from-file=tls.key=client-$SERVICE_URL.key.pem \
      --from-file=tls.crt=client-$SERVICE_URL.cert.pem \
      --from-file=ca.crt=ca-chain.cert.pem
    

    上述命令会将以下证书文件添加到 Secret 中:

    client-$SERVICE_URL.key.pem
    client-$SERVICE_URL.cert.pem
    ca-chain.cert.pem
    

    此处使用了 Secret Discovery Service (SDS)(而不是文件装载),这是分发证书的当前最佳做法。这种做法可以避免在添加新证书时重启 pod。从 Istio 1.8/1.9 开始,此技术使您不再需要网关 Secret 的读取权限 (RBAC)。

  4. 将证书添加到 DestinationRule,并将其命名为 client-destination-rule-to-external-service.yaml

    cat <<EOF > client-destination-rule-to-external-service.yaml
    apiVersion: networking.istio.io/v1alpha3
    kind: DestinationRule
    metadata:
     name: originate-mtls-for-mysql
    spec:
     host: $SERVICE_URL
     trafficPolicy:
       loadBalancer:
         simple: ROUND_ROBIN
       portLevelSettings:
       - port:
           number: 13306
         tls:
           mode: MUTUAL
           credentialName: client-credential
           sni: $SERVICE_URL
    EOF
    
  5. 将上述 YAML 定义应用于客户端集群:

    kubectl --context ${CLIENT_CLUSTER} apply -f client-destination-rule-to-external-service.yaml
    

    此规则仅在预先创建了 Secret 的情况下有效。Secret 可确保证书用于从出站网关到外部端点的 mTLS 加密。

完成这些步骤后,客户端设置即完成并如下图所示。

最终的客户端设置。

配置服务器端

概念指南中所述,对于服务器端,您需要在 Anthos Service Mesh 中配置入站网关。在创建必要的文件之前,最好回顾一下实现解决方案的服务器部分所需的组件。

基本上,您希望根据来源和证书来识别传入流量。此外,您希望仅将该流量路由到其目的地,即容器中的 MySQL 数据库。通常使用 Kubernetes 中的 Service 来实现此目的,但在本示例中,您需要识别网格通信内的传入流量,因此需要特殊的服务定义,其中包括以下元素:

  • TLS 证书(作为 Secret)
  • 入站流量网关
  • 虚拟服务

为入站网关创建 Secret

与出站网关一样,您需要相同的证书来保护入站网关的通信。为此,您需要将证书存储在 Secret 中,并使用入站网关对象定义此 Secret。

  1. 在 Cloud Shell 中,将生成的文件复制到您要执行下一个命令的位置:

    cp ./certs/3_application/private/$SERVICE_URL.key.pem server-$SERVICE_URL.key.pem
    cp ./certs/3_application/certs/$SERVICE_URL.cert.pem server-$SERVICE_URL.cert.pem
    
  2. 创建服务器密 Secret:

    kubectl --context ${SERVER_CLUSTER} create secret -n istio-system \
        generic mysql-credential \
        --from-file=tls.key=server-$SERVICE_URL.key.pem \
        --from-file=tls.crt=server-$SERVICE_URL.cert.pem \
        --from-file=ca.crt=ca-chain.cert.pem
    

    您向 Secret 添加了以下证书文件:

    server-$SERVICE_URL.key.pem
    server-$SERVICE_URL.cert.pem
    ca-chain.cert.pem
    

定义入站网关

要从客户端集群接收流量,您需要指定可以使用证书解密和验证 TLS 通信的入站网关。

定义检查流量是否符合安全和转发条件的入站网关。

上图显示了入站网关在集群中的位置。流量通过时,网关会检查其是否符合安全和转发条件。

  1. 在 Cloud Shell 中,使用以下 YAML 文件并将其命名为 server-ingress-gatway.yaml

    cat <<EOF > server-ingress-gatway.yaml
    apiVersion: networking.istio.io/v1alpha3
    kind: Gateway
    metadata:
     name: gateway-mysql
    spec:
     selector:
       istio: ingressgateway # Istio default gateway implementation
     servers:
     - port:
         number: 13306
         name: tls-mysql
         protocol: TLS
       tls:
         mode: MUTUAL
         credentialName: mysql-credential
       hosts:
       - "$SERVICE_URL"
    EOF
    
  2. 将上述 YAML 定义应用于客户端集群:

    kubectl --context ${SERVER_CLUSTER} apply -f server-ingress-gatway.yaml
    

    请注意 tls: 部分,因为它特别重要。在本部分中,您定义要使用 mTLS。为确保按预期实现,您需要提供您创建的包含证书的 Secret。

  3. 通过修补入站流量服务来启用端口 13306。创建以下 JSON 文件并将其命名为 gateway-patch.json,以启用此端口:

    cat <<EOF > gateway-patch.json
    [{
      "op": "add",
      "path": "/spec/ports/0",
      "value": {
        "name": "tls-mysql",
        "protocol": "TCP",
        "targetPort": 13306,
        "port": 13306
      }
    }]
    EOF
    
  4. 将补丁程序应用于网关服务:

    kubectl --context ${SERVER_CLUSTER} -n istio-system patch --type=json svc istio-ingressgateway -p "$(cat gateway-patch.json)"
    

现在您已在入站网关上打开端口,接下来需要提取来自新的入站网关的流量并将其定向到数据库 pod。您可以使用网格内部对象(虚拟服务)来实现此目的。

定义虚拟服务

如前所述,虚拟服务是影响网格内流量的流量匹配模式的定义。您需要正确识别来自入站网关的流量并将其转发到 MySQL 数据库服务,如下图所示。

识别来自入站网关的流量并将其转发到 MySQL 数据库服务。

  1. 在 Cloud Shell 中,创建以下 YAML 文件并将其命名为 server-virtual-service.yaml

    cat <<EOF > server-virtual-service.yaml
    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
     name: mysql-virtual-service
    spec:
     hosts:
       - "$SERVICE_URL"
     gateways:
       - gateway-mysql
     tcp:
       - route:
         - destination:
             port:
               number: 3306
             host: mysql.default.svc.cluster.local
    EOF
    

    此虚拟服务必须引用流量源自的入站网关,这一点很重要。网关命名为 gateway-mysql

    由于此虚拟服务应用于 L4,您需要提供 tcp 标志来描述 MySQL 流量。此标志实质上是告诉网格,此流量使用 L4。

    您可能已经注意到,入站流量服务使用端口 13306 转发流量。虚拟服务会提取该端口并将其转换回 3306

    最后,流量被转发到服务器 Kubernetes 集群中的 MySQL 服务器。在本示例中,服务器侦听标准 MySQL 端口 3306

  2. 将 YAML 定义应用于服务器集群:

    kubectl --context ${SERVER_CLUSTER} apply -f server-virtual-service.yaml
    

这两个定义解密 mTLS 封装的 MySQL 客户端请求,并将其转发到网格内的 MySQL 数据库。

您有必要了解,网格内部转发也是使用加密完成的,但在这种情况下,加密基于网格内部证书。mTLS 终止发生在网关处。

现在,您已经拥有与 MySQL 服务器的完整的双向加密通信方法。这种加密形式对 MySQL 客户端和服务器是透明的,因此无需更改应用。这可以使更改或轮替证书等任务变得简单。此外,这种通信方式可用于许多不同的场景。

测试设置

客户端和服务器端已准备就绪,现在您可以测试设置了。

测试从客户端到服务器端的流量。

现在可以生成一些从客户端到服务器端的流量。您希望跟踪流量,并确保流量按预期路由、加密和解密。

  1. 在 Cloud Shell 中,将 MySQL 服务器部署到服务器集群中:

    kubectl --context ${SERVER_CLUSTER} apply -f server/mysql-server/mysql.yaml
    
  2. 在客户端集群上启动 MySQL 客户端:

    kubectl run --context ${CLIENT_CLUSTER} --env=SERVICE_URL=$SERVICE_URL -it --image=mysql:5.6 mysql-client-1 --restart=Never -- /bin/bash
    

    容器启动后,您会看到一个如下所示的 shell:

    root@mysql-client-1:/#
    
  3. 连接到 MySQL 服务器:

    mysql -pyougottoknowme -h $SERVICE_URL
    
  4. 使用以下命令添加数据库和表:

    CREATE DATABASE test_encrypted_connection;
    USE test_encrypted_connection;
    CREATE TABLE message (id INT, content VARCHAR(20));
    INSERT INTO message (id,content) VALUES(1,"Crypto Hi");
    
  5. 连接并添加表后,退出 MySQL 连接和 pod:

    exit
    exit
    

    您需要输入两次 exit,第一次退出数据库连接,第二次退出 pod。如果 pod 在退出时停止响应,请按 Control+C 取消 bash shell。

通过以上步骤,您应该已经生成了一些有意义的日志记录输出,现在可以进一步分析。

在下一部分中,您将检查客户端流量是否通过代理和出站网关。您还将测试能否看到流量通过入站网关进入服务器端。

测试客户端代理和出站网关

  1. 在 Cloud Shell 中,在客户端代理上,检查是否可以看到 Istio 代理日志:

    kubectl --context ${CLIENT_CLUSTER} logs -l run=mysql-client-1 -c istio-proxy -f
    

    调试输出类似于以下内容:

    [2021-02-10T21:19:08.292Z] "- - -" 0 - "-" "-" 176 115 10 - "-" "-" "-" "-" "192.168.1.4:15443" outbound|15443|mysql|istio-egressgateway.istio-system.svc.cluster.local 192.168.1.12:58614 34.66.165.46:3306 192.168.1.12:39642 - -
    

    Ctrl+C 退出日志输出。

    在此日志条目中,您可以看到客户端请求在 IP 地址 34.66.165.46 的端口 3306 上运行的服务器。请求被转发 (outbound) 到侦听 IP 地址 192.168.1.4 端口 15443istio-egressgateway。您在虚拟服务 (client-virtual-service.yaml) 中定义了此转发。

  2. 读取出站网关代理日志:

    kubectl --context ${CLIENT_CLUSTER} logs -n istio-system -l app=istio-egressgateway -f
    

    调试输出类似于以下内容:

    [2021-02-10T21:19:08.292Z] "- - -" 0 - "-" "-" 176 115 19 - "-" "-" "-" "-" "34.66.165.46:13306" outbound|13306||34.66.165.46.nip.io 192.168.1.4:53542 192.168.1.4:15443 192.168.1.12:58614 34.66.165.46.nip.io -
    

    Ctrl+C 退出日志输出。

    在此日志条目中,您可以看到路由到侦听 IP 地址 192.168.1.4 端口 15443istio-egressgateway 的客户端请求被进一步路由到侦听 IP 地址 34.66.165.46 端口 13306. 的服务网格外部服务。您在虚拟服务 (client-virtual-service.yaml) 的第二部分中定义了此转发。

测试服务器端入站网关

  1. 在 Cloud Shell 中,在服务器端,查看入站网关代理日志:

    kubectl --context ${SERVER_CLUSTER} logs -n istio-system -l app=istio-ingressgateway -f
    

    日志中的输出类似于以下内容:

    [2021-02-10T21:22:27.381Z] "- - -" 0 - "-" "-" 0 78 5 - "-" "-" "-" "-" "100.96.4.8:3306" outbound|3306||mysql.default.svc.cluster.local 100.96.1.3:55730 100.96.1.3:13306 100.96.1.1:42244 34.66.165.46.nip.io -
    

    Ctrl+C 退出日志输出。

    在此日志条目中,您可以看到路由到侦听 IP 地址 34.66.165.46 端口 13306istio-ingressgateway 的外部客户端请求被进一步路由到端口 3306. 上的网格内 MySQL 服务 mysql.default.svc.cluster.local。您在入站网关 (server-ingress-gateway.yaml) 中定义了此转发。

  2. 对于 MySQL 服务器,查看 Istio 代理日志:

    kubectl --context ${SERVER_CLUSTER} logs -l app=mysql -c istio-proxy -f
    

    输出类似于以下内容:

    [2021-02-10T21:22:27.382Z] "- - -" 0 - "-" "-" 1555 1471 4 - "-" "-" "-" "-" "127.0.0.1:3306" inbound|3306|mysql|mysql.default.svc.cluster.local 127.0.0.1:45894 100.96.4.8:3306 100.96.1.3:55730 outbound_.3306_._.mysql.default.svc.cluster.local -
    

    Ctrl+C 退出日志输出。

    在此日志条目中,您可以看到对侦听 IP 地址 100.96.4.8 端口 3306 的 MySQL 数据库服务器的入站调用。该调用来自 IP 地址为 100.96.1.3 的 Ingress pod。

    如需详细了解 Envoy 日志记录格式,请参阅获取 Envoy 的访问日志

  3. 测试数据库以查看生成的输入:

    MYSQL=$(kubectl --context ${SERVER_CLUSTER} get pods -n default | tail -n 1 | awk '{print $1}')
    kubectl --context ${SERVER_CLUSTER} exec $MYSQL -ti -- /bin/bash
    
  4. 验证创建的数据库:

    mysql -pyougottoknowme
    USE test_encrypted_connection;
    SELECT * from message;
    

    输出内容类似如下:

    +------+-----------+
    | id   | content   |
    +------+-----------+
    |    1 | Crypto Hi |
    +------+-----------+
    1 row in set (0.00 sec)
    
  5. 退出 MySQL 数据库:

    exit
    exit
    

    您需要输入两次 exit,第一次退出数据库连接,第二次退出 pod。

通过省略证书测试访问

您已经测试并验证,使用注入证书时,访问可正常进行,现在可以测试反过来的情况:如果省略出站网关和证书注入,会发生什么。此测试也称为负面测试

要执行此测试,您可以在命名空间中启动另一个 pod,并且不启用代理注入。

  1. 在 Cloud Shell 中,创建一个新的命名空间:

    kubectl --context ${CLIENT_CLUSTER} create ns unencrypted
    
  2. 创建 pod 并在容器内启动交互式 shell:

    kubectl --context ${CLIENT_CLUSTER} run -it --image=mysql:5.6 \
    mysql-client-2 --env=SERVICE_URL=$SERVICE_URL \
    -n unencrypted --restart=Never -- /bin/bash
    
  3. 在交互式 shell 启动后尝试连接到数据库:

    mysql -pyougottoknowme -h $SERVICE_URL
    

    30 秒后,您将看到如下所示的输出:

    Warning: Using a password on the command line interface can be insecure.
    ERROR 2003 (HY000): Can't connect to MySQL server on '104.154.164.12.nip.io' (110)
    

    此警告是预期现象,因为此 pod 省略了出站网关,并尝试通过互联网直接访问入站网关 ($SERVICE_URL)。

  4. 尝试解析服务 IP 地址:

    resolveip $SERVICE_URL
    

    输出类似于以下内容:您的 IP 地址会有所不同。

    IP address of 104.154.164.12.nip.io is 104.154.164.12
    

    这证明 FQDN 可解析,并且连接失败确实是缺少证书注入导致的。

  5. 退出 MySQL 连接和 MySQL 服务器 pod:

    exit
    exit
    

进一步调查

本教程未介绍的一个主题是,出站配置通常由公司中的另一个角色或组织拥有,因为它们托管在 istio-system 命名空间中。请配置 Kubernetes RBAC 权限,使得只有网络管理员可以直接创建和修改本教程中讨论的资源。

您已经知道如何使用服务网格来确保安全通信,您可以将其与需要安全交换数据的应用搭配使用,或者在需要在证书层控制加密的使用场景中使用。首先,您可以安装 Anthos Service Mesh

尝试使用两个 GKE 集群,并使用本教程中的技术将其组合到一起。此技术也适用于两个外部 Kubernetes 集群之间的 Anthos 平台。

服务网格是一种提高集群内部安全性和外部服务安全性的绝佳方法。最后一个尝试的使用场景是使用不是第二个 Kubernetes 集群而是第三方提供商(例如付款服务机构)的 mTLS 端点。

清除数据

为避免系统因本教程中使用的资源向您的 Google Cloud 帐号收取费用,您可以删除您的项目。

删除项目

  1. 在 Cloud Console 中,转到管理资源页面。

    转到“管理资源”

  2. 在项目列表中,选择要删除的项目,然后点击删除
  3. 在对话框中输入项目 ID,然后点击关闭以删除项目。

后续步骤