WordPress是一种流行的开源内容管理系统(CMS),用于构建和管理网站。它提供了一个易于使用的界面,使用户可以通过简单的拖放操作来创建和编辑网站内容。

本文章内容需要对docker、ubuntu有较高的掌握程度以及有较强的动手能力,否则请勿根据文章进行实际操作。

服务器资源

负载均衡服务器:192.168.1.2 Ubuntu 22.04 1C1G 40G wordpress节点1服务器:192.168.1.3 Ubuntu 22.04 2C2G 40G wordpress节点2服务器:192.168.1.4 Ubuntu 22.04 2C2G 40G 不同VPS运营商可以通过NETBIRD组网,机房之间地理位置不要太远。 NETBIRD组网参考文章

软件清单

负载均衡:haproxy 单节点 web服务:nginx 双节点 php服务: wordpress 双节点 缓存数据库: redis 主从 对象存储:minio 双节点 关系型数据库:mysql 主从 容器引擎:docker 容器编排: docker-compose

wordpress插件

用户会话存储:Redis User Session Storage 媒体附件存储:Media Cloud

docker安装

参考官方文档

docker-compose安装

参考官方文档

wordpress镜像支持php redis扩展

由于官方镜像不支持redis,我们需要修改官方的Dockerfile。 官方地址 下载3个文件:Dockerfile、docker-entrypoint.sh、wp-config-docker.php php redis扩展下载地址 请下载Source code (tar.gz) 压缩包。 注:以上4个文件下载到wordpress节点服务器的同一个文件夹中,比如文件夹名称:wordpress-php8.1-fpm-alpine 将下载下来的redis压缩包重新解压压缩,得到redis.tar.gz。

tar zxvf phpredis-6.0.1.tar.gz
mv phpredis-6.0.1 redis
tar zcvf redis.tar.gz redis

修改Dockerfile

ADD redis.tar.gz /usr/src/php/ext/
在这个位置添加上面一行内容
# install the PHP extensions we need (https://make.wordpress.org/hosting/handbook/handbook/server-environment/#php-extensions)
...
        docker-php-ext-install -j "$(nproc)" \
                bcmath \
                exif \
                gd \
                intl \
                mysqli \
                zip \
                redis \
在这个位置添加redis
...
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
倒数第二行修改成这样

保存后,docker-entrypoint.sh赋予可执行权限

chmod +x docker-entrypoint.sh

编译容器镜像

docker build -t wordpress:php8.1-fpm-redis-alpine ./

负载均衡服务器 192.168.1.2

docker-compose.yml

version: '3.1'

services:
  haproxy:
    image: haproxy
    container_name: haproxy
    restart: always
    environment:
      - TZ=Asia/Shanghai
    ports:
      - "443:443"
    volumes:
      - /data/haproxy:/usr/local/etc/haproxy:ro
      - /data/haproxy/cert:/usr/local/etc/cert
    sysctls:
      - net.ipv4.ip_unprivileged_port_start=0

haproxy配置文件

存放路径:/data/haproxy/haproxy.cfg

global
    log 127.0.0.1   local0
    maxconn 4096
    daemon
    pidfile /tmp/haproxy.pid

defaults
    log     127.0.0.1       local3

    mode   http

    option  httplog
    option  dontlognull
    option  httpclose
    retries 3 
    option  redispatch
    option  abortonclose
    maxconn 2000

    timeout connect 5000
    timeout client  50000
    timeout server  50000

    stats   enable
    stats   uri /haproxy-stats
    stats   auth 用户名:密码    #设置监控页面的用户和密码
    stats   hide-version

frontend https_frontend
    bind *:443 ssl crt /usr/local/etc/cert/xxx.com.pem
    mode http

    acl domain_s3    hdr_beg(host) -i s3.xxx.com
    acl domain_minio hdr_beg(host) -i minio.xxx.com

    use_backend s3    if domain_s3
    use_backend minio if domain_minio

    default_backend blog

backend blog
    mode http
    balance roundrobin
    server s1 192.168.1.3:80 check inter 2000 fall 3
    server s2 192.168.1.4:80 check inter 2000 fall 3

backend s3
    mode http
    balance roundrobin
    server s1 192.168.1.3:9000 check inter 2000 fall 3
    server s2 192.168.1.4:9000 check inter 2000 fall 3

backend minio
    mode http
    balance roundrobin
    server s1 192.168.1.3:9001 check inter 2000 fall 3
    server s2 192.168.1.4:9001 check inter 2000 fall 3

将域名SSL证书文件xxx.com.pem , xxx.com.pem.key存放到/data/haproxy/cert/目录

wordpress节点1服务器 192.168.1.3

docker-compose.yml

version: '3.8'
services:
  mysql:
    image: mysql
    container_name: mysql
    volumes:
      - /data/mysql/data:/var/lib/mysql
      - /data/mysql/mysql.cnf:/etc/my.cnf
    environment:
      - MYSQL_ROOT_PASSWORD=数据库ROOT密码
      - MYSQL_DATABASE=wordpress
      - MYSQL_USER=wordpress
      - MYSQL_PASSWORD=wordpress用户密码
    restart: always
    ports:
      - "3306:3306"
  wordpress:
    image: wordpress:php8.1-fpm-redis-alpine 
    restart: always
    container_name: wordpress
    environment:
      - WORDPRESS_DB_HOST=mysql
      - WORDPRESS_DB_USER=wordpress
      - WORDPRESS_DB_PASSWORD=wordpress用户密码
      - WORDPRESS_DB_NAME=wordpress
    volumes:
      - /data/nginx/html:/var/www/html:rw
      - /data/php/php.ini:/usr/local/etc/php/php.ini
      - /etc/localtime:/etc/localtime
    links:
      - mysql
      - redis
  nginx:
    image: nginx:1.22.0-alpine
    container_name: nginx
    restart: always
    environment:
      - TZ=Asia/Shanghai
    ports:
      - "80:80"
    volumes:
      - /data/nginx/conf/nginx.conf:/etc/nginx/nginx.conf
      - /data/nginx/logs:/etc/nginx/logs
      - /data/nginx/html:/var/www/html:rw
      - /etc/localtime:/etc/localtime
    links:
      - wordpress
  redis:
    image: redis:7.0.4
    restart: always
    container_name: redis
    ports:
      - 6379:6379
    environment:
      - TZ=Asia/Shanghai
    volumes:
      - /data/redis/data:/data/data
      - /data/redis/conf:/usr/local/etc/redis
      - /data/redis/logs:/data/logs
    command: ["redis-server","/usr/local/etc/redis/redis.conf"]
  minio:
    image: minio/minio
    container_name: minio
    restart: always
    network_mode: "host"
    expose:
      - '9000'
      - '9001'
    volumes:
      - /data/minio/data:/data
      - /etc/localtime:/etc/localtime
      - /etc/timezone:/etc/timezone
    environment:
      - MINIO_ROOT_USER=用户名
      - MINIO_ROOT_PASSWORD=密码
    command: server --console-address ":9001" http://minio{1...2}/data

nginx配置文件

存放路径/data/nginx/conf/nginx.conf

    server {
        listen       80;
        server_name  xxx.com www.xxx.com;
        access_log logs/xxx.access.log main;
        error_log logs/xxx.error.log  notice;

        allow 192.168.1.2;
        allow 192.168.1.4;
        deny all;

        location ~ /.+\.php.*$ {
            if ($fastcgi_script_name ~ /(.+\.php.*)$) {
                set $valid_fastcgi_script_name $1;
            }

            fastcgi_pass   wordpress:9000;
            fastcgi_index  index.php;
            fastcgi_param  SCRIPT_FILENAME /var/www/html/$valid_fastcgi_script_name;
            include  fastcgi_params;
        }

        location / {
            root  /var/www/html/;
            index  index.php;

            if (-f $request_filename/){
                rewrite (.*) $1/ break;
            }
            if (-f $request_filename/index.php){
                rewrite (.*) $1/index.php;
            }
            if (!-f $request_filename){
                rewrite (.*) /index.php;
            }
        }
    }

wordpress php配置文件

php.ini修改以下配置,存放路径/data/php/php.ini php.ini文件可在官方下载的压缩包里找到。

max_execution_time = 90
session.save_handler = redis
session.save_path = "tcp://redis:6379?auth=密码"

redis配置文件

redis.conf修改以下配置,存放路径/data/redis/conf/redis.conf redis.conf文件可在官方下载的压缩包里找到。

pidfile /data/redis_6379.pid
logfile "/data/logs/redis.log"
dir /data/data
requirepass 密码

wordpress节点2服务器 192.168.1.4

docker-compose.yml

version: '3.8'
services:
  nginx:
    image: nginx:1.23.3-alpine
    container_name: nginx
    #network_mode: "host"
    ports:
       - "80:80"
    restart: always
    volumes:
      - /data/nginx/conf/nginx.conf:/etc/nginx/nginx.conf
      - /data/nginx/logs:/etc/nginx/logs/
      - /data/nginx/html:/var/www/html/:rw
      - /etc/localtime:/etc/localtime
    links:
      - wordpress
  wordpress:
    image: wordpress:php8.1-fpm-redis-alpine
    restart: always
    container_name: wordpress
    environment:
      - WORDPRESS_DB_HOST=192.168.1.3
      - WORDPRESS_DB_USER=wordpress
      - WORDPRESS_DB_PASSWORD=wordpress用户密码
      - WORDPRESS_DB_NAME=wordpress
    volumes:
      - /data/nginx/html:/var/www/html:rw
      - /data/php/php.ini:/usr/local/etc/php/php.ini
      - /etc/localtime:/etc/localtime
  mysql:
    image: mysql
    container_name: mysql
    volumes:
      - /data/mysql/data:/var/lib/mysql
      - /data/mysql/mysql.cnf:/etc/my.cnf
    environment:
      - MYSQL_ROOT_PASSWORD=ROOT用户密码
      - MYSQL_DATABASE=wordpress
      - MYSQL_USER=wordpress
      - MYSQL_PASSWORD=wordpress用户密码
    restart: always
    ports:
      - "3306:3306"
  redis:
    image: redis:7.0.4
    restart: always
    container_name: redis
    ports:
      - 6379:6379
    environment:
      - TZ=Asia/Shanghai
    volumes:
      - /data/redis/data:/data/data
      - /data/redis/conf:/usr/local/etc/redis
      - /data/redis/logs:/data/logs
    command: ["redis-server","/usr/local/etc/redis/redis.conf"]
  minio:
    image: minio/minio
    container_name: minio
    restart: always
    network_mode: "host"
    expose:
      - '9000'
      - '9001'
    volumes:
      - /data/minio/data:/data
      - /etc/localtime:/etc/localtime
      - /etc/timezone:/etc/timezone
    environment:
      - MINIO_ROOT_USER=用户名
      - MINIO_ROOT_PASSWORD=密码
    command: server --console-address ":9001" http://minio{1...2}/data

nginx配置文件

server {
        listen       80;
        server_name  xxx.com www.xxx.com;
        access_log logs/xxx.access.log main;
        error_log logs/xxx.error.log  notice;

        allow 192.168.1.2;
        deny all;

        location /wp-content/uploads/ {
            proxy_set_header Host $host;
            proxy_pass http://192.168.1.3/wp-content/uploads/;
        }

        location ~ /.+\.php.*$ {
            if ($fastcgi_script_name ~ /(.+\.php.*)$) {
                set $valid_fastcgi_script_name $1;
            }
            fastcgi_pass   wordpress:9000;
            fastcgi_index  index.php;
            fastcgi_param  SCRIPT_FILENAME /var/www/html/$valid_fastcgi_script_name;
            include  fastcgi_params;
        }

        location / {
            root  /var/www/html/;
            index  index.php;

            if (-f $request_filename/){
                rewrite (.*) $1/ break;
            }
            if (-f $request_filename/index.php){
                rewrite (.*) $1/index.php;
            }
            if (!-f $request_filename){
                rewrite (.*) /index.php;
            }
        }
    }

