Systemd สคริปต์แบบโต้ตอบและตัวจับเวลา

Systemd สคริปต์แบบโต้ตอบและตัวจับเวลา

การแนะนำ

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

ทำไมเราต้องมีเป้าหมาย?

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

ตัวอย่างของเป้าหมายเมื่อเปิดใช้งาน (ภาพรวมคุณลักษณะ) ด้วยการเรียกใช้สคริปต์เชิงโต้ตอบ

คำอธิบายของเป้าหมาย:

cat installer.target
[Unit]
Description=My installer
Requires=multi-user.target 
Conflicts=rescue.service rescue.target
After=multi-user.target rescue.service rescue.target 
AllowIsolate=yes
Wants=installer.service

เป้าหมายนี้จะเริ่มเมื่อมีการเรียกใช้ multi-user.target และเรียก installer.service อย่างไรก็ตามอาจมีบริการดังกล่าวหลายประการ

cat installer.service
[Unit]
# описание
Description=installer interactive dialog

[Service]
# Запустить один раз, когда остальное будет запущенно
Type=idle
# Команда запуска - вызов скрипта
ExecStart=/usr/bin/installer.sh
# Интерактивное взаимодействие с пользователем через tty3
StandardInput=tty
TTYPath=/dev/tty3
TTYReset=yes
TTYVHangup=yes

[Install]
WantedBy=installer.target

และสุดท้าย ตัวอย่างของสคริปต์ที่กำลังดำเนินการ:

#!/bin/bash
# Переходим в tty3
chvt 3
echo "Install, y/n ?"
read user_answer

สิ่งที่สำคัญที่สุดคือการเลือก Final.target - เป้าหมายที่ระบบควรไปถึงเมื่อเริ่มต้นระบบ ในระหว่างกระบวนการเริ่มต้น systemd จะผ่านการพึ่งพาและเปิดใช้งานทุกสิ่งที่ต้องการ
มีหลายวิธีในการเลือก Final.target ฉันใช้ตัวเลือกตัวโหลดสำหรับสิ่งนี้

การเปิดตัวครั้งสุดท้ายมีลักษณะดังนี้:

  1. บูตโหลดเดอร์เริ่มทำงาน
  2. bootloader เริ่มเปิดตัวเฟิร์มแวร์โดยส่งพารามิเตอร์ Final.target
  3. Systemd เริ่มสตาร์ทระบบ ตามลำดับไปที่ installer.target หรือ work.target จาก basic.target ผ่านการขึ้นต่อกัน (เช่น multi-user.target) หลังนำระบบไปทำงานในโหมดที่ต้องการ

กำลังเตรียมเฟิร์มแวร์สำหรับการเปิดตัว

เมื่อสร้างเฟิร์มแวร์ งานมักจะเกิดขึ้นจากการกู้คืนสถานะระบบเมื่อเริ่มต้นระบบและบันทึกเมื่อปิดเครื่อง สถานะหมายถึงไฟล์การกำหนดค่า ดัมพ์ฐานข้อมูล การตั้งค่าอินเทอร์เฟซ ฯลฯ

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

