前言
在「微服務」一詞流行的現今,「容器」也同時廣為人知。其中由 dotCloud 開源出的 Docker 受大家歡迎。 Docker 的技術和應用很深也很廣,但對於初次學習的人來說,光是從學習執行容器就可以從中體會到 Docker 迷人之處。
本篇的目的:
- 對 Docker 有基礎的認識
- 下載 Docker Hub image 並執行容器
Docker 介紹
Docker 哲學
Docker 的基本哲學 Build and Ship any Application Anywhere,也就是讓你可以建置任何應用程式並運行在任何地方。它讓我們可以分離應用程式運行的基礎設施(infrastructure),快速的建立、發佈應用程式。開發應用程式時也可以專注開發應用,而不用花太多的時間在安裝/運行環境的設置。
Docker 與虛擬主機不同
Docker 運行應用程式的環境被稱作 容器 (container)。它不像虛擬機器(virtual machine )一樣需要 虛擬機器監視器(hypervisor)模擬出軟體、韌體或硬體,反而直接運行在宿主機(host machine)的核心。以執行應用程式的運行來說,虛擬機器需要模擬出整套作業系統才能運行應用程式,而應用程式容器則是直接運行在宿主機上。不僅如此,宿主機也可以是虛擬機器。因此,它比虛擬機器又更為輕量級、執行啟動又更快。
Docker 可以做什麼?
- 一致性的發佈環境
讓開發和發佈有統一的標準環境,這有利於持續性整合與發佈continuous integration and continuous delivery (CI/CD) 工作流程。開發者撰寫程式碼,並運行在開發者的電腦的容器中,而其它部門也使用容器運行他們的應用程式。當所有的應用程式協同運作、測試都沒問題後,就可以發佈在同樣讓容器運行的正式環境中。若在測試中有任何容器內的程式有錯誤,只要修正部分程式碼,又可以快速的重新整合後發佈。 - 可攜性發佈和按需式縮放(scaling)
容器可以容易地運行在不同的環境,像是開發者電腦、虛擬機器、雲端環境或是雲端和本地的混合環境。因為容器的可攜性和輕量級地運行,我們可以輕易的按照商業需求擴張和縮小應用程式的實體數量。 - 在同一台機器運行更多的工作
容器提供輕量級的沙盒環境,在同一台機器可以運行多個容器共享硬體資源,不像虛擬機器需要模擬整個主機,它所需要的資源更多。 - 基礎設施即代碼(Infrastructure as Code)及軟體定義網路
Docker 的架構中提供 REST API 和客戶端 Docker CLI 來控制 Docker 物件,像是:images、containers、networks和 volumes。因此,各個容器的協同運作、網路、檔案系統都是可以被軟體定義、控制,像是用 Docker Compose File 和 Swarm。舉個實務上常常看到的例子: - 代碼修改網頁伺服器應用程式所監聽的 port 和掛載它所需的資料。
- 代碼控制應用程式副本的縮放。
- 因為代碼控制基礎設施,所以代碼可以保存基礎設施的設定也可以進行版本控制。
JavaScript 全端開發課程,16 週進度班帶你半年轉職工程師
Docker 架構
在進入實戰之前我們先來了解 Docker 的基本架構。
Docker 採用 server-client 架構,如下圖
圖來自 Docker overview
上圖有幾個主要元件:
- docker daemon (dockerd): 它是一個長期運行(long-running)的 server,它也是 daemon process 在作業系統的背景運行。
- REST API:這個介面可以讓我們與本地或是遠端的 docker daemon 通訊、操作 docker 物件如:images、containers、networks 和 volumes。
- Client docker CLI (docer): 操作 docker 物件常常是使用 CLI 比較方便,而不是用 REST API。例如:使用
docker run <image name=””></image>
來建立 container 並執行。若是使用 REST API 會比較麻煩。
curl –unix-socket /var/run/docker.sock -H “Content-Type: application/json”
-d ‘{“Image”: “<image name=””>”}’ </image>
-X POST http:/v1.24/containers/create
curl –unix-socket /var/run/docker.sock -X POST http:/v1.24/containers/1c6594fa
快速體驗 Docker – 以 Nginx 為例
我們前一章提到透過 docker CLI 與 docker daemon 通訊可以操作 docker 物件:images、containers、networks 和 volumes。下圖為 docker CLI 使用範例:
圖來修改自 Docker overview
- docker build (紅線):命令 daemon 建立一個應用程式的 image。若要分享 image 給別人,可以 docker push 把 image 上傳到 registry。 registry 是存放 image 的倉庫, docker 預設公開的 registry 是 Docker Hub 。當然,你也可以使用私人的 registery。
- docker pull (藍線):命令 daemon 從 registry 下載 image。
- docker run (綠線):命令 daemon 以 image 為範本建立並執行 container。
有了全局的概觀,接下來我們來實際建立一個 Nginx 網頁伺服器,
安裝 Docker 環境
- 進入 Docker Get Started,依照自己作業系統安裝 docker
- windows 安裝完會重新開機
- 灌完後執行 docker,就會有個常駐的小圖標
,之後我們就可以在終端機/命令提示字元用 docker CLI 指令了。
準備完執行環境後,接下來,我們來建立並執行 Nginx 的容器。
執行 Nginx container
1. 下載 Nginx image
我們從 Docker hub 下載 Nginx image 到本地端。在終端機輸入
docker pull nginx:1.18.0
這裡我們使用 Nginx 版號 1.18.0
2. 建立並執行容器
在終端機輸入
docker run –name nginx_1 -p 8080:80 nginx:1.18.0
就會在前景執行容器,要終止執行請鍵入 Ctrl + c。
這裡我們的docker run 的參數依序為:
(1)–name: 容器名為 nginx_1,若不加這參數,建立出的容器名就會隨機產生
(2)-p:nginx 的伺服器預設開 80 port。預設網路模式是 bridge,要從本機端連線到容器內可以設定本機端 8080 port。示意圖如下:
(3) nginx:1.18.0: nginx_1 會以 nginx:1.18.0 image 為範本建立容器
3. 測試 Nginx
接下來,打開本機端瀏覽器,訪問 http://localhost:8080/,就會看到以下畫面:
我們已成功架設網頁伺服器,接下放入 HTML 網頁原始碼 hello.html。
放入網頁原始碼
刪除舊的容器
我們先刪除舊的容器,以便我們重新建立容器,設定資料夾掛載點
若前景容器還在執行,鍵入 Ctrl + c,可以終止執行。另外,也可以開終端機 docker stop nginx_1 強制停止容器執行。
在終端機輸入
docker stop nginx_1 # 停止容器
docker rm nginx_1 # 刪除容器
掛載本地端的資料夾
假設本地端的資料夾路徑為:/Users/eugenechen/webserver/html,我們在裡面建立一個 HTML 檔案 hello.html,內容如下:
再重新建立容器
docker run –name nginx_1 -p 8080:80 -v /Users/eugenechen/webserver/html:/usr/share/nginx/html nginx:1.18.0
這裡我們加入一個新參數 -v,這用來把本機端的資料夾掛載到容器內的檔案系統的 /usr/share/nginx/html。也就是說,會在本機端資料夾內的資料會在容器的 /usr/share/nginx/html 中出現,它們共享資料。這用法叫做 bind mount,還有其它的設定但不在本節中討論。
測試訪問 hello.html
接下來,打開本機端瀏覽器,訪問 http://localhost:8080/hello.html,就會看到以下畫面:
到目前為止,我們完成了架設自己的網頁伺服器,接下來我們簡單的介紹 Docker 基本物件。
Docker 的基本物件
前一章我們體驗了用 Nginx image 建立網頁伺服器,然後掛載本機端的資料夾到容器,使網頁伺服器 可以回應我們寫的 HTML 檔案。以上情境,總結起來就是在設定/操作 Docker 的基本物件:
- container
- image
- network
- volume
它們的關係,用 class diagram 來表達如下:
所有的 Docker 物件都可以通過 docker XXX 指令管理,指令很多也有很多細部的指令,但也不用背,你到處加入 –help 查看指令,如:
docker –help # 查看可管理的物件指令
docker image –help # 查看 image 物件指令
Container(容器):映像檔的執行物件
Container 是 Docker 關鍵的技術。它是運作在 Docker Engine 上,不同的作業系統都有對應的 Docker Engine。
容器是映像檔(image)的實際執行物件,用 docker + run(建立並執行) / start(執行) / restart(重新執行) / stop(停止) / rm(移除) 指令可以管理容器。
當容器建立時,它會執行在一個沙箱環境,可以想像他有自己的檔案系統。另外,我們還可以為 container 設定 network 和 volume。
容器的 network 讓我們可以與別的容器和主機通訊,像是:從外部存取容器內的服務,可能是從 host,也可能是來自別的 container。
volume 是容器與別人共享的資料區,我們把資料放在裡面,可以:
- 與 host 共同存取
- 提供其它 container 掛載(mount)使用,共享同樣的資料區
- 它是獨立於 container,當 container 移除時,volume 裡的資料可以保留下來。
Image(映像檔):容器建立的範本
Image 是容器建立的範本,它打包著相關程式碼、函式庫、環境配置檔。簡而言之,就是一群檔案集合。 image 常常是基於別的 image 建立的,像是我們可以基於 nginx:1.18.0 加入客制的 Nginx 設定組態或加入你自己的檔案再重新建置(build)一個新的 image。
當要建立並執行 container 時一定要指定「唯一」的一個映像檔,如下格式
docker run <image></image>
Volume:獨立於容器的檔案空間
我們有時會希望:
- 容器移除時檔案可以留下 – 資料庫類型的容器常會需要
- 容器與 host 就可以共享檔案/資料夾 – 常用於 host 和容器共享檔案
- 同個檔案可以在不同容器間共享 – 容器間可以用共用的組態檔
簡單來說為了 「共用」和「留下」的目地,此時就會用到 : Volume。
docker run 使用參數 -v (–volume) 來掛載 volume,格式是:-v <source>:<target></target>,source 可以來自 host 或 volume,不管來自哪都不會因為容器刪除而消失; 目標(target)是指定要掛入容器內的位置。
Docker 提供三種掛載類型(mount types):
- bind mount:掛入現有的 host 檔案系統(filesystem),用在容器與 host 共享資料夾或檔案
- volume:掛入 volume 物件
- tmpfs mount:從 memory 掛載
我們只提常用的 bind mount 和 volume。
bind mount:與 host 共享
這是最簡單、常用的模式,直接把 host 的 source 掛載到容器的 target。例如, nginx_1 要在根目錄多出一個資料夾 /mydata。在 建立容器 時,用 -v 參數指定要掛入的 host 資料夾(或檔案)及掛入在容器中哪個目標(位置)。例如:
docker run –name nginx_1 -p 8080:80 -v /Users/eugenechen/mydata:/mydata nginx:1.18.0
前面架設 Nginx 的例子也是透過 bind mount,把容器的 /usr/share/nginx/html 換成本機端的資料夾。
若要更多 volume 就多寫幾個 -v,如: -v /Users/eugenechen/mydata1:/mydata1 -v /Users/eugenechen/mydata2:/mydata2
volume:容器掛載 Volume 實體
我們也可以建立一個 volume 物件,再把 volume 掛載到容器中。如此一來,container 的刪除不會影響到 volume,volume 也可以同時掛給多個容器。操作方法如下:
1. 建立一個 volume
docker volume create mydata2
2. 在 建立容器 時,一樣用 -v,只是 source 改成用 volume name
docker run –name nginx_1 -p 8080:80 -v /Users/eugenechen/mydata:/mydata -v mydata2:/mydata2 nginx:1.18.0
Network:與容器通訊
網路是 Docker 物件中重要的一環,它可以讓其它人存取容器中的應用程式,也可以讓各個 Docker daemons (dockerd) 串接起來,使容器在它們之間擴張(scale)複製,而容器不會察覺它目前運行在哪台主機。
docker 支援許多模式,但我們只考慮本機常遇到的 brdige 和 host 使用情境,不考慮 swarm 常用的 overlay 網路模式。
Bridge mode:分離容器和本機的網路
這是容器預設的網路模式。當我們 docker run 時,容器會使用一個預設的 network,名稱為 bridge 的網路物件(不同的作業系統可能名稱會有差異,mac 叫 bridge, linux 叫docker0)。 容器接上那個預設的 network 並配給一個 IP。 bridge 的圖解如下:
172.28.0.0/16 是指一網路的網段從 172.28.0.1 ~ 172.28.255.255,子網路的主機可以互相通訊。 16 是指 16 bits 的子網路遮罩 255.255.0.0。
若外界 192.168.0.5 要訪問我們的主機的 nginx_1 容器,我們要設定 -p (–publish),把容器內的 port 暴露出來,這參數會自動設定內外部 network 的轉換 Network Address Translation(NAT)。以前面的例子來說,當我們設定 -p 8080:80 時,訪問 192.168.0.2:8080 就會透過 NAT 轉到容器的 172.28.0.4:80。
Host mode:直接使用本機的網路
這是最簡單的模式,容器直接使用主機的網路,可以想成 container 執行在 host 的某個行程 (process),它監聽什麼 port ,那麼 host 的 port 就會被使用,所以連 -p 都不用設定。此外,有個小細節要注意,一個 port 只有一個 prcess 能監聽,同理,一個 port 也只能給一個容器物件監聽。
總結
我們首先大概介紹了 Docker ,然後以執行 Nginx 為例子快速建立網頁伺服器,最後再介紹 Docker 的重要的物件:Container, Image, Volume, Network。
下一步,你可以學習:
- 去 Docker Hub 下載別人做好的 Image,例如:資料庫 MySQL、MongoDB…等等,運行看看
- 建置自己的 Image
- 使用 Docker Compose 管理主機的容器
- 使用 service / swram 打造容器群集
附錄
我們列出常用指令如下:
(本文作者是 ALPHA Camp 助教 軟體工程師 EugeneChen)