วิธีส่งอาร์กิวเมนต์ไปยัง Shell Script ผ่านนักเทียบท่าทำงาน


133

ฉันยังใหม่กับโลกของนักเทียบท่า ฉันต้องเรียกใช้เชลล์สคริปต์ที่รับอาร์กิวเมนต์บรรทัดคำสั่งผ่านคอนเทนเนอร์นักเทียบท่า ตัวอย่าง: เชลล์สคริปต์ของฉันมีลักษณะดังนี้:

#!bin/bash
echo $1

Dockerfile มีลักษณะดังนี้:

FROM ubuntu:14.04
COPY ./file.sh /
CMD /bin/bash file.sh

ฉันไม่แน่ใจว่าจะส่งผ่านข้อโต้แย้งในขณะที่เรียกใช้คอนเทนเนอร์ได้อย่างไร

คำตอบ:


62

ใช้เหมือนกัน file.sh

#!/bin/bash
echo $1

สร้างอิมเมจโดยใช้ Dockerfile ที่มีอยู่:

docker build -t test .

เรียกใช้รูปภาพด้วยอาร์กิวเมนต์abcหรือxyzหรืออย่างอื่น

docker run -ti test /file.sh abc

docker run -ti test /file.sh xyz

27
ฉันคิดว่า ENTRYPOINT เป็นวิธีที่จะไปหากคุณไม่ต้องการให้ผู้ใช้ปลายทางรู้เกี่ยวกับ file.sh โดยตรง
greg.kindel

คุณจะเริ่มสคริปต์เช่นนี้docker run -ti test /file.sh abcได้อย่างไร docker run -ti test sh /file.sh abcผมรู้สึกว่าสคริปต์ที่เคยทำงานได้เนื่องจากมันควรจะเป็น sh หรือ / bin / sh จะทำงานอย่างถูกต้อง
Vamsidhar Muggulla

1
สำหรับใครที่มาที่นี่. เคล็ดลับ / usr / bin / env คือการกำหนดลักษณะที่เป็นทางเลือกซึ่งไม่ใช่ข้อกำหนดในการทำให้สิ่งนี้ใช้งานได้ ยัง #! บรรทัดระบุว่าล่ามใดที่จะใช้ buy default ดังนั้นจึงสามารถทำงานได้โดยการเรียกสคริปต์
61

167

ด้วยสคริปต์นี้ใน file.sh

#!/bin/bash
echo Your container args are: "$@"

และนี่ Dockerfile

FROM ubuntu:14.04
COPY ./file.sh /
ENTRYPOINT ["/file.sh"]

คุณควรจะสามารถ:

% docker build -t test .
% docker run test hello world
Your container args are: hello world

9
ถ้าคุณลืม "" around "/file.sh" เหมือนที่เคยทำก็จะใช้ไม่ได้
kev

6
ด้วยเหตุผลบางประการสิ่งนี้ใช้ไม่ได้กับENTRYPOINT ./file.sh
phil294

6
อย่าลืมchmod +x file.shตั้งค่าสถานะปฏิบัติการ
topskip

1
@kev รู้ไหมทำไมถึงเป็นแบบนั้น? อะไรคือความแตกต่างระหว่าง["/file.sh"]และ/file.shหรือแม้กระทั่ง[/file.sh]
Nitzankin

1
@Nitzankin ดูคำตอบของฉันว่าทำไมต้องจัดรูปแบบ json ที่เหมาะสม
BMitch

60

ด้วย Docker วิธีที่เหมาะสมในการส่งข้อมูลประเภทนี้คือผ่านตัวแปรสภาพแวดล้อม

ดังนั้นด้วย Dockerfile เดียวกันให้เปลี่ยนสคริปต์เป็น

#!/bin/bash
echo $FOO

หลังจากสร้างแล้วให้ใช้คำสั่งนักเทียบท่าต่อไปนี้:

docker run -e FOO="hello world!" test

20
เหตุใดจึงเป็นคำตอบที่ได้รับการโหวตสูงสุด Env vars เป็นอีกวิธีหนึ่งในการส่งผ่านข้อมูล แต่ OP ไม่ได้ถามอะไร และแน่นอนว่าไม่มีอะไรที่ไม่เหมาะสมอย่างแน่นอนเกี่ยวกับความปรารถนาของ OP ที่จะส่งต่อ args ไปยังคอนเทนเนอร์
เมฆบางส่วน

8
@PartlyCloudy ฉันคิดว่าคนชอบแบบนี้เพราะตั้งใจที่จะให้คำตอบที่ "เหมาะสม" แม้ว่าจะผิดอย่างเห็นได้ชัดก็ตาม หลักการออกแบบที่สำคัญของ Docker คือการให้ความสำคัญกับความเชื่อมากกว่าสามัญสำนึก
สิงหาคม

