Docker环境下Nginx HTTPS配置:Let’s Encrypt证书申请与部署

随着互联网的不断发展,网络安全问题日益凸显,HTTPS作为一种安全的网络通信协议,逐渐成为了网站标配。目前市面上有很多SSL供应商,如阿里、腾讯、DigiCert等。
选择Let's Encrypt作为SSL证书提供商,主要是因为其免费、易用、受信任的特点。Let's Encrypt打破了传统证书市场的垄断,使得任何网站都能轻松获得免费的SSL证书,降低了运营成本。同时,其提供的自动化工具简化了证书申请、安装和续期的过程,提高了效率。
本文将详细介绍如何在Docker环境下为Nginx部署Let's Encrypt证书,配置HTTPS。为了使教程更简单,本文使用 Docker-Compose进行操作。

一、准备工作

在开始之前,你需要了解一些情况:
1、请确保已经安装了Docker及Docker-Compose运行环境(可以参考此文章);
2、此外,你还需要一个域名(这里假定域名为example.xyz),并将该域名的DNS解析指向你的服务器IP地址;
3、使用非root 用户操作文章中的内容,并创建docker-compose 文件夹及docker-compose.yml;
4、确保防火墙允许80及443端口通过,如果使用了云服务,需要在安全组中开放对应端口访问权限;

二、搭建Nginx服务

利用vim编辑docker-compose.yml 文件,并配置如下内容:
version: '3.1'

services:
  webserver:
    image: nginx:1.22.1
    ports:
      - 80:80
      - 443:443
    restart: always
    volumes:
      - ./nginx/conf/:/etc/nginx/conf.d/:ro
      - ./certbot/www:/var/www/certbot/:ro
      - ./nginx/logs/:/var/log/nginx/:rw
关键信息解析,定义了一个webserver服务,镜像为nginx-1.22.1;
  1. ports:表示定义了容器和宿主机之间的端口映射:
    • - 80:80 将宿主机的80端口映射到容器的80端口,这通常是HTTP服务的默认端口。
    • - 443:443将宿主机的443端口映射到容器的443端口,这通常是HTTPS服务的默认端口。
  2. restart: always表示容器的重启策略。always意味着无论容器退出时状态如何,Docker都会尝试重新启动它。
  3. volumes:定义了容器和宿主机之间的卷映射,用于共享文件或目录。
    • - ./nginx/conf/:/etc/nginx/conf.d/:ro表示配置文件的映射,当前目录下加载nginx的配置;
    • - ./certbot/www:/var/www/certbot/:ro表示证书访问地址,用于后续证书的生成和验证;
    • - ./nginx/logs/:/var/log/nginx/:rw表示映射nginx的日志文件,会将容器中的日志写入到主机挂载目录;
请注意,我在卷声明的末尾添加了一个:ro。 表示“只读”。容器将永远无权将文件更新到此文件夹中。这没什么大不了的,但这非常的有用。它可以避免浪费宝贵的几个小时的调试时间。
将以下配置文件添加到本地文件夹中(./nginx/conf/),文件名为my.conf。不要忘记使用您自己的域名进行更新。
server {
    listen 80;
    listen [::]:80;

    server_name example.xyz www.example.xyz;
    server_tokens off;

    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }
}

这里的配置非常简单,以一个特殊的块,/.well-known/acme-challenge/ 用于后续let's encrypt证书的验证;

可以进入docker-compose 文件夹,执行以下命令,nginx服务即可访问:
docker-compose up -d

二、搭建Certbot服务

以certbot为客户端,采用webroot的方式生成ssl证书,并完成验证。

为此,我们需要使用 certbot 的 docker 映像,并将其作为服务添加到我们的 Docker-Compose 文件中(完整版本)。
version: '3.1'

