Docker環境でのFastAPIとRemix間の通信エラーの解決
エラーの概要
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環境で簡単に導入できるので個人的に便利だと感じた。
Docker
の環境は次のように構築した。
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:
今回の設定におけるそれぞれのサービスの場所
-
FastAPI
- Docker仮想ネットワーク内:http://fastapi:8000
- ローカルネットワーク内:http://localhost:8000
-
Remix
- Docker仮想ネットワーク内:http://remix:5173
- ローカルネットワーク内:http://localhost:5173
このように、Dockerの仮想ネットワーク内からアクセスする場合と実際のホストマシンのブラウザからアクセスする場合では指定するべきURLは異なる。これが今回のエラーの原因となっていた。
エラーの解決法
Remixはサーバーサイトレンダリング(SSR)とクライアントサイドレンダリング(CSR)の両方をサポートしており、今回のようにloader
関数内でAPIエンドポイントを叩く場合、それはサーバーサイドで実行されることになる。
つまり、今回のAPIを叩く処理はDockerの仮想ネットワーク内で実行されるため、エンドポイントの指定の際にローカルネットワークのURLであるhttp://localhost:8000
ではなく、http://fastapi:8000
にリクエストを送る必要があるということである。
今回はこの部分を修正することでエラーを解決することができた。
まとめ
今回の開発で初めてDocker
を使ったため、なかなか使い方がわかっておらず、苦労する点が多いですが、今回のエラーを通して重要だと感じたのは以下の点です。
- Dockerコンテナ間の通信はDockerの仮想ネットワークで行われる
- Dockerの仮想ネットワーク内とローカルネットワークは指定するべきURLが異なる
Dockerはさまざまな環境をダウンロードすることなく簡単に仮想環境に実現することのできる非常に便利なサービスであると感じますが、同時に細かい設定などに気を配る必要性が出ててきてより開発難易度が上がるようにも感じます。今後もDocker
についての理解を深めて、開発をしていきたいです。
Discussion