Программирование и IT

Что такое Docker

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

Обновлено
В этой статье

Какую боль он снимает#

Программа редко состоит из одного файла. Ей нужна определённая версия языка,
десяток библиотек, переменные окружения, иногда системные пакеты. На машине
автора всё это уже стоит, на машине коллеги — другой версии, на сервере — третьей.
Отсюда вечное «у меня работает».

Docker решает это переносом границы: упаковывается не программа, а целиком
слепок файловой системы, внутри которого программа уже установлена и настроена.
Запускается этот слепок одинаково везде, где есть Docker.

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

Образ и контейнер#

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

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

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

Ближайшая аналогия из программирования: образ — класс, контейнер — объект.
Из установочного диска тоже можно поставить систему на сколько угодно машин, а
сам диск от этого не меняется.

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

Первые команды#

Первая строка скачает образ и запустит программу внутри, вторая поднимет
Ubuntu и бросит тебя в её оболочку, третья запустит веб-сервер в фоне:

docker run hello-world
docker run -it ubuntu bash
docker run -d -p 8080:80 --name web nginx

Разбор ключей последней строки: -d — работать в фоне, -p 8080:80 — порт
8080 на твоей машине ведёт на порт 80 внутри контейнера, --name web — имя,
по которому дальше к нему обращаться, nginx — образ.

Порядок в -p запоминается так: слева то, что снаружи, справа — что внутри.
Если поменять местами, браузер по адресу localhost:8080 ничего не найдёт.

docker ps                 # что сейчас работает
docker ps -a              # все контейнеры, включая остановленные
docker logs -f web        # вывод программы, -f — следить за новым
docker exec -it web sh    # оболочка внутри работающего контейнера
docker stop web           # остановить
docker rm web             # удалить остановленный
docker image ls           # список скачанных и собранных образов

docker logs — первое, что смотрят, когда контейнер запустился и сразу упал.
Вторая подсказка там же: docker ps -a покажет код выхода в столбце состояния.

Образы копятся незаметно и занимают гигабайты. Проверить расход поможет
docker system df, освободить место — docker system prune; команда удаляет
остановленные контейнеры и неиспользуемые образы, поэтому читай её вопрос
перед подтверждением.

Dockerfile#

Образ описывают текстовым файлом с именем Dockerfile. Каждая инструкция —
отдельный слой.

FROM python:3.12-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

ENV PORT=8000
EXPOSE 8000

CMD ["python", "app.py"]

Построчно:

  • FROM — основа, готовый образ из реестра. Варианты с пометкой slim или
    alpine весят в разы меньше полных.
  • WORKDIR — рабочий каталог внутри образа, дальше все пути считаются от него.
  • COPY — перенести файлы из твоей папки в образ.
  • RUN — выполнить команду во время сборки.
  • ENV — переменная окружения.
  • EXPOSE — пометка о занятом порте; сама по себе она ничего не открывает,
    порт публикует ключ -p при запуске.
  • CMD — что запустить, когда контейнер стартует.

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

Рядом кладут .dockerignore — список того, что в образ не попадёт: каталог
.git, локальные виртуальные окружения, файлы с ключами. Файл устроен как
.gitignore и заодно ускоряет сборку.

Собрать и запустить:

docker build -t myapp:1.0 .
docker run -d -p 8000:8000 --name api myapp:1.0

Точка в конце build — это контекст сборки, каталог, откуда берутся файлы для
COPY. Метка -t даёт образу имя и версию; без версии подставится latest,
и через месяц ты не поймёшь, что именно работает на сервере.

Данные: тома#

Чтобы данные пережили удаление контейнера, их выносят наружу.

Именованный том Docker создаёт и хранит сам:

docker volume create pgdata
docker run -d --name db \
  -e POSTGRES_PASSWORD=secret \
  -v pgdata:/var/lib/postgresql/data \
  postgres:16

Второй способ — подставить внутрь каталог со своей машины:

docker run -d -p 8000:8000 -v "$PWD":/app myapp:1.0

Первый вид — именованный том: подходит для баз, работает быстро, лежит в
хозяйстве Docker. Второй — монтирование каталога: правка файла у тебя тут же
видна внутри, поэтому им пользуются при разработке.

Учебная база, поднятая такой командой, — самый простой способ тренировать
SQL-запросы без установки сервера на свою систему:
надоело — удалили контейнер и том.

Когда сервисов больше одного, команды перестают помещаться в голове, и их
описывают в файле compose.yaml:

