การแนะนำ
เมื่อพัฒนาสำหรับ 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 ฉันใช้ตัวเลือกตัวโหลดสำหรับสิ่งนี้
การเปิดตัวครั้งสุดท้ายมีลักษณะดังนี้:
- บูตโหลดเดอร์เริ่มทำงาน
- bootloader เริ่มเปิดตัวเฟิร์มแวร์โดยส่งพารามิเตอร์ Final.target
- Systemd เริ่มสตาร์ทระบบ ตามลำดับไปที่ installer.target หรือ work.target จาก basic.target ผ่านการขึ้นต่อกัน (เช่น multi-user.target) หลังนำระบบไปทำงานในโหมดที่ต้องการ
กำลังเตรียมเฟิร์มแวร์สำหรับการเปิดตัว
เมื่อสร้างเฟิร์มแวร์ งานมักจะเกิดขึ้นจากการกู้คืนสถานะระบบเมื่อเริ่มต้นระบบและบันทึกเมื่อปิดเครื่อง สถานะหมายถึงไฟล์การกำหนดค่า ดัมพ์ฐานข้อมูล การตั้งค่าอินเทอร์เฟซ ฯลฯ
Systemd รันกระบวนการในเป้าหมายเดียวกันแบบขนาน มีการขึ้นต่อกันที่ช่วยให้คุณกำหนดลำดับการเริ่มต้นของสคริปต์ได้
มันทำงานอย่างไรในโครงการของฉัน (
- ระบบเริ่มทำงาน
- เปิดตัวบริการ settings_restore.service โดยจะตรวจสอบการมีอยู่ของไฟล์ settings.txt ในส่วนข้อมูล หากไม่มี ไฟล์อ้างอิงจะถูกวางแทน จากนั้น การตั้งค่าระบบจะถูกกู้คืน:
- รหัสผ่านผู้ดูแลระบบ
- ชื่อโฮสต์
- เขตเวลา
- สถานที่
- กำหนดว่ามีการใช้สื่อทั้งหมดหรือไม่ ตามค่าเริ่มต้น ขนาดภาพจะเล็ก - เพื่อความสะดวกในการคัดลอกและบันทึกลงสื่อ เมื่อเริ่มต้นระบบจะตรวจสอบเพื่อดูว่ายังมีพื้นที่ที่ไม่ได้ใช้หรือไม่ หากมี ดิสก์จะถูกแบ่งพาร์ติชันใหม่
- กำลังสร้างรหัสเครื่องจากที่อยู่ MAC นี่เป็นสิ่งสำคัญสำหรับการได้รับที่อยู่เดียวกันผ่าน DHCP
- การตั้งค่าเครือข่าย
- จำกัดขนาดของบันทึก
- กำลังเตรียมไดรฟ์ภายนอกสำหรับการทำงาน (หากเปิดใช้งานตัวเลือกที่เกี่ยวข้องและเป็นไดรฟ์ใหม่)
- เริ่ม postgresq
- บริการกู้คืนเริ่มต้นขึ้น จำเป็นต้องเตรียม zabbix และฐานข้อมูล:
- ตรวจสอบว่ามีฐานข้อมูล zabbix อยู่แล้วหรือไม่ ถ้าไม่เช่นนั้น จะถูกสร้างขึ้นจากดัมพ์การเริ่มต้น (รวมอยู่ใน zabbix)
- มีการสร้างรายการเขตเวลา (จำเป็นต้องแสดงในเว็บอินเตอร์เฟส)
- พบ IP ปัจจุบัน ซึ่งแสดงอยู่ในปัญหา (คำเชิญให้ลงชื่อเข้าใช้คอนโซล)
- คำเชิญเปลี่ยนแปลง - วลี Ready to work ปรากฏขึ้น
- เฟิร์มแวร์พร้อมใช้งานแล้ว
ไฟล์บริการมีความสำคัญ แต่เป็นไฟล์ที่กำหนดลำดับการเปิดตัว
[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