前置閱讀
什麼是 Docker?
讓部署 Server 像手機安裝 App 一樣簡單
過去當你寫完了 Server 想要部署到伺服器上,需要在伺服器上配置一套執行環境,一旦伺服器上環境配置失敗,還可能出現整個服務掛掉的情況。
Docker 正是用 Linux 裡叫做 “容器” 的技術實現了這個特性,通過容器把你的服務需要的環境和程式碼都打包進去,保證可以正常執行,當你需要部署到 Server 上的時候,只需要伺服器上下載這個容器,然後執行就可以了。
編譯,打包成容器,分發
利用容器的這個原理,各種服務,部落格,網站,論壇,都可以使用由別人製作好容器,你再安裝到自己的伺服器上,容器之間互不干擾,乾淨整潔,大大簡化了維護和配置成本。
什麼是 “容器” ?
技術上來講,容器的實現比較複雜也是一種虛擬化技術,但是概念非常好理解。
拿 iOS 的 App 打個比方,一個 iOS App 要執行起來,需要四部分
- iOS 核心(Darwin)
- 系統提供的 Frameworks
- 各種依賴的第三方 Frameworks
- 程式自己
當你編譯出來一個 App 的時候,這個 App 相當於一個 容器
它包含了 各種依賴的第三方 Frameworks
和 程式自己
,而 iPhone 相當於你程式的宿主,提供了 iOS 核心
和 系統提供的 Frameworks
.
在 Linux 上,一個程式能跑起來,需要三部分
- Linux 核心
- 程式依賴的其他程式
- 程式自己
那麼一個容器裡,包含了 程式依賴的其他程式
和 程式自己
,核心則共用宿主的。
Linux 的容器環境和 iOS 執行 App 的 Sandbox 沙盒技術也非常相似,容器內無法直接訪問外部,但外部則可以訪問容器內。
安裝 Docker
通過 Docker 官網 的 Get Docker 指南,可以輕鬆把 Docker 裝在系統上,以下我將會以 Mac 為例。
在 Docker Store 裡下載 Mac 版本的安裝包
Mac 的核心是不支援容器技術的,因此 Mac 版本的 Docker 帶了一個微型的 Linux 髮型版,通過虛擬化技術執行在你的 Mac 上。
安裝好之後執行,你的右上角就會出現一個小鯨魚
此時終端也可以執行 docker
命令了
➜ ~ docker -v
Docker version 17.03.0-ce, build 60ccb22複製程式碼
如何構建一個容器?
構建容器前需要先構建容器映象,分為兩步
- 構建一個能夠執行你的程式的環境
- 把程式放進去
那麼第一步,就是從 Dockerfile 開始
編寫 Dockerfile
Dockerfile 是容器映象的描述檔案,告訴 Docker 如何建立一個容器映象。我們之前使用的 Vapor 框架也提供了一個可以執行 Vapor 的容器映象,一起來看看它的 Dockerfile 是怎麼寫的
FROM vapor/vapor:1.0-xenial
# Check if we should install mysql header files to the container (Defualt: false)
ARG INSTALL_MYSQL=false
RUN if [ ${INSTALL_MYSQL} = true ]; then \
apt-get update && \
apt-get install -y libmysqlclient20 libmysqlclient-dev && \
rm -r /var/lib/apt/lists/* \
;fi
# Check if we should install postgres header files to the container (Defualt: false)
ARG INSTALL_PGSQL=false
RUN if [ ${INSTALL_PGSQL} = true ]; then \
apt-get update && \
apt-get install -y libpq-dev && \
rm -r /var/lib/apt/lists/* \
;fi
# Check if we should install sqlite header files to the container (Defualt: false)
ARG INSTALL_SQLITE=false
RUN if [ ${INSTALL_SQLITE} = true ]; then \
apt-get update && \
apt-get install -y libsqlite3-dev && \
rm -r /var/lib/apt/lists/* \
;fi
# Set work dir to /vapor
WORKDIR /vapor
EXPOSE 8080複製程式碼
這一大坨其實就是四步,也是構建一個容器映象最基本的四步
第一步,設定基礎容器
FROM vapor/vapor:1.0-xenial複製程式碼
意思是基於 vapor/vapor:1.0-xenial
這個容器映象來構建,像程式設計中類的繼承關係一樣,容器映象也可以繼承,這樣的好處是每個容器映象只需要維護好自己的那部分,分發和保持健壯性更方便。
Docker 在構建時,會自動去本地和 Docker 雲端的倉庫裡尋找被 FROM 指定的映象。
第二步,安裝必要的依賴
# Check if we should install mysql header files to the container (Defualt: false)
ARG INSTALL_MYSQL=false
RUN if [ ${INSTALL_MYSQL} = true ]; then \
apt-get update && \
apt-get install -y libmysqlclient20 libmysqlclient-dev && \
rm -r /var/lib/apt/lists/* \
;fi
#### Check if we should install postgres header files to the container (Defualt: false)
ARG INSTALL_PGSQL=false
RUN if [ ${INSTALL_PGSQL} = true ]; then \
apt-get update && \
apt-get install -y libpq-dev && \
rm -r /var/lib/apt/lists/* \
;fi
# Check if we should install sqlite header files to the container (Defualt: false)
ARG INSTALL_SQLITE=false
RUN if [ ${INSTALL_SQLITE} = true ]; then \
apt-get update && \
apt-get install -y libsqlite3-dev && \
rm -r /var/lib/apt/lists/* \
;fi複製程式碼
父級的容器映象必然缺少部分你所需要的環境,因此你可以在這一步通過 RUN
命令來進行一些依賴安裝。
第三步,設定工作目錄
# Set work dir to /vapor
WORKDIR /vapor複製程式碼
我們將容器內的 /vapor
目錄作為工作目錄,意味著如果你 ssh
進入這個容器,會直接進入到這個目錄,容器將把這個資料夾當作各種命令執行的根目錄。
第四部,設定訪問埠
EXPOSE 8080複製程式碼
意味著容器會開啟 8080 埠的外部訪問,宿主機器執行這個容器的時候,可以使用 -p xxxx:8080
的方式,將宿主機器的埠和容器的埠連線起來。
這樣當使用者在網路上訪問宿主機器的 xxxx
埠的時候,就會變成通過 8080
埠訪問容器。
此時,如果容器內的 Server 監聽的是 8080
埠,那麼伺服器就可以接收到請求啦。
構建自己的 Dockerfile
首先,在終端進入到 vapor 專案根目錄,建立一個 Dockerfile。
為了跑起來 Vapor,我需要按照以下思路構建一個 Dockerfile
- 選一個有 Swift 執行環境的父容器映象
- 將 Swift 專案原始碼拷貝到工作目錄
- 配置編譯專案的環境並編譯
- 設定訪問埠 8000
# 選一個有 Swift 執行環境的容器映象 vapor/vapor:latest
FROM vapor/vapor:latest
WORKDIR /vapor
# 將 Swift 專案原始碼拷貝到工作目錄
ADD . /vapor
# 配置編譯專案的環境並編譯
RUN apt-get update && apt-get install -y openssl libssl-dev --no-install-recommends && \
vapor build && \
apt-get remove -y libssl-dev && \
rm -rf /var/lib/apt/lists/*
# 設定訪問埠 8000
EXPOSE 8000複製程式碼
現在執行 docker build . --tag sudongpo_image
Docker 就會先根據 FROM
引用的映象啟動一個容器作為編譯環境,然後逐條執行當前目錄裡的 Dockerfile
裡的命令,執行完畢後,會輸出一行資訊
Successfully built sudongpo複製程式碼
在執行完 Dockerfile 退出的時候,Docker 會把當時的容器狀態進行快照,變成一個不可改動的東西,叫做 容器映象
,並命名為 sudongpo_image
。
容器映象是什麼?
那麼映象和容器什麼關係呢?
容器映象相當於手機上的 ROM,是隻讀的部分,當容器建立的時候,這部分會變成容器的 Base Layer ,就像是一個鎖死的 Sketch 圖層,一個灰色的圓。
同時,Docker 會為容器建立一個可讀寫層,就像覆蓋在被鎖死的圖層上的另外一個圖層
在容器執行的時候,雖然你看起來可以修改任何東西,使得圖片變成了下圖的模樣
但本質上來講,底層灰色的圓圈,沒有被改動,只是表現層,即執行態的層面被改動了
這些變動會被 Docker 儲存在 /var/lib/docker/aufs/diff/xxxxx_hash/
裡(根據 Docker 的配置不同,路徑也會有所不同)。
我們往下看,來繼續梳理兩者的關係。
現在,你可以使用 docker run --name sudongpo -p 8000:8000 sudongpo_image vapor run
來建立並執行一個容器。
被隱藏的細節
docker run
其實是將 docker create
和 docker start
兩個命令合併在了一起。
這行命令做了以下幾件事
- 使用
sudongpo_image
這個容器映象作為基礎執行環境建立容器 --name sudongpo
將容器命名為sudongpo
-p 8000:8000
對映宿主的8000
埠到容器的8000
埠vapor run
當容器啟動後,在工作目錄執行vapor run
命令- 將
-p 8000:8000
vapor run
作為容器的配置項儲存起來,每次啟動容器的時候都按照這個配置進行啟動 - 執行容器
自此,你可以使用 docker start sudongpo
來開啟這個容器,或者使用 docker stop sudongpo
來關閉這個容器,容器啟動的時候,會自動完成埠對映,並執行 vapor run
。
在下一篇博文中,我們將把這個映象部署到你的伺服器上。