2주차 스터디가 진행됬다.
2주차에는 Observability가 주된 스터디 내용이였다.
이중에서 특히 Hubble + Promehteus + Grafna를 통해서 현재 네트워크 트래픽에 대한 지표를 수집하고 관찰하는것이 목적이다.
실습 환경 구성
실습 환경은 1주차와 똑같다. 실제 스터디에서는 각 주차별로 Vagrant를 별도로 제공해주고 있지만,
실습 환경이 똑같기때문에 나는 Vagrant로 다시 설치하지 않고 필요한 부분만 재설치해주었다.

차이점이 있다면, 1주차에는 kubeadm ini/join 을 명령어로 했다. 하지만 2주차에서는 configFile로 만들어서 사용한다.
그외에는 동일하기 때문에 cilium helm upgrade만 하고, 기존 w1의 Vragrant를 재사용했다.
helm upgrade --install cilium cilium/cilium --namespace kube-system \
--set k8sServiceHost=192.168.10.100 --set k8sServicePort=6443 \
--set ipam.mode="cluster-pool" --set ipam.operator.clusterPoolIPv4PodCIDRList={"172.20.0.0/16"} --set ipv4NativeRoutingCIDR=172.20.0.0/16 \
--set routingMode=native --set autoDirectNodeRoutes=true --set endpointRoutes.enabled=true \
--set kubeProxyReplacement=true --set bpf.masquerade=true --set installNoConntrackIptablesRules=true \
# cilium에서 노드에 대한 헬스체크를 하는 기능이나, 테스트 목적에서는 제외
--set endpointHealthChecking.enabled=false --set healthChecking=false \
# hubble을 사용하지만 아래에서 flag를 변경해서 재배포하므로 초기 구성환경에서는 제외
--set hubble.enabled=false --set operator.replicas=1 --set debug.enabled=true
Observability
Hubble
hubble은 cilium을 기반으로 eBPF를 활용하여 트래픽에 대한 가시성을 확보할 수 있는 오픈소스이다.
eBPF를 사용하면, 커널 레이어에서 처리되는게 많기 때문에 가시성 확보가 어려워 hubble을 통해 보완한다.
Hubble 설치
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}"
설치하는 방법은 cilium helm values에 추가하면 되기 때문에 설치하는것은 간단하다.
그리고 설치 전/후를 비교해보면 values로 추가한 값이 cilium configmap에 반영된것을 확인할 수 있다.

그리고 기본적으로 hubble API는 cilium이 설치되어 있는 개별 노드에 범위에서 동작하지만,
hubble relay를 배포하여 클러스터 범주로 확대 적용하여 편의성 있는 API를 제공하기도 한다.

그리고는 hubble-ui의 NodePort svc port로 접근하면 hubble-ui 접근이 가능하다.
- 내부적으로는 nginx를 사용해서 서비스를 제공하고 있다.
hubble-ui에서는 기본적으로 src, dst 그리고 port와 L7 정보가 표시된다.
그리고 클러스터 외부와 통신이 있을 때는 world identity가 추가되고 ip 정보가 추가된다.

이전에는 Pod간 트래픽을 확인하려면 tcpdump를 주로 했었다.
그리고 kubenretes를 운영할 때 가장 어려운것이 트래픽 확인이라고 생각한다.
cilium은 이러한 운영의 어려움을 알고, hubble을 제공하고 있기 때문에 calico 등에서 cilium으로 전환하는것에도 좋은 포인트라고 생각한다.
Hubble client
위에서는 hubble-ui 서비스의 NodePort로 접근을 했는데, hubble client를 설치하면 더 쉽게 접근할 수 있다.
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
which hubble
hubble status
---
(⎈|HomeLab:kube-system) 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"
다만, 기본적으로는 localhost로 port-forward를 열어야 접근이 가능하기에 cilium hubble 명령어를 통해서 port-forward를 해준다.
(⎈|HomeLab:kube-system) root@k8s-ctr:~# cilium hubble port-forward&
[1] 63667
(⎈|HomeLab:kube-system) root@k8s-ctr:~# ℹ️ Hubble Relay is available at 127.0.0.1:4245
---
(⎈|HomeLab:kube-system) root@k8s-ctr:~# hubble status
Healthcheck (via localhost:4245): Ok
Current/Max Flows: 9,801/12,285 (79.78%)
Flows/s: 31.78
Connected Nodes: 3/3
---
(⎈|HomeLab:kube-system) root@k8s-ctr:~# ss -tnlp |grep 4245
LISTEN 0 4096 127.0.0.1:4245 0.0.0.0:* users:(("cilium",pid=63667,fd=7))
LISTEN 0 4096 [::1]:4245 [::]:* users:(("cilium",pid=63667,fd=8))
---
(⎈|HomeLab:kube-system) root@k8s-ctr:~# hubble config view
basic-auth-password: ""
basic-auth-username: ""
config: /root/.config/hubble/config.yaml
그러면 hubble에 대한 자세한 상태와 설정 그리고 가장 중요한 hubble observe를 이용할 수 있다.

