๊ผฌ๋ฆฌ์— ๊ผฌ๋ฆฌ๋Š” ๋ฌด๋Š” Virtual Service๋ฅผ ๊ตฌ์„ฑ ํ–ˆ์„ ๋•Œ ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ• ๊นŒ? ๐Ÿ VirtualService๋Š” ํŠธ๋ž˜ํ”ฝ์ด ๋‚˜๊ฐ€๋Š” ์ชฝ์˜ Envoy Proxy์—์„œ, DestinationRule์€ ํŠธ๋ž˜ํ”ฝ์ด ๋“ค์–ด์˜ค๋Š” ์ชฝ์˜ Envoy Proxy์—์„œ Evaluation ๋œ๋‹ค.

6 minute read

์˜ค๋Š˜์˜ ์ฃผ์ œ๋Š” ๊ผฌ๋ฆฌ์— ๊ผฌ๋ฆฌ๋ฅผ ๋ฌด๋Š”, ๋ฌดํ•œ๋ฒˆ ํ˜ธ์ถœ์ด๋‹ค!

Istio์˜ VirtualService์˜ ๋™์ž‘์„ ์ดํ•ดํ•˜๋ ค๊ณ  ํ•  ๋•Œ, ์ดํ•ด๊ฐ€ ์•ˆ ๋˜๋Š”๊ฒŒ spec.hosts์—๋„ host ์ฃผ์†Œ๋ฅผ ์ ๊ณ , destination ์†์„ฑ์—๋„ host ์ฃผ์†Œ๋ฅผ ์ ๋Š” ๊ฑฐ ์˜€๋‹ค.

๊ทธ๋Ÿฐ ๋‚˜์—๊ฒŒ ์ด๋Ÿฐ ์งˆ๋ฌธ์ด ๋– ์˜ฌ๋ž๋Š”๋ฐโ€ฆ

  • โ€œ๋งŒ์•ฝ ์ž๊ธฐ ์ž์‹ ์„ destination์œผ๋กœ ๊ฐ–๋Š” VirtualService๋ฅผ ๋„์šฐ๋ฉด, ์ด VirtualService๋Š” ๋ฌดํ•œ๋ฒˆ evaluation ๋˜๋Š” ๊ฑธ๊นŒ?โ€
  • โ€œ๋งŒ์•ฝ VirtualService๋กœ host A์˜ ํŠธ๋ž˜ํ”ฝ์„ host B๋กœ ๋ณด๋‚ด๊ณ , ๋˜ ๋‹ค๋ฅธ VirtualService๋กœ๋Š” host B๋Š” ๋‹ค์‹œ host A๋กœ ํŠธ๋ž˜ํ”ฝ์„ ๋ณด๋‚ธ๋‹ค๋ฉด, ์ด ๊ฒฝ์šฐ๋„ evaluation์ด ๋ฌดํ•œ๋ฒˆ ๋˜๋Š” ๊ฑธ๊นŒ?โ€

๋ญ”๊ฐ€ โ€œ๋ฌดํ•œ๋ฒˆโ€ evaluation ๋˜๋Š” ๊ฒƒ์— ๋Œ€ํ•œ ๊ถ๊ธˆ์ฆ์ด ์ƒ๊ฒผ๊ณ , ์ง์ ‘ ํ•ด๋‹น ์ผ€์ด์Šค๋“ค์„ ํด๋Ÿฌ์Šคํ„ฐ์— ๋„์›Œ ์‹คํ—˜ํ•ด๋ณด์•˜๋‹ค!


๋‹ค์‹œ helloworld ์˜ˆ์ œ๋ฅผ ๋„์šฐ์ž

$ kubectl apply -n default -f https://raw.githubusercontent.com/istio/istio/1.20.2/samples/helloworld/helloworld.yaml
service/helloworld created
deployment.apps/helloworld-v1 created
deployment.apps/helloworld-v2 created

๊ทธ๋Ÿฐ๋ฐ svc/helloworld๊ฐ€ ์žˆ์œผ๋‹ˆ ํŠธ๋ž˜ํ”ฝ ๋ถ„์‚ฐ์ด ๋ญ”๊ฐ€ ์ž˜ ์•ˆ ๋˜๋Š” ๊ฒƒ ๊ฐ™์•„์„œ svc/helloworld๋Š” ์ง€์›Œ๋‘์ž!!

$ kubectl delete svc/helloworld

