Docker — не удается обмениваться данными между контейнерами в томе (docker-compose 3)

На данный момент у меня есть несколько контейнеров для веб-приложения (nginx, gunicorn, postgres и node для создания статических файлов из исходного кода и рендеринга на стороне сервера React). В Dockerfile для контейнера node у меня есть два шага: сборка и запуск (Dockerfile .узел). Он заканчивается двумя каталогами внутри контейнера: bundle_client — статический для nginx и bundle_server — он используется в самом контейнере узла для запуска экспресс-сервера.

Затем мне нужно поделиться встроенной статической папкой (bundle_client) с контейнером nginx. Для этого в соответствии со ссылкой на docker-compose в моем docker-compose.yml у меня есть следующие сервисы (см. полный docker-compose.yml):

node:
  volumes:
    - vnode:/usr/src

nginx:
  volumes:
    - vnode:/var/www/tg/static
  depends_on:
    - node

и объемы:

volumes:
  vnode:

Выполнение docker-compose build завершается без ошибок. При запуске docker-compose up все работает нормально, и я могу открыть localhost:80, и есть nginx, gunicorn и node express SSR, все отлично работает, и я вижу веб-страницу, но все статические файлы возвращают ошибку 404 не найдено.

Если я проверю тома с помощью docker volume ls, я увижу два вновь созданных тома с именами tg_vnode (которые мы рассматриваем здесь) и tg_vdata (см. полный файл docker-compose.yml)

Если я войду в контейнер nginx с docker run -ti -v /tmp:/tmp tg_node /bin/bash, я не увижу свою папку www/tg/static, которая должна отображать мои статические файлы из тома узла. Также я попытался создать пустую папку /var/www/tg/static с контейнером nginx Dockerfile.nginx, но она остается пустой.

Если я сопоставляю папку bundle_client с хост-компьютера в docker-compose.yml в разделе nginx.volumes как - ./client/bundle_client:/var/www/tg/static, все работает нормально, и я могу видеть все статические файлы, обслуживаемые nginx в браузере.

Что я делаю неправильно и как заставить мой контейнер делиться встроенным статическим контентом с контейнером nginx?

PS: я прочитал всю документацию, все проблемы с github и вопросы и ответы по стеку, и, как я понял, это должно работать, и нет информации, что делать, когда это не так.

UPD: Результат docker volume inspect vnode:

[
{
    "CreatedAt": "2018-05-18T12:24:38Z",
    "Driver": "local",
    "Labels": {
        "com.docker.compose.project": "tg",
        "com.docker.compose.version": "1.21.1",
        "com.docker.compose.volume": "vnode"
    },
    "Mountpoint": "/var/lib/docker/volumes/tg_vnode/_data",
    "Name": "tg_vnode",
    "Options": null,
    "Scope": "local"
}
]

Файлы: Dockerfile.node, docker-compose.yml

Dockerfile Nginx: Dockerfile.nginx


UPD: я создал упрощенное репо, чтобы воспроизвести вопрос: репозиторий (на npm install есть несколько предупреждений, не говоря уже о том, что он устанавливается и собирается нормально). В конце концов, когда мы открываем localhost:80, мы видим пустую страницу и сообщения 404 для статических файлов (vendor.js и app.js) в инструментах разработчика Chrome, но должно быть сообщение React app: static loaded, сгенерированное реагирующим скриптом.


