Docker是1種容器技術,它可以將利用和環境等進行打包,構成1個獨立的,類似于iOS的APP情勢的“利用”,這個利用可以直接被分發到任意1個支持Docker的環境中,通過簡單的命令便可啟動運行。Docker是1種最流行的容器化實現方案。和虛擬化技術類似,它極大的方便了利用服務的部署;又與虛擬化技術不同,它以1種更輕量的方式實現了利用服務的打包。使用Docker可讓每一個利用彼此相互隔離,在同1臺機器上同時運行多個利用,不過他們彼此之間同享同1個操作系統。Docker的優勢在于,它可以在更細的粒度上進行資源的管理,也比虛擬化技術更加節儉資源。
上圖:虛擬化和Docker架構對照,來自Docker官網
基本概念
開始實驗Docker之前,我們先來了解1下Docker的幾個基本概念:
鏡像:我們可以理解為1個預配置的系統光盤,這個光盤插入電腦后就能夠啟動1個操作系統。固然由因而光盤,所以你沒法修改它或保存數據,每次重啟都是1個原樣全新的系統。Docker里面鏡像基本上和這個差不多。
容器:一樣1個鏡像,我們可以同時啟動運行多個,運行期間的產生的這個實例就是容器。把容器內的操作和啟動它的鏡像進行合并,就能夠產生1個新的鏡像。
開始
Docker基于LXC技術實現,依賴于Linux內核,所以Docker目前只能在Linux以原生方式運行。目前主要的Linux發行版在他們的軟件倉庫中內置了Docker:
Ubuntu:
-
Ubuntu Trusty 14.04 (LTS)
-
Ubuntu Precise 12.04 (LTS)
-
Ubuntu Saucy 13.10
CentOS:
-
CentOS 7
Docker要求64位環境,這些操作系統下可以直接通過命令安裝Docker,老1些操作系統Docker官方也提供了安裝方案。下面的實驗基于CentOS 7進行。關于其他版本操作系統上Docker的安裝,請參考官方文檔:https://docs.docker.com/installation/
在CentOS 7上安裝Docker
使用yum從軟件倉庫安裝Docker:
yum install docker 首先啟動Docker的守護進程:service docker start 如果想要Docker在系統啟動時運行,履行:chkconfig docker on Docker在CentOS上好像和防火墻有沖突,利用防火墻規則后可能致使Docker沒法聯網,重啟Docker可以解決。
Docker倉庫
Docker使用類似git的方式管理鏡像。通過基本的鏡像可以定制創建出來不同種利用的Docker鏡像。Docker Hub是Docker官方提供的鏡像中心。在這里可以很方便地找到各類利用、環境的鏡像。
由于Docker使用聯合文件系統,所以鏡像就像是夾心餅干1樣1層層構成,相同底層的鏡像可以同享。所以Docker還是相當節儉磁盤空間的。要使用1 個鏡像,需要先從遠程的鏡像注冊中心拉取,這點非常類似git。
docker pull ubuntu 我們很容易就可以從Docker Hub鏡像注冊中心下載1個最新版本的ubuntu鏡像到本地。國內網絡可能會稍慢,DAOCLOUD提供了Docker Hub的國內加速服務,可以嘗試配置使用。
運行1個容器
使用Docker最關鍵的1步就是從鏡像創建容器。有兩種方式可以創建1個容器:使用docker create命令創建容器,或使用docker run命令運行1個新容器。兩個命令并沒有太大差別,只是前者創建后其實不會立即啟動容器。
以ubuntu為例,我們啟動1個新容器,并將ubuntu的Shell作為入口:
docker run -i -t ubuntu /bin/bash這時候候我們成功創建了1個Ubuntu的容器,并將當前終端連接為這個Ubuntu的bash shell。這時候候就能夠愉快地使用Ubuntu的相干命令了~可以快速體驗1下。
參數-i表示這是1個交互容器,會把當前標準輸入重定向到容器的標準輸入中,而不是終止程序運行。-t指為這個容器分配1個終端。
好了,按Ctrl+D可以退出這個容器了。
在容器運行期間,我們可以通過docker ps命令看到所有當前正在運行的容器。添加-a參數可以看到所有創建的容器:
docker ps -a [root@localhost ~]# docker ps -aCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
cb2b06c83a50 ubuntu:latest "sh -c /bin/bash" 7 minutes ago Exited (0) 7 seconds ago trusting_morse
每一個容器都有1個唯1的ID標識,通過ID可以對這個容器進行管理和操作。在創建容器時,我們可以通過–name參數指定1個容器名稱,如果沒有指定系統將會分配1個,就像這里的“trusting_morse”(甚么鬼TAT)。
當我們按Ctrl+D退出容器時,命令履行完了,所以容器也就退出了。要重新啟動這個容器,可使用docker start命令:
docker start -i trusting_morse 一樣,-i參數表示需要交互式支持。
注意:每次履行docker run命令都會創建新的容器,建議1次創建后,使用docker start/stop來啟動和停用容器。
存儲
在Docker容器運行期間,對文件系統的所有修改都會以增量的方式反應在容器使用的聯合文件系統中,其實不是真實的對只讀層數據信息修改。每次運行容器對它的修改,都可以理解成對夾心餅干又添加了1層奶油。這層奶油僅供當前容器使用。當刪除Docker容器,或通過該鏡像重新啟動時,之前的更改將會丟失。這樣做其實不便于我們持久化和同享數據。Docker的數據卷這個東西可以幫到我們。
在創建容器時,通過-v參數可以指定將容器內的某個目錄作為數據卷加載:
docker run -i -t -v /home/www ubuntu:latest sh -c '/bin/bash' 在容器中會多1個/home/www掛載點,在這個掛載點存儲數據會繞過聯合文件系統。我們可以通過下面的命令來找到這個數據卷在主機上真實的存儲位置:docker inspect -f {{.Volumes}} trusting_morse 你會看到輸出了1個指向到/var/lib/docker/vfs/dir/...的目錄。cd進入后你會發現在容器中對/home/www的讀寫創建,都會反應到這兒。事實上,/home/www就是掛載自這個位置。
有時候,我們需要將本地的文件目錄掛載到容器內的位置,一樣是使用數據卷這1個特性,-v參數格式為:
docker run -it -v [host_dir]:[container_dir]
host_dir是主機的目錄,container_dir是容器的目錄.
容器和容器之間是可以同享數據卷的,我們可以單獨創建1些容器,寄存如數據庫持久化存儲、配置文件1類的東西,但是這些容器其實不需要運行。
docker run --name dbdata ubuntu echo "Data container."在需要使用這個數據容器的容器創建時–volumes-from [容器名]的方式來使用這個數據同享容器。
網絡
Docker容器內的系統工作起來就像是1個虛擬機,容器內開放的端口其實不會真正開放在主機上。可使我們的容器更加安全,而且不會產生容器間端口的爭用。想要將Docker容器的端口開放到主機上,可使用類似端口映照的方式。
在Docker容器創建時,通過指定-p參數可以暴露容器的端口在主機上:
docker run -it -p 22 ubuntu sh -c '/bin/bash' 現在我們就將容器的22端口開放在了主機上,注意主機上對應端口是自動分配的。如果想要指定某個端口,可以通過-p [主機端口]:[容器端口]參數指定。
容器和容器之間想要網絡通訊,可以直接使用–link參數將兩個容器連接起來。例如WordPress容器對some-mysql的連接:
docker run --name some-wordpress --link some-mysql:mysql -p 8080:80 -d wordpress 環境變量
通過Docker打包的利用,對外就像是1個密閉的exe可履行文件。有時候我們希望Docker能夠使用1些外部的參數來使用容器,這時候候參數可以通過環境變量傳遞進去,通常情況下用來傳遞比如MySQL數據庫連接這類的東西。環境變量通過-e參數向容器傳遞:
docker run --name some-wordpress -e WORDPRESS_DB_HOST=10.1.2.3:3306 \
-e WORDPRESS_DB_USER=... -e WORDPRESS_DB_PASSWORD=... -d wordpress
關于Docker到現在就有了1個基本的認識了。接下來我會給大家介紹如何創建鏡像。
創建鏡像
Docker強大的威力在于可以把自己開發的利用隨同各種依賴環境1起打包、分發、運行。要創建1個新的Docker鏡像,通常基于1個已有的Docker鏡像來創建。Docker提供了兩種方式來創建鏡像:把容器創建為1個新的鏡像、使用Dockerfile創建鏡像。
將容器創建為鏡像
為了創建1個新的鏡像,我們先創建1個新的容器作為基底:
docker run -it ubuntu:latest sh -c '/bin/bash' 現在我們可以對這個容器進行修改了,例如我們可以配置PHP環境、將我們的項目代碼部署在里面等:apt-get install php
# some other opreations ...
當履行完操作以后,我們按Ctrl+D退出容器,接下來使用docker ps -a來查找我們剛剛創建的容器ID:docker ps -a 可以看到我們最后操作的那個ubuntu容器。這時候候只需要使用docker commit便可把這個容器變成1個鏡像了:docker commit 8d93082a9ce1 ubuntu:myubuntu這時候候docker容器會被創建為1個新的Ubuntu鏡像,版本名稱為myubuntu。以后我們可以隨時使用這個鏡像來創建容器了,新的容器將自動包括上面對容器的操作。
如果我們要在另外1臺機器上使用這個鏡像,可以將1個鏡像導出:
docker save -o myubuntu.tar.gz ubuntu:myubuntu 現在我們可以把剛才創建的鏡像打包為1個文件分發和遷移了。要在1臺機器上導入鏡像,只需要:docker import myubuntu.tar.gz這樣在新機器上就具有了這個鏡像。
注意:通過導入導出的方式分發鏡像其實不是Docker的最好實踐,由于我們有Docker Hub。
Docker Hub提供了類似GitHub的鏡像存管服務。1個鏡像發布到Docker Hub不但可以供更多人使用,而且便于鏡像的版本管理。關于Docker Hub的使用,以后我會單獨寫1篇文章展開介紹。另外,在1個企業內部可以通過自建docker-registry的方式來統1管理和發布鏡像。將Docker Registry集成到版本管理和上線發布的工作流當中,還有許多工作要做,在我整理出最好實踐后會第1時間分享。
使用Dockerfile創建鏡像
使用命令行的方式創建Docker鏡像通常難以自動化操作。在更多的時候,我們使用Dockerfile來創建Docker鏡像。Dockerfile是1個純文本文件,它記載了從1個鏡像創建另外一個新鏡像的步驟。撰寫好Dockerfile文件以后,我們就能夠輕而易舉的使用docker build命令來創建鏡像了.
Dockerfile非常簡單,唯一以下命令在Dockerfile中常被使用:
下面是1個Dockerfile的例子:
# This is a commentFROM ubuntu:14.04 MAINTAINER Kate Smith <ksmith@example.com> RUN apt-get update && apt-get install -y ruby ruby-dev
RUN gem install sinatra
這里其他命令都比較好理解,惟獨CMD和ENTRYPOINT我需要特殊說明1下。CMD命令可用指定Docker容器啟動時默許的命令,例如我們上面例子提到的docker run -it ubuntu:latest sh -c '/bin/bash'。其中sh -c '/bin/bash'就是通過手工指定傳入的CMD。如果我們不加這個參數,那末容器將會默許使用CMD指定的命令啟動。ENTRYPOINT是甚么呢?從字面看是進入點。沒錯,它就是進入點。ENTRYPOINT用來指定特定的可履行文件、Shell腳本,并把啟動參數或CMD指定的默許值,當作附加參數傳遞給ENTRYPOINT。
不好理解是吧?我們舉1個例子:
ENTRYPOINT ['/usr/bin/mysql']
CMD ['-h 192.168.100.128', '-p']
假定這個鏡像內已準備好了mysql-client,那末通過這個鏡像,不加任何額外參數啟動容器,將會給我們1個mysql的控制臺,默許連接到192.168.100.128這個主機。但是我們也能夠通過指定參數,來連接別的主機。但是不管不管如何,我們都沒法啟動1個除mysql客戶端之外的程序。由于這個容器的ENTRYPOINT就限定了我們只能在mysql這個客戶端內做事情。這下是否是明白了~
因此,我們在使用Dockerfile創建文件的時候,可以創建1個entrypoint.sh腳本,作為系統入口。在這個文件里面,我們可以進行1些基礎性的自舉操作,比如檢查環境變量,根據需要初始化數據庫等等。下面兩個文件是我在SimpleOA項目中添加的Dockerfile和entrypoint.sh,僅供參考:
-
https://github.com/starlight36/SimpleOA/blob/master/Dockerfile
-
https://github.com/starlight36/SimpleOA/blob/master/docker-entrypoint.sh
在準備好Dockerfile以后,我們就能夠創建鏡像了:docker build -t starlight36/simpleoa .關于Dockerfile的更詳細說明,請參考 https://docs.docker.com/reference/builder/。
雜項和最好實踐
在產品構建的生命周期里使用Docker,最好實踐是把Docker集成到現有的構建發布流程里面。這個進程其實不復雜,可以在延續集成系統構建測試完成后,將打包的步驟改成docker build,延續集成服務將會自動將構建相應的Docker鏡像。打包完成后,可以由延續集成系統自動將鏡像推送到Docker Registry中。生產服務器可以直接Pull最新版本的鏡像,更新容器便可很快地實現更新上線。目前Atlassian Bamboo已支持Docker的構建了。
由于Docker使用聯合文件系統,所以其實不用擔心屢次發布的版本會占用更多的磁盤資源,相同的鏡像只存儲1份。所以最好實踐是在不同層次上構建Docker鏡像。比如利用服務器依賴于PHP+Nginx環境,那末可以把定制好的這個PHP環境作為1個鏡像,利用服務器從這個鏡像構建鏡像。這樣做的好處是,如果PHP環境要升級,更新了這個鏡像后,重新構建利用鏡像便可完成升級,而不需要每一個利用項目分別升級PHP環境。
新手常常會有疑問的是關于Docker打包的粒度,比如MySQL要不要放在鏡像中?最好實踐是根據利用的范圍和可預感的擴大性來肯定Docker打包的粒度。例如某小型項目管理系統使用LAMP環境,由于團隊范圍和使用人數其實不會有太大的變化(可預計的團隊范圍范圍是幾人到幾千人),數據庫也不會承受沒法承載的記錄數(生命周期內可能1個表最多會有數10萬條記錄),并且客戶最關心的是快速部署使用。那末這時候候把MySQL作為依賴放在鏡像里是1種不錯的選擇。固然如果你在為1個互聯網產品打包,那最好就是把MySQL獨立出來,由于MySQL極可能會單獨做優化做集群等。
使用公有云構建發布運行Docker也是個不錯的選擇。DaoCloud提供了從構建到發布到運行的全生命周期服務。特別合適像微擎這類微信公眾平臺、或中小型企業CRM系統。上線周期更短,比使用IAAS、PAAS的云服務更具有優勢。