Amazon S3에 액세스하도록 Snowflake 설정하기

ohyujeong·2023년 7월 2일
0

설정을 하기에 앞서 Snowflake의 Integration에 대해 알아보자.

Integration이란?

일반적으로 Snowflake에서 데이터를 가져오거나 클라우드 서비스(예: S3)와 통합하기 위해 인증 정보를 사용해야 할 때, 해당 클라우드 공급자의 비밀 키 또는 액세스 토큰과 같은 자격 증명을 설정해야 한다. 그러나 Integration 을 사용하면 명시적인 자격 증명을 전달하지 않고도 클라우드 공급자와의 통합을 수행할 수 있다.

Integration 개체는 Snowflake에서 관리되는 개체로, 클라우드 공급자와의 연결 및 자격 증명 정보를 중앙에서 관리한다. 이를 통해 Snowflake 사용자는 개별적으로 자격 증명을 설정하거나 관리하지 않고도 해당 클라우드 공급자와의 통합을 간편하게 수행할 수 있다.

즉, Integration 을 사용하면 개발자는 자격 증명 정보를 코드에 하드코딩하지 않고도 클라우드 서비스와의 통합을 수행할 수 있다. 이는 보안 및 개발자의 편의성 측면에서 이점을 제공한다.

Integration의 흐름

  1. Snowflake 외부 Stage는 Stage를 생성할 때 정의한 Integration 개체를 참조한다.
CREATE or replace STAGE raw_data_stage_csv
  STORAGE_INTEGRATION = s3_int <-- Integration 정의
  URL = 's3://kpop-analysis/raw_data/' 
  FILE_FORMAT = csvformat;
  1. Snowflake는 계정에 대해 생성된 S3 IAM User와 Intergration을 자동으로 연결한다. Snowflake는 Snowflake 계정의 모든 S3 Integration에서 참조하는 단일 IAM 사용자를 생성한다.

  2. 조직의 AWS 관리자는 Stage 정의에서 참조된 버킷에 액세스할 수 있는 권한을 IAM 사용자에게 부여합니다. 생성하는 Stage 객체는 버킷을 참조하는 정의에 따라 서로 다른 버킷과 경로를 참조할 수 있으며 인증을 위해 동일한 Integration을 사용할 수 있다.

CREATE or replace STAGE raw_data_stage_csv
  STORAGE_INTEGRATION = s3_int 
  URL = 's3://kpop-analysis/raw_data/' <-- 참조된 S3 Bucket 경로
  FILE_FORMAT = csvformat;

1. S3 버킷에 대한 액세스 권한 구성

1-1. IAM Policy 구성

Snowflake가 폴더(및 하위 폴더)의 파일에 액세스하려면 S3 버킷 및 폴더에 대한 다음 권한이 필요하다.

  • s3:GetBucketLocation
  • s3:GetObject
  • s3:GetObjectVersion
  • s3:ListBucket
  • s3:PutObject (additional)
  • s3:DeleteObject (additional)

위의 권한을 가진 IAM Policy를 아래와 같이 생성한다.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
              "s3:PutObject",
              "s3:GetObject",
              "s3:GetObjectVersion",
              "s3:DeleteObject",
              "s3:DeleteObjectVersion"
            ],
            "Resource": "arn:aws:s3:::<bucket>/<prefix>/*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "s3:ListBucket",
                "s3:GetBucketLocation"
            ],
            "Resource": "arn:aws:s3:::<bucket>",
            "Condition": {
                "StringLike": {
                    "s3:prefix": [
                        "<prefix>/*"
                    ]
                }
            }
        }
    ]
}

