Linux: タスク自動化 — cron / systemd / journalctl
サーバー上のすべては自動化すべき — 定期バックアップ、ログクリーンアップ、ヘルスチェック。Linuxはシンプルなcronからモダンなsystemdタイマーまで、自動化ソリューションを提供しています。
📋 前提条件:まず以下を完了していること
- レッスン21:Bashスクリプト応用
1. このレッスンで学ぶこと
- crontab定期タスク構文
- systemdサービスユニットの作成
- systemdタイマーによるcron代替
- journalctlログクエリ
- logrotateログローテーション
2. 「もう午前2時に起きてスクリプトを走らせる必要がない」ストーリー
(1) 悩み:毎日手動バックアップ
小明のバックアップスクリプトは毎日手動実行する必要がありました。彼は目覚ましをセット — 午前2時に起きて./backup.shを実行。1週間後、彼は疲弊していました。
(2) cronが彼を解放した
ボブがcrontabを教えました:
crontab -e
# Add:
0 2 * * * /home/alice/scripts/backup.sh > /dev/null 2>&1
それ以来、システムは毎日午前2時に自動的にバックアップを実行。小明はたまにログを確認するだけで済みました。
(3) 得られるもの:機械に任せるべきことは機械に
cronを学んだ後、小明はすべてを一度に自動化しました:日次バックアップ、週次レポート、ログクリーンアップ、ヘルスチェックping。日常の運用時間は2時間から10分に削減されました。
3. 知識ポイント
(1) crontab — 定期タスク
cron構文: minute hour day month weekday command
min hour day month weekday command
(0-59)(0-23)(1-31)(1-12)(0-7)
(0=Sunday 7=Sunday)
# Special syntax
@reboot # Run on reboot
@daily # Every day at 0:00 (same as 0 0 * * *)
@hourly # Every hour (same as 0 * * * *)
@weekly # Every Monday at 0:00
@monthly # 1st of every month at 0:00
よく使う例:
# View current user's cron
crontab -l
# Edit crontab
crontab -e
# Delete all cron jobs
crontab -r
# Common examples
0 2 * * * /scripts/backup.sh # Daily at 2:00
*/5 * * * * /scripts/health-check.sh # Every 5 minutes
0 0 * * 0 /scripts/weekly-report.sh # Every Sunday at 0:00
0 0 1 * * /scripts/monthly-cleanup.sh # 1st of every month
0 9-17 * * 1-5 /scripts/business-hours.sh # Weekdays 9-17, every hour
crontab -rは現在のユーザーの全cronジョブを確認なしで削除します!誤削除はバックアップからのみ復旧可能。削除前に必ずcrontab -lでバックアップを取り、crontab -rではなくcrontab -eで編集する習慣を身につけてください。
~/.bashrcや~/.profileなどは読み込まれず、PATHは通常/usr/bin:/binのみ。スクリプトがカスタム環境変数に依存する場合は、先頭で手動sourceするか、crontab内でコマンドに絶対パスを使用してください。
(2) systemdサービス
# Service unit path
/etc/systemd/system/myapp.service
# Basic structure
[Unit]
Description=My Web Application
After=network.target
[Service]
Type=simple
User=alice
WorkingDirectory=/opt/myapp
ExecStart=/usr/bin/node server.js
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
# Usage
sudo systemctl daemon-reload # Reload configuration
sudo systemctl start myapp # Start
sudo systemctl enable myapp # Enable on boot
sudo systemctl status myapp # View status
sudo journalctl -u myapp -f # View logs
(3) systemdタイマー
Persistent=trueを設定)。これらのニーズがないシンプルな周期スクリプトならcronの方が軽量で直感的。新規プロジェクトではまずsystemdタイマーを試してみてください。
# Timer unit (replaces cron)
/etc/systemd/system/backup.timer
[Unit]
Description=Daily Backup Timer
[Timer]
OnCalendar=daily
Persistent=true
[Install]
WantedBy=timers.target
# Corresponding service
/etc/systemd/system/backup.service
[Unit]
Description=Daily Backup Service
[Service]
Type=oneshot
ExecStart=/opt/scripts/backup.sh
# Usage
sudo systemctl enable --now backup.timer
sudo systemctl list-timers --all
OnCalendar構文:
OnCalendar=daily # Every day at 0:00
OnCalendar=hourly # Every hour
OnCalendar=*-*-* 02:00:00 # Every day at 2:00
OnCalendar=Mon *-*-* 03:00:00 # Every Monday at 3:00
OnCalendar=*-*-1 00:00:00 # 1st of every month
OnCalendar=*:0/5 # Every 5 minutes
(4) journalctl — ログビューア
# Basic
journalctl # All logs
journalctl -u nginx # Specific service
journalctl -u nginx -f # Follow in real time
journalctl -u nginx --since "1 hour ago"
journalctl -u nginx --until "2026-07-07 10:00"
journalctl -p err # Error level and above only
journalctl -p err -u nginx # nginx error logs
# Management
journalctl --disk-usage # Check log disk usage
sudo journalctl --vacuum-size=500M # Limit log size
sudo journalctl --vacuum-time=7d # Keep 7 days
(5) logrotate — ログローテーション
/etc/cron.daily/logrotateのスクリプト)により毎日自動実行されます。/etc/logrotate.d/に設定を書くだけで済みます。設定変更後、logrotate -d /etc/logrotate.d/your-configでデバッグモードでプレビューし、自動的に反映させます。
# Configuration locations
/etc/logrotate.conf # Global config
/etc/logrotate.d/ # Per-app configs
# Example: Nginx log rotation
/var/log/nginx/*.log {
daily
rotate 7
compress
delaycompress
missingok
notifempty
create 640 www-data adm
postrotate
[ -f /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid`
endscript
}
よく使うディレクティブ:
| ディレクティブ | 意味 |
|---|---|
daily/weekly/monthly |
ローテーション頻度 |
rotate N |
N世代のアーカイブを保持 |
compress |
アーカイブを圧縮(gzip) |
delaycompress |
1サイクル遅延して圧縮 |
missingok |
ファイルがなくてもエラーにしない |
notifempty |
空ファイルはローテーションしない |
size 100M |
指定サイズに達した時のみローテーション |
maxage 30 |
30日以上古いアーカイブを削除 |
(6) ▶ サンプル:crontab設定
# Edit crontab
crontab -e
# Write the following content
# ┌───── minute (0-59)
# │ ┌───── hour (0-23)
# │ │ ┌───── day of month (1-31)
# │ │ │ ┌───── month (1-12)
# │ │ │ │ ┌───── day of week (0-7, 0=Sunday)
# * * * * * command
# Daily 2:00 AM automated backup
0 2 * * * /home/alice/scripts/backup.sh
# Every 5 minutes health check
*/5 * * * * /home/alice/scripts/health-check.sh
# Every Monday 3:00 AM weekly report
0 3 * * 1 /home/alice/scripts/weekly-report.sh
# 1st of every month archive cleanup
0 0 1 * * /home/alice/scripts/cleanup.sh
(7) ▶ サンプル:systemdサービスの作成
# Create a systemd service for a Node.js application
sudo tee /etc/systemd/system/webapp.service << 'EOF'
[Unit]
Description=Node.js Web Application
Documentation=https://example.com/docs
After=network.target
[Service]
Type=simple
User=alice
Group=alice
WorkingDirectory=/opt/webapp
Environment=NODE_ENV=production
Environment=PORT=3000
ExecStart=/usr/bin/node /opt/webapp/server.js
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=10
# Security options
ProtectSystem=full
NoNewPrivileges=true
PrivateTmp=true
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now webapp
sudo systemctl status webapp
出力:
TEXT● webapp.service - Node.js Web Application Loaded: loaded (/etc/systemd/system/webapp.service; enabled) Active: active (running) since Tue 2026-07-07 10:23:45 UTC Main PID: 5678 (node) Tasks: 12 (limit: 4915) Memory: 45.2M CPU: 1.234s
(8) ▶ サンプル:systemdタイマーでcronを代替
# Backup service
sudo tee /etc/systemd/system/backup.service << 'EOF'
[Unit]
Description=Daily Backup
[Service]
Type=oneshot
ExecStart=/opt/scripts/backup.sh
User=alice
EOF
# Backup timer
sudo tee /etc/systemd/system/backup.timer << 'EOF'
[Unit]
Description=Run backup daily at 2am
Requires=backup.service
[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
[Install]
WantedBy=timers.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now backup.timer
sudo systemctl list-timers --all
出力:
TEXTNEXT LEFT LAST PASSED Tue 2026-07-07 02:00:00 UTC 15h left Mon 2026-07-06 02:00:00 UTC 9h ago n/a n/a n/a n/a 1 timers listed.
(9) ▶ サンプル:journalctlログクエリ
# View nginx errors from the last hour
journalctl -u nginx -p err --since "1 hour ago"
# View all logs from yesterday
journalctl --since yesterday --until today
# View logs for a specific PID
journalctl _PID=1234
# Follow multiple services in real time
journalctl -u nginx -u webapp -f
# Export logs to file
journalctl -u webapp --since "2026-07-06" > webapp-logs.txt
# Check log disk usage
journalctl --disk-usage
# Archived and active journals take up 512.0M
# Clean up
sudo journalctl --vacuum-size=200M
出力:
TEXTJul 07 10:23:45 server nginx[1234]: GET /api/users 200 1234 Jul 07 10:23:50 server nginx[1234]: POST /api/data 500 0 Jul 07 10:24:01 server webapp[5678]: Error: ECONNREFUSED -- Logs begin at Mon 2026-07-06 00:00:01 -- Jul 07 09:00:00 server sshd[890]: Accepted publickey for alice Archived and active journals take up 512.0M Deleted archived journals: 23 Vacuuming done, freed 312.0M of archived journals
(10) ▶ サンプル:logrotate設定
# Configure log rotation for a custom application
sudo tee /etc/logrotate.d/custom-app << 'EOF'
/var/log/custom-app/*.log {
daily
rotate 14
compress
delaycompress
missingok
notifempty
create 640 www-data www-data
sharedscripts
postrotate
systemctl reload custom-app > /dev/null 2>&1 || true
endscript
}
EOF
# Test the configuration
sudo logrotate -d /etc/logrotate.d/custom-app
# Manually force execution
sudo logrotate -f /etc/logrotate.d/custom-app
出力:
TEXTreading config file /etc/logrotate.d/custom-app Handling 1 logs rotating pattern: /var/log/custom-app/*.log rotating 14 kept versions considering log /var/log/custom-app/app.log log needs rotating renaming /var/log/custom-app/app.log to /var/log/custom-app/app.log.1 creating new /var/log/custom-app/app.log mode=0640 user=www-data group=www-data
(11) ▶ 総合例:自動運用システム
#!/bin/bash
# setup-automation.sh - Configure automated operations system
set -euo pipefail
echo "=== Setting Up Automated Ops System ==="
# 1. Create scripts directory
mkdir -p /opt/scripts
# 2. Backup script
cat > /opt/scripts/backup.sh << 'BACKUPEOF'
#!/bin/bash
# Auto backup - triggered daily at 2 AM by cron or systemd timer
SOURCE="/var/www"
DEST="/backup"
RETENTION=30
mkdir -p "$DEST"
BACKUP_FILE="${DEST}/backup_$(date +%Y%m%d).tar.gz"
tar -czf "$BACKUP_FILE" \
--exclude=node_modules \
--exclude=.git \
"$SOURCE" 2>/dev/null
# Clean expired backups
find "$DEST" -name "backup_*.tar.gz" -mtime +$RETENTION -delete
# Log
echo "[$(date)] Backup: $BACKUP_FILE ($(du -h "$BACKUP_FILE" | cut -f1))" >> /var/log/backup.log
BACKUPEOF
chmod +x /opt/scripts/backup.sh
# 3. Health check script
cat > /opt/scripts/health-check.sh << 'HEALTHEOF'
#!/bin/bash
# Health check - runs every 5 minutes
WEBHOOK_URL="https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
SERVICES=("nginx" "postgresql" "webapp")
for svc in "${SERVICES[@]}"; do
if ! systemctl is-active --quiet "$svc"; then
MESSAGE="❌ $svc service abnormal $(date)"
echo "$MESSAGE" | mail -s "Service Alert" admin@example.com
# curl -X POST -d "text=$MESSAGE" "$WEBHOOK_URL"
fi
done
# Disk check
DISK_USAGE=$(df -h / | tail -1 | awk '{print $5}' | tr -d '%')
if [ "$DISK_USAGE" -gt 85 ]; then
echo "⚠️ Disk usage: ${DISK_USAGE}%" >> /var/log/disk-alert.log
fi
HEALTHEOF
chmod +x /opt/scripts/health-check.sh
# 4. Configure cron
(crontab -l 2>/dev/null; echo "") | crontab -
(crontab -l 2>/dev/null; echo "# Auto backup - daily at 2:00") | crontab -
(crontab -l 2>/dev/null; echo "0 2 * * * /opt/scripts/backup.sh > /dev/null 2>&1") | crontab -
(crontab -l 2>/dev/null; echo "# Health check - every 5 minutes") | crontab -
(crontab -l 2>/dev/null; echo "*/5 * * * * /opt/scripts/health-check.sh > /dev/null 2>&1") | crontab -
(crontab -l 2>/dev/null; echo "# Log cleanup - every Sunday") | crontab -
(crontab -l 2>/dev/null; echo "0 0 * * 0 journalctl --vacuum-size=200M > /dev/null 2>&1") | crontab -
echo ""
echo "=== Automated Ops System Setup Complete ==="
echo "Configured tasks:"
crontab -l | grep -v "^#"
echo ""
echo "Manual verification: crontab -l"
❓ よくある質問
Q: crontabの環境変数はターミナルと同じ? A: 違います!cronの実行環境のPATHは非常に最小限(通常
/usr/bin:/binのみ)。スクリプトが~/.bashrcで定義されたPATHに依存する場合は、スクリプト先頭でPATHを設定するか、cronコマンドで絶対パスを使用してください。
Q: systemdタイマーがcronより優れている点は? A: 1) 依存関係管理:タイマーは他のサービスに依存可能。2) ログ統合:journalctlへの自動ログ記録。3) 見送り実行:
Persistent=trueで次回起動時にキャッチアップ。4) より高精度:ミリ秒精度をサポート。5) 統一管理:systemctl list-timers。
Q: journalctlのログはハードディスクを埋める? A: デフォルトの最大はディスクの10%(ただし4GBまで)。
journalctl --vacuum-size=500Mで制限するか、/etc/systemd/journald.confでSystemMaxUse=500Mを設定してください。
Q: logrotateは自動実行される?cronの設定が必要? A: logrotateはシステム組み込みのcronジョブ(
/etc/cron.daily/logrotate)で毎日実行されます。/etc/logrotate.d/にルールを設定するだけで、追加のcron設定は不要です。
Q: crontabが実行されない時のデバッグ方法は? A: 3ステップのトラブルシューティング:1) cronサービスが動いているか確認:
systemctl status cron。2) cronログを確認:grep CRON /var/log/syslog。3) スクリプトを手動実行して動作確認:bash /path/to/script.sh。
📖 まとめ
- crontab:
minute hour day month weekday command、crontab -eで編集 - systemdサービス:
[Service] ExecStart=、Restart=on-failure - systemdタイマー:
OnCalendar=、Persistent=true - journalctl:
-u serviceでサービス別、-p errでレベル別、-fでリアルタイム追跡 - logrotate:
daily / rotate 7 / compressで自動ログローテーション
📝 練習問題
- 基本(難易度 ⭐):crontabを設定して毎時
dateを実行し/tmp/cron-test.logに追記し、journalctlでnginxまたはsshdの起動ログを確認する - 中級(難易度 ⭐⭐):シンプルな
hello.serviceを作成して起動(Type=simple)し、/var/log/test-app/*.logに対するlogrotate設定を作成(日次ローテーション、7日保持) - 上級(難易度 ⭐⭐⭐):毎日午前3時に
/tmpの期限切れファイルをクリーンアップするsystemdタイマーを書き、対応するサービスファイルを作成し、journalctlで実行ログを確認できるように設定する