استفاده از Istio Multicluster برای "Burst" بارهای کاری بین خوشه ها

۱. خوش آمدید

از اینکه در آزمایشگاه کد Istio Multi Cloud Burst گوگل به ما پیوستید متشکریم. این آزمایشگاه کد نیاز به تجربه عملی در سطح مبتدی با Kubernetes، Node و Go دارد.

آنچه شما نیاز خواهید داشت

  • حساب کاربری پلتفرم ابری گوگل (از حساب‌های کاربری موجود استفاده کنید، در غیر این صورت حساب‌های رایگان ارائه خواهیم داد)
  • روی لپ‌تاپ خود (برنامه‌های "kubectl"، "gcloud" و غیره را نصب کنید) یا می‌توانید از Google Cloud Shell استفاده کنید.

آنچه یاد خواهید گرفت

  • نحوه ایجاد یک کلاستر Kubernetes در GKE
  • نحوه نصب Istio روی خوشه Kubernetes با Helm
  • نحوه نصب Istio Multicluster با Helm
  • استقرار یک برنامه وب از منبع به Kubernetes
  • نوشتن و اعمال قوانین مسیریابی ترافیک به Istio
  • معیارهای پرومتئوس
  • ساخت و قرار دادن تصاویر کانتینر در داخل یک کلاستر Kubernetes

۲. راه‌اندازی

می‌توانید این آزمایشگاه کد را در هر یک از موارد زیر دنبال کنید:

  • پوسته ابری گوگل (توصیه می‌شود) : پوسته درون مرورگر، همراه با ابزارهای نصب‌شده
  • لپ‌تاپ شما (دستورالعمل‌های زیر را دنبال کنید)

با پلتفرم ابری گوگل شروع کنید

  1. اگر حساب GCP ندارید، کارت حساب کاربری رایگان خود را از مربی دریافت کنید.
  2. به کنسول گوگل کلود بروید و روی «انتخاب پروژه» کلیک کنید: 5c2d9bf74c78f7e4.png
  3. «شناسه» پروژه را جایی یادداشت کنید ، سپس برای انتخاب آن روی پروژه کلیک کنید: ecc5e8e97bfa6559.png

Cloud Shell یک پوسته خط فرمان در مرورگر شما فراهم می‌کند که ابزارهای مورد نیاز شما را نصب کرده و به طور خودکار به حساب Google Cloud Platform شما اعتبارسنجی می‌کند. (اگر نمی‌خواهید این تمرین را روی Cloud Shell اجرا کنید، به بخش بعدی بروید.)

به Cloud Console بروید و روی «Activate 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 را آسان‌تر کند:

۱. پوسته را در یک پنجره جدید جدا کنید:

۲. استفاده از ویرایشگر فایل: برای اجرای ویرایشگر فایل درون مرورگر، روی آیکون مداد در بالا سمت راست کلیک کنید. این گزینه برای شما مفید خواهد بود زیرا ما قطعه کدها را در فایل‌ها کپی خواهیم کرد.

۳. شروع تب‌های جدید: اگر به بیش از یک دستور ترمینال نیاز دارید.

۴. متن را بزرگتر کنید: اندازه فونت پیش‌فرض در Cloud Shell می‌تواند برای خواندن خیلی کوچک باشد.

Ctrl-+ در لینوکس/ Windows⌘-+ در macOS.

اگر در استفاده از محیط کاری خودتان نسبت به Cloud Shell احساس راحتی بیشتری می‌کنید، ابزارهای زیر را تنظیم کنید:

  1. نصب gcloud: (از قبل روی Cloud Shell نصب شده است.) دستورالعمل‌ها را برای نصب gcloud روی پلتفرم خود دنبال کنید. ما از این برای ایجاد یک کلاستر Kubernetes استفاده خواهیم کرد.
  2. نصب kubectl: (از قبل روی Cloud Shell نصب شده است.) دستور زیر را برای نصب اجرا کنید:
gcloud components install kubectl

