Docker技術入門
原文 http://starlight36.com/post/startup-to-docker
關注Docker這項技術差不多一年多了,最近關于Docker的使用案例越來越多,于是就想動手實驗下這項技術。自己整理了一個入門手冊,分享給大家。
Docker是什么
Docker是一種容器技術,它可以將應用和環境等進行打包,形成一個獨立的,類似于iOS的APP形式的“應用”,這個應用可以直接被分發到任 意一個支持Docker的環境中,通過簡單的命令即可啟動運行。Docker是一種最流行的容器化實現方案。和虛擬化技術類似,它極大的方便了應用服務的 部署;又與虛擬化技術不同,它以一種更輕量的方式實現了應用服務的打包。使用Docker可以讓每個應用彼此相互隔離,在同一臺機器上同時運行多個應用, 不過他們彼此之間共享同一個操作系統。Docker的優勢在于,它可以在更細的粒度上進行資源的管理,也比虛擬化技術更加節約資源。
虛擬化和Docker架構對比,來自Docker官網
基本概念
開始試驗Docker之前,我們先來了解一下Docker的幾個基本概念:
鏡像:我們可以理解為一個預配置的系統光盤,這個光盤插入電腦后就可以啟動一個操作系統。當然由于是光盤,所以你無法修改它或者保存數據,每次重啟都是一個原樣全新的系統。Docker里面鏡像基本上和這個差不多。
容器:同樣一個鏡像,我們可以同時啟動運行多個,運行期間的產生的這個實例就是容器。把容器內的操作和啟動它的鏡像進行合并,就可以產生一個新的鏡像。
開始
Docker基于 LXC 技術實現,依賴于Linux內核,所以Docker目前只能在Linux以原生方式運行。目前主要的Linux發行版在他們的軟件倉庫中內置了Docker:
Ubuntu:
- Ubuntu Trusty 14.04 (LTS)
- Ubuntu Precise 12.04 (LTS)
- Ubuntu Saucy 13.10 </ul>
- CentOS 7 </ul>
CentOS:
Docker要求64位環境,這些操作系統下可以直接通過命令安裝Docker,老一些操作系統Docker官方也提供了安裝方案。下面的實驗基于CentOS 7進行。關于其他版本操作系統上Docker的安裝,請參考官方文檔: https://docs.docker.com/installation/
在CentOS 7上安裝Docker
使用yum從軟件倉庫安裝Docker:
Shell
</div>yum install docker</div>
首先啟動Docker的守護進程:
Shell
</div>service docker start
</div>
如果想要Docker在系統啟動時運行,執行:
Shell
</div>chkconfig docker on</div>
Docker在CentOS上好像和防火墻有沖突,應用防火墻規則后可能導致Docker無法聯網,重啟Docker可以解決。
Docker倉庫
Docker使用類似git的方式管理鏡像。通過基本的鏡像可以定制創建出來不同種應用的Docker鏡像。 Docker Hub 是Docker官方提供的鏡像中心。在這里可以很方便地找到各類應用、環境的鏡像。
由于Docker使用聯合文件系統,所以鏡像就像是夾心餅干一樣一層層構成,相同底層的鏡像可以共享。所以Docker還是相當節約磁盤空間的。要使用一個鏡像,需要先從遠程的鏡像注冊中心拉取,這點非常類似git。
Shell
</div>docker pull ubuntu</div>
我們很容易就能從Docker Hub鏡像注冊中心下載一個最新版本的ubuntu鏡像到本地。國內網絡可能會稍慢, DaoCloud 提供了Docker Hub的國內加速服務,可以嘗試配置使用。
運行一個容器
使用Docker最關鍵的一步就是從鏡像創建容器。有兩種方式可以創建一個容器:使用docker create命令創建容器,或者使用docker run命令運行一個新容器。兩個命令并沒有太大差別,只是前者創建后并不會立即啟動容器。
以ubuntu為例,我們啟動一個新容器,并將ubuntu的Shell作為入口:
Shell
</div>docker run -it ubuntu:latest sh -c '/bin/bash'</div>
這時候我們成功創建了一個Ubuntu的容器,并將當前終端連接為這個Ubuntu的bash shell。這時候就可以愉快地使用Ubuntu的相關命令了~可以快速體驗一下。
參數-i表示這是一個交互容器,會把當前標準輸入重定向到容器的標準輸入中,而不是終止程序運行。-t指為這個容器分配一個終端。
好了,按Ctrl+D可以退出這個容器了。
在容器運行期間,我們可以通過docker ps命令看到所有當前正在運行的容器。添加-a參數可以看到所有創建的容器:
Shell
</div>docker ps -a
[root@localhost ~]# docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES cb2b06c83a50 ubuntu:latest "sh -c /bin/bash" 7 minutes ago Exited (0) 7 seconds ago trusting_morse
</div>
每個容器都有一個唯一的ID標識,通過ID可以對這個容器進行管理和操作。在創建容器時,我們可以通過–name參數指定一個容器名稱,如果沒有指定系統將會分配一個,就像這里的“trusting_morse”(什么鬼。。)。
當我們按Ctrl+D退出容器時,命令執行完了,所以容器也就退出了。要重新啟動這個容器,可以使用docker start命令:
Shell
</div>docker start -i trusting_morse
</div>
同樣,-i參數表示需要交互式支持。
注意:每次執行docker run命令都會創建新的容器,建議一次創建后,使用docker start/stop來啟動和停用容器。
存儲
在Docker容器運行期間,對文件系統的所有修改都會以增量的方式反映在容器使用的聯合文件系統中,并不是真正的對只讀層數據信息修改。每次運 行容器對它的修改,都可以理解成對夾心餅干又添加了一層奶油。這層奶油僅供當前容器使用。當刪除Docker容器,或通過該鏡像重新啟動時,之前的更改將 會丟失。這樣做并不便于我們持久化和共享數據。Docker的數據卷這個東西可以幫到我們。
在創建容器時,通過-v參數可以指定將容器內的某個目錄作為數據卷加載:
Shell
</div>docker run -it -v /home/www ubuntu:latest sh -c '/bin/bash'</div>
在容器中會多一個/home/www掛載點,在這個掛載點存儲數據會繞過聯合文件系統。我們可以通過下面的命令來找到這個數據卷在主機上真正的存儲位置:
Shell
</div>docker inspect -f {{.Volumes}} trusting_morse</div>
你會看到輸出了一個指向到/var/lib/docker/vfs/dir/…的目錄。cd進入后你會發現在容器中對/home/www的讀寫創建,都會反映到這兒。事實上,/home/www就是掛載自這個位置。
有時候,我們需要將本地的文件目錄掛載到容器內的位置,同樣是使用數據卷這一個特性,-v參數格式為:
Shell
</div>docker run -it -v [host_dir]:[container_dir]</div>
host_dir是主機的目錄,container_dir是容器的目錄。
容器和容器之間是可以共享數據卷的,我們可以單獨創建一些容器,存放如數據庫持久化存儲、配置文件一類的東西,然而這些容器并不需要運行。
Shell
</div>docker run --name dbdata ubuntu echo "Data container."</div>
在需要使用這個數據容器的容器創建時–volumes-from [容器名]的方式來使用這個數據共享容器。
網絡
Docker容器內的系統工作起來就像是一個虛擬機,容器內開放的端口并不會真正開放在主機上。可以使我們的容器更加安全,而且不會產生容器間端口的爭用。想要將Docker容器的端口開放到主機上,可以使用類似端口映射的方式。
在Docker容器創建時,通過指定-p參數可以暴露容器的端口在主機上:
Shell
</div>docker run -it -p 22 ubuntu sh -c '/bin/bash'</div>
現在我們就將容器的22端口開放在了主機上,注意主機上對應端口是自動分配的。如果想要指定某個端口,可以通過-p [主機端口]:[容器端口]參數指定。
容器和容器之間想要網絡通訊,可以直接使用–link參數將兩個容器連接起來。例如WordPress容器對some-mysql的連接:
Shell
</div>docker run --name some-wordpress --link some-mysql:mysql -p 8080:80 -d wordpress</div>
環境變量
通過Docker打包的應用,對外就像是一個密閉的exe可執行文件。有時候我們希望Docker能夠使用一些外部的參數來使用容器,這時候參數可以通過環境變量傳遞進去,通常情況下用來傳遞比如MySQL數據庫連接這種的東西。環境變量通過-e參數向容器傳遞:
Shell
</div>docker run --name some-wordpress -e WORDPRESS_DB_HOST=10.1.2.3:3306 \ -e WORDPRESS_DB_USER=... -e WORDPRESS_DB_PASSWORD=... -d wordpress
</div>
關于Docker到現在就有了一個基本的認識了。接下來我會給大家介紹如何創建鏡像,以及如何利用公有云直接發布的基于Docker的應用。
創建鏡像
Docker強大的威力在于可以把自己開發的應用隨同各種依賴環境一起打包、分發、運行。要創建一個新的Docker鏡像,通常基于一個已有的Docker鏡像來創建。Docker提供了兩種方式來創建鏡像:把容器創建為一個新的鏡像、使用Dockerfile創建鏡像。
將容器創建為鏡像
為了創建一個新的鏡像,我們先創建一個新的容器作為基底:
Shell
</div>docker run -it ubuntu:latest sh -c '/bin/bash'</div>
現在我們可以對這個容器進行修改了,例如我們可以配置PHP環境、將我們的項目代碼部署在里面等:
Shell
</div>apt-get install php # some other opreations ...</div>
當執行完操作之后,我們按Ctrl+D退出容器,接下來使用docker ps -a來查找我們剛剛創建的容器ID:
Shell
</div>docker ps -a</div>
可以看到我們最后操作的那個ubuntu容器。這時候只需要使用docker commit即可把這個容器變為一個鏡像了:
Shell
</div>docker commit 8d93082a9ce1 ubuntu:myubuntu
</div>
這時候docker容器會被創建為一個新的Ubuntu鏡像,版本名稱為myubuntu。以后我們可以隨時使用這個鏡像來創建容器了,新的容器將自動包含上面對容器的操作。
如果我們要在另外一臺機器上使用這個鏡像,可以將一個鏡像導出:
Shell
</div>docker save -o myubuntu.tar.gz ubuntu:myubuntu</div>
現在我們可以把剛才創建的鏡像打包為一個文件分發和遷移了。要在一臺機器上導入鏡像,只需要:
docker import myubuntu.tar.gz
這樣在新機器上就擁有了這個鏡像。
注意:通過導入導出的方式分發鏡像并不是Docker的最佳實踐,因為我們有Docker Hub。
Docker Hub提供了類似GitHub的鏡像存管服務。一個鏡像發布到Docker Hub不僅可以供更多人使用,而且便于鏡像的版本管理。關于Docker Hub的使用,之后我會單獨寫一篇文章展開介紹。另外,在一個企業內部可以通過自建 Docker Registry 的方式來統一管理和發布鏡像。將Docker Registry集成到版本管理和上線發布的工作流之中,還有許多工作要做,在我整理出最佳實踐后會第一時間分享。
使用Dockerfile創建鏡像
使用命令行的方式創建Docker鏡像通常難以自動化操作。在更多的時候,我們使用Dockerfile來創建Docker鏡像。 Dockerfile是一個純文本文件,它記載了從一個鏡像創建另一個新鏡像的步驟。撰寫好Dockerfile文件之后,我們就可以輕而易舉的使用 docker build命令來創建鏡像了。
Dockerfile非常簡單,僅有以下命令在Dockerfile中常被使用:
命令 | 參數 | 說明 | </tr>|||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
# | – | 注釋說明 | </tr>|||||||||||||||||||
FROM | <image>[:<tag>] | 從一個已有鏡像創建,例如ubuntu:latest | </tr>|||||||||||||||||||
MAINTAINER | Author <some-one@example.com> | 鏡像作者名字,如Max Liu <some-one@example.com> | </tr>|||||||||||||||||||
RUN | <cmd>或者[‘cmd1’, ‘cmd2’…] | 在鏡像創建用的臨時容器里執行單行命令 | </tr>|||||||||||||||||||
ADD | <src> <dest> | 將本地的<src>添加到鏡像容器中的<dest>位置 | </tr>|||||||||||||||||||
VOLUME | <path>或者[‘/var’, ‘home’] | 將指定的路徑掛載為數據卷 | </tr>|||||||||||||||||||
EXPOSE | <port> [<port>…] | 將指定的端口暴露給主機 | </tr>|||||||||||||||||||
ENV | <key> <value> 或者 <key> = <value> | 指定環境變量值 | </tr>|||||||||||||||||||
CMD | [“executable”,”param1″,”param2″] | 容器啟動時默認執行的命令。注意一個Dockerfile中只有最后一個CMD生效。 | </tr>|||||||||||||||||||
ENTRYPOINT | [“executable”, “param1″, “param2″] | 容器的進入點。 | </tr> </tbody> </table>