데이터실무

DA, SQL, DB보안 등 실무자를 위한 위한 DB기술 바이블!

하둡 WebHDFS 서비스

데이터 저장
분산파일시스템
하둡 WebHDFS 서비스
작성자
admin
작성일
2021-02-15 13:44
조회
2757

하둡 WebHDFS는 하둡 버전 1.0.0부터 제공한다. 기존의 하둡 분산 파일 시스템은 하둡 클라이언트나 SDK를 갖고 있어야만 하둡 분산 파일 시스템에 접근할 수 있지만, 버전 1.0.0부터는 HTTP Rest API를 제공하고 있어서 하둡 클라이언트나 SDK가 없더라도 하둡 분산 파일 시스템에서 파일을 처리할 수 있다.
하둡 WebHDFS의 기능은 HTTP 메소드 단위로 크게 4가지로 구분된다.


  • HTTP GET
    1. OPEN
    2. GETFILESTATUS
    3. LISTSTATUS
    4. GETCONTENTSUMMARY
    5. GETFILECHECKSUM
    6. GETHOMEDIRECTORY
    7. GETDELEGATIONTOKEN
  • HTTP PUT
    1. CREATE
    2. MKDIRS
    3. RENAME
    4. SETREPLICATION
    5. SETOWNER
    6. SETPERMISSION
    7. SETTIMES
    8. RENEWDELIGATIONTOKEN
    9. CANCELDELIGATIONTOKEN
  • HTTP POST
    1. APPEND
  • HTTP DELETE
    1. DELETE

HTTP GET

OPEN

OPEN은 파일 내용을 조회할 때 사용되는 기능으로 다음과 같이 사용한다.

sh> curl -i -L "http://<HOST>:<PORT>/webhdfs/v1/<PATH>?op=OPEN[&offset=<LONG>][&length=<LONG>[&buffersize=<INT>]"

리눅스에서 사용하는 명령어인 curl을 이용해 URL로 데이터를 전송하거나 수신할 수 있다. <HOST>와 <PORT>는 하둡 설정값을 참조하는데, 기본값은 네임 노드 주소:50090이다. 이 값을 변경하고 싶다면 다음과 같이 하둡 설정 파일인 conf/hdfs-site.xml을 수정한다.

...<property><name>dfs.http.address</name><value>0.0.0.0:50070</value></property>...




[Note]

하둡의 WebHDFS 서비스를 이용할 때 세션이 네임 노드에서 데이터 노드로 리다이렉트된다. 그래서 인터넷 웹 브라우저를 이용해 파일을 내려받거나 올릴 때 브라우저 옵션에서 URL 리다이렉트가 금지돼 있으면 서비스 이용이 불가능하다.


GETFILESTATUS

GETFILESTATUS는 파일이나 폴더의 상태 정보를 조회할 때 사용한다. 응답 데이터 형식이 JSON이라서 곧바로 웹 기반으로 사용할 수 있다.

sh> curl -i "http://<HOST>:<PORT>/webhdfs/v1/<PATH>?op=GETFILESTATUS"

다음은 응답 데이터의 예다.

