Istio 멀티클러스터를 사용하여 클러스터 간에 워크로드를 '확장'

1. 환영합니다

Google의 Istio 멀티 클라우드 버스트 Codelab에 참여해 주셔서 감사합니다.이 Codelab에서는 Kubernetes, Node, Go에 대한 초급 수준의 실무 경험이 필요합니다.

필요한 항목

  • Google Cloud Platform 계정 (기존 계정 사용 또는 무료 계정 제공)
  • 노트북('kubectl', 'gcloud' 등 설치) 또는 Google Cloud Shell을 사용할 수 있습니다.

학습할 내용

  • GKE에서 Kubernetes 클러스터를 만드는 방법
  • Helm을 사용하여 Kubernetes 클러스터에 Istio를 설치하는 방법
  • Helm으로 Istio 멀티 클러스터를 설치하는 방법
  • 소스에서 Kubernetes로 웹 애플리케이션 배포
  • Istio에 트래픽 라우팅 규칙 작성 및 적용
  • Prometheus 측정항목
  • Kubernetes 클러스터 내에서 컨테이너 이미지 빌드 및 푸시

2. 설정

다음 중 하나에서 이 Codelab을 따라 할 수 있습니다.

  • Google Cloud Shell(권장): 브라우저 내 셸, 도구가 설치되어 있음
  • 노트북 (아래 안내를 따르세요)

Google Cloud Platform 시작하기

  1. GCP 계정이 없는 경우 강사에게 무료 사용자 계정 카드를 받으세요.
  2. Google Cloud 콘솔로 이동하여 '프로젝트 선택'을 클릭합니다. 5c2d9bf74c78f7e4.png
  3. 프로젝트의 'ID'를 어딘가에 기록한 다음 프로젝트를 클릭하여 선택합니다. ecc5e8e97bfa6559.png

Cloud Shell은 필요한 도구가 설치되어 있고 Google Cloud Platform 계정으로 자동 인증된 브라우저 내 명령줄 셸을 제공합니다. (Cloud Shell에서 이 실습을 실행하지 않으려면 다음 섹션으로 건너뛰세요.)

Cloud 콘솔로 이동하여 오른쪽 상단 툴바에서 'Cloud Shell 활성화'를 클릭합니다.

68a17b036ce24ccb.png

Cloud Shell에 도구 추가

  1. kubectx****를 설치합니다. 여기에서 bash 스크립트를 다운로드하여 $PATH의 위치에 저장합니다.
  2. helm 설치****: 이 안내를 따릅니다.

또는 다음 명령어를 실행하여 ~/.bin에 모두 설치하고 $PATH에 추가합니다.

mkdir -p ~/.bin && \
cd ~/.bin && \
curl -LO https://raw.githubusercontent.com/ahmetb/kubectx/master/kubectx && \
chmod +x kubectx && \
curl -LO https://raw.githubusercontent.com/ahmetb/kubectx/master/kubens && \
chmod +x kubens && \
curl -LO  https://storage.googleapis.com/kubernetes-helm/helm-v2.12.0-linux-amd64.tar.gz && \
tar xzf helm-v2.12.0-linux-amd64.tar.gz && \
rm helm-v2.12.0-linux-amd64.tar.gz && \
mv linux-amd64/helm ./helm && \
rm -r linux-amd64 && \
export PATH=${HOME}/.bin:${PATH}

Cloud Shell을 더 쉽게 사용할 수 있는 몇 가지 간단한 팁은 다음과 같습니다.

1. 셸을 새 창으로 분리합니다.

2. 파일 편집기 사용: 오른쪽 상단의 연필 아이콘을 클릭하여 브라우저 내 파일 편집기를 실행합니다. 코드 스니펫을 파일에 복사할 때 유용합니다.

3. 새 탭 시작: 터미널 프롬프트가 두 개 이상 필요한 경우

4. 텍스트를 크게 만드세요. Cloud Shell의 기본 글꼴 크기가 너무 작아서 읽기 어려울 수 있습니다.

Linux/Windows에서는 Ctrl-+를, macOS에서는 ⌘-+를 누릅니다.

Cloud Shell보다 자체 워크스테이션 환경을 사용하는 것이 더 편하다면 다음 도구를 설정하세요.

  1. gcloud: 설치 (Cloud Shell에 사전 설치됨) 안내에 따라 플랫폼에 gcloud를 설치합니다. 이를 사용하여 Kubernetes 클러스터를 만듭니다.
  2. kubectl: 설치(Cloud Shell에 사전 설치됨) 다음 명령어를 실행하여 설치합니다.
gcloud components install kubectl

다음 명령어를 실행하여 gcloud를 인증합니다. Google 계정으로 로그인하라는 메시지가 표시됩니다. 그런 다음 미리 만든 프로젝트 (위 참고)를 기본 프로젝트로 선택합니다. 컴퓨팅 영역 구성은 건너뛸 수 있습니다.

gcloud init
  1. curl: 설치: 대부분의 Linux/macOS 시스템에 사전 설치되어 있습니다. 이미 가지고 있을 가능성이 높습니다. 그렇지 않은 경우 인터넷에서 설치 방법을 검색하세요.
  2. kubectx**** 설치: 여기에서 bash 스크립트를 $PATH의 위치로 다운로드합니다.
  3. helm 설치****: 이 안내를 따릅니다.

3. GCP 프로젝트 설정

프로젝트에서 GKE (Google Kubernetes Engine), GCR (Google Container Registry), GCB (Google Cloud Build) API를 사용 설정합니다.

gcloud services enable \
  cloudapis.googleapis.com \
  container.googleapis.com \
  containerregistry.googleapis.com \
  cloudbuild.googleapis.com

환경 변수 설정

설정 중에 Google Cloud 프로젝트를 광범위하게 사용하므로 빠른 참조를 위해 환경 변수를 설정해 보겠습니다.

export GCLOUD_PROJECT=$(gcloud config get-value project)

이 워크숍에서는 코드와 구성 파일을 만들 예정이므로 프로젝트 디렉터리를 만들고 해당 디렉터리로 변경하겠습니다.

mkdir -p src/istio-burst && \
cd src/istio-burst && \
export proj=$(pwd)

4. '기본' Kubernetes 클러스터 만들기

Google Kubernetes Engine (GKE)을 사용하면 관리형 Kubernetes 클러스터를 쉽게 만들 수 있습니다.

다음 명령어를 실행하면 Kubernetes 클러스터가 생성됩니다.

  • 'primary'라는 이름이 지정된
  • us-west1-a 영역에서
  • 사용 가능한 최신 버전의 Kubernetes
  • 초기 노드 4개
export cluster=primary
export zone=us-west1-a
gcloud container clusters create $cluster --zone $zone --username "admin" \
--cluster-version latest --machine-type "n1-standard-2" \
--image-type "COS" --disk-size "100" \
--scopes "https://www.googleapis.com/auth/compute",\
"https://www.googleapis.com/auth/devstorage.read_only",\
"https://www.googleapis.com/auth/logging.write",\
"https://www.googleapis.com/auth/monitoring",\
"https://www.googleapis.com/auth/servicecontrol",\
"https://www.googleapis.com/auth/service.management.readonly",\
"https://www.googleapis.com/auth/trace.append" \
--num-nodes "4" --network "default" \
--enable-cloud-logging --enable-cloud-monitoring --enable-ip-alias

(5분 정도 걸릴 수 있습니다. Cloud 콘솔에서 클러스터가 생성되는 것을 확인할 수 있습니다.)

Kubernetes 클러스터가 생성되면 gcloud에서 클러스터를 가리키는 사용자 인증 정보로 kubectl를 구성합니다.

