🐳

Rancher DesktopでDocker環境を構築する!③

2023/02/15に公開
2

前回のあらすじ
Rancher DesktopでDocker環境を構築する!②
引き続きdocker composeでLEMP環境を作っていきます。

目標

docker composeでLEMP環境を作る。

  • PHP8
  • MySQL8
  • Nginx(読み: エンジンエックス)

フレームワークはLaravel9を採用。
今回はDBサーバーコンテナを作るところまで進めます。

動作確認済み環境

  • MacBook Air(M1, 2020)
  • メモリ: 16GB
  • macOS: Ventura 13.0.1
  • Rancher Desktop Version 1.7.0 (1.7.0)
  • git version 2.37.1

本記事の想定読者

  • VirtualBoxやVagrantは使ったことあるけどDockerは触ったことがない方
  • なんとなく誰かが作ってくれたDocker環境で開発してるけどもうちょっとDockerに詳しくなりたい方

本記事執筆の目的

  • なんとなく誰かが作ってくれたDocker環境で開発してるけどもうちょっとDockerに詳しくなりたい方
    → まさに自分がこの状態だったので、改めてDockerの知識を基礎から身につける
  • Docker初心者が最低限の知識を獲得できる内容であること
  • エラーなど失敗ケースも含めて記載し、知見共有ができるような記事であること

本記事では取り扱わないこと

  • Git, GitHubの使い方詳細説明
  • MySQL以外のDBを用いた環境構築
  • Windowsでの環境構築

前提

  • Rancher Desktopがインストールされ、dockerの各種コマンドが叩けること

DBサーバーのコンテナを作る

MySQLの設定フォルダ・ファイルを作成

ルートディレクトリ直下にこのようなフォルダ・ファイルを作成しておきます。

infra/
  L mysql/
   L dockerfile
   L my.cnf
  L php/
  L nginx/
src/
docker-compose.yml

💀 設定ファイル名typoに注意

❌ my.conf
⭕️ my.cnf
ここでファイル名を間違えると、のちにビルドするときに失敗します(1敗)

docker-compose.ymlにDB設定を追記する

  db:
    build: ./infra/mysql
    volumes:
      - db-store:/var/lib/mysql

volumes:
  db-store:

※インデントに注意して追記してください。

infra/mysql/dockerfileの中身を書く

FROM mysql/mysql-server:8.0

ENV MYSQL_DATABASE=sndbox_laravel9 \
  MYSQL_USER=jibunnno_namae \
  MYSQL_PASSWORD=secret \
  MYSQL_ROOT_PASSWORD=secret \
  TZ=Asia/Tokyo

COPY ./my.cnf /etc/my.cnf
RUN chmod 644 /etc/my.cnf

MYSQL_DATABASE, MYSQL_USERは任意の命名でOKです。
※パスワードは本来は特定されないような文字列にするべきですが、今回はお試しなのでMySQLのデフォルトのままに進めます。

💀 実はここでtypoしてます。後々これで酷い目に遭います・・・
❌ ENV MYSQL_DATABASE=sndbox_laravel9
⭕️ ENV MYSQL_DATABASE=sandbox_laravel9 \

infra/mysql/my.cnfの中身を書く

[mysqld]
# default
skip-host-cache
skip-name-resolve
datadir = /var/lib/mysql
socket = /var/lib/mysql/mysql.sock
secure-file-priv = /var/lib/mysql-files
user = mysql

pid-file = /var/run/mysqld/mysqld.pid

# character set / collation
character_set_server = utf8mb4
collation_server = utf8mb4_ja_0900_as_cs_ks

# timezone
default-time-zone = SYSTEM
log_timestamps = SYSTEM

# Error Log
log-error = mysql-error.log

# Slow Query Log
slow_query_log = 1
slow_query_log_file = mysql-slow.log
long_query_time = 1.0
log_queries_not_using_indexes = 0

# General Log
general_log = 1
general_log_file = mysql-general.log

[mysql]
default-character-set = utf8mb4

