<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: HJ</title>
    <description>The latest articles on Forem by HJ (@hj_lee).</description>
    <link>https://forem.com/hj_lee</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1573856%2Fd2ece865-dec2-469e-b45a-60760d3f6caf.png</url>
      <title>Forem: HJ</title>
      <link>https://forem.com/hj_lee</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/hj_lee"/>
    <language>en</language>
    <item>
      <title>Ceph의 이해(2) - backfill, scrub</title>
      <dc:creator>HJ</dc:creator>
      <pubDate>Tue, 16 Jul 2024 05:26:58 +0000</pubDate>
      <link>https://forem.com/hj_lee/cephyi-ihae2-backfill-scrub-4311</link>
      <guid>https://forem.com/hj_lee/cephyi-ihae2-backfill-scrub-4311</guid>
      <description>&lt;p&gt;재밌는 얘기는 1편에 다 몰아서 했고...&lt;br&gt;
재미없는 얘기를 할 때가 왔다.&lt;br&gt;
내부에서 어떤 작업을 하는지 모니터링 메트릭 위주로 살펴봄.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. backfill
&lt;/h2&gt;

&lt;p&gt;Backfill은 데이터 리밸런싱 작업으로, 내용 자체는 간단하다.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjgdxlmnc9e9ormklebcu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjgdxlmnc9e9ormklebcu.png" alt="before backfill" width="428" height="389"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;한 PG에 두개의 OSD를 할당했다고 했을 때, 산술적으로 50%씩 데이터를 가질 것이다.(replication은 제외하고)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxg94uiv2gk5f38m2r4p0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxg94uiv2gk5f38m2r4p0.png" alt="after backfill" width="386" height="501"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;해당 PG에 OSD를 하나 더 할당하면, 당연히 33%가 된다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;backfill은 이 작업을 칭하는데, OSD가 추가되고 삭제되는 등의 작업이 이루어질때 backfill을 수행하게 된다.&lt;br&gt;
이거 자체는 뭐 어려운 부분은 아니지만, 백필 작업은 그 특성상 큰 부하를 일으킬 가능성이 높고 마찬가지로 여러 개의 PG가 동시에 백필 작업을 수행할 수 있다.&lt;/p&gt;

&lt;p&gt;이를 'thundering herd' 현상이라고 하는데(천둥 소리?) 이를 최소화하기 위한 설정도 존재는 한다(osd_max_backfills)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdtohd427vzh73mduoo2i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdtohd427vzh73mduoo2i.png" alt="Asyncreserver" width="758" height="511"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;백필 작업은 'Asyncreserver' 인스턴스를 통해 관리되는데, 각자의 인스턴스는 큐를 통해 데이터를 관리한다. asyncreserver는 remote_reserver, local_reserver 두가지를 한 OSD가 가지고 있고, local은 OSD의 데이터를 내보내고, remote는 받아오는 역할을 하게 됨.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fobwcrf7eh6fd3f1g4x5x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fobwcrf7eh6fd3f1g4x5x.png" alt="Asyncreserver Protocol" width="441" height="542"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;두 reserver는 메시지로 관리되고&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;MBackfillReserve&lt;/li&gt;
&lt;li&gt;Ack&lt;/li&gt;
&lt;li&gt;backfill&lt;/li&gt;
&lt;li&gt;BackfillComplete&lt;/li&gt;
&lt;li&gt;BackfillCompleteAck&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;의 순서대로 데이터를 서로 재분배한다.&lt;/p&gt;

&lt;p&gt;백필을 수행할때 PG의 우선순위도 변경할 수 있는데(priority), 이건 자세히 보진 않았다. 대개 이런 옵션들은 잘 쓰는 부분은 아니라서...강제로 force-backfill 명령어 등을 사용해서 수행하게 할 수도 있는 듯.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Scrubbing
&lt;/h2&gt;

&lt;p&gt;스크러빙은 fsck같은 무결성 확인 절차로, '복제본'을 사용 시에 해당 복제본이 적절한 데이터를 가지고 있는지 판별하는 역할을 한다.&lt;/p&gt;

&lt;p&gt;스크러빙은 soft scrubbing, deep scrubbing으로 나뉘어지고, 각자의 비교 항목은 다음과 같음.&lt;/p&gt;