gcloud container clusters get-credentials $cluster --zone=$zone

이제 새 클러스터에서 kubectl를 사용할 수 있습니다.

다음 명령어를 실행하여 클러스터의 Kubernetes 노드를 나열합니다. 상태가 'Ready'로 표시되어야 합니다.

kubectl get nodes

사용 편의성을 위해 Kubeconfig 이름 수정

컨텍스트를 자주 전환하므로 클러스터의 짧은 별칭이 있으면 편리합니다.

이 명령어는 방금 만든 kubeconfig 항목의 이름을 primary로 바꿉니다.

kubectx ${cluster}=gke_${GCLOUD_PROJECT}_${zone}_${cluster}

권한 설정:

Istio를 배포하려면 클러스터 관리자여야 합니다. 이 명령어는 Google Cloud 계정과 연결된 이메일을 클러스터 관리자로 설정합니다.

kubectl create clusterrolebinding cluster-admin-binding \
    --clusterrole=cluster-admin \
    --user=$(gcloud config get-value core/account)

5. '버스트' 클러스터 만들기

다음 명령어를 실행하면 Kubernetes 클러스터가 생성됩니다.

  • 'burst'라는 이름의
  • us-west1-a 영역에서
  • 사용 가능한 최신 버전의 Kubernetes
  • 초기 노드 1개
  • 최대 5개 노드까지 자동 확장 사용 설정
export cluster=burst
export zone=us-west1-a
gcloud container clusters create $cluster --zone $zone --username "admin" \
--cluster-version latest --machine-type "n1-standard-2" \
--image-type "COS" --disk-size "100" \
--scopes "https://www.googleapis.com/auth/compute",\
"https://www.googleapis.com/auth/devstorage.read_only",\
"https://www.googleapis.com/auth/logging.write",\
"https://www.googleapis.com/auth/monitoring",\
"https://www.googleapis.com/auth/servicecontrol",\
"https://www.googleapis.com/auth/service.management.readonly",\
"https://www.googleapis.com/auth/trace.append" \
--num-nodes "1" --enable-autoscaling --min-nodes=1 --max-nodes=5 \
--network "default" \
--enable-cloud-logging --enable-cloud-monitoring --enable-ip-alias

(5분 정도 걸릴 수 있습니다. Cloud 콘솔에서 클러스터가 생성되는 것을 확인할 수 있습니다.)

Kubernetes 클러스터가 생성되면 gcloud에서 클러스터를 가리키는 사용자 인증 정보로 kubectl를 구성합니다.

gcloud container clusters get-credentials $cluster --zone=$zone

이제 새 클러스터에서 kubectl를 사용할 수 있습니다.

다음 명령어를 실행하여 클러스터의 Kubernetes 노드를 나열합니다. 상태가 'Ready'로 표시되어야 합니다.

kubectl get nodes

사용 편의성을 위해 Kubeconfig 이름 수정

이 명령어는 방금 만든 kubeconfig 항목을 burst로 수정합니다.

kubectx ${cluster}=gke_${GCLOUD_PROJECT}_${zone}_${cluster}

권한 설정:

Istio Remote를 배포하려면 클러스터 관리자여야 합니다. 이 명령어는 Google Cloud 계정과 연결된 이메일을 클러스터 관리자로 설정합니다.

kubectl create clusterrolebinding cluster-admin-binding \
    --clusterrole=cluster-admin \
    --user=$(gcloud config get-value core/account)

6. 방화벽 규칙 적용

두 클러스터가 서로 통신하려면 방화벽 규칙을 만들어야 합니다.

다음 명령어를 실행하여 클러스터가 통신할 수 있도록 Google Cloud Platform에 방화벽 규칙을 만듭니다.

function join_by { local IFS="$1"; shift; echo "$*"; }
ALL_CLUSTER_CIDRS=$(gcloud container clusters list \
--filter="(name=burst OR name=primary) AND zone=$zone" \
--format='value(clusterIpv4Cidr)' | sort | uniq)
ALL_CLUSTER_CIDRS=$(join_by , $(echo "${ALL_CLUSTER_CIDRS}"))
ALL_CLUSTER_NETTAGS=$(gcloud compute instances list \
--filter="(metadata.cluster-name=burst OR metadata.cluster-name=primary) AND metadata.cluster-location=us-west1-a" \
--format='value(tags.items.[0])' | sort | uniq)
ALL_CLUSTER_NETTAGS=$(join_by , $(echo "${ALL_CLUSTER_NETTAGS}"))
gcloud compute firewall-rules create istio-multicluster-test-pods \
  --allow=tcp,udp,icmp,esp,ah,sctp \
  --direction=INGRESS \
  --priority=900 \
  --source-ranges="${ALL_CLUSTER_CIDRS}" \
  --target-tags="${ALL_CLUSTER_NETTAGS}" --quiet

이제 두 클러스터가 모두 설정되어 애플리케이션과 Istio를 배포할 준비가 되었습니다.

7. Istio 소개

Istio란 무엇인가요?

Istio는 '서비스를 연결, 보호, 제어, 관찰'하는 것을 목표로 하는 서비스 메시 제어 영역입니다. 다양한 방식으로 이 작업을 수행하지만 주로 배포된 각 Kubernetes 포드에 프록시 컨테이너 ( Envoy)를 사이드카로 추가합니다. 프록시 컨테이너는 범용 정책 및 원격 분석 허브 ( Mixer)와 함께 마이크로서비스 간의 모든 네트워크 통신을 제어합니다.

a25613cd581825da.png

이러한 정책은 Kubernetes 배포 및 서비스와 독립적으로 적용할 수 있습니다. 즉, 네트워크 운영자는 연결된 애플리케이션을 재배포하지 않고도 네트워크 활동을 관찰하고 네트워크 정책을 제한, 리디렉션 또는 재작성할 수 있습니다.

Istio에서 지원하는 트래픽 관리 기능은 다음과 같습니다.

  • 회로 차단기
  • 비율 기반 트래픽 분할
  • URL 재작성
  • TLS 종료
  • 상태 점검
  • 부하 분산

이 워크숍에서는 비율 기반 트래픽 분할에 중점을 둡니다.

Istio 용어

VirtualService

VirtualService는 호스트가 처리될 때 적용할 트래픽 라우팅 규칙 집합을 정의합니다.

게이트웨이

게이트웨이는 메시의 에지에서 작동하는 부하 분산기로, 수신 또는 발신되는 HTTP/TCP 연결을 수신합니다. 게이트웨이는 포트, SNI 구성 등을 지정할 수 있습니다.

DestinationRule

DestinationRule은 라우팅이 발생한 서비스로 향하는 트래픽에 적용되는 정책을 정의합니다. 부하 분산, 사이드카의 연결 풀 크기, 이상치 감지 설정의 구성을 지정합니다.

Istio 멀티 클러스터

두 클러스터를 만들 때 primary 클러스터는 자동 확장 없이 4개 노드였고 burst 클러스터는 최대 5개 노드로 자동 확장되는 1개 노드였음을 알 수 있습니다.

이 구성에는 두 가지 이유가 있습니다.

먼저 '온프레미스'에서 클라우드로의 시나리오를 시뮬레이션하려고 합니다. 온프레미스 환경에서는 인프라가 고정되어 있으므로 자동 확장 클러스터에 액세스할 수 없습니다.

