Istio ์ฒซ๊ฑธ์Œ. Hello Istio! ๊ทธ๋ฆฌ๊ณ  Virtual Service, Destination Rule๋„ ์•ˆ๋…•!

9 minute read

hell kitty
Hell?o

์ด๋ฒˆ ํฌ์ŠคํŠธ์—์„œ๋Š” Istio์—์„œ ์ œ๊ณตํ•˜๋Š” helloworld ์˜ˆ์ œ๋ฅผ ํ™œ์šฉํ•ด Istio์˜ Virtual Service์™€ Destination Rule์˜ ๊ธฐ๋Šฅ๋“ค์„ ์ง์ ‘ ์‹คํ—˜ํ•ด๋ณธ๋‹ค. ๐Ÿงช

์‚ฌ์ „ ์ค€๋น„

์ผ๋‹จ hello ์˜ˆ์ œ๋ฅผ ๋„์šธ ๋„ค์ž„์ŠคํŽ˜์ด์Šค์— label์„ ๋ถ€์—ฌํ•ด์„œ envoy sidecar๊ฐ€ ๋ถ™์„ ์ˆ˜ ์žˆ๋„๋ก ๋งŒ๋“ค์ž.

kubectl label ns default istio-injection=enabled

๊ทธ๋ฆฌ๊ณ  ์•ž์„  ํฌ์ŠคํŠธ๋ฅผ ์ฐธ๊ณ ํ•ด Istio์™€ Kiali, Prometheus Addon์„ ์„ค์น˜ํ•œ๋‹ค.

โžก๏ธ Install Istio and Addons(Prometheus, Kiali)

helloworld ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜

istio์˜ ์˜ˆ์ œ ์ค‘ ๊ฐ€์žฅ ์‹ฌํ”Œํ•œ ๋…€์„์œผ๋กœ /hello ์—”๋“œํฌ์ธํŠธ์— ์š”์ฒญ์„ ๋ณด๋‚ด๋ฉด, ์ž์‹ ์˜ ๋ฒ„์ „ ์ •๋ณด๋ฅผ ๋ฆฌํ„ดํ•œ๋‹ค.

$ curl -X GET localhost:5000/hello
Hello version: v1, instance: helloworld-v1-867747c89-n6sl2

์šฐ์„  ์š” ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋ฐฐํฌํ•ด๋ณด์ž!

$ 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

๊ทธ๋Ÿฌ๋ฉด, ๋ฒ„์ „ v1, v2์˜ helloworld ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๋ฐฐํฌ๋œ๋‹ค.

์ง€๊ธˆ ๋งŒ๋“ค์–ด์ง„ service/helloworld๋Š” v1, v2๋ฅผ round robin์œผ๋กœ ํŠธ๋ž˜ํ”ฝ์„ ๋ถ„์‚ฐํ•œ๋‹ค.

ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•ด ์•„๋ž˜ ๋ช…๋ น์–ด๋กœ ์ž„์‹œ Pod์„ ๋„์›Œ์„œ ํŠธ๋ž˜ํ”ฝ ๋ผ์šฐํŒ…์ด ์–ด๋–ป๊ฒŒ ๋˜๋Š”์ง€ ํ™•์ธํ•ด๋ณด์ž. (์ด๋•Œ, k port-forwardํ•œ ๊ฑธ๋กœ ์ ‘์†ํ•˜๋ฉด ์•ˆ ๋œ๋‹ค. ๊ทธ ์ด์œ ๋Š” ์š” ๊นƒํ—™ ์ด์Šˆ๋ฅผ ์ฐธ๊ณ )

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

v1 5ํšŒ, v2 8ํšŒ. ๊ฑฐ์˜ ๋ฐ˜๋ฐ˜ ์ •๋„๋กœ ๋ผ์šฐํŒ… ๋œ๋‹ค!

์•ˆ๋…•, Virtual Service!

๊ทธ๋Ÿผ ์ด์ œ istio์˜ Virtual Service๋กœ v1๊ณผ v2์˜ ํŠธ๋ž˜ํ”ฝ ๋น„์œจ์„ ์กฐ์ ˆํ•ด๋ณด์ž!

