Jak uruchomić skrypt powłoki na hoście z kontenera docker?

Jak sterować hostem z kontenera docker?

Na przykład, jak wykonać skopiowany do hosta skrypt bash?

 105
Author: Alex Ushakov, 2015-08-23

12 answers

To naprawdę zależy od tego, co trzeba skrypt bash zrobić!

Na przykład, jeśli skrypt bash tylko odbija jakieś wyjście, możesz po prostu zrobić

docker run --rm -v $(pwd)/mybashscript.sh:/mybashscript.sh ubuntu bash /mybashscript.sh

Inną możliwością jest to, że chcesz, aby skrypt bash zainstalował jakieś oprogramowanie-powiedzmy, że skrypt do zainstalowania docker-compose. możesz zrobić coś w stylu

docker run --rm -v /usr/bin:/usr/bin --privileged -v $(pwd)/mybashscript.sh:/mybashscript.sh ubuntu bash /mybashscript.sh

Ale w tym momencie naprawdę zaczynasz wiedzieć, co robi skrypt, aby zezwolić na określone uprawnienia, których potrzebuje na Twoim hoście wewnątrz pojemnika.

 31
Author: Paul Becotte,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-20 14:49:12

Rozwiązaniem, którego używam, jest połączenie się z hostem przez SSH i wykonanie polecenia w następujący sposób:

ssh -l ${USERNAME} ${HOSTNAME} "${SCRIPT}"

UPDATE

Ponieważ ta odpowiedź wciąż podnosi głosy, chciałbym przypomnieć (i Gorąco polecam), że konto, które jest używane do wywołania skryptu powinno być kontem bez żadnych uprawnień, a jedynie wykonującym ten skrypt jako sudo (można to zrobić z pliku sudoers).

 63
Author: Mohammed Noureldin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-08-03 15:16:02

Użyłem nazwanej rury. W systemie operacyjnym hosta Utwórz skrypt do pętli i odczytu poleceń, a następnie wywołaj eval.

Niech kontener dokujący odczyta tę nazwę rury.

Aby mieć dostęp do rury, musisz ją zamontować za pomocą głośnika.

Jest to podobne do mechanizmu SSH( lub podobnej metody opartej na gniazdach), ale ogranicza cię odpowiednio do urządzenia hosta, co jest prawdopodobnie lepsze. Ponadto nie musisz przekazywać informacji uwierzytelniających.

Moim jedynym ostrzeżeniem jest być ostrożnym, dlaczego to robisz. Jest to całkowicie coś do zrobienia, jeśli chcesz stworzyć metodę do samodzielnej aktualizacji z wejściem użytkownika lub cokolwiek innego, ale prawdopodobnie nie chcesz wywoływać polecenia, aby uzyskać dane konfiguracyjne, ponieważ właściwym sposobem byłoby przekazanie tego jako args / volume do Dockera. Uważaj również na fakt, że evaling, więc po prostu daj modelowi pozwolenia myśl.

Niektóre zinne odpowiedzi, takie jak uruchamianie skryptu.pod wolumin nie będzie działał ogólnie, ponieważ nie będzie miał dostępu do pełnych zasobów systemowych, ale może być bardziej odpowiedni w zależności od użycia.

 57
Author: Bradford Medeiros,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-17 08:50:18

Ta odpowiedź jest tylko bardziej szczegółową wersją rozwiązania Bradforda Medeirosa , które również dla mnie okazało się najlepszą odpowiedzią, więc zasługa należy się jemu.

W swojej odpowiedzi wyjaśnia, co robić (nazwane pipes), ale nie do końca jak to zrobić.

Muszę przyznać, że nie wiedziałem, jak się nazywa pipes w czasie, gdy czytałem jego rozwiązanie. Więc starałem się go wdrożyć( chociaż jest to naprawdę proste), ale udało mi się, więc chętnie pomogę przez wyjaśniam, jak to zrobiłem. Więc celem mojej odpowiedzi jest tylko wyszczególnienie poleceń, które musisz uruchomić, aby to działało, ale znowu, kredyt idzie do niego.

