← Назад к вопросам

Что происходит после создания Dockerfile

2.0 Middle🔥 151 комментариев
#Docker, Kubernetes и DevOps

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Процесс работы после создания 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.

Что происходит после создания Dockerfile | PrepBro