בדף הזה מוסבר איך לשנות משאבים באמצעות Policy Controller. זה שימושי למשל כשרוצים להגדיר ערכי ברירת מחדל. לדוגמה, יכול להיות שתרצו להוסיף תווית לכל המשאבים במרחב שמות ספציפי, או להגדיר את imagePullPolicy של Pod כ-Always כברירת מחדל אם הוא עדיין לא מוגדר.
הפעלת מוטציה
המסוף
כדי להפעיל את המוטציה, מבצעים את השלבים הבאים:
- במסוף Google Cloud , נכנסים לדף Policy בקטע Posture Management.
- בכרטיסייה Settings, בטבלת האשכולות, לוחצים על Edit edit בעמודה Edit configuration.
- מרחיבים את התפריט Edit Policy Controller configuration.
- מסמנים את התיבה Enable mutation 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 הם רשימות מסוג מפה, וצריך לציין את שדה המפתח של המפה. רשימות מסוג מפה הן מבנה של Kubernetes. פרטים נוספים עליהם זמינים במסמכי התיעוד של Kubernetes.
spec.parameters.assign.value הוא הערך שיוקצה ל-location.
השדה הזה לא מוגדר לפי סוג ויכול לקבל כל ערך, אבל חשוב לזכור ש-Kubernetes עדיין מאמת את הבקשה אחרי המוטציה, כך שהכנסת ערכים עם סכימה שגויה לאובייקט שמשתנה תגרום לדחיית הבקשה.
שימוש במוטציות למשרות
אם אתם מגדירים Job או CronJob, אתם צריכים לציין את הגרסה והקבוצה בנפרד, כמו בדוגמה הבאה:
applyTo:
- groups: ["batch"]
kind: ["Job"]
versions: ["v1"]
כשמשתמשים במוטטור למשימות, המשימות הקיימות לא משתנות אלא אם משנים אותן. שינוי של Job מפעיל בקשה ל-webhook לשינוי וגורם לשינוי שלו.
תהליך הביצוע
אולי הקונספט הכי חשוב שצריך להבין לגבי ה-webhooks של המוטציות ב-Kubernetes הוא מדיניות ההפעלה מחדש, כי הפלט של מוטציה אחת יכול לשנות את ההתנהגות של מוטציה אחרת. לדוגמה, אם מוסיפים קונטיינר משני חדש, למוטטור שמגדיר את מדיניות משיכת התמונות לכל הקונטיינרים יש עכשיו קונטיינר חדש למוטציה.
בפועל, המשמעות היא שעבור כל בקשה נתונה, יכול להיות שה-webhook לשינוי של Policy Controller ייקרא יותר מפעם אחת.
כדי לצמצם את זמן האחזור, Policy Controller מפעיל את עצמו מחדש כדי להימנע מבקשות HTTP נוספות. זה אומר שהחדרת ה-sidecar ומדיניות משיכת התמונות נותנות את התוצאה הצפויה.
שגרת המוטציה של Policy Controller תמשיך להפעיל את עצמה מחדש עד שהמשאב "יתכנס", כלומר לאיטרציות נוספות לא תהיה השפעה נוספת.
תחביר של מיקום
בתחביר המיקום נעשה שימוש ב-accessor של הנקודה (.) כדי לעבור בין שדות. ברשימות עם מפתחות, המשתמשים יכולים להפנות לאובייקטים ספציפיים ברשימה באמצעות התחביר [<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של מאגר הנתוניםfoo. spec.containers[name: *].volumeMounts[mountPath: "/my/mount"].readOnlyמפנה לשדהreadOnlyשל נקודת הטעינה/my/mountשל כל מאגרי הנתונים.
אם אתם מפנים למיקום שלא קיים כרגע במשאב, המיקום הזה נוצר כברירת מחדל. אפשר להגדיר את ההתנהגות הזו באמצעות בדיקת נתיב.
בדיקת נתיב
איך אפשר להגדיר ערך ברירת מחדל, כך שלא נשנה ערך שכבר קיים? יכול להיות שאנחנו רוצים להגדיר את /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 מפורטים כמה שיקולים חשובים לגבי שימוש ב-webhooks לשינוי. מכיוון ש-Policy Controller פועל כ-webhook לאישור של Kubernetes, העצה הזו רלוונטית גם כאן.
תחביר המוטציה של Policy Controller נועד להקל על הציות לבעיות תפעוליות שקשורות ל-webhooks של מוטציות, כולל אידמפוטנטיות.
מוטטורים לכתיבה
אטומיות
השיטה המומלצת היא להגדיר כל מוטטור כך שיהיה עצמאי ככל האפשר. מכיוון ש-Kubernetes הוא בעל מודל עקביות הדרגתי, מוטטור אחד לא צריך להסתמך על מוטטור שני כדי שיזהו אותו ויבצע את העבודה שלו כמו שצריך. לדוגמה, כשמוסיפים sidecar, צריך להוסיף את כל ה-sidecar, ולא לבנות אותו בחלקים באמצעות כמה מוטציות.
אימות
אם יש תנאי שאתם רוצים לאכוף, כדאי שלמוטטור תהיה מגבלה תואמת. כך אפשר לוודא שבקשות שמפירות את המדיניות יידחו, ושהפרות קיימות יזוהו בביקורת.
שחזור במקרה חירום
המוטציה מוטמעת כ-webhook למוטציות של Kubernetes. אפשר לעצור אותו באותו אופן כמו ה-validating webhook, אבל המשאב הרלוונטי הוא MutatingWebhookConfiguration שנקרא gatekeeper-mutating-webhook-configuration.
המאמרים הבאים
- הסברים ודוגמאות למוטציות של Gatekeeper מופיעים במסמכי התיעוד של קוד פתוח.