使用 nginx-proxy 自动续期 ssl 证书

1,411 阅读3分钟

免费的 ssl 证书基本上一年要续一次,一次还只能一个二级域名,我们可以使用 letsencrypt 来生成 ssl 证书,到期自动续期

不想看官方文档可以直接看项目实战

基本用法(使用 nginx-proxy 容器)

github 地址

必须在 nginx-proxy 容器上声明三个可写卷,以便可以与 letsencrypt-nginx-proxy-companion 容器共享它们:

/etc/nginx/certs 用于存储证书和私钥(nginx-proxy 容器只读)。
/etc/nginx/vhost.d 修改 vhosts 的配置(必需,CA 可以访问 http-01 挑战文件)。
/usr/share/nginx/html 写入 http-01 challenge files

使用示例:

第 1 步-Nginx 代理

使用声明的其他三个卷启动 nginx-proxy:

\$ docker run --detach \
 --name nginx-proxy \
 --publish 80:80 \
 --publish 443:443 \
 --volume /docker/compose/nginx1/certs:/etc/nginx/certs \
 --volume /docker/compose/nginx1/vhost.d:/etc/nginx/vhost.d \
 --volume /docker/compose/nginx1/html:/usr/share/nginx/html \
 --volume /var/run/docker.sock:/tmp/docker.sock:ro \
 jwilder/nginx-proxy

/var/run/docker.sock 将容器内的主机 docker socket()绑定到 nginx-proxy/tmp/docker.sock 。

第 2 步-Letsencrypt-nginx-proxy-companion

启动 letsencrypt-nginx 的代理-伴侣的容器中,充分利用卷的 nginx 代理有--volumes-from:

\$ docker run --detach \
 --name nginx-proxy-letsencrypt1 \
 --volumes-from nginx-proxy1 \
 --volume /var/run/docker.sock:/var/run/docker.sock:ro \
 --env "DEFAULT_EMAIL=549250640@qq.com" \
 jrcs/letsencrypt-nginx-proxy-companion

主机 docker 套接字也必须绑定到此容器中,这次为/var/run/docker.sock。

尽管是可选的,但建议您通过 DEFAULT_EMAIL 环境变量提供有效的默认电子邮件地址,以便“加密”可以警告您证书即将过期并允许您恢复帐户。

第 3 步-代理容器

一旦 nginx-proxy 和 letsencrypt-nginx-proxy-companion 容器都已启动并运行,请使用环境变量启动要代理的任何容器,VIRTUAL_HOST 并将 LETSENCRYPT_HOST 两者都设置为代理容器将要使用的域。

VIRTUAL_HOST 通过控制代理 nginx 的代理和 LETSENCRYPT_HOST 控制证书的创建和 SSL 通过启用 letsencrypt-nginx-proxy-companion

仅当容器是公开可访问的,VIRTUAL_HOST 并且容器的 LETSENCRYPT_HOST 变量和变量都设置为可正确解析给主机的域时,才会颁发证书。


\$ docker run --detach \
 --name web-app \
 --env "VIRTUAL_HOST=subdomain.yourdomain.tld" \
 --env "LETSENCRYPT_HOST=subdomain.yourdomain.tld" \
 nginx

被代理的容器必须使用 EXPOSE 其 Dockerfile 中的指令或使用--exposeto docker run 或标志来公开要代理的端口 docker create。

如果代理容器侦听并暴露了默认端口以外的其他端口 80,则可以强制 nginx-proxy 将其与 VIRTUAL_PORT 环境变量一起使用。

使用 Grafana 的示例(暴露并监听端口 3000):

\$ docker run --detach \
 --name grafana \
 --env "VIRTUAL_HOST=othersubdomain.yourdomain.tld" \
 --env "VIRTUAL_PORT=3000" \
 --env "LETSENCRYPT_HOST=othersubdomain.yourdomain.tld" \
 --env "LETSENCRYPT_EMAIL=mail@yourdomain.tld" \
 grafana/grafana

对要代理的任何其他容器重复步骤 3。

以上就是官方文档的翻译

下面是分享一下我的使用场景,比如我要部署一个 spring-boot+vue 的前后端分离项目

首先我要分配两个二级域名

api.ishare365.com.cn 给后端 tomcat 使用(不一定是 tomcat,或者是 springboot 打包后的 docker 镜像)
h5.ishare365.com.cn 给前端 nginx 使用

在 docker-compose 中使用

