使用 Docker 部署 NodeJS + MongoDB 專案

yhlben發表於2019-06-17

最近在學習 Docker,並用 Docker 重新部署了cdfang-spider專案,使用 docker 後確實大幅度地降低了部署難度。如果你也想用 Docker 來部署自己的專案,那就讓我們一起往下看。

本文通過以下 3 個方面來聊聊 Docker:

  • Docker 發展史。
  • Docker 基礎。
  • Docker 專案實戰。

Docker 發展史

上古時代

在很久以前,釋出一個 App 應用,應該是這樣的。首先購買一臺物理伺服器,然後手動安裝對應的作業系統,搭建 App 應用執行環境,部署 App 應用,最後才能被其他人訪問。這樣做看似沒毛病,但可能會造成幾個問題:

  • 部署非常慢。
    • 購買物理伺服器到收貨需要時間。
    • 手動安裝作業系統需要時間。
    • 安裝 App 應用以及對應的環境需要時間。
  • 成本非常高。
    • 物理伺服器很貴。
  • 資源浪費。
    • 如果專案很小,不能充分利用這臺伺服器的資源。
  • 難於遷移和擴充套件。
    • 如果 CPU,記憶體,硬碟不夠,只能加物理裝置,但這個是有上限的。
  • 可能會被限定硬體廠商。

虛擬化時代

為了解決物理裝置的諸多問題,出現了虛擬機器。虛擬機器出現之後大大地降低了部署難度,要想部署一個應用程式,新建一個虛擬機器就可以了,還可以根據應用程式的大小,分配合適的系統資源。

虛擬技術有以下幾個特點:

  • 一個物理機的資源分配到了不同的虛擬機器裡。
  • 很容易擴充套件,加物理機 / 虛擬機器。
  • 很容易雲化,阿里雲,AWS 等。

虛擬化技術實現了物理層的隔離,但卻還有以下問題:

  • 每一個虛擬機器都是一個完整的作業系統,每次新建都得手動安裝一遍。
  • 虛擬機器中的專案環境每次也需要重新安裝。
  • 虛擬機器本身消耗的系統資源也比較多。

容器化時代

為了更方便的部署專案,出現了容器化技術,主要有以下幾個特點:

  • 實現應用程式及其環境打包。
  • 實現應用之間相互隔離、共享同一個作業系統核心。
  • 容器本身比較輕,相比虛擬機器,佔用的系統資源更少。

Docker 是容器化技術的一種,也是最流行的一個。Docker 提供了一種隔離機制,它將不同應用程式的依賴項和庫打包在一起,執行在不同的容器中,從而實現應用層的隔離

容器化技術大都是基於 Linux 核心提供的兩個機制:Cgroups(實現資源按需分配)和 Namespace(實現任務隔離)。

虛擬化 vs 容器化

虛擬化和容器化都是目前主流的的部署技術,兩者之間的差別如下:

  • 虛擬機器技術已經發展了很多年,配套技術和標準都已經標準化了,而容器最近幾年才興起,配套技術和標準還在完善中。
  • 虛擬機器由於有 GuestOS(虛擬機器作業系統) 存在,可以和宿主機執行不同 OS,而容器只能支援和宿主機核心相同的作業系統,隔離性相對較差。
  • 容器比虛擬機器明顯更輕量級,對宿主機作業系統而言,容器就跟一個程式差不多。因此容器有著更快的啟動速度、更方便的叢集管理等優點。同時由於沒有 GuestOS 存在,在容器中執行應用和直接在宿主機上幾乎沒有效能損失,效能上優於虛擬機器。

Docker 基礎

Docker 的核心是在 Docker Engine 層實現應用層的隔離。

Docker 分層
Application(應用層)
Container(容器層)
Docker Engine (隔離層)
Host OS 作業系統
infrastructure(基礎設施)

Docker 分為 Client 和 Server 兩個部分,我們在 Client 中執行 Docker 命令,最後建立的 Container 和 Image 則會在 Server 中執行。Dcoker 架構如下圖所示:

Docker 架構

Image

Image 主要用來打包應用程式以及它的依賴環境,為 Container 提供必要的環境以及安裝好的應用程式。Image 本身並不能執行,只能通過 Container 去執行。

Image 主要有以下幾點特徵:

  • 檔案和 meta data 的集合(root filesystem)。
  • 分層的,並且每一層都可以新增改變刪除檔案,成為一個新的 Image。
  • 不同 Image 可以共享相同的底層。
  • Image 本身是隻讀的。

Image 可以通過 Dockerfile 去構建,也可以通過 DockerHub 上去拉取。

Dockerfile 是一個文字檔案,其中包含構建 Image 的所有命令。Docker 可以通過 docker build 從 Dockerfile 中讀取命令來自動構建 Image。常用配置資訊可以參考下文 Dockerfile 檔案中的註釋,也建議大家閱讀官方文件 Dockerfile reference

Container

Container 是執行 Image 的例項,通過 docker run image即可啟動並執行一個 Container。

Container 主要有以下幾點特徵:

  • 通過 Image 建立。
  • 在 Image 之上建立一個 Container 層(可讀寫)。
  • 類比物件導向:類(Image) 和例項(Container)。
  • Image 負責 App 的儲存和分發,Container 負責執行 App。

Networks

使用 Dcoker 部署專案常常會生成很多個容器,這些容器預設只能通過 ip 地址進行訪問,但新建一個容器所產生的 ip 地址是不可控的,這就給容器之間通訊帶來了一定的麻煩。Docker 中使用 Network 來管理容器之間的通訊,只要兩個 Conteiner 處於同一個 Network 之中,就可以通過容器名去互相通訊。

Docker 中內建 5 中型別的 Network :

  • bridge(相同 bridge 中的 container 可以相互訪問)。
  • host(將 container 與宿主機的網路相連通,雖然很直接,但是卻破獲了 container 的隔離性)。
  • none 禁用所有網路。
  • overlay 叢集使用。
  • macvlan。

除了這 5 中 Network 之外,使用者也可以自定義編寫 Network Plugin。

Docker Compose

Docker Compose 是一個工具,這個工具可以通過一個 yml 檔案定義多容器的 Docker 應用。通過一條命令就可以根據 yml 檔案的定義去建立或者管理多個容器。接下來分別使用命令列和 Docker Compose 的方式來對比一下建立容器的方式。

不使用 Docker Compose 建立容器

docker pull lmjben/cdfang-spider
docker pull mongo
docker network create webapp-network
docker run -d --network webapp-network -v ~/data/db:/data/db mongo
docker run -p 8082:8082 --network webapp-network -d lmjben/cdfang-spider
複製程式碼

可見,手動建立容器,需要在命令列中手動執行很多命令,這些命令一旦敲錯了,就得重來,不便於容器的管理。

使用 Docker Compose 建立容器

1、新建 docker-compose.yml 檔案。

version: '3.7'
services:
  database:
    image: mongo
    restart: always
    volumes:
      - ~/data/db:/data/db
    networks:
      - webapp-network
  web:
    image: yhlben/cdfang-spider
    depends_on:
      - database
    ports:
      - 8082:8082
    networks:
      - webapp-network
networks:
  webapp-network:
    driver: bridge
複製程式碼

2、執行 docker-compose

docker-compose up -d
複製程式碼

可見,使用 Docker Compose 建立容器只需要提前編寫好 yml 檔案,然後執行一條命令就行了,比起手動敲命令,更加方便。

除此之外,Docker Compose 還可以使用 docker-compose -scale 擴充套件多個相容的容器,用來實現負載均衡,可以擴容,也可以減容。例如:實現無縫部署專案,先擴容一個新的 Container,當 Container 啟動完畢後,加入到叢集中,然後更新老容器,更新完後再加入叢集中。

Docker Compose 配置

