استخدام مجموعات Istio المتعددة "لزيادة" أحمال العمل بين المجموعات

استخدام Istio Multicluster "لزيادة" أعباء العمل بين المجموعات

لمحة عن هذا الدرس التطبيقي حول الترميز

subjectتاريخ التعديل الأخير: مارس 30, 2021
account_circleتأليف موظف Google

1. مرحبًا

نشكرك على الانضمام إلينا في ورشة عمل Istio Multi Cloud Burst codelab من Google.تتطلّب ورشة العمل هذه خبرة عملية على مستوى المبتدئين في Kubernetes وNode وGo.

المتطلبات

  • حساب Google Cloud Platform (استخدِم حسابًا حاليًا أو سنمنحك حسابات مجانية)
  • الكمبيوتر المحمول (يجب تثبيت "kubectl" و"gcloud" وما إلى ذلك) أو يمكنك استخدام Google Cloud Shell

ما الذي ستتعرّف عليه

  • كيفية إنشاء مجموعة Kubernetes على Google Kubernetes Engine
  • كيفية تثبيت Istio على مجموعة Kubernetes باستخدام Helm
  • كيفية تثبيت Istio Multicluster باستخدام Helm
  • نشر تطبيق ويب من المصدر إلى Kubernetes
  • كتابة قواعد توجيه الزيارات وتطبيقها على Istio
  • مقاييس Prometheus
  • إنشاء صور حاويات ودفعها داخل مجموعة Kubernetes

2. الإعداد

يمكنك اتّباع هذا الدرس التطبيقي حول الترميز على أيّ من الرابطَين التاليَين:

  • Google Cloud Shell (مُقترَح): واجهة مستخدِم داخل المتصفّح، تأتي مع أدوات مثبّتة
  • الكمبيوتر المحمول (اتّبِع التعليمات أدناه)

البدء باستخدام Google Cloud Platform

  1. استلم بطاقة حساب المستخدم المجانية من المدرّس إذا لم يكن لديك حساب على Google Cloud Platform.
  2. انتقِل إلى Google Cloud Console وانقر على "اختيار مشروع": 5c2d9bf74c78f7e4.png
  3. دوِّن رقم تعريف المشروع في مكان ما، ثم انقر على المشروع لاختياره: ecc5e8e97bfa6559.png

توفّر Cloud Shell بيئة سطر أوامر داخل المتصفّح تتضمّن الأدوات التي تحتاجها مثبّتة ومصادقة تلقائيًا على حسابك على Google Cloud Platform. (إذا كنت لا تريد تنفيذ هذا التمرين على Cloud Shell، انتقِل إلى القسم التالي).

انتقِل إلى وحدة تحكّم السحابة الإلكترونية وانقر على "تفعيل Cloud Shell" في شريط الأدوات أعلى يسار الصفحة:

68a17b036ce24ccb.png

إضافة أدوات إلى Cloud Shell

  1. تثبيت kubectx****: من خلال تنزيل النصوص البرمجية bash من هنا إلى موقع في $PATH
  2. تثبيت helm****: اتّبِع هذه التعليمات.

بدلاً من ذلك، يمكنك تنفيذ هذه الأوامر لتثبيت كلا البرنامجَين في ~/.bin وإضافتهما إلى $PATH:

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

في ما يلي بعض النصائح السريعة التي يمكن أن تسهّل استخدام Cloud Shell:

1. افصل القشرة في نافذة جديدة:

2- باستخدام محرِّر الملفات: انقر على رمز القلم الرصاص في أعلى يسار الصفحة لتشغيل محرِّر ملفات داخل المتصفّح. سيفيدك ذلك لأنّنا سننسخ مقتطفات الرموز البرمجية إلى الملفات.

3. بدء علامات تبويب جديدة: إذا كنت بحاجة إلى أكثر من طلب واحد في المحطة الطرفية.

4. تكبير النص: قد يكون حجم الخط التلقائي على Cloud Shell صغيرًا جدًا بحيث لا يمكن قراءته.

