Sử dụng Istio Multicluster để "Burst" (Tăng đột biến) khối lượng công việc giữa các cụm

1. Chào mừng bạn

Cảm ơn bạn đã tham gia lớp học lập trình về tính năng Bùng nổ đa đám mây của Istio do Google tổ chức.Lớp học lập trình này yêu cầu bạn có kinh nghiệm thực hành ở cấp độ cơ bản với Kubernetes, Node và Go.

Những gì bạn cần

  • Tài khoản Google Cloud Platform (sử dụng tài khoản hiện có hoặc chúng tôi sẽ cung cấp tài khoản miễn phí)
  • Máy tính xách tay (cài đặt "kubectl", "gcloud" v.v.) hoặc bạn có thể sử dụng Google Cloud Shell.

Kiến thức bạn sẽ học được

  • Cách tạo một cụm Kubernetes trên GKE
  • Cách cài đặt Istio trên một cụm Kubernetes bằng Helm
  • Cách cài đặt Istio Multicluster bằng Helm
  • Triển khai một ứng dụng web từ nguồn đến Kubernetes
  • Viết và áp dụng các quy tắc định tuyến lưu lượng truy cập cho Istio
  • Chỉ số Prometheus
  • Tạo và đẩy hình ảnh vùng chứa bên trong một cụm Kubernetes

2. Thiết lập

Bạn có thể làm theo lớp học lập trình này trên:

  • Google Cloud Shell (nên dùng): shell trong trình duyệt, đi kèm với các công cụ đã cài đặt
  • máy tính xách tay (làm theo hướng dẫn bên dưới)

Bắt đầu với Google Cloud Platform

  1. Nhận thẻ tài khoản người dùng miễn phí từ người hướng dẫn nếu bạn chưa có tài khoản GCP.
  2. Truy cập vào Google Cloud Console rồi nhấp vào "Chọn một dự án": 5c2d9bf74c78f7e4.png
  3. Ghi lại "Mã nhận dạng" của dự án ở đâu đó, sau đó nhấp vào dự án để chọn: ecc5e8e97bfa6559.png

Cloud Shell cung cấp một trình bao dòng lệnh trong trình duyệt của bạn, với các công cụ bạn cần đã được cài đặt và tự động xác thực vào tài khoản Google Cloud Platform của bạn. (Nếu bạn không muốn chạy bài tập này trên Cloud Shell, hãy chuyển sang phần tiếp theo.)

Truy cập vào Cloud Console rồi nhấp vào "Kích hoạt Cloud Shell" trên thanh công cụ ở trên cùng bên phải:

68a17b036ce24ccb.png

Thêm công cụ vào Cloud Shell

  1. Cài đặt kubectx****: bằng cách tải tập lệnh bash xuống từ đây vào một vị trí trong $PATH.
  2. Cài đặt helm****: làm theo các hướng dẫn này.

Hoặc chạy các lệnh sau để cài đặt cả hai vào ~/.bin và thêm vào $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}

Một số mẹo nhanh có thể giúp bạn sử dụng Cloud Shell dễ dàng hơn:

1. Tách shell thành một cửa sổ mới:

2. Sử dụng trình chỉnh sửa tệp: Nhấp vào biểu tượng bút chì ở trên cùng bên phải để chạy trình chỉnh sửa tệp trong trình duyệt. Bạn sẽ thấy tính năng này hữu ích vì chúng ta sẽ sao chép các đoạn mã vào tệp.

3. Mở thẻ mới: Nếu bạn cần nhiều hơn một lời nhắc trên thiết bị đầu cuối.

4. Tăng cỡ chữ: Cỡ chữ mặc định trên Cloud Shell có thể quá nhỏ để đọc.

Ctrl-+ trên Linux/Windows⌘-+ trên macOS.

Nếu bạn cảm thấy thoải mái hơn khi sử dụng môi trường máy trạm của riêng mình thay vì Cloud Shell, hãy thiết lập các công cụ sau:

  1. Cài đặt gcloud: (Được cài đặt sẵn trên Cloud Shell.) Làm theo hướng dẫn để cài đặt gcloud trên nền tảng của bạn. Chúng ta sẽ dùng thông tin này để tạo một cụm Kubernetes.
  2. Cài đặt kubectl:(Được cài đặt sẵn trên Cloud Shell.) Chạy lệnh sau để cài đặt:
gcloud components install kubectl

Chạy lệnh sau để xác thực gcloud. Ứng dụng sẽ yêu cầu bạn đăng nhập bằng Tài khoản Google. Sau đó, hãy chọn dự án đã tạo trước (như ở trên) làm dự án mặc định. (Bạn có thể bỏ qua bước định cấu hình vùng tính toán):

gcloud init
  1. Cài đặt curl: Được cài đặt sẵn trên hầu hết các hệ thống Linux/macOS. Có lẽ bạn đã có ứng dụng này. Nếu không, hãy tìm thông tin trên Internet về cách cài đặt.
  2. Cài đặt kubectx****: bằng cách tải tập lệnh bash từ đây xuống một vị trí trong $PATH
  3. Cài đặt helm****: làm theo các hướng dẫn này.

3. Thiết lập dự án GCP

Bật API GKE (Google Kubernetes Engine), GCR (Google Container Registry) và GCB (Google Cloud Build) trên dự án của bạn:

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

Thiết lập biến môi trường

Chúng ta sẽ làm việc nhiều với dự án Google Cloud trong quá trình thiết lập. Hãy đặt một biến môi trường để tham khảo nhanh

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

Chúng ta sẽ tạo một số mã và tệp cấu hình trong hội thảo này. Vì vậy, hãy tạo một thư mục dự án và chuyển vào thư mục đó

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

4. Tạo cụm Kubernetes "chính"

