yn2011's blog

技術メモ

Docker Compose上で起動したwebpack-dev-serverに設定されたプロキシが動作しなくてハマった話

環境

  • macOS 10.14.4
  • docker-compose version 1.23.2
  • webpack-dev-server 3.7.2
  • webpack 4.37.0

フロントエンド(webpack-dev-server)、バックエンドをそれぞれDocker Compose上にコンテナとして起動して開発していた際にハマったお話。

事象

  • webpack-dev-serverはホストするjs内で発行されるHTTPリクエストをプロキシすることができる

    • 今回なぜプロキシが必要だったかというと、Chromelocalhost上ではfetch('http://localhost')...のようにlocalhostに対してリクエストを行うとCORSエラーが発生するため
    • jsのソースコード上ではfetch('/api/hoge')..のようにしておいて、webpack-dev-serverにlocalhostへプロキシさせることによって回避できる。(他にもブラウザの設定を変更する等の回避策があるが、webpack-dev-serverに設定されている方が他の人に親切)
  • webpack.config.jsを以下のように設定(抜粋)

    • APIサーバーは5000番ポートを開いているとする
  devServer: {
    contentBase: path.join(__dirname, 'dist'),
    compress: true,
    port: 8080,
    historyApiFallback: true,
    proxy: {
      '/api': 'http://localhost:5000',
    }
  • 上記設定を反映したwebpack-dev-serverを起動し(docker-compose up)、実際にlocalhost:8080にアクセスしてfetch('/api')...をブラウザから実行すると、webpack-dev-serverが以下のエラーを出力した
[HPM] Error occurred while trying to proxy request /api from localhost:8080 to http://localhost:5000 (ECONNREFUSED) (https://nodejs.org/api/errors.html#errors_common_system_errors)
  • httpレスポンスは以下(抜粋)
504 Gateway Timeout

Error occured while trying to proxy to: localhost:8080/api

原因

  • Docker Compose上のコンテナが参加するネットワークでは、docker-compose.yml上のサービス名で名前解決が行われるため。

  • 従って、webpack.config.jsを下記のように書き換えると上手くいく

...
 proxy: {
      '/api': 'http://api:5000',
    }
  • なお、docker-compose.ymlでは以下のようにサービス名をapiとしている
version: "3.4"
services:

  api:
    build:
      context: ./api

...

敗因

  • 書いてみるとこれだけのことだが、多分2,3時間ぐらいハマっていた
  • サービス名で名前解決ができること自体はDocker Composeのドキュメントに書いてあることだし、以前から知っていた。なぜ気づけなかったのか...
    • 多分だが、ブラウザからDocker Compose上のwebpack-dev-serverにアクセスする際は、
      • localhost:8080をDocker Composeのポート8080と接続していて
      • かつ実際にクライアントサイドのjsが動作しているのがローカルだし
      • バックエンドのapiサーバーもlocalhost:5000とDocker Compose上の5000を接続していた
    • のでwebpack-dev-serverが動作しているのがDocker Compose上であることを忘れていたというか、しっかり認識できていなかったような気がする。
    • また、Docker Compose上のネットワーク内のコンテナ間もlocalhostで通信できるようなイメージだったのも原因かもしれない。
  • あと、ステータスコード504 Gateway Timeoutの理解が不足している感もある。webpack-dev-serverがlocalhost:5000が存在しないためにレスポンスを得られない→ポート5000で動作しているはずのHTTPサーバーのログを確認する→ログがない→接続先がおかしい?というような手順をスムーズに踏めたら良かった。
    • Goで書いているHTTPサーバは外部ファイルへのログ出力を未実装で、結局調査のために書いたんだけど、ここを面倒くさがらずに早く実行するべきだったかな...

github.com