使用 Istio 多集群在集群之间“突增”工作负载

1. 欢迎

感谢您参加 Google 的 Istio 多云突发 Codelab。本 Codelab 需要具备 Kubernetes、Node 和 Go 的初级实操经验。

所需条件

  • Google Cloud Platform 账号(使用现有账号,或者我们将提供免费账号)
  • 您的笔记本电脑(安装“kubectl”“gcloud”等),或者您可以使用 Google Cloud Shell。

学习内容

  • 如何在 GKE 上创建 Kubernetes 集群
  • 如何使用 Helm 在 Kubernetes 集群上安装 Istio
  • 如何使用 Helm 安装 Istio 多集群
  • 将 Web 应用从源代码部署到 Kubernetes
  • 为 Istio 编写和应用流量路由规则
  • Prometheus 指标
  • 在 Kubernetes 集群内构建和推送容器映像

2. 准备工作

您可以在以下任一设备上学习此 Codelab:

  • Google Cloud Shell (推荐):浏览器内 Shell,附带已安装的工具
  • 笔记本电脑(请按照以下说明操作)

开始使用 Google Cloud Platform

  1. 如果您没有 GCP 账号,请向讲师领取免费的用户账号卡。
  2. 前往 Google Cloud 控制台,然后点击“选择项目”图标:5c2d9bf74c78f7e4.png
  3. 记下项目的“ID”,然后点击该项目以选择它:ecc5e8e97bfa6559.png

Cloud Shell 在浏览器中提供了一个命令行 shell,其中安装了您需要的工具,并且会自动针对您的 Google Cloud Platform 账号进行身份验证。(如果您不想在 Cloud Shell 中运行此练习,请跳到下一部分。)

前往 Cloud 控制台,然后点击右上角工具栏中的“激活 Cloud Shell”:

68a17b036ce24ccb.png

向 Cloud Shell 添加工具

  1. 安装 kubectx****:通过将 bash 脚本从此处下载到 $PATH 中的某个位置。
  2. 安装 helm****:按照这些说明操作。

或者,运行以下命令将两者都安装到 ~/.bin 并将其添加到您的 $PATH:

mkdir -p ~/.bin && \
cd ~/.bin && \
curl -LO https://raw.githubusercontent.com/ahmetb/kubectx/master/kubectx && \
chmod +x kubectx && \
curl -LO https://raw.githubusercontent.com/ahmetb/kubectx/master/kubens && \
chmod +x kubens && \
curl -LO  https://storage.googleapis.com/kubernetes-helm/helm-v2.12.0-linux-amd64.tar.gz && \
tar xzf helm-v2.12.0-linux-amd64.tar.gz && \
rm helm-v2.12.0-linux-amd64.tar.gz && \
mv linux-amd64/helm ./helm && \
rm -r linux-amd64 && \
export PATH=${HOME}/.bin:${PATH}

以下是一些快速提示,可帮助您更轻松地使用 Cloud Shell:

1. 将 shell 分离到新窗口中

2. 使用文件编辑器: 点击右上角的铅笔图标,启动浏览器内文件编辑器。在将代码段复制到文件时,您会发现此功能非常实用。

3. 启动新标签页:如果您需要多个终端提示。

4. 放大文字: Cloud Shell 中的默认字号可能太小,难以阅读。

在 Linux/Windows 上按 Ctrl-+,在 macOS 上按 ⌘-。

如果您更喜欢使用自己的工作站环境而不是 Cloud Shell,请设置以下工具:

  1. 安装 gcloud:(已预安装在 Cloud Shell 上)。按照说明在您的平台上安装 gcloud。我们将使用此配置来创建 Kubernetes 集群。
  2. 安装 kubectl:(已预安装在 Cloud Shell 上)。运行以下命令以进行安装:
gcloud components install kubectl

运行以下命令以对 gcloud 进行身份验证。系统会要求您使用 Google 账号登录。然后,选择预先创建的项目(如上所示)作为默认项目。(您可以跳过配置计算可用区):

gcloud init
  1. 安装 curl: 大多数 Linux/macOS 系统上都预安装了该工具。您可能已经拥有此功能。否则,请在网上搜索如何安装。
  2. 安装 kubectx****:通过从此处下载 bash 脚本到 $PATH 中的某个位置
  3. 安装 helm****:按照这些说明操作。

3. 设置 GCP 项目

在您的项目中启用 GKE (Google Kubernetes Engine)、GCR (Google Container Registry) 和 GCB (Google Cloud Build) API:

gcloud services enable \
  cloudapis.googleapis.com \
  container.googleapis.com \
  containerregistry.googleapis.com \
  cloudbuild.googleapis.com

设置环境变量

在设置过程中,我们将广泛使用 Google Cloud 项目,因此让我们设置一个环境变量以供快速参考

export GCLOUD_PROJECT=$(gcloud config get-value project)

在此研讨会期间,我们将创建一些代码和配置文件,因此让我们创建一个项目目录并切换到该目录

mkdir -p src/istio-burst && \
cd src/istio-burst && \
export proj=$(pwd)

4. 创建“主”Kubernetes 集群

您可以轻松使用 Google Kubernetes Engine (GKE) 创建托管式 Kubernetes 集群。

以下命令将创建一个 Kubernetes 集群:

  • 名为“primary”,
  • 在 us-west1-a 地区中,
  • 可用的最新 Kubernetes 版本,
  • 具有 4 个初始节点
export cluster=primary
export zone=us-west1-a
gcloud container clusters create $cluster --zone $zone --username "admin" \
--cluster-version latest --machine-type "n1-standard-2" \
--image-type "COS" --disk-size "100" \
--scopes "https://www.googleapis.com/auth/compute",\
"https://www.googleapis.com/auth/devstorage.read_only",\
"https://www.googleapis.com/auth/logging.write",\
"https://www.googleapis.com/auth/monitoring",\
"https://www.googleapis.com/auth/servicecontrol",\
"https://www.googleapis.com/auth/service.management.readonly",\
"https://www.googleapis.com/auth/trace.append" \
--num-nodes "4" --network "default" \
--enable-cloud-logging --enable-cloud-monitoring --enable-ip-alias

(这可能需要大约 5 分钟的时间。您可以在 Cloud 控制台中观看集群的创建过程。)

创建 Kubernetes 集群后,gcloud 会配置 kubectl,使其凭据指向该集群。

gcloud container clusters get-credentials $cluster --zone=$zone

