こんふまにあ

インターネットサーバの構築経験を生きた証として残すブログ

Nginx で実用的な WordPress サーバを構築するメモ

こんにちは。野又でございます。久々にブログをしたためることができそうなお仕事をさせてもらいました。

今回は、ずいぶん枯れてきているらしい LEMP(Linux, Nginx, MySQL, PHP-FPM) 構成の WordPress サーバをもっとラクに作れないか考えました。

CentOS 7.1 を Minimal Install (最小インストール)した状態から全てメモしました。各パッケージのバージョンを yum の base/epel リポジトリのみで管理して良い場合にはオススメできます。

共有型レンタルサーバ上での WordPress と比較して優位な部分

  • WordPress に特化したキャッシュ設定により、閲覧者に対する応答速度が劇的に向上
  • Apache + mod_php ではなく Nginx + PHP-FPM なので処理速度が少し向上

Nginx の設定で行ったこと

その他、行った事

  • vsftpd をインストールし、コンテンツのメンテナンスを FTP 経由で出来るようにする
  • アクセスログ、エラーログの logrotate 設定
  • MySQL データベースの定時バックアップ設定

構築メモ

▼ パッケージの更新

インストール直後なので必ず実施します。通信確認も兼ねています。

yum -y update
wget パッケージのインストール

最小インストールだと wget が無かったです。

yum -y install wget
▼ 時計合わせ

商用 VPS 契約ではきっと不要です。

yum -y install ntpdate
ntpdate ntp.nict.jp
SELinux の無効化

※作業の簡略化のために sed による修正を多用しますが、今後のバージョン次第で問題になる可能性があります。

setenforce 0
sed -i -e 's/^SELINUX=.*/SELINUX=disabled/' /etc/sysconfig/selinux
▼ EPEL (Extra Packages for Enterprise Linux) のインストール

Nginx をインストールするために epel リポジトリを有効にします。

yum -y install epel-release
yum clean all
▼ Nginx のインストール

epel リポジトリからインストールします。
細かい設定ファイルは本稿の後半で行います。

yum -y install nginx
mkdir -p /var/cache/nginx/cache
chown -R nginx:nginx /var/cache/nginx
chown -R nginx:nginx /usr/share/nginx
systemctl start nginx.service
systemctl enable nginx.service
PHP FastCGI Process Manager (PHP-FPM) のインストール

起動ユーザを nginx に統一し UNIX ドメインソケット経由での接続のみ受けられるようにします。

yum -y install php php-fpm httpd-tools
sed -i -e 's/^;*user *=.*$/user = nginx/' /etc/php-fpm.d/www.conf
sed -i -e 's/^;*group *=.*$/group = nginx/' /etc/php-fpm.d/www.conf
sed -i -e 's:^;*listen *=.*$:listen = /run/php-fpm/php-fpm.sock:' /etc/php-fpm.d/www.conf
sed -i -e 's/^;*listen\.owner *=.*$/listen.owner = nginx/' /etc/php-fpm.d/www.conf
sed -i -e 's/^;*listen\.group *=.*$/listen.group = nginx/' /etc/php-fpm.d/www.conf
sed -i -e 's/^;*listen\.mode *=.*$/listen.mode = 0660/' /etc/php-fpm.d/www.conf
chown -R nginx:nginx /var/lib/php/session/
systemctl start php-fpm.service
systemctl enable php-fpm.service

なお yum における依存関係のせいで Apache がこのタイミングで入ってしまいます。うっかり起動しないように mask しておきます。

systemctl mask httpd.service
phpMyAdmin のインストール

phpMyAdmin は base リポジトリからのインストールになります。このインストールで php 関連モジュールがさらに追加されるため PHP-FPM を再起動しておきます。

yum -y install phpmyadmin
chown -R nginx:nginx /usr/share/phpMyAdmin
systemctl restart php-fpm.service
MySQL(MariaDB) のインストール

MySQL を使うため MariaDB をインストールします。
本稿では my.cnf 関連のチューニングは行いませんので必要に応じて修正して下さい。

