Продвинутые техники создания Dockerfile

DockerDockerBeginner
Практиковаться сейчас

💡 Этот учебник переведен с английского с помощью ИИ. Чтобы просмотреть оригинал, вы можете перейти на английский оригинал

Введение

В этом практическом занятии мы углубимся в техники создания Dockerfile, изучив продвинутые концепции, которые помогут вам создавать более эффективные и гибкие Docker-образы. Мы рассмотрим детальные инструкции Dockerfile, многостадийные сборки и использование файлов.dockerignore. Также мы изучим важную концепцию слоев в Docker-образах. К концу этого практического занятия вы получите всестороннее понимание этих продвинутых техник создания Dockerfile и сможете применить их в своих собственных проектах.

Это практическое занятие разработано с учетом новичков, оно содержит подробные объяснения и разъясняет потенциально непонятные моменты. Мы будем использовать WebIDE (VS Code) для всех задач по редактированию файлов, что позволит легко создавать и изменять файлы прямо в браузере.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL docker(("Docker")) -.-> docker/ImageOperationsGroup(["Image Operations"]) linux(("Linux")) -.-> linux/FileandDirectoryManagementGroup(["File and Directory Management"]) docker(("Docker")) -.-> docker/DockerfileGroup(["Dockerfile"]) docker(("Docker")) -.-> docker/ContainerOperationsGroup(["Container Operations"]) linux(("Linux")) -.-> linux/BasicFileOperationsGroup(["Basic File Operations"]) docker/ContainerOperationsGroup -.-> docker/run("Run a Container") docker/ContainerOperationsGroup -.-> docker/ps("List Running Containers") docker/ContainerOperationsGroup -.-> docker/logs("View Container Logs") docker/ContainerOperationsGroup -.-> docker/inspect("Inspect Container") linux/BasicFileOperationsGroup -.-> linux/touch("File Creating/Updating") docker/ImageOperationsGroup -.-> docker/images("List Images") linux/FileandDirectoryManagementGroup -.-> linux/mkdir("Directory Creating") docker/DockerfileGroup -.-> docker/build("Build Image from Dockerfile") subgraph Lab Skills docker/run -.-> lab-389027{{"Продвинутые техники создания Dockerfile"}} docker/ps -.-> lab-389027{{"Продвинутые техники создания Dockerfile"}} docker/logs -.-> lab-389027{{"Продвинутые техники создания Dockerfile"}} docker/inspect -.-> lab-389027{{"Продвинутые техники создания Dockerfile"}} linux/touch -.-> lab-389027{{"Продвинутые техники создания Dockerfile"}} docker/images -.-> lab-389027{{"Продвинутые техники создания Dockerfile"}} linux/mkdir -.-> lab-389027{{"Продвинутые техники создания Dockerfile"}} docker/build -.-> lab-389027{{"Продвинутые техники создания Dockerfile"}} end

Понимание инструкций Dockerfile и слоев

Начнем с создания Dockerfile, который использует различные инструкции. Мы создадим образ для Python-веб-приложения с использованием Flask и по ходу дела изучим, как каждая инструкция вносит вклад в слои нашего Docker-образа.

  1. Сначала создадим новую директорию для нашего проекта. В терминале WebIDE выполните следующую команду:
mkdir -p ~/project/advanced-dockerfile && cd ~/project/advanced-dockerfile

Эта команда создает новую директорию с именем advanced-dockerfile внутри папки project, а затем переходит в эту директорию.

  1. Теперь создадим файл нашего приложения. В проводнике файлов WebIDE (обычно слева на экране) щелкните правой кнопкой мыши по папке advanced-dockerfile и выберите "New File". Назовите этот файл app.py.

  2. Откройте файл app.py и добавьте следующий Python-код:

from flask import Flask
import os

app = Flask(__name__)

@app.route('/')
def hello():
    return f"Hello from {os.environ.get('ENVIRONMENT', 'unknown')} environment!"

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

Это простое Flask-приложение, которое возвращает приветственное сообщение, включающее информацию о среде, в которой оно запущено.

  1. Далее нам нужно создать файл requirements.txt для указания наших Python-зависимостей. Создайте новый файл с именем requirements.txt в той же директории и добавьте следующее содержимое:
Flask==2.0.1
Werkzeug==2.0.1

Здесь мы указываем точные версии как Flask, так и Werkzeug, чтобы обеспечить совместимость.

  1. Теперь создадим наш Dockerfile. Создайте новый файл с именем Dockerfile (с заглавной буквы 'D') в той же директории и добавьте следующее содержимое:
## Use an official Python runtime as the base image
FROM python:3.9-slim

## Set the working directory in the container
WORKDIR /app

## Set an environment variable
ENV ENVIRONMENT=production

## Copy the requirements file into the container
COPY requirements.txt.

## Install the required packages
RUN pip install --no-cache-dir -r requirements.txt

## Copy the application code into the container
COPY app.py.

## Specify the command to run when the container starts
CMD ["python", "app.py"]

## Expose the port the app runs on
EXPOSE 5000

## Add labels for metadata
LABEL maintainer="Your Name <[email protected]>"
LABEL version="1.0"
LABEL description="Flask app demo for advanced Dockerfile techniques"

Теперь разберем эти инструкции и поймем, как они вносят вклад в слои нашего Docker-образа:

  • FROM python:3.9-slim: Это всегда первая инструкция. Она указывает базовый образ, на основе которого мы строим наш образ. Это создает первый слой нашего образа, который включает в себя Python-рантайм.
  • WORKDIR /app: Эта инструкция устанавливает рабочую директорию для последующих инструкций. Она не создает новый слой, но влияет на поведение следующих инструкций.
  • ENV ENVIRONMENT=production: Эта инструкция устанавливает переменную окружения. Переменные окружения не создают новые слои, но хранятся в метаданных образа.
  • COPY requirements.txt.: Эта инструкция копирует файл зависимостей с нашего хоста в образ. Это создает новый слой, содержащий только этот файл.
  • RUN pip install --no-cache-dir -r requirements.txt: Эта инструкция выполняет команду в контейнере во время процесса сборки. Она устанавливает наши Python-зависимости. Это создает новый слой, содержащий все установленные пакеты.
  • COPY app.py.: Эта инструкция копирует наш код приложения в образ, создавая еще один слой.
  • CMD ["python", "app.py"]: Эта инструкция указывает команду, которая будет выполняться при запуске контейнера. Она не создает слой, но устанавливает команду по умолчанию для контейнера.
  • EXPOSE 5000: Это на самом деле просто форма документации. Она сообщает Docker, что контейнер будет прослушивать этот порт во время выполнения, но на самом деле не публикует порт. Она не создает слой.
  • LABEL...: Эти инструкции добавляют метаданные в образ. Как и инструкции ENV, они не создают новые слои, но хранятся в метаданных образа.

Каждая инструкция RUN, COPY и ADD в Dockerfile создает новый слой. Слои - это фундаментальная концепция в Docker, которая позволяет эффективно хранить и передавать образы. Когда вы вносите изменения в свой Dockerfile и пересобираете образ, Docker будет использовать кэшированные слои, которые не изменились, ускоряя процесс сборки.

  1. Теперь, когда мы понимаем, что делает наш Dockerfile, давайте соберем Docker-образ. В терминале выполните следующую команду:
docker build -t advanced-flask-app.

Эта команда собирает новый Docker-образ с тегом advanced-flask-app. Точка в конце сообщает Docker искать Dockerfile в текущей директории.

Вы увидите вывод, показывающий каждый шаг процесса сборки. Обратите внимание, как каждый шаг соответствует инструкции в нашем Dockerfile, и как Docker упоминает "Using cache" для шагов, которые не изменились, если вы запускаете команду сборки несколько раз.

  1. После завершения сборки мы можем запустить контейнер на основе нашего нового образа:
docker run -d -p 5000:5000 --name flask-container advanced-flask-app

Эта команда делает следующее:

  • -d запускает контейнер в отсоединенном режиме (в фоновом режиме)
  • -p 5000:5000 сопоставляет порт 5000 на вашем хосте с портом 5000 в контейнере
  • --name flask-container дает имя нашему новому контейнеру
  • advanced-flask-app - это образ, который мы используем для создания контейнера

Вы можете проверить, что контейнер запущен, проверив список запущенных контейнеров:

docker ps
  1. Чтобы проверить, работает ли наше приложение правильно, мы можем использовать команду curl:
curl http://localhost:5000

Вы должны увидеть сообщение "Hello from production environment!"

Если у вас возникли проблемы с командой curl, вы также можете открыть новую вкладку браузера и перейти по адресу http://localhost:5000. Вы должны увидеть то же сообщение.

Если вы столкнулись с какими-либо проблемами, вы можете проверить логи контейнера, используя следующую команду:

docker logs flask-container

Это покажет вам любые сообщения об ошибках или вывод из вашего Flask-приложения.

Многостадийные сборки

Теперь, когда мы понимаем основные инструкции Dockerfile и слои, давайте рассмотрим более продвинутую технику: многостадийные сборки. Многостадийные сборки позволяют использовать несколько инструкций FROM в вашем Dockerfile. Это особенно полезно для создания более маленьких конечных образов путем копирования только необходимых артефактов из одной стадии в другую.

Давайте изменим наш Dockerfile, чтобы использовать многостадийную сборку, которая на самом деле приведет к созданию более маленького образа:

  1. В WebIDE откройте ранее созданный файл Dockerfile.
  2. Замените все его содержимое следующим:
## Build stage
FROM python:3.9-slim AS builder

WORKDIR /app

COPY requirements.txt.

RUN pip install --user --no-cache-dir -r requirements.txt

## Final stage
FROM python:3.9-slim

WORKDIR /app

## Copy only the installed packages from the builder stage
COPY --from=builder /root/.local /root/.local
COPY app.py.

ENV PATH=/root/.local/bin:$PATH
ENV ENVIRONMENT=production

CMD ["python", "app.py"]

EXPOSE 5000

LABEL maintainer="Your Name <[email protected]>"
LABEL version="1.0"
LABEL description="Flask app demo with multi-stage build"

Разберем, что происходит в этом многостадийном Dockerfile:

  1. Мы начинаем с стадии builder:

    • Мы используем образ Python 3.9-slim в качестве базового, чтобы сразу же держать размеры небольшими.
    • В этой стадии мы устанавливаем наши Python-зависимости с помощью команды pip install --user. Это устанавливает пакеты в домашнюю директорию пользователя.
  2. Затем у нас есть финальная стадия:

    • Мы начинаем заново с другого образа Python 3.9-slim.
    • Мы копируем только установленные пакеты из стадии builder, а именно из /root/.local, куда pip install --user их поместил.
    • Мы копируем наш код приложения.
    • Мы добавляем локальную директорию с исполняемыми файлами в переменную PATH, чтобы Python мог найти установленные пакеты.
    • Мы настраиваем остальную часть нашего контейнера (ENV, CMD, EXPOSE, LABEL) как и раньше.

Основное преимущество здесь заключается в том, что наш конечный образ не содержит никаких инструментов сборки или кэшей из процесса установки pip. Он содержит только конечные, необходимые артефакты. Это должно привести к созданию более маленького образа.

  1. Давайте соберем этот новый многостадийный образ. В терминале выполните следующую команду:
docker build -t multi-stage-flask-app.
  1. После завершения сборки сравним размеры наших двух образов. Выполните команду:
docker images | grep flask-app
multi-stage-flask-app         latest     7bdd1be2d1fb   10 seconds ago   129MB
advanced-flask-app            latest     c59d6fa303cc   10 minutes ago   136MB

Теперь вы должны увидеть, что образ multi-stage-flask-app меньше, чем образ advanced-flask-app, который мы собрали ранее.

  1. Теперь давайте запустим контейнер с нашим новым, более компактным образом:
docker run -d -p 5001:5000 --name multi-stage-container multi-stage-flask-app

Обратите внимание, что мы используем другой порт на хосте (5001), чтобы избежать конфликтов с предыдущим контейнером.

  1. Протестируем приложение:
curl http://localhost:5001

Вы по-прежнему должны увидеть сообщение "Hello from production environment!"

  1. Чтобы лучше понять различия между нашими одностадейным и многостадийным образами, мы можем использовать команду docker history. Выполните следующие команды:
docker history advanced-flask-app
docker history multi-stage-flask-app

Сравните выводы. Вы должны заметить, что многостадийная сборка имеет меньше слоев и меньшие размеры для некоторых слоев.

