2024年8月

回来了一段时间,在某招聘App上陆续有一些人主动找上门,但是基本都是毫无针对性的询问对岗位是否有意向。也陆续投了一些公司的,但是基本都是石沉大海。
一些人跟我说现在大环境不好,招聘的岗位少。这种思维我不敢苟同,只能说很多企业自身经营都成问题,只是借大环境不好来掩盖真实的问题所在。
有一家公司很积极撮合我去面试,感觉还行就赴约了。技术主管很nice,待人比较客气。但是让我印象深刻的是,在一个什么创意园里面,进门连水都没倒一杯。这也就算了,问着问着才知道这原来是家外包公司,没有公积金,只有社保。然后从事RPA相关的业务,然后老板还想搞什么人工智能的项目。
这公司HR貌似对我很有意向,还询问了我是否平时有使用AI编写代码的能力。我只能如实告诉她,自己平时使用小浣熊。然后她问我是哪个公司的,那个外国公司,名称我哪记得住。就告诉她,也可以使用阿里的通义灵码。我有时感觉她就是在套信息,好回头告诉老板,怎么说呢?她问我觉得AI编写的代码如何,我只能告诉她,但凡有点深度的代码,AI都是写不出来的。这个确实,ChatGPT稍微问点有深度的东西,它真的就开始胡说八道,毕竟它只是个加强版的搜索引擎。
然后投了2家公司,直接就跟我说不合适。后来才发现法本和德科也是外包公司,而且距离我住的地方挺远的。有意思的是,拿着本科的学历要求从事人工智能的岗位,估计是看我学历不够硬,想招聘985或211学历有经验的人。
最有意思的是一家琴智的公司,说是什么研究院改制为民营,然后骨干流失到政府其他研究机构。那么问题来了,如果待遇可以为何人员会流失。而且地方在一个创业孵化中心的位置。这位HR一上来就跟我介绍她公司的一些情况,主要从事遥感方面,要人工智能的。跟高校有合作,希望找一个会拉项目又会写代码搭建平台的人,薪资11K起。我觉得要是有这水平,还来你这应聘,直接出去创业不更好。
我一听顿时不知所措。遥感技术,没个博士生你敢来应聘,本来招生就少但就业率高。只能说可以考虑,结果就要求交换微信。结果后来给我打了个电话,咨询了一些情况,估计也是觉得我学历不大行,让我等通知。
我现在回头想想,觉得这家HR在保护我,在各种方法地让我不要去她公司。可能是待遇真的不行,不想坑我吧。而且这公司创业5年了,关于它的信息寥寥无几,给人感觉毫无存在感。唯一的好处或许就是它招聘上写的早九晚五,偶尔加班了这几个字样了。

背景

这个问题主要考察了Linux命令的使用,find 命令和 grep命令,在linux系统中,这两个命令用的比较广泛,工作中常常可以用来查找到指定的日志内容。
今天我们就来学一下两个命令,然后回答下这个问题吧。

命令介绍

1、find命令

find常用来在Linux系统中查找文件或者目录,查找到的文件名会被输入到标准输出。比如,你想要
1.1 查找某一个目录下所有的后缀名为.log的日志文件,可以这样使用
find /path -name ".log"
1.2 在当前目录下搜索名为filename.txt的文件
find . -name filename.txt
1.3 搜索指定大小的文件
find . -size +1M
1.4 搜索所有目录
find . -type d
总结: find命令,顾名思义,用于查找,在linux系统中通常是查询文件的作用,找到文件后再针对文件中的内容做查找或者过滤。
常见的参数选项有:

  • -name pattern:根据文件名进行匹配
  • -type type:根据文件类型进行匹配
  • -size n[cwbkMG]:根据文件大小进行匹配
  • -exec command {} ;:对搜索到的文件执行指定命令
  • -mtime n:根据文件的修改时间进行匹配
  • -maxdepth levels:限制递归搜索的深度
  • -mindepth levels:设置递归搜索的最小深度
  • -delete:删除搜索到的文件

2、grep 命令

grep
grep是一个常用的搜索工具,可在文件中搜索指定的模式并输出匹配的行,下面是一些常见的用法
1.1 在文件中搜索指定字符串
grep "pattern" file.txt
1.2 在多个文件中搜索指定字符串
grep "pattern" file1.txt file2.txt
1.3 递归搜索文件中的指定字符串
grep -r "pattern" /path/directory/
1.4 查找以2024开头的行
grep "^2024" file.txt
1.5 查找包含error的行并显示行号
grep -n "error" logfile.txt
总结:grep 命令,用于过滤,常见用法是从文件中过滤出包含指定字符内容,或者日志段等。
常见参数选项有:

  • 忽略大小写
    grep -i "pattern" file.txt
  • 显示匹配的行号
    grep -n "pattern" file.txt
  • 显示不匹配的行
    grep -v "pattern" file.txt
  • 显示匹配行上下文
    grep -C 2 "pattern" file.txt
  • 显示匹配行之后的内容
    grep -A 2 "pattern" file.txt
  • 显示匹配行之前的内容
    grep -B 2 "pattern" file.txt
  • 使用正则表达式
    grep -E "pattern" file.txt
  • 统计匹配行数
    grep -c "pattern" file.txt

如何在一些文件中, 找到log文件中指定字符的日志段?

1、首先,我们找到某一文件目录/path/directory下的所有的log文件
find /path/directory -name "*.log"
2、然后,我们在find输出的内容中进行grep 操作, 输出包含pattern的内容。
find /path/directory -type f -name "*.log" -exec grep "pattern" {} +
解释
/path/directory:是你要搜索的目录路径,可以根据实际情况进行替换。
-type f:表示只搜索普通文件。
-name "*.log":表示只搜索文件名以 .log 结尾的文件。
-exec grep "pattern" {} +:在找到的文件中执行 grep "pattern" 命令进行搜索。

CMS(Concurrent Mark Sweep)垃圾收集器是以“最短的停顿”著称的垃圾回收器,因此也是 JDK 9 之前使用最广泛的垃圾回收器之一。那么,问题来了,为什么 CMS 能实现最短停顿时间?CMS 垃圾回收器的工作原理又是啥呢?接下来,我们一起来看。

CMS 工作原理

CMS 之所以能实现最短停顿时间是和它的工作原理分不开的,它们存在因果关联关系,
因为 CMS 的工作原理,所以决定了 CMS 能实现最短的停顿时间

“最短停顿时间”指的是垃圾回收过程中,应用程序暂停的时间尽可能短。也就是在垃圾回收时,Stop The World(STW,全局停顿)时间要尽量短,因为只有 STW 够短,那么应用程序才能更快的执行。

那么 CMS 工作原理是啥呢?

CMS 垃圾回收器执行步骤分为以下两步:

  1. 标记
  2. 清除

而在以上过程中,“标记”阶段是需要大量时间的,反而“清除”需要的时间比较短(因为清除只需要把垃圾对象删除“回收”即可)。

那怎么才能提升整体的执行效率,保证最短的停顿时间呢?

于是 CMS 设计者开始动脑子了,这时候有人就提出:既然“标记”阶段比较费时,那我们就将“标记”阶段分阶段处理好了,并且最好能让他能与应用线程一起执行,这样就不需要 STW(全局停顿)了,那么停顿时间不久短了嘛?

因此 CMS 的设计者将垃圾回收的“标记”阶段,变成了以下 3 个阶段:

  1. 初始标记(STW)
    :只标记和 GC Roots 直接关联的对象,执行速度很快。
  2. 并发标记(和用户线程并发执行)
    :GC Roots 直接关联的对象继续往下(一直)遍历和标记,耗时会比较长。
  3. 重新标记(STW)
    :对上一步“并发标记”阶段因为用户线程执行,而导致变动的对象进行修正标记。

具体执行流程如下:
image.png
在整个标记的过程中,只有初始标记和重新标记需要 STW,但是初始标记只标记和 GC Roots 直接关联的对象,而重新标记只是对“并发标记”阶段的用户线程进行修正标记,因此这两个阶段执行的时间都很短,所以整个 STW 停顿时间也就很短。

并且为了让 CMS 能够拥有更短的停顿时间,所以在“标记”阶段完成之后,CMS 采用的是并发清除策略,也就是 GC 垃圾回收线程和用户线程一起执行,这样就不需要 STW 了,所以执行效率就更高了。

CMS 完整执行流程如下:

  1. 初始标记(STW)
    :只标记和 GC Roots 直接关联的对象,执行速度很快。
  2. 并发标记(和用户线程并发执行)
    :GC Roots 直接关联的对象继续往下(一直)遍历和标记,耗时会比较长。
  3. 重新标记(STW)
    :对上一步“并发标记”阶段因为用户线程执行,而导致变动的对象进行修正标记。
  4. 并发清除(和用户线程并发执行)
    :使用并发-清除算法将垃圾对象进行清除。

执行流程如下图所示:
image.png

课后思考

虽然 CMS 能实现最短停顿时间,但它也存在一些缺点,例如:会内存碎片的问题。那为什么它会有内存碎片的问题?又怎么能解决内存碎片的问题?

本文已收录到我的面试小站
www.javacn.site
,其中包含的内容有:Redis、JVM、并发、并发、MySQL、Spring、Spring MVC、Spring Boot、Spring Cloud、MyBatis、设计模式、消息队列等模块。

VMware vCenter Server 是 VMware vSphere 解决方案的核心组件,用于管理多个 ESXi 主机并实现更多高级功能特性(如 HA、DRS 以及 FT 等),相信大家已经非常熟悉了。对于 vCenter Server 的安装和部署,可能大部分人会选择使用 GUI 方式部署 vCenter Server,并使用 vCenter Server ISO 镜像文件中的 \vcsa-ui-installer\win32\installer.exe 部署程序,如下图所示。部署过程分为两个阶段,第一阶段配置并部署 vCenter Server 的 OVA 虚拟机文件;第二阶段设置并启动 vCenter Server 虚拟机的服务。

其实,对于 vCenter Server 的部署还支持 CLI 部署方式,像我这种在实验室中经常需要安装删除 vCenter Server 环境的来说,可能更喜欢 CLI 自动化部署方式,因为这样可以节约很多时间来做其他事情。如果你能了解 vCenter Server 的 GUI 部署过程,那对于使用 CLI 的部署方式应该也会非常简单。下面来看看如何使用 CLI 方式来部署我们所需要的 vCenter Server 环境,涵盖了 GUI 部署过程中的所有场景,更多内容和细节请查看
vCenter Server 产品文档

将 vCenter Server ISO 镜像文件挂载到本地虚拟机光驱 F 盘符上,可以看到 vCenter Server 安装镜像文件中包含的所有文件,如下图所示。注,对于 vCenter Server 的 CLI 部署将使用 PowerShell 方式执行,ISO 镜像文件基于 vCenter Server 8.0 U2b,内部版本号 23319993。

如你所见,要是使用 GUI 部署方式则选择 vcsa-ui-installer 文件夹下面所对应的部署程序,如果使用 CLI 部署方式则使用 \vcsa-cli-installer\win32\ 文件夹下面的 vcsa-deploy.exe 部署程序。

使用 CLI 部署方式除了要使用 vcsa-deploy.exe 部署程序以外,还需要用到 \vcsa-cli-installer\templates\install\ 文件夹下面的 JSON 配置文件,这些 JSON 配置文件中预定义了在部署 vCenter Server 过程中所需配置的信息,如部署位置 、FQDN 等。