yum -y install mariadb mariadb-server
systemctl start mariadb.service
systemctl enable mariadb.service
MySQL(MariaDB) の初期設定

MySQL起動してから実行します。

mysql_secure_installation

root パスを決めたらあとは [Y] か [Enter] で進めます。

Enter current password for root (enter for none): [Enter]
Set root password? [Y/n] Y
New password: ********
Re-enter new password: ********
Remove anonymous users? [Y/n] Y
Disallow root login remotely? [Y/n] Y
Remove test database and access to it? [Y/n] Y
Reload privilege tables now? [Y/n] Y
WordPress 用 DB と phpMyAdmin 用 DB を作成

wordpress のデータベース名を wordpress にしていますが、もっと別な名前が望ましいです。
また、接続用ユーザーの名称は admin 以外が望ましいです。

mysql -uroot -Dmysql -p******** -e "INSERT INTO user SET user='admin', password=password('@@@@@@@@'), host='localhost';"
mysql -uroot -Dmysql -p******** -e "CREATE DATABASE wordpress DEFAULT CHARACTER SET utf8;"
mysql -uroot -Dmysql -p******** -e "GRANT ALL ON wordpress.* to admin;"
mysql -uroot -Dmysql -p******** -e "source /usr/share/phpMyAdmin/sql/create_tables.sql"
mysql -uroot -Dmysql -p******** -e "GRANT ALL ON phpmyadmin.* to admin;"
mysql -uroot -Dmysql -p******** -e "FLUSH PRIVILEGES;"
FTP サーバのインストール

セキュリティ的には宜しくないのですが、本稿ではログインユーザ名を nginx にして作業を簡略化しています。

yum -y install vsftpd
echo "nginx" > /etc/vsftpd/chroot_list
echo "nginx" > /etc/vsftpd/user_list
mkdir -p /etc/vsftpd/user_conf
echo "local_root=/var/www"  > /etc/vsftpd/user_conf/nginx
sed -i -e 's/^#*anonymous_enable=.*/anonymous_enable=NO/' /etc/vsftpd/vsftpd.conf
sed -i -e 's/^#*chown_uploads=.*/chown_uploads=YES/' /etc/vsftpd/vsftpd.conf
sed -i -e 's/^#*chown_username=.*/chown_username=nginx/' /etc/vsftpd/vsftpd.conf
sed -i -e 's/^#*chroot_local_user=.*/chroot_local_user=NO/' /etc/vsftpd/vsftpd.conf
sed -i -e 's/^#*chroot_list_enable=.*/chroot_list_enable=YES/' /etc/vsftpd/vsftpd.conf
sed -i -e 's:^#*chroot_list_file=.*:chroot_list_file=/etc/vsftpd/chroot_list:' /etc/vsftpd/vsftpd.conf
echo "userlist_deny=NO" >> /etc/vsftpd/vsftpd.conf
echo "user_config_dir=/etc/vsftpd/user_conf" >> /etc/vsftpd/vsftpd.conf
systemctl start vsftpd.service
systemctl enable vsftpd.service

パスワードを与えれば FTP クライアントからログインできるようになります。

passwd nginx
New password: ********
Retype new password: ********
WordPress のダウンロードと展開

epel にあるからと yum で入れると英語版が入ってしまうらしいです。 WordPress 自身がアップデート機能を持っているため yum で管理する必要は特にありません。よって本家から日本語版をダウンロードして展開することにします。

wget http://ja.wordpress.org/latest-ja.tar.gz -P /var/www
tar xvzf /var/www/latest-ja.tar.gz -C /var/www
chown -R nginx:nginx /var/www/wordpress
mkdir -p /var/www/logs
chown nginx:nginx /var/www/logs
mkdir -p /var/www/fastcgi_cache
chown nginx:nginx /var/www/fastcgi_cache
▼ /etc/nginx/nginx.conf の編集

今回は以下のような設定にしてみました。極端なパラメータがあるかもしれません。