Ctrl-+ على نظام التشغيل Linux/Windows‎⌘-+ على نظام التشغيل macOS

إذا كنت تشعر بالارتياح باستخدام بيئة محطة العمل الخاصة بك أكثر من Cloud Shell، يمكنك إعداد الأدوات التالية:

  1. تثبيت gcloud: (مُثبَّت مسبقًا على Cloud Shell) اتّبِع التعليمات لتثبيت gcloud على منصّتك. سنستخدم هذا لإنشاء مجموعة Kubernetes.
  2. تثبيت kubectl:(مُثبَّت مسبقًا على Cloud Shell) شغِّل الأمر التالي للتثبيت:
gcloud components install kubectl

نفِّذ الأمر التالي لمصادقة gcloud. سيُطلب منك تسجيل الدخول باستخدام حسابك على Google. بعد ذلك، اختَر المشروع الذي تم إنشاؤه مسبقًا (كما هو موضّح أعلاه) كمشروع تلقائي. (يمكنك تخطّي ضبط منطقة الحوسبة):

gcloud init
  1. تثبيت curl: مثبَّت مسبقًا على معظم أنظمة التشغيل Linux/macOS من المحتمل أن يكون لديك هذا الخيار. أما إذا لم يكن الأمر كذلك، يمكنك البحث على الإنترنت عن كيفية تثبيته.
  2. تثبيت kubectx****: من خلال تنزيل نصوص bash البرمجية من هنا إلى موقع في $PATH
  3. تثبيت helm****: اتّبِع هذه التعليمات.

3. إعداد مشروع Google Cloud Platform

فعِّل واجهات برمجة التطبيقات GKE (Google Kubernetes Engine) وGCR (Google Container Registry) وGCB (Google Cloud Build) في مشروعك:

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 "أساسية"

يمكنك بسهولة إنشاء مجموعة Kubernetes مُدارة باستخدام Google Kubernetes Engine (GKE).

سيؤدي الأمر التالي إلى إنشاء مجموعة 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 Console.)

بعد إنشاء مجموعة Kubernetes، يضبط gcloud kubectl باستخدام بيانات الاعتماد التي تشير إلى المجموعة.

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

من المفترض أن تتمكّن الآن من استخدام kubectl مع مجموعتك الجديدة.

نفِّذ الأمر التالي لعرض قوائم عقد Kubernetes الخاصة بمجموعة 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
  • مع عقدة أولية واحدة
  • تم تفعيل التوسّع التلقائي لما يصل إلى 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 Console.)

بعد إنشاء مجموعة Kubernetes، يضبط gcloud kubectl باستخدام بيانات الاعتماد التي تشير إلى المجموعة.

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

من المفترض أن تتمكّن الآن من استخدام kubectl مع مجموعتك الجديدة.

نفِّذ الأمر التالي لعرض قوائم عقد Kubernetes الخاصة بمجموعة Kubernetes (من المفترض أن تظهر الحالة "جاهز"):

kubectl get nodes

تعديل أسماء Kubeconfig لتسهيل الاستخدام

سيؤدي هذا الأمر إلى تعديل إدخال kubeconfig الذي أجريته للتو إلى burst.

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

ضبط الأذونات:

يتطلب نشر Istio Remote أن تكون مشرفًا للمجموعة. سيضبط هذا الأمر عنوان البريد الإلكتروني المرتبط بحسابك على Google Cloud كمشرف للمجموعة.

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

6. تطبيق قواعد جدار الحماية

لكي تتواصل مجموعتَنا معًا، سنحتاج إلى إنشاء قاعدة جدار حماية.

نفِّذ الأوامر التالية لإنشاء قاعدة جدار حماية في Google Cloud Platform تسمح للمجموعات بالتواصل.

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

لقد تم إعداد كلتا مجموعتَينا العنقوديتين وأصبحنا جاهزين لنشر تطبيقنا وIstio عليهما.

7. مقدمة عن Istio

ما هو Istio؟