[client]
default-character-set = utf8mb4

💀 余談

この設定の timezone 周りで後々落とし穴に落ちますが一旦このまま進めます(あとで解説します)

イメージをビルドしてコンテナを起動する

下準備

cd dev/sandbox_laravel9

念の為起動中のコンテナがないか確認

docker compose ps

動いているコンテナがあれば停止しておく

docker container stop <コンテナのNAME>

ビルド

docker compose build


問題なさそうです。

  • コンテナ起動(これまでのコンテナ3つまとめて)
docker compose up -d

  • コンテナの状態確認
docker compose ps


3つとも起動していればOKです(STATUSがrunningになっている)

  • MySQLのバージョン確認
docker compose exec db mysql -V
mysql  Ver 8.0.32 for Linux on aarch64 (MySQL Community Server - GPL)

Laravelのマイグレーションができるか確認

※ここから落とし穴の連続です。

💀 マイグレーションしてみる(1回目)

docker compose exec app bash
root@gdfjhiouderg:/data# php artisan migrate

Illuminate\Database\QueryException
SQLSTATE[HY000] [2002] Connection refused ~~~
(以下略)

MySQLへの接続を拒否されちゃいました。src/.env を確認します。

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=

infra/mysql/dockerfileで設定した内容と食い違っているのが原因でしょう。

😇 対処方法 .envのDB接続情報を修正する

# INIT SETTING
# DB_CONNECTION=mysql
# DB_HOST=127.0.0.1
# DB_PORT=3306
# DB_DATABASE=laravel
# DB_USERNAME=root
# DB_PASSWORD=

# FIX SETTING
DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=sandbox_laravel9
DB_USERNAME=jibunnno_namae
DB_PASSWORD=secret

念の為元の設定をコメントアウトで残しつつ、接続情報を修正しました。

💀 マイグレーションしてみる(2回目)

docker compose exec app bash
root@gdfjhiouderg:/data# php artisan migrate

Illuminate\Database\QueryException
SQLSTATE[HY000] [1044] Access denied for user 'jibunnno_namae'@'%' to database 'sandbox_laravel9' ~~~
(以下略)

jibunnno_namaeユーザーにはDBアクセス権限がないらしいです。

MySQLにrootでログインして、jibunnno_namae ユーザーの存在チェック・権限チェックを行いましたが、
ユーザーも存在しており、全権限が付与されています。
が、

mysql> show grants for jibunnno_namae;
+-------------------------------------------------------------------+
| Grants for jibunnno_namae@%                                         |
+-------------------------------------------------------------------+
| GRANT USAGE ON *.* TO `jibunnno_namae`@`%`                          |
| GRANT ALL PRIVILEGES ON `sndbox_laravel9`.* TO `jibunnno_namae`@`%` |
+-------------------------------------------------------------------+
2 rows in set (0.00 sec)

GRANT ALL PRIVILEGES ON **sndbox**_laravel9.* TO
💀 typoしてました・・・dockerfileを書いた時点で間違えていたようです。

😇 対処方法 ビルドし直す

  1. 今あるDBコンテナを一旦破棄
  2. infra/mysql/dockerfileのtypoを修正する
  3. イメージをビルドし直す
  4. DBコンテナを再度作る〜起動

1. 今あるDBコンテナを停止・削除

docker-compose rm -fsv db (←ymlで指定したservice名)

☕️ 余談

  • docker compose にはコンテナの個別停止・削除機能はないようでした。
    なので今回は docker-compose コマンドで代用しました。
  • オプション -fsv の意味(3つの連結オプション)
  • -f : 稼働中コンテナを止めるかの確認をしない
  • -s : コンテナを削除する前に止める
  • -v : 匿名ボリュームを削除する

参考
https://aton-kish.github.io/blog/post/2020/10/04/docker-compose-rm/

2. infra/mysql/dockerfileのtypoを修正する
❌ ENV MYSQL_DATABASE=sndbox_laravel9
⭕️ ENV MYSQL_DATABASE=sandbox_laravel9 \

