<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Logstash on</title><link>https://taetaetae.github.io/tags/logstash/</link><description>Recent content in Logstash on</description><generator>Hugo</generator><language>en</language><lastBuildDate>Wed, 17 Feb 2021 16:53:49 +0900</lastBuildDate><atom:link href="https://taetaetae.github.io/tags/logstash/index.xml" rel="self" type="application/rss+xml"/><item><title>Elastic Stack으로 코로나19 대시보드 만들기 - 2부 : 대시보드</title><link>https://taetaetae.github.io/posts/make-dashboards-from-elasticstack-2/</link><pubDate>Wed, 17 Feb 2021 16:53:49 +0900</pubDate><guid>https://taetaetae.github.io/posts/make-dashboards-from-elasticstack-2/</guid><description>&lt;p>　&lt;a href="https://taetaetae.github.io/posts/make-dashboards-from-elasticstack-1/" rel="">지난 포스팅&lt;/a>에서는 ﻿데이터를 수급하며 전처리 과정을 거쳤고, Filebeat와 Logstash를 거쳐 Elasticsearch에 인덱싱 하는 것까지 알아보았다. 앞선 포스팅에서 이야기했지만 단순하게 데이터를 시각화 도구를 이용해서 대시보드를 만드는 게 아니라 데이터가 추가되면 만들어둔 대시보드에 자동으로 반영되는 흐름을 만들고 싶었다. 마침 파이프라인을 이틀 전에 만들었기 때문에 그동안의 빠진 데이터를 추가해야 하는 상황이다. 이 경우 Filebeat-Logstash-Elasticsearch 가 실행 중이라면 앞서 작성했던 파이썬 스크립트만 한번 실행해 주면 이틀 치 데이터가 파이프라인을 거쳐 Elasticsearch로 인덱싱이 된다. 즉, 별도로 데이터를 가져와서 재 가공하고 추가하는 다소 까다로운 작업이 미리 만들어둔 파이프라인 덕분에 한 번의 스크립트 실행으로 손쉽게 처리가 됨을 알 수 있다.&lt;/p>
&lt;p>　이제는 쌓여있는 데이터를 가지고 시각화를 해볼 차례이다. ElasticStack에서는 Kibana라는 강력한 시각화 도구를 제공하는데 이번 포스팅에서는 Kibana를 이용해서 대시보드를 만드는 방법에 대해 알아보려 한다.&lt;/p>
&lt;h2 id="visualize">Visualize&lt;/h2>
&lt;p>　﻿Elasticsearch에 인덱싱 되어있는 데이터들은 기본으로 제공되는 REST API를 통해서 조회할 수 있고 JSON 형태로 결과가 나오기 때문에 이를 가지고 다양하게 시각화를 할 수도 있다. 하지만 Kibana에서는 데이터를 조회하고 UI로 표현하는 일련의 모든 행위를 클릭 몇 번으로 할 수 있게 해주기 때문에 전문가가 아니더라도 조금만 만져보면 누구나 만들 수 있다.&lt;/p>
&lt;figure>&lt;a class="lightgallery" href="https://taetaetae.github.io/images/make-dashboards-from-elasticstack-2/visualize-main.jpg" title="/images/make-dashboards-from-elasticstack-2/visualize-main.jpg" data-thumbnail="/images/make-dashboards-from-elasticstack-2/visualize-main.jpg" data-sub-html="&lt;h2>New Visualizaion!!&lt;/h2>">
 &lt;img
 class="lazyload"
 src="https://taetaetae.github.io/svg/loading.min.svg"
 data-src="https://taetaetae.github.io/images/make-dashboards-from-elasticstack-2/visualize-main.jpg"
 data-srcset="https://taetaetae.github.io/images/make-dashboards-from-elasticstack-2/visualize-main.jpg, https://taetaetae.github.io/images/make-dashboards-from-elasticstack-2/visualize-main.jpg 1.5x, https://taetaetae.github.io/images/make-dashboards-from-elasticstack-2/visualize-main.jpg 2x"
 data-sizes="auto"
 alt="/images/make-dashboards-from-elasticstack-2/visualize-main.jpg" width="80%" />
 &lt;/a>&lt;figcaption class="image-caption">New Visualizaion!!&lt;/figcaption>
 &lt;/figure>
&lt;p>　버전업이 되면서 비쥬얼라이즈를 만드는 첫 화면 또한 변화가 생겼다. 기존에는 어떤 유형의 비쥬얼라이즈를 선택할 것인지에 대해 선택하는 화면부터 나왔는데 만드는 걸 보다 편리하게 도와주는 &lt;code>Lens&lt;/code>, &lt;code>TSVB&lt;/code> 같은 기능들이 먼저 반겨준다. 이 기능을 통해서 만드는 방법도 괜찮지만 보다 명시적으로 만들고 싶으니 하단에 &lt;code>Aggregation based&lt;/code>을 선택해서 원하는 비쥬얼라이즈의 타입을 선택해 보자. 이후 생성되어 있는 인덱스를 선택하면 본격적으로 비쥬얼라이즈를 그릴 수 있는 화면이 나오는데 대시보드 화면 기준으로 만들어야 할 항목별로 살펴보자.&lt;/p>
&lt;h3 id="전체-수">전체 수&lt;/h3>
&lt;figure>&lt;a class="lightgallery" href="https://taetaetae.github.io/images/make-dashboards-from-elasticstack-2/1.jpg" title="/images/make-dashboards-from-elasticstack-2/1.jpg" data-thumbnail="/images/make-dashboards-from-elasticstack-2/1.jpg" data-sub-html="&lt;h2>.&lt;/h2>">
 &lt;img
 class="lazyload"
 src="https://taetaetae.github.io/svg/loading.min.svg"
 data-src="https://taetaetae.github.io/images/make-dashboards-from-elasticstack-2/1.jpg"
 data-srcset="https://taetaetae.github.io/images/make-dashboards-from-elasticstack-2/1.jpg, https://taetaetae.github.io/images/make-dashboards-from-elasticstack-2/1.jpg 1.5x, https://taetaetae.github.io/images/make-dashboards-from-elasticstack-2/1.jpg 2x"
 data-sizes="auto"
 alt="/images/make-dashboards-from-elasticstack-2/1.jpg" width="80%" />
 &lt;/a>&lt;figcaption class="image-caption">.&lt;/figcaption>
 &lt;/figure>
