הבלוג של ינון פרק

טיפים קצרים וחדשות למתכנתים

טיפ Node ו Docker - השתמשו ב wait-on כדי לחכות לקונטיינרים אחרים

23/10/2021

אחת הבעיות בעבודה עם קונטיינרים היא שהקונטיינרים כל הזמן עולים ויורדים. במיוחד אם אני משתמש ב Orchestrator, אז כל התקנה חדשה של מערכת מעלה קונטיינר חדש, והרבה פעמים כשיש תקלה הקונטיינר כולו פשוט מתרסק וה Orchestrator יעלה אותו מחדש.

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

אני מקווה שאתם כבר מכירים את הסקריפט wait-for-it שגם מגיע עם דביאן וגם אפשר להוסיף בקלות להתקנה של כל קונטיינר ופותר בדיוק את הבעיה הזאת. ובכל מקרה אם המערכת שלכם כתובה ב Node.JS שווה להכיר שיש פיתרון יותר מוצלח שמשתלב באפליקציה והוא המודול wait-on.

מודול wait-on מאפשר לקבל רשימה של משאבים מכל מיני סוגים ויודע לחכות עד שכל המשאבים יהיו באוויר. הסוגים הנתמכים כוללים: קובץ, נתיבי רשת (http ו https), חיבור tcp וחיבור socket.

בהמתנה לקובץ התוכנית תמשיך רק כשהקובץ יהיה במקום שאתם צריכים אותו. בהמתנה לנתיבי רשת הקוד ישלח בקשת HTTP Head ויחכה לקבל תשובת HTTP 200, ובהמתנה ל tcp או socket הוא יחכה שיווצר חיבור. אפשר לשלוט גם בכל הפרמטרים של ההמתנה באמצעות אופציות שמעבירים לפונקציה ושווה להסתכל בתיעוד כדי לראות את הרשימה המלאה.

הנה דוגמה פשוטה לתוכנית Node.JS שמחכה ששרת redis יעלה על localhost ורק אז מתחברת אליו ומעלה ערך של מפתח בשם count:

const redis = require('redis');
const port = 3000;

const waitOn = require('wait-on');
const resources = [
  'tcp:localhost:6379'
];

waitOn({ resources }, () => {
  // here everything's ready
    console.log('ready!');

    const client = redis.createClient({
      port      : 6379,
      host      : 'localhost',
    });

  client.get('count', (err, count) => {
    if (err) return next(err);
    console.log(`count = ${count}`);
    client.incr('count', () => {
        client.quit();
    });
  });
});

דוקר: מה שלא צריך לדעת

25/09/2021

הקובץ הבא הוא קובץ Dockerfile תקין לגמרי שאני משתמש בו במצב פיתוח לפרויקט Node.JS:

FROM node:16

WORKDIR /app

הוא עובד אם מצמידים אליו קובץ docker-compose.yml שממפה את תיקיית הפרויקט לתיקיית /app על הקונטיינר, מגדיר את הפקודה להרצה ופותח את הפורטים המתאימים.

אבל המרחק בין ה Dockerfile הקטנצ'יק שלי ל Dockerfile אמיתי של מצב פרודקשן הוא עצום. בתור התחלה דוקרפייל של פרודקשן צריך באמת לבנות אימג', הוא צריך להעתיק את הקבצים הרלוונטים מהפרויקט לתיקיית /app, הוא צריך להתקין את התלויות בתוך האימג' כדי שלא ייווצרו בעיות תאימות, הוא צריך לאתחל קבצים שאפשר לאתחל בעת יצירת האימג', הוא צריך לכלול קובץ Entrypoint מסודר שיוכל להריץ קוד איתחול לקונטיינר כשזה עולה, ועוד ועוד.

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

ובחזרה לשאלה מהכותרת- בניית Dockerfile מסודר לפרודקשן למערכת דורשת מיומנות והשקעת זמן. זו בדיוק מה ש Devops יודעים ואוהבים לעשות. כמפתחים שבונים מערכת אנחנו לא צריכים לדעת הכל על Docker, אבל כן יש כמה דברים שבלעדיהם אנחנו אבודים:

  1. חשוב להכיר את ה "מה" - מה זה אומר לרוץ בתוך קונטיינר, מה המגבלות של הקונטיינר.

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

  3. חשוב לדעת לעבוד עם docker-compose, כי זה כלי שבעיקר מתכנתים משתמשים בו כדי ליצור לעצמנו סביבת פיתוח מהירה למערכת שמורכבת מהרבה סרביסים. צריך לדעת לכתוב את קבצי ההגדרות שלו ולהבין איזה אפשרויות קסטומיזציה קיימות לכל סרביס. אתם רוצים להיות מסוגלים לכתוב קובץ הגדרות שיהיה כמה שיותר קרוב למערכת הפרודקשן האמיתית, כולל העברת בקשות נכנסות דרך Nginx וכתיבה ללוג מרכזי.

  4. חשוב להבין איך נשמר מידע רגיש, מה זה Secrets ואיך לנהל אותם.

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

  6. חשוב להבין איך קונטיינרים מתקשרים אחד עם השני ועם העולם החיצון, על איזה רשת הם מדברים ואיך פותרים בעיות DNS או בעיות תקשורת בין קונטיינרים במצב פיתוח.