<bucket> : 권한을 가질 S3 bucket
<prefix> : 권한을 가질 S3 오브젝트의 경로
* : 와일드 카드로 모든 것을 의미한다. (예: <prefix>/*<prefix> 아래 모든 오브젝트)

주의할 점

Snowflake의 AWS Region과 같은 Region에 있는 S3 bucket이어야 한다.

1-2. IAM Role 생성

외부 ID는 나중에 Integration이 생성되고 나면 변경할 것이기 때문에 임시로 0000 을 입력해놓고 다음 단계로 넘어간다.

전에 생성한 Policy를 적용하여 Role 생성을 완료한다.
이것으로 S3 버킷에 대한 IAM Policy를 만들고 그 Policy를 Role에 부여했다.

1-3. Role ARN 가져오기


Integration 생성 시 참조할 IAM Role의 ARN(AWS Resource Name) 을 복사한다.


2. Snowflake Integration설정

2-1. Snowflake Integration 생성

아래 쿼리 형식을 사용하여 Integration을 생성한다.

CREATE STORAGE INTEGRATION <integration_name>
  TYPE = EXTERNAL_STAGE
  STORAGE_PROVIDER = 'S3'
  ENABLED = TRUE
  STORAGE_AWS_ROLE_ARN = '<iam_role>'
  STORAGE_ALLOWED_LOCATIONS = ('s3://<bucket>/<path>/', 's3://<bucket>/<path>/')
  [ STORAGE_BLOCKED_LOCATIONS = ('s3://<bucket>/<path>/', 's3://<bucket>/<path>/') ]

<integration_name> : 생성할 Integration의 이름
<iam_role> : 1-3 단계에서 복사한 IAM Role의 ARN
<bucket> : 접근할 S3 버킷명
<path> : 접근할 S3 경로. 이 경로 이하의 모든 오브젝트에 권한을 가진다.

2-2. Snowflake 계정의 AWS IAM 사용자 검색

DESC INTEGRATION <integration_name>;

다음 단계에서 사용하기 위해 표시된 값들을 저장해놓는다.

STORAGE_AWS_IAM_USER_ARN : Snowflake 계정에 대해 생성된 AWS IAM 사용자.
STORAGE_AWS_EXTERNAL_ID : 신뢰 관계를 설정하는 데 필요한 외부 ID.

2-3. Snowflake AWS IAM 사용자에게 버킷 객체 액세스 권한 부여

생성한 IAM Role의 상세 페이지의 신뢰정책 탭에서 아래와 같이 신뢰정책을 수정한다.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "AWS": "<snowflake_user_arn>"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringEquals": {
          "sts:ExternalId": "<snowflake_external_id>"
        }
      }
    }
  ]
}

<snowflake_user_arn> : 2-2 단계의 STORAGE_AWS_IAM_USER_ARN
<snowflake_external_id> : 2-2 단계의 STORAGE_AWS_EXTERNAL_ID

여기서 주의해야할 점!

STORAGE_AWS_EXTERNAL_ID 의 값은 생성한 Integration을 재생성하게 되면 값이 변경되니 만약 Integration을 재생성했다면 바뀐 값으로 다시 IAM Role의 신뢰정책에 업데이트 해줘야 한다.


3. 외부 Stage 생성하기

Stage란?

파일(일반적으로 대량의 데이터)을 Snowflake 데이터 웨어하우스로 로드하기 전에 임시로 저장하는 영역을 말한다.

아래와 같이 3가지 유형의 Stage가 있다.

사용자 Stage

각 Snowflake 사용자에 대해 자동으로 생성된다.

테이블 Stage

데이터를 로드하는 대상 테이블과 연관된 Stage로, 테이블을 생성하면 자동으로 이와 연관된 테이블 Stage가 생성된다.

외부 Stage

Snowflake 이외의 클라우드 스토리지 서비스(Amazon S3, Google Cloud Storage, Microsoft Azure 등)에서 데이터 파일을 참조하는 Stage이다. 다른 Stage와 다르게 GET, PUT 명령어를 사용할 수 없다.

외부 Stage 사용

외부 스테이지를 사용하면 대규모 데이터 세트를 빠르게 로드하고 클라우드 스토리지에 직접 액세스할 수 있어 데이터 로드 및 처리의 속도와 효율성을 높일 수 있다.
COPY INTO <table> 명령을 사용하여 외부 Stage(예: S3 bucket)에 있는 파일로부터 데이터를 로드할 수 있다.

2단계에서 생성한 Integration을 참조하는 외부 Stage를 아래 쿼리로 생성한다.

USE SCHEMA mydb.public;

CREATE STAGE my_s3_stage
  STORAGE_INTEGRATION = <integration_name>
  URL = 's3://<bucket_name>/<path>/'
  --FILE_FORMAT = my_csv_format; 선택적으로 추가

이렇게 s3://<bucket_name>/<path>/ 경로의 S3 오브젝트에 액세스 할 수 있는 Stage의 생성을 완료했다.


4. Integration을 참조한 외부 Stage를 사용하여 데이터 적재

만약 raw_data_stage라는 외부 Stage를 생성했고, <path> 에 있는 모든 파일의 데이터를 적재할 때
아래와 같이 쿼리할 수 있다. 단, 파일들의 컬럼 정보가 모두 같아야 한다.

정형 데이터 형식인 CSV 파일의 경우:

COPY INTO <schema_name>.<table_name> 
from '@raw_data_stage/<path>/'
--ON_ERROR ='continue' 옵션

# 컬럼 값 변형이 필요할 때
COPY INTO <schema_name>.<table_name> 
FROM (
    SELECT $1, $2, UPPER($3)
    FROM '@raw_data_stage/<path>/'
);

물론 아래와 같이 개별 파일을 적재할 수도 있다.

COPY INTO <schema_name>.<table_name> 
from @raw_data_stage/<path>/<object_name>.csv

반정형 데이터 형식인 json 파일의 경우:

COPY INTO <schema_name>.<table_name> 
FROM (
    SELECT
        $1:column_name1,
        $1:column_name2,
        $1:column_name3,
        $1:column_name4,
        $1:column_name5,
    FROM '@raw_data_stage/<path>/'
    )
FILE_FORMAT = (TYPE = JSON);

CSV를 가져올 때와 달리 $1 에 컬럼명을 명시하여 가져온다.

참고 링크

https://docs.snowflake.com/en/user-guide/data-load-s3-config-storage-integration
https://community.snowflake.com/s/article/S3-Storage-Integration-Error-assuming-AWS-ROLE

profile
거친 돌이 다듬어져 조각이 되듯

0개의 댓글