Docker + Laravel でデータベース接続

## コンテナ間でやり取り
コンテナは３つ

**dbコンテナ**
**backendコンテナ**
**webコンテナ**

```yml:docker-compose.yml
services:
  backend:
    build:
      context: ./backend
      dockerfile: ./Dockerfile
    ports:
      - "8000:8000"
    networks:
      - app-network

  web:
    build:
      context: ./web
      dockerfile: ./Dockerfile

    ports:
      - "80:80"
      - "5173:5173"
    environment:
      - HOST=0.0.0.0
    networks:
      - app-network
    depends_on:
      - db
  db:
    image: mariadb:10.11
    ports:
      - "3306:3306"
    environment:
      MYSQL_ROOT_PASSWORD: 1111
      MYSQL_DATABASE: introduce
      MYSQL_USER: db_user
      MYSQL_PASSWORD: 1111
    volumes:
      - mariadb-data:/var/lib/mysql
    networks:
      - app-network
volumes:
  mariadb-data:
networks:
  app-network:
    driver: bridge

```

Laravelはbackendコンテナで起動する。

```Dockerfile:Dockerfile
FROM node:20-alpine AS install
WORKDIR /laravel
COPY ./laravel-introduce/package.json /laravel/
RUN npm install

FROM php:8.2-fpm

RUN apt-get update \
    && apt-get install -y netcat-openbsd zlib1g-dev mariadb-client vim libzip-dev \
    && docker-php-ext-install zip pdo_mysql

WORKDIR /laravel
COPY ./laravel-introduce/.env /laravel/.env
COPY --from=install /laravel/node_modules /laravel/
COPY ./laravel-introduce/ /laravel/
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
RUN composer install
CMD ["php", "artisan","migrate"]
```

このDockerfileではコンテナ自体は正しく立ち上がったが、
```CMD ["php", "artisan","migrate"]```する際にdbコンテナに接続できない旨のエラーが出た。

## Migrateできない
起動後にbackendコンテナに入って```php artisan migrate```すると普通にできた。
たぶんdbコンテナが起動する前に```php artisan migrate```してるからエラーが出ていると思う

しかし```docker-compose.yml```には
```yml
depends_on:
      - db
```
と記述しているのになぜか

答えはいつも公式ドキュメントが教えてくれる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3765500/b86f628b-17a2-4812-9aa3-3d1d68f5705f.png)

**準備完了までは待機せずに実行状態まで待機する**

つまり起動の順番だけしか制御してないということ。

じゃあdbコンテナが起動するまで待ってやればいいと

```Dockerfile:Dockerfile
FROM node:20-alpine AS install
WORKDIR /laravel
COPY ./laravel-introduce/package.json /laravel/
RUN npm install

FROM php:8.2-fpm

RUN apt-get update \
    && apt-get install -y netcat-openbsd zlib1g-dev mariadb-client vim libzip-dev \
    && docker-php-ext-install zip pdo_mysql

WORKDIR /laravel
COPY ./laravel-introduce/.env /laravel/.env
COPY --from=install /laravel/node_modules /laravel/
COPY ./laravel-introduce/ /laravel/
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
RUN composer install
RUN cat <<EOF > /laravel/migration_run.sh
#!/bin/sh
until nc -z db 3306; do
echo "Waiting for MySQL...";
sleep 2;
done
php artisan migrate
php artisan serve --host=0.0.0.0 --port=8000
echo "db kidou!!!!!!!";
EOF

RUN chmod +x /laravel/migration_run.sh
ENTRYPOINT [ "/laravel/migration_run.sh" ]
```

重要なのはこの部分です
```Dockerfile
RUN cat <<EOF > /laravel/migration_run.sh
#!/bin/sh
until nc -z db 3306; do
echo "Waiting for MySQL...";
sleep 2;
done
echo "db kidou!!!!!!!";
php artisan migrate
php artisan serve --host=0.0.0.0 --port=8000
EOF

RUN chmod +x /laravel/migration_run.sh
ENTRYPOINT [ "/laravel/migration_run.sh" ]
```

シェルスクリプトは書けないのでChatGptに丸投げしました。
```RUN cat <<EOF > /laravel/migration_run.sh```
ここの部分ですが最初はEOFが出るまでファイルに書き込み続けるという処理のようです。
普通にターミナルでファイルに書き込む際は```echo hogehoge > migration_run.sh```
こうすると思いますが、Dockerfileだと違うみたいです。

```
until nc -z db 3306;
```
```nc```コマンドですがこれは```netcat```のコマンドです。
似ているもので言えば```netstat```ですが
動作は全く違います
```netstat```はコマンドをたたいたPCの通信状態を表示しますが
```netcat```外部のポートが通信可能かどうかなど
を確認してくれるようです
理解が正しいかはわかりません...

## Migrateできた。
Dockerfielを書き換えてコンテナ起動時にmigrateに成功した。
この問題を検索したりしても同じ症状の人はいたが原因が同じ人はいなかったように思う。
ほとんどの記事がLaravelの環境変数にサービス名を記述していなかったり、
データベースのログインユーザーがrootだったから接続できなったりといった内容のものだった。

そもそもDockerでlaravelを使う場合はみんなLaravel Sailをつかっているんだろうなと思った。
```conditiont```というディレクティブをcompose.ymlに書くことで起動が完了したのかを確認してくれるそうですが、ymlに依存するのはよくなさそうなので今回はシェルスクリプトで解決しました。