docker compose up -d      # поднять всё описанное
docker compose ps         # состояние сервисов
docker compose logs -f    # общий журнал
docker compose down       # остановить и убрать

Сервисы из одного файла видят друг друга по именам: приложение подключается к
базе по адресу db, а не по IP.

Когда Docker не нужен#

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

  • Один скрипт на своей машине. Виртуальное окружение языка решает вопрос
    зависимостей и не требует ничего ставить.
  • Статический сайт. Готовые страницы раскладываются файлами, упаковывать
    нечего.
  • Программа с графическим окном. Прокинуть интерфейс наружу можно, но
    возни больше, чем пользы.
  • Тяжёлая работа с диском на macOS и Windows. Монтированные каталоги там
    идут через виртуальную машину и заметно медленнее.

И обратная сторона: контейнер не делает программу безопасной автоматически.
Запуск от суперпользователя внутри, ключи, вшитые в образ, ключ --privileged
на всякий случай — всё это сводит изоляцию к нулю.

Частые ошибки#

Контейнер стартует и сразу останавливается. Контейнер живёт ровно столько,
сколько работает его главный процесс. Программа завершилась — контейнер
закрылся. Смотри docker logs.

Порт занят. bind: address already in use означает, что порт слева в -p
уже кем-то взят; найти виновника поможет ss -tulpn — команда разобрана в
шпаргалке по командам Linux.

Изменения в коде не видны. Образ собран со старой копией файлов. Либо
пересобери, либо смонтируй каталог ключом -v.

Ключи и пароли внутри образа. Всё, что попало в слой, достаётся из образа
кем угодно, даже если следующая инструкция это удалила. Секреты передают
переменными окружения при запуске — тот же принцип, что и с
ключами доступа к API.

План по этапам

  1. Запустить чужой образПоднять nginx командой docker run с пробросом порта и открыть страницу в браузере.
  2. Осмотреться внутриЗайти в работающий контейнер через docker exec, посмотреть файлы, выйти и убедиться, что контейнер жив.
  3. Собрать свой образНаписать Dockerfile из пяти инструкций для маленькой программы и собрать его с меткой версии.
  4. Проверить кеш слоёвИзменить строку кода, пересобрать и сравнить время с первой сборкой; затем переставить COPY и повторить.
  5. Сохранить данныеПоднять базу с именованным томом, записать строку, удалить контейнер, поднять заново и найти строку на месте.

Начать изучать эту тему у себя

План ляжет в твой репозиторий: отмечай этапы, веди конспект — история изменений покажет, как ты продвинулся.

Начать план

Проверь себя

1.Какая команда покажет список работающих контейнеров?

2.Контейнер запущен с ключом -p 8080:80. По какому порту к нему обращаться с твоей машины?

3.Что получается в результате выполнения docker build по Dockerfile?

4.Что нужно, чтобы данные базы пережили удаление контейнера?

Источники

Было полезно?

Ещё темы

Программирование и IT Что такое API API — это условленный способ одной программы попросить что-то у другой. Никакого экрана и кнопок: запрос уходит текстом по сети, ответ приходит в машиночитаемом виде. Разобравшись с четырьмя частями HTTP-запроса, ты сможешь читать документацию любого сервиса. Программирование и IT git rebase Команда переносит коммиты твоей ветки так, будто ты начал работу не от старого состояния, а от текущего. История становится линейной, но коммиты при этом создаются заново — с новыми хешами. Отсюда главное ограничение: переносить можно только то, чем не пользуется никто другой. Программирование и IT Arduino для начинающих Arduino — плата с микроконтроллером, которой можно объяснить, когда зажечь светодиод, а когда прочитать датчик. Программа пишется на упрощённом C++, заливается по USB и работает сама, без компьютера. Первое мигание светодиодом занимает минут двадцать. Программирование и IT git clone Команда git clone забирает репозиторий с сервера целиком — со всей историей, ветками и тегами — и кладёт копию в новую папку на твоём компьютере. С неё начинается работа с любым уже существующим проектом, своим или чужим. Программирование и IT git checkout Исторически `git checkout` делает сразу несколько несвязанных вещей: переводит рабочую копию на другую ветку, создаёт ветку, достаёт отдельный файл из коммита. Из-за этой перегруженности в git 2.23 появились две отдельные команды — `switch` и `restore`. Разберём обе стороны: старый способ и современную замену. Программирование и IT git branch Ветка в git — это подвижный указатель на коммит, а не копия файлов. Поэтому завести её ничего не стоит, а команда git branch отвечает за список веток, их создание, переименование и удаление.

Ещё сценарии