Docker - 製作一個合格的docker鏡像,以及最佳實踐

 

什麼是鏡像,什麼是容器


製作Docker鏡像之前,我們先明確一下鏡像是什麼,百度百科對於鏡像的定義如下:

鏡像(Mirroring)是一種文件存儲形式,是冗餘的一種類型,一個磁盤上的數據在另一個磁盤上存在一個完全相同的副本即爲鏡像。可以把許多文件做成一個鏡像文件,與GHOST等程序放在一個盤裏用GHOST等軟件打開後,又恢復成許多文件,RAID 1和RAID 10使用的就是鏡像。常見的鏡像文件格式有ISO、BIN、IMG、TAO、DAO、CIF、FCD。

Docker鏡像當然也是鏡像,它存儲當是應用所運行的環境的快照,恢復這個鏡像,那麼應用就應該正常啓動。那麼我們可以想到,應用所依賴的所有環境都應該在鏡像中,Docker本身就是Docker鏡像恢復的運行環境。所以我們可以給Docker鏡像做如下定義:

鏡像(image)可以看作是一個Linux系統快照,這個快照中記錄了構建鏡像時的系統狀態,鏡像中不僅包含你的應用,還包含應用運行需要的所有依賴和環境(library,操作系統)。

鏡像的標識方法:

鏡像名稱的格式是

url/namespace/name:tag

例如“sample.com/busybox:v3.2”,拉取該鏡像時,docker engine會嘗試從`sample.com`拉取該鏡像,如果鏡像名字中沒有指定url,會從docker hub拉取;名字中的tag並不是必須的,如果不指定,默認爲"latest"。

已經有很多人制作了很多應用的鏡像,共享在了DockerHub或者其他公共鏡像倉庫(例如國內的阿里雲鏡像倉庫),我們沒有必要重複造輪子,DockerHub就像GitHub一樣,我們可以從中拉取已有鏡像來使用,如果現有鏡像不能滿足需求,就需要自己製作鏡像,那麼下面我們來說說製作鏡像的方法。

 

製作鏡像


docker製作鏡像有兩種方法:

1. 利用dockerfile,將構建流程寫入dockerfile文件,然後執行,`docker build -f docker_file_name`;
2. 現有容器基礎上構建,`docker commit container_name/container_id new_image_name`。

Dockerfile

dockerfile是一個配置文件,它告訴docker如何構建鏡像,docker會根據dockerfile中的指令,一步一步的完成鏡像。一個典型的nodejs後端API項目dockerfile如下:

FROM base_image
WORKDIR /var/apps/app_name

# 安裝項目依賴包
COPY ./package.json ./
RUN cnpm install --production

# 拷貝項目文件
COPY ./ ./

EXPOSE 3000
CMD [ "node", "app.js" ]

`FROM`關鍵字確定了基礎鏡像,很多時候,我們不需要自己從頭開始製作,我們可以基於已有的輪子來做,基礎鏡像可以是操作系統,也可以是安裝了一些依賴的操作系統,後面的命令都是基於這個基礎,在這個基礎鏡像提供的環境中執行命令,進行操作。例如`WORKDIR`是在鏡像中指定了一個項目目錄,如果目錄不存在,會自動創建;`COPY`是將文件拷貝到鏡像內,這些文件時docker開始構建鏡像時讀取的,docker開始構建鏡像時會讀取dockerfile所在目錄的所有文件至docker engine中,不過有一個`.dockerignore`文件可以配置docker忽略讀取的文件,類似於`.gitignore`,`./`當前路徑即表示dockerfile所在的文件夾;`RUN`表示在鏡像中執行shell命令,`cnpm install --production`則表示安裝nodejs項目的依賴;接下來又有一個copy,拷貝所有項目文件;`EXPOSE`則是暴露項目的監聽端口;最後`CMD`表示鏡像啓動時執行的命令,這個命令必須是不被掛起的,不能以Service的形式,否則容器啓動就會馬上退出。

這裏大家可能會有疑問,爲什麼copy分爲兩部分,不在一個copy命令中一次性拷貝完成呢?這是因爲docker鏡像是分層構建的,每個命令都對應着鏡像的一層,而在兩次構建中某一層沒有改變時,則不會重新構建這一層,nodejs項目的依賴包很少變動,所以選擇放在鏡像的下一層,其它代碼文件頻繁變動,所以選擇和package.json的拷貝分開。

docker commit

在一個運行的容器中,有時候你需要添加一些依賴,或者修改某些文件,想下次啓動容器時依然保留改動,不想從頭構建,那可以使用`docker commit`基於容器生成一個鏡像。

docker commit [OPTIONS] container_id_or_name image:tag

下次啓動容器直接從`image:tag`這個鏡像啓動即可。

> 注:在容器中做了修改,需要重新啓動容器,然後執行`docker commit`才能生效。

 

管理鏡像


使用Docker命令可以方便的管理鏡像,具體說來,假設我們機器上有一個名爲`busybox:test`的鏡像,我們可以使用下面的方法對其進行增刪改查:

  • 查看本機都有哪些docker鏡像,使用`docker images`;
  • 使用 docker tag busybox:test yet_another_name:new_tag 取一個別名,這時再 docker images 會發現多了一個鏡像,不要被表象迷惑,該操作並不會重新創建鏡像,而是添加了一個引用,就像劉備和劉皇叔都是劉備一樣;
  • 使用 docker rmi busybox:test 刪除鏡像;
  • 使用 docker pull busybox:test 拉取鏡像;
  • 使用 docker push busybox:test 推送鏡像到鏡像倉庫。

 

需要注意的點


設置時區
一般常用的基礎鏡像,比如alpine、ubuntu、debian鏡像,默認是UTC時區,所以時間會和北京時間相差8個小時,所以把鏡像放到生產環境之前,需要重新設置時區,下面舉一個修改 alpine 基礎鏡像的時區作爲例子,其他基礎鏡像類似。首先啓動一個alpine 3.10.1 的容器(docker run -it alpine:3.10.1 sh),查看當前時間,可以看到容器默認使用UTC時間:
 

初始日期設置

修改爲東八區時區:

修改時區後,時間後延8個小時

 

設置語言

 

設置編碼

 

總結


兩種鏡像製作方法,建議經常使用第一種。製作完鏡像,就可以啓動容器了,啓動容器也有很多選項,很多時候啓動的容器並不會完全按照你的設想工作,這就要求你必須指定正確的啓動選項,尤其是容器需要額外的存儲和網絡時,這一部分內容也比較多,下一篇再講。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章