תכסו את ששת הסעיפים האלה ולאנשי ה Devops אצלכם יהיו חיים הרבה יותר קלים כשהם יבואו להפוך את קבצי ה Dockerfile הקטנים שתכתבו לקבצי הגדרות אמיתיים, ואת קבצי ה docker-compose.yml הקטנים שלכם להגדרות קוברנטס.

העלאת סרביס מ docker-compose ל Kubernetes

07/09/2021

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

המשך קריאה

טיפ דוקר: איך להוסיף קוד איתחול לאימג'

05/09/2021

בעבודה עם דוקר אנחנו פוגשים אימג'ים טובים ב Dockerhub שהיו יכולים להיות מושלמים אם רק היה מקום להוסיף עוד כמה שורות קוד לפני שהקונטיינר עולה. אם זה פיצ'ר שגם אתם צריכים, תשמחו לשמוע שהוא די פשוט למימוש בעזרת שני הטריקים הבאים.

המשך קריאה

מה ההבדל בין `docker compose` ל `docker-compose` ?

28/08/2021

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

הסיפור הרשמי של שתי הפקודות הוא פשוט - הכלי docker-compose הוא סקריפט פייתון, הוא היה שם קודם וקוד המקור שלו (אם מצאתי נכון) הוא כאן: https://github.com/docker/compose.

לעומתו docker compose מסומן בתור compose גירסה 2, כתוב ב go וקוד המקור שלו (בהנחה שמצאתי נכון) הוא כאן: https://github.com/docker/compose-cli.

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

  1. גירסה 2, כלומר docker compose תומכת ביצירת קונטיינרים בעננים של מייקרוסופט ואמזון באמצעות אינטגרציה מובנית. מספיק ליצור context, לכתוב docker compose up ויש לכם קונטיינרים באוויר ב ECS או ACI.

  2. על הלינוקס שלי, בגירסה 2 כשאני מעלה מכונות עם docker compose up ואז לוחץ Ctrl+C אני נשאר תקוע על איזה הודעת "המכונות עצרו". בגירסת docker-compose up לחיצה על Ctrl+C עוצרת את המכונות אבל גם מחזירה אותי למסוף.

  3. גירסה 1 כלומר docker-compose תומכת בכתיבת הלוג ל syslog ונראה שגירסה 2 לא. במילים אחרות קובץ קומפוז כזה:

version: '3'
services:
  worker:
    image: // image
    logging:
      driver: syslog
      options:
        syslog-address: "udp://XXX.papertrailapp.com:XXXX"
        tag: "{{.Name}}/{{.ID}}"

שמצאתי כאן בסטאק אוברפלו ובמקומות נוספים יעבוד עם docker-compose up אבל לא יעבוד עם docker compose up.

  1. קומפוז גירסה 2 (הגירסה בלי המקף) תומך ב Apple Silicon.

אם אתם מכירים או מצאתם הבדלים נוספים בין הכלים ורוצים לשתף אני אשמח לשמוע בתגובות.

שלושה דברים שאהבתי בקוברנטס (ואחד שממש לא)

20/08/2021

בתיאוריה, תרגום פרויקט מ Docker Compose ל Kubernetes הוא הדבר הכי קל בעולם: מפעילים סקריפט אחד שמתרגם את קבצי ה docker-compose.yml לקבצי ההגדרות של k8s, מעלים את התוצאה לקלאסטר קיים ונהנים מהחיים. המציאות לקחה לי קצת יותר עבודה ודרשה מספר שינויים בקוד המערכת - אבל בצד החיובי סיימתי עם קוד טוב יותר מזה שהיה בהתחלה וגם הבנה טובה יותר של הארכיטקטורה. מהתהליך גם למדתי לחבב מספר דברים בקוברנטס, והנה השלושה המרכזיים:

המשך קריאה

העלאת קונטיינר דוקר לענן של AWS

24/03/2021

אתמול פירסמתי מדריך על העלאת קונטיינר דוקר לענן של Microsoft, וכבר כמה אנשים שאלו איך אפשר לפרסם את אותו קונטיינר בענן הגדול יותר של AWS. אז פתחתי את ה AWS CLI וציפיתי להתפנק עם כל הכלים המושקעים של אמזון. לצערי זאת לא היתה החוויה שקיבלתי אבל בכל זאת הצלחתי להגיע לענן וגם לשרוד כדי לספר.