3. イメージをビルドし直す

docker compose build --no-cache db

4. DBコンテナを再度作る〜起動
先に今動いているコンテナがあれば停止する

docker compose ps
docker container stop <コンテナのNAME>
docker compose ps

STATUSがexitedになっていればOK

DBコンテナ作成・全コンテナ起動

docker compose up -d

💀 マイグレーションしてみる(3回目)

docker compose exec app bash
root@gdfjhiouderg:/data# php artisan migrate

Illuminate\Database\QueryException
SQLSTATE[HY000] [1044] Access denied for user 'jibunnno_namae'@'%' to database 'sandbox_laravel9' ~~~
(以下略)

だめでした(エラー内容は同じ)
MySQLの jibunnno_namae の権限情報を確認します。

mysql> show grants for jibunnno_namae;
+-------------------------------------------------------------------+
| Grants for jibunnno_namae@%                                         |
+-------------------------------------------------------------------+
| GRANT USAGE ON *.* TO `jibunnno_namae`@`%`                          |
| GRANT ALL PRIVILEGES ON `sndbox_laravel9`.* TO `jibunnno_namae`@`%` |
+-------------------------------------------------------------------+
2 rows in set (0.00 sec)

🙃🙃🙃

  • sndbox_laravel9 のtypoが直ってない・・
    → 単純にDB名をMySQLコマンドで変更すれば良い?(対処療法)
    → 操作権限の対象DB名も更新すれば良い?(対処療法)
  • そもそもユーザー名がなんか変?
    → <ユーザー名>@<ホスト名>になっていない?(※後述しますが変ではありません)

🙃 思いつく限り出来そうなことをやってみます

一旦MySQLからexitで抜けて root ユーザーで入り直す

mysql -u root -p

正しい名前のデータベースを新規作成

CREATE DATABASE sandbox_laravel9;

sndbox_laravel9を削除(テーブルはまだ空なので問題なし)

DROP DATABASE IF EXISTS sndbox_laravel9;

これでひとまず失敗作のsndbox_laravel9は消え去りました。

次は権限の設定

USE mysql;
SELECT user, host FROM user;

+------------------+-----------+
| user             | host      |
+------------------+-----------+
| jibunnno_namae   | %         |
| healthchecker    | localhost |
| mysql.infoschema | localhost |
| mysql.session    | localhost |
| mysql.sys        | localhost |
| root             | localhost |
+------------------+-----------+
6 rows in set (0.00 sec)

hostが変、なので更新してみます。
※💀 ↑この時点では気づいていませんが、別に変ではないです。
% はどのようなホスト名でも操作可能という意味なので、LaravelからMySQLにアクセスする分にはこのままで問題ないはずです。

UPDATE user SET host = 'db' WHERE user = 'jibunnno_namae';

Query OK, 1 row affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0
USE mysql;
SELECT user, host FROM user;

+------------------+-----------+
| user             | host      |
+------------------+-----------+
| jibunnno_namae   | db        |
| healthchecker    | localhost |
| mysql.infoschema | localhost |
| mysql.session    | localhost |
| mysql.sys        | localhost |
| root             | localhost |
+------------------+-----------+
6 rows in set (0.00 sec)

改めて権限確認

SHOW GRANTS FOR jibunnno_namae;

mysql> show grants for jibunnno_namae;
+-------------------------------------------------------------------+
| Grants for jibunnno_namae@%                                         |
+-------------------------------------------------------------------+
| GRANT USAGE ON *.* TO `jibunnno_namae`@`%`                          |
| GRANT ALL PRIVILEGES ON `sndbox_laravel9`.* TO `jibunnno_namae`@`%` |
+-------------------------------------------------------------------+
2 rows in set (0.00 sec)

なにも変わってない、ので
sandbox_laravel9 に対して
 テーブル参照・作成・削除
 レコード参照・挿入・更新・削除
= DB単位の一通りの権限を持たせる、で良さそう?

