Não tenho certeza sobre "host de controle", mas recentemente estive em uma palestra de cientistas de dados que estão usando o docker para executar scripts para processar cargas de trabalho enormes (usando GPUs montadas em AWS) e enviar o resultado para o host. Um caso de uso muito interessante. Essencialmente, scripts empacotados com um ambiente de execução confiável graças ao docker
KCD de
@KCD E por que eles preferem a contentorização de aplicativos via docker em vez de usar contêineres de nível de sistema (LXC)?
Alex Ushakov
Respostas:
30
Isso realmente depende do que você precisa que o script bash faça!
Por exemplo, se o script bash apenas exibe alguma saída, você pode apenas fazer
docker run --rm -v $(pwd)/mybashscript.sh:/mybashscript.sh ubuntu bash /mybashscript.sh
Outra possibilidade é que você queira que o script bash instale algum software - digamos, o script para instalar o docker-compose. você poderia fazer algo como
Mas, neste ponto, você realmente precisa saber intimamente o que o script está fazendo para permitir as permissões específicas de que ele precisa em seu host de dentro do contêiner.
Tive a ideia de fazer um container que se conecta ao host e cria novos containers.
Alex Ushakov
1
Docker não parece gostar de sua montagem relativa. Isso deve funcionardocker run --rm -v $(pwd)/mybashscript.sh:/work/mybashscript.sh ubuntu /work/mybashscript.sh
KCD de
5
A primeira linha inicia um novo contêiner ubuntu e monta o script onde ele pode ser lido. Ele não permite que o contêiner acesse o sistema de arquivos do host, por exemplo. A segunda linha expõe o host /usr/binao contêiner. Em nenhum dos casos, o contêiner tem acesso total ao sistema host. Talvez eu esteja errado, mas parece uma resposta ruim para uma pergunta ruim.
Paul
3
Muito justo - a pergunta era muito vaga. A pergunta não pedia "acesso total ao sistema host". Conforme descrito, se o script bash se destina apenas a ecoar alguma saída, ele NÃO PRECISA de nenhum acesso ao sistema de arquivos host. Para meu segundo exemplo, que foi instalar docker-compose, a única permissão necessária é acessar o diretório bin onde o binário é armazenado. Como eu disse no início, para fazer isso, você teria que ter ideias muito específicas sobre o que o script está fazendo para permitir as permissões corretas.
Paul Becotte
1
Tentei fazer isso, o script é executado no contêiner, não no host
All2Pie
62
A solução que utilizo é conectar-se ao host SSHe executar o comando assim:
ssh -l ${USERNAME} ${HOSTNAME} "${SCRIPT}"
ATUALIZAR
Como esta resposta continua recebendo votos, gostaria de lembrar (e altamente recomendável) que a conta que está sendo usada para invocar o script deve ser uma conta sem permissão alguma, mas apenas executando esse script como sudo(pode ser feito do sudoersarquivo).
Como outra solução alternativa, o contêiner pode gerar um conjunto de comandos e o host pode executá-los após a saída do contêiner: eval $ (docker run --rm -it container_name_to_output script)
parity3
Preciso executar uma linha de comando no Host de dentro de um contêiner do Docker, mas quando entro no contêiner, sshnão foi encontrado. Você tem alguma outra sugestão?
Ron Rosenfeld
@RonRosenfeld, qual imagem Docker você está usando? em caso de Debian / Ubuntu, execute o seguinte: apt update && apt install openssh-client.
Mohammed Noureldin
Seria o que quer que fosse instalado no meu Synology NAS. Como posso eu saber?
Ron Rosenfeld
@RonRosenfeld, desculpe, não entendi o que você quis dizer
Mohammed Noureldin
54
Usou um pipe nomeado. No sistema operacional host, crie um script para fazer um loop e ler comandos e, em seguida, chamar eval para isso.
Faça com que o contêiner do docker leia para esse canal nomeado.
Para poder acessar o tubo, você precisa montá-lo por meio de um volume.
Isso é semelhante ao mecanismo SSH (ou um método baseado em soquete semelhante), mas restringe você corretamente ao dispositivo host, o que provavelmente é melhor. Além disso, você não precisa passar informações de autenticação.
Meu único aviso é ser cauteloso sobre o motivo de estar fazendo isso. É totalmente algo a fazer se você deseja criar um método de autoatualização com a entrada do usuário ou qualquer outra coisa, mas você provavelmente não deseja chamar um comando para obter alguns dados de configuração, já que a maneira correta seria passar isso como args / volume no docker. Também tenha cuidado com o fato de que você está avaliando, então apenas dê uma olhada no modelo de permissão.
Algumas das.as outras respostas, como executar um script. Sob um volume não funcionarão genericamente, uma vez que não terão acesso a todos os recursos do sistema, mas pode ser mais apropriado dependendo de seu uso.
ATENÇÃO: Esta é a resposta certa / melhor e precisa de mais elogios. Qualquer outra resposta consiste em perguntar "o que você está tentando fazer" e abrir exceções para as coisas. Eu tenho um caso de uso muito específico que exige que eu seja capaz de fazer isso, e esta é a única boa resposta imho. O SSH acima exigiria reduzir os padrões de segurança / firewall, e o material de execução do docker está totalmente errado. Obrigado por isso. Presumo que não receba tantos votos positivos porque não é uma resposta simples de copiar / colar, mas esta é a resposta. +100 pontos de mim se eu pudesse
Farley
4
Para aqueles que procuram mais informações, você pode usar o seguinte script em execução na máquina host: unix.stackexchange.com/a/369465 Claro, você terá que executá-lo com 'nohup' e criar algum tipo de wrapper de supervisor para mantê-lo vivo (talvez use um cron job: P)
Esta pode ser uma boa resposta. No entanto, seria muito melhor se você fornecer mais detalhes e mais explicações sobre a linha de comando. É possível elaborar?
Mohammed Noureldin
5
Votado, isso funciona! Faça um pipe nomeado usando 'mkfifo host_executor_queue' onde o volume está montado. Então, para adicionar um consumidor que executa comandos que são colocados na fila como o shell do host, use 'tail -f host_executor_queue | sh & '. O & no final faz com que seja executado em segundo plano. Finalmente, para enviar comandos para a fila, use 'echo touch foo> host_executor_queue' - este teste cria um arquivo temporário foo no diretório inicial. Se você deseja que o consumidor inicie na inicialização do sistema, coloque '@reboot tail -f host_executor_queue | sh & 'no crontab. Basta adicionar o caminho relativo ao host_executor_queue.
Skybunk 01 de
11
Essa resposta é apenas uma versão mais detalhada da solução do Bradford Medeiros , que também para mim acabou sendo a melhor resposta, então o crédito vai para ele.
Em sua resposta, ele explica O QUE fazer ( tubos nomeados ), mas não exatamente COMO fazer.
Tenho que admitir que não sabia o que são chamados de pipes no momento em que li sua solução. Então, eu me esforcei para implementá-lo (embora seja realmente muito simples), mas tive sucesso, então estou feliz em ajudar explicando como fiz isso. Portanto, o objetivo da minha resposta é apenas detalhar os comandos que você precisa executar para fazê-lo funcionar, mas, novamente, o crédito vai para ele.
PARTE 1 - Testando o conceito de tubo nomeado sem janela de encaixe
No host principal, escolha a pasta onde deseja colocar o arquivo de pipe nomeado, por exemplo, /path/to/pipe/e um nome de pipe, por exemplo mypipe, e execute:
mkfifo /path/to/pipe/mypipe
O tubo é criado. Tipo
ls -l /path/to/pipe/mypipe
E verifique se os direitos de acesso começam com "p", como
prw-r--r-- 1 root root 0 mypipe
Agora execute:
tail -f /path/to/pipe/mypipe
O terminal está agora esperando que os dados sejam enviados para este tubo
Agora abra outra janela de terminal.
E então execute:
echo "hello world" > /path/to/pipe/mypipe
Verifique o primeiro terminal (aquele com tail -f), ele deve exibir "hello world"
PARTE 2 - Executar comandos através do tubo
No contêiner do host, em vez de executar tail -fapenas o que é enviado como entrada, execute este comando que o executará como comandos:
eval "$(cat /path/to/pipe/mypipe)"
Em seguida, no outro terminal, tente executar:
echo "ls -l" > /path/to/pipe/mypipe
Volte para o primeiro terminal e você verá o resultado do ls -lcomando.
PARTE 3 - Faça ouvir para sempre
Você deve ter notado que na parte anterior, logo após a ls -lsaída ser exibida, ele para de ouvir os comandos.
Em vez de eval "$(cat /path/to/pipe/mypipe)", execute:
while true; do eval "$(cat /path/to/pipe/mypipe)"; done
(você não pode saber disso)
Agora você pode enviar um número ilimitado de comandos um após o outro, todos eles serão executados, não apenas o primeiro.
PARTE 4 - Faça funcionar mesmo quando a reinicialização acontecer
A única ressalva é que se o host tiver que reiniciar, o loop "while" parará de funcionar.
Para lidar com a reinicialização, aqui está o que eu fiz:
Coloque o while true; do eval "$(cat /path/to/pipe/mypipe)"; doneem um arquivo chamado execpipe.shcom #!/bin/bashcabeçalho
Não se esqueça chmod +xdisso
Adicione-o ao crontab executando
crontab -e
E então adicionando
@reboot /path/to/execpipe.sh
Neste ponto, teste: reinicie o servidor e, quando ele estiver de volta, ecoe alguns comandos no pipe e verifique se eles são executados. Claro, você não é capaz de ver a saída dos comandos, então ls -lnão ajudará, mas touch somefileajudará.
Outra opção é modificar o script para colocar a saída em um arquivo, como:
while true; do eval "$(cat /path/to/pipe/mypipe)" &> /somepath/output.txt; done
Agora você pode executar ls -le a saída (stdout e stderr usando &>em bash) deve estar em output.txt.
PARTE 5 - Faça funcionar com o docker
Se você estiver usando docker compose e dockerfile como eu, aqui está o que eu fiz:
Vamos supor que você deseja montar a pasta pai do mypipe como /hostpipeem seu contêiner
Adicione isso:
VOLUME /hostpipe
em seu dockerfile para criar um ponto de montagem
Em seguida, adicione este:
volumes:
- /path/to/pipe:/hostpipe
no arquivo de composição do docker para montar / path / to / pipe como / hostpipe
Reinicie seus contêineres do docker.
PARTE 6 - Teste
Exec em seu contêiner do docker:
docker exec -it <container> bash
Vá para a pasta de montagem e verifique se você pode ver o tubo:
cd /hostpipe && ls -l
Agora tente executar um comando de dentro do contêiner:
AVISO: Se você tiver um host OSX (Mac OS) e um contêiner Linux, não funcionará (explicação aqui https://stackoverflow.com/a/43474708/10018801 e problema aqui https://github.com/docker / for-mac / issues / 483 ) porque a implementação do pipe não é a mesma, então o que você escreve no pipe do Linux pode ser lido apenas por um Linux e o que você escreve no pipe do Mac OS pode ser lido apenas por um Mac OS (esta frase pode não ser muito precisa, mas esteja ciente de que existe um problema de plataforma cruzada).
Por exemplo, quando executo a configuração da minha docker no DEV do meu computador Mac OS, o pipe nomeado conforme explicado acima não funciona. Mas, no teste e na produção, tenho host e contêineres Linux, e isso funciona perfeitamente.
PARTE 7 - Exemplo de contêiner Node.JS
Aqui está como eu envio um comando do meu contêiner node js para o host principal e recupero a saída:
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);
Isso funciona muito bem. E quanto à segurança? Quero usar isso para iniciar / parar contêineres do docker de dentro de um contêiner em execução? Acabo de criar um dockeruser sem quaisquer privilégios, exceto para executar comandos docker?
Kristof van Woensel
6
Escreva um servidor python de servidor simples ouvindo em uma porta (digamos 8080), vincule a porta -p 8080: 8080 com o contêiner, faça uma solicitação HTTP para localhost: 8080 para pedir ao servidor python que executa scripts de shell com popen, execute um curl ou escrevendo código para fazer uma solicitação 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()
IMO esta é a melhor resposta. A execução de comandos arbitrários na máquina host DEVE ser feita por meio de algum tipo de API (por exemplo, REST). Esta é a única maneira pela qual a segurança pode ser aplicada e os processos em execução podem ser controlados adequadamente (por exemplo, matar, lidar com stdin, stdout, código de saída e assim por diante). Claro, seria ótimo se essa API pudesse ser executada dentro do Docker, mas pessoalmente não me importo de executá-la diretamente no host.
barney765
6
Se você não está preocupado com a segurança e está simplesmente procurando iniciar um contêiner do docker no host de dentro de outro contêiner do docker como o OP, você pode compartilhar o servidor docker em execução no host com o contêiner do docker, compartilhando seu soquete de escuta.
Quando você executa o comando docker start em seu contêiner do docker, o servidor docker em execução no seu host verá a solicitação e fornecerá o contêiner irmão.
Considere que o docker deve ser instalado no contêiner, caso contrário, você também precisará montar um volume para o binário do docker (por exemplo /usr/bin/docker:/usr/bin/docker).
Tudo o que você precisa fazer para obter um shell completo para seu host Linux de dentro do contêiner do docker é:
docker run --privileged --pid=host -it alpine:3.8 \
nsenter -t 1 -m -u -n -i sh
Explicação:
--privileged: concede permissões adicionais ao contêiner, permite que o contêiner obtenha acesso aos dispositivos do host (/ dev)
--pid = host: permite que os contêineres usem a árvore de processos do host Docker (a VM na qual o Docker daemon está sendo executado) utilitário nsenter: permite executar um processo em namespaces existentes (os blocos de construção que fornecem isolamento aos contêineres)
nsenter (-t 1 -m -u -n -i sh) permite executar o processo sh no mesmo contexto de isolamento que o processo com PID 1. O comando inteiro irá então fornecer um sh shell interativo na VM
Essa configuração tem importantes implicações de segurança e deve ser usada com cautela (se houver).
Embora essa resposta possa resolver a questão do OP, sugere-se que você explique como funciona e por que resolve o problema. Isso ajuda os novos desenvolvedores a entender o que está acontecendo e como corrigir isso e problemas semelhantes por conta própria. Obrigado por contribuir!
Caleb Kleveter
1
Eu tenho uma abordagem simples.
Etapa 1: Monte /var/run/docker.sock:/var/run/docker.sock (para que você possa executar comandos do docker dentro de seu contêiner)
Etapa 2: execute abaixo dentro do seu contêiner. A parte principal aqui é ( - host de rede, pois será executado no contexto do host)
docker run -i --rm --network host -v /opt/test.sh:/test.sh alpine: 3.7 sh /test.sh
test.sh deve conter alguns comandos (ifconfig, netstat etc ...) tudo o que você precisa. Agora você poderá obter a saída do contexto do host.
De acordo com a documentação oficial do docker sobre rede usando rede de host, "No entanto, em todas as outras formas, como armazenamento, namespace de processo e namespace de usuário, o processo é isolado do host. Confira - docs.docker.com/network/network-tutorial-host
Peter Mutisya
0
Como Marcus lembra, docker é basicamente isolamento de processo. A partir do docker 1.8, você pode copiar arquivos de ambas as maneiras entre o host e o contêiner, consulte o documento dedocker cp
você sempre pode, no host, obter o valor de alguma variável em seu contêiner, algo como myvalue=$(docker run -it ubuntu echo $PATH)e testá-lo regularmente em um shell de script (é claro, você usará algo diferente de $ PATH, é apenas um exemplo), quando é algum valor específico, você inicia seu script
user2915097
0
Você pode usar o conceito de pipe, mas use um arquivo no host e fswatch para cumprir o objetivo de executar um script na máquina host a partir de um contêiner docker. Assim (use por sua própria conta e risco):
#! /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
Neste exemplo, dentro do contêiner, envie comandos para / dev / command_pipe, assim:
A ideia de isolamento é ser capaz de restringir o que um aplicativo / processo / contêiner (seja qual for o seu ângulo) pode fazer com o sistema host de forma muito clara. Portanto, poder copiar e executar um arquivo realmente quebraria todo o conceito.
Sim. Mas às vezes é necessário.
Não. Não é esse o caso, ou o Docker não é a coisa certa a se usar. O que você deve fazer é declarar uma interface clara para o que você deseja fazer (por exemplo, atualizar uma configuração de host) e escrever um cliente / servidor mínimo para fazer exatamente isso e nada mais. Geralmente, porém, isso não parece ser muito desejável. Em muitos casos, você deve simplesmente repensar sua abordagem e erradicar essa necessidade. O Docker surgiu quando basicamente tudo era um serviço acessível por meio de algum protocolo. Não consigo pensar em nenhum caso de uso adequado de um contêiner do Docker obtendo os direitos para executar coisas arbitrárias no host.
Eu tenho um caso de uso: tenho o serviço dockerized A(src no github). No Arepo, eu crio ganchos apropriados que, após o comando 'git pull', criam uma nova imagem do docker e as executo (e removem o contêiner antigo, é claro). A seguir: o github tem web-hooks que permitem criar uma solicitação POST para um link de endpoint arbitrário após o envio no master. Portanto, não quero criar o serviço B dockerizado que será esse endpoint e que só executará 'git pull' no repo A na máquina HOST (importante: o comando 'git pull' deve ser executado no ambiente HOST - não no ambiente B porque B não é possível executar o novo contêiner A dentro de B ...)
Kamil Kiełczewski
1
O problema: não quero ter nada no HOST, exceto linux, git e docker. E eu quero ter o serviço dockerizet A e o serviço B (que na verdade é um manipulador git-push que executa git pull no repo A depois que alguém faz git push no master). Portanto, git auto-deploy é um caso de uso problemático
Kamil Kiełczewski
@ KamilKiełczewski Estou tentando fazer exatamente o mesmo, você encontrou uma solução?
user871784
1
Dizer: "Não, não é esse o caso" é tacanho e pressupõe que você conheça todos os casos de uso do mundo. Nosso caso de uso é a execução de testes. Eles precisam ser executados em contêineres para testar corretamente o ambiente, mas devido à natureza dos testes, eles também precisam executar scripts no host.
Senica Gonzalez
1
Apenas para aqueles que estão se perguntando por que deixo uma resposta -7: a) tudo bem ser falível. Eu estava errado. Tudo bem que isso esteja documentado aqui. b) Os comentários realmente contribuem com valor; excluir a resposta também os excluiria. c) Ainda contribui com um ponto de vista que pode ser sensato considerar (não rompa seu isolamento se não for necessário. Às vezes, no entanto).
Respostas:
Isso realmente depende do que você precisa que o script bash faça!
Por exemplo, se o script bash apenas exibe alguma saída, você pode apenas fazer
Outra possibilidade é que você queira que o script bash instale algum software - digamos, o script para instalar o docker-compose. você poderia fazer algo como
Mas, neste ponto, você realmente precisa saber intimamente o que o script está fazendo para permitir as permissões específicas de que ele precisa em seu host de dentro do contêiner.
fonte
docker run --rm -v $(pwd)/mybashscript.sh:/work/mybashscript.sh ubuntu /work/mybashscript.sh
/usr/bin
ao contêiner. Em nenhum dos casos, o contêiner tem acesso total ao sistema host. Talvez eu esteja errado, mas parece uma resposta ruim para uma pergunta ruim.A solução que utilizo é conectar-se ao host
SSH
e executar o comando assim:ATUALIZAR
Como esta resposta continua recebendo votos, gostaria de lembrar (e altamente recomendável) que a conta que está sendo usada para invocar o script deve ser uma conta sem permissão alguma, mas apenas executando esse script como
sudo
(pode ser feito dosudoers
arquivo).fonte
ssh
não foi encontrado. Você tem alguma outra sugestão?apt update && apt install openssh-client
.Usou um pipe nomeado. No sistema operacional host, crie um script para fazer um loop e ler comandos e, em seguida, chamar eval para isso.
Faça com que o contêiner do docker leia para esse canal nomeado.
Para poder acessar o tubo, você precisa montá-lo por meio de um volume.
Isso é semelhante ao mecanismo SSH (ou um método baseado em soquete semelhante), mas restringe você corretamente ao dispositivo host, o que provavelmente é melhor. Além disso, você não precisa passar informações de autenticação.
Meu único aviso é ser cauteloso sobre o motivo de estar fazendo isso. É totalmente algo a fazer se você deseja criar um método de autoatualização com a entrada do usuário ou qualquer outra coisa, mas você provavelmente não deseja chamar um comando para obter alguns dados de configuração, já que a maneira correta seria passar isso como args / volume no docker. Também tenha cuidado com o fato de que você está avaliando, então apenas dê uma olhada no modelo de permissão.
Algumas das.as outras respostas, como executar um script. Sob um volume não funcionarão genericamente, uma vez que não terão acesso a todos os recursos do sistema, mas pode ser mais apropriado dependendo de seu uso.
fonte
Essa resposta é apenas uma versão mais detalhada da solução do Bradford Medeiros , que também para mim acabou sendo a melhor resposta, então o crédito vai para ele.
Em sua resposta, ele explica O QUE fazer ( tubos nomeados ), mas não exatamente COMO fazer.
Tenho que admitir que não sabia o que são chamados de pipes no momento em que li sua solução. Então, eu me esforcei para implementá-lo (embora seja realmente muito simples), mas tive sucesso, então estou feliz em ajudar explicando como fiz isso. Portanto, o objetivo da minha resposta é apenas detalhar os comandos que você precisa executar para fazê-lo funcionar, mas, novamente, o crédito vai para ele.
PARTE 1 - Testando o conceito de tubo nomeado sem janela de encaixe
No host principal, escolha a pasta onde deseja colocar o arquivo de pipe nomeado, por exemplo,
/path/to/pipe/
e um nome de pipe, por exemplomypipe
, e execute:O tubo é criado. Tipo
E verifique se os direitos de acesso começam com "p", como
Agora execute:
O terminal está agora esperando que os dados sejam enviados para este tubo
Agora abra outra janela de terminal.
E então execute:
Verifique o primeiro terminal (aquele com
tail -f
), ele deve exibir "hello world"PARTE 2 - Executar comandos através do tubo
No contêiner do host, em vez de executar
tail -f
apenas o que é enviado como entrada, execute este comando que o executará como comandos:Em seguida, no outro terminal, tente executar:
Volte para o primeiro terminal e você verá o resultado do
ls -l
comando.PARTE 3 - Faça ouvir para sempre
Você deve ter notado que na parte anterior, logo após a
ls -l
saída ser exibida, ele para de ouvir os comandos.Em vez de
eval "$(cat /path/to/pipe/mypipe)"
, execute:(você não pode saber disso)
Agora você pode enviar um número ilimitado de comandos um após o outro, todos eles serão executados, não apenas o primeiro.
PARTE 4 - Faça funcionar mesmo quando a reinicialização acontecer
A única ressalva é que se o host tiver que reiniciar, o loop "while" parará de funcionar.
Para lidar com a reinicialização, aqui está o que eu fiz:
Coloque o
while true; do eval "$(cat /path/to/pipe/mypipe)"; done
em um arquivo chamadoexecpipe.sh
com#!/bin/bash
cabeçalhoNão se esqueça
chmod +x
dissoAdicione-o ao crontab executando
E então adicionando
Neste ponto, teste: reinicie o servidor e, quando ele estiver de volta, ecoe alguns comandos no pipe e verifique se eles são executados. Claro, você não é capaz de ver a saída dos comandos, então
ls -l
não ajudará, mastouch somefile
ajudará.Outra opção é modificar o script para colocar a saída em um arquivo, como:
Agora você pode executar
ls -l
e a saída (stdout e stderr usando&>
em bash) deve estar em output.txt.PARTE 5 - Faça funcionar com o docker
Se você estiver usando docker compose e dockerfile como eu, aqui está o que eu fiz:
Vamos supor que você deseja montar a pasta pai do mypipe como
/hostpipe
em seu contêinerAdicione isso:
em seu dockerfile para criar um ponto de montagem
Em seguida, adicione este:
no arquivo de composição do docker para montar / path / to / pipe como / hostpipe
Reinicie seus contêineres do docker.
PARTE 6 - Teste
Exec em seu contêiner do docker:
Vá para a pasta de montagem e verifique se você pode ver o tubo:
Agora tente executar um comando de dentro do contêiner:
E deve funcionar!
AVISO: Se você tiver um host OSX (Mac OS) e um contêiner Linux, não funcionará (explicação aqui https://stackoverflow.com/a/43474708/10018801 e problema aqui https://github.com/docker / for-mac / issues / 483 ) porque a implementação do pipe não é a mesma, então o que você escreve no pipe do Linux pode ser lido apenas por um Linux e o que você escreve no pipe do Mac OS pode ser lido apenas por um Mac OS (esta frase pode não ser muito precisa, mas esteja ciente de que existe um problema de plataforma cruzada).
Por exemplo, quando executo a configuração da minha docker no DEV do meu computador Mac OS, o pipe nomeado conforme explicado acima não funciona. Mas, no teste e na produção, tenho host e contêineres Linux, e isso funciona perfeitamente.
PARTE 7 - Exemplo de contêiner Node.JS
Aqui está como eu envio um comando do meu contêiner node js para o host principal e recupero a saída:
fonte
Escreva um servidor python de servidor simples ouvindo em uma porta (digamos 8080), vincule a porta -p 8080: 8080 com o contêiner, faça uma solicitação HTTP para localhost: 8080 para pedir ao servidor python que executa scripts de shell com popen, execute um curl ou escrevendo código para fazer uma solicitação 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()
fonte
Se você não está preocupado com a segurança e está simplesmente procurando iniciar um contêiner do docker no host de dentro de outro contêiner do docker como o OP, você pode compartilhar o servidor docker em execução no host com o contêiner do docker, compartilhando seu soquete de escuta.
Consulte https://docs.docker.com/engine/security/security/#docker-daemon-attack-surface e veja se sua tolerância a risco pessoal permite isso para este aplicativo específico.
Você pode fazer isso adicionando os seguintes argumentos de volume ao seu comando inicial
ou compartilhando /var/run/docker.sock dentro de seu arquivo docker compose assim:
Quando você executa o comando docker start em seu contêiner do docker, o servidor docker em execução no seu host verá a solicitação e fornecerá o contêiner irmão.
crédito: http://jpetazzo.github.io/2015/09/03/do-not-use-docker-in-docker-for-ci/
fonte
/usr/bin/docker:/usr/bin/docker
).Minha preguiça me levou a encontrar a solução mais fácil que não foi publicada como resposta aqui.
É baseado no excelente artigo de luc juggery .
Tudo o que você precisa fazer para obter um shell completo para seu host Linux de dentro do contêiner do docker é:
Explicação:
--privileged: concede permissões adicionais ao contêiner, permite que o contêiner obtenha acesso aos dispositivos do host (/ dev)
--pid = host: permite que os contêineres usem a árvore de processos do host Docker (a VM na qual o Docker daemon está sendo executado) utilitário nsenter: permite executar um processo em namespaces existentes (os blocos de construção que fornecem isolamento aos contêineres)
nsenter (-t 1 -m -u -n -i sh) permite executar o processo sh no mesmo contexto de isolamento que o processo com PID 1. O comando inteiro irá então fornecer um sh shell interativo na VM
Essa configuração tem importantes implicações de segurança e deve ser usada com cautela (se houver).
fonte
fonte
Eu tenho uma abordagem simples.
Etapa 1: Monte /var/run/docker.sock:/var/run/docker.sock (para que você possa executar comandos do docker dentro de seu contêiner)
Etapa 2: execute abaixo dentro do seu contêiner. A parte principal aqui é ( - host de rede, pois será executado no contexto do host)
test.sh deve conter alguns comandos (ifconfig, netstat etc ...) tudo o que você precisa. Agora você poderá obter a saída do contexto do host.
fonte
Como Marcus lembra, docker é basicamente isolamento de processo. A partir do docker 1.8, você pode copiar arquivos de ambas as maneiras entre o host e o contêiner, consulte o documento de
docker cp
https://docs.docker.com/reference/commandline/cp/
Depois que um arquivo é copiado, você pode executá-lo localmente
fonte
myvalue=$(docker run -it ubuntu echo $PATH)
e testá-lo regularmente em um shell de script (é claro, você usará algo diferente de $ PATH, é apenas um exemplo), quando é algum valor específico, você inicia seu scriptVocê pode usar o conceito de pipe, mas use um arquivo no host e fswatch para cumprir o objetivo de executar um script na máquina host a partir de um contêiner docker. Assim (use por sua própria conta e risco):
Neste exemplo, dentro do contêiner, envie comandos para / dev / command_pipe, assim:
No host, você pode verificar se a rede foi criada:
fonte
Para expandir a resposta do usuário 2915097 :
A ideia de isolamento é ser capaz de restringir o que um aplicativo / processo / contêiner (seja qual for o seu ângulo) pode fazer com o sistema host de forma muito clara. Portanto, poder copiar e executar um arquivo realmente quebraria todo o conceito.
Não. Não é esse o caso, ou o Docker não é a coisa certa a se usar. O que você deve fazer é declarar uma interface clara para o que você deseja fazer (por exemplo, atualizar uma configuração de host) e escrever um cliente / servidor mínimo para fazer exatamente isso e nada mais. Geralmente, porém, isso não parece ser muito desejável. Em muitos casos, você deve simplesmente repensar sua abordagem e erradicar essa necessidade. O Docker surgiu quando basicamente tudo era um serviço acessível por meio de algum protocolo. Não consigo pensar em nenhum caso de uso adequado de um contêiner do Docker obtendo os direitos para executar coisas arbitrárias no host.
fonte
A
(src no github). NoA
repo, eu crio ganchos apropriados que, após o comando 'git pull', criam uma nova imagem do docker e as executo (e removem o contêiner antigo, é claro). A seguir: o github tem web-hooks que permitem criar uma solicitação POST para um link de endpoint arbitrário após o envio no master. Portanto, não quero criar o serviço B dockerizado que será esse endpoint e que só executará 'git pull' no repo A na máquina HOST (importante: o comando 'git pull' deve ser executado no ambiente HOST - não no ambiente B porque B não é possível executar o novo contêiner A dentro de B ...)