בפיתוח יישומי node.js בתוך דוקר אני בדרך כלל אוהב להריץ npm install
בתוך הקונטיינר בתור פקודה ראשונה כשהוא עולה, לפני שמפעיל את היישום האמיתי שלי, וככה אני יודע שתמיד כל החבילות יהיו במקום. ייתרון נוסף של הגישה הזאת הוא שאפשר להשתמש ב image שכבר קיים בדוקרהאב ב docker-compose.yml וזה חוסך לנו את שלב בניית האימג'.
אבל לפעמים יש מצבים שגם בסביבת פיתוח אנחנו רוצים ליצור יותר וודאות ולהתקין את כל החבילות רק בזמן בניית האימג', כדי לקבל קונטיינרים שיעלו יותר מהר וכדי שאפשר יהיה להרים קונטיינר מאפס גם בלי חיבור לרשת.
במצב כזה האינטואיציה הראשונה היא להריץ npm install
מתוך ה Dockerfile, למשל משהו כזה:
FROM node:18
WORKDIR /app
COPY package*.json .
RUN npm install
CMD ["node", "myapp.js"]
ואז אני משתמש בקובץ docker-compose.yml
כזה כדי למפות את התיקיה הנוכחית (סביבת הפיתוח) פנימה לקונטיינר:
version: "3.9"
services:
app:
build: .
command: sleep infinity
volumes:
- .:/app
הבעיה? מיפוי כזה גורם לדוקר להשתמש ב node_modules
של המחשב המארח במקום בספריות שהותקנו בזמן יצירת האימג'. אם המחשב המארח שלי הוא בפלטפורמה אחרת, משתמש בגירסה אחרת של node, או פשוט מוחק או משנה חלק מהמודולים אחרי שהאימג' נוצר, אז עלולות להיות לי בעיות.
זה קורה בגלל שמיפוי ה Volume של תיקיית הפיתוח מסתיר את קבצי ה node_modules
שהתקנתי בעת בניית האימג'.
ביום רגיל היינו פשוט מתקינים את המודולים באימג' בתיקיה אחרת, לדוגמה בריילס יש אפשרות להגדיר איפה הוא יחפש את הג'מים, ואנחנו יכולים להתקין אותם מחוץ לעץ קוד המקור. גם בפייתון אני יכול ליצור סביבה וירטואלית איפה שאני רוצה, ואני לא חייב לשים אותה דווקא במקום שמיפוי ה Volume ידרוס אותה.
ב node לצערי זה לא אפשרי ו node_modules
ממש חייב להיות בתוך תיקיית קוד המקור שלכם, ולכן הפיתרון דורש קצת יותר יצירתיות: אנחנו מגדירים Volume
בתוך ה Dockerfile
שיכיל את המודולים המותקנים, ואז בהרצה ממפים את אותו volume על גבי המיפוי של תיקיית המקור - כך יוצא שה volume החדש של ה node_modules
מסתיר את המיפוי של node_modules
מתיקיית המקור, שהוא בעצמו הסתיר את תיקיית node_modules
של האימג' (שעכשיו ריקה כי ההתקנה היתה על ה Volume).
קוד? ברור. זה ה Dockerfile - התוספת היחידה היא פקודת volume:
FROM node:18
WORKDIR /app
COPY package*.json .
VOLUME /app/node_modules
RUN npm install
CMD ["node", "myapp.js"]
כשמפעילים את האימג', פקודת run תעתיק את התוכן של node_modules
שנמצא באימג' לתוך ה Volume החדש, והוא ימופה לתוך /app/node_modules
במקום התיקיה מהמחשב המארח.
הטריק היחיד לזכור כאן הוא שהתיקיה node_modules
חייבת להיות קיימת על המחשב המארח בתיקיית הפיתוח (יכולה להיות ריקה), אחרת המיפוי נכשל והקונטיינר לא יעלה.