둘째, Istio를 실행하려면 위에서 정의된 4개 노드 설정이 최소 요구사항입니다. 여기서 의문이 생깁니다. Istio에 최소 4개의 노드가 필요한데 burst 클러스터가 노드 1개로 Istio를 실행할 수 있는 이유는 무엇일까요? Istio 멀티 클러스터는 훨씬 작은 Istio 서비스 집합을 설치하고 기본 클러스터의 Istio 설치와 통신하여 정책 규칙을 가져오고 텔레메트리 정보를 게시합니다.

8. 애플리케이션 아키텍처 개요

구성요소 개요

NodeJSRedis를 사용하여 3계층 애플리케이션을 배포합니다.

작업자

작업자 애플리케이션은 NodeJS로 작성되며 수신되는 POST HTTP 요청을 리슨하고, 요청에 해싱 작업을 실행하며, PREFIX라는 환경 변수가 정의된 경우 해시에 해당 값을 추가합니다. 해시가 계산되면 애플리케이션은 지정된 Redis 서버의 'calculation' 채널에 결과를 전송합니다.

나중에 PREFIX 환경 변수를 사용하여 멀티 클러스터 기능을 시연할 것입니다.

참고: 애플리케이션에서 사용하는 패키지입니다.

  • body-parser: HTTP 요청을 파싱할 수 있습니다.
  • cors: 교차 출처 리소스 공유 사용을 허용합니다.
  • dotenv: 환경 변수를 쉽게 파싱
  • express: 간편한 웹사이트 호스팅
  • ioredis: Redis 데이터베이스와 통신하는 클라이언트 라이브러리
  • morgan: 멋진 구조화된 로그를 제공합니다.

프런트엔드

프런트엔드는 express를 사용하여 웹페이지를 호스팅하는 NodeJS 애플리케이션이기도 합니다. 사용자가 입력한 빈도를 가져와 해당 비율로 worker 애플리케이션에 요청을 보냅니다. 이 애플리케이션은 'calculation'라는 Redis 채널의 메시지도 구독하고 결과를 웹페이지에 표시합니다.

애플리케이션은 다음 종속 항목을 사용합니다.

  • body-parser: HTTP 요청을 파싱할 수 있습니다.
  • dotenv: 환경 변수를 쉽게 파싱
  • express: 간편한 웹사이트 호스팅
  • ioredis: Redis 데이터베이스와 통신하는 클라이언트 라이브러리
  • morgan: 멋진 구조화된 로그를 제공합니다.
  • request: HTTP 요청을 할 수 있습니다.
  • socket.io: 웹페이지에서 서버로의 양방향 통신을 허용합니다.

이 웹페이지는 스타일 지정에 Bootstrap을 사용하며 실행하면 다음과 같이 표시됩니다.

e5e3b9cbede4cac4.png

아키텍처 다이어그램

7ae4bc22a58f80a6.png

배포 다이어그램

생성한 두 클러스터에 최종 애플리케이션을 배포할 것입니다. primary 클러스터에는 모든 구성요소 (frontend, worker, Redis)가 배포되지만 burst 클러스터에는 worker 애플리케이션만 배포됩니다.

다음은 두 클러스터를 설명하는 다이어그램입니다. 빨간색으로 표시된 상자는 Kubernetes 서비스이고 파란색으로 표시된 상자는 Kubernetes 배포입니다. 노란색 상자는 Istio 설치를 나타냅니다.

561db37c510944bd.png

클러스터에 Redis 배포가 없는데도 burst 클러스터에 Redis 서비스가 배포되어 있습니다. Kubernetes DNS가 요청을 해결할 수 있도록 클러스터에 이 서비스가 있어야 하지만 요청이 실제로 이루어지면 Istio 프록시가 primary 클러스터의 Redis 배포로 요청을 다시 라우팅합니다.

최종 애플리케이션에는 primary 클러스터에서 실행되는 추가 배포가 istiowatcher.라는 이름으로 지정됩니다. 이를 통해 트래픽이 특정 기준점을 초과할 때 트래픽을 burst로 자동으로 동적 리라우팅할 수 있습니다.

8f6183bdfc3f813c.png

9. 애플리케이션 배포 파일 만들기

애플리케이션을 배포하기 위해 Kubernetes 매니페스트를 만들어야 합니다.

프로젝트의 루트 디렉터리로 변경하고 kubernetes라는 새 폴더를 만듭니다.

mkdir ${proj}/kubernetes && cd ${proj}/kubernetes

frontend.yaml 작성

이렇게 하면 프런트엔드 이미지에 액세스하기 위한 Kubernetes 배포와 서비스가 모두 생성됩니다.

frontend.yaml에 다음을 삽입합니다.

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: frontend-deployment
  labels:
    app: frontend
spec:
  replicas: 1
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
    spec:
      containers:
      - name: frontend
        image: gcr.io/istio-burst-workshop/frontend
        ports:
        - containerPort: 8080
        readinessProbe:
            initialDelaySeconds: 10
            httpGet:
              path: "/_healthz"
              port: 8080
              httpHeaders:
              - name: "Cookie"
                value: "istio_session-id=x-readiness-probe"
        livenessProbe:
          initialDelaySeconds: 10
          httpGet:
            path: "/"
            port: 8080
            httpHeaders:
            - name: "Cookie"
              value: "istio_session-id=x-liveness-probe"
        env:
        - name: PORT
          value: "8080"
        - name: PROCESSOR_URL
          value: "http://worker-service"
        - name: REDIS_URL
          value: "redis-cache-service:6379"
---
apiVersion: v1
kind: Service
metadata:
  name: frontend-service
spec:
  type: ClusterIP
  selector:
    app: frontend
  ports:
  - name: http
    port: 80
    targetPort: 8080

Deployment에서 주목해야 할 주요 사항

  • 애플리케이션이 실행될 포트를 8080로 지정했습니다.
  • 작업자의 주소를 'http://worker-service'로 설정했으며 Kubernetes 기본 제공 DNS 기능을 사용하여 결과 서비스를 확인합니다.
  • REDIS_URL의 주소를 'redis-cache-service:6379'로 설정했으며 Kubernetes 기본 제공 DNS 기능을 사용하여 결과 IP 주소를 확인합니다.
  • 또한 컨테이너가 실행 중일 때 Kubernetes에 알리도록 컨테이너에 livenessreadiness 프로브를 설정했습니다.

worker-service.yaml 작성

여러 클러스터에서 이 서비스를 재사용하지만 각 클러스터에 대해 다른 배포를 작성할 것이므로 배포 정의와는 별도의 파일에 Kubernetes 서비스 정의를 작성합니다.

worker-service.yaml에 다음을 삽입합니다.

apiVersion: v1
kind: Service
metadata:
 name: worker-service
spec:
 type: ClusterIP
 selector:
   app: worker
 ports:
 - name: http
   port: 80
   targetPort: 8081

worker-primary.yaml 작성

기본 클러스터에 푸시할 worker 배포입니다.

worker-primary.yaml에 다음을 삽입합니다.

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
 name: worker-deployment
 labels:
   app: worker
spec:
 replicas: 1
 selector:
   matchLabels:
     app: worker
 template:
   metadata:
     labels:
       app: worker
       cluster-type: primary-cluster
   spec:
     containers:
     - name: worker
       image: gcr.io/istio-burst-workshop/worker
       imagePullPolicy: Always
       ports:
       - containerPort: 8081
       readinessProbe:
           initialDelaySeconds: 10
           httpGet:
             path: "/_healthz"
             port: 8081
             httpHeaders:
             - name: "Cookie"
               value: "istio_session-id=x-readiness-probe"
       livenessProbe:
         initialDelaySeconds: 10
         httpGet:
           path: "/"
           port: 8081
           httpHeaders:
           - name: "Cookie"
             value: "istio_session-id=x-liveness-probe"
       env:
       - name: PORT
         value: "8081"
       - name: REDIS_URL
         value: "redis-cache-service:6379"

