🐧 Linux 総合学習プラットフォーム
コンテナ Docker/Podman ・ 中級

ポート公開でアクセスする

コンテナ内で動く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 を付けたのにブラウザからつながらない」ときに疑うべき点がいくつかあります。1つは、そもそも -p を付け忘れているケースです。-d だけで起動するとバックグラウンドでは動きますが、ポートは開いていないので外からは見えません。2つ目は、左右の番号の取り違えです。アクセスするのはホスト側の番号(上の例なら8080)であって、コンテナ側の80番に直接つなぐわけではない点を取り違えると混乱します。3つ目は、コンテナ内のサービスがそもそも起動していない、あるいは別のポートで待っているケースで、このときは docker logs でコンテナの様子を確認します。

RHEL系で特有のつまずきが、ファイアウォール(firewalld)です。localhost からは curl でつながるのに、別のPCのブラウザからは届かない、というときは、ホストのファイアウォールが8080番を塞いでいる可能性が高いです。この場合は、firewall-cmd でそのポートを許可する設定を入れる必要があります。「自分のマシンからは見えるのに外からは見えない」ときは、コンテナ側ではなくホストのファイアウォールを疑うのが定石です。

EXPOSE との違い

ここで混同しやすいのが、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 の付け忘れ・番号違い・ファイアウォールを順に疑う』という基本を押さえておきましょう。

この項目に出てくる用語

ポートマッピングぽーとまっぴんぐ
ホストのポートとコンテナのポートを結ぶ設定。
コンテナこんてな
イメージから起動した、実行中のアプリの実体。

関連コマンド

docker rundocker ps

▶ 学習アプリでこの続きを学ぶ・演習する