先前使用Container的方式搭配Nginx架設了網站,為了讓網站有SSL憑證也試著用freessl方式取得憑證。因為免費的憑證每90天要手動更新一次,在網路上查詢自動化更新的資料後,發現可以使用certbot這個套件來進行網站的憑證安裝及自動更新。於是就這樣讓網站能順利自動更新SSL憑證。
但有個問題,因為letsencrypt工具將憑證儲存在container的image中,雖然可以定期更新,但更新的資料並沒有儲存到container的image中,因為我們並沒有定期去執行:
sudo docker commit webserver <label>
於是每當重新啟動container時就要手動進入container中去執行certbot renew及service nginx reload。想要玩container的人也一定會想要將這個手動的步驟給自動化,於是我就寫了一個shell讓container在啟動時就去行執行它:
certbot renew -quiet
nginx -g "daemon off;"
乍看之下一切都很完美,只要我有需要重新啟動這組網站的container image時,它就會自動更新需要更新的憑證,並啟動nginx來使用新的憑證。但…有一天災難來了…
某一次,因為更新了主要host的ubuntu套件,所以docker也重啟了,造成原本執行中的網站container image也停止而要重啟。於是我很正常地執行了基本的docker指令叫起我的網站container image,但發現有某此網站並無法正常取得SSL憑證。因為懶得去查實際的原因,所以使用了工程師慣用的最後手段(每次OS有問題懶得去排除就是只好整機重灌)我就再重新啟動了我的網站container image,這次更慘了,所有的網站都無法取得新的SSL憑證了。
先說明一下,為何我說「所有的網站」呢!? 主要是因為我有建立多個子網站,所以每個子網站我都會去要一份SSL憑證。算一算我大約有8組子網站,所以每次重啟網站的container image就會讓letsencrypt去重新取得8份新的憑證。
因為大絕招已無法解決問題了,這下真的要發揮柯南的精神:真相只有一個,只好去看看原因何在。
首先因為是無法用certbot取得SSL憑證,當然就是要由它查起了。於是我重新啟動網站container image,這次先不執行自動更新的模式,而是採用手動的方式,我登入網站container image的shell模式,並試著執行:
certbot renew
果不其然地,看到畫面中輸出了一些紅色的錯誤訊息,在查了一下這些錯誤訊息後,才知道原來letsencrypt取得SSL憑證是有次數限制的,每週不能超過5次。天啊! 我有8個子網站,這樣評估下來難怪第一次重啟動只有部份網站無法順利取得SSL憑證(正確來說應該有3個無法順利取得)。因為我懶得查,又重啟一次,結果就全部都無法取得SSL憑證了。
讀到這裏,可能有人會問說,不對啊,第一次啟動時不是有正常取得5份嗎? 沒錯是有正常取得5份,但這5份並沒有存起來啊,因為我們的網站container image在停止後,其中的資料如沒有執行docker commit的話是沒有儲存到的。
追根究底來說,就是letsencrypt更新的SSL憑證並沒有被儲存下來。要能讓資料被正常地儲存下來最好的方式就是資料要被在docker volume中才行。
letsencrypt資料儲存目錄
letsencrypt設定檔及資料儲存根目錄位於 /etc/letsencrypt,在其中有兩個主要用來儲存SSL憑證用的目錄:
- /etc/letsencrypt/archive
- /etc/letsencrypt/live
/etc/letsencrypt/live中儲存的是目前使用中的SSL憑證,它會依每個不同的網址再區分不同的目錄,裏面的憑證資料則是用symbol link的方式再連結到/etc/letsencrypt/archive裏各網址的子目錄中的憑證檔案。
/etc/letsencrypt/archive目錄中除了目前正在使用中的SSL憑證資料外,另外也會保留多份舊的SSL憑證資料。
目前我調整的方式是先將這兩個目錄的資料複制一份到docker volume中,再用symbol link的方式於/etc/letsencrypt目錄中建立live及archive的連結。
經過這樣的調整後,certbot所更新的SSL憑證資料就可以被保留下來,每次重啟container image時也會使用到最新的SSL憑證資料。