Qual é a diferença entre "expor" e "publicar" no Docker?

517

Estou experimentando Dockerfiles e acho que entendo a maior parte da lógica. No entanto, não vejo a diferença entre "expor" e "publicar" uma porta nesse contexto.

Todos os tutoriais que vi primeiro incluem o EXPOSEcomando no Dockerfile:

...
EXPOSE 8080
...

Em seguida, eles criam uma imagem a partir deste Dockerfile:

$ docker build -t an_image - < Dockerfile

E publique a mesma porta acima ao executar a imagem:

$ docker run -d -p 8080 an_image

ou publique todas as portas usando

$ docker run -d -P an_image

Qual é o sentido de expor uma porta no Dockerfile, se ela será publicada de qualquer maneira? Seria necessário expor uma porta primeiro e não publicá-la mais tarde? Efetivamente, eu gostaria de especificar todas as portas que usarei no Dockerfile ao criar a imagem e não me incomodar com elas novamente, executando-as simplesmente com:

$ docker run -d an_image

Isso é possível?

user1496984
fonte

Respostas:

732

Basicamente, você tem três opções:

  1. Nem especifique EXPOSEnem-p
  2. Especifique apenas EXPOSE
  3. Especifique EXPOSEe-p

1) Se você não especificar EXPOSEnem -p, o serviço no contêiner será acessível apenas de dentro do próprio contêiner.

2) Se você é EXPOSEuma porta, o serviço no contêiner não pode ser acessado de fora do Docker, mas de dentro de outros contêineres do Docker. Portanto, isso é bom para a comunicação entre contêineres.

3) Se você EXPOSEe -puma porta, o serviço no contêiner estiver acessível de qualquer lugar, mesmo fora do Docker.

A razão pela qual ambos são separados é IMHO porque:

  • a escolha de uma porta do host depende do host e, portanto, não pertence ao Dockerfile (caso contrário, isso dependeria do host),
  • e geralmente é suficiente se um serviço em um contêiner estiver acessível a partir de outros contêineres.

A documentação declara explicitamente:

A EXPOSEinstrução expõe portas para uso em links.

Também mostra como vincular contêineres , que basicamente é a comunicação entre contêineres sobre a qual falei.

PS: Se você faz -p, mas não faz EXPOSE, o Docker faz um implícito EXPOSE. Isso ocorre porque, se uma porta é aberta ao público, ela também é automaticamente aberta para outros contêineres do Docker. Daí -pinclui EXPOSE. Por isso não o listei acima como quarto caso.

Golo Roden
fonte
57
Eu acho que você não está correto com EXPOSE. De outros contêineres, você pode acessar todas as portas do contêiner sem expô-las. Eu tentei isso. O problema aqui é que o endereço IP do contêiner é imprevisível. Acredito que o link seja usado para especificar qual contêiner você deseja conectar (para vincular a um IP específico do contêiner), não para habilitar a conexão.
Jiri
7
"Se você não especificar qualquer um desses", seria útil se você esclareceu que com "aqueles" você quer dizer EXPOSEe -pe não os três pontos de bala precedentes. Me confundiu um pouco.
Pithikos 8/08/14
4
Para ser totalmente concluída, esta resposta também deve tratar de um quarto caso possível: você não especificou EXPOSE, mas você se especificar -p. Entendo que, se você sempre usa -pe executa contêineres individuais, não EXPOSEhá problema em omitir, mas isso se torna útil / necessário ao usar -Por --link. (E já que você não sabe como as outras pessoas vão usar sua imagem, EXPOSEdevem ser especificadas em quaisquer imagens públicas.)
GrandOpener
6
Os documentos não afirmam mais "A instrução EXPOSE expõe portas para uso em links".
Lorin Hochstein
11
Voto negativo, porque isso é substancialmente incorreto. Expor é basicamente documentação, e não usá-lo não restringe o acesso. Este é um mal-entendido perigoso se alguém confiar nele para limitar o acesso.
Mc0e 31/07
167

Resposta curta:

  • EXPOSEé uma maneira de documentar
  • --publish(ou -p) é uma maneira de mapear uma porta host para uma porta de contêiner em execução

Observe abaixo que:

  • EXPOSEestá relacionado a Dockerfiles( documentação )
  • --publishestá relacionado a docker run ...( execução / tempo de execução )

Expor e publicar portas

Nas redes do Docker, existem dois mecanismos diferentes que envolvem diretamente as portas de rede: expor e publicar portas. Isso se aplica à rede de pontes padrão e às redes de pontes definidas pelo usuário.

  • Você expõe portas usando a EXPOSEpalavra - chave no Dockerfile ou o --exposesinalizador para executar o Docker. A exposição de portas é uma maneira de documentar quais portas são usadas, mas na verdade não mapeia nem abre nenhuma porta . A exposição de portas é opcional.

  • Você publica portas usando o sinalizador --publishou --publish-allpara docker run. Isso informa ao Docker quais portas abrir na interface de rede do contêiner. Quando uma porta é publicada, ela é mapeada para uma porta de alta ordem disponível (maior que 30000) na máquina host, a menos que você especifique a porta a ser mapeada na máquina host em tempo de execução. Você não pode especificar a porta para a qual mapear na máquina host ao criar a imagem (no Dockerfile), porque não há como garantir que a porta estará disponível na máquina host em que você executa a imagem .

