Docker Ninja – Telegram
Docker Ninja
1.05K subscribers
30 photos
91 links
По всем вопросам обращаться к @nesudimov_eu
Download Telegram
Ты работаешь с микросервисной архитектурой и хочешь мониторить потребление ресурсов всех запущенных контейнеров в реальном времени. Какая команда покажет 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
Ты хочешь отследить, какие файлы изменились в контейнере после его запуска для диагностики проблем. Какая команда поможет тебе увидеть все изменения в файловой системе?
Anonymous Quiz
12%
docker logs container_name
23%
docker inspect container_name
49%
docker diff container_name
17%
docker history container_name
🤚Фэйсконтроль ты не пройдешь🤚

Ох уж эта вечная дилемма: либо даешь процессу полный root и он творит что хочет с системой, либо запускаешь под обычным юзером и потом часами разбираешься, почему оно не может открыть сокет или примонтировать том.

Но Linux-боги подумали об этом и придумали штуку под названием capabilities - это как талончик на краковскую колбасу VIP-пропуска в ночной клуб, только для процессов!🎫
В контексте докера это реализуется через флаг --cap-add!

Что такое capabilities?
Это набор конкретных привилегий, которые можно выдавать процессам по отдельности. Типа "можешь биндить на привилегированные порты", "можешь менять сетевые настройки", но при этом никакого доступа к дискам.

Как юзать --cap-add:
# Даем возможность биндиться на порты < 1024
docker run --cap-add NET_BIND_SERVICE nginx

# Позволяем менять сетевые настройки
docker run --cap-add NET_ADMIN my-vpn-app

# Даем права на работу с raw сокетами (для ping например)
docker run --cap-add NET_RAW alpine ping google.com


А кейсы то будут,?
➡️ Nginx/Apache часто нужно биндиться на 80/443 порт → NET_BIND_SERVICE
➡️ VPN-приложения ковыряются в маршрутизации → NET_ADMIN
➡️ Мониторинг-тулзы читают системную инфу → SYS_ADMIN
➡️ Бекапы работают с файловыми атрибутами → DAC_OVERRIDE

Хоба и ты умеешь гибко настраивать безопасность, а кому нужны лишние терки с коллегами из ИБ по середине рабочей недели?? Правильно - никому. Поэтому смело ставь реакт за полезняшку!🤑

🥷 Docker Ninja 🥷
Please open Telegram to view this post
VIEW IN TELEGRAM
1👍14🔥63🤔2
В процессе разработки ты изменил код и пересобрал образ. При запуске контейнера заметил, что приложение работает в старой директории. Какую директива Dockerfile надо использовать, чтобы установить рабочую директорию?
Anonymous Quiz
6%
RUN cd /app
75%
WORKDIR /app
13%
ENV PATH=/app
5%
COPY . /app
🍔Управляем объемами🚽

В мире IT существует довольно много идеалистических концепций, внедрив которые их авторы обещают золотые горы и кисельные берега. Одной из таких концепций является 12-factor app, в целом, и ее подпункт stateless, в частности. Штука очень полезная, но реальность оказывается гораздо прозаичнее, поскольку есть приложения и сервисы, которые по своей природе stateful.

Поэтому, запускаете вы контейнеры на проде или на тесте, всегда задавайте себе вопрос, а не stateful ли данное приложение. И если ответ положительный, то к контейнеру необходимо примонтировать том.

Возвращаясь к посту про типы томов, в compose, естественно, есть ручки, дергая за которые мы можем подключить тома к любому из описанных нами контейнеров.

Синтаксис:
services:
postgres:
image: postgres:15
volumes:
- pgdata:/var/lib/postgresql/data # именованный том
- ./logs:/var/log # bind mount

volumes:
pgdata: # объявляем именованный том


Кастомизируем именованный том:
services:
app:
...

volumes:
cache:
name: my_new_volume
driver: local
driver_opts:
...


Здесь мы указали:
Имя, с которым будет создан новый том. В компоузе будем обращаться по имени cache
Тип драйвера
Различные опции для этого драйвера

Зачем это нужно?
➡️ Базы данных: данные переживут пересоздание контейнера
➡️ Development: bind mount для live reload кода
➡️ Логи: складываем на хост для мониторинга
➡️ Кэши: если используем build в компоузе, то сваливаем в том кэш загруженных зависимостей

Volumes - это база любого production compose-файла. А в связке с build получается полноценная среда разработки! Поэтому знать, понимать и помнить это просто необходимо!

А на сегодня у меня все. Как обычно, не забывайте ставить свои реакты и всем хороших выходных.🍺

🥷 Docker Ninja 🥷
Please open Telegram to view this post
VIEW IN TELEGRAM
1👍8🍾4🔥32
🍋 Easy peasy lemon squeezy 🍋

Однажды составлял Dockerfile для приложения с distroless образом. Конечно же я не был подписан на ваш любимый и неповторимый Docker Ninja, поэтому, по незнанию воткнул там вот такое CMD go run main.go. И конечно же, контейнер не поднялся...

Ну что может пойти не так?