person Max    schedule 18.05.2018    source источник
comment
Что ж, проблема с использованием одного и того же контейнера тома в нескольких службах заключается в том, что контейнер, который был загружен первым, скопирует свои собственные данные в том, и все остальные увидят одно и то же. Если вы хотите избежать этого, вам нужно использовать nocopy option. Я вижу в вашем сочинении, что вы использовали его в нескольких местах, только одно место поместит его содержимое в том и выиграет с исходным содержанием. Сборка поместит файлы во время выполнения, если это происходит после монтирования. Мне трудно визуализировать вашу композицию, но посмотрите, помогут ли эти указатели.   -  person Tarun Lalwani    schedule 21.05.2018
comment
Да, ...что контейнер, который был загружен первым, скопирует свои данные в том, а остальные увидят то же самое, и это то, что мне нужно - изначально скопировать встроенный bundle_client артефакт в том, а затем использовать его в nginx контейнер. И я удостоверяюсь, что контейнер узла находится первым в порядке, устанавливая depends_on.node для контейнера nginx. Возможно, я перепутал Dokerfile и compose file и неправильно понял, как это на самом деле работает. Не могли бы вы объяснить - сборка помещает файлы во время выполнения, если это происходит после монтирования. Я не уверен, что понимаю это.   -  person Max    schedule 21.05.2018
comment
Я добавил nocopy: true к nginx.volumes в соответствии с длинным синтаксисом и прокомментировал использование vnode в контейнере gunicorn, но, к сожалению, папка внутри контейнера nginx все еще пуста.   -  person Max    schedule 21.05.2018
comment
Можно ли получить репо для отладки?   -  person Tarun Lalwani    schedule 21.05.2018
comment
Не могли бы вы добавить свой файл Dockerfile.nginx, пожалуйста? Я пытаюсь воспроизвести вашу ошибку. У меня есть некоторые подсказки, но мне нужно проверить контейнер nginx   -  person Alejandro Galera    schedule 21.05.2018
comment
@AlexGalera Конечно, я обновил вопрос.   -  person Max    schedule 21.05.2018
comment
@TarunLalwani К сожалению, в этом нет смысла, и при сборке произойдет сбой, поскольку он содержит зависимости от частного локального хранилища. Но я могу попытаться упростить это до очень простого репозитория gunicorn/node/nginx, чтобы воспроизвести общие тома с той же конфигурацией.   -  person Max    schedule 21.05.2018
comment
Да, потому что трудно комментировать такую ​​длинную вещь   -  person Tarun Lalwani    schedule 21.05.2018
comment
Не могли бы вы добавить тег docker-volume к вопросу, пожалуйста? Заранее спасибо.   -  person Alejandro Galera    schedule 21.05.2018
comment
@TarunLalwani Я обновил вопрос с упрощенным репо. Но так как у него есть части от оригинального, у него могут быть некоторые дополнительные вещи, но я надеюсь, что это только в конфигурации npm/webpack, так что это не имеет значения.   -  person Max    schedule 21.05.2018
comment
Вы просто хотите поделиться bundle_client с nginx по адресу var/www/test/static .. верно?   -  person Tarun Lalwani    schedule 21.05.2018
comment
Абсолютно так же просто. Я просто хочу запустить docker-compose up, а затем открыть localhost и посмотреть, как обслуживаются все новые связанные статические данные.   -  person Max    schedule 21.05.2018
comment
Мне это нужно для развертывания и автоматического принятия новых коммитов (сейчас с docker-compose и, возможно, с docker-stack в будущем), создания новой версии статических файлов (и обновления других частей) и повторного развертывания нового выпуска. В режиме разработки я использую том хоста ./client:/var/www..., и он работает нормально, но в новом выпуске (развертывании) я должен сказать докеру, чтобы он автоматически создавал файлы и делился ими с nginx.   -  person Max    schedule 21.05.2018
comment
Теперь я вижу проблему, но по какой-то причине узел не работает в вашем репо, проверьте это сейчас   -  person Tarun Lalwani    schedule 21.05.2018
comment
Почему в Dockerfile.node нет CMD? Также не определен сценарий запуска в package.json?   -  person Tarun Lalwani    schedule 21.05.2018
comment
Давайте продолжим это обсуждение в чате.   -  person Tarun Lalwani    schedule 21.05.2018


Ответы (2)


Вам нужны два изменения. В службе вашего узла добавьте том, например:

volumes:
  - vnode:/usr/src/bundle_client

Поскольку вы хотите поделиться /usr/src/bundle_client, вам НЕ следует использовать /usr/src/, потому что это также приведет к общему доступу к полной папке и структуре.

А затем в вашем сервисе nginx добавьте объем, например:

volumes:
  - type: volume
    source: vnode
    target: /var/www/test/static
    volume:
      nocopy: true

nocopy: true четко указывает на наше намерение, что на начальной карте контейнера содержимое сопоставленной папки не должно быть скопировано. И по умолчанию первый контейнер, сопоставленный с томом, получит содержимое сопоставленной папки. В вашем случае вы хотите, чтобы это был контейнер node.

Также перед тестированием убедитесь, что вы выполнили команду ниже, чтобы уничтожить кешированные тома:

docker-compose down -v

Вы можете видеть, что во время моего теста в контейнере были файлы:

У Nginx есть файлы

person Tarun Lalwani    schedule 21.05.2018

Пошаговое объяснение того, что происходит

Dockerfile.node

    ...
    COPY ./client /usr/src
    ...

докер-compose.yml

services:
  ...
  node:
    ...
    volumes:
      - ./server/nginx.conf:/etc/nginx/nginx.conf:ro
      - vnode:/usr/src
  ...
