<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[運維筆記]]></title><description><![CDATA[LINUX || OPS || DEV]]></description><link>https://tomme.me/</link><image><url>https://tomme.me/favicon.png</url><title>運維筆記</title><link>https://tomme.me/</link></image><generator>Ghost 2.6</generator><lastBuildDate>Mon, 13 Apr 2026 03:26:46 GMT</lastBuildDate><atom:link href="https://tomme.me/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[vim 配置分享]]></title><description><![CDATA[<h3 id="">前言</h3>
<p>Vim 厲害的地方有三個:</p>
<ol>
<li>各種快捷件, 熟練的話可以達到人機合一的境界.</li>
<li>各種神器插件, 功力夠的話也可以自己寫. 沒有做不到, 只有想不到.</li>
<li>運行在終端裡面 (廢話), 不過這滿重要的, 本人離不開終端R.</li>
</ol>
<p>本來想寫個<code>vim推坑指南</code>的, 但是想想這寫下來應該會不少內容..<br>
總而言之先分享 vim 配置文件吧~</p>
<hr>
<h3 id="">效果預覽</h3>
<p>先曬一張圖.</p>
<p><img src="https://tomme.me/content/images/2019/09/Snipaste_2019-09-14_04-17-25.png" alt="Snipaste_2019-09-14_04-17-25"></p>
<hr>
<h3 id="vim">配置vim</h3>
<h4 id="vundle">下載插件管理器: Vundle</h4>
<p><a href="https://github.com/VundleVim/Vundle.vim">Vundle</a> 是vim的插件管理器(之一), 使用以下指令安裝 <code>git clone https://github.com/VundleVim/Vundle.vim.git ~/.vim/bundle/Vundle.vim</code></p>
<h4 id="vimrc">配置文件 .vimrc</h4>
<p>貼上以下配置文件, 路徑為 <code>~/.vimrc</code>; Windows用戶是在 <code>%HOMEPATH%\_vimrc</code></p>]]></description><link>https://tomme.me/lets-vim/</link><guid isPermaLink="false">5d7baace484653612bf71b73</guid><category><![CDATA[Vim]]></category><dc:creator><![CDATA[Tommy Lin]]></dc:creator><pubDate>Fri, 13 Sep 2019 20:22:00 GMT</pubDate><content:encoded><![CDATA[<h3 id="">前言</h3>
<p>Vim 厲害的地方有三個:</p>
<ol>
<li>各種快捷件, 熟練的話可以達到人機合一的境界.</li>
<li>各種神器插件, 功力夠的話也可以自己寫. 沒有做不到, 只有想不到.</li>
<li>運行在終端裡面 (廢話), 不過這滿重要的, 本人離不開終端R.</li>
</ol>
<p>本來想寫個<code>vim推坑指南</code>的, 但是想想這寫下來應該會不少內容..<br>
總而言之先分享 vim 配置文件吧~</p>
<hr>
<h3 id="">效果預覽</h3>
<p>先曬一張圖.</p>
<p><img src="https://tomme.me/content/images/2019/09/Snipaste_2019-09-14_04-17-25.png" alt="Snipaste_2019-09-14_04-17-25"></p>
<hr>
<h3 id="vim">配置vim</h3>
<h4 id="vundle">下載插件管理器: Vundle</h4>
<p><a href="https://github.com/VundleVim/Vundle.vim">Vundle</a> 是vim的插件管理器(之一), 使用以下指令安裝 <code>git clone https://github.com/VundleVim/Vundle.vim.git ~/.vim/bundle/Vundle.vim</code></p>
<h4 id="vimrc">配置文件 .vimrc</h4>
<p>貼上以下配置文件, 路徑為 <code>~/.vimrc</code>; Windows用戶是在 <code>%HOMEPATH%\_vimrc</code></p>
<pre><code>&quot;&quot;&quot;&quot;&quot;&quot;&quot;&quot;&quot;&quot;
&quot; 基礎配置
&quot;&quot;&quot;&quot;&quot;&quot;&quot;&quot;&quot;&quot;

syntax on &quot; 支持語法高亮
set backspace=2 &quot; 映射backspace, 適用於MAC
set laststatus=2 &quot; 永遠開啟狀態列
set encoding=utf-8 &quot; 支持utf8
set termencoding=utf-8
set fillchars+=stl:\ ,stlnc:\
set term=xterm-256color &quot; 支持256色
set t_Co=256
set noautoindent &quot; 關閉自動縮排
set number &quot; 開啟行數
set hlsearch &quot; 高亮搜索
set incsearch &quot; 開啟全域搜索
set ignorecase &quot; 搜索無視大小寫
set cursorline &quot; 高亮所在行
set expandtab &quot; tab 轉為空格
set tabstop=2 &quot; tab 輸出兩個空格
set mouse=a &quot; 支持滑鼠
set wildmenu &quot; 指令提示菜單
au BufRead,BufNewFile *.vue set filetype=html &quot; 以html來對待vue

&quot;-- 摺疊配置 --
set foldcolumn=1
set foldlevelstart=99
setlocal foldmethod=marker
setlocal foldmarker={,}
nnoremap &lt;Space&gt; za &quot; 使用空白件折疊


&quot;&quot;&quot;&quot;&quot;&quot;&quot;&quot;&quot;&quot;
&quot; Vundle配置
&quot;&quot;&quot;&quot;&quot;&quot;&quot;&quot;&quot;&quot;

set nocompatible
filetype off
set rtp+=~/.vim/bundle/Vundle.vim
call vundle#begin()

&quot; 插件
Plugin 'gmarik/Vundle.vim' &quot; 插件管理器本人
Plugin 'mattn/emmet-vim' &quot; 快速插入html
Plugin 'tpope/vim-surround' &quot; 快速包圍
Plugin 'chrisbra/Colorizer' &quot; 顏色提示
Plugin 'scrooloose/nerdtree' &quot; 樹形目錄
Plugin 'jistr/vim-nerdtree-tabs' &quot; 樹型目錄強化
Plugin 'jiangmiao/auto-pairs' &quot; 自動補全對稱符
Plugin 'mkitt/tabline.vim' &quot; tab頁籤
Plugin 'itchyny/lightline.vim' &quot; 下方狀態列表
Plugin 'scrooloose/nerdcommenter' &quot; 快速註解
Plugin 'joshdick/onedark.vim' &quot; vim 主題
Plugin 'Glench/Vim-Jinja2-Syntax' &quot; python Jinja 模塊語法高亮

Plugin 'ryanoasis/vim-devicons' &quot; 目錄icon
&quot; 系統字體需要支持特殊字元, 推薦nerdfonts

Plugin 'prettier/vim-prettier' &quot; 一鍵排版
&quot; 需安裝prettier, npm install -g prettier

Plugin 'w0rp/ale' &quot; 代碼錯誤提示
&quot; 需安裝linter, 這邊使用jshint; npm install -g jshint
&quot; jshint 配置參考: https://github.com/victorporof/Sublime-JSHint#using-your-own-jshintrc-options

Plugin 'Valloric/YouCompleteMe' &quot; 補全提示
&quot; vim 需要支持python
&quot; 安裝方式: cd ~/.vim/bundle/YouCompleteMe &amp;&amp;  python3 install.py --ts-completer

call vundle#end()
filetype plugin indent on

&quot;&quot;&quot;&quot;&quot;&quot;&quot;&quot;&quot;&quot;
&quot; 插件配置
&quot;&quot;&quot;&quot;&quot;&quot;&quot;&quot;&quot;&quot;

colorscheme onedark &quot; vim 主題

&quot; -- lightline 主題 --
let g:lightline = {
      \ 'colorscheme': 'wombat',
      \ }

&quot; -- ALE 配置 --
let g:ale_sign_error = '✗'
let g:ale_sign_warning = '⚡'
let g:ale_open_list = 1

&quot; [ 跳至下一個錯誤提示
&quot; ] 跳至前一個錯誤提示
nmap [ :ALENext&lt;CR&gt;
nmap ] :ALEPrevious&lt;CR&gt;
&quot; nmap &lt;C-[&gt; :ALELast&lt;CR&gt;
&quot; nmap &lt;C-]&gt; :ALEFirst&lt;CR&gt;


&quot; -- NERDTree 配置 --
map &lt;C-n&gt; :NERDTreeToggle&lt;CR&gt;
let NERDTreeAutoCenter=1
let NERDTreeShowHidden=1
let NERDTreeWinSize=31
let g:nerdtree_tabs_open_on_console_startup=1
let NERDTreeShowBookmarks=1
&quot; let NERDTreeIgnore=['\.pyc','\~$','\.swp']
let NERDTreeQuitOnOpen=0
let g:NERDTreeChDirMode=2
let NERDTreeMapActivateNode='&lt;space&gt;'
let g:NERDSpaceDelims = 1
let g:NERDDefaultAlign = 'left'
let g:NERDCompactSexyComs = 1

&quot; 沒有文件開啟的時候關閉nerdtree
autocmd QuitPre * if empty(&amp;bt) | lclose | endif

&quot; -- 分頁快捷鍵配置 --
&quot; Ctrl + t 開啟分頁
&quot; Ctrl + x 關閉分頁
&quot; Ctrl + hjkl 切換分頁
map  &lt;C-t&gt; :tabnew&lt;CR&gt; 
map  &lt;C-x&gt; :tabclose&lt;CR&gt;
map  &lt;C-k&gt; :tabn&lt;CR&gt;
map  &lt;C-j&gt; :tabp&lt;CR&gt;
map  &lt;C-h&gt; :tabfirst&lt;CR&gt;
map  &lt;C-l&gt; :tablast&lt;CR&gt;

&quot; -- 其他自定義映射 --
&quot; 全局替換換行符, Mac使用者可能會用到
map &lt;C-m&gt; :%s/&lt;C-v&gt;&lt;C-m&gt;/\r/g&lt;CR&gt;

&quot; 使用 \ 和 - 分屏
&quot; Ctrl w + \ 垂直分屏
&quot; Ctrl w + - 水平分屏
nnoremap &lt;C-w&gt;\ &lt;C-w&gt;v
nnoremap &lt;C-w&gt;- &lt;C-w&gt;s

