🧅 Сидит дед, в сто шуб одет 🧅
Мы уже не раз касались такой темы как Layered File System в Docker (6, 19, 41, 64). И уже хорошо знаем, что благодаря этому механизму обеспечивается приличное количество фич, упрощающих процесс разработки. Особенно в условиях новомодного микросервисного ада!🤯
Но как же это реализовано изнутри?? Давайте глянем.
Как мы уже говорили, есть image layer и writable layer. Если быть честным, то и того и другого слоев будет несколько. Просто то, что было сделано в базовом образе, называется image layer и по своей сути является неизменным (read-only). Writable layer же - это чистое поле для экспериментов и каждое изменение фиксируется в нем как отдельный слой со своим хэш-ом - названием (наподобие commit hash в git-е).
Как хранится?
Docker хранит эти слои как отдельные директории в /var/lib/docker/driver_name/
За всем этим колдунством стоит так называемый
CoW:
Операции I/O реализованны через стратегию Copy-on-Write
🔹Read: файл читается из read-only слоев
🔹Write: когда мы пытаемся изменить файл в базовом образе, он копируется в верхний writable слой, и изменяется именно копия исходного файла
🔹Delete: создается "whiteout" файл, скрывающий нижний слой
Хеширование и дедупликация:
Каждый слой идентифицируется SHA256 (об этом чутка упоминал выше). Одинаковые слои физически хранятся один раз, независимо от количества образов!
Зачем эта сложность?
🔹Пересобираются только измененные слои
🔹Скачиваются только новые слои
🔹Один слой на много образов = оптимизация хранения
Теперь ты знаешь, почему docker pull иногда показывает "Already exists" - Docker просто переиспользует существующие слои!
Ставь 🤯, если ничего не понятно, но очень интересно и ты просто требуешь пояснительной бригады в виде длино-поста с красивыми картинками!🤓
🥷 Docker Ninja 🥷
Мы уже не раз касались такой темы как Layered File System в Docker (6, 19, 41, 64). И уже хорошо знаем, что благодаря этому механизму обеспечивается приличное количество фич, упрощающих процесс разработки. Особенно в условиях новомодного микросервисного ада!🤯
Но как же это реализовано изнутри?? Давайте глянем.
Как мы уже говорили, есть image layer и writable layer. Если быть честным, то и того и другого слоев будет несколько. Просто то, что было сделано в базовом образе, называется image layer и по своей сути является неизменным (read-only). Writable layer же - это чистое поле для экспериментов и каждое изменение фиксируется в нем как отдельный слой со своим хэш-ом - названием (наподобие commit hash в git-е).
Как хранится?
Docker хранит эти слои как отдельные директории в /var/lib/docker/driver_name/
/var/lib/docker/overlay2/
├── abc123.../diff # Слой с изменениями
├── def456.../diff # Еще один слой
├── ......
└── merged/ # Итоговая FS
За всем этим колдунством стоит так называемый
UnionFS. Storage driver-ы склеивают слои через механизм union mount. Верхние слои "перекрывают" нижние. Если файл меняется в нескольких слоях - побеждает верхний. Таким образом обеспечивается актуальность изменений.CoW:
Операции I/O реализованны через стратегию Copy-on-Write
🔹Read: файл читается из read-only слоев
🔹Write: когда мы пытаемся изменить файл в базовом образе, он копируется в верхний writable слой, и изменяется именно копия исходного файла
🔹Delete: создается "whiteout" файл, скрывающий нижний слой
Хеширование и дедупликация:
Каждый слой идентифицируется SHA256 (об этом чутка упоминал выше). Одинаковые слои физически хранятся один раз, независимо от количества образов!
Зачем эта сложность?
🔹Пересобираются только измененные слои
🔹Скачиваются только новые слои
🔹Один слой на много образов = оптимизация хранения
Теперь ты знаешь, почему docker pull иногда показывает "Already exists" - Docker просто переиспользует существующие слои!
Ставь 🤯, если ничего не понятно, но очень интересно и ты просто требуешь пояснительной бригады в виде длино-поста с красивыми картинками!🤓
🥷 Docker Ninja 🥷
🤯15🔥1
Ты разворачиваешь веб-приложение в контейнере и хочешь убедиться, что оно корректно запускается и отвечает на запросы. Какая директива Dockerfile поможет Docker автоматически проверять состояние приложения?
Anonymous Quiz
8%
CMD curl -f http://localhost:8080/health || exit 1
12%
RUN curl -f http://localhost:8080/health || exit 1
71%
HEALTHCHECK --interval=30s CMD curl -f http://localhost:8080/health || exit 1
9%
EXPOSE 8080 && curl -f http://localhost:8080/health
🕳 Если долго смотреть в cgroup, cgroup начинает смотреть в тебя 🕳
По вашим заявкам в посте про cgroups продолжаем углубляться в кроличью нору дальше. Что ж, мои дорогие любители покопаться в кишочках, давайте приступим. 🙏🏻
🔍 Находим нужную cgroup
Ищем ID контейнера, чтобы знать в какую папку стучать
🤿 Теперь ныряем в его метрики:
💾 Метрики памяти
🖥 CPU метрики
💿 Block I/O метрики
🥶Наш любимый freezer
Как мы уже разобрали в первом посте про cgroups, docker stats и флаг --memory работают именно с этими файлами.
Но, линукс не линукс, если бы все было так просто... Данная файловая структура актуальна для
🥷 Docker Ninja 🥷
По вашим заявкам в посте про cgroups продолжаем углубляться в кроличью нору дальше. Что ж, мои дорогие любители покопаться в кишочках, давайте приступим. 🙏🏻
🔍 Находим нужную cgroup
Ищем ID контейнера, чтобы знать в какую папку стучать
ddocker inspect test-container | grep Id
🤿 Теперь ныряем в его метрики:
💾 Метрики памяти
# Текущее использование памяти
cat /sys/fs/cgroup/memory/docker/CONTAINER_ID/memory.usage_in_bytes
# Лимит памяти контейнера
cat /sys/fs/cgroup/memory/docker/CONTAINER_ID/memory.limit_in_bytes
# Детальная статистика памяти
cat /sys/fs/cgroup/memory/docker/CONTAINER_ID/memory.stat
# Пиковое потребление памяти
cat /sys/fs/cgroup/memory/docker/CONTAINER_ID/memory.max_usage_in_bytes
# Swap использование
cat /sys/fs/cgroup/memory/docker/CONTAINER_ID/memory.memsw.usage_in_bytes
🖥 CPU метрики
# Общее время CPU в наносекундах
cat /sys/fs/cgroup/cpuacct/docker/CONTAINER_ID/cpuacct.usage
# CPU usage по ядрам
cat /sys/fs/cgroup/cpuacct/docker/CONTAINER_ID/cpuacct.usage_percpu
# Детальная статистика CPU
cat /sys/fs/cgroup/cpu/docker/CONTAINER_ID/cpu.stat
# Время в user/system mode
cat /sys/fs/cgroup/cpuacct/docker/CONTAINER_ID/cpuacct.stat
# CPU throttling статистика
cat /sys/fs/cgroup/cpu/docker/CONTAINER_ID/cpu.throttling_data
💿 Block I/O метрики
# Статистика по чтению/записи
cat /sys/fs/cgroup/blkio/docker/CONTAINER_ID/blkio.throttle.io_service_bytes
# IOPS счетчики
cat /sys/fs/cgroup/blkio/docker/CONTAINER_ID/blkio.throttle.io_serviced
# Время обслуживания I/O
cat /sys/fs/cgroup/blkio/docker/CONTAINER_ID/blkio.time
🥶Наш любимый freezer
# Текущее состояние контейнера
cat /sys/fs/cgroup/freezer/docker/CONTAINER_ID/freezer.state
# PID родительского процесса контейнера
cat /sys/fs/cgroup/freezer/docker/CONTAINER_ID/cgroup.procs
# Список всех потоков (threads), принадлежащих контейнеру (в старых версиях ядра).
cat /sys/fs/cgroup/freezer/docker/CONTAINER_ID/tasks
Как мы уже разобрали в первом посте про cgroups, docker stats и флаг --memory работают именно с этими файлами.
Но, линукс не линукс, если бы все было так просто... Данная файловая структура актуальна для
cgroups v1. Поэтому, ставь 🔥 если хочешь узнать про то, как работать с файлами cgroups v2.🥷 Docker Ninja 🥷
🔥20😁4🤣1🙈1
🧼 Моя оборона! 🧼
Я где-то слышал, что практически каждый второй Docker-образ, будь то Docker Hub или же приватные registry содержит критические уязвимости, но мы же продолжаем пулить их как ни в чем не бывало!
Проверять это утверждение я конечно же не буду, но не удивлюсь, если это окажется правдой... Да и как предлог заколлабиться с безопасниками и реализовать что-то новенькое для своего портфолио хороший!😏
Так шо сегодня разберем с вами Docker Scout - встроенный сканер безопасности докер образов aka "нет пробития"! Ага, прикиньте, есть такой. Я сам однажды офигел!!!
🔐 Запускаем сканирование
После docker login можем сканировать приватные образы:
📊 Детальный анализ
💡Что еще умеет?
🔹 Scout анализирует не только OS-пакеты, но и зависимости языков (Node.js, Python, Java, а это уже вполне себе такой Software Composition Analisys)
Кстати, тул умеет выгружать эту аналитику в sbom файл, который потом можно грузануть в какую-нибудь систему аналитики уязвимостей (типа Dependency Track или DefectDojo)
🔹 Интегрируется с Docker Desktop для GUI-анализа
🔹 Поддерживает webhook-и для автоматических уведомлений
Теперь об обороне стало думать легче, тылы прикрыты, так что можно спокойно ронять мыло в общественных местах💪🏻
Ставь 😎 если уже представляешь, как твой security-отдел будет тебя благодарить!
🥷 Docker Ninja 🥷
Я где-то слышал, что практически каждый второй Docker-образ, будь то Docker Hub или же приватные registry содержит критические уязвимости, но мы же продолжаем пулить их как ни в чем не бывало!
Проверять это утверждение я конечно же не буду, но не удивлюсь, если это окажется правдой... Да и как предлог заколлабиться с безопасниками и реализовать что-то новенькое для своего портфолио хороший!😏
Так шо сегодня разберем с вами Docker Scout - встроенный сканер безопасности докер образов aka "нет пробития"! Ага, прикиньте, есть такой. Я сам однажды офигел!!!
🔐 Запускаем сканирование
# Сканируем локальный образ
docker scout cves nginx:latest
# Получим что-то типа такого
✔️ Image stored for indexing
┌───────────────┬────────┬──────────┬───────────────┐
│ CVE │ Score │ Severity │ Package │
├───────────────┼────────┼──────────┼───────────────┤
│ CVE-2023-1234 │ 8.2 │ HIGH │ libc6 │
│ CVE-2023-5678 │ 6.5 │ MEDIUM │ openssl │
│ CVE-2023-9012 │ 4.3 │ LOW │ zlib │
└───────────────┴────────┴──────────┴───────────────┘
Summary: 15 vulnerabilities found (3 critical, 5 high, 4 medium, 3 low)
# Или прямо при сборке
docker scout cves local://my-app:v1.0
После docker login можем сканировать приватные образы:
docker scout cves registry.company.com/my-private-app:latest
📊 Детальный анализ
# Сканирование с рекомендациями по исправлению
docker scout recommendations nginx:latest
# Анализ по критичности (только HIGH и CRITICAL)
docker scout cves --severity high,critical node:18-alpine
# Сравнение двух версий образа
docker scout compare nginx:1.20 --to nginx:1.21
# Анализ конкретного слоя
docker scout cves --layer-details postgres:15
## Вывод довольно читабельный
Layer 1: base image (debian:bullseye)
├── CVE-2023-1234 (HIGH) in libc6
└── CVE-2023-5678 (MEDIUM) in openssl
Layer 3: application dependencies
├── CVE-2023-9012 (CRITICAL) in python3.9
└── CVE-2023-3456 (HIGH) in postgresql-15
💡Что еще умеет?
🔹 Scout анализирует не только OS-пакеты, но и зависимости языков (Node.js, Python, Java, а это уже вполне себе такой Software Composition Analisys)
🔹 Интегрируется с Docker Desktop для GUI-анализа
🔹 Поддерживает webhook-и для автоматических уведомлений
Теперь об обороне стало думать легче, тылы прикрыты, так что можно спокойно ронять мыло в общественных местах💪🏻
Ставь 😎 если уже представляешь, как твой security-отдел будет тебя благодарить!
🥷 Docker Ninja 🥷
😁7😎4👍2🤣1
Твое приложение в контейнере потребляет слишком много памяти и часто падает. Ты хочешь ограничить использование RAM до 2024 МБ. Какой флаг использовать при запуске?
Anonymous Quiz
26%
docker run --memory 2024m my-app
33%
docker run --mem-limit 2024m my-app
6%
docker run --ram 2024m my-app
34%
docker run --max-memory 2024m my-app
😁4🙈3🌚1🗿1
Коллеги, думаю многие из вас пережили на этой неделе рабочую 6-дневку. Я, как и вы, не исключение. Поэтому, предлагаю хорошенько почилить и порасслабонить в доставшиеся нам за такие муки, 3 выходных дня!!! А в среду снова начнем бой во славу кровавого энтерпрайза!🤼♂️
С праздником всех причастных!
А не причастным "Счастливого хеллоуина" и побольше сладостей!
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3🍾2👍1
🐧Некоторым птицам не место в контейнере. У них слишком яркие перья🐧
Ну и заключительный пост про файлы cgroups (первый, второй).
Начиная с версии ядра Linux 4.5 мы взаимодействуем с cgroups v2, который стал более структурированный, чем первая версия.
Выглядит это дело так:
🎯Определяем директорию нашего контейнера:
🧠Memory
⚡️CPU
💾I/O
🧊Freezer
🚨Контроль процессов
В большинстве случаев, вы столкнетесь именно с
Как видим, изменения почти не коснулись метрик памяти. Но вот по остальным есть довольно много нюансов. И если копать в эту сторону еще глубже, то мы, в любом случае, выйдем на линукс. А это уже выходит за рамки тематики данного канала. Да и сам линукс - это уж очень глубокая штука, которую надо разбирать в отдельном канале.
Но, если есть желание закопаться еще глубже, то ставьте ваши 👍🏻 на пост. 25+ 👍🏻 станут для меня знаком, что стоит копнуть еще и в линукс.
🥷 Docker Ninja 🥷
Ну и заключительный пост про файлы cgroups (первый, второй).
Начиная с версии ядра Linux 4.5 мы взаимодействуем с cgroups v2, который стал более структурированный, чем первая версия.
Выглядит это дело так:
🎯Определяем директорию нашего контейнера:
# Запускаем контейнер
docker run -d --name test-app nginx
# Путь к cgroup контейнера
CGROUP_PATH="/sys/fs/cgroup/system.slice/docker-$(docker inspect -f '{{.Id}}' test-app).scope"
🧠Memory
# Текущее потребление памяти
cat $CGROUP_PATH/memory.current
# 52428800 (50MB в байтах)
# Максимальное потребление с момента старта
cat $CGROUP_PATH/memory.max
# max (без лимитов)
# Детальная статистика памяти
cat $CGROUP_PATH/memory.stat
# anon 12582912
# file 8388608
# kernel_stack 32768
⚡️CPU
# Статистика использования CPU
cat $CGROUP_PATH/cpu.stat
# usage_usec 1542340
# user_usec 890123
# system_usec 652217
# Помечен ли процесс контейнера как "с минимальным приоритетом"
cat $CGROUP_PATH/cpu.idle
# Текущий вес процесса
cat $CGROUP_PATH/cpu.weight
# 100
💾I/O
# Статистика ввода-вывода
cat $CGROUP_PATH/io.stat
# 8:0 rbytes=1048576 wbytes=4096 rios=64 wios=8
# Текущий I/O pressure
cat $CGROUP_PATH/io.pressure
# some avg10=0.00 avg60=0.00 avg300=0.00 total=0
🧊Freezer
# Проверяем текущее состояние
cat $CGROUP_PATH/cgroup.freeze
# 0 (разморожен)
# Замораживаем контейнер без docker cli
echo 1 > $CGROUP_PATH/cgroup.freeze
# После выполнения попробуй зайти в терминал контейнера
# Проверяем состояние
cat $CGROUP_PATH/cgroup.freeze
# 1 (заморожен)
# Размораживаем обратно
echo 0 > $CGROUP_PATH/cgroup.freeze
🚨Контроль процессов
# Список PID-ов в контейнере
cat $CGROUP_PATH/cgroup.procs
# 1234
# 1235
# События cgroup (убийства OOM, миграции и проч.)
cat $CGROUP_PATH/cgroup.events
# populated 1
# frozen 0
В большинстве случаев, вы столкнетесь именно с
cgroups v2. Поэтому полезно углубиться именно в этот вариант каталогов.Как видим, изменения почти не коснулись метрик памяти. Но вот по остальным есть довольно много нюансов. И если копать в эту сторону еще глубже, то мы, в любом случае, выйдем на линукс. А это уже выходит за рамки тематики данного канала. Да и сам линукс - это уж очень глубокая штука, которую надо разбирать в отдельном канале.
Но, если есть желание закопаться еще глубже, то ставьте ваши 👍🏻 на пост. 25+ 👍🏻 станут для меня знаком, что стоит копнуть еще и в линукс.
🥷 Docker Ninja 🥷
👍21❤3😁2👎1
Твоя команда разрабатывает микросервис и хочет минимизировать размер итогового образа. У вас есть этап компиляции Go-приложения и этап создания runtime-образа. Какой подход поможет создать максимально облегчит итоговый образ?
Anonymous Quiz
7%
Использовать один большой Dockerfile с установкой всех зависимостей
20%
Добавить .dockerignore для исключения ненужных файлов
3%
Использовать базовый образ ubuntu:latest вместо alpine
70%
Применить multi-stage builds для разделения сборки и runtime
🧙🏾♂️Раньше был я Гендальф Серый. А теперь я Саша Белый 🧙♂️
Встречай, единственная и не повторимая команда
🎯 Базовое использование:
Ограничиваем прожорливый контейнер:
Можно обновлять сразу несколько контейнеров:
Зачем это нужно?
Как ты уже понял, команда идеальна для тушения пожаров на проде, когда нужно быстро отреагировать на нагрузку. Видишь, что контейнер начал тормозить — добавляешь ресурсы. Заметил утечку памяти — урезаешь лимиты до фикса. Все без простоев!
Помнишь лимиты памяти и политики рестарта? Их, к слову, тоже можно менять налету с помощью данной команды!
Как обычно, ставь свои реакты. Так я буду знать, что пост зашел, а мне стоит продолжать делиться знаниями в подобном формате!
🥷 Docker Ninja 🥷
Пятница. 18:00. Ты уже одной ногой в уличном ботинке. Но тут в телегу предательски прилетает алерт - одному из сервисов в контейнере не хватает памяти...
Заходишь на хост. Видишь, что свободной памяти еще выше крыши. Но метрики показывают другое.
Смотришь в inspect контейнера и видишь, что при запуске дали ограничение в половину всей памяти хоста. Ребутать(пык, мык) или стопать контейнер не вариант - инстанс сервиса только один, так что прод сразу встанет, а ты ляжешь под обильными плевками коллег, решивших расслабиться в вечер пятницы.
Что же делать? Как расширить память запущенного контейнера?
Встречай, единственная и не повторимая команда
docker update! Она позволяет менять ресурсы работающих контейнеров без их остановки.🎯 Базовое использование:
Ограничиваем прожорливый контейнер:
# Урезаем память до 512MB
docker update --memory=512m my-app
Можно обновлять сразу несколько контейнеров:
# Апдейтим все контейнеры с префиксом web
docker update --memory=256m web-app-1 web-app-2 web-app-3
Зачем это нужно?
Как ты уже понял, команда идеальна для тушения пожаров на проде, когда нужно быстро отреагировать на нагрузку. Видишь, что контейнер начал тормозить — добавляешь ресурсы. Заметил утечку памяти — урезаешь лимиты до фикса. Все без простоев!
Помнишь лимиты памяти и политики рестарта? Их, к слову, тоже можно менять налету с помощью данной команды!
Как обычно, ставь свои реакты. Так я буду знать, что пост зашел, а мне стоит продолжать делиться знаниями в подобном формате!
🥷 Docker Ninja 🥷
🔥33👍7❤2😁1
🙈If i ignore it, it will go away🙈
И тут самое время заюзать, возможно знакомый вам из git-а, формат ignore файлов. В нашем случае
Этот файл работает по тому же принципу, что и
Например он может выглядеть вот так:
Паттерны:
Можно юзать wildcard или более сложные паттерны
Зачем это нужно? 🤔
🔹Во-первых, размер! Build Context — это все, что Docker отправляет демону для сборки. Чем меньше мусора, тем быстрее передача и меньше итоговый образ.
🔹Во-вторых, безопасность. Никому не нужны секреты, приватные ключи или конфиги в production образе.
🔹В-третьих, кэширование слоев работает эффективнее без постоянно меняющихся логов и временных файлов.
Фиксируем,
А на сегодня у нас все и помните: игнорировать можно все что угодно, кроме отсуствия реактов на постах от любимых подписчиков!😔
Так что, обязательно ставьте!😏
🥷 Docker Ninja 🥷
Бывает, собираешь образ, а Docker берет и тащит в контейнер все что видит в рамках контекста. Node_modules на 300 МБ, .git со всей историей коммитов, IDE конфиги, логи разработки, личную коллекцию мемов из папкиdicks_pics/🍌 ...
В итоге, образ раздувается до неприличных размеров, а во время сборки можно спокойно уходить на обед.
Конечно, для кого-то это может быть преимуществом, но мы то с вами ответственные работники, правда ведь?!😏
И тут самое время заюзать, возможно знакомый вам из git-а, формат ignore файлов. В нашем случае
.dockerignore.Этот файл работает по тому же принципу, что и
.gitignore, только вместо исключения файлов из репозитория, он исключает их из контекста сборки Docker. Создаешь файл .dockerignore в корне проекта рядом с Dockerfile (руками или через init) и прописываешь паттерны того, что Docker должен проигнорировать.Например он может выглядеть вот так:
node_modules/
*.log
.git
.env
Паттерны:
Можно юзать wildcard или более сложные паттерны
# Исключить все .md файлы, кроме README.md
*.md
!README.md
# Исключить все временные файлы
**/temp/
**/*.tmp
Зачем это нужно? 🤔
🔹Во-первых, размер! Build Context — это все, что Docker отправляет демону для сборки. Чем меньше мусора, тем быстрее передача и меньше итоговый образ.
🔹Во-вторых, безопасность. Никому не нужны секреты, приватные ключи или конфиги в production образе.
🔹В-третьих, кэширование слоев работает эффективнее без постоянно меняющихся логов и временных файлов.
Фиксируем,
.dockerignore проверяется относительно Build Context, который вы указываете в docker build!А на сегодня у нас все и помните: игнорировать можно все что угодно, кроме отсуствия реактов на постах от любимых подписчиков!
Так что, обязательно ставьте!
🥷 Docker Ninja 🥷
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8🔥3👏2🤯1
Ты работаешь с микросервисной архитектурой и хочешь мониторить потребление ресурсов всех запущенных контейнеров в реальном времени. Какая команда покажет CPU, память и сетевую активность?
Anonymous Quiz
12%
docker ps --format table
47%
docker stats
35%
docker top --all
6%
docker system df
©️Хорошие копируют, великие воруют🦝
пушку ракету!🚀
Правило №1: Зависимости копируем отдельно
Частный случай для следующего правила, но стоит рассмотреть его отдельно
Делаем так:
Правило №2: От редко изменяемых к часто изменяемым
Docker кэширует слои сверху вниз. Как только один слой меняется, все нижние пересобираются
Правило №3: Дружи с .dockerignore
Смотрим пост про .dockerignore и проникаемся
Правило №4: Multi-stage для чистых артефактов
Собираем в одном образе, забираем результат в другом
Зачем это нужно?
Представь: у тебя Node.js приложение, ты фиксишь баг в main.js. При правильном COPY пересоберется только один последний слой. При неправильном - весь
В реальности это означает разницу между сборкой за 10 секунд и за 10 минут. Особенно критично для CI/CD пайплайнов, где каждая секунда на счету. Да и в момент разработки дико бесит, что нужно долго ждать пересборку...
Так что, воруйте все что здесь написано и ставьте ваши реакции!!!
🥷 Docker Ninja 🥷
Мы с вами уже довольно хорошо погрузились в различные аспекты написания Dockerfile. Все может казаться просто и лаконично: захотел закинуть сорсы в контейнер - используй COPY; надо закинуть что-то из архива - втыкай ADD; слишком тяжелая сборка - пробуй multistage build.Так шо сегодня рассмотрим best practices для COPY, которые превратят твою сборку в
Но порою, меняешь одну строчку в коде, а сборка после этого идет 15 минут. Как будто Docker внезапно забыл про все свои слои и решил: "А давайте-ка пересоберем все с нуля!"
А ведь проблема часто кроется в бездумных COPY в твоем Dockerfile.
Правило №1: Зависимости копируем отдельно
Частный случай для следующего правила, но стоит рассмотреть его отдельно
# ❌ Плохо - весь контекст копируется сразу
COPY . /app
RUN npm install
Делаем так:
# ✅ Хорошо - сначала только package.json
COPY package*.json /app/
RUN npm install
COPY . /app/
Правило №2: От редко изменяемых к часто изменяемым
Docker кэширует слои сверху вниз. Как только один слой меняется, все нижние пересобираются
COPY requirements.txt /app/ # Меняется редко
RUN pip install -r requirements.txt
COPY some_noscript.sh /noscripts/ # Меняется средне
COPY src/ /app/src/ # Меняется средне
COPY main.py /app/ # Меняется часто
Правило №3: Дружи с .dockerignore
Смотрим пост про .dockerignore и проникаемся
# ❌ Опасно - копируем весь мусор, а в довесок и секреты
COPY . /app
# ✅ Безопасно - фильтруем через .dockerignore
## Создай .dockerignore рядом с Dockerfile:
node_modules
.git
*.log
.env
.idea
.vscode
dist
Dockerfile
.dockerignore
Правило №4: Multi-stage для чистых артефактов
Собираем в одном образе, забираем результат в другом
FROM golang:1.23 AS sdk
...
RUN go build -o /bin/hello ./main.go
FROM scratch
COPY /app/some_file.txt /app/file.txt
COPY --from=sdk /bin/hello /bin/hello
CMD ["/bin/hello"]
Зачем это нужно?
Представь: у тебя Node.js приложение, ты фиксишь баг в main.js. При правильном COPY пересоберется только один последний слой. При неправильном - весь
npm install заново, а это могут быть гигабайты зависимостей!В реальности это означает разницу между сборкой за 10 секунд и за 10 минут. Особенно критично для CI/CD пайплайнов, где каждая секунда на счету. Да и в момент разработки дико бесит, что нужно долго ждать пересборку...
Так что, воруйте все что здесь написано и ставьте ваши реакции!!!
🥷 Docker Ninja 🥷
2👍13🔥5❤2😁2
Твое приложение собирается через multi-stage build. На финальной стадии нужно скопировать только скомпилированные бинарные файлы из промежуточной стадии. Какая директива Dockerfile правильно выполнит эту задачу?
Anonymous Quiz
7%
ADD --from=builder /app/bin /usr/local/bin
10%
RUN cp --from=builder /app/bin /usr/local/bin
9%
WORKDIR --from=builder /app/bin
74%
COPY --from=builder /app/bin /usr/local/bin
👍4❤2🆒2
😱 Игра началась 😱
Помнится, в самом начале моего девопсерского пути меня долго мучал вопрос, нафига нужен
Но как и в случае с "необязательным EXPOSE-ом", тут оказалось не так все просто...
Так что сегодня мы с вами слепим грузина с чемоданом и раз и навсегда разберемся в том, зачем нам
Exec vs Shell форма
Как и с нашим старым знакомцем CMD, у ENTRYPOINT есть две формы записи:
Комбинирование с CMD
А вот тут и начинается вся свистопляска...
Зачем это нужно?
ENTRYPOINT директива далеко не обязательная, но очень полезная. Из самых базовых кейсов можно привести следующие:
1️⃣ Нужно сделать из контейнера самостоятельную софтину.
2️⃣ Когда нужно гарантированно выполнить какой-либо скрипт.
3️⃣ Для правильной обработки сигналов ОС.
Как обычно, ставьте ваши реакты, если хочется побольше таких постов! Всем peace✌🏻 и хороших выходных.
🥷 Docker Ninja 🥷
Помнится, в самом начале моего девопсерского пути меня долго мучал вопрос, нафига нужен
ENTRYPOINT если уже есть CMD? Преимущества второго ведь на лицо - его можно изменить при запуске контейнера.Но как и в случае с "необязательным EXPOSE-ом", тут оказалось не так все просто...
Так что сегодня мы с вами слепим грузина с чемоданом и раз и навсегда разберемся в том, зачем нам
ENTRYPOINT. Но для начала мини обзорчик как это дело функционирует.Exec vs Shell форма
Как и с нашим старым знакомцем CMD, у ENTRYPOINT есть две формы записи:
# Exec форма (рекомендуемая)
ENTRYPOINT ["nginx", "-g", "daemon off;"]
# Shell форма
ENTRYPOINT nginx -g "daemon off;"
# Exec форма работает напрямую с ядром, без лишних оберток — быстрее и надежнее!
Комбинирование с CMD
А вот тут и начинается вся свистопляска...
ENTRYPOINT ["python", "app.py"]
CMD ["--help"]
# docker run myapp → выполнит python app.py --help
# docker run myapp --version → выполнит python app.py --version
# docker run myapp --config prod.conf → выполнит python app.py --config prod.conf
# ENTRYPOINT остается неизменным, а `CMD` становится аргументами по умолчанию!
Зачем это нужно?
ENTRYPOINT директива далеко не обязательная, но очень полезная. Из самых базовых кейсов можно привести следующие:
1️⃣ Нужно сделать из контейнера самостоятельную софтину.
docker run уже запускает нужный нам бинарник, а мы лишь назначаем аргументы в cmd (смотри пример выше)
2️⃣ Когда нужно гарантированно выполнить какой-либо скрипт.
Например делаем контейнер для запуска ансибла (такое практикуют если рабочи комп на винде). В `ENTRYPOINT` ставим скрипт, который грузит зависимые ansible модули из requirements, а в CMD указываем команды ансибла
3️⃣ Для правильной обработки сигналов ОС.
Для этого ставим в `ENTRYPOINT` то что мы собираемся запускать в контейнере (nginx, python, node). В `CMD` ставим его аргументы (my_app.py, server.js). В таком случае, если мы прервем работу контейнера штатными средствами (pause, stop), то сигнал получит не shell, а непосредственно то, что мы указали в `ENTRYPOINT`. А значит, мы получаем более корректное завершение работы нашего приложения.
Как обычно, ставьте ваши реакты, если хочется побольше таких постов! Всем peace✌🏻 и хороших выходных.
🥷 Docker Ninja 🥷
4🔥15👍8❤2
В пятницу вечером один из подписчиков задал вопрос к последнему посту: работает ли shell и exec формы в Distroless и From Scratch образах?
Вопрос хороший и очень интересный для разбора! Тем более, что, скорее всего, многие не знают что такое Distroless и From Scratch.
Давайте разберем этот момент в трех постах:
1️⃣ Что такое Distroless образы?
2️⃣ Что такое from scratch образы?
3️⃣ Как в данных образах запускается что либо, если они совсем пустые?
Please open Telegram to view this post
VIEW IN TELEGRAM
💊ДетралексDistroless: понедельники без тяжести💊
Distroless - это образы без дистрибутива. Звучит как сапожник без сапог, но тем не менее. Внутри только runtime-зависимости и все. Никаких shell-ов(да мы с вами об этом уже говорили - тык, брык), package manager-ов, лишних библиотек. Только то, что нужно твоему приложению для работы.
Сравни сам через docker images:
Результат: образ сжался с 72MB до 2MB! 🎯
Зачем юзать:
💊 Меньше уязвимостей - меньше головной боли с security
💊 Микроскопический размер - быстрее пуллится и стартует
💊 Невозможно зайти в контейнер через shell - дополнительная защита
💊 Отлично сочетается с multi-stage builds
Google предоставляет варианты на любой вкус:
🔹
🔹
🔹
🔹
Подобрать вариант под себя можно вот тут.
Минус только один: дебажить сложнее, так что обрадуйте своих любителей дебажить на проде - лафа закончилась😏
Попробовал Distroless? Ставь 🔥!
Этот пост лишь маленькая часть серии из 3-х постов про distroless и scratch образы. Ссылки на все части ты можешь найти в этом посте .
🥷 Docker Ninja 🥷
Сидишь ты значит, деплоишь в продакшен очередной микросервис, потирая красные от недосыпа глаза, как вдруг, ИБ-ешник присылает отчет Docker Scout с полным набором критических уязвимостей. Половина из них - в пакетах Ubuntu, которые твое приложение вообще никогда не юзает! Curl, wget, package manager - зачем это все в контейнере, если твой бинарник статически слинкован?
Первое что приходит на ум это аппнуть базовый образ - авось все уязвимые пакеты там пропатчены.
Но что, если образ уже последней версии? Будешь создавать свой образ Ubuntu в котором вырежешь все дырявые пакеты? Ну да, вариант, но до очередной уязвимости. А как будешь следить за патчами пакетов и самой ОС?
Как ты понимаешь, проблем тут возникает вагон и маленькая тележка. Но и на такой случай придумали решение
Distroless - это образы без дистрибутива. Звучит как сапожник без сапог, но тем не менее. Внутри только runtime-зависимости и все. Никаких shell-ов(да мы с вами об этом уже говорили - тык, брык), package manager-ов, лишних библиотек. Только то, что нужно твоему приложению для работы.
Сравни сам через docker images:
docker pull ubuntu:20.04
docker pull gcr.io/distroless/static-debian11
docker images
# Было (Ubuntu base)
ubuntu 20.04 b7bab04fd9aa 7 months ago 72.8MB
# Стало (Distroless)
gcr.io/distroless/static-debian12 latest e021002e4213 N/A 2.08MB
Результат: образ сжался с 72MB до 2MB! 🎯
Зачем юзать:
💊 Меньше уязвимостей - меньше головной боли с security
💊 Микроскопический размер - быстрее пуллится и стартует
💊 Невозможно зайти в контейнер через shell - дополнительная защита
💊 Отлично сочетается с multi-stage builds
Google предоставляет варианты на любой вкус:
🔹
/static (без какого-либо рантайма, хорошо подходит для go) 🔹
/java, 🔹
/python, 🔹
/nodejsПодобрать вариант под себя можно вот тут.
Минус только один: дебажить сложнее, так что обрадуйте своих любителей дебажить на проде - лафа закончилась😏
Попробовал Distroless? Ставь 🔥!
🥷 Docker Ninja 🥷
2👍10🔥6🆒3
Какой механизм Linux использует Docker для ограничения ресурсов контейнеров?
Anonymous Quiz
29%
Linux Namespaces
9%
AppArmor
53%
Linux cgroups
9%
chroot
Помните мы с вами однажды рассматривали флаг --validate? Я тут полистал свои старые посты и подумал, не порядок - как конфиг валидировать знаем, а что настроить в этом самом конфиге вообще не в курсе.По дефолту Docker работает с базовыми настройками, а файл daemon.json мы создаем сами, когда нужно что-то подкрутить в Docker Engine под свои нужды.
Таки пришло время разобрать этот момент. Слишком глубоко копать не будем, чтобы не перегреть наши светлые головки в самой середине недели!
Где искать этого неуловимого зверя?
Linux: /etc/docker/daemon.json
Windows: C:\ProgramData\docker\config\daemon.json
macOS: ~/.docker/daemon.json
Если Магомет не идет к горе, то гора не придет и подавно
Но это совсем не про нашу ситуацию, ведь мы с вами проактивные девопсеры!
Как уже говорили выше, если файла нет, смело создаем его сами:
sudo mkdir -p /etc/docker
sudo touch /etc/docker/daemon.json
Что можно настроить?
Простейший пример:
{
"storage-driver": "overlay2",
"log-driver": "json-file"
}После правок обязательно рестартуем демон:
sudo systemctl restart docker
Не забываем провалидировать конфиг - сэкономим нервы!
Зачем это надо?
В продакшене дефолтные настройки часто не катят. Нужно настроить логирование, сменить storage driver под конкретную файловую систему, добавить корпоративные registry. Без daemon.json придется делать это через флаги командной строки и после нескольких раз ты будешь молить бога релизов, чтобы он сжалился над тобой и дал умереть без мучений.
Так что обязательно юзайте и помните, что демонов боятся только те, кто не умеет ими управлять! Ставьте ваши реакты, если понравился пост! А на этом у меня все, хорошего рабочего дня!👌🏻
🥷 Docker Ninja 🥷
Please open Telegram to view this post
VIEW IN TELEGRAM
3👍8🔥7❤2
При работе с Docker Registry ты хочешь загрузить образ ubuntu:20.04 на локальную машину для последующего использования. Какая команда выполнит эту задачу?
Anonymous Quiz
8%
docker download ubuntu:20.04
74%
docker pull ubuntu:20.04
5%
docker fetch ubuntu:20.04
13%
docker get ubuntu:20.04
🛸Ограничиваем мыслительные ресурсы🛸
Как это работает?
В отличие от --cpu-shares (который работал по принципу "кто первый встал, того и тапки"), --cpus дает жесткий лимит в количестве ядер:
Под капотом это всеми любимые Linux cgroups крутят ручки CFS (Completely Fair Scheduler). Контейнер получает определенный time slice и не может его превысить, даже если остальные ядра простаивают!
Кейсы:
• Мультитенантность: изолируем нагрузку между клиентами
• CI/CD: ограничиваем тесты, чтобы не убить билд-агенты
• Микросервисы: гарантируем, что один сервис не захватит все ресурсы
• Debugging: воспроизводим условия слабых серверов локально
А теперь, тряхнем стариной. Если нужно изменить лимит на лету - есть docker update:
А в связке с ограничением памяти получается идеальная изоляция ресурсов!
ПыСы: используй дробные значения осознанно. CPU 0.1 - это реально мало, подходит только для совсем легких задач типа health-check-ов.
Теперь, когда ты умеешь ограничивать мыслительные ресурсы своих подопечных, задача трех тел для тебя станет нипочем, если ты понимаешь о чем я!👽
Как обычно ставь свои пальчики, сердешки и огонечки, чтобы твой покорный слуга все выходные ждал понедельника, дабы написать свой новый пост.🙇
🥷 Docker Ninja 🥷
Представь, заапускаешь ты такой на проде "безобидный" контейнер с ML-моделью, чтобы она отвечала коллегам на сообщения "Сломалось - почини", а он сжирает все 16 ядер и превращает остальные сервисы в овощи?🍆 Или когда один разработчик запустил криптомайнер... тьфу, тесты CPU-интенсивного алгоритма, и вся команда сидит без CI перед важным релизом.
И тут ты потихоньку начинаешь чувствовать мягкий металлический привкус медного тазика, которым накрывается твой пятничный трип по барам на Рубинштейна.🍺
Отставить панику! На помощь приходит флаг --cpus - надежный помощник страждущего под конец недели девопса! 💪
Как это работает?
В отличие от --cpu-shares (который работал по принципу "кто первый встал, того и тапки"), --cpus дает жесткий лимит в количестве ядер:
# Ограничиваем контейнер половиной CPU
docker run --cpus="0.5" nginx
# Даем полтора ядра для тяжелых вычислений
docker run --cpus="1.5" my-ml-model
# Четверть ядра для легких задач
docker run --cpus="0.25" redis
Под капотом это всеми любимые Linux cgroups крутят ручки CFS (Completely Fair Scheduler). Контейнер получает определенный time slice и не может его превысить, даже если остальные ядра простаивают!
Кейсы:
• Мультитенантность: изолируем нагрузку между клиентами
• CI/CD: ограничиваем тесты, чтобы не убить билд-агенты
• Микросервисы: гарантируем, что один сервис не захватит все ресурсы
• Debugging: воспроизводим условия слабых серверов локально
А теперь, тряхнем стариной. Если нужно изменить лимит на лету - есть docker update:
docker update --cpus="2.0" my_container
А в связке с ограничением памяти получается идеальная изоляция ресурсов!
ПыСы: используй дробные значения осознанно. CPU 0.1 - это реально мало, подходит только для совсем легких задач типа health-check-ов.
Теперь, когда ты умеешь ограничивать мыслительные ресурсы своих подопечных, задача трех тел для тебя станет нипочем, если ты понимаешь о чем я!👽
Как обычно ставь свои пальчики, сердешки и огонечки, чтобы твой покорный слуга все выходные ждал понедельника, дабы написать свой новый пост.🙇
🥷 Docker Ninja 🥷
Please open Telegram to view this post
VIEW IN TELEGRAM
1👍15❤4😁4🔥2
Кто плотно сидел на java или dotnet, тот будет приятно удивлен тому, что например в go вся наша вайб-код писанина удобно укомплектовывается в один единственный бинарный файл. И что самое интересное, никаких тебе рантаймов для работы этого бинарника не нужно - все уже положили внутрь!
После таких вот вундервафель может возникнуть закономерный вопрос. А можно в таком случае тот самый ультратонкий distroless (который, напомню, содержит в себе необходимый минимум + runtime) раздеть до самых носков? Чтобы размер нашего образа сводился лишь к размеру получившегося бинаря!
И вот, в продолжение нашей серии постов, встречайте его величество Scratch-образы! Тип образов, в котором не то что оболочки, в нем даже мокрого пятнышка не осталось!
Серьезно, это не шутка.
scratch - это спецобраз без операционной системы, без shell, без /bin/sh, без ВООБЩЕ НИЧЕГО:FROM scratch
COPY ./my-static-binary /
ENTRYPOINT ["/my-static-binary"]
Да как это вообще работает!?
Docker запускает твой бинарник напрямую как PID 1. Никакой магии, просто голый syscall к ядру хоста. Как будто бы мы запустили этот самый бинарь прямо на хосте.
Но не забывай:
ls, cat, ps, да и не нужны они раз shell-а нет😅Зато результат - образы размером в несколько мегабайт вместо гигабайт!
Зачем это нужно?
Идеально для production микросервисов на Go/Rust, которые компилируются в standalone бинарники. Меньше attack surface, быстрее пуллится, меньше места на диске.
Кстати, Scratch идеально подходит для финального образа в multi-stage builds:
# Этап сборки приложения
FROM golang:1.21-alpine AS builder
...
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o /app/main .
# Финальный этап - минимальный образ
FROM scratch
...
COPY --from=builder /app/main .
CMD ["./main"]
А на этом у меня все! Удачного начала дня! И по классике, не забывайте ставить ваши реакты, а то я буду рыдать горькими слезками
🥷 Docker Ninja 🥷
Please open Telegram to view this post
VIEW IN TELEGRAM
3🔥16👍10❤2😁2