בדף הזה מוסבר איך לשנות משאבים באמצעות Policy Controller. האפשרות הזו שימושית למקרים כמו הגדרת ערכי ברירת מחדל. לדוגמה, יכול להיות שתרצו להוסיף תווית לכל המשאבים במרחב שמות ספציפי, או להגדיר את ברירת המחדל של Pod ל-imagePullPolicy ל-Always אם היא עדיין לא מוגדרת.
הפעלת מוטציה
המסוף
כדי להפעיל מוטציה, מבצעים את השלבים הבאים:
- נכנסים לדף Policy בקטע Posture Management במסוף Google Cloud .
- בכרטיסייה הגדרות, בטבלת האשכולות, לוחצים על עריכה edit בעמודה עריכת ההגדרה.
- מרחיבים את התפריט Edit Policy Controller configuration (עריכת ההגדרה של Policy Controller).
- מסמנים את התיבה Enable mutation webhook (הפעלת webhook של שינוי).
- לוחצים על שמירת השינויים.
gcloud
כדי להפעיל מוטציה, מריצים את הפקודה הבאה:
gcloud container fleet policycontroller update \
--memberships=MEMBERSHIP_NAME \
--mutation
מחליפים את MEMBERSHIP_NAME בשם החברות של האשכול הרשום שרוצים להפעיל בו מוטציה. אפשר לציין כמה חברויות ולהפריד ביניהן באמצעות פסיקים.
הגדרות
- mutator: משאב Kubernetes שעוזר להגדיר את התנהגות המוטציה של Policy Controller.
- מערכת: סידור של כמה מוטטורים
דוגמה למוטציה
בדוגמה הבאה מוצג מחולל שינויים שמגדיר את 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 קושר את המוטטור למשאבים שצוינו. מכיוון שאנחנו משנים שדות ספציפיים באובייקט, אנחנו מגדירים באופן מרומז את הסכימה של האובייקט הזה. לדוגמה, אין היגיון להחיל את הפונקציה הנוכחית לשינוי ערכים על משאב Namespace. לכן, השדה הזה הוא חובה כדי ש-Policy Controller ידע לאילו סכימות המוטטור הזה רלוונטי.
פריטים של GroupVersionKinds שלא מופיעים ברשימה הזו לא ישתנו.
spec.location מציין איזה שדה צריך לשנות. במקרה הזה, אנחנו משתמשים ב-glob (*) כדי לציין שאנחנו רוצים לשנות את כל הערכים ברשימת מאגרי התגים. שימו לב שסוגי הרשימות היחידים שאפשר לעבור עליהם בשדה location הם רשימות מסוג map, וחובה לציין את שדה המפתח של המפה. רשימות מסוג מיפוי הן מבנה נתונים של Kubernetes. פרטים נוספים על התכונות האלה זמינים במסמכי התיעוד של Kubernetes.
spec.parameters.assign.value הוא הערך שיוקצה ל-location.
השדה הזה לא מוגדר כסוג נתונים מסוים, והוא יכול לקבל כל ערך. עם זאת, חשוב לזכור ש-Kubernetes עדיין מאמת את הבקשה אחרי השינוי, ולכן אם מוסיפים ערכים עם סכימה שגויה לאובייקט שמשנים, הבקשה נדחית.
שימוש בפונקציות לשינוי מאפיינים של משרות
אם אתם מגדירים Job או CronJob, אתם צריכים לציין את הגרסה והקבוצה בנפרד, כמו בדוגמה הבאה:
applyTo:
- groups: ["batch"]
kind: ["Job"]
versions: ["v1"]
כשמשתמשים ב-mutator עבור Jobs, המערכת לא משנה את ה-Jobs הקיימים אלא אם הם משתנים. שינוי של משימה מפעיל בקשה ל-webhook של מוטציה וגורם לשינוי שלה.
תהליך הביצוע
המושג הכי חשוב להבנה לגבי רכיבי webhook לשינוי ב-Kubernetes הוא מדיניות ההפעלה מחדש שלהם, כי הפלט של רכיב אחד לשינוי עשוי לשנות את ההתנהגות של רכיב אחר לשינוי. לדוגמה, אם מוסיפים קונטיינר sidecar חדש, למוטטור שמגדיר את מדיניות משיכת התמונות לכל הקונטיינרים יש עכשיו קונטיינר חדש לשינוי.
בפועל, המשמעות היא שייתכן ש-webhook של שינוי ב-Policy Controller ייקרא יותר מפעם אחת עבור כל בקשה נתונה.
כדי לצמצם את זמן האחזור, Policy Controller מפעיל את עצמו מחדש כדי להימנע מבקשות HTTP נוספות. המשמעות היא שהזרקת sidecar ומדיניות משיכת התמונות מניבות את התוצאה הצפויה.
שגרת המוטציה של Policy Controller תמשיך להפעיל את עצמה מחדש עד שהמשאב יתכנס, כלומר עד שאיטרציות נוספות לא ישפיעו יותר.
תחביר של מיקומים
תחביר המיקום משתמש בנקודה (.) כדי לעבור בין שדות. במקרה של רשימות עם מפתח, משתמשים יכולים להפנות לאובייקטים בודדים ברשימה באמצעות התחביר [<key>: <value>], או לכל האובייקטים ברשימה באמצעות [<key>: *].
אפשר להוסיף מירכאות יחידות (') או כפולות (") לערכים ולשדות. הפעולה הזו נדרשת אם יש תווים מיוחדים, כמו נקודות או רווחים.
במקרים של ערכים שמוקפים במירכאות, אפשר להוסיף תו בריחה לתווים מיוחדים על ידי הוספת \ לפני התווים. "Use \" to escape and \\\" to escape" הופך ל-Use " to escape and \" to escape.
כמה דוגמאות למשאב v1/Pod:
spec.priorityreferencesspec.priority-
spec.containers[name: "foo"].volumeMounts[mountPath: "/my/mount"].readOnlyמפנה לשדהreadOnlyשל/my/mountmount של מאגרfooהנתונים. -
spec.containers[name: *].volumeMounts[mountPath: "/my/mount"].readOnlyreferences thereadOnlyfield of the/my/mountmount of all containers.
אם מפנים למיקום שלא קיים כרגע במשאב, המיקום הזה נוצר כברירת מחדל. אפשר להגדיר את ההתנהגות הזו באמצעות בדיקת נתיב.
בדיקת נתיבים
איך אפשר לבצע הגדרת ברירת מחדל, כך שלא נשנה ערך שכבר קיים? יכול להיות שנרצה להגדיר את /secure-mount לקריאה בלבד לכל מאגרי התגים, אבל לא נרצה ליצור /secure-mount אם הוא לא קיים. אנחנו יכולים לבצע את הפעולות האלה באמצעות בדיקת נתיבים.
דוגמה שבה לא משנים את imagePullPolicy אם הוא כבר מוגדר: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.
תחביר המוטציה של Policy Controller נועד להקל על עמידה בדרישות תפעוליות בנוגע ל-webhooks של מוטציות, כולל אידמפוטנטיות.
כתיבת פונקציות לשינוי ערכים
אטומיות
השיטה המומלצת היא להפוך כל מוטציה לכמה שיותר עצמאית. מכיוון ש-Kubernetes היא עקבית בסופו של דבר, מוטציה אחת לא צריכה להסתמך על מוטציה שנייה כדי שהיא תזוהה ותבצע את העבודה שלה בצורה תקינה. לדוגמה, כשמוסיפים sidecar, מוסיפים את כל ה-sidecar, ולא בונים אותו בהדרגה באמצעות כמה פונקציות לשינוי.
אימות
אם יש תנאי שרוצים לאכוף, מומלץ להגדיר אילוץ תואם ל-mutator. כך אפשר לוודא שבקשות שמפירות את המדיניות יידחו, ושיימצאו הפרות קיימות בביקורת.
שחזור במקרה חירום
המוטציה מיושמת כ-webhook לשינוי ב-Kubernetes. אפשר להפסיק אותו באותו אופן כמו את ה-webhook לאימות, אבל המשאב הרלוונטי הוא MutatingWebhookConfiguration שנקרא gatekeeper-mutating-webhook-configuration.
המאמרים הבאים
- הסברים ודוגמאות לגבי מוטציות של Gatekeeper מופיעים במסמכי הקוד הפתוח.