<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>易博云天</title>
	<atom:link href="http://www.eavea.com/blog/index.php/feed" rel="self" type="application/rss+xml" />
	<link>http://www.eavea.com/blog</link>
	<description>专注技术：学习、交流、分享、免费，每天进步一点点！</description>
	<lastBuildDate>Mon, 13 Mar 2023 07:04:16 +0000</lastBuildDate>
	<language>zh-CN</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.6.1</generator>
		<item>
		<title>【今日头条极速版】边看新闻边赚钱，适合喜欢看新闻的小伙伴！</title>
		<link>http://www.eavea.com/blog/index.php/toutiao.html</link>
		<comments>http://www.eavea.com/blog/index.php/toutiao.html#comments</comments>
		<pubDate>Sun, 21 Feb 2021 07:52:16 +0000</pubDate>
		<dc:creator>eavea</dc:creator>
				<category><![CDATA[手机应用]]></category>
		<category><![CDATA[注册体验]]></category>
		<category><![CDATA[toutiao]]></category>
		<category><![CDATA[头条]]></category>
		<category><![CDATA[应用]]></category>
		<category><![CDATA[注册]]></category>
		<category><![CDATA[网赚]]></category>

		<guid isPermaLink="false">http://www.eavea.com/blog/?p=69</guid>
		<description><![CDATA[第1步：支付宝扫码下载【今日头条极速版】 &#160; &#160; 第2步：填写红包邀请码：Q0981718 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>第1步：支付宝扫码下载【今日头条极速版】</p>
<p>&nbsp;</p>
<div class="wp-caption alignnone" style="width: 676px"><img class=" " alt="今日头条极速版下载" src="http://pic.eavea.com/pic/69-1.jpg" width="666" /><p class="wp-caption-text">今日头条极速版下载</p></div>
<p>&nbsp;</p>
<p>第2步：填写红包邀请码：<span style="color: #ff0000; font-size: xx-large;">Q09817188</span></p>
<p>&nbsp;</p>
<div class="wp-caption alignnone" style="width: 676px"><img class=" " alt="今日头条极速版邀请" src="http://pic.eavea.com/pic/69-2.jpg" width="666" /><p class="wp-caption-text">今日头条极速版邀请</p></div>
<p>&nbsp;</p>
<p>第3步：获得专属红包奖励，当天即可提现到支付宝。</p>
<p>&nbsp;</p>
<div class="wp-caption alignnone" style="width: 676px"><img class=" " alt="今日头条极速版赚钱" src="http://pic.eavea.com/pic/69-3.jpg" width="666" /><p class="wp-caption-text">今日头条极速版赚钱</p></div>
<p>&nbsp;</p>
<p>第4步：每天看看新闻，做做日常任务，还有其他的（比如走路赚钱、吃饭补贴、睡觉赚钱），获得的金币每天凌晨会自动转换成余额，均可提现。</p>
<p>&nbsp;</p>
<p>PS：偷偷告诉你，还可以种种水果，1分钱包邮哦。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.eavea.com/blog/index.php/toutiao.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Elasticsearch Query DSL入门</title>
		<link>http://www.eavea.com/blog/index.php/elasticsearchquerydslrumen.html</link>
		<comments>http://www.eavea.com/blog/index.php/elasticsearchquerydslrumen.html#comments</comments>
		<pubDate>Tue, 14 Apr 2020 07:58:54 +0000</pubDate>
		<dc:creator>eavea</dc:creator>
				<category><![CDATA[后端技术]]></category>
		<category><![CDATA[Elasticsearch]]></category>

		<guid isPermaLink="false">http://www.eavea.com/blog/?p=67</guid>
		<description><![CDATA[Elasticsearch Query DSL入门 列举几个最基础的 DSL 语句，所有长达成百上千行的 DS [&#8230;]]]></description>
				<content:encoded><![CDATA[<div>Elasticsearch Query DSL入门</div>
<blockquote><p>列举几个最基础的 DSL 语句，所有长达成百上千行的 DSL 都是由这些基础语法组合起来的。</p></blockquote>
<h2 id="toc-heading-1">一、环境</h2>
<ul>
<li>Ubuntu 14.04/16、04</li>
<li>JDK1.8</li>
<li>Elasticsearch 5.3</li>
<li>Kibana 5.3.2</li>
</ul>
<h2 id="toc-heading-2">二、DSL介绍</h2>
<p>Query DSL 又叫查询表达式，是一种非常灵活又富有表现力的查询语言，采用 JSON 接口的方式实现丰富的查询，并使你的查询语句更灵活、更精确、更易读且易调试。</p>
<p>我平时喜欢借助 Kibana 来执行 DSL 语句，它能够辅助自己开发，也能用于 debug 和研究。</p>
<p>实际项目中一般封装一下第三方引擎来实现业务，而这些语法转换成 DSL 后经常有成百上千行，但其实一点也不复杂。不管是过滤、聚合还是嵌套，只要理解了最基本的语法，都是很好解读的。</p>
<p>这篇文章主要还是记录下我平时常用的几个 DSL，防止隔得时间久了，裸写 DSL 不一定写得出来。</p>
<h2 id="toc-heading-3">三、全文查询</h2>
<h3 id="toc-heading-4">1. match_all</h3>
<p>/_search 查找整个ES中所有索引的内容，/ 前面可以加上索引名，多个索引名之前用英文逗号分割。<br />
下面这个语法是 match_all 查询，Kibana 能够自动补全代码，最简单。</p>
<div>bash<i></i><i></i></div>
<pre><code>GET /_search
{
  "query": {
    "match_all": {}
  }
}
</code></pre>
<h3 id="toc-heading-5">2. match</h3>
<p>下边的例子就表示查找 host 为 wenyuanblog.com 的所有记录。</p>
<div>bash<i></i><i></i></div>
<pre><code>POST /wenyuanblog-2018.03.02/_search
{
  "query": {
    "match": {
      "host": "wenyuanblog.com"
    }
  }
}
</code></pre>
<h3 id="toc-heading-6">3. multi_match</h3>
<p>在多个字段上执行相同的 match 查询，下边的例子就表示查询 host 或 http_referer 字段中包含 wenyuanblog.com 的记录。</p>
<div>bash<i></i><i></i></div>
<pre><code>GET /wenyuanblog-2018.03.02/_search
{
  "query": {
    "multi_match": {
      "query": "wenyuanblog.com",
      "fields": [
        "host",
        "http_referer"
      ]
    }
  }
}
</code></pre>
<h3 id="toc-heading-7">4. query_string</h3>
<p>可以在查询里边使用 AND 或者 OR 来完成复杂的查询。</p>
<p>下边的例子表示查找 host 为 a.wenyuanblog.com 或者 b.wenyuanblog.com 的所有记录。</p>
<div>bash<i></i><i></i></div>
<pre><code>GET /wenyuanblog-2018.03.02/_search
{
  "query": {
    "query_string": {
      "query": "(a.wenyuanblog.com) OR (b.wenyuanblog.com)",
      "fields": [
        "host"
      ]
    }
  }
}
</code></pre>
<p>也可以组合更多的条件完成更复杂的查询请求。</p>
<p>下边的例子表示查询（host 为 a.wenyuanblog.com）或者是（host 为 b.wenyuanblog.com 且 status 为 404）的所有记录。</p>
<div>bash<i></i><i></i></div>
<pre><code>GET /wenyuanblog-2018.03.02/_search
{
  "query": {
    "query_string": {
      "query": "host:a.wenyuanblog.com OR (host:b.wenyuanblog.com AND status:404)"
    }
  }
}
</code></pre>
<p>与其相类似的还有个 simple_query_string 的关键字，可以将 query_string 中的 AND 或 OR 用 + 或 | 这样的符号替换掉。</p>
<h3 id="toc-heading-8">5. term</h3>
<p>term 可以用来精确匹配，精确匹配的值可以是数字、时间、布尔值或者是设置了 not_analyzed 不分词的字符串。</p>
<div>bash<i></i><i></i></div>
<pre><code>GET /wenyuanblog-2018.03.02/_search
{
  "query": {
    "term": {
      "status": {
        "value": 404
      }
    }
  }
}
</code></pre>
<p>term 对输入的文本不进行分析，直接精确匹配输出结果，如果要同时匹配多个值可以使用 terms。</p>
<div>bash<i></i><i></i></div>
<pre><code>GET /wenyuanblog-2018.03.02/_search
{
  "query": {
    "terms": {
      "status": [
        403,
        404
      ]
    }
  }
}
</code></pre>
<h3 id="toc-heading-9">6. range</h3>
<p>range 用来查询落在指定区间内的数字或者时间。<br />
下边的例子表示搜索所有状态为 200 到 399 之间的数据，这里的操作符主要有四个 gt 大于，gte 大于等于，lt 小于，lte 小于等于。</p>
<div>bash<i></i><i></i></div>
<pre><code>GET /wenyuanblog-2018.03.02/_search
{
  "query": {
    "range": {
      "status": {
        "gte": 200,
        "lte": 399
      }
    }
  }
}
</code></pre>
<p>当使用日期作为范围查询时，我们需要注意下日期的格式，官方支持的日期格式主要有两种：</p>
<p>① 时间戳，注意是毫秒粒度。</p>
<div>bash<i></i><i></i></div>
<pre><code>GET /wenyuanblog-2018.03.02/_search
{
  "query": {
    "range": {
      "@timestamp": {
        "gte": 1519920000000,
        "lte": 1519956000000,
        "format": "epoch_millis"
      }
    }
  }
}
</code></pre>
<p>② 日期字符串。</p>
<div>bash<i></i><i></i></div>
<pre><code>GET /wenyuanblog-2018.03.02/_search
{
  "query": {
    "range": {
      "@timestamp": {
        "gte": "2018-03-02 00:00:00",
        "lte": "2018-03-03",
        "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd",
        "time_zone": "+08:00"
      }
    }
  }
}
</code></pre>
<p>选择哪种方式根据实际情况决定，我们业务中用时间戳的情况居多。</p>
<p>如果采用日期字符串的方式，那么可以使用 format 字段指定匹配的日期格式，如果格式有多个就用 || 分开，像例子中那样，不过建议用同样的日期格式。</p>
<p>如果日期中缺少年月日这些内容，那么缺少的部分会用 unix 的开始时间（即 1970年1月1日）填充，当你将 “format”:”dd” 指定为格式时，那么 “gte”:10 将被转换成 1970-01-10T00:00:00.000Z。</p>
<p>Elasticsearch 中默认使用的是 UTC 时间，所以我们在使用时要通过 time_zone 来设置好时区，以免出错。</p>
<h3 id="toc-heading-10">7. exists</h3>
<p>查询出存在某字段的文档。</p>
<div>bash<i></i><i></i></div>
<pre><code>GET /wenyuanblog-2018.03.02/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "exists": {
            "field": "visitor.name"
          }
        }
      ]
    }
  },
  "from": 0,
  "size": 100
}
</code></pre>
<h3 id="toc-heading-11">8. bool组合查询</h3>
<p>通常我们可能需要将很多个条件组合在一起查出最后的结果，这个时候就需要使用 ES 提供的 bool 来实现了。<br />
布尔查询支持的子查询类型共有四种，分别是：must，should，must_not 和 filter。</p>
<p>must：类似于 SQL 中的 AND ，必须包含；<br />
must_not：类似于 SQL 中的 NOT，必须不包含；<br />
should：文档应该匹配 should 子句查询的一个或多个；<br />
filter：过滤器，文档必须匹配该过滤条件，跟 must 子句的唯一区别是，filter 不会对结果进行相关性评分 _score，换言之当我们的业务中无相关性的要求时，建议查询的过程中多用 filter。</p>
<p>下面是一个组合查询的例子，我们要查询 host 为 wenyuanblog.com 且 http_x_forworded_for 为 47.97.12.69 且 status 不为 200 的所有数据。</p>
<div>bash<i></i><i></i></div>
<pre><code>GET /wenyuanblog-2018.03.02/_search
{
  "query": {
    "bool": {
      "filter": [
        {
          "match": {
            "host": "wenyuanblog.com"
          }
        },
        {
          "match": {
            "http_x_forwarded_for": "47.97.12.69"
          }
        }
      ],
      "must_not": {
        "match": {
          "status": 200
        }
      }
    }
  }
}
</code></pre>
<p>这里再说一下 should。通常情况下，should 子句是数组字段，包含多个 should 子查询，默认情况下，匹配的文档必须满足其中一个子查询条件。</p>
<p>但我们可以通过显式设置布尔查询的参数 minimum_should_match 的值，从而改变默认匹配行为。该参数控制一个文档必须匹配的 should 子查询的数量，它有很多种配置方式：</p>
<p>如果设置为数字 3，表示至少需要匹配 3 个 should 子句；如果设置为一个百分比，例如 “minimum_should_match”:75%，则至少满足 75% 且向下取整（5个 should 子句，5*75%=3.75，向下取整为 3，也就是至少匹配 3 个 should 子句）。</p>
<p>下面是个例子。（注：在 bool query 中 minimum_should_match 只能紧跟在 should 的后面，放其他地方会出异常）</p>
<div>bash<i></i><i></i></div>
<pre><code>GET /wenyuanblog-2018.03.02/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "match": {
            "host": "a.wenyuanblog.com"
          }
        },
        {
          "match": {
            "host": "b.wenyuanblog.com"
          }
        },
        {
          "match": {
            "host": "c.wenyuanblog.com"
          }
        }
      ],
      "minimum_should_match": 2
    }
  }
}
</code></pre>
<h3 id="toc-heading-12">9. sort</h3>
<p>sort 是排序，也是很常用的查询，这里我举个按时间（@timestamp）倒叙查询的例子。</p>
<div>bash<i></i><i></i></div>
<pre><code>GET /wenyuanblog-2018.03.02/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "@timestamp": {
        "order": "desc"
      }
    }
  ]
}
</code></pre>
<h2 id="toc-heading-13">四、聚合查询</h2>
<h3 id="toc-heading-14">1. 分桶</h3>
<p>根据 host 字段的值进行分桶（有点类似于 SQL 中的 group by），这里的 host_bucket 是我给该桶起的名字。</p>
<div>bash<i></i><i></i></div>
<pre><code>GET /wenyuanblog-2018.03.02/_search
{
  "query": {
    "match_all": {}
  },
  "aggs": {
    "host_bucket": {
      "terms": {
        "field": "host"
      }
    }
  }
}
</code></pre>
<h3 id="toc-heading-15">2. 度量</h3>
<p>计算出 latency 字段的最大值（metric 有点类似于 SQL 的 avg、max、min），这里的 max_latency 是我给该度量起的名字。</p>
<div>bash<i></i><i></i></div>
<pre><code>GET /wenyuanblog-2018.03.02/_search
{
  "query": {
    "match_all": {}
  },
  "aggs": {
    "max_latency": {
      "max": {
        "field": "latency"
      }
    }
  }
}
</code></pre>
<h2 id="toc-heading-16">五、业务应用</h2>
<p>实际业务中的一些需求，属于较综合的查询语法。</p>
<h3 id="toc-heading-17">1. 聚合结果进行排序</h3>
<p>关键词：aggregations，terms，order。</p>
<p>先过滤出 host 字段值为 “wenyuanblog.com” 的记录，然后对 源IP 进行分桶聚合，最后根据聚合查询到的 文档数量倒序排序。</p>
<div>bash<i></i><i></i></div>
<pre><code>GET /wenyuanblog-2019.05.*/_search
{
  "query": {
    "bool": {
      "filter": [
        {
          "range": {
            "@timestamp": {
              "to": 1557503999000,
              "from": 1557417600000
            }
          }
        },
        {
          "term": {
            "host": "wenyuanblog.com"
          }
        }
      ]
    }
  },
  "from": 0,
  "size": 0,
  "aggregations": {
    "src_ip_bucket": {
      "terms": {
        "field": "http.src_ip.dotted",
        "size": 10,
        "order": {
          "_count": "desc"
        }
      }
    }
  }
}
</code></pre>
<h3 id="toc-heading-18">2. IP范围过滤+分桶+度量+脚本+排序</h3>
<p>关键词：range，aggregations，terms，metric，script，order。</p>
<p>先根据 IP范围 过滤，同时排除指定 host；<br />
接着对 源IP 进行分桶聚合，再计算 进流量、出流量 在该时间段内的总和，用脚本计算出 总流量（进流量+出流量） ，最后根据 总流量（进流量+出流量） 数值大小进行倒序排序。</p>
<p>使用groovy：</p>
<div>bash<i></i><i></i></div>
<pre><code>GET /wenyuanblog-2019.05.*/_search
{
  "query": {
    "bool": {
      "filter": [
        {
          "bool": {
            "must": [
              {
                "range": {
                  "@timestamp": {
                    "to": 1557503999000,
                    "from": 1557417600000
                  }
                }
              },
              {
                "range": {
                  "http.src_ip.dotted": {
                    "to": "10.255.255.255",
                    "from": "10.0.0.0"
                  }
                }
              },
              {
                "bool": {
                  "must_not": [
                    {
                      "terms": {
                        "host": [
                          "a.wenyuanblog.com",
                          "b.wenyuanblog.com"
                        ]
                      }
                    }
                  ]
                }
              }
            ]
          }
        }
      ]
    }
  },
  "from": 0,
  "size": 0,
  "aggs": {
    "appid_bucket": {
      "terms": {
        "field": "http.src_ip.dotted",
        "order": {
          "sum_total_bytes": "desc"
        },
        "size": 100
      },
      "aggs": {
        "sum_in_bytes": {
          "sum": {
            "field": "http.in_bytes"
          }
        },
        "sum_out_bytes": {
          "sum": {
            "field": "http.out_bytes"
          }
        },
        "sum_total_bytes": {
          "sum": {
            "script": {
              "lang": "groovy",
              "inline": "doc['http.in_bytes'].value + doc['http.out_bytes'].value"
            }
          }
        }
      }
    }
  }
}
</code></pre>
<p>使用 painless，修改 script 部分如下：</p>
<div>bash<i></i><i></i></div>
<pre><code>"sum_total_bytes": {
  "sum": {
    "script": {
      "lang": "painless",
      "inline": "doc['tcp.in_bytes'].value + doc['tcp.out_bytes'].value"
    }
  }
}
</code></pre>
<p>以前一直用 groovy，因为比较好用。但最近两年的某个版本开始将其 Deprecation 了，官方推荐使用 painless。<br />
painless 也能实现一些脚本语法，具体可以上官网查询。</p>
<h3 id="toc-heading-19">3. IP范围过滤+聚合过滤+度量</h3>
<p>关键词：range，aggregations，filter，metric。</p>
<p>先根据 IP范围 过滤，然后开始聚合，筛选出 目的端口 是 80、443 的数据，再计算这些数据中（80、443端口） 进流量、出流量 在该时间段内的总和。</p>
<p>至于为什么要在聚合中进行过滤而不是在聚合前就过滤，是因为下面语句只是完整 dsl 的一部分，该查询业务还要同时对其它端口进行聚合操作。</p>
<div>bash<i></i><i></i></div>
<pre><code>GET /wenyuanblog-2019.05.*/_search
{
  "query": {
    "bool": {
      "filter": [
        {
          "bool": {
            "must": [
              {
                "range": {
                  "@timestamp": {
                    "to": 1557503999000,
                    "from": 1557417600000
                  }
                }
              },
              {
                "range": {
                  "http.src_ip.dotted": {
                    "to": "10.255.255.255",
                    "from": "10.0.0.0"
                  }
                }
              }
            ]
          }
        }
      ]
    }
  },
  "from": 0,
  "size": 0,
  "aggs": {
    "dport_80_443": {
      "filter": {
        "bool": {
          "must": [
            {
              "terms": {
                "http.dport": [
                  80,
                  443
                ]
              }
            }
          ]
        }
      },
      "aggs": {
        "sum_in_bytes": {
          "sum": {
            "field": "http.in_bytes"
          }
        },
        "sum_out_bytes": {
          "sum": {
            "field": "http.out_bytes"
          }
        }
      }
    }
  }
}
</code></pre>
<h2 id="toc-heading-20">六、总结</h2>
<p>ES 很强大，它支持的查询有很多，这里我只是列举了平时在 Kibana 中经常调试的 DSL。</p>
<p>还有很多实际业务中经常用到的模糊查询（wildcard、regexp、prefix）、nested 查询、多层聚合等等，基本上都是先封装第三方引擎，然后开发时转成 DSL 并 copy 到 Kibana 进行验证和查错。</p>
<p>更复杂的应用这里就不列举出来了，必要时候官方文档是最好的资料。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.eavea.com/blog/index.php/elasticsearchquerydslrumen.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Elasticsearch索引备份与迁移</title>
		<link>http://www.eavea.com/blog/index.php/elasticsearchsuoyinbeifenyuqianyi.html</link>
		<comments>http://www.eavea.com/blog/index.php/elasticsearchsuoyinbeifenyuqianyi.html#comments</comments>
		<pubDate>Tue, 14 Apr 2020 07:57:23 +0000</pubDate>
		<dc:creator>eavea</dc:creator>
				<category><![CDATA[运维知识]]></category>
		<category><![CDATA[Elasticsearch]]></category>

		<guid isPermaLink="false">http://www.eavea.com/blog/?p=66</guid>
		<description><![CDATA[Elasticsearch索引备份与迁移 总结Elasticsearch各种备份与迁移方案，亲测。 详细记录了 [&#8230;]]]></description>
				<content:encoded><![CDATA[<div>Elasticsearch索引备份与迁移</div>
<blockquote><p>总结Elasticsearch各种备份与迁移方案，亲测。<br />
详细记录了具体步骤。</p></blockquote>
<h2 id="toc-heading-1">一、环境</h2>
<ul>
<li>Ubuntu 14.04/16、04</li>
<li>JDK1.8</li>
<li>Elasticsearch 5.3</li>
<li>Kibana 5.3.2</li>
</ul>
<h2 id="toc-heading-2">二、单节点环境</h2>
<h3 id="toc-heading-3">1. 创建备份存储目录</h3>
<div>bash<i></i><i></i></div>
<pre><code>sudo mkdir /media/es/es_backup
# 该目录要是elasticsearch可访问的，我们的环境一般是elasticsearch用户
chown -R elasticsearch:elasticsearch /media/es/es_backup/
</code></pre>
<h3 id="toc-heading-4">2. 修改elasticsearch.yml文件</h3>
<p>修改 elasticsearch.yml 文件，添加 path.repo 配置。</p>
<div>bash<i></i><i></i></div>
<pre><code>vim elasticsearch.yml
# 增加一行如下：
path.repo: ["/media/es/es_backup"]
</code></pre>
<h3 id="toc-heading-5">3. 重启Elasticsearch</h3>
<h3 id="toc-heading-6">4. 创建仓库</h3>
<p>借助 Kibana 操作。</p>
<div>bash<i></i><i></i></div>
<pre><code># 创建仓库，创建一个名为my_backup的仓库
PUT _snapshot/my_backup
{
  "type": "fs",
  "settings": {
    "location": "/media/es/es_backup",
    "compress": true
  }
}

# 检查仓库是否创建成功（my_backup为之前创建的仓库名称） 
GET _snapshot/my_backup

# 删除仓库
DELETE _snapshot/my_backup
</code></pre>
<h3 id="toc-heading-7">5. 备份索引数据</h3>
<div>bash<i></i><i></i></div>
<pre><code># 给所有索引创建快照
PUT _snapshot/my_backup/snapshot_name        

# 针对具体的index创建快照备份
# 其中my_backup是仓库名称，snapshot_name是快照的名字
# ignore_unavailable在创建快照的过程中会忽略不存在的索引，默认情况下，如果没有设置，在索引不存在的情况下快照请求将会失败
# include_global_state能够防止集群的全局状态被作为快照的一部分存储起来
# 多个索引间不要加空格，否则只会对第一个索引操作（我踩过坑！！！）
PUT _snapshot/my_backup/snapshot_name?wait_for_completion=true
{
  "indices": "index_1,index_2",
  "ignore_unavailable": true,
  "include_global_state": false
}

# 查看指定仓库下所有快照
GET _snapshot/my_backup/_all

# 查看具体某一个快照的信息
GET _snapshot/my_backup/snapshot_name/_status

# 删除快照，要指定仓库名和快照名
# 也能用于取消一个正在进行的快照创建过程，会删除备份了一半的快照
DELETE _snapshot/my_backup/snapshot_name
</code></pre>
<p>这里我取一个仓库名为 my_backup，快照名为 alert_snap，要备份的 index 为 .nina_view 和 cc-zabbix-2018.01.05。</p>
<h3 id="toc-heading-8">6. 将数据迁移到另一个环境</h3>
<div>bash<i></i><i></i></div>
<pre><code># 备份创建好之后，在仓库目录/media/es/es_backup里是这样的：
-rw-r--r-- 1 elasticsearch elasticsearch  317 Jan 22 17:44 index-0
-rw-r--r-- 1 elasticsearch elasticsearch    8 Jan 22 17:44 index.latest
drwxr-xr-x 4 elasticsearch elasticsearch 4096 Jan 22 17:44 indices/
-rw-r--r-- 1 elasticsearch elasticsearch  103 Jan 22 17:44 meta-9KWgeCYtSBqqVrfD42bsNw.dat
-rw-r--r-- 1 elasticsearch elasticsearch  237 Jan 22 17:44 snap-9KWgeCYtSBqqVrfD42bsNw.dat
</code></pre>
<h3 id="toc-heading-9">7. 在目标环境下，重复上面1~4步，建立仓库</h3>
<p>将源集群的备份内容（/media/es/es_backup 里的所有文件），复制到迁移目标的仓库目录里，接下来就是类似批量导入了。</p>
<div>bash<i></i><i></i></div>
<pre><code># 如果索引已经存在目标的集群，需要先关闭索引，恢复数据后再开启
POST /index_name/_close
POST _snapshot/my_backup/snapshot_name/_restore
POST /index_name/_open

# 从快照中恢复指定的索引，并给索引重命名
# indices里面可以写多个索引名，中间用逗号隔开
# rename_pattern可以写完整的索引名，也可以用正则匹配索引名的局部
# rename_replacement将替换rename_pattern中匹配到的局部(如果是正则,不是将整个索引名都替换)
#下面这条语句会把index_1,index_2恢复为restored_index_1和restored_index_2
POST /_snapshot/my_backup/snapshot_1/_restore
{
  "indices": "index_1,index_2",
  "rename_pattern": "index_(.+)",
  "rename_replacement": "restored_index_$1"
}

# 查看恢复的状态
GET _snapshot/my_backup/snapshot_name/_status 
</code></pre>
<h2 id="toc-heading-10">三、集群-多节点环境</h2>
<h3 id="toc-heading-11">1. 创建共享目录</h3>
<p>(todo…我没实施过，感觉挺麻烦的，不太推荐)<br />
使用 sshfs 在 ES 集群中每个节点的相同位置挂载一个共享目录。</p>
<p>假设 ES 集群有三个节点：192.168.1.10，192.168.1.11，192.168.1.12，</p>
<p>我这里共享目录用了 /data/es_backup，ES 备份仓库目录(也是共享目录的挂在点)用了 /mnt/es_backup，可以根据实际情况调整。</p>
<div>bash<i></i><i></i></div>
<pre><code># 在每个节点上安装sshfs
sudo apt-get install sshfs

# 选取其中一个节点的目录(非系统盘)作为共享目录 &lt;/br&gt;
# 假设选择的节点ip为192.168.1.10：
mkdir /data/es_backup
chown -R elasticsearch:elasticsearch /data/es_backup
chmod -R 777 /data/es_backup

# 在每个ES节点的相同位置创建存放ES仓库的目录
mkdir /mnt/es_backup
chown -R elasticsearch:elasticsearch /mnt/es_backup
chmod -R 777 /mnt/es_backup
# 每个节点挂载共享目录，执行同样的操作。$user为用户，一般为root，回车后输入密码，其中的参数-o allow_other允许了其他用户访问这个目录
sshfs $user@192.168.1.10:/data/es_backup /mnt/es_backup -o allow_other
# 如果修改了默认ssh端口, 比如23566, 则可以这样:
sshfs $user@192.168.1.10:/data/es_backup /mnt/es_backup -p 23566 -o allow_other

# 测试运行ES的用户是否有对共享目录的写权限
sudo su - elasticsearch
touch /mnt/es_backup/test.txt

# 修改每个节点的elasticsearch.yml文件，添加path.repo配置
path.repo: ["/mnt/es_backup"]
</code></pre>
<p>接下来的操作和上面单节点一样，重启 es，建立仓库，创建快照等等。</p>
<div>bash<i></i><i></i></div>
<pre><code># 在共享目录下为集群创建共享仓库
PUT _snapshot/my_backup
{
  "type": "fs",
  "settings": {
    "location": "/mnt/es_backup",
    "compress": true
  }
}
# 后面步骤省略...
</code></pre>
<p>最后补充一下：取消挂载共享目录，不要直接删除挂载目录，在每个节点下执行下面命令：</p>
<div>bash<i></i><i></i></div>
<pre><code># 这个命令会断开远程连接同时清空 /mnt/es_backup 目录。
fusermount -u /mnt/es_backup
</code></pre>
<p>类似的实现文件共享的方法还有 nfs 和 samba（支持 window 和 linux 互相访问）。</p>
<h3 id="toc-heading-12">2. 借助第三方工具</h3>
<p>第三方迁移工具，网上看了下，主要有三个：</p>
<ul>
<li>elasticsearch-dump</li>
<li>elasticsearch-exporter</li>
<li><a href="https://elasticsearch.cn/article/78" target="_0" rel="external nofollow noopener noreferrer">elasticsearch-migration</a></li>
</ul>
<p>优点：方便，快速。支持的功能很多(数据迁移、mapping迁移等等)。</p>
<p>缺点：原理是用了 scroll+bulk，百万、千万级别条数的数据可以使用，数据量过大还是老老实实用我上面整理的快照方法吧。<br />
备注：对 ES 操作时候，一些报错没有捕获到并写入 log。比如我们只允许自动创建 <code>cc-</code> 开头的索引，迁移生成的新索引需要改名为别的名字，你会发现迁移完，数据没有进去，这也算是第三方工具的不足之处，不过它应该会不断完善。</p>
<h2 id="toc-heading-13">四、总结</h2>
<p>不管是运维也好，开发也好，对数据的备份和迁移是经常要做的事。针对环境的不同特别是数据量的差异，需要适时选择合适的方式。</p>
<p>有时候也要根据自己的具体需求而定，比如为了辅助自己开发，需要回放数据，那完全可以编写一些简单的脚本来实现。</p>
<p>我写过几个简单的脚本，放在 <a title="GitHub" href="https://github.com/winyuan" target="_0" rel="external nofollow noopener noreferrer">GitHub</a> 上：</p>
<ul>
<li><a href="https://github.com/winyuan/es_replay" target="_0" rel="external nofollow noopener noreferrer">es_replay</a><br />
开发测试用途（数据导出、去重、构造和导入）</li>
<li><a href="https://github.com/winyuan/es_assistant" target="_0" rel="external nofollow noopener noreferrer">es_assistant</a><br />
一些用于协助开发和测试的 es 交互脚本</li>
<li><a href="https://github.com/winyuan/es_monitor" target="_0" rel="external nofollow noopener noreferrer">es_monitor</a><br />
Elaticsearch 部署机的信息采集脚本（这个是很久很久以前写的，那时候刚学 ES，能直接用，功能上没问题，但代码完全可以更优化一下，最近忙着学其他东西，所以一直没去动它。）</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.eavea.com/blog/index.php/elasticsearchsuoyinbeifenyuqianyi.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Elasticsearch部署步骤</title>
		<link>http://www.eavea.com/blog/index.php/elasticsearchbushubuzhou.html</link>
		<comments>http://www.eavea.com/blog/index.php/elasticsearchbushubuzhou.html#comments</comments>
		<pubDate>Tue, 14 Apr 2020 07:50:21 +0000</pubDate>
		<dc:creator>eavea</dc:creator>
				<category><![CDATA[运维知识]]></category>
		<category><![CDATA[Elasticsearch]]></category>

		<guid isPermaLink="false">http://www.eavea.com/blog/?p=64</guid>
		<description><![CDATA[Elasticsearch部署步骤 现在官方已经出到了ES7 beta，不过现在为止用的最多也是比较稳定的是5 [&#8230;]]]></description>
				<content:encoded><![CDATA[<div>Elasticsearch部署步骤</div>
<blockquote><p>现在官方已经出到了ES7 beta，不过现在为止用的最多也是比较稳定的是5.x。<br />
这里我简单记录下针对5.x版本的部署配置过程，暂不考虑优化（后面再总结优化相关的笔记）。</p></blockquote>
<h2 id="toc-heading-1">一、环境</h2>
<ul>
<li>Ubuntu 14.04/16、04</li>
<li>JDK1.8</li>
<li>Elasticsearch 5.6</li>
</ul>
<h2 id="toc-heading-2">二、安装JDK</h2>
<h3 id="toc-heading-3">1. 下载安装</h3>
<p><a href="http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html" target="_0" rel="external nofollow noopener noreferrer">http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html</a></p>
<div><img alt="jdk_download.png" src="https://www.wenyuanblog.com/medias/blogimages/jdk_download.png" data-original="/medias/blogimages/jdk_download.png" /></div>
<p>将刚刚下载的文件拷贝到 /opt 目录下，<br />
解压 jdk 到当前目录，使用命令 <code>tar -zxvf jdk-8u161-linux-x64.tar.gz</code></p>
<p>&nbsp;</p>
<p>修改文件名 <code>mv jdk1.8.0_161 jdk1.8</code><br />
至此，jdk1.8 已经全部安装完成了。</p>
<h3 id="toc-heading-4">2. 配置环境变量</h3>
<p>修改 <code>vim /etc/profile</code> ，最底部添加:</p>
<pre><code># Sun JDK profile
export JAVA_HOME=/opt/jdk1.8
export JRE_HOME=${JAVA_HOME}/jre  
export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib  
export PATH=${JAVA_HOME}/bin:$PATH
</code></pre>
<p>运行 <code>source /etc/profile</code>，使 /etc/profile 文件生效。</p>
<p>使用 <code>java -version</code> 和 <code>javac -version</code> 命令查看 jdk 版本及其相关信息，不会出现 command not found 错误，且显示的版本信息与前面安装的一致。</p>
<p><code>echo $JAVA_HOME</code>, <code>echo $CLASSPATH</code>, <code>echo $PATH</code>，看看自己的配置是否都正确。</p>
<h3 id="toc-heading-5">3. 开机设置</h3>
<p>每次重启服务器的时候，会发现 jdk 版本不正确，或者使用 <code>java -version</code> 显示不是所需版本，使用　<code>javac -version</code> 会出现 command not found 错误。</p>
<p>解决方法是再次运行 <code>source /etc/profile</code>，使 /etc/profile 文件生效。显然每次这么操作很麻烦。</p>
<p>因此我们编辑 <code>vim ~/.bashrc</code>，最后加一句</p>
<pre><code>source /etc/profile
</code></pre>
<h2 id="toc-heading-6">三、安装Elasticsearch</h2>
<h3 id="toc-heading-7">1. 下载安装</h3>
<p>登录官网 <a href="https://www.elastic.co/cn/downloads。" target="_0" rel="external nofollow noopener noreferrer">https://www.elastic.co/cn/downloads。</a></p>
<p>选择下载 elasticsearch，根据需要选择对应的安装包，这里选择 5.6 版本，下载完后得到 elasticsearch-5.6.0.tar.gz</p>
<p>操作系统中创建一个新用户专门用来管理 es：<code>adduser elasticsearch</code></p>
<p>用 elasticsearch 用户将 elasticsearch-5.6.0.tar.gz 拷贝到 /opt 目录下。</p>
<p>解压到当前目录，使用命令 <code>tar -zxvf elasticsearch-5.6.0.tar.gz</code>。</p>
<p>删除压缩文件，使用命令 <code>rm -f elasticsearch-5.6.0.tar.gz</code>。</p>
<p>至此，elasticsearch 安装完成</p>
<h3 id="toc-heading-8">2. ES集群需要改的系统配置</h3>
<p><code>vim /etc/security/limits.conf</code></p>
<pre><code>* soft nofile 65536
* hard nofile 131072
* soft nproc 2048
* hard nproc 4096
* soft memlock unlimited
* hard memlock unlimited
* - nofile 65536

root soft nofile 65536
root hard nofile 131072
root soft nproc 2048
root hard nproc 4096
root soft memlock unlimited
root hard memlock unlimited
root - nofile 65536
</code></pre>
<p><code>vim /etc/sysctl.conf</code></p>
<pre><code>vm.max_map_count=655360
vm.swappiness = 0
</code></pre>
<p>并执行 <code>sysctl -p</code></p>
<p>如果是 supervisor 托管的，修改 /etc/supervisord.conf 中的：<br />
minfd = 65536<br />
并重启 supervisor。</p>
<h3 id="toc-heading-9">3. 修改ES配置文件elasticsearch.yml</h3>
<pre><code>bootstrap.memory_lock: false
bootstrap.system_call_filter: false

cluster.name: elasticsearch
node.name: 192.168.10.201
node.master: true
node.data: true
http.enabled: true
network.host: 0.0.0.0
discovery.zen.ping.unicast.hosts: ["192.168.10.198","192.168.10.201"]

http.port: 9200

script.engine.groovy.inline.update: on
script.engine.groovy.inline.search: on
script.engine.groovy.inline.aggs: on
script.max_compilations_per_minute: 10000

path.data: /data/es

action.auto_create_index: cc-*,sc-*,.nina*,.security,.monitoring*,.watches,.triggered_watches,.watcher-history*

</code></pre>
<p>同时也要指定一下 jvm.options（同级目录下的一个配置文件） 里的 jvm 虚拟机内存。</p>
<h3 id="toc-heading-10">4. ES交给supervisor托管</h3>
<pre><code>[program:elasticsearch]
user = elasticsearch
startsecs = 3
autostart = true
autorestart = true
startretries = 3
stdout_logfile=NONE
stderr_logfile=NONE
environment = JAVA_HOME="/opt/jdk/", ES_JAVA_OPTS = "-Xms8g -Xmx8g"
command = /opt/elasticsearch/bin/elasticsearch
</code></pre>
<h2 id="toc-heading-11">四、安装Kibana</h2>
<h3 id="toc-heading-12">1. 从官网下载和es一样版本的Kibana</h3>
<p>拷贝到 /opt 目录，解压，修改配置文件 kibana.yml。</p>
<pre><code>server.host: "192.168.10.201"
elasticsearch.url: "http://192.168.10.198:9200"
</code></pre>
<h3 id="toc-heading-13">2. Kibana交给supervisor托管</h3>
<pre><code>[program:kibana]
startsecs = 3
autostart = true
autorestart = true
startretries = 3
redirect_stderr = true
command = /opt/kibana/bin/kibana
stdout_logfile = /var/log/kibana.log
</code></pre>
]]></content:encoded>
			<wfw:commentRss>http://www.eavea.com/blog/index.php/elasticsearchbushubuzhou.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Nginx正向代理与反向代理</title>
		<link>http://www.eavea.com/blog/index.php/nginxzhengxiangdailiyufanxiangdaili.html</link>
		<comments>http://www.eavea.com/blog/index.php/nginxzhengxiangdailiyufanxiangdaili.html#comments</comments>
		<pubDate>Tue, 14 Apr 2020 07:48:28 +0000</pubDate>
		<dc:creator>eavea</dc:creator>
				<category><![CDATA[运维知识]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Nginx]]></category>

		<guid isPermaLink="false">http://www.eavea.com/blog/?p=62</guid>
		<description><![CDATA[Nginx正向代理与反向代理 正向代理与反向代理的区别，以及用Nginx配置实现代理服务。 一、正向代理与反向 [&#8230;]]]></description>
				<content:encoded><![CDATA[<div>Nginx正向代理与反向代理</div>
<blockquote><p>正向代理与反向代理的区别，以及用Nginx配置实现代理服务。</p></blockquote>
<h2 id="toc-heading-1">一、正向代理与反向代理</h2>
<h3 id="toc-heading-2">1. 正向代理</h3>
<p>正向代理其实相当于请求的中继，比如说，如果某个网站国内无法访问，也就是被墙了，我可以选择两种方式：<br />
一个是配置代理服务器，第二种就是 VPN。</p>
<p>其实两种技术原理差不太多，如果说我自己访问不到这个网站，而这时可以通过一台可以访问到这个网站的服务器，也就是代理服务器，来进行访问，这台代理服务器，分析我们请求的信息，然后去对应的要去的网站上将内容取回来发给我，这样我就能读到想要访问的网站上的内容了，通过一下图片，可以很清晰的看出其原理：</p>
<div><img alt="forward_proxy.png" src="https://www.wenyuanblog.com/medias/blogimages/forward_proxy.png" width="500" height="300" align="center" data-original="/medias/blogimages/forward_proxy.png" /></div>
<p>&nbsp;</p>
<h3 id="toc-heading-3">2. 反向代理</h3>
<p>反向代理比正向代理更加透明，客户端并不知道访问的是代理服务器，当客户端请求一个网址的时候，会经过反向代理服务器，而这台反向代理服务器，会根据客户端的请求，将请求转发到内网服务器中，内网服务器处理请求并返回结果到反向代理服务器上，通过反向代理服务器，将结果最终返回到客户端，详情如图：</p>
<div><img alt="reverse_proxy.png" src="https://www.wenyuanblog.com/medias/blogimages/reverse_proxy.png" width="500" height="300" align="center" data-original="/medias/blogimages/reverse_proxy.png" /></div>
<p>&nbsp;</p>
<p><strong>简单来说：</strong><br />
正向代理代理的是客户端（如通过代理访问谷歌），而反向代理代理的是服务器（如 Nginx 代理 PHP 服务器）。</p>
<h2 id="toc-heading-4">二、通过SSH反向代理实现内网穿透</h2>
<h3 id="toc-heading-5">1. 背景</h3>
<p>我用学校实验室的一台服务器在跑数据，但是这台服务器没有公网 ip，只有学校内网 ip，故我只能到实验室登录。但有时候，需要在宿舍或者家里远程连接这台服务器。</p>
<p>于是，我就用到了<strong>反向代理</strong>。前提是我有一台公网 ip 的服务器（例如，阿里的学生机，或者 vultr 的 VPS 都可以）。</p>
<p>首先，准备好这两台服务器：</p>
<ul>
<li>学校实验室服务器（SchoolServer）
<ul>
<li>IP：192.168.10.50</li>
<li>ssh端口：22</li>
<li>用户：student</li>
<li>密码：helloworld</li>
</ul>
</li>
<li>我的外网服务器（MyServer）
<ul>
<li>IP：111.13.100.91</li>
<li>ssh端口：22</li>
<li>用户：wenyuanblog</li>
<li>密码：<a href="http://www.wenyuanblog.com/" target="_0">www.wenyuanblog.com</a></li>
</ul>
</li>
</ul>
<h3 id="toc-heading-6">2. 实现原理</h3>
<p>在学校内网服务器（以下简称 SchoolServer）中，通过 SSH 设置反向代理，指向我的公网服务器（以下简称 MyServer，该服务器作为反向代理服务器），之后用客户端连接 MyServer 时，MyServer 会把请求转交给 SchoolServer，从而间接登录学校的内网服务器。</p>
<p><strong>整个过程，我们作为客户端是感知不到代理服务器的存在的。客户端是否能感知代理服务器的存在，是区别正向代理和反向代理的关键。</strong></p>
<p>SSH 参数介绍</p>
<div>bash<i></i><i></i></div>
<pre><code># 反向代理命令
ssh -fCNR

# 正向代理命令
ssh -fCNL
</code></pre>
<p>参数说明</p>
<table>
<thead>
<tr>
<th>参数</th>
<th>含义</th>
</tr>
</thead>
<tbody>
<tr>
<td>-f</td>
<td>后台执行 ssh 指令</td>
</tr>
<tr>
<td>-C</td>
<td>允许压缩数据</td>
</tr>
<tr>
<td>-N</td>
<td>不执行远程指令</td>
</tr>
<tr>
<td>-R</td>
<td>将远程服务器的某个端口转发到本地服务器的指定端口</td>
</tr>
<tr>
<td>-L</td>
<td>将本地服务器的某个端口转发到远程服务器的指定端口</td>
</tr>
<tr>
<td>-p</td>
<td>指定远程服务器的端口</td>
</tr>
</tbody>
</table>
<h3 id="toc-heading-7">3. 内网服务器操作</h3>
<p>登录 SchoolServer，进行如下操作：</p>
<div>bash<i></i><i></i></div>
<pre><code># ssh -N -f -R MyServer端口:127.0.0.1:本地端口 MyServer用户@MyServerIP
ssh -N -f -R 20022:127.0.0.1:22 wenyuanblog@111.13.100.91
</code></pre>
<p>这句命令的意思是，将 MyServer 的 20022 端口转发至 SchoolServer 22 端口，最后是 MyServer 用户名和 IP。<br />
使用 <code>ps aux | grep ssh</code> 来查看是否运行。</p>
<h3 id="toc-heading-8">4. 公网服务器操作</h3>
<p>登录 MyServer，进行如下操作：</p>
<div>bash<i></i><i></i></div>
<pre><code># ssh SchoolServer用户名@127.0.0.1 -p 代理端口 -L MyServerIP:代理端口:127.0.0.1:22
ssh student@127.0.0.1 -p 20022 -L 111.13.100.91:20022:127.0.0.1:22
</code></pre>
<p>执行这条命名，输入 student 用户的密码，现在就已经跳转登录到内网服务器了。</p>
<p>保持当前窗口的登录状态，这时候使用 xshell、beyondcompare，就只要在登录的配置项里 IP 填 MyServer 的 IP，端口填 20022 就好了。</p>
<h2 id="toc-heading-9">三、Nginx配置实现代理服务</h2>
<h3 id="toc-heading-10">1. 背景</h3>
<p>到目前为止，我们都还没有用到 Nginx 的代理服务。那什么时候会需要呢，现在有了如下需求：</p>
<p>在学校内网服务器上跑了一个网页（实验室代码库 GitLab），我们需要在宿舍或者家里访问这个内部网页。</p>
<p>该项目是部署在内网的，所以在内网直接访问不会有问题，而从外网访问当然也就访问不到。</p>
<ul>
<li>学校实验室服务器（SchoolServer）
<ul>
<li>IP：192.168.10.50</li>
<li>页面 url：192.168.10.50:8000</li>
</ul>
</li>
<li>我的外网服务器（MyServer）
<ul>
<li>IP：111.13.100.91</li>
<li>站点域名：<a href="http://www.wenyuanblog.com/" target="_0">www.wenyuanblog.com</a></li>
</ul>
</li>
</ul>
<h3 id="toc-heading-11">2. 实现原理</h3>
<p>还是通过反向代理来实现。</p>
<p>通过 Nginx 实现端口转发，最后可以通过公网服务器的域名+端口号访问学校内网服务器的页面。<br />
即访问 <a href="http://www.wenyuanblog.com:28000/" target="_0">www.wenyuanblog.com:28000</a> 时，将得到 192.168.10.50:8000（实验室代码库）的页面。<br />
当然了，这样配置的话，我不得不记住 28000 这个端口号，显然不方便。</p>
<p>所以下面的配置，我将配置成二级域名的形式，即 gitlab.wenyuanblog.com -&gt; 192.168.10.50:8000</p>
<h3 id="toc-heading-12">3. 内网服务器操作</h3>
<p>登录 SchoolServer，进行如下操作（和二&gt;中一样）：</p>
<div>bash<i></i><i></i></div>
<pre><code># ssh -N -f -R MyServer端口:127.0.0.1:本地端口 MyServer用户@MyServerIP
ssh -N -f -R 28000:127.0.0.1:8000 wenyuanblog@111.13.100.91
</code></pre>
<p>这句命令的意思是，将 MyServer 的 28000 端口转发至 SchoolServer 8000 端口，最后是 MyServer 用户名和 IP。</p>
<p>使用 <code>ps aux | grep ssh</code> 来查看是否运行。</p>
<h3 id="toc-heading-13">4. 公网服务器操作</h3>
<p>登录 MyServer，增加一份 Nginx 的配置文件，我习惯放于 <code>/usr/local/nginx/conf/vhosts</code> 目录下，考虑到是代理的配置文件，我取名为 <code>school.proxy</code>。</p>
<p><code>vim school.proxy</code>，配置如下：</p>
<div>bash<i></i><i></i></div>
<pre><code>upstream school_gitlab {
        server 127.0.0.1:28000;
}
server {
        listen       80;
        server_name   gitlab.wenyuanblog.com;
        location / {
            proxy_pass http://school_gitlab;
            proxy_set_header Host $host;
        }
        access_log  /var/log/nginx/access/school_gitlab.log;
}
</code></pre>
<p>现在，我就可以通过二级域名访问学校实验室的 GitLab 了。</p>
<p>整个过程中，在 MyServer 的数据流向如下：<br />
<strong>二级域名访问</strong> -&gt; <strong>MyServer:80</strong> -&gt; <strong>nginx 分发</strong> -&gt; <strong>MyServer:28000</strong> -&gt; <strong>SchoolServer:8080</strong></p>
<h2 id="toc-heading-14">四、补充：用autossh代替ssh</h2>
<h3 id="toc-heading-15">1. 背景</h3>
<p>其实整个过程到上面为止已经结束了，目前已经通过反向代理实现内网穿透，平时肯定够用了。<br />
如果想追求更进一步的配置，可以继续往下看。</p>
<p>什么是更进一步的配置呢？</p>
<p>在前面二、三两部分内容的「内网服务器操作」时，我们使用 ssh 建立的反向连接其实很不稳定，长时间不使用连接就会自动释放，这个时候就需要使用 autossh。<br />
autossh 的参数与 ssh 的参数是一致的，但是不同的是，在隧道断开的时候，autossh 会自动重新连接而 ssh 不会。</p>
<h3 id="toc-heading-16">2. 步骤修改</h3>
<p>只需改动一个地方，那就是上面二和三里面的「3. 内网服务器操作」，其他操作不变。</p>
<p><strong>现在如下操作：</strong></p>
<p><strong>Step1. 配置公钥和私钥，实现免密码登录</strong>（为后面的脚本做准备）</p>
<ul>
<li>登录内网服务器（SchoolServer）<br />
切换到普通用户 student，不建议用 root。<br />
执行命令：<code>ssh-keygen -t rsa -P ''</code>，直接 ssh-keygen 然后三次回车就可以了。</p>
<ul>
<li><code>-P</code> 表示密码，<code>-P ''</code> 就表示空密码，也可以不用 <code>-P</code> 参数，这样就要三车回车，用 <code>-P</code> 就一次回车。</li>
<li>它在 <code>/home/student</code> 下生成 <code>.ssh</code> 目录，<code>.ssh</code> 下有 <code>id_rsa</code> 和 <code>id_rsa.pub</code>。（这样就生成了公钥/私钥对）</li>
</ul>
</li>
<li>把内网服务器（SchoolServer）下的 <code>id_rsa.pub</code> 复制到公网服务器（MyServer）下<br />
执行命令：<code>scp .ssh/id_rsa.pub wenyuanblog@111.13.100.91:/home/wenyuanblog/id_rsa.pub</code><br />
由于还没有免密码登录的，所以要输入密码。</li>
<li>公网服务器（MyServer）加入内网服务器（SchoolServer）的公钥<br />
登录公网服务器（MyServer），切换到 wenyuanblog 账户（下面的步骤不要用 root）。<br />
如果公网服务器的 wenyuanblog 目录下没有 <code>.ssh</code> 和 <code>authorized_keys</code> 文件则创建这个文件夹和文件。<br />
把从内网服务器复制的 <code>id_rsa.pub</code> 添加到 <code>.ssh/authorzied_keys</code> 文件里，即执行命令：<br />
<code>cat id_rsa.pub &gt;&gt; .ssh/authorized_keys</code><br />
<code>cat .ssh/authorized_keys</code><br />
<code>chmod 644 .ssh/authorized_keys</code></p>
<ul>
<li>authorized_keys 的权限要是 644 。</li>
</ul>
</li>
<li>内网服务器（SchoolServer）登录公网服务器（MyServer）试试<br />
回到内网服务器（SchoolServer），用之前的普通用户 student，执行：<code>ssh wenyuanblog@111.13.100.91</code><br />
第一次登录要 yes，现在内网机器就可以免密码登录公网机器了。</li>
</ul>
<p><strong>Step2. 登录内网服务器，启动 autossh，维持 ssh 连接</strong></p>
<p>上面提过，使用 ssh 建立的反向连接很不稳定，长时间不使用连接就会自动释放，这个时候就需要使用 autossh。</p>
<p>一般新机器需要安装 autossh：<br />
<code>yum install autossh</code>（CentOS）<br />
<code>apt-get install autossh</code>（Ubuntu）</p>
<p>切换到 wenyuanblog 用户（非 root 用户），在其 <code>home</code> 目录下创建一个 <code>autossh.sh</code> 脚本，开辟连接到公网服务器的隧道。<br />
<code>autossh.sh</code> 脚本内写入要开辟的端口：</p>
<div>bash<i></i><i></i></div>
<pre><code>#!/bin/bash
/usr/bin/autossh -NR 0.0.0.0:20022:127.0.0.1:22  -i ~/.ssh/id_rsa wenyuanblog@公网IP -p 22 &gt;&gt; ~/log/ssh_nat.log 2&gt;&amp;1 &amp;
/usr/bin/autossh -NR 0.0.0.0:28000:127.0.0.1:8000 -i ~/.ssh/id_rsa wenyuanblog@公网IP -p 22 &gt;&gt; ~/log/ssh_nat.log 2&gt;&amp;1 &amp;
</code></pre>
<p>这两行就分别代替了上面二和三中「3. 内网服务器操作」里的内容。</p>
<p>运行脚本：<br />
<code>chmod +x autossh.sh</code><br />
<code>sh autossh.sh</code></p>
<p>使用 <code>ps -ef|grep autossh</code> 和 <code>ps -ef|grep ssh</code> 查看当前运行中的 autossh 任务进程。</p>
<p>其余步骤不变，完成。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.eavea.com/blog/index.php/nginxzhengxiangdailiyufanxiangdaili.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Nmap基础用法</title>
		<link>http://www.eavea.com/blog/index.php/nmapjichuyongfa.html</link>
		<comments>http://www.eavea.com/blog/index.php/nmapjichuyongfa.html#comments</comments>
		<pubDate>Tue, 14 Apr 2020 07:45:26 +0000</pubDate>
		<dc:creator>eavea</dc:creator>
				<category><![CDATA[运维知识]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Nmap]]></category>

		<guid isPermaLink="false">http://www.eavea.com/blog/?p=60</guid>
		<description><![CDATA[Nmap基础用法 自己搜集总结的Nmap常用命令及参数说明。 一、简介 NMap，也就是Network Map [&#8230;]]]></description>
				<content:encoded><![CDATA[<div>Nmap基础用法</div>
<blockquote><p>自己搜集总结的Nmap常用命令及参数说明。</p></blockquote>
<h2 id="一、简介">一、简介</h2>
<p>NMap，也就是Network Mapper，是Linux下的网络扫描和嗅探工具包。<br />
nmap是在网络安全渗透测试中经常会用到的强大的扫描器，功能很强大。</p>
<h2 id="二、参数说明">二、参数说明</h2>
<p>-sP 渗透内网之后判断当前网络哪些主机在线</p>
<div>bash<i></i><i></i></div>
<pre><code>nmap -sP 192.168.1/255
</code></pre>
<p>-vv 显示详细的扫描过程<br />
-sS 使用SYN半开式扫描，这种扫描方式使得扫描结果更加正确（又称半开放,或隐身扫描）</p>
<div>bash<i></i><i></i></div>
<pre><code>nmap -vv -sS IP
</code></pre>
<p>-O 大写O代表OS，判断主机操作系统</p>
<div>bash<i></i><i></i></div>
<pre><code>nmap -O IP
</code></pre>
<p>延时策略<br />
-T(0-5) 默认为 3<br />
0 即 Paranoid 模式。为了避开IDS的检测使扫描速度极慢，nmap串所有的扫描，每隔至少5分钟发送一个包。<br />
1 即 Sneaky 模式。也差不多，只是数据包的发送间隔是15秒。<br />
2 即 Polite 模式。不增加太大的网络负载，串行每个探测，并使每个探测间隔0.4秒。<br />
3 即 Normal 模式。nmap的默认选项，在不使网络过载或者主机/端口丢失的情况下尽可能快速地扫描。<br />
4 即 Aggressive 模式。设置5分钟的超时限制，对每台主机的扫描时间不超过5分钟，并且对每次探测回应的等待时间不超过1.5秒。<br />
5 即 lnsane 模式。只适合快速的网络或者不在意丢失默些信息，每台主机的超时限制为75秒，对每次探测只等待0.3秒。</p>
<div>bash<i></i><i></i></div>
<pre><code>nmap -sS -T1 IP
</code></pre>
<p>-sV 探测端口的服务类型/具体版本等信息</p>
<div>bash<i></i><i></i></div>
<pre><code>nmap -vv -sV IP
</code></pre>
<p>-p 端口号 对某个端口的服务版本进行详细探测，有助于升入的针对性攻击，比如缓冲溢出攻击</p>
<div>bash<i></i><i></i></div>
<pre><code>nmap -vv -sV IP -p 21
</code></pre>
<p>适用于内外网的探测，以内网操作为示例（外网参数同）</p>
<div>bash<i></i><i></i></div>
<pre><code>简单端口扫描：nmap -vv -sT(sS、sF、sU、sA) 192.168.0.1 -D 127.0.0.1
（-D 伪造的地址）

OS检测：nmap -vv -sS -O 192.168.0.1

RPC鉴别：nmap -sS -sR 192.168.0.1
Linux 上的 portmap 就是一个简单的 RPC 服务，监听端口为111（默认）

Ping 扫射：nmap -sP 172.16.15.0/24
</code></pre>
<h2 id="三、十条nmap常用的扫描命令">三、十条nmap常用的扫描命令</h2>
<p>1）获取远程主机的系统类型及开放端口</p>
<div>bash<i></i><i></i></div>
<pre><code>nmap -sS -P0 -sV -O &lt;target&gt;
</code></pre>
<p>这里的 &lt; target &gt; 可以是单一IP，或主机名，或域名，或子网<br />
-sS TCP SYN 扫描（又称半开放，或隐身扫描）<br />
-P0 允许你关闭 ICMP pings.<br />
-sV 打开系统版本检测<br />
-O 尝试识别远程操作系统<br />
其它选项：<br />
-A 同时启用操作系统指纹识别和版本检测<br />
-v 详细输出扫描情况<br />
nmap -sS -P0 -A -v</p>
<p>2）列出开放了指定端口的主机列表</p>
<div>bash<i></i><i></i></div>
<pre><code>nmap -sT -p 80 -oG – 192.168.1.* | grep open
</code></pre>
<p>更改-p参数来指定端口号。参考“man nmap”中指定地址范围的不同方法。</p>
<p>3）在网络中寻找所有在线主机</p>
<div>bash<i></i><i></i></div>
<pre><code>nmap -sP 192.168.0.*
# 或者也可用以下命令指定 subnet：
nmap -sP 192.168.0.0/24
</code></pre>
<p>4）Ping指定范围内的 IP 地址</p>
<div>bash<i></i><i></i></div>
<pre><code>nmap -sP 192.168.1.100-254
</code></pre>
<p>nmap接受多种寻址符号、多个目标/范围等。</p>
<p>5）在某段子网上查找未占用的IP</p>
<div>bash<i></i><i></i></div>
<pre><code>nmap -T4 -sP 192.168.2.0/24 &amp;&amp; egrep "00:00:00:00:00:00" /proc/net/arp
</code></pre>
<p>6）在局域网上扫描Conficker蠕虫病毒</p>
<div>bash<i></i><i></i></div>
<pre><code>nmap -PN -T4 -p139,445 -n -v -script=smb-check-vulns -script-args safe=1 192.168.0.1-254
</code></pre>
<p>将192.168.0.1-256替换成你想要检查的IP</p>
<p>7）扫描网络上的恶意接入点（rogue APs）</p>
<div>bash<i></i><i></i></div>
<pre><code>nmap -A -p1-85,113,443,8080-8100 -T4 -min-hostgroup 50 -max-rtt-timeout 2000 -initial-rtt-timeout 300 -max-retries 3 -host-timeout 20m -max-scan-delay 1000 -oA wapscan 10.0.0.0/8
</code></pre>
<p>我用这个扫描命令成功地在一个非常大的网络中找到许多流氓APs。</p>
<p>8）使用诱饵扫描方法来扫描主机端口<br />
在扫描端口时使用诱饵，以避免被系统管理员捕获</p>
<div>bash<i></i><i></i></div>
<pre><code>sudo nmap -sS 192.168.0.10 -D 192.168.0.2
</code></pre>
<p>扫描目标设备/计算机（192.168.0.10）上打开的端口，同时设置一个诱饵地址（192.168.0.2）。这将在目标安全日志中显示诱饵ip地址，而不是您的ip。诱饵地址必须是活的。检查/var/log/secure上的目标安全日志，确保它工作正常。</p>
<p>9）子网的反向DNS记录列表</p>
<div>bash<i></i><i></i></div>
<pre><code>nmap -R -sL 209.85.229.99/27 | awk '{if($3=="not")print"("$2") no PTR";else print$3" is "$2}' | grep '('
</code></pre>
<p>10）显示网络上共有多少台Linux及Win设备</p>
<div>bash<i></i><i></i></div>
<pre><code>sudo nmap -F -O 192.168.1.1-255 | grep "Running: " &gt; /tmp/os; echo "$(cat /tmp/os | grep Linux | wc -l) Linux device(s)"; echo "$(cat /tmp/os | grep Windows | wc -l) Window(s) devices"
</code></pre>
<h2 id="四、nmap命令完整思维导图">四、nmap命令完整思维导图</h2>
]]></content:encoded>
			<wfw:commentRss>http://www.eavea.com/blog/index.php/nmapjichuyongfa.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Linux连接MySQL命令表</title>
		<link>http://www.eavea.com/blog/index.php/linuxlianjiemysqlminglingbiao.html</link>
		<comments>http://www.eavea.com/blog/index.php/linuxlianjiemysqlminglingbiao.html#comments</comments>
		<pubDate>Tue, 14 Apr 2020 07:42:47 +0000</pubDate>
		<dc:creator>eavea</dc:creator>
				<category><![CDATA[运维知识]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[MySQL]]></category>

		<guid isPermaLink="false">http://www.eavea.com/blog/?p=59</guid>
		<description><![CDATA[Linux连接MySQL命令表 Linux环境下通过一些命令快速操作数据库。 bash # 连接mysql（注 [&#8230;]]]></description>
				<content:encoded><![CDATA[<div>Linux连接MySQL命令表</div>
<blockquote><p>Linux环境下通过一些命令快速操作数据库。</p></blockquote>
<div>bash<i></i><i></i></div>
<pre><code># 连接mysql（注：-h、-P、-u、-p后面可以不用加空格）
mysql -h主机地址 -P端口 -u用户名 -p用户密码

# 显示数据库
mysql&gt; show databases;

# 使用某个数据库
mysql&gt; use xxx;

# 查看该数据库中的表
mysql&gt; show tables;

# 显示数据表的结构
mysql&gt; describe 表名;

# 显示表中的记录（注意表名大小写）
mysql&gt; select * from 表名;

# 创建数据库
mysql&gt; create database 数据库名;

# 建表  
use 库名;
create table 表名 （跟上字段列表）;
# 例如：创建表user,表中有id（序号，自增），name（姓名）,gender（性别）,birthday（出身年月）四个字段  
mysql&gt; use blog;  
mysql&gt; create table user (id int(3) auto_increment not null primary key, name char(8),gender char(2),birthday date);
mysql&gt; describe name;

# 往表中增加数据
# 例如：增加几条相关纪录。  
mysql&gt; insert into user values('','张三','男','2019-01-01');  
mysql&gt; insert into user values('','李四','女','2020-06-01');
mysql&gt; select * from name;

# 修改表中数据
# 例如：将张三的出生年月改为2019-06-01
mysql&gt; update user set birthday='2019-01-01' where name='张三';  

# 删除某表中的数据  
# 例如：删除名字是张三的数据。  
mysql&gt; delete from user where name='张三';  

# 删库和删表  
mysql&gt; drop database 库名;
mysql&gt; drop table 表名;

# 退出mysql
mysql&gt; exit (回车)
</code></pre>
]]></content:encoded>
			<wfw:commentRss>http://www.eavea.com/blog/index.php/linuxlianjiemysqlminglingbiao.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Windows环境安装MySQL5.7</title>
		<link>http://www.eavea.com/blog/index.php/windowshuanjinganzhuangmysql5-7.html</link>
		<comments>http://www.eavea.com/blog/index.php/windowshuanjinganzhuangmysql5-7.html#comments</comments>
		<pubDate>Tue, 14 Apr 2020 07:41:17 +0000</pubDate>
		<dc:creator>eavea</dc:creator>
				<category><![CDATA[运维知识]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[MySQL]]></category>

		<guid isPermaLink="false">http://www.eavea.com/blog/?p=57</guid>
		<description><![CDATA[Windows环境安装MySQL5.7 在Windows开发环境下安装MySQL5.7，使用官方免安装软件包， [&#8230;]]]></description>
				<content:encoded><![CDATA[<div>Windows环境安装MySQL5.7</div>
<blockquote><p>在Windows开发环境下安装MySQL5.7，使用官方免安装软件包，手动启停服务和编写批处理脚本实现MySQL启停。</p></blockquote>
<h2 id="toc-heading-1">一、安装</h2>
<h3 id="toc-heading-2">1. 下载免安装软件包</h3>
<p>我比较喜欢免安装版本的，比较干净。<br />
到官网的开发者专区下载MySQL（Community版）免安装软件包，<br />
下载地址：<a href="https://dev.mysql.com/downloads/mysql/" target="_0" rel="external nofollow noopener noreferrer">https://dev.mysql.com/downloads/mysql/</a><br />
* 当前最新版本是8.0，如果需要下载其它版本，可以点击页面中的 Looking for previous GA versions? （未来版本超链接入口可能会有变化）</p>
<div><img alt="mysql_download_in_windows.png" src="https://www.wenyuanblog.com/medias/blogimages/mysql_download_in_windows.png" data-original="/medias/blogimages/mysql_download_in_windows.png" /></div>
<p>&nbsp;</p>
<h3 id="toc-heading-3">2. 解压压缩包</h3>
<p>将下载的软件包解压并重命名到 <code>D:\Program Files\mysql-5.7</code> 目录下。</p>
<div><img alt="mysql_unzip_in_windows.png" src="https://www.wenyuanblog.com/medias/blogimages/mysql_unzip_in_windows.png" data-original="/medias/blogimages/mysql_unzip_in_windows.png" /></div>
<p>&nbsp;</p>
<h3 id="toc-heading-4">3. 进入命令行终端</h3>
<p>以Window10为例，<br />
【开始】-【搜索程序和文件】输入 <code>cmd</code>，在搜索到的程序图标上点击鼠标右键，选择【以管理员身份运行】。</p>
<h3 id="toc-heading-5">4. 进入命令行终端</h3>
<p>依次输入如下指令：</p>
<pre><code>C:\Windows\system32&gt; d:
D:\&gt; cd D:\Program Files\mysql-5.7\bin
</code></pre>
<h3 id="toc-heading-6">5. 初始化MySQL</h3>
<p>初始化可以选择带有随机密码或无密码，建议采用无密码初始化（可以设置密码），以免随机密码丢失。</p>
<pre><code>D:\Program Files\mysql-5.7\bin&gt; mysqld --initialize（随机密码）

D:\Program Files\mysql-5.7\bin&gt; mysqld --initialize-insecure（无密码）
</code></pre>
<h3 id="toc-heading-7">6. 安装mysqld</h3>
<pre><code>D:\Program Files\mysql-5.7\bin&gt; mysqld -install
</code></pre>
<p>* <code>mysql</code> 用于执行SQL命令，<code>mysqld</code> 用于执行数据库命令。</p>
<h3 id="toc-heading-8">7. 启动MySQL服务</h3>
<pre><code>D:\Program Files\mysql-5.7\bin&gt; net start mysql
</code></pre>
<div><img alt="mysql_start_in_windows.png" src="https://www.wenyuanblog.com/medias/blogimages/mysql_start_in_windows.png" data-original="/medias/blogimages/mysql_start_in_windows.png" /></div>
<p>&nbsp;</p>
<h3 id="toc-heading-9">8. 登录MySQL</h3>
<pre><code>D:\Program Files\mysql-5.7\bin&gt; mysql -u root -p
Enter password:（无密码则直接回车）
</code></pre>
<h2 id="toc-heading-10">二、添加root账号登录密码</h2>
<h3 id="toc-heading-11">1. 停止MySQL服务</h3>
<pre><code>D:\Program Files\mysql-5.7\bin&gt; net stop mysql
</code></pre>
<h3 id="toc-heading-12">2. 跳过密码验证</h3>
<pre><code>D:\Program Files\mysql-5.7\bin&gt; mysqld --console --skip-grant-tables
</code></pre>
<div><img alt="mysql_skip_grant_tables_in_windows.png" src="https://www.wenyuanblog.com/medias/blogimages/mysql_skip_grant_tables_in_windows.png" data-original="/medias/blogimages/mysql_skip_grant_tables_in_windows.png" /></div>
<p>此时，当前命令行终端窗口不能再输入命令了，我们需要再打开一个命令行窗口，进入 <code>D:\Program Files\mysql-5.7\bin&gt;</code>，方式和前面一样，即输入下面两行命令：</p>
<p>&nbsp;</p>
<pre><code>C:\Windows\system32&gt; d:
D:\&gt; cd D:\Program Files\mysql-5.7\bin
</code></pre>
<h3 id="toc-heading-13">3. 登录MySQL</h3>
<pre><code>D:\Program Files\mysql-5.7\bin&gt; mysql -u root -p
Enter password:（无需密码直接回车）
</code></pre>
<h3 id="toc-heading-14">4. 切换数据库</h3>
<pre><code>mysql&gt; use mysql;
</code></pre>
<h3 id="toc-heading-15">5. 执行修改密码的语句</h3>
<p>当前新版本的MySQL有密码规则：必须包含大写字母、小写字母、特殊符号并且8位以上。<br />
我们这边是强行修改字段的，会有警告，但现在是开发环境，可以不管它，就把密码设置成 <code>123456</code> 即可。</p>
<pre><code>mysql&gt; update mysql.user set authentication_string=password('123456') where user='root';
</code></pre>
<h3 id="toc-heading-16">6. 刷新权限并退出MySQL</h3>
<pre><code>mysql&gt; flush privileges;
mysql&gt; quit;
</code></pre>
<h3 id="toc-heading-17">7. 终止mysqld</h3>
<p>回到第一个命令行窗口，按 “Ctrl+C” 结束进程或者按 “Ctrl+Alt+Del” 打开任务管理器，结束“mysqld.exe”的进程。</p>
<h3 id="toc-heading-18">8. 重新启动MySQL服务</h3>
<pre><code>D:\Program Files\mysql-5.7\bin&gt; net start mysql
</code></pre>
<h3 id="toc-heading-19">9. 登录MySQL</h3>
<pre><code>D:\Program Files\mysql-5.7\bin&gt; mysql -u root -p
Enter password:123456
</code></pre>
<p>此时必须使用刚添加的密码才能登录MySQL。</p>
<h2 id="toc-heading-20">三、编写批处理脚本实现MySQL启停</h2>
<p>在前面安装完MySQL后，我们每次开机都是通过输入命令来切换目录、启停MySQL的，长期使用显然比较麻烦，为了偷懒，下面编写一个批处理脚本。</p>
<h3 id="toc-heading-21">1. 系统环境变量配置</h3>
<p>（1）新增系统变量<br />
变量名：MYSQL_HOME<br />
变量值：MySQL解压目录，如 D:\Program Files\mysql-5.7<br />
<img alt="mysql_global_config_system_variable.png" src="https://www.wenyuanblog.com/medias/blogimages/mysql_global_config_system_variable.png" data-original="/medias/blogimages/mysql_global_config_system_variable.png" /><br />
（2）追加PATH<br />
%MYSQL_HOME%\bin<br />
<img alt="mysql_global_config_path.png" src="https://www.wenyuanblog.com/medias/blogimages/mysql_global_config_path.png" data-original="/medias/blogimages/mysql_global_config_path.png" /></p>
<h3 id="toc-heading-22">2. 编写批处理脚本</h3>
<p>创建一个文件，并以 <code>.bat</code> 为后缀，如 <code>mysql.bat</code>，打开编辑，输入如下内容。输入后以管理员身份运行。（前提要MySQL已经安装或配置成功）</p>
<pre><code>cls 
@echo off
:设置窗口字体颜色
color 0F 
:设置窗口标题
TITLE MySQL管理程序 by winyuan

call :checkAdmin

goto menu
:菜单
:menu
cls
echo. 
echo.========= 请选择您要对MySQL的操作 =========
echo.
echo.1: 启动MySQL
echo.
echo.2: 关闭MySQL
echo. 
echo.3: 重启MySQL
echo. 
echo.4: 退 出
echo.
echo.========= 请输入您要选择的项目序号 =========
set /p id=
if "%id%"=="1" goto startup
if "%id%"=="2" goto shutdown
if "%id%"=="3" goto reboot
if "%id%"=="4" exit
pause

:启动
:startup
echo.
call :checkMySQL 1
echo.启动MySQL......
net start "MySQL"
echo.启动MySQL成功！
pause 
goto menu 

:停止
:shutdown
echo.
call :checkMySQL 2
echo.关闭MySQL......
net stop "MySQL"
echo.关闭MySQL成功！
pause 
goto menu

:重启
:reboot
echo.
call :checkMySQL 2
echo.关闭MySQL......
net stop "MySQL"
echo.关闭MySQL成功！
goto startup
goto menu

:退出
:goout
pause
goto menu

:检查MySQL进程是否存在
:checkMySQL
set /a count=0
for /f "tokens=1 delims= " %%i in ('tasklist /nh ^| find /i "MySQL"') do (set /a count+=1)
if %count% neq 0 if "%1" equ "1" (
  echo 警告：MySQL已启动
  goto goout
)
if %count% equ 0 if "%1" equ "2" (
  echo 警告：MySQL未启动
  goto goout
)

:检查是否是以管理员身份运行
:checkAdmin
echo test am i admin? &gt; %SystemRoot%\System32\wenyuanblog.txt
if not exist %SystemRoot%\System32\wenyuanblog.txt (
  echo 警告：请以管理员身份运行！
  pause
  exit
)
del %SystemRoot%\System32\wenyuanblog.txt
</code></pre>
<p>最后，用管理员身份运行 <code>mysql.bat</code>，如下图所示：<br />
<img alt="mysql_batch_script_in_windows.png" src="https://www.wenyuanblog.com/medias/blogimages/mysql_batch_script_in_windows.png" data-original="/medias/blogimages/mysql_batch_script_in_windows.png" /><br />
参考<br />
django中文网</p>
]]></content:encoded>
			<wfw:commentRss>http://www.eavea.com/blog/index.php/windowshuanjinganzhuangmysql5-7.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>【Python实战】实时获取tcpdump输出</title>
		<link>http://www.eavea.com/blog/index.php/pythonshizhanshishihuoqutcpdumpshuchu.html</link>
		<comments>http://www.eavea.com/blog/index.php/pythonshizhanshishihuoqutcpdumpshuchu.html#comments</comments>
		<pubDate>Tue, 14 Apr 2020 07:37:49 +0000</pubDate>
		<dc:creator>eavea</dc:creator>
				<category><![CDATA[后端技术]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[抓包]]></category>

		<guid isPermaLink="false">http://www.eavea.com/blog/?p=56</guid>
		<description><![CDATA[【Python实战】实时获取tcpdump输出 Python实时获取tcpdump输出。 一、背景 今天有个小 [&#8230;]]]></description>
				<content:encoded><![CDATA[<div>【Python实战】实时获取tcpdump输出</div>
<blockquote><p>Python实时获取tcpdump输出。</p></blockquote>
<h2 id="toc-heading-1">一、背景</h2>
<p>今天有个小需求，要确认客户端有没有往服务端发送udp包，但为了减轻工作量，不想每次到机器上手动执行tcpdump抓包命令。<br />
于是就写了个脚本来释放人力。</p>
<h2 id="toc-heading-2">二、代码实现</h2>
<p>整个脚本我还加了一些其他功能：时间戳、发送端IP提取，数据包分析，数据持久化等。这里都先去掉，仅记录下简单的实时获取tcpdump输出功能。<br />
代码如下：</p>
<div>python<i></i><i></i></div>
<pre><code># -*- coding: utf-8 -*-
# !/usr/bin/env python

# sudo tcpdump -tt -l -nn -c 5 -i enp4s0 udp port 514 or 51414

import subprocess

cmd = ['sudo', 'tcpdump', '-tt', '-l', '-nn', '-c', '5', '-i', 'enp4s0', 'udp', 'port', '514', 'or', '51414']
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)

while True:
    line = proc.stdout.readline()
    line = line.strip()
    if not line:
        print('tcpdump finished...')
        break
    print(line)
</code></pre>
<p>输出如下（实时）：</p>
<div>bash<i></i><i></i></div>
<pre><code>wenyuanblog@localhost:/home/test/script# python tcpdump_udp.py 
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on enp4s0, link-type EN10MB (Ethernet), capture size 262144 bytes
1499774951.124178 IP 192.168.10.210.41974 &gt; 192.168.10.251.514: UDP, length 139
1499774953.125664 IP 192.168.10.210.54995 &gt; 192.168.10.251.51414: UDP, length 139
1499774956.128498 IP 192.168.10.210.56748 &gt; 192.168.10.251.514: UDP, length 139
1499774958.129918 IP 192.168.10.210.53883 &gt; 192.168.10.251.51414: UDP, length 139
1499774961.132921 IP 192.168.10.210.58803 &gt; 192.168.10.251.514: UDP, length 139
5 packets captured
6 packets received by filter
0 packets dropped by kernel
tcpdump finished...
</code></pre>
<p>以上代码相当于手动执行了 <code>sudo tcpdump -tt -l -nn -c 5 -i enp4s0 udp port 514 or 51414</code> 这条命令。<br />
注意参数-l很重要（行显）。</p>
<h2 id="toc-heading-3">三、代码实现（更新）</h2>
<p>上面的代码能实现tcpdump的功能，但是有一个问题：没有做超时保护。即当程序执行时间过长时kill该进程（这里使用ctrl+c的方式）。<br />
要实现这个功能有很多种方案，例如定时器+多线程等，这里仅演示一种方案，代码如下：</p>
<div>python<i></i><i></i></div>
<pre><code># -*- coding: utf-8 -*-
# !/usr/bin/env python

# sudo tcpdump -tt -l -nn -c 50 -i enp4s0 udp port 514 or 51414

import subprocess
import signal
import time
import os
import re
import json

class CmdServer:

    def __init__(self, cmd, timeout=120):
        '''
        :param cmd: 执行命令（列表形式）
        :param timeout: 任务超时时间（seconds，进程运行超过该时间，kill该进程）
        :param taskname: 任务名称（根据该任务名称记录命令输出信息）
        '''
        self.cmd = cmd
        self.timeout = timeout
        self.base_path = reduce(lambda x, y: os.path.dirname(x), range(1), os.path.abspath(__file__))
        self.output_path = os.path.join(self.base_path, 'data.json')
        self.udp_flow_list = []
        self.begin_time = int(time.time())

    # 执行tcpdump任务
    def run(self):
        if os.path.exists(self.output_path):
            with open(self.output_path, 'r') as f:
                self.udp_flow_list = json.load(f)

        proc = subprocess.Popen(self.cmd, stdout=subprocess.PIPE)
        stdout = ''

        while proc.poll() == None:
            current_time = int(time.time())
            if current_time - self.begin_time &gt;= self.timeout:
                print('tcpdump timeout...')
                proc.send_signal(signal.SIGINT)
                stdout = proc.stdout.read()

        if proc.poll() is not None and not stdout:
            print('tcpdump finished...')
            stdout = proc.stdout.read()

        stdout_list = stdout.split('\n')
        if stdout_list:
            self._merge_data(stdout_list)
            self._save_data()

    # 数据合并（新增/更新）
    def _merge_data(self, stdout_list):
        for line in stdout_list:
            line = line.strip()
            if not line:
                continue
            timestamp = int(float(line.split('IP')[0].strip())) * 1000
            # 源
            src_ip_port_list = re.findall(r'IP(.+?)&gt;', line)
            if not src_ip_port_list:
                continue
            src_ip_port_str = src_ip_port_list[0].strip()
            src_ip = '.'.join(src_ip_port_str.split('.')[0:4])
            # 目的
            dst_ip_port_list = re.findall(r'&gt;(.+?):', line)
            if not dst_ip_port_list:
                continue
            dst_ip_port_str = dst_ip_port_list[0].strip()
            dst_port = dst_ip_port_str.split('.')[-1]

            # 新增/更新latest_timestamp
            src_item = filter(lambda x: src_ip == x['src_ip'], self.udp_flow_list)
            if src_item:
                src_item[0]['dst_port'] = dst_port
                src_item[0]['latest_timestamp'] = timestamp
            else:
                self.udp_flow_list.append(dict(
                    src_ip=src_ip,
                    dst_port=dst_port,
                    latest_timestamp=timestamp
                ))

    # 保存数据
    def _save_data(self):
        # 写入文件
        with open(self.output_path, 'w') as f:
            json.dump(self.udp_flow_list, f, encoding="utf-8", ensure_ascii=False)

if __name__ == '__main__':
    cmd = ['sudo', 'tcpdump', '-tt', '-l', '-nn', '-c', '5', '-i', 'enp4s0', 'udp', 'port', '514', 'or', '51414']
    cmd_server = CmdServer(cmd, 10)
    cmd_server.run()
</code></pre>
<h2 id="toc-heading-4">四、总结</h2>
<p>比较简单，仅仅是记录下。如果想基于Python的tcpdump做一些业务上的逻辑，可以参考下面的“参考链接”。<br />
参考<br />
<a href="https://blog.csdn.net/wskzgz/article/details/83822780" target="_0" rel="external nofollow noopener noreferrer">https://blog.csdn.net/wskzgz/article/details/83822780</a><br />
<a href="https://blog.csdn.net/wangqiuyun/article/details/46966839" target="_0" rel="external nofollow noopener noreferrer">https://blog.csdn.net/wangqiuyun/article/details/46966839</a><br />
<a href="http://www.cnblogs.com/idvcn/p/8716066.html" target="_0" rel="external nofollow noopener noreferrer">http://www.cnblogs.com/idvcn/p/8716066.html</a><br />
<a href="https://blog.csdn.net/kobeyan/article/details/4344192" target="_0" rel="external nofollow noopener noreferrer">https://blog.csdn.net/kobeyan/article/details/4344192</a><br />
<a href="https://blog.csdn.net/xhw88398569/article/details/48022967" target="_0" rel="external nofollow noopener noreferrer">https://blog.csdn.net/xhw88398569/article/details/48022967</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.eavea.com/blog/index.php/pythonshizhanshishihuoqutcpdumpshuchu.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>【Python实战】单变量异常值检测</title>
		<link>http://www.eavea.com/blog/index.php/pythonshizhandanbianliangyichangzhijiance.html</link>
		<comments>http://www.eavea.com/blog/index.php/pythonshizhandanbianliangyichangzhijiance.html#comments</comments>
		<pubDate>Tue, 14 Apr 2020 07:36:20 +0000</pubDate>
		<dc:creator>eavea</dc:creator>
				<category><![CDATA[后端技术]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[数据处理]]></category>

		<guid isPermaLink="false">http://www.eavea.com/blog/?p=55</guid>
		<description><![CDATA[【Python实战】单变量异常值检测 异常值检测是数据预处理阶段重要的环节，这篇文章介绍下对于单变量异常值检测 [&#8230;]]]></description>
				<content:encoded><![CDATA[<div>【Python实战】单变量异常值检测</div>
<blockquote><p>异常值检测是数据预处理阶段重要的环节，这篇文章介绍下对于单变量异常值检测的常用方法，通过Python代码实现。</p></blockquote>
<h2 id="toc-heading-1">一、什么是异常值</h2>
<p>异常值是在数据集中与其他观察值有很大差距的数据点，它的存在，会对随后的计算结果产生不适当的影响，因此检测异常值并加以适当的处理是十分必要的。</p>
<h2 id="toc-heading-2">二、异常值的处理</h2>
<p>异常值并不都是坏的，了解这一点非常重要。只是简单地从数据中删除异常值，而不考虑它们如何影响结果的话，可能会导致灾难。</p>
<blockquote><p>“异常值不一定是坏事。这些只是与其他模式不一致的观察。但事实上异常值非常有趣。例如，如果在生物实验中，某只老鼠没有死亡而其他老鼠都死了，去了解为什么将会非常有趣。这可能会带来新的科学发现。因此，检测异常值非常重要。” —— Pierre Lafaye de Micheaux，统计师</p></blockquote>
<p>对于异常值，一般有如下几种处理：</p>
<ul>
<li>删除含有异常值的记录（是否删除根据实际情况考虑）</li>
<li>将异常值视为缺失值，利用缺失值的处理方法进行处理</li>
<li>平均值修正（前后两个观测值的平均值）</li>
<li>不处理（直接在具有异常值的数据集上进行挖掘）</li>
</ul>
<h2 id="toc-heading-3">三、异常值的类型</h2>
<p>异常值有两种类型：单变量和多变量（Univariate and Multivariate）。单变量异常值是仅由一个变量中的极值组成的数据点，而多变量异常值是至少两个变量的组合异常分数。假设您有三个不同的变量 &#8211; X，Y，Z。如果您在三维空间中绘制这些变量的图形，它们应该形成一种云。位于此云之外的所有数据点都将是多变量异常值。</p>
<p>举个例子：做客户分析，发现客户的年平均收入是80万美元。但是，有两个客户的年收入是4美元和420万美元。这两个客户的年收入明显不同于其他人，那这两个观察结果将被视为异常值，并且是单变量异常值，当我们看到单变量的分布时，可以找到这些异常值。</p>
<p>再举个例子：身高和体重之间的关系。我们对“身高”和“体重”有单变量和双变量分布，如下图所示。</p>
<div><img alt="height_weight_outliers.png" src="https://www.wenyuanblog.com/medias/blogimages/height_weight_outliers.png" data-original="/medias/blogimages/height_weight_outliers.png" /></div>
<p>看箱线图（box plot后面会介绍）没有任何异常值，再看散点图（scatter plot），有两个值在一个特定的身高和体重的平均值以下。可见多变量异常值是n维空间中的异常值，必须通过多维度的分布才能体现出来。</p>
<p>&nbsp;</p>
<p>如果对异常值不太了解，可以阅读这篇<a title="数据探索指南" href="https://www.analyticsvidhya.com/blog/2016/01/guide-data-exploration/?utm_source=outlierdetectionpyod&amp;utm_medium=blog" target="_0" rel="external nofollow noopener noreferrer">《数据探索指南》</a>，上述部分解释也是从中摘录的。<br />
下面，我主要记录下单变量异常值检测的Python实现。</p>
<h2 id="toc-heading-4">四、常用异常检测方法</h2>
<p>原则上模拟数据集需要样本量足够大，这里仅是演示算法，所以就手动写了有限的样本。<br />
异常值的测量标准有很多，比较常见的是描述性统计法、三西格玛法（3σ法）、箱线图等：</p>
<h3 id="toc-heading-5">1. 描述性统计</h3>
<p>基于常识或经验，假定我们认为大于10的数值是不符合常理的。</p>
<p>下面用Python代码实现用描述性统计求异常值：</p>
<pre><code># -*- coding: utf-8 -*-

data = [2.78, 1.79, 4.73, 3.81, 2.78, 1.80, 4.81, 2.79, 1.78, 3.32, 10.8, 100.0]
threshold = 10

# 定义描述性统计识别异常值函数
def descriptive_statistics(data):
    return list(filter(lambda x: x &gt; threshold, data))

outliers = descriptive_statistics(data)
print('异常值共有：{0}个，分别是：{1}'.format(len(outliers), outliers))

# 输出：异常值共有：2个，分别是：[10.8, 100.0]
</code></pre>
<h3 id="toc-heading-6">2. 三西格玛（3σ）</h3>
<p>当数据服从正态分布时，99%的数值应该位于距离均值3个标准差之内的距离，P(|x−μ|&gt;3σ)≤0.003，当数值超出这个距离，可以认为它是异常值。<br />
正态分布状况下，数值分布表：</p>
<table>
<thead>
<tr>
<th>数值分布</th>
<th>在数据中的占比</th>
</tr>
</thead>
<tbody>
<tr>
<td>(μ-σ,μ+σ)</td>
<td>0.6827</td>
</tr>
<tr>
<td>(μ-2σ,μ+2σ)</td>
<td>0.9545</td>
</tr>
<tr>
<td>(μ-3σ,μ+3σ)</td>
<td>0.9973</td>
</tr>
</tbody>
</table>
<p>注：在正态分布中σ代表标准差，μ代表均值，x=μ为图形的对称轴</p>
<p>下面用Python代码实现用三西格玛求异常值：</p>
<pre><code># -*- coding: utf-8 -*-

import pandas as pd
import numpy as np

data = [2.78, 1.79, 4.73, 3.81, 2.78, 1.80, 4.81, 2.79, 1.78, 3.32, 10.8, 100.0]

# 定义3σ法则识别异常值函数
def three_sigma(data_series):
    rule = (data_series.mean() - 3 * data_series.std() &gt; data_series) | (data_series.mean() + 3 * data_series.std() &lt; data_series)
    index = np.arange(data_series.shape[0])[rule]
    outliers = data_series.iloc[index]
    return outliers.tolist()

data_series = pd.Series(data)
outliers = three_sigma(data_series)
print('异常值共有：{0}个，分别是：{1}'.format(len(outliers), outliers))

# 输出：异常值共有：1个，分别是：[100.0]
</code></pre>
<h3 id="toc-heading-7">3. 箱线图（box plot）</h3>
<p>和3σ原则相比，箱线图依据实际数据绘制，真实、直观地表现出了数据分布的本来面貌，且没有对数据作任何限制性要求（3σ原则要求数据服从正态分布或近似服从正态分布）。<br />
其判断异常值的标准以四分位数和四分位距为基础。四分位数给出了数据分布的中心、散布和形状的某种指示，具有一定的鲁棒性，即25%的数据可以变得任意远而不会很大地扰动四分位数，所以异常值通常不能对这个标准施加影响。鉴于此，箱线图识别异常值的结果比较客观，因此在识别异常值方面具有一定的优越性。<br />
箱线图提供了识别异常值的一个标准，即：<br />
上界 = Q3 + 1.5IQR<br />
下界 = Q1 &#8211; 1.5IQR<br />
小于下界或大于上界的值即为异常值。<br />
其中，<br />
Q3称为上四分位数（75%），表示全部观察值中只有四分之一的数据取值比它大；<br />
Q1称为下四分位数（25%），表示全部观察值中只有四分之一的数据取值比它小；<br />
IQR称为四分位数差，这里就是 Q3-Q1；<br />
1.5其实是个参数λ，这个参数通常取1.5（类似于正态分布中的μ±λ）</p>
<p>文字描述可能比较绕，下面用图片来解释下。<br />
<img alt="box_plot.png" src="https://www.wenyuanblog.com/medias/blogimages/box_plot.png" data-original="/medias/blogimages/box_plot.png" /></p>
<blockquote><p>第一四分位数 (Q1)，又称“较小四分位数”，等于该样本中所有数值由小到大排列后第25%的数字。<br />
第二四分位数 (Q2)，又称“中位数”，等于该样本中所有数值由小到大排列后第50%的数字。<br />
第三四分位数 (Q3)，又称“较大四分位数”，等于该样本中所有数值由小到大排列后第75%的数字。<br />
Q3与Q1的差距又称四分位距（InterQuartile Range,IQR）。</p></blockquote>
<p>四分位数的计算参见<a href="https://baike.baidu.com/item/四分位数" target="_0" rel="external nofollow noopener noreferrer">四分位数</a>和<a href="https://blog.csdn.net/ningyanggege/article/details/82625947" target="_0" rel="external nofollow noopener noreferrer">四分位数的计算</a></p>
<p>下面用Python代码实现用箱线图求异常值：</p>
<pre><code># -*- coding: utf-8 -*-

import pandas as pd

data = [2.78, 1.79, 4.73, 3.81, 2.78, 1.80, 4.81, 2.79, 1.78, 3.32, 10.8, 100.0]

# 定义箱线图识别异常值函数
def box_plot(data_series):
    q_abnormal_low = data_series.quantile(0.25) - 1.5 * (data_series.quantile(0.75) - data_series.quantile(0.25))
    q_abnormal_up = data_series.quantile(0.75) + 1.5 * (data_series.quantile(0.75) - data_series.quantile(0.25))
    index = (data_series &lt; q_abnormal_low) | (data_series &gt; q_abnormal_up)
    outliers = data_series.loc[index]
    return outliers.tolist()

sorted_data = sorted(data)
data_series = pd.Series(sorted_data)
outliers = box_plot(data_series)
print('异常值共有：{0}个，分别是：{1}'.format(len(outliers), outliers))

# 输出：异常值共有：2个，分别是：[10.8, 100.0]
</code></pre>
<h2 id="toc-heading-8">五、总结</h2>
<p>以上是最基础的几种单变量异常值检测方法，没有最好的，只有对当前数据场景最合适的。<br />
后期如果涉及机器学习的数据预处理，我会继续学习和研究多变量异常值的检测，相信会有更有意思的一些算法等着我去学习。<br />
参考<br />
异常检测的N种方法，阿里工程师都盘出来了：<a href="https://mp.weixin.qq.com/s/w7SbAHxZsmHqFtTG8ZAXNg" target="_0" rel="external nofollow noopener noreferrer">https://mp.weixin.qq.com/s/w7SbAHxZsmHqFtTG8ZAXNg</a><br />
数据探索指南：<a href="https://www.analyticsvidhya.com/blog/2016/01/guide-data-exploration/?utm_source=outlierdetectionpyod&amp;utm_medium=blog" target="_0" rel="external nofollow noopener noreferrer">https://www.analyticsvidhya.com/blog/2016/01/guide-data-exploration/?utm_source=outlierdetectionpyod&amp;utm_medium=blog</a><br />
Tukey method：<a href="https://en.wikipedia.org/wiki/Tukey%27s_range_test" target="_0" rel="external nofollow noopener noreferrer">https://en.wikipedia.org/wiki/Tukey%27s_range_test</a><br />
图基（Tukey）检验：一种值得倍加推崇的检验方法：<a href="http://blog.sina.com.cn/s/blog_60be90250100eojy.html" target="_0" rel="external nofollow noopener noreferrer">http://blog.sina.com.cn/s/blog_60be90250100eojy.html</a><br />
如何从大量数据中找出异常值：<a href="https://blog.csdn.net/wangyangzhizhou/article/details/83854951" target="_0" rel="external nofollow noopener noreferrer">https://blog.csdn.net/wangyangzhizhou/article/details/83854951</a><br />
概率论与数理统计 第四版 浙江大学出版社：<a href="https://book.douban.com/subject/3165271/" target="_0" rel="external nofollow noopener noreferrer">https://book.douban.com/subject/3165271/</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.eavea.com/blog/index.php/pythonshizhandanbianliangyichangzhijiance.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
