Kubernetes

Cilium Study [1기] (6주차) - Cilium ServiceMesh

yu3papa 2025. 8. 19. 11:59

Cilium 은 eBPF 기반의 효율적인 네트워킹을 이용하여 Ingress, Gateway API 지원이 가능하며, Istio 같은 Service Mesh 솔루션의 기능도 대체가 가능합니다.

 

이번 블로그에서는 아래 기능들을 학습해 보겠습니다.

  • Cilium을 Ingress Controller로 사용하는 Ingress 리소스 지원
  • Cilium을 Gateway API Controller로 사용하는 Gateway API 리소스 지원
  • Serice Mesh 기능 지원

0. 실습환경 구성

  • 윈도우 11 OS 에서 Vmware Workstation v17을 이용한 Linux 가상머신 2대 + 윈도우 호스트 PC
  • Rocky Linux 9.6 (커널버전 : 5.14.0-570.26.1.el9_6.x86_64)

  • K8S 클러스터를 구성하는 2개의 노드(k8s-cp, k8s-w1)가 같은 네트워크에 존재함
    • 172.31.0.0/20 네트워크
      • VMnet8 - NAT 네트워크
      • 윈도우 HOST 머신에서 접근 가능하고, 인터넷이 되는 네트워크

K8S 클러스터 구성

1단계 - 사전 준비

  • 클러스터의 모든 node 에서 수행 : k8s-cp, k8s-w1
K8S_MV='1.33' # Major Version
K8S_FV='1.33.3' # Full version
CONTAINERD_FV='1.7.27'
NERDCTL_FV='2.1.3'

# hosts 파일 편집
echo "172.31.1.10  k8s-cp" >> /etc/hosts
echo "172.31.1.11  k8s-w1" >> /etc/hosts

# 방화벽 종료 및 해제
systemctl stop firewalld
systemctl disable firewalld

# selinux 해제
setenforce 0
grubby --update-kernel ALL --args selinux=0

# Swap off
swapoff --all
sed -i '/swap/s/^/#/' /etc/fstab

# 필수 패키지 설치
dnf install -y socat

# packets traversing the bridge are processed by iptables for filtering
sysctl -w net.ipv4.ip_forward=1
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.d/k8s.conf

# enable br_netfilter for iptables 
modprobe br_netfilter
modprobe overlay
modprobe iptable_nat
echo "br_netfilter" >> /etc/modules-load.d/k8s.conf
echo "overlay" >> /etc/modules-load.d/k8s.conf
echo "iptable_nat" >> /etc/modules-load.d/k8s.conf

##### containerd 설치
dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
# dnf list containerd.io --showduplicates
dnf install -y containerd.io-$CONTAINERD_FV
# containerd configure to default and cgroup managed by systemd
containerd config default > /etc/containerd/config.toml
sed -i 's/SystemdCgroup = false/SystemdCgroup = true/g' /etc/containerd/config.toml
sed -i --follow-symlinks 's/registry.k8s.io\/pause:3.8/registry.k8s.io\/pause:3.10/g' /etc/containerd/config.toml

### k8s 관련 패키지 설치
cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://pkgs.k8s.io/core:/stable:/v$K8S_MV/rpm/
enabled=1
gpgcheck=1
gpgkey=https://pkgs.k8s.io/core:/stable:/v$K8S_MV/rpm/repodata/repomd.xml.key
exclude=kubelet kubeadm kubectl cri-tools kubernetes-cni
EOF

dnf install -y kubelet-$K8S_FV kubeadm-$K8S_FV kubectl-$K8S_FV --disableexcludes=kubernetes

# ready to install for k8s 
systemctl restart containerd && systemctl enable containerd
systemctl enable --now kubelet

# kubectl 명령어 자동완성 기능 사용하기
dnf install -y bash-completion
kubectl completion bash >/etc/bash_completion.d/kubectl
echo "alias k=kubectl" >> ~/.bashrc
echo "complete -F __start_kubectl k" >> ~/.bashrc
source ~/.bash_profile

# nerdctl 설치
wget https://github.com/containerd/nerdctl/releases/download/v$NERDCTL_FV/nerdctl-$NERDCTL_FV-linux-amd64.tar.gz
tar xfz nerdctl-$NERDCTL_FV-linux-amd64.tar.gz -C /usr/local/bin
rm -fr nerdctl-$NERDCTL_FV-linux-amd64.tar.gz
echo "source <(nerdctl completion bash)" >> ~/.bash_profile
source ~/.bash_profile

# kubecolor 설치
KUBECOLOR_FV='0.0.25'
wget https://github.com/hidetatz/kubecolor/releases/download/v$KUBECOLOR_FV/kubecolor_$KUBECOLOR_FV\_Linux_x86_64.tar.gz
tar xfz kubecolor_$KUBECOLOR_FV\_Linux_x86_64.tar.gz -C /usr/bin/
rm -fr xfz kubecolor_$KUBECOLOR_FV\_Linux_x86_64.tar.gz
echo "alias kc='kubecolor'" >> ~/.bashrc

# helm 설치
HELM_FV='3.17.4'

curl -O https://get.helm.sh/helm-v$HELM_FV-linux-amd64.tar.gz
tar xfz helm-v$HELM_FV-linux-amd64.tar.gz
mv linux-amd64/helm /usr/local/bin
rm -fr helm-v$HELM_FV-linux-amd64.tar.gz linux-amd64
helm completion bash > /etc/bash_completion.d/helm

# cilium cli & hubble cli 설치
CILIUM_CLI_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/cilium-cli/main/stable.txt)
CLI_ARCH=amd64
if [ "$(uname -m)" = "aarch64" ]; then CLI_ARCH=arm64; fi
curl -L --fail --remote-name-all https://github.com/cilium/cilium-cli/releases/download/${CILIUM_CLI_VERSION}/cilium-linux-${CLI_ARCH}.tar.gz >/dev/null 2>&1
tar xzvfC cilium-linux-${CLI_ARCH}.tar.gz /usr/local/bin
rm -fr cilium-linux-${CLI_ARCH}.tar.gz

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 >/dev/null 2>&1
tar xzvfC hubble-linux-${HUBBLE_ARCH}.tar.gz /usr/local/bin
rm -fr hubble-linux-${HUBBLE_ARCH}.tar.gz
  • k8s-cp 에서 k8s-w1, k8s-w0, router 로 SSH key 방식의 인증 구성
# k8s-w1, k8s-w0 SSH key 방식의 인증 구성
ssh-keygen -t rsa -f ~/.ssh/id_rsa -N ""
ssh-copy-id k8s-w1

 

2단계 - 클러스터 구성

  • kubeadm init : k8s-cp
K8S_FV='1.33.3'

kubeadm init \
--kubernetes-version=v$K8S_FV \
--pod-network-cidr=10.244.0.0/16 \
--service-cidr 10.96.0.0/16 \
--apiserver-advertise-address=172.31.1.10 \
--apiserver-cert-extra-sans jadeedu.com \
--cri-socket=unix:///run/containerd/containerd.sock

#  Setting kube config file
mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config
  • kubeadm join : k8s-w1
# k8s-cp 에서 join command 생성
kubeadm token create --print-join-command
kubeadm join 172.31.1.10:6443 --token iv3bdo.o4cjvcacio8k0gec --discovery-token-ca-cert-hash sha256:080eb62adfa0013d48400562edd68835b8545d1b665a0a03a2bc46dee07bc425

# k8s-w1 에서 위의 join command 수행
kubeadm join 172.31.1.10:6443 --token iv3bdo.o4cjvcacio8k0gec --discovery-token-ca-cert-hash sha256:080eb62adfa0013d48400562edd68835b8545d1b665a0a03a2bc46dee07bc425

 

cilium CNI 구성

  • Cilium 버전 : 1.18.1
# Cilium 설치 with Helm
helm repo add cilium https://helm.cilium.io/

CILIUM_FV='1.18.1'

helm install cilium cilium/cilium --version $CILIUM_FV --namespace kube-system \
--set k8sServiceHost=172.31.1.10 --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
# 클러스터 구성후 pod-cidr, service-cidr 값 확인
kubectl cluster-info dump | grep -m 2 -E "cluster-cidr|service-cluster-ip-range"
                            "--service-cluster-ip-range=10.96.0.0/16",
                            "--cluster-cidr=10.244.0.0/16",

# ipam 모드 확인
cilium config view | grep ^ipam
ipam                                              cluster-pool
ipam-cilium-node-update-rate                      15s

