Kafka는 분산 이벤트 스트리밍 플랫폼이다.

대부분의 실시간 서비스가 Kafka를 채택하는 이유는 단순히 빠르기 때문만이 아니라, 속도와 안정성이라는 서로 충돌하는 두 요구사항을 동시에 만족한다는 점 때문이다.

카프카를 처음 접했을 때 가장 놀랐던 점은
“이 정도 속도로 흘러가는데, 복제 기반 고가용성까지 챙긴다고?”
라는 의문이었고, 실제로 Kafka는 아래와 같은 구조적 특성 덕분에 이를 달성한다.


1. Random I/O 제거 (append-only 로그 구조)

Kafka는 파티션을 append-only log 로 저장한다.
즉, 항상 파일 끝(offset의 마지막)에 순차적으로 쓰기 때문에:

  • 디스크 seek이 거의 발생하지 않고
  • 디스크의 최대 sequential write 성능을 그대로 끌어다 쓸 수 있다
  • HDD/SSD 가리지 않고 높은 throughput이 난다

쓰기뿐 아니라 읽기도 page cache hit이 극대화되어 빠르다.


2. 네트워크 병목 최소화 (zero-copy + page cache)

일반적인 애플리케이션은
디스크 → 커널 → 유저 공간 → 커널(socket) → 네트워크
이런 복잡한 경로로 데이터를 이동시키지만,

Kafka는 Linux의 page cachesendfile(=zero-copy) 를 적극 활용한다.

  • 디스크 파일을 읽어도 실제로는 page cache(RAM)에서 처리되고
  • 데이터가 유저 공간을 거치지 않고
  • NIC가 DMA로 직접 가져가도록 한다

즉, 디스크·CPU·네트워크 병목을 동시에 제거하는 구조다.
(RAM이 부족하면 page cache가 줄어들어 성능만 조금 떨어진다)


3. 파티션 단위 리더 구조

파티션 단위로 leader/follower가 존재하며,
leader는 파티션 단위로 고르게 분산된다.

이 덕분에:

  • 파티션만 늘리면 그대로 수평 확장된다
  • 컨슈머도 파티션 개수에 따라 처리량이 선형 증가한다
  • 운영적으로도 브로커/컨슈머 부하를 쉽게 조절할 수 있다

리소스만 받쳐주면 확장은 매우 간단하다.


4. Pull 기반 Consumer (Backpressure 분리)

기존 MQ들은 push 모델이라
컨슈머가 느려지면 브로커가 밀려 전체 파이프라인이 병목에 걸렸지만,

Kafka는 pull 모델이다.

  • Consumer가 스스로 속도에 맞춰 가져감
  • Broker는 단순 저장소 역할
  • Retention 기간 동안 원본 데이터 유지

덕분에 컨슈머 속도 저하가 브로커 안정성에 전파되지 않는다.


5. Replication도 Pull 기반 (ISR 유지)

Kafka의 follower는 leader가 push하는 것이 아니라
스스로 leader의 로그를 pull해간다.

  • ISR(In-Sync Replicas)을 유지하여 follower가 최신 offset에 거의 근접
  • leader 장애 시 follower가 빠르게 리더로 승격
  • 복제 오버헤드도 leader 스레드를 막지 않음

즉, 고가용성을 유지하면서도 leader의 write 성능이 저하되지 않는 구조다.


6. Batch 기반 Produce/Consume

Kafka는 메시지를 하나씩 처리하지 않는다.
Producer와 Consumer 모두 batch 단위로 데이터를 주고받는다.

  • 네트워크 왕복 횟수 감소
  • 압축 효율 증가
  • consumer가 데이터 특성에 맞춰 batch size 조절
  • 불필요한 소/대 메시지 혼합 문제 완화

대형 메시지(수십 MB)는 Kafka 기본 설계에 맞지 않기 때문에
작은 메시지 다량 전송이 이상적인 구조다.

 

Kafka가 완벽한 시스템은 아니다. 하지만 기업들이 이 기술을 꾸준히 선택하는 데에는 분명한 이유가 있다.
일반적으로 서로 충돌하는 두 특성(속도와 안정성)을 동시에 달성하기 위해, 데이터 파이프라인의 모든 단계를 뜯어보고 최적화한 구조적 설계가 놀랍다.