여기서 livenessreadiness 프로브를 제공하고 애플리케이션에서 사용할 PORTREDIS_URL 환경 변수를 지정하는 동일한 패턴을 따르고 있습니다.

이 배포에서 또 한 가지 주목해야 할 점은 PREFIX 환경 변수가 없다는 것입니다. 즉, 계산 결과는 원시 해시 (접두사가 없음)가 됩니다.

이 배포의 마지막 핵심 사항은 cluster-type: primary-cluster 라벨입니다. 이는 나중에 Istio 멀티 클러스터에서 트래픽 라우팅을 할 때 사용됩니다.

redis.yaml 작성

작업자에서 프런트엔드로의 통신은 Redis 채널을 통해 이루어지므로 Redis 애플리케이션을 클러스터에 배포해야 합니다.

다음을 redis.yaml에 삽입합니다.

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
 name: redis-cache
spec:
 template:
   metadata:
     labels:
       app: redis-cache
   spec:
     containers:
     - name: redis
       image: redis:alpine
       ports:
       - containerPort: 6379
       readinessProbe:
         periodSeconds: 5
         tcpSocket:
           port: 6379
       livenessProbe:
         periodSeconds: 5
         tcpSocket:
           port: 6379
       volumeMounts:
       - mountPath: /data
         name: redis-data
       resources:
         limits:
           memory: 256Mi
           cpu: 125m
         requests:
           cpu: 70m
           memory: 200Mi
     volumes:
     - name: redis-data
       emptyDir: {}

Redis 애플리케이션의 준표준 배포입니다. redis:alpine 이미지를 기반으로 컨테이너를 설정하고, 적절한 포트를 노출하고, 적절한 리소스 한도를 설정합니다.

redis-service.yaml 작성

Redis 애플리케이션과 통신하려면 Kubernetes 서비스가 필요합니다.

다음을 redis-service.yaml에 삽입합니다.

apiVersion: v1
kind: Service
metadata:
 name: redis-cache-service
spec:
 type: ClusterIP
 selector:
   app: redis-cache
 ports:
 - port: 6379
   targetPort: 6379

이를 통해 Redis 배포에 액세스하는 redis-cache-service라는 서비스가 제공됩니다.

10. 애플리케이션 배포

이미지를 GCR에 푸시하고 Kubernetes 매니페스트를 작성했으므로 지금 애플리케이션을 배포하고 작동 방식을 확인하는 것이 좋습니다.

다음 명령어를 실행하여 애플리케이션을 배포합니다.

  1. 올바른 클러스터에 있는지 확인
kubectx primary
  1. Redis 캐시 배포
kubectl apply -f redis.yaml
  1. Redis 서비스 배포
kubectl apply -f redis-service.yaml
  1. 프런트엔드 배포
kubectl apply -f frontend.yaml
  1. 작업자 배포
kubectl apply -f worker-primary.yaml
  1. 작업자 서비스 배포
kubectl apply -f worker-service.yaml

애플리케이션이 GKE에 배포되었습니다. 축하합니다.

테스트

포드가 온라인 상태가 될 때까지 기다립니다.

kubectl get pods -w

모든 포드가 'Running' 상태가 되면 Ctrl + C를 누릅니다.

NAME                                   READY     STATUS    RESTARTS   AGE
frontend-deployment-695d95fbf7-76sd8   1/1       Running   0          2m
redis-cache-7475999bf5-nxj8x           1/1       Running   0          2m
worker-deployment-5b9cf9956d-g975p     1/1       Running   0          2m

LoadBalancer를 통해 프런트엔드를 노출하지 않았습니다. 나중에 Istio를 통해 애플리케이션에 액세스할 예정이기 때문입니다. 모든 항목이 작동하는지 테스트하려면 kubectl port-forward.를 사용합니다. 다음 명령어를 실행하여 로컬 (또는 Cloud Shell) 머신의 포트 8080을 frontend 배포를 실행하는 포트 8080으로 전달합니다.

kubectl port-forward \
$(kubectl get pods -l app=frontend -o jsonpath='{.items[0].metadata.name}') \
8080:8080

로컬에서 실행하는 경우: 웹브라우저를 열고 http://localhost:8080으로 이동합니다.

Cloud Shell에서 실행하는 경우: '웹 미리보기' 버튼을 클릭하고 '포트 8080에서 미리보기'를 선택합니다.

bdb5dc75f415be11.png

프런트엔드가 표시됩니다. '빈도' 상자에 숫자를 입력하면 해시가 표시됩니다.

1caafaffab26897a.png

축하합니다. 모든 것이 작동합니다.

Ctrl+C를 눌러 포트 전달을 중지합니다.

11. 배포된 애플리케이션 정리

클러스터에 Istio를 적용한 후 애플리케이션을 다시 배포할 예정이므로 먼저 현재 애플리케이션을 정리하겠습니다.

다음 명령어를 실행하여 방금 만든 모든 배포 및 서비스를 삭제합니다.

  1. redis-cache-service 삭제
kubectl delete -f redis-service.yaml
  1. redis 삭제
kubectl delete -f redis.yaml
  1. frontend 삭제
kubectl delete -f frontend.yaml
  1. worker 삭제
kubectl delete -f worker-primary.yaml
  1. worker-service 삭제
kubectl delete -f worker-service.yaml

12. 기본 클러스터에 Istio 설치

Istio 다운로드

Istio의 출시 버전은 GitHub에 호스팅됩니다. 다음 명령어를 실행하면 istio 버전 1.0.0이 다운로드되고 압축이 해제됩니다.

  1. 프로젝트의 루트로 변경합니다.
cd ${proj}
  1. 보관 파일 다운로드
curl -LO https://github.com/istio/istio/releases/download/1.0.0/istio-1.0.0-linux.tar.gz
  1. 보관 파일 추출 및 삭제
tar xzf istio-1.0.0-linux.tar.gz && rm istio-1.0.0-linux.tar.gz

Istio 템플릿 만들기

다음 Helm 명령어를 실행하면 클러스터에 Istio를 설치하는 템플릿이 생성됩니다.

helm template istio-1.0.0/install/kubernetes/helm/istio \
--name istio --namespace istio-system \
--set prometheus.enabled=true \
--set servicegraph.enabled=true  > istio-primary.yaml

이렇게 하면 Istio를 배포하고 실행하는 데 필요한 모든 정의와 사양이 포함된 istio-primary.yaml 파일이 현재 디렉터리에 생성됩니다.

두 개의 --set 매개변수를 확인합니다. 이러한 추가 기능은 Istio 시스템에 PrometheusServiceGraph 지원을 추가합니다. 실습 후반부에서 Prometheus 서비스를 사용합니다.

Istio 배포

Istio를 배포하려면 먼저 Istio 배포 및 서비스가 실행될 수 있는 istio-system이라는 네임스페이스를 만들어야 합니다.

kubectl create namespace istio-system

마지막으로 Helm으로 만든 istio-primary.yaml 파일을 적용합니다.

kubectl apply -f istio-primary.yaml

기본 네임스페이스 라벨 지정

Istio는 각 배포에 사이드카 프록시 서비스를 삽입하여 작동합니다. 이는 선택 기반으로 이루어지므로 Istio가 사이드카를 자동으로 삽입할 수 있도록 istio-injection=enableddefault 네임스페이스에 라벨을 지정해야 합니다.

kubectl label namespace default istio-injection=enabled

