ポート公開でアクセスする
コンテナ内で動くWebサーバなどに外からアクセスするには、ポートを公開する必要があります。docker run の -p オプションで「ホスト側ポート:コンテナ側ポート」を指定し、両者を結びます。たとえば -p 8080:80 とすれば、ホストの8080番への通信がコンテナの80番へ転送されます。これをポートマッピングと呼び、ブラウザから http://localhost:8080 でコンテナ内のサービスへ届きます。
コンテナの中でWebサーバやAPIを動かした。さあブラウザで見てみよう。ところが、つながらない。
理由はこうだ。コンテナは隔離された環境なので、中で待ち受けているサービスは、外との通り道を明示的に開けてやらないと、ホストや他のPCのブラウザからは見えない。
この通り道を開ける仕組みがポート公開で、docker run の -p オプションで設定する。ここを理解すると、コンテナで動かしたサービスにブラウザからつないで動作を確認できるようになり、コンテナがぐっと実用的に感じられる。まずは「ポートとは何の番号か」から押さえよう。
📮 外の通信は、どうやって中まで届くのか
ネットワーク上のサービスは、それぞれ「ポート番号」という窓口を持って通信を待ち受けている。たとえばWebサーバの nginx は、コンテナの中で通常80番ポートで待っている。
しかしこの80番はコンテナの内側の番号であって、外からはそのままでは届かない。
そこで、ホスト側のあるポートに来た通信を、コンテナ側のポートへ転送する対応づけを設定する。これをポートマッピングと呼ぶ。これにより、外部からはホストのポートに向けてアクセスするだけで、その通信がコンテナ内のサービスまで届くようになる。では、その対応づけをどう書くのか。
↔ -p は、どちらが外でどちらが中か
ポートマッピングは docker run の -p オプションで指定し、書式は -p ホスト側ポート:コンテナ側ポート だ。区切りはコロンで、ボリュームの -v と同じく「左がホスト、右がコンテナ」の順である。
たとえば docker run -d --name web -p 8080:80 nginx とすると、「ホストの8080番に来た通信を、コンテナの80番(nginx が待っているポート)へ転送する」という意味になる。
起動したら、ホスト上で curl http://localhost:8080 を実行して nginx の歓迎ページ(Welcome to nginx!)が返ってくれば成功だ。同じことはブラウザで http://localhost:8080 を開いても確認でき、コンテナ内の nginx の画面が表示される。
ここで素朴な疑問が湧く。左右の番号は、同じにしなくていいのか。答えは、必ずしも同じである必要はない、だ。
-p 8080:80 のように違えてもよい。これを使うと、同じ80番で待ち受けるコンテナを複数、ホスト側だけ別の番号にして同時に公開できる。たとえば1つ目を -p 8080:80、2つ目を -p 8081:80 で起動すれば、http://localhost:8080 と http://localhost:8081 で別々のコンテナにアクセスできる。
逆に、ホスト側で同じ番号を2つのコンテナに割り当てようとすると「そのポートは既に使われている(address already in use)」とエラーになる。これは1つのホストポートは同時に1つの宛先にしか結べないためで、よくある失敗の1つだ。
🔌 「-p を付けたのに、つながらない」
「-p を付けたのにブラウザからつながらない」ときに疑うべき点がいくつかある。
1つは、そもそも -p を付け忘れているケースだ。-d だけで起動するとバックグラウンドでは動くが、ポートは開いていないので外からは見えない。
2つ目は、左右の番号の取り違えだ。アクセスするのはホスト側の番号(上の例なら8080)であって、コンテナ側の80番に直接つなぐわけではない点を取り違えると混乱する。
3つ目は、コンテナ内のサービスがそもそも起動していない、あるいは別のポートで待っているケースで、このときは docker logs でコンテナの様子を確認する。
RHEL系には、これらとは別筋のつまずきがある。ファイアウォール(firewalld)だ。localhost からは curl でつながるのに、別のPCのブラウザからは届かない、というときは、ホストのファイアウォールが8080番を塞いでいる可能性が高い。
この場合は、firewall-cmd でそのポートを許可する設定を入れる必要がある。「自分のマシンからは見えるのに外からは見えない」ときは、コンテナ側ではなくホストのファイアウォールを疑うのが定石だ。
🧾 宣言するだけの EXPOSE と、配管する -p
ここで混同しやすいのが、Dockerfile の EXPOSE 命令との違いだ。EXPOSE 8080 は「このコンテナは8080番ポートを使いますよ」という宣言(ドキュメント)にすぎず、それだけでは外部からアクセスできるようにはならない。
実際に通り道を開けるのは、あくまで起動時の -p オプションのほうだ。EXPOSE はいわばメモ書き、-p が実際の配管工事、と役割を分けて理解しておこう。この2つの混同は初学者が必ず一度ははまるポイントだ。
なお、ホスト側のポート番号を省いて -p 80 のように書くと、ホスト側には空いている番号が自動で割り当てられる。どの番号に割り当てられたか、また現在どんなマッピングになっているかは、docker port web のように docker port コマンドで確認できる。
公開範囲を絞りたいときは、ホスト側にアドレスを添えて -p 127.0.0.1:8080:80 のように書く方法も覚えておくと役立つ。こうするとホスト自身(localhost)からのアクセスだけを受け付け、外部のPCからは届かなくなる。
データベースのように外に出したくないサービスを、同じホスト上の別コンテナからだけ使いたい、といった場面で有効だ。反対に、アドレスを付けない -p 8080:80 は、原則としてホストのすべてのネットワーク経由でそのポートを受け付ける設定になる。開発中の確認用なら前者で十分なことも多く、必要以上に広く公開しないという発想は、ここでも安全運用の基本になる。
🏗 この通り道は、現場でどう設計されるか
実務では、開発したWebアプリやAPIをコンテナで起動し、-p でポートを公開してブラウザや curl から動作を確認する、という流れが日常的だ。
複数のサービスを1台のホストで動かすときは、コンテナ側のポートは標準のまま(80や443など)にしておき、ホスト側のポートだけを 8080、8081、とずらして共存させる、という設計がよく使われる。
本番環境では、コンテナを直接外に公開するのではなく、前段にリバースプロキシ(nginx など)を1つ置き、そこで受けた通信を各コンテナのポートへ振り分ける構成が一般的だ。つまずいたら、まずこれ。『-p ホスト:コンテナ で通り道を開ける、アクセス先はホスト側の番号、つながらなければ -p の付け忘れ・番号違い・ファイアウォールを順に疑う』という基本を押さえておこう。