&quot; 快速重新語法高亮, 對於vue很有用
nnoremap &lt;Leader&gt;s :syntax sync fromstart&lt;CR&gt;
</code></pre>
<h4 id="vim">開啟vim, 安裝插件</h4>
<p>剛剛貼上了配置文件, 接下來還要安裝才會生效.<br>
打開的時候可能會有些錯誤提示, 別擔心那是因為我們先進行配置實際還沒有安裝.</p>
<p>上述的插件可以自己選擇安裝, 不需要的註解掉即可.<br>
接下來請使用<code>:PluginInstall</code>來安裝.</p>
<h3 id="">結尾</h3>
<p>有空的話再個別介紹一些插件吧, 希望大家都可以成為厲害的<code>Vimer</code>~</p>
]]></content:encoded></item><item><title><![CDATA[logstasht接收nginx日誌出現Unrecognized character escape 'x' (code 120)]]></title><description><![CDATA[<p>最近在用logstash統計nginx access log, 發現如果nginx log裡面帶有中文轉譯的字符的時候, logstash 會沒有辦法正確解析(json parse fail) nginx的log.</p>
<p><img src="https://tomme.me/content/images/2019/05/DeepinScreenshot_select-area_20190508125002.png" alt="DeepinScreenshot_select-area_20190508125002"></p>
<p>怎麼會呢, 不是都已經轉成json了嗎？ 我們這就去nginx log看一下什麼狀況, 可以看到錯誤的好像是 user_agent <code>\xE4\xBD\xA0</code></p>
<p><img src="https://tomme.me/content/images/2019/05/DeepinScreenshot_select-area_20190508125251.png" alt="DeepinScreenshot_select-area_20190508125251"></p>
<p>大家可以把下面的log拿去validate一下; 可以看到這並不是正確的json, 正確的utf-8字符轉譯應該要是 \u0000 這種格式, 並不是 \x00</p>
<pre><code>{&quot;status&quot;: &quot;200&quot;,&quot;remote_addr&quot;: &quot;192.168.21.152&quot;,&quot;remote_user&quot;: &quot;-&</code></pre>]]></description><link>https://tomme.me/logstash-nginx-logs-error-with-unrecognized-character-escape-x-code-120/</link><guid isPermaLink="false">5cd26f8078153744ea00b93c</guid><category><![CDATA[Logstash]]></category><category><![CDATA[ELK]]></category><dc:creator><![CDATA[Tommy Lin]]></dc:creator><pubDate>Wed, 08 May 2019 05:59:48 GMT</pubDate><content:encoded><![CDATA[<p>最近在用logstash統計nginx access log, 發現如果nginx log裡面帶有中文轉譯的字符的時候, logstash 會沒有辦法正確解析(json parse fail) nginx的log.</p>
<p><img src="https://tomme.me/content/images/2019/05/DeepinScreenshot_select-area_20190508125002.png" alt="DeepinScreenshot_select-area_20190508125002"></p>
<p>怎麼會呢, 不是都已經轉成json了嗎？ 我們這就去nginx log看一下什麼狀況, 可以看到錯誤的好像是 user_agent <code>\xE4\xBD\xA0</code></p>
<p><img src="https://tomme.me/content/images/2019/05/DeepinScreenshot_select-area_20190508125251.png" alt="DeepinScreenshot_select-area_20190508125251"></p>
<p>大家可以把下面的log拿去validate一下; 可以看到這並不是正確的json, 正確的utf-8字符轉譯應該要是 \u0000 這種格式, 並不是 \x00</p>
<pre><code>{&quot;status&quot;: &quot;200&quot;,&quot;remote_addr&quot;: &quot;192.168.21.152&quot;,&quot;remote_user&quot;: &quot;-&quot;,&quot;timestamp&quot;: &quot;2019-05-08T12:48:26+08:00&quot;,&quot;request&quot;: &quot;GET / HTTP/1.1&quot;,&quot;bytes_sent&quot;: &quot;944&quot;,&quot;http_referer&quot;: &quot;-&quot;,&quot;http_user_agent&quot;: &quot;\xE4\xBD\xA0&quot;,&quot;server_name&quot;: &quot;192.168.150.110&quot;,&quot;uri&quot;: &quot;/&quot;,&quot;args&quot;: &quot;-&quot;,&quot;proxy_host&quot;: &quot;mixed.iloveloli.net&quot;,&quot;upstream_addr&quot;: &quot;192.168.150.113:80&quot;,&quot;upstream_status&quot;: &quot;200&quot;,&quot;upstream_cache_status&quot;: &quot;-&quot;,&quot;request_time&quot;: &quot;0.001&quot;,&quot;upstream_response_time&quot;: &quot;0.001&quot;,&quot;X-Forwarded-For&quot;: &quot;-&quot;}
</code></pre>
<br>
<p>咱半呢？ 所以說 nginx 自己也知道知道有這種問題; 在1.11.8之後提供了escape=json這個參數(嚴格來說是escape這個參數); 完整說明可以看到文末的參考三<br>
我們這就去設定檔加上看看, 箭頭部份就是我加上的.</p>
<p><img src="https://tomme.me/content/images/2019/05/DeepinScreenshot_select-area_20190508130601.png" alt="DeepinScreenshot_select-area_20190508130601"></p>
<p>修改完就可以 <code>nginx -t &amp;&amp; nginx -s reload</code><br>
下面看一下修改完成之後的效果, 可以看到unicode有正確轉譯囉～ 結案！<br>
<img src="https://tomme.me/content/images/2019/05/DeepinScreenshot_select-area_20190508131051.png" alt="DeepinScreenshot_select-area_20190508131051"></p>
<p><a href="https://github.com/elastic/logstash/issues/7779">參考一 github issue</a><br>
<a href="https://github.com/elastic/examples/tree/master/Common%20Data%20Formats/nginx_json_logs#warning-invalid-json">參考二 elk 官方教學 nginx log</a><br>
<a href="http://nginx.org/en/docs/http/ngx_http_log_module.html#log_format">參考三 nginx log_format文檔</a></p>
]]></content:encoded></item><item><title><![CDATA[為Elasticsarch添增ik分析器優化中文搜索(二)]]></title><description><![CDATA[<h3 id="">前言</h3>
<p>雖然說安裝好ik分析器可以對中文能夠比較友善的處理了; 但測試後發現有些詞彙還是沒有成功分詞. 不過幸好也找到了解決辦法; 順便也介紹一下如何熱更新流行詞彙吧！</p>
<h4 id="">調整之前</h4>
<p>我們看看針對這個句子可以得到什麼結果: <code>首先呢, 既然要使用那個模塊, 就必須先確保你的 Nginx 有編譯該模塊</code></p>
<pre><code class="language-bash">root@ghost-elastic01:~# curl 'http://localhost:9200/ikhell/_analyze?pretty=true' -H 'Content-Type: application/json' -d '{ &quot;field&quot;: &quot;content&quot;, &quot;text&quot;:&quot;首先呢, 既然要使用那個模塊, 就必須先確保你的 Nginx 有編譯該模塊&quot;}'
{
  &quot;tokens&quot; : [
    {
      &quot;token&</code></pre>]]></description><link>https://tomme.me/elasticsearch-ik-analyzer-optimize/</link><guid isPermaLink="false">5c4814f192fdee06c01c1008</guid><category><![CDATA[Elasticsearch]]></category><dc:creator><![CDATA[Tommy Lin]]></dc:creator><pubDate>Wed, 23 Jan 2019 10:09:28 GMT</pubDate><content:encoded><![CDATA[<h3 id="">前言</h3>
<p>雖然說安裝好ik分析器可以對中文能夠比較友善的處理了; 但測試後發現有些詞彙還是沒有成功分詞. 不過幸好也找到了解決辦法; 順便也介紹一下如何熱更新流行詞彙吧！</p>
<h4 id="">調整之前</h4>
<p>我們看看針對這個句子可以得到什麼結果: <code>首先呢, 既然要使用那個模塊, 就必須先確保你的 Nginx 有編譯該模塊</code></p>
<pre><code class="language-bash">root@ghost-elastic01:~# curl 'http://localhost:9200/ikhell/_analyze?pretty=true' -H 'Content-Type: application/json' -d '{ &quot;field&quot;: &quot;content&quot;, &quot;text&quot;:&quot;首先呢, 既然要使用那個模塊, 就必須先確保你的 Nginx 有編譯該模塊&quot;}'
{
  &quot;tokens&quot; : [
    {
      &quot;token&quot; : &quot;首先&quot;,
      &quot;start_offset&quot; : 0,
      &quot;end_offset&quot; : 2,
      &quot;type&quot; : &quot;CN_WORD&quot;,
      &quot;position&quot; : 0
    },
    {
      &quot;token&quot; : &quot;呢&quot;,
      &quot;start_offset&quot; : 2,
      &quot;end_offset&quot; : 3,
      &quot;type&quot; : &quot;CN_CHAR&quot;,
      &quot;position&quot; : 1
    },
    {
      &quot;token&quot; : &quot;既然&quot;,
      &quot;start_offset&quot; : 5,
      &quot;end_offset&quot; : 7,
      &quot;type&quot; : &quot;CN_WORD&quot;,
      &quot;position&quot; : 2
    },
    {
      &quot;token&quot; : &quot;要使&quot;,
      &quot;start_offset&quot; : 7,
      &quot;end_offset&quot; : 9,
      &quot;type&quot; : &quot;CN_WORD&quot;,
      &quot;position&quot; : 3
    },
    {
      &quot;token&quot; : &quot;使用&quot;,
      &quot;start_offset&quot; : 8,
      &quot;end_offset&quot; : 10,
      &quot;type&quot; : &quot;CN_WORD&quot;,
      &quot;position&quot; : 4
    },
    {
      &quot;token&quot; : &quot;那&quot;,
      &quot;start_offset&quot; : 10,
      &quot;end_offset&quot; : 11,
      &quot;type&quot; : &quot;CN_CHAR&quot;,
      &quot;position&quot; : 5
    },
    {
      &quot;token&quot; : &quot;個&quot;,
      &quot;start_offset&quot; : 11,
      &quot;end_offset&quot; : 12,
      &quot;type&quot; : &quot;CN_CHAR&quot;,
      &quot;position&quot; : 6
    },
    {
      &quot;token&quot; : &quot;模&quot;,
      &quot;start_offset&quot; : 12,
      &quot;end_offset&quot; : 13,
      &quot;type&quot; : &quot;CN_CHAR&quot;,
      &quot;position&quot; : 7
    },
    {
      &quot;token&quot; : &quot;塊&quot;,
      &quot;start_offset&quot; : 13,
      &quot;end_offset&quot; : 14,
      &quot;type&quot; : &quot;CN_CHAR&quot;,
      &quot;position&quot; : 8
    },
    {
      &quot;token&quot; : &quot;就&quot;,
      &quot;start_offset&quot; : 16,
      &quot;end_offset&quot; : 17,
      &quot;type&quot; : &quot;CN_CHAR&quot;,
      &quot;position&quot; : 9
    },
    {
      &quot;token&quot; : &quot;必&quot;,
      &quot;start_offset&quot; : 17,
      &quot;end_offset&quot; : 18,
      &quot;type&quot; : &quot;CN_CHAR&quot;,
      &quot;position&quot; : 10
    },
    {
      &quot;token&quot; : &quot;須&quot;,
      &quot;start_offset&quot; : 18,
      &quot;end_offset&quot; : 19,
      &quot;type&quot; : &quot;CN_CHAR&quot;,
      &quot;position&quot; : 11
    },
    {
      &quot;token&quot; : &quot;先&quot;,
      &quot;start_offset&quot; : 19,
      &quot;end_offset&quot; : 20,
      &quot;type&quot; : &quot;CN_CHAR&quot;,
      &quot;position&quot; : 12
    },
    {
      &quot;token&quot; : &quot;確&quot;,
      &quot;start_offset&quot; : 20,
      &quot;end_offset&quot; : 21,
      &quot;type&quot; : &quot;CN_CHAR&quot;,
      &quot;position&quot; : 13
    },
    {
      &quot;token&quot; : &quot;保&quot;,
      &quot;start_offset&quot; : 21,
      &quot;end_offset&quot; : 22,
      &quot;type&quot; : &quot;CN_CHAR&quot;,
      &quot;position&quot; : 14
    },
    {
      &quot;token&quot; : &quot;你&quot;,
      &quot;start_offset&quot; : 22,
      &quot;end_offset&quot; : 23,
      &quot;type&quot; : &quot;CN_CHAR&quot;,
      &quot;position&quot; : 15
    },
    {
      &quot;token&quot; : &quot;的&quot;,
      &quot;start_offset&quot; : 23,
      &quot;end_offset&quot; : 24,
      &quot;type&quot; : &quot;CN_CHAR&quot;,
      &quot;position&quot; : 16
    },
    {
      &quot;token&quot; : &quot;nginx&quot;,
      &quot;start_offset&quot; : 25,
      &quot;end_offset&quot; : 30,
      &quot;type&quot; : &quot;ENGLISH&quot;,
      &quot;position&quot; : 17
    },
    {
      &quot;token&quot; : &quot;有&quot;,
      &quot;start_offset&quot; : 31,
      &quot;end_offset&quot; : 32,
      &quot;type&quot; : &quot;CN_CHAR&quot;,
      &quot;position&quot; : 18
    },
    {
      &quot;token&quot; : &quot;編&quot;,
      &quot;start_offset&quot; : 32,
      &quot;end_offset&quot; : 33,
      &quot;type&quot; : &quot;CN_CHAR&quot;,
      &quot;position&quot; : 19
    },
    {
      &quot;token&quot; : &quot;譯&quot;,
      &quot;start_offset&quot; : 33,
      &quot;end_offset&quot; : 34,
      &quot;type&quot; : &quot;CN_CHAR&quot;,
      &quot;position&quot; : 20
    },
    {
      &quot;token&quot; : &quot;該&quot;,
      &quot;start_offset&quot; : 34,
      &quot;end_offset&quot; : 35,
      &quot;type&quot; : &quot;CN_CHAR&quot;,
      &quot;position&quot; : 21
    },
    {
      &quot;token&quot; : &quot;模&quot;,
      &quot;start_offset&quot; : 35,
      &quot;end_offset&quot; : 36,
      &quot;type&quot; : &quot;CN_CHAR&quot;,
      &quot;position&quot; : 22
    },
    {
      &quot;token&quot; : &quot;塊&quot;,
      &quot;start_offset&quot; : 36,
      &quot;end_offset&quot; : 37,
      &quot;type&quot; : &quot;CN_CHAR&quot;,
      &quot;position&quot; : 23
    }
  ]
}
</code></pre>
<p><code>首先呢, 既然要使用那個模塊, 就必須先確保你的 Nginx 有編譯該模塊</code> 被分解為<br>
<code>首先</code>, <code>呢</code>, <code>既然</code>, <code>要使</code>, <code>使用</code>, <code>那</code>, <code>個</code>, <code>模</code>, <code>塊</code>, <code>就</code>, <code>必</code>, <code>須</code>, <code>先</code>, <code>確</code>, <code>保</code>, <code>你</code>, <code>的</code>, <code>nginx</code>, <code>有</code>, <code>編</code>, <code>譯</code>, <code>該</code>, <code>模</code>, <code>塊</code></p>
<p><strong>恩..可謂不盡理想呀..</strong></p>
<hr>
<h3 id="">添加自訂義字典</h3>
<h4 id="">修改配置文件</h4>
<p>根據作者文檔說明<a href="https://github.com/medcl/elasticsearch-analysis-ik">ik分析器</a>, 我們可以在<code>/etc/elasticsearch/analysis-ik/IKAnalyzer.cfg.xml</code>這個設定檔添增自定義的字典檔.</p>
<p>在 <code>ext_dict</code>的地方填入我們的字典路徑.</p>
<pre><code class="language-xml">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;!DOCTYPE properties SYSTEM &quot;http://java.sun.com/dtd/properties.dtd&quot;&gt;
&lt;properties&gt;
        &lt;comment&gt;IK Analyzer 扩展配置&lt;/comment&gt;
        &lt;!--用户可以在这里配置自己的扩展字典 --&gt;
        &lt;entry key=&quot;ext_dict&quot;&gt;custom/custom.dic&lt;/entry&gt;
        &lt;!--用户可以在这里配置自己的扩展停止词字典--&gt;
        &lt;entry key=&quot;ext_stopwords&quot;&gt;&lt;/entry&gt;
        &lt;!--用户可以在这里配置远程扩展字典 --&gt;
        &lt;entry key=&quot;remote_ext_dict&quot;&gt;&lt;/entry&gt;
        &lt;!--用户可以在这里配置远程扩展停止词字典--&gt;
        &lt;!-- &lt;entry key=&quot;remote_ext_stopwords&quot;&gt;words_location&lt;/entry&gt; --&gt;
&lt;/properties&gt;
</code></pre>
<h4 id="">添加字典檔</h4>
<pre><code class="language-bash">cd /etc/elasticsearch/analysis-ik
mkdir custom
wget https://raw.githubusercontent.com/samejack/sc-dictionary/master/main.txt -O custom/custom.dic
</code></pre>
<p><em>這邊使用網友製作的<a href="https://github.com/samejack/sc-dictionary">超齊百萬字典檔</a></em></p>
<h4 id="elasticsearch">重啟elasticsearch</h4>
<p><code>systemctl restart elasticsearch</code></p>
<h4 id="">驗證結果</h4>
<p>經過自定義的字典檔, 我們來看看分詞如何不同吧.</p>
<pre><code class="language-bash">root@ghost-elastic01:/etc/elasticsearch/analysis-ik# curl 'http://localhost:9200/ikhell/_analyze?pretty=true' -H 'Content-Type: application/json' -d '{ &quot;field&quot;: &quot;content&quot;, &quot;text&quot;:&quot;首先呢, 既然要使用那個模塊, 就必須先確保你的 Nginx 有編譯該模塊&quot;}'
{
  &quot;tokens&quot; : [
    {
      &quot;token&quot; : &quot;首先&quot;,
      &quot;start_offset&quot; : 0,
      &quot;end_offset&quot; : 2,
      &quot;type&quot; : &quot;CN_WORD&quot;,
      &quot;position&quot; : 0
    },
    {
      &quot;token&quot; : &quot;呢&quot;,
      &quot;start_offset&quot; : 2,
      &quot;end_offset&quot; : 3,
      &quot;type&quot; : &quot;CN_CHAR&quot;,
      &quot;position&quot; : 1
    },
    {
      &quot;token&quot; : &quot;既然&quot;,
      &quot;start_offset&quot; : 5,
      &quot;end_offset&quot; : 7,
      &quot;type&quot; : &quot;CN_WORD&quot;,
      &quot;position&quot; : 2
    },
    {
      &quot;token&quot; : &quot;要使&quot;,
      &quot;start_offset&quot; : 7,
      &quot;end_offset&quot; : 9,
      &quot;type&quot; : &quot;CN_WORD&quot;,
      &quot;position&quot; : 3
    },
    {
      &quot;token&quot; : &quot;使用&quot;,
      &quot;start_offset&quot; : 8,
      &quot;end_offset&quot; : 10,
      &quot;type&quot; : &quot;CN_WORD&quot;,
      &quot;position&quot; : 4
    },
    {
      &quot;token&quot; : &quot;那個&quot;,
      &quot;start_offset&quot; : 10,
      &quot;end_offset&quot; : 12,
      &quot;type&quot; : &quot;CN_WORD&quot;,
      &quot;position&quot; : 5
    },
    {
      &quot;token&quot; : &quot;模塊&quot;,
      &quot;start_offset&quot; : 12,
      &quot;end_offset&quot; : 14,
      &quot;type&quot; : &quot;CN_WORD&quot;,
      &quot;position&quot; : 6
    },
    {
      &quot;token&quot; : &quot;就必須&quot;,
      &quot;start_offset&quot; : 16,
      &quot;end_offset&quot; : 19,
      &quot;type&quot; : &quot;CN_WORD&quot;,
      &quot;position&quot; : 7
    },
    {
      &quot;token&quot; : &quot;必須先&quot;,
      &quot;start_offset&quot; : 17,
      &quot;end_offset&quot; : 20,
      &quot;type&quot; : &quot;CN_WORD&quot;,
      &quot;position&quot; : 8
    },
    {
      &quot;token&quot; : &quot;必須&quot;,
      &quot;start_offset&quot; : 17,
      &quot;end_offset&quot; : 19,
      &quot;type&quot; : &quot;CN_WORD&quot;,
      &quot;position&quot; : 9
    },
    {
      &quot;token&quot; : &quot;先&quot;,
      &quot;start_offset&quot; : 19,
      &quot;end_offset&quot; : 20,
      &quot;type&quot; : &quot;CN_CHAR&quot;,
      &quot;position&quot; : 10
    },
    {
      &quot;token&quot; : &quot;確保&quot;,
      &quot;start_offset&quot; : 20,
      &quot;end_offset&quot; : 22,
      &quot;type&quot; : &quot;CN_WORD&quot;,
      &quot;position&quot; : 11
    },
    {
      &quot;token&quot; : &quot;你的&quot;,
      &quot;start_offset&quot; : 22,
      &quot;end_offset&quot; : 24,
      &quot;type&quot; : &quot;CN_WORD&quot;,
      &quot;position&quot; : 12
    },
    {
      &quot;token&quot; : &quot;nginx&quot;,
      &quot;start_offset&quot; : 25,
      &quot;end_offset&quot; : 30,
      &quot;type&quot; : &quot;ENGLISH&quot;,
      &quot;position&quot; : 13
    },
    {
      &quot;token&quot; : &quot;有&quot;,
      &quot;start_offset&quot; : 31,
      &quot;end_offset&quot; : 32,
      &quot;type&quot; : &quot;CN_CHAR&quot;,
      &quot;position&quot; : 14
    },
    {
      &quot;token&quot; : &quot;編譯&quot;,
      &quot;start_offset&quot; : 32,
      &quot;end_offset&quot; : 34,
      &quot;type&quot; : &quot;CN_WORD&quot;,
      &quot;position&quot; : 15
    },
    {
      &quot;token&quot; : &quot;該&quot;,
      &quot;start_offset&quot; : 34,
      &quot;end_offset&quot; : 35,
      &quot;type&quot; : &quot;CN_CHAR&quot;,
      &quot;position&quot; : 16
    },
    {
      &quot;token&quot; : &quot;模塊&quot;,
      &quot;start_offset&quot; : 35,
      &quot;end_offset&quot; : 37,
      &quot;type&quot; : &quot;CN_WORD&quot;,
      &quot;position&quot; : 17
    }
  ]
}
</code></pre>
<p><code>首先呢, 既然要使用那個模塊, 就必須先確保你的 Nginx 有編譯該模塊</code> 被分解為<br>
<code>首先</code>, <code>呢</code>, <code>既然</code>, <code>要使</code>, <code>使用</code>, <code>那個</code>, <code>模塊</code>, <code>就必須</code>, <code>必須先</code>, <code>必須</code>, <code>先</code>, <code>確保</code>, <code>你的</code>, <code>nginx</code>, <code>有</code>, <code>編譯</code>, <code>該</code>, <code>模塊</code></p>
<p>是不是好了很多呢？</p>
<hr>
<h3 id="">熱更新</h3>
<p>網路上的詞彙日新月異, 因此我們的必須要能夠熱更新我們的字典檔; 這邊就依照文檔做一次示範.<br>
依照文檔說明, 可以在配置文件填入外部獲取字典的接口, 然後依照<code>Last-Modified</code>和<code>ETag</code>兩個header來決定是否重新獲取字典檔案.</p>
<blockquote>
<p>若文件被編輯過, 則Last-Modified和ETag會變動; 代表文件被改動過, 此時就會重新獲取字典檔.</p>
</blockquote>
<h4 id="">編輯配置文件</h4>
<p>首先也是編輯<code>IKAnalyzer.cfg.xml</code>這個配置文件; 找到<code>remote_ext_dict</code>這個入口.</p>
<pre><code class="language-xml">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;!DOCTYPE properties SYSTEM &quot;http://java.sun.com/dtd/properties.dtd&quot;&gt;
&lt;properties&gt;
        &lt;comment&gt;IK Analyzer 扩展配置&lt;/comment&gt;
        &lt;!--用户可以在这里配置自己的扩展字典 --&gt;
        &lt;entry key=&quot;ext_dict&quot;&gt;custom/custom.dic&lt;/entry&gt;
        &lt;!--用户可以在这里配置自己的扩展停止词字典--&gt;
        &lt;entry key=&quot;ext_stopwords&quot;&gt;&lt;/entry&gt;
        &lt;!--用户可以在这里配置远程扩展字典 --&gt;
        &lt;entry key=&quot;remote_ext_dict&quot;&gt;http://127.0.0.1/es/dic&lt;/entry&gt;
        &lt;!--用户可以在这里配置远程扩展停止词字典--&gt;
        &lt;!-- &lt;entry key=&quot;remote_ext_stopwords&quot;&gt;words_location&lt;/entry&gt; --&gt;
&lt;/properties&gt;
</code></pre>
<p>這邊因為外掛內容被改動過, 所以還是重啟elasticsearch一次: <code>systemctl restart elasticsearch</code></p>
<h4 id="nginx">配置nginx</h4>
<p>在<code>server</code>區塊當中加入以下的路徑接口</p>
<pre><code class="language-nginx">    location /es {
      alias /etc/nginx/elasticsearch;
    }
</code></pre>
<p>重新載入配置文件<code>nginx -s reload</code></p>
<h4 id="">配置字典檔案</h4>
<pre><code class="language-bash">mkdir /etc/nginx/elasticsearch
touch /etc/nginx/elasticsearch/dic
</code></pre>
<p>到這邊為止; 我們先測試能不能獲取到字典</p>
<pre><code class="language-bash">root@ubuntu-87:~# curl http://127.0.0.1/es/dic


</code></pre>
<p>因為目前字典是空的; 這樣就是成功囉.</p>
<h4 id="">驗證熱更新</h4>
<p>在更新之前先測試一次分詞: <code>傻眼貓咪氣pupu</code></p>
<pre><code class="language-bash">root@ubuntu-87:~# curl 'http://localhost:9200/ikhell/_analyze?pretty=true' -H 'Content-Type: application/json' -d '{ &quot;field&quot;: &quot;content&quot;, &quot;text&quot;:&quot;傻眼貓咪氣pupu&quot;}'
{
  &quot;tokens&quot; : [
    {
      &quot;token&quot; : &quot;傻眼&quot;,
      &quot;start_offset&quot; : 0,
      &quot;end_offset&quot; : 2,
      &quot;type&quot; : &quot;CN_WORD&quot;,
      &quot;position&quot; : 0
    },
    {
      &quot;token&quot; : &quot;貓咪&quot;,
      &quot;start_offset&quot; : 2,
      &quot;end_offset&quot; : 4,
      &quot;type&quot; : &quot;CN_WORD&quot;,
      &quot;position&quot; : 1
    },
    {
      &quot;token&quot; : &quot;氣&quot;,
      &quot;start_offset&quot; : 4,
      &quot;end_offset&quot; : 5,
      &quot;type&quot; : &quot;CN_CHAR&quot;,
      &quot;position&quot; : 2
    },
    {
      &quot;token&quot; : &quot;pupu&quot;,
      &quot;start_offset&quot; : 5,
      &quot;end_offset&quot; : 9,
      &quot;type&quot; : &quot;ENGLISH&quot;,
      &quot;position&quot; : 3
    }
  ]
}
</code></pre>
<p>可以看到被分詞為: <code>傻眼</code>,<code>貓咪</code>, <code>氣</code>, <code>pupu</code>.</p>
<p>然後我們寫入新的詞彙<br>
<code>echo -e &quot;傻眼貓咪\n氣pupu&quot; &gt; /etc/nginx/elasticsearch/dic</code></p>
<p>接著再測試一次</p>
<pre><code class="language-bash">root@ubuntu-87:~# curl 'http://localhost:9200/ikhell/_analyze?pretty=true' -H 'Content-Type: application/json' -d '{ &quot;field&quot;: &quot;content&quot;, &quot;text&quot;:&quot;傻眼貓咪氣pupu&quot;}'
{
  &quot;tokens&quot; : [
    {
      &quot;token&quot; : &quot;傻眼貓咪&quot;,
      &quot;start_offset&quot; : 0,
      &quot;end_offset&quot; : 4,
      &quot;type&quot; : &quot;CN_WORD&quot;,
      &quot;position&quot; : 0
    },
    {
      &quot;token&quot; : &quot;傻眼&quot;,
      &quot;start_offset&quot; : 0,
      &quot;end_offset&quot; : 2,
      &quot;type&quot; : &quot;CN_WORD&quot;,
      &quot;position&quot; : 1
    },
    {
      &quot;token&quot; : &quot;貓咪&quot;,
      &quot;start_offset&quot; : 2,
      &quot;end_offset&quot; : 4,
      &quot;type&quot; : &quot;CN_WORD&quot;,
      &quot;position&quot; : 2
    },
    {
      &quot;token&quot; : &quot;氣pupu&quot;,
      &quot;start_offset&quot; : 4,
      &quot;end_offset&quot; : 9,
      &quot;type&quot; : &quot;CN_WORD&quot;,
      &quot;position&quot; : 3
    },
    {
      &quot;token&quot; : &quot;pupu&quot;,
      &quot;start_offset&quot; : 5,
      &quot;end_offset&quot; : 9,
      &quot;type&quot; : &quot;ENGLISH&quot;,
      &quot;position&quot; : 4
    }
  ]
}
</code></pre>
<p>可以看到我們的流行詞彙都進來了唷！ 分詞結果為: <code>傻眼貓咪</code>, <code>傻眼</code>, <code>貓咪</code>, <code>氣pupu</code>, <code>pupu</code><br>
<em>經測試發現這並不是立即生效的, 可能要等數分鐘; 請大家給他一點時間ＸＤ</em></p>
<hr>
<h3 id="">尾聲</h3>
<p>中文搜尋的基本優化也就到這邊告個段落了, 之後會沿著這個脈絡繼續做Ghost博客和Elasticsearch搭配的相關文章唷！ 下次見～</p>
]]></content:encoded></item><item><title><![CDATA[為Elasticsarch添增ik分析器優化中文搜索(一)]]></title><description><![CDATA[<h3 id="">前言</h3>
<p>Elasticsearch作為一個母語為英文的索引軟體, 對中文的分詞效果簡直慘不忍睹; 不過還好有<strong>ik分析器</strong>可以使用, 解決了這尷尬的窘境.</p>
<hr>
<h3 id="">一顆慘不忍睹的栗子</h3>
<h4 id="">放置資料</h4>
<p>首先我們放四筆資料吧</p>
<pre><code class="language-bash">curl -XPOST http://localhost:9200/ik/fulltext/1 -H 'Content-Type:application/json' -d '{&quot;content&quot;:&quot;美国留给伊拉克的是个烂摊子吗&quot;}'
curl -XPOST http://localhost:9200/ik/fulltext/2 -H 'Content-Type:application/json' -d '{&quot;content&quot;:&quot;公安部：</code></pre>]]></description><link>https://tomme.me/elasticsearch-setup-ik-analyzer/</link><guid isPermaLink="false">5c46d9087081d5753e2838b8</guid><category><![CDATA[Elasticsearch]]></category><dc:creator><![CDATA[Tommy Lin]]></dc:creator><pubDate>Tue, 22 Jan 2019 08:49:59 GMT</pubDate><content:encoded><![CDATA[<h3 id="">前言</h3>
<p>Elasticsearch作為一個母語為英文的索引軟體, 對中文的分詞效果簡直慘不忍睹; 不過還好有<strong>ik分析器</strong>可以使用, 解決了這尷尬的窘境.</p>
<hr>
<h3 id="">一顆慘不忍睹的栗子</h3>
<h4 id="">放置資料</h4>
<p>首先我們放四筆資料吧</p>
<pre><code class="language-bash">curl -XPOST http://localhost:9200/ik/fulltext/1 -H 'Content-Type:application/json' -d '{&quot;content&quot;:&quot;美国留给伊拉克的是个烂摊子吗&quot;}'
curl -XPOST http://localhost:9200/ik/fulltext/2 -H 'Content-Type:application/json' -d '{&quot;content&quot;:&quot;公安部：各地校车将享最高路权&quot;}'
curl -XPOST http://localhost:9200/ik/fulltext/3 -H 'Content-Type:application/json' -d '{&quot;content&quot;:&quot;中韩渔警冲突调查：韩警平均每天扣1艘中国渔船&quot;}'
curl -XPOST http://localhost:9200/ik/fulltext/4 -H 'Content-Type:application/json' -d '{&quot;content&quot;:&quot;中国驻洛杉矶领事馆遭亚裔男子枪击 嫌犯已自首&quot;}'
</code></pre>
<h4 id="">嘗試搜尋</h4>
<p>我們現在有四筆資料, 讓我們嘗試搜尋<code>中国</code>試試看</p>
<pre><code class="language-bash">root@ubuntu-87:~# curl -XPOST &quot;http://localhost:9200/ik/fulltext/_search?pretty&quot;  -H 'Content-Type:application/json' -d'
{
    &quot;query&quot; : { &quot;match&quot; : { &quot;content&quot; : &quot;中国&quot; }},
    &quot;highlight&quot; : {
        &quot;fields&quot; : {
            &quot;content&quot; : {}
        }
    }
}
'
{
  &quot;took&quot; : 4,
  &quot;timed_out&quot; : false,
  &quot;_shards&quot; : {
    &quot;total&quot; : 5,
    &quot;successful&quot; : 5,
    &quot;skipped&quot; : 0,
    &quot;failed&quot; : 0
  },
  &quot;hits&quot; : {
    &quot;total&quot; : 3,
    &quot;max_score&quot; : 1.264571,
    &quot;hits&quot; : [
      {
        &quot;_index&quot; : &quot;ik&quot;,
        &quot;_type&quot; : &quot;fulltext&quot;,
        &quot;_id&quot; : &quot;4&quot;,
        &quot;_score&quot; : 1.264571,
        &quot;_source&quot; : {
          &quot;content&quot; : &quot;中国驻洛杉矶领事馆遭亚裔男子枪击 嫌犯已自首&quot;
        },
        &quot;highlight&quot; : {
          &quot;content&quot; : [
            &quot;&lt;em&gt;中&lt;/em&gt;&lt;em&gt;国&lt;/em&gt;驻洛杉矶领事馆遭亚裔男子枪击 嫌犯已自首&quot;
          ]
        }
      },
      {
        &quot;_index&quot; : &quot;ik&quot;,
        &quot;_type&quot; : &quot;fulltext&quot;,
        &quot;_id&quot; : &quot;3&quot;,
        &quot;_score&quot; : 0.68324494,
        &quot;_source&quot; : {
          &quot;content&quot; : &quot;中韩渔警冲突调查：韩警平均每天扣1艘中国渔船&quot;
        },
        &quot;highlight&quot; : {
          &quot;content&quot; : [
            &quot;&lt;em&gt;中&lt;/em&gt;韩渔警冲突调查：韩警平均每天扣1艘&lt;em&gt;中&lt;/em&gt;&lt;em&gt;国&lt;/em&gt;渔船&quot;
          ]
        }
      },
      {
        &quot;_index&quot; : &quot;ik&quot;,
        &quot;_type&quot; : &quot;fulltext&quot;,
        &quot;_id&quot; : &quot;1&quot;,
        &quot;_score&quot; : 0.2876821,
        &quot;_source&quot; : {
          &quot;content&quot; : &quot;美国留给伊拉克的是个烂摊子吗&quot;
        },
        &quot;highlight&quot; : {
          &quot;content&quot; : [
            &quot;美&lt;em&gt;国&lt;/em&gt;留给伊拉克的是个烂摊子吗&quot;
          ]
        }
      }
    ]
  }
}
</code></pre>
<p>結果跑出了三筆資料, 可以看到highlight的部分也就是elasticsearch認定搜索到的字詞</p>
<ol>
<li><code>中</code> <code>国</code>驻洛杉矶领事馆遭亚裔男子枪击 嫌犯已自首</li>
<li><code>中</code>韩渔警冲突调查：韩警平均每天扣1艘<code>中</code> <code>国</code>渔船</li>
<li>美<code>国</code>留给伊拉克的是个烂摊子吗</li>
</ol>
<p><strong>這明顯不是我們想要的結果呀！</strong></p>
<h4 id="">問題原因</h4>
<p>原來是因為分詞出了問題, 預設的分詞器根本不曉得中文這玩意兒, 於是就將詞逐字分詞了. 還不曉得分詞的同學可以看<a href="https://tomme.me/elasticsearch-analyzer/">Elasticsearch當中的分析器-Analyzer</a>.</p>
<p>讓我們看看這貨都將我們的句子怎麼分詞了:<br>
以第一筆資料為例子: <code>美国留给伊拉克的是个烂摊子吗</code></p>
<pre><code class="language-bash">root@ubuntu-87:~# curl -H &quot;Content-Type:application/json&quot;  &quot;http://localhost:9200/ik/fulltext/1/_termvectors?pretty&quot; -d '{ &quot;fields&quot; :
 [&quot;content&quot;] }'
{
  &quot;_index&quot; : &quot;ik&quot;,
  &quot;_type&quot; : &quot;fulltext&quot;,
  &quot;_id&quot; : &quot;1&quot;,
  &quot;_version&quot; : 1,
  &quot;found&quot; : true,
  &quot;took&quot; : 0,
  &quot;term_vectors&quot; : {
    &quot;content&quot; : {
      &quot;field_statistics&quot; : {
        &quot;sum_doc_freq&quot; : 14,
        &quot;doc_count&quot; : 1,
        &quot;sum_ttf&quot; : 14
      },
      &quot;terms&quot; : {
        &quot;个&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 9,
              &quot;start_offset&quot; : 9,
              &quot;end_offset&quot; : 10
            }
          ]
        },
        &quot;伊&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 4,
              &quot;start_offset&quot; : 4,
              &quot;end_offset&quot; : 5
            }
          ]
        },
        &quot;克&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 6,
              &quot;start_offset&quot; : 6,
              &quot;end_offset&quot; : 7
            }
          ]
        },
        &quot;吗&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 13,
              &quot;start_offset&quot; : 13,
              &quot;end_offset&quot; : 14
            }
          ]
        },
        &quot;国&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 1,
              &quot;start_offset&quot; : 1,
              &quot;end_offset&quot; : 2
            }
          ]
        },
        &quot;子&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 12,
              &quot;start_offset&quot; : 12,
              &quot;end_offset&quot; : 13
            }
          ]
        },
        &quot;拉&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 5,
              &quot;start_offset&quot; : 5,
              &quot;end_offset&quot; : 6
            }
          ]
        },
        &quot;摊&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 11,
              &quot;start_offset&quot; : 11,
              &quot;end_offset&quot; : 12
            }
          ]
        },
        &quot;是&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 8,
              &quot;start_offset&quot; : 8,
              &quot;end_offset&quot; : 9
            }
          ]
        },
        &quot;烂&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 10,
              &quot;start_offset&quot; : 10,
              &quot;end_offset&quot; : 11
            }
          ]
        },
        &quot;留&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 2,
              &quot;start_offset&quot; : 2,
              &quot;end_offset&quot; : 3
            }
          ]
        },
        &quot;的&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 7,
              &quot;start_offset&quot; : 7,
              &quot;end_offset&quot; : 8
            }
          ]
        },
        &quot;给&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 3,
              &quot;start_offset&quot; : 3,
              &quot;end_offset&quot; : 4
            }
          ]
        },
        &quot;美&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 0,
              &quot;start_offset&quot; : 0,
              &quot;end_offset&quot; : 1
            }
          ]
        }
      }
    }
  }
}
</code></pre>
<p>句子裡面每個字都被逐一分開了, 可謂碎屍萬段～好慘呀～！！</p>
<hr>
<h3 id="ik">ik分析器</h3>
<p>為了解決中文分詞這種好恐怖好恐怖的狀況, 我們必須換一個分析器. <code>ik分析器</code>是一個第三方的外掛, 也是本文的主角, 專治給中文胡亂分詞的elasticsearch.</p>
<p>提供github傳送門<a href="https://github.com/medcl/elasticsearch-analysis-ik">elasticsearch-analysis-ik</a></p>
<h4 id="">安裝</h4>
<p>我們直接透過內建的方式安裝吧. 這邊一定要注意的就是elasticsearch和ik分析器的版本必須對應.</p>
<pre><code class="language-bash"># 進入elasticsearch目錄
cd /usr/share/elasticsearch

