Cloudnet Cilium 2주차 스터디를 진행하며 정리한 글입니다.
이번 포스팅에서는 Cilium Hubble에 대해 이해하는 포스팅을 작성하겠습니다.
Hubble
https://docs.cilium.io/en/stable/overview/intro/#intro

Cilium에서 Hubble은 네트워크 및 보안 가시성을 제공하는 분산 추적/관찰 도구입니다.
Cilium은 eBPF를 사용해 클러스터 내부 네트워크 흐름을 커널 수준에서 추적하고, Hubble은 이 정보를 수집, 분석, 시각화하는 역할을 합니다.
Hubble 특징
1. 서비스 간 통신 및 의존성 시각화
- 어떤 서비스가 어떤 서비스와 통신하는지 확인
- 서비스 간 통신 빈도 파악
- 서비스 간 의존성 그래프 확인
- 어떤 HTTP 요청이 오가는지 파악
- 어떤 Kafka 토픽을 읽고 쓰는지 확인 (Cilium Kafka-aware 정책 설정 시)
2. 네트워크 모니터링 및 알림
- 네트워크 통신 실패 여부 파악
- 실패 원인 분석 (DNS 문제인지, 애플리케이션 문제인지, TCP 문제인지 등)
- Layer 4 (TCP) 또는 Layer 7 (HTTP) 문제 식별
- 최근 5분 간 DNS 해석 오류가 있었던 서비스 확인
- TCP 연결이 끊긴 서비스, 연결 타임아웃 발생 서비스 식별
- 응답 없는 TCP SYN 요청 비율 측정
3. 애플리케이션 수준 모니터링
- HTTP 4xx, 5xx 응답률 측정 (특정 서비스 또는 클러스터 전체)
- HTTP 요청-응답 간 95th / 99th percentile 지연 시간 측정
- 가장 성능이 나쁜 서비스 식별
- 서비스 간 HTTP 지연 시간 측정
4. 보안 관찰 기능 (Security Observability)
- 네트워크 정책에 의해 차단된 연결 기록 추적
- 클러스터 외부에서 접근한 서비스 식별
- 특정 DNS 이름을 조회한 서비스 확인
Hubble 구성 요소
- hubble-relay: 여러 노드에서 수집된 흐름을 중앙에서 집계해주는 gRPC 서버
- hubble-ui: 시각화 대시보드
- hubble-cli: CLI로 흐름 확인
- cilium-agent: 기본적으로 각 노드에서 흐름 이벤트 생성
Hubble 설치
현재 cilium 설치 시, hubble을 활성화시키지 않아서, cilium helm을 업그레이드 하며 hubble을 활성화하여 설치하도록 하겠습니다.
설치 했을 때, 기존에 생성되지 않았던 Hubble과 OpenMetrics 기반 메트릭이 활성화되어 있습니다.
hubble-relay, hubble-server, cilium-ca 관련 TLS 인증서들도 생성 되었습니다.
hubble-peer 서비스가 클러스터 IP 타입으로 생성되었고, 엔드포인트에 노드 3개가 각각 :4244로 Hubble gRPC 서버로 연결된 것을 확인할 수 있습니다.
hubble-peer는 ClusterIP 서비스로, Cilium이 각 노드의 Hubble Server를 4244 포트로 서비스 형태로 묶은 것입니다. hubble-relay는 hubble-peer를 통해 각 노드의 Cilium으로 연결합니다.
root@k8s-ctr:~# helm upgrade cilium cilium/cilium --namespace kube-system --reuse-values \
--set hubble.enabled=true \
--set hubble.relay.enabled=true \
--set hubble.ui.enabled=true \
# 위의 설정만 적용해도 되나, 아래 설정은 편의를 위해 설정
--set hubble.ui.service.type=NodePort \
--set hubble.ui.service.nodePort=31234 \
--set hubble.export.static.enabled=true \
--set hubble.export.static.filePath=/var/run/cilium/hubble/events.log \
--set prometheus.enabled=true \
--set operator.prometheus.enabled=true \
--set hubble.metrics.enableOpenMetrics=true \
--set hubble.metrics.enabled="{dns,drop,tcp,flow,port-distribution,icmp,httpV2:exemplars=true;labelsContext=source_ip\,source_namespace\,source_workload\,destination_ip\,destination_namespace\,destination_workload\,traffic_direction}"
Release "cilium" has been upgraded. Happy Helming!
NAME: cilium
LAST DEPLOYED: Sun Jul 27 05:39:42 2025
NAMESPACE: kube-system
STATUS: deployed
REVISION: 2
TEST SUITE: None
NOTES:
You have successfully installed Cilium with Hubble Relay and Hubble UI.
Your release version is 1.17.6.
For any further help, visit https://docs.cilium.io/en/v1.17/gettinghelp
root@k8s-ctr:~# cilium config view | grep -i hubble
enable-hubble true
enable-hubble-open-metrics true
root@k8s-ctr:~# kubectl get secret -n kube-system | grep -iE 'cilium-ca|hubble'
cilium-ca Opaque 2 105s
hubble-relay-client-certs kubernetes.io/tls 3 105s
hubble-server-certs kubernetes.io/tls 3 104s
root@k8s-ctr:~# ss -tnlp | grep -iE 'cilium|hubble' | tee after.txt
LISTEN 0 4096 127.0.0.1:9234 0.0.0.0:* users:(("cilium-operator",pid=5427,fd=9))
LISTEN 0 4096 127.0.0.1:34841 0.0.0.0:* users:(("cilium-agent",pid=7363,fd=53))
LISTEN 0 4096 127.0.0.1:9891 0.0.0.0:* users:(("cilium-operator",pid=5427,fd=6))
LISTEN 0 4096 127.0.0.1:9890 0.0.0.0:* users:(("cilium-agent",pid=7363,fd=6))
LISTEN 0 4096 127.0.0.1:9879 0.0.0.0:* users:(("cilium-agent",pid=7363,fd=59))
LISTEN 0 4096 127.0.0.1:9878 0.0.0.0:* users:(("cilium-envoy",pid=5933,fd=27))
LISTEN 0 4096 127.0.0.1:9878 0.0.0.0:* users:(("cilium-envoy",pid=5933,fd=26))
LISTEN 0 4096 0.0.0.0:9964 0.0.0.0:* users:(("cilium-envoy",pid=5933,fd=25))
LISTEN 0 4096 0.0.0.0:9964 0.0.0.0:* users:(("cilium-envoy",pid=5933,fd=24))
LISTEN 0 4096 *:4244 *:* users:(("cilium-agent",pid=7363,fd=51)) # 추가됨
LISTEN 0 4096 *:9965 *:* users:(("cilium-agent",pid=7363,fd=45)) # 추가됨
LISTEN 0 4096 *:9962 *:* users:(("cilium-agent",pid=7363,fd=7)) # 추가됨
LISTEN 0 4096 *:9963 *:* users:(("cilium-operator",pid=5427,fd=7))
root@k8s-ctr:~# kubectl get svc,ep -n kube-system hubble-peer
Warning: v1 Endpoints is deprecated in v1.33+; use discovery.k8s.io/v1 EndpointSlice
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/hubble-peer ClusterIP 10.96.76.2 <none> 443/TCP 5m34s
NAME ENDPOINTS AGE
endpoints/hubble-peer 192.168.10.100:4244,192.168.10.101:4244,192.168.10.102:4244 5m34s
# hubble ui 웹 접속 주소 확인
root@k8s-ctr:~# NODEIP=$(ip -4 addr show eth1 | grep -oP '(?<=inet\s)\d+(\.\d+){3}')
root@k8s-ctr:~# echo -e "http://$NODEIP:31234"
http://192.168.10.100:31234