# cilium 상태 확인
cilium status
    /¯¯\
 /¯¯\__/¯¯\    Cilium:             OK
 \__/¯¯\__/    Operator:           OK
 /¯¯\__/¯¯\    Envoy DaemonSet:    OK
 \__/¯¯\__/    Hubble Relay:       OK
    \__/       ClusterMesh:        disabled

DaemonSet              cilium                   Desired: 2, Ready: 2/2, Available: 2/2
DaemonSet              cilium-envoy             Desired: 2, Ready: 2/2, Available: 2/2
Deployment             cilium-operator          Desired: 1, Ready: 1/1, Available: 1/1
Deployment             hubble-relay             Desired: 1, Ready: 1/1, Available: 1/1
Deployment             hubble-ui                Desired: 1, Ready: 1/1, Available: 1/1
Containers:            cilium                   Running: 2
                       cilium-envoy             Running: 2
                       cilium-operator          Running: 1
                       clustermesh-apiserver
                       hubble-relay             Running: 1
                       hubble-ui                Running: 1
Cluster Pods:          4/4 managed by Cilium
Helm chart version:    1.18.1
Image versions         cilium             quay.io/cilium/cilium:v1.18.1@sha256:65ab17c052d8758b2ad157ce766285e04173722df59bdee1ea6d5fda7149f0e9: 2
                       cilium-envoy       quay.io/cilium/cilium-envoy:v1.34.4-1754895458-68cffdfa568b6b226d70a7ef81fc65dda3b890bf@sha256:247e908700012f7ef56f75908f8c965215c26a27762f296068645eb55450bda2: 2
                       cilium-operator    quay.io/cilium/operator-generic:v1.18.1@sha256:97f4553afa443465bdfbc1cc4927c93f16ac5d78e4dd2706736e7395382201bc: 1
                       hubble-relay       quay.io/cilium/hubble-relay:v1.18.1@sha256:7e2fd4877387c7e112689db7c2b153a4d5c77d125b8d50d472dbe81fc1b139b0: 1
                       hubble-ui          quay.io/cilium/hubble-ui-backend:v0.13.2@sha256:a034b7e98e6ea796ed26df8f4e71f83fc16465a19d166eff67a03b822c0bfa15: 1
                       hubble-ui          quay.io/cilium/hubble-ui:v0.13.2@sha256:9e37c1296b802830834cc87342a9182ccbb71ffebb711971e849221bd9d59392: 1

cilium config view | grep -E '^loadbalancer|l7'
enable-l7-proxy                                   true
loadbalancer-l7                                   envoy
loadbalancer-l7-algorithm                         round_robin
loadbalancer-l7-ports

 

샘플 어플리케이션 배포

# k8s-cp 노드에도 POD 가 스케줄링 될수 있도록 taint
kubectl taint nodes k8s-cp node-role.kubernetes.io/control-plane-


# k8s-cp 노드에 curl-pod 파드 배포

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: curl-pod
  labels:
    app: curl
spec:
  nodeName: k8s-cp
  containers:
  - name: curl
    image: nicolaka/netshoot
    command: ["tail"]
    args: ["-f", "/dev/null"]
  terminationGracePeriodSeconds: 0
EOF

# 샘플 애플리케이션 배포
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

 

  • 배포 후 네트워크 정보 확인
# IP 확인
kubectl get ciliumendpoints 
NAME                      SECURITY IDENTITY   ENDPOINT STATE   IPV4           IPV6
curl-pod                  17329               ready            172.20.0.30
webpod-697b545f57-4fcf7   34852               ready            172.20.0.175
webpod-697b545f57-nh8nf   34852               ready            172.20.1.55


kubectl exec -n kube-system ds/cilium -- cilium-dbg ip list
IP                IDENTITY                                                                     SOURCE
0.0.0.0/0         reserved:world
172.20.0.0/24     reserved:world
172.20.0.21/32    reserved:remote-node
172.20.0.30/32    k8s:app=curl                                                                 custom-resource
                  k8s:io.ciliuhttp://m.k8s.namespace.labels.kubernetes.io/metadata.name=default
                  k8s:io.ciliuhttp://m.k8s.policy.cluster=default
                  k8s:io.ciliuhttp://m.k8s.policy.serviceaccount=default
                  k8s:io.kubernetes.pod.namespace=default
172.20.0.166/32   reserved:ingress
172.20.0.175/32   k8s:app=webpod                                                               custom-resource
                  k8s:io.ciliuhttp://m.k8s.namespace.labels.kubernetes.io/metadata.name=default
                  k8s:io.ciliuhttp://m.k8s.policy.cluster=default
                  k8s:io.ciliuhttp://m.k8s.policy.serviceaccount=default
                  k8s:io.kubernetes.pod.namespace=default
172.20.1.10/32    reserved:host
172.20.1.23/32    k8s:io.ciliuhttp://m.k8s.namespace.labels.kubernetes.io/metadata.name=kube-system custom-resource
                  k8s:io.ciliuhttp://m.k8s.policy.cluster=default
                  k8s:io.ciliuhttp://m.k8s.policy.serviceaccount=coredns
                  k8s:io.kubernetes.pod.namespace=kube-system
                  k8s:k8s-app=kube-dns
172.20.1.55/32    k8s:app=webpod                                                               custom-resource
                  k8s:io.ciliuhttp://m.k8s.namespace.labels.kubernetes.io/metadata.name=default
                  k8s:io.ciliuhttp://m.k8s.policy.cluster=default
                  k8s:io.ciliuhttp://m.k8s.policy.serviceaccount=default
                  k8s:io.kubernetes.pod.namespace=default
172.20.1.126/32   reserved:ingress
172.20.1.127/32   k8s:app.kubernetes.io/name=hubble-ui                                         custom-resource
                  k8s:app.kubernetes.io/part-of=cilium
                  k8s:io.ciliuhttp://m.k8s.namespace.labels.kubernetes.io/metadata.name=kube-system
                  k8s:io.ciliuhttp://m.k8s.policy.cluster=default
                  k8s:io.ciliuhttp://m.k8s.policy.serviceaccount=hubble-ui
                  k8s:io.kubernetes.pod.namespace=kube-system
                  k8s:k8s-app=hubble-ui
172.20.1.250/32   k8s:io.ciliuhttp://m.k8s.namespace.labels.kubernetes.io/metadata.name=kube-system custom-resource
                  k8s:io.ciliuhttp://m.k8s.policy.cluster=default
                  k8s:io.ciliuhttp://m.k8s.policy.serviceaccount=coredns
                  k8s:io.kubernetes.pod.namespace=kube-system
                  k8s:k8s-app=kube-dns
172.20.1.251/32   k8s:app.kubernetes.io/name=hubble-relay                                      custom-resource
                  k8s:app.kubernetes.io/part-of=cilium
                  k8s:io.ciliuhttp://m.k8s.namespace.labels.kubernetes.io/metadata.name=kube-system
                  k8s:io.ciliuhttp://m.k8s.policy.cluster=default
                  k8s:io.ciliuhttp://m.k8s.policy.serviceaccount=hubble-relay
                  k8s:io.kubernetes.pod.namespace=kube-system
                  k8s:k8s-app=hubble-relay
172.31.1.10/32    reserved:kube-apiserver
                  reserved:remote-node
172.31.1.11/32    reserved:host

# 통신 확인 : 정상
kubectl exec -it curl-pod -- sh -c 'while true; do curl -s --connect-timeout 1 webpod | grep "IP: 172" ; echo "---" ; sleep 1; done'
IP: 172.20.0.175
---
IP: 172.20.1.55
---
IP: 172.20.0.175
---
IP: 172.20.0.175
---

 

1. Cilium Service Mesh

(용어정리) MSA 와 Service Mesh

  • MSA (Micro Service Architecture)
    • 시스템을 여러 개의 독립된 서비스로 나눠서, 이 서비스를 조합함으로써 기능을 제공하는 아키텍쳐 디자인 패턴

이러한 MSA 어플리케이션은 아래와 같은 Death Star 아키텍처의 모습을 보입니다.

