Skip to main content

Що таке .dockerignore і навіщо він потрібен?

.dockerignore це найпростіший Docker-файл, який ти колись напишеш. Він також один з найвпливовіших: контролює, що шиппиться на daemon під час docker build і запобігає трьом класам багів (повільні білди, роздуті image, витоки secret).

Теорія

TL;DR

  • Build context = директорія, що передаєш у docker build. Усе в ній заливається на daemon перед стартом білду.
  • Без .dockerignore УСЯ директорія шиппиться: node_modules, .git, build-output, IDE-config, secret, усе.
  • Синтаксис .dockerignore як у gitignore з одним винятком: лише один ** glob-патерн, без negation у деяких Docker-версіях.
  • Має бути у корені build context (поряд з Dockerfile).
  • Три причини, чому важить: швидкість білду, розмір image, безпека.

Швидкий приклад

# .dockerignore node_modules .git dist build *.log *.tmp .env* .DS_Store Dockerfile* .dockerignore README.md .vscode .idea coverage tests *.test.js *.spec.ts

З цим docker build . шле лише реальні source-файли. Без цього типовий Node-проект шле 200+ MB node_modules і .git-папку повну історії, що не треба в image.

Синтаксис патернів

# Коментар *.log # будь-який .log на будь-якій глибині logs/ # директорія logs і усе всередині !important.log # негація: включити, навіть якщо вище матчить **/temp # будь-яка 'temp'-директорія на будь-якій глибині src/**/*.test.js # test-файли будь-де під src/

Важливо: порядок важить. Пізніші правила перекривають раніші. Бери ! для винятків.

Чому кожен рядок важить

node_modules (і еквіваленти)

Найбільший контрибутор. Часто сотні MB. Dockerfile все одно перевстановлює через npm ci.

node_modules vendor # PHP / Go / Ruby vendored deps .venv # Python virtualenv __pycache__ # Python bytecode cache *.pyc

.git

Повна git-історія. Часто 50-500 MB на довго-живучих репо. Майже ніколи не потрібно у image.

Виняток: якщо білд потребує git-info (commit SHA через git rev-parse), бери git rev-parse HEAD > VERSION ПЕРЕД білдом, тоді виключай .git і COPY VERSION лише.

Build-output

dist build out target # Rust / Java

Ти перебудовуєш їх всередині image. Жодних причин шиппити host-output.

Secret і локальні config

.env* *.pem *.key secrets.yaml config.local.json

.env, що випадково COPY'ється у image, це витоковий прод-secret.

IDE / редактори / OS-сміття

.vscode .idea .DS_Store # macOS Thumbs.db # Windows *.swp # vim

Тести і dev-артефакти (іноді)

coverage tests # якщо не крутиш тести всередині image *.test.js

Залежить від твого build-flow. Якщо RUN npm test всередині білду (multi-stage), залиш тести. Інакше скіпай.

Типові помилки

Без .dockerignore взагалі

$ docker build -t myapp . [+] Building 0.0s => transferring context: 248.7MB ← занадто велико

248 MB upload на daemon кожен білд. Додай .dockerignore і дивися, як це впаде до кількох MB.

Забути, що COPY . . копіює усе у context

dockerfile
COPY . . # копіює усе, включно з .env, якщо не у .dockerignore

Якщо .env існує у build context І не у .dockerignore, він приземляється у image. Аудитори обожнюють їх знаходити.

Класти .dockerignore у піддиректорію

Docker читає .dockerignore лише з кореня build context. Build context це те, що передаєш у docker build (зазвичай . = поточна директорія). .dockerignore у ./api/.dockerignore ігнорується, поки не білдиш через docker build ./api.

Negate-патерни і дивуватися

