จะรันเชลล์สคริปต์บนโฮสต์จากคอนเทนเนอร์นักเทียบท่าได้อย่างไร


95

จะควบคุมโฮสต์จากคอนเทนเนอร์นักเทียบท่าได้อย่างไร

ตัวอย่างเช่นวิธีดำเนินการคัดลอกไปยังโฮสต์ bash script?


8
นั่นจะไม่ตรงข้ามกับการแยกโฮสต์จากนักเทียบท่าใช่ไหม
Marcus Müller

31
ใช่. แต่บางครั้งก็จำเป็น
Alex Ushakov


ไม่แน่ใจเกี่ยวกับ "โฮสต์ควบคุม" แต่เมื่อเร็ว ๆ นี้ฉันได้รับการพูดคุยจากนักวิทยาศาสตร์ข้อมูลที่ใช้นักเทียบท่าเพื่อเรียกใช้สคริปต์เพื่อประมวลผลปริมาณงานขนาดใหญ่ (โดยใช้ GPU ที่ติดตั้ง AWS) และส่งผลลัพธ์ไปยังโฮสต์ กรณีการใช้งานที่น่าสนใจมาก โดยพื้นฐานแล้วสคริปต์ที่มาพร้อมกับสภาพแวดล้อมการดำเนินการที่เชื่อถือได้ขอบคุณนักเทียบท่า
KCD

@KCD และทำไมพวกเขาถึงชอบ app-containerization ผ่าน docker แทนที่จะใช้ system-level container (LXC)?
Alex Ushakov

คำตอบ:


28

นั่นขึ้นอยู่กับสิ่งที่คุณต้องการให้สคริปต์ทุบตีทำ!

ตัวอย่างเช่นหากสคริปต์ bash เพียงแค่สะท้อนเอาต์พุตบางส่วนคุณก็ทำได้

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

ความเป็นไปได้อีกประการหนึ่งคือคุณต้องการให้สคริปต์ทุบตีติดตั้งซอฟต์แวร์บางตัว - พูดสคริปต์เพื่อติดตั้งนักเทียบท่าเขียน คุณสามารถทำสิ่งที่ชอบ

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

แต่ ณ จุดนี้คุณจะต้องรู้อย่างใกล้ชิดว่าสคริปต์กำลังทำอะไรอยู่เพื่ออนุญาตสิทธิ์เฉพาะที่จำเป็นบนโฮสต์ของคุณจากภายในคอนเทนเนอร์


1
ฉันมีความคิดที่จะสร้างคอนเทนเนอร์ที่เชื่อมต่อกับโฮสต์และสร้างคอนเทนเนอร์ใหม่
Alex Ushakov

1
นักเทียบท่าดูเหมือนจะไม่ชอบเมาท์ญาติของคุณ ควรใช้งานได้docker run --rm -v $(pwd)/mybashscript.sh:/work/mybashscript.sh ubuntu /work/mybashscript.sh
KCD

5
บรรทัดแรกเริ่มคอนเทนเนอร์อูบุนตูใหม่และติดตั้งสคริปต์ที่สามารถอ่านได้ ไม่อนุญาตให้คอนเทนเนอร์เข้าถึงระบบไฟล์โฮสต์เป็นต้น บรรทัดที่สองแสดงโฮสต์/usr/binไปยังคอนเทนเนอร์ ไม่ว่าในกรณีใดคอนเทนเนอร์จะไม่สามารถเข้าถึงระบบโฮสต์ได้อย่างสมบูรณ์ บางทีฉันอาจจะผิด แต่ดูเหมือนว่าจะเป็นคำตอบที่ไม่ดีสำหรับคำถามที่ไม่ดี
พอล

3
พอใช้ - คำถามค่อนข้างคลุมเครือ คำถามไม่ได้ถามถึง "สิทธิ์เข้าถึงระบบโฮสต์โดยสมบูรณ์" ตามที่อธิบายไว้หากสคริปต์ bash มีไว้เพื่อสะท้อนเอาต์พุตบางส่วนเท่านั้นก็จะไม่จำเป็นต้องเข้าถึงระบบไฟล์โฮสต์ สำหรับตัวอย่างที่สองของฉันซึ่งกำลังติดตั้ง Docker-compose สิทธิ์เดียวที่คุณต้องการคือการเข้าถึงไดเร็กทอรี bin ที่เก็บไบนารี ดังที่ฉันได้กล่าวไว้ในตอนต้น - ในการทำสิ่งนี้คุณจะต้องมีแนวคิดที่เฉพาะเจาะจงมากเกี่ยวกับสิ่งที่สคริปต์กำลังทำอยู่เพื่อให้ได้สิทธิ์ที่ถูกต้อง
Paul Becotte

