¿Cómo asigno una asignación de puertos a un contenedor Docker existente?

437

No estoy seguro de haber entendido mal algo aquí, pero parece que solo es posible establecer asignaciones de puertos creando un nuevo contenedor a partir de una imagen. ¿Hay alguna manera de asignar una asignación de puertos a un contenedor Docker existente?

thasmo
fuente
3
Usar iptables puede funcionar como esta respuesta Exponiendo un puerto en un Live Docker Container
Choldrim
2
Sospecho que esto es por diseño. Docker está tratando de forzarlo a ser "repetible" y el contenedor es un tipo de "sistema de registro". Cualquier cosa que haga como un paso que no afecte al contenedor sería un paso manual que se pierde fácilmente. Dicho de otra manera: desea que su contenedor represente toda la configuración necesaria para operar. Entonces, si desea abrir un nuevo puerto, debe crear un nuevo contenedor.
Lance Kind
2
Antigua pregunta y no la estoy respondiendo, pero me gustaría decir que tal vez usted y las personas que respondieron esta pregunta y sus respuestas pueden haber entendido completamente mal el concepto de acoplador. Los Docker son para aplicaciones sin estado, que pueden aumentar o disminuir la escala muchas veces. Nunca debe persistir algo dentro del contenedor para un entorno de producción que no pueda recrearse, si necesita persistir, asigne los directorios. Docker no es algo así como un "light vm", quizás lo que está buscando es linuxcontainers.org, lxd se basa en el concepto de docker pero con un "light vm" en mente.
Edgar Carvalho
en caso de que esto pueda ayudar, es posible usar la herramienta "Kitematic" para agregar la asignación de puertos a los contenedores que ya se están ejecutando. Esto debería implicar que debe haber un comando Docker para hacer exactamente lo mismo pero con un poco de google :) Buena suerte
Yaffah

Respuestas:

298

Puede cambiar la asignación de puertos editando directamente el hostconfig.jsonarchivo en /var/lib/docker/containers/[hash_of_the_container]/hostconfig.json

Puede determinar el [hash_of_the_container] mediante el docker inspect <container_name>comando y el valor del campo "Id" es el 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

Por lo tanto, no necesita crear una imagen con este enfoque. También puede cambiar la bandera de reinicio aquí.

PD: puede visitar https://docs.docker.com/engine/admin/ para obtener información sobre cómo reiniciar correctamente su motor Docker según su máquina host. Solía sudo systemctl restart dockerreiniciar mi motor Docker que se ejecuta en Ubuntu 16.04

holdfenytolvaj
fuente
17
Cuando la ventana acoplable se detiene, parece sobrescribir sus cambios, así que 2. pare la ventana acoplable, 3. cambie el archivo, 4. inicie el motor acoplable
Tacsiazuma el
55
He intentado lo anterior y funciona. Para más detalles ver: mybrainimage.wordpress.com/2017/02/05/…
rohitmohta
11
Es importante detener el contenedor, detener el motor acoplable y cambiar ambos hostconfig.jsony config.v2.jsonhacer que esto funcione. Use el enlace proporcionado por @rohitmohta para ver los detalles.
Kalpak Gadre
55
funcionó para mí, solo una cosa si usa la aplicación Docker en Mac, siga las instrucciones aquí para llegar a la carpeta / var / lib / docker / container: stackoverflow.com/a/41226917/2048266 , básicamente ejecute screen ~/Library/Containers/com.docker.docker/Data/com.docker.driver.amd64-linux/ttyUna vez que ejecute el tty puede navegar a / var / lib / docker
nommer
14
para Windows, ¿alguien puede compartirme la ubicación de la carpeta de contenedores?
Vijay
463

También estoy interesado en este problema.

Como mencionó @Thasmo , los reenvíos de puertos se pueden especificar SOLO con el comando docker run(y docker create).
Otros comandos, docker startno tiene -popción ydocker port solo muestra los reenvíos actuales.

Para agregar reenvíos de puertos, siempre sigo estos pasos,

  1. dejar de correr contenedor

    docker stop test01
    
  2. comprometer el contenedor

    docker commit test01 test02
    

    NOTA: Lo anterior, test02es una nueva imagen que estoy construyendo desde el test01contenedor.

  3. volver a ejecutar desde la imagen comprometida

    docker run -p 8080:8080 -td test02
    

Donde el primer 8080 es el puerto local y el segundo 8080 es el puerto de contenedores.

