在上一篇中,我們留下了幾個問題待解,其中一個是在 Host 中,明明沒有 ash 這個指令可以用,但為什麼可以用 container 執行這個指令並且成功地建立了一個 process 呢?這就必須要討論到什麼是 image 了,也就是上一篇中 docker run -it alpine ash
中的那個 alpine 是什麼。
為了可以觀察到一些更明顯的資訊,我們換個 image 來觀察看看,讓我們用 docker container 來啟動一個 nginx server:
$ docker run nginx
在執行這行指令後,我們可以看到以下的畫面:
接下來,就讓我們試試看能不能解讀這個畫面上的各種訊息。
在開始之前,我們先試試看用另外一個 terminal 登入 EC2 並且重新執行一次 docker run nginx
,這一次會看到的畫面如下:
這兩次執行後的結果有一個很大的差別,就是下面這段訊息:
Unable to find image 'nginx:latest' locally
latest: Pulling from library/nginx
31b3f1ad4ce1: Pull complete
fd42b079d0f8: Pull complete
30585fbbebc6: Pull complete
18f4ffdd25f4: Pull complete
9dc932c8fba2: Pull complete
600c24b8ba39: Pull complete
Digest: sha256:0b970013351304af46f322da1263516b188318682b2ab1091862497591189ff1
Status: Downloaded newer image for nginx:latest
這段訊息的第一句話是 Unable to find image 'nginx:latest' locally
,翻譯一下就是他在你的 Host 中找不到 nginx:lastest
這個 image,所以他需要先去把這個 image pull
下來。那問題來了,到底什麼是 image?他又是去哪裡 pull 的呢?
官網對於 image 的描述是:
An image is a read-only template with instructions for creating a Docker container.
from https://docs.docker.com/get-started/overview/
這裡有幾個很需要注意的點,第一個是 image 是 read-only 的,其次是 image 是一個 template,是用來讓我們建立 docker container 的。關於第一點我們稍後討論,先來想想看為什麼 image 可以用來建立 container。
簡單地說,image 其實是把啟動一個應用程式所需要的執行檔(或程式碼),及要啟動這個應用程式需要的函式庫、相關工具、相依的各種套件與檔案,還有一些環境變數等打包在一起。這樣當我們要啟動這個應用程式時,只需要載入這個 image,這個應用程式所需要的那些東西全都已經在了,換句話說,Host 中並不需要另外安裝或準備這些被依賴的套件、工具、函式庫等等等等,也保證了不管在哪裡執行,都能有一樣的結果,達到了「Build once, Run anywhere」的境界(雖然只是接近)。
在第一天的文章中有分享過,在沒有 docker 之前,要在 Linux 上安裝個軟體,對沒那麼熟悉 Linux 的人來說是件蠻令人害怕的事,因為永遠不知道在安裝過程中,是不是會缺少什麼相依的 lib,又或者相依的 lib 版本不對等各式各樣的問題。有時候連自己開發的應用程式也都會有問題,大家或許聽過一個笑話(?),非工程師,例如測試工程師、PM 或者客戶等關係人(甚至是工程師自己),最討厭工程師講的一句話就是「在我的電腦可以跑」,非工程師的人聽到這句話可能會覺得工程師是在推託,我自己有時也會不小心冒出這句話,不曉得其他朋友的心情怎麼樣,我自己講出這句話的時候,心情其實是恐懼的,擔心自己是不是少安裝了什麼東西?是不是又有什麼東西沒有設定到?
這些問題在有了 docker 之後,不能說完全沒有了,但真的降低很多,畢竟,把執行一個應用程式需要的東西都打包在一起了,大概也很難有問題。(我先打臉一下我自己這句話,自從 Mac M1 出來後,我就遇到了在 Mac M1 上打包的 image,在 AWS intel 的 EC2 上跑起來是會有問題的,但這個應該是硬體層級的問題了,可以在 build image 時加上 --platform linux/amd64
來指定一下指令集。)
到這裡,應該可以理解為什麼我們會說 image 是用來啟動 container 的一個「樣板」了,前文中所提出來的問題「為什麼 Host 中沒有 ash 但卻可以用 container 來執行」也有了答案,很簡單,ash 有被打包在 alpine 這個 image 中,所以當我們用 alpine 這個 image 來啟動 container 時,就可以在這個 container 中執行 ash 這個指令。
而 image 打包的東西除了上述的應用程式及其相依的函式庫/套件外,他還包含了應用程式執行時所需的整個系統環境,那這樣又有一個新問題產生了,image 會不會很大呢?如果我們有兩個 Node.js 程式要執行,同樣都需要 Node.js 16 這個執行環境,那這兩個 Node.js 如果分別被打包成 image 的話,是不是就分別包 Node.js 16 這個執行環境,這樣一來會不會很佔空間呢?老話一句,讓我們繼續研究下去~