์ผ๋‹จ ์ง€๊ธˆ์€ v1, v2 ๋‘˜์„ ๊ฐ™์ด ์“ฐ๋Š” K8s Service ํ•˜๋‚˜๋งŒ ๊ตฌ์„ฑํ–ˆ๋Š”๋ฐ, ๊ฐ๊ฐ์œผ๋กœ ๋ผ์šฐํŒ… ํ•˜๋Š” ์ƒˆ๋กœ์šด 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

๊ทธ๋ฆฌ๊ณ  ์•„๋ž˜์™€ ๊ฐ™์ด Virtual Service๋ฅผ ๊ตฌ์„ฑํ•ด์„œ, v1์—๋Š” 10% ํŠธ๋ž˜ํ”ฝ์ด, v2์—๋Š” 90%์˜ ํŠธ๋ž˜ํ”ฝ์ด ํ๋ฅผ ์ˆ˜ ์žˆ๋„๋ก ๊ตฌ์„ฑํ•ด๋ณด์ž.

# simple-virtual-service
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: helloworld-vs
spec:
  hosts:
  - "helloworld.default.svc.cluster.local"
  http:
  - route:
    - destination:
        host: helloworld-v1.default.svc.cluster.local
      weight: 20 # 20% ํŠธ๋ž˜ํ”ฝ
    - destination:
        host: helloworld-v2.default.svc.cluster.local
      weight: 80 # 80% ํŠธ๋ž˜ํ”ฝ

์œ„์˜ Virtual Service๋ฅผ ์ƒ์„ฑํ•œ ํ›„์— ๋‹ค์‹œ nginx Pod์— ์ ‘์†ํ•ด์„œ ๋‹ค์‹œ ํŠธ๋ž˜ํ”ฝ์„ ํ˜๋ ค๋ณด์ž.

$ kubectl apply -f simple-virtual-service.yaml
$ kubectl exec -it nginx -- sh
# <on nginx pod>
while true; do curl "http://helloworld.default:5000/hello"; done

์™€์šฐ!! ํŠธ๋ž˜ํ”ฝ์˜ 20:80 ๋น„์œจ๋กœ ๋ถ„์‚ฐ๋˜์—ˆ๋‹ค!! เดฆเตเดฆเดฟ ห‰อˆฬ€๊’ณห‰อˆฬ )โœง

Gateway์™€ ํ•จ๊ป˜ Virtual Service๋ฅผ ๊ตฌ์„ฑ

istio์—์„œ ์ œ๊ณตํ•˜๋Š” ์˜ˆ์ œ์—์„œ๋Š” helloworld-gateway.yaml๋ผ๋Š” ํŒŒ์ผ์— IngressGateway์™€ VirtualService๋ฅผ ์ •์˜ํ•ด์„œ ์ œ๊ณตํ•˜๊ณ  ์žˆ๋‹ค.

์ด๋ฒˆ์—๋Š” IngressGateway์™€ ํ•จ๊ป˜ VirtualService๋ฅผ ๊ตฌ์„ฑํ•ด๋ณด์ž.

์ผ๋‹จ IngressGateway๋ถ€ํ„ฐ ์ด๋ ‡๊ฒŒ ์ •์˜ํ•˜์ž.

# helloworld-gateway.yaml
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: helloworld-gateway
spec:
  selector:
    istio: ingressgateway # use istio default controller
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "*"

๊ทธ๋ฆฌ๊ณ  VirtualService๋Š” ์ด๋ ‡๊ฒŒ ๊ตฌ์„ฑํ•œ๋‹ค. ์ด๋ฒˆ์—๋Š” ๊ตฌ๋ถ„์„ ์œ„ํ•ด์„œ v1:v2๋ฅผ 70:30์œผ๋กœ ๊ตฌ์„ฑํ•˜์ž. ์ด๋ฒˆ์—๋Š” v1 ํŠธ๋ž˜ํ”ฝ์ด ๋” ๋งŽ๋‹ค!

# hellworld-vs-with-gateway.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: helloworld-vs-w-gateway
spec:
  hosts:
  - "*"
  gateways:
  - helloworld-gateway
  http:
  - route:
    - destination:
        host: helloworld-v1.default.svc.cluster.local
        port:
          number: 5000
      weight: 70 # 70% ํŠธ๋ž˜ํ”ฝ
    - destination:
        host: helloworld-v2.default.svc.cluster.local
        port:
          number: 5000
      weight: 30 # 30% ํŠธ๋ž˜ํ”ฝ

