Eu tenho um aplicativo com os seguintes serviços:
web/
- mantém e executa um servidor web python 3 flask na porta 5000. Usa sqlite3.worker/
- possui umindex.js
arquivo que é um trabalhador para uma fila. o servidor da web interage com essa fila usando uma API json over port9730
. O trabalhador usa redis para armazenamento. O trabalhador também armazena dados localmente na pastaworker/images/
Agora, esta questão diz respeito apenas ao worker
.
worker/Dockerfile
FROM node:0.12
WORKDIR /worker
COPY package.json /worker/
RUN npm install
COPY . /worker/
docker-compose.yml
redis:
image: redis
worker:
build: ./worker
command: npm start
ports:
- "9730:9730"
volumes:
- worker/:/worker/
links:
- redis
Quando executo docker-compose build
, tudo funciona como esperado e todos os módulos npm são instalados /worker/node_modules
como eu esperava.
npm WARN package.json unfold@1.0.0 No README data
> phantomjs@1.9.2-6 install /worker/node_modules/pageres/node_modules/screenshot-stream/node_modules/phantom-bridge/node_modules/phantomjs
> node install.js
<snip>
Mas quando o faço docker-compose up
, vejo este erro:
worker_1 | Error: Cannot find module 'async'
worker_1 | at Function.Module._resolveFilename (module.js:336:15)
worker_1 | at Function.Module._load (module.js:278:25)
worker_1 | at Module.require (module.js:365:17)
worker_1 | at require (module.js:384:17)
worker_1 | at Object.<anonymous> (/worker/index.js:1:75)
worker_1 | at Module._compile (module.js:460:26)
worker_1 | at Object.Module._extensions..js (module.js:478:10)
worker_1 | at Module.load (module.js:355:32)
worker_1 | at Function.Module._load (module.js:310:12)
worker_1 | at Function.Module.runMain (module.js:501:10)
Acontece que nenhum dos módulos está presente /worker/node_modules
(no host ou no contêiner).
Se no host, eu npm install
, então tudo funciona muito bem. Mas eu não quero fazer isso. Eu quero que o contêiner manipule dependências.
O que está acontecendo de errado aqui?
(Escusado será dizer que todos os pacotes estão dentro package.json
.)
volumes: - worker/:/worker/
bloco dodocker-compose.yml
arquivo. Esta linha substitui a pasta que você cria com o comando COPY.When I run docker-compose build, everything works as expected and all npm modules are installed in /worker/node_modules as I'd expect.
- Como você checou isso?Respostas:
Isso acontece porque você adicionou seu
worker
diretório como um volume ao seudocker-compose.yml
, pois o volume não é montado durante a compilação.Quando o docker cria a imagem, o
node_modules
diretório é criado dentro doworker
diretório e todas as dependências são instaladas nele. Em tempo de execução, oworker
diretório de docker externo é montado na instância do docker (que não possui o instaladonode_modules
), ocultando o quenode_modules
você acabou de instalar. Você pode verificar isso removendo o volume montado do seudocker-compose.yml
.Uma solução alternativa é usar um volume de dados para armazenar todos os
node_modules
dados, pois os volumes de dados são copiados nos dados da imagem da janela de encaixe incorporada antes daworker
montagem do diretório. Isso pode ser feito dadocker-compose.yml
seguinte maneira:Não tenho certeza se isso impõe problemas à portabilidade da imagem, mas como parece que você está usando o docker principalmente para fornecer um ambiente de tempo de execução, isso não deve ser um problema.
Se você quiser ler mais sobre volumes, há um bom guia do usuário disponível aqui: https://docs.docker.com/userguide/dockervolumes/
EDIT: Desde então, o Docker mudou sua sintaxe para exigir uma
./
montagem para arquivos relacionados ao arquivo docker-compose.yml.fonte
/worker/node_modules
permaneceu o mesmo de antes (com dependências antigas). Existe algum truque para usar o novo volume ao reconstruir a imagem?docker-compose rm
parece corrigir esse problema, mas acredito que deve haver uma solução melhor e mais fácil.rebuild --no-cache
sempre que os deps mudam?A
node_modules
pasta é substituída pelo volume e não é mais acessível no contêiner. Estou usando a estratégia de carregamento do módulo nativo para remover a pasta do volume:Dockerfile:
O
node_modules
diretório não está acessível de fora do contêiner porque está incluído na imagem.fonte
node_modules
não é acessível a partir de fora do recipiente, mas não é realmente uma desvantagem;)docker-compose run app npm install
, criará um node_modules no diretório atual e não precisará mais reconstruir a imagem.A solução fornecida pelo @FrederikNS funciona, mas eu prefiro nomear explicitamente meu volume node_modules.
Meu
project/docker-compose.yml
arquivo (docker-compose versão 1.6+):minha estrutura de arquivos é:
Ele cria um volume nomeado
project_node_modules
e o reutiliza toda vez que eu faço meu aplicativo.Minha
docker volume ls
aparência é assim:fonte
Recentemente, tive um problema semelhante. Você pode instalar em
node_modules
outro lugar e definir aNODE_PATH
variável de ambiente.No exemplo abaixo eu instalado
node_modules
em/install
worker / Dockerfile
docker-compose.yml
fonte
node_modules
base neste artigo . Mas isso me levou a experimentar esse problema . Esta solução aqui de criar um diretório separado para copiarpackage.json
, executarnpm install
lá e especificar aNODE_PATH
variável de ambientedocker-compose.yml
para apontar para anode_modules
pasta desse diretório funciona e parece certo.npm install
no host? Parecenode_modules
que aparecerá no host e será refletido no contêiner, tendo prioridadeNODE_PATH
. Portanto, o contêiner usará node_modules do host.Há uma solução elegante:
Apenas monte não o diretório inteiro, mas apenas o diretório do aplicativo. Dessa forma, você não terá problemas com
npm_modules
.Exemplo:
Dockerfile.dev:
fonte
ATUALIZAÇÃO: use a solução fornecida pelo @FrederikNS.
Eu encontrei o mesmo problema. Quando a pasta
/worker
é montada no contêiner - todo o seu conteúdo será sincronizado (portanto, a pasta node_modules desaparecerá se você não a tiver localmente.)Devido a pacotes npm incompatíveis baseados no sistema operacional, eu não podia apenas instalar os módulos localmente - em seguida, iniciar o contêiner, então ..
Minha solução para isso foi agrupar a fonte em uma
src
pasta e vincularnode_modules
a ela usando esse arquivo index.js . Portanto, oindex.js
arquivo agora é o ponto de partida do meu aplicativo.Quando executo o contêiner, montei a
/app/src
pasta no meu localsrc
pasta .Portanto, a pasta contêiner se parece com isso:
É feio , mas funciona ..
fonte
Devido à maneira como o Node.js carrega módulos ,
node_modules
pode estar em qualquer lugar do caminho para o seu código-fonte. Por exemplo, colocar a sua fonte no/worker/src
e suapackage.json
em/worker
, por isso/worker/node_modules
é o lugar onde eles estão instalados.fonte
Instalar node_modules no container para diferente da pasta do projeto e definir NODE_PATH para a pasta node_modules me ajuda (é necessário reconstruir o container).
Estou usando o docker-compose. Minha estrutura de arquivos do projeto:
docker-compose.yml:
Dockerfile na pasta nodejs:
fonte
NODE_PATH
foi a chave para mim.CMD npm start
não usa o NODE_PATH especificado.Também há uma solução simples sem mapear o
node_module
diretório para outro volume. Está prestes a mover a instalação de pacotes npm para o comando final do CMD.worker / Dockerfile
docker-compose.yml
fonte
Existem dois requisitos separados que eu vejo para ambientes de desenvolvimento de nós ... monte seu código-fonte no contêiner e monte os node_modules a partir do contêiner (para o seu IDE). Para realizar o primeiro, você faz a montagem habitual, mas não tudo ... apenas as coisas que você precisa
(a razão para não fazer
- /worker/node_modules
é porque o docker-compose manterá esse volume entre as execuções, o que significa que você pode divergir do que realmente está na imagem (derrotando o objetivo de não apenas ligar a montagem do host)).O segundo é realmente mais difícil. Minha solução é um pouco imprudente, mas funciona. Eu tenho um script para instalar a pasta node_modules na minha máquina host e só lembro de chamá-la sempre que atualizar o package.json (ou adicioná-lo ao make target que executa a compilação docker-compose localmente).
fonte
Na minha opinião, não devemos
RUN npm install
no Dockerfile. Em vez disso, podemos iniciar um contêiner usando o bash para instalar as dependências antes de executar o serviço formal do nófonte
node_modules
persistência mesmo após a remoção do contêiner, você também deve saber quando ou não fazernpm install
manualmente. O OP sugere fazê-lo em todas as compilações de imagens . Você pode fazer isso, mas também não precisa usar um volume para isso. Em cada compilação, os módulos estarão atualizados a qualquer momento.Você pode tentar algo assim no seu Dockerfile:
Então você deve usar o volume assim:
O script de inicialização deve fazer parte do seu repositório de trabalho e fica assim:
Portanto, o node_modules faz parte do volume do seu trabalhador e é sincronizado e os scripts npm são executados quando tudo estiver funcionando.
fonte
Você também pode abandonar o Dockerfile, devido à sua simplicidade, basta usar uma imagem básica e especificar o comando no seu arquivo de composição:
Isso é particularmente útil para mim, porque eu só preciso do ambiente da imagem, mas opere nos meus arquivos fora do contêiner e acho que é isso que você deseja fazer também.
fonte