요약 : 이번 편은 ingress / gateway API 는 일반적인 Kubernetes 의 과정과 유사합니다.
cilium 에서 해당 리소스를 ebpf 로 처리해준다는 내용입니다.
Service Mesh (서비스 메시) 란?
분산 애플리케이션 환경은 다양한 마이크로 애플리케이션들이 서로 데이터를 공유하고 클라우드와 온프레미스 경계를 넘나들며 네트워크 통신을 한다. 전통적인 IP 기반 통신을 넘어서 애플리케이션 프로토콜까지 이해해야만 진정한 가시성을 확보할 수 있게 되었다. 서비스 메시는 이러한 환경에서 관측가능성(observability)을 지원하는 인프라스트럭처다. 참고로 네트워크가 메시(mesh) 형태로 구성되어 있어 'mesh' 라고 명명되었다고 한다.
- L7 트래픽 (HTTP, REST, gRPC, WebSocket 등) 을 인식할 수 있어야 한다.
- 네트워크 식별자에 의존하지 않고, 신원을 기반으로 하여 서로를 인증할 수 있다.
- tracing 이나 metric 형태로 어플리케이션 모니터링(관측 가능성)이 가능해야 한다.
- 어플리케이션 코드 변경 없이 사용 가능해야 한다.
cilium 에서는 service mesh 도 자체적으로 지원합니다.

(좌측) 전통적인 서비스 메시의 경우(ambient 모드 X) envoy 사이드카 패턴으로 한 번의 네트워크 스택을 거쳐야 했습니다.
그림을 자세히 보면 envoy 로 트래픽을 가로채서 다시 application 으로 트래픽을 보낸다. 하지만 (우측) cilium 은 ambient 모드와 유사하게 노드마다 공통의 cilium envoy 를 두고 트래픽을 처리합니다. L3/L4 트래픽의 경우, cilium 의 eBPF 로 직접 처리하지만 L7 트래픽은 공통의 envoy 가 처리 후 socket 레벨 단으로 바로 전송합니다.
더 자세하게 실제로 구현한 내용을 보면,
보통 트래픽을 가로채기할 경우 iptables 의 REDIRECT 방법을 이용하긴 하지만, 패킷을 직접 수정하여 목적지 IP 주소가 변경되는 이슈가 존재합니다. 원래 목적지 주소를 유지한 채 패킷을 transparent 하게 전달하려면(NAT 처리하지 않고) TPROXY 를 사용합니다. cilium 은 트래픽이 도착하면 ebpf 코드가 먼저 처리하고 TPROXY 커널 기능을 사용하여 Envoy 로 투명하게 전달합니다.
출처 : https://docs.kernel.org/networking/tproxy.html#redirecting-traffic
kubernetes ingress support, gateway api support, GAMMA support 세 가지 방법을 지원하고 있습니다.
Kubernetes Ingress Support
Cilium Ingress 는 Kubernetes Ingress 의 기능을 서포트하며 dedicated 모드와 shared 모드를 제공합니다. 모드를 변경할 수 있으나, 새로운 로드밸런서의 ip 를 부여받기 때문에 ingress backend 와 활성화 되어 있는 연결이 종료될 수 있습니다.
| 모드 | 설명 |
| dedicated | 각 Ingress 리소스마다 별도의 LoadBalancer 를 생성 |
| shared | 클러스터 내 모든 Ingress 리소스가 하나의 LoadBalancer 를 공유 |
ingress 모드를 사용하려면 nodePort.enabled=true 옵션을 통해 nodePort 를 활성화하고, kubeProxyReplacement=true 옵션을 통해 kube-proxy 를 교체하여 활성화해야 합니다. 만약 L7 Proxy 를 사용하고자 한다면 l7Proxy=true 옵션을 활성화해야 합니다. (default 옵션)

