쿠버네티스 서비스 메시 구축하기 - Istio 기초와 실전 예제

백종현·2024년 12월 2일
0

istio란?

Istio는 현대적인 마이크로서비스 아키텍처를 위한 강력한 오픈소스 서비스 메시 플랫폼입니다. 특히 쿠버네티스 환경에서 복잡한 서비스 간 통신을 효율적으로 관리하고 제어할 수 있게 해주는 핵심 인프라스트럭처 솔루션입니다.
서비스 메시로서 Istio가 공식문서에서 말하는 세 가지 핵심 가치는 다음과 같습니다:

가시성 (Increase observability)

  • 서비스 간 통신 흐름을 실시간으로 모니터링
  • 상세한 메트릭, 로그, 트레이스 정보 제공
  • 서비스 의존성과 성능 병목 지점을 쉽게 파악

트래픽 관리 (Manage traffic)

  • 서비스 간 로드밸런싱과 라우팅 제어
  • A/B 테스팅, 카나리 배포 지원
  • 서킷브레이커를 통한 장애 격리

보안 (Secure by default)

  • 서비스 간 mTLS 암호화 통신
  • 접근 제어 및 인증 관리
  • 세분화된 보안 정책 적용

가장 주목할 만한 점은 애플리케이션 코드를 전혀 수정하지 않고도 이 모든 기능을 적용할 수 있다는 것입니다. Istio는 사이드카 프록시 패턴을 통해 기존 애플리케이션에 투명하게 통합되며, 개발자들은 비즈니스 로직에만 집중할 수 있습니다.
특히 마이크로서비스 환경에서 발생하는 서비스 디스커버리, 로드밸런싱, 장애 처리, 메트릭 수집 등의 복잡한 문제들을 Istio가 인프라 수준에서 해결해주므로, 쿠버네티스 기반의 시스템을 더욱 안정적이고 관리하기 쉽게 만들어줍니다.

istio의 구성

istio의 구조를 이해하기 위해서는 먼저 핵심 구성요소인 Envoy에 대해 알아볼 필요가 있습니다.

Envoy 프록시 소개
Envoy는 애플리케이션 레이어(L7)에서 작동하는 오픈소스 로드밸런서입니다. 기본적으로 nginx와 같은 리버스 프록시 역할을 수행하며, 트래픽 경로를 제어할 수 있습니다. 하지만 Envoy는 여기서 한 걸음 더 나아가 다음과 같은 강력한 기능들을 제공합니다:

  • 자동 재시도 메커니즘
  • Circuit breaker를 통한 장애 격리
  • 외부 서비스를 통한 글로벌 속도 제한
  • 이상치 탐지 기능
  • 상세한 요청 지표 제공

이러한 Envoy의 강력한 기능들을 활용하기 위해 쿠버네티스의 각 파드에 배포하면 시스템 전반에 대한 상세한 지표를 얻을 수 있습니다. 하지만 여기서 한 가지 문제가 발생합니다. Envoy를 개별적으로 배포하는 것은 좋지만, 수천 개의 파드에 일일이 접속하여 Traffic Retry, Circuit Breaker, Timeout 등의 설정을 관리하는 것은 현실적으로 불가능합니다. 바로 이 지점에서 Istio의 필요성이 대두됩니다.

Istio의 구조
Istio는 이러한 Envoy 프록시들을 효율적으로 관리할 수 있는 시스템을 제공합니다. 구조는 크게 두 가지 계층으로 나뉩니다:

Data Plane: 각 파드에 sidecar 형식으로 배포된 Envoy 프록시들의 집합
Control Plane: 이러한 Envoy 프록시들을 중앙에서 관리하고 제어하는 시스템

아래 사진은 이러한 Istio의 전체적인 아키텍처를 보여줍니다:
Istio_DataPlane+ControlPlane_구성
이제 이어지는 내용에서는 실제로 쿠버네티스 클러스터에 Istio를 설치하고, 마이크로서비스를 배포한 뒤, 가시성과 트래픽 관리를 어떻게 구현하는지 실습을 통해 자세히 알아보도록 하겠습니다.

Istio 설치 및 예시 배포

참조사항

  • 사전준비사항 : 현재 동작하는 쿠버네티스 클러스터와 kubectl 사용이 가능해야합니다.
  • 이번 실습에서는 istioctl을 통하여 istio를 설치해보겠습니다.
  • https://istio.io/latest/docs/setup/getting-started/ 를 참조하여 작성하였습니다.