&lt;h3&gt;
  
  
  1) soft scrubbing
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;주기: 짧음(보통 매일)&lt;/li&gt;
&lt;li&gt;검사 항목: 데이터 블록, 메타데이터&lt;/li&gt;
&lt;li&gt;성능 이슈 : 적음&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2) deep scrubbing
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;주기: 김(보통 매주)&lt;/li&gt;
&lt;li&gt;검사 항목: 비트 체크섬&lt;/li&gt;
&lt;li&gt;성능 이슈: 큼&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(스크러빙은 정보가 너무 없다. 왤케 문서가 부실하지.)&lt;/p&gt;

&lt;p&gt;스크러빙의 주체는 'PG'이다. 무결성을 확인할 때 복제본이라고 했으니 어느 정도 당연하기도 하다.&lt;br&gt;
스크러빙을 할 때는 객체 단위로 수행을 하게 되며, 복제본에 락을 걸고 검증을 수행하므로 일관성에 대해서는 보장된다. 사실 ceph야 스토리지 시스템이라서 일반적으로 수정을 할 일이 많진 않아서 가능할듯.(수정하는 경우도 있긴 있다고 함)&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;p&gt;스크러빙이 끝나고 나면, 문제가 있는 객체가 보고될 것이고 해당 상황에 따라서 조치하면 된다.&lt;/p&gt;

&lt;h2&gt;
  
  
  끝
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;사실 이것 외에도 피어링이나 업맵 등의 개념이 있지만 개인적으로 대충 감을 잡는 정도로만 알기도 하고, 도큐가 부실해서 찾기가 좀 힘든 것 같아서 이만 정리하려고 한다. Tiering 같은거는 S3에도 있는 기능이고.&lt;/li&gt;
&lt;li&gt;사내에서도 ceph를 통해 데이터를 구성중인데 생각만큼 막 좋은 것은 아닌 것 같고 관리포인트도 이것저것 있어서 그렇게 막 엄청 편하다~ 이건 아닌듯.&lt;/li&gt;
&lt;li&gt;나중에는 직접 구성해서 실습도 좀 해볼 생각이긴 한데, 장비가 없어서 잘 모르겠다.&lt;/li&gt;
&lt;li&gt;대규모 스토리지를 복제본, 샤딩까지 다 가능하게 하면서 데이터를 저장할 수 있다는건(심지어 지원기능도 많고) 큰 메리트라, 많이 대세가 되어가지 않나 싶다.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ceph</category>
      <category>storage</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Ceph 의 이해(1)</title>
      <dc:creator>HJ</dc:creator>
      <pubDate>Fri, 28 Jun 2024 13:45:05 +0000</pubDate>
      <link>https://forem.com/hj_lee/ceph-yi-ihae1-3ac3</link>
      <guid>https://forem.com/hj_lee/ceph-yi-ihae1-3ac3</guid>
      <description>&lt;h2&gt;
  
  
  개요
&lt;/h2&gt;

