[云] DockerCoins 练习笔记

DockerCoins

  • DockerCoins 由 5 个服务组成
    • rng (随机数生成器):

      • 这是一个Web服务,它的任务是生成随机字节。随机字节通常用于加密、安全令牌生成、测试等场景。
    • hasher (哈希计算器):

      • 这个服务接收数据(通常是通过POST请求发送的数据),然后计算这些数据的哈希值。哈希是一种算法,可以将数据转换成固定长度的字符串,通常用于验证数据完整性或在数据库中快速查找数据。
    • worker (后台进程):

      • 这是一个在后台运行的程序,它负责调用rnghasher服务。它可能负责协调任务,比如生成随机数据,然后发送给hasher进行哈希处理,并将结果存储或用于其他目的。
    • webui (Web用户界面):

      • 这是一个用户可以通过Web浏览器访问的界面,用于监控worker进程的进度。用户可以通过这个界面查看任务的状态,比如哪些任务正在执行,哪些已经完成。
    • redis (数据存储):

      • Redis是一个高性能的键值存储系统,通常用于缓存数据或作为数据库使用。在这个场景中,worker可能会更新Redis中的计数器,用于跟踪处理了多少数据或任务。
  • 部署一个名为 DockerCoins 的应用程序,
    • 该程序会生成一些随机字节,对这些字节进行哈希,递增一个计数器(以跟踪速度),并不断重复!
    • 您将尝试找出其扩展时的瓶颈,并使用 HPA(水平 Pod 自动缩放器)来扩展该应用程序。
  • 在Kubernetes中,水平Pod自动缩放器(Horizontal Pod Autoscaler,简称HPA)是一种可以根据实际负载自动调整Pod数量的机制。它主要用于应对应用程序负载的变化,以保持服务的稳定性和响应性。HPA可以基于多种指标进行自动扩展,如CPU、内存使用率,或者自定义的业务指标。

    什么是HPA?

    HPA通过观察Pod的CPU和内存使用情况,自动增加或减少Pod的数量。例如,如果Pod的平均CPU使用率超过了设定的目标值,HPA会增加Pod的数量来分摊负载;相反,如果CPU使用率低于目标值,HPA会减少Pod的数量以节省资源。

    如何工作?

    HPA的工作流程大致如下:

     
    • 指标数据采集:由Metrics Server收集Pod的性能指标数据。
    • 数据获取与计算:HPA控制器获取这些指标数据,并根据预设的规则计算目标Pod副本数量。
    • 副本数调整:HPA根据计算结果调整Pod的数量
    • 动态调整:HPA持续监控资源使用情况,并动态调整Pod数量以匹配负载。
  • 瓶颈是什么?

    在使用HPA时,可能会遇到的瓶颈包括:

    • 指标采集延迟Metrics Server采集指标的延迟可能影响HPA的响应速度
    • 资源限制集群资源不足可能限制Pod数量的增加。
    • 配置不当HPA配置不当可能导致过度缩放或缩放不足。
    • 应用程序性能应用程序本身的性能问题可能使得扩展Pod数量后仍然无法满足需求。
  • 如何扩展?

    要使用HPA进行扩展,你需要:

    • 确保Metrics Server已部署:HPA需要Metrics Server来获取性能指标。
    • 定义HPA资源:通过kubectl或HPA的API对象定义HPA,指定目标资源、最小和最大Pod数量以及目标指标
    • 监控和调整:监控HPA的活动,并根据实际情况调整HPA的配置。

例如,你可以使用以下命令为Deployment创建一个HPA,目标是将CPU使用率维持在50%:

kubectl autoscale deployment <deployment-name> --cpu-percent=50 --min=1 --max=10

这会创建一个HPA,自动将指定Deployment的Pod数量维持在1到10之间,具体数量根据CPU使用率来定。

环境设置

AWS 上设置。您应选择 Ubuntu Server 24.04 LTS (HVM), SSD Volume Type 作为 AMI(Amazon 机器映像),并选择 m4.large 作为实例类型。您需要按如下方式配置安全组,以确保您以后可以通过 Web 浏览器访问 Minikube 服务。