Bạn có thể dễ dàng tạo cụm Kubernetes được quản lý bằng Google Kubernetes Engine (GKE).

Lệnh sau đây sẽ tạo một cụm Kubernetes:

  • có tên là "primary",
  • trong vùng us-west1-a,
  • Phiên bản Kubernetes mới nhất hiện có,
  • với 4 nút ban đầu
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

(Quá trình này có thể mất khoảng 5 phút. Bạn có thể xem quá trình tạo cụm tại Cloud Console.)

Sau khi cụm Kubernetes được tạo, gcloud sẽ định cấu hình kubectl bằng thông tin đăng nhập trỏ đến cụm.

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

Giờ đây, bạn có thể sử dụng kubectl với cụm mới.

Chạy lệnh sau để liệt kê các nút Kubernetes của cụm (các nút này phải có trạng thái "Sẵn sàng"):

kubectl get nodes

Sửa đổi tên Kubeconfig để dễ sử dụng

Chúng ta sẽ thường xuyên chuyển đổi giữa các bối cảnh, vì vậy, việc có một bí danh ngắn cho các cụm của chúng ta là rất hữu ích.

Lệnh này sẽ đổi tên mục kubeconfig mà bạn vừa tạo thành primary

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

Đặt quyền:

Để triển khai Istio, bạn phải là quản trị viên cụm. Lệnh này sẽ đặt email liên kết với tài khoản Google Cloud của bạn làm quản trị viên cụm

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

5. Tạo cụm "burst"

Lệnh sau đây sẽ tạo một cụm Kubernetes:

  • có tên là "burst",
  • trong vùng us-west1-a,
  • Phiên bản Kubernetes mới nhất hiện có,
  • Có 1 nút ban đầu
  • Đã bật tính năng tự động mở rộng quy mô lên đến 5 nút
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

(Quá trình này có thể mất khoảng 5 phút. Bạn có thể xem quá trình tạo cụm tại Cloud Console.)

Sau khi cụm Kubernetes được tạo, gcloud sẽ định cấu hình kubectl bằng thông tin đăng nhập trỏ đến cụm.

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

Giờ đây, bạn có thể sử dụng kubectl với cụm mới.

Chạy lệnh sau để liệt kê các nút Kubernetes của cụm (các nút này phải có trạng thái "Sẵn sàng"):

kubectl get nodes

Sửa đổi tên Kubeconfig để dễ sử dụng

Lệnh này sẽ sửa đổi mục kubeconfig mà bạn vừa tạo thành burst

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

Đặt quyền:

Để triển khai Istio Remote, bạn phải là quản trị viên cụm. Lệnh này sẽ đặt email liên kết với tài khoản Google Cloud của bạn làm quản trị viên cụm

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

6. Áp dụng quy tắc tường lửa

Để 2 cụm này giao tiếp với nhau, chúng ta cần tạo một quy tắc cho Tường lửa.

Chạy các lệnh sau để tạo một quy tắc Tường lửa trong Google Cloud Platform. Quy tắc này sẽ cho phép các cụm của chúng ta giao tiếp

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

Chúng ta đã thiết lập cả hai cụm và sẵn sàng triển khai ứng dụng cũng như Istio trên các cụm đó!

7. Giới thiệu về Istio

Istio là gì?

Istio là một lớp điều khiển lưới dịch vụ nhằm "kết nối, bảo mật, kiểm soát và giám sát các dịch vụ". Istio thực hiện việc này theo nhiều cách, nhưng chủ yếu là bằng cách thêm một vùng chứa proxy ( Envoy) vào mỗi Pod Kubernetes mà bạn đã triển khai. Vùng chứa proxy kiểm soát mọi hoạt động giao tiếp mạng giữa các vi dịch vụ cùng với một chính sách đa năng và trung tâm đo từ xa ( Mixer).

a25613cd581825da.png

Các chính sách này có thể được áp dụng độc lập với các Dịch vụ và Hoạt động triển khai Kubernetes của bạn, tức là Network Operator có thể quan sát hoạt động mạng, hạn chế, chuyển hướng hoặc viết lại các chính sách mạng mà không cần triển khai lại các ứng dụng được liên kết.

Một số tính năng Quản lý lưu lượng truy cập mà Istio hỗ trợ là:

  • Cầu dao
  • Phân tách lưu lượng truy cập dựa trên tỷ lệ phần trăm
  • Viết lại URL
  • Chấm dứt TLS
  • Kiểm tra tình trạng
  • Cân bằng tải

Trong hội thảo này, chúng ta sẽ tập trung vào việc phân tách lưu lượng truy cập dựa trên tỷ lệ phần trăm.

Các điều khoản về Istio mà chúng ta sẽ sử dụng

VirtualService

VirtualService xác định một bộ quy tắc định tuyến lưu lượng truy cập để áp dụng khi một máy chủ được định địa chỉ.

Cổng

Cổng là một trình cân bằng tải hoạt động ở rìa của lưới, nhận các kết nối HTTP/TCP đến hoặc đi. Cổng có thể chỉ định Cổng, cấu hình SNI, v.v.

DestinationRule

DestinationRule xác định các chính sách áp dụng cho lưu lượng truy cập dành cho một dịch vụ sau khi quá trình định tuyến diễn ra. Chúng chỉ định cấu hình để cân bằng tải, kích thước nhóm kết nối từ sidecar và chế độ cài đặt phát hiện giá trị ngoại lệ.

Istio Multicluster

Có thể bạn đã nhận thấy khi chúng ta tạo hai cụm, cụm primary của chúng ta có 4 nút mà không có tính năng tự động mở rộng quy mô và cụm burst có 1 nút với tính năng tự động mở rộng quy mô lên đến 5 nút.

Có 2 lý do cho cấu hình này.

