「コンテナが exit code 137 で突然落ちた」「ENOENT が出ているのにファイルは確かに存在する」—— バックエンド開発やインフラ作業でこういったログに詰まった経験は誰にでもあるはずです。 exit code と errno は Linux がプロセスに渡す「なぜ失敗したか」の第一の手がかりです。 読み方を知っているだけで、デバッグにかかる時間が大きく変わります。 この記事では、exit code と errno の仕組みを体系的に整理し、頻出パターンの原因と対処法を解説します。
exit code とは
exit code(終了コード・終了ステータス)とは、プロセスが終了するときに親プロセス(シェルや OS)へ返す 0〜255 の整数値です。0 は正常終了、1 以上は何らかの異常を意味します。
シェルでは直前に実行したコマンドの exit code を $? で確認できます。
# exit code の確認方法
ls /存在しないパス
echo $? # → 2(ls の「No such file or directory」)
curl https://example.com -o /dev/null -s
echo $? # → 0(正常終了)
CI/CD パイプラインでは exit code が 0 以外になるとビルドが失敗扱いになります。 スクリプトの最後のコマンドが返す exit code がそのままスクリプト全体の exit code になるため、 意図しない「成功」を防ぐには set -e とset -o pipefail を必ず設定してください。
重要な exit code 一覧
exit code には慣習的な意味があります。特に 1・2・126・127・130・137・139・141・143 は頻出です。
| exit code | 意味 | 主な原因 | Docker での発生ケース |
|---|---|---|---|
| 0 | 正常終了 | 処理成功 | コンテナが正常終了 |
| 1 | 一般的なエラー | アプリ固有のエラー・スクリプト内の異常 | アプリのクラッシュ・設定ミス |
| 2 | 使い方の誤り | コマンドの引数・オプションが不正 | ENTRYPOINT の引数ミス |
| 126 | 実行不可 | 実行権限がない(パーミッション不足) | entrypoint.sh に +x がない |
| 127 | コマンドが見つからない | PATH にコマンドが存在しない | イメージにバイナリがない |
| 130 | Ctrl+C による中断 | SIGINT(シグナル番号 2)を受信 | docker stop での割り込み |
| 137 | SIGKILL による強制終了 | OOM Killer・メモリ超過・docker kill | --memory 制限超過・OOM |
| 139 | SIGSEGV(セグメンテーション違反) | 不正メモリアクセス・バッファオーバーフロー | C/C++ の null ポインタ参照等 |
| 141 | SIGPIPE(パイプ切断) | パイプの読み取り側がすでに終了 | ログパイプ先の終了 |
| 143 | SIGTERM による正常終了 | docker stop・Kubernetes による pod 終了 | ヘルスチェック失敗・デプロイ時の入れ替え |
128+N の法則 — シグナル番号との対応
シグナルによってプロセスが終了した場合の exit code は 128 + シグナル番号 になります。 たとえば SIGKILL のシグナル番号は 9 なので、128 + 9 = 137 です。
# シグナル番号の一覧を確認する
kill -l
# 出力例(抜粋)
1) SIGHUP 2) SIGINT 9) SIGKILL 11) SIGSEGV 13) SIGPIPE 15) SIGTERM
# 計算例
137 - 128 = 9 → SIGKILL
139 - 128 = 11 → SIGSEGV
143 - 128 = 15 → SIGTERM
ログに見慣れない exit code が出たときは、128 を引いてシグナル番号を求め、kill -l の出力と照合すればすぐに原因のシグナルを特定できます。 ぱんだツールズのエラーコード横断検索では、 exit code やシグナル名からまとめて意味を調べることができます。
errno とは — システムコールの失敗を伝える番号
errno(エラーナンバー)とは、システムコールが失敗したときにカーネルがセットするエラー番号です。 C 言語の errno.h ヘッダーで定義されており、 ENOENT・EACCES のようなシンボル名と対応する整数値(例: ENOENT = 2)を持ちます。
システムコール(open・read・write・connect など)が -1 を返したとき、 その理由を知るために errno を参照します。 C 言語では perror("操作名") を呼ぶとエラー番号と説明文を stderr に出力できます。
// C 言語でのエラー確認例
FILE *fp = fopen("/etc/shadow", "r");
if (fp == NULL) {
perror("fopen"); // → "fopen: Permission denied"
// errno は EACCES (13) にセットされている
}
Python・Go・Node.js などの高レベル言語でも、システムコール由来のエラーは内部で errno を受け取り、 ENOENT なら「ファイルが見つかりません」のメッセージに変換して例外やエラーオブジェクトとして アプリケーションに伝えています。
重要な errno 一覧
以下は実務で頻繁に遭遇する errno のシンボル名・番号・意味・典型的な発生ケースです。
| シンボル名 | 番号 | 意味 | 典型的な発生ケース |
|---|---|---|---|
| EPERM | 1 | Operation not permitted | root 権限が必要な操作・コンテナの Capability 不足 |
| ENOENT | 2 | No such file or directory | ファイル・ディレクトリが存在しない・パスの途中が欠け |
| EACCES | 13 | Permission denied | ファイルの rwx パーミッションによる拒否 |
| EBUSY | 16 | Device or resource busy | マウント中のデバイスをアンマウント・使用中のファイル削除 |
| EEXIST | 17 | File exists | 作成しようとしたファイル・ディレクトリが既に存在 |
| EINVAL | 22 | Invalid argument | システムコールに渡した引数が不正・フラグの組み合わせが無効 |
| ENOMEM | 12 | Out of memory | malloc 失敗・カーネルのメモリ割り当て枯渇 |
| ENOSPC | 28 | No space left on device | ディスク残量ゼロ・inode 枯渇 |
| EPIPE | 32 | Broken pipe | パイプの読み取り側が先に終了・TCP コネクション切断後の書き込み |
| EADDRINUSE | 98 | Address already in use | ポートが既に使用中(サーバー二重起動・TIME_WAIT 残留) |
| ECONNREFUSED | 111 | Connection refused | 接続先がリッスンしていない・ファイアウォールでポートが閉じている |
| ETIMEDOUT | 110 | Connection timed out | ネットワーク到達不能・ルーティングミス・SYN パケットのドロップ |
デバッグ実践 — ログとコマンドの使い方
strace でシステムコールを追う
strace はプロセスが発行するシステムコールをリアルタイムで表示するツールです。 「なぜ ENOENT が出るのか」「どのファイルを開こうとしているか」を一発で確認できます。
# コマンドをトレース(失敗したシステムコールのみ表示)
strace -e trace=openat,connect,stat myapp 2>&1 | grep -v "= 0"
# 既存プロセスに attach(PID 1234 の場合)
strace -p 1234 -e trace=network
# ファイル操作をすべて記録してファイルに保存
strace -ff -o strace.log myapp
journalctl と dmesg でシステムログを確認
# システムログの末尾をリアルタイム追跡
journalctl -xe -f
# OOM Killer のログを確認(メモリ不足による強制終了)
dmesg | grep -i "oom\|killed process\|out of memory"
# 特定サービスのログ
journalctl -u nginx.service --since "10 minutes ago"
errno のシンボル名を調べる
# errno コマンド(moreutils パッケージ)で全一覧
errno -l
# 番号から名前を調べる
errno 2 # → ENOENT 2 No such file or directory
# Python でも確認できる
python3 -c "import errno; print(errno.ENOENT)" # → 2
エラー番号・シンボル名・HTTPステータスコードをまとめて調べたいときは、エラーコード横断検索が便利です。 キーワードで絞り込んで errno・exit code・HTTP ステータスを横断的に検索できます。 シェルコマンドの使い方に迷ったときはシェルコマンド横断検索も合わせてお使いください。
Docker コンテナの exit code 読み方
Docker コンテナが終了したときの exit code は docker inspect で確認できます。
# exit code を確認
docker inspect <コンテナID> --format="{{.State.ExitCode}}"
# 終了理由と OOM フラグも確認
docker inspect <コンテナID> --format="{{.State.OOMKilled}}"
# コンテナのログを確認
docker logs --tail 100 <コンテナID>
Kubernetes の場合
kubectl describe pod <pod名> のLast State セクションに exit code と Reason: OOMKilled / Error / Completed が表示されます。 exit code 137 + OOMKilled: true なら、Pod の resources.limits.memory を増やすか、 アプリのメモリ使用量を削減してください。
まとめ
- exit code は 0 が正常終了、1 以上が異常終了。シグナルによる終了は 128 + シグナル番号
- 137(SIGKILL)は OOM が最多原因。
dmesg | grep oomで確認 - 143(SIGTERM)は docker stop や Kubernetes の正常な再起動によるもの。異常ではないケースも多い
- errno はシステムコール失敗時にカーネルがセットするエラー番号。ENOENT・EACCES・ECONNREFUSED が特に頻出
- ENOENT はファイル本体だけでなく、パス途中のディレクトリが欠けていても発生する
- EACCES はパーミッション、EPERM は権限自体が存在しない(root 権限・Capability 不足)
straceでシステムコールを追跡すると、エラーの根本原因を最速で特定できる- Docker では
docker inspect --format="{{.State.ExitCode}}"で exit code を確認する