类型 协议 端口范围 来源 描述
所有流量 所有 所有 0.0.0.0/0

运行以下命令以满足需求。

# 安装 Minikube
$ curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
$ sudo install minikube-linux-amd64 /usr/local/bin/minikube

# 安装 kubectl
$ curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
$ sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl

# 安装 Docker
$ sudo apt-get update && sudo apt-get install docker.io -y

# 安装 conntrack
$ sudo apt-get install -y conntrack

# 安装 httping
$ sudo apt-get install httping

# 安装 Helm
$ curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3
$ chmod 700 get_helm.sh
$ ./get_helm.sh

Docker: Docker 是一个开源的应用容器引擎,它允许开发者打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口。

conntrack: conntrack 是 Linux 内核的一个模块,用于跟踪穿过网络接口连接的状态。它被许多网络相关的应用程序使用,比如 iptables、netfilter 等。如果在使用这些工具或者需要跟踪连接状态,可能需要安装 conntrack。

httping: httping 是一个命令行工具,它结合了 ping 和 curl 的功能,可以用来测试 HTTP 服务器的响应时间和可靠性。它不是必需的,但如果需要监控 HTTP 服务,它可能会很有用。

Helm: Helm 是 Kubernetes 的一个包管理工具,它帮助您管理 Kubernetes 应用的部署和版本控制。如果在使用 Kubernetes 并且需要部署和管理应用,Helm 会非常有用。 

在 EC2 Ubuntu 上运行 Minikube

将用户添加到 “docker” 组并切换主组

$ sudo usermod -aG docker $USER && newgrp docker

启动 Minikube

$ minikube start

启动应用程序

您可以将作业的压缩文件从本地机器传输到 EC2 实例并 cd 到作业目录中。

dockercoins yaml 文件中启动应用程序(需要提前上传文件)        

$ kubectl apply -f dockercoins.yaml

等待所有组件运行

$ kubectl get po
# NAME                     READY   STATUS    RESTARTS   AGE
# hasher-89f59d444-r7v7j   1/1     Running   0          27s
# redis-85d47694f4-m8vnt   1/1     Running   0          27s
# rng-778c878fb8-ch7c2     1/1     Running   0          27s
# webui-7dfbbf5985-24z79   1/1     Running   0          27s
# worker-949969887-rkn48   1/1     Running   0          27s

从 UI 检查结果

$ minikube service webui
# |-----------|-------|-------------|----------------------------|
# | NAMESPACE | NAME  | TARGET PORT |            URL             |
# |-----------|-------|-------------|----------------------------|
# | default   | webui |          80 | http://172.31.67.128:30163 |
# |-----------|-------|-------------|----------------------------|
# ????  Opening service default/webui in default browser...
# ????  http://172.31.67.128:30163

 WebUI 端口转发

您需要将本地端口的连接转发到 pod 上的端口。

$ kubectl port-forward --address 0.0.0.0 <webui pod name> <local port>:<pod port>

本地端口可以是任意数字,Pod 端口是目标端口(例如,80)。

您可以在 Web 浏览器*问 DockerCoin Miner WebUI。地址为 <Public IPv4 address>:<local port>(例如,3.238.254.199:30163,其中 3.238.254.199 是实例的 Public IPv4 地址)。

注意:kubectl port-forward 不会返回。要继续进行练习,您需要打开另一个终端。

工作者 (Workers)

worker 数量从 2 扩展到 10(更改 replicas 的数量)。

$ kubectl scale deployment worker --replicas=3
工作者数量 1 2 3 4 5 10
哈希/秒

问题: 当工作者数量为 10 倍时,速度提升了多少?

Rng / Hasher

当我们将工作者数量扩展到 10 后,速度提升并不是 10 倍!有什么东西在拖慢速度... 但是什么呢?

