自作LinuxでWebサーバーを起動する
連載ナビ
この連載は「Linuxを使えるようになる」ではなく、「Linuxがどう動いているのか」を理解するための実践シリーズです。
今回は Linuxを理解する 〜 LFSで学ぶLinux内部構造 〜 の Part 7 / 第2回として、仕組みのつながりを意識しながら学びます。
- 前回: nginxをソースからビルドする
- 次回: なし(シリーズ最終回)
—
サーバー起動の確認は「プロセス・ポート・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つのサービス起動に集約される