참고로 cilium network policy 가 적용될 때는 ingress 서비스에 들어오기 전과 후 두 번 평가를 하게 됩니다.
설치
# helm 으로 설치 시 아래 옵션 활성화
--set ingressController.enabled=true
--set ingressController.loadbalancerMode=shared
# deploy, daemonset 활성화
$ kubectl -n kube-system rollout restart deployment/cilium-operator
$ kubectl -n kube-system rollout restart ds/cilium
설치 확인은 cilium config view 로 간단히 확인할 수 있습니다.
cilium config view | grep -E '^loadbalancer|l7'
실습
간단하게, external IP 를 발급 받고 외부에서 접근 가능하도록 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

192.168.10.211 IP 를 부여받고, k8s-w1 노드가 리더노드가 되었습니다.
아래 istio 실습 예제를 사용해보면서 cilium ingress support 와 어떻게 다른지 살펴보겠습니다.
kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.26/samples/bookinfo/platform/kube/bookinfo.yaml

다른 서비스 메시들과 다르게 사이드카 컨테이너가 생성되지 않고, ambient 모드와 유사해 보입니다.
이제 ingress 를 생성하여 외부에 노출해보겠습니다.

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
"/" 경로로 올 경우 productpage 서비스로, "/details" 경로로 올 경우 details 서비스로 가도록 ingress 설정하였습니다.
참고로, 생성된 ingress 를 확인해보면 ingress support 를 shared 모드로 설정해두었기 때문에 external-ip 가 ingress ip 와 같다는 것을 볼 수 있습니다.

