以前 一篇文章介紹 docker 的鏡像基本原理和概念 ,主要介紹在編寫 docker 鏡像的時候一些須要注意的事項和推薦的作法。java
雖然 Dockerfile 簡化了鏡像構建的過程,而且把這個過程能夠進行版本控制,可是不正當的 Dockerfile 使用也會致使不少問題:python
這篇文章會講述一些作法,但願能解決這些問題。docker
但願讀者可以對 docker 鏡像有必定的瞭解,閱讀這篇文章至少須要一下前提知識:ubuntu
Dockerfile 是由一個個指令組成的,每一個指令都對應着最終鏡像的一層。每行的第一個單詞就是命令,後面全部的字符串是這個命令的參數,關於 Dockerfile 支持的命令以及它們的用法,能夠參考 官方文檔 ,這裏再也不贅述。centos
當運行 docker build
命令的時候,整個的構建過程是這樣的:緩存
有些文章講優化鏡像會提倡使用盡可能小的基礎鏡像,好比 busybox 或者 alpine 等。我更推薦使用統一的你們比較熟悉的基礎鏡像,好比 ubuntu,centos 等,由於基礎鏡像只須要下載一次能夠共享,並不會形成太多的存儲空間浪費。它的好處是這些鏡像的生態比較完整,方便咱們安裝軟件,除了問題進行調試。網絡
常常變化的內容和基本不會變化的內容要分開,把不怎麼變化的內容放在下層,建立出來不一樣基礎鏡像供上層使用。好比能夠建立各類語言的基礎鏡像,python2.七、python3.四、go1.七、java7等等,這些鏡像包含了最基本的語言庫,每一個組能夠在上面繼續構建應用級別的鏡像。python2.7
不少人構建鏡像的時候,都有一種衝動——把可能用到的東西都打包到鏡像中。要遏制這種想法,鏡像中應該 只包含必需的東西 ,任何能夠有也能夠沒有的東西都不要放到裏面。由於鏡像的擴展很容易,並且運行容器的時候也很方便地對其進行修改。這樣能夠保證鏡像儘量小,構建的時候儘量快,也保證將來的更快傳輸、更省網絡資源。ide
不要在容器裏運行多個不一樣功能的進程,每一個鏡像中只安裝一個應用的軟件包和文件,須要交互的程序經過 pod(kubernetes 提供的特性) 或者容器之間的網絡進行交流。這樣能夠保證模塊化,不一樣的應用能夠分開維護和升級,也能減少單個鏡像的大小。模塊化
雖然看起來把不一樣的命令儘可能分開來,寫在多個命令中容易閱讀和理解。可是這樣會致使出現太多的鏡像層,而很差管理和分析鏡像,並且鏡像的層是有限的。儘可能把相關的內容放到同一個層,使用換行符進行分割,這樣能夠進一步減少鏡像大小,而且方便查看鏡像歷史。
儘管只安裝必須的內容,在這個過程當中也可能會產生額外的內容或者臨時文件,咱們要儘可能讓每層安裝的東西保持最小。
--no-install-recommends
參數告訴 apt-get
不要安裝推薦的軟件包/var/lib/apt/list/
緩存由於 docker 鏡像是分層的,任何修改都會新增一個層,修改文件或者目錄權限也是如此。若是修改大文件或者目錄的權限,會把這些文件複製一份,這樣很容易致使鏡像很大。
解決方案也很簡單,要麼在添加到 Dockerfile 以前就把文件的權限和用戶設置好,要麼在容器啓動腳本(entrypoint)作這些修改。
若是 Docker 發現某個層已經存在了,它會直接使用已經存在的層,而不會從新運行一次。若是你連續運行 docker build
屢次,會發現第二次運行很快就結束了。
不過從 1.10 版本開始,Content Addressable Storage 的引入致使緩存功能的實效,目前引入了 --cache-from
參數能夠手動指定一個鏡像來使用它的緩存。
最好把 Dockerfile 和對應的應用代碼一塊兒放到版本控制中,而後可以自動構建鏡像。這樣的好處是能夠追蹤各個版本鏡像的內容,方便了解不一樣鏡像有什麼區別,對於調試和回滾都有好處。
另外,若是運行鏡像的參數或者環境變量不少,也要有對應的文檔給予說明,而且文檔要隨着 Dockerfile 變化而更新,這樣任何人都能參考着文檔很容易地使用鏡像,而不是下載了鏡像不知道怎麼用。