축하합니다. 이제 Istio가 설치된 클러스터가 실행 중이므로 애플리케이션을 배포할 수 있습니다.

13. Istio 트래픽 관리로 애플리케이션 배포

Istio 트래픽 관리 구성 파일 만들기

Istio는 구성을 위해 yaml 파일을 사용하므로 Kubernetes와 유사하게 작동합니다. 따라서 Istio에 트래픽을 노출하고 라우팅하는 방법을 알려주는 파일 세트를 만들어야 합니다.

istio-manifests라는 디렉터리를 만들고 해당 디렉터리로 변경합니다.

mkdir ${proj}/istio-manifests && cd ${proj}/istio-manifests

frontend-gateway.yaml 작성

이 파일은 GKE LoadBalancer와 유사한 방식으로 Kubernetes 클러스터를 노출하고 모든 수신 트래픽을 프런트엔드 서비스로 라우팅합니다.

frontend-gateway.yaml이라는 파일을 만들고 다음을 삽입합니다.

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
 name: frontend-gateway
spec:
 selector:
   istio: ingressgateway # use Istio default gateway implementation
 servers:
 - port:
     number: 80
     name: http
     protocol: HTTP
   hosts:
   - "*"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
 name: frontend-ingress-virtual-service
spec:
 hosts:
 - "*"
 gateways:
 - frontend-gateway
 http:
 - route:
   - destination:
       host: frontend-service
       port:
         number: 80

redis-virtualservice.yaml 작성

redis-virtualservice.yaml이라는 파일을 만들고 다음을 삽입합니다.

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
 name: redis-virtual-service
spec:
 hosts:
 - redis-cache-service
 gateways:
 - mesh
 tcp:
 - route:
   - destination:
       host: redis-cache-service.default.svc.cluster.local

worker-virtualservice.yaml 작성

worker-virtualservice.yaml이라는 파일을 만들고 다음을 삽입합니다.

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
 name: worker-virtual-service
spec:
 hosts:
 - worker-service
 gateways:
 - mesh
 http:
 - route:
   - destination:
       host: worker-service.default.svc.cluster.local   
       port:
         number: 80

Istio 트래픽 관리 정책 배포

Istio 정책 배포는 kubectl apply을 사용하여 다른 Kubernetes 리소스와 동일한 방식으로 실행됩니다.

  1. Google 게이트웨이 적용
kubectl apply -f frontend-gateway.yaml
  1. Redis VirtualService 적용
kubectl apply -f redis-virtualservice.yaml
  1. 작업자 VirtualService 적용
kubectl apply -f worker-virtualservice.yaml

애플리케이션 배포

  1. kubernetes 디렉터리로 다시 변경합니다.
cd ${proj}/kubernetes
  1. Redis 캐시 배포
kubectl apply -f redis.yaml
  1. Redis 서비스 배포
kubectl apply -f redis-service.yaml
  1. 프런트엔드 배포
kubectl apply -f frontend.yaml
  1. 작업자 배포
kubectl apply -f worker-primary.yaml
  1. 작업자 서비스 배포
kubectl apply -f worker-service.yaml

확인

이제 Istio 및 트래픽 관리 정책이 있는 클러스터에 애플리케이션을 다시 배포했습니다.

모든 워크로드가 온라인 상태가 될 때까지 기다립니다.

모두 온라인 상태가 되면 frontend-ingressgateway.yaml에서 구성한 IngressGateway를 가져옵니다.

$ kubectl -n istio-system get svc istio-ingressgateway
NAME                   TYPE           CLUSTER-IP    EXTERNAL-IP     PORT(S)                                                                                                     AGE
istio-ingressgateway   LoadBalancer   10.36.3.112   35.199.158.10   80:31380/TCP,

<EXTERNAL-IP> 주소로 이동하거나 curl하면 프런트엔드가 표시됩니다.

$ curl 35.199.158.10
<!doctype html>
<html>

<head>
    <title>String Hashr</title>
    <!-- Bootstrap -->
...

14. '버스트' 클러스터에 Istio 설치

primary 클러스터에서 설정하고 배포하는 데 많은 시간을 할애했지만 배포해야 할 다른 클러스터가 있습니다.

이 섹션에서는 두 클러스터에서 구성 변수를 가져와야 하므로 각 명령어에 대해 어떤 클러스터를 가리키는지 주의해야 합니다.

Istio 원격 매니페스트 만들기

primary 클러스터에 Istio를 배포할 때와 마찬가지로 Helm을 사용하여 burst 클러스터에 Istio 원격 배포를 템플릿으로 만듭니다. 하지만 그 전에 primary 클러스터에 관한 정보를 몇 가지 확인해야 합니다.

기본 클러스터 정보 수집

primary 클러스터로 변경

kubectx primary

다음 명령어는 기본 클러스터에 있는 다양한 포드의 IP 주소를 가져옵니다. 이는 Istio Remote가 기본 클러스터와 다시 통신하는 데 사용됩니다.

export PILOT_POD_IP=$(kubectl -n istio-system get pod -l istio=pilot -o jsonpath='{.items[0].status.podIP}')
export POLICY_POD_IP=$(kubectl -n istio-system get pod -l istio-mixer-type=policy -o jsonpath='{.items[0].status.podIP}')
export STATSD_POD_IP=$(kubectl -n istio-system get pod -l istio=statsd-prom-bridge -o jsonpath='{.items[0].status.podIP}')
export TELEMETRY_POD_IP=$(kubectl -n istio-system get pod -l istio-mixer-type=telemetry -o jsonpath='{.items[0].status.podIP}')
export ZIPKIN_POD_IP=$(kubectl -n istio-system get pod -l app=jaeger -o jsonpath='{range .items[*]}{.status.podIP}{end}')

원격 템플릿 만들기

이제 helm를 사용하여 istio-remote-burst.yaml라는 파일을 만듭니다. 이 파일은 burst 클러스터에 배포할 수 있습니다.

프로젝트 루트로 변경

cd $proj
helm template istio-1.0.0/install/kubernetes/helm/istio-remote --namespace istio-system \
--name istio-remote \
--set global.remotePilotAddress=${PILOT_POD_IP} \
--set global.remotePolicyAddress=${POLICY_POD_IP} \
--set global.remoteTelemetryAddress=${TELEMETRY_POD_IP} \
--set global.proxy.envoyStatsd.enabled=true \
--set global.proxy.envoyStatsd.host=${STATSD_POD_IP} \
--set global.remoteZipkinAddress=${ZIPKIN_POD_IP} > istio-remote-burst.yaml

버스트 클러스터에 Istio Remote 설치

burst 클러스터에 Istio를 설치하려면 primary 클러스터에 설치할 때와 동일한 단계를 따라야 하지만 istio-remote-burst.yaml 파일을 대신 사용해야 합니다.

kubecontext를 버스트로 변경

kubectx burst

istio-system 네임스페이스 만들기

kubectl create ns istio-system

istio-burst.yaml 적용

kubectl apply -f istio-remote-burst.yaml

라벨 기본 네임스페이스

프록시가 자동으로 삽입될 수 있도록 default 네임스페이스에 라벨을 지정해야 합니다.

kubectl label namespace default istio-injection=enabled

축하합니다. 이제 burst 클러스터에 Istio Remote가 설정되었습니다. 하지만 이 시점에서는 클러스터가 여전히 통신할 수 없습니다. primary 클러스터에 배포하여 연결할 수 있는 burst 클러스터의 kubeconfig 파일을 생성해야 합니다.

'버스트' 클러스터의 kubeconfig 만들기

