Linux: タスク自動化 — cron / systemd / journalctl

サーバー上のすべては自動化すべき — 定期バックアップ、ログクリーンアップ、ヘルスチェック。Linuxはシンプルなcronからモダンなsystemdタイマーまで、自動化ソリューションを提供しています。

📋 前提条件:まず以下を完了していること

1. このレッスンで学ぶこと


2. 「もう午前2時に起きてスクリプトを走らせる必要がない」ストーリー

(1) 悩み:毎日手動バックアップ

小明のバックアップスクリプトは毎日手動実行する必要がありました。彼は目覚ましをセット — 午前2時に起きて./backup.shを実行。1週間後、彼は疲弊していました。

(2) cronが彼を解放した

ボブがcrontabを教えました:

BASH
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

TEXT
  min  hour  day  month  weekday  command
(0-59)(0-23)(1-31)(1-12)(0-7)
                       (0=Sunday 7=Sunday)
TEXT
# 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

よく使う例:

TEXT
# 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で編集する習慣を身につけてください。

💡 ヒント:cronの実行環境は対話型シェルとは異なります — ~/.bashrc~/.profileなどは読み込まれず、PATHは通常/usr/bin:/binのみ。スクリプトがカスタム環境変数に依存する場合は、先頭で手動sourceするか、crontab内でコマンドに絶対パスを使用してください。

(2) systemdサービス

BASH
# 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タイマー

💡 ヒント:systemdタイマーかcronか? 定期タスクが他のサービスに依存する、ログが必要、システムダウン中に見送られた可能性がある(キャッチアップが必要)場合はsystemdタイマーを選択(Persistent=trueを設定)。これらのニーズがないシンプルな周期スクリプトならcronの方が軽量で直感的。新規プロジェクトではまずsystemdタイマーを試してみてください。

BASH
# 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構文:

TEXT
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 — ログビューア

BASH
# 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 — ログローテーション

💡 ヒント:logrotateはシステムのcron(/etc/cron.daily/logrotateのスクリプト)により毎日自動実行されます。/etc/logrotate.d/に設定を書くだけで済みます。設定変更後、logrotate -d /etc/logrotate.d/your-configでデバッグモードでプレビューし、自動的に反映させます。

BASH
# 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設定

TEXT
# 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サービスの作成

BASH
# 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を代替

BASH
# 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

出力:

TEXT
NEXT                        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ログクエリ

BASH
# 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

出力:

TEXT
Jul 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設定

BASH
# 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

出力:

TEXT
reading 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) ▶ 総合例:自動運用システム

BASH
#!/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.confSystemMaxUse=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


📖 まとめ


📝 練習問題

  1. 基本(難易度 ⭐):crontabを設定して毎時dateを実行し/tmp/cron-test.logに追記し、journalctlでnginxまたはsshdの起動ログを確認する
  2. 中級(難易度 ⭐⭐):シンプルなhello.serviceを作成して起動(Type=simple)し、/var/log/test-app/*.logに対するlogrotate設定を作成(日次ローテーション、7日保持)
  3. 上級(難易度 ⭐⭐⭐):毎日午前3時に/tmpの期限切れファイルをクリーンアップするsystemdタイマーを書き、対応するサービスファイルを作成し、journalctlで実行ログを確認できるように設定する
Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%