วิธีสร้างบริการ systemd เสมือนเพื่อหยุด / เริ่มอินสแตนซ์หลาย ๆ ตัวพร้อมกันได้อย่างไร


12

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

systemdดูเหมือนว่าจะให้หน่วยการสร้างที่ฉันต้องการใช้PartOfและไฟล์เทมเพลตหน่วย แต่เมื่อฉันหยุดบริการหลักบริการลูกค้าลูกจะไม่หยุด ฉันจะทำให้มันทำงานกับ systemd ได้อย่างไร นี่คือสิ่งที่ฉันมี

ไฟล์หน่วยหลักapp.service:

[Unit]
Description=App Web Service

[Service]
# Don't run as a deamon (because we've got nothing to do directly)
Type=oneshot
# Just print something, because ExecStart is required
ExecStart=/bin/echo "App Service exists only to collectively start and stop App instances"
# Keep running after Exit start finished, because we want the instances that depend on this to keep running
RemainAfterExit=yes
StandardOutput=journal

ไฟล์เท็มเพลตหน่วยชื่อapp@.serviceใช้เพื่อสร้างอินสแตนซ์ของลูกค้า:

[Unit]
Description=%I Instance of App Web Service

[Service]
PartOf=app.service
ExecStart=/home/mark/bin/app-poc.sh %i
StandardOutput=journal

app-poc.shสคริปต์ของฉัน(พิสูจน์แนวคิดที่เพิ่งพิมพ์ไปยังล็อกไฟล์ในลูป):

#!/bin/bash
# Just a temporary code to fake a full daemon.
while :
do
  echo "The App PoC loop for $@"
  sleep 2;
done

เพื่อพิสูจน์แนวคิดฉันได้รับไฟล์ systemd unit ~/.config/systemd/userแล้ว

ฉันจะเริ่มต้นผู้ปกครองและอินสแตนซ์ตามแม่แบบ (หลังsystemctl --user daemon-reload):

systemctl --user start app
systemctl --user start app@customer.service

จากการใช้journalctl -fฉันจะเห็นว่าทั้งสองเริ่มต้นและอินสแตนซ์ของลูกค้ายังคงทำงาน ตอนนี้ฉันคาดว่าการปิดเครื่องพ่อแม่จะหยุดลูก (เพราะฉันใช้PartOf) แต่ก็ไม่ได้ นอกจากนี้การเริ่มต้นผู้ปกครองไม่ได้เริ่มต้นลูกอย่างที่คาดไว้เช่นกัน

systemctl --user stop app

ขอบคุณ!

(ฉันใช้ Ubuntu 16.04 กับ systemd 229)


1
"PartOf = กำหนดค่าการขึ้นต่อกันคล้ายกับต้อง = แต่ จำกัด เฉพาะการหยุดและรีสตาร์ทหน่วย" หากคุณต้องการเริ่มทำงานคุณไม่จำเป็นต้องใช้Requires=แทนหรือไม่
sourcejedi

คำตอบ:


10

คุณต้องย้ายสาย

PartOf=app.service

ออกจาก[Service]และเข้าไปใน[Unit]ส่วนและเพิ่ม[Unit]ของapp.serviceรายชื่อของลูกค้าที่จะเริ่มต้นเช่น

Wants=app@customer1.service app@customer2.service

หรือตามที่ sourcejedi ได้กล่าวไว้ในความคิดเห็นRequires=เช่นเดียวกัน คุณสามารถเก็บที่จะหยุดการบริการที่คุณเริ่มต้นด้วยมือที่ไม่อยู่ในรายการข้างต้นเช่นPartOfsystemctl --user start app@customer3.service


PartOfผมได้รับการยืนยันของคุณถูกต้องเกี่ยวกับ ขอบคุณ ฉันจะจัดการ "Wants" ผ่าน symlink ซึ่งกลายเป็นการกระทำเดียวที่ฉันต้องทำเพื่อเปิดใช้งานลูกค้าใหม่ด้วย systemd สำหรับกรณีทดสอบของฉัน: `ln -s /home/mark/.config/systemd/user/app@.service / home / mark / .config / systemd / ผู้ใช้ / app.service.wants / unity @ foo.service`
Mark Stosberg