위 그림에서 조그만 점 하나가 마이크로서비스입니다. 그리고 마이크로서비스간의 호출관계가 그물망 처럼 얽혀있어서 Service Mesh 환경이라고 명명하였으며 이러한 Service Mesh 환경에는 아래와 같은 문제점을 개발팀의 코딩이 아니라 인프라 레벨에서 해결해야 하는 도전과제가 있습니다.

  • 클라우드에서 마이크로서비스를 운영하는 데는 여러 도전과제가 있다.
    • 몇 가지를 꼽자면 신뢰할 수 없는 네트워크, 서비스 가용성, 이해하기 어려운 트래픽 흐름, 트래픽 암호화, 애플리케이션 상태, 성능 등이 있다.
  • 이런 어려움들은 각 애플리케이션 내에서 라이브러리를 사용해 패턴(서비스 디스커버리 등)들을 구현함으로써 완화된다.
  • 서비스들에 대한 관찰 가능성을 확보할 목적으로 메트릭과 트레이싱을 생성하고 배포하려면 추가적인 라이브러리와 서비스가 필요한다.
  • 서비스 메시는 이런 공통 관심사를 애플리케이션 대신 프로세스 외부에서 투명한 방식으로 구현하는 인프라다.
  • 이스티오는 다음과 같은 요소로 구성된 서비스 메시 구현체다.
    • 데이터 플레인, 애플리케이션과 함께 배포되는 서비스 프록시로 구서오디며, 정책을 구현하고 트래픽을 제어하고 메트릭과 트레이싱을 생성하는 등 애플리케이션을 보완한다.
    • 컨트롤 플레인, 운영자가 데이터 플레인의 네트워크 동작을 제어할 수 있도록 API를 노출한다.
  • 이스티오는 엔보이를 서비스 프록시로 사용하는데, 기능이 다양하고 동적으로 설정할 수 있기 때문이다.

Service Mesh

  • Micro Service Architecture를 적용한 시스템의 내부 통신이 Mesh 네트워크의 형태를 띄는 것에 빗대어 Service Mesh로 명명되었습니다.
  • Service Mesh 는 서비스 간 통신을 추상화하여 안전하고, 빠르고, 신뢰할 수 있게 만드는 전용 InfraStructure Layer입니다.
  • 추상화를 통해 복잡한 내부 네트워크를 제어하고, 추적하고, 내부 네트워크 관련 로직을 추가함으로써 안정성, 신뢰성, 탄력성, 표준화, 가시성, 보안성 등을 확보합니다.
  • Service Mesh 는 URL 경로, 호스트 헤더, API 버전 또는 기타 응용 프로그램 수준 규칙을 기반으로 하는 계층 7 네트워크 Layer 입니다.
  • Service Mesh 의 구현체인 경량화 Proxy를 통해 다양한 Routing Rules, Circuit breaker 등 공통기능을 설정할 수 있습니다. 이는 서비스 간 통신에 연관된 기능 뿐만 아니라, 서비스의 배포 전략에도 도움을 줍니다.

Vendor별 Service Mesh 정의

Service Mesh 솔루션의 대표적인 제품으로 istio 가 있으며, Cilium 을 이용하면 istio 가 하는 기능의 많은 부분을 대체할 수 있습니다.

Cilium Service Mesh

위 링크의 Cilium Service Mesh 의 기능을 보면 아래와 같으며, Istio 가 하고자 하는 일과 거의 동일하다는것을 알수 있습니다.

  • Resilient Connectivity: 
    • Service to service communication must be possible across boundaries such as clouds, clusters, and premises. Communication must be resilient and fault tolerant.
  • L7 Traffic Management: 
    • Load balancing, rate limiting, and resiliency must be L7-aware (HTTP, REST, gRPC, WebSocket, …).
  • Identity-based Security: 
    • Relying on network identifiers to achieve security is no longer sufficient, both the sending and receiving services must be able to authenticate each other based on identities instead of a network identifier.
  • Observability & Tracing: 
    • Observability in the form of tracing and metrics is critical to understanding, monitoring, and troubleshooting application stability, performance, and availability.
  • Transparency: 
    • The functionality must be available to applications in a transparent manner, i.e. without requiring to change application code.
Service Mesh 솔루션의 핵심은 "Business Logic"과 별개인 비기능적인 요소를 인프라레벨의 솔루션이 해줘야 한다는 것입니다.

Why Cilium Service Mesh?

  • L3/L4 수준 프로토콜 : eBPF 처리
    • IP, TCP, UDP와 같은 프로토콜을 포함한 모든 네트워크 처리에 대해 Cilium은 고효율 커널 내부 데이터 경로로 eBPF를 사용.
    • For all network processing including protocols such as IP, TCP, and UDP, Cilium uses eBPF as the highly efficient in-kernel datapath
  • L7 수준 애플리케이션 : cilium-envoy 처리
    • HTTP, Kafka, gRPC, DNS와 같은 애플리케이션 계층 프로토콜은 Envoy와 같은 프록시를 사용하여 파싱.
    • Protocols at the application layer such as HTTP, Kafka, gRPC, and DNS are parsed using a proxy such as Envoy.

2. K8S Ingress Support

K8S 에서 Ingress는 라우팅 규칙일뿐이며, 이러한 라우팅 규칙을 처리하는 Controller가 설치되어야 합니다. 

Cilium 도 Ingress Controller 역할 수행이 가능하며, 아래와 같은 조건으로 Cilium 이 구성되어야 합니다.

Cilium 에서 Ingress 는 2가지 방식을 지원합니다.

  • dedicated: 
    • The Ingress controller will create a dedicated loadbalancer for the Ingress.
  • shared: 
    • The Ingress controller will use a shared loadbalancer for all Ingress resources.
  • How Cilium Ingress and Gateway API differ from other Ingress controllers
    • Cilium의 Ingress 및 Gateway API 지원과 다른 Ingress 컨트롤러 간의 가장 큰 차이점 중 하나는 구현이 CNI와 얼마나 밀접하게 연결되어 있는지입니다. Cilium의 경우 Ingress와 Gateway API는 네트워킹 스택의 일부이므로 다른 Ingress 또는 Gateway API 컨트롤러(심지어 Cilium 클러스터에서 실행되는 다른 Ingress 또는 Gateway API 컨트롤러)와는 다른 방식으로 작동합니다.
    • 다른 Ingress 또는 Gateway API 컨트롤러는 일반적으로 클러스터에 Deployment 또는 데몬셋으로 설치되며, Loadbalancer Service 또는 이와 유사한 서비스를 통해 노출됩니다. (물론 Cilium은 이를 활성화할 수 있습니다).
    • Cilium Ingress 및 Gateway API 구성은 Loadbalancer 또는 NodePort 서비스를 통해 노출되거나, 선택적으로 Host network에서도 노출될 수 있습니다. 이 모든 경우에 트래픽이 서비스의 포트에 도착하면 eBPF 코드가 트래픽을 가로채어 투명하게 Envoy에 전달합니다(TPROXY 커널 기능을 사용하여).
      • 아래는 ChatGPT 내용으로 부정확할 수 있음
        • 패킷이 Kubernetes Service IP:Port에 도착하면, kube-proxy 없이 Cilium의 eBPF가 이를 가로챔.
        • Cilium은 Envoy Proxy를 L7 데이터플레인으로 사용. 하지만 단순 포트포워딩(REDIRECT)이 아니라, 커널의 TPROXY(Transparent Proxy) 기능을 이용함.
          • 커널 netfilter/iptables에서 지원하는 특수 기능
          • ‘투명 프록시’ 역할 : 클라이언트는 원래 목적지로 연결했다고 생각하지만, 사실상 로컬 프록시(여기서는 Envoy)로 트래픽이 리디렉션됨
          • 원본 destination IP/Port, source IP/Port를 유지한 채 소켓 레벨에서 Envoy가 트래픽을 가로채어 처리할 수 있음
            즉, Envoy가 "내가 80/443 포트에서 직접 리스닝한 것처럼" 패킷을 받을 수 있고, 그 과정은 클라이언트 입장에서 완전히 투명합니다.
        • 동작 경로
          • [Client] → [K8s Node:Ingress/Gateway Service Port]
            • → (eBPF Service LB) → (TPROXY) → [Envoy Proxy (Pod)]
            • → (L7 라우팅/정책 처리) → (eBPF) → [Backend Pod]
    • 이는 Client IP visibility 같은 문제에 영향을 미치며, 이는 다른 입력 컨트롤러에 대한 Cilium의 Ingress 또는 Gateway API과 다르게 작동합니다.
      또한 Cilium의 네트워크 정책 엔진이 Ingress에서 들어오는 트래픽 경계와 트래픽에 CiliumNetworkPolicy를 적용할 수 있도록 합니다.