* !Dockerfile !src/**

Цей allow-listy патерн крихкий і order-dependent. Простіше брати deny-list правила (node_modules, .git, dist).

Забути, що файл впливає і на multi-stage білди

Build context визначається раз для docker build, незалежно від того, скільки стейджів у Dockerfile. Кожен стейдж бачить той самий filtered context.

Inspecting що у build context

bash
# Подивитися, що буде заливатися docker build --no-cache --progress=plain -t test . 2>&1 | head -10 # Шукай рядок "transferring context" # Або dry-run з ручним tar tar --exclude-from=.dockerignore -cf - . | wc -c

Якщо колись думав «чому мій build context 1 GB?», du -sh */ .[^.]*/ 2>/dev/null | sort -h з кореня білду каже, що найбільше.

Реальне застосування

  • Кожен прод-проект: має .dockerignore. Дефолтний starter для будь-якого нового репо це додати його перед Dockerfile.
  • Monorepos: ще важливіше, директорії node_modules per package можуть давати гігабайти.
  • CI білди: великий build context, помножений на кожен CI-прогон, це реальні disk- і time-витрати.
  • Cloud builds (Cloud Build, GitHub Actions): шиппять context у remote-сервіс. Менший = швидше (і дешевше).

Питання для поглиблення

Q: Що відбувається, якщо я COPY файл, виключений .dockerignore?


A: Docker падає з «file not found», з точки зору Docker файл не у context.

Q: Чим .dockerignore відрізняється від .gitignore?


A: Той самий синтаксис (переважно). .gitignore контролює, що git відстежує. .dockerignore контролює, що docker build шле на daemon. Незалежні файли, часто перетинаються, але не ідентичні (наприклад, сам .git не ігнорує .gitignore, але має ігнорувати .dockerignore).

Q: Чи .dockerignore впливає на docker run -v mount?


A: Ні. .dockerignore build-time only. Bind mount при run-time монтує те, на що показуєш, незалежно від .dockerignore.

Q: Чи може .dockerignore бути в іншому місці?


A: Так, з BuildKit можна використати # syntax=docker/dockerfile:1.7 і файл <Dockerfile>.dockerignore. Корисно, коли одне репо має кілька Dockerfile з різними ignore-потребами. Дефолт це все ще .dockerignore у корені build context.

Q: (Senior) Чому строго залочений .dockerignore може сповільнити dev-workflow?


A: Якщо виключає речі, що Dockerfile очікує знайти (test-фікстури, dev-config), COPY падає, або image поводиться неправильно. Поширений підступ: виключати .git, поки build-крок потребує git log для версії. Рішення: попередньо розрахувати значення (git rev-parse HEAD > VERSION перед білдом), передати через --build-arg, або per-Dockerfile .dockerignore варіант для dev vs прод.

Приклади

Типовий .dockerignore Node-проекту

# Залежності (перебудовуються всередині image) node_modules bower_components # Build-output (перебудовуються всередині image) dist build out # Логи і temp *.log npm-debug.log* yarn-debug.log* yarn-error.log* # Environment / secret .env .env.* !.env.example # лишити приклад для документації # Редактори .vscode .idea *.swp .DS_Store # Тести / coverage coverage *.test.js *.spec.ts # Docker Dockerfile* .dockerignore compose*.yaml # VCS .git .gitignore .gitattributes

Результат: docker build context, що містить рівно потрібні source-файли. Build-час падає драматично; image менший; secret не витікає.

Замір до і після

bash
# До .dockerignore $ time docker build -t myapp . [+] Building ... transferring context: 312.4MB real 1m45s # Додати .dockerignore з node_modules і .git $ time docker build -t myapp . [+] Building ... transferring context: 4.2MB real 0m22s

Два рядки у .dockerignore, 5x швидший білд. Цифри множаться між CI-прогонами.

Multi-Dockerfile проект (BuildKit)

repo/ ├── api/ │ ├── Dockerfile │ └── Dockerfile.dockerignore # специфічно для api ├── web/ │ ├── Dockerfile │ └── Dockerfile.dockerignore # специфічно для web └── .dockerignore # shared baseline

З # syntax=docker/dockerfile:1.7 BuildKit авто-завантажує <Dockerfile>.dockerignore, якщо є, інакше root .dockerignore. Корисно, коли api і web мають різні exclusion-потреби.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.

Коментарі

Ще немає коментарів