CloudNet@ 가시다님이 진행하는 Istio 스터디 1기 - 1주차 정리 내용 입니다.
2025.04.07 - [스터디/Istio] - Istio 알아보기 - 1주차 <서비스 메시와 Istio>
이전 글에 이어서 이번엔 Istio 실습 환경을 구성하고 실제로 어떻게 동작되는지 확인 해보겠습니다.
먼저 실습 환경을 구성해보도록 하겠습니다.
✅ 실습 환경 구성
WSL 환경에서 kind를 설치하여 k8s 배포를 해보도록 하겠습니다.
WSL 설치
📌 우선, 저는 Window 11 를 사용하고 있습니다.
WSL 설치
🛠️ 명령 프롬프트 창에서 진행
$ wsl --install
설치가 완료되면 wsl 명령어를 입력하여 접속해주면 됩니다.
WSL에 Docker 설치
아래 명령어를 실행하여 Docker를 설치해주면 됩니다.
🛠️ WSL2 에 Docker 설치 : 아래 스크립트 실행 후 20초 대기하면 스크립트 실행 됨
$ curl -fsSL https://get.docker.com -o get-docker.sh
$ sudo sh ./get-docker.sh
🛠️ 설치 확인
$ docker info
$ docker ps
$ sudo systemctl status docker
$ cat /etc/group | grep docker
WSL에 Kind 및 관리 툴 설치
아래 명령어를 실행하여 Kind 및 관리 툴을 설치해줍니다.
Kind(Kubernetes in Docker) 란 Docker 컨테이너 위에 가벼운 k8s 클러스터를 실행시켜주는 툴 입니다.
🛠️ 기본 사용자 디렉터리 이동
$ cd $PWD
$ pwd
🛠️ AppArmor 비활성화, AppArmor는 Ubuntu에 기본 탑재된 보안 모듈
🛠️ kind 실행 시 충돌 방지를 위해 비활성화
$ sudo systemctl stop apparmor && sudo systemctl disable apparmor
🛠️ 필수 패키지 설치
🛠️ bridge-utils : 브릿지 네트워크 설정 도구
🛠️ net-tools, ifconfig : 네트워크 유틸리티
🛠️ jq : json 데이트를 파싱할 수 있는 CLI 도구
🛠️ tree : 디렉터리 구조를 트리 형태로 보여주는 도구
🛠️ unzip : 압축 해제 도구
🛠️ kubectx : Kubernets 컨텍스트를 빠르게 전환할 수 있는 도구, 즉 네임스페이스 변경에 용이함
🛠️ kubecolor : kubectl 출력에 색상을 입혀 가독성 향상
$ sudo apt update && sudo apt-get install bridge-utils net-tools jq tree unzip kubectx kubecolor -y
🛠️ Kind 설치
$ curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.27.0/kind-linux-amd64
$ chmod +x ./kind
$ sudo mv ./kind /usr/local/bin/kind
$ kind --version
🛠️ Kubernetes 클러스터를 제어하는 kubectl CLI 설치
$ curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
$ chmod +x kubectl
$ sudo mv ./kubectl /usr/bin
$ sudo kubectl version --client=true
🛠️ Helm 설치
$ curl -s https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash
$ helm version
🛠️ kubectl 명령어 자동완성 기능 활성화
$ source <(kubectl completion bash)
$ echo 'source <(kubectl completion bash)' >> ~/.bashrc
🛠️ kubectl 명령을 간편하게 k 로 사용할 수 있도록 설정
$ echo 'alias k=kubectl' >> ~/.bashrc
$ echo 'complete -o default -F __start_kubectl k' >> ~/.bashrc
🛠️ kube-ps1 설치 및 ps1 설정
🛠️ kube-ps1은 프롬프트(PS1)에 현재 연결된 kubernetes 컨텍스트/네임스페이스 정보를 표시해주는 유틸 입니다.
$ git clone https://github.com/jonmosco/kube-ps1.git
$ echo -e "source $PWD/kube-ps1/kube-ps1.sh" >> ~/.bashrc
$ cat <<"EOT" >> ~/.bashrc
KUBE_PS1_SYMBOL_ENABLE=true
function get_cluster_short() {
echo "$1" | cut -d . -f1
}
KUBE_PS1_CLUSTER_FUNCTION=get_cluster_short
KUBE_PS1_SUFFIX=') '
PS1='$(kube_ps1)'$PS1
EOT
🛠️ .bashrc 적용을 위해서 logout 후 터미널 다시 접속
$ exit
Kind 설치 확인 및 클러스터 배포
이제 Kind가 정상적으로 설치가 되었는지 클러스터 배포를 통해서 확인해보도록 하겠습니다.
🛠️ 클러스터 배포 전 확인
$ docker ps
🛠️ 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
🛠️ 컨트롤플레인 (컨테이너) 노드 1대가 실행
$ docker ps
$ docker images
🛠️ kube config 파일 확인
$ cat ~/.kube/config
혹은
$ cat $KUBECONFIG # KUBECONFIG 변수 지정 사용 시
🛠️ nginx 파드 배포 및 확인 : 컨트롤플레인 노드인데 파드가 배포 될까요?
$ kubectl run nginx --image=nginx:alpine
$ kubectl get pod -owide
🛠️ 노드에 Taints 정보 확인
$ kubectl describe node | grep Taints
Taints: <none>
🛠️ 클러스터 삭제
$ kind delete cluster
🛠️ kube config 삭제 확인
$ cat ~/.kube/config
혹은
$ cat $KUBECONFIG # KUBECONFIG 변수 지정 사용 시
정상적으로 명령어들이 잘 실행이되고 클러스터 삭제까지 완료되었으면,
이제 Istio 실습을 할 k8s 클러스터를 구성하고 실습을 진행해보도록 하겠습니다.
Istio 실습
실습을 하기 위해 우선 k8s 클러스터를 배포해줍니다.
k8s 클러스터 배포
아래 코드를 사용하여 실습용 k8s 클러스터를 생성해줍니다.
🛠️ 실습 파일 다운로드 후 샘플 코드 경로로 이동
$ git clone https://github.com/AcornPublishing/istio-in-action
$ cd istio-in-action/book-source-code-master
🛠️ kind를 사용하여 각 서비스들의 호스트 포트를 매핑해준 k8s 클러스터 생성
$ kind create cluster --name myk8s --image kindest/node:v1.23.17 --config - <
노드에 진입 후 Istio 1.17.8 설치
🛠️ myk8s-control-plane 진입
$ docker exec -it myk8s-control-plane bash
⚠️ 이제부턴 myk8s-control-plane 노드에서 진행합니다.
🛠️ Istio 실습 파일들이 마운트 잘 되었는지 확인
# tree /istiobook/ -L 1