มันทำงานอย่างไรในโครงการของฉัน ( https://habr.com/ru/post/477008/ https://github.com/skif-web/monitor)

  1. ระบบเริ่มทำงาน
  2. เปิดตัวบริการ settings_restore.service โดยจะตรวจสอบการมีอยู่ของไฟล์ settings.txt ในส่วนข้อมูล หากไม่มี ไฟล์อ้างอิงจะถูกวางแทน จากนั้น การตั้งค่าระบบจะถูกกู้คืน:
    • รหัสผ่านผู้ดูแลระบบ
    • ชื่อโฮสต์
    • เขตเวลา
    • สถานที่
    • กำหนดว่ามีการใช้สื่อทั้งหมดหรือไม่ ตามค่าเริ่มต้น ขนาดภาพจะเล็ก - เพื่อความสะดวกในการคัดลอกและบันทึกลงสื่อ เมื่อเริ่มต้นระบบจะตรวจสอบเพื่อดูว่ายังมีพื้นที่ที่ไม่ได้ใช้หรือไม่ หากมี ดิสก์จะถูกแบ่งพาร์ติชันใหม่
    • กำลังสร้างรหัสเครื่องจากที่อยู่ MAC นี่เป็นสิ่งสำคัญสำหรับการได้รับที่อยู่เดียวกันผ่าน DHCP
    • การตั้งค่าเครือข่าย
    • จำกัดขนาดของบันทึก
    • กำลังเตรียมไดรฟ์ภายนอกสำหรับการทำงาน (หากเปิดใช้งานตัวเลือกที่เกี่ยวข้องและเป็นไดรฟ์ใหม่)
  3. เริ่ม postgresq
  4. บริการกู้คืนเริ่มต้นขึ้น จำเป็นต้องเตรียม zabbix และฐานข้อมูล:
    • ตรวจสอบว่ามีฐานข้อมูล zabbix อยู่แล้วหรือไม่ ถ้าไม่เช่นนั้น จะถูกสร้างขึ้นจากดัมพ์การเริ่มต้น (รวมอยู่ใน zabbix)
    • มีการสร้างรายการเขตเวลา (จำเป็นต้องแสดงในเว็บอินเตอร์เฟส)
    • พบ IP ปัจจุบัน ซึ่งแสดงอยู่ในปัญหา (คำเชิญให้ลงชื่อเข้าใช้คอนโซล)
  5. คำเชิญเปลี่ยนแปลง - วลี Ready to work ปรากฏขึ้น
  6. เฟิร์มแวร์พร้อมใช้งานแล้ว

ไฟล์บริการมีความสำคัญ แต่เป็นไฟล์ที่กำหนดลำดับการเปิดตัว

[Unit]
Description=restore system settings
Before=network.service prepare.service postgresql.service systemd-networkd.service systemd-resolved.service

[Service]
Type=oneshot
ExecStart=/usr/bin/settings_restore.sh

[Install]
WantedBy=multi-user.target

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

และบริการที่สอง (การเตรียม zabbix)

#!/bin/sh
[Unit]
Description=monitor prepare system
After=postgresql.service settings_restore.service
Before=zabbix-server.service zabbix-agent.service

[Service]
Type=oneshot
ExecStart=/usr/bin/prepare.sh

[Install]
WantedBy=multi-user.target

ที่นี่จะซับซ้อนกว่านี้เล็กน้อย การเปิดตัวยังอยู่ใน multi-user.target แต่หลังจากเริ่ม postgresql DBMS และ settings_restore ของฉัน แต่ก่อนที่จะเริ่มบริการ zabbix

บริการจับเวลาสำหรับ logrotate

Systemd สามารถแทนที่ CRON ได้ อย่างจริงจัง. ยิ่งกว่านั้นความแม่นยำไม่ได้ขึ้นอยู่กับนาที แต่ขึ้นอยู่กับวินาที (จะเป็นอย่างไรถ้าจำเป็น) หรือคุณสามารถสร้างตัวจับเวลาที่ซ้ำซากจำเจซึ่งถูกเรียกโดยการหมดเวลาจากเหตุการณ์
มันเป็นตัวจับเวลาที่ซ้ำซากจำเจที่นับเวลาตั้งแต่เริ่มต้นเครื่องที่ฉันสร้างขึ้น
สิ่งนี้จะต้องมี 2 ไฟล์
logrotateTimer.service - คำอธิบายที่แท้จริงของบริการ:

[Unit]
Description=run logrotate

[Service]
ExecStart=logrotate /etc/logrotate.conf
TimeoutSec=300

ง่ายมาก - คำอธิบายของคำสั่งเรียกใช้
ไฟล์ที่สอง logrotateTimer.timer เป็นที่ที่ตัวจับเวลาทำงาน:

[Unit]
Description=Run logrotate

[Timer]
OnBootSec=15min
OnUnitActiveSec=15min

[Install]
WantedBy=timers.target

ที่นี่คืออะไร:

  • คำอธิบายตัวจับเวลา
  • เวลาเริ่มต้นครั้งแรก เริ่มต้นจากการบูตระบบ
  • ระยะเวลาของการเปิดตัวต่อไป
  • ขึ้นอยู่กับบริการตัวจับเวลา อันที่จริง นี่คือสตริงที่ทำให้ตัวจับเวลา

สคริปต์โต้ตอบเมื่อปิดเครื่องและเป้าหมายการปิดระบบของคุณ

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

แต่ความจริงก็คือคำสั่งที่เรียกใช้โดยตัวเลือก ExecOnStop นั้นถูกดำเนินการภายนอก TTY! ตรวจสอบได้ง่าย - วางคำสั่ง tty และบันทึกเอาต์พุต

ดังนั้นฉันจึงดำเนินการปิดระบบผ่านเป้าหมายของฉัน ฉันไม่ได้อ้างว่าถูกต้อง 100% แต่ใช้งานได้!
วิธีการดำเนินการ (ในแง่ทั่วไป):
ฉันสร้างเป้าหมาย my_shutdown.target ซึ่งไม่ได้ขึ้นอยู่กับใครเลย:
my_shutdown.เป้าหมาย

[Unit]
Description=my shutdown
AllowIsolate=yes
Wants=my_shutdown.service 

เมื่อไปที่เป้าหมายนี้ (ผ่าน systemctl isolate my_shutdwn.target) จะเปิดตัวบริการ my_shutdown.service ซึ่งงานนั้นง่ายมาก - เพื่อรันสคริปต์ my_shutdown.sh:

[Unit]
Description=MY shutdown

[Service]
Type=oneshot
ExecStart=/usr/bin/my_shutdown.sh
StandardInput=tty
TTYPath=/dev/tty3
TTYReset=yes
TTYVHangup=yes

WantedBy=my_shutdown.target

  • ภายในสคริปต์นี้ ฉันดำเนินการที่จำเป็น คุณสามารถเพิ่มสคริปต์จำนวนมากให้กับเป้าหมายเพื่อความยืดหยุ่นและความสะดวกสบาย:

my_shutdown.sh

#!/bin/bash --login
if [ -f /tmp/reboot ];then
    command="systemctl reboot"
elif [ -f /tmp/shutdown ]; then
    command="systemctl poweroff"
fi
#Вот здесь нужные команды
#Например, cp /home/user/data.txt /storage/user/
    $command

บันทึก. การใช้ไฟล์ /tmp/reboot และ /tmp/shutdown คุณไม่สามารถเรียกเป้าหมายด้วยพารามิเตอร์ได้ การบริการเท่านั้นที่เป็นไปได้

แต่ฉันใช้เป้าหมายเพื่อให้มีความยืดหยุ่นในการทำงานและรับประกันลำดับการดำเนินการ

อย่างไรก็ตาม สิ่งที่น่าสนใจที่สุดก็เกิดขึ้นในภายหลัง จำเป็นต้องปิด/รีสตาร์ทเครื่อง และมี 2 ตัวเลือก:

  • แทนที่การรีบูต การปิดระบบ และคำสั่งอื่นๆ (ยังคงเป็นลิงก์ไปยัง systemctl) ด้วยสคริปต์ของคุณ ภายในสคริปต์ ให้ไปที่ my_shutdown.target และสคริปต์ภายในเป้าหมายจะเรียก systemctl โดยตรง เช่น รีบูท systemctl
  • ตัวเลือกที่ง่ายกว่า แต่ฉันไม่ชอบมัน ในอินเทอร์เฟซทั้งหมด อย่าเรียกปิดระบบ/รีบูต/อื่นๆ แต่ให้เรียกเป้าหมายโดยตรง systemctl isolate my_shutdown.target

ฉันเลือกตัวเลือกแรก ใน systemd การรีบูท (เช่น poweroff) เป็นการเชื่อมโยงไปยัง systemd

ls -l /sbin/poweroff 
lrwxrwxrwx 1 root root 14 сен 30 18:23 /sbin/poweroff -> /bin/systemctl

ดังนั้น คุณสามารถแทนที่ด้วยสคริปต์ของคุณเองได้:
รีบูต

#!/bin/sh
    touch /tmp/reboot
    sudo systemctl isolate my_shutdown.target
fi

ที่มา: will.com

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