# 安裝
./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.4.2/elasticsearch-analysis-ik-6.4.2.zip

# 重啟es後生效
systemctl restart elasticsearch
</code></pre>
<h4 id="">測試</h4>
<p>來測試吧, 首先我們創建一個索引</p>
<pre><code class="language-bash">curl -XPUT &quot;http://localhost:9200/ik1?pretty&quot;
{
  &quot;acknowledged&quot; : true,
  &quot;shards_acknowledged&quot; : true,
  &quot;index&quot; : &quot;ik1&quot;
}

</code></pre>
<p>然後配置映射, 告訴他我們要使用ik分析器</p>
<pre><code class="language-bash">curl -XPOST &quot;http://localhost:9200/ik1/fulltext/_mapping?pretty&quot; -H 'Content-Type:application/json' -d'
{
        &quot;properties&quot;: {
            &quot;content&quot;: {
                &quot;type&quot;: &quot;text&quot;,
                &quot;analyzer&quot;: &quot;ik_max_word&quot;,
                &quot;search_analyzer&quot;: &quot;ik_max_word&quot;
            }
        }

}'

{
  &quot;acknowledged&quot; : true
}
</code></pre>
<p>接著一樣丟上剛剛的四筆文檔</p>
<pre><code class="language-bash">curl -XPOST http://localhost:9200/ik1/fulltext/1 -H 'Content-Type:application/json' -d '{&quot;content&quot;:&quot;美国留给伊拉克的是个烂摊子吗&quot;}'
curl -XPOST http://localhost:9200/ik1/fulltext/2 -H 'Content-Type:application/json' -d '{&quot;content&quot;:&quot;公安部：各地校车将享最高路权&quot;}'
curl -XPOST http://localhost:9200/ik1/fulltext/3 -H 'Content-Type:application/json' -d '{&quot;content&quot;:&quot;中韩渔警冲突调查：韩警平均每天扣1艘中国渔船&quot;}'
curl -XPOST http://localhost:9200/ik1/fulltext/4 -H 'Content-Type:application/json' -d '{&quot;content&quot;:&quot;中国驻洛杉矶领事馆遭亚裔男子枪击 嫌犯已自首&quot;}'
</code></pre>
<p>最後查看搜尋成果, 可以看到已經準確多啦</p>
<pre><code class="language-bash">{
    &quot;query&quot; : { &quot;match&quot; : { &quot;content&quot; : &quot;中国&quot; }},
    &quot;highlight&quot; : {
        &quot;fields&quot; : {
            &quot;content&quot; : {}
        }
    }
}
' 

{
  &quot;took&quot;: 6,
  &quot;timed_out&quot;: false,
  &quot;_shards&quot;: {
    &quot;total&quot;: 5,
    &quot;successful&quot;: 5,
    &quot;skipped&quot;: 0,
    &quot;failed&quot;: 0
  },
  &quot;hits&quot;: {
    &quot;total&quot;: 2,
    &quot;max_score&quot;: 0.6489038,
    &quot;hits&quot;: [
      {
        &quot;_index&quot;: &quot;ik1&quot;,
        &quot;_type&quot;: &quot;fulltext&quot;,
        &quot;_id&quot;: &quot;4&quot;,
        &quot;_score&quot;: 0.6489038,
        &quot;_source&quot;: {
          &quot;content&quot;: &quot;中国驻洛杉矶领事馆遭亚裔男子枪击 嫌犯已自首&quot;
        },
        &quot;highlight&quot;: {
          &quot;content&quot;: [
            &quot;&lt;em&gt;中国&lt;/em&gt;驻洛杉矶领事馆遭亚裔男子枪击 嫌犯已自首&quot;
          ]
        }
      },
      {
        &quot;_index&quot;: &quot;ik1&quot;,
        &quot;_type&quot;: &quot;fulltext&quot;,
        &quot;_id&quot;: &quot;3&quot;,
        &quot;_score&quot;: 0.2876821,
        &quot;_source&quot;: {
          &quot;content&quot;: &quot;中韩渔警冲突调查：韩警平均每天扣1艘中国渔船&quot;
        },
        &quot;highlight&quot;: {
          &quot;content&quot;: [
            &quot;中韩渔警冲突调查：韩警平均每天扣1艘&lt;em&gt;中国&lt;/em&gt;渔船&quot;
          ]
        }
      }
    ]
  }
}

</code></pre>
<p>搜尋結果兩筆資料</p>
<ol>
<li><code>中国</code>驻洛杉矶领事馆遭亚裔男子枪击 嫌犯已自首</li>
<li>中韩渔警冲突调查：韩警平均每天扣1艘<code>中国</code>渔船</li>
</ol>
<p><strong>這樣就對啦～</strong></p>
<h4 id="">驗證分詞</h4>
<p>我們來看看透過ik分析器之後, elasticsearch會怎麼分詞吧～<br>
一樣第一筆資料為例子: <code>美国留给伊拉克的是个烂摊子吗</code></p>
<pre><code class="language-bash">root@ubuntu-87:~# curl -H &quot;Content-Type:application/json&quot;  &quot;http://localhost:9200/ik1/fulltext/1/_termvectors?pretty&quot; -d '{ &quot;fields&quot;: [&quot;content&quot;] }'
{
  &quot;_index&quot; : &quot;ik1&quot;,
  &quot;_type&quot; : &quot;fulltext&quot;,
  &quot;_id&quot; : &quot;1&quot;,
  &quot;_version&quot; : 1,
  &quot;found&quot; : true,
  &quot;took&quot; : 0,
  &quot;term_vectors&quot; : {
    &quot;content&quot; : {
      &quot;field_statistics&quot; : {
        &quot;sum_doc_freq&quot; : 9,
        &quot;doc_count&quot; : 1,
        &quot;sum_ttf&quot; : 9
      },
      &quot;terms&quot; : {
        &quot;个&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 5,
              &quot;start_offset&quot; : 9,
              &quot;end_offset&quot; : 10
            }
          ]
        },
        &quot;伊拉克&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 2,
              &quot;start_offset&quot; : 4,
              &quot;end_offset&quot; : 7
            }
          ]
        },
        &quot;吗&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 8,
              &quot;start_offset&quot; : 13,
              &quot;end_offset&quot; : 14
            }
          ]
        },
        &quot;摊子&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 7,
              &quot;start_offset&quot; : 11,
              &quot;end_offset&quot; : 13
            }
          ]
        },
        &quot;是&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 4,
              &quot;start_offset&quot; : 8,
              &quot;end_offset&quot; : 9
            }
          ]
        },
        &quot;烂摊子&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 6,
              &quot;start_offset&quot; : 10,
              &quot;end_offset&quot; : 13
            }
          ]
        },
        &quot;留给&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 1,
              &quot;start_offset&quot; : 2,
              &quot;end_offset&quot; : 4
            }
          ]
        },
        &quot;的&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 3,
              &quot;start_offset&quot; : 7,
              &quot;end_offset&quot; : 8
            }
          ]
        },
        &quot;美国&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 0,
              &quot;start_offset&quot; : 0,
              &quot;end_offset&quot; : 2
            }
          ]
        }
      }
    }
  }
}
</code></pre>
<hr>
<h3 id="">尾聲</h3>
<p>透過ik分詞器, 原本被切的亂七八糟的中文句子也得以解脫囉～</p>
<p></p><p></p>]]></content:encoded></item><item><title><![CDATA[Elasticsearch當中的分析器-Analyzer]]></title><description><![CDATA[<h3 id="">前言</h3>
<p>相信很多朋友剛開始在使用Elasticsearch的時候,一定都會遇到一個問題： <strong>我的檔案內容清清楚楚的寫在那, 怎麼就是搜尋不到？</strong> 其中很大的可能就是分析器沒有正確配置唷！</p>
<hr>
<h3 id="">舉個例子</h3>
<h4 id="">搜尋英文進行式單詞</h4>
<p>首先放入一筆資料, 內容是 &quot;Set the shape to semi-transparent by calling set_trans(5)&quot;</p>
<pre><code class="language-bash">root@ubuntu-87:~# curl -XPOST http://localhost:9200/demo/demotype/1 -H 'Content-Type:application/json' -d '
{
  &quot;content&quot;: &quot;Set the shape to semi-transparent by calling set_</code></pre>]]></description><link>https://tomme.me/elasticsearch-analyzer/</link><guid isPermaLink="false">5c46a919e0213a720994d820</guid><category><![CDATA[Elasticsearch]]></category><dc:creator><![CDATA[Tommy Lin]]></dc:creator><pubDate>Tue, 22 Jan 2019 06:18:45 GMT</pubDate><content:encoded><![CDATA[<h3 id="">前言</h3>
<p>相信很多朋友剛開始在使用Elasticsearch的時候,一定都會遇到一個問題： <strong>我的檔案內容清清楚楚的寫在那, 怎麼就是搜尋不到？</strong> 其中很大的可能就是分析器沒有正確配置唷！</p>
<hr>
<h3 id="">舉個例子</h3>
<h4 id="">搜尋英文進行式單詞</h4>
<p>首先放入一筆資料, 內容是 &quot;Set the shape to semi-transparent by calling set_trans(5)&quot;</p>
<pre><code class="language-bash">root@ubuntu-87:~# curl -XPOST http://localhost:9200/demo/demotype/1 -H 'Content-Type:application/json' -d '
{
  &quot;content&quot;: &quot;Set the shape to semi-transparent by calling set_trans(5)&quot;
}' | jq .

{
  &quot;_index&quot;: &quot;demo&quot;,
  &quot;_type&quot;: &quot;demotype&quot;,
  &quot;_id&quot;: &quot;1&quot;,
  &quot;_version&quot;: 1,
  &quot;result&quot;: &quot;created&quot;,
  &quot;_shards&quot;: {
    &quot;total&quot;: 2,
    &quot;successful&quot;: 1,
    &quot;failed&quot;: 0
  },
  &quot;_seq_no&quot;: 0,
  &quot;_primary_term&quot;: 1
}
</code></pre>
<p>然後我們試著搜尋 call; 結果是搜尋不到這筆資料.</p>
<pre><code class="language-bash">root@ubuntu-87:~# curl -s -XPOST http://localhost:9200/demo/demotype/_search  -H 'Content-Type:application/json' -d '
{
  &quot;query&quot; : {
    &quot;match&quot; : {
      &quot;content&quot; : &quot;call&quot;
    }
  }
}' | jq .

{
  &quot;took&quot;: 6,
  &quot;timed_out&quot;: false,
  &quot;_shards&quot;: {
    &quot;total&quot;: 5,
    &quot;successful&quot;: 5,
    &quot;skipped&quot;: 0,
    &quot;failed&quot;: 0
  },
  &quot;hits&quot;: {
    &quot;total&quot;: 0,
    &quot;max_score&quot;: null,
    &quot;hits&quot;: []
  }
}
</code></pre>
<h4 id="">原因</h4>
<p>在Elasticsearch可以搜尋文檔之前, 他必須先將字詞內容做拆分(分詞)和加工, 然後透過這些拆分好的詞建構倒排索引, 之後使用者才能在透過關鍵詞搜尋到我們想要的文檔.</p>
<p>而文檔內容要如何分詞或是加工, 依靠的就是分析器的配置規則囉！</p>
<blockquote>
<p>也就是說,Elasticsearch認定的分詞是 calling 不是call, 所以搜尋不到.</p>
</blockquote>
<hr>
<h3 id="">分析器介紹</h3>
<p>先附上文檔<br>
英文: <a href="https://www.elastic.co/guide/en/elasticsearch/guide/current/analysis-intro.html">Elasticsearch: The Definitive Guide - Analysis and Analyzers</a><br>
中文: <a href="https://www.elastic.co/guide/cn/elasticsearch/guide/current/analysis-intro.html">Elasticsearch: 权威指南 - 分析与分析器</a></p>
<p>分析器是由: 字符過濾器(Character filters), 分詞器(Tokenizer), Token過濾器(Token filters) 組成的; 他們的工作流程如下</p>
<ol>
<li>
<p>字符過濾器(Character filters): 預處理<br>
首先，字符串按順序通過每個 字符過濾器 。他們的任務是在分詞前整理字符串。一個字符過濾器可以用來去掉HTML，或者將 <code>&amp;</code> 轉化成 <code>and</code>。</p>
</li>
<li>
<p>分詞器(Tokenizer): 主要分詞工作<br>
其次，字符串被 分詞器 分為單個的詞條。一個簡單的分詞器遇到空格和標點的時候，可能會將文本拆分成詞條。</p>
</li>
<li>
<p>Token過濾器(Token filters): 後續加工<br>
最後，詞條按順序通過每個 token 過濾器 。這個過程可能會改變詞條（例如，小寫化 Quick ），刪除詞條（例如， 像 <code>a</code>， <code>and</code>， <code>the</code> 等無用詞），或者增加詞條（例如，像 jump 和 leap 這種同義詞）。</p>
</li>
</ol>
<h4 id="">內置分析器效果預覽</h4>
<p>Elasticsearch 內置了幾種分析器, 透過套用不同的分析器就可以讓這句文檔產生不同的索引效果</p>
<ul>
<li>
<p>標準分析器 (Standard analyzer)<br>
標準分析器是Elasticsearch默認使用的分析器。它是分析各種語言文本最常用的選擇。它根據 Unicode 聯盟 定義的 單詞邊界 劃分文本。刪除絕大部分標點。最後，將詞條小寫。他會產生<br>
<code>set, the, shape, to, semi, transparent, by, calling, set_trans, 5</code></p>
</li>
<li>
<p>簡單分析器 (Simple analyzer)<br>
簡單分析器在任何不是字母的地方分隔文本，將詞條小寫。它會產生<br>
<code>set, the, shape, to, semi, transparent, by, calling, set, trans</code></p>
</li>
<li>
<p>空格分析器 (Whitespace analyzer)<br>
空格分析器在空格的地方劃分文本。它會產生<br>
<code>Set, the, shape, to, semi-transparent, by, calling, set_trans(5)</code></p>
</li>
<li>
<p>語言分析器 (Language analyzers)<br>
特定語言分析器可用於 很多語言。它們可以考慮指定語言的特點。例如， 英語 分析器附帶了一組英語無用詞（常用單詞，例如 and 或者 the ，它們對相關性沒有多少影響），它們會被刪除。 由於理解英語語法的規則，這個分詞器可以提取英語單詞的 詞幹 。</p>
</li>
</ul>
<p>英語 分詞器會產生下面的詞條：</p>
<p><code>set, shape, semi, transpar, call, set_tran, 5</code><br>
註意看 <code>transparent</code>, <code>calling</code> 和 <code>set_trans</code> 已經變為詞根格式。</p>
<p><strong>看到這邊大家應該了解到如果想要搜尋英文詞根的話; 必須套用語言分析器～</strong></p>
<hr>
<h3 id="">配置分析器</h3>
<p>分析器的配置放在索引的mapping之下, 而更改過的mapping無法影響已存在的文檔; 所以我們另外配置一個demo1來做示範</p>
<ol>
<li>新增索引: <code>curl -XPUT http://localhost:9200/demo1</code></li>
<li>配置映射:</li>
</ol>
<pre><code class="language-bash">curl -XPOST http://localhost:9200/demo1/demotype/_mapping -H 'Content-Type:application/json' -d'
{
  &quot;properties&quot;: {
    &quot;content&quot;: {
      &quot;type&quot;: &quot;text&quot;,
      &quot;analyzer&quot;: &quot;english&quot;
    }
  }
}'
</code></pre>
<ol start="3">
<li>
<p>新增文檔: <code>curl -XPOST http://localhost:9200/demo1/demotype/1 -H 'Content-Type:application/json' -d '{&quot;content&quot;:&quot;Set the shape to semi-transparent by calling set_trans(5)&quot;}'</code></p>
</li>
<li>
<p>嘗試搜尋:</p>
</li>
</ol>
<pre><code class="language-bash">root@ubuntu-87:~# curl -s -XPOST http://localhost:9200/demo1/demotype/_search  -H 'Content-Type:application/json' -d '
{
  &quot;query&quot; : {
    &quot;match&quot; : {
      &quot;content&quot; : &quot;call&quot;
    }
  }
}' | jq .

{
  &quot;took&quot;: 19,
  &quot;timed_out&quot;: false,
  &quot;_shards&quot;: {
    &quot;total&quot;: 5,
    &quot;successful&quot;: 5,
    &quot;skipped&quot;: 0,
    &quot;failed&quot;: 0
  },
  &quot;hits&quot;: {
    &quot;total&quot;: 1,
    &quot;max_score&quot;: 0.2876821,
    &quot;hits&quot;: [
      {
        &quot;_index&quot;: &quot;demo1&quot;,
        &quot;_type&quot;: &quot;demotype&quot;,
        &quot;_id&quot;: &quot;1&quot;,
        &quot;_score&quot;: 0.2876821,
        &quot;_source&quot;: {
          &quot;content&quot;: &quot;Set the shape to semi-transparent by calling set_trans(5)&quot;
        }
      }
    ]
  }
}
</code></pre>
<hr>
<h3 id="">查看分詞效果</h3>
<p>僅管我們進行了這些配置, 但是只能透過搜尋來測試有沒有成功, 這樣子還是比較模糊的.<br>
所以 Elasticsearch 提供了兩個 api 讓我們可以直接查看分詞效果: <code>_termvectors</code> 和 <code>_analyze</code>, 而不管是哪一個都提供了以下資訊</p>
<ul>
<li>剛檔案的分詞組成</li>
<li><code>start_offset</code>, <code>end_offset</code>: 字詞出現的位置, 用於高亮搜索</li>
<li><code>position</code>: 分詞出現的順序</li>
<li><code>type</code>: 詞的類別</li>
</ul>
<h4 id="">分詞效果-預設狀態</h4>
<p><strong>使用_analyze查看</strong></p>
<pre><code class="language-bash">root@ubuntu-87:~# curl 'http://localhost:9200/demo/_analyze?pretty=true' -H 'Content-Type: application/json' -d '{ &quot;field&quot;: &quot;content&quot;, &quot;text&quot;:&quot;Set the shape to semi-transparent by calling set_trans(5)&quot;}'

{
  &quot;tokens&quot; : [
    {
      &quot;token&quot; : &quot;set&quot;,
      &quot;start_offset&quot; : 0,
      &quot;end_offset&quot; : 3,
      &quot;type&quot; : &quot;&lt;ALPHANUM&gt;&quot;,
      &quot;position&quot; : 0
    },
    {
      &quot;token&quot; : &quot;the&quot;,
      &quot;start_offset&quot; : 4,
      &quot;end_offset&quot; : 7,
      &quot;type&quot; : &quot;&lt;ALPHANUM&gt;&quot;,
      &quot;position&quot; : 1
    },
    {
      &quot;token&quot; : &quot;shape&quot;,
      &quot;start_offset&quot; : 8,
      &quot;end_offset&quot; : 13,
      &quot;type&quot; : &quot;&lt;ALPHANUM&gt;&quot;,
      &quot;position&quot; : 2
    },
    {
      &quot;token&quot; : &quot;to&quot;,
      &quot;start_offset&quot; : 14,
      &quot;end_offset&quot; : 16,
      &quot;type&quot; : &quot;&lt;ALPHANUM&gt;&quot;,
      &quot;position&quot; : 3
    },
    {
      &quot;token&quot; : &quot;semi&quot;,
      &quot;start_offset&quot; : 17,
      &quot;end_offset&quot; : 21,
      &quot;type&quot; : &quot;&lt;ALPHANUM&gt;&quot;,
      &quot;position&quot; : 4
    },
    {
      &quot;token&quot; : &quot;transparent&quot;,
      &quot;start_offset&quot; : 22,
      &quot;end_offset&quot; : 33,
      &quot;type&quot; : &quot;&lt;ALPHANUM&gt;&quot;,
      &quot;position&quot; : 5
    },
    {
      &quot;token&quot; : &quot;by&quot;,
      &quot;start_offset&quot; : 34,
      &quot;end_offset&quot; : 36,
      &quot;type&quot; : &quot;&lt;ALPHANUM&gt;&quot;,
      &quot;position&quot; : 6
    },
    {
      &quot;token&quot; : &quot;calling&quot;,
      &quot;start_offset&quot; : 37,
      &quot;end_offset&quot; : 44,
      &quot;type&quot; : &quot;&lt;ALPHANUM&gt;&quot;,
      &quot;position&quot; : 7
    },
    {
      &quot;token&quot; : &quot;set_trans&quot;,
      &quot;start_offset&quot; : 45,
      &quot;end_offset&quot; : 54,
      &quot;type&quot; : &quot;&lt;ALPHANUM&gt;&quot;,
      &quot;position&quot; : 8
    },
    {
      &quot;token&quot; : &quot;5&quot;,
      &quot;start_offset&quot; : 55,
      &quot;end_offset&quot; : 56,
      &quot;type&quot; : &quot;&lt;NUM&gt;&quot;,
      &quot;position&quot; : 9
    }
  ]
}
</code></pre>
<p><strong>使用_termvectors查看</strong></p>
<pre><code class="language-bash">root@ubuntu-87:~# curl -H &quot;Content-Type:application/json&quot;  'http://localhost:9200/demo/demotype/1/_termvectors?pretty=true' -d '{&quot;fie
lds&quot; : [&quot;content&quot;]}'