Meet Up이 이루어진 지 한 달이 넘었지만 늦은 요약 및 소감을 적어본다

1. 다른 Meetup과의 분위기 차이

  1회 Meetup은 각자 현장에 있는 개발자들이 모여 나누는 성토대회(?) + 일부 현업들이 가진 답답함을 이야기하며 네트워킹 하는 분위기였다. 당시 설문 조사에서 RPA의 시장 수요는 유지 또는 축소 경향이 있었다. 2회는 완전히 성격이 바뀌어서 회사에 있는 개발자와 RPA 학원생분들의 상담 분위기에 가까웠다. 취업을 하려면 어떤 것을 준비해야 하는지 등의 컨설팅을 짝지어서 해줬던 기억이 있다. 2회 때 RPA 시장 분위기는 '이젠 점점 축소될 것이다' 하는 전망이 지배적이었다. 이유는 명확했다. AI가 나온 시점에서 대체서비스이 비해 구축비용이 너무 비쌌기 때문이다. 아니다 다를까 3회에서는 실제로 전통적인 RPA 솔루션을 이용하지 않고 fancy한 ETL을 구성해 업무 대시보드를 만든, 열정적인 현업분의 발표가 있었다. 3회의 분위기는 전체적으로 RPA의 구성이 바뀌고 AI와 결합되리라는 예측이 많았다.

2. Meetup에서 나왔던 주제들

  이번 Meetup에서 나온 주제는 다음과 같다.

   - AI와의 협업으로 사내 데이터를 정리, ETL 파이프라인과 대시보드를 만든 비개발자 사례

   - SLM을 소프트웨어에 심어 C레벨의 요구사항을 충족(?)한 사례

   - 앞으로 RPA와 AI가 Value Chain에서 담당하게 될 역할

  첫번째 발표는 2회 Meetup에서 예상됐던 그림이 실제 사례로 튀어나왔다. "어차피 더 싸게 구축할 수 있고, 비개발자 입장에서 유연하게 대체할 수 있는 툴이 나오는데 기존 RPA가 비교우위를 가질 수 있을까? 훨씬 싸게 할 수 있다면 누군가 반드시 그렇게 할 거다."의 사례가 눈앞에 나타났다. 심지어 부산에서 Meetup 참가를 위해 회사에서 출장 결재를 받아 올라오신 분이었다. 다양한 시청각 자료와 함께 발표를 하셨다. 기억이 나는대로 요약하자면,

  - 사내 활용하면 좋을 것으로 보이는 센서 데이터, 재무 데이터가 있었다. 센서 데이터는 아예 활용을 못 하고 있었고, 재무 데이터는 Report를 위해 루틴화된 엑셀 작업을 진행했다.

 - 이 데이터의 활용을 자동화, 시각화하기 위해 ChatGpt에게 전체적인 계획과 요구사항을 입력하고, 하라는대로 따라하고 안 되면 다시 피드백하면서 작업을 진행했다.

 - 결과적으로 특별한 유료 결제없이 사내 대시보드를 만드는 것에 성공했고, 이를 활용해 타이어 교체 주기 최적화 등 연간 5억원 이상의 절감효과를 C레벨 앞에서 발표했다.

 - 다른 나라의 팀에서 시찰(?)을 왔는데, 그 팀에서도 매우 훌륭한 방향성이라고 감탄했다.

