Fragments of verbose memory

冗長な記憶の断片 - Web技術のメモをほぼ毎日更新

Jan 25, 2021 (更新: Dec 21, 2025) - 日記

Docker: ポート公開・開放でよくあるトラブルと解決法

初めてDockerを扱うエンジニアを見てるとDockerポート周りで悩んでいる人を見かけます。別に難しい事はないのですが確かに紛らわしい事もありますのでまとめました。

結局ポートの問題とは、公開されないと意図せず公開されちゃうに帰着されます。つまりこんな感じ・・・

  • Dockerfile内でEXPOSEしたのに公開されない
  • ufwで閉じたはずなんだけどアクセスできちゃう
  • docker-compose でlinkしてないのに…
  • 起動時にデータベースにアクセスできない

思いついた順に簡単にまとめました。

Dockerfile内でEXPOSEしたのに公開されない

DockerfileでEXPOSE宣言しただけだとポートの解放は行われません。ついでにいうと、EXPOSEはイメージに書かれる単なるコメントなので、本当に何もしません。

EXPOSEを書くと、以下のようにinspectすることでportを得られます。

$ docker inspect <image> -f "{{ .Config.ExposedPorts }}"
map[4567/tcp:{}]

ただこれだけです。もうEXPOSEのことは忘れていいと思います…EXPOSEを書いても書かなくても次の-pオプションをつければ公開されるし、つけなければ公開されません。

ポート全公開オプション-P(大文字)の挙動には作用するようだけどどっちみち使わないよね?!

-pオプション

ポートを公開する必要のあるコンテナの起動には必ず-pオプションが必要です。

docker run nginx

ではポートは公開されないので以下のようにします。

docker run -p 127.0.0.1:80:80 nginx

省略時公開範囲に注意

ついでに書いておくと、ポートオプション127.0.0.1:80:80の部分、コロン(:)が3つつきますが公開するIPアドレス:公開するポート:コンテナのポートの順番です。最初の公開するIPアドレス:の部分は省略することができ、そのときは0.0.0.0:つまりホストのすべてのインターフェイスに対して公開となります。

ここで注意してほしいのは「公開するIPアドレス:の部分を省略する」ということはホスト外からのアクセスが来る可能性があるということです。「省略→公開」の設定になりますのでご注意して下さい。ローカルで試すなら、127.0.0.1:80:80のように使うべきですし、公開サービスならtraefikなどのルータを介してアクセスするべきだと思います。ここら辺の具体的な方法は以前の記事1つのサーバにたくさんのWebサービスを詰め込む方法 を参考にしてください。

ufwで閉じたはずなんだけどアクセスできちゃう

ufwはUbuntuで利用される、iptablesのフロントエンドです。簡単な操作でパケットフィルタリングができるため愛用者は多いのではないでしょうか?ただし、ufwとDocker併用するのは注意が必要です。Dockerはufwよりも優先度の高い方法(Chain)でポートの開放を行います。なので、ufwでDockerコンテナへのパケットフィルタリングができるとは思わない方がいいでしょう。これを回避する方法 もありますが、副作用があるのでおすすめされていません。

前項と合わせると、ufwで閉じていると思って-pオプションで公開するIPアドレス:を省略すると実は公開されていたなんてことがあり得ますので注意です。

docker compose でlinkしてないコンテナにつながる

例えば以下のようなdocker composeのサービスで

compose.yaml

services:
  app:
    image: ruby:3.2
    depends_on:
      - redis
    command: ping redis
  redis:
    image: redis:7

補足(2025年12月更新): Docker Compose V2ではdocker-composeコマンドがdocker composeに変更されました。また、設定ファイル名もdocker-compose.ymlからcompose.yamlが推奨されています。versionフィールドは不要になりました。

同じComposeプロジェクト内のサービスは、デフォルトで同じネットワークに属するため、サービス名で相互に接続できます。linksは非推奨となり、depends_onで起動順序を制御します。

$ docker compose exec app ping redis
PING redis (172.18.0.3) 56(84) bytes of data.
64 bytes from project-redis-1.project_default (172.18.0.3): icmp_seq=1 ttl=64 time=0.143 ms
64 bytes from project-redis-1.project_default (172.18.0.3): icmp_seq=2 ttl=64 time=0.189 ms
^C

docker compose でサービス起動時にデータベースにアクセスできない

depends_onは起動順序を制御しますが、依存サービスが「準備完了」するまで待つわけではありません。例えばappが起動した時点ではredisはまだ初期化中かもしれません。

解決策1: depends_on の condition を使う(推奨)

Docker Compose V2ではdepends_onにヘルスチェック条件を指定できます:

services:
  app:
    image: ruby:3.2
    depends_on:
      redis:
        condition: service_healthy
  redis:
    image: redis:7
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 5

解決策2: wait-for-it / dockerize を使う

ヘルスチェックが使えない場合は、wait-for-itdockerize でポート待機を行います。

services:
  app:
    image: ruby:3.2
    command: ["./wait-for-it.sh", "redis:6379", "--", "ruby", "app.rb"]
    depends_on:
      - redis

以上、Dockerのポート周りでよくあるトラブルと解決法をまとめました。2025年12月時点でDocker Compose V2が標準となっているため、新規プロジェクトではdocker composeコマンドと新しい記法の使用をおすすめします。

関連記事

Dockerの実践的な活用方法については、以下の記事も参考になります:

他にもはまりどころなどありましたらコメント欄でご指摘いただければと思います。