Trước tiên, chúng ta muốn mô phỏng một kịch bản "tại chỗ" lên đám mây. Trong môi trường tại chỗ, bạn không có quyền truy cập vào các cụm tự động mở rộng quy mô vì bạn có cơ sở hạ tầng cố định.

Thứ hai, chế độ thiết lập 4 nút (như được xác định ở trên) là yêu cầu tối thiểu để chạy Istio. Điều này đặt ra câu hỏi: nếu Istio yêu cầu tối thiểu 4 nút, thì làm cách nào để cụm burst của chúng ta chạy Istio với 1 nút? Câu trả lời là Istio Multicluster cài đặt một nhóm nhỏ hơn nhiều các dịch vụ Istio và giao tiếp với bản cài đặt Istio trong cụm chính để truy xuất các quy tắc chính sách và xuất bản thông tin đo từ xa.

8. Tổng quan về cấu trúc ứng dụng

Tổng quan về các thành phần

Chúng ta sẽ triển khai một ứng dụng gồm 3 cấp bằng cách sử dụng NodeJSRedis.

Worker

Ứng dụng worker được viết bằng NodeJS và sẽ lắng nghe các yêu cầu POST HTTP đến, thực hiện một thao tác băm trên các yêu cầu đó và nếu một biến môi trường có tên là PREFIX được xác định, thì ứng dụng sẽ thêm giá trị đó vào trước hàm băm. Sau khi tính toán xong, ứng dụng sẽ gửi kết quả trên kênh "calculation" trên máy chủ Redis đã chỉ định.

Sau này, chúng ta sẽ sử dụng biến môi trường PREFIX để minh hoạ chức năng nhiều cụm.

Để tham khảo: đây là các gói mà ứng dụng sử dụng.

  • body-parser: Cho phép chúng ta phân tích cú pháp các yêu cầu http
  • cors: Cho phép sử dụng tính năng Chia sẻ tài nguyên trên nhiều nguồn gốc
  • dotenv: Dễ dàng phân tích cú pháp các biến môi trường
  • express: Dễ dàng lưu trữ trang web
  • ioredis: Thư viện ứng dụng để giao tiếp với cơ sở dữ liệu Redis
  • morgan: Cung cấp nhật ký có cấu trúc rõ ràng

Giao diện người dùng

Giao diện người dùng của chúng tôi cũng là một ứng dụng NodeJS lưu trữ một trang web bằng express. Nó lấy tần suất do người dùng nhập và gửi yêu cầu đến ứng dụng worker của chúng tôi theo tốc độ đó. Ứng dụng này cũng đăng ký nhận thông báo trên một kênh Redis có tên "calculation" và hiển thị kết quả trên một trang web.

Ứng dụng này sử dụng các phần phụ thuộc sau.

  • body-parser: Cho phép chúng ta phân tích cú pháp các yêu cầu http
  • dotenv: Dễ dàng phân tích cú pháp các biến môi trường
  • express: Dễ dàng lưu trữ trang web
  • ioredis: Thư viện ứng dụng để giao tiếp với cơ sở dữ liệu Redis
  • morgan: Cung cấp nhật ký có cấu trúc rõ ràng
  • request: Cho phép đưa ra yêu cầu HTTP
  • socket.io: Cho phép giao tiếp hai chiều từ Trang web đến máy chủ

Trang web này sử dụng Bootstrap để tạo kiểu và khi chạy, trang web này sẽ có dạng như sau

e5e3b9cbede4cac4.png

Sơ đồ kiến trúc

7ae4bc22a58f80a6.png

Sơ đồ triển khai

Chúng ta sẽ triển khai ứng dụng cuối cùng trên hai cụm mà chúng ta đã tạo. Cụm primary sẽ có tất cả các thành phần (frontend, worker và Redis) được triển khai, nhưng cụm burst sẽ chỉ có ứng dụng worker được triển khai.

Dưới đây là biểu đồ mô tả 2 cụm này. Các hộp được viền màu đỏ là Dịch vụ Kubernetes, còn các hộp màu xanh dương là Triển khai Kubernetes. Các hộp màu vàng biểu thị quá trình cài đặt Istio.

561db37c510944bd.png

Lưu ý cách cụm burst vẫn có một dịch vụ cho Redis được triển khai cho cụm này, mặc dù không có Deployment nào cho Redis trong cụm. Chúng ta cần có dịch vụ này trong cụm để Kubernetes DNS có thể phân giải yêu cầu, nhưng khi yêu cầu thực sự được thực hiện, Istio Proxy sẽ định tuyến lại yêu cầu đến việc triển khai Redis trong cụm primary.

Ứng dụng cuối cùng sẽ có một Deployment bổ sung đang chạy trong cụm primary có tên là istiowatcher.. Đây là những gì sẽ cho phép chúng ta tự động định tuyến lại lưu lượng truy cập đến burst một cách linh hoạt khi lưu lượng truy cập vượt quá một ngưỡng nhất định.

8f6183bdfc3f813c.png

9. Tạo tệp triển khai ứng dụng

Chúng ta cần tạo một bộ tệp kê khai Kubernetes để triển khai ứng dụng

Thay đổi thành thư mục gốc của dự án và tạo một thư mục mới có tên là kubernetes

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

Viết frontend.yaml

Thao tác này sẽ tạo cả Kubernetes Deployment và Service để truy cập vào hình ảnh giao diện người dùng của chúng ta.

Chèn nội dung sau vào 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

