如何使用DockerCompose安装WordPress

来自菜鸟教程
跳转至:导航、​搜索

介绍

WordPress 是一个免费和开源的 内容管理系统 (CMS),建立在 MySQL 数据库之上,具有 PHP 处理。 由于其可扩展的插件架构和模板系统,以及其大部分管理可以通过 Web 界面完成的事实,WordPress 是创建不同类型的网站时的流行选择,从博客到产品页面再到电子商务网站。

运行 WordPress 通常需要安装 LAMP(Linux、Apache、MySQL 和 PHP)或 LEMP(Linux、Nginx、MySQL 和 PHP)堆栈,这可能很耗时。 但是,通过使用 DockerDocker Compose 等工具,您可以简化设置首选堆栈和安装 WordPress 的过程。 您可以使用 images,而不是手动安装单个组件,它标准化库、配置文件和环境变量等内容,并在 containers 中运行这些图像,在共享操作系统。 此外,通过使用 Compose,您可以协调多个容器(例如,应用程序和数据库)以相互通信。

在本教程中,您将构建一个多容器 WordPress 安装。 您的容器将包括一个 MySQL 数据库、一个 Nginx Web 服务器和 WordPress 本身。 您还将通过使用 Let's Encrypt 为您希望与您的站点关联的域获取 TLS/SSL 证书来保护您的安装。 最后,您将设置一个 cron 作业来更新您的证书,以便您的域保持安全。

先决条件

要遵循本教程,您将需要:

  • 运行 Ubuntu 18.04 的服务器,以及具有 sudo 权限和活动防火墙的非 root 用户。 有关如何设置这些的指导,请参阅此 初始服务器设置指南
  • 按照 如何在 Ubuntu 18.04 上安装和使用 Docker 的步骤 1 和 2 安装在您的服务器上的 Docker。
  • 按照 如何在 Ubuntu 18.04 上安装 Docker Compose 的第 1 步,在您的服务器上安装 Docker Compose。
  • 一个注册的域名。 本教程将自始至终使用 example.com。 您可以在 Freenom 免费获得一个,或使用您选择的域名注册商。
  • 为您的服务器设置了以下两个 DNS 记录。 您可以按照 this Introduction to DigitalOcean DNS 了解如何将它们添加到 DigitalOcean 帐户的详细信息(如果您正在使用):
    • 带有 example.com 的 A 记录指向您服务器的公共 IP 地址。
    • 带有 www.example.com 的 A 记录指向您服务器的公共 IP 地址。

第 1 步 — 定义 Web 服务器配置

在运行任何容器之前,我们的第一步是定义 Nginx Web 服务器的配置。 我们的配置文件将包含一些特定于 WordPress 的位置块,以及一个位置块,用于将 Let's Encrypt 验证请求定向到 Certbot 客户端以进行自动证书续订。

首先,为您的 WordPress 设置创建一个名为 wordpress 的项目目录并导航到它:

mkdir wordpress && cd wordpress

接下来,为配置文件创建一个目录:

mkdir nginx-conf

使用 nano 或您喜欢的编辑器打开文件:

nano nginx-conf/nginx.conf

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

将以下代码粘贴到文件中。 请务必将 example.com 替换为您自己的域名:

~/wordpress/nginx-conf/nginx.conf

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

        server_name example.com www.example.com;

        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:这定义了您的服务器名称和应该用于向您的服务器请求的服务器块。 请务必将此行中的 example.com 替换为您自己的域名。
  • indexindex 指令定义了在处理对服务器的请求时将用作索引的文件。 我们在这里修改了默认的优先顺序,将 index.php 移动到 index.html 的前面,以便 Nginx 尽可能优先考虑名为 index.php 的文件。
  • root:我们的 root 指令为对我们服务器的请求命名根目录。 这个目录 /var/www/html在构建时根据我们的 WordPress Dockerfile 中的说明创建的挂载点 。 这些 Dockerfile 指令还确保将 WordPress 版本中的文件挂载到此卷。

