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_size가 azure_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⟩'
);
기본 PROVIDER는 CONFIG예요.
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.csvroot/l_receipmonth=1997-10/l_shipmode=AIR/data_0.csvroot/l_receipmonth=1997-10/l_shipmode=TRUCK/data_0.csvroot/l_receipmonth=1997-11/l_shipmode=SHIP/data_0.csvroot/l_receipmonth=1997-11/l_shipmode=AIR/data_0.csvroot/l_receipmonth=1997-11/l_shipmode=TRUCK/data_0.csvroot/l_receipmonth=1997-12/l_shipmode=SHIP/data_0.csvroot/l_receipmonth=1997-12/l_shipmode=AIR/data_0.csvroot/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.csvroot/l_receipmonth=1997-11/l_shipmode=SHIP/data_0.csvroot/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-10root/l_receipmonth=1997-11root/l_receipmonth=1997-12
- 하위 디렉토리를 필터링하고 나열:
root/l_receipmonth=1997-10,root/l_receipmonth=1997-11,root/l_receipmonth=1997-12root/l_receipmonth=1997-10/l_shipmode=SHIProot/l_receipmonth=1997-10/l_shipmode=AIRroot/l_receipmonth=1997-10/l_shipmode=TRUCKroot/l_receipmonth=1997-11/l_shipmode=SHIProot/l_receipmonth=1997-11/l_shipmode=AIRroot/l_receipmonth=1997-11/l_shipmode=TRUCKroot/l_receipmonth=1997-12/l_shipmode=SHIProot/l_receipmonth=1997-12/l_shipmode=AIRroot/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=SHIProot/l_receipmonth=1997-10/l_shipmode=SHIP/data_0.csvroot/l_receipmonth=1997-11/l_shipmode=SHIP/data_0.csvroot/l_receipmonth=1997-12/l_shipmode=SHIP/data_0.csv
보시다시피 Blob 엔드포인트는 디렉토리 개념을 지원하지 않으므로 필터가 나열 후에만 수행될 수 있는 반면, ADLS 엔드포인트는 재귀적으로 파일을 나열해요. 특히 파티션/디렉토리 수가 많을수록 성능 차이가 매우 클 수 있어요.
더 알아보기 (Learn more)
- Secrets Manager 문서에서 secret 제공자를 확인해요.
- COPY 문 문서에서 데이터 쓰기 방법을 살펴볼 수 있어요.