دستور زیر را برای تأیید اعتبار gcloud اجرا کنید. از شما خواسته می‌شود که با حساب گوگل خود وارد شوید. سپس، پروژه از پیش ایجاد شده (که در بالا مشاهده می‌کنید) را به عنوان پروژه پیش‌فرض انتخاب کنید. (می‌توانید از پیکربندی منطقه محاسباتی صرف نظر کنید):

gcloud init
  1. نصب curl: روی اکثر سیستم‌های لینوکس/مک از قبل نصب شده است. احتمالاً شما آن را از قبل دارید. در غیر این صورت، نحوه نصب آن را در اینترنت جستجو کنید.
  2. نصب kubectx ****: با دانلود اسکریپت‌های bash از اینجا و قرار دادن آنها در مسیری در $PATH
  3. نصب helm ****: طبق این دستورالعمل‌ها عمل کنید.

۳. راه‌اندازی پروژه GCP

APIهای GKE (موتور کوبرنتیز گوگل)، GCR (ثبت کانتینر گوگل) و GCB (ساخت ابری گوگل) را در پروژه خود فعال کنید:

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)

۴. ایجاد خوشه "اولیه" Kubernetes

شما به راحتی می‌توانید با استفاده از موتور گوگل کوبرنتیز (GKE) خوشه مدیریت‌شده‌ی کوبرنتیز ایجاد کنید.

دستور زیر یک کلاستر Kubernetes ایجاد می‌کند:

  • به نام "اولیه"
  • در منطقه‌ی غرب آمریکا،
  • آخرین نسخه Kubernetes موجود است،
  • با ۴ گره اولیه
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

(این کار ممکن است حدود ۵ دقیقه طول بکشد. می‌توانید مراحل ایجاد کلاستر را در Cloud Console مشاهده کنید.)

پس از ایجاد کلاستر Kubernetes، gcloud kubectl با اعتبارنامه‌هایی که به کلاستر اشاره می‌کنند، پیکربندی می‌کند.

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

اکنون باید بتوانید kubectl با کلاستر جدید خود استفاده کنید.

دستور زیر را برای فهرست کردن گره‌های Kubernetes کلاستر خود اجرا کنید (آنها باید وضعیت "آماده" را نشان دهند):

kubectl get nodes

تغییر نام‌های Kubeconfig برای سهولت استفاده

ما مرتباً بین contextها جابجا خواهیم شد، بنابراین داشتن یک نام مستعار کوتاه برای خوشه‌هایمان مفید است.

این دستور، ورودی 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)

۵. ایجاد خوشه "burst"

دستور زیر یک کلاستر Kubernetes ایجاد می‌کند:

  • به نام "انفجار"
  • در منطقه‌ی غرب آمریکا،
  • آخرین نسخه Kubernetes موجود است،
  • با ۱ گره اولیه
  • قابلیت مقیاس‌بندی خودکار تا ۵ گره
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

(این کار ممکن است حدود ۵ دقیقه طول بکشد. می‌توانید مراحل ایجاد کلاستر را در 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)

۶. اعمال قوانین فایروال

برای اینکه دو کلاستر ما بتوانند با یکدیگر ارتباط برقرار کنند، باید یک قانون فایروال ایجاد کنیم.

دستورات زیر را اجرا کنید تا یک قانون فایروال در پلتفرم ابری گوگل ایجاد کنید که به خوشه‌های ما امکان برقراری ارتباط را می‌دهد.

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 خود را روی آنها مستقر کنیم!

۷. مقدمه‌ای بر ایستیو

ایستیو چیست؟

Istio یک صفحه کنترل مش سرویس است که هدف آن "اتصال، ایمن‌سازی، کنترل و مشاهده سرویس‌ها" است. این کار را به روش‌های مختلفی انجام می‌دهد، اما در درجه اول با قرار دادن یک کانتینر پروکسی ( Envoy ) در هر یک از Kubernetes Pod های مستقر شما. کانتینر پروکسی تمام ارتباطات شبکه بین میکروسرویس‌ها را به همراه یک خط مشی عمومی و هاب تله‌متری ( Mixer ) کنترل می‌کند.

a25613cd581825da.png

