Dockerfile — 10 типовых образов
Для кого эта статья
Если в Google или Яндексе вы вводите dockerfile example, dockerfile nodejs, python dockerfile flask, golang dockerfile multistage, react nginx dockerfile, spring boot dockerfile, php dockerfile apache или как собрать docker образ — вы попали в галерею готовых Dockerfile с разбором каждой строки, как в популярной галерее Turtle на Python: сначала рабочий код, потом объяснение «зачем так», потом проверка.
Статья для:
- школьников и студентов — лабораторная, курсовой, отчёт «приложение в Docker»;
- самоучек, которые скопировали чужой Dockerfile и хотят понять, что означает
COPY,WORKDIR,EXPOSE; - тех, кто уже нашёл Docker Compose — готовые стеки и теперь нужен
build: .— свой образ под Node, Python, Go или фронт.
Полный справочник инструкций — глава Dockerfile. Команды CLI — Docker. Несколько контейнеров — Compose. Nginx после сборки фронта — конфиги nginx.
Как читать каждый пример в галерее
У всех десяти карточек одинаковая структура — так проще искать нужный стек и сдавать лабораторную:
| Блок | Что внутри |
|---|---|
| Задача | Что вы получите и типичные запросы в поиске |
| Смысл простыми словами | Зачем этот Dockerfile, без жаргона |
| Структура папки | Какие файлы положить рядом |
| Dockerfile | Готовый текст для копирования |
| Разбор по строкам | Таблица «строка → что делает Docker» |
Что происходит при docker build | Пошагово, какие слои создаются |
| Сборка и проверка | Команды и что вы должны увидеть |
| Если не работает | Типичные ошибки новичков |
В примерах Turtle вы копируете код, запускаете и видите черепашку. Здесь то же самое: скопировали Dockerfile → docker build → docker run → открыли браузер или прочитали вывод в терминале. Разница только в том, что «холст» — это образ, а «кисть» — инструкции FROM, COPY, RUN.
Три слова — образ, контейнер, слой
| Термин | Простыми словами | Аналогия |
|---|---|---|
| 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 образов
| № | Сценарий | Поиск в Google | Порт |
|---|---|---|---|
| 1 | Проверка Docker | dockerfile alpine hello world | — |
| 2 | Статический сайт | nginx dockerfile static | 80 |
| 3 | Node.js API | node dockerfile example production | 3000 |
| 4 | Python API | python dockerfile flask gunicorn | 5000 |
| 5 | Go-сервис | golang dockerfile multistage | 8080 |
| 6 | React/Vue + nginx | react dockerfile nginx vite | 80 |
| 7 | Spring Boot | spring boot dockerfile jar | 8080 |
| 8 | PHP-сайт | php dockerfile apache | 80 |
| 9 | .NET API | aspnet core dockerfile | 8080 |
| 10 | Миграции / seed | docker run once script | — |
1. Минимальный образ — «Привет, Docker»
Задача: убедиться, что Docker установлен и команды build / run работают.
Поиск: dockerfile alpine hello world, docker tutorial first image.
Смысл простыми словами: вы не пишете приложение — вы проверяете цепочку «рецепт → образ → контейнер». Три строки Dockerfile достаточно для первой лабораторной по контейнерам.
Структура:
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 dockerfile static site, dockerfile copy html nginx.
Смысл простыми словами: образ — это 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 dockerfile example, node dockerfile multistage npm ci, express dockerfile production.
Смысл простыми словами: две стадии сборки. На первой ставят node_modules, на второй — только готовые файлы и зависимости для запуска. Компиляторы и eslint в финальный образ не попадают. Это самый частый запрос среди студентов на fullstack-курсах.
Структура:
my-api/
├── package.json
├── package-lock.json
├── src/
│ └── server.js
├── Dockerfile
└── .dockerignore
Минимальный src/server.js (чтобы пример завёлся):
const http = require('http');
const server = http.createServer((req, res) => {
if (req.url === '/health') {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('ok');
return;
}
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Node API в Docker работает\n');
});
// 0.0.0.0 — слушать все интерфейсы; иначе с хоста не достучаться
server.listen(3000, '0.0.0.0', () => {
console.log('Слушаю http://0.0.0.0:3000');
});
Dockerfile:
FROM node:20-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY /app/node_modules ./node_modules
COPY package.json ./
COPY src ./src
RUN addgroup -S app && adduser -S app -G app
USER app
EXPOSE 3000
HEALTHCHECK \
CMD wget -qO- http://127.0.0.1:3000/health || exit 1
CMD ["node", "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 dockerfile example, flask dockerfile requirements.txt, fastapi dockerfile.
Смысл простыми словами: образ содержит интерпретатор 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:
FROM python:3.12-slim
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
APP_HOME=/app
WORKDIR $APP_HOME
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
RUN adduser --disabled-password --gecos '' appuser && \
chown -R appuser:appuser $APP_HOME
USER appuser
EXPOSE 5000
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "2", "app:app"]
Разбор по строкам:
| Строка | Смысл |
|---|---|
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.
Поиск: golang dockerfile multistage, go dockerfile alpine, dockerfile go build.
Смысл простыми словами: на стадии builder компилятор Go превращает исходники в файл /server. В финальный образ копируется только этот файл — ни go.mod, ни исходников, ни компилятора. Образ на диске часто 15–25 МБ вместо сотен.
Структура:
go-service/
├── go.mod
├── go.sum
├── cmd/
│ └── server/
│ └── main.go
└── Dockerfile
Минимальный cmd/server/main.go:
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Go сервис в Docker")
})
http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, "ok")
})
http.ListenAndServe(":8080", nil)
}
Dockerfile:
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /server ./cmd/server
FROM alpine:3.19
RUN adduser -D appuser
USER appuser
COPY /server /server
EXPOSE 8080
CMD ["/server"]
Разбор по строкам:
| Строка | Смысл |
|---|---|
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 работают при обновлении страницы.
Поиск: react dockerfile nginx, vite dockerfile production, spa nginx try_files docker.
Смысл простыми словами: браузеру нужны только файлы из 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 /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 dockerfile example, dockerfile maven spring boot.
Смысл простыми словами: Spring Boot упаковывает приложение в один .jar со встроенным Tomcat. В Dockerfile сначала Maven собирает JAR, потом в лёгкий образ копируется только app.jar и команда java -jar.
Структура:
spring-app/
├── pom.xml
├── mvnw
├── .mvn/
├── src/
└── Dockerfile
Dockerfile:
FROM eclipse-temurin:21-jdk-alpine AS builder
WORKDIR /app
COPY pom.xml mvnw ./
COPY .mvn .mvn
COPY src ./src
RUN chmod +x mvnw && ./mvnw -q -DskipTests package
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
RUN addgroup -S app && adduser -S app -G app
USER app
COPY /app/target/*.jar app.jar
EXPOSE 8080
HEALTHCHECK \
CMD wget -qO- http://127.0.0.1:8080/actuator/health || exit 1
ENTRYPOINT ["java", "-jar", "app.jar"]
Разбор по строкам:
| Строка | Смысл |
|---|---|
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 dockerfile apache, dockerfile php website.
Смысл простыми словами: образ 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-образе.
Поиск: aspnet core dockerfile, dotnet dockerfile multistage.
Смысл простыми словами: стадия sdk компилирует проект (dotnet publish), стадия aspnet только запускает готовые DLL. Kestrel слушает порт из ASPNETCORE_URLS.
Структура:
dotnet-api/
├── DotnetApi.csproj
├── Program.cs
└── Dockerfile
Dockerfile:
FROM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS build
WORKDIR /src
COPY DotnetApi.csproj .
RUN dotnet restore
COPY . .
RUN dotnet publish -c Release -o /app/publish /p:UseAppHost=false
FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine
WORKDIR /app
RUN addgroup -S app && adduser -S app -G app
USER app
COPY /app/publish .
ENV ASPNETCORE_URLS=http://+:8080
EXPOSE 8080
HEALTHCHECK \
CMD wget -qO- http://127.0.0.1:8080/health || exit 1
ENTRYPOINT ["dotnet", "DotnetApi.dll"]
Разбор по строкам:
| Строка | Смысл |
|---|---|
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.
Поиск: dockerfile run script once, docker batch job example.
Смысл простыми словами: это не веб-сервер. Вы ждёте код выхода 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 |
| Примеры Turtle | тот же формат галереи для Python |
См. также
Другие статьи этого же раздела в боковом меню (как на странице "О разделе"). Практическая карта типовых IT-задач: термины, пошаговое внедрение, проверка качества и типичные ошибки. Простой консольный чат на C# — учебное приложение с сокетами: TCP между клиентом и сервером, многопоточность и обмен сообщениями в консоли. Примеры вёрстки на HTML и CSS с разбором: центрирование, Flexbox, Grid, формы, шапка, подвал и адаптив для учебы и портфолио. Перед началом работы обязательно изучите главу Turtle . Галерея 3D-фигур на Panda3D — карточки, куб, пирамида, сфера, сетки и составные сцены; код для локального запуска. Готовые docker-compose.yml с разбором каждой строки — nginx, PostgreSQL, Redis, WordPress, MongoDB. Примеры для школьников и студентов: postgres example, поднять базу локально, app + db. Примеры nginx.conf для статики, reverse proxy, React/Vue SPA, PHP, SSL и балансировки — построчный разбор директив, проверка curl и типичные ошибки для лабораторных и VPS. PromQL example — готовые запросы Prometheus и Grafana с построчным разбором: up, rate, node_exporter cpu, memory, disk, http_requests_total, histogram_quantile p99, алерты. Для студентов, лабораторных и devops docker compose. Готовые манифесты Kubernetes с разбором каждой строки — Pod, Deployment, Service, ConfigMap, Secret, Ingress. Примеры для Minikube, kind и kubectl apply. Примеры графиков Matplotlib на Python для школьников и студентов — sin, cos, парабола, столбцы, scatter, гистограмма, подграфики; код с подробным разбором. Примеры pandas на Python для школьников и студентов — DataFrame, фильтрация, groupby, очистка, merge, сводные таблицы и экспорт; код с подробным разбором каждой строки. p5.js и Processing — готовый код фигур с подробным разбором каждой строки: квадрат, треугольник, цветок, снежинка, фракталы, анимация; для школьников и студентов.Готовые решения
Простой консольный чат на CSharp
HTML + CSS — готовые макеты
Примеры фигур Turtle на Python
Примеры фигур Panda3D на Python
Docker Compose — готовые стеки
Nginx — конфиги под задачу
Prometheus + Grafana — запросы
Kubernetes YAML — минимальные манифесты
Matplotlib — графики
Pandas — типовые операции
Примеры фигур на Processing/p5.js