&lt;p>　﻿확진자, 사망자, 격리 해제의 총합을 표현하려 한다. 이렇게 &amp;lsquo;숫자&amp;rsquo;를 표현하려 하는 경우 &lt;code>Metric&lt;/code>을 활용하곤 한다. 우측에서 Aggregation 방법을 &amp;lsquo;sum&amp;rsquo;으로 설정하고 필드는 유형별로 각각 선택해 주자. 아래 &amp;lsquo;Add&amp;rsquo;버튼을 눌러 확진, 사망, 격리 해제 수를 모두 표시한 다음 저장을 눌러준다. Label을 지정하지 않으면 어떤 형태로 Aggregation을 했는지를 Label 영역에 보여주는데 그게 보기 싫다면 원하는 텍스트로 지정해 주는 것도 방법이다.&lt;/p>
&lt;h3 id="최근-수">최근 수&lt;/h3>
&lt;figure>&lt;a class="lightgallery" href="https://taetaetae.github.io/images/make-dashboards-from-elasticstack-2/2.jpg" title="/images/make-dashboards-from-elasticstack-2/2.jpg" data-thumbnail="/images/make-dashboards-from-elasticstack-2/2.jpg" data-sub-html="&lt;h2>.&lt;/h2>">
 &lt;img
 class="lazyload"
 src="https://taetaetae.github.io/svg/loading.min.svg"
 data-src="https://taetaetae.github.io/images/make-dashboards-from-elasticstack-2/2.jpg"
 data-srcset="https://taetaetae.github.io/images/make-dashboards-from-elasticstack-2/2.jpg, https://taetaetae.github.io/images/make-dashboards-from-elasticstack-2/2.jpg 1.5x, https://taetaetae.github.io/images/make-dashboards-from-elasticstack-2/2.jpg 2x"
 data-sizes="auto"
 alt="/images/make-dashboards-from-elasticstack-2/2.jpg" width="80%" />
 &lt;/a>&lt;figcaption class="image-caption">.&lt;/figcaption>
 &lt;/figure>
&lt;p>　﻿확진자, 사망자, 격리 해제의 &amp;lsquo;최근 데이터&amp;rsquo;를 보여주는 게 목적이다. 이 경우 Aggregation을 Top Hit으로 선택하면 필드를 선택할 수 있게 되는데 하루의 데이터가 총 18 row이기 때문에 (서울, 부산, &amp;hellip;, 제주, 검역) 18 row 을 전부 더한 값이 하루 기준의 합계가 된다. 여기서 정렬을 날짜 기준 내림차순으로 해줘야 가장 최근 데이터의 합계가 되는 점도 신경 써야 한다.&lt;/p>
&lt;h3 id="각-타입별-합계">각 타입별 합계&lt;/h3>
&lt;figure>&lt;a class="lightgallery" href="https://taetaetae.github.io/images/make-dashboards-from-elasticstack-2/3.jpg" title="/images/make-dashboards-from-elasticstack-2/3.jpg" data-thumbnail="/images/make-dashboards-from-elasticstack-2/3.jpg" data-sub-html="&lt;h2>.&lt;/h2>">
 &lt;img
 class="lazyload"
 src="https://taetaetae.github.io/svg/loading.min.svg"
 data-src="https://taetaetae.github.io/images/make-dashboards-from-elasticstack-2/3.jpg"
 data-srcset="https://taetaetae.github.io/images/make-dashboards-from-elasticstack-2/3.jpg, https://taetaetae.github.io/images/make-dashboards-from-elasticstack-2/3.jpg 1.5x, https://taetaetae.github.io/images/make-dashboards-from-elasticstack-2/3.jpg 2x"
 data-sizes="auto"
 alt="/images/make-dashboards-from-elasticstack-2/3.jpg" width="80%" />
 &lt;/a>&lt;figcaption class="image-caption">.&lt;/figcaption>
 &lt;/figure>
&lt;p>　﻿지역별로 타입별 수를 보기 위해 &lt;code>Pie&lt;/code> 타입으로 선택하여 진행한다. 타입별(예로 들어 확진이면 confirmed)로 합계를 구하기 위해 Aggregation을 &amp;lsquo;sum&amp;rsquo;으로 설정하면 빈 원이 나오지만 각 지역별로 차트를 잘라서 봐야 하기에 하단의 Buckets의 Add를 누르고 regieon의 필드를 Terms Aggregation 한다. 18 row의 데이터가 전부 보여야 하기에 정렬 개수를 늘리고 option 탭에서 보는 취향에 알맞게 설정값들을 바꿔준다.&lt;/p>
&lt;h3 id="타입별-추이">타입별 추이&lt;/h3>
&lt;figure>&lt;a class="lightgallery" href="https://taetaetae.github.io/images/make-dashboards-from-elasticstack-2/4.jpg" title="/images/make-dashboards-from-elasticstack-2/4.jpg" data-thumbnail="/images/make-dashboards-from-elasticstack-2/4.jpg" data-sub-html="&lt;h2>.&lt;/h2>">
 &lt;img
 class="lazyload"
 src="https://taetaetae.github.io/svg/loading.min.svg"
 data-src="https://taetaetae.github.io/images/make-dashboards-from-elasticstack-2/4.jpg"
 data-srcset="https://taetaetae.github.io/images/make-dashboards-from-elasticstack-2/4.jpg, https://taetaetae.github.io/images/make-dashboards-from-elasticstack-2/4.jpg 1.5x, https://taetaetae.github.io/images/make-dashboards-from-elasticstack-2/4.jpg 2x"
 data-sizes="auto"
 alt="/images/make-dashboards-from-elasticstack-2/4.jpg" width="80%" />
 &lt;/a>&lt;figcaption class="image-caption">.&lt;/figcaption>
 &lt;/figure>