现在,您应该能够在新集群中使用 kubectl 了。

运行以下命令以列出集群的 Kubernetes 节点(它们应显示“就绪”状态):

kubectl get nodes

修改 Kubeconfig 名称以方便使用

我们将经常在上下文之间切换,因此为集群设置一个简短的别名非常方便。

此命令会将您刚刚创建的 kubeconfig 条目重命名为 primary

kubectx ${cluster}=gke_${GCLOUD_PROJECT}_${zone}_${cluster}

设置权限

部署 Istio 需要您是集群管理员。此命令会将与您的 Google Cloud 账号关联的电子邮件地址设置为集群管理员

kubectl create clusterrolebinding cluster-admin-binding \
    --clusterrole=cluster-admin \
    --user=$(gcloud config get-value core/account)

5. 创建“突发”集群

以下命令将创建一个 Kubernetes 集群:

  • 名为“burst”,
  • 在 us-west1-a 地区中,
  • 可用的最新 Kubernetes 版本,
  • 具有 1 个初始节点
  • 已启用自动扩缩功能,最多可扩缩到 5 个节点
export cluster=burst
export zone=us-west1-a
gcloud container clusters create $cluster --zone $zone --username "admin" \
--cluster-version latest --machine-type "n1-standard-2" \
--image-type "COS" --disk-size "100" \
--scopes "https://www.googleapis.com/auth/compute",\
"https://www.googleapis.com/auth/devstorage.read_only",\
"https://www.googleapis.com/auth/logging.write",\
"https://www.googleapis.com/auth/monitoring",\
"https://www.googleapis.com/auth/servicecontrol",\
"https://www.googleapis.com/auth/service.management.readonly",\
"https://www.googleapis.com/auth/trace.append" \
--num-nodes "1" --enable-autoscaling --min-nodes=1 --max-nodes=5 \
--network "default" \
--enable-cloud-logging --enable-cloud-monitoring --enable-ip-alias

(这可能需要大约 5 分钟的时间。您可以在 Cloud 控制台中观看集群的创建过程。)

创建 Kubernetes 集群后,gcloud 会配置 kubectl,使其凭据指向该集群。

gcloud container clusters get-credentials $cluster --zone=$zone

现在,您应该能够在新集群中使用 kubectl 了。

运行以下命令以列出集群的 Kubernetes 节点(它们应显示“就绪”状态):

kubectl get nodes

修改 Kubeconfig 名称以方便使用

此命令会将您刚刚创建的 kubeconfig 条目修改为 burst

kubectx ${cluster}=gke_${GCLOUD_PROJECT}_${zone}_${cluster}

设置权限

部署 Istio Remote 需要您是集群管理员。此命令会将与您的 Google Cloud 账号关联的电子邮件地址设置为集群管理员

kubectl create clusterrolebinding cluster-admin-binding \
    --clusterrole=cluster-admin \
    --user=$(gcloud config get-value core/account)

6. 应用防火墙规则

为了使这两个集群能够相互通信,我们需要创建一条防火墙规则。

运行以下命令,在 Google Cloud Platform 中创建防火墙规则,以允许集群进行通信

function join_by { local IFS="$1"; shift; echo "$*"; }
ALL_CLUSTER_CIDRS=$(gcloud container clusters list \
--filter="(name=burst OR name=primary) AND zone=$zone" \
--format='value(clusterIpv4Cidr)' | sort | uniq)
ALL_CLUSTER_CIDRS=$(join_by , $(echo "${ALL_CLUSTER_CIDRS}"))
ALL_CLUSTER_NETTAGS=$(gcloud compute instances list \
--filter="(metadata.cluster-name=burst OR metadata.cluster-name=primary) AND metadata.cluster-location=us-west1-a" \
--format='value(tags.items.[0])' | sort | uniq)
ALL_CLUSTER_NETTAGS=$(join_by , $(echo "${ALL_CLUSTER_NETTAGS}"))
gcloud compute firewall-rules create istio-multicluster-test-pods \
  --allow=tcp,udp,icmp,esp,ah,sctp \
  --direction=INGRESS \
  --priority=900 \
  --source-ranges="${ALL_CLUSTER_CIDRS}" \
  --target-tags="${ALL_CLUSTER_NETTAGS}" --quiet

我们已设置好两个集群,可以开始在其中部署应用和 Istio 了!

7. Istio 简介

Istio 是什么?

Istio 是一种服务网格控制平面,旨在“连接、保护、控制和观察服务”。它通过多种方式实现此目的,但主要是通过将代理容器 ( Envoy) 边车化到每个已部署的 Kubernetes Pod 中。代理容器与通用政策和遥测中心 ( Mixer) 协同控制微服务之间的所有网络通信。

a25613cd581825da.png

这些政策可以独立于 Kubernetes Deployment 和 Service 应用,这意味着网络运营商可以观察网络活动,限制、重定向或重写网络政策,而无需重新部署关联的应用。

Istio 支持的部分流量管理功能包括:

  • 断路器
  • 基于百分比的流量拆分
  • 网址重写
  • TLS 终结
  • 健康检查
  • 负载均衡

在本研讨会中,我们将重点介绍基于百分比的流量拆分。

我们将使用的 Istio 术语

VirtualService

VirtualService 定义了一组在寻址主机时要应用的流量路由规则。

网关

网关是在网格边缘运行的负载平衡器,用于接收传入或传出 HTTP/TCP 连接。网关可以指定端口、SNI 配置等。

DestinationRule

DestinationRule 定义了在路由发生应用于以服务为目标的流量的政策。它们用于指定负载均衡的配置、来自 Sidecar 的连接池大小以及离群点检测设置。

Istio 多集群

您可能已经注意到,在创建两个集群时,primary 集群有 4 个节点,没有自动扩缩功能;而 burst 集群有 1 个节点,自动扩缩功能最多可扩容到 5 个节点。

此配置有两个原因。

首先,我们希望模拟从“本地”到云的场景。在本地环境中,由于基础架构是固定的,因此您无法访问自动扩缩集群。

其次,4 节点设置(如上所述)是运行 Istio 的最低要求。这引出了一个问题:如果 Istio 至少需要 4 个节点,那么我们的 burst 集群如何才能在 1 个节点上运行 Istio?答案是,Istio 多集群安装的 Istio 服务集要小得多,并且会与主集群中的 Istio 安装进行通信,以检索政策规则并发布遥测信息。

8. 应用架构概览

