יום שבת, 7 במאי 2016

על כלי ניהול תצורה / Provisioning - מבראשית ועד ימי ה Docker

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

ישנם מגוון כלים לניהול תצורה (Configuration Management בקיצור CM), המאפשרים לנהל מספר גדול של שרתים (מאות, אולי אלפים) בצורה אוטומטית ומסודרת. כלים אלו לרוב מספקים את היכולות הבאות:

  • Change Management - ניהול הגדרות ה"מצב הרצוי" בתצורת השרתים, בצורה מסודרת וניתנת לשליטה. למשל: שינוי הגדרות של nginx בשרתים מסוג x או הוספת תיקיה חדשה במערכת הקבצים של שרתים מסוג y.
    • חשוב שיהיה קל להבין ולהגדיר מהם "שרתים מסוג x". לעתים אגב, אלו יכולים להיות שרתים עם מערכות הפעלה שונות, ושמריצים תוכנות שונות.
  • Provisioning - זהו בעצם התהליך של העברת שרת ממצב נתון כלשהו - למצב הרצוי. 
    • זה יכול להיות התקנה של שרת מאפס, או בדיקת המצב בשרת קיים - וביצוע השינויים הנדרשים על מנת להגיע למצב הרצוי.
    • לרוב שינוי ההגדרה יכלול ביצוע שינוי בשרתים שכבר "באוויר", וגם הכללת השינוי החדש בשרתים הבאים שיקומו.
    • כאשר מבצעים שינויים, ישנן תקלות (פעולות רצויות שנכשלו) - תקלות שיש לנהל.
  • Orchestration - תיאום הפעולות בין כמה שרתים. למשל:
    • ביצוע שינוי מסוים באותו הזמן על מספר שרתים. אולי כדאי קודם לבצע בדיקה שמאשרת (כמיטב היכולת) שאכן כל השרתים מסוגלים לעבור את השינוי.
    • אם אחוז גבוה מהשרתים לא מצליח לעבור שינוי - אולי כדאי לעצור את השינוי המתגלגל, ולהתחיל פעולה של rollback (חזרה למצב הקודם)?
    • יש שינויים שצריכים להיעשות בצורה מדורגת כלשהי: קודם ביצוע שינוי בשרתי x ורק אח"כ אפשר לבצע את השינוי בשרתי y.
הכל כמובן - בצורה אוטומטית.


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







"דור ראשון"


CFEngine

את הכלי הזה יכירו בעיקר הוותיקים שבינינו. הוא התחיל לפעול ב93' (כלומר: שנת 1993- למי שלא הבין) והיווה מהפכה תפיסתית בעולם ניהול-השרתים. הוא התחיל כפרויקט קוד פתוח, אך ב 2008 נוספה לו גרסה "מסחרית" עם תמיכה - ומאז הוא משתמש בעיקר תצורות מסובכות של ארגונים גדולים.

CFEngine ממומש בשפת C, והוא ידוע במהירות הגבוהה שלו. מצד שני - הוא דורש ידע מלא בתכנות בשפת C על מנת לתפעל אותו, מה שמגביל מאוד את קהל היעד.