๊ทธ๋ฆฌ๊ณ  ๋˜ nginx Pod์— ์ ‘์†ํ•ด์„œ ํŠธ๋ž˜ํ”ฝ์„ ํ˜๋ ค๋ณด์ž. ๊ทธ๋Ÿฐ๋ฐ ์ด๋ฒˆ์—๋Š” default ns์— ์žˆ๋Š” helloworld์˜ Service๊ฐ€ ์•„๋‹ˆ๋ผ istio-system ns์— ์žˆ๋Š” istio-ingressgateway์— ํŠธ๋ž˜ํ”ฝ์„ ๋ณด๋‚ด์•ผ ํ•œ๋‹ค.

$ kubectl apply -f helloworld-gateway.yaml
$ kubectl apply -f helloworld-vs-with-gateway.yaml
$ kubectl exec -it nginx -- sh
# <on nginx pod>
while true; do curl "http://istio-ingressgateway.istio-system/hello"; done

Kiali์—์„œ ํŠธ๋ž˜ํ”ฝ์„ ํ™•์ธํ•ด๋ณด๋ฉด,

์™€์›…! ์ด๋ฒˆ์—๋Š” istio์˜ default ingress gateway๋ฅผ ํ†ตํ•ด์„œ ํŠธ๋ž˜ํ”ฝ์ด ๋ถ„์‚ฐ๋˜๊ณ  ์žˆ๋‹ค! ูฉ(^แ—œ^ )ูˆ

VirtualService์™€ Gateway ์กฐ๊ธˆ ๋”๋ณด๊ธฐ

์š” ๋ถ€๋ถ„์€ VirtualService์™€ Gateway ๋ฆฌ์†Œ์Šค์— ๋Œ€ํ•ด ์กฐ๊ธˆ๋” ๊นŠ์€ ๋‚ด์šฉ์„ ๋‹ค๋ฃฌ๋‹ค. ์ด ๋ถ€๋ถ„์„ ์Šคํ‚ตํ•ด๋„ helloworld ์˜ˆ์ œ๋ฅผ ์ง„ํ–‰ํ•˜๋Š”๋ฐ ๋ฌธ์ œ๊ฐ€ ์—†์œผ๋‹ˆ, Istio์— ์ž…๋ฌธํ•œ์ง€ ์–ผ๋งˆ ๋˜์ง€ ์•Š์•˜๋‹ค๋ฉด, ๊ณผ๊ฐํžˆ DestinationRule์„ ์“ฐ๋Š” ๋‹ค์Œ ๋‹จ๊ณ„๋กœ ๋„˜์–ด๊ฐ€์ž!

์—ฌ๊ธฐ์„œ ์ž ๊น! IngressGateway์™€ ํ•จ๊ป˜ ์“ฐ๋ฉด์„œ, VirtualService์—์„œ ๋ฐ”๋€ ๋ถ€๋ถ„์€ hosts์™€ gateways ๋ถ€๋ถ„์ด๋‹ค.

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: helloworld-vs-w-gateway
spec:
  hosts: # ์š”๊ธฐ!
  - "*"
  gateways: # ์š”๊ธฐ๋„!
  - helloworld-gateway
  ...

์ด๋•Œ, hosts ๋ถ€๋ถ„์€ *๋ผ๋Š” ์™€์ผ๋“œ์นด๋“œ(wildcard)๊ฐ€ ์‚ฌ์šฉ๋˜์—ˆ๋Š”๋ฐ, ์ด ์™€์ผ๋“œ์นด๋“œ๋Š” ์˜ค์ง gateways ์†์„ฑ๊ณผ ํ•จ๊ป˜ ์“ธ ์ˆ˜ ์žˆ๋‹ค. ๋งŒ์•ฝ gateways์™€ ํ•จ๊ป˜ ์“ฐ์ง€ ์•Š๋Š”๋‹ค๋ฉด, ์•„๋ž˜์˜ ์—๋Ÿฌ๋ฅผ ๋ณด๊ฒŒ ๋œ๋‹ค.

admission webhook โ€œvalidation.istio.ioโ€ denied the request:
configuration is invalid: wildcard host * is not allowed for virtual services bound to the mesh gateway