Hubble 클라이언트를 설치하겠습니다.
root@k8s-ctr:~# HUBBLE_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/hubble/master/stable.txt)
HUBBLE_ARCH=amd64
if [ "$(uname -m)" = "aarch64" ]; then HUBBLE_ARCH=arm64; fi
curl -L --fail --remote-name-all https://github.com/cilium/hubble/releases/download/$HUBBLE_VERSION/hubble-linux-${HUBBLE_ARCH}.tar.gz{,.sha256sum}
sudo tar xzvfC hubble-linux-${HUBBLE_ARCH}.tar.gz /usr/local/bin
root@k8s-ctr:~# which hubble
/usr/local/bin/hubble
# hubble cli를 사용하려면 hubble relay 주소를 지정해야함
root@k8s-ctr:~# hubble status
failed getting status: rpc error: code = Unavailable desc = connection error: desc = "transport: Error while dialing: dial tcp 127.0.0.1:4245: connect: connection refused"
# 포트포워딩
root@k8s-ctr:~# cilium hubble port-forward&
[1] 8366
root@k8s-ctr:~# ℹ️ Hubble Relay is available at 127.0.0.1:4245
root@k8s-ctr:~# ss -tnlp | grep 4245
LISTEN 0 4096 127.0.0.1:4245 0.0.0.0:* users:(("cilium",pid=8366,fd=7))
LISTEN 0 4096 [::1]:4245 [::]:* users:(("cilium",pid=8366,fd=8))
root@k8s-ctr:~# hubble status
Healthcheck (via localhost:4245): Ok
Current/Max Flows: 11,080/12,285 (90.19%)
Flows/s: 38.00
Connected Nodes: 3/3
root@k8s-ctr:~# hubble config view
basic-auth-password: ""
basic-auth-username: ""
config: /root/.config/hubble/config.yaml
debug: false
kube-context: ""
kube-namespace: kube-system
kubeconfig: ""
port-forward: false
port-forward-port: "4245"
request-timeout: 12s
server: localhost:4245
timeout: 5s
tls: false
tls-allow-insecure: false
tls-ca-cert-files: []
tls-client-cert-file: ""
tls-client-key-file: ""
tls-server-name: ""
hubble observe 명령어를 통해 트래픽을 확인해보겠습니다.
root@k8s-ctr:~# hubble observe
Jul 26 20:55:10.078: 10.0.2.15:60422 (host) -> kube-system/hubble-relay-5dcd46f5c-2lfmr:4222 (ID:20599) to-endpoint FORWARDED (TCP Flags: ACK)
Jul 26 20:55:10.078: 10.0.2.15:60422 (host) <> kube-system/hubble-relay-5dcd46f5c-2lfmr (ID:20599) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:10.078: 10.0.2.15:60422 (host) <> kube-system/hubble-relay-5dcd46f5c-2lfmr (ID:20599) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:10.078: 10.0.2.15:60422 (host) <> kube-system/hubble-relay-5dcd46f5c-2lfmr (ID:20599) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:10.078: 10.0.2.15:60422 (host) <- kube-system/hubble-relay-5dcd46f5c-2lfmr:4222 (ID:20599) to-stack FORWARDED (TCP Flags: ACK, PSH)
Jul 26 20:55:10.078: 10.0.2.15:60422 (host) <> kube-system/hubble-relay-5dcd46f5c-2lfmr (ID:20599) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:10.078: 10.0.2.15:60422 (host) <> kube-system/hubble-relay-5dcd46f5c-2lfmr (ID:20599) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:10.080: 10.0.2.15:60422 (host) -> kube-system/hubble-relay-5dcd46f5c-2lfmr:4222 (ID:20599) to-endpoint FORWARDED (TCP Flags: ACK, PSH)
Jul 26 20:55:10.081: 10.0.2.15:60422 (host) -> kube-system/hubble-relay-5dcd46f5c-2lfmr:4222 (ID:20599) to-endpoint FORWARDED (TCP Flags: ACK, FIN)
Jul 26 20:55:10.081: 10.0.2.15:60422 (host) <- kube-system/hubble-relay-5dcd46f5c-2lfmr:4222 (ID:20599) to-stack FORWARDED (TCP Flags: ACK, FIN)
Jul 26 20:55:10.081: 10.0.2.15:60422 (host) -> kube-system/hubble-relay-5dcd46f5c-2lfmr:4222 (ID:20599) to-endpoint FORWARDED (TCP Flags: ACK)
Jul 26 20:55:12.634: 192.168.10.102:49300 (host) -> 192.168.10.100:6443 (kube-apiserver) to-network FORWARDED (TCP Flags: ACK)
Jul 26 20:55:12.919: kube-system/hubble-ui-76d4965bb6-fk6kx:42022 (ID:49608) -> kube-system/hubble-relay-5dcd46f5c-2lfmr:4245 (ID:20599) to-endpoint FORWARDED (TCP Flags: ACK, PSH)
Jul 26 20:55:12.920: kube-system/hubble-ui-76d4965bb6-fk6kx:42022 (ID:49608) <- kube-system/hubble-relay-5dcd46f5c-2lfmr:4245 (ID:20599) to-network FORWARDED (TCP Flags: ACK, PSH)
Jul 26 20:55:14.420: 10.0.2.15:56230 (host) -> kube-system/hubble-ui-76d4965bb6-fk6kx:8081 (ID:49608) to-endpoint FORWARDED (TCP Flags: ACK)
Jul 26 20:55:14.420: 10.0.2.15:56230 (host) <> kube-system/hubble-ui-76d4965bb6-fk6kx (ID:49608) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:14.420: 10.0.2.15:56230 (host) -> kube-system/hubble-ui-76d4965bb6-fk6kx:8081 (ID:49608) to-endpoint FORWARDED (TCP Flags: ACK, PSH)
Jul 26 20:55:14.421: 10.0.2.15:56230 (host) <> kube-system/hubble-ui-76d4965bb6-fk6kx (ID:49608) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:14.421: 10.0.2.15:56230 (host) <> kube-system/hubble-ui-76d4965bb6-fk6kx (ID:49608) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:14.421: 10.0.2.15:56230 (host) <- kube-system/hubble-ui-76d4965bb6-fk6kx:8081 (ID:49608) to-stack FORWARDED (TCP Flags: ACK, PSH)
Jul 26 20:55:14.421: 10.0.2.15:56230 (host) <- kube-system/hubble-ui-76d4965bb6-fk6kx:8081 (ID:49608) to-stack FORWARDED (TCP Flags: ACK, FIN)
Jul 26 20:55:14.421: 10.0.2.15:56230 (host) -> kube-system/hubble-ui-76d4965bb6-fk6kx:8081 (ID:49608) to-endpoint FORWARDED (TCP Flags: ACK, FIN)
Jul 26 20:55:14.862: 127.0.0.1:8090 (world) <> kube-system/hubble-ui-76d4965bb6-fk6kx (ID:49608) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:14.862: 127.0.0.1:8090 (world) <> kube-system/hubble-ui-76d4965bb6-fk6kx (ID:49608) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:14.862: 127.0.0.1:37496 (world) <> kube-system/hubble-ui-76d4965bb6-fk6kx (ID:49608) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:14.863: 127.0.0.1:37496 (world) <> kube-system/hubble-ui-76d4965bb6-fk6kx (ID:49608) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:14.863: 127.0.0.1:37496 (world) <> kube-system/hubble-ui-76d4965bb6-fk6kx (ID:49608) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:14.863: 127.0.0.1:37496 (world) <> kube-system/hubble-ui-76d4965bb6-fk6kx (ID:49608) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:14.863: 127.0.0.1:37496 (world) <> kube-system/hubble-ui-76d4965bb6-fk6kx (ID:49608) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:14.863: 127.0.0.1:8090 (world) <> kube-system/hubble-ui-76d4965bb6-fk6kx (ID:49608) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:14.863: 127.0.0.1:8090 (world) <> kube-system/hubble-ui-76d4965bb6-fk6kx (ID:49608) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:15.031: kube-system/hubble-relay-5dcd46f5c-2lfmr:40558 (ID:20599) <- 192.168.10.102:4244 (host) to-endpoint FORWARDED (TCP Flags: ACK)
Jul 26 20:55:15.031: kube-system/hubble-relay-5dcd46f5c-2lfmr:40558 (ID:20599) -> 192.168.10.102:4244 (host) to-stack FORWARDED (TCP Flags: ACK)
Jul 26 20:55:15.475: 192.168.10.101:38498 (host) -> 192.168.10.100:6443 (kube-apiserver) to-network FORWARDED (TCP Flags: ACK)
Jul 26 20:55:15.911: 192.168.10.100:56393 (kube-apiserver) -> kube-system/hubble-ui-76d4965bb6-fk6kx:8081 (ID:49608) to-endpoint FORWARDED (TCP Flags: ACK, FIN)
Jul 26 20:55:15.911: 192.168.10.100:56393 (kube-apiserver) <- kube-system/hubble-ui-76d4965bb6-fk6kx:8081 (ID:49608) to-network FORWARDED (TCP Flags: ACK, FIN)
Jul 26 20:55:16.243: 127.0.0.1:8080 (world) <> kube-system/coredns-674b8bbfcf-hgjll (ID:63813) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:16.243: 127.0.0.1:36702 (world) <> kube-system/coredns-674b8bbfcf-hgjll (ID:63813) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:16.243: 127.0.0.1:8080 (world) <> kube-system/coredns-674b8bbfcf-56xkl (ID:63813) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:16.244: 127.0.0.1:36702 (world) <> kube-system/coredns-674b8bbfcf-hgjll (ID:63813) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:16.244: 127.0.0.1:36702 (world) <> kube-system/coredns-674b8bbfcf-hgjll (ID:63813) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:16.244: 127.0.0.1:8080 (world) <> kube-system/coredns-674b8bbfcf-hgjll (ID:63813) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:16.244: 127.0.0.1:8080 (world) <> kube-system/coredns-674b8bbfcf-hgjll (ID:63813) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:16.244: 127.0.0.1:8080 (world) <> kube-system/coredns-674b8bbfcf-hgjll (ID:63813) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:16.244: 127.0.0.1:8080 (world) <> kube-system/coredns-674b8bbfcf-hgjll (ID:63813) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:16.244: 127.0.0.1:36702 (world) <> kube-system/coredns-674b8bbfcf-hgjll (ID:63813) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:16.244: 127.0.0.1:36702 (world) <> kube-system/coredns-674b8bbfcf-hgjll (ID:63813) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:16.244: 127.0.0.1:36702 (world) <> kube-system/coredns-674b8bbfcf-56xkl (ID:63813) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:16.244: 127.0.0.1:36702 (world) <> kube-system/coredns-674b8bbfcf-56xkl (ID:63813) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:16.244: 127.0.0.1:36702 (world) <> kube-system/coredns-674b8bbfcf-56xkl (ID:63813) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:16.244: 127.0.0.1:36702 (world) <> kube-system/coredns-674b8bbfcf-56xkl (ID:63813) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:16.244: 127.0.0.1:36702 (world) <> kube-system/coredns-674b8bbfcf-56xkl (ID:63813) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:16.244: 127.0.0.1:8080 (world) <> kube-system/coredns-674b8bbfcf-56xkl (ID:63813) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:16.244: 127.0.0.1:8080 (world) <> kube-system/coredns-674b8bbfcf-56xkl (ID:63813) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:16.244: 127.0.0.1:8080 (world) <> kube-system/coredns-674b8bbfcf-56xkl (ID:63813) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:16.244: 127.0.0.1:8080 (world) <> kube-system/coredns-674b8bbfcf-56xkl (ID:63813) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:16.489: 127.0.0.1:42030 (world) <> kube-system/hubble-relay-5dcd46f5c-2lfmr (ID:20599) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:16.489: 127.0.0.1:42030 (world) <> kube-system/hubble-relay-5dcd46f5c-2lfmr (ID:20599) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:16.489: 127.0.0.1:42030 (world) <> kube-system/hubble-relay-5dcd46f5c-2lfmr (ID:20599) pre-xlate-rev TRACED (TCP)
Jul 26 20:55:16.489: 127.0.0.1:42030 (world) <> kube-system/hubble-relay-5dcd46f5c-2lfmr (ID:20599) pre-xlate-rev TRACED (TCP)
아래 Demo 어플리케이션 배포 전, 단축키를 지정하겠습니다.
# cilium 파드 이름
export CILIUMPOD0=$(kubectl get -l k8s-app=cilium pods -n kube-system --field-selector spec.nodeName=k8s-ctr -o jsonpath='{.items[0].metadata.name}')
export CILIUMPOD1=$(kubectl get -l k8s-app=cilium pods -n kube-system --field-selector spec.nodeName=k8s-w1 -o jsonpath='{.items[0].metadata.name}')
export CILIUMPOD2=$(kubectl get -l k8s-app=cilium pods -n kube-system --field-selector spec.nodeName=k8s-w2 -o jsonpath='{.items[0].metadata.name}')
echo $CILIUMPOD0 $CILIUMPOD1 $CILIUMPOD2
# 단축키(alias) 지정
alias c0="kubectl exec -it $CILIUMPOD0 -n kube-system -c cilium-agent -- cilium"
alias c1="kubectl exec -it $CILIUMPOD1 -n kube-system -c cilium-agent -- cilium"
alias c2="kubectl exec -it $CILIUMPOD2 -n kube-system -c cilium-agent -- cilium"
alias c0bpf="kubectl exec -it $CILIUMPOD0 -n kube-system -c cilium-agent -- bpftool"
alias c1bpf="kubectl exec -it $CILIUMPOD1 -n kube-system -c cilium-agent -- bpftool"
alias c2bpf="kubectl exec -it $CILIUMPOD2 -n kube-system -c cilium-agent -- bpftool"
Hubble 확인을 위한 Demo 어플리케이션 배포
스타워즈에서 영감을 받은 예제에서는 deathstar, tiefighter, xwing의 세 가지 마이크로서비스 애플리케이션이 있습니다.
deathstar는 포트 80에서 HTTP 웹서비스를 실행하며, 이 서비스는 두 개의 포드 복제본에 걸쳐 deathstar에 대한 요청을 로드 밸런싱하는 Kubernetes 서비스로 노출됩니다.
deathstar 서비스는 제국의 우주선에 착륙 서비스를 제공하여 착륙 포트를 요청할 수 있도록 합니다.
tiefighter 포드는 일반적인 제국 선박의 착륙 요청 클라이언트 서비스를 나타내며, xwing은 동맹 선박의 유사한 서비스를 나타냅니다.
deathstar 착륙 서비스에 대한 접근 제어를 위한 다양한 보안 정책을 테스트할 수 있도록 존재합니다.
https://docs.cilium.io/en/stable/gettingstarted/demo/

