Копирование кода с помощью Dockerfile или монтирование тома с помощью docker-compose

Я следую официальному руководству на сайте Docker

Файл докера имеет вид

FROM python:3
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
WORKDIR /code
COPY requirements.txt /code/
RUN pip install -r requirements.txt
COPY . /code/

docker-compose это:

  web:
    build: .
    command: python manage.py runserver 0.0.0.0:8000
    volumes:
      - .:/code

Я не понимаю, почему в Dockerfile они копируют код COPY . /code/, а затем снова монтируют его в docker-compose - .:/code? Разве недостаточно, если я либо копирую, либо монтирую?

Используется для сохранения изображения после работы с кодом.

Когда вы используете COPY, он сохраняет его как часть изображения.

Монтаж только во время разработки.

И volumes:, и command: в файле docker-compose.yml являются ненужными и должны быть удалены. Код и CMD по умолчанию для запуска должны быть включены в Dockerfile.

Когда вы настраиваете среду Docker, представьте, что вам предоставлен root-доступ к совершенно новой виртуальной машине, на которой не установлено ничего, кроме Docker. Идеальный вариант - это возможность docker run your-image в виде одной команды, взяв ее из какого-то реестра, с минимальным количеством дополнительных опций. Когда вы запускаете образ, вам не нужно отдельно предоставлять его исходный код или команду для запуска, они обычно должны быть встроены в образ.

В большинстве случаев вы сможете построить установку Compose с довольно небольшим количеством опций. Каждому сервису нужен image: или build: (оба, если вы планируете продвигать образ), часто environment:, ports: и depends_on: (помня об ограничениях последнего варианта), а вашему контейнеру (контейнерам) базы данных понадобится volumes: для их постоянного состояния. Обычно это все.

Единственный случай, когда вам нужно переопределить command: в Compose - это если вам нужно запустить отдельную команду на одном и том же изображении и кодовой базе. В контексте Python это часто возникает для запуска Celery worker рядом с приложением Django.

Вот полный пример с веб-приложением с базой данных и асинхронным рабочим. Кэш-слой Redis не обладает персистентностью, и никакие файлы не хранятся локально ни в каких контейнерах, кроме хранилища базы данных. Единственное, чего не хватает, - это настройки учетных данных базы данных, для чего требуются дополнительные переменные environment:. Обратите внимание на отсутствие volumes: для кода, единственное переопределение command: там, где это необходимо, и переменные environment:, предоставляющие имена хостов.

version: '3.8'
services:
  app:
    build: .
    ports: ['8000:8000']
    environment:
      REDIS_HOST: redis
      PGHOST: db
  worker:
    build: .
    command: celery worker -A queue -l info
    environment:
      REDIS_HOST: redis
      PGHOST: db
  redis:
    image: redis:latest
  db:
    image: postgres:13
    volumes:
      - pgdata:/var/lib/postgresql/data
volumes:
  pgdata:

Там, где вы видите, что volumes: перезаписывает код образа подобным образом, это обычно попытка избежать необходимости пересборки образа при изменении кода. Однако в показанном вами Dockerfile пересборка практически бесплатна, если предположить, что файл requirements.txt не изменился. Кроме того, почти всегда можно вести повседневную разработку вне Docker - для Python, в виртуальной среде - и использовать контейнерную установку для интеграционного тестирования и развертывания, и это обычно проще, чем убедить вашу IDE, что нужный ей интерпретатор языка находится в контейнере

Иногда Dockerfile выполняет дополнительную настройку (изменение окончаний строк или прав доступа, перестановка файлов), и монтирование volumes: будет скрывать это. Это означает, что вы никогда не запустите код, встроенный в образ в процессе разработки, поэтому если в настройках образа есть какие-то ошибки, вы их не увидите. Короче говоря, это вновь создает проблему "работает на моей машине", которой Docker обычно старается избежать.

Обычно мы используем один Dockerfile для создания образа, который мы используем как для производства, так и для разработки. Это увеличивает схожесть среды выполнения приложения, что очень хорошо.

В отличие от того, что пишет @David: довольно удобно вести повседневную разработку с помощью контейнера Docker. Ваш код работает в одном и том же окружении в продакшене и в разработке. Если вы используете virtualenv в разработке, вы не используете этот очень практичный атрибут Docker. Среды могут разойтись без вашего ведома, и prod может сломаться, в то время как dev продолжает работать.

Итак, как позволить одному Dockerfile создать образ, который можно запустить в производстве и использовать во время разработки? Сначала мы должны поговорить о коде, который мы хотим запускать. В production мы хотим иметь определенную коллекцию кода для запуска (скорее всего, код на определенном коммите в вашем репозитории). Но во время разработки мы постоянно меняем код, который хотим запустить, проверяя различные ветки или редактируя файлы. Как же удовлетворить оба требования?

Мы инструктируем Dockerfile скопировать некоторый каталог кода (в вашем примере .) в изображение, в вашем примере /code. Если мы не сделаем ничего другого: это будет код, который будет запущен. Так происходит в производстве. Но в разработке мы можем переопределить каталог /code с каталогом на хост-компьютере с помощью тома. В примере файл Docker Compose задает том. Тогда мы можем легко изменить код, запущенный в dev-контейнере, без необходимости пересборки образа.

diagram showing relationship between Docker image and two Docker containers

Кроме того: даже если перестройка выполняется быстро, позволить процессу Python перезапуститься с новыми файлами гораздо быстрее.

Вернуться на верх