גירסאות עדכניות של docker כוללות מנגנון שנקרא Context. הקונטקסט מספר לדוקר איפה המכונות שצריכות להפעיל את הקונטיינרים יושבות ובאיזה מנגנון אנחנו מדברים עם מכונות אלה (מי ה Orchestrator). לדוקר יש אינטגרציה מובנית עם AWS ועם Azure Devops כך שכל מה שצריך בשביל להעלות קונטיינרים לענן הוא:

  1. ליצור Context ולחבר אותו ל Azure Devops או ל AWS.

  2. להשתמש בפקודות Docker רגילות כדי להפעיל קונטיינרים.

  3. באופן אוטומטי הקונטיינרים ישתמשו בקונטקסט שהגדרנו ויעלו בענן במקום אצלי על המכונה.

ניסוי? בשמחה. אבל קודם נצטרך לעבור את מדיניות האבטחה הדרקונית של אמזון. בתוך קונסולת הניהול יש להיכנס לשירות שנקרא IAM וליצור משתמש חדש. יש במדריך כאן רשימה של כל ההרשאות שאותו משתמש צריך. אני לא מצאתי איך לעשות copy/paste בקלות להרשאות אז בחרתי מרשימת ה Policies מה שנראה לי קשור. סך הכל בשביל משתמש שמצליח להעלות קונטיינרים לענן שלהם הוספתי את ה Policies הבאים:

AmazonEC2FullAccess
IAMFullAccess
AWSAgentlessDiscoveryService
ElasticLoadBalancingFullAccess
AmazonEC2ContainerRegistryFullAccess
AWSCloudMapFullAccess
CloudWatchLogsFullAccess
AmazonECS_FullAccess
AmazonRoute53FullAccess
AWSCloudFormationFullAccess

השיטה של להוסיף הרשאות עד שזה עובד מתאימה לפוסט, אבל לאפליקציה אמיתית כן שווה להתאמץ ולמצוא את ההרשאות המינימליות שאתם צריכים.

אחרי יצירת המשתמש וההרשאות אתם צריכים לחבר אותו לדוקר קומפוז שלכם ובשביל זה נכנסים (עדיין ב IAM) לטאב Security Credentials, בוחרים Create Access Key ומשאירים את החלון עם ה Access Key ID וה Secret פתוח.

משם פותחים בחלון חדש מסוף שורת פקודה ומפעילים משם:

docker context create ecs myawscontext

המילה האחרונה בשורה היא שם ה Context ואפשר לשנות אותה לכל שם שאתם רוצים. בתגובה דוקר ישאל אתכם איך להתחבר ל AWS ואתם תוכלו להגיד לו שיש לכם אסימון גישה ותדביקו את המזהה והסוד במסוף.

בסיום התהליך יהיה לכם Context חדש עבור AWS ואפשר לוודא שזה עבד עם:

$ docker context ls
NAME                TYPE                DESCRIPTION                               DOCKER ENDPOINT               KUBERNETES ENDPOINT   ORCHESTRATOR
default             moby                Current DOCKER_HOST based configuration   unix:///var/run/docker.sock                         swarm
myawscontext *      ecs                 (eu-north-1)

שימו לב לשורה השניה מסוג ecs ועם האזור שבחרתם ליצור בו את המכונות.

ההבדל השני בין AWS ל Azure הוא שבעבודה עם AWS חייבים להשתמש ב Docker Compose ולא ניתן להעלות קונטיינר בודד. זה לא סוף העולם כי תמיד אפשר ליצור קובץ docker-compose.yml שמתאים לקונטיינר בודד. הנה הדמו שאני העליתי:

version: "3.9"
services:
  web:
    image: "nginxdemos/hello"
    ports:
      - "80:80"

את התוכן שמרתי בקובץ בשם docker-compose.yml בתיקיה חדשה (זה הקובץ היחיד בתיקיה) והמשכתי להעלות אותו לאמזון עם הפקודה:

$ docker compose up

הפקודה נכשלה באקראי מדי פעם על Timeout-ים, אבל גם לפעמים הצליחה, אז אם אתם מפעילים ומקבלים שגיאה שווה להפעיל פעם שניה בשביל לוודא שזו שגיאה אמיתית. כשהכל עובד תוכלו לראות את התוצאה עם:

$ docker compose ps
NAME                                                 SERVICE             STATUS              PORTS
task/mydockerdemo/cdf554598aab4f1fb3f1685bc074de98   web                 Running             mydoc-LoadB-13SBJMOZQ5M30-224910942.us-east-1.elb.amazonaws.com:80->80/http

