<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>elasticsearch | 伪架构师</title>
    <link>/tags/elasticsearch/</link>
      <atom:link href="/tags/elasticsearch/index.xml" rel="self" type="application/rss+xml" />
    <description>elasticsearch</description>
    <generator>Source Themes Academic (https://sourcethemes.com/academic/)</generator><language>zh</language><lastBuildDate>Wed, 31 Oct 2018 21:00:53 +0800</lastBuildDate>
    <image>
      <url>/img/logo-wide.png</url>
      <title>elasticsearch</title>
      <link>/tags/elasticsearch/</link>
    </image>
    
    <item>
      <title>使用 go-mysql-elasticsearch 把 MySQL 中的业务日志导入 Elasticsearch</title>
      <link>/post/from-mysql-to-es/</link>
      <pubDate>Wed, 31 Oct 2018 21:00:53 +0800</pubDate>
      <guid>/post/from-mysql-to-es/</guid>
      <description>

&lt;blockquote&gt;
&lt;p&gt;太长不看版：&lt;a href=&#34;https://github.com/siddontang/go-mysql-elasticsearch&#34; target=&#34;_blank&#34;&gt;&lt;code&gt;go-mysql-elasticsearch&lt;/code&gt;&lt;/a&gt; 能把 MySQL 数据库中的数据导入到 Elasticsearch 之中。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&#34;前言&#34;&gt;前言&lt;/h2&gt;

&lt;p&gt;相当一部分应用的日志是保存在数据库之中的，这些陈旧又稳定的应用在支撑着业务的运行。它们产生的日志通常来说也是有其价值的，但是如果能够融入到目前较为通用的 Elasticsearch 当中的话，可能有助于降低运维工作量，防止信息孤岛，并且进一步挖掘既有应用和业务的商业价值。&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://github.com/siddontang/go-mysql-elasticsearch&#34; target=&#34;_blank&#34;&gt;&lt;code&gt;go-mysql-elasticsearch&lt;/code&gt;&lt;/a&gt; 就是这样一个项目，它可以从 MySQL 的数据表中读取指定数据表的数据，发送到 ElasticSearch 之中。它会使用 &lt;code&gt;mysqldump&lt;/code&gt; 命令处理现有存量数据，并借助 binlog 的方式跟踪增量数据，从而保证 Elasticsearch 的数据和 MySQL 数据库中的数据保持同步。下面会简单讲一下这一项目的配置，并试验一个简单例子，最后根据实际情况进行一些改进。&lt;/p&gt;

&lt;h2 id=&#34;条件和假设&#34;&gt;条件和假设&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;目前该工具支持 MySQL 和 ES 的版本都是 5.x。&lt;/li&gt;
&lt;li&gt;MySQL 服务器需要开启 row 模式的 binlog。&lt;/li&gt;
&lt;li&gt;因为要使用 &lt;code&gt;mysqldump&lt;/code&gt; 命令，因此该进程的所在的服务器需要部署这一工具。&lt;/li&gt;
&lt;li&gt;这一工具使用 GoLang 开发，需要 Go 1.9+ 的环境进行构建。&lt;/li&gt;
&lt;li&gt;可用的 MySQL、Elasticsearch 以及 Kibana 实例。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;另外为了进行演示，这里做一点假设：&lt;/p&gt;

&lt;h3 id=&#34;业务日志表&#34;&gt;业务日志表&lt;/h3&gt;

&lt;pre&gt;&lt;code class=&#34;language-sql&#34;&gt;CREATE TABLE `biz_log` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `receive_content` text,
  `send_content` text,
  `fd_receive_content` text,
  `fd_send_content` text,
  `log_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8mb4;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;四个 &lt;code&gt;text&lt;/code&gt; 字段&lt;strong&gt;使用 JSON 格式&lt;/strong&gt;存储了几个不同的日志种类。&lt;/p&gt;

&lt;h2 id=&#34;工具构建&#34;&gt;工具构建&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;go get github.com/siddontang/go-mysql-elasticsearch&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cd $GOPATH/src/github.com/siddontang/go-mysql-elasticsearch&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;make&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&#34;转换配置&#34;&gt;转换配置&lt;/h2&gt;

&lt;p&gt;执行 &lt;code&gt;go-mysql-elasticsearch --help&lt;/code&gt;，会看到一系列的参数，最主要的参数就是 &lt;code&gt;-config&lt;/code&gt;，这个参数用于设置转换过程所需的参数配置文件，在源码的 &lt;a href=&#34;https://github.com/siddontang/go-mysql-elasticsearch/blob/master/etc/river.toml&#34; target=&#34;_blank&#34;&gt;&lt;code&gt;/etc/river.toml&lt;/code&gt;&lt;/a&gt; 中包含了一份配置样本。作者在这一配置样本中提供了非常详尽的注释，可以对转换过程做出很多定制。但是由于工具本身具备很好的适应能力，加上 ES 的强大功能，只需要一点简单的设置，就能够顺利完成常见任务了。&lt;/p&gt;

&lt;p&gt;一个简单的配置：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-ini&#34;&gt;# MySQL 的相关配置
# 指定用户必须具备复制权限
my_addr = &amp;quot;127.0.0.1:3306&amp;quot;
my_user = &amp;quot;root&amp;quot;
my_pass = &amp;quot;Flzx3000c&amp;quot;
my_charset = &amp;quot;utf8mb4&amp;quot;

# ES 相关配置
es_addr = &amp;quot;127.0.0.1:9200&amp;quot;
es_user = &amp;quot;&amp;quot;
es_pass = &amp;quot;&amp;quot;

# 数据源配置
# 以 Slave 模式工作
server_id = 10001
# mysql/mariadb
flavor = &amp;quot;mysql&amp;quot;

# mysqldump 路径，如果为空或者未设置，会跳过这一环节。
mysqldump = &amp;quot;mysqldump&amp;quot;
bulk_size = 128
flush_bulk_time = &amp;quot;200ms&amp;quot;
skip_no_pk_table = false


[[source]]
# 数据库名称
schema = &amp;quot;biz&amp;quot;
# 数据表同步范围，支持通配符
tables = [&amp;quot;biz_log&amp;quot;]

# 规则定义
[[rule]]
# 数据库名称
schema = &amp;quot;biz&amp;quot;
# 规则对应的数据表，支持通配符
table = &amp;quot;biz_log&amp;quot;
# 目标 ES 索引
index = &amp;quot;biz&amp;quot;
# 该规则在 ES 中生成的文档类型
type = &amp;quot;log_db&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&#34;同步&#34;&gt;同步&lt;/h2&gt;

&lt;p&gt;配置文件完成之后，就可以执行 &lt;code&gt;./go-mysql-elasticsearch -config=./river.toml&lt;/code&gt;，日志中会显示首先执行 &lt;code&gt;mysqldump&lt;/code&gt; 导出存量数据，然后开始守护进程阶段，跟踪 binlog 并进行同步。&lt;/p&gt;

&lt;p&gt;此时打开 Kibana，执行 &lt;code&gt;GET _search&lt;/code&gt;，会看到数据库记录已经进入了 ES 中，并且按照我们定义的规则进行了索引。在守护进行运行期间，如果有新的数据插入，也会同步到 ES 之中。&lt;/p&gt;

&lt;p&gt;但是这里我们会发现一个小问题，前面提到的 JSON 字段被作为单一的字符串存入了 ES 索引。这样就根据 JSON 中的特定字段进行搜索的需要就比较费劲了，而我们也知道，如果直接向 ES 提交文档，其中的 JSON 是会被映射为 Object 类型的。如果对 ES 索引进行数据类型的定义，会发现直接将 JSON 字段映射到 Object 类型后，同步过程会失败，返回错误认为将无效内容映射到了这一类型。因此可以推测是字符串并没有使用原有格式提交给 ES。&lt;/p&gt;

&lt;p&gt;经过对代码的阅读跟踪，发现在 &lt;a href=&#34;https://github.com/siddontang/go-mysql-elasticsearch/blob/d23841980e75da3d6f0c8a13b3876b9a70cb7b80/elastic/client.go#L139&#34; target=&#34;_blank&#34;&gt;&lt;code&gt;elastic/client.go&lt;/code&gt;&lt;/a&gt; 中对数据进行了一次 Json 编码：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;    default:
        //for create and index
        data, err = json.Marshal(r.Data)
        if err != nil {
            return errors.Trace(err)
        }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;下面就尝试进行一点改动，使之支持嵌套在字段内容中的 JSON 内容。&lt;/p&gt;

&lt;h2 id=&#34;json&#34;&gt;JSON&lt;/h2&gt;

&lt;p&gt;这里我想到了一个简单粗暴的办法就是，对数据报文进行一次检查，如果该字段内容是有效 JSON 的话，就使用 &lt;code&gt;github.com/buger/jsonparser&lt;/code&gt; 的 &lt;code&gt;set&lt;/code&gt; 方法，将压缩后的 JSON 字符串重新赋值给编码后的 &lt;code&gt;byte[]&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;首先给 &lt;code&gt;BulkRequest&lt;/code&gt; 定义一个新方法，用于数据编码&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;func (b *BulkRequest) encodeData() []byte {
     jsonResults,_ := json.Marshal(b.Data)
     // 判断是否有效的 JSON 数据
     isJson := func(s string) bool {
         var js map[string]interface{}
         return json.Unmarshal([]byte(s), &amp;amp;js) == nil

     }
     for key, value := range b.Data {
         stringValue, ok := value.(string)
         // 如果字段内容是字符串并且是 JSON 格式
         if ok &amp;amp;&amp;amp; isJson(stringValue) {
             // 设置编码后内容该字段的值为原文
             jsonResults,_ = jsonparser.Set(jsonResults, []byte(stringValue), key)
            }
     }
     return jsonResults
 }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;然后将原有的 &lt;code&gt;data, err = json.Marshal(r.Data)&lt;/code&gt; 替换为 &lt;code&gt;data = r.encodeData()&lt;/code&gt;，再次构建运行。会看到 ES 成功的将 JSON 字段进行了解析，生成了 Object 类型的映射关系。&lt;/p&gt;

&lt;h2 id=&#34;补充说明&#34;&gt;补充说明&lt;/h2&gt;

&lt;p&gt;这里引用&lt;a href=&#34;https://my.oschina.net/u/2282993/blog/1821930&#34; target=&#34;_blank&#34;&gt;go-mysql-elasticsearch功能及性能验证&lt;/a&gt; 一文的性能测试结果：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-plain&#34;&gt;1.全量同步
支持：需要安装mysqldump（mysql自带），同步11.5w数据，耗时3分13秒。
全量基于mysqldump，需要将工具和mysql安装在同一个节点，其它方式尚未找到。
2.增量同步
支持。
增量插入20W数据，耗时8分钟。
删除20w条数据，耗时6分。
更新20w条数据，12分钟。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这一工具还有一些其它亮点，例如多表聚合、字段过滤、自定义字段映射等。&lt;/p&gt;

&lt;h2 id=&#34;相关链接&#34;&gt;相关链接&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;项目地址：&lt;code&gt;https://github.com/siddontang/go-mysql-elasticsearch&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;配置文件样本：&lt;code&gt;https://github.com/siddontang/go-mysql-elasticsearch/blob/master/etc/river.toml&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Fork 地址：&lt;code&gt;https://github.com/fleeto/go-mysql-elasticsearch&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;作者原版教程：&lt;code&gt;https://www.jianshu.com/p/96c7858b580f&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;go-mysql-elasticsearch 功能及性能验证 ：&lt;code&gt;https://my.oschina.net/u/2282993/blog/1821930&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;JsonParser：&lt;code&gt;https://github.com/buger/jsonparser&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
</description>
    </item>
    
    <item>
      <title>Grafana 和 Elasticsearch</title>
      <link>/post/grafana-and-es/</link>
      <pubDate>Wed, 28 Dec 2016 19:56:08 +0800</pubDate>
      <guid>/post/grafana-and-es/</guid>
      <description>

&lt;p&gt;容器化和微服务，让世界花枝招展，又支离破碎。一个典型的运行在容器云之上的微服务架构的应用，通常是由多种服务和基础设施的支撑而来的。这对运维工作提出一个很大的挑战 —— 一个应用后端的众多系统，究竟是怎样的工作状况？&lt;/p&gt;

&lt;p&gt;事实上，所有构成这一应用的微服务以及支撑这一应用的所有基础设施，都会有各自的日志、指标数据，以及构建在上游的监控、日志系统。各处分散的数据和系统，会给支持团队造成极大的负担，也最终成为开发运维工作的拦路虎。&lt;/p&gt;

&lt;p&gt;之前的经验中，可以把自家应用的各种业务、技术指标通过 Zabbix 或者 influxDB 进行存储，经由 Grafana 的插件系统进行整合展示，目前流行的容器云支撑系统 Kubernetes，也能够通过 influxDB 在 Grafana 上展示 Heapster 搜集到的数据。指标的事情解决了，下面自然就是日志了。&lt;/p&gt;

&lt;p&gt;Grafana 也提供了针对 Elasticsearch 的数据源插件。下面用 Kubernetes 中正在运行的日志收集实例来展示 Grafana 对 ES 的支持。&lt;/p&gt;

&lt;h2 id=&#34;建立数据源&#34;&gt;建立数据源&lt;/h2&gt;

&lt;p&gt;首先是建立一个 Elasticsearch 类型的数据源。这里使用了一个 Kubernetes 集群的 ES 日志。Type 部分选择 Elasticsearch，然后填写 URL 地址、认证方式。Index name 填写 &lt;code&gt;logstash-*&lt;/code&gt;。大致如图所示。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/grafana-ds-es.png&#34; alt=&#34;datasource&#34; /&gt;&lt;/p&gt;

&lt;p&gt;点击下面的 &lt;code&gt;Test &amp;amp; Save&lt;/code&gt;，测试成功后完成数据源设置。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Access 一般来说都是选择 Proxy，也就是服务器间通信。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&#34;建立指标&#34;&gt;建立指标&lt;/h2&gt;

&lt;p&gt;接下来进入展示环节。利用新建的 ES 数据源，来建立展示单元。&lt;/p&gt;

&lt;h3 id=&#34;曲线图&#34;&gt;曲线图&lt;/h3&gt;

&lt;p&gt;首先建立一个 Graph Panel，数据源选择上面新建的 ES。&lt;/p&gt;

&lt;p&gt;Query 一栏需要按照 Lucene 语法进行查询。这里我们选择 &lt;code&gt;kubernetes.container_name: &amp;quot;jenkins&amp;quot;&lt;/code&gt;，也就是容器名称为 &amp;ldquo;jenkins&amp;rdquo; 的日志项。&lt;/p&gt;

&lt;p&gt;其他可以保持缺省即可。&lt;/p&gt;

&lt;p&gt;配置结束之后，稍等几秒钟，就会出现数据点。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/grafana-es-metric-count.png&#34; alt=&#34;count&#34; /&gt;&lt;/p&gt;

&lt;h3 id=&#34;日志表格&#34;&gt;日志表格&lt;/h3&gt;

&lt;p&gt;个人感觉上面的的数字在日志来说用处并不大，我们的目的还是在同一界面下查看日志。&lt;/p&gt;

&lt;p&gt;建立一个 Table Panel。配置数据源为 ES。&lt;strong&gt;Metric 选择 &lt;code&gt;Raw Document&lt;/code&gt;&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;经过短暂等候，上面表格会出现一堆的 JSON 文本，我们要通过 Options 来进行展现配置。&lt;/p&gt;

&lt;p&gt;在 Columns 中我们简单的新加两列：&amp;rdquo;kubernetes.container_name&amp;rdquo; 和 &amp;ldquo;log&amp;rdquo;，就会以表格的形式把这两列展示出来。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/grafana-es-log-table.png&#34; alt=&#34;table&#34; /&gt;&lt;/p&gt;

&lt;h2 id=&#34;告一段落&#34;&gt;告一段落&lt;/h2&gt;

&lt;p&gt;至此，Grafana 就成为一个集成了众多来源的运维入口。经过进一步的加工和配置（&lt;strong&gt;其实非常大量非常琐碎&lt;/strong&gt;），仅从这一个入口就能够完成很多的日常巡检任务；更重要的是，因为数据的统一展示，在业务、服务和基础设施之间建立了直观的联系，为事故的处理甚至预测，提供了更多的便利条件。&lt;/p&gt;
</description>
    </item>
    
  </channel>
</rss>