位置块:

  • location ~ /.well-known/acme-challenge:此位置块将处理对 .well-known 目录的请求,Certbot 将在其中放置一个临时文件以验证我们域的 DNS 是否解析到我们的服务器。 有了这个配置,我们将能够使用 Certbot 的 webroot 插件来获取我们域的证书。
  • location /:在这个位置块中,我们将使用 try_files 指令来检查匹配单个 URI 请求的文件。 但是,我们不会将 404 Not Found 状态作为默认值返回,而是将控制权传递给带有请求参数的 WordPress 的 index.php 文件。
  • 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.icolocation = /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 配置到位,您可以继续创建环境变量以在运行时传递给您的应用程序和数据库容器。

第二步——定义环境变量

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

我们可以在 .env 文件中设置敏感值并限制其流通,而不是在我们的 Docker Compose 文件(包含有关容器如何运行的信息的主文件)中设置所有这些值。 这将防止这些值复制到我们的项目存储库并被公开。

在您的主项目目录 ~/wordpress 中,打开一个名为 .env 的文件:

nano .env

我们将在此文件中设置的机密值包括 MySQL root 用户的密码,以及 WordPress 将用于访问数据库的用户名和密码。

将以下变量名称和值添加到文件中。 请记住在此处为每个变量提供您自己的值

~/wordpress/.env

MYSQL_ROOT_PASSWORD=your_root_password
MYSQL_USER=your_wordpress_database_user
MYSQL_PASSWORD=your_wordpress_database_password

我们已经包含了 root 管理帐户的密码,以及我们的应用程序数据库的首选用户名和密码。

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

因为您的 .env 文件包含敏感信息,您需要确保它包含在项目的 .gitignore.dockerignore 文件中,这些文件告诉 Git 和Docker 将哪些文件 not 分别复制到您的 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 中的 service 是一个正在运行的容器,服务定义指定了有关每个容器如何运行的信息。

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

首先,打开 docker-compose.yml 文件:

nano docker-compose.yml

添加以下代码来定义您的 Compose 文件版本和 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 映像会继续更新。 有关版本固定和避免依赖冲突的更多信息,请参阅 Dockerfile 最佳实践 上的 Docker 文档。
  • container_name:指定容器的名称。
  • restart:定义容器重启策略。 默认是no,但是我们已经设置容器重启,除非手动停止。
  • env_file:这个选项告诉 Compose 我们想从一个名为 .env 的文件中添加环境变量,该文件位于我们的构建上下文中。 在这种情况下,构建上下文是我们的当前目录。
  • environment:此选项允许您添加额外的环境变量,超出 .env 文件中定义的环境变量。 我们将 MYSQL_DATABASE 变量设置为等于 wordpress 以为我们的应用程序数据库提供名称。 因为这是非敏感信息,我们可以直接将其包含在 docker-compose.yml 文件中。
  • volumes:在这里,我们将一个名为 dbdata 的命名 volume 挂载到容器上的 /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 图像 。 如 Step 1 中所述,使用此图像可确保我们的应用程序具有 Nginx 处理 PHP 处理所需的 php-fpm 处理器。 这也是一个 alpine 图像,源自 Alpine Linux 项目,这将有助于减小我们的整体图像大小。 有关使用 alpine 映像的优缺点以及这对您的应用程序是否有意义的更多信息,请参阅 Docker Hub 的 Image Variants 部分下的完整讨论WordPress 图片页面
  • env_file:同样,我们指定要从 .env 文件中提取值,因为这是我们定义应用程序数据库用户和密码的地方。
  • environment:在这里,我们使用我们在 .env 文件中定义的值,但我们将它们分配给 WordPress 图像期望的变量名称:WORDPRESS_DB_USERWORDPRESS_DB_PASSWORD。 我们还定义了一个 WORDPRESS_DB_HOST,它将是运行在 db 容器上的 MySQL 服务器,可通过 MySQL 的默认端口 3306 访问。 我们的 WORDPRESS_DB_NAME 将与我们在 MySQL 服务定义中为 MYSQL_DATABASE 指定的值相同:wordpress
  • 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 以启用我们在 Step 1nginx.conf 文件中定义的配置选项。
  • volumes :在这里,我们定义了命名卷和绑定坐骑 : wordpress:/var/www/html:这会将我们的 WordPress 应用程序代码挂载到 /var/www/html 目录,我们在 Nginx 服务器块中设置为根目录。 ./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@example.com --agree-tos --no-eff-email --staging -d example.com -d www.example.com

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