1
พยายามแล้วสคริปต์จะทำงานในคอนเทนเนอร์ไม่ใช่บนโฮสต์
All2Pie

60

วิธีแก้ปัญหาที่ฉันใช้คือเชื่อมต่อกับโฮสต์SSHและดำเนินการคำสั่งดังนี้:

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

อัปเดต

เนื่องจากคำตอบนี้ยังคงได้รับการโหวตฉันจึงขอเตือน (และขอแนะนำเป็นอย่างยิ่ง) ว่าบัญชีที่ใช้ในการเรียกใช้สคริปต์ควรเป็นบัญชีที่ไม่มีสิทธิ์ใด ๆ เลย แต่จะเรียกใช้สคริปต์นั้นเป็นsudo(ซึ่งสามารถเป็นได้ ทำจากsudoersไฟล์)


ในฐานะที่เป็นวิธีแก้ปัญหาอื่นคอนเทนเนอร์สามารถส่งออกชุดคำสั่งและโฮสต์สามารถรันได้หลังจากที่คอนเทนเนอร์ออก: eval $ (docker run --rm -it container_name_to_output script)
parity3

ฉันต้องการเรียกใช้บรรทัดคำสั่งบนโฮสต์จากภายในคอนเทนเนอร์ Docker แต่เมื่อฉันเข้าไปในคอนเทนเนอร์sshไม่พบ คุณมีคำแนะนำอื่น ๆ หรือไม่?
Ron Rosenfeld

@RonRosenfeld คุณใช้อิมเมจ Docker ตัวไหน ในกรณีของ debian / ubuntu ให้เรียกใช้สิ่งนี้: apt update && apt install openssh-client.
Mohammed Noureldin

มันจะเป็นอะไรก็ได้ที่ติดตั้งบน Synology NAS ของฉัน ฉันจะบอกได้อย่างไร?
Ron Rosenfeld

@RonRosenfeld ขอโทษที่ฉันไม่เข้าใจว่าคุณหมายถึงอะไร
Mohammed Noureldin

52

ใช้ท่อที่มีชื่อ บนระบบปฏิบัติการโฮสต์สร้างสคริปต์เพื่อวนซ้ำและอ่านคำสั่งจากนั้นคุณเรียกใช้ eval

ให้ Docker container อ่านไปป์ที่มีชื่อนั้น

เพื่อให้สามารถเข้าถึงท่อได้คุณต้องติดตั้งผ่านโวลุ่ม

สิ่งนี้คล้ายกับกลไก SSH (หรือวิธีการใช้ซ็อกเก็ตที่คล้ายกัน) แต่ จำกัด คุณไว้ที่อุปกรณ์โฮสต์ซึ่งน่าจะดีกว่า นอกจากนี้คุณไม่จำเป็นต้องส่งผ่านข้อมูลการรับรองความถูกต้อง

คำเตือนเดียวของฉันคือระมัดระวังว่าทำไมคุณถึงทำเช่นนี้ เป็นสิ่งที่ต้องทำโดยสิ้นเชิงหากคุณต้องการสร้างวิธีการอัปเกรดด้วยตนเองด้วยการป้อนข้อมูลของผู้ใช้หรืออะไรก็ตาม แต่คุณอาจไม่ต้องการเรียกคำสั่งเพื่อรับข้อมูลการกำหนดค่าบางอย่างเนื่องจากวิธีที่เหมาะสมคือการส่งผ่านเป็น args / volume เป็นนักเทียบท่า นอกจากนี้โปรดระมัดระวังเกี่ยวกับความจริงที่ว่าคุณกำลังหลบหลีกดังนั้นเพียงแค่คิดแบบจำลองการอนุญาต

บางส่วนของคำตอบอื่น ๆ เช่นการเรียกใช้สคริปต์ภายใต้ไดรฟ์ข้อมูลจะไม่ทำงานโดยทั่วไปเนื่องจากจะไม่สามารถเข้าถึงทรัพยากรระบบทั้งหมดได้ แต่อาจเหมาะสมกว่าขึ้นอยู่กับการใช้งานของคุณ


