Keyboard shortcuts

Press ← or → to navigate between chapters

Press ? to show this help

Press Esc to hide this help

데이터셋의 S3 호환 API

S3 호환 API를 사용하면 AWS CLI, AWS SDK, Hadoop S3 파일시스템과 Cyberduck 같은 S3 클라이언트에서 Foundry 데이터셋의 파일을 읽고 쓰거나 삭제할 수 있습니다. 멀티파트 업로드도 지원합니다.

전체 Amazon S3 API를 구현하는 것은 아닙니다. 버킷 생성과 삭제는 지원하지 않으므로 데이터셋을 Foundry에서 미리 만들어야 합니다. 브랜치 생성이나 트랜잭션의 세부 제어가 필요하면 Foundry 데이터셋 REST API와 SDK를 사용합니다.

버킷, 객체 키와 데이터셋

S3 버킷은 Foundry 데이터셋 하나에 대응하고, 버킷 이름은 데이터셋 RID입니다. 객체 키는 그 데이터셋 내부의 논리 파일 경로입니다. 예를 들어 top-level-file.csv와 subfolder/nested-file.csv를 사용할 수 있습니다.

Compass 폴더와 프로젝트는 데이터를 구성하는 컨테이너이므로 S3 버킷으로 지정할 수 없습니다. 로그 파일을 따로 묶으려면 기존 데이터셋에서 logs/nested-file.csv 같은 키를 사용하거나 별도 데이터셋을 만듭니다. Foundry 폴더를 새로 만드는 것은 S3 파일 저장 위치를 만드는 동작이 아닙니다.

브랜치 지정

기본 브랜치가 아닌 브랜치에 접근하려면 버킷 이름을 <dataset-rid>.<branch-name>으로 구성합니다. 브랜치 이름을 생략하면 데이터셋의 기본 브랜치를 사용합니다. 지정한 브랜치는 미리 존재해야 합니다.

직접 사용할 수 있는 브랜치 이름 문자는 a-z, A-Z, 0-9, -, _입니다. /처럼 허용되지 않는 문자가 있으면 브랜치 이름을 Base64로 인코딩하고 끝의 = 패딩을 제거합니다. 예를 들어 feature/my-branch의 ZmVhdHVyZS9teS1icmFuY2g=에서 마지막 =를 빼고 사용합니다.

다음은 가상 RID와 브랜치를 사용하는 형식입니다.

ri.foundry.main.dataset.bafb7e96-84f1-4d23-a4a5-40b17c6912e7.mybranch

과거 트랜잭션 지정

버킷 이름을 <dataset-rid>.<transaction-rid>로 지정하면 과거 버전에 접근합니다. 확정된 트랜잭션만 사용할 수 있으며 읽기 전용입니다. 이 버킷에 객체를 추가하거나 삭제할 수 없습니다.

ri.foundry.main.dataset.bafb7e96-84f1-4d23-a4a5-40b17c6912e7.ri.foundry.main.transaction.0cdfe8c9-f595-4859-a194-7daecff9d6fe

자동 트랜잭션 관리

S3에는 Foundry 트랜잭션 개념이 없으므로 API가 다음 규칙으로 트랜잭션을 관리합니다.

  1. 파일 쓰기나 삭제가 실제로 발생할 때 트랜잭션을 엽니다.
  2. 여러 파일을 동시에 쓰면 같은 UPDATE 트랜잭션을 사용합니다. 여러 파일을 동시에 삭제하면 같은 DELETE 트랜잭션을 사용합니다.
  3. 쓰기와 삭제를 동시에 진행하는 것은 지원하지 않습니다. 브랜치에는 열린 트랜잭션이 최대 하나만 존재할 수 있기 때문입니다. 먼저 도착한 요청 유형이 트랜잭션을 열고, 다른 유형은 그것이 확정될 때까지 자체 트랜잭션을 열 수 없습니다. 두 유형의 변경이 섞여 적용되지는 않지만 처리 순서는 도착 시점에 따라 달라집니다.
  4. 쓰기나 삭제 후 짧은 비활성 기간이 지나면 자동 확정합니다. 계속 업로드가 들어오는 경우에는 일정 시간이 지난 뒤 활성 업로드가 없는 시점에 확정합니다. 병렬 업로드가 계속 진행 중이면 모든 업로드가 끝날 때까지 열려 있을 수 있습니다.
  5. 트랜잭션이 열려 있지만 활성 쓰기가 없는 동안 읽기 요청이 들어오면 가능한 경우 즉시 확정합니다. 활성 쓰기가 있으면 최신 확정 뷰를 읽습니다.