让我们保持工作者数量为 10,并使用经典的 httping 命令来检测延迟。可能的瓶颈现在是 rnghasher。(why?)

        1. 服务内部的依赖关系

  • 尽管 worker 本身在 Pod 上独立运行,但它依赖其他服务(如 rnghasher)来完成任务。worker 需要通过 HTTP 请求与这些服务通信,因此任何这些依赖服务的性能问题都会影响 worker 的速度。
  • 比如,worker 会调用 rng 来生成随机字节,然后调用 hasher 来对这些字节进行哈希。如果 rnghasher 的响应速度变慢,worker 的处理速度也会受到影响。
  • 结论:即使 worker 本身在 Pod 上运行,它仍然需要等待其他服务的响应,因此这些服务的性能将直接影响整体的吞吐量。

        2. 网络通信开销

  • Kubernetes Pod 之间的通信通常通过网络完成,虽然这些通信是在集群内部发生的,但网络延迟依然可能是一个影响因素。每次 worker 调用 rnghasher 时,都会通过 HTTP 请求进行网络通信。随着 worker 数量的增加,网络通信的总量也会增加,如果网络带宽或延迟成为瓶颈,扩展 worker 数量也无法带来线性增长的性能提升。
  • 结论:即使这些服务都在同一个 Kubernetes 集群内部运行,集群内部的网络延迟或瓶颈仍然可能限制性能的提升。

        3. 依赖服务的资源限制

  • rnghasher 这两个服务可能没有足够的资源(CPU、内存)来应对 10 个 worker 的并发请求。例如,rng 可能无法同时为 10 个 worker 生成随机数,而 hasher 也可能无法处理所有并发的哈希请求。如果这些服务没有足够的计算资源或是因为 CPU 饱和而变慢,worker 的速度提升就会受到限制。
  • 结论:如果 rnghasher 没有充足的计算资源支持多个 worker 的并发请求,那么即使增加了 worker 的数量,性能提升也会受限于这些服务的处理能力。

        4. Redis 存储瓶颈

  • worker 还依赖 redis 数据存储来更新计数器。如果 Redis 的写入速度过慢,或者 Redis 的 I/O 操作受限,worker 可能会等待 Redis 完成操作,这也可能成为瓶颈。
  • 结论:虽然 Redis 本质上是一个高速缓存系统,但如果并发访问 Redis 的负载过高,也可能影响整个应用程序的速度。

        5. 服务扩展限制

  • 在 Kubernetes 中,扩展服务的 Pod 数量并不一定意味着线性增长的性能。某些服务可能在资源利用率(如 CPU 或内存)上受到限制,或者有一个特定的瓶颈服务没有被水平扩展。如果 rnghasher 没有根据需求自动扩展,那么即使增加了 worker 的数量,也不会带来 10 倍的性能提升。
  • 结论:除了增加 worker 的数量外,还需要确保其他依赖的服务(如 rnghasher)能够水平扩展,以匹配更高的并发负载。

        解决方法:检测瓶颈

  • 使用工具(如 httping)对 rnghasher 服务进行延迟检测,以确定哪个服务成为了瓶颈。
  • 通过 Prometheus 和 HPA(水平 Pod 自动缩放器)来监控服务的资源使用情况,并根据需要扩展瓶颈服务(如 rnghasher)。
# 暴露服务,因为我们要在 k8s 集群外检测延迟
$ kubectl expose service rng --type=NodePort --target-port=80 --name=rng-np
$ kubectl expose service hasher --type=NodePort --target-port=80 --name=hasher-np

# 获取服务的 URL
$ kubectl get svc rng-np hasher-np

# 找到 minikube 地址
$ kubectl cluster-info
# Kubernetes 控制面板运行在 https://172.31.67.128:8443
# KubeDNS 运行在 https://172.31.67.128:8443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
# 此处,minikube 地址为 172.31.67.128

# 检测 hasher 的延迟
$ httping <minikube-address>:<hasher-np-port>