Cilium’s ingress config and CiliumNetworkPolicy

  • 노드별 envoy proxy 에 Network policy 정책 적용할 수 있다.
  • Ingress and Gateway API traffic bound to backend services via Cilium passes through a per-node Envoy proxy.
  • The per-node Envoy proxy has special code that allows it to interact with the eBPF policy engine, and do policy lookups on traffic.
  • This allows Envoy to be a Network Policy enforcement point, both for Ingress (and Gateway API) traffic, and also for east-west traffic via GAMMA or L7 Traffic Management.
  • However, for ingress config, there’s also an additional step. Traffic that arrives at Envoy for Ingress or Gateway API is assigned the special ingress identity in Cilium’s Policy engine. Cilium의 정책 엔진에서 특수 입력 ID가 할당.
  • 클러스터 외부에서 들어오는 트래픽은 일반적으로 클러스터에 (IP CIDR 정책이 없는 한) world identity가 할당됩니다.
  • 이는 실제로 실리움 인그레스에 두 개의 논리적 정책 집행 지점이 있다는 것을 의미합니다. two logical Policy enforcement points.
  • before traffic arrives at the ingress identity, and after, when it is about to exit the per-node Envoy.
  • This means that, when applying Network Policy to a cluster, it’s important to ensure that both steps are allowed, and that traffic is allowed from world to ingress, and from ingress to identities in the cluster (like the productpage identity in the image above). although the same principles also apply for Gateway API. Gateway API도 동일하게 적용된다.

지원되는 인그레스 애너테이션

Cilium K8S Ingress Support 관련 정보 확인

  • Cilium Ingress 와 Cilium GatewayAPI는 동시 활성화 불가능함. 다른 Ingress(nginx) 와는 가능.
# cilium 설치 시 아래 파라미터 적용되어 있음
## --set ingressController.enabled=true
## --set ingressController.loadbalancerMode=shared
## --set loadBalancer.l7.backend=envoy \
cilium config view | grep -E '^loadbalancer|l7'
enable-l7-proxy                                   true
loadbalancer-l7                                   envoy
loadbalancer-l7-algorithm                         round_robin # LB 정책을 구사할 수 있음 (default : RR)
loadbalancer-l7-ports

# ingress 에 예약된 내부 IP 확인 : node(cilium-envoy) 별로 존재
kubectl exec -it -n kube-system ds/cilium -- cilium ip list | grep ingress

172.20.0.166/32   reserved:ingress
172.20.1.126/32   reserved:ingress

# cilium-envoy 확인
kubectl get ds -n kube-system cilium-envoy -owide
NAME           DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR            AGE    CONTAINERS     IMAGES                                                                                                                                                            SELECTOR
cilium-envoy   2         2         2       2            2           kubernetes.io/os=linux   130m   cilium-envoy   quay.io/cilium/cilium-envoy:v1.34.4-1754895458-68cffdfa568b6b226d70a7ef81fc65dda3b890bf@sha256:247e908700012f7ef56f75908f8c965215c26a27762f296068645eb55450bda2   k8s-app=cilium-envoy

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-bb278   1/1     Running   1 (112m ago)   130m   172.31.1.11   k8s-w1   <none>           <none>
cilium-envoy-sdt2d   1/1     Running   1 (113m ago)   130m   172.31.1.10   k8s-cp   <none>           <none>


kc describe pod -n kube-system -l k8s-app=cilium-envoy
...
Containers:
  cilium-envoy:
    Container ID:  containerd://04cf571a901b894869802e2e3ca183b9a4314bd29f08b373fecd8fc13bb6f8b4
    Image:         quay.io/cilium/cilium-envoy:v1.34.4-1754895458-68cffdfa568b6b226d70a7ef81fc65dda3b890bf@sha256:247e908700012f7ef56f75908f8c965215c26a27762f296068645eb55450bda2
    Image ID:      quay.io/cilium/cilium-envoy@sha256:247e908700012f7ef56f75908f8c965215c26a27762f296068645eb55450bda2
    Port:          9964/TCP
    Host Port:     9964/TCP
    Command:
      /usr/bin/cilium-envoy-starter
    Args:
      --
      -c /var/run/cilium/envoy/bootstrap-config.json
      --base-id 0
      --log-level info
    State:       Running

    ...
    Mounts:
      /sys/fs/bpf from bpf-maps (rw)
      /var/run/cilium/envoy/ from envoy-config (ro)
      /var/run/cilium/envoy/artifacts from envoy-artifacts (ro)
      /var/run/cilium/envoy/sockets from envoy-sockets (rw)
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-6gsbl (ro)
...
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

#
ls -al /var/run/cilium/envoy/sockets
total 0
drwxr-xr-x 3 root root 120 Aug 19 15:22 .
drwxr-xr-x 4 root root  80 Aug 19 15:22 ..
srw-rw---- 1 root 1337   0 Aug 19 15:22 access_log.sock
srwxr-xr-x 1 root root   0 Aug 19 15:22 admin.sock
drwxr-xr-x 3 root root  60 Aug 19 15:22 envoy
srw-rw---- 1 root 1337   0 Aug 19 15:22 xds.sock


#
kubectl exec -it -n kube-system ds/cilium-envoy -- ls -al /var/run/cilium/envoy
total 0
drwxrwxrwx 5 root root 121 Aug 19 06:04 .
drwxr-xr-x 3 root root  19 Aug 19 06:22 ..
drwxr-xr-x 2 root root  35 Aug 19 06:04 ..2025_08_19_06_04_08.3101344363
lrwxrwxrwx 1 root root  32 Aug 19 06:04 ..data -> ..2025_08_19_06_04_08.3101344363
drwxr-xr-x 2 root root  40 Aug 19 06:22 artifacts
lrwxrwxrwx 1 root root  28 Aug 19 06:04 bootstrap-config.json -> ..data/bootstrap-config.json
drwxr-xr-x 3 root root 120 Aug 19 06:22 sockets