쓰기 직후 읽기(Read-after-write)는 일반적으로 보장하지 않습니다. 모든 쓰기나 삭제가 끝난 뒤 새 변경 요청을 보내기 전에 읽기 요청을 보내면 트랜잭션 확정을 유도할 수 있습니다. 업로드 중의 읽기 결과를 방금 쓴 전체 파일의 확정 상태로 해석하지 않습니다.

인증과 접근 권한

읽기에는 s3-proxy:datasets-read, 쓰기에는 s3-proxy:datasets-write 권한이 필요합니다. 기본 역할에서 각각 Viewer와 Editor에 해당합니다.

정적 인증 정보는 타사 애플리케이션의 서비스 사용자 권한을 사용합니다. 임시 인증 정보는 이를 발급받은 사용자의 토큰 범위와 권한을 사용합니다. 해당 주체에는 데이터셋의 프로젝트와 보안 표지(Marking)에 대한 접근도 필요합니다.

정적 인증 정보

장기간 사용하는 access key ID와 secret access key는 Foundry에 등록한 타사 애플리케이션의 서비스 사용자와 연결됩니다. 발급할 때 프로젝트 제한을 하나 이상 지정해야 하며, 제한 목록에 포함된 프로젝트의 데이터셋만 접근할 수 있습니다.

서비스 사용자에 연결된 장기 실행 작업에는 정적 인증 정보를 사용할 수 있습니다. S3 클라이언트에는 발급된 S3 키를 넣으며 타사 애플리케이션의 client ID와 client secret을 대신 넣지 않습니다.

임시 인증 정보

일반 사용자 토큰이나 OAuth 2.0으로 얻은 Foundry 토큰을 AssumeRoleWithWebIdentity STS API로 교환할 수 있습니다. 임시 인증 정보의 범위는 입력 토큰과 같습니다. DurationSeconds로 수명을 요청할 수 있지만 최대 1시간이며 원래 토큰의 남은 수명을 넘지 않습니다.

STS 엔드포인트는 https://<FOUNDRY_URL>/io/s3입니다. Foundry 서버에서 요구하는 요청 매개변수는 유효한 Foundry 토큰을 담은 WebIdentityToken입니다. 일부 SDK는 서버가 사용하지 않는 RoleArn, RoleSessionName도 입력하도록 요구합니다.

Python에서는 boto3 패키지를 사용합니다. FOUNDRY_BASE_URL 환경 변수에 https://를 포함한 Foundry 주소를, FOUNDRY_TOKEN_FILE에 사용자만 읽을 수 있는 토큰 파일 경로를 지정합니다. 파일에는 유효한 Foundry 토큰을 저장합니다.

import os
from pathlib import Path

import boto3

endpoint = os.environ["FOUNDRY_BASE_URL"].rstrip("/") + "/io/s3"
token = Path(os.environ["FOUNDRY_TOKEN_FILE"]).read_text().strip()
client = boto3.client("sts", endpoint_url=endpoint, region_name="foundry")
credentials = client.assume_role_with_web_identity(
    RoleArn="xxxxxxxxxxxxxxxxxxxx",
    RoleSessionName="foundry-s3",
    WebIdentityToken=token,
)["Credentials"]

credentials를 메모리에서 S3 클라이언트에 전달합니다. 이 객체에는 비밀 키가 있으므로 로그에 출력하지 않습니다.

직접 HTTP를 호출할 때에는 STS Query 프로토콜의 POST 폼 본문으로 토큰을 전달할 수 있습니다. 다음 Bash 명령은 토큰 값 대신 파일 경로를 curl에 전달하므로 토큰을 URL이나 명령 인자에 넣지 않습니다. 이 명령에 사용할 토큰 파일에는 줄바꿈을 포함하지 않습니다. S3_CREDENTIALS_XML에는 응답을 저장할 새 파일 경로를 지정합니다.

