バージョン(2023年8月4日現在)

OSAmazon Linux 2023
PHP8.2.7
Nginxnginx/1.24.0

AWSでEC2インスタンス起動

AWSコンソールにログインし、EC2インスタンス起動します。
今回はAmazon Linux 2023をチョイスしました。

※セキュリティグループのアタッチメントやEIPの関連付けなどは省略

移行元の Amazon Linux AMI-2018.3 ではパッケージ管理に yum が使われていましたが、 Amazon Linux 2023では dnf が使われているようです。

dnf(Dandified Yum)は、Fedora、CentOS、RHEL(Red Hat Enterprise Linux)などのRPMベースのディストリビューションで使用されています。dnfはyumの後継ツールとして開発され、より高速なパッケージ解決が可能のようです。(体感値ですが)dnfはyumに比べ、たしかにかなり早いような気がします。

注意点

コマンドを実行する際には適宜sudoコマンドを使うなどして適切な権限で実行してください。

タイムゾーン変更

# date
Fri Aug  4 07:37:44 UTC 2023
# timedatectl set-timezone Asia/Tokyo
Fri Aug  4 16:38:08 JST 2023

Nginx

# dnf install -y nginx
# nginx -v
nginx version: nginx/1.24.0

Nginxのconfファイルの設定は以下のようになっています。参考まで。

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

events {
    worker_connections              1024;
    use                             epoll;
}