구조적으로만 보면 데이터 엔지니어링에서 바라보기에는 대량 데이터를 다루기엔 견고하진 않아 보이지만, 이미 필요한 수준에서는 충분히 사용 가능한, 훌륭한 아키텍쳐였다. 문제 해결에 집중한 과정을 듣고 있으니, 이분이야 말로 내가 아는 '개발자' 개념에 부합하는 분이었다. 개발자는 단순히 IT적인 방식을 차용하는 사람이 아니라, 문제를 인식하고 해결하기 위해 프로세스나 IT적인 해결방식을 고민 후 액션하는 사람이기 때문이다.

  두번째 발표는 SLM을 장착한 소프트웨어 개발 사례였다. 기존 개발된 제품에 SLM을 붙인 사례인데, 흥미로운 점은 솔루션 업체 입장에서 꽤 현실적인 대응으로 보였다는 거다. C레벨에서는 'AI 붙인 거 개발해 와' 하지만 현실적으로 LLM을 붙여두면 클라우드를 쓰지 않는 사이트에서는 무용지물이기 때문이다.

  세번째 발표는 AI와 RPA가 엔터프라이즈에서 어떤 역할을 맡아야 하는가에 대한 문제였다. RPA는 기존 프로세스에서 담당하던 일을 하되, AI는 symentic한 요소들을 데이터 분석해서 쓰면 되는 거 아닌가를 골자로 한 발표였다. 보통 데이터 대기업에서는 DAE라고 해서 이를 위한 analytic engineer들이 있는데 이 역할을 AI가 도와주면 어떤가 하는 내용으로 보였다.

3. 앞으로 자동화가 발전하게 될 방향성 예상

  이제 주요 RPA 솔루션 업체들도 RPA라는 말 자체를 버리고 Automation이라는 말을 쓰기 시작했다. Automation 구현 과정에서 툴을 가리지 않고, LLM 또는 SLM이 접근 가능한 시스템에서 직접 무언가를 수행하는 방식으로 유연하게 구축될 가능성이 높다. 요즘 유행하는 MCP가 그런 형태를 위한 전형으로 보인다. 핵심적인 문제는 2가지다. 첫번째는 AI 자동화 과정에서 검증을 위한 프로토콜들을 잘 만들어야 한다는 점이다. 리스크가 높은 프로세스들은 어떤 index를 통해 '사람이 판단해야 될 사안'의 마지노선을 설정하게 한다거나, 결과 검증 자체도 AI에 맡기고 검증을 통과하지 못 하면 사람에게 간다거나 하는 형태가 예상된다. 두번째는 KPI 설정이다. 회사에서도 AI를 쓰게하고 싶다. 다만 자동화 형태로 구축하는 것을 붐업 시키기 위해선 KPI가 적절해야 한다. 가령 RPA 시절에서도 '자동화한 프로세스 개수'로 KPI를 준 경우와 '자동화를 통해 save한 돈'을 KPI로 설정하는 경우의 효율성 차이가 꽤 컸다. 측정 cost와 회사의 핵심 업무 등을 고려해 적당한 밸런스 체크가 중요하다. (물론 대부분의 회사 C레벨은 '내가 써보니까 좋던데? 이거 이용해서 뭐 만들어봐' 정도의 탑다운으로 진행하고 있어 보인다)

  추가적으로 이를 위한 CoE는 아직 존재 가치가 충분해 보인다. 각자의 러닝 커브가 다르고 원래 이런 비지니스 프로토콜에 적응해 있던 인원들은 거의 없다. 실제로 Meetup에서 첫번째 발표를 하셨던 분도 따로 팀을 구성했으나, 따라오는 인원은 소수라 develop에 어려움이 크다고 하셨다. 앞으로는 회사에서 도메인 지식을 갖춘 개발자들을 포함해 CoE를 조직하리라 본다.  CoE에서 Asset과 MCP 등을 빠르게 세팅할 수 있게 유도하고, 각 도메인 워커들은 그걸 활용해 심화 데이터셋, 프로세스를 만들어 내고, 검증 방향성을 제시하는 피드백이 자주 일어나지 않을까 싶다. 크게 봐서는 BI쪽에서도 DAE 역할로 상당 부분 활용될 수 있을 것으로 기대한다. 조만간 이걸 종합해서 심화 포스트를 하나 작성해 봐야 겠다.

