AI-TOL
📚 Complete Guide 🕐 25分 📊 中級者 ✓ Updated 2026-02-28

Cron式完全ガイド | 基礎から上級テクニックまで

Cron式構文の包括的ガイド。10の実践例、デバッグ手法、ベストプラクティス、FAQを含む。Linuxスケジュールタスクをマスターしよう。

Learn by doing

Practice with the while reading - free, no registration required

Open →

Real-World Examples

Case Study 1: 自動データベースバックアップ

Problem

スタートアップ企業が信頼できる毎日のPostgreSQLバックアップを必要としていました。最初の試みは失敗し、Cronログに "command not found" エラーが表示されていました。

Solution

問題はpg_dumpが環境変数を必要とし、スクリプトが相対パスを使用していたことでした。解決策は、スクリプトの先頭で環境変数を設定し、絶対パスを使用し、ログ記録とエラー処理を追加することでした:


                  #!/bin/bash
# データベースバックアップスクリプト

# 環境変数
export PGPASSWORD='your_password'
export PATH=/usr/local/bin:/usr/bin:/bin

# 設定
DB_NAME="mydb"
BACKUP_DIR="/backups"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$BACKUP_DIR/${DB_NAME}_${DATE}.sql"

# バックアップ作成
/usr/bin/pg_dump -h localhost -U postgres "$DB_NAME" > "$BACKUP_FILE" 2>&1

# 成功を確認
if [ $? -eq 0 ]; then
  echo "バックアップ成功: $BACKUP_FILE" >> /var/log/db_backup.log
  # 過去7日間のバックアップを保持
  find "$BACKUP_DIR" -name "${DB_NAME}_*.sql" -mtime +7 -delete
else
  echo "バックアップ失敗、エラーメッセージを確認してください" >> /var/log/db_backup.log
  exit 1
fi
                

Case Study 2: マイクロサービスヘルスチェック

Problem

Kubernetesにデプロイされたマイクロサービスは定期的なヘルスチェックを必要としていました。サービスが時々ハングアップし、自動再起動が必要でした。

Solution

定期的にヘルスチェックエンドポイントを呼び出すKubernetes CronJobリソースを作成:


                  apiVersion: batch/v1
kind: CronJob
metadata:
  name: health-check
