애플리케이션에서 HDFS 파일 목록을 읽고, 업로드가 온전히 끝났는지 확인한다. 셸 없이 코드로 처리해야 할 때 쓴다.
pyarrow 의 HadoopFileSystem 은 HADOOP_CONF_DIR 아래의 core-site.xml · hdfs-site.xml 을 읽으므로 HA 네임서비스와 Kerberos 설정을 그대로 활용한다. libhdfs 를 쓰므로 Hadoop 네이티브 라이브러리와 JVM 이 필요하다.
import pyarrow.fs as fs
hdfs = fs.HadoopFileSystem("default")
selector = fs.FileSelector("/user/hadoop/data", recursive=False)
for info in hdfs.get_file_info(selector):
kind = "DIR" if info.type == fs.FileType.Directory else "FILE"
print(kind, info.path, info.size)
export HADOOP_CONF_DIR=/etc/hadoop/conf
export CLASSPATH=$(hadoop classpath --glob)
CLASSPATH 를 설정하지 않으면 libhdfs 가 클래스를 찾지 못해 연결 단계에서 실패한다.
HTTP 로 접근한다면 WebHDFS 기반 라이브러리를 쓴다. JVM 이 필요 없지만 NameNode 와 DataNode 의 HTTP 포트가 모두 열려 있어야 한다. 이때 NameNode HTTP 포트는 Hadoop 3 기준 9870 이며, 2.x 의 50070 과 다르다.
업로드 검증은 세 단계로 한다. 존재하는지, 크기가 기대와 같은지, 체크섬이 일치하는지 본다.
Configuration conf = new Configuration();
try (FileSystem fs = FileSystem.get(URI.create("hdfs://ns1"), conf)) {
Path path = new Path("/user/data/upload.csv");
if (!fs.exists(path)) {
throw new IllegalStateException("not uploaded: " + path);
}
FileStatus st = fs.getFileStatus(path);
System.out.println("size=" + st.getLen() + " mtime=" + st.getModificationTime());
FileChecksum checksum = fs.getFileChecksum(path);
System.out.println("checksum=" + checksum);
}
getFileChecksum 이 돌려주는 값은 파일 전체의 MD5 가 아니라 블록별 CRC 를 다시 묶은 복합 체크섬이다. 따라서 로컬 파일의 md5sum 과 직접 비교할 수 없다. 비교가 성립하려면 다음이 모두 같아야 한다.
| 조건 | 설명 |
|---|---|
| 블록 크기 | dfs.blocksize 가 다르면 값이 달라진다 |
| 바이트당 체크섬 | dfs.bytes-per-checksum |
| 체크섬 타입 | CRC32 · CRC32C |
서로 다른 클러스터의 파일을 비교할 때 이 조건이 어긋나 값이 달라지는 일이 흔하다. Hadoop 3 에는 블록 구성과 무관하게 비교할 수 있는 COMPOSITE_CRC 방식이 있다.
hdfs dfs -checksum /user/data/upload.csv
hdfs dfs -Ddfs.checksum.combine.mode=COMPOSITE_CRC -checksum /user/data/upload.csv
로컬 파일과의 대조가 목적이라면 업로드 전에 계산한 md5 를 별도 파일로 함께 올려 두고, 다운로드 후 비교하는 방식이 단순하고 확실하다.
FileSystem.get 이 돌려주는 객체는 캐시에서 공유된다. 한쪽에서 close() 하면 같은 URI 를 쓰는 다른 코드에서 FileSystem closed 예외가 난다. 장기 실행 애플리케이션에서는 인스턴스를 공유하고 종료 시점에만 닫거나, 캐시를 꺼서 독립 인스턴스를 받는다.