逐个停掉组件:哪些停了,哪些撑住了
한국어 원문으로 표시합니다.
목표
진짜 Cilium 1.20.1 에서 cilium agent·operator·cilium-envoy·hubble-relay 가 각각 무엇을 맡는지, 하나씩 내려 보고 확인합니다. 먼저 cluster-pool IPAM 과 CiliumIdentity 가 어디에 기록되는지 읽고, 구성요소가 없는 동안 새 파드·L7 요청·흐름 조회·기존 트래픽이 어떻게 되는지를 기록한 뒤 되돌립니다.
왜 중요한가
"Cilium 이 죽었다" 는 신고는 어느 구성요소가 멈췄느냐에 따라 전혀 다른 사고입니다. 오퍼레이터는 클러스터에 한 번 하면 되는 일을 맡아 잠시 없어도 전달·정책 결정에 끼어들지 않습니다. envoy 가 없으면 L7 규칙이 걸린 경로만 멈춥니다. relay 가 없으면 여러 노드를 한곳에서 조회하는 길만 끊깁니다. 에이전트가 없으면 이미 적재된 데이터패스는 계속 돌지만 새 파드는 네트워크를 받을 수 없어 스케줄이 막힙니다.
구성요소를 내리는 단계는 관측을 기록한 뒤 반드시 되돌립니다(에이전트만 6단계에서 내리고 7단계에서 되돌립니다). 복구 뒤 전체 채점은 그때의 기록을 파드 uid·신원·흐름 시각·복구된 파드의 생성 시각과 대조하므로, 기록은 반드시 내려간 동안 만드세요.
환경 준비에 약 5분이 걸립니다. 6단계와 7단계 사이에는 에이전트가 없어 에이전트 명령이 동작하지 않습니다. 세션이 끝나면 /root/cca-parts 의 파일은 사라집니다.
단계
kubectl apply -f /opt/fixtures/cca-components/workload.yaml로 cca-parts 에 app(레플리카 2)·api-l7·client 를 띄우세요./root/cca-parts/ipam.json에 기록합니다 —ipam_mode·cluster_pool·mask_size(cilium-config 의 ipam, cluster-pool-ipv4-cidr, cluster-pool-ipv4-mask-size 는 숫자),node_pod_cidrs(CiliumNode 의 spec.ipam.podCIDRs 목록),status_pool·capacity(에이전트cilium-dbg status의 IPAM 줄에서 allocated from 뒤의 대역과 / 뒤의 숫자),router_ip(VM 의 cilium_host 장치 IPv4),pods(cca-parts 파드 4개의 이름 → IP 사전).- app 레플리카 두 파드와 client 의 CiliumEndpoint 신원 번호를 비교하고, app 의 CiliumIdentity 객체를 읽으세요.
/root/cca-parts/identity.json에app_identity,app_endpoints(app 파드 이름 2개),client_identity,security_labels(그 CiliumIdentity 의 security-labels 를키=값문자열로 정렬한 목록),allocation_mode(cilium-config 의 identity-allocation-mode) 를 기록합니다. - cilium-operator 를 replicas 0 으로 내리고 오퍼레이터 파드가 모두 사라질 때까지 기다리세요. 그 상태에서 cca-parts 에 파드
born-no-operator(이미지 client 와 같은 curl, 라벨born=no-operator, 명령 sleep 86400)를 만들고 Ready 가 되면/root/cca-parts/operator-down.json에operator_replicas(그때 Deployment 의 spec.replicas, 숫자),pod_uid,pod_ip,identity(그 파드의 CiliumEndpoint 신원) 를 기록합니다. 기록한 뒤 오퍼레이터를 1 로 되돌리고 available 이 될 때까지 기다립니다. - cca-parts 에 CiliumNetworkPolicy
l7-get을 만드세요 — endpointSelector app=api-l7, ingress 한 항목: fromEndpoints app=client, toPorts 에 TCP "8080" 과 rules.http[{method: GET, path: /allowed}]. client 에서 api-l7 의 /allowed 가 200, /secret 이 403 인 것을 확인한 뒤, kube-system 의 cilium-envoy DaemonSet 에 존재하지 않는 nodeSelectorcca-lab/envoy: "off"를 더해 envoy 파드를 치웁니다. 그동안 client 에서 api-l7 /allowed(5초 제한)와 app 서비스(http://app:8080/)를 요청하고 에이전트 안 hubble 로 client→api-l7 흐름을 읽어/root/cca-parts/envoy-down.json에l7_code,plain_code(HTTP 코드 문자열, 응답 없음은 "000"),flows(client→api-l7 흐름 compact 줄 목록) 를 기록합니다. 그다음 그 nodeSelector 키를 제거해 envoy 를 되돌리고 /allowed 가 다시 200 이 될 때까지 기다립니다. - kube-system 의 hubble-relay 를 replicas 0 으로 내리세요. VM 셸에서
hubble status --server <hubble-relay 서비스 ClusterIP>:80이 실패하는 오류 한 줄을, 에이전트 안에서hubble observe --last 5 -o compact의 흐름 줄과hubble status의 Current/Max Flows 줄을 읽어/root/cca-parts/relay-down.json에relay_error,local_flows(목록),local_status로 기록합니다. 그다음 relay 를 1 로 되돌리고 릴레이 경유 status 가 성공할 때까지 기다립니다. - kube-system 의 cilium DaemonSet 에 nodeSelector
cca-lab/agent: "off"를 더해 에이전트 파드를 치우세요(이 단계에서는 되돌리지 않습니다). 파드가 사라지면 노드 taint 를 확인하고, client 에서 app 서비스를 요청한 뒤, cca-parts 에 curl 파드orphan(명령 sleep 86400)을 만들어 FailedScheduling 이벤트를 기다립니다./root/cca-parts/agent-down.json에existing_code(client→app 코드 문자열),taint(노드에 걸린 cilium taint 의 key:effect),orphan_uid,orphan_phase,reason·message(orphan 의 FailedScheduling 이벤트) 를 기록합니다. - cilium DaemonSet 에서 nodeSelector 키
cca-lab/agent를 제거해 에이전트를 되돌리세요. 에이전트가 Ready 가 되고 노드의 agent-not-ready taint 가 사라지며 orphan 이 스케줄(PodScheduled=True)될 때까지 폴링한 뒤/root/cca-parts/restore.json에taint_removed(true/false),orphan_scheduled_at(orphan 의 PodScheduled 조건 lastTransitionTime),agent_pod(새 에이전트 파드 이름) 를 기록합니다. /root/cca-parts/report.txt에키=값여덟 줄을 씁니다 —ipam_mode,node_pod_cidr,identity_allocation_mode,without_operator_new_pod(born-no-operator 의 지금 phase),without_envoy_l7·without_envoy_plain(envoy-down.json 의 두 코드),without_agent_existing·without_agent_new_pod(agent-down.json 의 existing_code 와 orphan_phase). 값은 기록 파일·지금 설정과 일치해야 합니다.
참고
- 구성요소 개요(agent·operator·Hubble relay): https://docs.cilium.io/en/v1.20/overview/component-overview/
- Cilium Operator 의 역할과 없을 때 생기는 일: https://docs.cilium.io/en/v1.20/internals/cilium_operator/
- Cluster Scope IPAM(cluster-pool): https://docs.cilium.io/en/v1.20/network/concepts/ipam/cluster-pool/
- Envoy DaemonSet 모드: https://docs.cilium.io/en/v1.20/security/network/proxy/envoy/
- 노드 taint 와 관리되지 않는 파드: https://docs.cilium.io/en/v1.20/installation/taints/
- 에이전트 명령:
kubectl -n kube-system exec ds/cilium -c cilium-agent -- cilium-dbg status/hubble observe.
파드 주소는 어디서 잘라 온 것인가
kubectl apply -f /opt/fixtures/cca-components/workload.yaml 로 cca-parts 에 app(레플리카 2)·api-l7·client 를 띄우세요. /root/cca-parts/ipam.json 에 기록합니다 — ipam_mode·cluster_pool·mask_size(cilium-config 의 ipam, cluster-pool-ipv4-cidr, cluster-pool-ipv4-mask-size 는 숫자), node_pod_cidrs(CiliumNode 의 spec.ipam.podCIDRs 목록), status_pool·capacity(에이전트 cilium-dbg status 의 IPAM 줄에서 allocated from 뒤의 대역과 / 뒤의 숫자), router_ip(VM 의 cilium_host 장치 IPv4), pods(cca-parts 파드 4개의 이름 → IP 사전).
cluster-pool 모드에서는 클러스터 전체 풀을 노드마다 일정 크기로 잘라 CiliumNode 에 적어 두고, 각 노드의 에이전트가 그 조각 안에서 파드 주소를 줍니다. 풀·조각·실제 주소가 서로 포함 관계인지 확인하세요. 노드의 게이트웨이 역할을 하는 cilium_host 도 같은 조각에서 주소를 받습니다.
레플리카 둘은 신원 하나를 나눠 쓴다
app 레플리카 두 파드와 client 의 CiliumEndpoint 신원 번호를 비교하고, app 의 CiliumIdentity 객체를 읽으세요. /root/cca-parts/identity.json 에 app_identity, app_endpoints(app 파드 이름 2개), client_identity, security_labels(그 CiliumIdentity 의 security-labels 를 키=값 문자열로 정렬한 목록), allocation_mode(cilium-config 의 identity-allocation-mode) 를 기록합니다.
신원은 파드마다가 아니라 보안 관련 라벨 집합마다 하나입니다. crd 할당 방식에서는 그 집합이 CiliumIdentity 라는 클러스터 범위 객체로 저장되고 이름이 곧 번호입니다. 파드 이름에 붙는 해시 같은 값은 보안 라벨에 들어가지 않는다는 점을 확인해 보세요.
오퍼레이터가 없는 동안 태어난 파드
cilium-operator 를 replicas 0 으로 내리고 오퍼레이터 파드가 모두 사라질 때까지 기다리세요. 그 상태에서 cca-parts 에 파드 born-no-operator(이미지 client 와 같은 curl, 라벨 born=no-operator, 명령 sleep 86400)를 만들고 Ready 가 되면 /root/cca-parts/operator-down.json 에 operator_replicas(그때 Deployment 의 spec.replicas, 숫자), pod_uid, pod_ip, identity(그 파드의 CiliumEndpoint 신원) 를 기록합니다. 기록한 뒤 오퍼레이터를 1 로 되돌리고 available 이 될 때까지 기다립니다.
공식 문서는 오퍼레이터를 "노드마다가 아니라 클러스터에 한 번 하면 되는 일" 을 맡는 구성요소로, 전달이나 정책 결정의 경로에 있지 않다고 설명합니다. 이미 노드에 podCIDR 조각이 있으면 주소는 누가 주는지, 처음 보는 라벨 조합의 신원 객체는 누가 만드는지 관측해 보세요. 새 신원 번호로 kubectl get ciliumidentity 를 확인하세요.
envoy 를 치웠더니 L7 경로만 멈췄다
cca-parts 에 CiliumNetworkPolicy l7-get 을 만드세요 — endpointSelector app=api-l7, ingress 한 항목: fromEndpoints app=client, toPorts 에 TCP "8080" 과 rules.http [{method: GET, path: /allowed}]. client 에서 api-l7 의 /allowed 가 200, /secret 이 403 인 것을 확인한 뒤, kube-system 의 cilium-envoy DaemonSet 에 존재하지 않는 nodeSelector cca-lab/envoy: "off" 를 더해 envoy 파드를 치웁니다. 그동안 client 에서 api-l7 /allowed(5초 제한)와 app 서비스(http://app:8080/)를 요청하고 에이전트 안 hubble 로 client→api-l7 흐름을 읽어 /root/cca-parts/envoy-down.json 에 l7_code, plain_code(HTTP 코드 문자열, 응답 없음은 "000"), flows(client→api-l7 흐름 compact 줄 목록) 를 기록합니다. 그다음 그 nodeSelector 키를 제거해 envoy 를 되돌리고 /allowed 가 다시 200 이 될 때까지 기다립니다.
사이드카가 없는 Cilium 에서 HTTP 규칙은 노드의 envoy 가 판정합니다. eBPF 는 L3/L4 판정 뒤 그 연결을 프록시로 넘기는데, 받을 envoy 가 없으면 어떻게 될까요. L7 규칙이 없는 서비스는 프록시를 거치지 않습니다. nodeSelector 키에 / 가 있으면 JSON patch 경로에서 ~1 로 적습니다. 흐름은 hubble observe --since <시각> --namespace cca-parts -o compact 로 읽으세요.
릴레이가 죽어도 노드는 흐름을 모으고 있었다
kube-system 의 hubble-relay 를 replicas 0 으로 내리세요. VM 셸에서 hubble status --server <hubble-relay 서비스 ClusterIP>:80 이 실패하는 오류 한 줄을, 에이전트 안에서 hubble observe --last 5 -o compact 의 흐름 줄과 hubble status 의 Current/Max Flows 줄을 읽어 /root/cca-parts/relay-down.json 에 relay_error, local_flows(목록), local_status 로 기록합니다. 그다음 relay 를 1 로 되돌리고 릴레이 경유 status 가 성공할 때까지 기다립니다.
Hubble 은 에이전트마다 흐름을 링 버퍼에 모으고, relay 는 여러 노드의 버퍼를 한곳에서 질의하게 해 주는 중계자입니다. 둘 중 무엇이 멈추면 무엇을 잃는지 비교하세요. 실패한 명령의 오류는 표준 오류로 나오므로 2>&1 로 받습니다.
에이전트를 치웠는데 기존 요청은 계속 된다
kube-system 의 cilium DaemonSet 에 nodeSelector cca-lab/agent: "off" 를 더해 에이전트 파드를 치우세요(이 단계에서는 되돌리지 않습니다). 파드가 사라지면 노드 taint 를 확인하고, client 에서 app 서비스를 요청한 뒤, cca-parts 에 curl 파드 orphan(명령 sleep 86400)을 만들어 FailedScheduling 이벤트를 기다립니다. /root/cca-parts/agent-down.json 에 existing_code(client→app 코드 문자열), taint(노드에 걸린 cilium taint 의 key:effect), orphan_uid, orphan_phase, reason·message(orphan 의 FailedScheduling 이벤트) 를 기록합니다.
에이전트는 BPF 프로그램과 맵을 적재하고 갱신하는 쪽이지 패킷을 직접 나르는 프로세스가 아닙니다. 에이전트가 없는 동안 이미 적재된 것은 어떻게 될까요. 새 파드는 CNI 가 네트워크를 붙일 수 없으니 오퍼레이터가 노드에 taint 를 걸어 스케줄을 막습니다. 이벤트는 kubectl get events --field-selector involvedObject.name=orphan 으로 봅니다.
에이전트가 돌아오자 taint 가 풀린다
cilium DaemonSet 에서 nodeSelector 키 cca-lab/agent 를 제거해 에이전트를 되돌리세요. 에이전트가 Ready 가 되고 노드의 agent-not-ready taint 가 사라지며 orphan 이 스케줄(PodScheduled=True)될 때까지 폴링한 뒤 /root/cca-parts/restore.json 에 taint_removed(true/false), orphan_scheduled_at(orphan 의 PodScheduled 조건 lastTransitionTime), agent_pod(새 에이전트 파드 이름) 를 기록합니다.
taint 를 거는 것도 푸는 것도 오퍼레이터가 에이전트 파드 상태를 보고 합니다. 스케줄된 뒤 컨테이너가 Running 이 되기까지는 더 걸릴 수 있으니(샌드박스 재시도) 이 단계는 스케줄까지만 기다리세요. 에이전트가 바뀐 직후에는 hubble-relay 도 피어에 다시 붙는 동안 잠시 Ready 가 풀립니다. 마지막 전체 채점 전에 orphan 이 Running 인지도 한 번 확인해 보세요.
누가 멈추면 무엇이 멈추는가
/root/cca-parts/report.txt 에 키=값 여덟 줄을 씁니다 — ipam_mode, node_pod_cidr, identity_allocation_mode, without_operator_new_pod(born-no-operator 의 지금 phase), without_envoy_l7·without_envoy_plain(envoy-down.json 의 두 코드), without_agent_existing·without_agent_new_pod(agent-down.json 의 existing_code 와 orphan_phase). 값은 기록 파일·지금 설정과 일치해야 합니다.
보고서는 구성요소마다 "없는 동안 계속된 것" 과 "멈춘 것" 을 한 줄씩 대응시킨 표입니다. 기록 파일은 jq 로, 설정은 cilium-config 와 CiliumNode 에서 읽으세요.