最近在學習 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 架構如下圖所示:
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
。
- 定義 networks ,相當於執行
- volumes
- 定義 volume ,相當於執行
docker volume create xxx
。
- 定義 volume ,相當於執行
更多配置項可以參考官方文件 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 來管理,目前還在學習中,爭取早日整理好分享給大家,感謝大家支援!