{
  &quot;_index&quot; : &quot;demo&quot;,
  &quot;_type&quot; : &quot;demotype&quot;,
  &quot;_id&quot; : &quot;1&quot;,
  &quot;_version&quot; : 1,
  &quot;found&quot; : true,
  &quot;took&quot; : 0,
  &quot;term_vectors&quot; : {
    &quot;content&quot; : {
      &quot;field_statistics&quot; : {
        &quot;sum_doc_freq&quot; : 10,
        &quot;doc_count&quot; : 1,
        &quot;sum_ttf&quot; : 10
      },
      &quot;terms&quot; : {
        &quot;5&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 9,
              &quot;start_offset&quot; : 55,
              &quot;end_offset&quot; : 56
            }
          ]
        },
        &quot;by&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 6,
              &quot;start_offset&quot; : 34,
              &quot;end_offset&quot; : 36
            }
          ]
        },
        &quot;calling&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 7,
              &quot;start_offset&quot; : 37,
              &quot;end_offset&quot; : 44
            }
          ]
        },
        &quot;semi&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 4,
              &quot;start_offset&quot; : 17,
              &quot;end_offset&quot; : 21
            }
          ]
        },
        &quot;set&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 0,
              &quot;start_offset&quot; : 0,
              &quot;end_offset&quot; : 3
            }
          ]
        },
        &quot;set_trans&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 8,
              &quot;start_offset&quot; : 45,
              &quot;end_offset&quot; : 54
            }
          ]
        },
        &quot;shape&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 2,
              &quot;start_offset&quot; : 8,
              &quot;end_offset&quot; : 13
            }
          ]
        },
        &quot;the&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 1,
              &quot;start_offset&quot; : 4,
              &quot;end_offset&quot; : 7
            }
          ]
        },
        &quot;to&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 3,
              &quot;start_offset&quot; : 14,
              &quot;end_offset&quot; : 16
            }
          ]
        },
        &quot;transparent&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 5,
              &quot;start_offset&quot; : 22,
              &quot;end_offset&quot; : 33
            }
          ]
        }
      }
    }
  }
}
</code></pre>
<h4 id="">分詞效果-經過配置後</h4>
<p><strong>使用_analyze查看</strong></p>
<pre><code class="language-bash">root@ubuntu-87:~# curl 'http://localhost:9200/demo1/_analyze?pretty=true' -H 'Content-Type: application/json' -d '{ &quot;field&quot;: &quot;content
&quot;, &quot;text&quot;:&quot;Set the shape to semi-transparent by calling set_trans(5)&quot;}'
{
  &quot;tokens&quot; : [
    {
      &quot;token&quot; : &quot;set&quot;,
      &quot;start_offset&quot; : 0,
      &quot;end_offset&quot; : 3,
      &quot;type&quot; : &quot;&lt;ALPHANUM&gt;&quot;,
      &quot;position&quot; : 0
    },
    {
      &quot;token&quot; : &quot;shape&quot;,
      &quot;start_offset&quot; : 8,
      &quot;end_offset&quot; : 13,
      &quot;type&quot; : &quot;&lt;ALPHANUM&gt;&quot;,
      &quot;position&quot; : 2
    },
    {
      &quot;token&quot; : &quot;semi&quot;,
      &quot;start_offset&quot; : 17,
      &quot;end_offset&quot; : 21,
      &quot;type&quot; : &quot;&lt;ALPHANUM&gt;&quot;,
      &quot;position&quot; : 4
    },
    {
      &quot;token&quot; : &quot;transpar&quot;,
      &quot;start_offset&quot; : 22,
      &quot;end_offset&quot; : 33,
      &quot;type&quot; : &quot;&lt;ALPHANUM&gt;&quot;,
      &quot;position&quot; : 5
    },
    {
      &quot;token&quot; : &quot;call&quot;,
      &quot;start_offset&quot; : 37,
      &quot;end_offset&quot; : 44,
      &quot;type&quot; : &quot;&lt;ALPHANUM&gt;&quot;,
      &quot;position&quot; : 7
    },
    {
      &quot;token&quot; : &quot;set_tran&quot;,
      &quot;start_offset&quot; : 45,
      &quot;end_offset&quot; : 54,
      &quot;type&quot; : &quot;&lt;ALPHANUM&gt;&quot;,
      &quot;position&quot; : 8
    },
    {
      &quot;token&quot; : &quot;5&quot;,
      &quot;start_offset&quot; : 55,
      &quot;end_offset&quot; : 56,
      &quot;type&quot; : &quot;&lt;NUM&gt;&quot;,
      &quot;position&quot; : 9
    }
  ]
}
</code></pre>
<p><strong>使用_termvectors查看</strong></p>
<pre><code class="language-bash">root@ubuntu-87:~# curl -H &quot;Content-Type:application/json&quot;  'http://localhost:9200/demo1/demotype/1/_termvectors?pretty=true' -d '{&quot;fi
elds&quot; : [&quot;content&quot;]}'
{
  &quot;_index&quot; : &quot;demo1&quot;,
  &quot;_type&quot; : &quot;demotype&quot;,
  &quot;_id&quot; : &quot;1&quot;,
  &quot;_version&quot; : 1,
  &quot;found&quot; : true,
  &quot;took&quot; : 1,
  &quot;term_vectors&quot; : {
    &quot;content&quot; : {
      &quot;field_statistics&quot; : {
        &quot;sum_doc_freq&quot; : 7,
        &quot;doc_count&quot; : 1,
        &quot;sum_ttf&quot; : 7
      },
      &quot;terms&quot; : {
        &quot;5&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 9,
              &quot;start_offset&quot; : 55,
              &quot;end_offset&quot; : 56
            }
          ]
        },
        &quot;call&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 7,
              &quot;start_offset&quot; : 37,
              &quot;end_offset&quot; : 44
            }
          ]
        },
        &quot;semi&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 4,
              &quot;start_offset&quot; : 17,
              &quot;end_offset&quot; : 21
            }
          ]
        },
        &quot;set&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 0,
              &quot;start_offset&quot; : 0,
              &quot;end_offset&quot; : 3
            }
          ]
        },
        &quot;set_tran&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 8,
              &quot;start_offset&quot; : 45,
              &quot;end_offset&quot; : 54
            }
          ]
        },
        &quot;shape&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 2,
              &quot;start_offset&quot; : 8,
              &quot;end_offset&quot; : 13
            }
          ]
        },
        &quot;transpar&quot; : {
          &quot;term_freq&quot; : 1,
          &quot;tokens&quot; : [
            {
              &quot;position&quot; : 5,
              &quot;start_offset&quot; : 22,
              &quot;end_offset&quot; : 33
            }
          ]
        }
      }
    }
  }
}
</code></pre>
<hr>
<h3 id="">尾聲</h3>
<p>這樣基礎的分析器概念就說完囉, 之後會在新增中文分析器的利器<code>ik分析器</code>～</p>
<p></p>]]></content:encoded></item><item><title><![CDATA[使用Nginx作為緩存伺服器(Cache Server)]]></title><description><![CDATA[<h3 id="cacheserver">緩存伺服器 (Cache Server)</h3>
<p>緩存伺服器是用來減輕server loading/traffic的, 怎麼說呢？因為他會將使用者對server的請求結果, 緩存在自己身上; 這樣一來我們的server就不需要對於同樣的請求一直回覆, 因為緩存伺服器會代替我們回覆！</p>
<hr>
<h3 id="">架構解說</h3>
<pre><code class="language-none">第一次請求: 因為CacheServer上面沒資料, 所以回源獲取資料
User  --&gt;  Cache Server  --&gt;  Origin Server
User  &lt;--  Cache Server  &lt;--  Origin Server

第二次請求: CacheServer上面已經有緩存了, 直接代理回應
User  --&gt;  Cache Server    Origin Server
User  &lt;--  Cache Server    Origin Server</code></pre>]]></description><link>https://tomme.me/nginx-proxy-cache-server/</link><guid isPermaLink="false">5c457e185adef55e9e5ce64e</guid><category><![CDATA[Nginx]]></category><category><![CDATA[Proxy]]></category><category><![CDATA[Cache]]></category><dc:creator><![CDATA[Tommy Lin]]></dc:creator><pubDate>Mon, 21 Jan 2019 08:21:14 GMT</pubDate><content:encoded><![CDATA[<h3 id="cacheserver">緩存伺服器 (Cache Server)</h3>
<p>緩存伺服器是用來減輕server loading/traffic的, 怎麼說呢？因為他會將使用者對server的請求結果, 緩存在自己身上; 這樣一來我們的server就不需要對於同樣的請求一直回覆, 因為緩存伺服器會代替我們回覆！</p>
<hr>
<h3 id="">架構解說</h3>
<pre><code class="language-none">第一次請求: 因為CacheServer上面沒資料, 所以回源獲取資料
User  --&gt;  Cache Server  --&gt;  Origin Server
User  &lt;--  Cache Server  &lt;--  Origin Server

第二次請求: CacheServer上面已經有緩存了, 直接代理回應
User  --&gt;  Cache Server    Origin Server
User  &lt;--  Cache Server    Origin Server
</code></pre>
<hr>
<h3 id="">實際配置</h3>
<h4 id="">環境介紹</h4>
<p>這次的lab我們會用兩台ubuntu來示範, 兩台都會安裝nginx<br>
proxy.demosite.com: 緩存伺服器的域名<br>
origin.demosite.com: 源站的域名</p>
<h4 id="">緩存伺服器配置</h4>
<p>首先編輯<code>/etc/nginx/conf.d/cache.conf</code>, 這是全域的快取配置, 內容如下</p>
<pre><code class="language-markup">proxy_cache_path /tmp/nginx/cache levels=1:2 keys_zone=myzone:10m inactive=1d max_size=10g;
proxy_cache_key '$scheme$host$request_uri';
</code></pre>
<p>接著編輯<code>/etc/nginx/sites-enabled/proxy.conf</code> 起一個新的site, 內容如下</p>
<pre><code class="language-none">server {
    listen 80;

    server_name proxy.demosite.com;

    location ~ .*\.(html|png)$ {
        proxy_cache myzone;
        proxy_cache_valid  any 100m;
        proxy_pass http://origin.demosite.com;
    }

    location /info {
        proxy_cache myzone;
        expires -1;

        proxy_pass http://origin.demosite.com;
    }

    add_header X-Cache-Status $upstream_cache_status;
}
</code></pre>
<p>配置的緩存路徑資料夾要存在嘿: <code>mkdir -p /tmp/nginx/cache</code><br>
配置完成就可以重新載入配置文件<code>nginx -s reload</code></p>
<h4 id="">源伺服器配置</h4>
<p>源伺服器不需要做其他額外的配置啦～</p>
<hr>
<h3 id="">配置解說</h3>
<p>這邊解釋所使用到的基本配置參數, 更完整的內容請參閱<a href="http://nginx.org/en/docs/http/ngx_http_proxy_module.html">Module ngx_http_proxy_module</a></p>
<h4 id="">全域快取配置</h4>
<p>先看到上面<code>/etc/nginx/conf.d/cache.conf</code>這個檔案裡面的內容<br>
<code>proxy_cache_path</code>: 緩存路徑, 要把緩存的內容放在哪裡<br>
<code>levels=1:2</code>: 緩存的目錄結構<br>
<code>keys_zone=myzone:10m</code>: zone的名字和可使用記憶體大小, 配至1MB的zone大約可以存8000筆鍵值的快取<br>
<code>inactive=1d</code>: 如果一天之內沒人存取, 就會從自己身上刪除啦<br>
<code>max_size=10g</code>: 允許在身上存放的硬碟容量<br>
<code>proxy_cache_key '$scheme$host$request_uri$is_args$args';</code>: 每筆快取儲存的鍵值方式</p>
<h4 id="virtualsite">virtual site配置</h4>
<p>這配置就是<code>/etc/nginx/sites-enabled/proxy.conf</code><br>
<code>proxy_cache myzone;</code>: 代表要使用剛剛定義的zone<br>
<code>proxy_cache_valid any 100m;</code>: 對於任何結果緩存100分鐘<br>
<code>expires -1;</code>: 過期時間 -1, 就是不緩存啦～<br>
<code>add_header X-Cache-Status $upstream_cache_status;</code>: 增加回應的header, 這樣可以清楚知道有沒有中快取</p>
<hr>
<h3 id="">驗證測試</h3>
<p>重新載入nginx之後, 就可以來測試我們的緩存狀況囉～</p>
<h4 id="">觀察緩存路徑</h4>
<p>先看下我們的緩存路徑, 目前空空如也</p>
<pre><code class="language-bash">root@ubuntu-87:/etc/nginx/conf.d# tree /tmp/nginx/cache/
/tmp/nginx/cache/

