Como atribuir um mapeamento de porta a um contêiner do Docker existente?

436

Não sei se entendi algo errado aqui, mas parece que só é possível definir mapeamentos de portas criando um novo contêiner a partir de uma imagem. Existe uma maneira de atribuir um mapeamento de porta a um contêiner Docker existente?

thasmo
fonte
3
Usando iptables pode funcionar como esta resposta Expor uma porta em um Live Docker Container
Choldrim
2
Eu suspeito que isso é por design. O Docker está tentando forçá-lo a ser "repetível" e o contêiner é um tipo de "sistema de registro". Qualquer coisa que você faça como etapa que não afeta o contêiner seria uma etapa manual facilmente perdida. Disse de outra maneira: você deseja que seu contêiner represente toda a configuração necessária para operar. Portanto, se você deseja abrir uma nova porta, precisa criar um novo contêiner.
Lance Tipo
2
Pergunta antiga e não estou respondendo, mas gostaria de dizer que talvez você e as pessoas que aprovam essa pergunta e respostas possam ter entendido completamente o conceito de janela de encaixe. O Docker é para aplicativos sem estado, que pode aumentar ou diminuir várias vezes. Você nunca deve persistir em algo dentro do contêiner para um ambiente de produção que não possa ser recriado; se precisar persistir, mapeie os diretórios. O Docker não é algo como "light vm", talvez o que você esteja procurando seja linuxcontainers.org, lxd é baseado no conceito do docker, mas com uma "light vm" em mente.
Edgar Carvalho
Caso isso ajude, é possível usar a ferramenta "Kitematic" para adicionar mapeamento de porta a contêineres já em execução. Isso deve implicar que deve haver um comando docker para fazer exatamente a mesma coisa, mas com um pouco de pesquisa :) Boa sorte
Yaffah

Respostas:

298

Você pode alterar o mapeamento de portas editando diretamente o hostconfig.jsonarquivo em /var/lib/docker/containers/[hash_of_the_container]/hostconfig.json

Você pode determinar o [hash_of_the_container] através do docker inspect <container_name>comando e o valor do campo "Id" é o hash.

1) stop the container 
2) stop docker service (per Tacsiazuma's comment)
3) change the file
4) restart your docker engine (to flush/clear config caches)
5) start the container

Portanto, você não precisa criar uma imagem com essa abordagem. Você também pode alterar o sinalizador de reinicialização aqui.

PS Você pode visitar https://docs.docker.com/engine/admin/ para saber como reiniciar corretamente o mecanismo do docker de acordo com a máquina host. Eu costumava sudo systemctl restart dockerreiniciar o mecanismo do docker que está sendo executado no Ubuntu 16.04

holdfenytolvaj
fonte
17
Quando estivador pára, que parecem substituir as alterações, de modo 2. janela de encaixe stop, 3. arquivo de mudança, 4. partida do motor estivador
Tacsiazuma
5
Eu tentei o acima e funciona. Para mais detalhes veja: mybrainimage.wordpress.com/2017/02/05/...
rohitmohta
11
É importante parar o contêiner, interromper o mecanismo do docker e alterar os dois hostconfig.jsone config.v2.jsonfazer isso funcionar. Use o link fornecido por @rohitmohta para ver os detalhes.
Kalpak Gadre
5
funcionou para mim, apenas uma coisa se estiver usando o aplicativo docker no mac, siga as instruções aqui para chegar à pasta / var / lib / docker / containers: stackoverflow.com/a/41226917/2048266 , basicamente execute screen ~/Library/Containers/com.docker.docker/Data/com.docker.driver.amd64-linux/ttyQuando você obtiver o tty em execução, navegue para / var / lib / docker
nommer 27/07/18
14
para o windows, alguém pode me compartilhar a localização da pasta conatiners.?
Vijay
463

Também estou interessado neste problema.

Como o @Thasmo mencionou, o encaminhamento de porta pode ser especificado SOMENTE com o comando docker run(e docker create).
Outros comandos, docker startnão possui -popção edocker port exibe apenas os encaminhamentos atuais.

