CentOS7にapache2.4とphp7.2(RC)とmysql8.0.3(RC)をインストールしてhttp/2で通信する


# おことわり
この記事は、あんまよくわかってないことにチャレンジしてみた結果の備忘録です。
あまりよくないやり方とか、間違ってる内容が含まれる可能性がありますので、参考にされる際はご注意ください。:bow_tone1:
※むしろ、ここはこうしたほうがいいよとかそういう指摘ございましたら是非

また、作業をある程度自動化できるように、シェルコマンドの羅列には行の最後に && \ を追記しています。
シェルスクリプトにまとめるか、Dockerfileにしちゃえばって話な気もするけど、それはまた別途。

# モチベーション

- 新しい技術に触れてみたい
- 普段yumでしかやらないので、configureとかmakeとかよくわかってない
- 台風

# 環境

- OS:CentOS7(CentOS Linux release 7.3.1611 (Core))の上にたてたCentOS7(CentOS Linux release 7.4.1708 (Core))

```bash
# docker images | grep "centos"
centos              latest              196e0ce0c9fb        6 weeks ago         197MB
```

以下のシェルスクリプトを実行すると、上のcentosイメージを使ってランダムな名前を付けた使い捨てのCentOSコンテナを建てて、そこにシェルログインする。このコンテナ上でインストールや動作確認などを実施。

```centos.sh
#!/bin/bash

CONTAINER_NAME=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 8 | head -n1)

docker run --privileged --name ${CONTAINER_NAME} -d centos /sbin/init
docker exec -ti ${CONTAINER_NAME} bash
```

# 最終的に手動でインストールしたもの、バージョン
インストールする順番に上から並べる。

| 名前 | バージョン | prefix | DLページ | 備考 |
|:---------:|:---------:|:---------:|:---------:|:---------|
| OpenSSL | 1.0.2l | /opt/openssl | https://www.openssl.org/source/ | 言わずもがな、オープンソースのSSL通信用ライブラリ |
| nghttp2 | 1.27.0 | /opt/nghttp2 | https://github.com/nghttp2/nghttp2/releases/tag/v1.27.0 | http2のライブラリ。apacheでhttp/2で通信するために利用する。 |
| curl | 7.56.1 | /opt/curl7-56 | https://curl.haxx.se/download.html | http/2での通信の動作確認に使う。CentOSにデフォルトでインストールされているバージョン(7.29.0)だとhttp/2のエクステンションが使えないので、最新をインストールしなおす。 |
| apr | 1.6.3 | /opt/apr | https://apr.apache.org/download.cgi | apacheをインストールするのに使う。ApachePortableRuntime |
| apr-util | 1.6.1 | /opt/apr-util | https://apr.apache.org/download.cgi | 上同 |
| apache | 2.4.29 | /opt/httpd24 | https://httpd.apache.org/download.cgi#apache24 | apacheの最新。http/2で通信する(mod_http2を有効にする)には、2.4.14以降が必要 |
| mysql | 8.0.3-0.1.rc | /var/lib/mysql | https://dev.mysql.com/downloads/mysql/8.0.html | mysqlの最新。どうもRHEL向けにはrpmしか配ってないようなので、rpmを使う。 |
| php | 7.2.0RC4 | /opt/php7 | https://downloads.php.net/~pollita/ | phpの最新。10/26時点でRC5が最新。11/30に[GA予定](https://wiki.php.net/todo/php72)。 |

上記以外でインストールに必要なものは、yumでがつっとやる。

# 手順
dockerイメージを建ててシェルログイン後から。
なお、インストール以外に必要な作業は今回は省く(時刻設定やfirewallなど)。
また、すべての作業はrootで行われる。

## yumupdate && 必要なものをyumインストール
```bash
yum -y update && \
yum -y upgrade && \
yum -y install epel-release gcc gcc-c++ make cmake autoconf git telnet wget pcre which lsof libaio net-tools numactl && \
yum -y install zlib-devel bzip2-devel libcurl-devel libxml2-devel expat-devel pcre-devel ncurses-devel 
```
※最終的に不必要なものとか、動作確認に使うものとかも含まれてる

## /opt配下にprefixとして利用するディレクトリを作成する
```bash
cd /opt && \
mkdir httpd24 apr apr-util openssl php7 curl7-56
```
※mysqlはrpmインストールでデフォルトに設定されている/var/lib/配下にインストールされる