&lt;p>　﻿확진, 사망, 격리 해제 중에 사망을 제외하고 나머지 둘은 데이터의 크기가 크고 변화량이 비슷하기 때문에 x축은 시간으로 설정해두고 사망은 막대로, 나머지 둘은 라인으로 한 화면에서 표현하면 이 3가지 데이터를 한눈에 보기 좋을 것 같았다. &lt;code>Vertical bar&lt;/code> 을 선택하고 x축(Buckets &amp;gt; X-axis)은 데이터 타입인 convert_date로 설정한다. 다음으로 사망은 매일 몇 명 사망했는지 뚜렷하게 보기 위해 그냥 sum으로, 나머지 둘은 누적 합계가 더 의미 있어 보일 것 같아 Cumulative Sum으로 Aggregation을 한다. 한 화면에서 3가지의 데이터를 보여주기엔 다소 y 축의 크기가 맞지 않으므로 확진, 격리 해제를 별도의 y 축(Y-axes)으로 &amp;lsquo;Metrics &amp;amp; axes&amp;rsquo;탭에서 설정해 준다. 다음으로 각 항목의 범례를 위로 표시하여 보다 보기 편하게 배치한다.&lt;/p></description></item><item><title>Elastic Stack으로 코로나19 대시보드 만들기 - 1부 : 파이프라인 구성</title><link>https://taetaetae.github.io/posts/make-dashboards-from-elasticstack-1/</link><pubDate>Mon, 15 Feb 2021 17:50:12 +0900</pubDate><guid>https://taetaetae.github.io/posts/make-dashboards-from-elasticstack-1/</guid><description>&lt;p>﻿　얼마 전에 필자의 블로그를 보고 어느 교육 기관에서 ElasticStack에 대한 강의 요청이 들어왔다. 사실 관련 기술에 대해 지식이 아주 깊고 해박한 게 아니라서 약간의 반감부터 들었지만 ElasticStack을 전혀 모르는 사람들 기준으로 어떻게 돌아가는지에 대해서만 간단하게 소개하는 정도로 하면 된다고 하여 조심스럽지만 떨리는 마음으로 열심히 준비를 하기 시작했다. 그런데, 이런저런 이유로 갑자기 강의를 할 수 없게 되었고 그간 준비했던 내용들이 너무 아쉽지만 아무 소용이 없게 되어버렸다. 그냥 중단하기엔 아쉬운 마음이 너무 커서 준비했던 내용 중에 &amp;lsquo;데이터를 가지고 대시보드를 만드는 부분&amp;rsquo;은 누군가에겐 도움이 될까 싶어 블로그에 정리를 해보려 한다.&lt;/p>
&lt;blockquote>
&lt;p>﻿강의를 준비한 올해 1월 중순엔 Elasticsearch 버전이 7.10.2이었는데 블로그를 쓰고 있는 지금은 벌써 7.11으로 버전 업 되었다. 내가 아는 오픈소스 중에 버전업이 가장 빠른데 그렇다고 기능이 확 바뀌거나 습득하기 어렵게 바뀌진 않았다. 그만큼 사용자가 무엇을 원하는지 명확히 알고 작은 단위로 조금씩 바뀌어 가는 모습이 꽤 인상적이다.&lt;/p>&lt;/blockquote>
&lt;p>　﻿작년 초부터 코로나19 바이러스가 전 세계적으로 퍼지기 시작했고 아직까지도 진행 중이다. 나도 전염되는 건 아닐까 하는 두려움에 어디에서 얼마나 발생했는지를 확인하기 어렵던 시절 우리나라의 뛰어난 개발자들은 누가 시키지도 않았는데 정말 감사하게도 그 현황을 한눈에 볼 수 있도록 여러 유형으로 코로나19 바이러스 대시보드를 만들기 시작한다. 그 덕분에 좀 더 현황을 보기에 편해졌고 더욱 조심하게 되는 계기가 되었다고 생각한다. 이제는 포털사이트나 각종 매체를 통해 손쉽게 코로나19 바이러스의 현황을 볼 수 있지만 이러한 데이터를 가지고 검색엔진이지만 대시보드를 구축하는데 훌륭한 기능을 가지고 있는 ElasticStack을 활용해서 &amp;lsquo;나만의 대시보드&amp;rsquo;를 만드는 걸 정리해보고자 한다. 본 포스팅의 일회성으로 데이터를 가지고 대시보드를 만드는 것에서 끝나는 게 아니라 지속적으로 데이터가 업데이트된다는 가정하에 전반적인 &amp;ldquo;파이프라인&amp;quot;을 구축한 뒤 대시보드를 만들어 두고 데이터만 갱신하면 자동으로 대시보드 또한 업데이트되는 것을 목적으로 한다.
&lt;figure>&lt;a class="lightgallery" href="https://taetaetae.github.io/images/make-dashboards-from-elasticstack-1/pipeline.png" title="/images/make-dashboards-from-elasticstack-1/pipeline.png" data-thumbnail="/images/make-dashboards-from-elasticstack-1/pipeline.png" data-sub-html="&lt;h2>전체 흐름&lt;/h2>">
 &lt;img
 class="lazyload"
 src="https://taetaetae.github.io/svg/loading.min.svg"
 data-src="https://taetaetae.github.io/images/make-dashboards-from-elasticstack-1/pipeline.png"
 data-srcset="https://taetaetae.github.io/images/make-dashboards-from-elasticstack-1/pipeline.png, https://taetaetae.github.io/images/make-dashboards-from-elasticstack-1/pipeline.png 1.5x, https://taetaetae.github.io/images/make-dashboards-from-elasticstack-1/pipeline.png 2x"
 data-sizes="auto"
 alt="/images/make-dashboards-from-elasticstack-1/pipeline.png" width="100%" />
 &lt;/a>&lt;figcaption class="image-caption">전체 흐름&lt;/figcaption>
 &lt;/figure>&lt;/p>
&lt;blockquote>
&lt;p>﻿글을 모두 작성하고 보니 양이 생각보다 길어져서 데이터를 조회하고 필터링하여 Elasticsearch에 인덱싱 하는 대시보드를 만들기 위한 일종의 &amp;ldquo;데이터 파이프라인&amp;quot;을 구성하는 부분과 만들어진 데이터 기반으로 Kibana의 다양한 기능을 활용하여 대시보드를 만드는 2개의 포스팅으로 나누어 정리해보겠다.&lt;/p>&lt;/blockquote>
&lt;p>﻿　최종적으로 만들게 될 대시보드의 모습은 다음과 같다.
&lt;figure>&lt;a class="lightgallery" href="https://taetaetae.github.io/images/make-dashboards-from-elasticstack-1/kibana-dashboard.png" title="/images/make-dashboards-from-elasticstack-1/kibana-dashboard.png" data-thumbnail="/images/make-dashboards-from-elasticstack-1/kibana-dashboard.png" data-sub-html="&lt;h2>최종 목표!&lt;/h2>">
 &lt;img
 class="lazyload"
 src="https://taetaetae.github.io/svg/loading.min.svg"
 data-src="https://taetaetae.github.io/images/make-dashboards-from-elasticstack-1/kibana-dashboard.png"
 data-srcset="https://taetaetae.github.io/images/make-dashboards-from-elasticstack-1/kibana-dashboard.png, https://taetaetae.github.io/images/make-dashboards-from-elasticstack-1/kibana-dashboard.png 1.5x, https://taetaetae.github.io/images/make-dashboards-from-elasticstack-1/kibana-dashboard.png 2x"
 data-sizes="auto"
 alt="/images/make-dashboards-from-elasticstack-1/kibana-dashboard.png" width="100%" />
 &lt;/a>&lt;figcaption class="image-caption">최종 목표!&lt;/figcaption>
 &lt;/figure>&lt;/p>
