CentOS7 + Apache + PHP7 + MySQL + phpMyAdmin の LAMP開発環境 を Docker で作る

そろそろ AlmaLinux 辺りに手を付けないと、とは思いつつ今までの積み重ねを生かすためにひとまず Docker で LAMP環境 を作るのは最後までやってみました。

経緯

ベースは以下の記事の Apache + PHP 環境です。

ここに至るまでも

のような積み重ねを行ってきましたが、さらに MySQL や phpMyAdmin も加えます。

成果物

構成

ディレクトリ構造

PROJECT_ROOT/
    ├ apache/
    │   // 略
    │
    ├ cert/
    │
    ├ mysql/
    │   ├ cnfd/                    // /etc/my.cnf.d にマウント
    │   ├ data/                    // /var/lib/mysql/data にマウント
    │   ├ docker/
    │   │   └ Dockerfile           // Apache 用 Dockerfile
    │   │
    │   ├ log/                     // /var/log/mysql にマウント
    │   └ my.cnf                   // /etc/my.cnf として使用
    │
    ├ php/
    │   // 略
    │
    ├ phpmyadmin/sessions          // /sessions にマウント。 phpMyAdmin のセッション格納用
    │
    ├ template/
    │   ├ apache/
    │   │   ├ apache_vh.conf       // 仮想サイトのディレクティブ設定を記述した conf ファイル
    │   │   ├ apache_vh_ssl.conf   // 上同。SSL版
    │   │   ├ php.conf             // /etc/httpd/conf.d/php.conf を上書きするためのテンプレート
    │   │   └ ssl.conf             // /etc/httpd/conf.d/ssl.conf を上書きするためのテンプレート
    │   │
    │   └ mysql/
    │       ├ 20-default-authentication-plugin.cnf
    │       ├ 40-pass.cnf
    │       ├ 50-my-tuning.cnf
    │       └ base.cnf
    │
    ├ workspace/
    │   ├ entrypoint_db.sh     // MySQL イメージのエントリポイント
    │   └ entrypoint_web.sh    // Apache イメージのエントリポイント
    │
    ├ .env                     // docker-compose.yml 用の環境変数
    └ docker-compose.yml

docker-compose.yml

version: '3.8'
services:
  web:
    # 略
  db:
    build:
      context: ./mysql/docker
      dockerfile: Dockerfile
      args:
        DB_ROOT_PASSWORD: $DB_ROOT_PASSWORD
        DB_HOST_PORTNUM: $DB_HOST_PORTNUM
        DB_CONTAINER_PORTNUM: $DB_CONTAINER_PORTNUM
    labels:
      lamp.ambergrease.dolphin: "Ambergrease MySQL"
    volumes:
      # workspace
      - ./workspace:/workspace
      # docker settings template
      - ./template:/template
      # mysql cnf.d
      - ./mysql/cnfd:/etc/my.cnf.d
      # mysql cnf
      - ./mysql/my.cnf:/etc/my.cnf
      # mysql data
      - ./mysql/data:/var/lib/mysql/data
      # error log
      - ./mysql/log:/var/log/mysql
    tty: true
    ports:
      - "$DB_HOST_PORTNUM:$DB_CONTAINER_PORTNUM"
    entrypoint: bash -c "bash /workspace/entrypoint_db.sh $DB_CONTAINER_PORTNUM $DB_ROOT_PASSWORD $PHPMYADMIN_USER_NAME $PHPMYADMIN_USER_PASSWORD && /bin/bash"
  phpmyadmin:
    image: phpmyadmin/phpmyadmin
    labels:
      lamp.ambergrease.yacht: "Ambergrease phpMyAdmin"
    volumes:
      # sessions
      - ./phpmyadmin/sessions:/sessions
    environment:
      - PMA_ARBITRARY=1
      - PMA_HOST=db
      - PMA_USER=$PHPMYADMIN_USER_NAME
      - PMA_PASSWORD=$PHPMYADMIN_USER_PASSWORD
    ports:
      - "$PHPMYADMIN_HOST_PORTNUM:$PHPMYADMIN_CONTAINER_PORTNUM"

上述の通りベースは CentOS7 + Apache + PHP (Xdebug, composer) の環境で、そこに MySQL と phpMyAdmin を足した形になります。最初は公式の MySQL8 のイメージを使用しようかと思いましたが上手くいかなかったので結局 プレーンな CentOS7 に MySQL を足す形にしました。

