כל הוראה היא שכבה
Docker בונה את הדימוי בשכבות. כל הוראה (RUN, COPY, ADD) יוצרת שכבה חדשה, וכל שכבה נשמרת בקאש לפי ה-hash של מה שנכנס אליה. שינוי בהוראה אחת מבטל את הקאש שלה ושל כל ההוראות שאחריה.
מכאן הכלל היחיד שצריך לזכור:
מסדרים את ההוראות מהדבר שהכי פחות משתנה לדבר שהכי משתנה.
הדוגמה הקלאסית
# ❌ כל שינוי בקוד מתקין מחדש את כל התלויות
COPY . .
RUN npm ci
# ✅ התלויות מותקנות מחדש רק כשה-package.json משתנה
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
ההבדל בפועל: שנייה מול שתי דקות בכל בנייה. בפרויקט עם אלף בנייות ביום, זה ההבדל בין צינור עבודה לצינור סתום.
איך רואים את השכבות
docker history myapp:dev
כל שורה היא שכבה עם גודל. רואים מיד איזו הוראה נפחה את האימג'.
שלוש טכניקות שמזיזות את המחט
1. איחוד פקודות בכל שכבה. כל RUN נפרד הוא שכבה. עדיף:
RUN apk add --no-cache git curl && \
rm -rf /var/cache/apk/*
2. לא להשאיר זבל בשכבה. אם מחקתם קובץ בשכבה מאוחרת, הוא עדיין קיים בשכבה הקודמת — הדימוי לא מתכווץ. מחקו באותה פקודה שבה יצרתם.
3. שימוש ב-BuildKit mount לקאש של מנהל החבילות.
RUN --mount=type=cache,target=/root/.npm npm ci
זה שומר את קאש npm בין בנייות בלי שהוא ייכנס לאימג' בכלל.
מתי לבטל קאש בכוונה
לפעמים רוצים בנייה נקייה: אחרי עדכון תלויות מערכת, או כשמשהו מסריח. docker build --no-cache . עושה את זה. אבל אם אתם צריכים אותו בכל בנייה — כנראה שהבעיה היא בסדר ההוראות, לא בקאש.
הערה על COPY . .
הוא תמיד מפיל את הקאש, גם אם השתנה רק קובץ לוג. לכן .dockerignore טוב הוא גם כלי ביצועים, לא רק היגיינה.