Docker Ninja – Telegram
Docker Ninja
1.05K subscribers
30 photos
91 links
По всем вопросам обращаться к @nesudimov_eu
Download Telegram
🧅 Сидит дед, в сто шуб одет 🧅

Мы уже не раз касались такой темы как 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 контейнера, чтобы знать в какую папку стучать
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 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)
Кстати, тул умеет выгружать эту аналитику в sbom файл, который потом можно грузануть в какую-нибудь систему аналитики уязвимостей (типа Dependency Track или DefectDojo)
🔹 Интегрируется с 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
🛁 Keep calm and chill like it's the last time 🛁

Коллеги, думаю многие из вас пережили на этой неделе рабочую 6-дневку. Я, как и вы, не исключение. Поэтому, предлагаю хорошенько почилить и порасслабонить в доставшиеся нам за такие муки, 3 выходных дня!!! А в среду снова начнем бой во славу кровавого энтерпрайза!🤼‍♂️

С праздником всех причастных! 🎉

А не причастным "Счастливого хеллоуина" и побольше сладостей!🎃
Please open Telegram to view this post
VIEW IN TELEGRAM
3🍾2👍1
🐧Некоторым птицам не место в контейнере. У них слишком яркие перья🐧

Ну и заключительный пост про файлы 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 🥷
👍213😁2👎1
Твоя команда разрабатывает микросервис и хочет минимизировать размер итогового образа. У вас есть этап компиляции Go-приложения и этап создания runtime-образа. Какой подход поможет создать максимально облегчит итоговый образ?
Anonymous Quiz
7%
Использовать один большой Dockerfile с установкой всех зависимостей
20%
Добавить .dockerignore для исключения ненужных файлов
3%
Использовать базовый образ ubuntu:latest вместо alpine
70%
Применить multi-stage builds для разделения сборки и runtime
🧙🏾‍♂️Раньше был я Гендальф Серый. А теперь я Саша Белый 🧙‍♂️

Пятница. 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👍72😁1
🙈If i ignore it, it will go away🙈

Бывает, собираешь образ, а 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
©️Хорошие копируют, великие воруют🦝

Мы с вами уже довольно хорошо погрузились в различные аспекты написания Dockerfile. Все может казаться просто и лаконично: захотел закинуть сорсы в контейнер - используй COPY; надо закинуть что-то из архива - втыкай ADD; слишком тяжелая сборка - пробуй multistage build.

Но порою, меняешь одну строчку в коде, а сборка после этого идет 15 минут. Как будто Docker внезапно забыл про все свои слои и решил: "А давайте-ка пересоберем все с нуля!"

А ведь проблема часто кроется в бездумных COPY в твоем Dockerfile.

Так шо сегодня рассмотрим best practices для COPY, которые превратят твою сборку в пушку ракету!🚀

Правило №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🔥52😁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
👍42🆒2
😱 Игра началась 😱

Помнится, в самом начале моего девопсерского пути меня долго мучал вопрос, нафига нужен 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👍82
Вопросошная

В
пятницу вечером один из подписчиков задал вопрос к последнему посту: работает ли 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: понедельники без тяжести💊

Сидишь ты значит, деплоишь в продакшен очередной микросервис, потирая красные от недосыпа глаза, как вдруг, ИБ-ешник присылает отчет 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? Ставь 🔥!

Этот пост лишь маленькая часть серии из 3-х постов про distroless и scratch образы. Ссылки на все части ты можешь найти в этом посте.

🥷 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 (где хранить слои образов)
⚙️ Log driver (куда сливать логи контейнеров)
⚙️ Registry mirrors (зеркала для быстрого пулла)
⚙️ Insecure registries (приватные реестры без HTTPS)

Простейший пример:
{
"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🔥72
При работе с 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
🛸Ограничиваем мыслительные ресурсы🛸

Представь, заапускаешь ты такой на проде "безобидный" контейнер с 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👍154😁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 к ядру хоста. Как будто бы мы запустили этот самый бинарь прямо на хосте.

Но не забывай:
➡️ Бинарник должен быть статически скомпилирован (все зависимости внутри)
➡️ Никакого shell для дебага - только логи
➡️ Нет стандартных утилит типа 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👍102😁2