Docker для DevOps: от основ до CI/CD

Практический курс по Docker для начинающих DevOps-инженеров. Вы научитесь создавать контейнеры, работать с Docker Compose, настраивать CI/CD пайплайны и обеспечивать безопасность инфраструктуры.

1. Основы контейнеризации и запуск Docker

Основы контейнеризации и запуск Docker

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

Контейнеризация — это технология упаковки приложения и абсолютно всех необходимых для его работы компонентов (библиотек, конфигурационных файлов, утилит) в единый изолированный программный блок. Этот блок называется контейнером (container). Благодаря такому подходу приложение становится полностью независимым от инфраструктуры, на которой оно запускается.

Представьте мировую логистику до изобретения стандартизированных морских контейнеров. Грузчикам приходилось отдельно упаковывать бочки с вином, мешки с зерном и ящики с инструментами. Каждый тип груза требовал особых условий перевозки и крепления на корабле. Внедрение стандартного металлического контейнера произвело революцию: теперь портовым кранам и кораблям неважно, что находится внутри. Они просто перемещают стандартные блоки. Docker делает то же самое, но для программного обеспечения.

Виртуальные машины против контейнеров

До массового распространения Docker стандартом изоляции приложений была виртуализация на базе виртуальных машин (Virtual Machine, или ВМ). Чтобы понять преимущества Docker, необходимо сравнить эти два подхода.

Виртуальная машина эмулирует полноценный физический компьютер. Поверх физического сервера устанавливается специальная программа — гипервизор, которая распределяет ресурсы (процессор, память, диск) между виртуальными машинами. Каждая ВМ содержит собственную полноценную гостевую операционную систему.

Контейнеры работают иначе. Они используют виртуализацию на уровне операционной системы. Вместо запуска отдельной ОС для каждого приложения, контейнеры делят ядро одной операционной системы хоста (host), изолируя только процессы, сеть и файловую систему.

| Характеристика | Виртуальная машина | Docker-контейнер | | --- | --- | --- | | Архитектура | Включает полноценную гостевую ОС | Делит ядро ОС с хост-системой | | Размер | Гигабайты (тяжеловесные) | Мегабайты (легковесные) | | Скорость запуска | Минуты (загрузка ОС) | Секунды или миллисекунды | | Изоляция | Полная аппаратная изоляция | Изоляция на уровне процессов ОС | | Утилизация ресурсов | Высокие накладные расходы | Максимально эффективное использование |

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

Где: — сэкономленный объем оперативной памяти в мегабайтах. — память, потребляемая одной виртуальной машиной (включая фоновые процессы ОС). — память, потребляемая одним контейнером (только само приложение). — количество запущенных экземпляров приложения.

Допустим, для работы микросервиса в ВМ требуется 2048 МБ памяти (из которых 1500 МБ уходит на саму ОС Ubuntu), а контейнер с тем же микросервисом на базе легковесного дистрибутива Alpine Linux потребляет всего 50 МБ. Если нам нужно запустить 10 экземпляров сервиса для балансировки нагрузки, расчет будет следующим: . Мы экономим почти 20 гигабайт оперативной памяти, просто изменив подход к развертыванию.

Архитектура Docker: Клиент, Демон и Реестр

Docker работает по клиент-серверной архитектуре. Когда вы вводите команды в терминале, вы используете Docker Client (клиент). Клиент сам по себе не запускает контейнеры. Он лишь отправляет команды через REST API к Docker Daemon (демону) — фоновому процессу, который работает на хост-машине. Именно демон выполняет всю тяжелую работу: скачивает образы, выделяет память, настраивает сеть и запускает процессы. Такая архитектура позволяет клиенту управлять демоном как на локальном компьютере, так и на удаленном сервере в облаке.

Для практической работы необходимо глубоко понимать три базовых сущности Docker:

1. Образ (Image)

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

Каждый образ состоит из набора слоев (layers). Каждый слой представляет собой инструкцию. Например, первый слой — это базовая операционная система, второй — установка языка Python, третий — копирование исходного кода вашего приложения. Слои кэшируются. Если вы измените только код приложения (третий слой), Docker не будет заново скачивать ОС и устанавливать Python. Он пересоберет только измененный слой, что делает процесс сборки невероятно быстрым.

