서비스 디스커버리(Service Discovery)는 분산 시스템의 핵심 동작 원리 중 하나로,
서로 다른 서비스 간의 위치(IP, 포트, 엔드포인트)를 자동으로 탐색하는 메커니즘을 말합니다.
예를 들어, 마이크로서비스 환경에서 frontend가 backend에 요청을 보내려면backend의 위치 정보를 알아야 합니다.
하지만 다음과 같은 문제가 발생합니다.
따라서 고정된 IP에 접근하는 방식은 불가능합니다.
이 문제를 해결하기 위해 자동으로 서비스 위치를 탐색하는 시스템, 즉 서비스 디스커버리가 필요합니다.
전통적인 시스템에서 서비스 디스커버리는 보통 두 가지 방식 중 하나로 구현됩니다.

Kubernetes에서는 클라이언트가 Service Registry 역할을 하는 kube-apiserver (즉, Kubernetes API) 를 직접 조회하여 Pod IP를 얻는 구조로 볼 수 있습니다.
예를 들어, 클라이언트 애플리케이션이 Endpoints 리소스를 조회해 직접 대상 Pod를 선택하거나, 자체 로드밸런싱 로직을 수행할 수도 있습니다.
예시: Custom Client Discovery, Service Mesh Sidecar (Envoy가 직접 서비스 목록 조회)
특징
장점
단점

Kubernetes에서는 Service 리소스가 대표적인 Server-side Discovery 방식입니다.
클라이언트는 ClusterIP나 Service DNS (예: myapp.default.svc.cluster.local)로 요청을 보내면, kube-proxy가 해당 요청을 자동으로 적절한 Pod로 라우팅합니다.
예시: Kubernetes Service (ClusterIP, NodePort, LoadBalancer), Ingress, Istio Gateway
특징
장점
단점
Kubernetes는 위 두 가지 방식을 통합한 형태로 제공합니다.
서비스 디스커버리는 Service 리소스와 kube-dns(CoreDNS)를 통해 이루어집니다.
Service가 Pod 뒤의 로드 밸런서 역할 수행etcd가 모든 Service → Pod 매핑 정보를 저장DNS가 서비스 이름을 ClusterIP로 변환Kubernetes의 전체 흐름은 다음과 같습니다.
[Client Pod] → [DNS 조회] → [ClusterIP(Service)] → [kube-proxy(iptables/IPVS)] → [Pod Endpoint]