这些 JSON 配置文件一共分为两类,一类是 *_on_ESXi.json 文件,用于将 vCenter Server 虚拟机部署在 ESXi 主机上;另一类是  *_on_VC.json 文件,用于将 vCenter Server 虚拟机部署在现有 vCenter Server 上。第一类通常是用于初始部署,第二类是在已有 vSphere 环境的情况下进行部署,后面将分别介绍这几种 JSON 配置文件所代表的部署场景。

先看一下 vcsa-deploy.exe 部署程序的命令帮助,CLI 方式同样可以实现安装(install)、升级(upgrade)以及迁移(migrate)等功能。

F:\vcsa-cli-installer\win32\vcsa-deploy.exe --help

使用以下命令可以查看 vCenter Server 支持的部署大小以及对应的默认配置,可根据环境的情况自行选择哪种部署选项。

F:\vcsa-cli-installer\win32\vcsa-deploy.exe --supported-deployment-sizes

如果对 JSON 配置文件中的哪个配置选项有疑问,可以使用以下命令查看 JSON 配置文件中关于配置选项的说明。

F:\vcsa-cli-installer\win32\vcsa-deploy.exe install --template-help | more

下面正式进入 CLI 部署的配置过程,涉及到 vCenter Server 的多种部署场景,不同的场景会使用到不同的 JSON 配置文件以及环境。

一、将 vCenter Server 虚拟机部署到 ESXi 主机上

将 vCenter Server 虚拟机部署到 ESXi 主机上,通常是 ESXi 主机上已经具有本地存储和网络端口组等环境信息,这种场景需要用到 vCenter Server 镜像文件中的 embedded_vCSA_on_ESXi.json 配置文件,根据这个配置文件修改为实际环境中的信息,示例如下:

{
    "__version": "2.13.0",
    "__comments": "将 vCenter Server 虚拟机部署到 ESXi 主机上",
    "new_vcsa": {
        "esxi": {
            "hostname": "192.168.32.51",
            "username": "root",
            "password": "vSphere8@password",
            "deployment_network": "VM Network",
            "datastore": "Datastore-01"
        },
        "appliance": {
            "__comments": [
                "vCenter Server 部署参数"
            ],
            "thin_disk_mode": true,
            "deployment_option": "tiny",
            "name": "vcenter01"
        },
        "network": {
            "ip_family": "ipv4",
            "mode": "static",
            "system_name": "vcenter01.mulab.local",
            "ip": "192.168.32.55",
            "prefix": "24",
            "gateway": "192.168.32.254",
            "dns_servers": [
                "192.168.32.3"
            ]
        },
        "os": {
            "password": "vSphere8@password",
            "ntp_servers": "192.168.32.3",
            "ssh_enable": true
        },
        "sso": {
            "password": "vSphere8@password",
            "domain_name": "vsphere.local"
        }
    },
    "ceip": {
        "description": {
            "__comments": [
                "++++VMware Customer Experience Improvement Program (CEIP)++++",
                "VMware's Customer Experience Improvement Program (CEIP) ",
                "provides VMware with information that enables VMware to ",
                "improve its products and services, to fix problems, ",
                "and to advise you on how best to deploy and use our ",
                "products. As part of CEIP, VMware collects technical ",
                "information about your organization's use of VMware ",
                "products and services on a regular basis in association ",
                "with your organization's VMware license key(s). This ",
                "information does not personally identify any individual. ",
                "",
                "Additional information regarding the data collected ",
                "through CEIP and the purposes for which it is used by ",
                "VMware is set forth in the Trust & Assurance Center at ",
                "http://www.vmware.com/trustvmware/ceip.html . If you ",
                "prefer not to participate in VMware's CEIP for this ",
                "product, you should disable CEIP by setting ",
                "'ceip_enabled': false. You may join or leave VMware's ",
                "CEIP for this product at any time. Please confirm your ",
                "acknowledgement by passing in the parameter ",
                "--acknowledge-ceip in the command line.",
                "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"
            ]
        },
        "settings": {
            "ceip_enabled": false
        }
    }
}

另存为 deploy-on-esxi.json 配置文件到 files 文件夹,使用以下命令执行部署前预检查。

F:\vcsa-cli-installer\win32\vcsa-deploy.exe install .\files\deploy-on-esxi.json --accept-eula --no-ssl-certificate-verification --precheck-only

如果 JSON 配置文件与部署环境检查成功,则会输出类似下面的内容。

[SUCCEEDED] Successfully executed Task 'Precheck ovftool commandline parameters
to deploy a vCenter Server Appliance' in TaskFlow 'deploy-on-esxi' at 09:03:06
=================================== 09:03:07 ===================================
Result and Log File Information...
WorkFlow log directory:
C:\Users\JUNIOR~1\AppData\Local\Temp\vcsaCliInstaller-2024-08-06-09-02-x2dv8gw0\workflow_1722934960074

如果 JSON 配置文件与部署环境检查确定没问题,使用以下命令执行正式部署。

F:\vcsa-cli-installer\win32\vcsa-deploy.exe install .\files\deploy-on-esxi.json --accept-eula --no-ssl-certificate-verification

如果 vCenter Server 部署成功,则会输出类似下面的内容。

Successfully completed VCSA deployment.  VCSA Deployment Start Time:
2024-08-06T09:11:48.264Z VCSA Deployment End Time: 2024-08-06T09:33:42.921Z
 [SUCCEEDED] Successfully executed Task 'MonitorDeploymentTask: Monitoring
Deployment' in TaskFlow 'deploy-on-esxi' at 09:33:55
Monitoring VCSA Deploy task completed
== [START] Start executing Task: Join active domain if necessary at 09:33:56 ==
Domain join task not applicable, skipping task
 [SUCCEEDED] Successfully executed Task 'Running deployment: Domain Join' in
TaskFlow 'deploy-on-esxi' at 09:33:56
 [START] Start executing Task: Provide the login information about new
appliance. at 09:33:56
    Appliance Name: vcenter01
    System Name: vcenter01.mulab.local
    System IP: 192.168.32.55
    Log in as: Administrator@vsphere.local
 [SUCCEEDED] Successfully executed Task 'ApplianceLoginSummaryTask: Provide
appliance login information.' in TaskFlow 'deploy-on-esxi' at 09:33:56
=================================== 09:33:57 ===================================
Result and Log File Information...
WorkFlow log directory:
C:\Users\JUNIOR~1\AppData\Local\Temp\vcsaCliInstaller-2024-08-06-09-03-4auw6_e4\workflow_1722935032748

登录 vCenter Server,查看部署情况。

二、将 vCenter Server 虚拟机部署到现有 vCenter Server 上

将 vCenter Server 虚拟机部署到现有 vCenter Server 上,通常是当前环境中已经具有 vSphere 环境并想在之上部署另外一套环境,这种场景需要用到 vCenter Server 镜像文件中的 embedded_vCSA_on_VC.json 配置文件,根据这个配置文件修改为实际环境中的信息,示例如下:

{
    "__version": "2.13.0",
    "__comments": "将 vCenter Server 虚拟机部署到现有 vCenter Server 上",
    "new_vcsa": {
        "vc": {
            "__comments": [
                "'datacenter' must end with a datacenter name, and only with a datacenter name.",
                "'target' must end with an ESXi hostname, a cluster name, or a resource pool name.",
                "The item 'Resources' must precede the resource pool name. ",
                "All names are case-sensitive. ",
                "For details and examples, refer to template help, i.e. vcsa-deploy {install|upgrade|migrate} --template-help"
            ],
            "hostname": "vcsa8-01.mulab.local",
            "username": "administrator@vsphere.local",
            "password": "vSphere8@password",
            "deployment_network": "VM Network",
            "datacenter": [
                "mulab"
            ],
            "datastore": "vsanDatastore",
            "target": [
			    "vsan8-hci-esa"
            ]
        },
        "appliance": {
            "__comments": [
                "vCenter Server 部署参数"
            ],
            "thin_disk_mode": true,
            "deployment_option": "tiny",
            "name": "vcenter02"
        },
        "network": {
            "ip_family": "ipv4",
            "mode": "static",
            "system_name": "vcenter02.mulab.local",
            "ip": "192.168.32.56",
            "prefix": "24",
            "gateway": "192.168.32.254",
            "dns_servers": [
                "192.168.32.3"
            ]
        },
        "os": {
            "password": "vSphere8@password",
            "ntp_servers": "192.168.32.3",
            "ssh_enable": true
        },
        "sso": {
            "password": "vSphere8@password",
            "domain_name": "vsphere.local"
        }
    },
    "ceip": {
        "description": {
            "__comments": [
                "++++VMware Customer Experience Improvement Program (CEIP)++++",
                "VMware's Customer Experience Improvement Program (CEIP) ",
                "provides VMware with information that enables VMware to ",
                "improve its products and services, to fix problems, ",
                "and to advise you on how best to deploy and use our ",
                "products. As part of CEIP, VMware collects technical ",
                "information about your organization's use of VMware ",
                "products and services on a regular basis in association ",
                "with your organization's VMware license key(s). This ",
                "information does not personally identify any individual. ",
                "",
                "Additional information regarding the data collected ",
                "through CEIP and the purposes for which it is used by ",
                "VMware is set forth in the Trust & Assurance Center at ",
                "http://www.vmware.com/trustvmware/ceip.html . If you ",
                "prefer not to participate in VMware's CEIP for this ",
                "product, you should disable CEIP by setting ",
                "'ceip_enabled': false. You may join or leave VMware's ",
                "CEIP for this product at any time. Please confirm your ",
                "acknowledgement by passing in the parameter ",
                "--acknowledge-ceip in the command line.",
                "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"
            ]
        },
        "settings": {
            "ceip_enabled": false
        }
    }
}

另存为 deploy-on-vcenter.json 配置文件到 files 文件夹,使用以下命令执行部署前预检查。

F:\vcsa-cli-installer\win32\vcsa-deploy.exe install .\files\deploy-on-vcenter.json --accept-eula --no-ssl-certificate-verification --precheck-only

如果 JSON 配置文件与部署环境检查成功,则会输出类似下面的内容。

[SUCCEEDED] Successfully executed Task 'Precheck ovftool commandline parameters
to deploy a vCenter Server Appliance' in TaskFlow 'deploy-on-vcenter' at
09:05:55
=================================== 09:05:56 ===================================
Result and Log File Information...
WorkFlow log directory:
C:\Users\JUNIOR~1\AppData\Local\Temp\vcsaCliInstaller-2024-08-06-09-05-z39vgzmr\workflow_1722935126586

如果 JSON 配置文件与部署环境检查确定没问题,使用以下命令执行正式部署。

F:\vcsa-cli-installer\win32\vcsa-deploy.exe install .\files\deploy-on-vcenter.json --accept-eula --no-ssl-certificate-verification

如果 vCenter Server 部署成功,则会输出类似下面的内容。

Successfully completed VCSA deployment.  VCSA Deployment Start Time:
2024-08-06T09:18:57.371Z VCSA Deployment End Time: 2024-08-06T09:47:03.177Z
 [SUCCEEDED] Successfully executed Task 'MonitorDeploymentTask: Monitoring
