如何用Docker Compose安装WordPress

832 阅读9分钟

简介

WordPress是一个免费和开源的内容管理系统(CMS),建立在MySQL数据库和PHP处理之上。由于其可扩展的插件结构和模板系统,其大部分管理可以通过网络界面完成。这就是为什么WordPress在创建不同类型的网站时是一个受欢迎的选择,从博客到产品页面到电子商务网站。

运行WordPress通常需要安装LAMP(Linux、Apache、MySQL和PHP)或LEMP(Linux、Nginx、MySQL和PHP)堆栈,这可能很费时间。然而,通过使用像DockerDocker Compose这样的工具,你可以简化设置你喜欢的栈和安装WordPress的过程。你可以使用镜像,而不是手工安装单个组件,镜像将库、配置文件和环境变量等东西标准化。然后,在容器中运行这些镜像,即在一个共享的操作系统上运行的孤立的进程。此外,通过使用Compose,你可以协调多个容器--例如,一个应用程序和数据库--以相互通信。

在本教程中,你将建立一个多容器的WordPress安装。你的容器将包括一个MySQL数据库、一个Nginx网络服务器和WordPress本身。你还将通过Let's Encrypt为你希望与你的网站相关联的域名获得TLS/SSL证书来确保你的安装。最后,你将设置一个 cron工作来更新你的证书,以便你的域名保持安全。

前提条件

要学习本教程,你将需要。

  • 一台运行Ubuntu 20.04的服务器,以及一个具有sudo 权限的非root用户和一个活跃的防火墙。关于如何设置这些的指导,请阅读我们的初始服务器设置指南

  • 按照《如何在Ubuntu 20.04上安装和使用Docker》的步骤1和2,在你的服务器上安装Docker。

  • 按照《如何在Ubuntu 20.04上安装Docker Compose》的第1步,在你的服务器上安装Docker Compose。

  • 一个注册的域名。本教程将自始至终使用your_domain。你可以在Freenom免费获得一个,或者使用你选择的域名注册商。

  • 为你的服务器设置好以下两个DNS记录。你可以按照这个介绍DigitalOcean DNS,了解如何将它们添加到DigitalOcean账户的细节。

    • 一个A记录有 your_domain指向你的服务器的公共IP地址。
    • 一个A记录 www.your_domain指向你的服务器的公共IP地址。

一旦你把一切都设置好了,你就可以开始第一步了。

第1步 - 定义网络服务器配置

在运行任何容器之前,你的第一步是定义Nginx网络服务器的配置。你的配置文件将包括一些WordPress特有的位置块,还有一个位置块用来引导Let's Encrypt验证请求到Certbot客户端进行自动证书更新。

首先,为你的WordPress设置创建一个项目目录。在这个例子中,它被称为wordpress 。如果你愿意,你可以用不同的方式命名这个目录。

mkdir wordpress

然后导航到该目录。

cd wordpress

接下来,为配置文件建立一个目录。

mkdir nginx-conf

nano 或你喜欢的编辑器打开该文件。

nano nginx-conf/nginx.conf

在这个文件中,添加一个服务器块,其中包括服务器名称和文档根目录,以及用于引导Certbot客户端请求证书、PHP处理和静态资产请求的位置块。

在文件中添加以下代码。请确保将 your_domain用你自己的域名。

~/wordpress/nginx-conf/nginx.conf

server {
        listen 80;
        listen [::]:80;

        server_name your_domain www.your_domain;

        index index.php index.html index.htm;

        root /var/www/html;

        location ~ /.well-known/acme-challenge {
                allow all;
                root /var/www/html;
        }

        location / {
                try_files $uri $uri/ /index.php$is_args$args;
        }

        location ~ \.php$ {
                try_files $uri =404;
                fastcgi_split_path_info ^(.+\.php)(/.+)$;
                fastcgi_pass wordpress:9000;
                fastcgi_index index.php;
                include fastcgi_params;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                fastcgi_param PATH_INFO $fastcgi_path_info;
        }

        location ~ /\.ht {
                deny all;
        }
        
        location = /favicon.ico { 
                log_not_found off; access_log off; 
        }
        location = /robots.txt { 
                log_not_found off; access_log off; allow all; 
        }
        location ~* \.(css|gif|ico|jpeg|jpg|js|png)$ {
                expires max;
                log_not_found off;
        }
}

我们的服务器块包括以下信息。

指令。

  • listen:这告诉Nginx监听端口80 ,这将允许你使用Certbot的webroot插件进行证书请求。注意,你还没有包括端口443 - 一旦你成功获得证书,你将更新你的配置以包括SSL。
  • server_name:这定义了你的服务器名称和服务器块,应该用于对你的服务器的请求。请确保将这一行中的 your_domain用你自己的域名取代这一行中的
  • index:这个指令定义了在处理对你的服务器的请求时作为索引使用的文件。你在这里修改了默认的优先级顺序,将index.php 移到了index.html 前面,这样Nginx在可能的情况下会优先处理名为index.php 的文件。
  • root:这条指令命名了向你的服务器发出请求的根目录。这个目录,即/var/www/html ,是在WordPress Docker文件中的指令,在构建时作为一个挂载点创建的。这些Dockerfile指令也确保WordPress发布的文件被挂载到这个卷。