root@k8s-ctr:~# kubectl apply -f https://raw.githubusercontent.com/cilium/cilium/1.17.6/examples/minikube/http-sw-app.yaml
service/deathstar created
deployment.apps/deathstar created
pod/tiefighter created
pod/xwing created
root@k8s-ctr:~# kubectl get pod --show-labels
NAME READY STATUS RESTARTS AGE LABELS
deathstar-8c4c77fb7-bpd5j 0/1 ContainerCreating 0 11s app.kubernetes.io/name=deathstar,class=deathstar,org=empire,pod-template-hash=8c4c77fb7
deathstar-8c4c77fb7-dlgkl 1/1 Running 0 11s app.kubernetes.io/name=deathstar,class=deathstar,org=empire,pod-template-hash=8c4c77fb7
tiefighter 0/1 ContainerCreating 0 12s app.kubernetes.io/name=tiefighter,class=tiefighter,org=empire
xwing 0/1 ContainerCreating 0 12s app.kubernetes.io/name=xwing,class=xwing,org=alliance
# 자기 자신의 노드에 실행되고 있는 엔드포인트만 기록
root@k8s-ctr:~# c0 endpoint list
ENDPOINT POLICY (ingress) POLICY (egress) IDENTITY LABELS (source:key[=value]) IPv6 IPv4 STATUS
ENFORCEMENT ENFORCEMENT
355 Disabled Disabled 1 k8s:node-role.kubernetes.io/control-plane ready
k8s:node.kubernetes.io/exclude-from-external-load-balancers
reserved:host
469 Disabled Disabled 63813 k8s:io.cilium.k8s.namespace.labels.kubernetes.io/metadata.name=kube-system 172.20.0.129 ready
k8s:io.cilium.k8s.policy.cluster=default
k8s:io.cilium.k8s.policy.serviceaccount=coredns
k8s:io.kubernetes.pod.namespace=kube-system
k8s:k8s-app=kube-dns
2977 Disabled Disabled 63813 k8s:io.cilium.k8s.namespace.labels.kubernetes.io/metadata.name=kube-system 172.20.0.84 ready
k8s:io.cilium.k8s.policy.cluster=default
k8s:io.cilium.k8s.policy.serviceaccount=coredns
k8s:io.kubernetes.pod.namespace=kube-system
k8s:k8s-app=kube-dns
deathstar 서비스의 관점에서 보면, org= empire 라벨이 부착된 선박만 연결하여 착륙을 요청할 수 있습니다. 별도 policy을 시행하지 않기 때문에 Xwing과 타이파이터 모두 착륙을 요청할 수 있습니다.
이 때, endpoint와 identity값의 차이를 알아보겠습니다.
endpoint는 Cilium에서 endpoint는 각 Pod 또는 네트워크 엔티티를 대표하는 객체입니다.
identity는 라벨의 집합에 대해 Cilium이 부여하는 고유한 ID 숫자값입니다. 따라서, 동일한 라벨이 부여된 Pod는 동일한 identity를 가져야 합니다.
(하지만, 나는 왜 deathstar 라벨에 대해 identity가 다르게 나올까?;;;)
root@k8s-ctr:~# c1 endpoint list | grep -iE 'xwing|tiefighter|deathstar'
ENDPOINT POLICY (ingress) POLICY (egress) IDENTITY LABELS (source:key[=value]) IPv6 IPv4 STATUS
502 Disabled Disabled 4101 k8s:app.kubernetes.io/name=xwing 172.20.1.80 ready
k8s:class=xwing
1974 Disabled Disabled 2752 k8s:app.kubernetes.io/name=deathstar 172.20.1.6 ready
k8s:class=deathstar
root@k8s-ctr:~# c2 endpoint list | grep -iE 'xwing|tiefighter|deathstar'
ENDPOINT POLICY (ingress) POLICY (egress) IDENTITY LABELS (source:key[=value]) IPv6 IPv4 STATUS
362 Disabled Disabled 12397 k8s:app.kubernetes.io/name=deathstar 172.20.2.246 ready
k8s:class=deathstar
2507 Disabled Disabled 10657 k8s:app.kubernetes.io/name=tiefighter 172.20.2.56 ready
k8s:class=tiefighter