این سیاست‌ها می‌توانند مستقل از Kubernetes Deployments و Services شما اعمال شوند، به این معنی که اپراتور شبکه می‌تواند فعالیت شبکه را مشاهده کند، سیاست‌های شبکه را محدود، تغییر مسیر یا بازنویسی کند بدون اینکه برنامه‌های مرتبط را مجدداً مستقر کند.

برخی از ویژگی‌های مدیریت ترافیک Istio Supports عبارتند از:

  • قطع کننده مدار
  • تقسیم ترافیک مبتنی بر درصد
  • بازنویسی URL
  • خاتمه TLS
  • بررسی‌های بهداشتی
  • متعادل‌سازی بار

برای اهداف این کارگاه، ما بر تقسیم ترافیک مبتنی بر درصد تمرکز خواهیم کرد.

شرایط همکاری ما

خدمات مجازی

یک سرویس مجازی مجموعه‌ای از قوانین مسیریابی ترافیک را تعریف می‌کند که هنگام آدرس‌دهی به یک میزبان اعمال می‌شوند.

دروازه

یک دروازه (Gateway) یک متعادل‌کننده بار (load balancer) است که در لبه شبکه (mesh) عمل می‌کند و اتصالات HTTP/TCP ورودی یا خروجی را دریافت می‌کند. دروازه‌ها می‌توانند پورت‌ها، پیکربندی‌های SNI و غیره را مشخص کنند.

قانون مقصد

یک DestinationRule سیاست‌هایی را تعریف می‌کند که پس از مسیریابی، بر ترافیک در نظر گرفته شده برای یک سرویس اعمال می‌شوند. این سیاست‌ها پیکربندی مربوط به متعادل‌سازی بار، اندازه‌ی مخزن اتصال از محفظه‌ی کناری و تنظیمات تشخیص داده‌های پرت را مشخص می‌کنند.

چندخوشه‌ای ایستیو

شاید متوجه شده باشید که وقتی دو خوشه خود را ایجاد کردیم، خوشه primary ما ۴ گره بدون مقیاس‌بندی خودکار بود و خوشه burst ما ۱ گره با مقیاس‌بندی خودکار تا ۵ گره بود.

دو دلیل برای این پیکربندی وجود دارد.

ابتدا، می‌خواهیم سناریوی «رویداد آنلاین» به فضای ابری را شبیه‌سازی کنیم. در یک محیط روی رویداد آنلاین، به دلیل زیرساخت ثابت، به کلاسترهای مقیاس‌پذیر خودکار دسترسی ندارید.

دوم، حداقل الزامات برای اجرای Istio، راه‌اندازی ۴ گره (مطابق تعریف بالا) است. این سوال مطرح می‌شود: اگر Istio به حداقل ۴ گره نیاز دارد، خوشه burst ما چگونه می‌تواند Istio را با ۱ گره اجرا کند؟ پاسخ این است که Istio Multicluster مجموعه بسیار کوچک‌تری از سرویس‌های Istio را نصب می‌کند و با نصب Istio در خوشه اصلی ارتباط برقرار می‌کند تا قوانین سیاست را بازیابی کرده و اطلاعات تله‌متری را منتشر کند.

۸. بررسی اجمالی معماری برنامه

بررسی اجمالی اجزا

ما یک برنامه سه لایه را با استفاده از NodeJS و Redis مستقر خواهیم کرد.

کارگر

برنامه‌ی کارگر با استفاده از NodeJS نوشته شده است و به درخواست‌های ورودی POST HTTP گوش می‌دهد، عملیات هشینگ را روی آنها انجام می‌دهد و اگر متغیر محیطی به نام PREFIX تعریف شده باشد، آن مقدار را به هش اضافه می‌کند. پس از محاسبه‌ی هش، برنامه نتیجه را به کانال " calculation " در سرور Redis مشخص شده ارسال می‌کند.

ما بعداً از متغیر محیطی PREFIX برای نمایش عملکرد چندخوشه‌ای استفاده خواهیم کرد.

