Cilium Service Mesh는 기존의 사이드카 프록시(Envoy 등)를 강제적으로 사용하지 않고, eBPF를 활용해 커널 레벨에서 네트워크 및 애플리케이션 계층 트래픽을 제어하는 서비스 메시 구조를 제공한다.
이를 통해 애플리케이션 성능 손실을 최소화하면서도 L3~L7 보안 정책, 트래픽 관리, 모니터링 기능을 통합적으로 제공한다.
Cilium은 eBPF를 활용하여 Pod IP, Service IP 단위로 정책을 제어한다.
Kubernetes NetworkPolicy보다 확장된 CiliumNetworkPolicy(CNP)를 통해 CIDR, Label, Namespace 기반 정책을 정의한다.
L3 계층에서는 기본적으로 service-to-service 접근 제어를 수행하며, 클러스터 외부/내부 트래픽 모두 제어할 수 있다.
Cilium은 eBPF 기반 L7 필터링 기능을 제공하며 요청 단위(Path, Method, Header 등)까지 제어할 수 있는 L7 정책을 정의할 수 있다.
예를 들어 /admin 경로는 차단하고 /api 경로만 허용하는 세밀한 접근 제어가 가능하며, L7 로깅 및 메트릭을 통해 서비스 간 호출 패턴을 관찰하고, 보안 이상 탐지에도 활용한다.
기존 사이드카 방식과 달리, Transparent Proxy(TPROXY)를 활용해 Pod 네트워크 스택에서 바로 L7 트래픽을 가로챈다.
이 방식은 iptables/nftables 레벨에서 L7 프록시로 트래픽을 리다이렉션하는 것이 아니라, eBPF 프로그램이 직접 TPROXY를 통해 커널 내부에서 트래픽을 전달한다.
결과적으로 사이드카 없이도 L7 레벨 정책과 관찰 기능을 적용할 수 있으며, CPU/메모리 오버헤드가 크게 줄어든다.
특히, Envoy와 같은 별도의 프록시 프로세스를 모든 Pod에 배치하지 않고 노드 단위 공유 L7 프록시를 활용할 수 있다는 점에서 성능 및 관리 효율성이 크다.
ingressClassName: cilium 을 사용하여 표준 Kubernetes Ingress 리소스를 지원한다.
Path 기반 라우팅과 TLS Termination을 제공하며, 하위 호환성을 위해 kubernetes.io/ingress.class: cilium 주석(annotation)도 지원한다.
Ingress 컨트롤러는 LoadBalancer 타입의 Service를 생성하므로, 환경에서 LoadBalancer 지원이 필요하다. (필요 시 NodePort 또는 HostNetwork 모드로도 노출 가능)
Dedicated: Ingress 리소스별로 전용 LoadBalancer를 생성한다. (충돌 방지에 유리)
Shared: 모든 Ingress 리소스가 하나의 LoadBalancer를 공유한다. (리소스 절약 가능)
모드 변경 시 새로운 LoadBalancer IP가 할당되므로, 기존 활성 연결이 끊어질 수 있다.
NodePort 활성화: nodePort.enabled=true 또는 kubeProxyReplacement=true 필요
L7 Proxy 활성화: l7Proxy=true (기본값)
Ingress 컨트롤러는 기본적으로 LoadBalancer Service를 생성하므로, NodePort/HostNetwork 대안 구성이 필요할 수 있다.
Envoy는 기본적으로 HTTP 연결의 Source IP를 X-Forwarded-For 헤더에 추가한다.
externalTrafficPolicy와 관계없이, Cilium Ingress는 항상 TPROXY를 사용하여 Envoy에 전달하므로 Source IP가 유지된다.
즉, 일반적인 Ingress 컨트롤러와 달리 externalTrafficPolicy: Local 설정이 없어도 클라이언트 IP를 확인할 수 있다.
Cilium Ingress를 정확히 이해하기 위해서는 TPROXY에 대한 개념이해가 필요하다.
TPROXY란 커널 레벨에서 원래 목적지 IP/Port를 바꾸지 않고 소켓에 트래픽을 직접 할당할 수 있는 기능을 의미한다.
일반 REDIRECT(DNAT)와 달리, 클라이언트가 보낸 원래 목적지 정보(IP/Port)가 유지되므로, 백엔드로 원래 목적지 정보를 그대로 전달할 수 있다.
Envoy 같은 L7 프록시가 들어오는 트래픽을 목적지 주소 그대로 전달할 수 있기 때문에 L7 레벨 정책/라우팅을 적용할 수 있다.
TPROXY를 통해 트래픽이 전달되는 과정에 대해 확인해보자.
L7 정책이 걸린 패킷이 들어오면 Cilium에 의해 MARK_MAGIC_TO_PROXY 즉, 0x0200가 마킹된다.
//cilium/bpf/lib/proxy.h
#ifdef ENABLE_TPROXY
if (!from_host)
ctx->mark |= MARK_MAGIC_TO_PROXY;
else
#endif
ctx->mark = MARK_MAGIC_TO_PROXY | proxy_port << 16;
cilium_dbg_capture(ctx, DBG_CAPTURE_PROXY_PRE, proxy_port);
//cilium/bpf/lib/common.h
#define MARK_MAGIC_TO_PROXY 0x0200
Iptables를 통해 마킹된 패킷을 Cilium Host Proxy로 Redirection한다.
Node에서 확인해보면 pod에서 나가는 egress 트래픽의 경우, Cilium proxy port인 35531로 Iptables에 의해 패킷이 보내짐을 알 수 있다.
(⎈|HomeLab:N/A) root@k8s-ctr:~# iptables -t mangle -S | grep -i proxy
...
-A CILIUM_PRE_mangle -p tcp -m mark --mark 0xfb820200 -m comment --comment "cilium: TPROXY to host cilium-dns-egress proxy" -j TPROXY --on-port 33531 --on-ip 127.0.0.1 --tproxy-mark 0x200/0xffffffff
-A CILIUM_PRE_mangle -p udp -m mark --mark 0xfb820200 -m comment --comment "cilium: TPROXY to host cilium-dns-egress proxy" -j TPROXY --on-port 33531 --on-ip 127.0.0.1 --tproxy-mark 0x200/0xffffffff
(⎈|HomeLab:N/A) root@k8s-ctr:~# cat /var/run/cilium/state/proxy_ports_state.json
{"cilium-dns-egress":{"type":"dns","ingress":false,"port":33531}}
(⎈|HomeLab:N/A) root@k8s-ctr:~# ss -nltup | grep cilium
udp UNCONN 0 0 127.0.0.1:33531 0.0.0.0:* users:(("cilium-agent",pid=5430,fd=48))
아래 실습에서 확인을 해보겠지만, cilium-ingress 활성화 상태에서 cilium클래스의 Ingress리소스를 생성하면 cilium daemonset에 의해, cilium-ingress관련 iptables 규칙이 새롭게 생성된다.
(⎈|HomeLab:N/A) root@k8s-ctr:~# sudo iptables -t mangle -L CILIUM_PRE_mangle --line-numbers -n -v
Chain CILIUM_PRE_mangle (1 references)
num pkts bytes target prot opt in out source destination
1 0 0 MARK 0 -- * !lo 0.0.0.0/0 0.0.0.0/0 socket --transparent mark match ! 0xe00/0xf00 mark match ! 0x800/0xf00 /* cilium: any->pod redirect proxied traffic to host proxy */ MARK set 0x200
2 0 0 TPROXY 6 -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0xfb820200 /* cilium: TPROXY to host cilium-dns-egress proxy */ TPROXY redirect 127.0.0.1:33531 mark 0x200/0xffffffff
3 0 0 TPROXY 17 -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0xfb820200 /* cilium: TPROXY to host cilium-dns-egress proxy */ TPROXY redirect 127.0.0.1:33531 mark 0x200/0xffffffff
4 0 0 TPROXY 6 -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x17380200 /* cilium: TPROXY to host kube-system/cilium-ingress/listener proxy */ TPROXY redirect 127.0.0.1:14359 mark 0x200/0xffffffff
5 0 0 TPROXY 17 -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x17380200 /* cilium: TPROXY to host kube-system/cilium-ingress/listener proxy */ TPROXY redirect 127.0.0.1:14359 mark 0x200/0xffffffff
redirect port에 대해 확인을 해보면 cilium-envoy의 port이다.
(⎈|HomeLab:N/A) root@k8s-ctr:~# ss -nltup | grep cilium | grep 14359
tcp LISTEN 0 4096 127.0.0.1:14359 0.0.0.0:* users:(("cilium-envoy",pid=15257,fd=63))
tcp LISTEN 0 4096 127.0.0.1:14359 0.0.0.0:* users:(("cilium-envoy",pid=15257,fd=56))
tcp LISTEN 0 4096 127.0.0.1:14359 0.0.0.0:* users:(("cilium-envoy",pid=15257,fd=55))
tcp LISTEN 0 4096 127.0.0.1:14359 0.0.0.0:* users:(("cilium-envoy",pid=15257,fd=52))
Ingress로 들어오는 트래픽의 경우, Iptables Rule에 의해 cilium-envoy로 패킷이 보내진다.
현재 연결되어 있는 소켓이 없다면 신규로 튜플을 구성하여 커널에서 조회한다.
//cilium/bpf/lib/proxy.h
static __always_inline int \
NAME(struct __ctx_buff *ctx, const CT_TUPLE_TYPE * ct_tuple, \
__be16 proxy_port, void *tproxy_addr) \
{ \
struct bpf_sock_tuple *tuple = (struct bpf_sock_tuple *)ct_tuple; \
__u8 nexthdr = ct_tuple->nexthdr; \
__u32 len = sizeof(tuple->SK_FIELD); \
__u16 port; \
int result; \
... \
/* 로컬에 연결된 Socket이 없다면 새로운 TPROXY Socket을 할당한다. */ \
/* 목적지 포트를 proxy_port 즉 cilium proxy port로 설정한다. */
tuple->SK_FIELD.dport = proxy_port; \
/* 출발지 포트는 wildcard처리를 한다. */
tuple->SK_FIELD.sport = 0; \
/* 목적지 주소를 TPROXY 소켓이 바인딩된 로컬 IP로 지정한다. 일반적으로 127.0.0.1 혹은 ::1 같은 루프백 주소로 들어온다. */
memcpy(&tuple->SK_FIELD.daddr, tproxy_addr, sizeof(tuple->SK_FIELD.daddr)); \
/* 출발지 주소는 wildcard처리를 한다. */
memset(&tuple->SK_FIELD.saddr, 0, sizeof(tuple->SK_FIELD.saddr)); \
cilium_dbg3(ctx, DBG_LOOKUP_CODE, \
tuple->SK_FIELD.SADDR_DBG, tuple->SK_FIELD.DADDR_DBG, \
combine_ports(tuple->SK_FIELD.dport, tuple->SK_FIELD.sport)); \
result = assign_socket(ctx, tuple, len, nexthdr, false); \
if (result == CTX_ACT_OK) \
goto out; \
...
}
구성한 튜플을 기반으로 커널에서 TRPROXY 리스닝 소켓을 조회한 후, 패킷을 소켓에 직접 바인딩한다.
즉, Cilium에 의해 해당 패킷이 iptables REDIRECT/DNAT없이 TPROXY 소켓에 직접 바인딩 된다. 이 단계에서 커널 TCP/IP 스택을 거치지 않고 Envoy 소켓으로 직접 바인딩되므로, NAT 없이 원래 목적지 IP/Port를 유지한 채로 L7 처리가 가능하다.
//cilium/bpf/lib/proxy.h
assign_socket_tcp(struct __ctx_buff *ctx,
struct bpf_sock_tuple *tuple, __u32 len, bool established)
{
int result = DROP_PROXY_LOOKUP_FAILED;
struct bpf_sock *sk;
__u32 dbg_ctx;
/* tuple과 일치하는 TCP 소켓을 커널에서 조회한다.*/
sk = skc_lookup_tcp(ctx, tuple, len, BPF_F_CURRENT_NETNS, 0);
if (!sk)
goto out;
...
/* 패킷을 소켓에 직접 바인딩한다. */
result = sk_assign(ctx, sk, 0);
...
cilium 설치 명령어
helm install cilium cilium/cilium --version $2 --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 directRoutingSkipUnreachable=true \
--set kubeProxyReplacement=true --set bpf.masquerade=true --set installNoConntrackIptablesRules=true \
--set endpointHealthChecking.enabled=false --set healthChecking=false \
--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=30003 \
--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}" \
--set ingressController.enabled=true --set ingressController.loadbalancerMode=shared --set loadBalancer.l7.backend=envoy \
--set localRedirectPolicy=true --set l2announcements.enabled=true \
--set operator.replicas=1 --set debug.enabled=true >/dev/null 2>&1
k8s ingress support 설정 확인을 해보자.
Cilium을 설치할 때 ingressController를 true로 선택을 하였기 때문에 각 워커 노드별로 Cilium이 Ingress 용도로 예약한 IP가 존재한다.
이 IP는 실제 Pod가 가진 IP가 아니라, Cilium 에이전트가 관리하는 가상 IP이며, Ingress로 들어온 트래픽이 Envoy(L7 Proxy)로 전달될 때 식별 목적으로 사용된다.
(⎈|HomeLab:N/A) root@k8s-ctr:~# cilium config view | grep -E '^loadbalancer|l7'
enable-l7-proxy true
loadbalancer-l7 envoy
loadbalancer-l7-algorithm round_robin
loadbalancer-l7-ports
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl exec -it -n kube-system ds/cilium -- cilium ip list | grep ingress
172.20.0.147/32 reserved:ingress
172.20.1.202/32 reserved:ingress
Cilium Envoy는 hostNetwork로 각 노드에 한 대씩 daemonset으로 뜨며, 각 노드의 9964 포트를 listen하고 있다.
Cilium Envoy 서비스의 Endpoint로 Cilium Envoy pod의 9964포트가 잡혀있다.
Envoy는 Cilium Agent와 연결되며 admin.sock 유닉스 소켓을 통해 Cilium Agent ↔ Envoy 간 상태 조회/제어가 가능하다.
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl get svc,ep -n kube-system cilium-envoy
Warning: v1 Endpoints is deprecated in v1.33+; use discovery.k8s.io/v1 EndpointSlice
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/cilium-envoy ClusterIP None <none> 9964/TCP 18h
NAME ENDPOINTS AGE
endpoints/cilium-envoy 192.168.10.100:9964,192.168.10.101:9964 18h
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl get pod -n kube-system -l k8s-app=cilium-envoy -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
cilium-envoy-hw9kw 1/1 Running 0 17h 192.168.10.100 k8s-ctr <none> <none>
cilium-envoy-nvvw8 1/1 Running 0 17h 192.168.10.101 k8s-w1 <none> <none>
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl describe pod -n kube-system -l k8s-app=cilium-envoy
...
Volumes:
envoy-sockets:
Type: HostPath (bare host directory volume)
Path: /var/run/cilium/envoy/sockets
HostPathType: DirectoryOrCreate
envoy-artifacts:
Type: HostPath (bare host directory volume)
Path: /var/run/cilium/envoy/artifacts
HostPathType: DirectoryOrCreate
envoy-config:
Type: ConfigMap (a volume populated by a ConfigMap)
Name: cilium-envoy-config
Optional: false
bpf-maps:
Type: HostPath (bare host directory volume)
Path: /sys/fs/bpf
HostPathType: DirectoryOrCreate
...
(⎈|HomeLab:N/A) root@k8s-ctr:~# ls -al /var/run/cilium/envoy/sockets
total 0
drwxr-xr-x 3 root root 120 Aug 22 19:30 .
drwxr-xr-x 4 root root 80 Aug 22 19:29 ..
srw-rw---- 1 root 1337 0 Aug 22 19:30 access_log.sock
srwxr-xr-x 1 root root 0 Aug 22 19:29 admin.sock
drwxr-xr-x 3 root root 60 Aug 22 19:30 envoy
srw-rw---- 1 root 1337 0 Aug 22 19:30 xds.sock
(⎈|HomeLab:N/A) root@k8s-ctr:/var/run/cilium/envoy/sockets# kubectl exec -it -n kube-system ds/cilium-envoy -- cat /var/run/cilium/envoy/bootstrap-config.json > config.json
(⎈|HomeLab:N/A) root@k8s-ctr:~# cat config.json | jq
"connectTimeout": "2s",
"loadAssignment": {
"clusterName": "/envoy-admin",
"endpoints": [
{
"lbEndpoints": [
{
"endpoint": {
"address": {
"pipe": {
"path": "/var/run/cilium/envoy/sockets/admin.sock"
}
}
}
}
]
}
]
},
"name": "/envoy-admin",
"type": "STATIC"
}
],
"listeners": [
{
"address": {
"socketAddress": {
"address": "0.0.0.0",
"portValue": 9964
}
Cilium ingress의 경우 외부 트래픽을 받아들이기 위하여 LoadBalancer타입으로 생성된다.
endpoint로 잡힌 192.192.192.192:9999는 외부에서 들어온 패킷이 Cilium eBPF 경로를 타고 Envoy로 리디렉션되기 위한 가상 endpoint로, 실제 이 IP로 통신되지는 않는다.
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl get svc,ep -n kube-system cilium-ingress
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/cilium-ingress LoadBalancer 10.96.132.142 <pending> 80:30861/TCP,443:31103/TCP 18h
NAME ENDPOINTS AGE
endpoints/cilium-ingress 192.192.192.192:9999 18h
노드의 /sys/fs/bpf/cilium은 Cilium이 eBPF 오브젝트들을 올려두는 가상 파일시스템이다.
ControlPlane 노드의 /sys/fs/bpf/cilium 하위 트리 파일을 통해 구성 방식을 확인해보자.
/sys/fs/bpf/cilium
├── devices
...
│ ├── eth0
│ │ └── links
│ │ ├── cil_from_netdev
│ │ └── cil_to_netdev
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl -n kube-system exec -it ds/cilium -- cilium endpoint list
1215 Disabled Disabled 28440 k8s:app.kubernetes.io/name=hubble-ui 172.20.0.36 ready
k8s:app.kubernetes.io/part-of=cilium
k8s:io.cilium.k8s.namespace.labels.kubernetes.io/metadata.name=kube-system
k8s:io.cilium.k8s.policy.cluster=default
k8s:io.cilium.k8s.policy.serviceaccount=hubble-ui
k8s:io.kubernetes.pod.namespace=kube-system
k8s:k8s-app=hubble-ui
...
devices/eth0/links/cil_* 등은 노드 네트워크 인터페이스 입출구에 붙은 eBPF hook으로, Cilium이 커널 네트워크 스택의 RX/TX 경로에 끼어들어 외부에서 들어오는 패킷을 Envoy로 보내기 위해 TPROXY 동작을 삽입한다.
(예시 - 외부 클라이언트가 Ingress LB IP로 접속하면 eth0 RX → cil_from_netdev → eBPF → Envoy(TPROXY) 흐름을 탐.)
외부에서 Cilium-Ingress를 통해 호출하는 테스트를 위해 LB IPAM 및 L2 Announcement 설정을 추가한다.
cat << EOF | kubectl apply -f -
apiVersion: "cilium.io/v2"
kind: CiliumLoadBalancerIPPool
metadata:
name: "cilium-lb-ippool"
spec:
blocks:
- start: "192.168.10.211"
stop: "192.168.10.215"
EOF
cat << EOF | kubectl apply -f -
apiVersion: "cilium.io/v2alpha1"
kind: CiliumL2AnnouncementPolicy
metadata:
name: policy1
spec:
interfaces:
- eth1
externalIPs: true
loadBalancerIPs: true
EOF
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl get svc,ep cilium-ingress -n kube-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/cilium-ingress LoadBalancer 10.96.132.142 192.168.10.211 80:30861/TCP,443:31103/TCP 18h
NAME ENDPOINTS AGE
endpoints/cilium-ingress 192.192.192.192:9999 18h
#현재 L2 Announcement설정에 의한 리더 노드 = k8s-w1
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl -n kube-system get lease | grep "cilium-l2announce"
cilium-l2announce-kube-system-cilium-ingress k8s-w1 55s
외부와 내부 모두에서 cilium-ingress의 외부 IP를 통해 통신이 가능하다.
(⎈|HomeLab:N/A) root@k8s-ctr:~# LBIP=$(kubectl get svc -n kube-system cilium-ingress -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
(⎈|HomeLab:N/A) root@k8s-ctr:~# echo $LBIP
192.168.10.211
(⎈|HomeLab:N/A) root@k8s-ctr:~# arping -i eth1 $LBIP -c 2
ARPING 192.168.10.211
60 bytes from 08:00:27:b0:11:69 (192.168.10.211): index=0 time=249.708 usec
60 bytes from 08:00:27:b0:11:69 (192.168.10.211): index=1 time=315.533 usec
--- 192.168.10.211 statistics ---
2 packets transmitted, 2 packets received, 0% unanswered (0 extra)
root@router:~# LBIP=192.168.10.211
root@router:~# arping -i eth1 $LBIP -c 2
ARPING 192.168.10.211
60 bytes from 08:00:27:b0:11:69 (192.168.10.211): index=0 time=533.017 usec
60 bytes from 08:00:27:b0:11:69 (192.168.10.211): index=1 time=270.677 usec
^C
--- 192.168.10.211 statistics ---
2 packets transmitted, 2 packets received, 0% unanswered (0 extra)
rtt min/avg/max/std-dev = 0.271/0.402/0.533/0.131 ms
Istio 예제로 유명한 bookinfo로 실습을 진행한다.
배포 후 확인을 해보면 Istio 실습과는 다르게 Sidecar Container가 없다.
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl get pod,svc,ep
NAME READY STATUS RESTARTS AGE
pod/details-v1-766844796b-bfgg4 1/1 Running 0 53s
pod/productpage-v1-54bb874995-579qn 1/1 Running 0 53s
pod/ratings-v1-5dc79b6bcd-csprv 1/1 Running 0 53s
pod/reviews-v1-598b896c9d-dnqln 1/1 Running 0 53s
pod/reviews-v2-556d6457d-46p47 1/1 Running 0 53s
pod/reviews-v3-564544b4d6-b5bps 1/1 Running 0 53s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/details ClusterIP 10.96.159.26 <none> 9080/TCP 53s
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 24h
service/productpage ClusterIP 10.96.28.207 <none> 9080/TCP 53s
service/ratings ClusterIP 10.96.106.180 <none> 9080/TCP 53s
service/reviews ClusterIP 10.96.212.117 <none> 9080/TCP 53s
NAME ENDPOINTS AGE
endpoints/details 172.20.1.142:9080 53s
endpoints/kubernetes 192.168.10.100:6443 24h
endpoints/productpage 172.20.1.154:9080 53s
endpoints/ratings 172.20.1.21:9080 53s
endpoints/reviews 172.20.1.139:9080,172.20.1.186:9080,172.20.1.26:9080 53s
Ingress를 배포하여 통신을 확인해본다.
Ingress IP는 cilium-ingress 서비스의 외부 IP로 지정된다.
(⎈|HomeLab:N/A) root@k8s-ctr:~# cat << EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: basic-ingress
namespace: default
spec:
ingressClassName: cilium
rules:
- http:
paths:
- backend:
service:
name: details
port:
number: 9080
path: /details
pathType: Prefix
- backend:
service:
name: productpage
port:
number: 9080
path: /
pathType: Prefix
EOF
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
basic-ingress cilium * 192.168.10.211 80 74s
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl get svc -n kube-system cilium-ingress
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
cilium-ingress LoadBalancer 10.96.132.142 192.168.10.211 80:30861/TCP,443:31103/TCP 24h
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl describe ingress
...
Address: 192.168.10.211
Ingress Class: cilium
Rules:
Host Path Backends
---- ---- --------
*
/details details:9080 (172.20.1.142:9080)
/ productpage:9080 (172.20.1.154:9080)
(⎈|HomeLab:N/A) root@k8s-ctr:~# LBIP=$(kubectl get svc -n kube-system cilium-ingress -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
(⎈|HomeLab:N/A) root@k8s-ctr:~# echo $LBIP
192.168.10.211
(⎈|HomeLab:N/A) root@k8s-ctr:~# curl -so /dev/null -w "%{http_code}\n" http://$LBIP/
200
(⎈|HomeLab:N/A) root@k8s-ctr:~# curl -so /dev/null -w "%{http_code}\n" http://$LBIP/details/1
200
(⎈|HomeLab:N/A) root@k8s-ctr:~# curl -so /dev/null -w "%{http_code}\n" http://$LBIP/ratings
404
(⎈|HomeLab:N/A) root@k8s-ctr:~# hubble observe -f -t l7
Aug 23 10:52:09.731: 192.168.10.200:43330 (ingress) -> default/productpage-v1-54bb874995-579qn:9080 (ID:49314) http-request FORWARDED (HTTP/1.1 GET http://192.168.10.211/)
Aug 23 10:52:09.732: 192.168.10.200:43330 (ingress) <- default/productpage-v1-54bb874995-579qn:9080 (ID:49314) http-response FORWARDED (HTTP/1.1 200 2ms (GET http://192.168.10.211/))
Aug 23 10:52:37.857: 192.168.10.200:43452 (ingress) -> default/details-v1-766844796b-bfgg4:9080 (ID:20097) http-request FORWARDED (HTTP/1.1 GET http://192.168.10.211/details/1)
Aug 23 10:52:37.860: 192.168.10.200:43452 (ingress) <- default/details-v1-766844796b-bfgg4:9080 (ID:20097) http-response FORWARDED (HTTP/1.1 200 3ms (GET http://192.168.10.211/details/1))
Aug 23 10:53:11.823: 192.168.10.200:47462 (ingress) -> default/productpage-v1-54bb874995-579qn:9080 (ID:49314) http-request FORWARDED (HTTP/1.1 GET http://192.168.10.211/ratings)
Aug 23 10:53:11.832: 192.168.10.200:47462 (ingress) <- default/productpage-v1-54bb874995-579qn:9080 (ID:49314) http-response FORWARDED (HTTP/1.1 404 10ms (GET http://192.168.10.211/ratings))
Pod가 뜨는 WorkerNode에서 veth 트래픽을 캡처하여 TPROXY로 인해 Client IP가 보존되는 것을 확인해본다.
root@k8s-w1:~# PROID=172.20.1.154
root@k8s-w1:~# ip route | grep $PROID
172.20.1.154 dev lxc24276eae3fab proto kernel scope link
root@k8s-w1:~# PROVETH=lxc24276eae3fab
root@k8s-w1:~# ngrep -tW byline -d $PROVETH '' 'tcp port 9080'
lxc24276eae3fab: no IPv4 address assigned: Cannot assign requested address
interface: lxc24276eae3fab
filter: ( tcp port 9080 ) and ((ip || ip6) || (vlan && (ip || ip6)))
####
T 2025/08/23 19:56:14.533041 10.0.2.15:56674 -> 172.20.1.154:9080 [AP] #4
GET / HTTP/1.1.
host: 192.168.10.211.
user-agent: curl/8.5.0.
accept: */*.
x-forwarded-for: 192.168.10.200. # -> XFF에 client-ip가 담김.
x-forwarded-proto: http.
x-envoy-internal: true.
x-request-id: eabadda9-e958-4038-8446-aa5c5807c527.
.
##
T 2025/08/23 19:56:14.548321 172.20.1.154:9080 -> 10.0.2.15:56674 [AP] #6
HTTP/1.1 200 OK.
Server: gunicorn.
Date: Sat, 23 Aug 2025 10:56:14 GMT.
Connection: keep-alive.
Content-Type: text/html; charset=utf-8.
Content-Length: 2080.
...
추가로 Ingress로 들어오는 패킷이 TPROXY를 타고 cilium-envoy로 향하는 과정을 확인할 수 있다.
WorkerNode의 mangle IPTABLES를 확인하면 ingress로 들어오는 트래픽은 127.0.0.1의 10061로 향하라는 규칙이 있음을 확인할 수 있는데, 이는 cilium-envoy의 TCP 포트이다.
root@k8s-w1:~# sudo iptables -t mangle -L CILIUM_PRE_mangle --line-numbers -n -v
Chain CILIUM_PRE_mangle (1 references)
num pkts bytes target prot opt in out source destination
...
4 0 0 TPROXY 6 -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x4d270200 /* cilium: TPROXY to host kube-system/cilium-ingress/listener proxy */ TPROXY redirect 127.0.0.1:10061 mark 0x200/0xffffffff
5 0 0 TPROXY 17 -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x4d270200 /* cilium: TPROXY to host kube-system/cilium-ingress/listener proxy */ TPROXY redirect 127.0.0.1:10061 mark 0x200/0xffffffff
root@k8s-w1:~# ss -nltup | grep 10061
tcp LISTEN 0 4096 127.0.0.1:10061 0.0.0.0:* users:(("cilium-envoy",pid=11198,fd=63))
tcp LISTEN 0 4096 127.0.0.1:10061 0.0.0.0:* users:(("cilium-envoy",pid=11198,fd=56))
tcp LISTEN 0 4096 127.0.0.1:10061 0.0.0.0:* users:(("cilium-envoy",pid=11198,fd=55))
tcp LISTEN 0 4096 127.0.0.1:10061 0.0.0.0:* users:(("cilium-envoy",pid=11198,fd=52))
외부에서 LB를 호출시키면 IPTABLES의 TPROXY to host kube-system/cilium-ingress/listener proxy 규칙의 pkts가 하나씩 증가함을 확인할 수 있다.
root@k8s-w1:~# sudo iptables -t mangle -L CILIUM_PRE_mangle --line-numbers -n -v
Chain CILIUM_PRE_mangle (1 references)
num pkts bytes target prot opt in out source destination
...
4 0 0 TPROXY 6 -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x4d270200 /* cilium: TPROXY to host kube-system/cilium-ingress/listener proxy */ TPROXY redirect 127.0.0.1:10061 mark 0x200/0xffffffff
root@router:~# curl -so /dev/null -w "%{http_code}\n" http://$LBIP/
200
root@k8s-w1:~# sudo iptables -t mangle -L CILIUM_PRE_mangle --line-numbers -n -v
Chain CILIUM_PRE_mangle (1 references)
num pkts bytes target prot opt in out source destination
...
4 1 60 TPROXY 6 -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x4d270200 /* cilium: TPROXY to host kube-system/cilium-ingress/listener proxy */ TPROXY redirect 127.0.0.1:10061 mark 0x200/0xffffffff
이번에는 Ingress를 dedicated 모드로 생성해본다. Dedicated모드로 생성하게 되면 Ingress 리소스 별로 전용 LoadBalancer가 생성된다.
# 샘플 애플리케이션 배포
cat << EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: webpod
spec:
replicas: 2
selector:
matchLabels:
app: webpod
template:
metadata:
labels:
app: webpod
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- sample-app
topologyKey: "kubernetes.io/hostname"
containers:
- name: webpod
image: traefik/whoami
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: webpod
labels:
app: webpod
spec:
selector:
app: webpod
ports:
- protocol: TCP
port: 80
targetPort: 80
type: ClusterIP
EOF
# k8s-ctr 노드에 curl-pod 파드 배포
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: curl-pod
labels:
app: curl
spec:
nodeName: k8s-ctr
containers:
- name: curl
image: nicolaka/netshoot
command: ["tail"]
args: ["-f", "/dev/null"]
terminationGracePeriodSeconds: 0
EOF
(⎈|HomeLab:N/A) root@k8s-ctr:~# cat << EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: webpod-ingress-nginx
namespace: default
spec:
ingressClassName: nginx
rules:
- host: nginx.webpod.local
http:
paths:
- backend:
service:
name: webpod
port:
number: 80
path: /
pathType: Prefix
EOF
ingress.networking.k8s.io/webpod-ingress created
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
basic-ingress cilium * 192.168.10.211 80 19m
webpod-ingress cilium * 192.168.10.212 80 14s
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl get svc,ep cilium-ingress-webpod-ingress
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/cilium-ingress-webpod-ingress LoadBalancer 10.96.181.231 192.168.10.212 80:31255/TCP,443:31157/TCP 28s
NAME ENDPOINTS AGE
endpoints/cilium-ingress-webpod-ingress 192.192.192.192:9999 28s
#현재 L2 Announcement설정에 의한 리더 노드 = k8s-ctr
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl get lease -n kube-system | grep ingress
cilium-l2announce-default-cilium-ingress-webpod-ingress k8s-ctr 60s
위 실습과 동일하게 Pod가 뜨는 Node에서 veth 트래픽을 캡처하여 TPROXY로 인해 Client IP가 보존되는 것을 확인해본다.
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl get pod -l app=webpod -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
webpod-697b545f57-8qgpp 1/1 Running 0 76s 172.20.0.192 k8s-ctr <none> <none>
webpod-697b545f57-gkzn6 1/1 Running 0 76s 172.20.1.230 k8s-w1 <none> <none>
(⎈|HomeLab:N/A) root@k8s-ctr:~# ip link | grep 172.20.0.192
(⎈|HomeLab:N/A) root@k8s-ctr:~# WEBIP=172.20.0.192
(⎈|HomeLab:N/A) root@k8s-ctr:~# ip route | grep $WEBIP
172.20.0.192 dev lxc02bb82001369 proto kernel scope link
(⎈|HomeLab:N/A) root@k8s-ctr:~# WPODVETH=lxc02bb82001369
root@router:~# LB2IP=192.168.10.212
root@router:~# curl -so /dev/null -w "%{http_code}\n" http://$LB2IP/
200
(⎈|HomeLab:N/A) root@k8s-ctr:~# ngrep -tW byline -d $WPODVETH '' 'tcp port 80'
lxc02bb82001369: no IPv4 address assigned: Cannot assign requested address
interface: lxc02bb82001369
filter: ( tcp port 80 ) and ((ip || ip6) || (vlan && (ip || ip6)))
...
T 2025/08/23 21:40:53.991963 172.20.0.192:80 -> 10.0.2.15:36594 [AP] #6
HTTP/1.1 200 OK.
Date: Sat, 23 Aug 2025 12:40:53 GMT.
Content-Length: 341.
Content-Type: text/plain; charset=utf-8.
.
Hostname: webpod-697b545f57-8qgpp
IP: 127.0.0.1
IP: ::1
IP: 172.20.0.192
IP: fe80::5c51:1ff:fee8:c410
RemoteAddr: 10.0.2.15:36594
GET / HTTP/1.1.
Host: 192.168.10.212.
User-Agent: curl/8.5.0.
Accept: */*.
X-Envoy-Internal: true.
X-Forwarded-For: 192.168.10.200.
X-Forwarded-Proto: http.
X-Request-Id: 135499d1-ed5d-457b-9815-8b5fb49d63c9.
.
###
여기서 하나 확인할 수 있는 점은 L2 Announcement 설정으로 인해 k8s-w1에 떠있는 파드를 호출하더라도 k8s-ctr을 거쳐서 k8s-w1로 패킷이 향하는 것인데, 이 때문에 RemoteAddr의 모습이 다르다.
리더 노드인 k8s-ctr위의 Pod의 경우 L2 leader Node에서 바로 패킷을 전달하기 때문에, pod가 바라보는 RemoteAddr은 k8s-ctr의 첫 번째 NIC IP이다.
k8s-w1위의 Pod의 경우 L2 leader Node(k8s-ctr)에서 k8s-w1로 패킷이 전달되기 때문에, RemoteAddr이 Leader Node를 거친 src IP 즉, Ingress의 EXT-IP이다.
하지만, TPROXY로 인해 실제 Client IP가 보존되기 때문에 X-Forwarded-For의 값은 외부 노드 Router의 eth1 IP와 동일하다.
# k8s-ctr
(⎈|HomeLab:N/A) root@k8s-ctr:~# ngrep -tW byline -d $WPODVETH '' 'tcp port 80'
...
Hostname: webpod-697b545f57-8qgpp
IP: 127.0.0.1
IP: ::1
IP: 172.20.0.192
IP: fe80::5c51:1ff:fee8:c410
RemoteAddr: 10.0.2.15:36594
GET / HTTP/1.1.
Host: 192.168.10.212.
User-Agent: curl/8.5.0.
Accept: */*.
X-Envoy-Internal: true.
X-Forwarded-For: 192.168.10.200.
X-Forwarded-Proto: http.
X-Request-Id: 135499d1-ed5d-457b-9815-8b5fb49d63c9.
# k8s-w1
root@k8s-w1:~# ngrep -tW byline -d $WPODVETH '' 'tcp port 80'
...
Hostname: webpod-697b545f57-gkzn6
IP: 127.0.0.1
IP: ::1
IP: 172.20.1.230
IP: fe80::8c39:e6ff:fe38:4d79
RemoteAddr: 172.20.0.147:35985
GET / HTTP/1.1.
Host: 192.168.10.212.
User-Agent: curl/8.5.0.
Accept: */*.
X-Envoy-Internal: true.
X-Forwarded-For: 192.168.10.200.
X-Forwarded-Proto: http.
X-Request-Id: 7958092f-8b81-47a1-81c3-31d3e6725660.
ingress-nginx와 cilium Ingress는 공존이 가능하다.
# Ingress-Nginx 컨트롤러 설치
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm install ingress-nginx ingress-nginx/ingress-nginx --create-namespace -n ingress-nginx
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl get ingressclasses.networking.k8s.io
NAME CONTROLLER PARAMETERS AGE
cilium cilium.io/ingress-controller <none> 26h
nginx k8s.io/ingress-nginx <none> 51s
# ingress 설정
cat << EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: webpod-ingress-nginx
namespace: default
spec:
ingressClassName: nginx
rules:
- host: nginx.webpod.local
http:
paths:
- backend:
service:
name: webpod
port:
number: 80
path: /
pathType: Prefix
EOF
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
basic-ingress cilium * 192.168.10.211 80 44m
webpod-ingress cilium * 192.168.10.212 80 24m
webpod-ingress-nginx nginx nginx.webpod.local 192.168.10.213 80 12s
해당 LB를 호출하여 통신 상태를 확인해본다.
k8s-ctr, k8s-w1모두 RemoteAddr이 ingress-nginx-controller의 Pod IP임을 알 수 있다.
root@router:~# LB3IP=192.168.10.213
root@router:~# curl -H "Host: nginx.webpod.local" $LB3IP
# k8s-ctr
(⎈|HomeLab:N/A) root@k8s-ctr:~# ngrep -tW byline -d $WPODVETH '' 'tcp port 80'
Hostname: webpod-697b545f57-8qgpp
IP: 127.0.0.1
IP: ::1
IP: 172.20.0.192
IP: fe80::5c51:1ff:fee8:c410
RemoteAddr: 172.20.1.35:53392
GET / HTTP/1.1.
Host: nginx.webpod.local.
User-Agent: curl/8.5.0.
Accept: */*.
X-Forwarded-For: 192.168.10.200.
X-Forwarded-Host: nginx.webpod.local.
X-Forwarded-Port: 80.
X-Forwarded-Proto: http.
X-Forwarded-Scheme: http.
X-Real-Ip: 192.168.10.200.
X-Request-Id: 02a94cc39febb17e48af05ccff0d4f9e.
X-Scheme: http.
# k8s-w1
root@k8s-w1:~# ngrep -tW byline -d $WPODVETH '' 'tcp port 80'
Hostname: webpod-697b545f57-gkzn6
IP: 127.0.0.1
IP: ::1
IP: 172.20.1.230
IP: fe80::8c39:e6ff:fe38:4d79
RemoteAddr: 172.20.1.35:35976
GET / HTTP/1.1.
Host: nginx.webpod.local.
User-Agent: curl/8.5.0.
Accept: */*.
X-Forwarded-For: 192.168.10.200.
X-Forwarded-Host: nginx.webpod.local.
X-Forwarded-Port: 80.
X-Forwarded-Proto: http.
X-Forwarded-Scheme: http.
X-Real-Ip: 192.168.10.200.
X-Request-Id: 7adfa99642ab225d4dd4c4c22b3d8408.
X-Scheme: http.
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl get pod -n ingress-nginx -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
ingress-nginx-controller-67bbdf7d8d-f9bmk 1/1 Running 0 15m 172.20.1.35 k8s-w1 <none> <none>
TPROXY를 타는지를 확인을 해보면, nginx class의 Ingress의 경우 TPROXY 커널 기능을 사용하지 않는 것을 확인할 수 있다.
# nginx class Ingress 호출
(⎈|HomeLab:N/A) root@k8s-ctr:~# sudo iptables -t mangle -L CILIUM_PRE_mangle --line-numbers -n -v
Chain CILIUM_PRE_mangle (1 references)
...
4 0 0 TPROXY 6 -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x17380200 /* cilium: TPROXY to host kube-system/cilium-ingress/listener proxy */ TPROXY redirect 127.0.0.1:14359 mark 0x200/0xffffffff
6 0 0 TPROXY 6 -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x94480200 /* cilium: TPROXY to host default/cilium-ingress-default-webpod-ingress/listener proxy */ TPROXY redirect 127.0.0.1:18580 mark 0x200/0xffffffff
root@router:~# curl -H "Host: nginx.webpod.local" $LB3IP
# pkts 변화 없음.
Chain CILIUM_PRE_mangle (1 references)
...
4 0 0 TPROXY 6 -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x17380200 /* cilium: TPROXY to host kube-system/cilium-ingress/listener proxy */ TPROXY redirect 127.0.0.1:14359 mark 0x200/0xffffffff
6 0 0 TPROXY 6 -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x94480200 /* cilium: TPROXY to host default/cilium-ingress-default-webpod-ingress/listener proxy */ TPROXY redirect 127.0.0.1:18580 mark 0x200/0xffffffff
# cilium class Ingress 호출
root@router:~# curl -so /dev/null -w "%{http_code}\n" http://$LB2IP/
# cilium-ingress listener TPROXY pkts 1 증가
(⎈|HomeLab:N/A) root@k8s-ctr:~# (⎈|HomeLab:N/A) root@k8s-ctr:~# sudo iptables -t mangle -L CILIUM_PRE_mangle --line-numbers -n -v
Chain CILIUM_PRE_mangle (1 references)
...
4 1 60 TPROXY 6 -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x94480200 /* cilium: TPROXY to host default/cilium-ingress-default-webpod-ingress/listener proxy */ TPROXY redirect 127.0.0.1:18580 mark 0x200/0xffffffff
6 0 0 TPROXY 6 -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x17380200 /* cilium: TPROXY to host kube-system/cilium-ingress/listener proxy */ TPROXY redirect 127.0.0.1:14359 mark 0x200/0xffffffff
webpod에 대해 hubble observe로 모니터링을 해본다.
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl exec -n kube-system -c cilium-agent -it ds/cilium -- cilium-dbg endpoint list | grep webpod
3644 Disabled Disabled 59436 k8s:app=webpod 172.20.1.230 ready
# nginx class Ingress 호출
root@router:~# curl -H "Host: nginx.webpod.local" $LB3IP
(⎈|HomeLab:N/A) root@k8s-ctr:~# hubble observe -f --protocol tcp --from-identity 59436
Aug 23 13:43:51.260: ingress-nginx/ingress-nginx-controller-67bbdf7d8d-f9bmk:34564 (ID:47827) <- default/webpod-697b545f57-8qgpp:80 (ID:59436) to-endpoint FORWARDED (TCP Flags: SYN, ACK)
Aug 23 13:43:51.261: default/webpod-697b545f57-8qgpp:80 (ID:59436) <> ingress-nginx/ingress-nginx-controller-67bbdf7d8d-f9bmk (ID:47827) pre-xlate-rev TRACED (TCP)
Aug 23 13:43:51.261: default/webpod-697b545f57-8qgpp:80 (ID:59436) <> ingress-nginx/ingress-nginx-controller-67bbdf7d8d-f9bmk (ID:47827) pre-xlate-rev TRACED (TCP)
Aug 23 13:43:51.272: ingress-nginx/ingress-nginx-controller-67bbdf7d8d-f9bmk:34564 (ID:47827) <- default/webpod-697b545f57-8qgpp:80 (ID:59436) to-endpoint FORWARDED (TCP Flags: ACK, PSH)
Aug 23 13:43:51.337: ingress-nginx/ingress-nginx-controller-67bbdf7d8d-f9bmk:34564 (ID:47827) <- default/webpod-697b545f57-8qgpp:80 (ID:59436) to-network FORWARDED (TCP Flags: SYN, ACK)
Aug 23 13:43:51.344: ingress-nginx/ingress-nginx-controller-67bbdf7d8d-f9bmk:34564 (ID:47827) <- default/webpod-697b545f57-8qgpp:80 (ID:59436) to-network FORWARDED (TCP Flags: ACK, PSH)
특징
# cilium class Ingress 호출
root@router:~# curl -so /dev/null -w "%{http_code}\n" http://$LB2IP/
(⎈|HomeLab:N/A) root@k8s-ctr:~# hubble observe -f --from-identity 59436
# k8s-w1위의 webpod 호출됨
Aug 23 14:29:24.934: 10.0.2.15:59674 (host) <- default/webpod-697b545f57-gkzn6:80 (ID:59436) to-stack FORWARDED (TCP Flags: SYN, ACK)
Aug 23 14:29:24.935: 10.0.2.15:59674 (host) <- default/webpod-697b545f57-gkzn6:80 (ID:59436) to-stack FORWARDED (TCP Flags: ACK, PSH)
Aug 23 14:29:24.935: 192.168.10.200:40780 (ingress) <- default/webpod-697b545f57-gkzn6:80 (ID:59436) http-response FORWARDED (HTTP/1.1 200 1ms (GET http://192.168.10.212/))
Aug 23 14:29:39.941: 10.0.2.15:59674 (host) <- default/webpod-697b545f57-gkzn6:80 (ID:59436) to-stack FORWARDED (TCP Flags: ACK)
Aug 23 14:29:55.301: 10.0.2.15:59674 (host) <- default/webpod-697b545f57-gkzn6:80 (ID:59436) to-stack FORWARDED (TCP Flags: ACK)
Aug 23 14:30:10.661: 10.0.2.15:59674 (host) <- default/webpod-697b545f57-gkzn6:80 (ID:59436) to-stack FORWARDED (TCP Flags: ACK)
Aug 23 14:30:24.937: 10.0.2.15:59674 (host) <- default/webpod-697b545f57-gkzn6:80 (ID:59436) to-stack FORWARDED (TCP Flags: ACK, FIN)
# k8s-ctr위의 webpod 호출됨
Aug 23 14:30:28.342: 192.168.10.200:42700 (ingress) <- default/webpod-697b545f57-8qgpp:80 (ID:59436) http-response FORWARDED (HTTP/1.1 200 4ms (GET http://192.168.10.212/))
Aug 23 14:30:28.416: 172.20.1.202:38801 (ingress) <- default/webpod-697b545f57-8qgpp:80 (ID:59436) to-network FORWARDED (TCP Flags: SYN, ACK)
Aug 23 14:30:28.418: 172.20.1.202:38801 (ingress) <- default/webpod-697b545f57-8qgpp:80 (ID:59436) to-network FORWARDED (TCP Flags: ACK, PSH)
Aug 23 14:30:43.614: 172.20.1.202:38801 (ingress) <- default/webpod-697b545f57-8qgpp:80 (ID:59436) to-network FORWARDED (TCP Flags: ACK)
Aug 23 14:30:58.975: 172.20.1.202:38801 (ingress) <- default/webpod-697b545f57-8qgpp:80 (ID:59436) to-network FORWARDED (TCP Flags: ACK)
Aug 23 14:31:14.334: 172.20.1.202:38801 (ingress) <- default/webpod-697b545f57-8qgpp:80 (ID:59436) to-network FORWARDED (TCP Flags: ACK)
Aug 23 14:31:28.420: 172.20.1.202:38801 (ingress) <- default/webpod-697b545f57-8qgpp:80 (ID:59436) to-network FORWARDED (TCP Flags: ACK, FIN)
1) 외부 요청
2) 요청 패킷 전달 (Envoy -> webpod)
Envoy가 선택한 Pod로 요청 전달
2-1) Backend Pod가 k8s-w1(리더 노드)에 있는 경우
2-2) Backend Pod가 k8s-ctr(리모트 노드)에 있는 경우
3) 응답 패킷 전달 (WebPod → Client)
3-1) Backend Pod가 k8s-w1(리더 노드)에 있는 경우
3-2) Backend Pod가 k8s-ctr(리모트 노드)에 있는 경우