## ソースを持ってきて展開する
```bash
cd /usr/local/src && \
wget http://ftp.riken.jp/net/apache//httpd/httpd-2.4.29.tar.gz && \
wget http://ftp.riken.jp/net/apache//apr/apr-1.6.3.tar.gz && \
wget http://ftp.riken.jp/net/apache//apr/apr-util-1.6.1.tar.gz && \
wget https://github.com/nghttp2/nghttp2/releases/download/v1.27.0/nghttp2-1.27.0.tar.gz && \
wget https://www.openssl.org/source/old/1.0.2/openssl-1.0.2l.tar.gz && \
wget https://curl.haxx.se/download/curl-7.56.1.tar.gz && \
wget https://downloads.php.net/~remi/php-7.2.0RC4.tar.gz && \
for TARGET in $(ls) ; do tar zxvf $TARGET ; done && \
chown -R root:root /usr/local/src/*
```

## OpenSSLをインストールし、パスを通す。
```bash
cd openssl-1.0.2l && \
./config \
--prefix=/opt/openssl \
shared zlib && \
make && \
make install && \
echo /opt/openssl/lib > /etc/ld.so.conf.d/openssl102l.conf && \
ldconfig && \
ln -s /opt/openssl/bin/* /usr/local/bin/ && \
cd ..
```

確認

```bash
# openssl version
OpenSSL 1.0.2l  25 May 2017

# ldconfig -p | grep "openssl"
        libssl.so.1.0.0 (libc6,x86-64) => /opt/openssl/lib/libssl.so.1.0.0
        libssl.so (libc6,x86-64) => /opt/openssl/lib/libssl.so
        libcrypto.so.1.0.0 (libc6,x86-64) => /opt/openssl/lib/libcrypto.so.1.0.0
        libcrypto.so (libc6,x86-64) => /opt/openssl/lib/libcrypto.so

```

## nghttp2をインストールし、パスを通す

```bash
env OPENSSL_CFLAGS="-I/opt/openssl/include" OPENSSL_LIBS="-L/opt/openssl/lib -lssl -lcrypto" && \
./configure \
--prefix=/opt/nghttp2 && \
make && \
make install && \
echo /opt/nghttp2/lib > /etc/ld.so.conf.d/nghttp2.conf && \
ldconfig
cd ..
```

確認

```bash
# ldconfig -p | grep "nghttp"
        libnghttp2.so.14 (libc6,x86-64) => /opt/nghttp2/lib/libnghttp2.so.14
        libnghttp2.so (libc6,x86-64) => /opt/nghttp2/lib/libnghttp2.so
```

## curl-7.56.1をhttp2エクステンションを追加してインストールし、curl7-56コマンドで実行できるようにする。
```bash
cd curl7-56 && \
./configure \
--prefix=/opt/curl7-56 \
--with-ssl=/opt/openssl \
--with-nghttp2=/opt/nghttp2 && \
make && \
make install && \
ln -s /opt/curl7-56/bin/curl /usr/local/bin/curl7-56 && \
ln -s /opt/curl7-56/bin/curl-config /usr/local/bin/curl-config-7-56 && \
cd ..
```

確認

```bash
# curl --version
curl 7.29.0 (x86_64-redhat-linux-gnu) libcurl/7.29.0 NSS/3.28.4 zlib/1.2.7 libidn/1.28 libssh2/1.4.3
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp scp sftp smtp smtps telnet tftp
Features: AsynchDNS GSS-Negotiate IDN IPv6 Largefile NTLM NTLM_WB SSL libz unix-sockets

# curl7-56 --version
curl 7.56.1 (x86_64-pc-linux-gnu) libcurl/7.56.1 OpenSSL/1.0.2l zlib/1.2.7 nghttp2/1.27.0
Release-Date: 2017-10-23
Protocols: dict file ftp ftps gopher http https imap imaps pop3 pop3s rtsp smb smbs smtp smtps telnet tftp
Features: AsynchDNS IPv6 Largefile NTLM NTLM_WB SSL libz TLS-SRP HTTP2 UnixSockets HTTPS-proxy
```
curl7-56のほうはnghttp2がリンクされており、HTTP2が使えることが確認できる。またOpenSSLも自分でインストールしたものがリンクされている。

## apacheをインストールするためのaprとapr-utilをインストールする。
```bash
cd apr-1.6.3 && \
./configure --prefix=/opt/apr && \
make && \
make install && \
cd ..
```

```bash
cd apr-util-1.6.1 && \
./configure \
--prefix=/opt/apr-util \
--with-apr=/opt/apr && \
make && \
make install && \
cd ..
```

## apacheをインストールし、http/2で通信できることを確認する。
※configureのオプションについては、お好みで追加など