Para adicionar encaminhamentos de porta, eu sempre sigo estas etapas,

  1. parar de executar o contêiner

    docker stop test01
    
  2. confirmar o contêiner

    docker commit test01 test02
    

    NOTA: A descrição acima test02é uma nova imagem que estou construindo a partir do test01contêiner.

  3. re- correr a partir da imagem commited

    docker run -p 8080:8080 -td test02
    

Onde o primeiro 8080 é a porta local e o segundo 8080 é a porta do contêiner.

Fujimoto Youichi
fonte
15
E se eu quiser manter o nome test01?
user69715
12
Alguém sabe se existe um problema em aberto no Docker para permitir a especificação de porta (--publish) com docker start?
Elijah Lynn
8
E o que acontece com os volumes nesse cenário?
Andrew Savinykh
78
Esta é uma solução terrível, não tenho ideia de como conseguiu ganhar 250 votos positivos. Talvez aqueles que votaram mal não sabiam que tipo de confusão essa solução causa. Sim, é terrível e é igual a iniciar um novo contêiner em execução em uma porta diferente.
Arrrr
25
@Arrrr Talvez você queira deixar uma resposta melhor? Tenho certeza de que todos nós apreciaríamos se você nos dissesse a maneira muito melhor de fazer isso.
Crockeea 9/1018
40

Se por "existente" você quer dizer "em execução", não é possível (atualmente) adicionar um mapeamento de porta.

No entanto, você pode adicionar dinamicamente uma nova interface de rede com, por exemplo , Pipework , se precisar expor um serviço em um contêiner em execução sem parar / reiniciá-lo.

jpetazzo
fonte
4
Essa deve ser a resposta principal. Sucinto e aborda a questão do OP, que nenhum dos outros faz! Às vezes, um resultado negativo é um resultado!
Parcialmente nublado
19

Não tenho certeza se você pode aplicar o mapeamento de portas a um contêiner em execução. Você pode aplicar o encaminhamento de porta enquanto executa um contêiner diferente de criar um novo contêiner.

$ docker run -p <public_port>:<private_port> -d <image>  

começará a executar o contêiner. Este tutorial explica o redirecionamento de porta.

rajalokan
fonte
2
Sim, parece que só é possível definir opções como o mapeamento de portas na criação de contêineres.
thasmo
20
Para sua informação, esta resposta não está totalmente correta. docker runcria e inicia um novo contêiner. É equivalente a fazer docker createseguido por docker start.
Trevor Sullivan
19

A edição de hostconfig.json parece não estar funcionando agora. Ele termina apenas com essa porta sendo exposta, mas não publicada no host. Confirmar e recriar contêineres não é a melhor abordagem para mim. Ninguém mencionou docker network?

A melhor solução seria usar proxy reverso na mesma rede

  1. Crie uma nova rede se o contêiner anterior não estiver em nenhum nomeado.

    docker network create my_network

  2. Associe seu contêiner existente à rede criada

    docker network connect my_network my_existing_container

  3. Inicie um serviço de proxy reverso (por exemplo, nginx) publicando as portas necessárias, ingressando na mesma rede

    docker run -d --name nginx --network my_network -p 9000:9000 nginx

    Como opção, remova o default.conf no nginx

    docker exec nginx rm /etc/nginx/conf.d/default.conf

  4. Crie uma nova configuração nginx

    server
    {
        listen 9000;
    
        location / {
            proxy_pass http://my_existing_container:9000;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection 'upgrade';
            proxy_set_header Host $host;
            proxy_cache_bypass $http_upgrade;
        }
    }
    

    Copie a configuração para o contêiner nginx.

    docker cp ./my_conf.conf nginx:/etc/nginx/conf.d/my_conf.conf

  5. Reinicie o nginx

    docker restart nginx

Vantagens : Para publicar novas portas, você pode parar / atualizar / recriar com segurança o contêiner nginx como desejar, sem tocar no contêiner comercial. Se você precisar de um tempo de inatividade zero para o nginx, é possível adicionar mais serviços de proxy reverso ingressando na mesma rede. Além disso, um contêiner pode ingressar em mais de uma rede.