&lt;p&gt;요번에 ceph 장비를 모니터링해야 된다는 말이 있어서... Ceph에 대해 좀 리서치 해봤다.&lt;br&gt;
우린 사실 Ceph 관련된 모니터링 요소만 알면 되는데, 메트릭을 분석하려면 기본지식이 좀 있어야 하니 어쩔 수 없이[?] 메트릭에 나오는 단어들 위주로 분석을 한번 해봤다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;여담인데, 요즘은 참 좋은 시대긴 하다. GPT로 일단 간단하게 단어 스크리닝하고, 애매한 부분이나 이해가 안 되는 부분등은 도큐먼트로 채워 봤는데, GPT가 틀린 말을 거의 안 했음.&lt;/li&gt;
&lt;li&gt;그래서 쪼금 틀린 부분이 있을 수 있고...어느정도 내 편의대로 생각한 부분도 있을것이다...대부분 문서로 확인은 했지만...&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Ceph?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.ceph.com/en/reef/start/intro/"&gt;Ceph Intro&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvoqdf9by7j7k2cgebkv7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvoqdf9by7j7k2cgebkv7.png" alt="ceph intro" width="800" height="697"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ceph는 redhat이 만든 클라우드 플랫폼에 적합한 스토리지...서비스 같은 거?&lt;/li&gt;
&lt;li&gt;Object storage, block device, Filesystem 모두 지원!(대단하다)&lt;/li&gt;
&lt;li&gt;Cluster로 구성 가능하고, 대규모 파일 관리에 매우 좋아 보인다.&lt;/li&gt;
&lt;li&gt;Ceph는 매우 당연하게도 데이터 분산, 복제, 무결성 등을 지원하고, 데이터 분산 같은 것들은 CRUSH 라는 해시 알고리즘을 통해 분배하고 관리한다. CRUSH에 관해서는 자세히 찾아보진 않았지만, 상당히 자주 언급됨.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  OSD, object
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F40aqubzdqcs1uofp0vzi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F40aqubzdqcs1uofp0vzi.png" alt="OSD" width="416" height="119"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;object는 '논리적 최소 단위'로서, 일반적으로 생각하는 Object Storage의 객체와는 조금 동떨어진 것으로 보인다.(ceph에서는 RADOS object라고 함)&lt;br&gt;
일반적인 Object storage와는 비슷한 점이 있는데 object는 filesystem처럼 특정 디렉토리 구조에 얽매이는 것이 아니라 'flat한 namespace'에 저장된다고 함. 계층이 존재하지 않는다.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;OSD(object storage device)는 '물리 장비의 논리적 단위'로서 쉽게 말하면 disk 정도에 매핑될 것 같다. object는 OSD에 저장되고, OSD는 실제 디스크에 파일 세그먼트를 기록하는 형식이 되겠다.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Placement Group
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvkos3kqaw4a6syny6xu1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvkos3kqaw4a6syny6xu1.png" alt="PG" width="639" height="331"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;PG(Placement group)은 '논리적 단위'인데, 대부분의 분산, 복제 작업이 이 단위를 기준으로 이루어진다.&lt;/p&gt;

&lt;p&gt;'논리적 관점'에서는 object &amp;lt; PG.&lt;br&gt;
'물리적 관점'에서는 object &amp;lt; OSD&lt;br&gt;
가 된다.&lt;br&gt;&lt;br&gt;
이 PG의 개념을 도큐먼트에서는 'layer of indirection'(간접 계층) 라고 부르는데, PG라는 간접 계층을 둠으로서 object와 OSD간의 결합도를 줄이고 유연성을 증대시키는 효과를 가져온다.&lt;/p&gt;

&lt;p&gt;대충 살펴봤을때도 PG는 대부분의 데이터 무결성에 반드시 등장하는 존재이며, 해당 존재를 통해 Client가 접근해야 할 OSD를 정확하게 계산할 수 있다고 한다. 당연히 CRUSH 알고리즘을 통해서 ㅋㅋ&lt;/p&gt;

&lt;h3&gt;
  
  
  Replication
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxiosliopim7an9k8zi5v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxiosliopim7an9k8zi5v.png" alt="Replication" width="407" height="341"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;PG의 Replication은 나름대로 단순하다면 단순한 구조로, Primary, Secondary(+@)로 이루어진다.&lt;br&gt;
Primary에 먼저 데이터를 쓰고, Primary라 secondary에 분배하는 형식으로 무난한 구조라고 볼 수 있다.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sharding
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsy61mvt9a7cyyn927nos.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsy61mvt9a7cyyn927nos.png" alt="erasure encoding" width="651" height="632"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;PG의 Sharding은 erasure coding이라고 부르는데, erasure encoding function을 통해 데이터를 분해하고 추가 패리티를 붙여 OSD에 따로 저장한다.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F929c733xq2r6gv511bkz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F929c733xq2r6gv511bkz.png" alt="erasure decoding" width="564" height="659"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;마찬가지로 데이터 통합 시에는 decoding function을 이용하게 됨. 단순하다면 단순한 구조기도 하고...예시의 그림은 shard 하나가 실패(too slow)하는 것을 전제로 했는데, 이 상황에서는 데이터를 못 읽어오는 hang상태겠지만 실제로는 복제본이 있으니까 괜찮지 않을까 싶긴 하다.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pool
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7s0aq62eeexgf6i8yp23.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7s0aq62eeexgf6i8yp23.png" alt="Pool" width="469" height="242"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pool 또한 '논리 단위'로, Cluster보다는 작은 개념이지만 일종의 파티션 역할을 하는 단위이다.&lt;br&gt;
살펴봤을때 Openstack의 project라던가, k8s의 namespace와 같은 분리 파티션 역할을 하는 것 같다.&lt;br&gt;
&lt;br&gt;&lt;br&gt;
Pool을 분리하면 해당 요소들이 달라지게 된다.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ownership/Access to Objects&lt;/li&gt;
&lt;li&gt;Number of Placement Groups&lt;/li&gt;
&lt;li&gt;CRUSH Rule&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;단순히 논리적 분리 뿐만 아니라 Crush rule이나 PG 조절, 설정 분리와 같은 것도 있기 때문에 분리가 잘 된다면 storage의 사용 용도나 장비의 여건에 따라 아예 다른 장비처럼 운용이 가능하다는 게 가장 큰 메리트.&lt;/p&gt;

