שינוי משאבים

בדף הזה מוסבר איך לשנות משאבים באמצעות Policy Controller. האפשרות הזו שימושית למקרים כמו הגדרת ערכי ברירת מחדל. לדוגמה, יכול להיות שתרצו להוסיף תווית לכל המשאבים במרחב שמות ספציפי, או להגדיר את ברירת המחדל של Pod ל-imagePullPolicy אם היא לא מוגדרת כבר.Always

הפעלת מוטציה

המסוף

כדי להפעיל מוטציה, מבצעים את השלבים הבאים:

  1. במסוף Google Cloud , עוברים לדף Policy בקטע Posture Management.

    למדיניות

  2. בכרטיסייה הגדרות, בטבלת האשכולות, לוחצים על עריכה בעמודה עריכת הגדרה.
  3. מרחיבים את התפריט Edit Policy Controller configuration (עריכת ההגדרה של Policy Controller).
  4. מסמנים את תיבת הסימון Enable mutation webhook (הפעלת webhook של שינוי).
  5. לוחצים על שמירת השינויים.

gcloud

כדי להפעיל מוטציה, מריצים את הפקודה הבאה:

gcloud container fleet policycontroller update \
    --memberships=MEMBERSHIP_NAME \
    --mutation

מחליפים את MEMBERSHIP_NAME בשם החברות של האשכול הרשום כדי להפעיל את השינוי. אפשר לציין כמה חברויות ולהפריד ביניהן באמצעות פסיקים.

הגדרות

  • mutator: משאב Kubernetes שעוזר להגדיר את התנהגות המוטציה של Policy Controller.
  • מערכת: סידור של כמה מוטטורים

דוגמה למוטציה

בדוגמה הבאה מוצג mutator שמגדיר את imagePullPolicy ל-Always בכל מאגרי התגים בכל ה-Pods:

# set-image-pull-policy.yaml

apiVersion: mutations.gatekeeper.sh/v1alpha1
kind: Assign
metadata:
  name: always-pull-image
spec:
  applyTo:
  - groups: [""]
    kinds: ["Pod"]
    versions: ["v1"]
  location: "spec.containers[name: *].imagePullPolicy"
  parameters:
    assign:
      value: "Always"

בדוגמה הזו, יש שדות מטא-נתונים רגילים של Kubernetes ‏(apiVersion, kind, metadata.name), אבל spec הוא המקום שבו מוגדר אופן הפעולה של המוטטור.

spec.applyTo קושר את ה-mutator למשאבים שצוינו. מכיוון שאנחנו משנים שדות ספציפיים באובייקט, אנחנו מגדירים באופן מרומז את הסכימה של האובייקט הזה. לדוגמה, אין היגיון בהחלת המוטטור הנוכחי על משאב Namespace. לכן, השדה הזה הוא חובה כדי ש-Policy Controller ידע לאילו סכימות המוטטור הזה רלוונטי. פריטים שחסרים ברשימה GroupVersionKinds לא משתנים.

spec.location מציין איזה שדה צריך לשנות. במקרה הזה, אנחנו משתמשים ב-glob ‏ (*) כדי לציין שאנחנו רוצים לשנות את כל הערכים ברשימת הקונטיינר. שימו לב שסוגי הרשימות היחידים שאפשר לעבור עליהם בשדה location הם רשימות מסוג מפה, וחובה לציין את שדה המפתח של המפה. רשימות מסוג מיפוי הן מבנה נתונים של Kubernetes. פרטים נוספים על התכונות האלה זמינים במסמכי התיעוד של Kubernetes.

spec.parameters.assign.value הוא הערך שיוקצה ל-location. השדה הזה לא מוגדר כסוג נתונים מסוים, והוא יכול לקבל כל ערך. עם זאת, חשוב לזכור ש-Kubernetes עדיין מאמת את הבקשה אחרי השינוי, ולכן אם מוסיפים ערכים עם סכימה שגויה לאובייקט שמשנים, הבקשה נדחית.

שימוש בפונקציות לשינוי מאפיינים של משרות

אם אתם מגדירים Job או CronJob, אתם צריכים לציין את הגרסה והקבוצה בנפרד, כמו בדוגמה הבאה:

applyTo:
- groups: ["batch"]
  kind: ["Job"]
  versions: ["v1"]

כשמשתמשים בשינוי מאפיינים של משרות, המשרות הקיימות לא משתנות אלא אם הן עוברות שינוי. שינוי של משימה מפעיל בקשה ל-webhook של מוטציה וגורם לשינוי שלה.

תהליך הביצוע