&lt;h2 id="대시보드-구성-준비">대시보드 구성 준비&lt;/h2>
&lt;p>　﻿예전에는 Elasticsearch, Logstash, Kibana 3가지를 가지고 ELK라 불리다 Beat라는 경량 수집기 제품이 등장하며 이 모든 걸 ElasticStack라 부르기 시작했다. (&lt;a href="https://www.elastic.co/kr/elastic-stack" target="_blank" rel="noopener noreffer ">공식 홈페이지 참고&lt;/a>) 먼저 어떤 목표와 어떤 순서로 대시보드를 구성할 것인지에 대해 정리해봐야겠다.﻿&lt;/p>
&lt;h3 id="데이터">데이터&lt;/h3>
&lt;p>　﻿데이터는 &lt;a href="https://data.go.kr" target="_blank" rel="noopener noreffer ">공공데이터 포털&lt;/a>에서 가져오려다 조회를 해보니 누락되는 날짜도 있었고 원하는 데이터의 품질이 생각보다 좋지 않아서 다른 곳을 찾아봐야 했다. 그러다 간결하게 정리한 데이터가 &lt;a href="https://github.com/jooeungen/coronaboard_kr" target="_blank" rel="noopener noreffer ">깃헙&lt;/a>에 공개가 되어 있어서 그것을 사용하려 한다. 해당 데이터는 &lt;a href="https://coronaboard.kr/" target="_blank" rel="noopener noreffer ">https://coronaboard.kr/&lt;/a> 에서도 사용되는 데이터라고 한다. ﻿&lt;/p>
&lt;h3 id="데이터-전처리preprocessing">데이터 전처리(preprocessing)&lt;/h3>
&lt;p>　﻿원하는 데이터는 위 깃헙에서 제공하는 데이터 중에 지역별 발생 현황. 해당 &lt;a href="https://raw.githubusercontent.com/jooeungen/coronaboard_kr/master/kr_regional_daily.csv" target="_blank" rel="noopener noreffer ">데이터&lt;/a>를 살펴보면 요일별로 데이터가 &amp;lsquo;누적&amp;rsquo;되어 저장되어 있다. 즉, 서울지역 기준으로 2020년 2월 17일에 14명이 발생했고 2020년 2월 18일에 한 명도 발생하지 않았는데 14명으로 &amp;lsquo;누적&amp;rsquo;되어 저장되어 있다. 사실 이대로 해도 큰 문제는 없지만 어디까지나 별도의 가공 없이 최대한 원본 데이터(raw) 가 있어야 데이터 분석 시 다양하게 활용이 가능하기에 데이터를 분석하기 전에 전처리 과정이 필요했다. 정리하면, 집계 수가 누적되지 않고 날짜 기준으로 집계된 수만 있는 데이터를 원했다.﻿&lt;/p>
&lt;p>　﻿필자는 주로 java를 가지고 개발을 하지만 가끔 간단한 스크립트성 개발은 python을 활용하는 편이기에 다소 이쁜 코드는 아니지만 데이터를 조작하려 아래와 같은 코드를 작성하였다.&lt;/p></description></item><item><title>누구나 할 수 있는 엑세스 로그 분석 따라 해보기 (by Elastic Stack)</title><link>https://taetaetae.github.io/2019/02/10/access-log-to-elastic-stack/</link><pubDate>Sun, 10 Feb 2019 14:37:31 +0000</pubDate><guid>https://taetaetae.github.io/2019/02/10/access-log-to-elastic-stack/</guid><description>&lt;p>필자가 Elastic Stack을 알게된건 2017년 어느 여름 동기형이 공부하고 있는것을 보고 호기심에 따라하며 시작하게 되었다. 그때까지만 해도 버전이 2.x 였는데 지금 글을 쓰고있는 2019년 2월초 최신버전이 6.6이니 정말 빠르게 변화하는것 같다. &lt;!-- more -->빠르게 변화하는 버전만큼 사람들의 관심도 (드라마틱하게는 아니지만) 꾸준히 늘어나 개인적으로, 그리고 실무에서도 활용하는 범위가 많아지고 있는것 같다.&lt;/p>
&lt;script type="text/javascript" src="https://ssl.gstatic.com/trends_nrtr/1709_RC01/embed_loader.js">&lt;/script> &lt;script type="text/javascript"> trends.embed.renderExploreWidget("TIMESERIES", {"comparisonItem":[{"keyword":"elasticsearch","geo":"KR","time":"today 5-y"}],"category":0,"property":""}, {"exploreQuery":"date=today%205-y&amp;geo=KR&amp;q=elasticsearch","guestPath":"https://trends.google.co.kr:443/trends/embed/"}); &lt;/script>
&lt;p>그래서 그런지 최근들어 &lt;code>(아주 코딱지만큼 조금이라도 더 해본)&lt;/code> 필자에게 Elastic Stack 사용방법에 대해 물어보는 주변 지인들이 늘어나고 있다. 그리고 예전에 한창 공부했을때의 버전보다 많이 바꼈기에 이 기회에 &amp;ldquo;그대로 따라만 하면 Elastic Stack을 구성할 수 있을만한 글&amp;quot;을 써보고자 한다. 사실 필자가 예전에 &amp;ldquo;도큐먼트를 보기엔 너무 어려워 보이는 느낌적인 느낌&amp;rdquo; 때문에 삽질하며 구성한 힘들었던 기억을 되살려 최대한 심플하고 처음 해보는 사람도 따라하기만 하면 &amp;ldquo;아~ 이게 Elastic Stack 이구나!&amp;rdquo;, &amp;ldquo;이런식으로 돌아가는 거구나!&amp;rdquo; 하는 도움을 주고 싶다.&lt;/p>
&lt;blockquote>
&lt;p>+ 그러면서 최신버전도 살펴보고&amp;hellip; 1석2조, 이런게 바로 블로그를 하는 이유이지 않을까?
다시한번 말하지만 도큐먼트가 최고 지침서이긴 하다&amp;hellip;&lt;/p>&lt;/blockquote>
&lt;p>&lt;a href="https://www.elastic.co/kr/products" target="_blank" rel="noopener noreffer ">Elastic 공식 홈페이지&lt;/a>에 가면 각 제품군들에 대해 그림으로 된 자세한 설명과 도큐먼트가 있지만 이들을 어떤식으로 조합하여 사용하는지에 대한 전체적인 흐름을 볼 수 있는 곳은 없어 보인다. (지금 보면 도큐먼트가 그 어디보다 설명이 잘되어 있다고 생각되지만 사전 지식이 전혀없는 상태에서는 봐도봐도 어려워 보였다.)
이번 포스팅에서는 &lt;strong>Apache access log를 Elasticsearch에 인덱싱 하는 방법&lt;/strong>에 대해 설명해보고자 한다.&lt;/p>
&lt;h2 id="전체적인-흐름">전체적인 흐름&lt;/h2>
&lt;p>필자는 글보다는 그림을 좋아하는 편이라 전체적인 흐름을 그림으로 먼저 보자.&lt;/p>
&lt;a class="lightgallery" href="https://taetaetae.github.io/images/access-log-to-elastic-stack/concept.jpg" title="/images/access-log-to-elastic-stack/concept.jpg" data-thumbnail="/images/access-log-to-elastic-stack/concept.jpg">
 &lt;img
 class="lazyload"
 src="https://taetaetae.github.io/svg/loading.min.svg"
 data-src="https://taetaetae.github.io/images/access-log-to-elastic-stack/concept.jpg"
 data-srcset="https://taetaetae.github.io/images/access-log-to-elastic-stack/concept.jpg, https://taetaetae.github.io/images/access-log-to-elastic-stack/concept.jpg 1.5x, https://taetaetae.github.io/images/access-log-to-elastic-stack/concept.jpg 2x"
 data-sizes="auto"
 alt="/images/access-log-to-elastic-stack/concept.jpg" width="100%" />
 &lt;/a>