برای مرجع: اینها بسته‌هایی هستند که برنامه از آنها استفاده می‌کند.

  • body-parser: به ما اجازه می‌دهد درخواست‌های http خود را تجزیه و تحلیل کنیم.
  • cors: امکان استفاده از اشتراک‌گذاری منابع بین‌منبعی (Cross Origin Resource Sharing) را فراهم می‌کند.
  • dotenv: تجزیه آسان متغیرهای محیطی
  • express: میزبانی وب آسان
  • ioredis: کتابخانه کلاینت برای ارتباط با پایگاه‌های داده Redis
  • morgan: گزارش‌های ساختاریافته‌ی خوبی ارائه می‌دهد

ظاهر (فرانت‌اند)

فرانت‌اند ما نیز یک برنامه NodeJS است که با استفاده از اکسپرس، یک صفحه وب را میزبانی می‌کند. این برنامه فرکانس ورودی کاربر را دریافت کرده و درخواست‌ها را با همان نرخ به برنامه worker ما ارسال می‌کند. این برنامه همچنین در پیام‌های یک کانال Redis به نام " calculation " مشترک می‌شود و نتایج را در یک صفحه وب نمایش می‌دهد.

این برنامه از وابستگی‌های زیر استفاده می‌کند.

  • body-parser: به ما اجازه می‌دهد درخواست‌های http خود را تجزیه و تحلیل کنیم.
  • dotenv: تجزیه آسان متغیرهای محیطی
  • express: میزبانی وب آسان
  • ioredis: کتابخانه کلاینت برای ارتباط با پایگاه‌های داده Redis
  • morgan: لاگ‌های ساختاریافته‌ی خوبی ارائه می‌دهد
  • request: امکان ایجاد درخواست‌های HTTP را فراهم می‌کند.
  • socket.io: امکان ارتباط دو طرفه از صفحه وب به سرور را فراهم می‌کند.

این صفحه وب از بوت‌استرپ برای استایل‌دهی استفاده می‌کند و هنگام اجرا، به شکل زیر خواهد بود:

e5e3b9cbede4cac4.png

نمودار معماری

7ae4bc22a58f80a6.png

نمودار استقرار

ما برنامه نهایی خود را در دو کلاستری که ایجاد کرده‌ایم، مستقر خواهیم کرد. کلاستر primary تمام کامپوننت‌ها ( frontend ، worker و Redis) را در خود مستقر خواهد کرد، اما کلاستر burst فقط برنامه worker را مستقر خواهد کرد.

در اینجا نموداری وجود دارد که دو خوشه را توصیف می‌کند. کادرهای قرمز رنگ نشان‌دهنده سرویس‌های Kubernetes و کادرهای آبی رنگ نشان‌دهنده استقرارهای Kubernetes هستند. کادرهای زرد رنگ نشان‌دهنده نصب Istio هستند.

561db37c510944bd.png

توجه کنید که چگونه کلاستر burst هنوز سرویسی برای Redis مستقر در خود دارد، حتی با وجود اینکه هیچ Deployment برای Redis در کلاستر وجود ندارد. ما باید این سرویس را در کلاستر داشته باشیم تا Kubernetes DNS بتواند درخواست را حل کند، اما وقتی درخواست واقعاً ارسال می‌شود، Istio Proxy درخواست را به Redis مستقر در کلاستر primary دوباره مسیریابی می‌کند.

برنامه نهایی یک Deployment اضافی در کلاستر primary به نام istiowatcher. این همان چیزی است که به ما امکان می‌دهد وقتی ترافیک ما از یک آستانه مشخص عبور می‌کند، به صورت پویا و خودکار ترافیک را به burst تغییر مسیر دهیم.

8f6183bdfc3f813c.png

۹. ایجاد فایل‌های استقرار برنامه

برای استقرار برنامه خود باید مجموعه‌ای از مانیفست‌های Kubernetes ایجاد کنیم.

به دایرکتوری ریشه پروژه بروید و یک پوشه جدید به نام kubernetes ایجاد کنید.

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

نوشتن frontend.yaml

