原由是由於 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。