🐳

Docker環境でのFastAPIとRemix間の通信エラーの解決

2024/10/15に公開

エラーの概要

Remixコンテナのloader関数内から、FastAPIのエンドポイントを叩くと次のようなエラーが発生する。

TypeError: fetch failed
    at node:internal/deps/undici/undici:13185:13
    at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
    at async Module.downloadFile (/app/app/fastapi/download.file.ts:5:26)
    at async loader (/app/app/routes/document.$name.tsx:22:24)
    at async Object.callRouteLoader (/app/node_modules/@remix-run/server-runtime/dist/data.js:59:16)
    at async /app/node_modules/@remix-run/router/dist/router.cjs.js:4724:19
    at async callLoaderOrAction (/app/node_modules/@remix-run/router/dist/router.cjs.js:4790:16)
    at async Promise.all (index 0)
    at async defaultDataStrategy (/app/node_modules/@remix-run/router/dist/router.cjs.js:4649:17)
    at async callDataStrategyImpl (/app/node_modules/@remix-run/router/dist/router.cjs.js:4681:17)

今回はこのエラーについて何が原因でどうすることで解決できたのかを紹介する。

環境構築について

今回の使用技術は以下の通りである。

  • FastAPI: Python製の高性能なWebフレームワーク
  • Remix: ReactをベースにしたフルスタックWebフレームワーク
  • Docker: コンテナ化プラットフォーム
  • Docker Compose: 複数のDockerコンテナを一括管理するツール
  • PostgreSQL: データベース
  • Adminer: データベース管理システム

Adminerはデータベースを管理するためのブラウザで利用可能なGUIであり、Docker環境で簡単に導入できるので個人的に便利だと感じた。

https://www.adminer.org/

Dockerの環境は次のように構築した。

docker-compose.yml
services:
  fastapi:
    build:
      context: ./translate-api
      dockerfile: Dockerfile
    restart: always
    ports:
      - "8000:8000"  # FastAPIのポート
    volumes:
      - ./translate-api:/app
    environment:
      - PDF_SERVICES_CLIENT_ID=${PDF_SERVICES_CLIENT_ID}
      - PDF_SERVICES_CLIENT_SECRET=${PDF_SERVICES_CLIENT_SECRET}
      - OPENAI_API_KEY=${OPENAI_API_KEY}
      - POSTGRES_URL=${POSTGRES_URL}
      - POSTGRES_USER=${POSTGRES_USER}
      - POSTGRES_PORT=5432
      - POSTGRES_SERVER=${POSTGRES_SERVER}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
      - POSTGRES_DB=${POSTGRES_DB}
      - SUPABASE_URL=${SUPABASE_URL}
      - SUPABASE_DATABASE_PASSWORD=${SUPABASE_DATABASE_PASSWORD}
      - SUPABASE_ANON_KEY=${SUPABASE_ANON_KEY}
    env_file: 
      - .env
    depends_on:
      - postgres

  remix:
    build:
      context: ./translation-app
      dockerfile: Dockerfile
    restart: always
    ports:
      - "5173:5173"  # Remixのポート
    volumes:
      - ./translation-app:/usr/src/app
      - node_modules:/usr/src/app/node_modules  # named volume
    command: npm run dev
    environment:
      - VITE_FAST_API_URL=${VITE_FAST_API_URL}
      - VITE_FASTAPI_URL=${VITE_FASTAPI_URL}
    env_file: 
      - .env
  postgres:
    image: postgres:13
    restart: always
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
      interval: 10s
      timeout: 5s
      retries: 5
    environment:
      PGDATA: /var/lib/postgresql/data/pgdata
      POSTGRES_USER: ${POSTGRES_USER?Variable not set}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD?Variable not set}
      POSTGRES_DB: ${POSTGRES_DB?Variable not set}
    env_file: 
      - .env
    ports:
      - "5433:5432"  # Postgresのポート
    volumes:
      - postgres-data:/var/lib/postgresql/data/pgdata

  adminer:
    image: adminer
    restart: always
    ports:
      - "8080:8080"  # Adminerのポート
    environment:
      ADMINER_DEFAULT_SERVER: postgres
      ADMINER_DESIGN: pappu687
      ADMINER_DEFAULT_PASSWORD: ${POSTGRES_PASSWORD}
      ADMINER_DEFAULT_DATABASE: ${POSTGRES_DB}
    depends_on:
      - postgres

volumes:
  node_modules:
  postgres-data:

今回の設定におけるそれぞれのサービスの場所

このように、Dockerの仮想ネットワーク内からアクセスする場合と実際のホストマシンのブラウザからアクセスする場合では指定するべきURLは異なる。これが今回のエラーの原因となっていた。

エラーの解決法

Remixはサーバーサイトレンダリング(SSR)とクライアントサイドレンダリング(CSR)の両方をサポートしており、今回のようにloader関数内でAPIエンドポイントを叩く場合、それはサーバーサイドで実行されることになる。

つまり、今回のAPIを叩く処理はDockerの仮想ネットワーク内で実行されるため、エンドポイントの指定の際にローカルネットワークのURLであるhttp://localhost:8000ではなく、http://fastapi:8000にリクエストを送る必要があるということである。

今回はこの部分を修正することでエラーを解決することができた。

まとめ

今回の開発で初めてDockerを使ったため、なかなか使い方がわかっておらず、苦労する点が多いですが、今回のエラーを通して重要だと感じたのは以下の点です。

  • Dockerコンテナ間の通信はDockerの仮想ネットワークで行われる
  • Dockerの仮想ネットワーク内とローカルネットワークは指定するべきURLが異なる

Dockerはさまざまな環境をダウンロードすることなく簡単に仮想環境に実現することのできる非常に便利なサービスであると感じますが、同時に細かい設定などに気を配る必要性が出ててきてより開発難易度が上がるようにも感じます。今後もDockerについての理解を深めて、開発をしていきたいです。

Discussion