OpenLineage
고친 사람 github-actions[bot]
OpenLineage 는 데이터 작업이 무엇을 읽고 무엇을 썼는지 한 가지 형식으로 알리게 해 줍니다. 작업이 한 번 돌 때마다 이 형식의 기록이 나옵니다. 받는 쪽은 이 기록을 모아 데이터가 흘러간 길을 이어 그립니다. 어느 도구에서 나온 기록이든 모양이 같아서 한곳에 모을 수 있습니다.
쉽고 빠른 이해
무슨 일을 하는 물건인가 — 데이터 작업이 「무엇을 읽어 무엇을 썼다」를 기록하는 공통 양식입니다. 매일 밤 도는 매출 집계가 끝나면 「주문 표를 읽어 일별 매출 표를 썼다」는 기록이 한 건 나갑니다.
왜 이렇게 하나 — 데이터는 여러 도구를 거쳐 가공됩니다. 양식이 없으면 도구마다 기록하는 모양이 달라서 받는 쪽이 도구마다 따로 맞춰야 합니다. 양식이 하나면 도구는 한 번만 내보냅니다. 받는 쪽은 하나만 읽습니다.
어떻게 도나
- 작업이 시작할 때와 끝날 때 도구에 붙은 연동 코드가 기록을 보냅니다
- 받는 쪽이 기록을 쌓습니다
- 한 작업이 쓴 표를 다른 작업이 읽으면 두 기록을 선으로 잇습니다
대가 — 도구마다 연동 코드를 붙여야 합니다. 기록을 안 보내는 작업은 데이터가 흘러간 길에서 빠집니다. 같은 표를 도구마다 다른 이름으로 부르면 선이 끊깁니다. 데이터가 도구 하나 안에서만 흐르면 그 도구가 보여 주는 길로 충분해서 쓸 까닭이 적습니다.
상세
OpenLineage 는 데이터 계보를 모으는 방법을 정한 공개 규격입니다. 데이터 계보는 어느 데이터가 어느 데이터를 읽어 만들어졌는지 이어 둔 기록입니다. 숫자가 이상하면 이 기록을 거슬러 원본을 찾습니다. 표를 바꾸기 전에는 이 기록을 따라 내려가 영향받을 곳을 봅니다.
OpenLineage 가 정하는 것은 기록 한 건의 모양과 데이터에 이름을 붙이는 법입니다. 기록을 저장하고 화면에 그리는 일은 하지 않습니다. 그 일은 받는 쪽이 맡습니다. 받는 쪽은 기록을 모아 계보를 보여 주는 프로그램이나 제품입니다.
규격과 함께 받는 쪽의 본보기 프로그램도 만들어졌습니다. 그것이 Marquez입니다. 규격대로 기록을 받아 쌓도록 만든 이런 본보기를 기준 구현이라고 부릅니다.
공통 형식이 필요한 이유
데이터는 대개 여러 도구를 거쳐 가공됩니다. 흔히 함께 쓰는 도구 셋은 아래와 같습니다.
| 도구 | 하는 일 |
|---|---|
| Apache Airflow | 처리 단계들을 정해진 순서로 돌립니다 |
| Apache Spark | 큰 데이터를 여러 기계에 나눠 계산합니다 |
| dbt | 데이터 웨어하우스(분석하려고 데이터를 모아 둔 저장소) 안에서 표를 만듭니다 |
계보를 그리려면 이 도구들이 각자 무엇을 읽고 썼는지 알아야 합니다. 공통 형식이 없으면 받는 쪽이 도구마다 그 도구의 기록을 읽는 코드를 따로 짜야 합니다. 도구가 다섯, 받는 쪽 제품이 넷이면 그런 코드가 스무 벌 생깁니다. 도구 하나가 바뀌면 그 도구를 읽던 코드 넷이 한꺼번에 흔들립니다.
OpenLineage 는 그 사이에 형식 하나를 둡니다. 도구는 이 형식으로 한 번만 내보냅니다. 받는 쪽은 이 형식 하나만 읽습니다. 그러면 필요한 코드는 도구마다 붙는 연동 코드 다섯과 받는 쪽마다 하나씩인 읽는 코드 넷, 모두 아홉으로 줄어듭니다.
작업 · 실행 · 데이터셋
OpenLineage 는 계보를 세 가지 대상으로 나눠 적습니다. 이 절은 쇼핑몰 회사의 일별 매출 집계를 예로 삼습니다. 주문 표를 읽어 날짜마다 금액을 더한 뒤 일별 매출 표에 쓰는 작업입니다.
| 대상 | 뜻 | 쇼핑몰 예 |
|---|---|---|
| 작업(job) | 반복해서 도는 처리의 정의 | 매일 밤 도는 일별 매출 집계 |
| 실행(run) | 작업이 한 번 돈 것 | 9월 25일 새벽에 돈 집계 한 번 |
| 데이터셋(dataset) | 작업이 읽거나 쓴 데이터 | 주문 표 · 일별 매출 표 |
작업과 실행을 따로 두는 까닭은 같은 작업도 돌 때마다 결과가 다르기 때문입니다. 어제 실행은 성공하고 오늘 실행은 실패할 수 있습니다. 실행마다 따로 적어야 「이 숫자를 만든 것은 어느 실행인가」에 답할 수 있습니다.
실행마다 UUID(Universally Unique Identifier, 범용 고유 식별자)를 하나씩 붙입니다. UUID 는 여러 곳에서 따로 만들어도 겹치지 않도록 만든 긴 식별 값입니다. 같은 실행에서 나온 기록은 이 값이 같습니다.
작업과 데이터셋은 네임스페이스와 이름 두 값으로 가리킵니다. 데이터셋의 네임스페이스는 그 데이터가 사는 곳입니다. 데이터베이스 서버의 주소가 여기에 들어갑니다. 이름은 그 안에서 데이터를 찾는 경로입니다. 작업의 네임스페이스는 작업을 운영하는 사람이 작업들을 묶으려고 정하는 이름입니다.
실행 이벤트 한 건
기록 한 건을 실행 이벤트(run event)라고 부릅니다. 형식은 JSON(JavaScript Object Notation, 중괄호로 값을 묶어 적는 텍스트 형식)입니다.
아래는 일별 매출 집계가 성공으로 끝났을 때 나가는 이벤트입니다. 오른쪽 주석은 그 줄이 무엇을 가리키는지 적은 것입니다. JSON 에는 주석 문법이 없어서 설명하려고 따로 붙였습니다.
{
"eventType": "COMPLETE", // 끝났다
"eventTime": "2026-09-25T03:10:00Z",
"run": {
"runId": "3f1e7c2a-…" // 이 실행
},
"job": {
"namespace": "shop-etl",
"name": "daily_sales" // 이 작업
},
"inputs": [{
"namespace": "postgres://db:5432",
"name": "shop.public.orders" // 읽음
}],
"outputs": [{
"namespace": "postgres://db:5432",
"name": "shop.mart.daily_sales" // 씀
}],
"producer": "https://example.com/etl",
"schemaURL": "https://openlineage.io/spec/…"
}
이 한 건이 계보의 한 조각입니다. 「daily_sales 작업이 주문 표를 읽어 일별 매출 표를 썼다」를 담고 있습니다.
맨 위의 키가 무엇을 담는지는 아래와 같습니다.
| 키 | 담는 것 |
|---|---|
eventType |
실행이 어떤 상태에 들어섰나 |
eventTime |
그 상태가 된 시각 |
run |
이 실행의 식별 값과 덧붙일 정보 |
job |
어느 작업의 실행인가 |
inputs · outputs |
읽은 데이터셋과 쓴 데이터셋 |
producer |
이 이벤트를 만든 연동 코드를 가리키는 주소 |
schemaURL |
이 이벤트가 따르는 규격의 구조 정의를 가리키는 주소 |
이벤트의 모양은 JSON Schema 로 적혀 있습니다. JSON Schema 는 JSON 데이터가 어떤 구조여야 하는지를 적는 별도 규격입니다.
받는 쪽은 schemaURL 을 보고 이벤트가 어느 판의 구조를 따르는지 압니다.
실행 상태가 옮겨 가는 순서
실행 하나는 이벤트를 한 건만 보내지 않습니다. 시작할 때 한 건, 끝날 때 한 건을 보냅니다.
eventType 이 그 순간의 상태를 알립니다.
eventType |
뜻 |
|---|---|
START |
실행이 시작됐다 |
RUNNING |
아직 도는 중이다. 「돌고 있다」는 상태를 알리며 정보를 보탠다 |
COMPLETE |
성공으로 끝났다 |
FAIL |
실패로 끝났다 |
ABORT |
끝나기 전에 멈췄다 |
OTHER |
실행 상태를 바꾸지 않는다. 정보만 보탠다 |
실행은 START 에서 시작해 끝 상태 셋(COMPLETE · FAIL · ABORT) 가운데 하나로 닫힙니다. RUNNING 은 건너뛰어도 됩니다.
OTHER 는 상태를 옮기지 않는 보탬 이벤트입니다. 그래서 아래 상태도 밖에 있습니다.
stateDiagram-v2
[*] --> START
START --> RUNNING
START --> COMPLETE
START --> FAIL
START --> ABORT
RUNNING --> COMPLETE
RUNNING --> FAIL
RUNNING --> ABORT
COMPLETE --> [*]
FAIL --> [*]
ABORT --> [*]
받는 쪽은 runId 가 같은 이벤트를 한 실행으로 모읍니다.
그래서 START 에는 읽을 표만 적어도 됩니다. 쓴 표는 COMPLETE 에 보태면 됩니다.
작업이 끝나야 알 수 있는 정보는 끝날 때 보내는 이벤트에 싣습니다.
이름이 같으면 계보가 이어진다
이벤트 한 건은 작업 하나의 앞뒤만 압니다. 계보 전체는 받는 쪽이 여러 이벤트를 이어 붙여 만듭니다. 잇는 기준은 데이터셋의 네임스페이스와 이름입니다.
쇼핑몰에 작업이 하나 더 있다고 해 봅시다. 매출 보고서 작업 sales_report 가 일별 매출 표를 읽어 매출 보고서 표를 씁니다.
집계 작업의 이벤트는 일별 매출 표를 출력으로 적습니다. 보고서 작업의 이벤트는 같은 표를 입력으로 적습니다.
받는 쪽은 두 이벤트에 같은 네임스페이스와 이름이 나오면 둘을 한 데이터셋으로 보고 선을 긋습니다.
flowchart TD
A["주문 표 · shop.public.orders"] --> J1["작업 · daily_sales"]
J1 --> B["일별 매출 표 · shop.mart.daily_sales"]
B --> J2["작업 · sales_report"]
J2 --> C["매출 보고서 표"]
그래서 이름 짓는 법이 중요합니다. 한 도구는 데이터베이스 서버를 db:5432 로 부르고 다른 도구는 db.internal:5432 로 부른다고 해 봅시다.
같은 표가 두 데이터셋으로 갈립니다. 선이 끊깁니다. 계보가 거기서 멈춥니다.
OpenLineage 는 이를 막으려고 저장소 종류마다 네임스페이스와 이름을 짓는 규칙을 따로 정해 둡니다.
PostgreSQL 표라면 네임스페이스는 postgres://호스트:포트 꼴입니다. 이름은 데이터베이스.스키마.표 꼴입니다.
여기서 스키마는 데이터베이스 안에서 표를 묶는 폴더 같은 단위입니다.
앞 이벤트의 shop.public.orders 가 이 꼴을 따른 이름입니다. shop 데이터베이스의 public 스키마에 있는 orders 표입니다.
패싯으로 덧붙이는 정보
작업·실행·데이터셋에 붙는 기본 필드는 몇 개 안 됩니다. 나머지 정보는 패싯(facet)이라는 조각으로 붙입니다. 패싯은 이름 하나 아래에 관련 필드를 묶은 JSON 객체입니다. 필요한 패싯만 골라 붙이므로 이벤트가 쓸데없이 커지지 않습니다.
패싯은 붙는 대상이 정해져 있습니다. 자주 쓰는 것은 아래와 같습니다.
| 패싯 | 붙는 대상 | 담는 것 |
|---|---|---|
schema |
데이터셋 | 표의 열 구조 — 열 이름과 타입 |
columnLineage |
출력 데이터셋 | 출력 열마다 어느 입력 열에서 왔나 |
sql |
작업 | 작업이 돌린 SQL(Structured Query Language, 데이터베이스 질의 언어) 문 |
sourceCodeLocation |
작업 | 작업 코드가 있는 저장소와 경로 |
parent |
실행 | 이 실행을 품은 바깥 실행 |
errorMessage |
실행 | 실패했을 때의 오류 메시지 |
schema 패싯이 말하는 스키마는 표의 열 구조입니다. 앞 절 이름 속의 스키마(표를 묶는 단위)와는 뜻이 다릅니다.
columnLineage 는 표 단위보다 한 단계 잘게 본 계보입니다. 「일별 매출 표의 amount 열은 주문 표의 price 열과 quantity 열에서 왔다」를 적습니다.
열 하나를 지우거나 이름을 바꾸기 전에 그 열을 쓰는 곳을 찾을 때 씁니다.
parent 는 실행 사이의 포함 관계를 적습니다. Airflow 는 처리 단계 여럿을 묶은 작업 흐름을 돌립니다.
단계 하나가 OpenLineage 의 작업 하나로 기록됩니다. 작업 흐름 전체도 작업 하나로 기록됩니다.
흐름이 한 번 돌면 단계마다 실행이 하나씩 생깁니다. 단계의 실행마다 parent 로 바깥 흐름의 실행을 가리키면 받는 쪽이 둘을 묶어 보여 줄 수 있습니다.
패싯마다 두 필드가 따라붙습니다. 자기를 만든 쪽을 적는 _producer 와 자기 구조 정의를 가리키는 주소 _schemaURL 입니다.
패싯이 제 구조 정의를 스스로 가리키므로 받는 쪽은 처음 보는 패싯도 해석하거나 건너뛸 수 있습니다.
그래서 규격에 없는 패싯을 새로 만들어 붙여도 받는 쪽이 깨지지 않습니다.
이벤트를 내는 쪽과 받는 쪽
이벤트를 내는 쪽은 대개 도구에 붙이는 연동 코드입니다. 작업을 짜는 사람이 이벤트를 손으로 쓰지 않습니다. 연동 코드가 도구의 동작을 지켜보다가 시작과 끝에 이벤트를 만듭니다.
| 도구 | 연동 코드가 붙는 방식 |
|---|---|
| Airflow | 작업 흐름의 각 단계가 시작하고 끝날 때 이벤트를 보냅니다. 단계마다 무엇을 읽고 쓰는지는 그 단계의 코드가 알려 줍니다 |
| Spark | 작업 진행을 지켜보는 리스너를 붙입니다. Spark 가 짠 계산 계획에서 읽고 쓴 파일과 표를 뽑습니다 |
| dbt | 모델은 dbt 에서 표 하나를 만드는 SQL 파일입니다. dbt-ol 이 dbt 명령을 감싸 돌립니다. 명령이 끝나면 dbt 가 남긴 결과 파일을 읽어 모델마다 이벤트를 만듭니다 |
이벤트를 나르는 방법을 전송(transport)이라고 부릅니다. 흔한 것은 받는 쪽 주소로 HTTP(HyperText Transfer Protocol, 하이퍼텍스트 전송 규약) 요청을 보내는 방식입니다. 시험할 때는 파일이나 화면에 찍어 보기도 합니다.
이벤트가 많으면 메시지 큐를 사이에 둡니다. 메시지 큐는 보내는 쪽과 받는 쪽 사이에서 메시지를 잠시 쌓아 두는 중간 저장소입니다. 받는 쪽은 쌓인 이벤트를 제 속도로 꺼내 갑니다. Apache Kafka 가 흔히 쓰입니다.
flowchart TD
subgraph 내는쪽["이벤트를 내는 쪽"]
AF["Airflow"]
SP["Spark"]
DB["dbt"]
end
AF --> T["전송 · HTTP 또는 Kafka"]
SP --> T
DB --> T
T --> M["받는 쪽 · Marquez 등"]
M --> G["계보 그래프"]
받는 쪽은 이벤트를 쌓아 계보 그래프를 만듭니다. 기준 구현인 Marquez 가 이 일을 합니다. 받는 쪽이 Marquez 하나뿐인 것은 아닙니다. 여러 데이터 카탈로그 제품도 이 이벤트를 받아들입니다. 데이터 카탈로그는 회사 안의 데이터를 찾아보게 목록으로 정리해 주는 도구입니다.
실행 중에 모으는 계보
계보를 얻는 다른 길도 있습니다. 작업 코드에 든 SQL 문을 파서로 읽어 표 사이의 관계를 뽑는 방법입니다. 파서는 글을 읽어 문법 구조로 쪼개는 프로그램입니다. 이 방법은 코드를 읽어서 작업이 무엇을 읽고 쓸지 짐작합니다.
OpenLineage 이벤트는 작업이 돈 순간에 나갑니다. 그래서 그 실행이 무엇을 읽고 썼는지를 적습니다. 언제 돌았는지와 실패했는지도 함께 남습니다. 반대로 한 번도 돌지 않은 코드는 계보에 나타나지 않습니다.
쓰는 때와 안 쓰는 때
여러 도구에 걸친 데이터 파이프라인의 계보를 한곳에 모으고 싶을 때 씁니다. 데이터 파이프라인은 데이터를 여러 단계에 걸쳐 옮기고 가공하는 작업의 사슬입니다. Airflow 가 돌리는 Spark 작업이 만든 표를 dbt 가 다시 가공하는 식이면 도구 하나의 화면으로는 전체가 안 보입니다. 표를 바꾸기 전에는 이 모음에서 영향받을 곳을 찾습니다. 숫자가 틀렸을 때는 어느 실행이 원인인지 거슬러 찾습니다.
파이프라인이 도구 하나 안에서 끝나면 그 도구가 보여 주는 계보로 충분할 수 있습니다. dbt 는 자기 모델 사이의 관계를 스스로 그려 줍니다. 그 밖으로 데이터가 나가지 않으면 이벤트를 따로 모을 까닭이 적습니다.
쓰면 치르는 값도 있습니다. 도구마다 연동 코드를 붙여야 합니다. 도구의 판이 오르면 연동 코드도 같이 맞춰야 합니다. 연동이 없는 손 스크립트는 이벤트를 직접 보내야 합니다. 안 보내면 그 스크립트가 만든 표는 계보에서 빠집니다. 이벤트를 받아 쌓는 저장소도 따로 굴려야 합니다.
관련 항목
OpenLineage 가 기록하는 계보와 메타데이터
데이터 계보 · 데이터 리니지 · 열 단위 계보 · 메타데이터 · 운영 메타데이터 · 기술 메타데이터
OpenLineage 이벤트를 이루는 구성 요소
실행 이벤트 · 네임스페이스 · UUID · JSON · JSON Schema · 스키마
OpenLineage 이벤트를 내보내는 도구
Apache Airflow · Apache Spark · dbt · Apache Flink · Great Expectations
OpenLineage 이벤트를 받아 쌓는 저장소와 카탈로그
Marquez · DataHub · 데이터 카탈로그 · 그래프 데이터베이스
OpenLineage 이벤트를 나르는 전송 수단
HTTP · Apache Kafka · 메시지 큐 · 이벤트 스트리밍
OpenLineage 와 같은 발상으로 형식을 통일하는 공개 규격
OpenTelemetry · OpenAPI · CloudEvents · AsyncAPI
OpenLineage 계보로 하는 분석
영향 분석 · 근본 원인 분석 · 데이터 품질 · 데이터 관측성 · 스키마 진화
OpenLineage 계보가 이어 주는 저장소와 가공 단계
데이터 웨어하우스 · 데이터 레이크 · 데이터 마트 · ETL · 데이터 파이프라인 · 오케스트레이션 · SQL · 파서
다른 이름: Open Lineage · 오픈리니지 · OpenLineage 규격