volumes:
  vnode:
  1. docker-compose up создает с этим Dockerfile.node и разделом docker-compose именованный том с данными, сохраненными в /usr/src.

Dockerfile.nginx

FROM nginx:latest

COPY ./server/nginx.conf /etc/nginx/nginx.conf

RUN mkdir -p /var/www/tg/static

EXPOSE 80
EXPOSE 443

CMD ["nginx", "-g", "daemon off;"]
  1. Это приводит к тому, что контейнеры nginx, созданные с помощью docker-compose, будут иметь пустой /var/www/tg/static/

докер-compose.yml

 ...
 nginx:
    build:
      context: .
      dockerfile: ./Dockerfile.nginx
    container_name: tg_nginx
    restart: always
    volumes:
      - ./server/nginx.conf:/etc/nginx/nginx.conf:ro
      - vnode:/var/www/tg/static
    ports:
      - "80:80"
      - "443:443"
    depends_on:
      - node
      - gunicorn
    networks:
      - nw_web_tg

 volumes:
   vdata:
   vnode:
  1. docker-compose up создаст именованный том vnode и заполнит его данными из /var/www/tg/static (на данный момент пустого) в существующий vnode.

Итак, на данный момент - контейнер nginx имеет пустой каталог /var/www/tg/static, потому что он был создан пустым (см. mkdir в Dockerfile.nginx) - контейнер узла имеет каталог /usr/src с файлом клиента (см. Dockerfile.node) — vnode имеет содержимое /usr/src из node и /var/www/tg/static из nginx.

Определенно, чтобы передать данные из /usr/src из вашего контейнера node в контейнер /var/www/tg/static в контейнере nginx, вам нужно сделать что-то не очень красивое, потому что Docker еще не разработал другого способа: вам нужно объединить именованный том в исходной папке с bind volume в месте назначения:

 nginx:
     build:
       context: .
       dockerfile: ./Dockerfile.nginx
     container_name: tg_nginx
     restart: always
     volumes:
       - ./server/nginx.conf:/etc/nginx/nginx.conf:ro
       - /var/lib/docker/volumes/vnode/_data:/var/www/tg/static
     ports:
       - "80:80"
       - "443:443"
     depends_on:
       - node
       - gunicorn
     networks:
       - nw_web_tg

Просто измените в docker-compose - vnode:/var/www/tg/static на - /var/lib/docker/volumes/vnode/_data:/var/www/tg/static

person Alejandro Galera    schedule 21.05.2018
comment
Я так же думал и изначально не было RUN mkdir -p /var/www/tg/static его добавили потом просто для пробы. Если прокомментировать это, я получаю папку /var, которая не содержит www. - person Max; 21.05.2018
comment
Явное удаление с помощью RUN rm -rf /var/www/tg/static не имеет никакого эффекта. - person Max; 21.05.2018
comment
Хорошо, я неправильно понял. Смотрите объяснение, и мы продолжим. Я не очень хорошо понимаю, что вы пытаетесь сделать. Возможно, вы что-то не так поняли в именованных томах, и я надеюсь, что это немного прояснилось. - person Alejandro Galera; 21.05.2018
comment
Я попытаюсь объяснить кратко здесь: - person Max; 21.05.2018
comment
У меня есть контейнер узла, который на docker-compose up должен запустить npm install && npm build и создать папку артефакта со скомпилированной статикой и поместить ее куда-нибудь, чтобы передать ее в контейнер nginx (как я понимаю, именованный том - единственный способ сделать это). Далее, как я понял, когда я впервые упомяну том (здесь секция узла), он заполнит его заданными данными, а затем (в nginx — второе упоминание) будет использовать эти данные. И я удостоверяюсь, что node является первым, а nginx вторым на depends_on.node. Что я пропустил здесь? - person Max; 21.05.2018
comment
Вы правы насчет именованных томов. Где npm install создает эту информацию? В /usr/src в node контейнере? Я думаю, что вы упускаете то, что вам не следует создавать другой именованный том в контейнере nginx, потому что nginx является местом назначения данных из узла, а не источником данных (потому что он пуст). Итак, вам нужно создать привязку именованного тома к каталогу nginx. - person Alejandro Galera; 21.05.2018
comment
Точно, он создает папку со статическими файлами /usr/src/bundle_client и она там, если я проверяю контейнер узла. - person Max; 21.05.2018
comment
К сожалению, изменение имени тома на путь тоже не работает =( Я добавил упрощенный репозиторий в тело вопроса, может быть, это поможет. - person Max; 21.05.2018