반응형
안녕하세요?
Istio in Action 책을 공부하면서 내용을 조금씩 정리해보려고 합니다.
5장은 트래픽 제어: 세밀한 트래픽 라우팅 입니다.
실습 환경 준비
- MacOS, OrbStack으로 Container 구동
- Kind, k8s 1.33.1
- istioctl 1.27.0
curl -L https://istio.io/downloadIstio | sh -
istioctl install --set profile=demo -y
|\
| \
| \
| \
/|| \
/ || \
/ || \
/ || \
/ || \
/ || \
/______||__________\
____________________
\__ _____/
\_____/
WARNING: Istio is being upgraded from 1.13.0 to 1.27.0.
Running this command will overwrite it; use revisions to upgrade alongside the existing version.
Before upgrading, you may wish to use 'istioctl x precheck' to check for upgrade warnings.
✔ Istio core installed ⛵️
✔ Istiod installed 🧠
✔ Egress gateways installed 🛫
✔ Ingress gateways installed 🛬
✔ Installation complete
5.1 새로운 코드 배포의 위험 줄이기
5.1.1 배포 vs. 릴리스
- 배포는 운영 환경에 설치되지만 실제 운영 환경 트래픽을 받지는 않는 코드.
- 배포가 운영 환경에 설치되면 스모크 테스트를 수행하고 검증
- 릴리즈는 실제 트래픽을 새 배포로 가져오는 것, 그러니까 운영 환경 트래픽을 배포로 이관하는 순간.
- 배포와 릴리즈를 구분해야 새로운 코드를 안전하게 운영 환경에 도입할 수 있다.
- 위와 같이 트래픽을 제어해 내부 직원들에게 보낼 수 있다.
- 실 트래픽의 대부분은 구 버젼이 받고, 신 버젼은 일부만을 받고 있는 이 방식을 "카나리한다"고 말한다.
- 그리고 이걸 "카나리 릴리즈" 라고 부른다.
- 새 코드가 예상, 검증했던 기능이나 동작, 성능을 전달하지 않는다면? 트래픽을 이전 버젼으로 릴리즈를 롤백할 수 있다.
5.2 이스티오로 요청 라우팅하기
- 다크 런치 : 대다수의 사용자는 잘 작동한다고 알려진 버젼으로 보내고, 특정 사용자 집단만 신 버젼으로 보내기
5.2.2 catalog 서비스 v1 배포하기
apiVersion: v1
kind: ServiceAccount
metadata:
name: catalog
---
apiVersion: v1
kind: Service
metadata:
labels:
app: catalog
name: catalog
spec:
ports:
- name: http
port: 80
protocol: TCP
targetPort: 3000
selector:
app: catalog
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: catalog
version: v1
name: catalog
spec:
replicas: 1
selector:
matchLabels:
app: catalog
version: v1
template:
metadata:
labels:
app: catalog
version: v1
spec:
serviceAccountName: catalog
containers:
- env:
- name: KUBERNETES_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
image: istioinaction/catalog:latest
imagePullPolicy: IfNotPresent
name: catalog
ports:
- containerPort: 3000
name: http
protocol: TCP
securityContext:
privileged: false
k apply -f services/catalog/kubernetes/catalog.yaml
serviceaccount/catalog unchanged
service/catalog created
deployment.apps/catalog created
catalog 서비스가 동작하는지 확인
k run -i -n default --rm --restart=Never dummy --image=curlimages/curl --command -- sh -c 'curl -s http://catalog.istioinaction/items'
[
{
"id": 1,
"color": "amber",
"department": "Eyewear",
"name": "Elinor Glasses",
"price": "282.00"
},
{
"id": 2,
"color": "cyan",
"department": "Clothing",
"name": "Atlas Shirt",
"price": "127.00"
},
{
"id": 3,
"color": "teal",
"department": "Clothing",
"name": "Small Metal Shoes",
"price": "232.00"
},
{
"id": 4,
"color": "red",
"department": "Watches",
"name": "Red Dragon Watch",
"price": "232.00"
}
]
gateway, virtualservice 배포
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: catalog-gateway
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "catalog.istioinaction.io
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: catalog-vs-from-gw
spec:
hosts:
- "catalog.istioinaction.io"
gateways:
- catalog-gateway
http:
- route:
- destination:
host: catalog
호출 테스트
curl http://192.168.97.2:31733/items -H "Host: catalog.istioinaction.io"
[
{
"id": 1,
"color": "amber",
"department": "Eyewear",
"name": "Elinor Glasses",
"price": "282.00"
},
{
"id": 2,
"color": "cyan",
"department": "Clothing",
"name": "Atlas Shirt",
"price": "127.00"
},
{
"id": 3,
"color": "teal",
"department": "Clothing",
"name": "Small Metal Shoes",
"price": "232.00"
},
{
"id": 4,
"color": "red",
"department": "Watches",
"name": "Red Dragon Watch",
"price": "232.00"
}
]
5.2.3 catalog 서비스 v2 배포하기
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: catalog
version: v2
name: catalog-v2
spec:
replicas: 1
selector:
matchLabels:
app: catalog
version: v2
template:
metadata:
labels:
app: catalog
version: v2
spec:
containers:
- env:
- name: KUBERNETES_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: SHOW_IMAGE
value: "true"
image: istioinaction/catalog:latest
imagePullPolicy: IfNotPresent
name: catalog
ports:
- containerPort: 3000
name: http
protocol: TCP
securityContext:
privileged: false
5.2.4 모든 트래픽 catalog 서비스 v1으로 라우팅하기
- 이스티오가 v1, v2를 식별하려면 subset을 지정하는 DestinationRule을 만들어야한다.
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: catalog
spec:
host: catalog.istioinaction.svc.cluster.local
subsets:
- name: version-v1
labels:
version: v1
- name: version-v2
labels:
version: v2
그리고 virtualservice를 v1으로만 향하도록 업데이트
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: catalog-vs-from-gw
spec:
hosts:
- "catalog.istioinaction.io"
gateways:
- catalog-gateway
http:
- route:
- destination:
host: catalog
subset: version-v1
10번 호출해서 v1으로만 향하는지 확인
for i in {1..10}; do curl http://192.168.97.2:31733/items -H "Host: catalog.istioinaction.io"; printf "\n\n"; done
[
{
"id": 1,
"color": "amber",
"department": "Eyewear",
"name": "Elinor Glasses",
"price": "282.00"
},
{
"id": 2,
"color": "cyan",
"department": "Clothing",
"name": "Atlas Shirt",
"price": "127.00"
},
{
"id": 3,
"color": "teal",
"department": "Clothing",
"name": "Small Metal Shoes",
"price": "232.00"
},
{
"id": 4,
"color": "red",
"department": "Watches",
"name": "Red Dragon Watch",
"price": "232.00"
}
]
[
{
"id": 1,
"color": "amber",
"department": "Eyewear",
"name": "Elinor Glasses",
"price": "282.00"
},
{
"id": 2,
"color": "cyan",
"department": "Clothing",
"name": "Atlas Shirt",
"price": "127.00"
},
{
"id": 3,
"color": "teal",
"department": "Clothing",
"name": "Small Metal Shoes",
"price": "232.00"
},
{
"id": 4,
"color": "red",
"department": "Watches",
"name": "Red Dragon Watch",
"price": "232.00"
}
]...
5.2.5 특정 요청을 v2로 라우팅하기
- HTTP 헤더 x-istio-cohort: internal 을 포함한 트래픽은 catalog v2로 보내고 싶은 경우
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: catalog-vs-from-gw
spec:
hosts:
- "catalog.istioinaction.io"
gateways:
- catalog-gateway
http:
- match:
- headers:
x-istio-cohort:
exact: "internal"
route:
- destination:
host: catalog
subset: version-v2
- route:
- destination:
host: catalog
subset: version-v1
위와 같이 수정한뒤, x-istio-cohort: internal 헤더를 추가해서 라우팅이 v2로 잘 되는 모습을 볼 수 있음
curl http://192.168.97.2:31733/items -H "Host: catalog.istioinaction.io" -H "x-istio-cohort: internal"
[
{
"id": 1,
"color": "amber",
"department": "Eyewear",
"name": "Elinor Glasses",
"price": "282.00",
"imageUrl": "http://lorempixel.com/640/480"
},
{
"id": 2,
"color": "cyan",
"department": "Clothing",
"name": "Atlas Shirt",
"price": "127.00",
"imageUrl": "http://lorempixel.com/640/480"
},
{
"id": 3,
"color": "teal",
"department": "Clothing",
"name": "Small Metal Shoes",
"price": "232.00",
"imageUrl": "http://lorempixel.com/640/480"
},
{
"id": 4,
"color": "red",
"department": "Watches",
"name": "Red Dragon Watch",
"price": "232.00",
"imageUrl": "http://lorempixel.com/640/480"
}
]%
5.2.6 호출 그래프 내 깊은 위치에서 라우팅
- 이스티오의 라우팅 기능은 엔보이의 기능에서 나온다.
- 요청별 라우팅의 경우, 애플리케이션이 주입한 헤더를 사용하거나
- Agent와 같이 알려진 헤더 또는 쿠키 값에 의존할 수도 있다.
아래는 지금까지와 달리 메시의 모든 사이드카에 적용하는 방법
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: catalog
spec:
hosts:
- catalog
gateways:
- mesh <----- 이 virtualservice는 메시의 모든 사이드카에 적용된다.
http:
- match:
- headers:
x-istio-cohort:
exact: "internal"
route:
- destination:
host: catalog
subset: version-v2
- route:
- destination:
host: catalog
subset: version-v1
5.3 트래픽 전환
아래의 경우 90%의 트래픽은 v1, 10%는 v2로 이동하게 된다.
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: catalog
spec:
hosts:
- catalog
gateways:
- mesh
http:
- route:
- destination:
host: catalog
subset: version-v1
weight: 90 <-----------v1에 대부분
- destination:
host: catalog
subset: version-v2
weight: 10 <-----------일부 v2로
트래픽을 반반으로 나누고 싶다면 가중치만 업데이트하면 된다.
만약 v1, v2외 다른 버젼이 있을경우 DestinationRule에서 subset으로 선언해야 한다.
5.3.1 Flagger로 카나리 릴리스하기
- 라우팅 변경을 수동으로 하는건 실수 등의 위험이 있으므로
- Flagger를 사용해서 서비스 릴리스를 자동화할 수 있다.
- Flagger는 서비스 상태를 판단할 때 메트릭에 의존하며
- 성공 메트릭을 사용하려면 프로메테우스를 설치해 이스티오 데이터 플레인을 수집하게 해야 한다.
helm repo add flagger https://flagger.app
WARNING: Kubernetes configuration file is group-readable. This is insecure. Location: /Users/juyoungjung/.kube/config
WARNING: Kubernetes configuration file is world-readable. This is insecure. Location: /Users/juyoungjung/.kube/config
"flagger" has been added to your repositories
k apply -f https://raw.githubusercontent.com/fluxcd/flagger/main/artifacts/flagger/crd.yaml
customresourcedefinition.apiextensions.k8s.io/canaries.flagger.app created
customresourcedefinition.apiextensions.k8s.io/metrictemplates.flagger.app created
customresourcedefinition.apiextensions.k8s.io/alertproviders.flagger.app created
helm install flagger flagger/flagger --namespace=istio-system --set crd.create=false --set meshProvider=i
stio --set metricsServer=http://prometheus:9090
WARNING: Kubernetes configuration file is group-readable. This is insecure. Location: /Users/juyoungjung/.kube/config
WARNING: Kubernetes configuration file is world-readable. This is insecure. Location: /Users/juyoungjung/.kube/config
NAME: flagger
LAST DEPLOYED: Thu Aug 28 09:42:24 2025
NAMESPACE: istio-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
Flagger installed
Flagger는 아래와 같은 Canary 리소스를 사용해서 카나리 릴리스의 파라미터를 지정할 수 있다.
그리고 Flagger가 알아서 리소스를 만들어서 배포한다.
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
name: catalog-release
namespace: istioinaction
spec:
targetRef: <----------------------- 카나리 대상 디플로이먼트
apiVersion: apps/v1
kind: Deployment
name: catalog
progressDeadlineSeconds: 60
# Service / VirtualService Config
service: <------------------------- 서비스용 설정
name: catalog
port: 80
targetPort: 3000
gateways:
- mesh
hosts:
- catalog
analysis: <------------------------ 카나리 진행 파라미터
interval: 45s <------ 45초마다 카나리의 각 단계를 평가
threshold: 5 <------ 5회 초과 지정범위와 다르면 카나리를 중단하고 롤백한다
maxWeight: 50 <------ 트래픽이 50%에 도달하면 100%로 바꾼다.
stepWeight: 10 <----- 단계별로 10%씩 늘린다.
match:
- sourceLabels:
app: webapp
metrics:
- name: request-success-rate
thresholdRange:
min: 99 <-------- 1분 동안의 성공률이 99%이상이어야 한다.
interval: 1m
- name: request-duration
thresholdRange:
max: 500 <------- p99의 요청시간은 500ms까지 허용한다.
interval: 30s
k get canary catalog-release -w
NAME STATUS WEIGHT LASTTRANSITIONTIME
catalog-release Initializing 0 2025-08-28T00:53:05Z
catalog-release Initialized 0 2025-08-28T00:53:57Z
Flagger가 자동 생성한 VirtualService는 아래와 같다.
k get virtualservice catalog -o yaml
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
annotations:
helm.toolkit.fluxcd.io/driftDetection: disabled
kustomize.toolkit.fluxcd.io/reconcile: disabled
creationTimestamp: "2025-08-28T00:53:57Z"
generation: 1
name: catalog
namespace: istioinaction
ownerReferences:
- apiVersion: flagger.app/v1beta1
blockOwnerDeletion: true
controller: true
kind: Canary
name: catalog-release
uid: 939a05a4-5492-454f-89f0-fb959d7318c2
resourceVersion: "40620"
uid: 4a8de726-0981-4e4f-b626-4194dba1a914
spec:
gateways:
- mesh
hosts:
- catalog
http:
- match:
- sourceLabels:
app: webapp
route:
- destination:
host: catalog-primary
weight: 100 <------------ 100으로 갈것임을 알 수 있다.
- destination:
host: catalog-canary
weight: 0
- route:
- destination:
host: catalog-primary
weight: 100
curl http://192.168.97.2:31733/api/catalog -H "Host: webapp.istioinaction.io"
[{"id":1,"color":"amber","department":"Eyewear","name":"Elinor Glasses","price":"282.00"},{"id":2,"color":"cyan","department":"Clothing","name":"Atlas Shirt","price":"127.00"},{"id":3,"color":"teal","department":"Clothing","name":"Small Metal Shoes","price":"232.00"},{"id":4,"color":"red","department":"Watches","name":"Red Dragon Watch","price":"232.00"}]
이제 catalog-v2를 배포해서 카나리 진척과정을 볼 수 있다.
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: catalog
version: v1
name: catalog
spec:
replicas: 1
selector:
matchLabels:
app: catalog
version: v1
template:
metadata:
labels:
app: catalog
version: v1
spec:
containers:
- env:
- name: KUBERNETES_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: SHOW_IMAGE
value: "true"
image: istioinaction/catalog:latest
imagePullPolicy: IfNotPresent
name: catalog
ports:
- containerPort: 3000
name: http
protocol: TCP
securityContext:
privileged: false
k apply -f ch5/flagger/catalog-deployment-v2.yaml
배포하면 자동으로로 카나리가 진행된다.
k get canary catalog-release -w
NAME STATUS WEIGHT LASTTRANSITIONTIME
catalog-release Initialized 0 2025-08-28T00:53:57Z
catalog-release Progressing 0 2025-08-28T01:01:27Z
catalog-release Progressing 10 2025-08-28T01:02:12Z
catalog-release Progressing 10 2025-08-28T01:02:57Z
Flagger가 자동으로 VirtualService의 가중치를 제어한 것을 볼 수 있다.
k get vs catalog -o yaml
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
annotations:
helm.toolkit.fluxcd.io/driftDetection: disabled
kustomize.toolkit.fluxcd.io/reconcile: disabled
creationTimestamp: "2025-08-28T00:53:57Z"
generation: 2
name: catalog
namespace: istioinaction
ownerReferences:
- apiVersion: flagger.app/v1beta1
blockOwnerDeletion: true
controller: true
kind: Canary
name: catalog-release
uid: 939a05a4-5492-454f-89f0-fb959d7318c2
resourceVersion: "41686"
uid: 4a8de726-0981-4e4f-b626-4194dba1a914
spec:
gateways:
- mesh
hosts:
- catalog
http:
- match:
- sourceLabels:
app: webapp
route:
- destination:
host: catalog-primary
weight: 90
- destination:
host: catalog-canary
weight: 10
- route:
- destination:
host: catalog-primary
weight: 90
반응형
'Kubernetes' 카테고리의 다른 글
Istio in Action 4장 - Istio Gateway : 클러스터로 트래픽 들이기 (0) | 2025.08.20 |
---|---|
Gateway API, AWS Gateway API Controller (0) | 2025.04.26 |
Vault로 K8S Secret 관리하기 (0) | 2025.04.10 |
EKS Blue/Green Migration (0) | 2025.04.05 |
Kubernetes Scheduling과 Scheduling plugins 파보기 (0) | 2025.03.20 |