И тут я могу вам сказать, что мы наконец докопались до самой сути серии постов, отвечающих на вопрос подписчика. Напомню, вопрос звучал так: работает ли shell и exec формы в Distroless и From Scratch образах?

Давайте проникнемся shell и exec формами команд!
Когда ты пишешь CMD go run main.go (shell форма), Docker пытается выполнить это через /bin/sh -c "go run main.go". А тк в distroless и FROM scratch образах нет ни shell, ни /bin/sh, то запускаться это дело не будет.
#  Факапится в distroless
FROM gcr.io/distroless/static
COPY myapp /
CMD myapp --config prod.yaml

# Получаем:
exec /bin/sh: no such file or directory


Что же с exec формой?
#  А она работает как часы
FROM gcr.io/distroless/static
COPY myapp /
CMD ["/myapp", "--config", "prod.yaml"]


То же самое касается ENTRYPOINT:
#  Провал
ENTRYPOINT ./start.sh

# Успех
ENTRYPOINT ["./start.sh"]


Так же и с RUN. Если вы вдруг удумали собирать что-то в distroless/scratch образах (господь вам в помощь, отчаянные) его так же пишем в exec форме.

Но как же тогда работает exec?
Если что distroless, что scratch гол-ы, как сокол-ы, то как же вообще в нем запускается хоть что либо??
Тут все просто! Настолько просто что мы аж растянули это на 3 поста!😐 Так как контейнер это всего лишь процесс, то exec форма работает изнутри как системный вызов execve.

Запомни правило:
В минималистичных образах всегда используй exec форму ["команда", "аргумент1", "аргумент2"] вместо shell формы команда аргумент1 аргумент2.

Теперь твои контейнеры будут стартовать с загадочными ошибками, а не падать! 😄

Ставь свои реакты, если уже тоже наступал на эти грабли! Если не наступал тоже ставь! А на этом я откланяюсь и хорошей вам рабочей недели!

🥷 Docker Ninja 🥷
Please open Telegram to view this post
VIEW IN TELEGRAM
1👍15🔥62
В твоем проекте есть Docker Compose файл с несколькими сервисами. Ты хочешь проверить, корректно ли составлен файл перед запуском. Какую команду использовать?
Anonymous Quiz
26%
docker compose validate
32%
docker compose check
22%
docker compose config
20%
docker compose verify
👍6🔥3
🧙‍♀️Берём укропу, потом кошачью... 25 картошек, 17 ... ведро воды и ..., охапку дров - компоуз готов 🧙‍♀️

Все совпадения случайны! Ни один ПМ-м ни капли не похож на демогоргона. В среду все девопсы думают исключительно о работе и только о работе!

Среда Маленькая пятница. Неделя подошла к своей медиане, а ты уже душой на даче в Лосево, смотришь с котом под пледом новый сезон Очень странных дел... Но тут тебя затягивает в Изнанку очередной звонок Димы Горгина - PM-а: 'Слушай, а можешь быстренько поднять staging версию приложения? Только там порты другие нужны, и базу отдельную, и логи в другое место... А две?!'

А потом еще и продакшн со своими капризами подтянется! 😅

Хорошая новость - не нужно плодить docker-compose файлы! На такой случай юзаем override файлы для гибкого слияния конфигураций.


ДА, Docker Compose умеет накладывать конфигурации друг на друга не привлекая внимания санитаров:
# docker-compose.yml (база)
version: '3.8'
services:
app:
image: myapp:latest
ports:
- "3000:3000"

# docker-compose.override.yml (автоматом подхватывается)
version: '3.8'
services:
app:
ports:
- "8080:3000" # перезапишет базовый порт
environment:
- NODE_ENV=development


Для прода создаем отдельный файл:
# docker-compose.prod.yml
version: '3.8'
services:
app:
environment:
- NODE_ENV=production
deploy:
replicas: 3


Зачем заморачиваться?
Потому что без дублирования кода ты получаешь элегантное управление окружениями! Одна база, куча вариаций.
➡️Dev-у нужны дебаг порты? - Пжалста.
➡️Staging-у отдельная БД? - Легко.
➡️Проду кластер из трёх реплик? - Да не вопрос!

🍲А теперь заряжаем наш котел всеми нужными ингредиентами:🍲
docker compose -f docker-compose.yml -f docker-compose.prod.yml up

Хоба, и видишь финальный результат слияния!

Кстати, помнишь наши посты про depends_on и volumes? Все эти секции тоже отлично переопределяются через override файлы!

Теперь у тебя есть конфиг на все случаи жизни, а у меня твой реакт, репост и хорошее настроение! 🎉

🥷 Docker Ninja 🥷
Please open Telegram to view this post
VIEW IN TELEGRAM
1🔥15👍8😁42
В production среде твой контейнер потребляет слишком много RAM и влияет на другие сервисы. Контейнер уже запущен. Как ограничить память без остановки?
Anonymous Quiz
70%
Использовать команду docker update --memory
15%
Перезапустить контейнер с флагом --memory
11%
Изменить файлы в /sys/fs/cgroup/ вручную
5%
Остановить контейнер и пересобрать образ
🚽Метим территорию правильно🚽