L3, L4 Policy 적용하기
Cilium을 사용할 때는 IP 주소가 아니라 Pod에 붙은 라벨을 기반으로 네트워크 보안 정책을 정의합니다.
예를 들어, deathstar 서비스에는 org=empire 라벨이 붙은 pod만 접근하도록 제한할 수 있습니다. 이처럼 라벨 기반으로 특정 그룹만 통신을 허용하는 정책은 L3/L4 보안 정책이라고 부르며, IP나 포트 수준에서 제어가 가능합니다.
또한 Cilium은 stateful하게 연결을 추적하기 때문에, 정책에서 한 방향의 요청만 허용하면 응답 패킷은 자동으로 허용됩니다. 예를 들어 empire 소속이 deathstar에 요청을 보낼 수 있도록 허용했다면, deathstar의 응답은 별도 규칙 없이도 전달됩니다.

endpointSelector로 이 정책이 적용될 대상 pod가 deathstar 서비스이고, 접근을 허용한 pod 조건이 org=empire 라벨이 있고, L4 계층의 TCP 80 포트에 대해서 허용하는 규칙을 적용했습니다.
이 때, tiefighter는 해당 라벨이 존재하여 접근이 가능한 반면, xwing은 해당 라벨이 없어서 접근이 허용되지 않아 drop 된 것을 확인할 수 있습니다.
# CiliumNetworkPolicy
## CiliumNetworkPolicys는 "endpointSelector"를 사용하여 팟 레이블에서 정책이 적용되는 소스와 목적지를 식별합니다.
## 아래 정책은 TCP 포트 80에서 레이블(org=empire)이 있는 모든 팟에서 레이블(org=empire, class=deathstar)이 있는 데스스타 팟으로 전송되는 트래픽을 화이트리스트로 작성합니다.
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: "rule1"
spec:
description: "L3-L4 policy to restrict deathstar access to empire ships only"
endpointSelector:
matchLabels:
org: empire
class: deathstar
ingress:
- fromEndpoints:
- matchLabels:
org: empire
toPorts:
- ports:
- port: "80"
protocol: TCP
root@k8s-ctr:~# kubectl apply -f https://raw.githubusercontent.com/cilium/cilium/1.17.6/examples/minikube/sw_l3_l4_policy.yaml
ciliumnetworkpolicy.cilium.io/rule1 created
root@k8s-ctr:~# kubectl get pod --show-labels
NAME READY STATUS RESTARTS AGE LABELS
deathstar-8c4c77fb7-w7cjr 1/1 Running 0 21m app.kubernetes.io/name=deathstar,class=deathstar,org=empire,pod-template-hash=8c4c77fb7
deathstar-8c4c77fb7-zhrdx 1/1 Running 0 21m app.kubernetes.io/name=deathstar,class=deathstar,org=empire,pod-template-hash=8c4c77fb7
tiefighter 1/1 Running 0 21m app.kubernetes.io/name=tiefighter,class=tiefighter,org=empire
xwing 1/1 Running 0 21m app.kubernetes.io/name=xwing,class=xwing,org=alliance
root@k8s-ctr:~# kubectl exec xwing -- curl -s -XPOST deathstar.default.svc.cluster.local/v1/request-landing --connect-timeout 2
command terminated with exit code 28
root@k8s-ctr:~# hubble observe -f --type drop
Jul 26 21:36:32.679: default/xwing:43520 (ID:4101) <> default/deathstar-8c4c77fb7-zhrdx:80 (ID:2752) Policy denied DROPPED (TCP Flags: SYN)
Jul 26 21:36:33.738: default/xwing:43520 (ID:4101) <> default/deathstar-8c4c77fb7-zhrdx:80 (ID:2752) Policy denied DROPPED (TCP Flags: SYN)
root@k8s-ctr:~# kubectl exec tiefighter -- curl -s -XPOST deathstar.default.svc.cluster.local/v1/request-landing
Ship landed