Istio هي طائرة تحكّم في شبكة الخدمات تهدف إلى "ربط الخدمات وتأمينها والتحكّم فيها ومراقبتها". ويتم ذلك بطرق متنوعة، ولكن في المقام الأول من خلال إضافة حاوية وكيل ( Envoy) إلى كل وحدة من وحدات Kubernetes المُنشَأة. تتحكّم حاوية الوكيل في جميع عمليات الاتصال بالشبكة بين الخدمات الصغيرة مع سياسة للأغراض العامة ومركز بيانات إحصاءات ( Mixer).

a25613cd581825da.png

يمكن تطبيق هذه السياسات بشكل مستقل عن عمليات نشر Kubernetes وخدماتها، ما يعني أنّه يمكن لمشغّل الشبكة مراقبة نشاط الشبكة أو تقييد سياسات الشبكة أو إعادة توجيهها أو إعادة كتابتها بدون إعادة نشر التطبيقات المرتبطة.

في ما يلي بعض ميزات إدارة الزيارات التي يتيحها Istio:

  • قواطع الدائرة الكهربائية
  • تقسيم عدد الزيارات استنادًا إلى النسبة المئوية
  • إعادة كتابة عنوان URL
  • إنهاء بروتوكول أمان طبقة النقل (TLS)
  • فحوص صحية
  • موازنة الحمل

لأغراض هذه الورشة، سنركّز على تقسيم الزيارات استنادًا إلى النسبة المئوية.

بنود Istio التي سنستخدمها

VirtualService

تحدِّد VirtualService مجموعة من قواعد توجيه حركة البيانات لتطبيقها عند توجيه طلب إلى مضيف.

البوابة

البوابة هي جهاز موازنة حمل يعمل على حافة الشبكة ويتلقّى اتصالات HTTP/TCP واردة أو صادِرة. يمكن أن تحدّد البوابات المنافذ وإعدادات إشارة اسم الخادم (SNI) وما إلى ذلك.

DestinationRule

تحدِّد DestinationRule السياسات التي تنطبق على الزيارات المخصّصة لخدمة معيّنة بعد اكتمال عملية التوجيه. وهي تحدّد الإعدادات الخاصة بموازنة التحميل وحجم مجموعة اتصالات من العنصر الإضافي وإعدادات رصد القيم الشاذة.

Istio Multicluster

عندما أنشأنا مجموعتَنا، ربما لاحظت أنّ مجموعة primary كانت تتألف من 4 عقد بدون توسيع نطاق الاستضافة، وأنّ مجموعة burst كانت تتألف من عقدة واحدة مع توسيع نطاق الاستضافة حتى 5 عقد.

هناك سببان لهذه الإعدادات.

أولاً، نريد محاكاة سيناريو نقل البيانات من "الموقع الإلكتروني" إلى السحابة الإلكترونية. في البيئة المستضافة على الخادم، لا يمكنك الوصول إلى مجموعات "التصغير/التكبير التلقائي" لأنّ لديك بنية أساسية ثابتة.

ثانيًا، يُعدّ إعداد 4 عقد (على النحو المحدّد أعلاه) الحدّ الأدنى للمتطلبات لتشغيل Istio. يطرح هذا السؤال: إذا كان Istio يتطلّب 4 عقد على الأقل، كيف يمكن أن تعمل مجموعة burst على Istio باستخدام عقدة واحدة؟ الإجابة هي أنّ Istio Multicluster يُثبِّت مجموعة أصغر بكثير من خدمات Istio، ويتواصل مع عملية تثبيت Istio في المجموعة الأساسية لاسترداد قواعد السياسة ونشر معلومات القياس عن بُعد.

8. نظرة عامة على بنية التطبيق

نظرة عامة على المكوّنات

سننشر تطبيقًا من ثلاث طبقات باستخدام NodeJS وRedis.

العامل