Deployment' in TaskFlow 'deploy-on-vcenter' at 09:47:27
Monitoring VCSA Deploy task completed
== [START] Start executing Task: Join active domain if necessary at 09:47:28 ==
Domain join task not applicable, skipping task
 [SUCCEEDED] Successfully executed Task 'Running deployment: Domain Join' in
TaskFlow 'deploy-on-vcenter' at 09:47:28
 [START] Start executing Task: Provide the login information about new
appliance. at 09:47:29
    Appliance Name: vcenter02
    System Name: vcenter02.mulab.local
    System IP: 192.168.32.56
    Log in as: Administrator@vsphere.local
 [SUCCEEDED] Successfully executed Task 'ApplianceLoginSummaryTask: Provide
appliance login information.' in TaskFlow 'deploy-on-vcenter' at 09:47:29
=================================== 09:47:30 ===================================
Result and Log File Information...
WorkFlow log directory:
C:\Users\JUNIOR~1\AppData\Local\Temp\vcsaCliInstaller-2024-08-06-09-06-42t35ek9\workflow_1722935200962

登录 vCenter Server,查看部署情况。

三、部署多个 vCenter Server

前面两种场景分别是将 vCenter Server 部署到 ESXi 主机或者现有 vCenter Server 上,针对前面两种情况,可能你有同时部署多个 vCenter Server 环境的需求,可以将 embedded_vCSA_on_ESXi.json 或者 embedded_vCSA_on_VC.json 配置文件复制为多个 vCenter Server 的部署配置文件,然后将这些文件放在同一个文件夹内(如 deploy-multiple-vcenter),部署的时候指定这个文件夹即可同时部署多个 vCenter Server 环境。

如果已经配置好了 JSON 配置文件并放在了同一文件夹(如 deploy-multiple-vcenter),使用以下命令执行部署前预检查。

F:\vcsa-cli-installer\win32\vcsa-deploy.exe install .\files\deploy-multiple-vcenter\ --accept-eula --no-ssl-certificate-verification --precheck-only

如果文件夹中的 JSON 配置文件与部署环境检查确定没问题,使用以下命令执行正式部署。

F:\vcsa-cli-installer\win32\vcsa-deploy.exe install .\files\deploy-multiple-vcenter\ --accept-eula --no-ssl-certificate-verification

四、部署具有增强型链接模式的 vCenter Server

增强型链接模式(ELM)就是将环境中的多个 vCenter Server 实例链接到一起并加入到同一个 SSO 域当中,当你通过 SSO 管理员登录到任意一个 vCenter Server 的时候,可以同时看到链接的其他 vCenter Server,这样就可以实现多个 vCenter Server 实例的统一管理。可以查看这篇(
使用cmsso-util命令进行链接、删除、修改多个vCenter Server(VCSA)的SSO域。
)文章了解更多关于增强型链接模式 vCenter Server 的相关配置。

部署具有增强型链接模式的 vCenter Server 虚拟机到 ESXi 主机上,需要用到 vCenter Server 镜像文件中的 embedded_vCSA_replication_on_ESXi.json 配置文件,根据这个配置文件修改为实际环境中的信息,示例如下:

{
    "__version": "2.13.0",
    "__comments": "将具有增强型链接模式的 vCenter Server 虚拟机部署到 ESXi 主机上",
    "new_vcsa": {
        "esxi": {
            "hostname": "192.168.32.51",
            "username": "root",
            "password": "vSphere8@password",
            "deployment_network": "VM Network",
            "datastore": "Datastore-01"
        },
        "appliance": {
            "__comments": [
                "vCenter Server 部署参数"
            ],
            "thin_disk_mode": true,
            "deployment_option": "tiny",
            "name": "vcenter03"
        },
        "network": {
            "ip_family": "ipv4",
            "mode": "static",
            "system_name": "vcenter03.mulab.local",
            "ip": "192.168.32.57",
            "prefix": "24",
            "gateway": "192.168.32.254",
            "dns_servers": [
                "192.168.32.3"
            ]
        },
        "os": {
            "password": "vSphere8@password",
            "ntp_servers": "192.168.32.3",
            "ssh_enable": true
        },
        "sso": {
            "password": "vSphere8@password",
            "domain_name": "vsphere.local",
            "first_instance": false,
            "replication_partner_hostname": "vcenter01.mulab.local",
            "sso_port": 443
        }
    },
    "ceip": {
        "description": {
            "__comments": [
                "++++VMware Customer Experience Improvement Program (CEIP)++++",
                "VMware's Customer Experience Improvement Program (CEIP) ",
                "provides VMware with information that enables VMware to ",
                "improve its products and services, to fix problems, ",
                "and to advise you on how best to deploy and use our ",
                "products. As part of CEIP, VMware collects technical ",
                "information about your organization's use of VMware ",
                "products and services on a regular basis in association ",
                "with your organization's VMware license key(s). This ",
                "information does not personally identify any individual. ",
                "",
                "Additional information regarding the data collected ",
                "through CEIP and the purposes for which it is used by ",
                "VMware is set forth in the Trust & Assurance Center at ",
                "http://www.vmware.com/trustvmware/ceip.html . If you ",
                "prefer not to participate in VMware's CEIP for this ",
                "product, you should disable CEIP by setting ",
                "'ceip_enabled': false. You may join or leave VMware's ",
                "CEIP for this product at any time. Please confirm your ",
                "acknowledgement by passing in the parameter ",
                "--acknowledge-ceip in the command line.",
                "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"
            ]
        },
        "settings": {
            "ceip_enabled": false
        }
    }
}

另存为 deploy-elm-on-esxi.json 配置文件到 files 文件夹,使用以下命令执行部署前预检查。

F:\vcsa-cli-installer\win32\vcsa-deploy.exe install .\files\deploy-elm-on-esxi.json --accept-eula --no-ssl-certificate-verification --precheck-only

如果 JSON 配置文件与部署环境检查成功,则会输出类似下面的内容。

[SUCCEEDED] Successfully executed Task 'Precheck ovftool commandline parameters
to deploy a vCenter Server Appliance' in TaskFlow 'deploy-elm-on-esxi' at
09:42:46
=================================== 09:42:46 ===================================
Result and Log File Information...
WorkFlow log directory:
C:\Users\JUNIOR~1\AppData\Local\Temp\vcsaCliInstaller-2024-08-06-09-42-qzp_kn0v\workflow_1722937339642

如果 JSON 配置文件与部署环境检查确定没问题,使用以下命令执行正式部署。

F:\vcsa-cli-installer\win32\vcsa-deploy.exe install .\files\deploy-elm-on-esxi.json --accept-eula --no-ssl-certificate-verification

如果 vCenter Server 部署成功,则会输出类似下面的内容。

Successfully completed VCSA deployment.  VCSA Deployment Start Time:
2024-08-06T09:52:23.624Z VCSA Deployment End Time: 2024-08-06T10:15:36.574Z
 [SUCCEEDED] Successfully executed Task 'MonitorDeploymentTask: Monitoring
Deployment' in TaskFlow 'deploy-elm-on-esxi' at 10:16:03
Monitoring VCSA Deploy task completed
== [START] Start executing Task: Join active domain if necessary at 10:16:04 ==
Domain join task not applicable, skipping task
 [SUCCEEDED] Successfully executed Task 'Running deployment: Domain Join' in
TaskFlow 'deploy-elm-on-esxi' at 10:16:04
 [START] Start executing Task: Provide the login information about new
appliance. at 10:16:05
    Appliance Name: vcenter03
    System Name: vcenter03.mulab.local
    System IP: 192.168.32.57
    Log in as: Administrator@vsphere.local
 [SUCCEEDED] Successfully executed Task 'ApplianceLoginSummaryTask: Provide
appliance login information.' in TaskFlow 'deploy-elm-on-esxi' at 10:16:05
=================================== 10:16:05 ===================================
Result and Log File Information...
WorkFlow log directory:
C:\Users\JUNIOR~1\AppData\Local\Temp\vcsaCliInstaller-2024-08-06-09-44-5l_hhkqk\workflow_1722937468472

登录 vCenter Server,查看部署情况。

部署具有增强型链接模式的 vCenter Server 虚拟机到现有 vCenter Server 上,需要用到 vCenter Server 镜像文件中的 embedded_vCSA_replication_on_VC.json 配置文件,根据这个配置文件修改为实际环境中的信息,示例如下:

{
    "__version": "2.13.0",
    "__comments": "将具有增强型链接模式的 vCenter Server 虚拟机部署到现有 vCenter Server 上",
    "new_vcsa": {
        "vc": {
            "__comments": [
                "'datacenter' must end with a datacenter name, and only with a datacenter name.",
                "'target' must end with an ESXi hostname, a cluster name, or a resource pool name.",
                "The item 'Resources' must precede the resource pool name. ",
                "All names are case-sensitive. ",
                "For details and examples, refer to template help, i.e. vcsa-deploy {install|upgrade|migrate} --template-help"
            ],
            "hostname": "vcsa8-01.mulab.local",
            "username": "administrator@vsphere.local",
            "password": "vSphere8@password",
            "deployment_network": "VM Network",
            "datacenter": [
                "mulab"
            ],
            "datastore": "vsanDatastore",
            "target": [
			    "vsan8-hci-esa"
            ]
        },
        "appliance": {
            "__comments": [
                "vCenter Server 部署参数"
            ],
            "thin_disk_mode": true,
            "deployment_option": "tiny",
            "name": "vcenter04"
        },
        "network": {
            "ip_family": "ipv4",
            "mode": "static",
            "system_name": "vcenter04.mulab.local",
            "ip": "192.168.32.58",
            "prefix": "24",
            "gateway": "192.168.32.254",
            "dns_servers": [
                "192.168.32.3"
            ]
        },
        "os": {
            "password": "vSphere8@password",
            "ntp_servers": "192.168.32.3",
            "ssh_enable": true
        },
        "sso": {
            "password": "vSphere8@password",
            "domain_name": "vsphere.local",
            "first_instance": false,
            "replication_partner_hostname": "vcenter02.mulab.local",
            "sso_port": 443
        }
    },
    "ceip": {
        "description": {
            "__comments": [
                "++++VMware Customer Experience Improvement Program (CEIP)++++",
                "VMware's Customer Experience Improvement Program (CEIP) ",
                "provides VMware with information that enables VMware to ",
                "improve its products and services, to fix problems, ",
                "and to advise you on how best to deploy and use our ",
                "products. As part of CEIP, VMware collects technical ",
                "information about your organization's use of VMware ",
                "products and services on a regular basis in association ",
                "with your organization's VMware license key(s). This ",
                "information does not personally identify any individual. ",
                "",
                "Additional information regarding the data collected ",
                "through CEIP and the purposes for which it is used by ",
                "VMware is set forth in the Trust & Assurance Center at ",
                "http://www.vmware.com/trustvmware/ceip.html . If you ",
                "prefer not to participate in VMware's CEIP for this ",
                "product, you should disable CEIP by setting ",
                "'ceip_enabled': false. You may join or leave VMware's ",
                "CEIP for this product at any time. Please confirm your ",
                "acknowledgement by passing in the parameter ",
                "--acknowledge-ceip in the command line.",
                "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"
            ]
        },
        "settings": {
            "ceip_enabled": false
        }
    }
}