http {
    include                         /etc/nginx/mime.types;
    default_type                    application/octet-stream;
    server_names_hash_bucket_size   128;
    server_tokens off;

    log_format                      main    '$remote_addr - $upstream_cache_status [$time_local] $request '
                                            '$status - $body_bytes_sent "$http_referer" '
                                            '$http_user_agent $http_x_forwarded_for';
    access_log                      /var/log/nginx/access.log  main;

    sendfile                        on;
    tcp_nopush                      on;
    tcp_nodelay                     on;
    keepalive_timeout               65;
    types_hash_max_size             2048;

    #gzip
    gzip                            on;
    gzip_types                      text/plain
                                    text/xml
                                    text/css
                                    application/xml
                                    application/xhtml+xml
                                    application/rss+xml
                                    application/atom_xml
                                    application/javascript
                                    application/x-javascript
                                    application/x-httpd-php
                                    application/x-font-ttf
                                    font/opentype
                                    font/x-woff
                                    font/x-woff2
                                    application/vnd.ms-fontobject
                                    image/svg+xml
                                    video/mp4;
    gzip_proxied                    any;
    gzip_min_length                 1100;
    gzip_buffers                    4 32k;
    gzip_static                     on;
    gzip_vary                       on;

    #proxy
    proxy_cache_path                /var/cache/nginx
                                    levels=1:2
                                    keys_zone=czone:16m
                                    inactive=7d
                                    max_size=60g;
    proxy_temp_path                 /var/tmp/nginx;
    proxy_cache                     czone;
    proxy_cache_valid               200 301 302 10m;
    proxy_cache_valid               404 5m;
    proxy_cache_key                 "$scheme://$host$request_uri";
    proxy_set_header                Host               $host;
    proxy_set_header                X-Real-IP          $remote_addr;
    proxy_set_header                X-Forwarded-Host   $host;
    proxy_set_header                X-Forwarded-Server $host;
    proxy_set_header                X-Forwarded-For    $proxy_add_x_forwarded_for;
    proxy_buffering                 on;
    proxy_buffers                   8 32k;
    proxy_buffer_size               64k;
    proxy_connect_timeout           600;
    proxy_send_timeout              600;
    proxy_read_timeout              600;
    send_timeout                    600;

    #fast-cgi
    fastcgi_buffers                 8 128k;
    fastcgi_buffer_size             256k;
    fastcgi_cache_path              /var/cache/fast-cgi
                                    levels=1:2
                                    keys_zone=fczone:15m
                                    inactive=7d
                                    max_size=1000m;
    fastcgi_temp_path               /var/tmp/fast-cgi 1 2; 
    fastcgi_connect_timeout         60;
    fastcgi_read_timeout            90;
    fastcgi_send_timeout            60;
    fastcgi_cache_valid             200 2h;
    fastcgi_cache_valid             302 2h;
    fastcgi_cache_valid             301 4h;
    fastcgi_cache_valid             any 1m;
    fastcgi_index                   index.php;
    fastcgi_param                   SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include                         fastcgi_params;

    upstream backend {
        ip_hash;
        server                      unix:/var/run/php-fpm/php-fpm.sock;
    }

    index                           index.php index.html index.htm;
    client_max_body_size            80M;
    client_body_buffer_size         50M;
    ssl_protocols                   TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers                    '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:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';
    #ssl_ciphers                     ECDHE+AESGCM:DHE+AESGCM:HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers       on;
    ssl_session_cache               shared:SSL:15m;
    ssl_buffer_size                 8k;
    ssl_session_timeout             32m;

    add_header                      X-Cache         $upstream_cache_status;
    include                         /etc/nginx/conf.d/*.conf;
}
server {
    listen          80;
    server_name     example.com;
    rewrite ^(.*)$  https://example.com$1 permanent;
}


server {
    listen          443     ssl http2;
    server_name             example.com;
    root                    /path/to/project_name/public;

    ssl_certificate         /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key     /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_stapling            on;
    ssl_stapling_verify     on;
    ssl_trusted_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_dhparam             /etc/nginx/dhparam.pem;
    ssl_ciphers             ECDHE+AESGCM:DHE+AESGCM:HIGH:!aNULL:!MD5;
    add_header              Strict-Transport-Security 'max-age=31536000; includeSubDomains;';

    resolver                8.8.4.4 8.8.8.8 valid=300s;
    resolver_timeout        10s;

    location ~* \.(eot|ttf|otf|woff|woff2)$ {
        add_header 'Access-Control-Allow-Origin' '*';
        add_header 'Access-Control-Allow-Credentials' 'true';
        add_header 'Access-Control-Allow-Headers' 'Origin, X-Requested-With, Content-Type, Accept';
        add_header 'Access-Control-Allow-Method' 'GET, POST, OPTIONS, PUT, DELETE';
    }

    location ~ /\. { deny all; access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }
    location ~ ^/static/img/favicon { expires max; access_log off; log_not_found off; }
    location ~ .*\.(txt|xml|js|css|gz|ico|jpe?g|gif|png|wmv|flv|swf|mpg|eot|otf|ttf|woff|woff2) {
        access_log  off;
        expires 30d;
        etag off;
        break;
    }
    location / {
        try_files $uri $uri/ /index.php?q=$uri&$args;
    }
    location ~ \.php$ {
        set $do_not_cache 0;
        if ($request_method != "GET") {
            set $do_not_cache 1;
        }
        if ($http_cookie ~ ^.*(comment_author_|wordpress_logged_in|wp-postpass_).*$) {
            set $do_not_cache 1;
        }
        if ($http_x_wap_profile ~ ^[a-z0-9\"]+) {
            set $do_not_cache 1;
        }
        if ($http_profile ~ ^[a-z0-9\"]+) {
            set $do_not_cache 1;
        }
        if ($http_user_agent ~ ^.*(2.0\ MMP|240x320|400X240|AvantGo|BlackBerry|Blazer|Cellphone|Danger|DoCoMo|Elaine/3.0|EudoraWeb|Googlebot-Mobile|hiptop|IEMobile|KYOCERA/WX310K|LG/U990|MIDP-2.|MMEF20|MOT-V|NetFront|Newt|Nintendo\ Wii|Nitro|Nokia|Opera\ Mini|Palm|PlayStation\ Portable|portalmmm|Proxinet|ProxiNet|SHARP-TQ-GX10|SHG-i900|Small|SonyEricsson|Symbian\ OS|SymbianOS|TS21i-10|UP.Browser|UP.Link|webOS|Windows\ CE|WinWAP|YahooSeeker/M1A1-R2D2|iPhone|iPod|Android|BlackBerry9530|LG-TU915\ Obigo|LGE\ VX|webOS|Nokia5800).*) {
            set $do_not_cache 1;
        }
        if ($http_user_agent ~ ^(w3c\ |w3c-|acs-|alav|alca|amoi|audi|avan|benq|bird|blac|blaz|brew|cell|cldc|cmd-|dang|doco|eric|hipt|htc_|inno|ipaq|ipod|jigs|kddi|keji|leno|lg-c|lg-d|lg-g|lge-|lg/u|maui|maxo|midp|mits|mmef|mobi|mot-|moto|mwbp|nec-|newt|noki|palm|pana|pant|phil|play|port|prox|qwap|sage|sams|sany|sch-|sec-|send|seri|sgh-|shar|sie-|siem|smal|smar|sony|sph-|symb|t-mo|teli|tim-|tosh|tsm-|upg1|upsi|vk-v|voda|wap-|wapa|wapi|wapp|wapr|webc|winw|winw|xda\ |xda-).*) {
            set $do_not_cache 1;
        }
        if ($http_user_agent ~ ^(DoCoMo/|J-PHONE/|J-EMULATOR/|Vodafone/|MOT(EMULATOR)?-|SoftBank/|[VS]emulator/|KDDI-|UP\.Browser/|emobile/|Huawei/|IAC/|Nokia|mixi-mobile-converter/)) {
            set $do_not_cache 1;
        }
        if ($http_user_agent ~ (DDIPOCKET\;|WILLCOM\;|Opera\ Mini|Opera\ Mobi|PalmOS|Windows\ CE\;|PDA\;\ SL-|PlayStation\ Portable\;|SONY/COM|Nitro|Nintendo)) {
            set $do_not_cache 1;
        }
        fastcgi_no_cache        $do_not_cache;
        fastcgi_cache_bypass    $do_not_cache;
        fastcgi_cache_key       $scheme://$host$request_uri;
        fastcgi_cache           fczone;
        fastcgi_pass            backend;
    }
    location ^~ /.well-known/acme-challenge/ { default_type "text/plain"; root /var/www/letsencrypt; }
    location = /.well-known/acme-challenge/ { return 404; }
}
location ~ \.php$ {
    set $do_not_cache 0;
    if ($request_method != "GET") {
        set $do_not_cache 1;
    }
    if ($http_cookie ~ ^.*(comment_author_|wordpress_logged_in|wp-postpass_).*$) {
        set $do_not_cache 1;
    }
    if ($http_x_wap_profile ~ ^[a-z0-9\"]+) {
        set $do_not_cache 1;
    }
    if ($http_profile ~ ^[a-z0-9\"]+) {
        set $do_not_cache 1;
    }
    if ($http_user_agent ~ ^.*(2.0\ MMP|240x320|400X240|AvantGo|BlackBerry|Blazer|Cellphone|Danger|DoCoMo|Elaine/3.0|EudoraWeb|Googlebot-Mobile|hiptop|IEMobile|KYOCERA/WX310K|LG/U990|MIDP-2.|MMEF20|MOT-V|NetFront|Newt|Nintendo\ Wii|Nitro|Nokia|Opera\ Mini|Palm|PlayStation\ Portable|portalmmm|Proxinet|ProxiNet|SHARP-TQ-GX10|SHG-i900|Small|SonyEricsson|Symbian\ OS|SymbianOS|TS21i-10|UP.Browser|UP.Link|webOS|Windows\ CE|WinWAP|YahooSeeker/M1A1-R2D2|iPhone|iPod|Android|BlackBerry9530|LG-TU915\ Obigo|LGE\ VX|webOS|Nokia5800).*) {
        set $do_not_cache 1;
    }
    if ($http_user_agent ~ ^(w3c\ |w3c-|acs-|alav|alca|amoi|audi|avan|benq|bird|blac|blaz|brew|cell|cldc|cmd-|dang|doco|eric|hipt|htc_|inno|ipaq|ipod|jigs|kddi|keji|leno|lg-c|lg-d|lg-g|lge-|lg/u|maui|maxo|midp|mits|mmef|mobi|mot-|moto|mwbp|nec-|newt|noki|palm|pana|pant|phil|play|port|prox|qwap|sage|sams|sany|sch-|sec-|send|seri|sgh-|shar|sie-|siem|smal|smar|sony|sph-|symb|t-mo|teli|tim-|tosh|tsm-|upg1|upsi|vk-v|voda|wap-|wapa|wapi|wapp|wapr|webc|winw|winw|xda\ |xda-).*) {
        set $do_not_cache 1;
    }
    if ($http_user_agent ~ ^(DoCoMo/|J-PHONE/|J-EMULATOR/|Vodafone/|MOT(EMULATOR)?-|SoftBank/|[VS]emulator/|KDDI-|UP\.Browser/|emobile/|Huawei/|IAC/|Nokia|mixi-mobile-converter/)) {
        set $do_not_cache 1;
    }
    if ($http_user_agent ~ (DDIPOCKET\;|WILLCOM\;|Opera\ Mini|Opera\ Mobi|PalmOS|Windows\ CE\;|PDA\;\ SL-|PlayStation\ Portable\;|SONY/COM|Nitro|Nintendo)) {
        set $do_not_cache 1;
    }
    fastcgi_no_cache     $do_not_cache;
    fastcgi_cache_bypass $do_not_cache;
    fastcgi_cache_key    $scheme://$host$request_uri;
    fastcgi_cache        fczone;
    fastcgi_pass            unix:/var/run/php-fpm/php-fpm.sock;
    fastcgi_index           index.php;
    fastcgi_param           SCRIPT_FILENAME     $document_root$fastcgi_script_name;
    fastcgi_read_timeout    300;
    include                 fastcgi_params;
}

PHPインストール

# dnf install -y wget php php-mysqli php-fpm php-json php-devel php-gd
# php -v
PHP 8.2.7 (cli) (built: Jun  6 2023 21:28:56) (NTS gcc x86_64)
Copyright (c) The PHP Group
Zend Engine v4.2.7, Copyright (c) Zend Technologies
    with Zend OPcache v8.2.7, Copyright (c), by Zend Technologies

PHPもタイムゾーンを変更する必要があります。
あとはアップロードのファイルサイズの上限は最初にやっておいたほうがよさそうです。

date.timezone = "Asia/Tokyo"
memory_limit = 128M
upload_max_filesize = 32M

php-fpm側も設定しておきましょう。php-fpmの設定ファイルの実行ユーザーがapacheになっているので、nginxに変えます。

user = nginx
group = nginx
listen.owner = nginx
listen.group = nginx
listen.mode = 0660

サービス起動

#systemctl start php-fpm
#systemctl start nginx

Let’s Encryptインストール

SSL証明書発行のため、あらかじめドメインレジストリサービスやRoute53などでルーティングの設定を行っておきましょう。

# dnf install -y python3 augeas-libs pip
# python3 -m venv /opt/certbot/
# /opt/certbot/bin/pip install --upgrade pip
# /opt/certbot/bin/pip install certbot
# ln -s /opt/certbot/bin/certbot /usr/bin/certbot

SSL証明書を発行します。

# systemctl stop nginx
# certbot certonly
Successfully received certificate.

SSL証明書は発行されましたが怒られました。

nginx: [emerg] BIO_new_file("/etc/nginx/dhparam.pem") failed (SSL: error:80000002:system library::No such file or directory:calling fopen(/etc/nginx/dhparam.p>

Nginxにdhparamのパラメータを入れておいたのですが、これがないことが原因だったようです。

NginxがSSL接続を設定するためのDiffie-Hellman(DH)パラメータファイル(dhparam.pem)を見つけられなかったみたいです。これは、SSL/TLS接続におけるPerfect Forward Secrecy(PFS)を有効にするために使用されるもので、存在しない場合には上記のエラーが出ます。

下記のコマンドで生成してください。

sudo openssl dhparam -out /etc/nginx/dhparam.pem 2048

example.comにアクセスが可能になります。

さいごに

この環境ではWordPressを使用していますが、パフォーマンスが大きく改善されました。AWS(Amazon Linux 2023)+Nginx+php8で環境構築をご検討の方はぜひご参考にしてみてください。