تم كتابة تطبيق Worker بتنسيق NodeJS وسيستمع إلى طلبات POST HTTP الواردة، وسينفّذ عملية تجزئة عليها، وإذا تم تعريف متغيّر بيئة باسم PREFIX، سيضيف هذا المتغيّر إلى القيمة المجزّأة. بعد احتساب التجزئة، يرسل التطبيق النتيجة على القناة "calculation" على خادم Redis المحدّد.

سنستخدم متغيّر البيئة PREFIX لاحقًا لعرض وظيفة المجموعات المتعددة.

يُرجى العِلم أنّ هذه هي الحِزم التي يستخدمها التطبيق.

  • body-parser: يسمح لنا بتحليل طلبات http
  • cors: السماح باستخدام ميزة "مشاركة الموارد مع نطاقات خارجية"
  • dotenv: تحليل متغيرات البيئة بسهولة
  • express: استضافة المواقع الإلكترونية بسهولة
  • ioredis: مكتبة العميل للتواصل مع قواعد بيانات Redis
  • morgan: يوفّر سجلّاً منظَّمًا جيدًا

الواجهة الأمامية

الواجهة الأمامية هي أيضًا تطبيق NodeJS يستضيف صفحة ويب باستخدام express. يأخذ هذا الإجراء معدّل تكرار يُدخله المستخدم ويرسل طلبات إلى تطبيق worker وفقًا لهذا المعدّل. يشترك هذا التطبيق أيضًا في الرسائل على قناة Redis باسم "calculation" ويعرض النتائج في صفحة ويب.

يستخدم التطبيق التبعيات التالية.

  • body-parser: يسمح لنا بتحليل طلبات http
  • dotenv: تحليل متغيرات البيئة بسهولة
  • express: استضافة المواقع الإلكترونية بسهولة
  • ioredis: مكتبة العميل للتواصل مع قواعد بيانات Redis
  • morgan: توفير سجلّات منظَّمة بشكل جيد
  • request: السماح بإجراء طلبات HTTP
  • socket.io: السماح بالاتصال الثنائي الاتجاه من صفحة الويب إلى الخادم

تستخدم صفحة الويب هذه Bootstrap لتنسيق المحتوى، وعند تشغيلها، ستظهر على النحو التالي:

e5e3b9cbede4cac4.png

مخطّط البنية

7ae4bc22a58f80a6.png

مخطّط النشر

سننشر تطبيقنا النهائي على مجموعتَي البيانات اللتين أنشأناهما. ستتضمّن مجموعة primary جميع المكوّنات (frontend وworker وRedis)، ولكن لن يتم نشر سوى تطبيق worker في مجموعة burst.

في ما يلي مخطّط بياني يصف مجموعتَي البيانات. المربّعات التي تم تحديدها باللون الأحمر هي خدمات Kubernetes، والمربّعات التي تم تحديدها باللون الأزرق هي عمليات نشر Kubernetes. تشير المربّعات الصفراء إلى تثبيت Istio.

561db37c510944bd.png

يُرجى ملاحظة أنّ مجموعة burst لا تزال تتضمّن خدمة Redis تم نشرها فيها على الرغم من عدم توفّر عملية نشر لـ Redis في المجموعة. يجب أن تتوفّر هذه الخدمة في المجموعة حتى يتمكّن نظام أسماء النطاقات في Kubernetes من حلّ الطلب، ولكن عند تقديم الطلب فعليًا، سيعيد خادم Istio Proxy توجيه الطلب إلى عملية نشر Redis في مجموعة primary.

سيتضمّن التطبيق النهائي عملية نشر إضافية يتم تنفيذها في مجموعة 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 لتحويل الخدمة الناتجة.
  • لقد ضبطنا عنوان REDIS_URL على "redis-cache-service:6379" وسنستخدم ميزة نظام أسماء النطاقات المضمّنة في Kubernetes لتحويل عناوين IP الناتجة.
  • لقد ضبطنا أيضًا مسوحات liveness وreadiness على الحاوية للمساعدة في إبلاغ Kubernetes عندما تكون الحاوية قيد التشغيل.

كتابة 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"

