למנוע תקלות ידועות מראש
בדיקת grep לפני כל push של values
Section titled “בדיקת grep לפני כל push של values”grep -rn 'repoURL: ""' <FILE>grep -rnE 'mountPath: *"?/run/secrets/?"?([^A-Za-z0-9_/-]|$)' <DIR>grep -nP ' +$' <FILE>איך מוודאים: כל שלוש הפקודות לא מדפיסות כלום. השנייה תופסת mountPath שהוא /run/secrets עצמו (חייב להיות /run/secrets/<APP_DIR>).
תחנה 1: repos, tokens, variables, runner
Section titled “תחנה 1: repos, tokens, variables, runner”| מלכודת | מניעה זולה | מתי | תסמין אם מדלגים | קישור |
|---|---|---|---|---|
| group: display name במקום path (תרגול) | להעתיק את ה-URL מכפתור Code; git ls-remote git@<GITLAB_HOST>:<GROUP>/<REPO>.git | ביצירת repos, לפני ה-variable הראשון | clone ו-include נשברים | overview |
TRIDENT_GROUP ריק או Masked (תרגול) | group variable רגיל עם ה-path | לפני ה-pipeline הראשון | /trident-ci not found | variables |
Protect ON על variable של ה-token הכותב, ו-dev לא מוגן (תרגול) | Mask ON, Protect OFF; להריץ job var-check | ביצירת ה-variable | promote:dev מקבל 403, ה-token ריק | variables |
| מניחים project/group access tokens (תרגול) | service account; PAT כ-fallback | לפני יצירת tokens | אין כפתור ב-Free | identities |
| role ו-scope מתערבבים (תרגול) | לרשום לכל token: role (Members) ו-scope (יצירה) בנפרד | ביצירת כל token | 403 למרות token תקין; "נשנה scope ל-Reporter" | permissions |
| bot לא member ב-repo אחד (תרגול) | git ls-remote עם ה-credential של כל bot, על כל repo | מיד אחרי ההזמנה | HTTP Basic: Access denied על repo אחד; "ניתן Developer" הוא הכיוון הלא נכון | permissions |
| deploy token מחפשים ב-Access tokens (תרגול) | Settings → Repository → Deploy tokens | ביצירת token למשיכה | "אין לי איפה ליצור" | identities |
Welcome to GitLab נחשב הרשאה (תרגול) | אחרי ssh -T, גם git ls-remote על ה-repo | ב-VM חדשה | project could not be found or you don't have permission | ssh |
| runner: אין group runner ב-Free; scope ו-tag (תרגול) | project runner; לפתוח נעילה ולהפעיל לכל project שמריץ jobs | לפני ה-pipeline הראשון בכל repo | pending, no runner for tags trident | runners |
sudo gitlab-runner run אחרי register (תרגול) | רק register; sudo gitlab-runner list | ביצירת ה-runner | instance שני בחזית | runners |
git push דוחף branch אחד, ו-tags בכלל לא (תרגול) | git push -u origin poc dev ו-git push origin <TAG>; git ls-remote --heads origin | אחרי יצירת branches ו-tags | CI ו-Argo לא רואים branch; targetRevision: <TAG> לא נמצא | branches |
| משתני bootstrap הוגדרו כ-CI variables (תרגול) | export באותו shell ובכל טרמינל חדש; echo "${VAR_NAME:-EMPTY}" | לפני script bootstrap | !URL_VAR: export …, הערך ריק | variables |
gitlab-runner לא בקבוצת docker [COURSE] | sudo -u gitlab-runner docker info --format '{{.ServerVersion}}' | אחרי register | permission denied while trying to connect to the docker API | runners |
תחנה 2: build ו-publish ל-GitLab Container Registry
Section titled “תחנה 2: build ו-publish ל-GitLab Container Registry”| מלכודת | מניעה זולה | מתי | תסמין אם מדלגים | קישור |
|---|---|---|---|---|
build context הוא services/<SERVICE> (תרגול) | -f services/<SERVICE>/Dockerfile services/: ה-context מכיל כל COPY | בכתיבת ה-build | "/common/trident": not found | docker |
דגלי build בתוך docker push (תרגול) | docker push <IMAGE>:<CANDIDATE> בלבד | בהעתקה מ-build | push נכשל | ci/patterns |
for על כמה פריטי - (תרגול) | בלוק אחד עם - |; Pipeline editor → Validate | בכתיבת jobs | syntax error, כל פריט ב-shell משלו | ci/patterns |
| הנחה ש-artifacts מעבירים images (תרגול) | shell executor = daemon אחד; artifacts = קבצים | בחלוקה ל-build ו-publish | עם docker executor ה-image חסר ב-job הבא | ci/patterns |
| "ה-CI דוחף images ל-gitops" (תרגול) | <IMAGE> = $CI_REGISTRY_IMAGE/<SERVICE>; ל-gitops מגיע רק ה-tag | בהגדרת IMAGE_REPO | path שגוי, image לא נמצא | registry |
פירוק שגוי של שם image, api:8f3a2c1 (תרגול) | server עד ה-/ הראשון, tag אחרי ה-: האחרון, שם ה-service ב-path | בכתיבת ה-ref | manifest unknown | docker |
| DNS ב-build (תרגול) | cat /etc/docker/daemon.json ו-cat /etc/resolv.conf; להחיל מראש אם יש 127.0.0.53 | לפני ה-build הראשון | Temporary failure in name resolution | docker |
| containerd snapshotter של Docker 29 (תרגול) | docker info --format '{{.Driver}}' מצופה overlay2 | לפני ה-push הראשון | blob unknown to registry ב-push השני | docker |
| החלפת store מרוקנת את ה-images (תרגול) | אחרי שינוי daemon.json: להריץ את כל ה-pipeline (commit ריק), לא retry | אחרי systemctl restart docker | publish לא מוצא image | docker |
אין docker login לפני push [COURSE] | --password-stdin עם CI_REGISTRY_*, לפני ה-push | בכתיבת publish | denied: access forbidden | ci/patterns |
latest או retag [COURSE] | תמיד <CANDIDATE> | תמיד | אי אפשר להוכיח איזו גרסה רצה | registry |
תחנה 3: promote
Section titled “תחנה 3: promote”| מלכודת | מניעה זולה | מתי | תסמין אם מדלגים | קישור |
|---|---|---|---|---|
| clone מקומי של gitops מאחור (תרגול) | git pull לפני כל עריכה | לפני כל עריכה | "שינוי ה-CI לא אצלי"; rejected (non-fast-forward) | sync |
--force כדי לעקוף דחייה (תרגול) | git pull, ואז push; אף פעם לא --force | כשנדחה push | נמחק ה-commit של ה-CI, ה-tag חוזר ל-"", ה-render נשבר | sync |
| commit מקומי תועה שלא נדחף (תרגול) | git status -sb לפני merge; ב-reset הארגומנט הוא היעד | לפני merge ל-main | fatal: Not possible to fast-forward | sync |
promote.sh בלי cd ל-clone (תרגול) | לקרוא את הסקריפט שורה-שורה: מי רץ ואיפה | בהרכבת הסקריפט | commit ו-push בתיקייה הלא נכונה | templates |
git diff --cached --quiet ו-set -e (תרגול) | if ! git diff --cached --quiet; then …; fi; exit 0 = אין שינוי | בכתיבת ה-push | הסקריפט מת בדיוק כשיש שינוי | ci/patterns |
קוראים run ישן, main-20260928… (תרגול) | לבדוק ב-CANDIDATE את ה-branch והתאריך | לפני קריאת log | מתקנים את ה-pipeline הלא נכון | ci/overview |
promote:prod ב-pipeline ישן (תרגול) | ללחוץ רק ב-pipeline ש-staging שלו אומת | לפני prod | prod מקבל candidate ישן | ci/patterns |
403 מוסבר בסיבה אחת (תרגול) | להרחיב: token פג או בוטל, Protect עם token ריק, URL, token ב-project שגוי | כשיש 403 | נשארים בקטגוריה אחת | permissions |
[skip ci] ב-repo עם CI [COURSE] | להוסיף להודעת ה-commit של ה-promote | בכתיבת ה-push | לולאת pipelines | ci/patterns |
CI_JOB_TOKEN דוחף ל-repo אחר [COURSE] | service account token | בבחירת ה-credential | 403 | ci/overview |
תחנה 4: values ו-render של ה-chart
Section titled “תחנה 4: values ו-render של ה-chart”| מלכודת | מניעה זולה | מתי | תסמין אם מדלגים | קישור |
|---|---|---|---|---|
-f לוקח את המילה הבאה (תרגול) | helm template <RELEASE> <CHART_DIR> -n <NS> -f <FILE> …: chart ראשון, -f לכל קובץ | בכל render | Error: open … או non-absolute URLs should be in form of repo_name/path_to_chart | helm/overview |
| values בתיקייה הלא נכונה (תרגול) | ls על כל נתיב ב--f; ה-values ב-clone של gitops | בכל render | no such file or directory = נתיב, לא YAML | helm/overview |
$values ב-bash, ו-. תועה (תרגול) | נתיבים אמיתיים; $values קיים רק ב-Application | בכל render מקומי | $values ריק; invalid release name | helm/overview |
defaultImageTag ריק (תרגול) | --set defaultImageTag=probe לבידוד; לעשות promote ל-env | ב-render של env שלא קודם | YAML parse error … line N, השורה ב-render | helm/overview |
רווח חסר אחרי : (תרגול) | render אחרי כל עריכה | בכל עריכת values | mountPath:/run/secrets/<APP_DIR> הוא scalar; השגיאה בשורה הבאה | helm/overview |
מפתח מחוץ ל-generic: ב-col 0, או Secret במקום secret (תרגול) | grep -c 'image: /' "$R" מצופה 0 | אחרי כל render | image: /ingest-api:… או מפתח שמתעלמים ממנו | helm/values |
storage.className נשכח, אף שהערת הקובץ מונה אותו (תרגול) | לעבור על רשימת המפתחות מההערה, מפתח-מפתח; grep -c 'storageClassName: course-local-path' "$R" מצופה 1 | בכתיבה הראשונה של postgres | postgres-0 Pending, unbound immediate PersistentVolumeClaims | values |
| StorageClass default בקלאסטר כתיקון (תרגול) | ה-class נקבע ב-values, ב-Git | כשה-PVC Pending | לא ב-Git, ויקשור גם PVC לא קשורים | storage-probes |
volumeClaimTemplates של StatefulSet immutable (תרגול) | לתקן ב-Git לפני ה-sync הראשון: render, grep ורק אז push | לפני ה-create הראשון של postgres | StatefulSet.apps "postgres" is invalid … Forbidden | storage-probes |
Secret מאונט ב-/run/secrets עצמו (תרגול) | mountPath: /run/secrets/<APP_DIR>, ו-env מצביע על הקובץ; ה-grep שבראש העמוד | לפני push | StartError … read-only file system | secrets |
hostname ב-Ingress מופיע פעמיים (תרגול) | לשנות גם hosts[].hostname וגם extraTls[].hosts | בכל env | cert שגוי או 404 | values |
envConfigmaps לא לפי ה-CONTRACT (תרגול) | לכל service: טבלת audience מול הרשימה | בכתיבת base.yaml | service מקבל config שלא מיועד לו | compose-to-k8s |
required לא נתפס ב-lint (תרגול) | helm template על ה-values של כל env, לא helm lint | בבדיקת CI | lint ירוק, render נכשל | testing |
רשימה מוחלפת ולא ממוזגת [COURSE] | בדריסת list להעתיק את כל האיברים; grep -c livenessProbe "$R" | בדריסת containers או extraVolumes | probes נעלמים | helm/overview |
תחנה 5: bootstrap של Secrets
Section titled “תחנה 5: bootstrap של Secrets”| מלכודת | מניעה זולה | מתי | תסמין אם מדלגים | קישור |
|---|---|---|---|---|
| Secret רק ב-namespace אחד (תרגול, פעמיים) | לולאה על כל ה-environments, ואז kubectl -n <NS> get secret <SECRET> לכל <NS> | אחרי כל הרצת prepare-environment.sh | ImagePullBackOff ב-staging, not found ב-prod | snippets |
--docker-server עם https:// או נתיב (תרגול) | --docker-server=registry.gitlab.com: מארח בלבד | ביצירת ה-Secret | pull נכשל | secrets |
| deploy token לא נבדק (תרגול) | jwt/auth מחזיר 200 לפני שיוצרים Secret | אחרי יצירת ה-token | ImagePullBackOff, ו-401 בבדיקה | registry |
קובץ credential: שם שגוי, 664, token = username (תרגול) | לקרוא את ה-header של הסקריפט; ls -l <FILE>, wc -c <FILE>, cmp -s <FILE> <FILE>; תיקיות 700 | לפני הרצת script | credential file is not readable and non-empty | templates |
| newline בסוף ה-token (תרגול) | printf %s, read -rs, umask 077 | בכתיבת קבצי credential | סיסמה שגויה בלי הודעה | secrets |
| סיסמה כ-env ולא כקובץ (תרגול) | POSTGRES_PASSWORD_FILE=/run/secrets/<APP_DIR>/postgres_password | בכתיבת values | דליפה ל-describe וללוגים | secrets |
create נכשל בהרצה שנייה (תרגול) | create … --dry-run=client -o yaml, ואז apply --server-side | בכתיבת script | AlreadyExists | secrets |
url ב-repo Secret שונה מ-repoURL (תרגול) | kubectl -n argocd get secret <SECRET> -o jsonpath='{.data.url}', מפוענח base64, זהה בייט-לבייט | אחרי יצירת repo Secret | repository not found | operate |
register-repository.sh --verify בודק צורה בלבד (תרגול) | git ls-remote עם אותו credential | אחרי register | Argo מקבל Access denied | operate |
scripts בלי +x, או CRLF (תרגול / [COURSE]) | bash <FILE>; sed -i 's/\r$//' <FILE> | לפני ההרצה הראשונה | Permission denied או \r: command not found | env-cards |
Secrets אחרי ה-sync, וסקריפט bootstrap/ כאילו Argo קורא אותו (תרגול) | להכין namespace ו-Secrets לפני root | לפני ה-bootstrap של root | Failed אחרי 5 ניסיונות | operate |
תחנה 6: Argo Applications
Section titled “תחנה 6: Argo Applications”| מלכודת | מניעה זולה | מתי | תסמין אם מדלגים | קישור |
|---|---|---|---|---|
repoURL: "" נשאר (תרגול) | grep -rn 'repoURL: ""' <FILE> | לפני ה-bootstrap | הסקריפט מסרב, או repository not found | applications |
| source שלישי מצביע על templates במקום gitops, בגלל העתקת שורה קודמת (תרגול) | לקרוא את כל הבלוק: path לא מתחיל ב-charts/, וה-revision הוא main | בכתיבת Application | observability ריק | applications |
| tag על gitops (תרגול) | templates על <TAG>; values, root ו-kustomize על main | בכתיבת targetRevision | שום env לא מתעדכן אחרי promote | applications |
syncPolicy: {} נשאר (תרגול) | automated: {prune: true, selfHeal: true} | בכתיבת Application | נשאר OutOfSync, בלי self-heal | applications |
אחרי push kubectl get applications ריק (תרגול) | kubectl apply -f <FILE> של root, פעם אחת | אחרי ההכנה | No resources found | argocd/overview |
| שינוי מקומי שלא נדחף (תרגול) | git log origin/main..HEAD ריק | לפני שמסיקים מסקנה על Argo | Argo לא רואה את ה-clone | sync |
Unknown + Healthy נראה תקין (תרגול) | לקרוא SYNC קודם, ואז .status.conditions | בכל בדיקת סטטוס | staging ו-prod "תקינים" בלי משאב אחד | operate |
ComparisonError ב-env שלא קודם (תרגול) | לעשות promote; לא לערוך את ה-chart | לפני render של env | YAML parse error … line 47 | operate |
| Argo ויתר אחרי 5 ניסיונות (תרגול) | לתקן את ה-cluster, ואז sync ידני; finishedAt מול date -u | אחרי כל Failed | operationState.phase: Failed; refresh=hard לא מתניע; push לא עוזר | operate |
kubectl edit או scale על משאב של Argo (תרגול) | לתקן ב-Git: edit, commit, push | לפני כל תיקון | selfHeal מחזיר את השינוי | operate |
ה-path של root מכיל את root עצמו [COURSE] | root מצביע על argocd/apps, לא על קובץ ה-root | בכתיבת root | נעילה עצמית במחיקה | applications |
תחנה 7: כל environment מאומת
Section titled “תחנה 7: כל environment מאומת”| מלכודת | מניעה זולה | מתי | תסמין אם מדלגים | קישור |
|---|---|---|---|---|
Synced + Healthy נחשב "עובד" (תרגול) | /info פעמיים; accepted עולה | בכל env | env שלא עובד נראה ירוק | networking |
detections=0 נקרא כתקלה (תרגול) | להפעיל quick-transit בסימולטור ולבדוק count ב-DB | בהוכחת מסלול ה-DB | חיפוש באג שלא קיים | networking |
| אין לוגים, ומחפשים ב-init container (תרגול) | kubectl -n <NS> describe pod <POD> ו-Events; -c <CONTAINER>, --previous | כש-logs ריק | לא רואים StartError | kubernetes/overview |
| אותה התנגשות mount גם ב-signal-processor, ומניחים "downstream של ה-DB" (תרגול) | Events של כל Pod שנופל | לפני שמסיקים שהסיבה משותפת | מתקנים רק חצי | symptoms |
| תיקון template ב-StatefulSet, ומחכים שה-Pod יוחלף (תרגול) | kubectl -n <NS> delete pod <POD> (רק StatefulSet; Deployment מחליף לבד) | אחרי push של תיקון | ה-Pod השבור נשאר, כי ה-rolling update מחכה ל-Ready | storage-probes |
| מחיקה בלי לשאול מי מחזיק את ה-data (תרגול) | kubectl -n <NS> get pvc; ה-controller כבר מטפל? | לפני כל delete | אובדן data או מחיקה מיותרת | storage-probes |
promote:prod לפני שאומת staging (תרגול) | /info של staging נכון, ורק אז הכפתור | לפני prod | prod עולה עם גרסה לא מאומתת | ci/patterns |
| תיקון ב-Git לפני ה-create הראשון של prod (תרגול) | להכניס את className וה-mount ל-base.yaml לפני שמקדמים ל-prod | לפני promote:prod | אחרת צריך למחוק StatefulSet ו-PVC גם ב-prod | storage-probes |
| עובד ב-dev ולא ב-staging (תרגול) | לחפש את מה שמבדיל: namespace, קובץ values, tag | בכל כשל חלקי | ניחושים במקום differential | debugging/overview |
/ready מחזיר 503 [COURSE] | kubectl -n <NS> logs deploy/<SERVICE> --tail=30: איזו תלות חסרה | כש-Pod Running ולא Ready | חיפוש בעיה ב-probe במקום בתלות | storage-probes |
תחנה 8: observability
Section titled “תחנה 8: observability”| מלכודת | מניעה זולה | מתי | תסמין אם מדלגים | קישור |
|---|---|---|---|---|
ב-prepare-observability.sh: בלוק postgres מועתק, $ENV לא מוגדר ב-set -u, נתיב TLS שגוי (תרגול) | bash -n <FILE>; לקרוא כל שורה מול ה-header; להריץ פעמיים | לפני הרצה | unbound variable, Secret לא נוצר | templates |
trident-grafana-admin בלי שני המפתחות (תרגול) | --from-file לכל מפתח; kubectl -n <NS> get secret <SECRET> -o jsonpath='{.data}', מפוענח ל-keys | אחרי ההרצה | Grafana לא עולה | observability |
| ה-App אדום כי ה-namespace וה-Secrets הוכנו אחרי הניסיון (תרגול) | להכין, ואז sync ידני; push של bootstrap/ לא עוזר | אחרי ההכנה | Failed אחרי 5 ניסיונות | observability |
| source של ה-dashboards על templates (תרגול) | path לא מתחיל ב-charts/ ו-revision הוא main | בכתיבת ה-Application | dashboard ריק | applications |
| שם Service קצר בין namespaces (תרגול) | FQDN: <SERVICE>.<NS>.svc.cluster.local | בהגדרת target מ-namespace אחר | שם לא נפתר | networking |
relabel_configs מוחק את namespace [COURSE] | count by (namespace) (up) | אחרי עליית Prometheus | dashboard ריק או מערבב env-ים | observability |
תחנה 9: verify, cleanup, idempotency
Section titled “תחנה 9: verify, cleanup, idempotency”| מלכודת | מניעה זולה | מתי | תסמין אם מדלגים | קישור |
|---|---|---|---|---|
| exit codes הפוכים (תרגול) | 0 = הצלחה או אין שינוי; echo "exit=$?" | בכתיבת כל בדיקה | תנאי הפוך, סקריפט שמת | ci/patterns |
cleanup.sh נכשל בהרצה שנייה [COURSE] | --ignore-not-found ובדיקת קיום; "כבר נעדר" = הצלחה | בכתיבת cleanup | הרצף האידמפוטנטי נשבר | cleanup |
מוחקים namespace לפני root, או את argocd [COURSE] | root קודם; רק namespaces של הפרויקט | בניקוי | Argo מייצר מחדש; נמחקים PVC ו-data | cleanup |
דלתא בהעתקה, חצי זוג חוקים, placeholder שנשאר, תוצר לא הוגש, מונים לא נבדקו, עצירה ב-PASS [COURSE] | grep -rnE 'TODO|repoURL: ""' <FILE>; רשימת deliverables מול הקבצים; /info אחרי PASS | לפני הגשה | נכשלים בתהליך, לא בידע | exam-method |
docker system prune או שינוי שם project של compose [COURSE] | לא להריץ על ה-VM של הקורס | בניקוי | נמחקים images ו-cache של שאר המערכת | docker |
token או סיסמה ב-Git [COURSE] | grep לפני הגשה: last-20-minutes | לפני הגשה | credential ב-history | last-20-minutes |
בכל התחנות
Section titled “בכל התחנות”| מלכודת | מניעה זולה | מתי | תסמין אם מדלגים | קישור |
|---|---|---|---|---|
רווח אחרי \ בהמשך שורה (תרגול, 3 פעמים) | grep -nP ' +$' <FILE> ו-bash -n <FILE> | לפני כל commit וכל הרצה | הפקודה נשברת באמצע | exam-method |
| לא יודעים איפה הפקודה רצה (תרגול) | kubectl, helm, docker על ה-VM; CI variables רק ב-job | בכל פקודה חדשה | משתנה ריק, או kubeconfig חסר | architecture/overview |
| מנחשים סיבה שהטקסט לא תומך בה (תרגול) | grep -iE 'denied|403|unauthorized' על ההודעה | בכל שגיאה | תיקונים לא קשורים | when-stuck |
| תסמין בלי סיווג: קוד או סביבה (תרגול) | AssertionError = קוד; Cannot connect to the Docker daemon = סביבה | בכל שגיאה | מתקנים קוד שלא שבור | debugging/overview |