Docker container'dan ana bilgisayarı nasıl kontrol edebilirim?
Örneğin, ana bash betiğine kopyalananlar nasıl çalıştırılır?
Docker container'dan ana bilgisayarı nasıl kontrol edebilirim?
Örneğin, ana bash betiğine kopyalananlar nasıl çalıştırılır?
Yanıtlar:
Bu GERÇEKTEN o bash betiğinin ne yapmanız gerektiğine bağlı!
Örneğin, bash betiği yalnızca bazı çıktıları yansıtıyorsa,
docker run --rm -v $(pwd)/mybashscript.sh:/mybashscript.sh ubuntu bash /mybashscript.sh
Başka bir olasılık da, bash betiğinin bazı yazılımları yüklemesini istemenizdir - örneğin betik docker-compose'u kursun. gibi bir şey yapabilirsin
docker run --rm -v /usr/bin:/usr/bin --privileged -v $(pwd)/mybashscript.sh:/mybashscript.sh ubuntu bash /mybashscript.sh
Ancak bu noktada, ana makinenizde ihtiyaç duyduğu belirli izinleri konteynerin içinden sağlamak için betiğin ne yaptığını gerçekten bilmek zorunda kalıyorsunuz.
docker run --rm -v $(pwd)/mybashscript.sh:/work/mybashscript.sh ubuntu /work/mybashscript.sh
/usr/bin
konteynere gösterir. Her iki durumda da kapsayıcı, ana bilgisayar sistemine tam erişime sahip değildir. Belki yanılıyorum, ama kötü bir soruya kötü bir cevap gibi görünüyor.
Kullandığım çözüm, ana bilgisayara bağlanmak SSH
ve şu şekilde komutu çalıştırmak:
ssh -l ${USERNAME} ${HOSTNAME} "${SCRIPT}"
Bu cevap oy almaya devam ettiği için, betiği çağırmak için kullanılan hesabın hiçbir izne sahip olmayan bir hesap olması gerektiğini hatırlatmak isterim (ve kesinlikle tavsiye ederim), ancak yalnızca bu komut dosyasını sudo
( sudoers
dosyadan yapılır ).
ssh
bulunamadı. Başka bir öneriniz var mı?
apt update && apt install openssh-client
.
Adlandırılmış bir kanal kullandı. Ana bilgisayar işletim sisteminde, komutları döngülemek ve okumak için bir komut dosyası oluşturun ve ardından bunun üzerine eval çağırın.
Docker kapsayıcısının bu adlandırılmış boruya okumasını sağlayın.
Boruya erişebilmek için, bir birim aracılığıyla monte etmeniz gerekir.
Bu, SSH mekanizmasına (veya benzer bir soket tabanlı yönteme) benzer, ancak sizi uygun şekilde ana cihazla sınırlar, ki bu muhtemelen daha iyidir. Ayrıca kimlik doğrulama bilgilerinin etrafından dolaşmanız gerekmez.
Tek uyarım, bunu neden yaptığın konusunda dikkatli olmak. Kullanıcı girdisi veya her neyse, kendi kendine yükseltme yapmak için bir yöntem oluşturmak istiyorsanız, bu tamamen yapmanız gereken bir şeydir, ancak muhtemelen bazı yapılandırma verilerini almak için bir komut çağırmak istemezsiniz, çünkü doğru yol bunu bağımsız değişken olarak iletmek olacaktır / docker içine hacim. Ayrıca değerlendirmekte olduğunuz gerçeği konusunda da dikkatli olun, bu yüzden sadece izin modelini bir düşünün.
Betiğin bir birim altında çalıştırılması gibi diğer yanıtlardan bazıları, tüm sistem kaynaklarına erişemeyecekleri için genel olarak çalışmayacaktır, ancak kullanımınıza bağlı olarak daha uygun olabilir.
Güvenlik konusunda endişelenmiyorsanız ve sadece OP gibi başka bir docker konteynerinden ana bilgisayarda bir docker konteyner başlatmak istiyorsanız, ana bilgisayarda çalışan docker sunucusunu, dinleme soketini paylaşarak docker konteyneriyle paylaşabilirsiniz.
Lütfen https://docs.docker.com/engine/security/security/#docker-daemon-attack-surface adresine bakın ve kişisel risk toleransınızın bu özel uygulama için buna izin verip vermediğini görün.
Bunu, başlatma komutunuza aşağıdaki birim değiştirgelerini ekleyerek yapabilirsiniz.
docker run -v /var/run/docker.sock:/var/run/docker.sock ...
veya docker compose dosyanız içinde /var/run/docker.sock'u şu şekilde paylaşarak:
version: '3'
services:
ci:
command: ...
image: ...
volumes
- /var/run/docker.sock:/var/run/docker.sock
Docker kapsayıcınızda docker start komutunu çalıştırdığınızda, ana makinenizde çalışan docker sunucusu isteği görür ve kardeş kapsayıcıyı sağlar.
kredi: http://jpetazzo.github.io/2015/09/03/do-not-use-docker-in-docker-for-ci/
/usr/bin/docker:/usr/bin/docker
).
Bu cevap, Bradford Medeiros'un çözümünün sadece daha detaylı bir versiyonudur ve benim için de en iyi cevap olduğu ortaya çıktı, bu yüzden kredi ona gidiyor.
Cevabında, NE yapılması gerektiğini ( adlandırılmış borular ) açıklıyor, ancak tam olarak NASIL yapılacağını değil.
Çözümünü okuduğumda boruların ne olduğunu bilmediğimi itiraf etmeliyim. Bu yüzden onu uygulamak için mücadele ettim (aslında gerçekten basit olsa da), ama başardım, bu yüzden nasıl yaptığımı açıklayarak yardımcı olmaktan mutluluk duyuyorum. Bu yüzden cevabımın amacı, onu çalıştırmak için çalıştırmanız gereken komutları detaylandırmak, ama yine, kredi ona gidiyor.
Ana ana bilgisayarda, örneğin adlandırılmış kanal dosyanızı yerleştirmek istediğiniz klasörü /path/to/pipe/
ve örneğin bir kanal adını seçin mypipe
ve ardından şunu çalıştırın:
mkfifo /path/to/pipe/mypipe
Boru oluşturuldu. Tür
ls -l /path/to/pipe/mypipe
Ve erişim haklarının "p" ile başlamasını kontrol edin, örneğin
prw-r--r-- 1 root root 0 mypipe
Şimdi çalıştırın:
tail -f /path/to/pipe/mypipe
Terminal şimdi verilerin bu boruya gönderilmesini bekliyor
Şimdi başka bir terminal penceresi açın.
Ve sonra çalıştırın:
echo "hello world" > /path/to/pipe/mypipe
İlk terminali (olanı tail -f
) kontrol edin, "merhaba dünya" yazmalıdır
Ana bilgisayar kapsayıcısında, tail -f
yalnızca girdi olarak gönderilen her şeyi çıktı olarak çalıştırmak yerine, onu komut olarak yürütecek şu komutu çalıştırın:
eval "$(cat /path/to/pipe/mypipe)"
Ardından, diğer terminalden şunu çalıştırmayı deneyin:
echo "ls -l" > /path/to/pipe/mypipe
İlk terminale geri dönün ve ls -l
komutun sonucunu görmelisiniz .
Önceki bölümde, ls -l
çıktı görüntülendikten hemen sonra komutları dinlemeyi bıraktığını fark etmiş olabilirsiniz .
Bunun yerine şunu eval "$(cat /path/to/pipe/mypipe)"
çalıştırın:
while true; do eval "$(cat /path/to/pipe/mypipe)"; done
(bunu yapamazsın)
Artık birbiri ardına sınırsız sayıda komut gönderebilirsiniz, sadece birincisi değil, hepsi çalıştırılacaktır.
Tek uyarı, ana bilgisayarın yeniden başlatılması gerekirse, "while" döngüsü çalışmayı durduracaktır.
Yeniden başlatmayı işlemek için işte yaptığım şey:
Put while true; do eval "$(cat /path/to/pipe/mypipe)"; done
adlı bir dosyada execpipe.sh
ile #!/bin/bash
başlığındaki
Unutma chmod +x
o
Çalıştırarak crontab'e ekleyin
crontab -e
Ve sonra ekliyor
@reboot /path/to/execpipe.sh
Bu noktada test edin: sunucunuzu yeniden başlatın ve yedeklendiğinde, bazı komutları boruya yansıtın ve çalıştırılıp çalıştırılmadıklarını kontrol edin. Tabii ki, komutların çıktısını göremezsiniz, bu yüzden ls -l
yardımcı touch somefile
olmayacak , ancak yardımcı olacaktır.
Diğer bir seçenek, çıktıyı bir dosyaya koymak için komut dosyasını değiştirmektir, örneğin:
while true; do eval "$(cat /path/to/pipe/mypipe)" &> /somepath/output.txt; done
Artık çalıştırabilirsiniz ls -l
ve çıktı (hem stdout hem de stderr &>
bash içinde kullanılır ) output.txt içinde olmalıdır.
Hem docker compose hem de dockerfile'ı benim yaptığım gibi kullanıyorsanız, işte yaptığım şey:
Mypipe'ın üst klasörünü /hostpipe
konteynerinizdeki gibi bağlamak istediğinizi varsayalım
Bunu ekle:
VOLUME /hostpipe
bir bağlama noktası oluşturmak için dockerfile'ınızda
Sonra şunu ekleyin:
volumes:
- /path/to/pipe:/hostpipe
docker'ınızda / yol / için / borulu / hostpipe olarak bağlamak için dosya oluşturun
Docker konteynerlerinizi yeniden başlatın.
Docker konteynerinize yürütün:
docker exec -it <container> bash
Montaj klasörüne gidin ve boruyu görebildiğinizi kontrol edin:
cd /hostpipe && ls -l
Şimdi konteynerin içinden bir komut çalıştırmayı deneyin:
echo "touch this_file_was_created_on_main_host_from_a_container.txt" > /hostpipe/mypipe
Ve işe yaramalı!
UYARI: Bir OSX (Mac OS) ana makineniz ve bir Linux kapsayıcınız varsa, çalışmaz (açıklama burada https://stackoverflow.com/a/43474708/10018801 ve burada https://github.com/docker sorun) / for-mac / issues / 483 ) çünkü boru uygulaması aynı değildir, bu nedenle Linux'tan boruya yazdıklarınız yalnızca Linux tarafından okunabilir ve Mac OS'den boruya yazdıklarınız yalnızca bir Mac OS (bu cümle çok doğru olmayabilir, ancak platformlar arası bir sorunun olduğunu unutmayın).
Örneğin, Mac OS bilgisayarımdan DEV'de docker kurulumumu çalıştırdığımda, yukarıda açıklandığı gibi adlandırılmış kanal çalışmıyor. Ancak hazırlık ve üretimde Linux ana bilgisayarım ve Linux kapsayıcılarım var ve mükemmel çalışıyor.
Düğüm js kapsayıcımdan ana ana bilgisayara bir komut göndermek ve çıktıyı almak için şu şekilde:
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);
Bir bağlantı noktasında dinleyen basit bir sunucu python sunucusu yazın (örneğin 8080), kap ile -p 8080: 8080 bağlantı noktasını bağlayın, python sunucusundan popen ile kabuk komut dosyalarını çalıştırmasını istemek için localhost: 8080'e bir HTTP isteği gönderin, bir curl çalıştırın veya HTTP isteği yapmak için kod yazma 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()
Tembelliğim, burada cevap olarak yayınlanmayan en kolay çözümü bulmamı sağladı.
Bu dayanmaktadır harika bir yazı ile luc juggery .
Docker konteynerinizin içinden linux sunucunuza tam bir kabuk kazandırmak için yapmanız gereken tek şey:
docker run --privileged --pid=host -it alpine:3.8 \
nsenter -t 1 -m -u -n -i sh
Açıklama:
--privileged: kapsayıcıya ek izinler verir, kapsayıcının ana bilgisayarın (/ dev) cihazlarına erişmesine izin verir
--pid = host: kapsayıcıların Docker ana bilgisayarının (Docker arka plan programının çalıştığı VM) işlem ağacını kullanmasına izin verir nsenter yardımcı programı: mevcut ad alanlarında (kapsayıcılara yalıtım sağlayan yapı taşları) bir işlem çalıştırmaya izin verir
nsenter (-t 1 -m -u -n -i sh), sh işleminin PID 1 ile işlemle aynı yalıtım bağlamında çalıştırılmasına izin verir. Tüm komut daha sonra sanal makinede etkileşimli bir sh kabuğu sağlayacaktır.
Bu kurulumun büyük güvenlik etkileri vardır ve (varsa) dikkatli kullanılmalıdır.
docker run --detach-keys="ctrl-p" -it -v /:/mnt/rootdir --name testing busybox
# chroot /mnt/rootdir
#
Basit bir yaklaşımım var.
Adım 1: Mount /var/run/docker.sock:/var/run/docker.sock (Böylece konteynerinizin içinde docker komutlarını çalıştırabileceksiniz)
Adım 2: Bunu aşağıda kabınızın içinde gerçekleştirin. Buradaki anahtar kısım (- ağ ana bilgisayarı, çünkü bu, ana bilgisayar bağlamından yürütülecektir)
docker run -i --rm --network host -v /opt/test.sh:/test.sh alpine: 3.7 sh /test.sh
test.sh, ihtiyacınız olan bazı komutları (ifconfig, netstat vb.) içermelidir. Artık ana bilgisayar bağlam çıktısını alabileceksiniz.
Marcus'un hatırlattığı gibi, docker temelde süreç izolasyonudur. Docker 1.8'den başlayarak, dosyaları ana bilgisayar ve konteyner arasında her iki yönde de kopyalayabilirsiniz, bkz.docker cp
https://docs.docker.com/reference/commandline/cp/
Bir dosya kopyalandıktan sonra onu yerel olarak çalıştırabilirsiniz
myvalue=$(docker run -it ubuntu echo $PATH)
ve bunu bir komut dosyası kabuğunda düzenli olarak test edebilirsiniz (tabii ki, $ PATH dışında bir şey kullanacaksınız, sadece bir örnek), belirli bir değerdir, komut dosyanızı başlatırsınız
Boru konseptini kullanabilirsiniz, ancak ana makinede bir docker konteynerinden bir komut dosyası yürütme hedefini gerçekleştirmek için ana bilgisayar ve fswatch üzerindeki bir dosyayı kullanabilirsiniz . Öyle (Kendi sorumluluğunuzdadır kullanın):
#! /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
Bu örnekte, kapsayıcı içinde komutları / dev / command_pipe'a gönderin, örneğin:
/home/test # echo 'docker network create test2.network.com' > /dev/command_pipe
Ana bilgisayarda, ağın oluşturulup oluşturulmadığını kontrol edebilirsiniz:
$ docker network ls | grep test2
8e029ec83afe test2.network.com bridge local
User2915097'nin yanıtını genişletmek için :
İzolasyon fikri, bir uygulamanın / işlemin / kapsayıcının (bu açınız ne olursa olsun) ana sisteme neler yapabileceğini çok açık bir şekilde kısıtlayabilmektir. Bu nedenle, bir dosyayı kopyalayıp çalıştırabilmek gerçekten tüm konsepti bozacaktır.
Evet. Ama bazen gerekli.
Hayır. Durum bu değil veya Docker doğru kullanım değil. Yapmanız gereken şey, yapmak istediğiniz şey için net bir arayüz bildirmek (örneğin, bir ana bilgisayar yapılandırmasını güncellemek) ve tam olarak bunu yapmak için minimum bir istemci / sunucu yazmaktır . Ancak genel olarak bu pek arzu edilen bir durum gibi görünmüyor. Çoğu durumda, yaklaşımınızı yeniden düşünmeli ve bu ihtiyacı ortadan kaldırmalısınız. Docker, temelde her şey bazı protokoller kullanılarak erişilebilen bir hizmet olduğunda ortaya çıktı. Ana bilgisayarda keyfi şeyler yürütme haklarını alan bir Docker kapsayıcısının herhangi bir uygun kullanım durumunu düşünemiyorum.
A
( github'da src). Gelen A
repo ben 'git çekme' komutundan sonra yeni liman işçisi görüntü oluşturmak ve bunları çalıştırmak (ve tabii ki eski kabını kaldırın) doğru kanca oluşturur. Sonraki: github, ana sunucuya basıldıktan sonra rastgele uç nokta bağlantısına POST isteği oluşturmaya izin veren web kancalarına sahiptir. Bu yüzden, bu uç nokta olacak ve sadece HOST makinesindeki repo A'da 'git pull' çalıştıracak olan dockerized hizmet B'yi yaratmayacağım (önemli: 'git pull' komutu HOST ortamında çalıştırılmalıdır - B ortamında değil B'nin içinde yeni A konteyneri çalıştıramaz ...)