&lt;h2&gt;
  
  
  전체 관계도
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0h4pe9j3i74aygiqqg00.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0h4pe9j3i74aygiqqg00.png" alt="ceph Architecture" width="444" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;단순 도식화긴 하지만, 앞의 설명을 통해 어느정도 이해가 가능할 것이다.&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  결론
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;익숙한 개념들도 있고, 생소한 개념도 있고.&lt;/li&gt;
&lt;li&gt;현대의 데이터 분산 시스템을 차용함과 동시에, object 단위를 기반으로 대규모 클러스터 시스템과 함께 쓸 수 있게 고안한 것 같다. 일반적인 Cloud-k8s 시스템에서 스토리지를 따로 찾아서 설정하고 볼륨 지정하고 이게 좀 귀찮았는데 ceph를 잘 이용하면 하나의 엔드포인트만 봐도 되니 꽤 좋아 보임.&lt;/li&gt;
&lt;li&gt;다음은 PG를 중심으로 어떻게 데이터를 관리하고 분산하고 무결성을 확보하는지 알아본다.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ceph</category>
      <category>storage</category>
      <category>cloud</category>
    </item>
    <item>
      <title>k8s Executor 적용기(2)</title>
      <dc:creator>HJ</dc:creator>
      <pubDate>Mon, 24 Jun 2024 11:44:04 +0000</pubDate>
      <link>https://forem.com/hj_lee/k8s-executor-jeogyonggi2-1dhb</link>
      <guid>https://forem.com/hj_lee/k8s-executor-jeogyonggi2-1dhb</guid>
      <description>&lt;h2&gt;
  
  
  공식 문서
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://airflow.apache.org/docs/apache-airflow-providers-cncf-kubernetes/stable/operators.html"&gt;https://airflow.apache.org/docs/apache-airflow-providers-cncf-kubernetes/stable/operators.html&lt;/a&gt;&lt;br&gt;
&lt;br&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  구성 방법
&lt;/h2&gt;

&lt;p&gt;&lt;br&gt;Airflow 공식 문서의 가장 큰 문제는 helm등 차트로만 설치를 가이드하고 있다는 점인데, 회사 특성상 오프라인 폐쇄망에 배포해야 될 수도 있기 때문에 helm으로 우선 설치하고 오브젝트를 뜯어 보는 식으로 진행을 했다. (힘들다)&lt;/p&gt;

&lt;p&gt;Airflow의 구성 요소는 webserver, scheduler, trigger, worker인데, 우린 딱히 trigger를 쓸 일이 없어서 구성을 안 해도 무방하다. &lt;/p&gt;

&lt;p&gt;webserver는 GUI를 제공하고, scheduler는 task를 분배하고, worker는 일을 한다. tigger는 특정 조건을 달성하는 task를 실행해 주는 역할을 하는데, 일반적으로는 플로우를 통해서 scheduling을 하니까 쓸 데가 많진 않다.&lt;/p&gt;

&lt;p&gt;대략적인 flow는 다음과 같았다.&lt;br&gt;
&lt;br&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  DB 초기화
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3yjaapdrlq5wabjkbx12.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3yjaapdrlq5wabjkbx12.png" alt="PostgreSQL" width="567" height="137"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;airflow의 백엔드 DB로는 PostgreSQL을 사용했고, k8s에 올라가는만큼 초기화를 위한 Job이 하나 필요했다. 해당 Job은 한번만 실행되고 db migrate, init 등을 실행해 airflow를 사용할 수 있음.&lt;/p&gt;