Cilium + Hubble 실습
실습은 cilium docs에서 제공해주는 환경을 통해서 진행한다.

기본적으로 org 와 class로 labels로 구분이 되는 서비스가 배포되어 있으며,
각 org / class에 따라서 접근이 허용되는것이 있고, 접근이 허용되지 않는 서비스가 있다.
그래서 실습은 cilium networkpolicy를 통해서 각 구성요소에 맞는 접근제한을 하고 hubble 통해서 이를 확인하는것이다.
- networkpolicy는 사용했었는데, 생각보다 labels 지정을 잘못해주면 장애가 쉽게 발생하기도 하고
- 휴먼에러로 인한 문제점도 많아서, 이전 회사에서 도입을 했다가 지웠던 기억이 있다.. 나 또한.. networkpolicy로 장애를 냈었다.
ciliumnetworkpolicy(이하 cnp)는 기본적으로 labels을 기본으로 하기 때문에 --show-label을 보면 각 서비스마다 labels이 다르다.
(⎈|HomeLab:test) root@k8s-ctr:~# k get pods --show-labels
NAME READY STATUS RESTARTS AGE LABELS
deathstar-8c4c77fb7-gwzgb 1/1 Running 0 2m28s app.kubernetes.io/name=deathstar,class=deathstar,org=empire,pod-template-hash=8c4c77fb7
deathstar-8c4c77fb7-wq9t9 1/1 Running 0 2m28s app.kubernetes.io/name=deathstar,class=deathstar,org=empire,pod-template-hash=8c4c77fb7
tiefighter 1/1 Running 0 2m28s app.kubernetes.io/name=tiefighter,class=tiefighter,org=empire
xwing 1/1 Running 0 2m28s app.kubernetes.io/name=xwing,class=xwing,org=alliance
그리고 endpoint list를 확인해보면 기본적으로 POLICY가 모두 Disable로 되어 있어, 별도의 네트워크 제어를 하지 않는다는 뜻이다.

ciliumnetworkpolicy(cnp) 적용 전
cnp를 적용하기 이전에는 트래픽을 제어하는게 없다.
그렇기 때문에 아래 hubble-ui와 observe에서 보는것과 같이 별다른 제약이 없이 통신이 된다.

그리고 마찬가지로 hubble observe로 트래픽을 확인할 수 있고, 이때는 protocol, identity, podName으로 확인이 된다.
- identity는 endpoint list에서 확인할 수 있는 정보로 서비스마다 서로 독립적인 값을 가진다.
hubble observe -f --from-identity {??}
hubble observe -f --protocol {tcp|udp}
hubble observe -f {pod_name}
cnp 적용, Not L7 Rule
cnp의 가장 좋은 점은 단방향 정책만 적용해도 된다는것이다. 이것은 statuful한 연결 정책을 지원한다고라고 표현한다.
- networkpolicy의 경우에는 A -> B로 가는 정책을 허용하면, 반대로 B -> A로 가는 정책을 허용해줘야 했다.
- 하지만 cnp에서는 A -> B로 가는 정책만 허용하면, 반대 방향에 대해서는 별도 정책이 없어도 허용을 한다는것이다!! 좋다!
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
테스트하는 cnp 정보이다. 이를 해석하면 다음과 같다.
- org: empire + class: deathstar label을 가지고 있는 pod에만 적용이 되며
- 해당 pod에는 org: empire + TCP 80에 대해서만 허용하겠다는 뜻이다.
쉽게 말하면, 아래와 같이 org가 다른 xwing -> deathstar로 가는것을 차단하는것이다.

