けこのアプリ開発奮闘記

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

あけましておめでとうございます

(´・ω・`)ハッ 年末年始多忙過ぎてブログのこと忘れてた…!

というのは冗談ですが忙しかったのは本当です。ところでブログというものは見られてなんぼが半分、知識の整理が半分だと思っているのですが、

Qiitaで記事を書くとLGTMがつかない記事でも500くらいの閲覧数がつくんですよね。どちらも無名なのですが、こちらは1記事10閲覧くらいです。

導線がTwitterくらいしかないのである程度は仕方ないと思いますが、やっぱりQiitaはすごいですね!

本題

就活のためアウトプットとインプットの再整理のためにこのブログを書いていましたが、 - Qiitaで記事を書きたくなったこと - 知識の再整理だけならQiitaの下書き機能で十分

ということから、暫くQiitaで記事を書いてみようと思います。

それでは!みなさん、また会う日まで!

【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にアクセスできますね。次はログイン、行ってみますか!

【PHP】LaravelでささっとAPIを実装してみる

こんばんは、けこです。
今日はタイトル通り、LaravelでAPIを作ってみまーす。

特定のアドレスにリクエスト -> jsonが帰ってくるっていう流れですね。今日はとりあえずGETだけ実装して後々PUTやUPDATEも作っていきます。

環境は以前の記事の環境になります。

keko5342.hatenadiary.jp

ルーティング

LaravelでAPIを作るには api.php にルーティング情報を書く必要があります。

use App\Http\Controllers\HelloController;

# ~~~略~~~

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

併せて HelloController を作成.

php artisan make:controller HelloController

中身を修正.

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

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

いざ実行!

f:id:keko5342:20211203021142p:plain
できたー!

結構簡単にできました!Laravelはプログラムをシンプルに記述できるのが嬉しいところです。
WebだけでなくAPIもシンプルに記述することができました。

次はAPIを使った認証にチャレンジしてみます😉 それでは!

【Nginx】CSSが読み込まれない問題の解消

......Dockerで立てたNginxでCSSが反映されてない!何故だ!
以前の記事で立てたNginxのサーバにアクセスしたところ...

f:id:keko5342:20211202233651p:plain
どうして......

ビルドする前のvueのlocalでは動いているのでビルドのオプションが悪い説を提唱.

とりあえずキャッシュの削除

CSSが反映されない場合は大体キャッシュが悪い。Chromeの履歴からキャッシュの削除.

-> 変わらず

google先生で検索

見つかった解決策

  1. httpdのsendfileという設定が キャッシュされたファイルを送らない設定 をoffにする

  2. nginx.confにmime.typeを読み込む設定を追加する

1のこれは...本番環境だとoffにできないので却下.
2はnginx.confのhttp内に include /etc/nginx/mime.types; を追記する方法、mime.typesの中身を見てみると...

# cat /etc/nginx/mime.types

types {
    text/html                                        html htm shtml;
    text/css                                         css;
    text/xml                                         xml;
    image/gif                                        gif;
    image/jpeg                                       jpeg jpg;
    application/javascript                           js;
    application/atom+xml                             atom;
    application/rss+xml                              rss;

    text/mathml                                      mml;
    text/plain                                       txt;
    text/vnd.sun.j2me.app-descriptor                 jad;
    text/vnd.wap.wml                                 wml;
    text/x-component                                 htc;

    image/avif                                       avif;
    image/png                                        png;
    image/svg+xml                                    svg svgz;
    image/tiff                                       tif tiff;

......................................................

どうやらmimeTypeと拡張子を相互に変換してくれる設定っぽい、これが読み込まれていなかったので.cssファイルを渡す方法がわからなかったと...どうして最初から設定されていないんだ???

command + Shift + R でスーパーリロード.

f:id:keko5342:20211203001408p:plain
ちゃんと text/css で読み込まれた!

nginxの設定次第ではcssが読み込めないことがあるんですね...!また賢くなりました。

参考
  1. NginxでCSS、JSが反映されない時の対応メモ - yikegaya’s blog

【Docker】Vue + Nginxで作ったフロントエンド、Laravel + MySQLでバックエンドのコンテナ同士の連携

こんばんは、けこです。
今日はdocker composeを使った2つの環境の構築、そしてそれらの環境のネットワーク連携について調べました!
LaravelならフロントもDB接続もできるのにわざわざ環境を分ける必要があるのか?というところから解説していきたいと思います。

何故環境を分けるのか?
  1. Gitでのコード管理が容易

  2. スマホアプリを追加で作る際に転用できる

  3. 問題が起きた際に原因を特定しやすくなる

フロントエンドとバックエンドの環境を分けるメリットとして真っ先に上がるのはこれらの利点でしょうか、特に3の利点に関しては実運用で役立ちそうな予感がします。

Gitでのコード管理が容易

Laravelでフロントエンドとバックエンドを担当する場合、1つのリポジトリにフロントエンドとバックエンドが混在することになりどのコードを弄ったらいいかわからない...という状況が生まれることがあります。

もっとも、LaravelはMVCフレームワークなので他のフレームワークや言語を利用するという手もあります。APIだけの利用目的でもViewを返す代わりにJsonを返すプログラムを書くだけなのでとても簡単なのが嬉しいですね。

スマホアプリを追加で作る際に転用できる

最初にWebサービスを作ってから、軌道に乗ったらスマホアプリを作るというケースは少なからず存在します。
初めからAPIとしてバックエンドを独立させてSPAでフロントエンドを作っておけば、スマホアプリへの移行が簡単になります。

compose同士のネットワーク連携

本題に行きましょう、今回はVue+NginxのフロントエンドとLaravel+MySQLのバックエンドの連携にチャレンジしてみます。

2つのコンテナアプリケーションをdocker composeで立てて、それらを同じネットワークに配置できればいいわけですね。

composeを2つ用意

フロントエンド(参考:https://takake-blog.com/wsl-docker-vue-nginx/#Docker_compose

version: "3.8"

services:
  nginx:
    build: ./containers/nginx
    container_name: nginx
    ports:
      - '80:80'
    volumes:
      - ./vue_project:/var/www/html
      - ./containers/nginx/nginx.conf:/etc/nginx/nginx.conf

  vue-app:
    build: ./containers/vue_app
    container_name: vue-app
    ports:
      - '8080:8080'
    volumes:
      - ./vue_project:/app
    tty: true

バックエンド(参考:Laravel8公式)

# For more information: https://laravel.com/docs/sail
version: '3'
services:
    laravel.test:
        build:
            context: ./vendor/laravel/sail/runtimes/8.1
            dockerfile: Dockerfile
            args:
                WWWGROUP: '${WWWGROUP}'
        image: sail-8.1/app
        extra_hosts:
            - 'host.docker.internal:host-gateway'
        ports:
            - '3000:80'
        environment:
            WWWUSER: '${WWWUSER}'
            LARAVEL_SAIL: 1
            XDEBUG_MODE: '${SAIL_XDEBUG_MODE:-off}'
            XDEBUG_CONFIG: '${SAIL_XDEBUG_CONFIG:-client_host=host.docker.internal}'
        volumes:
            - '.:/var/www/html'
        networks:
            - sail
        depends_on:
            - mysql
    mysql:
        image: 'mysql/mysql-server:8.0'
        ports:
            - '${FORWARD_DB_PORT:-3306}:3306'
        environment:
            MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}'
            MYSQL_ROOT_HOST: "%"
            MYSQL_DATABASE: '${DB_DATABASE}'
            MYSQL_USER: '${DB_USERNAME}'
            MYSQL_PASSWORD: '${DB_PASSWORD}'
            MYSQL_ALLOW_EMPTY_PASSWORD: 1
        volumes:
            - 'sailmysql:/var/lib/mysql'
        networks:
            - sail
        healthcheck:
            test: ["CMD", "mysqladmin", "ping", "-p${DB_PASSWORD}"]
            retries: 3
            timeout: 5s
networks:
    sail:
        driver: bridge
volumes:
    sailmysql:
        driver: local

よく見てみると、バックエンドのcomposeファイルにnetworksとの記述があります。同じようにフロントエンドにも記述してみましょう。

そしてnginxのコンテナにCLIで入ってcurl localhost:8080

f:id:keko5342:20211202032233p:plain
が...ダメ...!

networksを調べて再挑戦!

docs.docker.jp

fmfm...ネットワークを合わせる必要があって、コンテナ名でアクセスできると。
そういえばdocker compose up -dした際にネットワークが作られているような...?

composeファイルで指定したネットワークどおりにネットワークが生成されているとは限らない

docker network lsコマンドで調べたところ、フォルダ名_ + ネットワーク名でネットワークが生成されるみたいです!
つまりフロントエンドの方をバックエンドのネットワーク名に合わせてやれば...

f:id:keko5342:20211202040625p:plain
できました

今日の学び
  • networks を使う際は目的のネットワーク名を先に調べておくこと

  • networksで同じ名前を指定しても目的のネットワーク名で生成されるとは限らないということ

  • コンテナと通信する際はホスト名にコンテナ名を使えるということ

以上!やはり基本的な情報を得るためには公式情報を見に行くに限る...!これでAPIを作った時にも通信できますね!

【PHP】Laravelにおけるテスト手順、勉強メモ

こんばんは、けこです。
今日は先日作ったsailによるLaravel環境のテストを試してみました!
php artisan互換のコマンドがsailで使えるのはありがたい!せっかくなので今回はsailコマンドで進めていきます。

前提

docker基本知識、下記リンクのOS別Laravel導入を終えていること。

インストール 8.x Laravel

~/.zshrc にエイリアスを記載済み

alias sail="./vendor/bin/sail"
テスト実行
sail test

でプロジェクトのtestsフォルダ内のFeature, Unitフォルダにあるテストメソッドを実行可能。php artisan testでも可。

testファイルの作り方

{プロジェクト名}/tests/Feature/UserTest.phpを作る場合、削除する場合は手動で対応しましょう。

sail artisan make:test UserTest

このようなコードが生成されるので、getメソッドの引数//userに変更します。

<?php

namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;

class UserTest extends TestCase
{
    /**
     * A basic feature test example.
     *
     * @return void
     */
    public function test_example()
    {
        #$response = $this->get('/'); 削除
        $response = $this->get('/user');

        $response->assertStatus(200);
    }
}

/userというルーティングは作られていないので失敗してしまいます。

f:id:keko5342:20211130221609p:plain
実行結果

ルーティングを加えて成功させる

/routes/web.phpに/userへのルーティングを追記、今回はルーティングのみの検証ということでviewは省略。

<?php

use Illuminate\Support\Facades\Route;

/*
コメント略
*/

Route::get('/', function () {
    return view('welcome');
});

Route::get('/user', function () {
    return 'user page';
});
再実行

sail test 無事成功、テストを作る時に必要なコーディングのほとんどを自動生成してくれるのでとても楽ですね!

f:id:keko5342:20211130225158p:plain
テスト成功!

最後

本日は以上となります、APIの値の検証やViewのテストなどもっと深掘りできると思うのでおいおい勉強してまとめていきます!

【PHP】Sailに出会いました

What is Sail?

~~~sailに出会うまでの経緯~~~
うーん、スタートアップ支援のためでLaravelを使うことになったけど環境どうしよう?Dockerで開発したいけどcomposerを入れてローカルに作るのが定石っぽい...うん、公式を見よう!

インストール 8.x Laravel

curl -s "https://laravel.build/example-app" | bash
cd example-app

./vendor/bin/sail up

docker環境があればこのコマンドを使うだけで開発環境ができると…fmfm.
docker環境はあるのでサクッと実行!

f:id:keko5342:20211129032338p:plain

なにやら沢山出てきましたがほとんどconfigファイルとLaravelのファイルの様子。docker-composeを実行してmysqlやらradisの環境をLaravelと一緒に作ってくれる感じ、とても便利。

もう少し調べてみる

mailhogは使わないし要らないやって場合は docker-compose.yml の該当範囲をコメントアウト.
mysql等の初期パスワードや初期ユーザは環境変数を格納している .env ファイルを触る.

と、これは開発環境だけでなくstg環境や本番環境を想定した作り!素晴らしい!

もうちょっと便利に

sailを ./vendor/bin/sail と書き続けるのは面倒臭い...

alias sail="./vendor/bin/sail"

ということで、~/.zshrcエイリアスを追記。これでsailと打つだけで良くなった!

終わり

sail build sail upみたいな感じでdocker composeと同じコマンドを使えることがわかった。

とても便利だけどこれだとcomposeとあまり変わらないような...?と思っていたんですが、WWWGroup, WWWUserあたりの環境変数を補完してくれるっぽい。docker compose build --no-cacheで出ていたWWWGroupが無いというエラーがsail build --no-cache では解消されていました。

もっと便利機能がある予感...また調べてみようとおもいます!では!