Istioctl 설치

  • Istioctl은 Istio 서비스 메시를 관리하기 위한 커맨드 라인 인터페이스(CLI) 도구입니다.
  • https://istio.io/latest/docs/releases/supported-releases/#support-status-of-istio-releases 페이지를 통하여 쿠버네티스 클러스터의 버전과 호환 가능한 istio 버전을 확인할 수 있습니다.
  • kubectl version을 사용하여, 버전을 확인 후, istioctl을 설치합니다. 1.30.6 버전이므로, ISTIO_VERSION 1.24.1 을 설치하겠습니다.
  • curl -L https://istio.io/downloadIstio | ISTIO_VERSION=1.24.1 TARGET_ARCH=x86_64 sh -
    명령어를 통하여 설치할 수 있습니다.
  • 이후 아래 명령어를 통해 istioctl을 사용할 수 있도록 환경변수를 등록합니다
cd istio-1.24.1
export PATH=$PWD/bin:$PATH

Istio 설치

  • 실제로 istioctl 명령어를 통해 istio를 설치해보겠습니다.
  • istioctl install -f samples/bookinfo/demo-profile-no-gateways.yaml -y
    명령어를 사용하여 설치합니다. (Istio Gateway 설치 X)
    • 이 포스트에서는 Istio Gateway가 아닌, Kubernetes Gateway API를 사용합니다.
      : Kubernetes Gateway API가 Istio의 최신 표준이 되어가고 있기 때문
      Istio가 성공적으로 설치된 사진
  • kubectl label namespace default istio-injection=enabled
    명령어를 사용하여 default namespace에 istio sidecar가 설치되도록 설정합니다.

Kubernetes Gateway API CRDs 설치