כלי זה, כמו הכלים הבאים אחריו עובדים ע"פ עקרון דומה:
  • שרת מרכזי מחזיק את התצורה הרצויה לסוגי השרתים השונים (ע"פ ארגון של קבוצות או כללים).
  • על כל שרת מותקן Agent שמתשאל מדי פעם את השרת המרכזי, וברגע שהוא מבין שיש לבצע שינוי תצורה (קרי: עדכון תוכנה, שינוי הגדרות, וכו') - הוא מבצע אותו.
    • כששרת מופעל בפעם הראשונה (לפעמים הוא כולל מערכת הפעלה ו Agent בלבד) - הוא יבצע את כל שינויי התצורה לפני שיתחבר ל Load Balancer (קרי: יהיה זמין לשאר ה Landscape).


איך CFEngine עובד. מקור: CFEngine.com


"דור שני"


Puppet

הכלי הבא שהופיע והשאיר חותם הוא Puppet, עם המטפורה המפורסמת של ה "Puppet Master" - השרת המרכזי שלו שהוא כמו "מפעיל בובות". גם Puppet החל בשנת 2005 כ Open Source ורק מאוחר יותר הופיעה גרסה מסחרית (Enterprise).
החידוש המשמעותי של Puppet היה שהוא לא דרש ידע מעמיק בתכנות, אלא כתיבת DSL (כלומר: Domain Specific Language) המבוססת על שפת רובי - דבר שצוות ה Operations של הארגון היה מסוגל לתפעל בצורה מלאה.

קוד Puppet לדוגמה

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

השפה של Puppet (או כלי CM אחרים) היא DSL שמותאמת לצרכים המאוד ספציפיים של ניהול תצורה, ועוזרת לשרת את קריאות הקוד (הקוד הרבה יותר נוח לקריאה מ bash) ולצמצם טעויות אפשריות. עוד ייתרון הוא היכולת להגדיר תצורה שתפעל על מערכות הפעלה שונות (דיסטרוס שונים של לינוקס, או אפילו לינוקס ו"חלונות").

השפה של Puppet מספקת שורה של פרמיטיבים (File, User, Package, Service, וכו') שניתן להרכיב למודולים (הניתנים לשימוש חוזר) או פשוט להוריד מוכנים מ PuppetForge[א]. היכולת לשתף מודולים ברמת הקהילה מסייעת לקיצור זמני-פיתוח והגברת אמינות ניהול התצורה - כל עוד מדובר במודולים איכותיים. יש לציין שלא כולם כאלו, וחשוב מאוד לברור את מקור המודולים בהם אתם משתמשים.

בזכות קלות השימוש - Puppet לקח מ CFEngine את הבכורה.


Chef

לא הרבה מאוחר יותר, בשנת 2008, הופיע כלי חשוב נוסף בשם Chef. גם הוא, אגב, כלל סדרת מטפורות בלתי-נשכחות ("Knife", "Kitchen", "Cookbook", ו "Recipe", וכו'). Chef שיפר כמה מהדברים שהציקו ב Puppet וזכה לפופולריות גדולה בעצמו. גם הוא - מבוסס על שפת רובי.
בשלב הזה Chef ו Puppet החלו בתחרות "ראש בראש", כאשר הם משלימים כל הזמן את הפיצ'רים החדשים אחד של השני. במהלך התחרות הזו נוספו לכל אחד מהם יכולות וכלים, מה שגרם להם בעצם להפוך מכלים פשוטים יחסית - לכלים מורכבים לשימוש, הדורשים מהמשתמשים למידה משמעותית.

בגדול, Chef נחשב יותר "ידידותי למפתח" (כזה שרגיל לכתוב בדיקות יחידה, ו/או לעבוד בכלי פיתוח)  בעוד Puppet מעט יותר ידידותי לאיש ה Operations (שאינו מתכנת).
יש אומרים ש Puppet נוח יותר לניהול תחנות עבודה של משתמשים, וש Chef - נוח יותר לניהול חוות שרתים (אולי בגלל ה output המפורט שלו שמסייע לאתר הבעיה - כאשר יש תקלות). בסופו של דבר, שני הכלים קרובים זה לזה ביכולות, ובבגרות. ל Puppet יש קהילה גדולה יותר, וב Chef מורכב יותר להתמקצע.

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

עוד נקודה מעניינת היא ששלושת הכלים שהזכרתי עד רגע זה (Chef, Puppet, ו CFEngine) תוכננו ונבנו בתקופה בה התפיסה הייתה לעדכן שרתים קיימים, ולאורך זמן.

אני אסביר: כאשר יש לכם שרתים פיסיים ("ברזלים"), התקנה מלאה של שרת עשויה לקחת עשרות דקות - ואפילו מספר שעות. לבצע את ההליך הזה אפילו פעם אחת בחודש - זו בעיה רצינית.
כלים כמו הכלים הנ"ל התמקצעו בנושא של עדכון הדרגתי של שרתים, מה שגם נקרא Convergence, קרי: א. בדוק באיזה מצב השרת, ב. עדכן את תצורת השרת, רק במה שצריך - לתצורה הרצויה (deltas). עדכונים כאלו יכלו להיות כל כמה ימים.

גישת ה Convergence אפשרה לבצע עדכונים תכופים בתצורת השרתים, מבלי לגזול זמן רב / לשלם ב downtime.
המחיר של גישה זו היא שניהול deltas לאורך זמן הוא מסוכן ולא יציב - ודברים יכולים (ואכן) נוטים להשתבש. למשל: מפתח התחבר ב SSH לשרת ושינה הרשאה של איזו תיקיה. עדכון נכשל והשאיר אחריו מצב בלתי רצוי שאינו מתוקן ע"י הגדרות התצורה של הכלי, וכו'.
הפתרון היה להחזיר שרת ל "baseline" - ברגע שדברים מסתבכים. לשלם את המחיר ההתקנה מחדש - בכדי לחזור למצב של סדר ושליטה.
הבעיה: בזמן שחלף עד שהבנו שהשרת הוא בתצורה בעייתית - התצורה הבעייתית יוצרת "רעש", ועוד רעש בפרודקשיין!

התפיסה השלטת היום היא תפיסה של Immutable Servers (או "כמעט Immutable Servers"), כאשר כל שינוי תצורה (או: כמעט כל שינוי תצורה) - משמעו ליצור Image חדש, נקי ומסודר של שרת, ולעדכן את המכונות הרצויות.
היכולת הזו מתאפשרת בעקבות טכנולוגיות הוירטואליזציה והענן שהפכו לנפוצות כ"כ, ומתקדמות צעד אחד נוסף קדימה בעקבות טכנולוגיה כמו Containers.

כלומר: כל פעם שאנו רוצים לבצע שינוי בשרתים (נניח להוסיף לשרת agent של logstash) - ניצור Image חדש ונקי, ונריץ את כלי שלבי ה provisioning מאפס עד לקבלת שרת בתצורה הרצויה (כולל השלב החדש של התקנת ה logstash agent). כעת נייצר מהשרת הזה Image ואת ה Image הזה - נתקין על המכונות הפיסיות בפרודקשיין.

גישת ה Immutable Server נחשבת אמינה ופשוטה יותר לשליטה - ולכן עדיפה. החיסרון: שינויים קטנים עדיין דורשים בנייה של Image - תהליך שיכול לארוך זמן רב (עשרות דקות עד שעות).
אם כל השינוי הנדרש בשרת הוא העתקה של קובץ בודד, שינוי הרשאות על תיקיה, או שינוי הגדרה - רבים מאיתנו יעדיפו שיהיה על השרת גם כלי CM בעזרתו יוכלו לבצע את השינוי בזריזות. הזריזות הזו חשובה מאוד כאשר יש להגיב לתקלות בפרודקשיין. במקביל, ניתן לבצע את השינוי בצורה מסודרת על ה Image ו"ליישר קו" ב deployment הבא.
גישה זו, היא גישת ה "כמעט Immutable Servers" - המשלבת בעצם בין שתי הגישות.


שינוי הגישה מניהול תצורה בעזרת deltas שמתמשכים למשך חודשים, לצורה של "Immutable Servers" או "כמעט Immutable Servers" (לא ידוע לי על מונח מוסכם) פתח הזדמנות לכלים פשוטים משמעותית, שלא צריכים להתמודד עם המורכבות הרבה של ניהול deltas בתצורת השרת - להצטרף לזירה.

אפשר לציין ש Chef ו Puppet הם עדיין הכלים הנפוצים. הם שולטים בזירת ה CM כבר זמן רב, והשפיעו עליה ועל עולם התוכנה רבות.



מקור: google trends



"דור שלישי"



Ansible

בשנת 2012, הופיעו כלי CM חדש בשם Ansible ששינה הרבה מצורת העבודה של Chef ו Puppet:
  • ניהול העדכונים: במקום שיהיה שרת מרכזי (או כמה לצרכי HA ו Scale) אליו כל ה Agent פונים ב pull, הגישה היא לבצע push: אתם פונים לשרתים השונים ואומרים להם לבצע את השינוי.
    • יתרונות: הסרת שרת הקונפיגורציה מה landscape. הבדיקות של תצורות חדשות נעשות ב cycles קצרים יותר, ב push העדכונים מגיעים לשרתים מהר יותר. קל לשימוש בסביבת פיתוח, בה ניתן לפתוח laptop ולעדכן שרת - בלי להתקין ולתחזק "שרת מרכזי".
    • חסרונות: כאשר מדובר ב scale גדול (מאות ואלפי שרתים) - עדכון השרתים ב push ייקח יותר זמן. אם בדיוק עלה שרת חדש לפני שיש image מעודכן - הוא עשוי לפספס את העדכון.
  • שפת התכנות: במקום DSL ומיומנויות של semi-תכנות, ההגדרה של התצורה נעשית בעזרת קובץ yaml פשוט.
    • יתרונות: הרבה פחות "קוד" לכתוב. זמן למידה קצר בהרבה של הכלי. אין צורך ברקע בתכנות בכלל על מנת ליצור הגדרות תצורה.
    • במקום שפת תכנות שיוצרת הפשטה מעל כל סוג של מערכת הפעלה, שכבת ההפשטה היא רזה. יש מודול לפקודות apt (נניח אובונטו) ומודול לפקודות yum (נניח Red Hat). ההפשטה הרזה לא מאפשרת "לכתוב סקריפט אחד שמתאים לכל מערכת הפעלה", אך מפשטת מאוד את הלמידה (כי אתם יודעים כיצד apt או yum עובדים, ומקלה להבין מה קורה בפועל.
    • חסרונות: פחות גמישות: בעיות CM מורכבות יידרשו כתיבת סקריפט מיוחד (בעיקר: שימוש ב Jinja2 - שפת templates בה ניתן לכתוב מעט קוד פייטון או להפעיל קוד bash).
    • הערה: Ansible הושפעה מפייטון (השפה בה היא כתובה) בגישת ה "Batteries included". המודולים הטובים שנכתבו / אושרו ע"י Ansible הם חלק מהכלי, ולא צריך להוריד אותם מ repository מרכזי כמו Chef או Puppet.
  • אין Agnets: אין צורך להתקין Agent (להלן Chef-Client או Puppet-Agent): על גבי השרתים. Ansible משתמש ב SSH סטנדרטי (ליתר דיוק: OpenSSH) בכדי לגשת לשרתים ולבצע בהם שינויים. עבור Windows - משתמשים ב PowerShell remoting.
    • יתרונות: אין צורך בהתקנה, עדכון, וניטור של Agents על גבי השרתים. רק צריך שרת עם SSH ו Python 2.5.
    • חסרונות: SSH הוא אטי יותר (פחות מתאים לעדכון אלפי שרתים),


Salt (מבית SaltSlack)

מעט לפני Ansible הופיעה ספרייה בשם Salt.

בכמה מובנים, Slat דומה למדי ל Ansible: הגדרות התצורה מוגדרות ב Yaml ולא בשפת תכנות, והוא פשוט בסדר גודל מ Chef או Puppet.
Salt ו Ansible שניהם כתובים בפייטון (Chef וגם Puppet כתובים בשפת רובי).

בכל זאת, הגישה של Salt להעברת העדכונים לשרתים - היא דיי דומה לזו של Chef ו Puppet.

המוטיבציה המוצהרת ליצירת Slat הייתה עדכון תצורה מהיר של קנה מידה גדול של שרתים. נראה שיצירת סביבת עבודה פשוטה יותר מ Chef/Puppet הייתה מהלך טבעי, אך משני - בתכנון של Salt.
  • השרת (הקרוי Slat Master) מבצע עדכונים ב push לתור של ZeroMQ. על השרתים מותקנים Minions (סוג של agents) שמאזינים לעדכונים על ה Queue ומבצעים מייד את העדכון. זו גישה משולבת בין ה Push של Ansible וה Pull של Chef/Puppet, שמבטיחה בד"כ זמני תגובה מהירים יותר מ Chef/Puppet (כי יש Push), ו scalability גבוה - היכולת לעדכן אלפי שרתים במקביל.
  • ניתן להגדיר ה Salt Master בהיררכיה כך שיש Centralized Master אחד שמגדיר מה לעשות, אך כמה Masters תחתיו שמפיצים את הבשורה לשרתים שונים. גישה זו יכולה לספק Scale גבוה אף יותר (נאמר: עשרות אלפי שרתים?)
בשנת 2014 Salt הציג Queue ייעודי משלו בשם RAET שהוא Scalable ואמין יותר מ ZeroMQ - לצורך הספציפי.



מקור: Google Trends


עידן ה Docker - "דור רביעי?"


כל כלי ה Configuration Management שותפים למורכבים מסוימת, מורכבות שנובעת מהקושי לבצע provisioning למערכת על "שרת אמיתי בפרודקשיין". מה לעשות: אתם זקוקים לכלי מוניטורינג, אבטחה, לוגים, הגדרות, וכו' - תצורת השרת היא מורכבת.

Docker הוא כלי שמנבאים שיחולל מהפיכה לא רק בסביבת ה Production - אלא גם בעולם הגדרות התצורה.
מכיוון ש Docker "מפרק" את סביבת השרת להרבה Containers קטנים, הוא גם הופך את בעיית ניהול התצורה לסט בעיות קטנות יותר: תצורת ה Application Server ב Container A, תצורת ה Nginx ב Container B, ותצורת ה Monitoring Deamon ב Container C. על אחד מהן עשויה להיות בלתי תלויה, ופשוטה הרבה יותר מתצורה כוללת.

ה dockerfile, אם כן, הוא בערך כמו קובץ ה Yaml של Ansible או Salt, המאתר כיצד להתקין תהליך ואת התלויות שלו. טעינה מחדש של container היא כ"כ מהירה (עשרות שניות) - שניתן (אולי?) להסתפק בגישה טהורה של "Immutable Servers".

מצד שני, לנהל אלפי containers (הנחה: על כל שרת יש 5-10 containers) - זו משימה קשה בהחלט! זו בעצם בעיית ה Orchestration ש Docker איננו מכסה. למשימה זו, נבנים כיום כלים ייעודיים, שהמוכרים ביניהם הם:
  • Kubernetes - כלי מבית גוגל, המשתמש בניסיון הקיים של גוגל בעבודה עם Linux Containers וניסיון שהצטבר עם כלי פנימי של גוגל בשם borg - האחראי על ה provisioning של השרתים הרבים מאוד ב Data Centers של גוגל. בעצם, גוגל כבר עושה היום משהו דומה.
    הניסיון הזה מתבטא למשל, בכל ש Kubernetes פתרה בעיות של Docker עצמו שהיו קיימות בגרסאות מוקדמות.
    Kubernetes מציע פתרון שלם יחסית, עם etcd ל container discovery, עם flannel לניהול הרשת בין ה containers (עניין מסובך ומתיש), ועוד.
    לסביבה העשירה הזו יש מחיר: יש לכלי CLI משלו, API משלו, וקבצי YAML משלו להגדרות התצורה - וזו סביבה שיש ללמוד להשתמש בה, בנוסף ל Docker.
  • Docker Swarm - הוא כלי אשר נוקט בגישה כמעט הפוכה: הוא תומך ב API הקיים של Docker, ובסביבת ה CLI - מה שאומר שקל מאוד להשתמש ב Docker Swarm עם ה toolchain הקיים של Docker (למשל: Docker Machine (לשעבר Krane) או Compose, וכו').
    Docker Swarm מתוחזק ע"י קהילת ה Docker, והוא חלק מ"פתרון כולל" שנבנה בתוך קהילה זו לניהול תצורה.
    האם זו בהחלט הגישה המועדפת? קשה לומר. התאימות ל Docker API מגבילה את הכלי למגבלות של Docker עצמו ומקשה עליו לבצע חלק מהדברים שניתן יהיה לעשות ב Kubernetes.

זה לא כ"כ משנה איזה עוד כלים קיימים או ייווצרו בקהילה של Docker.
אם וכאשר Docker יהפוך לסביבת ה Deployment המקובלת ב Production - הוא עתיד לבצע Disruption גם בעולם ה CM ולשנות אותו משמעותית ממה שאנו מכירים אותו היום.



סיכום


אם תשאלו את מי שעובד עם כלי CM כלשהו, מהו הכלי הטוב ביותר - יש סיכוי סביר שהוא יצהיר על הכלי שבו הוא כבר עובד. מדוע? כי לכלים הללו יש learning curve לא פשוט של התמחות. אם כבר מתרגלים לכלי ומתמחים בו - קשה ליזום החלפה שתדרוש למידה והסתגלות משמעותית מחדש. כפי שלא מחליפים מערכת תוכנה במכה אחת - כך גם בד"כ לא מחליפים סביבת CM במכה אחת.

הכלי הנכון ביותר לשימוש - משתנה עם הזמן, מכיוון שבחברות הצרכים משתנים: פעם יש רק לינוקס (נניח RedHat) ואז יש גם OpenSuse ו Windows. פעם ה deploy הוא תהליך חודשי - ולאחר מכן כבר מדברים על Continuous Deployment. פעם זה על שרתים של הארגון, ומעט אחרי זה - בענן. פעם על VMs וקצת אחרי זה על Containers.
הצרכים הארגוניים משתנים ככל כנראה מהר יותר ממה שניתן להסתגל לכלים השונים, ומכאן ניתן להסיק שאחוז גבוה מהארגונים משתמש בכל רגע נתון בכלי CM שאינו אופטימלי לצרכים שלו.

ישנה תקווה אמיתית שכלים חדשים ופשוטים יותר (Ansible או Swarm, למשל) יאפשרו לארגונים לבצע שינויים תכופים יותר בצורת העבודה - ולהתאים בצורה טובה יותר את מערכת ה CM לצרכים המשתנים של הארגון.


שיהיה בהצלחה!



-----


קישורים רלוונטיים


Chef vs. Ansible vs. Puppet vs. Salt בהקשר של OpenStack - אך עדיין מעניין ורלוונטי.

http://martinfowler.com/bliki/ImmutableServer.html - פוסט של מרטין פאוולר על Immutable Servers.

http://techblog.netflix.com/2013/03/ami-creation-with-aminator.html - פוסט מהבלוג של Netflix שמתאר את תהליך ה Provisioning שלהם

על Convergence מול Immutable Deployment


-----

[א] רמז לכך ש puppet נבנה בימים בהם הכלי המקובל לניהול קוד פתוח היה SourceForge? היום בוודאי היו קוראים לו PuppetHub...



3 תגובות:

  1. אפשר להוסיף כמה פרטים.
    1. כלי kubernetes הוא כלי לניהול גם ב google cloud . בא בחינם לכל לקוח.
    2. יש עוד כלים כמו nomad של hashicorp

    השבמחק
  2. מאמר טוב, תודה!
    יש לך המלצות לאיפה אפשר להתעמק עוד בתהליכים המקובלים כיום? בדגש על Docker (מלבד האתר שלהם...)

    אגב, התמונות בסוף "דור שני, ובסוף "דור שלישי" זהות

    השבמחק
    תשובות
    1. על Docker יש אינסוף חומר בערך, למשל: https://goo.gl/cWJP3d
      על מתודולוגיה עצמה - הייתי מנסה לראות את ה sessions על DevOps ב QCON או כנסים כמו DevOps Enterprise (למשל: https://www.youtube.com/watch?v=FV9X0xj6fFw או https://www.youtube.com/watch?v=_wnd-eyPoMo)

      אגב, התמונות בסוף "דור שני" ובסוף "דור שלישי" הן אולי דומות - אך בעצם שונות!

      :)

      ליאור

      מחק