services:
  webserver:
    image: nginx:latest
    ports:
      - 80:80
      - 443:443
    restart: always
    volumes:
      - ./nginx/conf/:/etc/nginx/conf.d/:ro
      - ./certbot/www:/var/www/certbot/:ro
      - ./certbot/conf/:/etc/nginx/ssl/:ro
      - ./nginx/logs/:/var/log/nginx/:rw
  certbot:
    image: certbot/certbot:latest
    volumes:
      - ./certbot/www/:/var/www/certbot/:rw
      - ./certbot/conf/:/etc/letsencrypt/:rw

执行docker-compose up -d 命令,

我们现在有两个服务,一个用于 nginx,一个用于 Certbot。您可能已经注意到他们声明了相同的卷。这是为了让他们一起交流。
您现在可以通过运行以下命令(注意替换为自己的域名)来测试一切是否正常。您应该会收到一条成功消息,例如“试运行成功”。
docker-compose run --rm  certbot certonly --webroot --webroot-path /var/www/certbot/ --dry-run -d example.xyz -d www.example.xyz

如果以上命令运行成功,去掉--dry-run 重新运行。在./certbot/conf 可以看到已生成的证书文件夹。

既然我们有了这些证书,剩下的就是nginx上的配置了(完整版本):
server {
    listen 80;
    listen [::]:80;

    server_name example.xyz www.example.xyz;
    server_tokens off;

    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }

    location / {
        return 301 https://example.xyz$request_uri;
    }
}

server {
    listen 443 default_server ssl http2;
    listen [::]:443 ssl http2;

    server_name example.org;

    ssl_certificate /etc/nginx/ssl/live/example.xyz/fullchain.pem;
    ssl_certificate_key /etc/nginx/ssl/live/example.xyz/privkey.pem;
    
    location / {
    	# ...
    }
}
现在重新加载 nginx 服务器,你的Nginx服务器已经可以通过HTTPS访问了。

三、证书续订

Certbot 和 Let's Encrypt 可能会遇到的一个小问题是证书只能持续 3 个月,为了Https的可用性,您将需要定期更新您使用的证书。
由于我们有这个 Docker 环境,因此更新 Let's Encrypt 证书比以往任何时候都更容易!执行以下命令即可:
docker compose run --rm certbot renew

也可以通过定时任务执行, 在docker-compose 下增加 文件,内容如下:

#!/bin/bash

# 对应的docker-compose 目录
cd /path/to/docker-compose

# 执行 certbot renew
docker-compose run --rm certbot renew

# 检查 certbot 是否成功生成了新的证书
if [[ $? -eq 0 ]]; then
    echo "Certbot renewed certificates successfully."
    
    # 重新加载 Nginx 以应用新的 SSL 证书
    docker-compose exec webserver nginx -s reload
else
    echo "Certbot did not renew any certificates."
fi

保存此脚本为 renew_certificates.sh 并赋予执行权限:

chmod +x renew_certificates.sh
设置cron定时任务,例如每月的第一天凌晨执行此脚本
crontab -e

输入 对应的数字 ,选择 /usr/bin/vim.basic  模式,进入vim。如果选择错误,可以按照说明退出后,执行select-editor 进行重新选择。

然后在打开的编辑器中添加一行:
0 0 1 * * /path/to/docker-compose/renew_certificates.sh >/dev/null 2>&1
这行的意思是在每个月的第一天凌晨0点0分执行 /path/to/renew_certificates.sh 脚本,并将输出重定向到/dev/null以避免邮件通知。
请根据实际情况替换 /path/to/ 为实际脚本存放路径。
注意:这里的例子假设你已经有一个使用Docker Compose管理的包含certbot和nginx容器的服务。docker-compose exec nginx nginx -s reload 命令用于在Nginx容器内部执行nginx -s reload来加载新的SSL证书。如果您的部署结构不同,可能需要相应调整命令。

四、总结

执行以上脚本如果修改目录,一定要注意不同目录的对应关系。否则可能会出现不同错误。