工做中不斷碰到Docker,今天終於算是正式開始學習了。在挑選系統學習Docker以及虛擬化技術的書籍時還碰到了很多麻煩,主要就是沒有特別經典的書!Docker的《初版Docker書》和《Docker技術入門與實戰》廣泛評價不高,而《Docker開發實踐》和《Dockeru源碼分析》又是2015年最近纔出的,評價不是不少。綜合看了下,最終仍是選擇了《Docker開發實踐》,如下都主要以這本書做爲學習資料。前端
《Docker開發實踐》中講了個故事:20世紀60年代之前的海運,貨物都放置在一塊兒,很容易擠壓受損。同時,不一樣的運輸方式之間的轉運也很麻煩,例如從碼頭和火車汽車轉運卸貨時。不一樣貨物和不一樣交通工具之間的組合是一個巨大的二維矩陣。海運界最後達成了一致,制定了國際標準集裝箱來解決這個棘手的問題。全部貨物都打包進集裝箱互相隔離,全部交通工具都經過集裝箱轉運,極大地提供了運輸的安全性和效率。linux
在軟件開發中咱們也常常碰到一樣的問題,使用了各類各樣技術框架的應用程序,從前端靜態網站到後端數據庫,從PHP到Java,以及多種多樣的部署環境,從測試服務器到線上環境,從虛擬機到公有云等等。Docker,正是這個集裝箱,而Docker的logo也的確是個集裝箱。nginx
很天然地咱們會問,Docker跟虛擬機有什麼區別啊?這個問題能夠拆成兩部分。由於Docker並非什麼徹底首創的技術,而是屬於很早便有了的容器技術,因此第一個問題就是容器與虛擬機的區別?同屬於容器技術,Docker的兄弟姐妹還有Solaris Zones、BSD jails、LXC等。但Docker如今這麼火,天然有它的獨到之處,因此第二個問題就是Docker與其餘容器的區別?git
關於第一個問題比較簡單,容器是一種輕量級的虛擬技術。它不像虛擬機那樣具備一套完整的CPU、內存和磁盤,對操做系統有絕對的權限。容器和宿主主機共享內核,全部容器共享操做系統,在一臺物理機上能夠運行成百上千的容器。第二個問題稍麻煩一些,與LXC相比,Docker對配置進行了抽象,使應用在任何平臺上的運行環境都一致。同時提供了版本控制、鏡像託管等相似Git的現代化設施和生態圈。github
整體來看,Docker的應用場景有:sql
Docker對Linux環境有兩個要求,一是64位系統,二是內核在3.8以上。而我使用的是Linux Mint 17的32位版,因此下載了源碼包準備編譯安裝。正愁找不到編譯安裝的資料時,發現Ubuntu軟件庫提供了已經編譯好的Docker 32位版,真是太好了!再看一下個人內核版本是3.13,也符合要求,因而直接用apt安裝。docker
cdai@dell ~ $ uname -a
Linux dell 3.13.0-37-generic #64-Ubuntu SMP Mon Sep 22 21:30:01 UTC 2014 i686 i686 i686 GNU/Linux
cdai@dell ~ $ apt-cache search docker
docker.io - Linux container runtime
kdocker - lets you dock any application into the system tray
vim-syntax-docker - Docker container engine - Vim highlighting syntax files
cdai@dell ~ $ sudo apt-get install docker.io
cdai@dell ~ $ docker -v
Docker version 1.0.1, build 990021a
儘管Docker能用了,可是官方Docker Hub中的鏡像都是爲64位系統準備的,下載這些鏡像後建立啓動容器時會報」finalize namespace drop capabilities operation not permitted」的錯誤。因此咱們能夠用官方提供的Dockerfile構建出32位版本的鏡像,才能在32位系統上使用。(鏡像構建的具體講解請參見第3.4節)數據庫
以構建32位版的Ubuntu爲例,執行官方GitHub上提供的Shell腳本便可。通過漫長的等待後,就能看到32位的Ubuntu鏡像已經成功安裝到咱們本地了,官方的腳本果真仍是挺靠譜的。bootstrap
cdai ~ $ docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
32bit/ubuntu 14.04 c062cc00654e About a minute ago 295.3 MB
注意:這裏爲了在個人老本子上學習研究Docker而作了workaround,真正應用時固然仍是必定要在64位機器上使用Docker的。ubuntu
在開始使用Docker以前,首先要了解Docker中的概念和它們之間的關係,不然直接上手可能會搞得一頭霧水。Docker中最重要的三個概念就是:鏡像、容器、庫。
下面就是典型的Docker工做流,從這張圖中能清晰地理解這三個重要概念之間的關係。本節接下來就根據這個Workflow逐一介紹經常使用的操做。
首先用docker search [keyword]
命令查看Docker Hub上都有哪些鏡像能夠下載,search後能夠用通配符表示關鍵字:
cdai ~ $ docker search ubuntu
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
ubuntu Ubuntu is a Debian-based Linux operating s... 2179 [OK]
ubuntu-upstart Upstart is an event-based replacement for ... 31 [OK]
torusware/speedus-ubuntu Always updated official Ubuntu docker imag... 25 [OK]
tleyden5iwx/ubuntu-cuda Ubuntu 14.04 with CUDA drivers pre-installed 17 [OK]
ubuntu-debootstrap debootstrap --variant=minbase --components... 12 [OK]
neurodebian NeuroDebian provides neuroscience research... 11 [OK]
...
接下來用docker pull [repository/url:tag]
命令下載鏡像。由於從官方Docker Hub下載很是慢,因此這裏從國內的鏡像站http://dockerpool.com/下載,速度很是快。(注:後面會講到用docker run命令建立容器,其實若是鏡像不存在Docker會自動去下載,這裏爲了學習pull命令因此手動下載鏡像)
cdai ~ $ docker pull dl.dockerpool.com:5000/ubuntu:14.04
cdai ~ $ docker pull dl.dockerpool.com:5000/centos:latest
下載完成後,就能夠用docker images
查看本地都有哪些鏡像。這裏的REPOSITORY列可能有三種類型:
cdai ~ $ docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
dl.dockerpool.com:5000/ubuntu 14.04 5506de2b643b 10 months ago 199.3 MB
dl.dockerpool.com:5000/centos latest 87e5b6b3ccc1 11 months ago 224 MB
若是想要查看鏡像的詳細信息,能夠用docker inspect [image-id]
命令查看。(注:下面容器一節會看到,這個命令也可以用來查看容器的詳細信息)
cdai ~ $ docker inspect 5506de2b643b
[{
"Architecture": "amd64",
"Author": "",
"Comment": "",
"Config": {
"AttachStderr": false,
"AttachStdin": false,
"AttachStdout": false,
"Cmd": [
"/bin/bash"
],
...
},
"Container": "201ae89099c20e577dd9c60cb9199c2dac0688d49efa676bf3eeb859666294bd",
"ContainerConfig": {
"AttachStderr": false,
"AttachStdin": false,
"AttachStdout": false,
...
},
"Created": "2014-10-23T23:53:59.03271073Z",
"DockerVersion": "1.3.0",
"Id": "5506de2b643be1e6febbf3b8a240760c6843244c41e12aa2f60ccbb7153d17f5",
"Os": "linux",
"Parent": "22093c35d77bb609b9257ffb2640845ec05018e3d96cb939f68d0e19127f1723",
"Size": 0
}]
瞭解了鏡像的基本操做後,咱們就能夠建立容器了。首先用docker create
建立容器或者用docker run [repository:tag]
建立並運行容器。容器能夠分爲兩種類型:
docker stop
或docker kill
也能終止容器。docker stop
或docker kill
來終止。說明:由於個人老本子是32位的,即使從第三方Hub安裝了鏡像也都是64位的沒法使用,因此這裏的例子都使用第一節中手動構建出的32位Ubuntu鏡像。
首先咱們建立運行一個交互型容器試試,在容器的控制檯裏簡單的輸出個」Hello,Docker」。怎麼樣?Docker容器很是輕量級,啓動很是快吧!用docker ps
能夠查看正在運行的容器,用docker ps -a
查看全部容器,包括未啓動的容器。(-l和-n=x能列出最後建立的一個或x個容器)
cdai ~ $ docker run -i -t 32bit/ubuntu:14.04 /bin/bash
root@328aa6305d82:/# echo "Hello, Docker!"
Hello, Docker!
root@328aa6305d82:/# exit
exit
cdai ~ $ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
328aa6305d82 32bit/ubuntu:14.04 /bin/bash 24 seconds ago Exited (0) 4 seconds ago stupefied_curie
如今再建立運行一個後臺型容器。能夠用restart參數設置容器異常退出時自動重啓:」always」無論什麼返回碼都嘗試重啓容器;」on-failure:x」則只在返回碼非0時纔會重啓,並嘗試x次。最後能夠接上一個容器啓動後執行的命令。這裏爲了作一個例子學習,因此用一個循環每隔5秒輸出」hello world」。(Docker 1.3後提供了docker exec
用於在容器運行以後中途啓動另外一個程序)
cdai ~ $ docker run -d 32bit/ubuntu:14.04 /bin/bash -c "while true; do echo hello world; sleep 5; done"
5c40590cc23cfa02bd6c0220da77b601973f01f8bc430baca22315ef68d81e44
cdai ~ $ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5c40590cc23c 32bit/ubuntu:14.04 /bin/bash -c while 5 seconds ago Up 5 seconds evil_bartik
如今容器已經在後臺運行起來了,沒有控制檯咱們怎樣能查看容器的狀態呢?Docker提供了一些很實用的容器內操做命令,例如docker logs [container-name]
可以查看容器內標準輸出流中的內容,docker top [container-name]
查看容器中的全部進程。下面就用這兩個命令查看一下剛纔啓動的容器運行得怎麼樣了?注意:啓動容器時若是沒用name參數指定容器名稱的話,Docker會自動生成一個,下面的命令都用這個名稱做爲參數。
cdai ~ $ docker logs -f evil_bartik
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
^C
cdai ~ $ docker top evil_bartik
UID PID PPID C STIME TTY TIME CMD
root 26803 1700 0 21:54 ? 00:00:00 /bin/bash -c while true; do echo hello world; sleep 5; done
root 26859 26803 0 21:55 ? 00:00:00 sleep 5
能夠看到咱們的容器運行良好,循環體在不斷輸出」hello world」到輸出流中。以前學習鏡像說過docker inspect
也能夠用於容器,如今就用它查看一下容器的詳細信息。從輸出能夠清晰地看到啓動命令、環境參數、網絡等信息。
cdai ~ $ docker inspect evil_bartik
[{
"Args": [
"-c",
"while true; do echo hello world; sleep 5; done"
],
"Config": {
"AttachStderr": false,
"AttachStdin": false,
"AttachStdout": false,
"Hostname": "5c40590cc23c",
"Image": "32bit/ubuntu:14.04",
...
},
"Created": "2015-09-03T13:54:03.389202827Z",
"Driver": "aufs",
...
"MountLabel": "",
"Name": "/evil_bartik",
"NetworkSettings": {
"Bridge": "docker0",
"Gateway": "172.17.42.1",
"IPAddress": "172.17.0.5",
"IPPrefixLen": 16,
"PortMapping": null,
"Ports": {}
},
...
}]
容器學習的差很少了,如今就把它停下來了吧。以前說過,對於後臺型容器有docker stop [container-name]
和docker kill [container-name]
兩種方法。那它們有什麼區別呢?前者會給容器內的進程發送SIGTERM信號,默認行爲是容器退出,固然容器內的程序也能夠捕獲該信號後自行處理。然後者會給容器內的進程發送SIGKILL信號,致使容器直接退出。
cdai ~ $ docker stop evil_bartik
evil_bartik
cdai ~ $ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5c40590cc23c 32bit/ubuntu:14.04 /bin/bash -c while 3 minutes ago Exited (-1) 5 seconds ago evil_bartik
328aa6305d82 32bit/ubuntu:14.04 /bin/bash 11 minutes ago Exited (0) 11 minutes ago stupefied_curie
本地鏡像的製做有使用commit命令和編寫Dockerfile兩種方式。下面就先分別介紹這兩種製做方式,而後學習一下如何把咱們本身製做的本地鏡像上傳到Docker Hub上。
首先啓動咱們以前建立的交互型容器stupefied_curie。啓動成功後會發現咱們並無進入容器的控制檯,這時就要使用以前沒有介紹的一個容器內使用的命令,attach命令,幫咱們從新進入一個已啓動容器的控制檯。(注意:執行attach後,要按一次回車纔會出現容器的控制檯界面。並且後臺型容器是沒法attach的)
cdai ~ $ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
328aa6305d82 32bit/ubuntu:14.04 /bin/bash 10 hours ago Exited (0) 10 hours ago stupefied_curie
attach上以後咱們就能夠在容器內操做了,這裏咱們安裝一個Sqlite數據庫並保存一個文本文件,準備好要製做成本地鏡像的容器。本覺得一切會很順利,結果卻碰到了容器內沒法上網的問題,由於默認容器與宿主主機是橋接的關係須要配置一些DNS等設置。上網查了一下最簡單的方法就是改一下網絡模式,讓容器使用與宿主主機同樣的網絡棧。這裏從新建立一個交互型容器,網絡模式使用host模式。
cdai ~ $ docker run -i -t --net="host" 32bit/ubuntu:14.04 /bin/bash
root@cdai:/# apt-get install sqlite3
Reading package lists... Done
Building dependency tree
Reading state information... Done
Suggested packages:
sqlite3-doc
The following NEW packages will be installed:
sqlite3
0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
Need to get 28.3 kB of archives.
After this operation, 161 kB of additional disk space will be used.
Get:1 http://archive.ubuntu.com/ubuntu/ trusty-updates/main sqlite3 i386 3.8.2-1ubuntu2.1 [28.3 kB]
Fetched 28.3 kB in 2s (10.8 kB/s)
Selecting previously unselected package sqlite3.
(Reading database ... 11535 files and directories currently installed.)
Preparing to unpack .../sqlite3_3.8.2-1ubuntu2.1_i386.deb ...
Unpacking sqlite3 (3.8.2-1ubuntu2.1) ...
Setting up sqlite3 (3.8.2-1ubuntu2.1) ...
root@cdai:/# echo "test docker commit" >> hellodocker
root@cdai:/# exit
exit
如今就能夠用docker commit [container-id]
命令將前面準備好的容器製做成鏡像了!(爲何有的命令用ID有的用name呢?)執行完成後,就能查咱們的鏡像已安裝到本地庫了。這裏還發現了一個問題,就是基於32位鏡像建立的容器,再製做成鏡像好像又是64位了,啓動時又會報」finalize namespace drop capabilities operation not permitted」錯誤,:(
cdai ~ $ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1718e35463ff 32bit/ubuntu:14.04 /bin/bash 53 seconds ago Exited (0) 8 seconds ago boring_babbage
cdai ~ $ docker commit --author="cdai" 1718e35463ff cdai/sqlite3:v1
e10622157a5df7c82bdf148bda021e0fe312c153852f7a6c765c216d4d636ecb
cdai ~ $ docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
cdai/sqlite3 v1 e10622157a5d 6 seconds ago 321.8 MB
32bit/ubuntu 14.04 c062cc00654e 11 hours ago 295.3 MB
上面咱們在容器的控制檯上手動執行一些操做,而Dockerfile是一種更加透明而且可重複的製做方式,由於咱們不是手動執行操做,而是將全部操做用Docker提供的命令和語法編寫到Dockerfile中。命令名所有大寫,經常使用的Dockerfile命令有:
其餘命令還有:MAINTAINER聲明做者信息、WORKDIR指定工做目錄(最後一個會做爲容器啓動後的工做目錄)、VOLUME掛載文件、CMD和ENTRYPOINT指定容器啓動後執行的命令、ONBUILD指定一些命令在當前鏡像構建時不會執行,而是在子鏡像構建時觸發。
瞭解了這些經常使用的命令,下面就能夠開始編寫Dockerfile了。注意:Dockerfile文件名默認就叫」Dockerfile」,不然執行build命令時Docker會找不到。編寫好後,執行docker build .
就能夠開始構建了,每一條Dockerfile的命令都至關於構建出一個臨時鏡像,最後一步會生成最終的目標鏡像。
cdai $ ls
-rw-r--r-- 1 root root 17 Sep 4 09:08 abc.txt
-rw-r--r-- 1 root root 188 Sep 4 09:08 Dockerfile
cdai $ cat Dockerfile
FROM 32bit/ubuntu:14.04
MAINTAINER cdai "XXX@163.com"
USER root
RUN apt-get install -y nginx
EXPOSE 80 8080
RUN touch test.txt
ADD abc.txt .
ENTRYPOINT ["ls"]
CMD ["-a", "-l"]
cdai $ docker build -t cdai/nginx:v1 .
Sending build context to Docker daemon 3.584 kB
Sending build context to Docker daemon
Step 0 : FROM 32bit/ubuntu:14.04
---> c062cc00654e
Step 1 : MAINTAINER cdai "dc_726@163.com"
---> Running in 53ece79679ed
---> 979f81286bbe
Removing intermediate container 53ece79679ed
Step 2 : USER root
---> Running in f5d29e4895e5
---> 4965bbb8d8cd
Removing intermediate container f5d29e4895e5
Step 3 : RUN apt-get install -y nginx
---> Running in d4e46baff94a
Reading package lists...
Building dependency tree...
Reading state information...
The following extra packages will be installed:
fontconfig-config fonts-dejavu-core geoip-database libfontconfig1
libfreetype6 libgd3 libgeoip1 libjbig0 libjpeg-turbo8 libjpeg8 libtiff5
libvpx1 libx11-6 libx11-data libxau6 libxcb1 libxdmcp6 libxml2 libxpm4
libxslt1.1 nginx-common nginx-core sgml-base xml-core
Suggested packages:
libgd-tools geoip-bin fcgiwrap nginx-doc sgml-base-doc debhelper
...
Setting up nginx (1.4.6-1ubuntu3.3) ...
---> 73f5d909ac29
Removing intermediate container d4e46baff94a
Step 4 : EXPOSE 80 8080
---> Running in 4333d2f3c58c
---> b693316fb774
Removing intermediate container 4333d2f3c58c
Step 5 : RUN touch test.txt
---> Running in 815dfbfb2005
...
首先用docker login
命令輸入咱們在Docker Hub的登陸信息,Docker會將其保存到~/.dockercfg中。
cdai $ docker login
Username: cdai
Password:
Email: XXX@163.com
Login Succeeded
cdai $ cat ~/.dockercfg
{"https://index.docker.io/v1/":{"auth":"XXXXaToxXXXXXXlEYiE=","email":"XXX@163.com"}}
cdai $ docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
cdai/sqlite3 v1 e10622157a5d 37 minutes ago 321.8 MB
32bit/ubuntu 14.04 c062cc00654e 11 hours ago 295.3 MB
cdai $ docker push cdai/sqlite3:v1
The push refers to a repository [cdai/sqlite3] (len: 1)
Sending image list
Pushing repository cdai/sqlite3 (1 tags)
c062cc00654e: Buffering to disk
...