[[43]}Część 1-testowanie koncepcji nazwanej rury bez Dockera

Na głównym hoście wybierz folder, w którym chcesz umieścić swój nazwany plik Potoku, na przykład /path/to/pipe/ i nazwę Potoku, na przykład mypipe, a następnie uruchom:

mkfifo /path/to/pipe/mypipe

Rura jest tworzona. Typ

ls -l /path/to/pipe/mypipe 

I sprawdź prawa dostępu zaczynają się od "p", takie as

prw-r--r-- 1 root root 0 mypipe

Teraz uruchom:

tail -f /path/to/pipe/mypipe
Terminal oczekuje teraz na wysłanie danych do tego potoku.]}

Teraz otwórz kolejne okno terminala.

A następnie uruchomić:

echo "hello world" > /path/to/pipe/mypipe

Sprawdź pierwszy terminal (ten z tail -f), powinien wyświetlać "hello world"

Część 2-Uruchamianie poleceń przez rurę

Na kontenerze hosta, zamiast uruchamiać tail -f, które wypisuje to, co jest wysyłane jako wejście, uruchom to polecenie, które wykona je jako polecenia:

eval "$(cat /path/to/pipe/mypipe)"

Następnie, z drugiego terminala, spróbuj uruchomić:

echo "ls -l" > /path/to/pipe/mypipe

Wróć do pierwszego terminala i powinieneś zobaczyć wynik polecenia ls -l.

PART 3 - Make it listen forever

Być może zauważyłeś, że w poprzedniej części, zaraz po wyświetleniu ls -l Wyjście przestaje nasłuchiwać poleceń.

Zamiast eval "$(cat /path/to/pipe/mypipe)", Uruchom:

while true; do eval "$(cat /path/to/pipe/mypipe)"; done

(you can nohup that)

Teraz możesz wysłać nieograniczoną liczbę komend jeden po inne, wszystkie zostaną stracone, nie tylko pierwszy.

Część 4-spraw, aby działało nawet po ponownym uruchomieniu

Jedynym zastrzeżeniem jest to, że jeśli host musi się zrestartować, pętla "while" przestanie działać.

Aby poradzić sobie z restartem, oto co zrobiłem:

Umieść while true; do eval "$(cat /path/to/pipe/mypipe)"; done w pliku o nazwie execpipe.sh z #!/bin/bash nagłówkiem

Nie zapomnij chmod +x to

Dodaj go do crontab, uruchamiając

crontab -e

A następnie dodanie

@reboot /path/to/execpipe.sh

W tym momencie, przetestuj go: zrestartuj swój server, a kiedy jest z powrotem, echo niektórych poleceń do rury i sprawdzić, czy są one wykonywane. Oczywiście nie możesz zobaczyć wyjścia poleceń, więc ls -l nie pomoże, ale touch somefile pomoże.

Inną opcją jest zmodyfikowanie skryptu, aby umieścić wyjście w pliku, na przykład:

while true; do eval "$(cat /path/to/pipe/mypipe)" &> /somepath/output.txt; done

Teraz możesz uruchomić ls -l, A wyjście (zarówno stdout, jak i stderr za pomocą &> w bash) powinno być w output.txt.

Część 5-spraw, aby działało z Dockerem

Jeśli używasz zarówno Docker komponuje i dockerfile tak jak ja, oto co zrobiłem: {]}

Załóżmy, że chcesz zamontować folder nadrzędny mypipe jako /hostpipe w kontenerze

Dodaj to:

VOLUME /hostpipe

W pliku dockerfile w celu utworzenia punktu montowania

Następnie dodaj to:

volumes:
   - /path/to/pipe:/hostpipe
W pliku Dockera można zamontować / path / to / pipe jako / hostpipe]} Uruchom ponownie kontenery dokujące.