另存为 deploy-elm-on-vcenter.json 配置文件到 files 文件夹,使用以下命令执行部署前预检查。

F:\vcsa-cli-installer\win32\vcsa-deploy.exe install .\files\deploy-elm-on-vcenter.json --accept-eula --no-ssl-certificate-verification --precheck-only

如果 JSON 配置文件与部署环境检查成功,则会输出类似下面的内容。

[SUCCEEDED] Successfully executed Task 'Precheck ovftool commandline parameters
to deploy a vCenter Server Appliance' in TaskFlow 'deploy-elm-on-vcenter' at
09:55:12
=================================== 09:55:13 ===================================
Result and Log File Information...
WorkFlow log directory:
C:\Users\JUNIOR~1\AppData\Local\Temp\vcsaCliInstaller-2024-08-06-09-54-lps7j7v1\workflow_1722938084996

如果 JSON 配置文件与部署环境检查确定没问题,使用以下命令执行正式部署。

F:\vcsa-cli-installer\win32\vcsa-deploy.exe install .\files\deploy-elm-on-vcenter.json --accept-eula --no-ssl-certificate-verification

如果 vCenter Server 部署成功,则会输出类似下面的内容。

[SUCCEEDED] Successfully executed Task 'MonitorDeploymentTask: Monitoring
Deployment' in TaskFlow 'deploy-elm-on-vcenter' at 10:35:50
Monitoring VCSA Deploy task completed
== [START] Start executing Task: Join active domain if necessary at 10:35:51 ==
Domain join task not applicable, skipping task
 [SUCCEEDED] Successfully executed Task 'Running deployment: Domain Join' in
TaskFlow 'deploy-elm-on-vcenter' at 10:35:51
 [START] Start executing Task: Provide the login information about new
appliance. at 10:35:52
    Appliance Name: vcenter04
    System Name: vcenter04.mulab.local
    System IP: 192.168.32.58
    Log in as: Administrator@vsphere.local
 [SUCCEEDED] Successfully executed Task 'ApplianceLoginSummaryTask: Provide
appliance login information.' in TaskFlow 'deploy-elm-on-vcenter' at 10:35:52
=================================== 10:35:52 ===================================
Result and Log File Information...
WorkFlow log directory:
C:\Users\JUNIOR~1\AppData\Local\Temp\vcsaCliInstaller-2024-08-06-09-56-8s9_eg18\workflow_1722938163017

登录 vCenter Server,查看部署情况。

部署具有增强型链接模式 vCenter Server 的时候,可能大部分情况是环境中已经有第一个 vCenter Server 了,然后再使用上面所述的方式部署第二个 vCenter Server 并将其链接到第一个 vCenter Server 中。其实,如果环境中没有第一个 vCenter Server 的时候,也可以同时部署增强型链接模式的 vCenter Server,比如,deploy-vcenter01-on-esxi.json 配置文件用于部署第一个 vCenter Server,deploy-vcenter01-elm-on-esxi.json 配置文件用于部署具有增强型链接模式的第二个 vCenter Server,将这两个 JSON 配置文件放在同一文件夹中,在部署的时候指定这个文件夹,这样 CLI 部署完第一个 vCenter Server 后,然后当第二个 vCenter Server 部署的时候就会自动链接到第一个 vCenter Server 中去。

五、将 vCenter Server 虚拟机部署到 ESXi 主机上并配置单节点 vSAN ESA 集群

对于初始构建 vSphere 环境的用户来说,可能 ESXi 主机安装部署以后,想直接将 ESXi 主机应用于 vSAN 环境,同时又想将 vSAN 存储用于承载 vCenter Server。如果使用以上所述场景,部署 vCenter Server 的前提是环境中必须具有用于存放 vCenter Server 的本地/共享存储,但是,VMware 这里提供了一种方式,在部署 vCenter Server 之前,可以直接将初始安装的 ESXi 主机转换成 vSAN 集群的第一台主机,这样配置为单节点 vSAN 集群后,再将配置后的 vSAN 存储用于承载 vCenter Server 虚拟机所使用的存储。如果是这样,就不用单独先配置 ESXi 主机的本地/共享存储用于部署 vCenter Server 了,这也是 VMware 很多解决方案所使用的方式,比如 VMware Cloud Foundation。

将 vCenter Server 虚拟机部署到 ESXi 主机上并配置单节点 vSAN ESA 集群,需要用到 vCenter Server 镜像文件中的 vCSA_with_cluster_on_ESXi.json 配置文件,根据这个配置文件修改为实际环境中的信息,示例如下:

{
    "__version": "2.13.0",
    "__comments": "将 vCenter Server 虚拟机部署到 ESXi 主机上并配置单节点 vSAN ESA 集群",
    "new_vcsa": {
        "esxi": {
            "hostname": "esxi.mulab.local",
            "username": "root",
            "password": "vSphere8@password",
            "deployment_network": "VM Network",
            "VCSA_cluster": {
                "__comments": [
                    "Optional section. You must provide this option if you want to create the vSAN bootstrap cluster"
                ],
                "datacenter": "datacenter",
                "cluster": "vsan-esa",
                "compression_only": false,
                "deduplication_and_compression": false,
                "enable_vlcm": true,
                "enable_vsan_esa": true,
                "storage_pool": {
                    "single_tier": [
                        "0654f79de904b1744a000c29669162b236",
                        "063ec1878a6b90d657000c296a1c16f648"
                    ]
                },
                "vsan_hcl_database_path": "all.json"
            }
        },
        "appliance": {
            "__comments": [
                "vCenter Server 部署参数"
            ],
            "thin_disk_mode": true,
            "deployment_option": "small",
            "name": "vcenter"
        },
        "network": {
            "ip_family": "ipv4",
            "mode": "static",
            "ip": "192.168.32.50",
            "dns_servers": [
                "192.168.32.3"
            ],
            "prefix": "24",
            "gateway": "192.168.32.254",
            "system_name": "vcenter.mulab.local"
        },
        "os": {
            "password": "vSphere8@password",
            "ntp_servers": "192.168.32.3",
            "ssh_enable": true
        },
        "sso": {
            "password": "vSphere8@password",
            "domain_name": "vsphere.local"
        }
    },
    "ceip": {
        "description": {
            "__comments": [
                "++++VMware Customer Experience Improvement Program (CEIP)++++",
                "VMware's Customer Experience Improvement Program (CEIP) ",
                "provides VMware with information that enables VMware to ",
                "improve its products and services, to fix problems, ",
                "and to advise you on how best to deploy and use our ",
                "products. As part of CEIP, VMware collects technical ",
                "information about your organization's use of VMware ",
                "products and services on a regular basis in association ",
                "with your organization's VMware license key(s). This ",
                "information does not personally identify any individual. ",
                "",
                "Additional information regarding the data collected ",
                "through CEIP and the purposes for which it is used by ",
                "VMware is set forth in the Trust & Assurance Center at ",
                "http://www.vmware.com/trustvmware/ceip.html . If you ",
                "prefer not to participate in VMware's CEIP for this ",
                "product, you should disable CEIP by setting ",
                "'ceip_enabled': false. You may join or leave VMware's ",
                "CEIP for this product at any time. Please confirm your ",
                "acknowledgement by passing in the parameter ",
                "--acknowledge-ceip in the command line.",
                "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"
            ]
        },
        "settings": {
            "ceip_enabled": false
        }
    }
}

JSON 配置文件中,关于 ESXi 主机用于 vSAN 存储池的硬盘信息可以在下图所示的地方获取。注意,由于是部署 vSAN ESA 环境,需要
下载 vSAN HCL 文件
到本地,并将文件的路径填入到对应的 JSON 配置文件当中,当前部署环境将 vSAN HCL 文件放在了运行 CLI 命令的当前目录(C:\Users\JUNIOR_MU\)。

另存为 deploy-on-esxi-with-vsan-esa.json 配置文件到 files 文件夹,使用以下命令执行部署前预检查。

F:\vcsa-cli-installer\win32\vcsa-deploy.exe install .\files\deploy-on-esxi-with-vsan-esa.json --accept-eula --no-ssl-certificate-verification --precheck-only

如果 JSON 配置文件与部署环境检查成功,则会输出类似下面的内容。

[SUCCEEDED] Successfully executed Task 'Running Pre-check: vSAN Cluster Health
Checks.' in TaskFlow 'install' at 10:53:30
Running Pre-check: vSphere vLCM Cluster Enablement.
 [START] Start executing Task: Running Pre-check for vSphere vLCM Cluster
Enablement. at 10:53:30
 [SUCCEEDED] Successfully executed Task 'Running Pre-check: vSphere vLCM Cluster
Enablement.' in TaskFlow 'install' at 10:53:30
 [SUCCEEDED] Successfully executed Task 'PrecheckTask: Running prechecks.' in
TaskFlow 'deploy-on-esxi-with-vsan-esa' at 10:53:30
vcsa-deploy execution successfully completed, workflow log dir:
C:\Users\JUNIOR~1\AppData\Local\Temp\vcsaCliInstaller-2024-08-06-10-52-j1nef3kt\workflow_1722941536363
=================================== 10:53:31 ===================================
Result and Log File Information...
WorkFlow log directory:
C:\Users\JUNIOR~1\AppData\Local\Temp\vcsaCliInstaller-2024-08-06-10-52-j1nef3kt\workflow_1722941536363

如果 JSON 配置文件与部署环境检查确定没问题,使用以下命令执行正式部署。

F:\vcsa-cli-installer\win32\vcsa-deploy.exe install .\files\deploy-on-esxi-with-vsan-esa.json --accept-eula --no-ssl-certificate-verification

如果 vCenter Server 部署成功,则会输出类似下面的内容。

Successfully completed VCSA deployment.  VCSA Deployment Start Time:
2024-08-06T11:12:39.139Z VCSA Deployment End Time: 2024-08-06T11:31:05.857Z
 [SUCCEEDED] Successfully executed Task 'MonitorDeploymentTask: Monitoring
Deployment' in TaskFlow 'deploy-on-esxi-with-vsan-esa' at 11:31:19
Monitoring VCSA Deploy task completed
== [START] Start executing Task: Join active domain if necessary at 11:31:20 ==
Domain join task not applicable, skipping task
 [SUCCEEDED] Successfully executed Task 'Running deployment: Domain Join' in
TaskFlow 'deploy-on-esxi-with-vsan-esa' at 11:31:20
==== [START] Start executing Task: Creating the new datacenter. at 11:31:21 ====
The certificate of server 'vcenter.mulab.local' will not be verified because you
have provided either the '--no-ssl-certificate-verification' or
'--no-esx-ssl-verify' command parameter, which disables verification for all
certificates. Remove this parameter from the command line if you want server
certificates to be verified.
 [SUCCEEDED] Successfully executed Task 'Running Datacenter Creation: Creating
the new datacenter for vSphere vLCM managed cluster.' in TaskFlow
'deploy-on-esxi-with-vsan-esa' at 11:31:23
 [START] Start executing Task: Creating vSphere vLCM managed cluster. at
11:31:24
Enabling vLCM Cluster creation task on vcenter.mulab.local
Running ExecuteClusterHostSeedingWorkflow...
 [SUCCEEDED] Successfully executed Task 'Running vSphere vLCM: Creating vSphere
vLCM managed cluster.' in TaskFlow 'deploy-on-esxi-with-vsan-esa' at 11:35:44
 [START] Start executing Task: Provide the login information about new
