nginx を入れて、サービスの入口を公開する
外部へ直接出ない環境で nginx を導入し、プロセス・待受 port・別ノード応答・ログを別々の証拠として確認します。
| 所要時間 | 約 60 分(概念 15 分+演習 45 分) |
|---|---|
| 前提知識 | Part 1 のパッケージ・ファイル・権限操作、Part 2 の IP・port・経路、Part 3 の systemd・journal と署名付き APT リポジトリの設定(Ch.6) |
| 使用機材 | 入口サーバ front01、操作端末 workstation01、署名済み内部 APT リポジトリ |
| 関連章 | 前: Part 3 / Chapter 7「バックアップとリストア」/後: Part 4 / Chapter 2「PostgreSQL を用意し、残高を安全に更新する」 |
「入っているものを回す」から「自分で入れて公開する」へ
Part 3 では、すでに導入されているサービスを起動し、状態とログを読み、変更後に復旧しました。Part 4 では一歩進み、必要なソフトウェアを取得して導入し、複数のサーバをつないで、ひとつのサービスを組み立てます。本章で作るのは、その最初の部品となる nginx(Web サーバのソフトウェア)の入口です。
演習環境のサーバは外部へ直接通信できません。そのため、インターネット上の配布元を前提にした素の apt update ではなく、署名済み内部 APT リポジトリを設定して取得経路を先に確認します。導入後は「プロセスが動いている」「port で待っている」「サーバ自身から応答する」「別ノードから届く」を別々に確かめます。
配布サーバ
│ 公開鍵・InRelease
│ Packages・.deb
▼
署名と hash を検証する APT
│
▼
front01
├─ nginx のプロセス
├─ HTTP の待受 port
└─ access log
▲
│ 別ノードから HTTP
workstation01
この章のゴール: 外部へ直接出られない環境で nginx を導入し、プロセス・待受 port・ローカル応答・別ノード応答・ログを根拠に、入口が使える状態を説明できる。
この章で入れる nginx は、街の改札サービスの「入口」にあたります。街の全体像と、街の言葉・技術の言葉の対応は Part 0(この教材で作る街)にまとめています。
本章では、次の範囲は扱いません。
- TLS・HTTPS。入口の暗号化は Chapter 5 で扱います。
- 複数バックエンドへの振り分け。負荷分散は Chapter 4 で扱います。
- nginx の設定ディレクティブの網羅。本章は入口をひとつ建てるところまでです。
- 継続監視・アラート。運用監視は Part 7 で扱います。
- VM や snapshot の作り方。これらは Part 5 が提供し、本章では用意されたものを利用します。
この章を終えるとできること
ひとつの HTTP 応答を、取得経路から別ノードの接続まで層ごとに確認できるようになります。本章で身につく 4 動作 は次のとおりです。
apt update と apt install を実行し、取得元の証跡を残す。systemctl status と ss -lntp を使い、動作中のプロセスと socket を対応づける。curl と workstation01 からの curl を分けて実行し、access log と照合する。入口を「動いている」だけで判断しない
1. 外部通信が閉じている環境:先に取得経路を組み立てる
apt は、package の一覧と本体を取得してから導入します。外部への通信が遮断された環境では、公開インターネット上の配布元へ直接つながることを前提にできません。そこで署名済み内部 APT リポジトリから、演習に必要な package を環境内の許可された経路で取得します。
ここで重要なのは、apt install nginx が成功したという結果だけではありません。公開鍵の fingerprint を別経路で照合し、Signed-By で信頼範囲を限定してから、apt update の URI と apt-cache policy で「どこから取得したか」を記録します。取得経路も、構築したサービスを説明する証拠のひとつです。
本章では、演習環境の署名済み内部 APT リポジトリ http://172.16.10.22/ubuntu を使います。公開鍵 fingerprint の正本 089E 07BD FFA2 6DB7 C720 3016 297F AD13 17CA 1DC4 は 教材の HTTPS ページ に掲載しています。
2. プロセスと port:働いている人と受付窓口
systemctl status nginx が示すのは、systemd が nginx のプロセスをどの状態として管理しているかです。一方、ss -lntp が示すのは、どのプロセスがどの TCP port で接続を待っているかです。
プロセスが active でも、期待した port で待っているとは限りません。逆に、目的の port が別のプロセスに使われている場合もあります。次のように、管理状態と通信の入口を分けて読みます。
systemctl status nginx ── プロセスは管理上どう見えるか ss -lntp ── どの port で誰が待っているか curl ── HTTP の要求に応答できるか
3. ローカル応答と別ノード応答:確認している経路が違う
front01 上の curl http://localhost/ は、nginx が同じ OS の中からの要求へ応答できることを確認します。この確認は、nginx のプロセス、待受 port、HTTP 応答までを短い経路で調べるのに向いています。
しかし、ローカルで応答しただけでは、別の機械から届くことは証明できません。workstation01 から front01 へ接続すると、名前解決、送信元の経路、受信側 interface、途中の通信制御まで確認範囲が広がります。ローカル成功・別ノード失敗なら、nginx 本体だけでなく、待受アドレスやネットワーク経路を疑う必要があります。
4. ログ:同じ要求を異なる記録から読む
nginx の access log には、HTTP の要求を受けた記録が残ります。journalctl -u nginx には、nginx service の起動、停止、reload、失敗など、systemd が集めた記録が残ります。
同じ「nginx のログ」でも役割が違います。クライアントの要求が届いたかを調べるなら access log、service の起動や設定反映に失敗した理由を調べるなら journal、というように問いに合わせて選びます。
📔 コラム · 成功をひとつの表示だけで決めない
active、LISTEN、HTTP 応答は、どれも別の事実です。ひとつの表示を「全部正常」の意味に広げず、層ごとの観測を積み上げると、障害時にも同じ順序で切り分けられます。
演習:nginx の入口を導入して別ノードへ公開する
演習環境
| 項目 | 値 |
|---|---|
| 構築対象 | front01 |
| 別ノード確認 | workstation01 |
| 接続方法 | workstation01 から ssh deployer@front01 |
| 必要権限 | sudo。APT 設定、package 導入、nginx 設定の変更に使う |
| パッケージ取得 | 署名済み内部 APT リポジトリ(noble / part4) |
| 所要時間 | 約 45 分(STEP 1〜6 の実行) |
🛟 失敗しても安全: front01 には変更前 snapshot が用意されている前提です。本章では VM や snapshot 自体を作りません。壊す操作は nginx の設定 1 か所に限定し、観測後に元へ戻します。演習終了時は基盤の手順で変更前 snapshot へ rollback し、導入物を残しません。
コマンド中のアドレスと接続名は、演習環境で受講者が実際に使う値です。
ほかの操作を始める前に、front01 で構築前の manifest(ファイルの場所・権限・更新日時などを一覧にしたもの)を採ります。対象は path・type・mode・uid・gid・size・mtime です。順に、場所・種別・権限・所有ユーザの番号・所有グループの番号・大きさ・更新日時です。章末に同じ条件でもう一度採り、既存の 3 分類の列挙後に差分を確認します。
# front01 で、章のほかの操作より先に実行する sudo find /etc /var/lib /srv /opt /usr/local "$HOME" \ -not -path '/var/lib/apt/*' \ -not -path '/var/lib/dpkg/*' \ -not -path '/var/lib/systemd/*' \ -not -path '/var/log/*' \ -not -path '/var/cache/*' \ -not -name '.bash_history' \ -not -path '/etc/passwd-' -not -path '/etc/group-' \ -not -path '/etc/shadow-' -not -path '/etc/gshadow-' \ -not -path '/var/lib/fwupd' -not -path '/var/lib/fwupd/*' \ -not -path '/etc/fwupd' -not -path '/etc/fwupd/*' \ -not -path '/var/lib/ucf' -not -path '/var/lib/ucf/*' \ -not -path '/var/lib/landscape' -not -path '/var/lib/landscape/*' \ -not -path '/etc/ld.so.cache' \ -not -path '/var/lib/ubuntu-release-upgrader/release-upgrade-available' \ -not -path '/var/lib/snapd/state.json' \ -not -path '/var/lib/snapd/maintenance.json' \ -not -path '/var/lib/logrotate/status' \ -printf '%p\t%y\t%m\t%U\t%G\t%s\t%T@\n' 2>/dev/null \ | LC_ALL=C sort \ | sudo tee /tmp/part4-manifest-before.tsv >/dev/null
配布リポジトリを設定し、取得経路を確認する
Part 3 で学んだ署名付き内部 APT リポジトリの設定を、新しいサーバへ適用します。まず front01 へ接続し、配布サーバから公開鍵を取得します。鍵の取得元と信頼する根拠は分けます。
ssh deployer@front01 REPO_URI='http://172.16.10.22/ubuntu' curl --fail --show-error --head "$REPO_URI/dists/noble/InRelease" curl --fail --show-error "$REPO_URI/infra-study-archive-keyring.asc" \ -o /tmp/infra-study-archive-keyring.asc
HTTP/1.1 200 OK Server: nginx/1.24.0 (Ubuntu) Date: Sun, 16 Aug 2026 03:52:36 GMT Content-Type: application/octet-stream Content-Length: 2699 Last-Modified: Sun, 16 Aug 2026 01:35:52 GMT Connection: keep-alive ETag: "6a8113f8-a8b" Accept-Ranges: bytes
curl の成功は HTTP で到達できたことだけを示し、署名の正しさは示しません。取得した鍵の fingerprint を表示し、教材の HTTPS ページに掲載された値と空白を除いた全桁で照合します。
GPG_HOME="$(mktemp -d)"
chmod 700 "$GPG_HOME"
gpg --homedir "$GPG_HOME" --show-keys --with-fingerprint --keyid-format long \
/tmp/infra-study-archive-keyring.asc
EXPECTED_FINGERPRINT='089E07BDFFA26DB7C7203016297FAD1317CA1DC4'
ACTUAL_FINGERPRINT="$(gpg --homedir "$GPG_HOME" --show-keys --with-colons \
/tmp/infra-study-archive-keyring.asc | awk -F: '$1=="fpr" {print $10; exit}')"
test "$ACTUAL_FINGERPRINT" = "$EXPECTED_FINGERPRINT"
fingerprint_status=$?
rm -rf -- "$GPG_HOME"
echo "fingerprint match=$fingerprint_status"
test "$fingerprint_status" -eq 0
gpg: directory '/home/deployer/.gnupg' created
gpg: keybox '/home/deployer/.gnupg/pubring.kbx' created
pub ed25519/297FAD1317CA1DC4 2026-08-05 [SC] [expires: 2027-08-05]
Key fingerprint = 089E 07BD FFA2 6DB7 C720 3016 297F AD13 17CA 1DC4
uid infra-study lab archive <archive@lab.invalid>
fingerprint match=0
小 STEP · 既存の取得先を確認し、変更せずに作業範囲を分ける
① 確認する。 配布リポジトリを追加する前に、現在有効な取得先を確認します。旧形式の /etc/apt/sources.list と、deb822 形式などを置く /etc/apt/sources.list.d/ の両方を見てください。片方だけでは設定を見落とします。
sudo grep -HnE '^[[:space:]]*(deb |Types:|URIs:)' \ /etc/apt/sources.list \ /etc/apt/sources.list.d/*.list \ /etc/apt/sources.list.d/*.sources 2>/dev/null || true
② 作業範囲を分ける。 表示された source は OS や管理者が所有する既存設定です。本章では削除も改名もせず、章専用ディレクトリ /etc/apt/sources.list.d.part4/ に自分の source を作ります。後続の apt-get と apt-cache には、そのディレクトリだけを参照する指定を毎回渡します。
③ 変更していないことを確かめる。 既存 source はこの時点でも有効なままです。章末の manifest 差分で、既存の /etc/apt/sources.list と /etc/apt/sources.list.d/ が変更されていないことまで確認します。
/etc/apt/sources.list.d/ubuntu.sources:43:Types: deb /etc/apt/sources.list.d/ubuntu.sources:44:URIs: http://archive.ubuntu.com/ubuntu /etc/apt/sources.list.d/ubuntu.sources:51:Types: deb /etc/apt/sources.list.d/ubuntu.sources:52:URIs: http://security.ubuntu.com/ubuntu
なぜ既存 source を無効化しないのか
これらはサーバを作ったときに設定された、OS や管理者が所有する取得先です。演習環境から外部へ直接通信できなくても、他者管理の設定を一時的に改名すると、その設定に依存する索引や cache を壊す可能性があります。本章の取得処理だけを章専用 source に限定すれば、既存設定に触れずに内部リポジトリを使えます。
ただし source を限定するだけでは不十分です。
apt-get updateは参照対象から外れた索引を掃除し、それに連動する AppStream のswcatalogも消します。そこでAPT::Get::List-Cleanup=0を同時に指定し、既存の索引を壊さないようにします。現場では、作業対象を限定するだけでなく、限定による副作用まで止めることが非干渉の条件です。
一致した鍵だけを keyring へ置き、deb822 形式の source を作ります。Signed-By は、このリポジトリに使う鍵を指定して信頼範囲を限定します。
sudo install -d -m 0755 /etc/apt/keyrings
sudo install -o root -g root -m 0644 \
/tmp/infra-study-archive-keyring.asc \
/etc/apt/keyrings/infra-study-archive-keyring.asc
sudo install -d -m 0755 /etc/apt/sources.list.d.part4
sudo tee /etc/apt/sources.list.d.part4/infra-study.sources > /dev/null <<EOF
Types: deb
URIs: $REPO_URI
Suites: noble
Components: part4
Architectures: amd64
Signed-By: /etc/apt/keyrings/infra-study-archive-keyring.asc
EOF
sudo chmod 0644 /etc/apt/sources.list.d.part4/infra-study.sources
cat /etc/apt/sources.list.d.part4/infra-study.sources
# 章専用 source だけを見る。既存の取得先と索引には触れない
APT_ONLY_PART4=(
-o Dir::Etc::sourcelist=/dev/null
-o Dir::Etc::sourceparts=/etc/apt/sources.list.d.part4
-o APT::Get::List-Cleanup=0
)
sudo apt-get "${APT_ONLY_PART4[@]}" update
apt-cache "${APT_ONLY_PART4[@]}" policy nginx
-rw-r--r-- 1 root root 425 Aug 16 03:52 /etc/apt/keyrings/infra-study-archive-keyring.asc
Types: deb
URIs: http://172.16.10.22/ubuntu
Suites: noble
Components: part4
Architectures: amd64
Signed-By: /etc/apt/keyrings/infra-study-archive-keyring.asc
Get:1 http://172.16.10.22/ubuntu noble InRelease [2699 B]
Get:2 http://172.16.10.22/ubuntu noble/part4 amd64 Packages [14.5 kB]
Fetched 17.2 kB in 0s (117 kB/s)
Reading package lists...
Building dependency tree...
Reading state information...
All packages are up to date.
nginx:
Installed: (none)
Candidate: 1.24.0-2ubuntu7.15
Version table:
1.24.0-2ubuntu7.15 500
500 http://172.16.10.22/ubuntu noble/part4 amd64 Packages
curl は HTTP 到達性、fingerprint の照合は鍵の同一性、APT は InRelease の署名、apt-cache policy は package の選択元を確認します。ひとつの成功表示を別の検査の代わりにせず、apt update が内部リポジトリの InRelease と Packages を取得し、署名 warning が無いことまで記録します。
nginx を取得して導入する
STEP 01 で確認した候補版の nginx を導入します。表示を流さず、exact version と取得元の component を記録してください。
APT_ONLY_PART4=(
-o Dir::Etc::sourcelist=/dev/null
-o Dir::Etc::sourceparts=/etc/apt/sources.list.d.part4
-o APT::Get::List-Cleanup=0
)
sudo apt-get "${APT_ONLY_PART4[@]}" install nginx
Reading package lists... Building dependency tree... Reading state information... The following additional packages will be installed: nginx-common Suggested packages: fcgiwrap nginx-doc ssl-cert The following NEW packages will be installed: nginx nginx-common 0 upgraded, 2 newly installed, 0 to remove and 0 not upgraded. Need to get 568 kB of archives. After this operation, 1602 kB of additional disk space will be used. Do you want to continue? [Y/n] Get:1 http://172.16.10.22/ubuntu noble/part4 amd64 nginx-common all 1.24.0-2ubuntu7.15 [44.9 kB] Get:2 http://172.16.10.22/ubuntu noble/part4 amd64 nginx amd64 1.24.0-2ubuntu7.15 [523 kB] debconf: unable to initialize frontend: Dialog debconf: (Dialog frontend will not work on a dumb terminal, an emacs shell buffer, or without a controlling terminal.) debconf: falling back to frontend: Readline debconf: unable to initialize frontend: Readline debconf: (This frontend requires a controlling tty.) debconf: falling back to frontend: Teletype dpkg-preconfigure: unable to re-open stdin: Fetched 568 kB in 0s (53.1 MB/s) Selecting previously unselected package nginx-common. (Reading database ... (Reading database ... 106672 files and directories currently installed.) Preparing to unpack .../nginx-common_1.24.0-2ubuntu7.15_all.deb ... Unpacking nginx-common (1.24.0-2ubuntu7.15) ... Selecting previously unselected package nginx. Preparing to unpack .../nginx_1.24.0-2ubuntu7.15_amd64.deb ... Unpacking nginx (1.24.0-2ubuntu7.15) ... Setting up nginx-common (1.24.0-2ubuntu7.15) ... debconf: unable to initialize frontend: Dialog debconf: (Dialog frontend will not work on a dumb terminal, an emacs shell buffer, or without a controlling terminal.) debconf: falling back to frontend: Readline debconf: unable to initialize frontend: Readline debconf: (This frontend requires a controlling tty.) debconf: falling back to frontend: Teletype Created symlink /etc/systemd/system/multi-user.target.wants/nginx.service → /usr/lib/systemd/system/nginx.service. Setting up nginx (1.24.0-2ubuntu7.15) ... * Upgrading binary nginx ...done. Processing triggers for man-db (2.12.0-4build2) ... Processing triggers for ufw (0.36.2-6) ... debconf: unable to initialize frontend: Dialog debconf: (Dialog frontend will not work on a dumb terminal, an emacs shell buffer, or without a controlling terminal.) debconf: falling back to frontend: Readline debconf: unable to initialize frontend: Readline debconf: (This frontend requires a controlling tty.) debconf: falling back to frontend: Teletype Running kernel seems to be up-to-date. No services need to be restarted. No containers need to be restarted. No user sessions are running outdated binaries. No VM guests are running outdated hypervisor (qemu) binaries on this host.
導入された package と候補版の情報も確認します。
APT_ONLY_PART4=(
-o Dir::Etc::sourcelist=/dev/null
-o Dir::Etc::sourceparts=/etc/apt/sources.list.d.part4
-o APT::Get::List-Cleanup=0
)
dpkg-query -W -f='${Package}\t${Status}\t${Version}\n' nginx
apt-cache "${APT_ONLY_PART4[@]}" policy nginx
nginx install ok installed 1.24.0-2ubuntu7.15
nginx:
Installed: 1.24.0-2ubuntu7.15
Candidate: 1.24.0-2ubuntu7.15
Version table:
*** 1.24.0-2ubuntu7.15 500
500 http://172.16.10.22/ubuntu noble/part4 amd64 Packages
100 /var/lib/dpkg/status
ここでの証拠は「コマンドが終了した」だけではありません。nginx が導入済みであることと、どの取得経路が選ばれたかの両方です。
systemd からプロセスの状態を読む
nginx package の導入後、systemd が service をどの状態として認識しているかを確認します。
systemctl status nginx --no-pager systemctl is-active nginx systemctl is-enabled nginx
● nginx.service - A high performance web server and a reverse proxy server
Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; preset: enabled)
Active: active (running) since Sun 2026-08-16 03:52:39 UTC; 2s ago
Docs: man:nginx(8)
Process: 1636 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
Process: 1638 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
Main PID: 1664 (nginx)
Tasks: 2 (limit: 2315)
Memory: 1.7M (peak: 3.7M)
CPU: 12ms
CGroup: /system.slice/nginx.service
├─1664 "nginx: master process /usr/sbin/nginx -g daemon on; master_process on;"
└─1667 "nginx: worker process"
active
enabled
status は原因調査に使える詳細、is-active は現在動作中か、is-enabled は OS 起動時の自動起動設定を短く返します。同じ systemd の確認でも問いが違います。
service の起動記録も保存します。
sudo journalctl -u nginx --no-pager -n 30
Aug 16 03:52:39 front01 systemd[1]: Starting nginx.service - A high performance web server and a reverse proxy server... Aug 16 03:52:39 front01 systemd[1]: Started nginx.service - A high performance web server and a reverse proxy server.
ss -lntp で待受 port を確認する
次に、nginx のプロセスが TCP の待受 socket を持っているかを確認します。
sudo ss -lntp | grep nginx
LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=1667,fd=5),("nginx",pid=1664,fd=5))
LISTEN 0 511 [::]:80 [::]:* users:(("nginx",pid=1667,fd=6),("nginx",pid=1664,fd=6))
systemctl status が active でも、この確認は省けません。Local Address:Port と users: を対応づけ、期待する HTTP port を nginx が使っていることを読み取ります。
ローカルと別ノードから同じ入口へ接続する
最初に front01 自身から HTTP 応答を確認します。
curl --fail --show-error --include http://localhost/
HTTP/1.1 200 OK
Server: nginx/1.24.0 (Ubuntu)
Date: Sun, 16 Aug 2026 03:52:42 GMT
Content-Type: text/html
Content-Length: 615
Last-Modified: Sun, 16 Aug 2026 03:52:38 GMT
Connection: keep-alive
ETag: "6a813406-267"
Accept-Ranges: bytes
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
この成功が示すのは、同じ OS 内で nginx まで届き、HTTP 応答を返せたことです。別ノードの経路はまだ通っていません。
別のターミナルで workstation01 を操作し、front01 の接続名へ要求を送ります。
curl --fail --show-error --include "http://front01/"
Server: nginx/1.24.0 (Ubuntu)
Date: Sun, 16 Aug 2026 03:53:03 GMT
Content-Type: text/html
Content-Length: 615
Last-Modified: Sun, 16 Aug 2026 03:52:38 GMT
Connection: keep-alive
ETag: "6a813406-267"
Accept-Ranges: bytes
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
この要求は workstation01 からネットワークを通って front01 へ届きます。ローカルでは成功し、別ノードでは失敗する場合、nginx のプロセスだけでなく、名前解決、経路、待受アドレス、通信制御を調べる必要があります。
front01 に戻り、直前の要求がどこへ記録されたかを確認します。
sudo tail -n 20 /var/log/nginx/access.log sudo journalctl -u nginx --no-pager -n 30
::1 - - [16/Aug/2026:03:52:42 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/8.5.0" 172.16.10.22 - - [16/Aug/2026:03:53:03 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/8.5.0" Aug 16 03:52:39 front01 systemd[1]: Starting nginx.service - A high performance web server and a reverse proxy server... Aug 16 03:52:39 front01 systemd[1]: Started nginx.service - A high performance web server and a reverse proxy server.
HTTP 要求の記録は access log、service の起動や reload の記録は journal で確認します。両方を並べると、「要求が届かなかった」のか「service 自体が起動できなかった」のかを分けられます。
待受 port を誤らせて切り分ける + GOAL 🎯
待受 port を一時的に変更し、構文検査・service 状態・HTTP 接続がそれぞれ何を教えるかを比べます。 この STEP では、狙ったとおりに壊れないところから始めます。 そこが本題です。
まず、変更の対象になる行を探します。「listen 80 を含む行」を全部探してください。
grep -nE 'listen .*80 default_server;' /etc/nginx/sites-available/default
22: listen 80 default_server; 23: listen [::]:80 default_server;
見つかった行数を数えてください。 1 行だと思っていたなら、その思い込みがこの STEP の主題です。
変更前の構文検査を行い、ファイルを退避してから、まず 1 行だけを変えます。
sudo nginx -t sudo cp /etc/nginx/sites-available/default /tmp/ch1-nginx-default.before sudo sed -i 's/listen 80 default_server;/listen 8080 default_server;/' /etc/nginx/sites-available/default sudo nginx -t sudo systemctl reload nginx
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful
入口を 8080 へ移したのだから、80 番への接続は失敗するはずです。確かめます。
curl --fail --show-error --include http://127.0.0.1/
HTTP/1.1 200 OK
Server: nginx/1.24.0 (Ubuntu)
Date: Sun, 16 Aug 2026 03:53:06 GMT
Content-Type: text/html
Content-Length: 615
Last-Modified: Sun, 16 Aug 2026 03:52:38 GMT
Connection: keep-alive
ETag: "6a813406-267"
Accept-Ranges: bytes
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
🔴 接続できてしまったら、そこから追う
失敗を期待した操作が成功したときは、期待のほうが間違っています。 設定を疑う前に、 いま実際に何番で待ち受けているかを見ます。
sudo ss -lntp | grep nginx
LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=1667,fd=5))
LISTEN 0 511 0.0.0.0:8080 0.0.0.0:* users:(("nginx",pid=1973,fd=11),("nginx",pid=1664,fd=11))
LISTEN 0 511 [::]:80 [::]:* users:(("nginx",pid=1973,fd=6),("nginx",pid=1667,fd=6),("nginx",pid=1664,fd=6))
待受の一覧には、まだ 80 番を持っている行が残っているはずです。理由は 2 つあります。
| 残っている理由 | 内容 | 性質 |
|---|---|---|
| IPv6 の行を変えていない | 既定の設定には listen 80 の行が IPv4 用と IPv6 用の 2 つある。変えたのは片方だけ | 🔴 設定を直さない限り消えない |
| 古いワーカーがまだ生きている | 設定の再読み込みは、処理中の接続を抱えたワーカーをその場で終わらせない | 一時的(やがて消える) |
「1 か所直したから塞がった」と考えるのは危険です。 同じ役割の設定が複数の場所に書かれていれば、 片方だけ直しても効きません。そして片方を直したことで直した気持ちになるぶん、 最初から何もしていないより見つけにくくなります。
もう 1 行も変えて、今度こそ 80 番が閉じることを確かめます。
sudo sed -i 's/listen \[::\]:80 default_server;/listen [::]:8080 default_server;/' /etc/nginx/sites-available/default sudo nginx -t sudo systemctl reload nginx sudo ss -lntp | grep nginx curl --fail --show-error http://127.0.0.1/
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
LISTEN 0 511 0.0.0.0:8080 0.0.0.0:* users:(("nginx",pid=1988,fd=11),("nginx",pid=1664,fd=11))
LISTEN 0 511 [::]:8080 [::]:* users:(("nginx",pid=1988,fd=13),("nginx",pid=1664,fd=13))
curl: (7) Failed to connect to 127.0.0.1 port 80 after 0 ms: Couldn't connect to server
ここで 3 つの検査が何を示すかを比べます。
| 検査 | 分かること | 分からないこと |
|---|---|---|
nginx -t | 設定の文法が読めること | その設定が意図どおりかどうか |
systemctl status | プロセスが動いていること | 期待する port で応答するかどうか |
curl | 実際に応答が返るかどうか | なぜ返らないのか |
ss -lntp | 実際に開いている port | 設定ファイルのどの行が原因か |
どれか 1 つでは足りません。 構文が正しく、service が active でも、接続できないことがあります。
退避したファイルを戻し、修正後にも同じ確認をします。
sudo cp /tmp/ch1-nginx-default.before /etc/nginx/sites-available/default sudo nginx -t sudo systemctl reload nginx systemctl status nginx --no-pager curl --fail --show-error http://127.0.0.1/ sudo ss -lntp | grep nginx sudo rm /tmp/ch1-nginx-default.before
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
● nginx.service - A high performance web server and a reverse proxy server
Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; preset: enabled)
Active: active (running) since Sun 2026-08-16 03:52:39 UTC; 30s ago
Docs: man:nginx(8)
Process: 1636 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
Process: 1638 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
Process: 1999 ExecReload=/usr/sbin/nginx -g daemon on; master_process on; -s reload (code=exited, status=0/SUCCESS)
Main PID: 1664 (nginx)
Tasks: 3 (limit: 2315)
Memory: 2.7M (peak: 3.9M)
CPU: 29ms
CGroup: /system.slice/nginx.service
├─1664 "nginx: master process /usr/sbin/nginx -g daemon on; master_process on;"
├─1988 "nginx: worker process"
└─2000 "nginx: worker process"
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=2000,fd=7),("nginx",pid=1664,fd=7))
LISTEN 0 511 0.0.0.0:8080 0.0.0.0:* users:(("nginx",pid=1988,fd=11))
LISTEN 0 511 [::]:80 [::]:* users:(("nginx",pid=2000,fd=8),("nginx",pid=1664,fd=8))
LISTEN 0 511 [::]:8080 [::]:* users:(("nginx",pid=1988,fd=13))
ゴール到達 ✓ ① LAN 内の取得経路を確認した / ② nginx を導入してプロセスと待受 port を対応づけた / ③ ローカルと別ノードの応答を分けて確認した / ④ 構文・service 状態・接続の 3 点で誤設定を切り分けて元へ戻した。
演習の証跡を保存したら、自分で追加した構築物を削除します。本章で導入した nginx と、依存で導入された nginx-common を名指しで purge します。apt autoremove は、章の外で auto 扱いになった package まで対象になり、clean snapshot の状態によって削除範囲が変わるため使いません。章専用 source ディレクトリ、keyring、一時取得ファイルも消します。既存の source は最初から変更していないため、復元処理や終了時の apt update は不要です。
purge の確認待ちに注意
apt purgeは-yを付けないと[Y/n]の確認で止まり、その間は package 管理の lock を保持します。ここでは削除対象をnginxとnginx-commonに名指ししたうえで-yを付け、確認待ちを発生させません。
sudo DEBIAN_FRONTEND=noninteractive apt purge -y nginx nginx-common sudo rm -rf /etc/apt/sources.list.d.part4 sudo rm -f /etc/apt/keyrings/infra-study-archive-keyring.asc rm -f /tmp/infra-study-archive-keyring.asc
| 分類 | 検査すること | 合格条件 |
|---|---|---|
| package | 本章で導入した nginx と nginx-common | install ok installed の package が残っていない |
| service | nginx service の状態 | 演習用 service が稼働していない |
| socket | 本章で開いた待受 port | 演習用 port の待受がない |
| 設定ファイル | /etc/nginx など nginx が配置した設定 | 本章で追加された path が残っていない |
| 取得設定 | 章専用 source と keyring | 本章で追加した取得設定が残っていない |
3 分類の外を manifest 差分で確認する
package・service・socket の 3 分類を自分で列挙して検査したあと、その外側に残る変更を、構築前後の sorted manifest で確認します。差分は 3 分類を置き換えるものではなく、列挙から漏れた残留候補を見つける安全網です。全差分は /var/tmp/part4-chapter-manifest.diff に一時保存し、「残留候補」と「更新日時だけ変わった通常ファイル」に分けて全件を画面に表示してから削除します。途中で切りません。表示に失敗したときは、構築前・章末の manifest と差分ファイルを消さずに残し、理由を表示します。表示された理由を確かめるまで、後片付けへ進まないでください。
次の除外には、それぞれ理由があります。
/var/lib/apt/は apt が索引を更新する場所で、章の作業とは無関係に必ず変わります。/var/lib/dpkg/は package 操作で更新される管理情報で、package 差分として別に判定します。/var/lib/systemd/は service の状態管理で更新される場所で、service・unit の分類として別に判定します。/var/log/は操作の証跡そのもので、コマンドを実行するだけでも変わります。/var/cache/は再生成できる一時的な cache で、章の構築物とは分けて扱います。.bash_historyは操作履歴として更新されるため、章が配置したファイルとは分けて扱います。/var/lib/snapd/state.json・/var/lib/snapd/maintenance.jsonと/var/lib/logrotate/statusは、snapd と logrotate が章と無関係に定期的に書き換える状態ファイルです。親ディレクトリは除外しません。
/var/lib/swcatalog/ は除外しません。限定した apt-get update に APT::Get::List-Cleanup=0 を付け、既存カタログを壊していないことを manifest 差分で確認します。
sudo find /etc /var/lib /srv /opt /usr/local "$HOME" \
-not -path '/var/lib/apt/*' \
-not -path '/var/lib/dpkg/*' \
-not -path '/var/lib/systemd/*' \
-not -path '/var/log/*' \
-not -path '/var/cache/*' \
-not -name '.bash_history' \
-not -path '/etc/passwd-' -not -path '/etc/group-' \
-not -path '/etc/shadow-' -not -path '/etc/gshadow-' \
-not -path '/var/lib/fwupd' -not -path '/var/lib/fwupd/*' \
-not -path '/etc/fwupd' -not -path '/etc/fwupd/*' \
-not -path '/var/lib/ucf' -not -path '/var/lib/ucf/*' \
-not -path '/var/lib/landscape' -not -path '/var/lib/landscape/*' \
-not -path '/etc/ld.so.cache' \
-not -path '/var/lib/ubuntu-release-upgrader/release-upgrade-available' \
-not -path '/var/lib/snapd/state.json' \
-not -path '/var/lib/snapd/maintenance.json' \
-not -path '/var/lib/logrotate/status' \
-printf '%p\t%y\t%m\t%U\t%G\t%s\t%T@\n' 2>/dev/null \
| LC_ALL=C sort \
| sudo tee /tmp/part4-manifest-after.tsv >/dev/null
sudo diff -u /tmp/part4-manifest-before.tsv /tmp/part4-manifest-after.tsv > /var/tmp/part4-chapter-manifest.diff
diff_rc=$?
if [ "$diff_rc" -le 1 ]; then
if awk 'BEGIN { FS = "\t"; c = "LC_ALL=C sort" }
/^\+\+\+|^---|^@@/ { next }
/^[+-]/ {
if (NF != 7) { printf "manifest の列数が 7 ではありません: %s\n", $0 > "/dev/stderr"; bad = 1; exit 2 }
t++; s = substr($1, 1, 1); p = substr($1, 2)
k = $2 " " $3 " " $4 " " $5 " " ($2 == "d" ? "-" : $6)
if (s == "-") { if (p in b) { dup = p; exit 2 } b[p] = k; bm[p] = $7; bt[p] = $2 }
else { if (p in a) { dup = p; exit 2 } a[p] = k; am[p] = $7 }
}
END {
if (bad) exit 2
if (dup != "") { printf "同じパスが重複しています: %s\n", dup > "/dev/stderr"; exit 2 }
print "== 残留候補(0 件で合格)=="
n = 0
for (p in b) {
if (!(p in a)) { print "消えた " p | c; n++ }
else if (a[p] != b[p]) { print "変わった " p " (" b[p] " -> " a[p] ")" | c; n++ }
}
for (p in a) if (!(p in b)) { print "増えた " p | c; n++ }
fflush(); close(c)
print "残留候補: " n " 件"
print "== 更新日時だけ変わった通常ファイル(1 件ずつ理由を説明できること)=="
m = 0; d = 0
for (p in b) if ((p in a) && a[p] == b[p] && am[p] != bm[p]) { if (bt[p] == "d") d++; else { print p | c; m++ } }
fflush(); close(c)
print "更新日時だけ変わった通常ファイル: " m " 件 / ディレクトリ: " d " 件"
print "差分行総数: " (t + 0)
}' /var/tmp/part4-chapter-manifest.diff; then
sudo rm -f /tmp/part4-manifest-before.tsv /tmp/part4-manifest-after.tsv
else
echo "差分の表示に失敗しました。構築前・章末の manifest と /var/tmp/part4-chapter-manifest.diff は残してあります。表示された理由を確認してください" >&2
fi
else
echo "差分を採れませんでした(diff の終了コード $diff_rc)。構築前 manifest /tmp/part4-manifest-before.tsv があるか確認してください" >&2
fi
sudo rm -f /var/tmp/part4-chapter-manifest.diff test ! -e /var/tmp/part4-chapter-manifest.diff
表示は 2 つの節に分かれます。合格の条件は、1 つ目の「残留候補」が 0 件であることです。残留候補は、構築前になかったパスが増えた、あったパスが消えた、種別・権限・所有者・サイズが変わった、のいずれかです(ディレクトリのサイズは比べません)。
2 つ目の「更新日時だけ変わった通常ファイル」は、中身の属性は同じで更新日時だけが違うファイルです。ここに出る行は 1 件ずつ、次のどちらかで説明できなければ完了にしません。
- OS の自動処理(package 索引の更新、通知用の記録など)が書いたもの。
- 章の操作の跡で、中身は戻っているもの。
どちらでも説明できない行が 1 件でもあれば、それは自分が挙げた 3 分類の外側にある残留です。何の分類にあたるかを考えて「4 番目の分類」を自分の後片付け手順へ足し、解消後に再確認してください。
(形の例です。M・K・T には実行したときの件数が入ります。更新日時だけ変わった通常ファイルの行と件数は、台・章によって変わります。合格の条件は「残留候補: 0 件」です) == 残留候補(0 件で合格)== 残留候補: 0 件 == 更新日時だけ変わった通常ファイル(1 件ずつ理由を説明できること)== (該当するパスが 1 行ずつ並びます) 更新日時だけ変わった通常ファイル: M 件 / ディレクトリ: K 件 差分行総数: T
if dpkg-query -W -f='${db:Status-Abbrev}\t${binary:Package}\n' nginx nginx-common 2>/dev/null | grep -q '^ii'; then
echo "対象 package が残っています"
else
echo "対象 package なし"
fi
if systemctl is-active --quiet nginx; then
echo "nginx service 稼働中"
else
echo "nginx service は非稼働"
fi
if sudo ss -lntp | grep -q ':80 '; then
echo "80 番の待受あり"
else
echo "80 番の待受なし"
fi
nginx_paths=(/etc/nginx /etc/default/nginx /etc/init.d/nginx /etc/logrotate.d/nginx)
nginx_path_left=0
for path in "${nginx_paths[@]}"; do
if sudo test -e "$path"; then
echo "残留 path: $path"
nginx_path_left=1
fi
done
[ "$nginx_path_left" -eq 0 ] && echo "nginx 設定 path なし"
test ! -e /etc/apt/sources.list.d.part4
test ! -e /etc/apt/keyrings/infra-study-archive-keyring.asc
test ! -e /tmp/infra-study-archive-keyring.asc
対象 package なし nginx service は非稼働 80 番の待受なし nginx 設定 path なし
この演習環境は、セッションが終わると基盤側の手順で初期状態へ戻ります。ただし、戻ることを当てにして消さずに終えるのではなく、自分が作ったものを自分で消し、消えたことを確認するところまでが演習です。
つまずきポイント
原因を探すときは、最初に nginx -t、次に systemctl status nginx、最後に curl と ss -lntp で接続経路を確認します。この順序なら、設定の文法、service の状態、通信の入口を混ぜずに調べられます。
ハマり #1 · nginx -t が構文エラーになる
設定変更後に構文検査が失敗した状態です。この段階では reload を続けません。
sudo nginx -t
2026/08/16 03:53:09 [emerg] 2014#2014: invalid parameter "listen" in /etc/nginx/sites-enabled/default:23 nginx: configuration file /etc/nginx/nginx.conf test failed
- 原因: ディレクティブ名、区切り、波括弧など、nginx が設定を解釈できない箇所があります。
- 対処: エラーに表示されたファイルと行を確認し、直前に変更した 1 か所を元へ戻します。再度
sudo nginx -tが成功するまで reload しません。
ハマり #2 · service は active なのに接続できない
systemctl status nginx はプロセスの管理状態を示しますが、期待する port で待っていることや、別ノードから到達できることまでは示しません。
sudo nginx -t systemctl status nginx --no-pager sudo ss -lntp | grep nginx curl --fail --show-error http://127.0.0.1/
curl: (7) Failed to connect to 127.0.0.1 port 80 after 0 ms: Couldn't connect to server
LISTEN 0 511 0.0.0.0:8080 0.0.0.0:* users:(("nginx",pid=2028,fd=11),("nginx",pid=1664,fd=11))
LISTEN 0 511 [::]:8080 [::]:* users:(("nginx",pid=2028,fd=12),("nginx",pid=1664,fd=12))
- 原因:
listenの port またはアドレスが、クライアントが接続する入口と一致していない可能性があります。 - 対処:
ss -lntpで nginx の待受を確認し、クライアントが指定した接続先と照合します。設定を修正したらnginx -t、reload、curlの順で再確認します。
ハマり #3 · ローカルでは成功するが workstation01 から失敗する
nginx 自体は HTTP 応答を返せています。失敗しているのは、別ノードから front01 までの追加経路です。
- 原因: 名前解決、経路、受信側の待受アドレス、途中の通信制御のどこかがローカル確認と異なります。
- 対処:
front01のss -lntp、workstation01側の名前解決と接続先、access log に要求が残ったかを順に確認します。access log に記録がなければ、HTTP 要求は nginx まで届いていません。
AI に相談
修了確認
問 1 · なぜ取得経路を先に確認するのか
外部へ直接通信できない演習環境で、apt install nginx より前に HTTP 到達性、fingerprint、署名、選択元を分けて確認する理由を説明してください。
問 2 · systemctl status と ss -lntp は何が違うか
systemctl status nginx が active なら、なぜ ss -lntp も確認する必要があるのでしょうか。
問 3 · ローカル成功だけでは足りない理由
front01 上の curl http://localhost/ が成功しました。それでも workstation01 からの curl を行う理由を、確認する経路の違いから説明してください。
問 4 · 3 点で原因を切り分ける
nginx -t は成功し、systemctl status nginx は active ですが、curl http://127.0.0.1/ は失敗しました。最初に何を確認し、どのような原因を疑いますか。
段階ヒント(問 1〜問 4)
問 1 のヒントと答え
ヒント 1 段 · 軽く方向だけ
package を「入れられるか」だけでなく、「どこから取るか」に注目してください。
ヒント 2 段 · 確認する表示
curl --head は配布リポジトリまでの HTTP 到達性、fingerprint は鍵の同一性、APT は InRelease の署名、apt-cache policy は package の選択元を確認します。
答えを見る
外部の配布元へ直接通信できないため、署名済み内部リポジトリへ到達でき、別経路で照合した鍵による署名検証が成功し、APT がそのリポジトリの part4 component を選んだことを先に確認します。導入が成功したかだけでなく、信頼した取得元から選んだことを証跡に残すためです。
問 2 のヒントと答え
ヒント 1 段 · 軽く方向だけ
「プロセスの管理状態」と「通信を待つ窓口」は同じ情報ではありません。
ヒント 2 段 · 具体的なコマンド
systemctl status nginx の Active: と、sudo ss -lntp | grep nginx の待受 port・プロセス名を対応づけます。
答えを見る
systemctl status は systemd が nginx のプロセスをどう管理しているかを示します。ss -lntp は nginx がどのアドレスと TCP port で接続を待っているかを示します。active でも期待する port にいない可能性があるため、両方が必要です。
問 3 のヒントと答え
ヒント 1 段 · 軽く方向だけ
localhost の要求は front01 の外へ出ません。
ヒント 2 段 · 比べる観測点
別ノード確認では、名前解決、送信経路、受信 interface、通信制御、access log まで確認範囲に入ります。
答えを見る
ローカルの成功は、同じ OS 内で nginx が応答できることだけを示します。workstation01 からの要求はネットワークを通るため、別ノードから入口までの経路も成立していることを確認できます。ローカル成功だけでは外部のクライアントから届くことを証明できません。
問 4 のヒントと答え
ヒント 1 段 · 軽く方向だけ
構文とプロセスが正常なら、次は接続先と実際の待受を比べます。
ヒント 2 段 · 具体的なコマンド
sudo ss -lntp | grep nginx を実行し、curl が接続した IPv4 の HTTP port と一致するか確認します。
答えを見る
最初に ss -lntp で nginx の待受アドレスと port を確認します。nginx -t の成功は構文が正しいこと、active はプロセスが動いていることしか示しません。期待する IPv4 の HTTP port と listen 設定がずれている、または別のアドレスだけで待っている可能性を疑います。
データを持つサービスへ
本章で HTTP の入口はできましたが、まだ要求に応じて読み書きするデータを持っていません。次章では PostgreSQL(データベース管理システム)と共有領域を用意し、入口の先に置くサービスが残高を安全に更新するためのデータ層を組み立てます。
Chapter 2: PostgreSQL と共有領域を用意し、残高を安全に更新する
- transaction(複数の更新を一体として確定する仕組み)で残高更新をまとめる
- 同じ処理の再送と競合を安全に扱う
- DB と共有ファイルの役割を分ける
参考資料
man systemctlman ssman curlman nginx