DockerCoins
- DockerCoins 由 5 个服务组成
-
rng (随机数生成器):
- 这是一个Web服务,它的任务是生成随机字节。随机字节通常用于加密、安全令牌生成、测试等场景。
-
hasher (哈希计算器):
- 这个服务接收数据(通常是通过POST请求发送的数据),然后计算这些数据的哈希值。哈希是一种算法,可以将数据转换成固定长度的字符串,通常用于验证数据完整性或在数据库中快速查找数据。
-
worker (后台进程):
- 这是一个在后台运行的程序,它负责调用
rng
和hasher
服务。它可能负责协调任务,比如生成随机数据,然后发送给hasher
进行哈希处理,并将结果存储或用于其他目的。
- 这是一个在后台运行的程序,它负责调用
-
webui (Web用户界面):
- 这是一个用户可以通过Web浏览器访问的界面,用于监控
worker
进程的进度。用户可以通过这个界面查看任务的状态,比如哪些任务正在执行,哪些已经完成。
- 这是一个用户可以通过Web浏览器访问的界面,用于监控
-
redis (数据存储):
- Redis是一个高性能的键值存储系统,通常用于缓存数据或作为数据库使用。在这个场景中,
worker
可能会更新Redis中的计数器,用于跟踪处理了多少数据或任务。
- 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
命令来检测延迟。可能的瓶颈现在是 rng
和 hasher
。(why?)
1. 服务内部的依赖关系:
- 尽管
worker
本身在 Pod 上独立运行,但它依赖其他服务(如rng
和hasher
)来完成任务。worker
需要通过 HTTP 请求与这些服务通信,因此任何这些依赖服务的性能问题都会影响worker
的速度。 - 比如,
worker
会调用rng
来生成随机字节,然后调用hasher
来对这些字节进行哈希。如果rng
或hasher
的响应速度变慢,worker
的处理速度也会受到影响。 - 结论:即使
worker
本身在 Pod 上运行,它仍然需要等待其他服务的响应,因此这些服务的性能将直接影响整体的吞吐量。
2. 网络通信开销:
- Kubernetes Pod 之间的通信通常通过网络完成,虽然这些通信是在集群内部发生的,但网络延迟依然可能是一个影响因素。每次
worker
调用rng
和hasher
时,都会通过 HTTP 请求进行网络通信。随着worker
数量的增加,网络通信的总量也会增加,如果网络带宽或延迟成为瓶颈,扩展worker
数量也无法带来线性增长的性能提升。 - 结论:即使这些服务都在同一个 Kubernetes 集群内部运行,集群内部的网络延迟或瓶颈仍然可能限制性能的提升。
3. 依赖服务的资源限制:
rng
和hasher
这两个服务可能没有足够的资源(CPU、内存)来应对 10 个worker
的并发请求。例如,rng
可能无法同时为 10 个worker
生成随机数,而hasher
也可能无法处理所有并发的哈希请求。如果这些服务没有足够的计算资源或是因为 CPU 饱和而变慢,worker
的速度提升就会受到限制。- 结论:如果
rng
或hasher
没有充足的计算资源支持多个worker
的并发请求,那么即使增加了worker
的数量,性能提升也会受限于这些服务的处理能力。
4. Redis 存储瓶颈:
worker
还依赖redis
数据存储来更新计数器。如果 Redis 的写入速度过慢,或者 Redis 的 I/O 操作受限,worker
可能会等待 Redis 完成操作,这也可能成为瓶颈。- 结论:虽然 Redis 本质上是一个高速缓存系统,但如果并发访问 Redis 的负载过高,也可能影响整个应用程序的速度。
5. 服务扩展限制:
- 在 Kubernetes 中,扩展服务的
Pod
数量并不一定意味着线性增长的性能。某些服务可能在资源利用率(如 CPU 或内存)上受到限制,或者有一个特定的瓶颈服务没有被水平扩展。如果rng
和hasher
没有根据需求自动扩展,那么即使增加了worker
的数量,也不会带来 10 倍的性能提升。 - 结论:除了增加
worker
的数量外,还需要确保其他依赖的服务(如rng
和hasher
)能够水平扩展,以匹配更高的并发负载。
解决方法:检测瓶颈
- 使用工具(如
httping
)对rng
和hasher
服务进行延迟检测,以确定哪个服务成为了瓶颈。 - 通过 Prometheus 和 HPA(水平 Pod 自动缩放器)来监控服务的资源使用情况,并根据需要扩展瓶颈服务(如
rng
或hasher
)。
# 暴露服务,因为我们要在 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 请求检测服务的响应时间或延迟),就需要将服务“暴露”出来,使其能够被外部客户端访问。
具体来说,暴露服务的主要原因有以下几个:
-
检测外部访问的性能:通过暴露服务,可以从集群外部进行 HTTP 请求来模拟真实用户的行为,这有助于更准确地检测和评估延迟或其他性能指标。
-
排查问题和监控:集群外的请求能够帮助你发现集群内部可能忽略的问题,尤其是在网络延迟和外部用户访问体验上。你可以通过
httping
等工具来监测服务的响应时间,从而检测服务是否存在瓶颈。 -
负载测试:暴露服务使得你可以对服务进行负载测试。通过模拟大量的外部请求,可以分析服务在高并发下的性能表现,并据此决定是否需要扩展服务(例如通过 HPA 来自动调整 Pods 的数量)。
-
外部访问需求:如果某些服务需要被外部用户或其他系统访问,那么暴露服务是必不可少的。
通过使用 kubectl expose
命令,服务可以通过一个特定的端口在外部可访问(例如通过 NodePort
或 LoadBalancer
类型的服务),这使得您可以从集群外部对这些服务进行延迟检测和性能分析。
服务 | 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
正确地检测 rng
或 hasher
服务的延迟,您需要在 httplat.yaml
文件中将 <FILL IN>
替换为目标服务的 URL 或地址。
具体步骤如下:
-
目标服务 URL:
rng
和hasher
服务是 DockerCoins 应用程序中的两个重要组件,分别负责生成随机字节和计算哈希值。- 您需要在
httplat.yaml
中填写对应服务的 URL,这样httplat
才能针对这些服务执行 HTTP 请求并收集延迟数据。
-
需要替换的值:
http://<FILL IN>
应该替换为rng
或hasher
服务的地址。例如,如果rng
服务暴露的地址为http://172.31.67.128:30163
,则需要将<FILL IN>
替换为该 URL。- 在
kubectl get svc
命令中,您可以找到rng-np
和hasher-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 在实际中的表现。
- 在 Prometheus UI 中持续监控
rng/hasher
(瓶颈组件)的延迟。 - 在应用 HPA 之前生成负载测试。
- 在负载测试作业完成前应用 HPA:
在终端中输入以下命令:
$ kubectl apply -f hpa.yaml
hpa.yaml
中的水平 Pod 自动缩放器旨在根据样本自动缩放策略,自动调整特定部署(您需要 <FILL IN>
,即 rng
或 hasher
)的副本数量。
让我们看看 HPA 的详细信息。如果您仍然看到 <unknown> / 5%
,请等待一会儿。
$ kubectl describe hpa <FILL IN> # FILL IN: rng 或 hasher
持续观察我们的 HPA(针对 `rng` 或 `hasher`),以查看 HPA 在实际中随着负载的增加或减少,如何自动扩展 rng
或 hasher
的 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
...
在输出中,您可以看到 rng
或 hasher
部署的当前副本数以及 Pod 的扩展目标和限制。随着负载的变化,Pod 数量会动态调整。
截图:
- 提供 Prometheus UI 查询
rng
或hasher
延迟的截图。- 提供在终端中监控 HPA 实时操作的截图,展示 HPA 如何随着负载增加或减少而自动扩展或缩减
rng
或hasher
的 Pod 数量。
开放性问题:
- 您认为基于 CPU 利用率对
rng
或hasher
进行扩展是否合理?如果不合理,还有什么其他的指标可以使用?请解释您的理由。
回答:
-
是否合理:在一些情况下,基于 CPU 利用率进行扩展是合理的,特别是当服务的工作负载与 CPU 使用密切相关时。然而,如果瓶颈并不是因为 CPU 饱和(例如,网络延迟、I/O 瓶颈或内存不足),仅基于 CPU 扩展可能并不能很好地反映真实的扩展需求。
-
其他可考虑的指标:
- 网络延迟(Latency):对于网络密集型的服务(如
rng
或hasher
),监控 HTTP 请求的延迟可能是更好的扩展指标。如果延迟超过某个阈值,表示服务已经负载过重,可以触发扩展。 - 内存使用率(Memory Usage):某些服务可能受内存限制影响较大,尤其是如果大量数据处理依赖内存缓存。监控内存使用情况,并在内存接近饱和时进行扩展,也是一种有效的策略。
- 请求速率(Request Rate):跟踪服务的请求速率(如每秒处理的 HTTP 请求数)也是一个扩展的好指标。如果请求速率急剧上升,服务可能需要更多的资源来处理并发请求。
- 网络延迟(Latency):对于网络密集型的服务(如
总结
通过这些步骤,您可以监控并自动扩展服务,并根据实际负载进行性能优化。
- 报告不同数量的工作器(workers)下DockerCoins的性能(2分)。
- 检测延迟并找出瓶颈组件(rng或hasher)(2分)。
- 上传HPA(水平Pod自动缩放器)运行的截图(2分):
- Prometheus用户界面
- 终端
- 开放性问题(1分):
- 一个更好的度量指标?