Вчера копался в старом legacy-проекте и наткнулся на докерфайл с директивой MAINTAINER. А звучит то как мейн-тей-нер! Сразу вспомнил школу, как классе эдак в пятом все друг друга спрашивали, а какая у тебя роспись и ты как маститый райтер достаешь маркер и портишь школьное иммущество свои личным тэгом.

Тем не менее, культура подписывать свое творение существует и у нас в ИТ-шке. Поэтому, если не хочешь прослыть лицемерной мгазью, которая лезет к нам в ИТ, давай посмотрим как же правильно подписывать докерфайлы.


FROM ubuntu:14.04
MAINTAINER "Вася Пупкин <vasya@example.com>"
RUN apt-get update


Бац, и все подумали, что мы теперь настоящие DevOps-ы!

Но Docker давно похоронил эту директиву и пометил как deprecated еще в версии 1.13. Почему? А цимес в том, что MAINTAINER была слишком специфичной — только для автора, и всё. А что если нужно добавить версию, описание, ссылки на документацию?

Тут на смену приходит LABEL 📝
Правильный современный подход использовать LABEL:
FROM ubuntu:22.04
LABEL maintainer="vasya@example.com"
LABEL version="1.0.0"
LABEL denoscription="Мой крутой микросервис"
LABEL org.opencontainers.image.source="https://github.com/vasya/my-app"


Зачем это нужно? 🤔

Когда у тебя в продакшне крутится сотня контейнеров, а в 3 утра что-то упало — метаданные твой спаситель! Быстро понять, кто автор, какая версия, где исходники. Плюс, многие CI/CD системы парсят эти лейблы для автоматизации.
Ну а уж если ты на досуге что-то разрабатываешь, публикуя на гитхаб, то сам бог велел пометить, что именно ты придумал очердной экстравагантный способ поднимать дженкинс в контейнере.

Ставь свой бесценный реакт, если не метишь там, где не положено и делаешь это всегда только по ветру!

🥷 Docker Ninja 🥷
Please open Telegram to view this post
VIEW IN TELEGRAM
👍175🔥3
📋 Это мой кандидатский сценарий. Слушай devops, слушай пролетарий 📋

Однажды мы с вам касались вопроса, как работают docker registry и там я вкинул парочку слов о неких манифестах и что мол это как инструкция к тому, как из слоев собрать полноценный образ.

Сегодня же хочу рассказать вам об этих самых манифестах чуточку подробнее, ведь если задуматься, то именно этот механизм, помимо прочих его фич, позволяет нам при пулле образа собрать все слои воедино, хотя мы качаем их по сети, где может случиться все что угодно!


Что это за зверь такой?
Manifest — это метаданные образа в JSON формате (этакий паспорт).
Когда ты делаешь docker pull, сначала качается именно манифест, а потом уже слои по списку.
# Можно даже посмотреть на структуру
docker manifest inspect nginx:latest


Хлобысь и ты видишь всю подноготную: архитектуру, размеры слоев, их SHA256 хеши и порядок восстановоения слоев.
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.index.v1+json",
"manifests": [
...,
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"size": 841,
"digest": "sha256:af320b2df7e2bde33bd04165a7cad0b510f6ce6461d8f2e9c906fc77f99a8d21",
"platform": {
"architecture": "unknown",
"os": "unknown"
}
},
...
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"size": 2292,
"digest": "sha256:7de350c1fbb1f7b119a1d08f69fef5c92624cb01e03bc25c0ae11072b8969712",
"platform": {
"architecture": "arm64",
"os": "linux",
"variant": "v8"
}
},
...
]
}


Нафига тут SHA256?
Выше я уже упоминал про некие хэши в формате SHA256. Вся соль в том, что каждый слой образа идентифицируется по SHA256 хешу его содержимого. То есть, на основе изменений высчитывается хэш. Изменился файл - изменился хеш - новый слой! По такому же принципу работает git.
Далее мы обращаемся при push-е/pull-e через Registry API v2. Он, в свою очередь использует Content Addressable Storage, чтобы разрулить все вопросики с образами.

При push/pull операциях:
1. 📤 Клиент шлет манифест в registry
2. 🔍 Registry проверяет целостность по хешам
3. 📦 Качаются только недостающие слои
4. Проверка SHA256 на каждом шаге

Ну они и напридумывали! А зачем это все?
➡️ Безопасность: невозможно подменить слой незаметно
➡️ Эффективность: дедупликация слоев между образами
➡️ Целостность: гарантия, что скачанное = загруженному
➡️ Версионирование: каждый образ — уникальная комбинация слоев

Теперь понимаешь, почему твой nginx всегда одинаковый на dev и prod!? 🎯

Все, больше не гружу ваши понедельничные головушки сложной информацией. Перевариваем и до встречи в следующих постах!

А ты ставь реакт, если тоже любишь копаться в архитектурных деталях! Пиши свои вопросы в директ канала! И продуктивного тебе рабочего/учебного дня!!!

🥷 Docker Ninja 🥷
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12👏3🔥2