Część 6-testowanie

Exec do twojego dokera Pojemnik:

docker exec -it <container> bash

Wejdź do folderu mount i sprawdź, czy widzisz rurę:

cd /hostpipe && ls -l

Teraz spróbuj uruchomić polecenie z kontenera:

echo "touch this_file_was_created_on_main_host_from_a_container.txt" > /hostpipe/mypipe

I powinno zadziałać!

Ostrzeżenie: jeśli masz hosta OSX (Mac OS) i kontener Linux, to nie będzie działać (Wyjaśnienie tutaj https://stackoverflow.com/a/43474708/10018801 i wydanie tutaj https://github.com/docker/for-mac/issues/483 ) ponieważ implementacja rury nie jest taka sama, więc to, co piszesz w potoku z Linuksa, może być odczytywane tylko przez Linuksa, a to, co piszesz w potoku z Mac OS, może być odczytywane tylko przez Mac OS (to zdanie może nie być zbyt dokładne, ale pamiętaj, że istnieje problem międzyplatformowy).

Na przykład, gdy uruchamiam konfigurację Dockera w DEV z mojego komputera Mac OS, nazwa rury, jak wyjaśniono powyżej, nie działa. Ale w inscenizacji i produkcji mam Linux host i kontenery Linux i działa idealnie.

Część 7-przykład z węzła.Js container

Oto jak wysyłam polecenie z mojego kontenera js węzła do głównego hosta i pobieram wyjście:

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);
 15
Author: Vincent,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-09-03 08:16:16

Napisać prosty serwer serwer Pythona nasłuchujący na porcie (powiedzmy 8080), powiązać port-p 8080:8080 z kontenerem, wysłać żądanie HTTP do localhost:8080, aby zapytać serwer Pythona uruchamiający skrypty powłoki z popen, uruchomić curl lub napisać kod, aby złożyć żądanie 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()
 6
Author: Frank Chang,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-03-16 01:11:52

Jeśli nie martwisz się o bezpieczeństwo i po prostu chcesz uruchomić kontener Dockera na hoście z innego kontenera Dockera, takiego jak OP, możesz udostępnić serwer Dockera działający na hoście z kontenerem Dockera, udostępniając jego gniazdo listen.

Zobacz https://docs.docker.com/engine/security/security/#docker-daemon-attack-surface i sprawdź, czy Twoja osobista tolerancja ryzyka pozwala na to dla tego konkretnego zastosowania.

Możesz to zrobić przez dodawanie następujących woluminów args do polecenia start

docker run -v /var/run/docker.sock:/var/run/docker.sock ...

Lub udostępniając /var / run / docker.sock wewnątrz Dockera tworzy plik w następujący sposób:

version: '3'

services:
   ci:
      command: ...
      image: ...
      volumes
         - /var/run/docker.sock:/var/run/docker.sock

Po uruchomieniu polecenia docker start w kontenerze docker, serwer docker działający na Twoim hoście zobaczy żądanie i dostarczy kontener rodzeństwa.

Kredyt: http://jpetazzo.github.io/2015/09/03/do-not-use-docker-in-docker-for-ci/

 6
Author: Matt Bucci,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-01-29 19:48:47

Moje lenistwo doprowadziło mnie do znalezienia najprostszego rozwiązania, które nie zostało opublikowane jako odpowiedź tutaj.

Jest oparty na świetny artykuł autorstwa Luca juggery ' ego.

Wszystko, co musisz zrobić, aby uzyskać pełną powłokę do hosta Linuksa z kontenera docker to:

docker run --privileged --pid=host -it alpine:3.8 \
nsenter -t 1 -m -u -n -i sh

Wyjaśnienie:

--privileged: nadaje kontenerowi dodatkowe uprawnienia, pozwala kontenerowi uzyskać dostęp do urządzeń hosta (/dev)

