Что происходит после создания Dockerfile
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Процесс работы после создания Dockerfile
Dockefile — это не готовый контейнер. Это просто набор инструкций (рецепт), по которому Docker создаёт образ. После создания Dockerfile начинается интересный процесс построения (build) и запуска контейнеров.
1. Что такое Dockerfile
Докерфайл — это текстовый файл с инструкциями для сборки образа:
# Dockerfile
FROM openjdk:17-jdk-alpine # Базовый образ
WORKDIR /app # Рабочая директория в контейнере
COPY build/libs/app.jar app.jar # Копируем JAR файл
EXPOSE 8080 # Какой порт открываем
ENTRYPOINT ["java", "-jar", "app.jar"] # Команда запуска
Сам по себе файл — это просто текст. Нужно собрать (build) образ.
2. Этап Build: Создание образа из Dockerfile
После создания Dockerfile вы выполняете команду:
docker build -t my-java-app:1.0 .
Что происходит:
┌─ Dockerfile ─────────────────────────────────┐
│ FROM openjdk:17-jdk-alpine │
│ WORKDIR /app │
│ COPY build/libs/app.jar app.jar │
│ EXPOSE 8080 │
│ ENTRYPOINT ["java", "-jar", "app.jar"] │
└──────────────────────────────────────────────┘
↓ (docker build)
┌─ Docker Image (abc123...) ─────────────────┐
│ Layer 1: openjdk:17 (базовая ОС + Java) │
│ Layer 2: WORKDIR /app │
│ Layer 3: COPY app.jar │
│ Layer 4: EXPOSE 8080 │
│ Layer 5: ENTRYPOINT │
└────────────────────────────────────────────┘
3. Слои (Layers) образа
Dockfile обрабатывается построчно, каждая строка создаёт слой (layer):
FROM openjdk:17-jdk-alpine # Layer 1: ~130MB
RUN apk add --no-cache curl # Layer 2: +5MB
WORKDIR /app # Layer 3: ~0MB (metadata)
COPY app.jar . # Layer 4: +50MB (размер JAR)
RUN chmod +x app.jar # Layer 5: +0MB
ENTRYPOINT ["java", "-jar",
Каждый слой кэшируется Docker-ом. Если вы пересобираете образ, Docker проверяет:
- Изменилась ли инструкция?
- Изменились ли файлы, которые копируются?
Если нет — использует кэшированный слой (очень быстро).
# Первая сборка
$ docker build -t app:1.0 .
Step 1/5 : FROM openjdk:17-jdk-alpine # Скачивает образ (130MB)
Step 2/5 : COPY app.jar . # Копирует файл
Step 3/5 : ENTRYPOINT [...] # Устанавливает точку входа
# Total: 180MB, time: 2 min
# Вторая сборка (ничего не изменилось)
$ docker build -t app:1.0 .
Step 1/5 : FROM openjdk:17-jdk-alpine # Using cache (моментально)
Step 2/5 : COPY app.jar . # Using cache
Step 3/5 : ENTRYPOINT [...] # Using cache
# Total: instant!
# Третья сборка (изменился app.jar)
$ docker build -t app:1.0 .
Step 1/5 : FROM openjdk:17-jdk-alpine # Using cache
Step 2/5 : COPY app.jar . # REBUILDS! (app.jar изменился)
Step 3/5 : ENTRYPOINT [...] # REBUILDS! (зависит от Layer 2)
# Total: ~5 сек
4. Оптимизация Dockerfile
Быстрое кэширование — это искусство:
# ПЛОХО: кэш часто сбивается
FROM openjdk:17-jdk-alpine
COPY . /app # Копируем ВСЁ (включая .git, node_modules)
WORKDIR /app
RUN ./gradlew build
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "build/libs/app.jar"]
# ХОРОШО: кэш используется эффективно
FROM openjdk:17-jdk-alpine
# Копируем только то, что нужно для сборки
COPY build.gradle gradle.properties ./
RUN ./gradlew dependencies # Скачиваем зависимости (кэшируется)
# Копируем исходный код
COPY src ./src
RUN ./gradlew build # Собираем (зависит только от src)
# Финальный образ
FROM openjdk:17-jdk-slim # Меньше образ (140MB -> 50MB)
COPY --from=0 /app/build/libs/app.jar /app.jar
ENTRYPOINT ["java", "-jar", "/app.jar"]
5. Multi-Stage Build для Java
Оптимизируем размер финального образа:
# Stage 1: Build (большой, ~500MB)
FROM openjdk:17-jdk-alpine AS builder
WORKDIR /app
COPY . .
RUN ./gradlew build # Собираем JAR (много промежуточных файлов)
# Stage 2: Runtime (маленький, ~50MB)
FROM openjdk:17-jdk-slim # Облегченный образ, без build-инструментов
WORKDIR /app
COPY --from=builder /app/build/libs/app.jar ./app.jar # Копируем только JAR
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
Результат:
- Первый образ (builder): 500MB (не использованный, удаляется)
- Финальный образ: 50MB (то что останется)
6. Сборка образа (docker build)
Когда вы запускаете build:
$ docker build -t my-java-app:1.0 --build-arg JAR_FILE=build/libs/app.jar .
Процесс:
1. Docker парсит Dockerfile
2. Для каждой инструкции:
a) Создаёт контейнер из предыдущего слоя
b) Выполняет инструкцию
c) Сохраняет результат как новый слой
d) Удаляет временный контейнер
3. Итоговый слой становится образом
4. Присваивает тег (tag) образу
Пример пошагово:
$ docker build -t app:1.0 .
Sending build context to Docker daemon 50.5MB # Отправляет файлы Docker daemon-у
Step 1/5 : FROM openjdk:17-jdk-alpine
---> sha256:abc123... # Image ID базового образа
Step 2/5 : WORKDIR /app
---> Running in 5f3e2d1a4c6b8 # Создал временный контейнер
---> 5f3e2d1a4c6b8 # ID слоя (hash)
Step 3/5 : COPY app.jar .
---> Running in 3c8b1e9f2a4d6
---> 3c8b1e9f2a4d6
Step 4/5 : EXPOSE 8080
---> Running in 7d2c5f8a1e9b3
---> 7d2c5f8a1e9b3
Step 5/5 : ENTRYPOINT ["java", "-jar", "app.jar"]
---> Running in 9e4a3b7c2d5f8
---> 9e4a3b7c2d5f8
Successfully built 9e4a3b7c2d5f8
Successfully tagged app:1.0
7. Docker Image Registry
Образ создан локально. Следующий шаг — сохранить или распространить:
# Просмотр локальных образов
$ docker images
REPOSITORY TAG IMAGE ID SIZE
my-java-app 1.0 9e4a3b7c2d5f8 185MB
openjdk 17-jdk abc123... 130MB
# Загрузить в Docker Hub (или приватный registry)
$ docker login
$ docker tag my-java-app:1.0 username/my-java-app:1.0
$ docker push username/my-java-app:1.0
# Теперь образ доступен для скачивания другими
$ docker pull username/my-java-app:1.0
8. Запуск контейнера из образа
Отступ: образ создан, теперь его нужно запустить:
# Базовый запуск
$ docker run my-java-app:1.0
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
/\\ ___ |_|_|_|_| | | | | (_| | ) ) ) )
' |____| .__|_| |_|_| |_|\__, | / / / /
=========|_|==============|___/=/_/_/_/
2024-03-23 10:30:45.123 INFO --- Spring Boot Application started
Процесс запуска:
┌─ Image (дно-дело, не изменяется) ──────────────┐
│ Layer 1: OS + Java runtime │
│ Layer 2: WORKDIR /app │
│ Layer 3: app.jar │
│ Layer 4: ENTRYPOINT java -jar app.jar │
└─────────────────────────────────────────────────┘
↓ (docker run)
┌─ Container (временный, живёт пока работает) ──┐
│ Write Layer (для файлов, созданных контейнером)│
│ Запуск ENTRYPOINT: java -jar app.jar │
│ Port 8080 доступен на localhost:8080 (-p) │
└─────────────────────────────────────────────────┘
# С маппингом портов и переменными окружения
$ docker run -p 8080:8080 \
-e SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/myapp \
-e SPRING_DATASOURCE_PASSWORD=secret \
--name my-app-container \
my-java-app:1.0
9. Docker Compose
Для сложных приложений с БД, кэшем и т.д.:
version: '3.9'
services:
app:
build:
context: .
dockerfile: Dockerfile
ports:
- "8080:8080"
environment:
- SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/myapp
- SPRING_DATASOURCE_PASSWORD=postgres
depends_on:
- db
db:
image: postgres:15
environment:
- POSTGRES_DB=myapp
- POSTGRES_PASSWORD=postgres
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
$ docker-compose up # Собирает и запускает ВСЕ сервисы
# Или пересобрать и запустить
$ docker-compose up --build
# Остановить
$ docker-compose down
10. Полный цикл жизни образа
1. СОЗДАНИЕ (CREATE)
Dockerfile → docker build → Image (abc123...)
2. ТЕСТИРОВАНИЕ (TEST)
docker run → контейнер запущен → тесты
3. РАСПРОСТРАНЕНИЕ (DISTRIBUTE)
docker tag → docker push → Docker Hub
4. РАЗВЁРТЫВАНИЕ (DEPLOY)
docker pull → docker run → Production
5. ОБНОВЛЕНИЕ (UPDATE)
Изменяем код → новая сборка → новый tag
docker build -t app:2.0
docker push app:2.0
6. УДАЛЕНИЕ (CLEANUP)
docker rmi app:1.0 # Удаляем старый образ
11. Отладка образа
Если что-то пошло не так:
# Запустить shell в контейнере
$ docker run -it my-java-app:1.0 /bin/sh
# Посмотреть логи
$ docker logs container_id
$ docker logs -f container_id # Follow mode (как tail -f)
# Инспектировать образ
$ docker inspect my-java-app:1.0 # Метаинформация
$ docker history my-java-app:1.0 # История слоёв
# Размер слоёв
$ docker history --no-trunc my-java-app:1.0
# Вход в работающий контейнер
$ docker exec -it container_id /bin/sh
12. Оптимизация для Production
# Production Dockerfile
FROM openjdk:17-jdk-slim
# Не запускаем как root
RUN useradd -m -u 1000 appuser
WORKDIR /app
COPY --from=builder /app/build/libs/app.jar ./app.jar
COPY --chown=appuser:appuser . .
USER appuser
EXPOSE 8080
# Healthcheck
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8080/actuator/health || exit 1
ENTRYPOINT ["java", "-XX:+UseG1GC", "-XX:MaxRAMPercentage=75.0", "-jar", "app.jar"]
Итоговая схема: После создания Dockerfile
Dockerfile ──(docker build)──> Docker Image ──(docker run)──> Container
↓
(docker push)
↓
Docker Registry
↓
(docker pull)
↓
Production environment
↓
Приложение работает!
Ключевые команды
docker build -t app:1.0 . # Собрать образ
docker run -p 8080:8080 app:1.0 # Запустить контейнер
docker images # Список образов
docker ps # Запущённые контейнеры
docker logs container_id # Логи
docker push app:1.0 # Загрузить в registry
docker pull app:1.0 # Скачать из registry
Dockefile — это всего лишь начало. После его создания начинается интересная работа: оптимизация размера, кэширование слоёв, безопасность, и наконец развёртывание на production.