Перейти к основному контенту
Tech Path Finder
КурсыИнтервьюКод-ревьюБлог
Tech Path Finder

Персонализированный путеводитель в IT. Квизы, мок-интервью, код ревью и аналитика прогресса.

@potapov_me

Платформа

  • Курсы
  • Прогресс
  • Мок-интервью
  • Код ревью
  • Живое ревью с ИИ
  • Тренажёр переговоров
  • Закладки

Контент

  • Блог
  • Главная
  • Обратная связь

Компания

  • О проекте
  • Тарифы
  • Условия использования
  • Конфиденциальность
  • Согласие на обработку данных
  • Cookie
  • Реквизиты

Аккаунт

  • Войти
  • Зарегистрироваться
  • Профиль

© 2026 Tech Path Finder. Все права защищены.

·ИП Потапов К.С.·Политика конфиденциальности·
Сделано с ❤️ в России
  1. Создание Dockerfile
dockerfile

Создание Dockerfile

Инструкции Dockerfile, лучшие практики написания

Создание Dockerfile

Dockerfile — это рецепт сборки вашего образа. Изучите все инструкции и лучшие практики написания.

#Что такое Dockerfile?

Dockerfile — это текстовый файл с инструкциями для автоматической сборки Docker-образа. Каждая инструкция создаёт новый слой образа.

Dockerfile позволяет:

  • Воспроизводить сборку образа
  • Версионировать конфигурацию приложения
  • Автоматизировать процесс сборки в CI/CD

#Базовая структура

Пример Dockerfile для Python-приложения:

# Базовый образ FROM python:3.11-slim # Метаданные LABEL maintainer="John Doe <john@example.com>" LABEL version="1.0" # Рабочая директория WORKDIR /app # Копирование зависимостей COPY requirements.txt . # Установка зависимостей RUN pip install --no-cache-dir -r requirements.txt # Копирование кода приложения COPY . . # Переменные окружения ENV PYTHONUNBUFFERED=1 ENV APP_ENV=production # Проброс порта (документация) EXPOSE 8000 # Пользователь (безопасность) RUN useradd --create-home --shell /bin/bash appuser USER appuser # Команда запуска CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]

#Инструкции Dockerfile

#FROM

FROM задаёт базовый образ. Должна быть первой инструкцией (кроме ARG).

# Базовый синтаксис FROM <образ>[:<тег>] # Примеры FROM python:3.11-slim FROM node:18-alpine FROM ubuntu:22.04

Рекомендации:

  • Используйте конкретные теги (python:3.11.4-slim, не python:latest)
  • Выбирайте минимальные образы (slim, alpine)
  • Для multi-stage используйте несколько инструкций FROM

#LABEL

LABEL добавляет метаданные к образу.

LABEL maintainer="John Doe <john@example.com>" LABEL version="1.0" LABEL description="My Python Application" # Несколько меток в одной инструкции LABEL com.example.vendor="ACME" \ com.example.version="1.0" \ com.example.release-id="2024.01"

Просмотр метаданных:

docker inspect --format='{{json .Config.Labels}}' myapp

#WORKDIR

WORKDIR устанавливает рабочую директорию для последующих инструкций.

WORKDIR /app RUN pwd # /app WORKDIR /src RUN pwd # /src # Создаёт директорию, если не существует WORKDIR /nonexistent # Будет создана

Важно: Используйте абсолютные пути. Избегайте RUN cd /app && ... — используйте WORKDIR.

#COPY

COPY копирует файлы из контекста сборки в образ.

# Копирование одного файла COPY requirements.txt . # Копирование нескольких файлов COPY src/ /app/src/ COPY README.md /app/ # Копирование с переименованием COPY app.py /app/main.py # Копирование нескольких файлов с wildcard COPY *.py /app/src/

Контекст сборки:

  • Файлы, доступные Dockerfile, определяются контекстом
  • Контекст задаётся при сборке: docker build -t myapp .
  • Точка (.) означает текущую директорию
  • .dockerignore исключает файлы из контекста

#ADD

ADD похож на COPY, но с дополнительными возможностями:

  • Автоматическая распаковка архивов
  • Загрузка файлов по URL