&lt;ol>
&lt;li>외부에서의 접근이 발생하면 apache 웹서버에서 설정한 경로에 access log가 파일로 생성이 되거나 있는 파일에 추가가 된다. 해당 파일에는 한줄당 하나의 엑세스 정보가 남게 된다.&lt;/li>
&lt;li>fileBeat에서 해당 파일을 트래킹 하고 있다가 라인이 추가되면 이 정보를 logstash 에게 전달해준다.&lt;/li>
&lt;li>logastsh 는 filebeat에서 전달한 정보를 특정 port로 input 받는다.&lt;/li>
&lt;li>받은 정보를 filter 과정을 통해 각 정보를 분할 및 정제한다. (ip, uri, time 등)&lt;/li>
&lt;li>정리된 정보를 elasticsearch 에 ouput 으로 보낸다. (정확히 말하면 인덱싱을 한다.)&lt;/li>
&lt;li>elasticsearch 에 인덱싱 된 정보를 키바나를 통해 손쉽게 분석을 한다.&lt;/li>
&lt;/ol>
&lt;p>한번의 설치고 일련의 과정이 뚝딱 된다면 너무 편하겠지만, 각각의 레이어가 나뉘어져있는 이유는 하는 역활이 전문적으로(?) 나뉘어져 있고 각 레이어에서는 세부 설정을 통해 보다 효율적으로 데이터를 관리할 수 있기 때문이다.&lt;/p>
&lt;blockquote>
&lt;p>beats라는 레이어가 나오기 전에는 logstash에서 직접 file을 바라보곤 했었는데 beats가 logstash 보다 가벼운 shipper 목적으로 나온 agent 이다보니 통상 logstash 앞단에 filebeat를 위치시키곤 한다고 한다.&lt;/p>&lt;/blockquote>
&lt;p>전체적인 그림은 위와 같고, 이제 이 글을 보고있는 여러분들이 따라할 차례이다. 각 레이어별로 하나씩 설치를 해보며 구성을 해보자. 설치순서는 데이터 흐름의 순서에 맞춰 다음과 같은 순서로 설치를 해야 효율적으로 볼수가 있다. (아래순서대로 하지 않을경우 설치/시작/종료 를 각각의 타이밍에 맞추어 해줘야 할것 같아 복잡할것같다.)&lt;/p>
&lt;pre tabindex="0">&lt;code>elasticsearch → logstash → kibana → filebeat
&lt;/code>&lt;/pre>&lt;p>이 포스팅은 CentOS 7.4에서 Java 1.8, apache 2.2가 설치되어있다는 가정하에 보면 될듯하다. 또한 각 레이어별 설명은 구글링을 하거나 Elastic 공식 홈페이지에 가보면 자세히 나와있으니 기본 설명은 안하는것으로 하고, 각 레이어의 세부 설정은 하지 않는것으로 한다.&lt;/p>
&lt;h3 id="elasticsearch">Elasticsearch&lt;/h3>
&lt;p>&lt;a href="https://www.elastic.co/kr/products/elasticsearch" target="_blank" rel="noopener noreffer ">공식 홈페이지&lt;/a>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-markdown" data-lang="markdown">&lt;span class="line">&lt;span class="cl">다운받고 압축풀고 심볼릭 경로 만들고 (심볼릭 경로는 선택사항)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.6.0.tar.gz
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ tar zxvf elasticsearch-6.6.0.tar.gz
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ ln -s elasticsearch-6.6.0 elasticsearch
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">설정 파일을 열고 추가해준다.
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ cd elasticsearch/conf
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ vi elasticsearch.yml
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">path.data: /~~~/data/elasticsearch (기본경로에서 변경할때추가)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">path.logs: /~~~/logs/elasticsearch
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">network.host: 0.0.0.0 # 외부에서 접근이 가능하도록 (실제 ip를 적어줘도 됨)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">elasticsearch 의 시작과 종료를 조금이나마 편하게 하기위해 스크립트를 작성해줌 (이것또한 선택사항)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ cd ../bin
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ echo &amp;#39;./elasticsearch -d -p es.pid&amp;#39; &amp;gt; start.sh
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ echo &amp;#39;kill &lt;span class="sb">`cat es.pid`&lt;/span>&amp;#39; &amp;gt; stop.sh
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">$ chmod 755 start.sh stop.sh
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>혹시 아래와 같은 에러가 발생할경우 &lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docker.html#docker-cli-run-prod-mode" target="_blank" rel="noopener noreffer ">공식문서&lt;/a> 대로 진행해준다.&lt;/p></description></item><item><title>내 서버에는 누가 들어오는걸까? (실시간 user-agent 분석기)</title><link>https://taetaetae.github.io/2018/04/10/apache-access-log-user-agent/</link><pubDate>Tue, 10 Apr 2018 23:20:19 +0000</pubDate><guid>https://taetaetae.github.io/2018/04/10/apache-access-log-user-agent/</guid><description>&lt;p>Desktop 및 스마트폰의 대중화로 다양한 OS와 브라우저들을 사용하게 되었다. 이때, 내가 운영하는 웹서버에 들어오는 사람들은 무슨 기기로 접속을 하는 것일까? 혹여 특정 OS의 특정 브라우저에서만 안되는 버그를 잡기 위해 몇일밤을 고생하며 겨우 수정했는데&amp;hellip; 과연 그 OS의 브라우저에서는 접속은 하기나 하는걸까? (ㅠㅠ) &lt;!-- more -->
만약, 접속 사용자의 Device 정보를 알고있다면 고생하며 버그를 잡기 전에 먼저 해당 Device 사용율을 체크해 볼수도 있고(수정이 아닌 간단한 얼럿으로 해결한다거나?) 비지니스 모델까지 생각해야하는 서비스라면 타겟팅을 정하는 등 다양한 활용도가 높은 것이 바로 &lt;code>User-Agent&lt;/code>라고 한다(이하 UA). 일반 Apache 를 웹서버로 운영하고 있다고 가정을 하고 어떻게 분석을 할수 있었는지, 그리고 분석을 하며 좀더 우아한(?) 방법은 없는지 알아 보고자 한다.&lt;/p>
&lt;h2 id="user-agent가-뭐야">User-Agent가 뭐야?&lt;/h2>
&lt;p>백문이 불여일타(?)라 했던가, 우선 &lt;a href="http://www.useragentstring.com" target="_blank" rel="noopener noreffer ">http://www.useragentstring.com&lt;/a> 를 들어가보자. 그러면 자신의 OS 및 브라우저 등 정보를 파싱해서 보여주는데 &lt;a href="https://en.wikipedia.org/wiki/User_agent" target="_blank" rel="noopener noreffer ">위키백과&lt;/a>에 따르면 &amp;lsquo;사용자를 대신하여 일을 수행하는 소프트웨어 에이전트&amp;rsquo;라고 한다. 즉, UA만 알아도 어떤 기기/브라우저를 사용하는지 알 수 있다는것.
&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent" target="_blank" rel="noopener noreffer ">mozilla&lt;/a>에 가보면 스팩 등 다양한 UA를 볼수가 있는데 특히 맨 아래보면 기기/브라우저별로 지원정보가 나와있다. 여기서도 보면 모든 모바일 삼성 브라우저를 제외하고는 전부 지원이 되는걸 확인할수 있다.&lt;/p>
&lt;figure>&lt;a class="lightgallery" href="https://taetaetae.github.io/images/apache-access-log-user-agent/browser_compatibility.png" title="/images/apache-access-log-user-agent/browser_compatibility.png" data-thumbnail="/images/apache-access-log-user-agent/browser_compatibility.png" data-sub-html="&lt;h2>출처 : developer.mozilla.org&lt;/h2>">
 &lt;img
 class="lazyload"
 src="https://taetaetae.github.io/svg/loading.min.svg"
 data-src="https://taetaetae.github.io/images/apache-access-log-user-agent/browser_compatibility.png"
 data-srcset="https://taetaetae.github.io/images/apache-access-log-user-agent/browser_compatibility.png, https://taetaetae.github.io/images/apache-access-log-user-agent/browser_compatibility.png 1.5x, https://taetaetae.github.io/images/apache-access-log-user-agent/browser_compatibility.png 2x"
 data-sizes="auto"
 alt="/images/apache-access-log-user-agent/browser_compatibility.png" />
 &lt;/a>&lt;figcaption class="image-caption">출처 : developer.mozilla.org&lt;/figcaption>
 &lt;/figure>