🛠️ 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
🛠️ istioctl 바이너리 파일 사용할 수 있도록 bin 폴더에 복사
# cp istio-$ISTIOV/bin/istioctl /usr/local/bin/istioctl
🛠️ 설치된 istio 버전 확인
# istioctl version --remote=false
🛠️ default 프로파일 컨트롤 플레인 배포
🛠️ 배포 전 k8s 사전 조건 충족 검사
# istioctl x precheck
🛠️ 프로파일 리스트 확인
# istioctl profile list
🛠️ default 프로파일로 설치
# istioctl install --set profile=default -y
🛠️ 설치 확인 : istiod, istio-ingressgateway, crd 등
# kubectl get all,svc,ep,sa,cm,secret,pdb -n istio-system
# kubectl get crd | grep istio.io | sort
# istioctl verify-install

🛠️ 보조 도구 설치 (Grafana, Kiali, Jaeger, Prometheus 등)
# kubectl apply -f istio-$ISTIOV/samples/addons
🛠️ 보조 도구가 정상적으로 생성이 되었는지 확인
# kubectl get pods -n istio-system

🛠️ 설치 확인이 완료되었으면 접속 했던 도커 컨테이너 노드에서 나옵니다.
# exit
서비스 메시에 첫 애플리케이션 배포
이제 k8s 클러스터에 istio 까지 설치가 완료되었으니,
실습에 사용될 애플리케이션을 배포 해보도록 하겠습니다.
⚠️ 이제부턴 WSL 환경에서 진행합니다.
🛠️ 네임스페이스 생성
# kubectl create namespace istioinaction
🛠️ Envoy 사이드카를 생성 시키는 방법들
🛠️ 방법 1. Yaml 파일을 적용할 때, istioctl kube-inject 명령어를 사용하여 Envoy 사이드카를 주입 하는 방법
# docker exec -it myk8s-control-plane istioctl kube-inject -f /istiobook/services/catalog/kubernetes/catalog.yaml
🛠️ 방법 2. 네임스페이스에 레이블을 추가하여 해당 네임스페이스에서 생기는 파드에 자동으로 Envoy 사이드카를 생성하도록 하는 방법
# kubectl label namespace istioinaction istio-injection=enabled
저는 네임스페이스에 라벨을 추가하는 방법을 사용하였습니다.
🛠️ 어플리케이션 생성
# kubectl apply -f istio-in-action/bookㅣㅣ
-source-code-master/services/catalog/kubernetes/catalog.yaml -n istioinaction
# kubectl apply -f istio-in-action/book-source-code-master/services/webapp/kubernetes/webapp.yaml -n istioinaction
🛠️ istioinaction 네임스페이스에 파드 생성 되었는지 확인
# kubectl get pods -n istioinaction
🛠️ 접속 테스트용 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
🛠️ webapp 접속 확인
# kubectl exec -it netshoot -- curl -s http://webapp.istioinaction/api/catalog/items/1 | jq
Istio 실습을 위한 AddOn 설치