まずは欲しい権限をつけてみます

GRANT CREATE ON sandbox_laravel9.* to jibunnno_namae@db;
FLUSH PRIVILEGES;

※💀 ↑CREATEしかつけてないので後でマイグレーションでコケます

💀 マイグレーションしてみる(4回目)

紆余曲折あったものの、ようやくマイグレーションが試せそうです

docker compose exec app bash
php artisan migrate


だめでした(4敗目)
ただしエラー内容が変わっています、172.19.0.4・・・?

回想

hostが変、なので更新してやる
UPDATE user SET host = 'db' WHERE user = 'jibunnno_namae';

↑変なのではなく % はどのようなホスト名でも操作可能ですよ、の意
元に戻す

UPDATE user SET host = '%' WHERE user = 'jibunnno_namae';

もう一度 php artisan migrate するも同様のエラーでした。

🙃 思いつく限り出来そうなことをやってみます(その2)

appコンテナからでて、ここまでのソースをコミットして、コンテナ全部破棄〜再作成・起動

git status
git add .
git commit -m "DBサーバーコンテナ作成途中"

コンテナ破棄

docker compose down

コンテナ作成・起動

docker compose up -d

💀 マイグレーションしてみる(5回目)


だめでした(5敗目)
CREATE権限しか持ってないためSELECTできずにコケているようです(migrationテーブルだけ作成に成功していました)
jibunnno_namae ユーザーの権限を見直し・修正

mysql> SHOW GRANTS FOR jibunnno_namae;

今の権限↓

GRANT USAGE ON . TO jibunnno_namae@%
GRANT CREATE ON sandbox_laravel9.* TO jibunnno_namae@%
GRANT ALL PRIVILEGES ON sndbox_laravel9.* TO jibunnno_namae@%

GRANT INSERT,SELECT,UPDATE,DELETE ON sandbox_laravel9.* to jibunnno_namae;

一通りのCRUD権限を付与成功(もしかしたらあとで権限足りなくなるかも・・・)
念の為、必要そうなものは全部付与(ユーザー管理系は除く)

mysql> SHOW GRANTS FOR jibunnno_namae;

| GRANT DROP, FILE, INDEX, ALTER, CREATE TEMPORARY TABLES, EXECUTE, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, TRIGGER, CREATE TABLESPACE ON *.* TO `jibunnno_namae`@`%` |
| GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, LOCK TABLES ON `sandbox_laravel9`.* TO `jibunnno_namae`@`%`                                                                          |
| GRANT ALL PRIVILEGES ON `sndbox_laravel9`.* TO `jibunnno_namae`@`%`

参考
https://blog.katsubemakito.net/mysql/grant
https://dev.mysql.com/doc/refman/8.0/ja/grant.html

😇 マイグレーションしてみる(6回目)

そろそろ成功したいところです

出来ました・・・

mysql> show tables;
+----------------------------+
| Tables_in_sandbox_laravel9 |
+----------------------------+
| failed_jobs                |
| migrations                 |
| password_resets            |
| personal_access_tokens     |
| users                      |
+----------------------------+
5 rows in set (0.00 sec)

試しにレコードを作ってみる

docker compose exec app bash
php artisan tinker

$user = new App\Models\User();
$user->name = 'phper';
$user->email = 'phper@example.com';
$user->password = Hash::make('secret');
$user->save();

レコードが出来たか確認、DBコンテナ→MySQLに入る

USE sandbox_laravel9;
SELECT * FROM users\G;

mysql> SELECT * FROM users\G;
*************************** 1. row ***************************
               id: 1
             name: phper
            email: phper@example.com
email_verified_at: NULL
         password: $2y$10$gHDUrIWO/f4o6g/xtoEHY.Cf08v4kUpG7fmxSX.ny.aGvSMU6udIq
   remember_token: NULL
       created_at: 2023-02-12 12:35:58
       updated_at: 2023-02-12 12:35:58

