Cloudera Data Engineering(CDE) 을 운영하면서 겪은 세 가지 사안을 모았다. Spark 잡 실패 번들에서 실제 원인을 가려내는 방법, CDE Airflow 에서 동시 요청을 하나씩 처리하도록 제한하는 방법, CDE 내장 이메일 알림과 메일 서버로 나가는 트래픽 감사 방법이다.
CDE 잡 실행 번들(cde-job-run-*.zip) 에서 submitter_jobs_api.log 의 exitCode: 1, reason: "Error" 는 spark-container 종료 사실만 알려 준다. 직접 원인은 드라이버 로그 마지막의 파이썬 예외에 있다. 익스큐터에서 전 stage 가 Finished task ... result sent to driver 로 끝났고 셔플·JDBC·Python 워커에 실패 기록이 없으면 분산 처리는 성공했고 애플리케이션 코드가 마지막 검증 단계에서 의도적으로 RuntimeError 를 던진 것이다. 이때 처리 루프의 시작 로그 자체가 없는 항목은 예외 처리 실패가 아니라 순회 대상에서 빠진 것이고, 처리 순서가 뒤섞여 있으면 DataFrame 순서가 아니라 별도 컬렉션(groupby, dict, set) 을 기준으로 순회하고 있다는 신호다. 데이터 0건일 때의 처리 정책(NODATA 상태 코드로 DONE 처리, 빈 결과물 생성, 최소한 [스킵] 사유 로그) 을 코드에 정의해야 "조용히 사라진 뒤 마지막에 실패" 하지 않는다.
CDE 로그에 흔히 보이지만 무시해도 되는 경고는 다음과 같다.
| 메시지 | 성격 |
|---|---|
StandbyException: Operation category READ is not supported in state standby |
NameNode HA 에서 standby 에 먼저 붙었다가 active 로 자동 재시도. 정상 |
HiveServer2CredentialProvider: Failed to get HS2 delegation token |
HS2 JDBC 미사용이면 무해. HMS 토큰은 정상 발급 |
ERROR StatusLogger Reconfiguration failed |
log4j2 초기화 노이즈 |
Ignoring non-Spark config property: dex.safariEnabled / cde.iceberg.enabled |
CDE 전용 프로퍼티 |
NativeCodeLoader / ProcfsMetricsGetter / DomainSocketFactory |
컨테이너 환경 표준 경고 |
CDE Airflow 는 관리형이라 airflow.cfg 를 직접 고칠 수 없으므로 Pool 과 DAG 파라미터로 직렬화한다. 여러 DAG 가 동시에 몰리면 slots=1 인 Pool 이 전역으로 하나씩 돌게 하고, 한 DAG 안에서 태스크가 병렬로 터지면 DAG 단위 제한을 건다.
CDEJobRunOperator(
task_id="job_1",
job_name="job_1",
pool="cde_serial",
priority_weight=10,
)
with DAG(
dag_id="cde_serial_dag",
max_active_runs=1,
max_active_tasks=1,
catchup=False,
default_args={"depends_on_past": True},
):
...
from airflow.models.baseoperator import chain
chain(job_1, job_2, job_3)
Pool 은 Airflow UI → Admin → Pools 에서 cde_serial 을 slots=1 로 만든다. 특정 태스크 하나만 중복을 막으려면 max_active_tis_per_dag=1 을 그 태스크에 지정한다. 제출 자체가 429/타임아웃으로 실패하면 retries, retry_delay, retry_exponential_backoff=True 를 함께 건다. Virtual Cluster 의 동시 실행 한도·오토스케일도 같이 확인한다.
CDE 는 잡 단위 이메일 알림을 내장한다. Virtual Cluster 생성 마법사에서 Configure Email Alerting 으로 Sender Email Address 와 SMTP Host 를 넣고 Test SMTP Configs 로 검증한다. 이 옵션은 VC 를 만들 때만 설정할 수 있고 나중에 켜거나 끌 수 없으므로, 기존 VC 에 알림이 보이지 않으면 재생성이 필요하다. 잡 생성 화면의 Alerts 에서 Job Failure 와 Job SLA Miss 를 선택하고 수신자를 여러 개 넣을 수 있다. 대안으로 Cloudera Observability 의 Workload View 알림, 또는 DAG 의 EmailOperator / on_failure_callback 이 있으며, SMTP 인증정보는 Airflow Connection/Secret 으로 관리한다.
알림이 오지 않을 때는 SMTP 설정보다 아웃바운드 경로 차단이 원인인 경우가 많다. 발송 출발지는 VC 네임스페이스(VC ID) 의 runtime-api-server 파드이므로 그 안에서 DNS·TCP·TLS 를 확인한다.
NS=<vc-id>
POD=$(kubectl -n $NS get pod -l app=runtime-api-server -o jsonpath='{.items[0].metadata.name}')
kubectl -n $NS exec $POD -- getent hosts smtp.example.com
kubectl -n $NS exec $POD -- timeout 5 bash -c 'cat < /dev/null > /dev/tcp/smtp.example.com/587 && echo OPEN || echo BLOCKED'
kubectl -n $NS exec $POD -- openssl s_client -starttls smtp -connect smtp.example.com:587 -brief
kubectl -n $NS logs $POD --since=30m | grep -i -E 'smtp|mail|alert'
퍼블릭 클라우드는 25번 포트를 기본 차단하는 경우가 많으므로 587(STARTTLS) 또는 465(SMTPS) 를 쓰고, 메일 서버 릴레이 허용 목록에 NAT GW 나 워커 노드 출발지 IP 가 있는지 확인한다.
설정한 알림 외에 1분마다 메일 서버를 찌르는 트래픽이 있을 때, 메일 서버 협조 없이 ECS 노드에서 출발지를 특정하는 방법이다. -s 128 로 헤더만 저장하면 메일 본문과 SMTP AUTH 정보가 캡처 파일에 남지 않는다.
sudo tcpdump -nn -i any "host <mail-server-ip> and port 25 and tcp[tcpflags] & tcp-syn != 0 and tcp[tcpflags] & tcp-ack = 0" -s 128
sudo tcpdump -nn -i any "dst port 25" -s 128
kubectl get pod -A -o wide | grep <출발지IP>
sudo timeout 120 bash -c 'while :; do ss -tnp state all "dst <mail-server-ip>"; sleep 1; done' | grep -v '^State'
sudo cat /proc/<PID>/cgroup
sudo cat /proc/<PID>/cmdline | tr '\0' ' '
kubectl get pod -A -o jsonpath='{range .items[*]}{.metadata.uid}{"\t"}{.metadata.namespace}{"/"}{.metadata.name}{"\n"}{end}' | grep <podUID>
출발지 IP 가 파드 CIDR 이면 컨테이너, 노드 IP 면 호스트 프로세스(cron, CM agent 등) 다. 타임스탬프 초 단위가 매분 같으면 cron 성 발신이다. CDP 환경에서 "설정한 적 없는" 발신 주체로는 Cloudera Manager Alert Publisher, Airflow smtp 섹션의 email_on_failure / email_on_retry 기본값, Alertmanager 계열, 다른 VC 의 알림 설정이 흔하다. 상시 감시는 VPC Flow Logs 나 egress 방화벽 세션 로그, 노드 레벨 conntrack -E -p tcp --dst <ip> --event-mask NEW -o timestamp 가 패킷 캡처보다 부담이 적다.