המושג הכי חשוב להבנה לגבי רכיבי webhook לשינוי ב-Kubernetes הוא מדיניות ההפעלה מחדש, כי הפלט של רכיב אחד לשינוי עשוי לשנות את ההתנהגות של רכיב אחר לשינוי. לדוגמה, אם מוסיפים קובץ sidecar חדש, למוטציה שמגדירה את מדיניות משיכת התמונות לכל הקונטיינרים יש עכשיו קונטיינר חדש לשינוי.

בפועל, המשמעות היא שייתכן ש-webhook של שינוי ב-Policy Controller ייקרא יותר מפעם אחת עבור כל בקשה נתונה.

כדי לצמצם את זמן האחזור, Policy Controller מפעיל את עצמו מחדש כדי להימנע מבקשות HTTP נוספות. המשמעות היא שהזרקת sidecar ומדיניות משיכת התמונות מניבות את התוצאה הצפויה.

שגרת המוטציה של Policy Controller תמשיך להפעיל את עצמה מחדש עד שהמשאב יתכנס, כלומר עד שאיטרציות נוספות לא ישפיעו יותר.

תחביר של מיקומים

תחביר המיקום משתמש בנקודה (.) כדי לעבור בין שדות. במקרה של רשימות עם מפתח, משתמשים יכולים להפנות לאובייקטים בודדים ברשימה באמצעות התחביר [<key>: <value>], או לכל האובייקטים ברשימה באמצעות [<key>: *].