يُرجى العلم أنّنا نتّبع النمط نفسه لتقديم فحصَي liveness وreadiness بالإضافة إلى تحديد متغيّرات البيئة PORT وREDIS_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

نحتاج إلى خدمة Kubernetes للتواصل مع تطبيق Redis.

أدخِل ما يلي في 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-cache-service للوصول إلى عملية نشر Redis.

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. نشر خدمة Worker
kubectl apply -f worker-service.yaml

لقد نشرنا تطبيقنا على GKE. تهانينا!

الاختبار

الانتظار إلى أن تظهر مجموعات التطبيقات على الإنترنت

kubectl get pods -w

بعد أن تصبح جميع وحدات pod "قيد التشغيل"، اضغط على 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.. يمكنك تنفيذ الأمر التالي لإعادة توجيه المنفذ 8080 على جهازك المحلي (أو Cloud Shell) إلى المنفذ 8080 الذي يشغّل عملية النشر frontend.

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. ستؤدي الأوامر التالية إلى تنزيل الإصدار 1.0.0 من istio وفك ضغطه.

  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-primary.yaml في الدليل الحالي يحتوي على جميع التعريفات والمواصفات اللازمة لنشر Istio وتشغيله.

لاحِظ المَعلمتَين --set. تضيف هذه الإصدارات دعم Prometheus وServiceGraph إلى نظام Istio. سنستخدم خدمة Prometheus لاحقًا في هذا البرنامج التدريبي.

نشر Istio

لنشر Istio، نحتاج أولاً إلى إنشاء مساحة اسم تسمى istio-system يمكن تشغيل عمليات نشر Istio وخدماته فيها.

kubectl create namespace istio-system

وأخيرًا، طبِّق ملف istio-primary.yaml الذي أنشأناه باستخدام Helm.

kubectl apply -f istio-primary.yaml

مساحة الاسم التلقائية للعلامة

يعمل Istio من خلال إدخال خدمة وكيل جانبي في كل عملية من عمليات النشر. يتم إجراء ذلك على أساس الموافقة، لذا نحتاج إلى تصنيف مساحة الاسم default باستخدام istio-injection=enabled حتى تتمكّن Istio من إدخال الإضافة تلقائيًا نيابةً عنا.

kubectl label namespace default istio-injection=enabled

تهانينا! لدينا مجموعة قيد التشغيل باستخدام Istio وجاهزة لنشر تطبيقنا.

13. نشر تطبيقنا باستخدام إدارة حركة المرور في Istio

إنشاء ملفات ضبط إدارة حركة المرور في Istio

يعمل Istio بطريقة مشابهة لـ Kubernetes لأنّه يستخدم ملفات yaml للضبط. في هذا السياق، نحتاج إلى إنشاء مجموعة من الملفات التي تُعلم Istio كيفية عرض الزيارات وتوجيهها.

أنشئ دليلاً باسم istio-manifests وانتقِل إليه.

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

كتابة frontend-gateway.yaml

سيعرِض هذا الملف مجموعة Kubernetes بطريقة مشابهة لـ GKE LoadBalancer وسيوجّه كلّ الزيارات الواردة إلى خدمة الواجهة الأمامية.

أنشئ ملفًا باسم 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 بالطريقة نفسها المتّبعة مع موارد Kubernetes الأخرى، باستخدام kubectl apply

  1. تطبيق بوابتنا
kubectl apply -f frontend-gateway.yaml
  1. تطبيق Redis VirtualService
kubectl apply -f redis-virtualservice.yaml
  1. تطبيق VirtualService العاملة
kubectl apply -f worker-virtualservice.yaml

نشر التطبيق

  1. الرجوع إلى دليل kubernetes
cd ${proj}/kubernetes
  1. نشر ذاكرة التخزين المؤقت Redis
kubectl apply -f redis.yaml
  1. نشر خدمة Redis
kubectl apply -f redis-service.yaml
  1. نشر الواجهة الأمامية
kubectl apply -f frontend.yaml
  1. نشر العامل
kubectl apply -f worker-primary.yaml
  1. نشر خدمة Worker