Fujimoto Youichi
fuente
15
¿Qué pasa si quiero mantener el nombre de test01?
user69715
12
¿Alguien sabe si hay un problema abierto con Docker para permitir la especificación del puerto (--publish) docker start?
Elijah Lynn
8
¿Y qué pasa con los volúmenes en este escenario?
Andrew Savinykh
78
Esta es una solución terrible, no tengo idea de cómo logró ganar 250 votos a favor. Tal vez aquellos que votaron por el voto no sabían qué tipo de desorden causa esta solución. Sí, es terrible, y equivale a iniciar un nuevo contenedor que se ejecuta en un puerto diferente.
Arrrr
25
@Arrrr ¿Quizás le gustaría dejar una mejor respuesta? Estoy seguro de que todos apreciaríamos que nos dijeras la mejor manera de hacerlo.
crockeea
40

Si por "existente" te refieres a "en ejecución", entonces (actualmente) no es posible agregar una asignación de puertos.

Sin embargo, puede agregar dinámicamente una nueva interfaz de red con, por ejemplo , Pipework , si necesita exponer un servicio en un contenedor en ejecución sin detenerlo / reiniciarlo.

jpetazzo
fuente
44
Esta debería ser la mejor respuesta. ¡Breve y aborda la pregunta de OP que ninguno de los otros hace! ¡A veces un resultado negativo es un resultado!
Parcialmente nublado
19

No estoy seguro si puede aplicar la asignación de puertos a un contenedor en ejecución. Puede aplicar el reenvío de puertos mientras ejecuta un contenedor que es diferente de crear un nuevo contenedor.

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

comenzará a ejecutar el contenedor. Este tutorial explica la redirección de puertos.

rajalokan
fuente
2
Sí, por lo que parece que solo es posible establecer opciones como la asignación de puertos en la creación del contenedor.
thasmo
20
FYI esta respuesta no es del todo correcta. docker runcrea e inicia un nuevo contenedor. Es equivalente a hacer docker createseguido de docker start.
Trevor Sullivan el
19

Editar hostconfig.json parece no funcionar ahora. Solo termina con ese puerto expuesto pero no publicado en el host. Comprometer y recrear contenedores no es el mejor enfoque para mí. Nadie mencionó docker network?

La mejor solución sería usar proxy inverso dentro de la misma red

  1. Cree una nueva red si su contenedor anterior no está en ninguno.

    docker network create my_network

  2. Une tu contenedor existente a la red creada

    docker network connect my_network my_existing_container

  3. Inicie un servicio de proxy inverso (por ejemplo, nginx) publicando los puertos que necesita, uniéndose a la misma red

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

    Opcionalmente, elimine default.conf en nginx

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

  4. Crear una nueva configuración 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 la configuración al contenedor nginx.

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

  5. Reiniciar nginx

    docker restart nginx

Ventajas : para publicar nuevos puertos, puede detener / actualizar / recrear de forma segura el contenedor nginx como lo desee sin tocar el contenedor empresarial. Si necesita cero tiempo de inactividad para nginx, es posible agregar más servicios proxy invertidos uniéndose a la misma red. Además, un contenedor puede unirse a más de una red.

Editar:

Para invertir los servicios proxy que no son http, el archivo de configuración es un poco diferente. Aquí hay un ejemplo simple:

upstream my_service {
    server my_existing_container:9000;
}

server {
    listen 9000;
    proxy_pass my_service;
}
Sean C.
fuente
1
Es sorprendente y práctico, pero para los sistemas empresariales este enfoque parece ofuscar. Es mucho mejor dejar que un solo sistema controle el flujo de trabajo.
Afshin
1
@Afshin Bueno, para sistemas o proyectos empresariales, creo que esta solución es mejor que recrear (causa tiempo de inactividad) o piratear el archivo hostconfig.json (al menos no se ha presentado oficialmente). El contenedor adicional solo expone el puerto interno del contenedor de su empresa, en lugar de realizar cambios en él.
Sean C.
1
Enfoque impresionante. Necesitaba configurar nginx de manera diferente para que mi contenedor funcione detrás de un proxy, pero parece la forma correcta de hacer las cosas. Funciona también para la implementación azul-verde.
Brooks DuBois
18

En Fujimoto, el ejemplo de Youichi test01es un contenedor, mientras que test02es una imagen.

Antes de hacerlo docker run, puede eliminar el contenedor original y luego asignarle nuevamente el mismo nombre:

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

(Utilizando -Ppara exponer puertos a puertos aleatorios en lugar de asignarlos manualmente).

Luca
fuente
12
Por favor tenga cuidado. PERDERÁ todos sus datos, dependiendo de la aplicación que contenga.
Barry
14

Si lo ejecuta docker run <NAME>, generará una nueva imagen, que probablemente no sea lo que desea.

Si desea cambiar una imagen actual, haga lo siguiente:

docker ps -a

Tome la identificación de su contenedor de destino y vaya a:

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

Detener el contenedor:

docker stop <NAME>

Cambiar los archivos

vi config.v2.json

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

Y cambiar archivo

vi hostconfig.json

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

Reinicia tu docker y debería funcionar.