Những điều quan trọng cần lưu ý trong Deployment

  • Chúng tôi đã chỉ định cổng mà ứng dụng sẽ chạy là 8080
  • Chúng ta đã đặt địa chỉ cho worker là "http://worker-service" và sẽ sử dụng tính năng DNS tích hợp của Kubernetes để phân giải dịch vụ kết quả
  • Chúng tôi đã đặt địa chỉ cho REDIS_URL là "redis-cache-service:6379" và sẽ sử dụng tính năng DNS tích hợp của Kubernetes để phân giải các địa chỉ IP kết quả.
  • Chúng tôi cũng đã thiết lập các lệnh livenessreadiness để thăm dò vùng chứa nhằm giúp Kubernetes biết khi nào vùng chứa đang hoạt động.

Viết worker-service.yaml

Chúng ta sẽ viết định nghĩa dịch vụ Kubernetes trong một tệp riêng biệt so với định nghĩa Triển khai vì chúng ta sẽ sử dụng lại dịch vụ này trên nhiều cụm, nhưng sẽ viết một định nghĩa Triển khai khác cho mỗi cụm.

Chèn nội dung sau vào worker-service.yaml

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

Viết worker-primary.yaml

Đây sẽ là quá trình triển khai worker mà chúng ta sẽ chuyển sang cụm chính.

Chèn nội dung sau vào 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"

Lưu ý rằng trong ví dụ này, chúng ta đang tuân theo cùng một mẫu cung cấp các lệnh dò tìm livenessreadiness, cũng như chỉ định các biến môi trường PORTREDIS_URL để ứng dụng của chúng ta sử dụng.

Một điều khác cần lưu ý trong quá trình triển khai này là việc thiếu biến môi trường PREFIX. Điều này có nghĩa là kết quả tính toán của chúng tôi sẽ là các hàm băm thô (không có tiền tố).

Điểm mấu chốt cuối cùng của việc triển khai này là nhãn cluster-type: primary-cluster. Chúng ta sẽ sử dụng tính năng đó sau này khi thực hiện Định tuyến lưu lượng truy cập trên Istio Multicluster

Viết redis.yaml

Thông tin liên lạc từ worker trở lại giao diện người dùng thông qua một kênh Redis. Do đó, chúng ta cần triển khai một ứng dụng Redis vào cụm.

Chèn nội dung sau vào 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: {}

Đây là một chế độ triển khai bán chuẩn của ứng dụng Redis. Thao tác này sẽ thiết lập một vùng chứa dựa trên hình ảnh redis:alpine, hiển thị các cổng thích hợp và đặt giới hạn tài nguyên hợp lý.

Viết redis-service.yaml

Chúng ta cần một Dịch vụ Kubernetes để giao tiếp với ứng dụng Redis

Chèn nội dung sau vào redis-service.yaml

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

Thao tác này cung cấp dịch vụ có tên redis-cache-service để truy cập vào Redis Deployment của chúng tôi.

10. Triển khai ứng dụng

Sau khi đẩy các hình ảnh của chúng ta lên GCR và viết các tệp kê khai Kubernetes, đây là thời điểm thích hợp để triển khai ứng dụng và xem cách ứng dụng hoạt động!

Chạy các lệnh sau để triển khai ứng dụng

  1. Đảm bảo chúng tôi đang ở đúng cụm
kubectx primary
  1. Triển khai bộ nhớ đệm Redis
kubectl apply -f redis.yaml
  1. Triển khai dịch vụ Redis
kubectl apply -f redis-service.yaml
  1. Triển khai giao diện người dùng
kubectl apply -f frontend.yaml
  1. Triển khai Worker
kubectl apply -f worker-primary.yaml
  1. Triển khai Dịch vụ Worker
kubectl apply -f worker-service.yaml

Chúng ta đã triển khai ứng dụng của mình lên GKE. Xin chúc mừng!

Kiểm thử

Chờ các pod chuyển sang trạng thái trực tuyến

kubectl get pods -w

Sau khi tất cả các pod đều ở trạng thái "Running" (Đang chạy), hãy nhấn tổ hợp phím 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

Bạn sẽ nhận thấy rằng chúng tôi không hiển thị giao diện người dùng thông qua LoadBalancer. Đó là vì sau này chúng ta sẽ truy cập vào ứng dụng thông qua Istio. Để kiểm thử xem mọi thứ có đang hoạt động hay không, chúng ta sẽ sử dụng kubectl port-forward. Chạy lệnh sau để chuyển tiếp cổng 8080 trên máy cục bộ (hoặc Cloud Shell) sang cổng 8080 đang chạy quá trình triển khai frontend.

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

Nếu bạn đang chạy cục bộ: mở một trình duyệt web rồi chuyển đến http://localhost:8080

Nếu bạn đang chạy trong Cloud Shell: hãy nhấp vào nút "Xem trước trên web" rồi chọn "Xem trước trên cổng 8080"

bdb5dc75f415be11.png

Bạn sẽ thấy giao diện người dùng! Và nếu nhập một số vào hộp "tần suất", bạn sẽ thấy các hàm băm bắt đầu xuất hiện

1caafaffab26897a.png

Chúc mừng bạn, mọi thứ đã hoạt động!

Nhấn vào Ctrl+C để dừng chuyển tiếp cổng.

11. Dọn dẹp ứng dụng đã triển khai

Chúng ta sẽ áp dụng Istio cho cụm rồi triển khai lại ứng dụng. Vì vậy, trước tiên, hãy dọn dẹp ứng dụng hiện tại.

Chạy các lệnh sau để xoá tất cả các Deployment và dịch vụ mà bạn vừa tạo

  1. Xoá redis-cache-service
kubectl delete -f redis-service.yaml
  1. Xoá redis
kubectl delete -f redis.yaml
  1. Xoá frontend
kubectl delete -f frontend.yaml
  1. Xoá worker
kubectl delete -f worker-primary.yaml
  1. Xoá worker-service
kubectl delete -f worker-service.yaml

12. Cài đặt Istio trên Cụm chính

Tải Istio