# 检测 rng 的延迟
$ httping <minikube-address>:<rng-np-port>

 

        在 Kubernetes(k8s)集群外暴露服务是为了能够从集群外部进行延迟检测。通常,Kubernetes 集群内的服务是隔离的,只有集群内部的其他服务或 Pods 才能直接访问它们。如果要从集群外部对这些服务进行测试或访问(例如,通过 HTTP 请求检测服务的响应时间或延迟),就需要将服务“暴露”出来,使其能够被外部客户端访问。

        具体来说,暴露服务的主要原因有以下几个:

  1. 检测外部访问的性能:通过暴露服务,可以从集群外部进行 HTTP 请求来模拟真实用户的行为,这有助于更准确地检测和评估延迟或其他性能指标。

  2. 排查问题和监控:集群外的请求能够帮助你发现集群内部可能忽略的问题,尤其是在网络延迟和外部用户访问体验上。你可以通过 httping 等工具来监测服务的响应时间,从而检测服务是否存在瓶颈。

  3. 负载测试:暴露服务使得你可以对服务进行负载测试。通过模拟大量的外部请求,可以分析服务在高并发下的性能表现,并据此决定是否需要扩展服务(例如通过 HPA 来自动调整 Pods 的数量)。

  4. 外部访问需求:如果某些服务需要被外部用户或其他系统访问,那么暴露服务是必不可少的。

        通过使用 kubectl expose 命令,服务可以通过一个特定的端口在外部可访问(例如通过 NodePortLoadBalancer 类型的服务),这使得您可以从集群外部对这些服务进行延迟检测和性能分析。

服务 Hasher Rng
延迟 (毫秒)

问题: 哪个服务是瓶颈?

应用程序监控

使用 Prometheus 收集指标

Prometheus 是一个开源工具,用于监控和收集应用程序的指标,帮助用户查看并理解应用程序的关键性能指标。

我们将把 Prometheus 部署在我们的 Kubernetes 集群中,并学习如何查询它。

# 安装 prometheus
$ helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
$ helm install prometheus prometheus-community/prometheus

# 暴露服务端口
$ kubectl expose service prometheus-server --type=NodePort --target-port=9090 --name=prometheus-server-np

在继续之前,我们的 metric-server 插件默认是禁用的。使用以下命令检查:

$ minikube addons list

如果它是禁用的,您会看到类似 metrics-server: disabled 的提示。启用它:

$ minikube addons enable metrics-server

我们想要通过 httplat 收集延迟指标,它是一个简洁的 Prometheus 导出器,用于暴露单个 HTTP 目标的延迟。(请在 httplat.yaml 中替换 <FILL IN> 为所需的值)。

$ kubectl apply -f httplat.yaml
$ kubectl expose deployment httplat --port=9080

# 检查部署是否就绪
$ kubectl get deploy httplat
# NAME      READY   UP-TO-DATE   AVAILABLE   AGE
# httplat   1/1     1            1           43s

        在 httplat.yaml 文件中,<FILL IN> 通常指代需要替换为实际值的占位符。在这个场景中,httplat 是一个 Prometheus 导出器,用于收集 HTTP 服务的延迟指标。为了使 httplat 正确地检测 rnghasher 服务的延迟,您需要在 httplat.yaml 文件中将 <FILL IN> 替换为目标服务的 URL 或地址。

具体步骤如下:

  1. 目标服务 URL

    • rnghasher 服务是 DockerCoins 应用程序中的两个重要组件,分别负责生成随机字节和计算哈希值。
    • 您需要在 httplat.yaml 中填写对应服务的 URL,这样 httplat 才能针对这些服务执行 HTTP 请求并收集延迟数据。
  2. 需要替换的值

    • http://<FILL IN> 应该替换为 rnghasher 服务的地址。例如,如果 rng 服务暴露的地址为 http://172.31.67.128:30163,则需要将 <FILL IN> 替换为该 URL。
    • kubectl get svc 命令中,您可以找到 rng-nphasher-np 服务的暴露地址(NodePort),这些就是您需要填入的值。