1. 서론

 - 재화나 서비스를 판매하는 기업은 판매 채널을 가지고 있다. 자사채널, 메타서치, 메타부킹 등으로 나뉘지만 본질적으로 마케팅 관점에서는 display, click이 일어나는 공간이고, customer의 입장에서는 view, click, book(purchase)를 하는 곳이다.

 - IT에서 대부분 이런 행위는 API나 EDI 등으로 연결된 Data Pipeline에 의해 일어난다. 결국 Request - Response 구조다.

 - 문제는 Request - Response / View - Click - Book 사이에 일어나는 데이터 액션이 1:N 또는 1:N:N 같은 형태로 일어난다는 점이다.

 - 심지어 각 scope에서도 semantic normalization → label unification → aggregation 과정이 필요하다. (결과적으로 Response는 같은데 Request 조건이 다름. 가령 항공권을 검색할 때, ICN-NRT나 SEL-NRT는 같은 Response)

 

2. 본론

a. 1:1로 정보를 degrade 시키는 경우

 - 1:N으로 일어난다고 하더라도 Business 요구사항에서는 직관적인 1:1을 선호한다. 결국 big data로는 큰 흐름을 보고자 하기 때문이다. 직관적이라 의사결정자에게 어필하기 쉽다는 것도 큰 장점이다.

 - 1:1로 정보를 degrade 시킬 때 가장 좋은 방법은 request에 있는 요청 정보들 중 키값을 조합해 대표성을 띠는 유니크 키로 만드는 것이다. 가령 채널 + 검색어 + 검색(필터)조건 등이다.

 - 1:1로 만들기 때문에 다른 어떤 케이스보다 semantic normalization → label unification → aggregation가 중요하다. 특히 TOP N 같은 metric을 본다면 필수다.

b. 1:N으로 붙이는 경우

 - 1:N으로 붙이면 raw data를 그대로 밀어넣기 때문에 ETL 과정은 편할 수 있다. ETL 관점에서는 capacity가 거의 유일한 관리 point이다.

 - 데이터 사용자 기준에서는 관계를 1:N으로 만드는 dimension을 꼭 모두 넣어서 분석해야 한다. 그렇지 않으면 request의 metric들이 중복으로 계산되기 때문이다.

 - 다만 중복으로 계산하더라도 정확한 지표가 아닌 Volume index로 보고 상대적 크기 지표로 활용한다면 괜찮을 수도 있다. 가령 N에 해당하는 것도 넓게 생각하면 display 되는 scope으로서 가중치를 부여할 수 있기 때문이다. (더 포괄적인 검색어에 대해 더 높은 가중치 부여해 business 의사 결정)

 - 특히 데이터가 복잡한 경우 고의로 request to book(purchase)을 만드는 게 더 직관적일 수 있다. 그래도 분석 기간은 단기로 잡는 게 좋다. 보통은 시장의 시즌이 존재하기 때문이다.

 - 1:N:N도 필요하다면 확대해서 쪼개고 컨셉마다 dataset을 만드는 것이 좋을 수 있다. 위에 설명한 것 같이 기간은 되도록 너무 길지 않게, dimension 생략에 따른 부작용을 잘 이해해야 한다.

c. N:N으로 붙여야 하는 경우

 - 아무리봐도 N:N으로 느껴진다면 semantic normalization → label unification → aggregation 과정을 거치지 않았거나, 응답간의 교집합이 자주 존재하는 경우다. 최근에는 AI를 통해 semantic normalization이 가능한 영역이 더 넓어졌다고는 하지만, 분명히 다르게 봐야 되는 케이스들이 존재한다.

 - 가령 검색에서 제주 놀거리, 제주 여행, 제주 투어, 제주 액티비티를 검색한다고 하면, 어떤 것들을 같은 것으로 묶고 어떤 것은 다른 것으로 묶을지 쉽게 판단하기 어렵다. request의 intent를 통일시킬 수 있는 정보가 부족하기 때문이다.

 - 그래서 이런 데이터는 큰 topic 단위로 세분화시켜 다양한 dataset의 컨셉을 만들어야 한다. 사업이 상대적으로 영세하고 검색데이터가 크지 않다면, 이런 분석은 후순위로 밀어두는 것이 더 낫다. 반대로 사업 규모가 크다면 기획실로부터 다양한 컨셉들이 나와 각 사업부서의 대시보드가 꾸려져야 한다.

 - 대부분은 좀 더 실무자에게 적합한 대시보드일 가능성이 높다. 위로 report 된다면, 보통은 trend 추적을 위해서이며  영역별 TOP N개가 선택된다.

 