# Распаковка архива ADD archive.tar.gz /app/ # Загрузка по URL (не рекомендуется) ADD https://example.com/file.txt /app/

Рекомендация: Используйте COPY по умолчанию. ADD — только для распаковки архивов.

#RUN

RUN выполняет команду в новом слое образа.

# Shell форма (запускается через /bin/sh -c) RUN apt update && apt install -y nginx # Exec форма (предпочтительна) RUN ["apt", "update", "&&", "apt", "install", "-y", "nginx"] # Многострочные команды для читаемости RUN apt update && \ apt install -y \ git \ curl \ vim && \ rm -rf /var/lib/apt/lists/*

Важно: Объединяйте команды в один RUN для уменьшения количества слоёв.

#CMD

CMD задаёт команду по умолчанию для запуска контейнера.

# Exec форма (предпочтительна) CMD ["nginx", "-g", "daemon off;"] # Shell форма CMD nginx -g "daemon off;" # С переменными окружения CMD ["python", "manage.py", "runserver"]

Важно:

  • Можно переопределить при запуске: docker run myapp bash
  • Только одна CMD в Dockerfile (последняя используется)
  • Используйте exec форму для корректной обработки сигналов

#ENTRYPOINT

ENTRYPOINT задаёт главную команду контейнера.

# Exec форма ENTRYPOINT ["python", "app.py"] # Shell форма ENTRYPOINT python app.py

Разница между CMD и ENTRYPOINT:

ХарактеристикаCMDENTRYPOINT
ПереопределениеЛегко (docker run image command)Требует --entrypoint
НазначениеАргументы по умолчаниюОсновная команда
КомбинированиеМожно использовать с ENTRYPOINTМожет использовать CMD как аргументы

Паттерн: ENTRYPOINT + CMD

ENTRYPOINT ["python", "app.py"] CMD ["--host", "0.0.0.0", "--port", "8000"]

При запуске:

# Использует CMD по умолчанию docker run myapp # python app.py --host 0.0.0.0 --port 8000 # Переопределяет CMD docker run myapp --help # python app.py --help

#EXPOSE

EXPOSE документирует порты, которые слушает приложение.

EXPOSE 80 EXPOSE 443 EXPOSE 8000 8080

Важно: EXPOSE не публикует порты на хосте. Для доступа используйте -p при запуске:

docker run -p 8080:80 myapp

#ENV

ENV устанавливает переменные окружения.

# Одна переменная ENV APP_ENV=production # Несколько переменных ENV DB_HOST=localhost \ DB_PORT=5432 \ DB_NAME=myapp # Использование в Dockerfile ENV APP_HOME=/app WORKDIR $APP_HOME

Переменные доступны:

  • В последующих инструкциях Dockerfile
  • В запущенном контейнере
  • Через docker run -e VAR=value

#ARG

ARG определяет переменные для времени сборки.

# Переменная сборки ARG VERSION=1.0 LABEL version=${VERSION} # Использование в RUN ARG PYTHONDONTWRITEBYTECODE=1 ENV PYTHONDONTWRITEBYTECODE=${PYTHONDONTWRITEBYTECODE} # Переменная без значения по умолчанию ARG SECRET_KEY RUN echo ${SECRET_KEY} > /tmp/secret

Разница ARG vs ENV:

  • ARG доступна только при сборке
  • ENV доступна в запущенном контейнере

Передача при сборке:

docker build --build-arg VERSION=2.0 -t myapp .

#VOLUME

VOLUME создаёт точку монтирования для внешнего хранилища.

VOLUME ["/data"] VOLUME /data /logs

При запуске Docker создаёт anonymous volume:

docker run myapp # Volume создан в /var/lib/docker/volumes/

Или монтируйте свой volume:

docker run -v mydata:/data myapp

#USER

USER задаёт пользователя для запуска процессов.

# Создание пользователя RUN useradd --create-home --shell /bin/bash appuser # Переключение пользователя USER appuser # С указанием группы USER appuser:appgroup

Безопасность: Не запускайте контейнеры от root в production.

#HEALTHCHECK

HEALTHCHECK определяет команду для проверки работоспособности.

HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD curl -f http://localhost:8000/health || exit 1