Service는 Pod의 동적 IP를 추상화하기 위한 가상 IP(ClusterIP)를 제공합니다.
이는 단순한 가상 엔드포인트가 아니라,
kube-proxy가 iptables 또는 IPVS 규칙을 생성하여 커널 레벨에서 트래픽을 라우팅하는 구조입니다.
apiVersion: v1
kind: Service
metadata:
name: random-generator
spec:
selector:
app: random-generator
ports:
- port: 80
targetPort: 8080
app=random-generator)을 가진 Pod 집합을 선택Pod가 재시작되더라도 Service의 IP는 변하지 않으므로 클라이언트는 항상 동일한 주소로 접근할 수 있습니다.
Service 리소스는 단순한 라우팅 객체가 아니라,
Kubernetes 네트워크 컨트롤 플레인(kube-proxy + CoreDNS + API Server + etcd)이
동적으로 IP 매핑을 유지하는 추상화 계층입니다.
Kubernetes의 초기 버전에서는 Pod가 생성될 때 kubelet이 해당 네임스페이스 내 모든 Service 정보를 환경 변수로 주입했습니다.
apiVersion: v1
kind: Service
metadata:
name: random-generator
namespace: default
spec:
selector:
app: random-generator
ports:
- name: http
port: 80
targetPort: 8080
RANDOM_GENERATOR_SERVICE_HOST=10.96.12.24
RANDOM_GENERATOR_SERVICE_PORT=80
RANDOM_GENERATOR_PORT=tcp://10.96.12.24:80
RANDOM_GENERATOR_PORT_80_TCP_PROTO=tcp
RANDOM_GENERATOR_PORT_80_TCP_ADDR=10.96.12.24
RANDOM_GENERATOR_PORT_80_TCP_PORT=80
이 방식은 정적인 환경에서는 유용했지만 Service 생성과 삭제가 빈번한 동적 환경에서는 부적합했습니다.
따라서 현재는 DNS 기반 탐색 방식이 표준으로 사용됩니다.
Kubernetes는 CoreDNS를 통해 각 Service 이름을 FQDN(Fully Qualified Domain Name) 형태로 등록합니다.
<ServiceName>.<Namespace>.svc.cluster.local
# 예시
random-generator.default.svc.cluster.local
CoreDNS는 etcd를 직접 읽지 않고, kube-apiserver를 watch하여 Service와 EndpointSlice 정보를 인메모리에 유지합니다. 이 정보를 기반으로 DNS 질의에 응답합니다.
클라이언트는 해당 FQDN으로 Service에 접근하며, 내부적으로는 다음 흐름이 작동합니다.
DNS 질의 → CoreDNS 응답(일반: ClusterIP / Headless: Pod IP) → kube-proxy(iptables/IPVS) → Pod 로드밸런싱
$ dig random-generator.default.svc.cluster.local
;; 응답
random-generator.default.svc.cluster.local. 5 IN A 10.96.12.24
$ dig SRV _http._tcp.random-generator.default.svc.cluster.local
;; 응답
_http._tcp.random-generator.default.svc.cluster.local. 5 IN SRV 0 100 80 random-generator.default.svc.cluster.local.
Headless Service (ClusterIP-None): A/AAAA 질의에 Pod IP들을 직접 반환하여 애플리케이션이 Pod로 바로 연결됩니다.
ExternalName Service: DNS가 CNAME을 반환해 외부 도메인으로 위임합니다.
kube-proxy: 각 노드에서 iptables 또는 IPVS 규칙을 생성하여 ClusterIP:Port → PodIP:Port로 DNAT하고, rr/lc/wrr 등 스케줄러(특히 IPVS)를 통해 로드밸런싱합니다.
clusterIP를 None으로 설정하면 Kubernetes는 ClusterIP를 생성하지 않고
각 Pod의 실제 IP를 직접 DNS에 등록한다.
apiVersion: v1
kind: Service
metadata:
name: my-db
spec:
clusterIP: None
selector:
app: my-db
ports:
- port: 5432
$ dig my-db.default.svc.cluster.local
;; ANSWER SECTION:
my-db.default.svc.cluster.local. 5 IN A 10.0.1.12
my-db.default.svc.cluster.local. 5 IN A 10.0.1.13
db-0.my-db.default.svc.cluster.local → 10.0.1.12
db-1.my-db.default.svc.cluster.local → 10.0.1.13
Headless Service는 Pod 개별 DNS 레코드가 필요한 상태 저장 워크로드(DB, Kafka, Cassandra 등)에서 사용됩니다.
StatefulSet과 결합하면 Pod별로 고유한 네트워크 ID를 유지할 수 있습니다.
Kubernetes는 내부뿐만 아니라 외부와의 통신을 위해 다양한 Service Type을 제공합니다.
| 타입 | 설명 | 주요 사용처 | 특이사항 / 주의사항 |
|---|---|---|---|
| ClusterIP | 기본 Service 타입, 클러스터 내부에서만 접근 가능 | 내부 서비스 간 통신 (Pod → Pod) | spec.clusterIP에 내부 IP 부여, 외부 접근 불가 |
| NodePort | 각 Node의 고정 포트를 개방하여 외부 접근 가능 | 간단한 외부 노출, 로컬 테스트, 비HTTP 트래픽 | 클러스터 외부 IP + 포트번호(30000~32767)로 접근 가능 |
| LoadBalancer | 클라우드 제공자 L4 로드밸런서와 연동 | 운영 환경에서 외부 서비스 공개 | 클라우드 종속적 (MetalLB 등으로 대체 가능) |
| ExternalName | 내부 DNS 이름을 외부 FQDN으로 매핑 (CNAME 역할만 수행) | 내부 DNS alias 목적 (예: 내부에서 외부 도메인을 “내부 서비스처럼” 보이게) | 실질적인 트래픽 라우팅 x, 일부 환경(Istio 등)에서 차단됨, 운영에서는 거의 사용하지 않음 |
| Headless Service | clusterIP: None → 개별 Pod IP를 직접 반환 | StatefulSet, DB 클러스터, 샤딩 구조 | 로드밸런싱 대신 직접 Pod별 접근 필요 |
| Ingress | L7 (HTTP/HTTPS) 기반 라우팅 컨트롤러 리소스 | 여러 서비스의 도메인 기반 통합 관리 | 별도의 Ingress Controller 필요 (Nginx, Istio, Traefik 등) |
| (참고) Egress + ServiceEntry | 클러스터 외부로 나가는 트래픽 제어 (Istio 등) | 외부 API 호출 시 보안 제어 및 관찰(Observability) | NetworkPolicy나 Istio ServiceEntry로 세밀 제어 가능 |
apiVersion: v1
kind: Service
metadata:
name: web-api
spec:
type: LoadBalancer
selector:
app: web-api
ports:
- port: 80
targetPort: 8080
$ kubectl get svc web-api
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
web-api LoadBalancer 10.0.23.41 52.231.24.187 80:31544/TCP 1m
외부 사용자는 52.231.24.187로 접근하며, 내부적으로는 다음 경로로 트래픽이 전달됩니다.
ExternalIP → NodePort → ClusterIP → Pod
apiVersion: v1
kind: Service
metadata:
name: external-db
spec:
type: ExternalName
externalName: db.example.com
이 설정이 생성되면, external-db.default.svc.cluster.local 로 질의한 DNS 요청은
Kubernetes DNS(CoreDNS)를 통해 다음처럼 처리됩니다.
external-db.default.svc.cluster.local. IN CNAME db.example.com.
즉, CoreDNS가 CNAME 레코드로 단순 반환하고, 이후의 실제 IP 조회(A 레코드 조회)는 클러스터 외부의 일반 DNS 리졸버가 처리합니다.