ํ•˜๊ณ  v1, v2 ๋ฒ„์ „์— API call์„ ๋ณด๋‚ผ ์ˆ˜ ์žˆ๋„๋ก ๊ฐ๊ฐ์˜ K8s Service ๋ฆฌ์†Œ์Šค๋ฅผ ๋งŒ๋“ค์–ด๋‘์ž.

$ kubectl apply -f - <<EOF
apiVersion: v1
kind: Service
metadata:
  name: helloworld-v1
  labels:
    app: helloworld
    service: helloworld
    version: v1
spec:
  ports:
  - port: 5000
    name: http
  selector:
    app: helloworld
    version: v1
---
apiVersion: v1
kind: Service
metadata:
  name: helloworld-v2
  labels:
    app: helloworld
    service: helloworld
    version: v2
spec:
  ports:
  - port: 5000
    name: http
  selector:
    app: helloworld
    version: v2
EOF

์•„, ๊ทธ๋ฆฌ๊ณ  helloworld v1, v2์— ํŠธ๋ž˜ํ”ฝ์„ ํ˜๋ ค๋ณด๋‚ผ ํ…Œ์ŠคํŠธ์šฉ nginx Pod๋„ ํ•˜๋‚˜ ๋„์šฐ์ž.

$ k run nginx --image=nginx
$ k exec -it nginx -- sh
# <on some pod>
while true; do curl "http://helloworld.default:5000/hello"; done