Các bản phát hành của Istio được lưu trữ trên GitHub. Các lệnh sau sẽ tải phiên bản 1.0.0 của istio xuống và giải nén phiên bản đó.

  1. Thay đổi thành thư mục gốc của dự án
cd ${proj}
  1. Tải bản lưu trữ xuống
curl -LO https://github.com/istio/istio/releases/download/1.0.0/istio-1.0.0-linux.tar.gz
  1. Giải nén và xoá tệp lưu trữ
tar xzf istio-1.0.0-linux.tar.gz && rm istio-1.0.0-linux.tar.gz

Tạo mẫu Istio

Việc chạy lệnh Helm sau đây sẽ tạo mẫu để cài đặt Istio vào cụm của bạn.

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

Thao tác này sẽ tạo một tệp có tên là istio-primary.yaml trong thư mục hiện tại của bạn, chứa tất cả các định nghĩa và quy cách cần thiết để triển khai và chạy Istio.

Hãy chú ý đến 2 tham số --set. Các tiện ích này bổ sung khả năng hỗ trợ PrometheusServiceGraph cho hệ thống Istio. Chúng ta sẽ sử dụng dịch vụ Prometheus sau trong phòng thí nghiệm.

Triển khai Istio

Để triển khai istio, trước tiên, chúng ta cần tạo một không gian tên có tên là istio-system mà các Dịch vụ và Hoạt động triển khai Istio có thể chạy trong đó.

kubectl create namespace istio-system

Cuối cùng, hãy áp dụng tệp istio-primary.yaml mà chúng ta đã tạo bằng Helm

kubectl apply -f istio-primary.yaml

Không gian tên mặc định của nhãn

Istio hoạt động bằng cách chèn một dịch vụ proxy phụ vào từng Deployment của bạn. Việc này được thực hiện dựa trên cơ chế chọn tham gia, vì vậy, chúng ta cần gắn nhãn không gian tên default bằng istio-injection=enabled để Istio có thể tự động chèn vùng chứa phụ cho chúng ta.

kubectl label namespace default istio-injection=enabled

Xin chúc mừng! Chúng ta đã có một cụm đang hoạt động với Istio, sẵn sàng để chúng ta triển khai ứng dụng!

13. Triển khai ứng dụng bằng tính năng quản lý lưu lượng truy cập của Istio

Tạo tệp cấu hình quản lý lưu lượng truy cập Istio

Istio hoạt động tương tự như Kubernetes vì sử dụng các tệp yaml để định cấu hình. Theo đó, chúng ta cần tạo một bộ tệp để cho Istio biết cách hiển thị và định tuyến lưu lượng truy cập.

Tạo một thư mục có tên là istio-manifests rồi chuyển vào thư mục đó

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

Viết frontend-gateway.yaml

Tệp này sẽ hiển thị cụm Kubernetes của chúng ta theo cách tương tự như GKE LoadBalancer và sẽ định tuyến tất cả lưu lượng truy cập đến dịch vụ giao diện người dùng của chúng ta.

Tạo một tệp có tên là frontend-gateway.yaml rồi chèn nội dung sau.

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

Viết redis-virtualservice.yaml

Tạo một tệp có tên là redis-virtualservice.yaml rồi chèn nội dung sau

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

Viết worker-virtualservice.yaml

Tạo một tệp có tên là worker-virtualservice.yaml rồi chèn nội dung sau

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

Triển khai các chính sách quản lý lưu lượng truy cập của Istio

Việc triển khai các chính sách của Istio được thực hiện theo cách tương tự như các tài nguyên khác của Kubernetes, bằng kubectl apply

  1. Áp dụng Cổng
kubectl apply -f frontend-gateway.yaml
  1. Áp dụng Redis VirtualService
kubectl apply -f redis-virtualservice.yaml
  1. Áp dụng Worker VirtualService
kubectl apply -f worker-virtualservice.yaml

Triển khai ứng dụng

  1. Chuyển về thư mục kubernetes của chúng tôi
cd ${proj}/kubernetes
  1. Triển khai bộ nhớ đệm Redis
kubectl apply -f redis.yaml
  1. Triển khai dịch vụ Redis
kubectl apply -f redis-service.yaml
  1. Triển khai giao diện người dùng
kubectl apply -f frontend.yaml
  1. Triển khai Worker
kubectl apply -f worker-primary.yaml
  1. Triển khai Dịch vụ Worker
kubectl apply -f worker-service.yaml

Xác minh

Tại thời điểm này, chúng tôi đã triển khai lại ứng dụng của mình trên một cụm có Istio và các chính sách quản lý lưu lượng truy cập.

Hãy đợi tất cả các khối lượng công việc của chúng ta chuyển sang trạng thái trực tuyến

Sau khi tất cả các thành phần này hoạt động, hãy lấy IngressGateway mà chúng ta đã định cấu hình trong frontend-ingressgateway.yaml

$ 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,

Bạn có thể duyệt đến địa chỉ <EXTERNAL-IP> hoặc dùng lệnh curl để xem giao diện người dùng!

$ curl 35.199.158.10
<!doctype html>
<html>

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

14. Cài đặt Istio trên Cụm "burst"

Chúng tôi đã dành rất nhiều thời gian để thiết lập và triển khai trên cụm primary, nhưng chúng tôi còn một cụm khác để triển khai!

Trong phần này, chúng ta sẽ cần lấy các biến cấu hình trên cả hai cụm. Vì vậy, hãy chú ý đến cụm mà chúng ta đang trỏ đến cho từng lệnh.

Tạo tệp kê khai từ xa Istio

Tương tự như khi triển khai Istio cho cụm primary, chúng ta sẽ sử dụng Helm để tạo mẫu cho việc triển khai Istio từ xa cho cụm burst. Tuy nhiên, trước khi có thể làm như vậy, chúng ta cần lấy một số thông tin về cụm primary của mình

