惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

小众软件
小众软件
IT之家
IT之家
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Security Archives - TechRepublic
Security Archives - TechRepublic
P
Proofpoint News Feed
C
CERT Recently Published Vulnerability Notes
阮一峰的网络日志
阮一峰的网络日志
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
The Cloudflare Blog
P
Palo Alto Networks Blog
Know Your Adversary
Know Your Adversary
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Cisco Talos Blog
Cisco Talos Blog
L
Lohrmann on Cybersecurity
AWS News Blog
AWS News Blog
J
Java Code Geeks
博客园_首页
Scott Helme
Scott Helme
WordPress大学
WordPress大学
有赞技术团队
有赞技术团队
T
The Exploit Database - CXSecurity.com
Security Latest
Security Latest
V
Visual Studio Blog
Cloudbric
Cloudbric
Jina AI
Jina AI
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
博客园 - 叶小钗
Apple Machine Learning Research
Apple Machine Learning Research
博客园 - 聂微东
人人都是产品经理
人人都是产品经理
A
Arctic Wolf
C
Cybersecurity and Infrastructure Security Agency CISA
S
SegmentFault 最新的问题
The Last Watchdog
The Last Watchdog
SecWiki News
SecWiki News
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
W
WeLiveSecurity
K
Kaspersky official blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Hacker News: Ask HN
Hacker News: Ask HN
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
宝玉的分享
宝玉的分享
Hugging Face - Blog
Hugging Face - Blog
量子位
Google Online Security Blog
Google Online Security Blog
博客园 - Franky
Simon Willison's Weblog
Simon Willison's Weblog
博客园 - 三生石上(FineUI控件)
Recent Commits to openclaw:main
Recent Commits to openclaw:main

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Ещё раз о Docker
magnusroot · 2026-04-29 · via Все публикации подряд на Хабре

Часть 1. Введение

В данной статье хотел бы обобщить знания и опыт, которые накопились за время использования Docker.

И так, без долгих предисловий, в путь по океану наполненному синими китами!

Важно понять, контейнер - это НЕ виртуальная машина. Это лишь обёртка для одного процесса, который может порождать другие процессы. Он использует ядро и память той системы на которой запущен. Он "эфeмерен" и должен создаваться и уничтожаться без боязни и жалости. Контейнер занимает меньше места и ресурсов, чем виртуальная машина, но не может хранить состояния (по умолчанию)

Где Docker поможет, а где нет.

Docker имеет много преимуществ по сравнению со стандартным способом использования ПО.

  • Принятие решения об используемой архитектуре в проекте становится проще, так как приложению теперь без разницы где запускаться;

  • Все приложения упаковываются по стандарту Open Container Initiative;

  • В одном образе находятся нужные файловые системы, зависимости, директории, код;

  • ПО отделяется от “железа”;

  • Накладные расходы за использование технологии невелики.

Но не стоит думать, что Docker станет “серебряной пулей”, которая решит все проблемы и задачи. Docker не заменит и не старается заменить:

  • Виртуальные машины;

  • Облака;

  • ПО для развёртынвания хостов или конфигураций;

  • Среды разработки.

Терминология

Для успешного освоения docker нужно понимать как он устроен, а значит важно не путаться в терминологии.

  1. Клиент Docker - это то, с помощью чего мы можем управлять сервером Docker, вызывается командой docker

  2. Сервер Docker - это команда dockerd для запуска серверного процесса, который собирает и запускает контейнеры через клиент.

  3. Образы Docker - это любые образы, соответствующие стандарту OSI. Они состоят из нескольких слоёв файловой системы и метаданных. В них собраны все файлы, необходимые для выполнения приложения в контейнере.

  4. Контейнер Docker - это контейнер созданный из образа Docker. Один контейнер может существовать только один раз, но из одного образа может быть созданно любое количество контейнеров. Сам термин Docker-контейнер не является точным, так как Docker просто использует функционал операционной системы

  5. Минимальный хост - небольшой образ ОС, который поддерживает хостинг контейнеров.

