[Cilium Study] 1 주 차 - 2. CNI와 Kubernetes 네트워킹에서 이어집니다.
[Cilium Study] 1주차 - 2. CNI와 Kubernetes 네트워킹
이전 포스팅 [Ciliu Study] 1주 차 - 1. 실습 환경 구성에서 이어집니다. [Cilium Study] 1주차 - 1. 실습 환경 구성'가시다'님의 [Cilium Study] 1기의 내용을 정리하는 시리즈 구성의 포스팅을 시작하려고 합니
tech-recipe.tistory.com
iptables의 복잡한 작동 방식
지난 포스팅 말미에 'webpod' Service의 ClusterIP에 대한 라우팅 규칙에 대해서 잠깐 살펴보았습니다. 그 내용을 다시 복기해보면 다음과 같습니다.
# webpod svc의 clsuter ip에 대한 iptables 정보 확인
kubectl get svc webpod -o jsonpath="{.spec.clusterIP}"
SVCIP=$(kubectl get svc webpod -o jsonpath="{.spec.clusterIP}")
iptables -t nat -S | grep $SVCIP
10.96.56.150
-A KUBE-SERVICES -d 10.96.56.150/32 -p tcp -m comment --comment "default/webpod cluster IP" -m tcp --dport 80 -j KUBE-SVC-CNZCPOCNCNOROALA
-A KUBE-SVC-CNZCPOCNCNOROALA ! -s 10.244.0.0/16 -d 10.96.56.150/32 -p tcp -m comment --comment "default/webpod cluster IP" -m tcp --dport 80 -j KUBE-MARK-MASQ
순서 | 출력 문구 | 설명 |
첫 번째 규칙 | -A KUBE-SERIVCE | KUBE-SERVICE라는 체인에 규칙을 추가 |
-d 10.96.56.150/32 | 목적지(-d)가 10.96.56.150인 트래픽을 의미, 여기에서는 webapp service의 Cluster IP를 의미 | |
-p tcp --dport 80 | protocol이 TCP이고 목적지 포트(--port)가 80인 트래픽을 의미 | |
-m commnet --commnet "..." | 규칙에 대한 주석 | |
-j KUBE-SVC-CNZ... | 앞의 규칙을 만족하는 트래픽을 다음 체인(KUBE-SVC-CNZCPOCNCNOROALA)으로 점프 | |
두 번째 규칙 | -A KUBE-SVC-CNZCPOCNCNOROALA | webpod 서비스 전용 체인에 규칙을 추가 |
! -s 10.244.0.0/16 | 출발지 IP가 10.244.0.0/16 대역(Pod CIDR)이 아닌(!) 트래픽을 의미 | |
-j KUBE_MARK_MASQ | Masquerading 표식을 남기라는 의미 |
여기서 'SVC-CNZCPOCNCNOROALA'라는 이름의 iptables 규칙에 대해서 살펴보면 정말 어마어마한 것을 알 수 있습니다. 다음 명령어를 통해 한번 알아보도록 하죠.
# CNZCPOCNCNOROALA 문자열이 들어가는 iptables 규칙 조회
iptables-save | grep CNZCPOCNCNOROALA
:KUBE-SVC-CNZCPOCNCNOROALA - [0:0]
-A KUBE-SERVICES -d 10.96.56.150/32 -p tcp -m comment --comment "default/webpod cluster IP" -m tcp --dport 80 -j KUBE-SVC-CNZCPOCNCNOROALA
-A KUBE-SVC-CNZCPOCNCNOROALA ! -s 10.244.0.0/16 -d 10.96.56.150/32 -p tcp -m comment --comment "default/webpod cluster IP" -m tcp --dport 80 -j KUBE-MARK-MASQ
-A KUBE-SVC-CNZCPOCNCNOROALA -m comment --comment "default/webpod -> 10.244.1.3:80" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-SVF7XOS5TDWJWJZZ
-A KUBE-SVC-CNZCPOCNCNOROALA -m comment --comment "default/webpod -> 10.244.2.3:80" -j KUBE-SEP-UUGX2XES2F42KZPX
여기서 중요하게 볼 것은 바로 위 출력 중 마지막 2개 줄입니다.
- -A KUBE-SVC-CNZCPOCNCNOROALA -m comment --comment "default/webpod -> 10.244.1.3:80" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-SVF7XOS5TDWJWJZZ
- -A KUBE-SVC-CNZCPOCNCNOROALA -m comment --comment "default/webpod -> 10.244.2.3:80" -j KUBE-SEP-UUGX2XES2F42KZPX
각각을 해석해 보면 다음과 같습니다.
- "default/webpod -> 10.244.1.3:80"이라는 주석과 함께 50% 확률로 트래픽을 'KUBE-SEP-SVF7XOS5TDWJWJZZ' 체인으로 점프
- (위 규칙이 적용되지 않은 경우)"default/webpod -> 10.244.2.3:80"이라는 주석과 함께 50% 확률로 트래픽을 'KUBE-SEP-UUGX2XES2F42KZPX' 체인으로 점프
여기서 등장하는 IP 주소인 10.244.1.3과 10.244.2.3은 앞서 생성한 webpod들의 IP 주소입니다. 이것이 의미하는 바는 매우 중요합니다. 바로 Kubernetes에서 Service와 End point Pod들에 대한 로드밸런싱을 이런 규칙들을 순차적으로 적용하면서 이루어진다는 것입니다.
조금 더 깊은 확인을 위해 'KUBE-SEP-SVF7XOS5TDWJWJZZ' 체인 규칙과 'KUBE-SEP-UUGX2XES2F42KZPX' 체인 규칙에 대해서 살펴보면 아래와 같은 것을 볼 수 있습니다.
# 'KUBE-SEP-SVF7XOS5TDWJWJZZ' 체인 규칙
-A KUBE-SEP-SVF7XOS5TDWJWJZZ -s 10.244.1.3/32 -m comment --comment "default/webpod" -j KUBE-MARK-MASQ
-A KUBE-SEP-SVF7XOS5TDWJWJZZ -p tcp -m comment --comment "default/webpod" -m tcp -j DNAT --to-destination 10.244.1.3:80
# 'KUBE-SEP-UUGX2XES2F42KZPX' 체인 규칙
-A KUBE-SEP-UUGX2XES2F42KZPX -s 10.244.2.3/32 -m comment --comment "default/webpod" -j KUBE-MARK-MASQ
-A KUBE-SEP-UUGX2XES2F42KZPX -p tcp -m comment --comment "default/webpod" -m tcp -j DNAT --to-destination 10.244.2.3:80
위 두 개의 체인 규칙 중 하나(50% 확률로 로드 밸런싱 되므로)에 도달한 트래픽은 'KUBE-MARK-MASQ' 체인 규칙을 적용한 후, DNAT을 수행하여 비로소 최종 목적지 Pod(10.244.1.3:80 또는 10.244.2.3:80)에 도달하게 됩니다. 이를 간단히 정리해 보면 아래와 같습니다.
- 목적지 IP가 10.96.56.150/32 이면서 목적지 포트가 80인 트래픽이 'KUBE-SERVICE'라는 체인에 감지
- 'KUBE-SERVICE' 체인의 규칙(Rule)에 따라 'KUBE-SVC-CNZCPOCNCNOROALA' 체인으로 점프
- 패킷은 'KUBE-SVC-CNZCPOCNCNOROALA' 체인에서 확률을 통해 구성된 로드 밸런싱 룰을 거치면서 목적지 체인으로 점프
- 목적지 체인(각 체인은 Service의 endpoint Pod를 가리킴)에서 최종적으로 DNAT을 통해 목적지 Pod로 라우팅
kube-proxy가 iptables를 기반으로 작동하게 된다면, kubernetes에서 움직이는 패킷은 항상 위와 같은 단계(실제로는 조금 더 복잡)를 거치게 됩니다. 조금 더 엄밀한 작동 방식에 대한 이해를 위해서 다음 포스팅의 링크를 남깁니다.
[network] Packet flow in netfilter ( iptables)
TL;DR linux 기반 OS에서 Firewall설정을 이야기하면 iptables에 대한 이야기를 많이 한다. iptables는 packet filtering에 대한 network rule을 만드는 userspace영역의 command line program이다.
velog.io
시간 복잡도 O(n) 문제
앞서 살펴본 바와 같이, iptables를 사용하게 되면 여러 복잡한 단계를 거쳐 패킷이 실제 목적지에 도달하게 됩니다. 그 규칙의 수가 적은 경우에는 이러한 복잡도가 크게 문제가 되지 않지만, kubernetes의 서비스가 10,000개로 늘어나 그 규칙이 추가되면 과연 어떻게 될까요? 당연한 이야기지만, 이때부터는 문제가 발생하기 시작합니다.
iptables의 기본 동작 방식
iptables(netfilter)는 엄밀하게는 user spcae에서 네트워크 규칙을 만드는 명령어입니다. 여기서 생성된 규칙은 netfilter에 의해서 적용되는데, 가장 첫 번째로 일치하는 규칙이 나올 때까지(First Match Winds) 순차적으로 처리되게 됩니다. 바로 이 부분이 iptables(netfilter)의 가장 큰 약점이 되는 부분입니다.
첫 번째로, 어떤 패킷이 iptables에서 생성한 규칙 중 뒤에 위치한 규칙과 일치하는 경우, 해당 규칙을 적용받아야 하는 트래픽의 규칙 검사 시간이 길어질 수 있습니다. 실제로 아래와 같은 명령어를 통해서 규칙의 순서를 확인할 수 있으며, 이것이 실제 문제가 될 수 있다는 것은 쉽게 유추할 수 있습니다.
# 1. 첫 번째 Service 생성
kubectl create service clusterip nginx-service --tcp=80:80
# 2. 두 번째 Service 생성
kubectl create service clusterip api-service --tcp=8080:8080
# 3. 세 번째 Service 생성
kubectl create service clusterip db-service --tcp=5432:5432
# KUBE-SERVICES 체인 확인
iptables -t nat -L KUBE-SERVICES -n --line-numbers
# 결과:
Chain KUBE-SERVICES (2 references)
num target prot opt source destination
1 KUBE-SVC-NPX46M4PTMTKRN6Y 6 -- 0.0.0.0/0 10.96.0.1 /* default/kubernetes:https cluster IP */ tcp dpt:443
2 KUBE-SVC-TCOU7JCQXEZGVUNU 17 -- 0.0.0.0/0 10.96.0.10 /* kube-system/kube-dns:dns cluster IP */ udp dpt:53
3 KUBE-SVC-ERIFXISQEP7F7OF4 6 -- 0.0.0.0/0 10.96.0.10 /* kube-system/kube-dns:dns-tcp cluster IP */ tcp dpt:53
4 KUBE-SVC-JD5MR3NA4I4DYORP 6 -- 0.0.0.0/0 10.96.0.10 /* kube-system/kube-dns:metrics cluster IP */ tcp dpt:9153
5 KUBE-SVC-CNZCPOCNCNOROALA 6 -- 0.0.0.0/0 10.96.56.150 /* default/webpod cluster IP */ tcp dpt:80
6 KUBE-NODEPORTS 0 -- 0.0.0.0/0 0.0.0.0/0 /* kubernetes service nodeports; NOTE: this must be the last rule in this chain */ ADDRTYPE match dst-type LOCAL
게다가, 해당 Service에 endpoint Pod가 많은 경우를 생각해 봅시다. Replica가 20인 nginx Deployment를 만들어 보고, 이를 ClusterIP Service로 노출시킨 후 iptables를 통해 체인을 확인해 보겠습니다.
# replica가 20인 Deployment 생성
kubectl create deployment nginx --image=nginx --replicas 20
deployment.apps/nginx created
# Deployment 노출
kubectl expose deployment nginx --port=80
service/nginx exposed
# nginx Service의 iptables 규칙 확인
kubectl get svc nginx -o jsonpath="{.spec.clusterIP}"
SVCIP=$(kubectl get svc nginx -o jsonpath="{.spec.clusterIP}")
iptables -t nat -S | grep $SVCIP
10.96.246.55-A KUBE-SERVICES -d 10.96.246.55/32 -p tcp -m comment --comment "default/nginx cluster IP" -m tcp --dport 80 -j KUBE-SVC-2CMXP7HKUVJN7L6M
-A KUBE-SVC-2CMXP7HKUVJN7L6M ! -s 10.244.0.0/16 -d 10.96.246.55/32 -p tcp -m comment --comment "default/nginx cluster IP" -m tcp --dport 80 -j KUBE-MARK-MASQ
# 'KUBE-SVC-2CMXP7HKUVJN7L6M' 규칙 조회
iptables-save | grep KUBE-SVC-2CMXP7HKUVJN7L6M
:KUBE-SVC-2CMXP7HKUVJN7L6M - [0:0]
-A KUBE-SERVICES -d 10.96.246.55/32 -p tcp -m comment --comment "default/nginx cluster IP" -m tcp --dport 80 -j KUBE-SVC-2CMXP7HKUVJN7L6M
-A KUBE-SVC-2CMXP7HKUVJN7L6M ! -s 10.244.0.0/16 -d 10.96.246.55/32 -p tcp -m comment --comment "default/nginx cluster IP" -m tcp --dport 80 -j KUBE-MARK-MASQ
-A KUBE-SVC-2CMXP7HKUVJN7L6M -m comment --comment "default/nginx -> 10.244.1.10:80" -m statistic --mode random --probability 0.04999999981 -j KUBE-SEP-WANUSTWS24GIDBO5
-A KUBE-SVC-2CMXP7HKUVJN7L6M -m comment --comment "default/nginx -> 10.244.1.11:80" -m statistic --mode random --probability 0.05263157887 -j KUBE-SEP-WVRGOMK67P4Z4SUM
-A KUBE-SVC-2CMXP7HKUVJN7L6M -m comment --comment "default/nginx -> 10.244.1.12:80" -m statistic --mode random --probability 0.05555555550 -j KUBE-SEP-WX7L4YZKYX75Q2LJ
-A KUBE-SVC-2CMXP7HKUVJN7L6M -m comment --comment "default/nginx -> 10.244.1.13:80" -m statistic --mode random --probability 0.05882352963 -j KUBE-SEP-U3D6P2DGJPD3GTJQ
-A KUBE-SVC-2CMXP7HKUVJN7L6M -m comment --comment "default/nginx -> 10.244.1.4:80" -m statistic --mode random --probability 0.06250000000 -j KUBE-SEP-P33L4LSPB7KVLSK6
-A KUBE-SVC-2CMXP7HKUVJN7L6M -m comment --comment "default/nginx -> 10.244.1.5:80" -m statistic --mode random --probability 0.06666666688 -j KUBE-SEP-IZW656N5ZXYN5BEC
-A KUBE-SVC-2CMXP7HKUVJN7L6M -m comment --comment "default/nginx -> 10.244.1.6:80" -m statistic --mode random --probability 0.07142857136 -j KUBE-SEP-C4VXQDW52UV45WW3
-A KUBE-SVC-2CMXP7HKUVJN7L6M -m comment --comment "default/nginx -> 10.244.1.7:80" -m statistic --mode random --probability 0.07692307699 -j KUBE-SEP-DST4PJJC54MIXJRG
-A KUBE-SVC-2CMXP7HKUVJN7L6M -m comment --comment "default/nginx -> 10.244.1.8:80" -m statistic --mode random --probability 0.08333333349 -j KUBE-SEP-IZ3BWJXID3BT25MP
-A KUBE-SVC-2CMXP7HKUVJN7L6M -m comment --comment "default/nginx -> 10.244.1.9:80" -m statistic --mode random --probability 0.09090909082 -j KUBE-SEP-EX7QVTQRSKU3VWAS
-A KUBE-SVC-2CMXP7HKUVJN7L6M -m comment --comment "default/nginx -> 10.244.2.10:80" -m statistic --mode random --probability 0.10000000009 -j KUBE-SEP-PZD5WGOLWE26ORLM
-A KUBE-SVC-2CMXP7HKUVJN7L6M -m comment --comment "default/nginx -> 10.244.2.11:80" -m statistic --mode random --probability 0.11111111101 -j KUBE-SEP-DJUNAFMAXUZFPIRM
-A KUBE-SVC-2CMXP7HKUVJN7L6M -m comment --comment "default/nginx -> 10.244.2.12:80" -m statistic --mode random --probability 0.12500000000 -j KUBE-SEP-6REO6EX33YRWBWTC
-A KUBE-SVC-2CMXP7HKUVJN7L6M -m comment --comment "default/nginx -> 10.244.2.13:80" -m statistic --mode random --probability 0.14285714272 -j KUBE-SEP-NMXX52BXGUI3RUM3
-A KUBE-SVC-2CMXP7HKUVJN7L6M -m comment --comment "default/nginx -> 10.244.2.4:80" -m statistic --mode random --probability 0.16666666651 -j KUBE-SEP-6TTN4SHIRO4JAVGT
-A KUBE-SVC-2CMXP7HKUVJN7L6M -m comment --comment "default/nginx -> 10.244.2.5:80" -m statistic --mode random --probability 0.20000000019 -j KUBE-SEP-XW6I7J74P22Q2AZM
-A KUBE-SVC-2CMXP7HKUVJN7L6M -m comment --comment "default/nginx -> 10.244.2.6:80" -m statistic --mode random --probability 0.25000000000 -j KUBE-SEP-UOZCNMEBPO5JGMU4
-A KUBE-SVC-2CMXP7HKUVJN7L6M -m comment --comment "default/nginx -> 10.244.2.7:80" -m statistic --mode random --probability 0.33333333349 -j KUBE-SEP-66LGOVHJ4OIBFECU
-A KUBE-SVC-2CMXP7HKUVJN7L6M -m comment --comment "default/nginx -> 10.244.2.8:80" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-LPKO42H7F2UE25RC
-A KUBE-SVC-2CMXP7HKUVJN7L6M -m comment --comment "default/nginx -> 10.244.2.9:80" -j KUBE-SEP-LKN6QCAH6MR2O4YF
보시는 바와 같이 nginx service의 endpoint에 대해서 로드 밸런싱 규칙으로 총 20개의 규칙이 생성된 것을 확인할 수 있습니다. 이처럼 iptables를 기반으로 한 kube-proxy는 service의 수가 많이 지면 많아질수록 그 규칙 역시 선형적으로 늘어나고, 이를 적용받는 패킷은 O(n)의 시간 복잡도 문제를 일으키게 됩니다.
또한, Kubernetes에서 빈번하게 일어나는 Pod 스케일링이나, 새로운 서비스의 생성에 따라 매번 iptables는 새로운 규칙을 생성해야 하고 업데이트해야 합니다. 이것 역시 Kubernetes에 많은 부담을 주게 도비니다.
eBPF와 Cilium
iptables가 가진 문제점을 해결하기 위한 방법 중 하나로 등장하게 된 것이 eBPF를 활용하는 CNI인 Cilium입니다. Cilium은 iptalbes가 가진 시간 복잡도 O(n) 문제와 netfilter 체인의 오버헤드 비용을 eBPF 기술을 통해 해결하고자 합니다. eBPF는 리눅스 커널 기술 중 하나로 BPF(Berkely Patcket Filter)의 확장(extneded)입니다. 네트워크 패킷 캡처 및 분석을 위한 도구인 tcpdump나 Wireshark에서도 사용하는 기술입니다.
eBPF(extended BPF)란?
최초의 BPF는 말 그대로 패킷 필터의 기능을 수행하였으나, 지금의 확장된 BPF(eBPF)는 패킷 필터뿐만 아니라 Linux 커널에서 실행되는 프로그램을 안전하고 효율적으로 개발하고 로드할 수 있는 기술로 발전하였습니다. Kubernetes의 네트워크 관점에서 살펴보면 iptables(netfilter)와 eBPF는 다음과 같이 비교할 수 있습니다.
구분 | iptables | eBPF |
핵심 동작 방식 | 규칙 체인(Chain) 순서 탐색 패킷이 들어오면 PREROUTING → KUBE-SERIVCE → KUBE-SVC-XXX 등 미리 정해진 규칙 목록을 순서대로 따라가며 일치하는 규칙을 찾음 |
이벤트 기반 프로그램 실행 및 맵(Map) 조회 네트워크 인터페이스에 eBPF 프로그램을 부착, 패킷이 도착하면 이 프로그램이 즉시 실행되어, 서비스 IP를 해시맵(BPF Map)에서 한 번에 조회하고 목적지 Pod IP를 찾음 |
성능(규칙 증가 시) | O(n) - 선형적 성능 저하 서비스와 Pod가 많이져 규칙이 늘어나면, 패킷이 일치하는 규칙을 찾기까지 탐색해야 할 목록이 길어져 성능이 저하 |
O(n) - 거의 일정한 성능 해시맵을 사용하여 서비스 수와 관계없이 거의 일정한 속도록 조회가 가능하여 대규모 클러스터 환경에서 성능 저하가 거의 없음 |
유연성 및 자원 | Netfilter 프레임 워크에 종속적 정해진 훅(Hook)과 체인 구조 안에서만 작동 |
높은 프로그래밍 유연성 커널의 다양한 지점에 프로그램을 연결할 수 있어, 네트워킹 뿐만 아니라 보안, 모니터링에도 활용 가능 |
규칙 업데이트 | 전체 테이블 잠금(LooK) 가능성 규칙을 업데이트할 때 테이블 전체에 잠금이 걸릴 수 있어, 동적인 환경(kubernetes)에서 지연을 유발 |
원자적(Atomic) 맵 업데이트 맵의 특정 항목만을 빠르고 안전하게 업데이트하여 동적인 환경에서 더욱 유리 |
Cilium의 라우팅 모드
Cilium은 eBPF 기반의 CNI로 라우팅 여러 가지 라우팅 모드를 지원합니다. 특별한 설정이 없이 배포하면 기본적으로 vxlan을 사용하는 방식으로 배포됩니다. 이는 Flannel과 같은 오버레이 네트워크를 사용하는 것으로 Cilium의 성능을 100% 사용하기에는 다소 부족한 방법입니다. Cilium에서는 Native Routing 모드를 지원하고 있으며, 이를 통해 vxlan의 오버헤드 비용을 감소시킵니다.
# 최초 실습 환경이 아닌 다른 환경에서 구성
# Node IP 대역: 192.168.200.0/24
# Pod CIDR: 10.0.0.0/8
# vxlan을 기반으로 한 라우팅 방식을 사용하는 경우 Routing table
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 192.168.200.1 0.0.0.0 UG 0 0 0 ens160
10.0.0.0 10.0.2.39 255.255.255.0 UG 0 0 0 cilium_host
10.0.1.0 10.0.2.39 255.255.255.0 UG 0 0 0 cilium_host
10.0.2.0 10.0.2.39 255.255.255.0 UG 0 0 0 cilium_host
10.0.2.39 0.0.0.0 255.255.255.255 UH 0 0 0 cilium_host
10.0.3.0 10.0.2.39 255.255.255.0 UG 0 0 0 cilium_host
192.168.200.0 0.0.0.0 255.255.255.0 U 0 0 0 ens160
# native-routing을 기반으로 한 라우팅 방식을 사용하는 경우 Routing table
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 192.168.200.1 0.0.0.0 UG 0 0 0 ens160
10.0.0.0 192.168.200.111 255.255.255.0 UG 0 0 0 ens160
10.0.1.0 192.168.200.112 255.255.255.0 UG 0 0 0 ens160
10.0.2.182 0.0.0.0 255.255.255.255 UH 0 0 0 lxc_health
10.0.3.0 192.168.200.113 255.255.255.0 UG 0 0 0 ens160
192.168.200.0 0.0.0.0 255.255.255.0 U 0 0 0 ens160
위 내용에서 알 수 있듯, vxlan과 같은 오버레이 네트워크를 사용하는 경우에는 vtep과 같은 역할을 하는 cilium_host를 통해 각 노드별 Pod CIDR에 대한 라우팅 정보가 구성되어 있는 것을 확인할 수 있습니다. 이에 반하여 native-routing을 사용하는 경우에는 위와 같이 호소트의 인터페이스를 통해 직접 라우팅 테이블이 구성되어 있는것을 확인할 수 있습니다. 이를 활용하면 Pod의 트래픽이 마치 노드의 네이티브 패킷인 것처럼 전달되어 그 오버헤드 비용을 상당히 줄일 수 있습니다.
이 외에도 Cilium이 가진 장점은 매우 많습니다. 이에 관해서는 별도의 포스팅을 통해 다시 한번 정리하도록 하겠습니다. 그럼 이쯤에서 '[Cilium Study] 1주 차'와 관련된 포스팅 시리즈를 마무리하도록 하겠습니다.
'Study > CS' 카테고리의 다른 글
[Cilium Study] 2주차 - 2. Prometheus와 Grafana로 시스템 모니터링 (14) | 2025.07.27 |
---|---|
[Cilium Study] 2주차 - 1. Cilium Hubble을 통한 네트워크 관측 (8) | 2025.07.27 |
[Cilium Study] 1주차 - 2. CNI와 Kubernetes 네트워킹 (6) | 2025.07.21 |
[Cilium Study] 1주차 - 1. 실습 환경 구성 (8) | 2025.07.19 |
Bootable container, 그리고 bootc (0) | 2025.04.16 |