워크플로 관리
워크플로: 정형적인 업무 프로세스와 같이 정해진 업무를 원활하게 진행하기 위한 구조
주요 역할
1) 정기적으로 태스크를 실행
2) 비정상적인 상태를 감지하여 그것에 대한 해결을 돕는 기능
기존에는 업무용으로 개발된 워크플로 도구를 사용하였지만 최근에는 데이터를 위한 워크플로 도구를 따로 사용한다. ex) Airflow, Luigi, Oozie
태스크: 데이터를 잇달아 이동하면서 반복되는 정해진 처리 = 데이터의 이동
주요 기능
- 태스크를 정기적인 스케줄로 실행하고 결과 통지
- 태스크 간의 의존관계를 정하고 순서에 따라 실행
- 태스크의 실행 결과를 보관하고, 오류 발생 시 재실행
선언형과 스크립트형
선언형: 제공된 기능만을 이용하여 최소한의 기술로 태스크를 정의한다. 유지 보수성이 높다. ex) Oozie
스크립트형: 태스크의 정의를 프로그래밍할 수 있고 데이터 처리를 태스크 안에서 실행하는 등 유연성이 높다. ex) Airflow
ETL에서는 스크립트형, SQL에서는 선언형 도구를 이용
오류
빅데이터 처리 과정에서는 다양한 오류가 발생하는데 미리 예기치 못한 오류가 발생할 가능성을 고려하여 오류 발생 시의 신속하게 대처할 수 있는 방법을 결정해두는 것이 중요하다.
오류는 재실행하면 자동으로 회복할 수 있는 것들도 있지만 수동으로 처리해야 하는 오류도 많다. 이를 일일이 구분할 수는 없기 때문에 기본적으로 수작업에 의한 복구를 전제한 태스크를 설계한다. 실패한 태스크는 모두 기록하여 나중에 실행할 수 있도록 한다.
워크플로 도구에 의해 실행되는 일련의 태스크를 플로우라고 하자. 플로우는 각자 고정 파라미터가 있는데, 만약 플로우가 실패하더라도 나중에 동일 파라미터로 재실행할 수 있게 하는 것이 복구의 기초이다. 대부분의 워크플로 도구는 실행한 플로우와 파라미터를 자동으로 데이터베이스에 기록하여 미완료된 태스크만을 실행할 수 있다.
여러 번 발생하는 오류는 재시도를 자동화하는 것이 편리하다. 이때 재시도 횟수가 중요한데, 너무 적으면 장애가 복구되기 전에 재시도가 종료될 것이고 너무 많다면 태스크가 실패하지 않은 것처럼 될 수 있기 때문에 이를 태스크의 성격에 따라 적절히 지정해 주어야 한다. 예기치 않은 오류가 발생한다면 수작업으로 해결하는 것도 방법이다.
백필: 파라미터에 포함된 일시를 순서대로 바꿔가면서 일정 기간의 플로우를 연속해서 실행하는 구조다. 태스크의 실패가 며칠 동안이나 계속된 후에 이를 모두 모아서 재실행하고 싶을 때나 새롭게 만든 워크플로를 과거로 거슬러 올라가 실행하고 싶은 경우에 사용한다. 대량의 태스크를 백필할 때는 테스트 삼아 조금 씩 백필을 실행하여 미리 어떤 오류가 발생하는지 알아보는 것도 좋다.
멱등한 조작으로 태스크 기술하기
각 태스크는 원칙적으로 "마지막까지 성공"하거나 "실패하면 아무것도 남지 않음" 둘 중 하나만 존재해야 한다.
원자성 조작: 각 태스크가 시스템에 변경을 가하는 것을 한 번만 할 수 있도록 하는 것으로, 워크플로에 포함된 태스크를 모두 원자성 있는 조작으로 구현함으로써, 재시도 시의 안정성을 높일 수 있다. 하지만 원자성 조작 직후에 문제가 발생하면, 원자성 조작 자체는 성공하고 있음에도 워크플로 관리 도구는 그것을 오류로 여기는 경우가 있다. 만약 아주 작은 가능성도 허가하지 않을 때는 원자성 조작이 아니라 오류를 직접 수동으로 복구해야 한다.
멱등한 조작: 동일한 태스크를 여러 번 실행해도 동일한 결과가 되도록 하는 것으로, 추가와 치환 중 하나를 실시한다. 멱등한 태스크를 만들기 위해서는 원칙적으로는 치환을 사용하여야 한다.
멱등한 추가
항상 멱등한 태스크를 구현하는 것은 어렵다. 모든 데이터를 치환하면 부하가 생기기 때문에 테이블 파티셔닝 방식을 사용한다. 테이블을 기준 단위의 파티션으로 분할하고, 파티션 단위로 치환한다.
원자성을 지닌 추가
만약 추가가 여러 번 반복된다면 중간 테이블을 만들어 처리한 후, 마지막에 목적 테이블에 한 번에 추가하는 것이 안전하다. 중간 테이블을 만들기 위해 테이블을 치환하고(멱등), 마지막 테이블에는 1회의 쓰기만 실행하여 원자성 조작을 한다. 이렇게 되면 플로우가 실패하면 아무것도 쓰이지 않아 실패한 태스크를 재실행하면 된다.
워크플로 전체를 멱등으로 하기
각 태스크를 멱등으로 하는 것이 이상적이긴 하지만, 워크플로가 안정적으로 실행되고 있는 한 태스크가 꼭 멱등일 필요는 없다. 추가가 문제시되는 것은 재시도 시에 중복의 가능성이 있기 때문이며, 그 점만 주의한다면 일반적인 운용으로 문제될 일은 없다.
재실행의 안전성을 위해서라면 적어도 각 플로우가 전체로서 멱등하게 되도록 구현해야 한다. 예를 들면 중간 테이블을 초기화하는 태스크를 실행하고 그다음부터 추가의 태스크를 계속 실행한다. 그렇게 하면 재실행을 해도 안전하다.
태스크 큐
태스크의 크기나 동시 실행 수를 변화시킴으로써 자원의 소비량을 조정하여 태스크가 원활하게 실행되도록 하는 것이 중요하다.
병렬화
파일을 압축하는 태스크가 있다고 한다면 파일의 수만큼 태스크를 실행하게 된다. 이때 태스크 큐를 사용한다. 모든 태스크는 일단 큐에 저장되고 일정 수의 워커 프로세스가 그것을 순서대로 꺼내면서 병렬화가 실현된다. 만약 8개의 워커를 기동하면, 8개가 병렬로 실행된다.
각 태스크는 CPU뿐 아니라 디스크 I/O나 네트워크 I/O도 소비한다. 워커의 수를 늘리면 좀 더 실행 속도를 높일 수 있다. 하지만 워커를 너무 증가시키면 병목 현상이 발생할 수 있다.
태스크 수의 적정화
태스크를 너무 작게 만들어 다수 실행하면 오버헤드가 커져 성능이 낮아진다. 따라서 수백 개 정도를 모아서 하나의 태스크로 하는 것이 적정한 크기라고 할 수 있다. 결과적으로 태스크는 너무 크지도, 너무 작지도 않은 적당한 크기로 분할된 다수의 태스크를 잘 구성하여야 한다.
배치 형의 데이터 플로우
MapReduce의 시대는 끝났다
데이터 플로우: 기술적인 발전에 따라 다단계의 데이터 처리를 그대로 분산 시스템의 내부에서 실행 ex) Spark, Flink, GoogleCloud Dataflow
MapReduce 구조
맵리듀스는 이제 잘 쓰이지 않지만 개념 자체는 여러 데이터 플로우에서 쓰이고 있다.
중요 구조는 분할된 데이터를 처리하는 첫 번째 단계를 Map, 그 결과를 모아서 집계하는 두번째 단계를 Reduce하고 한다. 이를 여러 번 반복하여 진행하기 때문에 사이클에서 다음 사이클로 이동할 때까지의 대기 시간이 적지 않게 발생한다.
MapReduce 대체 프레임워크
DAG
노드 간의 방향을 가지고 있는 비순환 그래프로 MapReduce도 Map과 Reduce 두 종류의 노드로 이루어진 DAG라고 할 수 있다. 하지만 하나의 노드에서 처리가 끝나지 않으면 다음 처리로 진행할 수 없다.
반면 데이터 플로우에서는 각 노드가 동시 병행으로 실행되므로 이러한 대기 시간을 없앤다.
Spark와 같은 데이터 플로우 프레임워크에서는 프로그래밍 언어를 사용하여 직접 DAG의 데이터 구조를 조립할 수 있다.
지연평가
프로그램의 각 행은 실제로는 DAG의 데이터 구조를 조립하고 있을 뿐이고 실행 결과를 요구함에 따라 데이터 처리가 시작된다. 이렇게 함으로써 내부 스케줄러가 분산 시스템에 효과적인 실행 계획을 세워줄 수 있게 된다.
데이터 플로우와 워크플로 조합하기
데이터 플로우로 파이프라인을 모두 실행할 수 있는 것은 아니기 때문에 워크플로 관리가 필요하다.
따라서 데이터 플로우의 프로그램도 다시 워크플로의 일부로서 실행되는 하나의 태스크로 고려할 수 있다.
데이터를 읽어들이는 플로우
데이터 플로우를 이용하려면 데이터 소스에서 분산 스토리지에 데이터를 복사하여 이용해야 데이터 소스에 성능 문제가 생기지 않는다. 이 때 외부의 데이터 소스에서 데이터를 읽어 들일 때는 벌크 형의 전송도구로 태스크를 구현한다.
분산 스토리지로 옮기면 데이터 플로우를 통해 데이터를 가공할 수 있다.
데이터를 써서 내보내는 플로우
데이터 플로우 안에서 대량의 데이터를 외부에 전송하는 것은 위험할 수 있기 때문에 CSV 파일과 같은 형태로 변환하여 분산 스토리지에 넣는다. 외부 시스템에 데이터를 전송하는 것은 워크플로의 역할이다.
데이터 플로우와 SQL을 나누어 사용하기
데이터 웨어하우스의 파이프라인
SQL을 MPP 데이터베이스에서 실행하는 경우로 데이터 플로우는 데이터를 가공하여 CSV 파일 등을 분산 스토리지에 써넣는 역할까지 하고, 그 이후 태스크 실행은 워크플로가 맡는다.
여기서 로드가 의미하는 것이 분산 스토리지에 다시 데이터를 넣는 것을 의미한다.
데이터 마트의 파이프라인
분산 시스템상의 쿼리 엔진에서 실행하는 경우로 분산 스토리지 상의 데이터를 매일 반복되는 배치로 가공하여
열 지향 스토리지 형식으로 보관한다. 쿼리 엔진을 사용한 SQL 실행이나 결과를 데이터 마트로 보내는 것은
워크플로에서 실행한다.
애드 혹 데이터 파이프라인
애드 혹 분석은 데이터 처리를 수작업으로 시행하므로 워크플로는 필요하지 않다. 데이터 플로우를 통해 데이터를 구조화한다면, 쿼리 엔진을 통해 고속으로 SQL을 실행할 수 있다. 여기에는 ODBC나 JDBC 드라이버가 사용된다. 쿼리 엔진과 시각화 도구와의 조합은 무수히 많이 존재하여 아직 안정적이지 않으니 RDB와 MPP 데이터베이스를 데이터 마트로 하는 편이 확실하다.
스트리밍 형의 데이터 플로우
배치 처리와 스트림 처리로 경로 나누기
실시간에 가까운 데이터 처리에는 조금 더 특별한 파이프라인을 만들어야 한다. 예를 들면 실시간 모니터링, 로그 관리 시스템과 같은 처리 시스템이 있다. 분산 스토리지를 거치지 않고 처리를 계속하는 것을 스트림 처리라고 한다.
배치 처리와 스트림 처리는 서로를 보완하는 관계이다. 앞으로 도달할 데이터에 흥미가 있다면 스트림 처리가 적합하고, 과거 데이터를 집계하고 싶다면 배치 처리 쪽이 우수하다.
배치 처리와 스트림 처리 통합하기
둘 다 데이터를 작게 분할해서 DAG에서 실행한다는 점에서는 같지만, 배치 처리에서는 원래 있는 데이터를 작게 나누어서 DAG에 흘려 넣고, 스트림 처리에서는 끊임없이 데이터가 생성되며 그것이 DAG 안에 흘러들어옴에 따라 처리가 진행된다.
Spark 스트리밍의 DAG
스파크는 원래 배치 처리를 위한 분산 시스템이었지만 Spark Streaming 기능이 통합되면서 스트림 처리까지 취급하는 프레임워크가 되었다. 배치 처리 스크립트와 비교해보면 데이터를 읽고 쓰는 초기화 부분에 차이가 있을 뿐,
데이터를 처리하는 부분은 똑같다는 것을 알 수 있다.
스트림 처리의 결과를 배치 처리로 치환하기
스트림 처리의 문제
- 틀린 결과를 어떻게 수정할 것인가 -> 시간을 되돌린다는 개념이 없기 때문
- 늦게 전송된 데이터 취급 -> 데이터가 부정확해질 수 있다.
=> 이러한 문제들에 대한 전통적인 대처 방법은 스트림 처리와는 별개로 배치 처리를 실행시켜 후자의 결과가 옳다고 하는 것이다.
람다 아키텍처
람다 아키텍처는 위의 해결책을 발전시킨 방법으로 3개의 레이어로 구분한다.
먼저 배치 레이어에서 반드시 모든 데이터를 처리한다. 과거의 데이터를 장기적인 스토리지에 축적하고, 여러 번 다시 집계할 수 있게 한다. 배치 처리 결과는 서빙 레이어를 통해 접근한다. 서빙 레이어에서 얻어진 결과를 배치 뷰라고 한다. 이는 정기적으로 업데이트되지만, 실시간 정보를 얻을 수 없다.
이를 위해 스피드 레이어를 통해 실시간 뷰를 구성하여 스트림 처리를 한다. 실시간 뷰는 배치 뷰가 업데이트될 동안까지만 이용되고, 오래된 데이터는 순서대로 삭제된다.
마지막은 배치 뷰와 실시간 뷰 모두를 조합시키는 형태로 쿼리를 실행한다. 예를 들면, 최근 24시간 데이터는 실시간 뷰를, 그 이전의 데이터는 배치 뷰를 활용하는 것이다. 배치 처리만 안정되게 동작한다면 스트림 처리를 다시 실행할 필요가 없다는 것이 람다 아키텍처의 개념이다.
카파 아키텍처
람다 아키텍처는 두 개의 레이어를 구성하기 때문에 개발 효율이 나쁘다. 카파 아키텍처는 람다 아키텍처를 단순화하여 스피드 레이어만을 남긴다. 대신 메시지 브로커의 데이터 보관 기간을 충분히 하여 문제가 생기면 메시지 배송 시간을 과거로 다시 설정한다. 이렇게 하여 데이터를 덮어씌워 새로운 결과로 다시 쓰인다.
이렇게 하면 부하가 높아지는 문제가 생긴다. 하지만 클라우드의 확장성을 이용하면 크게 문제가 되지 않기 때문에 스트림 처리를 다시 하는 것이 간단하다는 것이 카파 아키텍처의 개념이다.
아웃 오브 오더의 데이터 처리
아웃 오브 오더 데이터 문제
늦게 도달하는 메시지, 즉 프로세스 시간과 이벤트 시간과의 차이로 인해 생기는 문제
스트림 처리는 기본적으로 프로세스 시간에 의한 실시간 데이터 처리다. 하지만 이 성질이 혼란을 일으킬 수 있다. 만약 유지 보수 등으로 스트림 처리를 일시적으로 멈춘다면 쌓여 있던 데이터 처리가 재개되는데 실제로 보내진 데이터양은 변함이 없지만 결과가 요동치게 될 수도 있다. 그러므로 데이터가 처음에 생성된 시간 즉, 이벤트 시간으로 집계해야 올바른 결과를 얻을 수 있다.
윈도우: 시간을 일정 간격으로 나누어 집계하는 것 ex) 1시간 추이를 1분 간격 60개로 나누어 각각의 윈도우로 이벤트 수 집계
이벤트 시간 윈도윙
이벤트 시간에 의해 윈도우를 나누는 것을 말하며, 메시지가 배송된 데이터는 무작위 순으로 나열된,
아웃 오브 오더 상태이므로, 이것을 적절히 순서를 바꿔 집계 결과를 업데이트해야 한다.
빅데이터 분석 기반 예시
'데이터 엔지니어링 > 데이터 엔지니어링 기초' 카테고리의 다른 글
데이터 웨어하우스란? (0) | 2022.03.17 |
---|---|
빅데이터를 지탱하는 기술 Ch6 - 빅데이터 분석 기반 구축 (0) | 2022.01.05 |
빅데이터를 지탱하는 기술 Ch4 - 빅데이터의 축적 (0) | 2022.01.03 |
빅데이터를 지탱하는 기술 Ch3 - 빅데이터 분산 처리 (0) | 2022.01.03 |
빅데이터를 지탱하는 기술 Ch2 - 빅데이터의 탐색 (0) | 2022.01.03 |