14
ความสนใจ: นี่เป็นคำตอบที่ถูกต้อง / ดีที่สุดและต้องการคำชมอีกเล็กน้อย คำตอบอื่น ๆ คือการถามว่า "คุณกำลังพยายามทำอะไร" และสร้างข้อยกเว้นสำหรับสิ่งต่างๆ ฉันมีกรณีการใช้งานที่เฉพาะเจาะจงมากซึ่งต้องการให้ฉันสามารถทำสิ่งนี้ได้และนี่เป็นคำตอบเดียวที่ดี SSH ข้างต้นจะต้องมีการลดมาตรฐานความปลอดภัย / ไฟร์วอลล์และนักเทียบท่าที่รันสิ่งต่าง ๆ ก็ผิดพลาด ขอบคุณสำหรับสิ่งนี้. ฉันคิดว่าสิ่งนี้ไม่ได้รับการโหวตมากนักเพราะไม่ใช่คำตอบคัดลอก / วางแบบธรรมดา แต่นี่คือคำตอบ +100 คะแนนจากฉันถ้าฉันทำได้
Farley

3
สำหรับผู้ที่กำลังมองหาข้อมูลเพิ่มเติมคุณสามารถใช้สคริปต์ต่อไปนี้ที่ทำงานบนเครื่องโฮสต์: unix.stackexchange.com/a/369465แน่นอนว่าคุณจะต้องเรียกใช้ด้วย 'nohup' และสร้างเสื้อคลุมหัวหน้างานบางประเภท เพื่อรักษามันให้คงอยู่ (อาจจะใช้ cron job ก็ได้: P)
sucotronic

7
นี่อาจเป็นคำตอบที่ดี อย่างไรก็ตามจะดีกว่ามากถ้าคุณให้รายละเอียดเพิ่มเติมและคำอธิบายบรรทัดคำสั่งเพิ่มเติม เป็นไปได้อย่างละเอียด?
Mohammed Noureldin

5
โหวตแล้วได้ผล! สร้างไปป์ที่ตั้งชื่อโดยใช้ 'mkfifo host_executor_queue' ที่ต่อโวลุ่ม จากนั้นในการเพิ่มผู้บริโภคซึ่งรันคำสั่งที่ใส่ลงในคิวเป็นเชลล์ของโฮสต์ให้ใช้ 'tail -f host_executor_queue | ช & '. เครื่องหมาย & ในตอนท้ายทำให้มันทำงานในพื้นหลัง สุดท้ายในการพุชคำสั่งลงในคิวให้ใช้ 'echo touch foo> host_executor_queue' - การทดสอบนี้สร้างไฟล์ temp foo ที่โฮมไดเร็กทอรี หากคุณต้องการให้ผู้บริโภคเริ่มต้นเมื่อเริ่มต้นระบบให้ใส่ '@reboot tail -f host_executor_queue | sh & 'ใน crontab เพียงเพิ่มเส้นทางสัมพัทธ์ไปที่ host_executor_queue
skybunk

1
ใครช่วยแก้ไขคำตอบด้วยโค้ดตัวอย่างได้ไหม
Lucas Pottersky

6

หากคุณไม่กังวลเกี่ยวกับความปลอดภัยและคุณต้องการเริ่มต้น Docker container บนโฮสต์จากภายใน docker container อื่นเช่น OP คุณสามารถแชร์เซิร์ฟเวอร์ Docker ที่ทำงานบนโฮสต์กับ Docker container ได้โดยแชร์ซ็อกเก็ตสำหรับฟัง

โปรดดูhttps://docs.docker.com/engine/security/security/#docker-daemon-attack-surfaceและดูว่าการยอมรับความเสี่ยงส่วนบุคคลของคุณอนุญาตให้ใช้กับแอปพลิเคชันนี้หรือไม่

คุณสามารถทำได้โดยเพิ่ม Volume args ต่อไปนี้ในคำสั่ง start ของคุณ

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

หรือโดยการแชร์ /var/run/docker.sock ภายในไฟล์นักเทียบท่าของคุณเขียนดังนี้:

version: '3'

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

เมื่อคุณรันคำสั่ง docker start ภายในคอนเทนเนอร์นักเทียบท่าของคุณเซิร์ฟเวอร์นักเทียบท่าที่ทำงานบนโฮสต์ของคุณจะเห็นคำขอและจัดเตรียมคอนเทนเนอร์พี่น้อง