기본적인 Kubernetes 서비스 디스커버리는 L4 수준의 연결을 해결하지만,
정책 기반 트래픽 제어, 버전별 라우팅, 가중치 기반 트래픽 분할 등을 위해서는 Istio나 Knative
와 같은 상위 레이어의 솔루션을 사용합니다.
reviews 서비스 트래픽 분할 (v1 90%, v2 10%)apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v1
weight: 90
- destination:
host: reviews
subset: v2
weight: 10실습 코드는 해당 깃 레포지토리에 있습니다.
# 네임스페이스 생성
kubectl create ns sd-lab
# DNS/네트워크 진단용 파드
kubectl run dnsutils -n sd-lab --image=registry.k8s.io/e2e-test-images/jessie-dnsutils:1.3 -- sleep 36000
kubectl wait pod/dnsutils -n sd-lab --for=condition=Ready --timeout=180s

# 01-deploy-svc.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: echo
namespace: sd-lab
spec:
replicas: 3
selector:
matchLabels:
app: echo
template:
metadata:
labels:
app: echo
spec:
containers:
- name: echo
image: hashicorp/http-echo:1.0
args: ["-text=hello-from-echo"]
ports:
- containerPort: 5678
---
apiVersion: v1
kind: Service
metadata:
name: echo
namespace: sd-lab
spec:
selector:
app: echo
ports:
- name: http
port: 80
targetPort: 5678
type: ClusterIPkubectl apply -f 01-deploy-svc.yaml
kubectl -n sd-lab get pod -l app=echo -o wide
kubectl -n sd-lab get svc echo
kubectl -n sd-lab get endpoints echo
kubectl -n sd-lab get endpointslices -l kubernetes.io/service-name=echo 
# A 레코드(ClusterIP)
kubectl exec dnsutils -n sd-lab -- dig +short echo.sd-lab.svc.cluster.local A
# SRV 레코드(포트명 기반)
kubectl exec dnsutils -n sd-lab -- dig +short SRV _http._tcp.echo.sd-lab.svc.cluster.local
kubectl run busybox -n sd-lab --image=busybox:1.36 --restart=Never -it -- sh
# 컨테이너 접근 후
wget -qO- http://echo.sd-lab.svc.cluster.local
exit
# 컨테이너 외부에서 busybox 삭제
kubectl delete pods busybox -n sd-lab 
POD=$(kubectl -n sd-lab get pod -l app=echo -o jsonpath='{.items[0].metadata.name}')
kubectl delete pod "$POD" -n sd-lab
kubectl wait -n sd-lab --for=condition=Ready pod -l app=echo --timeout=180s
kubectl run busybox -n sd-lab --image=busybox:1.36 --restart=Never -it -- sh
# 컨테이너 접근 후
wget -qO- http://echo.sd-lab.svc.cluster.local
exit
# 컨테이너 외부에서 busybox 삭제
kubectl delete pods busybox -n sd-lab