3. 결론

 - 거의 대부분의 마케팅 조직, 기획 조직에서 활용하는 데이터기 때문에, ETL을 주로 하는 포지션일지라도 도메인에서 무엇을 쪼개어 분석하고자 하는가에 관심을 가져야 이런 설계를 진행할 수 있다.

 - 특히 이 고민은 최소한 datalake에서 BI로 넘어가는 상황에서 필수적으로 해야 한다. 이 컨셉을 각각 따로 잡고 설계하고 데이터 사용자에게 설명해주지 않으면, 도메인 유저들은 데이터를 불신하기 시작한다. 데이터 불신이 장기화 되면 데이터 없는 의사결정 습관이 생기게 되고, 직관으로 의사결정하는 것을 '경험'이라고 포장하는 문화가 만들어 진다. 데이터를 근거로 들지 않는 '경험'은 그 규모가 커질수록 위험하다. 그것이 장기화 되면 조직을 무너뜨리는 레거시가 된다.

Multi-Dimension 데이터 결합 시 자주 마주치는 문제 상황

완전히 같은 dimension으로 aggregation된 data를 결합할 때는 정합성 확보가 쉽다.

단순히 left join이나 outer join을 수행해도 무방하다.

그러나 실무에서는 어떤 dataset에 다른 dimension이 요구되는 상황이 흔하다.

예를 들어, 어떤 상품의 조회수와 구매수에 대한 dataset을 만들려고 한다.

그럼 조회수 datalake와 구매수가 있는 data warehouse에서 각각 data를 extract하고 결합하는 프로세스를 거친다.

이때 조회수를 aggregate하는 dimension과 data warehouse를 aggregation 하는 dimension을 다를 수 있다.

흔히 이런 경우에 자주 요청되는 것은 '공급업체' 같은 것들이다. '조회'를 할 때는 대부분 공급업체를 따로 요청하지 않는다.

보통은 조회 결과가 나온 후 필터되는 형태다. (물론 뭐 검색 데이터의 request에 공급업체가 필수라거나 하면 괜찮겠지만)

그렇게 되면 조회수가 공급업체 차원과 조인되면서 조회수가 공급업체 수만큼 중복 집계되는 multiply 현상이 발생한다.

이런 dataset의 사용자는 분석 시, 공급업체를 반드시 dimension으로 포함하거나, 필터링 equal 조건으로 지정해야 정확한 집계 결과를 얻을 수 있다.

 

결론

이렇게 결합된 데이터셋은 주로 마케팅, 사업 기획 부서에서 BI 툴을 통해 분석에 활용된다.

전환율(conversion rate) 분석과 같이 여러 dimension을 결합하는 데이터셋은 이러한 dimension 불일치 문제를 반드시 고려하도록 BI툴 사용자에게 가이드해야 한다.

 

상황: 특정 condition에서 보정을 위해 grouping & apply 적용

이슈: 재계산되지 않은 컬럼은 str, 재계산된 컬럼은 float인 상태에서 .str.replace 후 astype(float)으로 형변환하니 기존 float값이 모두 NaN이 됨(정확히는 기존 컬럼에서 %를 없애고 숫자로 convert하는 과정. 재계산하는 컬럼은 이미 계산을 위해 %가 없어진 상태.)

원인: accessor가 'str로 변환한 다음에 replace하고 형변환'처럼 보이지만, 실제로는 해당 시리즈가 str임을 전제하고 벡터작업을 함. 따라서 str이 아닌 값들은 type 정보를 잃어 버리게 되고 type 정보를 잃어버린 value들은 형변환 시 na값이 됨

 

결론: 시리즈 타입을 확신할 수 없다면 accessor 쓰기 전에 웬만하면 형변환을 명시적으로 해주도록 하자. 웬만하면 수집, 추출할 때 data source에서 type이 정렬되어 들어오긴 하지만, 그렇지 않은 data source도 있을 수 있다.