kubectl apply -f worker-service.yaml

التحقّق من ذلك

في هذه المرحلة، أعدنا نشر تطبيقنا على مجموعة تتضمّن Istio وسياسات إدارة الزيارات.

لننتظر حتى تصبح جميع أعباء العمل متاحة على الإنترنت.

بعد أن تصبح جميع الأجهزة متصلة بالإنترنت، احصل على IngressGateway الذي أعددناه في 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,

يمكنك الانتقال إلى عنوان <EXTERNAL-IP> أو استخدامه مع أداة curl، ومن المفترض أن تظهر لك الواجهة الأمامية.

$ curl 35.199.158.10
<!doctype html>
<html>

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

14. تثبيت Istio على &quot;مجموعة الذروة&quot;

لقد قضينا الكثير من الوقت في الإعداد والنشر على مجموعة primary، ولكن لدينا مجموعة أخرى كاملة للنشر عليها.

في هذا القسم، سنحتاج إلى الحصول على متغيّرات الإعدادات في كلتا مجموعتَينا، لذا انتبِه جيدًا إلى المجموعة التي نشير إليها لكلّ أمر.

إنشاء ملف بيان Istio عن بُعد

تمامًا كما فعلنا عند نشر Istio في مجموعة primary، سنستخدم Helm لإنشاء نموذج لنشر Istio عن بُعد في مجموعة burst. قبل أن نتمكّن من إجراء ذلك، نحتاج إلى الحصول على بعض المعلومات عن مجموعة primary.

جمع معلومات المجموعة الأساسية

التغيير إلى مجموعة primary

kubectx primary

تسترجع الأوامر التالية عناوين IP للوحدات المختلفة في المجموعة الأساسية. يستخدم Istio Remote هذه العناصر للتواصل مع المجموعة الأساسية.

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

إنشاء نموذج عن بُعد

سنستخدم الآن helm لإنشاء ملف باسم istio-remote-burst.yaml يمكننا نشره بعد ذلك في مجموعة burst.

التغيير إلى جذر المشروع

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

تثبيت Istio Remote على مجموعة Burst Cluster

لتثبيت Istio على مجموعة burst، علينا اتّباع الخطوات نفسها المتّبعة عند التثبيت على مجموعة 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

تهانينا! في هذه المرحلة، تم إعداد Istio Remote على مجموعة burst. في هذه المرحلة، لا تزال المجموعات غير قادرة على التواصل. نحتاج إلى إنشاء ملف kubeconfig لقطّعة burst التي يمكننا نشرها في قطّعة primary لربطهما معًا.

إنشاء ملف kubeconfig للمجموعة "burst"

التغيير إلى مجموعة معالجة مكثّفة

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 Multicluster. لننشر تطبيقنا على مستوى المجموعة

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 الذي أنشأناه سابقًا. هناك اختلافان رئيسيان.

الفرق الرئيسي الأول هو أنّنا أضفنا متغيّر البيئة PREFIX بالقيمة "bursty-".

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.

يؤدي ذلك إلى تقسيم الزيارات مناصفةً بين مجموعتَي البيانات.

النشر في المجموعة

نشر redis-service.yaml في مجموعة الفواصل الزمنية القصيرة

تغيير ملف kubeconfig إلى burst

kubectx burst

الانتقال إلى جذر المشروع

cd ${proj}

بعد ذلك، يمكنك نشر التطبيق.

نشر redis-service.yaml في مجموعة الفواصل الزمنية القصيرة

kubectl apply -f kubernetes/redis-service.yaml

نشر worker-burst.yaml في مجموعة burst

kubectl apply -f kubernetes/worker-burst.yaml

نشر worker-service.yaml في مجموعة الفواصل الزمنية القصيرة

kubectl apply -f kubernetes/worker-service.yaml

تطبيق Istio VirtualServices

تغيير ملف kubeconfig إلى primary

kubectx primary

بعد ذلك، عليك نشر التطبيق.

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

التحقّق من أنّ الميزة تعمل

