בניית צינור CI/CD טוב עם GitHub Actions זה כבר לא משהו נוסף "למתי שיש זמן": בצוותים מודרניים, זה כמעט דרישה לפריסה מהירה ואמינה. למרות זאת, מציאת דוגמה מלאה, גנרית ומחושבת היטב שתוכלו להתאים לחברה שלכם היא לעתים קרובות הרבה יותר מסובכת ממה שזה נראה.
בשורות הבאות נשלב את התיאוריה הקלאסית של CI/CD עם דוגמאות יישום מהעולם האמיתי באמצעות פעולות GitHub, צינורות רב פעמיים, משימות, סקריפטים של bash, מודולי PowerShell PnPפריסות ל-Kubernetes, Google Cloud ו-Kinsta, יחד עם שיטות עבודה מומלצות לאבטחה, ניטור ומדרגיות. הרעיון הוא שתוכלו לקחת את החלקים הללו, להתאים אותם להקשר שלכם ולהימנע מרבות מהמכשולים האופייניים.
למה אתם צריכים צינור CI/CD בנוי היטב
בפיתוח מקצועי עכשווי, CI/CD היא מערכת הדם של הקוד.היא משלבת שינויים, מרימה בדיקות, בונה ארטיפקטים ופורסת גרסאות חדשות עם התערבות מינימלית. ללא תהליך עבודה זה, כל פריסה הופכת למשימה ידנית, איטית ומועדת לשגיאות.
אינטגרציה רציפה (CI) מתמקדת באימות שינויים ברגע שהם מועלים למאגר, בדיקות יחידה, בדיקות ליטרים וניתוחים סטטיים מבוצעים כדי לאתר באגים במהירות האפשרית. ככל שתקבלו משוב מהר יותר, כך תוכלו לתקן אותם מהר יותר, וכל רגרסיה תהיה פחות כואבת.
פריסה רציפה (CD במובן של פריסה רציפה) או Continuous Delivery (בהתאם לרמת האוטומציה) מוסיף אוטומציה של חלק השחרור: בניית תמונות, פרסום חבילות, פריסה לסביבות בדיקה, בימוי או ייצור, ואפילו שינוי תעבורה באמצעות אסטרטגיות כחול-ירוק או קנרי.
בחברות עם הרבה קוד מדור קודםPipeline טוב הוא אחד המנופים הטובים ביותר למודרניזציה של המערכת האקולוגית: הוא מאפשר להכניס בדיקות לשירותים מדור קודם, להפוך משימות לאוטומטיות שבוצעו בעבר באופן ידני, ולהפחית את עלות התחזוקה של תשתיות כמו Jenkins או Nexus שהתיישנו.
מהן פעולות GitHub ומדוע הן משתלבות כל כך טוב עם CI/CD?
GitHub Actions היא פלטפורמת האוטומציה המובנית בתוך GitHub. זה מאפשר לך להגדיר זרימות עבודה בקבצי YAML בתוך המאגר עצמו. בעזרתו, תוכל לקמפל, לבדוק, לנתח ולפרוס את התוכנה שלך מבלי להגדיר שרתי CI חיצוניים.
זרימת עבודה היא אוסף של משימות ושלבים אשר נגרמים על ידי אירועים כגון push, pull_request, schedule (CRON), workflow_dispatch (ידני) או אפילו פעולות על בעיות. כל משימה פועלת ברצף (לדוגמה, ubuntu-latest) ומורכב משלבים המשתמשים בפעולות או פקודות רב פעמיות run.
GitHub מציע שוק ענק למניות היכן שיש לכם אינטגרציות מוכנות כמעט לכל דבר: Docker, Kubernetes, AWS, Azure, Google Cloud, SonarCloud, Slack, Jira, ניתוח אבטחה, מערכות הפעלה (linters) לאלף שפות וכו'. זה מקטין מאוד את הזמן הנדרש להקמת צינורות הפעלה מתקדמים.
בהשוואה לפתרונות כמו ג'נקינס או קונקורסל-GitHub Actions יש מספר יתרונות ברורים: זהו שירות מנוהל (אתה לא מנהל שרתים), הוא קשור קשר הדוק לקוד, הוא משתמש במודל תשלום לפי שימוש, והוא נתמך על ידי קהילה עצומה. יתר על כן, מפתחים רבים כבר מכירים אותו מפרויקטים אישיים, מה שמקצר משמעותית את עקומת הלמידה.
רכיבים בסיסיים של זרימת עבודה של פעולות ב-GitHub
הכל מתחיל בקובץ YAML ב... .github/workflows/, לדוגמה ci.yml o build-test-deploy.ymlלמרות שהתחביר יכול לגדול במידה ניכרת, המבנה הבסיסי פשוט יחסית.
החלקים המרכזיים של YAML הם: name (שם תהליך העבודה), on (אירועים שמעוררים זאת), jobs (קבוצת משימות לוגיות), ובתוך כל משימה, runs-on (רָץ), steps (צעדים), env (משתנים גלובליים) ו if (תנאים לביצוע שלבים או משימות).
עבודות מייצגות בלוקים של עבודה שניתן להריץ במקביל או בשרשרת באמצעות needsבתוך כל משימה, השלבים משתמשים בפעולות (uses:) או פקודות (run:דוגמה אופיינית כוללת: בדיקת קוד, התקנת תלויות, ביצוע linter, בדיקות ובנייה.
סודות ומשתני סביבה הם מנוהלים ברמת המאגר, הארגון או הסביבה. בזרימות עבודה, הם מופנים באמצעות ${{ secrets.MI_SECRET }} ולאפשר עבודה עם מפתחות API, אסימוני פריסה או אישורי ענן מבלי לחשוף אותם במאגר.
YAML מאפשר גם בניית מערכי ביצוע עם strategy.matrix, שימושי מאוד לבדיקת הקוד שלך על גרסאות שונות של Node, Python או Java, או אפילו על מערכות הפעלה שונות מבלי לכתוב את אותו בלוק מספר פעמים.
תכנן צינור CI/CD מודרני תוך שימוש בשיטות עבודה מומלצות
צינור תקין מחולק בדרך כלל לשלבים ברוריםבדיקות מהירות (lint, בדיקות יחידה), בניית ארטיפקט, שחרור (גרסה, תיוג, יומן שינויים, פרסום במאגר ארטיפקט) ופריסה בסביבה אחת או יותר.
שלב האינטגרציה הרציפה צריך להיות מהיר ככל האפשר. זה מבטיח שכל בקשת דחיפה או משיכה מקבלת משוב כמעט מיידי. נוהג נפוץ הוא להריץ את הבדיקות השונות במקביל באמצעות מערכים או משימות נפרדות, בהנחה של עלות מעט גבוהה יותר בתמורה להפחתת זמן ההמתנה הכולל.
כדי לנתק את הצינור מהשפה הקונקרטיתניתן להשתמש בכלי משימות כמו Task (בדומה ל-Make אך עם תחביר ידידותי יותר למשתמש). בדרך זו, זרימת העבודה של GitHub Actions מפעילה רק משימות גנריות (task test, task lintוכו') וכל מאגר מגדיר כיצד הם מיושמים באופן פנימי בהתאם לשאלה האם מדובר ב-Node, Java, Python וכו'.
ניהול גרסאות וארטיפקטים נכנסים לתמונה במהלך שלב השחרור.כאן בונים תמונת Docker, קובץ jar/war, חבילת npm או כל ארטיפקט אחר, מעלים אותו לרישום המתאים (רישום Docker, Maven, npm ב-Artifact Registry וכו'), מתייגים קומיטים ויוצרים גרסאות GitHub או יומני שינויים בעזרת כלים כמו. גיט-קליף או פעולות שחרור.
לבסוף, שלב הפריסה העבירו את הארטיפקט הזה לסביבת זמן הריצה: Kubernetes (GKE), Google App Engine, Cloud Functions, שירותים ב-Kinsta, השרתים שלכם דרך SSH וכו'. כאן תוכלו לשרשר שלבים עוקבים, כגון בדיקות פונקציונליות לאחר פריסה או התראות Slack עם פרטי שחרור.
דוגמה: השלמת צינור עם ESLint, בדיקות ופריסה ב-Kinsta
דוגמה ממחישה מאוד היא שימוש ב-GitHub Actions כדי לאמת אפליקציית React עם ESLint ומבחני יחידה, ולאחר מכן לפרוס אותה ב-Kinsta באמצעות ה-API שלה. הכל מתואם בתהליך עבודה יחיד של CI/CD.
החלק הראשון של ה-YAML מגדיר את הטריגר ושם הצינור. לדוגמה, שהוא פועל על כל אחד מהם push y pull_request לסניף main, ואפילו לתזמן עם משימות CRON (לדוגמה, כל יום בחצות או כל יום שני בשעה 8:00 UTC) באמצעות האירוע schedule.
ניתן לכנות את העבודה הראשונה בצנרת eslint והוא אחראי על בדיקת תחביר הקוד. הוא פועל ב ubuntu-latest ומשתמש במערך של גרסאות Node (למשל, 18.x, 20.x), עם שלבים לבדיקה והגדרה של Node עם actions/setup-node, תלויות npm במטמון, התקנה עם npm ci ולזרוק npm run lint.
העבודה השנייה, testsזה תלוי ב eslint דרך needs: eslintכך שהוא פועל רק אם בדיקת התחביר מצליחה. בפנים, התבנית חוזרת על עצמה: בדיקה, התקנת תלויות וביצוע של npm run test על גרסה ספציפית של Node.
העבודה השלישית, deploy, הוא מחובר לאחר שתי העבודות באמצעות needs: ומשתמש בצעד עם curl כדי לקרוא ל-Kinsta API. לשם כך, מפתח ה-API ומזהה האפליקציה מוגדרים כסודות ב-GitHub (KINSTA_API_KEY y APP_ID) וחשופים בעבודה דרך env כדי לבנות את בקשת ה-POST שתפעיל את הפריסה.
חשוב להבין שעבודת פריסה זו קינסטה רואה בקבלת ה-API עצמה הצלחה; עם זאת, אם הפריסה תיכשל לאחר מכן באופן פנימי בתוך קינסטה, תהליך העבודה של GitHub עדיין עשוי להציג סטטוס ירוק. יש לזכור זאת כדי למנוע שאננות ולהשלים את התהליך עם ניטור לאחר הפריסה.
ניהול cron מתקדם ותזמון תהליכי עבודה
תחביר CRON בפעולות GitHub זה מבוסס על פורמט יוניקס בן חמישה שדות: דקה, שעה, יום בחודש, חודש ויום בשבוע. כל שדה יכול להשתמש כוכביות, טווחים, רשימות ושלבים (*, 1-5, 1,15,30, */5), המאפשר תזמון משימות תחזוקה, גיבויים, ניקיונות או בדיקות תקופתיות.
לדוגמה: 0 0 * * * לבצע את תהליך העבודה בכל חצות (UTC), בעוד 0 8 * * 1 זה קורה בכל יום שני בשעה 8:00. זה משתלב בצורה חלקה עם הטריגרים הרגילים של push y pull_requestכך שאותו YAML יוכל להגיב גם לשינויי קוד וגם לביצועים מתוזמנים.
יכולת זו אידיאלית למשימות שאין טעם לשחרר בכל קומיט.סריקות אבטחה אינטנסיביות (למשל, עם בדיקת תלויות OWASP ב-Java), ביקורות תלויות, בדיקות כיסוי בדיקות או ניקוי ארטיפקטים ישנים ברישום.
שימוש חוזר בזרימת עבודה: קנה מידה של CI/CD למאות מאגרים
כאשר לארגון שלך יש עשרות או מאות מאגריםהעתקה והדבקה של אותו קובץ YAML בכל מקום היא מתכון לכאוס. כל שינוי דורש שינוי של חצי מ-GitHub Enterprise, מה שהופך את שמירה על עקביות ושיטות עבודה מומלצות לכמעט בלתי אפשרית.
הפתרון טמון בתכנון זרימות עבודה רב פעמיות מרוכז במאגר "תבניות" של CI/CD. זרימות עבודה אלו חושפות קלטים ופלט, וכל שירות מגדיר רק YAML קטן שמפעיל אותם, ומעביר פרמטרים כגון סוג ארטיפקט (Docker, ספריית Java, חבילת npm), זמן ריצה של פריסה (GKE, GAE, פונקציית ענן וכו') או פריטי משימה שיש לבצע.
דפוס נפוץ הוא להפריד שלוש זרימות עבודה גדולות לשימוש חוזר: אחד מ build-check-task (אינטגרציה רציפה), עוד אחד של build-release-dockerfile או חפצים אחרים ופריסה שלישית (deploy-gke, deploy-gaeוכו'), כך שכל מאגר בונה את הצינור שלו על ידי שילובם.
כדי לתמצת לוגיקה משותפת, ניתן גם להגדיר פעולות מותאמות אישית. en .github/actionsלדוגמה, כדי להגדיר את Gradle, Java, Node או Task, כדי לקבל מטא-דאטה של בנייה, כדי לפרסם תמונות Docker, כדי לתייג גרסאות ב-Git עם סקריפט bash, או כדי לשלוח התראות ל-Slack. כלל הזהב הוא שמאגרי שירות צריכים להשתמש רק בזרימות עבודה רב פעמיות, ולא בפעולות אלו ישירות, כך שתאימות לאחור תהיה קלה יותר לניהול.
אינטגרציה רציפה ומהירה עם משימות, מטריצות וניתוח סטטי
במהלך שלב הבנייה או הבדיקה, מומלץ להפעיל דברים רבים במקביל.בדיקות יחידה, ניתוח סטטי (PMD, Checkstyle, SpotBugs ב-Java; ESLint ב-JS/TS), סריקה עם SonarCloud וכו'. זה שומר על זמן הצינור הכולל סביר גם בבסיסי קוד גדולים.
משימה (Taskfile.yml) פועלת כשכבת הפשטה על פקודות ספציפיות, מה שמאפשר לזרימת העבודה של CI פשוט לקרוא task check, task test o task lintעבור פרויקט Java, ניתן להעביר משימות אלו ל-Gradle באמצעות JUnit, PMD, Checkstyle ו-SpotBugs; עבור פרויקט Node, ניתן להעביר אותן ל-Jest, ESLint וכלי אבטחה כגון npm audit או דומה.
פעולות GitHub מוסיפות את קטע המערך כדי להריץ את אותן משימות על גרסאות שונות של זמן הריצה: לדוגמה, בדיקת ספריית Node על 16, 18 ו-20, או פרויקט Python על 3.10 ו-3.12. זה פשוט כמו להצהיר על מערך של גרסאות ולהשתמש בו בתצורת המשימה.
גישה זו שימושית במיוחד בארגונים המעוניינים לתמוך במספר ערימות. (Java, Node, TypeScript, Python וכו') מבלי שיהיה צורך לכתוב מחדש את לוגיקת הצינור עבור כל מאגר: המשימה מסתגלת לכל שפה וזרימות העבודה הניתנות לשימוש חוזר נשארות כמעט זהות.
שלב השחרור: ניהול גרסאות, תיוג ופרסום של פריטים
לאחר שעברו את הבדיקות, הגיע הזמן לבנות את החפץ שייפרס בפועל.תמונת Docker, קובץ JAR, חבילת npm, כל מה שמתאים. זה כולל גם את כלי השפה וגם את רישומי הארגון ומדיניות הגרסאות שלו.
חלק מפרויקטי ג'אווה משתמשים בתוספים כמו Gradle Axion. לנהל גרסאות המבוססות על תגיות Git. בהקשרים מעורבים (Java, Node וכו') ייתכן שיהיה פשוט יותר להשתמש בסקריפט bash מותאם אישית שמחשב את הגרסה הבאה (לדוגמה באמצעות SemVer), יוצר את התג, דוחף אותו לשלט ויוצר את הגרסה המתאימה.
כלים כמו git-cliff הם עוזרים ליצור יומני שינויים בהתבסס על הודעות commit, השינויים מסווגים לפי סוג (תכונה, תיקון, תקלה וכו'). שילובם ב-pipeline מבטיח שכל גרסה מגיעה עם יומן שינויים ברור מבלי שאף אחד יצטרך לכתוב אותו ידנית.
כדי לפרסם אובייקטים, משולבים פעולות ואישורים מתאימים.רישומי Docker (Docker Hub, GitHub Container Registry, Artifact Registry), מאגרי Maven, רישומי npm וכו'. שוב, אישורים מאוחסנים כסודות ומוכנסים לעבודות רק בעת הצורך.
פריסה רציפה לסביבות Kubernetes, GCP, Kinsta וסביבות אחרות
פריסה היא המקום שבו CI/CD מקיימים אינטראקציה עם התשתיתכאן, GitHub Actions משתלב בצורה חלקה כמעט עם כל פלטפורמה: Kubernetes, App Engine, Cloud Functions, שרתים מסורתיים, פלטפורמות כמו Kinsta וכו'.
עבור Kubernetes (לדוגמה ב-GKE) הדפוס הרגיל זה: אימות עם גוגל קלאוד (באמצעות פעולות רשמיות), הגדרה kubectl בתוך הקשר של האשכול, יש להחיל את המניפסטים או התרשימי Helm, ובמידת הצורך, לבצע פריסה מבוקרת (למשל, עם קנרי או כחול-ירוק) ולוודא את הסטטוס באמצעות פקודות מ- kubectl rollout status.
במקרה של App Engine או Cloud Functionsהצינור בונה את התמונה או הארטיפקט, מפרסם אותו ברישום הארטיפקטים ולאחר מכן מפעיל את פקודות הפריסה. gcloud באופן מתאים, שוב באמצעות אישורים מנוהלים כגון סודות ורצים ארעיים.
כאשר הפריסה מתבצעת מול ממשקי API חיצוניים כמו אלה של Kinstaבדרך כלל מספיק צעד אחד curl או פעולה ייעודית ששולחת את הבקשה עם אסימון האימות והפרמטרים הדרושים (מזהה אפליקציה, ענף וכו'). המשימה נחשבת מוצלחת אם ה-API מגיב נכון לבקשת הגרסה החדשה.
הפריסה כמעט תמיד מלווה בהודעה. ל-Slack, Teams או כלי תקשורת אחרים, תוך ציון איזה שירות נפרס, באיזו סביבה, עם איזו גרסה, מי הפעיל אותו וקישורים ליומני זרימת העבודה. בסביבת הייצור, זה משמש גם לביקורת ומעקב.
בקרת איכות: אבטחה, ניטור ויומני רישום
אוטומציה של בנייה ופריסה היא נהדרת, אבל בלי נראות בנוגע למה שקורה, הצינור יכול להפוך לקופסה שחורה. GitHub Actions מציע יומני רישום מפורטים לפי ביצוע, לפי משימה ולפי שלב, ומאפשר לך לאבחן כשלים בקומפילציה, בדיקה או פריסה.
עבור צרכים מתקדמים יותר, משולבים שירותי תצפית חיצוניים. כגון Datadog, New Relic או Splunk, אשר אוספים מדדים על זרימות עבודה, זמני ביצוע, שיעורי כשל וכו', ועוזרים לזהות צווארי בקבוק ולתעדף אופטימיזציות של צינורות עבודה.
במקביל, לביטחון יש תפקיד מפתחניהול סודות מוצפנים, מדיניות גישה מינימלית נדרשת, סקירת הרשאות פעולה, שילוב סורקי פגיעויות קוד ותלויות (סריקת קוד, סריקת סודות, OWASP וכו') בתוך זרימות העבודה עצמן.
צוותים רבים מוסיפים גם בדיקות לאחר פריסה בסביבה המעודכנת: בדיקות פונקציונליות מקצה לקצה, בדיקות ביצועים, בדיקות עשן בסיסיות, ואם משהו מתקלקל, מנגנוני החזרה למצב קודם אוטומטיים המשקמים את הגרסה היציבה הקודמת.
ניהול זרימת עבודה: ענפים מוגנים ובקשות משיכה
אופן העבודה עם ענפים ובקשות משיכה חייב להיות תואם ל-CI/CD כדי שהכל יהיה הגיוני. הדבר הנפוץ ביותר הוא להגן על הענף הראשי (main o master) ודורשים שכל שינוי יעבור דרך יחסי ציבור ויעבור בדיקות צינור.
GitHub מאפשר לך להגדיר כללי הגנה על ענפים מדיניות זו כופה שימוש בבקשות משיכה, חוסמת פעולות קומיט ישירות ודורשת שבדיקות סטטוס מסוימות (זרימות עבודה ספציפיות של פעולות) יהיו ירוקות לפני מתן אפשרות למיזוג. ייתכן שהן גם דורשות מינימום של תיקונים, כללי אישור וכו'.
מודל זה מבטיח שהקוד שמגיע לייצור הוא עבר בדיקה אנושית וכל בדיקות הצינור האוטומטיות, מה שמפחית באופן דרסטי את הסיכון לחמוק משגיאות חמורות או פגיעויות.
בחברות עם סביבות מרובות פריסה (פיתוח, בייצור, הפקה) לסביבות ייצור שמורה בדרך כלל למיזוגים לתוך הענף הראשי, בעוד שענפים אחרים עשויים להפעיל פריסות לסביבות קודמות לצורך בדיקות פנימיות או הדגמות.
מבט על התמונה הגדולה, צינור CI/CD מעוצב היטב עם GitHub Actions זה הופך לעמוד השדרה של הפיתוח: שילוב שינויים, הרצת חבילות בדיקות מקיפות, בנייה ופרסום של ארטיפקטים, פריסה לפלטפורמות ענן מרובות, ניטור באמצעות כלי תצפית וניהול באמצעות כללי הסתעפות ובקשות משיכה ברורים. בעזרת זרימות עבודה רב פעמיות, פעולות מותאמות אישית, כלי עזר כמו Task, Rease Action ו-Git Cliff, וניהול סודות והרשאות חזק, ניתן לתמוך בכל דבר, החל מאפליקציות Python פשוטות ועד ארכיטקטורות Kubernetes מורכבות, תוך שמירה על מהירות אספקה, איכות קוד ואבטחה מבלי להעמיס על הצוות משימות ידניות.