กำลังรัน systemd ในคอนเทนเนอร์

เราติดตามหัวข้อการใช้ systemd ในคอนเทนเนอร์มาเป็นเวลานาน ย้อนกลับไปในปี 2014 Daniel Walsh วิศวกรด้านความปลอดภัยของเราเขียนบทความ กำลังรัน systemd ภายใน Docker Containerและอีกสองสามปีต่อมา - อีกอันที่เรียกว่า กำลังรัน systemd ในคอนเทนเนอร์ที่ไม่มีสิทธิพิเศษโดยระบุว่าสถานการณ์ยังไม่ดีขึ้นมากนัก โดยเฉพาะอย่างยิ่งเขาเขียนว่า “น่าเสียดาย แม้แต่สองปีต่อมา ถ้าคุณใช้ Google “ระบบนักเทียบท่า” สิ่งแรกที่ปรากฏขึ้นก็คือบทความเก่าๆ ของเขา ถึงเวลาที่ต้องเปลี่ยนแปลงบางอย่างแล้ว” นอกจากนี้เราได้พูดคุยกันแล้วเกี่ยวกับ ความขัดแย้งระหว่าง Docker และนักพัฒนา systemd.

กำลังรัน systemd ในคอนเทนเนอร์

ในบทความนี้ เราจะแสดงให้เห็นว่ามีอะไรเปลี่ยนแปลงไปบ้างตามกาลเวลา และ Podman จะช่วยเราในเรื่องนี้ได้อย่างไร

มีเหตุผลหลายประการในการรัน systemd ภายในคอนเทนเนอร์ เช่น:

  1. คอนเทนเนอร์หลายบริการ – หลายๆ คนต้องการดึงแอปพลิเคชันหลายบริการของตนออกจากเครื่องเสมือนและเรียกใช้งานในคอนเทนเนอร์ แน่นอนว่าจะดีกว่าหากแยกแอปพลิเคชันดังกล่าวออกเป็นไมโครเซอร์วิส แต่ไม่ใช่ทุกคนที่รู้วิธีการทำเช่นนี้หรือเพียงแค่ไม่มีเวลา ดังนั้นการเรียกใช้แอปพลิเคชันเช่นบริการที่ systemd เปิดตัวจากไฟล์หน่วยจึงเหมาะสมอย่างยิ่ง
  2. ไฟล์หน่วย Systemd – แอปพลิเคชันส่วนใหญ่ที่ทำงานภายในคอนเทนเนอร์สร้างขึ้นจากโค้ดที่เคยทำงานบนเครื่องเสมือนหรือเครื่องจริง แอปพลิเคชันเหล่านี้มีไฟล์หน่วยที่เขียนขึ้นสำหรับแอปพลิเคชันเหล่านี้ และเข้าใจว่าควรเปิดใช้งานอย่างไร ดังนั้นจึงเป็นการดีกว่าที่จะเริ่มบริการโดยใช้วิธีการที่รองรับ แทนที่จะแฮ็กบริการเริ่มต้นของคุณเอง
  3. Systemd เป็นผู้จัดการกระบวนการ มันจัดการบริการต่างๆ (ปิดเครื่อง เริ่มบริการใหม่ หรือฆ่ากระบวนการซอมบี้) ได้ดีกว่าเครื่องมืออื่นๆ

ที่กล่าวว่ามีสาเหตุหลายประการที่จะไม่รัน systemd ในคอนเทนเนอร์ สิ่งสำคัญคือ systemd/journald ควบคุมเอาต์พุตของคอนเทนเนอร์และเครื่องมือเช่น Kubernetes หรือ เปิดกะ คาดว่าคอนเทนเนอร์จะเขียนบันทึกโดยตรงไปยัง stdout และ stderr ดังนั้น หากคุณกำลังจะจัดการคอนเทนเนอร์ผ่านเครื่องมือการประสานเหมือนที่กล่าวไว้ข้างต้น คุณควรพิจารณาใช้คอนเทนเนอร์แบบ systemd อย่างจริงจัง นอกจากนี้ นักพัฒนา Docker และ Moby มักต่อต้านการใช้ systemd ในคอนเทนเนอร์อย่างรุนแรง