: "${FOUNDRY_BASE_URL:?Foundry 기본 URL이 필요합니다}"
: "${FOUNDRY_TOKEN_FILE:?토큰 파일 경로가 필요합니다}"
: "${S3_CREDENTIALS_XML:?응답 저장 경로가 필요합니다}"
umask 077
curl --fail-with-body --silent --show-error \
    --data-urlencode "Action=AssumeRoleWithWebIdentity" \
    --data-urlencode "Version=2011-06-15" \
    --data-urlencode "WebIdentityToken@${FOUNDRY_TOKEN_FILE}" \
    --output "${S3_CREDENTIALS_XML}" \
    "${FOUNDRY_BASE_URL%/}/io/s3"

응답 파일에도 비밀 키와 세션 토큰이 있으므로 접근을 제한합니다. HTTP 디버그 로깅이나 본문 로깅을 활성화하면 폼 본문이 노출될 수 있습니다.

XML 응답에는 AccessKeyId, SecretAccessKey, SessionToken, Expiration이 들어갑니다. 다음 값은 형식 예시입니다.

<?xml version='1.0' encoding='UTF-8'?>
<AssumeRoleWithWebIdentityResponse xmlns="https://sts.amazonaws.com/doc/2011-06-15/">
  <AssumeRoleWithWebIdentityResult>
    <Credentials>
      <AccessKeyId>PLTRLZZJE0...</AccessKeyId>
      <SecretAccessKey>2j3hKX4EDP...</SecretAccessKey>
      <SessionToken>eyJwbG50ci...</SessionToken>
      <Expiration>2023-08-30T10:55:08.841403951Z</Expiration>
    </Credentials>
  </AssumeRoleWithWebIdentityResult>
</AssumeRoleWithWebIdentityResponse>

임시 인증 정보를 쓰면 타사 애플리케이션 등록 절차는 필요하지 않습니다. 클라이언트에 access key ID, secret access key와 session token을 모두 전달합니다.

정적 인증 정보 발급 절차

애플리케이션 등록과 서비스 사용자 권한

애플리케이션 구성 관리는 Developer Console이 활성화되어 있으면 해당 도구를 사용합니다. Developer Console을 사용할 수 없는 환경의 Control Panel 절차는 다음과 같습니다.

  1. Control Panel의 Third party applications에서 New application을 선택합니다.
  2. Client type을 Confidential client로 설정합니다.
  3. Authorization grant types에서 Client credentials grant를 활성화합니다.
  4. Summary의 Register application을 선택합니다.
  5. 완료 화면에서 Client ID를 보관하고 Enable application use에서 Enable로 전환합니다.

등록하면 서비스 사용자가 자동 생성됩니다. Manage application의 Authorization grant types, Client credentials grant, Service user에서 이름과 ID를 확인합니다. 이 사용자를 프로젝트와 보안 표지에 직접 추가하거나, 필요한 권한을 가진 그룹에 추가합니다.

키 생성

키를 생성하는 사용자는 해당 조직에서 User experience administrator 역할이 필요합니다. 다음 요청은 인증 정보를 발급합니다. FOUNDRY_BASE_URL은 https://를 포함한 주소, FOUNDRY_CLIENT_ID는 타사 애플리케이션 ID, FOUNDRY_PROJECT_RID는 접근을 허용할 최상위 프로젝트 RID입니다.

Bash 예시에는 curl과 jq가 필요합니다. FOUNDRY_AUTH_HEADER_FILE에는 Authorization: Bearer 다음에 유효한 사용자 토큰을 담은 한 줄짜리 비공개 파일의 경로를 지정합니다. 토큰 값을 명령 인자에 직접 넣지 않습니다. S3_CREDENTIALS_JSON은 응답을 저장할 새 파일 경로입니다.

: "${FOUNDRY_BASE_URL:?Foundry 기본 URL이 필요합니다}"
: "${FOUNDRY_CLIENT_ID:?애플리케이션 ID가 필요합니다}"
: "${FOUNDRY_PROJECT_RID:?프로젝트 RID가 필요합니다}"
: "${FOUNDRY_AUTH_HEADER_FILE:?인증 헤더 파일이 필요합니다}"
: "${S3_CREDENTIALS_JSON:?응답 저장 경로가 필요합니다}"
umask 077
jq -n --arg clientId "$FOUNDRY_CLIENT_ID" --arg project "$FOUNDRY_PROJECT_RID" \
    '{clientId: $clientId, projectRestrictions: [$project]}' |