举个例子,假设 rng-np 服务的 URL 是 http://172.31.67.128:30200,那么 httplat.yaml 文件的相关部分将变为:

 

 配置 Prometheus 以收集检测到的延迟。

$ kubectl annotate service httplat \
          prometheus.io/scrape=true \
          prometheus.io/port=9080 \
          prometheus.io/path=/metrics

 连接到 Prometheus

$ kubectl get svc prometheus-server-np
# NAME                   TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
# prometheus-server-np   NodePort   10.96.122.34   <none>        80:24057/TCP   87s

 

为 Prometheus UI 端口转发

您需要将本地端口的连接转发到 pod 上的端口。

$ kubectl port-forward --address 0.0.0.0 <prometheus server pod name> <local port>:9090
或
$ kubectl port-forward --address 0.0.0.0 service/prometheus-server-np <local port>:80

注意:为什么有差异?

这两种方式的区别在于你是对 Pod 进行端口转发还是对 Service 进行端口转发:

1. Pod 端口转发

  • 当你指定 Prometheus 服务器的 Pod 名称 来进行端口转发时,实际上你是在将本地的端口(如 9090)直接映射到该特定 Pod 内的 9090 端口上。
  • 场景:这种方法适用于当你想直接访问某个特定 Pod 内的服务时,尤其是在 Pod 还没有被服务(Service)对象暴露的情况下。
  • 优点:可以精确地将请求发送到某个特定的 Pod,适合测试或调试特定的 Pod 服务。

2. Service 端口转发

  • 当你通过 Service 进行端口转发时,你是在将本地的端口映射到 Kubernetes 集群内 服务 的暴露端口上,这个服务可以负载均衡多个 Pod 的请求。
  • 场景:适用于当你要访问通过 Service 暴露的多个 Pod 提供的服务,而不是单独的某一个 Pod 时。它更加灵活,因为你不需要知道特定的 Pod 名称。
  • 优点:能够更方便地访问由 Service 暴露的应用程序,并且适合在有多个 Pod 提供相同服务的情况下使用。

要检索 Prometheus 服务器 pod 名称,请在终端中输入以下命令:

$ kubectl get po

 

访问 Prometheus

您可以在 Web 浏览器*问 Prometheus。地址为 <Public IPv4 地址>:<本地端口>。检查 httplab 指标是否可用。您可以尝试使用以下 PromQL 表达式来绘制图表,检测 rng/hasher 的延迟(如您在 httplat.yaml 中指定的)。

rate(httplat_latency_seconds_sum[2m])/rate(httplat_latency_seconds_count[2m])

HPA

为了优化应用程序的瓶颈,您可以指定一个水平 pod 自动缩放器,该缩放器可以基于一些指标扩展 Pod 的数量。

生成负载测试

这是生成负载测试的方法。在下一节的进一步说明之前,您不需要生成负载测试。

创建一个负载测试作业,文件为:

file: loadtest-job.yaml
apiVersion: batch/v1
kind: Job
metadata:
  generateName: loadtest
spec:
  template:
    spec:
      containers:
      - name: siege
        image: schoolofdevops/loadtest:v1
        command: ["siege",  "--concurrent=15", "--benchmark", "--time=4m", "http://<FILL IN>"] # FILL IN: rng 或 hasher
      restartPolicy: Never
  backoffLimit: 4

启动负载测试:

kubectl create -f loadtest-job.yaml

这将启动一个持续 4 分钟的临时作业。

获取作业的信息:

kubectl get jobs
kubectl describe  job loadtest-xxx

将 loadtest-xxx 替换为实际的作业名称。

查看负载测试的输出:

kubectl logs  -f loadtest-xxxx

将 loadtest-xxxx 替换为实际的 pod ID。

[示例输出]

** SIEGE 3.0.8
** Preparing 15 concurrent users for battle.
The server is now under siege...
Lifting the server siege...      done.

