Cloudera Data Engineering 1.24 (Private Cloud, ECS 기반) Virtual Cluster. Git 연동이 없고 PC 에 CDE CLI 를 설치할 수 없어 게이트웨이 노드의 cde 바이너리와 REST API 로만 접근한다.
| 이름 | 형태 | 용도 |
|---|---|---|
CDP 콘솔 (CDE_CDP_ENDPOINT) |
https://console-cdp.apps.<cluster>.<domain> |
액세스 키 방식 CLI 인증. CM → Data Services → Open CDP Private Cloud Data Services 로 열리는 URL |
VC 엔드포인트 (config.yaml 의 vcluster-endpoint) |
https://<vc-id>.cde-<hash>.apps.<domain>/dex/api/v1 |
Job · Resource · Run API |
| Service 엔드포인트 | https://service.cde-<hash>.apps.<domain> |
Knox 토큰 발급. CDE UI → Cluster Details → GRAFANA CHARTS 링크의 호스트명 |
토큰 URL 은 Service 엔드포인트에 /gateway/authtkn/knoxtoken/api/v1/token 을 붙인 것이다. 응답의 access_token 은 30 분 유효한 Bearer 토큰이다.
export CDE_TOKEN=$(curl -k -s -u <workload_user> \
"https://service.cde-<hash>.<domain>/gateway/authtkn/knoxtoken/api/v1/token" \
| python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")
export CDE_JOB_URL="https://<vc-endpoint>/dex/api/v1"
curl -k -s -H "Authorization: Bearer ${CDE_TOKEN}" "${CDE_JOB_URL}/jobs" | python3 -m json.tool
curl -k -s -H "Authorization: Bearer ${CDE_TOKEN}" -X POST "${CDE_JOB_URL}/jobs/<job-name>/run" -H "Content-Type: application/json"
jq 가 없는 서버에서는 위처럼 python3 -c 로 대신한다.
Kerberos 티켓으로 curl --negotiate -u : 를 시도했으나 응답 헤더가 www-authenticate: BASIC realm="application" 이었다. 이 환경의 authtkn 토폴로지는 ShiroProvider(ID/PW) 만 켜져 있어 SPNEGO 로는 발급되지 않는다.
curl 은 -n 옵션이 있으면 홈 디렉터리의 .netrc 를 읽는다. OS 계정별로 분리되므로 전용 서비스 계정 홈에 만들고 chmod 600 한다. machine 값은 URL 의 호스트명과 정확히 같아야 한다.
machine service.cde-<hash>.<domain>
login <workload_user>
password ${WORKLOAD_PASSWORD}
chmod 600 ~/.netrc
curl -k -s -n "https://service.cde-<hash>.<domain>/gateway/authtkn/knoxtoken/api/v1/token"
401 이 계속 나면 -v 로 Authorization: Basic 헤더가 나가는지 보고, -u user:pass 직접 지정으로 자격증명 자체가 맞는지 분리해 확인한다.
NiFi 등 셸에서 호출할 때는 비밀번호를 jceks 에 넣고 작은 Java 클래스로 읽는다. hadoop credential 명령에는 값을 읽는 하위 명령이 없어 직접 만든다.
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.security.alias.CredentialProviderFactory;
public class ReadCred {
public static void main(String[] args) throws Exception {
if (args.length < 2) { System.err.println("Usage: ReadCred <alias> <provider>"); System.exit(1); }
Configuration conf = new Configuration();
conf.set(CredentialProviderFactory.CREDENTIAL_PROVIDER_PATH, args[1]);
char[] credential = ${MASKED});
if (credential == null) { System.err.println("alias not found: " + args[0]); System.exit(1); }
System.out.println(new String(credential));
}
}
JCEKS_DIR=/etc/security/credentials
JCEKS_PROVIDER="jceks://file${JCEKS_DIR}/cde_credentials.jceks"
CLASS_DIR=/opt/hadoop-tools/jceks
mkdir -p ${JCEKS_DIR} ${CLASS_DIR}; chmod 700 ${JCEKS_DIR}
hadoop credential create cde.password -provider ${JCEKS_PROVIDER} # 값은 프롬프트로 입력
hadoop credential create cde.user -value etl_svc -provider ${JCEKS_PROVIDER}
hadoop credential create cde.knox.url -value "https://service.cde-<hash>.<domain>/gateway/authtkn/knoxtoken/api/v1/token" -provider ${JCEKS_PROVIDER}
hadoop credential list -provider ${JCEKS_PROVIDER}
javac -source 1.8 -target 1.8 -cp "$(hadoop classpath)" -d ${CLASS_DIR} ReadCred.java
java -cp "${CLASS_DIR}:$(hadoop classpath)" ReadCred cde.user ${JCEKS_PROVIDER}
javac 가 없는 서버에서는 다른 서버에서 컴파일한 ReadCred.class 와 jceks 파일을 복사해 쓴다. Hadoop 이 Java 8 이면 -source 1.8 -target 1.8 로 컴파일해야 UnsupportedClassVersionError 가 나지 않는다. jceks 파일은 chmod 400 으로 둔다.
JOB_NAME=$1
ETL_USER=$(java -cp "${CLASS_DIR}:$(hadoop classpath)" ReadCred cde.user ${JCEKS_PROVIDER} 2>/dev/null)
ETL_PASS=$(java -cp "${CLASS_DIR}:$(hadoop classpath)" ReadCred cde.password ${JCEKS_PROVIDER} 2>/dev/null)
CDE_TOKEN_BASE_URL=$(java -cp "${CLASS_DIR}:$(hadoop classpath)" ReadCred cde.knox.url ${JCEKS_PROVIDER} 2>/dev/null)
CDE_TOKEN=$(curl -k -s -u "${ETL_USER}:${ETL_PASS}" "${CDE_TOKEN_BASE_URL}" \
| python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")
unset ETL_PASS
~/.cde/config.yaml 에 vcluster-endpoint · user · tls-insecure 를 두면 실행 때마다 비밀번호를 묻는다. 비밀번호 입력을 없애려면 CDP 콘솔에서 발급한 액세스 키를 쓴다.
export CDE_CDP_ENDPOINT=https://console-cdp.apps.<cluster>.<domain>
export CDE_CDP_ACCESS_KEY_ID=${ACCESS_KEY_ID}
export CDE_CDP_PRIVATE_KEY=${PRIVATE_KEY}
cde job list
로그 확인은 다음 명령으로 한다.
cde run list --filter 'job[eq]<job-name>'
cde run logs --id <RUN_ID> --type driver/stdout
cde job create --name my_job --type spark --application-file app.py --log-level DEBUG
curl -k -H "Authorization: Bearer ${CDE_TOKEN}" "${CDE_JOB_URL}/job-runs/<RUN_ID>/logs?type=driver/stdout"