بلاگ/راه‌اندازی Istio Service Mesh روی کلاستر Kubernetes خودمیزبان: مدیریت ترافیک و mTLS بین سرویس‌ها
به‌روز شده

راه‌اندازی Istio Service Mesh روی کلاستر Kubernetes خودمیزبان: مدیریت ترافیک و mTLS بین سرویس‌ها

1405/08/200 بازدید
راه‌اندازی Istio Service Mesh روی کلاستر Kubernetes خودمیزبان: مدیریت ترافیک و mTLS بین سرویس‌ها

اگر چند میکروسرویس روی یک کلاستر Kubernetes خودمیزبان دارید و دیگر نمی‌دانید کدام سرویس با کدام سرویس حرف می‌زند یا ترافیک بین‌شان رمزنگاری شده یا نه، وقتش است سراغ Istio بروید. در این مقاله راه‌اندازی Istio روی Kubernetes را قدم‌به‌قدم روی یک کلاستر خودمیزبان (self-hosted) انجام می‌دهیم: نصب با Helm، فعال کردن sidecar injection، تعریف Gateway و VirtualService برای مدیریت ترافیک، و فعال کردن mTLS خودکار بین سرویس‌ها.

نمودار سرویس‌مش Istio روی کلاستر Kubernetes خودمیزبان

سرویس‌مش چیست و Istio چه مشکلی را حل می‌کند

وقتی تعداد سرویس‌ها از چهار پنج تا بیشتر می‌شود، منطق ارتباط بین آن‌ها (retry، timeout، رمزنگاری، لاگ‌گیری از ترافیک) را نمی‌شود توی کد هر سرویس تکرار کرد. سرویس‌مش (service mesh) این منطق را از کد اپلیکیشن جدا می‌کند و می‌گذارد روی یک لایه شبکه‌ای کنار هر Pod.

Istio این کار را با یک پروکسی کوچک (Envoy) انجام می‌دهد که کنار هر Pod به‌عنوان sidecar اجرا می‌شود. تمام ترافیک ورودی و خروجی آن Pod از این پروکسی رد می‌شود، بدون این‌که کد سرویس چیزی از وجود Istio بداند. یک control plane به اسم istiod این پروکسی‌ها را کانفیگ می‌کند و گواهی‌های mTLS را صادر و تمدید می‌کند.

روی کلاستر خودمیزبان (یعنی کلاستری که خودتان با kubeadm یا ابزار مشابه روی سرور راه‌اندازی کرده‌اید، نه یک سرویس مدیریت‌شده) این کار اهمیت بیشتری دارد چون شبکه‌ی بین نودها را خودتان طراحی کرده‌اید و کسی جز شما مسئول امنیت ترافیک داخلی نیست.

پیش‌نیازها

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

  • یک کلاستر Kubernetes در حال اجرا (نسخه 1.28 به بالا کافی است) روی سرور مجازی یا سرور اختصاصی آریانت، با حداقل سه نود برای این‌که بشود ترافیک را بین Podهای مختلف پخش کرد. اگر هنوز کلاستر نساخته‌اید، راه‌اندازی کلاستر Kubernetes با kubeadm را قبل از این مقاله انجام دهید.
  • kubectl تنظیم‌شده و متصل به کلاستر.
  • helm نسخه 3.
  • حداقل 2 vCPU و 4 گیگ رم آزاد روی هر نود کارگر، چون هر sidecar یک Envoy اضافه اجرا می‌کند و مصرف رم کلاستر را بالا می‌برد. این عدد را با kubectl top nodes قبل و بعد از نصب چک کنید تا غافلگیر نشوید.
  • یک CNI که NetworkPolicy را پشتیبانی کند (Calico یا Cilium)، چون mTLS در سطح بالاتر کار می‌کند ولی بهتر است شبکه هم پایه‌ی درستی داشته باشد.

نصب Istio با Helm

Istio سه چارت اصلی دارد: base (CRDها)، istiod (control plane) و gateway (برای ترافیک ورودی). اگر با Helm کار نکرده‌اید، اول Helm چیست و چطور نصب برنامه‌ها را ساده می‌کند را بخوانید. چارت‌ها به ترتیب نصب می‌شوند:

helm repo add istio https://istio-release.storage.googleapis.com/charts
helm repo update

kubectl create namespace istio-system

helm install istio-base istio/base -n istio-system --set defaultRevision=default
helm install istiod istio/istiod -n istio-system --wait

بعد از چند ثانیه با این دستور وضعیت را چک کنید:

kubectl get pods -n istio-system