์ข‹์•˜์–ด!! ์ค€๋น„๋Š” ๋๋‚ฌ๋‹ค!! ๏ผˆยดโˆ€`๏ผ‰๏ฝ‚


๋ฌดํ•œ Evaluation์ด ์ผ์–ด๋‚ ์ง€ ์‹คํ—˜

์ž๊ธฐ ์ž์‹ ์œผ๋กœ ๋ผ์šฐํŒ… ํ•˜๋Š” Virtual Service

โ€œ๋งŒ์•ฝ ์ž๊ธฐ ์ž์‹ ์„ destination์œผ๋กœ ํ•˜๋Š” VirtualService๋ฅผ ๋„์šฐ๋ฉด, ์ด VirtualService๋Š” ๋ฌดํ•œ๋ฒˆ evaluation ๋˜๋Š” ๊ฑธ๊นŒ?โ€

# self-destination.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: self-destination
spec:
  hosts:
  - "helloworld-v1.default.svc.cluster.local"
  http:
  - route:
    - destination:
        host: helloworld-v1.default.svc.cluster.local

์š”๋Ÿฐ VirtualService๋ฅผ ๋งŒ๋“ค๊ณ , nginx Pod์— ์ ‘์†ํ•ด์„œ ํŠธ๋ž˜ํ”ฝ์„ ํ˜๋ ค๋ณด์ž.

$ k exec -it nginx -- sh
# <on some pod>
while true; do curl "http://helloworld-v1.default.svc.cluster.local:5000/hello"; done

ํ์Œโ€ฆ?

๋งŒ์•ฝ ๋ฌดํ•œ๋ฒˆ Evaluation์ด ์ผ์–ด๋‚ฌ๋‹ค๋ฉด, timeout์ด ๋ฐœ์ƒํ–ˆ์„ ๊ฒƒ ๊ฐ™์€๋ฐ, ์ž๊ธฐ ์ž์‹ ์œผ๋กœ ํŠธ๋ž˜ํ”ฝ์ด ์ž˜ ๊ฐ€๊ณ  ์žˆ๋‹ค!!

์„œ๋กœ์—๊ฒŒ ํŠธ๋ž˜ํ”ฝ์„ ๋ณด๋‚ด ๋ฒ„๋ฆฌ๋Š” Virtual Service

์„œ๋กœ ํŠธ๋ž˜ํ”ฝ์„ ๋ฏธ๋ฃจ๋Š” VirtualService๋ฅผ ๊ตฌ์„ฑํ•ด๋ณด์ž.

# circular-traffic-shift.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: traffic-shift-v1-to-v2
spec:
  hosts:
  - "helloworld-v1.default.svc.cluster.local"
  http:
  - route:
    - destination:
        host: helloworld-v2.default.svc.cluster.local
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: traffic-shift-v2-to-v1
spec:
  hosts:
  - "helloworld-v2.default.svc.cluster.local"
  http:
  - route:
    - destination:
        host: helloworld-v1.default.svc.cluster.local

๋ญ”๊ฐ€ ์˜ˆ์ƒํ•˜๊ธฐ๋กœ๋Š” v1์ด๋ž‘ v2๊ฐ€ ๋ฒˆ๊ฐˆ์•„ ๊ฐ€๋ฉด์„œ ๋‚˜์˜ฌ ์ค„ ์•Œ์•˜๋Š”๋ฐ, vs/traffic-shift-v1-to-v2์— ๋ช…์‹œํ•œ ๋Œ€๋กœ ํŠธ๋ž˜ํ”ฝ์ด ์˜ค์ง v2๋กœ๋งŒ ์ž˜ ๊ฐ€๊ณ  ์žˆ๋‹ค!!

๊ฒฐ๋ก 

๋ฌดํ•œ๋ฒˆ Evaluation์ด ์ผ์–ด๋‚˜์ง€ ์•Š๋Š” ์ด์œ ๋Š”, Envoy Proxy๊ฐ€ ๋™์ž‘ํ•˜๋Š” ๋ฐฉ์‹์— ์žˆ๋‹ค.

์ผ๋‹จ Envoy Proxy๋Š” ์•ฑ์—์„œ in/out ํ•˜๋Š” ํŠธ๋ž˜ํ”ฝ์„ ๋Œ€์‹  ์ปจํŠธ๋กค ํ•˜๋Š” ๋…€์„์ด๋‹ค. ๊ทธ๋ž˜์„œ nginx๊ฐ€ helloworld-v2.default์— ์š”์ฒญ์„ ๋ณด๋‚ธ๋‹ค๋ฉด, ํ˜„์žฌ Service Mesh์— ์ •์˜๋œ VirtualService ์ค‘์— ์ €๊ณณ์„ hosts๋กœ ๊ฐ–๋Š” VirtualService๋ฅผ ์ฐพ์€ ํ›„, ๊ทธ ViritualService์˜ ๊ทœ์น™์„ ์ ์šฉํ•œ๋‹ค.

์ด๋•Œ, Envoy Proxy๊ฐ€ ํŠธ๋ž˜ํ”ฝ์„ helloworld-v1.default๋กœ ๋ณด๋‚ด๋ผ๊ณ  ์ •ํ–ˆ์œผ๋ฉด ์ถ”๊ฐ€์ ์ธ evaluation์€ ๋”์ด์ƒ ์—†๋‹ค! ์ฆ‰, ๋‹ค์‹œ helloworld-v1.default๋ฅผ hosts๋กœ ๊ฐ–๋Š” VirtualService๋ฅผ ์ฐพ์ง€๋Š” ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

๋‚˜๊ฐ€๋Š” ํŠธ๋ž˜ํ”ฝ์—๋Š” VirtualService ๊ทœ์น™์„ ์ ์šฉ

์–ด๋–ป๊ฒŒ ๋ณด๋ฉด ๋‚ด๊ฐ€ ์ฐฉ๊ฐํ•œ ๋ถ€๋ถ„์€ VirtualService๊ฐ€ evaluation ๋˜๋Š” ์ง€์ ์ด ํŠธ๋ž˜ํ”ฝ์ด ๋„๋‹ฌํ•˜๋Š” Pod์˜ Envoy Proxy๋ผ๊ณ  ์ƒ๊ฐํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ธ ๊ฒƒ ๊ฐ™๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์‹ค์ œ๋ก  ๊ทธ ๊ณผ์ •์ด ํŠธ๋ž˜ํ”ฝ์„ ๋ณด๋‚ด๋Š” Pod์˜ Envoy Proxy์—์„œ ์ง„ํ–‰ ๋˜์—ˆ๋‹ค.

๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— VirtualService์—๋Š” Istio Service Mesh ์™ธ๋ถ€์˜ host๋“ค๋„ hosts์™€ destination์— ์ ์„ ์ˆ˜ ์žˆ๋‹ค.

naver.com์œผ๋กœ ๊ฐ€๋Š” ํŠธ๋ž˜ํ”ฝ์„ ์™ธ๋ถ€๊ฐ€ ์•„๋‹ˆ๋ผ ๋‚ด๋ถ€์˜ ๋‹ค๋ฅธ Pod์œผ๋กœ ๋ณด๋‚ด๋ฒ„๋ฆฐ๋‹ค๊ฑฐ๋‚˜ ๋ฏธ๋Ÿฌ๋ง ํ•˜๋„๋ก VirtualService๋กœ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

๋˜, VirtualService ๊ทœ์น™์€ Envoy Proxy๊ฐ€ ์—†๋Š” Pod์—์„œ๋Š” ์ ์šฉ๋˜์ง€ ์•Š๋Š” ์ ๋„ VirtualService์˜ ๊ทœ์น™์ด ํŠธ๋ž˜ํ”ฝ์„ ๋ณด๋‚ด๋Š” Pod์˜ Envoy Proxy์—์„œ ์ผ์–ด๋‚˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

๋“ค์–ด์˜ค๋Š” ํŠธ๋ž˜ํ”ฝ์—๋Š” DestinationRule ๊ทœ์น™์„ ์ ์šฉ

๋ฐ˜๋Œ€๋กœ ํŠธ๋ž˜ํ”ฝ์ด ๋„๋‹ฌํ•˜๋Š” Pod์˜ Envoy Proxy์—์„œ๋Š” DestinationRule์˜ evaluation์ด ์ผ์–ด๋‚œ๋‹ค!!

์˜ˆ๋ฅผ ๋“ค์–ด,

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: helloworld-v1-max-connection
spec:
  host: helloworld-v1.default.svc.cluster.local
  trafficPolicy:
    connectionPool:
      http:
        http2MaxRequests: 1

์š”๋ ‡๊ฒŒ DestinationRule์„ ์„ค์ •ํ•˜๋ฉด, ์˜ค์ง ํ•œ๋ฒˆ์— ํ•˜๋‚˜์˜ http request๋งŒ ํ—ˆ์šฉํ•˜๋„๋ก ์ ์šฉํ•ด๋ณด์ž.

์š”๋ ‡๊ฒŒ ํ•˜๋ฉด, nginx Pod์— ์ ‘์†ํ•˜๋Š” ํ„ฐ๋ฏธ๋„ 2๊ฐœ๋ฅผ ์—ด์–ด์„œ ์•„๋ž˜ ์š”์ฒญ์„ ๋ณด๋‚ด๋ฉด ์ด๋Ÿฐ ํ˜„์ƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

$ while true; do curl "http://helloworld-v2.default.svc.cluster.local:5000/hello"; done

(DR ์„ค์ •์€ helloworld-v1.default์— ํ–ˆ๋Š”๋ฐ, ์•ž์—์„œ VS ์„ค์ • ํ•œ๊ฒŒ ์žˆ์–ด์„œ helloworld-v2.default์— ์š”์ฒญ์„ ๋ณด๋‚ธ๋‹ค!!)

์ž˜ ์•ˆ ๋ณด์ด๋‹ˆ ํ™•๋Œ€ ํ•ด์„œ ๋ณด์ž

์บก์ณ๋ฅผ ๋ณด๋ฉด ์ฒ˜์Œ์—๋Š” v1์œผ๋กœ ์š”์ฒญ์ด ์ž˜ ๊ฐ€๋‹ค๊ฐ€, ๋‹ค๋ฅธ ํ„ฐ๋ฏธ๋„์—์„œ๋„ ์š”์ฒญ์„ ๋ณด๋‚ด๊ธฐ ์‹œ์ž‘ํ•˜๋ฉด์„œ reset reason: overflowupstream connect error or disconnect/reset before headers ์—๋Ÿฌ๋ฅผ ๋ฐ›๊ฒŒ ๋œ๋‹ค.

์‹ ๊ธฐํ•œ ์ ์€ ๋ถ„๋ช… ์š”์ฒญ์€ helloworld-v2๋กœ ๋ณด๋ƒˆ๋Š”๋ฐ, helloworld-v1์˜ DR ๋ฃฐ์— ์˜ํ•ด upstream overflow๊ฐ€ ๋ฐœ์ƒ ํ–ˆ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

๋˜, ๋งŒ์•ฝ Istio Service Mesh ๋ฐ”๊นฅ์˜ Pod์ด DR์ด ์„ค์ •๋œ helloworld-v1.default์— ์š”์ฒญ์„ ๋ณด๋‚ด๋„, ๋˜‘๊ฐ™์ด ์ด max http request์˜ ๊ทœ์น™์„ ์ ์šฉ ๋ฐ›๋Š” ๊ฒƒ์ด๋‹ค!! ์ด ๋˜ํ•œ DestinationRule์ด ํŠธ๋ž˜ํ”ฝ์„ ๋ฐ›๋Š” ์ชฝ์˜ Envoy Proxy์—์„œ ์ ์šฉ ๋˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค!

์ฆ‰, ์ด ๊ฒฝ์šฐ ํŠธ๋ž˜ํ”ฝ์„ ๋ฐ›๋Š” ์ชฝ์˜ DestinationRule์ด ์ ์šฉ๋œ ๊ฑฐ๋ผ๊ณ  ๋ณผ ์ˆ˜ ์žˆ๋‹ค.