\u003cp\u003eElassandra是一个基于Apache Cassandra的Elasticsearch实现,有效结合了两者的优势,弥补了Elasticsearch的一些使用限制(单点故障、在线升级等)。结合\u003ca href=\"https://github.com/fluent/fluent-bit-docs\"\u003eFluent-Bit\u003c/a\u003e以及Kibana,Elassandra为kubernetes集群日志分析提供了一个高效独特的方案。\u003c/p\u003e\n\u003ch2\u003eElasticsearch升级\u003c/h2\u003e\n\u003cp\u003eElasticsearch采用主从分片架构设计:主节点管理映射修改;只有主分片支持写操作,副本分片只能进行读操作;当主分片故障时,主节点可以把副本分片升级为主分片。Cassandra的引入增强了Elassandra的可用性,弱化了主节点的单点控制作用,并且实现了多点写操作。所有的节点都可以搜索请求,请求映射更新,并根据Cassandra的复制因子进行写操作。\u003c/p\u003e\n\u003cp\u003e因此,Elassandra也可以更容易地通过kubernetes来管理,而且可以实现无宕机维护(滚动重启升级)。除此以外,对集群进行水平扩容或收缩也很容易了,因为Elassandra完全兼容现有的Elasticsearch索引,我们只需要移动那些已经被重建的索引(重新索引)。Elassandra同时原生支持kubernetes集群的跨数据中心副本服务。\u003c/p\u003e\n\u003ch2\u003eEFK部署\u003c/h2\u003e\n\u003cp\u003e为了更好地让Elassandra在Kubernetes中发挥作用,我们同样搭配了一个EFK数据栈:在日志处理和转发上,我们使用了\u003ca href=\"https://github.com/fluent/fluent-bit-docs\"\u003eFluent-Bit\u003c/a\u003e,一个支持Elasticsearch后端的轻量日志处理引擎(当然也可以使用传统的\u003ca href=\"https://github.com/fluent/fluentd\"\u003eFluentd\u003c/a\u003e或\u003ca href=\"https://github.com/elastic/beats/tree/master/filebeat\"\u003eFilebeat\u003c/a\u003e);可视化面板方面则使用了普遍的Kibana。\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"https://dzone.com/storage/temp/11061537-elassandra-fluentbit-kibana.png\" alt=\"图片\" /\u003e\u003cbr /\u003e\n基于EFK的Kubernetes集群结构图\u003c/p\u003e\n\u003cp\u003e为使部署方便高效,我们使用了Helm的chart安装包的方式,安装源为\u003ca href=\"https://github.com/strapdata/strapcharts\"\u003eStrapdata的Helm\u003c/a\u003e仓库。\u003c/p\u003e\n\u003cp\u003e首先,我们建立了一个三节点集群,硬件上使用了SSD存储,而软件则使用了Azure的Kubenetes服务。集群搭建完成后,我们使用helm把Elassandra安装到所有节点中,命令如下:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003ehelm install --name \u0026quot;elassandra\u0026quot; --namespace default \\\n --set image.repo=strapdata/elassandra \\\n --set image.tag=6.2.3.10 \\\n --set config.cluster_size=3 \\\n --set persistence.storageClass=managed-premium \\\n strapdata/elassandra\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e安装完成后,我们可以发现Elasticsearch服务和Cassandra服务已经暴露在kubernetes中了。\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003eNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE\n\nservice/cassandra ClusterIP 10.0.193.114 \u0026lt;none\u0026gt; 9042/TCP,9160/TCP 14h\n\nservice/elassandra ClusterIP None \u0026lt;none\u0026gt; 7199/TCP,7000/TCP,7001/TCP,9300/TCP,9042/TCP,9160/TCP,9200/TCP 14h\n\nservice/elasticsearch ClusterIP 10.0.75.121 \u0026lt;none\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e其次,部署Kibana。为保持兼容性,我们需要一个同Elasticsearch相同版本的安装包,例如本文都使用了版本6.2.3。\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003ehelm install --namespace default --name my-kibana \\\n --set image.tag=6.2.3 \\\n --set service.externalPort=5601 \\\n stable/kibana\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e如果希望把Kibana服务暴露在公共IP上,我们在这里可以安装一个\u003ca href=\"https://kubernetes.io/docs/concepts/services-networking/ingress/\"\u003ekubernetes的入口代理\u003c/a\u003e—\u003ca href=\"https://docs.traefik.io/\"\u003eTraefik\u003c/a\u003e:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003ehelm install --name traefik --namespace kube-system --set dashboard.domain=traefik-dashboard.aks1.strapdata.com stable/traefik\n\nhelm install --namespace $NAMESAPCE --name my-kibana --set image.tag=6.2.3 \\\n --set service.externalPort=5601 \\\n --set env.ELASTICSEARCH_URL=\u0026quot;http://elassandra-elasticsearch:9200\u0026quot; \\\n --set ingress.enabled=true,ingress.hosts[0]=\u0026quot;kibana.${1:-aks1.strapdata.com}\u0026quot;,ingress.annotations.\u0026quot;kubernetes\\.io/ingress\\.class\u0026quot;=\u0026quot;traefik\u0026quot; \\\n stable/kibana\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e最后,我们需要使用一个特定的Elasticsearch索引模板来安装Fluent-Bit,该模板加载了Elassandra相关的设置,从而可以优化日志存储和搜索的性能。\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003ehelm install --name my-fluentbit --set trackOffsets=\u0026quot;true\u0026quot; \\\n--set backend.type=\u0026quot;es\u0026quot;,backend.es.host=\u0026quot;elassandra-elasticsearch.default.svc.cluster.local\u0026quot;,backend.es.time_key=\u0026quot;es_time\u0026quot;,backend.es.pipeline=\u0026quot;fluentbit\u0026quot; \\\n--set parsers.enabled=true,parsers.json[0].name=\u0026quot;docker\u0026quot;,parsers.json[0].timeKey=\u0026quot;time\u0026quot;,parsers.json[0].timeFormat=\u0026quot;%Y-%m-%dT%H:%M:%S.%L\u0026quot;,parsers.json[0].timeKeep=\u0026quot;Off\u0026quot; strapdata/fluent-bit\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e到这里,我们就完成了对Elassandra、Fluent-Bit和Kibana的部署,Fluent-Bit开始源源不断地把pod中的日志输送到Elassandra集群。\u003c/p\u003e\n\u003cp\u003e接下来,我们再看一些针对Elassandra的优化设置:\u003c/p\u003e\n\u003ch2\u003eElassandra索引优化\u003c/h2\u003e\n\u003cp\u003e\u003ca href=\"https://github.com/strapdata/helm-charts/tree/master/charts/fluent-bit\"\u003eHelm中Fluent-Bit的chart包\u003c/a\u003e为Elassandra提供了一个Elasticsearch索引模板,模板设置如下:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e\u0026quot;settings\u0026quot;: {\n \u0026quot;index\u0026quot;: {\n \u0026quot;replication\u0026quot;: \u0026quot;DC1:2\u0026quot;,\n \u0026quot;table_options\u0026quot;: \u0026quot;compaction = {'compaction_window_size': '4', 'compaction_window_unit': 'HOURS', 'class': 'org.apache.cassandra.db.compaction.TimeWindowCompactionStrategy'}\u0026quot;,\n \u0026quot;mapping\u0026quot;: {\n \u0026quot;total_fields\u0026quot;: {\n \u0026quot;limit\u0026quot;: \u0026quot;10000\u0026quot;\n }\n },\n \u0026quot;refresh_interval\u0026quot;: \u0026quot;15s\u0026quot;,\n \u0026quot;drop_on_delete_index\u0026quot;: true,\n \u0026quot;index_insert_only\u0026quot;: true,\n \u0026quot;index_static_columns\u0026quot;: true,\n \u0026quot;search_strategy_class\u0026quot;: \u0026quot;RandomSearchStrategy\u0026quot;\n }\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch3\u003eInsert-Only模式\u003c/h3\u003e\n\u003cp\u003eElassandra中,Elasticsearc文档的字段\u003ca href=\"https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-source-field.html\"\u003e_source\u003c/a\u003e作为列项被存储在Cassandra表中。在通过Elasticsearch索引API向Cassandra存储层插入数据时,空字段会被默认赋值为null,从而避免覆盖已有文档。该插入操作同时产生了一些Cassandra墓碑,这些墓碑其实对很多不变日志记录是没有用处的,这时设置index.index_insert_only字段将会避免产生墓碑,从而达到优化Cassandra存储的目的。\u003c/p\u003e\n\u003ch3\u003eDrop on Delete Index设置\u003c/h3\u003e\n\u003cp\u003eElassandra采用Cassandra作为数据存储层。一般情况下,Elassandra删除一个索引并不会真正删除底层Cassandra中的表和键空间。我们可以通过更改对index.drop_on_delete_index的设置,从而在删除表中索引的同时也能自动删除Cassandra中的表。\u003c/p\u003e\n\u003ch3\u003e副本管理\u003c/h3\u003e\n\u003cp\u003eElassandra中,Cassandra取代Elasticsearch实现了数据副本管理。底层Cassandra的键空间副本映射保存了位置和副本数量。Elasticsearch模板中的index.replication字段定义了Cassandra的副本映射。本例中,我们在数据中心DC1中保存了两个副本。\u003c/p\u003e\n\u003ch3\u003etable_options字段\u003c/h3\u003e\n\u003cp\u003etable_options字段定义Cassandra使用创建时间的表选项。因为日志记录是不变的,我们在这里选择使用\u003ca href=\"http://thelastpickle.com/blog/2016/12/08/TWCS-part1.html\"\u003eTime Window Compactio策略\u003c/a\u003e来设计时间序列数据,当然我们也可以使用默认的TTL策略或压缩策略(默认为LZ4)。\u003c/p\u003e\n\u003ch3\u003e搜索策略类\u003c/h3\u003e\n\u003cp\u003e在Elassandra中,调度节点会根据search_strategy_class定义的搜索策略将子请求分发到其他可用的节点中。默认的PrimaryFirstSearchStrategy策略会将子请求发送到所有的节点。我们在这里使用了RandomSearchStrategy,在数据中心能够获得一个结果的最小节点集。比如在我们采用六节点两个副本要求的集群时,这种策略将只请求三个节点而不是六个,从而极大地减轻了集群的全局负载。\u003c/p\u003e\n\u003ch2\u003eElassandra映射优化\u003c/h2\u003e\n\u003cp\u003e由于Elasticsearch使用了多值字段,Elassandra将所有类型为X的字段都保存到了一个Cassandra的列表X中。如果我们使用工具\u003ca href=\"https://docs.datastax.com/en/cassandra/3.0/cassandra/tools/ToolsSSTabledump.html\"\u003esstabledump\u003c/a\u003e查看产生的SSTables表,将得到如下由filebeat产生的Cassandra列:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e{\n \u0026quot;partition\u0026quot; : {\n \u0026quot;key\u0026quot; : [ \u0026quot;RL5Bp2cBWzCxo-DQnARL\u0026quot; ],\n \u0026quot;position\u0026quot; : 160123\n },\n \u0026quot;rows\u0026quot; : [\n {\n \u0026quot;type\u0026quot; : \u0026quot;row\u0026quot;,\n \u0026quot;position\u0026quot; : 160691,\n \u0026quot;liveness_info\u0026quot; : { \u0026quot;tstamp\u0026quot; : \u0026quot;2018-12-13T11:09:14.378005Z\u0026quot; },\n \u0026quot;cells\u0026quot; : [\n { \u0026quot;name\u0026quot; : \u0026quot;es_time\u0026quot;, \u0026quot;deletion_info\u0026quot; : { \u0026quot;marked_deleted\u0026quot; : \u0026quot;2018-12-13T11:09:14.378004Z\u0026quot;, \u0026quot;local_delete_time\u0026quot; : \u0026quot;2018-12-13T11:09:14Z\u0026quot; } },\n { \u0026quot;name\u0026quot; : \u0026quot;es_time\u0026quot;, \u0026quot;path\u0026quot; : [ \u0026quot;868796ae-fec7-11e8-aa29-7b1a7ab32955\u0026quot; ], \u0026quot;value\u0026quot; : \u0026quot;2018-12-13 11:08:42.265Z\u0026quot; },\n { \u0026quot;name\u0026quot; : \u0026quot;kubernetes\u0026quot;, \u0026quot;deletion_info\u0026quot; : { \u0026quot;marked_deleted\u0026quot; : \u0026quot;2018-12-13T11:09:14.378004Z\u0026quot;, \u0026quot;local_delete_time\u0026quot; : \u0026quot;2018-12-13T11:09:14Z\u0026quot; } },\n { \u0026quot;name\u0026quot; : \u0026quot;kubernetes\u0026quot;, \u0026quot;path\u0026quot; : [ \u0026quot;868796aa-fec7-11e8-aa29-7b1a7ab32955\u0026quot; ], \u0026quot;value\u0026quot; : {\u0026quot;container_name\u0026quot;: [\u0026quot;logs-generator\u0026quot;], \u0026quot;host\u0026quot;: [\u0026quot;aks-nodepool1-36080323-0\u0026quot;], \u0026quot;annotations\u0026quot;: null, \u0026quot;docker_id\u0026quot;: [\u0026quot;e38071228edf79584ef4eafdfb67c0144605a31730e71b02b3f6e1c8f27e0ea3\u0026quot;], \u0026quot;pod_id\u0026quot;: [\u0026quot;721a6540-fca4-11e8-8d8b-f6dcc5e73f85\u0026quot;], \u0026quot;pod_name\u0026quot;: [\u0026quot;logs-generator\u0026quot;], \u0026quot;namespace_name\u0026quot;: [\u0026quot;default\u0026quot;], \u0026quot;labels\u0026quot;: [{\u0026quot;app\u0026quot;: null, \u0026quot;controller-revision-hash\u0026quot;: null, \u0026quot;release\u0026quot;: null, \u0026quot;pod-template-generation\u0026quot;: null, \u0026quot;statefulset_kubernetes_io/pod-name\u0026quot;: null, \u0026quot;kubernetes_io/cluster-service\u0026quot;: null, \u0026quot;k8s-app\u0026quot;: null, \u0026quot;name\u0026quot;: null}]} },\n { \u0026quot;name\u0026quot; : \u0026quot;log\u0026quot;, \u0026quot;deletion_info\u0026quot; : { \u0026quot;marked_deleted\u0026quot; : \u0026quot;2018-12-13T11:09:14.378004Z\u0026quot;, \u0026quot;local_delete_time\u0026quot; : \u0026quot;2018-12-13T11:09:14Z\u0026quot; } },\n { \u0026quot;name\u0026quot; : \u0026quot;log\u0026quot;, \u0026quot;path\u0026quot; : [ \u0026quot;868796ab-fec7-11e8-aa29-7b1a7ab32955\u0026quot; ], \u0026quot;value\u0026quot; : \u0026quot;I1213 11:08:42.265287 6 logs_generator.go:67] 362287 PUT /api/v1/namespaces/ns/pods/s2qj 215\\n\u0026quot; },\n { \u0026quot;name\u0026quot; : \u0026quot;stream\u0026quot;, \u0026quot;deletion_info\u0026quot; : { \u0026quot;marked_deleted\u0026quot; : \u0026quot;2018-12-13T11:09:14.378004Z\u0026quot;, \u0026quot;local_delete_time\u0026quot; : \u0026quot;2018-12-13T11:09:14Z\u0026quot; } },\n { \u0026quot;name\u0026quot; : \u0026quot;stream\u0026quot;, \u0026quot;path\u0026quot; : [ \u0026quot;868796ac-fec7-11e8-aa29-7b1a7ab32955\u0026quot; ], \u0026quot;value\u0026quot; : \u0026quot;stderr\u0026quot; },\n { \u0026quot;name\u0026quot; : \u0026quot;time\u0026quot;, \u0026quot;deletion_info\u0026quot; : { \u0026quot;marked_deleted\u0026quot; : \u0026quot;2018-12-13T11:09:14.378004Z\u0026quot;, \u0026quot;local_delete_time\u0026quot; : \u0026quot;2018-12-13T11:09:14Z\u0026quot; } },\n { \u0026quot;name\u0026quot; : \u0026quot;time\u0026quot;, \u0026quot;path\u0026quot; : [ \u0026quot;868796ad-fec7-11e8-aa29-7b1a7ab32955\u0026quot; ], \u0026quot;value\u0026quot; : \u0026quot;2018-12-13 11:08:42.265Z\u0026quot; }\n ]\n }\n ]\n }\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e我们可以看到, \u003ca href=\"https://docs.datastax.com/en/cql/3.3/cql/cql_using/useCollections.html\"\u003eCassandra的集合类型\u003c/a\u003e(链表,集合,映射),产生了很多没有用的负载。对此,Elassandra使用了新的属性来扩展Elasticsearch映射,而这些属性\u0026quot;cql_collection\u0026quot;:\u0026quot;singtelon\u0026quot;可以将Cassandra类型显式地映射到单值字段。由于Fluent-Bit索引模板使用了这种单值字段映射属性,Cassandra列存储也变得更加轻量:\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e\nroot@elassandra-0:/usr/share/cassandra# tools/bin/sstabledump /var/lib/cassandra/data/logstash_2018_12_17/flb_type-751e28a0022f11e9a83bbd84c8d6464a/mc-9-big-Data.db\n{\n \u0026quot;partition\u0026quot; : {\n \u0026quot;key\u0026quot; : [ \u0026quot;nmWfvWcBHsJPeHipoGsM\u0026quot; ],\n \u0026quot;position\u0026quot; : 402735\n },\n \u0026quot;rows\u0026quot; : [\n {\n \u0026quot;type\u0026quot; : \u0026quot;row\u0026quot;,\n \u0026quot;position\u0026quot; : 403179,\n \u0026quot;liveness_info\u0026quot; : { \u0026quot;tstamp\u0026quot; : \u0026quot;2018-12-17T19:23:34.412Z\u0026quot; },\n \u0026quot;cells\u0026quot; : [\n { \u0026quot;name\u0026quot; : \u0026quot;es_time\u0026quot;, \u0026quot;value\u0026quot; : \u0026quot;2018-12-17 19:23:33.603Z\u0026quot; },\n { \u0026quot;name\u0026quot; : \u0026quot;kubernetes\u0026quot;, \u0026quot;value\u0026quot; : {\u0026quot;container_name\u0026quot;: \u0026quot;logs-generator\u0026quot;, \u0026quot;host\u0026quot;: \u0026quot;aks-nodepool1-36080323-0\u0026quot;, \u0026quot;docker_id\u0026quot;: \u0026quot;e33b2cda2ed7ac3bc5a6504cd79b6ea999137a11791d67fbb8b497fe06d8d700\u0026quot;, \u0026quot;pod_id\u0026quot;: \u0026quot;8ecacdcd-0229-11e9-8d8b-f6dcc5e73f85\u0026quot;, \u0026quot;pod_name\u0026quot;: \u0026quot;logs-generator\u0026quot;, \u0026quot;namespace_name\u0026quot;: \u0026quot;default\u0026quot;, \u0026quot;labels\u0026quot;: [{\u0026quot;app\u0026quot;: null, \u0026quot;component\u0026quot;: null, \u0026quot;controller-revision-hash\u0026quot;: null, \u0026quot;tier\u0026quot;: null, \u0026quot;pod-template-generation\u0026quot;: null, \u0026quot;name\u0026quot;: null, \u0026quot;pod-template-hash\u0026quot;: null, \u0026quot;version\u0026quot;: null, \u0026quot;k8s-app\u0026quot;: null, \u0026quot;kubernetes_io/cluster-service\u0026quot;: null, \u0026quot;release\u0026quot;: null, \u0026quot;statefulset_kubernetes_io/pod-name\u0026quot;: null, \u0026quot;run\u0026quot;: [\u0026quot;logs-generator\u0026quot;]}], \u0026quot;annotations\u0026quot;: null} },\n { \u0026quot;name\u0026quot; : \u0026quot;log\u0026quot;, \u0026quot;value\u0026quot; : \u0026quot;I1217 19:23:33.600235 6 logs_generator.go:67] 2850 POST /api/v1/namespaces/ns/pods/65w 207\\n\u0026quot; },\n { \u0026quot;name\u0026quot; : \u0026quot;stream\u0026quot;, \u0026quot;value\u0026quot; : \u0026quot;stderr\u0026quot; },\n { \u0026quot;name\u0026quot; : \u0026quot;time\u0026quot;, \u0026quot;value\u0026quot; : \u0026quot;2018-12-17 19:23:33.603Z\u0026quot; }\n ]\n }\n ]\n }\n\u003c/code\u003e\u003c/pre\u003e\n\u003ch2\u003eElassandra存储优化\u003c/h2\u003e\n\u003cp\u003eFluent-Bit为每一条日志记录都添加了一些相关的kubernetes元数据,对于某一个容器来的日志记录来说,所有的元数据都是相同的。对于这些日志记录的元数据,由于Cassandra采用了宽列存储,我们可以只存储一次,这样大大减小了SSTables的容量。下图展示了Cassandra存储中的Elasticsearch documents结构:\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"https://dzone.com/storage/temp/11061557-cassandra-static-column-storage.png\" alt=\"图片\" /\u003e\u003c/p\u003e\n\u003cp\u003e为实现上述结构,我们使用\u003ca href=\"https://www.elastic.co/guide/en/elasticsearch/reference/current/pipeline-processor.html\"\u003eElasticsearch管道\u003c/a\u003e改变了原始的JSON文档,增加了一个时间戳(唯一的)来作为timeuuid(类型1UUID),并使用Cassandra复合主键构建了一个document_id字段,复合键包括使用Docker容器ID的分区键 ,以及使用新timeuuid的集群键。\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003ecurl -H \u0026quot;Content-Type: application/json\u0026quot; -XPUT \u0026quot;http://elassandra-0:9200/_ingest/pipeline/fluentbit\u0026quot; -d'{\n \u0026quot;description\u0026quot; : \u0026quot;fluentbit elassandra pipeline\u0026quot;,\n \u0026quot;processors\u0026quot; : [\n {\n \u0026quot;timeuuid\u0026quot; : {\n \u0026quot;field\u0026quot;: \u0026quot;es_time\u0026quot;,\n \u0026quot;target_field\u0026quot;: \u0026quot;ts\u0026quot;,\n \u0026quot;formats\u0026quot; : [\u0026quot;ISO8601\u0026quot;],\n \u0026quot;timezone\u0026quot; : \u0026quot;Europe/Amsterdam\u0026quot;\n }\n },\n {\n \u0026quot;set\u0026quot; : {\n \u0026quot;field\u0026quot;: \u0026quot;_id\u0026quot;,\n \u0026quot;value\u0026quot;: \u0026quot;[\\\u0026quot;{{kubernetes.docker_id}}\\\u0026quot;,\\\u0026quot;{{ts}}\\\u0026quot;]\u0026quot;\n }\n }\n ]\n}'\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e这样,我们可以将下面的映射属性添加到我们的\u003ca href=\"https://github.com/strapdata/helm-charts/blob/master/charts/fluent-bit/templates/elassandra-template.yaml\"\u003eFluent-bit Elasticsearch模板中\u003c/a\u003e:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e分区键列中字段赋值:cql_partition_key:true, cql_primary_key_order:0\u003c/li\u003e\n\u003cli\u003e集群键列中字段赋值:cql_primary_key_order:1,cql_type:timeuuid\u003c/li\u003e\n\u003cli\u003eKubernetes元数据列中字段赋值: cql_static_column:true\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e我们同时添加了索引设置:index.index_static_columns:true ,用来索引Cassandra静态列和所有的行。\u003c/p\u003e\n\u003cp\u003e在清除现存的索引、重新部署新的Elasticsearch管道和模板之后,SSTables只需要每天为每个容器保存一个宽列日志存储。这样,我们大大优化了存储性能,并且Kubernetes元数据只需要每天保存一个Docker容器即可。同时由于timeuuid列可以被Elasticsearch当作日期使用,Cassandra集群键清除了一些时间字段,设置是时间戳字段,例如es_time和time等。\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e{\n \u0026quot;partition\u0026quot; : {\n \u0026quot;key\u0026quot; : [ \u0026quot;f9f237c2d64dd8b92130fd34f567b162f0ae1972e7afabee9151539ba31ccadd\u0026quot; ],\n \u0026quot;position\u0026quot; : 2800\n },\n \u0026quot;rows\u0026quot; : [\n {\n \u0026quot;type\u0026quot; : \u0026quot;static_block\u0026quot;,\n \u0026quot;position\u0026quot; : 3326,\n \u0026quot;cells\u0026quot; : [\n { \u0026quot;name\u0026quot; : \u0026quot;kubernetes\u0026quot;, \u0026quot;value\u0026quot; : {\u0026quot;container_name\u0026quot;: \u0026quot;put-template\u0026quot;, \u0026quot;host\u0026quot;: \u0026quot;aks-nodepool1-36080323-0\u0026quot;, \n \u0026quot;pod_id\u0026quot;: \u0026quot;e24f2521-1256-11e9-8fe6-de1ce27ac649\u0026quot;, \n \u0026quot;pod_name\u0026quot;: \u0026quot;fluent-bit-sc6m6\u0026quot;, \u0026quot;namespace_name\u0026quot;: \u0026quot;default\u0026quot;, \n \u0026quot;labels\u0026quot;: [{\u0026quot;app\u0026quot;: \u0026quot;my-fluentbit-fluent-bit\u0026quot;, \u0026quot;component\u0026quot;: null, \u0026quot;controller-revision-hash\u0026quot;: \u0026quot;3156758786\u0026quot;, \u0026quot;tier\u0026quot;: null, \n \u0026quot;pod-template-generation\u0026quot;: \u0026quot;1\u0026quot;, \u0026quot;name\u0026quot;: null, \u0026quot;pod-template-hash\u0026quot;: null, \u0026quot;version\u0026quot;: null, \u0026quot;k8s-app\u0026quot;: null, \u0026quot;kubernetes_io/cluster-service\u0026quot;: null, \n \u0026quot;release\u0026quot;: [\u0026quot;my-fluentbit\u0026quot;], \u0026quot;statefulset_kubernetes_io/pod-name\u0026quot;: null}], \n \u0026quot;annotations\u0026quot;: [{\u0026quot;checksum/config\u0026quot;: [\u0026quot;3375211361605629fc5a1f970e1fce0ce2fabbcb08ef4631acdc4bd2ac41fd7b\u0026quot;], \n \u0026quot;scheduler_alpha_kubernetes_io/critical-pod\u0026quot;: null, \u0026quot;prometheus_io/port\u0026quot;: null, \u0026quot;prometheus_io/scrape\u0026quot;: null}]}, \n \u0026quot;tstamp\u0026quot; : \u0026quot;2019-01-07T08:33:35.968Z\u0026quot; }\n ]\n },\n {\n \u0026quot;type\u0026quot; : \u0026quot;row\u0026quot;,\n \u0026quot;position\u0026quot; : 3326,\n \u0026quot;clustering\u0026quot; : [ \u0026quot;e4375630-1256-11e9-a990-c3ec4d724241\u0026quot; ],\n \u0026quot;liveness_info\u0026quot; : { \u0026quot;tstamp\u0026quot; : \u0026quot;2019-01-07T08:33:35.954Z\u0026quot; },\n \u0026quot;cells\u0026quot; : [\n { \u0026quot;name\u0026quot; : \u0026quot;log\u0026quot;, \u0026quot;value\u0026quot; : \u0026quot; % Total % Received % Xferd Average Speed Time Time Time Current\\n\u0026quot; },\n { \u0026quot;name\u0026quot; : \u0026quot;stream\u0026quot;, \u0026quot;value\u0026quot; : \u0026quot;stderr\u0026quot; }\n ]\n },\n {\n \u0026quot;type\u0026quot; : \u0026quot;row\u0026quot;,\n \u0026quot;position\u0026quot; : 3326,\n \u0026quot;clustering\u0026quot; : [ \u0026quot;e4375630-1256-11e9-0566-b0312df0dcfc\u0026quot; ],\n \u0026quot;liveness_info\u0026quot; : { \u0026quot;tstamp\u0026quot; : \u0026quot;2019-01-07T08:33:35.964Z\u0026quot; },\n \u0026quot;cells\u0026quot; : [\n { \u0026quot;name\u0026quot; : \u0026quot;log\u0026quot;, \u0026quot;value\u0026quot; : \u0026quot; Dload Upload Total Spent Left Speed\\n\u0026quot; },\n { \u0026quot;name\u0026quot; : \u0026quot;stream\u0026quot;, \u0026quot;value\u0026quot; : \u0026quot;stderr\u0026quot; }\n ]\n }\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e我们可以在这里使用Kibana来创建面板,分析日志。\u003c/p\u003e\n\u003cp\u003e\u003cimg src=\"https://dzone.com/storage/temp/11061561-elassandra-fluentbit-kibana-discover.png\" alt=\"图片\" /\u003e\u003c/p\u003e\n\u003ch2\u003e可用性测试\u003c/h2\u003e\n\u003cp\u003e最后,我们通过实例来验证一下Elassandra的可用性。\u003c/p\u003e\n\u003cp\u003e首先,创建一个日志产生器,输出实验所需要的日志记录。\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003efor i in {1..20}; do kubectl run logs-generator${i} --generator=run-pod/v1 --image=k8s.gcr.io/logs-generator:v0.1.1 \\\n--restart=Never --env \u0026quot;LOGS_GENERATOR_LINES_TOTAL=50000\u0026quot; --env \u0026quot;LOGS_GENERATOR_DURATION=3m\u0026quot;; done\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e其次,我们强制关闭一个Elassandra pod,人为制造一个单点故障,然后由Kubernetes控制器自动重启。\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e$kubectl delete pod/elassandra-1 pod \u0026quot;elassandra-1\u0026quot; deleted\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e这种情况下,如果Cassandra只有一个副本,关闭主节点将会产生写错误。\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e[2019/01/10 16:46:09] [ warn] [out_es] Elasticsearch error\n{\u0026quot;took\u0026quot;:3,\u0026quot;ingest_took\u0026quot;:2,\u0026quot;errors\u0026quot;:true,\u0026quot;items\u0026quot;:[{\u0026quot;index\u0026quot;:{\u0026quot;_index\u0026quot;:\u0026quot;logstash-2019.01.10\u0026quot;,\u0026quot;_type\u0026quot;:\u0026quot;flb_type\u0026quot;,\u0026quot;_id\u0026quot;:\u0026quot;[\\\u0026quot;c0e45980637031ed19386d8a2b3fa736597057eea46917b8c28b73ba640e3cc3\\\u0026quot;,\\\u0026quot;25c13480-14f6-11e9-2b53-a734ca6c6447\\\u0026quot;]\u0026quot;,\u0026quot;status\u0026quot;:500,\u0026quot;error\u0026quot;:{\u0026quot;type\u0026quot;:\u0026quot;write_failure_exception\u0026quot;,\u0026quot;reason\u0026quot;:\u0026quot;Operation failed - received 0 responses and 1 failures\u0026quot;}}},{\u0026quot;index\u0026quot;:{\u0026quot;_index\u0026quot;:\u0026quot;logstash-2019.01.10\u0026quot;,\u0026quot;_type\u0026quot;:\u0026quot;flb_type\u0026quot;,\u0026quot;_id\u0026quot;:\u0026quot;[\\\u0026quot;c0e45980637031ed19386d8a2b3fa736597057eea46917b8c28b73ba640e3cc3\\\u0026quot;,\\\u0026quot;372b3680-14f6-11e9-d3d8-e1397bcf3034\\\u0026quot;]\u0026quot;,\u0026quot;status\u0026quot;:500,\u0026quot;error\u0026quot;:{\u0026quot;type\u0026quot;:\u0026quot;write_failure_exception\u0026quot;,\u0026quot;reason\u0026quot;:\u0026quot;Operation failed - received 0 responses and 1 failures\u0026quot;}}},{\u0026quot;index\u0026quot;:{\u0026quot;_index\u0026quot;:\u0026quot;logstash-2019.01.10\u0026quot;,\u0026quot;_type\u0026quot;:\u0026quot;flb_type\u0026quot;,\u0026quot;_id\u0026quot;:\u0026quot;[\\\u0026quot;c0e45980637031ed19386d8a2b3fa736597057eea46917b8c28b73ba640e3cc3\\\u0026quot;,\\\u0026quot;41a8c280-14f6-11e9-bc51-b90088b4aa65\\\u0026quot;]\u0026quot;,\u0026quot;status\u0026quot;:500,\u0026quot;error\u0026quot;:{\u0026quot;type\u0026quot;:\u0026quot;write_failure_exception\u0026quot;,\u0026quot;reason\u0026quot;:\u0026quot;Operation failed - received 0 responses and 1 failur\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e然后,我们为Logstash索引创建两个Cassandra副本,并重复上述测试。\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003ekubectl exec -it elassandra-0 -- cqlsh\nConnected to elassandra at 127.0.0.1:9042.\n[cqlsh 5.0.1 | Cassandra 3.11.3.5 | CQL spec 3.4.4 | Native protocol v4]\nUse HELP for help.\ncqlsh\u0026gt; ALTER KEYSPACE logstash_2019_01_10 WITH replication = {'class': 'NetworkTopologyStrategy','DC1':'2'};\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e虽然在测试中Fluent-Bit提示器显示有一些连接中断错误,但它仍然将数据保存到了Elassandra。\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003e[2019/01/10 16:57:39] [error] [http_client] broken connection to elassandra-elasticsearch.default.svc.cluster.local:9200 ?\n[2019/01/10 16:57:39] [ warn] [out_es] http_do=-1\n[2019/01/10 16:57:39] [error] [http_client] broken connection to elassandra-elasticsearch.default.svc.cluster.local:9200 ?\n[2019/01/10 16:57:39] [ warn] [out_es] http_do=-1\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003eCassandra使用了多主节点架构,即便重启一个节点,我们仍然可以通过Elasticsearch的API在其他节点上进行写操作。如果丢失了一个节点,Elasticsearch指示器将变为黄色,但搜索功能仍能继续使用。\u003c/p\u003e\n\u003cpre\u003e\u003ccode\u003ehealth status index uuid pri rep docs.count docs.deleted store.size pri.store.size\nyellow open logstash-2019.01.10 p01ngidBRDSVsi99NupG5g 3 1 227478 0 88.3mb 88.3mb\nyellow open .kibana 68N_Dsk0SoOG9jHElafDUw 3 1 3 0 15.8kb 15.8kb\n\u003c/code\u003e\u003c/pre\u003e\n\u003cp\u003e节点重启后,Elasticsearch指示器将重新变为绿色,Cassandra节点2和节点3会将\u003ca href=\"https://docs.datastax.com/en/cassandra/3.0/cassandra/operations/opsRepairNodesHintedHandoff.html\"\u003ehint handoff\u003c/a\u003e发送到节点1,以便恢复丢失的日志记录。\u003c/p\u003e\n\u003ch2\u003e结语\u003c/h2\u003e\n\u003cp\u003eElassandra的出现为Kubernetes中的日志处理增加了很多使用场景,如上文所示:通过相应的设置,你可以使用Elasticsearch的管道处理器来更改或优化Cassandra存储,即便我们不使用Elasticsearch的索引功能(在Elasticsearch映射中设置index=no,如此将只获得底层Cassandra表中的数据);在维护Elassandra集群时,我们也可以在不关机的情况下实现滚动升级;亦或者在不用重新索引的情况下轻松实现水平扩展。\u003c/p\u003e\n\u003cp\u003e当然,Elassandra也终于开始为混合云中的跨Kubernetes集群提供副本支持,详情将在其他文章中展开。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e英文原文地址\u003c/strong\u003e:\u003ca href=\"https://dzone.com/articles/kubernetes-logs-with-elassandra?utm_medium=feed\u0026amp;utm_source=feedpress.me\u0026amp;utm_campaign=Feed:%20dzone\"\u003ehttps://dzone.com/articles/kubernetes-logs-with-elassandra?utm_medium=feed\u0026amp;utm_source=feedpress.me\u0026amp;utm_campaign=Feed:%20dzone\u003c/a\u003e\u003c/p\u003e\n