位置区块。

  • location ~ /.well-known/acme-challenge:这个位置块将处理对.well-known 目录的请求,Certbot将在那里放置一个临时文件,以验证你的域名的DNS是否解析到你的服务器。有了这个配置,你就可以使用Certbot的webroot插件来为你的域名获得证书。
  • location /:在这个位置块中,一个try_files 指令被用来检查与单个URI请求相匹配的文件。然而,你将用请求参数将控制权传递给WordPress的index.php 文件,而不是默认返回404 Not Found状态。
  • location ~ \.php$:这个位置块将处理PHP处理,并将这些请求代理给你的wordpress 容器。因为你的WordPress Docker镜像将基于php:fpm 镜像,你也将在这个块中包括FastCGI协议的特定配置选项。Nginx需要一个独立的PHP处理器来处理PHP请求。在这种情况下,这些请求将由包含在php:fpm 镜像中的php-fpm 处理器来处理。此外,这个位置块包括FastCGI特定的指令、变量和选项,将请求代理给运行在wordpress 容器中的WordPress应用程序,为解析的请求URI设置首选索引,并解析URI请求。
  • location ~ /\.ht:这个块将处理.htaccess 文件,因为Nginx不会提供这些文件。deny_all 指令确保.htaccess 文件不会被提供给用户。
  • location = /favicon.ico,location = /robots.txt: 这些块确保对/favicon.ico/robots.txt 的请求不会被记录下来。
  • location ~* \.(css|gif|ico|jpeg|jpg|js|png)$:这个块关闭了对静态资产请求的记录,并确保这些资产是高度可缓存的,因为它们通常是昂贵的服务。

关于FastCGI代理的更多信息,请阅读Understanding and Implementing FastCGI Proxying in Nginx。关于服务器和位置块的信息,请查看《理解Nginx服务器和位置块选择算法》。

编辑完毕后,保存并关闭该文件。如果你使用了nano ,按CTRL+XY ,然后按ENTER

有了Nginx的配置,就可以继续创建环境变量,在运行时传递给应用程序和数据库容器。

第2步 - 定义环境变量

你的数据库和WordPress应用程序容器在运行时需要访问某些环境变量,以使你的应用程序数据能够持续存在并被应用程序访问。这些变量包括敏感和非敏感的信息:你的MySQL密码和应用程序数据库用户和密码的敏感值,以及你的应用程序数据库名称和主机的非敏感信息。

与其在你的Docker Compose文件(包含你的容器如何运行的信息的主文件)中设置所有这些值,不如在一个.env 文件中设置敏感值并限制其流通。这将防止这些值被复制到你的项目库中,并被公开曝光。

在你的主项目目录下。 ~/wordpress,打开一个名为.env 的文件。

nano .env

你在这个文件中设置的机密值包括MySQL用户的密码,以及WordPress将用来访问数据库的用户名和密码。

在文件中添加以下变量名称和值。记住在这里为每个变量提供你自己的值

~/wordpress/.env

MYSQL_ROOT_PASSWORD=your_root_password
MYSQL_USER=your_wordpress_database_user
MYSQL_PASSWORD=your_wordpress_database_password

包括管理账户的密码,以及你的应用程序数据库的首选用户名和密码。

当你完成编辑后,保存并关闭该文件。

因为你的.env 文件包含敏感信息,你要确保它包含在你项目的.gitignore.dockerignore 文件中。这将告诉Git和Docker哪些文件不能分别复制到你的Git仓库和Docker镜像。

如果你打算使用Git进行版本控制,请用git init将你当前的工作目录初始化为一个仓库

git init

然后创建并打开一个.gitignore 文件。

nano .gitignore

在文件中添加.env

~/wordpress/.gitignore

.env

当你完成编辑时,保存并关闭该文件。

同样,将.env 添加到.dockerignore 文件中也是一个很好的预防措施,这样当你使用这个目录作为你的构建环境时,它就不会出现在你的容器上。

打开该文件。

nano .dockerignore

.env 添加到文件中。

~/wordpress/.dockerignore

.env

在这下面,你可以选择性地添加与你的应用程序的开发相关的文件和目录。

~/wordpress/.dockerignore

.env
.git
docker-compose.yml
.dockerignore

完成后保存并关闭该文件。

有了你的敏感信息,你现在可以继续在docker-compose.yml 文件中定义你的服务。

第3步 - 使用Docker Compose定义服务

