<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Jongmin&#39;s Lifelog</title>
  <icon>https://www.gravatar.com/avatar/b47cc7a7502c34344cff139c35f843bd</icon>
  <subtitle>Survivor of digital era, SW engineer, evangelist, husband and father</subtitle>
  <link href="/atom.xml" rel="self"/>
  
  <link href="http://kimjmin.net/"/>
  <updated>2023-03-06T05:56:03.317Z</updated>
  <id>http://kimjmin.net/</id>
  
  <author>
    <name>Jongmin Kim (김종민)</name>
    <email>kimjmin@gmail.com</email>
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>2023 - 새로운 시작</title>
    <link href="http://kimjmin.net/2023/03/2023-03-06-new-journey/"/>
    <id>http://kimjmin.net/2023/03/2023-03-06-new-journey/</id>
    <published>2023-03-05T15:00:00.000Z</published>
    <updated>2023-03-06T05:56:03.317Z</updated>
    
    <content type="html"><![CDATA[<p>언제 2023년이 되었는가 싶더니 벌써 3월이 되었습니다. 시간이 정말 후딱 후딱 가네요.<br>작년 12월 부터 석달간 아주 푸~~~~욱 쉬며 시간을 보냈습니다.</p><h2 id="그-동안의-근황"><a href="#그-동안의-근황" class="headerlink" title="그 동안의 근황"></a>그 동안의 근황</h2><p>우선 2022년 크리스마스 시즌… 우리집의 실질적 지주 춘식이 굿즈가 6개 정도 늘었습니다.</p><iframe src="https://www.facebook.com/plugins/post.php?href=https%3A%2F%2Fwww.facebook.com%2Fkimjmin81%2Fposts%2Fpfbid0FrtUxgAARMvXc6KzTdN8wNsF3FdCYZXGC7cW3iMsaLn5m8ubqgzmPzwegcb4P5owl&show_text=true&width=640" width="640" height="700" style="border:none;overflow:hidden" scrolling="no" frameborder="0" allowfullscreen="true" allow="autoplay; clipboard-write; encrypted-media; picture-in-picture; web-share"></iframe><p>더 많은 춘식이를 영접하러 판교에 있는 카카오 아지트 프렌즈샾에도 다녀왔고요.</p><iframe src="https://www.facebook.com/plugins/post.php?href=https%3A%2F%2Fwww.facebook.com%2Fkimjmin81%2Fposts%2Fpfbid0354EpvZL8Qw9sBe3ZCEvif4zPDEZDWaaaj6biJpeomw4NeZm3BR627eRojAxBEMhbl&show_text=true&width=640" width="640" height="800" style="border:none;overflow:hidden" scrolling="no" frameborder="0" allowfullscreen="true" allow="autoplay; clipboard-write; encrypted-media; picture-in-picture; web-share"></iframe><p>2월 초에는 부여, 공주로 가족 여행을 다녀왔습니다.</p><iframe src="https://www.facebook.com/plugins/post.php?href=https%3A%2F%2Fwww.facebook.com%2Fkimjmin81%2Fposts%2Fpfbid02JHSYbXF3eARtiM9NJfZnSCtGV8EiC6kb7qmdEpAFimegpXnY6mL6fSELcTeuZ3DMl&show_text=true&width=640" width="640" height="900" style="border:none;overflow:hidden" scrolling="no" frameborder="0" allowfullscreen="true" allow="autoplay; clipboard-write; encrypted-media; picture-in-picture; web-share"></iframe><p>그리고 미루고 있던 숙제인 작년 유럽 : 프랑스 - 네덜란드 여행 이야기도 유튜브에 전부 편집해서 올렸습니다. 하루에 한편씩 총 13편 입니다.</p><iframe width="560" height="315" src="https://www.youtube.com/embed/AFmRGqyRiNw" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe><p><a href="https://www.youtube.com/watch?v=7SBunCUdwO0&amp;list=PLC6B2VGhofLr5D3j9OGZ9QaMivMtfJCgZ">래밀리TV : 래밀리 가족 유럽 여행기 전체 재생목록 보기</a></p><p>그리고 개인적으로 가장 많은 시간을 들인… 작업실 리모델링을 했습니다. 약간(?)의 탕진잼을 하면서 이런 저런 공구들을 구입해서 벽에 전시도 해 보고, 오드로이드 클러스터도 구축 해 보려고 이런 저런 잡스러운 짓들을 해 봤습니다. 딱히 뭘 해보겠다 하는 목적은 없고요, 그냥 평소에 지르고 싶었던 소소한 것들, 어릴때 한번쯤 꿈꿨던 나만의 본부? 느낌으로 꾸며보고 싶었습니다.</p><p>그리고 하는 김에 이런 저런 소소한 작업들 기록 해 보려고 유튜브 채널도 하나 새로 파 봤습니다.</p><iframe width="560" height="315" src="https://www.youtube.com/embed/JXpKDrHWMTE" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe><p><a href="https://www.youtube.com/@ramildaddy">래밀대디TV 유튜브 채널</a></p><p>그리고 어쩌다 보니 저희 본가 가족들과 약 2년에 한번 꼴로 갔었던 알펜시아에 아이들 개학 전 마지막으로 다녀왔고요.</p><iframe src="https://www.facebook.com/plugins/post.php?href=https%3A%2F%2Fwww.facebook.com%2Fkimjmin81%2Fposts%2Fpfbid0kqxcULnzpABBszUa6nUaNGov6n4wBC6KJZezkM9NpmsnARLGSwktLULzwqiHU4hrl&show_text=true&width=640" width="640" height="700" style="border:none;overflow:hidden" scrolling="no" frameborder="0" allowfullscreen="true" allow="autoplay; clipboard-write; encrypted-media; picture-in-picture; web-share"></iframe><h2 id="새로운-시작"><a href="#새로운-시작" class="headerlink" title="새로운 시작"></a>새로운 시작</h2><p><a href="/2022/12/2022-12-coffee-chatted">커피챗 하면서 만난 분들</a> 과의 감사한 시간 이후에도 몇몇 분들과의 순수한 고기 모임과, 개발자 분들의 소중한 네트워크 자리에도 초청 받아 감사하게도 좋은 시간을 보내기도 했습니다.<br>개인적으로 연락 주셔서 Elastic 관련해서 약간의 도움을 드리고자 만나 뵌 분들도 계셨고, 저명한 교수님께서 초청 해 주셔서 해외 있는 학생 분들께 온라인으로 강연을 할 기회도 있었습니다.</p><p>제가 쉬는 동안 기회를 주시고자 적지 않은 분들께서 연락을 주시고 만남을 가졌습니다.</p><ul><li>약 절반 정도의 분들은 제가 가진 Elastic 기술, 지식 경험을 높이 평가하시고 그와 관련된 기회를 제안 해 주셨고</li><li>약 30% 정도의 분들은 Developer Advocate 경험을 값지게 생각 하시고 제안을 주셨습니다.</li><li>나머지 분들은 새로운 경험에 대한 제안 해 주셨고요.</li></ul><p>매력적이지 않은 곳이 없었고, 전부 함께 하고 싶은 곳 뿐이었습니다. 한분 한분께 고개 숙여 다시 한번 깊이 감사드립니다.<br>합류 제안에 긍정적인 답변을 드리지 못한 분들께는 감사와 죄송한 마음에 다시 한번 고개 숙이며, 더 좋은 분들과 함께 계속 번창하시기를 진심으로 기원합니다.</p><p>3월 6일 부터 <strong>래블업(<a href="https://www.lablup.com">https://www.lablup.com</a>)</strong> 에서 <strong>Developer Advocate / Developer Experience Engineer (DEX)</strong> 로 새로운 출발을 하게 되었습니다.</p><p>AI 분야는 저도 아직 생소하고 무지한 분야라 지금은 기대 보다는 걱정이 앞서는 것이 사실입니다. 제 인생의 큰 마일스톤이었던 Elastic 을 비우고 새로운 채움을 해 나가야 하는 것이 가장 큰 과제일 것 같습니다.<br>그럼에도 불구하고 AI 라는 거대한 흐름에 말석에라도 매달려 볼 수 있는 기회를 갖게 된 것도 참 감사한 일이라는 생각이 듭니다. 새로운 곳에서도 제 가치를 다시 증명 해 나갈 수 있는 시간이 될 수 있도록 새로 마음을 다잡고 열심히 해야겠습니다. :)</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;언제 2023년이 되었는가 싶더니 벌써 3월이 되었습니다. 시간이 정말 후딱 후딱 가네요.&lt;br&gt;작년 12월 부터 석달간 아주 푸~~~~욱 쉬며 시간을 보냈습니다.&lt;/p&gt;
&lt;h2 id=&quot;그-동안의-근황&quot;&gt;&lt;a href=&quot;#그-동안의-근황&quot; cla
      
    
    </summary>
    
      <category term="Life" scheme="http://kimjmin.net/categories/Life/"/>
    
    
      <category term="Life" scheme="http://kimjmin.net/tags/Life/"/>
    
      <category term="Lablup" scheme="http://kimjmin.net/tags/Lablup/"/>
    
  </entry>
  
  <entry>
    <title>종민과의 커피챗 - 그 후.</title>
    <link href="http://kimjmin.net/2022/12/2022-12-coffee-chatted/"/>
    <id>http://kimjmin.net/2022/12/2022-12-coffee-chatted/</id>
    <published>2022-12-22T15:00:00.000Z</published>
    <updated>2022-12-23T05:26:28.595Z</updated>
    
    <content type="html"><![CDATA[<p>Elastic 을 떠난지도 3주가 되었네요.</p><p><a href="/2022/12/2022-12-coffee-chat">종민과의 커피챗 - Coffee Chat with Jongmin</a> 포스트를 쓰고 SNS 에 공유한 뒤 많은 분들이 메일, 페이스북, 링크드인, 신청 폼 으로 커피챗 신청을 해 주셨습니다. 약 2주 동안 거의 매일 약속이 2~3 개씩 있어서 열심히 돌아다녔네요. 그 동안 집에서 재택 근무를 많이 하다가 오히려 이번 기회에 바깥에 많이 돌아다니니 체력적으로 많이 지치던게 느껴졌습니다. ㅎㅎ</p><p><img src="busy_free.jpeg" alt="백수가 제일 바쁨"></p><p>구글이 알고리즘이 제 블로그랑 SNS를 반영한건지, 유튜브에 실제로 최근 실리콘 밸리의 Layoff 와 관련된 영상도 요즘 부쩍 눈에 띄더라고요.<br><a href="https://youtu.be/EOzswWI_QE8">당일 통보, 당일 해고. 그날의 이야기</a><br>구독도 안 한 처음 보는 분의 채널이었는데도 우연히 구글 알고리즘이 보여준 저 영상을 보고도 동병상련을 느끼기도 했습니다.</p><p>좋은 기회를 제안 해 주신 분들, 그냥 안부를 묻고 싶어서 만나뵌 분들, 아무런 교류가 없었는데 호기심에 신청 해 주셨던 처음 뵙는 분들, 정말 다양한 분들이 계셨습니다. 예술, 인공지능, 클라우드, 건축, 오픈소스, HR, 경영 등 다양한 분야에 계신 분들과 흥미로운 주제로 많은 이야기들을 나누었고, 시야와 생각이 좀 더 넓어지고 자유로워 지는 느낌을 많이 받았습니다. layoff 이후 우울해 질 수 있었던 시기에 시간 내어 주시고 자존감을 많이 높여주신 분들께 다시 한번 감사의 말씀을 드립니다.</p><p>이전 포스트에 올려두었던 소개자료(이력서) 를 보시고 다양한 제안을 해 주셨는데, 역시나 Tech Evangelist 포지션에 대한 제안이 제일 많았습니다. 어느 정도 팀과 제품이 완성이 되었는데 고객들의 눈높이에 맞춰 <code>&quot;우리 제품 정말 좋은데 이걸 말로 설명할 방법이 없네&quot;</code> 의 해결을 필요로 하신 분들이 계셨습니다. 그 밖에도 <code>Elastic 기술과 데이터 분석</code> 을 함께 해 보고자 하시는 분들도 계셨고요. 사실 예전부터 <code>팀 관리나 작은 조직의 CTO</code> 일도 해 보고 싶다는 생각도 있었는데 그와 관련한 이야기와 경험담도 나눠 주신 분들도 계셨습니다. 하나의 만남도 허투로 할 것이 없는 모두 소중한 시간들이었습니다.</p><p>다들 바쁘고 경제적으로도 많이 어려운 시기에 이런 배부른 이야기 하긴 죄송스럽지만 우선 지금은 잠시 쉬면서 스스로 정비를 하고 오랬동안 손 놓았던 코딩 공부도 좀 다시 해 볼 예정입니다. 여유가 되면 개인적으로 프로젝트도 한번 해 보고요.<br>아이들 겨울 방학 동안은 가족과 함께 보내고 2023년 2월 중순에서 말 정도 즈음에 일 시작하면 좋겠다 생각 하는 중입니다. 그 중에도 제안 주셨던 분들께는 다시 또 찾아뵙고 인사 드리고 하겠습니다.</p><p>날씨도 경제도 많이 얼어붙어서 몸도 마음도 추운 시기입니다. 모두들 힘들지만 기운내시고 행복한 연말, 힘찬 새해 맞으시길 바랍니다. 🥰</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;Elastic 을 떠난지도 3주가 되었네요.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/2022/12/2022-12-coffee-chat&quot;&gt;종민과의 커피챗 - Coffee Chat with Jongmin&lt;/a&gt; 포스트를 쓰고 SNS 에 공유한 뒤 많은 분들이
      
    
    </summary>
    
      <category term="Life" scheme="http://kimjmin.net/categories/Life/"/>
    
    
      <category term="Life" scheme="http://kimjmin.net/tags/Life/"/>
    
      <category term="Coffee" scheme="http://kimjmin.net/tags/Coffee/"/>
    
  </entry>
  
  <entry>
    <title>종민과의 커피챗 - Coffee Chat with Jongmin</title>
    <link href="http://kimjmin.net/2022/12/2022-12-coffee-chat/"/>
    <id>http://kimjmin.net/2022/12/2022-12-coffee-chat/</id>
    <published>2022-12-06T15:00:00.000Z</published>
    <updated>2023-03-06T05:55:47.228Z</updated>
    
    <content type="html"><![CDATA[<p>Elastic 을 떠나게 되었습니다. <a href="/2022/12/2022-12-farewell-elastic">자세한 스토리는 여기에</a><br><span style='color:blue'>I left Elastic. <a href="/2022/12/2022-12-farewell-elastic/#I-left-Elastic-abruptly">Detail story is here</a></span></p><p>연말 동안 느긋하게 시간을 갖고 새로운 곳에 가 보고 사람들을 만나며 그동안 우물 안에 갇혀 있던 생각들을 넓혀보려고 합니다.<br><span style='color:blue'>During the end of the year days, I am going to spend some time to relax, visit new places, meet people, and wide up my thoughts and minds that have been stuck in the well.</span></p><p>저에 대한 소개 자료입니다. 표준 이력서 형식이 아니라 생각의 흐름대로 정리 해 보았습니다. 표준 형식의 이력서는 커피챗 이후 마음이 결정되면 필요한 내용으로 작성 해 드리겠습니다.<br><span style='color:blue'>This is an introduction about me. Rather than a standard resume format, I wrote it according to the flow of thought. If resume in standard format needed, I will write with necessary content after the coffee chat.</span></p><ul><li><a href="http://kimjmin.net/data/Resume_Jongmin-Kim_2022-kr.pdf">김종민 소개자료 - 한국어</a></li><li><a href="http://kimjmin.net/data/Resume_Jongmin-Kim_2022-en.pdf">Resume in English</a> - <span style='color:blue'>Google translated from Korean and didn’t inspected yet. Some sentences may not clear.</span></li></ul><p>제 커피챗 일정은 잡히는 대로 아래 캘린더에 업데이트 해 놓겠습니다. 필요하시면 참고하세요.<br><span style='color:blue'>I will update my schedule at calendar below once planned. Please refer if necessary</span></p><iframe src="https://calendar.google.com/calendar/embed?height=600&wkst=1&bgcolor=%23ffffff&ctz=Asia%2FSeoul&showCalendars=0&showTabs=1&showNav=1&showPrint=0&src=NjYwMjE0NGU0NDUzYWIyN2RjZjA3MTdlYzJiNTI1MTBiYTIyMDg1MWQ0NjA0YjAzZDM1ZTM0OTMxNDFkNzFmM0Bncm91cC5jYWxlbmRhci5nb29nbGUuY29t&color=%23009688" style="border:solid 1px #777" width="800" height="600" frameborder="0" scrolling="no"></iframe><p>신청은 편하신 채널로 주시거나<br><span style='color:blue'>You can make contact through convenient channel for you.</span></p><ul><li>Facebook : <a href="https://www.facebook.com/kimjmin81">https://www.facebook.com/kimjmin81</a></li><li>Linkedin : <a href="https://www.linkedin.com/in/kimjmin">https://www.linkedin.com/in/kimjmin</a></li><li>Twitter : <a href="https://twitter.com/kimjmin">https://twitter.com/kimjmin</a></li><li>Email : <a href="mailto:kimjmin@gmail.com">kimjmin@gmail.com</a></li></ul><p>아래 폼을 입력 해 주시면 제가 연락 드리겠습니다.<br><span style='color:blue'>Or you may submit form below. I will make contact to you.</span></p><iframe src="https://docs.google.com/forms/d/e/1FAIpQLSe65g-Br7FVq06eFWgUIQ1Z0Meq20K6uI4HkBvnBJS1R_kNuA/viewform?embedded=true" width="640" height="931" frameborder="0" marginheight="0" marginwidth="0">로드 중…</iframe>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;Elastic 을 떠나게 되었습니다. &lt;a href=&quot;/2022/12/2022-12-farewell-elastic&quot;&gt;자세한 스토리는 여기에&lt;/a&gt;&lt;br&gt;&lt;span style=&#39;color:blue&#39;&gt;I left Elastic. &lt;a href=&quot;/2
      
    
    </summary>
    
      <category term="Life" scheme="http://kimjmin.net/categories/Life/"/>
    
    
      <category term="Life" scheme="http://kimjmin.net/tags/Life/"/>
    
      <category term="Coffee" scheme="http://kimjmin.net/tags/Coffee/"/>
    
  </entry>
  
  <entry>
    <title>Elastic 을 떠나며 - Farewell Elastic</title>
    <link href="http://kimjmin.net/2022/12/2022-12-farewell-elastic/"/>
    <id>http://kimjmin.net/2022/12/2022-12-farewell-elastic/</id>
    <published>2022-12-02T15:00:00.000Z</published>
    <updated>2022-12-21T02:05:44.538Z</updated>
    
    <content type="html"><![CDATA[<p><a href="#I-left-Elastic-abruptly">Jump to english</a></p><h4 id="갑작스럽게-Elastic-을-떠나게-되었습니다"><a href="#갑작스럽게-Elastic-을-떠나게-되었습니다" class="headerlink" title="갑작스럽게 Elastic 을 떠나게 되었습니다."></a>갑작스럽게 Elastic 을 떠나게 되었습니다.</h4><p>실리콘밸리의 추운 감원 바람이 Elastic 에도 불어오게 되면서 12월 1일 13% 의 인력을 감축하게 된다는 CEO 의 발표가 있었습니다.<br><a href="https://www.elastic.co/blog/ceo-ash-kulkarni-email-to-elastic-employees">https://www.elastic.co/blog/ceo-ash-kulkarni-email-to-elastic-employees</a></p><p>저희 Community 팀 대다수 인원들 또한 대상이 되어 저 역시 Elastic 을 갑작스레 떠나게 되었습니다.</p><p>Elastic 이라는 회사가 제게 어떤 의미였는지 개인적으로 저를 아시는 분들은 아마도 다 알고 계실거라 생각됩니다. 2015년 6월 저의 생일날 합류하게 되어 (<a href="/2015/06/join-elastic">Elastic 입사 이야기</a>) 7년 6개월간 몸담으며 제 인생에 있어서 황금기라고 말할 수 있는 시기를 보내게 해 준 회사였고, 저에게 한없이 부푼 꿈과 행복을 안겨준 회사였습니다.</p><p>처음 통보를 받았을 때는 잠깐 정신이 멍했습니다. 우선 사설 채널로 한국 팀 동료분들께 상황을 설명드리고 일단 무엇부터 해야 하나 차분히 생각하기 시작했습니다.</p><p>그 와중에도 제일 먼저 생각난 것은 당장 다음주 부터 참석하기로 했던 행사들은 어쩌지 하는 생각이 먼저 들면서 (저도 어쩔 수 없는 대한민국 직장인인가봅니다) 행사 담당자분들께 참여가 불가능하게 되어 죄송하다는 메일과 단톡 메세지들을 먼저 전달했습니다. 그리고 나서 업무적으로 관계가 있던 생각나는 가까운 몇몇 분들께도 연락을 드렸습니다.</p><p>그런 후에야 앞으로 어떻게 될 지를 설명하는 HR 팀 메일을 차근 차근 읽어보기 시작했습니다. 회사 내부 정보에 접속할 수 있는 대부분 채널들은 이미 종료 된 상태이고, 이메일과 캘린더는 24시간 동안은 유지될 예정이니 처리 못한 expense 등을 처리하라는 내용 등이 있었습니다. 짧게 진행된 (새로 예정되었을) 매니저와의 콜에서도 회사 상황이 이렇게 되었다 라는 이야기만 들으며 상황만 전달 받고 콜을 마쳤습니다.</p><p>보도가 나간 사이 페이스북과 링크드인 그리고 개인 메일로 현/전 직장 동료들로부터 수많은 격려와 안부의 메시지를 받았습니다. 위로의 말도 있었고 축하의 말도 있었습니다. 한국 동료분들은 전화 주셔서 아직 기회는 있을테니 힘내라는 격려를 해 주셨고, 하루 짧은 시간 동안 당황 -&gt; (아주 짧은)걱정 -&gt; (매우 긴)감사 의 시간을 보냈습니다.</p><p>우선 24시간 내에 정리해야 할 것들이 있어 첫날은 정신 없이 정리 할 것들을 정리했습니다. 둘째날인 금요일에 <a href="https://www.linkedin.com/feed/update/urn:li:activity:7004261753379291136/">링크드인에 상황을 남겼고</a> 여기에도 많은 전 동료들이 위로의 반응을 남겨주어 큰 위로를 받았습니다.</p><p>저녁에 참석하기로 했던 데브릴 나이트도 상황이 이렇게 됬는데 가야 하나? 잠깐 고민했지만, 오랬만에 반가운 분들의 얼굴을 보고 싶어 마음먹고 참석했습니다. 아쉽게도 가져가려던 스티커와 스웨그는 못 가져가게 되었지만 참석해서 만난 분들께는 상황을 설명을 드렸고 마찬가지로 큰 위로의 시간을 얻고 왔습니다.</p><p><img src="devril_night.jpeg" alt="DevRel Night"></p><p>이제 다음 행보를 준비해야 하는데 레쥬메 정리는 시간이 걸릴것 같아 우선 다른 분들께도 이제 상황을 알려드려야 할 것 같아서 블로그를 먼저 쓰게 되었습니다.</p><p>서두에 말씀드렸지만 Elastic 이라는 조직, 제품, 사람들은 제 인생에 있어 정말 큰 선물과도 같았습니다. 국제적으로 어려운 경제 상황 때문에 어쩔 수 없는 작별을 하게 되었지만 평생 제 인생에 큰 행복의 기억으로 남아있을 것입니다. 조직을 떠나게 되어도 커뮤니티와의 인연은 가능한 한 계속 유지 할 생각입니다.</p><p>그 동안 제 실력보다는 Elastic 이라는 브랜드를 등에 업고 활동 한 것이 많았고, 반대로 Elastic 역시 제가 했던 활동들을 기반으로 김종민 이라는 브랜드를 이용해서 한국에서의 입지를 넓혀갔던지라, 저와 Elastic 은 뗄레야 떼기 힘든 관계였습니다. 아마 이런 상황이 아니었다면 제 스스로 Elastic 의 문을 열고 새로운 곳으로 나가기는 힘들었겠지요. 이 뜻밖의 상황이 오랬만에 눈을 돌려 바깥 세상도 다시 한번 돌아보고, Elastic 이라는 브랜드를 떼어낸 김종민 이라는 개인이 얼마나 평가받을 수 있는 사람인지도 시험 해 볼 수 있는 기회가 됬다고 생각하니 오히려 홀가분해지고 살짝 기운도 납니다.</p><p>그 동안 Elastic 을 통해 했던 유니크한 경험들</p><ul><li>커뮤니티 활동 경력을 기반으로 리모트 입사</li><li>국내 커뮤니티 회원 0 -&gt; 8k / 국내 직원 0 -&gt; 00 / 국내 고객 0 -&gt; 000</li><li>수백명이 모인 컨퍼런스에서의 발표. 제 사회 초년때의 버킷리스트 중 하나가 100명 앞에서 발표하기였습니다.</li><li>제 영상과 블로그를 보고 공부해서 취업에 성공했다는 주니어 엔지니어분의 감사 메일</li><li>커뮤니티에서 발굴해서 Elastic 에 입사해 제 덕분에 Elastic 에서 자신이 꿈꾸던 삶을 시작할 수 있게 되었다고 말씀 해 주신 직원분</li><li>Elastic 입사 당시 국내에는 손에 꼽을 정도로 적었던 DevRel 직군이 이제는 수십명이 모여 밋업을 할 정도로 성장한 대한민국의 DevRel 문화</li><li>멘토링을 통해 Elastic 글로벌 컨퍼런스에서 온라인 발표 경험을 제공 해 드린 대학생 분들</li><li>마지막으로 실리콘밸리의 감원 칼바람(?) 을 맞아본 경험 (역시 매우 소중하게 생각합니다)</li></ul><p>들은 어디를 가던 간에 평생 잊지 못할 제 믿거름이 될거라 믿습니다.</p><p>지금 또 드는 생각이, 그 동안 감사하게 꾸준히 참여 해 왔던 커뮤니티 컨퍼런스</p><ul><li>파이콘 (2016~)</li><li>데이터야놀자 (2018~)</li><li>한국 커뮤니티 데이 (2016~)</li><li>올해 처음 참석한 우부콘</li></ul><p>리더 분들께는 앞으로 Elastic 의 참여는 어렵게 되서 죄송한 마음이 드네요. 아마 아시아 커뮤니티 팀 전체가 정리가 되어서 당분간은 저 대신 지원 할 사람도 없는 상황일겁니다.</p><p>당분간은 쉬면서 생각도 정리하면서 여유를 갖고 시간을 좀 보내려고 합니다. 오히려 그동안 코로나도 있고 해서 재택하느라 집에만 있었는데, 일부러 밖에도 자주 나가서 기운을 차려야 할 것 같습니다. 커피챗 해 주실 분들 편하게 연락주세요. 😁</p><h4 id="I-left-Elastic-abruptly"><a href="#I-left-Elastic-abruptly" class="headerlink" title="I left Elastic abruptly"></a>I left Elastic abruptly</h4><p>As Silicon Valley’s cold winds of layoff also blew to Elastic, the CEO announced on December 1st that Elastic is reducing 13% of workforce.<br><a href="https://www.elastic.co/blog/ceo-ash-kulkarni-email-to-elastic-employees">https://www.elastic.co/blog/ceo-ash-kulkarni-email-to-elastic-employees</a></p><p>My community team has also been targeted and I recently had to leave Elastic.</p><p>Those of you who know me personally probably know what the company Elastic meant to me. I joined on my birthday in June 2015 (<a href="/2015/06/join-elastic">History of Joining Elastic</a>), and it was a company that allowed me to spend a period that can be called a golden age in my life for 7 years and 6 months. Elastic gave me unlimited dreams and happiness.</p><p>When I notified, I was momentarily stunned. First, I explained the situation to my Korean teammates through a private channel, and I started thinking about what need to be done first.</p><p>In the meantime, the first thing that came to my mind was the events I was supposed to attend from next week (I guess I’m a Korean worker who can’t help it), so I sent emails and chat messages to organizers of the events to apologize for not being able to participate. Then, I text to a few people close to me who I had a business relationship with.</p><p>After that, I started reading through the email from HR team explaining what going to be happened next. Most of working channels and accesses of the company’s internal information have already been terminated. Only email calendar system will be maintained for 24 hours, so there was a message to process expenses that could not be processed and handle other things need to be done. Short zoom call with new team leader (Team was recently re-orgaznied) was scheduled. At the call, I just was told of company situation and general informations and ended up shortly.</p><p>After the public announcement, I received numerous messages of encouragement and regards from current and former colleagues through Facebook, LinkedIn, and personal mail. There were words of encorage and words of congratulations. My Korean colleagues called me, encouraged me to cheered me up with words of hope. My day passed with moments of  embarrassment -&gt; (short) worrying -&gt; (long) gratitude.</p><p>First of all, there are things that need to be handled out within 24 hours, so the first day was hectic. On Friday (the second day), <a href="https://www.linkedin.com/feed/update/urn:li:activity:7004261753379291136/">I left my situation on LinkedIn</a> also many former colleagues left consoling responses, giving great comfort.</p><p>I bit hasitated with DevRel Night event, which was supposed to attend that evening. I am in situation like this, should I go? I thought about it for a bit, and I decided to attend because wanted to see people’s in face who hadn’t seen for a long time. Unfortunately, I couldn’t bring the stickers and swag that I wanted to share. I explained the situation to the people I met there and I got a great time of comfort as well.</p><p><img src="devril_night.jpeg" alt="DevRel Night"></p><p>Now I need to prepare for my next move, but resume update will take some time. First of all, I decided to let others know about my current situation, so I am writing this blog first.</p><p>As I said for the first, the organization, product, and people of Elastic have been a great gift for my life. I had to say goodbye because of world wide economical situation, but it will remain as a memory of great happiness for the rest of my life. Even if I leave the organization, I plan to keep my relationship with the community as long as possible.</p><p>In the meantime, many my activities been done with the brand Elastic I am sitting on the shoulder rather than my own capabilities. And also Elastic was using the name of Kim Jongmin’s (which is me) value to expand its presence in Korea. It meant me and Elastic were inseparable. If it weren’t like this, it will never happened for me to open out the door of Elastic on my own and walk away. I am thinking in good way that this unexpected situation was an opportunity to look around the outside world once again after a long time, and to test how much individual Kim Jongmin, who step down from shoulder of Elastic, can be evaluated by himself.</p><p>Unique experiences I had with Elastic so far</p><ul><li>Get hired as remote employment with background of community activity experience</li><li>Raised local community members 0 -&gt; 8k / employees 0 -&gt; 00 / customers 0 -&gt; 000</li><li>Spoke at conference sessions with hundreds of people. One of my bucket lists in my early years was speaking in front of 100 people.</li><li>Got thank you email from a junior engineer who informed that he made getting a job after watching my videos and blogs and studied with it.</li><li>Local employees who discovered from the community and joined Elastic and thanked to me, he was able to start a new life in Elasitc which he was looking for.</li><li>Korea’s DevRel culture has grown to such an extent that dozens of DevRel positions, which were very few and hard to find in Korea at the time of joining Elastic, are now gathering and holding meetups.</li><li>College students who provided online presentation experience at the Elastic Global Conference through my mentoring.</li><li>Finally, the experience of being layed off in Silicon Valley way. (I truly think it is very precious experience)</li></ul><p>I believe that wherever I go, these will be a valuable source of nutrient for me.</p><p>And one thing is hitting my brain, about the community conferences that I have been gratefully participating in.</p><ul><li>PyCon (2016~)</li><li>Datayanolja (Play Data) (2018~)</li><li>Korea Community Day (2016~)</li><li>Ubucon (attended for the first time this year)</li></ul><p>To organizers, I feel sorry that it will be difficult for Elastic to be sponsor in the future. Most asian community folks has left, so there will be no one to support for sponsoring in the future.</p><p>For the time being, I’m going to take a break, organize my thoughts, and will spend some time. Rather, I’ve been staying at home most time (becuase of COVID), now I need to go outside to see people and refresh and wide up my mind. If you would like to have a coffee chat with me, please feel free to contact. 😁</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;&lt;a href=&quot;#I-left-Elastic-abruptly&quot;&gt;Jump to english&lt;/a&gt;&lt;/p&gt;
&lt;h4 id=&quot;갑작스럽게-Elastic-을-떠나게-되었습니다&quot;&gt;&lt;a href=&quot;#갑작스럽게-Elastic-을-떠나게-되었습니다&quot; class=
      
    
    </summary>
    
      <category term="Life" scheme="http://kimjmin.net/categories/Life/"/>
    
    
      <category term="Elastic" scheme="http://kimjmin.net/tags/Elastic/"/>
    
  </entry>
  
  <entry>
    <title>서울시 지하철 승하차인원 대시보드 v3 - 2. 대시보드 생성</title>
    <link href="http://kimjmin.net/2022/07/2022-07-seoul-metro-v3-2/"/>
    <id>http://kimjmin.net/2022/07/2022-07-seoul-metro-v3-2/</id>
    <published>2022-07-24T15:00:00.000Z</published>
    <updated>2022-12-21T02:05:44.537Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p><a href="/2022/07/2022-07-seoul-metro-v3-1">서울시 지하철 승하차인원 대시보드 v3 - 1. 데이터 색인</a><br>서울시 지하철 승하차인원 대시보드 v3 - 2. 대시보드 생성</p></blockquote><iframe width="560" height="315" src="https://www.youtube.com/embed/UewT3OMnI_c" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe><p>지난 시간에 저장한 데이터를 가지고 이번에는 Kibana 대시보드를 만들었습니다. 이 포스트에서 Kibana 대시보드 만드는 방법을 하나 하나 설명하지는 않습니다. 자세한 내용은 위 영상에 설명되어 있으니, 영상을 보시면서 차근 차근 따라 해 보시기 바랍니다.</p><p><img src="kibana-dashboard.png" alt=""></p><p>완성된 대시보드는 아래 링크로 가면 직접 실행 해 보실 수 있습니다.</p><ul><li><a href="https://elastic-korea-user-group.kb.asia-northeast3.gcp.elastic-cloud.com:9243/s/public/app/dashboards?auth_provider_hint=anonymous1#/view/341038f0-0be8-11ed-831c-fd6dfb8e6030">서울시 지하철 승하차인원 대시보드</a></li></ul><p>이 외에도 앞으로 다양한 데모들을 로그인 없이 공개적으로 볼 수 있게 계속 만들어 나갈 계획입니다.</p><ul><li>Elastic 한국 커뮤니티용 Kibana 데모 모음 : <a href="https://ela.st/demo-kr">https://ela.st/demo-kr</a></li></ul><p>만약에 공유하실 수 있는 좋은 데모가 있으신 분들은 위에 kibana 데모 모음 화면의 계정 신청 링크에서 신청하시고 함게 만들어 주시면 감사드리겠습니다.</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;/2022/07/2022-07-seoul-metro-v3-1&quot;&gt;서울시 지하철 승하차인원 대시보드 v3 - 1. 데이터 색인&lt;/a&gt;&lt;br&gt;서울시 지하철 승하차인원 대시보드 v3 - 2. 대시보드 생성&lt;/p&gt;

      
    
    </summary>
    
      <category term="Engineering" scheme="http://kimjmin.net/categories/Engineering/"/>
    
    
      <category term="Elastic" scheme="http://kimjmin.net/tags/Elastic/"/>
    
      <category term="Engineering" scheme="http://kimjmin.net/tags/Engineering/"/>
    
      <category term="Kibana" scheme="http://kimjmin.net/tags/Kibana/"/>
    
      <category term="Seoul Metro" scheme="http://kimjmin.net/tags/Seoul-Metro/"/>
    
      <category term="Dashboard" scheme="http://kimjmin.net/tags/Dashboard/"/>
    
  </entry>
  
  <entry>
    <title>서울시 지하철 승하차인원 대시보드 v3 - 1. 데이터 색인</title>
    <link href="http://kimjmin.net/2022/07/2022-07-seoul-metro-v3-1/"/>
    <id>http://kimjmin.net/2022/07/2022-07-seoul-metro-v3-1/</id>
    <published>2022-07-01T15:00:00.000Z</published>
    <updated>2022-12-21T02:05:44.537Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>서울시 지하철 승하차인원 대시보드 v3 - 1. 데이터 색인<br><a href="/2022/07/2022-07-seoul-metro-v3-2">서울시 지하철 승하차인원 대시보드 v3 - 2. 대시보드 생성</a></p></blockquote><iframe width="560" height="315" src="https://www.youtube.com/embed/W_oU9JF5fsE" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe><p>제가 Elastic Stack 을 처음 시작할 때 부터 사골처럼 우려먹고 있는 예제가 있습니다. 그런데 아직도 이것보다 좋은 예제 데이터셋을 찾지를 못했네요.</p><p>바로 서울시 지하철 승하차인원 대시보드입니다.</p><h6 id="2015년-EMOCON-에서-처음-발표한-버전-Elasticsearch-2-x-Kibana-4-x"><a href="#2015년-EMOCON-에서-처음-발표한-버전-Elasticsearch-2-x-Kibana-4-x" class="headerlink" title="2015년 EMOCON 에서 처음 발표한 버전 (Elasticsearch 2.x, Kibana 4.x)"></a>2015년 EMOCON 에서 처음 발표한 버전 (Elasticsearch 2.x, Kibana 4.x)</h6><ul><li><a href="https://youtu.be/ec-XzM6_CgU">ELK 스택을 사용한 서울시 지하철 대시보드 만들기</a></li></ul><h6 id="2019-년에-3편으로-나누어-다시-녹화한-시리즈-버전-Elastic-Stack-7-x"><a href="#2019-년에-3편으로-나누어-다시-녹화한-시리즈-버전-Elastic-Stack-7-x" class="headerlink" title="2019 년에 3편으로 나누어 다시 녹화한 시리즈 버전 (Elastic Stack 7.x)"></a>2019 년에 3편으로 나누어 다시 녹화한 시리즈 버전 (Elastic Stack 7.x)</h6><ul><li><a href="https://youtu.be/ypsEZXVYLo4">Elastic Stack을 이용한 서울시 지하철 대시보드 다시 만들기 #1</a></li><li><a href="https://youtu.be/KzxpvgO8h5k">Elastic Stack을 이용한 서울시 지하철 대시보드 다시 만들기 #2</a></li><li><a href="https://youtu.be/h1EAH4hTsiw">Elastic Stack을 이용한 서울시 지하철 대시보드 다시 만들기 #3</a></li></ul><p>이 대시보드를 Elastic Stack 새 버전으로 다시 만들어보려고 합니다. 내용이 길어질 것 같아 이 포스트에서는 데이터 색인 부분만 다루도록 하겠습니다.</p><h2 id="데이터-소스-파일"><a href="#데이터-소스-파일" class="headerlink" title="데이터 소스 파일"></a>데이터 소스 파일</h2><p>그 동안 소스 데이터는 서울시 공공데이터 포털에서 제공하는</p><ul><li>서울시 역코드로 지하철역 위치 조회</li><li>서울교통공사 지하철 역명 다국어 표기 정보</li><li>서울교통공사 연도별 일별 시간대별 역별 승하차 인원</li></ul><p>등의 json, csv 데이터를 받아 여기에서 Elasticsearch 에 색인하기 적합한 모양으로 <a href="https://github.com/eskrug/elastic-demos/blob/master/seoul-metro-logs/01-data-process-ingest.md">변환하는 프로그램</a> 을 만들어 사용하고 있었습니다.<br>그런데 언제부터인가 서울시 공공데이터 포털에서 위 데이터들이 사라져서 찾을수가 없습니다. 또 <a href="http://www.seoulmetro.co.kr/kr/board.do?menuIdx=551">서울교통공사 홈페이지</a>에서 제공하는 데이터도 어떤 년도는 xlsx 어떤 년도는 csv 처럼 파일 형식이 통일되어 있지도 않고, 컬럼도 어떤년도는 날짜가 A열에 있고 어떤 년도는 A열에 역 코드가 있는 등 데이터 좀 뒤죽박죽 아쉽게 되어 있었습니다.</p><p><img src="metro-source-provide.jpg" alt=""></p><p>그래서 제가 이전에 만들었던 데이터들을 기반으로 하여 좀 더 다양한 데이터들을 찾아서 추가하고, 형식을 통일한 뒤 배포하기 쉬운 형태로 제작해서 직접 배포하기로 결정했습니다.</p><p>소스 파일은 아래의 캐글(Kaggle) 페이지에 있습니다. 내려받으려면 캐글 계정이 필요합니다.<br><a href="https://www.kaggle.com/datasets/kimjmin/seoul-metro-usage">https://www.kaggle.com/datasets/kimjmin/seoul-metro-usage</a><br><img src="kaggle-seoul-metro.png" alt="서울 지하철 승하차 인원 Seoul Metro Usage Kaggle Page"></p><p>서울 지하철 승하차인원 대시보드는 그냥 보여주는 용도가 아니라, 항상 Elastic 을 처음 시작하시는 분들이 처음부터 쉽게 따라서 만들어 볼 수 있는 튜토리얼을 제공하는 것이 목적이기 때문에 소스 파일에 다 넣어놓지 않고 색인 때 다양한 과정을 통해 데이터를 제대로 만들어 가도록 했습니다. (캐글에서 제공하는 용량이 한정적이기도 했고요)</p><p>제공하는 파일들은 다음과 같습니다.</p><h5 id="seoul-metro-station-info-csv"><a href="#seoul-metro-station-info-csv" class="headerlink" title="seoul-metro-station-info.csv"></a><a href="https://www.kaggle.com/datasets/kimjmin/seoul-metro-usage?select=seoul-metro-station-info.csv">seoul-metro-station-info.csv</a></h5><p>서울 지하철 역별 메타정보 파일입니다. 사이즈는 약 85KB 이고 1~8호선 총 286 개의 역 정보가 저장되어 있습니다.<br>각 행의 정보는 다음과 같습니다.</p><ul><li><code>station.code</code> : 역 내부 코드 - 각 로그 파일의 station_code 와 연결되는 키값 (238)</li><li><code>station.fr_code</code> : 역 공개 코드 (211-2)</li><li><code>line.num</code> : 호선 number (2)</li><li><code>line.name</code> : 호선 text (2호선)</li><li><code>line.name_sub</code> : 세부 호선 text (을지로순환선) - 2호선 처럼 한 호선이 을지로순환선, 성수지선 처럼 또 나눠진 것을 구분하기 위한 정보입니다.</li><li><code>line.station_seq</code> : 호선별 역 순번 (23) - 나중에 지도에 각 호선을 연결하여 그릴 때 사용하기 위한 호선별 순번입니다. <strong>station.code</strong> 는 역이 생긴 순서대로 매겨지기 때문에 예를 들어 두 역 사이에 나중에 역이 추가된다면 순번이 <strong>24 - 29 - 25</strong> 처럼 되어 버려서 지도에 그릴때 직선이 뒤죽박죽이 되어 버립니다.</li><li><code>station.name_full</code> : 역명 전체 (서울대입구(관악구청))</li><li><code>station.name</code> : 역명 “|” 로 구분 (서울대입구|관악구청) - 나중에 이 역을 [“서울대입구”, “관악구청”] 같이 배열로 저장하기 위해 구분 한 것입니다.</li><li><code>station.name_chc</code> : 역명 한자 (서울大入口(冠岳區廳))</li><li><code>station.name_chn</code> : 역명 중국어 (首尔大学(冠岳区厅))</li><li><code>station.name_en</code> : 역명 영어 (Seoul Nat‘l Univ. (Gwanak-gu Office))</li><li><code>station.name_jp</code> : 역명 일본어 (ソウルデイック)</li><li><code>geo.latitude</code> : 좌표계 위도 (37.481247)</li><li><code>geo.longitude</code> : 좌표계 경도 (126.952739)</li><li><code>geo.sigungu_code</code> : 시군구코드 (11210) - <a href="https://maps.elastic.co/#file/south_korea_municipalities">Elastic Map Service</a> 를 이용해서 표현하기 위한 값입니다.</li><li><code>geo.sigungu_name</code> : 시군구명 (관악구)</li><li><code>geo.addres_road</code> : 역 도로명 주소 (서울특별시 관악구 남부순환로 지하1822(봉천동))</li><li><code>geo.address_land</code> : 역 지번 주소 (서울특별시 관악구 봉천동 979-2 서울대입구역(2호선))</li><li><code>geo.phone</code> : 역 대표 전화번호 (02-6110-2281)</li></ul><h5 id="seoul-metro-2015-logs-csv-seoul-metro-2021-logs-csv"><a href="#seoul-metro-2015-logs-csv-seoul-metro-2021-logs-csv" class="headerlink" title="seoul-metro-2015.logs.csv ~ seoul-metro-2021.logs.csv"></a><a href="https://www.kaggle.com/datasets/kimjmin/seoul-metro-usage?select=seoul-metro-2015.logs.csv">seoul-metro-2015.logs.csv</a> ~ <a href="https://www.kaggle.com/datasets/kimjmin/seoul-metro-usage?select=seoul-metro-2021.logs.csv">seoul-metro-2021.logs.csv</a></h5><p>년도별 지하철 승하차 인원 집계 파일입니다. 각 파일의 사이즈는 100MB 를 넘지 않도록 했습니다. 이렇게 하면 Filebeats 나 Logstash, Elastic Agent 를 사용하지 않고 Kibana 에서 직접 파일 업로드 기능을 이용해서 색인 할 수 있습니다. (제한 100MB)<br>각 행의 정보는 다음과 같습니다.</p><ul><li><code>timestamp</code> : 타임스탬프, 매 시작 정각값이며 ISO8601 형식으로 되어 있습니다 (2015-01-02T00:00:00.000+09:00)</li><li><code>station_code</code> : 역 내부 코드 - <strong>seoul-metro-station-info.csv</strong> 의 <strong>station.code</strong> 에 대입합니다. (151)</li><li><code>people_in</code> : 승차인원 (281)</li><li><code>people_out</code> : 하차인원 (311)</li></ul><p>이제 <a href="https://www.kaggle.com/datasets/kimjmin/seoul-metro-usage">캐글 페이지</a> 에서 Download 버튼을 눌러 소스들을 다운로드 합니다.<br><img src="file-download.png" alt=""></p><h2 id="seoul-metro-station-info-csv-색인"><a href="#seoul-metro-station-info-csv-색인" class="headerlink" title="seoul-metro-station-info.csv 색인"></a>seoul-metro-station-info.csv 색인</h2><p>이제 먼저 seoul-metro-station-info.csv 파일을 색인하겠습니다. 먼저 사용할 Elasticsearch 와 Kibana 를 준비합니다. Elastic Cloud 를 사용하여도 되고 직접 설치한 클러스터가 있으면 그것을 사용해도 됩니다.<br>간편하게 Kibana 의 Data Visualizer 의 파일 업로드 기능을 이용해서 색인 하겠습니다. Machine Learning 메뉴에서 File 을 선택하면 됩니다. 참고로 머신러닝 기능이지만 Basic 무료 라이센스에서 사용 가능합니다.</p><p><img src="data-visualizer.png" alt=""></p><p>이제 seoul-metro-station-info.csv 파일을 드래그 &amp; 드롭 하거나 파일찾기를 이용해서 업로드를 하면 Data Visualizer 가 파일을 읽어들여 필드명과 매핑을 적절하게 설정 해 줍니다. 업로드를 한 다음에 import 버튼을 클릭합니다.</p><p><img src="data-visualizer-import.png" alt=""></p><p>우선은 기본 설정으로 색인을 한 뒤 필요한 부분은 <strong>_reindex API</strong> 를 이용해서 다시 하도록 하겠습니다. Data View 는 생성하지 않도록 <strong>create data view 는 체크를 해제</strong> 하고 색인할 인덱스 이름은 <strong>seoul-metro-station-info-temp</strong> 로 하겠습니다.<br>이제 import 버튼을 눌러 업로드 한 파일의 색인을 시작합니다.</p><p><img src="data-visualizer-import-2.png" alt=""></p><p>285 개의 서울시 역 정보 도큐먼트 색인이 모두 끝났습니다.</p><p><img src="data-visualizer-import-3.png" alt=""></p><p>Dev Tools 에서 데이터가 제대로 들어갔는지 확인 해 보겠습니다.</p><p><img src="seoul-metro-station-info-temp-search.png" alt=""></p><h4 id="seoul-metro-station-info-매핑-설정"><a href="#seoul-metro-station-info-매핑-설정" class="headerlink" title="seoul-metro-station-info 매핑 설정"></a>seoul-metro-station-info 매핑 설정</h4><p>데이터는 잘 들어갔지만 매핑을 손보아야 합니다. <strong>seoul-metro-station-info</strong> 인덱스를 만들면서 매핑을 아래와 같이 추가합니다.<br><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">PUT seoul-metro-station-info</span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">&quot;mappings&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;properties&quot;</span>: &#123;</span><br><span class="line">      <span class="string">&quot;geo&quot;</span>: &#123;</span><br><span class="line">        <span class="string">&quot;properties&quot;</span>: &#123;</span><br><span class="line">          <span class="string">&quot;addres_road&quot;</span>: &#123; <span class="string">&quot;type&quot;</span>: <span class="string">&quot;text&quot;</span> &#125;,</span><br><span class="line">          <span class="string">&quot;address_land&quot;</span>: &#123; <span class="string">&quot;type&quot;</span>: <span class="string">&quot;text&quot;</span> &#125;,</span><br><span class="line">          <span class="string">&quot;latitude&quot;</span>: &#123; <span class="string">&quot;type&quot;</span>: <span class="string">&quot;float&quot;</span> &#125;,</span><br><span class="line">          <span class="string">&quot;longitude&quot;</span>: &#123; <span class="string">&quot;type&quot;</span>: <span class="string">&quot;float&quot;</span> &#125;,</span><br><span class="line">          <span class="string">&quot;phone&quot;</span>: &#123; <span class="string">&quot;type&quot;</span>: <span class="string">&quot;text&quot;</span> &#125;,</span><br><span class="line">          <span class="string">&quot;sigungu_code&quot;</span>: &#123; <span class="string">&quot;type&quot;</span>: <span class="string">&quot;keyword&quot;</span> &#125;,</span><br><span class="line">          <span class="string">&quot;sigungu_name&quot;</span>: &#123; <span class="string">&quot;type&quot;</span>: <span class="string">&quot;keyword&quot;</span> &#125;,</span><br><span class="line">          <span class="string">&quot;location&quot;</span>: &#123; <span class="string">&quot;type&quot;</span>: <span class="string">&quot;geo_point&quot;</span> &#125;</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;,</span><br><span class="line">      <span class="string">&quot;line&quot;</span>: &#123;</span><br><span class="line">        <span class="string">&quot;properties&quot;</span>: &#123;</span><br><span class="line">          <span class="string">&quot;name&quot;</span>: &#123; <span class="string">&quot;type&quot;</span>: <span class="string">&quot;keyword&quot;</span> &#125;,</span><br><span class="line">          <span class="string">&quot;name_sub&quot;</span>: &#123; <span class="string">&quot;type&quot;</span>: <span class="string">&quot;keyword&quot;</span> &#125;,</span><br><span class="line">          <span class="string">&quot;num&quot;</span>: &#123; <span class="string">&quot;type&quot;</span>: <span class="string">&quot;byte&quot;</span> &#125;,</span><br><span class="line">          <span class="string">&quot;station_seq&quot;</span>: &#123; <span class="string">&quot;type&quot;</span>: <span class="string">&quot;byte&quot;</span> &#125;</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;,</span><br><span class="line">      <span class="string">&quot;station&quot;</span>: &#123;</span><br><span class="line">        <span class="string">&quot;properties&quot;</span>: &#123;</span><br><span class="line">          <span class="string">&quot;code&quot;</span>: &#123; <span class="string">&quot;type&quot;</span>: <span class="string">&quot;short&quot;</span> &#125;,</span><br><span class="line">          <span class="string">&quot;fr_code&quot;</span>: &#123; <span class="string">&quot;type&quot;</span>: <span class="string">&quot;keyword&quot;</span> &#125;,</span><br><span class="line">          <span class="string">&quot;name&quot;</span>: &#123; <span class="string">&quot;type&quot;</span>: <span class="string">&quot;keyword&quot;</span> &#125;,</span><br><span class="line">          <span class="string">&quot;name_chc&quot;</span>: &#123; <span class="string">&quot;type&quot;</span>: <span class="string">&quot;keyword&quot;</span> &#125;,</span><br><span class="line">          <span class="string">&quot;name_chn&quot;</span>: &#123; <span class="string">&quot;type&quot;</span>: <span class="string">&quot;keyword&quot;</span> &#125;,</span><br><span class="line">          <span class="string">&quot;name_en&quot;</span>: &#123; <span class="string">&quot;type&quot;</span>: <span class="string">&quot;text&quot;</span> &#125;,</span><br><span class="line">          <span class="string">&quot;name_full&quot;</span>: &#123; <span class="string">&quot;type&quot;</span>: <span class="string">&quot;keyword&quot;</span> &#125;,</span><br><span class="line">          <span class="string">&quot;name_jp&quot;</span>: &#123; <span class="string">&quot;type&quot;</span>: <span class="string">&quot;keyword&quot;</span> &#125;</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h4 id="필드-구조-정리를-위한-ingest-pipeline"><a href="#필드-구조-정리를-위한-ingest-pipeline" class="headerlink" title="필드 구조 정리를 위한 ingest pipeline"></a>필드 구조 정리를 위한 ingest pipeline</h4><p>루트에 있는 필드들을 geo, line, station 필드의 하위 필드로 적절하게 나누어 저장을 할 것입니다. <strong>seoul-metro-station-info-temp</strong> 인덱스의 도큐먼트들을 <strong>seoul-metro-station-info</strong> 로 재색인 할 때 사용할 ingest pipeline 을 다음과 같이 입력합니다.<br><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">PUT _ingest/pipeline/seoul-metro-station-pipe</span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">&quot;processors&quot;</span>: [</span><br><span class="line">    &#123; <span class="string">&quot;set&quot;</span>: &#123; <span class="string">&quot;field&quot;</span>: <span class="string">&quot;_id&quot;</span>, <span class="string">&quot;value&quot;</span>: <span class="string">&quot;&#123;&#123;station_code&#125;&#125;&quot;</span> &#125; &#125;,</span><br><span class="line">    &#123; <span class="string">&quot;set&quot;</span>: &#123; <span class="string">&quot;field&quot;</span>: <span class="string">&quot;geo.location.lon&quot;</span>, <span class="string">&quot;value&quot;</span>: <span class="string">&quot;&#123;&#123;geo_longitude&#125;&#125;&quot;</span> &#125; &#125;,</span><br><span class="line">    &#123; <span class="string">&quot;set&quot;</span>: &#123; <span class="string">&quot;field&quot;</span>: <span class="string">&quot;geo.location.lat&quot;</span>, <span class="string">&quot;value&quot;</span>: <span class="string">&quot;&#123;&#123;geo_latitude&#125;&#125;&quot;</span> &#125; &#125;,</span><br><span class="line">    &#123; <span class="string">&quot;convert&quot;</span>: &#123; <span class="string">&quot;field&quot;</span>: <span class="string">&quot;geo.location.lon&quot;</span>, <span class="string">&quot;type&quot;</span>: <span class="string">&quot;float&quot;</span> &#125; &#125;,</span><br><span class="line">    &#123; <span class="string">&quot;convert&quot;</span>: &#123; <span class="string">&quot;field&quot;</span>: <span class="string">&quot;geo.location.lat&quot;</span>, <span class="string">&quot;type&quot;</span>: <span class="string">&quot;float&quot;</span> &#125; &#125;,</span><br><span class="line">    &#123; <span class="string">&quot;split&quot;</span>: &#123; <span class="string">&quot;field&quot;</span>: <span class="string">&quot;station_name&quot;</span>, <span class="string">&quot;separator&quot;</span>: <span class="string">&quot;\\|&quot;</span> &#125; &#125;,</span><br><span class="line">    &#123; <span class="string">&quot;split&quot;</span>: &#123; <span class="string">&quot;field&quot;</span>: <span class="string">&quot;line_name_sub&quot;</span>, <span class="string">&quot;separator&quot;</span>: <span class="string">&quot;\\|&quot;</span> &#125; &#125;,</span><br><span class="line">    &#123;<span class="string">&quot;rename&quot;</span>: &#123; <span class="string">&quot;field&quot;</span>: <span class="string">&quot;geo_addres_road&quot;</span>, <span class="string">&quot;target_field&quot;</span>: <span class="string">&quot;geo.addres_road&quot;</span> &#125; &#125;,</span><br><span class="line">    &#123;<span class="string">&quot;rename&quot;</span>: &#123; <span class="string">&quot;field&quot;</span>: <span class="string">&quot;geo_address_land&quot;</span>, <span class="string">&quot;target_field&quot;</span>: <span class="string">&quot;geo.address_land&quot;</span> &#125; &#125;,</span><br><span class="line">    &#123;<span class="string">&quot;rename&quot;</span>: &#123; <span class="string">&quot;field&quot;</span>: <span class="string">&quot;geo_latitude&quot;</span>, <span class="string">&quot;target_field&quot;</span>: <span class="string">&quot;geo.latitude&quot;</span> &#125; &#125;,</span><br><span class="line">    &#123;<span class="string">&quot;rename&quot;</span>: &#123; <span class="string">&quot;field&quot;</span>: <span class="string">&quot;geo_longitude&quot;</span>, <span class="string">&quot;target_field&quot;</span>: <span class="string">&quot;geo.longitude&quot;</span> &#125; &#125;,</span><br><span class="line">    &#123;<span class="string">&quot;rename&quot;</span>: &#123; <span class="string">&quot;field&quot;</span>: <span class="string">&quot;geo_phone&quot;</span>, <span class="string">&quot;target_field&quot;</span>: <span class="string">&quot;geo.phone&quot;</span> &#125; &#125;,</span><br><span class="line">    &#123;<span class="string">&quot;rename&quot;</span>: &#123; <span class="string">&quot;field&quot;</span>: <span class="string">&quot;geo_sigungu_code&quot;</span>, <span class="string">&quot;target_field&quot;</span>: <span class="string">&quot;geo.sigungu_code&quot;</span> &#125; &#125;,</span><br><span class="line">    &#123;<span class="string">&quot;rename&quot;</span>: &#123; <span class="string">&quot;field&quot;</span>: <span class="string">&quot;geo_sigungu_name&quot;</span>, <span class="string">&quot;target_field&quot;</span>: <span class="string">&quot;geo.sigungu_name&quot;</span> &#125; &#125;,</span><br><span class="line">    &#123;<span class="string">&quot;rename&quot;</span>: &#123; <span class="string">&quot;field&quot;</span>: <span class="string">&quot;line_name&quot;</span>, <span class="string">&quot;target_field&quot;</span>: <span class="string">&quot;line.name&quot;</span> &#125; &#125;,</span><br><span class="line">    &#123;<span class="string">&quot;rename&quot;</span>: &#123; <span class="string">&quot;field&quot;</span>: <span class="string">&quot;line_name_sub&quot;</span>, <span class="string">&quot;target_field&quot;</span>: <span class="string">&quot;line.name_sub&quot;</span> &#125; &#125;,</span><br><span class="line">    &#123;<span class="string">&quot;rename&quot;</span>: &#123; <span class="string">&quot;field&quot;</span>: <span class="string">&quot;line_num&quot;</span>, <span class="string">&quot;target_field&quot;</span>: <span class="string">&quot;line.num&quot;</span> &#125; &#125;,</span><br><span class="line">    &#123;<span class="string">&quot;rename&quot;</span>: &#123; <span class="string">&quot;field&quot;</span>: <span class="string">&quot;line_station_seq&quot;</span>, <span class="string">&quot;target_field&quot;</span>: <span class="string">&quot;line.station_seq&quot;</span> &#125; &#125;,</span><br><span class="line">    &#123;<span class="string">&quot;rename&quot;</span>: &#123; <span class="string">&quot;field&quot;</span>: <span class="string">&quot;station_code&quot;</span>, <span class="string">&quot;target_field&quot;</span>: <span class="string">&quot;station.code&quot;</span> &#125; &#125;,</span><br><span class="line">    &#123;<span class="string">&quot;rename&quot;</span>: &#123; <span class="string">&quot;field&quot;</span>: <span class="string">&quot;station_fr_code&quot;</span>, <span class="string">&quot;target_field&quot;</span>: <span class="string">&quot;station.fr_code&quot;</span> &#125; &#125;,</span><br><span class="line">    &#123;<span class="string">&quot;rename&quot;</span>: &#123; <span class="string">&quot;field&quot;</span>: <span class="string">&quot;station_name&quot;</span>, <span class="string">&quot;target_field&quot;</span>: <span class="string">&quot;station.name&quot;</span> &#125; &#125;,</span><br><span class="line">    &#123;<span class="string">&quot;rename&quot;</span>: &#123; <span class="string">&quot;field&quot;</span>: <span class="string">&quot;station_name_chc&quot;</span>, <span class="string">&quot;target_field&quot;</span>: <span class="string">&quot;station.name_chc&quot;</span> &#125; &#125;,</span><br><span class="line">    &#123;<span class="string">&quot;rename&quot;</span>: &#123; <span class="string">&quot;field&quot;</span>: <span class="string">&quot;station_name_chn&quot;</span>, <span class="string">&quot;target_field&quot;</span>: <span class="string">&quot;station.name_chn&quot;</span> &#125; &#125;,</span><br><span class="line">    &#123;<span class="string">&quot;rename&quot;</span>: &#123; <span class="string">&quot;field&quot;</span>: <span class="string">&quot;station_name_en&quot;</span>, <span class="string">&quot;target_field&quot;</span>: <span class="string">&quot;station.name_en&quot;</span> &#125; &#125;,</span><br><span class="line">    &#123;<span class="string">&quot;rename&quot;</span>: &#123; <span class="string">&quot;field&quot;</span>: <span class="string">&quot;station_name_jp&quot;</span>, <span class="string">&quot;target_field&quot;</span>: <span class="string">&quot;station.name_jp&quot;</span> &#125; &#125;,</span><br><span class="line">    &#123;<span class="string">&quot;rename&quot;</span>: &#123; <span class="string">&quot;field&quot;</span>: <span class="string">&quot;station_name_full&quot;</span>, <span class="string">&quot;target_field&quot;</span>: <span class="string">&quot;station.name_full&quot;</span> &#125; &#125;</span><br><span class="line">  ]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>이제 <strong>seoul-metro-station-info-temp</strong> 인덱스의 도큐먼트들을 <strong>seoul-metro-station-info</strong> 로 재색인 합니다.<br><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">POST _reindex</span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">&quot;source&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;index&quot;</span>: <span class="string">&quot;seoul-metro-station-info-temp&quot;</span></span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="string">&quot;dest&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;index&quot;</span>: <span class="string">&quot;seoul-metro-station-info&quot;</span>,</span><br><span class="line">    <span class="string">&quot;pipeline&quot;</span>: <span class="string">&quot;seoul-metro-station-pipe&quot;</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>아래와 같이 285 개의 도큐먼트가 created 되었다는 메시지가 나오면 성공입니다.<br><img src="reindexed.png" alt=""></p><p>재색인 작업이 끝났으면 <strong>seoul-metro-station-info-temp</strong> 인덱스와 <strong>seoul-metro-station-pipe</strong> 인제스트 파이프라인은 삭제하셔도 됩니다.<br><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">DELETE seoul-metro-station-info-temp</span><br><span class="line">DELETE _ingest/pipeline/seoul-metro-station-pipe</span><br></pre></td></tr></table></figure></p><h2 id="데이터-확장을-위한-스크립트-작업"><a href="#데이터-확장을-위한-스크립트-작업" class="headerlink" title="데이터 확장을 위한 스크립트 작업"></a>데이터 확장을 위한 스크립트 작업</h2><p>승하차인원 집계 로그 데이터를 색인하면서 다양한 변환 작업을 해 줄 것이기 때문에 이를 위한 준비를 먼저 해보도록 하겠습니다. </p><h4 id="hour-and-week-스크립트"><a href="#hour-and-week-스크립트" class="headerlink" title="hour_and_week 스크립트"></a>hour_and_week 스크립트</h4><p>나중에 만들 대시보드에 아래와 같이 요일별, 시간대별 값을 보는 차트를 만들 것입니다.<br><img src="hours_weekdays.png" alt=""></p><p>그러기 위해서는 쿼리 시점에서 timestamp 필드값에서 요일과 시각 정보를 만드는 것 보다 미리 별도의 시각값과 요일값 필드를 만들어 각 도큐먼트에 넣어 두는 것이 성능이나 자원 활용 면에서 여러가지로 유용합니다. timestamp 필드로부터 요일과 시각 정보를 추출해서 저장하는 스크립트를 만들고 <code>_scripts</code> 에 <code>hour_and_week</code> 라는 이름으로 미리 저장을 하겠습니다.</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">PUT _scripts/hour_and_week</span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">&quot;script&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;lang&quot;</span>: <span class="string">&quot;painless&quot;</span>,</span><br><span class="line">    <span class="string">&quot;source&quot;</span>: <span class="string">&quot;&quot;</span><span class="string">&quot;def ts=ctx[params[&#x27;dateTimeField&#x27;]];</span></span><br><span class="line"><span class="string">def sdf=new SimpleDateFormat(&quot;</span>yyyy-MM-dd<span class="string">&#x27;T&#x27;</span>HH:mm:ss.SSS<span class="string">&quot;);</span></span><br><span class="line"><span class="string">def date=sdf.parse(ts);</span></span><br><span class="line"><span class="string">def cal=Calendar.getInstance();</span></span><br><span class="line"><span class="string">cal.setTime(date);</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">ctx[params[&#x27;hourOfDayField&#x27;]]=cal.get(Calendar.HOUR_OF_DAY);</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">def dowNum=cal.get(Calendar.DAY_OF_WEEK)-1;</span></span><br><span class="line"><span class="string">def dowEn=[&quot;</span>Sun<span class="string">&quot;,&quot;</span>Mon<span class="string">&quot;,&quot;</span>Tue<span class="string">&quot;,&quot;</span>Wed<span class="string">&quot;,&quot;</span>Thu<span class="string">&quot;,&quot;</span>Fri<span class="string">&quot;,&quot;</span>Sat<span class="string">&quot;][dowNum];</span></span><br><span class="line"><span class="string">def dowKr=[&quot;</span>일<span class="string">&quot;,&quot;</span>월<span class="string">&quot;,&quot;</span>화<span class="string">&quot;,&quot;</span>수<span class="string">&quot;,&quot;</span>목<span class="string">&quot;,&quot;</span>금<span class="string">&quot;,&quot;</span>토<span class="string">&quot;][dowNum];</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">ctx[params[&#x27;dayOfWeekField&#x27;]]=[&quot;</span>num<span class="string">&quot;:dowNum, &quot;</span>en<span class="string">&quot;:dowEn, &quot;</span>kr<span class="string">&quot;:dowKr];&quot;</span><span class="string">&quot;&quot;</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>이 스크립트에 사용되는 파라메터 3가지는 다음과 같습니다.</p><ul><li>dateTimeField : 시각과 요일 정보를 추출할 date 타입의 필드명</li><li>hourOfDayField : 시각 값을 저장할 필드명</li><li>dayOfWeekField : 날짜 값을 저장할 필드명</li></ul><p>이 스크립트를 테스트 하기 위해서 임시로 ingest pipeline 을 만들어봅니다. 이름은 temp_hourAndWeek 으로 했습니다.<br><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">PUT _ingest/pipeline/temp_hourAndWeek</span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">&quot;processors&quot;</span>: [</span><br><span class="line">    &#123;</span><br><span class="line">      <span class="string">&quot;script&quot;</span>: &#123;</span><br><span class="line">        <span class="string">&quot;id&quot;</span>: <span class="string">&quot;hour_and_week&quot;</span>,</span><br><span class="line">        <span class="string">&quot;params&quot;</span>: &#123;</span><br><span class="line">          <span class="string">&quot;dateTimeField&quot;</span>: <span class="string">&quot;timestamp&quot;</span>,</span><br><span class="line">          <span class="string">&quot;hourOfDayField&quot;</span>: <span class="string">&quot;hour_of_day&quot;</span>,</span><br><span class="line">          <span class="string">&quot;dayOfWeekField&quot;</span>: <span class="string">&quot;day_of_week&quot;</span></span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  ]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>timestamp 필드에 date 타입의 값이 있으면 hour_of_day 와 day_of_week 에 값을 넣도록 합니다. 이제 이 ingest pipeline 을 _simulate 를 이용해서 테스트 해 봅니다.<br><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">POST _ingest/pipeline/temp_hourAndWeek/_simulate</span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">&quot;docs&quot;</span>: [</span><br><span class="line">    &#123;</span><br><span class="line">      <span class="string">&quot;_source&quot;</span>: &#123;</span><br><span class="line">        <span class="string">&quot;timestamp&quot;</span>: <span class="string">&quot;2022-07-01T12:00:00.000+0900&quot;</span></span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  ]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>위와 같이 입력하면 아래와 같은 결과가 리턴됩니다.<br><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  <span class="string">&quot;docs&quot;</span> : [</span><br><span class="line">    &#123;</span><br><span class="line">      <span class="string">&quot;doc&quot;</span> : &#123;</span><br><span class="line">        <span class="string">&quot;_index&quot;</span> : <span class="string">&quot;_index&quot;</span>,</span><br><span class="line">        <span class="string">&quot;_id&quot;</span> : <span class="string">&quot;_id&quot;</span>,</span><br><span class="line">        <span class="string">&quot;_source&quot;</span> : &#123;</span><br><span class="line">          <span class="string">&quot;hour_of_day&quot;</span> : <span class="number">12</span>,</span><br><span class="line">          <span class="string">&quot;timestamp&quot;</span> : <span class="string">&quot;2022-07-01T12:00:00.000+0900&quot;</span>,</span><br><span class="line">          <span class="string">&quot;day_of_week&quot;</span> : &#123;</span><br><span class="line">            <span class="string">&quot;en&quot;</span> : <span class="string">&quot;Fri&quot;</span>,</span><br><span class="line">            <span class="string">&quot;num&quot;</span> : <span class="number">5</span>,</span><br><span class="line">            <span class="string">&quot;kr&quot;</span> : <span class="string">&quot;금&quot;</span></span><br><span class="line">          &#125;</span><br><span class="line">        &#125;,</span><br><span class="line">        <span class="string">&quot;_ingest&quot;</span> : &#123;</span><br><span class="line">          <span class="string">&quot;timestamp&quot;</span> : <span class="string">&quot;2022-07-02T05:25:30.495605395Z&quot;</span></span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  ]</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><br>hour_of_day 필드에 시각 값인 12, day_of_week 필드에 하위 필드로 해당 날짜의 요일인 금요일이 영어, 한글 그리고 순번으로 입력 된 것을 확인할 수 있습니다. 순번을 넣은 이유는 나중에 시각화를 하고 정렬할 때 필요합니다. 저 값이 없으면 요일을 가나다 또는 알파벳 순으로 밖에 나열을 못합니다. (예: 금목수월일토화)</p><h4 id="enrich-pipeline-process"><a href="#enrich-pipeline-process" class="headerlink" title="enrich pipeline process"></a>enrich pipeline process</h4><p>승하차인원 집계 로그 파일이 색인될 때 앞서 만든 <strong>seoul-metro-station-info</strong> 인덱스의 정보를 가져와 조인 할 수 있도록 enrich 프로세서를 포함하는 인제스트 파이프라인을 만들겠습니다. 자세한 사용 방법에 대한 설명은 공식 도큐먼트의 <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ingest-enriching-data.html">Enrich your data</a> 페이지를 참고하시기 바랍니다.</p><p>먼저 enrich policy 를 만들어야 합니다. seoul-metro-station-info 인덱스에서 <code>station.code</code> 필드와 일치하는 도큐먼트를 가져와 병합하는 <strong>seoul-metro-info_policy</strong> 를 만들고 활성(<strong>_execute</strong>) 해 줍니다.<br><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">PUT /_enrich/policy/seoul-metro-info_policy</span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">&quot;match&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;indices&quot;</span>: <span class="string">&quot;seoul-metro-station-info&quot;</span>,</span><br><span class="line">    <span class="string">&quot;match_field&quot;</span>: <span class="string">&quot;station.code&quot;</span>,</span><br><span class="line">    <span class="string">&quot;enrich_fields&quot;</span>: [ <span class="string">&quot;line&quot;</span>, <span class="string">&quot;station&quot;</span>, <span class="string">&quot;geo&quot;</span> ]</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">POST /_enrich/policy/seoul-metro-info_policy/_execute</span><br></pre></td></tr></table></figure></p><p>방금 만든 seoul-metro-info_policy 의 enrich 프로세서를 포함하는 인제스트 파이프라인을 만들고 문서를 테스트 해 봅니다.<br><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">PUT _ingest/pipeline/seoul-metro-logs-pipe</span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">&quot;processors&quot;</span>: [</span><br><span class="line">    &#123;</span><br><span class="line">      <span class="string">&quot;enrich&quot;</span>: &#123;</span><br><span class="line">        <span class="string">&quot;policy_name&quot;</span>: <span class="string">&quot;seoul-metro-info_policy&quot;</span>,</span><br><span class="line">        <span class="string">&quot;field&quot;</span>: <span class="string">&quot;station_code&quot;</span>,</span><br><span class="line">        <span class="string">&quot;target_field&quot;</span>: <span class="string">&quot;info&quot;</span></span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  ]</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">POST _ingest/pipeline/seoul-metro-logs-pipe/_simulate</span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">&quot;docs&quot;</span>: [</span><br><span class="line">    &#123;</span><br><span class="line">      <span class="string">&quot;_source&quot;</span>: &#123;</span><br><span class="line">        <span class="string">&quot;@timestamp&quot;</span>: <span class="string">&quot;2015-01-01T05:00:00.000+09:00&quot;</span>,</span><br><span class="line">        <span class="string">&quot;station_code&quot;</span>: <span class="number">150</span>,</span><br><span class="line">        <span class="string">&quot;people_in&quot;</span>: <span class="number">441</span>,</span><br><span class="line">        <span class="string">&quot;people_out&quot;</span>: <span class="number">392</span></span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  ]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>아래와 같이 데이터가 확장 되었으면 성공입니다.<br><img src="enriched-log.png" alt=""></p><h4 id="enrich-hour-and-week-date-파이프라인"><a href="#enrich-hour-and-week-date-파이프라인" class="headerlink" title="enrich, hour_and_week, date 파이프라인"></a>enrich, hour_and_week, date 파이프라인</h4><p>이제 앞에서 만든 enrich 프로세서와 hour_and_week 스크립트, 그리고 그 외 필요한 프로세서들을 포함하는 <strong>seoul-metro-logs-pipe</strong> 파이프라인을 만듭니다. 앞에 만든 파이프라인과 이름이 중복되어도 덧씌워지기 때문에 상관 없습니다.</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">PUT _ingest&#x2F;pipeline&#x2F;seoul-metro-logs-pipe</span><br><span class="line">&#123;</span><br><span class="line">  &quot;description&quot;: &quot;Ingest pipeline for seoul-metro-logs-%&#123;+YYYY&#125; index&quot;,</span><br><span class="line">  &quot;processors&quot;: [</span><br><span class="line">    &#123;</span><br><span class="line">      &quot;enrich&quot;: &#123;</span><br><span class="line">        &quot;policy_name&quot;: &quot;seoul-metro-info_policy&quot;,</span><br><span class="line">        &quot;field&quot;: &quot;station_code&quot;,</span><br><span class="line">        &quot;target_field&quot;: &quot;info&quot;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;,</span><br><span class="line">    &#123;</span><br><span class="line">      &quot;script&quot;: &#123;</span><br><span class="line">        &quot;id&quot;: &quot;hour_and_week&quot;,</span><br><span class="line">        &quot;params&quot;: &#123;</span><br><span class="line">          &quot;dateTimeField&quot;: &quot;timestamp&quot;,</span><br><span class="line">          &quot;hourOfDayField&quot;: &quot;hour_of_day&quot;,</span><br><span class="line">          &quot;dayOfWeekField&quot;: &quot;day_of_week&quot;</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;,</span><br><span class="line">    &#123; &quot;rename&quot;: &#123; &quot;field&quot;: &quot;info.geo.sigungu_name&quot;, &quot;target_field&quot;: &quot;geo.sigungu_name&quot; &#125; &#125;,</span><br><span class="line">    &#123; &quot;rename&quot;: &#123; &quot;field&quot;: &quot;info.geo.sigungu_code&quot;, &quot;target_field&quot;: &quot;geo.sigungu_code&quot; &#125; &#125;,</span><br><span class="line">    &#123; &quot;rename&quot;: &#123; &quot;field&quot;: &quot;info.geo.location&quot;, &quot;target_field&quot;: &quot;geo.location&quot; &#125; &#125;,</span><br><span class="line">    &#123; &quot;rename&quot;: &#123; &quot;field&quot;: &quot;info.station&quot;, &quot;target_field&quot;: &quot;station&quot; &#125; &#125;,</span><br><span class="line">    &#123; &quot;rename&quot;: &#123; &quot;field&quot;: &quot;info.line&quot;, &quot;target_field&quot;: &quot;line&quot; &#125; &#125;,</span><br><span class="line">    &#123; &quot;date&quot;: &#123; &quot;field&quot;: &quot;timestamp&quot;, &quot;formats&quot;: [ &quot;ISO8601&quot; ], &quot;timezone&quot; : &quot;Asia&#x2F;Seoul&quot; &#125; &#125;,</span><br><span class="line">    &#123; &quot;remove&quot;: &#123; &quot;field&quot;: [ &quot;info&quot;, &quot;station_code&quot;, &quot;timestamp&quot; ] &#125; &#125;</span><br><span class="line">  ]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h6 id="seoul-metro-logs-pipe-파이프라인은-다음과-같은-작업들을-합니다"><a href="#seoul-metro-logs-pipe-파이프라인은-다음과-같은-작업들을-합니다" class="headerlink" title="seoul-metro-logs-pipe 파이프라인은 다음과 같은 작업들을 합니다."></a>seoul-metro-logs-pipe 파이프라인은 다음과 같은 작업들을 합니다.</h6><ul><li><code>enrich</code> : seoul-metro-info_policy 인덱스에서 가져온 정보들을 info 필드의 하위에 넣습니다.</li><li><code>script</code> : @timestamp 필드로부터 hour_of_day, day_of_week 정보를 추출해서 입력합니다.</li><li><code>rename</code> : enrich 에서 가져온 info.geo, info.line, info.station 필드들을 바깥으로 옮깁니다.</li><li><code>remove</code> : 필요 없어진 info 필드와 중복 값을 가진 station_code 를 삭제합니다.</li></ul><p>이제 다시 도큐먼트를 넣고 테스트를 해 봅니다.</p><p><img src="pipe-simul.png" alt=""></p><h2 id="seoul-metro-YYYY-logs-csv-색인"><a href="#seoul-metro-YYYY-logs-csv-색인" class="headerlink" title="seoul-metro-%{+YYYY}.logs.csv 색인"></a>seoul-metro-%{+YYYY}.logs.csv 색인</h2><h4 id="seoul-metro-station-info-매핑-설정-1"><a href="#seoul-metro-station-info-매핑-설정-1" class="headerlink" title="seoul-metro-station-info 매핑 설정"></a>seoul-metro-station-info 매핑 설정</h4><p>이제 <strong>seoul-metro-logs*</strong> 형식을 가진 인덱스가 색인될 때 자동으로 매핑을 적용할 인덱스 템플릿을 만들겠습니다.</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">PUT _index_template/seoul-metro-logs_template</span><br><span class="line">&#123;</span><br><span class="line">  <span class="string">&quot;index_patterns&quot;</span>: [ <span class="string">&quot;seoul-metro-logs*&quot;</span> ],</span><br><span class="line">  <span class="string">&quot;template&quot;</span>: &#123;</span><br><span class="line">    <span class="string">&quot;mappings&quot;</span>: &#123;</span><br><span class="line">      <span class="string">&quot;properties&quot;</span>: &#123;</span><br><span class="line">        <span class="string">&quot;@timestamp&quot;</span>: &#123; <span class="string">&quot;type&quot;</span>: <span class="string">&quot;date&quot;</span> &#125;,</span><br><span class="line">        <span class="string">&quot;year&quot;</span>: &#123; <span class="string">&quot;type&quot;</span>: <span class="string">&quot;integer&quot;</span> &#125;,</span><br><span class="line">        <span class="string">&quot;people_in&quot;</span>: &#123; <span class="string">&quot;type&quot;</span>: <span class="string">&quot;integer&quot;</span> &#125;,</span><br><span class="line">        <span class="string">&quot;people_out&quot;</span>: &#123; <span class="string">&quot;type&quot;</span>: <span class="string">&quot;integer&quot;</span> &#125;,</span><br><span class="line">        <span class="string">&quot;hour_of_day&quot;</span>: &#123; <span class="string">&quot;type&quot;</span>: <span class="string">&quot;byte&quot;</span> &#125;,</span><br><span class="line">        <span class="string">&quot;day_of_week&quot;</span>: &#123;</span><br><span class="line">          <span class="string">&quot;properties&quot;</span>: &#123;</span><br><span class="line">            <span class="string">&quot;en&quot;</span>: &#123; <span class="string">&quot;type&quot;</span>: <span class="string">&quot;keyword&quot;</span> &#125;,</span><br><span class="line">            <span class="string">&quot;kr&quot;</span>: &#123; <span class="string">&quot;type&quot;</span>: <span class="string">&quot;keyword&quot;</span> &#125;,</span><br><span class="line">            <span class="string">&quot;num&quot;</span>: &#123; <span class="string">&quot;type&quot;</span>: <span class="string">&quot;byte&quot;</span> &#125;</span><br><span class="line">          &#125;</span><br><span class="line">        &#125;,</span><br><span class="line">        <span class="string">&quot;geo&quot;</span>: &#123;</span><br><span class="line">          <span class="string">&quot;properties&quot;</span>: &#123;</span><br><span class="line">            <span class="string">&quot;sigungu_code&quot;</span>: &#123; <span class="string">&quot;type&quot;</span>: <span class="string">&quot;keyword&quot;</span> &#125;,</span><br><span class="line">            <span class="string">&quot;sigungu_name&quot;</span>: &#123; <span class="string">&quot;type&quot;</span>: <span class="string">&quot;keyword&quot;</span> &#125;,</span><br><span class="line">            <span class="string">&quot;location&quot;</span>: &#123; <span class="string">&quot;type&quot;</span>: <span class="string">&quot;geo_point&quot;</span> &#125;</span><br><span class="line">          &#125;</span><br><span class="line">        &#125;,</span><br><span class="line">        <span class="string">&quot;line&quot;</span>: &#123;</span><br><span class="line">          <span class="string">&quot;properties&quot;</span>: &#123;</span><br><span class="line">            <span class="string">&quot;name&quot;</span>: &#123; <span class="string">&quot;type&quot;</span>: <span class="string">&quot;keyword&quot;</span> &#125;,</span><br><span class="line">            <span class="string">&quot;name_sub&quot;</span>: &#123; <span class="string">&quot;type&quot;</span>: <span class="string">&quot;keyword&quot;</span> &#125;,</span><br><span class="line">            <span class="string">&quot;num&quot;</span>: &#123; <span class="string">&quot;type&quot;</span>: <span class="string">&quot;byte&quot;</span> &#125;,</span><br><span class="line">            <span class="string">&quot;station_seq&quot;</span>: &#123; <span class="string">&quot;type&quot;</span>: <span class="string">&quot;byte&quot;</span> &#125;</span><br><span class="line">          &#125;</span><br><span class="line">        &#125;,</span><br><span class="line">        <span class="string">&quot;station&quot;</span>: &#123;</span><br><span class="line">          <span class="string">&quot;properties&quot;</span>: &#123;</span><br><span class="line">            <span class="string">&quot;code&quot;</span>: &#123; <span class="string">&quot;type&quot;</span>: <span class="string">&quot;short&quot;</span> &#125;,</span><br><span class="line">            <span class="string">&quot;fr_code&quot;</span>: &#123; <span class="string">&quot;type&quot;</span>: <span class="string">&quot;keyword&quot;</span> &#125;,</span><br><span class="line">            <span class="string">&quot;name&quot;</span>: &#123; <span class="string">&quot;type&quot;</span>: <span class="string">&quot;keyword&quot;</span> &#125;,</span><br><span class="line">            <span class="string">&quot;name_chc&quot;</span>: &#123; <span class="string">&quot;type&quot;</span>: <span class="string">&quot;keyword&quot;</span> &#125;,</span><br><span class="line">            <span class="string">&quot;name_chn&quot;</span>: &#123; <span class="string">&quot;type&quot;</span>: <span class="string">&quot;keyword&quot;</span> &#125;,</span><br><span class="line">            <span class="string">&quot;name_en&quot;</span>: &#123; <span class="string">&quot;type&quot;</span>: <span class="string">&quot;keyword&quot;</span> &#125;,</span><br><span class="line">            <span class="string">&quot;name_full&quot;</span>: &#123;</span><br><span class="line">              <span class="string">&quot;type&quot;</span>: <span class="string">&quot;keyword&quot;</span>,</span><br><span class="line">              <span class="string">&quot;fields&quot;</span>: &#123;</span><br><span class="line">                <span class="string">&quot;nori&quot;</span>: &#123; <span class="string">&quot;type&quot;</span>: <span class="string">&quot;text&quot;</span>, <span class="string">&quot;analyzer&quot;</span>: <span class="string">&quot;nori&quot;</span> &#125;</span><br><span class="line">              &#125;</span><br><span class="line">            &#125;,</span><br><span class="line">            <span class="string">&quot;name_jp&quot;</span>: &#123; <span class="string">&quot;type&quot;</span>: <span class="string">&quot;keyword&quot;</span> &#125;</span><br><span class="line">          &#125;</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="logstash-필터-설정"><a href="#logstash-필터-설정" class="headerlink" title="logstash 필터 설정"></a>logstash 필터 설정</h4><p>실시간 로그 수집은 Filebeats 또는 Elatic Agent 등이 잘 되어 있지만 간단한 커스텀 데이터를 색인하는 데에는 Logstash 가 아직까지는 수월합니다. Logstash 로 파일에서 데이터를 수집해서 csv 형식을 파싱하고 elasticsearch 로 내보내도록 다음과 같이 config 설정 파일을 만들어주도록 하겠습니다.</p><p>처음에는 output 을 <code>stdout &#123; &#125;</code> 으로 테스트 하면서 데이터가 제대로 확장되었는지 확인하고 나중에 <code>elasticsearch &#123; &#125;</code> 로 변경하는 것이 좋습니다.</p><figure class="highlight ruby"><table><tr><td class="code"><pre><span class="line">input &#123;</span><br><span class="line">  file &#123;</span><br><span class="line">    path =&gt; <span class="string">&quot;/Users/kimjmin/elastic/source/seoul-metro/seoul-metro-*.logs.csv&quot;</span></span><br><span class="line">    start_position =&gt; <span class="string">&quot;beginning&quot;</span></span><br><span class="line">    sincedb_path =&gt; <span class="string">&quot;/dev/null&quot;</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">filter &#123;</span><br><span class="line">  <span class="comment"># csv 파싱</span></span><br><span class="line">  csv &#123;</span><br><span class="line">    source =&gt; <span class="string">&quot;message&quot;</span></span><br><span class="line">    skip_header =&gt; <span class="literal">true</span></span><br><span class="line">    columns =&gt; [ <span class="string">&quot;timestamp&quot;</span>, <span class="string">&quot;station_code&quot;</span>, <span class="string">&quot;people_in&quot;</span>, <span class="string">&quot;people_out&quot;</span> ]</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment"># timestamp 필드로부터 year 값 추출.</span></span><br><span class="line">  mutate &#123;</span><br><span class="line">    copy =&gt; &#123; <span class="string">&quot;timestamp&quot;</span> =&gt; <span class="string">&quot;year&quot;</span> &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  mutate &#123;</span><br><span class="line">    split =&gt; &#123; <span class="string">&quot;year&quot;</span> =&gt; <span class="string">&quot;-&quot;</span> &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  mutate &#123;</span><br><span class="line">    replace =&gt; &#123; <span class="string">&quot;year&quot;</span> =&gt; <span class="string">&quot;%&#123;[year][0]&#125;&quot;</span> &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment"># 숫자 필드 타입 변경</span></span><br><span class="line">  mutate &#123;</span><br><span class="line">    convert =&gt; &#123;</span><br><span class="line">      <span class="string">&quot;station_code&quot;</span> =&gt; <span class="string">&quot;integer&quot;</span></span><br><span class="line">      <span class="string">&quot;people_in&quot;</span> =&gt; <span class="string">&quot;integer&quot;</span></span><br><span class="line">      <span class="string">&quot;people_out&quot;</span> =&gt; <span class="string">&quot;integer&quot;</span></span><br><span class="line">      <span class="string">&quot;year&quot;</span> =&gt; <span class="string">&quot;integer&quot;</span></span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment"># 사용하지 않는 필드 삭제</span></span><br><span class="line">  mutate &#123;</span><br><span class="line">    remove_field =&gt; [<span class="string">&quot;@version&quot;</span>, <span class="string">&quot;event&quot;</span>, <span class="string">&quot;log&quot;</span>, <span class="string">&quot;host&quot;</span>, <span class="string">&quot;message&quot;</span>]</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">output &#123;</span><br><span class="line">  <span class="comment"># stdout &#123; &#125;</span></span><br><span class="line">  elasticsearch &#123;</span><br><span class="line">    cloud_id =&gt; <span class="string">&quot;seoul-metro-test:YXNpYS1ub3J0aGVhc3QzLmdj...&quot;</span></span><br><span class="line">    cloud_auth =&gt; <span class="string">&quot;ingest:password&quot;</span></span><br><span class="line">    index =&gt; <span class="string">&quot;seoul-metro-logs-%&#123;[year]&#125;&quot;</span></span><br><span class="line">    pipeline =&gt; <span class="string">&quot;seoul-metro-logs-pipe&quot;</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>전체 파이프라인을 거치면서 데이터는 다음과 같이 변경됩니다.</p><p><img src="data-pipelined.png" alt=""></p><p>이제 Logstash 를 실행하고 데이터가 정상적으로 색인되었는지 확인합니다.</p><p><img src="seoul-metro-logs-search.png" alt=""></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;blockquote&gt;
&lt;p&gt;서울시 지하철 승하차인원 대시보드 v3 - 1. 데이터 색인&lt;br&gt;&lt;a href=&quot;/2022/07/2022-07-seoul-metro-v3-2&quot;&gt;서울시 지하철 승하차인원 대시보드 v3 - 2. 대시보드 생성&lt;/a&gt;&lt;/p&gt;

      
    
    </summary>
    
      <category term="Engineering" scheme="http://kimjmin.net/categories/Engineering/"/>
    
    
      <category term="Elastic" scheme="http://kimjmin.net/tags/Elastic/"/>
    
      <category term="Engineering" scheme="http://kimjmin.net/tags/Engineering/"/>
    
      <category term="Kibana" scheme="http://kimjmin.net/tags/Kibana/"/>
    
      <category term="Seoul Metro" scheme="http://kimjmin.net/tags/Seoul-Metro/"/>
    
      <category term="Dashboard" scheme="http://kimjmin.net/tags/Dashboard/"/>
    
  </entry>
  
  <entry>
    <title>Elastic Cloud 에서 Cross Cluster 설치하기</title>
    <link href="http://kimjmin.net/2022/06/2022-06-elastic-cross-cluster/"/>
    <id>http://kimjmin.net/2022/06/2022-06-elastic-cross-cluster/</id>
    <published>2022-06-21T15:00:00.000Z</published>
    <updated>2022-12-21T02:05:44.537Z</updated>
    
    <content type="html"><![CDATA[<p>Elasticsearch 의 가장 큰 시스템 단위는 클러스터 입니다. 기본적으로 클러스터가 다르면 서로 데이터 간섭이 일어나지 않으며 클러스터 마다 (정확히는 노드 마다) 서로 다른 접근 엔드포인트를 가지고 있습니다.</p><p>서비스에서 여러개의 서로 다른 클러스터를 동시에 운영하고 있거나, DR(Disaster Recovery) 구성, 멀티 리전 운영 등을 위해 한 클러스터에서 다른 클러스터의 데이터를 접속해야 하는 경우가 종종 있는데 Elastic 에서는 여러 클러스터의 데이터를 동시에 검색할 수 있는 <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-cross-cluster-search.html">Cross Cluster Search</a> 과 서로 다른 클러스터의 데이터를 동기화 시키는 <a href="https://www.elastic.co/guide/en/elasticsearch/reference/8.2/xpack-ccr.html">Cross Cluster Replication</a> 기능들을 지원하고 있습니다.</p><p>이 포스트에서는 Elastic Cloud 서비스 (<a href="https://cloud.elastic.co">https://cloud.elastic.co</a>) 에서 Cross Cluster 기능들을 사용하는 방법에 대해 설명 드리려고 합니다.</p><h2 id="같은-Elastic-Cloud-계정"><a href="#같은-Elastic-Cloud-계정" class="headerlink" title="같은 Elastic Cloud 계정"></a>같은 Elastic Cloud 계정</h2><p>Elastic Cloud 서비스에는 Cross Cluster 기능을 사용하기 위해 Trust 라는 것을 설정해야 합니다. Elastic Cloud 관리 화면의 아무 클러스터 메뉴에 가서 Features 아래에 Trust 메뉴로 가면 <strong>Trust all my deployments</strong> 체크가 있습니다. 여기에 체크가 되어 있으면 같은 Elastic Cloud 계정 (Organization) 끼리는 기본적으로 Cross Cluster 기능을 허용하게 됩니다.</p><p><img src="trust-all-my-depoly.png" alt=""></p><h2 id="서로-다른-Elastic-Cloud-계정"><a href="#서로-다른-Elastic-Cloud-계정" class="headerlink" title="서로 다른 Elastic Cloud 계정"></a>서로 다른 Elastic Cloud 계정</h2><p>서로 다른 Elastic Cloud 계정 간의 클러스터들 끼리 Cross Cluster 기능을 활성화 하기 위해서는 각 클러스터에 대해 Trust 정보를 추가해야 합니다. <strong>이 작업은 2개 클러스터 모두 진행해야 합니다.</strong></p><p>먼저 Elastic Cloud 관리 페이지에서 Cross Cluster 를 사용할 클러스터의 설정 화면 중 <strong>Security</strong> 메뉴로 이동합니다. 화면의 <strong>Trusted deployments</strong> 섹션에 있는 <strong>Create trus</strong> 버튼 클릭합니다.</p><p><img src="create-trust.png" alt=""></p><p>이제 Trust 를 추가 할 Elastic Cloud Orgazniation(계정) 의 ID와 연결 해 줄 클러스터(Deployment) ID 를 입력해야 합니다. 이 정보들은 연결 할 클러스터 관리 화면에서 확인할 수 있습니다.</p><p><img src="add-org-id.png" alt=""></p><p>연결할 클러스터의 우측 상단 아이콘의 드롭다운 메뉴에서 Organization 화면으로 이동하면 Organization ID 를 확인할 수 있습니다. 이 ID 를 복사해서 이전 클러스터 관리 화면의 Add organization ID 란에 넣어줍니다.</p><p><img src="org-id.png" alt=""></p><p><strong>All deployments</strong> 를 체크하면 연결한 organization 의 모든 Cluster 와 Cross Cluster 설정이 가능합니다. 특정 Cluster 만 허용하고 싶으면 <strong>Specific deployments</strong> 를 선택하고 Deployment ID 를 넣어줍니다. Deployment ID 는 연결할 클러스터 관리 화면으로 들어간 뒤 <strong>URL 에 나타나는 32자의 문자열</strong> 입니다. 이 문자열을 복사해서 붙여넣어줍니다.</p><p><img src="dep-id-url.png" alt=""></p><p><strong>이 과정을 서로에 대해 각각 연결할 클러스터 모두 진행 해 줍니다</strong></p><h2 id="Remote-Cluster-설정"><a href="#Remote-Cluster-설정" class="headerlink" title="Remote Cluster 설정"></a>Remote Cluster 설정</h2><p>이제 Organization 간의 Trust 작업을 마쳤으면 Elasticsearch 클러스터애 Remote Cluster 정보를 등록해야 합니다. Kibana 의 Stack Management 메뉴의 Remote Cluster 메뉴로 이동한 뒤 <strong>Add a remote cluster</strong> 버튼을 클릯합니다.</p><p><img src="remote-cluster.png" alt=""></p><p>Name 은 원격 클러스터의 닉네임으로 사용할 이름을 적어줍니다.<br>그리고 <strong>Manually enter proxy…</strong> 를 체크하여 <strong>Proxy address</strong> 와 <strong>Server Name</strong> 을 입력해야 합니다.</p><p><img src="add-mycrawl.png" alt=""></p><p>이 정보들은 연결할 Elastic Cloud 의 관리 화면 중 앞에서 보았던 Security 메뉴의 맨 아래 Remote cluster parameters 섹션에 있습니다. 값이 화면에 표시되지는 않으며 Prox address 와 Server name 링크를 클릭하면 각각 값들이 클립보드로 복사되어 위 Kibana 의 Add Remote Cluster 화면에 붙여넣기 하면 됩니다.</p><p><img src="remote-cluster-param.png" alt=""></p><p>이제 원격 클러스터에 접속할 준비는 다 끝났습니다. 앞에서 만든 원격 클러스터 이름 뒤에 콜론 <code>:</code> 을 붙이고 인덱스를 넣어 원격 클러스터에 있는 데이터를 사용할 수 있습니다. 다음은 mycrawl 이라고 명명한 원격 클러스터의 googleplayreview 인덱스를 검색한 결과입니다.<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">GET mycrawl:googleplayreview&#x2F;_search</span><br></pre></td></tr></table></figure></p><p><img src="remote-search.png" alt=""></p><p>다음과 같이 멀티테넌시로 원격 클러스터와 로컬 클러스터의 인덱스들을 한꺼번에 검색하는것도 가능합니다.<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">GET remote:my-index,my-index&#x2F;_search</span><br></pre></td></tr></table></figure></p><p>기본적으로는 같은 메이저 버전의 클러스터들 끼리 원격 클러스터로 설정이 가능하며, 이전 버전의 경우 가장 마지막 마이너 버전의 클러스터와의 연결이 지원됩니다.</p><p><img src="ccr-suppert-matrix.png" alt=""></p><p>따라서 예를 들어 현재 8.2 버전 클러스터에서 7.11 버전 클러스터를 연결하고 싶으면 먼저 7.11 버전을 7.x 의 마지막 마이너 버전인 7.17 로 업데그레이드를 해야 합니다.</p><h2 id="맺음말"><a href="#맺음말" class="headerlink" title="맺음말"></a>맺음말</h2><p>Cross Cluster 기능은 하나의 서비스에서 여러 클러스터를 운영하는데 같이 검색이나 분석을 하거나 DR 구성등을 하는데 유용하게 쓸 수 있습니다. 그리고 지금 또 한가지 유용한 팁은, 현재 운영중인 클러스터를 업그레이드를 해야 할 때 운영중 업그레이드가 부담스럽다면 기존 서비스는 그대로 두고 최신 클러스터를 새로 만들어서 새로운 데이터는 새 클러스터로 색인하고 기존 데이터도 새 클러스터에서 예전 클러스터 데이터를 같이 조회하도록 하면 좀 더 안전하고 쉽게 최신 버전을 사용할 수 있습니다. </p><p>메이저 버전이 다르다면 이전 클러스터는 메이저의 마지막 버전으로는 업그레이드 해야 합니다. Elastic Cloud 에서는 원클릭으로 바로 가능합니다.</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;Elasticsearch 의 가장 큰 시스템 단위는 클러스터 입니다. 기본적으로 클러스터가 다르면 서로 데이터 간섭이 일어나지 않으며 클러스터 마다 (정확히는 노드 마다) 서로 다른 접근 엔드포인트를 가지고 있습니다.&lt;/p&gt;
&lt;p&gt;서비스에서 여러
      
    
    </summary>
    
      <category term="Engineering" scheme="http://kimjmin.net/categories/Engineering/"/>
    
    
      <category term="Elastic" scheme="http://kimjmin.net/tags/Elastic/"/>
    
      <category term="Engineering" scheme="http://kimjmin.net/tags/Engineering/"/>
    
      <category term="Cross Cluster Search" scheme="http://kimjmin.net/tags/Cross-Cluster-Search/"/>
    
      <category term="Cross Cluster Replication" scheme="http://kimjmin.net/tags/Cross-Cluster-Replication/"/>
    
      <category term="CCR" scheme="http://kimjmin.net/tags/CCR/"/>
    
      <category term="DR" scheme="http://kimjmin.net/tags/DR/"/>
    
  </entry>
  
  <entry>
    <title>Elastic 8.0 설치하기</title>
    <link href="http://kimjmin.net/2022/02/2022-02-elastic-8-install/"/>
    <id>http://kimjmin.net/2022/02/2022-02-elastic-8-install/</id>
    <published>2022-02-15T15:00:00.000Z</published>
    <updated>2022-12-21T02:05:44.537Z</updated>
    
    <content type="html"><![CDATA[<p>정말 오랬만에 블로그 포스팅을 하네요. 얼마 전에 드디어 Elastic 8.0 이 출시되었습니다. 6.x 릴리스 까지는 보통 1년 ~ 18개월 정도의 텀을 두고 비교적 빠르게 버전 업데이트를 했는데 7.x 릴리스는 2019년에 출시되고 나서 거의 3년 가까이 유지를 해 왔네요.</p><p>사실 7.0 에서 7.17 버전으로 오기 까지 마이너 업데이트를 하면서 변경된 기능들이 정말 많았습니다. <a href="/2019/04/2019-04-elastic-stack-7-release">7.0 버전 출시 때 작성했던 포스트</a>에서도 이야기했지만, 보통 Elastic 의 기능 추가는 <font color='blue'><strong>마이너 릴리스</strong>에서 기능들이 추가</font> 되고 <font color='red'><strong>메이져 릴리스</strong>는 데이터 저장방식 등의 구조 변경에 초점을 맞추기 때문에 <strong>오히려 기능들이 Deprecated 되거나 Expire</strong></font> 되는 경우가 많습니다.</p><p>이번 8.0 릴리스의 가장 큰 테마는 <strong>NLP (자연어 처리)</strong> 와 <strong>Vector Search</strong> 입니다. 저도 이 분야에는 알못이라서 차근 차근 공부 해 가면서 계속 공유 해 보도록 하겠습니다. 자세한 내용은 <a href="https://www.elastic.co/blog/whats-new-elastic-8-0-0">Elastic 8.0 출시 블로그</a>를 참고하세요. 아직은 영문 버전만 올라와 있는데 곧 한국어로 번역되어 올라올 예정입니다.</p><p>Elastic 처음 설치 과정에도 몇가지 변경된 내용이 있습니다. 오늘 포스트에서는 Elasticsearch 와 Kibana 의 처음 설치 과정이 어떻게 변경되었는지에 대해 간단하게 한번 살펴보겠습니다.</p><p>현재는 (그리고 아마 앞으로도 계속) Elastic에서 하는 거의 모든 홍보, 영업이 대부분 Elastic Cloud 를 중심으로 이루어지고 있습니다. Elastic 공식 홈페이지(<a href="https://www.elastic.co">https://www.elastic.co</a>) 에서도 대부분의 링크들이 클라우드 서비스 페이지(<a href="https://cloud.elastic.co">https://cloud.elastic.co</a>) 로 연결되도록 유도하고 있고, 가입된 Elastic Cloud 계정을 로그인 정보로 이용해서 공식 교육 페이지라던가 내부 행사 등에 사용합니다.</p><p>하지만 여전히 온프렘으로 설치, 사용하시는 분들이 많이 계신걸로 알고, 오늘은 간단히 로컬에서 설치하는 방법 까지만 살펴보고, 클러스터 구성이나 추가적인 설정들은 이후 다른 포스트에서 계속 다루도록 하겠습니다. 설치 파일 다운로드를 위해서는 Elastic 공식 홈페이지(<a href="https://www.elastic.co">https://www.elastic.co</a>) 에서 상단에 product 메뉴로 이동해서 Elasticsearch + Kibana 부분을 클릭합니다.</p><p><img src="elastic-co-prod.png" alt=""></p><p>그리고 그 아래 Or Download … 링크를 클릭합니다.</p><p><img src="elastic-co-download.png" alt=""></p><p>그러면 시작 튜토리얼 페이지 (<a href="https://www.elastic.co/start">https://www.elastic.co/start</a>) 로 이동하게 됩니다. 이제 여기서 안내하는대로 쭉 따라서 하시면 되는데, 먼저 elasticsearch 를 설치할 OS의 버전을 선택하고 아래 버튼을 눌러 elasticsearch 설치 파일을 다운로드 합니다.</p><p><img src="elastic-co-start.png" alt=""></p><p>그 다음은 <code>tar</code> 또는 <code>zip</code> 명령을 이용해서 압축을 풀고 시작 튜토리얼 페이지에 나온 대로 명령들을 실행하시면서 설치하면 됩니다. 각 명령 옆에 있는 스니펫 카피 버튼을 누르면 클립보드에 명령이 복사되어 바로 붙여넣기를 하실 수 있습니다.</p><p><img src="elastic-co-start-sniffet.png" alt=""></p><p><code>bin/elasticsearch</code> 명령어로 실행을 하고 난 뒤, 터미널을 잘 보고 계시면 이전 버전까지는 보이지 않았던 다음과 같은 메시지가 하나 나타탑니다.</p><p><img src="es-cli-1.png" alt=""></p><p>OSS 버전(7.11 부터 OSS 는 제공하지 않고 현재 최소 기능 버전은 Basic 입니다) 에서는 제공하지 않았고 Basic 에서도 선택적으로 활성화 가능했던 Security 기능이 이제 필수로 활성화가 됩니다. 슈퍼 관리자(superuser) 계정인 <code>elastic</code> 의 패스워드가 자동으로 생성 되고 그 외에 다른 노드, 클라이언트 및 에이젼트 연동에 필요한 암호화 키 정보들이 표시가 됩니다.</p><p>이제 Kibana를 실행시켜 봅니다. Kibana도 처음 실행하면 보안 설정을 마저 마무리 하라는 메시지가 터미널에 나타납니다.</p><p><img src="kb-cli-1.png" alt=""></p><p>웹브라우저를 열고 kibana 화면에 들어가면 (디폴트 포트 :5601) <code>enrollment token</code> 을 입력하라는 화면이 보입니다.</p><p><img src="kb-enroll-1.png" alt=""></p><p>여기 아까 elasticsearch 실행 터미널에 있던 enrollment 토큰을 복사해서</p><p><img src="es-cli-2.png" alt=""></p><p>Kibana 화면의 enrollment 입력란에 붙여넣기 해 줍니다. 그리고 <code>Configure Elastic</code> 버튼을 클릭합니다.</p><p><img src="kb-enroll-2.png" alt=""></p><p>그럼 또다시 Kibana verification code 를 입력하라는 화면이 나옵니다. 요즘은 6자리 숫자 인증이 공통 갬성인것 같습니다.</p><p><img src="kb-num-verify-1.png" alt=""></p><p>이 숫자는 kibana 실행 터미널에서 볼 수 있습니다.</p><p><img src="kb-cli-verify-num.png" alt=""></p><p>터미널에서 확인된 숫자를 kibana 화면에 입력 해 줍니다.</p><p><img src="kb-num-verify-2.png" alt=""></p><p>그럼 이제 kibana 가 나머지 설정들을 진행합니다.</p><p><img src="kb-processing.png" alt=""></p><p>Kibana 홈 디렉토리 아래애 <code>config/kibana.yml</code> 파일을 살펴보면 elasticsearch 와 연동에 필요한 암화 정보 들이 자동으로 추가되어 있는 것도 확인할 수 있습니다.</p><p><img src="kibana-yml.png" alt=""></p><p>잠시 기다리면 이제 kibana 화면에 로그인 화면이 나타납니다. elasticsearch 터미널에 표시되었던 슈퍼 관리자 elastic 의 패스워드를 복사해서</p><p><img src="es-cli-pw.png" alt=""></p><p>사용자 elastic, 패스워드는 붙여넣고 로그인 합니다.</p><p><img src="kb-login-pw.png" alt=""></p><p>이제 예전처럼 elasticsearch 와 kibana 를 사용할 수 있습니다. 화면 디자인은 7.17 버전에서 크게 변한 것은 없습니다.</p><p><img src="kb-dashboard.png" alt=""></p><p>메이져 릴리즈가 나왔는데 이전 버전과 화면이 동일한 것은 살짝 아쉽기는 합니다. 새 버전 느낌이 나도록 배경 색상 정도라도 바꿔주면 좋지 않았을까 하는 생각이 있지만, 7.x 에서도 마이너 버전이 업데이트 될 때 마다 바뀐게 많았기 때문에 8.x 도 앞으로 계속 마이너 릴리스가 나올 때마다 발전될것이라 기대 해 봅니다.</p><p>오늘 포스팅은 이것으로 마치고 다음에는 또 다른 8.x 내용에 대해 다루어 보도록 하겠습니다. 🤓</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;정말 오랬만에 블로그 포스팅을 하네요. 얼마 전에 드디어 Elastic 8.0 이 출시되었습니다. 6.x 릴리스 까지는 보통 1년 ~ 18개월 정도의 텀을 두고 비교적 빠르게 버전 업데이트를 했는데 7.x 릴리스는 2019년에 출시되고 나서 거의
      
    
    </summary>
    
      <category term="Engineering" scheme="http://kimjmin.net/categories/Engineering/"/>
    
    
      <category term="Elasticsearch" scheme="http://kimjmin.net/tags/Elasticsearch/"/>
    
      <category term="Elastic" scheme="http://kimjmin.net/tags/Elastic/"/>
    
      <category term="Engineering" scheme="http://kimjmin.net/tags/Engineering/"/>
    
      <category term="Kibana" scheme="http://kimjmin.net/tags/Kibana/"/>
    
      <category term="Release" scheme="http://kimjmin.net/tags/Release/"/>
    
  </entry>
  
  <entry>
    <title>오픈소스 비즈니스에 대한 단상</title>
    <link href="http://kimjmin.net/2021/06/2021-06-opensource-business/"/>
    <id>http://kimjmin.net/2021/06/2021-06-opensource-business/</id>
    <published>2021-06-09T15:00:00.000Z</published>
    <updated>2022-12-21T02:05:44.536Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>먼저 이 글은 제 개인의 생각일 뿐 제가 속한 조직이나 제가 관리자로 있는 커뮤니티의 입장을 대변하진 않습니다. 이 내용들이 어떤 논쟁의 근거로 쓰이는 일은 없기를 바랍니다.</p></blockquote><p>어제 페이스북에 친구 공개로 끄적여 본 내용입니다.<br><img src="fb-post-about-oss.png" alt="페이스북 포스트에 적은 근황"><br>기왕 이야기 꺼낸거 생각을 한번 정리 하고자 오랬만에 블로그에 글을 씁니다.</p><p>작년에 한번 <a href="/2020/06/2020-06-elastic-devrel">Elastic 라이센스, 그리고 오픈 소스 에반젤리스트의 딜레마</a> 라는 제목으로 비슷한 주제의 글을 쓴 적이 있었습니다. 그 때도 <strong>Elastic의 가장 큰 경쟁 제품은 A사도, S사도, D사도 아닌 Elastic 오픈 소스 입니다.</strong> 라는 이야기를 했었는데, 이제 Elastic 제품들의 라이센스가 <a href="https://www.elastic.co/kr/pricing/faq/licensing">Apache 에서 SSPL 로 변경이 되어</a> 더 이상 <strong>오픈소스</strong> 라고 부르면 안되긴 하지만 다른 오픈소스 기업들이 다 같이 겪고 있는 고민이라고 생각 되어서 글 제목에는 오픈소스라는 단어를 달았습니다.</p><blockquote><p>한동안 커뮤니티에서 여러번 설명을 드렸지만, Elasticsearch 와 Kibana 를 더 이상 <strong>오픈소스</strong> 라고 부르지 못하는 이유는 <strong>Open Sorce</strong> 라는 단어에 대한 사용권(?)은 <a href="https://opensource.org">Open Source Initiative</a> 에서 승인한 라이센스들 에서만 가능하기 때문입니다. Elastic 이 이번에 변경한 <a href="https://www.elastic.co/kr/blog/elastic-license-v2">SSPL</a> 역시 다른 오픈소스들과 마찬가지로</p></blockquote><blockquote><ul><li><strong>무료로 사용이 가능하고</strong></li><li><strong>소스 코드가 공개되어 있고</strong></li><li><strong>사용자는 제품에 코드 기여가 가능</strong></li></ul></blockquote><blockquote><p>하지만, OSI 에서 승인하고 있는 종류의 라이센스가 아니라서 오픈소스라고 부르면 안됩니다. 기존 오픈소스와 뚜렷하게 다른 점은</p></blockquote><blockquote><ul><li><strong>13항 - 클라우드에서 제품 서비스로 제공하는 경우 기능에 대한 소스를 공개해야 한다</strong></li></ul></blockquote><blockquote><p>는 점 입니다. <a href="https://www.mongodb.com/licensing/server-side-public-license">SSPL 은 MongoDB 에서 처음 발표 한 라이센스</a> 인데, 과거 오픈소스 라이센스들이 제정 되던 시기에는 소프트웨어는 다운로드 받아 설치하는 것 이었지만, 지금은 소프트웨어를 클라우드에서 구매해서 사용하는 것으로 환경이 바뀌었기 때문에 과거에 제정된 라이센스에는 맞지 않는 조항들을 개선한 것이라고 볼 수 있습니다. <a href="https://webassets.mongodb.com/_com_assets/legal/SSPL-compared-to-AGPL.pdf">AGPL 과 SSPL 비교</a></p></blockquote><p>서론이 길었네요. 이제 Elastic이 한국에서 했던 비즈니스 활동에 대해서 이야기를 좀 해 보겠습니다. 제가 속한 Elastic 의 이야기지만 많은 오픈소스 회사들도 비슷하리라 생각합니다.</p><h2 id="Elastic-의-비즈니스-패턴"><a href="#Elastic-의-비즈니스-패턴" class="headerlink" title="Elastic 의 비즈니스 패턴"></a>Elastic 의 비즈니스 패턴</h2><p>지금까지 국내에서 제가 보아 온 Elstic 의 비즈니스 활동은 몇가지 패턴이 있었습니다.</p><ul><li><a href="#SI-신규-프로젝트">SI 신규 프로젝트</a></li><li><a href="#시스템-고도화">시스템 고도화</a></li><li><a href="#무료-오픈-기능을-사용하다가-유료로-전환">무료(오픈) 기능을 사용하다가 유료로 전환</a></li></ul><h3 id="SI-신규-프로젝트"><a href="#SI-신규-프로젝트" class="headerlink" title="SI 신규 프로젝트"></a>SI 신규 프로젝트</h3><p>우리 나라는 아직도 SI(Service Integration) 라고 하는 프로젝트 발주 형식의 비즈니스가 많습니다. 그리고 이 SI 라고 하는 큰 과정 안에서 <strong>개발자 인건비, 장비 구매비, SW 구매비</strong> 등과 같은 비용들이 산정이 됩니다.</p><p>Elastic 의 주 비즈니스는 년간 구독 방식의 기술지원 서비스 입니다. 당연히 일반적으로 SI 에서 이런 비용을 산정하는 경우는 잘 없습니다. 있다고 해도 보통 1~2주의 컨설팅이나 교육 구매 정도입니다. 따라서 Elastic은 SI 프로젝트에 참여가 가능한 파트너사에게 상용 기능과 기술지원 서비스를 제공하는 방식으로 비즈니스를 모색합니다. Elastic 유료 라이센스와 기술지원 비용이 결코 저렴하지 않기 때문에 가격 면에서 경쟁 하기가 사실 쉽지는 않습니다.</p><p>이 때 Elastic과의 협업 없이 오픈된 무료 기능만 가지고 기능을 개발 해 주겠다는 경쟁자가 나타납니다. 당연히 가격은 공식 파트너사의 제품 보다 훨씬 저렴하겠고요.</p><p>Elastic 은 (제 생각에)다른 오픈소스 DB 보다는 비교적 쉬운 편이지만, 그래도 잘 설치하고 제대로 활용하기 위해서는 만만치 않은 기술과 노하우가 필요합니다. 프로젝트 진행 과정에서 Elastic 의 공식 파트너라면 이런 문제들이 나타날 때 마다 Elastic 기술지원 서비스를 받을 수 있지만, 나중에 나타난 경쟁업체에는 Elastic을 아무리 잘 하는 엔지니어가 있어도 모든 문제에 대처가 어려울 수 있습니다. 설치 중 오류 문제, 성능 문제 등등 프로젝트에 난항을 겪다 보면 결국에는 고객의 입에서</p><ul><li>오픈소스는 너무 어려워. 이래서 쓰면 안돼.</li></ul><p>라는 말이 나와 버리게 됩니다. 😩</p><h3 id="시스템-고도화"><a href="#시스템-고도화" class="headerlink" title="시스템 고도화"></a>시스템 고도화</h3><p>Oracle 같은 RDB 나 Hadoop 같은 시스템을 유지하다가 새로운 기능의 요구조건을 충족시키기 위한 솔루션으로 Elastic 을 선택해서 진행하는 경우입니다. 위의 SI 신규 프로젝트와 마찬가지로 파트너와 진행하는 경우도 많고, 똑같은 문제도 종종 발생하지만, 이 경우에는 <strong>희망</strong>을 주는 경우가 있습니다.</p><p>Elastic 을 경험했고 좋아하는 엔지니어(보통 이런 분들을 <strong>챔피언</strong> 이라고 합니다)가 있는 경우입니다.</p><p>이 경우 보통 초반 미팅은 Elastic 에서 어렵게 설명하지 않아도 고객사의 챔피언이 직접 경험한 Elastic 의 장점을 이야기하고 설득 해 주는 경우도 많습니다. 이렇게 처음 이야기는 매끄럽게 잘 진행됩니다.</p><p>이렇게 프로젝트 수주 - 진행 - 납품까지 잘 이어지는 아름다운 그림이 나오면 좋겠는데 다음과 같은 경우가 생깁니다.</p><ol><li><p>고객사에서 비용적인 이슈가 발생해서 프로젝트 비용을 줄여야 하는 경우가 생깁니다. 유료 솔루션이나 장비의 구매 가격을 갑자기 내리는 것은 불가능합니다. 이 때 만만하게 비용을 줄일 수 있는 것이 없을까 하다가 눈이 가는 것이 Elastic 의 무료(오픈소스) 버전입니다.<br><strong><em>“죄송하지만 저희 기술지원 안 받고 무료 버전 가지고 저희가 한번 해 보겠습니다”</em></strong><br>라는 답변이 저희에게 돌아옵니다.</p></li><li><p>구매 결정권자 또는 상부 라인에서 갑자기 챔피언을 호출합니다.<br><strong><em>“이거 오픈소스라던데 왜 돈주고 사는거에요? 이러다 비용 감사 들어오면 뭐라고 할거에요? 블라 블라…”</em></strong><br>그리고 자기 혼났다고 챔피언에게서 연락이 옵니다. 🥺</p></li></ol><p>역시 Elastic 의 비즈니스 경쟁 상대는 Elastic 무료버전 입니다.</p><h3 id="무료-오픈-기능을-사용하다가-유료로-전환"><a href="#무료-오픈-기능을-사용하다가-유료로-전환" class="headerlink" title="무료(오픈) 기능을 사용하다가 유료로 전환"></a>무료(오픈) 기능을 사용하다가 유료로 전환</h3><p>가장 아름답고 이상적인 그림의 비즈니스입니다. Elastic 을 오랬동안 잘 써 왔고, 커뮤니티에도 열심히 참여하였으며, Elastic 의 다양한 기능과 철학들을 잘 이해하고 있는 고객이 새로운 시스템 또는 서비스로 도약하기 위해 연락을 하는 경우입니다.</p><p>“처음에는 검색 때문에, 로깅 때문에 Elastic을 쓰기 시작했는데 사용하다 보니 이런 저런 유용한 기능들이 많았어요. 그 동안에는 필요해서 서로 다른 솔루션들을 쓰고 있었는데 Elastic 으로 기능들을 통합하면 앞으로 좀 더 다양한 분석도 가능해지고, 새로운 비즈니스 전략도 발굴할 수 있을것 같아요. 이 과정에서 Elastic 의 도움을 받고 싶습니다.”</p><p><img src="jjangu.jpg" alt="그저 빛"></p><p>정말 감동적입니다. </p><p>사실 Elastic 을 처음 써 보는 고객의 경우에는 기초적인 질문 부터 시작해서, Elastic 에서 다 알아서 해줬으면 하는 생각으로 접근하는 경우가 많아 컨설팅이나 기술지원 팀에서도 응대 하는 것이 쉽지 않는 경우가 많습니다. 하지만 이렇게 이미 경험이 있는 고객의 경우에는 기술지원팀 에게 질문을 할 때도 어떤 부분이 문제인지를 대부분 잘 알고 질문을 하기 때문에 기술지원팀 입장에서도 협업하기가 매우 수월합니다.</p><h2 id="Elastic-의-비즈니스-철학"><a href="#Elastic-의-비즈니스-철학" class="headerlink" title="Elastic 의 비즈니스 철학"></a>Elastic 의 비즈니스 철학</h2><p>지금은 수많은 오픈소스 제품들이 있지만 오픈소스로 비즈니스를 잘 성공시킨 기업은 많지 않습니다. 얼핏 제 머릿속에도 RedHat과 Elastic 정도 밖에 떠오르지가 않네요. Elastic 은 코드가 오픈된 제품이고 무료 기능과 유료 상용 기능이 있지만, Elastic 비즈니스의 핵심은 기술지원입니다. 그리고 제가 자주 하는 이야기이지만, Elastic의 기술지원의 철학은 단순히 고객의 질문에 답변만 하는 것이 아니라, 고객과 함께 문제 해결의 과정을 동행하는 것에 있습니다.</p><p>Elastic 이 오픈소스 비즈니스로 성장하는 여정에서 언급을 하지 않을 수 없는 고객이 <a href="https://www.elastic.co/kr/videos/open-source-technology-elasticsearch-at-goldman-sachs">골드만삭스</a> 입니다. 공식 릴리즈도 아닌 초기 0.9x 버전, 유료 기능은 전혀 없던 시절 부터 회사의 모든 시스템을 Elastic 으로 통합하고 Elastic 과 기술 지원 협약을 맺었습니다. 현재 골드만삭스의 증권 거래, 분석 시스템은 물론 채용시스템의 이력서 검색 기능이라던가 내부 회계 결재 같이 모든 기능 구석구석에서 Elastic 이 쓰이고 있습니다. 골드만 삭스에는 현재도 수백명의 Elastic 전문 엔지니어들이 수천개의 노드의 클러스터를 운영하고 있습니다.</p><p>골드만 삭스 엔지니어의 운영 노하우는 다시 Elastic 엔지니어링 팀에게 피드백 되어 Elastic 의 공식 기능으로 개발 된 경우도 많이 있습니다. 대표적으로 현재 대용량 클러스터에서 자주 사용되는 <a href="https://www.elastic.co/kr/blog/implementing-hot-warm-cold-in-elasticsearch-with-index-lifecycle-management">Hot-Warm-Cold 아키텍쳐</a> 입니다. 수천개의 노드를 운영하던 골드만 삭스 엔지니어가 제안하여 Elastic 엔지니어들이 이런 구조의 클러스터 아키텍트를 구성할 수 있는 config 옵션을 추가하고, 릴리즈 하여 Elastic의 모든 사용자들이 사용할 수 있게 되었습니다.</p><p>이처럼 골드만 삭스 라는 좋은 선례를 경험했기 때문에 Elastic 창시자들은 Elastic 이 모든 고객들과 골드만 삭스 같은 관계가 되기를 희망합니다.</p><h2 id="기술지원-엔지니어를-모십니다"><a href="#기술지원-엔지니어를-모십니다" class="headerlink" title="기술지원 엔지니어를 모십니다"></a>기술지원 엔지니어를 모십니다</h2><p>Elastic 에는 현재 대한민국에 5분 - 해외에 계신 한국어가 가능하신 분 까지 합하면 7분의 기술지원 엔지니어가 있습니다. 한국의 비즈니스 규모를 생각하면 굉장히 많은데, 한국 고객만 다루지 않고 해외 고객들의 문의도 두루두루 담당하고 계시기 때문에 그렇습니다. Elastic 기술지원 엔지니어는 국가가 아니라 리전(아시아) 단위로 채용하는데 한국 엔지니어분들이 뛰어나신 분들이 많아 많이 계신거라고 봅니다.</p><p><a href="https://jobs.elastic.co/jobs/support/distributed-apj/support-engineer-korean-speaking/2426932">그리고 한국에서 기술지원 엔지니어는 계속 채용중입니다.</a> 한국의 기술지원 엔지니어인 조인석님 께서 쓰신 <a href="https://brunch.co.kr/@insuk/54">글로벌 오픈소스 기술 지원 여정에 동참하시겠습니까?</a>도 한번 읽어보세요.</p><p>Elastic 과 함께 해 주셔서 늘 감사드립니다. 앞으로도 계속 잘 부탁드립니다. 😘</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;blockquote&gt;
&lt;p&gt;먼저 이 글은 제 개인의 생각일 뿐 제가 속한 조직이나 제가 관리자로 있는 커뮤니티의 입장을 대변하진 않습니다. 이 내용들이 어떤 논쟁의 근거로 쓰이는 일은 없기를 바랍니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;어제 페이
      
    
    </summary>
    
      <category term="Engineering" scheme="http://kimjmin.net/categories/Engineering/"/>
    
    
      <category term="Elastic" scheme="http://kimjmin.net/tags/Elastic/"/>
    
      <category term="Engineering" scheme="http://kimjmin.net/tags/Engineering/"/>
    
      <category term="SW Engineer" scheme="http://kimjmin.net/tags/SW-Engineer/"/>
    
      <category term="오픈소스" scheme="http://kimjmin.net/tags/%EC%98%A4%ED%94%88%EC%86%8C%EC%8A%A4/"/>
    
      <category term="커뮤니티" scheme="http://kimjmin.net/tags/%EC%BB%A4%EB%AE%A4%EB%8B%88%ED%8B%B0/"/>
    
      <category term="DevRel" scheme="http://kimjmin.net/tags/DevRel/"/>
    
  </entry>
  
  <entry>
    <title>Elastic의 새로운 고급 필드들</title>
    <link href="http://kimjmin.net/2021/04/2021-04-advanced-doc-fields/"/>
    <id>http://kimjmin.net/2021/04/2021-04-advanced-doc-fields/</id>
    <published>2021-04-13T15:00:00.000Z</published>
    <updated>2022-12-21T02:05:44.536Z</updated>
    
    <content type="html"><![CDATA[<iframe width="560" height="315" src="https://www.youtube.com/embed/9_MkRTXH0QU" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe><p>Elastic은 버전업이 될 때 마다 나오는 새로운 기능들이 많습니다. 워낙 기본 기능도 다양하기 때문에 기본 기능 활용의 학습에만도 많은 시간을 들이게 되는데, 이번 포스트에서는 비교적 최근에 출시된 새로운 필드들에 대해 한번 살펴보고 활용 방법을 알아보도록 하겠습니다. 다음은 이 포스트에서 살펴볼 필드들입니다.</p><ul><li><a href="#unsigned-long">unsigned_long</a></li><li><a href="#version">version</a></li><li><a href="#flattened">flattened</a></li><li><a href="#aggregate-metric-double">aggregate_metric_double</a></li><li><a href="#dense-vector">dense_vector</a></li></ul><h3 id="unsigned-long"><a href="#unsigned-long" class="headerlink" title="unsigned_long"></a><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/unsigned-long.html">unsigned_long</a></h3><p>이름 그대로 양/음 부호를 없앤 long 정수 타입입니다. Java의 long 타입인 64비트로 표현 가능한 최대 정수값인 <code>0 ~ 18446744073709551615</code> 까지 숫자의 저장이 가능합니다. 1천8백경이 넘네요. 입력은 아래처럼 심플합니다.<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">PUT my_index</span><br><span class="line">&#123;</span><br><span class="line">  &quot;mappings&quot;: &#123;</span><br><span class="line">    &quot;properties&quot;: &#123;</span><br><span class="line">      &quot;my_counter&quot;: &#123;</span><br><span class="line">        &quot;type&quot;: &quot;unsigned_long&quot;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h3 id="version"><a href="#version" class="headerlink" title="version"></a><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/version.html">version</a></h3><p>elasticsearch 에 시스템 메트릭 같은 정보들을 저장하게 되면 버전 정보값을 저장해야 할 일이 있습니다. 보통 버전 정보는 7.12.2 처럼 3군데 이상으로 구분되어있는 경우가 많아 소수점이 있는 숫자로 저장하기 힘들고 보통은 문자열로 저장을 많이 합니다. 그러다 보니 버전 간의 대/소를 비교하거나 정렬하는 것이 쉽지가 않았습니다. <code>version</code> 타입을 사용하면 이런 버전 형식의 값의 비교 또는 정렬이 가능합니다.<br>다음과 같이 version 타입의 필드인 my_version을 갖는 version-test 인덱스를 선언하고 값들을 넣습니다.<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">PUT version-test</span><br><span class="line">&#123;</span><br><span class="line">  &quot;mappings&quot;: &#123;</span><br><span class="line">    &quot;properties&quot;: &#123;</span><br><span class="line">      &quot;my_version&quot;: &#123;</span><br><span class="line">        &quot;type&quot;: &quot;version&quot;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">POST version-test&#x2F;_bulk</span><br><span class="line">&#123; &quot;index&quot; : &#123;&#125;&#125;</span><br><span class="line">&#123;&quot;my_version&quot;: &quot;1.5.0&quot;&#125;</span><br><span class="line">&#123; &quot;index&quot; : &#123;&#125;&#125;</span><br><span class="line">&#123;&quot;my_version&quot;: &quot;1.2.5&quot;&#125;</span><br><span class="line">&#123; &quot;index&quot; : &#123;&#125;&#125;</span><br><span class="line">&#123;&quot;my_version&quot;: &quot;2.1.7&quot;&#125;</span><br><span class="line">&#123; &quot;index&quot; : &#123;&#125;&#125;</span><br><span class="line">&#123;&quot;my_version&quot;: &quot;1.5.3-beta&quot;&#125;</span><br><span class="line">&#123; &quot;index&quot; : &#123;&#125;&#125;</span><br><span class="line">&#123;&quot;my_version&quot;: &quot;1.5.3-alpha2&quot;&#125;</span><br></pre></td></tr></table></figure><br>이제 1.3.9 초과 1.5.3-beta 미만의 값으로 range 쿼리를 해 보면<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">GET version-test&#x2F;_search</span><br><span class="line">&#123;</span><br><span class="line">  &quot;query&quot;: &#123;</span><br><span class="line">    &quot;range&quot;: &#123;</span><br><span class="line">      &quot;my_version&quot;: &#123;</span><br><span class="line">        &quot;gt&quot;: &quot;1.3.9&quot;,</span><br><span class="line">        &quot;lt&quot;: &quot;1.5.3-beta&quot;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>다음과 같이 결과를 확인할 수 있습니다.<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">...</span><br><span class="line">  &quot;hits&quot; : &#123;</span><br><span class="line">    &quot;total&quot; : &#123;</span><br><span class="line">      &quot;value&quot; : 2,</span><br><span class="line">      &quot;relation&quot; : &quot;eq&quot;</span><br><span class="line">    &#125;,</span><br><span class="line">    &quot;max_score&quot; : 1.0,</span><br><span class="line">    &quot;hits&quot; : [</span><br><span class="line">      &#123;</span><br><span class="line">        &quot;_index&quot; : &quot;version-test&quot;,</span><br><span class="line">        &quot;_type&quot; : &quot;_doc&quot;,</span><br><span class="line">        &quot;_id&quot; : &quot;wlujzngBoohoz6ycrpvX&quot;,</span><br><span class="line">        &quot;_score&quot; : 1.0,</span><br><span class="line">        &quot;_source&quot; : &#123;</span><br><span class="line">          &quot;my_version&quot; : &quot;1.5.0&quot;</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;,</span><br><span class="line">      &#123;</span><br><span class="line">        &quot;_index&quot; : &quot;version-test&quot;,</span><br><span class="line">        &quot;_type&quot; : &quot;_doc&quot;,</span><br><span class="line">        &quot;_id&quot; : &quot;xlujzngBoohoz6ycrpvX&quot;,</span><br><span class="line">        &quot;_score&quot; : 1.0,</span><br><span class="line">        &quot;_source&quot; : &#123;</span><br><span class="line">          &quot;my_version&quot; : &quot;1.5.3-alpha2&quot;</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    ]</span><br><span class="line">  &#125;</span><br><span class="line">...</span><br></pre></td></tr></table></figure><br>다음과 같이 my_version 필드의 오름차순으로 정렬을 해 보면<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">GET version-test&#x2F;_search</span><br><span class="line">&#123;</span><br><span class="line">  &quot;sort&quot;: [</span><br><span class="line">    &#123;</span><br><span class="line">      &quot;my_version&quot;: &#123;</span><br><span class="line">        &quot;order&quot;: &quot;desc&quot;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  ]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>다음처럼 정렬이 됩니다.<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">...</span><br><span class="line">&#123; &quot;my_version&quot; : &quot;1.2.5&quot; &#125;</span><br><span class="line">...</span><br><span class="line">&#123; &quot;my_version&quot; : &quot;1.5.0&quot; &#125;</span><br><span class="line">...</span><br><span class="line">&#123; &quot;my_version&quot; : &quot;1.5.3-alpha2&quot; &#125;</span><br><span class="line">...</span><br><span class="line">&#123; &quot;my_version&quot; : &quot;1.5.3-beta&quot; &#125;</span><br><span class="line">...</span><br><span class="line">&#123; &quot;my_version&quot; : &quot;2.1.7&quot; &#125;</span><br><span class="line">...</span><br></pre></td></tr></table></figure></p><h3 id="flattened"><a href="#flattened" class="headerlink" title="flattened"></a><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/flattened.html">flattened</a></h3><p>매핑에 하위 필드를 가지는 필드를 설계할 때는 보통 ojbect 또는 경우에 따라 nested 타입으로 설계를 합니다. 하지만 하위 필드로 어떤 타입의 필드가 오게 될지를 모르고, 데이터를 생성하는 개발자들이 자유롭게 형식을 만들 수 있도록 하고 싶은 경우 flattened 타입으로 선언이 가능합니다.<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">PUT flatten-label</span><br><span class="line">&#123;</span><br><span class="line">  &quot;mappings&quot;: &#123;</span><br><span class="line">    &quot;properties&quot;: &#123;</span><br><span class="line">      &quot;labels&quot;: &#123;</span><br><span class="line">        &quot;type&quot;: &quot;flattened&quot;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>다음과 같이 labels 필드 안에 하위 필드로 다양한 값들을 넣어보겠습니다.<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">PUT flatten-label&#x2F;_doc&#x2F;1</span><br><span class="line">&#123;</span><br><span class="line">  &quot;labels&quot;: &#123;</span><br><span class="line">    &quot;size&quot;: &quot;XL&quot;,</span><br><span class="line">    &quot;color&quot;: &quot;red&quot;,</span><br><span class="line">    &quot;price&quot;: 9000,</span><br><span class="line">    &quot;manufactured_date&quot;: &quot;2020-10-21&quot;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>입력한 값은 모두 정상적으로 들어가며, 다시 flatten-label 인덱스의 매핑을 확인 해 보아도 하위 필드는 자동으로 생성되지 않습니다.<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">### 요청 ###</span><br><span class="line">GET flatten-label&#x2F;_mapping</span><br><span class="line"></span><br><span class="line">### 결과 ###</span><br><span class="line">&#123;</span><br><span class="line">  &quot;flatten-label&quot; : &#123;</span><br><span class="line">    &quot;mappings&quot; : &#123;</span><br><span class="line">      &quot;properties&quot; : &#123;</span><br><span class="line">        &quot;labels&quot; : &#123;</span><br><span class="line">          &quot;type&quot; : &quot;flattened&quot;</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>입력된 하위 필드의 값은 모두 정상적으로 검색이 됩니다. 2개의 문서를 추가로 넣고 검색을 해 봅니다.<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">PUT flatten-label&#x2F;_doc&#x2F;2</span><br><span class="line">&#123;</span><br><span class="line">  &quot;labels&quot;: &#123;</span><br><span class="line">    &quot;color&quot;: &quot;green&quot;,</span><br><span class="line">    &quot;price&quot;: 11000</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line">PUT flatten-label&#x2F;_doc&#x2F;3</span><br><span class="line">&#123;</span><br><span class="line">  &quot;labels&quot;: &#123;</span><br><span class="line">    &quot;color&quot;: &quot;blue&quot;,</span><br><span class="line">    &quot;price&quot;: 13000</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">### 쿼리 ###</span><br><span class="line">GET flatten-label&#x2F;_search</span><br><span class="line">&#123;</span><br><span class="line">  &quot;query&quot;: &#123;</span><br><span class="line">    &quot;match&quot;: &#123;</span><br><span class="line">      &quot;labels.color&quot;: &quot;red&quot;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">### 결과 ###</span><br><span class="line">  &quot;hits&quot; : &#123;</span><br><span class="line">    &quot;total&quot; : &#123;</span><br><span class="line">      &quot;value&quot; : 1,</span><br><span class="line">      &quot;relation&quot; : &quot;eq&quot;</span><br><span class="line">    &#125;,</span><br><span class="line">    &quot;max_score&quot; : 0.9530773,</span><br><span class="line">    &quot;hits&quot; : [</span><br><span class="line">      &#123;</span><br><span class="line">        &quot;_index&quot; : &quot;flatten-label&quot;,</span><br><span class="line">        &quot;_type&quot; : &quot;_doc&quot;,</span><br><span class="line">        &quot;_id&quot; : &quot;1&quot;,</span><br><span class="line">        &quot;_score&quot; : 0.9530773,</span><br><span class="line">        &quot;_source&quot; : &#123;</span><br><span class="line">          &quot;labels&quot; : &#123;</span><br><span class="line">            &quot;size&quot; : &quot;XL&quot;,</span><br><span class="line">            &quot;color&quot; : &quot;red&quot;,</span><br><span class="line">            &quot;price&quot; : 9000,</span><br><span class="line">            &quot;manufactured_date&quot; : &quot;2020-10-21&quot;</span><br><span class="line">          &#125;</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    ]</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure><br>한가지 주의 할 점은, 하위 필드는 입력 형태가 어떻든 모두 <code>keyword</code> 형태로 저장이 됩니다. 따라서 range 같은 쿼리는 할 수가 없습니다. price 필드를 내림차순으로 정렬 해 보면 다음처럼 9000 이 12000 보다 먼저 나오게 됩니다.<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">### 쿼리 ###</span><br><span class="line">GET flatten-label&#x2F;_search</span><br><span class="line">&#123;</span><br><span class="line">  &quot;sort&quot;: [</span><br><span class="line">    &#123;</span><br><span class="line">      &quot;labels.price&quot;: &#123;</span><br><span class="line">        &quot;order&quot;: &quot;desc&quot;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  ]</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">### 결과 ###</span><br><span class="line">    &quot;hits&quot; : [</span><br><span class="line">      &#123;</span><br><span class="line">        &quot;_index&quot; : &quot;flatten-label&quot;,</span><br><span class="line">        &quot;_type&quot; : &quot;_doc&quot;,</span><br><span class="line">        &quot;_id&quot; : &quot;1&quot;,</span><br><span class="line">        &quot;_score&quot; : null,</span><br><span class="line">        &quot;_source&quot; : &#123;</span><br><span class="line">          &quot;labels&quot; : &#123;</span><br><span class="line">            &quot;size&quot; : &quot;XL&quot;,</span><br><span class="line">            &quot;color&quot; : &quot;red&quot;,</span><br><span class="line">            &quot;price&quot; : 9000,</span><br><span class="line">            &quot;manufactured_date&quot; : &quot;2020-10-21&quot;</span><br><span class="line">          &#125;</span><br><span class="line">        &#125;,</span><br><span class="line">        &quot;sort&quot; : [</span><br><span class="line">          &quot;9000&quot;</span><br><span class="line">        ]</span><br><span class="line">      &#125;,</span><br><span class="line">      &#123;</span><br><span class="line">        &quot;_index&quot; : &quot;flatten-label&quot;,</span><br><span class="line">        &quot;_type&quot; : &quot;_doc&quot;,</span><br><span class="line">        &quot;_id&quot; : &quot;3&quot;,</span><br><span class="line">        &quot;_score&quot; : null,</span><br><span class="line">        &quot;_source&quot; : &#123;</span><br><span class="line">          &quot;labels&quot; : &#123;</span><br><span class="line">            &quot;color&quot; : &quot;blue&quot;,</span><br><span class="line">            &quot;price&quot; : 13000</span><br><span class="line">          &#125;</span><br><span class="line">        &#125;,</span><br><span class="line">        &quot;sort&quot; : [</span><br><span class="line">          &quot;13000&quot;</span><br><span class="line">        ]</span><br><span class="line">      &#125;,</span><br><span class="line">      &#123;</span><br><span class="line">        &quot;_index&quot; : &quot;flatten-label&quot;,</span><br><span class="line">        &quot;_type&quot; : &quot;_doc&quot;,</span><br><span class="line">        &quot;_id&quot; : &quot;2&quot;,</span><br><span class="line">        &quot;_score&quot; : null,</span><br><span class="line">        &quot;_source&quot; : &#123;</span><br><span class="line">          &quot;labels&quot; : &#123;</span><br><span class="line">            &quot;color&quot; : &quot;green&quot;,</span><br><span class="line">            &quot;price&quot; : 11000</span><br><span class="line">          &#125;</span><br><span class="line">        &#125;,</span><br><span class="line">        &quot;sort&quot; : [</span><br><span class="line">          &quot;11000&quot;</span><br><span class="line">        ]</span><br><span class="line">      &#125;</span><br><span class="line">    ]</span><br></pre></td></tr></table></figure></p><h3 id="aggregate-metric-double"><a href="#aggregate-metric-double" class="headerlink" title="aggregate_metric_double"></a><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/aggregate-metric-double.html">aggregate_metric_double</a></h3><p>도큐먼트의 숫자 타입의 필드들은 aggregation을 통해 min, max, sum, avg 등의 값을 계산할 수 있습니다. 이 계산은 raw 도큐먼트들의 필드를 쿼리 때 마다 계산할 수도 있지만, aggs 를 위한 통계성 데이터만 저장 해 놓을 때에 매우 유용하게 사용될 수 있습니다. 이 때 사용하는 필드 타입이 <code>aggregate_metric_double</code> 입니다. aggregate_metric_double 에는 다음의 두 설정 값이 있습니다.</p><ul><li>metrics : 하위 필드로 저장할 메트릭을 지정합니다. [<code>min</code>, <code>max</code>, <code>sum</code>, <code>value_count</code>] 가 올 수 있습니다. <code>avg</code>는 별도로 저장하지 않는데, sum 과 value_count 를 이용해서 계산하게 됩니다.</li><li>default_metric : 쿼리 시 사용할 디폴트 하위 필드를 지정합니다. 이 필드의 값을 <code>exist</code>, <code>range</code>, <code>term</code>, <code>terms</code> 쿼리로 검색할 수 있습니다.</li></ul><p>다음과 같이 revenue-stats 인덱스에 revenue 필드를 aggregate_metric_double 타입으로 선언합니다.<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">PUT revenue-stats</span><br><span class="line">&#123;</span><br><span class="line">  &quot;mappings&quot;: &#123;</span><br><span class="line">    &quot;properties&quot;: &#123;</span><br><span class="line">      &quot;revenue&quot;: &#123;</span><br><span class="line">        &quot;type&quot;: &quot;aggregate_metric_double&quot;,</span><br><span class="line">        &quot;metrics&quot;: [ &quot;min&quot;, &quot;max&quot;, &quot;sum&quot;, &quot;value_count&quot; ],</span><br><span class="line">        &quot;default_metric&quot;: &quot;max&quot;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>이 인덱스의 2020-01, 2020-02, 2020-03 도큐먼트에 revenue 필드의 하위 필드로 min, max, sum, value_count 값을 입력합니다.<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">PUT revenue-stats&#x2F;_doc&#x2F;2020-01</span><br><span class="line">&#123;</span><br><span class="line">  &quot;revenue&quot;: &#123;</span><br><span class="line">    &quot;min&quot;: 25,</span><br><span class="line">    &quot;max&quot;: 470,</span><br><span class="line">    &quot;sum&quot;: 124400,</span><br><span class="line">    &quot;value_count&quot;: 374</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line">PUT revenue-stats&#x2F;_doc&#x2F;2020-02</span><br><span class="line">&#123;</span><br><span class="line">  &quot;revenue&quot;: &#123;</span><br><span class="line">    &quot;min&quot;: 35,</span><br><span class="line">    &quot;max&quot;: 600,</span><br><span class="line">    &quot;sum&quot;: 187465,</span><br><span class="line">    &quot;value_count&quot;: 491</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line">PUT revenue-stats&#x2F;_doc&#x2F;2020-03</span><br><span class="line">&#123;</span><br><span class="line">  &quot;revenue&quot;: &#123;</span><br><span class="line">    &quot;min&quot;: 20,</span><br><span class="line">    &quot;max&quot;: 580,</span><br><span class="line">    &quot;sum&quot;: 159650,</span><br><span class="line">    &quot;value_count&quot;: 420</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>이제 <code>revenue</code> 필드를 각각 min, max, avg, sum, value_count 하는 aggs 들을 쿼리 해 봅니다.<font color=red>(revenue.min 같은 하위 필드가 아닌 revenue 필드인 것에 주목하세요)</font><br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">### 쿼리 ###</span><br><span class="line">GET revenue-stats&#x2F;_search</span><br><span class="line">&#123;</span><br><span class="line">  &quot;aggs&quot;: &#123;</span><br><span class="line">    &quot;min-revenue&quot;: &#123; &quot;min&quot;: &#123; &quot;field&quot;: &quot;revenue&quot; &#125; &#125;,</span><br><span class="line">    &quot;max-revenue&quot;: &#123; &quot;max&quot;: &#123; &quot;field&quot;: &quot;revenue&quot; &#125; &#125;,</span><br><span class="line">    &quot;sum-revenue&quot;: &#123; &quot;sum&quot;: &#123; &quot;field&quot;: &quot;revenue&quot; &#125; &#125;,</span><br><span class="line">    &quot;avg-revenue&quot;: &#123; &quot;avg&quot;: &#123; &quot;field&quot;: &quot;revenue&quot; &#125; &#125;,</span><br><span class="line">    &quot;total-count&quot;: &#123; &quot;value_count&quot;: &#123; &quot;field&quot;: &quot;revenue&quot; &#125; &#125;</span><br><span class="line">  &#125;, </span><br><span class="line">  &quot;size&quot;: 0</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">### 결과 ###</span><br><span class="line">  &quot;aggregations&quot; : &#123;</span><br><span class="line">    &quot;max-revenue&quot; : &#123;</span><br><span class="line">      &quot;value&quot; : 600.0</span><br><span class="line">    &#125;,</span><br><span class="line">    &quot;total-count&quot; : &#123;</span><br><span class="line">      &quot;value&quot; : 1285</span><br><span class="line">    &#125;,</span><br><span class="line">    &quot;sum-revenue&quot; : &#123;</span><br><span class="line">      &quot;value&quot; : 471515.0</span><br><span class="line">    &#125;,</span><br><span class="line">    &quot;min-revenue&quot; : &#123;</span><br><span class="line">      &quot;value&quot; : 20.0</span><br><span class="line">    &#125;,</span><br><span class="line">    &quot;avg-revenue&quot; : &#123;</span><br><span class="line">      &quot;value&quot; : 366.9377431906615</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure><br>aggs 는 전부 revenue 필드를 대상으로 했지만 min, max, sum 등은 revenue 아래의 하위 필드인 min, max 필드들의 값을 가지고 집계가 된 것을 확인할 수 있습니다.<br>다음은 range 쿼리로 revenue 값이 500 이상 600 미만인 값을 가져오는 쿼리입니다.<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">### 쿼리 ###</span><br><span class="line">GET revenue-stats&#x2F;_search</span><br><span class="line">&#123;</span><br><span class="line">  &quot;query&quot;: &#123;</span><br><span class="line">    &quot;range&quot;: &#123;</span><br><span class="line">      &quot;revenue&quot;: &#123;</span><br><span class="line">        &quot;gte&quot;: 500,</span><br><span class="line">        &quot;lt&quot;: 600</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">### 결과 ###</span><br><span class="line">  &quot;hits&quot; : &#123;</span><br><span class="line">    &quot;total&quot; : &#123;</span><br><span class="line">      &quot;value&quot; : 1,</span><br><span class="line">      &quot;relation&quot; : &quot;eq&quot;</span><br><span class="line">    &#125;,</span><br><span class="line">    &quot;max_score&quot; : 1.0,</span><br><span class="line">    &quot;hits&quot; : [</span><br><span class="line">      &#123;</span><br><span class="line">        &quot;_index&quot; : &quot;revenue-stats&quot;,</span><br><span class="line">        &quot;_type&quot; : &quot;_doc&quot;,</span><br><span class="line">        &quot;_id&quot; : &quot;2020-03&quot;,</span><br><span class="line">        &quot;_score&quot; : 1.0,</span><br><span class="line">        &quot;_source&quot; : &#123;</span><br><span class="line">          &quot;revenue&quot; : &#123;</span><br><span class="line">            &quot;min&quot; : 20,</span><br><span class="line">            &quot;max&quot; : 580,</span><br><span class="line">            &quot;sum&quot; : 159650,</span><br><span class="line">            &quot;value_count&quot; : 420</span><br><span class="line">          &#125;</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    ]</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure><br>range 검색은 revenue 필드를 대상으로 했지만 매핑에서 default 로 지정한 revenue.max 값을 검색한 결과가 나타납니다. 만약에 range 검색을 revenue.max, revenue.min 같은 필드를 대상으로 검색하게 되면 검색 결과는 나타나지 않습니다.</p><h3 id="dense-vector"><a href="#dense-vector" class="headerlink" title="dense_vector"></a><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/dense-vector.html">dense_vector</a></h3><p>dense_vector 를 이용하면 필드에 다차원의 벡터 값들을 입력해서 검색을 할 수 있습니다. <code>dims</code> 속성을 이용해서 차원 수를 입력합니다. 다음은 books 인덱스에 문자열 title 필드와 3차원의 벡터를 갖는 vector_recommend 필드에 값을 입력하는 예제입니다.<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">PUT books</span><br><span class="line">&#123;</span><br><span class="line">  &quot;mappings&quot;: &#123;</span><br><span class="line">    &quot;properties&quot;: &#123;</span><br><span class="line">      &quot;title&quot;: &#123;</span><br><span class="line">        &quot;type&quot;: &quot;text&quot;</span><br><span class="line">      &#125;,</span><br><span class="line">      &quot;vector_recommend&quot;: &#123;</span><br><span class="line">        &quot;type&quot;: &quot;dense_vector&quot;,</span><br><span class="line">        &quot;dims&quot;: 3</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>이제 books 인덱스에 머신러닝 관련 서적들을 입력 해 보겠습니다. vector_recommend 필드에 순서대로<br>[ “페이지 수”, “사용자 평점”, “책 가격” ]<br>3개의 항목값들을 배열로 입력하도록 하겠습니다. 벡터 간격이 너무 벌어지지 않도록 모든 값을 0~10 사이로 맞추기 위해 페이지 수는 1/100, 책 가격은 1/5000 을 한 값을 입력했습니다.<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line"># 1차원: 페이지 수&#x2F;100</span><br><span class="line"># 2차원: 사용자 평점 (최대 10.0)</span><br><span class="line"># 3차원: 책 가격 &#x2F; 5000</span><br><span class="line"></span><br><span class="line">POST books&#x2F;_bulk</span><br><span class="line">&#123;&quot;index&quot;: &#123;&quot;_id&quot;: 1&#125;&#125;</span><br><span class="line">&#123; &quot;title&quot;: &quot;혼자 공부하는 머신러닝+딥러닝&quot;, &quot;vector_recommend&quot;: [5.8, 10.0, 5.2] &#125;</span><br><span class="line">&#123;&quot;index&quot;: &#123;&quot;_id&quot;: 2&#125;&#125;</span><br><span class="line">&#123; &quot;title&quot;: &quot;핸즈온 머신러닝&quot;, &quot;vector_recommend&quot;: [9.52, 9.1, 9.8] &#125;</span><br><span class="line">&#123;&quot;index&quot;: &#123;&quot;_id&quot;: 3&#125;&#125;</span><br><span class="line">&#123; &quot;title&quot;: &quot;파이썬 머신러닝 완벽가이드&quot;, &quot;vector_recommend&quot;: [6.48, 9.6, 7.6] &#125;</span><br><span class="line">&#123;&quot;index&quot;: &#123;&quot;_id&quot;: 4&#125;&#125;</span><br><span class="line">&#123; &quot;title&quot;: &quot;밑바닥부터 시작하는 딥러닝&quot;, &quot;vector_recommend&quot;: [3.12, 9.5, 4.8] &#125;</span><br><span class="line">&#123;&quot;index&quot;: &#123;&quot;_id&quot;: 5&#125;&#125;</span><br><span class="line">&#123; &quot;title&quot;: &quot;빅데이터분석기사 필기&quot;, &quot;vector_recommend&quot;: [3.68, 9.6, 5.0] &#125;</span><br><span class="line">&#123;&quot;index&quot;: &#123;&quot;_id&quot;: 6&#125;&#125;</span><br><span class="line">&#123; &quot;title&quot;: &quot;선형대수와 통계학으로 배우는 머신러닝 with 파이썬&quot;, &quot;vector_recommend&quot;: [6.24, 9.8, 7.5] &#125;</span><br><span class="line">&#123;&quot;index&quot;: &#123;&quot;_id&quot;: 7&#125;&#125;</span><br><span class="line">&#123; &quot;title&quot;: &quot;파이썬 라이브러리를 활용한 머신러닝&quot;, &quot;vector_recommend&quot;: [4.8, 9.2, 6.4] &#125;</span><br><span class="line">&#123;&quot;index&quot;: &#123;&quot;_id&quot;: 8&#125;&#125;</span><br><span class="line">&#123; &quot;title&quot;: &quot;파이썬 머신러닝 판다스 데이터 분석&quot;, &quot;vector_recommend&quot;: [3.92, 9.2, 5.0] &#125;</span><br><span class="line">&#123;&quot;index&quot;: &#123;&quot;_id&quot;: 9&#125;&#125;</span><br><span class="line">&#123; &quot;title&quot;: &quot;텐서플로 2와 머신러닝으로 시작하는 자연어 처리&quot;, &quot;vector_recommend&quot;: [5.6, 8.0, 7.0] &#125;</span><br><span class="line">&#123;&quot;index&quot;: &#123;&quot;_id&quot;: 10&#125;&#125;</span><br><span class="line">&#123; &quot;title&quot;: &quot;R 데이터 분석 머신러닝&quot;, &quot;vector_recommend&quot;: [3.08, 8.6, 4.0] &#125;</span><br></pre></td></tr></table></figure><br>이제 <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-script-score-query.html#vector-functions">script_score</a> 쿼리를 이용해서, books 인덱스의 도큐먼트들 중에</p><ul><li>페이지 수가 적고 : <code>1</code></li><li>평점은 높고 : <code>10</code></li><li>가격은 저렴한 : <code>1</code><br>추천 결과값을 가져오는 쿼리를 cosineSimilarity 를 이용해서 실행 해 보겠습니다.<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">GET books&#x2F;_search</span><br><span class="line">&#123;</span><br><span class="line">  &quot;query&quot;: &#123;</span><br><span class="line">    &quot;script_score&quot;: &#123;</span><br><span class="line">      &quot;query&quot;:&#123;</span><br><span class="line">        &quot;match_all&quot;: &#123;&#125;</span><br><span class="line">      &#125;,</span><br><span class="line">      &quot;script&quot;: &#123;</span><br><span class="line">        &quot;source&quot;: &quot;cosineSimilarity(params.query_vector, &#39;vector_recommend&#39;) + 1.0&quot;, </span><br><span class="line">        &quot;params&quot;: &#123;</span><br><span class="line">          &quot;query_vector&quot;: [1, 10, 1]</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">### 결과 ###</span><br><span class="line">&quot;hits&quot; : [</span><br><span class="line">...</span><br><span class="line">&quot;_source&quot; : &#123;</span><br><span class="line">  &quot;title&quot; : &quot;R 데이터 분석 머신러닝&quot;,</span><br><span class="line">  &quot;vector_recommend&quot; : [3.08, 8.6, 4.0]</span><br><span class="line">...</span><br><span class="line">&quot;_source&quot; : &#123;</span><br><span class="line">  &quot;title&quot; : &quot;밑바닥부터 시작하는 딥러닝&quot;,</span><br><span class="line">  &quot;vector_recommend&quot; : [3.12, 9.5, 4.8]</span><br><span class="line">...</span><br><span class="line">&quot;_source&quot; : &#123;</span><br><span class="line">  &quot;title&quot; : &quot;빅데이터분석기사 필기&quot;,</span><br><span class="line">  &quot;vector_recommend&quot; : [3.68, 9.6, 5.0]</span><br><span class="line">...</span><br></pre></td></tr></table></figure>가장 저렴하고 페이지수가 적은 책 부터 나타납니다. 이번에는 페이지수는 중간 <code>5</code>, 가격은 중간에서 약간 위 <code>6</code> 을 기준으로 놓고 그 조건에 맞는 책을 추천받는 쿼리를 실행 해 보겠습니다.<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">GET books&#x2F;_search</span><br><span class="line">&#123;</span><br><span class="line">  &quot;query&quot;: &#123;</span><br><span class="line">    &quot;script_score&quot;: &#123;</span><br><span class="line">      &quot;query&quot;:&#123;</span><br><span class="line">        &quot;match_all&quot;: &#123;&#125;</span><br><span class="line">      &#125;,</span><br><span class="line">      &quot;script&quot;: &#123;</span><br><span class="line">        &quot;source&quot;: &quot;cosineSimilarity(params.query_vector, &#39;vector_recommend&#39;) + 1.0&quot;, </span><br><span class="line">        &quot;params&quot;: &#123;</span><br><span class="line">          &quot;query_vector&quot;: [5, 10, 6]</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">### 결과 ###</span><br><span class="line">...</span><br><span class="line">&quot;_source&quot; : &#123;</span><br><span class="line">  &quot;title&quot; : &quot;파이썬 머신러닝 판다스 데이터 분석&quot;,</span><br><span class="line">  &quot;vector_recommend&quot; : [3.92, 9.2, 5.0]</span><br><span class="line">...</span><br><span class="line">&quot;_source&quot; : &#123;</span><br><span class="line">  &quot;title&quot; : &quot;파이썬 라이브러리를 활용한 머신러닝&quot;,</span><br><span class="line">  &quot;vector_recommend&quot; : [4.8, 9.2, 6.4]</span><br><span class="line">...</span><br><span class="line">&quot;_source&quot; : &#123;</span><br><span class="line">  &quot;title&quot; : &quot;혼자 공부하는 머신러닝+딥러닝&quot;,</span><br><span class="line">  &quot;vector_recommend&quot; : [5.8, 10.0, 5.2]</span><br><span class="line">...</span><br></pre></td></tr></table></figure></li></ul><p>elastcsearch 에는 이밖에도 다양한 고급 속성, 기능 들이 많이 있습니다. 잘 찾아보고 나에게 도움이 되는 기능들을 잘 활용 해 보시기 바랍니다.</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/9_MkRTXH0QU&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;acceler
      
    
    </summary>
    
      <category term="Engineering" scheme="http://kimjmin.net/categories/Engineering/"/>
    
    
      <category term="Elasticsearch" scheme="http://kimjmin.net/tags/Elasticsearch/"/>
    
      <category term="Elastic" scheme="http://kimjmin.net/tags/Elastic/"/>
    
      <category term="fields" scheme="http://kimjmin.net/tags/fields/"/>
    
  </entry>
  
  <entry>
    <title>Elastic Cloud 활용 2 - Elastic Cloud 마이그레이션</title>
    <link href="http://kimjmin.net/2020/07/2020-07-elastic-cloud-migration/"/>
    <id>http://kimjmin.net/2020/07/2020-07-elastic-cloud-migration/</id>
    <published>2020-07-19T15:00:00.000Z</published>
    <updated>2022-12-21T02:05:44.536Z</updated>
    
    <content type="html"><![CDATA[<p>오늘은 Elastic Cloud 활용 시리즈의 두번째로 Elastic Cloud 에서 Snapshot &amp; Restore 기능을 이용한 클러스터 마이그레이션에 대해서 알아보도록 하겠습니다.</p><blockquote><p><a href="/2020/07/2020-07-aws-s3-to-elastic-cloud">1 - Amazon S3 에서 Elastic Cloud 로 데이터 수집하기</a><br>2 - Elastic Cloud 마이그레이션</p></blockquote><h3 id="Elastic-클러스터-마이그레이션"><a href="#Elastic-클러스터-마이그레이션" class="headerlink" title="Elastic 클러스터 마이그레이션"></a>Elastic 클러스터 마이그레이션</h3><p>시스템을 운영하다 보면 버전이나 장비 업그레이드, 클라우드 서비스 사업자 교체 등의 이유로 데이터를 통째로 옮겨야 하는 경우가 종종 발생합니다. Elasticsearch 에는 데이터 마이그레이션을 할 수 있는 다양한 방법들이 있는데 잘 알려진 방법으로는</p><ul><li><a href="#소스-데이터를-다시-색인">소스 데이터를 다시 색인</a></li><li><a href="#data-디렉토리를-통째로-복사">data 디렉토리를 통째로 복사</a></li><li><a href="#Logstash-또는-reindex-API-를-이용해서-다른-클러스터로-복사">Logstash 또는 _reindex API 를 이용해서 다른 클러스터로 복사</a></li><li><a href="#Cross-Cluster-Search-를-이용해서-다수의-클러스터-운용">Cross Cluster Search 를 이용해서 다수의 클러스터 운용</a></li><li><a href="#snapshot-restore-API를-이용한-복원">_snapshot, _restore API를 이용한 복원</a></li></ul><p>등이 있습니다.</p><h5 id="소스-데이터를-다시-색인"><a href="#소스-데이터를-다시-색인" class="headerlink" title="소스 데이터를 다시 색인"></a>소스 데이터를 다시 색인</h5><p>저는 개인적으로 <strong>소스 데이터를 다시 색인</strong> 하는 것을 선호하는 편입니다. 시간이 가장 오래 걸리긴 하지만 Elasticsearch 새로운 장비나 버전에 대한 호환성 등에 신경 쓸 필요가 없고 마이그레이션 중 발생할 수 있는 문제를 체크 해 가며 가장 안전하게 할 수 있는 방법이기 때문입니다.</p><h5 id="data-디렉토리를-통째로-복사"><a href="#data-디렉토리를-통째로-복사" class="headerlink" title="data 디렉토리를 통째로 복사"></a>data 디렉토리를 통째로 복사</h5><p><strong>data 디렉토리를 통째로 복사</strong> 하는 방법은 보통 데이터 마이그레이션 보다는 플러그인 패치나 업그레이드를 할 때 주로 사용하는 방법입니다. <code>path.data</code> (디폴트는 홈 디렉토리의 <code>\data</code>) 내의 파일들은 그대로 두고 Elasticsearch 홈 디렉토리의 <code>\bin</code>, <code>\lib</code>, <code>\plugins</code> 등만 새 버전으로 덮어 씌운 뒤 노드를 다시 시작하면 데이터를 새로 색인 할 필요가 없이 바로 새 버전의 Elasticsearch 가 실행이 됩니다.</p><p>5.x 이상 부터는 메이저 버전으로의 롤링 업그레이드도 지원하기 때문에 클러스터 다운 없이 무중단으로 업그레이드도 가능합니다. 롤링 업그레이드 할 때는<br>샤드 리로케이션을 중단 -&gt; 노드 재시작 -&gt; 샤드 리로케이션 활성 -&gt; 클러스터 상태 Green 으로 변경 확인 -&gt; 다시 처음부터<br>를 반복해야 하기 때문에 중간에 순서가 꼬이지 않도록 조심해야 합니다. 자세한 순서와 명령들은 공식 도큐먼트에 잘 나와 있습니다.<br><a href="https://www.elastic.co/guide/en/elasticsearch/reference/7.8/setup-upgrade.html">https://www.elastic.co/guide/en/elasticsearch/reference/7.8/setup-upgrade.html</a></p><p>다만 이 때도 플러그인이라던가 depricated 된 API 등을 체크하지 않으면 샤드들이 살아나지 않을 수도 있습니다. 이것은 과거 6.4 버전때 제가 한번 실패했고</p><iframe width="560" height="315" src="https://www.youtube.com/embed/P6ezu7FJthg" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe><p>다시 했던 경험이 있는데</p><iframe width="560" height="315" src="https://www.youtube.com/embed/1NPnSJ5i0-M" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe><p>위의 두 영상들을 보면서 확인하실 수 있습니다.</p><h5 id="Logstash-또는-reindex-API-를-이용해서-다른-클러스터로-복사"><a href="#Logstash-또는-reindex-API-를-이용해서-다른-클러스터로-복사" class="headerlink" title="Logstash 또는 _reindex API 를 이용해서 다른 클러스터로 복사"></a>Logstash 또는 _reindex API 를 이용해서 다른 클러스터로 복사</h5><p><strong>Logstash 또는 _reindex API 를 이용해서 다른 클러스터로 복사</strong> 하는 것도 시간은 걸리지만 비교적 안전하게 데이터를 옮길 수 있는 방법입니다. 기존 클러스터의 _source 데이터를 이용해서 다른 클러스터로 다시 색인을 하는 것이기 때문에 기능적으로는 <strong>소스 데이터를 다시 색인</strong> 하는 것과 차이가 없습니다. 이 방법을 사용하기 위해서는 기존 클러스터의 인덱스들은 모두 _source 를 저장하고 있어야 합니다. (인덱스 mapping 에서 _source 를 저장하지 않도록 하는 설정도 있습니다.) 참고로 _reindex 로 재색인을 하는 경우 도큐먼트id 까지 동일하게 복사를 하며 Logstash 로 하는 경우 <code>docinfo</code> 라는 설정값으로 지정을 할 수 있습니다.</p><p>사용 방법은 Logstash 도큐먼트 페이지<br><a href="https://www.elastic.co/guide/en/logstash/current/plugins-inputs-elasticsearch.html">https://www.elastic.co/guide/en/logstash/current/plugins-inputs-elasticsearch.html</a><br>에서 확인하실 수 있습니다.</p><h5 id="Cross-Cluster-Search-를-이용해서-다수의-클러스터-운용"><a href="#Cross-Cluster-Search-를-이용해서-다수의-클러스터-운용" class="headerlink" title="Cross Cluster Search 를 이용해서 다수의 클러스터 운용"></a>Cross Cluster Search 를 이용해서 다수의 클러스터 운용</h5><p>Elastic 클러스터 운영 경험과 노하우가 많이 쌓인 분들은 Cross Cluster Search 를 이용해서 여러 클러스터를 운영 해 가며 마이그레이션 하는 방법도 있습니다. 새로운 데이터는 새 버전으로만 색인을 하고 과거 클러스터와 새 클러스터를 한 클라이언트(Kibana) 에서 같이 검색하면서 사용하다가 과거 클러스터의 데이터들의 보관 주기가 끝나면 이제 새 클러스터로만 운영하는 방식입니다. 장기간의 계획으로 운영 되어야 하겠지만 잘만 활용 되면 매우 유용하게 사용할 수 있는 방법입니다.</p><p>Elasticsearch 는 5.3, Kibana 는 5.7 버전 부터 Cross Cluster Search 기능을 지원합니다. 아래는 제가 오래 전에 Cross Cluster Search 관련해서 발표했던 장표를 캡쳐 한 것입니다.</p><p><img src="cross-cluster-kibana.png" alt="Cross Cluster"></p><h3 id="snapshot-restore-API를-이용한-복원"><a href="#snapshot-restore-API를-이용한-복원" class="headerlink" title="_snapshot, _restore API를 이용한 복원"></a>_snapshot, _restore API를 이용한 복원</h3><p>Elasticsearch 에는 스냅샷을 찍어 인덱스를 저장하는 기능이 있습니다. 이 스냅샷을 다른 클러스터에서 복원(restore) 시키는 것으로 클러스터의 데이터를 옮길 수 있습니다. Snapshot, Restore 방법은 아래 도큐먼트에 있습니다.<br><a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/snapshot-restore.html">https://www.elastic.co/guide/en/elasticsearch/reference/current/snapshot-restore.html</a></p><p>스냅샷은 로컬 디스크나 공유 디스크, HDFS, Amazon S3, Azure, Google Cloud Storage 등에도 저장이 가능합니다.<br><a href="https://www.elastic.co/guide/en/elasticsearch/plugins/5.5/repository.html">https://www.elastic.co/guide/en/elasticsearch/plugins/5.5/repository.html</a></p><p>Elastic Cloud 에서 클러스터를 생성 할 때는 GCP, Azure, AWS 중에 선택이 가능합니다.</p><p><img src="es-cloud-vendors.png" alt="Elastic Cloud 서비스 제공자"></p><p>Elastic Cloud 에서는 자동적으로 매 30분 마다 클러스터의 스냅샷을 해당 서비스 사업자의 스토리지(AWS의 경우 S3)에 자동으로 저장하고 있습니다. 클러스터에 문제가 생기면 인덱스를 삭제하고 과거 스냅샷의 복원이 가능합니다.</p><p><img src="es-cloud-snapshot.png" alt="Elastic Cloud 스냅샷"></p><p>또한 Elastic Cloud 에서 새 클러스터를 생성할 때 다른 클러스터가 저장 해 놓은 스냅샷으로부터 클러스터를 생성하거나 가져와서 복원 하는 것이. 이는 운영 서비스 데이터를 복사해서 개발 클러스터를 만들거나 할 때 매우 유용합니다. 다만 <font color='orange'>같은 서비스 사업자와 같은 리전에 있는 클러스터의 스냅샷</font>만 사용 가능한 제약이 있습니다.</p><p><img src="es-cloud-sn-select.png" alt="Elastic Cloud 스냅샷 선택"></p><p>아마도 이번에 Elastic Cloud 의 서울 리전 발표로 Elastic Cloud 를 사용하시는 분들이 서울 리전으로의 데이터 마이그레이션을 계획하고 계신 분들이 많으실겁니다. <font color='blue'>Elastic Cloud 관리 화면과 Kibana 에서 스냅샷 환경을 직접 설정하면 스냅샷을 다른 리전에서도 복원이 가능합니다<font>.</p><h5 id="s3-client-설정"><a href="#s3-client-설정" class="headerlink" title="s3.client 설정"></a>s3.client 설정</h5><p>Amazon S3 를 스냅샷 리파지토리로 사용하기 위해서는 S3 클라이언트 정보를 설정해야 합니다. 기본적인 설정법은 아래 도큐먼트 페이지에 있습니다.<br><a href="https://www.elastic.co/guide/en/elasticsearch/plugins/7.8/repository-s3-client.html">https://www.elastic.co/guide/en/elasticsearch/plugins/7.8/repository-s3-client.html</a></p><p>먼저 AWS IAM 에서 <code>AmazonS3FullAccess</code> 권한을 가진 사용자의 <code>Access Key</code> 와 <code>Secret Key</code> 가 필요합니다. 위의 도큐먼트에서는 이 값들을 elasticsearch-keystore 에서 설정하도록 안내를 하고 있습니다.</p><p><img src="keystore-doc.png" alt=""></p><p>keystore 는 터미널에서 하는 작업이기 때문에 위와 같은 명령은 온프렘 환경에서만 가능합니다. 다행이 Elastic Cloud 에서는 Security 메뉴에서 keystore 값을 설정할 수 있도록 기능을 제공하고 있습니다. Security 화면의 Elasticsearch Keystore 섹션의 Add settings 버튼을 클릭하면</p><p><img src="security-keystore.png" alt=""></p><p>오른쪽에서 아래와 같은 화면이 팝업되어 설정을 추가할 수 있습니다.</p><p><img src="aws-access-key.png" alt=""></p><p>클라이언트 정보는 <code>s3.client.CLIENT_NAME.SETTING_NAME</code> 같은 형태로 설정합니다. 위 스크린샷에서 설정한 클라이언트의 이름은 <code>default</code> 이고 <code>access_key</code> 값을 지정 한 것입니다. 같은 방법으로 <code>s3.client.default.secret_key</code>도 저장을 합니다.</p><p>이제 Kibana 의 Stack Management 메뉴에 있는 Snapshot and Restore 메뉴에서 새로운 S3 리파지토리를 등록할 수 있습니다.</p><p><img src="kibana-snapshot-1.png" alt=""> <img src="kibana-snapshot-2.png" alt=""> <img src="kibana-snapshot-3.png" alt=""></p><p>또는 Dev Tools 에서 _snapshot API 를 이용해서 바로 등록하는 것도 가능합니다.<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">PUT _snapshot&#x2F;s3-repository</span><br><span class="line">&#123;</span><br><span class="line">  &quot;type&quot;: &quot;s3&quot;,</span><br><span class="line">  &quot;settings&quot;: &#123;</span><br><span class="line">    &quot;bucket&quot;: &quot;my_bucket&quot;,</span><br><span class="line">    &quot;client&quot;: &quot;default&quot;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>이제 스냅샷을 실행하면 앞서 지정한 S3 버킷에 저장이 됩니다. 다른 클러스터에도 같은 S3 리파지토리를 등록하고 S3 버킷에 저장된 스냅샷을 복원할 수 있습니다.</p><p>더 자세한 사용법은 아래 영상을 참고하시기 바라며 안전하게 마이그레이션 성공하시길 바랍니다. 🤓</p><iframe width="560" height="315" src="https://www.youtube.com/embed/YDDvdFqaIWU" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;오늘은 Elastic Cloud 활용 시리즈의 두번째로 Elastic Cloud 에서 Snapshot &amp;amp; Restore 기능을 이용한 클러스터 마이그레이션에 대해서 알아보도록 하겠습니다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=
      
    
    </summary>
    
      <category term="Engineering" scheme="http://kimjmin.net/categories/Engineering/"/>
    
    
      <category term="Elasticsearch" scheme="http://kimjmin.net/tags/Elasticsearch/"/>
    
      <category term="AWS" scheme="http://kimjmin.net/tags/AWS/"/>
    
      <category term="Elastic" scheme="http://kimjmin.net/tags/Elastic/"/>
    
      <category term="Elastic Cloud" scheme="http://kimjmin.net/tags/Elastic-Cloud/"/>
    
      <category term="S3" scheme="http://kimjmin.net/tags/S3/"/>
    
      <category term="Snapshot" scheme="http://kimjmin.net/tags/Snapshot/"/>
    
  </entry>
  
  <entry>
    <title>Elastic Cloud 활용 1 - Amazon S3 에서 Elastic Cloud 로 데이터 수집하기</title>
    <link href="http://kimjmin.net/2020/07/2020-07-aws-s3-to-elastic-cloud/"/>
    <id>http://kimjmin.net/2020/07/2020-07-aws-s3-to-elastic-cloud/</id>
    <published>2020-07-12T15:00:00.000Z</published>
    <updated>2022-12-21T02:05:44.536Z</updated>
    
    <content type="html"><![CDATA[<p>드디어 Elastic Cloud 의 서울 리전이 생긴다는 기쁜 소식입니다.</p><p><img src="es-seoul-blog.png" alt="Elastic Cloud 서울 리전 출시"></p><p>Elastic Cloud 는 <a href="https://cloud.elastic.co">https://cloud.elastic.co</a> 에서 쉽게 가입하고 시작할 수 있습니다. 서울 리전 런칭을 위해 사전 신청과 런칭 행사도 준비하고 있으니 관심 있는 분들은 아래 링크에서 신청 하시면 됩니다.🤓<br><a href="https://events.elastic.co/2020-elasticcloud-seoul-promo">https://events.elastic.co/2020-elasticcloud-seoul-promo</a></p><p>Elastic Cloud 활용에 유용한 정보들을 시리즈로 작성 해 보려고 합니다. 오늘은 첫번째인 Logstash를 이용한 AWS S3 에서 Elastic Cloud 로 데이터 수집하기 입니다.</p><blockquote><p>1 - Amazon S3 에서 Elastic Cloud 로 데이터 수집하기<br><a href="/2020/07/2020-07-elastic-cloud-migration">2 - Elastic Cloud 마이그레이션</a></p></blockquote><p>데이터 스토리지인 Amazon S3는 AWS 에서 가장 많이 쓰이는 서비스 중 하나입니다. 이번 포스트에서는 S3의 정적 웹사이트 호스팅 기능을 이용하는 웹 페이지 로그를 Logstash로 파싱해서 Elastic Cloud 로 수집하는 방법에 대해 설명하도록 하겠습니다.</p><p>Logstash 에 대해서 잘 모르시는 분들은 Elastic 공식 홈페이지에 있는 <a href="https://www.elastic.co/kr/webinars/getting-started-logstash">Logstash 시작하기</a> 영상을 먼저 보시기 바랍니다.</p><blockquote><p>이 포스트를 작성하고 있는 중에 Elastic 공식 홈페이지의 <a href="https://www.elastic.co/kr/blog/getting-aws-logs-from-s3-using-filebeat-and-the-elastic-stack">Filebeat와 Elastic Stack을 사용해 S3의 AWS 로그 가져오기</a> 블로그가 한글로 번역이 되었습니다. 중복되는 내용이 있지만 같은 내용도 여러 번 보는 것이 도움이 되니 같이 보시면 좋을 것 같습니다.</p></blockquote><p>그리고 앞으로는 가능하면 Elastic 관련 블로그는 활용 영상과 함께 올리도록 하겠습니다. 영상은 블로그 맨 아래에 있습니다.</p><h3 id="Amazon-S3-정적-웹사이트의-접속-로그"><a href="#Amazon-S3-정적-웹사이트의-접속-로그" class="headerlink" title="Amazon S3 정적 웹사이트의 접속 로그"></a>Amazon S3 정적 웹사이트의 접속 로그</h3><p>블로그를 쉽게 쓰시려는 분들은 <a href="https://section.blog.naver.com">네이버</a>, <a href="https://www.tistory.com/">티스토리</a>, <a href="https://medium.com/">미디엄</a> 같은 가입형 서비스를 편리하게 이용이 가능합니다. 또는 직접 서버를 호스팅 해서 <a href="http://www.textcube.org">텍스트큐브</a>, <a href="https://wordpress.org/">워드프레스</a> 같은 설치형 블로그를 운영하시는 분들도 많이 계실겁니다. 그리고 비교적(?) 최근에는 마크다운을 이용해서 포스트를 작성하고 HTML로 빌드를 하여 업로드 하는 정적 호스팅형 블로그를 이용하시는 분들도 계실텐데요, 정적 호스팅 블로그 중 가장 널리 쓰이는 것은 아마도 Ruby 기반의 <a href="https://jekyllrb.com">지킬(jekyll)</a> 일 것이고요, 저는 Node.js 기반인 <a href="https://hexo.io/">헥소(Hexo)</a>를 사용하고 있습니다.</p><p>정적 블로그 호스팅은 데이터베이스나 미들웨어가 필요 없고 웹 서버만 있으면 되기 때문에 비교적 저렴한 비용으로 호스팅이 가능합니다. 깃헙에서도 무료로 정적 웹 페이지 생성 기능을 제공하고, Amazon S3 에서도 버킷에 있는 html 파일들로 정적 웹사이트를 호스팅 하는 기능을 제공합니다. 제 블로그도 S3의 정적 웹사이트 호스팅 기능을 이용하고 있습니다.</p><p>Amazon S3 의 접속 기록을 다른 버킷으로 저장하는 기능이 있습니다. 이 기능을 이용하면 S3의 정적 웹 호스팅 웹사이트의 접속 로그의 분석이 가능합니다. 저는 kimjmin.net 이라는 버킷에 블로그의 소스 파일을 올려놓고 kimjmin.net.logs 라는 버킷으로 접속 로그를 수집하고 있습니다.</p><p><img src="static-access-log-bucket.png" alt="S3 정적 웹사이트 로그 수집 설정"></p><h3 id="Logstash-S3-input"><a href="#Logstash-S3-input" class="headerlink" title="Logstash S3 input"></a>Logstash S3 input</h3><p>Logstash 는 100여개에 가까운 거의 모든 서비스와 시스템의 입력과 출력을 지원합니다. Logstash의 <a href="https://www.elastic.co/guide/en/logstash/current/plugins-inputs-s3.html">S3 input plugin</a> 기능을 이용해서 데이터를 수집 해 보도록 하겠습니다.</p><p>설정 가능한 옵션들이 많이 있지만, S3 input 에서 주로 사용하는 설정은 아래와 같습니다.<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">input &#123;</span><br><span class="line">  s3 &#123;</span><br><span class="line">    region &#x3D;&gt; &quot;ap-northeast-2&quot;  </span><br><span class="line">    bucket &#x3D;&gt; &quot;kimjmin.net.logs&quot;</span><br><span class="line">    prefix &#x3D;&gt; &quot;logstash-ingest-data&#x2F;json&#x2F;&quot;</span><br><span class="line">    access_key_id &#x3D;&gt; &quot;AKIAJ2ZIBNECS98IGD4A&quot;</span><br><span class="line">    secret_access_key &#x3D;&gt; &quot;TrcfgIAqw7V+fASZ23MrunoVSPef6dsfT4m&#x2F;d8P7&quot;</span><br><span class="line">    interval &#x3D;&gt; 10</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><ul><li><strong>region</strong> : 리전 코드. 서울은 <code>ap-northeast-2</code></li><li><strong>bucket</strong> : 버킷명</li><li><strong>prefix</strong> : 버킷의 하위 경로</li><li><strong>access_key_id</strong> : AWS 액세스 키 ID</li><li><strong>secret_access_key</strong> : AWS 비밀 액세스 키</li><li><strong>interval</strong> : 리프레시 시간. 단위 - 초. 디폴트 - 60</li></ul><p>먼저 S3 접속이 되는지 간단히 테스트를 위해 <code>stdout&#123; &#125;</code> 으로 출력을 설정하고 Logstash 를 실행 해 보면 다음과 같이 S3 버킷으로 부터 데이터가 수집되는 것을 볼 수 있습니다.<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">input &#123;</span><br><span class="line">  s3 &#123;</span><br><span class="line">    ...</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">output &#123;</span><br><span class="line">  stdout &#123; &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br><img src="s3.png" alt="Logstash S3 수집 결과"></p><blockquote><font color="red">버킷 용량이 크면 처음 출력이 나타나기 까지도 오랜 시간이 걸리기 때문에 먼저 적은 용량의 샘플 데이터를 임시 버킷에 복사 해 놓고 테스트 하는것이 좋습니다.</font></blockquote><h3 id="Logstash-filter-를-이용한-데이터-파싱"><a href="#Logstash-filter-를-이용한-데이터-파싱" class="headerlink" title="Logstash filter 를 이용한 데이터 파싱"></a>Logstash filter 를 이용한 데이터 파싱</h3><p>Logstash 는 <code>input</code>, <code>filter</code>, <code>output</code> 세 단계로 이루어집니다. Elastic 서울 오피스에서 제가 진행했던 세미나를 들으신 분들은 아마 실습 때 많이 해 보셨을텐데요, filter 의 <code>grok</code> 기능을 이용해서 메시지 스트림을 여러 필드로 분리하고, <code>geoip</code>, <code>useragent</code> 등을 이용해서 데이터를 더욱 확장할 수 있습니다. <a href="https://www.elastic.co/kr/webinars/getting-started-logstash">Logstash 시작하기</a> 영상에서도 설명하고 있습니다.</p><h5 id="Grok"><a href="#Grok" class="headerlink" title="Grok"></a>Grok</h5><p>아래와 같은 일반적인 아파치 로그를 수집한다고 할 때<br><figure class="highlight"><table><tr><td class="code"><pre><span class="line">14.49.42.25 - - [12/May/2019:01:24:44 +0000] &quot;GET /articles/ppp-over-ssh/ HTTP/1.1&quot; 200 18586 &quot;-&quot; &quot;Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2b1) Gecko/20091014 Firefox/3.6b1 GTB5&quot;</span><br></pre></td></tr></table></figure><br>보통은 해당 데이터는 message 필드에 문자열로 입력이 됩니다.<br><img src="apache-log.png" alt=""></p><p>grok 필터를 이용해서 message 필드 패턴을 <code>COMBINEDAPACHELOG</code> 로 지정하면 다음과 같이 데이터를 파싱할 수 있습니다.<br><figure class="highlight"><table><tr><td class="code"><pre><span class="line">filter &#123;</span><br><span class="line">  grok &#123;</span><br><span class="line">    match =&gt; &#123; &quot;message&quot; =&gt; &quot;%&#123;COMBINEDAPACHELOG&#125;&quot; &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p><img src="apache-grok.png" alt=""></p><p>아파치 로그 처럼 Amazon S3 의 정적 웹 페이지 접속 로그도 지원하는 grok 패턴이 있는데, 바로 <code>S3_ACCESS_LOG</code> 입니다. logstash 설정에 다음의 filter 를 추가합니다.<br><figure class="highlight"><table><tr><td class="code"><pre><span class="line">input &#123;</span><br><span class="line">  s3 &#123;</span><br><span class="line">    ...</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">filter &#123;</span><br><span class="line">  grok &#123;</span><br><span class="line">    match =&gt; &#123; &quot;message&quot; =&gt; &quot;%&#123;S3_ACCESS_LOG&#125;&quot;&#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">output &#123;</span><br><span class="line">  stdout &#123; &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>이제 다시 Logstash로 S3 의 로그를 수집 해 보며 아래와 같이 데이터가 파싱된 것을 볼 수 있습니다.</p><p><img src="s3-grok.png" alt=""></p><h5 id="GeoIP"><a href="#GeoIP" class="headerlink" title="GeoIP"></a>GeoIP</h5><p>위 로그에 clientip 라는 필드에 접속한 클라이언트의 ip 주소가 있습니다. Logstash 는 <code>geoip</code> 필터를 이용해서 ip 주소로 부터 접속한 클라이언트의 위치 정보 (국가, 도시, 위도 및 경도 등)를 가져올 수 있습니다. 다음과 같이 filter 에 geoip 를 추가하면 geoip 라는 필드가 생성되고 하위 필드로 다양한 위치 정보들이 추가됩니다.<br><figure class="highlight"><table><tr><td class="code"><pre><span class="line">...</span><br><span class="line">filter &#123;</span><br><span class="line">  grok &#123;</span><br><span class="line">    match =&gt; &#123; &quot;message&quot; =&gt; &quot;%&#123;S3_ACCESS_LOG&#125;&quot;&#125;</span><br><span class="line">  &#125;</span><br><span class="line">  geoip &#123;</span><br><span class="line">    source =&gt; &quot;clientip&quot;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line">...</span><br></pre></td></tr></table></figure></p><p><img src="s3-geoip.png" alt=""></p><p>당연하지만 geoip 는 grok 아래에 와야 정상적으로 작동합니다.</p><h5 id="User-Agent"><a href="#User-Agent" class="headerlink" title="User Agent"></a>User Agent</h5><p>다시 위의 Grok 예제를 보면 agent 라는 이름의 필드에 클라이언트의 브라우저, OS, 디바이스 정보들이 문자열로 있는 것이 보입니다. <code>useragent</code> 필터를 이용해서 이 값을 구분 해 줄 수 있습니다. 다음과 같이 useragent 필터를 추가하면 user_agent 필드에 클라이언트 정보가 추가됩니다.<br><figure class="highlight"><table><tr><td class="code"><pre><span class="line">...</span><br><span class="line">filter &#123;</span><br><span class="line">  grok ...</span><br><span class="line">  geoip ...</span><br><span class="line">  useragent &#123;</span><br><span class="line">    source =&gt; &quot;agent&quot;</span><br><span class="line">    target =&gt; &quot;user_agent&quot;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line">...</span><br></pre></td></tr></table></figure><br><img src="s3-useragent.png" alt=""></p><h5 id="Date"><a href="#Date" class="headerlink" title="Date"></a>Date</h5><p>로그 데이터를 살펴보면 <code>@timestamp</code> 와 <code>timestamp</code> 필드 두 개가 있는 것을 확인할 수 있습니다. </p><p><img src="s3-date-1.png" alt=""></p><p>@timestamp 필드는 Elasticsearch 가 인식할 수 있는 날짜/시간 형태로 되어 있고, timestamp 필드는 문자열 값이며 Elasticsearch 가 디폴트로 이해할 수 있는 포맷도 아닙니다. 그러나 @timestamp 는 데이터를 읽어드리는 시점에 Logstash 가 기록한 값, 즉 로그가 출력되고 있는 현재 시간이고, 실제로 저 로그가 S3에 저장된 시간은 timestamp 필드에 있는 값입니다. 따라서 실제로 우리에게 필요한 값인 timestamp 필드를 Elasticsearch 의 날짜 형태로 바꿔서 저장 할 필요가 있습니다. 이 때 사용할 수 있는 것이 date 필터 입니다. 다음과 같이 date 필터를 추가하면 @timestamp 의 값이 timestamp 에 있는 날짜로 바뀌게 됩니다.</p><figure class="highlight"><table><tr><td class="code"><pre><span class="line">...</span><br><span class="line">filter &#123;</span><br><span class="line">  grok ...</span><br><span class="line">  geoip ...</span><br><span class="line">  useragent ...</span><br><span class="line">  date &#123;</span><br><span class="line">    match =&gt; [ &quot;timestamp&quot;, &quot;dd/MMM/yyyy:HH:mm:ss Z&quot; ]</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p><img src="s3-date-2.png" alt=""></p><h3 id="Elastic-Cloud-로-데이터-수집"><a href="#Elastic-Cloud-로-데이터-수집" class="headerlink" title="Elastic Cloud 로 데이터 수집"></a>Elastic Cloud 로 데이터 수집</h3><p>이제 지금까지 만든 Logstash 설정을 이용해서 Elastic Cloud 로 Amazon S3 데이터를 색인 해 보겠습니다. Elasticsearch 로 데이터를 보내려면 output 구문에 elasticsearch { } 를 추가합니다. stdout { } 을 삭제하지 않으면 elasticsearch 와 콘솔 화면에 모두 출력할 수도 있습니다.<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">output &#123;</span><br><span class="line">  # stdout &#123; &#125;</span><br><span class="line">  elasticsearch &#123;</span><br><span class="line">    hosts &#x3D;&gt; [&quot;localhost:9200&quot;]</span><br><span class="line">    user &#x3D;&gt; &quot;elastic&quot;</span><br><span class="line">    password &#x3D;&gt; &quot;changeme&quot;</span><br><span class="line">    index &#x3D;&gt; &quot;kimjmin-net-logs-%&#123;+yyyy.MM.dd&#125;&quot;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>보통은 hosts, user, password 설정을 사용하지만, Elastic Cloud를 사용하면 이 설정 대신 <code>cloud_id</code> 그리고 <code>cloud_auth</code> 설정을 이용할 수 있습니다. cloud_id 는 Elastic Cloud 관리 화면에서 확인이 가능합니다.</p><p><img src="cloud-auth.png" alt=""></p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">output &#123;</span><br><span class="line">  elasticsearch &#123;</span><br><span class="line">    cloud_id: &quot;:YXAtbm9ydGhlYXN0LTEuYXdzLmZvdW5kLmlvJDA5ZGUzMjVlMmU0YTRiMzJiZWM0NWRhNmMzOTJlZDdkJDZmNjExYTMzZjZkNTRhZDQ5ZGQyYWM2NGUzMjg0YjQ3&quot;</span><br><span class="line">    cloud_auth: &quot;elastic:changeme&quot;</span><br><span class="line">    index &#x3D;&gt; &quot;kimjmin-net-logs-%&#123;+yyyy.MM.dd&#125;&quot;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>이제 Elastic Cloud 에 있는 Kibana 에서 데이터가 입력 된 것을 확인합니다.</p><p><img src="kibana-s3-data.png" alt=""></p><p>Elastic Cloud 로 데이터가 잘 수집이 되었습니다. 다음 포스트에서는 Logstash 를 Kibana 에서 관리할 수 있도록 설정하는 방법과 S3 접속 로그를 이용해서 Kibana 에서 대시보드를 만드는 부분을 다루어 보도록 하겠습니다.</p><iframe width="560" height="315" src="https://www.youtube.com/embed/_kF0cLGpu7w" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;드디어 Elastic Cloud 의 서울 리전이 생긴다는 기쁜 소식입니다.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;es-seoul-blog.png&quot; alt=&quot;Elastic Cloud 서울 리전 출시&quot;&gt;&lt;/p&gt;
&lt;p&gt;Elastic Cloud 는 &lt;a hre
      
    
    </summary>
    
      <category term="Engineering" scheme="http://kimjmin.net/categories/Engineering/"/>
    
    
      <category term="Elasticsearch" scheme="http://kimjmin.net/tags/Elasticsearch/"/>
    
      <category term="AWS" scheme="http://kimjmin.net/tags/AWS/"/>
    
      <category term="Elastic" scheme="http://kimjmin.net/tags/Elastic/"/>
    
      <category term="Elastic Cloud" scheme="http://kimjmin.net/tags/Elastic-Cloud/"/>
    
      <category term="Logstash" scheme="http://kimjmin.net/tags/Logstash/"/>
    
      <category term="S3" scheme="http://kimjmin.net/tags/S3/"/>
    
  </entry>
  
  <entry>
    <title>Elastic Observability 를 이용한 Azure 모니터링</title>
    <link href="http://kimjmin.net/2020/06/2020-06-o11y-azure-0/"/>
    <id>http://kimjmin.net/2020/06/2020-06-o11y-azure-0/</id>
    <published>2020-06-15T15:00:00.000Z</published>
    <updated>2022-12-21T02:05:44.535Z</updated>
    
    <content type="html"><![CDATA[<p>COVID-19 때문에 커뮤니티 밋업을 온라인으로 진행하고 있습니다. 2020년 6월에는 Elastic Observability 를 이용해서 Azure 클라우드의 정보를 수집하고 시각화 하는 내용으로 진행을 하려고 합니다. 밋업 시간 동안 모든 작업을 진행하기가 어려워서 Azure 클라우드와 Elastic 클라우드의 클러스터와 가상 머신들을 생성하는 작업은 사전에 진행 해 놓으려고 합니다.</p><p>아래는 사전 작업 영상과, 작업에 사용된 Azure Cli 명령어 입니다.</p><iframe width="560" height="315" src="https://www.youtube.com/embed/jDGd4c62Rx4" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line"># Azure 로그인</span><br><span class="line">az login</span><br><span class="line"></span><br><span class="line"># 대한민국 중부에 리소스 그룹 o11y-az 생성 </span><br><span class="line">az group create --name o11y-az --location koreacentral</span><br><span class="line"></span><br><span class="line"># 리소스 그룹에 네트워크 보안 그룹 o11y-az-sec 생성</span><br><span class="line">az network nsg create --resource-group o11y-az \</span><br><span class="line">--name o11y-az-sec --location koreacentral</span><br><span class="line"></span><br><span class="line"># 네트워크 보안 그룹에 포트 ssh:22, http:80 포트를 외부로부터 접속 가능하도록 설정</span><br><span class="line">az network nsg rule create --resource-group o11y-az --nsg-name o11y-az-sec \</span><br><span class="line">--name ssh --access Allow --protocol Tcp --direction Inbound --priority 100 \</span><br><span class="line">--source-address-prefix Internet --source-port-range &quot;*&quot; \</span><br><span class="line">--destination-address-prefix &quot;*&quot; --destination-port-range 22</span><br><span class="line"></span><br><span class="line">az network nsg rule create --resource-group o11y-az --nsg-name o11y-az-sec \</span><br><span class="line">--name http --access Allow --protocol Tcp --direction Inbound --priority 200 \</span><br><span class="line">--source-address-prefix Internet --source-port-range &quot;*&quot; \</span><br><span class="line">--destination-address-prefix &quot;*&quot; --destination-port-range 80</span><br><span class="line"></span><br><span class="line"># 리소스 그룹에 가상 네트워크 o11y-az-vn 생성</span><br><span class="line">az network vnet create --resource-group o11y-az --name o11y-az-vn \</span><br><span class="line">--address-prefix 10.0.0.0&#x2F;16 --subnet-name default</span><br><span class="line"></span><br><span class="line"># 가상 네트워크에 네트워크 보안 그룹 적용</span><br><span class="line">az network vnet subnet update --resource-group o11y-az \</span><br><span class="line">--vnet-name o11y-az-vn \</span><br><span class="line">--name default --network-security-group o11y-az-sec</span><br><span class="line"></span><br><span class="line"># 리소스 그룹에 가상 머신 o11y-az-vm 생성 후 생성된 가상 네트워크에 연결</span><br><span class="line"># 이미지 : UbuntuServer:18.04-LTS &#x2F; 디스크 : 50GB</span><br><span class="line">az vm create --resource-group o11y-az \</span><br><span class="line">--name o11y-az-vm --image Canonical:UbuntuServer:18.04-LTS:latest \</span><br><span class="line">--size Standard_D2s_v3 --data-disk-sizes-gb 50 \</span><br><span class="line">--public-ip-address-allocation static \</span><br><span class="line">--vnet-name o11y-az-vn --subnet default --nsg o11y-az-sec --generate-ssh-keys --verbose</span><br></pre></td></tr></table></figure><p>위 명령대로 실행하고 나면 Azure 포털에 다음과 같이 o11y-az 리소스 그룹과 필요한 자원들이 생성됩니다.</p><p><img src="az-resources.png" alt=""></p><p>당연한 이야기지만 리소스 그룹과 자원 이름들은 임의로 설정이 가능합니다. 리소스 그룹만 삭제하면 포함된 모든 자원들이 삭제되기 때문에 테스트 후 초기화 하기 편합니다.<br>계속 해서 영상을 참고해서 가상 머신에 접속하고 nginx 를 설치합니다.</p><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line"># 가상 머신에 연결</span><br><span class="line">ssh -i &lt;private key path&gt; user_id@&lt;ip address&gt;</span><br><span class="line"></span><br><span class="line"># nginx 설치</span><br><span class="line">sudo apt-get update</span><br><span class="line">sudo apt-get install nginx</span><br><span class="line">sudo service nginx restart</span><br></pre></td></tr></table></figure><p>Metricbeat 과 Filebeat 은 아래 링크에서 다운로드 한 뒤 Kibana 에 있는 안내대로 설치하면 됩니다.<br><a href="https://www.elastic.co/downloads/beats/metricbeat">https://www.elastic.co/downloads/beats/metricbeat</a><br><a href="https://www.elastic.co/downloads/beats/filebeat">https://www.elastic.co/downloads/beats/filebeat</a></p><p><img src="kibana-intro.png" alt=""></p><blockquote><p>ps:영상에 사용된 Elastic Cloud 클러스터와 Azure 리소스는 영상 녹화 후 모두 삭제 했습니다. 🤓</p></blockquote><h3 id="2020-06-24-에-진행한-온라인-밋업의-본-영상입니다"><a href="#2020-06-24-에-진행한-온라인-밋업의-본-영상입니다" class="headerlink" title="2020-06-24 에 진행한 온라인 밋업의 본 영상입니다."></a>2020-06-24 에 진행한 온라인 밋업의 본 영상입니다.</h3><p>최영락 님의 첫번째 발표 영상입니다.</p><ul><li>제목 : MS Azure 클라우드에서 Elastic 시작하기 (부제: Azure 와 만난 Elastic)</li><li>발표 자료: <a href="https://aka.ms/azureelastic">https://aka.ms/azureelastic</a></li></ul><iframe width="560" height="315" src="https://www.youtube.com/embed/Ygn8RlkBa6o" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe><p>제가 발표한 두번째 파트 입니다.</p><ul><li>제목 : Elastic Observability 를 이용한 Azure 모니터링</li><li>발표 자료: <a href="https://docs.google.com/presentation/d/e/2PACX-1vSrlNw8_7TWYbWo_szydslBGxJ-0eYWxhND0SuPtVZsnjkVnDXzCxb9ZYTPxmJ9Oo6C5wKk-yUx_Fll/pub?start=false&amp;loop=false&amp;delayms=3000">링크</a></li></ul><iframe width="560" height="315" src="https://www.youtube.com/embed/TStDNsMWh7Y" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;COVID-19 때문에 커뮤니티 밋업을 온라인으로 진행하고 있습니다. 2020년 6월에는 Elastic Observability 를 이용해서 Azure 클라우드의 정보를 수집하고 시각화 하는 내용으로 진행을 하려고 합니다. 밋업 시간 동안 모든 
      
    
    </summary>
    
      <category term="Engineering" scheme="http://kimjmin.net/categories/Engineering/"/>
    
    
      <category term="Elasticsearch" scheme="http://kimjmin.net/tags/Elasticsearch/"/>
    
      <category term="Meetup" scheme="http://kimjmin.net/tags/Meetup/"/>
    
      <category term="Elastic" scheme="http://kimjmin.net/tags/Elastic/"/>
    
      <category term="Monitoring" scheme="http://kimjmin.net/tags/Monitoring/"/>
    
      <category term="Observability" scheme="http://kimjmin.net/tags/Observability/"/>
    
      <category term="Azure" scheme="http://kimjmin.net/tags/Azure/"/>
    
      <category term="모니터링" scheme="http://kimjmin.net/tags/%EB%AA%A8%EB%8B%88%ED%84%B0%EB%A7%81/"/>
    
      <category term="통합 가시성" scheme="http://kimjmin.net/tags/%ED%86%B5%ED%95%A9-%EA%B0%80%EC%8B%9C%EC%84%B1/"/>
    
  </entry>
  
  <entry>
    <title>Elastic 라이센스, 그리고 오픈 소스 에반젤리스트의 딜레마</title>
    <link href="http://kimjmin.net/2020/06/2020-06-elastic-devrel/"/>
    <id>http://kimjmin.net/2020/06/2020-06-elastic-devrel/</id>
    <published>2020-06-01T15:00:00.000Z</published>
    <updated>2022-12-21T02:05:44.535Z</updated>
    
    <content type="html"><![CDATA[<p>정말 오랬만에 글을 씁니다. 반년 넘게 글을 쓰지 않았었네요. 그 동안 새로운 집으로 이사도 했고 여러가지 개인적인 행사에 신경 쓰느라 블로그 관리에 많이 소홀했던 것 같습니다. 오랬만에 그 동안 해 보고 싶었던 오픈소스 라이센스와 비즈니스에 대한 이야기를 해 보려고 합니다.</p><p>Elastic 한국 커뮤니티를 시작 한 것이 인연이 되어 Elastic 의 직원으로 합류한지 어느덧 햇수로 5년이 되었습니다. 제 포지션을 Elastic 에서는 <strong>Community Engineer</strong> 라고 부르고 있고 어떤 곳에서는 <strong>에반젤리스트(Evangelist)</strong>, <strong>디벨로퍼 에드버킷(Developer Advocate)</strong> 또는 <strong>데브릴(DevRel)</strong> 등으로 불리우는 일을 하고 있습니다. 이 역할에 대해서는 마이크로소프트의 디벨로퍼 애드버킷이신 유정협(Justin Yoo) 님께서 아주 자세하게 소개를 해 주셨으니 궁금하신 분은 유정협 님의 글 - <a href="https://justinchronicles.net/ko/2020/03/14/what-do-developer-advocates-do">디벨로퍼 아드보캇은 뭘 하는 사람인가요?</a> 를 한번 읽어 보시기 바랍니다.</p><p>Elastic 이 만든 프로덕트인 Elastic Stack 은 (ELK Stack 으로 더 잘 알려져 있는) 오픈소스로 잘 알려져 있습니다. 오픈 소스로 제품을 개발하는 회사들이 비즈니스를 하는 방법은 다양하지만, 대개는 다음의 과정을 많이 거칩니다.</p><ol><li>기술지원</li><li>상용(유료) 기능 추가</li><li>Cloud 서비스 제공</li></ol><p>Elastic 도 위의 과정을 거치면서 비즈니스를 진행 해 왔습니다. 물론 이 과정에서 다양한 제품과 새로운 서비스를 개발하였고, 다양한 회사들을 인수 하기도 하면서 제품과 서비스를 확장 해 왔습니다.</p><h3 id="Elastic-Stack-과-솔루션"><a href="#Elastic-Stack-과-솔루션" class="headerlink" title="Elastic Stack 과 솔루션"></a>Elastic Stack 과 솔루션</h3><p>Elastic 의 근간이 되는 ELKB 의 핵심 기능은 모두 오픈소스입니다. 처음 Elastic 의 제품 포트폴리오는 아래와 같았습니다.</p><p><img src="elkbx.png" alt="Elastic Stack 포트폴리오"></p><p>기본 스택인 Elasticsearch, Logstash, Kibana, Beats 는 전부 오픈소스로 제공이 되었고, 그 외의 Security, Alerting, Monitoring, Graph 같은 상용 기능은 X-Pack 이라는 이름의 플러그인 설치를 통해 제공이 되었습니다.</p><p>그 이후에 Elastic 은 ELKB 스택 전반에 걸쳐 APM, 머신러닝, SIEM 등의 기능과 서비스를 계속 해서 개발을 해 왔습니다. 이 각각의 기능, 제품들을 솔루션 이라고 명명을 하였고 이 모든것을 클라우드에 서비스를 하면서 포트폴리오를 다음과 같이 재 편성하였습니다.</p><p><img src="solutions.png" alt="Elastic 솔루션"></p><p>또 솔루션이 종류가 점점 많아짐에 따라 최근에는 솔루션들을 </p><ul><li><a href="https://www.elastic.co/kr/enterprise-search">Enterprise Search</a></li><li><a href="https://www.elastic.co/kr/observability">Observability</a></li><li><a href="https://www.elastic.co/kr/security">Security</a> </li></ul><p>세 개의 카테고리로 구분을 하고 각 솔루션을 이 카테고리 안에 배치하였습니다.</p><p><img src="3products.png" alt=""></p><blockquote><p>스포일러 : 나중에는 Kibana 좌측 메뉴도 위 3개의 카테고리에서 펼침 기능으로 바뀔 예정입니다.</p></blockquote><p>특히 Observability 는 APM, 모니터링, 메트릭, 로그분석 등을 아우르는 개념으로 저도 Elastic 에서 제품화 한 이름으로 처음 접하게 된 단어라 어떻게 한국어로 번역해야 할지 고민이 많았습니다. Elastic 한국 팀에서는 <strong>Observability</strong> 를 공식적으로 <strong>통합 가시성</strong> 이라는 단어로 번역해서 부르고 있습니다.</p><h3 id="Elastic-라이센스-특히-Basic-라이센스에-대하여"><a href="#Elastic-라이센스-특히-Basic-라이센스에-대하여" class="headerlink" title="Elastic 라이센스 - 특히 Basic 라이센스에 대하여"></a>Elastic 라이센스 - 특히 Basic 라이센스에 대하여</h3><p>Elastic 제품들은 오픈소스 외에도 다음과 같은 다양한 라이센스들을 가지고 있습니다. 전체 내용은 Elastic의 <a href="https://www.elastic.co/kr/subscriptions">기술지원 구독</a> 홈페이지를 방문하시면 볼 수 있습니다.</p><p><img src="licenses.png" alt="Elastic 라이센스 - 오픈소스, 기본, 골드, 플래티넘, 엔터프라이즈"></p><p>Elastic 에서 제공하는 제품들 중 가장 이야기거리가 많은 것이 <strong>Basic(기본)</strong> 입니다. Basic 에 속한 기능들은 오픈소스 라이센스는 아니지만 무료로 사용할 수 있도록 제공되고 있는 것 들입니다. 대표적으로 <a href="https://www.elastic.co/kr/what-is/kibana-canvas">Canvas</a> 같은 확장 기능과 <a href="https://www.elastic.co/kr/blog/a-profile-a-day-keeps-the-doctor-away-the-elasticsearch-search-profiler">Search Profiler</a> 같은 개발자 도구, <a href="https://www.elastic.co/kr/blog/improving-kibanas-query-language">Kibana 쿼리 자동 완성</a> 같은 편의 기능등이 대부분 Basic 라이센스에 속해 있습니다.</p><p>과거 X-Pack 플러그인을 제공하던 시기에는 플러그인을 Elasticsearch 그리고 Kibana 에 설치하고 라이센스 키를 적용하면 상용 기능들을 이용할 수 있었습니다. 이 시기에도 Basic 라이센스에 속한 기능들을 무료로 사용할 수 있었으나, X-Pack 을 먼저 설치 한 후 <code>elasticsearch.yml</code>, <code>kibana.yml</code> 에서 x-pack 을 Basic 으로 다시 명시해야 했기에 사용하는 분들이 많이 없었습니다. 그리고 플러그인을 한번 설치해야 한다는 것이 꽤 큰 장벽이었기 때문에 <a href="https://www.elastic.co/kr/blog/doubling-down-on-open">Elastic 6.3 버전 부터는 Basic 라이센스의 기능들이 포함된 설치 파일을 기본 배포판에 포함시키고 Gold, Platinum 라이센스의 기능들 까지 모두 코드를 오픈하였습니다</a>.</p><p>아파치 라이센스의 기능만을 포함하는 배포판은 다운로드 페이지에서 별도의 링크로 제공됩니다. 각 스택의 다운로드 페이지에 다음과 같은 링크를 찾을 수 있습니다.</p><p><img src="oss-download.png" alt=""></p><p>Elastic의 Basic 배포판에는 상당히 많은 기능들이 포함되어 있습니다. 사실 Basic 을 넘어 Gold, Platinum 에만 속해 있는 기능은 실제로 얼마 되지 않을 정도입니다. 아래는 OSS, Basic, Platinum 의 Kibana 메뉴들을 각각 비교 한 것입니다.</p><p><img src="kibana-compare.png" alt=""></p><p>물론 메뉴 개수 말고도 각 기능 안에서의 차이들이 있습니다. Machine Learning 의 경우 Basic 에서는 Data Visualizer 만 활성화 되고 Anomaly Detection 같은 기능은 Platinum 이상에서 활성화가 됩니다.</p><p><img src="data-visualizer.png" alt=""></p><p>Basic 에서 제공하는 Data Visualizer 도 실제 운영을 할 때에는 상당히 편리한 기능입니다. Beats 나 Logstash 등을 이용하지 않고 Kibana 에서 파일을 업로드 하면 알아서 소스 파일을 분석해서 인덱스 매핑과 ingest pipeline 등을 자동으로 만들어 주는 기능입니다.</p><p>이 Basic 에 있는 기능은 모두 무료로 사용이 가능합니다. 무료로 배포 되고 깃헙에 코드도 공개되어 있지만 라이센스는 Elastic 사에 귀속되어 있기 때문에 몇 가지는 주의 하셔야 합니다.</p><p>Elastic 제품을 내재 한 제품으로 비즈니스를 하려면 Elastic사와 협약을 체결하지 않은 경우 OSS 버전으로만 비즈니스를 해야 합니다. SI, 클라우드 서비스, 컨설팅, 기술지원, 교육 등이 모두 포함됩니다. 그래서 클라우드 서비스 사업자들 중에 Elastic 사와 파트너 협약을 하지 않고 Elastic Stack 서비스를 제공하는 사업자들은 OSS 라이센스에 속한 기능만 서비스를 해야 합니다.</p><blockquote><p>사실 Elastic 사에서 서비스 하는 공식 클라우드 (<a href="https://cloud.elastic.co">https://cloud.elastic.co</a>) 가 아닌 다른 서비스들을 통해 Elastic 을 시작 해 보신 분들은 OSS 기능만 경험 해 보시고 Basic 이상의 기능이 있는 줄 모르는 경우가 많아 Elastic 직원의 입장에서는 상당히 안타까운 경우가 많습니다.</p></blockquote><p>그리고 당연한 이야기지만 Basic 이상 기능의 소스 코드를 베껴서 자사 제품을 만드는 것도 하시면 안됩니다. 라이센스 전문은 <a href="https://github.com/elastic/elasticsearch/tree/master/licenses">Elastic 깃헙</a>에 있습니다.</p><p><img src="github-license-file.png" alt=""></p><p>그럼 Basic 라이센스는 쓰라고 만들어 놓은게 맞기는 한거냐? 라고 반문 하시는 분들이 계실것 같습니다. 네. Basic 라이센스는 Elastic 사가 사용자 분들을 위해 만들고 무료로 쓰시라고 제공 해 드리는 기능이 맞습니다. Basic 에는 심지어 APM, SIEM 같은 대부분 상용으로 구매해야 하는 도구들 까지 속해 있습니다. 다만 이 기능들의 <strong>소스코드, 배포에 대한 권리, 비즈니스를 할 권리 등은 Elastic 사에 귀속</strong>이 되어 있다는 점을 명심하시면 될 것 같습니다.</p><p>Elastic을 내재해서 APM, SIEM 같은 도구들을 만들어 서비스 하는 사업을 하시는 분들도 실제로 많이 계실텐데, Elastic 사도 그런 분들과 동등한 입장으로 (오픈소스) Elastic Stack 을 이용해서 APM, SIEM 등을 만들어 비즈니스를 하는 회사라고, Elastic 오픈소스 커뮤니티와 잠시 분리해서 생각 해 보시면 조금 이해가 되실 것 같습니다. 다만 Elastic 사는 자신들이 개발한 상용 기능 중 머신러닝을 이용한 이상징후 탐지 등은 유료로, APM, SIEM 등은 무료로 제공 하고 있는 것입니다.</p><p>저한테 Basic 을 사용해서 회사 시스템에서 쓰는 것은 문제가 없느냐는 질문을 주시는 분들도 자주 계십니다. 저 라이센스 전문을 읽어봐도 사실 Elastic 은 제품에 대한 소유권만 주로 명시하고 있지 어디까지 사용해도 된다고 속 시원하게 설명을 하고 있지 않습니다. 적절할지 모르겠으나, 얼마 전에 이 내용에 대해 이해를 도울 수 있을 것 같은 비유가 생각이 나서 한번 설명을 드려보겠습니다.</p><blockquote><p>COVID-19 때문에 동네 주민센터에서 주민들에게 무료로 손 소독제를 나누어 주기로 했습니다. 주민센터 앞에 손 소독제를 용기에 담아 쌓아놓고 필요하신 분들은 자유롭게 가져다 쓰세요 라고 팻말도 적어 놓았습니다.<br>그런데 어떤 사람이 주민센터에서 가져온 손 소독제를 다른 용기에 담아 돈을 받고 팔기 시작했습니다.<br>주민센터에서는 필요하신 분들은 사용을 하라고 배부를 한 것입니다. 다른 곳에 가져다 팔지 말라고 따로 명시는 하지 않았지만, 만약에 위 판매자의 행동이 적절하지 않다고 판단이 되는 경우 법적인 조치를 취할 수도 있을 것입니다.</p></blockquote><p>회사에서 APM, 로깅, 메트릭 등이 필요하면, Elastic 의 Basic 기능에서 제공되니 가져다 쓰시면 됩니다. 하지만 Elastic 에서 어떤 회사, 조직, 개인의 Basic 라이센스 기능의 사용이 Elastic 비즈니스에 부정적인 영향을 미친다고 판단이 되는 경우에는 어떤 조치를 취할 수도 있지 않을까 생각 해 봅니다.</p><h3 id="Elastic-커뮤니티-오픈-소스-에반젤리스트의-딜레마"><a href="#Elastic-커뮤니티-오픈-소스-에반젤리스트의-딜레마" class="headerlink" title="Elastic 커뮤니티 - 오픈 소스 에반젤리스트의 딜레마"></a>Elastic 커뮤니티 - 오픈 소스 에반젤리스트의 딜레마</h3><p><a href="https://www.facebook.com/groups/elasticsearch.kr">Elastic 한국 커뮤니티</a>는 제가 Elastic 에 입사하기 전에 시작한 곳입니다. 저는 이 커뮤니티가 Elastic 사의 소유물이라고 생각하지 않으며, 다른 Elastic 직원들에게도 이 점을 항상 명확히 전하고 있습니다.</p><p>물론 저는 Elastic 사로 부터 월급을 받는 사람이라, 커뮤니티에는 가능하면 Elastic 사의 비즈니스에 도움이 되는 이야기를 해야 할 의무가 있습니다. 하지만 공지글에도 명시 했듯이 (Elastic 기술과 관련된) 모든 질문, 답변, 홍보 등을 환영합니다. Elastic 의 상용 기능과 경쟁을 하는 제품의 홍보나 추천도 제가 막을 권리는 없습니다. (저희께 더 좋다 라고 슬쩍 어필을 할 수는 있겠지요 ㅎ)</p><p>제가 Elastic 에서 일을 처음 시작할 때만 해도 에반젤리스트, 디벨로퍼 애드버킷으로 일하시는 분들이 그리 많지 않았던 걸로 기억하지만 지금은 어느정도 이름 있는 테크, 서비스 기업들은 디벨로퍼 애드버킷에 준하는 일을 하시는 분들이 많이 계신 것 같습니다. 만약에 Elastic 이 오픈소스가 아닌 상용 제품이나 서비스 만을 취급하는 회사였다면 저는 좀 더 마음 편하게 일을 할 수 있었을 것 같습니다. 하지만 저에게는 Elastic 이 오픈 소스를 하기 때문에 갖는 딜레마가 있습니다.</p><p>제게는 Elastic 직원으로서 Elastic의 비즈니스 (Basic 이상)을 어필해야 하는 의무가 있다고 스스로 생각을 하고 있습니다. 하지만 또 한편으로는 이 역할은 영업, 솔루션 아키텍트 분들께 맡기고 저는 광범위한 Elastic 커뮤니티 - <em>이는 Elastic 오픈소스와 Elastic 의 상용 서비스의 경쟁 제품들을 사용하는 사용자 분들, 그리고 이런 제품들을 개발하고 서비스하는 회사들 까지 모두 포함한다고 생각합니다</em> - 를 위해 일해야 하는 의무도 가지고 있습니다. 그래서 제가 아는 지식과 정보를 최대한 제공 해 드리고자 하면서도 Elastic 비즈니스(상용 제품, 기술지원, 컨설팅, …)는 carnivalize 하지 않기 위해서 항상 신경이 쓰입니다.</p><p>제가 오픈 소스를 하는 회사의 에반젤리스트로서 가지는 딜레마는 이것입니다.</p><font color=red><strong>Elastic의 가장 큰 경쟁 제품은 A사도, S사도, D사도 아닌 Elastic 오픈 소스 입니다.</strong></font><p>저희 영업 분들이 Elastic 유료 기능, 기술 지원 등을 판매하려고 해도 오픈소스(그리고 Basic)이 워낙 잘 되어 있어서 팔기 힘들다는 것이 참 아이러니 합니다. 열심히 저희와 미팅 하면서 구매 검토 하시다가 오픈소스로 (기술 지원은 알아서) 하기로 결정했다는 통보를 받을 때면 마음이 착찹할 때도 많습니다.</p><p>대한민국은 Elastic 글로벌 사 입장에서 보면 매출이 1%도 되지 않는 작은 시장입니다. 하지만 <a href="https://www.elastic.co/kr">Elastic 홈페이지</a>가 전부는 아니지만 한글을 서비스하고 있고, (현재는 8개국어를 제공하지만 한국어가 5번째로 제공되기 시작한 언어였습니다. 제 개인적으로 가장 뿌듯하게 생각하는 부분입니다) <a href="https://www.elastic.co/kr/blog/nori-the-official-elasticsearch-plugin-for-korean-language-analysis">한글 형태소 분석기</a> 까지 본사에서 직접 개발 해서 관리 해 주고 있습니다. 그리고 대한민국에 6명의 기술지원 엔지니어분들이 계셔서 한국어로 기술지원 응대가 가능합니다. 앞으로도 한국에서 계속 Elastic의 비즈니스가 잘 되었으면 하는 바램입니다.</p><blockquote><p>한국 시장을 위해서만 한국 기술지원 엔지니어분들을 채용 한 것이 아니라 APEC 리전 기준으로 채용을 했는데, 한국에 워낙 뛰어난 엔지니어 분들이 많으셔서 많이 뽑혔습니다. 🤓</p></blockquote><p>두서 없이 적다 보니 꽤 긴 글이 되었네요. Elastic 제품과 커뮤니티를 사랑하는 한 사람으로서 Elastic 기술, 그리고 우리 Elastic 한국 커뮤니티가 계속해서 발전하기를 바라며 글을 마치겠습니다. 😘</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;정말 오랬만에 글을 씁니다. 반년 넘게 글을 쓰지 않았었네요. 그 동안 새로운 집으로 이사도 했고 여러가지 개인적인 행사에 신경 쓰느라 블로그 관리에 많이 소홀했던 것 같습니다. 오랬만에 그 동안 해 보고 싶었던 오픈소스 라이센스와 비즈니스에 대
      
    
    </summary>
    
      <category term="Engineering" scheme="http://kimjmin.net/categories/Engineering/"/>
    
    
      <category term="Elastic" scheme="http://kimjmin.net/tags/Elastic/"/>
    
      <category term="Engineering" scheme="http://kimjmin.net/tags/Engineering/"/>
    
      <category term="SW Engineer" scheme="http://kimjmin.net/tags/SW-Engineer/"/>
    
      <category term="오픈소스" scheme="http://kimjmin.net/tags/%EC%98%A4%ED%94%88%EC%86%8C%EC%8A%A4/"/>
    
      <category term="커뮤니티" scheme="http://kimjmin.net/tags/%EC%BB%A4%EB%AE%A4%EB%8B%88%ED%8B%B0/"/>
    
      <category term="DevRel" scheme="http://kimjmin.net/tags/DevRel/"/>
    
  </entry>
  
  <entry>
    <title>Elasticsearch 에서 한글 형태소 분석 잘 해보기</title>
    <link href="http://kimjmin.net/2019/08/2019-08-how-to-analyze-korean/"/>
    <id>http://kimjmin.net/2019/08/2019-08-how-to-analyze-korean/</id>
    <published>2019-08-28T11:06:16.000Z</published>
    <updated>2022-12-21T02:05:44.535Z</updated>
    
    <content type="html"><![CDATA[<p>요즘 서울시 지하철 대시보드를 다시 만들고 있습니다. 아래는 녹화한 첫 번째 영상입니다. 공공데이터로부터 추출, 색인, 매핑 및 템플릿 설정 부분까지 진행했는데 이번 블로그에서 다루는 내용들이 있습니다.</p><iframe width="560" height="315" src="https://www.youtube.com/embed/ypsEZXVYLo4" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe><p>색인된 정보들 중에 제일 자주 검색하는 것이 지하철 역명입니다. 물론 전부 keyword 로 해 놓고 정확한 역명만 선택되도록 해도 되지만, 그래도 검색엔진을 사용하는데 좀 더 편리하고 품질이 좋은 검색 기능을 만들어 봐야겠지요. 그렇게 하기 위해 오늘 이리 저리 했던 시행 착오들을 좀 공유 해 보려고 합니다.</p><p>공공데이터에서 받은 데이터에서 추출한 지하철역 명 종류는 대략 900여개 입니다. 그 중에 검색 때문에 애를 먹이는 종류가 있었으니 바로 지하철역 명에 <code>***입구</code> 라는 이름이 들어간 역 들입니다.</p><p>우선 테스트를 위해 <code>stations</code> 인덱스에 다음 10개 역명 문서를 입력하겠습니다.<br><figure class="highlight json"><table><tr><td class="code"><pre><span class="line">PUT stations/_bulk</span><br><span class="line">&#123;<span class="attr">&quot;index&quot;</span>: &#123;<span class="attr">&quot;_id&quot;</span>: <span class="string">&quot;1&quot;</span>&#125;&#125;</span><br><span class="line">&#123;<span class="attr">&quot;station&quot;</span>:<span class="string">&quot;홍대입구&quot;</span>&#125;</span><br><span class="line">&#123;<span class="attr">&quot;index&quot;</span>: &#123;<span class="attr">&quot;_id&quot;</span>: <span class="string">&quot;2&quot;</span>&#125;&#125;</span><br><span class="line">&#123;<span class="attr">&quot;station&quot;</span>:<span class="string">&quot;서울대입구&quot;</span>&#125;</span><br><span class="line">&#123;<span class="attr">&quot;index&quot;</span>: &#123;<span class="attr">&quot;_id&quot;</span>: <span class="string">&quot;3&quot;</span>&#125;&#125;</span><br><span class="line">&#123;<span class="attr">&quot;station&quot;</span>:<span class="string">&quot;총신대입구(이수)&quot;</span>&#125;</span><br><span class="line">&#123;<span class="attr">&quot;index&quot;</span>: &#123;<span class="attr">&quot;_id&quot;</span>: <span class="string">&quot;4&quot;</span>&#125;&#125;</span><br><span class="line">&#123;<span class="attr">&quot;station&quot;</span>:<span class="string">&quot;충정로(경기대입구)&quot;</span>&#125;</span><br><span class="line">&#123;<span class="attr">&quot;index&quot;</span>: &#123;<span class="attr">&quot;_id&quot;</span>: <span class="string">&quot;5&quot;</span>&#125;&#125;</span><br><span class="line">&#123;<span class="attr">&quot;station&quot;</span>:<span class="string">&quot;성신여대입구(돈암)&quot;</span>&#125;</span><br><span class="line">&#123;<span class="attr">&quot;index&quot;</span>: &#123;<span class="attr">&quot;_id&quot;</span>: <span class="string">&quot;6&quot;</span>&#125;&#125;</span><br><span class="line">&#123;<span class="attr">&quot;station&quot;</span>:<span class="string">&quot;숭실대입구(살피재)&quot;</span>&#125;</span><br><span class="line">&#123;<span class="attr">&quot;index&quot;</span>: &#123;<span class="attr">&quot;_id&quot;</span>: <span class="string">&quot;7&quot;</span>&#125;&#125;</span><br><span class="line">&#123;<span class="attr">&quot;station&quot;</span>:<span class="string">&quot;청량리(서울시립대입구)&quot;</span>&#125;</span><br><span class="line">&#123;<span class="attr">&quot;index&quot;</span>: &#123;<span class="attr">&quot;_id&quot;</span>: <span class="string">&quot;8&quot;</span>&#125;&#125;</span><br><span class="line">&#123;<span class="attr">&quot;station&quot;</span>:<span class="string">&quot;한성대입구&quot;</span>&#125;</span><br><span class="line">&#123;<span class="attr">&quot;index&quot;</span>: &#123;<span class="attr">&quot;_id&quot;</span>: <span class="string">&quot;9&quot;</span>&#125;&#125;</span><br><span class="line">&#123;<span class="attr">&quot;station&quot;</span>:<span class="string">&quot;숙대입구&quot;</span>&#125;</span><br><span class="line">&#123;<span class="attr">&quot;index&quot;</span>: &#123;<span class="attr">&quot;_id&quot;</span>: <span class="string">&quot;10&quot;</span>&#125;&#125;</span><br><span class="line">&#123;<span class="attr">&quot;station&quot;</span>:<span class="string">&quot;남한산성입구&quot;</span>&#125;</span><br></pre></td></tr></table></figure></p><p>아무런 매핑 설정을 안 해줬으니 당연히 제대로 검색이 될 리 없습니다. <code>홍대</code>, <code>서울대</code>,<code>입구</code> 같은 검색어로는 검색이 안 되고 <code>홍대입구</code>, <code>숭실대입구</code> 처럼 텀 전체를 정확히 넣어야 검색이 됩니다. 역명에 () 괄호가 같이 있는 역 들은 <code>돈암</code>, <code>이수</code> 같은 검색어로도 검색은 되었습니다.</p><p><img src="hongdae.png" alt=""> <img src="hongdaeipgu.png" alt=""> <img src="donam.png" alt=""></p><p>우선 아래와 같이 한글 형태소 분석기 nori_tokenizer 를 적용해서 멀티필드 <code>station.nori</code> 를 하나 추가하고 데이터를 다시 색인했습니다.<br><figure class="highlight json"><table><tr><td class="code"><pre><span class="line">PUT stations</span><br><span class="line">&#123;</span><br><span class="line">  <span class="attr">&quot;settings&quot;</span>: &#123;</span><br><span class="line">    <span class="attr">&quot;analysis&quot;</span>: &#123;</span><br><span class="line">      <span class="attr">&quot;analyzer&quot;</span>: &#123;</span><br><span class="line">        <span class="attr">&quot;nori&quot;</span>: &#123;</span><br><span class="line">          <span class="attr">&quot;tokenizer&quot;</span>: <span class="string">&quot;nori_tokenizer&quot;</span></span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="attr">&quot;mappings&quot;</span>: &#123;</span><br><span class="line">    <span class="attr">&quot;properties&quot;</span>: &#123;</span><br><span class="line">      <span class="attr">&quot;station&quot;</span>: &#123;</span><br><span class="line">        <span class="attr">&quot;type&quot;</span>: <span class="string">&quot;text&quot;</span>,</span><br><span class="line">        <span class="attr">&quot;fields&quot;</span>: &#123;</span><br><span class="line">          <span class="attr">&quot;nori&quot;</span>: &#123;</span><br><span class="line">            <span class="attr">&quot;type&quot;</span>: <span class="string">&quot;text&quot;</span>,</span><br><span class="line">            <span class="attr">&quot;analyzer&quot;</span>: <span class="string">&quot;nori&quot;</span></span><br><span class="line">          &#125;</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>이제 station.nori 필드로 검색을 하면 <code>홍대</code> 로도 검색이 됩니다.</p><p><img src="nori-hong.png" alt=""></p><p>자, 이제 새로운 문제에 직면하게 되는데요, <code>station.nori</code> 필드에서 <code>홍대입구</code> 로 검색을 하게 되면 다음과 같이 <code>입구</code>를 포함한 모든 역명이 나오게 됩니다.</p><p><img src="hong-all.png" alt=""></p><p>검색어로 넣은 <code>홍대입구</code> 가 <code>홍대</code>, <code>입구</code> 로 분리되서 각각의 텀을 찾은 것을 쉽게 짐작할 수가 있습니다. 그럼 색인은 nori_tokenizer 로 하고 검색은 standard 로 하면 해결되지 않을까요?<br>매핑에 <code>search_analyzer</code> 를 <code>standard</code> 로 넣어 수정하고 데이터를 다시 색인 해 보겠습니다.</p><figure class="highlight"><table><tr><td class="code"><pre><span class="line">...</span><br><span class="line">  &quot;mappings&quot;: &#123;</span><br><span class="line">    &quot;properties&quot;: &#123;</span><br><span class="line">      &quot;station&quot;: &#123;</span><br><span class="line">        &quot;type&quot;: &quot;text&quot;,</span><br><span class="line">        &quot;fields&quot;: &#123;</span><br><span class="line">          &quot;nori&quot;: &#123;</span><br><span class="line">            &quot;type&quot;: &quot;text&quot;,</span><br><span class="line">            &quot;analyzer&quot;: &quot;nori&quot;,</span><br><span class="line">            &quot;search_analyzer&quot;: &quot;standard&quot;</span><br><span class="line">          &#125;</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p>다시 <code>홍대입구</code>를 검색어로 넣고 검색 해 보았는데</p><p><img src="hong-stand.png" alt=""></p><p>어라… 데이터가 나오지 않습니다. <code>_termvectors</code> 를 이용해서 색인된 텀 확인 들어갑니다.</p><p><img src="hong-termvector.png" alt=""></p><p>역시 짐작대로 <code>홍대</code> 와 <code>입구</code> 만 있고 <code>홍대입구</code> 는 없습니다. elastic 홈페이지의 도큐먼트에서 <a href="https://www.elastic.co/guide/en/elasticsearch/plugins/current/analysis-nori-tokenizer.html">nori 형태소 분석기 문서</a>를 확인 해 봅니다.</p><p><img src="nori-doc.png" alt=""></p><p><code>decompound_mode</code> 설정의 디폴트 값이 <code>discard</code> 입니다. 그럼 이걸 <code>mixed</code> 로 바꾸면 <code>홍대입구</code> 텀이 저장되는지 해 봅니다. 인덱스 날리고, 매핑을 다시 만들고 데이터를 다시 색인 해 보겠습니다.</p><figure class="highlight"><table><tr><td class="code"><pre><span class="line">PUT stations</span><br><span class="line">&#123;</span><br><span class="line">  <span class="attr">&quot;settings&quot;</span>: &#123;</span><br><span class="line">    <span class="attr">&quot;analysis&quot;</span>: &#123;</span><br><span class="line">      <span class="attr">&quot;analyzer&quot;</span>: &#123;</span><br><span class="line">        <span class="attr">&quot;nori&quot;</span>: &#123;</span><br><span class="line">          <span class="attr">&quot;tokenizer&quot;</span>: <span class="string">&quot;my_nori_tokenizer&quot;</span></span><br><span class="line">        &#125;</span><br><span class="line">      &#125;,</span><br><span class="line">      <span class="attr">&quot;tokenizer&quot;</span>: &#123;</span><br><span class="line">        <span class="attr">&quot;my_nori_tokenizer&quot;</span>: &#123;</span><br><span class="line">          <span class="attr">&quot;type&quot;</span>: <span class="string">&quot;nori_tokenizer&quot;</span>,</span><br><span class="line">          <span class="attr">&quot;decompound_mode&quot;</span>: <span class="string">&quot;mixed&quot;</span></span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="attr">&quot;mappings&quot;</span>: &#123;</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p><img src="hong-stand.png" alt=""></p><p>여전히 안됩니다. <code>_termvectors</code>를 확인 해 보아도 아까랑 같습니다. 왜 그럴까요…?<br><code>decompound_mode</code> 옵션의 <code>none</code>, <code>discard</code>, <code>mixed</code> 값의 차이가 궁금해서 세개를 다 해 보기로 합니다. 매핑에 멀티필드로 <code>nori_none</code>, <code>nori_discard</code>, <code>nori_mixed</code> 를 만들어 보겠습니다.</p><figure class="highlight json"><table><tr><td class="code"><pre><span class="line">PUT stations</span><br><span class="line">&#123;</span><br><span class="line">  <span class="attr">&quot;settings&quot;</span>: &#123;</span><br><span class="line">    <span class="attr">&quot;analysis&quot;</span>: &#123;</span><br><span class="line">      <span class="attr">&quot;analyzer&quot;</span>: &#123;</span><br><span class="line">        <span class="attr">&quot;nori_none&quot;</span>: &#123;</span><br><span class="line">          <span class="attr">&quot;tokenizer&quot;</span>: <span class="string">&quot;nori_t_none&quot;</span></span><br><span class="line">        &#125;,</span><br><span class="line">        <span class="attr">&quot;nori_discard&quot;</span>: &#123;</span><br><span class="line">          <span class="attr">&quot;tokenizer&quot;</span>: <span class="string">&quot;nori_t_discard&quot;</span></span><br><span class="line">        &#125;,</span><br><span class="line">        <span class="attr">&quot;nori_mixed&quot;</span>: &#123;</span><br><span class="line">          <span class="attr">&quot;tokenizer&quot;</span>: <span class="string">&quot;nori_t_mixed&quot;</span></span><br><span class="line">        &#125;</span><br><span class="line">      &#125;,</span><br><span class="line">      <span class="attr">&quot;tokenizer&quot;</span>: &#123;</span><br><span class="line">        <span class="attr">&quot;nori_t_none&quot;</span>: &#123;</span><br><span class="line">          <span class="attr">&quot;type&quot;</span>: <span class="string">&quot;nori_tokenizer&quot;</span>,</span><br><span class="line">          <span class="attr">&quot;decompound_mode&quot;</span>: <span class="string">&quot;none&quot;</span></span><br><span class="line">        &#125;,</span><br><span class="line">        <span class="attr">&quot;nori_t_discard&quot;</span>: &#123;</span><br><span class="line">          <span class="attr">&quot;type&quot;</span>: <span class="string">&quot;nori_tokenizer&quot;</span>,</span><br><span class="line">          <span class="attr">&quot;decompound_mode&quot;</span>: <span class="string">&quot;discard&quot;</span></span><br><span class="line">        &#125;,</span><br><span class="line">        <span class="attr">&quot;nori_t_mixed&quot;</span>: &#123;</span><br><span class="line">          <span class="attr">&quot;type&quot;</span>: <span class="string">&quot;nori_tokenizer&quot;</span>,</span><br><span class="line">          <span class="attr">&quot;decompound_mode&quot;</span>: <span class="string">&quot;mixed&quot;</span></span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="attr">&quot;mappings&quot;</span>: &#123;</span><br><span class="line">    <span class="attr">&quot;properties&quot;</span>: &#123;</span><br><span class="line">      <span class="attr">&quot;station&quot;</span>: &#123;</span><br><span class="line">        <span class="attr">&quot;type&quot;</span>: <span class="string">&quot;text&quot;</span>,</span><br><span class="line">        <span class="attr">&quot;fields&quot;</span>: &#123;</span><br><span class="line">          <span class="attr">&quot;nori_none&quot;</span>: &#123;</span><br><span class="line">            <span class="attr">&quot;type&quot;</span>: <span class="string">&quot;text&quot;</span>,</span><br><span class="line">            <span class="attr">&quot;analyzer&quot;</span>: <span class="string">&quot;nori_none&quot;</span>,</span><br><span class="line">            <span class="attr">&quot;search_analyzer&quot;</span>: <span class="string">&quot;standard&quot;</span></span><br><span class="line">          &#125;,</span><br><span class="line">          <span class="attr">&quot;nori_discard&quot;</span>: &#123;</span><br><span class="line">            <span class="attr">&quot;type&quot;</span>: <span class="string">&quot;text&quot;</span>,</span><br><span class="line">            <span class="attr">&quot;analyzer&quot;</span>: <span class="string">&quot;nori_discard&quot;</span>,</span><br><span class="line">            <span class="attr">&quot;search_analyzer&quot;</span>: <span class="string">&quot;standard&quot;</span></span><br><span class="line">          &#125;,</span><br><span class="line">          <span class="attr">&quot;nori_mixed&quot;</span>: &#123;</span><br><span class="line">            <span class="attr">&quot;type&quot;</span>: <span class="string">&quot;text&quot;</span>,</span><br><span class="line">            <span class="attr">&quot;analyzer&quot;</span>: <span class="string">&quot;nori_mixed&quot;</span>,</span><br><span class="line">            <span class="attr">&quot;search_analyzer&quot;</span>: <span class="string">&quot;standard&quot;</span></span><br><span class="line">          &#125;</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>인덱스 삭제하고, 만들고, 데이터 다시 색인합니다. 다시 <code>_termvectors</code> 로 <code>nori_none</code>, <code>nori_discard</code>, <code>nori_mixed</code> 세개 필드를 확인 해봅니다.</p><p><img src="hongdae_t_1.png" alt=""> <img src="hongdae_t_2.png" alt=""> <img src="hongdae_t_3.png" alt=""></p><p>세개 모두 <code>홍대</code>, <code>입구</code> 두개의 텀만 있고 차이가 없습니다.😳</p><p>이때 뭔가 머리를 스치는 느낌이 왔습니다. 이번에는 홍대입구 말고 <code>서울대입구</code> 가 저장된 도큐먼트의 값을 확인 해 보았습니다.</p><p><img src="seoul_1.png" alt=""> <img src="seoul_2.png" alt=""> <img src="seoul_3.png" alt=""></p><p>느낌이 맞았습니다. <code>홍대입구</code>는 <code>홍대</code>와 <code>입구</code> 가 합쳐진 복합어로 치지 않고 두 텀을 모두 별개의 사전으로 간주합니다. <code>서울대</code> 의 경우 <code>서울</code> 과 <code>대</code> 가 합쳐진 복합어로 쳐서 <code>none</code>, <code>discard</code>, <code>mixed</code> 세 옵션에 따라 <code>서울대</code>, <code>서울</code>+<code>대</code>, <code>서울</code>+<code>대</code>+<code>서울대</code> 이렇게 다른 모습으로 텀들이 저장됩니다.</p><p>그럼 지금 우리의 과제인 <code>홍대</code>+<code>입구</code>+<code>홍대입구</code> 를 모두 저장하는 과제는 어떻게 해야 할까요.</p><p>Elasticsearch 의 토큰필터 중에 나란히 있는 두개의 텀을 같이 저장하는 토큰필터가 있는데 바로 <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-shingle-tokenfilter.html">shingle</a> 입니다. nGram 의 단어 버전이라고 보시면 됩니다. <code>nori_discard</code> 토크나이저에 <code>shingle</code> 토큰필터를 합쳐서 매핑을 다시 만들어봅니다. 다른 토크나이저는 지웠습니다.<br>인덱스 삭제하고, 매핑 수정하고, 데이터 입력하고… (벌써 몇번째인가요? 😩)</p><figure class="highlight json"><table><tr><td class="code"><pre><span class="line">PUT stations</span><br><span class="line">&#123;</span><br><span class="line">  <span class="attr">&quot;settings&quot;</span>: &#123;</span><br><span class="line">    <span class="attr">&quot;analysis&quot;</span>: &#123;</span><br><span class="line">      <span class="attr">&quot;analyzer&quot;</span>: &#123;</span><br><span class="line">        <span class="attr">&quot;nori_discard&quot;</span>: &#123;</span><br><span class="line">          <span class="attr">&quot;tokenizer&quot;</span>: <span class="string">&quot;nori_t_discard&quot;</span>,</span><br><span class="line">          <span class="attr">&quot;filter&quot;</span>: <span class="string">&quot;shingle&quot;</span></span><br><span class="line">        &#125;</span><br><span class="line">      &#125;,</span><br><span class="line">      <span class="attr">&quot;tokenizer&quot;</span>: &#123;</span><br><span class="line">        <span class="attr">&quot;nori_t_discard&quot;</span>: &#123;</span><br><span class="line">          <span class="attr">&quot;type&quot;</span>: <span class="string">&quot;nori_tokenizer&quot;</span>,</span><br><span class="line">          <span class="attr">&quot;decompound_mode&quot;</span>: <span class="string">&quot;discard&quot;</span></span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="attr">&quot;mappings&quot;</span>: &#123;</span><br><span class="line">    <span class="attr">&quot;properties&quot;</span>: &#123;</span><br><span class="line">      <span class="attr">&quot;station&quot;</span>: &#123;</span><br><span class="line">        <span class="attr">&quot;type&quot;</span>: <span class="string">&quot;text&quot;</span>,</span><br><span class="line">        <span class="attr">&quot;fields&quot;</span>: &#123;</span><br><span class="line">          <span class="attr">&quot;nori_discard&quot;</span>: &#123;</span><br><span class="line">            <span class="attr">&quot;type&quot;</span>: <span class="string">&quot;text&quot;</span>,</span><br><span class="line">            <span class="attr">&quot;analyzer&quot;</span>: <span class="string">&quot;nori_discard&quot;</span>,</span><br><span class="line">            <span class="attr">&quot;search_analyzer&quot;</span>: <span class="string">&quot;standard&quot;</span></span><br><span class="line">          &#125;</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>다시 <code>홍대입구</code>의 텀 들을 확인 해 봅니다.</p><p><img src="hong-shingle.png" alt=""></p><p><code>홍대</code>, <code>입구</code>, <code>홍대 입구</code> 텀이 보입니다! <code>홍대 입구</code> 텀이 가운데 공백이 들어가 있는데 이건 <code>token_separator</code> 옵션을 조정하면 해결하면 될 것 같습니다.</p><p>그리고 <code>서울대입구</code> 텀도 확인을 해 봅니다. 화면이 길어 캡쳐를 하지 않았는데, 나온 텀들은 다음과 같습니다.<br><code>대</code>, <code>대 입구</code>, <code>서울</code>, <code>서울 대</code>, <code>입구</code></p><p><code>서울대입구</code> 라는 텀을 만들려면 <code>max_shingle_size</code> 값을 3으로 해 주어야 할것 같습니다.</p><p>이제 다시 … </p><figure class="highlight"><table><tr><td class="code"><pre><span class="line">PUT stations</span><br><span class="line">&#123;</span><br><span class="line">  <span class="attr">&quot;settings&quot;</span>: &#123;</span><br><span class="line">    <span class="attr">&quot;analysis&quot;</span>: &#123;</span><br><span class="line">      <span class="attr">&quot;analyzer&quot;</span>: &#123;</span><br><span class="line">        <span class="attr">&quot;nori_discard&quot;</span>: &#123;</span><br><span class="line">          <span class="attr">&quot;tokenizer&quot;</span>: <span class="string">&quot;nori_t_discard&quot;</span>,</span><br><span class="line">          <span class="attr">&quot;filter&quot;</span>: <span class="string">&quot;my_shingle&quot;</span></span><br><span class="line">        &#125;</span><br><span class="line">      &#125;,</span><br><span class="line">      <span class="attr">&quot;tokenizer&quot;</span>: &#123;</span><br><span class="line">        <span class="attr">&quot;nori_t_discard&quot;</span>: &#123;</span><br><span class="line">          <span class="attr">&quot;type&quot;</span>: <span class="string">&quot;nori_tokenizer&quot;</span>,</span><br><span class="line">          <span class="attr">&quot;decompound_mode&quot;</span>: <span class="string">&quot;discard&quot;</span></span><br><span class="line">        &#125;</span><br><span class="line">      &#125;,</span><br><span class="line">      <span class="attr">&quot;filter&quot;</span>: &#123;</span><br><span class="line">        <span class="attr">&quot;my_shingle&quot;</span>: &#123;</span><br><span class="line">          <span class="attr">&quot;type&quot;</span>: <span class="string">&quot;shingle&quot;</span>,</span><br><span class="line">          <span class="attr">&quot;token_separator&quot;</span>: <span class="string">&quot;&quot;</span>,</span><br><span class="line">          <span class="attr">&quot;max_shingle_size&quot;</span>: <span class="number">3</span></span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="attr">&quot;mappings&quot;</span>: &#123;</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p>이제 <code>서울대입구</code> 텀을 확인하면</p><p><img src="seoul-full.png" alt=""></p><p>😭감격해서 눈물이 나네요. 이제 <code>홍대입구</code> 로 검색하면 <code>입구</code>는 제외되고 <code>홍대입구</code> 만 검색이 됩니다.</p><p><img src="hong_final_1.png" alt=""> <img src="hong_final_2.png" alt=""> <img src="hong_final_3.png" alt=""></p><p>Elaticsearch, 시작은 쉽지만 잘 하려면 많은 고민과 노력이 필요합니다.<br><strong>전문가가 필요하시면 저희 기술지원 구독을 문의하세요!! 😁</strong></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;요즘 서울시 지하철 대시보드를 다시 만들고 있습니다. 아래는 녹화한 첫 번째 영상입니다. 공공데이터로부터 추출, 색인, 매핑 및 템플릿 설정 부분까지 진행했는데 이번 블로그에서 다루는 내용들이 있습니다.&lt;/p&gt;
&lt;iframe width=&quot;560&quot;
      
    
    </summary>
    
      <category term="Engineering" scheme="http://kimjmin.net/categories/Engineering/"/>
    
    
      <category term="Elasticsearch" scheme="http://kimjmin.net/tags/Elasticsearch/"/>
    
      <category term="Elastic" scheme="http://kimjmin.net/tags/Elastic/"/>
    
      <category term="nori" scheme="http://kimjmin.net/tags/nori/"/>
    
      <category term="노리" scheme="http://kimjmin.net/tags/%EB%85%B8%EB%A6%AC/"/>
    
      <category term="한글 형태소 분석" scheme="http://kimjmin.net/tags/%ED%95%9C%EA%B8%80-%ED%98%95%ED%83%9C%EC%86%8C-%EB%B6%84%EC%84%9D/"/>
    
      <category term="서울시 지하철" scheme="http://kimjmin.net/tags/%EC%84%9C%EC%9A%B8%EC%8B%9C-%EC%A7%80%ED%95%98%EC%B2%A0/"/>
    
  </entry>
  
  <entry>
    <title>2019 파이콘 부스 운영 후기</title>
    <link href="http://kimjmin.net/2019/08/2019-08-pycon-seoul/"/>
    <id>http://kimjmin.net/2019/08/2019-08-pycon-seoul/</id>
    <published>2019-08-21T03:00:00.000Z</published>
    <updated>2022-12-21T02:05:44.535Z</updated>
    
    <content type="html"><![CDATA[<p>올해도 어김없이 파이콘 행사가 돌아왔습니다. <a href="/2017/08/2017-08-pycon-seoul">2017년</a>, <a href="/2016/08/pyconapac-seoul-2016">2016년</a> 행사 운영 후기는 남겼는데 작년에는 운영기를 남기지 않았었네요. 그러고보니 블로그도 참 오랬만에 쓰는 것 같습니다.</p><p>Elastic 은 올해로 4년째 파이콘 한국 스폰서로 참여하고 있습니다. 처음 참여할때는 저 혼자 부스 지키면서 점심 교대 할 사람도 없어 아침에 김밥 싸 와서 부스 뒤에 앉아 먹고 그랬는데, 올해는 저희 직원분들이 4분이나 같이 해 주셔서 개인적으로 많이 편했습니다. 그러고보니 Elastic 도 이제 전 세계적으로는 직원이 1,500명 한국은 15분이 계시네요. 제가 처음 입사했을땐 전 세계에 200명 이었는데, 회사가 잘 커서 뿌듯합니다. 🥰</p><p>부스는 제일 구석에 있었는데, 그래도 꽤나 많은 분들이 와 주셨습니다. 여느 때와 마찬가지로 와서 설문 남겨 주시는 분들께 양말, 티셔츠, 휴대폰 홀더 같은 SWAG 들을 다는 못 드리고 (수량이 부족해서) 50%의 꽝 확률로 추첨해서 드렸습니다. 그래도 첫 날 오후 3시 즘에 다 떨어졌네요.</p><p><img src="IMG_2084.jpg" alt=""> <img src="IMG_2085.jpg" alt=""> <img src="IMG_2089.jpg" alt=""></p><p>올해는 저희 행사 예산 중에 상품 구매 예산이 20만원 정도 나와서 3분 정도는 추첨을 통해 더 좋은 경품을 드릴 수 있게 되었습니다. 제가 행사 전날 일렉트로 마트 가서 사 온 경품으로 드릴 짐벌, 키보드, 이어폰 입니다.</p><p><img src="IMG_2120.jpg" alt=""></p><p>하지만 설문만 하신 분들 전부를 대상으로 경품을 추첨 하기는 좀 그랬습니다. 파이콘 하면 열정있는 개발자들이 많이 모이는 곳인데 뭔가 개발자 스러운 챌린지 이벤트를 좀 해야하지 않나 싶었습니다. Elastic Stack 은 여러 제품들로 되어 있지만 사실 파이썬으로 만들어 진 것은 큐레이터 정도 밖에 없어서 파이콘은 항상 참여하면서도 딱히 뭔가 괜찮은 이벤트를 할 아이디어가 그 동안 없었는데, 올해는 아이디어를 좀 내서 Elastic APM 을 수집하는 챌린지를 해 보았습니다. 그래서 챌린지에 참석하신 분들 대상으로 좀 좋은 경품들을 드리기로 했습니다. </p><p>저희 Elastic 커뮤니티 회원들이 사용 하는 클라우드 서버가 있는데, 여기 파이콘 이벤트를 위한 스페이스와 전용 계정을 만들고 안내 페이지를 후다닥 구글 도큐먼트로 만들었습니다.</p><p><a href="https://docs.google.com/document/d/1QU7fui79WinkxLrl7goXNTRXveR6ozlqTOORp3-X218"><img src="apm-event.png" alt=""></a></p><p>본인이 개발 중인 토이 프로젝트 등에 몇 줄의 코드만 삽입하면 저희가 준비한 클라우드 서버로 APM 데이터가 수집되는 이벤트 입니다. Elastic APM 에서 지원되는 장고, 플래스크, node.js, 레일즈, 스프링 등을 사용 해 보신 분이라면 30분 정도면 금방 할 수 있는 수준이라고 저는 생각했습니다. 업계 유명 연예인이신 <a href="https://blog.outsider.ne.kr/">변정훈</a> 님이 node.js 로 제일 처음 참여 해 주셨고, 다른 분들도 한분 한분 참여 해 주셨는데, 둘째날 마감 30분 전에 부스 앞에 노트북 들고 앉아서 완성 해 주신 분도 계셨습니다. 일단 수집에 성공하면 클라우드 서버에 바로 목록이 나타나기 때문에 저희가 실시간으로 확인이 가능합니다.</p><p><img src="apm-screen.png" alt=""> <img src="apm-screen-2.png" alt=""></p><p>추첨에 참여할 조건은 </p><ol><li>APM 데이터가 성공적으로 클라우드에 수집됨</li><li>일반 설문에 답변하고 설문 마지막 문항에 애플리케이션명 정확히 기입</li></ol><p>이었습니다. 한 20분 정도만 지원하면 딱 좋겠다 했는데, 정확히 17분이 지원 해 주셨고 그 중 5분은 설문 미참여 내지는 불확실로 추첨 제외 해서 최종적으로 12분이서 1,2,3등 상품을 두고 사다리 타기를 했습니다. 아래는 사다리 추첨 때의 영상과 사진들입니다.</p><iframe src="https://www.facebook.com/plugins/video.php?href=https%3A%2F%2Fwww.facebook.com%2Fkimjmin81%2Fvideos%2F2992519370789422%2F&show_text=0&width=560" width="560" height="315" style="border:none;overflow:hidden" scrolling="no" frameborder="0" allowTransparency="true" allowFullScreen="true"></iframe><p><img src="Image from iOS-2.jpg" alt=""> <img src="Image from iOS-6.jpg" alt=""> </p><p>챌린지 이벤트 진행이 제가 생각했던 모양대로 어느정도 잘 흘러가서 참 좋았습니다. 이벤트 안내 페이지와 클라우드 서버는 파이콘이 끝난 이후에도 계속 유지하고 있습니다. Elastic APM 테스트 해 보실 분들은 지금도 들어가셔서 해 보실 수 있습니다. <a href="https://docs.google.com/document/d/1QU7fui79WinkxLrl7goXNTRXveR6ozlqTOORp3-X218">이벤트 안내 페이지는 여기 있습니다.</a></p><p>일반 SWAG 추첨이랑 챌린지 이벤트 외에도 올해는 Elastic Cloud 30일 무료 제공 코드도 가지고 왔습니다. 그냥 가입하면 원래는 14일간 무료입니다. 스탠딩 배너에 저희 <a href="https://www.facebook.com/groups/elasticsearch.kr/">한국 Elastic 페이스북 커뮤니티</a>로 링크되는 QR 코드까지 포함하면 이번 부스에는 가지고 온 QR 코드 링크만 4개였네요</p><p><img src="IMG_2113.jpg" alt=""></p><p>올해는 부스에서 도와 주신 분들이 많아 맘 편하게 세션도 몇개 들을 수 있었습니다. 파이콘 대가이시면서 저희 서포트 엔지니어이신 조인석님의 발표도 들었습니다.</p><p><img src="IMG_2099.jpg" alt=""></p><p>올해도 여느때와 마찬가지로 즐거운 시간이었습니다. 매년 점점 더 발전하면서도 처음의 즐거움과 열정을 잃지 않는 파이썬 커뮤니티, 앞으로도 계속 번창하길 바라겠습니다. 내년에는 Gold 보다 더 높은 스폰서로 참여할 수 있도록 노력 해 보겠습니다.</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;올해도 어김없이 파이콘 행사가 돌아왔습니다. &lt;a href=&quot;/2017/08/2017-08-pycon-seoul&quot;&gt;2017년&lt;/a&gt;, &lt;a href=&quot;/2016/08/pyconapac-seoul-2016&quot;&gt;2016년&lt;/a&gt; 행사 운영 후기는 남겼
      
    
    </summary>
    
      <category term="ICT" scheme="http://kimjmin.net/categories/ICT/"/>
    
    
      <category term="PyCon" scheme="http://kimjmin.net/tags/PyCon/"/>
    
      <category term="Conference" scheme="http://kimjmin.net/tags/Conference/"/>
    
      <category term="파이콘" scheme="http://kimjmin.net/tags/%ED%8C%8C%EC%9D%B4%EC%BD%98/"/>
    
  </entry>
  
  <entry>
    <title>Elastic Stack 7.0 출시 밎 지금까지의 변경들</title>
    <link href="http://kimjmin.net/2019/04/2019-04-elastic-stack-7-release/"/>
    <id>http://kimjmin.net/2019/04/2019-04-elastic-stack-7-release/</id>
    <published>2019-04-09T15:00:00.000Z</published>
    <updated>2022-12-21T02:05:44.525Z</updated>
    
    <content type="html"><![CDATA[<p>Elasticsearch, Kibana, Logstash, Beats 제품들을 개발하는 Elastic 사 에서는 이 4 제품들을 통틀어 Elastic Stack 이라고 부릅니다. 예전에 Beats가 생기기 전에는 ELK(Elsaticsearch, Logstash, Kibana) 스택으로 더 잘 알려져 있었는데 이 스토리는 <a href="https://www.elastic.co/kr/elk-stack">https://www.elastic.co/kr/elk-stack</a> 페이지에서 좀 더 자세히 보실 수 있습니다.</p><p>Elastic Stack은 5.0 부터 ELKB 제품들의 버전을 모두 일치시키면서 같이 릴리즈를 해 왔습니다. 여느 소프트웨어 제품들과 마찬가지로 Elastic Stack 제품들은 <code>6.5.1</code> 처럼 <code>메이져.마이너.버그픽스</code> 규칙으로 넘버링이 됩니다. 하지만 보통 Elastic Stack은 <font color='blue'><strong>마이너 버전</strong>에서 기능들이 추가</font> 되고 <font color='red'><strong>메이져 버전</strong>은 색인, 검색 성능 및 안전성의 개선에 초점을 맞추기 때문에 <strong>오히려 기능들이 Deprecated 되거나 Expire</strong></font> 되는 경우가 많습니다. 메이져 업데이트의 경우 Elasticsearch 가 핵심으로 사용하는 Lucene(루씬) 의 메이져 버전과 같이 업데이트 하기 때문에 데이터의 저장 방식이 바뀐다거나 탐색 또는 스코어링 공식이 바뀌는 등의 구조적인 변화가 있기 때문에 이전 버전에서 지원하던 기능이 더 이상 지원되지 않는 경우가 많기 때문입니다.</p><p>5.0 버전 부터 그 동안 있었던 주요 변경점을 간단히 살펴보면 다음과 같습니다. 상세한 내용은 공식 홈페이지의 릴리스 블로그 포스트 (<a href="https://www.elastic.co/blog/category/releases">https://www.elastic.co/blog/category/releases</a>) 에서 모두 살펴보실 수 있습니다.</p><h3 id="Elastic-Stack-5-x"><a href="#Elastic-Stack-5-x" class="headerlink" title="Elastic Stack 5.x"></a>Elastic Stack 5.x</h3><blockquote><p>5.x 에서는 상용 플러그인인 X-Pack 출시와 Elastic Cloud Enterprise 같은 로드맵의 발표, 그리고 BKD-Tree를 이용한 IP, Geo 등의 연산 및 색인속도의 상승이 가장 주요한 포인트였습니다.</p></blockquote><p><img src="5-improve.png" alt=""></p><ul><li><a href="https://www.elastic.co/blog/elastic-stack-5-0-0-released">5.0</a> : X-Pack, Ingest Node, Painless 스크립트 언어, BKD-Tree 를 이용한 half-float 등의 구조 추가및 성능 향상, 기본 검색으로 BM25 사용, 운영 환경에서 부트스트랩 체크, Kibana 전반적인 디자인 변경 등</li><li><a href="https://www.elastic.co/blog/elastic-stack-5-3-0-released">5.3</a> : Cross Cluster Search, Logstash의 Persistent Queues (디스크 큐), Logstash 모니터링, Beats 모듈</li><li><a href="https://www.elastic.co/blog/elastic-stack-5-4-0-released">5.4</a> : <a href="https://www.elastic.co/kr/blog/introducing-machine-learning-for-the-elastic-stack">머신러닝</a> 기능 추가</li><li><a href="https://www.elastic.co/blog/elastic-stack-5-6-0-released">5.6</a> : 6.0 으로 롤링 업그레이드를 위한 마이그레이션 도구 지원</li></ul><h3 id="Elastic-Stack-6-x"><a href="#Elastic-Stack-6-x" class="headerlink" title="Elastic Stack 6.x"></a>Elastic Stack 6.x</h3><blockquote><p>6.x 은 sparse fields 를 개선한 디스크 저장 효율 개선, Sequence ID를 이용한 리커버리 효율 개선, 5.6 에서 6.x 으로 중단 없는 롤링 업그레이드가 가장 주요한 포인트였습니다. 그리고 시스템 안정성 때문에 Type 을 하나로 제한하는 변화가 있었습니다. 그 외에도 점차 스택에서 솔루션 으로 제품 컨셉이 변경이 되면서 각 마이너 버전 마다 추가되는 기능들이 많았습니다.</p></blockquote><p><img src="6-improve.png" alt=""></p><ul><li><a href="https://www.elastic.co/blog/elastic-stack-6-0-0-released">6.0</a> : <strong><font color='red'>type을 한개만 쓸 수 있도록 제한</font></strong>, 롤링 업그레이드, Sequence ID를 이용한 리커버리, sparse fields, Kibana 디자인이 색약자들을 고려한 형태로 변경 등</li><li><a href="https://www.elastic.co/blog/elastic-stack-6-1-0-released">6.1</a> : APM 출시</li><li><a href="https://www.elastic.co/blog/elastic-stack-6-2-0-released">6.2</a> : SAML 연동 인증, Beats 모니터링, Kibana의 Vega 시각화 도구 추가</li><li><a href="https://www.elastic.co/blog/elastic-stack-6-3-0-released">6.3</a> : <strong><a href="https://www.elastic.co/kr/blog/doubling-down-on-open">X-Pack 소스코드 공개 및 통합</a></strong>, 기본 배포판에 X-Pack Basic 탑재. 기본 배포 방식이 바뀌어서 6.2 –&gt; 6.3 으로 업그레이드 하는 것이 오히려 5.6 –&gt; 6.0 으로 업그레이드 하는 것 보다 복잡해졌습니다. 이에 따른 설정 방법은 <a href="/2018/08/2018-08-install-security-over-es63">관련 블로그 포스트</a> 에서도 설명했습니다. 그 외에도 Elasticsearch SQL, Rollups 기능이 추가되었습니다.</li><li><a href="https://www.elastic.co/blog/elastic-stack-6-4-0-released">6.4</a> : 다른 기능은 큰 변경이 없지만, 저희 입장에서는 <a href="https://www.elastic.co/kr/blog/nori-the-official-elasticsearch-plugin-for-korean-language-analysis">한국어 형태소 분석기인 노리</a>가 출시되었기 때문에 큰 의미가 있는 릴리즈였습니다.</li><li><a href="https://www.elastic.co/blog/elastic-stack-6-5-0-released">6.5</a> : 클러스터간 복제(Cross Cluster Replication), Kibana에 Infra, Log 앱 추가, Beats 중앙 관리 기능 등</li><li><a href="https://www.elastic.co/blog/elastic-stack-6-6-0-released">6.6</a> : 프로즌 인덱스, Kibana에서 Index Lifecicle 관리 UI 등. 그리고 정식 릴리즈에 포함된 것은 아니지만 이 시기에 <a href="/2019/01/2019-01-korea-region-map">Elastic Map Service 에 한국 시군구 지도를 추가</a>했습니다.</li><li><a href="https://www.elastic.co/blog/elastic-stack-6-7-0-released">6.7</a> : Kibana 지도 확장 기능 및 Uptime 앱 추가.</li></ul><h3 id="Elastic-Stack-7-0"><a href="#Elastic-Stack-7-0" class="headerlink" title="Elastic Stack 7.0"></a><a href="https://www.elastic.co/blog/elastic-stack-7-0-0-released">Elastic Stack 7.0</a></h3><p><img src="illustration-7point0-launch-01.svg" alt="https://www.elastic.co/blog/elastic-stack-7-0-0-released"></p><p>그리고 2019년 4월 7.0 버전이 출시되었습니다. 7.0 출시는 이전의 5.0, 6.0 출시 때와 비교하면 얌전하게 출시된 것 같습니다. 예전에는 메이져 버전이 출시되면 홈페이지도 전면적으로 개편하고 라이브 출시 행사도 하고 했었는데 말이죠. 7.0 에서 반영된 주요 기능들은 다음과 같습니다. 여느 메이져 버전때와 마찬가지로 7.0 에서는 기본적인 성능과 안전성 향상에 주요 포인트를 두고 있습니다.</p><h5 id="Elasticsearch"><a href="#Elasticsearch" class="headerlink" title="Elasticsearch"></a><a href="https://www.elastic.co/blog/elasticsearch-7-0-0-released">Elasticsearch</a></h5><ul><li>Java High-Level Rest Client의 모든 기능이 완성되었습니다. 8.0 에서 transport client 는 제거됩니다.</li><li>Adaptive Replica Selection 이 7.0 부터 디폴트로 됩니다. Replica가 여러개가 있으면 더 빠른 Replica를 능동적으로 찾아 쿼리합니다.</li><li>Cross Cluster Search를 할 때 여러 원격 클러스터를 통합 검색할 때 라운드 트립을 최소화 시켜 성능을 향상시킵니다.</li><li>Refresh Interval이 기존에는 리프레시 간격이 기본 1초로 설정되어 실시간에 가까운 검색 기능을 제공했지만 이제는 (디폴트로 30초 동안) 검색 요청이 없으면 search idle 샤드로 설정하고 다음 검색 요청이 올 때 까지 리프레시가 잠정적으로 중지됩니다. 이는 색인 성능을 비약적으로 상승시킵니다.</li><li>Minimun Master Node 설정이 자동화됩니다. 이제 Quorum (Split Brain) 때문에 마스터 후보 노드의 1/2+1 값을 따로 설정하지 않아도 마스터 후보 노드가 추가되거나 제거될 때 이 값이 자동으로 설정됩니다.</li><li>Faster Top-k Retrieval 가 적용되어 단순한 쿼리를 할때 전체 도큐먼트가 아닌 top 10,000개의 도큐먼트에서 쿼리를 합니다. total hit 수와 스코어가 변하지만 쿼리 결과는 그대로이며 쿼리 속도가 약 10배 가량 향상됩니다. 정확한 hit 수를 가져와야 하는 쿼리나 aggregation 에는 적용되지 않습니다.</li><li>디폴트 Primary Shard 개수가 5 에서 1로 변경됩니다.</li><li>Open JDK 가 기본 번들로 포함되어 Java를 따로 설치하지 않아도 실행이 됩니다.</li></ul><blockquote><p>기본 설정 중에서 특히 중요한 디스커버리 설정하는 부분이 바뀌었습니다. 예전처럼 <code>discovery.zen.ping.unicast.hosts:</code> 로 하시면 안되고요 <code>discovery.seed_hosts:</code> 로 설정 해야 합니다. <code>discovery.zen.minimum_master_nodes</code> 설정도 없어지고 대신 <code>cluster.initial_master_nodes:</code> 설정에 있는 master eligible nodes 들 중에서 과반수 이상이 살아있어야 클러스터가 정상적으로 실행되도록 변경되었습니다. 자세한 내용은 도큐먼트를 참고하세요.<br><a href="https://www.elastic.co/guide/en/elasticsearch/reference/7.0/discovery-settings.html">https://www.elastic.co/guide/en/elasticsearch/reference/7.0/discovery-settings.html</a></p></blockquote><h5 id="Kibana"><a href="#Kibana" class="headerlink" title="Kibana"></a><a href="https://www.elastic.co/blog/kibana-7-0-0-released">Kibana</a></h5><p><img src="dark-mode.gif" alt=""></p><ul><li>Kibana에 새로운 UI 템플릿 셋트가 적용되었습니다.</li><li>다크 테마가 추가되었습니다.</li><li>대시보드에 반응형 디자인을 적용해서 되어 모바일 화면에서도 잘 보입니다.</li><li>Kibana Query Language가 디폴트로 적용됩니다.</li><li>확장된 Time Picker가 적용됩니다.</li></ul><h5 id="API-변경"><a href="#API-변경" class="headerlink" title="API 변경"></a>API 변경</h5><p>사실 추가된 기능들 보다 신경 쓰이는 것들은 기존에 쓰던 방식의 API의 변경 입니다. API 변경에 대한 토론은 <a href="https://github.com/elastic/elasticsearch/issues/15613#issuecomment-239435920">해당 깃헙 이슈</a> 에서 보실 수 있습니다.</p><p>7.0을 사용하려면 클라이언트 프로그램들을 새로운 API에 맞게 수정 해야 하기 때문에 업그레이드가 망설여 지는 것이 사실입니다. 특히 6.0 때 부터 예고되었던 <strong><font color='red'>Type 구조의 삭제</font></strong> 때문에 걱정 하시는 분들이 많을 것입니다. 알아보기 편하게 7.0 버전에서 변경된 API들을 아래 정리 해 보았습니다.</p><table><thead><tr><th>Search API</th><th>6.x 이전</th><th>7.0 이후</th></tr></thead><tbody><tr><td>search</td><td>/{index}/{type}/_search</td><td>{index}/_search</td></tr><tr><td>msearch</td><td>/{index}/{type}/_msearch</td><td>/{index}/_msearch</td></tr><tr><td>count</td><td>/{index}/{type}/_count</td><td>/{index}/_count</td></tr><tr><td>explain</td><td>/{index}/{type}/{id}/_explain</td><td>/{index}/_explain/{id}</td></tr><tr><td>search template</td><td>/{index}/{type}/_search/template</td><td>/{index}/_search/template</td></tr><tr><td>msearch template</td><td>/{index}/{type}/_msearch/template</td><td>/{index}/_msearch/template</td></tr></tbody></table><table><thead><tr><th>Document API</th><th>6.x 이전</th><th>7.0 이후</th></tr></thead><tbody><tr><td>index</td><td>/{index}/{type}/{id}</td><td>/{index}/<strong>_doc</strong>/{id}</td></tr><tr><td>delete</td><td>/{index}/{type}/{id}</td><td>/{index}/<strong>_doc</strong>/{id}</td></tr><tr><td>get</td><td>/{index}/{type}/{id}</td><td>/{index}/<strong>_doc</strong>/{id}</td></tr><tr><td>update</td><td>/{index}/{type}/{id}/_update</td><td>/{index}/_update/{id}</td></tr><tr><td>get source</td><td>/{index}/{type}/{id}/_source</td><td>/{index}/_source/{id}</td></tr><tr><td>bulk</td><td>/{index}/{type}/_bulk</td><td>/{index}/_bulk</td></tr><tr><td>mget</td><td>/{index}/{type}/_mget</td><td>/{index}/_mget</td></tr><tr><td>termvectors</td><td>/{index}/{type}/{id}/_termvector</td><td>/{index}/_termvector/{id}</td></tr><tr><td>mtermvectors</td><td>/{index}/{type}/_mtermvectors</td><td>/{index}/_mtermvectors</td></tr></tbody></table><table><thead><tr><th>Index API</th><th>6.x 이전</th><th>7.0 이후</th></tr></thead><tbody><tr><td>create index</td><td>/{index}</td><td>변경 없음</td></tr><tr><td>get mapping</td><td>/{index}/_mapping/{type}</td><td>/{index}/_mapping</td></tr><tr><td>put mapping</td><td>/{index}/_mapping/{type}</td><td>/{index}/_mapping</td></tr><tr><td>get field mapping</td><td>/{index}/{type}/_mapping/field/{fields}</td><td>/{index}/_mapping/field/{fields}</td></tr><tr><td>get template</td><td>/_template/{template}</td><td>변경 없음</td></tr><tr><td>put template</td><td>/_template/{template}</td><td>변경 없음</td></tr></tbody></table><p>Search 그리고 Index API 에서는 type 을 입력하던 부분을 모두 생략한다고 생각 하면 될 것 같습니다. Document API 에서를 type 이 들어가던 부분을 지정자 <strong><code>_doc</code></strong> 로 대치하면 대부분 적용됩니다. 그리고 제가 테스트 해 본 결과 Document API 의 경우에는 7.0 에서 <code>/&#123;index&#125;/_update/&#123;id&#125;</code> 대신 <code>/&#123;index&#125;/_doc/&#123;id&#125;/_update</code>  처럼 과거 형식으로 입력을 해도 정상적으로 실행이 됩니다.</p><h4 id="5-x-에서-업그레이드를-할-때-다중-type을-단일-type으로"><a href="#5-x-에서-업그레이드를-할-때-다중-type을-단일-type으로" class="headerlink" title="5.x 에서 업그레이드를 할 때 다중 type을 단일 type으로"></a>5.x 에서 업그레이드를 할 때 다중 type을 단일 type으로</h4><p>사실 큰 문제는 5.x 이전 버전에서 한 인덱스에 여러 타입을 사용중인 경우 일 것입니다. 이 경우는 어쩔 수 없이 Logstash 또는 _reindex API를 이용해서 구조를 변경해서 새로 재 색인을 해야 합니다. 이에 관한 가이드는 <a href="https://www.elastic.co/guide/en/elasticsearch/reference/6.0/removal-of-types.html">Removal of mapping types</a> 페이지에서 볼 수 있습니다. 좀 더 팁을 드리자면 다음과 같은 경우들로 구분해서 생각을 해 볼 수 있을 것 같습니다.</p><h5 id="type-별로-다른-매핑을-사용하는-경우"><a href="#type-별로-다른-매핑을-사용하는-경우" class="headerlink" title="type 별로 다른 매핑을 사용하는 경우"></a>type 별로 다른 매핑을 사용하는 경우</h5><p>이때는 각각의 type 별로 새로운 인덱스를 만들어야 할 것입니다. 예를 들어 아래와 같이 my_index 라는 인덱스에 type_1, type_2 두개의 타입이 있는 경우<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">PUT my_index</span><br><span class="line">&#123;</span><br><span class="line">  &quot;mappings&quot;: &#123;</span><br><span class="line">    &quot;type_1&quot;: &#123;</span><br><span class="line">      &quot;properties&quot;: &#123;</span><br><span class="line">        ...</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;,</span><br><span class="line">    &quot;type_2&quot;: &#123;</span><br><span class="line">      &quot;properties&quot;: &#123;</span><br><span class="line">        ...</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>아래 처럼 타입 별로 인덱스를 나누어 생성하고 데이터를 재색인 해야 합니다.<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">PUT my_index-type_1</span><br><span class="line">&#123; ... &#125;</span><br><span class="line"></span><br><span class="line">PUT my_index-type_2</span><br><span class="line">&#123; ... &#125;</span><br></pre></td></tr></table></figure></p><p>Logstash 의 입/출력 설정은 다음과 같이 설정해서 재색인이 가능합니다.<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">input &#123;</span><br><span class="line">  elasticsearch &#123;</span><br><span class="line">    hosts &#x3D;&gt; &quot;http:&#x2F;&#x2F;10.0.0.1:9200&quot;</span><br><span class="line">    index &#x3D;&gt; &quot;my_index&quot;</span><br><span class="line">    query &#x3D;&gt; &#39;&#123; &quot;query&quot;: &#123; &quot;query_string&quot;: &#123; &quot;query&quot;: &quot;*&quot; &#125; &#125; &#125;&#39;</span><br><span class="line">    size &#x3D;&gt; 500</span><br><span class="line">    scroll &#x3D;&gt; &quot;5m&quot;</span><br><span class="line">    docinfo &#x3D;&gt; true</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">output &#123;</span><br><span class="line">  elasticsearch &#123;</span><br><span class="line">    hosts &#x3D;&gt; [&quot;http:&#x2F;&#x2F;10.0.0.2:9200&quot;]</span><br><span class="line">    index &#x3D;&gt; &quot;my_index-%&#123;[@metadata][_type]&#125;&quot;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h5 id="type-별로-매핑이-거의-유사한-경우-단-동일한-도큐먼트-ID가-존재하지-않아야-함"><a href="#type-별로-매핑이-거의-유사한-경우-단-동일한-도큐먼트-ID가-존재하지-않아야-함" class="headerlink" title="type 별로 매핑이 거의 유사한 경우. 단 동일한 도큐먼트 ID가 존재하지 않아야 함."></a>type 별로 매핑이 거의 유사한 경우. 단 동일한 도큐먼트 ID가 존재하지 않아야 함.</h5><p>이 경우는 한 인덱스로 매핑을 병합해서 사용할 수 있을 것입니다. 다음과 같이 my_index 안에 type_1 에는 name, age, gender, 그리고 type_2 에는 name, age, address 라는 필드가 생성되어 있다고 가정을 하면<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">PUT my_index</span><br><span class="line">&#123;</span><br><span class="line">  &quot;mappings&quot;: &#123;</span><br><span class="line">    &quot;type_1&quot;: &#123;</span><br><span class="line">      &quot;properties&quot;: &#123;</span><br><span class="line">        &quot;name&quot;: ...</span><br><span class="line">        &quot;age&quot;: ...</span><br><span class="line">        &quot;gender&quot;: ...</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;,</span><br><span class="line">    &quot;type_2&quot;: &#123;</span><br><span class="line">      &quot;properties&quot;: &#123;</span><br><span class="line">        &quot;name&quot;: ...</span><br><span class="line">        &quot;age&quot;: ...</span><br><span class="line">        &quot;address&quot;: ...</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>아래와 같이 하나의 my_index 인덱스에 name, age, gender, address 를 병합하는 매핑을 생성합니다.<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">PUT my_index</span><br><span class="line">&#123;</span><br><span class="line">  &quot;mappings&quot;: &#123;</span><br><span class="line">    &quot;properties&quot;: &#123;</span><br><span class="line">      &quot;name&quot;: ...</span><br><span class="line">      &quot;age&quot;: ...</span><br><span class="line">      &quot;gender&quot;: ...</span><br><span class="line">      &quot;address&quot;: ...</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>만약에 type의 이름이 유의미한 값이었다면 type의 이름도 하나의 필드 값으로 만들어 저장하면 됩니다.<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">PUT my_index</span><br><span class="line">&#123;</span><br><span class="line">  &quot;mappings&quot;: &#123;</span><br><span class="line">    &quot;properties&quot;: &#123;</span><br><span class="line">      &quot;type&quot;: ... # &lt;-- &quot;type_1&quot; 또는 &quot;type_2&quot; 입력</span><br><span class="line">      &quot;name&quot;: ...</span><br><span class="line">      &quot;age&quot;: ...</span><br><span class="line">      &quot;gender&quot;: ...</span><br><span class="line">      &quot;address&quot;: ...</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>이 경우 Logstash 의 입/출력 설정은 다음과 같이 해서 재색인이 가능합니다.<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">input &#123;</span><br><span class="line">  elasticsearch &#123;</span><br><span class="line">    hosts &#x3D;&gt; &quot;http:&#x2F;&#x2F;10.0.0.1:9200&quot;</span><br><span class="line">    index &#x3D;&gt; &quot;my_index&quot;</span><br><span class="line">    query &#x3D;&gt; &#39;&#123; &quot;query&quot;: &#123; &quot;query_string&quot;: &#123; &quot;query&quot;: &quot;*&quot; &#125; &#125; &#125;&#39;</span><br><span class="line">    size &#x3D;&gt; 500</span><br><span class="line">    scroll &#x3D;&gt; &quot;5m&quot;</span><br><span class="line">    docinfo &#x3D;&gt; true</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">filter &#123;</span><br><span class="line">   mutate &#123;</span><br><span class="line">    add_field &#x3D;&gt; &#123; &quot;type&quot; &#x3D;&gt; &quot;%&#123;[@metadata][_type]&#125;&quot; &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">output &#123;</span><br><span class="line">  elasticsearch &#123;</span><br><span class="line">    hosts &#x3D;&gt; [&quot;http:&#x2F;&#x2F;10.0.0.2:9200&quot;]</span><br><span class="line">    index &#x3D;&gt; &quot;my_index&quot;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><h5 id="한-인덱스-안에-parent-child-구조로-type-이-나뉘어-있는-경우"><a href="#한-인덱스-안에-parent-child-구조로-type-이-나뉘어-있는-경우" class="headerlink" title="한 인덱스 안에 parent - child 구조로 type 이 나뉘어 있는 경우"></a>한 인덱스 안에 parent - child 구조로 type 이 나뉘어 있는 경우</h5><p>이 경우와 관련해서는 예전의 <a href="/2018/01/2018-01-parent-child-to-join">Elasticsearch 6.x 에서의 join 사용</a> 포스트를 참고하시기 바랍니다.</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;Elasticsearch, Kibana, Logstash, Beats 제품들을 개발하는 Elastic 사 에서는 이 4 제품들을 통틀어 Elastic Stack 이라고 부릅니다. 예전에 Beats가 생기기 전에는 ELK(Elsaticsearch,
      
    
    </summary>
    
      <category term="Engineering" scheme="http://kimjmin.net/categories/Engineering/"/>
    
    
      <category term="Elasticsearch" scheme="http://kimjmin.net/tags/Elasticsearch/"/>
    
      <category term="Elastic" scheme="http://kimjmin.net/tags/Elastic/"/>
    
      <category term="Engineering" scheme="http://kimjmin.net/tags/Engineering/"/>
    
      <category term="Kibana" scheme="http://kimjmin.net/tags/Kibana/"/>
    
      <category term="Release" scheme="http://kimjmin.net/tags/Release/"/>
    
  </entry>
  
  <entry>
    <title>Elastic Maps Service 한국지도 추가</title>
    <link href="http://kimjmin.net/2019/01/2019-01-korea-region-map/"/>
    <id>http://kimjmin.net/2019/01/2019-01-korea-region-map/</id>
    <published>2019-01-27T15:00:00.000Z</published>
    <updated>2022-12-21T02:05:44.525Z</updated>
    
    <content type="html"><![CDATA[<h2 id="Elasticsearch-Kibana-에서-위치정보-사용"><a href="#Elasticsearch-Kibana-에서-위치정보-사용" class="headerlink" title="Elasticsearch, Kibana 에서 위치정보 사용"></a>Elasticsearch, Kibana 에서 위치정보 사용</h2><p>Elasticsearch 에서는 숫자, 텍스트 뿐 아니라 지역 정보를 가지고 활용이 가능합니다. logstash 또는 ingest pipeline 의 geopoint 필터를 사용하면 IP 주소로부터 다음과 같은 정보들의 추출이 가능합니다.</p><p><img src="region-info.png" alt=""></p><p>Elasticsearch에 지역정보를 저장하고 Kibana 에서 지역 정보를 표시하는 방법은 아래의 2가지가 있습니다.</p><p><img src="kibana-vis.png" alt=""></p><p>Coordinate Map은 위의 <code>geoip.location</code> 필드의 <code>&#123; &quot;lon&quot;:113.25, &quot;lat&quot;:23.1167 &#125;</code> 같은 geo_point 형식으로 저장된 데이터를 이용해서 지도에 표시하는 방법입니다. 이 정보를 기반으로 아래와 같이 지도에 원이나 사각형, 열지도(heatmap) 형식으로 수치를 표현하는 것이 가능합니다.</p><p><img src="coordinate-map.png" alt=""></p><p>Region Map은 geo_point 가 아니라 term aggregation 을 이용해서 위의 <code>geoip.country_code</code> 필드의  <code>KR</code>,<code>US</code>,<code>CN</code> 같은 keyword 형식으로 저장된 필드의 값을 가지고 미리 정의된 geo-hash 영역에 대입하여 지도에 나타내는 방식입니다.</p><p><img src="region-map.png" alt=""></p><p>Region Map에서 지원되는 지도들은 Options 의 Vector Map 항목에서 선택이 가능합니다.</p><p><img src="select-region.png" alt=""></p><p>지원되는 지도와 데이터의 종류들은 Elastic Maps Service - <a href="https://maps.elastic.co">https://maps.elastic.co</a> 페이지에서 확인이 가능합니다.</p><p><img src="ems-world.png" alt=""></p><h2 id="Elastic-Maps-Service-에-한국-지도-추가"><a href="#Elastic-Maps-Service-에-한국-지도-추가" class="headerlink" title="Elastic Maps Service 에 한국 지도 추가"></a>Elastic Maps Service 에 한국 지도 추가</h2><p>기쁘게도 이번에 Elastic Maps Service에 한국 지도 2종이 추가되었습니다.</p><ul><li>South Korea Provinces</li><li>South Korea Municipalities</li></ul><p>기본적으로 Elastic Maps Service 는 위키데이터에 있는 정보들을 활용합니다. South Korea Provinces 의 경우에는 <a href="https://www.wikidata.org/wiki/Q884">wikidata</a>에 있는 정보를 활용해서 8개의 도와 서울특별시, 광역시 정도 들을 표시합니다.</p><p><img src="ems-sk-province.png" alt=""></p><p>South Korea Municipalities 지도는 시,군,구 단위까지의 정보를 표시할 수 있습니다. 이 정보는 wikidata에 없기 때문에 <a href="https://github.com/e9t">박은정</a> 님으로부터 처음 작성된 <a href="https://github.com/southkorea/southkorea-maps">South Korea Maps</a> 의 정보를 활용하고 있습니다. 이 중 <a href="http://kostat.go.kr">통계청 통계지리정보서비스 - KOSTAT</a> 자료 부분을 활용합니다. </p><p><img src="ems-south-korea.png" alt=""></p><p>데이터의 출처와 라이센스는 Elastic Maps Service 의 깃헙<br><a href="https://github.com/elastic/ems-file-service/blob/master/sources/kr/README.md">https://github.com/elastic/ems-file-service/blob/master/sources/kr/README.md</a><br>에 명시하고 있습니다.</p><p>맵 geo.json 파일은 South Korea Maps 에 있는 실제 맵 보다 사이즈를 줄이기 위해 2차 가공을 다시 한번 했습니다. KOGL 라이센스는 2차 가공을 허용한다고 명시하고 있어서 문제는 없다고 판단했습니다. Merge 기록은 아래에 있습니다.<br><a href="https://github.com/elastic/ems-file-service/pull/74">https://github.com/elastic/ems-file-service/pull/74</a></p><p>이제 Kibana의 Region Map에서 대한민국 시군구 지도를 활용할 수 있습니다. options 에서 vector map을 South Korea Municipalities 으로 선택하고 분석할 keyword 값과 일치하는 join field를 선택하면 됩니다.<br><img src="kibana-south-korea.png" alt=""></p><p>시군구 명칭과 코드들은 <a href="https://maps.elastic.co/#file/south_korea_municipalities">https://maps.elastic.co/#file/south_korea_municipalities</a> 에서 확인할 수 있으며 <a href="https://sgis.kostat.go.kr/contents/shortcut/shortcut_05_01.jsp">통계청 통계지리정보서비스</a> 에서 코드표를 다운로드 할 수 있습니다. <strong>알림마당 &gt; 자료신청 &gt; 자료 다운로드</strong> 페이지 하단에 <strong>코드표 및 이용설명서</strong> 를 다운로드 하시면 됩니다.</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;Elasticsearch-Kibana-에서-위치정보-사용&quot;&gt;&lt;a href=&quot;#Elasticsearch-Kibana-에서-위치정보-사용&quot; class=&quot;headerlink&quot; title=&quot;Elasticsearch, Kibana 에서 위치정보 
      
    
    </summary>
    
      <category term="Engineering" scheme="http://kimjmin.net/categories/Engineering/"/>
    
    
      <category term="Elasticsearch" scheme="http://kimjmin.net/tags/Elasticsearch/"/>
    
      <category term="Elastic" scheme="http://kimjmin.net/tags/Elastic/"/>
    
      <category term="Elastic Maps Service" scheme="http://kimjmin.net/tags/Elastic-Maps-Service/"/>
    
  </entry>
  
  <entry>
    <title>Elastic Stack 롤링 업그레이드</title>
    <link href="http://kimjmin.net/2018/12/2018-12-elasticsearch-rolling-upgrade/"/>
    <id>http://kimjmin.net/2018/12/2018-12-elasticsearch-rolling-upgrade/</id>
    <published>2018-12-07T15:00:00.000Z</published>
    <updated>2022-12-21T02:05:44.522Z</updated>
    
    <content type="html"><![CDATA[<p>얼마 전에 Elastic Stack 6.5 가 출시되었습니다. 6.5 는 마이너 업그레이드이지만 상당히 방대한 새로운 기능들을 포함하고 있습니다. 6.5 에 대한 자세한 내용들은 출시 블로그에서 확인 바랍니다.<br><a href="https://www.elastic.co/kr/blog/elastic-stack-6-5-0-released">https://www.elastic.co/kr/blog/elastic-stack-6-5-0-released</a></p><p><a href="/2018/01/2018-01-build-es-cluster-8">Elastic Cluster 구성</a> 시리즈에서 구성했던 시스템을 계속해서 제 여러 용도의 데모 서버로 사용하고 있습니다. 6.1 버전으로 처음 설치 한 이후에 가끔 한번씩 새 버전이 나올 때 마다 업그레이드를 해 주고 있었습니다. 이번에도 6.5.0 이 나오면서 이전에는 6.4.2 버전이었 던 것ㅇ르 새로 나온 6.5.1로 업그레이드를 하였습니다. 롤링 업그레이드 하는 전체 과정은 동영상으로 기록했습니다. 포스트 맨 하단에 있습니다.</p><p>데모 서버의 컨셉 아키텍쳐는 아래와 같습니다. 총 4개의 서버에 각각 Elasticsearch 노드를 하나씩 설치했습니다. 3개는 데이터 노드로 사용하고 하나는 Coordinate Only 노드로 설정해서 서비스 노드로 사용합니다. 서비스 노드가 있는 서버에는 Kibana, Logstash, Filebeat, Metricbeat 그리고 테스트를 위해 PHP, MySQL, Apache 웹 서버를 깔고 워드프레스 블로그를 설치 해 놓았습니다.</p><p><img src="es-architecture.png" alt=""></p><p>Elastic Stack 업그레이드에 대한 내용은 아래의 공식 도큐먼트에 자세히 나와 있습니다.<br><a href="https://www.elastic.co/guide/en/elastic-stack/current/upgrading-elastic-stack.html">https://www.elastic.co/guide/en/elastic-stack/current/upgrading-elastic-stack.html</a></p><p>Elasticsearch 를 업그레이드 하는 방법은 크게 다음의 3가지로 구분할 수 있습니다</p><p><strong><a href="#Full-Cluster-Restart">1. Full Cluster Restart</a></strong><br><strong><a href="#Cluster-Re-Index">2. Cluster Re-Index</a></strong><br><strong><a href="#Rolling-Upgrade">3. Rolling Upgrade</a></strong></p><p><img src="upgrade-support.png" alt=""></p><p>2.x –&gt; 5.x 와 같이 메이져 버전으로 업그레이드를 할 때는 롤링 업그레이드가 불가능합니다. 따라서 전체 클러스터를 재시작해야 합니다. 마이너 버전 끼리만 롤링 업그레이드가 가능하나 예외적으로 5.x 의 마지막 마이너 버전인 5.6.x 에서는 6.x 버전으로 롤링 업그레이드가 가능합니다.</p><p>다음은 각각 업그레이드에 대한 설명입니다.</p><blockquote><p>참고로 Elasticsearch 는 3, 4 메이져 버전이 없고 1.x &gt; 2.x &gt; 5.x &gt; 6.x 순서로 릴리즈가 되었습니다.</p></blockquote><h4 id="Full-Cluster-Restart"><a href="#Full-Cluster-Restart" class="headerlink" title="Full Cluster Restart"></a>Full Cluster Restart</h4><p>Full Cluster Restart 는 말 그대로 모든 노드를 내렸다가 프로그램을 업그레이드 하고 다시 전체 재시작을 하는 방법입니다. 클러스터 전체 재시작이 일어나기 때문에 필연적으로 운영 시스템의 가동 중단 시간이 발생하게 됩니다.</p><p>데이터가 저장 된 경로의 내용들을 그대로 두고 elasticsearch를 새로 설치 한 다음에 <code>path.data</code> 경로만 기존 데이터 경로로 설정하여 재실행 하면 업그레이드가 끝납니다. Unix 의 경우는 path.data 경로의 심볼릭 링크를 변경해서 업그레이드 하기도 합니다.</p><p><img src="full-cluster-restart.gif" alt=""></p><p>다만 Full Cluster Restart 라도 <strong><font color=red>2개 단계 이상의 상위 메이져 버전으로는 업그레이드가 불가능합니다</font></strong>. 2.x 버전의 운영 클러스터를 6.x 버전의 운영 클러스터로 업그레이드는 할 수 없습니다. 2개 단계 이상으로 업그레이드를 하기 위해서는 <strong><a href="#Cluster-Re-Index">Cluster Re-Index</a></strong>를 해야 합니다.<br>그리고 2.x 에서 생성한 인덱스를 5.x 으로 full cluster restart 해서 유지한 경우에도 이 인덱스는 6.x 으로 업그레이드가 불가능합니다. 5.x 클러스터에서 사용중이지만 2.x 에서 생성된 인덱스는 5.x 클러스터 안에서 다시 재색인을 해 주어야 합니다.</p><p>이전 버전에서 찍은 스냅샷을 새 버전에서 restore 해서 업그레이드 하는 방법도 있습니다. 이 경우에도 색인이 끝난 샤드의 세그먼트 파일을 그대로 복사하는 것이기 때문에 내부적으로는 <code>path.data</code> 를 유지한 Full Cluster Restart 방식과 유사하게 동작합니다. 따라서 2개 단계 이상의 버전에서는 스냅샷을 불러올 수 없습니다.</p><p><img src="snapshot-restore.png" alt=""></p><p><strong>Full Cluster Restart 의 특징을 요약하면 다음과 같습니다.</strong></p><blockquote></blockquote><ul><li>운영 시스템의 가동 중단 시간이 필연적으로 발생합니다.</li><li>2 단계 이상 버전으로 업그레이드가 불가능합니다.</li><li>스냅샷 &amp; 복원 과정도 Full Cluster Restart 와 동일합니다.</li></ul><h4 id="Cluster-Re-Index"><a href="#Cluster-Re-Index" class="headerlink" title="Cluster Re-Index"></a>Cluster Re-Index</h4><p>2.x 에서 6.x 와 같이 두개의 메이져 버전을 업그레이드 하는 경우에는 클러스터를 새로 구성하고 이전 클러스터에서 새로운 클러스터로 데이터를 재색인 하는 방법을 사용할 수 있습니다. 미리 클러스터를 이중화 시켜서 데이터의 재색인이 끝난 뒤 클라이언트 프로그램이 새로운 Elasticsearch 클러스터를 바라보도록 변경하기만 하면 가동 중단 시간도 거의 없이 안전한 업그레이드가 가능합니다. 마치 <a href="https://martinfowler.com/bliki/BlueGreenDeployment.html">Blue Green 배포</a> 기법과 유사한 개념입니다.</p><p>인덱스에 <code>_source</code> 를 저장하지 않도록 설정했으면 그 인덱스는 재색인이 불가능 합니다. 이 경우 원본 데이터에서 다시 가져오는 방법을 써야 합니다.</p><p>이전 클러스터의 데이터를 새로운 클러스터로 재색인 하기 위해서는 <code>Logstash</code> 를 사용하거나 <code>_reindex API</code>를 활용합니다.</p><p><img src="reindex.png" alt=""></p><p>Logstash 를 사용하는 방법은 다음과 같습니다.<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">input &#123;</span><br><span class="line">  elasticsearch &#123;</span><br><span class="line">    hosts &#x3D;&gt; &quot;es.production.mysite.org&quot;</span><br><span class="line">    index &#x3D;&gt; &quot;mydata-2018.09.*&quot;</span><br><span class="line">    query &#x3D;&gt; &#39;&#123; &quot;query&quot;: &#123; &quot;query_string&quot;: &#123; &quot;query&quot;: &quot;*&quot; &#125; &#125; &#125;&#39;</span><br><span class="line">    size &#x3D;&gt; 500</span><br><span class="line">    scroll &#x3D;&gt; &quot;5m&quot;</span><br><span class="line">    docinfo &#x3D;&gt; true</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line">output &#123;</span><br><span class="line">  elasticsearch &#123;</span><br><span class="line">    index &#x3D;&gt; &quot;copy-of-production.%&#123;[@metadata][_index]&#125;&quot;</span><br><span class="line">    document_type &#x3D;&gt; &quot;%&#123;[@metadata][_type]&#125;&quot;</span><br><span class="line">    document_id &#x3D;&gt; &quot;%&#123;[@metadata][_id]&#125;&quot;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br><code>docinfo =&gt; true</code> 설정을 하면 document_id 까지 모두 동일하게 재색인을 합니다. 더 자세한 설명은 <a href="https://www.elastic.co/guide/en/logstash/current/plugins-inputs-elasticsearch.html">Logstahs Elasticsearch input 도큐먼트</a>를 참고하세요.</p><p>원격 클러스터간 _reindex API 를 사용하는 방법은 다음과 같습니다.<br><figure class="highlight json"><table><tr><td class="code"><pre><span class="line">POST _reindex</span><br><span class="line">&#123;</span><br><span class="line">  <span class="attr">&quot;source&quot;</span>: &#123;</span><br><span class="line">    <span class="attr">&quot;remote&quot;</span>: &#123;</span><br><span class="line">      <span class="attr">&quot;host&quot;</span>: <span class="string">&quot;http://otherhost:9200&quot;</span>,</span><br><span class="line">      <span class="attr">&quot;username&quot;</span>: <span class="string">&quot;user&quot;</span>,</span><br><span class="line">      <span class="attr">&quot;password&quot;</span>: <span class="string">&quot;pass&quot;</span></span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="attr">&quot;index&quot;</span>: <span class="string">&quot;source&quot;</span>,</span><br><span class="line">    <span class="attr">&quot;query&quot;</span>: &#123;</span><br><span class="line">      <span class="attr">&quot;match&quot;</span>: &#123;</span><br><span class="line">        <span class="attr">&quot;test&quot;</span>: <span class="string">&quot;data&quot;</span></span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="attr">&quot;dest&quot;</span>: &#123;</span><br><span class="line">    <span class="attr">&quot;index&quot;</span>: <span class="string">&quot;dest&quot;</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>원격 클러스터간 _reindex API를 사용하려면 <code>elasticsearch.yml</code> 파일에 다음과 같이 <code>reindex.remote.whitelist</code> 설정이 되어 있어야 합니다.<br><figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">reindex.remote.whitelist: &quot;otherhost:9200, another:9200, 127.0.10.*:9200, localhost:*&quot;</span><br></pre></td></tr></table></figure><br>_reindex 는 기본적으로 <code>document_id</code>, <code>@timestamp</code> 값 까지 동일하게 복사합니다. 더 자세한 설명은 <a href="https://www.elastic.co/guide/en/elasticsearch/reference/6.5/docs-reindex.html">_reindex API 도큐먼트</a>를 참고하세요.</p><p><strong>Cluster Re-Index 의 특징을 요약하면 다음과 같습니다.</strong></p><blockquote></blockquote><ul><li>2 단계 이상의 버전으로 바로 업그레이드가 가능합니다.</li><li>클러스터를 별도로 준비를 위한 비용이 발생합니다.</li><li>데이터를 새로 색인해야 하기 때문에 다른 업그레이드 방법들과 비교하여 시간이 오래 걸립니다.</li><li>비교적 안정적으로 가동 중단 시간 없이 업그레이드가 가능합니다.</li><li>_source 가 저장되어 있지 않으면 사용이 불가능합니다.</li></ul><h4 id="Rolling-Upgrade"><a href="#Rolling-Upgrade" class="headerlink" title="Rolling Upgrade"></a>Rolling Upgrade</h4><p>6.4.1 -&gt; 6.5.1 같이 마이너 버전 간에는 롤링 업그레이드가 가능합니다. 롤링 업그레이드는 클러스터에 있는 노드들을 하나씩 내리고 업그레이드 한 뒤 다시 시작해서 업그레이드를 하는 방식입니다. Elasticsearch 노드들 간에는 메이져 버전이 같으면 마이너 버전이 달라도 클러스터 구성이 가능하기 때문에 사용 가능한 방법입니다. 클러스터 전체를 재시작 하지 않기 때문에 가동 중단 시간이 발생하지 않습니다.</p><p><img src="rolling-upgrade.gif" alt=""></p><p>원래는 마이너 버전 간의 업그레이드이지만, 5.6.x 부터는 메이져 버전의 가장 마지막 버전은 그 다음 메이져 버전으로도 롤링 업그레이드가 가능합니다. 메이져 버전 간에 롤링 업그레이드를 할때는 Kibana에 제공되는 마이그레이션 도구를 이용해서 이전 버전의 인덱스들이 새 버전으로 마이그레이션 했을 때 문제 없이 실행이 되는지를 체크하는 것이 안전합니다. </p><p>6.2 이전의 버전과 6.3 이후 버전의 경우에도 배포판의 방식에 큰 변경이 있었기 때문에 별도 가이드 도구가 제공됩니다. 아래 사이트에서 확인이 가능합니다.<br><a href="https://www.elastic.co/products/upgrade_guide">https://www.elastic.co/products/upgrade_guide</a><br><img src="upgrade-tool.png" alt=""></p><p>그리고 예전에 작성된 <a href="/2018/08/2018-08-install-security-over-es63">Elastic 6.3 에서 상용 라이센스 활성</a> 블로그포스트도 참고하시기 바랍니다.</p><p>롤링 업그레이드는 다음 순서로 진행됩니다. 자세한 내용은 <a href="https://www.elastic.co/guide/en/elasticsearch/reference/6.5/rolling-upgrades.html">Rolling upgrades 도큐먼트</a> 에 있습니다.</p><ol><li>Shard Allocation 중지 : 노드를 중단했을때 샤드들이 재배치 되지 않도록 다음 명령을 실행합니다.<figure class="highlight json"><table><tr><td class="code"><pre><span class="line">PUT _cluster/settings</span><br><span class="line">&#123;</span><br><span class="line">  <span class="attr">&quot;persistent&quot;</span>: &#123;</span><br><span class="line">    <span class="attr">&quot;cluster.routing.allocation.enable&quot;</span>: <span class="string">&quot;none&quot;</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li>Sync Flus 실행 : Primary - Replica 샤드들 간의 세그먼트 저장 상태를 동기화 시켜줍니다.<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">POST _flush&#x2F;synced</span><br></pre></td></tr></table></figure></li><li>노드 1개 중단 : 중단하고 나면 클러스터 상태가 <strong><font color="orange">Yellow</font></strong> 로 됩니다.</li><li>중단한 노드 업그레이드 : 이 때 설치된 플러그인들도 모두 제거하고 새 버전에 맞게 새로 설치 해 줘야 합니다.</li><li>중단한 노드 재시작 : 아래 명령으로 노드가 정상적으로 실행됬는지 확인합니다.<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">GET _cat&#x2F;nodes</span><br></pre></td></tr></table></figure></li><li>Shard Allocation 재가동 : unassigned 된 샤들이 새 노드에 다시 배치되도록 다음 명령을 실행합니다.<figure class="highlight json"><table><tr><td class="code"><pre><span class="line">PUT _cluster/settings</span><br><span class="line">&#123;</span><br><span class="line">  <span class="attr">&quot;persistent&quot;</span>: &#123;</span><br><span class="line">    <span class="attr">&quot;cluster.routing.allocation.enable&quot;</span>: <span class="literal">null</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li>클러스터 상태가 <strong><font color="green">Green</font></strong> 이 될 때 까지 기다립니다. 클러스터 상태는 아래 명령으로 확인이 가능합니다.<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">GET _cat&#x2F;health</span><br></pre></td></tr></table></figure></li><li>클러스터 상태가 <strong><font color="green">Green</font></strong>이 되고 나면 다시 1번 과정 부터 모든 노드들에 돌아가면서 실행을 반복합니다.</li></ol><p><strong>Rolling Upgrade 의 특징을 요약하면 다음과 같습니다.</strong></p><blockquote></blockquote><ul><li>마이너 버전 혹은 메이져의 마지막 버전에서만 사용이 가능합니다.</li><li>운영 시스템의 가동 중단 시간이 발생하지 않습니다.</li><li>절차가 복잡하여 플러그인 미설치, 샤드 중단 미실행 등의 실수나 unassign 중 새로 색인된 샤드 등이 있을 때 대규모의 샤드 재배치가 발생하여 시스템에 부하가 발생 할 위험이 있습니다.</li></ul><p>아래는 처음 언급한 데모 환경을 6.4.2 에서 6.5.1로 업그레이드 하는 과정을 녹화 한 영상입니다. 앞의 롤링 업그레이드 과정을 실행하고 있으니 참고 해 보시기 바랍니다.</p><iframe width="560" height="315" src="https://www.youtube.com/embed/fkwMt_K0nRQ" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;얼마 전에 Elastic Stack 6.5 가 출시되었습니다. 6.5 는 마이너 업그레이드이지만 상당히 방대한 새로운 기능들을 포함하고 있습니다. 6.5 에 대한 자세한 내용들은 출시 블로그에서 확인 바랍니다.&lt;br&gt;&lt;a href=&quot;https:/
      
    
    </summary>
    
      <category term="Engineering" scheme="http://kimjmin.net/categories/Engineering/"/>
    
    
      <category term="Elasticsearch" scheme="http://kimjmin.net/tags/Elasticsearch/"/>
    
      <category term="Elastic" scheme="http://kimjmin.net/tags/Elastic/"/>
    
      <category term="Elastic Cluster Settings" scheme="http://kimjmin.net/tags/Elastic-Cluster-Settings/"/>
    
  </entry>
  
  <entry>
    <title>나는 과연 SW Engineer 라고 말할 수 있을까?</title>
    <link href="http://kimjmin.net/2018/10/2018-10-am-i-engineer/"/>
    <id>http://kimjmin.net/2018/10/2018-10-am-i-engineer/</id>
    <published>2018-10-25T15:00:00.000Z</published>
    <updated>2022-12-21T02:05:44.521Z</updated>
    
    <content type="html"><![CDATA[<p>저는 지금 Elastic 에서 Community Engineer 라는 직책을 맡고 있습니다. 주 업무는 개발자 커뮤니티를 지원하는 일이고요 (물론 저는 한국에 직원이 많이 없어서 이런 저런 다른 일들도 많이 합니다), 다른 회사에서는 Evangelist 혹은 Developer Advocate 이라는 직함을 쓰는 곳도 있습니다. 저도 예전에는 전도사(Evangelist) 직함을 사용했었습니다.</p><p>소속되어 있는 팀도 지금은 Community Team 이라고 하는데, 예전 팀 이름은 DevRel Team 이었습니다. 처음 입사할 때는 7명이었는데 사람들이 나가고, 들어오고, 팀 옮기고 하면서 지금은 17명 입니다. 아직은 기업에서 Community 팀을 따로 운영하지 않는 곳이 많이 있지만, Elastic은 Community 팀을 매우 중요하게 생각합니다. 홈페이지의 엔지니어 팀 소개에도 당당히 3번째로 올라와 있습니다.</p><p><img src="engineering-page.png" alt=""></p><p>새 입사 직원들의 입문 교육때도 Community Team 에 대해 이야기하는 시간이 별도로 있고, 가끔은 저희 팀 미팅에 CEO(창업자)인 Shay Banon이 참여하기도 합니다. 그리고 제 매니저인 저희 팀 리더도 대부분의 리더쉽 미팅에 참여하며 꽤 중요한 발언들을 하곤 합니다.</p><p>Community Team은 팀원들의 역할이 제각각이라 Job을 정의하는것이 쉽지가 않습니다. 팀 안에 크게는 Advocate 과 Program 팀이 나누어져 있는데, Program 팀은 일반 Community 들을 찾아내고, 필요한 도움을 주는것이 주 역할이고, Advocate 은 컨퍼런스나 밋업에서 저희 기술 소개를 하는것이 주 역할입니다. 저는 Advocate 이지만 한국에서는 Program도 같이 겸하고 있습니다.</p><p><img src="community-team.jpg" alt=""></p><p>Community Team 은 저희 회사에서 Distributed 비중이 가장 큰 팀입니다. 17명의 직원이 14개 국가, 11개의 서로 다른 타임존에서 근무를 하고 있습니다. 각자 지역에서 하는 일도 다르고, 역할의 비중도 다르다보니, 저희는 회사가 정한 업무나 매니저와 함께 일을 정하는 것이 아니라, 대부분은 개개인이 스스로 일을 찾아서 하는 편입니다. 간혹 program 팀원이 어느 지역에 어떤 컨퍼런스나 밋업이 있다고 찾아서 알려주고 가서 발표좀 해 달라고 요청이 오기도 하지만, 보통은 각자 로컬에 있는 이벤트들을 직접 찾아서 발표 신청하고, 발표 준비해서 발표하고, 끝나면 이벤트 정리하는 일들을 각각 하는 편입니다.</p><p>이런 식으로 일을 하다 보니 팀원들마다 하는 일의 분량도, 비중도 다릅니다. 특히 저 같은 경우 한국에 직원이 많지 않다보니 서포트, 교육, 기술영업도 많이 하는데, 저희 유럽에 있는 팀원들의 경우는 1년에 컨퍼런스, 밋업에서 발표하는 횟수가 50번 정도 됩니다. 그래서 저희는 개인 평가 지표라는 것이 없습니다. 각자가 하는 일의 비중이 다르다보니 비교가 쉽지 않습니다. 좋은 점은 자유도가 높아 목표달성에 대한 스트레스가 적고, 나쁘게 말하면 승진이나 연봉 상승의 기회가 적습니다.</p><p>팀원들마다 각자 고충이 있겠지만 저희 팀원들이 하는 가장 큰 고민은 오늘 제목처럼</p><blockquote><p>나는 과연 SW Engineer 라고 말할 수 있을까?</p></blockquote><p>하는 것입니다.</p><p>저희 advocate 팀원들도 대부분 이전 직장에서는 프로그램 개발 일을 하던 사람들이었습니다. 커뮤니티 활동이 좋아서 지금은 커뮤니티 활동이 직업이 되었지만, 마음 한 구석에는 *<em>그래도 엔지니어인데, 이렇게 개발에 손을 놔도 되나…</em> 하는 생각들을 대부분 하고 있습니다. 저 역시 마찬가지이고요.</p><p>지금은 저도 컴퓨터로 하는 생산 작업들은</p><ul><li>발표/교육 자료 만들기</li><li>데모 만들기</li><li>블로그 쓰기</li></ul><p>정도입니다. 물론 써 놓은 블로그들과, 데모를 만들면서 작업한 설정파일 등이 나중에 서포트나 컨설팅, 교육 등을 할때 정말 유용하게 쓰이고는 있습니다. 그래도 마지막으로 뭔가 돌아가는 프로그램을 만들어본건 언제인지 기억도 잘 안 나네요.</p><p>하지만 Elastic 에서 만큼은 Community Engineer 라는 업무에 자부심을 가지고 일할 수 있을것 같습니다. 이유는 크게 두가지 입니다.</p><h3 id="1-엔지니어로서-동료들에게-인정을-받는다"><a href="#1-엔지니어로서-동료들에게-인정을-받는다" class="headerlink" title="1. 엔지니어로서 동료들에게 인정을 받는다"></a>1. 엔지니어로서 동료들에게 인정을 받는다</h3><p>요즘 기업들 마다 SW Engineer 구하려고 난리입니다. 좋은 프로그래머 구하기가 하늘의 별따기라고 하지요. 비전공자이거나 심지어 다른 업무를 하던 사람들도 요즘은 코딩을 배워 프로그래머가 되려고 하는 사람들도 많습니다. 또 예전처럼 주니어때는 코딩을 하다가 연차가 오르면 관리자로 가고 하지 않고, 요즘은 코딩을 오래 한 사람들의 경험을 그만큼 인정하고 대우하는 경우도 많아진 것 같습니다. 최근 분위기가 이렇다보니 은연중에 코딩에 손을 놓고, 따라가기가 벅찬 사람들은 좌절감을 느낄수도 있을것 같습니다.</p><p>Elastic에는 2018년 10월 현재 1,100 명 정도의 직원이 있는데 60% 정도가 엔지니어입니다. 엔지니어들 중에는 코드를 짜는 엔지니어들이 대다수이지만, 주 업무가 그렇지 않은 엔지니어들도 많습니다. 대표적으로</p><ul><li>기술지원(support) 엔지니어</li><li>커뮤니티 엔지니어</li><li>교육 엔지니어</li><li>컨설턴트</li><li>솔루션 아키텍트</li></ul><p>등이 있습니다. </p><p>제가 Elastic은 정말 멋진 회사라고 생각하는 이유 중 하나가, 업무의 경중을 가리지 않고 서로 존중하는 모습들 때문에 그렇습니다. 회사 리더들이 정한 정책같은 이유 때문이 아니라 순수하게 동료들끼리 서로의 직업을 존중해줍니다.</p><p>저는 Community Engineer 이지만 항상 처음 만나는 회사 동료들과 제 소개를 하면 다른 엔지니어들로부터 <strong><em>“정말 멋진 일을 하고 있군! 우리 회사가 성장하기 위해서 커뮤니티팀이 정말 중요하지”</em></strong> 라는 이야기를 자주 듣습니다. 제가 2015년에 입사하고 그 동안 한국에서 했던 <a href="/2014/03/start-es-community">커뮤니티 밋업</a>, 행사에 대한 일이나, 회사 <a href="/2017/11/2017-11-technical-translations">홈페이지 번역 리뷰</a> 한 일 등을 이야기 하면 <strong><em>“그건 종민이 너 아니면 누구도 너 만큼 해 낼 수 없는 일이었고, 네가 우리 동료라서 정말 행운이다.”</em></strong> 라는 이야기도 여러번 들었습니다.</p><p>저 뿐만이 아니라 기술지원 엔지니어 같은 경우도 비슷합니다. 한국에 현재 기술지원(support) 엔지니어도 3분이 계신데, 다들 유망한 회사에서 오신 실력있고 인정받는 개발자 분들이었습니다. 사실 개발을 주 업무로 하시던 분들에게 서포트 엔지니어로 합류를 권해드리는게 쉽게 내키는 일은 아니지만, Elastic의 서포트 엔지니어들은 충분히 그럴만한 가치가 있다고 생각합니다.</p><p>저희 회사의 첫 Support Engineer 이자, 현재 서포트 팀의 최고 헤드인 Marty 에게 예전에 들었던 이야기 입니다. Elastic에 직원이 스무명 남짓이던 시절, Elastic 에서 서포트팀을 맡아 줄 사람이 필요하다고 Elastic에 합류한 예전 직장동료가 Marty를 스카우트 하러 왔습니다. 합류하기로 결정하고 Elastic으로 가면서<br><strong><em>“서포트는 보통 개발자들이 자기가 하기 귀찮아하는 고객 상대 같은 일들을 대신 처리 해 주는 일이겠지”</em></strong><br>라는 생각으로 왔는데, 처음에 오자마자 개발자들이 모두 자기에게 몰려와서<br><strong><em>“정말 잘 왔다! 이런 중요한 직책을 맡아줘서 너무 고맙고, 앞으로 우리 회사의 성공은 너의 어깨에 달려있다. 잘 부탁한다.”</em></strong><br>이런 이야기들을 막 해주길래 이게 무슨 상황이지? 여긴 대체 뭐 하는 회사야? 라는 생각을 했다고 합니다.</p><p>올해 저희 서포트 엔지니어들 끼리 summit을 하느라 모두 출장중일 때 개발팀과 기타 다른 엔지니어들이 1주일 동안 자신들의 업무를 중지하고 서포트 엔지니어들의 일을 대신 한 적이 있었는데 슬랙의 서포트 채널에 계속 보이던 이야기가<br><strong><em>“서포트 엔지니어들은 이렇게 어려운 일들을 어떻게 매일 처리하고 있는지, 정말 존경스럽다.”</em></strong><br>라는 대화들을 자주 했습니다.</p><p>개발자가 아니라서 인정받기 힘들것 같은 걱정은 Elastic에 있는 동안은 하지 않아도 될 것 같습니다.</p><p>그리고 두 번째는</p><h3 id="2-뛰어난-개발자가-많다"><a href="#2-뛰어난-개발자가-많다" class="headerlink" title="2. 뛰어난 개발자가 많다"></a>2. 뛰어난 개발자가 많다</h3><p>입니다.</p><p>이상하게 들리실지 모르지만, 회사에 너무 뛰어난 개발자들이 많아서, 사실 제가 개발로 그분들을 따라잡을 수 있을것 같다는 생각이 들지 않습니다.<br>그래도 저 역시 나름 이전 직장에서는 개발 좀 한다는 이야기 들으면서 다니긴 했는데, Elastic에는 <strong>탑 레벨 아파치 커미터도 10명이나 있고</strong>, 한글 형태소 분석기가 필요해서 만들어달라고 했더니, 기존 형태소 분석기 소스만 적당히 보고 <strong><a href="https://www.elastic.co/kr/blog/nori-the-official-elasticsearch-plugin-for-korean-language-analysis">2주만에 버그도 없고 10배나 가볍고 빠른 분석기를 만들어 오는 개발자</a></strong> 라던가, <strong><a href="https://www.elastic.co/kr/blog/timelion-timeline">출장 비행기에서 혼자 데이터 분석 시각 툴을 만드는 개발자</a></strong> 같은 소위 굇수들이 있어서 사실 여기서 내가 궂이 열등감 느끼며 개발할 필요는 없겠다 라는 생각이 들었습니다. 저는 계속 제가 좋아하는 일 하면서 즐겁게 지내면 될 것 같습니다.</p><p>그래도 올해 파이콘에서 하이퍼커넥트 에서 주관한 <a href="https://hyperconnect.github.io/2018/08/18/gem-pick-start.html">젬 줍기 배틀</a>에 참석해서 오랫만에 코드 한번 짜 봤는데, 순위가 중간 이상은 갔습니다. 감을 많이 잃긴 했지만 다시 하면 할 수는 있을것 같더군요. (사실 파이썬 코딩은 이 날 처음 해봤습니다.)</p><p>여하튼, 도전적인 포스트의 제목에 대한 제 결론은</p><blockquote><p>SW Engineer 로 부터 멀어질 것 같은 불안감이 스스로에게서 나왔지만, 좋은 동료들이 있어 극복 해 낼 수 있을것 같다.</p></blockquote><p>정도로 마무리 하겠습니다.</p><p>마지막 사진은 이번에 더블린에서 찍은 저희 엔지니어들의 모습입니다. 사람이 점점 많아져서 제작년에는 사진작가가 사다리를 놓고 찍고, 작년에는 건물 3층 창문에서 찍었는데, 올해는 드론을 날려서 찍었습니다.</p><p><img src="eah-dublin.jpg" alt=""></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;저는 지금 Elastic 에서 Community Engineer 라는 직책을 맡고 있습니다. 주 업무는 개발자 커뮤니티를 지원하는 일이고요 (물론 저는 한국에 직원이 많이 없어서 이런 저런 다른 일들도 많이 합니다), 다른 회사에서는 Evange
      
    
    </summary>
    
      <category term="Engineering" scheme="http://kimjmin.net/categories/Engineering/"/>
    
    
      <category term="Elastic" scheme="http://kimjmin.net/tags/Elastic/"/>
    
      <category term="Engineering" scheme="http://kimjmin.net/tags/Engineering/"/>
    
      <category term="SW Engineer" scheme="http://kimjmin.net/tags/SW-Engineer/"/>
    
  </entry>
  
</feed>