# 02-deploy-readiness.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: echo-ready
namespace: sd-lab
spec:
replicas: 2
selector:
matchLabels:
app: echo-ready
template:
metadata:
labels:
app: echo-ready
spec:
containers:
- name: echo
image: hashicorp/http-echo:1.0
args: ["-text=ready-echo"]
ports:
- containerPort: 5678
readinessProbe:
httpGet:
path: /
port: 5678
initialDelaySeconds: 10
periodSeconds: 2
---
apiVersion: v1
kind: Service
metadata:
name: echo-ready
namespace: sd-lab
spec:
selector:
app: echo-ready
ports:
- name: http
port: 80
targetPort: 5678
type: ClusterIPkubectl apply -f 02-deploy-readiness.yaml
kubectl rollout status deploy/echo-ready -n sd-lab --timeout=180s
# 준비 전/후 Endpoints/EndpointSlice 변화 관찰
watch kubectl get ep echo-ready -n sd-lab
watch kubectl get endpointslices -n sd-lab -l kubernetes.io/service-name=echo-ready


# 03-headless-stateful.yaml
apiVersion: v1
kind: Service
metadata:
name: demo-hl
namespace: sd-lab
spec:
clusterIP: None
selector:
app: demo-hl
ports:
- port: 8080
name: http
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: demo
namespace: sd-lab
spec:
serviceName: demo-hl
replicas: 2
selector:
matchLabels:
app: demo-hl
template:
metadata:
labels:
app: demo-hl
spec:
containers:
- name: web
image: hashicorp/http-echo:1.0
args: ["-text=stateful"]
ports:
- containerPort: 8080kubectl apply -f 03-headless-stateful.yaml
kubectl rollout status sts/demo -n sd-lab --timeout=180s
# Headless는 Pod IP 목록을 바로 반환
kubectl exec dnsutils -n sd-lab -- dig +short demo-hl.sd-lab.svc.cluster.local A
# Pod별 FQDN → 개별 IP 확인
for i in 0 1; do
echo -n "demo-$i: "
kubectl exec dnsutils -n sd-lab -- dig +short demo-$i.demo-hl.sd-lab.svc.cluster.local A
done
# 파드 확인
kubectl get pod -n sd-lab -o wide 

코드 작성
# 04-externalname.yaml
# ExternalName 서비스 (httpbin.org)
apiVersion: v1
kind: Service
metadata:
name: httpbin-external
namespace: sd-lab
spec:
type: ExternalName
externalName: httpbin.org
---
# Egress 허용 네트워크 정책 (특정 파드만 외부 접근 가능)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-external-egress-for-dnsutils
namespace: sd-lab
spec:
podSelector:
matchLabels:
app: dnsutils # 이 라벨이 있는 파드만 외부로 나갈 수 있음
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 0.0.0.0/0 # 외부 전체 허용
ports:
- protocol: TCP
port: 80
- protocol: TCP
port: 443
코드 배포 및 확인
kubectl apply -f 04-externalname.yaml
# 라벨 추가
kubectl label -n sd-lab pod dnsutils app=dnsutils --overwrite
# 라벨 확인
kubectl -n sd-lab get pod dnsutils -o jsonpath='{.metadata.labels}'
# CNAME 확인
kubectl exec dnsutils -n sd-lab -- dig +short httpbin-external.sd-lab.svc.cluster.local CNAME
kubectl run busybox -n sd-lab --image=busybox:1.36 --restart=Never -it -- sh
# 외부 도메인 직접 접근
wget -qO- http://httpbin.org/get | head -c 200
# ExternalName 서비스 경유 접근 (내부 DNS → 외부 CNAME)
wget -qO- http://httpbin-external.sd-lab.svc.cluster.local/get | head -c 200
exit
# 컨테이너 외부에서 busybox 삭제
kubectl delete pods busybox -n sd-lab



