如何使用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)堆栈,但安装单个组件非常耗时任务。 我们可以使用 DockerDocker 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_domainyour_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 守护程序主机上。 我们定义了两个网络,internalexternal,以保护 webserverdrupalmysql 服务的通信。

volumes 键用于定义命名卷 drupal-datadb-datacertbot-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

我们将看到 mysqldrupalwebserver 服务的 StateUp,而 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

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

这些块启用 SSL,因为我们已经包含了我们的 SSL 证书和密钥位置以及推荐的标头。 这些标头将使我们能够在 SSL LabsSecurity Headers 服务器测试站点上获得 A 评级。

我们的 rootindex 指令也位于此块中,步骤 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

我们将看到 mysqldrupalwebserver 服务为 Upcertbot 将以 [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_DATABASEMYSQL_USERMYSQL_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 主题页面