버스트 클러스터로 변경

kubectx burst

환경 설정

클러스터의 kubeconfig 파일을 만들기 위해 클러스터에 관한 몇 가지 정보를 수집해야 합니다.

  1. 클러스터 이름 가져오기
CLUSTER_NAME=$(kubectl config view --minify=true -o "jsonpath={.clusters[].name}")
  1. 클러스터 서버 이름 가져오기
SERVER=$(kubectl config view --minify=true -o "jsonpath={.clusters[].cluster.server}")
  1. istio-multi 서비스 계정 인증 기관의 보안 비밀 이름 가져오기
SECRET_NAME=$(kubectl get sa istio-multi -n istio-system -o jsonpath='{.secrets[].name}')
  1. 이전 보안 비밀에 저장된 인증 기관 데이터 가져오기
CA_DATA=$(kubectl get secret ${SECRET_NAME} -n istio-system -o "jsonpath={.data['ca\.crt']}")
  1. 이전 보안 비밀에 저장된 토큰 가져오기
TOKEN=$(kubectl get secret ${SECRET_NAME} -n istio-system -o "jsonpath={.data['token']}" | base64 --decode)

kubeconfig 파일 만들기

이러한 모든 환경 변수를 설정한 후 kubeconfig 파일을 만들어야 합니다.

cat <<EOF > burst-kubeconfig
apiVersion: v1
clusters:
   - cluster:
       certificate-authority-data: ${CA_DATA}
       server: ${SERVER}
     name: ${CLUSTER_NAME}
contexts:
   - context:
       cluster: ${CLUSTER_NAME}
       user: ${CLUSTER_NAME}
     name: ${CLUSTER_NAME}
current-context: ${CLUSTER_NAME}
kind: Config
preferences: {}
users:
   - name: ${CLUSTER_NAME}
     user:
       token: ${TOKEN}
EOF

이렇게 하면 현재 디렉터리에 primary 클러스터가 burst 클러스터를 인증하고 관리하는 데 사용할 수 있는 burst-kubeconfig라는 새 파일이 생성됩니다.

기본 클러스터로 다시 변경

kubectx primary

보안 비밀을 만들고 라벨을 지정하여 '버스트'에 kubeconfig 적용

kubectl create secret generic burst-kubeconfig --from-file burst-kubeconfig -n istio-system

Istio가 멀티 클러스터 인증에 사용할 수 있도록 보안 비밀에 라벨을 지정합니다.

kubectl label secret burst-kubeconfig istio/multiCluster=true -n istio-system

축하합니다. 두 클러스터 모두 인증되었으며 Istio 멀티 클러스터를 통해 서로 통신하고 있습니다. 애플리케이션을 크로스 클러스터로 배포해 보겠습니다.

15. 크로스 클러스터 애플리케이션 배포

배포 만들기

kubernetes 디렉터리로 변경합니다.

cd ${proj}/kubernetes

'버스트' 클러스터의 작업자 배포 만들기: worker-burst.yaml

worker-burst.yaml이라는 파일을 만들고 다음을 삽입합니다.

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: worker-deployment
  labels:
    app: worker
spec:
  replicas: 1
  selector:
    matchLabels:
      app: worker
  template:
    metadata:
      labels:
        app: worker
        cluster-type: burst-cluster
    spec:
      containers:
      - name: worker
        image: gcr.io/istio-burst-workshop/worker
        imagePullPolicy: Always
        ports:
        - containerPort: 8081
        readinessProbe:
            initialDelaySeconds: 10
            httpGet:
              path: "/_healthz"
              port: 8081
              httpHeaders:
              - name: "Cookie"
                value: "istio_session-id=x-readiness-probe"
        livenessProbe:
          initialDelaySeconds: 10
          httpGet:
            path: "/"
            port: 8081
            httpHeaders:
            - name: "Cookie"
              value: "istio_session-id=x-liveness-probe"
        env:
        - name: PORT
          value: "8081"
        - name: REDIS_URL
          value: "redis-cache-service:6379"
        - name: PREFIX
          value: "bursty-"

이 파일이 앞에서 만든 worker-primary.yaml과 거의 동일하다는 것을 알 수 있습니다. 두 가지 주요 차이점이 있습니다.

첫 번째 주요 차이점은 값이 'bursty-'인 PREFIX 환경 변수가 추가되었다는 것입니다.

env:
- name: PORT
  value: "8081"
- name: REDIS_URL
  value: "redis-cache-service:6379"
- name: PREFIX
  value: "bursty-"

즉, burst 클러스터의 작업자는 전송하는 모든 해시에 'bursty-'을 접두사로 붙입니다. 이를 통해 애플리케이션이 진정한 크로스 클러스터인지 알 수 있습니다.

두 번째 주요 차이점은 이 배포의 cluster-type 라벨이 primary-cluster에서 burst-cluster로 변경되었다는 것입니다.

labels:
  app: worker
  cluster-type: burst-cluster

나중에 VirtualService를 업데이트할 때 이 라벨을 사용합니다.

Istio 서비스 수정

현재 Istio 서비스는 두 배포를 모두 활용하지 않습니다. 트래픽의 100% 가 '기본' 클러스터로 라우팅됩니다. 변경해 보겠습니다.

istio-manifests 디렉터리로 변경합니다.

cd ${proj}/istio-manifests

DestinationRule을 포함하도록 worker-virtualservice.yaml 수정

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: worker-virtual-service
spec:
  hosts:
  - worker-service
  gateways:
  - mesh
  http:
  - route:
    - destination:
        host: worker-service.default.svc.cluster.local    
        subset: primary
        port:
          number: 80        
      weight: 50
    - destination:
        host: worker-service.default.svc.cluster.local     
        subset: burst  
        port:
          number: 80        
      weight: 50
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: worker-destination-rule
spec:
  host: worker-service
  trafficPolicy:
    loadBalancer:
      simple: RANDOM
  subsets:
  - name: primary
    labels:
      cluster-type: primary-cluster
  - name: burst
    labels:
     cluster-type: burst-cluster

VirtualService에 두 번째 대상을 추가했습니다. 여전히 동일한 호스트 (worker-service.default.svc.cluster.local))를 참조하지만 트래픽의 50% 는 primary 하위 집합으로 라우팅되고 나머지 50% 는 burst 하위 집합으로 라우팅됩니다.

cluster-type: primary-cluster 라벨이 있는 배포를 primary 하위 집합으로 정의하고 cluster-type: burst-cluster 라벨이 있는 배포를 burst 하위 집합으로 정의했습니다.

이렇게 하면 두 클러스터 간에 트래픽이 50/50으로 분할됩니다.

클러스터에 배포

버스트 클러스터에 redis-service.yaml 배포

burst kubeconfig로 변경

kubectx burst

프로젝트 루트로 변경

cd ${proj}

그런 다음 배포합니다.

버스트 클러스터에 redis-service.yaml 배포

kubectl apply -f kubernetes/redis-service.yaml

버스트 클러스터에 worker-burst.yaml 배포

kubectl apply -f kubernetes/worker-burst.yaml

버스트 클러스터에 worker-service.yaml 배포

kubectl apply -f kubernetes/worker-service.yaml

Istio VirtualServices 적용

primary kubeconfig로 변경

kubectx primary

그런 다음 배포

kubectl apply -f istio-manifests/worker-virtualservice.yaml

작동 확인

작동하는지 확인하려면 Istio 인그레스 지점으로 이동하여 해시의 약 50% 에 'burst-'가 접두사로 붙어 있는지 확인합니다.

78fb6e235e9f4a07.png

