Bagaimana cara mengontrol host dari kontainer buruh pelabuhan?
Misalnya, bagaimana cara mengeksekusi skrip yang disalin ke host bash?
Bagaimana cara mengontrol host dari kontainer buruh pelabuhan?
Misalnya, bagaimana cara mengeksekusi skrip yang disalin ke host bash?
Jawaban:
Itu BENAR-BENAR tergantung pada apa yang Anda butuhkan untuk skrip bash itu!
Misalnya, jika skrip bash hanya menggemakan beberapa output, Anda dapat melakukannya
docker run --rm -v $(pwd)/mybashscript.sh:/mybashscript.sh ubuntu bash /mybashscript.sh
Kemungkinan lainnya adalah Anda ingin skrip bash menginstal beberapa perangkat lunak - katakanlah skrip untuk menginstal docker-compose. Anda bisa melakukan sesuatu seperti
docker run --rm -v /usr/bin:/usr/bin --privileged -v $(pwd)/mybashscript.sh:/mybashscript.sh ubuntu bash /mybashscript.sh
Tetapi pada titik ini Anda benar-benar harus mengetahui secara dekat apa yang dilakukan skrip untuk mengizinkan izin khusus yang diperlukan pada host Anda dari dalam penampung.
docker run --rm -v $(pwd)/mybashscript.sh:/work/mybashscript.sh ubuntu /work/mybashscript.sh
/usr/binke penampung. Dalam kedua kasus tersebut, penampung tidak memiliki akses penuh ke sistem host. Mungkin saya salah, tapi sepertinya jawaban yang buruk untuk pertanyaan yang buruk.
Solusi yang saya gunakan adalah menyambung ke host SSHdan menjalankan perintah seperti ini:
ssh -l ${USERNAME} ${HOSTNAME} "${SCRIPT}"
Karena jawaban ini terus mendapatkan suara, saya ingin mengingatkan (dan sangat merekomendasikan), bahwa akun yang digunakan untuk memanggil skrip haruslah akun tanpa izin sama sekali, tetapi hanya menjalankan skrip itu sebagai sudo(itu bisa dilakukan dari sudoersfile).
sshtidak ditemukan. Apakah Anda punya saran lain?
apt update && apt install openssh-client.
Menggunakan pipa bernama. Pada os host, buat skrip untuk mengulang dan membaca perintah, lalu Anda memanggil eval untuk itu.
Minta kontainer buruh pelabuhan membaca pipa bernama itu.
Untuk dapat mengakses pipa, Anda perlu memasangnya melalui volume.
Ini mirip dengan mekanisme SSH (atau metode berbasis soket serupa), tetapi membatasi Anda dengan benar ke perangkat host, yang mungkin lebih baik. Selain itu, Anda tidak perlu memberikan informasi otentikasi.
Satu-satunya peringatan saya adalah berhati-hati tentang mengapa Anda melakukan ini. Ini benar-benar sesuatu yang harus dilakukan jika Anda ingin membuat metode untuk meningkatkan sendiri dengan input pengguna atau apa pun, tetapi Anda mungkin tidak ingin memanggil perintah untuk mendapatkan beberapa data konfigurasi, karena cara yang tepat adalah meneruskannya sebagai args / volume ke buruh pelabuhan. Juga berhati-hatilah tentang fakta bahwa Anda mengelak, jadi pikirkan saja model izinnya.
Beberapa dari. Jawaban lain seperti menjalankan skrip. Di bawah volume tidak akan berfungsi secara umum karena mereka tidak akan memiliki akses ke sumber daya sistem penuh, tetapi mungkin lebih tepat tergantung pada penggunaan Anda.
Jika Anda tidak khawatir tentang keamanan dan Anda hanya ingin memulai kontainer pekerja galangan pada host dari dalam kontainer pekerja galangan lain seperti OP, Anda dapat berbagi server pekerja galangan yang berjalan pada tuan rumah dengan kontainer pekerja galangan dengan membagikan soket pendengarnya.
Silakan lihat https://docs.docker.com/engine/security/security/#docker-daemon-attack-surface dan lihat apakah toleransi risiko pribadi Anda memungkinkan hal ini untuk aplikasi khusus ini.
Anda dapat melakukan ini dengan menambahkan argumen volume berikut ke perintah mulai Anda
docker run -v /var/run/docker.sock:/var/run/docker.sock ...
atau dengan membagikan /var/run/docker.sock dalam file pembuatan buruh pelabuhan Anda seperti ini:
version: '3'
services:
ci:
command: ...
image: ...
volumes
- /var/run/docker.sock:/var/run/docker.sock
Saat Anda menjalankan perintah docker start dalam container docker Anda, server docker yang berjalan di host Anda akan melihat permintaan dan menyediakan container saudara.
kredit: http://jpetazzo.github.io/2015/09/03/do-not-use-docker-in-docker-for-ci/
/usr/bin/docker:/usr/bin/docker).
Jawaban ini hanyalah versi yang lebih rinci dari solusi Bradford Medeiros , yang bagi saya juga merupakan jawaban terbaik, jadi pujian diberikan kepadanya.
Dalam jawabannya, dia menjelaskan APA yang harus dilakukan ( dinamai pipa ) tetapi tidak persis BAGAIMANA melakukannya.
Saya harus mengakui bahwa saya tidak tahu apa yang dinamai pipa pada saat saya membaca solusinya. Jadi saya berjuang untuk menerapkannya (meskipun sebenarnya sangat sederhana), tetapi saya berhasil, jadi saya dengan senang hati membantu dengan menjelaskan bagaimana saya melakukannya. Jadi inti dari jawaban saya hanyalah merinci perintah yang perlu Anda jalankan untuk membuatnya berfungsi, tetapi sekali lagi, kredit diberikan kepadanya.
Di host utama, pilih folder tempat Anda ingin meletakkan file pipa bernama Anda, misalnya /path/to/pipe/dan nama pipa, misalnya mypipe, lalu jalankan:
mkfifo /path/to/pipe/mypipe
Pipa dibuat. Tipe
ls -l /path/to/pipe/mypipe
Dan periksa hak akses dimulai dengan "p", seperti
prw-r--r-- 1 root root 0 mypipe
Sekarang jalankan:
tail -f /path/to/pipe/mypipe
Terminal sekarang menunggu data untuk dikirim ke pipa ini
Sekarang buka jendela terminal lain.
Dan kemudian jalankan:
echo "hello world" > /path/to/pipe/mypipe
Periksa terminal pertama (yang dengan tail -f), itu harus menampilkan "hello world"
Pada penampung host, alih-alih berjalan tail -fyang hanya mengeluarkan apa pun yang dikirim sebagai masukan, jalankan perintah ini yang akan menjalankannya sebagai perintah:
eval "$(cat /path/to/pipe/mypipe)"
Kemudian, dari terminal lain, coba jalankan:
echo "ls -l" > /path/to/pipe/mypipe
Kembali ke terminal pertama dan Anda akan melihat hasil dari ls -lperintah tersebut.
Anda mungkin telah memperhatikan bahwa di bagian sebelumnya, tepat setelah ls -loutput ditampilkan, ia berhenti mendengarkan perintah.
Alih-alih eval "$(cat /path/to/pipe/mypipe)", jalankan:
while true; do eval "$(cat /path/to/pipe/mypipe)"; done
(Anda tidak dapat melakukannya)
Sekarang Anda dapat mengirim perintah dalam jumlah tak terbatas satu demi satu, semuanya akan dieksekusi, bukan hanya yang pertama.
Satu-satunya peringatan adalah jika host harus reboot, loop "while" akan berhenti bekerja.
Untuk menangani reboot, inilah yang telah saya lakukan:
Letakkan while true; do eval "$(cat /path/to/pipe/mypipe)"; donedalam file yang disebut execpipe.shdengan #!/bin/bashsundulan
Jangan lupa untuk chmod +xitu
Tambahkan ke crontab dengan menjalankan
crontab -e
Dan kemudian menambahkan
@reboot /path/to/execpipe.sh
Pada titik ini, ujilah: reboot server Anda, dan ketika sudah kembali, gema beberapa perintah ke dalam pipa dan periksa apakah sudah dijalankan. Tentu saja, Anda tidak dapat melihat keluaran dari perintah, jadi ls -ltidak akan membantu, tetapi touch somefileakan membantu.
Pilihan lainnya adalah memodifikasi skrip untuk meletakkan output dalam sebuah file, seperti:
while true; do eval "$(cat /path/to/pipe/mypipe)" &> /somepath/output.txt; done
Sekarang Anda dapat menjalankan ls -ldan output (baik stdout dan stderr yang digunakan &>di bash) harus dalam output.txt.
Jika Anda menggunakan docker compose dan dockerfile seperti yang saya lakukan, inilah yang telah saya lakukan:
Anggaplah Anda ingin memasang folder induk mypipe seperti /hostpipedi penampung Anda
Tambahkan ini:
VOLUME /hostpipe
di file dok Anda untuk membuat titik pemasangan
Kemudian tambahkan ini:
volumes:
- /path/to/pipe:/hostpipe
di file penulisan buruh pelabuhan Anda untuk memasang / path / ke / pipa sebagai / hostpipe
Mulai ulang kontainer buruh pelabuhan Anda.
Jalankan ke kontainer buruh pelabuhan Anda:
docker exec -it <container> bash
Masuk ke folder mount dan periksa apakah Anda dapat melihat pipa:
cd /hostpipe && ls -l
Sekarang coba jalankan perintah dari dalam penampung:
echo "touch this_file_was_created_on_main_host_from_a_container.txt" > /hostpipe/mypipe
Dan itu harus berhasil!
PERINGATAN: Jika Anda memiliki host OSX (Mac OS) dan wadah Linux, itu tidak akan berfungsi (penjelasan di sini https://stackoverflow.com/a/43474708/10018801 dan masalah di sini https://github.com/docker / for-mac / issues / 483 ) karena implementasi pipa tidak sama, jadi apa yang Anda tulis ke dalam pipa dari Linux hanya dapat dibaca oleh Linux dan apa yang Anda tulis ke pipa dari Mac OS hanya dapat dibaca oleh Mac OS (kalimat ini mungkin tidak terlalu akurat, tetapi perlu diketahui bahwa ada masalah lintas platform).
Misalnya, ketika saya menjalankan pengaturan buruh pelabuhan di DEV dari komputer Mac OS saya, pipa bernama seperti yang dijelaskan di atas tidak berfungsi. Tetapi dalam pementasan dan produksi, saya memiliki host Linux dan wadah Linux, dan berfungsi dengan sempurna.
Berikut adalah cara saya mengirim perintah dari node js container saya ke host utama dan mengambil hasilnya:
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);
Tulis server python server sederhana yang mendengarkan di port (katakanlah 8080), ikat port -p 8080: 8080 dengan kontainer, buat permintaan HTTP ke localhost: 8080 untuk meminta server python menjalankan skrip shell dengan popen, jalankan curl atau menulis kode untuk membuat permintaan 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()
Kemalasan saya membuat saya menemukan solusi termudah yang tidak dipublikasikan sebagai jawaban di sini.
Ini didasarkan pada artikel bagus oleh luc juggery .
Yang perlu Anda lakukan untuk mendapatkan shell penuh ke host linux Anda dari dalam container docker Anda adalah:
docker run --privileged --pid=host -it alpine:3.8 \
nsenter -t 1 -m -u -n -i sh
Penjelasan:
--privileged: memberikan izin tambahan ke container, ini memungkinkan container mendapatkan akses ke perangkat host (/ dev)
--pid = host: memungkinkan wadah untuk menggunakan pohon proses dari host Docker (VM tempat daemon Docker berjalan) utilitas nsenter: memungkinkan untuk menjalankan proses di ruang nama yang ada (blok bangunan yang menyediakan isolasi ke wadah)
nsenter (-t 1 -m -u -n -i sh) memungkinkan untuk menjalankan proses sh dalam konteks isolasi yang sama seperti proses dengan PID 1. Seluruh perintah kemudian akan menyediakan shell sh interaktif di VM
Pengaturan ini memiliki implikasi keamanan yang besar dan harus digunakan dengan hati-hati (jika ada).
docker run --detach-keys="ctrl-p" -it -v /:/mnt/rootdir --name testing busybox
# chroot /mnt/rootdir
#
Saya memiliki pendekatan sederhana.
Langkah 1: Pasang /var/run/docker.sock:/var/run/docker.sock (Jadi Anda akan dapat menjalankan perintah buruh pelabuhan di dalam wadah Anda)
Langkah 2: Jalankan ini di bawah di dalam wadah Anda. Bagian kuncinya di sini adalah ( --network host karena ini akan dijalankan dari konteks host)
docker run -i --rm --network host -v /opt/test.sh:/test.sh alpine: 3.7 sh /test.sh
test.sh harus berisi beberapa perintah (ifconfig, netstat dll ...) apapun yang Anda butuhkan. Sekarang Anda akan bisa mendapatkan keluaran konteks host.
Seperti yang diingatkan Marcus, buruh pelabuhan pada dasarnya adalah proses isolasi. Dimulai dengan buruh pelabuhan 1.8, Anda dapat menyalin file dua arah antara host dan wadah, lihat dokumendocker cp
https://docs.docker.com/reference/commandline/cp/
Setelah file disalin, Anda dapat menjalankannya secara lokal
myvalue=$(docker run -it ubuntu echo $PATH)dan mengujinya secara teratur di shell skrip (tentu saja, Anda akan menggunakan sesuatu selain $ PATH, hanya sekedar contoh), ketika itu adalah beberapa nilai tertentu, Anda meluncurkan skrip Anda
Anda dapat menggunakan konsep pipa, tetapi gunakan file pada host dan fswatch untuk mencapai tujuan menjalankan skrip pada mesin host dari kontainer buruh pelabuhan. Seperti itu (Gunakan dengan risiko Anda sendiri):
#! /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
Dalam contoh ini, di dalam wadah, kirim perintah ke / dev / command_pipe, seperti ini:
/home/test # echo 'docker network create test2.network.com' > /dev/command_pipe
Di host, Anda dapat memeriksa apakah jaringan telah dibuat:
$ docker network ls | grep test2
8e029ec83afe test2.network.com bridge local
Untuk memperluas user2915097 ini respon :
Ide isolasi adalah untuk dapat membatasi apa yang dapat dilakukan oleh aplikasi / proses / wadah (apapun sudut pandang Anda) terhadap sistem host dengan sangat jelas. Karenanya, dapat menyalin dan mengeksekusi file benar-benar akan merusak keseluruhan konsep.
Iya. Tapi terkadang perlu.
Tidak. Bukan itu masalahnya, atau Docker bukanlah hal yang tepat untuk digunakan. Apa yang harus Anda lakukan adalah mendeklarasikan antarmuka yang jelas untuk apa yang ingin Anda lakukan (misalnya memperbarui konfigurasi host), dan menulis klien / server minimal untuk melakukan hal itu dan tidak lebih. Secara umum, bagaimanapun, ini tampaknya tidak terlalu diinginkan. Dalam banyak kasus, Anda cukup memikirkan kembali pendekatan Anda dan menghapus kebutuhan itu. Docker muncul ketika pada dasarnya semuanya adalah layanan yang dapat dijangkau menggunakan beberapa protokol. Saya tidak dapat memikirkan kasus penggunaan yang tepat dari container Docker yang mendapatkan hak untuk mengeksekusi barang sewenang-wenang di host.
A(src di github). Dalam Arepo saya membuat hook yang tepat yang setelah perintah 'git pull' membuat image buruh pelabuhan baru dan menjalankannya (dan menghapus container lama tentunya). Berikutnya: github memiliki web-hooks yang memungkinkan pembuatan permintaan POST ke tautan titik akhir arbitrer setelah push on master. Jadi saya tidak akan membuat layanan dockerized B yang akan menjadi titik akhir itu dan yang hanya akan menjalankan 'git pull' di repo A di mesin HOST (penting: perintah 'git pull' harus dijalankan di lingkungan HOST - bukan di lingkungan B karena B tidak dapat menjalankan wadah baru A di dalam B ...)