Azure 확장

Azure 확장

azure 확장은 Azure Blob Storage에 대한 파일 시스템 추상화를 DuckDB에 추가하는 로드 가능한 확장이에요. 데이터 읽기와 쓰기를 모두 지원해요.

출처: 문서

본문

azure 확장은 Azure Blob Storage에 대한 파일 시스템 추상화를 DuckDB에 추가하는 로드 가능한 확장으로, 데이터 읽기와 쓰기를 모두 가능하게 해요.

설치 및 로드

azure 확장은 첫 사용 시 공식 확장 저장소에서 투명하게 자동 로드돼요. 직접 설치하고 로드하려면 다음을 실행해요.

INSTALL azure;
LOAD azure;

사용법

인증이 설정되면 Azure 저장소를 다음과 같이 쿼리할 수 있어요.

Azure Blob Storage

허용 URI 스킴: az 또는 azure

SELECT count(*)
FROM 'az://⟨my_container⟩/⟨path⟩/⟨my_file⟩.⟨parquet_or_csv⟩';

Glob도 지원돼요.

SELECT *
FROM 'az://⟨my_container⟩/⟨path⟩/*.csv';
SELECT *
FROM 'az://⟨my_container⟩/⟨path⟩/**';

또는 완전 한정 경로 문법으로:

SELECT count(*)
FROM 'az://⟨my_storage_account⟩.blob.core.windows.net/⟨my_container⟩/⟨path⟩/⟨my_file⟩.⟨parquet_or_csv⟩';
SELECT *
FROM 'az://⟨my_storage_account⟩.blob.core.windows.net/⟨my_container⟩/⟨path⟩/*.csv';

Azure Data Lake Storage (ADLS)

허용 URI 스킴: abfss

SELECT count(*)
FROM 'abfss://⟨my_filesystem⟩/⟨path⟩/⟨my_file⟩.⟨parquet_or_csv⟩';

Glob도 지원돼요.

SELECT *
FROM 'abfss://⟨my_filesystem⟩/⟨path⟩/*.csv';
SELECT *
FROM 'abfss://⟨my_filesystem⟩/⟨path⟩/**';

또는 완전 한정 경로 문법으로:

SELECT count(*)
FROM 'abfss://⟨my_storage_account⟩.dfs.core.windows.net/⟨my_filesystem⟩/⟨path⟩/⟨my_file⟩.⟨parquet_or_csv⟩';
SELECT *
FROM 'abfss://⟨my_storage_account⟩.dfs.core.windows.net/⟨my_filesystem⟩/⟨path⟩/*.csv';

Azure Blob Storage에 쓰기

COPY을 사용해 데이터를 Azure Blob 또는 ADLSv2 저장소에 직접 쓸 수 있어요.

-- Write query results to a Parquet file on Blob Storage
COPY (SELECT * FROM my_table)
TO 'az://⟨my_container⟩/⟨path⟩/output.parquet';
-- Write a table to a CSV file on ADLSv2 Storage
COPY my_table
TO 'abfss://⟨my_container⟩/⟨path⟩/output.csv';

완전 한정 경로도 사용할 수 있어요.

COPY my_table
TO 'az://⟨my_storage_account⟩.blob.core.windows.net/⟨my_container⟩/⟨path⟩/output.parquet';

구성

다음 구성 옵션을 사용해 확장이 원격 파일을 읽는 방식을 제어해요.

이름 설명 타입 기본값
azure_http_stats EXPLAIN ANALYZE에 Azure Storage의 HTTP 정보 포함. BOOLEAN false
azure_read_transfer_concurrency 단일 병렬 읽기에 Azure 클라이언트가 사용할 수 있는 최대 스레드 수. azure_read_transfer_chunk_sizeazure_read_buffer_size보다 작으면 이 값을 1보다 크게 설정하면 Azure 클라이언트가 버퍼를 채우기 위해 동시 요청을 할 수 있음. BIGINT 5
azure_read_transfer_chunk_size 단일 요청에서 Azure 클라이언트가 읽을 최대 크기(바이트). azure_read_buffer_size의 약수로 설정하는 것이 권장됨. BIGINT 1024*1024
azure_read_buffer_size 읽기 버퍼 크기. azure_read_transfer_chunk_size로 균등하게 나눠지는 것이 권장됨. UBIGINT 1024*1024
azure_transport_option_type Azure SDK에서 사용할 기본 어댑터. 유효한 값: default 또는 curl. VARCHAR default
azure_context_caching 쿼리 수행 시 DuckDB 커넥션 컨텍스트에서 기본 Azure SDK HTTP 커넥션의 캐싱을 활성화/비활성화. 부작용이 의심되면 false로 설정해 끌 수 있음 (권장되지 않음). BOOLEAN true