위 아키텍처 형식으로 구성하여 실습해보도록 하겠습니다.
🛠️ Gateway, VirtualService 생성
# 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
🛠️ 생성한 gw, vs 확인
# kubectl get gw,vs -n istioinaction
🛠️ 변수 등록
# ISTIOIGW=<istion-gateway의 envoy 사이드카 프록시의 이름>
# WEBAPP=<webapp 파드의 envoy 사이드카 프록시의 이름>
🛠️ istioctl proxy-config 명령어들
아래의 각 명령어들은 Envoy 프록시 내부 설정을 확인하는 도구입니다. 디버깅할 때 굉장히 유용합니다.
🛠️ 기본 정보 전체
🛠️ 해당 프록시(Envoy)가 가지고 있는 모든 설정(listener, route, endpoint 등)을 요약해서 한눈에 보여줍니다.
# docker exec -it myk8s-control-plane istioctl proxy-config all $ISTIOIGW
# docker exec -it myk8s-control-plane istioctl proxy-config all $WEBAPP
🛠️ Listener 설정 확인 (수신 포트 관련)
🛠️ 프록시(Envoy)가 현재 수신하고 있는 포트, IP, 필터 체인 정보를 보여줍니다.
# docker exec -it myk8s-control-plane istioctl proxy-config listener $ISTIOIGW
# docker exec -it myk8s-control-plane istioctl proxy-config listener $WEBAPP
🛠️ 라우팅 테이블 확인 (URL 패턴 → 백엔드 서비스 매핑)
🛠️ 프록시(Envoy)가 보유하고 있는 라우팅 설정을 보여줍니다.
# docker exec -it myk8s-control-plane istioctl proxy-config route $ISTIOIGW
# docker exec -it myk8s-control-plane istioctl proxy-config route $WEBAPP
🛠️ 클러스터 정보 (백엔드 서비스 관련)
🛠️ 프록시(Envoy)가 백엔드와 통신하기 위해 관리하는 클러스터 정보를 보여줍니다.
# docker exec -it myk8s-control-plane istioctl proxy-config cluster $ISTIOIGW
# docker exec -it myk8s-control-plane istioctl proxy-config cluster $WEBAPP
🛠️ 엔드포인트 정보 (서비스의 실제 Pod IP 등)
🛠️ 각 클러스터가 실제로 연결하는 Pod IP 및 포트 정보(EDS)를 보여줍니다.
# docker exec -it myk8s-control-plane istioctl proxy-config endpoint $ISTIOIGW
# docker exec -it myk8s-control-plane istioctl proxy-config endpoint $WEBAPP
🛠️ Envoy의 로깅 레벨 확인
해당 프록시에서 사용 중인 로그 레벨(proxy log level)을 확인하거나 수정합니다.
# docker exec -it myk8s-control-plane istioctl proxy-config log $ISTIOIGW
# 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
🛠️ istio-ingressgateway 서비스의 외부 접근을 하기 위한 NodePort 변경
# kubectl patch svc -n istio-system istio-ingressgateway -p '{"spec": {"type": "NodePort", "ports": [{"port": 80, "targetPort": 8080, "nodePort": 30000}]}}'
🛠️ externalTrafficPolicy: Local로 설정하여, 요청한 클라이언트의 IP를 확인 할 수 있도록 설정
# kubectl patch svc -n istio-system istio-ingressgateway -p '{"spec":{"externalTrafficPolicy": "Local"}}'
🛠️ API 호출 테스트
# curl -s http://127.0.0.1:30000/api/catalog | jq
# curl -s http://127.0.0.1:30000/api/catalog/items/1 | jq
# curl -s http://127.0.0.1:30000/api/catalog -I | head -n 1
🛠️ 이제 Prometheus, grafana, kiali, jaeger를 NodePort Type으로 변경해줍니다.
# 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}]}}'
🛠️ 이제 api 반복 호출을 통하여 istio 에서 트래픽을 어떻게 관찰하고 관리하는지 보기 위한 반복 호출
# 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
🛠️ Kiali 대시보드에 접속해서 요청이 정상적으로 잘 되고 있는지 그래프로 확인
# open http://localhost:30003
Namespace 를 istioincation 로 선택 후 Graph (Traffic, Versioned app graph) 에서 Display 옵션 중 ‘Traffic Distribution’ 과 ‘Traffic Animation’ 활성화! , Service nods, Security 체크 해보자 (Last 1m, Evety 10s)

