けこのアプリ開発奮闘記

アプリ開発で得た知見を共有していきます。なるべく文章が堅くならないように意識しています。

【Docker】Dockerネットワークの落とし穴に(勝手に)嵌まった話

最近の活動

スタートアップのプロジェクトのミーティングやら資料やらの準備をしていたらブログを書く時間が...ない!

このブログは更新頻度はぼちぼちでいいから1年間は続けようと考えているので張り切って書いていきますよー!

axiosを使ってAPI通信できない問題

以前の記事で作った環境にて、NginxからLaravelのコンテナにcurlを飛ばすことができました。その際のコマンドは以下のような形、

$ curl http://laravel.test

じゃあaxiosを使ってVueからAPI通信できるな!ヨシッ(某猫並感)

そんなうまい話はありませんでした。

f:id:keko5342:20211210000324p:plain
エラーーーー!

ポート番号を変えてみてもダメ...一体どうして...?となったので調べてみました!

コンテナ同士の見え方

どうやらコンテナ同士の見え方の差が問題だとわかりました!

この場合の見え方の差というのは、Dockerから見たコンテナブラウザから見たコンテナの見え方の差です。

現在のDockerの構成を図解するとこんな感じ、緑の枠内全体が同じネットワークに設定されています。laravel.testというコンテナ名でNginxからLaravelにアクセスできたことからも同一ネットワークに配置できていることがわかります。

f:id:keko5342:20211210001210p:plain
現在のDocker構成

一方で、ブラウザからDockerのコンテナ名であるlaravel.testというアドレスはどう解釈されるのでしょうか?

f:id:keko5342:20211210001716p:plain
もしかして:名前解決できない

繋がらないんですね。要するにDockerからコンテナ名でアクセスできても、それはDocker内だけの話であってホストのブラウザからは何も見えないわけです。

ここまで来たらaxiosで通信できなかった理由がわかるのではないでしょうか。そう、JavaScriptはブラウザに読み込まれてから実行されます。ブラウザから見れないものがJavaScriptからはアクセスできるはずがないというわけです。

解決法

簡単です。ブラウザから見てアクセスできるアドレスでリクエストを飛ばしてやればいいわけです。今回のLaravelサーバはDockerのlocalhost:80でリッスンしているポートを3000ポートに転送しています。

つまり、localhost:3000にリクエストを飛ばせばいいわけですね!とは言ってもポートが違うのでクロスオリジンアクセスを許可しないといけません。まずはその設定から。

プロジェクト名/config/cors.php

    'paths' => ['api/*', 'sanctum/csrf-cookie'],

    'allowed_methods' => ['*'],

    'allowed_origins' => ['*'],

    'allowed_origins_patterns' => [],

    'allowed_headers' => ['*'],

    'exposed_headers' => [],

    'max_age' => 0,

    'supports_credentials' => true,   # 初期false -> trueに

ルートとコントローラの作成

HelloController

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

/**
 * API用サンプルコントローラ
 */
class HelloController extends Controller
{
    public function index(){
        return response()->json(['message' => 'Hello API!']);
    }
}

api.php

/**
 * APIのサンプルプログラム
 */
Route::get('/HelloAPI', [HelloController::class, 'index']);

VueのAboutページを編集、Vueのサーバを立ち上げて/aboutにアクセス(※Aboutがない方は vue add router を実行すると生成されます。)

<template>
  <div class="about">
    <h1 id="message">{{ message }}</h1>
  </div>
</template>

<script>
import axios from 'axios';
export default {
  data() {
    return {
      message: null
    }
  },
  // ちゃんとlocalhost:3000にアクセス
  mounted () {
    axios
      .get('http://localhost:3000/api/HelloAPI')
      .then(response => (this.message = response.data.message))
  }
}
</script>

f:id:keko5342:20211210031335p:plain
Hello!

ふー、これでAPIにアクセスできますね。次はログイン、行ってみますか!