logo

Docker環境のServerComponentからlocalhostにアクセスできない

2023-08-09
a year ago

開発環境

  • node 18.16.1
  • NextJS 13.4.10
  • NestJS 10.0.0

前提

NextJS x NestJS を Dockerを利用してモノレポで環境構築しています。

NextJS はAppRouterを利用しています。

参考までに、今回の記事で取り上げる環境のdocker-compose.ymlを下記します。

version: '3.8'

x-common: &common
  tty: true
  environment:
    NODE_ENV: ${NODE_ENV}
    DATABASE_URL: ${DATABASE_URL}
    TZ: ${TZ}
  volumes:
    - .:/app

services:
  db:
    container_name: db
    image: postgres:14.2-alpine
    restart: always
    environment:
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: ${POSTGRES_DB}
      TZ: ${TZ}
    volumes:
      - ./db/postgres/init.d:/docker-entrypoint-initdb.d
      - ./db/postgres/pgdata:/var/lib/postgresql/data
    ports:
      - 5432:5432

  server:
    << : *common
    build:
      context: .
      dockerfile: ./dockerfiles/server/Dockerfile
    container_name: server
    command: yarn start:dev
    depends_on:
      - db
    ports:
      - 3300:3300
      - 5555:5555

  client:
    << : *common
    build:
      context: .
      dockerfile: ./dockerfiles/client/Dockerfile
    container_name: client
    command: yarn dev
    depends_on:
      - server
    ports:
      - 3000:3000
      - 6006:6006
      - 8080:8080

本題

AppRouterが stable になったことで、実務では利用できていませんが個人的に色々試してる段階です。

現状は、テストやモック関連のライブラリがうまく動かなかったりと、仕事で利用するのはまだ先になりそうだなと感じています。

そんな中、個人開発で出会したエラーを1つ紹介します。

結論

APIのURLはhttp://localhost:3300ではなくhttp://host.docker.internal:3300を利用する

説明

ポート番号(3300)は試した環境で設定したものなので重要ではありません。

ホスト名のところが重要です。

同一のネットワーク内でコンテナを実装していますが、ServerComponentではホスト名の解決ができていない?ようです。(←ここの理解が浅いので分かる方に教えてほしいです・・)

例えば、以下のようなServerComponentを実装するとエラーが発生します。

import axios from 'axios';

export default async function TestFetch() {
  const result = await axios.get('http://localhost:3300');
  console.log({ result });

  return <></>;
}
client  | - error Error: connect ECONNREFUSED 127.0.0.1:3300
client  |     at RedirectableRequest.emit (node:events:513:28)
client  |     at ClientRequest.emit (node:events:513:28)
client  |     at Socket.socketErrorListener (node:_http_client:502:9)
client  |     at Socket.emit (node:events:513:28)
client  | digest: "4000622448"

host.docker.internal は Docker for Mac および Docker for Windows で提供される特別なホスト名であり、ホストマシン自体を指す IP アドレスに解決されます。この方法を使用することで、コンテナ内部からホストマシン上のサービスにアクセスできるようになります。

さいごに

今回のケースは公式ドキュメントやissueなどに情報がなかったので一旦動作することを目的にしました。

一つの解決策として参考にしてもらえると嬉しいです。

参照