appliance. at 11:35:45
    Appliance Name: vcenter
    System Name: vcenter.mulab.local
    System IP: 192.168.32.50
    Log in as: Administrator@vsphere.local
Your vCenter Server Appliance is deployed on a vSAN cluster. However this
one-host cluster must be further configured in order to provide the full
benefits of a vSAN cluster.
vSAN Configuration Instructions
1.       Add additional hosts to the vSAN cluster (a minimum of two).
2.       Complete the required configuration of the vSAN cluster.
3.       Apply the correct VM Storage Policy to the vCenter Server Appliance.
 [SUCCEEDED] Successfully executed Task 'ApplianceLoginSummaryTask: Provide
appliance login information.' in TaskFlow 'deploy-on-esxi-with-vsan-esa' at
11:35:45
=================================== 11:35:46 ===================================
Result and Log File Information...
WorkFlow log directory:
C:\Users\JUNIOR~1\AppData\Local\Temp\vcsaCliInstaller-2024-08-06-11-00-u9zayc8j\workflow_1722942009531

登录 vCenter Server,查看部署情况。

当前是单节点 vSAN 集群,需要增加 ESXi 主机以满足 vSAN 集群最少主机要求(3台)。

vSAN 存储容量以及当前默认存储策略配置。

vSAN ESA 集群 ESXi 主机磁盘配置。

vSAN ESA 集群 ESXi 主机网络配置,需要添加配置用于 vSAN 流量的 vmkernel 网卡。

配置好完整的 vSAN 集群后,请将 vCenter Server 的策略调整为 vSAN 存储策略。

引言

在 ClickHouse,我们认为可观测性仅仅是另一个实时分析问题。作为一款高性能的实时分析数据库,ClickHouse 被用于多种场景,包括
时间序列数据
的实时分析。其应用场景的多样性推动了大量
分析函数
的发展,这些函数有助于查询大多数数据类型。这些查询特性和高压缩率使得越来越多的用户开始利用 ClickHouse 来存储可观测性数据。这类数据通常以三种形式出现:logs(日志)、metrics(指标)和 traces(追踪)数据。在这篇博客中,作为可观测性系列的第二篇文章,我们将探讨如何在 ClickHouse 中收集、存储和查询 trace(追踪)数据。

本文主要关注使用
OpenTelemetry
收集追踪数据并将其存储到 ClickHouse 中。结合 Grafana 和最近在
ClickHouse 插件
方面的发展,
追踪
数据可以轻松地可视化,并且可以与
日志

指标
相结合,从而深入理解系统的行为和性能,在检测和诊断问题时提供帮助。

我们尽量确保所有示例都可以被复现。虽然本文着重于数据收集和可视化的基础知识,但我们还提供了一些关于模式优化的小贴士。为了示例的目的,我们从
官方 OpenTelemetry 示例项目
进行了分叉,增加了对 ClickHouse 的支持,并包含了一个开箱即用的 Grafana 仪表板来可视化追踪数据。

什么是 Traces(追踪数据)?

Telemetry(遥测)是指系统关于自身行为产生的数据。这类数据可以表现为日志、指标和追踪数据等形式。追踪记录了请求(由应用程序或终端用户发起)在微服务和 serverless 架构等多服务架构中的传播路径。一条追踪数据由多个跨度(span)组成,每个跨度代表一个工作单元或操作。跨度提供了操作详情,主要是执行所需的时间,此外还包括
其他元数据
和相关日志消息。这些跨度按树状结构组织,第一个跨度关联根节点,覆盖整个追踪从开始到结束的过程。在这个根节点及其后的每个跨度之下,捕获子操作。随着我们在树中导航,我们可以看到构成上层的子操作和步骤。这为我们提供了越来越多的上下文,了解原始请求执行的工作。这一过程如下图所示:

image

当追踪数据与指标和日志结合时,对于深入了解系统的运行情况以及检测和解决问题至关重要。

什么是 OpenTelemetry?

OpenTelemetry 项目是一个供应商中立的开源框架,它包含 SDK、API 和组件,允许摄入、转换并将可观测性数据发送到后端。更具体地说,它主要包括以下几个核心组件:

  • 一系列规格和约定
    ,规定了如何收集和存储指标、日志和追踪数据。这包括针对特定语言代理的建议,以及基于
    protobuf
    的 OpenTelemetry 行协议 (OTLP) 的完整规范。这通过提供客户端-服务器接口和消息格式的完整描述,使数据可以在服务之间传输。
  • 特定语言的库和 SDK
    ,用于仪器化、生成、收集和导出可观测性数据。这对于收集追踪数据尤其重要。
  • OTEL Collector
    ,采用 Golang 编写,提供了一个供应商无关的实现,用于接收、处理和导出可观测性数据。OTEL Collector 通过支持 Prometheus 和 OTLP 等多种输入格式,以及广泛的导出目标(包括 ClickHouse),提供了一个集中式的处理网关。

总之,OpenTelemetry 标准化了日志、指标和追踪数据的收集。重要的是,它不负责将这些数据存储在可观测性后端中——这就是 ClickHouse 发挥作用的地方!

为什么选择 ClickHouse?

Trace 数据通常表示为一张表,每个跨度为一行,因此可以被视为另一种实时分析问题。除了为此类数据类型提供高压缩比外,ClickHouse 还提供了一个丰富的 SQL 接口,附加有
分析函数
,使得查询追踪变得简单。与 Grafana 结合使用时,用户可以获得一种成本效益高的方式来存储和可视化追踪数据。尽管其他存储解决方案可能提供类似的压缩水平,但 ClickHouse 在低延迟查询方面独树一帜,它是全球最快的分析型数据库。实际上,这些特性使得 ClickHouse 成为了许多商业可观测性解决方案首选的后端,例如:
Signoz.io

Highlight.io

qryn

BetterStack
,或者像
Uber

Cloudflare

Gitlab
这样的自建大规模可观测性平台。

Instrumentation(仪器化)库

为最流行的编程语言提供了仪器化库。这些库既提供了自动仪器化代码的功能,利用应用程序/服务框架来捕获最常见的指标和追踪数据,也提供了手动仪器化技术。虽然自动仪器化通常已足够,后者允许用户针对其代码的特定部分进行仪器化,可能更详细地捕捉应用程序特定的指标和追踪信息。

为了本篇博客的目的,我们只关注追踪信息的捕获。
OpenTelemetry 示例应用由一个微服务架构组成
,其中包含许多依赖服务,每个服务使用不同的语言,为实施者提供参考。下面的简单示例展示了如何仪器化一个 Python Flask API 来收集追踪数据:

# These are the necessary import declarations
from opentelemetry import trace

from random import randint
from flask import Flask, request

# Acquire a tracer
tracer = trace.get_tracer(__name__)

app = Flask(__name__)

@app.route("/rolldice")
def roll_dice():
	return str(do_roll())

def do_roll():
	# This creates a new span that's the child of the current one
	with tracer.start_as_current_span("do_roll") as rollspan:
    	res = randint(1, 6)
    	rollspan.set_attribute("roll.value", res)
    	return res

对于每个库的详细指南超出了本篇博客的范围,我们鼓励用户
阅读与其使用的语言相关的文档

OTEL Collector

OTEL 收集器接收来自可观测性来源的数据,如来自仪器化库的追踪数据,处理这些数据,并将其导出到目标后端。OTEL 收集器还可以通过支持多种输入格式(如 Prometheus 和 OTLP)以及广泛的导出目标(包括 ClickHouse)提供一个集中式处理网关。

Collector(收集器)使用了管道的概念。这些管道可以是日志、指标或追踪类型,并且由 receiver(接收器)、processor(处理器)和 exporter(导出器)组成。

image

在这个架构中的
接收器
充当 OTEL 数据的输入。这可以通过拉取或推送模型实现。虽然这可以在多种协议上发生,但从仪器化库来的追踪数据将通过
OTLP
使用 gRPC 或 HTTP 推送。随后
处理器
运行在这些数据上,提供过滤、批处理和丰富功能。最后,导出器通过推送或拉取的方式将数据发送到后端目的地。在我们的案例中,我们将把数据推送到 ClickHouse。

需要注意的是,虽然收集器更常被用作 gateway(网关)/aggregator(聚合器),处理诸如批处理和重试等任务,但它也可以作为代理本身部署——这对于日志收集非常有用,正如我们在之前的帖子中所述。OTLP 代表了网关和代理实例之间通信的 OpenTelemetry 数据标准,可以通过 gRPC 或 HTTP 实现。对于追踪数据的收集目的,收集器仅仅作为网关部署,如下所示:

image

对于高负载环境,可以构建更高级的架构。我们推荐这个
优秀的视频讨论可能的选择

ClickHouse 支持

通过
社区贡献
,ClickHouse 在 OTEL 导出器中得到了支持,支持日志、追踪和指标。与 ClickHouse 的通信通过官方 Go 客户端以优化的原生格式和协议进行。在使用 OpenTelemetry 收集器之前,用户应考虑以下几点:

https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/exporter/clickhouseexporter

  • 代理使用的 ClickHouse 数据模型和模式是硬编码的。截至本文撰写时,没有能力更改所使用的类型或编解码器。通过在部署连接器之前创建表来缓解这个问题,从而强制执行您的 schema。
  • 导出器不是随核心 OTEL 发布一起分发的,而是作为
    contrib
    镜像的一个扩展。实际上这意味着在任何 HELM chart 中使用正确的 Docker 镜像。对于更轻量级的部署,用户可以
    构建自定义收集器镜像
    ,仅包含所需的组件。
  • 截至版本 0.74,如果未设置为
    default
    (如 demo 分支中所使用的),用户应在部署前预先在 ClickHouse 中创建数据库。
    CREATE DATABASE otel
    
  • 导出器处于 Alpha 阶段,用户应遵循
    OpenTelemetry 提供的建议

示例应用

image

OpenTelemetry 提供了一个演示应用
,给出了 OpenTelemetry 实施的实际示例。这是一个分布式微服务架构,支持一个销售望远镜的网上商店。这个电子商务用例有助于创建一系列简单易懂的服务机会,例如推荐、支付和货币转换。网店受到负载生成器的影响,导致每个已仪器化的服务生成日志、追踪和指标。除了为实践者提供一个学习如何在其首选语言中进行仪器化的现实示例外,此演示还允许供应商展示其 OpenTelemetry 与其可观测性后端的集成。本着这一精神,我们
分叉了这个应用
并做了必要的更改,以便将追踪数据存储在 ClickHouse 中。

image

请注意上述架构中使用的语言范围和处理诸如支付和推荐等操作的组件数量。建议用户
查看代码
以了解其首选语言的服务。由于存在作为网关的收集器,因此无需对任何仪器化代码进行修改。这种架构分离是 OpenTelemetry 的一个明显优势 —— 只需更改收集器中的目标导出器即可更改后端。

本地部署

演示使用每个服务的 Docker 容器。可以使用
docker compose

官方文档
中概述的步骤来部署演示,将 ClickHouse 分支替换为原始仓库。

git clone https://github.com/ClickHouse/opentelemetry-demo.git
cd opentelemetry-demo/
docker compose up --no-build

我们已经
修改了 docker-compose 文件
,以包含一个 ClickHouse 实例,在其中存储数据,并对其他服务可用,名称为
clickhouse