&lt;p&gt;helm이나 pip로 구성할땐 몰랐는데, airflow는 admin user를 따로 만들어 줘야 했다. 해당 커맨드를 통해 만들 수 있다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;airflow &lt;span class="nb"&gt;users &lt;/span&gt;create &lt;span class="nt"&gt;--username&lt;/span&gt; admin &lt;span class="nt"&gt;--firstname&lt;/span&gt; Admin &lt;span class="nt"&gt;--lastname&lt;/span&gt; User &lt;span class="nt"&gt;--role&lt;/span&gt; Admin &lt;span class="nt"&gt;--email&lt;/span&gt; lol@lol.com &lt;span class="nt"&gt;--password&lt;/span&gt; admin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;h3&gt;
  
  
  Scheduler
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2msu0kbwarzg76opaf0p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2msu0kbwarzg76opaf0p.png" alt="scheduler" width="488" height="156"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;스케줄러는 airflow scheduler 등으로 올라가는데, 몇가지 구성 요소가 필요했다.&lt;/p&gt;

&lt;p&gt;당연히 PVC는 dag, log 등으로 분리하는거고, airflow.cfg를 configmap으로 동일하게 넣어 줘야 하므로 필수라고 할 수 있다.&lt;/p&gt;

&lt;p&gt;k8s executor만의 특이한 점이라면 pod_template_file.yaml인데, 이 yaml 파일으로 worker가 만들어지고 실행된다. 얘는 다른 것들과는 다르게 core Executor가 local Executor로 설정된다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  pod_template_file.yaml: |
    apiVersion: v1
    kind: Pod
    metadata:
      name: placeholder-name
      labels:
        tier: airflow
        component: worker
        release: airflow
    spec:
      containers:
        - envFrom: &lt;span class="o"&gt;[]&lt;/span&gt;
          &lt;span class="nb"&gt;env&lt;/span&gt;:
            - name: AIRFLOW__CORE__EXECUTOR
              value: LocalExecutor
            - name: AIRFLOW_HOME
              value: /opt/airflow
            - name: AIRFLOW__CORE__SQL_ALCHEMY_CONN
              valueFrom:
                secretKeyRef:
                  name: airflow-metadata
                  key: connection
            - name: AIRFLOW__DATABASE__SQL_ALCHEMY_CONN
              valueFrom:
                secretKeyRef:
                  name: airflow-metadata
                  key: connection
            - name: AIRFLOW_CONN_AIRFLOW_DB
              valueFrom:
                secretKeyRef:
                  name: airflow-metadata
                  key: connection
            &lt;span class="c"&gt;# Dynamically created environment variables&lt;/span&gt;
            &lt;span class="c"&gt;# Dynamically created secret envs&lt;/span&gt;
            &lt;span class="c"&gt;# Extra env&lt;/span&gt;
          image: apache/airflow:2.8.3
...

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;container env 스펙을 보면 AIRFLOW_&lt;em&gt;CORE&lt;/em&gt;_EXECUTOR가 Local Executor이다.&lt;/p&gt;

&lt;p&gt;즉 이런 구조로 돌아가는 녀석이라는 거다.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F81zukmta8zdhh39cz3gk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F81zukmta8zdhh39cz3gk.png" alt="scheduler flow" width="639" height="173"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Task를 관리하는 scheduler는 worker를 pod로 따로 띄우고, Worker는 Image를 불러와 Pod로 작업을 따로 수행하게 하고(Local Executor), Pod가 실행을 완료하면 worker 또한 작업을 종료하고 다시 죽는다.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm4zlbnvecv7qucbhco85.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm4zlbnvecv7qucbhco85.png" alt="worker flow" width="662" height="173"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;결과적으로 scheduler만 남게 되고, 이 작업은 병렬로 수행되므로 작업이 몰릴때는 많은 pod을 소환[?]해서 큰 케파를 유지하고 깔끔한 아키텍쳐를 유지 가능하게 된다.&lt;/p&gt;

&lt;h2&gt;
  
  
  새로운 고민&lt;br&gt;&lt;br&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Code Entrypoint
&lt;/h3&gt;