--PID = host: pozwala kontenery wykorzystujące drzewo procesów hosta Dockera (maszyny Wirtualnej, w której uruchomiony jest Demon Dockera) nsenter utility: pozwala uruchomić proces w istniejących przestrzeniach nazw (bloki konstrukcyjne, które zapewniają izolację kontenerów)

Nsenter (- t 1-m-u-n-i sh) pozwala uruchomić proces sh w tym samym kontekście izolacji, co proces z PID 1. Cała Komenda dostarczy następnie interaktywną powłokę SH w maszynie wirtualnej

Ta konfiguracja ma poważne konsekwencje dla bezpieczeństwa i powinna być używana z ostrzeżeniami (jeśli istnieją).

 3
Author: Shmulik ahituv,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-07-28 18:14:46
docker run --detach-keys="ctrl-p" -it -v /:/mnt/rootdir --name testing busybox
# chroot /mnt/rootdir
# 
 1
Author: Zibri,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-12-14 15:12:09

Mam proste podejście.

Krok 1: Mount / var / run / docker.sock: / var / run / docker.sock (dzięki czemu będziesz mógł wykonywać polecenia dokera wewnątrz kontenera)

Krok 2: wykonaj to poniżej wewnątrz kontenera. Kluczowa część to (--host sieciowy {[6] } ponieważ będzie to wykonywane z kontekstu hosta)

Docker run-I --RM --network host-v /opt/test.sh:/test.sh alpine: 3.7 sh /test.sh

Test.sh powinien zawierać niektóre polecenia (ifconfig, netstat itp...) cokolwiek potrzebujesz. Teraz będziesz mógł uzyskać wyjście kontekstu hosta.

 1
Author: Bala,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-01-23 07:09:45

Jak przypomina Marcus, docker to w zasadzie izolacja procesu. Począwszy od docker 1.8, możesz kopiować pliki w obie strony między hostem a kontenerem, zobacz dokument docker cp

Https://docs.docker.com/reference/commandline/cp/

Po skopiowaniu pliku można go uruchomić lokalnie

 0
Author: user2915097,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-23 06:53:28

Możesz użyć koncepcji pipe, ale użyj pliku na hoście i fswatch, aby osiągnąć cel, jakim jest wykonanie skryptu na komputerze hosta z kontenera dokera. Podobnie (Używaj na własne ryzyko):

#! /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

W tym przykładzie wewnątrz kontenera wysyłane są polecenia do/dev / command_pipe, w następujący sposób:

/home/test # echo 'docker network create test2.network.com' > /dev/command_pipe

Na hoście możesz sprawdzić, czy sieć została utworzona:

$ docker network ls | grep test2
8e029ec83afe        test2.network.com                            bridge              local
 0
Author: Fábio Silva,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-10 15:14:39

To expand on user2915097 ' S odpowiedź :

Ideą izolacji jest możliwość ograniczenia tego, co aplikacja/proces/kontener (niezależnie od Twojego kąta) może zrobić z systemem hosta bardzo wyraźnie. Stąd możliwość skopiowania i wykonania pliku naprawdę złamałaby całą koncepcję.

Tak. Ale czasami jest to konieczne.

Nie. To nie jest przypadek, lub Docker nie jest rzeczą właściwą do korzystania. To, co powinieneś zrobić, to zadeklarować jasno interfejs do tego ,co chcesz zrobić (np. aktualizowanie konfiguracji hosta), i napisać minimalny klient / serwer, aby zrobić dokładnie to i nic więcej. Generalnie jednak nie wydaje się to zbyt pożądane. W wielu przypadkach należy po prostu przemyśleć swoje podejście i wyeliminować tę potrzebę. Docker powstał, gdy w zasadzie wszystko było usługą, która była osiągalna za pomocą jakiegoś protokołu. Nie przychodzi mi do głowy żaden właściwy sposób użycia kontenera Dockera, który ma prawa do wykonania dowolnego rzeczy na temat gospodarza.

 -7
Author: Marcus Müller,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:54:50