curl --fail-with-body --silent --show-error \
    --header "@${FOUNDRY_AUTH_HEADER_FILE}" \
    --header "Content-type: application/json" \
    --data-binary @- \
    --output "${S3_CREDENTIALS_JSON}" \
    "${FOUNDRY_BASE_URL%/}/io/s3/api/v2/credentials"

PowerShell에서는 FOUNDRY_TOKEN_FILE의 토큰을 읽어 헤더를 구성합니다. 응답은 $credentials에 보관하며 화면에 출력하지 않습니다.

$required = @("FOUNDRY_BASE_URL", "FOUNDRY_TOKEN_FILE", "FOUNDRY_CLIENT_ID", "FOUNDRY_PROJECT_RID")
foreach ($name in $required) {
    if (-not [Environment]::GetEnvironmentVariable($name)) { throw "$name is required" }
}
$token = [IO.File]::ReadAllText($env:FOUNDRY_TOKEN_FILE).Trim()
$headers = @{
    "Authorization" = "Bearer $token"
    "Content-type" = "application/json"
}
$body = @{
    "clientId" = $env:FOUNDRY_CLIENT_ID
    "projectRestrictions" = @($env:FOUNDRY_PROJECT_RID)
} | ConvertTo-Json
$uri = $env:FOUNDRY_BASE_URL.TrimEnd("/") + "/io/s3/api/v2/credentials"
$credentials = Invoke-RestMethod -Uri $uri -Method POST -Headers $headers -Body $body

projectRestrictions에는 여러 프로젝트를 넣을 수 있지만 최소 하나가 필요합니다. 반환한 키는 안전하게 보관합니다.

프로젝트 RID 확인

프로젝트와 일반 폴더는 모두 ri.compass.main.folder.{RID_VALUE} 형식을 사용합니다. 문자열 모양만으로 프로젝트인지 판별할 수 없습니다. 하위 폴더, 데이터셋 등 프로젝트가 아닌 RID를 넣으면 인증 정보 사용 시 권한 오류가 발생합니다.

프로젝트 RID를 확인하는 방법은 다음과 같습니다.

  • Files의 Projects 탭에서 프로젝트를 오른쪽 클릭하고 Copy RID를 선택합니다. 이 목록에는 프로젝트만 표시되지만 개인 폴더의 프로젝트는 나타나지 않으며 휴지통에 있는 프로젝트는 Copy RID를 사용할 수 없습니다.
  • 프로젝트 루트를 열고 파일 목록 위 Actions에서 Copy RID를 선택합니다. Actions가 보이지 않으면 목록 선택을 해제합니다.
  • 프로젝트 루트의 빈 영역을 선택해 항목 선택을 해제하고 Project details 패널의 Metadata에서 RID를 복사합니다. 패널이 어떤 리소스를 설명하는지도 확인합니다.

Actions와 상세 패널은 현재 연 폴더에 적용됩니다. 하위 폴더가 아닌 프로젝트 루트에서 확인해야 합니다. 프로젝트 제한은 접근할 데이터셋의 범위를 정하며 데이터셋 안의 파일 위치를 정하지 않습니다. 파일 위치는 버킷으로 선택한 데이터셋과 logs/ 같은 객체 키 접두사로 지정합니다.

다른 조직의 애플리케이션

기본 발급 요청은 인증한 사용자의 조직에 있는 애플리케이션을 대상으로 합니다. 애플리케이션이 다른 조직에 있으면 다음처럼 조직 ID를 지정합니다.

https://<FOUNDRY_URL>/io/s3/api/v2/credentials?organizationRid=<ORGANIZATION_ID>

키 목록과 철회

활성 상태이며 철회하지 않은 키를 조회하면 client ID와 프로젝트 제한도 확인할 수 있습니다.