&lt;p&gt;&lt;br&gt;말하기 앞서 k8s executor의 dag를 보면… 사실상의 Local executor처럼 동작하는 것을 볼 수 있다.&lt;/p&gt;

&lt;p&gt;cmds를 통해 커맨드라인을 실행하고, 이 실행은 image를 불러와서 실행시키는 형식이다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;first_task &lt;span class="o"&gt;=&lt;/span&gt; KubernetesPodOperator&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="nv"&gt;task_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"airflow_task"&lt;/span&gt;,
    &lt;span class="nv"&gt;image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;harbor&amp;gt;/airflow_src/&amp;lt;blabla&amp;gt;:latest"&lt;/span&gt;,
    &lt;span class="nv"&gt;cmds&lt;/span&gt;&lt;span class="o"&gt;=[&lt;/span&gt;&lt;span class="s2"&gt;"python"&lt;/span&gt;, &lt;span class="s2"&gt;"blabla.py"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;,
    &lt;span class="nv"&gt;arguments&lt;/span&gt;&lt;span class="o"&gt;=[&lt;/span&gt;&lt;span class="s2"&gt;"--execution_date"&lt;/span&gt;, &lt;span class="s2"&gt;"execution_date"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;,
    &lt;span class="nv"&gt;provide_context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;True,
    &lt;span class="nv"&gt;dag&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dag,
&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;여기서 가장 큰 문제가 발생하는데… image의 진입점 문제이다.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdoormjgefi3aljy6jdhe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdoormjgefi3aljy6jdhe.png" alt="Image flow" width="420" height="322"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;k8s executor dag는 특정 ‘이미지’를 불러와서 실행한다.&lt;/p&gt;

&lt;p&gt;dag 하나에 최소한 하나의 이미지가 있어야 된다는 점인데, 이 부분이 가장 큰 문제가 된다.&lt;/p&gt;

&lt;p&gt;사내에서는 harbor로 커스텀 이미지를 관리하고 있는데, python 이미지를 alphine 기준 최소한으로 잡았을 때도 어지간해선 100M 정도 사이즈가 나온다.&lt;/p&gt;

&lt;p&gt;즉..pipeline당 100M, 10개면 1G, 100개면 10G…&lt;/p&gt;

&lt;p&gt;이게 무한정 늘어난다고 생각해 봤을때 이미지서버에 대한 부하가 걱정될 수 밖에 없다.&lt;/p&gt;

&lt;p&gt;처음부터 논의했던 문제긴 한데, 그래서 진입점을 어떻게 하느냐에 대한 의논은 좀 했었다.&lt;/p&gt;

&lt;p&gt;이런 식으로 구성하면 용량 문제는 어느정도 해결은 할 수 있을 것이다.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fprvc11smudn43qbbn75g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fprvc11smudn43qbbn75g.png" alt="image category flow" width="462" height="720"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;최대한 공통점을 가지는 파이프라인을 묶어서 이미지를 빌드하고, Image를 최소화한 다음에 cmd를 통해 호출하는 진입점을 다르게 가는 것이다.&lt;/p&gt;

&lt;p&gt;이렇게 할 경우 총 이미지 용량은 줄어들게 된다. 물론 깔끔하진 않다….&lt;/p&gt;

&lt;p&gt;나 같은 경우에는 이런 구조를 싫어해서, 파이프라인 갯수가 감당이 안 될 정도가 아닌 이상 버텨 보기로 하고 일단 뭉개고 있는 중[?]. 사실 하드웨어가 제일 싸니까~&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Pod capacity
&lt;/h3&gt;

&lt;p&gt;&lt;br&gt;이게..생각보다 Pod가 &lt;strong&gt;많이 뜬다.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;그리고 생각보다 &lt;strong&gt;메모리를 많이 먹는다.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F59h3gg7th8n4nzsuwe2b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F59h3gg7th8n4nzsuwe2b.png" alt="Fail flow" width="662" height="178"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;테스트시 제일 많이 발생한 문제인데, Pod이 memory가 부족하거나 하는 경우 제대로 된 작업을 하지 못하게 되고, worker는 pod는 띄웠는데 일이 안 끝나니까 그냥 Fail 처리해 버린다.&lt;/p&gt;

&lt;p&gt;어지간하면 Fail retry로 수행되기야 하겠지만, Airflow의 특성상 특정 시기에 작업이 몰릴 수도 있으니 작업을 분배하거나 node를 분배하는 등의 설정이 따로 필요할 수 있다.&lt;/p&gt;

&lt;p&gt;cfg를 통한 동시성 설정이나 동시수행 등의 설정을 바꿔서 분배하면 좀 나아질 듯.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# DAG&lt;/span&gt;
start_task &lt;span class="o"&gt;=&lt;/span&gt; KubernetesPodOperator&lt;span class="o"&gt;(&lt;/span&gt;
    ...
    &lt;span class="nv"&gt;startup_timeout_seconds&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;30000 &lt;span class="c"&gt;# pod이 끝날때까지 3만초 기다림&lt;/span&gt;
    ...
&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# cfg&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;core]
parallelism &lt;span class="o"&gt;=&lt;/span&gt; 8
&lt;span class="o"&gt;[&lt;/span&gt;scheduler]
dag_concurrentcy &lt;span class="o"&gt;=&lt;/span&gt; 8
max_active_tasks_per_dag &lt;span class="o"&gt;=&lt;/span&gt; 4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;h2&gt;
  
  
  소회&lt;br&gt;&lt;br&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;사실 대부분 내 욕심으로 만들어진 아키텍쳐이다. 아직 플랫폼 초기니까…가능하겠지.&lt;/li&gt;
&lt;li&gt;생각만큼 k8s의 장점이 장점으로 와닿지 않는다. 작은 구조는 작은 구조대로 문제가 생기는데, 이것이 대규모화 됐을 때의 문제 스케일이 훨씬 클 것 같아서 그게 가장 걱정이다.&lt;/li&gt;
&lt;li&gt;구조가 직관적이고 쓸데 없는 구조가 없이 깔끔하다는 점은 좋지만, 그거 외에 단점이 이것저것 너무 많아서 누구보고 쓰라고 하면 딱히 추천하고 싶진 않다. MQ  하나 관리하지 않는다고 생기는 단점이 큰 것 같기도 하고.&lt;/li&gt;
&lt;li&gt;쓰진 않았지만 사실 가장 귀찮은 건 인자값 전달이었는데, 다른 operator와는 달리 변환이 필요하거나 하기도 해서 좀 불편하다.(cmds를 사용하니…).
쉽게 전달하기 위해 xcom이라는 게 있다던데 훑어보니 이게 더 불편해 보여서 아예 시도도 안함.&lt;/li&gt;
&lt;li&gt;그럼에도 CI/CD가 잘 되어 있거나 하는 환경에선 메리트가 있을 것이다. MSA 구조 등엔 도움될 부분이 있을 것임.&lt;/li&gt;
&lt;li&gt;특히 배포가 쉽다는 점에선 장점이 있을 것이다. (사실 이것도 MQ쓴다고 배포가 어려울 것은 없다만)&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>airflow</category>
      <category>datapipeline</category>
      <category>k8s</category>
    </item>
    <item>
      <title>k8s Executor 적용기(1)</title>
      <dc:creator>HJ</dc:creator>
      <pubDate>Tue, 04 Jun 2024 13:49:55 +0000</pubDate>
      <link>https://forem.com/hj_lee/k8s-executor-jeogyonggi1-1n9e</link>
      <guid>https://forem.com/hj_lee/k8s-executor-jeogyonggi1-1n9e</guid>
      <description>&lt;h2&gt;
  
  
  1. 발단
&lt;/h2&gt;

&lt;p&gt;airflow에 대해 잘 모르던 시기에 사정상 내가 구축하지 않은 시스템을 유지보수할 일이 생겼다. &lt;/p&gt;

&lt;p&gt;airflow를 좀 아는 팀원이랑 같이 가서 살펴보니 celery executor를 사용하는 전통적인(?) airflow 구조였음.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzf43cunpmyx5r8ekkty5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzf43cunpmyx5r8ekkty5.png" alt="celery architecture" width="569" height="216"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;scheduler가 job을 배치하고, worker가 가져가서 수행한다는 전형적인 구조인데,&lt;br&gt;
시스템에 문제가 생긴 이유는 RabbitMQ의 네트워크 문제.&lt;/p&gt;

&lt;p&gt;실제로는 scheduler, worker, webserver가 동일망이었으니, 그림은 이렇게 됨.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnqsvjpa1tgivs25wzmw1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnqsvjpa1tgivs25wzmw1.png" alt="celery fault" width="591" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;airflow 또한 systemctl로 올라가 있었는데, systemctl retry가 안돼서인지 뭔지 worker가 모두 죽어버린 상황.&lt;/p&gt;

&lt;p&gt;담당자는 이 사실을 모른 채로 꽤 많은 시간이 지났고...뭐 어쨌거나 근본적인 원인분석이 됐으니 차후에 다시 기동하는 등의 사건 등이 있었으나 중요한 건 이 다음에 든 의문이었다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;왜 굳이 이렇게 구성해야 하는가?&lt;/strong&gt; 하는 의문....&lt;br&gt;
&lt;br&gt;&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. 고민
&lt;/h2&gt;

&lt;p&gt;사실 내가 airflow에 대해 무지한 면이 많았으므로 애초에 왜 메시지큐를 사용해야 했는가 하는 의문부터 출발한 것인데, &lt;/p&gt;

&lt;p&gt;celery 기반의 airflow scheduler는 task를 job으로 만들어서 MQ에다 하나씩 던져준다.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyxhws1qq8fki8jbdvwnd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyxhws1qq8fki8jbdvwnd.png" alt="scheduler" width="798" height="498"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;이렇게 구성하면 장점은&lt;/p&gt;

&lt;p&gt;1) 많은 수의 job을 병렬 처리할 수 있다.&lt;br&gt;
2) 전체적인 작업 capacity가 늘어난다.&lt;br&gt;
3) worker가 분산처리되므로 자원분배 측면에서 리소스를 잘 활용할 수 있다.&lt;/p&gt;

