在docker中使用nginx,并在nginx容器中使用acme.sh自动续签证书,本文中使用CloudFlare的dns验证进行自动续签。

因为默认docker nginx镜像中没有cron,但acme的自动续签功能又依赖于cron,所以只能在此之上构建一个带cron的docker镜像。

编写dockerfile

nano ./dockerfile

# 以nginx镜像为基础镜像
FROM nginx:latest

# 更新软件包并安装 cron 和 curl(虽然nginx镜像已经包含curl了)
RUN apt-get update && apt-get install -y cron curl && \
    curl https://get.acme.sh | sh

# 定义启动命令,启动cron和nginx
CMD ["sh", "-c", "cron & nginx -g 'daemon off;'"]

构建镜像

注意:过程需要连接到get.acme.sh和apt-get源,docker源,请保持网络通畅。

docker build -t nginx-acme -f dockerfile .

配置docker compose

使用刚才构建好的镜像nginx-acme

mkdir nginx-acme && cd nginx-acme

nano ./docker-compose.yaml

services:
  nginx:
    image: nginx-acme
    network_mode: "host"
    volumes:
      - ./ssl:/file/ssl
      - ./nginx.conf:/etc/nginx/nginx.conf
      - ./account.conf:/root/.acme.sh/account.conf
    restart: always
    command: ["tail", "-f", "/dev/null"]

创建nginx.conf文件,用于映射进容器来配置nginx,证书路径填/file/ssl/cert.pem,私钥路径填/file/ssl/key.pem

nano ./nginx.conf

……
listen 443 ssl;
#证书路径填/file/ssl/cert.pem
ssl_certificate /file/ssl/cert.pem;
#私钥路径填/file/ssl/key.pem
ssl_certificate_key /file/ssl/key.pem;
……

创建account.conf文件,用于映射进容器来配置acme.sh

nano ./account.conf

#LOG_FILE="/root/.acme.sh/acme.sh.log"
#LOG_LEVEL=1

#AUTO_UPGRADE="1"

#NO_TIMESTAMP=1

ACCOUNT_EMAIL="填邮箱"
CF_Token="填cloudflare的token"
CF_Zone_ID="填cloudflare区域id"
CF_Account_ID="填cloudflare的账户id"
CERT_HOME="/file/ssl"
Le_Challenge_Type="dns-01"
USER_PATH='/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'

配置好之后,直接启动docker compose服务手动申请证书一次,否则nginx会报错找不到证书文件。

docker compose up -d

申请测试证书

进入容器测试申请证书是否成功(使用docker ps查看nginx-acme容器id)

docker exec -it [nginx-acme容器id] /bin/bash

申请测试证书(仅临时测试用,不受信任)命令:

/root/.acme.sh/acme.sh --issue --staging -d "域名" --dns dns_cf --key-file /file/ssl/key.pem --cert-file /file/ssl/cert.pem

申请测试证书成功后就可以退出并关掉docker compose服务,并删掉./ssl 里面所有的文件。

exit

docker compose down

rm -rf ./ssl/*

启动服务

然后改一下 docker-compose.yaml,让其正常启动nginx和cron( 删掉最后一行,也就是command: ["tail", "-f", "/dev/null"]

services:
  nginx:
    image: nginx-acme
    network_mode: "host"
    volumes:
      - ./ssl:/file/ssl
      - ./nginx.conf:/etc/nginx/nginx.conf
      - ./account.conf:/root/.acme.sh/account.conf
    restart: always

启动docker compose服务

docker compose up -d

进入容器申请证书

docker exec -it [nginx-acme容器id] /bin/bash

申请普通证书命令:

/root/.acme.sh/acme.sh --issue -d "域名" --dns dns_cf --key-file /file/ssl/key.pem --cert-file /file/ssl/cert.pem --reloadcmd "nginx -s reload"

成功申请后即可exit退出,证书到期时cron会自动运行acme.sh实现续签,并执行nginx -s reload 重载nginx配置文件更新证书。

最终文件目录

.
|-- docker-compose.yaml
|-- account.conf
|-- nginx.conf
`-- ssl
    |-- *.domain_ecc
    |   |-- *.domain.cer
    |   |-- *.domain.conf
    |   |-- *.domain.csr
    |   |-- *.domain.csr.conf
    |   |-- *.domain.key
    |   |-- backup
    |   |-- ca.cer
    |   `-- fullchain.cer
    |-- cert.pem
    `-- key.pem

不需要担心容器被还原后自动续期失效,因为已经把acme.sh的配置文件account.conf映射到容器内,而且证书安装路径、nginx重载指令等配置都保存在./ssl/domain/domain.conf。

枯死的灌木!