當Node.js遇見Docker

farsun發表於2021-09-09


Node.js Best Practices - How to Become a Better Developer in 2017提到的幾點,我們Fundebug深有同感:

使用ES6

使用Promise

使用LTS

使用Docker

...

想必大家都知道ES6,Promise以及LTS,那Docker是啥玩意啊?翻遍Node文件也沒見蹤跡啊!

當Node.js遇見Docker

GitHub倉庫: Fundebug/nodejs-docker

什麼是Docker?

Docker是最流行的的容器工具,沒有之一。本文並不打算深入介紹Docker,不過可以從幾個簡單的角度來理解Docker。

從程式的角度理解Docker

在Linux中,所有的程式構成了一棵樹。可以使用pstree命令進行檢視:

pstree

init─┬─VBoxService───7*[{VBoxService}]

     ├─acpid

     ├─atd

     ├─cron

     ├─dbus-daemon

     ├─dhclient

     ├─dockerd─┬─docker-containe─┬─docker-containe─┬─redis-server───2*[{redis-server}]

     │         │                 │                 └─8*[{docker-containe}]

     │         │                 ├─docker-containe─┬─mongod───16*[{mongod}]

     │         │                 │                 └─8*[{docker-containe}]

     │         │                 └─11*[{docker-containe}]

     │         └─13*[{dockerd}]

     ├─6*[getty]

     ├─influxd───9*[{influxd}]

     ├─irqbalance

     ├─puppet───{puppet}

     ├─rpc.idmapd

     ├─rpc.statd

     ├─rpcbind

     ├─rsyslogd───3*[{rsyslogd}]

     ├─ruby───{ruby}

     ├─sshd─┬─sshd───sshd───zsh───pstree

     │      ├─sshd───sshd───zsh

     │      └─sshd───sshd───zsh───mongo───2*[{mongo}]

     ├─systemd-logind

     ├─systemd-udevd

     ├─upstart-file-br

     ├─upstart-socket-

     └─upstart-udev-br

可知,init程式為所有程式的根(root),其PID為1。

Docker將不同應用的程式隔離了起來,這些被隔離的程式就是一個個容器。隔離是基於兩個Linux核心機制實現的,Namesapce和Cgroups。

Namespace可以從UTD、IPC、PID、Mount,User和Network的角度隔離程式。比如,不同的程式將擁有不同PID空間,這樣容器中的程式將看不到主機上的程式,也看不到其他容器中的程式。這與Node.js中模組化以隔離變數的名稱空間的思想是異曲同工的。

透過Cgroups,可以限制程式對CPU,記憶體等資源的使用。簡單地說,我們可以透過Cgroups指定容器只能使用1G記憶體。

從程式角度理解Docker,那每一個Docker容器就是被隔離的程式及其子程式。上文pstree的輸出中可以分辨出2個容器: mongodb和redis。

從檔案的角度理解Docker

基於Namespace與Cgroups的容器工具其實早已存在,例如Linux-VServer,OpenVZ,LXC。然而,真正引爆容器技術的卻是後來者Docker。為什麼呢?個人覺得是因為Docker映象以及Dockerfile。

在Linux中,一切皆檔案,程式的執行離不開各種各樣的檔案。跑一個簡單的Node.js程式,傳統的做法是手動安裝各種依賴然後執行;而Docker則是將所有依賴(包括作業系統,Node,NPM模組,原始碼)打包到一個Docker映象中,然後基於這個映象執行容器。

Docker映象可以透過Docker倉庫共享給其他人,這樣他們只需要下載映象即可執行程式。想象一下,當我們需要在另一臺主機(比如生產伺服器,新同事的機器)上執行一個Node.js應用,僅僅需要下載對應的Docker映象就可以了,是不是很方便呢?

Docker映象可以透過文字檔案,即Dockerfile進行定義。不妨看一個簡單的例子(由於不可抗力,這個Dockerfile構建大概會失敗,僅作為參考):

# 基於Ubuntu

FROM ubuntu

# 安裝Node.js與NPM

RUN apt-get update && apt-get -y install nodejs npm

# 安裝NPM模組:Express

RUN npm install express

# 新增原始碼

ADD app.js /

其中,FROM,RUN與ADD為Dockerfile命令。結合註釋,該Dockerfile的含義非常直白。基於這個Dockerfile,使用docker build命令就可以構建對應的Docker映象。基於這個Docker映象,就可以執行Docker容器來執行app.js:

var express = require("express");

var app = express();

app.get("/", function(req, res)

{

    res.send("Hello Fundebug!n");

});

app.listen(3000);

Dockerfile實際上是將Docker映象程式碼化了,另一方面也是將安裝依賴的過程程式碼化了,於是我們就可以像管理原始碼一樣使用git對Dockerfile進行版本管理。

為啥用Docker?

當你的系統越來越複雜的時候,你會發現Docker的價值。

從應用架構角度理解Docker

剛開始,你只需要寫一個Node.js程式,掛載一個靜態網站;然後,你做了一個使用者賬號系統,這時需要資料庫了,比如說MySQL; 後來,為了提升效能,你引入了Memcached快取;終於有一天,你決定把前後端分離,這樣可以提高開發效率;當使用者越來越多,你又不得不使用Nginx做反向代理; 對了,隨著功能越來越多,你的應用依賴也會越來越多...總之,你的應用架構只會越來越複雜。不同的元件的安裝,配置與執行步驟各不相同,於是你不得不寫一個很長的文件給新同事,只為了讓他搭建一個開發環境。

使用Docker的話,你可以為不同的元件逐一編寫Dockerfile,分別構建映象,然後執行在各個容器中。這樣做,將複雜的架構統一了,所有元件的安裝和執行步驟統一為幾個簡單的命令:

構建Docker映象: docker build

上傳Docker映象: docker push

下載Docker映象: docker pull

執行Docker容器: docker run

從應用部署角度理解Docker

通常,你會有開發,測試和生產伺服器,對於某些應用,還會需要進行構建。不同步驟的依賴會有一些不同,並且在不同的伺服器上執行。如果手動地在不同的伺服器上安裝依賴,是件很麻煩的事情。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4289/viewspace-2819064/,如需轉載,請註明出處,否則將追究法律責任。

相關文章