การมาของพอดแมน

เรามีความยินดีที่จะรายงานว่าในที่สุดสถานการณ์ก็คืบหน้าแล้ว ทีมงานที่รับผิดชอบดูแลคอนเทนเนอร์ที่ Red Hat ตัดสินใจพัฒนา เครื่องยนต์คอนเทนเนอร์ของคุณเอง. เขาได้รับชื่อ พอดแมน และนำเสนออินเทอร์เฟซบรรทัดคำสั่ง (CLI) เช่นเดียวกับ Docker และคำสั่ง Docker เกือบทั้งหมดสามารถใช้ใน Podman ได้ในลักษณะเดียวกัน เรามักจะจัดสัมมนาซึ่งปัจจุบันเรียกว่า เปลี่ยนนักเทียบท่าเป็นพ็อดแมนและสไลด์แรกสุดเรียกร้องให้เขียน: alias docker=podman

หลายคนทำเช่นนี้

ฉันและ Podman ไม่มีทางต่อต้านคอนเทนเนอร์แบบ systemd เลย ท้ายที่สุดแล้ว Systemd เป็นระบบย่อย init ของ Linux ที่ใช้บ่อยที่สุด และการไม่อนุญาตให้มันทำงานอย่างถูกต้องในคอนเทนเนอร์ หมายถึงการเพิกเฉยต่อจำนวนคนนับพันที่คุ้นเคยกับการใช้งานคอนเทนเนอร์

Podman รู้ว่าต้องทำอย่างไรเพื่อให้ systemd ทำงานได้อย่างถูกต้องในคอนเทนเนอร์ มันต้องการสิ่งต่าง ๆ เช่นการติดตั้ง tmpfs บน /run และ /tmp เธอชอบที่จะเปิดใช้งานสภาพแวดล้อมแบบ "คอนเทนเนอร์" และคาดว่าจะมีสิทธิ์ในการเขียนไปยังส่วนของไดเรกทอรี cgroup และไปยังโฟลเดอร์ /var/log/journald

เมื่อคุณเริ่มต้นคอนเทนเนอร์ซึ่งมีคำสั่งแรกเป็น init หรือ systemd Podman จะกำหนดค่า tmpfs และ Cgroups โดยอัตโนมัติเพื่อให้แน่ใจว่า systemd เริ่มทำงานโดยไม่มีปัญหา หากต้องการบล็อกโหมดเรียกทำงานอัตโนมัตินี้ ให้ใช้อ็อพชัน --systemd=false โปรดทราบว่า Podman จะใช้โหมด systemd เมื่อเห็นว่าจำเป็นต้องรันคำสั่ง systemd หรือ init เท่านั้น

นี่คือข้อความที่ตัดตอนมาจากคู่มือ:

ผู้ชาย podman วิ่ง
...

–systemd=true|false

ใช้งานคอนเทนเนอร์ในโหมด systemd เปิดใช้งานตามค่าเริ่มต้น

หากคุณรันคำสั่ง systemd หรือ init ภายในคอนเทนเนอร์ Podman จะกำหนดค่าจุดเมานท์ tmpfs ในไดเร็กทอรีต่อไปนี้:

/run, /run/lock, /tmp, /sys/fs/cgroup/systemd, /var/lib/วารสาร

นอกจากนี้สัญญาณหยุดเริ่มต้นจะเป็น SIGRTMIN+3

ทั้งหมดนี้ทำให้ systemd สามารถทำงานในคอนเทนเนอร์แบบปิดโดยไม่มีการแก้ไขใดๆ

หมายเหตุ: systemd พยายามเขียนไปยังระบบไฟล์ cgroup อย่างไรก็ตาม SELinux จะป้องกันไม่ให้คอนเทนเนอร์ทำเช่นนี้ตามค่าเริ่มต้น หากต้องการเปิดใช้งานการเขียน ให้เปิดใช้งานพารามิเตอร์บูลีนของcontainer_manage_cgroup:

setsebool -P container_manage_cgroup จริง

ตอนนี้ดูว่า Dockerfile มีลักษณะอย่างไรสำหรับการรัน systemd ในคอนเทนเนอร์โดยใช้ Podman:

# cat Dockerfile

FROM fedora

RUN dnf -y install httpd; dnf clean all; systemctl enable httpd

EXPOSE 80

CMD [ "/sbin/init" ]

นั่นคือทั้งหมดที่

ตอนนี้เราประกอบภาชนะ:

# podman build -t systemd .

เราบอกให้ SELinux อนุญาตให้ systemd แก้ไขการกำหนดค่า Cgroups:

# setsebool -P container_manage_cgroup true

อย่างไรก็ตาม หลายคนลืมขั้นตอนนี้ไปแล้ว โชคดีที่ต้องทำเพียงครั้งเดียวและการตั้งค่าจะถูกบันทึกหลังจากรีบูตระบบ

ตอนนี้เราเพิ่งเริ่มคอนเทนเนอร์:

# podman run -ti -p 80:80 systemd

systemd 239 running in system mode. (+PAM +AUDIT +SELINUX +IMA -APPARMOR +SMACK +SYSVINIT +UTMP +LIBCRYPTSETUP +GCRYPT +GNUTLS +ACL +XZ +LZ4 +SECCOMP +BLKID +ELFUTILS +KMOD +IDN2 -IDN +PCRE2 default-hierarchy=hybrid)

Detected virtualization container-other.

Detected architecture x86-64.

Welcome to Fedora 29 (Container Image)!

Set hostname to <1b51b684bc99>.

Failed to install release agent, ignoring: Read-only file system

File /usr/lib/systemd/system/systemd-journald.service:26 configures an IP firewall (IPAddressDeny=any), but the local system does not support BPF/cgroup based firewalling.

Proceeding WITHOUT firewalling in effect! (This warning is only shown for the first loaded unit using IP firewalling.)

[  OK ] Listening on initctl Compatibility Named Pipe.

[  OK ] Listening on Journal Socket (/dev/log).

[  OK ] Started Forward Password Requests to Wall Directory Watch.

[  OK ] Started Dispatch Password Requests to Console Directory Watch.

[  OK ] Reached target Slices.

…

[  OK ] Started The Apache HTTP Server.

เพียงเท่านี้ บริการก็เริ่มทำงานแล้ว:

$ curl localhost

<html  xml_lang="en" lang="en">

…

</html>

หมายเหตุ: อย่าลองสิ่งนี้บน Docker! ที่นั่นคุณยังต้องเต้นรำกับแทมบูรีนเพื่อปล่อยคอนเทนเนอร์ประเภทนี้ผ่านภูต (จะต้องระบุช่องและแพ็คเกจเพิ่มเติมเพื่อให้ทั้งหมดนี้ทำงานได้อย่างราบรื่นใน Docker หรือจะต้องทำงานในคอนเทนเนอร์ที่มีสิทธิ์ โปรดดูรายละเอียดที่ статье.)

สิ่งดีๆ อีกสองสามอย่างเกี่ยวกับ Podman และ systemd

Podman ทำงานได้ดีกว่า Docker ในไฟล์ systemd unit

หากจำเป็นต้องสตาร์ทคอนเทนเนอร์เมื่อระบบบู๊ต คุณสามารถแทรกคำสั่ง Podman ที่เหมาะสมลงในไฟล์ systemd unit ซึ่งจะเริ่มบริการและติดตามดู Podman ใช้โมเดล fork-exec มาตรฐาน กล่าวอีกนัยหนึ่ง กระบวนการคอนเทนเนอร์เป็นลูกของกระบวนการ Podman ดังนั้น systemd จึงสามารถตรวจสอบได้อย่างง่ายดาย