Среда выполнения

Основной средой выполнения с сертификацией OSI является containerd - высокоуровневая среда по умолчанию в Docker и Kubernetes.

Сама же containerd использует несколько низкоуровневых сред для администрирования и создания контейнеров:

  • runc - используется чаще всего;

  • crun - написана на С;

  • Kata Containers - от Intel, Hyper и OpenStack. Может работать и с контейнерами, и с виртуальными машинами;

  • gVisor - изолированная среда выполнения, реализованная в пространстве пользователя;

  • Nabla Containers - ещё одна изолированная среда выполнения.

Архитектура Docker

Docker очень сложно устроен. Очень. Но нам, обычным пользователям, бояться этого не стоит, так как вся сложность спрятана под капотом, а мы видим лишь стандартную модель “клиент-сервер”.

Под капотом используются:

  • Различные механизмы ядра ОС, включая iptables;

  • Виртуальные сетевые мосты;

  • Cgroups;

  • Capabilities (привелегии);

  • Secure compututing mode (безопасный режим вычислений);

  • и многое другое.

Что видит пользователь:

  • Клиент;

  • Сервер;

  • Registry (хранит образы и их метаданные).

Сетевые порты

По умолчанию Docker использует три порта:

  • 2375 TCP - для нешифрованного трафика;

  • 2376 TCP - для зашифрованный SSL-соединений;

  • 2377 для режима Swarm.

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

Сокет Unix может располагаться по разным путям, но обычно он находится в /var/run/docker.sock.

В более новых версиях docker.sock создаётся в корневом каталоге пользователя, внутри .docker/run/, а на /var/run/docker.sock делается ссылка.

Сеть для контейнеров

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

Docker выделяет частные подсети из неиспользуемого блока согласно RFC 1918. Сеть соединяется мостом с локальной сетью хоста через интерфейс docker0.

Можно настроить контейнер так, чтобы он работал с сетью напрямую, не используя bridge-режим, для этого нужно при запуске контейнера указать --net=host. А можно и совсем отключить сеть, указав --net=none.

Контроль изменений

Из коробки у Docker есть два способа контроля изменений. Один отслеживает слои файловой системы, второй - теги этих образов.

Каждый слой в контейнере обладает уникальным хэшем для идентификации и все наборы изменений накладываются поверх предыдущих.

Теги же выбираются для образа в ходе стандартного процесса сборки. В начале работы с Docker удобно использовать тег latest, которым обычно помечается последняя доступная версия. Для продакшена же этот путь не подойдёт и там нужно укзывать конкретную версию приложения.

Плюсы и минусы

Ограниченная изоляция

По умолчанию контейнеры изолированы друг от друга, но не стоит думать, что эта изоляция полная. Так как процессы в контейнере это процессы основной операционной системы и отображаются в ps, то и ресуры ЦП и ОЗУ будут использовать общие, а также будут работать на том же ядре, что и основная ОС и другие контейнеры. Поэтому нужно дважды подумать, прежде чем давать контейнеру ресуррсов больше, чем ему требуется. Более того, многие контейнеры используют UID 0 для запуска процессов и это может привести к компрометации всей системы.

Контейнеры легковесны

Контейнер по своей сути это просто ссылка на многослойный образ файловой системы и включает в себя немного метаданных о конфигурации.

Приложения без сохранения состояния

Контейнеры не хранят в себе никакие данные. При удалении или пересоздании контейнера все наработки будут уничтожены. Во многих случаях даже состояние конфигурации выносится в переменные среды и не встраивается в контейнер, а применяется при развёртывании.

Сохранение состояния всё-таки возможно

Если же данные нужно сохранять, то для этого всегда можно использовать Volumes.

Часть 2. В которой рассказывается о работе с Docker образами

Вообще второй частью должна была идти инструкция по установке Docker на различные ОС, но так как эта тема довольно скучная да и может меняться время от времени, так что я просто оставлю это здесь.