للتأكّد من أنّه يعمل، انتقِل إلى نقطة Istio Ingress، ولاحظ أنّ حوالي% 50 من التجزئات تبدأ بالبادئة "burst-".

78fb6e235e9f4a07.png

هذا يعني أنّنا نتواصل بنجاح على مستوى المجموعات. جرِّب تغيير الأوزان في الخدمات المختلفة وتطبيق ملف worker-virtualservice.yaml. هذه طريقة رائعة لموازنة عدد الزيارات بين المجموعات، ولكن ماذا لو كان بإمكاننا إجراء ذلك تلقائيًا؟

16. الاستفادة من مقاييس Prometheus

مقدّمة عن Prometheus

Prometheus هي مجموعة أدوات مفتوحة المصدر لمراقبة الأنظمة وإصدار تنبيهات، وقد تم إنشاؤها في الأصل في SoundCloud. ويحافظ على نموذج بيانات متعدد الأبعاد يتضمّن بيانات سلاسل زمنية يتم تحديدها حسب اسم المقياس وأزواج المفتاح/القيمة.

في ما يلي مخطّط Prometheus Architecture كمرجع:

601e1155a825e0c2.png

عند نشر Istio مع Prometheus، يتم تلقائيًا إعداد تقارير عن مقاييس مختلفة لخادم Prometheus. يمكننا استخدام هذه المقاييس لإدارة مجموعاتنا على الفور.

استكشاف مقاييس Prometheus

للبدء، علينا إتاحة عملية نشر Prometheus.

انتقِل إلى علامة التبويب "الأحمال" في GKE، ثم انتقِل إلى "حمّل prometheus".

b4a7a3cd67db05b3.png

بعد عرض تفاصيل عملية النشر، انتقِل إلى "الإجراءات" -> "إظهار".

c04a482e55bdfd41.png

اختَر إعادة التوجيه إلى المنفذ 9090، واكتب "موازن الحمولة".

d5af3ba22a7a6ebb.png

واختَر "إظهار".

سيؤدي ذلك إلى إنشاء خدمة على عنوان IP متاح للجميع يمكننا استخدامه لاستكشاف مقاييس Prometheus.

انتظِر حتى تصبح نقطة النهاية قيد التشغيل، وبعد ذلك انقر على عنوان IP بجانب "نقاط النهاية الخارجية". b1e40ad90851da29.png

من المفترض أن تظهر لك الآن واجهة مستخدم Prometheus.

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

يقربنا ذلك من النتيجة، ولكن علينا تقليل هذه المقاييس قليلاً إلى مجموعة منطقية.

لإجراء ذلك، يمكننا استخدام الكلمات الرئيسية sum وby لتجميع النتائج وجمعها.

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 للحصول على المقياس.

يمكننا طلب البيانات من واجهة برمجة التطبيقات باستخدام طلب GET على http على النحو التالي. استبدِل <prometheus-ip-here>

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

في ما يلي مثال على الردّ:

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

يمكننا الآن استخراج قيمة المقياس من ملف JSON.

التنظيف

علينا حذف الخدمة التي استخدمناها للتو لعرض Prometheus. في Google Cloud Console، انتقِل إلى أعلى الخدمة التي أنشأناها للتو وانقر على "حذف".

d58cb51b4c922751.png

الخطوات التالية:

بعد أن توصّلنا إلى طريقة لاكتشاف كيفية انتقال الزيارات عبر المجموعة وبأي معدّل، تكون خطوتنا التالية هي كتابة ملف ثنائي صغير يجري طلبات بحث بشكل دوري في Prometheus، وإذا تجاوز عدد الطلبات في الثانية إلى worker حدًا معيّنًا، يتم تطبيق أوزان وجهات مختلفة على الخدمة الافتراضية للعامل لإرسال كل الزيارات إلى مجموعة burst. بعد انخفاض عدد الطلبات في الثانية إلى ما دون الحدّ الأدنى، أرسِل جميع الزيارات مرة أخرى إلى primary.