2. Контейнер (Container)

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

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

3. Реестр (Registry)

Реестр — это хранилище образов. Самым популярным публичным реестром является Docker Hub. Это своеобразный магазин приложений для разработчиков. Если вам нужна база данных PostgreSQL, веб-сервер Nginx или язык программирования, вам не нужно устанавливать их вручную. Достаточно скачать готовый официальный образ из Docker Hub.

Практика: Запуск первого контейнера

Перейдем от теории к практике. Взаимодействие с Docker происходит через интерфейс командной строки (CLI). Главная команда, с которой начинается работа любого DevOps-инженера — это docker run. Она приказывает системе найти образ, создать из него контейнер и запустить его.

Попробуем запустить популярный веб-сервер Nginx:

Эта короткая строка выполняет колоссальный объем работы под капотом. Разберем каждый элемент команды:

  • docker run — базовая команда создания и запуска.
  • -d (сокращение от detach) — запускает контейнер в фоновом режиме. Без этого флага логи сервера выводились бы прямо в терминал, и при его закрытии сервер бы остановился.
  • -p 8080:80 (сокращение от publish) — проброс портов. Мы говорим Docker: "Возьми порт 8080 на моем физическом компьютере и свяжи его с портом 80 внутри контейнера". Теперь, если открыть браузер и ввести адрес локального хоста с портом 8080, мы увидим стартовую страницу Nginx.
  • --name my-web-server — присваивает контейнеру понятное имя. Если не указать этот флаг, Docker сгенерирует случайное имя.
  • nginx — имя образа, который нужно использовать.
  • Когда вы нажимаете Enter, Docker сначала ищет образ nginx на вашем компьютере. Если его нет, он автоматически скачивает его из Docker Hub, распаковывает слои, создает изолированную сеть, монтирует файловую систему и запускает процесс веб-сервера.

    Управление жизненным циклом

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

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

    Она выведет таблицу с идентификаторами контейнеров, их именами, статусом и проброшенными портами. Если вы хотите увидеть вообще все контейнеры, включая остановленные, добавьте флаг -a (от слова all).

    Для остановки работающего приложения применяется команда stop, которой нужно передать имя или идентификатор контейнера:

    Важно понимать фундаментальный принцип: остановка контейнера не удаляет его данные. Контейнер просто переходит в спящий режим и перестает потреблять процессорное время и оперативную память. Его можно запустить снова командой docker start my-web-server.

    Если же контейнер больше не нужен и вы хотите освободить место на диске, его необходимо удалить:

    Обратите внимание, что по умолчанию Docker не позволит удалить работающий контейнер. Его нужно сначала остановить, либо использовать принудительное удаление с флагом -f (force).

    Лучшие практики и типичные ошибки новичков

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

    Первая и главная ошибка — хранение важных данных внутри контейнера. Контейнеры по своей природе эфимерны (временны). Это означает, что контейнер может быть остановлен, удален и пересоздан в любую секунду. Если ваша база данных сохраняет информацию во внутренний слой контейнера, при его удалении вы потеряете все данные безвозвратно. Для решения этой проблемы используются специальные механизмы монтирования внешних томов (Volumes).

    Вторая ошибка — запуск нескольких процессов в одном контейнере. Философия Docker гласит: "Один контейнер — один процесс". Не пытайтесь установить веб-сервер, базу данных и кэш в один образ. Разделите их на три независимых контейнера. Это упростит масштабирование, обновление и поиск ошибок.

    Относитесь к контейнерам не как к домашним питомцам, которых вы лечите и выхаживаете (как это было с серверами раньше), а как к безликим рабочим единицам. Если один контейнер завис или выдал ошибку, вы не подключаетесь к нему для ручного исправления файлов. Вы просто удаляете его командой docker rm и мгновенно запускаете на его месте новый, абсолютно чистый клон из того же образа. Этот паттерн называется Cattle vs Pets (Скот против Питомцев) и является основой современной облачной инженерии.