این کار هم Kubernetes Deployment و هم Service را برای دسترسی به تصویر frontend ما ایجاد می‌کند.

کد زیر را در 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 " قرار داده‌ایم و از ویژگی DNS داخلی Kubernetes برای حل مشکل سرویس حاصل استفاده خواهیم کرد.
  • ما آدرس REDIS_URL خود را روی " redis-cache-service:6379 " تنظیم کرده‌ایم و از ویژگی DNS داخلی Kubernetes برای حل آدرس‌های IP حاصل استفاده خواهیم کرد.
  • ما همچنین کاوشگرهای liveness و readiness برای کانتینر تنظیم کرده‌ایم تا به Kubernetes در مورد زمان راه‌اندازی و اجرای کانتینر اطلاع دهیم.

نوشتن worker-service.yaml

ما تعریف سرویس Kubernetes را در فایلی جداگانه از تعریف Deployment می‌نویسیم، زیرا قرار است از این سرویس در چندین خوشه دوباره استفاده کنیم، اما برای هر خوشه یک Deployment متفاوت خواهیم نوشت.

موارد زیر را در 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

ارتباط از worker ما به frontend از طریق کانال Redis است و به همین دلیل باید یک برنامه Redis را در کلاستر خود مستقر کنیم.

کد زیر را در redis.yaml وارد کنید:

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

این یک پیاده‌سازی نیمه‌استاندارد از یک برنامه Redis است. این یک کانتینر بر اساس تصویر redis:alpine ایجاد می‌کند، پورت‌های مناسب را در معرض نمایش قرار می‌دهد و محدودیت‌های منابع معقولی را تعیین می‌کند.

نوشتن redis-service.yaml

ما برای ارتباط با برنامه Redis خود به یک سرویس Kubernetes نیاز داریم.

موارد زیر را در redis-service.yaml وارد کنید

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

این سرویسی به نام redis-cache-service برای دسترسی به Redis Deployment ما فراهم می‌کند.

۱۰. استقرار برنامه

حالا که ایمیج‌هایمان به 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

وقتی همه پادها "در حال اجرا" شدند، 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

متوجه خواهید شد که ما frontend خود را از طریق 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 اجرا می‌کنید: روی دکمه‌ی «پیش‌نمایش وب» کلیک کنید و «پیش‌نمایش روی پورت ۸۰۸۰» را انتخاب کنید.

bdb5dc75f415be11.png

شما باید ظاهر برنامه را ببینید! و اگر عددی را در کادر "تعداد دفعات" وارد کنید، باید شاهد ظاهر شدن هشتگ‌ها باشید.

1caafaffab26897a.png

تبریک می‌گویم؛ همه چیز روبراه شد!

برای متوقف کردن فوروارد کردن پورت، Ctrl+C را فشار دهید.

۱۱. پاکسازی برنامه‌ی مستقر

ما قصد داریم Istio را روی کلاستر خود اعمال کنیم و سپس برنامه خود را مجدداً مستقر کنیم، بنابراین ابتدا برنامه فعلی خود را پاکسازی می‌کنیم.

دستورات زیر را اجرا کنید تا تمام Deploymentها و سرویس‌هایی که ایجاد کرده‌اید حذف شوند.

  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

۱۲. نصب Istio روی کلاستر اصلی

دریافت ایستیو

نسخه‌های Istio در GitHub میزبانی می‌شوند. دستورات زیر نسخه ۱.۰.۰ 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 Deployments and Services بتواند در آن اجرا شود.

kubectl create namespace istio-system

و در نهایت فایل istio-primary.yaml که با Helm ایجاد کردیم را اعمال کنید.

kubectl apply -f istio-primary.yaml

فضای نام پیش‌فرض برچسب

Istio با تزریق یک سرویس پروکسی sidecar به هر یک از Deployment های شما کار می‌کند. این کار به صورت اختیاری انجام می‌شود، بنابراین باید فضای نام default خود را با istio-injection=enabled برچسب‌گذاری کنیم تا Istio بتواند به طور خودکار sidecar را برای ما تزریق کند.

kubectl label namespace default istio-injection=enabled

