

Ubuntu 26.04 LTSを搭載したAMD Ryzen 9 9950X構成のワークステーションで、大規模なLLM(大規模言語モデル)の推論スクリプトやデータ集計タスクを運用する際、従来のcronによる定期実行だけでは限界があります。メモリ使用量が突発的に32GBを超えた際にプロセスがOOM Killerによって強制終了され、再起動の設定もされていないために、数時間ものダウンタイムが発生してしまうといったトラブルは珍しくありません。こうした「止まらないシステム」の構築には、単なる実行命令ではなく、プロセスの依存関係やリソース制限を制御できるsystemdによるサービス管理が不可欠です。
自作スクリプトを単なるバックグラウンドプロセスとして放置するのではなく、systemdユニットとして定義することで、cgroups v2を用いたCPU・メモリ使用量の厳密な制約から、cronに代わる高機能なsystemd timerの構築、さらにはjournalctlを活用した詳細なログ追跡まで、一元的な管理が可能になります。依存関係(After/Requires)に基づいた起動順序の制御や、Restartポリシーによる自動復旧の実装手法を習得し、堅牢なサーバー・ワークステーション環境を自らの手で構築する技術を身につけます。

Linuxシステムにおけるプロセス管理の核となるsystemdは、単なるデーモン起動ツールではなく、システム全体の依存関係をグラフ構造として管理する高度なリソースマネージャーです。自作サービスを構築する際、その最小単位となるのが「Unitファイル」です。Unitファイルには、.service(プロセス実行)、.timer(定期実行)、.mount(ファイルシステムマウント)など、役割に応じた拡張子が存在します。
自作サービスの設計において最も重要なのは、プロセスのライフサイクルをどのように定義するかという点です。例えば、特定のスクリプトを起動する場合、単にExecStartにパスを記述するだけでなく、そのプロセスが「どのような状態になったら成功とみなすか」を規定する必要があります。ここで重要になるのが、Type=ディレクティブの選択です。
最も一般的なType=simpleは、ExecStartで指定されたプロセスが起動した直後に「起動完了」とみなされます。一方、バックグラウンドへフォークする古いスタイルのデーモンにはType=forkingを使用し、プロセスのPIDをsystemdに追跡させる必要があります。さらに、現代的なアプリケーションでは、sd_notifyライブラリを用いてプロセス自身が準備完了を通知するType=notifyが推奨されます。これにより、ネットワークの初期化や巨大なモデルデータのメモリロード(例:16GBの重みデータ)が完了するまで、依存する他のサービスが待機状態を維持することが可能になります。
また、定期実行タスクにおいては、従来のcronとsystemd timerの選択が議論の対象となります。cronはシンプルですが、実行中のプロセス管理や、前回の実行が遅延した場合の制御に弱点があります。対してsystemd timerは、.serviceユニットとペアで動作するため、実行ログの集約、依存関係の定義、およびリソース制限の適用が容易です。
| 機能・特性 | cron | systemd timer |
|---|---|---|
| 実行トリガー | 分・時・日・月・曜による固定スケジュール | カレンダー形式(OnCalendar)および相対時間(OnUnitActiveSec) |
| 依存関係管理 | 不可(単発のプロセス起動のみ) | 可能(After=, Requires= 等で他のUnitと連携) |
| ログ管理 | /var/log/syslog等への標準出力転送が必要 | journalctlへ直接、構造化されたログとして記録 |
| 実行状態の追跡 | 実行中かどうかの判定が困難 | ユニットの状態(active/inactive)を正確に把握可能 |
| リソース制限 | cgroupによる制御が困難 | CPU, Memory, I/O等のcgroup v2による厳格な制限が可能 |
| エラー時の再試行 | 不可(次回のスケジュールまで待機) | Restart=ディレクティブにより自動再起動が可能 |
自作サービスの運用において、単一のプロセスが動いていること以上に重要なのは、「システムの状態に応じた正しい順序での起動・停止」です。systemdの真価は、Requires=、Wants=、After=、Before=といったディレクティブを用いた依存関係グラフの構築にあります。
例えば、AMD Ryzen 9 9950Xのような多コアCPUを搭載したサーバー上で、大量のデータ解析を行う自作サービスを運用する場合を想定します。このサービスがネットワーク経由で取得したデータを処理するのであれば、After=network-online.targetを指定し、ネットワークインターフェース(例:10GbE NIC)が完全にUP状態であることを保証しなければなりません。もしRequires=を使用した場合、依存するネットワークユニットが失敗すると、自作サービスも連鎖的に停止します。一方、Wants=は「あれば起動するが、失敗しても自分は止まらない」という緩やかな依存関係を定義できます。
サービスの堅牢性を高める上で不可欠なのが、Restart=ポリシーの設定です。メモリリークやセグメンテーション違反によってプロセスが異常終了した際、システムを自動的に修復する仕組みが必要です。
no: 再起動しない(デフォルト)always: 正常終了・異常終了に関わらず再起動on-failure: 非ゼロの終了ステータスまたはシグナルによる停止時に再起動on-aborted: シグナルによる中断時に再起動設定例として、Restart=on-failureとRestartSec=30sを組み合わせることで、クラッシュ発生から30秒後に自動復旧を試みる設計が可能です。この際、無限ループ的な再起動を防ぐためにStartLimitIntervalSec=(再起動試行間隔)とStartLimitBurst=(最大試行回数)の調整も必須です。
また、サービス実行時の環境変数管理にはEnvironmentFile=を使用します。/etc/default/my-serviceのようなファイルに、DB_PORT=5432やCACHE_SIZE=4Gといったパラメータを記述しておくことで、Unitファイルのロジックを書き換えることなく、設定値の変更のみで動作環境のチューニングが可能になります。これにより、開発環境と本番環境(例:メモリ128GB搭載機 vs メモリ8GB搭載機)での構成管理を容易にします。
自作サービスが期待通りに動作しない場合、その原因は「起動順序の不備」「権限不足」「環境変数の欠落」のいずれかに集約されることがほとんどです。systemdを用いたデバッグにおいて、最も強力な武器となるのがjournalctlによるログ解析です。
systemdは標準出力(stdout)および標準エラー出力(stderr)をすべてキャプチャし、バイナリ形式のジャーナルとして管理します。特定のサービスに関するログのみを抽出するには、journalctl -u my-service.serviceを実行します。さらに、トラブル発生時刻に絞り込む際は、--since "1 hour ago"や--since "2026-05-20 10:00:00"といった時間指定が極めて有効です。ログの重要度(Priority)に基づいたフィルタリングも可能です。例えば、致命的なエラーのみを確認したい場合は-p errを指定します。
よくある落とし穴の一つに、「ユーザーサービス(User Service)」と「システムサービス」の混同があります。systemctl --userで管理されるユニットは、そのユーザーがログインしている間のみ動作します。サーバー用途として、ログアウト後もバックグラウンドで実行し続けたい場合は、loginctl enable-linger <username>を実行して、ユーザーセッションの永続化(Linger)を有効にする必要があります。これを忘れると、「SSH接続中は動いているが、切断するとサービスが停止する」という不可解な挙動に悩まされることになります。
また、ファイルシステム権限の問題も頻発します。User=およびGroup=ディレクティブで実行ユーザーを制限している場合、そのユーザーが/var/lib/my-app/などの書き込みディレクトリに対して適切なパーミッション(例:drwxr-xr-x)を持っているかを確認しなければなりません。
以下のチェックリストは、自作サービスのデバッグ時に確認すべき主要な項目です。
systemd-analyze verify /etc/systemd/system/my.service で検証したかEnvironmentFile= で指定したパスは存在し、読み取り権限があるかWorkingDirectory= が正しく設定されており、相対パスによるファイル参照が壊れていないかdmesg や journalctl -k を確認し、OOM Killer(Out Of Memory Killer)によってプロセスが強制終了されていないか大規模な計算処理やデータ集約型の自作サービスを運用する場合、単にプロセスを動かすだけでなく、システム全体の安定性を損なわないための「リソース隔離」が求められます。現代のLinux(Ubuntu 24.04 LTS以降やRHEL 9等)では、cgroups v2(Control Groups version 2)が標準となっており、systemdを通じて非常に細粒度な制御が可能です。
特に重要なのが、メモリ使用量の制限です。例えば、Pythonベースのデータ解析スクリプトが予期せぬメモリ増大を起こし、システム全体をクラッシュさせるのを防ぐため、MemoryMax=4Gを設定します。これを超過した際、systemdは対象プロセスに対してOOM Killを発動させますが、他の重要なサービス(例:SSHデーモンやデータベース)への影響を最小限に抑えることができます。
CPUリソースの制御についても、CPUWeight=(旧CPULimitに近い概念)やCPUQuota=を用いて、計算負荷の高いタスクが他のプロセスを妨害しないように調整します。
CPUQuota=50%: プロセスが使用できるCPU時間を、論理コア1つ分の半分に制限します。CPUWeight=100: 他のサービスとの相対的な優先度を指定します(デフォルトは100)。さらに、I/O負荷の制御も重要です。大量のログ書き込みやデータ移動を行うサービスには、IOWeight=を設定し、ディスクI/Oの帯域を制限することで、システムの応答性能(Latency)を維持できます。[NVMe Gen5 SSD](/glossary/ssd)のような高速なストレージを使用している環境でも、バックグラウンドのバッチ処理がディスクバスを占有してしまうと、Webサーバー等のリアルタイム性が求められるサービスに致命的な遅延をもたらします。
リソース使用状況のモニタリングには、systemd-cgtopという強力なツールがあります。これはtopコマンドと同様の操作感で、cgroupごとのCPU使用率、メモリ消費量、I/Oスループットをリアルタイムに表示します。
以下に、高負荷な計算プロセス(例:[AMD Ryzen 9 9950X](/glossary/ryzen-9950x)上のシミュレーションタスク)を管理するためのUnitファイル設定のベストプラクティスを示します。
[Service]
# プロセスの実行ユーザーとグループを指定
User=analyzer
Group=analyzer-group
# メモリ使用量を最大4GBに制限(超過時はOOM Killer対象)
MemoryMax=4G
MemoryHigh=3.5G
# CPU使用率を論理コア1つ分(200%相当の環境なら50%に制限)の半分に抑える
CPUQuota=50%
# I/O優先度を低めに設定(他のプロセスへの影響を最小化)
IOWeight=20
# 異常終了時に30秒後に自動再起動
Restart=on-failure
RestartSec=30s
# プロセスが完了したことを親プロセスに通知する仕組みを利用
Type=notify
このように、cgroups v2を活用したリソース制限を組み込むことで、自作サービスは「単体で動くプログラム」から、「システム全体の安定性に寄与する、管理されたコンポーネント」へと昇華されます。
Linuxシステムの運用において、タスクの実行タイミングとプロセスのライフサイクルをどのように制御するかは、システムの安定性とリソース効率に直結します。従来、定期実行にはcronが、プロセス管理にはシェルスクリプトやinit系スクリプトが用いられてきましたが、現代的なLinuxディストリビューション(Ubuntu 26.04 LTSやRHEL 10等)では、systemdによる一元管理が標準です。
特に、単なる時間ベースの実行だけでなく、前工程の完了を待機する「依存関係」や、特定のメモリ使用量を超えないための「cgroups v2によるリソース制限」を同一のユニット定義内で完結できる点が、systemdの最大の強みです。以下に、運用設計時に直面する主要な選択肢と技術仕様の比較をまとめました。
定期実行タスクの実装において、最も頻繁に検討されるのがcronとsystemd Timerの使い分けです。cronは軽量で設定が容易ですが、実行状況の追跡や依存関係の制御には不向きです。一方、systemd Timerはユニットファイルとしての管理が可能であり、モノトニック(経過時間ベース)な指定も可能です。
| 機能・特性 | systemd Timer | cron (crontab) | 運用上の差異 |
|---|---|---|---|
| トリガー方式 | カレンダー型 + 経過時間型 | カレンダー型のみ | 起動後○分後に実行等の指定可否 |
| 依存関係制御 | 他のユニットとの連携が可能 | 単独プロセスとして動作 | ネットワーク起動待ち等の制御 |
| ログ管理 | journalctlで一元管理 | syslog または 個別ログファイル | ログの検索性と集約性 |
| エラーリカバリ | Restart設定による自動再試行 | 再実行には外部スクリプトが必要 | プロセス異常終了時の復旧能力 |
| リソース制限 | cgroups v2による詳細制御 | OS全体の制約に依存 | CPU/メモリ使用量の隔離精度 |
systemd Timerを採用する場合、OnCalendar=(例:Daily)だけでなく、OnUnitActiveSec=10minといった「前回の実行から10分後」という指定が可能です。これにより、タスクの実行間隔が前回の終了時刻に依存して変動する、高精度なバッチ処理設計が可能になります。
serviceユニットを定義する際、Type=ディレクティブの設定は、systemdが「プロセスが起動した」と判断する基準を決定します。この設定を誤ると、依存している他のサービスが、メインプロセスが準備完了になる前に起動してしまうという不整合が発生します。
| Type設定値 | プロセス・ライフサイクル | 成功判定のタイミング | 最適なユースケース |
|---|---|---|---|
simple | フォアグラウンドで継続実行 | execve() 直後 | 常駐型のデーモン(nginx等) |
forking | 親プロセスが子を生成して終了 | 親プロセスの終了時 | 従来のデーモン化プロセス |
oneshot | 単発の処理を実行して終了 | プロセスが完全に終了した時 | 初期化スクリプト、DBマイグレート |
notify | 実行中にソケット通信を行う | sd_notify() 受信時 | 準備完了を通知できる高度なアプリ |
例えば、Pythonで記述されたWebサーバーを運用する場合、Type=simpleではプロセスが起動した瞬間に「起動完了」とみなされます。もしアプリケーション内で大規模なモデルのロード(例:32GBのメモリ確保)が必要な場合、ロードが終わる前に後続のサービスがリクエストを送ってしまうため、Type=notifyまたはType=forkingによる制御検討が必要です。
複雑なマイクロサービス構成では、サービスの起動順序(Ordering)と、必須条件(Dependency)を分離して考える必要があります。After=は「順番」を、Requires=は「生存条件」を定義します。
| ディレクティブ | 動作の詳細説明 | 依存先停止時の影響 | 主な実装シナリオ |
| :--- | :連結されたユニットの起動順序を制御 | 依存元が停止しても継続 | ネットワーク有効化後の実行 |
| Requires= | 必須の依存関係(強結合) | 依存先停止時に自ユニットも停止 | DB稼働が前提のアプリケーション |
| Wants= | 努力目標的な依存関係(弱結合) | 依存先停止でも継続 | ログ収集ツールの同時起動 |
| BindsTo= | 極めて強い結合(プロセスと密接) | 依存先停止時に即座に強制停止 | ネットワークインターフェース等 |
Requiresを使用する場合、データベース([PostgreSQLなど)の停止がアプリケーションサービスの連鎖的な停止を招くため、可用性を重視する構成ではWantsを選択し、不測の事態によるサービスダウンを防ぐ設計が推奨されます。
マルチテナントなサーバーや、コンテナ環境に近い運用を行う場合、特定のユニットがシステム全体のメモリを枯渇させないよう、serviceファイル内でリソース制約を課すことが不可欠です。
| 設定項目名 | 指定値の例 | 対象リソース | システムへの影響・効果 |
|---|---|---|---|
CPUQuota= | 50% | CPU使用率(コア単位) | CPU飢餓状態(Starvation)の防止 |
MemoryMax= | 2G | RAM使用量の上限 | OOM Killerによる強制終了の閾値 |
IOWeight= | 100 (range 1-1000) | ディスクI/O帯域 | 重い書き込み処理の優先度制御 |
TasksMax= | 500 | プロセス・スレッド数 | フォーク爆弾(Fork Bomb)への対策 |
特にMemoryMaxの設定は重要です。Java仮想マシン(JVM)を使用するアプリケーションにおいて、Xmx(Heap最大値)よりも低い値をMemoryMaxに設定してしまうと、OSによってプロセスが即座にKillされるため、必ず物理メモリの余裕を持たせた設計を行ってください。
systemdには、システム全体を管理する「Systemインスタンス」と、特定のログインユーザーに紐づく「Userインスタンス」が存在します。権限分離(Privilege Separation)の観点から、どちらのレベルでユニットを定義すべきかの判断基準を示します。
| 比較項目 | System Unit | User Unit | 管理上の差異 |
|---|---|---|---|
| 管理者権限 | root / sudo必須 | 一般ユーザー権限で可能 | セキュリティ境界の設計 |
| 設定ディレクトリ | /etc/systemd/system/ | ~/.config/systemd/user/ | 設定ファイルの配置場所 |
| 実行タイミング | OS起動時(Boot) | ユーザーログイン時 | ログインセッションへの依存度 |
| インスタンス管理 | systemctl | systemctl --user | コマンドライン引数の違い |
| 対象プロセス | システム基盤・デーモン | ユーザー固有のスクリプト | 影響範囲(Blast Radius)の差 |
デスクトップ環境や、特定の開発者のみが利用するバッチ処理においては、User Unitを用いることで、root権限を介さずに安全に自動化プロセスを構築できます。これにより、万が一スクリプトに脆弱性があった場合でも、システム全体の侵害(System-wide compromise)を防ぐことが可能です。
AWSのt3.microインスタンス(vCPU 2、メモリ1GiB)を利用する場合、systemd自体の動作による追加料金は発生しません。しかし、サービスが増加してCPU使用率が継続的に高まると、バーストクレジットを使い果たし、パフォーマンス低下を防ぐためにt3.smallへのアップグレードが必要になります。この際、インスタンス単価が約2倍に跳ね上がるため、リソース設計がコスト管理の鍵となります。
Raspberry Pi 5(8GBモデル)などのシングルボードコンピュータで、systemdによるバックグラウンドタスクを運用する場合、アイドル時の消費電力は約3W〜5W程度です。軽量なPythonスクリプトの実行であれば、CPU負荷が劇的に増えない限り、電気代への影響は月額数十円から百数十円の範囲に収まります。ただし、高負荷な計算処理(ML推論等)を伴うサービスでは、消費電力が10Wを超えることもあります。
###Q3. systemd Timerとcron、どちらを採用すべきですか?
実行の精度と依存関係の管理が必要ならsystemd Timer一択です。cronは「分単位」の起動しかできませんが、systemd Timerはミリ秒単位の制御や、After=network.targetのようなネットワーク起動待ちの設定が可能です。一方で、既存の大量のシェルスクリプトをそのまま流用したい場合は、学習コストの低いcronの方が手軽です。ただし、ログの一元管理(journalctl)という観点ではTimerに分があります。
特定のユーザーログイン時のみ動作させたいデスクトップ自動化ツールなどはUser Service(--userモード)を使用します。root権限を必要とせず、/home/user/.config/systemd/user/で管理できるためセキュリティ面でも有利です。一方、Webサーバーやデータベースのように、OS起動直後から全ユーザーに対してリソースを提供する必要がある場合は、System Serviceとして定義し、システム全体で管理すべきです。
U[bun](/glossary/bun-runtime)tu 22.04 LTS以降で標準採用されているcgroup v2では、MemoryMaxの設定が厳密に機能します。例えば、特定のサービスに対してMemoryMax=512Mと設定した場合、プロセスがこの上限を超えるとカーネルによってOOM Killerが作動し、即座にプロセスが強制終了されます。制限をかける際は、Pythonなどのランタイムが消費するベースメモリ量に、余裕を持たせた数値を割り当てることが運用の鉄則です。
/usr/bin/python3のようなシステム標準のパスを使用すると、OSアップデート時にライブラリ構成が変わり、サービスが起動しなくなるリスクがあります。解決策として、venv(仮想環境)を作成し、.serviceファイルのExecStartに/home/user/app/venv/bin/pythonとフルパスで記述してください。これにより、Python 3.12などの特定のバージョン依存関係を完全に分離・固定できます。
Exit Code 137は、LinuxカーネルのOOM Killerによってプロセスが強制終了されたことを示しています。多くの場合、systemdユニットファイル内で設定したMemoryMaxやMemoryHighの制限値を超過したことが原因です。journalctl -u [サービス名]を実行し、ログ内に「Out of memory」の文言がないか確認してください。解決には、割り当てメモリの増量か、アプリケーション側のメモリリーク対策が必要です。
Requires=は指定したユニットが失敗した場合に自身のサービスも停止させる強力な設定ですが、相手側の起動完了を待つわけではありません。ネットワーク待ちが必要な場合は、必ずAfter=network-online.targetを併記してください。また、依存先が「起動中」の状態で自サービスが動き出すとエラーになるため、Wants=と組み合わせて、依存ユニットの起動プロセスを確実に制御する記述が必要です。
標準的なDockerコンテナ(ランタイム:runc)では、systemdの動作は推奨されません。コンテナは「1コンテナ・1プロセス」が原則であり、systemdのようなPID 1を必要とする複雑な初期化プロセスを含めると、イメージサイズが増大し、コンテナの軽量性が損なわれます。コンテナ内の複数プロセス管理には、systemdの代わりにsupervisordや、Kubernetesのサイドカーパターンを利用するのが現在の業界標準です。
2026年現在、Wasmtimeなどのランタイムを用いて、WebAssemblyモジュールをsystemdサービスとして管理する手法が注目されています。従来の[Dockerコンテナと比較して、メモリ消費量を数MB単位に抑えつつ、ネイティブに近い実行速度を実現できるため、エッジコンピューティングにおける超軽量なバックグラウンドタスクとしての活用が進んでいます。cgroupによるリソース制限と組み合わせることで、極めて安全なマルチテナント環境が構築可能です。
[Service]、[Timer]、[Install] の各セクションの役割を正しく理解し、目的に応じたUnitファイルの設計を行う。After= や Requires= を適切に設定し、ネットワークやファイルシステム等のリソース準備完了を待機させる構成がシステムの堅牢性を決める。Restart=on-failure 等の再起動ポリシーと、cgroupsによるメモリ(MemoryMax)やCPU(CPUWeight)の制限を組み合わせ、単一プロセスの暴走を防ぐ。systemd --user を適切に使い分け、セキュリティリスクを最小化した運用設計を検討する。journalctl -u [unit_name] を活用し、標準出力・標準エラー出力を詳細に追跡する。まずは自身の開発環境で、簡単なシェルスクリプトをsystemdサービスとして登録し、systemctl status での動作確認から始めてみてください。習熟後は、バックアップ処理などの定期実行タスクをTimerへ移行し、cgroupsによるリソース制限の実装に挑戦しましょう。

PCパーツ・ガジェット専門
自作PCパーツやガジェットの最新情報を発信中。実測データに基づいた公平なランキングをお届けします。
よくお寄せいただく質問にお答えします
マザーボード
G.SKILL Trident Z5 Neo RGBシリーズ DDR5 RAM (AMD EXPO) 96GB (2x48GB) 6000MT/s CL26-36-36-96 1.45V デスクトップコンピュータメモリ U-DIMM - マットホワイト (F5-6000J2636H48GX2-TZ5NRW)
¥249,888メモリ
64GB 2X32GB DDR5 5600MHZ PC5-44800 2Rx8 1.1V CL46 288ピン ECC アンバッファード UDIMM NEMIX RAMメモリキット Lenovo ThinkSystem ST45 V3 タワーサーバー対応
¥409,780メモリ
Kingston (キングストン) 64GB DDR5 SDRAM メモリモジュール - サーバー マザーボード コンピューター用 - 64GB - DDR5-5600/PC5-44800 DDR5 SDRAM - 5600 MHz デュアルランクメモリ - CL46-1.10 V - ECC - 登録済み - 288ピン - DIMM
¥922,492メモリ
サーバー 用RAM メモリ96GB DDR5 5600MHz PC5-44800 ECC-RDIMM 2Rx4 (EC8 10x4) デュアルランク 1.1V ECC Registered DIMM 288ピン M321RYGA0PB0-CWM
¥999,800マザーボード
NEMIX RAM 64GB (1X64GB) DDR4 2666MHZ PC4-21300 4Rx4 1.2V CL19 288ピン ECC LRDIMM Lenovo ThinkSystem 4X77A78614対応 負荷軽減サーバーメモリ
¥77,545マザーボード
NEMIX RAM 64GB (1X64GB) DDR4 2666MHz PC4-21300 4Rx4 1.2V CL19 288ピン ECC LRDIMM 負荷低減サーバーメモリ
¥62,623この記事で紹介したOS・ソフトウェアをAmazonで確認できます。Prime対象商品なら翌日届きます。
Q: さらに詳しい情報はどこで?
A: 自作.comコミュニティで質問してみましょう。