一般情况下我还是比较喜欢 docker-compose 中编排 docker 容器,不管是一个容器还是多个,毕竟可以写在 docker-compose 文件中,就算时间久了我还是知道怎么运行的。

首先可以创建包含 nginx-proxy 和 letsencrypt-nginx-proxy-companion 的容器

version: "3.1"
services:
 nginx-proxy:
 container_name: nginx-proxy
 image: jwilder/nginx-proxy
 ports:
 - "80:80"
 - "443:443"
 volumes:
 - ./conf:/etc/nginx/conf.d
 - ./vhost:/etc/nginx/vhost.d
 - ./html:/usr/share/nginx/html
 - ./nginx/log:/var/log/nginx
 - ./dhparam:/etc/nginx/dhparam
 - ./certs:/etc/nginx/certs:ro
 - /var/run/docker.sock:/tmp/docker.sock:ro
 environment:
 - VIRTUAL_HOST=ishare365.com.cn,*.ishare365.com.cn
 - LETSENCRYPT_HOST=ishare365.com.cn,*.ishare365.com.cn
 - LETSENCRYPT_EMAIL=549250640@qq.com
 restart: unless-stopped

 letsencrypt:
 container_name: nginx-proxy-le
 image: jrcs/letsencrypt-nginx-proxy-companion
 depends_on:
 - nginx-proxy
 volumes:
 - ./vhost:/etc/nginx/vhost.d
 - ./html:/usr/share/nginx/html
 - ./dhparam:/etc/nginx/dhparam
 - ./certs:/etc/nginx/certs
 - /var/run/docker.sock:/var/run/docker.sock:ro
 environment:
 - NGINX_PROXY_CONTAINER=nginx-proxy
 restart: unless-stopped

networks:
 default:
 external:
 name: nginx-proxy

创建要代理的容器 tomcat 和 nginx

要注意的是如果容器暴露的不是 8080 端口需要使用 VIRTUAL_PORT 做关联 比如 tomcat 是 8080

version: "3.1"
services:
 tomcat:
 image: bitnami/tomcat:latest
 container_name: tomcat
 volumes:
 # - /usr/local/docker/tomcat/webapps:/usr/local/tomcat/webapps
 - ./webapps:/bitnami
 environment:
 TZ: "Asia/Shanghai"
 TOMCAT_USERNAME: "uuu111111" # 这是假的不用试了
 TOMCAT_PASSWORD: "111111"
 TOMCAT_ALLOW_REMOTE_MANAGEMENT: "1"
 VIRTUAL_PORT: "8080"
 VIRTUAL_HOST: "api.ishare365.com.cn"
 LETSENCRYPT_HOST: "api.ishare365.com.cn"
 restart: always
 networks:
 - default_network

 openresty:
 image: openresty/openresty:1.19.3.1-2-alpine
 container_name: openresty
 expose:
 - "80"
 volumes:
 - ./www:/www/:rw
 - ./openresty/conf.d/:/etc/nginx/conf.d/:ro
 # - ./openresty/ssl/:/ssl:rw
 - ./openresty/openresty.conf:/usr/local/openresty/nginx/conf/nginx.conf:ro
 - ./openresty/fastcgi-php.conf:/usr/local/openresty/nginx/conf/fastcgi-php.conf:ro
 - ./openresty/fastcgi_params:/usr/local/openresty/nginx/conf/fastcgi_params:ro
 - ./openresty/log/:/var/log/nginx/:rw
 environment:
 TZ: "Asia/Shanghai"
 VIRTUAL_HOST: "h5.ishare365.com.cn"
 LETSENCRYPT_HOST: "h5.ishare365.com.cn"
 networks:
 - default_network

networks:
 default_network:
 external: true

然后执行

docker-compose up -d

docker ps

CONTAINER ID        IMAGE                                    COMMAND                  CREATED             STATUS              PORTS                                      NAMES
63f1cebc8eee        bitnami/tomcat:latest                    "/opt/bitnami/script…"   7 seconds ago       Up 5 seconds        8080/tcp, 8443/tcp                         tomcat
fdb7ee0d51be        openresty/openresty:1.19.3.1-2-alpine    "/usr/local/openrest…"   7 seconds ago       Up 5 seconds        80/tcp                                     openresty

可以看到两个容器已经启动

然后分别访问
api.ishare365.com.cn
h5.ishare365.com.cn
可以看到浏览器中已经显示为 https 的小绿锁了

我们可以随便丢一个 vue 项目到 nginx 上去试试