LinuxでApache2.4(httpd 2.4.43)＋PHP7.4をソースコンパイルしてWebサーバー構築 - 3.MySQL8.0導入編

# 前提と準備

## Linuxサーバー構築の記事
- [Sambaでファイルサーバー構築(CentOS 8.1・openSUSE 15.1・Ubuntu 20.04)](https://qiita.com/kazumi75kitty/items/4298795029ba9f67b9d2)
- [LinuxでApache2.4＋PHP7.4をソースコンパイル - 1.Apache導入](https://qiita.com/kazumi75kitty/items/daaf8ff8feb50ad04f41) /[【Raspberry Pi】](https://qiita.com/kazumi75kitty/items/67686eccaaec73251458)
- [LinuxでApache2.4＋PHP7.4をソースコンパイル - 2.PHP導入](https://qiita.com/kazumi75kitty/items/fca116b1b387fe56c93c) /[【Raspberry Pi】](https://qiita.com/kazumi75kitty/items/50f1a447f6ebc2ee2b61)
- LinuxでApache2.4＋PHP7.4をソースコンパイル - 3.MySQL導入【この記事】/[【Raspberry Pi】](https://qiita.com/kazumi75kitty/items/4212dacc45944f27ca94)
- [LinuxでApache2.4＋PHP7.4 - 4.セキュリティ(chownとfirewalld)](https://qiita.com/kazumi75kitty/items/8ad51806ac50bd04b644)
- [LinuxでIPsecゲートウェイをVPN構築 - 1.StrongSwan導入](https://qiita.com/kazumi75kitty/items/60941f00ae91c4d9f555) /[【Ubuntu 20.04＋Raspberry Pi】](https://qiita.com/kazumi75kitty/items/08259681247a6c2ebd0d)
- [LinuxでIPsecゲートウェイをVPN構築 - 2.VPNへの接続確認](https://qiita.com/kazumi75kitty/items/f5a33abda9f8edd5c47f) /[【Ubuntu 20.04＋Raspberry Pi】](https://qiita.com/kazumi75kitty/items/c83f920f052d83d62457)

[前回](https://qiita.com/kazumi75kitty/items/fca116b1b387fe56c93c)はApache2.4にPHP7.4をソースコンパイルでWebアプリ環境を構築しましたが、今回は、前回に引き続き、データベースサーバーを構築します(⑅•ᴗ•⑅)

データベースサーバーはWebサーバー本体と切り離し可能ですが、今回は簡単なため、Webサーバーと一体でデータベースのMySQLを構築します

## 環境
- Webサーバープログラム：Apache 2.4.43 ＋ PHP 7.4.6 ＋ MySQL 8.0
- クライアント：Windows10 Pro
- サーバーのアーキテクチャ：x64(動作はHyper-Vの第2世代で確認) Linuxのディストリビューション：CentOS 8.1 / openSUSE 15.1 Leap / Ubuntu 20.04(すべて64bit)

## 前提
- ユーザーはrootでインストール(私の検証ではadminという管理者アカウントにて、そこからsudoで処理しています)
- どのディストリビューションでも、ファイアウォールはfirewalldを使う(ディストリビューション独自のファイアウォールコマンドは使用しない)
- [前回](https://qiita.com/kazumi75kitty/items/fca116b1b387fe56c93c)の記事のApache＋PHP導入を完了していること

## サーバー条件
### IPアドレス
- クライアント：192.168.1.11
- Webサーバー：192.168.1.18(どのディストリビューションでも同じIPアドレスで検証)
- データベースサーバー：(Webサーバーと一体)
- 所属ネットワークセグメント：192.168.1.0/24
![Webサーバー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617906/a7610e28-afe4-2724-d772-8e45a4b1931e.png "Webサーバーとクライアントのネットワーク図")

### パッケージを個別ダウンロードしてインストールする機能とバージョン(2020年6月時点)
- zlib-1.2.11.tar.gz
- apr-1.7.0.tar.gz
- apr-util-1.6.1.tar.gz
- mysql80-community-release-el8-1.noarch.rpm (CentOS 8.1)
- mysql80-community-release-sl15-3.noarch.rpm (openSUSE 15.1)
- mysql-apt-config_0.8.15-1_all.deb (Ubuntu 20.04)
- oniguruma-devel-6.8.2-1.el8.x86_64.rpm (CentOS 8.1)
- httpd-2.4.43.tar.gz
- php-7.4.6.tar.gz

それ以外の必要なパッケージは、ディストリビューションの標準パッケージコマンド(dnfやaptなど)でインストールし、個別ダウンロードは不要です。

ダウンロードについては、公式サイトにアクセスして、そこからダウンロードしてFTPで転送するか、ダウンロードファイルのURLさえわかれば、wgetで入手することもできますが、入手方法は省略しています。

# 作業手順
## MySQL 8.0の導入
### MySQLのインストール
ディストリビューションの標準パッケージコマンドにあるMySQLは、バージョンが古いものや互換であるMariaDBであることが多いため、明示的なMySQLのバージョンを導入する場合は、MySQL公式からレポジトリをダウンロードする必要があります。

ダウンロードしたレポジトリを適用し、MySQLをインストールします。

```CentOS8.1
# cd [mysql80-community-release-el8-1.noarch.rpmの配置されているディレクトリ]
# dnf -y install mysql80-community-release-el8-1.noarch.rpm
# dnf -y install mysql-server
```

```openSUSE15.1
# cd [mysql80-community-release-sl15-3.noarch.rpmの配置されているディレクトリ]
# zypper install mysql80-community-release-sl15-3.noarch.rpm
…(中略)…
mysql80-community-release-sl15-3.noarch (RPM ファイルキャッシュ): 署名の検証に失敗しました [4-署名の公開鍵がありません]
中止(A)、再試行(R)、無視(I)？ [a/r/i] (a):  ←　「i」で続行する

# zypper install mysql-community-server
鍵を拒否しますか(R)? 今後ずっと信頼しますか(A)? [r/a/?] (r): 　←　「a」で続行する
(あとはインストールするか問われるので「y」を選択)
```

```Ubuntu20.04
# cd [mysql-apt-config_0.8.15-1_all.deb.debの配置されているディレクトリ]
# dpkg -i mysql-apt-config_0.8.15-1_all.deb

Which MySQL product do you wish to configure?
→「MySQL Server & Cluster」を選択

Which server version do you wish to receive?
→「mysql-8.0」を選択

Which MySQL product do you wish to configure? に戻る
→「Ok」を選択

# apt-get -y update　　←　アップデート後は再起動することが望ましい

# apt-get -y install mysql-client mysql-server
→ 途中MySQLのroot(Linuxのrootではない)のパスワードを設定する必要がある
```

なお、MySQL 8.0の場合**認証基盤がハッシュ値対応で、しかもデフォルトでハッシュ値ベースの認証基盤を有効化している**場合があります。しかし、これはPHP7でも対応していないらしく(7.4になった今でも対応している話は聞いていない)、PHPで平文パスワードで記載してアクセスすると認証がうまくできない。そのため、/etc/my.cnfを修正する必要がある(CentOS8.1の場合は/etc/my.cnfでインクルードしている/etc/my.cnf.d/mysql-default-authentication-plugin.cnfを編集する)。

またUbuntu 20.04では、MySQLのapt-getでのインストール中に、MySQLのroot(Linuxのrootではない)パスワードの設定と**「Select default authentication plugin」**というように認証基盤の選択ができるのでありがたい(✿´ ꒳ ` )

```openSUSE15.1
# vi /etc/my.cnf
```

```CentOS8.1
# vi /etc/my.cnf.d/mysql-default-authentication-plugin.cnf
```

```ini:my.cnfまたはmysql-default-authentication-plugin.cnf
default-authentication-plugin = mysql_native_password　←　先頭の「#」ははずす
```

```Ubuntu20.04の場合
[認証基盤を/etc/my.cnfの修正で行う必要はない。インストール時に選択する]
インストール中に「Select default authentication plugin」と問われる
→「Use Legacy Authentication Method」を選択する
```

### MySQLの起動

```CentOS8.1
# systemctl start mysqld
# systemctl enable mysqld
# systemctl status mysqld
```

```openSUSE15.1・Ubuntu20.04
# systemctl start mysql
# systemctl enable mysql
# systemctl status mysql
```

次に、MySQLの起動を行った後に、MySQLの初期パスワードを確認するため、MySQLのログを確認。なおUbuntu 20.04ではパスワード設定をあらかじめインストール時に実施したので、初期パスワードをログから確認する作業は不要。

```CentOS8.1
# less -r /var/log/mysql/mysqld.log
```

```openSUSE15.1
# less -r /var/log/mysql/mysqld.log
```

その際、**「[Note] A temporary password is generated for root@localhost:～」が存在すれば、初期パスワードが設定されている**。なければ設定されていない(CentOS 8.1のMySQL 8.0は設定されていない)。このパスワードを用いて、MySQLコマンドラインを実行する

```xml:Ubuntu20.04はこの手順は飛ばす
[MySQL初期パスワードなし]
# mysql -u root
[MySQL初期パスワードあり]
# mysql -u root -p　　←　初期パスワードを入力してログイン

mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY '<MySQLのrootパスワード(Linuxのrootパスワードではない)>';
Query OK, 0 rows affected (0.00 sec)

mysql> exit
```

ここで、MySQLの実際に運用想定のパスワードを設定する。そこで、rootというのは**Linuxのrootではなく、MySQLの中のrootである**ことに注意したい。

「Query OK, 0 rows affected (0.00 sec)」と出力されれば、MySQLのrootパスワードの設定が完了するので、exitで終了する。

では、設定後のMySQLのrootパスワードでログインを試す

```
# mysql -u root -p
Enter password: 　←　MySQLのrootパスワードを入力

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 12
Server version: 8.0.17 Source distribution

Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>
```

こうなればMySQLのrootログインが成功となる。または、以下でも確認できた

```
# mysqladmin ping -u root -p
Enter Password: 　←　MySQLのrootパスワードを入力
mysqld is alive
```

「mysqld is alive」となればOK

## MySQLを試す
### ユーザーとデータベースを作ってみる
MySQLが動いたということなので、実際ユーザーとデータベースを作ってみます。

- データベース名：manutest
- テストユーザー名：test
- テストユーザーパスワード：test0
- 文字コード：UTF-8

最初にMySQLのrootでログインします。

```
# mysql -u root -p
Enter password: 　←　MySQLのrootパスワードを入力

mysql> CREATE DATABASE manutest CHARACTER SET utf8;
↑ データベース「manutest」を文字コードUTF-8で作成する

mysql> SHOW DATABASES;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| manutest           |　　←　作成したデータベースが表示される
| mysql              |
| performance_schema |
| sys                |
+--------------------+
5 rows in set (0.00 sec)

mysql> CREATE USER 'test'@'localhost' IDENTIFIED BY 'test0';
↑ ユーザー名「test」をパスワード「test0」にて作成する

mysql> GRANT ALL ON manutest.* TO 'test'@'localhost';
↑ データベース「manutest」に対して、ユーザー「test」が全機能を使えるようにする
```

エラーがなく「Query OK, 0 rows affected」が表示されれば、データベースとユーザーは作成完了。

**パスワードポリシーエラーが発生する場合**

もしパスワードポリシーに反しているとエラーが発生してしまう場合は、ポリシーの状態を確認して、変更できるみたいです。ちなみにCentOS 8.1では存在せず、openSUSE 15.1では存在していました。

```
mysql> SHOW VARIABLES LIKE 'validate_password%';
↑ Empty setならポリシーは存在しないが、もし存在する場合は、初期では
　validate_password_lengthが8、validate_password.policyが「MEDIUM」

mysql> SET GLOBAL validate_password.length=4;
mysql> SET GLOBAL validate_password.policy=LOW;
↑ これで4文字以内のパスワード、試験目的で簡単なパスワードを設定できる

mysql> SET GLOBAL validate_password.length=8;
mysql> SET GLOBAL validate_password.policy=MEDIUM;
↑ 戻す場合
```

では、作成したテスト用のユーザーとデータベースで、MySQLを使えるか試します。

```
mysql> exit
Bye

# mysql -u test -p
Enter password:　　←　パスワード「test0」を入れる
(中略)

mysql> SHOW DATABASES;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| manutest           |　　←　作成したデータベースが表示されれば成功
+--------------------+
2 rows in set (0.00 sec)
```

### テーブルを作成してデータを入れてみる
では、作成したデータベースに、テーブルを作ってそこにデータを入れてみます！

今回は、このテーブルをテスト用に作成します。
テーブル名：**testtb**

||id|name|memo|
|:-:|:-:|:-:|:-:|
|型|INT|VARCHAR(64)|VARCHAR(256)|
|必須属性|PRIMARY KEY|NOT NULL|初期値NULL|
|その他属性|AUTO_INCREMENT|-|-|

```
mysql> USE manutest;
mysql> CREATE TABLE testtb (id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(64) NOT NULL, memo VARCHAR(256) DEFAULT NULL);
Query OK, 0 rows affected (0.11 sec)

mysql> SHOW TABLES;
+--------------------+
| Tables_in_manutest |
+--------------------+
| testtb             |
+--------------------+
1 row in set (0.00 sec)
```

テーブルの作成に成功したら、実際にデータを入れてみます。
テストテーブルtesttbは、idが自動採番で、nameが必須入力となるため、例として以下の値を挿入します。

- id：(自動採番のため省略)
- name：テスト
- memo：Only for test.

```
mysql> INSERT INTO testtb (name, memo) VALUES ('テスト', 'Only for test.');
mysql> SELECT * FROM testtb;
+----+-----------+----------------+
| id | name      | memo           |
+----+-----------+----------------+
|  1 | テスト    | Only for test. |
+----+-----------+----------------+
1 row in set (0.00 sec)
```

このように登録したデータが文字化けすることなくデータベースに登録されました♪
成功です！

## PHP上でMySQLを扱ってみる
MySQLのインストールも、動作も問題なく確認できたので、いよいよPHPからMySQLを扱ってみたいと思います。

MySQLは終了して、PHPページを作成します。

### PHPからMySQLの接続を確認

まずはPHPでPOSTにて受け取ったデータをMySQLデータベースへ登録する受信PHPを作成します！っとその前に、PHPでMySQLに接続できるかちゃんと確かめないといけません(\*˘ᗜ˘\*;)

接続確認ページを作ります

```
# cd ~
# vi connect.php
```

```php:connect.php
<?php
    $dsn = 'mysql:dbname=manutest; host=127.0.0.1';
    $usr = 'test';
    $pass = 'test0';
    
    try {
        $db = new PDO($dsn, $usr, $pass);
        print 'Connection successful.';
        $db = NULL;
    }
    catch(PDOException $e){
        die('Connect error : '.$e->getMessage());
    }
?>
```

これを、[Apache導入時](https://qiita.com/kazumi75kitty/items/daaf8ff8feb50ad04f41)にインストールした先のWebページの格納場所 /usr/local/apache2/htdocs/に移動しましょう！

```
# mv connect.php /usr/local/apache2/htdocs/
```

PHPページの配置なので、特にApacheを再起動することは不要なので、https://[LinuxサーバーのIPアドレス]/connect.php と入力して確認。今回はLinuxサーバーのIPアドレスは192.168.1.18なので、ブラウザでhttps:～の後に、192.168.1.18/connect.phpをURLを入力してアクセス。

![conn.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617906/eee25965-095b-eed6-488e-2c82b6327012.png "接続成功")
connect.phpでは接続成功すると上記のメッセージを出力するので、↑の画像は接続が成功したんです！！

### PHPからMySQLにデータを登録できるかを確認
続いて、いよいよデータが登録できるかを確認します。
再びWebページを作ります。

まずは登録フォームから。

```
# vi test_form.html
```

```html:test_form.html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>テストページ - Test page</title>
</head>
<body>
<form method="POST" action="test_form.php">
    <input type="text" name="name" />
    <input type="text" name="memo" />
    <input type="submit" value="Submit" />
</form>
</body>
</html>
```

続いて、フォームから受け取ったデータをMySQLデータベースへ追加するPHPを作成

```
# vi test_form.php
```

```php:test_form.php
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>テスト入力 - Test insert</title>
</head>
<body>
<?php
    function getDb(){
        $dsn = 'mysql:dbname=manutest; host=127.0.0.1';
        $usr = 'test';
        $pass = 'test0';
        
        try {
            $db = new PDO($dsn, $usr, $pass);
            $db->exec('SET NAMES utf8');
        }
        catch(PDOException $e){
            die('Connect error : '.$e->getMessage());
        }
        
        return $db;
    }
    
    try {
        $db = getDb();
        
        $stt = $db->prepare('INSERT INTO testtb (name, memo) VALUES (:one, :two)');
        
        $stt->bindValue(':one', $_POST['name']);
        $stt->bindValue(':two', $_POST['memo']);
        $stt->execute();
        
        print $_POST['name'].' - '.$_POST['memo'].' : Insert OK.';
        
        $stt = $db->query("SELECT * FROM testtb");
?>
    <table border="1">
<?php
        while($row = $stt->fetch(PDO::FETCH_ASSOC)){
?>
        <tr>
            <td><?php print $row['name']; ?></td>
            <td><?php print $row['memo']; ?></td>
        </tr>
<?php
        }
?>
   </table>
<?php
         $db = NULL;
   }
    catch(PDOException $e){
        die('Process error : '.$e->getMesssage());
    }
?>
</body>
</html>
```

上記2ファイルをApacheのWebページデータの場所へ。

```
# mv test_form.* /usr/local/apache2/htdocs/
```

では、ブラウザでhttps://[IPアドレス]/test_form.htmlにアクセスしてみます。

![reg1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617906/26cec13e-b9e6-7486-2961-940c83b18dbd.png "登録するデータ")
適当に左右のフォームに文字列を入れて…(なるべく日本語で)、Submitを押します

![reg2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617906/5e91ffd3-ccc0-6623-acac-21dd754e0e49.png "登録成功！！")
成功しました！入力されたデータがPHPにPOSTされて、MySQLのデータベースに登録されました。ではコマンドラインからも確認してみましょう。

PHPでは、manutestというデータベースに、ユーザーtestで接続しているので、コマンドからもtestでMySQLにログインします。

```
# mysql -u test -p
Enter password:　　← パスワード「test0」を入力

mysql> USE manutest;
mysql> SELECT * FROM testtb;
+----+-----------------+----------------+
| id | name            | memo           |
+----+-----------------+----------------+
|  1 | テスト          | Only for test. |
|  2 | パンケーキ      | 食べたい       |
+----+-----------------+----------------+
2 rows in set (0.00 sec)
```

ちゃんと追加されていました！！(\*˘ᗜ˘\*).｡.:\*♡

# あとがき
ApacheとPHP＋MySQLは、私の過去の実務経験からも、大企業までも頻繁にこのパターンで使われていました。逆にJavaは少なかったかな…と感じています。

PHPのほうがJavaの商用ライセンスよりも安価でオープンなので、大企業でも、もちろん中小個人でも導入しやすいのかな…って感じがします。

LinuxへのApache＋PHP＋MySQLに導入する費用としては、ライセンスがいらないので、あとは給料に対する労力の費用と、中古PCやラズパイ関係なく、装置の費用なのかな…と感じがします。

導入費用が1時間2500円であれば、Webサーバーとファイルサーバーを新規構築するのに2時間かければ、合計5000円。それに、中古PC or ラズパイとHDDやSSDの新品の組み合わせが3～40000円となれば、新規導入費用は5万円弱なので、高価なWindows Serverマシンを30万円かけて買うよりは、6分の1以下と、中小企業や個人経営程度でもITソリューションの潤いはやってくるのかな…と感じる。

特に、ラズパイなどIoTの場合は、センサーなどの物性分野とハイブリッドで組み合わせて、データ化できるのも期待されるので、今後はいろいろと視野を広げて、Qiitaに載せてみたいと思う( ˙꒳​˙ᐢ )

# 今後と次回
PHPによるWebアプリサーバー構築のほかにも、Java(OpenJDK)によるWebアプリサーバーについても、構築に触れてみたい