一方、 phpMyAdmin は最後にちょい足しで済んだので公式イメージをそのまま利用しています。

.env

# Web系 略

DB_ROOT_PASSWORD=Password-1234
DB_HOST_PORTNUM=13306
DB_CONTAINER_PORTNUM=3306
PHPMYADMIN_USER_NAME=admin
PHPMYADMIN_USER_PASSWORD=Password-1234
PHPMYADMIN_HOST_PORTNUM=8081
PHPMYADMIN_CONTAINER_PORTNUM=80

MySQL の設定と phpMyAdmin の設定が増えました。項目は分かりやすいと思います。

mysql/docker/Dockerfile

FROM centos:centos7
# args
ARG DB_ROOT_PASSWORD
ARG DB_HOST_PORTNUM
ARG DB_CONTAINER_PORTNUM
# timezone
RUN cp -p -f /usr/share/zoneinfo/Japan /etc/localtime
# yum install
RUN yum -y update && yum -y install \
    epel-release \
    sudo \
    less \
    # network ss (instaed of netstat)
    iproute
# rpm
RUN rpm -ivh https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm
# mysql
RUN yum -y install mysql-community-devel mysql-community-server
# mysql log
RUN mkdir /var/log/mysql
# mkdir and owner and so on
RUN chown -R mysql:mysql /var/log/mysql
RUN chown -R mysql /var/lib/mysql/
RUN chgrp -R mysql /var/lib/mysql/
RUN mkdir /template
RUN mkdir /workspace

上述の通り、 CentOS7 に MySQL を足す形で構築。

20-default-authentication-plugin.cnf

[mysqld]
# default_authentication_plugin=caching_sha2_password
default-authentication-plugin = mysql_native_password

パスワード保存形式を mysql_native_password に変更。

base.cnf

[mysqld]
basedir = /var/lib/mysql
datadir = /var/lib/mysql/data
plugin_dir = /usr/lib64/mysql/plugin

# socket
socket = /var/lib/mysql/mysql.sock

# language
lc-messages-dir = /usr/share/mysql-8.0/japanese

# error messages
lc_messages_dir = /usr/share/mysql-8.0

# not only lowercase characters in tablename
lower_case_table_names = 0

# character set
character_set_server = utf8mb4
collation_server = utf8mb4_ja_0900_as_cs_ks

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

# port

port = 3306

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

# Slow Query Log
slow_query_log = ON
slow_query_log_file = /var/log/mysql/slow_query.log
long_query_time = 5.0

# General Log
general_log = 1
general_log_file = /var/log/mysql/mysql-query.log

[mysql]
default-character-set = utf8mb4

[client]
default-character-set = utf8mb4

basedir, datadir, plugin_dir の指定や socketファイルの位置、 lc-messages-dir の言語ファイルの場所、 character_set_server, collation_server の文字コードや照合順序のデフォルト値等々、必要な項目を足しておきます。

workspace/entrypoint_db.sh

#!/bin/bash

SYSDBPATH="/var/lib/mysql/data/sys/"
DBINDFILE="sys_config.ibd"
SOCKFILE="/var/lib/mysql/mysql.sock"

# setting file replace and copy
sed -e "s/DB_CONTAINER_PORTNUM/${1}/gi" /template/mysql/base.cnf > /etc/my.cnf.d/base.cnf

cp /template/mysql/20-default-authentication-plugin.cnf /etc/my.cnf.d/20-default-authentication-plugin.cnf
cp /template/mysql/40-paas.cnf /etc/my.cnf.d/40-paas.cnf
cp /template/mysql/50-my-tuning.cnf /etc/my.cnf.d/50-my-tuning.cnf

# mysql initialize
if [ ! -e $SYSDBPATH$DBINDFILE ]; then
    /usr/sbin/mysqld --user=mysql --initialize &
    # wait for initialize process
    wait
    echo "mysqld initialized"
    /usr/sbin/mysqld --user=mysql &
    echo "mysqld boot"
    # wait for the mysql.sock file to be created
    # If you do not wait, you will get the error below when executing the command to change the password.
    # ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock' (2)
    while [ ! -e $SOCKFILE ]
    do
      sudo sleep 1
    done
    # get the init password
    DB_INIT_PASSWORD=$(sudo grep 'temporary password' /var/log/mysql/mysql-error.log | sudo awk '{print $13}')
    # change root password
    sudo mysql -u root -p${DB_INIT_PASSWORD} --connect-expired-password -e "ALTER USER 'root'@'localhost' IDENTIFIED BY '${2}'; CREATE USER '${3}'@'%' IDENTIFIED BY '${4}'; GRANT ALL ON *.* TO 'admin'@'%'; FLUSH PRIVILEGES;"