์ด๋•Œ, โ€œmesh gatewayโ€œ๋ผ๋Š” ํ‘œํ˜„์ด ๋“ฑ์žฅํ•˜๋Š”๋ฐ, ๊ฐ„๋‹จํ•˜๊ฒŒ ๋งํ•˜์ž๋ฉด, istio์˜ ์ „์ฒด sidecar proxy์˜ ๋ชจ์Œ์„ ๋งํ•œ๋‹ค. ์ฆ‰, sidecar ์ „์ฒด๋ฅผ ๋Œ€์ƒ์œผ๋กœ ํ•  ๋•Œ๋Š” wildcard host *๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

๋‹คํ–‰ํžˆ ์šฐ๋ฆฌ๋Š” helloworld-gateway๋ผ๊ณ  gateway๋ฅผ ๋ช…์‹œํ•ด์ค˜์„œ wildcard host *๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค. wildcard host๋กœ ๋ช…์‹œํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— helloworld-gateway์—์„œ ๋“ค์–ด์˜ค๋Š” ๋ชจ๋“  ์š”์ฒญ์„ ์š” VirtualService์—์„œ ์ฒ˜๋ฆฌํ•˜๊ฒŒ ๋œ๋‹ค!

๊ทธ๋Ÿฐ๋ฐ, hosts์— wildcard host *๋ฅผ ์“ฐ๋Š” ๋ถ€๋ถ„์€ ๋˜ ์žˆ๋‹ค. ๋ฐ”๋กœ istio Gateway ๋ฆฌ์†Œ์Šค์˜ ์š” ๋ถ€๋ถ„์ด๋‹ค.

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: helloworld-gateway
spec:
  selector:
    istio: ingressgateway # use istio default controller
  servers:
  - port:
      number: 80
    hosts:
    - "*" # ์š”๊ธฐ!

Gateway์˜ spec.servers[].hosts ๋ถ€๋ถ„์€ ingress gateway๊ฐ€ ๋…ธ์ถœํ•˜๋Š” ํฌํŠธ๋ฅผ ์‚ฌ์šฉํ•  VirtualService์˜ hosts์— ๋Œ€ํ•œ ์กฐ๊ฑด์„ ๋ช…์‹œํ•œ๋‹ค. ๋งŒ์•ฝ, Gateway serversp[].hosts ๊ฐ’์ด *.example.com ์˜€๋‹ค๋ฉด, dev.example.com๊ณผ prod.example.com๋ฅผ host๋กœ ๊ฐ–๋Š” VirtualService๋Š” ํ•ด๋‹น Gateway๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, example.com์ด๋‚˜ newexample.com๋ฅผ host๋กœ ๊ฐ–๋Š” VirtualService๋Š” ํ•ด๋‹น Gateway๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค.

์ด๋ฒˆ ๊ฒฝ์šฐ๋Š” Gateway์—์„œ wildcard host *๋กœ ์ „์ฒด ํ—ˆ์šฉํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์–ด๋–ค VirtualService๋„ helloworld-gateway๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค!


์•ˆ๋…•, Destination Rule!

์ด๋ฒˆ์—” DestinationRule ๋ฆฌ์†Œ์Šค์™€ VirtualService๋ฅผ ์กฐํ•ฉํ•ด์„œ ํŠธ๋ž˜ํ”ฝ์„ ๋ถ„์‚ฐํ•ด๋ณด์ž!

์ผ๋‹จ ์‹œ์ž‘์— ์•ž์„œ ์•ž์—์„œ ๋งŒ๋“ค์–ด๋’€๋˜ ๋ฆฌ์†Œ์Šค๋Š” ๋ชจ๋‘ ์ง€์›Œ๋‘์ž. ์ฒซ ์˜ˆ์ œ์—์„œ ๋งŒ๋“ค์–ด๋’€๋˜ v1, v2์˜ K8s Service๊นŒ์ง€ ๋‘˜๋‹ค ์ง€์šด๋‹ค!

kubectl delete vs helloworld-vs-w-gateway
kubectl delete gw helloworld-gateway
kubectl delete svc helloworld-v1 helloworld-v2

์ด์ œ ์•„๋ž˜์™€ ๊ฐ™์ด DestinationRule์„ ์ •์˜ํ•˜๊ณ  apply ํ•ด๋ณด์ž!