Editar:

Para inverter serviços proxy não-http, o arquivo de configuração é um pouco diferente. Aqui está um exemplo simples:

upstream my_service {
    server my_existing_container:9000;
}

server {
    listen 9000;
    proxy_pass my_service;
}
Sean C.
fonte
1
É incrível e prático, mas para sistemas corporativos essa abordagem parece ofuscante. É muito melhor deixar um único sistema controlar o fluxo de trabalho.
Afshin
1
@ Afshin Bem, para sistemas ou projetos corporativos, acho que essa solução é melhor do que recriar (causar tempo de inatividade) ou invadir o arquivo hostconfig.json (pelo menos não oficialmente apresentado). O contêiner extra apenas expõe a porta interna do contêiner comercial, em vez de fazer alterações nela.
22718 Sean C #
1
Abordagem impressionante. Eu precisava configurar o nginx de maneira diferente para que meu contêiner funcionasse atrás de um proxy, mas parece ser a maneira correta de fazer as coisas. Também funciona para implantação azul esverdeado.
Brooks DuBois
18

No exemplo de Fujimoto, Youichi test01é um contêiner, enquantotest02 é uma imagem.

Antes de fazer, docker runvocê pode remover o contêiner original e atribuir ao contêiner o mesmo nome novamente:

$ docker stop container01
$ docker commit container01 image01
$ docker rm container01
$ docker run -d -P --name container01 image01

(Utilizando -Ppara expor portas a portas aleatórias, em vez de atribuir manualmente).

Luca
fonte
12
Por favor, esteja ciente. você perderá todos os seus dados, dependendo do aplicativo interno.
Barry
14

Se você executar, docker run <NAME>ela gerará uma nova imagem, o que provavelmente não é o que você deseja.

Se você deseja alterar uma imagem atual, faça o seguinte:

docker ps -a

Pegue o ID do seu container de destino e vá para:

cd /var/lib/docker/containers/<conainerID><and then some:)>

Pare o recipiente:

docker stop <NAME>

Mude os arquivos

vi config.v2.json