Параметры:

  • --interval — период между проверками (по умолчанию 30s)
  • --timeout — таймаут команды (по умолчанию 30s)
  • --start-period — время на старт приложения (по умолчанию 0s)
  • --retries — количество попыток до статуса unhealthy (по умолчанию 3)

Проверка статуса:

docker ps # Покажет статус (healthy/unhealthy) docker inspect --format='{{.State.Health.Status}}' container_id

#STOPSIGNAL

STOPSIGNAL задаёт сигнал для остановки контейнера.

STOPSIGNAL SIGTERM STOPSIGNAL SIGINT

По умолчанию Docker отправляет SIGTERM, затем SIGKILL через 10 секунд.

#SHELL

SHELL меняет оболочку для shell-формы инструкций.

# Использование bash вместо sh SHELL ["/bin/bash", "-c"] RUN source /etc/profile && echo $HOME

#Multi-stage сборка

Multi-stage builds используют несколько инструкций FROM для разделения этапов.

#Пример для Go-приложения

# Этап 1: Сборка FROM golang:1.21 AS builder WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY . . RUN CGO_ENABLED=0 GOOS=linux go build -o myapp # Этап 2: Запуск FROM alpine:3.18 RUN apk --no-cache add ca-certificates WORKDIR /root/ COPY --from=builder /app/myapp . COPY --from=builder /etc/ssl/certs /etc/ssl/certs CMD ["./myapp"]

Преимущества:

  • Финальный образ не содержит компилятор и зависимости сборки
  • Размер уменьшается в разы (с 800 МБ до 20 МБ)
  • Повышенная безопасность (меньше attack surface)

#Пример для Node.js

# Этап 1: Сборка FROM node:18 AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build # Этап 2: Production FROM node:18-alpine WORKDIR /app COPY package*.json ./ RUN npm ci --only=production COPY --from=builder /app/dist ./dist EXPOSE 3000 CMD ["node", "dist/index.js"]

#Best Practices

#1. Используйте .dockerignore

Создайте .dockerignore в корне проекта:

.git
.gitignore
*.md
.env
.env.local
__pycache__
*.pyc
node_modules
venv
.Dockerfile
docker-compose*.yml

#2. Копируйте зависимости перед кодом

# ✅ Хорошо — кэширование работает COPY requirements.txt . RUN pip install -r requirements.txt COPY . . # ❌ Плохо — кэш сбрасывается при любом изменении кода COPY . . RUN pip install -r requirements.txt

#3. Объединяйте RUN-команды

# ✅ Хорошо — один слой RUN apt update && \ apt install -y git curl && \ rm -rf /var/lib/apt/lists/* # ❌ Плохо — три слоя RUN apt update RUN apt install -y git curl RUN rm -rf /var/lib/apt/lists/*

#4. Не используйте sudo

# ✅ Хорошо RUN useradd appuser USER appuser # ❌ Плохо USER root RUN sudo -u appuser ...

#5. Используйте конкретные теги

# ✅ Хорошо FROM python:3.11.4-slim FROM node:18.16.0-alpine # ❌ Плохо FROM python:latest FROM node:latest FROM ubuntu

#6. Минимизируйте количество слоёв

# ✅ Хорошо COPY requirements.txt setup.py ./ RUN pip install -r requirements.txt # ❌ Плохо COPY requirements.txt . COPY setup.py . RUN pip install -r requirements.txt

#7. Обрабатывайте зомби-процессы

Для корректной обработки сигналов используйте:

  • Exec форму CMD/ENTRYPOINT
  • Или init-систему (tini, dumb-init)
# С tini FROM python:3.11-slim RUN apt update && apt install -y tini ENTRYPOINT ["/usr/bin/tini", "--"] CMD ["python", "app.py"]

#Примеры Dockerfile

#Python (Django)

FROM python:3.11-slim ENV PYTHONDONTWRITEBYTECODE=1 \ PYTHONUNBUFFERED=1 \ DJANGO_SETTINGS_MODULE=config.settings.production WORKDIR /app # Зависимости COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # Код COPY . . # Сборка статики RUN python manage.py collectstatic --noinput # Пользователь RUN useradd --create-home --shell /bin/bash appuser USER appuser EXPOSE 8000 HEALTHCHECK --interval=30s --timeout=3s \ CMD curl -f http://localhost:8000/health/ || exit 1 CMD ["gunicorn", "--bind", "0.0.0.0:8000", "config.wsgi"]

