ElasticSearch中熔断器

Elasticsearch Service 提供了多种官方的熔断器(circuit breaker),用于防止内存使用过高导致 ES 集群因为 OutOfMemoryError 而出现问题。Elasticsearch 设置有各种类型的子熔断器,负责特定请求处理的内存限制。此外,还有一个父熔断器,用于限制所有子熔断器上使用的内存总量。 Circuit breaker settings 断路器设置 Elasticsearch contains multiple circuit breakers used to prevent operations from causing an OutOfMemoryError. Each breaker specifies a limit for how much memory it can use. Additionally, there is a parent-level breaker that specifies the total amount of memory that can be used across all breakers. Elasticsearch 包含多个断路器,用于防止操作导致 OutOfMemoryError。每个断路器都指定了其可以使用的内存量的限制。此外,还有一个父级断路器,用于指定可在所有断路器中使用的内存总量。 Except where noted otherwise, these settings can be dynamically updated on a live cluster with the cluster-update-settings API. 除非另有说明,否则可以使用 cluster-update-settings API 在实时集群上动态更新这些设置。 For information about circuit breaker errors, see Circuit breaker errors. 有关断路器错误的信息,请参阅断路器错误。 下面提到的静态和动态的意思是,这个配置参数的属性。 动态设置(Dynamic Settings): 可以使用集群更新设置 API 在运行中的集群上进行配置和更新。 在未启动或已关闭的节点上,也可以通过 elasticsearch.yml 文件进行本地配置。 静态设置(Static Settings): 只能在未启动或已关闭的节点上通过 elasticsearch.yml 文件进行配置。 必须在集群中的每个相关节点上进行设置。 主要用于配置静态的集群设置和节点设置。 静态设置的配置只在节点启动时生效,不会受到集群运行时的影响。 Parent circuit breaker 父断路器 The parent-level breaker can be configured with the following settings: 可以使用以下设置配置父级断路器: indices.breaker.total.use_real_memory (Static) Determines whether the parent breaker should take real memory usage into account (true) or only consider the amount that is reserved by child circuit breakers (false). Defaults to true. (静态)确定父断路器是应考虑实际内存使用情况(true)还是仅考虑子断路器保留的内存量(false)。默认值为 true。 indices.breaker.total.limit (Dynamic) Starting limit for overall parent breaker. Defaults to 70% of JVM heap if indices.breaker.total.use_real_memory is false. If indices.breaker.total.use_real_memory is true, defaults to 95% of the JVM heap. (动态)整体父断路器的起始限制。如果 indices.breaker.total.use_real_memory 为 false,默认为 JVM 堆的 70%;如果为 true,默认为 JVM 堆的 95%。 Field data circuit breaker 字段数据断路器 The field data circuit breaker estimates the heap memory required to load a field into the field data cache. If loading the field would cause the cache to exceed a predefined memory limit, the circuit breaker stops the operation and returns an error. 字段数据断路器估计将字段加载到字段数据缓存中所需的堆内存。如果加载该字段会导致缓存超出预定义的内存限制,则断路器将停止操作并返回错误。 ...

2023-11-20 · 5 min

Elasticsearch稳定性介绍

