Hadoop-AWS 모듈: Amazon Web Services 통합

Hadoop-AWS 모듈: Amazon Web Services 통합

Apache Hadoop의 hadoop-aws 모듈은 AWS 통합을 지원해요. Apache Hadoop의 기본 클래스패스에 S3A 클라이언트를 포함하려면 hadoop-env.sh의 HADOOP_OPTIONAL_TOOLS 목록에 hadoop-aws가 있는지 확인하세요. 클라이언트 측 상호작용은 ~/.hadooprc 파일에서 관련 JAR을 로드하도록 선언할 수 있어요:

hadoop_add_to_classpath_tools hadoop-aws

이 파일의 설정은 배포된 애플리케이션에 전파되지 않지만 hadoop fs 명령 같은 로컬 클라이언트에는 작동해요.

출처: 문서

본문

호환성 (Compatibility)

디렉터리 마커 호환성 (Directory Marker Compatibility)

이 릴리스는 아래에 파일이나 디렉터리를 만들 때 디렉터리 마커를 결코 삭제하지 않아요. 이것은 2021년 이전에 릴리스된 Hadoop S3A 클라이언트 버전과 호환되지 않아요. 자세한 내용은 S3A and Directory Markers를 참고하세요.

문서 (Documents)

  • Connecting
  • Encryption
  • Performance
  • The upgrade to AWS Java SDK V2
  • Working with Third-party S3 Stores
  • Troubleshooting
  • Prefetching
  • S3A and Directory Markers
  • Auditing
  • Committing work to S3 with the "S3A Committers"
  • S3A Committers Architecture
  • Working with IAM Assumed Roles
  • S3A Delegation Token Support
  • S3A Delegation Token Architecture
  • Auditing Architecture
  • Testing
  • S3Guard

개요 (Overview)

Apache Hadoop의 hadoop-aws 모듈은 AWS 통합을 지원해요.

Hadoop S3A 클라이언트 소개 (Introducing the Hadoop S3A client.)

Hadoop의 "S3A" 클라이언트는 Amazon S3 객체 스토어와 호환 구현에 대해 고성능 IO를 제공해요.

  • S3 객체를 직접 읽고 씀.
  • 표준 S3 클라이언트와 호환.
  • Amazon EMR의 s3:// 클라이언트(EMRFS)로 만든 파일과 호환.
  • 수 GB 객체에 대한 파티셔닝된 업로드 지원.
  • Apache ORC와 Apache Parquet 같은 컬럼형 데이터 작업을 위한 고성능 랜덤 IO 모드 제공.
  • 최신 S3 기능과 인증 방식을 지원하는 Amazon의 Java V2 SDK 사용.
  • 환경 변수, Hadoop 구성 속성, Hadoop 키 관리 스토어, IAM 역할을 통한 인증 지원.
  • 버킷별(bucket) 구성 지원.
  • 읽기·쓰기 모두에 대한 S3 "Server Side Encryption" 지원: SSE-S3, SSE-KMS, SSE-C.
  • S3-CSE 클라이언트 측 암호화 지원.
  • Hadoop 메트릭으로 계측됨.
  • 오픈소스 커뮤니티가 활발히 유지보수.

다른 S3 커넥터 (Other S3 Connectors)

S3에 대한 다른 Hadoop 커넥터가 있어요. S3A만 Hadoop 프로젝트 자체가 활발히 유지보수해요. Amazon EMR의 s3:// 클라이언트는 Amazon EMR 팀에서 유지보수하며 활발히 유지돼요.

시작하기 (Getting Started)

S3A는 hadoop-common과 그 의존성들과 함께 두 개의 JAR에 의존해요:

  • hadoop-aws JAR. S3A 커넥터를 포함.
  • bundle JAR. 전체 shaded AWS V2 SDK를 포함.

hadoop-common과 hadoop-aws의 버전은 동일해야 해요. Maven 빌드에 라이브러리를 가져오려면 빌드 의존성에 hadoop-aws JAR를 추가해요. 그러면 호환되는 aws-sdk JAR를 끌어올 거예요. hadoop-aws JAR는 그 고유 의존성(AWS SDK JAR) 외에 다른 의존성을 선언하지 않아요. 이것은 다운스트림 애플리케이션에서 Hadoop 의존성 JAR을 제외/튜닝하는 것을 단순화해요. hadoop-client 또는 hadoop-common 의존성은 선언해야 해요.

<properties>
 <!-- Your exact Hadoop version here-->
  <hadoop.version>3.0.0</hadoop.version>
</properties>

<dependencies>
  <dependency>
    <groupId>org.apache.hadoop</groupId>
    <artifactId>hadoop-client</artifactId>
    <version>${hadoop.version}</version>
  </dependency>
  <dependency>
    <groupId>org.apache.hadoop</groupId>
    <artifactId>hadoop-aws</artifactId>
    <version>${hadoop.version}</version>
  </dependency>
</dependencies>

경고 (Warnings)

Amazon S3는 "객체 스토어"의 한 예예요. 확장성과 특히 고가용성을 달성하기 위해 S3는 —많은 다른 클라우드 객체 스토어가 그랬듯— 클래식 "POSIX" 파일시스템이 약속하는 제약 중 일부를 완화했어요. 이 주제에 대한 추가 논의는 The Hadoop FileSystem API Definition을 참고하세요.