spec:
  schedule: "*/5 * * * *"  # 5分ごと
  successfulJobsHistoryLimit: 3
  failedJobsHistoryLimit: 3
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: health-check
            image: curlimages/curl:latest
            imagePullPolicy: Always
            command:
            - /bin/sh
            - -c
            - |-
              response=$(curl -s -o /dev/null -w "%{http_code}" http://service:8080/health)
              if [ $response -ne 200 ]; then
                echo "ヘルスチェック失敗、HTTPステータス: $response"
                # 再起動ロジックをトリガー
                kubectl rollout restart deployment/my-service
              fi
          restartPolicy: OnFailure
                

Case Study 3: ログローテーションとアーカイブ

Problem

Webサーバーのログファイルが急速に増加し、ディスク容量が頻繁に不足していました。ログの自動ローテーション、圧縮、クリーンアップが必要でした。

Solution

logrotateとcronを使用したログ管理:


                  /var/log/nginx/*.log {
  daily
  missingok
  rotate 14
  compress
  delaycompress
  notifempty
  create 0640 www-data adm
  sharedscripts
  postrotate
    [ -f /var/run/nginx.pid ] && kill -USR1 $(cat /var/run/nginx.pid)
  endscript
  lastaction
    # 7日以上前の圧縮ログをアーカイブディレクトリに移動
    find /var/log/nginx -name "*.gz" -mtime +7 -exec mv {} /archive/nginx/ \;
  endaction
}

# Cronタスク(logrotateが自動作成)
0 0 * * * /usr/sbin/logrotate -f /etc/logrotate.conf
                

Case Study 4: 定時メールレポート

Problem

財務部門は毎朝8時に前日の売上レポートをメールで受信する必要がありました。

Solution

レポートを生成しメールを送信するスクリプトを作成:


                  #!/usr/bin/env python3
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import subprocess
from datetime import datetime, timedelta

def generate_report():
    """売上レポートを生成"""
    yesterday = datetime.now() - timedelta(days=1)
    date_str = yesterday.strftime('%Y-%m-%d')

    # SQLクエリを実行
    cmd = f'''
    mysql -u reporter -ppassword -h db.internal -e "
    SELECT product, SUM(amount) as revenue
    FROM sales
    WHERE DATE(created_at) = '{date_str}'
    GROUP BY product
    "
    '''
    result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
    return result.stdout

def send_email(report):
    """メールを送信"""
    msg = MIMEMultipart()
    msg['From'] = '[email protected]'
    msg['To'] = '[email protected]'
    msg['Subject'] = f'売上レポート - {datetime.now().strftime("%Y-%m-%d")}'

    body = f'''
    売上レポート
    {'='*40}

    {report}

    {'='*40}
    このメールはシステムにより自動生成されました。返信しないでください。
    '''
    msg.attach(MIMEText(body, 'plain'))

    with smtplib.SMTP('smtp.company.com', 587) as server:
        server.starttls()
        server.login('[email protected]', 'password')
        server.send_message(msg)

if __name__ == '__main__':
    report = generate_report()
    send_email(report)
                

Case Study 5: 分散スケジュールタスクの同期

Problem

複数のサーバーで実行されるスケジュールタスクは、重複処理を避けるために同時に1台のみ実行を確保する必要がありました。

Solution

相互排他を保証するために分散ロックを使用:


                  #!/bin/bash
# 分散データ処理タスク

LOCK_FILE="/tmp/data_processing.lock"
LOCK_TIMEOUT=3600  # 1時間タイムアウト
REDIS_HOST="redis.internal"

# ロックを取得
acquire_lock() {
  redis-cli -h "$REDIS_HOST" SETNX "$LOCK_FILE" "$HOSTNAME:$$" > /dev/null
  return $?
}

# ロックを解放
release_lock() {
  redis-cli -h "$REDIS_HOST" DEL "$LOCK_FILE" > /dev/null
}

# メインロジック
main() {
  echo "データ処理を開始..."

  # ロックの有効期限を確認
  lock_time=$(redis-cli -h "$REDIS_HOST" GET "$LOCK_FILE.ttl")
  if [ -n "$lock_time" ]; then
    current_time=$(date +%s)
    if [ $((current_time - lock_time)) -gt $LOCK_TIMEOUT ]; then
      echo "ロックが期限切れ、強制取得"
      release_lock
    fi
  fi

  # ロックの取得を試行
  if ! acquire_lock; then
    owner=$(redis-cli -h "$REDIS_HOST" GET "$LOCK_FILE")
    echo "タスク実行中: $owner"
    exit 0
  fi

  # ロックタイムアウトを設定
  redis-cli -h "$REDIS_HOST" SETEX "$LOCK_FILE.ttl" "$LOCK_TIMEOUT" "$(date +%s)" > /dev/null

  # タスクを実行
  python3 /scripts/process_data.py

  # ロックを解放
  release_lock

  echo "データ処理完了"
}

# 実行
main
                

Case Study 6: 段階的デプロイメント

Problem

サービス中断なしですべてのサーバーを段階的に更新する必要があり、各バッチ5台、2分間隔でした。

Solution

Cronトリガー段階的デプロイメントシステム:


                  #!/bin/bash
# 段階的デプロイスクリプト

DEPLOYMENT_CONFIG="/etc/deployment/config.json"
CURRENT_BATCH=$(cat /var/run/deployment_batch 2>/dev/null || echo 0)
BATCH_SIZE=5
BATCH_DELAY=120  # 2分

# サーバーリストを取得
servers=$(jq -r '.servers[]' "$DEPLOYMENT_CONFIG")
total_servers=$(echo "$servers" | wc -l)

# 現在のバッチを計算
start=$((CURRENT_BATCH * BATCH_SIZE))
end=$((start + BATCH_SIZE))

echo "デプロイバッチ $((CURRENT_BATCH + 1)) 開始、サーバー $start-$end"

# 現在のバッチをデプロイ
echo "$servers" | sed -n "${start},${end}p" | while read server; do
  echo "$server にデプロイ中..."
  ssh "$server" 'bash -s' < /scripts/update_service.sh
done

# 進行状況を記録
if [ $end -lt $total_servers ]; then
  echo $((CURRENT_BATCH + 1)) > /var/run/deployment_batch
  echo "バッチ完了、次を待機..."
else
  echo "全バッチデプロイ完了"
  rm -f /var/run/deployment_batch
fi
                

Case Study 7: 証明書自動更新

Problem

SSL証明書は90日ごとの更新が必要で、手動手順は忘れられがちでサービス中断を引き起こしていました。

Solution

Let's Encrypt自動更新を使用:


                  #!/bin/bash
# SSL証明書自動更新

DOMAINS="example.com www.example.com api.example.com"
EMAIL="[email protected]"
CERT_DIR="/etc/letsencrypt/live"
LOG_FILE="/var/log/cert_renewal.log"

log() {
  echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE"
}

# 証明書有効期限を確認
check_cert_expiry() {
  local domain=$1
  local cert_file="$CERT_DIR/$domain/cert.pem"

  if [ ! -f "$cert_file" ]; then
    log "証明書が見つかりません: $domain"
    return 1
  fi

  # 残り日数を計算
  expiry_date=$(openssl x509 -enddate -noout -in "$cert_file" | cut -d= -f2)
  expiry_epoch=$(date -d "$expiry_date" +%s)
  current_epoch=$(date +%s)
  days_left=$(( (expiry_epoch - current_epoch) / 86400 ))

  echo $days_left
}

# 証明書を更新
renew_cert() {
  log "証明書更新開始: $DOMAINS"

  certbot certonly --non-interactive --agree-tos     --email "$EMAIL"     --webroot     -w /var/www/html     -d $DOMAINS     >> "$LOG_FILE" 2>&1

  if [ $? -eq 0 ]; then
    log "証明書更新成功"

    # nginxをリロード
    systemctl reload nginx

    # 通知を送信
    echo "証明書更新成功" | mail -s "証明書更新" "$EMAIL"
  else
    log "証明書更新失敗"
    echo "証明書更新失敗、ログを確認してください" | mail -s "更新失敗" "$EMAIL"
    exit 1
  fi
}

# メインロジック
for domain in $DOMAINS; do
  days_left=$(check_cert_expiry "$domain")

  if [ $days_left -lt 30 ]; then
    log "証明書は$days_left日後に有効期限切れ、更新開始"
    renew_cert
  else
    log "証明書はあと$days_left日あります、更新不要"
  fi
done
                

Case Study 8: キャッシュウォームアップ

Problem

Eコマースサイトはピーク時間中にキャッシュヒット率が低く、ピーク前にキャッシュをウォームアップする必要がありました。

Solution

ピーク前に人気商品キャッシュを自動的にウォームアップ:


                  #!/usr/bin/env python3
import requests
import json
from datetime import datetime

API_BASE = "https://api.example.com"
CACHE_ENDPOINT = f"{API_BASE}/cache/warmup"

def get_popular_products():
    """人気商品を取得"""
    response = requests.get(f"{API_BASE}/analytics/popular", params={
      'limit': 1000,
      'time_range': '24h'
    })
    return response.json()['products']

def warmup_cache(product_ids):
    """キャッシュをウォームアップ"""
    chunks = [product_ids[i:i+50] for i in range(0, len(product_ids), 50)]

    for chunk in chunks:
      try:
        response = requests.post(CACHE_ENDPOINT, json={
          'product_ids': chunk,
          'ttl': 3600
        }, timeout=30)

        if response.status_code == 200:
          print(f"ウォームアップ成功: {len(chunk)} 商品")
        else:
          print(f"ウォームアップ失敗: {response.text}")
      except Exception as e:
        print(f"ウォームアップエラー: {e}")

def main():
  print(f"[{datetime.now()}] キャッシュウォームアップ開始")

  try:
    # 人気商品を取得
    products = get_popular_products()
    product_ids = [p['id'] for p in products]

    print(f"{len(product_ids)} 人気商品を取得")

    # キャッシュをウォームアップ
    warmup_cache(product_ids)

    print(f"[{datetime.now()}] キャッシュウォームアップ完了")

  except Exception as e:
    print(f"ウォームアップ失敗: {e}")
    exit 1

if __name__ == '__main__':
  main()
                

Case Study 9: データ同期とクリーンアップ

Problem

本番環境の匿名化データをテスト環境に同期する必要があり、過去30日間のみ保持する必要がありました。

Solution

データ同期とクリーンアップスクリプトを作成:


                  #!/bin/bash
# データベースをテスト環境に同期

PROD_DB="production"
TEST_DB="test_staging"
RETENTION_DAYS=30

log() {
  echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}

# 本番データをエクスポート(匿名化)
dump_data() {
  log "本番データエクスポート開始..."

  mysqldump -h prod-db.internal -u exporter -ppassword \
    --single-transaction \
    --quick \
    --lock-tables=false \
    --where="DATE(created_at) >= DATE(NOW()) - INTERVAL $RETENTION_DAYS DAY" \
    "$PROD_DB" \
    users orders products \
    | gzip > /tmp/dump.sql.gz

  # 匿名化
  zcat /tmp/dump.sql.gz | sed \
    -e 's/"\(email\|phone\|ssn\)":"[^"]*"/"\1":"***"/g' \
    -e 's/"\(credit_card\|password\)":"[^"]*"/"\1":"***"/g' \
    | gzip > /tmp/dump_anon.sql.gz

  log "データエクスポートと匿名化完了"
}

# テスト環境にインポート
import_data() {
  log "テスト環境へのインポート開始..."

  gunzip < /tmp/dump_anon.sql.gz | mysql -h test-db.internal -u importer -ppassword "$TEST_DB"

  log "データインポート完了"
}

# 一時ファイルをクリーンアップ
cleanup() {
  log "一時ファイルをクリーンアップ..."
  rm -f /tmp/dump*.sql.gz
}

# 古いテスト環境データをクリーンアップ
cleanup_old_data() {
  log "古いテスト環境データをクリーンアップ..."

  mysql -h test-db.internal -u admin -ppassword "$TEST_DB" <<EOF
DELETE FROM orders WHERE DATE(created_at) < DATE(NOW()) - INTERVAL $RETENTION_DAYS DAY;
DELETE FROM audit_logs WHERE DATE(created_at) < DATE(NOW()) - INTERVAL $RETENTION_DAYS DAY;
OPTIMIZE TABLE users, orders, products;
EOF

  log "古いデータクリーンアップ完了"
}

# メインプロセス
main() {
  log "===== データ同期開始 ====="

  dump_data
  import_data
  cleanup_old_data
  cleanup

  log "===== データ同期完了 ====="
}

main
                

Case Study 10: モニタリングとアラート統合

Problem

スケジュールタスクが失敗したとき、運用チームに即座に通知する必要があり、既存のモニタリングシステムとの統合が必要でした。

Solution

PrometheusとAlertmanagerを使用してアラート統合:


                  #!/bin/bash
# モニタリング統合付きタスクラッパー

TASK_NAME="data_import"
TASK_COMMAND="/usr/bin/python3 /scripts/import.py"
LOG_FILE="/var/log/tasks/${TASK_NAME}.log"
METRICS_FILE="/var/lib/node_exporter/textfile_collector/${TASK_NAME}.prom"

# 初期化
START_TIME=$(date +%s)
echo "# HELP task_last_success_timestamp 最後の成功時刻" > "$METRICS_FILE"
echo "# TYPE task_last_success_timestamp gauge" >> "$METRICS_FILE"

# タスクを実行
run_task() {
  echo "[$(date)] タスク実行開始: $TASK_NAME" >> "$LOG_FILE"

  $TASK_COMMAND >> "$LOG_FILE" 2>&1
  EXIT_CODE=$?

  END_TIME=$(date +%s)
  DURATION=$((END_TIME - START_TIME))

  if [ $EXIT_CODE -eq 0 ]; then
    echo "[$(date)] タスク成功、所要時間: ${DURATION}s" >> "$LOG_FILE"

    # 成功メトリックを記録
    echo "task_last_success_timestamp{task="$TASK_NAME"} $END_TIME" >> "$METRICS_FILE"
    echo "task_duration_seconds{task="$TASK_NAME",status="success"} $DURATION" >> "$METRICS_FILE"
  else
    echo "[$(date)] タスク失敗、終了コード: $EXIT_CODE" >> "$LOG_FILE"

    # 失敗メトリックを記録
    echo "task_last_success_timestamp{task="$TASK_NAME"} 0" >> "$METRICS_FILE"
    echo "task_duration_seconds{task="$TASK_NAME",status="failed"} $DURATION" >> "$METRICS_FILE"

    # アラートを送信
    curl -X POST http://alertmanager:9093/api/v1/alerts -d '[
      {
        "labels": {
          "alertname": "TaskFailed",
          "task": "'$TASK_NAME'",
          "severity": "critical"
        },
        "annotations": {
          "summary": "タスク失敗: '$TASK_NAME'",
          "description": "終了コード: '$EXIT_CODE'、ログ: '$LOG_FILE'"
        }
      }
    ]'

    exit 1
  fi
}

run_task
                

Best Practices

常に絶対パスを使用する

出力をログファイルにリダイレクト

PATH環境変数を設定

シバンを使用する

実行権限を追加

複雑な式をテスト

Master Today

Put your knowledge into practice with our free, privacy-focused tools

Try →