אפשר להשתמש במירכאות יחידות (') או במירכאות כפולות (") כדי לציין ערכים ושדות. הפעולה הזו נדרשת אם יש תווים מיוחדים, כמו נקודות או רווחים.

במקרים של ערכים שמוקפים במירכאות, אפשר להוסיף לפני תווים מיוחדים את התו \ כדי לסמן אותם בתו בריחה (escape). ‫"Use \" to escape and \\\" to escape" הופך ל-Use " to escape and \" to escape.

כמה דוגמאות למשאב v1/Pod:

  • spec.priority הפניות spec.priority
  • spec.containers[name: "foo"].volumeMounts[mountPath: "/my/mount"].readOnly מפנה לשדה readOnly של /my/mount mount של מאגר foo.
  • spec.containers[name: *].volumeMounts[mountPath: "/my/mount"].readOnly references the readOnly field of the /my/mount mount of all containers.

אם מפנים למיקום שלא קיים כרגע במשאב, המיקום הזה נוצר כברירת מחדל. אפשר להגדיר את ההתנהגות הזו באמצעות בדיקת נתיב.

בדיקת נתיבים

איך אפשר לבצע הגדרת ברירת מחדל, כך שלא נשנה ערך שכבר קיים? יכול להיות שנרצה להגדיר את /secure-mount לקריאה בלבד לכל מאגרי התגים, אבל לא נרצה ליצור /secure-mount אם הוא לא קיים. אנחנו יכולים לבצע את הפעולות האלה באמצעות בדיקת נתיבים.

דוגמה שבה לא משנים את imagePullPolicy אם הוא כבר מוגדר:

# set-image-pull-policy.yaml

apiVersion: mutations.gatekeeper.sh/v1alpha1
kind: Assign
metadata:
  name: always-pull-image
spec:
  applyTo:
  - groups: [""]
    kinds: ["Pod"]
    versions: ["v1"]
  location: "spec.containers[name: *].imagePullPolicy"
  parameters:
    assign:
      value: "Always"
    pathTests:
    - subPath: "spec.containers[name: *].imagePullPolicy"
      condition: "MustNotExist"

עוד דוגמה שבה נמנעים מיצירת מאגר תגים ריק sidecar אם הוא לא קיים:

# set-image-pull-policy.yaml

apiVersion: mutations.gatekeeper.sh/v1alpha1
kind: Assign
metadata:
  name: always-pull-image
spec:
  applyTo:
  - groups: [""]
    kinds: ["Pod"]
    versions: ["v1"]
  location: 'spec.containers[name: "sidecar"].imagePullPolicy'
  parameters:
    assign:
      value: "Always"
    pathTests:
    - subPath: 'spec.containers[name: "sidecar"]'
      condition: "MustExist"

אפשר לציין כמה בדיקות של נתיבים, אם צריך.

subPath חייב להיות קידומת של location (או שווה לו).

הערכים החוקיים היחידים של condition הם MustExist ו-MustNotExist.

התאמה

בנוסף, מוטטורים מאפשרים התאמה באמצעות אותם קריטריונים כמו אילוצים.

פונקציות לשינוי ערכים

כרגע יש שני סוגים של משנים: Assign ו-AssignMetadata.

הקצאה

Assign יכול לשנות כל ערך מחוץ לשדה metadata של משאב. לכל GroupVersionKinds יש סכימה ייחודית, ולכן הוא חייב להיות קשור לקבוצה של GroupVersionKinds מסוימים.

הסכימה שלו היא:

apiVersion: mutations.gatekeeper.sh/v1alpha1
kind: Assign
metadata:
  name: always-pull-image
spec:
  applyTo:
  - groups: [""]
    kinds: ["Pod"]
    versions: ["v1"]
  match:
    kinds: # redundant because of `applyTo`, but left in for consistency
      - apiGroups: ["*"]
        kinds: ["*"]
    namespaces: ["my-namespace"]
    scope: "Namespaced" # or "Cluster"
    excludedNamespaces: ["some-other-ns"]
    labelSelector:
      matchLabels:
        mutate: "yes"
      matchExpressions:
      - key: "my-label"
        operator: "In" # or, "NotIn", "Exists", or "DoesNotExist"
        values: ["my-value"]
    namespaceSelector:
      matchLabels:
        mutate: "yes"
      matchExpressions:
      - key: "my-label"
        operator: "In" # or, "NotIn", "Exists", or "DoesNotExist"
        values: ["my-value"]
  location: "spec.containers[name: *].imagePullPolicy"
  parameters:
    pathTests:
    - subPath: 'spec.containers[name: "sidecar"]' # must be a prefix of `location`
      condition: "MustExist" # or "MustNotExist"
    - subPath: "spec.containers[name: *].imagePullPolicy"
      condition: "MustNotExist"
    assign:
      value: "Always" # any type can go here, not just a string

AssignMetadata

AssignMetadata יכול להוסיף תוויות חדשות של מטא-נתונים. אי אפשר לשנות את הערך של תוויות מטא-נתונים קיימות. אחרת, אפשר יהיה לכתוב מערכת של פונקציות לשינוי ערכים שתבצע קריאה רקורסיבית ללא הגבלה, וכתוצאה מכך הבקשות יגיעו לזמן קצוב לתפוגה.

מכיוון שלכל המשאבים יש את אותה סכימה של metadata, אין צורך לציין לאיזה משאב חל AssignMetadata.

בנוסף, מכיוון של-AssignMetadata אין הרשאה לבצע פעולות רבות, הסכימה שלו פשוטה יותר.

apiVersion: mutations.gatekeeper.sh/v1alpha1
kind: AssignMetadata
metadata:
  name: set-team-name
spec:
  match:
    kinds:
      - apiGroups: ["*"]
        kinds: ["*"]
    namespaces: ["my-namespace"]
    scope: "Namespaced" # or "Cluster"
    excludedNamespaces: ["some-other-ns"]
    labelSelector:
      matchLabels:
        mutate: "yes"
      matchExpressions:
      - key: "my-label"
        operator: "In" # or, "NotIn", "Exists", or "DoesNotExist"
        values: ["my-value"]
    namespaceSelector:
      matchLabels:
        mutate: "yes"
      matchExpressions:
      - key: "my-label"
        operator: "In" # or, "NotIn", "Exists", or "DoesNotExist"
        values: ["my-value"]
  location: "metadata.labels.team" # must start with `metadata.labels`
  parameters:
    assign:
      value: "Always" # any type can go here, not just a string

שיטות מומלצות

הערות לגבי Kubernetes

במסמכי התיעוד של Kubernetes מפורטים כמה שיקולים חשובים לגבי השימוש ב-webhook של שינוי. ההמלצה הזו רלוונטית גם כאן, כי Policy Controller פועל כ-webhook של Kubernetes admission.

תחביר המוטציה של Policy Controller נועד להקל על עמידה בדרישות תפעוליות בנוגע ל-webhooks של מוטציות, כולל אידמפוטנטיות.

כתיבת פונקציות לשינוי נתונים

אטומיות

מומלץ להפוך כל מוטטור לכמה שיותר עצמאי. מכיוון ש-Kubernetes היא עקבית בסופו של דבר, מוטציה אחת לא צריכה להסתמך על מוטציה שנייה כדי שהיא תזוהה ותבצע את העבודה שלה בצורה תקינה. לדוגמה, כשמוסיפים sidecar, מוסיפים את כל ה-sidecar, ולא בונים אותו בהדרגה באמצעות כמה פונקציות לשינוי.

אימות

אם יש תנאי שרוצים לאכוף, מומלץ שהמוטציה תכלול אילוץ תואם. כך אפשר לוודא שבקשות שמפירות את המדיניות יידחו, ושיימצאו הפרות קיימות בביקורת.

שחזור במקרה חירום

המוטציה מיושמת כ-webhook לשינוי ב-Kubernetes. אפשר להפסיק אותו באותו אופן כמו את ה-webhook לאימות, אבל המשאב הרלוונטי הוא MutatingWebhookConfiguration שנקרא gatekeeper-mutating-webhook-configuration.

המאמרים הבאים