: "${FOUNDRY_BASE_URL:?Foundry 기본 URL이 필요합니다}"
: "${FOUNDRY_AUTH_HEADER_FILE:?인증 헤더 파일이 필요합니다}"
curl --fail-with-body --silent --show-error \
    --header "@${FOUNDRY_AUTH_HEADER_FILE}" \
    "${FOUNDRY_BASE_URL%/}/io/s3/api/v2/credentials"

지정한 키를 철회하려면 다음 요청을 사용합니다. 해당 키를 사용하는 작업의 접근이 끊기므로 정확한 키를 확인해야 합니다.

: "${FOUNDRY_BASE_URL:?Foundry 기본 URL이 필요합니다}"
: "${FOUNDRY_AUTH_HEADER_FILE:?인증 헤더 파일이 필요합니다}"
: "${S3_ACCESS_KEY_ID:?철회할 S3 액세스 키 ID가 필요합니다}"
curl --fail-with-body --silent --show-error --request DELETE \
    --header "@${FOUNDRY_AUTH_HEADER_FILE}" \
    "${FOUNDRY_BASE_URL%/}/io/s3/api/v2/credentials/${S3_ACCESS_KEY_ID}"

공통 클라이언트 설정

설정값과 조건
Hostname / Endpointhttps://<FOUNDRY_URL>/io/s3
Regionfoundry. 서명 버전 4 검증에 사용합니다. 표준 AWS 리전만 허용하는 클라이언트에서는 us-east-1을 사용합니다.
Credentials발급한 access key ID와 secret access key. 임시 인증 정보이면 session token도 필요합니다.
Path-style accesstrue. 버킷을 호스트 이름이 아니라 URL 경로에 넣어야 합니다.
Bucket데이터셋 RID. 예: ri.foundry.main.dataset.<uuid>

지원 URL은 https://<FOUNDRY_URL>/io/s3/<dataset-rid>/<logical-filepath> 형식입니다. 버킷 이름을 서브도메인에 넣는 virtual-hosted-style은 지원하지 않습니다. Path-style을 지원하지 않는 클라이언트는 사용할 수 없습니다. 미리 서명한 URL(Presigned URL)은 putObject에 대해 지원합니다.

지원하는 S3 연산

AbortMultipartUpload
CompleteMultipartUpload
CopyObject
CreateMultipartUpload
DeleteObject
DeleteObjects
GetObject
HeadBucket
HeadObject
ListMultipartUploads
ListObjects
ListObjectsV2
ListParts
PutObject
UploadPart

클라이언트별 구성 예시

실행 가능한 Python 예시는 환경 변수로 입력을 받습니다. FOUNDRY_BASE_URL에는 https://를 포함한 주소, FOUNDRY_DATASET_RID에는 대상 데이터셋 RID를 지정합니다. AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY에는 발급한 S3 키를 넣고 임시 인증 정보이면 AWS_SESSION_TOKEN도 지정합니다. 키를 코드나 로그에 직접 기록하지 않습니다. 설정 파일과 UI 표의 자리표시자는 실제 환경 값으로 바꿉니다.

AWS CLI

aws configure --profile foundry에서 S3 키와 리전 foundry를 입력합니다. ~/.aws/config에 다음처럼 endpoint와 path-style을 함께 지정할 수 있습니다.

[profile foundry]
region = foundry
endpoint_url = https://<FOUNDRY_URL>/io/s3
s3 =
    addressing_style = path
    signature_version = s3v4

endpoint를 프로필에 넣지 않았다면 명령마다 --endpoint-url을 지정합니다.

: "${FOUNDRY_BASE_URL:?Foundry 기본 URL이 필요합니다}"
: "${FOUNDRY_DATASET_RID:?데이터셋 RID가 필요합니다}"
aws --profile foundry --endpoint-url "${FOUNDRY_BASE_URL%/}/io/s3" \
    s3 ls "s3://${FOUNDRY_DATASET_RID}"

임시 키를 직접 설정하는 경우 AWS_SESSION_TOKEN도 지정합니다. Foundry 토큰을 STS로 자동 교환하도록 구성하려면 다음 프로필을 사용할 수 있습니다.

[profile foundry]
region = foundry
endpoint_url = https://<FOUNDRY_URL>/io/s3
web_identity_token_file = ~/.foundry/web-identity.token
role_arn = xxxxxxxxxxxxxxxxxxxx
s3 =
    addressing_style = path
    signature_version = s3v4

