hadoop distcp 는 MapReduce 잡으로 파일을 병렬 복사한다. 대상은 HDFS 사이, HDFS 에서 S3 호환 스토리지, 그 반대가 모두 된다. hdfs dfs -cp 는 클라이언트 한 곳을 거치므로 대용량에서는 비교가 되지 않는다.
hadoop distcp /user/source /user/destination
hadoop distcp hdfs://src-nn:8020/data hdfs://dst-nn:8020/data
hadoop distcp hdfs://nn:8020/user/data s3a://mybucket/data
| 옵션 | 뜻 |
|---|---|
-m <n> |
매퍼 수. 병렬도를 정한다 |
-update |
크기·타임스탬프가 다른 파일만 복사 |
-overwrite |
대상 파일을 무조건 덮어쓴다 |
-delete |
원본에 없는 파일을 대상에서 지운다. -update 또는 -overwrite 와 함께 쓴다 |
-diff |
스냅샷 두 개의 차이만 복사 |
-bandwidth <MB> |
매퍼당 대역폭 제한(MB/s) |
-p[rbugpcaxt] |
권한·복제수·소유자·그룹·타임스탬프 등 속성 보존 |
-strategy dynamic |
매퍼가 작업을 동적으로 나눠 가진다. 파일 크기 편차가 클 때 유리 |
-i |
실패를 무시하고 진행 |
-log <path> |
로그 출력 경로 |
-bandwidth 는 매퍼당 값이다. -m 20 -bandwidth 10 이면 이론상 최대 200MB/s 를 쓴다. 전체 대역을 제한하려면 매퍼 수와 함께 계산한다.
-update 는 경로 해석이 다르다. 붙이면 소스 디렉터리의 내용이 대상 디렉터리 아래로 들어가고, 없으면 소스 디렉터리 자체가 대상 아래에 생긴다. 반복 실행하는 스크립트에서 경로가 한 단계씩 깊어지는 사고가 여기서 나온다.
보안이 켜진 쪽에서 kinit 한 뒤, 비보안 쪽으로의 단순 인증 폴백을 허용한다.
kinit -kt /etc/security/keytabs/hdfs.keytab hdfs@KRB.EXAMPLE.NET
hadoop distcp \
-D ipc.client.fallback-to-simple-auth-allowed=true \
-D dfs.client.use.datanode.hostname=true \
hdfs://insecure-nn:8020/user/data \
hdfs://secure-nn:8020/user/backup
ipc.client.fallback-to-simple-auth-allowed=true 는 보안 클러스터의 클라이언트가 비보안 상대와 통신할 때 필요하다. 잡은 보안 클러스터에서 실행하는 편이 권한 처리가 단순하다.
두 클러스터가 모두 Kerberos 이고 realm 이 다르면 cross-realm trust 를 먼저 맺어야 한다. 그렇지 않으면 GSS initiate failed 가 난다.
자격증명은 명령줄에 적지 않고 core-site.xml 이나 자격증명 공급자(jceks)에 둔다. 명령줄 인자는 ps 와 잡 설정에 그대로 남는다.
<property>
<name>fs.s3a.endpoint</name>
<value>https://minio.example.net</value>
</property>
<property>
<name>fs.s3a.path.style.access</name>
<value>true</value>
</property>
<property>
<name>fs.s3a.access.key</name>
<value>${ACCESS_KEY}</value>
</property>
<property>
<name>fs.s3a.secret.key</name>
<value>${SECRET_KEY}</value>
</property>
S3 는 디렉터리 rename 이 원자적이지 않다. 큰 작업에서는 커밋 단계가 오래 걸리거나 중간 실패 시 정리가 필요할 수 있다. 타임스탬프 기반 -update 는 S3 쪽 메타데이터 특성 때문에 기대와 다르게 동작할 수 있으므로, 체크섬 비교를 끄고(-skipcrccheck) 크기 기준으로 맞추는 방식이 흔히 쓰인다.
소량이면 FileSystem API 로 처리할 수 있다. 이때 FileSystem 객체는 캐시를 공유하므로 close() 호출 시점에 주의한다. 다른 코드가 같은 URI 의 인스턴스를 쓰고 있는 상태에서 닫으면 그쪽에서 FileSystem closed 예외가 난다. 공유가 문제라면 fs.%s.impl.disable.cache=true 를 주고 독립 인스턴스를 받는다.
Configuration conf = new Configuration();
conf.setBoolean("fs.s3a.impl.disable.cache", true);
try (FileSystem s3 = FileSystem.get(URI.create("s3a://bucket/path"), conf)) {
// ...
}