kubectl expose deploy/echo -n sd-lab --name=echo-np --type=NodePort --port=80 --target-port=5678
kubectl get svc -n sd-lab echo-np -o wide
open http://10.110.241.216:30218

kubectl get nodes -o wide 
# ${NODE_INTERNAL-IP}:${NODEPORT}
open http://192.168.65.185:30218 
kubectl -n sd-lab expose deploy/echo --name=echo-lb --type=LoadBalancer --port=80 --target-port=5678
kubectl -n sd-lab get svc echo-lb -w
# ${EXTERNAL-IP}:${NODEPORT}
open http://192.168.65.202:80

kubectl get pods -A | grep ingress 
kubectl create ns ingress-nginx
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/cloud/deploy.yaml
kubectl get pods -n ingress-nginx -w kubectl get pods -A | grep ingress 
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: echo
namespace: sd-lab
spec:
ingressClassName: nginx
rules:
- host: echo.sd.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: echo
port:
number: 80kubectl apply -f 05-ingress.yaml
kubectl get ingress echo -n sd-lab 
192.168.65.203 echo.sd.local# 사이트 접근
open http://echo.sd.local 
# 모드 확인 (iptables 또는 IPVS)
kubectl -n kube-system get cm kube-proxy -o yaml | grep -A2 mode:
# (노드에서) iptables 모드 예시
sudo iptables-save | grep -i KUBE-SVC | head
# (노드에서) IPVS 모드 예시
# 만약 없다면 설치 진행
sudo ipvsadm -Ln
“” : 기본 iptables 모드 (default)
"ipvs” : IPVS 로드밸런싱 사용 중
"nftables” : (v1.28+에서 추가) nftables 백엔드 사용 중

iptables 확인 : 서비스 트래픽을 처리하기 위해 만든 NAT 체인 목록 (각 개별 체인은 개별의 Service 객체(ClusterIP)에 대응)

어느 서비스가 어느 체인에 매칭되는지 확인 하는 명령어
sudo iptables-save | grep -A3 KUBE-SERVICES

