CAS(Cloud Analytic Services) 는 SAS Viya 의 인메모리 분석 엔진이다. 컨트롤러 한 대와 워커 여러 대로 구성되며 MPP 모드에서는 데이터를 워커에 분산해 둔다. Viya 3.5 는 호스트에 직접 설치되고 Viya 4 는 Kubernetes 위에 CAS Operator 가 배포하는 방식이라 같은 증상이라도 확인할 곳이 다르다.
cas-shared-default 가 뜨지 않는데 operator 와 controller 파드는 살아 있는 상황이다. operator 로그를 본다.
kubectl logs -n <viya-namespace> deploy/sas-cas-operator --tail=200
kubectl get casdeployment -n <viya-namespace>
kubectl describe casdeployment default -n <viya-namespace>
로그에 Reconciling CASDeployment 만 반복되고 진전이 없다면 operator 가 원하는 상태에 도달하지 못하는 것이다. 다음 두 줄이 자주 함께 나온다.
currently received list of cluster nodes [error: number nodes=0]
Waited for x.xxxxs due to client-side throttling, not priority and fairness
number nodes=0 은 operator 가 배치 대상 노드를 하나도 못 찾았다는 뜻이다. CAS 는 보통 전용 노드 풀에 레이블과 테인트로 고정해 두므로, 그 레이블을 가진 노드가 실제로 존재하고 Ready 인지 확인한다. 노드 풀을 교체했거나 오토스케일러가 노드를 줄인 뒤 이 증상이 나타나는 경우가 많다.
kubectl get nodes -L workload.sas.com/class
kubectl get nodes -o json | jq -r '.items[].spec.taints'
client-side throttling 은 operator 가 API 서버에 너무 많은 요청을 보내 클라이언트 쪽에서 스스로 속도를 낮추고 있다는 뜻이다. 그 자체가 원인이라기보다 reconcile 이 반복되면서 생긴 결과인 경우가 많으므로, 위의 노드 문제를 먼저 해결한다.
파드는 떴는데 CAS 세션이 만들어지지 않으면 자원 부족을 본다. CAS 워커는 요청 메모리가 크므로 노드에 여유가 없으면 Pending 에서 멈춘다.
kubectl get pods -n <viya-namespace> -l app.kubernetes.io/name=sas-cas-server -o wide
kubectl describe pod -n <viya-namespace> <cas-worker-pod> | sed -n '/Events/,$p'
MPP 구성이라고 해서 워커가 노드마다 하나씩 자동으로 배치되는 것은 아니다. CAS 워커는 일반적인 Kubernetes 워크로드이므로 기본적으로는 스케줄러가 알아서 배치하고, 자원이 넉넉하면 한 노드에 워커 여러 개가 몰릴 수 있다. 워커를 노드에 하나씩 펼치고 싶으면 명시적으로 제약을 건다.
podAntiAffinity 로 같은 노드에 두 개가 뜨지 못하게 한다.
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
casoperator.sas.com/node-type: worker
topologyKey: kubernetes.io/hostname
topologySpreadConstraints 로 노드 간 균등 분산을 유도할 수도 있다. requiredDuringScheduling 은 조건을 만족하지 못하면 아예 배치하지 않으므로, 노드 수가 워커 수보다 적으면 Pending 이 된다. 그 경우 whenUnsatisfiable: ScheduleAnyway 쪽이 안전하다.
전용 노드 풀에만 올리려면 노드에 레이블을 붙이고 nodeSelector 또는 nodeAffinity 로 고정한다. 이런 변경은 배포 매니페스트를 직접 고치는 것이 아니라 Viya 배포의 kustomize overlay(site-config/ 아래)에 패치로 두어야 다음 배포에서 덮이지 않는다.
SAS Studio 에서 CAS 테이블 미리보기가 실패하고 TK KERNEL 오류 — Could not create connection to read table from CAS 가 나오는 경우다. Compute 세션이 CAS 에 연결을 만들지 못한 상태이므로 아래 순서로 좁힌다.
CAS 프로세스와 포트부터 본다. 기본 포트는 바이너리 5570, REST 8777 이다.
sudo /etc/init.d/sas-viya-cascontroller-default status
sudo /etc/init.d/sas-viya-casworker-default status
sudo ss -tnlp | grep -E '5570|5571|8777'
포트가 열려 있으면 세션 쪽을 본다. 세션이 만료됐거나 caslib 할당이 풀린 상태일 수 있다.
options cashost="cas-controller.example.internal" casport=5570;
cas _all_ terminate;
cas mysess;
proc cas; session.status; quit;
caslib _all_ assign;
노드를 교체해 IP 가 바뀌었다면 /etc/hosts 와 DNS 를 다시 확인한다. CAS 는 컨트롤러와 워커가 서로를 이름으로 찾으므로 이름 해석이 어긋나면 세션 생성 단계에서 실패한다.
The operation failed because another node requested that all operation on this communicator fail
MPP 클러스터에서 한 노드가 실패하면 나머지 노드가 그 연산 전체를 중단한다. 즉 이 메시지는 원인이 아니라 결과다. 실제로 죽은 노드를 찾아야 한다.
워커 로그에서 먼저 실패한 노드를 찾고, 그 노드에서 OOM 여부를 본다. CAS 는 메모리를 크게 쓰므로 커널 OOM killer 에 죽는 경우가 가장 흔하다.
dmesg -T | grep -i -E 'oom|killed process'
journalctl -u sas-viya-casworker-default --since '-1h'
Viya 4 라면 파드의 종료 사유를 본다.
kubectl get pod -n <viya-namespace> <cas-worker-pod> \
-o jsonpath='{.status.containerStatuses[*].lastState.terminated.reason}{"\n"}'
OOMKilled 면 메모리 요청·한도를 올리거나 데이터를 나눠 처리한다. 노드 간 네트워크가 끊기는 경우라면 방화벽과 MTU 를 함께 본다 — MPP 노드 사이 통신은 대용량 전송이라 MTU 불일치에 민감하다.
CAS 용 PV 가 빠르게 차는 것은 대개 정상적인 동작의 누적이다. 이 볼륨에는 인메모리 테이블의 디스크 사본, caslib 에 저장(persist)한 테이블, spill 파일, 백업·스냅샷이 함께 쌓인다. CAS 는 이것들을 자동으로 정리하지 않는다.
무엇이 남아 있는지부터 확인한다.
proc cas;
table.tableInfo;
run;
du -sh /path/to/cas-pv/* | sort -rh | head -20
주된 누적 원인은 네 가지다. promote=yes 로 올려 둔 테이블이 세션을 넘어 남는 것, 세션이 비정상 종료돼 임시 테이블이 정리되지 않은 것, 한 번 돌려 본 백업이 그대로 남은 것, 그리고 같은 데이터를 사용자마다 따로 올려 중복 적재된 것이다.
정리는 테이블 단위로 한다.
proc cas;
table.dropTable / caslib="CASUSER" name="tablename" quiet=true;
run;
파일 시스템에서 직접 지우는 것은 CAS 가 도는 중에는 하지 않는다. 메타데이터와 어긋나 이후 조회가 실패한다. 근본적으로는 세션 종료 시 임시 테이블이 정리되도록 운영 규칙을 세우고, 승격 테이블에는 보존 기간을 정해 주기적으로 걷어내는 편이 낫다.