catalog 어플리케이션에 500에러 재현하고 retry로 복원력 높여보기

만약 '간헐적/일시적 네트워크 오류'가 발생하여 webapp -> catalog 로 요청이 실패하는 경우 발생 시, 애플리케이션 코드 수정 없이 복원력을 높여보겠습니다.
🛠️ k8s 노드 접속
# docker exec -it myk8s-control-plane bash
⚠️ 이제부턴 myk8s-control-plane 노드에서 진행합니다.
🛠️ istioinaction 으로 네임스페이스 변경
# kubectl config set-context $(kubectl config current-context) --namespace=istioinaction
🛠️ 오류 발생 스크립트 실행 (500 에러를 50프로 확률로 발생하도록 설정)
# cd /istiobook/bin
# chmod 755 chaos.sh
# ./chaos.sh 500 50
아래 이미지를 보면 에러가 발생한 것을 확인 할 수 있습니다.

이제 에러 발생 시, 애플리케이션 코드 수정 없이 reslience 하게 retry 하도록 해보겠습니다.
조건은 Proxy(Envoy)에 endpoint(catalog) 5xx 에러 시 retry 하도록 적용하겠습니다.
🛠️ 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: # retries 조건을 넣어줍니다.
attempts: 3
retryOn: 5xx
perTryTimeout: 2s
EOF
새로 정의한 VirtualService 의 내용을 확인해보면, catalog 로 가는 요청에 대한 설정 입니다.
따라서 이전에는 webapp 에서 catalog 로 요청 시 5xx 에러가 나오면 바로 통신 실패로 취급하였는데,
새로운 VritualService를 적용 시키면 5xx 에러가 나올 시, 3번 까지 retry를 시도하고 난 후에 통신 성공 or 실패 를 판단하기 때문에 오류률이 줄어들게 됩니다.
아래 이미지를 보면 실제로 오류률이 줄어든 것을 확인 할 수 있습니다.

라우팅 경로 변경
Istio를 사용하면 라우팅 경로도 손쉽게 변경 할 수 있습니다.
이번에 해볼 시나리오는 특정 사용자 집단만 새 배포로 라우팅 되도록 해보겠습니다.
🛠️ 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 에러 발생 끄기
# cd /istiobook/bin/
# ./chaos.sh 500 delete
이제 DestinationRule을 적용해보도록 하겠습니다.
🛠️ DestinationRule 적용
# 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
🛠️ DestinationRule 적용 후 다시 반복 접속 실행
# 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
적용 시킨 결과를 보면, 기본적으로 VirtualService에 별도의 subnet-based 적용 규칙이 없으면 round-robin 방식으로 동작을 합니다.

이제 VirtualService에 규칙을 추가해보도록 하겠습니다.
🛠️ VirtualService 수정
# 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
🛠️ VirtualService 수정 후 다시 반복 접속 실행
# 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
VirtualService 의 route를 수정하고 그래프를 보면 아래의 이미지와 같이, v1으로만 트래픽이 가는 것을 확인 할 수 있습니다.

이제 특정 헤더는 v2, 그 외에는 v1 접속을 하도록 설정 해보도록 하겠습니다.
🛠️ VirtualService 수정
# 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
위와 같이 VirtualService를 수정하면 사용자의 요청 헤더에 x-dark-launch: v2 가 존재할 시,
catalog-version-v2 로 요청이 가고 그 외 나머지 요청은 catalog-version-1 으로 가도록 설정했습니다.
VirtualService 수정을 완료하고 사용자 요청을 해보도록 하겠습니다.
🛠️ version-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
아래 이미지를 보면 이전과 다르게 v2로만 트래픽이 가는 것을 확인 할 수 있습니다.

1주차 Istio 스터디 내용은 여기까지 입니다.
다음 글에서 뵙겠습니다.
'스터디 > Istio' 카테고리의 다른 글
Istio 스터디 2주차 - <Envoy 란?> (0) | 2025.04.16 |
---|---|
Istio 스터디 - 1주차 <서비스 메시와 Istio> (0) | 2025.04.07 |