Elasticsearch 的团队致力于不断改进 Elasticsearch 和 Apache Lucene,以保护您的数据。与任何分布式系统一样,Elasticsearch 非常复杂,每个部分都可能遇到需要正确处理的边缘情况。我们将讨论在面对硬件和软件故障时,为改进 Elasticsearch 在健壮性和弹性方面所做的持续努力。 提升 Elasticsearch 的弹性:https://www.elastic.co/cn/videos/improving-elasticsearch-resiliency Elasticsearch 和弹性:https://www.elastic.co/cn/elasticon/conf/2016/sf/elasticsearch-and-resiliency 参考资料:https://www.elastic.co/guide/en/elasticsearch/resiliency/current/index.html 本文不是 Elasticsearch 的 Release docs,只关注与 Elasticsearch 在弹性/稳定性方面的进展。 V5.0.0 使用两阶段提交进行集群状态发布 Elasticsearch 中的主节点会持续监控集群节点,并在某个节点无法及时响应其 PING 请求时将其从集群中移除。如果主节点剩下的节点过少,它将主动放弃主节点的角色,然后开始新的主节点选举过程。当网络分区导致主节点失去许多从节点时,在检测到节点丢失并且主节点下行之前,有一个很短的时间窗口。在这个时间窗口内,主节点可能会错误地接受和确认集群状态变更。为了避免这种情况,我们在集群状态发布中引入了一个新的阶段,其中提议的集群状态会发送给所有节点,但尚未提交。只有在足够多的节点主动确认更改后,更改才会被 committed,并向节点发送 commit 消息#13062。A master node in Elasticsearch continuously monitors the cluster nodes and removes any node from the cluster that doesn’t respond to its pings in a timely fashion. If the master is left with too few nodes, it will step down and a new master election will start. When a network partition causes a master node to lose many followers, there is a short window in time until the node loss is detected and the master steps down. During that window, the master may erroneously accept and acknowledge cluster state changes. To avoid this, we introduce a new phase to cluster state publishing where the proposed cluster state is sent to all nodes but is not yet committed. Only once enough nodes actively acknowledge the change, it is committed and commit messages are sent to the nodes. See #13062。一句话总结:局部断网导致主节点与其他节点失联时,在其他节点检测到,并且投票出新的主节点前,有一个很短的时间窗口,在这个时间窗口内,主节点可能会错误地接受变更。改进:引入了一个新的状态来解决这个问题,想修改集群的状态,需要足够多的节点主动确认后才可以。 V5.0.0 使索引创建能够灵活应对索引关闭和整个集群崩溃 恢复索引需要一定的分片副本(2 除外)可用于分配主分片副本的仲裁。这意味着如果集群在足够的分片被分配#9126之前就宕机了,将无法分配一个主分片。如果一个索引在足够的分片副本启动之前就被关闭了,重启索引也会出现同样的问题,无法重新打开索引#15281。分配 ID#14739通过跟踪集群中已分配的分片副本来解决这个问题。这使得在仅有一个分片副本的情况下也可以安全地恢复一个索引。分配 ID 还可以区分索引已创建但尚未启动任何分片的情况。如果在索引被关闭之前,没有启动任何分片,那么在重新打开索引时将会分配一个全新的分片。Recovering an index requires a quorum (with an exception for 2) of shard copies to be available to allocate a primary. This means that a primary cannot be assigned if the cluster dies before enough shards have been allocated (#9126). The same happens if an index is closed before enough shard copies were started, making it impossible to reopen the index (#15281). Allocation IDs (#14739) solve this issue by tracking allocated shard copies in the cluster. This makes it possible to safely recover an index in the presence of a single shard copy. Allocation IDs can also distinguish the situation where an index has been created but none of the shards have been started. If such an index was inadvertently closed before at least one shard could be started, a fresh shard will be allocated upon reopening the index。一句话总结:恢复一个索引,需要一定数量的(a quorum:至少一半)分片来投票选出能够用来恢复的分片,(分片数为 2 除外,根本没法投,谁可用就用谁),这样如果集群还没有足够多的分片就宕机了,那么这个主分片就没法恢复了。另外,如果一个索引在足够的分片副本启动之前就被关闭了,重启索引也会出现同样的问题。改进:引入了分配 ID 的概念。 ...

2023-11-16 · 15 min

随便写点什么吧