해당 경로들로 통신을 해보겠습니다. 먼저 내부에서 접근해보겠습니다.
LBIP=$(kubectl get svc -n kube-system cilium-ingress -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo $LBIP
# / 로 통신 -> 성공
curl -so /dev/null -w "%{http_code}\n" http://$LBIP/
# /details 로 통신 -> 성공
curl -so /dev/null -w "%{http_code}\n" http://$LBIP/details/1
# /ratings 로 통신 -> 실패
curl -so /dev/null -w "%{http_code}\n" http://$LBIP/ratings


정상적으로 ingress 정책에 따라 통신이 되고 있습니다. -v 옵션을 주어서 자세히 보면 envoy 서버를 경유해서 x-envoy-upstream-service-time 이라는 특정 헤더가 세팅되는 것을 확인할 수 있습니다. (외부에서 접근하면 x-forwarded-for 헤더로 source-ip 를 기재하게 됩니다.)
다른 ingress 와 공존 여부
공존은 할 수 있으나 cilium-ingress 를 사용하지는 않습니다.
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm install ingress-nginx ingress-nginx/ingress-nginx --create-namespace -n ingress-nginx

helm 으로 nginx ingress 를 설치하면 별도의 nginx ingress 가 생성됩니다. 이후 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

nginx.webpod.local 에 의해 트래픽이 전달되는 것을 확인할 수 있습니다.
dedicated mode
지금까지 shared 모드를 살펴보았으니 dedicated 모드를 살펴보겠습니다.
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
현재 전역적으로 로드밸런서 모드를 shared 모드로 사용하고 있어, ingress.cilium.io/loadbalancer-mode: dedicated 애너테이션을 통해 dedicated 모드로 로드밸런서를 생성합니다.

cilium 클래스로 igress ip 가 하나 더 생긴 것을 볼 수 있습니다. 위에서 마찬가지로 접속하는 클라이언트의 ip 를 x-forwarded-for 헤더에 담아서 통신하게 됩니다.
ingress path type
cilium ingress path type 은 Exact, Prefix, ImplementationSpecific 세 가지를 지원합니다.
Exact 는 정확히 일치할 경우, Prefix 는 접두사처럼 앞에만 일치하는 경우, ImplementationSpecific 는 IngressClass에 따라 달라집니다. 별도 pathType 으로 처리하거나, Prefix 또는 Exact 경로 유형과 같이 동일하게 처리할 수 있습니다.
kubectl apply -f https://raw.githubusercontent.com/cilium/cilium/main/examples/kubernetes/servicemesh/ingress-path-types.yaml
kubectl apply -f https://raw.githubusercontent.com/cilium/cilium/main/examples/kubernetes/servicemesh/ingress-path-types-ingress.yaml
적용하면, 아래와 같이 스펙이 결정됩니다.
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
예상할 수 있듯이, exact 는 경로가 정확히 일치해야 하고, prefix 는 가장 많이 일치하는 접두사의 경로를 라우팅하고, ImplementationSpecific 은 cilium 구현체의 경로에 기반하여 동작합니다.

Kubernetes Gateway API Support
Gateway API 란?
Gateway API 는 Kubernetes 에서 서비스 노출을 위한 차세대 표준으로, Ingress API 보다 더 확장 가능한 인터페이스를 제공한다.
Cilium Gateway API Support 는 Kubernetes Gateway API 에 대한 포괄적인 지원을 제공합니다.
Gateway API 에 대한 내용은 https://kubernetes.io/docs/concepts/services-networking/gateway/ 내용을 참고해주세요.
설치
Kubernetes 1.23 이상에서 Gateway API 관련 CRD 를 설치합니다.
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/v1.2.0/config/crd/standard/gateway.networking.k8s.io_gatewayclasses.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/v1.2.0/config/crd/standard/gateway.networking.k8s.io_gateways.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/v1.2.0/config/crd/standard/gateway.networking.k8s.io_httproutes.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/v1.2.0/config/crd/standard/gateway.networking.k8s.io_referencegrants.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/v1.2.0/config/crd/standard/gateway.networking.k8s.io_grpcroutes.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/v1.2.0/config/crd/experimental/gateway.networking.k8s.io_tlsroutes.yaml
⚠️ 주의 : cilium ingress controller 와 cilium gateway api 는 같이 사용할 수 없습니다.
ingressController.enabled 설정은 비활성화하고, gatewayAPI.enabled 설정은 활성화합니다.
helm upgrade cilium cilium/cilium --version 1.18.1 --namespace kube-system --reuse-values \
--set ingressController.enabled=false --set gatewayAPI.enabled=true
kubectl -n kube-system rollout restart deployment/cilium-operator
kubectl -n kube-system rollout restart ds/cilium
내부적으로 cilium operator 가 gateway api 리소스들을 검증하고, Cilium agent 가 Cilium Envoy Configuration 리소스를 처리하게 됩니다.
실습
cat << EOF | kubectl apply -f -
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: my-gateway
spec:
gatewayClassName: cilium
listeners:
- protocol: HTTP
port: 80
name: web-gw
allowedRoutes:
namespaces:
from: Same
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: http-app-1
spec:
parentRefs:
- name: my-gateway
namespace: default
rules:
- matches:
- path:
type: PathPrefix
value: /details
backendRefs:
- name: details
port: 9080
- matches:
- headers:
- type: Exact
name: magic
value: foo
queryParams:
- type: Exact
name: great
value: example
path:
type: PathPrefix
value: /
method: GET
backendRefs:
- name: productpage
port: 9080
EOF
같은 네임스페이스로 들어오는 80 트래픽에 대해서, /details URL 은 details 서비스 9080 포트로, /?great=example&magic=foo URL 은 productpage 서비스 9080 포트로 가는 Gateway API 를 설정합니다.

조건에 부합하도록 페이로드를 작성해보니 정상 통신되고 있습니다. https 도 테스트해보겠습니다.
1. 로컬 CA(자체 루트 인증기관) 생성
내 로컬에서 만든 인증서를 시스템이 신뢰하도록 설정해주고, 로컬 CA 를 생성한 다음 시스템에 등록하겠습니다.
## mkcert 설치
apt install -y mkcert
## *.cilium.rocks 도메인에 대한 CA 생성
mkcert '*.cilium.rocks'
## 로컬 인증서 신뢰
mkcert -install
## CA 저장 위치 확인
# CA 파일은 $(mkcert -CAROOT)가 가리키는 사용자 데이터 디렉터리에 저장되고 (예: rootCA.pem, rootCA-key.pem) "mkcert -CAROOT" 명령어로 확인합니다.
mkcert -CAROOT
2. 생성한 인증서 기반으로 Secret 객체 생성

cat << EOF | kubectl apply -f -
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: tls-gateway
spec:
gatewayClassName: cilium
listeners:
- name: https-1
protocol: HTTPS
port: 443
hostname: "bookinfo.cilium.rocks"
tls:
certificateRefs:
- kind: Secret
name: demo-cert
- name: https-2
protocol: HTTPS
port: 443
hostname: "webpod.cilium.rocks"
tls:
certificateRefs:
- kind: Secret
name: demo-cert
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: https-app-route-1
spec:
parentRefs:
- name: tls-gateway
hostnames:
- "bookinfo.cilium.rocks"
rules:
- matches:
- path:
type: PathPrefix
value: /details
backendRefs:
- name: details
port: 9080
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: https-app-route-2
spec:
parentRefs:
- name: tls-gateway
hostnames:
- "webpod.cilium.rocks"
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: webpod
port: 80
EOF
위와 같이 tls-gateway 를 생성해주고 테스트 해보겠습니다.

정상 통신됩니다! 이번엔 TLS 를 살펴보겠습니다. TLS 는 SSL Termination (Offloading) 과 SSL Passthrough 모드가 존재합니다.
SSL Termination 은 외부 SSL 트래픽을 복호화하여 내부에 평문 트래픽을 제공하고, SSL Passthrough 모드는 그대로 SSL 트래픽을 제공합니다.

Client → Gateway: HTTPS
Gateway → Pod: HTTP

Client → Gateway: HTTPS
Gateway → Pod: HTTPS
먼저 Nginx ConfigMap 을 생성하고, 그 ConfigMap 으로 nginx 를 deploy 합니다.
cat <<'EOF' > nginx.conf
events {
}
http {
log_format main '$remote_addr - $remote_user [$time_local] $status '
'"$request" $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log;
server {
listen 443 ssl;
root /usr/share/nginx/html;
index index.html;
server_name nginx.cilium.rocks;
ssl_certificate /etc/nginx-server-certs/tls.crt;
ssl_certificate_key /etc/nginx-server-certs/tls.key;
}
}
EOF
kubectl create configmap nginx-configmap --from-file=nginx.conf=./nginx.conf
cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
name: my-nginx
labels:
run: my-nginx
spec:
ports:
- port: 443
protocol: TCP
selector:
run: my-nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
selector:
matchLabels:
run: my-nginx
replicas: 1
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- name: my-nginx
image: nginx
ports:
- containerPort: 443
volumeMounts:
- name: nginx-config
mountPath: /etc/nginx
readOnly: true
- name: nginx-server-certs
mountPath: /etc/nginx-server-certs
readOnly: true
volumes:
- name: nginx-config
configMap:
name: nginx-configmap
- name: nginx-server-certs
secret:
secretName: demo-cert
EOF
TLS Passthrough 모드로 Gateway 를 생성합니다.
cat << EOF | kubectl apply -f -
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: cilium-tls-gateway
spec:
gatewayClassName: cilium
listeners:
- name: https
hostname: "nginx.cilium.rocks"
port: 443
protocol: TLS
tls:
mode: Passthrough
allowedRoutes:
namespaces:
from: All
---
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: TLSRoute
metadata:
name: nginx
spec:
parentRefs:
- name: cilium-tls-gateway
hostnames:
- "nginx.cilium.rocks"
rules:
- backendRefs:
- name: my-nginx
port: 443
EOF

통신 잘 되네요~!
'인프라 > 쿠버네티스' 카테고리의 다른 글
| [cilium] Security (0) | 2025.09.07 |
|---|---|
| [cilium] k8s / cilium 성능 테스트 (0) | 2025.08.30 |
| [cilium] cluster mesh (2) | 2025.08.15 |
| [cilium] BGP 설정 방법 (1) | 2025.08.15 |
| [cilium] cilium 의 LoadBalancer 서비스 (2) | 2025.08.09 |