【Docker】docker-composeのホスト側ポートを同じポート番号で使い回す方法
先日、ローカルのDocker環境にてDjangoとDjango REST frameworkを使ってAPIとそのAPIからデータを取得してDBに保存するという簡単なアプリを作成していました。
しかし、なぜかアプリからAPIに接続することができない….
その時に陥った問題と原因、解決方法について解説します!
- ローカルのDocker環境でlocalhostに通信できず困っている人
localhostでAPIに接続できない
作成したアプリとAPIで想定していたURLはそれぞれ以下の通りでした
- アプリ
- http://localhost/
- API(保存)
- http://localhost/api/v1/save/
まずはAPIから作成し、ブラウザからアクセス。
コンソールが表示され、GETでDBからデータの一覧が取得されている。
フォームからPOSTするとDBに正常に保存されることを確認まで完了できました。
続いてアプリを作成。
APIへのPOST処理を作って、フォームから送信できるようにしました。
ともに完成し、いざアプリからAPIへのデータ送信を実行するとなぜか接続できない…
原因は『localhost』
想定していたアプリやAPIのURLをlocalhostにしており、ブラウザでも問題なく表示されていたため、アプリのPOST先も同様に http://localhost/api/v1/save/ と指定していましたが、これが問題でした。
アプリ自体はDockerコンテナ上で動作しているため、アプリでlocalhostの指定は、コンテナ内を指しているということがわかりました。
ブラウザからのlocalhostはホストマシンを指しているため、接続先がないとなったわけですね。
Dockerコンテナからホストマシンのlocalhostに接続する
Dockerコンテナからホストマシンのlocalhostに接続するには、接続先をホストマシンのIPアドレスを指定することで可能になります。
Dockerコンテナ内から見たホストマシンのIPアドレスを確認
ここでは docker-compose を使用していますので、使用されていない方は適宜読み替えてください
# Dockerコンテナの中に入る
# "web"は、docker-compose.ymlで指定しているアプリやAPIを使用しているコンテナのサービス名
$ docker-compose exec web bash
# 入れたら以下のコマンドを実行
$ cat /etc/hosts | awk 'END{print $1}' | sed -e 's/[0-9]+$/1/g'
192.168.96.1
cat /etc/hosts
– /etc/hosts
ファイルの内容を確認
awk 'END{print $1}'
– 最後の行を取り出す
sed -e 's/[0-9]+$/1/g'
– 取り出したIPアドレスと末尾を1に変える
IPアドレスの設定をdocker-compose.ymlに追加
Dockerコンテナは起動ごとに動的にIPアドレスが割り当てられるため、毎回 /etc/hosts
ファイルの内容が書き換わってしまいます。
なので、docker-compose.ymlの extra_hosts
オプションを使って、先程確認したIPアドレスをDockerコンテナないの /etc/hosts
に追加するようにします。
docker-compose.yml
version: '3'
services:
web:
container_name: django-docker
{ ... }
depends_on:
- db
extra_hosts: # 追加
- localhost:192.168.96.1 # 追加
Dockerコンテナを再ビルドして再起動
# Dockerコンテナを起動している場合
$ docker-compose down
# 再ビルド&再起動
$ docker-compose build; docker-compose up -d
# Dockerコンテナの中に入る
$ docker-compose exec web bash
# /etc/hostsを確認
$ 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
192.168.96.1 localhost # ←これが追加されていたらOK
192.168.96.3 96062a57a93d
これで無事APIへ接続と処理が成功しました。
以上になります。
初めてDjangoを使用していたときに起こったことだったため、てっきりDjangoやPythonの使い方が間違っているのか?と見当違いなことを調査してしまっていて、原因がDockerに関する事だったのは盲点でした。
自分が実際に困ったことだったので記事にしました。
ご参考になればと思います☀️