이 방식에서는 환경 변수의 AWS 키나 ~/.aws/credentials에 별도 키를 넣을 필요가 없습니다. ~/.foundry/web-identity.token에는 유효한 Foundry 토큰을 저장하므로 파일을 안전하게 보호해야 합니다. role_arn은 서버에서 사용하지 않지만 AWS CLI 검증 때문에 최소 20자로 제공해야 합니다. STS에도 같은 endpoint가 적용되도록 --endpoint-url만 쓰지 말고 프로필에 endpoint_url을 설정합니다.

Python Boto3

boto3와 pandas를 설치한 환경에서 실행합니다. FOUNDRY_OBJECT_KEY에는 읽을 CSV 파일의 논리 경로를 지정합니다.

import os

import boto3
import pandas as pd
from botocore.config import Config

s3 = boto3.client(
    "s3",
    aws_access_key_id=os.environ["AWS_ACCESS_KEY_ID"],
    aws_secret_access_key=os.environ["AWS_SECRET_ACCESS_KEY"],
    aws_session_token=os.environ.get("AWS_SESSION_TOKEN"),
    endpoint_url=os.environ["FOUNDRY_BASE_URL"].rstrip("/") + "/io/s3",
    region_name="foundry",
    config=Config(signature_version="s3v4", s3={"addressing_style": "path"}),
)

obj = s3.get_object(
    Bucket=os.environ["FOUNDRY_DATASET_RID"],
    Key=os.environ["FOUNDRY_OBJECT_KEY"],
)
df = pd.read_csv(obj["Body"])
print(df)

임시 인증 정보이면 환경 변수의 session token을 함께 전달합니다. Config에서 path-style을 명시합니다.

Spark와 Hadoop S3A

S3A를 사용할 수 있도록 Hadoop AWS 의존성이 구성된 Spark 환경이 필요합니다.

import os

from pyspark.sql import SparkSession

hostname = os.environ["FOUNDRY_BASE_URL"].rstrip("/") + "/io/s3"
dataset_rid = os.environ["FOUNDRY_DATASET_RID"].replace(".", "-")
builder = (
    SparkSession.builder
        .config("fs.s3a.access.key", os.environ["AWS_ACCESS_KEY_ID"])
        .config("fs.s3a.secret.key", os.environ["AWS_SECRET_ACCESS_KEY"])
        .config("fs.s3a.endpoint", hostname)
        .config("fs.s3a.endpoint.region", "foundry")
        .config("fs.s3a.path.style.access", "true")
)
if os.environ.get("AWS_SESSION_TOKEN"):
    builder = builder.config("fs.s3a.session.token", os.environ["AWS_SESSION_TOKEN"])
spark_session = builder.getOrCreate()
df = spark_session.read.parquet(f"s3a://{dataset_rid}/*")
df.show()

Hadoop AWS 클라이언트는 .이 포함된 버킷 이름을 호스트 이름으로 해석하지 못해 bucket is null/empty 같은 오류가 발생할 수 있습니다. 이 경우 RID의 .을 -로 바꾸는 우회 방법을 사용합니다. 임시 인증 정보에서는 session token 설정을 활성화합니다.

Cyberduck

  1. Foundry S3 연결 프로필을 열어 Cyberduck에 등록합니다.
  2. 자동 생성한 북마크에서 Server를 https://<FOUNDRY_URL>로 설정합니다.
  3. Access Key ID와 Secret Access Key를 입력합니다.
  4. More Options의 Path에 ri.foundry.main.dataset.<uuid>를 넣습니다.
  5. 북마크 설정을 닫고 북마크를 열어 연결합니다.

Google Storage Transfer Service

Foundry 데이터셋을 Cloud Storage 버킷으로 전송하려면 먼저 Google Cloud에서 에이전트 풀과 전송 에이전트를 구성합니다. 전송 작업에서 Source type을 S3-compatible object storage로 선택하고 해당 풀을 지정합니다.

항목값
Bucketri.foundry.main.dataset.<uuid>
Endpointhttps://<FOUNDRY_URL>/io/s3
Signing regionfoundry
Signing processSignature Version 4(SigV4)
Addressing-stylePath-style requests
Network protocolHTTPS
Listing API versionListObjectsV2