# envoy 의 config 확인
kubectl exec -it -n kube-system ds/cilium-envoy -- cat /var/run/cilium/envoy/bootstrap-config.json > envoy.json
cat envoy.json | jq
{
  "admin": {
    "address": {
      "pipe": {
        "path": "/var/run/cilium/envoy/sockets/admin.sock"
      }
    }
  },
  "applicationLogConfig": {
    "logFormat": {
      "textFormat": "[%Y-%m-%d %T.%e][%t][%l][%n] [%g:%#] %v"
    }
  },
  "bootstrapExtensions": [
    {
      "name": "envoy.bootstrap.internal_listener",
      "typedConfig": {
        "@type": "type.googleapis.com/envoy.extensions.bootstrap.internal_listener.v3.InternalListener"
      }
    }
  ],
  "dynamicResources": {
    "cdsConfig": {
      "apiConfigSource": {
        "apiType": "GRPC",
        "grpcServices": [
          {
            "envoyGrpc": {
              "clusterName": "xds-grpc-cilium"
            }
          }
        ],
        "setNodeOnFirstMessageOnly": true,
        "transportApiVersion": "V3"
      },
      "initialFetchTimeout": "30s",
      "resourceApiVersion": "V3"
    },
    "ldsConfig": {
      "apiConfigSource": {
        "apiType": "GRPC",
        "grpcServices": [
          {
            "envoyGrpc": {
              "clusterName": "xds-grpc-cilium"
            }
          }
        ],
        "setNodeOnFirstMessageOnly": true,
        "transportApiVersion": "V3"
      },
      "initialFetchTimeout": "30s",
      "resourceApiVersion": "V3"
    }
  },
  "node": {
    "cluster": "ingress-cluster",
    "id": "host~127.0.0.1~no-id~localdomain"
  },
  "overloadManager": {
    "resourceMonitors": [
      {
        "name": "envoy.resource_monitors.global_downstream_max_connections",
        "typedConfig": {
          "@type": "type.googleapis.com/envoy.extensions.resource_monitors.downstream_connections.v3.DownstreamConnectionsConfig",
          "max_active_downstream_connections": "50000"
        }
      }
    ]
  },
  "staticResources": {
    "clusters": [
      {
        "circuitBreakers": {
          "thresholds": [
            {
              "maxRetries": 128
            }
          ]
        },
        "cleanupInterval": "2.500s",
        "connectTimeout": "2s",
        "lbPolicy": "CLUSTER_PROVIDED",
        "name": "ingress-cluster",
        "type": "ORIGINAL_DST",
        "typedExtensionProtocolOptions": {
          "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": {
            "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions",
            "commonHttpProtocolOptions": {
              "idleTimeout": "60s",
              "maxConnectionDuration": "0s",
              "maxRequestsPerConnection": 0
            },
            "useDownstreamProtocolConfig": {}
          }
        }
      },
      {
        "circuitBreakers": {
          "thresholds": [
            {
              "maxRetries": 128
            }
          ]
        },
        "cleanupInterval": "2.500s",
        "connectTimeout": "2s",
        "lbPolicy": "CLUSTER_PROVIDED",
        "name": "egress-cluster-tls",
        "transportSocket": {
          "name": "cilium.tls_wrapper",
          "typedConfig": {
            "@type": "type.googleapis.com/cilium.UpstreamTlsWrapperContext"
          }
        },
        "type": "ORIGINAL_DST",
        "typedExtensionProtocolOptions": {
          "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": {
            "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions",
            "commonHttpProtocolOptions": {
              "idleTimeout": "60s",
              "maxConnectionDuration": "0s",
              "maxRequestsPerConnection": 0
            },
            "upstreamHttpProtocolOptions": {},
            "useDownstreamProtocolConfig": {}
          }
        }
      },
      {
        "circuitBreakers": {
          "thresholds": [
            {
              "maxRetries": 128
            }
          ]
        },
        "cleanupInterval": "2.500s",
        "connectTimeout": "2s",
        "lbPolicy": "CLUSTER_PROVIDED",
        "name": "egress-cluster",
        "type": "ORIGINAL_DST",
        "typedExtensionProtocolOptions": {
          "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": {
            "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions",
            "commonHttpProtocolOptions": {
              "idleTimeout": "60s",
              "maxConnectionDuration": "0s",
              "maxRequestsPerConnection": 0
            },
            "useDownstreamProtocolConfig": {}
          }
        }
      },
      {
        "circuitBreakers": {
          "thresholds": [
            {
              "maxRetries": 128
            }
          ]
        },
        "cleanupInterval": "2.500s",
        "connectTimeout": "2s",
        "lbPolicy": "CLUSTER_PROVIDED",
        "name": "ingress-cluster-tls",
        "transportSocket": {
          "name": "cilium.tls_wrapper",
          "typedConfig": {
            "@type": "type.googleapis.com/cilium.UpstreamTlsWrapperContext"
          }
        },
        "type": "ORIGINAL_DST",
        "typedExtensionProtocolOptions": {
          "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": {
            "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions",
            "commonHttpProtocolOptions": {
              "idleTimeout": "60s",
              "maxConnectionDuration": "0s",
              "maxRequestsPerConnection": 0
            },
            "upstreamHttpProtocolOptions": {},
            "useDownstreamProtocolConfig": {}
          }
        }
      },
      {
        "connectTimeout": "2s",
        "loadAssignment": {
          "clusterName": "xds-grpc-cilium",
          "endpoints": [
            {
              "lbEndpoints": [
                {
                  "endpoint": {
                    "address": {
                      "pipe": {
                        "path": "/var/run/cilium/envoy/sockets/xds.sock"
                      }
                    }
                  }
                }
              ]
            }
          ]
        },
        "name": "xds-grpc-cilium",
        "type": "STATIC",
        "typedExtensionProtocolOptions": {
          "envoy.extensions.upstreams.http.v3.HttpProtocolOptions": {
            "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions",
            "explicitHttpConfig": {
              "http2ProtocolOptions": {}
            }
          }
        }
      },
      {
        "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
          }
        },
        "filterChains": [
          {
            "filters": [
              {
                "name": "envoy.filters.network.http_connection_manager",
                "typedConfig": {
                  "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
                  "httpFilters": [
                    {
                      "name": "envoy.filters.http.router",
                      "typedConfig": {
                        "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router"
                      }
                    }
                  ],
                  "internalAddressConfig": {
                    "cidrRanges": [
                      {
                        "addressPrefix": "10.0.0.0",
                        "prefixLen": 8
                      },
                      {
                        "addressPrefix": "172.16.0.0",
                        "prefixLen": 12
                      },
                      {
                        "addressPrefix": "192.168.0.0",
                        "prefixLen": 16
                      },
                      {
                        "addressPrefix": "127.0.0.1",
                        "prefixLen": 32
                      }
                    ]
                  },
                  "routeConfig": {
                    "virtualHosts": [
                      {
                        "domains": [
                          "*"
                        ],
                        "name": "prometheus_metrics_route",
                        "routes": [
                          {
                            "match": {
                              "prefix": "/metrics"
                            },
                            "name": "prometheus_metrics_route",
                            "route": {
                              "cluster": "/envoy-admin",
                              "prefixRewrite": "/stats/prometheus"
                            }
                          }
                        ]
                      }
                    ]
                  },
                  "statPrefix": "envoy-prometheus-metrics-listener",
                  "streamIdleTimeout": "300s"
                }
              }
            ]
          }
        ],
        "name": "envoy-prometheus-metrics-listener"
      },
      {
        "address": {
          "socketAddress": {
            "address": "127.0.0.1",
            "portValue": 9878
          }
        },
        "filterChains": [
          {
            "filters": [
              {
                "name": "envoy.filters.network.http_connection_manager",
                "typedConfig": {
                  "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
                  "httpFilters": [
                    {
                      "name": "envoy.filters.http.router",
                      "typedConfig": {
                        "@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router"
                      }
                    }
                  ],
                  "internalAddressConfig": {
                    "cidrRanges": [
                      {
                        "addressPrefix": "10.0.0.0",
                        "prefixLen": 8
                      },
                      {
                        "addressPrefix": "172.16.0.0",
                        "prefixLen": 12
                      },
                      {
                        "addressPrefix": "192.168.0.0",
                        "prefixLen": 16
                      },
                      {
                        "addressPrefix": "127.0.0.1",
                        "prefixLen": 32
                      }
                    ]
                  },
                  "routeConfig": {
                    "virtual_hosts": [
                      {
                        "domains": [
                          "*"
                        ],
                        "name": "health",
                        "routes": [
                          {
                            "match": {
                              "prefix": "/healthz"
                            },
                            "name": "health",
                            "route": {
                              "cluster": "/envoy-admin",
                              "prefixRewrite": "/ready"
                            }
                          }
                        ]
                      }
                    ]
                  },
                  "statPrefix": "envoy-health-listener",
                  "streamIdleTimeout": "300s"
                }
              }
            ]
          }
        ],
        "name": "envoy-health-listener"
      }
    ]
  }
}
# envoy configmap 설정 내용 확인
kubectl -n kube-system get configmap cilium-envoy-config
kubectl -n kube-system get configmap cilium-envoy-config -o json \
  | jq -r '.data["bootstrap-config.json"]' \
  | jq .