你的docker-compose.yml 文件将包含你设置的服务定义。Compose中的服务是一个正在运行的容器,而服务定义指定了关于每个容器将如何运行的信息。

使用Compose,你可以定义不同的服务来运行多容器应用程序,因为Compose允许你将这些服务与共享网络和卷连接在一起。这对你目前的设置很有帮助,因为你将为你的数据库、WordPress应用程序和Web服务器创建不同的容器。你还将创建一个容器来运行Certbot客户端,为你的网络服务器获取证书。

首先,创建并打开docker-compose.yml 文件。

nano docker-compose.yml

添加以下代码来定义你的Compos文件版本和db 数据库服务。

~/wordpress/docker-compose.yml

version: '3'

services:
  db:
    image: mysql:8.0
    container_name: db
    restart: unless-stopped
    env_file: .env
    environment:
      - MYSQL_DATABASE=wordpress
    volumes: 
      - dbdata:/var/lib/mysql
    command: '--default-authentication-plugin=mysql_native_password'
    networks:
      - app-network

db 服务定义包含以下选项。

  • image:这告诉Compose要拉什么镜像来创建容器。你在这里钉住mysql:8.0 镜像,以避免将来在mysql:latest 镜像继续更新时发生冲突。关于版本固定和避免依赖性冲突的更多信息,请阅读Docker文档中的Dockerfile最佳实践
  • container_name:这指定了一个容器的名字。
  • restart:这定义了容器的重启策略。默认是no ,但你已经设置了容器的重启,除非它被手动停止。
  • env_file:这个选项告诉 Compose,你想从位于你的构建环境中的一个叫做.env 的文件中添加环境变量。在这种情况下,构建环境就是你的当前目录。
  • environment:这个选项允许你添加额外的环境变量,除了那些在你的.env 文件中定义的变量。你将设置MYSQL_DATABASE 变量等于wordpress ,为你的应用程序数据库提供一个名称。因为这是非敏感信息,你可以直接在docker-compose.yml 文件中包括它。
  • volumes:在这里,你要把一个名为dbdata挂载到容器上的/var/lib/mysql 目录。这是大多数发行版上的MySQL的标准数据目录。
  • command:这个选项指定了一个命令来覆盖镜像的默认CMD指令。在这种特殊情况下,你将为Docker镜像的标准mysqld 命令添加一个选项,该命令在容器上启动MySQL服务器。这个选项,--default-authentication-plugin=mysql_native_password ,将--default-authentication-plugin 系统变量设置为mysql_native_password ,指定哪种认证机制应该管理对服务器的新认证请求。由于PHP和你的WordPress镜像不支持 MySQL较新的默认认证,你必须做这个调整以认证你的应用程序数据库用户。
  • networks:这指定了你的应用服务将加入app-network 网络,你将在文件的底部定义这个网络。

接下来,在你的db 服务定义下面,为你的wordpress 应用服务添加定义。

~/wordpress/docker-compose.yml

...
  wordpress:
    depends_on: 
      - db
    image: wordpress:5.1.1-fpm-alpine
    container_name: wordpress
    restart: unless-stopped
    env_file: .env
    environment:
      - WORDPRESS_DB_HOST=db:3306
      - WORDPRESS_DB_USER=$MYSQL_USER
      - WORDPRESS_DB_PASSWORD=$MYSQL_PASSWORD
      - WORDPRESS_DB_NAME=wordpress
    volumes:
      - wordpress:/var/www/html
    networks:
      - app-network

在这个服务定义中,你要命名你的容器,并定义一个重启策略,就像你对db 服务所做的那样。你还将添加一些特定于该容器的选项。

  • depends_on:这个选项确保你的容器将按照依赖性的顺序启动,wordpress 容器在db 容器之后启动。你的WordPress应用程序依赖于你的应用程序数据库和用户的存在,所以表达这种依赖性的顺序将使你的应用程序能够正常启动。
  • image:在这个设置中,你正在使用5.1.1-fpm-alpine WordPress镜像。正如在步骤1中所讨论的,使用这个镜像可以确保你的应用程序有Nginx所需要的php-fpm 处理器来处理PHP。这也是一个alpine 镜像,来自Alpine Linux项目,这将有助于减少整个镜像的大小。关于使用alpine 镜像的好处和坏处,以及这对你的应用程序是否有意义的更多信息,请查看Docker Hub WordPress镜像页面镜像变体部分的完整讨论。
  • env_file:再一次,你指定要从你的.env 文件中提取数值,因为这是你定义你的应用程序数据库用户和密码的地方。
  • environment:在这里,你使用你在.env 文件中定义的值,但把它们分配给WordPress镜像所期望的变量名:WORDPRESS_DB_USERWORDPRESS_DB_PASSWORD 。你还定义了一个WORDPRESS_DB_HOST ,它将是运行在db 容器上的MySQL服务器,可以通过MySQL的默认端口3306 。你的WORDPRESS_DB_NAME 将是你在MySQL服务定义中为你的MYSQL_DATABASEwordpress 所指定的相同的值。
  • volumes:你正在把一个叫做wordpress 的命名卷挂载到WordPress镜像创建的 /var/www/html 挂载点。以这种方式使用一个命名的卷将允许你与其他容器共享你的应用程序代码。
  • networks:你也在把wordpress 容器添加到app-network 网络中。