Docker Compose 的配置檔案一般定義在 docker-compose.yml 檔案中,主要的配置項如下:

  • services
    • 一個 service 代表一個 container,這個 container 可以從 dockerHub 中的映象來建立,也可以使用本地 dockerfile build 出來的映象來建立。
    • service 的啟動類似 docker run,可以給 service 指定 network 和 volume 的引用。
  • networks
    • 定義 networks ,相當於執行 docker network create xxxx
  • volumes
    • 定義 volume ,相當於執行 docker volume create xxx

更多配置項可以參考官方文件 compose-file

Docker 專案實戰

接下來以 cdfang-spider 專案為例,使用 Docker 部署專案。

全手動部署

1、編寫 Dockerfile 檔案。

# 載入基礎映象
FROM mhart/alpine-node

# 註釋
LABEL maintainer = "lmjben <yinhengliben@gmail.com>"

# 建立工作目錄
RUN rm -rf /app
RUN mkdir /app
WORKDIR /app

# 安裝專案依賴
COPY . /app
RUN npm install
RUN npm run build
RUN mv ./dist/* ./

# 對外暴露埠
EXPOSE 8082

# 啟動 Image 時執行命令
CMD BUILD_ENV=docker node app.js
複製程式碼

2、通過 Dockerfile 檔案構建 Image。

docker build -t lmjben/cdfang-spider .
複製程式碼

3、拉取 mongo 官方 Image。

docker pull mongo
複製程式碼

4、建立 network,讓兩個容器可以相互通訊。

docker network create webapp-network
複製程式碼

5、執行容器

docker run -d --network webapp-network -v ~/data/db:/data/db mongo
docker run -p 8082:8082 --network webapp-network -d lmjben/cdfang-spider
複製程式碼

6、通過訪問 localhost:8082 訪問專案。

自動化部署

1、編寫 Dockerfile 檔案。

# 載入基礎映象
FROM mhart/alpine-node

# 註釋
LABEL maintainer = "lmjben <yinhengliben@gmail.com>"

# 建立工作目錄
RUN rm -rf /app
RUN mkdir /app
WORKDIR /app

# 安裝專案依賴
COPY . /app
RUN npm install
RUN npm run build
RUN mv ./dist/* ./

# 對外暴露埠
EXPOSE 8082

# 啟動 Image 時執行命令
CMD BUILD_ENV=docker node app.js
複製程式碼

2、在 dockerHub 上授權 github 專案,這樣當 github 專案有更新時,會自動執行 Dockerfile 進行構建,並將構建結果儲存到 dockerHub 倉庫中。

3、編寫 docker-compose.yml 檔案。

version: '3.7'
services:
  database:
    image: mongo
    restart: always
    volumes:
      - ~/data/db:/data/db
    networks:
      - webapp-network
  web:
    image: yhlben/cdfang-spider
    depends_on:
      - database
    ports:
      - 8082:8082
    networks:
      - webapp-network
networks:
  webapp-network:
    driver: bridge
複製程式碼

4、一鍵啟動,確保已安裝 docker-compose。

docker-compose up -d
複製程式碼

5、通過訪問 localhost:8082 訪問專案。

總結

通過 Docker 部署完專案後,感受很不錯,主要分以下幾點:

  • 使用 Docker Compose 一鍵啟動專案。
  • 再也不用在伺服器上安裝各種雜七雜八的環境,全部封裝到 Image 裡,啟動一個 Container 跑起來就行了,不用的時候直接刪除 Container 就行了,伺服器上不會受到任何汙染。
  • 對於耗時的 Image 構建過程,直接交給 dockerHub 去自動構建。

最後,附上專案原始碼地址:cdfang-spider

本專案使用單機部署,即所有的容器都在同一臺伺服器上。除此之外,docker 還支援分散式容器部署,可以使用 docker swarm 或者 kubernetes 來管理,目前還在學習中,爭取早日整理好分享給大家,感謝大家支援!

相關文章