5-1. 워크플로 관리

[기초 지식] 워크플로 관리: 데이터의 흐름을 일원 관리하기

- 워크플로 관리(workflow management): 기업 내의 정형적인 업무 프로세스(신청, 승인, 보고 등)와 같이 정해진 업무를 원할하게 진행하기 위한 구조

    - 매일 매일의 태스크를 관리하는 구조가 정기적인 배치 처리 실행에도 유용하기 때문에 데이터 처리 현장에서도 자주 이용

- 일반적인 데이터 워크플로 관리:

    - 태스크는 정해진 스케줄에 따라 자동으로 실행

    - 무언가 비정상적 일이 발생한 경우에는 사람이 개입하여 문제 해결

워크플로 관리 도구

- 워크플로 관리 도구의 주요 역할: 

    - 정기적으로 태스크를 실행

    - 비정상적인 상태를 감지하여 그것에 대한 해결

- 대표적인 오픈소스 워크플로 관리 도구: Airflow, Digdag, Oozie

 

워크플로 관리 도구와 태스크

- 태스크(Task): 실행되는 개별 데이터 처리 단위

 

기본 기능과 빅데이터에서 요구되는 기능

- 워크플로 관리 도구가 필요한 이유: 태스크 실행에 실패할 수 있기 때문

- 아래 기능이 필요 (모든 태스크를 일원 관리하기 쉽게 해야 함)

    - 태스크를 정기적인 스케줄로 실행하고 결과 통지

    - 태스크 간의 의존 관계 정하고 정해준 순서대로 빠짐없이 실행

    - 태스크 실행 결과를 보관하고, 오류 발생 시에는 재실행할 수 있도록

 

선언 형과 스크립트 형

선언형(Declarative): XML, YAML 등의 서식으로 워크플로 기술

    - ex. Oozie

    - 미리 제공된 기능만 이용

    - 누가 작성해도 동일한 워크플로가 되기 때문에 유지 보수성이 높아짐

스크립트형 (Scripting): 스크립트 언어(ex. python)로 워크플로를 정의

    - ex. Airflow

    - 유연함: 태스크 정의를 프로그래밍할 수 있음

- 데이터 수집 과정에서는 스크립트 형의 도구를 사용하여 유연하게 워크플로를 조립하고, 데이터가 모아지면 정형적인 처리만 하게 되므로 이후에는 선언형 도구를 사용할 수 있다

오류로부터의 복구 방법 먼저 생각하기

빅데이터를 취급하고 있으면 다양한 오류가 발생

- 미리 예기치 못한 오류가 발생할 가능성을 고려하여 오류 발생 시의 대처 방법을 결정해두는 것이 중요

 

복구와 플로우의 재실행

- 수작업에 의한 복구를 전제한 태스크 설계

    - 오류로부터 자동 회복할 수 있다는 점은 고려하지 않는 것이 좋다 (오류의 종류는 수많기 때문에 전부 대응하는 것은 불가능)

- 플로우: 일련의 태스크

- 복구의 기초

    - 각 플로우 실행시 날짜를 파라미터화 한다

    - 동일 플로우에, 동일 파라미터를 건넬 시 동일한 태스크가 실행될 수 있다 => 플로우가 도중에 실패해도 나중에 동일 파라미터로 재실행 가능

 

워크플로의 버전 관리

- 복잡화된 데이터 파이프라인의 구축은 더 이상 시스템 개발고 ㅏ다른 분야가 아니기 대문에 소프트웨어 개발 분야에서 연마되어 온 버전 관리 기법을 도입하는 것을 추천 (Git)

 

태스크를 되도록 작게 유지하기

- 큰 태스크는 나누어 적당히 작은 복수의 태스크로 플로우를 구성하자

    - 문제가 발생해도 도중에 재개할 수 있음

 

재시도

- 여러 차례 반복하는 오류는 자동화

- 오류의 원인을 그때마다 조사하여 대책 마련

 

백필

- 일정 기간의 플로우를 연속해서 실행하는 구조

- 대량의 태스크를 실행할 때에는 성능상의 주의가 필요

    - 테스트 삼아 조금씩 백필 실행

 

 

멱등한 조작으로 태스크를 기술하기