이어서 대상 Cloud Storage 버킷, 스케줄과 전송 설정을 지정합니다.

Apache NiFi

PutS3Object 프로세서로 파일을 쓰는 예시 설정입니다.

항목값
Object Key데이터셋 내부 경로. 예: path/to/file.csv
Bucketri.foundry.main.dataset.<uuid>
Access Key ID<ACCESS_KEY_ID>
Secret Access Key<SECRET_ACCESS_KEY>
RegionUS East (N. Virginia), 즉 us-east-1
Use Path Style Accesstrue
Endpoint Override URLhttps://<FOUNDRY_URL>/io/s3

Airbyte

S3 destination으로 Foundry 데이터셋에 파일을 쓸 수 있습니다.

항목값
Destination typeS3
S3 Key ID<ACCESS_KEY_ID>
S3 Access Key<SECRET_ACCESS_KEY>
S3 Bucket Nameri.foundry.main.dataset.<uuid>
S3 Bucket Path파일을 쓸 데이터셋 내부의 유효한 하위 경로
S3 Bucket Regionus-east-1
Output FormatAirbyte가 제공하는 모든 출력 형식을 Foundry 데이터셋에 저장할 수 있습니다.
Endpointhttps://<FOUNDRY_URL>/io/s3

DuckDB

duckdb Python 패키지를 사용하고 httpfs 확장을 설치할 수 있는 환경에서 실행합니다. 환경 변수로 받은 값을 SQL 문자열 리터럴로 변환하며, 비밀 키를 포함한 SQL은 로그에 출력하지 않습니다.

import os
from urllib.parse import urlsplit

import duckdb

def sql_literal(value: str) -> str:
    return "'" + value.replace("'", "''") + "'"

base = urlsplit(os.environ["FOUNDRY_BASE_URL"])
endpoint = base.netloc + base.path.rstrip("/") + "/io/s3"
options = [
    "TYPE S3",
    "KEY_ID " + sql_literal(os.environ["AWS_ACCESS_KEY_ID"]),
    "SECRET " + sql_literal(os.environ["AWS_SECRET_ACCESS_KEY"]),
    "REGION 'foundry'",
    "ENDPOINT " + sql_literal(endpoint),
    "URL_STYLE 'path'",
    "USE_SSL true",
]
if os.environ.get("AWS_SESSION_TOKEN"):
    options.append("SESSION_TOKEN " + sql_literal(os.environ["AWS_SESSION_TOKEN"]))

connection = duckdb.connect()
connection.execute("INSTALL httpfs")
connection.execute("LOAD httpfs")
connection.execute("CREATE SECRET foundry_secret (" + ", ".join(options) + ")")
path = "s3://" + os.environ["FOUNDRY_DATASET_RID"] + "/spark/*.parquet"
connection.execute("CREATE TABLE new_tbl AS SELECT * FROM " + sql_literal(path))

DuckDB의 ENDPOINT에는 https://를 넣지 않습니다. 예시에서는 주소를 분해해 스킴을 제거하고 USE_SSL true로 HTTPS를 사용합니다. CREATE TABLE은 질의 결과를 DuckDB 테이블로 만듭니다.

Polars

polars 패키지를 설치한 환경에서 실행합니다.

import os

import polars as pl

dataset_rid = os.environ["FOUNDRY_DATASET_RID"]
storage_options = {
    "aws_access_key_id": os.environ["AWS_ACCESS_KEY_ID"],
    "aws_secret_access_key": os.environ["AWS_SECRET_ACCESS_KEY"],
    "aws_region": "foundry",
    "endpoint_url": os.environ["FOUNDRY_BASE_URL"].rstrip("/") + "/io/s3",
}
if os.environ.get("AWS_SESSION_TOKEN"):
    storage_options["aws_session_token"] = os.environ["AWS_SESSION_TOKEN"]

df = pl.scan_parquet(
    f"s3://{dataset_rid}/spark/*.parquet",
    storage_options=storage_options,
)

scan_parquet는 지연 실행 프레임을 구성합니다. 실제 데이터를 읽는 시점과 후속 처리는 사용 중인 Polars 작업에 맞게 정합니다.