У контейнера нет изменений кода, у изображения есть

#docker #docker-compose

#docker #docker-compose

Вопрос:

Да, этот вопрос был опубликован ранее. Да, я применил соответствующие решения и проигнорировал нерелевантные. Я просто не понимаю, что здесь происходит.

Проблема: я вношу изменения в свой исходный код, изменяя сообщение о возврате по умолчанию. Я КОПИРУЮ дерево src в свой базовый образ (многоступенчатые сборки). Я создаю stagingImage из базового изображения. Базовое изображение имеет изменение кода, но контейнер, выполняющий stagingImage, возвращает сообщение о предварительном изменении кода. Я воспроизвел это поведение на macOS (Catalina) и Amazon Linux 2.

Обратите внимание, что я делаю это вручную, т. Е. Я не полагаюсь на IDE, inotfy-ы и тому подобное. Единственными задействованными инструментами являются CLI, vim, make и docker (-compose).

Подробности: у меня есть Dockerfile, который способен выполнять многоступенчатые сборки. Вот соответствующая часть сборки baseImage:

 COPY ./composer.json /var/www/html/composer.json
COPY ./php-apache/auth.json  /root/.composer
COPY ./ /var/www/html
  

и вот пример того, как я создаю свои другие изображения :

 FROM php:7.4-apache AS cleanImage
COPY --from=baseImage / /

FROM cleanImage AS stagingImage
COPY ./.env.staging /var/www/html/.env
RUN /bin/bash -c 'rm -rf /var/lib/apt/lists/*'
# Set entrypoint
CMD ["-D", "FOREGROUND"]
ENTRYPOINT ["apachectl"]
  

Я изменил Makefile из главы 10 «Docker в действии». Вот пример:

 ## image-base   : Build the base image that the others are based on
.PHONY: image-base
image-base: metadata
  @echo "Building Base Image"
  docker image build --no-cache --force-rm --tag src:$(BUILD_ID)-base 
  -f src/Dockerfile 
  --target baseImage 
  --build-arg BUILD_ID='$(BUILD_ID)' 
  --build-arg BUILD_DATE='$(BUILD_TIME_RFC_3339)' 
  --build-arg VCS_REF='$(VCS_REF)' 
  ./src
  @echo "Built Base Image. BUILD_ID: $(BUILD_ID)"

## image-staging    : Build the staging image
.PHONY: image-staging
image-staging: metadata image-base
  @echo "Building Staging App Image"
  docker image build -t src:$(BUILD_ID)-staging 
  -f src/Dockerfile 
  --target=stagingImage 
  --build-arg BUILD_ID='$(BUILD_ID)' 
  --build-arg BUILD_DATE='$(BUILD_TIME_RFC_3339)' 
  --build-arg VCS_REF='$(VCS_REF)' 
  ./src
  @echo "Built Staging App Image. BUILD_ID: $(BUILD_ID)-staging"

## up env=<env>   : Bring up environments. env values are prod, local, staging.
.PHONY: up
up:
ifeq ($(strip $(BUILD_ID)),)
    $(error BUILD_ID environment variable is not set. Run `make metadata` to generate one)
endif
  docker-compose -f docker-compose.yml -f docker-$(env).yml up -d
  

Где BUILD_ID имеет вид ГГГГММДД-эпоха-git_SHA. Обратите внимание, что базовое изображение использует флаг —no-cache .

Пока все хорошо (я думаю).

Мой файл `docker-compose.yml« выглядит следующим образом:

 version: '3.7'

volumes:
  web_app:

services:
  php-apache:
    logging:
      driver: "json-file"
      options:
        max-file: "5"
        max-size: "10m"
    volumes:
      - web_app:/var/www/html
    env_file:
      - ./src/.env.prod
  

и мой docker-staging.yml файл выглядит так:

 version: '3.7'
services:
  php-apache:
    image: 'src:20200924-174443-3f16358-staging'
    container_name: perfboard-staging
    ports:
      - 8888:80
      - 9261:443
    env_file:
      - ./src/.env.staging
  

Да, я жестко запрограммировал имя stagingImage для целей отладки.

Что я ожидаю увидеть: когда я нажимаю localhost: 8888, я ожидаю увидеть свое измененное сообщение. У меня нет.

При проверке базового изображения появляется измененное сообщение. Я не могу напрямую проверить stagingImage, потому что я продолжаю получать ошибку Apache, предположительно, из-за точки входа.

Если я удалю все изображения и контейнеры из своей системы, это будет вести себя так, как ожидалось.

Удаление указанных выше базовых элементов и stagingImage не устраняет проблему.

Есть идеи о том, где искать?

Ответ №1:

Ваш docker-compose.yml файл указывает

 volumes:
  - web_app:/var/www/html
  

Это приводит к тому, что содержимое web_app тома монтируется поверх /var/www/html каталога в контейнере, скрывая все, что изначально было в образе.

Вы должны удалить это объявление тома.

Только в первый раз, когда вы запускаете контейнер, Docker скопирует содержимое образа в пустой именованный том. С этого момента он обрабатывает том как пользовательские данные и никогда не вносит в него никаких изменений; даже если базовое изображение обновлено, содержимое тома нет, и когда вы запускаете контейнер, том имеет приоритет над обновленным кодом изображения.

Существует ряд практических проблем, связанных с зависимостью от поведения «копировать из образа в том» (это не работает с монтированием привязки к хосту; это не работает в Kubernetes; это приводит к игнорированию обновлений изображений), и я бы очень постарался избежать монтирования любого тома поверхваш код приложения.

Если вы считаете, что важно иметь том по какой-то другой причине, вам нужно вызвать Compose для его удаления, чтобы он был воссоздан и снова имел поведение «только в первый раз». docker-compose down -v будут удалены все контейнеры, сети и тома, но это также будет включать такие вещи, как данные вашей базы данных. Возможно, вы сможете использовать docker-compose down (без -v ) для остановки контейнера, а затем использовать docker volume ls; docker volume rm dirname_web_app для ручного удаления тома.

Комментарии:

1. Моему приложению необходимо запускать cronjob один раз в день. IIUC, это означает, что мне нужен другой контейнер с cron, который может получить доступ к тому же исходному коду. Согласно моим исследованиям, использование томов — правильный способ сделать это, и это единственная причина, по которой том был там. Какова правильная практика для запуска задания cron в дополнение к моему веб-приложению? Скопировать код src в каждый контейнер отдельно?

2. Тома не предназначены или не предназначены для хранения кода. Вы можете создать отдельный образ и COPY добавить в него код; или, если в вашем существующем образе уже есть код задачи и демон cron (вероятно), вы можете запустить тот же образ с альтернативой command: ; или вы можете использовать библиотеку планировщика задач на родном языке.