azure_transport_option_type을 명시적으로 curl로 설정하면 다음 효과가 있어요.

  • Linux에서는 인증서 문제(Error: Invalid Error: Fail to get a new connection for: https://storage_account_name.blob.core.windows.net/. Problem with the SSL CA cert (path? access rights?))를 해결할 수 있어요. 확장이 여러 경로에서 번들 인증서를 찾으려고 하기 때문이에요 (curl은 기본적으로 그렇게 하지 않으며 정적 링크로 인해 잘못될 수 있음).
  • Windows에서는 기본 어댑터(WinHTTP)를 대체하여 모든 curl 기능(예: socks 프록시 사용)을 사용할 수 있게 해줘요.
  • 모든 운영 체제에서 다음 환경 변수를 존중해요.
    • CURL_CA_INFO: libcurl로 보내지는 인증 기관을 포함하는 PEM 인코딩 파일 경로. 이 옵션은 Linux에서만 동작하며 다른 플랫폼에서 설정하면 오류가 발생할 수 있음.
    • CURL_CA_PATH: libcurl로 보내지는 인증 기관을 포함하는 PEM 인코딩 파일을 보관하는 디렉토리 경로.

예제:

SET azure_http_stats = false;
SET azure_read_transfer_concurrency = 5;
SET azure_read_transfer_chunk_size = 1_048_576;
SET azure_read_buffer_size = 1_048_576;

인증

Azure 확장은 인증을 구성하는 두 가지 방법이 있어요. 권장 방법은 Secrets를 사용하는 것이에요.

Secret으로 인증

Azure 확장에는 여러 Secret 제공자가 있어요.

  • 서로 다른 저장소 계정에 대해 서로 다른 secret을 정의해야 한다면 SCOPE 구성을 사용해요. SCOPE는 끝에 슬래시가 필요하다는 점에 주의해요 (SCOPE 'azure://some_container/').
  • 완전 한정 경로를 사용하면 ACCOUNT_NAME 속성은 선택 사항이에요.
CONFIG 제공자

기본 제공자인 CONFIG(즉, 사용자 구성)는 커넥션 문자열 또는 익명으로 저장소 계정에 대한 접근을 허용해요. 예를 들어:

CREATE SECRET secret1 (
    TYPE azure,
    CONNECTION_STRING '⟨value⟩'
);

인증을 사용하지 않는다면 여전히 저장소 계정 이름을 지정해야 해요. 예를 들어:

CREATE SECRET secret2 (
    TYPE azure,
    PROVIDER config,
    ACCOUNT_NAME '⟨storage_account_name⟩'
);

기본 PROVIDERCONFIG예요.

credential_chain 제공자

credential_chain 제공자는 Azure 자격 증명 체인을 통해 Azure SDK가 자동으로 가져온 자격 증명으로 연결하는 것을 허용해요. 기본적으로는 Azure 문서가 지정한 순서대로 자격 증명을 시도하는 DefaultAzureCredential 체인이 사용돼요. 예를 들어:

CREATE SECRET secret3 (
    TYPE azure,
    PROVIDER credential_chain,
    ACCOUNT_NAME '⟨storage_account_name⟩'
);

DuckDB는 CHAIN 키워드로 특정 체인을 지정하는 것도 허용해요. 이는 순서대로 시도될 제공자의 세미콜론으로 구분된 목록(a;b;c)을 받아요. 예를 들어:

CREATE SECRET secret4 (
    TYPE azure,
    PROVIDER credential_chain,
    CHAIN 'cli;env',
    ACCOUNT_NAME '⟨storage_account_name⟩'
);

가능한 값은 다음과 같아요. cli; managed_identity; workload_identity; env; default;

명시적 CHAIN을 제공하지 않으면 기본값은 default가 돼요.

관리 ID (Managed Identity)

관리 ID(MI)는 credential_chain을 통해 우아하고 자동으로 사용될 수 있어요. 실행자가 단일 MI만 사용할 수 있는 일반적인 경우에는 구성이 필요 없어요.

실행 환경에 여러 ID가 있다면 MANAGED_IDENTITY 제공자를 사용하고 어떤 ID를 사용할지 지정해요. 이 제공자는 CLIENT_ID, OBJECT_ID 또는 RESOURCE_ID 중 하나로 ID를 지정할 수 있어요. 예:

CREATE SECRET secret1 (
    TYPE AZURE,
    PROVIDER MANAGED_IDENTITY,
    ACCOUNT_NAME '⟨storage account name⟩',
    CLIENT_ID '⟨used-assigned managed identity client id⟩'
);

이 제공자는 ID를 지정하지 않고 사용할 수 있어요. 단일 ID만 사용 가능하면 이 제공자는 credential_chain 제공자와 동일하게 동작하며 사용 가능한 단일 ID를 사용해요. 여러 ID가 사용 가능하면 동작은 정의되지 않아요(더 정확히는 Azure SDK가 정의). 따라서 이 상황에서는 명시적 ID 설정을 권장해요.

SERVICE_PRINCIPAL 제공자

SERVICE_PRINCIPAL 제공자는 Azure Service Principal (SPN)으로 연결하는 것을 허용해요.

secret으로:

CREATE SECRET azure_spn (
    TYPE azure,
    PROVIDER service_principal,
    TENANT_ID '⟨tenant_id⟩',
    CLIENT_ID '⟨client_id⟩',
    CLIENT_SECRET '⟨client_secret⟩',
    ACCOUNT_NAME '⟨storage_account_name⟩'
);

또는 인증서로:

CREATE SECRET azure_spn_cert (
    TYPE azure,
    PROVIDER service_principal,
    TENANT_ID '⟨tenant_id⟩',
    CLIENT_ID '⟨client_id⟩',
    CLIENT_CERTIFICATE_PATH '⟨client_cert_path⟩',
    ACCOUNT_NAME '⟨storage_account_name⟩'
);

프록시 구성

secret 사용 시 프록시 정보를 구성하려면 secret 정의에 HTTP_PROXY, PROXY_USER_NAME, PROXY_PASSWORD를 추가할 수 있어요. 예를 들어:

CREATE SECRET secret5 (
    TYPE azure,
    CONNECTION_STRING '⟨value⟩',
    HTTP_PROXY 'http://localhost:3128',
    PROXY_USER_NAME 'john',
    PROXY_PASSWORD 'doe'
);
  • secret 사용 시 HTTP_PROXY 환경 변수는 명시적 값을 제공하지 않는 한 여전히 존중돼요.
  • secret 사용 시 변수로 인증 세션의 SET 변수는 무시돼요.
  • Azure credential_chain 제공자의 경우 실제 토큰은 secret이 생성될 때가 아니라 쿼리 시점에 가져와져요.

변수로 인증 (폐기 예정)

SET variable_name = variable_value;

여기서 variable_name은 다음 중 하나일 수 있어요.

이름 설명 타입 기본값
azure_storage_connection_string Azure 커넥션 문자열. Azure 요청을 인증하고 구성하는 데 사용. STRING -
azure_account_name Azure 계정 이름. 설정하면 확장이 자격 증명을 자동으로 감지하려 시도 (커넥션 문자열을 전달하면 사용되지 않음). STRING -
azure_endpoint Azure 자격 증명 제공자가 사용될 때 Azure 엔드포인트 재정의. STRING blob.core.windows.net
azure_credential_chain ;로 구분된 문자열 형식의 Azure 자격 증명 제공자 순서 목록. 예: 'cli;managed_identity;env'. 가능한 값 목록은 credential_chain 제공자 섹션 참고. 커넥션 문자열을 전달하면 사용되지 않음. STRING -
azure_http_proxy Azure 로그인 및 요청 시 사용할 프록시. STRING HTTP_PROXY 환경 변수 (설정된 경우).
azure_proxy_user_name 필요한 경우 HTTP 프록시 사용자 이름. STRING -
azure_proxy_password 필요한 경우 HTTP 프록시 비밀번호. STRING -

추가 정보

로깅

Azure 확장은 Azure Blob storage에 연결하기 위해 Azure SDK에 의존하며 SDK 로그를 콘솔에 인쇄하는 것을 지원해요. 로그 수준을 제어하려면 AZURE_LOG_LEVEL 환경 변수를 설정해요.

예를 들어 verbose 로그는 Python에서 다음과 같이 활성화할 수 있어요.

import os
import duckdb

os.environ["AZURE_LOG_LEVEL"] = "verbose"

duckdb.sql("CREATE SECRET myaccount (TYPE azure, PROVIDER credential_chain, SCOPE 'az://myaccount.blob.core.windows.net/')")
duckdb.sql("SELECT count(*) FROM 'az://myaccount.blob.core.windows.net/path/to/blob.parquet'")

ADLS와 Blob Storage의 차이

ADLS가 Blob storage와 유사한 기능을 구현하긴 하지만, globbing에 특히 (복잡한) glob 패턴을 사용할 때 ADLS 엔드포인트를 사용하는 것에는 몇 가지 중요한 성능 이점이 있어요.

이를 설명하기 위해 Blob과 ADLS 엔드포인트를 각각 사용해 내부적으로 glob이 어떻게 수행되는지 예를 살펴볼게요.

다음 파일 시스템을 사용해요.

root
├── l_receipmonth=1997-10
│   ├── l_shipmode=AIR
│   │   └── data_0.csv
│   ├── l_shipmode=SHIP
│   │   └── data_0.csv
│   └── l_shipmode=TRUCK
│       └── data_0.csv
├── l_receipmonth=1997-11
│   ├── l_shipmode=AIR
│   │   └── data_0.csv
│   ├── l_shipmode=SHIP
│   │   └── data_0.csv
│   └── l_shipmode=TRUCK
│       └── data_0.csv
└── l_receipmonth=1997-12
    ├── l_shipmode=AIR
    │   └── data_0.csv
    ├── l_shipmode=SHIP
    │   └── data_0.csv
    └── l_shipmode=TRUCK
        └── data_0.csv

다음 쿼리는 Blob 엔드포인트를 통해 수행돼요.

SELECT count(*)
FROM 'az://root/l_receipmonth=1997-*/l_shipmode=SHIP/*.csv';

다음 단계를 수행해요.

  • 접두사 root/l_receipmonth=1997-로 모든 파일을 나열
    • root/l_receipmonth=1997-10/l_shipmode=SHIP/data_0.csv
    • root/l_receipmonth=1997-10/l_shipmode=AIR/data_0.csv
    • root/l_receipmonth=1997-10/l_shipmode=TRUCK/data_0.csv
    • root/l_receipmonth=1997-11/l_shipmode=SHIP/data_0.csv
    • root/l_receipmonth=1997-11/l_shipmode=AIR/data_0.csv
    • root/l_receipmonth=1997-11/l_shipmode=TRUCK/data_0.csv
    • root/l_receipmonth=1997-12/l_shipmode=SHIP/data_0.csv
    • root/l_receipmonth=1997-12/l_shipmode=AIR/data_0.csv
    • root/l_receipmonth=1997-12/l_shipmode=TRUCK/data_0.csv
  • 요청된 패턴 root/l_receipmonth=1997-*/l_shipmode=SHIP/*.csv로 결과를 필터링
    • root/l_receipmonth=1997-10/l_shipmode=SHIP/data_0.csv
    • root/l_receipmonth=1997-11/l_shipmode=SHIP/data_0.csv
    • root/l_receipmonth=1997-12/l_shipmode=SHIP/data_0.csv

한편 동일한 쿼리는 datalake 엔드포인트를 통해 다음과 같이 수행할 수 있어요.

SELECT count(*)
FROM 'abfss://root/l_receipmonth=1997-*/l_shipmode=SHIP/*.csv';

이것은 다음 단계를 수행해요.

  • root/의 모든 디렉토리를 나열
    • root/l_receipmonth=1997-10
    • root/l_receipmonth=1997-11
    • root/l_receipmonth=1997-12
  • 하위 디렉토리를 필터링하고 나열: root/l_receipmonth=1997-10, root/l_receipmonth=1997-11, root/l_receipmonth=1997-12
    • root/l_receipmonth=1997-10/l_shipmode=SHIP
    • root/l_receipmonth=1997-10/l_shipmode=AIR
    • root/l_receipmonth=1997-10/l_shipmode=TRUCK
    • root/l_receipmonth=1997-11/l_shipmode=SHIP
    • root/l_receipmonth=1997-11/l_shipmode=AIR
    • root/l_receipmonth=1997-11/l_shipmode=TRUCK
    • root/l_receipmonth=1997-12/l_shipmode=SHIP
    • root/l_receipmonth=1997-12/l_shipmode=AIR
    • root/l_receipmonth=1997-12/l_shipmode=TRUCK
  • 하위 디렉토리를 필터링하고 나열: root/l_receipmonth=1997-10/l_shipmode=SHIP, root/l_receipmonth=1997-11/l_shipmode=SHIP, root/l_receipmonth=1997-12/l_shipmode=SHIP
    • root/l_receipmonth=1997-10/l_shipmode=SHIP/data_0.csv
    • root/l_receipmonth=1997-11/l_shipmode=SHIP/data_0.csv
    • root/l_receipmonth=1997-12/l_shipmode=SHIP/data_0.csv

보시다시피 Blob 엔드포인트는 디렉토리 개념을 지원하지 않으므로 필터가 나열 후에만 수행될 수 있는 반면, ADLS 엔드포인트는 재귀적으로 파일을 나열해요. 특히 파티션/디렉토리 수가 많을수록 성능 차이가 매우 클 수 있어요.

더 알아보기 (Learn more)

  • Secrets Manager 문서에서 secret 제공자를 확인해요.
  • COPY 문 문서에서 데이터 쓰기 방법을 살펴볼 수 있어요.