- 멱등성: 동일한 태스크를 여러 번 실행해도 동일한 결과가 된다

    - 재실행의 안전성을 확보하는 데에 가장 중요

    - 태스크를 도중까지 실행하다 실패했을 때 도중 경과가 남아있으면, 태스크 재실행에 의해 데이터가 혼재 될 수 있음

    - 각 태스크는 원칙적으로 '마지막까지 성공'하거나 '실패하면 아무것도 남지 않음' 이 둘 중 하나만 존재해야 함

        - '도중가지 성공'이라는 어정쩡한 상황은 허가하지 않아야 함

 

원자성 조작

- 원자성 조작(atomic operation): 각 태스크가 시스템에 변경을 가하는 것을 한 번만 할 수 있도록 하는 것

- 워크플로에 포함된 태스크를 모두 원자성 조작으로 구현함으로써 재시도의 안전성을 높일 수 있음

- 원자성 조작이 문제를 야기하는 경우:

    - 원자성 조작 직후에 문제가 발생하면, 워크플로 관리 도구에서 이를 오류로 여겨 재시도 하는 경우

    -> 재시도로 인한 중복 발생

 

멱등한 조작 - 추가와 치환

- 멱등한 조작(idempotent operation): 동일한 태스크를 여러 번 실행해도 동일한 결과가 되도록 하는 것

    - 안정성을 확보하는 가장 확실한 방법

- 위 예시에서 멱등한 조작이면 재실행 해도 중복 발생 안함

- 원칙적으로는 항상 데이터를 덮어써야 함

    - 추가 (append)는 데이터가 중복되지만, 치환 (replace, overwrite)은 반복해도 결과가 변하지 않음

- 태스크에 부여된 파라티러를 잘 이용해 여러 번 실행해도 항상 치환이 되도록 설계

 

멱등한 추가

- 테이블 파티셔닝하여, 파티션 단위로 치환하도록 한다

- 태스크를 멱등으로 구성하는 것이 어렵다면, 그것을 포기하고 원자성을 지닌 추가로 운용

    - 오류 발생 시, 수작업으로 복구

 

원자성을 지닌 추가

- 하나의 테이블에 몇 번이고 데이터를 써넣을 때가 있음

    - 이 경우 추가를 반복하지 말고 중간 테이블을 만들어 처리한 후, 마지막에 목적 테이블에 한 번에 추가하는 것이 안전

 

워크플로 전체를 멱등으로 하기

- 데이터 파이프라인을 안정적으로 운용하기 위해서는 거기에 포함된 태스크나 플로우를 가능한 한 멱등으로 해야 함

- 데이터 마트를 구축하는 플로우에서도 되도록 추가는 삼가고 테이블마다 치환

    - 멱등한 태스크는 실패시 안전하게 재실행할 수 있음

태스크 큐: 자원의 소비량 컨트롤하기

- 워크플로 관리 도구에서 요구되는 커다란 역할 중 하나가 부하 컨트롤

- 병렬화가 필요한 경우

    - 너무 대량의 태스크를 동시 실행하면 서버에 과부하가 걸리므로 어느 정도 제한을 해야 함

- 잡 큐 (job queue) (= 태스크 큐(task queue)) 구조

    - 모든 태스크는 일단 큐에 저장되고 일정 수의 워커 프로세스가 그것을 순서대로 꺼내면서 병렬화가 실현

병목 현상의 해소

- 워크플로 실행 시 자주 발생하는 문제 (서버의 내부적인 요인)

    1. CPU 사용률 100% -> CPU 코어 수를 늘린다. 서버를 증설한다.

    2. 메모리 부족 -> 메모리를 증설한다. 스왑 디스크를 추가한다. 태스크를 작게 분할한다.

    3. 디스크 넘침 -> 각 태스크가 임시 파일을 삭제하고 있는지 확인한다. 디스크를 증설한다.

    4. 디스크 I/O의 한계 -> SSD 등의 고속 디스크를 사용한다. 여러 디스크로 분산한다.

    6. 네트워크 대역의 한계 -> 고속 네트워크를 사용한다. 데이터의 압축률을 높인다.

    7. 통신 오류나 타임 아웃 -> 시스템 상의 한계일 가능성이 있다. 서버를 분리한다.

- 서버 외부 요인이 원인인 병목 현상은 문제를 제거할 수 없다.

 

태스크 수의 적정화 - 너무 크거나 너무 작지 않은 정도로 잘 분할하기