# helloworld-dr.yaml
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: helloworld-dr
spec:
  host: helloworld.default.svc.cluster.local
  subsets: # subset๋“ค์„ ์ •์˜ํ•˜์ž!
  - name: helloworld-v1
    labels:
      version: v1
  - name: helloworld-v2
    labels:
      version: v2

๊ทธ๋ฆฌ๊ณ  ์•„๋ž˜์™€ ๊ฐ™์ด VirtualService๋ฅผ ๊ตฌ์„ฑํ•œ๋‹ค.

# helloworld-vs-w-dr.yaml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: helloworld-vs-w-dr
spec:
  hosts:
  - "helloworld.default.svc.cluster.local"
  http:
  - route:
    - destination:
        host: helloworld.default.svc.cluster.local
        subset: helloworld-v1 # ์ด๋ฒˆ์—๋Š” subset์„ ์“ด๋‹ค!
      weight: 70 # 70% ํŠธ๋ž˜ํ”ฝ
    - destination:
        host: helloworld.default.svc.cluster.local
        subset: helloworld-v2
      weight: 30 # 30% ํŠธ๋ž˜ํ”ฝ

๋ฆฌ์†Œ์Šค๋ฅผ ๋งŒ๋“  ํ›„์— ํŠธ๋ž˜ํ”ฝ์„ ํ˜๋ ค์ฃผ๋ฉดโ€ฆ

DestinationRule๋กœ ๋ฒ„์ „๋ณ„ subset์„ ๋งŒ๋“ค์—ˆ์„ ๋•Œ

์™€์šฐ! ์ด๋ฒˆ์—๋„ ํŠธ๋ž˜ํ”ฝ์ด ๋น„์œจ๋Œ€๋กœ ์ž˜ ๋ถ„์‚ฐ๋˜์—ˆ๋‹ค!! ๋†€๋ผ์šด ์ ์€ ๊ฐ ๋ฒ„์ „์— ๋Œ€์‘๋˜๋Š” K8s Service๋ฅผ ๋งŒ๋“ค์ง€ ์•Š๊ณ , DestinationRule์—์„œ selector๋กœ subset์„ ๋งŒ๋“ค๊ธฐ๋งŒ ํ–ˆ๋˜ ๊ฒƒ์ด๋‹ค!!

๋ฒ„์ „์— ๋Œ€์‘๋˜๋Š” K8s Service๋ฅผ ๋งŒ๋“ค์—ˆ์„ ๋•Œ

๋งบ์Œ๋ง

์ง€๊ธˆ๊นŒ์ง€ helloworld ์˜ˆ์ œ๋ฅผ ํ†ตํ•ด Istio VirtualService, Gateway, DestinationRule์˜ ์•„์ฃผ ๊ธฐ๋ณธ์ ์ธ ์‚ฌ์šฉ๋ฒ•์„ ์ตํ˜”๋‹ค!

Istio๋ผ๋Š” ๋…€์„์ด ์ฒ˜์Œ๋ณด๋ฉด ๊ฐ ์ปดํฌ๋„ŒํŠธ๋“ค์ด ์–ฝ๊ธฐ์„ค๊ธฐ ์—ฎ์—ฌ ์žˆ์—ฌ์„œ ํŒŒ์•…ํ•˜๊ธฐ๊ฐ€ ์–ด๋ ค์› ๋˜ ๊ฒƒ ๊ฐ™๋‹ค. ํŠนํžˆ ์ด๋ฏธ Istio๋กœ ์„œ๋น„์Šค๋ฉ”์‹œ๊ฐ€ ๊ตฌ์ถ•๋œ ์ƒํƒœ์—์„œ Istio๋ฅผ ๋ฐ›์•„๋“ค์ด๋ ค๊ณ  ํ•˜๋‹ˆ ํŠนํžˆ ๋จธ๋ฆฌ๊ฐ€ ๋” ๋ณต์žกํ–ˆ๋˜ ๊ฒƒ ๊ฐ™๋‹ค (๐–ฆนแฏ…๐–ฆน)

๋‹ค์Œ์œผ๋ก  Istio์—์„œ ์ œ๊ณตํ•˜๋Š” Book Info ์˜ˆ์ œ๋ฅผ ํ•œ๋ฒˆ ์‹ค์Šตํ•˜๊ณ  ์ •๋ฆฌํ•ด๋ณด๋ ค๊ณ  ํ•œ๋‹ค. ๊ทธ๋Ÿผ ๋น ์ž‰!