はじめに
この新しい環境では、DAYMARK の安全な公開ラッパーがすでに用意されています。次に必要なのは、専用の帳票アカウントで動く実際のスケジュールです。端末での手動実行の成功は、予定ジョブが入力を見つけて読み取れることの証明にはなりません。
対応する systemd service と timer を設定して、繰り返される実際の起動を観察し、直前の帳票を失わずにディレクトリと入力のエラーから復旧します。Daymark は業務サイト、Workstation の開始場所は /srv/daymark-sales です。基本的な端末操作で十分です。前のガイド付き演習で扱ったシェルラッパーの概念を再利用します。計算プログラムとラッパーのインストールは不要です。
実際の timer からエクスポートする
このステップでは、正しい業務結果を確かめ、systemd にエクスポートを 2 回実行させます。Daymark から始めて Records を開きます。初期の元データは 11 個、3450 セントです。Reports は前回分の TOTAL,1,100 のままです。ジョブはこのファイルを実際の閉店集計に置き換える必要があります。
Workstation に切り替えます。この環境は独立しており、正しいラッパーは準備済みですが帳票 timer は未設置です。cat はファイル、pwd は現在のディレクトリを表示します。
pwd
cat publish.sh
ラッパーは出力先の隣に書き、計算成功後だけ名前を変更します。入力と出力の引数を受け取ります。id はアカウントの身元、ls -ld はディレクトリの所有者とアクセス権ビットを表示します。ディレクトリを通過するには検索権限、ファイル作成には書き込み権限が必要です。
id daymark-report
ls -ld reports
準備済みのシステムアカウントが reports/ を所有しています。入力の読み取り権限と親ディレクトリの検索権限によって orders.csv を読めます。帳票ディレクトリの所有権によって公開できます。root や全員への書き込み許可を近道にしないでください。
systemd はこの instance のサービス管理機構です。.service ファイルは 1 回の実行、同名の .timer はそれをいつ要求するかを定義します。Type=oneshot は有限ジョブの終了を待ちます。User は実際の実行アカウントを選びます。WorkingDirectory は相対パス orders.csv の解決場所を決めます。ExecStart は実行プログラム、ラッパー、出力を絶対パスで指定します。端末の現在地や対話シェルの設定はシステムサービスに引き継がれません。RemainAfterExit は設定しません。終了したジョブが inactive になれば、次の timer イベントで再実行できます。
sudo tee FILE は保護されたファイルを書きながら内容も表示します。<<'UNIT' は引用符付き here-document です。対応する終了語まで貼り付けます。指定した D10 units だけを作成します。
sudo tee /etc/systemd/system/d10-sales.service <<'UNIT'
[Unit]
Description=Daymark sales export
[Service]
Type=oneshot
User=daymark-report
WorkingDirectory=/srv/daymark-sales
ExecStart=/bin/bash /srv/daymark-sales/publish.sh orders.csv /srv/daymark-sales/reports/sales.csv
UNIT
この演習用 timer は有効化から 5 秒後に始まり、前回の起動から 20 秒後に次を要求します。AccuracySec=1s は小さな実行時間幅を許容します。マシン側でさらに遅れることもあります。これは短い練習で、本番の日次スケジュールではありません。毎日決まった時刻なら、この 2 つの相対指定の代わりに OnCalendar=*-*-* 23:00:00 を使い、サーバーのタイムゾーンを明示的に選ぶ方法があります。本演習では実際の実行を観察するため短い間隔を使い続けます。
sudo tee /etc/systemd/system/d10-sales.timer <<'UNIT'
[Unit]
Description=Daymark short training schedule
[Timer]
OnActiveSec=5s
OnUnitActiveSec=20s
AccuracySec=1s
[Install]
WantedBy=timers.target
UNIT
daemon-reload は unit 定義を再読み込みします。enable --now は今後の起動用に timer を timers.target に接続し、今も起動します。開始するのは timer であり、手動エクスポートではありません。
sudo systemctl daemon-reload
sudo systemctl enable --now d10-sales.timer
sleep 50 は待つだけでジョブを実行しません。service を手動で起動したり、シェルのループで timer を代用したりしないでください。
sleep 50
list-timers は実際の LAST と NEXT の起動情報を表示します。--no-pager はスクロール表示器を開かず直接出力します。
systemctl list-timers d10-sales.timer --no-pager
journal は systemd の実際のイベントログです。-u は対象 unit、-n 20 は出力件数を指定します。少なくとも 2 回の完全な実行があり、uid=... cwd=/srv/daymark-sales、帳票ハッシュ、正常終了が確認できるはずです。
sudo journalctl -u d10-sales.service -n 20 --no-pager
数値 uid を id daymark-report と比べます。成功した oneshot の終了後に inactive (dead) になるのは正常です。動作中の timer が次の実行を要求します。show -p は特定の状態プロパティを選びます。
systemctl show d10-sales.service -p User -p WorkingDirectory -p Result -p ExecMainStatus --no-pager
User=daymark-report、正しいディレクトリ、Result=success、ExecMainStatus=0 が期待値です。起動がなければ systemctl status d10-sales.timer --no-pager を読み、この unit ファイルだけを直して daemon-reload を実行し、d10-sales.timer だけを再起動します。restart は timer を停止して開始し、timer の変更を反映します。再度待って journal を確認してください。CHDIR なら WorkingDirectory、Permission denied なら入力と帳票ディレクトリの所有権を確認し、権限を広げる方法で解決しないでください。
Daymark に戻り、Reports を再読み込みして新しい CSV をダウンロードします。合計は TOTAL,11,3450 です。Records をもう一度開いてホームに戻り、通常の業務ページも利用可能なことを確認します。
次の予定実行で復旧する
このステップでは、実際の予定実行の失敗を観察し、前の帳票を保ち、復旧後に新しい閉店集計を公開します。Workstation から始めます。D10 units と本演習の入力だけを扱い、無関係なサービスは停止しないでください。
まず作業ディレクトリの誤りを作ります。sed -i は指定ファイルをその場で編集し、s|old|new| は一致する文字列を置換します。意図的な障害です。
sudo sed -i 's|WorkingDirectory=/srv/daymark-sales|WorkingDirectory=/tmp|' /etc/systemd/system/d10-sales.service
sudo systemctl daemon-reload
sleep 30
sudo journalctl -u d10-sales.service -n 15 --no-pager
プロセスは帳票アカウントのままですが、orders.csv は /tmp 内で解決され、Export failed: ... No such file ... で失敗します。作業ディレクトリ自体がなければ、ラッパー開始前に CHDIR になります。daemon-reload は定義を更新するだけでジョブを実行しません。この失敗した試行は timer が起動しました。
cat reports/sales.csv
末尾は引き続き TOTAL,11,3450 の必要があります。Daymark で Reports を再読み込みしてダウンロードし、同じバイト列をまだ利用できることを確認します。Workstation に戻ってディレクトリを直します。
sudo sed -i 's|WorkingDirectory=/tmp|WorkingDirectory=/srv/daymark-sales|' /etc/systemd/system/d10-sales.service
sudo systemctl daemon-reload
次に入力の失敗を練習します。cp は用意された不正な fixture を、この演習の orders.csv だけに上書きします。計算プログラムは非 0 で終了し、ラッパーは直前の帳票を変えずに残す必要があります。
cp fixtures/broken.csv orders.csv
sleep 30
sudo journalctl -u d10-sales.service -n 15 --no-pager
意図的な不正数値の失敗が出ます。show は journal 表示コマンドの成功とは独立して、最後の実際の終了状態を示します。
systemctl show d10-sales.service -p Result -p ExecMainStatus --no-pager
Result=exit-code と ExecMainStatus=1 が期待値です。前の帳票が残ることを確かめます。
cat reports/sales.csv
Tea が 2 個増えた有効な新しい閉店集計に戻します。次の起動時に 13 個、3950 セントへ更新される必要があります。
cp fixtures/next.csv orders.csv
sleep 30
sudo journalctl -u d10-sales.service -n 15 --no-pager
cat reports/sales.csv
Tea,5,1250 と TOTAL,13,3950 が表示され、journal に後の実際の公開記録があるはずです。古いままなら最新のエラーを読み、ディレクトリ、入力名、ラッパーを直して次の timer イベントを待ちます。CSV を手書きで置き換えたり service を手動で起動したりしないでください。失敗した oneshot もこの timer で再実行できます。
Daymark で Records と Reports を再読み込みし、再度ダウンロードして変更後の元データと TOTAL,13,3950 を比較します。採点用に timer を動作させたままにしてください。本演習は再起動時の復旧、タイムゾーン方針、複数マシンの並行実行、実行されなかった日の集計をテストしません。
まとめ
完全な CSV の公開、意味のある失敗状態、復旧を練習しました。予定帳票は、実際の実行ユーザー、起動記録、業務合計が一致して初めて役立ちます。この単一 instance の演習は架空データと短い練習用スケジュールを使い、完全な本番帳票基盤ではありません。