Thu thập thông tin về cụm chính

Thay đổi thành cụm primary

kubectx primary

Các lệnh sau đây truy xuất địa chỉ IP của nhiều nhóm trong cụm chính. Istio Remote dùng các cổng này để giao tiếp lại với cụm chính.

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}')

Tạo mẫu từ xa

Giờ đây, chúng ta sẽ dùng helm để tạo một tệp có tên là istio-remote-burst.yaml. Sau đó, chúng ta có thể triển khai tệp này vào cụm burst.

Thay đổi thành thư mục gốc của dự án

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

Cài đặt Istio Remote trên Cụm tăng tốc

Để cài đặt Istio trên cụm burst, chúng ta cần làm theo các bước tương tự như khi cài đặt trên cụm primary, nhưng chúng ta cần sử dụng tệp istio-remote-burst.yaml.

Thay đổi kubecontext thành burst

kubectx burst

Tạo vùng chứa tên istio-system

kubectl create ns istio-system

Áp dụng istio-burst.yaml

kubectl apply -f istio-remote-burst.yaml

Không gian tên mặc định của nhãn

Một lần nữa, chúng ta cần gắn nhãn không gian tên default để có thể tự động chèn proxy.

kubectl label namespace default istio-injection=enabled

Xin chúc mừng! Đến đây, chúng ta đã thiết lập Istio Remote trên cụm burst. Tuy nhiên, tại thời điểm này, các cụm vẫn không thể giao tiếp. Chúng ta cần tạo một tệp kubeconfig cho cụm burst mà chúng ta có thể triển khai đến cụm primary để liên kết chúng với nhau.

Tạo kubeconfig cho cụm "burst"

Thay đổi thành cụm chụp liên tục

kubectx burst

Thiết lập môi trường

Chúng tôi cần thu thập một số thông tin về cụm để tạo tệp kubeconfig cho cụm đó.

  1. Lấy tên của cụm
CLUSTER_NAME=$(kubectl config view --minify=true -o "jsonpath={.clusters[].name}")
  1. Lấy tên máy chủ của cụm
SERVER=$(kubectl config view --minify=true -o "jsonpath={.clusters[].cluster.server}")
  1. Lấy tên của khoá bí mật cho Tổ chức phát hành chứng chỉ tài khoản dịch vụ istio-multi
SECRET_NAME=$(kubectl get sa istio-multi -n istio-system -o jsonpath='{.secrets[].name}')
  1. Lấy dữ liệu Cơ quan cấp chứng chỉ được lưu trữ trong khoá bí mật trước đó
CA_DATA=$(kubectl get secret ${SECRET_NAME} -n istio-system -o "jsonpath={.data['ca\.crt']}")
  1. Lấy mã thông báo được lưu trữ trong khoá bí mật trước đó
TOKEN=$(kubectl get secret ${SECRET_NAME} -n istio-system -o "jsonpath={.data['token']}" | base64 --decode)

Tạo tệp kubeconfig

Sau khi thiết lập tất cả các biến môi trường đó, chúng ta cần tạo tệp 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

Thao tác này sẽ tạo một tệp mới có tên là burst-kubeconfig trong thư mục hiện tại. Tệp này có thể được cụm primary dùng để xác thực và quản lý cụm burst.

Chuyển về cụm chính

kubectx primary

Áp dụng kubeconfig cho "burst" bằng cách tạo một bí mật và gắn nhãn cho bí mật đó

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

Gắn nhãn cho khoá bí mật để Istio biết cách sử dụng khoá này cho quy trình xác thực nhiều cụm

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

Xin chúc mừng! Chúng tôi đã xác thực cả hai cụm và giao tiếp với nhau thông qua Istio Multicluster. Hãy triển khai ứng dụng Cross-Cluster

15. Triển khai ứng dụng trên nhiều cụm

Tạo các phiên bản triển khai

Thay đổi thành thư mục kubernetes

cd ${proj}/kubernetes

Tạo việc triển khai worker cho cụm "burst": worker-burst.yaml

Tạo một tệp có tên là worker-burst.yaml rồi chèn nội dung sau vào tệp đó:

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-"

Lưu ý rằng tệp này gần giống với worker-primary.yaml mà chúng ta đã tạo trước đó. Có 2 điểm khác biệt chính.

Điểm khác biệt chính đầu tiên là chúng tôi đã thêm biến môi trường PREFIX có giá trị "bursty-"

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

Điều này có nghĩa là worker của chúng ta trong cụm burst sẽ thêm tiền tố "bursty-" vào tất cả các hàm băm mà worker gửi. Chúng ta có thể dùng tiền tố này để biết rằng ứng dụng của chúng ta thực sự là ứng dụng đa cụm.

Điểm khác biệt chính thứ hai là chúng tôi đã thay đổi nhãn cluster-type trên bản triển khai này từ primary-cluster thành burst-cluster

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

Chúng ta sẽ sử dụng nhãn này sau khi cập nhật VirtualService.

Sửa đổi các dịch vụ Istio

Hiện tại, các dịch vụ Istio của chúng tôi không tận dụng được cả hai quy trình triển khai. 100% lưu lượng truy cập của chúng tôi đang được chuyển đến cụm "chính". Hãy thay đổi điều đó.

Chuyển sang thư mục istio-manifests của chúng tôi

cd ${proj}/istio-manifests

Chỉnh sửa worker-virtualservice.yaml để thêm 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

Bạn có thể thấy chúng tôi đã thêm một đích đến thứ hai vào VirtualService của mình. Nó vẫn tham chiếu đến cùng một máy chủ lưu trữ (worker-service.default.svc.cluster.local)), nhưng 50% lưu lượng truy cập đang được chuyển đến tập hợp con primary và 50% còn lại được chuyển đến tập hợp con burst.