اگر Pod با نام istiod-... به وضعیت Running رسید، control plane آماده است. حالا Gateway ورودی را در namespace خودش نصب می‌کنیم تا مدیریت ترافیک ورودی از control plane جدا باشد:

kubectl create namespace istio-ingress
helm install istio-ingress istio/gateway -n istio-ingress

با kubectl get svc -n istio-ingress یک Service از نوع LoadBalancer یا NodePort می‌بینید. روی کلاستر خودمیزبان معمولا LoadBalancer واقعی ندارید، پس همان NodePort را با IP نود و پورتی که نشان می‌دهد باز کنید، یا با MetalLB یک IP شناور واقعی جلوی آن بگذارید.

فعال کردن Sidecar Injection

Istio فقط برای Podهایی که sidecar دارند کار می‌کند؛ باید به namespace بگویید کدام Podها این پروکسی را بگیرند. ساده‌ترین راه برچسب زدن روی namespace است:

kubectl label namespace default istio-injection=enabled

از این لحظه هر Pod جدیدی که در namespace default ساخته شود، یک کانتینر دوم به اسم istio-proxy کنار خودش دارد. Podهای موجود را باید دوباره راه‌اندازی کنید تا sidecar بگیرند:

kubectl rollout restart deployment -n default

با kubectl get pods -n default چک کنید که ستون READY به جای 1/1 حالا 2/2 نشان می‌دهد؛ عدد دوم همان istio-proxy است.

Gateway و VirtualService برای مدیریت ترافیک

Gateway مشخص می‌کند روی کدام پورت و پروتکل ترافیک وارد مش شود، VirtualService مشخص می‌کند آن ترافیک به کدام سرویس و با چه قانونی برسد. مثلا برای یک سرویس به اسم checkout:

apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
  name: checkout-gateway
  namespace: default
spec:
  selector:
    istio: ingress
  servers:
    - port:
        number: 80
        name: http
        protocol: HTTP
      hosts:
        - "checkout.example.com"
---
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: checkout-vs
  namespace: default
spec:
  hosts:
    - "checkout.example.com"
  gateways:
    - checkout-gateway
  http:
    - route:
        - destination:
            host: checkout
            subset: v1
          weight: 90
        - destination:
            host: checkout
            subset: v2
          weight: 10

این تنظیم 10 درصد ترافیک را به نسخه v2 سرویس می‌فرستد و 90 درصد را به v1 — یک canary release ساده، بدون این‌که یک خط کد در خود سرویس تغییر کند. وزن‌ها را هر بار کمی بیشتر کنید و لاگ خطای v2 را زیر نظر بگیرید؛ اگر چیزی غلط شد، کافی است وزن را برگردانید به صفر.

mTLS خودکار بین سرویس‌ها

تا این‌جا ترافیک مدیریت می‌شود ولی هنوز رمزنگاری نشده. Istio با یک منبع به اسم PeerAuthentication می‌تواند mTLS را بین تمام sidecarها اجباری کند، بدون این‌که کد سرویس‌ها گواهی یا کلیدی مدیریت کند — istiod خودش گواهی‌ها را صادر می‌کند و هر چند ساعت تمدید می‌کند:

apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
  name: default
  namespace: istio-system
spec:
  mtls:
    mode: STRICT

با mode: STRICT هر سرویسی که sidecar ندارد دیگر نمی‌تواند مستقیم با سرویس‌های داخل مش حرف بزند؛ این را قبل از اعمال روی کل کلاستر، اول روی یک namespace تست کنید تا سرویسی که هنوز sidecar نگرفته از کار نیفتد. برای چک کردن این‌که ارتباط رمزنگاری‌شده است:

istioctl proxy-config secret <pod-name> -n default

اگر گواهی معتبر با تاریخ انقضا برگرداند، یعنی آن Pod دارد از mTLS استفاده می‌کند.

اجازه ارتباط رمزنگاری‌شده بین سرویس‌های دارای sidecar در برابر مسدود شدن اتصال سرویس بدون mTLS

نصب و تنظیم این گواهی‌ها دستی هم شدنی است؛ اگر با مدیریت گواهی SSL در Kubernetes آشنا نیستید، راه‌اندازی cert-manager را هم ببینید که همین کار صدور و تمدید خودکار را برای ترافیک ورودی از بیرون کلاستر انجام می‌دهد.

مشاهده ترافیک با Kiali و Grafana

بدون دیدن نقشه‌ی ترافیک، نگه‌داری یک مش چند سرویسی سخت می‌شود. Istio یک سری addon آماده دارد:

kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.24/samples/addons/prometheus.yaml
kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.24/samples/addons/grafana.yaml
kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.24/samples/addons/kiali.yaml