docker-noob
fuente
2
Esto no funcionó para mí en Docker versión 17.09.0-ce. Después de que comencé, los archivos de configuración del contenedor se sobrescribieron a los valores anteriores.
thegeko
3
reiniciar el servicio acoplable en el sistema host @thegeko
yurenchen
1
¡esto funciona! tks! 1. detener el contenedor, 2. cambiar archivos, 3. reiniciar la ventana acoplable, 4. reiniciar el contenedor
datdinhquoc
12

A la inversa, si no se siente cómodo con la configuración de profundidad de Docker, IPtables sería su 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 es solo un truco, no una forma recomendada, funciona con mi escenario porque no pude detener el contenedor, espero que también te ayude.

Mansur Ali
fuente
Esta es una respuesta genial ! ¡Gracias! Si quiero asignar DOCKER_PORTa MACHINE_PORTqué partes debe ser cambiado?
Ciprian Tomoiagă
Tenga en cuenta que Docker no sabrá acerca de esta adición manual. Las entradas de SO no se eliminan cuando más tarde reinicia el servicio con la ventana acoplable exponiendo los puertos correctamente. Entonces, cuando algo cambie, asegúrese de verificar iptables con mucho cuidado. ¡Especialmente busca entradas duplicadas!
Anthony
8

Podemos usar herramientas útiles como ssh para lograr esto fácilmente.

Estaba usando el host ubuntu y la imagen de docker basada en ubuntu.

  1. La ventana acoplable interna tiene el cliente openssh instalado.
  2. El acoplador externo (host) tiene un servidor openssh-server instalado.

cuando se necesita un nuevo puerto para mapear,

dentro del docker ejecuta el siguiente comando

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

172.17.0.1 era la ip de la interfaz del acoplador (puede obtener esto ejecutándose ifconfig docker0 | grep "inet addr" | cut -f2 -d":" | cut -f1 -d" "en el host).

aquí tenía el puerto 8888 local asignado de nuevo a los hosts 8888. puede cambiar el puerto según sea necesario.

si necesita un puerto más, puede eliminar el ssh y agregarle una línea más de -R con el nuevo puerto.

He probado esto con netcat.

Melchi
fuente
2
  1. Pare el motor del acoplador y ese contenedor.
  2. Ir al /var/lib/docker/containers/${container_id}directorio y editarhostconfig.json
  3. Edita PortBindings.HostPortque quieres el cambio.
  4. Arranque el motor acoplable y el contenedor.
ezileli
fuente
-1

Para los usuarios de Windows y Mac, hay otra forma bastante fácil y amigable de cambiar el puerto de mapeo:

  1. descargar kitematic

  2. vaya a la página de configuración del contenedor, en la pestaña de puertos, puede modificar directamente el puerto publicado allí.

  3. iniciar el contenedor nuevamente

Juan
fuente
2
Intenté este enfoque. Kinematic aplicó las asignaciones de puertos, de hecho. Sin embargo, para aplicarlos, recreó mi contenedor a partir de la imagen original. Por lo tanto, si tiene miedo de perder los cambios realizados en el contenedor, no use este método.
VeganHunter
1
Preferí esto, entiendo que no responde la pregunta y crea un nuevo contenedor. Pero al menos funciona, y este resultado SO apareció durante mi búsqueda. +1
2b77bee6-5445-4c77-b1eb-4df3e5
-7

Respuesta corta: no puede asignar una asignación de puerto a un contenedor Docker existente

Necesitas un nuevo contenedor ... lidiar con él.

Stephen
fuente
44
No hay necesidad de la actitud. Además, esta respuesta carece por completo de detalles.
lovefaithswing
1
No hay necesidad de los detalles. El tono es crucial para transmitir esta respuesta que ahorra tiempo y la naturaleza efímera de los contenedores.
Stephen
-11

Si simplemente desea cambiar el puerto del contenedor en ejecución, haga lo siguiente:

  1. detener el contenedor existente

    sudo docker stop NAME

  2. ahora reinicie con el nuevo mapeo de puertos

    sudo docker run -d -p 81:80 NOMBRE

donde como:

"-d" al fondo / deamon el docker

"-p" habilita la asignación de puertos

Puerto externo (expuesto) "81" que utiliza para acceder con su navegador

Puerto de escucha del contenedor interno de acoplamiento "80"

Vlad Gulin
fuente
44
Esto es simplemente incorrecto. En el docker runcomando, NAMEes el nombre de la imagen para realizar un contenedor de, mientras que en docker stopel NAMEse refiere al nombre del contenedor de la parada.
jonatan
1
"docker run" creará un nuevo contenedor. No puede usar el mismo nombre de contenedor que el que se detuvo porque solo puede tener un contenedor con ese nombre. Por lo tanto, debe hacer: "docker stop NAME", docker container rm NAME, luego "docker run -d -p 81:80 NAME" tal como lo escribió.
Lance Kind