else
    /usr/sbin/mysqld --user=mysql &
    echo "mysqld boot"
fi
  • 最初の方はテンプレートから設定ファイルをコピー・一部設定をパラメータで置換
  • if [ ! -e $SYSDBPATH$DBINDFILE ]; then 以降は結構クセモノ。初回起動時は MySQL のデータファイルが存在しないため --initializeオプション で初期化を試みます
    • データファイルの配置場所は永続化しているので、初めて Dockerコンテナ をビルドしたのかそうでないのかで MySQL のデータファイルの有無が変化します
    • 問題は初回起動なのか異なるのかの判定。今回は sys_config.ibd が存在するかどうかで判定することで成功しました
      • なお、初期化からソケットが作られる ( mysql.sockファイル が作成される) まで時間がかかるため、 while [ ! -e $SOCKFILE ] の条件と sudo sleep 1 で確立まで待機しています
      • 初期化時に root のパスワードがログファイルに吐き出されるため、以下を実行
        • awkコマンド で取り出し
        • 取り出した初期パスワードを用い、 .env にあるパスワードに変更するように rootパスワード 変更の SQL文 を実行
          • このとき通常だとエラーが出てしまうので -eオプション を指定
  • sys_config.ibd が存在する(=2回目以降の起動) 場合は、単純に MySQL のプロセスを起動するだけ

以上のような工程を実行しています。


phpMyAdmin に関しては公式イメージを利用し、環境変数で設定を実施しているだけなのでスルーします。

余談 (名前の由来)

以上を以て一通りの LAMP環境 となったわけですが、この設定集には「Ambergrease」と名前を付けました。

アンバーグリス。龍涎香。かつては龍の涎が固まったものと考えられていたためにその名前となったわけですが、英名の方は古フランス語の ambre gris から、らしいです。

その正体はマッコウクジラの腸内に発生する結石。クジラは Docker のロゴでおなじみですね(種は問わず)。

さて、マッコウクジラ等のクジラから採れる油脂は灯りの燃料としても用いられたという歴史があるようです。

灯り、すなわちランプ(LAMP)。そこでこのマッコウクジラからの連想で、油を重視して grease の単語を採用しました。

クジラによるLAMP。そのため、「Ambergrease」。

参考

MySQL 初期化

bin/mysqld --initialize --user=mysql & とか /usr/sbin/mysqld --user=mysql --initialize & とか

5.7 からは mysqld --initialize に変更。それ以前は mysql_install_db

ディレクトリ初期化

# chown -R mysql /var/lib/mysql/
# chgrp -R mysql /var/lib/mysql/

basedir = /var/lib/mysql/ の場合

mysql-files

Insecure configuration for –pid-file: Location ‘/var/run/mysqld’ in the path is accessible to all OS users. Consider choosing a different directory.

初期管理者パスワード変更

Warning: Using a password on the command line interface can be insecure

[MySQL][ERROR] Fatal error: Please read “Security” section of the manual to find out how to run mysqld as root!

[Warning] [MY-010068] [Server] CA certificate ca.pem is self signed.

[ERROR] [MY-011011] [Server] Failed to find valid data directory.

ERROR 2002 (HY000): Can’t connect to local MySQL server through socket ‘/var/run/mysqld/mysqld.sock’ (2)

mysql.sock がない

外部接続

シェルスクリプト

wait

コマンド実行結果を echo で出力

Docker の MySQL公式イメージ

それ以上のことを行いたい場合には別の方法で行う必要がある。 鍵をにぎるのが同じくdocker-entrypoint.shの中に記述のある、/docker-entrypoint-initdb.d/だ。 このディレクトリにある、シェルスクリプトやsqlファイルが実行されるようになっている。

DockerでのMySQLのセットアップ方法とその仕組みを理解する

何……だと……(早く知りたかった

Docker Compose

ログ確認には docker-compose logs

Docker Compose の command と entrypoint

phpMyAdmin

この記事を書いた人

アルム=バンド

フロントエンド・バックエンド・サーバエンジニア。LAMPやNodeからWP、Gulpを使ってejs,Scss,JSのコーディングまで一通り。たまにRasPiで遊んだり、趣味で開発したり。