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

1. مرحبًا

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

المتطلبات

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

ما ستتعلمه

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

2. الإعداد

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

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

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

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

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

انتقِل إلى Cloud Console وانقر على "تفعيل 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. بدء علامات تبويب جديدة: إذا كنت بحاجة إلى أكثر من طلب واحد في Terminal

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:

  • الذي يحمل الاسم "أساسي"،
  • في المنطقة 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 في مجموعتك (يجب أن تعرض الحالة "جاهز"):

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 في مجموعتك (يجب أن تعرض الحالة "جاهز"):

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 Pod التي تم نشرها. يتحكّم حاوية الخادم الوكيل في جميع عمليات الاتصال بالشبكة بين الخدمات المصغّرة بالتزامن مع سياسة عامة الغرض ومركز القياس عن بُعد ( 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.

عامل

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

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

للعلم، هذه هي الحِزم التي يستخدمها التطبيق.

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

Frontend

واجهتنا الأمامية هي أيضًا تطبيق 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) التي تم نشرها فيها، بينما سيحتوي مجموعة burst على تطبيق worker فقط.

في ما يلي مخطط بياني يصف المجموعتين. المربّعات المحدّدة باللون الأحمر هي "خدمات 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 Multicluster.

كتابة 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. Deploy Worker
kubectl apply -f worker-primary.yaml
  1. نشر خدمة العامل
kubectl apply -f worker-service.yaml

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

الاختبار

الانتظار إلى حين توفّر الحاويات على الإنترنت

kubectl get pods -w

بعد أن تصبح جميع الحاويات في حالة "قيد التشغيل"، اضغط على Ctrl + C.

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

ستلاحظ أنّنا لم نعرض الواجهة الأمامية من خلال LoadBalancer. ويرجع ذلك إلى أنّنا سنصل إلى التطبيق لاحقًا من خلال Istio. للتأكّد من أنّ كل شيء يعمل بشكل صحيح، سنستخدم kubectl port-forward. شغِّل الأمر التالي لإعادة توجيه المنفذ 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: انقر على الزر "معاينة الويب" (Web Preview) واختَر "معاينة على المنفذ 8080" (Preview on port 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 من خلال إدخال خدمة وكيل sidecar في كل عمليات النشر. يتم ذلك على أساس الاشتراك، لذا علينا تصنيف مساحة الاسم 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. تطبيق Worker VirtualService
kubectl apply -f worker-virtualservice.yaml

نشر التطبيق

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

لقد أمضينا الكثير من الوقت في إعداد ونشر مجموعة 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

لتثبيت 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.

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

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

نشر redis-service.yaml إلى مجموعة Burst

التغيير إلى ملف 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:

601e1155a825e0c2.png

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

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

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

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

b4a7a3cd67db05b3.png

بعد الاطّلاع على تفاصيل عملية النشر، انتقِل إلى "الإجراءات" (Actions) -> "عرض" (Expose).

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. إنشاء Cross Cluster Burst

الإعداد

توجيه كل الزيارات إلى خدمة العامل إلى المجموعة الأساسية

سنعتبر أنّ توجيه جميع الزيارات المتّجهة إلى 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 المتعدد المراحل هذا إلى تنزيل الإصدار 1.0.0 من Istio واستخراجه في المرحلة الأولى. في المرحلة الثانية، يتم نسخ كل شيء من الدليل إلى الصورة، ثم يتم نسخ 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. في هذه الحالة، يتم توجيه جميع الزيارات إلى مجموعة 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

المضي قدمًا