วิธีเรียกใช้หลายบริการในคอนเทนเนอร์ Docker เดียว

เผยแพร่แล้ว: 2022-06-30

ภาพประกอบแสดงโลโก้ Docker

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

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

การระบุปัญหา

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

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

การรวมกระบวนการหลายขั้นตอนไว้ในจุดเข้าใช้งานเดียว

สคริปต์ Wrapper เป็นวิธีแก้ปัญหาที่ง่ายที่สุด คุณสามารถเขียนสคริปต์ที่เริ่มกระบวนการทั้งหมดของคุณและรอให้เสร็จสิ้นได้ การตั้งค่าสคริปต์เป็น Docker ENTRYPOINT ของคุณจะเรียกใช้เป็นกระบวนการเบื้องหน้าของคอนเทนเนอร์ ทำให้คอนเทนเนอร์ทำงานต่อไปจนกว่าจะมีสคริปต์ที่ห่อหุ้มตัวใดตัวหนึ่งออก

 #!/bin/bash

/opt/ขั้นแรก &

/opt/ขั้นตอนที่สอง &

รอ -n

ออก $?

สคริปต์นี้เริ่มต้นไบนารี /opt/first-process และ /opt/second-process ภายในคอนเทนเนอร์ การใช้ & อนุญาตให้สคริปต์ดำเนินการต่อโดยไม่ต้องรอให้แต่ละกระบวนการออก wait ใช้เพื่อระงับสคริปต์จนกว่ากระบวนการใดกระบวนการหนึ่งจะยุติลง สคริปต์จะออกมาพร้อมกับรหัสสถานะที่ออกโดยสคริปต์ที่เสร็จสิ้น

โมเดลนี้ส่งผลให้คอนเทนเนอร์ทำงานทั้ง first-process ที่หนึ่งและกระบวนการ second-process จนกระทั่งหนึ่งในนั้นออกจากกระบวนการ เมื่อถึงจุดนั้น คอนเทนเนอร์จะหยุด แม้ว่ากระบวนการอื่นอาจยังทำงานอยู่

หากต้องการใช้สคริปต์นี้ ให้แก้ไข ENTRYPOINT และ CMD ของอิมเมจ Docker เพื่อให้เป็นกระบวนการเบื้องหน้าของคอนเทนเนอร์:

 ENTRYPOINT ["/bin/sh"]
CMD ["./path/to/script.sh"]

ตัวเลือก --init คอนเทนเนอร์

ความท้าทายประการหนึ่งในการจัดการกระบวนการคอนเทนเนอร์คือการทำความสะอาดอย่างมีประสิทธิภาพขณะออกจากระบบ นักเทียบท่าใช้งาน CMD ของคุณเป็น ID กระบวนการ 1 ทำให้รับผิดชอบในการจัดการสัญญาณและกำจัดซอมบี้ หากสคริปต์ของคุณไม่มีความสามารถเหล่านี้ คุณอาจจบลงด้วยกระบวนการลูกที่ถูกละเลยยังคงอยู่ในคอนเทนเนอร์ของคุณ

คำสั่ง docker docker run มีแฟ --init ที่ปรับเปลี่ยนจุดเข้าใช้งานเพื่อใช้ tini เป็น PID 1 นี่เป็นกระบวนการเริ่มต้นขั้นต่ำที่รัน CMD ของคุณ จัดการการส่งต่อสัญญาณ และเก็บเกี่ยวซอมบี้อย่างต่อเนื่อง

การใช้ --init นั้นคุ้มค่าหากคุณคาดหวังว่าจะเกิดกระบวนการจำนวนมากและไม่ต้องการจัดการการล้างข้อมูลด้วยตนเอง Tini เป็นรส init น้ำหนักเบาที่ออกแบบมาสำหรับภาชนะบรรจุ มันมีขนาดเล็กกว่าทางเลือกที่เต็มเปี่ยมมากเช่น systemd และ upstart

การใช้ตัวจัดการกระบวนการเฉพาะ

การเขียนสคริปต์แบบแมนนวลนั้นเหมาะสมที่สุดอย่างรวดเร็วเมื่อคุณมีกระบวนการมากมายที่ต้องจัดการ การใช้ตัวจัดการกระบวนการเป็นอีกวิธีหนึ่งในการเรียกใช้บริการต่างๆ ภายในคอนเทนเนอร์ Docker ของคุณ ผู้จัดการกระบวนการจะกลายเป็น ENTRYPOINT ของคุณและมีหน้าที่รับผิดชอบในการเริ่มต้น บำรุงรักษา และทำความสะอาดหลังจากกระบวนการของผู้ปฏิบัติงานของคุณ