#Node.js (Express)

FROM node:18-alpine ENV NODE_ENV=production WORKDIR /app # Зависимости COPY package*.json ./ RUN npm ci --only=production # Код COPY . . # Пользователь RUN addgroup -g 1001 -S nodejs && \ adduser -S nodejs -u 1001 USER nodejs EXPOSE 3000 HEALTHCHECK --interval=30s --timeout=3s \ CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1 CMD ["node", "server.js"]

#Java (Spring Boot)

# Этап 1: Сборка FROM maven:3.9-eclipse-temurin-17 AS builder WORKDIR /app COPY pom.xml . RUN mvn dependency:go-offline COPY src ./src RUN mvn package -DskipTests # Этап 2: Запуск FROM eclipse-temurin:17-jre-alpine WORKDIR /app COPY --from=builder /app/target/*.jar app.jar RUN addgroup -S spring && adduser -S spring -G spring USER spring:spring EXPOSE 8080 HEALTHCHECK --interval=30s --timeout=3s \ CMD wget --no-verbose --tries=1 --spider http://localhost:8080/actuator/health || exit 1 ENTRYPOINT ["java", "-jar", "app.jar"]

#Отладка Dockerfile

#Проверка синтаксиса

# Hadolint — линтер для Dockerfile hadolint Dockerfile # Установка brew install hadolint # macOS pip install hadolint # Python

#Просмотр слоёв

# История слоёв docker history myapp:latest # Детальная информация docker inspect myapp:latest # Размер каждого слоя docker image inspect --format='{{range .RootFS.Layers}}{{println .}}{{end}}' myapp

#Сборка с выводом

# Подробный вывод docker build --progress=plain -t myapp . # Без кэша docker build --no-cache -t myapp . # С аргументами docker build --build-arg DEBUG=1 -t myapp .

#🧪 Лабораторные работы

#Lab 2.1: Создание первого Dockerfile

Цель: Научиться писать базовый Dockerfile для Python-приложения.

Задание:

  1. Создайте директорию для проекта
  2. Создайте простой app.py с Flask
  3. Создайте requirements.txt
  4. Напишите Dockerfile
  5. Соберите образ и запустите контейнер
# Создание структуры проекта mkdir myflaskapp && cd myflaskapp # app.py cat > app.py << 'EOF' from flask import Flask app = Flask(__name__) @app.route('/') def hello(): return "Hello from Docker!" if __name__ == '__main__': app.run(host='0.0.0.0', port=5000) EOF # requirements.txt cat > requirements.txt << 'EOF' flask==3.0.0 EOF # Dockerfile cat > Dockerfile << 'EOF' FROM python:3.11-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . EXPOSE 5000 CMD ["python", "app.py"] EOF # Сборка образа docker build -t myflaskapp . # Запуск контейнера docker run -d --name flask-app -p 5000:5000 myflaskapp # Проверка curl http://localhost:5000 docker logs flask-app

Ожидаемый результат: Вы видите "Hello from Docker!" в браузере.


#Lab 2.2: Оптимизация кэширования слоёв

Цель: Понять, как работает кэширование слоёв Docker.

Задание:

  1. Создайте Dockerfile с неправильным порядком инструкций
  2. Соберите образ и измените код приложения
  3. Обратите внимание, что кэш не используется
  4. Исправьте порядок и соберите снова
# ❌ Плохой Dockerfile (кэш не работает) cat > Dockerfile.bad << 'EOF' FROM python:3.11-slim WORKDIR /app COPY . . COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt CMD ["python", "app.py"] EOF # ✅ Хороший Dockerfile (кэш работает) cat > Dockerfile.good << 'EOF' FROM python:3.11-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . CMD ["python", "app.py"] EOF # Сборка с выводом для наблюдения за кэшем docker build --progress=plain -t test-bad -f Dockerfile.bad . docker build --progress=plain -t test-good -f Dockerfile.good . # Измените app.py (добавьте комментарий) # Соберите снова и сравните вывод