1
@augurar: เพื่อปรับปรุงคำตอบนี้คุณอาจอธิบายว่าทำไมคุณคิดว่าคำตอบนี้ "ผิดอย่างเห็นได้ชัด"
Emil Stenström

2
มีปัญหา XY มากมายถามเกี่ยวกับ SO เนื่องจาก OP ระบุว่าพวกเขายังใหม่สำหรับ Docker คำตอบที่สมเหตุสมผลจึงแสดงวิธีที่แนะนำในการบรรลุเป้าหมาย จึงทำให้ที่นี่ตอบโจทย์มาก.
colm.anseo

1
วิธีที่ง่ายที่สุดในการทำงานให้เสร็จแทนที่จะส่งผ่านตัวแปรเป็น build args และสิ่งที่ยุ่งเหยิง มีประโยชน์มากสำหรับการส่งผ่านความลับเป็นตัวแปรสภาพแวดล้อม
Arvind Sridharan

32

มีบางสิ่งที่โต้ตอบที่นี่:

  1. docker run your_image arg1 arg2จะเข้ามาแทนที่ค่าของกับCMD arg1 arg2นั่นเป็นการแทนที่ CMD อย่างสมบูรณ์โดยไม่ผนวกค่าเพิ่มเติมเข้าไป นี่คือเหตุผลที่คุณมักเห็นว่าdocker run some_image /bin/bashใช้ bash shell ในคอนเทนเนอร์

  2. เมื่อคุณมีทั้ง ENTRYPOINT และค่า CMD ที่กำหนดไว้นักเทียบท่าจะเริ่มคอนเทนเนอร์โดยการเชื่อมต่อทั้งสองและรันคำสั่งที่ต่อกัน ดังนั้นหากคุณกำหนดจุดเข้าfile.shใช้งานตอนนี้คุณสามารถเรียกใช้คอนเทนเนอร์ด้วย args เพิ่มเติมที่จะถูกส่งผ่านเป็น args ไปยังfile.shเพิ่มเติมที่จะส่งผ่านเป็นอาร์กิวเมนต์ไป

  3. จุดเข้าใช้งานและคำสั่งในนักเทียบท่ามีสองไวยากรณ์ไวยากรณ์สตริงที่จะเปิดเชลล์และไวยากรณ์ json ที่จะดำเนินการกับ exec เชลล์มีประโยชน์ในการจัดการสิ่งต่างๆเช่นการเปลี่ยนเส้นทาง IO การผูกคำสั่งหลายคำเข้าด้วยกัน (ด้วยสิ่งต่างๆเช่น&&) การแทนที่ตัวแปร ฯลฯ อย่างไรก็ตามเชลล์นั้นเข้ามาขัดขวางการจัดการสัญญาณ (หากคุณเคยเห็นการหน่วงเวลา 10 วินาทีเพื่อหยุด คอนเทนเนอร์มักเป็นสาเหตุ) และด้วยการเชื่อมจุดเข้าและคำสั่งเข้าด้วยกัน หากคุณกำหนดจุดเข้าใช้งานของคุณเป็นสตริงมันจะทำงาน/bin/sh -c "file.sh"ซึ่งใช้ได้เพียงอย่างเดียว แต่ถ้าคุณมีคำสั่งที่กำหนดเป็นสตริงด้วยเช่นกันคุณจะเห็นบางอย่างเช่น/bin/sh -c "file.sh" /bin/sh -c "arg1 arg2"คำสั่งถูกเรียกใช้งานภายในคอนเทนเนอร์ของคุณซึ่งไม่ค่อยดีนัก ดูตารางที่นี่สำหรับข้อมูลเพิ่มเติมเกี่ยวกับวิธีการทำงานของตัวเลือกทั้งสองนี้

  4. -cตัวเลือกเชลล์ใช้อาร์กิวเมนต์เดียวเท่านั้น ทุกอย่างหลังจากนั้นจะได้รับผ่านการเป็น$1,$2ฯลฯ ที่อาร์กิวเมนต์เดียว แต่ไม่เป็นเชลล์สคริปต์ที่ฝังตัวจนกว่าคุณจะผ่านไปอย่างชัดเจน args เช่น/bin/sh -c "file.sh $1 $2" "arg1" "arg2"จะใช้งานได้ แต่/bin/sh -c "file.sh" "arg1" "arg2"จะไม่file.shถูกเรียกว่าไม่มีอาร์กิวเมนต์

การออกแบบร่วมกันคือ:

FROM ubuntu:14.04
COPY ./file.sh /
RUN chmod 755 /file.sh
# Note the json syntax on this next line is strict, double quotes, and any syntax
# error will result in a shell being used to run the line.
ENTRYPOINT ["file.sh"]

จากนั้นคุณเรียกใช้ด้วย:

docker run your_image arg1 arg2

มีรายละเอียดเพิ่มเติมเกี่ยวกับเรื่องนี้ที่:


1
ฉันได้ทดลองตั้งค่าจุดเข้าใช้งาน["bash", "--login", "-c"]เพื่อรับแหล่งที่มา / etc / profile ในภาพ แต่ต่อมาก็สงสัยว่าทำไมไม่มีการส่ง args ไปยังเชลล์สคริปต์ที่ส่งไปยังนักเทียบท่า ... คำตอบของคุณเคลียร์ได้แล้วขอบคุณ !
Apteryx

23

สิ่งที่ฉันมีคือไฟล์สคริปต์ที่เรียกใช้สิ่งต่างๆ ไฟล์ scrip นี้อาจจะค่อนข้างซับซ้อน เรียกว่า "run_container" สคริปต์นี้รับอาร์กิวเมนต์จากบรรทัดคำสั่ง:

run_container p1 p2 p3

run_container อย่างง่ายอาจเป็น:

#!/bin/bash
echo "argc = ${#*}"
echo "argv = ${*}"

สิ่งที่ฉันต้องการทำคือหลังจาก "เทียบท่า" สิ่งนี้ฉันต้องการที่จะเริ่มต้นคอนเทนเนอร์นี้ด้วยพารามิเตอร์บนบรรทัดคำสั่งนักเทียบท่าดังนี้:

docker run image_name p1 p2 p3

และให้รันสคริปต์ run_container โดยมี p1 p2 p3 เป็นพารามิเตอร์

นี่คือทางออกของฉัน:

Dockerfile:

FROM docker.io/ubuntu
ADD run_container /
ENTRYPOINT ["/bin/bash", "-c", "/run_container \"$@\"", "--"]

7
การแทนที่ค่าที่สามในENTRYPOINTอาร์เรย์ด้วย"/run_container \"$@\""วิธีการอาร์กิวเมนต์ที่มีช่องว่างจะได้รับการจัดการอย่างถูกต้อง (เช่นdocker run image_name foo 'bar baz' quux)
davidchambers

หลังจากเพิ่มคำสั่ง switch / case ไปยังไฟล์ bash ของฉัน ENTRYPOINT ["run_container.sh"] ไม่ทำงานสำหรับฉันอีกต่อไป แต่ ENTRYPOINT ["sh", "-c", "run_container.sh"] จะไม่ยอมรับพารามิเตอร์ของฉันอีกต่อไป โซลูชันนี้ (พร้อมคำแนะนำ @davidchambers) ใช้ได้ผลสำหรับฉัน
rhamilton

10

หากคุณต้องการเรียกใช้ @build time:

CMD /bin/bash /file.sh arg1

ถ้าคุณต้องการเรียกใช้เวลา @run:

ENTRYPOINT ["/bin/bash"]
CMD ["/file.sh", "arg1"]

จากนั้นในเชลล์โฮสต์

docker build -t test .
docker run -i -t test

2
ENTRYPOINTเป็นคำตอบที่ดีสำหรับ OP ที่ฉันคิดว่าต้องการรันไทม์ แต่ถ้าคุณต้องการตัวแปรเวลาในการสร้างจริงๆคำตอบนี้ก็เสีย ใช้ARGและdocker build --build-arg docs.docker.com/engine/reference/builder/#arg
greg.kindel

0

อีกทางเลือกหนึ่ง ...

เพื่อให้การทำงานนี้

docker run -d --rm $IMG_NAME "bash:command1&&command2&&command3"

ใน dockerfile

ENTRYPOINT ["/entrypoint.sh"]

ใน entrypoint.sh

#!/bin/sh

entrypoint_params=$1
printf "==>[entrypoint.sh] %s\n" "entry_point_param is $entrypoint_params"

PARAM1=$(echo $entrypoint_params | cut -d':' -f1) # output is 1 must be 'bash' it     will be tested    
PARAM2=$(echo $entrypoint_params | cut -d':' -f2) # the real command separated by     &&

printf "==>[entrypoint.sh] %s\n" "PARAM1=$PARAM1"
printf "==>[entrypoint.sh] %s\n" "PARAM2=$PARAM2"

if [ "$PARAM1" = "bash" ];
then
    printf "==>[entrypoint.sh] %s\n" "about to running $PARAM2 command"
    echo $PARAM2 | tr '&&' '\n' | while read cmd; do
        $cmd
    done    
fi

ข้อสังเกตและข้อ จำกัด บางประการ .... คำสั่งที่มี ":" ต้องการการเปลี่ยนแปลงใน cut -d ':' และคำสั่งเช่น docker run -d --rm $ IMG_NAME "bash: echo $ PATH" จะแสดงค่าโฮสต์พา ธ แทนโฮสต์ หนึ่ง
wagnermarques
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.