组件概览

我们将使用 NodeJSRedis 部署一个三层应用。

工作器

工作器应用使用 NodeJS 编写,将监听传入的 POST HTTP 请求,对这些请求执行哈希运算,并且如果定义了名为 PREFIX 的环境变量,则会在哈希前面加上该值。计算出哈希后,应用会在指定 Redis 服务器上的“calculation”渠道上发送结果。

我们稍后将使用 PREFIX 环境变量来演示多集群功能。

供参考:以下是应用使用的软件包。

  • body-parser: 允许我们解析 HTTP 请求
  • cors: 允许使用跨域资源共享
  • dotenv: 轻松解析环境变量
  • express: 轻松托管网站
  • ioredis: 用于与 Redis 数据库通信的客户端库
  • morgan: 提供结构清晰的日志

前端

我们的前端也是一个 NodeJS 应用,它使用 express 托管网页。它会获取用户输入的频次,并以该频次向我们的 worker 应用发送请求。此应用还会订阅名为“calculation”的 Redis 渠道上的消息,并在网页中显示结果。

该应用使用以下依赖项。

  • body-parser: 允许我们解析 HTTP 请求
  • dotenv: 轻松解析环境变量
  • express: 轻松托管网站
  • ioredis: 用于与 Redis 数据库通信的客户端库
  • morgan: 提供结构清晰的日志
  • request: 允许发出 HTTP 请求
  • socket.io: 允许从网页到服务器的双向通信

此网页使用 Bootstrap 进行样式设置,运行时的外观如下所示

e5e3b9cbede4cac4.png

架构图

7ae4bc22a58f80a6.png

部署图

我们将把最终应用部署到我们创建的两个集群中。primary 集群将部署所有组件(frontendworker 和 Redis),但 burst 集群仅部署 worker 应用。

下图展示了这两个集群。以红色框线标出的为 Kubernetes 服务,以蓝色框线标出的为 Kubernetes Deployment。黄色方框表示我们安装的 Istio。

561db37c510944bd.png

请注意,即使 burst 集群中没有 Redis 的 Deployment,该集群仍然部署了 Redis 的服务。我们需要在集群中提供此服务,以便 Kubernetes DNS 可以解析请求,但当实际发出请求时,Istio 代理会将请求重新路由到 primary 集群中的 Redis 部署。

最终应用将在 primary 集群中运行一个名为 istiowatcher. 的额外部署。这样一来,当流量超过特定阈值时,我们就可以自动将流量动态重新路由到 burst

8f6183bdfc3f813c.png

9. 创建应用部署文件

我们需要创建一组 Kubernetes 清单来部署应用

进入项目的根目录,然后创建一个名为 kubernetes 的新文件夹

mkdir ${proj}/kubernetes && cd ${proj}/kubernetes

编写 frontend.yaml

此命令将同时创建 Kubernetes Deployment 和 Service,以便访问我们的前端映像。

将以下内容插入到 frontend.yaml 中。

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: frontend-deployment
  labels:
    app: frontend
spec:
  replicas: 1
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
    spec:
      containers:
      - name: frontend
        image: gcr.io/istio-burst-workshop/frontend
        ports:
        - containerPort: 8080
        readinessProbe:
            initialDelaySeconds: 10
            httpGet:
              path: "/_healthz"
              port: 8080
              httpHeaders:
              - name: "Cookie"
                value: "istio_session-id=x-readiness-probe"
        livenessProbe:
          initialDelaySeconds: 10
          httpGet:
            path: "/"
            port: 8080
            httpHeaders:
            - name: "Cookie"
              value: "istio_session-id=x-liveness-probe"
        env:
        - name: PORT
          value: "8080"
        - name: PROCESSOR_URL
          value: "http://worker-service"
        - name: REDIS_URL
          value: "redis-cache-service:6379"
---
apiVersion: v1
kind: Service
metadata:
  name: frontend-service
spec:
  type: ClusterIP
  selector:
    app: frontend
  ports:
  - name: http
    port: 80
    targetPort: 8080

Deployment 中的关键注意事项

  • 我们已将应用运行所要使用的端口指定为 8080
  • 我们将工作线程的地址设置为“http://worker-service”,并将使用 Kubernetes 内置的 DNS 功能来解析生成的服务
  • 我们将 REDIS_URL 的地址设置为“redis-cache-service:6379”,并将使用 Kubernetes 内置的 DNS 功能来解析生成的 IP 地址。
  • 我们还为容器设置了 livenessreadiness 探测,以帮助 Kubernetes 了解容器何时启动并运行。

编写 worker-service.yaml

我们将 Kubernetes 服务定义写入与 Deployment 定义不同的文件中,因为我们将在多个集群中重复使用此服务,但会为每个集群编写不同的 Deployment。

worker-service.yaml 中插入以下内容

apiVersion: v1
kind: Service
metadata:
 name: worker-service
spec:
 type: ClusterIP
 selector:
   app: worker
 ports:
 - name: http
   port: 80
   targetPort: 8081

编写 worker-primary.yaml

这是我们将推送到主集群的 worker 部署。

将以下内容插入到 worker-primary.yaml 中。

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
 name: worker-deployment
 labels:
   app: worker
spec:
 replicas: 1
 selector:
   matchLabels:
     app: worker
 template:
   metadata:
     labels:
       app: worker
       cluster-type: primary-cluster
   spec:
     containers:
     - name: worker
       image: gcr.io/istio-burst-workshop/worker
       imagePullPolicy: Always
       ports:
       - containerPort: 8081
       readinessProbe:
           initialDelaySeconds: 10
           httpGet:
             path: "/_healthz"
             port: 8081
             httpHeaders:
             - name: "Cookie"
               value: "istio_session-id=x-readiness-probe"
       livenessProbe:
         initialDelaySeconds: 10
         httpGet:
           path: "/"
           port: 8081
           httpHeaders:
           - name: "Cookie"
             value: "istio_session-id=x-liveness-probe"
       env:
       - name: PORT
         value: "8081"
       - name: REDIS_URL
         value: "redis-cache-service:6379"

请注意,在此示例中,我们遵循了相同的模式,即提供 livenessreadiness 探测器,并指定供应用使用的 PORTREDIS_URL 环境变量。

在此部署中,还需要注意的一点是缺少 PREFIX 环境变量。这意味着我们的计算结果将是原始哈希值(没有任何前缀)。

此部署的最后一个关键点是 cluster-type: primary-cluster 标签。稍后,我们将在 Istio 多集群上进行流量路由时使用该功能

编写 redis.yaml

工作器与前端之间的通信是通过 Redis 渠道进行的,因此我们需要将 Redis 应用部署到集群中。

将以下内容插入到 redis.yaml

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
 name: redis-cache
spec:
 template:
   metadata:
     labels:
       app: redis-cache
   spec:
     containers:
     - name: redis
       image: redis:alpine
       ports:
       - containerPort: 6379
       readinessProbe:
         periodSeconds: 5
         tcpSocket:
           port: 6379
       livenessProbe:
         periodSeconds: 5
         tcpSocket:
           port: 6379
       volumeMounts:
       - mountPath: /data
         name: redis-data
       resources:
         limits:
           memory: 256Mi
           cpu: 125m
         requests:
           cpu: 70m
           memory: 200Mi
     volumes:
     - name: redis-data
       emptyDir: {}

这是 Redis 应用的半标准部署。它会基于 redis:alpine 映像启动容器,公开适当的端口并设置合理的资源限制。

编写 redis-service.yaml

我们需要一个 Kubernetes 服务来与 Redis 应用通信

将以下内容插入到 redis-service.yaml

apiVersion: v1
kind: Service
metadata:
 name: redis-cache-service
spec:
 type: ClusterIP
 selector:
   app: redis-cache
 ports:
 - port: 6379
   targetPort: 6379

这会提供名为 redis-cache-service 的服务,以访问我们的 Redis 部署。

10. 部署应用

将映像推送到 GCR 并编写 Kubernetes 清单后,就可以部署应用并查看其运行情况了!

运行以下命令以部署应用

  1. 确保我们位于正确的集群中
kubectx primary
  1. 部署 Redis 缓存
kubectl apply -f redis.yaml
  1. 部署 Redis 服务
kubectl apply -f redis-service.yaml
  1. 部署前端
kubectl apply -f frontend.yaml
  1. 部署工作器
kubectl apply -f worker-primary.yaml
  1. 部署 Worker 服务
kubectl apply -f worker-service.yaml

我们已将应用部署到 GKE。恭喜!

测试

等待 pod 上线

kubectl get pods -w

当所有 Pod 都处于“Running”状态时,按 Ctrl + C

NAME                                   READY     STATUS    RESTARTS   AGE
frontend-deployment-695d95fbf7-76sd8   1/1       Running   0          2m
redis-cache-7475999bf5-nxj8x           1/1       Running   0          2m
worker-deployment-5b9cf9956d-g975p     1/1       Running   0          2m

您会注意到,我们没有通过 LoadBalancer 公开前端。这是因为稍后我们将通过 Istio 访问应用。为了测试一切是否正常运行,我们将使用 kubectl port-forward. 运行以下命令,将本地(或 Cloud Shell)机器上的端口 8080 转接到运行 frontend 部署的端口 8080。

kubectl port-forward \
$(kubectl get pods -l app=frontend -o jsonpath='{.items[0].metadata.name}') \
8080:8080

如果您在本地运行:打开网络浏览器,然后前往 http://localhost:8080

如果您在 Cloud Shell 中运行:请点击“网页预览”按钮,然后选择“在端口 8080 上预览”

bdb5dc75f415be11.png

您应该会看到前端!如果您在“频次”框中输入一个数字,您应该会看到开始出现哈希

1caafaffab26897a.png

恭喜,一切都已正常运行!

Ctrl+C 即可停止转发端口。

11. 清理已部署的应用

我们将对集群应用 Istio,然后重新部署应用,因此我们先清理当前应用。

运行以下命令以删除您刚刚创建的所有 Deployment 和服务

  1. 删除redis-cache-service
kubectl delete -f redis-service.yaml
  1. 删除redis
kubectl delete -f redis.yaml
  1. 删除frontend
kubectl delete -f frontend.yaml
  1. 删除worker
kubectl delete -f worker-primary.yaml
  1. 删除worker-service
kubectl delete -f worker-service.yaml

12. 在主集群上安装 Istio

获取 Istio

Istio 的版本托管在 GitHub 上。以下命令将下载并解压缩 1.0.0 版的 Istio。

  1. 切换到项目的根目录
cd ${proj}
  1. 下载归档文件
curl -LO https://github.com/istio/istio/releases/download/1.0.0/istio-1.0.0-linux.tar.gz
  1. 提取并移除归档文件
tar xzf istio-1.0.0-linux.tar.gz && rm istio-1.0.0-linux.tar.gz

创建 Istio 模板

运行以下 Helm 命令将创建用于在集群中安装 Istio 的模板。

helm template istio-1.0.0/install/kubernetes/helm/istio \
--name istio --namespace istio-system \
--set prometheus.enabled=true \
--set servicegraph.enabled=true  > istio-primary.yaml

此命令会在当前目录中创建一个名为 istio-primary.yaml 的文件,其中包含部署和运行 Istio 所需的所有定义和规范。

请注意这两个 --set 参数。这些插件为 Istio 系统添加了 PrometheusServiceGraph 支持。我们将在稍后的实验中使用 Prometheus 服务。

部署 Istio

若要部署 Istio,我们首先需要创建一个名为 istio-system 的命名空间,以便 Istio 部署和服务可以在其中运行。

kubectl create namespace istio-system

最后,使用 Helm 应用我们创建的 istio-primary.yaml 文件

kubectl apply -f istio-primary.yaml

标签默认命名空间

Istio 的工作方式是将一个边车代理服务注入到每个部署中。此操作是选择性加入的,因此我们需要使用 istio-injection=enableddefault 命名空间添加标签,以便 Istio 可以自动为我们注入边车。

kubectl label namespace default istio-injection=enabled

恭喜!我们已创建并运行了集群,并且 Istio 已准备就绪,可以部署应用了!

13. 使用 Istio 流量管理功能部署应用

创建 Istio 流量管理配置文件

Istio 的工作方式与 Kubernetes 类似,因为它使用 YAML 文件进行配置。为此,我们需要创建一组文件来告知 Istio 如何公开和路由我们的流量。

创建名为 istio-manifests 的目录并切换到该目录

mkdir ${proj}/istio-manifests && cd ${proj}/istio-manifests

编写 frontend-gateway.yaml

此文件将以类似于 GKE LoadBalancer 的方式公开我们的 Kubernetes 集群,并将所有入站流量路由到我们的前端服务。

