스터디/Istio

Istio 스터디 - 1주차 <Istio 실습 해보기>

황동리 2025. 4. 9. 15:40
반응형

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