No estoy seguro sobre el "host de control", pero recientemente estuve en una charla de científicos de datos que están usando Docker para ejecutar scripts para procesar cargas de trabajo enormes (usando GPU montadas en AWS) y enviar el resultado al host. Un caso de uso muy interesante. Esencialmente scripts empaquetados con un entorno de ejecución confiable gracias a Docker
KCD
@KCD ¿Y por qué prefieren la contenedorización de aplicaciones a través de Docker en lugar de utilizar contenedores a nivel de sistema (LXC)?
Alex Ushakov
Respuestas:
28
¡Eso REALMENTE depende de lo que necesites que haga ese script bash!
Por ejemplo, si el script bash solo hace eco de alguna salida, puede hacer
docker run --rm -v $(pwd)/mybashscript.sh:/mybashscript.sh ubuntu bash /mybashscript.sh
Otra posibilidad es que desee que el script bash instale algún software, por ejemplo, el script para instalar docker-compose. podrías hacer algo como
Pero en este punto, realmente está empezando a tener que saber íntimamente qué está haciendo el script para permitir los permisos específicos que necesita en su host desde el interior del contenedor.
Tuve la idea de hacer un contenedor que se conecta al host y crea nuevos contenedores.
Alex Ushakov
1
A Docker no parece gustarle tu montura relativa. Esto debería funcionardocker run --rm -v $(pwd)/mybashscript.sh:/work/mybashscript.sh ubuntu /work/mybashscript.sh
KCD
5
La primera línea inicia un nuevo contenedor de ubuntu y monta el script donde se puede leer. No permite que el contenedor acceda al sistema de archivos del host, por ejemplo. La segunda línea expone al anfitrión /usr/binal contenedor. En ningún caso, el contenedor tiene acceso completo al sistema host. Tal vez me equivoque, pero parece una mala respuesta a una mala pregunta.
Paul
3
Muy bien, la pregunta era bastante vaga. La pregunta no pedía "acceso completo al sistema host". Como se describió, si el script bash solo tiene la intención de hacer eco de alguna salida, no NECESITA ningún acceso al sistema de archivos del host. Para mi segundo ejemplo, que fue instalar docker-compose, el único permiso que necesita es acceder al directorio bin donde se almacena el binario. Como dije al principio, para hacer esto, tendría que tener ideas muy específicas sobre lo que está haciendo el script para permitir los permisos correctos.
Paul Becotte
1
Intenté esto, el script se ejecuta en el contenedor, no en el host
All2Pie
60
La solución que uso es conectarme al host SSHy ejecutar el comando de esta manera:
ssh -l ${USERNAME} ${HOSTNAME} "${SCRIPT}"
ACTUALIZAR
Como esta respuesta sigue recibiendo votos, me gustaría recordar (y lo recomiendo encarecidamente) que la cuenta que se está utilizando para invocar el script debe ser una cuenta sin ningún permiso, pero solo ejecutando ese script como sudo(eso puede ser hecho desde sudoersarchivo).
Como otra solución, el contenedor podría generar un conjunto de comandos y el host podría ejecutarlos después de que el contenedor salga: eval $ (docker run --rm -it container_name_to_output script)
parity3
Necesito ejecutar una línea de comando en el Host desde dentro de un contenedor Docker, pero cuando entro al contenedor, sshno se encuentra. ¿Tiene alguna otra sugerencia?
Ron Rosenfeld
@RonRosenfeld, ¿qué imagen de Docker estás usando? en el caso de Debian / Ubuntu, ejecute lo siguiente: apt update && apt install openssh-client.
Mohammed Noureldin
Sería lo que esté instalado en mi Synology NAS. ¿Cómo puedo decir?
Ron Rosenfeld
@RonRosenfeld, lo siento, no entiendo lo que quieres decir
Mohammed Noureldin
52
Usó una tubería con nombre. En el sistema operativo host, cree un script para ejecutar y leer comandos, y luego llame a eval en eso.
Haga que el contenedor de la ventana acoplable lea en esa tubería con nombre.
Para poder acceder a la tubería, debe montarla a través de un volumen.
Esto es similar al mecanismo SSH (o un método basado en socket similar), pero lo restringe adecuadamente al dispositivo host, que probablemente sea mejor. Además, no tiene que pasar información de autenticación.
Mi única advertencia es que tenga cuidado con la razón por la que está haciendo esto. Es algo que debe hacer si desea crear un método para autoactualizarse con la entrada del usuario o lo que sea, pero probablemente no desee llamar a un comando para obtener algunos datos de configuración, ya que la forma correcta sería pasar eso como argumentos. / volume en la ventana acoplable. También tenga cuidado con el hecho de que está evaluando, así que solo piense en el modelo de permisos.
Algunas de las otras respuestas, como ejecutar un script. Bajo un volumen, no funcionarán de forma genérica ya que no tendrán acceso a todos los recursos del sistema, pero podría ser más apropiado según su uso.
ATENCIÓN: Esta es la respuesta correcta / mejor, y necesita un poco más de elogio. Todas las demás respuestas son jugar con preguntar "qué estás tratando de hacer" y hacer excepciones para algunas cosas. Tengo un caso de uso muy específico que requiere que pueda hacer esto, y esta es la única buena respuesta en mi humilde opinión. SSH anterior requeriría reducir los estándares de seguridad / firewall, y la función de ejecución de la ventana acoplable es simplemente incorrecta. Gracias por esto. Supongo que esto no recibe tantos votos positivos porque no es una respuesta simple de copiar / pegar, pero esta es la respuesta. +100 puntos míos si pudiera
Farley
3
Para aquellos que buscan más información, pueden usar el siguiente script que se ejecuta en la máquina host: unix.stackexchange.com/a/369465 Por supuesto, tendrá que ejecutarlo con 'nohup' y crear algún tipo de envoltorio supervisor para mantenerlo vivo (tal vez use un trabajo cron: P)
sucotronic
7
Esta puede ser una buena respuesta. Sin embargo, sería mucho mejor si proporcionara más detalles y una explicación más detallada de la línea de comandos. ¿Es posible elaborar?
Mohammed Noureldin
5
Upvoted, esto funciona! Cree una tubería con nombre usando 'mkfifo host_executor_queue' donde se monta el volumen. Luego, para agregar un consumidor que ejecute comandos que se colocan en la cola como shell del host, use 'tail -f host_executor_queue | sh & '. El & al final hace que se ejecute en segundo plano. Finalmente, para enviar comandos a la cola, use 'echo touch foo> host_executor_queue'; esta prueba crea un archivo temporal foo en el directorio de inicio. Si desea que el consumidor se inicie al iniciar el sistema, coloque '@reboot tail -f host_executor_queue | sh & 'en crontab. Simplemente agregue la ruta relativa a host_executor_queue.
Skybunk
1
¿Alguien podría editar la respuesta con un código de ejemplo?
Lucas Pottersky
6
Si no le preocupa la seguridad y simplemente está buscando iniciar un contenedor de la ventana acoplable en el host desde dentro de otro contenedor de la ventana acoplable como el OP, puede compartir el servidor de la ventana acoplable que se ejecuta en el host con el contenedor de la ventana acoplable compartiendo su socket de escucha.
Cuando ejecuta el comando de inicio de docker dentro de su contenedor de docker, el servidor de docker que se ejecuta en su host verá la solicitud y aprovisionará el contenedor hermano.
Tenga en cuenta que la ventana acoplable debe estar instalada en el contenedor; de lo contrario, también deberá montar un volumen para el binario de la ventana acoplable (p /usr/bin/docker:/usr/bin/docker. Ej .).
Gerry
1
Tenga cuidado al montar el conector de la ventana acoplable en su contenedor, esto podría ser un problema de seguridad grave: docs.docker.com/engine/security/security/…
DatGuyKaj
@DatGuyKaj gracias, he editado mi respuesta para reflejar los problemas descritos en su recurso.
Matt Bucci
Esto no responde a la pregunta, que se trata de ejecutar un script en el host, no en un contenedor
Brandon
5
Esta respuesta es solo una versión más detallada de la solución de Bradford Medeiros , que para mí también resultó ser la mejor respuesta, por lo que el mérito es de él.
En su respuesta, explica QUÉ hacer ( tuberías con nombre ) pero no exactamente CÓMO hacerlo.
Tengo que admitir que no sabía qué se llaman tuberías en el momento en que leí su solución. Así que luché para implementarlo (aunque en realidad es realmente simple), pero tuve éxito, así que estoy feliz de ayudar explicando cómo lo hice. Entonces, el punto de mi respuesta es solo detallar los comandos que necesita ejecutar para que funcione, pero nuevamente, el crédito es para él.
PARTE 1: Prueba del concepto de tubería con nombre sin Docker
En el host principal, elija la carpeta donde desea colocar su archivo de /path/to/pipe/tubería con nombre, por ejemplo mypipe, y un nombre de tubería, por ejemplo , y luego ejecute:
mkfifo /path/to/pipe/mypipe
Se crea la tubería. Tipo
ls -l /path/to/pipe/mypipe
Y compruebe que los derechos de acceso comienzan con "p", como
prw-r--r-- 1 root root 0 mypipe
Ahora ejecuta:
tail -f /path/to/pipe/mypipe
El terminal ahora está esperando que se envíen datos a esta tubería
Ahora abra otra ventana de terminal.
Y luego ejecuta:
echo "hello world" > /path/to/pipe/mypipe
Verifique la primera terminal (la que tiene tail -f), debería mostrar "hola mundo"
PARTE 2 - Ejecutar comandos a través de la tubería
En el contenedor del host, en lugar de ejecutar, tail -fque solo genera lo que se envía como entrada, ejecute este comando que lo ejecutará como comandos:
eval "$(cat /path/to/pipe/mypipe)"
Luego, desde la otra terminal, intente ejecutar:
echo "ls -l" > /path/to/pipe/mypipe
Regrese a la primera terminal y debería ver el resultado del ls -lcomando.
PARTE 3 - Haz que escuche para siempre
Es posible que haya notado que en la parte anterior, justo después de ls -lque se muestra la salida, deja de escuchar los comandos.
En lugar de eval "$(cat /path/to/pipe/mypipe)", ejecuta:
while true; do eval "$(cat /path/to/pipe/mypipe)"; done
(no puedes hacer eso)
Ahora puede enviar un número ilimitado de comandos uno tras otro, todos se ejecutarán, no solo el primero.
PARTE 4: haz que funcione incluso cuando se reinicia
La única advertencia es que si el host tiene que reiniciarse, el ciclo "while" dejará de funcionar.
Para manejar el reinicio, esto es lo que hice:
Ponlo while true; do eval "$(cat /path/to/pipe/mypipe)"; doneen un archivo llamado execpipe.shcon #!/bin/bashencabezado
No se olvide de chmod +xella
Agréguelo a crontab ejecutando
crontab -e
Y luego agregando
@reboot /path/to/execpipe.sh
En este punto, pruébelo: reinicie su servidor, y cuando esté respaldado, repita algunos comandos en la tubería y verifique si están ejecutados. Por supuesto, no puede ver el resultado de los comandos, por ls -llo que no ayudará, perotouch somefile ayudará.
Otra opción es modificar el script para colocar la salida en un archivo, como por ejemplo:
while true; do eval "$(cat /path/to/pipe/mypipe)" &> /somepath/output.txt; done
Ahora puede ejecutar ls -ly la salida (stdout y stderr usando&> en bash) debería estar en output.txt.
PARTE 5 - Haz que funcione con Docker
Si está utilizando docker compose y dockerfile como yo, esto es lo que hice:
Supongamos que desea montar la carpeta principal de mypipe como /hostpipeen su contenedor
Agrega esto:
VOLUME /hostpipe
en su dockerfile para crear un punto de montaje
Luego agrega esto:
volumes:
- /path/to/pipe:/hostpipe
en su docker, componga el archivo para montar / ruta / a / tubería como / hostpipe
Reinicie sus contenedores Docker.
PARTE 6 - Prueba
Ejecutar en su contenedor Docker:
docker exec -it <container> bash
Vaya a la carpeta de montaje y compruebe que puede ver la tubería:
cd /hostpipe && ls -l
Ahora intente ejecutar un comando desde dentro del contenedor:
ADVERTENCIA: Si tiene un host OSX (Mac OS) y un contenedor de Linux, no funcionará (explicación aquí https://stackoverflow.com/a/43474708/10018801 y problema aquí https://github.com/docker / for-mac / issues / 483 ) porque la implementación de la tubería no es la misma, por lo que lo que escribe en la tubería desde Linux solo puede ser leído por Linux y lo que escribe en la tubería desde Mac OS solo puede ser leído por un Mac OS (esta frase puede no ser muy precisa, pero tenga en cuenta que existe un problema multiplataforma).
Por ejemplo, cuando ejecuto la configuración de mi ventana acoplable en DEV desde mi computadora Mac OS, la tubería nombrada como se explicó anteriormente no funciona. Pero en la puesta en escena y la producción, tengo un host Linux y contenedores Linux, y funciona perfectamente.
PARTE 7 - Ejemplo del contenedor Node.JS
Así es como envío un comando desde mi contenedor node js al host principal y recupero el resultado:
const pipePath = "/hostpipe/mypipe"
const outputPath = "/hostpipe/output.txt"
const commandToRun = "pwd && ls-l"
console.log("delete previous output")
if (fs.existsSync(outputPath)) fs.unlinkSync(outputPath)
console.log("writing to pipe...")
const wstream = fs.createWriteStream(pipePath)
wstream.write(commandToRun)
wstream.close()
console.log("waiting for output.txt...") //there are better ways to do that than setInterval
let timeout = 10000 //stop waiting after 10 seconds (something might be wrong)
const timeoutStart = Date.now()
const myLoop = setInterval(function () {
if (Date.now() - timeoutStart > timeout) {
clearInterval(myLoop);
console.log("timed out")
} else {
//if output.txt exists, read it
if (fs.existsSync(outputPath)) {
clearInterval(myLoop);
const data = fs.readFileSync(outputPath).toString()
if (fs.existsSync(outputPath)) fs.unlinkSync(outputPath) //delete the output file
console.log(data) //log the output of the command
}
}
}, 300);
Esto funciona muy bien. ¿Y la seguridad? ¿Quiero usar esto para iniciar / detener contenedores Docker desde dentro de un contenedor en ejecución? ¿Simplemente hago un dockeruser sin ningún privilegio, excepto para ejecutar comandos de docker?
Kristof van Woensel
4
Escriba un servidor python simple escuchando en un puerto (digamos 8080), vincule el puerto -p 8080: 8080 con el contenedor, haga una solicitud HTTP a localhost: 8080 para pedirle al servidor Python que ejecute scripts de shell con popen, ejecute un curl o escribiendo código para hacer una solicitud HTTP curl -d '{"foo": "bar"}' localhost: 8080
#!/usr/bin/pythonfrom BaseHTTPServer import BaseHTTPRequestHandler,HTTPServer
import subprocess
import json
PORT_NUMBER = 8080# This class will handles any incoming request from# the browser classmyHandler(BaseHTTPRequestHandler):defdo_POST(self):
content_len = int(self.headers.getheader('content-length'))
post_body = self.rfile.read(content_len)
self.send_response(200)
self.end_headers()
data = json.loads(post_body)
# Use the post data
cmd = "your shell cmd"
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
p_status = p.wait()
(output, err) = p.communicate()
print"Command output : ", output
print"Command exit status/return code : ", p_status
self.wfile.write(cmd + "\n")
returntry:
# Create a web server and define the handler to manage the# incoming request
server = HTTPServer(('', PORT_NUMBER), myHandler)
print'Started httpserver on port ' , PORT_NUMBER
# Wait forever for incoming http requests
server.serve_forever()
except KeyboardInterrupt:
print'^C received, shutting down the web server'
server.socket.close()
En mi opinión, esta es la mejor respuesta. La ejecución de comandos arbitrarios en la máquina host DEBE realizarse a través de algún tipo de API (por ejemplo, REST). Esta es la única forma en que se puede aplicar la seguridad y los procesos en ejecución pueden controlarse adecuadamente (por ejemplo, matar, manejar stdin, stdout, código de salida, etc.). Por supuesto, sería bonito si esta API pudiera ejecutarse dentro de Docker, pero personalmente no me importa ejecutarla directamente en el host.
barney765
2
Mi pereza me llevó a encontrar la solución más fácil que no se publicó como respuesta aquí.
Todo lo que necesita hacer para obtener un shell completo para su host Linux desde dentro de su contenedor Docker es:
docker run --privileged --pid=host -it alpine:3.8 \
nsenter -t 1 -m -u -n -i sh
Explicación:
--privileged: otorga permisos adicionales al contenedor, permite que el contenedor obtenga acceso a los dispositivos del host (/ dev)
--pid = host: permite que los contenedores usen el árbol de procesos del host de Docker (la máquina virtual en la que se ejecuta el demonio de Docker) Utilidad nsenter: permite ejecutar un proceso en espacios de nombres existentes (los bloques de construcción que proporcionan aislamiento a los contenedores)
nsenter (-t 1 -m -u -n -i sh) permite ejecutar el proceso sh en el mismo contexto de aislamiento que el proceso con PID 1. Todo el comando proporcionará un shell sh interactivo en la VM
Esta configuración tiene importantes implicaciones de seguridad y debe usarse con precauciones (si corresponde).
Si bien esta respuesta podría resolver la pregunta del OP, se sugiere que explique cómo funciona y por qué resuelve el problema. Esto ayuda a los nuevos desarrolladores a comprender lo que está sucediendo y cómo solucionar este y otros problemas similares por sí mismos. ¡Gracias por contribuir!
Caleb Kleveter
1
Tengo un enfoque simple.
Paso 1: Monte /var/run/docker.sock:/var/run/docker.sock (para que pueda ejecutar comandos de docker dentro de su contenedor)
Paso 2: Ejecute esto a continuación dentro de su contenedor. La parte clave aquí es ( - host de red, ya que se ejecutará desde el contexto del host)
Según la documentación oficial de Docker sobre redes que utilizan la red de host, "Sin embargo, en todas las demás formas, como el almacenamiento, el espacio de nombres de procesos y el espacio de nombres de usuarios, el proceso está aislado del host". Salida - docs.docker.com/network/network-tutorial-host
Peter Mutisya
0
Como recuerda Marcus, la ventana acoplable es básicamente un proceso de aislamiento. A partir de la ventana acoplable 1.8, puede copiar archivos en ambos sentidos entre el host y el contenedor, consulte el documento dedocker cp
siempre puede, en el host, obtener el valor de alguna variable en su contenedor, algo así como myvalue=$(docker run -it ubuntu echo $PATH)y probarlo regularmente en un shell de script (por supuesto, usará algo más que $ PATH, solo es un ejemplo), cuando es un valor específico,
inicia
0
Puede usar el concepto de tubería, pero use un archivo en el host y fswatch para lograr el objetivo de ejecutar un script en la máquina host desde un contenedor docker. Así (use bajo su propio riesgo):
#! /bin/bash
touch .command_pipe
chmod +x .command_pipe
# Use fswatch to execute a command on the host machine and log result
fswatch -o --event Updated .command_pipe | \
xargs -n1 -I "{}" .command_pipe >> .command_pipe_log &
docker run -it --rm \
--name alpine \
-w /home/test \
-v $PWD/.command_pipe:/dev/command_pipe \
alpine:3.7 sh
rm -rf .command_pipe
kill %1
En este ejemplo, dentro del contenedor, envíe comandos a / dev / command_pipe, así:
La idea del aislamiento es poder restringir lo que una aplicación / proceso / contenedor (cualquiera que sea su ángulo en esto) puede hacer al sistema host de manera muy clara. Por lo tanto, poder copiar y ejecutar un archivo realmente rompería todo el concepto.
Si. Pero a veces es necesario.
No. Ese no es el caso, o Docker no es lo correcto para usar. Lo que debe hacer es declarar una interfaz clara para lo que desea hacer (por ejemplo, actualizar una configuración de host) y escribir un cliente / servidor mínimo para hacer exactamente eso y nada más. Generalmente, sin embargo, esto no parece ser muy deseable. En muchos casos, simplemente debe repensar su enfoque y erradicar esa necesidad. Docker nació cuando básicamente todo era un servicio al que se podía acceder mediante algún protocolo. No puedo pensar en ningún caso de uso adecuado de un contenedor Docker que obtenga los derechos para ejecutar cosas arbitrarias en el host.
Tengo un caso de uso: tengo un servicio dockerizado A(src en github). En el Arepositorio, creo los ganchos adecuados que, después del comando 'git pull', crean una nueva imagen de la ventana acoplable y los ejecutan (y eliminan el contenedor antiguo, por supuesto). Siguiente: github tiene web-hooks que permiten crear una solicitud POST a un enlace de punto final arbitrario después de presionar el maestro. Por lo tanto, no crearé el servicio B acoplado, que será ese punto final y que solo ejecutará 'git pull' en el repositorio A en la máquina HOST (importante: el comando 'git pull' debe ejecutarse en el entorno HOST, no en el entorno B porque B no se puede ejecutar un nuevo contenedor A dentro de B ...)
Kamil Kiełczewski
1
El problema: no quiero tener nada en HOST excepto linux, git y docker. Y quiero tener el servicio A y el servicio B de dockerizet (que de hecho es un controlador de git-push que ejecuta git pull en el repositorio A después de que alguien haga git push en el maestro). Así que la implementación automática de git es un caso de uso problemático
Kamil Kiełczewski
@ KamilKiełczewski Estoy tratando de hacer exactamente lo mismo, ¿has encontrado una solución?
user871784
1
Decir "No, ese no es el caso" es una actitud estrecha y supone que conoce todos los casos de uso del mundo. Nuestro caso de uso está ejecutando pruebas. Deben ejecutarse en contenedores para probar correctamente el entorno, pero dada la naturaleza de las pruebas, también deben ejecutar scripts en el host.
Senica Gonzalez
1
Solo para aquellos que se preguntan por qué dejo una respuesta de -7: a) está bien ser falible. Estaba equivocado. Está bien que esto esté documentado aquí. b) Los comentarios realmente aportan valor; eliminar la respuesta los eliminaría también. c) Aún aporta un punto de vista que sería prudente considerar (no rompa su aislamiento si no es necesario. Sin embargo, a veces es necesario).
Respuestas:
¡Eso REALMENTE depende de lo que necesites que haga ese script bash!
Por ejemplo, si el script bash solo hace eco de alguna salida, puede hacer
Otra posibilidad es que desee que el script bash instale algún software, por ejemplo, el script para instalar docker-compose. podrías hacer algo como
Pero en este punto, realmente está empezando a tener que saber íntimamente qué está haciendo el script para permitir los permisos específicos que necesita en su host desde el interior del contenedor.
fuente
docker run --rm -v $(pwd)/mybashscript.sh:/work/mybashscript.sh ubuntu /work/mybashscript.sh
/usr/bin
al contenedor. En ningún caso, el contenedor tiene acceso completo al sistema host. Tal vez me equivoque, pero parece una mala respuesta a una mala pregunta.La solución que uso es conectarme al host
SSH
y ejecutar el comando de esta manera:ACTUALIZAR
Como esta respuesta sigue recibiendo votos, me gustaría recordar (y lo recomiendo encarecidamente) que la cuenta que se está utilizando para invocar el script debe ser una cuenta sin ningún permiso, pero solo ejecutando ese script como
sudo
(eso puede ser hecho desdesudoers
archivo).fuente
ssh
no se encuentra. ¿Tiene alguna otra sugerencia?apt update && apt install openssh-client
.Usó una tubería con nombre. En el sistema operativo host, cree un script para ejecutar y leer comandos, y luego llame a eval en eso.
Haga que el contenedor de la ventana acoplable lea en esa tubería con nombre.
Para poder acceder a la tubería, debe montarla a través de un volumen.
Esto es similar al mecanismo SSH (o un método basado en socket similar), pero lo restringe adecuadamente al dispositivo host, que probablemente sea mejor. Además, no tiene que pasar información de autenticación.
Mi única advertencia es que tenga cuidado con la razón por la que está haciendo esto. Es algo que debe hacer si desea crear un método para autoactualizarse con la entrada del usuario o lo que sea, pero probablemente no desee llamar a un comando para obtener algunos datos de configuración, ya que la forma correcta sería pasar eso como argumentos. / volume en la ventana acoplable. También tenga cuidado con el hecho de que está evaluando, así que solo piense en el modelo de permisos.
Algunas de las otras respuestas, como ejecutar un script. Bajo un volumen, no funcionarán de forma genérica ya que no tendrán acceso a todos los recursos del sistema, pero podría ser más apropiado según su uso.
fuente
Si no le preocupa la seguridad y simplemente está buscando iniciar un contenedor de la ventana acoplable en el host desde dentro de otro contenedor de la ventana acoplable como el OP, puede compartir el servidor de la ventana acoplable que se ejecuta en el host con el contenedor de la ventana acoplable compartiendo su socket de escucha.
Consulte https://docs.docker.com/engine/security/security/#docker-daemon-attack-surface y vea si su tolerancia al riesgo personal lo permite para esta aplicación en particular.
Puede hacer esto agregando los siguientes argumentos de volumen a su comando de inicio
o compartiendo /var/run/docker.sock dentro de su docker componga un archivo como este:
Cuando ejecuta el comando de inicio de docker dentro de su contenedor de docker, el servidor de docker que se ejecuta en su host verá la solicitud y aprovisionará el contenedor hermano.
crédito: http://jpetazzo.github.io/2015/09/03/do-not-use-docker-in-docker-for-ci/
fuente
/usr/bin/docker:/usr/bin/docker
. Ej .).Esta respuesta es solo una versión más detallada de la solución de Bradford Medeiros , que para mí también resultó ser la mejor respuesta, por lo que el mérito es de él.
En su respuesta, explica QUÉ hacer ( tuberías con nombre ) pero no exactamente CÓMO hacerlo.
Tengo que admitir que no sabía qué se llaman tuberías en el momento en que leí su solución. Así que luché para implementarlo (aunque en realidad es realmente simple), pero tuve éxito, así que estoy feliz de ayudar explicando cómo lo hice. Entonces, el punto de mi respuesta es solo detallar los comandos que necesita ejecutar para que funcione, pero nuevamente, el crédito es para él.
PARTE 1: Prueba del concepto de tubería con nombre sin Docker
En el host principal, elija la carpeta donde desea colocar su archivo de
/path/to/pipe/
tubería con nombre, por ejemplomypipe
, y un nombre de tubería, por ejemplo , y luego ejecute:Se crea la tubería. Tipo
Y compruebe que los derechos de acceso comienzan con "p", como
Ahora ejecuta:
El terminal ahora está esperando que se envíen datos a esta tubería
Ahora abra otra ventana de terminal.
Y luego ejecuta:
Verifique la primera terminal (la que tiene
tail -f
), debería mostrar "hola mundo"PARTE 2 - Ejecutar comandos a través de la tubería
En el contenedor del host, en lugar de ejecutar,
tail -f
que solo genera lo que se envía como entrada, ejecute este comando que lo ejecutará como comandos:Luego, desde la otra terminal, intente ejecutar:
Regrese a la primera terminal y debería ver el resultado del
ls -l
comando.PARTE 3 - Haz que escuche para siempre
Es posible que haya notado que en la parte anterior, justo después de
ls -l
que se muestra la salida, deja de escuchar los comandos.En lugar de
eval "$(cat /path/to/pipe/mypipe)"
, ejecuta:(no puedes hacer eso)
Ahora puede enviar un número ilimitado de comandos uno tras otro, todos se ejecutarán, no solo el primero.
PARTE 4: haz que funcione incluso cuando se reinicia
La única advertencia es que si el host tiene que reiniciarse, el ciclo "while" dejará de funcionar.
Para manejar el reinicio, esto es lo que hice:
Ponlo
while true; do eval "$(cat /path/to/pipe/mypipe)"; done
en un archivo llamadoexecpipe.sh
con#!/bin/bash
encabezadoNo se olvide de
chmod +x
ellaAgréguelo a crontab ejecutando
Y luego agregando
En este punto, pruébelo: reinicie su servidor, y cuando esté respaldado, repita algunos comandos en la tubería y verifique si están ejecutados. Por supuesto, no puede ver el resultado de los comandos, por
ls -l
lo que no ayudará, perotouch somefile
ayudará.Otra opción es modificar el script para colocar la salida en un archivo, como por ejemplo:
Ahora puede ejecutar
ls -l
y la salida (stdout y stderr usando&>
en bash) debería estar en output.txt.PARTE 5 - Haz que funcione con Docker
Si está utilizando docker compose y dockerfile como yo, esto es lo que hice:
Supongamos que desea montar la carpeta principal de mypipe como
/hostpipe
en su contenedorAgrega esto:
en su dockerfile para crear un punto de montaje
Luego agrega esto:
en su docker, componga el archivo para montar / ruta / a / tubería como / hostpipe
Reinicie sus contenedores Docker.
PARTE 6 - Prueba
Ejecutar en su contenedor Docker:
Vaya a la carpeta de montaje y compruebe que puede ver la tubería:
Ahora intente ejecutar un comando desde dentro del contenedor:
¡Y debería funcionar!
ADVERTENCIA: Si tiene un host OSX (Mac OS) y un contenedor de Linux, no funcionará (explicación aquí https://stackoverflow.com/a/43474708/10018801 y problema aquí https://github.com/docker / for-mac / issues / 483 ) porque la implementación de la tubería no es la misma, por lo que lo que escribe en la tubería desde Linux solo puede ser leído por Linux y lo que escribe en la tubería desde Mac OS solo puede ser leído por un Mac OS (esta frase puede no ser muy precisa, pero tenga en cuenta que existe un problema multiplataforma).
Por ejemplo, cuando ejecuto la configuración de mi ventana acoplable en DEV desde mi computadora Mac OS, la tubería nombrada como se explicó anteriormente no funciona. Pero en la puesta en escena y la producción, tengo un host Linux y contenedores Linux, y funciona perfectamente.
PARTE 7 - Ejemplo del contenedor Node.JS
Así es como envío un comando desde mi contenedor node js al host principal y recupero el resultado:
fuente
Escriba un servidor python simple escuchando en un puerto (digamos 8080), vincule el puerto -p 8080: 8080 con el contenedor, haga una solicitud HTTP a localhost: 8080 para pedirle al servidor Python que ejecute scripts de shell con popen, ejecute un curl o escribiendo código para hacer una solicitud HTTP curl -d '{"foo": "bar"}' localhost: 8080
#!/usr/bin/python from BaseHTTPServer import BaseHTTPRequestHandler,HTTPServer import subprocess import json PORT_NUMBER = 8080 # This class will handles any incoming request from # the browser class myHandler(BaseHTTPRequestHandler): def do_POST(self): content_len = int(self.headers.getheader('content-length')) post_body = self.rfile.read(content_len) self.send_response(200) self.end_headers() data = json.loads(post_body) # Use the post data cmd = "your shell cmd" p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True) p_status = p.wait() (output, err) = p.communicate() print "Command output : ", output print "Command exit status/return code : ", p_status self.wfile.write(cmd + "\n") return try: # Create a web server and define the handler to manage the # incoming request server = HTTPServer(('', PORT_NUMBER), myHandler) print 'Started httpserver on port ' , PORT_NUMBER # Wait forever for incoming http requests server.serve_forever() except KeyboardInterrupt: print '^C received, shutting down the web server' server.socket.close()
fuente
Mi pereza me llevó a encontrar la solución más fácil que no se publicó como respuesta aquí.
Está basado en el gran artículo de luc juggery .
Todo lo que necesita hacer para obtener un shell completo para su host Linux desde dentro de su contenedor Docker es:
Explicación:
--privileged: otorga permisos adicionales al contenedor, permite que el contenedor obtenga acceso a los dispositivos del host (/ dev)
--pid = host: permite que los contenedores usen el árbol de procesos del host de Docker (la máquina virtual en la que se ejecuta el demonio de Docker) Utilidad nsenter: permite ejecutar un proceso en espacios de nombres existentes (los bloques de construcción que proporcionan aislamiento a los contenedores)
nsenter (-t 1 -m -u -n -i sh) permite ejecutar el proceso sh en el mismo contexto de aislamiento que el proceso con PID 1. Todo el comando proporcionará un shell sh interactivo en la VM
Esta configuración tiene importantes implicaciones de seguridad y debe usarse con precauciones (si corresponde).
fuente
fuente
Tengo un enfoque simple.
Paso 1: Monte /var/run/docker.sock:/var/run/docker.sock (para que pueda ejecutar comandos de docker dentro de su contenedor)
Paso 2: Ejecute esto a continuación dentro de su contenedor. La parte clave aquí es ( - host de red, ya que se ejecutará desde el contexto del host)
test.sh debe contener algunos comandos (ifconfig, netstat, etc.) lo que necesite. Ahora podrá obtener la salida del contexto del host.
fuente
Como recuerda Marcus, la ventana acoplable es básicamente un proceso de aislamiento. A partir de la ventana acoplable 1.8, puede copiar archivos en ambos sentidos entre el host y el contenedor, consulte el documento de
docker cp
https://docs.docker.com/reference/commandline/cp/
Una vez que se copia un archivo, puede ejecutarlo localmente
fuente
myvalue=$(docker run -it ubuntu echo $PATH)
y probarlo regularmente en un shell de script (por supuesto, usará algo más que $ PATH, solo es un ejemplo), cuando es un valor específico,Puede usar el concepto de tubería, pero use un archivo en el host y fswatch para lograr el objetivo de ejecutar un script en la máquina host desde un contenedor docker. Así (use bajo su propio riesgo):
En este ejemplo, dentro del contenedor, envíe comandos a / dev / command_pipe, así:
En el host, puede verificar si se creó la red:
fuente
Para ampliar la respuesta del usuario 2915097 :
La idea del aislamiento es poder restringir lo que una aplicación / proceso / contenedor (cualquiera que sea su ángulo en esto) puede hacer al sistema host de manera muy clara. Por lo tanto, poder copiar y ejecutar un archivo realmente rompería todo el concepto.
No. Ese no es el caso, o Docker no es lo correcto para usar. Lo que debe hacer es declarar una interfaz clara para lo que desea hacer (por ejemplo, actualizar una configuración de host) y escribir un cliente / servidor mínimo para hacer exactamente eso y nada más. Generalmente, sin embargo, esto no parece ser muy deseable. En muchos casos, simplemente debe repensar su enfoque y erradicar esa necesidad. Docker nació cuando básicamente todo era un servicio al que se podía acceder mediante algún protocolo. No puedo pensar en ningún caso de uso adecuado de un contenedor Docker que obtenga los derechos para ejecutar cosas arbitrarias en el host.
fuente
A
(src en github). En elA
repositorio, creo los ganchos adecuados que, después del comando 'git pull', crean una nueva imagen de la ventana acoplable y los ejecutan (y eliminan el contenedor antiguo, por supuesto). Siguiente: github tiene web-hooks que permiten crear una solicitud POST a un enlace de punto final arbitrario después de presionar el maestro. Por lo tanto, no crearé el servicio B acoplado, que será ese punto final y que solo ejecutará 'git pull' en el repositorio A en la máquina HOST (importante: el comando 'git pull' debe ejecutarse en el entorno HOST, no en el entorno B porque B no se puede ejecutar un nuevo contenedor A dentro de B ...)