创建一个名为 frontend-gateway.yaml 的文件,并插入以下内容。

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
 name: frontend-gateway
spec:
 selector:
   istio: ingressgateway # use Istio default gateway implementation
 servers:
 - port:
     number: 80
     name: http
     protocol: HTTP
   hosts:
   - "*"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
 name: frontend-ingress-virtual-service
spec:
 hosts:
 - "*"
 gateways:
 - frontend-gateway
 http:
 - route:
   - destination:
       host: frontend-service
       port:
         number: 80

编写 redis-virtualservice.yaml

创建一个名为 redis-virtualservice.yaml 的文件,并插入以下内容

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
 name: redis-virtual-service
spec:
 hosts:
 - redis-cache-service
 gateways:
 - mesh
 tcp:
 - route:
   - destination:
       host: redis-cache-service.default.svc.cluster.local

编写 worker-virtualservice.yaml

创建一个名为 worker-virtualservice.yaml 的文件,并插入以下内容

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
 name: worker-virtual-service
spec:
 hosts:
 - worker-service
 gateways:
 - mesh
 http:
 - route:
   - destination:
       host: worker-service.default.svc.cluster.local   
       port:
         number: 80

部署 Istio 流量管理政策

部署 Istio 政策的方式与其他 Kubernetes 资源相同,使用 kubectl apply

  1. 应用我们的网关
kubectl apply -f frontend-gateway.yaml
  1. 应用 Redis VirtualService
kubectl apply -f redis-virtualservice.yaml
  1. 应用工作器 VirtualService
kubectl apply -f worker-virtualservice.yaml

部署应用

  1. 切换回我们的 kubernetes 目录
cd ${proj}/kubernetes
  1. 部署 Redis 缓存
kubectl apply -f redis.yaml
  1. 部署 Redis 服务
kubectl apply -f redis-service.yaml
  1. 部署前端
kubectl apply -f frontend.yaml
  1. 部署工作器
kubectl apply -f worker-primary.yaml
  1. 部署 Worker 服务
kubectl apply -f worker-service.yaml

验证

至此,我们已在具有 Istio 和流量管理政策的集群上重新部署了应用。

我们等待所有工作负载上线

所有服务都处于在线状态后,获取我们在 frontend-ingressgateway.yaml 中配置的 IngressGateway

$ kubectl -n istio-system get svc istio-ingressgateway
NAME                   TYPE           CLUSTER-IP    EXTERNAL-IP     PORT(S)                                                                                                     AGE
istio-ingressgateway   LoadBalancer   10.36.3.112   35.199.158.10   80:31380/TCP,

浏览 <EXTERNAL-IP> 地址或使用 curl 命令访问该地址,您应该会看到前端!

$ curl 35.199.158.10
<!doctype html>
<html>

<head>
    <title>String Hashr</title>
    <!-- Bootstrap -->
...

14. 在“突发”集群上安装 Istio

我们一直在 primary 集群上花费大量时间进行设置和部署,但我们还有另一个完整的集群需要部署!

在本部分中,我们需要获取两个集群中的配置变量,因此请密切注意每个命令指向哪个集群。

创建 Istio 远程清单

与将 Istio 部署到 primary 集群时类似,我们将使用 Helm 为将 Istio 远程部署到 burst 集群创建模板。不过,在此之前,我们需要先了解一些有关 primary 集群的信息

收集主集群信息

更改为 primary 集群

kubectx primary

以下命令用于检索主集群中各个 Pod 的 IP 地址。这些用于 Istio Remote 与主集群进行通信。

export PILOT_POD_IP=$(kubectl -n istio-system get pod -l istio=pilot -o jsonpath='{.items[0].status.podIP}')
export POLICY_POD_IP=$(kubectl -n istio-system get pod -l istio-mixer-type=policy -o jsonpath='{.items[0].status.podIP}')
export STATSD_POD_IP=$(kubectl -n istio-system get pod -l istio=statsd-prom-bridge -o jsonpath='{.items[0].status.podIP}')
export TELEMETRY_POD_IP=$(kubectl -n istio-system get pod -l istio-mixer-type=telemetry -o jsonpath='{.items[0].status.podIP}')
export ZIPKIN_POD_IP=$(kubectl -n istio-system get pod -l app=jaeger -o jsonpath='{range .items[*]}{.status.podIP}{end}')

创建远程模板

现在,我们将使用 helm 创建一个名为 istio-remote-burst.yaml 的文件,然后将其部署到 burst 集群。

更改为项目根目录

cd $proj
helm template istio-1.0.0/install/kubernetes/helm/istio-remote --namespace istio-system \
--name istio-remote \
--set global.remotePilotAddress=${PILOT_POD_IP} \
--set global.remotePolicyAddress=${POLICY_POD_IP} \
--set global.remoteTelemetryAddress=${TELEMETRY_POD_IP} \
--set global.proxy.envoyStatsd.enabled=true \
--set global.proxy.envoyStatsd.host=${STATSD_POD_IP} \
--set global.remoteZipkinAddress=${ZIPKIN_POD_IP} > istio-remote-burst.yaml

在突发集群上安装 Istio Remote

若要在 burst 集群上安装 Istio,我们需要按照在 primary 集群上安装时所用的步骤操作,但需要改用 istio-remote-burst.yaml 文件。

将 kubecontext 更改为 burst

kubectx burst

创建 istio-system 命名空间

kubectl create ns istio-system

应用 istio-burst.yaml

kubectl apply -f istio-remote-burst.yaml

标签默认命名空间

同样,我们需要为 default 命名空间添加标签,以便自动注入代理。

kubectl label namespace default istio-injection=enabled

恭喜!至此,我们已在 burst 集群上设置了 Istio Remote。不过,此时集群仍无法通信。我们需要为 burst 集群生成一个 kubeconfig 文件,以便部署到 primary 集群,从而将这两个集群关联起来。

为“突发”集群创建 kubeconfig

更改为突发集群

kubectx burst

设置环境

我们需要收集一些有关集群的信息,以便为其创建 kubeconfig 文件。

  1. 获取集群的名称
CLUSTER_NAME=$(kubectl config view --minify=true -o "jsonpath={.clusters[].name}")
  1. 获取集群服务器名称
SERVER=$(kubectl config view --minify=true -o "jsonpath={.clusters[].cluster.server}")
  1. 获取 istio-multi 服务账号证书授权机构的 Secret 名称