Ожидаемый результат: В хорошем Dockerfile слой с pip install используется из кэша, в плохом — пересобирается каждый раз.


#Lab 2.3: Multi-stage сборка для Go-приложения

Цель: Научиться использовать многоэтапную сборку для уменьшения размера образа.

Задание:

  1. Создайте простое Go-приложение
  2. Напишите Dockerfile с multi-stage сборкой
  3. Сравните размер образов до и после
# main.go cat > main.go << 'EOF' package main import "fmt" func main() { fmt.Println("Hello from Go in Docker!") } EOF # go.mod cat > go.mod << 'EOF' module example.com/myapp go 1.21 EOF # Dockerfile с multi-stage cat > Dockerfile << 'EOF' # Этап 1: Сборка FROM golang:1.21 AS builder WORKDIR /app COPY go.mod main.go ./ RUN go build -o myapp main.go # Этап 2: Запуск FROM alpine:3.18 RUN apk --no-cache add ca-certificates WORKDIR /root/ COPY --from=builder /app/myapp . CMD ["./myapp"] EOF # Сборка и проверка размера docker build -t myapp-multi . docker images | grep myapp # Сравните с одноэтапной сборкой cat > Dockerfile.single << 'EOF' FROM golang:1.21 WORKDIR /app COPY go.mod main.go ./ RUN go build -o myapp main.go CMD ["./myapp"] EOF docker build -t myapp-single -f Dockerfile.single . docker images | grep myapp

Ожидаемый результат: Multi-stage образ в 10-20 раз меньше одноэтапного.


#Lab 2.4: Безопасный Dockerfile с не-root пользователем

Цель: Научиться создавать безопасные образы с непривилегированным пользователем.

Задание:

  1. Создайте Dockerfile с запуском от root
  2. Проверьте, под каким пользователем работает процесс
  3. Добавьте создание пользователя и переключение на него
  4. Проверьте безопасность
# ❌ Небезопасный Dockerfile cat > Dockerfile.insecure << 'EOF' FROM python:3.11-slim WORKDIR /app COPY app.py . CMD ["python", "app.py"] EOF # ✅ Безопасный Dockerfile cat > Dockerfile.secure << 'EOF' FROM python:3.11-slim # Создание пользователя RUN useradd --create-home --shell /bin/bash appuser WORKDIR /app COPY --chown=appuser:appuser app.py . # Переключение на непривилегированного пользователя USER appuser CMD ["python", "app.py"] EOF # Сборка docker build -t insecure -f Dockerfile.insecure . docker build -t secure -f Dockerfile.secure . # Проверка пользователя docker run --rm insecure whoami # root docker run --rm secure whoami # appuser

Ожидаемый результат: Безопасный образ запускается от пользователя appuser, а не от root.


#Lab 2.5: HEALTHCHECK для веб-приложения

Цель: Научиться добавлять проверку работоспособности в Dockerfile.

Задание:

  1. Добавьте HEALTHCHECK в Dockerfile из Lab 2.1
  2. Соберите образ
  3. Проверьте статус health
# Dockerfile с HEALTHCHECK cat > Dockerfile << 'EOF' FROM python:3.11-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . EXPOSE 5000 # Добавление health check HEALTHCHECK --interval=10s --timeout=3s --start-period=5s --retries=3 \ CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:5000')" || exit 1 CMD ["python", "app.py"] EOF # Сборка и запуск docker build -t myflaskapp-health . docker run -d --name flask-health myflaskapp-health # Проверка статуса docker ps # Ищите (healthy) в статусе docker inspect --format='{{.State.Health.Status}}' flask-health # Логи health check docker inspect --format='{{json .State.Health.Log}}' flask-health | jq

Ожидаемый результат: Статус контейнера меняется с starting на healthy через несколько секунд.


#Чеклист выполнения лабораторных

  • Lab 2.1: Flask-приложение доступно по порту 5000
  • Lab 2.2: Поняли разницу в кэшировании между плохим и хорошим Dockerfile
  • Lab 2.3: Multi-stage образ значительно меньше одноэтапного
  • Lab 2.4: Безопасный образ запускается от appuser, а не root
  • Lab 2.5: HEALTHCHECK показывает статус healthy

Далее: Управление контейнерами