接下来,在wordpress 应用程序服务定义下面,为你的webserver Nginx服务添加以下定义。

~/wordpress/docker-compose.yml

...
  webserver:
    depends_on:
      - wordpress
    image: nginx:1.15.12-alpine
    container_name: webserver
    restart: unless-stopped
    ports:
      - "80:80"
    volumes:
      - wordpress:/var/www/html
      - ./nginx-conf:/etc/nginx/conf.d
      - certbot-etc:/etc/letsencrypt
    networks:
      - app-network

在这里,你正在为你的容器命名,并使其在启动顺序上依赖于wordpress 容器。你还在使用一个alpine 镜像--1.15.12-alpine Nginx 镜像

这个服务定义还包括以下选项。

  • ports:这暴露了端口80 ,以启用你在步骤1nginx.conf 文件中定义的配置选项。
  • volumes:在这里,你要定义一个命名卷和绑定挂载的组合。
    • wordpress:/var/www/html:这将把你的WordPress应用程序代码挂载到/var/www/html 目录下,这个目录是你在Nginx服务器块中设置的root
    • ./nginx-conf:/etc/nginx/conf.d::这将把主机上的Nginx配置目录绑定到容器上的相关目录,确保你对主机上的文件所做的任何改变都会反映在容器上。
    • certbot-etc:/etc/letsencrypt:这将为你的域名挂载相关的Let's Encrypt证书和密钥到容器上的相应目录。

你还将这个容器添加到app-network 网络中。

最后,在你的webserver 定义下面,为certbot 服务添加你的最后一个服务定义。请确保用你自己的信息替换这里列出的电子邮件地址和域名。

~/wordpress/docker-compose.yml

  certbot:
    depends_on:
      - webserver
    image: certbot/certbot
    container_name: certbot
    volumes:
      - certbot-etc:/etc/letsencrypt
      - wordpress:/var/www/html
    command: certonly --webroot --webroot-path=/var/www/html --email sammy@your_domain --agree-tos --no-eff-email --staging -d your_domain -d www.your_domain

这个定义告诉Compose从Docker Hub提取certbot/certbot 镜像。它还使用命名卷来与Nginx容器共享资源,包括certbot-etc 中的域名证书和密钥,以及wordpress 中的应用程序代码。

同样,你已经使用depends_on ,指定一旦webserver 服务运行,certbot 容器就应该启动。

你还包括了一个command 选项,它指定了一个子命令,与容器的默认certbot 命令一起运行。certonly 子命令将获得一个带有下列选项的证书。

  • --webroot:这告诉Certbot使用webroot插件,将文件放在webroot文件夹中进行验证。这个插件依赖于HTTP-01验证方法,它使用HTTP请求来证明Certbot可以从响应给定域名的服务器上访问资源。
  • --webroot-path:这指定了webroot目录的路径。
  • --email:你喜欢的用于注册和恢复的电子邮件。
  • --agree-tos:: 这表明您同意ACME的用户协议
  • --no-eff-email:这告诉Certbot你不希望与电子前线基金会(EFF)分享你的电子邮件。如果你愿意的话,请随意省略。
  • --staging:这告诉Certbot您希望使用Let's Encrypt的暂存环境来获取测试证书。使用这个选项可以测试你的配置选项,避免可能的域名请求限制。关于这些限制的更多信息,请阅读Let's Encrypt的速率限制文档
  • -d:这允许你指定你想应用于你的请求的域名。在这个例子中,你已经包括了 your_domainwww.your_domain.请确保用你自己的域名替换这些。

certbot 服务定义下面,添加你的网络和卷的定义。

~/wordpress/docker-compose.yml

...
volumes:
  certbot-etc:
  wordpress:
  dbdata:

networks:
  app-network:
    driver: bridge  

你的顶层volumes 密钥定义了卷certbot-etc,wordpress, 和dbdata 。当Docker创建卷时,卷的内容被存储在主机文件系统的一个目录中,/var/lib/docker/volumes/ ,该目录由Docker管理。然后每个卷的内容会从这个目录挂载到使用该卷的任何容器中。通过这种方式,可以在容器之间共享代码和数据。

用户定义的桥接网络app-network ,使你的容器之间的通信,因为它们是在同一个Docker守护者主机上。这简化了应用程序内的流量和通信,因为它在同一个桥接网络上的容器之间打开了所有的端口,而没有向外界暴露任何端口。因此,你的dbwordpresswebserver 容器可以互相通信,你只需要暴露端口80 ,以便前端访问应用程序。