wordpress php配置文件

max_execution_time = 90
session.save_handler = redis
session.save_path = "tcp://192.168.1.3:6379?auth=密码"

redis配置文件

pidfile /data/redis_6379.pid
logfile "/data/logs/redis.log"
dir /data/data
replicaof 192.168.1.3 6379
masterauth 密码
requirepass 密码
appendonly yes

mysql主从配置

参考文章

minio配置

浏览器打开网址:https://minio.xxx.com,登录(账号密码见docker-compose.yml) 创建Buckets,桶名称为wordpress 修改Buckets的Access Policy为custom Write Policy内容为任何人只读:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": [
                    "*"
                ]
            },
            "Action": [
                "s3:GetBucketLocation"
            ],
            "Resource": [
                "arn:aws:s3:::wordpress"
            ]
        },
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": [
                    "*"
                ]
            },
            "Action": [
                "s3:GetObject"
            ],
            "Resource": [
                "arn:aws:s3:::wordpress/*"
            ]
        }
    ]
}

创建Policy,名称为wordpress,Raw Policy内容为桶wordpress读写权限:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Stmt1492520492000",
            "Effect": "Allow",
            "Action": [
                "s3:GetBucketLocation",
                "s3:ListBucket",
                "s3:ListBucketMultipartUploads"
            ],
            "Resource": [
                "arn:aws:s3:::wordpress"
            ]
        },
        {
            "Sid": "Stmt1492521245000",
            "Effect": "Allow",
            "Action": [
                "s3:*"
            ],
            "Resource": [
                "arn:aws:s3:::wordpress/*"
            ]
        }
    ]
}

创建新用户wordpress,为该用户创建新的Access Keys,并记录Access Keys和Secret Key,设置Assign Policies为wordpress权限。

wordpress插件配置

用户会话存储 Redis User Session Storage配置

安装并启用。 修改文件/data/nginx/html/wp-config.php,添加以下内容

define( 'WP_REDIS_USER_SESSION_HOST' , '192.168.1.3' );
define( 'WP_REDIS_USER_SESSION_PORT' , 6379 );
define( 'WP_REDIS_USER_SESSION_SOCKET' , null );
define( 'WP_REDIS_USER_SESSION_AUTH' , '密码' );
define( 'WP_REDIS_USER_SESSION_DB' , '0' );
define( 'WP_REDIS_USER_SESSION_SERIALIZER' , Redis::SERIALIZER_PHP );

媒体附件存储 Media Cloud配置

安装并启用。 在设置界面中,点击Cloud Storage下面的Settings Enable Cloud Storage 开启 Storage Provider 选择minio Access Key 填入Access Keys Secret 填入Secret Key Bucket 填wordpress Region 选择Automatic Custom Region 填入Custom Custom Endpoint 填s3.xxx.com 其他设置根据自己情况修改

后台登录问题排查

/data/nginx/html/wp-config.php代码中以下常量两个节点必须一致,否则负载均衡轮询验证登录时会被踢出登录状态。

define( 'AUTH_KEY',         getenv_docker('WORDPRESS_AUTH_KEY',         'xxx') );
define( 'SECURE_AUTH_KEY',  getenv_docker('WORDPRESS_SECURE_AUTH_KEY',  'xxx') );
define( 'LOGGED_IN_KEY',    getenv_docker('WORDPRESS_LOGGED_IN_KEY',    'xxx') );
define( 'NONCE_KEY',        getenv_docker('WORDPRESS_NONCE_KEY',        'xxx') );
define( 'AUTH_SALT',        getenv_docker('WORDPRESS_AUTH_SALT',        'xxx') );
define( 'SECURE_AUTH_SALT', getenv_docker('WORDPRESS_SECURE_AUTH_SALT', 'xxx') );
define( 'LOGGED_IN_SALT',   getenv_docker('WORDPRESS_LOGGED_IN_SALT',   'xxx') );
define( 'NONCE_SALT',       getenv_docker('WORDPRESS_NONCE_SALT',       'xxx') );