vi /etc/nginx/nginx.conf

修正した部分を示します。

# For more information on configuration, see:
#   * Official English Documentation: http://nginx.org/en/docs/
#   * Official Russian Documentation: http://nginx.org/ru/docs/

user nginx nginx;
worker_processes auto;
worker_rlimit_nofile 131072;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

events {
    worker_connections 2048;
    use epoll;
    multi_accept on;
}

http {
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for" $request_time';

    access_log  /var/log/nginx/access.log  main;

    server_tokens       off;
    sendfile            on;
    tcp_nopush          on;
    tcp_nodelay         on;
    keepalive_timeout   20;
    client_header_timeout 20;
    client_body_timeout 20;
    reset_timedout_connection on;
    types_hash_max_size 2048;
    server_names_hash_bucket_size 128;

    include             /etc/nginx/mime.types;
    default_type        application/octet-stream;

    #upstream
    upstream php-fpm {
        server unix:/run/php-fpm/php-fpm.sock;
    }

    #buffers
    client_body_buffer_size 10K;
    client_header_buffer_size 1k;
    client_max_body_size 8m;
    large_client_header_buffers 2 1k;

    #gzip
    gzip_static on;
    gzip on;
    gzip_http_version 1.0;
    gzip_vary on;
    gzip_comp_level 1;
    gzip_types text/plain
               text/css
               text/xml
               text/javascript
               application/json
               application/javascript
               application/x-javascript
               application/xml
               application/xml+rss;
    gzip_disable "MSIE [1-6]\.";
    gzip_disable "Mozilla/4";
    gzip_buffers 4 32k;
    gzip_min_length 1100;
    gzip_proxied off;

    #open_file_cache
    open_file_cache max=1000 inactive=20s;
    open_file_cache_valid 30s;
    open_file_cache_min_uses 2;
    open_file_cache_errors on;

    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    if_modified_since before;

    fastcgi_cache_path /var/www/fastcgi_cache levels=1:2 keys_zone=whole:64m max_size=1g inactive=1d;

    # Load modular configuration files from the /etc/nginx/conf.d directory.
    # See http://nginx.org/en/docs/ngx_core_module.html#include
    # for more information.
    include /etc/nginx/conf.d/*.conf;

#   server {
#       listen       80 default_server;
#       listen       [::]:80 default_server;
#       server_name  _;
#       root         /usr/share/nginx/html;
#
#       # Load configuration files for the default server block.
#       include /etc/nginx/default.d/*.conf;
#
#       location / {
#       }
#
#       error_page 404 /404.html;
#           location = /40x.html {
#       }
#
#       error_page 500 502 503 504 /50x.html;
#           location = /50x.html {
#       }
#   }
}
▼ /etc/nginx/conf.d/phpMyAdmin.conf の新規作成

phpMyAdmin を任意のポート(本稿では 53306 番ポート)で動作させるようにします。

touch /etc/nginx/conf.d/phpMyAdmin.conf
vi /etc/nginx/conf.d/phpMyAdmin.conf

追加した内容。

server {
    root         /usr/share/phpMyAdmin;
    listen       53306;
    server_name  _;
    access_log   /var/log/nginx/phpMyAdmin.access.log;
    error_log    /var/log/nginx/phpMyAdmin.error.log;
    location / {
        index       index.php;
    }
    location ~ \.php$ {
        auth_basic              "Authentication";
        auth_basic_user_file    /var/www/.htpasswd;
        include fastcgi_params;
        fastcgi_index           index.php;
        fastcgi_pass            php-fpm;
        fastcgi_cache_bypass    1;
        fastcgi_no_cache        1;
        fastcgi_param           SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param           PATH_INFO $fastcgi_script_name;
    }
}
▼ /etc/nginx/conf.d/WordPress.conf の新規作成

wordpress を 80 番ポートで使用します。本稿ではドメイン直下にインストールしています。

touch /etc/nginx/conf.d/WordPress.conf
vi /etc/nginx/conf.d/WordPress.conf

追加した内容。

server {
    listen       80;
    server_name  _;
    root         /var/www/wordpress;
    access_log   /var/www/logs/WordPress.access.log main;
    error_log    /var/www/logs/WordPress.error.log warn;

    rewrite /wp-admin$ $scheme://$host$uri/ permanent;

    location / {
        index  index.php;
        try_files $uri $uri/ /index.php?q=$uri&$args;
        port_in_redirect off;
        if (-f $request_filename) { break; }
        set $supercache_dir  /wp-content/cache/supercache/;
        set $supercache_file $document_root$supercache_dir${http_host}${uri}/index.html;
        set $supercache_uri '';
        set $do_not_cache '';
        if (-f $supercache_file) {
            set $supercache_uri $supercache_dir${http_host}${uri}/index.html;
        }
        if ($query_string ~ .*=.*) {
            set $supercache_uri '';
        }
        if ($request_method != "GET") {
            set $supercache_uri '';
            set $do_not_cache 1;
        }
        if ($http_cookie ~* "comment_author_|wordpress_(?!test_cookie)|wp-postpass_" ) {
            set $supercache_uri '';
            set $do_not_cache 1;
        }
        if ($http_x_wap_profile ~ ^[a-z0-9\"]+) {
            set $supercache_uri '';
            set $do_not_cache 1;
        }
        if ($http_profile ~ ^[a-z0-9\"]+) {
            set $supercache_uri '';
            set $do_not_cache 1;
        }

        set $mobile '@p';
        if ($http_user_agent ~* '(Android|iPad|Kindle|Silk)') {
            set $supercache_uri '';
            set $mobile "@t";
        }
        if ($http_user_agent ~* '(iPhone|iPod|incognito|webmate|Android.*Mobile|dream|CUPCAKE|froyo|BlackBerry|webOS|s8000|bada|IEMobile|Googlebot\-Mobile|AdsBot\-Google)') {
            set $supercache_uri '';
            set $mobile "@s";
        }
        if ($http_user_agent ~* '(DoCoMo|J-PHONE|Vodafone|MOT-|UP\.Browser|DDIPOCKET|ASTEL|PDXGW|Palmscape|Xiino|sharp pda browser|Windows CE|L-mode|WILLCOM|SoftBank|Semulator|Vemulator|J-EMULATOR|emobile|-mobile-converter|PSP)') {
            set $supercache_uri '';
            set $mobile "@k";
        }
    }
    location = /favicon.ico {
        log_not_found off;
        access_log off;
    }
    location = /robots.txt {
        log_not_found off;
        access_log off;
    }
    location ~* /\. {
        deny all;
    }
    location = /wp-config.php {
        deny all;
    }
    location = /wp-admin/admin-ajax.php {
        include fastcgi_params;
        fastcgi_pass            php-fpm;
        fastcgi_cache_bypass    1;
        fastcgi_no_cache        1;
        fastcgi_param           SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param           PATH_INFO $fastcgi_script_name;
    }
    location ~ /(wp-login|install|setup-config|wp-admin/.*)\.php$ {
        auth_basic              "Authentication";
        auth_basic_user_file    /var/www/.htpasswd;
        client_max_body_size    1024M;
        include fastcgi_params; 
        fastcgi_pass            php-fpm;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_cache_bypass    1;
        fastcgi_no_cache        1;
        fastcgi_param           SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param           PATH_INFO $fastcgi_script_name;
        fastcgi_param           PHP_VALUE "upload_max_filesize=1000M";
        fastcgi_param           PHP_VALUE "post_max_size=1G"; 
    }
    location ~* \.(jpg|jpeg|png|gif|swf|bmp|ico)$ {
        log_not_found off;
        access_log off;
        expires max;
        break;
    }
    location ~* \.(txt|html|html|css|js)$ {
        log_not_found off;
        access_log off;
        expires 1d;
        break;
    }
    location ~* \.php {
	try_files $uri =404;
        include fastcgi_params;
        fastcgi_pass            php-fpm;
        fastcgi_cache           whole;
        fastcgi_cache_key       "$scheme://$host$request_uri$mobile";
        fastcgi_cache_valid     200 30m;
        fastcgi_cache_valid     any 1m;
        fastcgi_ignore_headers  Cache-Control Expires;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_cache_bypass    $do_not_cache;
        fastcgi_no_cache        $do_not_cache;
        fastcgi_param           SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param           PATH_INFO $fastcgi_script_name;
    }
}
BASIC 認証用 ID/PASS の作成

※ここでは ID に guest を用いますが、もちろん guest じゃないほうがいいです。

touch /var/www/.htpasswd
chown nginx:nginx /var/www/.htpasswd
htpasswd /var/www/.htpasswd guest
New password: ********
Re-type new password: ********
▼ Nginx の記述確認と再起動

確認コマンドを実行します。問題なければ syntax is ok が出力されます。

nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

サービスを再起動します。

systemctl restart nginx.service
systemctl restart php-fpm.service
▼ firewalld を使わず iptables にする

各自のポリシー次第ですが、自分は iptables を使うようにしました。

yum -y install iptables-services
▼ 各種ポートの開放 (iptables)

FTP(20, 21) SSH(22) Web(80) phpMyAdmin(53306) を開けます。
固定 IP からの管理環境であれば 80/tcp 以外はできるだけ IP アドレスを用いて厳しく制限したいです。

vi /etc/sysconfig/iptables

その内容。

*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 20 -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 21 -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 80 -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 53306 -j ACCEPT
-A INPUT -j DROP
-A FORWARD -j DROP
COMMIT

起動します。

systemctl stop firewalld.service
systemctl mask firewalld.service
systemctl start iptables.service
systemctl enable iptables.service
▼ ログローテーション設定の追記 (logrotate)

ログの保存場所を追加したので、ログローテーションの設定も追記する必要があります。

vi /etc/logrotate.d/nginx

その内容。

/var/log/nginx/*log {
    create 0644 nginx nginx
    daily
    rotate 10
    missingok
    notifempty
    compress
    sharedscripts
    postrotate
        /bin/kill -USR1 `cat /run/nginx.pid 2>/dev/null` 2>/dev/null || true
    endscript
}

/var/www/logs/*log {
    create 0644 nginx nginx
    daily
    rotate 10
    missingok
    notifempty
    compress
    sharedscripts
    postrotate
        /bin/kill -USR1 `cat /run/nginx.pid 2>/dev/null` 2>/dev/null || true
    endscript
}
MySQL データベースの定時バックアップ (mysqldump)

シェルスクリプトを作成し crontab 依存で定期的に dump をとります。

touch /etc/cron.daily/mysql_backup.cron
chmod 755 /etc/cron.daily/mysql_backup.cron
vi /etc/cron.daily/mysql_backup.cron

その内容。

#!/bin/sh
mysql_dump_dir=/var/www/mysql_backup/
mysql_dump_file=`date +%Y%m%d%H%M%S`.sql.gz
mkdir -p ${mysql_dump_dir}
/usr/bin/mysqldump -uroot -p******** -x --all-databases --lock-all-tables --hex-blob | gzip > ${mysql_dump_dir}${mysql_dump_file}
chmod 660 ${mysql_dump_dir}${mysql_dump_file}
chown nginx:nginx ${mysql_dump_dir}${mysql_dump_file}
find ${mysql_dump_dir} -mtime +15 -name "*.sql.gz" | xargs rm -f

いちど手動でキックしてみて、指定したディレクトリにダンプファイルが生成されているか確認します。

/etc/cron.daily/mysql_backup.cron

動作確認

ブラウザから以下にアクセスします(サーバの IP アドレスが ip.add.re.ss だとします)。
BASIC 認証が必要になっているはずです。

  • http://ip.add.re.ss:53306/
  • http://ip.add.re.ss/wp-admin/setup-config.php

以上となります。