Docker + Docker Compose
Цель урока
Сделать полноценный CRUD для задач с правильными HTTP-статусами (201/204/404), обработкой ошибок, и подготовить Task к подключению JPA.
Теория · для собеса
1 Docker — зачем ▸
- Одинаковая среда везде (dev, CI, staging, prod) — «у меня работает» больше не аргумент
- Изоляция — приложение не зависит от того, что стоит на хосте
- Версионирование —
taskflow:0.1.0, можно откатиться - CI/CD — собираем образ, пушим в registry, деплоим на любой хост
2 Слои Docker-образа ▸
[ Base image ] ← eclipse-temurin:21-jdk-alpine
[ Build tools ] ← Gradle, исходники
[ Dependencies ] ← ./gradlew dependencies (кешируется)
[ Source code ] ← .java файлы
[ Build artifacts ] ← JAR
[ Runtime ] ← JRE (для запуска)
[ Final image ] ← только JAR + JRE
Multi-stage build — сначала собираем с JDK, потом копируем JAR в JRE-образ. Финальный — ~250 MB вместо 800 MB.
3 Docker Compose ▸
Compose — инструмент для запуска многосервисного приложения одной командой:
app(TaskFlow)postgreskafkaprometheus(опционально)
Всё через файл docker-compose.yml.
---
Практика: улучшаем CRUD
-
1
Dockerfile(multi-stage)# Stage 1: Build FROM eclipse-temurin:21-jdk-alpine AS builder WORKDIR /build # Кешируем зависимости отдельно COPY gradlew settings.gradle.kts build.gradle.kts ./ COPY gradle ./gradle RUN chmod +x ./gradlew && ./gradlew dependencies --no-daemon # Копируем исходники и собираем COPY src ./src RUN ./gradlew bootJar --no-daemon # Stage 2: Runtime FROM eclipse-temurin:21-jre-alpine RUN addgroup -S taskflow && adduser -S taskflow -G taskflow WORKDIR /app COPY --from=builder /build/build/libs/*.jar app.jar USER taskflow # Healthcheck для Docker/K8s HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \ CMD wget -qO- http://localhost:8080/actuator/health/liveness || exit 1 EXPOSE 8080 ENTRYPOINT ["java", \ "-XX:+UseContainerSupport", \ "-XX:MaxRAMPercentage=75.0", \ "-Djava.security.egd=file:/dev/./urandom", \ "-jar", "app.jar"] -
2
.dockerignore.git .gitignore .idea *.iml .gradle build out *.log .DS_Store node_modules -
3Сборка
docker build -t taskflow:0.1.0 . docker images | grep taskflow # taskflow 0.1.0 245 MB -
4
docker-compose.ymlversion: '3.8' services: postgres: image: postgres:16-alpine container_name: taskflow-postgres environment: POSTGRES_DB: taskflow_dev POSTGRES_USER: taskflow POSTGRES_PASSWORD: dev_password ports: ["5432:5432"] volumes: - pgdata:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U taskflow"] interval: 10s timeout: 5s retries: 5 zookeeper: image: confluentinc/cp-zookeeper:7.6.0 environment: ZOOKEEPER_CLIENT_PORT: 2181 kafka: image: confluentinc/cp-kafka:7.6.0 depends_on: [zookeeper] ports: ["9092:9092"] environment: KAFKA_BROKER_ID: 1 KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092 KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 app: build: . image: taskflow:0.1.0 container_name: taskflow-app depends_on: postgres: { condition: service_healthy } kafka: { condition: service_started } environment: SPRING_PROFILES_ACTIVE: dev SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/taskflow_dev SPRING_DATASOURCE_USERNAME: taskflow SPRING_DATASOURCE_PASSWORD: dev_password SPRING_KAFKA_BOOTSTRAP_SERVERS: kafka:9092 ports: ["8080:8080"] healthcheck: test: ["CMD", "wget", "-qO-", "http://localhost:8080/actuator/health/liveness"] interval: 30s timeout: 5s retries: 3 start_period: 30s volumes: pgdata: {} -
5Запуск
# Поднять всё docker compose up -d # Логи docker compose logs -f app docker compose logs -f kafka # Статус docker compose ps # Остановить docker compose down # Остановить + удалить volumes docker compose down -v -
6Тест
# Health check $ curl http://localhost:8080/actuator/health {"status":"UP"} # Регистрация $ curl -X POST http://localhost:8080/api/auth/register \ -H "Content-Type: application/json" \ -d '{"username":"alice","email":"a@e.com","password":"password123"}' # Логин $ curl -X POST http://localhost:8080/api/auth/login \ -H "Content-Type: application/json" \ -d '{"username":"alice","password":"password123"}' -
7Логи
# Следить за app docker compose logs -f app | grep -E "Started|ERROR" # Внутри контейнера docker compose exec app sh $ ls /app $ java -version -
8
-XX:MaxRAMPercentage=75.0Берёт 75% доступной RAM контейнера как max heap. Docker ограничивает RAM через
--memory=512m— JVM сама подстроится. Без этого — JVM возьмёт всю RAM хоста, что может убить контейнер. -
9Multi-stage — почему важен размер
eclipse-temurin:21-jdk— 700 MBeclipse-temurin:21-jre— 200 MB- Финальный образ с JRE + JAR = 250 MB
- В K8s это меньше времени на pull при каждом поде
---
Зачем это на собесе
После урока ты должен уметь ответить на:
docker run taskflow:0.0.9 если 0.1.0 сломан.deploy.resources.limits.memory=512Mi в K8s + MaxRAMPercentage = JVM не убьёт контейнер.5 вопросов на углубление
Раскрой вопрос и нажми «🤔 Хочу разобрать подробнее» — он попадёт в страницу ответов.
Разбор внутри: image, container, JVM и зависимости приложения.
Разбор внутри: builder stage, runtime stage и маленький production image.
Разбор внутри: слои, кеш Gradle и быстрая пересборка.
Разбор внутри: приложение, Postgres, Kafka и Adminer одной командой.
Разбор внутри: проверка контейнера и связь с Actuator.
Готов идти дальше?
Выбери вопросы, которые тебе интересны, и изучи их. Потом — к следующему уроку.
🚀 Перейти к Уроку 23Сначала пройди все секции и выбери хотя бы 1 вопрос
⬅️ Назад к Уроку 21