de: rede de contêineres do Docker

Atualização em outubro de 2019 : a parte do texto acima não está mais nos documentos, mas uma versão arquivada está aqui: docs.docker.com/v17.09/engine/userguide/networking/#exposing-and-publishing-ports

Talvez a documentação atual seja a seguinte:

Portas publicadas

Por padrão, quando você cria um contêiner, ele não publica nenhuma de suas portas no mundo externo. Para disponibilizar uma porta para serviços fora do Docker ou para contêineres do Docker que não estão conectados à rede do contêiner, use o sinalizador --publishou -p. Isso cria uma regra de firewall que mapeia uma porta de contêiner para uma porta no host do Docker.

e pode ser encontrado aqui: docs.docker.com/config/containers/container-networking/#published-ports

Além disso,

EXPOR

... A EXPOSEinstrução não publica realmente a porta . Ele funciona como um tipo de documentação entre a pessoa que cria a imagem e a pessoa que executa o contêiner, sobre quais portas devem ser publicadas.

de: referência do Dockerfile






Acesso ao serviço quando EXPOSE/ --publishnão está definido:

Na resposta de @Golo Roden, afirma-se que:

"Se você não especificar nenhum deles, o serviço no contêiner não poderá ser acessado de nenhum lugar, exceto de dentro do próprio contêiner."

Talvez tenha sido o caso no momento em que a resposta estava sendo escrita, mas agora parece que, mesmo se você não usar EXPOSEou --publish, a hostoutra containersrede da mesma rede poderá acessar um serviço que você pode iniciar dentro desse contêiner.

Como testar isso:

Eu usei o seguinte Dockerfile. Basicamente, eu começo com o ubuntu e instalo um pequeno servidor web:

FROM ubuntu
RUN apt-get update && apt-get install -y mini-httpd

Eu builda imagem como "testexpose" e runum novo contêiner com:

docker run --rm -it testexpose bash

Dentro do contêiner, inicio algumas instâncias de mini-httpd:

root@fb8f7dd1322d:/# mini_httpd -p 80
root@fb8f7dd1322d:/# mini_httpd -p 8080
root@fb8f7dd1322d:/# mini_httpd -p 8090

Sou capaz de usar a curlpartir do host ou de outros contêineres para buscar a home page do mini-httpd.

tgogos
fonte
16
agora é a resposta correta. resposta aceita é baseada em versões anteriores, ao que parece.
Luke W
qual é a porta do host usada para curl?
tempestade cerebral
Eu usei o IP do contêiner (algo como 172.17.0.2) e todas as portas que estou mencionando. Se você estiver usando o Docker para Mac / Windows, a rede será diferente. Não há docker0ponte.
Tgogos
ao publicar todas as portas EXPOSEd com o sinalizador "-P", como posso saber qual porta é usada no host?
Sixty4bit
1
@ sixty4bit dê uma olhada nesta pergunta: Como descobrir qual porta aleatória o Docker escolheu? .
Tgogos 5/07
9

Consulte a referência da documentação oficial: https://docs.docker.com/engine/reference/builder/#expose

Isso EXPOSEpermite que você defina portas privadas (contêiner) e públicas (host) para expor no momento da criação da imagem quando o contêiner estiver em execução, se você o executar -P.

$ docker help run
...
  -P, --publish-all                    Publish all exposed ports to random ports
...

A porta pública e o protocolo são opcionais; se nenhuma porta pública for especificada, uma porta aleatória será selecionada no host pela janela de encaixe para expor a porta do contêiner especificado no Dockerfile.

Uma boa prática é não especificar porta pública, porque limita apenas um contêiner por host (um segundo contêiner lançará uma porta já em uso).

Você pode usar -pem docker runcontrolar o que porto público dos portos de contentores expostos vai ser ligada.

De qualquer forma, se você não usar EXPOSE(com o -Pdocker run) nem -p, nenhuma porta será exposta.

Se você sempre usa -pem docker runvocê não precisa EXPOSE, mas se você usar EXPOSEo seu docker runcomando pode ser mais simples, EXPOSEpode ser útil se você não se importa em qual porta será expor no host, ou se você tem certeza de apenas um contêiner será carregado.

tonelada
fonte
isto está correto. Quando você tiver EXPOSE portNumber no Dockerfile, lembre-se de chamar o docker executado com -P.
KunYu Tsai
6

Você expõe portas usando a palavra-chave EXPOSE no Dockerfile ou o sinalizador --expose à execução do docker. Expor portas é uma maneira de documentar quais portas são usadas, mas na verdade não mapeia nem abre nenhuma porta. A exposição de portas é opcional.

Fonte: github commit

mzalazar
fonte
3

A maioria das pessoas usa o docker compor com redes. A documentação declara:

O recurso de rede do Docker suporta a criação de redes sem a necessidade de expor portas na rede; para obter informações detalhadas, consulte a visão geral desse recurso).

O que significa que, se você usa redes para comunicação entre contêineres, não precisa se preocupar em expor portas.

herm
fonte
-5

EXPOSE é usado para mapear a porta do contêiner de porta local, ou seja: se você especificar exposto no arquivo docker, como

EXPOSE 8090

O que será feito mapeará a porta 8090 do host local para a porta 8090 do contêiner

Mansur Ali
fonte