Docker簡述
Docker是一種OS虛擬化技術,是一個開源的應用容器引擎。它可以讓開發者將應用打包到一個可移植的容器中,並且該容器可以執行在幾乎所有linux系統中(Windows10目前也原生支援,Win10前需要內建虛擬機器),正所謂“一次打包,到處執行”。
Docker容器的執行是完全的沙箱機制,相互之間不會有任何關聯(除非自己串聯叢集)。網路、儲存、程式等資源,不僅對於不同的容器是相互隔離,對於宿主機和容器直接也是隔離的,除非你手動對映暴露埠或者掛載儲存卷。
很多人不理解,Docker和虛擬機器到底有什麼區別。
從這兩張結構圖來看,Docker比虛擬機器少了一層虛擬機器作業系統,Docker的應用直接Docker引擎上執行。由於虛擬機器需要一層作業系統,所以會導致虛擬機器的體積非常大,通常在幾G到十幾G之間。並且通常一個虛擬機器上,不只一個應用,因此對於整體的虛擬叢集管理並不太友好,比較難做到靈活分配。而一個Docker映象的體積大約在幾十M到幾百M之間,一般一個映象只打包一個應用,由多個映象組成一個完整的專案,並且映象易於複製,可以跨平臺執行,這樣可以使專案的部署管理有更好的靈活性。所以Docker無論從資源消耗上、管理上、使用上都在虛擬機器之上,因此我們又有何理由不使用這樣的容器化技術呢?
對於容器化技術的學習,可謂是深如海。從基本的映象、容器操作,到映象的打包、容器的部署,再到企業生產級的容器叢集管理技術(Docker官方的Swarm、Google的Kubernetes),如此多的內容,並不是所有人技術人員都能一朝學會。不過除了生產級別的叢集管理技術有難度意外,其他內容從學習使用的角度來說,其實是非常簡單的,況且K8s這種東西,對於普通開發來說也是很少能接觸到。
說到這裡,可能還有很多人覺得這個是公司層面、運維層面的操作,不是很瞭解Docker對於普通開發來說,意味著什麼,對我們有什麼好處?
- 多辦公環境,一鍵部署。假如你在公司一套開發環境,在家一套開發環境,當你公司的開發環境變更時,在家的環境就要跟著變,如果是使用Docker,將一些依賴型的應用,如Redis、ZK、Mysql等邊緣服務都打包在docker裡面。無論你在哪裡改變了內容,只要在執行時更新下映象,就可以按照最新的內容去執行了,不需要一個手動去安裝,適配。
- 聯調測試,無需依賴他人。當後端完成對外的介面後,將後端應用打包進docker,這樣無論是前端、測試,在何地何時都可以自己把容器啟動起來進行聯調測試,而不需要自己手動一步步地搭建這個後端環境。
- ...
下面就來一步步講解下,普通開發所需要的Docker知識。
概念介紹
學習Docker首先要了解下幾個基礎概念:
- 宿主機,Host,執行Docker所在的物理機,是Docker執行的系統環境。
- 映象,Image,相當於一個程式模板,通過這個模板可以生成很多個相似的容器。可以理解為Java中的類,它本身不具備執行執行的能力,是一個物件抽象的模板。每個映象可以有多個版本,用tag來區分。映象可以通過Dockerfile來構建。
- 容器,Container,Docker執行的最小單位物件。它是通過映象例項化出來的一個可執行物件。容器的修改,可以提交反作用於映象,更新這個容器的模板。
- 倉庫,Repository,用於儲存管理映象的倉庫,類似於git管理程式碼的倉庫一樣,可以管理多版本的映象。
映象、容器、倉庫的關係如下:
一句話總結就是,從倉庫中拉取映象,利用映象生成容器。基本操作
瞭解完Docker的基本概念,我們開始來開始學習下入門操作。此處省略所有的Docker安裝過程,自己去官網下載就行了,基本是傻瓜式安裝。
拉取映象
通過docker pull ${image_uri}:${image_tag}
命令,可以從遠端倉庫(預設是Docker Hub)中拉取所需要的映象。
在Docker Hub的網站上可以搜尋下自己需要的映象以及版本。例如Ubuntu,上面提供了幾個版本。
我們拉一下16.04版本的ubuntu映象。然後通過docker images
命令,檢視儲存在本地映象,發現多了一個ubuntu的映象。
容器建立、啟動、停止、登入
有了映象以後,就可以通過docker run -it ${image_id}
建立啟動一個容器了。
image_id
是映象的id,通過docker images
能檢視到,也可以是映象名(REPOSITORY:TAG)。
-it
可以讓你在啟動後,連上容器的終端。連上終端後,就可以在裡面隨意操作容器裡面的內容了。
exit
退出容器後,容器就會自動停止了。但是這個容器依然還存在,只是”關機“了。(可以通過ctrl+p,ctrl+q
,退出容器登入,而不關閉容器)
通過docker ps -a
可以看到我們的容器已經Exited
了。
通過docker start ${container_id}
,我們把這個容器再次啟動。通過docker ps
(加上*-a包含顯示未啟動的容器),可以看到容器的狀態為UP*。
同理,我們可以通過docker stop ${container_id}
來停止容器,
在用docker start
命令的時候,如果不加上*-a*引數,預設不會連線上容器的。不過我們可以在start後,通過docker attach ${container_id}
來登入容器。
通過以上的基本操作,你基本可以利用docker當作一個虛擬機器來使用了。如果想把容器和虛擬機器的網路、儲存打通,可以網上搜下了解下網路與卷掛載等容器設定。
更新映象
在上面的例子中,我們pull下來的僅僅是一個ubuntu的原始映象,並沒有過多的內容。下面我們在這個映象的容器裡面,安裝一個jdk。
這樣我們的容器裡面就有一個jdk了,但是如果我們再用這個ubuntu原始映象再建立一個容器,它是不會用這個jdk的。所以我們就需要把這個容器的內容,提交到映象當中。通過docker commit ${container_id} ${repository}:${tag}
,在本地將容器內容提交到映象當中。然後就可以擁有一個帶jdk的ubuntu映象了。
以上的更新僅限於在本地的映象,如果想把容器推送到雲端就需要用docker push
命令。前提是你已經登入了倉庫擁有許可權。
映象倉庫
上面提到,預設情況下,倉庫是用Docker Hub。我們pull 和push都是在Docker hub上操作,但是如果映象是內部私有使用的話,沒有必要去使用Docker Hub,一個是網路慢,另一個是私有安全性問題。
針對以上問題,有兩種解決方法,一個是自己搭建私有服務,另一個是用雲服務的映象管理平臺(如阿里雲的“容器映象服務”)。前者對於一般開發者來說並沒有必要,而且還要搞認證的,比較麻煩,這裡不細說。下面介紹下如何用阿里雲服務作為自己的私有倉庫。
先在阿里雲上建立一個映象倉庫,獲得一個倉庫地址,如registry.cn-shenzhen.aliyuncs.com/zackku/jdk。這裡一個倉庫地址,對應一種映象(tag不同)。
利用docker login
,先對阿里雲的服務進行登入。
然後對上面的jdk映象打tag(其實也是改倉庫源的過程)
最後把映象推送到阿里雲就行了。
推送後,就能在阿里雲的倉庫上看到這個映象。
通過搭建私有倉庫,我們就可以完全拋開宿主機的環境,構建好一個映象,就可以到處執行了。
Dockerfile構建映象
從上面介紹,我們已經瞭解到,如何從拉取一個映象、修改容器內容、提交映象去構建一個我們所需要的映象。但通過這些操作去構建一個映象,一個是太繁瑣,另一個問題是不清晰,沒辦法直觀的瞭解映象的構成。
Dockerfile就可以很好的解決該問題。它可以通過編寫一個構建過程,來一站式構建映象。下面同樣以ubuntu為基礎映象,安裝jdk構建一個新映象為例,看看Dockerfile是怎麼寫的。
然後執行docker build -t registry.cn-shenzhen.aliyuncs.com/zackku/jdk2:1.0 .
就能把映象構建出來了。
Dockerfile高階技巧
上面是Dockerfile的基本使用,但實際情況下我們並不像(或者說不僅是)上面描述那樣去構建映象。下面介紹兩個常用的使用原則。
分層構建。其實Docker的映象是分層結構的,看回之前推送到遠端倉庫的例子。
紅框裡面就是映象一層層的提交,如果這層已經本地構建過了,下次不需要構建了,同理如果遠端已經有這層了,也不需要推送這層。而且這種分層是可以在不同映象間共享的,例如不同的Java專案都是依賴於JDK的執行環境,那麼它們就可以共用JDK這層映象內容。所以,基於這樣的特性,我們就應該要分層去構建映象,抽象映象共同點。具體操作的話,我們大致可以去分兩次構建映象,先構建一個base映象,用於不同映象的底層,例如Java專案的所有基礎執行環境,然後再通過base映象,構建develop表層的應用映象。相當於把應用程式打包丟到develop層裡面。並且這層要告訴Docker是怎麼執行程式的。
儘量構建小的base層。映象的體積也是在使用Docker的時候要考慮的一個重要因素,因為如果映象的體積過大,在更新映象,拉取映象的時候效率會低。尤其在剛剛所說的base層裡面,如果base層做得太大太臃腫,裡面程式過多,不僅僅體積大,還會讓CPU、網路等資源消耗過大。其實我們在用Docker的時候,一般是一個容器只包含一個程式專案,關於這個程式的監控、健康等內容,在容器外通過k8s等叢集管理去做,所以容器本身只需要保證自己的程式能夠執行起來就行了。
至於上面我用ubuntu作為基礎的作業系統是比較多餘的,這裡推薦只用apline作業系統作為程式的最底層映象,它是一款輕型的Linux發行版,系統體積與執行時的資源消耗都相當低,十分適合用於Docker容器。基於apline的作業系統,我們在上面新增自己所需要的環境,例如安裝一個Tomcat、JDK等,從而構建一個base的映象。
上所說的base映象,其實不太需要自己的寫一層Dockfile,docker官方就直接提供了各種語言、環境的基礎映象,在github的docker-library裡面。如果再有自己的團隊的執行環境的要求,可以在這個Dockerfile基礎上去新增修改即可,或者再抽象多一層。
至於Dockfile怎麼寫,語法是什麼,網上有大把詳細的說明,由於篇幅問題,不在這裡展開。
docker-compose啟動叢集
前面已經介紹完一個單獨的容器是如何構建與啟動的了,但我們的專案往往不是隻有一個容器的,把所有程式打包在一個容器不是正確的做法。所以我們怎麼去管理啟動這麼多的容器,是一個必修的課題。在企業級的層面,有K8S,Swarm這種容器編排的管理工具,但稍微比較複雜,個人使用的話也沒有太大必要。
這裡推薦用Docker官方的docker-compose,它可以把所有的容器編排方式寫在一個檔案裡,然後通過docker-compose up
命令,就可以把一套的容器按照你的編排全部啟動起來。
在這個例子的services包含每個容器的配置,其中的redis、mongodb用的是預設的映象、預設的配置,myproject是我們自己的專案。通過這樣的編排,我們就能讓我們的專案連上redis和mongodb。最後通過docker-compose up
就會自動拉取映象,按照編排跑起來了。
具體的語法也不贅述,關鍵就是容器的卷掛載,網路的配置,埠的暴露,容器的依賴關係。如果把這套東西用起來,慢慢自然就會了解,重要的是動手去做一遍,嘗試一下。
更多技術文章、精彩乾貨,請關注
部落格:zackku.com
微信公眾號:Zack說碼