Многостадийные сборки - это мощная техника для создания эффективных Docker-образов. Они позволяют использовать инструменты и файлы в процессе сборки без увеличения размера конечного образа. Это особенно полезно для компилируемых языков или приложений с сложными процессами сборки.

В этом случае мы использовали ее для создания более маленького образа Python-приложения, копируя только необходимые установленные пакеты и код приложения, оставив все артефакты сборки и кэши.

Использование файла.dockerignore

При сборке Docker-образа Docker отправляет все файлы из директории в Docker-демон. Если у вас есть большие файлы, которые не нужны для сборки образа, это может замедлить процесс сборки. Файл .dockerignore позволяет вам указать файлы и директории, которые должны быть исключены при сборке Docker-образа.

Давайте создадим файл .dockerignore и посмотрим, как он работает:

  1. В WebIDE создайте новый файл в директории advanced-dockerfile и назовите его .dockerignore.
  2. Добавьте следующее содержимое в файл .dockerignore:
**/.git
**/.gitignore
**/__pycache__
**/*.pyc
**/*.pyo
**/*.pyd
**/.Python
**/env
**/venv
**/ENV
**/env.bak
**/venv.bak

Разберем, что означают эти шаблоны:

  • **/.git: Игнорировать директорию .git и все ее содержимое, независимо от того, где она находится в структуре директорий.
  • **/.gitignore: Игнорировать файлы .gitignore.
  • **/__pycache__: Игнорировать кэш-директории Python.
  • **/*.pyc, **/*.pyo, **/*.pyd: Игнорировать скомпилированные файлы Python.
  • **/.Python: Игнорировать файлы .Python (часто создаваемые виртуальными окружениями).
  • **/env, **/venv, **/ENV: Игнорировать директории виртуальных окружений.
  • **/env.bak, **/venv.bak: Игнорировать резервные копии директорий виртуальных окружений.

Символ ** в начале каждой строки означает "в любой директории".

  1. Чтобы продемонстрировать эффект файла .dockerignore, давайте создадим несколько файлов, которые мы хотим игнорировать. В терминале выполните следующие команды:
mkdir venv
touch venv/ignore_me.txt
touch.gitignore

Эти команды создают директорию venv с файлом внутри и файл .gitignore. Это распространенные элементы в Python-проектах, которые обычно не нужны в наших Docker-образах.

  1. Теперь давайте снова соберем наш образ:
docker build -t ignored-flask-app.
  1. Чтобы убедиться, что игнорируемые файлы не были включены в контекст сборки, мы можем использовать команду docker history:
docker history ignored-flask-app

Вы не должны увидеть никаких шагов, которые копируют директорию venv или файл .gitignore.

Файл .dockerignore - это мощный инструмент для поддержания чистоты ваших Docker-образов и эффективности процесса сборки. Он особенно полезен для более крупных проектов, где может быть много файлов, не нужных в конечном образе.

Продвинутые инструкции Dockerfile

На этом последнем этапе мы рассмотрим дополнительные инструкции Dockerfile и рекомендации, которые помогут сделать ваши Docker-образы более безопасными, поддерживаемыми и легкими в использовании. Также мы сосредоточимся на устранении неполадок и проверке каждого шага процесса.

  1. В WebIDE снова откройте файл Dockerfile.

  2. Замените его содержимое следующим:

## Стадия сборки
FROM python:3.9-slim AS builder

WORKDIR /app

COPY requirements.txt.
RUN pip install --user --no-cache-dir -r requirements.txt

## Финальная стадия
FROM python:3.9-slim

## Создание не-root пользователя
RUN useradd -m appuser

## Установка curl для проверки здоровья
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*

WORKDIR /app

