Fragments of verbose memory

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

Jan 25, 2021 - コメント - 日記

DockerのPort(ポート)周りのトラブルあるある

初めて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のサービスで

docker-compose.yml

version: '3'
services:
  app:
    image: ruby:3.0
    links:
      - redis
    command: ping -i
  redis:
    image: redis:3.2

links は以前のDockerではサービス内のコンテナ間接続制御に使われていましたが、今は起動順にしか関係しません。なので、linksの部分を削除してもネットワーク的につながります。

$ docker-compose exec app ping redis                                                                                   [17:26:38]
PING redis (172.18.0.3) 56(84) bytes of data.
64 bytes from a_redis_1.a_default (172.18.0.3): icmp_seq=1 ttl=64 time=0.143 ms
64 bytes from a_redis_1.a_default (172.18.0.3): icmp_seq=2 ttl=64 time=0.189 ms
64 bytes from a_redis_1.a_default (172.18.0.3): icmp_seq=3 ttl=64 time=0.113 ms
^C
--- redis ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 14ms
rtt min/avg/max/mdev = 0.113/0.148/0.189/0.032 ms

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

docker-composeはlinksオプションで起動順しか管理しませんので、前項の例にあるようなappが起動したときには依存するredisはまだ起動中かもしれません。このようなときにはdockerlize のような仕組みを使って依存サービスの起動を待つ必要があります。詳しい解説がありましたので、そちらに譲ります。


以上駆け足で4点説明してきました。他にもはまりどころなどありましたらコメント欄でご指摘いただければと思います。