Dockerfile — 10 типовых образов
Приветствую! Здесь вы наверняка найдете, что ищете. Примеры в лаборатории рассчитаны на то, что мы разбираем что-то конкретное.
Текущая статья посвящена типовым Dockerfile (Node, Python, Go, React+Nginx, Spring Boot, PHP, .NET) — построчный разбор и проверка docker build.
Поэтому за теорией по текущей теме вам — в энциклопедию. Если ещё не погружались, то маршрут прост:
- Основы
- Система и сеть
- Данные и разметка
- Код и разработка
- Языки
- Искусственный интеллект
- Проект
- Инфраструктура и безопасность
- Спин-офф
Обязательно пройдитесь.
А теперь приступим к нашему предмету.
Полный справочник инструкций — глава Dockerfile.
Команды CLI — Docker.
Несколько контейнеров — Compose.
Nginx после сборки фронта — конфиги nginx.
Три слова — образ, контейнер, слой
| Термин | Простыми словами | Аналогия |
|---|---|---|
| Dockerfile | Текстовый рецепт «как собрать программу в коробку» | Рецепт торта |
| Образ (image) | Готовая «коробка» с ОС, файлами и командой запуска | Замороженный торт из магазина |
| Контейнер | Запущенный экземпляр образа (процесс) | Торт на столе, который едят |
| Слой (layer) | Каждая строка RUN / COPY добавляет шаг; Docker кэширует неизменённые шаги |
Слои в торте: пока не меняете крем, нижние слои не перепекаете |
Dockerfile docker build docker run
────────── ──────────── ──────────
FROM node.. → образ my-api:1 → контейнер (процесс node)
COPY ..
RUN npm ci
CMD ["node", ..]
Контекст сборки — папка, которую вы указываете точкой в конце: docker build -t имя .
В эту папку Docker смотрит при COPY. Лишнее отсекает .dockerignore.
Поиграйте с порядком инструкций в симуляторе выше — затем разберите пример №3 Node.js: там видно, зачем сначала копируют package-lock.json, потом исходники.
Шпаргалка инструкций Dockerfile
| Инструкция | Когда выполняется | Зачем нужна |
|---|---|---|
FROM |
Начало сборки / новая стадия | «На чём строим» — alpine, node, python… |
WORKDIR |
При сборке и в контейнере | Текущая папка для COPY и RUN |
COPY |
Сборка | Скопировать файлы с вашего ПК в образ |
RUN |
Сборка | Установить пакеты, собрать проект |
ENV |
Сборка + контейнер | Переменные среды (NODE_ENV, пути) |
EXPOSE |
Документация | Какой порт слушает приложение внутри |
USER |
Сборка + контейнер | От какого пользователя идёт процесс |
CMD / ENTRYPOINT |
Только при docker run |
Что запустить, когда контейнер стартует |
HEALTHCHECK |
Во время работы контейнера | Docker периодически проверяет «жив ли сервис» |
Важно: EXPOSE 3000 не открывает порт на вашем ноутбуке. Чтобы зайти из браузера, нужен docker run -p 8080:3000 (слева — ваш ПК, справа — порт в контейнере).
Сборка и запуск
- Создайте папку проекта (например
my-api/). - Положите
Dockerfileи.dockerignoreв корень. - Соберите образ (имя и тег — любые):
docker build -t myapp:1 .
- Запустите контейнер:
docker run --rm -p 8080:3000 myapp:1
| Часть команды | Смысл |
|---|---|
docker build |
Прочитать Dockerfile и собрать образ |
-t myapp:1 |
Имя образа myapp, тег 1 (версия) |
. |
Контекст — текущая папка |
docker run |
Создать контейнер из образа и запустить |
--rm |
Удалить контейнер после остановки |
-p 8080:3000 |
localhost:8080 на ПК → порт 3000 в контейнере |
Перед docker build Docker Desktop должен быть Running. Ошибка Cannot connect to the Docker daemon — демон не запущен, а не опечатка в Dockerfile.
Общий .dockerignore
Создайте файл .dockerignore рядом с Dockerfile:
.git
.gitignore
.env
*.log
node_modules
__pycache__
.venv
dist
build
target
coverage
.idea
.vscode
README.md
Разбор по строкам:
| Строка | Зачем |
|---|---|
.git |
История Git не нужна в образе; сборка быстрее |
.env |
Пароли и ключи нельзя запекать в слои образа |
node_modules |
Зависимости ставят npm ci внутри RUN, а не копируют с Windows/macOS |
dist / target |
Собранные файлы с хоста могут быть от другой ОС |
README.md |
Документация в runtime не нужна |
Без .dockerignore команда COPY . . может затянуть гигабайты и сломать кэш.
Оглавление — 10 образов
| № | Сценарий | Порт |
|---|---|---|
| 1 | Проверка Docker | — |
| 2 | Статический сайт | 80 |
| 3 | Node.js API | 3000 |
| 4 | Python API | 5000 |
| 5 | Go-сервис | 8080 |
| 6 | React/Vue + nginx | 80 |
| 7 | Spring Boot | 8080 |
| 8 | PHP-сайт | 80 |
| 9 | .NET API | 8080 |
| 10 | Миграции / seed | — |
1. Минимальный образ — «Привет, Docker»
Задача: убедиться, что Docker установлен и команды build / run работают.
Смысл простыми словами: вы не пишете приложение — вы проверяете цепочку «рецепт → образ → контейнер». Три строки Dockerfile достаточно для первой проверки Docker.
Структура:
hello/
├── Dockerfile
└── .dockerignore
Dockerfile:
FROM alpine:3.19
RUN echo "Образ собран успешно"
CMD ["echo", "Привет из контейнера!"]
Разбор по строкам:
| Строка | Что делает Docker | Зачем так |
|---|---|---|
FROM alpine:3.19 |
Берёт готовый базовый образ с Docker Hub (~7 МБ) | Минимальная «операционка» для учебы; тег 3.19 фиксирует версию |
RUN echo "Образ собран…" |
Во время сборки выполняет команду и создаёт новый слой | Показывает разницу: RUN = при build, не при run |
CMD ["echo", "Привет…"] |
Команда по умолчанию при docker run |
Exec-форма ["программа", "арг1"] — без оболочки /bin/sh |
Что происходит при docker build -t hello:1 .:
- Docker скачивает
alpine:3.19, если его ещё нет локально. - Создаёт временный контейнер, выполняет
echo→ фиксирует слой. - Записывает в образ метаданные
CMD. - Помечает результат тегом
hello:1.
Сборка и проверка:
docker build -t hello:1 .
docker run --rm hello:1
| Шаг | Ожидаемый результат |
|---|---|
build |
В конце Successfully tagged hello:1 |
run |
В терминале строка Привет из контейнера! |
docker ps после run |
Пусто — контейнер уже завершился |
Если не работает:
| Ошибка | Причина | Что сделать |
|---|---|---|
Cannot connect to the Docker daemon |
Docker не запущен | Запустить Docker Desktop |
unable to prepare context |
Нет прав на папку | Открыть терминал в каталоге hello/ |
| Пустой вывод | Старая версия Docker | Обновить Docker Desktop |
2. Статический сайт на nginx
Задача: отдать HTML/CSS из папки public/ без Node, Python и без npm run build.
Смысл простыми словами: образ — это nginx + ваши файлы. Браузер запрашивает страницу → nginx читает файл с диска внутри контейнера → отдаёт HTML. Подходит для лендинга, учебного сайта, отчёта по вебу.
Структура:
static-site/
├── public/
│ └── index.html
├── Dockerfile
└── .dockerignore
public/index.html:
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<title>Статика в Docker</title>
</head>
<body>
<h1>Сайт из Docker</h1>
<p>Файл лежит в public/index.html и копируется в образ.</p>
</body>
</html>
Dockerfile:
FROM nginx:1.27-alpine
COPY public/ /usr/share/nginx/html/
EXPOSE 80
HEALTHCHECK CMD wget -qO- http://127.0.0.1/ || exit 1
Разбор по строкам:
| Строка | Что делает | Зачем |
|---|---|---|
FROM nginx:1.27-alpine |
Базовый образ с уже установленным nginx | Не ставим nginx вручную через apt |
COPY public/ /usr/share/nginx/html/ |
Копирует содержимое public/ в стандартную папку статики nginx |
URL / → файл index.html |
EXPOSE 80 |
Пишет в метаданные «сервис слушает 80» | Напоминание; порт на хост задаёт -p |
HEALTHCHECK … wget … |
Раз в 30 с дергает главную страницу | Статус healthy в docker ps; в учебе можно убрать |
Что происходит при docker build:
- Слой nginx (из Hub).
- Новый слой — ваши HTML-файлы поверх
/usr/share/nginx/html/. - Образ готов; nginx не запущен до
docker run.
Сборка и проверка:
docker build -t static:1 .
docker run --rm -p 8080:80 static:1
| Действие | Результат |
|---|---|
Открыть http://localhost:8080 |
Заголовок «Сайт из Docker» |
docker run без -p |
Сайт не откроется с ПК — порт не проброшен |
Разбор -p 8080:80:
| Число | Где |
|---|---|
8080 |
Порт на вашем компьютере (localhost) |
80 |
Порт внутри контейнера, где слушает nginx |
Тот же nginx одной строкой в Compose — стек №1.
Если не работает:
| Симптом | Причина |
|---|---|
| 403 Forbidden | Нет index.html или неверный путь COPY |
| Пустая страница Welcome to nginx | COPY не туда — проверьте public/ |
| Connection refused | Забыли -p или занят порт 8080 — смените на -p 8888:80 |
3. Node.js API (Express / Fastify)
Задача: упаковать REST API в образ для production: без dev-зависимостей, с кэшем npm и не от root.
Смысл простыми словами: две стадии сборки. На первой ставят node_modules, на второй — только готовые файлы и зависимости для запуска. Компиляторы и eslint в финальный образ не попадают. Это самый частый запрос среди студентов на fullstack-курсах.
Структура:
my-api/
├── package.json
├── package-lock.json
├── src/
│ └── server.js
├── Dockerfile
└── .dockerignore
Минимальный src/server.js (чтобы пример завёлся):
Разбор по строкам:
| Строка | Что делает | Зачем |
|---|---|---|
FROM node:20-alpine AS deps |
Стадия 1 — только установка зависимостей | Имя deps для COPY --from=deps |
WORKDIR /app |
Дальше все пути относительно /app |
Как cd /app в терминале |
COPY package.json package-lock.json ./ |
Копирует только манифесты | При правке server.js слой npm ci берётся из кэша |
RUN npm ci --omit=dev |
Ставит пакеты строго по lock-файлу | ci = как в CI; --omit=dev без jest/eslint |
FROM node:20-alpine AS runner |
Стадия 2 — чистый runtime | В финале нет мусора стадии deps |
ENV NODE_ENV=production |
Переменная для Node и библиотек | Меньше отладочного режима |
COPY --from=deps … node_modules |
Забирает папку из другой стадии | Ключ multi-stage |
COPY src ./src |
Исходники после зависимостей | Правка кода не перезапускает npm ci |
adduser + USER app |
Процесс не root | Требование безопасности в отчётах |
EXPOSE 3000 |
Документирует порт API | Связка с -p …:3000 |
HEALTHCHECK … /health |
Проверка эндпоинта из server.js |
Без /health контейнер станет unhealthy |
CMD ["node", "src/server.js"] |
Запуск при docker run |
Exec-форма — корректные сигналы остановки |
Что происходит при docker build:
- Стадия
deps: слоиCOPY package*→RUN npm ci(долго только при смене lock-файла). - Стадия
runner: копированиеnode_modulesизdeps, затемsrc. - Слой
USER app— все последующие команды от непривилегированного пользователя. - В финальный образ не входят исходники стадии
deps, кромеnode_modules.
Сборка и проверка:
docker build -t my-api:1 .
docker run --rm -p 3000:3000 my-api:1
В другом терминале:
curl http://localhost:3000/health
curl http://localhost:3000/
| Команда | Ожидаемый ответ |
|---|---|
/health |
ok |
/ |
Node API в Docker работает |
Если не работает:
| Симптом | Причина | Решение |
|---|---|---|
curl с хоста пустой / timeout |
Сервер слушает 127.0.0.1 |
В коде listen(3000, '0.0.0.0') |
npm ci падает |
Нет package-lock.json |
Выполнить npm install локально и закоммитить lock |
EACCES при старте |
Права на файлы | Перед USER добавить chown -R app:app /app |
unhealthy |
Нет /health |
Добавить маршрут или убрать HEALTHCHECK |
Манифесты npm — Манифесты зависимостей. API + Postgres в Compose — стек app + db.
4. Python (Flask / FastAPI)
Задача: запустить веб-приложение Python через gunicorn (production), а не через flask run.
Смысл простыми словами: образ содержит интерпретатор Python, установленные pip-пакеты и ваш app.py. При docker run стартует gunicorn — он принимает HTTP и передаёт запросы во Flask. Порядок COPY requirements.txt → pip install → COPY . . экономит время при каждой правке кода.
Структура:
flask-app/
├── app.py
├── requirements.txt
├── Dockerfile
└── .dockerignore
app.py (минимум):
from flask import Flask
app = Flask(__name__)
@app.route("/")
def index():
return "Flask в Docker\n"
@app.route("/health")
def health():
return "ok"
requirements.txt:
flask==3.0.0
gunicorn==21.2.0
Dockerfile:
Разбор по строкам:
| Строка | Смысл |
|---|---|
python:3.12-slim |
Официальный образ Python; slim меньше, чем полный |
PYTHONDONTWRITEBYTECODE=1 |
Не создавать .pyc в образе |
PYTHONUNBUFFERED=1 |
print и логи сразу в docker logs |
APP_HOME=/app |
Переменная для пути; удобно менять одно место |
WORKDIR $APP_HOME |
Рабочая директория /app |
COPY requirements.txt + RUN pip install |
Слой зависимостей отдельно от кода |
COPY . . |
Весь проект после pip |
adduser / chown / USER appuser |
Файлы принадлежат пользователю, не root |
gunicorn --bind 0.0.0.0:5000 |
Слушать снаружи контейнера |
--workers 2 |
Два процесса — для учебы достаточно |
app:app |
Модуль app.py, объект Flask app |
Таблица кэша (как в отчёте по Docker):
| Изменили файл | Что пересобирается |
|---|---|
Только app.py |
Слои начиная с COPY . . |
requirements.txt |
pip install и всё ниже |
Dockerfile |
Часто вся сборка |
Сборка и проверка:
docker build -t flask-app:1 .
docker run --rm -p 5000:5000 flask-app:1
curl http://localhost:5000/
curl http://localhost:5000/health
FastAPI — замените зависимости и CMD:
fastapi==0.110.0
uvicorn[standard]==0.27.0
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "5000"]
Если не работает:
| Ошибка | Причина |
|---|---|
ModuleNotFoundError: flask |
Нет строки в requirements.txt |
Failed to find attribute app |
В app.py объект должен называться app |
| Порт занят | docker run -p 5001:5000 |
5. Go-сервис (multi-stage)
Задача: собрать один бинарник и положить его в крошечный образ без Go SDK.
Смысл простыми словами: на стадии builder компилятор Go превращает исходники в файл /server. В финальный образ копируется только этот файл — ни go.mod, ни исходников, ни компилятора. Образ на диске часто 15–25 МБ вместо сотен.
Структура:
go-service/
├── go.mod
├── go.sum
├── cmd/
│ └── server/
│ └── main.go
└── Dockerfile
Минимальный cmd/server/main.go:
Разбор по строкам:
| Строка | Смысл |
|---|---|
golang:1.22-alpine AS builder |
Образ с компилятором Go |
COPY go.mod go.sum + go mod download |
Кэш модулей до копирования исходников |
CGO_ENABLED=0 |
Бинарник без привязки к C-библиотекам хоста |
GOOS=linux |
Целевая ОС внутри контейнера |
-ldflags="-s -w" |
Убрать отладочные символы — файл меньше |
-o /server ./cmd/server |
Имя бинарника и пакет с main |
FROM alpine:3.19 |
Финал — только Linux + ваш бинарник |
COPY --from=builder /server /server |
Единственный артефакт из стадии сборки |
CMD ["/server"] |
Запуск бинарника как PID 1 |
Сборка и проверка:
docker build -t go-svc:1 .
docker run --rm -p 8080:8080 go-svc:1
curl http://localhost:8080/health
Если не работает:
| Ошибка | Решение |
|---|---|
go: cannot find main module |
Выполнить go mod init в корне проекта |
no Go files |
Путь ./cmd/server должен содержать package main |
Distroless-вариант — энциклопедия, Go.
6. React / Vue SPA + nginx
Задача: собрать фронт (npm run build), раздавать статику через nginx; маршруты React Router работают при обновлении страницы.
Смысл простыми словами: браузеру нужны только файлы из dist/ (HTML, JS, CSS). Node.js в production не обязателен — он нужен лишь на этапе сборки. Поэтому два этапа: builder (Node) и runner (nginx).
Структура:
frontend/
├── package.json
├── package-lock.json
├── vite.config.js
├── index.html
├── src/
├── nginx.conf
└── Dockerfile
nginx.conf:
server {
listen 80;
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
}
Разбор nginx (зачем в лабораторной):
| Строка | Смысл |
|---|---|
root /usr/share/nginx/html |
Сюда Dockerfile копирует dist/ |
try_files $uri $uri/ /index.html |
Если файла нет (маршрут /about) — отдать index.html, React дорисует страницу |
Dockerfile:
FROM node:20-alpine AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM nginx:1.27-alpine
COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80
HEALTHCHECK CMD wget -qO- http://127.0.0.1/ || exit 1
Разбор по строкам:
| Строка | Смысл |
|---|---|
npm ci + npm run build |
Сборка фронта в стадии builder |
COPY --from=builder /app/dist |
В nginx попадает только результат сборки |
nginx.conf → conf.d/default.conf |
Подмена дефолтного виртуального хоста |
Нет CMD |
В образе nginx уже задана команда запуска |
Сборка и проверка:
docker build -t spa:1 .
docker run --rm -p 8080:80 spa:1
Откройте http://localhost:8080. Для React Router зайдите на вложенный путь (если есть роуты) — без try_files будет 404 от nginx.
Если не работает:
| Симптом | Причина |
|---|---|
| Пустая страница | Неверная папка — у Vite это dist, у Create React App тоже часто dist |
404 на /about |
Нет try_files в nginx.conf |
npm run build падает в Docker |
Не хватает памяти — закройте лишние программы |
Подробнее proxy и TLS — Nginx — конфиги. Компоненты React — галерея React.
7. Java Spring Boot (JAR)
Задача: собрать fat JAR в образе с JDK, запускать на JRE.
Смысл простыми словами: Spring Boot упаковывает приложение в один .jar со встроенным Tomcat. В Dockerfile сначала Maven собирает JAR, потом в лёгкий образ копируется только app.jar и команда java -jar.
Структура:
spring-app/
├── pom.xml
├── mvnw
├── .mvn/
├── src/
└── Dockerfile
Dockerfile:
Разбор по строкам:
| Строка | Смысл |
|---|---|
eclipse-temurin:21-jdk-alpine |
JDK для компиляции |
./mvnw … package |
Сборка JAR без установленного Maven на ПК |
-DskipTests |
Быстрее для учебной сборки |
jre-alpine в финале |
Только среда выполнения, без компилятора |
COPY … *.jar app.jar |
Один понятный файл в runtime |
/actuator/health |
Эндпоинт Spring Actuator (нужна зависимость в pom.xml) |
ENTRYPOINT ["java", "-jar", "app.jar"] |
Команда запуска; аргументы docker run дописываются в конец |
Сборка и проверка:
docker build -t spring:1 .
docker run --rm -p 8080:8080 spring:1
Первая сборка может занять 5–15 минут — Maven скачивает зависимости.
Если не работает:
| Ошибка | Решение |
|---|---|
no main manifest |
В pom.xml должен быть spring-boot-maven-plugin |
| Health 404 | Добавить spring-boot-starter-actuator или убрать HEALTHCHECK |
Несколько JAR в target/ |
Уточнить имя: COPY …/myapp-0.0.1-SNAPSHOT.jar app.jar |
8. PHP с Apache
Задача: быстрый учебный сайт на PHP без настройки php-fpm и второго контейнера.
Смысл простыми словами: образ php:8.3-apache уже содержит веб-сервер Apache и модуль PHP. Вы копируете .php файлы в /var/www/html/ — Apache выполняет PHP и отдаёт результат браузеру.
Структура:
php-site/
├── public/
│ └── index.php
└── Dockerfile
public/index.php:
<?php
header('Content-Type: text/plain; charset=utf-8');
echo "PHP " . PHP_VERSION . " в Docker\n";
echo "Время: " . date('c') . "\n";
Dockerfile:
FROM php:8.3-apache
RUN docker-php-ext-install pdo pdo_mysql
COPY public/ /var/www/html/
EXPOSE 80
Разбор по строкам:
| Строка | Смысл |
|---|---|
php:8.3-apache |
PHP 8.3 + Apache в одном образе |
docker-php-ext-install pdo pdo_mysql |
Расширения для лабораторных с MySQL |
COPY public/ /var/www/html/ |
index.php доступен как http://…/ |
EXPOSE 80 |
Apache слушает 80 внутри контейнера |
Сборка и проверка:
docker build -t php-site:1 .
docker run --rm -p 8080:80 php-site:1
В браузере — версия PHP и время.
Если не работает:
| Симптом | Причина |
|---|---|
| Скачивается файл вместо выполнения | Файл не .php или не в public/ |
| 403 | Нет index.php в /var/www/html |
Production-вариант — nginx + php-fpm в двух сервисах: Nginx PHP-FPM, WordPress Compose.
9. ASP.NET Core
Задача: собрать и опубликовать .NET 8 Web API в компактном runtime-образе.
Смысл простыми словами: стадия sdk компилирует проект (dotnet publish), стадия aspnet только запускает готовые DLL. Kestrel слушает порт из ASPNETCORE_URLS.
Структура:
dotnet-api/
├── DotnetApi.csproj
├── Program.cs
└── Dockerfile
Dockerfile:
Разбор по строкам:
| Строка | Смысл |
|---|---|
COPY DotnetApi.csproj + dotnet restore |
Кэш NuGet до копирования всего кода |
dotnet publish … -o /app/publish |
Готовые файлы для запуска |
UseAppHost=false |
Запуск через dotnet MyApp.dll (проще в Linux-контейнере) |
aspnet:8.0-alpine |
Runtime без SDK |
ASPNETCORE_URLS=http://+:8080 |
Kestrel на всех интерфейсах, порт 8080 |
DotnetApi.dll |
Имя = имя проекта в .csproj |
Сборка и проверка:
docker build -t dotnet-api:1 .
docker run --rm -p 8080:8080 dotnet-api:1
В Program.cs для учебы:
app.MapGet("/", () => "ASP.NET в Docker");
app.MapGet("/health", () => "ok");
Если не работает:
| Ошибка | Решение |
|---|---|
could not find DotnetApi.dll |
Имя DLL = имя .csproj |
| Connection refused с хоста | Проверить ASPNETCORE_URLS и -p 8080:8080 |
10. Job-контейнер (миграции или seed)
Задача: контейнер запускает скрипт и завершается — миграции БД, загрузка тестовых данных, ночной batch.
Смысл простыми словами: это не веб-сервер. Вы ждёте код выхода 0 (успех) и статус Exited (0) в docker ps -a. В Kubernetes тот же паттерн — ресурс Job.
Структура:
db-job/
├── migrate.sh
└── Dockerfile
migrate.sh:
#!/bin/sh
set -e
echo "Подключение: $DATABASE_URL"
# Раскомментируйте для реальной БД:
# psql "$DATABASE_URL" -f ./migrations/001_init.sql
echo "Миграции применены"
Dockerfile:
FROM alpine:3.19
RUN apk add --no-cache postgresql-client bash
WORKDIR /job
COPY migrate.sh .
RUN chmod +x migrate.sh
USER nobody
ENTRYPOINT ["./migrate.sh"]
Разбор по строкам:
| Строка | Смысл |
|---|---|
apk add postgresql-client |
Утилита psql для SQL |
chmod +x migrate.sh |
Скрипт исполняемый |
USER nobody |
Даже одноразовая задача не от root |
ENTRYPOINT ["./migrate.sh"] |
При docker run всегда стартует скрипт |
Нет EXPOSE |
Сетевой сервер не поднимается |
Запуск рядом с Postgres из Compose:
docker build -t db-job:1 .
docker run --rm \
-e DATABASE_URL=postgres://app:secret@db:5432/appdb \
--network myproject_default \
db-job:1
| Параметр | Смысл |
|---|---|
-e DATABASE_URL=… |
Пароль не в образе, только при запуске |
--network … |
Имя db резолвится в IP контейнера PostgreSQL |
--rm |
Удалить контейнер после успеха |
Имя сети смотрите: docker network ls после docker compose up из галереи Compose.
Проверка: в выводе Миграции применены; docker ps -a — контейнер Exited (0).
Слои и кэш — одна таблица на все примеры
| Правило | Пример |
|---|---|
| Редко меняющееся — выше | COPY package.json перед COPY src |
| Тяжёлое — отдельный слой | RUN npm ci, RUN pip install |
| Секреты — не в образ | .env в .dockerignore, -e при run |
| Фиксируйте версии | node:20-alpine, не node:latest |
| Меньше финальный образ | multi-stage для Go, Node, Java, .NET, SPA |
Проверить слои:
docker history my-api:1 --no-trunc
Частые ошибки (все примеры)
| Ошибка | Что значит | Что проверить |
|---|---|---|
COPY failed: file not found |
Нет файла в контексте | Путь, .dockerignore, вы в нужной папке |
port is already allocated |
Порт занят на хосте | Другой -p 8888:3000 |
| Сайт не открывается | Нет проброса порта | -p хост:контейнер |
| API не отвечает с ПК | Слушает только localhost | 0.0.0.0 в коде |
permission denied |
USER без прав на файлы |
chown перед USER |
| Огромный образ | Всё в одной стадии | Multi-stage, .dockerignore |
Чек-лист перед сдачей лабораторной
- В Dockerfile зафиксированы теги (
node:20-alpine), неlatest. - Есть
.dockerignore, нет.envв образе. - Зависимости копируются до исходников.
- В README —
docker build,docker run, URL илиcurlдля проверки. - Для HTTP указаны
EXPOSEи-p. - Скриншот
docker psили ответа в браузере приложен к отчёту.
Связанные материалы
| Материал | Зачем |
|---|---|
| Dockerfile (теория) | все инструкции, анти-паттерны |
| Docker | build, run, push |
| Docker Compose — готовые стеки | build: . + БД + nginx |
| Nginx — конфиги | SPA, proxy, PHP-FPM |
| Шаблоны | минимальный Dockerfile на одной странице |
| GitHub Actions — CI/CD | docker build в pipeline |