[转]K8S 六种存储解决方案的性能比较测试

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[转]K8S 六种存储解决方案的性能比较测试相关的知识,希望对你有一定的参考价值。

参考技术A

原文地址: https://toutiao.io/posts/nmflsd/preview

大多数开发人员都认为在部署集群时选择合适的存储技术极为重要。但是,在 Kubernetes 这片市场上,人们对于存储技术的选择并没有一个标准的答案。本文将 介绍 Kubernetes 中常用的 6 种存储,分析它们的使用场景和优缺点,并对它们进行性能测试,找出哪种存储解决方案才是最理想的选择。

存储技术的选择在很大程度上取决于工程师们要运行的工作负载类型。如果你正在使用 Kubernetes,你可能偏向于通过动态配置使用 volume 来进行块存储。对于裸机集群,你就需要为你的用例选择最为合适的技术,并将其集成到你的硬件上。

此外,例如 AKS、EKS 和 GKE 等公有云可以开箱即用并带有块存储,但这并不意味着它们是最好的选择。在许多情况下,默认公有云存储类的故障转移时间较长。例如,在 AWS EBS 中存在一个故障的 VM,附在此 VM Pod 上的 volume 需要 5 分钟以上的时间才能在另一个节点上重新联机。但是,像 Portworx 和 OpenEBS 此类云原生存储就可以很快解决这种问题。

本文的目的就是寻找在 Kubernetes 中最常用的存储解决方案,并进行基本性能比较。本次测试使用以下存储后端对 Azure AKS 执行所有测试:

测试结果

*注:请将结果作为存储选择期间的标准之一,但不要仅根据本文的数据做出最终判断。

在描述过程之前,我们先总结一下最终结论。如果我们忽略本地 Azure pvc 或 hostPath,测试结果是:

那么这究竟是为什么呢?让我们从每个后端存储介绍安装说明开始,详述 AKS 测试的具体过程!

各存储解决方案的安装及优缺点
*注:本节不含 Azure hostpath 的安装介绍。

本文将把 Azure 原生 Storage Class 作为所有测试的基线。Azure 会动态创建托管磁盘并将其映射到 VM 中,其中 Kubernetes 为 Pod 的 volume。

如果只是为了使用它,你没有必要再做其他事情。当你配置新的 AKS 集群时,它会自动预定义为“default”和“managed-premium”两种存储类。Premium 类将使用基于 SSD 的高性能和低延迟磁盘。

优点:

弊端:

Ceph Rook 需要设计特定的硬件配置,根据数据类型生成 pg 组,配置日志 SSD 分区(在 bluestore 之前)并定义 crush 映射。 它是一种处理整个存储集群安装的简洁方法。

在 AKS 上安装 Ceph Rook :

优点:

弊端:

GlusterFS 是一个的开源存储解决方案。 Heketi 是 GlusterFS RESTful volume 的管理界面。它提供了一种便捷的方式让 GlusterFS 具有动态配置的能力。如果没有这种访问权限,用户就必须手动创建 GlusterFS volume 并将其映射到 Kubernetes pv 上。

*注:关于 GlusterFS 的更多信息,见: https://docs.gluster.org/en/latest/Install-Guide/Overview/

这里使用了 Heketi 快速入门指南[2]进行安装:

以下是修复该问题的 PR:

同时,动态配置问题也会对测试造成一定影响。对于 Kubernetes 控制平面,Heketi restURL 是不可用的。测试人员尝试利用 kube dns record、Pod IP 和 svc IP 来解决这个问题,但是都没有奏效。为此,他们最后选择通过 Heketi CLI 手动创建 volume。

以下是在 Kubernetes 上安装 Heketi Gluster 的更多内容:

优点:

弊端:

OpenEBS 代表了一种新的 CAS(Container Attached Storage)概念,属于云原生存储类别。 它是基于单个微服务的存储控制器和基于多个微服务的存储复制品。

作为开源项目,目前它提供 2 个后端:Jiva 和 cStor。cStor 作为控制器,它的副本部署在一个 namespace(安装 openebs 的 namespace)中,也可以说它采用的是原始磁盘而不是格式化分区。每个 Kubernetes volume 都有自己的存储控制器,它可以在节点可用存储容量范围内进行扩展。

在 AKS 上安装它非常简单:

优点:

弊端:

*注:OpenEBS 团队帮忙修改的测试用例场景,见: https://github.com/kmova/openebs/tree/fio-perf-tests/k8s/demo/dbench

最后为大家介绍一种比较新颖的解决方案。

Portworx 是另一个专为 Kubernetes 设计的云原生存储,专注于高度分散的环境。它是一个主机可寻址存储,每个 volume 都直接映射到它所连接的主机上。它根据应用程序 I/O 的类型提供自动调整。 不幸的是,它不是开源的存储方案。然而,它免费提供了 3 个节点可进行试用。

*注:关于 Portworx 更多信息,见: https://portworx.com/makes-portworx-unique/

在 AKS 上安装 Portworx 很容易,可以使用 Kubernetes 规格生成器:

优点:

弊端:

AKS 测试环境

在本次测试中,测试人员配置了具有 3 个 VM 的基本 Azure AKS 集群。为了能够连接托管的 Premium SSD,测试人员必须使用 E 类型大小的 VM。因此他们选择了 Standard_E2s_v3,只有 2 个 vCPU 和 16GB RAM。

每个 AKS 集群都会自动配置第二个资源组(RG)MC_ <name>,你可以在其中找到所有 VM、NIC 。在 RG 内部,测试人员创建了 3 个 1TB 高级 SSD 托管磁盘并手动将它们连接到每个 VM 中。

它允许我在每个专用于测试的实例中获得 1TB 的空磁盘。据 Azure 称,它的性能可以在 5000 IOPS 和 200 MB/s 吞吐量之间,具体取决于 VM 和磁盘大小。

性能结果

重要说明:单个存储性能测试的结果是无法单独评估的,它们必须相互比较才能显示出差距。测试的方法有很多种,下面是其中较为简单的一种方法。

为了进行测试,测试人员决定使用名为 Dbench 的负载测试器。 它是 Pod 的 Kubernetes 部署清单 , 同时它也是运行 FIO 的地方,并且带有 Flexible IO Tester 等 8 个测试用例。

测试在 Docker 镜像的入口点指定:

注:所有测试的完整测试输出,见:

https://gist.github.com/pupapaik/76c5b7f124dbb69080840f01bf71f924

随机读/写带宽

随机读取测试表明,GlusterFS、Ceph 和 Portworx 在读取时的性能比 AWS 本地磁盘上的主机路径快好几倍,因为它们读取的是缓存。GlusterFS 写入速度最快,它与本地磁盘几乎达到了相同的值。

随机读/写 IOPS

随机 IOPS 显示 Portworx 和 Ceph 效果最佳。Portworx 在写入时的 IOPS 与本机 Azure pvc 几乎相同,这非常好。

读/写延迟

延迟测试返回了有趣的结果,因为本机 Azure pvc 比大多数其他测试存储都慢。Portworx 和 Ceph 实现了最佳读取速度。但是对于写入,GlusterFS 比 Ceph 更好。与其他存储相比,OpenEBS 延迟非常高。

顺序读/写

顺序读/写测试显示与随机测试类似的结果,但 Ceph 的读取是 GlusterFS 的 2 倍多。除了表现非常差的 OpenEBS 之外,写入结果几乎都在同一级别上。

混合读/写 IOPS

最后一个测试用例验证了混合读/写 IOPS,即使在 mixed write 上,Portworx 和 Ceph 也提供了比原生 Azure pvc 更好的 IOPS。

以上就是所有测试结果,希望这篇文章对你有所帮助。

--

参考文献:

[1] https://github.com/rook/rook/blob/master/Documentation/ceph-quickstart.md#ceph-storage-quickstart

[2] https://github.com/gluster/gluster-kubernetes/blob/master/docs/setup-guide.md#deployment

性能测试之-wrk(转)

性能测试之-wrk(转)

转载地址:http://zjumty.iteye.com/blog/2221040

http://www.cnblogs.com/rainy-shurun/p/5867946.html

测试先行是软件系统质量保证的有效手段. 在单元测试方面, 我们有非常成熟的 xUnit 方案. 在集成测试方面, 我们 selenium 等自动化方案. 在性能测试方面也有很多成熟的工具, 比如 LoadRunner, Jmeter 等. 但是很多工具都是给专门的性能测试人员使用的, 功能虽然强大, 但是安装和操作不太方便. 作为开发人员, 我们有些时候想快速验证我们的解决方案是不是存在性能问题, 或者在并发情况下是否有意想不到的问题.  安装 LoadRunner 这样工具, 录制脚本很麻烦, 用起来就像在用大炮打蚊子. 

wrk 是一个很简单的 http 性能测试工具. 也可以叫做 http benchmark 工具. 只有一个命令行, 就能做很多基本的 http 性能测试. 

wrk 的开源的, 代码在 github 上. https://github.com/wg/wrk 

首先要说的一点是: wrk 只能运行在 Unix 类的系统上. 比如 linux, mac, solaris 等. 也只能在这些系统上编译.  

这里不得不说一下, 为什么很多人说 mac 是最好的开发环境. 不是因为使用 mac 逼格有多高. 而是你可以同时得到 windows 和 linux 的好处. 多数 linux 下的开发工具都可以在 mac 上使用. 很多都是预编译好的, 有些只需要编译一下就能用. 

wrk 的一个很好的特性就是能用很少的线程压出很大的并发量. 原因是它使用了一些操作系统特定的高性能 io 机制, 比如 select, epoll, kqueue 等. 其实它是复用了 redis 的 ae 异步事件驱动框架.  确切的说 ae 事件驱动框架并不是 redis 发明的, 它来至于 Tcl的解释器 jim, 这个小巧高效的框架, 因为被 redis 采用而更多的被大家所熟知. 

要用 wrk, 首先要编译 wrk. 
你的机器上需要已经安装了 git 和基本的c编译环境. wrk 本身是用 c 写的. 代码很少. 并且没有使用很多第三方库.  所以编译基本不会遇到什么问题. 

git clone https://github.com/wg/wrk.git  
cd wrk  
make  

就 ok了. 
make 成功以后在目录下有一个 wrk 文件. 就是它了. 你可以把这个文件复制到其他目录, 比如 bin 目录. 或者就这个目录下执行. 

如果编译过程中出现: 

src/wrk.h:11:25: fatal error: openssl/ssl.h: No such file or directory  

 #include <openssl/ssl.h>  

是因为系统中没有安装openssl的库. 

sudo apt-get install libssl-dev 

或 

sudo yum install  openssl-devel 

我们先来做一个简单的性能测试: 

wrk -t12 -c100 -d30s http://www.baidu.com  

30秒钟结束以后可以看到如下输出: 

复制代码
Running 30s test @ http://www.baidu.com  
12 threads and 100 connections  
  Thread Stats   Avg      Stdev     Max   +/- Stdev  
    Latency   538.64ms  368.66ms   1.99s    77.33%  
    Req/Sec    15.62     10.28    80.00     75.35%  
5073 requests in 30.09s, 75.28MB read  
  Socket errors: connect 0, read 5, write 0, timeout 64  
Requests/sec:    168.59  
Transfer/sec:      2.50MB  
复制代码

先解释一下输出: 
12 threads and 100 connections 
这个能看懂英文的都知道啥意思: 用12个线程模拟100个连接. 
对应的参数 -t 和 -c 可以控制这两个参数. 

一般线程数不宜过多. 核数的2到4倍足够了. 多了反而因为线程切换过多造成效率降低. 因为 wrk 不是使用每个连接一个线程的模型, 而是通过异步网络 io 提升并发量. 所以网络通信不会阻塞线程执行. 这也是 wrk 可以用很少的线程模拟大量网路连接的原因. 而现在很多性能工具并没有采用这种方式, 而是采用提高线程数来实现高并发. 所以并发量一旦设的很高, 测试机自身压力就很大. 测试效果反而下降. 

下面是线程统计: 

Thread Stats   Avg      Stdev     Max   +/- Stdev  
  Latency   538.64ms  368.66ms   1.99s    77.33%  
  Req/Sec    15.62     10.28    80.00     75.35%  

Latency: 可以理解为响应时间, 有平均值, 标准偏差, 最大值, 正负一个标准差占比. 
Req/Sec: 每个线程每秒钟的完成的请求数, 同样有平均值, 标准偏差, 最大值, 正负一个标准差占比. 

一般我们来说我们主要关注平均值和最大值. 标准差如果太大说明样本本身离散程度比较高. 有可能系统性能波动很大. 

接下来: 

5073 requests in 30.09s, 75.28MB read  
Socket errors: connect 0, read 5, write 0, timeout 64  
Requests/sec:    168.59  
Transfer/sec:      2.50MB  

30秒钟总共完成请求数和读取数据量. 
然后是错误统计, 上面的统计可以看到, 5个读错误, 64个超时. 
然后是所以线程总共平均每秒钟完成168个请求. 每秒钟读取2.5兆数据量. 

可以看到, 相对于专业性能测试工具. wrk 的统计信息是非常简单的. 但是这些信息基本上足够我们判断系统是否有问题了. 

wrk 默认超时时间是1秒. 这个有点短. 我一般设置为30秒. 这个看上去合理一点. 
如果这样执行命令:    

./wrk -t12 -c100 -d30s -T30s http://www.baidu.com

可以看到超时数就大大降低了, Socket errors 那行没有了: 

复制代码
Running 30s test @ http://www.baidu.com  
12 threads and 100 connections  
  Thread Stats   Avg      Stdev     Max   +/- Stdev  
    Latency     1.16s     1.61s   14.42s    86.52%  
    Req/Sec    22.59     19.31   108.00     70.98%  
4534 requests in 30.10s, 67.25MB read  
Requests/sec:    150.61  
Transfer/sec:      2.23MB
复制代码

通过 -d 可以设置测试的持续时间. 一般只要不是太短都是可以的. 看你自己的忍耐程度了. 
时间越长样本越准确. 如果想测试系统的持续抗压能力, 采用 loadrunner 这样的专业测试工具会更好一点. 

想看看响应时间的分布情况可以加上--latency参数: 

wrk -t12 -c100 -d30s -T30s --latency http://www.baidu.com
复制代码
Running 30s test @ http://www.baidu.com  
12 threads and 100 connections  
  Thread Stats   Avg      Stdev     Max   +/- Stdev  
    Latency     1.22s     1.88s   17.59s    89.70%  
    Req/Sec    14.47      9.92    98.00     77.06%  
  Latency Distribution  
50%  522.18ms  
75%    1.17s  
90%    3.22s  
99%    8.87s  
3887 requests in 30.09s, 57.82MB read  
  Socket errors: connect 0, read 2, write 0, timeout 0  
Requests/sec:    129.19  
Transfer/sec:      1.92MB 
复制代码

可以看到50%在0.5秒以内, %75在1.2s 以内. 看上去还不错. 

看到这里可能有人会说了, HTTP 请求不会总是这么简单的, 通常我们会有 POST,GET 等多个 method, 会有 Header, 会有 body 等. 

在我第一次知道有 wrk 这个工具的时候他确实还不太完善, 要想测试一些复杂的请求还有点难度. 现在 wrk 支持 lua 脚本. 在这个脚本里你可以修改 method, header, body, 可以对 response 做一下自定义的分析. 因为是 lua 脚本, 其实这给了你无限的可能. 但是这样一个强大的功能如果不谨慎使用, 会降低测试端的性能, 测试结果也受到影响. 

一般修改method, header, body不会影响测试端性能, 但是操作 request, response 就要格外谨慎了. 

我们通过一些测试场景在看看怎么使用 lua 脚本. 

POST + header + body. 

首先创建一个 post.lua 的文件: 

wrk.method = "POST"  
wrk.body   = "foo=bar&baz=quux"  
wrk.headers["Content-Type"] = "application/x-www-form-urlencoded"

就这三行就可以了, 当然 headers 可以加入任意多的内容. 
然后执行: 

wrk -t12 -c100 -d30s -T30s --script=post.lua --latency http://www.baidu.com

当然百度可能不接受这个 post 请求. 

对 wrk 对象的修改全局只会执行一次. 
通过 wrk 的源代码可以看到 wrk 对象的源代码有如下属性: 

复制代码
local wrk = {  
   scheme  = "http",  
   host    = "localhost",  
   port    = nil,  
   method  = "GET",  
   path    = "/",  
   headers = {},  
   body    = nil,  
   thread  = nil,  
}  
复制代码

schema, host, port, path 这些, 我们一般都是通过 wrk 命令行参数来指定. 

wrk 提供的几个 lua 的 hook 函数: 

setup 函数 
这个函数在目标 IP 地址已经解析完, 并且所有 thread 已经生成, 但是还没有开始时被调用. 每个线程执行一次这个函数. 
可以通过thread:get(name),  thread:set(name, value)设置线程级别的变量. 

init 函数 
每次请求发送之前被调用. 
可以接受 wrk 命令行的额外参数. 通过 -- 指定. 

delay函数 
这个函数返回一个数值, 在这次请求执行完以后延迟多长时间执行下一个请求. 可以对应 thinking time 的场景. 

request函数 
通过这个函数可以每次请求之前修改本次请求的属性. 返回一个字符串. 这个函数要慎用, 会影响测试端性能. 

response函数 
每次请求返回以后被调用. 可以根据响应内容做特殊处理, 比如遇到特殊响应停止执行测试, 或输出到控制台等等. 

复制代码
function response(status, headers, body)  
if status ~= 200 then  
      print(body)  
      wrk.thread:stop()  
   end  
end  
复制代码

done函数 
在所有请求执行完以后调用, 一般用于自定义统计结果. 

复制代码
done = function(summary, latency, requests)  
   io.write("------------------------------\\n")  
for _, p in pairs({ 50, 90, 99, 99.999 }) do  
      n = latency:percentile(p)  
      io.write(string.format("%g%%,%d\\n", p, n))  
   end  
end
复制代码

下面是 wrk 源代码中给出的完整例子: 

复制代码
local counter = 1  
local threads = {}  
function setup(thread) thread:set("id", counter) table.insert(threads, thread) counter = counter + 1 end
function init(args) requests = 0 responses = 0 local msg = "thread %d created" print(msg:format(id)) end
function request() requests = requests + 1 return wrk.request() end function response(status, headers, body) responses = responses + 1 end
function done(summary, latency, requests) for index, thread in ipairs(threads) do local id = thread:get("id") local requests = thread:get("requests") local responses = thread:get("responses") local msg = "thread %d made %d requests and got %d responses" print(msg:format(id, requests, responses)) end end
复制代码

 

测试复合场景时, 也可以通过 lua 实现访问多个 url. 
例如这个复杂的 lua 脚本, 随机读取 paths.txt 文件中的 url 列表, 然后访问.: 

 

复制代码
counter = 1  
math.randomseed(os.time())  
math.random(); math.random(); math.random()  
function file_exists(file)  
  local f = io.open(file, "rb")  
if f then f:close() end  
return f ~= nil  
end  
function shuffle(paths) local j, k local n = #paths for i = 1, n do j, k = math.random(n), math.random(n) paths[j], paths[k] = paths[k], paths[j] end return paths end
function non_empty_lines_from(file) if not file_exists(file) then return {} end lines = {} for line in io.lines(file) do if not (line == \'\') then lines[#lines + 1] = line end end return shuffle(lines) end paths = non_empty_lines_from("paths.txt") if #paths <= 0 then print("multiplepaths: No paths found. You have to create a file paths.txt with one path per line") os.exit() end print("multiplepaths: Found " .. #paths .. " paths") request = function() path = paths[counter] counter = counter + 1 if counter > #paths then counter = 1 end return wrk.format(nil, path) end
复制代码

关于 cookie 
有些时候我们需要模拟一些通过 cookie 传递数据的场景. wrk 并没有特殊支持, 可以通过 wrk.headers["Cookie"]="xxxxx"实现. 
下面是在网上找的一个离职, 取 Response的cookie作为后续请求的cookie 

复制代码
function getCookie(cookies, name)  
  local start = string.find(cookies, name .. "=")  
if start == nil then  
return nil  
  end  
return string.sub(cookies, start + #name + 1, string.find(cookies, ";", start) - 1)  
end  
response = function(status, headers, body)  
  local token = getCookie(headers["Set-Cookie"], "token")  
if token ~= nil then  
    wrk.headers["Cookie"] = "token=" .. token  
  end  
end 
复制代码

wrk 本身的定位不是用来替换 loadrunner 这样的专业性能测试工具的. 其实有这些功能已经完全能应付平时开发过程中的一些性能验证了.

以上是关于[转]K8S 六种存储解决方案的性能比较测试的主要内容,如果未能解决你的问题,请参考以下文章

Redis 的性能幻想与残酷现实(转)

BrotliDeflateZopfliLZMALZHAMBzip2六种无损数据压缩性能比较

(转)原理到实现 | K8S 存储之 NFS

Ceph 架构与原理

性能测试四十:Mysql存储过程造数据

Ceph持久化存储为k8s应用提供存储方案