使用 Kubernetes 部署

可以轻松地使用
官方说明
在 Kubernetes 中部署演示。我们建议复制
values 文件
并修改
收集器配置
。一个示例 values 文件,将所有跨度发送到 ClickHouse Cloud 实例,可以在
这里
找到。可以下载并使用修改后的 helm 命令进行部署。

helm install -f values.yaml my-otel-demo open-telemetry/opentelemetry-demo

集成 ClickHouse

在此文中,我们专注于导出追踪数据。尽管日志和指标也可以存储在 ClickHouse 中,但我们为了简化起见使用默认配置。日志默认未启用,而指标则发送到 Prometheus。

要将追踪数据发送到 ClickHouse,必须通过文件
otel-config-extras.yaml
添加自定义 OTEL 收集器配置。这将与
主配置
合并,并覆盖任何现有声明。附加配置如下所示:

exporters:
 clickhouse:
   endpoint: tcp://clickhouse:9000?dial_timeout=10s&compress=lz4
   database: default
   ttl_days: 3
   traces_table_name: otel_traces
   timeout: 5s
   retry_on_failure:
     enabled: true
     initial_interval: 5s
     max_interval: 30s
     max_elapsed_time: 300s

processors:
 batch:
   timeout: 5s
   send_batch_size: 100000

service:
 pipelines:
   traces:
     receivers: [otlp]
     processors: [spanmetrics, batch]
     exporters: [logging, clickhouse]

主要更改在于配置 ClickHouse 作为导出器。这里有几个关键设置:

  • endpoint
    设置指定了 ClickHouse 主机和端口。注意通信通过 TCP(端口 9000)进行。对于安全连接,应使用 9440 端口,并带有
    secure=true
    参数,例如
    'clickhouse://<username>:<password>@<host>:9440?secure=true'
    。或者,使用
    dsn
    参数。注意我们连接到主机
    clickhouse
    。这是添加到本地部署 Docker 映像中的 ClickHouse 容器。可以自由修改此路径,例如指向 ClickHouse Cloud 集群。
  • ttl_days
    — 这通过 TTL 功能控制 ClickHouse 中的数据保留。参见“Schema”部分。

我们的追踪管道利用 OTLP 接收器从仪器化库接收追踪数据。然后,此管道将这些数据传递给两个处理器:

Schema(模式)

一旦部署完成,我们可以通过对表
otel_traces
执行简单的 SELECT 来确认追踪数据正被发送到 ClickHouse。这代表了所有跨度发送的主要数据。请注意,我们使用
clickhouse-client
访问容器(它应该在主机上的默认 9000 端口上暴露)。

SELECT *
FROM otel_traces
LIMIT 1
FORMAT Vertical

Row 1:
──────
Timestamp:      	2023-03-20 18:04:35.081853291
TraceId:        	06cabdd45e7c3c0172a8f8540e462045
SpanId:         	b65ebde75f6ae56f
ParentSpanId:   	20cc5cb86c7d4485
TraceState:
SpanName:       	oteldemo.AdService/GetAds
SpanKind:       	SPAN_KIND_SERVER
ServiceName:    	adservice
ResourceAttributes: {'telemetry.auto.version':'1.23.0','os.description':'Linux 5.10.104-linuxkit','process.runtime.description':'Eclipse Adoptium OpenJDK 64-Bit Server VM 17.0.6+10','service.name':'adservice','service.namespace':'opentelemetry-demo','telemetry.sdk.version':'1.23.1','process.runtime.version':'17.0.6+10','telemetry.sdk.name':'opentelemetry','host.arch':'aarch64','host.name':'c97f4b793890','process.executable.path':'/opt/java/openjdk/bin/java','process.pid':'1','process.runtime.name':'OpenJDK Runtime Environment','container.id':'c97f4b7938901101550efbda3c250414cee6ba9bfb4769dc7fe156cb2311735e','os.type':'linux','process.command_line':'/opt/java/openjdk/bin/java -javaagent:/usr/src/app/opentelemetry-javaagent.jar','telemetry.sdk.language':'java'}
SpanAttributes: 	{'thread.name':'grpc-default-executor-1','app.ads.contextKeys':'[]','net.host.name':'adservice','app.ads.ad_request_type':'NOT_TARGETED','rpc.method':'GetAds','net.host.port':'9555','net.sock.peer.port':'37796','rpc.service':'oteldemo.AdService','net.transport':'ip_tcp','app.ads.contextKeys.count':'0','app.ads.count':'2','app.ads.ad_response_type':'RANDOM','net.sock.peer.addr':'172.20.0.23','rpc.system':'grpc','rpc.grpc.status_code':'0','thread.id':'23'}
Duration:       	218767042
StatusCode:     	STATUS_CODE_UNSET
StatusMessage:
Events.Timestamp:   ['2023-03-20 18:04:35.145394083','2023-03-20 18:04:35.300551833']
Events.Name:    	['message','message']
Events.Attributes:  [{'message.id':'1','message.type':'RECEIVED'},{'message.id':'2','message.type':'SENT'}]
Links.TraceId:  	[]
Links.SpanId:   	[]
Links.TraceState:   []
Links.Attributes:   []

每一行代表一个跨度,其中一些也是根跨度。有一些关键字段,基本理解这些字段将使我们能够构建有用的查询。有关追踪元数据的完整描述可在此处找到
这里

https://opentelemetry.io/docs/concepts/signals/traces/

  • TraceId
    - Trace Id 代表 Span 所属的追踪。
  • SpanId
    - Span 的唯一 ID。
  • ParentSpanId
    - Span 的父 Span 的 Span ID。这使得可以构建追踪调用历史。对于根跨度,这将是空的。
  • SpanName
    - 操作的名称。
  • SpanKind
    - 创建 Span 时,它的种类要么是客户端、服务器、内部、生产者或消费者。这个种类向追踪后端暗示了如何组装追踪。它实际上描述了 Span 与其子项和父项之间的关系。
  • ServiceName
    - Span 起源的服务名称,例如 Adservice。
  • ResourceAttributes
    - 包含元数据的键值对,您可以使用这些元数据来注解 Span,以携带有关正在跟踪的操作的信息。例如,这可能包含 Kubernetes 信息,如 Pod 名称或有关主机的值。请注意,我们的模式强制要求键和值都是字符串类型,并且使用 Map 类型。
  • SpanAttributes
    - 额外的 Span 层级属性,例如
    thread.id
  • Duration
    - Span 的持续时间,以纳秒为单位。
  • StatusCode
    - 可能是 UNSET、OK 或 ERROR。当应用程序代码中出现已知错误(如异常)时,会设置后者。
  • Events*
    - 虽然可能不适合用于仪表板概览,但这些可能会引起应用程序开发者的兴趣。这可以视为 Span 上的一个结构化注解,通常用于标记 Span 持续时间内有意义的单一时刻,例如页面变为交互式时。
    Events.Timestamp

    Events.Name

    Events.Attributes
    可用于重建整个事件 — 请注意,这依赖于数组位置。
  • Links*
    - 这些表示与其他 Span 的因果关系。例如,这些可能是由于特定操作而执行的异步操作。由于请求操作而排队的处理任务可能是一个合适的 Span 链接。这里,开发人员可能会将第一个追踪的最后一个 Span 与第二个追踪的第一个 Span 链接起来,以因果关联它们。在 ClickHouse 模式中,我们再次依赖于数组类型,并通过关联列
    Links.TraceId

    Links.SpanId

    Links.Attributes
    的位置来实现这一点。

请注意,收集器对模式有其偏好,包括强制实施特定的编解码器。虽然这些对于一般情况来说是合理的选择,但它们阻止了用户通过收集器配置调整配置以满足自身需求。希望修改编解码器或 ORDER BY 键的用户(例如,
适应特定用户的访问模式
),应在提前预创建表。

CREATE TABLE otel_traces
(
	`Timestamp` DateTime64(9) CODEC(Delta(8), ZSTD(1)),
	`TraceId` String CODEC(ZSTD(1)),
	`SpanId` String CODEC(ZSTD(1)),
	`ParentSpanId` String CODEC(ZSTD(1)),
	`TraceState` String CODEC(ZSTD(1)),
	`SpanName` LowCardinality(String) CODEC(ZSTD(1)),
	`SpanKind` LowCardinality(String) CODEC(ZSTD(1)),
	`ServiceName` LowCardinality(String) CODEC(ZSTD(1)),
	`ResourceAttributes` Map(LowCardinality(String), String) CODEC(ZSTD(1)),
	`SpanAttributes` Map(LowCardinality(String), String) CODEC(ZSTD(1)),
	`Duration` Int64 CODEC(ZSTD(1)),
	`StatusCode` LowCardinality(String) CODEC(ZSTD(1)),
	`StatusMessage` String CODEC(ZSTD(1)),
	`Events.Timestamp` Array(DateTime64(9)) CODEC(ZSTD(1)),
	`Events.Name` Array(LowCardinality(String)) CODEC(ZSTD(1)),
	`Events.Attributes` Array(Map(LowCardinality(String), String)) CODEC(ZSTD(1)),
	`Links.TraceId` Array(String) CODEC(ZSTD(1)),
	`Links.SpanId` Array(String) CODEC(ZSTD(1)),
	`Links.TraceState` Array(String) CODEC(ZSTD(1)),
	`Links.Attributes` Array(Map(LowCardinality(String), String)) CODEC(ZSTD(1)),
	INDEX idx_trace_id TraceId TYPE bloom_filter(0.001) GRANULARITY 1,
	INDEX idx_res_attr_key mapKeys(ResourceAttributes) TYPE bloom_filter(0.01) GRANULARITY 1,
	INDEX idx_res_attr_value mapValues(ResourceAttributes) TYPE bloom_filter(0.01) GRANULARITY 1,
	INDEX idx_span_attr_key mapKeys(SpanAttributes) TYPE bloom_filter(0.01) GRANULARITY 1,
	INDEX idx_span_attr_value mapValues(SpanAttributes) TYPE bloom_filter(0.01) GRANULARITY 1,
	INDEX idx_duration Duration TYPE minmax GRANULARITY 1
)
ENGINE = MergeTree
PARTITION BY toDate(Timestamp)
ORDER BY (ServiceName, SpanName, toUnixTimestamp(Timestamp), TraceId)
TTL toDateTime(Timestamp) + toIntervalDay(3)
SETTINGS index_granularity = 8192, ttl_only_drop_parts = 1