Transactions:                  47246 hits
Availability:                 100.00 %
Elapsed time:                 239.82 secs
Data transferred:               1.62 MB
Response time:                  0.08 secs
Transaction rate:             197.01 trans/sec
Throughput:                     0.01 MB/sec
Concurrency:                   14.94
Successful transactions:       47246
Failed transactions:               0
Longest transaction:            0.61
Shortest transaction:           0.00

FILE: /var/log/siege.log
You can disable this annoying message by editing
the .siegerc file in your home directory; change
the directive 'show-logfile' to false.

创建自动缩放策略

请在 hpa.yaml 文件中替换 <FILL IN> 为所需的值。现在我们可以看到 HPA 在实际中的表现。

  1. 在 Prometheus UI 中持续监控 rng/hasher(瓶颈组件)的延迟。
  2. 在应用 HPA 之前生成负载测试。
  3. 在负载测试作业完成前应用 HPA:

在终端中输入以下命令:

$ kubectl apply -f hpa.yaml

hpa.yaml 中的水平 Pod 自动缩放器旨在根据样本自动缩放策略,自动调整特定部署(您需要 <FILL IN>,即 rnghasher)的副本数量。

让我们看看 HPA 的详细信息。如果您仍然看到 <unknown> / 5%,请等待一会儿。

$ kubectl describe hpa <FILL IN> # FILL IN: rng 或 hasher

持续观察我们的 HPA(针对 `rng` 或 `hasher`),以查看 HPA 在实际中随着负载的增加或减少,如何自动扩展 rnghasher 的 Pod 数量。

$ kubectl get hpa <FILL IN> -w  # FILL IN: rng 或 hasher

示例输出

NAME    REFERENCE         TARGETS      MINPODS   MAXPODS   REPLICAS   AGE
rng     Deployment/rng    cpu: 7%/5%   1         10        6          12m
...

在输出中,您可以看到 rnghasher 部署的当前副本数以及 Pod 的扩展目标和限制。随着负载的变化,Pod 数量会动态调整。

截图

  • 提供 Prometheus UI 查询 rnghasher 延迟的截图。
  • 提供在终端中监控 HPA 实时操作的截图,展示 HPA 如何随着负载增加或减少而自动扩展或缩减 rnghasher 的 Pod 数量。

开放性问题:

  • 您认为基于 CPU 利用率对 rnghasher 进行扩展是否合理?如果不合理,还有什么其他的指标可以使用?请解释您的理由。

回答

  • 是否合理:在一些情况下,基于 CPU 利用率进行扩展是合理的,特别是当服务的工作负载与 CPU 使用密切相关时。然而,如果瓶颈并不是因为 CPU 饱和(例如,网络延迟、I/O 瓶颈或内存不足),仅基于 CPU 扩展可能并不能很好地反映真实的扩展需求。

  • 其他可考虑的指标

    1. 网络延迟(Latency):对于网络密集型的服务(如 rnghasher),监控 HTTP 请求的延迟可能是更好的扩展指标。如果延迟超过某个阈值,表示服务已经负载过重,可以触发扩展。
    2. 内存使用率(Memory Usage):某些服务可能受内存限制影响较大,尤其是如果大量数据处理依赖内存缓存。监控内存使用情况,并在内存接近饱和时进行扩展,也是一种有效的策略。
    3. 请求速率(Request Rate):跟踪服务的请求速率(如每秒处理的 HTTP 请求数)也是一个扩展的好指标。如果请求速率急剧上升,服务可能需要更多的资源来处理并发请求。

总结

通过这些步骤,您可以监控并自动扩展服务,并根据实际负载进行性能优化。

  • 报告不同数量的工作器(workers)下DockerCoins的性能(2分)。
  • 检测延迟并找出瓶颈组件(rng或hasher)(2分)。
  • 上传HPA(水平Pod自动缩放器)运行的截图(2分):
    • Prometheus用户界面
    • 终端
  • 开放性问题(1分):
    • 一个更好的度量指标?
上一篇:Java性能调优:实战技巧与最佳实践


下一篇:算法剖析:双指针