curl로 테스트해보면 위 그림과 같이 xwing에서는 호출이 실패하고, tiefighter에서는 허용되는 모습을 볼 수 있다.
마찬가지로 hubble-ui와 hubble observe에서도 확인이 가능하다.

보통 허용된 트래픽(FORWARDED) 트래픽에 대해서는 정상 트래픽이기 때문에 확인을 하지 않고
차단되고 있는 것을 확인하는 경우가 많은데 이때 --verdict 옵션 등으로 상세한 트래픽 필터링 제어가 가능하다.
# verdict 옵션으로 차단된 트래픽에 대해서만 확인이 가능하다.
# 또는 pod 옵션이나, to prefix 등의 많은 옵션을 통해서 정교한 트래픽 필터링이 가능하다.
hubble observe -f --protocol tcp --pod deathstar --verdict DROPPED
cnp 적용, contain L7 Rule
ciliumnetworkpolicy과 networkpolicy의 가장 큰 차이점이라고 하면 L7에 대한 트래픽이 제어가 가능하다는점이다.
아래와 같이 테스트를 해보면 위 정책에서 rules.http.method와 rules.http.path가 포함되어 있다.
TCP 80 중에서도 POST /v1/request-landing Path에 대해서만 허용을 하겠다는 내용이다.
- 보통 단일 서비스에서 대외용/대내용 API를 별도 구현할 때도 있고,
- 대내용일때는 AUTH Header에 대한 검증을 하지 않는 경우가 있는데 이때 세세하게 Path를 제어할 때 사용하면 좋을것 같다.
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"
Not L7 Rule과 비교해보면 hubble-ui에서 L7 Info 정보가 생겼다.
그리고 hubble observe에서는 --procotol http로 조회해보면 http-requset/response를 볼 수 있다.
그리고 여기에 더해 http responseCode와 latency도 확인할 수 있다!!
- 개인적으로는 latency를 볼 수 있는게 가장 좋았다.
- 쿠버네티스를 운영하다보면 응답지연 그리고 ACL의 지옥에 갇히게 된다. 그때 hubble을 사용하면 한눈에 장애를 파악할 수 있다.

그리고 cnp에서 정의하지 않은 요청을 하면 차단(=Drop)이 된다.
cnp가 없다면 All Allow지만, cnp가 적용되면 cnp에 정의되지 않은 것은 default deny 정책이 적용된다.
이것이 cnp를 적용할때 사전에 꼭 확인해야하는 이유이고, 이런것으로 적용이 어려운 이유이다.