- 작은 태스크를 자주 실행하면 오버헤드만 커짐

- 태스크가 너무 클 경우에는 나누고, 너무 작을 경우에는 하나로 모음으로써 각 태스크가 적절한 크기가 될 수 있도록 조정

 

 

5-2. 배치 형의 데이터 플로우

MapReduce의 시대는 끝났다 - 데이터 플로우와 워크플로

- 데이터 플로우(data flow): 다단계의 데이터 처리를 그대로 분산 시스템 내부에서 실행 (워크플로와 구분해서 사용)

MapReduce의 구조

- 과거 빅데이터의 대표적인 기술이었지만 이제 새롭게 사용되는 일은 없어짐

    - Google: MillWheel
    - Hadoop: Tez
- MapReduce의 개념
    1. Split: 분산 처리를 위해 데이터 파일을 일정 크로 나눔
    2. Map: 분할된 데이터를 처리 (함수 적용 ex. count)
    3. Reduce: 분산된 결과를 모아서 집계

- 하나의 사이클이 끝나지 않으면 다음 처리로 이동하지 못함

    - 하나의 사이클에서 다음 사이클로 이동할 때까지의 대기 시간이 긴 단점

- 애드 혹 분석에서는 실현 어려움

 

MapReduce를 대신할 새로운 프레임워크 - DAG에 의한 내부 표현

- DAG(Directed Acyclic Graph) 데이터 구조로 새로운 프레임워크에서 사용

    - 데이터 플로우에서 실행해야 할 일련의 태스크를 DAG에 의핸 데이터 구조로 표현

    - 화살표는 태스크 실행 순서

    - 의존 관계를 유지하며 실행 순서를 알맞게 정하면 모든 태스크를 빠짐없이 완료할 수 있음

    - DAG는 시스템 내부적인 표현으로 이용자가 존재를 의식할 일 거의 없음: 데이터 플로우에 국한되지 않고, Hive on Tez나 Presto같은 쿼리 엔진에서도 DAG 구조 채택

Spark에서의 DAG

- Spark과 같은 데이터 플로우 프레임워크서는 프로그래밍 언어를 사용하여 DAG를 직접 조립

    - 처리 내용이 복잡해지면 DAG도 그만큼 복잡해짐

- 지연 평가 (lazy evaluation): 프로그램의 각 행은 실제로는 DAG 데이터 구조를 조립할 뿐, 후에 실행 결과를 요구할 때 데이터 처리가 시작됨

    => 내부 스케줄러가 분산 시스템에 효과적인 실행 계획을 세울 수 있음

데이터 플로우와 워크플로를 조합하기

- 태스크를 정기적으로 실행하거나, 실패한 태스크를 기록하여 복구하는 것은 데이터 플로우에서 할 수 없음. 워크플로 관리 필요

    - 데이터 플로우의 프로그램도 워크플로의 태스크로 고려할 수 있음

- 분산 시스템 외부와 데이터를 주고받을 경우도 워크플로 안에서 실행하는 것이 바람직

데이터를 읽어들이는 플로우

- 워크플로 조합

    - Task 1: 데이터 소스에서 분산 스토리지로 벌크 전송

    - Task 2: 데이터 플로우로 필요한 연산, 분산 스토리지에서 실행

데이터를 써서 내보내는 플로우

- 워크플로 조합

    - Task 1: 데이터 플로우를 통해 CSV 같이 취급하기 쉬운 형식으로 변환

    - Task 2: 외부로 전송

 

데이터 플로우와 SQL을 나누어 사용하기

- 데이터 웨어하우스 파이프라인 (SQL을 MPP 데이터베이스에서 실행)

    - 데이터 플로우: DB에서 분산 스토리지로 벌크 전송 -> 데이터 웨어하우스로 로드

    - SQL: 배치 처리 (시각화)

- 데이터 마트 파이프라인 (쿼리 엔진은 분산 시스템에서 실행)

    - 데이터 플로우: DB에서 분산 스토리지로 벌크 전송

    - SQL: 쿼리 엔진으로 배치 처리 (마트 제작)

대화식 플로우 - 애드혹 분석의 파이프라인

- 데이터 처리를 수작업으로 시행하므로 워크플로는 필요하지 않음

- 구조화되어 있지 않은 데이터를 애드 혹 분석시, 데이터 플로우가 매우 유용