이는 클러스터 간 통신이 성공적으로 이루어지고 있음을 의미합니다. 다양한 서비스의 가중치를 변경하고 worker-virtualservice.yaml 파일을 적용해 보세요. 이는 클러스터 간 트래픽을 분산하는 좋은 방법이지만 자동으로 할 수 있다면 어떨까요?

16. Prometheus 측정항목 활용

Prometheus 소개

Prometheus는 원래 SoundCloud에서 빌드한 오픈소스 시스템 모니터링 및 알림 툴킷입니다. 측정항목 이름과 키/값 쌍으로 식별되는 시계열 데이터가 포함된 다차원 데이터 모델을 유지합니다.

참고로 Prometheus 아키텍처 다이어그램은 다음과 같습니다.

601e1155a825e0c2.png

Prometheus와 함께 배포된 Istio는 다양한 측정항목을 Prometheus 서버에 자동으로 보고합니다. 이러한 측정항목을 사용하여 즉시 클러스터를 관리할 수 있습니다.

Prometheus 측정항목 살펴보기

시작하려면 Prometheus 배포를 노출해야 합니다.

GKE의 워크로드 탭으로 이동하여 'prometheus' 워크로드로 드릴다운합니다.

b4a7a3cd67db05b3.png

배포 세부정보를 확인한 후 작업 -> 노출로 이동합니다.

c04a482e55bdfd41.png

9090 포트로 전달하도록 선택하고 '부하 분산기'를 입력합니다.

d5af3ba22a7a6ebb.png

'노출'을 선택합니다.

이렇게 하면 Prometheus 측정항목을 탐색하는 데 사용할 수 있는 공개적으로 액세스 가능한 IP 주소에 서비스가 생성됩니다.

엔드포인트가 작동될 때까지 기다린 후 '외부 엔드포인트' 옆에 있는 IP 주소를 클릭합니다. b1e40ad90851da29.png

이제 Prometheus UI가 표시됩니다.

ed273552270337ec.png

Prometheus는 자체 워크숍이 될 만큼 충분한 측정항목을 제공합니다. 하지만 지금은 istio_requests_total 측정항목을 살펴보는 것으로 시작하겠습니다.

이 쿼리를 실행하면 많은 데이터가 반환됩니다. Istio 서비스 메시를 통과하는 모든 요청에 관한 측정항목이며, 그 양이 매우 많습니다. 관심 있는 항목만 필터링하도록 표현식을 변경합니다.

대상 서비스가 worker-service.default.svc.cluster.local이고 소스가 frontend-deployment인 요청(최근 15초로 제한됨)

이 쿼리는 다음과 같습니다.

istio_requests_total{reporter="destination",
destination_service="worker-service.default.svc.cluster.local",
source_workload="frontend-deployment"}[15s]

또한 훨씬 더 관리하기 쉬운 데이터 세트를 제공합니다.

19d551fd5eac3785.png

하지만 여전히 약간 밀도가 높습니다. 모든 요청이 아닌 초당 요청 수를 알고 싶습니다.

이를 위해 기본 제공 rate 함수를 사용할 수 있습니다.

rate(istio_requests_total{reporter="destination",
destination_service="worker-service.default.svc.cluster.local",
source_workload="frontend-deployment"}[15s])

dbb9dc063a18da9b.png

거의 다 왔지만 이러한 측정항목을 논리적 그룹으로 조금 더 줄여야 합니다.

sumby 키워드를 사용하여 결과를 그룹화하고 합산할 수 있습니다.

sum(rate(istio_requests_total{reporter="destination",
destination_service="worker-service.default.svc.cluster.local",
source_workload="frontend-deployment"}[15s])) by (source_workload,
source_app, destination_service)

898519966930ec56.png

좋습니다. Prometheus에서 필요한 정확한 측정항목을 가져올 수 있습니다.

최종 Prometheus 쿼리

지금까지 배운 내용을 바탕으로 Prometheus에 요청해야 하는 최종 쿼리는 다음과 같습니다.

sum(rate(istio_requests_total{reporter="destination",
destination_service="worker-service.default.svc.cluster.local",
source_workload="frontend-deployment"}[15s])) by (source_workload,
source_app, destination_service)

이제 HTTP API를 사용하여 측정항목을 가져올 수 있습니다.

다음과 같이 http GET 요청을 만들어 쿼리로 API를 쿼리할 수 있습니다. <prometheus-ip-here>를

curl http://<prometheus-ip-here>/api/v1/query?query=sum\(rate\(istio_requests_total%7Breporter%3D%22destination%22%2C%0Adestination_service%3D%22worker-service.default.svc.cluster.local%22%2C%0Asource_workload%3D%22frontend-deployment%22%7D%5B15s%5D\)\)%20by%20\(source_workload%2C%0Asource_app%2C%20destination_service\)

응답 예시는 다음과 같습니다.

{
    "status": "success",
    "data": {
        "resultType": "vector",
        "result": [
            {
                "metric": {
                    "destination_service": "worker-service.default.svc.cluster.local",
                    "source_app": "frontend",
                    "source_workload": "frontend-deployment"
                },
                "value": [
                    1544404907.503,
                    "18.892886390062788"
                ]
            }
        ]
    }
}

이제 JSON에서 측정항목 값을 추출할 수 있습니다.

삭제하기

Prometheus를 노출하는 데 사용한 서비스를 삭제해야 합니다. Google Cloud Console에서 방금 만든 서비스의 상단으로 이동하여 '삭제'를 클릭합니다.

d58cb51b4c922751.png

다음 단계:

트래픽이 클러스터를 통과하는 방식과 속도를 파악하는 방법을 알아냈으므로 다음 단계는 주기적으로 Prometheus를 쿼리하는 작은 바이너리를 작성하는 것입니다. 초당 worker 요청이 특정 기준점을 초과하면 작업자 가상 서비스에 다른 대상 가중치를 적용하여 모든 트래픽을 burst 클러스터로 전송합니다. 초당 요청 수가 하한선 아래로 떨어지면 모든 트래픽을 primary로 다시 보냅니다.

17. 크로스 클러스터 버스트 만들기

설정

작업자 서비스의 모든 트래픽을 기본 클러스터로 설정

worker-service로 향하는 모든 트래픽이 primary 클러스터로 라우팅되는 것을 애플리케이션의 '기본' 상태로 간주합니다.

다음과 같이 $proj/istio-manifests/worker-virtualservice.yaml를 수정합니다.

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: worker-virtual-service
spec:
  hosts:
  - worker-service
  gateways:
  - mesh
  http:
  - route:
    - destination:
        host: worker-service.default.svc.cluster.local    
        subset: primary
        port:
          number: 80        
      weight: 100
    - destination:
        host: worker-service.default.svc.cluster.local     
        subset: burst  
        port:
          number: 80        
      weight: 0
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: worker-destination-rule
spec:
  host: worker-service
  trafficPolicy:
    loadBalancer:
      simple: RANDOM
  subsets:
  - name: primary
    labels:
      cluster-type: primary-cluster
  - name: burst
    labels:
     cluster-type: burst-cluster

primary 클러스터에 연결되어 있는지 확인합니다.

kubectx primary

istio-manifests/worker-virtualservice.yaml 적용

kubectl apply -f istio-manifests/worker-virtualservice.yaml

istiowatcher 데몬 작성

속도와 이식성을 위해 Go를 사용하여 이 서비스를 작성합니다. 애플리케이션의 전체 흐름은 시작하고 매초 프로메테우스를 쿼리하여

src에 istiowatcher라는 새 디렉터리를 만듭니다.