Laravel越しにテーブル操作ができました!
が、タイムゾーンが狂っているようです・・・

タイムゾーンを変更する

💀 infra/mysql/dockerfileを見直す

TZ=Asia/Tokyo で設定されています。
ということはMySQL自体もこの通りに設定されているはずです。

SHOW VARIABLES LIKE '%time_zone%';

mysql> SHOW VARIABLES LIKE '%time_zone%';
+------------------+--------+
| Variable_name    | Value  |
+------------------+--------+
| system_time_zone | JST    |
| time_zone        | SYSTEM |
+------------------+--------+
2 rows in set (0.01 sec)

一見問題なさそうに見えますが、2行目のtime_zoneがSYSTEMを参照しているみたいですね👀
→SYSTEM・・・もしやLinux(MySQLコンテナ)のタイムゾーン設定?

💀 infra/mysql/my.cnfを見直す

🙃 思いつく限り出来そうなことをやってみます

infra/mysql/my.cnf

# timezone
default-time-zone = SYSTEM
log_timestamps = SYSTEM

どうやらここが問題?らしいので、

default-time-zone = 'Asia/Tokyo'

に変更

MySQLコンテナを再起動

docker compose restart db

動作確認してみる
appコンテナに入ってLaravel tinkerで同じように別なユーザーデータを作ってみる

docker compose exec app bash
php artisan tinker

$user = new App\Models\User();
$user->name = 'timezone_tester_2nd';
$user->email = 'timezone_tester_2nd@example.com';
$user->password = Hash::make('time2');
$user->save();


だめでした(現在時刻11:23)

一旦ここまでの内容をコミットして、コンテナ破棄〜再作成を試して・・・
と思ったのですが、my.cnfの default-time-zone = SYSTEM を変えるのを忘れていました・・

変更してもう一回ユーザー作り直して確認してみました↓

だめでした(2敗)(現在時刻11:31)

🙃 思いつく限り出来そうなことをやってみます(その2)

一旦ここまでの内容をコミットして、コンテナ破棄〜再作成を試してましょうか・・・

appコンテナからでて、ここまでのソースをコミットして、コンテナ全部破棄〜再作成・起動

git status
git add .
git commit -m "DBコンテナのタイムゾーン設定をJSTに修正"

コンテナ破棄

docker compose down

コンテナ作成・起動

docker compose up -d

(ユーザー作成作業諸々中略)

だめでした(3敗)

🙃 思いつく限り出来そうなことをやってみます(その3)

Laravel側でタイムスタンプの時間がズレてるのでは?(もう他に原因が思いつかない)

src/config/app.php

/*
|------------------------------------------------------------------
| Application Timezone
|------------------------------------------------------------------
|
| Here you may specify the default timezone for your application, h
| will be used by the PHP date and date-time functions. We have 
| ahead and set this to a sensible default for you out of the box.
|
*/
'timezone' => 'UTC',

'timezone' => 'Asia/Tokyo', に変更して再検証(まずはコンテナ再起動等一切なしで)
(ユーザー作成作業諸々中略)

成功💡(登録時刻12:06)
最初からapp.phpをいじるだけでよかったのでは・・・🤔

おわりに

最後までお読みくださりありがとうございました🙏

マイグレーション・MySQLユーザーの権限設定・タイムゾーン設定と、散々失敗を繰り返しましたが、
同様のエラーでハマってしまった方の一助となれたら幸いです。

これで開発に必要な環境の用意はできたので、ここからLaravelの機能を使い倒したり、PHPの開発環境整備なども手を動かしながら都度記事化していけたらいいなと思います。

また、今回はGit, GitHubに関する説明は全て飛ばしてしまいましたが、そちらもいずれ記事化してみたいですね👍

Discussion

Tanner / テナーTanner / テナー

こんにちは!
もし可能であれば、タグの #dokcerを #dockerに修正いただけると助かります!

sataksatak

ご返事が遅れましたが、typoのご指摘ありがとうございます!🙇
たった今修正しました〜