"Config": {
    ....
    "ExposedPorts": {
        "80/tcp": {},
        "8888/tcp": {}
    },
    ....
},
"NetworkSettings": {
....
"Ports": {
     "80/tcp": [
         {
             "HostIp": "",
             "HostPort": "80"
         }
     ],

E mudar arquivo

vi hostconfig.json

"PortBindings": {
     "80/tcp": [
         {
             "HostIp": "",
             "HostPort": "80"
         }
     ],
     "8888/tcp": [
         {
             "HostIp": "",
             "HostPort": "8888"
         } 
     ]
 }

Reinicie sua janela de encaixe e ela deve funcionar.

docker-noob
fonte
2
Isso não funcionou para mim no Docker versão 17.09.0-ce. Após iniciar, os arquivos de configuração do contêiner foram substituídos de volta aos valores antigos.
thegeko
3
reinicie o serviço docker no sistema host @thegeko
yurenchen 17/10
1
isso funciona! tks! 1.stop container, 2.change files, 3.restart docker, 4.start back container
datdinhquoc
12

O contrário, se você não se sentir confortável com as tabelas de IP da configuração de profundidade do Docker, seria seu amigo.

iptables -t nat -A DOCKER -p tcp --dport ${YOURPORT} -j DNAT --to-destination ${CONTAINERIP}:${YOURPORT}

iptables -t nat -A POSTROUTING -j MASQUERADE -p tcp --source ${CONTAINERIP} --destination ${CONTAINERIP} --dport ${YOURPORT}

iptables -A DOCKER -j ACCEPT -p tcp --destination ${CONTAINERIP} --dport ${YOURPORT}

Este é apenas um truque, não uma maneira recomendada de como isso funciona com o meu cenário, porque eu não conseguia parar o contêiner, espero que também o ajude.

Mansur Ali
fonte
Esta é uma ótima resposta! Obrigado! Se eu quiser mapear DOCKER_PORTpara MACHINE_PORTque as peças devem ser alteradas?
Ciprian Tomoiagă
A janela de encaixe não saberá sobre esta adição manual. As entradas SO não são removidas quando você reinicia o serviço posteriormente, com a janela de encaixe expondo as portas corretamente. Portanto, quando algo mudar, verifique o iptables com muito cuidado. Procure especialmente entradas duplicadas!
anthony
8

usamos ferramentas úteis como ssh para fazer isso facilmente.

Eu estava usando o host do ubuntu e a imagem do docker baseada no ubuntu.

  1. A janela de encaixe interna possui o openssh-client instalado.
  2. A janela de encaixe externa (host) possui o servidor openssh-server instalado.

quando uma nova porta é necessária para ser mapeada,

dentro da janela de encaixe, execute o seguinte comando

ssh -R8888:localhost:8888 <username>@172.17.0.1

172.17.0.1 era o ip da interface do docker (você pode obtê-lo executando ifconfig docker0 | grep "inet addr" | cut -f2 -d":" | cut -f1 -d" "no host).

aqui eu tinha a porta 8888 local mapeada de volta para os hosts 8888. você pode alterar a porta conforme necessário.

se você precisar de mais uma porta, poderá matar o ssh e adicionar mais uma linha de -R a essa nova porta.

Eu testei isso com o netcat.

melchi
fonte
2
  1. Pare o mecanismo do docker e esse contêiner.
  2. Vá para o /var/lib/docker/containers/${container_id}diretório e editehostconfig.json
  3. Edite PortBindings.HostPortque você deseja a alteração.
  4. Inicie o mecanismo de encaixe e o contêiner.
ezileli
fonte
-1

Para usuários de Windows e Mac, existe outra maneira bastante fácil e amigável de alterar a porta de mapeamento:

  1. baixar kitematic

  2. vá para a página de configurações do contêiner, na guia portas, você pode modificar diretamente a porta publicada lá.

  3. inicie o contêiner novamente

John
fonte
2
Eu tentei essa abordagem. A Kinematic aplicou os mapeamentos de portas, de fato. No entanto, para aplicá-los, recriou meu contêiner a partir da imagem original. Portanto, se você tiver medo de perder as alterações feitas no próprio contêiner, não use esse método.
VeganHunter
1
Eu preferi isso, acho que ele não responde à pergunta e cria um novo contêiner. Mas pelo menos funciona e esse resultado apareceu durante minha pesquisa. 1
2b77bee6-5445-4c77-b1eb-4df3e5
-7

Resposta curta: Você não pode atribuir um mapeamento de porta a um contêiner Docker existente

Você precisa de um novo contêiner ... lide com isso.

Stephen
fonte
4
Não há necessidade de atitude. Além disso, esta resposta está completamente sem detalhes.
lovefaithswing 21/04
1
Não há necessidade de detalhes. O tom é crucial para transmitir essa resposta que economiza tempo e a natureza efêmera dos contêineres.
stephen
-11

Se você simplesmente deseja alterar a porta do contêiner em execução, faça:

  1. parar o contêiner existente

    sudo docker stop NAME

  2. agora reinicie com o novo mapeamento de porta

    sudo docker run -d -p 81:80 NAME

enquanto que:

"-d" para segundo plano / instalar a janela de encaixe

"-p" ativa o mapeamento de portas

Porta externa (exposta) "81" usada para acessar com o navegador

Porta de escuta do contêiner de docker interno "80"

Vlad Gulin
fonte
4
Isso está errado. No docker runcomando, NAMEé o nome da imagem a ser executado a partir de um recipiente, enquanto que na docker stopo NAMEse refere ao nome do recipiente para paragem.
Jonatan #:
1
"docker run" criará um novo contêiner. Ele não pode usar o mesmo nome de contêiner que foi interrompido porque você só pode ter um contêiner com esse nome. Portanto, você deve fazer: "docker stop NAME", docker container rm NAME e, em seguida, "docker run -d -p 81:80 NAME" conforme você o escreveu.
Lance Tipo