1. baseDir 및 하위 filter는 files로 대체하자

 - baseDir은 웬만하면 쓰지 말자. Directory로 지정해서 쓰면 디버깅할 때, 어떤 작업에서 정확히 어떤 parquet가 쓰였는가를 찾기가 거의 불가능하다.

 - 이것때문에 아예 baseDir에 있는 파일을 미리 삭제하거나 이동시키고 작업하는 케이스도 있는데 그럼 이제 아예 작업했던 데이터를 까볼 방법이 없어진다.

 - 특히 local에 떨군 parquet를 사용해 load한다면, directory에 있는 모든 파일을 업로드해 버린다. files를 같이 써도 둘 다 적용돼 버린다. ingestion 성능을 위해서라도 필요하다.

 - files로 설정하고 spec 로깅을 남기면 spec 로깅만 보고 바로 어떤 파일이 문제였나 알 수 있다.

 - 공식 문서 기준으로 그냥 local이 아니라 s3 같은 곳에서 가져오는 경우 files가 우선이라고 하던데, 그렇다고 한들 굳이 baseDir을 쓰는 장점이 없다. spec 작성을 손으로 할 거면 모를까.

2. transformSpec을 사용하기 보다는 전처리하고 그대로 올리자

 - data type을 위해서 dimensionsSpec을 쓰는 건 ingestion에 크게 지장이 없는데, transformSpec은 데이터가 크면 ingestion에 영향을 끼친다

 - 결과적으로 transformSpec을 쓰면 worker가 해야 할 일을 druid한테 시키는 행위가 된다. 개별 worker 단위에서 어차피 데이터를 extract해서 메모리에 load하고 있는 시점이 있을텐데 굳이 druid에 넘기는 건 합리적이지 않다. (심지어 둘 중에 일반적으로는 druid 리소스가 더 귀하지 않나 싶다.)

 - 문제는 이를 활용해 만들어진 dataset은 안 쓰고 싶다고 일부만 바꾸지는 못 하는 것처럼 보인다. 최소한 나는 그랬는데, Success로 뜨고 기존 __time에 해당하는 segment가 일부 지워지는 현상을 겪었다.

3. granularitySpec

 - segmentGranularity, queryGranularity 이렇게 2개가 핵심.

 - segmentGranularity는 데이터를 저장할 때 쪼개는 시간 단위인데 기본적으로는 data의 가장 작은 단위랑 맞춰주는 게 안전하다. 데이터 활용에 대한 요구사항을 어느 정도 숙지하고 있다면, 그 부분에 맞춰서 지정해주면 좋다. 일반적으로는 HOUR 미만으로 들어가려면 최신 데이터를 빠르게 따라가는 topic이라는 거라 파이프라인 전체 관리를 타이트하게 해야 한다.

 - Load하는 관점에서 보면, segmentGranularity가 MONTH라면 5월 레코드를 하나만 넣어도, 기존 5월 data는 모두 삭제되고 지금 넣은 레코드로 업데이트 된다. MONTH로 해놨는데 5월 30일 데이터가 이상하다? 그럼 5월 전체를 다시 넣어야 된다. 하지만 데이터 활용을 거의 주로 월단위로 한다면? 그땐 선택이다. 가용 리소스 대비 데이터가 너무 크다? 그럼 불편해도 MONTH 쓰자. 아니라면 웬만해선 DAY를 초과하지 않는 걸 추천한다.

 - queryGranularity는 rollup하는 시간 단위다. druid가 group by 성능이 좋은 가장 큰 이유는, 미리 agg를 다 해놓는다는 거다. 일종의 답안지를 미리 작성해 놓는 건데, 이건 웬만하면 raw data의 최소치에 맞추는 게 좋다. 원본이 HOUR면 HOUR로 DAY면 DAY로 하자. 집계를 해버리는 거라 데이터의 사용 입장에서 시간 해상도를 더 낮추기 어렵기 때문이다. 이건 너무 크게 하면 마치 라면을 짜게 끓인 거랑 비슷하다. 밍밍하면 소금 더 타면 되지만 짤 때 물을 더 타면 이미 그건 우리가 아는 라면이 아닐 가능성이 높기 때문.