SECRET_NAME=$(kubectl get sa istio-multi -n istio-system -o jsonpath='{.secrets[].name}')
  1. 获取存储在之前 Secret 中的证书授权机构数据
CA_DATA=$(kubectl get secret ${SECRET_NAME} -n istio-system -o "jsonpath={.data['ca\.crt']}")
  1. 获取存储在之前 Secret 中的令牌
TOKEN=$(kubectl get secret ${SECRET_NAME} -n istio-system -o "jsonpath={.data['token']}" | base64 --decode)

创建 kubeconfig 文件

设置完所有这些环境变量后,我们需要创建 kubeconfig 文件

cat <<EOF > burst-kubeconfig
apiVersion: v1
clusters:
   - cluster:
       certificate-authority-data: ${CA_DATA}
       server: ${SERVER}
     name: ${CLUSTER_NAME}
contexts:
   - context:
       cluster: ${CLUSTER_NAME}
       user: ${CLUSTER_NAME}
     name: ${CLUSTER_NAME}
current-context: ${CLUSTER_NAME}
kind: Config
preferences: {}
users:
   - name: ${CLUSTER_NAME}
     user:
       token: ${TOKEN}
EOF

这将在当前目录中创建一个名为 burst-kubeconfig 的新文件,primary 集群可以使用该文件对 burst 集群进行身份验证和管理。

切换回主集群

kubectx primary

通过创建 Secret 并为其添加标签,应用“突发”的 kubeconfig

kubectl create secret generic burst-kubeconfig --from-file burst-kubeconfig -n istio-system

为 Secret 添加标签,以便 Istio 知道要将其用于多集群身份验证

kubectl label secret burst-kubeconfig istio/multiCluster=true -n istio-system

恭喜!我们已对两个集群进行身份验证,并通过 Istio 多集群功能让它们彼此通信。我们来跨集群部署应用

15. 部署跨集群应用

创建部署

切换到 kubernetes 目录

cd ${proj}/kubernetes

为“突发”集群创建工作器部署:worker-burst.yaml

创建一个名为 worker-burst.yaml 的文件,并在其中插入以下内容:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: worker-deployment
  labels:
    app: worker
spec:
  replicas: 1
  selector:
    matchLabels:
      app: worker
  template:
    metadata:
      labels:
        app: worker
        cluster-type: burst-cluster
    spec:
      containers:
      - name: worker
        image: gcr.io/istio-burst-workshop/worker
        imagePullPolicy: Always
        ports:
        - containerPort: 8081
        readinessProbe:
            initialDelaySeconds: 10
            httpGet:
              path: "/_healthz"
              port: 8081
              httpHeaders:
              - name: "Cookie"
                value: "istio_session-id=x-readiness-probe"
        livenessProbe:
          initialDelaySeconds: 10
          httpGet:
            path: "/"
            port: 8081
            httpHeaders:
            - name: "Cookie"
              value: "istio_session-id=x-liveness-probe"
        env:
        - name: PORT
          value: "8081"
        - name: REDIS_URL
          value: "redis-cache-service:6379"
        - name: PREFIX
          value: "bursty-"

请注意,此文件与我们之前创建的 worker-primary.yaml 几乎完全相同。主要有两个区别。

第一个主要区别在于,我们添加了值为“bursty-”的 PREFIX 环境变量

env:
- name: PORT
  value: "8081"
- name: REDIS_URL
  value: "redis-cache-service:6379"
- name: PREFIX
  value: "bursty-"

这意味着 burst 集群中的工作器将以“bursty-”为前缀发送所有哈希,我们可以使用此信息来确定应用是否真正跨集群。

第二个主要区别是,我们将此部署的 cluster-type 标签从 primary-cluster 更改为 burst-cluster

labels:
  app: worker
  cluster-type: burst-cluster

稍后,当我们更新 VirtualService 时,将使用此标签。

修改 Istio 服务

目前,我们的 Istio 服务并未同时利用这两个部署。我们 100% 的流量都路由到了“主”集群。我们来修改一下。

切换到 istio-manifests 目录

cd ${proj}/istio-manifests

修改 worker-virtualservice.yaml 以包含 DestinationRules

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: worker-virtual-service
spec:
  hosts:
  - worker-service
  gateways:
  - mesh
  http:
  - route:
    - destination:
        host: worker-service.default.svc.cluster.local    
        subset: primary
        port:
          number: 80        
      weight: 50
    - destination:
        host: worker-service.default.svc.cluster.local     
        subset: burst  
        port:
          number: 80        
      weight: 50
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: worker-destination-rule
spec:
  host: worker-service
  trafficPolicy:
    loadBalancer:
      simple: RANDOM
  subsets:
  - name: primary
    labels:
      cluster-type: primary-cluster
  - name: burst
    labels:
     cluster-type: burst-cluster

您可以看到,我们已向 VirtualService 添加了第二个目标。它仍然引用同一主机 (worker-service.default.svc.cluster.local)),但 50% 的流量会路由到 primary 子集,另外 50% 的流量会路由到 burst 子集。

我们将 primary 子集定义为具有 cluster-type: primary-cluster 标签的部署,并将 burst 子集定义为具有 cluster-type: burst-cluster 标签的部署。

这样一来,我们就可以有效地将流量在两个集群之间平均分配。

部署到集群

将 redis-service.yaml 部署到突发集群

更改为 burst kubeconfig

kubectx burst

切换到我们的项目根目录

cd ${proj}

然后部署

将 redis-service.yaml 部署到突发集群

kubectl apply -f kubernetes/redis-service.yaml

将 worker-burst.yaml 部署到突发集群

kubectl apply -f kubernetes/worker-burst.yaml

将 worker-service.yaml 部署到突发集群

kubectl apply -f kubernetes/worker-service.yaml

应用 Istio VirtualService

更改为 primary kubeconfig

kubectx primary

然后部署

kubectl apply -f istio-manifests/worker-virtualservice.yaml

验证是否正常运行

如需验证它是否正常运行,请浏览到您的 Istio 入站流量点,并注意大约 50% 的哈希值都带有“burst-”前缀。

78fb6e235e9f4a07.png

这意味着我们已成功实现跨集群通信!尝试更改不同服务的权重,然后应用 worker-virtualservice.yaml 文件。这是平衡集群之间流量的好方法,但如果我们能自动执行此操作,岂不是更好?

16. 利用 Prometheus 指标

Prometheus 简介