kubectl get crd [gateways.gateway.networking.k8s.io](http://gateways.gateway.networking.k8s.io/) &> /dev/null || \ { kubectl kustomize "[github.com/kubernetes-sigs/gateway-api/config/crd?ref=v1.2.0](http://github.com/kubernetes-sigs/gateway-api/config/crd?ref=v1.2.0)" | kubectl apply -f -; }
를 통하여 이후 사용할 Kubernetes Gateway API를 위해 CRD를 설치하도록 하겠습니다.

실제 sample application을 배포

https://istio.io/latest/docs/examples/bookinfo/

위 링크에서 제공하는 istio 예시 application을 배포하겠습니다. 아키텍처는 아래와 같습니다.
예시 application 아키텍처 사진

  • kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.24/samples/bookinfo/platform/kube/bookinfo.yaml
    명령어를 통하여 애플리케이션을 설치합니다.

  • 아래와 같이 결과가 나오면 정상입니다. kubectl get pods를 했을때 2개의 컨테이너가 ready 상태가 되어야합니다.

$ kubectl get services
NAME          TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
details       ClusterIP   10.0.0.212      <none>        9080/TCP   29s
kubernetes    ClusterIP   10.0.0.1        <none>        443/TCP    25m
productpage   ClusterIP   10.0.0.57       <none>        9080/TCP   28s
ratings       ClusterIP   10.0.0.33       <none>        9080/TCP   29s
reviews       ClusterIP   10.0.0.28       <none>        9080/TCP   29s

$ kubectl get pods
NAME                              READY   STATUS    RESTARTS   AGE
details-v1-558b8b4b76-2llld       2/2     Running   0          2m41s
productpage-v1-6987489c74-lpkgl   2/2     Running   0          2m40s
ratings-v1-7dc98c7588-vzftc       2/2     Running   0          2m41s
reviews-v1-7f99cc4496-gdxfn       2/2     Running   0          2m41s
reviews-v2-7d79d5bd5d-8zzqd       2/2     Running   0          2m41s
reviews-v3-7dbcdcbc56-m8dph       2/2     Running   0          2m41s
  • 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>"
    명령어를 통하여 실제로 요청이 정상적으로 성공하는지 확인합니다.

트래픽을 outside로 열기

  • kubectl annotate gateway bookinfo-gateway networking.istio.io/service-type=ClusterIP --namespace=default
    명령어를 통해 Kubernetes Gateway를 생성합니다
  • kubectl annotate gateway bookinfo-gateway networking.istio.io/service-type=ClusterIP --namespace=default
    명령어를 통해 ClusterIP로 설정합니다.
  • kubectl get gateway 명령어 입력시 아래와 같이 값이 떠야합니다.
$ kubectl get gateway
NAME               CLASS   ADDRESS                                            PROGRAMMED   AGE
bookinfo-gateway   istio   bookinfo-gateway-istio.default.svc.cluster.local   True         42s
  • kubectl port-forward svc/bookinfo-gateway-istio 8080:80
    명령어를 통해 로컬에서 접근할 수 있도록 열고, http://localhost:8080/productpage 에 들어가 정상적으로 작동하는지 확인합니다.
    productpage_접근_성공_사진

대시보드 설정 및 확인

  • kubectl apply -f samples/addons 명령어를 통해 Prometheus , Grafana , Jaeger 와 함께 Kiali 대시보드를 배포합니다.
  • istioctl dashboard kiali 명령어를 치면 아래와 같은 결과가 나오게 됩니다.
Failed to open browser; open http://localhost:20001/kiali in your browser.
  • 출력된 URL(http://localhost:20001/kiali)을 통해 브라우저에서 대시보드를 확인할 수 있습니다.
    kiali_Traffic_graph_사진_
  • for i in $(seq 1 1000); do curl -s -o /dev/null "http://127.0.0.1:8080/productpage"; done 와 같이 부하를 주게 되면, 요청이 증가한 Dashboard를 통해 확인할 수 있습니다.
    요청_증가된_dashboard_사진

Istio 서킷브레이커 시나리오

  • Istio에는 여러가지 기능이 있으나, 이번 포스트에서는 서킷브레이커 시나리오를 처리해보겠습니다.
  • product-v1에서 v2,v3를 제외한 reviews-v1에만 요청을 보내도록 설정하고, 새로운 reviews-v1 오류파드를 만들어 에러를 발생시키고, 서킷브레이커를 통해 이 파드에는 요청을 보내지 않도록 해보겠습니다.
  • 먼저 DestinationRule을 통해 "이 서비스에 대한 트래픽을 어떻게 처리할지"에 대한 상세 규칙을 정의합니다.
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: reviews-v1-circuit-breaker
spec:
  host: reviews       # 적용할 서비스
  subsets:           # 서비스의 하위 집합 정의
  - name: v1
    labels:          # 어떤 pod에 적용할지
      version: v1
    trafficPolicy:   # 트래픽 정책 설정
      outlierDetection:  # 서킷브레이커 설정
        consecutive5xxErrors: 10    # 연속 에러 횟수
        interval: 5s              # 체크 주기
        baseEjectionTime: 3m      # 차단 시간
        maxEjectionPercent: 100   # 최대 차단 비율
  • VirtualService를 사용하여 "트래픽을 어디로 보낼지"를 결정하는 라우팅 규칙을 정의합니다.
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: reviews
spec:
  hosts:
  - reviews     # 어떤 서비스에 대한 규칙인지
  http:
  - route:      # 라우팅 규칙
    - destination:
        host: reviews    # 목적지 서비스
        subset: v1      # DestinationRule에서 정의한 subset
  • 이후 명령어를 통해, 의도적으로 reviews-v1에 대해 실패하는 pod를 만듭니다.
apiVersion: apps/v1
kind: Deployment
metadata:
  name: reviews-v1-fail
spec:
  replicas: 1
  selector:
    matchLabels:
      app: reviews
      version: v1
  template:
    metadata:
      labels:
        app: reviews
        version: v1
    spec:
      containers:
      - name: reviews
        image: docker.io/istio/examples-bookinfo-reviews-v1:1.20.2
        command: ["/bin/sh"]
        args: ["-c", "sleep 50000; exit 1"]
        ports:
        - containerPort: 9080
  • 이후 요청을 하게 되면, 몇번의 아래와 같은 실패 이후, 서킷 브레이킹때문에 오류가 발생하지 않고, 기존의 성공하던 pod로만 요청이 가게 됩니다.
    실패하는_화면
    v1_fail_deploy에_요청이_가지_않는_모습

마무리

Istio는 이 외에도 트래픽 분할, 카나리 배포, 보안 정책 적용 등 더 많은 기능을 제공합니다.
이 포스트는 Istio의 방대한 기능 중 극히 일부분만을 다루었습니다. Istio는 깊이 있게 공부할 내용이 많은 서비스 메시 솔루션입니다. 트래픽 관리, 보안 정책, 관찰가능성(Observability) 등 각 영역별로 수많은 설정과 기능들이 있으며, 이를 실제 프로덕션 환경에 적용하기 위해서는 더 많은 학습과 경험이 필요합니다.

profile
노력하는 사람

0개의 댓글

관련 채용 정보