بعد از بالا آمدن Podها:

istioctl dashboard kiali

Kiali نقشه زنده‌ی سرویس‌ها را نشان می‌دهد: کدام سرویس چقدر درخواست به کدام سرویس دیگر می‌فرستد، نرخ خطا چقدر است و کدام یال قفل سبز mTLS دارد. Grafana داشبوردهای آماده‌ای برای تاخیر و throughput هر سرویس دارد که می‌توانید بدون نوشتن یک کوئری هم اولین نگاه را بیندازید. اگر پیش از این مانیتورینگ سرور با Prometheus و Grafana را روی همین کلاستر راه انداخته‌اید، همان Grafana را برای داشبوردهای Istio هم می‌توانید استفاده کنید.

برای مقایسه، تفاوت وضعیت قبل و بعد از Istio را این‌طور می‌شود خلاصه کرد:

ویژگیبدون Istioبا Istio
رمزنگاری بین سرویس‌هاباید در کد هر سرویس پیاده‌سازی شودmTLS خودکار با PeerAuthentication
توزیع ترافیک canaryنیاز به تغییر کد یا ingress سفارشیوزن‌دهی در VirtualService، بدون تغییر کد
مشاهده نرخ خطا بین سرویس‌هاباید لاگ‌ها را از هر سرویس جمع کردنقشه زنده در Kiali
مصرف منابعکمترهر Pod یک sidecar اضافه دارد

ردیف آخر جدول را جدی بگیرید: روی یک کلاستر کوچک با نودهای کم‌حجم، اضافه شدن یک Envoy به هر Pod می‌تواند منابع آزاد را سریع تمام کند. قبل از فعال کردن sidecar injection روی همه namespaceها، یک بار با kubectl top pods مصرف رم را بعد از تزریق روی یک namespace تست چک کنید.

چه زمانی به Istio نیاز دارید

اگر کل اپلیکیشن‌تان دو سه سرویس است که مستقیم با هم صحبت می‌کنند، Istio بیشتر هزینه‌ی عملیاتی اضافه می‌کند تا فایده. جایی که به درد می‌خورد وقتی تعداد سرویس‌ها بالا می‌رود و دیگر کسی در تیم نمی‌داند دقیقا کدام سرویس به کدام سرویس وابسته است، یا وقتی باید canary release را بدون دست زدن به کد اجرا کنید.

برای این حجم کار، نودهای کلاستر باید منابع آزاد کافی داشته باشند؛ یک سرور مجازی یا سرور اختصاصی آریانت با IPv4 و IPv6 اختصاصی و شبکه‌ی پایدار بین نودها، بستر مناسبی برای یک کلاستر Kubernetes خودمیزبان با Istio فراهم می‌کند.

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

جمع‌بندی

Istio روی یک کلاستر Kubernetes خودمیزبان سه کار اصلی انجام می‌دهد: ترافیک بین سرویس‌ها را با Gateway و VirtualService قابل مدیریت می‌کند، با PeerAuthentication رمزنگاری mTLS را بدون تغییر کد اجباری می‌کند، و با Kiali نشان می‌دهد این ترافیک چه شکلی دارد. قبل از فعال کردن آن روی کل کلاستر، اول روی یک namespace تست کنید و مصرف منابع را با چشم خودتان ببینید.

سؤالات متداول

آیا Istio روی یک کلاستر تک‌نودی هم کار می‌کند؟ بله، control plane و sidecarها روی یک نود هم بالا می‌آیند، ولی چون هر Pod یک Envoy اضافه می‌گیرد، رم مصرفی بیشتر می‌شود. برای تست و یادگیری مشکلی نیست.

تفاوت istiod با ingress controller معمولی چیست؟ ingress controller فقط ترافیک ورودی از بیرون کلاستر را مدیریت می‌کند. istiod علاوه بر آن، ترافیک بین Podهای داخل کلاستر را هم کنترل و رمزنگاری می‌کند.

بعد از فعال کردن STRICT mTLS، سرویس‌های قدیمی از کار می‌افتند؟ اگر sidecar نگرفته باشند، بله ممکن است ارتباطشان قطع شود. به همین دلیل باید اول namespace به namespace تست کنید، نه یک‌باره روی کل کلاستر.

برای کلاستر Kubernetes خودمیزبان چه نوع سرور لازم است؟ به شبکه‌ی پایدار بین نودها و رم آزاد کافی برای sidecarهای اضافه نیاز دارید؛ یک سرور مجازی یا سرور اختصاصی با منابع قابل ارتقا برای این کار مناسب‌تر از یک سرور کوچک ثابت است.