Мы же двинемся дальше и посмотрим как работать с образами Docker.

Структура Dockerfile

Для создания образа Docker с помощью инструментов по умолчанию нужен dockerfile. Типичный dockerfile может содержать следующие параметры

FROM ubuntu:22.04.3
ARG email="test@test.com"
LABEL "maintainer"=$email
USER root
ENV AP /data/app
ENV SCPATH /etc/supervisor/conf.d
RUN apt-get -y update
RUN apt-get install supervisor
RUN mkdir -p /var/log/supervisor
COPY ./supervisord/conf.d/* $SCPATH/
WORKDIR #AP 
RUN npm install
CMD ["supervisord", "-n"]

FROM указывает на базовый образ ОС, который будет взят за основу. Базовым образом обычно выбирают Alpine linux, так как он занимает очень мало места, но есть одно но. Данный образ основан на библиотеке musl, а не на стандартной GNU C. Это может повлиять на приложения на основе Java и разрешение имён DNS. Плюс используется bin/sh, вместо /bin/bash. При необходимости нужные библиотеки можно доустановить.

ARG позволяет задавать переменные, которые доступны только во время сборки образа.

LABEL добавляет метки с помощью пар “ключ - значение”, которые затем можно использовать для поиска и идентификации контейнеров. Посмотреть метки образа можно с помощью команды docker image inspect.

USER позволяет менять пользователя в контейнере, так как по умолчанию Docker работает от рута.

ENV задаёт переменные командной оболочки, которые приложение может использовать для конфигурации во время работы или в процессе сборки.

RUN выполняет необходимые команды.

В данном примере указана команда apt-get -y update, но в реальном образе её использование не рекомендуется, так как образ перестанет быть воспроизводимым, так как версии пакетов в репозиториях со временем меняются.

COPY копирует файлы из локальной системы в образ.

ADD может копировать не только файлы, но и архивы. И даже из сети по ссылкам.

WORKDIR меняет рабочий каталог в образе для остальных инструкций в сборке.

CMD определяет команду, запускающую процесс, который будет выполняться в контейнере.

Порядок команд в Dockerfile влияет на время сборки. Изменения, которые происходят чаще всего должны располагаться как можно ближе к концу файла, чтобы при сборке пересобиралось как можно меньше слоёв.
Каждое использование COPY и RUN создаёт новый слой в образе, так что для ускорения сборки и уменьшения размера образа будет разумно располагать данные команды на одной строке.
Рекомендуется запускать в контейнере только один процесс. Эта рекомендация происходит из идеи, что контейнер должен выполнять одну функцию.

Сборка образа

Рядом с файлом dockerfile можно расположить файл .dockerignore, в котором можно указать файлы и каталоги, которые не должны попасть на хост Docker при сборке образа, например, туда можно добавить .git.

При сборке Docker полагает, что dockerfile находится в текущем каталоге. Если это не так, то путь можно указать с помощью аргумента -f.

Также Docker при сборке использует кэш, но бывают случаи, когда необходимо этого не делать, тогда нужно использовать ключ --no-cache.

Сборку образа можно запустить следующими командами:

docker image build -t example/docker-hello:latest .
или
docker build -t example/docker-hello:latest .

Точка в конце build обозначает текущий каталог.

Запуск образа

Запустить собранный образ можно командой

docker container run --rm -d -p 8080:8080 example/docker-hello:latest

где --rm указывает удалить контейнер после выполнения работы

-d запускает его в фоновом режиме

-p 8080:8080 указывает с какого порта хоста на какой порт контейнера сделать проброс.

После запуска определить IP-адрес контейнера можно, посмотрев вывод команды docker context list или при сборке задать переменную DOCKER_HOST. Также если был задан UNIX-сокет для DOCKER_ENDPOINT, то адрес будет 127.0.0.1.

Аргументы, которые мы задавали при сборке, можно переопределить при запуске с помощью аргумента --build-arg, например

docker image build --build-arg email=test2@test1.org

Также можно передавать значения переменных, в уже готовые контейнеры при запуске, но с помощью аргумента --env

docker container run --env WHO="blablabla"

Хранение образов

Есть несколько мест для хранения образов - публичные реестры и частные реестры.

Самым популярным публичным является Docker Hub.

Если нужно использовать частный реестр, то можно использовать варианты, такие как:

Чтобы залогиниться в реестре нужно ввести команду docker login, потом свои логин и пароль, а Docker сохранит их и будет в будущем использовать при необходимости.

Для безопасности лучше использовать не логин и пароль, а токен доступа, который можно создать в личном кабинете

Для отправки образов в хаб необходимо использовать команду docker image build -t docker.io/USER_NAME/docker-test:latest, где docker.io - имя реестра, а USER_NAME - логин.

Если мы хоти заменить теги для созданного ранее образа, то можно использовать команду docker image tag.

Для поиска доступных образов используется команда docker search NEED_NAME.

Оптимизация образа

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

Первый, можно экспортировать содержимое контейнера в архив и потом его посмотреть - docker container export CONTAINER_ID -0 test.tar.

Второй, подключиться напрямую к серверу Docker:

  1. Определяем где хранятся файлы образа - docker image inspect ubuntu:latest

  2. Подключаемся - docker container run --rm -it --privileged --pid=host debian nsenter -t 1 -m -u -n -i sh

Многоэтапные сборки

Суть многоэтапной сборки проста. Например, нам нужно получить контейнер в котором будет какое-нибудь приложение на go. Для этого в dockerfile мы сначала запустим один контейнер, где будет проходить вся сборка, но будет много ненужного софта для работы самого приложения, а потом перенесём полученный артефакт в другой контейнер, в котором будет минимальное окружение, а значит и минимально занимаемый объём.

Пример:

# Этап 1: Сборка приложения
FROM golang:1.22-alpine AS builder

# Устанавливаем рабочую директорию
WORKDIR /app

# Копируем go.mod и go.sum для предварительной загрузки зависимостей
COPY go.mod go.sum ./
RUN go mod download

# Копируем исходный код
COPY . .

# Компилируем приложение
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
    go build -o /myapp .

# Этап 2: Финальный образ
FROM alpine:latest

# Устанавливаем рабочую директорию
WORKDIR /

# Копируем скомпилированный бинарь из этапа сборки
COPY --from=builder /myapp .

# Указываем команду запуска
CMD ["./myapp"]

Здесь нужно обратить внимание на строки FROM golang:1.22-alpine AS builder и FROM alpine:latest. Именно в них происходит магия многоэтапной сборки.

Суммирование слоёв

В Docker слои всегда суммируются. Соответственно для уменьшения образа нужно удалять всё что не нужно для работы образа на том же слое, что и добавлять, располагая команды на одной строке. Например, удаление кэша при установке нужных пакетов можно производить командой

RUN dnf install -y httpd && dnf clean all

Нужно помнить, что слои формируются командами FROM, RUN, COPY и ADD.

Мультиархитектурные сборки

Для сборок под различные архитектуры был разработан плагин buildx и обычно он уже установлен в системе. Проверить его наличие можно с помощью команды:

docker buildx version

Чтобы собрать образы для разных платформ нужно ввести подобную команду:

docker buidx build --platform linux/amd64, linux/arm64 --tag docker.io/USERNAME/test:latest .

Часть 3. В которой мы знакомимся с контейнерами.

Контейнер - это самодостаточная среда выполнения, которая использует ядро хост-системы и может работать изолировано от других контейнеров в системе.

Вот такое определение приводится в libcontainer. От него мы и будем отталкиваться.

Одним из главных преимуществ контейнеров является эффективность по ресурсам, так как нам не нужен целый экземпляр ОС для выполнения приложения. Контейнер использует ядро хостовой ОС и память хостовой ОС, а следовательно нет накладных расходов на поддержание дополнительных слоёв и абстракций.

При использовании контейнеров мы можем выполнять только процессы совместимые с ядром, то есть нельзя запустить приложение для Windows напрямую на хосте Linux. И наоборот данное правило тоже работает.

Запуск контейнера

Запустить контейнер можно командой docker container run. На самом деле это команда включает в себя два шага:

docker container create - создаёт контейнер из образа
и
docker container start - выполняет контейнер

Эти команды можно выполнять и по отдельности, применяя к ним те же ключи, что и к run.

Базовая конфигурация

Имя контейнера

По умолчанию контейнер получает имя состоящее из прилагательного и имени какого-нибудь известного человека, но это действие можно легко изменить, с помощью аргумента --name

docker container create --name="test-service" ubuntu:latest sleep 120

В пределах хоста у контейнеров должны быть уникальные имена.

Метки

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

docker container run --rm -d --name test-service -l deployer=Tzintch ubuntu:latest sleep 120

Это даст нам возможность потом при поиске фильтровать метаданные

docker container ls -a -f label=deployer=Tzintch

Выше были использованы несколько аргументов значение которых необходимо пояснить. rm - даёт команду удалить контейнер после выполнения задачи d - запускает контейнер фоном l - вешает лейбл

Также можно использовать аргументы t - для выделения псевдо-TTY i - указатель, что сеанс будет интерактивным и мы хотим оставить поток STDIN открытым

Если в образе не определена ENTRYPOINT, то при запуске контейнера нужно указывать команду на исполнение. Если ENTRYPOINT определена, то возможно передать аргументы для команды, которая там записана.

Также есть возможность задать имя хоста внутри контейнера, это делается с помощью аргумента --hostname.

... hostname="test-pc" ...

DNS

По умолчанию контейнер внутри себя делает точную копию файла /etc/resolve.conf с хостовой машины, но и это поведение можно переопределить с помощью аргументов --dns и --dns-search. Если Вы не хотите указывать поисковый домен, то можно просто указать --dns-search=.

MAC-адрес

Мы имеем возможность задать нужный MAC-адрес для нашего контейнера. Делается это с помощью аргумента --mac-address. В основном это никогда не нужно, но может пригодиться, если возникнет необходимость зарезервировать определённые адреса для Docker и избежать конфликтов с другими службами, которые также могут использовать частные диапазоны адресов.

Тома хранилища

Если возникает необходимость сохранять часть настроек контейнера, то можно подключить внешнюю директорию к внутренней директории контейнера, делается это с помощью команды --mount или -v

docker container run --rm -ti --mount type=bind,target=/mnt/session_data,source=/data ubuntu:latest /bin/bash
или
docker container run --rm -ti -v /mnt/session_data:/data ubuntu:latest /bin/bash

По умолчанию директории монтируются в режиме чтения/записи. Если нужно примонтировать только в режиме чтения, то используется дополнение readonly для --mount и :ro для -v

docker container run --rm -ti --mount readonly  type=bind,target=/mnt/session_data,source=/data ubuntu:latest /bin/bash
или
docker container run --rm -ti -v /mnt/session_data:/data:ro ubuntu:latest /bin/bash

Если точки монтирования не созданы заранее, то они будут созданы автоматически.

Если на хосте включен SELinux, то могут возникнуть проблемы из-за отсутствия прав доступа, решается это с помощью опции -z

... -v /mnt/session_data:/data:z ...

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

Квоты

Docker может ограничивать контейнеры в ресурсах благодаря использованию cgroups, но эти возможности должны быть включены в ядре. Если cgroups работают, то ограничивать можно ЦП, память и SWAP.

ЦП

Доля процессорного времени

В Docker пул процессорного времени делится на 1024. Если мы хотим отдать контейнеру половину вычислительной мощности, то можно выделить ему 512 долей --cpu-shares=512 Это не значит, что другие ресурсы не смогут выполнять свои действия на данных мощностях, но будет ограничено время их выполнения, в то время как контейнер получит свои стандартные 100 мкс.

Привязка к процессору

Мы можем привязать контейнер к одному или нескольким ядрам ЦП --cpuset-cpus=0 Данный аргумент задаёт начало индекса и берёт первое ядро. Данный вариант можно комбинировать с выделением долей процессорного времени.

Упрощение квот на ресурсы ЦП

В современных версиях Docker выставлять ограничения на использование ЦП стало проще. Используется команда -cpus со значением от 0.01 до количества ядер в системе.

... -cpus=".25"

Команда docker container update позволяет динамически корректировать ограничения ресурсов.

ОЗУ

В отличие от ограничений ЦП, ограничения на ОЗУ действуют жёстко и контейнер не получит больше памяти, чем ему было выделено и если выйдет за лимиты, то начнёт использовать файл подкачки.

Задаются ограничения аргументом --memory

... --memory 512m ...

Данное ограничение выделит контейнеру 512 МБ ОЗУ и столько же файла подкачки. Если же задать дополнительно параметр --memory-swap -1, то размер используемого свопа будет неограничен.

Если контейнер упрётся в ограничения памяти, то к нему может прийти OOM Killer и убить его. Чтобы отключить его для данного контейнера можно использовать аргументы --oom-kill-disable и --oom-score-adj, но лучше этого не делать.

Блочный ввод-вывод

При желании или необходимости можно также ограничить скорости чтения и записи на диск. Механизм будет действовать подобно ограничениям на ЦП.

--device-read-bps - ограничения скорости чтения с устройства (байт в секунду)
--device-read-iops - ограничения скорости чтения с устройства (операций в секунду)
--device-write-bps - ограничения скорости записи на устройство (байт в секунду)
--device-write-iops - ограничения скорости записи на устройство (операций в секунду)

Запуск контейнера

Запустить контейнер можно командой docker container start, но чтобы знать какой запускать нужно для начала узнать или его имя, или хэш, или часть хэша, главное, чтобы эта часть была уникальной, сделать это можно с помощью команды

docker container ls -a
или
docker ps -a

Если контейнеров много, то можно использовать фильтр

docker container ls -a --filter ancestor=redis:2.8

Автоматический перезапуск контейнера

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

no - никогда
always - всегда
on-failure - при сбое (количетсво попыток можно ограничить on-failure:3)
unless-stopped - пока не остановят

Остановка контейнера

Остановить контейнер можно командой docker container stop. Данная команда отправляет контейнеру сигнал SIGTERM контейнер будет выключен. Если же этого не произошло и процесс зависает, то можно использовать аргумент -t.

docker container stop -t 25

В этом случае будет отправлен сигнал SIGTERM и если он не отработает, то через 25 секунд (по умолчанию - 10), то будет отправлен сигнал SIGKILL и завершит процесс принудительно. После этого его можно опять запустить при необходимости с той же конфигурацией. Мы можем повторно запускать контейнер без создания новых до тех пор пока не удалим его. Можно использовать сразу принудительно завершение docker container kill

Приостановка контейнера

Есть возможность приостановить работу контейнера, сохранив за ним выделенные ресурсы и оставить записи в таблице процессов - docker container pause. Это отправит сигнал SIGSTOP и поставит контейнер на паузу. Запустить снова можно с помощью docker container unpause.

Удаление контейнера

Удалить контейнер можно с помощью команды docker container rm, но сделать это можно только после остановки контейнера. Если же останавливать контейнер желания и необходимости нет, то можно сделать удаление с помощью аргумента -f или --force.

Далее можно удалить и образы, если они больше не нужны. Список всех образов выводится командой docker image ls, а потом нужный удаляется с помощью docker image rm. Образы можно удалять только тогда, когда их не используют контейнеры

Удаление всего, что не используется

Если нужно удалить все контейнеры, образы и сети, которые не связаны ни с чем, то можно применить команду docker system prune. Если нужно удалить всё, а не только несвязанное, то можно добавить аргумент -a.

Удалить все контейнеры можно командой

docker container rm $(docker container ls -a -q)

Удалить все образы можно командой

docker image rm $(docker image -q)

Часть 4. Собираем информацию в Docker

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

В Docker есть команды, которые позволяют облегчить различные задачи, например:

  • Просмотр версии Docker;

  • Просмотр информации о сервере;

  • Загрузка обновлений образов;

  • Изучение контейнеров;

  • Вход в работающий контейнер;

  • Возврат результатов;

  • Просмотр журналов;

  • Мониторинг статистики и т.д.

Просмотр версии Docker

docker version

Версии компонентов могут различаться

Информация о сервере

docker system info

Выведется информация об образах, контейнерах, плагинах и т.д. По умолчанию файл конфигурации Docker содержится в файле /etc/docker/daemon.json. Для большинства аргументов, которые передаются в dockerd здесь можно задать постоянные значения.

Загрузка обновлений образов

docker image pull ubuntu:latest 

Docker не будет автоматически обновлять локальный образ.

Изучение контейнера

docker container inspect

Выдаёт подробный вывод о контейнере.

Изучение командной оболочки

docker container run -it ubuntu:latest /bin/bash

Если после запуска мы посмотрим на запущенные процессы, то увидим только bash. Это происходит потому, что в контейнере ничего не запускается автоматически.

Возврат результата

docker container run ubuntu:latest /bin/cat /etc/passwd

В ответ мы получим содержимое файла /etc/passwd. Символ | в контейнер не передаётся. Если нужно выполнить пайп именно в контейнере, то нужно передавать строку для выполнения совместно с /bin/bash.

Что происходит в контейнере

Exec

docker contanter exec -ti ... /bin/bash

Данной командой мы откроем TTY в контейнере и перейдём в него.

Volume

docker volume ls

Позволяет посмотреть список томов, которые были созданы самим Docker через docker volume create my-data.

docker volume rm

Позволяет удалить том, если он больше не нужен.

Журналирование

docker container logs

Позволяет вывести логи из контейнера. Файлы из этого журнала хранятся в /var/lib/docker/containers/id/. Чтобы не смотреть логи локально можно настроить их отправку во внешние системы с помощью настроек файла daemon.json.

Мониторинг

stats

docker container stats

Выведет динамический срез информации о работе контейнера с информацией:

  • Имя контейнера;

  • Потребление ресурсов ЦПУ, где 100% - одно ядро;

  • ОЗУ;

  • Сеть, Docker перенаправляет трафик на порты контейнера даже если они ещё не запущены. Также данную информацию можно получить через API /stats

events

docker system events

Показывает все события, которые происходят в Docker.

cAdvisor

Инструмент от компании Google, который сам ставится через Docker или обычным бинарником и позволяет следить за контейнерами. Также метрики с него можно отгружать в Prometheus

Часть 5. Учимся отлаживать контейнеры

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

Самым простым способом заглянуть внутрь контейнера является команда

docker container top

В ответ мы получим список процессов внутри контейнера, упорядоченный по PID. Стоит учесть, что имя пользователя для некоторых UID может отличаться в контейнере и в хостовой машине, а где-то его может и не быть совсем.

Получить более детальную информамцию можно другими способами. Можно использовать стандартные утилиты Linux, например strace, например:

sudo straсe -p PROCESS_NUMBER

Подобным образом можно использовать и lsof

sudo lsof -p PROCESS_NUMBER

Все пути в выводе будут указаны относительно вида на файловую систему изнутри контейнера.

Всегда есть возможность завершить в контейнере отдельную службу с помощью kill и контейнер продожит работать, только если таким способом не убить процесс верхнего уровня (PID 1). Но лучше так не делать, в конце концов, проще создать новый контейнер, чтобы не произошло каких-то неожиданных эффектов.

Отладка сетей

Посмотреть существующие сети можно командой docker network ls. Чтобы посмотреть, какие контейнеры подключены к определённой сети, можно подать команду docker network inspect.

История образа

С помощью команды docker image history можно отследить происхождение и базовый образ. Данная команда показывает все слои, их размеры и команды с помощью которых образ собирался. Данная команда поможет определить почему образ получился большего объёма, чем ожидалось и поможет понять как можно его улучшить.

Изучение контейнера

На диске есть каталог, который выделяется для контейнера, обычно это /var/lib/docker/containers и в нём лежат очень длинные хэши. Можно ввести команду docker container ls и взять короткий хэш с помощью которого и найти нужный длинный. В каталоге будут файлы, которые монтируются к контейнеру.

Также можно проверить записывает ли контейнер что-либо в свою внутреннюю файловую систему. Сделать это можно с помощью команды docker container diff, например:

docker container diff nginx-fs C /run

В ответе будут выданы строки с литерами A и C в начале. A означает added, то есть было добавлено, а C - changed, то есть было изменено.

Часть 6. Знакомимся с Docker Compose

Изначально Docker Compose был отдельным приложением написанным на Python и запускался через docker-compose. Теперь это считается первой версией. Сейчас используется docker compose, это вторая версия, которая полностью переписана на go и является плагином для Docker. Docker Compose обычно использует один декларативный YAML-файл для каждого проекта - docker-compose.yaml. Этот файл легко читается и будет работать одинаково у любого пользователя. В данном файле указываются все важные требования для каждого сервиса, а также их взаиможействие друг с другом. Также происходит валидация и проверка логики.

Содержимое файла

version: '3.8'
services:
  test-agent:
    image: 'test-agent:1.3.1'
    restart: unless-stopped
    container_name: test-agent
    hostname: test-agent
    environment:
      - USERNAME=login
      - PASSWORD=password
      - NGINX_UPSTREAM_CONFIG=true
      - UPSTREAM_SERVER=192.0.2.101:80
    sysctls:
    kernel.sem: "32000 1024000000 500 32000"
    ports:
      - "80:80/tcp"
    volumes:
      - ./logs:/var/log/nginx
    networks:
      botnet
networks:
  botnet:
    driver: bridge

Первая строка указывает версию языка конфигурации Compose. Оставшийся код разделён на две части: services и networks.

Services

Самая важная часть конфигурации. Здесь указывается какие приложения ы хотим запускать и с какими параметрами. Файл docker-compose.yaml может ссылаться на переменные среды в формате ${ИМЯ ПЕРЕМЕННОЙ}, чтобы извлекать секреты, не сохраняя их в файле.

Networks

Данный раздел позволяет определить именованую сеть с указанием её типа.

Запуск сервисов

После заполнения compose-файла можно проверить его правильность

docker compose config

Если всё хорошо, то команда выведет наш файл. Если возникнут проблемы, то выдаст ошибку с дополнительными сведениями.

Собрать любые нужные контейнеры можно с помощью

docker compose build

Для запуска контейнера в фоновом режиме используем

docker compose up -d

Использование .env

Если нам необходимо передать в контейнер при его запуске несколько значений переменных, то проще всего это сделать с помощью .env файла. В него необходимо поместить нужное содержимое в виде пар ключ-значение, например:

PASSWORD=pswd01

Далее запускаем контейнер.

Если файл находится в той же директории, что и docker-compose.yaml, то он будет считан автоматически.

Сначала Docker Compose считывает все значения по умолчанию из файла docker-compose, затем перезаписывает их данными из файла .env, а уже потом берёт значения из переменной среды пользователя. А это значит, что в файле .env должны быть только общие для всех пользователей данные.

Заключение

В данной статье я постарался охватить самое необходимое для использования Docker, что бывает нужно часто и без чего очень сложно. Здесь не упомянуты утилиты для управления контейнерами, такие как LazyDocker или Portainer, и тем более не затронута тема оркестрации. Но это уже совсем отдельные вопросы, разбираться с которыми нужно отдельно и обстоятельно.