Jekyll2022-11-25T03:08:44+00:00https://nephtyws.github.io/feed.xmlNephtyw’S Programming StashRelated with computer science and programming thingsWoong Seok Kang야곰 테크캐스트 발표 - 여러 규모의 스타트업에서 개발자로 살아남는 방법2022-10-04T13:05:00+00:002022-10-04T13:05:00+00:00https://nephtyws.github.io/story/yagom-techcast-13-startup-presentation<p>우연찮게 기회가 되어서 야곰이라는 곳에서 주최하는 테크캐스트에서 발표를 하게 되었습니다. 주제가 <code class="language-plaintext highlighter-rouge">스타트업에서 일어난 일</code> 이었는데.. 마침 제가 스타트업에서 일한 경험이 조금 있다보니 운이 좋게 초대되어서 발표하게 되었던 거 같아요.</p>
<p>같이 발표하신 분 두 분이 계셨는데, 두 분 모두 내공이 엄청나신 분이어서 발표를 하면서 제가 기존에 가진 스타트업에 대한 생각을 정리하는 계기도 되었지만, 또 생각치도 못하게 많은 스타트업 이야기들을 풍부하게 들을 수 있어 오히려 저도 매우 좋은 시간이었습니다!</p>
<p>이 글의 핵심인 발표 자료와 테크캐스트 링크는 여기 있습니다! (영구 박제되는…)</p>
<ul>
<li><a href="https://yagom.net/courses/techcast-13/">야곰 테크캐스트 링크</a></li>
<li><a href="https://speakerdeck.com/woongseok/yagom-tekeukaeseuteu-balpyo-yeoreo-gyumoyi-seutateueobeseo-gaebaljaro-salanamneun-bangbeob">발표 자료</a></li>
</ul>Woong Seok Kang우연찮게 기회가 되어서 야곰이라는 곳에서 주최하는 테크캐스트에서 발표를 하게 되었습니다. 주제가 스타트업에서 일어난 일 이었는데.. 마침 제가 스타트업에서 일한 경험이 조금 있다보니 운이 좋게 초대되어서 발표하게 되었던 거 같아요.AWSKRUG 2022/09 발표 - 클라우드 데이터 플랫폼을 구성하는 최신 기술 알아보기2022-09-22T14:15:00+00:002022-09-22T14:15:00+00:00https://nephtyws.github.io/data/awskrug-ds-presentation-3<p>퇴사 이후 첫 발표를 아주 오랜만에 하게 되었습니다. 사실 퇴사하면 시간이 많이 날 줄 알았는데 막상 발표 준비를 해보니 역시 또 그렇진 않더군요..</p>
<p>사실 말로 때운 게 너무 많은 발표 자료라서 공개하긴 좀 부끄럽지만 (자료 안에 확실한 정보만 있는 것도 아니고.. 특히 BigQuery 가격 비교는 제대로 했는지 모르겠네요) 그래도 데이터 웨어하우스나 데이터 레이크에 대한 개념, 최신 트렌드 소개 정도는 가볍게 볼만한 내용들이라 올려봅니다!</p>
<p>어느덧 AWSKRUG DS에서 세 번째 발표를 하게 되었는데, 인연이란게 참 무서운 것 같습니다. 앞으로도 좀 더 뭔가 열심히 해볼 수 있도록 해야겠네요. 발표하는데 많이 보러 와주시고 (무려 70명!) 마지막에 질문도 많이 주셔서 덕분에 너무 보람찬 시간이었습니다.</p>
<p>이 글의 핵심인 발표 자료는 여기 있습니다!</p>
<ul>
<li><a href="https://speakerdeck.com/woongseok/09-balpyo-keulraudeu-deiteo-peulraespomeul-guseonghaneun-coesin-gisul-alabogi">발표 자료</a></li>
</ul>Woong Seok Kang퇴사 이후 첫 발표를 아주 오랜만에 하게 되었습니다. 사실 퇴사하면 시간이 많이 날 줄 알았는데 막상 발표 준비를 해보니 역시 또 그렇진 않더군요..데이터 엔지니어란 무엇인가?2022-05-09T17:15:00+00:002022-05-09T17:15:00+00:00https://nephtyws.github.io/story/what-is-data-engineer<blockquote>
<p>혹시 코딩 할 줄 아세요?</p>
</blockquote>
<p>2019년에 이직했을 당시 회사의 개발자 동료에게 들어본 말입니다. 당시 저는 적당한 규모의 스타트업의 첫 번째 데이터 엔지니어로 입사했었는데, 아무래도 데이터 엔지니어라는 포지션이 생소하고 어떤 일을 하는지 잘 알려져 있지 않아서 그런 것 같다고 생각했지만..</p>
<p>3년이 흐른 지금도 데이터 엔지니어라는 포지션의 정의, 그리고 도대체 회사에서 어떤 일을 하는지에 대한 것들이 다른 직군 분들이 보시기에 정확히 정리되진 않은 것 같습니다. (심지어는 데이터 엔지니어들 사이에서도요!)</p>
<p>이런 생각을 하게된 계기는 최근에 블라인드 게시글 중 <code class="language-plaintext highlighter-rouge">데이터 엔지니어를 개발자로 취급해야 하나요?</code> 라는 글을 보며 문득 떠오른 게, 만약 저한테 <code class="language-plaintext highlighter-rouge">데이터 엔지니어는 무슨 일을 하나요?</code> 하고 물어본다고 해도 한 문장으로 설명할 수가 없겠더라고요. 그래서 이 기회에 2022년의 데이터 엔지니어란 무엇이고, 어떤 일을 하며, 최근의 트렌드가 무엇인지 한 번 정리해보는 시간을 가져보려 합니다.</p>
<h1 id="데이터-엔지니어-in-스타트업">데이터 엔지니어 in 스타트업</h1>
<p>설명하기에 앞서 먼저 데이터 엔지니어를 채용하고 있는 유명 스타트업들의 채용 공고문을 한 번 살펴보겠습니다.</p>
<p><img src="https://user-images.githubusercontent.com/13582777/167293371-ba37fd8d-5182-4a2d-93b8-63f508515384.png" alt="쏘카 데이터 엔지니어 채용 공고" />
<em>쏘카 데이터 엔지니어 채용 공고</em>
<img src="https://user-images.githubusercontent.com/13582777/167293471-4644d793-4412-47d1-bcd7-1fcff456e4d3.png" alt="몰로코 데이터 엔지니어 채용 공고" />
<em>몰로코 데이터 엔지니어 채용 공고</em>
<img src="https://user-images.githubusercontent.com/13582777/167293515-74ed8ea2-15a7-4e59-b139-564a66fc7691.png" alt="토스증권 데이터 엔지니어 채용 공고" />
<em>토스증권 데이터 엔지니어 채용 공고</em>
<img src="https://user-images.githubusercontent.com/13582777/167293562-f514673d-a842-4e2d-98df-49efcb3533c8.png" alt="카카오스타일 데이터 엔지니어 채용 공고" />
<em>카카오스타일 데이터 엔지니어 채용 공고</em></p>
<p>순서대로 쏘카, 몰로코, 토스증권, 카카오스타일의 데이터 엔지니어 채용 공고인데요. 채용 공고를 보고 데이터 엔지니어가 무엇인지 감이 오시나요? 공통적으로 등장하는 항목들을 조금 정리해보면,</p>
<ul>
<li>서비스의 다양한 곳에서 발생하는 정형/비정형 데이터를 수집하기 위한 배치/실시간 파이프라인 구축 및 운영</li>
<li>데이터 레이크/데이터 웨어하우스 등 전사 인원들의 원활한 데이터 분석을 위한 데이터 플랫폼 운영</li>
<li>대용량의 데이터 가공을 통해 실제 서비스 혹은 분석에 사용될 수 있는 데이터 마트 구축</li>
<li>데이터 기반 서비스 제공 및 서빙 - 관련 <code class="language-plaintext highlighter-rouge">*Ops</code> 또한 담당</li>
</ul>
<p>이런 내용들이 공통적으로 등장하는 것을 확인할 수 있습니다. 각 내용들에 대해서 제가 데이터 엔지니어로 일할 때의 경험을 보태서 설명해보도록 하겠습니다.</p>
<h2 id="데이터-엔지니어의-업무">데이터 엔지니어의 업무</h2>
<ul>
<li><strong>서비스의 다양한 곳에서 발생하는 정형/비정형 데이터를 수집하기 위한 배치/실시간 파이프라인 구축 및 운영</strong>
<ul>
<li>웹, 앱 등의 클라이언트 환경, DB나 서버같은 내부 백엔드 환경, 그리고 third-party tool (주로 마케팅 툴 - Facebook Ad, Appsflyer, …) 에서 발생하는 데이터들을 ETL/ELT</li>
<li>이를 위해 batch/streaming pipeline을 구성하고, data schema를 정의하고, ETL/ELT를 위해 분산 컴퓨팅 프레임워크 (Spark, Presto, Athena, BigQuery 등) 를 사용합니다.</li>
</ul>
</li>
<li><strong>데이터 레이크/데이터 웨어하우스 등 전사 인원들의 원활한 데이터 분석을 위한 데이터 플랫폼 운영</strong>
<ul>
<li>클라우드를 활용하지 않는 환경에서는 Hadoop + ecosystem (Spark, Hive, Presto 등) 을 주로 사용하고, 클라우드를 활용하는 환경에서는 AWS EMR, Athena, Glue, GCP BigQuery, Dataproc 등의 managed data service를 활용해서 데이터 플랫폼을 구축합니다.</li>
<li>Snowflake, Databricks 등의 상용 SaaS 플레이어들도 존재하고 있고, 실제로 사용하는 회사도 많습니다. (스타트업보단 규모가 좀 더 큰 회사들이 많이 사용합니다)</li>
</ul>
</li>
<li><strong>대용량의 데이터 가공을 통해 실제 서비스 혹은 분석에 사용될 수 있는 데이터 마트 구축</strong>
<ul>
<li>전사 고객들이 핵심 데이터에 쉽고 빠르게 접근할 수 있어야 하기 때문에, 원천 데이터에서 다양한 추상화를 거쳐 주로 데이터 마트라고 부르는 가공 데이터를 만듭니다.</li>
<li>데이터 마트에는 매출 통계와 같은 단순 분석용 데이터부터 결제/정산/쿠폰/멤버십과 같은 고객 데이터, 추천 모델 생성에 사용되는 데이터, 추천 모델이 만들어낸 데이터 등 실제 고객들에게 전달되는 데이터들 또한 존재할 수 있습니다.</li>
<li>조회를 위한 BI tool (Tableau, Redash, Superset, Jupyter, Zeppelin, …) 도 주로 데이터 엔지니어들이 운영하며, 요새는 누구나 ad-hoc하게 분석할 수 있게 해주는 Amplitude와 같은 SaaS도 운영하곤 합니다.</li>
</ul>
</li>
<li><strong>데이터 기반 서비스 제공 및 서빙 - 관련 <code class="language-plaintext highlighter-rouge">*Ops</code> 또한 담당</strong>
<ul>
<li>위에 언급된 업무들을 하기 위해서는 인프라를 운영하는 것이 거의 필수적인데, 보통은 데이터 엔지니어들이 데이터 관련 서비스의 DevOps (배포, 모니터링 등) 를 주요 역할 중 하나로 가져가는 경우가 많습니다.</li>
<li>왜냐면 DevOps 엔지니어분들이 데이터 환경까지 봐주시기엔 바쁘시기도 하고, 데이터 환경 자체가 production 환경과는 현저하게 격리된 경우가 많으며 또한 데이터 관련 배포는 실제 서비스의 배포와는 다른 주기로 이뤄지는 경우도 많기 때문입니다.</li>
<li>또한 AB 테스트, 추천 결과 서빙 등 데이터 기반 서비스를 production으로 내보내야 할 때는 개발부터 배포까지 E2E로 진행하기도 하고, DataOps, MLOps 같은 것들이 부상하면서 관련된 일도 필요하다면 데이터 엔지니어들이 같이 수행하기도 합니다.</li>
</ul>
</li>
</ul>
<p>위와 같이 데이터 엔지니어의 업무를 어느정도 정리해보았는데요. 그렇다면 이러한 업무를 수행하기 위해서 갖춰야 할 능력들은 무엇이 있을까요? 저의 주관적인 경험을 바탕으로 데이터 엔지니어가 갖춰야 할, 혹은 갖추면 좋을 법한 기술들에 대해 정리해보았습니다.</p>
<h2 id="데이터-엔지니어가-갖춰야-할-기술">데이터 엔지니어가 갖춰야 할 기술</h2>
<h3 id="tech-skills">Tech skills</h3>
<ul>
<li><strong>분산 처리에 대한 기본적인 이해</strong>
<ul>
<li>데이터 엔지니어들이 주로 사용하는 고수준 프레임워크인 Spark, Athena (Presto), BigQuery, Hive 등은 전부 분산 컴퓨팅을 바탕으로 만들어진 프레임워크들이고, 만약 실무에서 분산 처리를 하지 않더라도 데이터의 규모가 늘어나면 자연스럽게 분산/병렬 처리를 해야만 데이터를 원활하게 처리할 수 있기 때문에, 기본적으로 분산 처리에 대한 이해가 있다면, 그리고 이러한 프레임워크들이 어떻게 동작하는지 알면 좋은 점이 생각보다 많습니다.</li>
<li>데이터가 매우 많은 상황에서는 프레임워크가 제공하는 연산 하나, 코드 한 줄이 미치는 영향이 생각보다 클 수 있기 때문입니다. 실제로 코드 몇 줄을 바꿔주는 것으로 컴퓨팅 용량을 아주 많이 차지하는 작업을 크게 개선할 수 있기도 하고요. (예를 들어 <code class="language-plaintext highlighter-rouge">groupByKey</code> 같은 연산을 다른 연산으로 바꿀 수 있다면…)</li>
</ul>
</li>
<li><strong>DB/SQL에 대한 이해</strong>
<ul>
<li>데이터 엔지니어가 뛰노는 환경들 (Athena, BigQuery 등) 은 일종의 데이터베이스입니다. 일반적으로 서비스에 활용되는 RDB/NoSQL DB와 다른 부분도 많고, 공유하는 부분도 많습니다. 그렇기에 DB에 대한 일반적인 상식을 많이 알면 알수록, 특히 위에서 언급한 직접 운용하고 있는 데이터 플랫폼의 기반을 알면 알수록 production 환경에서 플랫폼을 운영하기 수월해집니다.</li>
<li>또한 요즘에는 정말 많은 경우에 데이터 조회, 심지어는 빅데이터 플랫폼에서는 약간 생소할 수도 있는 개념인 삽입/삭제까지 SQL을 이용해서 하기 때문에 SQL을 매우 잘 다루는 것이 더더욱 중요해졌고, 더 나아가 내가 만들어낸 쿼리가 실제로 어떻게 동작하는지 이해할 수 있다면 SQL 관련 업무, 그리고 주변 동료들이 SQL을 사용하면서 어려움을 겪을 때 좋은 해결책을 제시해줄 수 있습니다.</li>
</ul>
</li>
<li><strong>프로그래밍</strong>
<ul>
<li>데이터 엔지니어가 업무를 잘 하기 위해서는 최소 한 개 이상의 프로그래밍 언어에 아주 능숙해야하며, 또한 여러 도구들을 운용하려면 도구에 맞는 언어를 다룰 수 있어야 합니다.
<ul>
<li>예를 들어 Airflow와 같은 workflow engine은 Python으로</li>
<li>Spark와 같은 분산 처리 프레임워크 활용은 Scala로</li>
<li>밥먹듯이 하는 데이터 조회/처리는 SQL로</li>
<li>production 데이터 서빙 백엔드 서버를 구축할 때는 고성능 API 서버를 만들 수 있는 Go 등</li>
</ul>
</li>
</ul>
</li>
<li><strong>(클라우드) 인프라에 대한 이해</strong>
<ul>
<li>데이터 플랫폼만을 전담하는 DevOps 엔지니어를 채용하는 회사는 잘 없습니다. 그렇기에 데이터 엔지니어들은 인프라 관리에 익숙해야 하고, 클라우드를 잘 활용해서 인프라를 잘 운영할 수 있어야 합니다. 만약 클라우드를 활용하지 않는 회사라면 on-premise 인프라 운영 능력이 필요할 수 있습니다.
<ul>
<li>클라우드 managed service 운영, 데이터 서비스용 CI/CD 파이프라인 구축, Kubernetes 운영, 모니터링, on-premise 환경이라면 CDH 등의 Hadoop 배포판 운영 등</li>
</ul>
</li>
<li>데이터 엔지니어가 DevOps 전문가가 될 필요는 없지만 (되기도 어렵고), 적어도 DevOps 엔지니어 분들과 원활한 협업을 할 수 있는 수준은 되어야 인프라 문제를 스스로 해결하며 실무를 진행할 수 있습니다.</li>
</ul>
</li>
</ul>
<h3 id="특히-스타트업에서-정말-중요한-soft-skills">(특히 스타트업에서 정말 중요한) Soft skills</h3>
<ul>
<li><strong>서비스에 대한 이해</strong>
<ul>
<li>전사 조직이 어떤 방향으로 나아가야 할지를 데이터 엔지니어가 제시할 수는 없지만, 일을 함께하는 동료들과 함께 서비스를 개선하기 위해서는 어떤 데이터를 보아야할지 의견을 모으고, 적절한 지표가 설정되면 해당하는 데이터를 모두가 잘 볼 수 있도록 전사적인 흐름을 만들어내야 합니다. 이를 위해서는 어느정도 서비스에 대해 이해하고 있어야 합니다.</li>
<li>특히 이는 작은 규모의 스타트업, 혹은 데이터 문화가 잘 조성되지 않은 회사에서는 더욱 중요한 기술입니다. 데이터 문화가 제대로 조성되어있지 않은 환경에서는 데이터를 제대로 보기 어렵고, 데이터를 볼 필요성을 느끼지 못할 수도 있으며 오히려 이상한 데이터를 보며 잘못된 의사결정을 만들어내는 단초가 될 수 있습니다.</li>
<li>전사적인 지표 설정 과정이나 데이터 문화를 만들어나가는 과정에 혹시 데이터 엔지니어가 개입되지 않더라도, 적어도 전사적인 차원에서 어떤 데이터를 수집해야하고, 장기적인 관점에서 어떤 데이터가 더 수집되면 현재 가지고 있는 데이터들을 더 잘 활용할 수 있게 되는지 끊임없이 고민해야합니다.</li>
<li>그렇지 않으면 분명 데이터를 수집한지 몇 개월 지나지 않아서 <code class="language-plaintext highlighter-rouge">이때 이런 데이터를 추가로 수집했었더라면 좋았을 텐데..</code> (데이터를 수집할 수 있는 시기를 놓치면 다시는 그 데이터를 얻지 못할 수 있습니다) 혹은 <code class="language-plaintext highlighter-rouge">그때 schema를 잘 정의했더라면 이런 문제가 생기지 않았을 텐데..</code> 하는 후회가 밀려올 수 있습니다.</li>
</ul>
</li>
<li><strong>뛰어난 커뮤니케이션 스킬</strong>
<ul>
<li>전사적으로 데이터 엔지니어가 무엇을 해야하는지 잘 정의되어있고, 데이터 엔지니어가 왜 필요한지 알며, 데이터 엔지니어가 어떠한 문제를 해결해줄지 알고 있는 조직은 (특히 스타트업일수록) 정말로 많지 않습니다. 혹은 전사적으로 이러한 필요성을 느낀다고 하더라도, 나와 함께 일을 하는 동료들은 이러한 사실을 모를 수도 있습니다.</li>
<li>데이터 엔지니어, 더 나아가서는 데이터 조직은 협업을 하지 않고서는 전사적으로 크나큰 변화를 만들기 어려운 직군이라고 생각합니다. 데이터 엔지니어가 일을 잘 하기 위해서는 전사 동료, 혹은 팀들이 어떤 문제를 해결하고 싶은지 파악하고, 그 중 데이터를 통해 효과적으로 해결할 수 있는 문제가 무엇인지 선별하여 좋은 해결책을 제시할 수 있어야 한다고 생각합니다.</li>
<li>이런 협업을 잘 하려면 당연하게도 커뮤니케이션 스킬이 뛰어나면 뛰어날수록 좋습니다. 특히, 나와 같이 일하는 동료들, 혹은 회사가 데이터 엔지니어나 데이터 조직이 무슨 일을 하는지 잘 모르는 단계에서는 이를 위해 사내의 세일즈 조직처럼 움직여야 할 수도 있습니다.</li>
</ul>
</li>
<li><strong>Time to market을 지키는 능력</strong>
<ul>
<li>데이터 엔지니어를 비롯한 많은 엔지니어들은 종종 아주 어렵고 한 번에 해결하기 어려우며 해결까지 오랜 시간이 걸리는 문제를 푸는 경우가 있습니다. 또한 스타트업일수록 이런 문제를 푸는 시간은 많이 주어지지 않고요. 그렇기에 일을 적절하게 쪼개고, 마일스톤에 맞춰 배포할 수 있는 능력이 필요합니다.</li>
<li>예를 들어 스타트업에서 추천 시스템을 만드는 경우 완벽한 추천 시스템을 1년동안 만들어내는 것보다 (물론 1년을 다 사용한다고 해도 대부분 완벽하게 만들 수도 없습니다) 2주 혹은 한 달정도의 주기로 시스템을 만들고 유저 피드백을 받거나 알고리즘을 바꾸면서 개선하는 것이 더욱 나은 방법일 것입니다.</li>
<li>특히 시시각각이 중요한 B2C 서비스의 경우 이 능력이 더욱 중요합니다. 예를 들어, 경쟁사보다 좋은 서비스를 빨리 내놓는 것은 직무를 막론하고 중요한 능력입니다.</li>
</ul>
</li>
<li><strong>Time to market을 지키지 않을 능력</strong>
<ul>
<li>하지만 때로는 무리하게, 혹은 프로토타입을 이용해 time to market을 지키는 것보다 시간을 가지고 수행하는 것이 좋을 때도 있습니다. (너무 당연한 말인가요?)</li>
<li>예를 들어 서비스에서 계속 사용할 데이터 플랫폼을 구축하는데 오래 걸린다는 단순한 이유만으로 RDB 한 대를 띄워놓고 데이터를 몰아넣는 것보단, 장기적인 관점에서 데이터 플랫폼을 구축하는 것이 더욱 이득이 된다고 판단한다면 이를 설득하여 실행하는 능력도 있어야 합니다.</li>
<li>이를 위해서는 데이터 도메인에 대한 전문성이 있어야 하고, 이를 바탕으로 나와 같이 일하는 동료들을 설득할 수 있어야 합니다.</li>
</ul>
</li>
</ul>
<p>사실 위에 적은 내용이 비단 데이터 엔지니어에게만 해당되는 내용은 아니긴 합니다. 다만 데이터 엔지니어의 경우 이러한 능력을 지니는 것이 다른 개발 직무에 비해 중요하다고 생각하는데, 왜냐하면 본인의 의지에 따라, 혹은 능력 여하에 따라 회사에서 데이터 플랫폼만 운영하는 데이터 엔지니어가 될 수도 있고, 전사적으로 데이터 관련 문화를 만들고, 서비스에 영향을 줄 수 있는 데이터를 만드는 데이터 엔지니어가 될 수도 있기 때문입니다. (물론 요즘에는 데이터 엔지니어도 포지션이 점점 세부적으로 변해가고 있기 때문에, 이러한 예는 데이터 문화가 잘 조성되지 않은 작은 스타트업에 적합하다는 사실도 같이 말씀드리고 싶습니다. 어떤 규모에 도달하면 데이터 플랫폼만 운영하는 것도 굉장히 도전적인 일로 변합니다. 특히 여러가지 비즈니스 제약사항이 있다면 더더욱 그렇습니다)</p>
<p>위에 적은 내용만 보면 모든 회사에 데이터 엔지니어가 필수적으로 있어야 할 것 같은데요. 하지만 아쉽게도 모든 회사가 데이터 엔지니어를 필요로 하진 않습니다. (혹은, 데이터 엔지니어를 중요하게 채용할 필요는 없습니다)</p>
<p>그렇다면 데이터 엔지니어는 어떤 회사에 가면 좋을까요? 제 생각은 다음과 같습니다.</p>
<h2 id="데이터-엔지니어가-활약할-수-있는-회사">데이터 엔지니어가 활약할 수 있는 회사</h2>
<ul>
<li><strong>고객이 명확히 존재하는 회사 (주로 B2C 회사)</strong>
<ul>
<li>고객이 존재하는 회사는 항상 서비스에 진입하는 고객의 모든 데이터를 궁금해합니다. 데이터 엔지니어를 비롯한 데이터 조직이 가장 신나게 활약할 수 있는 회사입니다.</li>
</ul>
</li>
<li><strong>데이터 기술 플랫폼을 만드는 회사, 혹은 기술 조직</strong>
<ul>
<li>다양한 목적으로 데이터 기술 플랫폼을 만드는 회사나 조직이 있습니다. 특히, 대기업의 경우 사내에서 사용하는 도구, 기술, 프레임워크들이 꽤나 많고 비중있게 유지보수되기도 합니다. 이때, 도메인 지식을 가진 데이터 엔지니어들은 소프트웨어 엔지니어처럼 활약할 수 있는 부분이 많다고 생각합니다.</li>
</ul>
</li>
</ul>
<p>다만 위의 항목 중 어느 것에도 해당하지 않는 회사라면, 데이터 엔지니어를 비롯한 데이터 직군이 활약할 여지가 상대적으로 적다고 생각합니다. 데이터는 주의깊게 봐줄 사람이 있을 때만 의미가 있고, 어떤 회사는 이런 데이터를 보는 것이 중요하지 않을 수 있으며, 심지어는 데이터를 볼 필요가 없는 비즈니스거나 수집할 데이터가 없을 수도 있기 때문입니다.</p>
<p>지금까지는 주로 스타트업 데이터 엔지니어의 역할에 대해 살펴봤습니다. 그렇다면 좀 더 규모가 큰 회사의 데이터 엔지니어는 어떤 일을 할까요? (위에서도 그랬지만 여기서부터는 더욱 더 순수한 저의 생각입니다)</p>
<h1 id="데이터-엔지니어-in-대기업">데이터 엔지니어 in 대기업</h1>
<p><img src="https://user-images.githubusercontent.com/13582777/167394656-d51f9575-0080-42c0-b67e-4fc6b03cf1c7.png" alt="네이버 클라우드 빅데이터 플랫폼 분석 엔지니어 채용 공고" />
<em>네이버 클라우드 빅데이터 플랫폼 분석 엔지니어 채용 공고</em>
<img src="https://user-images.githubusercontent.com/13582777/167394816-264af751-951e-4373-aa71-f90067b48a43.png" alt="네이버 CLOVA AI 머신러닝을 위한 대규모 데이터 플랫폼 구축 엔지니어 채용 공고" />
<em>네이버 CLOVA AI 머신러닝을 위한 대규모 데이터 플랫폼 구축 엔지니어 채용 공고</em>
<img src="https://user-images.githubusercontent.com/13582777/167396460-b644c6ab-ac7e-4eaa-9e8a-f16abbe113eb.png" alt="카카오 데이터실 데이터 엔지니어 채용 공고" />
<em>카카오 데이터실 데이터 엔지니어 채용 공고</em>
<img src="https://user-images.githubusercontent.com/13582777/167396158-dc0718f3-2c73-4470-9917-60b6c5a2b121.png" alt="라인 Ads 데이터 엔지니어 채용 공고" />
<em>라인 Ads 데이터 엔지니어 채용 공고</em>
<img src="https://user-images.githubusercontent.com/13582777/167397577-29cc9165-9602-4b77-b541-200358c42ae2.png" alt="현대자동차 AIRS 모빌리티 서비스 데이터 개발자 채용 공고" />
<em>현대자동차 AIRS 모빌리티 서비스 데이터 개발자 채용 공고</em></p>
<p>순서대로 우리나라의 대표적인 IT 대기업으로 분류되는 네이버, 카카오, 라인 그리고 현대자동차 AIRS의 채용 공고를 가져왔습니다. 전반적으로 살펴보았을 때 느낀 점은 다음과 같습니다.</p>
<h2 id="대기업-데이터-엔지니어-공고의-특징">대기업 데이터 엔지니어 공고의 특징</h2>
<ul>
<li>데이터 엔지니어가 무슨 역할을 하는지 좀 더 명확하게 정의되어 있습니다. 스타트업의 데이터 엔지니어는 특히 회사의 규모가 작을수록, 데이터 문화가 잘 조성되지 않았을수록 전사에 영향력을 주어야 하는 경우가 있지만, 대기업의 데이터 엔지니어는 전사에 영향을 주기보단 소속되어 있는 팀과 조직에 영향력을 주어야 한다는 느낌을 받았습니다. (왜냐하면 이미 전문 데이터 조직에서 일을 하기 때문일 것입니다)</li>
<li>즉, 스타트업 데이터 엔지니어가 generalist에 가깝게 업무를 진행하는 경우가 많다면, 반대로 대기업 데이터 엔지니어는 조금 더 특정한 기술 도메인의 specialist 처럼 일을 해야한다는 느낌입니다.</li>
<li>그래서 이런 대기업 기술 조직의 경우 사용하는 기술의 숫자가 스타트업에 비해 적더라도 각 기술을 좀 더 깊게 봐야한다던가, 그리고 좀 더 긴 호흡으로 기술을 고도화하는 경우가 더 많을 것 같다는 생각입니다. 그도 그럴 것이, 스타트업에서는 그런 기술 고도화가 필요하지 않을 수 있습니다. 예를 들어 우리나라에서 네이버, 카카오만큼의 트래픽과 데이터를 소화하는 곳은 많지 않고, 더욱 큰 데이터를 처리하는 곳에서는 좀 더 튼튼한 기술들이 필요할 테니까요.</li>
</ul>
<h1 id="데이터-엔지니어-in-미국">데이터 엔지니어 in 미국</h1>
<p>마지막으로 미국 유망 기업들의 채용 공고를 살펴보는 시간을 갖도록 하겠습니다.</p>
<p><img src="https://user-images.githubusercontent.com/13582777/167398230-0afbc84a-b078-4827-8f18-255ca140c767.png" alt="Airbnb 데이터 엔지니어 채용 공고" />
<em>Airbnb 데이터 엔지니어 채용 공고</em>
<img src="https://user-images.githubusercontent.com/13582777/167398735-8448ee6c-756a-41ae-b0a1-0c1dfb8fe336.png" alt="Uber 빅데이터 엔지니어 채용 공고" />
<em>Uber 빅데이터 엔지니어 채용 공고</em>
<img src="https://user-images.githubusercontent.com/13582777/167398928-1d5917e4-e76c-413d-98d2-fc0a505cd3aa.png" alt="Meta 데이터 엔지니어 채용 공고" />
<em>Meta 데이터 엔지니어 채용 공고</em>
<img src="https://user-images.githubusercontent.com/13582777/167399109-e90bbf6a-3aef-468a-8b21-ddceb2533d94.png" alt="Netflix 데이터 플랫폼 엔지니어 채용 공고" />
<em>Netflix 데이터 플랫폼 엔지니어 채용 공고</em>
<img src="https://user-images.githubusercontent.com/13582777/167399436-26a1792d-8c6c-408b-a04a-98609c2ba2d8.png" alt="Google 클라우드 데이터 엔지니어 채용 공고" />
<em>Google 클라우드 데이터 엔지니어 채용 공고</em></p>
<p>전반적으로 한국의 채용 공고와 큰 차이점이 없는 것 같지만, 몇 가지 느낀 점을 정리해보면 다음과 같습니다.</p>
<h2 id="미국-데이터-엔지니어-공고의-특징">미국 데이터 엔지니어 공고의 특징</h2>
<ul>
<li><code class="language-plaintext highlighter-rouge">large scale</code>, <code class="language-plaintext highlighter-rouge">high performance</code>, <code class="language-plaintext highlighter-rouge">exteremly efficient and reliable</code>, <code class="language-plaintext highlighter-rouge">high complex</code> 등 기술적 장벽에 대해 이야기하고 있는 공고가 많았습니다. 아무래도 전세계를 대상으로 서비스를 하고 있는 회사이니 그럴만 한 것 같습니다.</li>
<li>그러면서도 동시에 커뮤니케이션 능력, 비즈니스 사고 등 soft skill에 대한 요구사항도 눈에 띄었습니다. (욕심이 많네요..)</li>
<li><code class="language-plaintext highlighter-rouge">데이터 엔지니어</code> 라는 넓은 범위의 직무를 지칭하기보단, <code class="language-plaintext highlighter-rouge">데이터 플랫폼 엔지니어</code> 등의 구체적인 직무를 제시하는게 눈에 띄었습니다. (이는 몇몇 한국 기업에서도 사용하고 있는 직무 분류입니다. 예를 들어 토스, 데브시스터즈 등)
<ul>
<li>즉, 데이터 엔지니어 중에서도 데이터 플랫폼 엔지니어는 무엇보다도 플랫폼 기술 지식이 중요할 것으로 예상됩니다.</li>
</ul>
</li>
<li>또한, 똑같은 <code class="language-plaintext highlighter-rouge">데이터 엔지니어</code> 포지션이라도 어떤 팀에 속하냐에 따라 업무 범위나 필요로 하는 기술이 달라지는 것을 볼 수 있었습니다.
<ul>
<li>예를 들어, Google의 경우 <code class="language-plaintext highlighter-rouge">Data Engineer, Core Infrastructure</code> 라는 포지션은 아주 높은 수준의 기술 이해도를 요구했습니다. 그러나 <code class="language-plaintext highlighter-rouge">Data Engineer, Youtube</code> 포지션은 기술 이해도와 더불어 비즈니스 요구사항을 처리하는 것과 관련된 내용도 찾아볼 수 있었습니다.</li>
</ul>
</li>
</ul>
<p>한국과 미국의 채용 공고들을 살펴보니, 어느정도 <code class="language-plaintext highlighter-rouge">데이터 엔지니어</code> 라는 것이 무엇인지 좀 더 감이 오는 느낌입니다.</p>
<h1 id="마무리">마무리</h1>
<p>여기까지 읽으셨다면 이제 데이터 엔지니어가 무슨 일을 하는 사람들이고, 회사에서 어떤 역할을 가지고 있으며, 어떠한 기술들을 가지고 있는지 어느정도 파악이 완료되신 상태일 겁니다. (그랬으면 좋겠네요…)</p>
<p>보면서 느끼셨겠지만, 데이터 엔지니어의 업무 범위는 생각보다 넓고 다양하며, 또 회사의 산업군마다, 규모마다 데이터 엔지니어에게 요구하는 기술들이 꽤나 차이나기도 합니다. 심지어는 필요에 따라 데이터 엔지니어라는 포지션을 좀 더 잘게 쪼개서 뽑기도 하고요.</p>
<p>예를 들어 어떤 포지션은 좀 더 비즈니스 문제 해결에 초점이 맞춰진 포지션도 있고, 반면에 또 다른 회사의 포지션은 기술적인 문제를 중점적으로 해결해야하는 포지션도 있는 것 같습니다.</p>
<p>미래에는 데이터 엔지니어가 어떻게 변할지 저도 잘은 모르겠습니다만, 각자가 가진 역량에 따라 커리어 패스를 잘 설정하는 것이 어느 때보다 중요한 시점인 것 같습니다.</p>
<p>최대한 자세하게 기술하려고 했지만 오랜만에 글을 쓰다보니 군데군데 빠진 내용이나 틀린 내용이 있을텐데, 댓글로 지적해주시면 반영해보도록 하겠습니다.</p>
<p>긴 글 읽어주셔서 감사합니다!</p>Woong Seok Kang혹시 코딩 할 줄 아세요?AWS Summit Korea 2021 발표 - 커머스 스타트업의 효율적인 데이터 분석 플랫폼 구축기2021-11-02T16:15:00+00:002021-11-02T16:15:00+00:00https://nephtyws.github.io/data/aws-summit-presentation<p>오랜만에 블로그에 글을 올려보네요. 글감은 정말 많지만 사는 게 바빠서 글을 올리지 못하고 있습니다. 최근에는 이직도 했는데 이직 사이에 약 2주 정도 텀이 있었고
그 텀을 이용해서 블로그 글도 쓰고, 공부도 하려고 했는데 정말 시간이 왜 이렇게나 빠른지 야속할 뿐입니다.</p>
<p>어쨌든 작년 5월에 AWS Summit Korea 2021에서 발표했던 자료가 유튜브에 올라와서 오랜만에 블로그에 글을 썼습니다. 이번 AWS Summit은 코로나로 인해 5월에
녹화를 하고, 7월쯤인가 온라인으로 개최된 다음 9월 말쯤 유튜브에 영상이 공개된 거 같네요. 정말 좋은 기회로 발표하게 되었지만, 코로나 때문에 온라인으로 진행되어 다소
아쉬웠고 발표 시간도 15분 정도밖에 되지 않아 심도있는 내용을 다루지 못해 한 번 더 아쉬웠습니다.</p>
<p>아마도 올해 4월쯤 AWSKRUG 모임에서 AWS Summit 내용을 가지고 60분 짜리 발표를 했던 거 같은데, 그 자료가 있으면 더 좋았겠지만 아쉽게도 잃어버린 거 같습니다.</p>
<p>다음에는 더 재밌고 좋은 글로 다시 찾아오도록 하겠습니다.</p>
<p>이 글의 핵심인 발표 자료는 여기 있습니다!</p>
<ul>
<li><a href="https://www.youtube.com/watch?v=j7O9RZDsacY&list=PLORxAVAC5fUW7yw8e0olxjf11Qv010Jz-&index=51&ab_channel=AmazonWebServicesKorea">유튜브</a></li>
</ul>Woong Seok Kang오랜만에 블로그에 글을 올려보네요. 글감은 정말 많지만 사는 게 바빠서 글을 올리지 못하고 있습니다. 최근에는 이직도 했는데 이직 사이에 약 2주 정도 텀이 있었고 그 텀을 이용해서 블로그 글도 쓰고, 공부도 하려고 했는데 정말 시간이 왜 이렇게나 빠른지 야속할 뿐입니다.AWSKRUG Data Science 2020/12 발표 - Let the Airflow in AWS2020-12-03T12:35:00+00:002020-12-03T12:35:00+00:00https://nephtyws.github.io/data/awskrug-ds-presentation-2<p>또 한차례 우연한 기회로 AWSKRUG 소모임에서 발표를 하게 되었습니다. 마침 AWS Managed Airflow가 나온 시점이어서 Airflow를 주제로 발표를 하게 되었는데요.
주로 production scale에서 Airflow를 어떻게 운영했는지, 그리고 자잘한 tips, 마지막으로는 Managed Airflow를 살짝 살펴보면서 마무리했습니다. 다음에 또
좋은 기회가 있다면 참여해보고 싶네요!</p>
<p>발표 자료는 여기에: <a href="https://www2.slideshare.net/ssuser9c8444/awskrug-ds-202012-let-the-airflow-in-aws">https://www2.slideshare.net/ssuser9c8444/awskrug-ds-202012-let-the-airflow-in-aws</a></p>Woong Seok Kang또 한차례 우연한 기회로 AWSKRUG 소모임에서 발표를 하게 되었습니다. 마침 AWS Managed Airflow가 나온 시점이어서 Airflow를 주제로 발표를 하게 되었는데요. 주로 production scale에서 Airflow를 어떻게 운영했는지, 그리고 자잘한 tips, 마지막으로는 Managed Airflow를 살짝 살펴보면서 마무리했습니다. 다음에 또 좋은 기회가 있다면 참여해보고 싶네요!Spark 3.0에 새로 추가된 기능 소개 및 설명2020-07-01T15:30:00+00:002020-07-01T15:30:00+00:00https://nephtyws.github.io/data/whats-new-in-spark-3<p><a href="https://spark.apache.org/releases/spark-release-3-0-0.html">Spark 3.0.0</a>이 6월 18일에 출시되었습니다. 정말 오랜만의 major update인 만큼 다양한 feature들이 Spark에 추가되었는데요.
1.x에서 2.x으로 넘어올 때 Dataset API, Catalyst Optimizer 등이 추가되었던 게 벌써 엊그제같은데 벌써 3 버전이 나오다니 감회가 새롭네요. 그만큼 더 공부해야 할 것들도 늘어나겠죠…</p>
<p>Spark 3.0의 ticket list를 보고 제가 중요하다고 생각하는 변경점/추가된 기능에 대해서 간략히 정리해보았습니다!</p>
<h3 id="version-update">Version Update</h3>
<ul>
<li><a href="https://issues.apache.org/jira/browse/SPARK-24417">[SPARK-24417] Build and Run Spark on JDK11</a></li>
<li><a href="https://issues.apache.org/jira/browse/SPARK-23534">[SPARK-23534] Spark run on Hadoop 3.0.0</a></li>
<li><a href="https://issues.apache.org/jira/browse/SPARK-26132">[SPARK-26132] Remove support for Scala 2.11 in Spark 3.0.0</a></li>
<li><a href="https://issues.apache.org/jira/browse/SPARK-30968">[SPARK-30968] Upgrade aws-java-sdk-sts to 1.11.655</a></li>
<li><a href="https://issues.apache.org/jira/browse/SPARK-30695">[SPARK-30695] Upgrade Apache ORC to 1.5.9</a></li>
</ul>
<p>하나같이 주목할만한 변화들입니다. JDK 11로 인해서 JVM 자체의 성능 향상 (+GC) 을 기대할 수 있게 됐고, Hadoop이 개선됨에 따라 S3에 접속할 때 S3A 등의 최신 conncetor를 사용할 수 있고,
Scala 2.11이 제거됨에 따라 backward compatibility를 고려하지 않아도 되니 아주 사소하면서 동시에 아주 중요한 업데이트들이 이루어진 것 같습니다! 특히 ORC도 최신 버전으로 업데이트 되었다니 기쁘네요 (저는 main format으로 ORC를 사용하기 때문에… ㅎㅎ)</p>
<h3 id="major-features">Major features</h3>
<ul>
<li><a href="https://issues.apache.org/jira/browse/SPARK-24615">[SPARK-24615] SPIP: Accelerator-aware task scheduling for Spark</a>
<ul>
<li>Spark가 GPU를 지원하기 위한 초석으로, standalone, YARN, Kubernetes를 cluster scheduler로 사용할 때 GPU resource도 잘 할당할 수 있도록 도와주는 프로젝트입니다. 이번에 Nvidia에서
<a href="https://blogs.nvidia.com/blog/2020/06/24/apache-spark-gpu-acceleration/">Making Spark Fly: NVIDIA Accelerates World’s Most Popular Data Analytics Platform</a> 와 같이 ML이 아닌 상황 (Dataset, Spark SQL 등) 에서도 GPU를 활용할 수 있도록
하는 프로젝트들을 진행하고 있던데, GPU가 점점 Spark와 같은 분산 처리 프레임워크로도 들어오고 있군요!</li>
</ul>
</li>
<li><a href="https://issues.apache.org/jira/browse/SPARK-31412">[SPARK-31412] New Adaptive Query Execution in Spark SQL</a>
<ul>
<li>AQE는 말 그대로 query plan을 생성할 때 최적의 plan을 짤 수 있도록 optimization 해주는 기법들의 통칭이라고 생각하시면 됩니다. 주로 RBO나 CBO를 적용해서 rule에 걸리는 것들은 rule-based로 치환하고,
runtime 이전의 statistics를 가지고 cost를 비교해서 cost가 적은 plan을 선택하는 CBO가 query optimization의 큰 줄기들인데요. Spark에는 이미 여러 rule과 CBO option들이 적용되어 있는데 이번에 좀 더 개선된 것 같습니다.
특히 <a href="https://issues.apache.org/jira/browse/SPARK-29544">[SPARK-29544] Optimize skewed join at runtime with new Adaptive Execution</a> 에서 볼 수 있듯이 skew join에 관한 optimization이 많이 들어갔습니다. skew join은
생각보다 실무에서 많이 접할 수 있는 (e.g. 두 데이터를 join하는 데 데이터 양이 너무 차이난다거나, 전체 데이터에 NULL 같은 게 대다수여서 distribute가 잘 안 된다거나) 문제고, 이를 해결하기 위해선 사용자가
데이터의 분포를 정확히 알고 수동으로 분배해주는 전략이 필요했는데, 이걸 자동으로 해준다니 대단하네요!</li>
</ul>
</li>
<li><a href="https://issues.apache.org/jira/browse/SPARK-11150">[SPARK-11150] Dynamic partition pruning</a>
<ul>
<li>1만 번대 ticket인 걸로 보아서 굉장히 오래된 ticket인 것 같습니다. 주요 골자는 compile time에 알 수 있는 static partitioning과는 달리 (e.g. join 조건이 <code class="language-plaintext highlighter-rouge">t1.foo = t2.bar AND t2.bar = 1</code> 처럼 주어지면 <code class="language-plaintext highlighter-rouge">t1.foo</code>가 <code class="language-plaintext highlighter-rouge">1</code>이라는 것을 쉽게 유추할 수 있습니다)
<code class="language-plaintext highlighter-rouge">SELECT * FROM dim_table JOIN fact_table ON (dim_table.partcol = fact_table.partcol) WHERE dim_table.othercol > 10</code> 이런 query처럼 <code class="language-plaintext highlighter-rouge">partcol</code>을 compile time에 알 수 없는 상황이 오면 전체 데이터를 다 읽을 수밖에 없습니다. 이런 상황에
dynamic partitioning을 runtime에 해서 <code class="language-plaintext highlighter-rouge">partcol</code>의 분포를 알아낼 수 있다면 join strategy 등을 broadcast나 hash로 바꿔버릴 수도 있습니다. (특히 skew가 발생한 상황에서) Spark summit에서의 발표 내용을 보면 최대 100배 빨라진 경우도 있다는데,
충분히 그럴 잠재력이 있는 major change라는 생각이 듭니다! 이것도 위의 변경사항과 같이 적용되어 특정 query들이 굉장히 빨라졌을 거 같네요.</li>
</ul>
</li>
<li><a href="https://issues.apache.org/jira/browse/SPARK-25603">[SPARK-25603] Generalize Nested Column Pruning</a>
<ul>
<li>Nested column (JSON, map 등) 에 대해서도 column pruning을 적용하고, Parquet에만 적용되던 기존 nested column pruning을 다른 format (ORC 등) 에도 적용하는 ticket 입니다. 요새 점점 비정형 데이터가 늘어나고, JSON format의 데이터가
더 활발히 돌아다니는 추세인데 이런 게 잘 되면 성능 향상에 큰 이점이 있겠네요!</li>
</ul>
</li>
</ul>
<h3 id="minor-features">Minor features</h3>
<ul>
<li><a href="https://issues.apache.org/jira/browse/SPARK-27901">[SPARK-27901] Improve the error messages of SQL parser</a>
<ul>
<li>이것은 사실 major에 적어도 될 정도로 중요한 변경 중 하나인데, Spark SQL의 parser인 <a href="https://www.antlr.org/">ANTLR4</a>가 많은 경우에 굉장히 뜬금없는 에러 메시지를 뱉어주는데 (예를 들어 comma를 빼먹었는데 완전 이상한 곳을 찍어준다던지)
그걸 개선하는 ticket 입니다. 이 에러 메시지에 낚여서 버린 시간이 얼마인지 생각해보면 이건 정말 중요한 변경인 것 같습니다.</li>
</ul>
</li>
<li><a href="https://issues.apache.org/jira/browse/SPARK-27395">[SPARK-27395] New format of EXPLAIN command</a>
<ul>
<li>현재 <code class="language-plaintext highlighter-rouge">EXPLAIN</code> command는 약간 불친절한 면이 있는데 ticket 내용에도 보면 모두가 인지하고 있는 사실인 것 같습니다. EXPLAIN의 readability가 향상되면 더 좋은 코드를 만들어낼 수 있겠죠?</li>
</ul>
</li>
<li><a href="https://issues.apache.org/jira/browse/SPARK-25390">[SPARK-25390] Data source V2 API refactoring</a>, <a href="https://issues.apache.org/jira/browse/SPARK-27589">[SPARK-27589] Spark file source V2</a>
<ul>
<li>데이터를 불러오는 부분에서도 최적화 및 리팩토링이 이루어진 것 같습니다. Datasource는 주로 DB에서, Filesource는 S3 등의 storage에서 데이터를 가져올 때 사용하는데 어떤 변경점이 있었는지는 정확히 잘 모르겠지만
매우 기대가 되는 부분 중 하나입니다. 특히 기존 Datasource V1은 좀 오래 전 코드 (1.4 정도) 라서 한 번 갈아엎을 때가 되긴 한 거 같아요.</li>
</ul>
</li>
<li><a href="https://issues.apache.org/jira/browse/SPARK-23977">[SPARK-23977] Add commit protocol binding to Hadoop 3.1 PathOutputCommitter mechanism</a>
<ul>
<li>예전에는 S3과 연동하면서 성능이 저하되는 경우가 많았는데 (S3N을 쓴다던지, rename 때문에 2번 쓴 다던지) Hadoop 및 Spark, 그리고 AWS에서 아주 가열차게 코드를 수정해주는 덕분에 이제 S3를 써도
제 성능을 온전히 낼 수 있게 됐습니다. 특히 Hadoop의 zero-rename committer, EMR의 EMRFS committer, 그리고 이번 Spark의 본격적인 S3A connector 지원까지 아주 기대되는 부분이 많은 것 같습니다!</li>
</ul>
</li>
</ul>
<p>제가 눈여겨보던 Spark 3의 feature 중 하나는 <code class="language-plaintext highlighter-rouge">Arrow</code> 지원이었는데 (현재는 PySpark에서 JVM 왔다갔다 하면서 SerDe로 발생하는 overhead가 너무 크기 때문) 아직 3.0.0에는 나오지 않은 게 좀 아쉽지만,
워낙 다양하고 많은 최적화가 이루어진 버전이라 Scala 기반의 Spark를 즐겁게 사용하면서 기다릴 수 있을 것 같네요. 또 눈에 띄는 major change가 있으면 요약글로 돌아오도록 하겠습니다!</p>Woong Seok KangSpark 3.0.0이 6월 18일에 출시되었습니다. 정말 오랜만의 major update인 만큼 다양한 feature들이 Spark에 추가되었는데요. 1.x에서 2.x으로 넘어올 때 Dataset API, Catalyst Optimizer 등이 추가되었던 게 벌써 엊그제같은데 벌써 3 버전이 나오다니 감회가 새롭네요. 그만큼 더 공부해야 할 것들도 늘어나겠죠…AWSKRUG Data Science 2020/05 발표 - 데이터 엔지니어가 실무에서 맞닥뜨리는 문제들2020-05-14T13:10:00+00:002020-05-14T13:10:00+00:00https://nephtyws.github.io/data/awskrug-ds-presentation<p>우연한 기회로 AWSKRUG의 DS 소모임에서 발표를 하게 되었습니다. 주로 회사에서 겪었던 문제들 + 이론적인 내용들이 포함되어 있습니다. 온라인 발표는 처음이었는데 생각보다는 할만했던 것 같습니다. 생각보다 많은 분이 들어주시고 질문도 활발하게 해주셔서 재밌는 경험이었습니다. 준비하면서 공부를 많이 했는데 역시 사람은 공부를 열심히 해야 하는 것 같다는 생각이…</p>
<p>발표 자료는 여기에: <a href="https://www.slideshare.net/ssuser9c8444/awskrug-ds">https://www.slideshare.net/ssuser9c8444/awskrug-ds</a></p>
<p>목차는 <code class="language-plaintext highlighter-rouge">1) ORC Deep Dive</code> <code class="language-plaintext highlighter-rouge">2) ORC Schema Evolution with AWS Glue</code> <code class="language-plaintext highlighter-rouge">3) AWS EMR 격하게 tuning 해보기</code> 입니다! 다음에 좋은 주제가 있으면 또 해보고 싶네요!</p>Woong Seok Kang우연한 기회로 AWSKRUG의 DS 소모임에서 발표를 하게 되었습니다. 주로 회사에서 겪었던 문제들 + 이론적인 내용들이 포함되어 있습니다. 온라인 발표는 처음이었는데 생각보다는 할만했던 것 같습니다. 생각보다 많은 분이 들어주시고 질문도 활발하게 해주셔서 재밌는 경험이었습니다. 준비하면서 공부를 많이 했는데 역시 사람은 공부를 열심히 해야 하는 것 같다는 생각이…Scala의 예외 처리 - Option, Either, Try2020-04-12T09:10:00+00:002020-04-12T09:10:00+00:00https://nephtyws.github.io/programming/scala-option-either-try<p>Scala에서는 JVM 기반 언어 최대의 적인 <code class="language-plaintext highlighter-rouge">NPE (NullPointerException)</code>를 functional하게 handling 할 수 있는 다양한 수단을 제공하고 있습니다. Scala의 exception handling 3인방인 <code class="language-plaintext highlighter-rouge">Option, Either, Try</code> 에 대해 알아보도록 하겠습니다!</p>
<h3 id="option">Option</h3>
<p>Java에서는 빈 List에 <code class="language-plaintext highlighter-rouge">find</code> 를 할 때 값이 없으면 <code class="language-plaintext highlighter-rouge">null</code>을 반환하거나 <code class="language-plaintext highlighter-rouge">exception throw</code>를 하는 것이 일반적인 상황입니다. 이 일반적인 상황은 매우 위험한 상황이 될 수 있는데 잘못해서 null을 reference 했다간 큰 일이 날 수 있고, exception throw 또한 호출부에서 잘 해결해주지 않으면 프로그램이 뻗어버리기 때문입니다. <code class="language-plaintext highlighter-rouge">exception</code> 이 발생했다고 해서 프로그램이 뻗어버리는 것은 좋은 상황은 아니죠.</p>
<p>Scala에서는 이러한 일을 좀 더 우아하게 처리해줄 수 있게 <code class="language-plaintext highlighter-rouge">Option[T]</code> 라는 type을 제공합니다. 값이 있으면 <code class="language-plaintext highlighter-rouge">Some(value)</code>, 값이 없으면 <code class="language-plaintext highlighter-rouge">None</code>을 반환하는 녀석입니다. 여기서 <code class="language-plaintext highlighter-rouge">None</code>은 <code class="language-plaintext highlighter-rouge">Option[Nothing]</code>과 동치입니다. 즉, Scala에서 금기시되는 <code class="language-plaintext highlighter-rouge">null</code> 대신 <code class="language-plaintext highlighter-rouge">None</code>을 한 번이라도 사용하셨다면, 알게 모르게 <code class="language-plaintext highlighter-rouge">Option</code>을 사용하고 계셨던 겁니다. 함수형 프로그래밍에 익숙하신 분들은 눈치채셨겠지만 특정 값을 <code class="language-plaintext highlighter-rouge">포장하고, 꺼내는</code> 방법을 제공하는 <code class="language-plaintext highlighter-rouge">Option[T]</code>는 Scala에서 제공하는 대표적인 <code class="language-plaintext highlighter-rouge">Monad</code> 중 하나입니다. 어쨌든 <code class="language-plaintext highlighter-rouge">Option[T]</code>를 통해 길고, 때로는 반복적인 작업이 될 수 있는 전통적 try-catch statement을 좀 더 우아하고 깔끔하게 해결할 수 있습니다. 또한, 다음과 같이 함수를 작성한다면 받는 쪽에서 반드시 예외 처리를 하게 됩니다:</p>
<div class="language-scala highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">upperString</span><span class="o">(</span><span class="n">value</span><span class="k">:</span> <span class="kt">String</span><span class="o">)</span><span class="k">:</span> <span class="kt">Option</span><span class="o">[</span><span class="kt">String</span><span class="o">]</span> <span class="k">=</span> <span class="o">{</span>
<span class="nf">if</span> <span class="o">(</span><span class="nv">value</span><span class="o">.</span><span class="py">isEmpty</span><span class="o">)</span> <span class="nc">None</span>
<span class="k">else</span> <span class="nc">Some</span><span class="o">(</span><span class="nv">value</span><span class="o">.</span><span class="py">upper</span><span class="o">)</span>
<span class="o">}</span>
</code></pre></div></div>
<p>왜냐면 <code class="language-plaintext highlighter-rouge">validateName</code>의 return type은 <code class="language-plaintext highlighter-rouge">Option[String]</code> 이므로, 받는 쪽에서는 <code class="language-plaintext highlighter-rouge">get</code> 혹은 <code class="language-plaintext highlighter-rouge">getOrElse</code>, <code class="language-plaintext highlighter-rouge">fold</code>와 같은 함수를 이용해서 해당 값을 안전하게 받는 처리를 해야하기 때문입니다. 이렇게 function signature만 보고도 parameter와 return value를 손쉽게 유추할 수 있으니 아주 편리해집니다.</p>
<h3 id="either">Either</h3>
<p>위의 예제에서 특정 함수가 예외 상황을 만났을 때 어떻게 기본값을 주는지 살펴보았습니다. caller가 None을 받게 된다면 그에 맞춰 에러를 내든, 기본값으로 serving 하든, 자유도있게 처리해주면 되겠지요. 하지만 좀 더 강력한 sign을 주고 싶으면 어떻게 할까요? 예를 들면, <code class="language-plaintext highlighter-rouge">exception</code> 자체를 반환한다던가, <code class="language-plaintext highlighter-rouge">exception의 이유</code>를 반환한다던가, 다른 type을 반환한다던가요. 그럴 땐 Scala의 <code class="language-plaintext highlighter-rouge">Either</code> 를 사용하면 됩니다. Scala의 Either는 <code class="language-plaintext highlighter-rouge">Either[Left, Right]</code> 로 표현되며, 주로 <code class="language-plaintext highlighter-rouge">Left</code>에는 error가, <code class="language-plaintext highlighter-rouge">Right</code>에는 올바른 값이 들어갑니다.</p>
<div class="language-scala highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">upperString</span><span class="o">(</span><span class="n">value</span><span class="k">:</span> <span class="kt">String</span><span class="o">)</span><span class="k">:</span> <span class="kt">Either</span><span class="o">[</span><span class="kt">String</span>, <span class="kt">String</span><span class="o">]</span> <span class="k">=</span> <span class="o">{</span>
<span class="nf">if</span> <span class="o">(</span><span class="nv">value</span><span class="o">.</span><span class="py">isEmpty</span><span class="o">)</span> <span class="nc">Left</span><span class="o">(</span><span class="s">"Value cannot be empty"</span><span class="o">)</span>
<span class="k">else</span> <span class="nc">Right</span><span class="o">(</span><span class="nv">value</span><span class="o">.</span><span class="py">upper</span><span class="o">)</span>
<span class="o">}</span>
</code></pre></div></div>
<p>그리고 받는 쪽에서는 다음과 같이 처리해주면 됩니다. <code class="language-plaintext highlighter-rouge">case match</code>나 <code class="language-plaintext highlighter-rouge">fold</code>를 강제함으로서 좀 더 exception handling에 신경쓰게 할 수 있습니다. 혹은, 아예 다른 값을 주는 것도 가능하니 자유롭게 받아서 써도 되고요.</p>
<div class="language-scala highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">upperString</span><span class="o">(</span><span class="n">inputValue</span><span class="o">)</span> <span class="k">match</span> <span class="o">{</span>
<span class="k">case</span> <span class="nc">Left</span><span class="o">(</span><span class="n">error</span><span class="o">)</span> <span class="k">=></span> <span class="n">s</span><span class="s">"Upperstring failed: $error"</span><span class="o">,</span>
<span class="k">case</span> <span class="nc">Right</span><span class="o">(</span><span class="n">result</span><span class="o">)</span> <span class="k">=></span> <span class="n">s</span><span class="s">"Upperstring succeeded: $result"</span>
<span class="o">}</span>
</code></pre></div></div>
<h3 id="try">Try</h3>
<p>하지만 Either는 Modern Scala에서 거의 쓰이지 않습니다. Either에 대해서는 여러 논쟁이 많습니다만, Scala 2.10부터 Either의 자리를 <code class="language-plaintext highlighter-rouge">Try</code>라는 녀석이 대체하고 난 이후부터는 거의 대부분에 상황에서 <code class="language-plaintext highlighter-rouge">Either</code> 대신 <code class="language-plaintext highlighter-rouge">Try</code>를 사용할 수 있습니다. 일단 Either가 좋지 않은 이유는 <code class="language-plaintext highlighter-rouge">Monad</code>가 아니기 때문입니다. 그래서 <code class="language-plaintext highlighter-rouge">flatMap</code>과 같은 연산도 없고, Scala의 개념인 functional programming과 약간 동떨어진 느낌도 납니다. 반면에 Try는 Either와 사용 방법이 거의 똑같으면서 <code class="language-plaintext highlighter-rouge">Monadic</code> 입니다. (Monad의 성격을 완전히 만족하지는 않지만 호환은 가능한 성격입니다) Haskell에선 Either가 Monad이기 때문에 Try 같은 개념이 없지만, Scala에선 Try를 통해 Either가 부족한 점을 보완하고 있습니다. 다음과 같이 사용합니다.</p>
<div class="language-scala highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Try</span> <span class="o">{</span>
<span class="nf">upperString</span><span class="o">(</span><span class="n">str</span><span class="o">)</span>
<span class="o">}</span>
<span class="k">match</span> <span class="o">{</span>
<span class="k">case</span> <span class="nc">Success</span><span class="o">(</span><span class="k">_</span><span class="o">)</span> <span class="k">=></span> <span class="o">...</span>
<span class="k">case</span> <span class="nc">Failure</span><span class="o">(</span><span class="k">_</span><span class="o">)</span> <span class="k">=></span> <span class="o">...</span>
<span class="o">}</span>
</code></pre></div></div>
<p>거의 Either와 사용법이 똑같죠? 하지만 Try를 쓰면 다음과 같은 문법도 사용할 수 있습니다:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Try(upperString).toOption
</code></pre></div></div>
<p>감이 오시나요? Try의 결과를 evaluation 하지 않고 Option으로 감싸서 처리하는 순간을 뒤로 미루는 겁니다. (lazy evaluation 처럼요!) Either의 경우에는 Left, Right에서 명시적으로 evaluation이 일어나기 때문에, 위처럼 사용하기 쉽지 않습니다. 바깥에서 error를 제대로 처리해주지 않는다면 결국 프로그램이 뻗을 수도 있고요. Monadic인 Try를 사용한 아름다운 코드를 하나 공유해봅니다!</p>
<div class="language-scala highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">getURLContent</span><span class="o">(</span><span class="n">url</span><span class="k">:</span> <span class="kt">String</span><span class="o">)</span><span class="k">:</span> <span class="kt">Try</span><span class="o">[</span><span class="kt">Iterator</span><span class="o">[</span><span class="kt">String</span><span class="o">]]</span> <span class="k">=</span>
<span class="k">for</span> <span class="o">{</span>
<span class="n">url</span> <span class="k"><-</span> <span class="nf">parseURL</span><span class="o">(</span><span class="n">url</span><span class="o">)</span>
<span class="n">connection</span> <span class="k"><-</span> <span class="nc">Try</span><span class="o">(</span><span class="nv">url</span><span class="o">.</span><span class="py">openConnection</span><span class="o">())</span>
<span class="n">is</span> <span class="k"><-</span> <span class="nc">Try</span><span class="o">(</span><span class="nv">connection</span><span class="o">.</span><span class="py">getInputStream</span><span class="o">)</span>
<span class="n">source</span> <span class="k">=</span> <span class="nv">Source</span><span class="o">.</span><span class="py">fromInputStream</span><span class="o">(</span><span class="n">is</span><span class="o">)</span>
<span class="o">}</span> <span class="k">yield</span> <span class="nv">source</span><span class="o">.</span><span class="py">getLines</span><span class="o">()</span>
<span class="nf">getURLContent</span><span class="o">(</span><span class="s">"http://danielwestheide.com/foobar"</span><span class="o">)</span> <span class="k">match</span> <span class="o">{</span>
<span class="k">case</span> <span class="nc">Success</span><span class="o">(</span><span class="n">lines</span><span class="o">)</span> <span class="k">=></span> <span class="nv">lines</span><span class="o">.</span><span class="py">foreach</span><span class="o">(</span><span class="n">println</span><span class="o">)</span>
<span class="k">case</span> <span class="nc">Failure</span><span class="o">(</span><span class="n">ex</span><span class="o">)</span> <span class="k">=></span> <span class="k">match</span> <span class="o">{</span>
<span class="k">case</span> <span class="n">e</span><span class="k">:</span> <span class="kt">FileNotFoundException</span> <span class="o">=></span> <span class="o">...</span>
<span class="k">case</span> <span class="n">e</span><span class="k">:</span> <span class="kt">MalformedURLException</span> <span class="o">=></span> <span class="o">...</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>위처럼 Try의 결과를 evaluation해서 사용하는 pattern을 <code class="language-plaintext highlighter-rouge">Monad transfomer</code> 라고 합니다. Monad로 wrapping 되어 있는 값들을 꺼내서 판단하고 다시 바깥으로 반환하는 거죠. map이나 flatMap을 사용할 수도 있지만 for yield를 사용해서 결과도 한 눈에 들어오고, case match도 줄이는 좋은 코드라고 생각해서 공유해봤습니다.</p>
<h2 id="결론">결론</h2>
<blockquote>
<p>Scala에서 예외 처리를 할 땐 <code class="language-plaintext highlighter-rouge">Option</code>, 좀 더 강한 처리를 해주고 싶으면 <code class="language-plaintext highlighter-rouge">Try</code>를 이용하면 된다.</p>
</blockquote>
<h3 id="references">References</h3>
<ul>
<li>https://xebia.com/blog/try-option-or-either/</li>
<li>https://stackoverflow.com/questions/25467760/scalas-either-not-having-flatmap-meaning-of-either-left-right</li>
</ul>Woong Seok KangScala에서는 JVM 기반 언어 최대의 적인 NPE (NullPointerException)를 functional하게 handling 할 수 있는 다양한 수단을 제공하고 있습니다. Scala의 exception handling 3인방인 Option, Either, Try 에 대해 알아보도록 하겠습니다!Airflow의 execution_date에 대하여2020-04-12T08:20:00+00:002020-04-12T08:20:00+00:00https://nephtyws.github.io/data/airflow-execution-date<p><a href="https://airflow.apache.org/">Airflow</a>는 Airbnb에서 시작된 Job orchestration framework로 데이터 엔지니어링 사이드에서 꽤나 많이 사용하는 도구 중 하나입니다. 저도 현업에서 production용으로 이미 사용하고 있고, 20+ DAGs, 200+ tasks를 매일매일 돌리고 있습니다. 이와 비슷한 도구로는 Spotify에서 만든 <a href="https://github.com/spotify/luigi">Luigi</a>가 있습니다. 슈퍼 마리오에서 배관을 타고 들어가는 Luigi 캐릭터를 보고 지은 이름인 듯 합니다.</p>
<p>어쨌든 두 가지 모두를 현업에서 운영해보니 각각의 도구가 가진 장단점이 명확하게 보이기 시작했습니다. 먼저 Airflow가 더 좋은 점은 Airflow에는 scheduler가 integration 되어 있어 외부 triggering에 의존하지 않고 직접 각 job을 tracking 할 수 있다는 것이 가장 명확한 장점입니다. 보통 Luigi를 사용할 때는 Luigi 자체는 job orchestrator로 사용하고, 이러한 Luigi task를 Jenkins같은 도구를 이용해서 trigger 하곤 합니다. 이렇게 사용했을 때의 문제는 Luigi와 Jenkins 사이의 결합이 탄탄하지 않고, (여기선 coupling이 tight해야 좋습니다!) job triggering이 매끄럽지 않으며, Jenkins가 SPoF가 된다는 사실입니다. 보통 Jenkins 안에는 다른 job도 섞여있기 마련이니, 그게 싫으면 별도의 Luigi용 Jenkins를 두어야 한다는 거겠죠.</p>
<p>하지만 그 점 빼고는 모든 면에서 Luigi가 낫다는 느낌을 받기도 했습니다. 일단 task간의 dependency check가 용이하고, 굳이 DAG 개념에 얽매이지 않고 task끼리 inter-dependency를 용이하게 걸어줄 수 있으며, 오늘 언급할 주제인 execution_date, 즉 marker를 check하는 것도 아주 자유도가 높아서 프로그래머가 원하는대로 로직을 설계할 수 있게 되어 있습니다.</p>
<p>제가 어떤 근거로 이러한 이야기를 하는 지에 대해 이제 천천히 얘기해보겠습니다.</p>
<h2 id="airflow-execution_date와-marker">Airflow execution_date와 marker</h2>
<p>대부분의 Job orchestration framework는 <code class="language-plaintext highlighter-rouge">marker</code> 라는 것을 이용해서 특정 job의 success/failure 여부를 확인하게 됩니다. Spark나 Hive job을 이용하다보면 HDFS에 <code class="language-plaintext highlighter-rouge">_SUCCESS</code> 와 같은 dummy file 혹은 folder가 만들어지는 것을 심심찮게 확인할 수 있는데, 이것은 Spark/Hive가 내부적으로 HDFS에 write 하는 작업이 성공했는지 실패했는지 여부를 모든 node에서 명시적으로 확인할 수 있게 하기 위하여 <code class="language-plaintext highlighter-rouge">marker</code>를 HDFS에 남겨놓은 것입니다.</p>
<p>유사한 방식으로, <code class="language-plaintext highlighter-rouge">Luigi</code>나 <code class="language-plaintext highlighter-rouge">Airflow</code>와 같은 framework들도 marker를 이용해서 job의 상태를 확인할 수 있습니다. Luigi에서는 아예 <code class="language-plaintext highlighter-rouge">file marker</code>라는 개념을 지원해서 위처럼 특정 경로에 어떤 파일의 존재 유무로 job의 상태를 확인할 수 있기도 하지만, 이것보다 좀 더 현대적인 방법은 바로 <code class="language-plaintext highlighter-rouge">DB</code>를 이용하는 것입니다. 특히 요즘처럼 하나의 HDFS를 이용하지 않고 S3와 같은 distributed object storage로 이용할 때는 더더욱 DB를 이용하는 것이 신뢰성이 높은 방법입니다. 왜냐하면 S3와 같은 object storage들의 consistency model은 <a href="https://en.wikipedia.org/wiki/Eventual_consistency">eventual consistency</a>이기 때문에 자칫하면 큰 참사가 날 수도 있습니다.</p>
<p><code class="language-plaintext highlighter-rouge">Luigi</code>와 <code class="language-plaintext highlighter-rouge">Airflow</code> 모두 DB 기반의 marker를 제공하긴 하지만, 이 두 개의 도구 사이에는 아주 큰 차이가 있습니다. 먼저, Luigi는 custom marker를 지원합니다. 사실 custom marker를 지원한다기보단 framework 차원에서 정해진 marker rule이 잘 없습니다. 사용자가 직접 marker DB를 지정하고, marker content를 지정해서 사용자의 job에서 marker를 조회해서 dependency를 확인하는 느낌입니다.</p>
<p>반면에, <code class="language-plaintext highlighter-rouge">Airflow</code>는 <code class="language-plaintext highlighter-rouge">exeuction_date</code>라는 one and only marker를 기본적으로 제공합니다. 기본적으로 Airflow는 <code class="language-plaintext highlighter-rouge">PostgreSQL</code>을 marker DB로 사용하고, 각 task instance가 success/failure 했는지 여부를 marker DB에 기록합니다. 이는 Airflow의 Web UI에서 간편하게 확인할 수 있죠.</p>
<p>여기까진 좋은데, Airflow의 execution_date는 <code class="language-plaintext highlighter-rouge">UTC based datetime</code>입니다. 이게 왜 문제가 될까요?</p>
<h2 id="airflow에서-dag-혹은-task-간의-dependency-check">Airflow에서 DAG 혹은 task 간의 dependency check</h2>
<p>Airflow에서 execution_date는 단순히 해당 TI (task instance) 의 시작일을 알려주는 것이 아닙니다. Airflow는 execution_date를 통해 각 TI에 id를 부여할 뿐만 아니라, 이것을 통해 TI를 unique하게 구분할 수 있습니다. (e.g. <code class="language-plaintext highlighter-rouge">TestJob-20200412170000</code>) 그럼 다음과 같은 상황을 예로 한 번 들어봅시다.</p>
<blockquote>
<p>매일 02시에 한 번 동작하는 batch job이 있다. 그리고 매일 08시에 동작하는 batch job은 이전 작업 (02시 batch job) 이 반드시 성공해야만 동작할 수 있다. 어떻게 확인할 수 있을까?</p>
</blockquote>
<p>Luigi였다면 그냥 <code class="language-plaintext highlighter-rouge">TestJob-20200402</code> 처럼 <code class="language-plaintext highlighter-rouge">yyyymmdd</code> 형태의 marker를 조회하면 쉽게 dependency check를 할 수 있었을 겁니다. 혹은 이게 못미덥다면, <code class="language-plaintext highlighter-rouge">yyyymmddHHMMSS</code> 단위로 해당 job의 marker를 조회하면 됩니다. 그런데, Airflow에서 dependency check를 하려면 다음과 같이 해야합니다.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">dag_time</span><span class="p">:</span> <span class="n">Time</span> <span class="o">=</span> <span class="n">dag</span><span class="p">[</span><span class="s">'schedule_time'</span><span class="p">]</span>
<span class="n">dag_timedelta</span> <span class="o">=</span> <span class="n">timedelta</span><span class="p">(</span><span class="n">hours</span><span class="o">=</span><span class="n">dag_time</span><span class="p">.</span><span class="n">hours</span><span class="p">,</span> <span class="n">minutes</span><span class="o">=</span><span class="n">dag_time</span><span class="p">.</span><span class="n">minutes</span><span class="p">)</span>
<span class="n">my_time</span> <span class="o">=</span> <span class="n">timedelta</span><span class="p">(</span><span class="n">hours</span><span class="o">=</span><span class="n">watcher_time</span><span class="p">.</span><span class="n">hours</span><span class="p">,</span> <span class="n">minutes</span><span class="o">=</span><span class="n">watcher_time</span><span class="p">.</span><span class="n">minutes</span><span class="p">)</span>
<span class="k">yield</span> <span class="n">ExternalTaskSensor</span><span class="p">(</span>
<span class="n">task_id</span><span class="o">=</span><span class="sa">f</span><span class="s">"waiting_</span><span class="si">{</span><span class="n">dag</span><span class="p">[</span><span class="s">'name'</span><span class="p">]</span><span class="si">}</span><span class="s">"</span><span class="p">,</span>
<span class="n">external_dag_id</span><span class="o">=</span><span class="n">dag</span><span class="p">[</span><span class="s">'name'</span><span class="p">],</span>
<span class="n">execution_delta</span><span class="o">=</span><span class="n">my_time</span> <span class="o">-</span> <span class="n">dag_timedelta</span><span class="p">,</span>
<span class="n">timeout</span><span class="o">=</span><span class="mi">600</span><span class="p">,</span>
<span class="p">)</span>
</code></pre></div></div>
<p>즉, 내가 check하고 싶은 DAG의 time을 가져와서 그것의 delta를 직접 계산한 후에 Sensor에 넣어줘야 합니다. 물론 이렇게 하면 대부분의 경우를 cover 할 수 있긴 한데, execution_date의 개념을 제대로 알지 못하면 헷갈릴 수도 있습니다.</p>
<p>또한 이 방법은 schedule time을 기반으로 하기 때문에, manual trigger job에 대해서는 watch 할 수 없다는 것이 문제입니다. 이때문에 job이 실패한 경우 실패한 TI에서 모든 걸 해결해야만 하고, 그냥 처음부터 다시 돌리고 싶어서 clear를 하지 않고 manual execution을 해버리면 dependency job을 제대로 찾지 못해서 job이 fail 합니다.</p>
<p>이쯤되면 <code class="language-plaintext highlighter-rouge">ExternalTaskSensor</code>를 잘못 만들었다는 생각이 솔솔 들고 있습니다. 위의 예제는 DAG를 watch하는 예제인데, 이걸 task까지 확장시키면 코드도 엄청 늘어나고 DAG 안의 task도 엄청 늘어나서 보기가 썩 좋진 않습니다.</p>
<h2 id="그래도-airflow는-잘-만든-도구다">그래도 Airflow는 잘 만든 도구다</h2>
<p>갑자기 급 마무리하는 느낌이 들긴 하지만, 어쨌든 Airflow는 production 환경에서 사용하기 적합한 도구입니다. 그런데 다른 orchestration framework에 익숙하신 분이라면 Airflow를 사용하실 때 조금 주의하실 필요는 있겠습니다. 우리가 기존에 사용하던 framework와 약간 다른 면이 있기 때문입니다.</p>
<h3 id="결론">결론</h3>
<blockquote>
<p>Airflow의 execution_date는 UTC based datetime이고, TI의 unique ID이며, 실행 시마다 정확한 execution time을 알아야 DAG/task를 watch 할 수 있다.</p>
</blockquote>
<p>제가 인지하고 있는 다양한 불편함을 언젠가는 직접 PR을 날려서 해소할 그날이 왔으면 좋겠네요. 모쪼록 이 글이 execution_date를 이해하시는 데 도움이 되었으면 좋겠습니다!</p>Woong Seok KangAirflow는 Airbnb에서 시작된 Job orchestration framework로 데이터 엔지니어링 사이드에서 꽤나 많이 사용하는 도구 중 하나입니다. 저도 현업에서 production용으로 이미 사용하고 있고, 20+ DAGs, 200+ tasks를 매일매일 돌리고 있습니다. 이와 비슷한 도구로는 Spotify에서 만든 Luigi가 있습니다. 슈퍼 마리오에서 배관을 타고 들어가는 Luigi 캐릭터를 보고 지은 이름인 듯 합니다.[ZEPPELIN-4611] Fetching rows with newline character ( ) breaks entire table2020-04-12T07:50:00+00:002020-04-12T07:50:00+00:00https://nephtyws.github.io/oss/oss-zeppelin-1<p>얼마 전 사내에서 데이터 분석가분의 troubleshooting을 도와주다가 Zeppelin의 버그를 발견했습니다. 바로 table content에 개행문자 (\n) 가 있으면 전체 table이 깨져 보이는 버그였습니다. 워낙 원인이 명확해보이는 버그라 망설임없이 Zeppelin JIRA에 <a href="https://issues.apache.org/jira/browse/ZEPPELIN-4611">report</a>를 올렸고, maintainer 분이 금방 패치해주셨습니다. 사실 너무 고치기 쉬워보이는 내용이라 직접 PR을 작성할 생각도 있었는데, maintainer 분이 굉장히 빠르게 PR을 올려주시더군요…</p>
<p>일단 실제 사용 환경에서는 <code class="language-plaintext highlighter-rouge">translate</code> 함수를 이용해 각종 개행문자를 whitespace로 치환해주니 멀쩡히 동작하는 것을 확인했습니다. 근데 이런 문제가 Zeppelin 0.8.2까지 해결되지 않았다는 게 여러모로 신기하네요. 굉장히 후진 console을 제공하는 Athena도 (?) 이미 잘 대응되어 있던 문제인데요.</p>
<p>OSS에 contribution 할 아이디어는 굉장히 많은데 아직 하나도 실현된 게 없습니다. 이 포스트를 기점으로 OSS에 기여하겠다는 의지를 불태울 계획입니다. 다음엔 Spark에 contribution한 내용으로 찾아뵙겠습니다!</p>Woong Seok Kang얼마 전 사내에서 데이터 분석가분의 troubleshooting을 도와주다가 Zeppelin의 버그를 발견했습니다. 바로 table content에 개행문자 (\n) 가 있으면 전체 table이 깨져 보이는 버그였습니다. 워낙 원인이 명확해보이는 버그라 망설임없이 Zeppelin JIRA에 report를 올렸고, maintainer 분이 금방 패치해주셨습니다. 사실 너무 고치기 쉬워보이는 내용이라 직접 PR을 작성할 생각도 있었는데, maintainer 분이 굉장히 빠르게 PR을 올려주시더군요…