17. إنشاء ذروة على مستوى مجموعات متعددة

الإعداد

ضبط جميع الزيارات للخدمة العاملة على المجموعة الأساسية

سنعتبر أنّ جميع الزيارات المقصودة لـ worker-service والتي يتم توجيهها إلى مجموعة primary هي الحالة "التلقائية" لتطبيقنا.

عدِّل $proj/istio-manifests/worker-virtualservice.yaml لتظهر على النحو التالي:

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

تأكَّد من أنّك متصل بمجموعة primary.

kubectx primary

تطبيق istio-manifests/worker-virtualservice.yaml

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

كتابة برنامج istiowatcher الخدمي

سنستخدم Go لكتابة هذه الخدمة بسبب سرعتها وسهولة نقلها. سيكون المسار العام للتطبيق هو بدء التشغيل واستعلام Prometheus كل ثانية.

أنشئ دليلاً جديدًا في src باسم istiowatcher.

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

سنتصل بـ istioctl من داخل الحاوية من أجل التلاعب بمستوى التحكّم في Istio من داخل المجموعة.

كتابة 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

هذا هو الملف الذي سيتم تطبيق istiowatcher عليه عندما تتجاوز الطلبات في الثانية إلى worker من frontend 15 طلبًا.

أنشئ ملفًا جديدًا باسم burst.yaml وأدخِل ما يلي فيه.

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

كتابة ملف natural.yaml

سنعتبر هذه الحالة "طبيعية" ونعود إليها عندما ينخفض عدد الطلبات في الثانية من frontend إلى worker إلى أقل من 10. في هذه الحالة، يتم توجيه% 100 من الزيارات إلى مجموعة primary.

أنشئ ملفًا جديدًا باسم natural.yaml وأدخِل ما يلي فيه

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

إنشاء istiowatcher ودفعه

نفِّذ ما يلي لإرسال الدليل الحالي إلى Google Cloud Build (GCB)، الذي سينشئ الصورة ويصنّفها في GCR.

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

نشر istiowatcher

الانتقال إلى دليل kubernetes

cd ${proj}/kubernetes/

كتابة ملف نشر: istiowatcher.yaml

أنشئ ملفًا باسم istiowatcher.yaml وأدخِل ما يلي (استبدِل <your-project-id>).

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

النشر

التأكّد من أنّنا ننفّذ العملية في المجموعة الأساسية

kubectx primary

نشر istiowatcher.yaml في مساحة الاسم istio-system

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

من المهم ملاحظة تعليمات serviceAccountName وautomountServiceAccountToken في ملف YAML. يمنحنا ذلك بيانات الاعتماد اللازمة لتشغيل istioctl من داخل المجموعة.

نحتاج أيضًا إلى نشر هذا ضمن مساحة الاسم istio-system لضمان حصولنا على بيانات الاعتماد الخاصة بـ istio-pilot-service-account. (لا يتوفّر في مساحة الاسم default).

راقِب عملية تبديل الزيارات تلقائيًا.

حان وقت اللقطة السحرية. لننتقل إلى الواجهة الأمامية ونزيد عدد الطلبات في الثانية إلى 20.

يُرجى العِلم أنّ هذه العملية تستغرق بضع ثوانٍ، ولكنّنا نزيد من سرعتها، ويكون مُسبَق جميع التجزئات هو "bursty-".

ويرجع ذلك إلى أنّنا نأخذ عيّنات من Prometheus على نطاق 15s، ما يؤدّي إلى تأخّر وقت الاستجابة قليلاً. إذا أردنا نطاقًا أكثر دقة، يمكننا تغيير طلب البحث إلى prometheus ليكون 5s.

18. ما هي الخطوات التالية؟

التنظيف

لست بحاجة إلى تنظيف المساحة إذا كنت تستخدم حسابًا مؤقتًا تم توفيره لهذه الورشة.

يمكنك حذف مجموعات Kubernetes وقاعدة جدار الحماية والصور في GCR.

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

الخطوات المستقبلية