### インストール
```bash
cd httpd-2.4.29 && \
./configure \
--prefix=/opt/httpd24 \
--enable-http2 \
--with-nghttp2=/opt/nghttp2 \
--enable-ssl \
--with-ssl=/opt/openssl \
--with-apr=/opt/apr \
--with-apr-util=/opt/apr-util \
--enable-so \
--with-pcre=builtin \
--with-expat=builtin \
--enable-mpms-shared=all && \
make && \
make install && \
ln -s /opt/httpd24/bin/* /usr/local/bin/ && \
cd ..
```

### SSL通信をするために自己証明書を作成
http/2はSSL通信が必須。Let'sEncryptとかを試してもいいけど、今回は自己証明書で。
※curlは自己証明書を使ってるSSL通信に対して警告を吐く。無視する場合は -k オプションを使う。

```bash
cd /opt/httpd24
mkdir cert && cd cert
openssl genrsa 2048 > server.key
openssl req -new -key server.key > server.csr  #commonnameは適当に、それ以外は空でおｋ
openssl x509 -days 3650 -req -signkey server.key < server.csr > server.crt
chmod 400 server.*
```
※openssl req は対話的にやる必要があります、注意

### config修正
以下の部分を修正する。

#### httpd.conf

```bash

cd /opt/httpd24/conf && \
mkdir .backup && \
cp -pri httpd.conf .backup/httpd.conf.org && \
vi httpd.conf
===
#LoadModule socache_shmcb_module modules/mod_socache_shmcb.so
#LoadModule http2_module modules/mod_http2.so
#LoadModule ssl_module modules/mod_ssl.so
↓
LoadModule socache_shmcb_module modules/mod_socache_shmcb.so
LoadModule http2_module modules/mod_http2.so
LoadModule ssl_module modules/mod_ssl.so
===

===
#ServerName www.example.com:80
↓
ServerName www.example.com:80
===
#localhostでよかったかも、自己証明書のcommon nameと合わせておいたほうが、何かとよいかもしれない？

===
#Include conf/extra/httpd-ssl.conf
↓
Include conf/extra/httpd-ssl.conf
===
```

#### httpd-ssl.conf

```bash
cd extra && \
mkdir .backup && \
cp -pri httpd-ssl.conf .backup/httpd-ssl.conf.org && \
vi httpd-ssl.conf

===
SSLCipherSuite HIGH:MEDIUM:!MD5:!RC4
↓
SSLCipherSuite ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK
===

===
SSLProtocol all -SSLv3
↓
SSLProtocol -All +TLSv1 +TLSv1.1 +TLSv1.2
===

デフォルトで書いてある、
<VirtualHost _default_:443>
~
</VirtualHost>
をすべてコメントアウト
以下を追記。

<VirtualHost *:443>

  <IfModule http2_module>
    ProtocolsHonorOrder On
    Protocols h2 http/1.1
  </IfModule>

  ServerName www.example.com:443
  DocumentRoot "/opt/httpd24/ssl_htdocs/"

  SSLEngine on
  SSLCertificateFile "/opt/httpd24/cert/server.crt"
  SSLCertificateKeyFile "/opt/httpd24/cert/server.key"

  <Directory "/opt/httpd24/ssl_htdocs/">
    Options +Indexes +Includes +FollowSymLinks
    AllowOverride All
    Require all granted
  </Directory>

  BrowserMatch "MSIE [2-5]" \
     nokeepalive ssl-unclean-shutdown \
     downgrade-1.0 force-response-1.0

  SetEnvIf    Request_URI "\.(gif|jpg|png|css|js)$" nolog
  ErrorLog    /var/log/error_log
  CustomLog   /var/log/access_log combined env=!nolog

  Header add Strict-Transport-Security "max-age=15552000"

  CustomLog "/opt/httpd24/logs/ssl_request_log" \
         "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"

</VirtualHost>

```

ドキュメントルートを作成

```bash
mkdir /opt/httpd24/ssl_htdocs
```

#### 起動

```bash

# apachectl -t
Syntax OK

# apachectl #apache起動。停止する場合は、 apachectl stop

# lsof -i:80
COMMAND   PID   USER   FD   TYPE    DEVICE SIZE/OFF NODE NAME
httpd   13919   root    3u  IPv4 248445154      0t0  TCP *:http (LISTEN)
httpd   13920 daemon    3u  IPv4 248445154      0t0  TCP *:http (LISTEN)
httpd   13921 daemon    3u  IPv4 248445154      0t0  TCP *:http (LISTEN)
httpd   13922 daemon    3u  IPv4 248445154      0t0  TCP *:http (LISTEN)

# lsof -i:443
COMMAND   PID   USER   FD   TYPE    DEVICE SIZE/OFF NODE NAME
httpd   13919   root    4u  IPv4 248445160      0t0  TCP *:https (LISTEN)
httpd   13920 daemon    4u  IPv4 248445160      0t0  TCP *:https (LISTEN)
httpd   13921 daemon    4u  IPv4 248445160      0t0  TCP *:https (LISTEN)
httpd   13922 daemon    4u  IPv4 248445160      0t0  TCP *:https (LISTEN)
```
止めるときは、apachectl stop 
※systemctlに登録するとかは、別で。いったん動けばおｋ

