原由是由于 Let's Encrypt
的管理证书协议 ACMEv1
要在今年废弃掉,致使一台老服务器上的 https
失效。Let's Encrypt
官方推荐的方法是更新到支持 ACMEv2
的 certbot
版本。服务器是Ubuntu 14.04,支持ACMEv2
的客户端须要更新到Ubuntu 16.04 ,由于更新系统的不肯定性,因此想到了使用 Docker 去解决这个问题。node
项目结构大概是这样nginx
Nginx 作 Node Server 的反向代理,Certbot 用来获取并更新 Nginx 的 ssl 证书。docker
咱们的目的是用容器将三个服务整合在一块儿。npm
首先是要将 Node Server 进行Docker 化,直接在项目中加入 Dockerfile。json
FROM node:6.17.1 ENV NODE_ENV=production WORKDIR /server COPY ["package.json", "package-lock.json*", "yarn.lock", "npm-shrinkwrap.json*", "./"] RUN yarn --silent --production COPY . . EXPOSE 3000 RUN yarn global add gulp CMD ["yarn", "deploy"]
环境是直接用 node 官方的 v6.17.1
,WORKDIR 是以后命令的工做目录,安装依赖,暴露 3000 端口,执行部署命令。gulp
中途遇到了个小问题,由于deploy 命令后面是用 gulp 执行的,容器中局部依赖安装 gulp 找不到该命令,这里找到一篇文章来讨论这个问题 Why do we need to install gulp globally and locally? 这里我只是按最无脑的方法解决了,也能够使用 npm link
或者 alias node_modules/.bin/gulp
的方式去处理。服务器
Dockerfile 完成以后,用 docker build . -t nodeapp
来构建 docker image。app
在思考如何才能解决证书问题的时候,看到 certbot 的官网上有 Docker 的安装方式,可是由于容器间的通信问题,certbot 虽然能自动获取证书,但须要手动去安装到 nginx 上。less
大致的思路以下:ide
按照这个思路作的时候,发现已经有人造过轮子了。Boilerplate for nginx with Let’s Encrypt on docker-compose 他的处理方式是经过 docker-compose
来将 Nginx 和 certbot 两个容器经过上图的方式创建联系。
这里有个鸡生蛋蛋生鸡的问题,LetsEncrypt 的验证方式是访问域名中的一个 /.well-known/acme-challenge/xxx
地址来进行验证,返回的数据是由 certbot 来提供。但若是一开始不提供证书的话,nginx 就不能启动。做者的方法是先生成一个假证书来启动 nginx,而后用获取到的真证书替换掉假证书。
由于在上面 Boilerplate 中已经有一个脚原本处理,就不重复造轮子了。
nginx 的配置文件以下
server { listen 80; server_name example.org; server_tokens off; location /.well-known/acme-challenge/ { root /var/www/certbot; } location / { return 301 https://$host$request_uri; } } server { listen 443 ssl; server_name example.org; server_tokens off; ssl_certificate /etc/letsencrypt/live/example.org/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.org/privkey.pem; include /etc/letsencrypt/options-ssl-nginx.conf; ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; location / { proxy_pass http://example.org; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }
咱们如今已经得到了一个有了合法证书的 Nginx 服务器了,将 node server 接在 Nginx 的后面就大功告成了。 咱们直接在前面的boilerplate中提供的 docker-compose.yml
进行一些修改。
version: "3" services: nodeapp: image: nodeserver:1.0.0 container_name: nodeapp restart: unless-stopped volumes: - /data/usersFolder:/server/config ports: - "3000:3000" networks: - app-network nginx: image: nginx:1.15-alpine container_name: nginx_server restart: unless-stopped volumes: - ./data/nginx:/etc/nginx/conf.d - ./data/certbot/conf:/etc/letsencrypt - ./data/certbot/www:/var/www/certbot ports: - "80:80" - "443:443" networks: - app-network command: '/bin/sh -c ''while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g "daemon off;"''' certbot: image: certbot/certbot restart: unless-stopped container_name: certbot_one volumes: - ./data/certbot/conf:/etc/letsencrypt - ./data/certbot/www:/var/www/certbot entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'" networks: app-network: driver: bridge
经过建立一个 network 来让 nginx 直接和 node server 通讯,nginx 的conf 也能够写的比较顺滑。之前我记得能够使用 —link
来进行容器间通讯,但官方更推荐的作法仍是建立一个 network。
执行 boilerplate 中的脚本启动 nginx 并获取到证书,再经过 docker-compose up
启动 node server。 这样,咱们获得了一个带 https 并经过 nginx 做为反向代理的 node server。