Образы, контейнеры, Dockerfile, Compose и запуск Java-приложений
Ответ: Docker — платформа для упаковки приложения вместе со всеми зависимостями (JDK, библиотеки, конфигурация) в образ и запуска его в изолированном контейнере. Приложение ведёт себя одинаково на ноутбуке разработчика, в CI и на сервере.
| Критерий | Контейнер | Виртуальная машина |
|---|---|---|
| Ядро ОС | Общее с хостом | Своё у каждой ВМ (гостевая ОС) |
| Размер | Десятки-сотни МБ | Гигабайты |
| Запуск | Секунды или меньше | Десятки секунд и минуты |
| Изоляция | На уровне процессов (слабее) | На уровне железа через гипервизор (сильнее) |
| Накладные расходы | Малые | Заметные: память и CPU под гостевую ОС |
docker pull postgres:16 # скачать образ из реестра
docker run -d --name db postgres:16 # создать и запустить контейнер из образа
docker run -d --name db2 postgres:16 # второй независимый контейнер из того же образа
репозиторий:тег. Тег latest — это просто имя по умолчанию, а не «самая свежая версия»; в продакшене фиксируйте конкретную версию.
Ответ: Контейнер — это обычный процесс Linux, к которому применены средства ядра:
| Механизм | Что обеспечивает |
|---|---|
| Namespaces | Изоляцию: свой набор процессов (PID), сеть (net), файловая система (mnt), hostname (uts), пользователи (user), IPC |
| cgroups | Ограничение и учёт ресурсов: CPU, память, ввод-вывод |
| Union FS (overlay2) | Слоистую файловую систему: слои образа только для чтения плюс слой контейнера для записи |
docker (CLI) → dockerd (демон) → containerd → runc (создаёт контейнер)| Инструкция | Назначение |
|---|---|
FROM |
Базовый образ (начало сборки) |
WORKDIR |
Рабочая директория для следующих инструкций |
COPY / ADD |
Копирование файлов в образ |
RUN |
Выполнить команду при сборке образа (создаёт слой) |
ENV |
Переменная окружения (доступна и при сборке, и при запуске) |
ARG |
Параметр только на время сборки |
EXPOSE |
Документирует порт, который слушает приложение (порт не публикует) |
USER |
Пользователь, под которым запускается процесс |
CMD / ENTRYPOINT |
Что выполнить при запуске контейнера |
HEALTHCHECK |
Команда проверки работоспособности |
FROM eclipse-temurin:21-jre
WORKDIR /app
COPY target/app.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
ENTRYPOINT — основная исполняемая команда контейнера; аргументы из docker run добавляются к нейCMD — команда или аргументы по умолчанию; целиком заменяется, если передать свою команду в docker runENTRYPOINT задаёт программу, CMD — аргументы по умолчанию
ENTRYPOINT ["java", "-jar", "app.jar"]
CMD ["--spring.profiles.active=prod"]
# docker run my-app --spring.profiles.active=dev -> заменит только CMD
ENTRYPOINT java -jar app.jar) процессом с PID 1 становится sh, и сигнал остановки (SIGTERM) может не дойти до JVM — приложение не завершится корректно.
COPY просто копирует файлыADD умеет ещё скачивать по URL и распаковывать tar-архивы; из-за неявного поведения рекомендуется COPYARG существует только во время сборки (--build-arg)ENV сохраняется в образе и доступен приложению при запускеОтвет: Каждая инструкция RUN, COPY, ADD создаёт новый слой. Docker кэширует слои: если инструкция и её входные данные не менялись, слой берётся из кэша. Как только слой изменился, все слои после него пересобираются.
pom.xml, скачивание зависимостей), потом часто меняющееся (исходники)RUN и убирать за собой временные файлы в той же команде
COPY pom.xml .
RUN mvn -q dependency:go-offline # слой с зависимостями: меняется редко
COPY src ./src # исходники: меняются часто
RUN mvn -q package -DskipTests
Файл исключает ненужное из контекста сборки: ускоряет сборку, не попадают лишние и секретные файлы.
target/
.git/
.idea/
*.log
.env
Ответ: В multi-stage Dockerfile несколько FROM. На первом этапе собирают приложение (нужны JDK и Maven), на втором — копируют только результат в лёгкий образ. Инструменты сборки в итоговый образ не попадают.
# этап 1: сборка
FROM maven:3.9-eclipse-temurin-21 AS build
WORKDIR /app
COPY pom.xml .
RUN mvn -q dependency:go-offline
COPY src ./src
RUN mvn -q package -DskipTests
# этап 2: запуск
FROM eclipse-temurin:21-jre
WORKDIR /app
COPY --from=build /app/target/*.jar app.jar
ENTRYPOINT ["java", "-jar", "app.jar"]
-alpine, distroless)jlink — собрать урезанный runtime только с нужными модулями JDK.dockerignore и очистка временных файлов в том же RUNОтвет: JVM, начиная с JDK 10 (и 8u191), учитывает ограничения контейнера по памяти и CPU. Но без настройки доля памяти для кучи невелика: по умолчанию максимальная куча — около 25% от лимита контейнера.
FROM eclipse-temurin:21-jre
WORKDIR /app
COPY app.jar app.jar
USER 1000
ENTRYPOINT ["java", "-XX:MaxRAMPercentage=75.0", "-jar", "app.jar"]
docker run -d --name app -p 8080:8080 --memory=512m --cpus=1.5 my-app
-XX:MaxRAMPercentage задаёт долю памяти контейнера под кучу; можно использовать и обычный -XmxXmx не стоит делать равным лимитуOOMKilled, код выхода 137Runtime.availableProcessors(), а значит на размеры пулов потоковSPRING_PROFILES_ACTIVE), а не пересобирайте образОтвет: Файлы внутри контейнера исчезают вместе с ним, поэтому для постоянных данных используют монтирование.
| Тип | Что это | Когда использовать |
|---|---|---|
| Volume | Хранилище, которым управляет Docker (/var/lib/docker/volumes) |
Данные БД и любое постоянное состояние; переживает удаление контейнера |
| Bind mount | Каталог или файл хоста, смонтированный в контейнер | Разработка (исходники, конфигурация с хоста) |
| tmpfs | Временное хранилище в памяти | Секреты и временные данные, которые не должны попасть на диск |
docker volume create pgdata
docker run -d --name db -v pgdata:/var/lib/postgresql/data postgres:16 # volume
docker run -d -v $(pwd)/config:/app/config:ro my-app # bind mount, только чтение
| Драйвер | Особенности |
|---|---|
bridge |
По умолчанию: контейнеры в частной сети хоста. Пользовательская bridge-сеть даёт DNS по имени контейнера |
host |
Контейнер использует сеть хоста напрямую, без изоляции (Linux) |
none |
Без сети |
overlay |
Сеть между несколькими хостами (Swarm) |
docker network create backend
docker run -d --name db --network backend postgres:16
docker run -d --name app --network backend -p 8080:8080 my-app
# внутри app база доступна по адресу db:5432
localhost — это сам контейнер. К БД в другом контейнере нужно обращаться по имени сервиса (jdbc:postgresql://db:5432/mydb), а не по localhost.
-p 8080:80 публикует порт: порт_хоста:порт_контейнера. EXPOSE порт не публикует, он только документирует.
Ответ: Docker Compose описывает несколько контейнеров приложения в одном YAML-файле (compose.yaml / docker-compose.yml) и запускает их одной командой: docker compose up -d. Compose сам создаёт общую сеть, где сервисы видят друг друга по имени.
services:
app:
build: .
ports:
- "8080:8080"
environment:
SPRING_DATASOURCE_URL: jdbc:postgresql://db:5432/mydb
SPRING_DATASOURCE_USERNAME: app
SPRING_DATASOURCE_PASSWORD: secret
depends_on:
db:
condition: service_healthy
restart: unless-stopped
db:
image: postgres:16
environment:
POSTGRES_DB: mydb
POSTGRES_USER: app
POSTGRES_PASSWORD: secret
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d mydb"]
interval: 5s
retries: 10
volumes:
pgdata:
depends_on без condition ждёт только старта контейнера, а не готовности БД; для ожидания готовности нужен healthcheck и service_healthyrestart: no, on-failure, always, unless-stoppedup, down (-v удаляет и тома), logs -f, ps, build| Команда | Что делает |
|---|---|
docker build -t name:tag . |
Собрать образ из Dockerfile |
docker run -d -p 8080:8080 --name app name |
Создать и запустить контейнер в фоне |
docker ps / docker ps -a |
Работающие / все контейнеры |
docker logs -f app |
Логи контейнера в реальном времени |
docker exec -it app sh |
Зайти внутрь работающего контейнера |
docker stop app / start / restart |
Управление контейнером |
docker rm app / docker rmi image |
Удалить контейнер / образ |
docker images |
Список образов |
docker pull / docker push |
Скачать / отправить образ в реестр |
docker inspect app |
Подробная информация (JSON): сеть, тома, переменные |
docker stats |
Потребление CPU и памяти |
docker system prune |
Удалить неиспользуемые контейнеры, сети и образы |
docker stop посылает SIGTERM и ждёт (по умолчанию 10 секунд), затем SIGKILL; docker kill сразу посылает SIGKILL. docker run = create + start.
ENV, ни в COPY, ни в ARG. Они остаются в слоях и видны через docker history-e, --env-file, Docker/Compose secrets, секреты оркестратора, Vault.env в репозиторий, добавьте его в .gitignore и .dockerignoreUSER в Dockerfilelatest)--memory, --cpus) и привилегии (--cap-drop, --read-only)--privileged и не монтируйте docker.sock без необходимости
RUN useradd -r -u 1001 appuser
USER appuser
Ответ: Это инструменты разных уровней.
| Docker | Kubernetes |
|---|---|
| Собирает образы и запускает контейнеры на одном хосте | Оркестратор: управляет контейнерами на кластере из многих машин |
| Docker Compose — несколько контейнеров на одной машине | Автомасштабирование, самовосстановление, плавные обновления (rolling update), балансировка и service discovery |
| Подходит для разработки и небольших развёртываний | Подходит для продакшена с высокой доступностью |
containerd, и с CRI-O. Docker остаётся стандартом для сборки образов.