胡言乱语【1】 昨天去了西溪湿地公园,在网络上搜了下攻略发现,有收费区和免费区,而且景色还没什么两样,果断选择无成本的方案。免费的就是最贵的,结果就是人挤人,还有二货明明走在机动车道了,还不满足,还要并排着走(大有不服你来撞我的意思)。开车的人还不能鸣笛,只能找机会借对面的道绕过去,速度缓慢,走走停停。我心想要是开个油车手动挡走这条路,简直就是地狱级。 胡言乱语【2】 碰见一个三口之家朝我走过来,爸爸手里拿着两片叶子,放在嘴边吹响了。小男孩说:“看来爸爸在农村长大还是有用的(会吹叶子做的哨子)。”爸爸和妈妈都笑了,问他:“那你现在在哪里啊?”然后我已经走远了。一位寒窗苦读、艰苦奋斗的农村青年,终于在城市站稳脚跟,周末带着老婆孩子去感受城市里的农村(湿地)。 胡言乱语【3】 杭州是新一线城市,武汉也是新一线城市。武汉房价比不过杭州,东湖也比不过西湖。在历史气息这块,我想还不能直接妄下结论。虽然没有像杭州那样留下一首《忆江南》的诗,但是近代史很多关键性的事件也是在武汉发生的。 关于西溪湿地里高庄的历史 高庄又名西溪山庄,始建于清顺治十四年(1657)至康熙三年(1664年)之间,是清代文人高士奇在西溪的别墅。 高士奇,杭州人,其学识渊博,能诗文,擅书法,精考证,善鉴赏,被清人比作李白、宋濂一流人物,所藏书画甚富。康熙二十八年(1689),康熙南巡时,曾临幸西溪山庄,并赐“竹窗”二字和诗一首:“花源路几重,柴桑出沃土。烟翠竹窗幽,雪香梅岸古”。 传说康熙曾南巡到西溪,独与高士奇泛小舟至高庄观览良久,高士奇将康熙驻趾之亭取名为“宸览亭”,亭柱上有楹联:“翠辇曾停红莲依旧,宸游不再斯事难逢。”居中的石碑上四个大字“高庄宸迹”,此处就是当年康熙皇帝留在西溪的胜景遗迹。” 因为是在免费区,人更多了,大部分都是穿着古装在摆拍的,不得不佩服妹子的专业能力,还有道具的书,捧在手里,拿着小扇子。 而我只是看着水池边的白条,但凡有个网兜,捞起来炸至金黄,还真是美味😋呢。

2023-10-29 · 2 min

Docker Compose 运行 Elastic Stack 8.X