HTTP/1.1 200 OK Content-Type: application/json Transfer-Encoding: chunked {"FileStatus":{"accessTime" : 0, "blockSize" : 0, "group" : "supergroup", "length" : 0, //in bytes, zero for directories "modificationTime" : 1320173277227, "owner" : "webuser", "pathSuffix" : "". "permission" : "777", "replication" : 0, "type" : "DIRECTORY" //enum {FILE, DIRECTORY}}}

하둡 WebHDFS의 대부분 응답 형식은 JSON 오브젝트다.


LISTSTATUS

LISTSTATUS는 지정한 폴더 안의 파일과 폴더 목록을 조회할 때 사용한다.

sh> curl -i "http://<HOST>:<PORT>/webhdfs/v1/<PATH>?op=LISTSTATUS"

다음은 응답 데이터의 예다.

HTTP/1.1 200 OK Content-Type: application/json Content-Length: 427 {"FileStatuses": {"FileStatus": [{"accessTime" : 1320171722771, "blockSize" : 33554432, "group" : "supergroup", "length" : 24930, "modificationTime" : 1320171722771, "owner" : "webuser", "pathSuffix" : "a.patch", "permission" : "644", "replication" : 1, "type" : "FILE"},{"accessTime" : 0, "blockSize" : 0, "group" : "supergroup", "length" : 0, "modificationTime" : 1320895981256, "owner" : "szetszwo", "pathSuffix" : "bar", "permission" : "711", "replication" : 0, "type" : "DIRECTORY"}, ... ]}}


GETCONTENTSUMMARY

GETCONTENTSUMMARY는 지정한 파일이나 폴더에 대한 요약 정보를 보여준다.

sh> curl -i "http://<HOST>:<PORT>/webhdfs/v1/<PATH>?op=GETCONTENTSUMMARY"

다음은 응답 데이터의 예다.

HTTP/1.1 200 OK Content-Type: application/json Transfer-Encoding: chunked {"ContentSummary" : {"directoryCount" : 2, "fileCount" : 1, "length" : 24930, "quota" : -1, "spaceConsumed": 24930, "spaceQuota" : -1}}


GETFILECHECKSUM

GETFILECHECKSUM은 지정한 파일의 checksum 데이터를 요청할 때 사용한다.

sh> curl -i "http://<HOST>:<PORT>/webhdfs/v1/<PATH>?op=GETFILECHECKSUM"

이 기능은 네임 노드에 요청하지만 데이터 노드에서 응답한다. 그래서 두 번의 응답 데이터를 받게 된다. 다음은 응답 데이터의 예다.

첫 번째 응답 HTTP/1.1 307 TEMPORARY_REDIRECT Location: http://<DATANODE>:<PORT>/wedhdfs/v1/<PATH>?op=GETFILECHECKSUM... Content-Length: 0 두 번째 응답 HTTP/1.1 200 OK Content-Type: application/json Transfer-Encoding: chunked {"FileChecksum":{"algorithm" : "MD5-of-1MD5-of-512CRC32", "bytes" : "eadb10de24aa375748930df6e185c0d...", "length" : 28}}


GETHOMEDIRECTORY

GETHOMEDIRECTORY는 현재 접근하는 사용자 계정의 홈 디렉터리 전체 경로를 요청한다.

sh> curl -i "http://<HOST>:<PORT>/webhdfs/v1/<PATH>?op=GETHOMEDIRECTORY"

다음은 응답 데이터의 예다.

HTTP/1.1 200 OK Content-Type: application/json Transfer-Encoding: chunked {"Path" : "/user/webuser"}

하둡은 웹 접근 계정을 별도로 설정하지 않을 경우 기본적으로 webuser 사용자 계정을 사용한다. 웹 접근 사용자 계정을 변경하려면 다음 두 가지 방법을 사용한다.


  • 하둡 설정에서 기본 웹 접근 사용자 계정 지정

    하둡 설정 파일(conf/hdfs-site.xml)에 다음 부분을 추가한다.

    ...<property><name>dfs.web.ugi</name><value>[사용자 계정], [사용자 그룹]</value><description>The user account used by the web interface. Syntax: USERNAME, GROUP1, GROUP2, ...</description></property>...

  • URL에 사용자 계정 지정

    URL으로 데이터를 요청할 때 다음과 같은 속성을 추가한다. 단, 이 방법은 하둡 보안 설정을 해제한 상태에서만 가능하다.

    sh> curl -i "http://<HOST>:<PORT>/webhdfs/v1/?user,name=<사용자 계정>&op=GETHOMEDIRECTORY"



[Note]

만약 HDFS에서 파일에 대한 접근 권한을 사용하지 않을 경우에는 conf/hdfs-site.xml 에 다음 설정을 추가하자.

<property>
<name>dfs.permissions</name>
<value>false</value>
</property>

HDFS를 재시작한 후부터는 모든 파일과 폴더는 누구나 접근할 수 있다.


HTTP PUT

CREATE

CREATE는 하둡파일 시스템 내에 파일을 생성할 때 사용한다. 다음 예제는 데이터가 없는 파일을 생성한다.

sh> curl -i -X PUT "http://<HOST>:<PORT>/webhdfs/v1<PATH>?op=CREATE[&overwrite]=<true&#124;false>[&blocksize=<LONG>[&replication=<SHORT>][&permission=<OCTAL>][&buffersize=<INT>]"

이 기능은 네임 노드에 요청하지만 실제 데이터를 쓰기 위해 데이터 노드로 리다이렉트된다. 다음은 응답 데이터의 예다.

HTTP/1.1 307 TEMPORARY_REDIRECT Location: http://<DATANODE>:<PORT>/webhdfs/v1/<PATH>?op=CREATE... Content-Length: 0 / HTTP/1.1 201 Created Location: webhdfs: webhdfs: //<HOST>:<PORT>/<PATH> Content-Length: 0

다음은 실제 데이터 파일을 이용할 때다.

curl -i -X PUT -T <LOCAL_FILE> "http://<DATANODE>:<PORT>/webhdfs/v1/<PATH>?op=CREATE [&overwrite=<true&#124;false>][&blocksize=<LONG>[&replication=<SHORT>][&permission=<OCTAL>][&buffersize=<INT>]"


MKDIRS

MKDIRS는 폴더를 생성할 때 사용한다.

sh> curl -i -X PUT "http://<HOST>:<PORT>/webhdfs/v1/<PATH>?op=MKDIRS[&permission=<OCTAL>]"

사용자를 지정하지 않을 경우에는 기본 사용자인 webuser로 폴더가 생성된다. 다음은 응답 데이터의 예다.

HTTP/1.1 200 OK Content-Type: application/json Transfer-Encoding: chunked {"boolean" : true}


RENAME

RENAME은 폴더나 파일의 이름을 변경하거나 이동할 때 사용한다.

sh> curl -i -X PUT "<HOST>:<PORT>/webhdfs/v1/?op=RENAME&destination=<PATH>"

소유자와 접근 권한 정보는 그대로 유지된다. 다음은 응답 데이터의 예다.

HTTP/1.1 200 OK Content-Type: application/json Transfer-Encoding: chunked {"boolean": true}


SETREPLICATION

SETREPLICATION은 지정한 파일의 복제본 수를 지정할 때 사용한다.

sh> curl -i -X PUT "http://<HOST>:<POST>/webhdfs/v1/<PATH>?op=SETREPLICATION[&replication=<SHORT>]"

다음은 응답 데이터의 예다.

HTTP/1.1 200 OK Content-Type: application/json Transfer-Encoding: chunked {"boolean": true}


SETOWNER

SETOWNER는 지정한 폴더나 파일이 소유자를 설정할 때 사용한다.

sh> curl -i -X PUT "http://<HOST>:<PORT>/webhdfs/v1/<PATH>?op=SETOWNER[&owner=<USER>][&group=<GROUP>]"

다음은 응답 데이터의 예다.

HTTP/1.1 200 OK Content-Length: 0

응답은 HTTP Status OK다. 별도의 응답 데이터는 없다.


SETPERMISSION

SETPERMISSION은 지정한 폴더나 파일의 접근 권한을 변경할 때 사용한다.

sh> curl -i -X PUT "http://<HOST>:<PORT>/webhdfs/v1/<PATH>?op=SETPERMISSION[&permission=<OCTAL>]"

다음은 응답 데이터의 예다.

HTTP/1.1 200 OK Content-Length: 0

응답은 HTTP Status OK다. SETOWNER와 동일하게 별도 데이터 보디는 없다.


SETTIMES

SETTIMES는 폴더나 파일의 수정된 시간과 접근한 시간을 변경할 때 사용한다.

sh> curl -i -X PUT "http://<HOST>:<PORT>/webhdfs/v1/<PATH>?op=SETTIMES[&modificationtime=<TIME>[&accesstime=<TIME>]"

다음은 응답 데이터의 예다.

HTTP/1.1 200 OK Content-Length: 0

별도의 응답 데이터는 없다.


HTTP POST

APPEND

APPEND는 이미 존재하는 파일에 데이터를 추가할 때 사용한다. 하둡 분산 파일 시스템은 기본적으로 append 기능을 사용할 수 없도록 설정돼 있기 때문에 다음과 같이 hdfs-site.xml에 설정을 추가해야 한다.

- conf/hdfs-site.xml

...<property><name>dfs.support.append</name><value>true</value></property>... sh> curl -i -X POST -T <LOCAL_FILE> "http://<DATANODE>:<PORT>/webhdfs/v1/<PATH>?op=APPEND[&buffersize=<INT>]"

LOCAL_FILE은 추가할 데이터가 파일로 존재하면 그 파일의 위치를 지정한다. buffersize는 데이터를 전송할 때 사용하는 버퍼다.

다음은 응답 데이터의 예다.

HTTP/1.1 307 TEMPORARY_REDIRECT Location: http://<DATANODE>:<PORT>/webhdfs/v1<PATH>?op=APPEND... Content-Length: 0 HTTP/1.1 200 OK Content-Length: 0

데이터가 추가될 경우 하둡 네임 노드가 아닌 데이터 노드와 통신하기 때문에 데이터 노드로 리다이렉션된다.


HTTP DELETE

DELETE

DELETE는 폴더나 파일을 삭제할 때 사용한다.

sh> curl -i -X DELETE "http://<host>:<port>/webhdfs/v1/<path>?op=DELETE[&recursive=<true&#124;false>]"

recursive 옵션은 폴더를 지정하면 하위 폴더와 파일을 모두 삭제할 것인지를 선택할 때 사용한다. true로 설정하면 하위 폴더와 파일을 모두 삭제하고, false로 설정하면 하위에 폴더나 파일이 있으면 {“boolean”:false}로 응답한다.

다음은 응답 데이터의 예다.

HTTP/1.1 200 OK Content-Type: application/json Transfer-Encoding: chunked {"boolean": true}


소스코드

하둡 WebHDFS 서비스와 HttpClient를 이용해 다음과 같은 서비스를 만들 수 있다.

[그림 Ⅱ-1-11] 하둡 분산 파일 시스템을 이용한 스토리지 서비스

[그림 Ⅱ-1-11] 하둡 분산 파일 시스템을 이용한 스토리지 서비스

[HDFSFile.java]

package io; import java.io.File; import java.io.IOExecption; import java.io.Serializable; import java.net.HttpURLConnection; import java.util.ArrayList; import org.apache.http.HttpEntift; import org.apache.http.HttpResponse; import org.apache.http.client.ClientPortocolException; import org.apache.http.client.HttpClient;

import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.util.EntityUtils; import org.apache.log4j.Logger; import org.json.JSONArrary; import org.json.JSONException; import org.json.JSONObject; /** *HDFSFile 객체. java.io.File 객체를 상속받지는 않았지만, 동일한 기능을 수행할 수 있다. * *@author say501 *@version 0.0.1 *@since 2012 * */ public class HDFSFile implements Serializable{private static final long serialVersionUID = 1L; private static final Logger LOG = Logger.getLogger (HDFSFile.class); /** * 기본 파일 정보 * - filename: 파일 이름 *-type: 폴더 또는 파일 *-length: 파일인 경우 파일 크기 *-lastModified: 마지막으로 수정된 일자 *-fullpath: 파일 시스템 안에서 파일 전체 경로*/ private String filename; private String type; private long length; private long lastModified; private String fullpath; /** * 파일 정보를 위한 JSON 형식의 배열 */

private JSONArray fileStatus; /** * HDFS 기본 URL */ private final String dstDfs = "http://namenode.localdomain:5010/webhdfs/v1"; /** *HDFSFile 기본 생성자 * * @param fullpath 대상 파일 시스템의 절대 경로 */  public HDFSFile (String fullpath) {//원격 HDFS 절대 경로 this.filename = new File (fullpath).getName(); // 파일 형식 설정 this.type = "DIRECTORY"; //Http 클라이언트를 생성한다. HttpClient client = new DefaultHttpClient (); // Http GET 메소드 요청을 생성한다. HttpGet httpGet = new HttpGet (dstDfs + fullpath + "?op=LISTSTATUS"); try {//서버에 요청한다. HttpResponese response = client.execute (httpGet); // 요청 결과를 가져온다. int responseCode = response.getStatusLine().getStatusCode(); if (responseCode == httpURLConnection.HTTP_OK){// 응답 데이터를 가져온다.

HttpEntify entity = response.getEntity (); if (entity != null) {//응답 데이터를 문자열로 변환한다. String result = EntityUtils.toString (entity); try {//응답 데이터를 JSON 오브젝트 형식으로 변환한다. JSONObject json = new JSONObject(result); this.fileStatus = json.getJSONObject("FileStatuses").getJSONArray ("FileStatus");} catch (JSONException e){LOG.error(e);}}}}catch(ClientProtocolException e){LOG.error (e);} catch (IOExeption e){LOG.error (e);}} /** *HDFSFile 내부 생성자 * *@param filename 파일 이름 * @ param fullpath 파일 절대 경로*/ private HDFSFile (String filename, String fullpath, String type, long length, long)

lastModified){//파일 기본 정보 설정 this.filename = filename; this.fullpath = fullpath; this.type=type; this.length = length; this.lastModified = lastModified;} /** * 폴더를 지정하면 하위 폴더와 파일 목록을 가져온다. * * @return 하위 폴더와 파일 목록을 반환한다. */ public HDFSFile[] listFiles () {ArrayList<HDFSFile> result = new ArrayList<HDFSFile>(); for (int i = 0; i<this.fileStatus.length (); i++) {try {JSONObject obj = (JSONObject) this.fileStatus.get(i); HDFSFile file = new HDFSFile (obj.getString ("pathSuffix"), this.fullpath + "/" + obj.getString("pathSuffix"), obj.getString("type"), obj.getLong("length"), obj.getLong("modifiacationTime"));result.add (file);} catch (JSONException e) {LOG.error (e);}} return result.toArray (new HDFSFile[result.size()]);

} /** * 폴더를 지정하면 하위 폴더와 파일 목록을 필터를 적용해 가져온다. * *@param fileFilter 목록을 필터링할 필터 구현체 *@return 하위 폴더와 파일 목록을 반환한다. *@see #listFiles() */ public HDFSFile[] listFIles (HDFSFileFilter fileFilter) {ArrayList<HDFSFile> result = new ArrayList<HDFSFile> (); for (int i=0; i<this.fileStatus.length (); i++>){try {JSONObject obj = (JSONObject) this.fileStatus.get(i); HDFSFile file = new HDFSFile (obj.getString ("pathSuffix"), this.fullpath + "/" + obj.getString("pathSuffix"), obj.getString ("type"), obj.getLong("length"), obj.getLog("modificationTime")); if(fileFilter.accept (file)) {result.add (file);}}catch (JSONException e) {LOG.error (e);}} return result.toArray (new HDFSFile[result.size()]);}

/** * 파일인가? * * @return 파일일 경우 true를 반환한다. */ public boolean isFile() {return "FILE".equals (this.type);} /** * 폴더인가? * * @return 폴더일 경우 true를 반환한다. */ public boolean isDirectory () {return "DIRECTORY".equals (this.type);} /** *  폴더 또는 파일 이름을 가져온다. * * @return 폴더 또는 파일 이름을 반환한다. */ public String getName() {return this.filename;} /** * HDFS 내의 절대 경로를 가져온다. * * @ return 절대 경로를 반환한다. */ public String getAbsolutePath() {return this.fullpath;}

/** * 파일일 경우 파일 크기를 가져온다. 폴더일 경우에는 항상 "0"이다. * * @return 파일 크기를 반환한다. */ public long length() {return this.length;} /** * 폴더 또는 파일이 마지막으로 변경된 일자를 가져온다. * 폴더 또는 파일 이름 변경, 데이터 추가 등. * * @ return 마지막으로 변경된 일자를 반환한다. */ public long lastModified () {return this.lastModified;} [메소드 ... ]}

위의 예제는 java.io.File 객체와 유사하게 HDFSFile 객체를 구현한 것이다. 간단하게 java.io.File 객체를 이용한 로컬 파일 시스템용 스토리지와 하둡 분산 파일 시스템 용 스토리지를 손쉽게 변경해 사용할 수 있다. 단 사용자 인증은 하둡 시스템을 이용하지 않고 미들웨어에서 처리해야 한다.