มีหลายทางเลือกเมื่อนำแนวทางนี้ไปใช้ supervisord เป็นตัวเลือกยอดนิยมซึ่งสามารถกำหนดค่าได้อย่างง่ายดายผ่านไฟล์ /etc/supervisor/conf.d/supervisord.conf :

 [โปรแกรม:อาปาเช่2]
command=/usr/sbin/apache2 -DFOREGROUND

[โปรแกรม:mysqld]
command=/usr/sbin/mysqld_safe

ไฟล์กำหนดค่านี้กำหนดค่า supervisord เพื่อเริ่ม Apache และ MySQL หากต้องการใช้ในคอนเทนเนอร์ Docker ให้เพิ่มแพ็คเกจที่จำเป็นทั้งหมดลงในอิมเมจของคุณ จากนั้นคัดลอกไฟล์ config supervisord ของคุณไปยังตำแหน่งที่ถูกต้อง ตั้งค่า supervisord เป็น CMD ของอิมเมจเพื่อเรียกใช้โดยอัตโนมัติเมื่อคอนเทนเนอร์เริ่มทำงาน

 จากอูบุนตู:ล่าสุด
เรียกใช้ apt-get install -y apache2 mysql-server Supervisor
COPY Supervisord.conf /etc/supervisor/conf.d/supervisord.conf
ENTRYPOINT ["/bin/sh"]
CMD ["/usr/bin/supervisord"]

เนื่องจาก supervisord ทำงานอย่างต่อเนื่อง จึงเป็นไปไม่ได้ที่จะหยุดคอนเทนเนอร์เมื่อกระบวนการตรวจสอบของคุณออกจากกระบวนการ ตัวเลือกอื่นคือ s6-overlay ซึ่งมีความสามารถนี้ มันใช้โมเดลบริการประกาศที่คุณวางสคริปต์บริการโดยตรงใน /etc/services.d :

 # เพิ่ม s6-overlay ให้กับรูปภาพของคุณ
เพิ่ม https://github.com/just-containers/s6-overlay/releases/download/v3.1.0.0/s6-overlay-noarch.tar.xz /tmp
รัน tar -C / -Jxpf /tmp/s6-overlay-noarch.tar.xz

เรียกใช้ printf "#!/bin/shn/usr/sbin/apache2 -DFOREGROUND" > /etc/services.d/first-service/run
เรียกใช้ chmod +x /etc/services.d/first-service/run

# ใช้ s6-overlay เป็นจุดเริ่มต้นสำหรับรูปภาพของคุณ
ENTRYPOINT ["/init"]

คุณสามารถเพิ่มสคริปต์ finish ที่เรียกใช้งานได้ภายในไดเร็กทอรีบริการของคุณเพื่อจัดการกับการหยุดคอนเทนเนอร์ด้วย docker stop s6-overlay จะเรียกใช้สคริปต์เหล่านี้โดยอัตโนมัติเมื่อกระบวนการได้รับสัญญาณ TERM เนื่องจากคำสั่ง stop

สคริปต์ที่เสร็จสิ้นจะได้รับรหัสออกจากบริการเป็นอาร์กิวเมนต์แรก รหัสถูกตั้งค่าเป็น 256 เมื่อบริการถูกฆ่าเนื่องจากสัญญาณที่ไม่ถูกตรวจจับ สคริปต์จำเป็นต้องเขียนโค้ดทางออกสุดท้ายไปที่ /run/s6-linux-init-container-results/exitcode ; s6-overlay จะอ่านไฟล์นี้และออกพร้อมกับค่าภายใน ทำให้โค้ดนั้นถูกใช้เป็นโค้ดหยุดของคอนเทนเนอร์ของคุณ

 #!/bin/sh
echo "$1" > /run/s6-linux-init-container-results/exitcode

เมื่อใดที่คุณควรเรียกใช้หลายกระบวนการในคอนเทนเนอร์

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

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

บทสรุป

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

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

ตัวจัดการกระบวนการให้ทุกสิ่งที่คุณต้องการ แต่ขยายภาพของคุณด้วยแพ็คเกจและการกำหนดค่าเพิ่มเติม สคริปต์ Wrapper นั้นง่ายกว่า แต่อาจต้องจับคู่กับแฟ --init ของ Docker เพื่อป้องกันการแพร่กระจายของกระบวนการซอมบี้