0 directories, 0 files
</code></pre>
<h4 id="">首次請求緩存伺服器</h4>
<p>接著對緩存伺服器做一次請求看看</p>
<pre><code class="language-bash">❯ curl -v proxy.demosite.com
* Rebuilt URL to: proxy.demosite.com/
*   Trying 192.168.41.87...
* TCP_NODELAY set
* Connected to proxy.demosite.com (192.168.41.87) port 80 (#0)
&gt; GET / HTTP/1.1
&gt; Host: proxy.demosite.com
&gt; User-Agent: curl/7.54.0
&gt; Accept: */*
&gt;
&lt; HTTP/1.1 200 OK
&lt; Server: nginx/1.10.3 (Ubuntu)
&lt; Date: Mon, 21 Jan 2019 08:57:37 GMT
&lt; Content-Type: text/html
&lt; Content-Length: 334
&lt; Connection: keep-alive
&lt; Last-Modified: Tue, 15 Jan 2019 04:20:55 GMT
&lt; ETag: &quot;5c3d5fa7-14e&quot;
&lt; X-Cache-Status: MISS
&lt; Accept-Ranges: bytes
&lt;
&lt;html&gt;
  &lt;body&gt;
  this is 41.166
  gun.png
  &lt;img src=&quot;gun.jpg&quot; width=&quot;50px&quot; height=&quot;50px&quot;&gt;
  &lt;br&gt;

  about/about.png
  &lt;img src=&quot;about/about.png&quot; width=&quot;50px&quot; height=&quot;50px&quot;&gt;
  &lt;br&gt;

  info/info.png
  &lt;img src=&quot;info/info.png&quot; width=&quot;50px&quot; height=&quot;50px&quot;&gt;
  &lt;br&gt;

  &lt;a href=&quot;about&quot;&gt;about&lt;/a&gt;
  &lt;a href=&quot;info&quot;&gt;info&lt;/a&gt;
  &lt;/body&gt;
&lt;/html&gt;
* Connection #0 to host proxy.demosite.com left intact

</code></pre>
<p>大家注意看到這時候表頭多了這個<code>X-Cache-Status: MISS</code><br>
代表</p>
<ol>
<li>我們的緩存配置生效囉</li>
<li><code>MISS</code>代表身上沒緩存, 因此回源獲取資料; 反之則為<code>HIT</code></li>
</ol>
<h4 id="">再次查看緩存路徑</h4>
<p>經過請求之後, 我們再次查看緩存路徑. 已經有一筆資料囉！<br>
而這個目錄結構就是我們上面配置的<code>level=1:2</code></p>
<pre><code class="language-bash">root@ubuntu-87:/etc/nginx/conf.d# tree /tmp/nginx/cache/
/tmp/nginx/cache/
└── 3
    └── 51
        └── 600a83b12cb107e844ddd14077759513
</code></pre>
<h4 id="">第二次請求緩存伺服器</h4>
<p>第二次存取的時候因為緩存伺服器身上已經有資料了, 所以就得到<code>HIT</code>囉！</p>
<pre><code class="language-bash">❯ curl -v &quot;proxy.demosite.com&quot; 2&gt;&amp;1 | grep X-Cache-Status
&lt; X-Cache-Status: HIT

</code></pre>
<h3 id="proxy_cache_key">緩存解說 proxy_cache_key</h3>
<p>這部分對於緩存的正確性以及後續的清理緩存還是比較關鍵的, 因此拉出來解釋一下.</p>
<h4 id="">鍵值意義</h4>
<p>對於每一筆請求, 我們的緩存伺服器是如何知道自己身上有沒有緩存呢？ 沒錯就是看<code>proxy_cache_key</code>！<br>
還記得上面我們配置<code>proxy_cache_key '$scheme$host$request_uri$is_args$args';</code></p>
<p>以我們剛剛的請求來說, <code>proxy.demosite.com</code> , 產生的鍵值就會是 <code>httpproxy.demosite.com/</code></p>
<pre><code class="language-none">(http)  proxy.demosite.com (/)
$scheme $host              $request_uri $is_args$args
</code></pre>
<br>
<p>我們可以看一下剛才產生的緩存文件, 這個快取文件就是完整的html檔案, 裡面就說明了他的鍵值<br>
<code>KEY: httpproxy.demosite.com/</code></p>
<pre><code class="language-bash">root@ubuntu-87:/etc/nginx/conf.d# cat /tmp/nginx/cache/3/51/600a83b12cb107e844ddd14077759513
0E\_=\E\G&quot;5c3d5fa7-14e&quot;
KEY: httpproxy.demosite.com/
HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Mon, 21 Jan 2019 09:07:12 GMT
Content-Type: text/html
Content-Length: 334
Last-Modified: Tue, 15 Jan 2019 04:20:55 GMT
Connection: close
ETag: &quot;5c3d5fa7-14e&quot;
Accept-Ranges: bytes

&lt;html&gt;
  &lt;body&gt;
  this is 41.166
  gun.png
  &lt;img src=&quot;gun.jpg&quot; width=&quot;50px&quot; height=&quot;50px&quot;&gt;
  &lt;br&gt;

  about/about.png
  &lt;img src=&quot;about/about.png&quot; width=&quot;50px&quot; height=&quot;50px&quot;&gt;
  &lt;br&gt;

  info/info.png
  &lt;img src=&quot;info/info.png&quot; width=&quot;50px&quot; height=&quot;50px&quot;&gt;
  &lt;br&gt;

  &lt;a href=&quot;about&quot;&gt;about&lt;/a&gt;
  &lt;a href=&quot;info&quot;&gt;info&lt;/a&gt;
  &lt;/body&gt;
&lt;/html&gt;

</code></pre>
<p>所以 <strong>只要使用者請求的Key不同, nginx就可以生成不同的快取文件</strong><br>
換句話說, 如果您的網站下面這兩個請求應該得到不同的結果,</p>
<ul>
<li>proxy.demosite.com?name=foo</li>
<li>proxy.demosite.com?name=bar<br>
您對於<code>proxy_cache_key</code>的定義就必須精細到<code>arg</code>, 否則nginx會忽略存取參數; 一直以為是同樣的一份文件唷！</li>
</ul>
<p>而如果您的網站http/https可以不用視為兩份緩存, 那key就可以省略scheme這個變數囉！</p>
<h4 id="">鍵值計算</h4>
<p>那麼md5怎麼來的呢, 就是使用Key得到的; 我們可以驗證看看</p>
<pre><code class="language-bash">❯ echo -n 'httpproxy.demosite.com/' | md5
600a83b12cb107e844ddd14077759513
</code></pre>
<h3 id="">清理緩存</h3>
<p>所以說, 配置了緩存就要可以清理緩存; 否則有時候網站有更新, 使用者還是會持續看到舊的資料.</p>
<h4 id="">方法一: 刪除整個目錄</h4>
<p>此方法最為直觀簡易啦～ 整個緩存全部清除, 也不用想太多<br>
<code>rm -rf /tmp/nginx/cache/*</code><br>
<code>nginx -s reload</code></p>
<p>優點: 快速簡易<br>
缺點: 無法控制要刪除的項目</p>
<blockquote>
<p>這個方法適用於網站單純的情況下, 不需要考慮如果緩存伺服器突然失去所有檔案, 進而突發回源造成大量存取</p>
</blockquote>
<h4 id="">方法二: 刪除指定目錄/域名</h4>
<p>什麼是刪除指定目錄呢？ 比如我們今天想刪除 /info 下面所有的緩存<br>
或是比如我們的緩存伺服器同時有兩個網站, 而我只想對單一網站進行緩存清理; 就可以使用這個方法囉！</p>
<ol>
<li>首先我們理解我們的緩存檔案裡面都會存放我們定義的鍵值</li>
<li>找出鍵值符合的檔案</li>
<li>批量刪除</li>
</ol>
<p>先看一下; 目前我的緩存共有四筆, 其中的鍵值也都取出來觀察</p>
<pre><code class="language-bash">root@ubuntu-87:/etc/nginx/conf.d# tree /tmp/nginx/cache/
/tmp/nginx/cache/
├── 3
│   ├── 51
│   │   └── 600a83b12cb107e844ddd14077759513
│   └── 9b
│       └── 3ace4f08b744df7310abeb90109e19b3
├── 5
│   └── d8
│       └── 80af67f39abf128943025ab5e1498d85
└── c
    └── 19
        └── 53e313c4a7c020ab414089c28c4c419c


root@ubuntu-87:/etc/nginx/conf.d# grep -ar &quot;KEY: httpproxy.demosit
e.com/&quot; /tmp/nginx/cache
/tmp/nginx/cache/3/51/600a83b12cb107e844ddd14077759513:KEY: httpproxy.demosite.com/
/tmp/nginx/cache/3/9b/3ace4f08b744df7310abeb90109e19b3:KEY: httpproxy.demosite.com/about/about.png
/tmp/nginx/cache/5/d8/80af67f39abf128943025ab5e1498d85:KEY: httpproxy.demosite.com/info/info.png
/tmp/nginx/cache/c/19/53e313c4a7c020ab414089c28c4c419c:KEY: httpproxy.demosite.com/info/info.html
</code></pre>
<p>四筆鍵值記錄分別是</p>
<ul>
<li>httpproxy.demosite.com/</li>
<li>httpproxy.demosite.com/about/about.png</li>
<li>httpproxy.demosite.com/info/info.png</li>
<li>httpproxy.demosite.com/info/info.html</li>
</ul>
<p><strong>實際操作: 清除 /info 下面的所有緩存</strong><br>
<code>grep -alr &quot;KEY: httpproxy.demosite.com/info&quot; /tmp/nginx/cache | xargs rm</code></p>
<br>
<p>這時候再看一下我們的緩存目錄, 該目錄之下的緩存都清除完畢囉！</p>
<pre><code class="language-bash">root@ubuntu-87:/etc/nginx/conf.d# tree /tmp/nginx/cache/
/tmp/nginx/cache/
├── 3
│   ├── 51
│   │   └── 600a83b12cb107e844ddd14077759513
│   └── 9b
│       └── 3ace4f08b744df7310abeb90109e19b3
├── 5
│   └── d8
└── c
    └── 19
</code></pre>
<hr>
<h3 id="">結語</h3>
<p>我們下次見啦～</p>
<p></p><p></p><p></p><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[Elasticsearch 聚合基礎(二): 度量聚合(metric)]]></title><description><![CDATA[<h3 id="elasticsearch">Elasticsearch 聚合搜尋: 度量計算</h3>
<p>什麼是度量計算？ 就是將抓取出來的資料, 做加總、取平均、抓最大最小等等..<br>
當然其他還有很多, 不過這邊就介紹最常用的部分～ 有興趣看<a href="https://www.elastic.co/guide/cn/elasticsearch/guide/current/_adding_a_metric_to_the_mix.html">添加度量指标| Elasticsearch: 权威指南| Elastic</a></p>
<h4 id="">範例: 統計班級+性別+分數</h4>
<p>假設我們的原始資料結構如下</p>
<table>
<thead>
<tr>
<th>class</th>
<th>gender</th>
<th>score</th>
</tr>
</thead>
<tbody>
<tr>
<td>A</td>
<td>Girl</td>
<td>81</td>
</tr>
<tr>
<td>C</td>
<td>Boy</td>
<td>54</td>
</tr>
<tr>
<td>B</td>
<td>Girl</td>
<td>63</td>
</tr>
<tr>
<td>B</td>
<td>Boy</td>
<td>71</td>
</tr>
<tr>
<td>C</td>
<td>Boy</td>
<td>24</td>
</tr>
<tr>
<td>C</td>
<td>Girl</td>
<td>93</td>
</tr>
<tr>
<td>C</td>
<td>Boy</td>
<td>85</td>
</tr>
</tbody>
</table>
<p>我們的目標是計算出:</p>
<ol>
<li>個班級的平均分數</li>
<li>個班級男生女生的平均分數</li>
<li>找出每個班級分數最高的人, 和其性別</li>
</ol>
<hr>
<br>
<h4 id="">建立資料</h4>]]></description><link>https://tomme.me/elasticsearch-aggregation-basic-group-by-metric/</link><guid isPermaLink="false">5c04e5504f51f17314869bf4</guid><category><![CDATA[ELK]]></category><category><![CDATA[Elasticsearch]]></category><dc:creator><![CDATA[Tommy Lin]]></dc:creator><pubDate>Mon, 03 Dec 2018 09:21:39 GMT</pubDate><content:encoded><![CDATA[<h3 id="elasticsearch">Elasticsearch 聚合搜尋: 度量計算</h3>
<p>什麼是度量計算？ 就是將抓取出來的資料, 做加總、取平均、抓最大最小等等..<br>
當然其他還有很多, 不過這邊就介紹最常用的部分～ 有興趣看<a href="https://www.elastic.co/guide/cn/elasticsearch/guide/current/_adding_a_metric_to_the_mix.html">添加度量指标| Elasticsearch: 权威指南| Elastic</a></p>
<h4 id="">範例: 統計班級+性別+分數</h4>
<p>假設我們的原始資料結構如下</p>
<table>
<thead>
<tr>
<th>class</th>
<th>gender</th>
<th>score</th>
</tr>
</thead>
<tbody>
<tr>
<td>A</td>
<td>Girl</td>
<td>81</td>
</tr>
<tr>
<td>C</td>
<td>Boy</td>
<td>54</td>
</tr>
<tr>
<td>B</td>
<td>Girl</td>
<td>63</td>
</tr>
<tr>
<td>B</td>
<td>Boy</td>
<td>71</td>
</tr>
<tr>
<td>C</td>
<td>Boy</td>
<td>24</td>
</tr>
<tr>
<td>C</td>
<td>Girl</td>
<td>93</td>
</tr>
<tr>
<td>C</td>
<td>Boy</td>
<td>85</td>
</tr>
</tbody>
</table>
<p>我們的目標是計算出:</p>
<ol>
<li>個班級的平均分數</li>
<li>個班級男生女生的平均分數</li>
<li>找出每個班級分數最高的人, 和其性別</li>
</ol>
<hr>
<br>
<h4 id="">建立資料</h4>
<p>先在這邊下載原始資料: <a href="https://tomme.me/demo/elasticsearch/students.json">students.json</a>,<br>
<em>注意最後一行要空白</em></p>
<p>然後將他上傳: curl -H &quot;Content-Type: Application/json&quot; -XPOST &quot;192.168.40.41:9200/students/doc/_bulk&quot; --data-binary @students.json</p>
<p><em>大家記得把 elasticsearch ip 換成自己的~</em></p>
<p>然後看一下是不是所有學生都進去了; 很好！ 50個學生all in.</p>
<pre><code>❯ curl http://192.168.40.41:9200/_cat/indices\?v
health status index               uuid                   pri rep docs.count docs.deleted store.size pri.store.size
green  open   students            0SjW3-EeQ5-uI7KiUNQ8uQ   3   1         50            0     15.7kb           690b
</code></pre>
<hr>
<br>
<h4 id="">聚合思路</h4>
<p>要取得每個班級學生的平均分數,無法一步到位; 這邊就一步一步來看</p>
<ol>
<li><strong>先依照班級分類</strong><br>
這邊就要使用<a href="https://tomme.me/elasticsearch-aggregation-basic-group-by-bucketing/">聚合分組</a>, 還不知道怎麼操作的同學跟著連結複習一下~</li>
</ol>
<p>第一步我們要把班級給分類出來, 那麼依照上一篇的思路我們的結構應該如下</p>
<pre><code>{
  聚合: {
    以班級分類: {
      詞分類: {
        欄位: 班級
      }
    }
  }
}
</code></pre>
<br>
<p>1-1. <strong>實際操作</strong></p>
<pre><code>curl -s -H &quot;Content-Type: Application/json&quot; -XPOST &quot;http://192.168.40.41:9200/students/_search&quot; -d '
{
  &quot;size&quot;: 0,
  &quot;query&quot;: {
    &quot;bool&quot;: {}
  },
  &quot;aggs&quot;: {
    &quot;group_by_class&quot;: {
      &quot;terms&quot;: {
        &quot;field&quot;: &quot;class.keyword&quot;
      }
    }
  }
}
' | jq .

{
  &quot;took&quot;: 297,
  &quot;timed_out&quot;: false,
  &quot;_shards&quot;: {
    &quot;total&quot;: 3,
    &quot;successful&quot;: 3,
    &quot;skipped&quot;: 0,
    &quot;failed&quot;: 0
  },
  &quot;hits&quot;: {
    &quot;total&quot;: 50,
    &quot;max_score&quot;: 0,
    &quot;hits&quot;: []
  },
  &quot;aggregations&quot;: {
    &quot;group_by_class&quot;: {
      &quot;doc_count_error_upper_bound&quot;: 0,
      &quot;sum_other_doc_count&quot;: 0,
      &quot;buckets&quot;: [
        {
          &quot;key&quot;: &quot;C&quot;,
          &quot;doc_count&quot;: 20
        },
        {
          &quot;key&quot;: &quot;A&quot;,
          &quot;doc_count&quot;: 13
        },
        {
          &quot;key&quot;: &quot;D&quot;,
          &quot;doc_count&quot;: 9
        },
        {
          &quot;key&quot;: &quot;B&quot;,
          &quot;doc_count&quot;: 8
        }
      ]
    }
  }
}
</code></pre>
<br>
<ol start="2">
<li><strong>分數計算</strong><br>
光是上面那樣分類班級的結果, 肯定是無法滿足我們的目標的. 只能知道為什麼B班級的人特別少XD</li>
</ol>
<p>沒關係, 咱繼續聚合下去～</p>
<p>複習一下聚合的結構</p>
<pre><code>{
  聚合: {
    &lt;聚合名字自取&gt;: {
      &lt;聚合種類&gt;: {
        &lt;聚合欄位&gt;: &quot;&quot;
      }
    }
  }
}
</code></pre>
<p>那麼要這次的目標, 他應該變成這樣</p>
<pre><code>{
  聚合: {
    平均分數: {
      平均: {
        欄位: 分數
      }
    }
  }
}
</code></pre>
<br>
<p>2-1. <strong>結合兩個聚合</strong></p>
<p>所以說這邊我們要先分類班級, 然後計算分數; 組裝起來會變成這樣</p>
<pre><code>{
  聚合: {
    以班級分類: {
      詞分類: {
        欄位: 班級
      },
      聚合: {
        平均分數: {
          平均: {
            欄位: 分數
          }
        }
      }
    }
  }
}
</code></pre>
<br>
<p>2-2. <strong>實際操作</strong></p>
<pre><code>curl -s -H &quot;Content-Type: Application/json&quot; -XPOST &quot;http://192.168.40.41:9200/students/_search&quot; -d '
{
  &quot;size&quot;: 0,
  &quot;query&quot;: {
    &quot;bool&quot;: {}
  },
  &quot;aggs&quot;: {
    &quot;group_by_class&quot;: {
      &quot;terms&quot;: {
        &quot;field&quot;: &quot;class.keyword&quot;
      },
      &quot;aggs&quot;: {
        &quot;avg_score&quot;: {
          &quot;avg&quot;: {
            &quot;field&quot;: &quot;score&quot;
          }
        }
      }
    }
  }
}
' | jq .

{
  &quot;took&quot;: 10365,
  &quot;timed_out&quot;: false,
  &quot;_shards&quot;: {
    &quot;total&quot;: 3,
    &quot;successful&quot;: 3,
    &quot;skipped&quot;: 0,
    &quot;failed&quot;: 0
  },
  &quot;hits&quot;: {
    &quot;total&quot;: 50,
    &quot;max_score&quot;: 0,
    &quot;hits&quot;: []
  },
  &quot;aggregations&quot;: {
    &quot;group_by_class&quot;: {
      &quot;doc_count_error_upper_bound&quot;: 0,
      &quot;sum_other_doc_count&quot;: 0,
      &quot;buckets&quot;: [
        {
          &quot;key&quot;: &quot;C&quot;,
          &quot;doc_count&quot;: 20,
          &quot;avg_score&quot;: {
            &quot;value&quot;: 62.75
          }
        },
        {
          &quot;key&quot;: &quot;A&quot;,
          &quot;doc_count&quot;: 13,
          &quot;avg_score&quot;: {
            &quot;value&quot;: 60.76923076923077
          }
        },
        {
          &quot;key&quot;: &quot;D&quot;,
          &quot;doc_count&quot;: 9,
          &quot;avg_score&quot;: {
            &quot;value&quot;: 71.77777777777777
          }
        },
        {
          &quot;key&quot;: &quot;B&quot;,
          &quot;doc_count&quot;: 8,
          &quot;avg_score&quot;: {
            &quot;value&quot;: 73.625
          }
        }
      ]
    }
  }
}
</code></pre>
<p>到目前為止; 已經算出每個班級的平均啦</p>
<hr>
<br>
<ol start="3">
<li><strong>再使用性別作為分類</strong><br>
到目前有跟上的同學, 一定腦筋很快的已經寫好了聚合了<br>
就在堆一個以性別做分類下去就對啦～～</li>
</ol>
<pre><code>{
  &quot;aggs&quot;: {
    &quot;group_by_class&quot;: {
      &quot;terms&quot;: {
        &quot;field&quot;: &quot;class.keyword&quot;
      },
      &quot;aggs&quot;: {
        &quot;avg_score&quot;: {
          &quot;avg&quot;: {
            &quot;field&quot;: &quot;score&quot;
          },
          &quot;aggs&quot;: {
            &quot;group_by_gender&quot;: {
              &quot;terms&quot;: {
                &quot;field&quot;: &quot;gender.keyword&quot;
              }
            }
          }
        }
      }
    }
  }
}
</code></pre>
<p>噗.. 結果得到一個&quot;avg不接受子聚合&quot;</p>
<pre><code>{
  &quot;error&quot;: {
    &quot;root_cause&quot;: [
      {
        &quot;type&quot;: &quot;aggregation_initialization_exception&quot;,
        &quot;reason&quot;: &quot;Aggregator [avg_score] of type [avg] cannot accept sub-aggregations&quot;
      }
    ],
    &quot;type&quot;: &quot;aggregation_initialization_exception&quot;,
    &quot;reason&quot;: &quot;Aggregator [avg_score] of type [avg] cannot accept sub-aggregations&quot;
  },
  &quot;status&quot;: 500
}
</code></pre>
<br>
沒關係; 那咱們把它寫在上面; 結構如下
<pre><code>{
  聚合: {
    以班級分類: {
      詞分類: {
        欄位: 班級
      },
      聚合: {
        以性別分類: {
	      詞分類: {
            欄位: 性別
          },
          聚合: {
            平均分數: {
              平均: {
                欄位: 分數
              }
            }
          }
        }
      }
    }
  }
}
</code></pre>
<br>
<p>3-1. <strong>實際操作</strong></p>
<pre><code>curl -s -H &quot;Content-Type: Application/json&quot; -XPOST &quot;http://192.168.40.41:9200/students/_search&quot; -d '
{
  &quot;size&quot;: 0,
  &quot;query&quot;: {
    &quot;bool&quot;: {}
  },
  &quot;aggs&quot;: {
    &quot;group_by_class&quot;: {
      &quot;terms&quot;: {
        &quot;field&quot;: &quot;class.keyword&quot;
      },
      &quot;aggs&quot;: {
        &quot;group_by_gender&quot;: {
          &quot;terms&quot;: {
            &quot;field&quot;: &quot;gender.keyword&quot;
          },
          &quot;aggs&quot;: {
            &quot;avg_score&quot;: {
              &quot;avg&quot;: {
                &quot;field&quot;: &quot;score&quot;
              }
            }
          }
        }
      }
    }
  }
}
' | jq .


{
  &quot;took&quot;: 561,
  &quot;timed_out&quot;: false,
  &quot;_shards&quot;: {
    &quot;total&quot;: 3,
    &quot;successful&quot;: 3,
    &quot;skipped&quot;: 0,
    &quot;failed&quot;: 0
  },
  &quot;hits&quot;: {
    &quot;total&quot;: 50,
    &quot;max_score&quot;: 0,
    &quot;hits&quot;: []
  },
  &quot;aggregations&quot;: {
    &quot;group_by_class&quot;: {
      &quot;doc_count_error_upper_bound&quot;: 0,
      &quot;sum_other_doc_count&quot;: 0,
      &quot;buckets&quot;: [
        {
          &quot;key&quot;: &quot;C&quot;,
          &quot;doc_count&quot;: 20,
          &quot;group_by_gender&quot;: {
            &quot;doc_count_error_upper_bound&quot;: 0,
            &quot;sum_other_doc_count&quot;: 0,
            &quot;buckets&quot;: [
              {
                &quot;key&quot;: &quot;Boy&quot;,
                &quot;doc_count&quot;: 15,
                &quot;avg_score&quot;: {
                  &quot;value&quot;: 60.06666666666667
                }
              },
              {
                &quot;key&quot;: &quot;Girl&quot;,
                &quot;doc_count&quot;: 5,
                &quot;avg_score&quot;: {
                  &quot;value&quot;: 70.8
                }
              }
            ]
          }
        },
        {
          &quot;key&quot;: &quot;A&quot;,
          &quot;doc_count&quot;: 13,
          &quot;group_by_gender&quot;: {
            &quot;doc_count_error_upper_bound&quot;: 0,
            &quot;sum_other_doc_count&quot;: 0,
            &quot;buckets&quot;: [
              {
                &quot;key&quot;: &quot;Girl&quot;,
                &quot;doc_count&quot;: 7,
                &quot;avg_score&quot;: {
                  &quot;value&quot;: 71.42857142857143
                }
              },
              {
                &quot;key&quot;: &quot;Boy&quot;,
                &quot;doc_count&quot;: 6,
                &quot;avg_score&quot;: {
                  &quot;value&quot;: 48.333333333333336
                }
              }
            ]
          }
        },
        {
          &quot;key&quot;: &quot;D&quot;,
          &quot;doc_count&quot;: 9,
          &quot;group_by_gender&quot;: {
            &quot;doc_count_error_upper_bound&quot;: 0,
            &quot;sum_other_doc_count&quot;: 0,
            &quot;buckets&quot;: [
              {
                &quot;key&quot;: &quot;Boy&quot;,
                &quot;doc_count&quot;: 5,
                &quot;avg_score&quot;: {
                  &quot;value&quot;: 65.6
                }
              },
              {
                &quot;key&quot;: &quot;Girl&quot;,
                &quot;doc_count&quot;: 4,
                &quot;avg_score&quot;: {
                  &quot;value&quot;: 79.5
                }
              }
            ]
          }
        },
        {
          &quot;key&quot;: &quot;B&quot;,
          &quot;doc_count&quot;: 8,
          &quot;group_by_gender&quot;: {
            &quot;doc_count_error_upper_bound&quot;: 0,
            &quot;sum_other_doc_count&quot;: 0,
            &quot;buckets&quot;: [
              {
                &quot;key&quot;: &quot;Girl&quot;,
                &quot;doc_count&quot;: 6,
                &quot;avg_score&quot;: {
                  &quot;value&quot;: 73.33333333333333
                }
              },
              {
                &quot;key&quot;: &quot;Boy&quot;,
                &quot;doc_count&quot;: 2,
                &quot;avg_score&quot;: {
                  &quot;value&quot;: 74.5
                }
              }
            ]
          }
        }
      ]
    }
  }
}
</code></pre>
<p>到這邊為止, 已經成功達成&quot;以班級作為分類, 每個班級男生女生的平均分數了&quot;.</p>
<br>
<h4 id="">最後一個: 得分最高者</h4>
<p>哈.. 只要把&quot;平均&quot;, 也就是avg的地方改成max就好啦！<br>
這邊就不佔版面了, 請同學自行試試看囉～</p>
<p>謝謝大家～ 下課！</p>
<h3></h3>]]></content:encoded></item><item><title><![CDATA[Elasticsearch 聚合基礎(一): 分组聚合(bucketing)]]></title><description><![CDATA[<h3 id="elasticsearch">Elasticsearch 聚合搜尋: 分組</h3>
<p>Elasticsearch 的聚合搜尋, 可以說是最常的用的功能了. 什麼是聚合搜尋呢？ 就是針對搜尋出來的結果, 再去做計算. 比如可以計算最大值、平均值、最小值、總和、95%、分組、累加... 等等的計算.</p>
<p>這篇從基礎的分組開始帶大家了解如何做聚合搜尋, 以及他的概念.</p>
<br>
<h3 id="">範例: 球球分類</h3>
<p><img src="https://tomme.me/content/images/2018/12/Screen-Shot-2018-12-03-at-1.20.50-PM.png" alt="Screen-Shot-2018-12-03-at-1.20.50-PM"></p>
<p>首先, 我們有一堆球球; 這些球球都有自己個別的元素, 包含形狀和顏色. 所謂分類就是讓有相同屬性的球球分到一類, 比如相同的形狀或是相同的顏色.</p>
<p>透過分類, 我們可以得到</p>
<ol>
<li>這一坨球球裡面有哪些形狀？</li>
<li>每個形狀有幾顆球球?</li>
<li>每個形狀, 有幾個不同顏色的球球？</li>
</ol>
<br>
<h4 id="">建立資料</h4>
<p>那就先把資料建立進去吧, 還沒安裝的朋友請參考<a href="https://tomme.me/hello-world-series-elasticsearch/">Hello World 系列 - Elasticsearch</a>.</p>
<p>我們用批量上傳的方式來丟資料, 可以在這邊下載原始資料<a href="https://tomme.me/demo/elasticsearch/balls.json">balls.json</a><br>
<em>注意最後一行要空白！</em></p>
<p>然後將他上傳: <code>curl -H &quot;</code></p>]]></description><link>https://tomme.me/elasticsearch-aggregation-basic-group-by-bucketing/</link><guid isPermaLink="false">5c04b8964f51f17314869bab</guid><category><![CDATA[Elasticsearch]]></category><category><![CDATA[ELK]]></category><dc:creator><![CDATA[Tommy Lin]]></dc:creator><pubDate>Mon, 03 Dec 2018 06:16:28 GMT</pubDate><content:encoded><![CDATA[<h3 id="elasticsearch">Elasticsearch 聚合搜尋: 分組</h3>
<p>Elasticsearch 的聚合搜尋, 可以說是最常的用的功能了. 什麼是聚合搜尋呢？ 就是針對搜尋出來的結果, 再去做計算. 比如可以計算最大值、平均值、最小值、總和、95%、分組、累加... 等等的計算.</p>
<p>這篇從基礎的分組開始帶大家了解如何做聚合搜尋, 以及他的概念.</p>
<br>
<h3 id="">範例: 球球分類</h3>
<p><img src="https://tomme.me/content/images/2018/12/Screen-Shot-2018-12-03-at-1.20.50-PM.png" alt="Screen-Shot-2018-12-03-at-1.20.50-PM"></p>
<p>首先, 我們有一堆球球; 這些球球都有自己個別的元素, 包含形狀和顏色. 所謂分類就是讓有相同屬性的球球分到一類, 比如相同的形狀或是相同的顏色.</p>
<p>透過分類, 我們可以得到</p>
<ol>
<li>這一坨球球裡面有哪些形狀？</li>
<li>每個形狀有幾顆球球?</li>
<li>每個形狀, 有幾個不同顏色的球球？</li>
</ol>
<br>
<h4 id="">建立資料</h4>
<p>那就先把資料建立進去吧, 還沒安裝的朋友請參考<a href="https://tomme.me/hello-world-series-elasticsearch/">Hello World 系列 - Elasticsearch</a>.</p>
<p>我們用批量上傳的方式來丟資料, 可以在這邊下載原始資料<a href="https://tomme.me/demo/elasticsearch/balls.json">balls.json</a><br>
<em>注意最後一行要空白！</em></p>
<p>然後將他上傳: <code>curl -H &quot;Content-Type: Application/json&quot; -XPOST 192.168.40.41:9200/demo/doc/_bulk --data-binary @balls.json</code></p>
<p>大家記得把 elasticsearch ip 換成自己的~</p>
<br>
<p>然後看一下, 25顆球都歸檔啦～</p>
<pre><code>❯ curl http://192.168.40.41:9200/_cat/indices\?v
health status index               uuid                   pri rep docs.count docs.deleted store.size pri.store.size
green  open   demo                bBTVU1ssRjKvoIamaO7Irg   3   1         25            0       34kb         20.7kb
</code></pre>
<br>
<h3 id="">來分類吧: 用形狀分類</h3>
<p>第一步, 我們先用形狀來分類. 我們預期得到以下結果:<br>
<img src="https://tomme.me/content/images/2018/12/Screen-Shot-2018-12-03-at-2.19.30-PM.png" alt="Screen-Shot-2018-12-03-at-2.19.30-PM"></p>
<br>
<p>注意圖片中每個分組寫著<code>Buckets</code>, 意思就是<code>桶</code>; Elasticsearch 當中的分組都適用桶裝的唷.</p>
<p>所以怎麼用 elasticsearch 辦到呢？ 咱們接著做囉！</p>
<h4 id="term">以詞(term)分類來作聚合搜尋</h4>
<p>首先, 聚合的普遍結構長成這樣:</p>
<pre><code>{
  聚合: {
    &lt;聚合名字自取&gt;: {
      &lt;聚合種類&gt;: {
        &lt;聚合欄位&gt;: &quot;&quot;
      }
    }
  }
}
</code></pre>
<p>那麼以這次的目標來說 就是下面這樣</p>
<pre><code>{
  聚合: {
    以形狀分類: {
      詞分類: {
        欄位: 形狀
      }
    }
  }
}
</code></pre>
<br>
<h4 id="">實際操作</h4>
<pre><code>❯ curl -s -H &quot;Content-Type: Application/json&quot; -XPOST &quot;http://192.168.40.41:9200/demo/_search&quot; -d '
{
  &quot;size&quot;: 0,
  &quot;query&quot;: {
    &quot;bool&quot;: {}
  },
  &quot;aggs&quot;: {
    &quot;group_by_shape&quot;: {
      &quot;terms&quot;: {
        &quot;field&quot;: &quot;shape.keyword&quot;
      }
    }
  }
}
' | jq .


{
  &quot;took&quot;: 177,
  &quot;timed_out&quot;: false,
  &quot;_shards&quot;: {
    &quot;total&quot;: 3,
    &quot;successful&quot;: 3,
    &quot;skipped&quot;: 0,
    &quot;failed&quot;: 0
  },
  &quot;hits&quot;: {
    &quot;total&quot;: 25,
    &quot;max_score&quot;: 0,
    &quot;hits&quot;: []
  },
  &quot;aggregations&quot;: {
    &quot;group_by_shape&quot;: {
      &quot;doc_count_error_upper_bound&quot;: 0,
      &quot;sum_other_doc_count&quot;: 0,
      &quot;buckets&quot;: [
        {
          &quot;key&quot;: &quot;rectangle&quot;,
          &quot;doc_count&quot;: 9
        },
        {
          &quot;key&quot;: &quot;circle&quot;,
          &quot;doc_count&quot;: 8
        },
        {
          &quot;key&quot;: &quot;triangle&quot;,
          &quot;doc_count&quot;: 8
        }
      ]
    }
  }
}
</code></pre>
<p>可以看到最後結果的部分; 已經得到我們想要的分類囉！</p>
<p>這邊注意幾個地方</p>
<ol>
<li><code>size: 0</code>: 因為我們在乎的是聚合後的結果, 而不是原始資料; 所以這邊size就可以等於0. 事實上, 如果您也有使用 grafana 或是 kibana, 也會發現他們也是這樣使用的.</li>
<li><code>took: 177</code>: 代表這次搜尋花了 177 毫秒.</li>
<li><code>hits: 25</code>: 代表總共有 25 顆球球.</li>
<li><code>aggregations</code>: 透過聚合得到的資料都會出現在這物件裡.</li>
<li><code>buckets</code>: 桶, 每個分類都是用桶子裝著 XD</li>
</ol>
<hr>
<br>
<h3 id="">接著再以顏色做分類</h3>
<p>上面分類出來, 只能得到各形狀有幾顆球球; 但是我們還需要知道更細的分類<br>
<strong>每個形狀裡面, 個別又有多少顏色的球？</strong></p>
<p>這是我們想要得到的結果:<br>
<img src="https://tomme.me/content/images/2018/12/Screen-Shot-2018-12-03-at-3.10.25-PM.png" alt="Screen-Shot-2018-12-03-at-3.10.25-PM"></p>
<p>這時候, 有了上面分類的概念, 只要照著嵌套下去就行了～<br>
所以說, 結構長成這樣:</p>
<pre><code>{
  聚合: {
    以形狀分類: {
      詞分類: {
        欄位: 形狀
      },
      聚合: {
        以顏色分類: {
          詞分類: {
            欄位: 顏色
          }
        }
      }
    }
  }
}
</code></pre>
<hr>
<br>
<h4 id="">實際操作</h4>
<pre><code>❯ curl -s -H &quot;Content-Type: Application/json&quot; -XPOST &quot;http://192.168.40.41:9200/demo/_search&quot; -d '
{
  &quot;size&quot;: 0,
  &quot;query&quot;: {
    &quot;bool&quot;: {}
  },
  &quot;aggs&quot;: {
    &quot;group_by_shape&quot;: {
      &quot;terms&quot;: {
        &quot;field&quot;: &quot;shape.keyword&quot;
      },
      &quot;aggs&quot;: {
        &quot;group_by_color&quot;: {
          &quot;terms&quot;: {
            &quot;field&quot;: &quot;color.keyword&quot;
          }
        }
      }
    }
  }
}
' | jq .

{
  &quot;took&quot;: 65,
  &quot;timed_out&quot;: false,
  &quot;_shards&quot;: {
    &quot;total&quot;: 3,
    &quot;successful&quot;: 3,
    &quot;skipped&quot;: 0,
    &quot;failed&quot;: 0
  },
  &quot;hits&quot;: {
    &quot;total&quot;: 25,
    &quot;max_score&quot;: 0,
    &quot;hits&quot;: []
  },
  &quot;aggregations&quot;: {
    &quot;group_by_shape&quot;: {
      &quot;doc_count_error_upper_bound&quot;: 0,
      &quot;sum_other_doc_count&quot;: 0,
      &quot;buckets&quot;: [
        {
          &quot;key&quot;: &quot;rectangle&quot;,
          &quot;doc_count&quot;: 9,
          &quot;group_by_color&quot;: {
            &quot;doc_count_error_upper_bound&quot;: 0,
            &quot;sum_other_doc_count&quot;: 0,
            &quot;buckets&quot;: [
              {
                &quot;key&quot;: &quot;blue&quot;,
                &quot;doc_count&quot;: 5
              },
              {
                &quot;key&quot;: &quot;red&quot;,
                &quot;doc_count&quot;: 3
              },
              {
                &quot;key&quot;: &quot;yellow&quot;,
                &quot;doc_count&quot;: 1
              }
            ]
          }
        },
        {
          &quot;key&quot;: &quot;circle&quot;,
          &quot;doc_count&quot;: 8,
          &quot;group_by_color&quot;: {
            &quot;doc_count_error_upper_bound&quot;: 0,
            &quot;sum_other_doc_count&quot;: 0,
            &quot;buckets&quot;: [
              {
                &quot;key&quot;: &quot;blue&quot;,
                &quot;doc_count&quot;: 4
              },
              {
                &quot;key&quot;: &quot;red&quot;,
                &quot;doc_count&quot;: 3
              },
              {
                &quot;key&quot;: &quot;yellow&quot;,
                &quot;doc_count&quot;: 1
              }
            ]
          }
        },
        {
          &quot;key&quot;: &quot;triangle&quot;,
          &quot;doc_count&quot;: 8,
          &quot;group_by_color&quot;: {
            &quot;doc_count_error_upper_bound&quot;: 0,
            &quot;sum_other_doc_count&quot;: 0,
            &quot;buckets&quot;: [
              {
                &quot;key&quot;: &quot;yellow&quot;,
                &quot;doc_count&quot;: 6
              },
              {
                &quot;key&quot;: &quot;blue&quot;,
                &quot;doc_count&quot;: 2
              }
            ]
          }
        }
      ]
    }
  }
}
</code></pre>
<p>鏘鏘鏘～ 這樣就達到我們要的結果了～ 如果說每個球球身上還有分數的話, 還可以繼續嵌套下去將它計算出來唷！</p>
<p>所以上面得答案依序是</p>
<ol>
<li>這一坨球球裡面有哪些形狀？ <code>rectangle, triangle, circle</code></li>
<li>每個形狀有幾顆球球? <code>rectangle:9, triangle:8, circle: 8</code></li>
<li>每個形狀, 有幾個不同顏色的球球？</li>
</ol>
<pre><code>rectangle-blue: 5, rectangle-red: 3, rectangle-yellow: 1
triangle-blue: 2, triangle-red: 0, triangle-yellow: 6
circle-blue: 4, circle-red: 3, circle-yellow: 1
</code></pre>
<p>分組聚合就到這邊告一段落囉, 謝謝大家.</p>
]]></content:encoded></item><item><title><![CDATA[Linux 使用wondershaper限制帶寬速度]]></title><description><![CDATA[<h3 id="">前情提要</h3>
<p>為什麼要限制帶寬哩？ 原因是之前管理的某服務器被黑了; 被拿去做肉雞參與了在世界上某地的一場DDOS戰役.. 見下圖</p>
<p><img src="https://tomme.me/content/images/2018/11/Screen-Shot-2018-11-26-at-2.49.16-PM.png" alt="Screen-Shot-2018-11-26-at-2.49.16-PM"></p>
<p>流量被吃, CPU可能還被拿去挖礦, 這都已經夠慘了; 供應商直接把你斷網, 然後給你一個告知 原文如下</p>
<blockquote>
<p>Hi there,</p>
<p>We've detected an outgoing Denial of Service attack (<a href="http://do.co/21Y1Gc1">http://do.co/21Y1Gc1</a>) originating from your Droplet. Due to the traffic’s harmful nature, your Droplet was taken offline; this means it is not connected to the internet</p></blockquote>]]></description><link>https://tomme.me/linux-use-wondershaper-to-limit-bandwidth/</link><guid isPermaLink="false">5bfb7fe853e54358c72d6357</guid><dc:creator><![CDATA[Tommy Lin]]></dc:creator><pubDate>Mon, 26 Nov 2018 06:35:04 GMT</pubDate><content:encoded><![CDATA[<h3 id="">前情提要</h3>
<p>為什麼要限制帶寬哩？ 原因是之前管理的某服務器被黑了; 被拿去做肉雞參與了在世界上某地的一場DDOS戰役.. 見下圖</p>
<p><img src="https://tomme.me/content/images/2018/11/Screen-Shot-2018-11-26-at-2.49.16-PM.png" alt="Screen-Shot-2018-11-26-at-2.49.16-PM"></p>
<p>流量被吃, CPU可能還被拿去挖礦, 這都已經夠慘了; 供應商直接把你斷網, 然後給你一個告知 原文如下</p>
<blockquote>
<p>Hi there,</p>
<p>We've detected an outgoing Denial of Service attack (<a href="http://do.co/21Y1Gc1">http://do.co/21Y1Gc1</a>) originating from your Droplet. Due to the traffic’s harmful nature, your Droplet was taken offline; this means it is not connected to the internet and all hosted sites and services are unreachable. We know that this action is disruptive, but it’s necessary to protect you, our network, and the target of your Droplet’s attack.</p>
<p>You can access your droplet using this console link: <a href="https://cloud.digitalocean.com/droplets/119932431/console">https://cloud.digitalocean.com/droplets/119932431/console</a></p>
<p>Because this means your Droplet has been compromised, you’ll need to back up your data and transfer it to a new Droplet. We have a recovery tool to assist you, but any databases on your Droplet will need to be backed up before we boot your Droplet into the recovery tool because you won’t be able to make the backups afterwards.</p>
<p>Specific backup steps vary depending on the database software in use, which is most commonly MySQL. If you’re not sure how, <a href="http://do.co/1h0uWgm">http://do.co/1h0uWgm</a> will show you how to back up your databases from MySQL.</p>
<p>Once you have finished backing up your data, the next step is downloading and transferring your data to your new Droplet. Please update this ticket when you’re ready and we’ll configure this Droplet so you can proceed.</p>
<p>If you’ve enabled our backup service or have a snapshot of the Droplet, you can restore directly from that image instead of going through the recovery process. Be aware that this will destroy any changes or additions made to the Droplet since the creation date of the image you use to restore from. If you do this, please update the ticket as we will need to reconfigure networking to get your Droplet back online.</p>
<p>If you don’t need the data from this Droplet, you can destroy this Droplet at your convenience. If you’d like to keep the current IP address, you will need to use our rebuild function. This acts like a clean install of your OS and is currently the only way to ensure you retain your IP. As with restoring from an image, please let us know once you’ve done this.</p>
<p>If you have any further questions, or if we can further assist, please let us know.</p>
<p>Regards,</p>
<p>Trust &amp; Safety<br>
DigitalOcean Support</p>
</blockquote>
<br>
<p>內容大致如下</p>
<blockquote>
<p>我們發現尼的vps有大量惡意流量唷, 為了保護尼, 我們將它斷網了.  不過尼還是可以透過 web console 登入唷 ! 即使進去後你還是沒有網路德, 備份好之後請跟我們說唷, 我們會協助尼將資料轉移到新的機器, 至於現在這台就這樣讓他去吧~ 886</p>
</blockquote>
<hr>
<p>哇擦... 發現惡意流量你可以限速就行了吧？ 搞成這樣至於嗎？<br>
嘛... 算了 當作沒有事先了解遊戲規則吧...</p>
<br>
<h3 id="">正文</h3>
<p>所以說.. 我們就乖乖的自己限速一下吧...</p>
<p>先看一下<a href="https://github.com/magnific0/wondershaper">官方</a>, 這套件其實就是 tc 的前端, 方便使用者調用 tc ; 而 tc,  就是 linux 內核用來控制流量的機制.<br>
關於 tc 網路上介紹的文章已經很多了, 那部分還真的有點艱深... 小弟理解不足, 就不在這邊介紹了, 請各位大大自行移駕.</p>
<p>再說用法之前, 還需要再補充一點. tc 本身對於上傳可以比較準確地控制, 下載則無法. 原因是對於出口流量, 較容易控制要出去多少, 而近來的流量, 必須要靠中介網卡<code>ifb</code>才可以; 但這部分超出文章範圍哩, 有興趣的自行去理解吧！</p>
<hr>
<br>
<h4 id="">下載</h4>
<pre><code>cd /tmp/
git clone  https://github.com/magnific0/wondershaper.git
cp wondershaper/wondershaper /usr/bin/
</code></pre>
<pre><code>wondershaper -h
USAGE: /usr/bin/wondershaper [-hcs] [-a &lt;adapter&gt;] [-d &lt;rate&gt;] [-u &lt;rate&gt;]

Limit the bandwidth of an adapter

OPTIONS:
   -h           Show this message
   -a &lt;adapter&gt; Set the adpter
   -d &lt;rate&gt;    Set maximum download rate (in Kbps) and/or
   -u &lt;rate&gt;    Set maximum upload rate (in Kbps)
   -p           Use presets in /etc/conf.d/wondershaper.conf
   -c           Clear the limits from adapter
   -s           Show the current status of adapter
   -v           Show the current version

MODES:
   wondershaper -a &lt;adapter&gt; -d &lt;rate&gt; -u &lt;rate&gt;
   wondershaper -c -a &lt;adapter&gt;
   wondershaper -s -a &lt;adapter&gt;

EXAMPLES:
   wondershaper -a eth0 -d 1024 -u 512
   wondershaper -a eth0 -u 512
   wondershaper -c -a eth0
</code></pre>
<hr>
<br>
<h4 id="">使用</h4>
<p>首先看一下未限速的上傳: 100Mbits<br>
<img src="https://tomme.me/content/images/2018/11/Screen-Shot-2018-11-26-at-2.09.46-PM-2.png" alt="Screen-Shot-2018-11-26-at-2.09.46-PM-2"></p>
<br>
<p>然後使用該工具查看網卡的預設配置:  <code>wondershaper -s -a ens160</code><br>
<img src="https://tomme.me/content/images/2018/11/Screen-Shot-2018-11-26-at-2.21.33-PM.png" alt="Screen-Shot-2018-11-26-at-2.21.33-PM"></p>
<br>
<p>接下來, 幫他上限速. 這邊就只限制上傳速度: <code>wondershaper -a ens160 -u 20480</code>; 這樣子是鎖在<code>20Mbits</code></p>
<p><img src="https://tomme.me/content/images/2018/11/Screen-Shot-2018-11-26-at-2.24.56-PM.png" alt="Screen-Shot-2018-11-26-at-2.24.56-PM"></p>
<br>
<p>最後看一下成果吧: 16Mbits<br>
<img src="https://tomme.me/content/images/2018/11/Screen-Shot-2018-11-26-at-2.27.56-PM.png" alt="Screen-Shot-2018-11-26-at-2.27.56-PM"></p>
<br>
<p>如果不要限制了的話: <code>wondershaper -c -a ens160</code></p>
<hr>
<h4 id="">結論</h4>
<p>希望大家的雲主機都能安安穩穩~~~</p>
]]></content:encoded></item><item><title><![CDATA[Nginx 阻擋國家並且配置白名單]]></title><description><![CDATA[<h3 id="">前言</h3>
<p>使用 <a href="https://nginx.org/en/docs/http/ngx_http_geoip_module.html">ngx_http_geoip_module</a> 搭配國家阻擋, 要特別注意的是他和我們常用的 <a href="http://nginx.org/en/docs/http/ngx_http_access_module.html">ngx_http_access_module</a>, 是會互相打架的哦. 什麼意思哩？ 就是如果比如你把某個國家阻擋掉, 那麼就算你特別配置允許該國家特定 ip  <code>allow A.B.C.D</code>, 也是沒有用的哦, 那整個國家就是被擋在外面啦！ 下面我們就來看要怎麼配置阻擋國家和特定白名單吧.</p>
<br>
<h3 id="">事前準備</h3>
<p>測試環境<br>
<code>Ubuntu 14.04 LTS</code><br>
<code>Ubuntu 16.04 LTS</code></p>
<p>首先呢, 既然要使用那個模塊, 就必須先確保你的 Nginx 有編譯該模塊. 可以用這個指令查看. <code>nginx -V 2&gt;&amp;1 | grep -o with-http_</code></p>]]></description><link>https://tomme.me/nginx-geoblock-setup-with-whitelist/</link><guid isPermaLink="false">5b2a5734f28358265cebd83a</guid><category><![CDATA[Nginx]]></category><dc:creator><![CDATA[Tommy Lin]]></dc:creator><pubDate>Wed, 20 Jun 2018 15:21:56 GMT</pubDate><content:encoded><![CDATA[<h3 id="">前言</h3>
<p>使用 <a href="https://nginx.org/en/docs/http/ngx_http_geoip_module.html">ngx_http_geoip_module</a> 搭配國家阻擋, 要特別注意的是他和我們常用的 <a href="http://nginx.org/en/docs/http/ngx_http_access_module.html">ngx_http_access_module</a>, 是會互相打架的哦. 什麼意思哩？ 就是如果比如你把某個國家阻擋掉, 那麼就算你特別配置允許該國家特定 ip  <code>allow A.B.C.D</code>, 也是沒有用的哦, 那整個國家就是被擋在外面啦！ 下面我們就來看要怎麼配置阻擋國家和特定白名單吧.</p>
<br>
<h3 id="">事前準備</h3>
<p>測試環境<br>
<code>Ubuntu 14.04 LTS</code><br>
<code>Ubuntu 16.04 LTS</code></p>
<p>首先呢, 既然要使用那個模塊, 就必須先確保你的 Nginx 有編譯該模塊. 可以用這個指令查看. <code>nginx -V 2&gt;&amp;1 | grep -o with-http_geoip_module</code> 如果輸出是空白的, 代表你的 Nginx 沒有這個模塊. 那麼恭喜你, 就可以繼續按照本課程繼續下一步. 我的也是熱騰騰編譯進去的, 如果你們的已經如下圖正確顯示, 那請跳到配置部份 ~<br>
<img src="https://tomme.me/content/images/2018/06/2018-06-20_22-23.png.png" alt="2018-06-20_22-23.png"></p>
<br>
<p>要加入這個模塊的話, 就必須從編譯開始.  但是要編譯之前呢, 請先 <code>apt-get install libgeoip-dev</code>, 這個是 geoip 所需要的函式庫, 如果缺少安裝的話就會有下面錯誤:<br>
<code>configure: error: the GeoIP module requires the GeoIP library. You can either do not enable the module or install the library.</code></p>
<p>安裝好了就開始編譯吧！ 請在 <code>configure</code> 步驟的時候加入 <code>--with-http_geoip_module</code>,已經開始恍神的同學快到 <a href="https://tomme.me/install-nginx-from-source-on-ubuntu16-04/">Ubuntu16.04 從源安裝nginx</a> 惡補一下.<br>
<img src="https://tomme.me/content/images/2018/06/2018-06-20_22-31.png" alt="2018-06-20_22-31"></p>
<br>
<p><code>configure</code> 完了之後就是編譯 <code>make</code>, 跑完之後, 記得先暫停服務 <code>service nginx stop</code>,  然後安裝 <code>make install</code>. 最後啟動服務 <code>service nginx start</code>.</p>
<p>這時候再次執行 <code>nginx -V 2&gt;&amp;1 | grep -o with-http_geoip_module</code> , 就可以看到已經加入模塊啦！</p>
<br>
<p>在配置之前, 還有個最重要的工作, 下載 geoip 的資料庫！ 完成了就可以開始配置了.</p>
<pre><code>cd /etc/nginx
wget http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz 
gunzip GeoIP.dat.gz
</code></pre>
<br>
<h3 id="">配置</h3>
<p>請在 <code>/etc/nginx/nginx.conf</code> 裡面的 <code>http</code> 區塊裡面加入這段, 請注意我使用 <code>$remote_addr</code> 這個變數, 如果你們的 Nginx 是躲在 proxy 後面的, 請先參照 <a href="https://tomme.me/retrieve-cdn-x-forwarded-for-header/">Nginx 獲取 CDN X-Forwarded-For</a>, 把表頭的 X-Forwarded-For 替換至 remote_addr. (如果覺的麻煩的話, 解決方法就是用別的變數取代也可以)</p>
<pre><code>    # 資料庫位址
    geoip_country /etc/nginx/GeoIP.dat;

    # 從 $remote_addr 這個變數, 來定義 $ip_whitelist 這個變數
    geo $remote_addr $ip_whitelist {
      default 0;
      1.200.216.57 1;
    }
</code></pre>
<p>接下來配置一個設定檔 <code>/etc/nginx/conf.d/geoblock.conf</code>, 內容如下. 這邊配置阻擋了日本, 台灣, 新加坡. 其他的國家代碼可以在 <a href="https://dev.maxmind.com/geoip/legacy/codes/iso3166/">maxmind</a> 網站找到哦.</p>
<pre><code># 如果是白名單, 就不用繼續看下去了
if ($ip_whitelist = 1) {
    break;
}

# 這些國家給他 403
if ($geoip_country_code ~ (JP|TW|SG)) {
    return 403;
}
</code></pre>
<p>最後一步就是在你的 <code>server</code> 引用進來</p>
<pre><code>server {
 listen 80 default_server;

 root /usr/share/nginx/html;
 index index.html index.htm;

 server_name localhost;
 
 # 引用我們的 geoblock 配置
 include /etc/nginx/conf.d/geoblock.conf;

 location / {
  try_files $uri $uri/ =404;
 }
}
</code></pre>
<br>
<h3 id="">驗證</h3>
<p>依照剛剛的配置, 就是阻擋日本, 台灣, 新加坡; 然後白名單是一個日本IP (118.11.213.239 );</p>
<p>實驗者一號, 日本, 阻擋！<br>
<img src="https://tomme.me/content/images/2018/06/2018-06-20_23-24.png" alt="2018-06-20_23-24"></p>
<p>實驗者二號, 新加坡, 阻擋！<br>
<img src="https://tomme.me/content/images/2018/06/2018-06-20_23-13.png" alt="2018-06-20_23-13"></p>
<p>實驗者三號, 台灣, 阻擋！<br>
<img src="https://tomme.me/content/images/2018/06/2018-06-20_23-09.png" alt="2018-06-20_23-09"></p>
<p>實驗者四號, 日本 白名單, pass！<br>
<img src="https://tomme.me/content/images/2018/06/2018-06-20_23-15.png" alt="2018-06-20_23-15"></p>
<p>(嘛, 相信以各位的功力, 這點馬賽克是傷不了你們的眼睛的)<br>
這樣就達成今天的目的啦～ 我們下次見！</p>
<br>
<h3 id="">尾聲</h3>
<p>一個 moment 就把整個國家 block 掉！</p>
]]></content:encoded></item><item><title><![CDATA[使用 dnsmasq 配置内部 DNS server]]></title><description><![CDATA[<h3 id="">前言</h3>
<p>為什麼要配置內部的 dns server 呢？ 因為現在各種應用溝通都開始使用 FQDN 啦～ 其實要在 public nameserver 配置 private ip 也可以, 但是有幾點考量 :</p>
<ul>
<li>有時候他們被限制不能上網, 那又需要解析 domain 的時候, 就必須要用內部 dns 啦</li>
<li>明明要去的目的地就在旁邊, 不需要繞出去 internet 在查詢一次</li>
<li>如果被人發現你們的域名解出來是 private ip, 那 ... 其實他也不能怎樣, 就是很有趣的發現這樣XD.</li>
</ul>
<br>
<h3 id="bind9">說好的 bind9 呢？</h3>
<p>沒有錯！ 我這一開始也是安裝 bind9, 但是配置到一半我就投降了！ 他比較適合架構比較大的時候使用吧, 我們這就是一個小小的內部 dns server, 追求的就是一個單純快速簡易穩定～</p>
<br>
<hr>
<h3 id="">入正題！</h3>
<p>安裝環境:  <code>Ubuntu 16.04.</code></p>]]></description><link>https://tomme.me/use-dnsmasq-as-internal-dsn-server/</link><guid isPermaLink="false">5b1a5d9ef28358265cebd82c</guid><dc:creator><![CDATA[Tommy Lin]]></dc:creator><pubDate>Fri, 08 Jun 2018 11:35:55 GMT</pubDate><content:encoded><![CDATA[<h3 id="">前言</h3>
<p>為什麼要配置內部的 dns server 呢？ 因為現在各種應用溝通都開始使用 FQDN 啦～ 其實要在 public nameserver 配置 private ip 也可以, 但是有幾點考量 :</p>
<ul>
<li>有時候他們被限制不能上網, 那又需要解析 domain 的時候, 就必須要用內部 dns 啦</li>
<li>明明要去的目的地就在旁邊, 不需要繞出去 internet 在查詢一次</li>
<li>如果被人發現你們的域名解出來是 private ip, 那 ... 其實他也不能怎樣, 就是很有趣的發現這樣XD.</li>
</ul>
<br>
<h3 id="bind9">說好的 bind9 呢？</h3>
<p>沒有錯！ 我這一開始也是安裝 bind9, 但是配置到一半我就投降了！ 他比較適合架構比較大的時候使用吧, 我們這就是一個小小的內部 dns server, 追求的就是一個單純快速簡易穩定～</p>
<br>
<hr>
<h3 id="">入正題！</h3>
<p>安裝環境:  <code>Ubuntu 16.04.4 LTS</code><br>
第一步就是安裝啦 <code>apt-get install dnsmasq</code><br>
安裝好後會有個設定檔 <code>/etc/dnsmasq.conf</code> , 裡面寫的落落長但全都註解的, 我們就先把他改名子, 當作之後的配置參考. <code>mv /etc/dnsmasq.conf /etc/dnsmasq.conf.bak</code></p>
<br>
<h3 id="">基本配置</h3>
<p>基本配置這樣就可以啦～ 如果還需要其他選項, 比如 <code>ttl</code>, 或是 <code>ptr</code> 或是要使用哪張網卡, 監聽哪個端口, 都可以去剛剛的 <code>/etc/dnsmasq.conf.bak</code> 裡面找需要的參數使用.  我們就先把目前的配置講完吧.</p>
<p>會有三個配置文件</p>
<ul>
<li><code>/etc/dnsmasq.conf</code>: 主要配置, 要加什麼選項通常在這</li>
<li><code>/etc/hosts_myns.conf</code>: 裡面內容格式和 <code>/etc/hosts</code> 一樣, 就是本地 dns 紀錄</li>
<li><code>/etc/hosts_myns.conf</code>: 上游選項, 就是說如果你身上沒有紀錄的話, 他要去哪裡幫你找答案</li>
</ul>
<br>
<pre><code>root@ubuntu:~# cat /etc/dnsmasq.conf
# 我们自己的ns纪录对照, 格式和 /etc/hosts一样
no-hosts
addn-hosts=/etc/hosts_myns.conf

# 自己没有纪录的话就向外询问
resolv-file=/etc/resolv_myns.conf

# 如果要指定是 CNAME 的话这样配置
# 这笔纪录一定要存在于hosts (/etc/hosts_myns.conf)
cname=fish01.tux.com,fish.tux.com
</code></pre>
<pre><code>root@ubuntu:~# cat /etc/hosts_myns.conf
2.2.2.2 www.bar.com
3.3.3.3 fish.tux.com
</code></pre>
<pre><code>root@ubuntu:~# cat /etc/resolv_myns.conf
nameserver=8.8.8.8
nameserver=168.95.1.1
</code></pre>
<br>
<p>改完後記得 <code>systemctl reload dnsmasq</code> 或是 <code>systemctl restart dnsmasq</code></p>
<blockquote>
<p>兩者的差別是, 如果你配置的 domain, 在 internet 已經查詢的到, 你要強制覆蓋的話, 使用 <code>reload</code> 指令是不會馬上生效的哦, 因為緩存還沒失效</p>
</blockquote>
<p>就這樣短短幾行配置, 完成了 <code>A 紀錄</code>, <code>CNAME 紀錄</code>, <code>上游 dns</code> 配置啦～ 下面就演示一下成果</p>
<br>
<hr>
<h3 id="">驗收</h3>
<p><strong>配置的A紀錄</strong><br>
<img src="https://tomme.me/content/images/2018/06/2018-06-08_19-28.png" alt="2018-06-08_19-28"><br>
<em>可以看到預設的 <code>ttl</code> 是 0 哦</em></p>
<br>
<p><strong>配置的CNAME紀錄</strong><br>
<img src="https://tomme.me/content/images/2018/06/2018-06-08_19-30.png" alt="2018-06-08_19-30"></p>
<br>
<p><strong>沒配置的紀錄, 要去詢問上游</strong><br>
<img src="https://tomme.me/content/images/2018/06/2018-06-08_19-31.png" alt="2018-06-08_19-31"></p>
<br>
<hr>
<h3 id="">尾聲</h3>
<p>嘛～  有時間還是要把 <code>bind9</code> 學好阿～人家可是老字號阿.</p>
]]></content:encoded></item><item><title><![CDATA[Collectd的SNMP模塊搭配Logstash]]></title><description><![CDATA[<h3 id="">前言</h3>
<p>最近使用 ELK/G 在彙整資料, 雖然說 Logstash 已經能幫我們處理 <code>netflow</code>, <code>log</code>, <code>api</code> 等資訊, 但還有一樣東西是運維不可或缺的: <strong>沒錯就是<code>snmp</code></strong> !!</p>
<blockquote>
<p>秉持 hello world 的精神, 這篇先介紹怎麼使用 collectd snmp 模塊抓取本機的資訊, 熟悉之後就可以把資料拋給 logstash 囉</p>
</blockquote>
<p>安裝環境: <code>Ubuntu 16.04 LTS</code></p>
<br>
<h3 id="snmpcollectd">安裝和配置 snmp 及 collectd</h3>
<pre><code>apt-get install snmp snmpd collectd
mv /etc/snmp/snmpd.conf /etc/snmp/snmpd.conf.bak</code></pre>]]></description><link>https://tomme.me/use-collectd-snmp-plugin-with-logstash/</link><guid isPermaLink="false">5b09b7f8f28358265cebd81f</guid><dc:creator><![CDATA[Tommy Lin]]></dc:creator><pubDate>Sat, 26 May 2018 20:50:48 GMT</pubDate><content:encoded><![CDATA[<h3 id="">前言</h3>
<p>最近使用 ELK/G 在彙整資料, 雖然說 Logstash 已經能幫我們處理 <code>netflow</code>, <code>log</code>, <code>api</code> 等資訊, 但還有一樣東西是運維不可或缺的: <strong>沒錯就是<code>snmp</code></strong> !!</p>
<blockquote>
<p>秉持 hello world 的精神, 這篇先介紹怎麼使用 collectd snmp 模塊抓取本機的資訊, 熟悉之後就可以把資料拋給 logstash 囉</p>
</blockquote>
<p>安裝環境: <code>Ubuntu 16.04 LTS</code></p>
<br>
<h3 id="snmpcollectd">安裝和配置 snmp 及 collectd</h3>
<pre><code>apt-get install snmp snmpd collectd
mv /etc/snmp/snmpd.conf /etc/snmp/snmpd.conf.bak
echo &quot;rwcommunity mysnmp&quot; &gt;&gt; /etc/snmp/snmpd.conf
</code></pre>
<blockquote>
<p>這樣就安裝和配置完成<code>snmpd</code>部份了哦, 可以看到剛才把預設的配置檔做備份, 真正的配置目前只有<code>rwcommunity mysnmp</code> 這樣一行而已哦! 這是個人習慣, 一來可以很清楚自己下的設定, 二來未來再次部屬的時候也可以很快的copy paste哦！</p>
</blockquote>
<p>改完設定檔以後記得要重新啟動服務<code>systemctl restart snmpd</code></p>
<br>
<h3 id="collectd">配置Collectd</h3>
<p><strong>Collectd 基礎配置</strong></p>
<pre><code>cat /etc/collectd.conf                                                          
Hostname    &quot;localhost&quot;
LoadPlugin logfile
LoadPlugin write_log
LoadPlugin snmp
TypesDB    &quot;/usr/share/collectd/types.db&quot;
TypesDB    &quot;/usr/share/collectd/types.db.custom&quot;

&lt;Plugin logfile&gt;
        LogLevel info
        File STDOUT
        Timestamp true
        PrintSeverity false
&lt;/Plugin&gt;
&lt;Plugin snmp&gt;
  &lt;Data &quot;std_traffic&quot;&gt;
      Type &quot;if_octets&quot;
      Table true
      Instance &quot;IF-MIB::ifDescr&quot;
      Values &quot;IF-MIB::ifInOctets&quot; &quot;IF-MIB::ifOutOctets&quot;
  &lt;/Data&gt;
  &lt;Host &quot;my_archlinux&quot;&gt;
      Address &quot;127.0.0.1&quot;
      Version 1
      Community &quot;pp&quot;
      Collect &quot;std_traffic&quot;
      Interval 10
  &lt;/Host&gt;
&lt;/Plugin&gt;
</code></pre>
<br>
<p>名詞解釋:<br>
<code>Hostname</code>： 讓別人知道是誰在採資料<br>
<code>logfile</code>： 寫入檔案的模塊, 必須和<code>write_log</code>一起使用, <code>STDOUT</code> 就是在畫面打印啦<br>
<code>write_log</code>: 寫入採集到的 metric, 等到測試完成這個可以註解掉哦～<br>
<code>snmp</code>： 就是採集snmp的模塊囉<br>
<code>&lt;Data &quot;std_traffic&quot;&gt;</code>：採集資料名, 自取, 稍後會在Host用到<br>
<code>Instance &quot;IF-MIB::ifDescr&quot;</code>： 資料參照名, 這邊就是網卡的敘述名稱<br>
<code>Values &quot;IF-MIB::ifInOctets&quot; &quot;IF-MIB::ifOutOctets&quot;</code> 實際要採集的資料<br>
<code>&lt;Host &quot;my_archlinux&quot;&gt;</code>： 主機名, 自取辨識用<br>
<code>Collect &quot;std_traffic&quot;</code>：剛才取的採集資料名</p>
<p>這時候執行<code>sudo collectd -f</code> 就可以看到資料打印囉</p>
<pre><code>[2017-08-18 00:56:52] plugin_load: plugin &quot;logfile&quot; successfully loaded.       
[2017-08-18 00:56:52] plugin_load: plugin &quot;snmp&quot; successfully loaded.          
[2017-08-18 00:56:52] plugin_load: plugin &quot;write_log&quot; successfully loaded.     
[2017-08-18 00:56:52] Initialization complete, entering read-loop.             
[2017-08-18 00:56:52] write_log values:                                        
my_archlinux.snmp.if_octets-lo.rx 6065342 1502989012                           
my_archlinux.snmp.if_octets-lo.tx 6065342 1502989012                           

[2017-08-18 00:56:52] write_log values:                                        
my_archlinux.snmp.if_octets-tun0.rx 548103 1502989012                          
my_archlinux.snmp.if_octets-tun0.tx 471309 1502989012                          

[2017-08-18 00:56:52] write_log values:                                        
my_archlinux.snmp.if_octets-Intel_Corporation_Wireless_3160.rx 327940 1502989012                                                                               
my_archlinux.snmp.if_octets-Intel_Corporation_Wireless_3160.tx 248255 1502989012                                                                               

[2017-08-18 00:56:52] write_log values:                                        
my_archlinux.snmp.if_octets-Qualcomm_Atheros_Killer_E220x_Gigabit_Ethernet_Controller.rx 1208338895 1502989012                                                 
my_archlinux.snmp.if_octets-Qualcomm_Atheros_Killer_E220x_Gigabit_Ethernet_Controller.tx 64125573 1502989012                                                   

^C[2017-08-18 00:56:53] Exiting normally.                                      
[2017-08-18 00:56:53] collectd: Stopping 5 read threads.                       
[2017-08-18 00:56:53] collectd: Stopping 5 write threads.       
</code></pre>
<p>上面 Collect 抓到的資訊分別是<br>
<code>Host</code> - <code>Type</code> - <code>Type-Instance</code> - <code>Metric</code> - <code>Timestamp</code></p>
<p>OK! 沒問題了, 那我們就進行下一步吧！</p>
<br>
<hr>
<h3 id="logstash">來配置和Logstash的傳接球吧</h3>
<h4 id="collect">Collect 配置</h4>
<pre><code>cat /etc/collectd/collectd.conf
LoadPlugin logfile
&lt;Plugin logfile&gt;
	LogLevel info
	File STDOUT
	Timestamp true
	PrintSeverity false
&lt;/Plugin&gt;
LoadPlugin network
LoadPlugin snmp
LoadPlugin write_log
&lt;Plugin network&gt;
    &lt;Server &quot;192.168.40.43&quot; &quot;25826&quot;&gt;
    &lt;/Server&gt;
&lt;/Plugin&gt;
&lt;Plugin snmp&gt;
  &lt;Data &quot;std_traffic&quot;&gt;
      Type &quot;if_octets&quot;
      Table true
      Instance &quot;IF-MIB::ifDescr&quot;
      Values &quot;IF-MIB::ifInOctets&quot; &quot;IF-MIB::ifOutOctets&quot;
  &lt;/Data&gt;
  &lt;Host &quot;my_archlinux&quot;&gt;
      Address &quot;127.0.0.1&quot;
      Version 1
      Community &quot;pp&quot;
      Collect &quot;std_traffic&quot;
      Interval 10
  &lt;/Host&gt;
&lt;/Plugin&gt;
</code></pre>
<blockquote>
<p>可以看到這邊就是多了一個 <code>network</code>模塊, 要填入 logstash 的 ip 哦, 25826 就是我設定 logstash 監聽(<s>接球</s>)的埠號/端口</p>
</blockquote>
<br>
<h4 id="logstash">Logstash 配置</h4>
<p>不熟悉的人先看<a href="https://tomme.me/hello-world-series-logstash/">這篇</a>哦</p>
<pre><code>input {
  udp {
    port =&gt; 25826
    buffer_size =&gt; 1452
    codec =&gt; collectd { }
    type =&gt; &quot;collectd&quot;
  }
}

filter {}

output {
if [type] == &quot;collectd&quot; {
  stdout {                   
    codec =&gt; rubydebug       
  }  
  elasticsearch {
    hosts =&gt; [&quot;192.168.40.44:9200&quot;]
    index =&gt; &quot;collectd-%{+YYYY.MM.dd}&quot;
        }
  }
}
</code></pre>
<blockquote>
<p>這邊的 Collectd 和 Logstash 都有開啟 stdout (標準輸出) , 先確定配置沒有問題; 之後用 daemon 模式開啟就不需要囉</p>
</blockquote>
<p>確定資料沒問題之後, 就可以用 <code>systemctl start collectd</code> 的方式來執行囉!</p>
<h2 id="br"><br></h2>
<h3 id="">常見問題集</h3>
<h5 id="xy"><strong>模塊表示: 我只接受x個值, 你卻給我y個</strong></h5>
<pre><code>[2018-03-18 05:16:25] snmp plugin: DataSet `memory' requires 1 values, but config talks about 2
</code></pre>
<p>這是在說什麼呢？<br>
原來是type造成的. 什麼意思? 有看到上面設定檔有個<code>types.db</code>嗎, 設定檔裡面的type就是去參照他的, 我們看一下<code>types.db</code>裡面他提到的<code>memory</code>部份.</p>
<pre><code>tommy@ubuntu:~/collectd$ grep memory /usr/share/collectd/types.db
memory                  value:GAUGE:0:281474976710656
memory_lua              value:GAUGE:0:281474976710656
vs_memory              value:GAUGE:0:9223372036854775807
</code></pre>
<br>
<p>當你今天使用memory這個type的時候, 就不能回傳多個值, 這時候你可以建立一個自己的, 記得要再設定檔裡面把這個<code>types.db.custom</code>也加進去</p>
<pre><code>tommy@ubuntu:~/collectd$ grep memory /usr/share/collectd/types.db.custom
linux_memory                    mem_total:GAUGE:0:281474976710656 mem_avail:GAUGE:0:281474976710656
</code></pre>
<p>然後type就可以選擇<code>linux_memory</code>這樣就好嚕</p>
<br>
<h5 id="mib"><strong>我想導入其他的MIB, 要怎麼做呢？</strong></h5>
<p>要導入其他廠商的MIB可以參考這個<br>
<a href="http://net-snmp.sourceforge.net/wiki/index.php/TUT:Using_and_loading_MIBS">導入mib</a></p>
<br>
<h5 id="syslogudpconnectionftomxxx"><strong>我的syslog裡面出現很多<code>UDP connection ftom xxx</code>這樣的垃圾訊息</strong></h5>
<p>可以用下面的配置來指定日誌等級</p>
<pre><code># RHEL OS
cat /etc/sysconfig/snmp
# OPTIONS=&quot;-LS0-6d&quot;
</code></pre>
<pre><code># Ubuntu OS
cat /etc/default/snmpd
SNMPDOPTS='-LS4d -Lf /dev/null -u snmp -g snmp -I -smux,mteTrigger,mteTriggerConf -p /run/snmpd.pid'
</code></pre>
<p>可以參考<a href="http://www.itadminstrator.com/2017/05/how-to-configure-snmp-logging-on-rhel-7.html">這裡</a></p>
<br>
<h5 id="syslogsnmpd1234cannotstatfsrunuser1000gvfspermissiondenied"><strong>我的syslog裡面有這些訊息  <code>snmpd[1234] Cannot statfs /run/user/1000/gvfs: Permission denied</code></strong></h5>
<p><a href="https://wiki.opennms.org/wiki/SNMP_spams_my_log">snmp 官方</a>說這個無法防止, 所以請在rsyslog端做忽略</p>
<pre><code>cat /etc/rsyslog.d/040-snmp-statfs.conf

if $programname == 'snmpd' and $msg contains 'statfs' then {
   stop
}
</code></pre>
<br>
<h5 id="oid"><strong>還有哪些OID可以用？</strong></h5>
<blockquote>
<p>喂～這不算問題吧？<br>
<a href="http://www.debianadmin.com/linux-snmp-oids-for-cpumemory-and-disk-statistics.html">linux 常用</a></p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[使用fail2ban防止ssh騷擾]]></title><description><![CDATA[<h3 id="">前言</h3>
<p>fail2ban 是一個可以防止各種鬼鬼祟祟, 偷偷摸摸行為的程式; 利用他可以有效防止各種莫名其妙的騷擾！</p>
<blockquote>
<p>翻譯: 此程式可以讓你配置規則, 用定義好的正則過濾器閱讀日誌判斷各式暴力破解, 最後執行如加入iptables, 寄信等各式功能.</p>
</blockquote>
<br>
<p>安裝環境: <code>Ubuntu 16.04.2 LTS</code></p>
<p>首先直接安裝<code>apt-get install fail2ban</code></p>
<br>
<p>安裝好之後, 來到 <code>/etc/fai2ban</code>這個路徑, 可以看到下面這些檔案<br>
<img src="https://tomme.me/content/images/2018/05/d0b54349-94af-4a19-a929-44e1b8d1c628.png" alt="d0b54349-94af-4a19-a929-44e1b8d1c628"></p>
<p>挑重要的來說<br>
<code>jail</code>: 存放你的規則<br>
<code>filter</code>: 存放你的過濾條件<br>
<code>action</code>: 存放你的執行動作</p>
<p>所以完整的判斷流程就是 <code>jail</code> =&gt; <code>filter</code> =&gt; <code>action</code></p>
<br>
<p>然後我們看一下<code>jail.conf</code>, 裡面放了一些預設的規則, 這邊介紹一下常用的參數意思.<br>
<img src="https://tomme.me/content/images/2018/05/6b5a016e-cc90-4c14-b305-ce3cf6dfed2d.png" alt="6b5a016e-cc90-4c14-b305-ce3cf6dfed2d"></p>
<p><img src="https://tomme.me/content/images/2018/05/39694119-80e7-4773-8100-59fc3c4cffe5.png" alt="39694119-80e7-4773-8100-59fc3c4cffe5"></p>
<p><img src="https://tomme.me/content/images/2018/05/791c1d34-ee27-4deb-9ff1-31c927ed8dfd.png" alt="791c1d34-ee27-4deb-9ff1-31c927ed8dfd"></p>
<p><code>[DEFAULT]</code> : 模塊名稱, 這個模塊下定義的就是所有預設行為啦.<br>
<code>ignoreip</code>: 也就是白名單ip.<br>
<code>ignorecommand</code></p>]]></description><link>https://tomme.me/use-fail2ban-to-prevent-ssh-harassment/</link><guid isPermaLink="false">5b030290f28358265cebd816</guid><category><![CDATA[Linux]]></category><dc:creator><![CDATA[Tommy Lin]]></dc:creator><pubDate>Mon, 21 May 2018 17:37:12 GMT</pubDate><content:encoded><![CDATA[<h3 id="">前言</h3>
<p>fail2ban 是一個可以防止各種鬼鬼祟祟, 偷偷摸摸行為的程式; 利用他可以有效防止各種莫名其妙的騷擾！</p>
<blockquote>
<p>翻譯: 此程式可以讓你配置規則, 用定義好的正則過濾器閱讀日誌判斷各式暴力破解, 最後執行如加入iptables, 寄信等各式功能.</p>
</blockquote>
<br>
<p>安裝環境: <code>Ubuntu 16.04.2 LTS</code></p>
<p>首先直接安裝<code>apt-get install fail2ban</code></p>
<br>
<p>安裝好之後, 來到 <code>/etc/fai2ban</code>這個路徑, 可以看到下面這些檔案<br>
<img src="https://tomme.me/content/images/2018/05/d0b54349-94af-4a19-a929-44e1b8d1c628.png" alt="d0b54349-94af-4a19-a929-44e1b8d1c628"></p>
<p>挑重要的來說<br>
<code>jail</code>: 存放你的規則<br>
<code>filter</code>: 存放你的過濾條件<br>
<code>action</code>: 存放你的執行動作</p>
<p>所以完整的判斷流程就是 <code>jail</code> =&gt; <code>filter</code> =&gt; <code>action</code></p>
<br>
<p>然後我們看一下<code>jail.conf</code>, 裡面放了一些預設的規則, 這邊介紹一下常用的參數意思.<br>
<img src="https://tomme.me/content/images/2018/05/6b5a016e-cc90-4c14-b305-ce3cf6dfed2d.png" alt="6b5a016e-cc90-4c14-b305-ce3cf6dfed2d"></p>
<p><img src="https://tomme.me/content/images/2018/05/39694119-80e7-4773-8100-59fc3c4cffe5.png" alt="39694119-80e7-4773-8100-59fc3c4cffe5"></p>
<p><img src="https://tomme.me/content/images/2018/05/791c1d34-ee27-4deb-9ff1-31c927ed8dfd.png" alt="791c1d34-ee27-4deb-9ff1-31c927ed8dfd"></p>
<p><code>[DEFAULT]</code> : 模塊名稱, 這個模塊下定義的就是所有預設行為啦.<br>
<code>ignoreip</code>: 也就是白名單ip.<br>
<code>ignorecommand</code>: 額外的腳本來判別他是不是壞人. (其實不常用, 但是截圖入鏡只好介紹).<br>
<code>bantime</code>: 要關進牢裡多久, 配置<code>-1</code>就是終身監禁哦.<br>
<code>findtime</code>: 觀察間期.<br>
<code>maxretry</code>: 可以犯幾次錯.<br>
<code>enabled</code>: 預設每個規則都不啟用, 如果要啟用的話記得在那個模塊下配置 <code>enabled = true</code>.<br>
<code>filter</code>: 過濾配置的名子, <code>$(__name__)s</code>就是和規則同名啦.<br>
<code>banaction</code>: 判定有罪後的處置方式.</p>
<br>
<blockquote>
<p>因為fail2ban預設唯一開啟的就是sshd規則; 那這篇就不再介紹新增規則啦～ 留給下一篇配置nginx再說 ٩(｡・ω・｡)</p>
</blockquote>
<p>不過還是要帶大家看一下常用指令和效果！</p>
<br>
<p>關於監獄的各種登記紀錄, 可以在 <code>/var/log/fail2ban.log</code>查看, 如下圖所示 (一堆鬼鬼祟祟的人都被登記啦～)<br>
<img src="https://tomme.me/content/images/2018/05/2202a8c1-d6ae-4983-8990-3555ef9348b0.png" alt="2202a8c1-d6ae-4983-8990-3555ef9348b0"></p>
<br>
<p><code>fail2ban-client status</code> 這個指令可以查看目前建立了哪些監獄<br>
<img src="https://tomme.me/content/images/2018/05/100eb053-cae4-49ab-8af4-f4c0d504597a.png" alt="100eb053-cae4-49ab-8af4-f4c0d504597a"></p>
<p>然後如果你對<code>sshd</code>這個監獄有興趣, 那就<code>fail2ban-client status sshd</code> 來查看; 可以看到裡面已經關三個人啦！<br>
<img src="https://tomme.me/content/images/2018/05/7b591c3b-1f65-4dbb-9e90-b1f2cadba728.png" alt="7b591c3b-1f65-4dbb-9e90-b1f2cadba728"></p>
<br>
<p>最後就是來<code>iptables</code>查看一下<br>
<img src="https://tomme.me/content/images/2018/05/screenshot-2018-05-22-01-21-38.png" alt="screenshot-2018-05-22-01-21-38"></p>
<br>
<h3 id="">尾聲</h3>
<p>把那些討厭的人通通關起來 (`･ω･´)</p>
]]></content:encoded></item><item><title><![CDATA[使用 snmp 監控檔案]]></title><description><![CDATA[<h3 id="snmp">使用snmp監控檔案大小, 以及檔案中出現指定字樣的次數</h3>
<p>snmp提供兩個entry來達到目標, 使用此方法可以來監控比如nginx access log裡面502的次數, 又或者是 error log的大小.</p>
<br>
<p><strong>監控檔案大小, 單位為kb</strong>: <code>file FILE [MAXSIZE]</code></p>
<p><strong>監控指定字樣出現次數</strong>: <code>logmatch NAME FILE CYCLETIME REGEX</code></p>
<br>
<p>在<code>/etc/snmp/snmpd.conf</code>新增以下兩行</p>
<pre><code>logmatch mylogcheck /var/log/dummy/dummy.log 150 SYSTEM STATS
file /var/log/dummy/dummy.log 2
</code></pre>
<p>這樣的意思就是說</p>
<ul>
<li>監控<code>/var/log/dummy/dummy.log</code></li></ul>]]></description><link>https://tomme.me/use-snmp-to-monitor-file/</link><guid isPermaLink="false">5b001ad4f28358265cebd801</guid><category><![CDATA[SNMP]]></category><dc:creator><![CDATA[Tommy Lin]]></dc:creator><pubDate>Sat, 19 May 2018 12:42:16 GMT</pubDate><content:encoded><![CDATA[<h3 id="snmp">使用snmp監控檔案大小, 以及檔案中出現指定字樣的次數</h3>
<p>snmp提供兩個entry來達到目標, 使用此方法可以來監控比如nginx access log裡面502的次數, 又或者是 error log的大小.</p>
<br>
<p><strong>監控檔案大小, 單位為kb</strong>: <code>file FILE [MAXSIZE]</code></p>
<p><strong>監控指定字樣出現次數</strong>: <code>logmatch NAME FILE CYCLETIME REGEX</code></p>
<br>
<p>在<code>/etc/snmp/snmpd.conf</code>新增以下兩行</p>
<pre><code>logmatch mylogcheck /var/log/dummy/dummy.log 150 SYSTEM STATS
file /var/log/dummy/dummy.log 2
</code></pre>
<p>這樣的意思就是說</p>
<ul>
<li>監控<code>/var/log/dummy/dummy.log</code>這個檔案 SYSTEM STATS出現的次數</li>
<li><code>/var/log/dummy/dummy.log</code>這個檔案的大小不能超過2k</li>
<li><code>mylogcheck</code>只是名子, 可以自己取</li>
</ul>
<hr>
<br>
<p>先看一下狀態, 目前SYSTEM STAT出現三次, 並且檔案大小是8.3k</p>
<pre><code>root@ubuntu:/var/log/dummy# grep &quot;SYSTEM STAT&quot; dummy.log | wc -l
3
root@ubuntu:/var/log/dummy# du -h dummy.log
8.3K dummy.log
</code></pre>
<br>
<h3 id="">查看出現次數</h3>
<pre><code>❯ snmpwalk -v2c -c labsnmp 192.168.41.123 .1.3.6.1.4.1.2021.16
UCD-SNMP-MIB::logMatchMaxEntries.0 = INTEGER: 250
UCD-SNMP-MIB::logMatchIndex.1 = INTEGER: 1
UCD-SNMP-MIB::logMatchName.1 = STRING: mylogcheck
UCD-SNMP-MIB::logMatchFilename.1 = STRING: /var/log/dummy/dummy.log
UCD-SNMP-MIB::logMatchRegEx.1 = STRING: SYSTEM STATS
UCD-SNMP-MIB::logMatchGlobalCounter.1 = Counter32: 8
UCD-SNMP-MIB::logMatchGlobalCount.1 = INTEGER: 8
UCD-SNMP-MIB::logMatchCurrentCounter.1 = Counter32: 3
UCD-SNMP-MIB::logMatchCurrentCount.1 = INTEGER: 3
UCD-SNMP-MIB::logMatchCounter.1 = Counter32: 3
UCD-SNMP-MIB::logMatchCount.1 = INTEGER: 0
UCD-SNMP-MIB::logMatchCycle.1 = INTEGER: 150
UCD-SNMP-MIB::logMatchErrorFlag.1 = INTEGER: noError(0)
UCD-SNMP-MIB::logMatchRegExCompilation.1 = STRING: Success
</code></pre>
<p><em>抓到該字詞出現3次</em></p>
<br>
<h3 id="">查看檔案大小</h3>
<pre><code>❯ snmpwalk -v2c -c labsnmp 192.168.41.123 .1.3.6.1.4.1.2021.15.1
UCD-SNMP-MIB::fileIndex.1 = INTEGER: 1
UCD-SNMP-MIB::fileName.1 = STRING: /var/log/dummy/dummy.log
UCD-SNMP-MIB::fileSize.1 = INTEGER: 8 kB
UCD-SNMP-MIB::fileMax.1 = INTEGER: 2 kB
UCD-SNMP-MIB::fileErrorFlag.1 = INTEGER: error(1)
UCD-SNMP-MIB::fileErrorMsg.1 = STRING: /var/log/dummy/dummy.log: size exceeds 2kb (= 8kb)
</code></pre>
<p><em>抓到檔案大小8k, 超過我們配置的2k</em></p>
<hr>
<br>
<h3 id="oid">那個 oid 是哪來的？</h3>
<p>其實我沒有找到oid.. Orz<br>
那我是怎麼知道要使用這個oid呢？ 是用下面這種偷吃步的方式啦！</p>
<p><strong>因為snmpd已經註冊了我們配置的名子, 所以就大範圍搜尋！</strong></p>
<pre><code>❯ snmpwalk -v2c -c labsnmp 192.168.41.123 .1.3.6.1.4.1.2021 | grep mylogcheck
UCD-SNMP-MIB::logMatchName.1 = STRING: mylogcheck
</code></pre>
<br>
<p><strong>找到以後,再把他翻譯回來</strong></p>
<pre><code>❯ snmptranslate -On UCD-SNMP-MIB::logMatchName.1
.1.3.6.1.4.1.2021.16.2.1.2.1
</code></pre>
<p><em>其實不用特別翻譯也可以用哦, 前提是你的snmp client有安裝mib; 我覺得反解回來oid比較通用啦～</em></p>
<br>
<hr>
<p>額外技巧: snmp正反解<br>
正解</p>
<pre><code>% snmptranslate .1.3.6.1.2.1.1.3.0
SNMPv2-MIB::sysUpTime.0
</code></pre>
<p>反解</p>
<pre><code>% snmptranslate -On SNMPv2-MIB::sysUpTime.0
.1.3.6.1.2.1.1.3.0
</code></pre>
<hr>
<p>參考:<br>
<a href="http://www.net-snmp.org/docs/man/snmpd.conf.html">SNMPD manpage (Log File Monitoring)</a><br>
<a href="http://net-snmp.sourceforge.net/wiki/index.php/TUT:snmptranslate">snmptranslate</a></p>
]]></content:encoded></item><item><title><![CDATA[Ansible 的 Facts]]></title><description><![CDATA[<h3 id="ansiblefacts">什麼是 Ansible facts 呢?</h3>
<p>可以把他想像成auto_discover, 它能夠幫你蒐集一些機器的基本訊息, 如硬盤,IP, 主機名稱 等等的, 並且註冊到playbook變數, 讓之後可以繼續使用.有一些文章提到, 如果你已經有架構內所有機器的資訊, 不需要他來幫你<code>gather_facts</code> 那就可以把他關掉哦. 不過這個功能其實滿好用的,下面來介紹一下吧～</p>
<p>首先我們編寫一個playbook, 內容就是基本的ping就好了<br>
<img src="https://tomme.me/content/images/2018/05/6dd7d7e6-b0c7-47d3-b101-a44db653b55d.png" alt="6dd7d7e6-b0c7-47d3-b101-a44db653b55d"></p>
<p>執行效果如下:<br>
<img src="https://tomme.me/content/images/2018/05/845b02fb-ae81-444c-a31f-c9538a31b089.png" alt="845b02fb-ae81-444c-a31f-c9538a31b089"></p>
<br>
<p>可以看到, 他在真正執行tasks之前, 做了一個動作<code>Gathering Facts</code>, 那就是他在蒐集資訊哦. 這是他預設的行為, 不過這項任務其實很單純, 並不需要這些資訊,那我們可以把他關掉. 有兩個地方可以關掉, 第一個就是<code>ansible.cfg</code>, 另外也可以在playbook裡面各別關掉哦. 首先看看ansible.cfg.<br>
<img src="https://tomme.me/content/images/2018/05/ffa7c10b-8a6c-485b-b0ba-f684c11edb21.png" alt="ffa7c10b-8a6c-485b-b0ba-f684c11edb21"></p>
<p>有三個選項: 預設是 implicit<br>
<code>smart</code>: 預設蒐集資訊, 但是不重複蒐集<br>
<code>implicit</code>: 預設一律蒐集, 指定</p>]]></description><link>https://tomme.me/facts-in-ansible/</link><guid isPermaLink="false">5afd9967f28358265cebd7fa</guid><category><![CDATA[Hello World]]></category><category><![CDATA[Ansible]]></category><dc:creator><![CDATA[Tommy Lin]]></dc:creator><pubDate>Thu, 17 May 2018 15:08:28 GMT</pubDate><content:encoded><![CDATA[<h3 id="ansiblefacts">什麼是 Ansible facts 呢?</h3>
<p>可以把他想像成auto_discover, 它能夠幫你蒐集一些機器的基本訊息, 如硬盤,IP, 主機名稱 等等的, 並且註冊到playbook變數, 讓之後可以繼續使用.有一些文章提到, 如果你已經有架構內所有機器的資訊, 不需要他來幫你<code>gather_facts</code> 那就可以把他關掉哦. 不過這個功能其實滿好用的,下面來介紹一下吧～</p>
<p>首先我們編寫一個playbook, 內容就是基本的ping就好了<br>
<img src="https://tomme.me/content/images/2018/05/6dd7d7e6-b0c7-47d3-b101-a44db653b55d.png" alt="6dd7d7e6-b0c7-47d3-b101-a44db653b55d"></p>
<p>執行效果如下:<br>
<img src="https://tomme.me/content/images/2018/05/845b02fb-ae81-444c-a31f-c9538a31b089.png" alt="845b02fb-ae81-444c-a31f-c9538a31b089"></p>
<br>
<p>可以看到, 他在真正執行tasks之前, 做了一個動作<code>Gathering Facts</code>, 那就是他在蒐集資訊哦. 這是他預設的行為, 不過這項任務其實很單純, 並不需要這些資訊,那我們可以把他關掉. 有兩個地方可以關掉, 第一個就是<code>ansible.cfg</code>, 另外也可以在playbook裡面各別關掉哦. 首先看看ansible.cfg.<br>
<img src="https://tomme.me/content/images/2018/05/ffa7c10b-8a6c-485b-b0ba-f684c11edb21.png" alt="ffa7c10b-8a6c-485b-b0ba-f684c11edb21"></p>
<p>有三個選項: 預設是 implicit<br>
<code>smart</code>: 預設蒐集資訊, 但是不重複蒐集<br>
<code>implicit</code>: 預設一律蒐集, 指定 <code>gather_facts: False</code>來關閉<br>
<code>explicit</code>: 預設一律不蒐集, 指定 <code>gather_facts: True</code>來開啟</p>
<br>
那我們這邊把他改成 explicit, 讓他一律不蒐集吧！
<p><img src="https://tomme.me/content/images/2018/05/307af39f-31b3-4944-9783-67da042eda5b.png" alt="307af39f-31b3-4944-9783-67da042eda5b"></p>
<p>這時候再次執行剛剛的playbook, 可以看到他不再蒐集囉. 在簡單的任務中, 這樣可以省不少時間呢.<br>
<img src="https://tomme.me/content/images/2018/05/acd54608-9ac5-4f91-a8a5-0af45a3a8355.png" alt="acd54608-9ac5-4f91-a8a5-0af45a3a8355"></p>
<br>
<blockquote>
<p>欸..上面才說要介紹,怎麼就關掉了哪.. 別急阿～ 這就把他打開</p>
</blockquote>
<hr>
<h3 id="">怎麼使用他？</h3>
<p>下面示範如何各別開啟, 然後這個<code>facts</code>又能給我們帶來什麼好處吧！<br>
比如你需要在你的任務中, 呼叫那台機器的host name 或是 IP, 那就可以像這樣使用哦.</p>
<p>這邊示範取得主機 IP<br>
<img src="https://tomme.me/content/images/2018/05/bde24b5b-3bc6-4b85-9aa2-55a4fff2d55b.png" alt="bde24b5b-3bc6-4b85-9aa2-55a4fff2d55b"></p>
<p>運行結果：<br>
<img src="https://tomme.me/content/images/2018/05/60158569-2dab-4461-a1fe-f9029180840e.png" alt="60158569-2dab-4461-a1fe-f9029180840e"></p>
<br>
<hr>
<h3 id="wait">Wait!!</h3>
<p><code>ansible_default_ipv4['address']</code> 這是打哪來的？<br>
這就是他蒐集到的<code>facts</code>啦～<br>
使用 <code>ansible &lt;host&gt; -m setup</code> 可以看到全部的參數哦<br>
<img src="https://tomme.me/content/images/2018/05/30d680c2-5fe5-4cdf-b605-ece4e360f657.png" alt="30d680c2-5fe5-4cdf-b605-ece4e360f657"></p>
<br>
<p>這樣一來, 機器的很多資訊不用再定義一次啦～ 不過...拿到自己的資訊可能不太稀奇, 我能不能讓<code>LAB_PROXY10</code>的任務, 自動去抓取<code>LAB_WEB1</code>的 IP 呢？</p>
<hr>
<h3 id="facts">共享facts</h3>
<p>獨樂(ㄩㄝ↘)樂不若與眾樂(ㄩㄝ↘)樂, 在你那邊蒐集到的資訊, 我借來用可以吧？當然可以, 請看下面示範<br>
<em>請注意這邊只有 LAB_WEB1 有蒐集事實 (gather_facts); LAB_PROXY10 則沒有</em><br>
<img src="https://tomme.me/content/images/2018/05/02dc72f1-60b7-417f-831b-18dc5994e1c9.png" alt="02dc72f1-60b7-417f-831b-18dc5994e1c9"></p>
<p>運行結果如下<br>
<img src="https://tomme.me/content/images/2018/05/130cecff-4a28-4df2-ae93-736b0384a22e.png" alt="130cecff-4a28-4df2-ae93-736b0384a22e"></p>
<p>這邊的 <code>inventory_hostname</code> 指的就是自己, 因為 LAB_PROXY10 自己沒有蒐集事實, 所以他第一個會輸出 <code>VARIABLE IS NOT DEFINED</code></p>
<p>但是 LAB_WEB有蒐集事實, 所以就可以去輸出他的 IP 哦; 這邊就成功讓 LAB_PROXY10 去輸出 LAB_WEB1的 IP 資訊啦～</p>
<blockquote>
<p>好像有什麼沒說清楚..</p>
</blockquote>
<br>
<hr>
<h3 id="waitx2">Wait x2</h3>
<p>第一次我們不用共享facts的時候, 我們這樣配置 <code>var=ansible_default_ipv4['address']</code><br>
第二次要facts的時候變成 <code>var=hostvars[inventory_hostname]['ansible_default_ipv4']['address']</code></p>
<p>還記得我們可以在<code>inventory</code>裡面定義變數嗎？ 然後 <code>facts</code> 這也視為變數, 這兩個會參在一起<s>做成撒尿牛丸</s>變成<code>hostvars</code>; 所以可以想像 <code>hostvars</code> 就是全局的變數, 看下面的範例吧</p>
<p>LAB_WEB1 什麼都不做, 就是蒐集事實<br>
LAB_PROXY10 負責把全局變數打印出來<br>
<img src="https://tomme.me/content/images/2018/05/0d943a40-54e2-4396-9624-da8cace8e184.png" alt="0d943a40-54e2-4396-9624-da8cace8e184"></p>
<p>然後把打印的結果, 丟到<a href="https://jsoneditoronline.org/">這裡</a>看<br>
<img src="https://tomme.me/content/images/2018/05/aa37c32d-0742-4ca9-bb8b-0a6d345f176c.png" alt="aa37c32d-0742-4ca9-bb8b-0a6d345f176c"></p>
<p>可以看到<br>
LAB_PROXY10 下面只有24個選項可以用; LAB_WEB 有110個選項, 那就是<code>gather facts</code>帶來的哦</p>
<br>
<hr>
<h3 id="">尾聲</h3>
<p>關於 <code>facts</code> 就介紹到這邊啦～<br>
複習一下重點</p>
<ul>
<li>如果要取自己的變數, 參照 <code>ansible setup 模塊</code> 提供的就可以了</li>
<li>如果還要取別人的變數, 就要從全局變數 <code>hostvars</code> 一路寫下來</li>
<li><code>gather facts</code>只要做一次, 就可以在 playbook 裡面沿用下去</li>
</ul>
<p>我們下次見～</p>
]]></content:encoded></item></channel></rss>