그리고 cnp에서 L7 제어를 하게 되면 아래와 같이 cilium-envoy를 거치게 된다.
- 여기 나오는 envoy는 istio에서 사용하는 envoy와 동일한 컨테이너이다.
물론 istio를 사용하면 virtualservice를 통해서 L7에 대한 제어가 가능하다.
하지만 이는 target pod에서 진행되기 때문에, 그만큼 많은 레이어를 통과하기 때문에 지연이 생길 수 밖에 없다.
결국에는 target pod에 있는 istio sidecar에서 제어하기 때문에 그만큼의 자원을 소모하게 된다.
[ External Client ]
│
▼
[ Node 외부 Interface ] (ex. LoadBalancer, Ingress, NodePort)
│
▼
[Cilium BPF (Ingress)] <─── L3/L4 정책 적용
│
▼
[cilium-envoy (Sidecar X, Daemon 형태)] <─── L7 Policy 적용 (HTTP Path, Header 등)
│
▼
[Cilium BPF (Post L7)] <─── Envoy 통과 이후 최종 Forward
│
▼
[ Target Pod (Service Backend) ]
Hubble Flowlog
hubble observe -f를 하면 콘솔창에서 트래픽 흐름을 확인할 수 있다.
하지만, 우리는 콘솔에서만 보고자하는 것은 아니다. output 파일로 만들어서 로그 시스템이 수집하거나/SIEM으로 수집해서
보안 목적으로도 활용할 수 있고 트래픽 차단 확인 목적으로도 사용할 수 있다.
이때 유용한 설정이 hubble export static|dynamic 기능이다!
helm upgrade cilium cilium/cilium --namespace kube-system --reuse-values \
--set hubble.enabled=true \
--set hubble.export.static.enabled=true \
--set hubble.export.static.filePath=/var/run/cilium/hubble/events.log
cilium helm values를 보면 아래와 같이 export 중에서도 static|dynamic 설정이 있다.
- static으로 설정하면 cilium-config, configmap에 저장되고 반영하려면 rollout을 해야한다. 그리고 1개만 반영한다.
- dynamic으로 설정하면 dynamic.config.configMapName으로 지정한 별도 configmap에 저장된다.
자동 반영된다. 다수 지정이 가능하다.
실제 운영 환경에서 사용할 때는 CNI를 rollout하기 힘들기 때문에 static보다는 dynamic을 사용하고 또한
목적별로 별도 파일로 만들어서 수집하고자 하기 때문에 dynamic을 사용하는 경우가 더 많을것 같다.
export:
fileMaxSizeMb: 10
fileMaxBackups: 5
static:
enabled: false
filePath: /var/run/cilium/hubble/events.log
fieldMask: []
# - time
# - source
# - destination
# - verdict
allowList: []
# - '{"verdict":["DROPPED","ERROR"]}'
denyList: []
# - '{"source_pod":["kube-system/"]}'
# - '{"destination_pod":["kube-system/"]}'
# --- Dynamic exporters configuration.
# Dynamic exporters may be reconfigured without a need of agent restarts.
dynamic:
enabled: false
config:
configMapName: cilium-flowlog-config
createConfigMap: true
# ---- Exporters configuration in YAML format.
content:
- name: all
fieldMask: []
includeFilters: []
excludeFilters: []
filePath: "/var/run/cilium/hubble/events.log"
# - name: "test002"
# filePath: "/var/log/network/flow-log/pa/test002.log"
# fieldMask: ["source.namespace", "source.pod_name", "destination.namespace", "destination.pod_name", "verdict"]
# includeFilters:
# - source_pod: ["default/"]
# event_type:
# - type: 1
그리고 tail -f 로 보거나, cat으로 보면 아래와 같이 hubble observe에서 보는것과 비슷하게 볼 수 있다.
별도 Filters를 하지 않으면, 패킷 단위로 로그가 남게된다. pod labels부터 시작해서 너무 많은 정보가 남아서 그대로 사용하기는 어렵다.

지금까지 cilium 설치 시, 같이 설치할 수 있는 hubble에 대해서 알아보았다.
hubble은 kubernetes와 같이 논리적으로 구분되어 사용하는 플랫폼에서 아주 좋은 가시화 솔루션이다.
그리고 cnp를 통해서 트래픽 제어도 가능하고, 필요한 경우 hubble.export로 파일로 저장한 다음에 SaaS에서 수집할 수도 있다.
다음장에서는 cilium metrics를 prometheus로 수집하고, 이를 grafana 대시보드로 구성하는 방법에 대해서 알아보자!
'기술 토론장 > [K8s] Kubernetes' 카테고리의 다른 글
| [Cilium][2주차] 심화학습 과제! (3) | 2025.07.23 |
|---|---|
| [Cilium][2주차] Prometheus/Grafana을 통한 Cilium 가시성 확보 (0) | 2025.07.22 |
| [Cilium][1주차] 테스트 환경 구성과 Cilium 개념 정리 (0) | 2025.07.17 |
| [k8s] helm upgrade fail, remove api 조치 방안 (2) | 2023.02.05 |
| [k8s] kubenetes coreDNS 개념정리 (0) | 2023.01.29 |