14

ฉันได้เรียนรู้ว่านี่คือสิ่งที่ systemd "Target Units" มีไว้สำหรับ ด้วยการใช้หน่วยเป้าหมายฉันจะได้รับผลประโยชน์ที่ฉันต้องการโดยไม่จำเป็นต้องสร้าง[Service]ส่วนปลอมที่ฉันมีด้านบน ตัวอย่าง "ไฟล์หน่วยเป้าหมาย" ที่ใช้งานได้มีลักษณะดังนี้:

# named like app.target
[Unit]
Description=App Web Service

# This collection of apps should be started at boot time.
[Install]
WantedBy=multi-user.target

จากนั้นอินสแตนซ์ของลูกค้าแต่ละรายควรรวมPartOfไว้ใน[Unit]ส่วน (ตามที่ระบุโดย @meuh) และควรมี[Install]ส่วนเพื่อให้enableและdisableสามารถทำงานกับบริการเฉพาะได้:

# In a file name like app@.service
[Unit]
Description=%I Instance of App Web Service
PartOf=app.target

[Service]
ExecStart=/home/mark/bin/app-poc.sh %i
Restart=on-failure
StandardOutput=journal

# When the service runs globally, make it run as a particular user for added security
#User=myapp
#Group=myapp

# When systemctl enable is used, make this start when the App service starts
[Install]
WantedBy=app.target

ในการแสดงอินสแตนซ์ลูกค้าและเริ่มต้นเมื่อเป้าหมายเริ่มทำงานคำสั่งเปิดใช้งานครั้งเดียวนี้จะถูกใช้:

 systemctl enable app

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


สถานะเป็นอย่างไร ฉันไม่สามารถหาวิธีง่ายๆในการรับสถานะของบริการทั้งหมดที่แอพต้องการ ฉันรู้ว่าฉันจะเขียนสคริปต์ได้อย่างไร แต่ ...
Tommi Kyntola

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

2
มันไม่ง่ายอย่างนั้น แพคเก็ตที่สคริปต์นั้นจะเป็นของ? มันจะต้องมีการแก้ไขทุกครั้งที่มีการเพิ่มองค์ประกอบใหม่ ลืมไปเลยว่าการปรับใช้และการบำรุงรักษากลายเป็นเรื่องยุ่งเหยิง สิ่งที่ฉันต้องการคือเพียงเพิ่มแพ็คเก็ตใหม่ด้วยการตั้งค่า partOf เพื่อระบุว่ามีอยู่ในกลุ่มนั้นและไม่แก้ไขสคริปต์บางส่วน จากนั้นหยุดและเริ่มต้นของเป้าหมายนั้นเหมือนเมื่อก่อน ใช้งานได้ แต่สถานะดูเหมือนจะไม่อยู่ในขอบเขตนั้น ฉันไม่สามารถหาวิธีที่จะได้รับรายชื่อของหน่วยที่มีรันไทม์อยู่ในเป้าหมาย กรณีการใช้งานนี้ไม่ครอบคลุมโดย systemd
Tommi Kyntola

2
@TommiKyntola นี่คือ bash one-liner ที่คุณไม่จำเป็นต้องอัปเดตเนื่องจากการเปลี่ยนแปลงการอ้างอิงเป้าหมาย:systemctl status $(systemctl list-dependencies --plain otp.target)
Mark Stosberg

2
@TommiKyntola ฉันยอมรับว่าsystemdสามารถปรับปรุงการใช้งานที่นี่ ฉันได้เปิดคำขอคุณสมบัติเพื่อแนะนำสถานะที่ดีขึ้นสำหรับเป้าหมาย
Mark Stosberg
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.