&lt;p&gt;단점은&lt;/p&gt;

&lt;p&gt;1) 관리포인트가 늘어난다.&lt;br&gt;
2) 유지보수에서 봐야 되는 것들이 더 늘어난다. (남의 시스템이라 더 모르겠더라.. )&lt;br&gt;
3) worker, scheduler, webserver 등을 다 따로 체크해야 한다.&lt;br&gt;
(솔직히 관리포인트 말고는 별 차이는 없지만, 다른 아키텍쳐를 써보고 싶은 마음이 컸다. 이거 대부분 k8s 쓰면 해결은 됨.)&lt;br&gt;
&lt;br&gt;&lt;br&gt;
&lt;br&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. 해답
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;팀원한테 좀 징징대봤다.&lt;/li&gt;
&lt;li&gt;아 우리 어차피 pipeline도 얼마없는데 관리도 힘든 MQ 꼭 써야돼요?
( 나는 MQ에 대한 슬픈 사건이 꽤 많다... )&lt;/li&gt;
&lt;li&gt;이거 안쓰고 localexecutor 같은건 문제가 있겠죠? 이건 안되겠고.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;이러한 질문에 팀원이 대답해줬다.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;어 그거 라인인가 어딘가에서 k8s executor라고 쓰던데요.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;찾아보니&lt;br&gt;
1) worker=pod이라서 MQ 없어도 되고&lt;br&gt;
2) k8s 환경에서 선형적으로 작업량 늘어나도 분산처리 쉽고&lt;br&gt;
3) image로 소스 처리하니까 CI/CD 쓸수있어요.&lt;/p&gt;

&lt;p&gt;대략 이런 구조가 된다.&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7nee2jc4tcwr9tejedqj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7nee2jc4tcwr9tejedqj.png" alt="k8s executor" width="800" height="594"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;scheduler는 task를 job으로 만들고, 해당 job 하나하나는 해당하는 소스(이미지)를 불러와서 pod으로 기동된다.&lt;/p&gt;

&lt;p&gt;우리는 하버를 쓰기때문에 배포할 수도 있고, 나중에는 확장해서 CI/CD까지 노려볼 수 있었다. 내가 그렇게 하고 싶어하던 전체 자동화!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;안 할 이유가 없다&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;라고 생각했고, 안이한 생각이었음을 뒤늦게 깨닫게 된다....&lt;/p&gt;

</description>
      <category>airflow</category>
      <category>datapipeline</category>
      <category>k8s</category>
    </item>
  </channel>
</rss>
