其實這個話題很簡單,不是很想寫這篇文章。不過的確仍是有不少朋友在打包構建部署上存在一些問題,恰巧最近使用 Docker 部署了幾個 ThinkJS 相關的項目,因此仍是拿出來講說吧。須要提早說明的是本文並非 Docker 的基礎教程,默認你們都是瞭解 Docker 的。而後我會分享一下我以爲 ThinkJS 項目構建和部署過程當中可能須要注意的點,咱們先說說如何構建鏡像,而後再說一下可能出現的問題。html
FROM mhart/alpine-node:8.9.4
首先推薦你們基於 mhart/alpine-node 的鏡像來構建,由於真的很小(20M左右)!不過由於小有不少的構建工具都不是很全,若是趕上一些安裝時須要編譯的模塊時可能會缺乏環境,這個時候你能夠選擇手動去安裝環境,固然也能夠選擇使用 Docker 官方的 library/node 鏡像(800M左右),爲了省事推薦這種狀況下仍是使用後者比較好。前端
COPY package.json /animaris/package.json RUN npm i --production --registry=https://registry.npm.taobao.org
選好了基礎鏡像以後下面就須要拷貝項目文件了。這裏推薦你們先把 packge.json 文件 COPY 進來而後安裝依賴。由於依賴的變化比較小,能夠認爲一段時間內這部分的鏡像層都是穩定的。而項目代碼會隨着需求變動而常常變化。綜上所述,咱們選擇讓不變的鏡像層優先打包保證鏡像層的最大可複用性。node
一個正常的 ThinkJS 項目其實線上運行只須要如下幾個文件:nginx
app/
:項目原始碼文件夾,若是是未編譯項目的話對應的是 src/
目錄view/
:前端模板文件夾www/
:前端靜態資源文件夾production.js
:ThinkJS 啓動腳本因此只要按照順序把這些文件 COPY 進去就能夠了。git
你們日常都習慣使用 PM2 來啓動 NodeJS 項目,它最大的好處就是可以幫咱們守護項目進程,當進程被殺死的時候能幫咱們自動重啓。不過 Docker 自己就是有自動重啓的特性的,因此在不少層面上 Docker 和 PM2 一塊使用都有點衝突。其實由於 Docker 充當了守護的角色,咱們徹底能夠直接使用 node production.js
命令去啓動。如下是一個完整的 ThinkJS 項目 Dockerfile 示例:github
FROM mhart/alpine-node:8.9.4 WORKDIR /animaris COPY package.json /animaris/package.json RUN npm i --production --registry=https://registry.npm.taobao.org COPY src /animaris/src COPY view /animaris/view COPY www /animaris/www COPY production.js /animaris/production.js ENV DOCKER=true EXPOSE 8360 CMD [ "node", "/animaris/production.js" ]
使用以下命令進行構建:docker
docker build -t lizheming/animaris ./Dockerfile
以後使用以下命令運行鏡像,便可使用 http://localhost:8360 訪問網站:shell
docker run -p 8360:8360 lizheming/animaris
使用 Docker 必定不能忘記的特性就是容器銷燬後容器內的全部資源都是會被銷燬的,下回會從新初始化,因此若是是須要持久化保存的應避免寫到容器中,須要選擇外部的持久化存儲,例如 Volume 共享卷或者 S3 等相關服務。npm
ThinkJS 項目啓動後通常會有 logs
, runtime
兩個目錄會寫入文件。其中 logs
是用來存儲線上日誌用的,這個最好使用共享卷的形式外載出來,方便以後排查問題。runtime
這個是 ThinkJS 運行時臨時文件的存儲地方,例如 cache 和 session 等。session 會話默認是使用文件類型存儲的,若是使用 Docker 的話推薦選擇 MySQL 等外部存儲。其它的功能有相關需求也能夠參考 session 服務。固然若是懶得用直接把 runtime
共享卷出來也是能夠的。json
還有就是若是有用戶上傳類的需求會上傳到本地文件夾上也要記得共享出來,不然丟數據就完蛋叻!
ThinkJS 一直主張在生產環境中使用 Nginx 來處理靜態資源,這樣不須要通過 Node 層直接 Server 轉發性能會更高。不過這樣就給鏡像打包形成了必定的麻煩,由於靜態資源也被打包到項目鏡像裏去了,而 Nginx 鏡像正常是沒辦法跨鏡像讀取到文件的,因此就死解了。
在 ThinkJS 中是利用 think-resource
這個中間件來處理靜態資源的訪問的,而後它在線上環境的狀態是 enabled: false
。其實靜態資源過一層 Node 並消耗不了多少資源,除非對性能有嚴苛要求的,我建議均可以直接把這個功能打開。這樣全部的請求都統一成 Nginx 轉發到 Node 鏡像,解決了 Nginx 須要跨鏡像讀取文件的問題。具體可參考官方文檔的「爲何上線後靜態資源訪問不了?」
module.exports = [ ... { handle: 'resource', enable: true // 始終開啓,默認爲 `enable: isDev` 表示只再開發環境下開啓 }, ... ]
固然若是真的要在打包成一個鏡像的狀況下用 Nginx 處理靜態資源也不是沒有辦法,咱們能夠利用共享捲來操做。Node 鏡像經過將鏡像文件共享卷的形式映射到本地,而後 Nginx 鏡像經過共享卷的形式將以前本地映射的文件再映射到鏡像中。這樣經過本地的一層轉發便可實現 Nginx 的跨鏡像訪問靜態資源。
# ThinkJS 鏡像 docker run \ -v ./www:/app/www \ -p 8360:8360 \ lizheming/animaris # Nginx 鏡像 docker run \ -v ./nginx.conf:/etc/nginx/conf.d/animaris.conf \ -v ./www:/var/www/animaris/www \ -p 80:80 \ nginx
server { listen 80; server_name animaris.eming.li; root /var/www/animaris/www; location ~ /static/ { etag on; expires max; } }
日常咱們是使用 development.js
和 production.js
來區分開發環境和生產環境的,然鵝這麼打包以後發現都是生產環境了。這樣有時候咱們須要不一樣環境不一樣配置就不太好弄了。這裏推薦你們使用環境變量來區分。
think.env = process.ENV.ENV;
docker run -e ENV=test -p 8360:8360 lizheming/animaris
有用戶提出疑問 #1106,說「網站500了爲啥 logs 裏沒看到日誌?」。這是由於雖然 ThinkJS 內部的日誌都是用 think-logger
模塊處理了,可是由於跨模塊或者啓動時機的問題,有一些日誌沒辦法走日誌模塊記錄並存儲到文件,會直接打到終端裏。因此當沒有日誌的時候咱們能夠考慮去 docker logs
中撈一下終端的日誌,說不定有意外的驚喜。
雖然文字寫了不少,除了一些普適問題須要考慮一下以外,其實總體來看真個構建過程仍是很簡單的。同時也歡迎你們有什麼問題和疑問留言交流。