Vários FROMs - o que significa

112

Quero construir uma imagem docker para o projeto Linkurious no github, que requer o banco de dados Neo4j e o Node.js para ser executado.

minha primeira abordagem foi declarar uma imagem de base para minha imagem, contendo o Neo4j. Os documentos de referência não definem "imagem de base" de nenhuma maneira útil:

Imagem de base: uma imagem sem pai é uma imagem de base

do qual li que só posso ter uma imagem de base se essa imagem não tiver a própria imagem de base.

mas o que é uma imagem de base? Isso significa que se eu declarar neo4j / neo4j em uma diretiva FROM, que quando minha imagem for executada, o banco de dados neo será executado automaticamente e estará disponível no contêiner na porta 7474?

lendo a referência do Docker (consulte: https://docs.docker.com/reference/builder/#from ) Eu vejo:

FROM pode aparecer várias vezes em um único Dockerfile para criar várias imagens. Simplesmente anote a última saída de ID de imagem pelo commit antes de cada novo comando FROM.

eu quero criar várias imagens? parece que o que eu quero é ter uma única imagem que contenha o conteúdo de outras imagens, por exemplo, neo4j e node.js

Não encontrei nenhuma diretiva para declarar dependências no manual de referência. não há dependências como no RPM onde, para executar minha imagem, o contexto de chamada deve primeiro instalar as imagens de que precisa?

Estou confuso...

ekkis
fonte
Nota: maio de 2017, agora você tem vários FROMem a Dockerfile. Veja minha resposta editada abaixo.
VonC
Veja se você acha minha resposta mais limpa. E se for assim, considere aceitá-lo.
Evan Carroll

Respostas:

113

o que é uma imagem de base?

Um conjunto de arquivos, mais EXPOSEas portas 'd ENTRYPOINTe CMD.
Você pode adicionar arquivos e construir uma nova imagem com base nessa imagem de base, com um novo Dockerfilecomeçando com uma FROMdiretiva: a imagem mencionada a seguir FROMé "a imagem de base" para sua nova imagem.

Isso significa que se eu declarar neo4j/neo4jem uma FROMdiretiva, quando minha imagem for executada, o banco de dados neo será executado automaticamente e estará disponível no contêiner na porta 7474?

Somente se você não substituir CMDe ENTRYPOINT.
Mas a imagem em si é suficiente: você usaria um FROM neo4j/neo4jse tivesse que adicionar arquivos relacionados a neo4jpara seu uso específico de neo4j.

FROM pode aparecer várias vezes em um único Dockerfile

Não: há uma proposta para remover esse "recurso" de qualquer maneira ( problema 13026 )

O problema 14412 menciona:

O uso de múltiplos FROMnão é realmente um recurso, mas um bug (bem, o limite é restrito e há poucos casos de uso para múltiplos FROMem um Dockerfile).


Atualização em maio de 2017 (18 meses depois), com docker (moby) 17.05-ce .

Vários FROM podem ser usados ​​em um único Dockerfile.
Consulte " Builder pattern vs. Multi-stage builds in Docker " (por Alex Ellis ) e PR 31257 por Tõnis Tiigi .

Antes:

O padrão do construtor envolve o uso de duas imagens Docker - uma para executar uma construção e outra para enviar os resultados da primeira construção sem a penalidade da cadeia de construção e das ferramentas na primeira imagem.

Depois de:

A sintaxe geral envolve a adição de FROMtempos adicionais em seu Dockerfile - o que for a última FROMinstrução é a imagem de base final. Para copiar artefatos e saídas de imagens intermediárias, use COPY --from=<base_image_number>.

Primeira parte do Dockerfile:

FROM golang:1.7.3 as builder
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html  
COPY app.go    .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

Segunda parte do mesmo (!) Dockerfile:

FROM alpine:latest  
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /go/src/github.com/alexellis/href-counter/app    .
CMD ["./app"]  

O resultado seria duas imagens, uma para a construção, uma com apenas o aplicativo resultante (muito, muito menor)

REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE

multi               latest              bcbbf69a9b59        6 minutes ago       10.3MB  
golang              1.7.3               ef15416724f6        4 months ago        672MB  
VonC
fonte
2
pena sobre a remoção de vários FROMs. parece-me mais útil, particularmente na ausência de mecanismo de dependência. com RPMs, por exemplo, posso declarar que meu pacote precisa de outro pacote para rodar, então na hora da instalação tudo é configurado para mim. a realidade é que quase tudo vai exigir várias dependências, então, na ausência de vários DEs, como isso deve funcionar?
ekkis
3
@ekkis, como mencionei na minha resposta anterior ( stackoverflow.com/a/33295292/6309 ), você executa seu sistema orquestrando vários contêineres, cada um fornecendo um serviço específico, e se comunicando por meio de --link ( docs.docker.com/ userguide / dockerlinks /… ).
VonC
2
@VonC Claro, em um mundo ideal, com um novo aplicativo e todos os padrões compreendidos. Nesse ínterim, espero que haja mais instâncias de pessoas tentando migrar suas soluções para o docker e tendo necessidades que não são resolvidas por rede, como dependências de software, todas usando uma base compatível, mas DE vários Dockerfiles. Em vez disso, o melhor que posso descobrir até agora é separar seus Dockerfiles para criar o meu próprio.
rainabba
@rainabba Concordo. Monólitos legados não serão migrados facilmente. Leituras interessantes: martinfowler.com/articles/… , threedots.tech/post/microservices-or-monolith-its-detail , hackernoon.com/…
VonC
2

A primeira resposta é muito complexa, histórica e pouco informativa para o meu gosto.


Na verdade, é bastante simples. O Docker fornece uma funcionalidade chamada builds de vários estágios, a ideia básica aqui é,

  • Livre você de ter que remover manualmente o que você não quer, forçando você a colocar na lista de permissões o que você quer,
  • Recursos gratuitos que, de outra forma, seriam usados ​​devido à implementação do Docker.

Vamos começar com o primeiro. Muitas vezes você verá com algo como o Debian.

RUN apt-get update \ 
  && apt-get dist-upgrade \
  && apt-get install <whatever> \
  && apt-get clean

Podemos explicar tudo isso nos termos acima. O comando acima é encadeado, de modo que representa uma única alteração sem a necessidade de imagens intermediárias. Se foi escrito assim,

RUN apt-get update ;
RUN apt-get dist-upgrade;
RUN apt-get install <whatever>;
RUN apt-get clean;

Isso resultaria em mais 3 imagens intermediárias temporárias. Tendo-o reduzido a uma imagem, resta um problema: apt-get cleannão limpa os artefatos usados ​​na instalação. Se um mantenedor do Debian incluir em sua instalação um script que modifica o sistema, essa modificação também estará presente na solução final (veja algo parecido pepperflashplugin-nonfreepara um exemplo disso).

Usando uma compilação de vários estágios, você obtém todos os benefícios de uma única ação alterada, mas isso exigirá que você coloque na lista de permissões manualmente e copie os arquivos que foram introduzidos na imagem temporária usando a COPY --fromsintaxe documentada aqui. Além disso, é uma ótima solução onde não há alternativa (como um apt-get clean) e, de outra forma, você teria muitos arquivos desnecessários em sua imagem final.

Veja também

Evan Carroll
fonte
obrigado, mas não vejo como você está tratando do meu problema. para mim, FROM é um mecanismo de herança e ter várias diretivas significa que posso herdar de vários pais. em sua resposta, você não faz menção a FROM ou ao conceito de aproveitar as vantagens do empacotamento de software de outros
ekkis
1
Talvez seja essa a confusão. FROMé principalmente uma declaração de namespace. O qualificador é mais uma extensão do que uma herança. Você pode declarar vários namespaces. E cada um desses namespaces pode estender um outro namespace. @ekkis Se a outra resposta funcionar para você, continue com ela.
Evan Carroll