تبریک! ما یک کلاستر راه‌اندازی شده و در حال اجرا با Istio داریم که آماده‌ی استقرار برنامه‌ی ماست!

۱۳. اپلیکیشن خود را با Istio Traffic Management مستقر کنید

ایجاد فایل‌های پیکربندی مدیریت ترافیک Istio

Istio مشابه Kubernetes عمل می‌کند، زیرا از فایل‌های yaml برای پیکربندی استفاده می‌کند. در همین راستا، ما باید مجموعه‌ای از فایل‌ها را ایجاد کنیم که به Istio بگویند چگونه ترافیک ما را افشا و مسیریابی کند.

یک دایرکتوری با نام istio-manifests ایجاد کنید و به آن بروید.

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

نوشتن frontend-gateway.yaml

این فایل، کلاستر Kubernetes ما را به شیوه‌ای مشابه GKE LoadBalancer در معرض نمایش قرار می‌دهد و تمام ترافیک ورودی را به سرویس frontend ما هدایت می‌کند.

یک فایل با نام 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 ما را اعمال کنید
kubectl apply -f redis-virtualservice.yaml
  1. خدمات مجازی کارگر ما را اعمال کنید
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 کنید تا ظاهر (frontend) را ببینید!

$ curl 35.199.158.10
<!doctype html>
<html>

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

۱۴. نصب Istio روی کلاستر "burst"

ما زمان زیادی را صرف راه‌اندازی و استقرار در کلاستر primary خود کرده‌ایم، اما یک کلاستر کامل دیگر هم برای استقرار در آن داریم!

در این بخش باید متغیرهای پیکربندی را در هر دو خوشه خود دریافت کنیم، بنابراین به دقت توجه کنید که برای هر دستور به کدام خوشه ارجاع داده شده‌ایم.

ایجاد مانیفست ریموت Istio

درست مانند زمانی که Istio را در کلاستر primary مستقر کردیم، قرار است از Helm برای الگوسازی استقرار istio remote در کلاستر 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 را برای "burst" اعمال کنید.

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 با یکدیگر ارتباط برقرار می‌کنیم. بیایید برنامه خود را به صورت Cross-Cluster مستقر کنیم.

۱۵. یک برنامه چندخوشه‌ای (Cross-Cluster Application) مستقر کنید

ایجاد استقرارها

به دایرکتوری kubernetes بروید

cd ${proj}/kubernetes

ایجاد استقرار کارگر برای خوشه "burst": 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-"

این یعنی worker ما در burst cluster، تمام hash هایی که ارسال می‌کند را با پیشوند " bursty- " شروع می‌کند. می‌توانیم از این موضوع برای اطمینان از اینکه برنامه ما واقعاً بین خوشه ای است، استفاده کنیم.

دومین تفاوت کلیدی این است که ما برچسب cluster-type را در این استقرار از primary-cluster به burst-cluster تغییر داده‌ایم.

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

ما بعداً هنگام به‌روزرسانی سرویس مجازی خود از این برچسب استفاده خواهیم کرد.

اصلاح سرویس‌های Istio

در حال حاضر سرویس‌های Istio ما از هر دو پیاده‌سازی ما استفاده نمی‌کنند. ۱۰۰٪ ترافیک ما به خوشه "اصلی" هدایت می‌شود. بیایید این را تغییر دهیم.

به دایرکتوری 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) ارجاع می‌دهد، اما ۵۰٪ از ترافیک به زیرمجموعه primary و ۵۰٪ دیگر به زیرمجموعه burst هدایت می‌شود.

ما زیرمجموعه primary را به عنوان استقرارهایی تعریف کرده‌ایم که برچسب cluster-type: primary-cluster دارند و زیرمجموعه burst را به عنوان استقرارهایی تعریف کرده‌ایم که برچسب cluster-type: burst-cluster دارند.

این به طور موثر ترافیک ما را به صورت ۵۰/۵۰ بین دو خوشه تقسیم می‌کند.

استقرار در خوشه

redis-service.yaml را در کلاستر burst مستقر کنید.

