のっぴきならぬ事情でDockerコンテナ内からホストへlocalhostでアクセスする必要なときに役立ちそうなのでメモ。

Dockerコンテナ内からホストへアクセスするには

こちらの記事が参考になりました。
--add-hostオプションを利用すればなんとかなりそうです。

Dockerのコンテナの中からホストOS上のプロセスと通信する方法 – Qiita
https://qiita.com/Iju/items/badde64d530e6bade382

localhost じゃなくて良いのなら

host.docker.internalというDNS名が用意されているので、それを利用すればよさそうです。
ドキュメントによるとMac/Windowsで利用できそうです。

Networking features in Docker Desktop for Windows | Docker Documentation
https://docs.docker.com/docker-for-windows/networking/

The host has a changing IP address (or none if you have no network access). From 18.03 onwards our recommendation is to connect to the special DNS name host.docker.internal, which resolves to the internal IP address used by the host. This is for development purpose and will not work in a production environment outside of Docker Desktop for Windows.

(Google翻訳)ホストのIPアドレスは変更されています(ネットワークアクセスがない場合はなし)。18.03以降では、ホストが使用する内部IPアドレスに解決される特別なDNS名host.docker.internalに接続することをお勧めします。 開発目的のためであり、Docker Desktop for Windows以外の実稼働環境では機能しません。

Networking features in Docker Desktop for Mac | Docker Documentation
https://docs.docker.com/docker-for-mac/networking/

The host has a changing IP address (or none if you have no network access). From 18.03 onwards our recommendation is to connect to the special DNS name host.docker.internal, which resolves to the internal IP address used by the host. This is for development purpose and will not work in a production environment outside of Docker Desktop for Mac.

(Google翻訳)ホストのIPアドレスは変更されています(ネットワークアクセスがない場合はなし)。 18.03以降では、ホストが使用する内部IPアドレスに解決される特別なDNS名host.docker.internalに接続することをお勧めします。 これは開発用であり、Docker Desktop for Mac以外の運用環境では動作しません。

試してみる

準備

まずホストでHTTPサービスを80ポートで立ち上げます。

> python -m http.server 80

Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...

# Dockerコンテナを起動するのでもOK
> docker run -it --rm -p 80:80 python \
  python -m http.server 80

確認

立ち上げたサービスに対してDockerコンテナ内からcurlでアクセスしてみます。
利用するDockerイメージはなんでもよいです。curlがインストールされていなかったら先にインストールします。

> docker run -it --rm alpine \
  sh -c 'apk add curl;curl http://localhost:80'

(略)
OK: 7 MiB in 18 packages
curl: (7) Failed to connect to localhost port 80: Connection refused

はい。
コンテナ内でlocalhostに対してアクセスすると当然ながらコンテナ内へのアクセスとなり、エラーとなります。

--add-hostを足してみる

--add-hostを利用するとコンテナ内の/etc/hostsファイルに設定を追加できるそうです。

Dockerで/etc/hostsファイルが操作出来ない対策 – Qiita
https://qiita.com/jagaximo/items/6b71a03518bbd53d4de6

> docker run -it --rm alpine \
  cat /etc/hosts

127.0.0.1       localhost
::1     localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.3      9f215da7195e

なので、localhostに対してホストのIPアドレスをみにいくように設定を追加してみます。

# ホストのプライベートIPアドレスを確認
> ipconfig getifaddr en0


> docker run -it --rm \
  --add-host=localhost:<ホストのプライベートIPアドレス> \
  alpine \
  cat /etc/hosts

127.0.0.1       localhost
::1     localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
<ホストのプライベートIPアドレス>  localhost
172.17.0.3      6aff7879fc0a

追加できることが確認できたらcurlでアクセスしてみます。

> docker run -it --rm \
  --add-host=localhost:<ホストのプライベートIPアドレス> \
  alpine \
  sh -c 'apk add curl;curl -D - -s -o /dev/null http://localhost:80'

(略)
OK: 7 MiB in 18 packages
HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/3.8.1
Date: Wed, 12 Feb 2020 02:43:26 GMT
Content-type: text/html; charset=utf-8
Content-Length: 987

やったぜ。
使う機会があるかはわかりませんが、できることは確認できました。

参考

Dockerのコンテナの中からホストOS上のプロセスと通信する方法 – Qiita
https://qiita.com/Iju/items/badde64d530e6bade382

Networking features in Docker Desktop for Windows | Docker Documentation
https://docs.docker.com/docker-for-windows/networking/

Networking features in Docker Desktop for Mac | Docker Documentation
https://docs.docker.com/docker-for-mac/networking/

Dockerで/etc/hostsファイルが操作出来ない対策 – Qiita
https://qiita.com/jagaximo/items/6b71a03518bbd53d4de6

Pythonの標準ライブラリでさくっとAPIサーバとWebサーバを立ち上げる – Qiita
https://cloudpack.media/44251

cURLコマンドでレスポンスヘッダのみを取得する – Qiita
https://qiita.com/yousan/items/fcc15e1046939c465ab7

macOS で自分のプライベート IP アドレスを見つける方法 – yu8mada
https://yu8mada.com/2018/07/14/how-to-find-my-private-ip-address-in-macos/

元記事はこちら

Dockerコンテナ内からホストへ'localhost'でアクセスしてみる