4. "dropExisting": true

 - 이건 드루이드에 있는 약간의 변태성 때문에 효율이 생기는 옵션이다. 드루이드는 seg를 교체하면 그 전에 이 seg 뭐였어 라는 걸 다 남겨두기 때문에 이 옵션을 주지 않으면 druid 저장 공간이 빠르게 차는 경험을 하게 된다.

 - superset 같은 BI툴을 연결해 사용한다면, 더더욱 latest data만 활용하기 때문에 웬만하면 기본 옵션처럼 넣는 것을 추천한다.

 - 대부분 데이터 크기가 엄청 크지 않다면 드라마틱한 효과는 없겠으나, 드루이드 관리자가 슬퍼지지 않게 설정해주자

1. 이슈: config로 쓰고 있던 google sheet 문서가 손상되어 접근이 불가능한 상태. 사본 만들기 및 import range 함수를 통한 불러오기도 내부 에러로 실패. 중요한 hourly dag였기 때문에 바로 fix하고 배치가 돌아야 하는 상황.

2. 추정 원인: 구글 내부 에러(잦은 api 호출 등으로 막히는 상황도 고려했으나 그 정도의 대량 호출은 없었음. 구글 드라이브 인스턴스가 죽었다고 하기에는 소유자의 다른 문서들은 잘 열리는 상황. 저장중 모종의 이유로 인해 손상된 것으로 보임.)

3. 해결: daily로 google sheet로 된 모든 config를 S3로 xlsx로 백업하는 dag가 있어 해당 파일을 통해 복원. 권한 등의 적절한 복원을 위해 '편집자'는 confluence 등에 별도 작성할 필요가 있음

 

교훈: 외부화된 config는 손상되거나 접근이 불가능해질 경우 대처가 안 되므로 항상 백업을 진행하는 것이 좋음

 

cf) google sheet에 대한 api 접근은 은근히 많이 실패하는 편이다. queue limit과도 별 상관없고 짧은 기간 동안 특정 region에 할당된 인스턴스가 죽으면서 발생하는 것으로 보였다. N mins 단위로 도는 배치에서는 이중삼중으로 retry를 할 필요가 있다. 현재 진행하는 프로젝트에서는 여러 gcp project의 service account를 통해 retry를 할 수 있게 구성했다. 그럼에도 불구하고 문서 자체가 손상되면 어쩔 수가 없다. 원본 문서는 다음날 복구 되었으나, deprecated 처리했다.

현업과 소통하다 보면,

오케스트레이터에 직접 현업용 아이디를 만들어주고 권한을 컨트롤 하는 것보다,

구글시트에 config를 만드고 입력하라고 하는 게 커뮤니케이션상 훨씬 편할 때가 많다.

크게 느끼는 장점은 4가지이다.

1. 오케에서 Asset은 잘못 변경 시 audit로그를 뒤져야 하지만, 구글시트에서는 history가 바로 보인다

2. 새로운 인원이 추가되더라도 해당 인원의 아이디만 권한 허용해주면 끝이다

3. 현업들은 대부분 엑셀에 훨씬 익숙하다

4. 다른 과제의 Asset을 변경하는 등의 변수를 신경쓰지 않아도 된다. 물론 오케에서도 할 수는 있지만 시트가 훨씬 직관적이다

 

 

Asset에 해당하는 값을 여러 개 입력하기 때문에 컬럼을 그만큼 늘리는 건 가독성이 안 좋고,

Asset명 / 설정값으로 세팅하는데 그런 경우 가져오는 패턴을 기록해 본다.

dicConfig = dtConfig.AsEnumerable().ToDictionary(Function(row) row.Field(Of String)(" Asset명"), Function(row) row.Field(Of object)("설정값"))

 

+ 기존 config에 추가되게 하고 싶다면 For each row를 사용해 InitAllSettings에 있는 패턴처럼 하면 된다.

<for each row> dicConfig(row(" Asset명").ToString) = row(" 설정값").ToString </for each row>

+ Recent posts