Chúng ta đã xác định tập hợp con primary là những triển khai có nhãn cluster-type: primary-cluster và tập hợp con burst là những triển khai có nhãn cluster-type: burst-cluster.

Điều này giúp phân tách lưu lượng truy cập của chúng tôi thành 50/50 giữa hai cụm.

Triển khai cho Cụm

Triển khai redis-service.yaml vào cụm tăng tốc

Thay đổi thành burst kubeconfig

kubectx burst

Thay đổi thành thư mục gốc của dự án

cd ${proj}

Sau đó, triển khai

Triển khai redis-service.yaml vào cụm burst

kubectl apply -f kubernetes/redis-service.yaml

Triển khai worker-burst.yaml vào cụm tăng tốc

kubectl apply -f kubernetes/worker-burst.yaml

Triển khai worker-service.yaml vào cụm tăng tốc

kubectl apply -f kubernetes/worker-service.yaml

Áp dụng VirtualService của Istio

Thay đổi thành primary kubeconfig

kubectx primary

Sau đó, hãy triển khai

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

Xác minh xem có hiệu quả không

Để xác minh rằng tính năng này hoạt động, hãy duyệt đến điểm truy cập Istio và lưu ý rằng khoảng 50% hàm băm có tiền tố "burst-".

78fb6e235e9f4a07.png

Điều này có nghĩa là chúng ta đang giao tiếp thành công trên nhiều cụm! Hãy thử thay đổi trọng số trên các dịch vụ khác nhau và áp dụng tệp worker-virtualservice.yaml. Đây là một cách hay để cân bằng lưu lượng truy cập giữa các cụm, nhưng nếu chúng ta có thể thực hiện việc này một cách tự động thì sao?

16. Khai thác các chỉ số Prometheus

Giới thiệu về Prometheus

Prometheus là một bộ công cụ nguồn mở để giám sát và cảnh báo hệ thống, ban đầu được xây dựng tại SoundCloud. Nền tảng này duy trì một mô hình dữ liệu đa chiều với dữ liệu chuỗi thời gian được xác định bằng tên chỉ số và các cặp khoá/giá trị.

Để tham khảo, đây là sơ đồ Kiến trúc Prometheus:

601e1155a825e0c2.png

Khi được triển khai cùng với Prometheus, Istio sẽ tự động báo cáo nhiều chỉ số cho máy chủ Prometheus. Chúng ta có thể sử dụng các chỉ số này để quản lý các cụm của mình ngay lập tức.

Khám phá các chỉ số Prometheus của chúng tôi

Để bắt đầu, chúng ta cần hiển thị Prometheus Deployment.

Chuyển đến thẻ Workloads (Khối lượng công việc) trong GKE, truy sâu vào khối lượng công việc "prometheus".

b4a7a3cd67db05b3.png

Sau khi bạn xem thông tin chi tiết về việc triển khai, hãy chuyển đến phần Actions (Hành động) -> Expose (Hiển thị).

c04a482e55bdfd41.png

Chọn chuyển tiếp đến cổng 9090 rồi nhập "Bộ cân bằng tải"

d5af3ba22a7a6ebb.png

Sau đó, chọn "Phơi sáng"

Thao tác này sẽ tạo một Dịch vụ trên địa chỉ IP có thể truy cập công khai mà chúng ta có thể dùng để khám phá các Chỉ số Prometheus

Đợi cho đến khi điểm cuối hoạt động, sau đó nhấp vào địa chỉ IP bên cạnh "Điểm cuối bên ngoài" b1e40ad90851da29.png

Lúc này, bạn sẽ thấy giao diện người dùng Prometheus.

ed273552270337ec.png

Prometheus cung cấp đủ chỉ số để trở thành một hội thảo riêng. Tuy nhiên, trước mắt, chúng ta sẽ bắt đầu bằng cách khám phá chỉ số istio_requests_total.

Việc thực thi truy vấn đó sẽ trả về một loạt dữ liệu. Đây là các chỉ số về tất cả các yêu cầu đang được xử lý thông qua lưới dịch vụ Istio. Chúng ta sẽ thay đổi biểu thức để lọc ra những gì chúng ta thực sự quan tâm:

Các yêu cầu mà dịch vụ đích là worker-service.default.svc.cluster.local và nguồn là frontend-deployment chỉ giới hạn trong 15 giây gần nhất

Truy vấn đó có dạng như sau:

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

Và cung cấp cho chúng tôi một tập dữ liệu dễ quản lý hơn nhiều để làm việc

19d551fd5eac3785.png

Nhưng vẫn còn hơi dày. Chúng tôi muốn biết số yêu cầu mỗi giây, chứ không phải tất cả các yêu cầu.

Để lấy được giá trị đó, chúng ta có thể sử dụng hàm rate tích hợp sẵn

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

dbb9dc063a18da9b.png

Điều này giúp chúng ta tiến gần hơn, nhưng chúng ta cần giảm thêm một chút các chỉ số đó thành một nhóm logic.

Để làm việc này, chúng ta có thể dùng các từ khoá sumby để nhóm và tính tổng kết quả

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

Tuyệt lắm! Chúng ta có thể lấy chính xác các chỉ số cần thiết từ Prometheus.

Câu truy vấn Prometheus cuối cùng

Với tất cả những gì chúng ta đã học, truy vấn cuối cùng mà chúng ta cần hỏi Prometheus là

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)

Giờ đây, chúng ta có thể sử dụng HTTP API của họ để lấy chỉ số này.

Chúng ta có thể truy vấn API của chúng bằng truy vấn của mình bằng cách đưa ra yêu cầu GET http như sau. Thay thế <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\)

Sau đây là một ví dụ về phản hồi:

{
    "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"
                ]
            }
        ]
    }
}

Giờ đây, chúng ta có thể trích xuất giá trị chỉ số từ JSON

Dọn dẹp

Chúng ta cần xoá Dịch vụ mà chúng ta vừa dùng để hiển thị Prometheus. Trong Google Cloud Console, hãy chuyển đến đầu dịch vụ mà chúng ta vừa tạo rồi nhấp vào "Xoá"

d58cb51b4c922751.png

Các bước tiếp theo:

Sau khi tìm ra cách khám phá lưu lượng truy cập đang di chuyển qua cụm và tốc độ di chuyển, bước tiếp theo của chúng tôi là viết một tệp nhị phân nhỏ định kỳ truy vấn prometheus và nếu số yêu cầu mỗi giây đến worker vượt quá một ngưỡng nhất định, hãy áp dụng các trọng số đích đến khác nhau cho dịch vụ ảo của worker để gửi tất cả lưu lượng truy cập đến cụm burst. Khi số yêu cầu mỗi giây giảm xuống dưới một ngưỡng thấp hơn, hãy gửi tất cả lưu lượng truy cập trở lại primary.

17. Tạo một Cụm tăng tốc chéo

Thiết lập

Đặt tất cả lưu lượng truy cập cho dịch vụ worker vào cụm chính

Chúng tôi sẽ coi tất cả lưu lượng truy cập đến worker-service được định tuyến đến cụm primary là trạng thái "mặc định" của ứng dụng

Chỉnh sửa $proj/istio-manifests/worker-virtualservice.yaml để có dạng như sau

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

Đảm bảo bạn đã kết nối với cụm primary

kubectx primary

Áp dụng istio-manifests/worker-virtualservice.yaml

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

Viết trình nền istiowatcher

Chúng ta sẽ sử dụng Go để viết dịch vụ này vì tốc độ và tính di động của nó. Quy trình tổng thể của ứng dụng sẽ là khởi động và mỗi giây, truy vấn prometheus,

Tạo một thư mục mới trong src có tên là istiowatcher

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

Chúng ta sẽ gọi istioctl từ bên trong vùng chứa để thao tác với bảng điều khiển Istio từ bên trong cụm.

Viết istiowatcher.go

Tạo một tệp có tên istiowatcher.go trong thư mục đó rồi chèn nội dung sau vào tệp

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)
        }
}

Viết tệp Docker

Tạo một tệp mới có tên Dockerfile và chèn nội dung sau vào tệp đó.

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 nhiều giai đoạn này tải xuống và trích xuất bản phát hành 1.0.0 của Istio ở giai đoạn đầu tiên. Giai đoạn thứ hai sao chép mọi thứ từ thư mục của chúng ta vào hình ảnh, sau đó sao chép istioctl từ giai đoạn xây dựng sang /usr/local/bin (để ứng dụng của chúng ta có thể gọi), lấy các phần phụ thuộc, biên dịch mã và đặt CMD thành "istiowatcher"

Viết burst.yaml

Đây là tệp istiowatcher sẽ áp dụng khi số yêu cầu/giây đến worker từ frontend vượt quá 15.

Tạo một tệp mới có tên burst.yaml và chèn nội dung sau vào tệp đó.

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

Viết natural.yaml

Chúng tôi sẽ coi đây là trạng thái "tự nhiên" mà chúng tôi quay lại khi số yêu cầu/giây từ frontend đến worker giảm xuống dưới 10. Ở trạng thái này, 100% lưu lượng truy cập đang được định tuyến đến cụm primary.

Tạo một tệp mới có tên natural.yaml rồi chèn nội dung sau vào tệp đó

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

Tạo và đẩy istiowatcher

Chạy lệnh sau để gửi thư mục hiện tại đến Google Cloud Build (GCB). Thư mục này sẽ tạo và gắn thẻ hình ảnh trong GCR.

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

Triển khai istiowatcher

Chuyển sang thư mục kubernetes của chúng tôi

cd ${proj}/kubernetes/

Viết tệp triển khai: istiowatcher.yaml

Tạo một tệp có tên là istiowatcher.yaml và chèn nội dung sau (Thay thế <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

Triển khai

Đảm bảo chúng tôi đang chạy trong cụm chính

kubectx primary

Triển khai istiowatcher.yaml trong không gian tên istio-system

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

Điều quan trọng cần lưu ý là các chỉ thị serviceAccountNameautomountServiceAccountToken trong yaml. Thao tác này cung cấp cho chúng ta thông tin đăng nhập cần thiết để chạy istioctl trong cụm.

Chúng ta cũng cần triển khai việc này trong không gian tên istio-system để đảm bảo có thông tin đăng nhập cho istio-pilot-service-account. (không tồn tại trong không gian tên default).

Xem lưu lượng truy cập tự động chuyển đổi!

Giờ là khoảnh khắc kỳ diệu! Hãy chuyển đến giao diện người dùng và tăng số yêu cầu/giây lên 20

Bạn sẽ thấy quá trình này mất vài giây, nhưng chúng ta sẽ tăng tốc độ và tất cả các hàm băm đều có tiền tố "bursty-"!

Nguyên nhân là do chúng tôi đang lấy mẫu prometheus trong phạm vi 15s, điều này khiến thời gian phản hồi của chúng tôi bị trễ một chút. Nếu muốn có một dải băng hẹp hơn nhiều, chúng ta có thể thay đổi truy vấn thành prometheus thành 5s.

18. Tiếp theo là gì?

Dọn dẹp

Bạn không cần dọn dẹp nếu đang sử dụng tài khoản tạm thời được cung cấp cho hội thảo này.

Bạn có thể xoá các cụm Kubernetes, quy tắc tường lửa và hình ảnh trong 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

Tiến về phía trước