เครดิต: http://jpetazzo.github.io/2015/09/03/do-not-use-docker-in-docker-for-ci/


1
พิจารณาว่าต้องติดตั้งนักเทียบท่าในคอนเทนเนอร์มิฉะนั้นคุณจะต้องติดตั้งโวลุ่มสำหรับไบนารีนักเทียบท่าด้วย (เช่น/usr/bin/docker:/usr/bin/docker)
Gerry

1
โปรดระมัดระวังเมื่อติดตั้งซ็อกเก็ตDocker
DatGuyKaj

@DatGuyKaj ขอบคุณฉันได้แก้ไขคำตอบของฉันเพื่อสะท้อนปัญหาที่ระบุโดยทรัพยากรของคุณ
Matt Bucci

สิ่งนี้ไม่ตอบคำถามซึ่งเกี่ยวกับการเรียกใช้สคริปต์บนโฮสต์ไม่ใช่ในคอนเทนเนอร์
Brandon

5

คำตอบนี้เป็นเพียงโซลูชันของ Bradford Medeiros ที่มีรายละเอียดมากขึ้นซึ่งสำหรับฉันแล้วก็กลายเป็นคำตอบที่ดีที่สุดดังนั้นเครดิตจึงไปที่เขา

ในคำตอบของเขาเขาอธิบายว่าต้องทำอะไร ( ตั้งชื่อไปป์ ) แต่ไม่ใช่ว่าจะทำอย่างไร

ฉันต้องยอมรับว่าฉันไม่รู้ว่าท่อชื่ออะไรในตอนที่ฉันอ่านวิธีแก้ปัญหาของเขา ดังนั้นฉันจึงพยายามที่จะนำมันไปใช้ (แม้ว่ามันจะง่ายมาก) แต่ฉันก็ทำได้สำเร็จดังนั้นฉันจึงยินดีที่จะช่วยอธิบายว่าฉันทำได้อย่างไร ดังนั้นประเด็นของคำตอบของฉันก็คือการให้รายละเอียดคำสั่งที่คุณต้องใช้เพื่อให้มันใช้งานได้ แต่อีกครั้งเครดิตจะไปที่เขา

ตอนที่ 1 - การทดสอบแนวคิดท่อที่มีชื่อโดยไม่มีนักเทียบท่า

บนโฮสต์หลักให้เลือกโฟลเดอร์ที่คุณต้องการใส่ไฟล์ไปป์ที่มีชื่อของคุณเช่น/path/to/pipe/และชื่อไปป์เป็นต้นmypipeจากนั้นเรียกใช้:

mkfifo /path/to/pipe/mypipe

ท่อถูกสร้างขึ้น ประเภท

ls -l /path/to/pipe/mypipe 

และตรวจสอบสิทธิ์การเข้าถึงที่ขึ้นต้นด้วย "p" เช่น

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

ตอนนี้เรียกใช้:

tail -f /path/to/pipe/mypipe

ขณะนี้เทอร์มินัลกำลังรอข้อมูลที่จะส่งไปยังท่อนี้

ตอนนี้เปิดหน้าต่างเทอร์มินัลอื่น

แล้วเรียกใช้:

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

ตรวจสอบเทอร์มินัลแรก (เครื่องที่มีtail -f) ควรแสดงคำว่า "hello world"

ตอนที่ 2 - เรียกใช้คำสั่งผ่านท่อ

บนคอนเทนเนอร์โฮสต์แทนที่จะเรียกใช้tail -fซึ่งเพียงแค่ส่งออกสิ่งที่ส่งเป็นอินพุตให้รันคำสั่งนี้ซึ่งจะเรียกใช้เป็นคำสั่ง:

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

จากนั้นลองรันจากเทอร์มินัลอื่น:

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

กลับไปที่เทอร์มินัลแรกและคุณจะเห็นผลลัพธ์ของls -lคำสั่ง

ตอนที่ 3 - ทำให้ฟังตลอดไป

คุณอาจสังเกตเห็นว่าในส่วนก่อนหน้านี้ทันทีหลังจากที่ls -lเอาต์พุตปรากฏขึ้นมันจะหยุดฟังคำสั่ง

แทนที่จะeval "$(cat /path/to/pipe/mypipe)"เรียกใช้:

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

(คุณไม่สามารถทำได้)

