如何使用DockerCompose安装Drupal
作为 Write for DOnations 计划的一部分,作者选择了 联合国基金会 来接受捐赠。
本教程的原始 WordPress 版本 由 Kathleen Juell 编写。
介绍
Drupal 是用 PHP 编写的内容管理系统 (CMS),并在开源 GNU 通用公共许可证 下分发。 世界各地的人们和组织都使用 Drupal 为政府网站、个人博客、企业等提供支持。 Drupal 与其他 CMS 框架的独特之处在于其 不断发展的社区 和一组功能,包括安全流程、可靠性能、模块化和适应灵活性。
Drupal 需要安装 LAMP(Linux、Apache、MySQL 和 PHP)或 LEMP(Linux、Nginx、MySQL 和 PHP)堆栈,但安装单个组件非常耗时任务。 我们可以使用 Docker 和 Docker Compose 等工具来简化 Drupal 的安装过程。 本教程将使用 Docker 镜像在 Docker 容器中安装各个组件。 通过使用 Docker Compose,我们可以为数据库、应用程序以及它们之间的网络/通信定义和管理多个容器。
在本教程中,我们将使用 Docker Compose 安装 Drupal,以便我们可以利用容器化并将我们的 Drupal 网站部署在服务器上。 我们将为 MySQL 数据库、Nginx 网络服务器和 Drupal 运行容器。 我们还将通过使用 Let's Encrypt 为我们想要与我们的站点关联的域获取 TLS/SSL 证书来保护我们的安装。 最后,我们将设置一个 cron 作业 来更新我们的证书,以便我们的域保持安全。
先决条件
要遵循本教程,我们将需要:
- 运行 Ubuntu 18.04 的服务器,以及具有
sudo
权限和活动防火墙的非 root 用户。 有关如何设置这些的指导,请参阅此 初始服务器设置指南 。 - 按照 如何在 Ubuntu 18.04 上安装和使用 Docker 的步骤 1 和 2 安装在您的服务器上的 Docker。 本教程已在 19.03.8 版本上进行了测试。
- 按照 如何在 Ubuntu 18.04 上安装 Docker Compose 的第 1 步,在您的服务器上安装 Docker Compose。 本教程已经在 1.21.2 版本上进行了测试。
- 一个注册的域名。 本教程将自始至终使用
your_domain
。 您可以在 Freenom 免费获得一个,或使用您选择的域名注册商。 - 为您的服务器设置了以下两个 DNS 记录。 你可以关注 DigitalOcean DNS 简介有关如何将它们添加到 DigitalOcean 帐户的详细信息,如果您正在使用: 带有 your_domain 的 A 记录指向您服务器的公共 IP 地址。 带有 www.your_domain 的 A 记录指向您服务器的公共 IP 地址。
第 1 步 — 定义 Web 服务器配置
在运行任何容器之前,我们需要为 Nginx Web 服务器定义配置。 我们的配置文件将包括一些 Drupal 特定的位置块,以及一个位置块,用于将 Let's Encrypt 验证请求定向到 Certbot 客户端以进行自动证书续订。
首先,让我们为我们的 Drupal 设置创建一个名为 drupal
的项目目录:
mkdir drupal
进入新创建的目录:
cd drupal
现在我们可以为我们的配置文件创建一个目录:
mkdir nginx-conf
使用 nano 或您喜欢的文本编辑器打开文件:
nano nginx-conf/nginx.conf
在这个文件中,我们将添加一个服务器块,其中包含我们的服务器名称和文档根目录的指令,以及用于指导 Certbot 客户端请求证书、PHP 处理和静态资产请求的位置块。
将以下代码添加到文件中。 请务必将 your_domain
替换为您自己的域名:
~/drupal/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; } rewrite ^/core/authorize.php/core/authorize.php(.*)$ /core/authorize.php$1; location ~ \.php$ { try_files $uri =404; fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass drupal: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
在构建时由我们的 Drupal Dockerfile 中的指令创建为挂载点。 这些 Dockerfile 指令还确保将 Drupal 版本中的文件挂载到此卷。rewrite
:如果指定的正则表达式 (^/core/authorize.php/core/authorize.php(.*)$
) 匹配请求 URI,则按照替换字符串 (/core/authorize.php$1
) 中指定的方式更改 URI。
位置块:
location ~ /.well-known/acme-challenge
:此位置块将处理对.well-known
目录的请求,Certbot 将在其中放置一个临时文件以验证我们域的 DNS 是否解析到我们的服务器。 有了这个配置,我们将能够使用 Certbot 的 webroot 插件来获取我们域的证书。location /
:在这个位置块中,我们将使用try_files
指令来检查匹配单个 URI 请求的文件。 然而,我们不会将404 Not Found
状态作为默认值返回,而是将控制权传递给 Drupal 的index.php
文件以及请求参数。location ~ \.php$
:此位置块将处理 PHP 处理并将这些请求代理到我们的 drupal 容器。 由于我们的 Drupal Docker 映像将基于php:fpm
映像,因此我们还将在此块中包含特定于 FastCGI 协议 的配置选项。 Nginx 需要一个独立的 PHP 处理器来处理 PHP 请求:在我们的例子中,这些请求将由php:fpm
图像中包含的php-fpm
处理器处理。 此外,此位置块包括特定于 FastCGI 的指令、变量和选项,它们会将请求代理到在我们的 Drupal 容器中运行的 Drupal 应用程序,为解析的请求 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 服务器和位置块选择算法。
完成编辑后保存并关闭文件。
随着 Nginx 配置到位,您可以继续创建环境变量以在运行时传递给您的应用程序和数据库容器。
第二步——定义环境变量
我们的 Drupal 应用程序需要一个数据库(MySQL、PostgresSQL 等)来保存与站点相关的信息。 Drupal 容器需要在运行时访问某些环境变量才能访问数据库 (MySQL) 容器。 这些变量包含数据库凭据等敏感信息,因此我们不能直接在 Docker Compose 文件中公开它们——该文件包含有关我们的容器将如何运行的信息的主文件。
始终建议在 .env
文件中设置敏感值并限制其流通。 这将防止这些值复制到我们的项目存储库并被公开。
在主项目目录 ~/drupal
中,创建并打开一个名为 .env
的文件:
nano .env
将以下变量添加到 .env
文件中,将突出显示的部分替换为您要使用的凭据:
~/drupal/.env
MYSQL_ROOT_PASSWORD=root_password MYSQL_DATABASE=drupal MYSQL_USER=drupal_database_user MYSQL_PASSWORD=drupal_database_password
我们现在已经添加了 MySQL 根管理帐户的密码,以及我们的应用程序数据库的首选用户名和密码。
我们的 .env
文件包含敏感信息,因此始终建议将其包含在项目的 .gitignore
和 .dockerignore
文件中,这样它就不会被添加到我们的 Git 存储库和 Docker 中图片。
如果您打算使用 Git 进行版本控制, 使用 git init
将当前工作目录初始化为存储库:
git init
打开 .gitignore
文件:
nano .gitignore
添加以下内容:
~/drupal/.gitignore
.env
保存并退出文件。
同样,打开.dockerignore
文件:
nano .dockerignore
然后添加以下内容:
~/drupal/.dockerignore
.env .git
保存并退出文件。
现在我们已经采取措施保护我们的凭据作为环境变量,让我们进入下一步,在 docker-compose.yml
文件中定义我们的服务。
第 3 步 — 使用 Docker Compose 定义服务
Docker Compose 是一个用于定义和运行多容器 Docker 应用程序的工具。 我们定义了一个 YAML 文件 来配置我们应用程序的服务。 Docker Compose 中的 service 是一个正在运行的容器,Compose 允许我们将这些服务与共享卷和网络链接在一起。
我们将为我们的 Drupal 应用程序、数据库和 Web 服务器创建不同的容器。 除了这些,我们还将创建一个容器来运行 Certbot 以便为我们的 Web 服务器获取证书。
创建一个 docker-compose.yml
文件:
nano docker-compose.yml
添加以下代码来定义 Compose 文件版本和 mysql
数据库服务:
~/drupal/docker-compose.yml
version: "3" services: mysql: image: mysql:8.0 container_name: mysql command: --default-authentication-plugin=mysql_native_password restart: unless-stopped env_file: .env volumes: - db-data:/var/lib/mysql networks: - internal
让我们用 mysql
服务的所有配置选项来一一介绍:
image
:这指定将用于/拉取用于创建容器的图像。 始终建议使用带有正确版本标签的图像,不包括latest
标签,以避免将来发生冲突。 阅读 Docker 文档中有关 Dockerfile 最佳实践 的更多信息。container_name
:定义容器的名称。command
:用于覆盖图像中的默认命令(CMD 指令)。 MySQL支持不同的认证插件,但是mysql_native_password
是传统的认证方式。 由于 PHP 和 Drupal 不支持 较新的 MySQL 身份验证 ,我们需要将--default-authentication-plugin=mysql_native_password
设置为默认身份验证机制。restart
:用于定义容器重启策略。unless-stopped
策略会重新启动容器,除非它被手动停止。env_file
:从文件中添加环境变量。 在我们的例子中,它将从上一步中定义的.env
文件中读取环境变量。volumes
:这会挂载主机路径或命名卷,指定为服务的子选项。 我们正在将一个名为db-data
的命名卷挂载到容器上的/var/lib/mysql
目录中,默认情况下 MySQL 将在其中写入其数据文件。networks
:这定义了我们的应用服务将加入的internal
网络。 我们将在文件末尾定义网络。
我们已经定义了我们的 mysql
服务定义,现在让我们将 drupal
应用程序服务的定义添加到文件末尾:
~/drupal/docker-compose.yml
... drupal: image: drupal:8.7.8-fpm-alpine container_name: drupal depends_on: - mysql restart: unless-stopped networks: - internal - external volumes: - drupal-data:/var/www/html
在这个服务定义中,我们命名容器并定义重启策略,就像我们对 mysql
服务所做的那样。 我们还添加了一些特定于该容器的选项:
image
:这里,我们使用的是 8.7.8-fpm-alpine Drupal 图像。 此图像具有我们的 Nginx Web 服务器处理 PHP 处理所需的php-fpm
处理器。 此外,我们使用的是alpine
镜像,源自 Alpine Linux 项目,这将减小整体镜像的大小,并在 Dockerfile 最佳实践 中被推荐。 Drupal 有更多版本的镜像,所以请在 Dockerhub 上查看它们。depends_on
:用于表示服务之间的依赖关系。 将mysql
服务定义为对我们的drupal
容器的依赖将确保我们的drupal
容器将在mysql
容器之后创建,并使我们的应用程序能够启动顺利。networks
:在这里,我们已将此容器与internal
网络一起添加到external
网络。 这将确保我们的mysql
服务只能通过internal
网络从drupal
容器访问,同时使其他容器可以通过external
网络访问此容器.volumes
:我们正在将一个名为drupal-data
的命名卷挂载到由 Drupal 映像 创建的/var/www/html
挂载点。 以这种方式使用命名卷将允许我们与其他容器共享我们的应用程序代码。
接下来,让我们在 drupal
服务定义之后添加 Nginx 服务定义:
~/drupal/docker-compose.yml
... webserver: image: nginx:1.17.4-alpine container_name: webserver depends_on: - drupal restart: unless-stopped ports: - 80:80 volumes: - drupal-data:/var/www/html - ./nginx-conf:/etc/nginx/conf.d - certbot-etc:/etc/letsencrypt networks: - external
同样,我们正在命名我们的容器并使其依赖于 Drupal 容器以启动顺序。 我们还使用了 alpine 图像——1.17.4-alpine Nginx 图像。
此服务定义还包括以下选项:
ports
:这会暴露端口80
以启用我们在步骤 1 中nginx.conf
文件中定义的配置选项。volumes
:在这里,我们定义了命名卷和主机路径:drupal-data:/var/www/html
:这会将我们的 Drupal 应用程序代码挂载到/var/www/html
目录,我们将其设置为 Nginx 服务器块中的根目录。./nginx-conf:/etc/nginx/conf.d
:这会将宿主机上的 Nginx 配置目录挂载到容器上的相关目录,确保我们对宿主机上的文件所做的任何更改都会反映到容器中。certbot-etc:/etc/letsencrypt
:这会将我们域的相关 Let's Encrypt 证书和密钥挂载到容器上的相应目录。networks
:我们定义了external
网络只是为了让这个容器与drupal
容器而不是与mysql
容器通信。
最后,我们将为 certbot
服务添加我们的最后一个服务定义。 请务必将 sammy@your_domain
和 your_domain
替换为您自己的电子邮件和域名:
~/drupal/docker-compose.yml
... certbot: depends_on: - webserver image: certbot/certbot container_name: certbot volumes: - certbot-etc:/etc/letsencrypt - drupal-data:/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
中的域证书和密钥以及 drupal-data
中的应用程序代码。
我们还使用 depends_on
来确保 certbot
容器将在 webserver
服务运行后启动。
我们在这里没有指定任何 networks
因为这个容器不会通过网络与任何服务通信。 它只是添加我们使用命名卷安装的域证书和密钥。
我们还包含了 command
选项,该选项指定一个子命令以使用容器的默认 certbot
命令运行。 Certbot 客户端支持获取和安装证书的插件。 我们正在使用 webroot
插件通过在命令行中包含 certonly
和 --webroot
来获取证书。 从官方 Certbot 文档 中阅读有关插件和附加命令的更多信息。
在 certbot
服务定义之后,添加网络和卷定义:
~/drupal/docker-compose.yml
... networks: external: driver: bridge internal: driver: bridge volumes: drupal-data: db-data: certbot-etc:
顶级 networks
键让我们指定要创建的网络。 networks
允许在所有端口上的服务/容器之间进行通信,因为它们位于同一个 Docker 守护程序主机上。 我们定义了两个网络,internal
和 external
,以保护 webserver
、drupal
和 mysql
服务的通信。
volumes
键用于定义命名卷 drupal-data
、db-data
和 certbot-etc
。 当 Docker 创建卷时,卷的内容存储在主机文件系统上的目录 /var/lib/docker/volumes/
中,由 Docker 管理。 然后,每个卷的内容都会从此目录挂载到使用该卷的任何容器中。 通过这种方式,可以在容器之间共享代码和数据。
完成的 docker-compose.yml
文件将如下所示:
~/drupal/docker-compose.yml
version: "3" services: mysql: image: mysql:8.0 container_name: mysql command: --default-authentication-plugin=mysql_native_password restart: unless-stopped env_file: .env volumes: - db-data:/var/lib/mysql networks: - internal drupal: image: drupal:8.7.8-fpm-alpine container_name: drupal depends_on: - mysql restart: unless-stopped networks: - internal - external volumes: - drupal-data:/var/www/html webserver: image: nginx:1.17.4-alpine container_name: webserver depends_on: - drupal restart: unless-stopped ports: - 80:80 volumes: - drupal-data:/var/www/html - ./nginx-conf:/etc/nginx/conf.d - certbot-etc:/etc/letsencrypt networks: - external certbot: depends_on: - webserver image: certbot/certbot container_name: certbot volumes: - certbot-etc:/etc/letsencrypt - drupal-data:/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 networks: external: driver: bridge internal: driver: bridge volumes: drupal-data: db-data: certbot-etc:
我们完成了定义我们的服务。 接下来,让我们启动容器并测试我们的证书请求。
第 4 步 — 获取 SSL 证书和凭证
我们可以使用 docker-compose up
命令启动我们的容器,它将按照我们指定的顺序创建和运行我们的容器。 如果我们的域请求成功,我们将在输出中看到正确的退出状态,并在 Web 服务器容器的 /etc/letsencrypt/live
文件夹中看到正确的证书。
要在后台运行容器,请使用带有 -d
标志的 docker-compose up
命令:
docker-compose up -d
您将看到类似的输出,确认您的服务已创建:
Output... Creating mysql ... done Creating drupal ... done Creating webserver ... done Creating certbot ... done
使用 docker-compose ps
命令检查服务的状态:
docker-compose ps
我们将看到 mysql
、drupal
和 webserver
服务的 State
为 Up
,而 certbot
将退出并显示 0
状态消息:
Output Name Command State Ports -------------------------------------------------------------------------- certbot certbot certonly --webroot ... Exit 0 drupal docker-php-entrypoint php-fpm Up 9000/tcp mysql docker-entrypoint.sh --def ... Up 3306/tcp, 33060/tcp webserver nginx -g daemon off; Up 0.0.0.0:80->80/tcp
如果您在 mysql
、drupal
或 webserver
服务的 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 Oct 5 09:15 . drwxr-xr-x 9 root root 4096 Oct 5 09:15 .. -rw-r--r-- 1 root root 740 Oct 5 09:15 README drwxr-xr-x 2 root root 4096 Oct 5 09:15 your_domain
现在一切运行成功,我们可以编辑我们的 certbot
服务定义以删除 --staging
标志。
打开docker-compose.yml
文件,进入certbot
服务定义,将命令选项中的--staging
标志替换为--force-renewal
标志,会告诉Certbot您要申请与现有证书具有相同域的新证书。 更新后的 certbot
定义如下所示:
~/drupal/docker-compose.yml
... certbot: depends_on: - webserver image: certbot/certbot container_name: certbot volumes: - certbot-etc:/etc/letsencrypt - drupal-data:/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 2020-01-03. 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
这将给出以下输出:
OutputStopping webserver ... done
接下来,让我们删除我们之前创建的 Nginx 配置文件:
rm nginx-conf/nginx.conf
打开另一个版本的文件:
nano nginx-conf/nginx.conf
将以下代码添加到文件以将 HTTP 重定向到 HTTPS 并添加 SSL 凭据、协议和安全标头。 请记住将 your_domain
替换为您自己的域:
~/drupal/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; listen [::]:443 ssl; 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; 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; location / { try_files $uri $uri/ /index.php$is_args$args; } rewrite ^/core/authorize.php/core/authorize.php(.*)$ /core/authorize.php$1; location ~ \.php$ { try_files $uri =404; fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass drupal: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 插件。 它还包括一个 rewrite
指令,将 HTTP 请求指向根目录到 HTTPS。
HTTPS 服务器块启用 ssl
和 http2
。 要详细了解 HTTP/2 如何在 HTTP 协议上迭代以及它对网站性能的好处,请参阅 如何在 Ubuntu 18.04 上设置支持 HTTP/2 的 Nginx 的介绍。
这些块启用 SSL,因为我们已经包含了我们的 SSL 证书和密钥位置以及推荐的标头。 这些标头将使我们能够在 SSL Labs 和 Security Headers 服务器测试站点上获得 A 评级。
我们的 root
和 index
指令也位于此块中,步骤 1 中讨论的其余 Drupal 特定位置块也是如此。
保存并关闭更新的 Nginx 配置文件。
在重新创建 webserver
容器之前,我们需要添加一个 443
端口映射到我们的 webserver
服务定义,因为我们已经启用了 SSL 证书。
打开docker-compose.yml
文件:
nano docker-compose.yml
在 webserver
服务定义中进行以下更改:
~/drupal/docker-compose.yml
... webserver: image: nginx:1.17.4-alpine container_name: webserver depends_on: - drupal restart: unless-stopped ports: - 80:80 - 443:443 volumes: - drupal-data:/var/www/html - ./nginx-conf:/etc/nginx/conf.d - certbot-etc:/etc/letsencrypt networks: - external ...
启用 SSL 证书后,我们的 docker-compose.yml
将如下所示:
~/drupal/docker-compose.yml
version: "3" services: mysql: image: mysql:8.0 container_name: mysql command: --default-authentication-plugin=mysql_native_password restart: unless-stopped env_file: .env volumes: - db-data:/var/lib/mysql networks: - internal drupal: image: drupal:8.7.8-fpm-alpine container_name: drupal depends_on: - mysql restart: unless-stopped networks: - internal - external volumes: - drupal-data:/var/www/html webserver: image: nginx:1.17.4-alpine container_name: webserver depends_on: - drupal restart: unless-stopped ports: - 80:80 - 443:443 volumes: - drupal-data:/var/www/html - ./nginx-conf:/etc/nginx/conf.d - certbot-etc:/etc/letsencrypt networks: - external certbot: depends_on: - webserver image: certbot/certbot container_name: certbot volumes: - certbot-etc:/etc/letsencrypt - drupal-data:/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 networks: external: driver: bridge internal: driver: bridge volumes: drupal-data: db-data: certbot-etc:
保存并关闭文件。 让我们使用更新后的配置重新创建 webserver
服务:
docker-compose up -d --force-recreate --no-deps webserver
这将给出以下输出:
OutputRecreating webserver ... done
使用 docker-compose ps
检查服务:
docker-compose ps
我们将看到 mysql
、drupal
和 webserver
服务为 Up
而 certbot
将以 [X116X 退出] 状态消息:
Output Name Command State Ports -------------------------------------------------------------------------- certbot certbot certonly --webroot ... Exit 0 drupal docker-php-entrypoint php-fpm Up 9000/tcp mysql 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
现在,我们所有的服务都在运行,我们很高兴通过 Web 界面安装 Drupal。
第 6 步 — 通过 Web 界面完成安装
让我们通过 Drupal 的 Web 界面完成安装。
在 Web 浏览器中,导航到服务器的域。 记得用你自己的域名替换your_domain
:
https://your_domain
选择要使用的语言:
单击保存并继续。 我们将登陆安装配置文件页面。 Drupal 有多个配置文件,因此选择 Standard 配置文件并单击 保存并继续。
选择配置文件后,我们将前进到数据库配置页面。 选择数据库类型为 MySQL、MariaDB、Percona Server 或等效的,并输入 数据库名称、用户名和密码的值从对应于步骤 2 中 .env
文件中分别定义的 MYSQL_DATABASE
、MYSQL_USER
和 MYSQL_PASSWORD
的值。 点击Advanced Options,将Host的值设置为mysql
服务容器的名称。 点击保存并继续。
配置数据库后,它将开始安装 Drupal 默认模块和主题:
安装站点后,我们将登陆 Drupal 站点设置页面,用于配置站点名称、电子邮件、用户名、密码和区域设置。 填写信息点击【X37X】保存并继续【X58X】:
点击保存并继续后,我们可以看到Welcome to Drupal页面,说明我们的Drupal站点已经启动并运行成功。
现在我们的 Drupal 安装已经完成,我们需要确保我们的 SSL 证书会自动更新。
第 7 步 — 更新证书
Let's Encrypt 证书的有效期为 90 天,因此我们需要设置自动续订流程以确保它们不会失效。 一种方法是使用 cron
调度实用程序创建作业。 在这种情况下,我们将创建一个 cron
作业来定期运行一个脚本,该脚本将更新我们的证书并重新加载我们的 Nginx 配置。
让我们创建 ssl_renew.sh
文件来更新我们的证书:
nano ssl_renew.sh
添加以下代码。 请记住将目录名称替换为您自己的非 root 用户:
~/drupal/ssl_renew.sh
#!/bin/bash cd /home/sammy/drupal/ /usr/local/bin/docker-compose -f docker-compose.yml run certbot renew --dry-run && \ /usr/local/bin/docker-compose -f docker-compose.yml kill -s SIGHUP webserver
此脚本更改为 ~/drupal
项目目录并运行以下 docker-compose
命令。
docker-compose run
:这将启动一个certbot
容器并覆盖我们的certbot
服务定义中提供的command
。 我们在这里不使用certonly
子命令,而是使用renew
子命令,它将更新即将到期的证书。 我们在这里包含了--dry-run
选项来测试我们的脚本。docker-compose kill
:这将向webserver
容器发送SIGHUP
信号以重新加载 Nginx 配置。
关闭文件并通过运行以下命令使其可执行:
sudo 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 2. /usr/bin/vim.basic 3. /usr/bin/vim.tiny 4. /bin/ed Choose 1-4 [1]: ...
在文件末尾,添加以下行,将 sammy
替换为您的用户名:
crontab
... */5 * * * * /home/sammy/drupal/ssl_renew.sh >> /var/log/cron.log 2>&1
这会将作业间隔设置为每五分钟一次,因此我们可以测试我们的续订请求是否按预期工作。 我们还创建了一个日志文件 cron.log
来记录作业的相关输出。
五分钟后,使用tail
命令查看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
退出tail
进程。
我们现在可以修改 crontab 文件以在每周的第二天凌晨 2 点运行脚本。 将 crontab 的最后一行更改为以下内容:
crontab
... * 2 * * 2 /home/sammy/drupal/ssl_renew.sh >> /var/log/cron.log 2>&1
退出并保存文件。
现在,让我们从 ssl_renew.sh
脚本中删除 --dry-run
选项。 首先,打开它:
nano ssl_renew.sh
然后将内容更改为以下内容:
~/drupal/ssl_renew.sh
#!/bin/bash cd /home/sammy/drupal/ /usr/local/bin/docker-compose -f docker-compose.yml run certbot renew && \ /usr/local/bin/docker-compose -f docker-compose.yml kill -s SIGHUP webserver
我们的 cron
工作现在将通过在符合条件时更新它们来处理我们的 SSL 证书到期。
结论
在本教程中,我们使用 Docker Compose 创建了一个带有 Nginx Web 服务器的 Drupal 安装。 作为此工作流程的一部分,我们为我们希望与 Drupal 站点关联的域获取了 TLS/SSL 证书,并创建了一个 cron 作业以在必要时更新这些证书。
如果您想了解有关 Docker 的更多信息,请查看我们的 Docker 主题页面。