שכבר נותן לנו את כתובת המכונה שמריצה את הדמו. גלישה לנתיב שמופיע שם תציג דף פתיחה משרת nginx.

אחרי שהכל עבד אפשר להשתמש ב:

$ docker compose down

כדי להוריד את הסרביסים שיצרתם, או להפעיל מחדש docker compose up כדי להעלות גירסה חדשה.

ממש שמחתי לראות את העבודה הטובה שעשו ב Docker כדי לייצר ממשק זהה להעלאת קונטיינרים למספר ספקי ענן. למרות זאת קשה להתעלם מהעובדה שהגישה לשרתים של Azure היתה קלה ומהירה יותר. גם אם נתעלם מנושא בחירת ההרשאות - ב Azure היה לי הרבה יותר קל למצוא את ה Instances שנוצרו בממשק הבקרה שלהם ולראות סטטוס של המכונות דרך ממשק ה Web. זמני המתנה לפקודות docker היו קצרים יותר ולא נתקלתי בכישלונות אקראיים שהסתדרו מעצמם. אם היתה תחרות בין עננים אז לפחות בצד של ממשק עם דוקר הענן של מייקרוסופט מנצח בגדול.

העלאת קונטיינר דוקר לענן של Microsoft

23/03/2021

לפני כמה ימים כתבתי כאן על החשבון החינמי לשנה שפתחתי על Azure Devops, והיום היתה לי הזדמנות לשחק עם עוד מנגנון נחמד של המערכת והוא התקנת קונטיינרים בענן וגיליתי שדוקר כבר עשו בשבילנו את כל העבודה הקשה.

גירסאות עדכניות של docker כוללות מנגנון שנקרא Context. הקונטקסט מספר לדוקר איפה המכונות שצריכות להפעיל את הקונטיינרים יושבות ובאיזה מנגנון אנחנו מדברים עם מכונות אלה (מי ה Orchestrator). לדוקר יש אינטגרציה מובנית עם AWS ועם Azure Devops כך שכל מה שצריך בשביל להעלות קונטיינרים לענן הוא:

  1. ליצור Context ולחבר אותו ל Azure Devops או ל AWS.

  2. להשתמש בפקודות Docker רגילות כדי להפעיל קונטיינרים.

  3. באופן אוטומטי הקונטיינרים ישתמשו בקונטקסט שהגדרנו ויעלו בענן במקום אצלי על המכונה.

ניסוי? בשמחה. בהנחה שיש גם לכם חשבון ב Azure Devops תוכלו להפעיל:

docker login azure

ואז דוקר יפתח דפדפן ויבקש מכם להתחבר לחשבון ה Azure Devops שלכם.

אחרי הכניסה מפעילים את הפקודה:

docker context create aci mycontext

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

$ docker context ls
NAME                TYPE                DESCRIPTION                               DOCKER ENDPOINT               KUBERNETES ENDPOINT   ORCHESTRATOR
default             moby                Current DOCKER_HOST based configuration   unix:///var/run/docker.sock                         swarm
mycontext *         aci                 yo@centralus

אפשר לראות שיש לי שני קונטקסטים על המכונה: הראשון נקרא default והשני נקרא mycontext.

הפקודה הבאה אומרת לדוקר להתחיל להשתמש בקונטקסט השני:

$ docker context use mycontext

ועכשיו אנחנו מוכנים להרים קונטיינר לענן. הפקודה הבאה יצרה קונטיינר דמו של שרת node.js:

$ docker run -d -p 80:80 nginxdemos/hello

לפי האימג' nginxdemos/hello. כמובן שאתם יכולים לבחור כל Docker Image שתרצו ויש גם תמיכה מלאה ב docker-compose (עליה עוד נדבר בפוסט המשך בעתיד). לפקודת ה run יכול לקחת קצת זמן לסיים לרוץ אבל בסוף זה נגמר ואפשר לראות עם docker ps מה קיבלנו:

$ docker ps
CONTAINER ID        IMAGE               COMMAND             STATUS              PORTS
boring-aryabhata    nginxdemos/hello                        Running             52.182.228.142:80->80/tcp

כניסה לכתובת ה IP של המכונה תציג את דף הכניסה מהאימג', שהוא דף Hello World של שרת nginx.

שכבות ב Docker Images

13/05/2019

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

המשך קריאה

בניית אימג'ים עם Dockerfile

08/05/2019

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

בואו נראה כמה דוגמאות בהן אנחנו בונים יישומים מסוגים שונים בתור אימג'ים כדי שלאחרים יהיה קל להתקין ולהפעיל את היישומים שלנו.

המשך קריאה