材料准备 如题,使用 Docker Compose 运行 Elastic Stack 8.X,五个月前还在前司做 7.9 版本的 ELK 迁移,如今已经是前司了,当时总共不到 20TB,最近问前同事数据量已经到 40TB 了,而且根据她发我的截图来看,三千多个索引,副本分片却只有两千多,不操心了,事不关己高高挂起,反正现在负责 ELK 的是新来的陌生人。今天看已经发到 8.10 版本了,来装一个感受下新特性。 官方 Docker Compose 官方安装指引文档 额外操作参考博客 version: "2.2" services: setup: image: docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION} volumes: - certs:/usr/share/elasticsearch/config/certs user: "0" command: > bash -c ' if [ x${ELASTIC_PASSWORD} == x ]; then echo "Set the ELASTIC_PASSWORD environment variable in the .env file"; exit 1; elif [ x${KIBANA_PASSWORD} == x ]; then echo "Set the KIBANA_PASSWORD environment variable in the .env file"; exit 1; fi; if [ ! -f config/certs/ca.zip ]; then echo "Creating CA"; bin/elasticsearch-certutil ca --silent --pem -out config/certs/ca.zip; unzip config/certs/ca.zip -d config/certs; fi; if [ ! -f config/certs/certs.zip ]; then echo "Creating certs"; echo -ne \ "instances:\n"\ " - name: es01\n"\ " dns:\n"\ " - es01\n"\ " - localhost\n"\ " ip:\n"\ " - 127.0.0.1\n"\ " - name: es02\n"\ " dns:\n"\ " - es02\n"\ " - localhost\n"\ " ip:\n"\ " - 127.0.0.1\n"\ " - name: es03\n"\ " dns:\n"\ " - es03\n"\ " - localhost\n"\ " ip:\n"\ " - 127.0.0.1\n"\ > config/certs/instances.yml; bin/elasticsearch-certutil cert --silent --pem -out config/certs/certs.zip --in config/certs/instances.yml --ca-cert config/certs/ca/ca.crt --ca-key config/certs/ca/ca.key; unzip config/certs/certs.zip -d config/certs; fi; echo "Setting file permissions"; chown -R root:root config/certs; find . -type d -exec chmod 750 \{\} \;; find . -type f -exec chmod 640 \{\} \;; echo "Waiting for Elasticsearch availability"; until curl -s --cacert config/certs/ca/ca.crt https://es01:9200 | grep -q "missing authentication credentials"; do sleep 30; done; echo "Setting kibana_system password"; until curl -s -X POST --cacert config/certs/ca/ca.crt -u "elastic:${ELASTIC_PASSWORD}" -H "Content-Type: application/json" https://es01:9200/_security/user/kibana_system/_password -d "{\"password\":\"${KIBANA_PASSWORD}\"}" | grep -q "^{}"; do sleep 10; done; echo "All done!"; ' healthcheck: test: ["CMD-SHELL", "[ -f config/certs/es01/es01.crt ]"] interval: 1s timeout: 5s retries: 120 es01: depends_on: setup: condition: service_healthy image: docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION} volumes: - certs:/usr/share/elasticsearch/config/certs - esdata01:/usr/share/elasticsearch/data ports: - ${ES_PORT}:9200 environment: - node.name=es01 - cluster.name=${CLUSTER_NAME} - cluster.initial_master_nodes=es01,es02,es03 - discovery.seed_hosts=es02,es03 - ELASTIC_PASSWORD=${ELASTIC_PASSWORD} - bootstrap.memory_lock=true - xpack.security.enabled=true - xpack.security.http.ssl.enabled=true - xpack.security.http.ssl.key=certs/es01/es01.key - xpack.security.http.ssl.certificate=certs/es01/es01.crt - xpack.security.http.ssl.certificate_authorities=certs/ca/ca.crt - xpack.security.transport.ssl.enabled=true - xpack.security.transport.ssl.key=certs/es01/es01.key - xpack.security.transport.ssl.certificate=certs/es01/es01.crt - xpack.security.transport.ssl.certificate_authorities=certs/ca/ca.crt - xpack.security.transport.ssl.verification_mode=certificate - xpack.license.self_generated.type=${LICENSE} mem_limit: ${MEM_LIMIT} ulimits: memlock: soft: -1 hard: -1 healthcheck: test: [ "CMD-SHELL", "curl -s --cacert config/certs/ca/ca.crt https://localhost:9200 | grep -q 'missing authentication credentials'" ] interval: 10s timeout: 10s retries: 120 es02: depends_on: - es01 image: docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION} volumes: - certs:/usr/share/elasticsearch/config/certs - esdata02:/usr/share/elasticsearch/data environment: - node.name=es02 - cluster.name=${CLUSTER_NAME} - cluster.initial_master_nodes=es01,es02,es03 - discovery.seed_hosts=es01,es03 - bootstrap.memory_lock=true - xpack.security.enabled=true - xpack.security.http.ssl.enabled=true - xpack.security.http.ssl.key=certs/es02/es02.key - xpack.security.http.ssl.certificate=certs/es02/es02.crt - xpack.security.http.ssl.certificate_authorities=certs/ca/ca.crt - xpack.security.transport.ssl.enabled=true - xpack.security.transport.ssl.key=certs/es02/es02.key - xpack.security.transport.ssl.certificate=certs/es02/es02.crt - xpack.security.transport.ssl.certificate_authorities=certs/ca/ca.crt - xpack.security.transport.ssl.verification_mode=certificate - xpack.license.self_generated.type=${LICENSE} mem_limit: ${MEM_LIMIT} ulimits: memlock: soft: -1 hard: -1 healthcheck: test: [ "CMD-SHELL", "curl -s --cacert config/certs/ca/ca.crt https://localhost:9200 | grep -q 'missing authentication credentials'" ] interval: 10s timeout: 10s retries: 120 es03: depends_on: - es02 image: docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION} volumes: - certs:/usr/share/elasticsearch/config/certs - esdata03:/usr/share/elasticsearch/data environment: - node.name=es03 - cluster.name=${CLUSTER_NAME} - cluster.initial_master_nodes=es01,es02,es03 - discovery.seed_hosts=es01,es02 - bootstrap.memory_lock=true - xpack.security.enabled=true - xpack.security.http.ssl.enabled=true - xpack.security.http.ssl.key=certs/es03/es03.key - xpack.security.http.ssl.certificate=certs/es03/es03.crt - xpack.security.http.ssl.certificate_authorities=certs/ca/ca.crt - xpack.security.transport.ssl.enabled=true - xpack.security.transport.ssl.key=certs/es03/es03.key - xpack.security.transport.ssl.certificate=certs/es03/es03.crt - xpack.security.transport.ssl.certificate_authorities=certs/ca/ca.crt - xpack.security.transport.ssl.verification_mode=certificate - xpack.license.self_generated.type=${LICENSE} mem_limit: ${MEM_LIMIT} ulimits: memlock: soft: -1 hard: -1 healthcheck: test: [ "CMD-SHELL", "curl -s --cacert config/certs/ca/ca.crt https://localhost:9200 | grep -q 'missing authentication credentials'" ] interval: 10s timeout: 10s retries: 120 kibana: depends_on: es01: condition: service_healthy es02: condition: service_healthy es03: condition: service_healthy image: docker.elastic.co/kibana/kibana:${STACK_VERSION} volumes: - certs:/usr/share/kibana/config/certs - kibanadata:/usr/share/kibana/data ports: - ${KIBANA_PORT}:5601 environment: - SERVERNAME=kibana - ELASTICSEARCH_HOSTS=https://es01:9200 - ELASTICSEARCH_USERNAME=kibana_system - ELASTICSEARCH_PASSWORD=${KIBANA_PASSWORD} - ELASTICSEARCH_SSL_CERTIFICATEAUTHORITIES=config/certs/ca/ca.crt mem_limit: ${MEM_LIMIT} healthcheck: test: [ "CMD-SHELL", "curl -s -I http://localhost:5601 | grep -q 'HTTP/1.1 302 Found'" ] interval: 10s timeout: 10s retries: 120 volumes: certs: driver: local esdata01: driver: local esdata02: driver: local esdata03: driver: local kibanadata: driver: local 步骤分析 来欣赏下官方文档里面都干了些啥吧 创建了一个卷用来存储各种证书 判断 Elasticsearch 的密码和 Kibana 的密码是否设置(在 .env 文件中设置环境变量的值,包括密码,端口) 自签了一个 CA 然后给每个节点签发了证书,不了解这个流程的,自行谷歌 在 YAML 中运行 bash,使用 bash 执行命令为每个节点生成配置文件,又是 YAML 格式的,俄罗斯套娃。 ...