Prometheus 是一种开源系统监控和提醒工具包,最初在 SoundCloud 上构建而成。它维护一个多维数据模型,其中时间序列数据通过指标名称和键值对进行标识。

以下是 Prometheus 架构图,供您参考:

601e1155a825e0c2.png

如果 Istio 与 Prometheus 一起部署,则会自动向 Prometheus 服务器报告各种指标。我们可以使用这些指标来动态管理集群。

探索 Prometheus 指标

首先,我们需要公开 Prometheus 部署。

前往 GKE 中的“工作负载”标签页,然后向下钻取到“prometheus”工作负载。

b4a7a3cd67db05b3.png

查看部署的详细信息后,依次前往“操作”>“公开”。

c04a482e55bdfd41.png

选择转发到端口 9090,然后输入“负载平衡器”

d5af3ba22a7a6ebb.png

然后选择“公开”

这将在可公开访问的 IP 地址上创建一个服务,我们可以使用该服务来探索 Prometheus 指标

等待端点开始运行,然后点击“外部端点”旁边的 IP 地址 b1e40ad90851da29.png

现在,您应该会看到 Prometheus 界面。

ed273552270337ec.png

Prometheus 提供了足够的指标,可以成为自己的研讨会。不过,我们先从探索 istio_requests_total 指标开始。

执行该查询会返回大量数据。它是有关所有流经 Istio 服务网格的请求的指标,数量非常多!我们将更改表达式,以便过滤出我们真正感兴趣的内容:

目标服务为 worker-service.default.svc.cluster.local 且来源为 frontend-deployment 的请求,仅限过去 15 秒内的请求

该查询如下所示:

istio_requests_total{reporter="destination",
destination_service="worker-service.default.svc.cluster.local",
source_workload="frontend-deployment"}[15s]

并为我们提供了一组更易于管理的数据

19d551fd5eac3785.png

但仍有点密集。我们想知道每秒请求数,而不是所有请求。

为此,我们可以使用内置的 rate 函数

rate(istio_requests_total{reporter="destination",
destination_service="worker-service.default.svc.cluster.local",
source_workload="frontend-deployment"}[15s])

dbb9dc063a18da9b.png

这样就更接近目标了,但我们需要将这些指标进一步归入一个逻辑组。

为此,我们可以使用 sumby 关键字对结果进行分组和求和

sum(rate(istio_requests_total{reporter="destination",
destination_service="worker-service.default.svc.cluster.local",
source_workload="frontend-deployment"}[15s])) by (source_workload,
source_app, destination_service)

898519966930ec56.png

太好了!我们可以从 Prometheus 中获取所需的精确指标。

最终的 Prometheus 查询

根据我们所学,我们需要向 Prometheus 提出的最终查询是

sum(rate(istio_requests_total{reporter="destination",
destination_service="worker-service.default.svc.cluster.local",
source_workload="frontend-deployment"}[15s])) by (source_workload,
source_app, destination_service)

现在,我们可以使用其 HTTP API 来获取相应指标。

我们可以通过发出如下所示的 HTTP GET 请求,使用查询来查询其 API。将 <prometheus-ip-here> 替换为

curl http://<prometheus-ip-here>/api/v1/query?query=sum\(rate\(istio_requests_total%7Breporter%3D%22destination%22%2C%0Adestination_service%3D%22worker-service.default.svc.cluster.local%22%2C%0Asource_workload%3D%22frontend-deployment%22%7D%5B15s%5D\)\)%20by%20\(source_workload%2C%0Asource_app%2C%20destination_service\)

以下是示例回答:

{
    "status": "success",
    "data": {
        "resultType": "vector",
        "result": [
            {
                "metric": {
                    "destination_service": "worker-service.default.svc.cluster.local",
                    "source_app": "frontend",
                    "source_workload": "frontend-deployment"
                },
                "value": [
                    1544404907.503,
                    "18.892886390062788"
                ]
            }
        ]
    }
}

现在,我们可以从 JSON 中提取指标值

清理

我们需要删除刚刚用于公开 Prometheus 的服务。在 Google Cloud 控制台中,前往我们刚刚创建的服务的顶部,然后点击“删除”

d58cb51b4c922751.png

后续步骤

我们已经找到了一种方法来发现流量在集群中的流动方式和速率,下一步是编写一个小型二进制文件,该文件会定期查询 Prometheus,如果每秒对 worker 的请求数超过某个阈值,则在工作虚拟服务上应用不同的目标权重,以将所有流量发送到 burst 集群。一旦每秒请求数低于下限,就将所有流量重新发送到 primary

17. 创建跨集群突发

设置

将工作服务的所有流量都设置为主集群

我们会将所有以 worker-service 为目的地的流量路由到 primary 集群,视为应用的“默认”状态

修改 $proj/istio-manifests/worker-virtualservice.yaml,使其如下所示

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: worker-virtual-service
spec:
  hosts:
  - worker-service
  gateways:
  - mesh
  http:
  - route:
    - destination:
        host: worker-service.default.svc.cluster.local    
        subset: primary
        port:
          number: 80        
      weight: 100
    - destination:
        host: worker-service.default.svc.cluster.local     
        subset: burst  
        port:
          number: 80        
      weight: 0
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: worker-destination-rule
spec:
  host: worker-service
  trafficPolicy:
    loadBalancer:
      simple: RANDOM
  subsets:
  - name: primary
    labels:
      cluster-type: primary-cluster
  - name: burst
    labels:
     cluster-type: burst-cluster

确保您已连接到 primary 集群

kubectx primary

应用 istio-manifests/worker-virtualservice.yaml

kubectl apply -f istio-manifests/worker-virtualservice.yaml

编写了 istiowatcher 守护程序

我们将使用 Go 来编写此服务,因为该语言具有速度快和可移植性强的特点。应用的总体流程是启动并每秒查询一次 Prometheus,

在 src 中新建一个名为 istiowatcher 的目录

mkdir -p ${proj}/src/istiowatcher && cd ${proj}/src/istiowatcher

我们将从容器内调用 istioctl,以便从集群内操纵 Istio 控制平面。

编写了 istiowatcher.go

在该目录中创建一个名为 istiowatcher.go 的文件,并将以下内容插入其中

package main

import (
        "github.com/tidwall/gjson"
        "io/ioutil"
        "log"
        "net/http"
        "os/exec"
        "time"
)

