Istio advanced
Istio를 통해 요청 라우팅, A/B 테스트, 트래픽 관리 규칙을 사용하여 Kubernetes 환경에서 효율적인 서비스 배포가 가능하다.
개요
Istio 트래픽 라우팅 규칙을 명확하게 이해하기 위해선 트래픽 관리 모델을 이해해야합니다. 이를 위해 기능의 작동 방식과 아키텍쳐 개요를 통해 트래픽 관리에 대해 읽어보실 것을 권장드립니다.
Request Routing
DEMO
앱을 확인해본다면 Istio Bookinfo 샘플은 각각 여러 버전이 있는 네 개의 개별 마이크로서비스로 구성되어 있습니다. 그 중 하나인 리뷰 서비스의 세 가지 다른 버전이 배포되어 동시에 실행되고 있습니다. 이로 인해 발생하는 문제를 설명하기 위해, 브라우저에서 Bookinfo 앱의 /productpage
에 접근하고 여러 번 새로 고침을 해보세요. URL은 http://$GATEWAY_URL/productpage
이며, 여기서 $GATEWAY_URL
은 Bookinfo 문서에 설명된 대로 인그레스의 외부 IP 주소입니다.
새로 고침할 때마다 책 리뷰 출력에 별점이 포함되기도 하고 포함되지 않기도 하는 것을 알 수 있습니다. 이는 명시적인 기본 서비스 버전이 라우팅되지 않기 때문에 Istio가 요청을 모든 사용 가능한 버전으로 라운드 로빈 방식으로 라우팅하기 때문입니다.
이 작업의 초기 목표는 모든 트래픽을 마이크로서비스의 v1(버전 1)으로 라우팅하는 규칙을 적용하는 것입니다. 이후에는 HTTP 요청 헤더의 값에 따라 트래픽을 라우팅하는 규칙을 적용할 것입니다.
확인해보면 총 3개의 버전이 존재합니다. 목표는 별점이 포함된 v1으로 라우팅 규칙을 적용하는 것입니다.



Route to v1
virtual-service 설정을 통해 v1으로 라우팅합니다.
kubectl apply -f samples/bookinfo/networking/virtual-service-all-v1.yaml
virtual-service를 설정하는 yaml 파일은 다음과 같습니다.
모든 서비스 구성을 v1으로 라우트하는 것을 확인할 수 있습니다.
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: productpage
spec:
hosts:
- productpage
http:
- route:
- destination:
host: productpage
subset: v1
---
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v1
---
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: ratings
spec:
hosts:
- ratings
http:
- route:
- destination:
host: ratings
subset: v1
---
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: details
spec:
hosts:
- details
http:
- route:
- destination:
host: details
subset: v1
---
이때 subset
은 DestinationRule
에 저장되어있으며 해당 파일에서 살펴보면 각 라벨 중 키 값에 따라 version을 구분하는 것을 확인할 수 있습니다.
각 서브셋에 맞춰 RANOM하게 트래픽을 분배합니다.
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
name: reviews
spec:
host: reviews
trafficPolicy:
loadBalancer:
simple: RANDOM
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
- name: v3
labels:
version: v3
---------------------------------
...
Pod Template:
Labels: app=reviews
version=v1
...
VirtualService와 DestinationRule(참고)
VirtualService와 Destination은 Istio traffic routing의 핵심이 됩니다. 이를 이해하기 위해선 아래 포스트를 참고합니다.
- k8s service가 바라보는 트래픽이 다수인 경우

서로 다른 두 개의 파드를 같은 label로 설정하여, endpoints가 다수로 설정된 경우엔 트래픽들을 자동으로 round robin으로 배포시키도록 동작합니다.
- k8s service에 selector로 지정하는 경우

해당 트래픽은 일치하는 label로 이동합니다.
- Istio VirtualService를 정의한 경우