mkdir -p ${proj}/src/istiowatcher && cd ${proj}/src/istiowatcher

클러스터 내에서 Istio 컨트롤 플레인을 조작하기 위해 컨테이너 내에서 istioctl를 호출합니다.

istiowatcher.go 작성

해당 디렉터리에 istiowatcher.go이라는 파일을 만들고 다음을 삽입합니다.

package main

import (
        "github.com/tidwall/gjson"
        "io/ioutil"
        "log"
        "net/http"
        "os/exec"
        "time"
)

func main() {
        //These are in requests per second
        var targetLow float64 = 10
        var targetHigh float64 = 15
        // This is for the ticker in milliseconds
        ticker := time.NewTicker(1000 * time.Millisecond)

        isBurst := false

        // Our prometheus query
        reqQuery := `/api/v1/query?query=sum(rate(istio_requests_total{reporter="destination",destination_service="worker-service.default.svc.cluster.local",source_workload="frontend-deployment"}[15s]))by(source_workload,source_app,destination_service)`

        for t := range ticker.C {
                log.Printf("Checking Prometheus at %v", t)

                // Check prometheus
                // Note that b/c we are querying over the past 5 minutes, we are getting a very SLOW ramp of our reqs/second
                // If we wanted this to be a little "snappier" we can scale it down to say 30s
                resp, err := http.Get("http://prometheus.istio-system.svc.cluster.local:9090" + reqQuery)
                if err != nil {
                        log.Printf("Error: %v", err)
                        continue
                }
                defer resp.Body.Close()
                body, _ := ioutil.ReadAll(resp.Body)

                val := gjson.Get(string(body), "data.result.0.value.1")
                log.Printf("Value: %v", val)

                currentReqPerSecond := val.Float()
                log.Printf("Reqs per second %f", currentReqPerSecond)

                if currentReqPerSecond > targetHigh && !isBurst {
                        applyIstio("burst.yaml")
                        log.Println("Entering burst mode")
                        isBurst = true
                } else if currentReqPerSecond < targetLow && isBurst {
                        applyIstio("natural.yaml")
                        log.Println("Returning to natural state.")
                        isBurst = false
                }
        }
}

func applyIstio(filename string) {
        cmd := exec.Command("istioctl", "replace", "-f", filename)
        if err := cmd.Run(); err != nil {
                log.Printf("Error hit applying istio manifests: %v", err)
        }
}

Dockerfile 작성

Dockerfile라는 새 파일을 만들고 다음을 삽입합니다.

FROM golang:1.11.2-stretch as base

FROM base as builder

WORKDIR /workdir
RUN curl -LO https://github.com/istio/istio/releases/download/1.0.0/istio-1.0.0-linux.tar.gz
RUN tar xzf istio-1.0.0-linux.tar.gz
RUN cp istio-1.0.0/bin/istioctl ./istioctl

FROM base 

WORKDIR /go/src/istiowatcher
COPY . .
COPY --from=builder /workdir/istioctl /usr/local/bin/istioctl

RUN go get -d -v ./...
RUN go install -v ./...

CMD ["istiowatcher"]

이 다단계 Dockerfile은 첫 번째 단계에서 Istio 1.0.0 버전을 다운로드하고 추출합니다. 두 번째 단계에서는 디렉터리의 모든 항목을 이미지에 복사한 다음 빌드 단계에서 istioctl/usr/local/bin에 복사하고 (애플리케이션에서 호출할 수 있도록) 종속 항목을 가져오고 코드를 컴파일하고 CMD를 'istiowatcher'로 설정합니다.

burst.yaml 작성

frontend에서 worker으로의 초당 요청 수가 15를 초과할 때 istiowatcher가 적용되는 파일입니다.

burst.yaml라는 새 파일을 만들고 다음을 삽입합니다.

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
 name: worker-virtual-service
spec:
 hosts:
 - worker-service
 gateways:
 - mesh
 http:
 - route:
   - destination:
       host: worker-service.default.svc.cluster.local   
       subset: primary
       port:
         number: 80       
     weight: 0
   - destination:
       host: worker-service.default.svc.cluster.local    
       subset: burst 
       port:
         number: 80       
     weight:  100

natural.yaml 작성

frontend에서 worker로의 초당 요청 수가 10 미만으로 떨어지면 이 상태로 돌아갑니다. 이 상태에서는 트래픽의 100% 가 primary 클러스터로 라우팅됩니다.

natural.yaml라는 새 파일을 만들고 다음을 삽입합니다.

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
 name: worker-virtual-service
spec:
 hosts:
 - worker-service
 gateways:
 - mesh
 http:
 - route:
   - destination:
       host: worker-service.default.svc.cluster.local   
       subset: primary
       port:
         number: 80       
     weight: 100
   - destination:
       host: worker-service.default.svc.cluster.local    
       subset: burst 
       port:
         number: 80       
     weight: 0

istiowatcher 빌드 및 푸시

다음을 실행하여 현재 디렉터리를 Google Cloud Build (GCB)로 전송합니다. 그러면 GCR에서 이미지가 빌드되고 태그가 지정됩니다.

gcloud builds submit -t gcr.io/${GCLOUD_PROJECT}/istiowatcher

istiowatcher 배포

kubernetes 디렉터리로 변경합니다.

cd ${proj}/kubernetes/

배포 파일 작성: istiowatcher.yaml

istiowatcher.yaml이라는 파일을 만들고 다음을 삽입합니다(<your-project-id> 대체).

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: istiowatcher-deployment
  labels:
    app: istiowatcher
spec:
  replicas: 1
  selector:
    matchLabels:
      app: istiowatcher
  template:
    metadata:
      labels:
        app: istiowatcher
    spec:
      serviceAccountName: istio-pilot-service-account
      automountServiceAccountToken: true
      containers:
      - name: istiowatcher
        image: gcr.io/<your-project-id>/istiowatcher
        imagePullPolicy: Always

배포

기본 클러스터에서 실행 중인지 확인

kubectx primary

istio-system 네임스페이스에 istiowatcher.yaml 배포

kubectl apply -n istio-system -f istiowatcher.yaml

yaml의 serviceAccountNameautomountServiceAccountToken 지시어에 유의해야 합니다. 이렇게 하면 클러스터 내에서 istioctl를 실행하는 데 필요한 사용자 인증 정보가 제공됩니다.

istio-pilot-service-account의 사용자 인증 정보를 확보하기 위해 istio-system 네임스페이스 내에 이를 배포해야 합니다. (default 네임스페이스에 없음)

트래픽이 자동으로 전환되는 것을 확인하세요.

이제 마법 같은 순간을 즐겨 보세요. 프런트엔드로 이동하여 초당 요청 수를 20으로 늘립니다.

몇 초가 걸리지만 모든 해시에 'bursty-'가 접두사로 붙습니다.

이는 15s 범위에서 Prometheus를 샘플링하기 때문에 응답 시간이 약간 지연되기 때문입니다. 훨씬 더 좁은 범위를 원한다면 Prometheus에 대한 쿼리를 5s.로 변경하면 됩니다.

18. 다음 단계

삭제하기

이 워크숍을 위해 제공된 임시 계정을 사용하는 경우 정리할 필요가 없습니다.

Kubernetes 클러스터, 방화벽 규칙, GCR의 이미지를 삭제할 수 있습니다.

gcloud container clusters delete primary --zone=us-west1-a
gcloud container clusters delete burst --zone=us-west1-a
gcloud compute firewall-rules delete istio-multicluster-test-pods 
gcloud container images delete gcr.io/$GCLOUD_PROJECT/istiowatcher

향후 계획