2023-09-28 · 5 min

记一次Prometheus的离奇经历

前情提要 书接上回,为了获取客户的 Prometheus 监控数据,我写了个脚本用来通过 API 获取监控数据,然后转换成 OpenMetrics 格式以方便传输和导入,代码如下。 import datetime import subprocess import requests import sys """ http://localhost:9090 """ prometheus_url = input("请输入Prometheus链接: ") username = input("请输入用户名: 如无认证,请回车跳过") password = input("请输入密码: 如无认证,请回车跳过") print("下面的两个变量只需填写一个或者不填使用默认值") step = input("请输入每两个数据点间隔(单位秒,建议为5的倍数): ") hours = input("请输入往前查询的小时数(单位小时,建议不填): ") auth = None metric_param = "e" # 将查询出所有带有elasticsearch的指标 if username != "" and password != "": auth = (username, password) # 检查用户输入是否为数字 def is_number(value): try: int(value) return True except ValueError: return False # 如果用户同时设置了hours和step,按用户的输入值查询 if hours != "" and step != "": hours = int(hours) step = int(step) print("将查询过去{}小时的数据,步长为{}秒".format(hours, step)) else: # 如果用户没有输入hours和step,使用默认值 if hours == "" and step == "": print("将使用默认值查询") hours = 30 step = 10 elif hours != "": hours = int(hours) # 根据用户输入的hours计算step step = int(60 * 60 / (11000 / hours)) + 1 print("将查询过去{}小时的数据,步长为{}秒".format(hours, step)) elif step != "": step = int(step) # 根据用户输入的step计算hours hours = int(11000 / (60 / step) / 60) print("将查询过去{}小时的数据,步长为{}秒".format(hours, step)) else: print("输入的小时数和步长必须为有效的数字。") sys.exit(1) end_time = datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%SZ") query_time = datetime.datetime.now() - datetime.timedelta(hours=hours) start_time = query_time.strftime("%Y-%m-%dT%H:%M:%SZ") series = requests.get('{}/api/v1/label/__name__/values'.format(prometheus_url), auth=auth) if series.status_code != 200: print("查询失败,请检查{}/api/v1/label/__name__/values的连通性".format(prometheus_url)) sys.exit(1) else: metric_list = [queryQL for queryQL in series.json()['data'] if '{}'.format(metric_param) in queryQL] # 将查询出所有带有elasticsearch的指标 print("本次一共查询了{}个指标".format(len(metric_list))) with open('openmetrics.txt', 'w') as f: line_count = 0 # 用于统计行数的计数器 for metric_name in metric_list: metric_response = requests.get( '{}/api/v1/query_range?query={}&start={}&end={}&step={}s'.format( prometheus_url, metric_name, start_time, end_time, step)) print(metric_response.url) if metric_response.status_code != 200: print("查询失败,状态码为{}".format(metric_response.status_code)) sys.exit(1) else: prometheus_data = metric_response.json() for result in prometheus_data['data']['result']: metric_name = result['metric']['__name__'] labels = [] for key, value in result['metric'].items(): if key != '__name__': labels.append(f'{key}="{value}"') labels = ','.join(labels) openmetrics = [] for value in result['values']: openmetrics.append(f'{metric_name}{{{labels}}} {value[1]} {value[0]}\n') openmetrics = ''.join(openmetrics) f.write(openmetrics) line_count += openmetrics.count('\n') # 统计写入的行数 f.write('# EOF\n') # 文档末尾标志必须添加,promtool才能正常识别 with open('./note.txt', 'a') as f: # 使用'a'模式来追加内容 f.write('\n') f.write("本次查询的时间范围数据为{},查询的步长为{}秒,共查询了{}种指标,共写入{}行数据".format( end_time, step, len(metric_list), line_count)) # print("写入完成,共写入{}行数据".format(line_count)) # subprocess.run(['gzip', 'openmetrics.txt']) # 脚本运行完毕后自动压缩文本文件 已知我本地测试的 Prometheus 采集频率是每秒一次,假设我们把一秒当作最小的单位,那么可以说采集到的监控数据是一条连续的线。根据代码中的公式 hours = int(11000 / (60 / step) / 60),我们说当步长越大时,跨步就越大,那么数据点的数量应该越小,就好比你走同一段路,你迈的步子越大,是不是需要的步数就越少。 为了记录每一次的步长,所以在代码的最后面将每次查询的参数记录到另一个文本中,如下所示: # 没有停止各种exporter,数据会不停地增加 本次查询的时间范围数据为2023-09-25T19:28:22Z,查询的步长为5秒,共查询了593种指标,共写入9265448行数据 本次查询的时间范围数据为2023-09-25T19:29:41Z,查询的步长为10秒,共查询了593种指标,共写入7027362行数据 本次查询的时间范围数据为2023-09-25T19:31:04Z,查询的步长为6秒,共查询了593种指标,共写入10554827行数据 本次查询的时间范围数据为2023-09-25T19:31:40Z,查询的步长为12秒,共查询了593种指标,共写入5942033行数据 本次查询的时间范围数据为2023-09-25T19:32:14Z,查询的步长为60秒,共查询了593种指标,共写入6093494行数据 本次查询的时间范围数据为2023-09-25T19:33:08Z,查询的步长为30秒,共查询了593种指标,共写入4305816行数据 本次查询的时间范围数据为2023-09-25T19:33:39Z,查询的步长为15秒,共查询了593种指标,共写入4897653行数据 本次查询的时间范围数据为2023-09-25T19:34:03Z,查询的步长为45秒,共查询了593种指标,共写入5551486行数据 本次查询的时间范围数据为2023-09-25T19:37:27Z,查询的步长为55秒,共查询了593种指标,共写入5826733行数据 本次查询的时间范围数据为2023-09-25T19:38:07Z,查询的步长为6秒,共查询了593种指标,共写入10711991行数据 # 停止了exporter但是并不是完全没有变动 本次查询的时间范围数据为2023-09-25T19:46:03Z,查询的步长为5秒,共查询了593种指标,共写入9281524行数据 本次查询的时间范围数据为2023-09-25T19:46:32Z,查询的步长为6秒,共查询了593种指标,共写入10808191行数据 本次查询的时间范围数据为2023-09-25T19:46:57Z,查询的步长为7秒,共查询了593种指标,共写入9272944行数据 本次查询的时间范围数据为2023-09-25T19:47:22Z,查询的步长为8秒,共查询了593种指标,共写入8113585行数据 本次查询的时间范围数据为2023-09-25T19:47:44Z,查询的步长为9秒,共查询了593种指标,共写入7386695行数据 本次查询的时间范围数据为2023-09-25T19:48:14Z,查询的步长为10秒,共查询了593种指标,共写入7019614行数据 本次查询的时间范围数据为2023-09-25T19:48:44Z,查询的步长为5秒,共查询了593种指标,共写入9267445行数据 本次查询的时间范围数据为2023-09-25T19:49:37Z,查询的步长为55秒,共查询了593种指标,共写入5840872行数据 本次查询的时间范围数据为2023-09-25T19:50:40Z,查询的步长为15秒,共查询了593种指标,共写入4990036行数据 本次查询的时间范围数据为2023-09-25T19:54:53Z,查询的步长为15秒,共查询了593种指标,共写入5005466行数据 求助智障 以下是和人工智障的对话内容: ...