ตอนนี้คุณสามารถส่งคำสั่งได้ไม่ จำกัด จำนวนทีละคำสั่งคำสั่งทั้งหมดจะถูกดำเนินการไม่ใช่แค่คำสั่งแรก

ตอนที่ 4 - ทำให้มันใช้งานได้แม้จะเกิดการรีบูต

ข้อแม้เดียวคือหากโฮสต์ต้องรีบูตลูป "while" จะหยุดทำงาน

เพื่อจัดการกับการรีบูตนี่คือสิ่งที่ฉันได้ทำ:

ใส่while true; do eval "$(cat /path/to/pipe/mypipe)"; doneไฟล์ที่เรียกว่าexecpipe.shมี#!/bin/bashส่วนหัว

อย่าลืมchmod +xมัน

เพิ่มลงใน crontab โดยเรียกใช้

crontab -e

แล้วเพิ่ม

@reboot /path/to/execpipe.sh

ณ จุดนี้ให้ทดสอบ: รีบูตเซิร์ฟเวอร์ของคุณและเมื่อมีการสำรองข้อมูลให้สะท้อนคำสั่งบางคำลงในไพพ์และตรวจสอบว่ามีการดำเนินการหรือไม่ แน่นอนคุณไม่สามารถเห็นผลลัพธ์ของคำสั่งดังนั้นls -lจะไม่ช่วย แต่touch somefileจะช่วยได้

อีกทางเลือกหนึ่งคือการแก้ไขสคริปต์เพื่อใส่เอาต์พุตในไฟล์เช่น:

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

ตอนนี้คุณสามารถเรียกใช้ls -lและเอาต์พุต (ทั้ง stdout และ stderr ที่ใช้&>ใน bash) ควรอยู่ใน output.txt

ตอนที่ 5 - ทำให้มันใช้งานได้กับนักเทียบท่า

หากคุณใช้ทั้ง docker compose และ dockerfile เหมือนที่ฉันทำนี่คือสิ่งที่ฉันทำ:

สมมติว่าคุณต้องการติดตั้งโฟลเดอร์หลักของ mypipe /hostpipeในคอนเทนเนอร์ของคุณ

เพิ่มสิ่งนี้:

VOLUME /hostpipe

ใน dockerfile ของคุณเพื่อสร้างจุดเชื่อมต่อ

จากนั้นเพิ่มสิ่งนี้:

volumes:
   - /path/to/pipe:/hostpipe

ในไฟล์นักเทียบท่าของคุณเขียนเพื่อติดตั้ง / path / to / pipe as / hostpipe

รีสตาร์ทคอนเทนเนอร์นักเทียบท่าของคุณ

ตอนที่ 6 - การทดสอบ

ดำเนินการในคอนเทนเนอร์นักเทียบท่าของคุณ:

docker exec -it <container> bash

เข้าไปในโฟลเดอร์ mount และตรวจสอบว่าคุณสามารถเห็นท่อ:

cd /hostpipe && ls -l

ตอนนี้ลองเรียกใช้คำสั่งจากภายในคอนเทนเนอร์:

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

และมันควรจะทำงาน!

คำเตือน: หากคุณมีโฮสต์ OSX (Mac OS) และคอนเทนเนอร์ Linux มันจะไม่ทำงาน (คำอธิบายที่นี่https://stackoverflow.com/a/43474708/10018801และปัญหาที่นี่https://github.com/docker / for-mac / issue / 483 ) เนื่องจากการใช้งานไปป์ไม่เหมือนกันดังนั้นสิ่งที่คุณเขียนลงในไพพ์จาก Linux สามารถอ่านได้โดย Linux เท่านั้นและสิ่งที่คุณเขียนลงในไพพ์จาก Mac OS สามารถอ่านได้โดย a Mac OS (ประโยคนี้อาจไม่ถูกต้องมากนัก แต่โปรดทราบว่ามีปัญหาข้ามแพลตฟอร์มอยู่)

ตัวอย่างเช่นเมื่อฉันเรียกใช้การตั้งค่านักเทียบท่าใน DEV จากคอมพิวเตอร์ Mac OS ไปป์ที่มีชื่อตามที่อธิบายไว้ข้างต้นไม่ทำงาน แต่ในการจัดเตรียมและการผลิตฉันมีโฮสต์ Linux และคอนเทนเนอร์ Linux และทำงานได้อย่างสมบูรณ์

ตอนที่ 7 - ตัวอย่างจากคอนเทนเนอร์ Node.JS

นี่คือวิธีที่ฉันส่งคำสั่งจาก node js container ไปยังโฮสต์หลักและดึงเอาท์พุท:

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);

