Multi-Team 개요와 리소스 격리
Apache Airflow Official Documentation (in-repo snapshot) — Apache Software Foundation core-concepts/multi-team.rst - 개요, Core Concepts, Resource Isolation, Enabling Multi-Team Mode, Creating/Managing Teams, Configuring Team Resources (L18-343)
이 모듈을 다 읽으면
- Multi-Team이 멀티테넌시와 어떻게 다른지 설명할 수 있다
- Team ↔ Dag Bundle ↔ Task/Dag/Callback/Trigger의 관계 체인을 설명할 수 있다
- Variables/Connections/Pools/XCom이 팀 스코프로 격리되는 방식을 설명할 수 있다
Multi-Team Airflow는 하나의 배포 안에서 여러 팀에게 리소스 격리와 팀 기반 접근 제어를 제공하는, 3.3 기준 아직 실험적인(preview) 기능이다. 이 모듈은 Multi-Team의 정의와 적용 범위, Team·Dag Bundle의 소유 관계, Variables/Connections/Pools/XCom/Secrets의 팀 스코프 격리 방식, 그리고 활성화와 팀 CLI 관리를 다룬다.
Multi-Team이란 무엇이고 언제 쓰는가
Multi-Team은 아직 실험적(experimental)인 preview 기능이다. Airflow 3.3이 이 기능의 상당 부분을 제공하지만 아직 완전하지 않으며, 일부 기능은 이후 릴리스(3.4+)에 예정되어 있고 사용자 피드백에 따라 사전 경고 없이 동작이 바뀔 수 있다.
Multi-Team Airflow는 조직이 하나의 Airflow 배포 안에서 여러 팀을 운영하면서 자원 격리와 팀 기반 접근 제어를 제공하는 기능이다. 인프라를 여러 팀에 공유하면서도 리소스의 논리적 분리를 유지해야 하는 중·대형 조직을 위해 설계되었다. Multi-Team Airflow는 멀티테넌시(multi-tenancy)와는 다르다 — 하나의 배포 안에서의 격리를 제공할 뿐 완전한 테넌트 분리를 위한 것이 아니다. 모든 팀은 같은 Airflow 인프라, 스케줄러, 메타데이터 데이터베이스를 공유한다.
Multi-Team 모드는 다음과 같은 경우에 적합하다: 여러 팀이 Airflow 인프라를 공유해야 할 때, UI·API 레벨에서 팀 간 Variables·Connections·Secrets 등의 리소스 격리가 필요할 때(태스크 레벨 격리에는 한계가 있으므로 보안 모델 문서 참고), 팀별로 분리된 실행 환경을 원할 때, Airflow UI에서 팀별로 분리된 뷰를 원할 때, 단일 Airflow 배포를 공유해 운영 부담이나 비용을 최소화하고 싶을 때.
핵심 포인트
- Multi-Team은 멀티테넌시가 아니다 — 하나의 배포/스케줄러/메타DB를 공유하면서 논리적으로만 분리한다
- 많은 팀이 인프라를 공유해야 하고, UI/API 레벨의 리소스 격리와 팀별 실행 환경이 필요할 때 적합하다
- 3.3 기준 아직 실험적(preview) 기능이며 일부 기능은 3.4+에서 완성될 예정이다
Team, Dag Bundle, 그리고 소유 관계 체인
Team(팀)은 조직 내 사용자 그룹을 나타내는 논리적 단위다. 팀은 부분적으로 Airflow 메타데이터 데이터베이스에 저장되며 리소스 격리의 기반이 된다. DB의 팀 구조는 매우 단순해서 name 필드 하나만 갖는다 — 3~50자, 소문자·숫자·하이픈·언더스코어로만 구성되고 언더스코어 두 개가 연속으로 오면 안 된다.
팀은 Dag Bundle을 통해 Dag와 연결된다. 팀 이름과 번들 이름을 잇는 별도의 연관 테이블을 쓴다. 하나의 Dag Bundle은 최대 한 팀에만 속할 수 있다. 번들이 어떤 팀에 배정되면: 그 번들 안의 모든 Dag가 그 팀에 속하고, 그 Dag들의 태스크는 팀 소속을 상속하며, 그 Dag들과 연관된 모든 콜백도 팀 소속을 상속하고, 그 Dag들의 태스크가 만든 트리거도 팀 소속을 상속한다. 스케줄러는 이 관계를 이용해 어떤 실행기를 쓸지 결정한다. 관계 체인은 Task/Callback → Dag → Dag Bundle → Team 순이다.
핵심 포인트
- 팀 이름은 3~50자, 소문자/숫자/하이픈/언더스코어만 허용되고 언더스코어 두 개 연속은 금지된다
- Dag Bundle은 최대 한 팀에만 속할 수 있고, 그 번들의 모든 Dag·태스크·콜백·트리거가 팀을 상속한다
- 관계 체인은 Task/Callback → Dag → Dag Bundle → Team 순이며, 스케줄러는 이 관계로 실행기를 고른다
리소스 격리: Variables, Connections, Pools, XCom, Secrets
Multi-Team 모드가 켜지면 다음 리소스가 팀별로 스코프될 수 있다: Variables(팀원은 자기 팀 소유이거나 글로벌인 변수만 접근), Connections(같은 패턴), Pools(팀에 배정 가능), XComs(태스크는 자기 팀의 Dag XCom만 접근하며, 읽기의 경우 글로벌 Dag까지 포함). 팀 배정이 없는 리소스는 글로벌로 취급되어 모든 팀이 접근할 수 있다.
XCom은 특히 완전히 팀 스코프다. Multi-Team 모드가 켜지면 Task Execution API를 통한 XCom 접근은, 요청하는 태스크의 팀(Dag → 번들 → 팀 관계로 유도)으로 스코프되며 팀 간 XCom 공유는 전혀 없다. 태스크는 자기 팀 Dag의 XCom과, 읽기에 한해 글로벌(팀 없는) Dag의 XCom을 읽을 수 있다. 태스크는 자기 팀 Dag의 XCom만 쓰거나 지울 수 있다 — 글로벌 Dag의 XCom을 변경할 수는 없으며, 이는 팀 스코프 Variables·Connections의 동작 방식과 같다. 이 경계는 Execution API에서만 강제되며, DB에 직접 접근하는 컴포넌트는 이 제약을 받지 않는다(보안 모델 문서 참고).
시크릿 백엔드도 팀을 인식한다 — 환경 변수, 메타스토어, 로컬 파일시스템 백엔드는 팀 인식(team-aware)이며, 커스텀 시크릿 백엔드는 사안에 따라 지원된다. 태스크가 Variable이나 Connection을 요청하면, 시크릿 백엔드는 있다면 팀별 값을 반환하며 요청하는 태스크의 팀을 기준으로 자동으로 올바른 값을 찾아준다.
핵심 포인트
- XCom은 완전히 팀 스코프다 — 자기 팀 또는 글로벌(팀 없는) Dag의 XCom만 읽을 수 있고, 쓰기/삭제는 자기 팀 Dag에만 가능하다
- 이 XCom 경계는 Task Execution API에서만 강제되며, DB에 직접 접근하는 컴포넌트는 제약받지 않는다
- 환경변수/메타스토어/로컬 파일시스템 시크릿 백엔드는 팀을 인식해 요청 태스크의 팀에 맞는 값을 자동으로 돌려준다
Auth Manager 요구사항과 활성화·팀 관리 CLI
Multi-Team을 쓰려면 사용 중인 auth manager가 이를 지원해야 한다. 호환되는 auth manager는 두 메서드를 구현해야 한다: is_authorized_team(사용자가 특정 팀에 대한 특정 액션을 수행할 권한이 있는지 판단하며, 주로 팀 소속 확인에 쓰인다), _get_teams(auth manager가 정의한 팀 집합을 반환한다). 초기화 시 Airflow는 auth manager가 정의한 팀과 메타데이터 DB의 팀을 비교한다 — 어느 방향이든 불일치(예: auth manager는 정의했지만 DB에는 없는 팀, 또는 그 반대)가 있으면 UserWarning이 발생한다. 시작 자체는 막히지 않으므로 시작 로그에서 이 경고를 확인해야 한다. 사용 중인 auth manager가 이 메서드들을 구현하지 않았다면 런타임에 NotImplementedError가 발생한다. Multi-Team과 호환되는 auth manager의 예로는 Simple auth manager(개발 용도로만 권장)와 Keycloak auth manager가 있다.
Multi-Team 모드를 켜려면 airflow.cfg에 `[core] multi_team = True`를 설정하거나 `AIRFLOW__CORE__MULTI_TEAM=True` 환경 변수를 쓴다. 기존 배포에서 이 설정을 바꾸는 것은 신중한 계획이 필요한 작업이다.
팀 관리는 Airflow CLI로 한다. `airflow teams create <name>`으로 팀을 만들며 이름 규칙(3~50자, 소문자/숫자/하이픈/언더스코어, 연속 언더스코어 금지)을 따라야 한다. `airflow teams list`로 배포된 모든 팀과 이름을 조회한다. `airflow teams delete <name>`으로 팀을 삭제하며(--yes로 확인 프롬프트 생략 가능), 연관된 리소스(Dag 번들, Variables, Connections, Pools)가 있는 팀은 삭제할 수 없으므로 먼저 연관을 제거해야 한다. `airflow teams sync`(3.3.0+)는 Dag 번들 설정에 있는 각 team_name 중 DB에 아직 없는 팀을 자동으로 생성해, 번들 설정을 팀을 선언하는 단일 지점으로 쓸 수 있게 해주며, 이미 존재하던 팀을 포함해 기본 풀이 없는 팀에는 기본 풀도 만들어준다. `airflow teams verify`(3.4.0+)는 멀티팀 설정을 DB와 대조해 기본 풀이 없는 팀이나, Dag 번들이 참조하지만 DB에 없는 팀 같은 문제를 보고한다.
핵심 포인트
- 멀티팀을 쓰려면 auth manager가 is_authorized_team과 _get_teams를 구현해야 하며, 없으면 런타임에 NotImplementedError가 발생한다
- auth manager와 DB의 팀 목록이 어긋나면 시작은 막히지 않지만 UserWarning이 로그에 남는다
- airflow teams sync(3.3.0+)는 Dag 번들 설정에 있는 team_name들을 DB에 자동 생성하고 누락된 기본 풀도 만들어준다
- airflow teams verify(3.4.0+)는 기본 풀이 없는 팀이나 번들이 참조하지만 DB에 없는 팀을 점검한다
Team-scoped Variables/Connections/Pools 설정 방법
팀 스코프 변수는 환경 변수 포맷 `AIRFLOW_VAR__{TEAM}___{KEY}`로 설정한다 — 팀 앞은 더블 언더스코어(AIRFLOW_VAR__ 접두사의 일부), 팀과 키 사이는 트리플 언더스코어다. 예: 글로벌 변수는 `AIRFLOW_VAR_MY_VARIABLE`, team_a 전용 변수는 `AIRFLOW_VAR__TEAM_A___MY_VARIABLE`. Connections도 같은 패턴을 따른다: `AIRFLOW_CONN__{TEAM}___{CONN_ID}`.
Pools는 `airflow pools set <name> <slots> "<설명>" --team-name <team>`으로 팀에 배정한다(--team-name은 multi_team이 꺼져 있으면 거부되고, 지정하는 팀은 미리 존재해야 한다). Multi-Team이 켜져 있으면 `airflow teams create`가 팀마다 `default_pool_<team_name>`이라는 기본 풀을 자동으로 만들며, 그 팀의 번들에 속한 태스크는 다른 풀을 명시적으로 지정하지 않는 한 이 기본 풀을 자동으로 쓴다. REST API로도 배정할 수 있다 — `POST /api/v2/pools` 요청 본문에 team_name 필드를 넣거나, `PATCH /api/v2/pools/{pool_name}`으로 기존 풀의 팀 배정을 바꾼다(team_name을 생략하거나 null로 주면 다시 글로벌 풀이 된다). UI에서는 Admin > Pools에서 Multi-Team이 켜져 있을 때 나타나는 Team 드롭다운으로 배정한다.
핵심 포인트
- 팀 스코프 변수/커넥션의 환경변수 포맷은 AIRFLOW_VAR__{TEAM}___{KEY} / AIRFLOW_CONN__{TEAM}___{CONN_ID}로, 팀 앞은 더블 언더스코어, 팀과 키 사이는 트리플 언더스코어다
- airflow teams create는 팀마다 default_pool_<team_name>이라는 기본 풀을 자동 생성하고, 그 팀의 태스크는 별도 풀을 지정하지 않으면 이 풀을 쓴다
- REST API로 풀의 team_name을 PATCH할 때 생략하거나 null을 주면 다시 글로벌 풀이 된다