2023-09-25 · 11 min

获取客户 Prometheus 监控数据

业务背景 在排查问题时,想通过 Grafana 看板查看用户的监控,只能靠拍照,效率低,质量一般。设计一个方案能够方便地将问题出现前 24 小时的监控数据拿到,在本地导入,就能够在本地 Grafana 方便地查看。Prometheus 本身只提供了 API 查询的功能,并没有导出数据功能;自带的 promtool 也只提供验证规则文件和配置文件、调试等功能。 参考文章 Analyzing Prometheus data with external tools Prometheus backfilling 方案一:使用 API 导出转换成 CSV 使用 API 查询,将查询到的数据转换成 CSV。刚好 Grafana 有插件能够将 CSV 作为数据源。经过实验后并不是特别顺利,能够读取到 CSV,但没有成功绘制出图像。 总结 经过实验后并不是特别顺利,能够读取到 CSV 但没有成功绘制出图像。看板中部分查询语句中包含看板变量,CSV 数据源无法实现看板变量。 方案二:拷贝 Prometheus 数据文件 Prometheus 按照两个小时为一个时间窗口,将两小时内产生的数据存储在一个块(Block)中。每个块都是一个单独的目录,里面包含该时间窗口内的所有样本数据(chunks)、元数据文件(meta.json)以及索引文件(index)。其中索引文件会将指标名称和标签索引到样本数据的时间序列中。此期间如果通过 API 删除时间序列,删除记录会保存在单独的逻辑文件 tombstone 当中。 Prometheus 为了防止丢失暂存在内存中的还未被写入磁盘的监控数据,引入了 WAL 机制。WAL 被分割成默认大小为 128M 的文件段(segment),之前版本默认大小是 256M,文件段以数字命名,长度为 8 位的整型。WAL 的写入单位是页(page),每页的大小为 32KB,所以每个段大小必须是页的大小的整数倍。如果 WAL 一次性写入的页数超过一个段的空闲页数,就会创建一个新的文件段来保存这些页,从而确保一次性写入的页不会跨段存储。这些数据暂时没有持久化,TSDB 通过 WAL 将数据保存到磁盘上(保存的数据没有压缩,占用内存较大),当出现宕机时,启动多协程读取 WAL,恢复数据。 [mingming.chen@m162p65 data]$ tree . ├── 01E2MA5GDWMP69GVBVY1W5AF1X │ ├── chunks # 保存压缩后的时序数据,每个 chunks 大小为 512M,超过会生成新的 chunks │ │ └── 000001 │ ├── index # chunks 中的偏移位置 │ ├── meta.json # 记录 block 块元信息,比如样本的起始时间、chunks 数量和数据量大小等 │ └── tombstones # 通过 API 方式对数据进行软删除,将删除记录存储在此处(API 的删除方式,并不是立即将数据从 chunks 文件中移除) ├── 01E2MH175FV0JFB7EGCRZCX8NF │ ├── chunks │ │ └── 000001 │ ├── index │ ├── meta.json │ └── tombstones ├── 01E2MQWYDFQAXXPB3M1HK6T20A │ ├── chunks │ │ └── 000001 │ ├── index │ ├── meta.json │ └── tombstones ├── lock ├── queries.active └── wal # 防止数据丢失(数据收集上来暂时是存放在内存中,wal 记录了这些信息) ├── 00000366 # 每个数据段最大为 128M,存储默认存储两个小时的数据量 ├── 00000367 ├── 00000368 ├── 00000369 └── checkpoint.000365 └── 00000000 无论是 block 数据还是 wal 数据,都是可以直接打包,转移到本地的 Prometheus。需要注意的是版本问题,且本地 Prometheus 不能有数据。如果本地监控数据目录不为空,那么导入时会出现问题(因为时间问题)。只需要近期数据,太远的数据没有价值,可以通过 block 文件里面的 meta.json 查看时间戳。 ...