| 체인 이름 | 역할 |
|---|---|
KUBE-SERVICES | 모든 Kubernetes Service 트래픽의 진입점 |
KUBE-SVC-* | 각 Service(ClusterIP)에 대한 라우팅 규칙 |
KUBE-SEP-* | Service가 참조하는 각 Pod Endpoint 규칙 |
KUBE-EXTERNAL-SERVICES | NodePort / LoadBalancer 트래픽 처리 |
KUBE-PROXY-FIREWALL | 방화벽 성격의 기본 정책 |
FLANNEL-* | CNI(Flannel) 네트워크 관련 NAT / Forward 규칙 |
# 06-echo-split.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: echo-v1
namespace: sd-lab
spec:
replicas: 1
selector:
matchLabels: { app: echo, version: v1 }
template:
metadata:
labels: { app: echo, version: v1 }
spec:
containers:
- name: echo
image: hashicorp/http-echo:1.0
args: ["-text=echo-v1"]
ports: [{containerPort: 5678}]
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: echo-v2
namespace: sd-lab
spec:
replicas: 1
selector:
matchLabels: { app: echo, version: v2 }
template:
metadata:
labels: { app: echo, version: v2 }
spec:
containers:
- name: echo
image: hashicorp/http-echo:1.0
args: ["-text=echo-v2"]
ports: [{containerPort: 5678}]
---
apiVersion: v1
kind: Service
metadata:
name: echo-mesh
namespace: sd-lab
spec:
selector:
app: echo
ports:
- name: http
port: 80
targetPort: 5678
---
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: echo
namespace: sd-lab
spec:
host: echo-mesh
subsets:
- name: v1
labels: { version: v1 }
- name: v2
labels: { version: v2 }
---
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: echo
namespace: sd-lab
spec:
hosts: ["echo-mesh"]
http:
- route:
- destination: { host: echo-mesh, subset: v1 }
weight: 80
- destination: { host: echo-mesh, subset: v2 }
weight: 20
# 사이드카 주입 네임스페이스로 전환
kubectl label ns sd-lab istio-injection=enabled --overwrite
# 매니페스트 적용
kubectl apply -f 06-echo-split.yaml
kubectl -n sd-lab rollout status deploy/echo-v1 --timeout=180s
kubectl -n sd-lab rollout status deploy/echo-v2 --timeout=180s
# busybox 생성
kubectl run busybox -n sd-lab --image=busybox:1.36 --restart=Never -it -- sh
# 여러 번 호출해 가중치(80/20) 체감
for i in $(seq 1 100); do
wget -qO- http://echo-mesh.sd-lab.svc.cluster.local | grep echo-;
done | sort | uniq -c
exit
# 확인후 삭제
kubectl delete pods busybox -n sd-lab
작업하기전 주의사항 : istio-ingressgateway가 설치되어 있는 환경이여야 함
| 항목 | istio-ingress | istio-ingressgateway |
|---|---|---|
| 설치 기본값 | 별도로 설치 필요 | Istio 기본 설치 시 포함 |
| 커스터마이징 | 제한적 (Ingress 스펙에 의존) | 자유로움 (Gateway + VirtualService 조합) |
| TLS/HTTPS 처리 | Ingress Controller 수준 | Gateway 리소스에서 완전 지원 |
| 외부 접근 제어 | Kubernetes 네이티브 방식 | Istio 정책 (AuthorizationPolicy 등)과 연동 |
| 권장 여부 | ❌ (과거 호환용) | ✅ (Istio 공식 권장) |
kubectl get svc -n istio-ingress istio-ingressgateway 
네임스페이스 생성 및 CRD, Knative Istio Controller(istio용 네트워크 레이어 플러그인) 설치
# 네임스페이스 생성
kubectl create ns knative-serving
kubectl create ns sd-lab
# CRD, Serving Core, Knative Istio Controller 설치
kubectl apply -f https://github.com/knative/serving/releases/download/knative-v1.13.0/serving-crds.yaml
kubectl apply -f https://github.com/knative/serving/releases/download/knative-v1.13.0/serving-core.yaml
kubectl apply -f https://github.com/knative/net-istio/releases/download/knative-v1.13.0/net-istio.yaml


ingressclasss 생성 및 확인
kubectl apply -f - <<'EOF'
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
name: istio.ingress.networking.knative.dev
spec:
controller: istio.io/ingress-controller
EOF
# 확인
kubectl get ingressclasses

gateway 존재 여부 확인
kubectl get gateway -A

kubectl get gateway -n knative-serving knative-ingress-gateway -o yaml | grep selector -A 3
kubectl get gateway -n knative-serving knative-local-gateway -o yaml | grep selector -A 3 
Knative Service 및 configmap 코드 작성 (07-knative.yaml)
# -------------------------------
# 1. Knative ↔ Istio Gateway 매핑 설정 (수정 완료)
# -------------------------------
apiVersion: v1
kind: ConfigMap
metadata:
name: config-istio
namespace: knative-serving
labels:
serving.knative.dev/release: "v1.11.2"
data:
# Istio Ingress Gateway 네임스페이스
gateway.knative-serving.knative-ingress-gateway: "istio-ingress/istio-ingressgateway"
#(내부 트래픽용)
local-gateway.knative-serving.knative-local-gateway: "istio-ingress/istio-ingressgateway"
---
# -------------------------------
# 2. Knative Service (echo-kn)
# -------------------------------
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: echo-kn
namespace: sd-lab
spec:
template:
metadata:
annotations:
autoscaling.knative.dev/minScale: "0"
autoscaling.knative.dev/maxScale: "5"
networking.knative.dev/ingress-class: "istio.ingress.networking.knative.dev"
serving.knative.dev/rollout-duration: "10s"
spec:
containers:
- image: hashicorp/http-echo:1.0
args: ["-text=hello-knative"]
ports:
- name: http1
containerPort: 5678
securityContext:
allowPrivilegeEscalation: false
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
capabilities:
drop: ["ALL"]
kubectl apply -f 07-knative.yaml kubectl get configmap -n knative-serving config-istio -o yaml
kubectl get svc -n sd-lab echo-kn -o yaml 