L7 Policy 적용하기
Cilium에서는 L7 정책 처리를 위해 Envoy 프록시가 자동으로 사이드카 없이 데몬셋 형태로 배포되며, 이를 통해 HTTP 수준의 정책 필터링을 수행합니다. 실제로는 cilium-envoy라는 데몬이 각 노드에 존재하며, Cilium이 정책에 따라 트래픽을 이 프록시에 전달해 L7 처리를 맡깁니다.

앞선 L3/L4 정책에서는 tiefighter와 xwing에게 deathstar 서비스 전체에 대한 접근을 일괄 허용하거나 차단했지만, 실제 운영 환경에서는 이보다 더 세밀한 제어가 필요합니다. 특히 마이크로서비스 환경에서는 각 서비스가 수행해야 하는 HTTP 요청만 허용하고, 나머지는 모두 차단하는 식의 최소 권한 원칙이 중요합니다.
예를 들어, deathstar 서비스가 GET /exhaust-port 외에도 내부 유지보수용 API를 함께 노출하고 있다면, 단순한 L3/L4 제어만으로는 이를 구분해 막을 수 없기 때문에 이런 경우, 요청의 HTTP 메서드나 경로에 따라 허용 여부를 결정하는 L7 정책이 필요합니다.

# 기존 rule1 정책을 업데이트 해서 사용
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: "rule1"
spec:
description: "L7 policy to restrict access to specific HTTP call"
endpointSelector:
matchLabels:
org: empire
class: deathstar
ingress:
- fromEndpoints:
- matchLabels:
org: empire
toPorts:
- ports:
- port: "80"
protocol: TCP
rules:
http:
- method: "POST"
path: "/v1/request-landing"
root@k8s-ctr:~# kubectl apply -f https://raw.githubusercontent.com/cilium/cilium/1.17.6/examples/minikube/sw_l3_l4_l7_policy.yaml
ciliumnetworkpolicy.cilium.io/rule1 configured
root@k8s-ctr:~# kubectl exec tiefighter -- curl -s -XPOST deathstar.default.svc.cluster.local/v1/request-landing
Ship landed
# 아까와 다르게 L7 layer엣 HTTP 호출에 대해 자세하게 남는다.
root@k8s-ctr:~# hubble observe -f --pod deathstar --protocol http
Jul 26 21:46:17.054: default/tiefighter:33516 (ID:10657) -> default/deathstar-8c4c77fb7-w7cjr:80 (ID:12397) http-request FORWARDED (HTTP/1.1 POST http://deathstar.default.svc.cluster.local/v1/request-landing)
Jul 26 21:46:17.070: default/tiefighter:33516 (ID:10657) <- default/deathstar-8c4c77fb7-w7cjr:80 (ID:12397) http-response FORWARDED (HTTP/1.1 200 22ms (POST http://deathstar.default.svc.cluster.local/v1/request-landing))

'스터디 > Cilium' 카테고리의 다른 글
| [Cilium] IPAM (3) | 2025.08.03 |
|---|---|
| [Cilium] Prometheus, Grafana을 활용한 Cilium 모니터링 (6) | 2025.07.27 |
| [Cilium] Cilium CNI 설치 및 통신 확인 (7) | 2025.07.20 |
| [Cilium] Cilium CNI과 eBPF 소개 (5) | 2025.07.20 |
| [Cilium] 사전 실습 환경 구성, Flannel CNI 설치 (3) | 2025.07.19 |