تغییر به burst kubeconfig

kubectx burst

به ریشه پروژه ما تغییر دهید

cd ${proj}

سپس مستقر شوید

redis-service.yaml را در کلاستر burst مستقر کنید.

kubectl apply -f kubernetes/redis-service.yaml

worker-burst.yaml را در کلاستر burst مستقر کنید.

kubectl apply -f kubernetes/worker-burst.yaml

worker-service.yaml را در کلاستر burst مستقر کنید.

kubectl apply -f kubernetes/worker-service.yaml

خدمات مجازی Istio را اعمال کنید

تغییر به kubeconfig primary

kubectx primary

سپس مستقر کنید

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

تأیید کنید که کار می‌کند

برای تأیید کارکرد آن، به نقطه Istio Ingress خود مراجعه کنید و توجه کنید که حدود ۵۰٪ از هش‌ها با پیشوند "burst-" شروع می‌شوند.

78fb6e235e9f4a07.png

این یعنی ما با موفقیت در حال صحبت در مورد خوشه‌های متقاطع هستیم! سعی کنید وزن‌ها را روی سرویس‌های مختلف تغییر دهید و فایل worker-virtualservice.yaml را اعمال کنید. این یک روش عالی برای متعادل کردن ترافیک بین خوشه‌ها است، اما اگر بتوانیم این کار را به صورت خودکار انجام دهیم چه؟

۱۶. استفاده از معیارهای پرومتئوس

مقدمه‌ای بر پرومتئوس

پرومتئوس ، یک جعبه ابزار متن‌باز برای نظارت و هشداردهی به سیستم‌های مختلف است که در ابتدا در ساوندکلاود ساخته شده است. این جعبه ابزار یک مدل داده چندبعدی با داده‌های سری زمانی دارد که با نام معیار و جفت‌های کلید/مقدار شناسایی می‌شوند.

برای مرجع، نمودار معماری پرومتئوس در اینجا آمده است:

601e1155a825e0c2.png

Istio، هنگامی که با Prometheus مستقر می‌شود، به طور خودکار معیارهای مختلفی را به سرور Prometheus گزارش می‌دهد. ما می‌توانیم از این معیارها برای مدیریت خوشه‌های خود در لحظه استفاده کنیم.

بررسی معیارهای پرومتئوس ما

برای شروع، باید Prometheus Deployment را نمایش دهیم.

به برگه Workloads در GKE بروید، و به حجم کار "prometheus" بروید.

b4a7a3cd67db05b3.png

پس از مشاهده جزئیات استقرار، به Actions -> Expose بروید.

c04a482e55bdfd41.png

پورت 9090 را برای ارسال انتخاب کنید و عبارت "Load balancer" را تایپ کنید.

d5af3ba22a7a6ebb.png

و "افشا" را انتخاب کنید

این یک سرویس روی یک آدرس IP عمومی ایجاد می‌کند که می‌توانیم از آن برای بررسی معیارهای پرومتئوس خود استفاده کنیم.

صبر کنید تا نقطه پایانی عملیاتی شود و پس از آن، روی آدرس IP کنار "نقاط پایانی خارجی" کلیک کنید. b1e40ad90851da29.png

حالا باید به رابط کاربری Prometheus نگاه کنید.

ed273552270337ec.png

پرومتئوس معیارهای کافی برای تبدیل شدن به یک کارگاه مستقل را ارائه می‌دهد. فعلاً، ما با بررسی معیار istio_requests_total شروع می‌کنیم.

اجرای آن کوئری مجموعه‌ای از داده‌ها را برمی‌گرداند. این داده‌ها، معیارهایی برای تمام درخواست‌هایی هستند که از شبکه سرویس Istio عبور می‌کنند و این مقدار زیادی است! ما عبارت خود را تغییر می‌دهیم تا آنچه را که واقعاً به آن علاقه‌مند هستیم، فیلتر کنیم:

درخواست‌هایی که سرویس مقصد آنها worker-service.default.svc.cluster.local و منبع آنها frontend-deployment است که به ۱۵ ثانیه آخر محدود شده است.

