برو؟ ضربه شدید! با اپراتور پوسته آشنا شوید (بررسی و گزارش تصویری از KubeCon EU'2020)

امسال، کنفرانس اصلی Kubernetes اروپایی - KubeCon + CloudNativeCon Europe 2020 - مجازی بود. با این حال، چنین تغییری در قالب ما را از ارائه گزارشی که مدت‌ها برنامه‌ریزی کرده بودیم منع نکرد. ضربه شدید! Meet the Shell-operator» اختصاص داده شده به پروژه منبع باز ما اپراتور پوسته.

این مقاله با الهام از بحث، رویکردی برای ساده‌سازی فرآیند ایجاد اپراتورها برای Kubernetes ارائه می‌کند و نشان می‌دهد که چگونه می‌توانید با حداقل تلاش با استفاده از یک اپراتور پوسته، خودتان را بسازید.

برو؟ ضربه شدید! با اپراتور پوسته آشنا شوید (بررسی و گزارش تصویری از KubeCon EU'2020)

معرفی کردن ویدئوی گزارش (~23 دقیقه به زبان انگلیسی، به طور قابل توجهی آموزنده تر از مقاله) و عصاره اصلی آن به صورت متنی. برو!

در Flant ما دائماً همه چیز را بهینه و خودکار می کنیم. امروز در مورد یک مفهوم هیجان انگیز دیگر صحبت خواهیم کرد. ملاقات: اسکریپت نویسی پوسته بومی ابری!

با این حال، اجازه دهید با زمینه ای که در آن همه این اتفاق می افتد شروع کنیم: Kubernetes.

Kubernetes API و کنترلرها

API در Kubernetes را می توان به عنوان نوعی سرور فایل با دایرکتوری برای هر نوع شی نشان داد. اشیاء (منابع) در این سرور با فایل های YAML نشان داده می شوند. علاوه بر این، سرور دارای یک API اساسی است که به شما امکان می دهد سه کار را انجام دهید:

  • دریافت کنید منبع با نوع و نام آن؛
  • تغییر دادن منبع (در این مورد، سرور فقط اشیاء "درست" را ذخیره می کند - همه مواردی که به اشتباه شکل گرفته اند یا برای فهرست های دیگر در نظر گرفته شده اند دور ریخته می شوند).
  • مسیر برای منبع (در این مورد، کاربر بلافاصله نسخه فعلی/به روز شده خود را دریافت می کند).

بنابراین، Kubernetes به عنوان یک نوع سرور فایل (برای مانیفست های YAML) با سه روش اصلی عمل می کند (بله، در واقع روش های دیگری وجود دارد، اما فعلا آنها را حذف می کنیم).

برو؟ ضربه شدید! با اپراتور پوسته آشنا شوید (بررسی و گزارش تصویری از KubeCon EU'2020)

مشکل این است که سرور فقط می تواند اطلاعات را ذخیره کند. برای اینکه آن را به کار بگیرید نیاز دارید کنترل کننده - دومین مفهوم مهم و اساسی در دنیای Kubernetes.

دو نوع اصلی کنترل کننده وجود دارد. اولی اطلاعات را از Kubernetes می گیرد، طبق منطق تودرتو پردازش می کند و به K8s برمی گرداند. مورد دوم اطلاعات را از Kubernetes می گیرد، اما برخلاف نوع اول، وضعیت برخی از منابع خارجی را تغییر می دهد.

بیایید نگاهی دقیق تر به روند ایجاد استقرار در Kubernetes بیندازیم:

  • Deployment Controller (شامل در kube-controller-manager) اطلاعات مربوط به Deployment را دریافت می کند و ReplicaSet ایجاد می کند.
  • ReplicaSet بر اساس این اطلاعات دو کپی (دو پاد) ایجاد می کند، اما این پادها هنوز برنامه ریزی نشده اند.
  • زمان‌بند، غلاف‌ها را زمان‌بندی می‌کند و اطلاعات گره‌ها را به YAML‌های آن‌ها اضافه می‌کند.
  • Kubelets تغییراتی را در یک منبع خارجی ایجاد می کند (مثلاً Docker).

سپس کل این دنباله به ترتیب معکوس تکرار می شود: kubelet ظروف را بررسی می کند، وضعیت غلاف را محاسبه می کند و آن را پس می فرستد. کنترلر ReplicaSet وضعیت را دریافت کرده و وضعیت مجموعه replica را به روز می کند. همین اتفاق در مورد Deployment Controller می افتد و کاربر در نهایت وضعیت به روز شده (فعالی) را دریافت می کند.

برو؟ ضربه شدید! با اپراتور پوسته آشنا شوید (بررسی و گزارش تصویری از KubeCon EU'2020)

اپراتور پوسته

به نظر می رسد که Kubernetes بر اساس کار مشترک کنترلرهای مختلف است (اپراتورهای Kubernetes نیز کنترل کننده هستند). این سوال پیش می آید که چگونه می توان اپراتور خود را با حداقل تلاش ایجاد کرد؟ و در اینجا کسی که ما توسعه دادیم به کمک می آید اپراتور پوسته. این به مدیران سیستم اجازه می دهد تا با استفاده از روش های آشنا اظهارات خود را ایجاد کنند.

برو؟ ضربه شدید! با اپراتور پوسته آشنا شوید (بررسی و گزارش تصویری از KubeCon EU'2020)

مثال ساده: کپی کردن اسرار

بیایید به یک مثال ساده نگاه کنیم.

فرض کنید یک خوشه Kubernetes داریم. فضای نام دارد default با مقداری راز mysecret. علاوه بر این، فضاهای نام دیگری در خوشه وجود دارد. برخی از آنها برچسب خاصی به آنها چسبانده اند. هدف ما کپی کردن Secret در فضاهای نام با یک برچسب است.

این کار به دلیل این واقعیت پیچیده است که فضاهای نام جدیدی ممکن است در خوشه ظاهر شوند و برخی از آنها ممکن است این برچسب را داشته باشند. از طرف دیگر، زمانی که برچسب حذف می شود، Secret نیز باید حذف شود. علاوه بر این، خود Secret نیز می تواند تغییر کند: در این مورد، Secret جدید باید در تمام فضاهای نام دارای برچسب کپی شود. اگر Secret به طور تصادفی در هر فضای نامی حذف شود، اپراتور ما باید فورا آن را بازیابی کند.

اکنون که وظیفه فرموله شده است، زمان آن رسیده است که اجرای آن را با استفاده از عملگر پوسته آغاز کنیم. اما ابتدا ارزش گفتن چند کلمه در مورد خود اپراتور پوسته را دارد.

اپراتور پوسته چگونه کار می کند

مانند سایر بارهای کاری در Kubernetes، shell-operator در pod خود اجرا می شود. در این غلاف در دایرکتوری /hooks فایل های اجرایی ذخیره می شوند. اینها می توانند اسکریپت هایی در Bash، Python، Ruby و غیره باشند. ما به چنین فایل های اجرایی قلاب می گوییم (قلاب).

برو؟ ضربه شدید! با اپراتور پوسته آشنا شوید (بررسی و گزارش تصویری از KubeCon EU'2020)

Shell-operator در رویدادهای Kubernetes مشترک می شود و این قلاب ها را در پاسخ به رویدادهایی که ما نیاز داریم اجرا می کند.

برو؟ ضربه شدید! با اپراتور پوسته آشنا شوید (بررسی و گزارش تصویری از KubeCon EU'2020)

چگونه اپراتور پوسته می داند که کدام قلاب را و چه زمانی اجرا کند؟ نکته این است که هر قلاب دو مرحله دارد. در طول راه اندازی، اپراتور پوسته همه هوک ها را با آرگومان اجرا می کند --config این مرحله پیکربندی است. و پس از آن، قلاب ها به روش معمولی راه اندازی می شوند - در پاسخ به رویدادهایی که به آنها متصل شده اند. در مورد دوم، قلاب زمینه اتصال را دریافت می کند (زمینه الزام آور) - داده ها با فرمت JSON که در ادامه با جزئیات بیشتر در مورد آن صحبت خواهیم کرد.

ساخت اپراتور در Bash

اکنون آماده اجرا هستیم. برای انجام این کار، باید دو تابع بنویسیم (به هر حال، ما توصیه می کنیم کتابخانه shell_lib، که نوشتن قلاب ها را در Bash بسیار ساده می کند):

  • اولین مورد برای مرحله پیکربندی مورد نیاز است - زمینه اتصال را نشان می دهد.
  • دومی شامل منطق اصلی قلاب است.

#!/bin/bash

source /shell_lib.sh

function __config__() {
  cat << EOF
    configVersion: v1
    # BINDING CONFIGURATION
EOF
}

function __main__() {
  # THE LOGIC
}

hook::run "$@"

گام بعدی این است که تصمیم بگیریم به چه چیزهایی نیاز داریم. در مورد ما، باید پیگیری کنیم:

  • منبع مخفی برای تغییرات;
  • همه فضاهای نام در خوشه، به طوری که شما می دانید که کدام یک برچسب به آنها متصل است.
  • اسرار را هدف قرار دهید تا اطمینان حاصل کنید که همه آنها با راز منبع همگام هستند.

در منبع مخفی مشترک شوید

پیکربندی اتصال برای آن بسیار ساده است. ما نشان می دهیم که با نام به Secret علاقه مند هستیم mysecret در فضای نام default:

برو؟ ضربه شدید! با اپراتور پوسته آشنا شوید (بررسی و گزارش تصویری از KubeCon EU'2020)

function __config__() {
  cat << EOF
    configVersion: v1
    kubernetes:
    - name: src_secret
      apiVersion: v1
      kind: Secret
      nameSelector:
        matchNames:
        - mysecret
      namespace:
        nameSelector:
          matchNames: ["default"]
      group: main
EOF

در نتیجه، هنگامی که مخفی منبع تغییر کند، قلاب فعال می شود (src_secret) و متن الزام آور زیر را دریافت کنید:

برو؟ ضربه شدید! با اپراتور پوسته آشنا شوید (بررسی و گزارش تصویری از KubeCon EU'2020)

همانطور که می بینید، شامل نام و کل شی است.

پیگیری فضاهای نام

اکنون باید در فضای نام مشترک شوید. برای انجام این کار، پیکربندی binding زیر را مشخص می کنیم:

- name: namespaces
  group: main
  apiVersion: v1
  kind: Namespace
  jqFilter: |
    {
      namespace: .metadata.name,
      hasLabel: (
       .metadata.labels // {} |  
         contains({"secret": "yes"})
      )
    }
  group: main
  keepFullObjectsInMemory: false

همانطور که می بینید، یک فیلد جدید در پیکربندی با نام ظاهر شده است jqFilter. همانطور که از نامش پیداست، jqFilter تمام اطلاعات غیر ضروری را فیلتر می کند و یک شی JSON جدید با فیلدهای مورد علاقه ما ایجاد می کند. یک قلاب با پیکربندی مشابه، زمینه اتصال زیر را دریافت خواهد کرد:

برو؟ ضربه شدید! با اپراتور پوسته آشنا شوید (بررسی و گزارش تصویری از KubeCon EU'2020)

این شامل یک آرایه است filterResults برای هر فضای نام در خوشه. متغیر بولی hasLabel نشان می دهد که آیا یک برچسب به یک فضای نام معین متصل است یا خیر. انتخابگر keepFullObjectsInMemory: false نشان می دهد که نیازی به نگهداری اشیاء کامل در حافظه نیست.

ردیابی اسرار هدف

ما مشترک همه رازهایی هستیم که حاشیه نویسی مشخص شده است managed-secret: "yes" (اینها هدف ما هستند dst_secrets):

- name: dst_secrets
  apiVersion: v1
  kind: Secret
  labelSelector:
    matchLabels:
      managed-secret: "yes"
  jqFilter: |
    {
      "namespace":
        .metadata.namespace,
      "resourceVersion":
        .metadata.annotations.resourceVersion
    }
  group: main
  keepFullObjectsInMemory: false

در این مورد jqFilter تمام اطلاعات به جز فضای نام و پارامتر را فیلتر می کند resourceVersion. آخرین پارامتر هنگام ایجاد راز به حاشیه نویسی منتقل شد: به شما امکان می دهد نسخه های اسرار را مقایسه کنید و آنها را به روز نگه دارید.

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

برو؟ ضربه شدید! با اپراتور پوسته آشنا شوید (بررسی و گزارش تصویری از KubeCon EU'2020)

بر اساس همه این اطلاعات، می توان یک الگوریتم اساسی ایجاد کرد. روی همه فضاهای نام تکرار می شود و:

  • اگر hasLabel مسائل true برای فضای نام فعلی:
    • راز جهانی را با راز محلی مقایسه می کند:
      • اگر آنها یکسان باشند، هیچ کاری نمی کند.
      • اگر متفاوت باشند - اجرا می کند kubectl replace یا create;
  • اگر hasLabel مسائل false برای فضای نام فعلی:
    • اطمینان حاصل می کند که Secret در فضای نام داده شده نیست:
      • اگر Secret محلی وجود دارد، آن را با استفاده از آن حذف کنید kubectl delete;
      • اگر راز محلی شناسایی نشود، کاری انجام نمی دهد.

برو؟ ضربه شدید! با اپراتور پوسته آشنا شوید (بررسی و گزارش تصویری از KubeCon EU'2020)

پیاده سازی الگوریتم در Bash می توانید در ما دانلود کنید مخازن با مثال.

به این ترتیب توانستیم با استفاده از 35 خط پیکربندی YAML و تقریباً همان مقدار کد Bash، یک کنترلر ساده Kubernetes ایجاد کنیم! وظیفه اپراتور پوسته این است که آنها را به هم مرتبط کند.

با این حال، کپی کردن اسرار تنها حوزه کاربرد این ابزار نیست. در اینجا چند مثال دیگر برای نشان دادن توانایی او وجود دارد.

مثال 1: ایجاد تغییرات در ConfigMap

بیایید به یک Deployment متشکل از سه پاد نگاه کنیم. پادها از ConfigMap برای ذخیره برخی از تنظیمات استفاده می کنند. هنگامی که پادها راه اندازی شدند، ConfigMap در وضعیت خاصی قرار داشت (بیایید آن را v.1 بنامیم). بر این اساس، همه پادها از این نسخه خاص ConfigMap استفاده می کنند.

حال فرض می کنیم که ConfigMap تغییر کرده است (v.2). با این حال، پادها از نسخه قبلی ConfigMap (نسخه 1) استفاده خواهند کرد:

برو؟ ضربه شدید! با اپراتور پوسته آشنا شوید (بررسی و گزارش تصویری از KubeCon EU'2020)

چگونه می توانم آنها را وادار کنم که به ConfigMap (نسخه 2) جدید سوئیچ کنند؟ پاسخ ساده است: از یک الگو استفاده کنید. بیایید یک حاشیه نویسی چک جمع را به بخش اضافه کنیم template تنظیمات استقرار:

برو؟ ضربه شدید! با اپراتور پوسته آشنا شوید (بررسی و گزارش تصویری از KubeCon EU'2020)

در نتیجه این چک‌سام در همه پادها ثبت می‌شود و مانند Deployment خواهد بود. اکنون فقط باید هنگام تغییر ConfigMap، حاشیه نویسی را به روز کنید. و اپراتور پوسته در این مورد مفید است. تنها کاری که باید انجام دهید این است که برنامه ریزی کنید قلابی که در ConfigMap مشترک می شود و چک سام را به روز می کند.

اگر کاربر تغییراتی را در ConfigMap ایجاد کند، اپراتور پوسته متوجه آنها خواهد شد و جمع چک را مجدداً محاسبه می کند. پس از آن جادوی Kubernetes وارد بازی می شود: ارکستراتور غلاف را می کشد، یک مورد جدید ایجاد می کند، منتظر می ماند تا تبدیل شود. Ready، و به سراغ بعدی می رود. در نتیجه، Deployment همگام سازی می شود و به نسخه جدید ConfigMap سوئیچ می شود.

برو؟ ضربه شدید! با اپراتور پوسته آشنا شوید (بررسی و گزارش تصویری از KubeCon EU'2020)

مثال 2: کار با تعاریف منابع سفارشی

همانطور که می دانید، Kubernetes به شما اجازه می دهد تا انواع سفارشی اشیاء را ایجاد کنید. به عنوان مثال، شما می توانید نوع ایجاد کنید MysqlDatabase. فرض کنید این نوع دارای دو پارامتر فراداده است: name и namespace.

apiVersion: example.com/v1alpha1
kind: MysqlDatabase
metadata:
  name: foo
  namespace: bar

ما یک خوشه Kubernetes با فضای نام های مختلف داریم که در آن می توانیم پایگاه داده MySQL ایجاد کنیم. در این مورد می توان از shell-operator برای ردیابی منابع استفاده کرد MysqlDatabase، اتصال آنها به سرور MySQL و همگام سازی حالت های مورد نظر و مشاهده شده خوشه.

برو؟ ضربه شدید! با اپراتور پوسته آشنا شوید (بررسی و گزارش تصویری از KubeCon EU'2020)

مثال 3: مانیتورینگ شبکه خوشه ای

همانطور که می دانید استفاده از پینگ ساده ترین راه برای نظارت بر شبکه است. در این مثال نحوه اجرای چنین نظارتی را با استفاده از اپراتور پوسته نشان خواهیم داد.

اول از همه، شما باید در نودها مشترک شوید. اپراتور پوسته به نام و آدرس IP هر گره نیاز دارد. با کمک آنها، او این گره ها را پینگ می کند.

configVersion: v1
kubernetes:
- name: nodes
  apiVersion: v1
  kind: Node
  jqFilter: |
    {
      name: .metadata.name,
      ip: (
       .status.addresses[] |  
        select(.type == "InternalIP") |
        .address
      )
    }
  group: main
  keepFullObjectsInMemory: false
  executeHookOnEvent: []
schedule:
- name: every_minute
  group: main
  crontab: "* * * * *"

پارامتر executeHookOnEvent: [] از اجرای قلاب در پاسخ به هر رویدادی (یعنی در پاسخ به تغییر، افزودن، حذف گره ها) جلوگیری می کند. با این حال، او اجرا خواهد شد (و لیست گره ها را به روز کنید) برنامه ریزی شده است - هر دقیقه، همانطور که توسط میدان تجویز شده است schedule.

حال این سوال پیش می آید که دقیقاً چگونه از مشکلاتی مانند از دست دادن بسته مطلع شویم؟ بیایید نگاهی به کد بیندازیم:

function __main__() {
  for i in $(seq 0 "$(context::jq -r '(.snapshots.nodes | length) - 1')"); do
    node_name="$(context::jq -r '.snapshots.nodes['"$i"'].filterResult.name')"
    node_ip="$(context::jq -r '.snapshots.nodes['"$i"'].filterResult.ip')"
    packets_lost=0
    if ! ping -c 1 "$node_ip" -t 1 ; then
      packets_lost=1
    fi
    cat >> "$METRICS_PATH" <<END
      {
        "name": "node_packets_lost",
        "add": $packets_lost,
        "labels": {
          "node": "$node_name"
        }
      }
END
  done
}

ما لیست گره ها را تکرار می کنیم، نام و آدرس IP آنها را دریافت می کنیم، آنها را پینگ می کنیم و نتایج را برای Prometheus ارسال می کنیم. اپراتور شل می تواند معیارها را به Prometheus صادر کند، آنها را در فایلی که مطابق مسیر مشخص شده در متغیر محیطی قرار دارد ذخیره می کند $METRICS_PATH.

در اینجا چنین است می توانید یک اپراتور برای نظارت ساده شبکه در یک خوشه بسازید.

مکانیسم صف

این مقاله بدون توضیح مکانیسم مهم دیگری که در اپراتور پوسته تعبیه شده است ناقص خواهد بود. تصور کنید که نوعی قلاب را در پاسخ به یک رویداد در خوشه اجرا می کند.

  • اگر در همان زمان، اتفاقی در خوشه بیفتد، چه اتفاقی می‌افتد؟ یکی بیشتر رویداد؟
  • آیا اپراتور پوسته نمونه دیگری از هوک را اجرا خواهد کرد؟
  • اگر مثلاً پنج رویداد همزمان در خوشه اتفاق بیفتد چه؟
  • آیا اپراتور پوسته آنها را به صورت موازی پردازش خواهد کرد؟
  • منابع مصرف شده مانند حافظه و CPU چطور؟

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

بیایید این را با مثال هایی توضیح دهیم. فرض کنید دو قلاب داریم. اولین رویداد به قلاب اول می رود. هنگامی که پردازش آن کامل شد، صف به جلو حرکت می کند. سه رویداد بعدی به قلاب دوم هدایت می شوند - آنها از صف حذف می شوند و "در یک دسته" وارد آن می شوند. به این معنا که هوک مجموعه ای از رویدادها را دریافت می کند - یا به طور دقیق تر، مجموعه ای از زمینه های الزام آور.

همچنین اینها رویدادها را می توان در یک رویداد بزرگ ترکیب کرد. پارامتر مسئول این است group در پیکربندی صحافی

برو؟ ضربه شدید! با اپراتور پوسته آشنا شوید (بررسی و گزارش تصویری از KubeCon EU'2020)

شما می توانید هر تعداد صف/قلاب و ترکیبات مختلف آنها را ایجاد کنید. به عنوان مثال، یک صف می تواند با دو قلاب کار کند یا برعکس.

برو؟ ضربه شدید! با اپراتور پوسته آشنا شوید (بررسی و گزارش تصویری از KubeCon EU'2020)

تنها کاری که باید انجام دهید این است که فیلد را بر اساس آن پیکربندی کنید queue در پیکربندی صحافی اگر نام صف مشخص نشده باشد، قلاب در صف پیش فرض اجرا می شود (default). این مکانیسم صف به شما این امکان را می دهد که هنگام کار با قلاب ها، تمام مشکلات مدیریت منابع را به طور کامل حل کنید.

نتیجه

ما توضیح دادیم که یک shell-operator چیست، نشان دادیم که چگونه می توان از آن برای ایجاد سریع و بدون زحمت اپراتورهای Kubernetes استفاده کرد و چندین مثال از کاربرد آن ارائه دادیم.

اطلاعات دقیق در مورد اپراتور پوسته و همچنین آموزش سریع نحوه استفاده از آن در قسمت مربوطه موجود است. مخازن در GitHub. از تماس با ما در مورد سوالات دریغ نکنید: می توانید آنها را در یک ویژه بحث کنید گروه تلگرام (به روسی) یا به زبان این انجمن (به انگلیسی).

و اگر آن را دوست داشتید، ما همیشه خوشحالیم که مسائل/PR/ستاره های جدید را در GitHub می بینیم، جایی که، اتفاقا، می توانید دیگران را پیدا کنید پروژه های جالب. در میان آنها ارزش برجسته کردن را دارد اپراتور افزونه، که برادر بزرگ shell-operator است. این ابزار از نمودارهای Helm برای نصب افزونه‌ها استفاده می‌کند، می‌تواند به‌روزرسانی‌ها را ارائه کند و پارامترها/مقدارهای مختلف نمودار را نظارت کند، روند نصب نمودارها را کنترل کند، و همچنین می‌تواند آنها را در پاسخ به رویدادهای خوشه تغییر دهد.

برو؟ ضربه شدید! با اپراتور پوسته آشنا شوید (بررسی و گزارش تصویری از KubeCon EU'2020)

فیلم ها و اسلایدها

ویدئویی از اجرا (23 دقیقه):


ارائه گزارش:

PS

در وبلاگ ما نیز بخوانید:

منبع: www.habr.com

اضافه کردن نظر