Когда проект разрастается, настройка окружения превращается в настоящий квест. На одном сервере стоит PHP 7.4, на другом — 8.1, у одного разработчика — Windows, у другого — Mac, а в продакшене — свой Linux со своими нюансами. Знакомая ситуация?
Docker решает эту проблему, упаковывая приложение со всеми его зависимостями в изолированный контейнер, который работает одинаково на любой системе. Это позволяет разработчикам забыть о фразе «на моей машине работает», автоматизировать развёртывание и легко масштабировать инфраструктуру.
В этой статье мы разберём:
- Что такое контейнеризация и чем Docker отличается от виртуальных машин.
- Установку Docker и Docker Compose на разных ОС.
- Основные команды для работы с контейнерами и образами.
- Создание собственных образов через Dockerfile.
- Организацию многоконтейнерных приложений с Docker Compose.
- Практические примеры: запуск LEMP-стека и приложения на Node.js.
- Автоматизацию развёртывания и лучшие практики.
1. Что такое Docker и зачем он нужен
Docker — это платформа для разработки, доставки и запуска приложений в контейнерах. Контейнер — это лёгкая, изолированная среда, которая содержит всё необходимое для работы приложения: код, библиотеки, системные зависимости, переменные окружения.
Контейнеры vs виртуальные машины
Главное отличие — контейнеры используют ядро хост-системы, а виртуальные машины эмулируют полноценное аппаратное обеспечение с собственной ОС.
| Характеристика | Контейнеры (Docker) | Виртуальные машины |
|---|---|---|
| Запуск | Мгновенный | От секунд до минут |
| Ресурсы | Занимают минимум памяти | Требуют много памяти и диска |
| Изоляция | На уровне процессов | Полная аппаратная изоляция |
| Переносимость | Высокая | Ограничена гипервизором |
| Время развёртывания | Секунды | Минуты |
Преимущества Docker:
- Единообразие окружений — приложение работает одинаково на всех этапах (разработка, тестирование, продакшен).
- Изоляция — контейнеры не влияют друг на друга.
- Лёгкость масштабирования — можно запускать десятки экземпляров одного сервиса.
- Версионирование — образы можно сохранять, тегировать и откатывать.
- Упрощение деплоя — не нужно вручную настраивать сервер, достаточно запустить контейнер.
2. Установка Docker и Docker Compose
2.1 Установка Docker на Ubuntu / Debian
# Обновление пакетов и установка зависимостей
sudo apt update
sudo apt install -y ca-certificates curl
# Создание стандартизированной директории для ключей
sudo install -m 0755 -d /etc/apt/keyrings
# Добавление официального GPG-ключа Docker
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
# Добавление актуального репозитория в системные источники
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Установка Docker
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-pluginПосле добавления пользователя в группу необходимо выйти из системы и зайти заново.
2.2 Установка Docker на CentOS / RHEL / Rocky Linux
# Установка зависимостей
sudo dnf install -y yum-utils
# Добавление репозитория
sudo yum-config-manager --add-repo https://download.docker.com/linux/rhel/docker-ce.repo
# Установка Docker
sudo dnf install docker-ce docker-ce-cli containerd.io
# Добавление пользователя в группу
sudo usermod -aG docker $USER
# Запуск и автозагрузка
sudo systemctl start docker
sudo systemctl enable docker2.3 Установка Docker на Windows
- Скачайте и установите Docker Desktop с официального сайта.
- Убедитесь, что в BIOS включена виртуализация (Hyper-V или WSL2).
- После установки перезагрузите систему.
- В настройках Docker Desktop выберите использование WSL2 для лучшей производительности.
После установки Docker Desktop стоит проверить в настройках (Settings → Resources → WSL Integration), правильно ли настроена интеграция с WSL2 — это сильно влияет на производительность при работе с файловой системой.
2.4 Установка Docker Compose
Docker Compose — это инструмент для запуска многоконтейнерных приложений. Обычно он устанавливается отдельно.
# Скачивание последней версии
sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
# Права на выполнение
sudo chmod +x /usr/local/bin/docker-compose
# Проверка версии
docker-compose --versionВ новых версиях Docker Compose интегрирован в команду docker compose (без дефиса). Если у вас установлен Docker Desktop, он уже идёт в комплекте.
3. Основы работы с Docker
3.1 Образы (Images) и контейнеры (Containers)
- Образ — это шаблон, содержащий всё необходимое для запуска приложения (ОС, зависимости, код). Образы неизменяемы.
- Контейнер — запущенный экземпляр образа. Он может быть изменён, но изменения не сохраняются в образе.
3.2 Основные команды Docker
Работа с образами:
# Поиск образа в Docker Hub
docker search nginx
# Скачивание образа
docker pull nginx:latest
# Список локальных образов
docker images
# Удаление образа
docker rmi nginx:latestРабота с контейнерами:
# Запуск контейнера из образа
docker run -d --name mynginx -p 8080:80 nginx
# Список запущенных контейнеров
docker ps
# Список всех контейнеров (включая остановленные)
docker ps -a
# Остановка контейнера
docker stop mynginx
# Запуск остановленного контейнера
docker start mynginx
# Удаление контейнера
docker rm mynginx
# Выполнение команды внутри контейнера
docker exec -it mynginx bash
# Просмотр логов
docker logs mynginx
# Просмотр статистики использования ресурсов
docker statsКлючи команды docker run:
| Ключ | Описание |
|---|---|
-d | Запуск в фоновом режиме (detach) |
--name | Указание имени контейнера |
-p | Проброс портов (хост:контейнер) |
-v | Монтирование томов (хост:контейнер) |
-e | Установка переменной окружения |
--restart | Политика перезапуска (always, on-failure) |
-it | Интерактивный режим с терминалом |
3.3 Работа с томами (Volumes)
По умолчанию данные внутри контейнера не сохраняются после его удаления. Чтобы этого избежать, используют тома.
Три способа монтирования данных:
1. Базовое монтирование директории (bind mount):
docker run -d -p 8080:80 -v /путь/на/хосте:/usr/share/nginx/html nginx2. Именованный том (volume):
# Создание тома
docker volume create mydata
# Использование тома
docker run -d -v mydata:/data alpine3. Временное хранилище (tmpfs) — только в памяти:
docker run -d --tmpfs /tmp alpine4. Создание собственных образов с Dockerfile
Dockerfile — это текстовый файл с инструкциями по сборке образа.
4.1 Структура Dockerfile
Рассмотрим простой пример для PHP-приложения:
# Базовый образ (используем официальный PHP с Apache)
FROM php:8.1-apache
# Копирование файлов проекта в контейнер
COPY . /var/www/html/
# Установка PHP-расширений
RUN docker-php-ext-install mysqli pdo pdo_mysql
# Включение модуля Apache
RUN a2enmod rewrite
# Настройка рабочей директории
WORKDIR /var/www/html
# Открываем порт
EXPOSE 80
# Команда по умолчанию (запуск Apache)
CMD ["apache2-foreground"]
4.2 Основные инструкции Dockerfile
| Инструкция | Описание |
|---|---|
FROM | Базовый образ |
RUN | Выполнение команды во время сборки |
COPY | Копирование файлов с хоста в контейнер |
ADD | Аналог COPY, но умеет распаковывать архивы |
WORKDIR | Установка рабочей директории |
ENV | Установка переменных окружения |
ARG | Переменная времени сборки |
EXPOSE | Документирование портов |
CMD | Команда по умолчанию при запуске |
ENTRYPOINT | Основная команда для запуска контейнера |
VOLUME | Монтирование тома |
4.3 Сборка и запуск образа
# Сборка образа
docker build -t my-php-app .
# Запуск контейнера из собранного образа
docker run -d -p 8080:80 --name myapp my-php-app4.4 Оптимизация Dockerfile
Советы по сборке эффективных образов:
- Используйте многоступенчатую сборку (multi-stage) для уменьшения размера.
- Объединяйте команды
RUNв одну строку (чтобы уменьшить количество слоёв). - Копируйте файлы в конце — это ускорит повторную сборку при изменении кода.
- Используйте .dockerignore, чтобы исключить ненужные файлы.
- Если часто меняется код приложения, но базовые зависимости (например, базовый образ ОС) остаются теми же, Docker будет эффективно использовать уже собранные слои — это ускорит повторные сборки.
Пример с многоступенчатой сборкой для Node.js:
# Этап сборки
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# Этап исполнения
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80
5. Docker Compose для многоконтейнерных приложений
Docker Compose позволяет описывать все сервисы приложения в одном файле docker-compose.yml и запускать их одной командой.
5.1 Базовая структура docker-compose.yml
services:
web:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./html:/usr/share/nginx/html
restart: always
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: ${MYSQL_DATABASE}
volumes:
- db_data:/var/lib/mysql
restart: always
volumes:
db_data:5.2 Основные команды Docker Compose
# Запуск всех сервисов (в фоне)
docker-compose up -d
# Остановка и удаление контейнеров
docker-compose down
# Просмотр логов
docker-compose logs -f
# Пересборка и запуск
docker-compose up -d --build
# Масштабирование сервиса
docker-compose up -d --scale web=3
# Выполнение команды в контейнере
docker-compose exec web bash5.3 LEMP-стек: Linux, Nginx, MySQL и PHP в связке
Структура проекта:
lemp/
├── docker-compose.yml
├── nginx/
│ └── default.conf
└── app/
└── index.php
docker-compose.yml:
services:
db:
image: mariadb:10.6
environment:
MARIADB_ROOT_PASSWORD: "strong_root_password"
MARIADB_DATABASE: "my_project_db"
volumes:
- db_data:/var/lib/mysql
healthcheck:
# Обязательно передаём пароль root, чтобы утилита mysqladmin могла выполнить ping
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p$$MARIADB_ROOT_PASSWORD"]
interval: 5s
timeout: 2s
retries: 5
restart: always
php:
image: php:8.1-fpm
# Тот же код, что видит nginx: отсюда php-fpm берёт исполняемые .php-файлы.
# Ключевое — путь внутри контейнера (/var/www/html) совпадает с nginx,
# чтобы переменная SCRIPT_FILENAME из nginx разрешилась в реальный файл.
volumes:
- ./app:/var/www/html
depends_on:
db:
condition: service_healthy
restart: always
nginx:
image: nginx:1.25-alpine
ports:
- "80:80"
volumes:
# Статика отдаётся nginx напрямую, поэтому ему нужен доступ к тем же файлам (read-only)
- ./app:/var/www/html:ro
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- php
restart: always
volumes:
db_data:Обратите внимание на $$ в healthcheck: одинарный $ compose подставляет из своего окружения, а $$ экранирует символ, чтобы $MARIADB_ROOT_PASSWORD дошёл до контейнера буквально и раскрылся уже внутри.
nginx/default.conf:
server {
listen 80;
server_name _;
# Web root совпадает с точкой монтирования кода в php-fpm
root /var/www/html;
index index.php index.html;
# Обработка PHP: проксируем FastCGI-запросы на сервис php (TCP 9000 внутри compose-сети)
location ~ \.php$ {
# Защита от исполнения файлов, которых реально нет (например, /foo.php/bar.jpg)
try_files $uri =404;
include fastcgi_params;
fastcgi_pass php:9000;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
# Вся остальная статика — nginx отдаёт сам
location / {
try_files $uri $uri/ =404;
}
}Почему fastcgi_pass php:9000, а не unix-сокет: в официальном образе php:8.1-fpm по умолчанию настроен на TCP-порт 9000, и внутри общей сети compose сервис php доступен по имени. Unix-сокет потребовал бы общего тома между контейнерами nginx и php — для учебного стека TCP проще и корректнее.
app/index.php:
<?php
// Простейшая страница для проверки, что стек работает.
// В продакшене вызов phpinfo() нужно удалить — он раскрывает конфигурацию сервера.
phpinfo();Запуск и проверка:
# Из корня проекта (там, где docker-compose.yml)
docker compose up -d --build
# Проверяем, что все три сервиса поднялись
docker compose ps
# Статика и PHP идут через nginx на 80-м порту
curl -I http://localhost # ожидаем HTTP/1.1 200 OK
curl http://localhost | grep -o 'PHP Version' # признак, что php-fpm отработалЕсли curl -I http://localhost отвечает, а вывод содержит «PHP Version» — связка nginx → php-fpm → база собрана верно. Устранять неполадки удобно по логам: docker compose logs nginx и docker compose logs php.
Про доступ к базе из PHP. В коде приложения хост базы — не
localhost, а имя сервисаdb(внутри compose-сети):new mysqli('db', 'root', 'strong_root_password', 'my_project_db').localhostвнутри контейнера php указал бы на сам контейнер php, где базы нет.
Никогда не храните пароли в docker-compose.yml в репозитории. Используйте .env и добавьте его в .gitignore.
5.4 Пример: Node.js + Redis
services:
app:
build: .
ports:
- "3000:3000"
environment:
- REDIS_URL=redis://redis:6379
depends_on:
- redis
restart: always
redis:
image: redis:alpine
restart: alwaysВ нашем каталоге шаблонов docker-compose вы найдёте примеры с .env, healthcheck и безопасными настройками — их можно быстро адаптировать под свой проект.
6. Автоматизация развёртывания
6.1 Использование Docker в CI/CD
Контейнеры идеально вписываются в пайплайны непрерывной интеграции и доставки.
Типичный сценарий:
- Разработчик пушит код в репозиторий.
- CI-система (например, GitHub Actions или GitLab CI) собирает Docker-образ.
- Образ тестируется в контейнере (запуск тестов, линтеров).
- Готовый образ отправляется в реестр (Docker Hub, приватный реестр).
- На сервере новый образ развёртывается автоматически.
Пример GitHub Actions для сборки и публикации образа:
name: Build and Push Docker Image
on:
push:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: . # КРИТИЧЕСКИ ВАЖНО: указываем контекст, где лежит Dockerfile
push: true
tags: myuser/myapp:latestЭти значения хранятся в настройках репозитория (Settings → Secrets → Actions) и никогда не должны попадать в код. Не копируйте эти значения в репозиторий. Переменные DOCKER_USERNAME и DOCKER_PASSWORD берутся из секретов GitHub (Settings → Secrets → Actions)
6.2 Развёртывание на сервере
На продакшен-сервере достаточно иметь установленные Docker и Docker Compose.
Самый простой способ деплоя:
# Копирование кода и docker-compose.yml на сервер
rsync -avz --exclude='node_modules' --exclude='.git' ./ user@server:/app/
# Правильный деплой без длительного падения сайта (Downtime)
cd /app
# Сначала скачиваем новые образы в фоновом режиме, пока старые контейнеры работают
docker compose pull
# Перезапускаем только изменившиеся сервисы за доли секунды (без использования команды down)
docker compose up -dМожно автоматизировать этот процесс с помощью простого скрипта:
#!/usr/bin/env bash
set -euo pipefail
echo "Начинаем деплой..."
git pull origin main
echo "Контейнеры остановлены."
# Это деплой с простоем!
docker compose down
echo "Образы собраны."
docker-compose build --pull
echo "Сервисы запущены."
docker-compose up -d
echo "✅ Деплой успешно завершён!"6.3 Использование Docker Swarm или Kubernetes
Для крупных проектов с десятками серверов используют оркестраторы:
- Docker Swarm — встроенный в Docker инструмент для кластеризации.
- Kubernetes — мощная, но сложная система от Google, ставшая стандартом в индустрии.
Для небольших и средних проектов Docker Swarm может быть хорошим выбором благодаря своей простоте.
# Инициализация кластера Swarm
docker swarm init
# Развёртывание стека только для кластера!!!
docker stack deploy -c docker-compose.yml myapp
# Управление сервисами
docker service ls
docker service scale myapp_web=5🔍 Важно: Команда docker stack deploy предназначена для кластеров Docker Swarm и не является заменой обычного docker-compose up. Для одиночного сервера используйте docker compose up -d.
7. Практические советы и лучшие практики
7.1 Безопасность Docker
- Не запускайте контейнеры от root — используйте пользователя с ограниченными правами.
- Регулярно обновляйте образы — старые версии могут содержать уязвимости.
- Не храните секреты в образах — используйте переменные окружения или секреты (Docker Secrets).
- Ограничивайте ресурсы контейнеров (CPU и память) с помощью
--cpusи--memory. - Сканируйте образы на уязвимости (например, Docker Scout или Trivy).
7.2 Оптимизация и управление ресурсами
# Ограничение памяти и CPU при запуске
docker run -d --memory=256m --cpus=0.5 nginx
# Очистка неиспользуемых объектов Docker
docker system prune -af
# Очистка всех томов, образов и контейнеров
docker system prune -a --volumes⚠️ Осторожно: Команды docker system prune -a --volumes удаляют все неиспользуемые объекты, включая тома с данными. Не используйте их на продакшене без полного понимания последствий.
7.3 Логирование
По умолчанию Docker записывает всё в stdout/stderr. Для продакшена стоит настроить драйвер логов:
# В docker-compose.yml
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"7.4 Когда НЕ стоит использовать Docker
- Очень маленькие проекты — настройка Docker может быть избыточной.
- Приложения с высокими требованиями к производительности — особенно если критична задержка при работе с железом.
- Специфическое ПО — иногда проще установить напрямую на хост.
8. Заключение
Docker — это мощный и гибкий инструмент, который позволяет:
- Упаковывать приложения в изолированные и переносимые контейнеры.
- Описывать всю инфраструктуру кодом (Infrastructure as Code).
- Легко масштабировать и автоматизировать развёртывание.
Мы рассмотрели ключевые концепции, установку, базовые команды, создание образов, многоконтейнерные приложения и автоматизацию. Практические примеры показывают, как можно использовать Docker для организации реальных проектов.
Освоив контейнеризацию, вы перейдёте на новый уровень разработки и администрирования. Ваши проекты станут более надёжными, предсказуемыми и легко масштабируемыми.
На test.codestack.space/ собраны не только статьи, но и полезные инструменты для веб‑разработчиков и администраторов: конструкторы конфигураций, шаблоны Dockerfile, сниппеты для CI/CD и чек‑листы по безопасности — чтобы сложные вещи можно было быстро развернуть и адаптировать под свой сервер
👤 Зарегистрируйтесь, чтобы добавить материал в закладки.
