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

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

이 Codelab 정보

subject최종 업데이트: 3월 30, 2021
account_circle작성자: Google 직원

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. 여기에서 Bash 스크립트를 $PATH의 위치에 다운로드하여 kubectx****를 설치합니다.
  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. 여기에서 Bash 스크립트를 $PATH의 위치에 다운로드하여 kubectx****를 설치합니다.
  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 노드를 나열합니다 (상태가 '준비됨'으로 표시됨).

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 노드를 나열합니다 (상태가 '준비됨'으로 표시됨).

kubectl get nodes

사용 편의를 위해 Kubeconfig 이름 수정

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

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)

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

a25613cd581825da.png

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

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

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

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

사용할 Istio 용어

VirtualService

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

게이트웨이

게이트웨이는 들어오거나 나가는 HTTP/TCP 연결을 수신하는 메시지의 에지에서 작동하는 부하 분산기입니다. 게이트웨이는 포트, SNI 구성 등을 지정할 수 있습니다.

DestinationRule

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

Istio 멀티클러스터

두 클러스터를 만들 때 primary 클러스터는 자동 확장 없이 노드 4개로, burst 클러스터는 최대 5개 노드까지 자동 확장되는 노드 1개로 구성되었음을 확인했을 것입니다.

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

먼저 '온프레미스'에서 Cloud로의 시나리오를 시뮬레이션해 보겠습니다. 온프레미스 환경에서는 고정된 인프라가 있으므로 자동 확장 클러스터에 액세스할 수 없습니다.

두 번째로, 4노드 설정 (위에서 정의)은 Istio를 실행하는 데 필요한 최소 요구사항입니다. 여기서 궁금한 점은 Istio에 노드가 4개 이상 필요한데 burst 클러스터는 노드 1개로 Istio를 실행할 수 있는지입니다. 그 이유는 Istio 멀티클러스터가 훨씬 적은 수의 Istio 서비스를 설치하고 기본 클러스터의 Istio 설치와 통신하여 정책 규칙을 검색하고 원격 분석 정보를 게시하기 때문입니다.

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

구성요소 개요

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

작업자

worker 애플리케이션은 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: 웹페이지에서 서버로의 양방향 통신을 허용합니다.

이 웹페이지는 스타일 지정을 위해 부트스트랩을 사용하며 실행하면 다음과 같이 표시됩니다.

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

모든 포드가 '실행 중'이면 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 매개변수 2개를 확인합니다. 이를 통해 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에서 사이드카를 자동으로 삽입할 수 있도록 default 네임스페이스에 istio-injection=enabled 라벨을 지정해야 합니다.

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. 게이트웨이 적용
kubectl apply -f frontend-gateway.yaml
  1. Redis VirtualService 적용
kubectl apply -f redis-virtualservice.yaml
  1. Worker 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. &#39;버스트&#39; 클러스터에 Istio 설치

primary 클러스터를 설정하고 배포하는 데 많은 시간을 보냈지만 배포할 또 다른 클러스터가 있습니다.

이 섹션에서는 두 클러스터에서 구성 변수를 가져와야 하므로 각 명령어에서 어떤 클러스터가 가리키는지 주의 깊게 살펴보세요.

Istio 원격 매니페스트 만들기

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

기본 클러스터 정보 수집

primary 클러스터로 변경

kubectx primary

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

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 원격 설치

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

kubecontext를 burst로 변경

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 원격을 설정했습니다. 하지만 이 시점에서는 클러스터가 여전히 통신할 수 없습니다. 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

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

기본 클러스터로 다시 변경하기

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

worker-virtualservice.yaml을 수정하여 DestinationRules 포함

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 하위 집합으로 라우팅됩니다.

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

이렇게 하면 두 클러스터 간에 트래픽이 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 주소에 서비스가 생성됩니다.

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

이제 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 콘솔에서 방금 만든 서비스 상단으로 이동하여 '삭제'를 클릭합니다.

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를 사용하여 이 서비스를 작성합니다. 애플리케이션의 전반적인 흐름은 시작하고 1초마다 prometheus를 쿼리하는 것입니다.

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>는 프로젝트 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 네임스페이스에 존재하지 않습니다.

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

이제 마법 같은 순간입니다. 프런트엔드로 이동하여 req/초를 20으로 높이겠습니다.

몇 초 정도 걸리지만 속도가 점점 빨라지고 모든 해시에 'bursty-' 접두사가 추가됩니다.

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

18. 다음 단계

삭제하기

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

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

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

향후 계획