PK
パーネルカニック研究所
今日もどこかでkernel panic。
root@pk-lab:~#

自作LinuxでWebサーバーを起動する

連載ナビ

この連載は「Linuxを使えるようになる」ではなく、「Linuxがどう動いているのか」を理解するための実践シリーズです。

今回は Linuxを理解する 〜 LFSで学ぶLinux内部構造 〜 の Part 7 / 第2回として、仕組みのつながりを意識しながら学びます。

—

サーバー起動の確認は「プロセス・ポート・HTTP応答」の3層で行う

シリーズの最終到達点です。前回自分でビルドしたnginxを起動し、HTTP応答を返すところまで確認します。

結論から言うと、「Webサーバーが動いている」ことの確認は、次の3層を必ず分けて行います。

1. プロセス層: nginxのプロセスは生きているか(systemctl status / ps)

2. ソケット層: TCP 80番で待ち受け(LISTEN)しているか(ss -ltnp)

3. アプリ層: HTTPリクエストに正しいステータスで応答するか(curl -i)

「起動した気がする」ではなく、この3つの証拠を順に揃えるのが実運用の作法です。そしてこの3層は、この連載で学んできたこと——プロセスとPID(Part 2)、systemd(Part 5)、ソケットとss(Part 6)、ソースビルド(Part 7)——がすべて合流する場所でもあります。

3層はそれぞれ独立した失敗モードを持っているから

なぜ分けて確認するのか。理由は、各層の失敗原因がまったく別物で、混ぜて見ると原因を取り違えるからです。

  • プロセスは起動したが、ポートが競合してbindに失敗している(プロセス層OK・ソケット層NG)
  • LISTENはしているが、bindアドレスが 127.0.0.1 なので外部から届かない(ソケット層は一見OK・確認する場所を間違えるとNGに見える)
  • ポートまでは正常だが、ドキュメントルートの設定ミスで403や404が返る(アプリ層NG)

たとえば「curlで繋がりません」という報告だけでは、この3つのどれかが分かりません。しかし「ss ではLISTENしているのに、curlが403」と言えれば、調査対象は即座にnginxの設定と権限に絞れます。下の層から順に確認すれば、原因の存在範囲を一段ずつ狭めていけるのです。

起動して3層の証拠を順に揃える

前回 /usr/local/nginx にインストールしたnginxを起動します。起動前に必ず設定テストを行うのが第一歩です(SSHの sshd -t と同じ作法です)。


# 0. 設定ファイルの文法チェック
sudo /usr/local/nginx/sbin/nginx -t

nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful

この2行が出たら起動します。80番は特権ポート(1024未満)なので sudo が必要です。


# 1. 起動(デーモンとしてバックグラウンドに常駐する)
sudo /usr/local/nginx/sbin/nginx

# 2. プロセス層の確認: master と worker の親子関係を見る
ps -ef | grep [n]ginx

root       845     1  0 10:00 ?  00:00:00 nginx: master process /usr/local/nginx/sbin/nginx
nobody     846   845  0 10:00 ?  00:00:00 nginx: worker process

rootのmasterプロセスが設定読み込みとworker管理を行い、権限を落としたworkerプロセスが実際のリクエストを処理する——nginxの基本構造がそのまま出力に現れます。masterの親PIDが 1(init/systemd)である点も、Part 2で学んだプロセスツリーの復習です。


# 3. ソケット層の確認
sudo ss -ltnp | grep :80

LISTEN 0 511  0.0.0.0:80  0.0.0.0:*  users:(("nginx",pid=845,fd=6))

# 4. アプリ層の確認: -i でレスポンスヘッダも表示
curl -i http://127.0.0.1

HTTP/1.1 200 OK
Server: nginx/1.26.2
Content-Type: text/html
...

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...

HTTP/1.1 200 OK と Server: nginx ヘッダ、そして /usr/local/nginx/html/index.html の中身が返ってくれば、3層すべての証拠が揃いました。自分でビルドしたバイナリが、自分のLinux上でHTTPを話しています。

systemdのサービスにして「OSの一部」に組み込む

手動起動のままでは、再起動すると消えてしまいます。仕上げとして、Part 5で学んだsystemdのunitファイルを自分で書き、nginxをサービスとして登録します。/etc/systemd/system/nginx.service を作成してください。


[Unit]
Description=nginx web server (built from source)
After=network.target

[Service]
Type=forking
PIDFile=/usr/local/nginx/logs/nginx.pid
ExecStartPre=/usr/local/nginx/sbin/nginx -t
ExecStart=/usr/local/nginx/sbin/nginx
ExecReload=/usr/local/nginx/sbin/nginx -s reload
ExecStop=/usr/local/nginx/sbin/nginx -s quit

[Install]
WantedBy=multi-user.target

ポイントは3つです。

  • Type=forking — nginxは起動後に自分でデーモン化(fork)するため、systemdには「forkして親が抜けたら起動完了」と伝える。PIDの追跡には PIDFile を使う
  • ExecStartPre に -t を入れることで、設定が壊れている状態では起動処理に入らない
  • ExecReload の -s reload は、masterプロセスへのシグナル送信で、接続を切らずに設定を再読み込みする

登録して動かします(手動起動したnginxは sudo /usr/local/nginx/sbin/nginx -s quit で先に止めておきます)。


sudo systemctl daemon-reload
sudo systemctl enable --now nginx
systemctl status nginx --no-pager

Active: active (running) と表示され、enabled になっていれば、再起動後も自動でnginxが立ち上がります。apt install nginx が一瞬でやっていた「サービス登録」の正体は、このunitファイル1枚だったわけです。