下面是docker-compose.yml 文件的全部内容。

~/wordpress/docker-compose.yml

version: '3'

services:
  db:
    image: mysql:8.0
    container_name: db
    restart: unless-stopped
    env_file: .env
    environment:
      - MYSQL_DATABASE=wordpress
    volumes: 
      - dbdata:/var/lib/mysql
    command: '--default-authentication-plugin=mysql_native_password'
    networks:
      - app-network

  wordpress:
    depends_on: 
      - db
    image: wordpress:5.1.1-fpm-alpine
    container_name: wordpress
    restart: unless-stopped
    env_file: .env
    environment:
      - WORDPRESS_DB_HOST=db:3306
      - WORDPRESS_DB_USER=$MYSQL_USER
      - WORDPRESS_DB_PASSWORD=$MYSQL_PASSWORD
      - WORDPRESS_DB_NAME=wordpress
    volumes:
      - wordpress:/var/www/html
    networks:
      - app-network

  webserver:
    depends_on:
      - wordpress
    image: nginx:1.15.12-alpine
    container_name: webserver
    restart: unless-stopped
    ports:
      - "80:80"
    volumes:
      - wordpress:/var/www/html
      - ./nginx-conf:/etc/nginx/conf.d
      - certbot-etc:/etc/letsencrypt
    networks:
      - app-network

  certbot:
    depends_on:
      - webserver
    image: certbot/certbot
    container_name: certbot
    volumes:
      - certbot-etc:/etc/letsencrypt
      - wordpress:/var/www/html
    command: certonly --webroot --webroot-path=/var/www/html --email sammy@your_domain --agree-tos --no-eff-email --staging -d your_domain -d www.your_domain

volumes:
  certbot-etc:
  wordpress:
  dbdata:

networks:
  app-network:
    driver: bridge  

当你完成编辑后,保存并关闭该文件。

有了你的服务定义,你就可以启动容器并测试你的证书请求了。

第4步--获取SSL证书和凭证

用以下命令启动你的容器 docker-compose up命令,它将按照你指定的顺序创建和运行你的容器。通过添加-d 标志,该命令将在后台运行db,wordpress, 和webserver 容器。

docker-compose up -d

下面的输出确认了你的服务已经被创建。

OutputCreating db ... done
Creating wordpress ... done
Creating webserver ... done
Creating certbot   ... done

使用 docker-compose ps来检查你的服务的状态。

docker-compose ps

一旦完成,你的dbwordpresswebserver 服务将成为Upcertbot 容器将以0 状态信息退出。

Output  Name                 Command               State           Ports       
-------------------------------------------------------------------------
certbot     certbot certonly --webroot ...   Exit 0                      
db          docker-entrypoint.sh --def ...   Up       3306/tcp, 33060/tcp
webserver   nginx -g daemon off;             Up       0.0.0.0:80->80/tcp 
wordpress   docker-entrypoint.sh php-fpm     Up       9000/tcp           

db,wordpress, 或webserver 服务的State 栏中出现任何非Up 的信息,或certbot 容器的退出状态非0 的信息,意味着你可能需要用 docker-compose logs命令检查服务日志。

docker-compose logs service_name

现在你可以检查你的证书是否已经被挂载到webserver 容器中,用 docker-compose exec:

docker-compose exec webserver ls -la /etc/letsencrypt/live

一旦你的证书请求成功,以下是输出结果。

Outputtotal 16
drwx------    3 root     root          4096 May 10 15:45 .
drwxr-xr-x    9 root     root          4096 May 10 15:45 ..
-rw-r--r--    1 root     root           740 May 10 15:45 README
drwxr-xr-x    2 root     root          4096 May 10 15:45 your_domain

现在你知道你的请求将被成功,你可以编辑certbot 服务定义,删除--staging 标志。

打开docker-compose.yml

nano docker-compose.yml

找到文件中含有certbot 服务定义的部分,将command 选项中的--staging 标志替换为--force-renewal 标志,这将告诉 Certbot 你想请求一个与现有证书相同域名的新证书。以下是带有更新标志的certbot 服务定义。

~/wordpress/docker-compose.yml

...
  certbot:
    depends_on:
      - webserver
    image: certbot/certbot
    container_name: certbot
    volumes:
      - certbot-etc:/etc/letsencrypt
      - certbot-var:/var/lib/letsencrypt
      - wordpress:/var/www/html
    command: certonly --webroot --webroot-path=/var/www/html --email sammy@your_domain --agree-tos --no-eff-email --force-renewal -d your_domain -d www.your_domain
...

你现在可以运行docker-compose up 来重新创建certbot 容器。你还将包括--no-deps 选项,告诉 Compose 它可以跳过启动webserver 服务,因为它已经在运行了。

docker-compose up --force-recreate --no-deps certbot

下面的输出表明你的证书请求是成功的。

OutputRecreating certbot ... done
Attaching to certbot
certbot      | Saving debug log to /var/log/letsencrypt/letsencrypt.log
certbot      | Plugins selected: Authenticator webroot, Installer None
certbot      | Renewing an existing certificate
certbot      | Performing the following challenges:
certbot      | http-01 challenge for your_domain
certbot      | http-01 challenge for www.your_domain
certbot      | Using the webroot path /var/www/html for all unmatched domains.
certbot      | Waiting for verification...
certbot      | Cleaning up challenges
certbot      | IMPORTANT NOTES:
certbot      |  - Congratulations! Your certificate and chain have been saved at:
certbot      |    /etc/letsencrypt/live/your_domain/fullchain.pem
certbot      |    Your key file has been saved at:
certbot      |    /etc/letsencrypt/live/your_domain/privkey.pem
certbot      |    Your cert will expire on 2019-08-08. To obtain a new or tweaked
certbot      |    version of this certificate in the future, simply run certbot
certbot      |    again. To non-interactively renew *all* of your certificates, run
certbot      |    "certbot renew"
certbot      |  - Your account credentials have been saved in your Certbot
certbot      |    configuration directory at /etc/letsencrypt. You should make a
certbot      |    secure backup of this folder now. This configuration directory will
certbot      |    also contain certificates and private keys obtained by Certbot so
certbot      |    making regular backups of this folder is ideal.
certbot      |  - If you like Certbot, please consider supporting our work by:
certbot      | 
certbot      |    Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
certbot      |    Donating to EFF:                    https://eff.org/donate-le
certbot      | 
certbot exited with code 0

有了证书,你就可以继续修改Nginx的配置,使之包括SSL。

第5步 - 修改Web服务器配置和服务定义

在Nginx配置中启用SSL将涉及添加HTTP重定向到HTTPS,指定SSL证书和密钥位置,以及添加安全参数和头文件。

由于你要重新创建webserver 服务,以包括这些添加的内容,你现在可以停止它。

docker-compose stop webserver

在修改配置文件之前,使用curl ,从Certbot获取推荐的Nginx安全参数。

curl -sSLo nginx-conf/options-ssl-nginx.conf https://raw.githubusercontent.com/certbot/certbot/master/certbot-nginx/certbot_nginx/_internal/tls_configs/options-ssl-nginx.conf

这个命令将把这些参数保存在一个名为options-ssl-nginx.conf 的文件中,位于nginx-conf 目录中。

接下来,删除你先前创建的Nginx配置文件。

rm nginx-conf/nginx.conf

创建并打开另一个版本的文件。

nano nginx-conf/nginx.conf

在文件中添加以下代码,将HTTP重定向到HTTPS,并添加SSL证书、协议和安全头文件。记住要用你自己的域名替换 your_domain用你自己的域名。

~/wordpress/nginx-conf/nginx.conf

server {
        listen 80;
        listen [::]:80;

        server_name your_domain www.your_domain;

        location ~ /.well-known/acme-challenge {
                allow all;
                root /var/www/html;
        }

        location / {
                rewrite ^ https://$host$request_uri? permanent;
        }
}

server {
        listen 443 ssl http2;
        listen [::]:443 ssl http2;
        server_name your_domain www.your_domain;

        index index.php index.html index.htm;

        root /var/www/html;

        server_tokens off;

        ssl_certificate /etc/letsencrypt/live/your_domain/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/your_domain/privkey.pem;

        include /etc/nginx/conf.d/options-ssl-nginx.conf;

        add_header X-Frame-Options "SAMEORIGIN" always;
        add_header X-XSS-Protection "1; mode=block" always;
        add_header X-Content-Type-Options "nosniff" always;
        add_header Referrer-Policy "no-referrer-when-downgrade" always;
        add_header Content-Security-Policy "default-src * data: 'unsafe-eval' 'unsafe-inline'" always;
        # add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
        # enable strict transport security only if you understand the implications

        location / {
                try_files $uri $uri/ /index.php$is_args$args;
        }

        location ~ \.php$ {
                try_files $uri =404;
                fastcgi_split_path_info ^(.+\.php)(/.+)$;
                fastcgi_pass wordpress:9000;
                fastcgi_index index.php;
                include fastcgi_params;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                fastcgi_param PATH_INFO $fastcgi_path_info;
        }

        location ~ /\.ht {
                deny all;
        }
        
        location = /favicon.ico { 
                log_not_found off; access_log off; 
        }
        location = /robots.txt { 
                log_not_found off; access_log off; allow all; 
        }
        location ~* \.(css|gif|ico|jpeg|jpg|js|png)$ {
                expires max;
                log_not_found off;
        }
}

HTTP服务器块指定了Certbot更新请求到.well-known/acme-challenge 目录的webroot。它还包括一个重写指令,将到根目录的HTTP请求引向HTTPS。

HTTPS服务器块使sslhttp2 。要阅读更多关于HTTP/2如何迭代HTTP协议以及它对网站性能的好处,请阅读《如何在Ubuntu 18.04上设置支持HTTP/2的Nginx》介绍。

这个区块还包括你的SSL证书和密钥位置,以及推荐的Certbot安全参数,这些参数保存在nginx-conf/options-ssl-nginx.conf

此外,还包括一些安全头信息,这将使你在SSL实验室安全头信息服务器测试网站等地方获得A级评级。这些标头包括 X-Frame-Options, X-Content-Type-Options, Referrer Policy, Content-Security-Policy, , 和 X-XSS-Protection.HTTPStrict Transport Security(HSTS)头被注释掉了--只有在你了解其影响并评估了其"预加载 "功能的情况下才启用它。

你的rootindex 指令也在这个块中,其他在步骤1中讨论的WordPress特定的位置块也在这里。

一旦你完成编辑,保存并关闭该文件。

在重新创建webserver 服务之前,你将需要在你的webserver 服务定义中添加一个443 端口映射。

打开你的docker-compose.yml 文件。

nano docker-compose.yml

webserver 服务定义中,添加以下端口映射。

~/wordpress/docker-compose.yml

...
  webserver:
    depends_on:
      - wordpress
    image: nginx:1.15.12-alpine
    container_name: webserver
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - wordpress:/var/www/html
      - ./nginx-conf:/etc/nginx/conf.d
      - certbot-etc:/etc/letsencrypt
    networks:
      - app-network

以下是编辑后的完整的docker-compose.yml 文件。

~/wordpress/docker-compose.yml

version: '3'

services:
  db:
    image: mysql:8.0
    container_name: db
    restart: unless-stopped
    env_file: .env
    environment:
      - MYSQL_DATABASE=wordpress
    volumes: 
      - dbdata:/var/lib/mysql
    command: '--default-authentication-plugin=mysql_native_password'
    networks:
      - app-network

  wordpress:
    depends_on: 
      - db
    image: wordpress:5.1.1-fpm-alpine
    container_name: wordpress
    restart: unless-stopped
    env_file: .env
    environment:
      - WORDPRESS_DB_HOST=db:3306
      - WORDPRESS_DB_USER=$MYSQL_USER
      - WORDPRESS_DB_PASSWORD=$MYSQL_PASSWORD
      - WORDPRESS_DB_NAME=wordpress
    volumes:
      - wordpress:/var/www/html
    networks:
      - app-network

  webserver:
    depends_on:
      - wordpress
    image: nginx:1.15.12-alpine
    container_name: webserver
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - wordpress:/var/www/html
      - ./nginx-conf:/etc/nginx/conf.d
      - certbot-etc:/etc/letsencrypt
    networks:
      - app-network

  certbot:
    depends_on:
      - webserver
    image: certbot/certbot
    container_name: certbot
    volumes:
      - certbot-etc:/etc/letsencrypt
      - wordpress:/var/www/html
    command: certonly --webroot --webroot-path=/var/www/html --email sammy@your_domain --agree-tos --no-eff-email --force-renewal -d your_domain -d www.your_domain

volumes:
  certbot-etc:
  wordpress:
  dbdata:

networks:
  app-network:
    driver: bridge  

编辑完成后,保存并关闭该文件。

重新创建webserver 服务。

docker-compose up -d --force-recreate --no-deps webserver

docker-compose ps 检查你的服务。

docker-compose ps

输出结果应该表明,你的dbwordpresswebserver 服务正在运行。

Output  Name                 Command               State                     Ports                  
----------------------------------------------------------------------------------------------
certbot     certbot certonly --webroot ...   Exit 0                                           
db          docker-entrypoint.sh --def ...   Up       3306/tcp, 33060/tcp                     
webserver   nginx -g daemon off;             Up       0.0.0.0:443->443/tcp, 0.0.0.0:80->80/tcp
wordpress   docker-entrypoint.sh php-fpm     Up       9000/tcp    

随着你的容器的运行,你可以通过网页界面完成你的WordPress安装。

第6步 - 通过网络界面完成安装

在你的容器运行时,通过WordPress的网页界面完成安装。

在你的网络浏览器中,导航到你的服务器的域名。记住要用你自己的域名代替 your_domain用你自己的域名代替。

https://your_domain

选择你想使用的语言。

WordPress Language Selector

点击 "继续"后,你将进入主设置页面,在那里你将需要为你的网站选择一个名字和一个用户名。在这里选择一个令人难忘的用户名(而不是 "admin")和一个强大的密码是个好主意。你可以使用WordPress自动生成的密码或创建你自己的密码。

最后,你将需要输入你的电子邮件地址,并决定你是否要阻止搜索引擎对你的网站进行索引。

WordPress Main Setup Page

点击页面底部的安装WordPress将带你到一个登录提示。

WordPress Login Screen

一旦登录,你就可以进入WordPress的管理仪表板。

WordPress Main Admin Dashboard

随着WordPress安装完成,你可以采取措施确保SSL证书自动更新。

第7步--更新证书

Let's Encrypt证书的有效期为90天。你可以设置一个自动更新过程,以确保它们不会失效。一种方法是用cron ,创建一个工作。在下面的例子中,你将创建一个cron 作业,定期运行一个脚本,更新你的证书并重新加载Nginx配置。

首先,打开一个名为ssl_renew.sh 的脚本。

nano ssl_renew.sh

在该脚本中添加以下代码,以更新你的证书并重新加载你的网络服务器配置。记住,用你自己的非root用户名替换这里的示例用户名。

~/wordpress/ssl_renew.sh

#!/bin/bash

COMPOSE="/usr/local/bin/docker-compose --no-ansi"
DOCKER="/usr/bin/docker"

cd /home/sammy/wordpress/
$COMPOSE run certbot renew --dry-run && $COMPOSE kill -s SIGHUP webserver
$DOCKER system prune -af

这个脚本首先将docker-compose 二进制文件分配给一个叫做COMPOSE 的变量,并指定--no-ansi 选项,该选项将运行不带ANSI 控制字符docker-compose 命令。然后,它对docker 二进制文件做同样的处理。最后,它切换到~/wordpress 项目目录,并运行以下docker-compose 命令。

  • docker-compose run:这将启动一个certbot 容器并覆盖你的certbot 服务定义中提供的command 。不使用certonly 子命令,而是使用renew 子命令,它将更新接近到期的证书。还包括--dry-run 选项来测试你的脚本。
  • docker-compose kill:这将向webserver 容器发送一个SIGHUP 信号,重新加载Nginx配置。

然后,它运行 docker system prune来删除所有未使用的容器和图像。

当你完成编辑后,关闭该文件。用以下命令使其可执行。

chmod +x ssl_renew.sh

接下来,打开你的 crontab 文件,在指定的时间间隔内运行更新脚本。

sudo crontab -e 

如果这是你第一次编辑这个文件,你会被要求选择一个编辑器。

Outputno crontab for root - using an empty one

Select an editor.  To change later, run 'select-editor'.
  1. /bin/nano        <---- easiest
  2. /usr/bin/vim.basic
  3. /usr/bin/vim.tiny
  4. /bin/ed

Choose 1-4 [1]:
...

在这个文件的最底部,添加以下一行。

crontab

...
*/5 * * * * /home/sammy/wordpress/ssl_renew.sh >> /var/log/cron.log 2>&1

这将把工作间隔设置为每五分钟一次,这样你就可以测试你的更新请求是否按计划进行了。一个日志文件,cron.log ,被创建来记录工作的相关输出。

五分钟后,检查cron.log ,确认更新请求是否成功。

tail -f /var/log/cron.log

下面的输出证实了续订成功。

Output- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
** DRY RUN: simulating 'certbot renew' close to cert expiry
**          (The test certificates below have not been saved.)

Congratulations, all renewals succeeded. The following certs have been renewed:
  /etc/letsencrypt/live/your_domain/fullchain.pem (success)
** DRY RUN: simulating 'certbot renew' close to cert expiry
**          (The test certificates above have not been saved.)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

在你的终端输入CTRL+C ,退出。

你可以修改crontab 文件来设置每天的间隔时间。例如,要在每天中午运行该脚本,你可以修改该文件的最后一行,如下所示。

crontab

...
0 12 * * * /home/sammy/wordpress/ssl_renew.sh >> /var/log/cron.log 2>&1

你也要从你的ssl_renew.sh 脚本中删除--dry-run 选项。

~/wordpress/ssl_renew.sh

#!/bin/bash

COMPOSE="/usr/local/bin/docker-compose --no-ansi"
DOCKER="/usr/bin/docker"

cd /home/sammy/wordpress/
$COMPOSE run certbot renew && $COMPOSE kill -s SIGHUP webserver
$DOCKER system prune -af

你的cron 工作将确保你的Let's Encrypt证书在符合条件时更新,从而不会失效。你还可以用Logrotate工具设置日志轮换,以轮换和压缩你的日志文件。

总结

在本教程中,你使用Docker Compose创建了一个带有Nginx网络服务器的WordPress安装。作为这个工作流程的一部分,你获得了与你的WordPress网站相关的域名的TLS/SSL证书。此外,你创建了一个cron 工作,在必要时更新这些证书。

作为提高网站性能和冗余度的额外步骤,你可以参考以下关于交付和备份WordPress资产的文章。

如果你有兴趣探索使用Kubernetes的容器化工作流程,你也可以查看。