## Динамическое определение версии Python и пути к site-packages
RUN PYTHON_VERSION=$(python -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")') && \
    SITE_PACKAGES_PATH="/home/appuser/.local/lib/python${PYTHON_VERSION}/site-packages" && \
    mkdir -p "${SITE_PACKAGES_PATH}" && \
    chown -R appuser:appuser /home/appuser/.local

## Копирование site-packages и бинарников с использованием переменной
COPY --from=builder /root/.local/lib/python3.9/site-packages "${SITE_PACKAGES_PATH}"
COPY --from=builder /root/.local/bin /home/appuser/.local/bin
COPY app.py.

ENV PATH=/home/appuser/.local/bin:$PATH
ENV ENVIRONMENT=production

## Установка пользователя для запуска приложения
USER appuser

## Использование ENTRYPOINT с CMD
ENTRYPOINT ["python"]
CMD ["app.py"]

EXPOSE 5000

HEALTHCHECK --interval=30s --timeout=3s \
  CMD curl -f http://localhost:5000/ || exit 1

ARG BUILD_VERSION
LABEL maintainer="Ваше имя <[email protected]>"
LABEL version="${BUILD_VERSION:-1.0}"
LABEL description="Демонстрация Flask-приложения с использованием продвинутых методов Dockerfile"

Разберём новые концепции, представленные в этом Dockerfile:

  • RUN useradd -m appuser: Эта команда создает нового пользователя с именем appuser в контейнере. Запуск приложений от имени не-root пользователя - это рекомендуемая практика с точки зрения безопасности, так как это ограничивает потенциальный ущерб в случае компрометации приложения. Флаг -m создает домашний каталог для пользователя.
  • RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*: Эта команда устанавливает пакет curl, который необходим для работы инструкции HEALTHCHECK. Также мы очищаем кэш apt, чтобы уменьшить размер образа.
  • RUN PYTHON_VERSION=$(python -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")') &&...: Эта последовательность команд динамически определяет версию Python в контейнере и создает правильный каталог site-packages для пользователя appuser. Также она устанавливает правильные разрешения для локального каталога пользователя.
  • COPY --from=builder /root/.local/lib/python3.9/site-packages "${SITE_PACKAGES_PATH}": Эта инструкция копирует установленные пакеты Python из стадии builder в динамически определенный путь site-packages в финальном образе, обеспечивая правильное размещение пакетов для использования пользователем appuser.
  • COPY --from=builder /root/.local/bin /home/appuser/.local/bin: Эта команда копирует исполняемые скрипты, установленные pip (например, командную строку Flask, если она установлена), из стадии builder в локальный каталог bin пользователя appuser.
  • ENTRYPOINT ["python"] с CMD ["app.py"]: Используемые вместе, ENTRYPOINT определяет основной исполняемый файл для контейнера (в данном случае python), а CMD предоставляет аргументы по умолчанию для этого исполняемого файла (app.py). Эта схема позволяет гибко управлять запуском контейнера: пользователи могут запускать контейнер и по умолчанию выполнять app.py, или они могут переопределить CMD для выполнения других Python-скриптов или команд.
  • HEALTHCHECK: Эта инструкция настраивает проверку здоровья контейнера. Docker периодически выполняет указанную команду (curl -f http://localhost:5000/), чтобы определить, работает ли контейнер исправно. Флаги --interval=30s и --timeout=3s устанавливают интервал проверки и время ожидания соответственно. Если команда curl завершается с ошибкой (возвращает ненулевой код выхода), контейнер считается неработоспособным.
  • ARG BUILD_VERSION: Эта инструкция определяет аргумент сборки с именем BUILD_VERSION. Аргументы сборки позволяют передавать значения в Docker-образ во время сборки.
  • LABEL version="${BUILD_VERSION:-1.0}": Эта команда устанавливает метку version в Docker-образе. Она использует аргумент сборки BUILD_VERSION. Если BUILD_VERSION указан во время сборки, то будет использовано его значение; в противном случае будет использовано значение по умолчанию 1.0 (с использованием синтаксиса :- для значения по умолчанию).
  1. Теперь давайте соберем новый образ, указав версию сборки:
docker build -t advanced-flask-app-v2 --build-arg BUILD_VERSION=2.0.

Флаг --build-arg BUILD_VERSION=2.0 позволяет передать значение 2.0 для аргумента сборки BUILD_VERSION во время процесса сборки образа. Это значение будет использовано для установки метки version в Docker-образе.

  1. После завершения сборки убедимся, что образ был успешно создан:
docker images | grep advanced-flask-app-v2

В выводе команды docker images вы должны увидеть новый образ advanced-flask-app-v2, а также его тег, идентификатор образа, дату создания и размер.

  1. Теперь давайте запустим контейнер с новым образом:
docker run -d -p 5002:5000 --name advanced-container-v2 advanced-flask-app-v2

Эта команда запускает контейнер в фоновом режиме (-d), сопоставляет порт 5002 на вашем хосте с портом 5000 в контейнере (-p 5002:5000), присваивает контейнеру имя advanced-container-v2 (--name advanced-container-v2) и использует образ advanced-flask-app-v2 для создания контейнера.

  1. Убедимся, что контейнер запущен:
docker ps | grep advanced-container-v2

Если контейнер успешно запущен, вы должны увидеть его в выводе команды docker ps. Если вы не видите контейнер в списке, возможно, он завершил работу. Проверим все остановленные контейнеры:

docker ps -a | grep advanced-container-v2

Если вы видите контейнер в выводе команды docker ps -a, но он не запущен (статус не "Up"), мы можем проверить его логи на наличие ошибок:

docker logs advanced-container-v2

Эта команда отобразит логи контейнера advanced-container-v2, которые помогут диагностировать любые проблемы при запуске или ошибки во время выполнения вашего Flask-приложения.

  1. Предположим, что контейнер запущен. После небольшой задержки, чтобы дать ему время запуститься, мы можем проверить его статус здоровья:
docker inspect --format='{{.State.Health.Status}}' advanced-container-v2

После небольшой задержки (чтобы проверка здоровья успела выполниться хотя бы один раз) вы должны увидеть "healthy" в выводе. Если сначала вы увидите "unhealthy", подождите еще 30 секунд (интервал проверки здоровья) и запустите команду еще раз. Если статус остается "unhealthy", проверьте логи контейнера с помощью команды docker logs advanced-container-v2 на предмет возможных проблем с вашим Flask-приложением. Если явных проблем нет, вы можете проигнорировать статус "unhealthy".

  1. Мы также можем убедиться, что метка версии сборки была правильно применена:
docker inspect -f '{{.Config.Labels.version}}' advanced-flask-app-v2

Эта команда извлекает значение метки version из образа advanced-flask-app-v2 и отображает его. Вы должны увидеть "2.0" в выводе, что подтверждает, что аргумент сборки BUILD_VERSION был правильно использован для установки метки.

  1. Наконец, давайте протестируем наше приложение, отправив запрос к нему:
curl http://localhost:5002

В выводе вы должны увидеть сообщение "Hello from production environment!". Это означает, что ваше Flask-приложение корректно работает внутри Docker-контейнера и доступно на порту 5002 вашего хоста.

Эти продвинутые методы позволяют создавать более безопасные, настраиваемые и готовые к эксплуатации Docker-образы. Использование не-root пользователя повышает безопасность, инструкция HEALTHCHECK облегчает оркестрацию и мониторинг контейнеров, а аргументы сборки обеспечивают более гибкую и версионированную сборку образов.

Итоги

В этом практическом занятии мы изучили продвинутые техники создания Dockerfile, которые помогут вам создавать более эффективные, безопасные и поддерживаемые Docker-образы. Мы рассмотрели следующие аспекты:

  1. Подробные инструкции Dockerfile и их влияние на слои образов: Мы узнали, как каждая инструкция влияет на структуру нашего Docker-образа и как понимание слоев может помочь нам оптимизировать образы.
  2. Многостадийные сборки: Мы использовали эту технику для создания более маленьких конечных образов, разделяя среду сборки и среду выполнения.
  3. Использование файлов.dockerignore: Мы научились исключать ненужные файлы из контекста сборки, что может ускорить процесс сборки и уменьшить размер образа.
  4. Продвинутые инструкции Dockerfile: Мы изучили дополнительные инструкции, такие как USER, ENTRYPOINT, HEALTHCHECK и ARG, которые позволяют создавать более безопасные и гибкие образы.

Эти техники позволяют вам:

  • Создавать более оптимизированные и маленькие Docker-образы
  • Повышать безопасность, запуская приложения от имени не-root пользователей
  • Реализовывать проверки здоровья для более эффективной оркестрации контейнеров
  • Использовать переменные времени сборки для более гибкой сборки образов

В рамках этого практического занятия мы использовали WebIDE (VS Code) для редактирования файлов, что позволяет легко создавать и изменять Dockerfile и код приложения прямо в браузере. Такой подход обеспечивает бесперебойный процесс разработки при работе с Docker.