ใช้งานได้ดี ความปลอดภัยล่ะ ฉันต้องการใช้สิ่งนี้เพื่อเริ่ม / หยุดคอนเทนเนอร์นักเทียบท่าจากภายในคอนเทนเนอร์ที่กำลังทำงานอยู่? ฉันเพิ่งสร้างนักเทียบท่าโดยไม่มีสิทธิพิเศษใด ๆ ยกเว้นการเรียกใช้คำสั่งนักเทียบท่าหรือไม่?
Kristof van Woensel

4

เขียนเซิร์ฟเวอร์ python แบบธรรมดาที่ฟังบนพอร์ต (พูด 8080) ผูกพอร์ต -p 8080: 8080 กับคอนเทนเนอร์ส่งคำขอ HTTP ไปยัง localhost: 8080 เพื่อขอให้เซิร์ฟเวอร์ python ที่เรียกใช้เชลล์สคริปต์ด้วยป๊อปเพนเรียกใช้ curl หรือ การเขียนโค้ดเพื่อสร้างคำขอ 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()

IMO นี่คือคำตอบที่ดีที่สุด การเรียกใช้คำสั่งตามอำเภอใจบนเครื่องโฮสต์ต้องทำผ่าน API บางประเภท (เช่น REST) นี่เป็นวิธีเดียวที่สามารถบังคับใช้ความปลอดภัยและสามารถควบคุมกระบวนการทำงานได้อย่างเหมาะสม (เช่นการฆ่าการจัดการ stdin, stdout, exit-code และอื่น ๆ ) แน่นอนว่ามันจะดีถ้า API นี้สามารถทำงานใน Docker ได้ แต่โดยส่วนตัวแล้วฉันไม่คิดที่จะเรียกใช้บนโฮสต์โดยตรง
barney765

2

ความขี้เกียจของฉันทำให้ฉันพบวิธีแก้ปัญหาที่ง่ายที่สุดที่ไม่ได้เผยแพร่เป็นคำตอบที่นี่

มันจะขึ้นอยู่กับบทความดีดีจากjuggery luc

สิ่งที่คุณต้องทำเพื่อให้ได้เชลล์เต็มไปยังโฮสต์ linux ของคุณจากภายในคอนเทนเนอร์นักเทียบท่าของคุณคือ:

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

คำอธิบาย:

--privileged: ให้สิทธิ์เพิ่มเติมกับคอนเทนเนอร์ทำให้คอนเทนเนอร์สามารถเข้าถึงอุปกรณ์ของโฮสต์ (/ dev)

--pid = host: อนุญาตให้คอนเทนเนอร์ใช้แผนผังกระบวนการของโฮสต์ Docker (VM ที่ Docker daemon กำลังทำงานอยู่) ยูทิลิตี้ nsenter: อนุญาตให้รันกระบวนการในเนมสเปซที่มีอยู่ (บล็อคส่วนประกอบที่ให้การแยกกับคอนเทนเนอร์)

nsenter (-t 1 -m -u -n -i sh) อนุญาตให้รันกระบวนการ sh ในบริบทการแยกเดียวกันกับกระบวนการที่มี PID 1 จากนั้นคำสั่งทั้งหมดจะจัดเตรียมเชลล์ sh แบบโต้ตอบใน VM

การตั้งค่านี้มีผลกระทบด้านความปลอดภัยที่สำคัญและควรใช้ด้วยความระมัดระวัง (ถ้ามี)


1
docker run --detach-keys="ctrl-p" -it -v /:/mnt/rootdir --name testing busybox
# chroot /mnt/rootdir
# 

3
แม้ว่าคำตอบนี้อาจช่วยแก้ปัญหาของ OP ได้ แต่ขอแนะนำให้คุณอธิบายวิธีการทำงานและสาเหตุที่ช่วยแก้ปัญหาได้ ซึ่งจะช่วยให้นักพัฒนาซอฟต์แวร์รายใหม่เข้าใจว่าเกิดอะไรขึ้นและจะแก้ไขปัญหานี้และปัญหาที่คล้ายกันได้อย่างไร ขอบคุณที่ร่วมให้ข้อมูล!
Caleb Kleveter

1

ฉันมีวิธีง่ายๆ