...
{
  "admin": {
    "address": {
      "pipe": {
        "path": "/var/run/cilium/envoy/sockets/admin.sock"
      }
    }
  },
...
    "listeners": [
      {
        "address": {
          "socketAddress": {
            "address": "0.0.0.0",
            "portValue": 9964
...

tree /sys/fs/bpf
├── cilium
│   ├── devices
│   │   ├── cilium_host
│   │   │   └── links
│   │   │       ├── cil_from_host
│   │   │       └── cil_to_host
│   │   ├── cilium_net
│   │   │   └── links
│   │   │       └── cil_to_host
│   │   └── ens160
│   │       └── links
│   │           ├── cil_from_netdev
│   │           └── cil_to_netdev
│   ├── endpoints
│   │   ├── 359
│   │   │   └── links
│   │   │       ├── cil_from_container
│   │   │       └── cil_to_container
│   │   └── 431
│   │       └── links
│   │           ├── cil_from_container
│   │           └── cil_to_container
│   └── socketlb
│       └── links
│           └── cgroup
│               ├── cil_sock4_connect
│               ├── cil_sock4_getpeername

...

# cilium-envoy 서비스는 Headless Service 임
kubectl get svc,ep -n kube-system cilium-envoy
NAME                   TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
service/cilium-envoy   ClusterIP   None         <none>        9964/TCP   139m

NAME                     ENDPOINTS                           AGE
endpoints/cilium-envoy   172.31.1.10:9964,172.31.1.11:9964   139m

# 클러스터 외부의 트래픽을 인입하기 위한 LoadBalacer -> Shared mode로 동작하고 있음
kubectl get svc,ep -n kube-system cilium-ingress
NAME                     TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)                      AGE
service/cilium-ingress   LoadBalancer   10.96.163.95   <pending>     80:31761/TCP,443:32094/TCP   139m

NAME                       ENDPOINTS              AGE
endpoints/cilium-ingress   192.192.192.192:9999   139m # Cilium → Envoy 간의 제어 채널(Control Plane), 외부 클라이언트가 접근하는 데이터 채널이 아님 by ChatGPT

 

  • LB-IPAM 설정 후 확인 : CiliumL2AnnouncementPolicy
# 현재 L2 Announcement 활성화 상태
cilium config view | grep l2
enable-l2-announcements                           true
enable-l2-neigh-discovery                         false

# BGP 모드를 사용하지 않고, 노드 한곳이 Reader가 되는 L2 Announcement 사용하고 있음
#  k8s 노드와 충돌나지 않는 대역 사용
cat << EOF | kubectl apply -f -
apiVersion: "cilium.io/v2" 
kind: CiliumLoadBalancerIPPool
metadata:
  name: "cilium-lb-ippool"
spec:
  blocks:
  - start: "172.31.15.211"
    stop:  "172.31.15.215"
EOF
ciliumloadbalancerippool.cilium.io/cilium-lb-ippool created

kubectl get ippool
NAME               DISABLED   CONFLICTING   IPS AVAILABLE   AGE
cilium-lb-ippool   false      False         4               49s

kubectl get ippools -o jsonpath='{.items[*].status.conditions[?(@.type!="cilium.io/PoolConflict")]}' | jq
{
  "lastTransitionTime": "2025-08-19T08:35:16Z",
  "message": "5",
  "observedGeneration": 1,
  "reason": "noreason",
  "status": "Unknown",
  "type": "cilium.io/IPsTotal"
}
{
  "lastTransitionTime": "2025-08-19T08:35:16Z",
  "message": "4",
  "observedGeneration": 1,
  "reason": "noreason",
  "status": "Unknown",
  "type": "cilium.io/IPsAvailable"
}
{
  "lastTransitionTime": "2025-08-19T08:35:16Z",
  "message": "1",
  "observedGeneration": 1,
  "reason": "noreason",
  "status": "Unknown",
  "type": "cilium.io/IPsUsed"
}

# L2 Announcement 정책 설정
cat << EOF | kubectl apply -f -
apiVersion: "cilium.io/v2alpha1"
kind: CiliumL2AnnouncementPolicy
metadata:
  name: policy1
spec:
  interfaces:
  - ens160
  externalIPs: true
  loadBalancerIPs: true
EOF
ciliuml2announcementpolicy.cilium.io/policy1 created

# 현재 리더 역할 노드 확인
kubectl -n kube-system get lease | grep "cilium-l2announce"
cilium-l2announce-kube-system-cilium-ingress   k8s-w1

kubectl -n kube-system get lease/cilium-l2announce-kube-system-cilium-ingress -o yaml | yq
apiVersion: coordination.k8s.io/v1
kind: Lease
metadata:
  creationTimestamp: "2025-08-19T08:37:30Z"
  name: cilium-l2announce-kube-system-cilium-ingress
  namespace: kube-system
  resourceVersion: "19082"
  uid: ef5e1e23-9c8a-4f53-ab3a-ebd841d889e0
spec:
  acquireTime: "2025-08-19T08:37:30.304959Z"
  holderIdentity: k8s-w1
  leaseDurationSeconds: 15
  leaseTransitions: 0
  renewTime: "2025-08-19T08:40:10.856518Z"

# K8S 클러스터 내부 LB EX-IP로 호출 가능
LBIP=$(kubectl get svc -n kube-system cilium-ingress -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo $LBIP
172.31.15.211

arping $LBIP -c 2
ARPING 172.31.15.211 from 172.31.1.10 ens160
Unicast reply from 172.31.15.211 [00:0C:29:F3:C0:59]  0.772ms
Unicast reply from 172.31.15.211 [00:0C:29:F3:C0:59]  1.256ms
Sent 2 probes (1 broadcast(s))
Received 2 response(s)

# k8s 외부 노드(윈도우)에서 LB EX-IP로 호출 가능 확인
C:\> ping -n 1 172.31.15.211
C:\> arp -a | findstr 172.31.15.211
  172.31.15.211         00-0c-29-f3-c0-59     동적

 

Ingress HTTP Example : XFF 확인

# 기존의 curl-pod, webpod를 디폴트 네임스페이스에서 삭제
kubectl delete all --all

# Deploy the Demo App : 공식 문서는 release-1.11 로 ARM CPU 에서 실패한다. 1.26 버전을 높여서 샘플 배포 할 것!

kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.26/samples/bookinfo/platform/kube/bookinfo.yaml

# istio 와 다르게 사이드카 컨테이너가 없다 1/1 , NodePort와 LoadBalancer 서비스 없다.
kubectl get pod,svc,ep
NAME                                  READY   STATUS    RESTARTS   AGE
pod/details-v1-766844796b-4zzvd       1/1     Running   0          3m
pod/productpage-v1-54bb874995-rct8k   1/1     Running   0          3m
pod/ratings-v1-5dc79b6bcd-t5ts9       1/1     Running   0          3m
pod/reviews-v1-598b896c9d-5qkk8       1/1     Running   0          3m
pod/reviews-v2-556d6457d-msx2g        1/1     Running   0          3m
pod/reviews-v3-564544b4d6-wlx6x       1/1     Running   0          3m

NAME                  TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
service/details       ClusterIP   10.96.68.140    <none>        9080/TCP   3m
service/kubernetes    ClusterIP   10.96.0.1       <none>        443/TCP    3m6s
service/productpage   ClusterIP   10.96.249.214   <none>        9080/TCP   3m
service/ratings       ClusterIP   10.96.246.152   <none>        9080/TCP   3m
service/reviews       ClusterIP   10.96.112.144   <none>        9080/TCP   3m

NAME                    ENDPOINTS                                             AGE
endpoints/details       172.20.0.124:9080                                     3m
endpoints/kubernetes    172.31.1.10:6443                                      3m6s
endpoints/productpage   172.20.1.181:9080                                     3m
endpoints/ratings       172.20.1.210:9080                                     3m
endpoints/reviews       172.20.0.63:9080,172.20.0.97:9080,172.20.1.180:9080   3m


kc describe ingressclasses.networking.k8s.io
Name:         cilium
Labels:       app.kubernetes.io/managed-by=Helm
Annotations:  meta.helm.sh/release-name: cilium
              meta.helm.sh/release-namespace: kube-system
Controller:   cilium.io/ingress-controller
Events:       <none>

kubectl get ingressclasses.networking.k8s.io
NAME     CONTROLLER                     PARAMETERS   AGE
cilium   cilium.io/ingress-controller   <none>       3h5m

# Basic ingress for istio bookinfo demo application, which can be found in below
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

# Adress 는 cilium-ingress LoadBalancer 의 EX-IP
kubectl get svc -n kube-system cilium-ingress
NAME             TYPE           CLUSTER-IP     EXTERNAL-IP     PORT(S)                      AGE
cilium-ingress   LoadBalancer   10.96.163.95   172.31.15.211   80:31761/TCP,443:32094/TCP   3h6m

kubectl get ingress
NAME            CLASS    HOSTS   ADDRESS         PORTS   AGE
basic-ingress   cilium   *       172.31.15.211   80      45s

kc describe ingress
Name:             basic-ingress
Labels:           <none>
Namespace:        default
Address:          172.31.15.211
Ingress Class:    cilium
Default backend:  <default>
Rules:
  Host        Path  Backends
  ----        ----  --------
  *
              /details   details:9080 (172.20.0.124:9080)
              /          productpage:9080 (172.20.1.181:9080)
Annotations:  <none>
Events:       <none>

# 호출 확인
LBIP=$(kubectl get svc -n kube-system cilium-ingress -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo $LBIP
172.31.15.211

# 호출확인
curl -so /dev/null -w "%{http_code}\n" http://$LBIP/
200
curl -so /dev/null -w "%{http_code}\n" http://$LBIP/details/1
200
curl -so /dev/null -w "%{http_code}\n" http://$LBIP/ratings
404 # <- Ingress 에 매칭되는 조건이 없어서 404 임, 하지만 MSA 내에서 details 마이크로서비스가 호출하기 때문에 웹에서는 정상 화면 출력됨

# Access the Bookinfo application
curl "http://$LBIP/productpage?u=normal"

# 클러스터 외부 (VM을 실행하고 있는 윈도우 호스트)에서 접속확인


# hubble 모니터링


# 호출했을때 envoy 정보 확인
curl -s http://$LBIP/details/1 -v
*   Trying 172.31.15.211:80...
* Connected to 172.31.15.211 (172.31.15.211) port 80 (#0)
> GET /details/1 HTTP/1.1
> Host: 172.31.15.211
> User-Agent: curl/7.76.1
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< content-type: application/json
< server: envoy
< date: Tue, 19 Aug 2025 09:28:02 GMT
< content-length: 178
< x-envoy-upstream-service-time: 2
<
* Connection #0 to host 172.31.15.211 left intact
{"id":1,"author":"William Shakespeare","year":1595,"type":"paperback","pages":200,"publisher":"PublisherA","language":"English","ISBN-10":"1234567890","ISBN-13":"123-1234567890"}

 

dedicated mode

  • Ingress 관련 모드를 shared, dedicated 로 글로벌하게 설정 가능
  • 글로벌 설정을 shared 모드로 한 후에, 특정 ingress 만 dedicated 모드로 설정 가능
    • "ingress.cilium.io/loadbalancer-mode: dedicated" 어노테이션 이용
# 디폴트 네임스페이스에서 curl-pod, webpod 샘플 배포
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: curl-pod
  labels:
    app: curl
spec:
  nodeName: k8s-cp
  containers:
  - name: curl
    image: nicolaka/netshoot
    command: ["tail"]
    args: ["-f", "/dev/null"]
  terminationGracePeriodSeconds: 0
EOF

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

# ingress.cilium.io/loadbalancer-mode 어노테이션을 이용하여 dedicated 모드의 ingress 리소스 생성
cat << EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: webpod-ingress
  namespace: default
  annotations:
    ingress.cilium.io/loadbalancer-mode: dedicated
spec:
  ingressClassName: cilium
  rules:
  - http:
      paths:
      - backend:
          service:
            name: webpod
            port:
              number: 80
        path: /
        pathType: Prefix
EOF

# webpod 용 ingress 리소스가 dedicated 모드로 생성되었음
k get ing
NAME             CLASS    HOSTS   ADDRESS         PORTS   AGE
basic-ingress    cilium   *       172.31.15.211   80      86m
webpod-ingress   cilium   *       172.31.15.212   80      23s

kc describe ingress webpod-ingress
Name:             webpod-ingress
Labels:           <none>
Namespace:        default
Address:          172.31.15.212
Ingress Class:    cilium
Default backend:  <default>
Rules:
  Host        Path  Backends
  ----        ----  --------
  *
              /   webpod:80 (172.20.1.103:80,172.20.0.159:80)
Annotations:  ingress.cilium.io/loadbalancer-mode: dedicated
Events:       <none>

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.30.171   172.31.15.212   80:32606/TCP,443:30831/TCP   2m9s

NAME                                      ENDPOINTS              AGE
endpoints/cilium-ingress-webpod-ingress   192.192.192.192:9999   2m9s

# LB EX-IP에 대한 L2 Announcement 의 Leader 노드 확인
kubectl get lease -n kube-system | grep ingress
cilium-l2announce-default-cilium-ingress-webpod-ingress   k8s-cp
cilium-l2announce-kube-system-cilium-ingress              k8s-w1

# 클러스터 외부 (VM을 실행하고 있는 윈도우 호스트)에서 접속확인


 

Ingress and Network Policy Example

# 클러스터 전체(모든 네임스페이스)에 적용되는 정책 : 
# 클러스터 외부에서 접속 불가 정책 설정
#  --> 참고로 아래 정책 적용 후 Hubble-ui 로 접속 불가!
cat << EOF | kubectl apply -f -
apiVersion: "cilium.io/v2"
kind: CiliumClusterwideNetworkPolicy
metadata:
  name: "external-lockdown"
spec:
  description: "Block all the traffic originating from outside of the cluster"
  endpointSelector: {}
  ingress:
  - fromEntities:
    - cluster
EOF
ciliumclusterwidenetworkpolicy.cilium.io/external-lockdown created

kubectl get ciliumclusterwidenetworkpolicy
NAME                VALID
external-lockdown   True


# 클러스터 안의 POD에서는 Ingress 의 LBIP를 이용하여 접속 가능
kubectl exec curl-pod -- curl --fail -v http://"$LBIP"/details/1
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0*   Trying 172.31.15.211:80...
* Connected to 172.31.15.211 (172.31.15.211) port 80
* using HTTP/1.x
> GET /details/1 HTTP/1.1
> Host: 172.31.15.211
> User-Agent: curl/8.14.1
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 200 OK
< content-type: application/json
< server: envoy
< date: Tue, 19 Aug 2025 10:50:56 GMT
< content-length: 178
< x-envoy-upstream-service-time: 4
<
{ [178 bytes data]
100   178  100   178    0     0  22300      0 --:--:-- --:--:-- --:--:-- 25428
* Connection #0 to host 172.31.15.211 left intact
{"id":1,"author":"William Shakespeare","year":1595,"type":"paperback","pages":200,"publisher":"PublisherA","language":"English","ISBN-10":"1234567890","ISBN-13":"123-1234567890"}

# 클러스터안의 노드에서는 Ingress 의 LBIP를 이용하여 접속 불가능
curl --fail -v http://"$LBIP"/details/1
*   Trying 172.31.15.211:80...
* Connected to 172.31.15.211 (172.31.15.211) port 80 (#0)
> GET /details/1 HTTP/1.1
> Host: 172.31.15.211
> User-Agent: curl/7.76.1
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 403 Forbidden
< content-length: 15
< content-type: text/plain
< date: Tue, 19 Aug 2025 10:52:41 GMT
< server: envoy
* The requested URL returned error: 403
* Closing connection 0
curl: (22) The requested URL returned error: 403

# 클러스터 외부(VM을 호스팅하는 윈도우 PC)에서도 Ingress 의 LBIP를 이용하여 접속 불가능
C:\> curl --fail -v http://172.31.15.211/details/1
*   Trying 172.31.15.211:80...
* Connected to 172.31.15.211 (172.31.15.211) port 80
* using HTTP/1.x
> GET /details/1 HTTP/1.1
> Host: 172.31.15.211
> User-Agent: curl/8.13.0
> Accept: */*
>
< HTTP/1.1 403 Forbidden
< content-length: 15
< content-type: text/plain
< date: Tue, 19 Aug 2025 10:54:15 GMT
< server: envoy
* The requested URL returned error: 403
<
* closing connection #0
curl: (22) The requested URL returned error: 403


# Allow all the traffic originating from a specific CIDR
cat << EOF | kubectl apply -f -
apiVersion: "cilium.io/v2"
kind: CiliumClusterwideNetworkPolicy
metadata:
  name: "allow-cidr"
spec:
  description: "Allow all the traffic originating from a specific CIDR"
  endpointSelector:
    matchExpressions:
    - key: reserved:ingress
      operator: Exists
  ingress:
  - fromCIDRSet:
    # Please update the CIDR to match your environment
    - cidr: 172.31.0.1/32 
    - cidr: 127.0.0.1/32
EOF

# 요청 성공 : k8s-cp , 외부 윈도우 PC 모두 가능
curl --fail -v http://"$LBIP"/details/1
C:\> curl --fail -v http://172.31.15.211/details/1
*   Trying 172.31.15.211:80...
* Connected to 172.31.15.211 (172.31.15.211) port 80
* using HTTP/1.x
> GET /details/1 HTTP/1.1
> Host: 172.31.15.211
> User-Agent: curl/8.13.0
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 200 OK
< content-type: application/json
< server: envoy
< date: Tue, 19 Aug 2025 10:59:44 GMT
< content-length: 178
< x-envoy-upstream-service-time: 3
<
{"id":1,"author":"William Shakespeare","year":1595,"type":"paperback","pages":200,"publisher":"PublisherA","language":"English","ISBN-10":"1234567890","ISBN-13":"123-1234567890"}* Connection #0 to host 172.31.15.211 left intact

# Default Deny Ingress Policy : DNS쿼리와 kube-system내의 파드 제외 to deny all traffic by default
cat << EOF | kubectl apply -f -
apiVersion: cilium.io/v2
kind: CiliumClusterwideNetworkPolicy
metadata:
  name: "default-deny"
spec:
  description: "Block all the traffic (except DNS) by default"
  egress:
  - toEndpoints:
    - matchLabels:
        io.kubernetes.pod.namespace: kube-system
        k8s-app: kube-dns
    toPorts:
    - ports:
      - port: '53'
        protocol: UDP
      rules:
        dns:
        - matchPattern: '*'
  endpointSelector:
    matchExpressions:
    - key: io.kubernetes.pod.namespace
      operator: NotIn
      values:
      - kube-system
EOF
ciliumclusterwidenetworkpolicy.cilium.io/default-deny created

kubectl get ciliumclusterwidenetworkpolicy
NAME                VALID
allow-cidr          True
default-deny        True
external-lockdown   True


# 요청 실패되는것을 확인
curl --fail -v http://"$LBIP"/details/1
C:\> curl --fail -v http://172.31.15.211/details/1
*   Trying 172.31.15.211:80...
* Connected to 172.31.15.211 (172.31.15.211) port 80
* using HTTP/1.x
> GET /details/1 HTTP/1.1
> Host: 172.31.15.211
> User-Agent: curl/8.13.0
> Accept: */*
>
< HTTP/1.1 403 Forbidden
< content-length: 15
< content-type: text/plain
< date: Tue, 19 Aug 2025 11:02:52 GMT
< server: envoy
* The requested URL returned error: 403
<
* closing connection #0
curl: (22) The requested URL returned error: 403

# ingress 를 통해서 인입 시 허용
cat << EOF | kubectl apply -f -
apiVersion: cilium.io/v2
kind: CiliumClusterwideNetworkPolicy
metadata:
  name: allow-ingress-egress
spec:
  description: "Allow all the egress traffic from reserved ingress identity to any endpoints in the cluster"
  endpointSelector:
    matchExpressions:
    - key: reserved:ingress
      operator: Exists
  egress:
  - toEntities:
    - cluster
EOF
kubectl get ciliumclusterwidenetworkpolicy
NAME                   VALID
allow-cidr             True
allow-ingress-egress   True
default-deny           True
external-lockdown      True


curl --fail -v http://"$LBIP"/details/1
C:\> curl --fail -v http://172.31.15.211/details/1
*   Trying 172.31.15.211:80...
* Connected to 172.31.15.211 (172.31.15.211) port 80
* using HTTP/1.x
> GET /details/1 HTTP/1.1
> Host: 172.31.15.211
> User-Agent: curl/8.13.0
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 200 OK
< content-type: application/json
< server: envoy
< date: Tue, 19 Aug 2025 11:04:48 GMT
< content-length: 178
< x-envoy-upstream-service-time: 2
<
{"id":1,"author":"William Shakespeare","year":1595,"type":"paperback","pages":200,"publisher":"PublisherA","language":"English","ISBN-10":"1234567890","ISBN-13":"123-1234567890"}* Connection #0 to host 172.31.15.211 left intact

# 정책 삭제
kubectl delete CiliumClusterwideNetworkPolicy --all
ciliumclusterwidenetworkpolicy.cilium.io "allow-cidr" deleted
ciliumclusterwidenetworkpolicy.cilium.io "allow-ingress-egress" deleted
ciliumclusterwidenetworkpolicy.cilium.io "default-deny" deleted
ciliumclusterwidenetworkpolicy.cilium.io "external-lockdown" deleted

 

Ingress Path Types Example

# Apply the base definitions
kubectl apply -f https://raw.githubusercontent.com/cilium/cilium/main/examples/kubernetes/servicemesh/ingress-path-types.yaml

# 확인
kubectl get -f https://raw.githubusercontent.com/cilium/cilium/main/examples/kubernetes/servicemesh/ingress-path-types.yaml

# Apply the Ingress
kubectl apply -f https://raw.githubusercontent.com/cilium/cilium/main/examples/kubernetes/servicemesh/ingress-path-types-ingress.yaml

# 확인
kc describe ingress multiple-path-types
Name:             multiple-path-types
Labels:           <none>
Namespace:        default
Address:          172.31.15.211
Ingress Class:    cilium
Default backend:  <default>
Rules:
  Host                   Path  Backends
  ----                   ----  --------
  pathtypes.example.com
                         /exact    exactpath:80 (172.20.1.85:3000)
                         /         prefixpath:80 (172.20.1.26:3000)
                         /prefix   prefixpath2:80 (172.20.1.211:3000)
                         /impl     implpath:80 (172.20.0.173:3000)
                         /impl.+   implpath2:80 (172.20.1.63:3000)
Annotations:             <none>
Events:                  <none>

kc get ingress multiple-path-types -o yaml
spec:
  ingressClassName: cilium
  rules:
  - host: pathtypes.example.com
    http:
      paths:
      - backend:
          service:
            name: exactpath
            port:
              number: 80
        path: /exact
        pathType: Exact
      - backend:
          service:
            name: prefixpath
            port:
              number: 80
        path: /
        pathType: Prefix
      - backend:
          service:
            name: prefixpath2
            port:
              number: 80
        path: /prefix
        pathType: Prefix
      - backend:
          service:
            name: implpath
            port:
              number: 80
        path: /impl
        pathType: ImplementationSpecific
      - backend:
          service:
            name: implpath2
            port:
              number: 80
        path: /impl.+
        pathType: ImplementationSpecific

# 호출 확인
export PATHTYPE_IP=`k get ing multiple-path-types -o json | jq -r '.status.loadBalancer.ingress[0].ip'`
echo $PATHTYPE_IP
172.31.15.211

curl -s -H "Host: pathtypes.example.com" http://$PATHTYPE_IP/ | jq
{
  "path": "/",
  "host": "pathtypes.example.com",
  "method": "GET",
  "proto": "HTTP/1.1",
  "headers": {
    "Accept": [
      "*/*"
    ],
    "User-Agent": [
      "curl/7.76.1"
    ],
    "X-Envoy-Internal": [
      "true"
    ],
    "X-Forwarded-For": [
      "172.31.1.10"
    ],
    "X-Forwarded-Proto": [
      "http"
    ],
    "X-Request-Id": [
      "4945a58d-ca60-49cb-b711-e65d32dad962"
    ]
  },
  "namespace": "default",
  "ingress": "",
  "service": "",
  "pod": "prefixpath-5d6b989d4-8mqrz"
}

# 파드명 이름 확인
kubectl get pod | grep path
exactpath-7488f8c6c6-sw6bn        1/1     Running   0          2m36s
implpath-7d8bf85676-b2m4c         1/1     Running   0          2m36s
implpath2-56c97c8556-757wm        1/1     Running   0          2m36s
prefixpath-5d6b989d4-8mqrz        1/1     Running   0          2m36s
prefixpath2-b7c7c9568-dgcc8       1/1     Running   0          2m36s

# Should show prefixpath
curl -s -H "Host: pathtypes.example.com" http://$PATHTYPE_IP/ | grep -E 'path|pod'
 "path": "/",
 "host": "pathtypes.example.com",
 "pod": "prefixpath-5d6b989d4-8mqrz"

# Should show exactpath
curl -s -H "Host: pathtypes.example.com" http://$PATHTYPE_IP/exact | grep -E 'path|pod'
 "path": "/exact",
 "host": "pathtypes.example.com",
 "pod": "exactpath-7488f8c6c6-sw6bn"

# Should show prefixpath2
curl -s -H "Host: pathtypes.example.com" http://$PATHTYPE_IP/prefix | grep -E 'path|pod'
 "path": "/prefix",
 "host": "pathtypes.example.com",
 "pod": "prefixpath2-b7c7c9568-dgcc8"

# Should show implpath
curl -s -H "Host: pathtypes.example.com" http://$PATHTYPE_IP/impl | grep -E 'path|pod'
 "path": "/impl",
 "host": "pathtypes.example.com",
 "pod": "implpath-7d8bf85676-b2m4c"

# Should show implpath2
curl -s -H "Host: pathtypes.example.com" http://$PATHTYPE_IP/implementation | grep -E 'path|pod'
 "path": "/implementation",
 "host": "pathtypes.example.com",
 "pod": "implpath2-56c97c8556-757wm"

# 삭제
kubectl delete -f https://raw.githubusercontent.com/cilium/cilium/main/examples/kubernetes/servicemesh/ingress-path-types.yaml
kubectl delete -f https://raw.githubusercontent.com/cilium/cilium/main/examples/kubernetes/servicemesh/ingress-path-types-ingress.yaml

# ingress 실습 리소스 삭제
kubectl delete ingress basic-ingress webpod-ingress
ingress.networking.k8s.io "basic-ingress" deleted
ingress.networking.k8s.io "webpod-ingress" deleted