- 구조화된 데이터는 쿼리 엔진을 사용

 

5-3. 스트리밍 형의 데이터 플로우

배치 처리와 스트림 처리로 경로 나누기

- 배치 처리 중심의 데이터 파이프라인의 결점은 데이터가 분석할 수 있게 될 때까지 시간이 걸림

- 실시간: 이벤트 발생에서 몇 초 후에는 결과를 알 수 있는 것

- 실시간성이 높은 데이터 처리 시스템 예

    - 시스템 모니터링

    - 로그 관리 시스템

    - 복합 이벤트 처리 (Complex Event Processing, CEP)

- 스트림 처리 (streaming processing): 분산 스토리지를 거치지 않고 처리를 계속하는 것

- 스트림 처리는 실시간성이 우수하지만, 과거 데이터를 취급하는 데에는 부적합

배치 처리와 스트림 처리 통합하기

- 배치 처리: 먼저 데이터가 있고, 그것을 작게 나눠서 DAG에 흘려 넣음 (유한 데이터 bounded data)

- 스트림 처리: 끊임없이 데이터가 생성되며, 그것이 DAG 안에 흘러들어옴 (무한 데이터 unbounded data)

- DAG를 사용한 데이터 플로우에서는 배치 처리와 스트림 처리를 동일하게 프로그래밍하는 것이 가능

Spark 스트리밍의 DAG

- 데이터를 읽고 쓰는 초기화 부분(socketTextStream("localhost", 9999))에 차이가 있을 뿐, 데이터 처리 중심부 (Map -> Reduce)는 동일

- 배치 처리는 데이터 처리가 끝나면 종료되는데, 스트림 처리는 프로그램을 정지할 때까지 끝없이 실행

 

스트림 처리의 결과를 배치 처리로 치환하기

- 스트림 처리의 두 가지 문제

    1. 잘못된 데이터 수정하기 어려움

    2. 늦게 전송된 데이터 취급

- 배치 처리를 실행해 스트림 처리의 문제를 대처

    - 스트림 처리 결과는 배치 처리 결과가 나올 때까지의 잠정 값으로 이용

람다 아키텍처: 배치 레이어, 서빙 레이어, 스피드 레이어

- 배치 레이어: 과거 데이터를 장기적인 스토리지에 축적하고, 여러 번이고 다시 집계

    - 대규모 배치 처리를 실행할 수 있는 반면, 1회 처리에는 긴 시간 걸림

- 서빙 레이어: 응답이 빠른 데이터베이스 설치

    - 배치 뷰: 서빙 레이어에서 얻어진 결과

        - 정기적으로 업데이트되지만, 실시간 정보를 얻을 수 없음

- 스피드 레이어: 스트림 처리

    - 실시간 뷰: 배치 뷰가 업데이트될 동안까지 이용되고 오래된 데이터는 순서대로 삭제

- 장점: 실시간 뷰의 결과는 나중에 배치 뷰로 치환

- 단점: 나쁜 개발 효율

    - 스피드 레이어와 배치 레이어 모두 똑같은 처리를 구현하는 비효율

 

카파 아키텍처

- 카파 아키텍처: 람다 아키텍처를 단순화함

- 배치 레이어, 서빙 레이어 제거하고 스피드 레이어만 남김

- 메시지 브로커의 데이터 보관 기간을 충분히 길게 함

    - 스트림 처리를 멱등으로 구현하여, 과거 데이터 보정(백필)을 가능하도록 함

- 단점: 부하가 높아짐

    - 클라우드 서비스 보급에 의해 자원 확보가 어렵지 않게 되었으므로 '스트림 처리르 다시 하는 것이 간단하다'는 게 카파 아키텍처의 철학

 

아웃 오브 오더의 데이터 처리

- 아웃 오브 오더(out of order) 데이터: 늦게 도달하는 메시지, 프로세스 시간과 이벤트 시간의 차이

 

원래 데이터의 모습은 '이벤트 시간'으로 얻을 수 있다

- 이벤트 시간으로 집계한 것이 올바른 결과

이벤트 시간 윈도잉

: 이벤트 시간에 의해 윈도우를 나누는 것

- 과거 이벤트 상태를 보관하면서, 데이터가 도달할 때마다 해당하는 윈도우를 재집계할 필요 있음

- 일정 시간 늦게 온 데이터는 무시

+ Recent posts