ขั้นตอนที่ 1: ติดตั้ง /var/run/docker.sock:/var/run/docker.sock (ดังนั้นคุณจะสามารถดำเนินการคำสั่งนักเทียบท่าภายในคอนเทนเนอร์ของคุณได้)

ขั้นตอนที่ 2: ดำเนินการด้านล่างนี้ภายในคอนเทนเนอร์ของคุณ ส่วนสำคัญที่นี่คือ (- โฮสต์เครือข่ายเนื่องจากจะดำเนินการจากบริบทโฮสต์)

นักเทียบท่าวิ่ง -i --rm - โฮสต์เครือข่าย -v /opt/test.sh:/test.sh อัลไพน์: 3.7 sh /test.sh

test.sh ควรมีคำสั่งบางอย่าง (ifconfig, netstat ฯลฯ ... ) สิ่งที่คุณต้องการ ตอนนี้คุณจะสามารถรับเอาต์พุตบริบทของโฮสต์ได้


2
ตามเอกสารอย่างเป็นทางการของนักเทียบท่าเกี่ยวกับระบบเครือข่ายโดยใช้เครือข่ายโฮสต์ "อย่างไรก็ตามด้วยวิธีอื่น ๆ ทั้งหมดเช่นพื้นที่จัดเก็บเนมสเปซของกระบวนการและเนมสเปซของผู้ใช้กระบวนการนี้จะแยกออกจากโฮสต์" ตรวจสอบ - docs.docker.com/network/network-tutorial-host
Peter Mutisya

0

ตามที่มาร์คัสเตือนนักเทียบท่านั้นโดยพื้นฐานแล้วการแยกกระบวนการ เริ่มต้นด้วยนักเทียบท่า 1.8 คุณสามารถคัดลอกไฟล์ได้ทั้งสองวิธีระหว่างโฮสต์และคอนเทนเนอร์ดูเอกสารของdocker cp

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

เมื่อคัดลอกไฟล์แล้วคุณสามารถเรียกใช้ในเครื่องได้


1
ฉันรู้ว่ามัน จะเรียกใช้สคริปต์นี้จากภายในคอนเทนเนอร์นักเทียบท่าได้อย่างไร
Alex Ushakov

1
ซ้ำกันของstackoverflow.com/questions/31720935/… ?
user2915097

2
@ AlexUshakov: ไม่มีทาง การทำเช่นนั้นจะทำลายข้อดีของนักเทียบท่าได้มาก อย่าทำ อย่าไปลองเลย พิจารณาสิ่งที่คุณต้องทำใหม่
Marcus Müller

ดูฟอรัม
user2915097

1
คุณสามารถรับค่าของตัวแปรบางตัวในคอนเทนเนอร์ของคุณบนโฮสต์ได้ตลอดเวลาสิ่งที่ต้องการmyvalue=$(docker run -it ubuntu echo $PATH)และทดสอบเป็นประจำในเชลล์สคริปต์ (แน่นอนคุณจะใช้อย่างอื่นที่ไม่ใช่ $ PATH เป็นเพียงตัวอย่างเท่านั้น) เมื่อมัน เป็นค่าเฉพาะบางอย่างคุณเปิดสคริปต์ของคุณ
user2915097

0

คุณสามารถใช้แนวคิดไพพ์แต่ใช้ไฟล์บนโฮสต์และfswatchเพื่อบรรลุเป้าหมายในการเรียกใช้สคริปต์บนเครื่องโฮสต์จากคอนเทนเนอร์นักเทียบท่า เช่นนั้น (ใช้ด้วยความเสี่ยงของคุณเอง):

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

ในตัวอย่างนี้ภายในคอนเทนเนอร์ส่งคำสั่งไปที่ / dev / command_pipe ดังนี้:

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

บนโฮสต์คุณสามารถตรวจสอบได้ว่าสร้างเครือข่ายหรือไม่:

$ docker network ls | grep test2
8e029ec83afe        test2.network.com                            bridge              local

-7

หากต้องการขยายการ ตอบสนองของผู้ใช้ 2915097 :

แนวคิดของการแยกคือการสามารถ จำกัด สิ่งที่แอปพลิเคชัน / กระบวนการ / คอนเทนเนอร์ (ไม่ว่าคุณจะอยู่ที่มุมใดก็ตาม) สามารถทำกับระบบโฮสต์ได้อย่างชัดเจน ดังนั้นความสามารถในการคัดลอกและเรียกใช้ไฟล์จะทำลายแนวคิดทั้งหมด