2023-09-08 · 17 min

折腾博客的意义

WordPress 没有像大城市里的 Z 时代青少年一样在初中高中就开始折腾各种网络安全、编程技术、折腾博客,我在大二时才有建一个网站的想法,买了阿里云的 ECS,买了域名,装了宝塔,然后 LNMP 一键启动,WordPress 无脑梭哈,还折腾了备案。但是除了写了几个测试的文章,后面连续费云主机的动力都没有了,于是索性连域名也卖了。记得好像是十几块钱买的,还卖了 9 块钱。我的博客从网络世界消失了。对于我来说,我知道了原来网站是这么运行的,原来还有宝塔和 WordPress 这么牛逼的工具,原来备案 TM 的这么麻烦! Hexo 再后来技术上走向正规,经常查一些技术文章,看到别人写的博客,知道了 Hexo,知道了 GitHub Pages,开始折腾静态博客,使用 Hexo 和使用人数最多的主题 Next,再结合网络上的各种方案魔改。当时我想的就是“整挺好,以后就这样不换了”。Hexo 是截止我写这篇文时用的最久的框架,相处久了,了解的多了,遇到麻烦的次数也就多了。 VuePress 期间还折腾了文档站点,看到了别人的文档站点挺好的,我的一些文档也可以放进文档站点作为文档库啊。然后又开始了 VuePress、Docusaurus,因为抱着一旦启用就不换的决心,每个方案都折腾了不少时间。最终我的 GitHub 仓库还残存着一个 Docs 的仓库。然后我发现文档这个东西就是个伪需求,就此作罢。 Hexo in Docker 一开始 Hexo 运行在 Mac 上,我不敢随便重置系统。后来对 Docker 的运用加深,开始折腾在 Docker 里运行 Hexo,也还算顺利。Dockerfile 存在 Dropbox 里,存在 iCloud 里,非常的安稳和踏实。但是后面的事告诉我事情没这么简单:Dockerfile 中每次都拉取 latest 的 node 基础镜像,git clone 也是每次都是最新的,导致版本不兼容时就会有报错,或者是警告。这个问题不是不能解决,指定版本就好了呀,是的。但我还是开始嫌弃 Hexo 了,甚至我都没有继续写下去的动力了(借口)。在群里吐槽 Hexo 的问题的时候,馆长说那你可以用 Hugo 啊,就一个可执行文件。此时 Hexo 的种种不好,都浮现脑海:速度慢,环境麻烦,node_modules…… Hugo 终于下定决心,试试 Hugo。 brew install hugo hugo new site blog cd blog git clone [你的想要的主题] themes hugo new post/first.md hugo server -D 不行我要开始无脑吹了:把之前的 blog 的 Markdown 文件复制到 Hugo 目录,生成速度嗖嗖的。我选的非常精简的主题,摆脱了 npm,摆脱了 package.json,整个主题非常的简单、简洁。以前 Hexo 需要安装插件实现搜索、字数统计,Hugo 全都聚合在了一个可执行文件里。有好处也有不好:好处是,不用折腾了,啥都有;坏处可能就好像花钱买了不需要的东西,目前这点坏处我是无所谓的。 ...

2023-08-28 · 4 min

Flask+Vue 前后端分离记录

如题,记录告警平台从原来的 layui 升级到 Vue,并实现 Flask+Vue 前后端分离,记录前后端三种解决跨域的方式(有点像茴香豆的四种写法?没事,技多不压身) ...

2023-07-12 · 3 min

分享一份值得分享的 SRE 面试题

如题,分享记录一份还不错的面试题。看面试题的目的不是为了记住题目然后去应试(也不可能人家题都不带换的),如果是这样的话,就算过了,面试真正在工作中遇到挑战时一脸懵逼,怕是离大礼包不远了。看面试题是为了知道自己的水平,了解哪些是常见问题,如果有不足的地方就要提升弥补。 ...

2023-07-11 · 5 min

Kuboard-spray 图形化安装高可用的 Kubernetes

不管你有没有听说过 Kuboard,它都是一个非常有名的 K8s 管理工具,官方描述的是 Kuboard - Kubernetes 多集群管理界面,我这次想介绍的是 Kuboard-spray ...

2023-07-07 · 2 min