追記(2024/5/23): 記事の内容だとAPIコンテナのホスト側のポート番号を指定しなくてもいいようです。つまり開けるポートがフロントエンドのものだけで良くなります。逆に下の追記のやり方だと、APIサーバのポートも開けないと動きません。また時間があるときに記事の内容を変更します。
追記(2024/5/22): こんな難しいことをせずにbaseUrl = http://localhost:8000
でいけました。htmlファイルから見たAPIサーバの場所なのでホスト側のポート番号を打たないといけないっぽいです。
背景
フロントエンドはReactで開発し、本番環境はNginxで配信、バックエンドはFastAPIでWebアプリケーションを開発していました。
これを全てDocker上で動かしています。
コンテナは以下の4つです。
コンテナ名 | ホスト側ポート番号 | コンテナ側ポート番号 | 処理 |
---|---|---|---|
db | - | 5432 | PostgreSQLを動かしている |
backend | - | - | バックグラウンドで非同期で処理を走らせている |
api | 8000 | 8000 | FastAPIを動かしている |
frontend | 3000 | 3000 | Reactのビルドをした後にNginxでそのビルドファイルを配信している |
実現したかったこと
jsファイルの中で、APIコンテナに対してaxiosを使ってデータを受け取りたかった。
具体的には、jsファイルの中で以下のような処理をしたかった。
const baseUrl = '<APIのURL>';
const response = await axios.get(`${baseUrl}`)
問題解決までにしたこと
Docker側で名前解決してくれる(参考↓)ことを知っていたので、まずは
baseUrl = `http://api:8000`
としてみたがFastAPI側にデータが届いていない。
urlを間違えたのではないかと考え、フロントエンドコンテナからping api
と打つとちゃんと通信ができていた。
※pingがないと言われたときは以下の記事が参考になりました。
また、フロントエンドコンテナからcurl http://api:8000
と打ってもうまく通信できていた。
そもそもデータが届いていないので違うとは思うが、CORS関係のエラーかもしれないと思い、FastAPI側で以下のように設定したがもちろんうまくいかず。
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
# CORSの設定
app.add_middleware(
CORSMiddleware,
allow_origins=[`*`], # リクエストを許可するオリジンのリスト
allow_credentials=True, # 認証情報の共有を許可
allow_methods=["*"], # すべてのHTTPメソッドを許可
allow_headers=["*"], # すべてのHTTPヘッダーを許可
)
ここで、ChatGPTに聞いて、Nginxのリバースプロキシというものがあり、それを使ってみることにした。
以下にNginxの設定ファイルを記載する。
server {
listen 3000;
server_name localhost;
location / {
root /var/www/;
index index.html;
}
location /api/ {
proxy_pass http://api:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
リバースプロキシについては以下が参考になる。
これにより、例えばhttp://localhost:3000/api
に届いた通信をhttp://api:8000
に渡してあげることができる。
また、jsファイルの方も
baseUrl = `http://localhost:3000/api`
と変更した。
これでもう一度コンテナを再起動すると、無事にAPI通信ができるようになった。
まとめ
フロントエンドコンテナとAPIコンテナでやり取りができないときにしたことをまとめた。
ReactでビルドしたファイルをNginxで配信しており、htmlファイルから見たときにhttp://api:8000
がどこにあるかが分からないことが原因だった。
解決策として、Nginxのリバースプロキシの機能を使ってhttp://localhost:3000/api
-> http://api:8000
にプロキシし、htmlファイルからAPIコンテナの場所を参照できるようにした。