### 動作確認

```bash
# 動作確認用のファイルを作成
echo "test.html 80" > /opt/httpd24/htdocs/test.html
echo "test.html 443" > /opt/httpd24/ssl_htdocs/test.html

curl http://localhost/test.html
 => test.html 80 を出力

curl https://localhost/test.html -k
 => test.html 443 を出力

curl7-56 https://localhost/test.html -k
 => test.html 443 を出力

# ログ確認
tail /opt/httpd24/logs/ssl_request_log
[29/Oct/2017:12:42:31 +0000] 127.0.0.1 TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256 "GET /test.html HTTP/1.1" 14
[29/Oct/2017:12:43:19 +0000] 127.0.0.1 TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256 "GET /test.html HTTP/2.0" 14
```
上記ログで、http/1.1 http/2 それぞれで通信できたことを確認できた。
※時刻が気になる場合はちゃんと設定しておきましょう。

## PHP7.2をインストール
※configureのオプションはお好みで足してください

### インストール

```bash
cd /usr/local/src/php-7.2.0RC4 && \
./configure \
--prefix=/opt/php7 \
--with-apxs2=/opt/httpd24/bin/apxs \
--enable-mysqlnd \
--with-pdo-mysql=mysqlnd \
--with-curl=/opt/curl7-56 \
--with-openssl=/opt/openssl
make && \
make install && \
cd ..
```

### 確認

```bash
# 確認用ファイル作成
echo "<?php phpinfo(); ?>" >/opt/httpd24/ssl_htdocs/phpinfo.php

# curlで接続
curl7-56 https://localhost/phpinfo.php -k
 => phpinfoがどばどばーっとでてくる。ちゃんと生成されてるっぽいのでまぁいっかと思ったけど、念のため内容をファイルにリダイレクトしローカルに持ってきてブラウザから確認。php7.2(RC)がインストールされて動いてることが確認できた。

# log
tail /opt/httpd24/logs/ssl_request_log -n 1
[29/Oct/2017:12:47:57 +0000] 127.0.0.1 TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256 "GET /phpinfo.php HTTP/2.0" 80510
```

## MySQLインストール
MySQLだけ、rpmで配布されているものを利用。
※mysqlのソースインストールでググると、ソースからcmakeでインストールしてる記事が大量に出てくるんだけど今回はrpm利用。

[公式DLページ](https://dev.mysql.com/downloads/mysql/8.0.html)から自分の環境にあったものを選ぶ。
今回は「Red Hat Enterprise Linux 7 / Oracle Linux 7 (x86, 64-bit), RPM Bundle」をもってきて、必要なものをrpmで入れる。

```bash
mkdir /usr/local/src/mysql8 && \
wget https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-8.0.3-0.1.rc.el7.x86_64.rpm-bundle.tar && \
tar xvf mysql-8.0.3-0.1.rc.el7.x86_64.rpm-bundle.tar && \
rpm -Uvh mysql-community-server-8.0.3-0.1.rc.el7.x86_64.rpm mysql-community-client-8.0.3-0.1.rc.el7.x86_64.rpm mysql-community-common-8.0.3-0.1.rc.el7.x86_64.rpm mysql-community-libs-8.0.3-0.1.rc.el7.x86_64.rpm
```
で、とりあえず最低限必要なものはOK.

## 起動、動作確認

```bash
systemctl start mysqld.service
 => A temporary password is generatedのメッセージがでるので、初期パスワードを確認する。mysqld.logに書いてある。

mysql -uroot -p
Enter password: => ログに出力された初期パスワードを入力。

mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY '[パスワード]'; 
 => rootのパスワードを変更。以後自由。
```

以上で、CentOS7上に最新のapache+php+mysqlの構成、および動作確認までができました。

### 補足
何回も試行錯誤してコンテナぽこじゃが建ててるとどんどんゴミコンテナがたまってくので、以下のシェルスクリプトで一斉お掃除。

```bash:remove-container-all.sh
#!/bin/bash
docker stop $(docker ps -aq)
docker rm $(docker ps -aq)
```