ブラックボックスだった場所を、全部自分の言葉で説明できるか

自作したnginxを起動し、「プロセス(master/worker)・ポート(ss でLISTEN確認)・HTTP応答(curl -i で200 OK)」の3層で動作を証明し、unitファイルを書いてsystemdに組み込む——これがシリーズの最終到達点です。

ここまでの道のりを振り返ると、curl -i http://127.0.0.1 の1行の裏で起きていることを、すべて自分で組み立てたか、少なくとも自分の手で確認したことになります。カーネルとユーザー空間の境界、プロセスとPID 1、ツールチェーンとビルド、systemdとジャーナル、ソケットと経路。次に本番環境でトラブルに向き合うとき、どの層を疑い、どのコマンドで証拠を取るか——それを「推測」ではなく「経験」から選べるはずです。それがこの連載の狙いでした。

図解テキスト


boot complete
  -> systemd (PID 1)
  -> nginx.service (unitファイル)
       ExecStartPre: nginx -t
       ExecStart:    nginx (master -> worker)
  -> listen 0.0.0.0:80        … ss -ltnp で確認
  -> curl/browser -> HTTP/1.1 200 OK

この回で実行するコマンド

上から順番に実行すれば、この回の内容を体験できます。

まずはそのままコピペして実行し、次に1行ずつ意味を確認してください。


sudo /usr/local/nginx/sbin/nginx -t
sudo /usr/local/nginx/sbin/nginx
ps -ef | grep [n]ginx
sudo ss -ltnp | grep :80
curl -i http://127.0.0.1

ハンズオン手順

1. ターミナルを開いて、上のコマンドを1行ずつ実行する

2. nginx -t の syntax is ok / test is successful の2行を確認してから起動する

3. ps の出力で、masterとworkerのPIDと実行ユーザーの違いをメモする

4. curl -i のレスポンスから、ステータス行と Server ヘッダを書き写す

5. 本文のunitファイルを作成し、systemctl status nginx で active (running) を確認する

6. エラーが出たら、そのエラーメッセージをそのまま残す

追加で見るべき観点

### 1. systemd 管理下での状態確認


systemctl status nginx --no-pager
systemctl is-enabled nginx
  • Active: active (running) ならプロセスは生存
  • enabled なら再起動後も自動起動(sudo reboot 後に curl が通れば完全な証明)

### 2. エラー時のログ確認


journalctl -u nginx -b --no-pager | tail -n 50
sudo tail -n 50 /usr/local/nginx/logs/error.log
  • journalctl側 = systemdから見た起動の成否(ExecStartPre の失敗もここに出る)
  • error.log側 = nginx自身が書く設定・権限・bindのエラー

### 3. 設定の整合性確認


sudo /usr/local/nginx/sbin/nginx -t
sed -n '1,60p' /usr/local/nginx/conf/nginx.conf
  • listen 80; の行と、root html;(実体は /usr/local/nginx/html)が意図通りかを確認
  • 設定を変えたら「-t → nginx -s reload(またはsystemctl reload nginx)」の順を習慣にする

典型トラブルと対処

### bind() failed (98: Address already in use)

error.logに bind() to 0.0.0.0:80 failed (98: Address already in use) と出るパターンです。

  • 原因: 既に別プロセス(apt版のnginxやApacheなど)が80番を使用している
  • 対処:

sudo ss -ltnp | grep :80

users:(...) に表示された競合プロセスを特定・停止してから、nginxを起動し直します。

### Permission denied

  • 原因: 非rootで特権ポート80にbindしようとした、またはログ・ドキュメントルートへの読み書き権限不足
  • 対処: 起動は sudo(またはsystemd経由)で行い、workerの実行ユーザーが root 配下のファイルを読めるかを ls -l で確認する

### curl で接続できない

  • 原因候補: 起動失敗、bindアドレスの相違(127.0.0.1のみ)、ファイアウォール
  • 対処順(下の層から):

1. systemctl status nginx — プロセス層

2. sudo ss -ltnp | grep :80 — ソケット層(bindアドレスも見る)

3. curl -v http://127.0.0.1 — アプリ層(-v でTCP接続とHTTP交換の経過を表示)

ローカルの curl は通るのに別マシンから届かない場合は、nginxではなくbindアドレスかファイアウォール(セキュリティグループ)側を疑います。

図解: リクエスト処理の最小経路


Browser / curl
  -> TCP :80                      … ss / ファイアウォールの守備範囲
  -> nginx master (root)          … 設定読み込み・worker管理
  -> nginx worker (非特権ユーザー) … 実際のリクエスト処理
  -> document root (/usr/local/nginx/html/index.html)
  -> HTTP 200 response

この経路のどこで止まっているかを意識すると、

ログとコマンド結果を結びつけて診断できます。

チェックポイント

  • コマンドが最後まで実行できた
  • 「プロセス・ポート・HTTP応答」の3層それぞれの確認コマンドを言える
  • unitファイルの Type=forking と ExecStartPre の役割を説明できる
  • Address already in use が出たときの最初の一手を言える

今回理解できたこと

  • 起動確認は「プロセス・ポート・HTTP応答」の3層で見ると再現性が上がる
  • nginxはroot権限のmasterと非特権のworkerという構造で動いている
  • サービス登録の正体はsystemdのunitファイルであり、自分で書ける
  • Linux内部の各要素(プロセス・ソケット・init・ビルド)が1つのサービス起動に集約される