آن کوئری به این شکل است:

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)

۸۹۸۵۱۹۹۶۶۹۳۰ec۵۶.png

عالیه! ما می‌تونیم معیارهای دقیقی که نیاز داریم رو از پرومتئوس بگیریم.

آخرین سوال ما در مورد پرومتئوس

با تمام آموخته‌هایمان، آخرین سوالی که باید از پرومتئوس بپرسیم این است که

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

حالا می‌توانیم از HTTP API آنها برای دریافت معیار استفاده کنیم.

ما می‌توانیم با ارسال یک درخواست http GET مانند زیر، از api آنها با کوئری خودمان کوئری بگیریم. <prometheus-ip-here> را جایگزین کنید.

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

در اینجا یک پاسخ نمونه آورده شده است:

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

حالا می‌توانیم مقدار متریک خود را از JSON استخراج کنیم.

پاکسازی

ما باید سرویسی را که برای نمایش پرومتئوس استفاده کردیم، حذف کنیم. در کنسول ابری گوگل، به بالای سرویسی که ایجاد کرده‌ایم بروید و روی «حذف» کلیک کنید.

d58cb51b4c922751.png

مراحل بعدی:

پس از یافتن راهی برای کشف چگونگی و نرخ ترافیک در حال عبور از خوشه، گام بعدی ما نوشتن یک فایل باینری کوچک است که به صورت دوره‌ای از prometheus پرس‌وجو می‌کند و اگر تعداد درخواست‌ها در هر ثانیه به worker از یک آستانه مشخص بالاتر رفت، وزن‌های مقصد مختلفی را روی سرویس مجازی worker خود اعمال می‌کنیم تا تمام ترافیک را به burst cluster ارسال کند. به محض اینکه تعداد درخواست‌ها در هر ثانیه از یک آستانه پایین‌تر پایین‌تر آمد، تمام ترافیک را به primary ارسال می‌کنیم.

۱۷. یک انفجار خوشه متقاطع ایجاد کنید

راه‌اندازی

تمام ترافیک مربوط به worker-service را به کلاستر اصلی اختصاص دهید.

ما تمام ترافیکی که برای 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 ایجاد کنید و کد زیر را در آن قرار دهید.

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

این داکرفایل چند مرحله‌ای، نسخه ۱.۰.۰ Istio را در مرحله اول دانلود و استخراج می‌کند. مرحله دوم همه چیز را از دایرکتوری ما در ایمیج کپی می‌کند، سپس istioctl از مرحله ساخت به /usr/local/bin کپی می‌کند (تا بتواند توسط برنامه ما فراخوانی شود)، وابستگی‌ها را دریافت می‌کند، کد را کامپایل می‌کند و CMD روی " istiowatcher " تنظیم می‌کند.

نوشتن burst.yaml

این فایلی است که istiowatcher زمانی که تعداد درخواست‌ها در هر ثانیه به worker از frontend از ۱۵ بیشتر شود، اعمال خواهد کرد.

یک فایل جدید با نام 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 Could 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 وجود ندارد).

تماشا کنید که ترافیک به طور خودکار تغییر می‌کند!

حالا برای لحظه جادویی! بیایید به بخش فرانت‌اند خود برویم و req/second را به 20 افزایش دهیم.

توجه کنید که چند ثانیه طول می‌کشد، اما ما آن را به سرعت افزایش می‌دهیم و همه هش‌های ما پیشوند "bursty-" را دارند!

دلیل این امر این است که ما از پرومتئوس در محدوده 15s نمونه‌برداری می‌کنیم که باعث می‌شود زمان پاسخ ما کمی تأخیر داشته باشد. اگر می‌خواستیم محدوده بسیار محدودتری داشته باشیم، می‌توانستیم درخواست خود را به پرومتئوس 5s.

۱۸. قدم بعدی چیست؟

پاکسازی

اگر از حساب موقت ارائه شده برای این کارگاه استفاده می‌کنید، نیازی به پاکسازی ندارید.

می‌توانید کلاسترهای 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

رفتن به جلو