如何使用Nginx、Let'sEncrypt和DockerCompose保护容器化的Node.js应用程序
介绍
有多种方法可以增强 Node.js 应用程序的灵活性和安全性。 使用像 Nginx 这样的 反向代理 可以为您提供负载平衡请求、缓存静态内容和实现 传输层安全 (TLS) 的能力。 在您的服务器上启用加密的 HTTPS 可确保与您的应用程序之间的通信保持安全。
在容器上使用 TLS/SSL 实现反向代理涉及一组与直接在主机操作系统上工作不同的过程。 例如,如果您从 Let's Encrypt 获取运行在服务器上的应用程序的证书,您可以直接在主机上安装所需的软件。 容器允许您采取不同的方法。 使用 Docker Compose,您可以为您的应用程序、Web 服务器和 Certbot 客户端 创建容器,使您能够获取证书。 通过执行这些步骤,您可以利用容器化工作流的模块化和可移植性。
在本教程中,您将使用 Docker Compose 部署带有 Nginx 反向代理的 Node.js 应用程序。 您将获得与您的应用程序关联的域的 TLS/SSL 证书,并确保它从 SSL Labs 获得高安全等级。 最后,您将设置一个 cron 作业来更新您的证书,以便您的域保持安全。
先决条件
要遵循本教程,您将需要:
- 一个 Ubuntu 18.04 服务器,一个具有
sudo
权限的非 root 用户,以及一个活动的防火墙。 有关如何设置这些的指导,请参阅此 初始服务器设置指南 。 - Docker 和 Docker Compose 安装在您的服务器上。 有关安装 Docker 的指导,请遵循 如何在 Ubuntu 18.04 上安装和使用 Docker 的步骤 1 和 2。 有关安装 Compose 的指导,请遵循 如何在 Ubuntu 18.04 上安装 Docker Compose 的步骤 1。
- 一个注册的域名。 本教程将自始至终使用 example.com。 您可以在 Freenom 免费获得一个,或使用您选择的域名注册商。
- 为您的服务器设置了以下两个 DNS 记录。 您可以按照 this Introduction to DigitalOcean DNS 了解如何将它们添加到 DigitalOcean 帐户的详细信息(如果您正在使用):
- 带有
example.com
的 A 记录指向您服务器的公共 IP 地址。 - 带有
www.example.com
的 A 记录指向您服务器的公共 IP 地址。
- 带有
第 1 步 — 克隆和测试节点应用程序
作为第一步,我们将使用 Node 应用程序代码克隆存储库,其中包括我们将用于使用 Compose 构建应用程序映像的 Dockerfile。 我们可以首先通过使用 docker 运行命令 构建和运行应用程序来测试应用程序,无需反向代理或 SSL。
在非 root 用户的主目录中,从 DigitalOcean 社区 GitHub 帐户 克隆 nodejs-image-demo 存储库。 此存储库包含 如何使用 Docker 构建 Node.js 应用程序中描述的设置中的代码。
将存储库克隆到名为 node_project
的目录中:
git clone https://github.com/do-community/nodejs-image-demo.git node_project
切换到 node_project
目录:
cd node_project
在此目录中,有一个 Dockerfile,其中包含使用 Docker 节点:10 映像 和当前项目目录的内容构建 Node 应用程序的说明。 您可以通过键入以下内容查看 Dockerfile 的内容:
cat Dockerfile
OutputFROM node:10-alpine RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app WORKDIR /home/node/app COPY package*.json ./ USER node RUN npm install COPY --chown=node:node . . EXPOSE 8080 CMD [ "node", "app.js" ]
这些说明通过将项目代码从当前目录复制到容器并使用 npm install
安装依赖项来构建 Node 映像。 他们还利用 Docker 的 缓存和图像分层 ,将包含项目列出的依赖项的 package.json
和 package-lock.json
的副本与应用程序的其余部分的副本分开代码。 最后,指令指定容器将以非根 node 用户身份运行,并在应用程序代码和 node_modules
目录上设置适当的权限。
有关此 Dockerfile 和 Node 映像最佳实践的更多信息,请参阅如何使用 Docker 构建 Node.js 应用程序的 第 3 步中的完整讨论。
要在没有 SSL 的情况下测试应用程序,您可以使用 docker build 和 -t
标志构建和标记映像。 我们将图像称为 node-demo
,但您可以随意将其命名为其他名称:
docker build -t node-demo .
构建过程完成后,您可以使用 docker images 列出您的图像:
docker images
您将看到以下输出,确认应用程序映像构建:
OutputREPOSITORY TAG IMAGE ID CREATED SIZE node-demo latest 23961524051d 7 seconds ago 73MB node 10-alpine 8a752d5af4ce 3 weeks ago 70.7MB
接下来,使用 docker run
创建容器。 我们将在此命令中包含三个标志:
-p
:这会发布容器上的端口并将其映射到我们主机上的端口。 我们将在主机上使用端口80
,但如果您有另一个进程在该端口上运行,您可以根据需要随意修改此端口。 有关其工作原理的更多信息,请参阅 Docker 文档中关于 端口绑定 的讨论。-d
:这会在后台运行容器。--name
:这让我们可以给容器起一个好记的名字。
运行以下命令来构建容器:
docker run --name node-demo -p 80:8080 -d node-demo
使用 docker ps 检查正在运行的容器:
docker ps
您将看到确认您的应用程序容器正在运行的输出:
OutputCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 4133b72391da node-demo "node app.js" 17 seconds ago Up 16 seconds 0.0.0.0:80->8080/tcp node-demo
您现在可以访问您的域来测试您的设置:http://example.com
。 记得用自己的域名替换example.com
。 您的应用程序将显示以下登录页面:
现在您已经测试了应用程序,您可以停止容器并删除图像。 再次使用 docker ps
得到你的 CONTAINER ID
:
docker ps
OutputCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 4133b72391da node-demo "node app.js" 17 seconds ago Up 16 seconds 0.0.0.0:80->8080/tcp node-demo
使用 docker stop 停止容器。 请务必将此处列出的 CONTAINER ID
替换为您自己的应用程序 CONTAINER ID
:
docker stop 4133b72391da
您现在可以使用 docker system prune 和 -a
标志删除停止的容器和所有图像,包括未使用的和悬空的图像:
docker system prune -a
在输出提示时键入 y
以确认您要删除已停止的容器和图像。 请注意,这也会删除您的构建缓存。
测试完您的应用程序映像后,您可以继续使用 Docker Compose 构建其余设置。
第 2 步 — 定义 Web 服务器配置
有了我们的应用程序 Dockerfile,我们可以创建一个配置文件来运行我们的 Nginx 容器。 我们将从一个最小配置开始,其中包括我们的域名、文档根、代理信息和一个位置块,用于将 Certbot 的请求定向到 .well-known
目录,它将在其中放置一个临时文件以验证我们域的 DNS 是否解析到我们的服务器。
首先,在当前项目目录下为配置文件创建一个目录:
mkdir nginx-conf
使用 nano
或您喜欢的编辑器打开文件:
nano nginx-conf/nginx.conf
添加以下服务器块以将用户请求代理到您的 Node 应用程序容器,并将 Certbot 的请求定向到 .well-known
目录。 请务必将 example.com
替换为您自己的域名:
~/node_project/nginx-conf/nginx.conf
server { listen 80; listen [::]:80; root /var/www/html; index index.html index.htm index.nginx-debian.html; server_name example.com www.example.com; location / { proxy_pass http://nodejs:8080; } location ~ /.well-known/acme-challenge { allow all; root /var/www/html; } }
这个服务器块将允许我们将 Nginx 容器作为反向代理启动,它将请求传递给我们的 Node 应用程序容器。 它还将允许我们使用 Certbot 的 webroot 插件 来获取我们域的证书。 这个插件依赖于 HTTP-01 验证方法,它使用 HTTP 请求来证明 Certbot 可以从响应给定域名的服务器访问资源。
完成编辑后,保存并关闭文件。 想了解更多关于Nginx服务器和位置块算法的知识,请参考这篇文章了解Nginx服务器和位置块选择算法。
有了 Web 服务器配置详细信息,我们可以继续创建我们的 docker-compose.yml
文件,这将允许我们创建我们的应用程序服务和我们将用来获取证书的 Certbot 容器。
第三步——创建 Docker Compose 文件
docker-compose.yml
文件将定义我们的服务,包括 Node 应用程序和 Web 服务器。 它将指定诸如命名卷之类的详细信息,这对于在容器之间共享 SSL 凭据以及网络和端口信息至关重要。 它还允许我们在创建容器时指定要运行的特定命令。 该文件是定义我们的服务如何协同工作的核心资源。
打开当前目录中的文件:
nano docker-compose.yml
首先,定义应用服务:
~/node_project/docker-compose.yml
version: '3' services: nodejs: build: context: . dockerfile: Dockerfile image: nodejs container_name: nodejs restart: unless-stopped
nodejs
服务定义包括以下内容:
build
:这定义了 Compose 构建应用程序映像时将应用的配置选项,包括context
和dockerfile
。 如果你想使用像 Docker Hub 这样的注册表中的现有镜像,你可以使用 image 指令 来代替,其中包含有关你的用户名、存储库和镜像标签的信息。context
:这定义了应用程序映像构建的构建上下文。 在这种情况下,它是当前项目目录。dockerfile
:这指定 Compose 将用于构建的 Dockerfile - 您在 Step 1 中查看的 Dockerfile。image
、container_name
:这些将名称应用于图像和容器。restart
:定义重启策略。 默认为no
,但我们已将容器设置为重启,除非它停止。
请注意,我们不包括此服务的绑定挂载,因为我们的设置侧重于部署而不是开发。 有关详细信息,请参阅有关 bind mounts 和 volumes 的 Docker 文档。
为了启用应用程序和 Web 服务器容器之间的通信,我们还将在重启定义下方添加一个名为 app-network
的桥接网络:
~/node_project/docker-compose.yml
services: nodejs: ... networks: - app-network
像这样的用户定义的桥接网络可以在同一 Docker 守护进程主机上的容器之间进行通信。 这简化了应用程序内的流量和通信,因为它打开了同一桥接网络上容器之间的所有端口,同时不向外界公开任何端口。 因此,您可以选择性地仅打开公开前端服务所需的端口。
接下来,定义 webserver
服务:
~/node_project/docker-compose.yml
... webserver: image: nginx:mainline-alpine container_name: webserver restart: unless-stopped ports: - "80:80" volumes: - web-root:/var/www/html - ./nginx-conf:/etc/nginx/conf.d - certbot-etc:/etc/letsencrypt - certbot-var:/var/lib/letsencrypt depends_on: - nodejs networks: - app-network
我们为 nodejs
服务定义的一些设置保持不变,但我们还进行了以下更改:
image
:这告诉 Compose 从 Docker Hub 中提取最新的 Alpine-based Nginx 映像。 有关alpine
镜像的更多信息,请参阅 如何使用 Docker 构建 Node.js 应用程序的第 3 步。ports
:这会暴露端口80
以启用我们在 Nginx 配置中定义的配置选项。
我们还指定了以下命名卷和绑定挂载:
web-root:/var/www/html
:这会将我们网站的静态资产添加到容器上的/var/www/html
目录中,复制到名为web-root
的卷中。./nginx-conf:/etc/nginx/conf.d
:这会将宿主机上的 Nginx 配置目录绑定挂载到容器上的相关目录,确保我们对宿主机上的文件所做的任何更改都会反映在容器中。certbot-etc:/etc/letsencrypt
:这会将我们域的相关 Let's Encrypt 证书和密钥挂载到容器上的相应目录。certbot-var:/var/lib/letsencrypt
:这会将 Let's Encrypt 的默认工作目录挂载到容器上的相应目录。
接下来,为 certbot
容器添加配置选项。 请务必将域名和邮箱信息替换为您自己的域名和联系邮箱:
~/node_project/docker-compose.yml
... certbot: image: certbot/certbot container_name: certbot volumes: - certbot-etc:/etc/letsencrypt - certbot-var:/var/lib/letsencrypt - web-root:/var/www/html depends_on: - webserver 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
中的域证书和密钥,certbot-var
中的 Let's Encrypt 工作目录,以及 web-root
中的应用程序代码]。
同样,我们使用 depends_on
来指定 certbot
容器应在 webserver
服务运行后启动。
我们还包括一个 command
选项,用于指定容器启动时要运行的命令。 它包括带有以下选项的 certonly
子命令:
--webroot
:这告诉 Certbot 使用 webroot 插件将文件放在 webroot 文件夹中进行身份验证。--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.com
和www.example.com
。 请务必将这些替换为您自己的域首选项。
作为最后一步,添加卷和网络定义。 请务必将此处的用户名替换为您自己的非 root 用户:
~/node_project/docker-compose.yml
... volumes: certbot-etc: certbot-var: web-root: driver: local driver_opts: type: none device: /home/sammy/node_project/views/ o: bind networks: app-network: driver: bridge
我们的命名卷包括我们的 Certbot 证书和工作目录卷,以及我们网站静态资产的卷 web-root
。 在大多数情况下,Docker 卷的默认驱动程序是 local
驱动程序,它在 Linux 上接受类似于 挂载命令 的选项。 多亏了这一点,我们能够使用 driver_opts
指定驱动程序选项列表,在运行时将主机上的 views
目录(包含我们应用程序的静态资产)挂载到卷上。 然后可以在容器之间共享目录内容。 关于views
目录内容的更多信息,请参见如何使用Docker构建Node.js应用程序的第2步。
完成后 docker-compose.yml
文件将如下所示:
~/node_project/docker-compose.yml
version: '3' services: nodejs: build: context: . dockerfile: Dockerfile image: nodejs container_name: nodejs restart: unless-stopped networks: - app-network webserver: image: nginx:mainline-alpine container_name: webserver restart: unless-stopped ports: - "80:80" volumes: - web-root:/var/www/html - ./nginx-conf:/etc/nginx/conf.d - certbot-etc:/etc/letsencrypt - certbot-var:/var/lib/letsencrypt depends_on: - nodejs networks: - app-network certbot: image: certbot/certbot container_name: certbot volumes: - certbot-etc:/etc/letsencrypt - certbot-var:/var/lib/letsencrypt - web-root:/var/www/html depends_on: - webserver 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: certbot-var: web-root: driver: local driver_opts: type: none device: /home/sammy/node_project/views/ o: bind networks: app-network: driver: bridge
准备好服务定义后,您就可以启动容器并测试您的证书请求了。
第 4 步 — 获取 SSL 证书和凭证
我们可以使用 docker-compose up 启动我们的容器,它将按照我们指定的顺序创建和运行我们的容器和服务。 如果我们的域请求成功,我们将在输出中看到正确的退出状态,并在 webserver
容器的 /etc/letsencrypt/live
文件夹中看到正确的证书。
使用 docker-compose up
和 -d
标志创建服务,这将在后台运行 nodejs
和 webserver
容器:
docker-compose up -d
您将看到确认您的服务已创建的输出:
OutputCreating nodejs ... done Creating webserver ... done Creating certbot ... done
使用 docker-compose ps 检查服务的状态:
docker-compose ps
如果一切顺利,您的 nodejs
和 webserver
服务应该是 Up
并且 certbot
容器将退出并显示 0
状态消息:
Output Name Command State Ports ------------------------------------------------------------------------ certbot certbot certonly --webroot ... Exit 0 nodejs node app.js Up 8080/tcp webserver nginx -g daemon off; Up 0.0.0.0:80->80/tcp
如果您在 nodejs
和 webserver
服务的 State
列中看到 Up
以外的任何内容,或者 0
以外的退出状态对于 certbot
容器,请务必使用 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 Dec 23 16:48 . drwxr-xr-x 9 root root 4096 Dec 23 16:48 .. -rw-r--r-- 1 root root 740 Dec 23 16:48 README drwxr-xr-x 2 root root 4096 Dec 23 16:48 example.com
现在您知道您的请求将成功,您可以编辑 certbot
服务定义以删除 --staging
标志。
打开docker-compose.yml
:
nano docker-compose.yml
找到文件中具有 certbot
服务定义的部分,并将 command
选项中的 --staging
标志替换为 --force-renewal
标志,这将告诉 Certbot您要申请与现有证书具有相同域的新证书。 certbot
服务定义现在应该如下所示:
~/node_project/docker-compose.yml
... certbot: image: certbot/certbot container_name: certbot volumes: - certbot-etc:/etc/letsencrypt - certbot-var:/var/lib/letsencrypt - web-root:/var/www/html depends_on: - webserver 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
您将看到指示您的证书请求成功的输出:
Outputcertbot | 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-03-26. 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 证书和密钥位置。 它还将涉及指定我们的 Diffie-Hellman 组,我们将用于 Perfect Forward Secrecy。
由于您要重新创建 webserver
服务以包含这些添加,因此您现在可以停止它:
docker-compose stop webserver
接下来,在当前项目目录中为您的 Diffie-Hellman 密钥创建一个目录:
mkdir dhparam
使用 openssl 命令 生成您的密钥:
sudo openssl dhparam -out /home/sammy/node_project/dhparam/dhparam-2048.pem 2048
生成密钥需要一些时间。
要将相关的 Diffie-Hellman 和 SSL 信息添加到您的 Nginx 配置中,请首先删除您之前创建的 Nginx 配置文件:
rm nginx-conf/nginx.conf
打开另一个版本的文件:
nano nginx-conf/nginx.conf
将以下代码添加到文件以将 HTTP 重定向到 HTTPS 并添加 SSL 凭据、协议和安全标头。 请记住将 example.com
替换为您自己的域:
~/node_project/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; server_tokens off; ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; ssl_buffer_size 8k; ssl_dhparam /etc/ssl/certs/dhparam-2048.pem; ssl_protocols TLSv1.2 TLSv1.1 TLSv1; ssl_prefer_server_ciphers on; ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5; ssl_ecdh_curve secp384r1; ssl_session_tickets off; ssl_stapling on; ssl_stapling_verify on; resolver 8.8.8.8; location / { try_files $uri @nodejs; } location @nodejs { proxy_pass http://nodejs:8080; 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 } root /var/www/html; index index.html index.htm index.nginx-debian.html; }
HTTP 服务器块为 .well-known/acme-challenge
目录的 Certbot 续订请求指定 webroot。 它还包括一个 重写指令 ,它将 HTTP 请求指向根目录到 HTTPS。
HTTPS 服务器块启用 ssl
和 http2
。 要详细了解 HTTP/2 如何在 HTTP 协议上迭代以及它对网站性能的好处,请参阅 如何在 Ubuntu 18.04 上设置支持 HTTP/2 的 Nginx 的介绍。 此块还包括一系列选项,以确保您使用的是最新的 SSL 协议和密码,并且 OSCP 装订已打开。 OSCP 装订允许您在初始 TLS 握手 期间提供来自 证书颁发机构 的时间戳响应,这可以加快身份验证过程。
该块还指定您的 SSL 和 Diffie-Hellman 凭据和密钥位置。
最后,我们将代理传递信息移至此块,包括带有 try_files 指令的位置块,将请求指向我们的别名 Node.js 应用程序容器,以及该别名的位置块,其中包括安全标头,这将使我们能够在 SSL Labs 和 Security Headers 服务器测试站点等内容上获得 A 评级。 这些标头包括 X-Frame-Options、X-Content-Type-Options、Referrer Policy、Content-Security-Policy、和X-XSS-保护。 HTTP Strict Transport Security (HSTS) 标头已被注释掉——只有在您了解其含义并评估其 “预加载”功能 时才启用此功能。
完成编辑后,保存并关闭文件。
在重新创建 webserver
服务之前,您需要在 docker-compose.yml
文件中的服务定义中添加一些内容,包括 HTTPS 的相关端口信息和 Diffie-Hellman 卷定义。
打开文件:
nano docker-compose.yml
在 webserver
服务定义中,添加以下端口映射和 dhparam
命名卷:
~/node_project/docker-compose.yml
... webserver: image: nginx:latest container_name: webserver restart: unless-stopped ports: - "80:80" - "443:443" volumes: - web-root:/var/www/html - ./nginx-conf:/etc/nginx/conf.d - certbot-etc:/etc/letsencrypt - certbot-var:/var/lib/letsencrypt - dhparam:/etc/ssl/certs depends_on: - nodejs networks: - app-network
接下来,将 dhparam
卷添加到 volumes
定义中:
~/node_project/docker-compose.yml
... volumes: ... dhparam: driver: local driver_opts: type: none device: /home/sammy/node_project/dhparam/ o: bind
与 web-root
卷类似,dhparam
卷会将存储在主机上的 Diffie-Hellman 密钥挂载到 webserver
容器中。
完成编辑后保存并关闭文件。
重新创建 webserver
服务:
docker-compose up -d --force-recreate --no-deps webserver
使用 docker-compose ps
检查您的服务:
docker-compose ps
您应该看到输出表明您的 nodejs
和 webserver
服务正在运行:
Output Name Command State Ports ---------------------------------------------------------------------------------------------- certbot certbot certonly --webroot ... Exit 0 nodejs node app.js Up 8080/tcp webserver nginx -g daemon off; Up 0.0.0.0:443->443/tcp, 0.0.0.0:80->80/tcp
最后,您可以访问您的域以确保一切都按预期工作。 将浏览器导航到 https://example.com
,确保将 example.com
替换为您自己的域名。 您将看到以下登录页面:
您还应该在浏览器的安全指示器中看到锁定图标。 如果您愿意,可以导航到 SSL 实验室服务器测试登录页面 或 Security Headers 服务器测试登录页面 。 我们包含的配置选项应该让您的网站在这两个方面都获得 A 评级。
第 6 步 — 更新证书
Let's Encrypt 证书的有效期为 90 天,因此您需要设置一个自动续订过程以确保它们不会失效。 一种方法是使用 cron
调度实用程序创建作业。 在这种情况下,我们将使用脚本来安排 cron
作业,该脚本将更新我们的证书并重新加载我们的 Nginx 配置。
在您的项目目录中打开一个名为 ssl_renew.sh
的脚本:
nano ssl_renew.sh
将以下代码添加到脚本以更新您的证书并重新加载您的 Web 服务器配置:
~/node_project/ssl_renew.sh
#!/bin/bash COMPOSE="/usr/local/bin/docker-compose --no-ansi" DOCKER="/usr/bin/docker" cd /home/sammy/node_project/ $COMPOSE run certbot renew --dry-run && $COMPOSE kill -s SIGHUP webserver $DOCKER system prune -af
此脚本首先将 docker-compose
二进制文件分配给名为 COMPOSE
的变量,并指定 --no-ansi
选项,它将在没有 ANSI 控制的情况下运行 docker-compose
命令字符。 然后它对 docker
二进制文件执行相同的操作。 最后,它切换到 ~/node_project
目录并运行以下 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
如果这是您第一次编辑此文件,您将被要求选择一个编辑器:
crontab
no crontab for root - using an empty one Select an editor. To change later, run 'select-editor'. 1. /bin/ed 2. /bin/nano <---- easiest 3. /usr/bin/vim.basic 4. /usr/bin/vim.tiny Choose 1-4 [2]: ...
在文件的底部,添加以下行:
crontab
... */5 * * * * /home/sammy/node_project/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.) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Killing webserver ... done
您现在可以修改 crontab
文件以设置每日间隔。 例如,要在每天中午运行脚本,您可以将文件的最后一行修改为如下所示:
crontab
... 0 12 * * * /home/sammy/node_project/ssl_renew.sh >> /var/log/cron.log 2>&1
您还需要从 ssl_renew.sh
脚本中删除 --dry-run
选项:
~/node_project/ssl_renew.sh
#!/bin/bash COMPOSE="/usr/local/bin/docker-compose --no-ansi" DOCKER="/usr/bin/docker" cd /home/sammy/node_project/ $COMPOSE run certbot renew && $COMPOSE kill -s SIGHUP webserver $DOCKER system prune -af
您的 cron
作业将确保您的 Let's Encrypt 证书不会在符合条件时更新它们而失效。 您还可以 使用 Logrotate 实用程序 设置日志轮换,以轮换和压缩您的日志文件。
结论
您已经使用容器来设置和运行带有 Nginx 反向代理的 Node 应用程序。 您还为应用程序的域保护了 SSL 证书,并设置了 cron
作业以在必要时更新这些证书。
如果您有兴趣了解有关 Let's Encrypt 插件的更多信息,请参阅我们关于使用 Nginx 插件 或 独立插件 的文章。
您还可以通过查看以下资源了解有关 Docker Compose 的更多信息:
- 如何在 Ubuntu 18.04 上安装 Docker Compose。
- 如何在 Ubuntu 16.04 上使用 Docker 和 Docker Compose 配置持续集成测试环境。
- 如何使用 Docker Compose 设置 Laravel、Nginx 和 MySQL 。
Compose 文档 也是了解更多关于多容器应用程序的重要资源。