除了 TTL(见下文)之外,关于此模式还有一些重要的观察结果:

  • ORDER BY
    - 我们 schema 中的
    ORDER BY
    子句决定了数据如何排序并存储在磁盘上。这也会影响
    稀疏索引的构建
    ,并且最重要的是直接影响我们的压缩级别和查询性能。当前的子句
    (ServiceName, SpanName, toUnixTimestamp(Timestamp), TraceId)
    按照从右到左的顺序对数据进行排序,并针对首先按 ServiceName 过滤的查询进行了优化。对后续排序的列的过滤限制将变得越来越无效。如果您的访问模式因诊断工作流程的不同而不同,您可能需要修改此顺序和使用的列。这样做时,请考虑最佳实践以确保
    键得到最优利用


  • PARTITION BY
    - 此子句会导致数据在磁盘上物理分离。虽然这对于高效删除数据很有用(见下文的 TTL),但它可能会
    正面和负面影响查询性能
    。基于分区表达式
    toDate(Timestamp)
    ,它按天创建分区,针对最近数据(例如最后 24 小时)的查询将会受益。对于跨越多个分区/天数的查询(只有在您扩展保留期限超过默认的 3 天时才可能发生),相反可能会受到负面影响。如果您将数据保留期限扩展到几个月或几年,或者您的访问模式需要针对更宽的时间范围,则应考虑使用不同的表达式,例如按周分区,如果您的 TTL 为一年的话。


  • Map
    - 在上述 schema 中,Map 类型广泛用于属性。这是因为键在这里是动态的且与应用程序相关。Map 类型的灵活性是有用的,但也存在一定的代价。访问 Map 的键需要读取并加载整个列。因此,访问 Map 的键的成本比将键作为根级别的显式列更高——尤其是如果 Map 很大且包含许多键的情况下。性能差异取决于 Map 的大小,但可能会相当显著。为了应对这一问题,用户应该
    将经常查询的 Map 键/值对物化
    到根级别的列中。这些
    物化列
    将在插入时从相应的 Map 值填充,并可用于快速访问。下面是一个示例,我们将 Map 列
    ResourceAttributes
    中的键
    host.name
    物化到根级别的列
    Host
    中:

CREATE TABLE otel_traces
  (
      `Timestamp` DateTime64(9) CODEC(Delta(8), ZSTD(1)),
      `HostName` String MATERIALIZED ResourceAttributes['host.name'],
      `ResourceAttributes` Map(LowCardinality(String), String) CODEC(ZSTD(1)),
       ....
  )
  ENGINE = MergeTree
  PARTITION BY toDate(Timestamp)
  ORDER BY (ServiceName, SpanName, toUnixTimestamp(Timestamp), TraceId)
  TTL toDateTime(Timestamp) + toIntervalDay(3)

另外,这可以在数据插入后,一旦识别出您的访问模式时再进行:

ALTER TABLE otel_traces ADD COLUMN HostName String MATERIALIZED ResourceAttributes['host.name'];

ALTER TABLE otel_traces MATERIALIZE COLUMN HostName;

此过程需要使用变异(mutation),这可能会
I/O 密集且应谨慎安排
。Map 类型还要求值必须是相同类型,在本例中为字符串类型。这种类型信息的丢失可能会在查询时需要进行类型转换。此外,用户应该了解访问 Map 键所需的语法 - 见“查询追踪”部分。

TTL

通过收集器参数
ttl_days
,用户能够通过 ClickHouse 的 TTL 功能控制数据的过期时间。这个值反映在表达式
TTL toDateTime(Timestamp) + toIntervalDay(3)
中,默认值为 3。超过这个时间的数据将通过一个异步后台进程被删除。有关 TTL 的更多详细信息,请参阅
这里

上述模式使用了
PARTITION BY
来辅助 TTL 功能。具体来说,当结合参数
ttl_only_drop_parts=1
使用时,这允许一天的数据被有效地删除。如上所述,这可能会
正面和负面影响查询

Trace Id Materialized View(物化视图)

除了主表之外,ClickHouse 导出器还会创建一个
materialized view
。Materialized view 是一种特殊的触发器,它会在数据插入到目标表时存储 SELECT 查询的结果。这个目标表可以汇总数据(使用聚合操作),并以针对特定查询优化的格式存储。在导出器的情况下,会创建以下视图:

CREATE MATERIALIZED VIEW otel_traces_trace_id_ts_mv TO otel_traces_trace_id_ts
(
	`TraceId` String,
	`Start` DateTime64(9),
	`End` DateTime64(9)
) AS
SELECT
	TraceId,
	min(Timestamp) AS Start,
	max(Timestamp) AS End
FROM otel_traces
WHERE TraceId != ''
GROUP BY TraceId

这个具体的 materialized view 执行
GROUP BY TraceId
并识别每个 ID 的最大和最小时间戳。这在插入到表
otel_traces
的每一组数据(可能是数百万行)时执行。汇总后的数据随后被插入到目标表
otel_traces_trace_id_ts
中。下面展示了一些来自该表的行及其模式:

SELECT *
FROM otel_traces_trace_id_ts
LIMIT 5

┌─TraceId──────────────────────────┬─────────────────────────Start─┬───────────────────────────End─┐
│ 000040cf204ee714c38565dd057f4d97 │ 2023-03-20 18:39:44.064898664 │ 2023-03-20 18:39:44.066019830 │
│ 00009bdf67123e6d50877205680f14bf │ 2023-03-21 07:56:30.185195776 │ 2023-03-21 07:56:30.503208045 │
│ 0000c8e1e9f5f910c02a9a98aded04bd │ 2023-03-20 18:31:35.967373056 │ 2023-03-20 18:31:35.968602368 │
│ 0000c8e1e9f5f910c02a9a98aded04bd │ 2023-03-20 18:31:36.032750972 │ 2023-03-20 18:31:36.032750972 │
│ 0000dc7a6d15c638355b33b3c6a8aaa2 │ 2023-03-21 00:31:37.075681536 │ 2023-03-21 00:31:37.247680719 │
└──────────────────────────────────┴───────────────────────────────┴───────────────────────────────┘

5 rows in set. Elapsed: 0.009 sec.


CREATE TABLE otel_traces_trace_id_ts
(
	`TraceId` String CODEC(ZSTD(1)),
	`Start` DateTime64(9) CODEC(Delta(8), ZSTD(1)),
	`End` DateTime64(9) CODEC(Delta(8), ZSTD(1)),
	INDEX idx_trace_id TraceId TYPE bloom_filter(0.01) GRANULARITY 1
)
ENGINE = MergeTree
ORDER BY (TraceId, toUnixTimestamp(Start))
TTL toDateTime(Start) + toIntervalDay(3)

如所示,目标表
otel_traces_trace_id_ts
使用
(TraceId, toUnixTimestamp(Start))
作为其
ORDER BY
键。这样就可以让用户快速确定特定追踪的时间范围。

我们在下面的 "查询追踪" 部分探讨了这个 materialized view 的价值,但我们发现它在加速执行 TraceId 查找的更广泛的查询方面的作用有限。然而,它确实提供了一个很好的入门示例供用户借鉴。

用户可能希望扩展或修改这个 materialized view。例如,可以在 materialized view 的聚合和目标表中添加一个
ServiceName
数组,以便快速识别追踪的服务。可以通过在部署收集器之前预创建表和 materialized view 来实现这一点,或者
在创建后修改视图和表
。用户还可以向主表附加新的 materialized view 以满足其他访问模式需求。请参阅
我们最近的博客
获取更多详细信息。

最后,上述功能也可以通过
projections
实现。尽管它们没有提供所有 Materialized view 的功能,但它们直接包含在表定义中。与 Materialized Views 不同,projections 会被原子性地更新,并与主表保持一致,ClickHouse 会在查询时自动选择最优版本。

查询追踪数据

导出器文档
提供了一些优秀的入门查询示例。需要更多灵感的用户可以参考我们在下面展示的仪表板中的
这些查询
。在查询追踪数据时有几个重要的概念:

https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/exporter/clickhouseexporter/README.md#traces
https://gist.github.com/gingerwizard/dbc250063933d76462faf117b4d56b9a

  • 在主
    otel_traces
    表上进行 TraceId 查找可能会比较耗时,尽管有布隆过滤器的存在。如果你需要深入查看某个特定的追踪记录,可以利用
    otel_traces_trace_id_ts
    表来确定追踪记录的时间范围 —— 如上所述。这个时间范围随后可以作为附加过滤条件应用于
    otel_traces
    表,该表包含了时间戳作为 ORDER BY 键的一部分。如果在查询中应用服务名称作为过滤条件,则查询可以进一步优化(尽管这会限制到特定服务的跨度)。考虑以下两种查询变体及其各自的时间,这两种查询都会返回与追踪相关的跨度。

只使用
otel_traces
表:

SELECT
        Timestamp,
    TraceId,
    SpanId,
    SpanName
FROM otel_traces
WHERE TraceId = '0f8a2c02d77d65da6b2c4d676985b3ab'
ORDER BY Timestamp ASC

50 rows in set. Elapsed: 0.197 sec. Processed 298.77 thousand rows, 17.39 MB (1.51 million rows/s., 88.06 MB/s.)

利用我们的
otel_traces_trace_id_ts
表,并使用得到的时间来作为过滤条件:

WITH '0f8a2c02d77d65da6b2c4d676985b3ab' AS trace_id,
     (
        SELECT min(Start)
        FROM otel_traces_trace_id_ts
  	   WHERE TraceId = trace_id
     ) AS start,
     (
  	   SELECT max(End) + 1
  	   FROM otel_traces_trace_id_ts
  	   WHERE TraceId = trace_id
     ) AS end
 SELECT  Timestamp,
     TraceId,
     SpanId,
     SpanName
 FROM otel_traces
 WHERE (TraceId = trace_id) AND (Timestamp >= start) AND (Timestamp <= end)
 ORDER BY Timestamp ASC

 50 rows in set. Elapsed: 0.110 sec. Processed 225.05 thousand rows, 12.78 MB (2.05 million rows/s., 116.52 MB/s.)
SELECT
    toStartOfHour(Timestamp) AS hour,
    count(*),
    lang,
    avg(Duration) AS avg,
    quantile(0.9)(Duration) AS p90,
    quantile(0.95)(Duration) AS p95,
    quantile(0.99)(Duration) AS p99
FROM otel_traces
WHERE (ResourceAttributes['host.name']) = 'bcea43b12a77'
GROUP BY
    hour,
    ResourceAttributes['telemetry.sdk.language'] AS lang
ORDER BY hour ASC

确定可用于查询的 Map 键可能很具挑战性 —— 特别是如果应用程序开发人员添加了自定义元数据。使用一个
聚合组合函数
,下面的查询可以识别
ResourceAttributes
列中的键。根据需要调整以适应其他列,例如
SpanAttributes

SELECT groupArrayDistinctArray(mapKeys(ResourceAttributes)) AS `Resource Keys`
FROM otel_traces
FORMAT Vertical

Row 1:
──────
Resource Keys:    ['telemetry.sdk.name','telemetry.sdk.language','container.id','os.name','os.description','process.pid','process.executable.name','service.namespace','telemetry.auto.version','os.type','process.runtime.description','process.executable.path','host.arch','process.runtime.version','process.runtime.name','process.command_args','process.owner','host.name','service.instance.id','telemetry.sdk.version','process.command_line','service.name','process.command','os.version']

1 row in set. Elapsed: 0.330 sec. Processed 1.52 million rows, 459.89 MB (4.59 million rows/s., 1.39 GB/s.)

使用 Grafana 进行可视化与诊断

我们推荐使用
Grafana
结合
官方 ClickHouse 插件
来可视化和分析追踪数据。之前的
文章

视频教程
已深入解析了此插件的使用方法。近期,我们对该插件进行了升级,新增了利用
追踪面板
展示追踪数据的功能,这不仅作为一种可视化方式,还能够融入到 Grafana 的
Explore
界面中。追踪面板对列有
严格的命名和类型要求
,遗憾的是,这些要求当前与 OTEL 规范不完全一致。以下查询能产生适合在追踪可视化中展示的结果:

WITH
	'ec4cff3e68be6b24f35b4eef7e1659cb' AS trace_id,
	(
    	SELECT min(Start)
    	FROM otel_traces_trace_id_ts
    	WHERE TraceId = trace_id
	) AS start,
	(
    	SELECT max(End) + 1
    	FROM otel_traces_trace_id_ts
    	WHERE TraceId = trace_id
	) AS end
SELECT
	TraceId AS traceID,
	SpanId AS spanID,
	SpanName AS operationName,
	ParentSpanId AS parentSpanID,
	ServiceName AS serviceName,
	Duration / 1000000 AS duration,
	Timestamp AS startTime,
	arrayMap(key -> map('key', key, 'value', SpanAttributes[key]), mapKeys(SpanAttributes)) AS tags,
	arrayMap(key -> map('key', key, 'value', ResourceAttributes[key]), mapKeys(ResourceAttributes)) AS serviceTags
FROM otel_traces
WHERE (TraceId = trace_id) AND (Timestamp >= start) AND (Timestamp <= end)
ORDER BY startTime ASC

image

通过 Grafana 中的
变量

数据链接
功能,用户可以创建复杂的交互工作流,实现图表的动态过滤。下面的仪表板包含了多种可视化元素:

https://grafana.com/docs/grafana/latest/dashboards/variables/
https://grafana.com/docs/grafana/latest/panels-visualizations/configure-data-links/

  • 以堆叠柱状图展示服务请求量概览
  • 多线图展示各服务的 99% 延迟
  • 条形图展示每个服务的错误率
  • 按 traceId 聚合的追踪列表——服务在此作为跨度链中的首项
  • 当筛选到特定追踪时填充的追踪面板

此仪表板为我们提供了关于错误和性能的基本诊断能力。OpenTelemetry demo 中包含了一些
现有场景
,用户可以在其中启用特定服务问题。其中一个场景涉及
推荐服务的内存泄漏
。虽然没有指标数据无法完成整个问题解决流程,但我们仍能识别出有问题的追踪记录。示例如下:

image

此仪表板现已随插件一起
打包
提供,并且可通过
示例 demo
获取。

使用 Parameterized Views(参数化视图)

上述查询可能相当复杂。例如,我们不得不使用
arrayMap
函数来确保属性结构正确。我们可以将此工作推迟到查询时的
物化或默认列
处理,从而简化查询。然而,这仍然需要编写较多的SQL代码。在Explore视图中可视化追踪时,这一点尤其繁琐。

为了简化查询语法,ClickHouse 提供了参数化视图功能。参数化视图类似于普通视图,但可以在创建时带有不立即解析的参数。这些视图可以通过表函数来使用,表函数将视图名称作为函数名,参数值作为其参数。这能极大地减少 Grafana 用户所需的 ClickHouse 查询语法。下面创建了一个接受 traceId 作为参数并返回追踪面板所需结果的视图。尽管参数化视图中最近
新增了对 CTE 的支持
,但下面示例中我们还是使用了较早的简单查询:

CREATE VIEW trace_view AS
SELECT
	TraceId AS traceID,
	SpanId AS spanID,
	SpanName AS operationName,
	ParentSpanId AS parentSpanID,
	ServiceName AS serviceName,
	Duration / 1000000 AS duration,
	Timestamp AS startTime,
	arrayMap(key -> map('key', key, 'value', SpanAttributes[key]), mapKeys(SpanAttributes)) AS tags,
	arrayMap(key -> map('key', key, 'value', ResourceAttributes[key]), mapKeys(ResourceAttributes)) AS serviceTags
FROM otel_traces
WHERE TraceId = {trace_id:String}

运行此视图时,我们只需传入一个
traceId
,例如,

SELECT *
FROM trace_view(trace_id = '1f12a198ac3dd502d5201ccccad52967')

这样可以显著降低查询追踪记录的复杂度。接下来,我们使用
Grafana 的 Explore 视图
来查询特定追踪。请注意,需将
Format
值设为
Trace
才能呈现追踪图:

image

参数化视图最适合于那些用户执行常规任务,需要即席分析的常见工作负载场景,比如检查特定追踪记录。

压缩

ClickHouse 用于存储追踪数据的一个优势在于其高压缩比。使用下面的查询,我们可以看到对于这个演示生成的追踪数据,我们实现了大约 9 到 10 倍的压缩率。这个数据集是在使用提供的
负载生成器服务
对演示进行压力测试时生成的,测试条件为 2000 个虚拟用户持续 24 小时。我们已经将这个数据集公开供大众使用。可以按照
这里的步骤
将数据插入到 ClickHouse 中。为了托管这些数据,我们建议使用
ClickHouse Cloud 的开发服务
(16GB 内存,两核),这对于这种规模的数据集来说已经足够。

SELECT
	formatReadableSize(sum(data_compressed_bytes)) AS compressed_size,
	formatReadableSize(sum(data_uncompressed_bytes)) AS uncompressed_size,
	round(sum(data_uncompressed_bytes) / sum(data_compressed_bytes), 2) AS ratio
FROM system.columns
WHERE table = 'otel_traces'
ORDER BY sum(data_compressed_bytes) DESC

┌─compressed_size─┬─uncompressed_size─┬─ratio─┐
│ 13.68 GiB   	  │ 132.98 GiB    	  │  9.72 │
└─────────────────┴───────────────────┴───────┘

1 row in set. Elapsed: 0.003 sec.

SELECT
	name,
	formatReadableSize(sum(data_compressed_bytes)) AS compressed_size,
	formatReadableSize(sum(data_uncompressed_bytes)) AS uncompressed_size,
	round(sum(data_uncompressed_bytes) / sum(data_compressed_bytes), 2) AS ratio
FROM system.columns
WHERE table = 'otel_traces'
GROUP BY name
ORDER BY sum(data_compressed_bytes) DESC

┌─name───────────────┬─compressed_size─┬─uncompressed_size─┬───ratio─┐
│ ResourceAttributes │ 2.97 GiB    	   │ 78.49 GiB     	   │   26.43 │
│ TraceId        	 │ 2.75 GiB    	   │ 6.31 GiB      	   │	2.29 │
│ SpanAttributes 	 │ 1.99 GiB    	   │ 22.90 GiB     	   │   11.52 │
│ SpanId         	 │ 1.68 GiB    	   │ 3.25 GiB      	   │	1.94 │
│ ParentSpanId   	 │ 1.35 GiB    	   │ 2.74 GiB      	   │	2.02 │
│ Events.Timestamp   │ 1.02 GiB        │ 3.47 GiB      	   │ 	3.4  │
│ Timestamp      	 │ 955.77 MiB  	   │ 1.53 GiB      	   │	1.64 │
│ Duration       	 │ 619.43 MiB  	   │ 1.53 GiB      	   │	2.53 │
│ Events.Attributes  │ 301.09 MiB      │ 5.12 GiB      	   │   17.42 │
│ Links.TraceId  	 │ 36.52 MiB       │ 1.60 GiB      	   │   44.76 │
│ Events.Name    	 │ 22.92 MiB       │ 248.91 MiB    	   │   10.86 │
│ Links.SpanId   	 │ 17.77 MiB       │ 34.49 MiB     	   │	1.94 │
│ HostName       	 │ 8.32 MiB        │ 4.56 GiB      	   │  561.18 │
│ StatusCode     	 │ 1.11 MiB    	   │ 196.80 MiB        │  177.18 │
│ StatusMessage  	 │ 1.09 MiB    	   │ 219.08 MiB    	   │  201.86 │
│ SpanName       	 │ 538.55 KiB  	   │ 196.82 MiB    	   │  374.23 │
│ SpanKind       	 │ 529.98 KiB  	   │ 196.80 MiB    	   │  380.25 │
│ ServiceName    	 │ 529.09 KiB  	   │ 196.81 MiB    	   │   380.9 │
│ TraceState     	 │ 138.05 KiB  	   │ 195.93 MiB    	   │ 1453.35 │
│ Links.Attributes   │ 11.10 KiB   	   │ 16.23 MiB     	   │ 1496.99 │
│ Links.TraceState   │ 1.71 KiB    	   │ 2.03 MiB          │ 1218.44 │
└────────────────────┴─────────────────┴───────────────────┴─────────┘

20 rows in set. Elapsed: 0.003 sec.

我们将在本系列的后续博客中探讨如何优化模式以及如何进一步提高压缩率。

后续工作

当前的 ClickHouse 导出器处于 Alpha 阶段。这一状态反映了它发布的相对较新以及成熟度。虽然我们计划在这个导出器上投入资源,但我们已经识别出一些挑战和可能的改进方向:

  • Schema(模式)
    - 当前 schema 包含了一些可能过早的优化措施。例如,使用布隆过滤器可能对于某些工作负载来说并不必要。如下面所示,这些过滤器会占用一定的空间(大约占总数据大小的 1%)。
SELECT
  formatReadableSize(sum(secondary_indices_compressed_bytes)) AS compressed_size,
  formatReadableSize(sum(secondary_indices_uncompressed_bytes)) AS uncompressed_size
 FROM system.parts
 WHERE (table = 'otel_traces') AND active
 
 ┌─compressed_size─┬─uncompressed_size─┐
 │ 425.54 MiB      │ 1.36 GiB          │
 └─────────────────┴───────────────────┘

我们发现
TraceId
列上的过滤器非常有效,值得加入到
schema
中。这似乎抵消了大部分物化视图带来的好处,后者增加了额外的查询和维护复杂性,其价值令人怀疑。不过,它提供了一个很好的例子,说明了物化视图如何可以用来加速查询。我们没有足够的证据证明其他布隆过滤器的价值,因此我们建议用户自行尝试。

  • High memory(高内存消耗)
    - 我们发现 OTEL 收集器非常耗内存。在早期配置中,我们使用
    batch processor
    在 5 秒后或者当批处理达到 100,000 行时向 ClickHouse 发送数据。虽然这优化了 ClickHouse 的插入操作并且遵循了
    最佳实践
    ,但在高负载下可能会非常耗内存 —— 特别是在
    收集日志
    时。这个问题可以通过减少刷新时间和/或批处理大小来缓解。需要注意的是,这样做需要进行调优,否则可能导致
    ClickHouse 中积累过多的分区
    。另外,用户也可以选择使用
    异步插入
    来发送数据。这会减少批处理大小,但在导出器中仅支持通过 HTTP 协议。要激活此功能,请在导出器配置中使用
    connection_params
    ,例如,
connection_params:
 	async_insert: 1
 	wait_for_async_insert: 0

需要注意的是,这种方式对于 ClickHouse 的插入操作来说效率较低。

结论

这篇博客展示了如何使用 OpenTelemetry 轻松地收集和存储追踪数据到 ClickHouse。我们已经为 ClickHouse 分叉了 OpenTelemetry 的 demo 项目,探讨了使用 Grafana 查询和可视化技术的方法,并指出了减少查询复杂性和未来项目工作的几个方向。对于更进一步的阅读,我们鼓励用户探索本文之外的主题,例如如何大规模部署 OTEL 收集器、如何处理背压以及如何保证交付。我们将在后续的博文中探讨这些主题,并在我们的 ClickHouse 实例中添加指标数据,然后再探讨如何优化 Schema 以及如何管理数据生命周期。

更多