09-knative-rbac.yaml)# 09-knative-rbac.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: istio-reader
rules:
- apiGroups: [""]
resources: ["services", "endpoints", "pods"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: knative-serving-istio-access
namespace: istio-ingress
subjects:
- kind: ServiceAccount
name: controller
namespace: knative-serving
roleRef:
kind: ClusterRole
name: istio-reader
apiGroup: rbac.authorization.k8s.io kubectl apply -f 09-knative-rbac.yaml
kubectl get clusterrole istio-reader
kubectl get rolebinding knative-serving-istio-access -n istio-system 
kubectl rollout restart deployment controller -n knative-servingkubectl get configmap config-network -n knative-serving -o yaml | grep ingress.class 
watch -n 2 kubectl get ksvc -n sd-lab echo-kn
클러스터 내부에서 테스트
# Busybox Pod 생성 및 테스트
kubectl run busybox -n sd-lab --image=busybox:1.36 --restart=Never -it -- sh
# Pod 내부에서 실행
wget -qO- http://echo-kn.sd-lab.svc.cluster.local
# 예상 출력: hello-knative
클러스터 내부 통신 테스트 결과 : bad address 발생

=> 내부 테스트 실패 (bad address)
wget -qO- http://echo-kn.sd-lab.svc.cluster.local
wget: bad address 'echo-kn.sd-lab.svc.cluster.local'
이 메시지는 DNS 해석 실패를 의미합니다. 즉, echo-kn.sd-lab.svc.cluster.local 이라는 이름을 CoreDNS가 인식하지 못함.
Pod 스케일 변화 확인
# 요청 전 (Pod 없음)
kubectl -n sd-lab get pod -l serving.knative.dev/service=echo-kn
# 요청 후 자동 생성 확인 (watch 모드)
kubectl -n sd-lab get pod -l serving.knative.dev/service=echo-kn -w
Ingress gateway 의 External-IP / 주소 확인
kubectl get svc -n istio-ingress istio-ingressgateway
kubectl get ksvc -n sd-lab echo-kn
# 양식
curl -v -H "Host: ${DOMAIN}" http://${EXTERNAL-IP}
#예시
curl -v -H "Host: echo-kn.sd-lab.svc.cluster.local" http://192.168.65.201
curl -v -H "Host: echo-kn.sd-lab.svc.cluster.local" http://192.168.65.201
< HTTP/1.1 404 Not Found
< server: istio-envoy
여기서 중요한 점은:
- istio-envoy 응답 → Istio Gateway까지는 접근됨
- 404 Not Found → VirtualService 매칭 실패
자동 스케일링 동작 확인
# 여러 개의 요청 전송 (부하 생성)
for i in {1..100}; do
curl -s http://echo-kn.sd-lab.192-168-65-201.sslip.io &
done
# Pod 개수 증가 확인 (최대 5개까지)
kubectl -n sd-lab get pod -l serving.knative.dev/service=echo-kn -w
# 요청이 없으면 약 90초 후 Pod 개수가 0으로 감소
삭제
kubectl delete ksvc echo-kn -n sd-lab
kubectl delete ns sd-lab --grace-period=0 --force
kubectl delete ns knative-serving --grace-period=0 --force
clusterIP: None)
안녕하세요. 작성해주신 글 잘 읽었습니다. ExternalName 실습하신 부분에서 궁금한 부분이 있어 여쭤보려 합니다.
일반적으로 cluster 내부 -> 외부에 대한 제어가 필수적으로 사용되는걸까요? 예제 내용에서 NetworkPolicy로 규칙을 적용하여 특정 Pod만 외부로 연결이 가능하도록 하셨던데 보안 측면에서 일반적으로 사용되는 방식인가해서 질문드립니다. 클러스터 운영이나 관리쪽으로 경험이 거의 없어서요.