경고 1: 디렉터리는 모방된다 (Warning #1: Directories are mimicked)

S3A 클라이언트는 다음으로 디렉터리를 모방해요:

  • mkdirs 호출 후 스텁(stub) 항목을 만들고, 아래 어딘가에 파일이 추가될 때 그것을 삭제.
  • 디렉터리를 나열할 때 경로가 디렉터리 경로로 시작하는 모든 객체를 검색해 나열로 반환.
  • 디렉터리를 이름을 바꿀 때 그런 나열을 가져와 S3가 개별 객체를 대상 파일 이름의 새 객체로 복사하도록 요청.
  • 디렉터리를 삭제할 때 그런 나열을 가져와 배치로 항목을 삭제.
  • 디렉터리를 이름을 바꾸거나 삭제할 때 그런 나열을 가져와 개별 파일에 대해 작업.

몇 가지 결과:

  • 디렉터리는 수정 시간이 없을 수 있어요. 이것에 의존하는 Hadoop의 일부는 예기치 않은 동작을 할 수 있어요. 예: YARN의 AggregatedLogDeletionService는 적절한 로그 파일을 제거하지 않을 거예요.
  • 디렉터리 나열은 느릴 수 있어요. 가능할 때마다 고성능 재귀 나열에는 listFiles(path, recursive)를 사용해요.
  • 호출자가 충분히 노력하면 파일 아래에 파일을 만드는 것이 가능해요.
  • 디렉터리 이름을 바꾸는 시간은 아래(직간접적으로) 파일 수와 파일 크기에 비례해요. (복사는 S3 스토리지 내부에서 실행되므로 시간은 클라이언트에서 S3까지의 대역폭과 무관해요.)
  • 디렉터리 이름 변경은 원자적이지 않아요: 중간에 실패할 수 있으며 호출자는 커밋 알고리즘의 일부로 원자적 이름 변경에 안전하게 의존할 수 없어요.
  • 디렉터리 삭제는 원자적이지 않으며 중간에 실패할 수 있어요.

마지막 세 가지 이슈는 S3를 HDFS나 다른 "실제" 파일시스템이 아닌 작업의 즉시 대상으로 사용할 때 표면화돼요. S3A 커미터는 S3에 대한 작업을 커밋하는 메커니즘을 제공하며, Hadoop 리소스에 대해 객체 스토어에서의 원자적 커밋에 대한 전체 이야기는 Committer Architecture 문서를 참고하세요.

Amazon S3 또는 타사 스토어에 연결하기 (Connecting to Amazon S3 or a third-party store)

Connecting to an Amazon S3 Bucket through the S3A Connector를 참고하세요. 또한 S3 endpoint와 region 설정을 자세히 확인하세요.

S3로 인증하기 (Authenticating with S3)

Authenticating with S3를 참고하세요.

Hadoop 자격 증명 제공자로 비밀 저장하기 (Storing secrets with Hadoop Credential Providers)

Hadoop Credential Provider Framework는 보안 "Credential Providers"가 비밀을 Hadoop 구성 파일 밖에, 로컬 또는 Hadoop 파일시스템의 암호화된 파일에 저장하고 요청에 포함할 수 있게 해줘요. 민감한 데이터가 있는 S3A 구성 옵션(fs.s3a.secret.key, fs.s3a.access.key, fs.s3a.session.token, fs.s3a.encryption.key)은 S3A 파일시스템 URL이 데이터 접근에 사용될 때 값이 읽혀지는 저장된 바이너리 파일에 그 데이터를 저장할 수 있어요. 이 자격 증명 제공자에 대한 참조는 Hadoop 구성에서 선언돼요. 자세한 읽기는 Credential Provider API를 참고하세요.

다음 구성 옵션을 Hadoop Credential Provider 스토어에 저장할 수 있어요:

  • fs.s3a.access.key
  • fs.s3a.secret.key
  • fs.s3a.session.token
  • fs.s3a.encryption.key
  • fs.s3a.encryption.algorithm

처음 세 개는 인증용이고, 마지막 두 개는 암호화용이에요. 후자 중 암호화 키만 "민감"으로 간주될 수 있어요. 하지만 알고리즘을 자격 증명에 포함할 수 있으면 JCECKS 파일이 S3에 기록되는 새 데이터를 암호화하는 데 필요한 모든 옵션을 포함할 수 있게 해줘요.

1단계: 자격 증명 파일 만들기 (Step 1: Create a credential file)

자격 증명 파일은 어떤 Hadoop 파일시스템에도 만들 수 있어요. HDFS나 Unix 파일시스템에 만들 때 권한은 읽는 사람에게 파일을 비공개로 유지하도록 자동 설정돼요 — 디렉터리 권한은 건드리지 않으므로, 사용자는 파일을 포함하는 디렉터리가 현재 사용자만 읽을 수 있는지 확인해야 해요.

hadoop credential create fs.s3a.access.key -value 123 \
    -provider jceks://[email protected]:9001/user/backup/s3.jceks

hadoop credential create fs.s3a.secret.key -value 456 \
    -provider jceks://[email protected]:9001/user/backup/s3.jceks

자격 증명 파일은 안에 어떤 항목이 보관되는지 보려면 나열할 수 있어요:

hadoop credential list -provider jceks://[email protected]:9001/user/backup/s3.jceks

Listing aliases for CredentialProvider: jceks://[email protected]:9001/user/backup/s3.jceks
fs.s3a.secret.key
fs.s3a.access.key

이 시점에서 자격 증명은 사용할 준비가 됐어요.

2단계: hadoop.security.credential.provider.path 속성 구성 (Step 2: Configure the hadoop.security.credential.provider.path property)

제공자에 대한 URL은 hadoop.security.credential.provider.path 구성 속성에 명령줄 또는 XML 구성 파일로 설정해야 해요.

<property>
  <name>hadoop.security.credential.provider.path</name>
  <value>jceks://[email protected]:9001/user/backup/s3.jceks</value>
  <description>Path to interrogate for protected credentials.</description>
</property>

이 속성은 비밀 파일의 경로만 제공하므로 구성 옵션 자체는 더 이상 민감 항목이 아니에요. hadoop.security.credential.provider.path 속성은 모든 파일시스템과 비밀에 전역이에요. S3A 파일시스템용 자격 증명 제공자만 나열하는 fs.s3a.security.credential.provider.path라는 또 다른 속성이 있어요. 두 속성은 하나로 결합되며, fs.s3a. 속성의 제공자 목록이 hadoop.security 목록보다 우선해요 (즉 공통 목록에 앞선(prepended)돼요).

<property>
  <name>fs.s3a.security.credential.provider.path</name>
  <value />
  <description>
    Optional comma separated list of credential providers, a list
    which is prepended to that set in hadoop.security.credential.provider.path
  </description>
</property>

이것은 자격 증명 목록에 대체 비밀을 추가하지 않고 버킷별로 다른 자격 증명 제공자를 바인딩하는 것을 지원하기 위해 추가됐어요. 하지만 일부 애플리케이션(예: Hive)은 사용자가 자격 증명 제공자 목록을 동적으로 업데이트하는 것을 막아요. 버킷별 비밀이 이제 지원되므로 JCEKS 파일 및 기타 자격 증명 소스에 버킷별 키를 포함하는 것이 더 좋아요.

자격 증명 제공자의 비밀 사용 (Using secrets from credential providers)

제공자가 Hadoop 구성에 설정되면 Hadoop 명령은 비밀이 XML 파일에 있는 것처럼 정확히 작동해요.

hadoop distcp \
    hdfs://nn1.example.com:9001/user/backup/007020615 s3a://glacier1/

hadoop fs -ls s3a://glacier1/

제공자에 대한 경로는 명령줄에도 설정할 수 있어요:

hadoop distcp \
    -D hadoop.security.credential.provider.path=jceks://[email protected]:9001/user/backup/s3.jceks \
    hdfs://nn1.example.com:9001/user/backup/007020615 s3a://glacier1/

hadoop fs \
  -D fs.s3a.security.credential.provider.path=jceks://[email protected]:9001/user/backup/s3.jceks \
  -ls s3a://glacier1/

제공자 경로 자체는 민감한 비밀이 아니므로 명령줄에 선언해도 위험이 없어요.

일반 S3A 클라이언트 구성 (General S3A Client configuration)

모든 S3A 클라이언트 옵션은 fs.s3a. 접두사를 가진 옵션으로 구성돼요. 클라이언트는 서로 다른 버킷이 공유 설정을 재정의할 수 있게 하는 버킷별 구성을 지원해요. 이것은 버킷의 엔드포인트, 암호화, 인증 메커니즘을 바꾸는 데 흔히 사용돼요.

다음은 프로덕션에 사용할 S3A 속성 중 일부예요. Performance는 스레드·네트워크 풀 옵션을 포함한 성능 관련 설정을, Testing은 테스트 관련 옵션을 다뤄요.

<property>
  <name>fs.s3a.aws.credentials.provider</name>
  <value>
    org.apache.hadoop.fs.s3a.TemporaryAWSCredentialsProvider,
    org.apache.hadoop.fs.s3a.SimpleAWSCredentialsProvider,
    software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider,
    org.apache.hadoop.fs.s3a.auth.IAMInstanceCredentialsProvider
  </value>
  <description>
    Comma-separated class names of credential provider classes which implement
    software.amazon.awssdk.auth.credentials.AwsCredentialsProvider.

    When S3A delegation tokens are not enabled, this list will be used
    to directly authenticate with S3 and other AWS services.
    When S3A Delegation tokens are enabled, depending upon the delegation
    token binding it may be used
    to communicate wih the STS endpoint to request session/role
    credentials.
  </description>
</property>

<property>
  <name>fs.s3a.security.credential.provider.path</name>
  <value />
  <description>
    Optional comma separated list of credential providers, a list
    which is prepended to that set in hadoop.security.credential.provider.path
  </description>
</property>

<property>
  <name>fs.s3a.assumed.role.arn</name>
  <value />
  <description>
    AWS ARN for the role to be assumed.
    Required if the fs.s3a.aws.credentials.provider contains
    org.apache.hadoop.fs.s3a.AssumedRoleCredentialProvider
  </description>
</property>

<property>
  <name>fs.s3a.assumed.role.session.name</name>
  <value />
  <description>
    Session name for the assumed role, must be valid characters according to
    the AWS APIs.
    Only used if AssumedRoleCredentialProvider is the AWS credential provider.
    If not set, one is generated from the current Hadoop/Kerberos username.
  </description>
</property>

<property>
  <name>fs.s3a.assumed.role.policy</name>
  <value/>
  <description>
    JSON policy to apply to the role.
    Only used if AssumedRoleCredentialProvider is the AWS credential provider.
  </description>
</property>

<property>
  <name>fs.s3a.assumed.role.session.duration</name>
  <value>30m</value>
  <description>
    Duration of assumed roles before a refresh is attempted.
    Used when session tokens are requested.
    Range: 15m to 1h
  </description>
</property>

<property>
  <name>fs.s3a.assumed.role.sts.endpoint</name>
  <value/>
  <description>
    AWS Security Token Service Endpoint.
    If unset, uses the default endpoint.
    Only used if AssumedRoleCredentialProvider is the AWS credential provider.
    Used by the AssumedRoleCredentialProvider and in Session and Role delegation
    tokens.
  </description>
</property>

<property>
  <name>fs.s3a.assumed.role.sts.endpoint.region</name>
  <value></value>
  <description>
    AWS Security Token Service Endpoint's region;
    Needed if fs.s3a.assumed.role.sts.endpoint points to an endpoint
    other than the default one and the v4 signature is used.
    Used by the AssumedRoleCredentialProvider and in Session and Role delegation
    tokens.
  </description>
</property>

<property>
  <name>fs.s3a.assumed.role.credentials.provider</name>
  <value>org.apache.hadoop.fs.s3a.SimpleAWSCredentialsProvider</value>
  <description>
    List of credential providers to authenticate with the STS endpoint and
    retrieve short-lived role credentials.
    Only used if AssumedRoleCredentialProvider is the AWS credential provider.
    If unset, uses "org.apache.hadoop.fs.s3a.SimpleAWSCredentialsProvider".
  </description>
</property>

<property>
  <name>fs.s3a.delegation.token.binding</name>
  <value></value>
  <description>
    The name of a class to provide delegation tokens support in S3A.
    If unset: delegation token support is disabled.

    Note: for job submission to actually collect these tokens,
    Kerberos must be enabled.

    Bindings available in hadoop-aws are:
    org.apache.hadoop.fs.s3a.auth.delegation.SessionTokenBinding
    org.apache.hadoop.fs.s3a.auth.delegation.FullCredentialsTokenBinding
    org.apache.hadoop.fs.s3a.auth.delegation.RoleTokenBinding
  </description>
</property>

<property>
  <name>fs.s3a.connection.maximum</name>
  <value>96</value>
  <description>Controls the maximum number of simultaneous connections to S3.
    This must be bigger than the value of fs.s3a.threads.max so as to stop
    threads being blocked waiting for new HTTPS connections.
    Why not equal? The AWS SDK transfer manager also uses these connections.
  </description>
</property>

<property>
  <name>fs.s3a.attempts.maximum</name>
  <value>5</value>
  <description>
    Number of times the AWS client library should retry errors before
    escalating to the S3A code: {@value}.
    The S3A connector does its own selective retries; the only time the AWS
    SDK operations are not wrapped is during multipart copy via the AWS SDK
    transfer manager.
  </description>
</property>

<property>
  <name>fs.s3a.socket.send.buffer</name>
  <value>8192</value>
  <description>Socket send buffer hint to amazon connector. Represented in bytes.</description>
</property>

<property>
  <name>fs.s3a.socket.recv.buffer</name>
  <value>8192</value>
  <description>Socket receive buffer hint to amazon connector. Represented in bytes.</description>
</property>

<property>
  <name>fs.s3a.paging.maximum</name>
  <value>5000</value>
  <description>How many keys to request from S3 when doing
     directory listings at a time.</description>
</property>

<property>
  <name>fs.s3a.multipart.size</name>
  <value>64M</value>
  <description>How big (in bytes) to split upload or copy operations up into.
    A suffix from the set {K,M,G,T,P} may be used to scale the numeric value.
  </description>
</property>

<property>
  <name>fs.s3a.multipart.threshold</name>
  <value>128M</value>
  <description>How big (in bytes) to split upload or copy operations up into.
    This also controls the partition size in renamed files, as rename() involves
    copying the source file(s).
    A suffix from the set {K,M,G,T,P} may be used to scale the numeric value.
  </description>
</property>

<property>
  <name>fs.s3a.multiobjectdelete.enable</name>
  <value>true</value>
  <description>When enabled, multiple single-object delete requests are replaced by
    a single 'delete multiple objects'-request, reducing the number of requests.
    Beware: legacy S3-compatible object stores might not support this request.
  </description>
</property>

<property>
  <name>fs.s3a.acl.default</name>
  <description>Set a canned ACL for newly created and copied objects. Value may be Private,
      PublicRead, PublicReadWrite, AuthenticatedRead, LogDeliveryWrite, BucketOwnerRead,
      or BucketOwnerFullControl.
    If set, caller IAM role must have "s3:PutObjectAcl" permission on the bucket.
  </description>
</property>

<property>
  <name>fs.s3a.multipart.purge</name>
  <value>false</value>
  <description>True if you want to purge existing multipart uploads that may not have been
    completed/aborted correctly. The corresponding purge age is defined in
    fs.s3a.multipart.purge.age.
    If set, when the filesystem is instantiated then all outstanding uploads
    older than the purge age will be terminated -across the entire bucket.
    This will impact multipart uploads by other applications and users. so should
    be used sparingly, with an age value chosen to stop failed uploads, without
    breaking ongoing operations.
  </description>
</property>

<property>
  <name>fs.s3a.multipart.purge.age</name>
  <value>86400</value>
  <description>Minimum age in seconds of multipart uploads to purge
    on startup if "fs.s3a.multipart.purge" is true
  </description>
</property>

<property>
  <name>fs.s3a.encryption.algorithm</name>
  <description>Specify a server-side encryption or client-side
    encryption algorithm for s3a: file system. Unset by default. It supports the
    following values: 'AES256' (for SSE-S3), 'SSE-KMS', 'SSE-C', and 'CSE-KMS'
  </description>
</property>

<property>
  <name>fs.s3a.encryption.key</name>
  <description>Specific encryption key to use if fs.s3a.encryption.algorithm
    has been set to 'SSE-KMS', 'SSE-C' or 'CSE-KMS'. In the case of SSE-C
    , the value of this property should be the Base64 encoded key. If you are
    using SSE-KMS and leave this property empty, you'll be using your default's
    S3 KMS key, otherwise you should set this property to the specific KMS key
    id. In case of 'CSE-KMS' this value needs to be the AWS-KMS Key ID
    generated from AWS console.
  </description>
</property>

<property>
    <name>fs.s3a.encryption.context</name>
    <description>Specific encryption context to use if fs.s3a.encryption.algorithm
      has been set to 'SSE-KMS' or 'DSSE-KMS'. The value of this property is a set
      of non-secret comma-separated key-value pairs of additional contextual
      information about the data that are separated by equal operator (=).
    </description>
</property>

<property>
  <name>fs.s3a.signing-algorithm</name>
  <description>Override the default signing algorithm so legacy
    implementations can still be used</description>
</property>

<property>
  <name>fs.s3a.block.size</name>
  <value>32M</value>
  <description>Block size to use when reading files using s3a: file system.
    A suffix from the set {K,M,G,T,P} may be used to scale the numeric value.
  </description>
</property>

<property>
  <name>fs.s3a.buffer.dir</name>
  <value>${env.LOCAL_DIRS:-${hadoop.tmp.dir}}/s3a</value>
  <description>Comma separated list of directories that will be used to buffer file
    uploads to.
    Yarn container path will be used as default value on yarn applications,
    otherwise fall back to hadoop.tmp.dir
  </description>
</property>

<property>
  <name>fs.s3a.fast.upload.buffer</name>
  <value>disk</value>
  <description>
    The buffering mechanism to for data being written.
    Values: disk, array, bytebuffer.

    "disk" will use the directories listed in fs.s3a.buffer.dir as
    the location(s) to save data prior to being uploaded.

    "array" uses arrays in the JVM heap

    "bytebuffer" uses off-heap memory within the JVM.

    Both "array" and "bytebuffer" will consume memory in a single stream up to the number
    of blocks set by:

        fs.s3a.multipart.size * fs.s3a.fast.upload.active.blocks.

    If using either of these mechanisms, keep this value low

    The total number of threads performing work across all threads is set by
    fs.s3a.threads.max, with fs.s3a.max.total.tasks values setting the number of queued
    work items.
  </description>
</property>

<property>
  <name>fs.s3a.fast.upload.active.blocks</name>
  <value>4</value>
  <description>
    Maximum Number of blocks a single output stream can have
    active (uploading, or queued to the central FileSystem
    instance's pool of queued operations.

    This stops a single stream overloading the shared thread pool.
  </description>
</property>

<property>
  <name>fs.s3a.readahead.range</name>
  <value>64K</value>
  <description>Bytes to read ahead during a seek() before closing and
    re-opening the S3 HTTP connection. This option will be overridden if
    any call to setReadahead() is made to an open stream.
    A suffix from the set {K,M,G,T,P} may be used to scale the numeric value.
  </description>
</property>

<property>
  <name>fs.s3a.user.agent.prefix</name>
  <value></value>
  <description>
    Sets a custom value that will be prepended to the User-Agent header sent in
    HTTP requests to the S3 back-end by S3AFileSystem.  The User-Agent header
    always includes the Hadoop version number followed by a string generated by
    the AWS SDK.  An example is "User-Agent: Hadoop 2.8.0, aws-sdk-java/1.10.6".
    If this optional property is set, then its value is prepended to create a
    customized User-Agent.  For example, if this configuration property was set
    to "MyApp", then an example of the resulting User-Agent would be
    "User-Agent: MyApp, Hadoop 2.8.0, aws-sdk-java/1.10.6".
  </description>
</property>

<property>
  <name>fs.s3a.impl</name>
  <value>org.apache.hadoop.fs.s3a.S3AFileSystem</value>
  <description>The implementation class of the S3A Filesystem</description>
</property>

<property>
  <name>fs.s3a.retry.limit</name>
  <value>7</value>
  <description>
    Number of times to retry any repeatable S3 client request on failure,
    excluding throttling requests.
  </description>
</property>

<property>
  <name>fs.s3a.retry.interval</name>
  <value>500ms</value>
  <description>
    Initial retry interval when retrying operations for any reason other
    than S3 throttle errors.
  </description>
</property>

<property>
  <name>fs.s3a.retry.throttle.limit</name>
  <value>20</value>
  <description>
    Number of times to retry any throttled request.
  </description>
</property>

<property>
  <name>fs.s3a.retry.throttle.interval</name>
  <value>100ms</value>
  <description>
    Initial between retry attempts on throttled requests, +/- 50%. chosen at random.
    i.e. for an intial value of 3000ms, the initial delay would be in the range 1500ms to 4500ms.
    Backoffs are exponential; again randomness is used to avoid the thundering heard problem.
    500ms is the default value used by the AWS S3 Retry policy.
  </description>
</property>

<property>
  <name>fs.s3a.committer.name</name>
  <value>file</value>
  <description>
    Committer to create for output to S3A, one of:
    "file", "directory", "partitioned", "magic".
  </description>
</property>

<property>
  <name>fs.s3a.committer.magic.enabled</name>
  <value>true</value>
  <description>
    Enable support in the S3A filesystem for the "Magic" committer.
  </description>
</property>

<property>
  <name>fs.s3a.committer.threads</name>
  <value>8</value>
  <description>
    Number of threads in committers for parallel operations on files
    (upload, commit, abort, delete...)
  </description>
</property>

<property>
  <name>fs.s3a.committer.staging.tmp.path</name>
  <value>tmp/staging</value>
  <description>
    Path in the cluster filesystem for temporary data.
    This is for HDFS, not the local filesystem.
    It is only for the summary data of each file, not the actual
    data being committed.
    Using an unqualified path guarantees that the full path will be
    generated relative to the home directory of the user creating the job,
    hence private (assuming home directory permissions are secure).
  </description>
</property>

<property>
  <name>fs.s3a.committer.staging.unique-filenames</name>
  <value>true</value>
  <description>
    Option for final files to have a unique name through job attempt info,
    or the value of fs.s3a.committer.staging.uuid
    When writing data with the "append" conflict option, this guarantees
    that new data will not overwrite any existing data.
  </description>
</property>

<property>
  <name>fs.s3a.committer.staging.conflict-mode</name>
  <value>append</value>
  <description>
    Staging committer conflict resolution policy.
    Supported: "fail", "append", "replace".
  </description>
</property>

<property>
  <name>fs.s3a.committer.abort.pending.uploads</name>
  <value>true</value>
  <description>
    Should the committers abort all pending uploads to the destination
    directory?

    Set to false if more than one job is writing to the same directory tree.
  </description>
</property>

<property>
  <name>fs.s3a.list.version</name>
  <value>2</value>
  <description>
    Select which version of the S3 SDK's List Objects API to use.  Currently
    support 2 (default) and 1 (older API).
  </description>
</property>

<property>
  <name>fs.s3a.etag.checksum.enabled</name>
  <value>false</value>
  <description>
    Should calls to getFileChecksum() return the etag value of the remote
    object.
    WARNING: if enabled, distcp operations between HDFS and S3 will fail unless
    -skipcrccheck is set.
  </description>
</property>

<property>
  <name>fs.s3a.change.detection.source</name>
  <value>etag</value>
  <description>
    Select which S3 object attribute to use for change detection.
    Currently support 'etag' for S3 object eTags and 'versionid' for
    S3 object version IDs.  Use of version IDs requires object versioning to be
    enabled for each S3 bucket utilized.  Object versioning is disabled on
    buckets by default. When version ID is used, the buckets utilized should
    have versioning enabled before any data is written.
  </description>
</property>

<property>
  <name>fs.s3a.change.detection.mode</name>
  <value>server</value>
  <description>
    Determines how change detection is applied to alert to inconsistent S3
    objects read during or after an overwrite. Value 'server' indicates to apply
    the attribute constraint directly on GetObject requests to S3. Value 'client'
    means to do a client-side comparison of the attribute value returned in the
    response.  Value 'server' would not work with third-party S3 implementations
    that do not support these constraints on GetObject. Values 'server' and
    'client' generate RemoteObjectChangedException when a mismatch is detected.
    Value 'warn' works like 'client' but generates only a warning.  Value 'none'
    will ignore change detection completely.
  </description>
</property>

<property>
  <name>fs.s3a.change.detection.version.required</name>
  <value>true</value>
  <description>
    Determines if S3 object version attribute defined by
    fs.s3a.change.detection.source should be treated as required.  If true and the
    referred attribute is unavailable in an S3 GetObject response,
    NoVersionAttributeException is thrown.  Setting to 'true' is encouraged to
    avoid potential for inconsistent reads with third-party S3 implementations or
    against S3 buckets that have object versioning disabled.
  </description>
</property>

<property>
  <name>fs.s3a.ssl.channel.mode</name>
  <value>default_jsse</value>
  <description>
    If secure connections to S3 are enabled, configures the SSL
    implementation used to encrypt connections to S3. Supported values are:
    "default_jsse", "default_jsse_with_gcm", "default", and "openssl".
    "default_jsse" uses the Java Secure Socket Extension package (JSSE).
    However, when running on Java 8, the GCM cipher is removed from the list
    of enabled ciphers. This is due to performance issues with GCM in Java 8.
    "default_jsse_with_gcm" uses the JSSE with the default list of cipher
    suites. "default_jsse_with_gcm" is equivalent to the behavior prior to
    this feature being introduced. "default" attempts to use OpenSSL rather
    than the JSSE for SSL encryption, if OpenSSL libraries cannot be loaded,
    it falls back to the "default_jsse" behavior. "openssl" attempts to use
    OpenSSL as well, but fails if OpenSSL libraries cannot be loaded.
  </description>
</property>

<property>
  <name>fs.s3a.downgrade.syncable.exceptions</name>
  <value>true</value>
  <description>
    Warn but continue when applications use Syncable.hsync when writing
    to S3A.
  </description>
</property>

<property>
  <name>fs.s3a.create.checksum.algorithm</name>
  <description>
    Indicates the algorithm used to create the checksum for the object
    to be uploaded to S3. Unset by default. It supports the following values:
    'CRC32', 'CRC32C', 'SHA1', 'SHA256', "CRC64_NVME", "none"
    The CRC64_NVME option requires aws-crt on the classpath, and is still
    tangibly slower than CRC32C, which has its own instruction on x86 and ARM.
  </description>
</property>

<property>
  <name>fs.s3a.audit.enabled</name>
  <value>true</value>
  <description>
    Should auditing of S3A requests be enabled?
  </description>
</property>

AWS 서비스 클라이언트용 커스텀 헤더 구성 (Configuring Custom Headers for AWS Service Clients)

S3와 STS 요청에 사용자 정의 헤더를 설정할 수 있어요. 이 헤더는 클라이언트 수준에서 설정되며 이 서비스들에 대한 모든 요청에 전송돼요.

구성 속성:

  • fs.s3a.client.s3.custom.headers: S3 서비스 요청용 커스텀 헤더.
  • fs.s3a.client.sts.custom.headers: AWS STS에 대한 모든 요청에 커스텀 헤더 설정.

헤더 형식: 커스텀 헤더는 = 로 구분된 키-값 쌍으로 지정해야 해요. 단일 헤더의 여러 값은 ; 로 구분할 수 있어요. 여러 헤더는 , 로 구분할 수 있어요.

<property>
    <name>fs.s3a.client.s3.custom.headers</name>
    <value>Header1=Value1</value>
</property>

<property>
    <name>fs.s3a.client.sts.custom.headers</name>
    <value>Header1=Value1;Value2,Header2=Value1</value>
</property>

재시도와 복구 (Retry and Recovery)

S3A 클라이언트는 네트워크 실패로부터 복구하려 최선의 노력을 해요. 이 섹션은 그것이 무엇을 하는지 상세히 다뤄요. S3A는 AWS SDK가 반환한 예외를 서로 다른 범주로 나누고, 그 유형과 실패하는 연산이 멱등(idempotent)인지 여부에 따라 다른 재시도 정책을 선택해요.

복구 불가능한 문제: 빠른 실패 (Unrecoverable Problems: Fail Fast)

  • 객체/버킷 스토어 없음: FileNotFoundException
  • 접근 권한 없음: AccessDeniedException
  • 복구 불가능으로 간주되는 네트워크 오류 (UnknownHostException, NoRouteToHostException, AWSRedirectException)
  • 중단: InterruptedIOException, InterruptedException
  • 거부된 HTTP 요청: InvalidRequestException

이것들과 다른 것들은 모두 복구 불가능으로 간주돼요: S3A는 그것들로부터 복구를 시도하지 않아요. AWS SDK 자체가 S3A 커넥터가 예외를 보기 전에 재시도할 수 있어요. 예를 들어 SDK는 일시적인 DNS 오류일 경우 UnknownHostException에 재시도할 거예요.

잠재적으로 복구 가능한 문제: 재시도 (Possibly Recoverable Problems: Retry)

  • 연결 타임아웃: ConnectTimeoutException. S3 엔드포인트(또는 프록시)에 연결을 설정하기 전에 타임아웃.
  • HTTP 응답 상태 코드 400, "Bad Request". 상태 코드 400, Bad Request는 보통 요청이 복구 불가능함을 의미해요. 일반적인 "No" 응답이에요. 매우 드물게 복구되므로 복구 불가능 실패가 아닌 이 범주에 있는 것이에요.

이 실패들은 fs.s3a.retry.interval에 설정된 지수적 대기 간격으로 fs.s3a.retry.limit에 설정된 한도까지 재시도돼요.

멱등 연산에서만 재시도 가능 (Only retriable on idempotent operations)

일부 네트워크 실패는 멱등 연산에서 발생하면 재시도 가능으로 간주돼요. 요청이 S3에 의해 처리된 후 발생했는지 알 방법이 없어요.

  • SocketTimeoutException: 일반 네트워크 실패.
  • EOFException: 데이터를 읽는 동안 연결이 끊어짐.
  • "No response from Server" (443, 444) HTTP 응답.
  • 다른 어떤 AWS 클라이언트, 서비스, S3 예외.

이 실패들은 지수적 대기로 재시도돼요.

S3로부터의 조절된(throttled) 요청

<property>
  <name>fs.s3a.retry.throttle.limit</name>
  <value>${fs.s3a.attempts.maximum}</value>
  <description>
    Number of times to retry any throttled request.
  </description>
</property>

<property>
  <name>fs.s3a.retry.throttle.interval</name>
  <value>1000ms</value>
  <description>
    Interval between retry attempts on throttled requests.
  </description>
</property>

참고: AWS SDK 내부에도 조절(throttling)이 발생하며 fs.s3a.attempts.maximum 값으로 관리돼요. 조절 이벤트는 S3A 파일시스템 메트릭과 통계에 추적돼요.

Amazon KMS는 모든 사용자 계정과 애플리케이션에서 KMS의 총 사용률에 기반해 고객을 조절할 수 있어요. S3 요청의 조절은 너무 흔해요. 너무 많은 클라이언트가 S3 스토리지의 같은 shard에 접근하려 하기 때문이에요. 이것은 일반적으로 읽기가 너무 많을 때 발생하며, 읽기는 Hadoop 애플리케이션에서 가장 흔해요. 이 문제는 데이터를 저장할 때 사용되는 Hive의 파티셔닝 전략(연도별로 파티셔닝하고 그다음 월별로)에 의해 악화돼요. 이것은 시작 부분이 거의 또는 전혀 변하지 않는 경로를 만들어 결국 모든 데이터가 같은 shard(들)에 저장되게 해요.

다음은 값비싼 연산들이며, S3 버킷의 일부에 대해 이들이 많을수록 S3 버킷이 겪는 부하가 커져요:

  • 많은 클라이언트가 디렉터리 나열이나 경로에 getFileStatus 호출 (각각 LIST와 HEAD 요청)
  • 데이터를 읽을 때 발행되는 GET 요청
  • 컬럼형 데이터(ORC, Parquet)를 읽을 때 사용되는 랜덤 IO는 파일당 하나의 단순한 GET보다 훨씬 더 많은 GET 요청을 의미해요.
  • 그 S3 버킷 부분에 대한 활성 쓰기 수.

특수한 경우는 S3 버킷의 일부에 충분한 데이터가 기록되어 S3가 데이터를 둘 이상의 shard로 나누기로 결정할 때예요: 이것은 시간이 걸릴 수 있는 어떤 복사 연산에 의해 수행되는 것으로 믿어져요. 이것이 진행되는 동안 이 경로 아래의 데이터에 접근하는 S3 클라이언트는 평소보다 더 조절될 거예요.

완화 전략:

  • 중간 데이터/다른 애플리케이션/역할에 대해 별도의 버킷 사용.
  • 같은 버킷에서 서로 다른 데이터셋에 대해 상당히 다른 경로 사용.
  • 시도 사이에 더 긴 지연을 제공하도록 fs.s3a.retry.throttle.interval 값을 증가.
  • 쿼리의 병렬성을 줄임. 병렬로 데이터에 접근하려는 태스크가 많을수록 부하가 커짐.
  • fs.s3a.threads.max 줄임.

읽기-동안-덮어쓰기 처리 (Handling Read-During-Overwrite)

읽기-동안-덮어쓰기는 작성자가 파일을 덮어쓰는 동안 읽는 사람이 파일에 열린 입력 스트림을 가진 조건이에요. 구성에 따라 S3AFileSystem은 이를 감지하고, 읽는 사람의 입력 스트림이 원래 버전의 파일에서 바이트를 읽는 것에서 새 버전의 바이트를 읽는 것으로 조용히 전환될 수 있는 조건에서 RemoteFileChangedException을 던질 수 있어요.

이 동작을 제어하는 구성 항목:

<property>
  <name>fs.s3a.change.detection.source</name>
  <value>etag</value>
  <description>
    Select which S3 object attribute to use for change detection.
  </description>
</property>

<property>
  <name>fs.s3a.change.detection.mode</name>
  <value>server</value>
  <description>
    Determines how change detection is applied to alert to S3 objects
    rewritten while being read. Value 'server' indicates to apply the attribute
    constraint directly on GetObject requests to S3. Value 'client' means to do a
    client-side comparison of the attribute value returned in the response.  Value
    'server' would not work with third-party S3 implementations that do not
    support these constraints on GetObject. Values 'server' and 'client' generate
    RemoteObjectChangedException when a mismatch is detected.  Value 'warn' works
    like 'client' but generates only a warning.  Value 'none' will ignore change
    detection completely.
  </description>
</property>

<property>
  <name>fs.s3a.change.detection.version.required</name>
  <value>true</value>
  <description>
    Determines if S3 object version attribute defined by
    fs.s3.change.detection.source should be treated as required.
  </description>
</property>

기본 구성에서는 S3 객체 eTag가 변경을 감지하는 데 사용돼요. 파일시스템이 Get Object로 S3에서 파일을 가져올 때 eTag를 캡처하고 그 eTag를 각 후속 요청의 If-Match 조건에 사용해요. 동시 작성자가 파일을 덮어썼다면 'If-Match' 조건이 실패하고 RemoteFileChangedException이 던져져요.

이 기본 구성에서도 새 쓰기가 열린 리더에서 이 예외를 트리거하지 않을 수 있어요. 예를 들어 리더가 파일에서 앞으로만 읽는다면 단일 S3 'Get Object' 요청만 이루어지고 파일의 전체 내용이 단일 응답에서 스트리밍돼요. 'Get Object' 요청 후 파일의 덮어쓰기는 이미 첫 바이트를 읽은 입력 스트림을 가진 리더가 전혀 보지 못할 거예요. 반면 뒤로 seek하면 'Get Object' 요청이 새로 발생할 수 있으며, 이것이 RemoteFileChangedException을 트리거할 수 있어요.

S3 버전으로 변경 감지. eTag 대신 S3 객체 버전 id를 변경 감지 메커니즘으로 사용하도록 전환할 수 있어요. 이 옵션을 사용하려면 파일시스템이 사용하는 모든 S3 버킷에서 객체 버전 관리(object versioning)가 활성화되어야 해요. eTag 대신 version id를 사용하는 이점은 RemoteFileChangedException의 빈도가 잠재적으로 줄어든다는 것이에요. 객체 버전 관리가 활성화되면 객체의 이전 버전은 덮어쓴 후에도 사용 가능해요. 이것은 열린 입력 스트림이 동시 작성자가 파일을 덮어쓴 후에도 뒤로 seek할 수 있음을 의미해요. 리더는 첫 바이트를 읽은 파일 버전에 대한 일관된 뷰를 유지할 거예요. version ID는 객체 버전 관리 활성화 전에 기록된 객체에 대해 null이므로, 이 옵션은 S3 버킷이 처음부터 객체 버전 관리를 활성화한 경우에만 사용해야 해요.

참고: 파일을 이름을 바꿀 때 복사된 파일은 다른 버전 번호를 가질 수 있어요.

변경 감지 모드. 구성 가능한 변경 감지 모드가 다음 옵션이에요.

버킷별 구성으로 다른 S3 버킷 구성하기 (Configuring different S3 buckets with Per-Bucket Configuration)

서로 다른 S3 버킷은 서로 다른 S3A 클라이언트 구성으로 접근할 수 있어요. 이것은 서로 다른 엔드포인트, 데이터 읽기·쓰기 전략, 로그인 세부 정보를 허용해요. 작은 집합의 수정 불가능한 값(현재 fs.s3a.impl)을 제외한 모든 fs.s3a 옵션은 버킷별로 설정할 수 있어요. 버킷별 옵션은 옵션의 fs.s3a. 접두사를 fs.s3a.bucket.BUCKETNAME.으로 대체해 설정하며, BUCKETNAME은 버킷 이름이에요. 버킷에 연결할 때 명시적으로 설정된 모든 옵션은 기본 fs.s3a. 값을 덮어써요.

예를 들어 구성은 Amazon EC2에 배포될 때 사용 가능한 IAM 역할 정보를 사용하는 기본 구성을 가질 수 있어요:

<property>
  <name>fs.s3a.aws.credentials.provider</name>
  <value>org.apache.hadoop.fs.s3a.auth.IAMInstanceCredentialsProvider</value>
</property>

이것은 S3A 버킷의 기본 인증 메커니즘이 돼요. 야간 데이터에 사용되는 s3a://nightly/ 버킷에는 세션 키를 줄 수 있어요:

<property>
  <name>fs.s3a.bucket.nightly.access.key</name>
  <value>AKAACCESSKEY-2</value>
</property>

<property>
  <name>fs.s3a.bucket.nightly.secret.key</name>
  <value>SESSIONSECRETKEY</value>
</property>

<property>
  <name>fs.s3a.bucket.nightly.session.token</name>
  <value>Short-lived-session-token</value>
</property>

<property>
  <name>fs.s3a.bucket.nightly.aws.credentials.provider</name>
  <value>org.apache.hadoop.fs.s3a.TemporaryAWSCredentialsProvider</value>
</property>

마지막으로 공용 s3a://noaa-isd-pds/ 버킷은 익명으로 접근할 수 있어요:

<property>
  <name>fs.s3a.bucket.noaa-isd-pds.aws.credentials.provider</name>
  <value>org.apache.hadoop.fs.s3a.AnonymousAWSCredentialsProvider</value>
</property>

버킷별 구성과 deprecated 구성 옵션 (per-bucket configuration and deprecated configuration options)

deprecated 암호화 옵션의 버킷별 선언은 전역 옵션보다 우선할 거예요 — 전역 옵션이 더 새로운 구성 키를 사용하더라도요. 이것은 XML 파일에서 암호화 옵션을 설정할 때 fs.bucket.BUCKET.fs.s3a.server-side-encryption-algorithm 옵션이 fs.bucket.s3a.encryption.algorithm의 전역 값보다 우선함을 의미해요. 암호화 키 옵션 fs.s3a.encryption.key와 그 전신 fs.s3a.server-side-encryption.key도 마찬가지예요.

사이트 구성:

<property>
  <name>fs.s3a.bucket.nightly.server-side-encryption-algorithm</name>
  <value>SSE-KMS</value>
</property>

<property>
  <name>fs.s3a.bucket.nightly.server-side-encryption.key</name>
  <value>arn:aws:kms:eu-west-2:1528130000000:key/753778e4-2d0f-42e6-b894-6a3ae4ea4e5f</value>
</property>

<property>
  <name>fs.s3a.encryption.algorithm</name>
  <value>AES256</value>
</property>

<property>
  <name>fs.s3a.encryption.key</name>
  <value>unset</value>
</property>

<property>
  <name>fs.s3a.encryption.context</name>
  <value>unset</value>
</property>

"nightly" 버킷은 KMS 키 arn:aws:kms:eu-west-2:1528130000000:key/753778e4-2d0f-42e6-b894-6a3ae4ea4e5f로 SSE-KMS를 사용해 암호화될 거예요.

Requester Pays 버킷 (Requester Pays buckets)

S3A는 Requester Pays가 활성화된 버킷을 지원해요. 버킷이 requester pays로 구성되면 요청자가 요청당 비용을 부담해야 해요. 요청이 성공하려면 S3 클라이언트가 각 요청에 요청 플래그(보통 헤더)를 설정해 이 요청에 대해 지불할 것임을 인정해야 해요. S3A 내에서 이 기능을 활성화하려면 fs.s3a.requester.pays.enabled 속성을 구성해요.

<property>
    <name>fs.s3a.requester.pays.enabled</name>
    <value>true</value>
</property>

스토리지 클래스 (Storage Classes)

Amazon S3는 애플리케이션의 동작에 따라 선택할 수 있는 다양한 Storage Classes를 제공해요. 올바른 스토리지 클래스를 사용하면 버킷의 비용을 줄일 수 있어요. S3A는 기본적으로 PUT 객체 요청에 Standard 스토리지 클래스를 사용하며, 이는 일반적인 사용 사례에 적합해요. 특정 스토리지 클래스를 사용하려면 fs.s3a.create.storage.class 속성의 값을 원하는 스토리지 클래스로 설정해요.

<property>
  <name>fs.s3a.create.storage.class</name>
  <value>intelligent_tiering</value>
</property>

S3A는 현재 아카이브 스토리지 클래스에서 읽기를 지원하지 않는다는 점에 유의하세요. 그렇게 하려 하면 AccessDeniedException with InvalidObjectState가 던져질 거예요. 파일이 s3a 커넥터를 통해 "이름이 바뀌면" 복사된 다음 삭제돼요. 스토리지 클래스는 보통 전파돼요.

S3 on Outposts용 S3A 구성 (Configuring S3A for S3 on Outposts)

S3A는 이제 S3 on Outposts를 지원해요. 접근 포인트(access point)를 통한 데이터 접근은 버킷 이름 대신 Amazon Resource Name (ARN)을 사용해 이루어져요. Outposts에서 지원되는 유일한 스토리지 클래스는 OUTPOSTS이며, 기본적으로 객체는 SSE-S3로 암호화돼요. 다음 버킷별 구성 속성을 사용해 Access Point ARN 속성을 설정할 수 있어요:

<property>
  <name>fs.s3a.bucket.sample-outpost-bucket.accesspoint.arn</name>
  <value>arn:aws:s3-outposts:region:account-id:outpost/outpost-id/accesspoint/accesspoint-name</value>
  <description>Configure S3a traffic to use this S3 on Outposts Access Point ARN</description>
</property>

이것은 S3A에 대해 sample-outpost-bucket 접근이 새 Access Point ARN을 통해 가도록 구성해요. 그래서 예를 들어 s3a://sample-outpost-bucket/key는 이제 버킷 대신 Outpost의 S3에서 데이터를 가져올 때 구성된 ARN을 사용할 거예요.

S3A가 S3에 데이터를 쓰는 방법 (How S3A writes data to S3)

원래 S3A 클라이언트는 OutputStream에 쓰여질 때 모든 데이터를 디스크에 버퍼링해 파일 쓰기를 구현했어요. 업로드는 스트림의 close() 메서드가 호출될 때만 시작됐어요. 이것은 특히 대형 업로드에서 출력을 느리게 만들었고, 작은 (가상) 디스크의 디스크 공간을 채울 수도 있었어요. Hadoop 2.7은 S3AFastOutputStream 대안을 추가했고 Hadoop 2.8이 확장했어요. 이제 안정적인 것으로 간주되며 hadoop에 더 이상 제공되지 않는 원래 S3AOutputStream을 대체했어요.

"fast" 출력 스트림:

  • fs.s3a.multipart.size가 설정한 크기의 블록으로 대형 파일을 업로드해요. 즉 multipart 업로드가 시작되는 임계값과 각 업로드의 크기는 동일해요. 이 동작은 fs.s3a.multipart.uploads.enabled 플래그로 활성화/비활성화할 수 있으며 기본적으로 true로 설정돼요.
  • 블록을 디스크(기본) 또는 on-heap 또는 off-heap 메모리에 버퍼링.
  • 백그라운드 스레드에서 병렬로 블록 업로드.
  • 버퍼링된 데이터가 이 파티션 크기를 초과하자마자 블록 업로드 시작.
  • fs.s3a.create.checksum.algorithm에 설정된 어떤 체크섬을 사용해 기록된 데이터에 업로드 체크섬을 계산. 이것은 파일/파트 업로드에 포함되고 스토어에서 검증돼요. 이것은 타사 스토어 호환성 문제의 원인이 될 수 있어요.
  • 데이터를 디스크에 버퍼링할 때 fs.s3a.buffer.dir에 나열된 디렉터리(들)를 사용해요. 버퍼링할 수 있는 데이터의 크기는 사용 가능한 디스크 공간으로 제한돼요.
  • 활성 및 보류 블록 업로드의 통계를 포함한 메트릭으로 파일시스템에 출력 통계를 생성.
  • close() 시간은 파일의 총 크기가 아니라 남은 업로드 데이터 양에 의해 결정.
  • 데이터가 여전히 쓰여지는 동안 업로드를 시작하므로 매우 많은 양의 데이터가 생성될 때 상당한 이점을 제공해요. 인메모리 버퍼링 메커니즘은 중간 데이터 스토리지에 디스크를 사용하지 않으므로 S3 엔드포인트 옆에서 실행할 때 속도 향상도 제공할 수 있어요.
<property>
  <name>fs.s3a.fast.upload.buffer</name>
  <value>disk</value>
  <description>
    The buffering mechanism to use.
    Values: disk, array, bytebuffer.

    "disk" will use the directories listed in fs.s3a.buffer.dir as
    the location(s) to save data prior to being uploaded.

    "array" uses arrays in the JVM heap

    "bytebuffer" uses off-heap memory within the JVM.

    Both "array" and "bytebuffer" will consume memory in a single stream up to the number
    of blocks set by:

        fs.s3a.multipart.size * fs.s3a.fast.upload.active.blocks.

    If using either of these mechanisms, keep this value low

    The total number of threads performing work across all threads is set by
    fs.s3a.threads.max, with fs.s3a.max.total.tasks values setting the number of queued
    work items.
  </description>
</property>

<property>
  <name>fs.s3a.multipart.size</name>
  <value>100M</value>
  <description>How big (in bytes) to split upload or copy operations up into.
    A suffix from the set {K,M,G,T,P} may be used to scale the numeric value.
  </description>
</property>

<property>
  <name>fs.s3a.fast.upload.active.blocks</name>
  <value>8</value>
  <description>
    Maximum Number of blocks a single output stream can have
    active (uploading, or queued to the central FileSystem
    instance's pool of queued operations.

    This stops a single stream overloading the shared thread pool.
  </description>
</property>

참고:

  • 스트림에 기록된 데이터 양이 fs.s3a.multipart.size에 설정된 것보다 적으면 업로드는 원래 출력 스트림과 같이 OutputStream.close() 연산에서 수행돼요.
  • 발행된 Hadoop 메트릭 모니터는 라이브 큐 길이와 업로드 연산 수를 포함하므로, 작업 백로그가 있거나 데이터 생성 속도와 네트워크 대역폭 사이의 불일치가 있을 때 식별할 수 있어요. 현재 스트림에서 toString()을 호출해 스트림별 통계를 기록할 수도 있어요.
  • 쓰여지고 있는 파일은 close() 호출에서 쓰기가 완료될 때까지 보이지 않으며, close()는 업로드가 완료될 때까지 블록돼요.

디스크에 업로드 데이터 버퍼링: fs.s3a.fast.upload.buffer=disk

fs.s3a.fast.upload.buffer가 disk로 설정되면 모든 데이터는 업로드 전에 로컬 하드 디스크에 버퍼링돼요. 이것은 소비되는 메모리 양을 최소화하므로 힙 크기를 큐된 업로드의 제한 요소로 제거해요 — 정확히 원래의 "직접 디스크" 버퍼링처럼요. 이것이 기본 버퍼 메커니즘이에요. 버퍼링할 수 있는 데이터 양은 사용 가능한 디스크 공간에 의해 제한돼요.

<property>
  <name>fs.s3a.fast.upload.buffer</name>
  <value>disk</value>
</property>

<property>
  <name>fs.s3a.buffer.dir</name>
  <value>${env.LOCAL_DIRS:-${hadoop.tmp.dir}}/s3a</value>
  <description>Comma separated list of directories that will be used to buffer file
    uploads to.
  </description>
</property>

ByteBuffers에 업로드 데이터 버퍼링: fs.s3a.fast.upload.buffer=bytebuffer

fs.s3a.fast.upload.buffer가 bytebuffer로 설정되면 모든 데이터는 업로드 전에 "Direct" ByteBuffers에 버퍼링돼요. 이것은 디스크에 버퍼링하는 것보다 빠를 수 있고, 디스크 공간이 작다면(예: 작은 EC2 VM) 버퍼링할 디스크 공간이 많지 않을 수 있어요. ByteBuffers는 JVM의 메모리에 생성되지만 Java Heap 자체에는 생성되지 않아요. 버퍼링할 수 있는 데이터 양은 Java 런타임, 운영체제, 그리고 YARN 애플리케이션의 경우 각 컨테이너에 요청된 메모리 양에 의해 제한돼요. S3로의 업로드 대역폭이 느릴수록 메모리를 소진할 위험이 크며, 따라서 업로드 설정 조정에 더 많은 주의가 필요해요.

<property>
  <name>fs.s3a.fast.upload.buffer</name>
  <value>bytebuffer</value>
</property>

바이트 배열에 업로드 데이터 버퍼링: fs.s3a.fast.upload.buffer=array

fs.s3a.fast.upload.buffer가 array로 설정되면 모든 데이터는 업로드 전에 JVM 힙의 바이트 배열에 버퍼링돼요. 이것은 디스크에 버퍼링하는 것보다 빠를 수 있어요. 버퍼링할 수 있는 데이터 양은 JVM 힙의 사용 가능한 크기에 의해 제한돼요. S3로의 쓰기 대역폭이 느릴수록 힙 오버플로우 위험이 커져요. 이 위험은 업로드 설정을 조정해 완화할 수 있어요.

<property>
  <name>fs.s3a.fast.upload.buffer</name>
  <value>array</value>
</property>

업로드 스레드 튜닝 (Upload Thread Tuning)

Array와 Byte buffer 메커니즘 모두 각각 on-heap 또는 off-heap의 매우 많은 양의 메모리를 소비할 수 있어요. 디스크 버퍼 메커니즘은 메모리를 많이 사용하지 않지만 하드 디스크 용량을 소비할 거예요. 단일 프로세스에 기록되는 출력 스트림이 많으면 사용되는 메모리 또는 디스크 양은 모든 스트림의 활성 메모리/디스크 사용량의 배수예요. 특히 데이터가 메모리에 버퍼링된다면 메모리 소진 위험을 줄이기 위해 신중한 튜닝이 필요할 수 있어요.

튜닝할 수 있는 여러 파라미터:

  • 데이터 업로드 또는 다른 어떤 큐된 파일시스템 연산에 대해 파일시스템에서 사용 가능한 총 스레드 수: fs.s3a.threads.max
  • 실행을 위해 큐에 넣을 수 있는 연산 수(스레드를 기다리는): fs.s3a.max.total.tasks
  • 단일 출력 스트림이 활성화할 수 있는 블록 수(즉 스레드가 업로드 중이거나 파일시스템 스레드 큐에 큐됨): fs.s3a.fast.upload.active.blocks
  • 유휴 스레드가 퇴역 전에 스레드 풀에 머무를 수 있는 시간: fs.s3a.threads.keepalivetime

단일 스트림의 최대 허용 활성 블록 수에 도달하면, 그 활성 블록 중 하나 이상의 업로드가 완료될 때까지 그 스트림에서 더 이상 블록을 업로드할 수 없어요. 즉 이제 가득 찬 데이터 블록의 업로드를 트리거하는 write() 호출은 큐에 용량이 생길 때까지 대신 블록돼요.

이것이 어떻게 합쳐질까요? fs.s3a.threads.max에 설정된 스레드 풀은 공유(그리고 모든 스레드에 걸쳐 사용되도록 의도됨)되므로 여기의 더 큰 수는 더 많은 병렬 연산을 허용할 수 있어요. 하지만 업로드는 네트워크 대역폭을 요구하므로 스레드를 더 추가한다고 속도 향상이 보장되지는 않아요. 스레드 풀에 대한 추가 태스크 큐(fs.s3a.max.total.tasks)는 모든 진행 중인 백그라운드 S3A 연산을 포함해요 (향후 계획: 병렬화된 이름 변경 연산, 비동기 디렉터리 연산). 메모리 버퍼링을 사용할 때 fs.s3a.fast.upload.active.blocks의 작은 값은...

<property>
  <name>fs.s3a.fast.upload.active.blocks</name>
  <value>4</value>
  <description>
    Maximum Number of blocks a single output stream can have
    active (uploading, or queued to the central FileSystem
    instance's pool of queued operations.

    This stops a single stream overloading the shared thread pool.
  </description>
</property>

부분 업로드 실패 후 정리 (Cleaning up after partial Upload Failures)

남은 multipart 업로드를 정리하는 네 가지 메커니즘이 있어요:

  • AWS Lifecycle 규칙. 이것이 가장 간단하며 특별한 이유(예: 스토어가 lifecycle 규칙을 지원하지 않음)가 없으면 사용해야 해요.
  • 나이별로 업로드를 나열·삭제하는 Hadoop s3guard CLI 명령. S3Guard 섹션에 문서화.
  • 디렉터리 이름 변경·삭제 중 자동 스캔·삭제를 위해 fs.s3a.directory.operations.purge.uploads를 true로 설정.
  • 구성 파라미터 fs.s3a.multipart.purge (아래에서 다룸).

큰 스트림 쓰기 연산이 중단되면 중간 파티션 업로드가 S3에 남을 수 있어요 — 비용이 청구될 데이터. S3A 커미터 작업이 중간에 멈추면 출력 디렉터리에 완료되지 않은 multipart 업로드가 많을 수 있어요. fs.s3a.multipart.purge를 활성화하고 24시간 같은 초 단위의 정리 시간을 설정해 이 청구를 줄일 수 있어요. S3A FileSystem 인스턴스가 0보다 큰 정리 시간으로 인스턴스화되면 시작 시 이 시간보다 오래된 모든 미해결 파티션 요청을 삭제해요. 하지만 이것은 특히 매우 큰 버킷에 대해 전체 스캔이 이루어지므로 파일시스템 인스턴스화를 느리게 해요.

<property>
  <name>fs.s3a.multipart.purge</name>
  <value>true</value>
  <description>True if you want to purge existing multipart uploads that may not have been
     completed/aborted correctly</description>
</property>

<property>
  <name>fs.s3a.multipart.purge.age</name>
  <value>24h</value>
  <description>Minimum age in seconds of multipart uploads to purge</description>
</property>

S3A 클라이언트가 fs.s3a.multipart.purge=true로 인스턴스화되면 전체 버킷의 모든 오래된 업로드를 삭제할 거예요. 즉 모든 애플리케이션의 그 버킷에 대한 모든 multipart 업로드에 영향을 미칠 거예요. fs.s3a.multipart.purge를 기본값 false로 두면 클라이언트가 파티션 속도를 재설정하거나 변경하려 시도하지 않을 거예요. 이 옵션을 사용하는 모범 사례는 S3A 정상 사용에서 multipart 정리를 비활성화하고 수동/예약된 하우스키핑 연산에서만 활성화하는 것이에요.

메트릭 (Metrics)

S3A 메트릭은 Hadoop의 metrics2 프레임워크를 통해 모니터링할 수 있어요. S3A는 s3a-file-system이라는 자체 메트릭 시스템을 만들며, 클라이언트의 각 인스턴스는 JVM 고유 숫자 ID로 이름이 지정된 자체 메트릭 소스를 만들 거예요. 간단한 예로 hadoop-metrics2.properties에 다음을 추가해 모든 S3A 메트릭을 10초마다 로그 파일에 쓸 수 있어요:

s3a-file-system.sink.my-metrics-config.class=org.apache.hadoop.metrics2.sink.FileSink
s3a-file-system.sink.my-metrics-config.filename=/var/log/hadoop-yarn/s3a-metrics.out
*.period=10

그 파일의 줄은 다음과 같이 구조화될 거예요:

1511208770680 s3aFileSystem.s3aFileSystem: Context=s3aFileSystem, s3aFileSystemId=892b02bb-7b30-4ffe-80ca-3a9935e1d96e, bucket=bucket,
Hostname=hostname-1.hadoop.apache.com, files_created=1, files_copied=2, files_copied_bytes=10000, files_deleted=5, fake_directories_deleted=3,
directories_created=3, directories_deleted=0, ignored_errors=0, op_copy_from_local_file=0, op_exists=0, op_get_file_status=15, op_glob_status=0,
op_is_directory=0, op_is_file=0, op_list_files=0, op_list_located_status=0, op_list_status=3, op_mkdirs=1, op_rename=2, object_copy_requests=0,
object_delete_requests=6, object_list_requests=23, object_continue_list_requests=0, object_metadata_requests=46, object_multipart_aborted=0,
object_put_bytes=0, object_put_requests=4, object_put_requests_completed=4, stream_write_failures=0, stream_write_block_uploads=0,
stream_write_block_uploads_committed=0, stream_write_block_uploads_aborted=0, stream_write_total_time=0, stream_write_total_data=0,
s3guard_metadatastore_put_path_request=10, s3guard_metadatastore_initialization=0, object_put_requests_active=0, object_put_bytes_pending=0,
stream_write_block_uploads_active=0, stream_write_block_uploads_pending=0, stream_write_block_uploads_data_pending=0,
S3guard_metadatastore_put_path_latencyNumOps=0, S3guard_metadatastore_put_path_latency50thPercentileLatency=0, ...

다른 구성에 따라 다른 시스템, 컨텍스트 등의 메트릭도 기록될 수 있어요. AWS SDK 자체의 저수준 메트릭은 현재 이 메트릭에 포함되지 않는다는 점에 유의하세요.

체크섬 (Checksums)

S3 클라이언트는 여러 방식으로 요청에 체크섬을 사용할 수 있어요:

  1. 요청 헤더의 체크섬 제공.
  2. 요청 헤더의 Content-MD5 해시 제공.
  3. 스토어에 PUT/POST되는 데이터의 체크섬 제공.
  4. 스토어에서 다운로드된 데이터 검증.

다양한 옵션이 성능과 호환성에 영향을 줄 수 있어요. 여기서의 위험과 이슈를 이해하려면:

  • 요청 체크섬 생성(1항)과 검증(4항)은 "필요할 때(when required)" 또는 "항상(always)"으로 할 수 있어요. "always" 옵션은 더 엄격하지만 타사 호환성 문제가 생길 수 있어요.
  • 일부 타사 스토어는 Content-MD5 헤더를 요구하며 그것 없이는 실패해요 (2항)
  • 데이터 업로드 체크섬(3항)은 계산 비용이 들 수 있고 타사 스토어와 호환되지 않을 수 있어요
  • 가장 효율적인 데이터 업로드 체크섬은 CRC32C이며, x86과 ARM CPU에 적절한 구현 회로를 위한 명시적 opcode가 있어요.
  • 데이터 다운로드 검증 체크섬도 계산 비용이 들어요.
Option Purpose Values Default
fs.s3a.request.md5.header MD5 헤더 활성화 boolean true
fs.s3a.checksum.generation 모든 요청에 체크섬 생성 boolean false
fs.s3a.checksum.validation 다운로드 시 체크섬 검증 boolean false
fs.s3a.create.checksum.algorithm 파일 생성/복사 시 체크섬 알고리즘 NONE, CRC32, CRC32C, CRC32_C, CRC64NVME, CRC64_NVME, SHA256, SHA1 ""

체크섬 생성과 검증을 켜는 것은 명백한 조치처럼 보일 수 있지만, 이것을 고려하세요: 당신은 HTTPS 채널(전송되는 모든 블록의 암호화적으로 강력한 HMAC 체크섬을 포함)을 통해 S3 스토어와 통신하고 있어요. 이것들은 CRC* 알고리즘보다 훨씬 강력하며 계산 비용은 이미 지불하고 있어요: 그렇다면 왜 더 추가하나요? TLS가 네트워크 트래픽이 암호화된 순간부터 복호화될 때까지 변경되지 않음을 보장하므로, 추가 체크섬 생성/검증이 하는 모든 것은 암호 해독과 복호화 사이의 우발적 손상이 없음을 보장하는 것뿐이에요.

CRC64NVME 기능을 활성화하려면 software.amazon.awssdk.crt:aws-crt 모듈에 대한 의존성을 추가해요. 체크섬/알고리즘 비호환성은 "Completing multipart upload"에서 "missing part"로 보고되는 실패로 표면화될 수 있어요. 대안으로 체크섬 알고리즘이 설정되고 파트 순서가 순차적이지 않을 때 multipart 업로드의 실패로 표면화될 수 있어요. 이것은 FileSystem multipart API를 통해서만 가능해요; magic committer를 통한 일반 데이터 쓰기는 그것을 마주치지 않을 거예요.

기타 주제 (Other Topics)

distcp로 데이터 복사하기 (Copying Data with distcp)

Hadoop의 distcp 도구는 Hadoop 클러스터와 Amazon S3 사이에서 데이터를 복사하는 데 자주 사용돼요. S3 복사에 대해 구체적으로는 Copying Data Between a Cluster and Amazon S3를 참고하세요. distcp update 명령은 데이터의 증분 업데이트를 시도해요. 파일이 다른 길이일 때는 파일이 일치하지 않는지 검증하는 것이 간단하지만, 크기가 같을 때는 그렇지 않아요. DistCp는 소스와 대상 파일시스템의 파일 체크섬을 비교해 이것을 해결하며, 파일시스템이 호환되지 않는 체크섬 알고리즘을 가져도 그렇게 하려 시도해요. S3A 커넥터는 업로드된 파일의 체크섬으로 HTTP etag 헤더를 호출자에게 제공할 수 있어요. 그렇게 하면 hdfs와 s3a 사이의 distcp 연산이 깨져요. 이런 이유로 etag-as-checksum 기능은 기본적으로 비활성화돼 있어요.

<property>
  <name>fs.s3a.etag.checksum.enabled</name>
  <value>false</value>
  <description>
    Should calls to getFileChecksum() return the etag value of the remote
    object.
    WARNING: if enabled, distcp operations between HDFS and S3 will fail unless
    -skipcrccheck is set.
  </description>
</property>

활성화하면 두 S3 버킷 사이의 distcp가 체크섬을 사용해 객체를 비교할 수 있어요. 각각 단일 파일 PUT으로 업로드됐거나, multipart PUT이라면 fs.s3a.multipart.size 값이 구성한 대로 같은 크기의 블록으로 업로드됐다면 그 체크섬은 동일해야 해요. distcp에서 체크섬 검증을 비활성화하려면 -skipcrccheck 옵션을 사용해요:

hadoop distcp -update -skipcrccheck -numListstatusThreads 40 /user/alice/datasets s3a://alice-backup/datasets

고급 - 커스텀 사인어 (Advanced - Custom Signers)

AWS는 요청을 인증하는 데 요청 서명(request signing)을 사용해요. 일반적으로 사인어를 재정의할 필요가 없으며 기본값이 바로 작동해요. 하지만 필요한 경우 이 섹션에서 커스텀 사인어를 구성하는 방법을 다뤄요. 설정할 넓은 구성 범주는 두 가지가 있어요. 하나는 커스텀 사인어 등록용, 다른 하나는 사용 지정용이에요.

커스텀 사인어 등록 (Registering Custom Signers):

<property>
  <name>fs.s3a.custom.signers</name>
  <value>comma separated list of signers</value>
  <!-- Example
  <value>AWS4SignerType,CS1:CS1ClassName,CS2:CS2ClassName:CS2InitClass</value>
  -->
</property>

각 커스텀 사인어의 허용 가능한 값:

  • SignerName — 기본 사인어 중 하나가 사용되는 경우에 사용됨. (예: AWS4SignerType, QueryStringSignerType, AWSS3V4SignerType). 커스텀 사인어가 사용되지 않으면 이 값은 설정할 필요가 없어요.
  • SignerName:SignerClassName — 지정된 이름과 이 사인어의 클래스로 새 사인어를 등록. Signer Class는 software.amazon.awssdk.core.signer.Signer를 구현해야 해요.
  • SignerName:SignerClassName:SignerInitializerClassName — 위와 유사하지만 커스텀 SignerInitializer (org.apache.hadoop.fs.s3a.AwsSignerInitializer) 클래스를 지정할 수도 있어요.

사인어 사용 (Usage of the Signers):

사인어는 서비스별(S3 등)로 또는 모든 서비스에 대한 공통 사인어로 설정할 수 있어요.

<property>
  <name>fs.s3a.s3.signing-algorithm</name>
  <value>${S3SignerName}</value>
  <description>Specify the signer for S3</description>
</property>

<property>
  <name>fs.s3a.signing-algorithm</name>
  <value>${SignerName}</value>
</property>

특정 서비스의 경우 서비스 특정 사인어를 먼저 찾아요. 그것이 지정되지 않으면 공통 사인어를 찾아요. 이것도 지정되지 않으면 SDK 설정이 사용돼요.

더 알아보기 (Learn more)