Virtual Service는 반드시 하나의 서비스에 연결되어야 합니다. Virtual Service는 클라이언트가 요청을 보낼 때 사용하는 주소(호스트)를 정의하고, 이 주소에 대한 트래픽을 어떻게 라우팅할지를 설정하는 역할을 합니다.
구성은 다음과 같습니다.
$ kubectl apply -f - <<EOF
apiVersion: v1
kind: Service
metadata:
name: svc-hello
labels:
app: hello
spec:
selector:
app: hello
ports:
- name: http
protocol: TCP
port: 8080
---
apiVersion: v1
kind: Service
metadata:
name: svc-hello-v1
labels:
app: hello
spec:
selector:
app: hello
version: v1
ports:
- name: http
protocol: TCP
port: 8080
---
apiVersion: v1
kind: Service
metadata:
name: svc-hello-v2
labels:
app: hello
spec:
selector:
app: hello
version: v2
ports:
- name: http
protocol: TCP
port: 8080
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: vs-hello
spec:
hosts:
- "svc-hello.default.svc.cluster.local"
http:
- match:
- uri:
prefix: /v2
route:
- destination:
host: "svc-hello-v2.default.svc.cluster.local"
- route:
- destination:
host: "svc-hello-v1.default.svc.cluster.local"
EOF
HTTP Request URI에 따라 각 pod 들로 라우팅 되도록 정의할 수 있습니다.
공식 문서의 예시를 추가로 살펴보면 아래 단계와 같이 동작합니다.
- 트래픽 수신:
- 클라이언트가 hosts(
reviews.prod.svc.cluster.local
)로 HTTP 요청을 보냅니다.
- 클라이언트가 hosts(
- VirtualService 매칭:
- Istio는 수신한 요청의 호스트가
reviews.prod.svc.cluster.local
인지 확인합니다. - 요청의 URI가
/wpcatalog
또는/consumercatalog
로 시작하는지 검사합니다.
- Istio는 수신한 요청의 호스트가
- URI 재작성 및 라우팅:
- 만약 요청 URI가
/wpcatalog
또는/consumercatalog
인 경우:reviews.prod.svc.cluster.local
로 라우팅 됩니다.
- 만약 요청 URI가 위의 두 패턴과 일치하지 않는 경우:
- 기본적으로
reviews-v1-route
규칙이 적용됩니다. - 이후
reviews.prod.svc.cluster.local
로 라우팅 됩니다.
- 기본적으로
- 만약 요청 URI가
hosts:
- reviews.prod.svc.cluster.local
http:
- name: "reviews-v2-routes"
match:
- uri:
prefix: "/wpcatalog"
- uri:
prefix: "/consumercatalog"
route:
- destination:
host: reviews.prod.svc.cluster.local
- name: "reviews-v1-route"
route:
- destination:
host: reviews.prod.svc.cluster.local
- Istio VirtualService 를 활용하여 트래픽을 조절할 수 있습니다.

yaml 파일은 다음과 같습니다.
hosts:
- "svc-hello.default.svc.cluster.local"
http:
- route:
- destination:
host: "svc-hello-v1.default.svc.cluster.local"
weight: 90
- destination:
host: "svc-hello-v2.default.svc.cluster.local"
weight: 10
rewrite, redirection 모두 동작합니다.
hosts:
- reviews.prod.svc.cluster.local
http:
- name: "reviews-v2-routes"
match:
- uri:
prefix: "/wpcatalog"
- uri:
prefix: "/consumercatalog"
rewrite:
uri: "/newcatalog"
route:
- destination:
host: reviews.prod.svc.cluster.local
- name: "reviews-v1-route"
route:
- destination:
host: reviews.prod.svc.cluster.local
- Istio DestinationRule을 구성하여 Service 목적지를 정의할 수 있습니다.

Selector를 통한 Service 정의가 없어도 DestinationRule을 정의하여 labels를 지정할 수 있고 해당 규칙을 VirtualService에서 subset으로 참고하여 정의할 수 있습니다.
공식 문서의 예제를 참고하면 다음과 같습니다.
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: dr-hello
spec:
host: reviews.prod.svc.cluster.local
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: reviews-route
spec:
hosts:
- reviews.prod.svc.cluster.local
http:
- name: "reviews-v2-routes"
match:
- uri:
prefix: "/wpcatalog"
- uri:
prefix: "/consumercatalog"
rewrite:
uri: "/newcatalog"
route:
- destination:
host: reviews.prod.svc.cluster.local
subset: v2
- name: "reviews-v1-route"
route:
- destination:
host: reviews.prod.svc.cluster.local
subset: v1
- 트래픽 수신:
- 클라이언트가 hosts(
reviews.prod.svc.cluster.local
)로 HTTP 요청을 보냅니다.
- 클라이언트가 hosts(
- VirtualService 매칭:
- Istio는 수신한 요청의 호스트가
reviews.prod.svc.cluster.local
인지 확인합니다. - 요청의 URI가
/wpcatalog
또는/consumercatalog
로 시작하는지 검사합니다.
- Istio는 수신한 요청의 호스트가
- URI 재작성 및 라우팅:
- 만약 요청 URI가
/wpcatalog
또는/consumercatalog
인 경우:- 요청 URI는
/newcatalog
로 rewrite됩니다. - 이 요청은
reviews-v2-routes
규칙에 따라v2
서브셋으로 라우팅됩니다.
- 요청 URI는
- 만약 요청 URI가 위의 두 패턴과 일치하지 않는 경우:
- 기본적으로
reviews-v1-route
규칙이 적용되어v1
서브셋으로 라우팅됩니다.
- 기본적으로
- 만약 요청 URI가
- DestinationRule 적용:
- 요청이
v2
또는v1
서브셋으로 라우팅되면, 해당 서브셋에 정의된 서비스 인스턴스 중 하나로 트래픽이 전달됩니다. DestinationRule
에 설정된 로드 밸런싱 정책(예: RANDOM)이 적용되어, 여러 인스턴스 중 하나로 요청이 분배됩니다.
- 요청이
따라서, Service
를 직접 참조하는 대신 DestinationRule
을 통해 더 세부적인 트래픽 관리와 정책 적용이 가능합니다. 이를 통해 A/B 테스트, 카나리 배포 등 다양한 트래픽 관리 시나리오를 구현할 수 있습니다.
참고
v1으로 트래픽을 라우팅 하기 위해 VirtualService와 DestinationRule을 확인합니다.
- DestinationRule
트래픽이 각 host로 들어온다면 DestinationRule을 VirtualService로 라우팅되며 이를 백엔드로 전달하게 됩니다.
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
name: productpage
spec:
host: productpage
subsets:
- name: v1
labels:
version: v1
---
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
name: reviews
spec:
host: reviews
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
- name: v3
labels:
version: v3
---
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
name: ratings
spec:
host: ratings
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
- name: v2-mysql
labels:
version: v2-mysql
- name: v2-mysql-vm
labels:
version: v2-mysql-vm
---
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
name: details
spec:
host: details
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
---
- VirtualService
규칙에 따라 destination을 판단하며, 해당 트래픽을 subset에 따라 전달합니다.
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: productpage
spec:
hosts:
- productpage
http:
- route:
- destination:
host: productpage
subset: v1
---
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v1
---
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: ratings
spec:
hosts:
- ratings
http:
- route:
- destination:
host: ratings
subset: v1
---
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: details
spec:
hosts:
- details
http:
- route:
- destination:
host: details
subset: v1
---
확인해보았다면 아래 코드를 통해 리소스를 생성한 뒤 실제로 트래픽이 라우팅 되는지 확인합니다.
kubectl apply -f samples/bookinfo/networking/virtual-service-all-v1.yaml
kubectl apply -f samples/bookinfo/networking/destination-rule-all.yaml
# 생성된 리소스 확인
kubectl get destinationrules -o yaml
kubectl get virtualservices.networking.istio.io

Route based on user identity
헤더를 통해 사용자가 일치하는 경우 v2로 트래픽을 전환할 수 있습니다.
kubectl apply -f samples/bookinfo/networking/virtual-service-reviews-test-v2.yaml
해당 yaml 파일은 다음과 같습니다.
hosts:
- reviews
http:
- match:
- headers:
end-user:
exact: jason
route:
- destination:
host: reviews
subset: v2
- route:
- destination:
host: reviews
subset: v1
jason / jason
해당 유저로 로그인해보면 reviews가 정상적으로 보이는 것을 확인할 수 있고, 이는 트래픽이 v2로 간다는 것을 의미합니다.

Fault Injection
kubectl apply -f samples/bookinfo/networking/virtual-service-reviews-test-v2.yaml
kubectl apply -f samples/bookinfo/networking/destination-rule-all.yaml
위의 구성을 사용하면 요청이 다음과 같이 흐릅니다.
productpage
→reviews:v2
→ratings
(사용자 전용 - jason)productpage
→reviews:v1
(default)
테스트를 위해 의도적인 HTTP delay 주입
의도적인 지연을 위해 Delay를 설정합니다.
kubectl apply -f samples/bookinfo/networking/virtual-service-ratings-test-delay.yaml
hosts:
- ratings
http:
- fault:
delay:
fixedDelay: 7s # fault를 사용한 딜레이 설정
percentage:
value: 100
match:
- headers:
end-user:
exact: jason
route:
- destination:
host: ratings
subset: v1
- route:
- destination:
host: ratings
subset: v1
페이지를 불러오기까지 약 6초 가량이 소요되는 것을 확인할 수 있습니다.

하지만 페이지를 확인해보면 에러가 발생하였는데 이는 각 서비스별로 타임아웃이 다르게 동작하기 때문입니다.
- 제품(Product) 페이지와 리뷰(Review) 서비스 간의 타임아웃은 3초 + 1회 재시도로 총 6초입니다.
- 리뷰(Review) 서비스와 평점(Rating) 서비스 간의 지연 시간은 10초로 하드코딩되어 있습니다.
따라서 리뷰와 평점의 통신은 정상적으로 수행될 수 있지만, 수행 되기 전 제품과 리뷰간 통신에서 타임 아웃 에러가 발생하는 것입니다.
버그 수정하는 방법
버그를 해결하는 방법으로는 여러가지가 있지만 일반적으로 문제를 해결하는 방법은 다음과 같습니다.
productpage
→reviews
사이의 타임아웃 시간을 증가시키거나reviews
→ratings
사이의 타임아웃 시간을 감소시키기- 재시작이 필요합니다.
- HTTP abort fault 도입
HTTP abort fault
kubectl apply -f samples/bookinfo/networking/virtual-service-ratings-test-abort.yaml
hosts:
- ratings
http:
- m atch:
- headers:
end-user:
exact: jason
fault:
abort:
percentage:
value: 100.0
httpStatus: 500
route:
- destination:
host: ratings
subset: v2
- route:
- destination:
host: ratings
subset: v1
jason으로 로그인했을 시 Ratings 서비스를 조회해본다면 100%로 에러가 발생하게 됩니다. 이를 통해 ratings는 조회되지 않지만 정상적으로 리뷰는 조회되는 것을 확인할 수 있습니다.

여기서 로그아웃하고 테스트해보면 에러없이 리뷰가 출력되는 것을 확인할 수 있습니다.

Traffic shifting(다른 버전으로 트래픽 전환)
reviews의 모든 트래픽을 v1과 v3의 가중치로 라우팅 할 수 있도록 설정합니다
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v1
weight: 50
- destination:
host: reviews
subset: v3
weight: 50
새로고침 시 트래픽이 가중치에 따라 라우팅되는 것을 확인할 수 있습니다.


Prometheus에서 메트릭 쿼리
이번엔 Prometheus를 사용하여 Istio Metrics를 쿼리하는 방법을 알아봅니다. metric 쿼리 값을 사용하기 위해 웹 인터페이스를 사용할 수 있습니다.
Prometheus가 정상 동작하는 것을 확인합니다.
kubectl -n istio-system get svc prometheus
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
prometheus ClusterIP 10.102.220.222 <none> 9090/TCP 23h
istioctl dashboard prometheus # 프로메테우스 실행
Query 사용이 가능합니다.

마찬가지로 Graph
로 확인 가능합니다.

쿼리가 어떤 특성을 가지는지, 각 요청 전체를통해 조회 가능합니다.


다양한 쿼리가 존재합니다. 또한 쿼리 자체는 Prometheus의 PromQL을 따르므로 고급 쿼리는 아래 문서를 참고하여 조합합니다.
Grafana를 활용한 Metric 시각화
[Grafana] → [Dashboards] → [Istio] → [Istio Mesh Dashboard]
for i in $(seq 1 1000); do
echo "요청 #$i 실행 중..."
curl -s -o /dev/null "http://localhost:8080/productpage"
done
트래픽이 정상적으로 감지되는 것을 확인할 수 있습니다.

Istio Service Dashboard를 확인해본다면 다음과 같이 출력되는 것을 확인할 수 있습니다.

Accessing External Services
모든 아웃바운드 트래픽이 기본적으로 Istio가 활성화된 파드에서 사이드카 프록시로 리디렉션되기 때문에, 클러스터 외부의 URL 접근성은 프록시의 구성에 따라 달라집니다.
기본적으로 Istio는 Envoy 프록시를 구성하여 알려지지 않은 서비스에 대한 요청을 통과시키도록 설정합니다. 이는 Istio를 시작하는 데 편리한 방법이지만, 보다 엄격한 제어를 구성하는 것이 일반적으로 더 바람직합니다.
이 작업에서는 외부 서비스에 접근하는 세 가지 방법을 보여줍니다:
- Envoy 프록시가 메시에 구성되지 않은 서비스에 대한 요청을 통과하도록 허용합니다.
- 서비스 항목을 구성하여 외부 서비스에 대한 제어된 접근을 제공합니다.
- 특정 IP 범위에 대해 Envoy 프록시를 완전히 우회합니다.
kubectl apply -f samples/curl/curl.yaml # 샘플 앱 배포
# 테스트 파드
export SOURCE_POD=$(kubectl get pod -l app=curl -o jsonpath='{.items..metadata.name}')
Envoy의 외부 서비스에 대한 패스스루 설정
Istio에는 외부 서비스(즉, Istio의 내부 서비스 레지스트리에 정의되지 않은 서비스)의 사이드카 처리를 구성하는 설치 옵션인 meshConfig.outboundTrafficPolicy.mode가 있습니다.
이 옵션이 ALLOWANY로 설정되면, Istio 프록시는 알려지지 않은 서비스에 대한 호출을 통과시킵니다. 이 옵션이 REGISTRYONLY로 설정되면, Istio 프록시는 메시에 정의된 HTTP 서비스나 서비스 항목이 없는 호스트에 대한 요청을 차단합니다. ALLOW_ANY는 기본값으로, 외부 서비스에 대한 접근을 제어하지 않고 Istio를 빠르게 평가할 수 있도록 해줍니다. 이후에 외부 서비스에 대한 접근을 구성할 수 있습니다.
이 접근 방식을 실행하려면 Istio 설치가 meshConfig.outboundTrafficPolicy.mode 옵션이 ALLOWANY로 설정되어 있는지 확인해야 합니다. Istio를 설치할 때 명시적으로 REGISTRYONLY 모드로 설정하지 않았다면, 기본적으로 이 옵션이 활성화되어 있을 것입니다.
확실하지 않은 경우, 다음 명령어를 실행하여 메쉬 구성을 표시할 수 있습니다:
$ kubectl get configmap istio -n istio-system -o yaml
meshConfig.outboundTrafficPolicy.mode의 값이 REGISTRYONLY로 명시적으로 설정되어 있지 않다면, 이 옵션이 ALLOWANY로 설정되어 있다고 볼 수 있습니다. ALLOW_ANY는 유일한 다른 가능한 값이며 기본값입니다.
apiVersion: v1
data:
mesh: |-
accessLogFile: /dev/stdout
defaultConfig:
discoveryAddress: istiod.istio-system.svc:15012
defaultProviders:
metrics:
- prometheus
enablePrometheusMerge: true
extensionProviders:
- envoyOtelAls:
port: 4317
service: opentelemetry-collector.observability.svc.cluster.local
name: otel
- name: skywalking
skywalking:
port: 11800
service: tracing.istio-system.svc.cluster.local
- name: otel-tracing
opentelemetry:
port: 4317
service: opentelemetry-collector.observability.svc.cluster.local
rootNamespace: istio-system
trustDomain: cluster.local
meshNetworks: 'networks: {}'
kind: ConfigMap
metadata:
creationTimestamp: "2024-12-30T04:49:25Z"
labels:
app.kubernetes.io/instance: istio
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: istiod
app.kubernetes.io/part-of: istio
app.kubernetes.io/version: 1.24.2
helm.sh/chart: istiod-1.24.2
install.operator.istio.io/owning-resource: unknown
install.operator.istio.io/owning-resource-namespace: istio-system
istio.io/rev: default
operator.istio.io/component: Pilot
operator.istio.io/managed: Reconcile
operator.istio.io/version: 1.24.2
release: istio
name: istio
namespace: istio-system
resourceVersion: "81624"
uid: 2caea856-1ae2-411b-aefb-cf5e99eddd25
REGISTRY_ONLY 모드를 명시적으로 구성한 경우, 원래의 istioctl 설치 명령을 변경된 설정으로 다시 실행하여 변경할 수 있습니다.
$ istioctl install <flags-you-used-to-install-Istio> --set meshConfig.outboundTrafficPolicy.mode=ALLOW_ANY
SOURCE_POD에서 외부 HTTPS 서비스에 몇 번 요청을 보내 200 응답을 확인합니다:
$ kubectl exec "$SOURCE_POD" -c curl -- curl -sSI <https://www.google.com> | grep "HTTP/"; kubectl exec "$SOURCE_POD" -c curl -- curl -sI <https://edition.cnn.com> | grep "HTTP/"
HTTP/2 200
HTTP/2 200
이러한 방식이 Mesh에서 이그레스 트래픽을 전송하는 방법입니다. 외부 서비스에 접근하는 이 간단한 방법은 외부 서비스에 대한 트래픽에 대한 Istio 모니터링 및 제어 기능을 잃는 단점이 있습니다.
다음 섹션에서는 메쉬의 외부 서비스 접근을 모니터링하고 제어하는 방법을 보여줍니다.
외부 서비스 엑세스 통제
외부 서비스에 대한 접근을 제어된 방식으로 활성화하려면, meshConfig.outboundTrafficPolicy.mode
옵션을 ALLOWANY 모드에서 REGISTRYONLY 모드로 변경해야 합니다.
ALLOWANY 모드에서 이미 접근 가능한 서비스에 대해 제어된 접근을 추가할 수 있습니다. 이렇게 하면 다른 서비스의 접근을 차단하지 않고 일부 외부 서비스에서 Istio 기능을 사용할 수 있습니다. 모든 서비스를 구성한 후에는 모드를 REGISTRYONLY로 전환하여 의도하지 않은 접근을 차단할 수 있습니다.
meshConfig.outboundTrafficPolicy.mode
옵션을 REGISTRY_ONLY로 변경합니다.
IstioOperator 구성을 사용하여 Istio를 설치한 경우, 다음 필드를 구성에 추가합니다:
spec:
meshConfig:
outboundTrafficPolicy:
mode: REGISTRY_ONLY
그렇지 않은 경우, 원래의 istioctl install
명령어에 해당 설정을 추가합니다. 예를 들어:
$ istioctl install <flags-you-used-to-install-Istio> \\
--set meshConfig.outboundTrafficPolicy.mode=REGISTRY_ONLY
SOURCE_POD에서 외부 HTTPS 서비스에 몇 번 요청을 보내 이제 차단되었는지 확인합니다:
$ kubectl exec "$SOURCE_POD" -c curl -- curl -sI <https://www.google.com> | grep "HTTP/"; kubectl exec "$SOURCE_POD" -c curl -- curl -sI <https://edition.cnn.com> | grep "HTTP/"
command terminated with exit code 35
command terminated with exit code 35
REGISTRY_ONLY에서 외부 HTTP 서비스에 엑세스하는 방법
DNS resolution은 아래의 서비스 항목에서 보안 조치로 사용됩니다. resolution을 NONE으로 설정하면 공격의 가능성이 열립니다.
악의적인 클라이언트는 HOST 헤더에 httpbin.org를 설정하여 자신이 httpbin.org에 접근하고 있다고 가장할 수 있지만, 실제로는 httpbin.org와 연결되지 않은 다른 IP에 연결될 수 있습니다. Istio 사이드카 프록시는 HOST 헤더를 신뢰하고, 다른 호스트의 IP 주소로 전달되고 있음에도 불구하고 잘못된 트래픽을 허용합니다. 그 호스트는 악의적인 사이트일 수도 있고, 메쉬 보안 정책에 의해 금지된 합법적인 사이트일 수도 있습니다.
DNS resolution을 사용하면 사이드카 프록시는 원래의 목적지 IP 주소를 무시하고 트래픽을 httpbin.org로 직접 전달하며, httpbin.org의 IP 주소를 얻기 위해 DNS 쿼리를 수행합니다.
정리하면 사이드카 프록시는 DNS 요청에 대해 목적지 IP를 무시하고 직접 IP로 변환합니다.
해당 사이드카 프록시의 Resolution을 사용하지 않고 None으로 설정한 경우 클라이어트가 HOST 헤더를 조작하여 다른 IP로 유도할 수 있으며 이는 악의적인 사이트에 연결될 위험이 존재합니다. DNS Resolution을 통해 사이드카 프록시는 요청의 헤더를 신뢰하지 않고 실제 도메인에 대한 DNS 쿼리를 사용함으로써 신뢰있는 연결을 보장합니다.
- 도메인 이름 요청: 클라이언트가 특정 도메인 이름(예: httpbin.org)에 대한 요청을 보냅니다.
- 사이드카 프록시의 역할 : 사이드카 프록시는 이 요청을 가로채고, 요청의 HOST 헤더를 확인합니다.3.
- DNS 쿼리 수행 : 사이드카 프록시는 네임서버에 DNS 쿼리를 보내어 해당 도메인 이름에 대한 IP 주소를 조회합니다. 이 과정에서 사이드카 프록시는 원래의 목적지 IP 주소를 무시하고, DNS 쿼리를 통해 얻은 IP 주소를 사용합니다.
- 트래픽 전달: DNS 해상도를 통해 확인된 IP 주소로 트래픽을 전달합니다. 이를 통해 사이드카 프록시는 요청이 올바른 서비스로 향하도록 보장합니다.
실제 코드로 확인해보겠습니다.
- httpbin.org라는 주소로 Mesh external 요청을 허용합니다.
kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1
kind: ServiceEntry
metadata:
name: httpbin-ext
spec:
hosts:
- httpbin.org
ports:
- number: 80
name: http
protocol: HTTP
resolution: DNS
location: MESH_EXTERNAL
EOF
- 실제 요청을 전송합니다.
Istio sidecar proxy에 의해 X-Envoy-Decorator-Operation
이 추가되었다는 것에 주목해야합니다.
kubectl exec "$SOURCE_POD" -c curl -- curl -sS http://httpbin.org/headers
{
"headers": {
"Accept": "*/*",
"Host": "httpbin.org",
"User-Agent": "curl/8.11.1",
"X-Amzn-Trace-Id": "Root=1-6777952e-757f264e3554bc305d81b5d8",
"X-Envoy-Attempt-Count": "1",
"X-Envoy-Decorator-Operation": "httpbin.org:80/*",
"X-Envoy-Peer-Metadata": "ChoKCkNMVVNURVJfSUQSDBoKS3ViZXJuZXRlcwp3CgZMQUJFTFMSbSprCg0KA2FwcBIGGgRjdXJsCikKH3NlcnZpY2UuaXN0aW8uaW8vY2Fub25pY2FsLW5hbWUSBhoEY3VybAovCiNzZXJ2aWNlLmlzdGlvLmlvL2Nhbm9uaWNhbC1yZXZpc2lvbhIIGgZsYXRlc3QKHwoETkFNRRIXGhVjdXJsLTdjZDY0YmI2YzUtZno4eGcKFgoJTkFNRVNQQUNFEgkaB2RlZmF1bHQKSAoFT1dORVISPxo9a3ViZXJuZXRlczovL2FwaXMvYXBwcy92MS9uYW1lc3BhY2VzL2RlZmF1bHQvZGVwbG95bWVudHMvY3VybAoXCg1XT1JLTE9BRF9OQU1FEgYaBGN1cmw=",
"X-Envoy-Peer-Metadata-Id": "sidecar~10.244.0.79~curl-7cd64bb6c5-fz8xg.default~default.svc.cluster.local"
}
}
[2025-01-03T07:43:42.212Z] "GET /headers HTTP/1.1" 200 - via_upstream - "-" 0 818 412 412 "-" "curl/8.11.1" "db4a7573-01e5-9ae9-8bd8-19b3a0737928" "httpbin.org" "34.200.57.114:80" outbound|80||httpbin.org 10.244.0.79:50896 34.197.122.172:80 10.244.0.79:36258 - default
HTTPS로 접근하는 방식 또한 비슷합니다.
- ServiceEntry 추가
- 요청 보내기
- 로그 확인하기
정상적으로 443번으로 나간 것을 확인할 수 있습니다.
kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1
kind: ServiceEntry
metadata:
name: google
spec:
hosts:
- www.google.com
ports:
- number: 443
name: https
protocol: HTTPS
resolution: DNS
location: MESH_EXTERNAL
EOF
kubectl exec "$SOURCE_POD" -c curl -- curl -sSI https://www.google.com | grep "HTTP/"
kubectl logs "$SOURCE_POD" -c istio-proxy | tail
[2025-01-03T07:47:00.918Z] "- - -" 0 - - - "-" 870 5628 462 - "-" "-" "-" "-" "142.250.197.68:443" outbound|443||www.google.com 10.244.0.79:47298 142.250.197.68:443 10.244.0.79:47286 www.google.com -
클러스터 간 요청과 유사하게, ServiceEntry 구성을 사용하여 접근하는 외부 서비스에 대해서도 라우팅 규칙을 설정할 수 있습니다. 클러스터 내부 또는 외부의 서비스에 대한 호출에 대해 동일한 Istio 서비스 메시 기능을 모두 사용할 수 있게 해줍니다.
이번 예제에서는 httpbin.org 서비스에 대한 호출에 타임아웃 규칙을 설정합니다.
kubectl exec "$SOURCE_POD" -c curl -- time curl -o /dev/null -sS -w "%{http_code}\n" http://httpbin.org/delay/5
200
real 0m 5.42s
user 0m 0.00s
sys 0m 0.00s
httpbin.org에서 timeout을 설정합니다.
kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: httpbin-ext
spec:
hosts:
- httpbin.org
http:
- timeout: 3s
route:
- destination:
host: httpbin.org
weight: 100
EOF
timeout이 정상적으로 동작하는 것을 확인할 수 있습니다.
kubectl exec "$SOURCE_POD" -c curl -- time curl -o /dev/null -sS -w "%{http_code}\n" http://httpbin.org/delay/5
504
real 0m 3.02s
user 0m 0.00s
sys 0m 0.00s
우회하여 외부 서비스에 접근하기
istio-sidecar-injector
configmap을 업데이트하고 애플리케이션을 다시 배포한 후 curl
요청을 보냅니. 이때 Istio 사이드카는 클러스터 내의 내부 요청만 가로채서 관리하게 되며 모든 외부 요청은 사이드카를 우회하여 의도한 목적지로 바로 이동합니다.
내부 10.0.0.1/24
만 관리합니다.
istioctl install <flags-you-used-to-install-Istio> --set values.global.proxy.includeIPRanges="10.0.0.1/24"
다음과 같습니다.
kubectl exec "$SOURCE_POD" -c curl -- curl -sS http://httpbin.org/headers
{
"headers": {
"Accept": "*/*",
"Host": "httpbin.org",
...
}
}
HTTP 또는 HTTPS를 통해 외부 서비스에 액세스하는 것과 달리 Istio 사이드카와 관련된 헤더(X-Envoy-Decorator-Operation
)가 표시되지 않으며 외부 서비스로 전송된 요청은 사이드카 로그에 나타나지 않습니다. 또한 Istio 사이드카를 우회하면 더 이상 외부 서비스에 대한 액세스를 모니터링할 수 없게되며 이는 보안적인 문제가 발생할 수 있습니다.
Istio 메시에서 외부 서비스를 호출하는 세 가지 방법을 살펴보았습니다.
- Envoy를 구성하여 외부 서비스에 대한 접근을 허용합니다.
- Istio 사이드카 프록시를 통해 트래픽을 찾아갑니다.
ALLOW_ANY
기본 값으로 인해 서비스에 대한 엑세스를 모니터링 하거나 Istio가 트래픽 제어 기능을 활용할 수 없습니다.
- 서비스 엔트리를 사용하여 메시 내부에 접근 가능한 외부 서비스를 등록합니다. (Best Practice)
- 클러스터 내부 또는 외부의 서비스에 대한 호출에 대해 동일한 Istio 서비스 메시 기능을 모두 사용할 수 있게 해줍니다.
- 외부 IP를 다시 매핑된 IP 테이블에서 제외하도록 Istio 사이드카를 구성합니다.
- Istio 사이드카 프록시를 우회하여 외부 서버에 접속합니다.
- 클러스터 공급자를 사용하는 경우 공급자별 지식과 구성이 필요합니다.
- 액세스 모니터링 손실, 외부 서비스 트래픽 제한을 위한 Istio 기능을 활용할 수 없습니다.
Visualizing Mesh
kiali를 설치합니다.
kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.24/samples/addons/kiali.yaml
kubectl -n istio-system get svc kiali
istioctl dashboard kiali
# http://localhost:20001/kiali


가중치와 애니메이션이 동작하는 것을 확인할 수 있습니다.