&lt;h2 id="기존의-방법">기존의 방법&lt;/h2>
&lt;p>그럼 어떻게 내 서버에 들어온 사용자들의 UA를 확인할 수 있을까? (앞서 Apache를 웹서버로 운영한다고 했으니) Apache access log 에는 Apache에서 제공해주는 모듈을 이용해 접속한 클라이언트의 정보가 남겨지곤 한다. 그렇다면 이 access log를 리눅스 명령어든 엑셀로 뽑아서든지 활용해서 정규식으로 포맷팅 하고 그 결과를 다시 그룹화 시키면 얼추 원하는 데이터를 추출해 낼수 있다. ( 버거형들이 만들어둔 정규식을 가져다 사용할수도 있겠다. &lt;a href="https://regexr.com/?37l4e" target="_blank" rel="noopener noreffer ">https://regexr.com/?37l4e&lt;/a> )
하지만, 우선 자동화가 안되어있어 데이터를 구하고 싶을때마다 귀차니즘에 걸릴수 있고 슈퍼 개발자 파워를 기반으로(?) 데이터 추출을 자동화 한들 &lt;code>실시간&lt;/code>으로 보고싶을땐 제한사항이 많다.&lt;/p>
&lt;h2 id="좀더-나은-방법">좀더 나은 방법(?)&lt;/h2>
&lt;p>실시간 데이터를 모니터링 하는데에는 다양한 오픈소스와 다양한 툴이 있겠지만 경험이 부족한건지 아직까진 ElasticStack 만한걸 못본것 같다. 간단하게 설명을 하면 access log 를 사용하지 않고 front단에서 javascript 로 UA를 구한다음 이러한 정보를 받을수 있는 API를 만들어 그쪽으로 보내면 서버에서 해당 UA를 분석해서 카프카로 보내고 ..!@#$%^blabla&amp;hellip;
^^; 그림으로 보자.&lt;/p>
&lt;figure>&lt;a class="lightgallery" href="https://taetaetae.github.io/images/apache-access-log-user-agent/user_agent_method_1.png" title="/images/apache-access-log-user-agent/user_agent_method_1.png" data-thumbnail="/images/apache-access-log-user-agent/user_agent_method_1.png" data-sub-html="&lt;h2>좀더 나은 방법&lt;/h2>">
 &lt;img
 class="lazyload"
 src="https://taetaetae.github.io/svg/loading.min.svg"
 data-src="https://taetaetae.github.io/images/apache-access-log-user-agent/user_agent_method_1.png"
 data-srcset="https://taetaetae.github.io/images/apache-access-log-user-agent/user_agent_method_1.png, https://taetaetae.github.io/images/apache-access-log-user-agent/user_agent_method_1.png 1.5x, https://taetaetae.github.io/images/apache-access-log-user-agent/user_agent_method_1.png 2x"
 data-sizes="auto"
 alt="/images/apache-access-log-user-agent/user_agent_method_1.png" />
 &lt;/a>&lt;figcaption class="image-caption">좀더 나은 방법&lt;/figcaption>
 &lt;/figure>
&lt;p>front단에서는 &lt;code>navigator.userAgent&lt;/code>를 활용하여 UA를 구할수 있었고, API에서는 UA를 받고 파싱을 하는데 관련 코드는 다음과 같이 작성하였다.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">static&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kd">final&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">VERSION_SEPARATOR&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s">&amp;#34;.&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">userAgentParsingToMap&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">userAgent&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Map&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Object&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">dataMap&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">HashMap&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">browser&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Browser&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">lookup&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">userAgent&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">HashMap&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">os&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">OS&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">lookup&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">userAgent&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">HashMap&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">device&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Device&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">lookup&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">userAgent&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">dataMap&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">put&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;browserName&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">browser&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;family&amp;#34;&lt;/span>&lt;span class="p">));&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">dataMap&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">put&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;browserVersion&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">getVersion&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">browser&lt;/span>&lt;span class="p">));&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">dataMap&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">put&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;osName&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">os&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;family&amp;#34;&lt;/span>&lt;span class="p">));&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">dataMap&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">put&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;osVersion&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">getVersion&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">os&lt;/span>&lt;span class="p">));&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">dataMap&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">put&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;deviceModel&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">device&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;model&amp;#34;&lt;/span>&lt;span class="p">));&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">dataMap&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">put&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;deviceBrand&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">device&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;brand&amp;#34;&lt;/span>&lt;span class="p">));&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kd">private&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="nf">getVersion&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">HashMap&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">dataMap&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">majorVersion&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">dataMap&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;major&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">StringUtils&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">isEmpty&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">majorVersion&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">StringUtils&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">EMPTY&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">minorVersion&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">dataMap&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;minor&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">pathVersion&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">String&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">dataMap&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;path&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">StringBuffer&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sb&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">new&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">StringBuffer&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">sb&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">majorVersion&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">StringUtils&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">isEmpty&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">minorVersion&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">sb&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">VERSION_SEPARATOR&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">sb&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">minorVersion&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">if&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="n">StringUtils&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">isEmpty&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">pathVersion&lt;/span>&lt;span class="p">))&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">sb&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">VERSION_SEPARATOR&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">sb&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">pathVersion&lt;/span>&lt;span class="p">);&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="k">return&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">sb&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="na">toString&lt;/span>&lt;span class="p">();&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>참고로 Java단에서 UA를 파싱하는 parser가 여러가지가 있는데 그중 &lt;code>uap_clj&lt;/code>라는 모듈이 그나마 잘 파싱이 되어서 사용하게 되었다.&lt;/p>
&lt;ul>
&lt;li>모듈별 비교&lt;/li>
&lt;/ul>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>모듈&lt;/th>
 &lt;th>Browser&lt;/th>
 &lt;th>OS&lt;/th>
 &lt;th>Device&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>eu.bitwalker.useragentutils.UserAgent&lt;/td>
 &lt;td>O&lt;/td>
 &lt;td>불명확함 (Android 5.x)&lt;/td>
 &lt;td>X&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>net.sf.uadetector.UserAgentStringParser&lt;/td>
 &lt;td>O&lt;/td>
 &lt;td>O&lt;/td>
 &lt;td>불명확함 (Smartphone)&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>uap_clj.java.api.*&lt;/td>
 &lt;td>O&lt;/td>
 &lt;td>O&lt;/td>
 &lt;td>O&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;ul>
&lt;li>Parsing 비교
&lt;ul>
&lt;li>UA&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-markdown" data-lang="markdown">&lt;span class="line">&lt;span class="cl">&amp;#34;Mozilla/5.0 (Linux; Android 5.1.1; Nexus 6 Build/LYZ28E) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Mobile Safari/537.36&amp;#34;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>결과&lt;/li>
&lt;/ul>
&lt;pre tabindex="0">&lt;code> - Browser : {patch=3239, family=Chrome Mobile, major=63, minor=0}
 - OS : {patch=1, patch_minor=, family=Android, major=5, minor=1}
 - Device : {model=Nexus 6, family=Nexus 6, brand=Generic_Android}
&lt;/code>&lt;/pre>&lt;p>위와 같이 구성을 하면 Elasticsearch에 인덱싱된 데이터를 Kibana에서 입맛에 맞게 실시간으로 볼수있게 되었다!
하지만 API를 만드는 부분 + front에서 별도의 javascript가 들어간다는 부분 + 운영하는 모든 페이지에 해당 javascript를 넣어야지만 볼수있는 부분 등 약간 아쉬운 감이 있다. 뭔가 더 좋은 방법이 없을까? 깔끔하면서도 &lt;code>우아한&lt;/code> 방법. 짱구를 열심히 더 굴려보자.&lt;/p></description></item><item><title>아파치 엑세스 로그를 엘라스틱서치에 인덱싱 해보자.</title><link>https://taetaetae.github.io/2018/01/25/apache-access-log-to-es/</link><pubDate>Thu, 25 Jan 2018 21:18:35 +0000</pubDate><guid>https://taetaetae.github.io/2018/01/25/apache-access-log-to-es/</guid><description>&lt;p>apache access log 를 분석하고 싶은 상황이 생겼다. 아니 그보다 apache access에 대해서 실시간으로 보고싶었고, log를 검색 &amp;amp; 데이터를 가공하여 유의미한 분석결과를 만들어 보고 싶었다. 그에 생각한것이 (역시) &lt;code>ElasticStack&lt;/code>.&lt;!-- more -->&lt;/p>
&lt;p>처음에 생각한 방안은 아래 그림처럼 단순했다.
&lt;figure>&lt;a class="lightgallery" href="https://taetaetae.github.io/images/apache-access-log-to-es/model_1.png" title="/images/apache-access-log-to-es/model_1.png" data-thumbnail="/images/apache-access-log-to-es/model_1.png" data-sub-html="&lt;h2>처음 생각한 단순한 구조&lt;/h2>">
 &lt;img
 class="lazyload"
 src="https://taetaetae.github.io/svg/loading.min.svg"
 data-src="https://taetaetae.github.io/images/apache-access-log-to-es/model_1.png"
 data-srcset="https://taetaetae.github.io/images/apache-access-log-to-es/model_1.png, https://taetaetae.github.io/images/apache-access-log-to-es/model_1.png 1.5x, https://taetaetae.github.io/images/apache-access-log-to-es/model_1.png 2x"
 data-sizes="auto"
 alt="/images/apache-access-log-to-es/model_1.png" />
 &lt;/a>&lt;figcaption class="image-caption">처음 생각한 단순한 구조&lt;/figcaption>
 &lt;/figure>&lt;/p>
&lt;p>하지만, 내 단순한(?) 예상은 역시 빗나갔고 logstash에서는 다음과 같은 에러를 내뱉었다.&lt;/p>
&lt;blockquote>
&lt;p>retrying individual bulk actions that failed or were rejected by the previous bulk request&lt;/p>&lt;/blockquote>
&lt;p>request가 많아짐에 따라 elasticsearch가 버벅거리더니 logstash에서 대량작업은 거부하겠다며 인덱싱을 멈췄다. 고민고민하다 elasticsearch에 인덱싱할때 부하가 많이 걸리는 상황에서 중간에 버퍼를 둔 경험이 있어서 facebook그룹에 문의를 해봤다.
&lt;a href="https://www.facebook.com/groups/elasticsearch.kr/?multi_permalinks=1566735266745641" target="_blank" rel="noopener noreffer ">https://www.facebook.com/groups/elasticsearch.kr/?multi_permalinks=1566735266745641&lt;/a>
역시 나보다 한참을 앞서가시는 분들은 이미 에러가 뭔지 알고 있으셨고, 중간에 버퍼를 두고 하니 잘된다는 의견이 있어 나도 따라해봤다. 물론 답변중에 나온 redis가 아닌 기존에도 비슷한 구조에서 사용하고 있던 kafka를 적용.
아, 그전에 현재구성은 Elasticsearch 노드가 총 3대로 클러스터 구조로 되어있는데 노드를 추가로 늘리며 스케일 아웃을 해보기전에 할수있는 마지막 방법이다 생각하고 중간에 kafka를 둬서 부하를 줄여보고 싶었다. (언제부턴가 마치 여러개의 톱니바퀴가 맞물려 돌아가는듯한 시스템 설계를 하는게 재밌었다.) 아래 그림처럼 말이다.&lt;/p>
&lt;figure>&lt;a class="lightgallery" href="https://taetaetae.github.io/images/apache-access-log-to-es/model_2.png" title="/images/apache-access-log-to-es/model_2.png" data-thumbnail="/images/apache-access-log-to-es/model_2.png" data-sub-html="&lt;h2>그나마 좀더 생각한 구조&lt;/h2>">
 &lt;img
 class="lazyload"
 src="https://taetaetae.github.io/svg/loading.min.svg"
 data-src="https://taetaetae.github.io/images/apache-access-log-to-es/model_2.png"
 data-srcset="https://taetaetae.github.io/images/apache-access-log-to-es/model_2.png, https://taetaetae.github.io/images/apache-access-log-to-es/model_2.png 1.5x, https://taetaetae.github.io/images/apache-access-log-to-es/model_2.png 2x"
 data-sizes="auto"
 alt="/images/apache-access-log-to-es/model_2.png" />
 &lt;/a>&lt;figcaption class="image-caption">그나마 좀더 생각한 구조&lt;/figcaption>
 &lt;/figure>
&lt;p>그랬더니 거짓말 처럼 에러하나 없이 잘 인덱싱이 될수 있었다. logstash가 양쪽에 있는게 약간 걸리긴 하지만, 처음에 생각한 구조보다는 에러가 안나니 다행이라 생각한다.&lt;/p>
&lt;p>이 구조를 적용하면서 얻은 Insight가 있기에, 각 항목별로 적어 보고자 한다. ( &lt;del>이것만 적어놓기엔 너무 없어보여서..&lt;/del> )&lt;/p>
&lt;h2 id="access-log-를-어떻게-분석하여-인덱싱-할것인가">access log 를 어떻게 분석하여 인덱싱 할것인가?&lt;/h2>
&lt;p>apache 2.x를 사용하고 별도의 로그 포맷을 정하지 않으면 아래와 같은 access log가 찍힌다.
&lt;code>123.1.1.1 - - [25/Jan/2018:21:55:35 +0900] &amp;quot;GET /api/test?param=12341234 HTTP/1.1&amp;quot; 200 48 1144 &amp;quot;http://www.naver.com/&amp;quot; &amp;quot;Mozilla/5.0 (iPhone; CPU iPhone OS 11_1_2 like Mac OS X) AppleWebKit/604.3.5 (KHTML, like Gecko) Mobile/15B202 NAVER(inapp; blog; 100; 4.0.44)&amp;quot;&lt;/code>
그럼 이 로그를 아무 포맷팅 없이 로깅을 하면 그냥 한줄의 텍스트가 인덱싱이 된다. 하지만 이렇게 되면 elasticsearch 데이터를 다시 재가공하거나 별도의 작업이 필요할수도 있으니 중간에 있는 logstash에게 일을 시켜 좀더 nice 한 방법으로 인덱싱을 해보자. 바로 logstash 의 filter 기능이다. 그중 Grok filter 라는게 있는데 패턴을 적용하여 row data 를 필터링하는 기능이다. 조금 찾아보니 너무 고맙게도 아파치 필터 예제가 있어 수정하여 적용할수 있었다. &lt;a href="http://grokconstructor.appspot.com/do/match?example=2" target="_blank" rel="noopener noreffer ">http://grokconstructor.appspot.com/do/match?example=2&lt;/a>
그래서 적용한 필터설정은 다음과 같다.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">filter &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> grok &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">match&lt;/span> &lt;span class="o">=&lt;/span>&amp;gt; &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">message&lt;/span> &lt;span class="o">=&lt;/span>&amp;gt; &lt;span class="s2">&amp;#34;%{IP:clientIp} (?:-|) (?:-|) \[%{HTTPDATE:timestamp}\] \&amp;#34;(?:%{WORD:httpMethod} %{URIPATH:uri}%{GREEDYDATA}(?: HTTP/%{NUMBER})?|-)\&amp;#34; %{NUMBER:responseCode} (?:-|%{NUMBER})&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>이렇게 하고 elasticsearch 에 인덱싱을 하면 키바나에서 다음과 같이 볼수 있다.
&lt;figure>&lt;a class="lightgallery" href="https://taetaetae.github.io/images/apache-access-log-to-es/access_log_kibana.png" title="/images/apache-access-log-to-es/access_log_kibana.png" data-thumbnail="/images/apache-access-log-to-es/access_log_kibana.png" data-sub-html="&lt;h2>키바나에 내가 원하는 구조대로 이쁘게 들어가 있는 access log&lt;/h2>">
 &lt;img
 class="lazyload"
 src="https://taetaetae.github.io/svg/loading.min.svg"
 data-src="https://taetaetae.github.io/images/apache-access-log-to-es/access_log_kibana.png"
 data-srcset="https://taetaetae.github.io/images/apache-access-log-to-es/access_log_kibana.png, https://taetaetae.github.io/images/apache-access-log-to-es/access_log_kibana.png 1.5x, https://taetaetae.github.io/images/apache-access-log-to-es/access_log_kibana.png 2x"
 data-sizes="auto"
 alt="/images/apache-access-log-to-es/access_log_kibana.png" />
 &lt;/a>&lt;figcaption class="image-caption">키바나에 내가 원하는 구조대로 이쁘게 들어가 있는 access log&lt;/figcaption>
 &lt;/figure>&lt;/p>
&lt;h2 id="각-필드가-아닌-한줄로-인덱싱이-되어버린다">각 필드가 아닌 한줄로 인덱싱이 되어버린다.&lt;/h2>
&lt;p>Elasticsearch 에 인덱싱이 되긴 하는데 로그 한줄이 통째로 들어가 버린다. &lt;code>message&lt;/code>라는 이름으로&amp;hellip; 알고보니 현재 구조는 logstash가 kafka 앞 뒤에 있다보니 producer logstash 와 consumer logstash 의 &lt;code>codec&lt;/code>이 맞아야 제대로 인덱싱이 될수 있었다.
먼저 access log에서 kafka 로 produce 하는 logstash 에서는 output 할때 codec 을 맞춰주고&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">output &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> kafka &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">bootstrap_servers&lt;/span> &lt;span class="o">=&lt;/span>&amp;gt; &lt;span class="s2">&amp;#34;123.1.2.3:9092,123.1.2.4:9092&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">topic_id&lt;/span> &lt;span class="o">=&lt;/span>&amp;gt; &lt;span class="s2">&amp;#34;apache-log&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">codec&lt;/span> &lt;span class="o">=&lt;/span>&amp;gt; json&lt;span class="o">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>kafka 에서 consume 하는 logstash 에서는 input 에서 codec 을 맞춰준다.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">input &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> kafka &lt;span class="o">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">bootstrap_servers&lt;/span> &lt;span class="o">=&lt;/span>&amp;gt; &lt;span class="s2">&amp;#34;123.1.2.3:9092,123.1.2.4:9092&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">topic_id&lt;/span> &lt;span class="o">=&lt;/span>&amp;gt; &lt;span class="s2">&amp;#34;apache-log&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nv">codec&lt;/span> &lt;span class="o">=&lt;/span>&amp;gt; json&lt;span class="o">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>그렇게 되면 codec이 맞아 각 필드로 &lt;code>이쁘게&lt;/code> 인덱싱을 할수 있게 되었다.&lt;/p></description></item></channel></rss>