同样,我们使用 depends_on 来指定 certbot 容器应在 webserver 服务运行后启动。

我们还包括一个 command 选项,它指定一个子命令以与容器的默认 certbot 命令一起运行。 certonly 子命令 将获得具有以下选项的证书:

  • --webroot:这告诉 Certbot 使用 webroot 插件将文件放在 webroot 文件夹中进行身份验证。 这个插件依赖于 HTTP-01 验证方法,它使用 HTTP 请求来证明 Certbot 可以从响应给定域名的服务器访问资源。
  • --webroot-path:指定 webroot 目录的路径。
  • --email:您用于注册和恢复的首选电子邮件。
  • --agree-tos:表示您同意 ACME 的订阅者协议
  • --no-eff-email:这告诉 Certbot 您不希望与 Electronic Frontier Foundation (EFF) 共享您的电子邮件。 如果您愿意,可以随意省略它。
  • --staging:这告诉 Certbot 您想使用 Let's Encrypt 的暂存环境来获取测试证书。 使用此选项可让您测试配置选项并避免可能的域请求限制。 有关这些限制的更多信息,请参阅 Let's Encrypt 的 速率限制文档
  • -d:这允许您指定要应用于您的请求的域名。 在本例中,我们包含了 example.comwww.example.com。 请务必将这些替换为您自己的域。

certbot 服务定义下方,添加您的网络和卷定义:

~/wordpress/docker-compose.yml

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

networks:
  app-network:
    driver: bridge  

我们的顶级 volumes 键定义了卷 certbot-etcwordpressdbdata。 当 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@example.com --agree-tos --no-eff-email --staging -d example.com -d www.example.com

volumes:
  certbot-etc:
  wordpress:
  dbdata:

networks:
  app-network:
    driver: bridge  

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

准备好服务定义后,您就可以启动容器并测试您的证书请求了。

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

我们可以使用 docker-compose up 命令启动我们的容器,它将按照我们指定的顺序创建和运行我们的容器。 如果我们的域请求成功,我们将在输出中看到正确的退出状态,并在 webserver 容器的 /etc/letsencrypt/live 文件夹中看到正确的证书。

使用 docker-compose up-d 标志创建容器,这将运行 dbwordpresswebserver 容器在后台:

docker-compose up -d

您将看到确认您的服务已创建的输出:

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

使用 docker-compose ps 检查服务的状态:

docker-compose ps

如果一切顺利,您的 dbwordpresswebserver 服务将是 Up 并且 certbot 容器将退出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           

如果您在 dbwordpresswebserver 服务的 State 列中看到 Up 以外的任何内容,或退出状态除了 certbot 容器的 0 之外,请务必使用 docker-compose logs 命令检查服务日志:

docker-compose logs service_name

您现在可以使用 docker-compose exec 检查您的证书是否已安装到 webserver 容器:

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 example.com

现在您知道您的请求将成功,您可以编辑 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@example.com --agree-tos --no-eff-email --force-renewal -d example.com -d www.example.com
...

您现在可以运行 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 example.com
certbot      | http-01 challenge for www.example.com
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/example.com/fullchain.pem
certbot      |    Your key file has been saved at:
certbot      |    /etc/letsencrypt/live/example.com/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 将涉及向 HTTPS 添加 HTTP 重定向,指定我们的 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 凭据、协议和安全标头。 请记住将 example.com 替换为您自己的域:

~/wordpress/nginx-conf/nginx.conf

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

        server_name example.com www.example.com;

        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 example.com www.example.com;

        index index.php index.html index.htm;

        root /var/www/html;

        server_tokens off;

        ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/example.com/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 服务器块为 .well-known/acme-challenge 目录的 Certbot 续订请求指定 webroot。 它还包括一个 重写指令 ,它将 HTTP 请求指向根目录到 HTTPS。

HTTPS 服务器块启用 sslhttp2。 要详细了解 HTTP/2 如何在 HTTP 协议上迭代以及它对网站性能的好处,请参阅 如何在 Ubuntu 18.04 上设置支持 HTTP/2 的 Nginx 的介绍。

此块还包括我们的 SSL 证书和密钥位置,以及我们保存到 nginx-conf/options-ssl-nginx.conf 的推荐 Certbot 安全参数。

此外,我们还包含一些安全标头,使我们能够在 SSL LabsSecurity Headers 服务器测试站点等内容上获得 A 评级。 这些标头包括 X-Frame-OptionsX-Content-Type-OptionsReferrer PolicyContent-Security-Policy、和X-XSS-保护HTTP Strict Transport Security (HSTS) 标头已被注释掉——只有在您了解其含义并评估其 “预加载”功能 时才启用此功能。

我们的 rootindex 指令也位于此块中,与 Step 1 中讨论的其余 WordPress 特定位置块一样。

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

在重新创建 webserver 服务之前,您需要将 443 端口映射添加到 webserver 服务定义。

打开您的 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@example.com --agree-tos --no-eff-email --force-renewal -d example.com -d www.example.com

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    

随着容器的运行,您现在可以通过 Web 界面完成 WordPress 安装。

第 6 步 — 通过 Web 界面完成安装

随着我们的容器运行,我们可以通过 WordPress Web 界面完成安装。

在您的网络浏览器中,导航到您的服务器的域。 记得用你自己的域名替换example.com

https://example.com

选择您要使用的语言:

单击 继续 后,您将进入主设置页面,您需要为您的站点选择一个名称和一个用户名。 在这里选择一个容易记住的用户名(而不是“admin”)和一个强密码是个好主意。 您可以使用 WordPress 自动生成的密码或创建自己的密码。

最后,您需要输入您的电子邮件地址并决定是否要阻止搜索引擎将您的网站编入索引:

单击页面底部的 Install WordPress 将带您进入登录提示:

登录后,您将可以访问 WordPress 管理仪表板:

完成 WordPress 安装后,您现在可以采取措施确保您的 SSL 证书将自动更新。

第 7 步 — 更新证书

Let's Encrypt 证书的有效期为 90 天,因此您需要设置一个自动续订过程以确保它们不会失效。 一种方法是使用 cron 调度实用程序创建作业。 在这种情况下,我们将创建一个 cron 作业来定期运行一个脚本,该脚本将更新我们的证书并重新加载我们的 Nginx 配置。

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

nano ssl_renew.sh

将以下代码添加到脚本以更新您的证书并重新加载您的 Web 服务器配置。 请记住将此处的示例用户名替换为您自己的非 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 配置。 有关使用此过程重新加载 Nginx 配置的更多信息,请参阅 this Docker blog post on deploying the official Nginx image with Docker

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

完成编辑后关闭文件。 使其可执行:

chmod +x ssl_renew.sh

接下来,打开您的 root 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/example.com/fullchain.pem (success)
** DRY RUN: simulating 'certbot renew' close to cert expiry
**          (The test certificates above have not been saved.)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

您现在可以修改 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 Web 服务器的 WordPress 安装。 作为此工作流程的一部分,您获得了要与 WordPress 站点关联的域的 TLS/SSL 证书。 此外,您还创建了 cron 作业以在必要时更新这些证书。

作为提高站点性能和冗余的附加步骤,您可以参考以下有关交付和备份 WordPress 资产的文章:

如果您有兴趣探索使用 Kubernetes 的容器化工作流程,您还可以查看: