ServiceMesh - Istio - Week1
Book for Istio study : ‘Istio in action’
표지는 그라셋 드 생소보르 컬렉션 중 아이슬란드 여인(Icelandic woman)이다.
1. 서비스 메시 소개하기
서비스 메시란?
분산 애플리케이션 네트워크 인프라
애플리케이션 간의 통신을 안전하고 복원력 있게 만들며 관찰 가능성과 제어 능력을 제공
Data Plane과 Control Plane으로 구성된 아키텍처를 통해 네트워크 트래픽을 관리하고 정책을 구현
특정 프로그래밍 언어나 프레임워크에 의존하지 않고도 중요한 네트워킹 기능을 애플리케이션 외부에서 구축할 수 있다.
서비스 메시는 왜 필요한가?
클라우드 네이티브 환경의 문제들
- 불규칙한 요청 처리 시간
- 서비스 간 통신에서 성능 저하가 발생하면 연쇄 장애를 일으킬 수 있다.
- 배포 자동화의 위험성
- 테스트 자동화로 잡히지 않는 버그가 배포되거나, 블루-그린 배포 접근법이 오히려 ‘빅뱅’ 릴리스로 이어질 수 있다.
- 보안의 일관성 부족
- 팀마다 다른 보안 접근법을 사용해 혼란이 야기된다.
서비스메시는 복원력, 보안, 메트릭 수집 등의 기능을 애플리케이션 외부에서 해결
효율성을 높이고(비싼 자원 == 개발자) 운영을 단순화
이스티오는 서비스 메시의 오픈소스 구현체이다.
주요 특징
- Data Plane
- Envoy 프록시를 기반으로 한 서비스 프록시를 사용해 트래픽 관리, 보안 강화, 메트릭 및 트레이싱 생성 등의 기능을 제공한다.
- Control Plane
- 운영자가 Data Plane의 동작을 제어할 수 있도록 API를 노출하며, 정책 설정과 보안 관리를 지원한다.
이스티오는 애플리케이션 코드 변경 없이도 복원력있는 시스템 구축을 가능하게 하며, 다양한 플랫폼(K8S, VM 등)에서 사용할 수 있다.
주요 기능
- 복원력 강화
- 재시도, 타임아웃, 서킷 브레이커 등을 통해 장애에 대한 대응력을 높임
- 관찰 가능성
- 메트릭과 트레이싱 데이터를 통해 시스템 상태를 실시간으로 파악
- 트래픽 제어
- 카나리 릴리즈, 단계적 롤아웃 등 세밀한 릴리스 전략 구현 지원
- 보안 강화
- mTLS를 통해 전송 계층 암호화를 적용하며, 정책 강제와 접근 제어를 지원
서비스 메시와 다른 기술 비교
- ESB
- 중앙집중식 구조로 인해 병목 현상 발생 가능
- 비즈니스 로직과 네트워킹 문제 혼합
- API 게이트웨이
- 공개 API 관리에 초점
- 내부 트래픽 처리시 병목 현상 유발 가능
- 서비스 메시
- 분산형 구조로 병목 지점 제거
- 복원력, 보안, 관찰 가능성 등 네트워킹 문제에만 집중
서비스 메시 도입시 고려사항
- 복잡성 증가
- 요청 경로에 프록시라는 레이어가 추가되어 디버깅이 어려워짐
- 프록시에 익숙치 않은 이들에게는 블랙박스가 되어 디버깅이 어려워질 수 있음
- 테넌시 관리
- 적절한 격리 모델 및 정책 없이는 여러 서비스 간 충돌 가능성이 존재함
- 운영 부담
- 새로운 계층으로 인해 조직 내 절차와 거버넌스를 재정비해야 할 필요가 있음
모든 기술에는 트레이드 오프가 존재
서비스 메시도 마찮가지다.
도입 전에 조직의 요구 사항과 제약 조건을 충분히 평가하고 계획적으로 실행해야 한다.
2. 이스티오 첫 걸음
1장에서는 서비스 메시에 대해 다루었다면, 2장에서부터 이스티오를 다룬다.
자세한 부분은 스터디가 진행되면서 깊이 다루겠지만, 우선 핸즈온 형식으로 이스티오가 어떤 구조로 무엇을 할 수 있는지 전체적인 청사진을 제공한다.
스터디에서는 kind를 통해 클러스터를 구성하고 실습을 진행한다.
1. Docker Desktop 설치
책에서는 최소 vCPU 4, Memory 8GB 할당 권고(그 이상이면 됨)
2. kind 및 utils 설치
필수
- kind
- kubectl
- helm
# Install Kind
brew install kind
kind --version
# Install kubectl
brew install kubernetes-cli
kubectl version --client=true
## kubectl -> k 단축키 설정
echo "alias k=kubectl" >> ~/.zshrc
# Install Helm
brew install helm
helm version
유틸
- krew
- kube-ps1
- kubectx
- kubecolor
- neat
- stern
# Utils 설치
brew install krew
brew install kube-ps1
brew install kubectx
# kubectl 출력 시 하이라이트 처리
brew install kubecolor
echo "alias kubectl=kubecolor" >> ~/.zshrc
echo "compdef kubecolor=kubectl" >> ~/.zshrc
# krew 플러그인 설치
kubectl krew install neat stern
kind 기본 사용
# Create a cluster with kind
kind create cluster
# 클러스터 배포 확인
kind get clusters
kind get nodes
kubectl cluster-info
# 노드 정보 확인
kubectl get node -o wide
# 파드 정보 확인
kubectl get pod -A
kubectl get componentstatuses
kind로 k8s 배포
# 클러스터 배포 전 확인
docker ps
# 방안1 : 환경변수 지정
export KUBECONFIG=$PWD/kubeconfig
# Create a cluster with kind : 1.29.14 , 1.30.10 , 1.31.6 , 1.32.2
kind create cluster --name myk8s --image kindest/node:v1.32.2 --config - <<EOF
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
extraPortMappings:
- containerPort: 30000
hostPort: 30000
- containerPort: 30001
hostPort: 30001
- containerPort: 30002
hostPort: 30002
- containerPort: 30003
hostPort: 30003
kubeadmConfigPatches:
- |
kind: ClusterConfiguration
controllerManager:
extraArgs:
bind-address: "0.0.0.0"
etcd:
local:
extraArgs:
listen-metrics-urls: "http://0.0.0.0:2381"
scheduler:
extraArgs:
bind-address: "0.0.0.0"
- |
kind: KubeProxyConfiguration
metricsBindAddress: "0.0.0.0"
EOF
# 확인
$ kind get nodes --name myk8s
myk8s-control-plane
$ kubens default
Context "kind-myk8s" modified.
Active namespace is "default".
# kind 는 별도 도커 네트워크 생성 후 사용 : 기본값 172.18.0.0/16
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
4b5fa1fad600 bridge bridge local
a98f566148c0 host host local
03a59a7c78a8 kind bridge local
840dfd9b7e86 minikube bridge local
1eef9e15a3b9 multinode-cluster bridge local
ff5e4368dd44 none null local
$ docker inspect kind | jq
[
{
"Name": "kind",
"Id": "03a59a7c78a86b83709bdf17ac47d6ebb7df2203917bc9fa0542481c80da6b86",
"Created": "2025-04-06T11:59:24.607331927Z",
"Scope": "local",
"Driver": "bridge",
"EnableIPv4": true,
"EnableIPv6": true,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.18.0.0/16",
"Gateway": "172.18.0.1"
},
{
"Subnet": "fc00:f853:ccd:e793::/64",
"Gateway": "fc00:f853:ccd:e793::1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"b20ffea7929859a4b608d875a61656fd0192acb30288be9d07e54f95c21bbe60": {
"Name": "myk8s-control-plane",
"EndpointID": "f3e6da308de04ef87206ddb53575c9bc74f15e64bddd32a749fa9d7c044b35c0",
"MacAddress": "3e:ea:44:2e:d6:d2",
"IPv4Address": "172.18.0.2/16",
"IPv6Address": "fc00:f853:ccd:e793::2/64"
}
},
"Options": {
"com.docker.network.bridge.enable_ip_masquerade": "true",
"com.docker.network.driver.mtu": "65535"
},
"Labels": {}
}
]
# k8s api 주소 확인 : 어떻게 로컬에서 접속이 되는 걸까?
$ kubectl cluster-info
Kubernetes control plane is running at https://127.0.0.1:50593
CoreDNS is running at https://127.0.0.1:50593/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
# 노드 정보 확인 : CRI 는 containerd 사용
$ kubectl get node -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
myk8s-control-plane Ready control-plane 2m29s v1.32.2 172.18.0.2 <none> Debian GNU/Linux 12 (bookworm) 6.10.14-linuxkit containerd://2.0.3
# 파드 정보 확인 : CNI 는 kindnet 사용
$ kubectl get pod -A -o wide
NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
kube-system coredns-668d6bf9bc-892kl 1/1 Running 0 2m48s 10.244.0.2 myk8s-control-plane <none> <none>
kube-system coredns-668d6bf9bc-fb48v 1/1 Running 0 2m48s 10.244.0.4 myk8s-control-plane <none> <none>
kube-system etcd-myk8s-control-plane 1/1 Running 0 2m55s 172.18.0.2 myk8s-control-plane <none> <none>
kube-system kindnet-z64s9 1/1 Running 0 2m48s 172.18.0.2 myk8s-control-plane <none> <none>
kube-system kube-apiserver-myk8s-control-plane 1/1 Running 0 2m55s 172.18.0.2 myk8s-control-plane <none> <none>
kube-system kube-controller-manager-myk8s-control-plane 1/1 Running 0 2m53s 172.18.0.2 myk8s-control-plane <none> <none>
kube-system kube-proxy-7rzjh 1/1 Running 0 2m48s 172.18.0.2 myk8s-control-plane <none> <none>
kube-system kube-scheduler-myk8s-control-plane 1/1 Running 0 2m55s 172.18.0.2 myk8s-control-plane <none> <none>
local-path-storage local-path-provisioner-7dc846544d-7q5l6 1/1 Running 0 2m48s 10.244.0.3 myk8s-control-plane <none> <none>
# 네임스페이스 확인 >> 도커 컨테이너에서 배운 네임스페이스와 다릅니다!
$ kubectl get namespaces
NAME STATUS AGE
default Active 3m19s
kube-node-lease Active 3m18s
kube-public Active 3m19s
kube-system Active 3m19s
local-path-storage Active 3m15s
# 컨트롤플레인노드(컨테이너) 확인 : 도커 컨테이너 이름은 myk8s-control-plane
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b20ffea79298 kindest/node:v1.32.2 "/usr/local/bin/entr…" 3 minutes ago Up 3 minutes 0.0.0.0:30000-30003->30000-30003/tcp, 127.0.0.1:50593->6443/tcp myk8s-control-plane
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
kindest/node v1.32.2 142f543559cc 4 weeks ago 1.5GB
kindest/node <none> f226345927d7 7 weeks ago 1.5GB
gcr.io/k8s-minikube/kicbase v0.0.45 7c93b02056db 7 months ago 1.67GB
gcr.io/k8s-minikube/kicbase <none> 81df28859520 7 months ago 1.67GB
kindest/node v1.23.17 14d0a9a892b9 13 months ago 1.31GB
$ docker exec -it myk8s-control-plane ss -tnlp
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 4096 127.0.0.1:10248 0.0.0.0:* users:(("kubelet",pid=673,fd=22))
LISTEN 0 4096 127.0.0.11:35265 0.0.0.0:*
LISTEN 0 4096 127.0.0.1:2379 0.0.0.0:* users:(("etcd",pid=615,fd=9))
LISTEN 0 4096 172.18.0.2:2380 0.0.0.0:* users:(("etcd",pid=615,fd=7))
LISTEN 0 4096 172.18.0.2:2379 0.0.0.0:* users:(("etcd",pid=615,fd=8))
LISTEN 0 4096 127.0.0.1:36009 0.0.0.0:* users:(("containerd",pid=105,fd=11))
LISTEN 0 4096 *:2381 *:* users:(("etcd",pid=615,fd=13))
LISTEN 0 4096 *:10250 *:* users:(("kubelet",pid=673,fd=19))
LISTEN 0 4096 *:10249 *:* users:(("kube-proxy",pid=898,fd=21))
LISTEN 0 4096 *:10259 *:* users:(("kube-scheduler",pid=503,fd=3))
LISTEN 0 4096 *:10257 *:* users:(("kube-controller",pid=539,fd=3))
LISTEN 0 4096 *:10256 *:* users:(("kube-proxy",pid=898,fd=9))
LISTEN 0 4096 *:6443 *:* users:(("kube-apiserver",pid=533,fd=3))
# 디버그용 내용 출력에 ~/.kube/config 권한 인증 로드
$ kubectl get pod -v6
I0408 20:25:47.930544 72014 loader.go:395] Config loaded from file: /Users/******/.kube/config
I0408 20:25:47.949372 72014 round_trippers.go:553] GET https://127.0.0.1:50593/api/v1/namespaces/default/pods?limit=500 200 OK in 12 milliseconds
No resources found in default namespace.
# kube config 파일 확인
cat $KUBECONFIG
ls -l $KUBECONFIG
kube-ops-view
# kube-ops-view
# helm show values geek-cookbook/kube-ops-view
$ helm repo add geek-cookbook https://geek-cookbook.github.io/charts/
helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --set service.main.type=NodePort,service.main.ports.http.nodePort=30000 --set env.TZ="Asia/Seoul" --namespace kube-system
"geek-cookbook" already exists with the same configuration, skipping
NAME: kube-ops-view
LAST DEPLOYED: Tue Apr 8 20:26:32 2025
NAMESPACE: kube-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
1. Get the application URL by running these commands:
export NODE_PORT=$(kubectl get --namespace kube-system -o jsonpath="{.spec.ports[0].nodePort}" services kube-ops-view)
export NODE_IP=$(kubectl get nodes --namespace kube-system -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
# 설치 확인
$ kubectl get deploy,pod,svc,ep -n kube-system -l app.kubernetes.io/instance=kube-ops-view
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/kube-ops-view 1/1 1 1 59s
NAME READY STATUS RESTARTS AGE
pod/kube-ops-view-6658c477d4-krv4n 1/1 Running 0 59s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kube-ops-view NodePort 10.96.47.104 <none> 8080:30000/TCP 59s
NAME ENDPOINTS AGE
endpoints/kube-ops-view 10.244.0.5:8080 59s
# kube-ops-view 접속 URL 확인 (2 배율)
$ open "http://127.0.0.1:30000/#scale=2"
클러스터 삭제
# 클러스터 삭제
kind delete cluster --name myk8s
docker ps
cat $KUBECONFIG
unset KUBECONFIG
실습!!
실습 환경 준비
#
git clone https://github.com/AcornPublishing/istio-in-action
cd istio-in-action/book-source-code-master
#
kind create cluster --name myk8s --image kindest/node:v1.23.17 --config - <<EOF
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
extraPortMappings:
- containerPort: 30000 # Sample Application (istio-ingrssgateway)
hostPort: 30000
- containerPort: 30001 # Prometheus
hostPort: 30001
- containerPort: 30002 # Grafana
hostPort: 30002
- containerPort: 30003 # Kiali
hostPort: 30003
- containerPort: 30004 # Tracing
hostPort: 30004
- containerPort: 30005 # kube-ops-view
hostPort: 30005
extraMounts:
- hostPath: /... # 각자 자신의 pwd 경로로 설정
containerPath: /istiobook
networking:
podSubnet: 10.10.0.0/16
serviceSubnet: 10.200.1.0/24
EOF
# 설치 확인
docker ps
# 노드에 기본 툴 설치
docker exec -it myk8s-control-plane sh -c 'apt update && apt install tree psmisc lsof wget bridge-utils net-tools dnsutils tcpdump ngrep iputils-ping git vim -y'
# (옵션) kube-ops-view
helm repo add geek-cookbook https://geek-cookbook.github.io/charts/
helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --set service.main.type=NodePort,service.main.ports.http.nodePort=30005 --set env.TZ="Asia/Seoul" --namespace kube-system
kubectl get deploy,pod,svc,ep -n kube-system -l app.kubernetes.io/instance=kube-ops-view
## kube-ops-view 접속 URL 확인
open "http://localhost:30005/#scale=1.5"
open "http://localhost:30005/#scale=1.3"
# (옵션) metrics-server
helm repo add metrics-server https://kubernetes-sigs.github.io/metrics-server/
helm install metrics-server metrics-server/metrics-server --set 'args[0]=--kubelet-insecure-tls' -n kube-system
kubectl get all -n kube-system -l app.kubernetes.io/instance=metrics-server
2.2 이스티오 컨트롤 플레인 알아보기
# myk8s-control-plane 진입 후 설치 진행
docker exec -it myk8s-control-plane bash
-----------------------------------
# 코드 파일들 마운트 확인
$ tree /istiobook/ -L 1
/istiobook/
|-- README.md
|-- appendices
|-- bin
|-- ch10
|-- ch11
|-- ch12
|-- ch13
|-- ch14
|-- ch2
|-- ch3
|-- ch4
|-- ch5
|-- ch6
|-- ch7
|-- ch8
|-- ch9
|-- services
# istioctl 설치
$ export ISTIOV=1.17.8
$ echo 'export ISTIOV=1.17.8' >> /root/.bashrc
$ curl -s -L https://istio.io/downloadIstio | ISTIO_VERSION=$ISTIOV sh -
$ tree istio-$ISTIOV -L 2 # sample yaml 포함
istio-1.17.8
|-- LICENSE
|-- README.md
|-- bin
| -- istioctl
|-- manifest.yaml
|-- manifests
| |-- charts
| |-- examples
| -- profiles
|-- samples
| |-- README.md
| |-- addons
| |-- bookinfo
| |-- certs
| |-- cicd
| |-- custom-bootstrap
| |-- extauthz
| |-- external
| |-- grpc-echo
| |-- health-check
| |-- helloworld
| |-- httpbin
| |-- jwt-server
| |-- kind-lb
| |-- multicluster
| |-- open-telemetry
| |-- operator
| |-- ratelimit
| |-- security
| |-- sleep
| |-- tcp-echo
| |-- wasm_modules
| -- websockets
|-- tools
|-- _istioctl
|-- certs
-- istioctl.bash
$ cp istio-$ISTIOV/bin/istioctl /usr/local/bin/istioctl
$ istioctl version --remote=false
1.17.8
# default 프로파일 컨트롤 플레인 배포
$ istioctl x precheck # 설치 전 k8s 조건 충족 검사
✔ No issues found when checking the cluster. Istio is safe to install or upgrade!
To get started, check out https://istio.io/latest/docs/setup/getting-started/
$ istioctl profile list
Istio configuration profiles:
ambient
default
demo
empty
external
minimal
openshift
preview
remote
$ istioctl install --set profile=default -y
✔ Istio core installed
✔ Istiod installed
✔ Ingress gateways installed
✔ Installation complete
# 설치 확인 : istiod, istio-ingressgateway, crd 등
$ kubectl get all,svc,ep,sa,cm,secret,pdb -n istio-system
...
NAME READY STATUS RESTARTS AGE
istio-ingressgateway-58888b4f9b-gv7r9 1/1 Running 0 2m43s
istiod-78c465d86b-tsd8l 1/1 Running 0 3m
...
$ kubectl get crd | grep istio.io | sort
authorizationpolicies.security.istio.io 2025-04-08T11:35:47Z
destinationrules.networking.istio.io 2025-04-08T11:35:47Z
envoyfilters.networking.istio.io 2025-04-08T11:35:47Z
gateways.networking.istio.io 2025-04-08T11:35:47Z
istiooperators.install.istio.io 2025-04-08T11:35:47Z
peerauthentications.security.istio.io 2025-04-08T11:35:47Z
proxyconfigs.networking.istio.io 2025-04-08T11:35:47Z
requestauthentications.security.istio.io 2025-04-08T11:35:47Z
serviceentries.networking.istio.io 2025-04-08T11:35:47Z
sidecars.networking.istio.io 2025-04-08T11:35:47Z
telemetries.telemetry.istio.io 2025-04-08T11:35:47Z
virtualservices.networking.istio.io 2025-04-08T11:35:47Z
wasmplugins.extensions.istio.io 2025-04-08T11:35:47Z
workloadentries.networking.istio.io 2025-04-08T11:35:47Z
workloadgroups.networking.istio.io 2025-04-08T11:35:47Z
$ istioctl verify-install # 설치 확인
...
Checked 15 custom resource definitions
Checked 2 Istio Deployments
✔ Istio is installed and verified successfully
# 보조 도구 설치
$ kubectl apply -f istio-$ISTIOV/samples/addons
#
kubectl get pod -n istio-system
NAME READY STATUS RESTARTS AGE
grafana-b854c6c8-hmg6f 1/1 Running 0 72s
istio-ingressgateway-996bc6bb6-hgfzp 1/1 Running 0 2m34s
istiod-7df6ffc78d-4r28x 1/1 Running 0 2m48s
jaeger-5556cd8fcf-pkbdf 1/1 Running 0 72s
kiali-648847c8c4-zf9lx 1/1 Running 0 72s
prometheus-7b8b9dd44c-mmn8p 2/2 Running 0 72s
# 빠져나오기
exit
-----------------------------------
#
$ kubectl get cm -n istio-system istio -o yaml
apiVersion: v1
data:
mesh: |-
defaultConfig:
discoveryAddress: istiod.istio-system.svc:15012
proxyMetadata: {}
tracing:
zipkin:
address: zipkin.istio-system:9411
enablePrometheusMerge: true
rootNamespace: istio-system
trustDomain: cluster.local
meshNetworks: 'networks: {}'
kind: ConfigMap
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","data":{"mesh":"defaultConfig:\n discoveryAddress: istiod.istio-system.svc:15012\n proxyMetadata: {}\n tracing:\n zipkin:\n address: zipkin.istio-system:9411\nenablePrometheusMerge: true\nrootNamespace: istio-system\ntrustDomain: cluster.local","meshNetworks":"networks: {}"},"kind":"ConfigMap","metadata":{"annotations":{},"labels":{"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.17.8","release":"istio"},"name":"istio","namespace":"istio-system"}}
creationTimestamp: "2025-04-08T11:35:47Z"
labels:
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.17.8
release: istio
name: istio
namespace: istio-system
resourceVersion: "1056"
uid: c316f813-0e90-44c4-bdd8-871141f93e64
## install neast
$ kubectl krew install neat
$ export PATH="${KREW_ROOT:-$HOME/.krew}/bin:$PATH"
$ kubectl get cm -n istio-system istio -o yaml | kubectl neat
apiVersion: v1
data:
mesh: |-
defaultConfig:
discoveryAddress: istiod.istio-system.svc:15012
proxyMetadata: {}
tracing:
zipkin:
address: zipkin.istio-system:9411
enablePrometheusMerge: true
rootNamespace: istio-system
trustDomain: cluster.local
meshNetworks: 'networks: {}'
kind: ConfigMap
metadata:
labels:
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.17.8
release: istio
name: istio
namespace: istio-system
2.3 서비스 메시에 첫 애플리케이션 배포해보기
#
$ kubectl create ns istioinaction
namespace/istioinaction created
# 방법1 : yaml에 sidecar 설정을 추가
cat services/catalog/kubernetes/catalog.yaml
docker exec -it myk8s-control-plane istioctl kube-inject -f /istiobook/services/catalog/kubernetes/catalog.yaml
...
- args:
- proxy
- sidecar
- --domain
- $(POD_NAMESPACE).svc.cluster.local
- --proxyLogLevel=warning
- --proxyComponentLogLevel=misc:error
- --log_output_level=default:info
- --concurrency
- "2"
env:
- name: JWT_POLICY
value: third-party-jwt
- name: PILOT_CERT_PROVIDER
value: istiod
- name: CA_ADDR
value: istiod.istio-system.svc:15012
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
...
image: docker.io/istio/proxyv2:1.13.0
name: istio-proxy
# 방법2 : namespace에 레이블을 추가하면 istiod (오퍼레이터)가 해당 namepsace의 pod spec에 자동으로 sidecar 설정을 주입
$ kubectl label namespace istioinaction istio-injection=enabled
namespace/istioinaction labeled
$ kubectl get ns --show-labels
NAME STATUS AGE LABELS
default Active 12m kubernetes.io/metadata.name=default
...
istioinaction Active 67s istio-injection=enabled,kubernetes.io/metadata.name=istioinaction
...
#
$ kubectl get mutatingwebhookconfiguration
NAME WEBHOOKS AGE
istio-revision-tag-default 4 7m59s # 특정 revision의 사이드카 주입 설정 관리
istio-sidecar-injector 4 8m25s # Istio는 각 애플리케이션 Pod에 Envoy 사이드카 프록시를 자동으로 주입
## 네임스페이스나 Pod에 istio-injection=enabled 라벨이 있어야 작동
$ kubectl get mutatingwebhookconfiguration istio-sidecar-injector -o yaml
#
$ kubectl get cm -n istio-system istio-sidecar-injector -o yaml | kubectl neat
#
$ cat services/catalog/kubernetes/catalog.yaml
$ kubectl apply -f services/catalog/kubernetes/catalog.yaml -n istioinaction
serviceaccount/catalog created
service/catalog created
deployment.apps/catalog created
$ cat services/webapp/kubernetes/webapp.yaml
$ kubectl apply -f services/webapp/kubernetes/webapp.yaml -n istioinaction
serviceaccount/webapp created
service/webapp created
deployment.apps/webapp created
#
$ kubectl get pod -n istioinaction
NAME READY STATUS RESTARTS AGE
catalog-6cf4b97d-qx8ln 2/2 Running 0 38s
webapp-7685bcb84-jkf25 2/2 Running 0 17s
# 접속 테스트용 netshoot 파드 생성
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: netshoot
spec:
containers:
- name: netshoot
image: nicolaka/netshoot
command: ["tail"]
args: ["-f", "/dev/null"]
terminationGracePeriodSeconds: 0
EOF
# catalog 접속 확인
kubectl exec -it netshoot -- curl -s http://catalog.istioinaction/items/1 | jq
{
"id": 1,
"color": "amber",
"department": "Eyewear",
"name": "Elinor Glasses",
"price": "282.00"
}
# webapp 접속 확인
kubectl exec -it netshoot -- curl -s http://webapp.istioinaction/api/catalog/items/1 | jq
{
"id": 1,
"color": "amber",
"department": "Eyewear",
"name": "Elinor Glasses",
"price": "282.00"
}
# 아래 방법 대신 임시 사용
kubectl port-forward -n istioinaction deploy/webapp 8080:8080
확인 후 CTRL+C 로 종료
#
open http://localhost:8080
2.4 복원력, 관찰 가능성, 트래픽 제어 기능을 갖춘 이스티오의 능력 살펴보기
# istioctl proxy-status : 단축어 ps
$ docker exec -it myk8s-control-plane istioctl proxy-status
NAME CLUSTER CDS LDS EDS RDS ECDS ISTIOD VERSION
catalog-6cf4b97d-qx8ln.istioinaction Kubernetes SYNCED SYNCED SYNCED SYNCED NOT SENT istiod-7df6ffc78d-4r28x 1.17.8
istio-ingressgateway-996bc6bb6-hgfzp.istio-system Kubernetes SYNCED SYNCED SYNCED NOT SENT NOT SENT istiod-7df6ffc78d-4r28x 1.17.8
webapp-7685bcb84-jkf25.istioinaction Kubernetes SYNCED SYNCED SYNCED SYNCED NOT SENT istiod-7df6ffc78d-4r28x 1.17.8
$ docker exec -it myk8s-control-plane istioctl ps
NAME CLUSTER CDS LDS EDS RDS ECDS ISTIOD VERSION
catalog-6cf4b97d-qx8ln.istioinaction Kubernetes SYNCED SYNCED SYNCED SYNCED NOT SENT istiod-7df6ffc78d-4r28x 1.17.8
istio-ingressgateway-996bc6bb6-hgfzp.istio-system Kubernetes SYNCED SYNCED SYNCED NOT SENT NOT SENT istiod-7df6ffc78d-4r28x 1.17.8
webapp-7685bcb84-jkf25.istioinaction Kubernetes SYNCED SYNCED SYNCED SYNCED NOT SENT istiod-7df6ffc78d-4r28x 1.17.8
#
$ cat ch2/ingress-gateway.yaml
$ cat <<EOF | kubectl -n istioinaction apply -f -
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: outfitters-gateway
namespace: istioinaction
spec:
selector:
istio: ingressgateway # use istio default controller
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "*"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: webapp-virtualservice
namespace: istioinaction
spec:
hosts:
- "*"
gateways:
- outfitters-gateway
http:
- route:
- destination:
host: webapp
port:
number: 80
EOF
#
$ kubectl get gw,vs -n istioinaction
NAME AGE
gateway.networking.istio.io/outfitters-gateway 17s
NAME GATEWAYS HOSTS AGE
virtualservice.networking.istio.io/webapp-virtualservice ["outfitters-gateway"] ["*"] 17s
# istioctl proxy-status : 단축어 ps
$ docker exec -it myk8s-control-plane istioctl proxy-status
NAME CLUSTER CDS LDS EDS RDS ECDS ISTIOD VERSION
catalog-6cf4b97d-qx8ln.istioinaction Kubernetes SYNCED SYNCED SYNCED SYNCED NOT SENT istiod-7df6ffc78d-4r28x 1.17.8
istio-ingressgateway-996bc6bb6-hgfzp.istio-system Kubernetes SYNCED SYNCED SYNCED SYNCED NOT SENT istiod-7df6ffc78d-4r28x 1.17.8
webapp-7685bcb84-jkf25.istioinaction Kubernetes SYNCED SYNCED SYNCED SYNCED NOT SENT istiod-7df6ffc78d-4r28x 1.17.8
ISTIOIGW=istio-ingressgateway-996bc6bb6-hgfzp.istio-system
WEBAPP=webapp-7685bcb84-jkf25.istioinaction
# istioctl proxy-config : 단축어 pc
docker exec -it myk8s-control-plane istioctl proxy-config all $ISTIOIGW
docker exec -it myk8s-control-plane istioctl proxy-config all $WEBAPP
docker exec -it myk8s-control-plane istioctl proxy-config listener $ISTIOIGW
docker exec -it myk8s-control-plane istioctl proxy-config route $ISTIOIGW
docker exec -it myk8s-control-plane istioctl proxy-config cluster $ISTIOIGW
docker exec -it myk8s-control-plane istioctl proxy-config endpoint $ISTIOIGW
docker exec -it myk8s-control-plane istioctl proxy-config log $ISTIOIGW
docker exec -it myk8s-control-plane istioctl proxy-config listener $WEBAPP
docker exec -it myk8s-control-plane istioctl proxy-config route $WEBAPP
docker exec -it myk8s-control-plane istioctl proxy-config cluster $WEBAPP
docker exec -it myk8s-control-plane istioctl proxy-config endpoint $WEBAPP
docker exec -it myk8s-control-plane istioctl proxy-config log $WEBAPP
# envoy 가 사용하고 있는 인증서 정보 확인
docker exec -it myk8s-control-plane istioctl proxy-config secret $ISTIOIGW
docker exec -it myk8s-control-plane istioctl proxy-config secret $WEBAPP
#
$ docker exec -it myk8s-control-plane istioctl proxy-config routes deploy/istio-ingressgateway.istio-system
NAME DOMAINS MATCH VIRTUAL SERVICE
http.8080 * /* webapp-virtualservice.istioinaction
* /healthz/ready*
* /stats/prometheus*
# istio-ingressgateway 서비스 NodePort 변경 및 nodeport 30000로 지정 변경
$ kubectl get svc,ep -n istio-system istio-ingressgateway
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/istio-ingressgateway LoadBalancer 10.200.1.232 <pending> 15021:30243/TCP,80:31244/TCP,443:30816/TCP 24m
NAME ENDPOINTS AGE
endpoints/istio-ingressgateway 10.10.0.8:15021,10.10.0.8:8080,10.10.0.8:8443 24m
$ kubectl patch svc -n istio-system istio-ingressgateway -p '{"spec": {"type": "NodePort", "ports": [{"port": 80, "targetPort": 8080, "nodePort": 30000}]}}'
service/istio-ingressgateway patched
$ kubectl get svc -n istio-system istio-ingressgateway
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
istio-ingressgateway NodePort 10.200.1.232 <none> 15021:30243/TCP,80:30000/TCP,443:30816/TCP 24m
# istio-ingressgateway 서비스 externalTrafficPolicy 설정 : ClientIP 수집 확인
$ kubectl patch svc -n istio-system istio-ingressgateway -p '{"spec":{"externalTrafficPolicy": "Local"}}'
service/istio-ingressgateway patched
$ kubectl describe svc -n istio-system istio-ingressgateway
...
External Traffic Policy: Local
#
$ kubectl stern -l app=webapp -n istioinaction
$ kubectl stern -l app=catalog -n istioinaction
#
$ curl -s http://127.0.0.1:30000/api/catalog | jq
[
{
"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"
}
]
$ curl -s http://127.0.0.1:30000/api/catalog/items/1 | jq
{
"id": 1,
"color": "amber",
"department": "Eyewear",
"name": "Elinor Glasses",
"price": "282.00"
}
$ curl -s http://127.0.0.1:30000/api/catalog -I | head -n 1
HTTP/1.1 200 OK
# webapp 반복 호출
while true; do curl -s http://127.0.0.1:30000/api/catalog/items/1 ; sleep 1; echo; done
while true; do curl -s http://127.0.0.1:30000/api/catalog -I | head -n 1 ; date "+%Y-%m-%d %H:%M:%S" ; sleep 1; echo; done
while true; do curl -s http://127.0.0.1:30000/api/catalog -I | head -n 1 ; date "+%Y-%m-%d %H:%M:%S" ; sleep 0.5; echo; done
2.4.1 이스티오 관찰 가능성
# NodePort 변경 및 nodeport 30001~30003으로 변경 : prometheus(30001), grafana(30002), kiali(30003), tracing(30004)
$ kubectl patch svc -n istio-system prometheus -p '{"spec": {"type": "NodePort", "ports": [{"port": 9090, "targetPort": 9090, "nodePort": 30001}]}}'
$ kubectl patch svc -n istio-system grafana -p '{"spec": {"type": "NodePort", "ports": [{"port": 3000, "targetPort": 3000, "nodePort": 30002}]}}'
$ kubectl patch svc -n istio-system kiali -p '{"spec": {"type": "NodePort", "ports": [{"port": 20001, "targetPort": 20001, "nodePort": 30003}]}}'
$ kubectl patch svc -n istio-system tracing -p '{"spec": {"type": "NodePort", "ports": [{"port": 80, "targetPort": 16686, "nodePort": 30004}]}}'
service/prometheus patched
service/grafana patched
service/kiali patched
service/tracing patched
# Prometheus 접속 : envoy, istio 메트릭 확인
open http://127.0.0.1:30001
# Grafana 접속
open http://127.0.0.1:30002
# Kiali 접속 1 : NodePort
open http://127.0.0.1:30003
# (옵션) Kiali 접속 2 : Port forward
kubectl port-forward deployment/kiali -n istio-system 20001:20001 &
open http://127.0.0.1:20001
# tracing 접속 : 예거 트레이싱 대시보드
open http://127.0.0.1:30004
그라파나 확인: 대시보드 - Istio Service Dashboard -> 상단 Service(webapp 선택)
트래픽 반복 접속 해둥 상태
오픈 트레이싱을 통한 분산 트레이싱: Jaeger 트레이싱 대시보드 확인
Kiali 확인
- Namespace를 istioinaction으로 선택 후 Graph(Traffic, Versioned app graph)에서 Display 옵션 중 ‘Traffic Distribution’, ‘Traffic Animation’ 활성화!
- Service nodes, Security 체크(last 1m, Evety 10s)
2.4.2 복원력을 위한 이스티오
catalog에 의도적으로 500 에러를 재현하고 retry로 복원력 높이기 먄약 ‘간혈적/일시전 네트워크 오류’가 발생하여 webapp은 catalog 요청이 실패하는 경우, 애플리케이션 코드 수정 없이 복원력을 높여보자!
# bin/chaos.sh {error code} {frequency}
#!/usr/bin/env bash
if [ $1 == "500" ]; then
POD=$(kubectl get pod | grep catalog | awk '{ print $1 }')
echo $POD
for p in $POD; do
if [ ${2:-"false"} == "delete" ]; then
echo "Deleting 500 rule from $p"
kubectl exec -c catalog -it $p -- curl -X POST -H "Content-Type: application/json" -d '{"active":
false, "type": "500"}' localhost:3000/blowup
else
PERCENTAGE=${2:-100}
kubectl exec -c catalog -it $p -- curl -X POST -H "Content-Type: application/json" -d '{"active":
true, "type": "500", "percentage": '"${PERCENTAGE}"'}' localhost:3000/blowup
echo ""
fi
done
fi
#
$ docker exec -it myk8s-control-plane bash
----------------------------------------
# istioinaction 로 네임스페이스 변경
$ cat /etc/kubernetes/admin.conf
$ kubectl config set-context $(kubectl config current-context) --namespace=istioinaction
Context "kubernetes-admin@myk8s" modified.
$ cat /etc/kubernetes/admin.conf
$ cd /istiobook/bin/
$ chmod +x chaos.sh
$ ./chaos.sh 500 100 # 모니터링 : kiali, grafana, tracing
catalog-6cf4b97d-qx8ln
blowups=[object Object]
$ ./chaos.sh 500 50 # 모니터링 : kiali, grafana, tracing
catalog-6cf4b97d-qx8ln
blowups=[object Object]
kubectl config set-context $(kubectl config current-context) --namespace=default
cat /etc/kubernetes/admin.conf
----------------------------------------
-
./chaos.sh 500 100
-
./chaos.sh 500 50
에러 발생 시 reslience 하게 retry 하도록 애플리케이션 코드 수정 없이 해보기! Resiliency하게 해보자 -> Proxy(envoy)에 endpoint(catalog) 5xx 에러시 retry 적용
(on myk8s-control-plane)
# catalog 3번까지 요청 재시도 할 수 있고, 각 시도에는 2초의 제한 시간이 있음.
$ cat <<EOF | kubectl -n istioinaction apply -f -
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: catalog
spec:
hosts:
- catalog
http:
- route:
- destination:
host: catalog
retries:
attempts: 3
retryOn: 5xx
perTryTimeout: 2s
EOF
$ kubectl get vs -n istioinaction
NAME GATEWAYS HOSTS AGE
catalog ["catalog"] 55s
webapp-virtualservice ["outfitters-gateway"] ["*"] 43m
결과적으로 client에 응답 성공률이 높아졌다!
그라파나(Istio Mesh Dashboard): retry 적용 후 Success Rate은 증가하고, 5xx 에러는 감소하는 것을 확인
2.4.3 트래픽 라우팅을 위한 이스티오
특정 사용자 그룹만 타겟으로 새 버전을 라우팅하고, 릴리즈에 단계적 접근: catalog v2에 imageUrl 핃드 추가
(x myk8s-control-plane)
# catalog v2 배포
$ cat <<EOF | kubectl -n istioinaction apply -f -
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
EOF
# (옵션) 500 에러 발생 꺼두기
$ docker exec -it myk8s-control-plane bash
----------------------------------------
$ cd /istiobook/bin/
$ ./chaos.sh 500 delete
catalog-6cf4b97d-qx8ln catalog-v2-6df885b555-kqgrx
Deleting 500 rule from catalog-6cf4b97d-qx8ln
blowups=[object Object]Deleting 500 rule from catalog-v2-6df885b555-kqgrx
blowups=[object Object]root@myk8s-control-plane:/istiobook/bin# exit
$ exit
----------------------------------------
#
$ kubectl get deploy,pod,svc,ep -n istioinaction
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/catalog 1/1 1 1 59m
deployment.apps/catalog-v2 1/1 1 1 2m42s
deployment.apps/webapp 1/1 1 1 59m
NAME READY STATUS RESTARTS AGE
pod/catalog-6cf4b97d-qx8ln 2/2 Running 0 59m
pod/catalog-v2-6df885b555-kqgrx 2/2 Running 0 2m42s
pod/webapp-7685bcb84-jkf25 2/2 Running 0 59m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/catalog ClusterIP 10.200.1.181 <none> 80/TCP 59m
service/webapp ClusterIP 10.200.1.190 <none> 80/TCP 59m
NAME ENDPOINTS AGE
endpoints/catalog 10.10.0.13:3000,10.10.0.16:3000 59m
endpoints/webapp 10.10.0.14:8080 59m
$ kubectl get gw,vs -n istioinaction
NAME AGE
gateway.networking.istio.io/outfitters-gateway 54m
NAME GATEWAYS HOSTS AGE
virtualservice.networking.istio.io/catalog ["catalog"] 12m
virtualservice.networking.istio.io/webapp-virtualservice ["outfitters-gateway"] ["*"] 54m
# 반복 접속 종료해두기
v1만 접속 설정
#
$ cat <<EOF | kubectl -n istioinaction apply -f -
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: catalog
spec:
host: catalog
subsets:
- name: version-v1
labels:
version: v1
- name: version-v2
labels:
version: v2
EOF
#
$ kubectl get gw,vs,dr -n istioinaction
NAME AGE
gateway.networking.istio.io/outfitters-gateway 60m
NAME GATEWAYS HOSTS AGE
virtualservice.networking.istio.io/catalog ["catalog"] 18m
virtualservice.networking.istio.io/webapp-virtualservice ["outfitters-gateway"] ["*"] 60m
NAME HOST AGE
destinationrule.networking.istio.io/catalog catalog 9s
# 반복 접속 : v1,v2 분산 접속 확인
while true; do curl -s http://127.0.0.1:30000/api/catalog | jq; date "+%Y-%m-%d %H:%M:%S" ; sleep 1; echo; done
# v1 라우팅 VS 수정(업데이트)
$ cat <<EOF | kubectl -n istioinaction apply -f -
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: catalog
spec:
hosts:
- catalog
http:
- route:
- destination:
host: catalog
subset: version-v1
EOF
# 반복 접속 : v1 접속 확인
while true; do curl -s http://127.0.0.1:30000/api/catalog | jq; date "+%Y-%m-%d %H:%M:%S" ; sleep 1; echo; done
헤더에 따라 라우팅 특정 헤더는 v2, 나머지는 v1 접속 설정
# 라우팅 VS 수정(업데이트)
cat <<EOF | kubectl -n istioinaction apply -f -
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: catalog
spec:
hosts:
- catalog
http:
- match:
- headers:
x-dark-launch:
exact: "v2"
route:
- destination:
host: catalog
subset: version-v2
- route:
- destination:
host: catalog
subset: version-v1
EOF
#
$ kubectl get gw,vs,dr -n istioinaction
NAME AGE
gateway.networking.istio.io/outfitters-gateway 62m
NAME GATEWAYS HOSTS AGE
virtualservice.networking.istio.io/catalog ["catalog"] 20m
virtualservice.networking.istio.io/webapp-virtualservice ["outfitters-gateway"] ["*"] 62m
NAME HOST AGE
destinationrule.networking.istio.io/catalog catalog 99s
# 반복 접속 : v1 접속 확인
$ while true; do curl -s http://127.0.0.1:30000/api/catalog | jq; date "+%Y-%m-%d %H:%M:%S" ; sleep 1; echo; 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"
}
]
# 반복 접속 : v2 접속 확인
$ while true; do curl -s http://127.0.0.1:30000/api/catalog -H "x-dark-launch: v2" | jq; date "+%Y-%m-%d %H:%M:%S" ; sleep 1; echo; done
[
{
"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"
}
]
실습 완료 후 자원 정리하기
kubectl delete deploy,svc,gw,vs,dr --all -n istioinaction && kind delete cluster --name myk8s
시간이 지난만큼 앞으로의 실습은 현시점(25.4.6 기준) 최신 버전으로 진행한다.
- 실습 환경: docker (kind - k8s 1.32.2), istio 1.25.1
- kind
#
kind create cluster --name myk8s --image kindest/node:v1.32.2 --config - <<EOF
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
extraPortMappings:
- containerPort: 30000 # Sample Application
hostPort: 30000
- containerPort: 30001 # Prometheus
hostPort: 30001
- containerPort: 30002 # Grafana
hostPort: 30002
- containerPort: 30003 # Kiali
hostPort: 30003
- containerPort: 30004 # Tracing
hostPort: 30004
- containerPort: 30005 # kube-ops-view
hostPort: 30005
networking:
podSubnet: 10.10.0.0/16
serviceSubnet: 10.200.1.0/24
EOF
# 설치 확인
docker ps
# 노드에 기본 툴 설치
docker exec -it myk8s-control-plane sh -c 'apt update && apt install tree psmisc lsof wget bridge-utils net-tools dnsutils tcpdump ngrep iputils-ping git vim -y'
# (옵션) kube-ops-view
helm repo add geek-cookbook https://geek-cookbook.github.io/charts/
helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --set service.main.type=NodePort,service.main.ports.http.nodePort=30005 --set env.TZ="Asia/Seoul" --namespace kube-system
kubectl get deploy,pod,svc,ep -n kube-system -l app.kubernetes.io/instance=kube-ops-view
## kube-ops-view 접속 URL 확인
open "http://localhost:30005/#scale=1.5"
open "http://localhost:30005/#scale=1.3"
# (옵션) metrics-server
helm repo add metrics-server https://kubernetes-sigs.github.io/metrics-server/
helm install metrics-server metrics-server/metrics-server --set 'args[0]=--kubelet-insecure-tls' -n kube-system
kubectl get all -n kube-system -l app.kubernetes.io/instance=metrics-server
export ISTIOV=1.25.1
curl -s -L https://istio.io/downloadIstio | ISTIO_VERSION=$ISTIOV sh -
cd istio-$ISTIOV
# default 프로파일 배포
cat <<EOF | istioctl install -y -f -
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
profile: demo
components:
ingressGateways:
- name: istio-ingressgateway
enabled: true
egressGateways:
- name: istio-egressgateway
enabled: false
EOF
- addon 설치
# 설치 확인 : istiod(데몬, 컨트롤플레인), istio-ingressgateway, crd 등
kubectl get all,svc,ep,sa,cm,secret,pdb -n istio-system
kubectl get crd | grep istio.io | sort
# istio-ingressgateway 서비스 NodePort 변경 및 nodeport 30000로 지정 변경
kubectl patch svc -n istio-system istio-ingressgateway -p '{"spec": {"type": "NodePort", "ports": [{"port": 80, "targetPort": 8080, "nodePort": 30000}]}}'
kubectl get svc -n istio-system istio-ingressgateway
# istio-ingressgateway 서비스 externalTrafficPolicy 설정 : ClientIP 수집 확인 용도
kubectl patch svc -n istio-system istio-ingressgateway -p '{"spec":{"externalTrafficPolicy": "Local"}}'
kubectl describe svc -n istio-system istio-ingressgateway
# default 네임스페이스에 istio-proxy sidecar 주입 설정 - Docs
kubectl label namespace default istio-injection=enabled
kubectl get ns --show-labels
# addon 설치
kubectl apply -f samples/addons
kubectl rollout status deployment/kiali -n istio-system
#
kubectl get pod,svc -n istio-system
# NodePort 변경 및 nodeport 30001~30003으로 변경 : prometheus(30001), grafana(30002), kiali(30003), tracing(30004)
kubectl patch svc -n istio-system prometheus -p '{"spec": {"type": "NodePort", "ports": [{"port": 9090, "targetPort": 9090, "nodePort": 30001}]}}'
kubectl patch svc -n istio-system grafana -p '{"spec": {"type": "NodePort", "ports": [{"port": 3000, "targetPort": 3000, "nodePort": 30002}]}}'
kubectl patch svc -n istio-system kiali -p '{"spec": {"type": "NodePort", "ports": [{"port": 20001, "targetPort": 20001, "nodePort": 30003}]}}'
kubectl patch svc -n istio-system tracing -p '{"spec": {"type": "NodePort", "ports": [{"port": 80, "targetPort": 16686, "nodePort": 30004}]}}'
# Prometheus 접속 : envoy, istio 메트릭 확인
open http://127.0.0.1:30001
# Grafana 접속
open http://127.0.0.1:30002
# Kiali 접속 1 : NodePort
open http://127.0.0.1:30003
# (옵션) Kiali 접속 2 : Port forward
kubectl port-forward deployment/kiali -n istio-system 20001:20001 &
open http://127.0.0.1:20001
# tracing 접속 : 예거 트레이싱 대시보드
open http://127.0.0.1:30004
- Bookinfo sample application 배포 - Docs
# Bookinfo Application 배포
kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml
# 확인 : 서비스 어카운트(sa)는 spiffe 에 svid 에 사용됨
kubectl get all,sa
# product 웹 접속 확인
kubectl exec "$(kubectl get pod -l app=ratings -o jsonpath='{.items[0].metadata.name}')" -c ratings -- curl -sS productpage:9080/productpage | grep -o "<title>.*</title>"
# productpage 파드 로그
kubectl logs -l app=productpage -c istio-proxy --tail=-1
kubectl logs -l app=productpage -c productpage -f
- Open the application to outside traffic
# Istio Gateway/VirtualService 설정
cat samples/bookinfo/networking/bookinfo-gateway.yaml
apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
name: bookinfo-gateway
spec:
selector:
istio: ingressgateway # use istio default controller
servers:
- port:
number: 8080
name: http
protocol: HTTP
hosts:
- "*"
---
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: bookinfo
spec:
hosts:
- "*"
gateways:
- bookinfo-gateway
http:
- match:
- uri:
exact: /productpage
- uri:
prefix: /static
- uri:
exact: /login
- uri:
exact: /logout
- uri:
prefix: /api/v1/products
route:
- destination:
host: productpage
port:
number: 9080
kubectl apply -f samples/bookinfo/networking/bookinfo-gateway.yaml
# Istio Gateway/VirtualService 설정 확인
kubectl get gw,vs
istioctl proxy-status
# productpage 파드의 istio-proxy 로그 확인 Access log 가 출력 - Default access log format : 링크
kubectl logs -l app=productpage -c istio-proxy -f
kubectl stern -l app=productpage
# productpage 웹 접속 : 새로고침
open http://127.0.0.1:30000/productpage
curl -v -s http://127.0.0.1:30000/productpage | grep -o "<title>.*</title>"
# 반복 접속
for i in {1..100}; do curl -s http://127.0.0.1:30000/productpage | grep -o "<title>.*</title>" ; done
while true; do curl -s http://127.0.0.1:30000/productpage | grep -o "<title>.*</title>" ; echo "--------------" ; sleep 0.5; done
새로 고침할 때마다 트래픽이 다른 버전으로 라우팅된다.
- v1
- v2
- v3
(트래픽 흐름을 istio를 통해 사각화하여 볼 수 있다.)
(istio에 Service Mesh를 시각화하여 한 눈에 확인할 수 있다.)
- istio-ingress gateway에 istio-proxy에도 로깅 변경 해보자.
kubectl exec -it deploy/istio-ingressgateway -n istio-system -- curl -X POST http://localhost:15000/logging
kubectl exec -it deploy/istio-ingressgateway -n istio-system -- curl -X POST http://localhost:15000/logging?http=debug
kubectl exec -it deploy/istio-ingressgateway -n istio-system -- curl -X POST http://localhost:15000/logging?http=info
- istio-proxy 파드에 envoy 컨테이너 admin 페이지 접속
# istio-proxy 파드에 envoy 컨테이너 admin 접속 포트 포워딩 설정
kubectl port-forward deployment/deploy-websrv 15000:15000 &
# envoy 컨테이너 admin 페이지 접속
open http://localhost:15000
도전해보자!
- 과제 1. Istio 관리(설정, 업그레이드 등)에 편리성을 제공하는 Sail Operator 설치 및 사용
- 과제 2. Istio IngressGW를 Gateway API(구현체는 무엇이든 상관X)를 통한 실습 환경 구성 및 사용 - Docs
- 과제 3. Istio Proxy를 K8S Native Sidecars로 구성 및 사용 - blog, k8s-docs, k8s-blog - 쿠버네티스 네이티브 사이드카 컨테이너(Sidecar Containers) - Youtube
도전과제까지 진행하기에는 너무나도 빠듯한 일정이다… 하지만 너무나도 차고 넘치도록 유익한 스터디인 것은 분명하다.
Let me know what you think of this article in the comment section below!