นักเทียบท่าใช้โมเดลไคลเอ็นต์-เซิร์ฟเวอร์ และคำสั่ง Docker CLI สามารถวางลงในไฟล์หน่วยได้โดยตรง อย่างไรก็ตาม เมื่อไคลเอ็นต์ Docker เชื่อมต่อกับ Docker daemon แล้ว (ไคลเอ็นต์) จะกลายเป็นเพียงกระบวนการอื่นที่จัดการ stdin และ stdout ในทางกลับกัน systemd ไม่มีความคิดเกี่ยวกับการเชื่อมต่อระหว่างไคลเอ็นต์ Docker และคอนเทนเนอร์ที่ทำงานภายใต้การควบคุมของ Docker daemon ดังนั้นภายในโมเดลนี้ โดยพื้นฐานแล้ว systemd จึงไม่สามารถตรวจสอบบริการได้

การเปิดใช้งาน systemd ผ่านซ็อกเก็ต

Podman จัดการการเปิดใช้งานผ่านซ็อกเก็ตอย่างถูกต้อง เนื่องจาก Podman ใช้โมเดล fork-exec จึงสามารถส่งต่อซ็อกเก็ตไปยังกระบวนการคอนเทนเนอร์ย่อยได้ นักเทียบท่าไม่สามารถทำได้เนื่องจากใช้โมเดลไคลเอ็นต์-เซิร์ฟเวอร์

บริการ varlink ที่ Podman ใช้เพื่อสื่อสารกับไคลเอนต์ระยะไกลไปยังคอนเทนเนอร์นั้นเปิดใช้งานจริงผ่านซ็อกเก็ต แพ็คเกจ Cockpit-podman ซึ่งเขียนด้วย Node.js และเป็นส่วนหนึ่งของโปรเจ็กต์ห้องนักบิน ช่วยให้ผู้ใช้สามารถโต้ตอบกับคอนเทนเนอร์ Podman ผ่านทางเว็บอินเตอร์เฟสได้ web daemon ที่ใช้งาน Cockpit-podman จะส่งข้อความไปยังซ็อกเก็ต varlink ที่ systemd ฟังอยู่ จากนั้น Systemd จะเปิดใช้งานโปรแกรม Podman เพื่อรับข้อความและเริ่มจัดการคอนเทนเนอร์ การเปิดใช้งาน systemd บนซ็อกเก็ตช่วยลดความจำเป็นในการรัน daemon อย่างต่อเนื่องเมื่อใช้ API ระยะไกล

นอกจากนี้ เรากำลังพัฒนาไคลเอนต์ Podman อีกตัวที่เรียกว่า podman-remote ซึ่งใช้ Podman CLI เดียวกัน แต่เรียก varlink เพื่อรันคอนเทนเนอร์ Podman-remote สามารถทำงานบนเซสชัน SSH ได้ ทำให้คุณสามารถโต้ตอบกับคอนเทนเนอร์บนเครื่องต่างๆ ได้อย่างปลอดภัย เมื่อเวลาผ่านไป เราวางแผนที่จะเปิดใช้งาน podman-remote เพื่อรองรับ MacOS และ Windows ควบคู่ไปกับ Linux เพื่อให้นักพัฒนาบนแพลตฟอร์มเหล่านั้นสามารถรันเครื่องเสมือน Linux โดยที่ Podman varlink ทำงานอยู่ และมีประสบการณ์เต็มรูปแบบที่คอนเทนเนอร์ทำงานบนเครื่องภายในเครื่อง

SD_แจ้งเตือน

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

ในแผนงาน

เราวางแผนที่จะเพิ่มคำสั่ง podman create systemd CONTAINERID ซึ่งจะสร้างไฟล์ systemd unit เพื่อจัดการคอนเทนเนอร์เฉพาะที่ระบุ สิ่งนี้ควรใช้ได้ทั้งในโหมดรูทและโหมดไร้รูทสำหรับคอนเทนเนอร์ที่ไม่มีสิทธิ์ เรายังเห็นคำขอรันไทม์ systemd-nspawn ที่เข้ากันได้กับ OCI อีกด้วย

ข้อสรุป

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

ที่มา: will.com

เพิ่มความคิดเห็น