ใช่. แต่บางครั้งก็จำเป็น

ไม่เป็นเช่นนั้นหรือ Docker ไม่ใช่สิ่งที่ถูกต้องที่จะใช้ สิ่งที่คุณควรทำคือการประกาศอินเตอร์เฟซที่ชัดเจนสำหรับสิ่งที่คุณต้องการจะทำ (เช่นการปรับปรุงการกำหนดค่าโฮสต์) และเขียนลูกค้าน้อยที่สุด / เซิร์ฟเวอร์ที่จะทำตรงนั้นและไม่มีอะไรเพิ่มเติม อย่างไรก็ตามโดยทั่วไปสิ่งนี้ดูเหมือนจะไม่เป็นที่ต้องการมากนัก ในหลาย ๆ กรณีคุณควรคิดใหม่และกำจัดความต้องการนั้นให้สิ้นซาก นักเทียบท่าเกิดขึ้นเมื่อโดยพื้นฐานแล้วทุกอย่างเป็นบริการที่สามารถเข้าถึงได้โดยใช้โปรโตคอลบางอย่าง ฉันไม่สามารถนึกถึงกรณีการใช้งานที่เหมาะสมของคอนเทนเนอร์ Docker ที่ได้รับสิทธิ์ในการดำเนินการตามอำเภอใจบนโฮสต์


ฉันมีกรณีการใช้งาน: ฉันมีบริการ Dockerized A(src บน github) ในArepo ฉันสร้าง hooks ที่เหมาะสมซึ่งหลังจากคำสั่ง 'git pull' สร้างอิมเมจนักเทียบท่าใหม่และเรียกใช้ (และลบคอนเทนเนอร์เก่าแน่นอน) ถัดไป: github มี web-hooks ซึ่งอนุญาตให้สร้างคำขอ POST ไปยังลิงก์ปลายทางโดยพลการหลังจากกดบน master ดังนั้นฉันจึงไม่ต้องการสร้างบริการ Dockerized B ซึ่งจะเป็นจุดสิ้นสุดนั้นและจะเรียกใช้ 'git pull' ใน repo A ในเครื่อง HOST เท่านั้น (สำคัญ: คำสั่ง 'git pull' ต้องดำเนินการในสภาพแวดล้อม HOST ไม่ใช่ในสภาพแวดล้อม B เพราะ B ไม่สามารถเรียกใช้คอนเทนเนอร์ใหม่ A ภายใน B ... )
Kamil Kiełczewski

1
ปัญหา: ฉันต้องการไม่มีอะไรใน HOST ยกเว้น linux, git และ docker และฉันต้องการมีบริการ dockerizet A และบริการ B (ซึ่งในความเป็นจริง git-push handler ซึ่งเรียกใช้ git pull บน repo A หลังจากที่มีคนกดคอมไพล์บนมาสเตอร์) ดังนั้น git auto-deploy จึงเป็นปัญหาในการใช้งาน
Kamil Kiełczewski

@ KamilKiełczewskiฉันกำลังพยายามทำเหมือนเดิมคุณพบวิธีแก้ปัญหาหรือยัง?
user871784

1
การพูดว่า "ไม่ไม่ใช่อย่างนั้น" เป็นเรื่องที่แคบและสมมติว่าคุณรู้จักการใช้งานทุกกรณีในโลก กรณีการใช้งานของเรากำลังดำเนินการทดสอบ พวกเขาจำเป็นต้องรันในคอนเทนเนอร์เพื่อทดสอบสภาพแวดล้อมอย่างถูกต้อง แต่ด้วยลักษณะของการทดสอบพวกเขายังต้องเรียกใช้สคริปต์บนโฮสต์
Senica Gonzalez

1
สำหรับผู้ที่สงสัยว่าทำไมฉันจึงตอบ -7: a) ตกลงที่จะเข้าใจผิด ฉันผิดไป. ตกลงว่ามีการบันทึกไว้ที่นี่ b) ความคิดเห็นมีส่วนช่วยในการสร้างมูลค่า การลบคำตอบก็เป็นการลบคำตอบเช่นกัน c) มันยังคงก่อให้เกิดมุมมองที่ควรพิจารณา (อย่าแยกความโดดเดี่ยวของคุณถ้าคุณไม่จำเป็นต้องทำบางครั้งคุณต้องทำ)
Marcus Müller
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.