func main() {
        //These are in requests per second
        var targetLow float64 = 10
        var targetHigh float64 = 15
        // This is for the ticker in milliseconds
        ticker := time.NewTicker(1000 * time.Millisecond)

        isBurst := false

        // Our prometheus query
        reqQuery := `/api/v1/query?query=sum(rate(istio_requests_total{reporter="destination",destination_service="worker-service.default.svc.cluster.local",source_workload="frontend-deployment"}[15s]))by(source_workload,source_app,destination_service)`

        for t := range ticker.C {
                log.Printf("Checking Prometheus at %v", t)

                // Check prometheus
                // Note that b/c we are querying over the past 5 minutes, we are getting a very SLOW ramp of our reqs/second
                // If we wanted this to be a little "snappier" we can scale it down to say 30s
                resp, err := http.Get("http://prometheus.istio-system.svc.cluster.local:9090" + reqQuery)
                if err != nil {
                        log.Printf("Error: %v", err)
                        continue
                }
                defer resp.Body.Close()
                body, _ := ioutil.ReadAll(resp.Body)

                val := gjson.Get(string(body), "data.result.0.value.1")
                log.Printf("Value: %v", val)

                currentReqPerSecond := val.Float()
                log.Printf("Reqs per second %f", currentReqPerSecond)

                if currentReqPerSecond > targetHigh && !isBurst {
                        applyIstio("burst.yaml")
                        log.Println("Entering burst mode")
                        isBurst = true
                } else if currentReqPerSecond < targetLow && isBurst {
                        applyIstio("natural.yaml")
                        log.Println("Returning to natural state.")
                        isBurst = false
                }
        }
}

func applyIstio(filename string) {
        cmd := exec.Command("istioctl", "replace", "-f", filename)
        if err := cmd.Run(); err != nil {
                log.Printf("Error hit applying istio manifests: %v", err)
        }
}

编写 Dockerfile

创建一个名为 Dockerfile 的新文件,并将以下内容插入其中。

FROM golang:1.11.2-stretch as base

FROM base as builder

WORKDIR /workdir
RUN curl -LO https://github.com/istio/istio/releases/download/1.0.0/istio-1.0.0-linux.tar.gz
RUN tar xzf istio-1.0.0-linux.tar.gz
RUN cp istio-1.0.0/bin/istioctl ./istioctl

FROM base 

WORKDIR /go/src/istiowatcher
COPY . .
COPY --from=builder /workdir/istioctl /usr/local/bin/istioctl

RUN go get -d -v ./...
RUN go install -v ./...

CMD ["istiowatcher"]

此多阶段 Dockerfile 会在第一阶段下载并提取 Istio 的 1.0.0 版本。第二阶段将我们目录中的所有内容复制到映像中,然后将 istioctl 从 build 阶段复制到 /usr/local/bin(以便我们的应用可以调用它),获取依赖项,编译代码并将 CMD 设置为“istiowatcher

编写 burst.yaml

当从 frontendworker 的每秒请求数超过 15 时,istiowatcher 将应用此文件。

创建一个名为 burst.yaml 的新文件,并将以下内容插入其中。

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
 name: worker-virtual-service
spec:
 hosts:
 - worker-service
 gateways:
 - mesh
 http:
 - route:
   - destination:
       host: worker-service.default.svc.cluster.local   
       subset: primary
       port:
         number: 80       
     weight: 0
   - destination:
       host: worker-service.default.svc.cluster.local    
       subset: burst 
       port:
         number: 80       
     weight:  100

写入 natural.yaml

当每秒来自 frontendworker 的请求数降至 10 以下时,我们会将此状态视为“自然”状态。在此状态下,100% 的流量会路由到 primary 集群。

创建一个名为 natural.yaml 的新文件,并将以下内容插入其中

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
 name: worker-virtual-service
spec:
 hosts:
 - worker-service
 gateways:
 - mesh
 http:
 - route:
   - destination:
       host: worker-service.default.svc.cluster.local   
       subset: primary
       port:
         number: 80       
     weight: 100
   - destination:
       host: worker-service.default.svc.cluster.local    
       subset: burst 
       port:
         number: 80       
     weight: 0

构建并推送 istiowatcher

运行以下命令,将当前目录发送到 Google Cloud Build (GCB),后者将在 GCR 中构建并标记映像。

gcloud builds submit -t gcr.io/${GCLOUD_PROJECT}/istiowatcher

部署 istiowatcher

切换到 kubernetes 目录

cd ${proj}/kubernetes/

编写部署文件:istiowatcher.yaml

创建一个名为 istiowatcher.yaml 的文件,并插入以下内容(替换 <your-project-id>)。

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: istiowatcher-deployment
  labels:
    app: istiowatcher
spec:
  replicas: 1
  selector:
    matchLabels:
      app: istiowatcher
  template:
    metadata:
      labels:
        app: istiowatcher
    spec:
      serviceAccountName: istio-pilot-service-account
      automountServiceAccountToken: true
      containers:
      - name: istiowatcher
        image: gcr.io/<your-project-id>/istiowatcher
        imagePullPolicy: Always

部署

确保我们在主集群中运行

kubectx primary

istio-system 命名空间中部署 istiowatcher.yaml

kubectl apply -n istio-system -f istiowatcher.yaml

请务必注意 YAML 中的 serviceAccountNameautomountServiceAccountToken 指令。这样,我们便获得了从集群内运行 istioctl 所需的凭据。

我们还需要在 istio-system 命名空间内部署此内容,以确保我们拥有 istio-pilot-service-account 的凭据。(它在 default 命名空间中不存在)。

观看流量自动切换!

现在,激动人心的时刻到了!我们前往前端,将每秒请求数提升到 20

请注意,此过程需要几秒钟时间,但我们会逐渐增加哈希值,并且所有哈希值都带有“bursty-”前缀!

这是因为我们是在 15s 范围内对 Prometheus 进行抽样,这会导致我们的响应时间略有延迟。如果我们想要更窄的频段,可以将对 Prometheus 的查询更改为 5s.

18. 接下来做什么?

清理

如果您使用的是为此研讨会提供的临时账号,则无需清理。

您可以删除 Kubernetes 集群、防火墙规则和 GCR 中的映像

gcloud container clusters delete primary --zone=us-west1-a
gcloud container clusters delete burst --zone=us-west1-a
gcloud compute firewall-rules delete istio-multicluster-test-pods 
gcloud container images delete gcr.io/$GCLOUD_PROJECT/istiowatcher

展望未来