Docker 基礎

1. Docker 簡介

1.1 什麼是 Docker?

Docker的英文翻譯是「搬運工」的意思,他搬運的東西就是咱們常說的集裝箱Container,Container 裏面裝的是任意類型的 App,咱們的開發人員能夠經過 Docker 將App 變成一種標準化的、可移植的、自管理的組件,咱們能夠在任何主流的操做系統中開發、調試和運行。php

從概念上來看 Docker 和咱們傳統的虛擬機比較相似,只是更加輕量級,更加方便使,Docker 和虛擬機最主要的區別有如下幾點:html

  • 虛擬化技術依賴的是物理CPU和內存,是硬件級別的;而咱們的 Docker 是構建在操做系統層面的,利用操做系統的容器化技術,因此 Docker 一樣的能夠運行在虛擬機上面。
  • 咱們知道虛擬機中的系統就是咱們常說的操做系統鏡像,比較複雜;而 Docker 比較輕量級,咱們能夠用 Docker 部署一個獨立的 Redis,就相似於在虛擬機當中安裝一個 Redis 應用,可是咱們用 Docker 部署的應用是徹底隔離的。
  • 咱們都知道傳統的虛擬化技術是經過快照來保存狀態的;而 Docker 引入了相似於源碼管理的機制,將容器的快照歷史版本一一記錄下來,切換成本很是之低。
  • 傳統虛擬化技術在構建系統的時候很是複雜;而 Docker 能夠經過一個簡單的 Dockerfile 文件來構建整個容器,更重要的是 Dockerfile 能夠手動編寫,這樣應用程序開發人員能夠經過發佈 Dockerfile 來定義應用的環境和依賴,這樣對於持續交付很是有利。 ​​​​

1.2 爲啥要用容器?

應用容器是個啥樣子呢,一個作好的應用容器長得就像一個裝好了一組特定應用的虛擬機同樣,好比我如今想用 Redis,那我就找個裝好了 Redis 的容器就能夠了,而後運行起來,我就能直接使用了。node

那爲何不能直接安裝一個 Redis 呢?確定是可行的,可是有的時候根據每一個人電腦的不一樣,在安裝的時候可能會報出各類各樣的錯誤,萬一你的機器中毒了,你的電腦掛了,你全部的服務都須要從新安裝。可是有了 Docker 或者說有了容器就不同了,你就至關於有了一個能夠運行起來的虛擬機,只要你能運行容器,Redis 的配置就省了。並且若是你想換個電腦,沒問題,很簡單,直接把容器」端過來」就可使用容器裏面的服務了。python

1.3 Docker Engine

Docker Engine是一個C/S架構的應用程序,主要包含下面幾個組件:mysql

  • 常駐後臺進程Dockerd
  • 一個用來和 Dockerd 交互的 REST API Server
  • 命令行CLI接口,經過和 REST API 進行交互(咱們常用的 docker 命令)

1.4 Docker 架構

Docker 使用 C/S (客戶端/服務器)體系的架構,Docker 客戶端與 Docker 守護進程通訊,Docker 守護進程負責構建,運行和分發 Docker 容器。Docker 客戶端和守護進程能夠在同一個系統上運行,也能夠將 Docker 客戶端鏈接到遠程 Docker 守護進程。Docker 客戶端和守護進程使用 REST API 經過UNIX套接字或網絡接口進行通訊。 ​​linux

  • Docker Damon:dockerd,用來監聽 Docker API 的請求和管理 Docker 對象,好比鏡像、容器、網絡和 Volume。
  • Docker Client:docker,docker client 是咱們和 Docker 進行交互的最主要的方式方法,好比咱們能夠經過 docker run 命令來運行一個容器,而後咱們的這個 client 會把命令發送給上面的 Dockerd,讓他來作真正事情。
  • Docker Registry:用來存儲 Docker 鏡像的倉庫,Docker Hub 是 Docker 官方提供的一個公共倉庫,並且 Docker 默認也是從 Docker Hub 上查找鏡像的,固然你也能夠很方便的運行一個私有倉庫,當咱們使用 docker pull 或者 docker run 命令時,就會從咱們配置的 Docker 鏡像倉庫中去拉取鏡像,使用 docker push 命令時,會將咱們構建的鏡像推送到對應的鏡像倉庫中。
  • Images:鏡像,鏡像是一個只讀模板,帶有建立 Docker 容器的說明,通常來講的,鏡像會基於另外的一些基礎鏡像並加上一些額外的自定義功能。好比,你能夠構建一個基於 Centos 的鏡像,而後在這個基礎鏡像上面安裝一個 Nginx 服務器,這樣就能夠構成一個屬於咱們本身的鏡像了。
  • Containers:容器,容器是一個鏡像的可運行的實例,可使用 Docker REST API 或者 CLI 來操做容器,容器的實質是進程,但與直接在宿主執行的進程不一樣,容器進程運行於屬於本身的獨立的命名空間。所以容器能夠擁有本身的 root 文件系統、本身的網絡配置、本身的進程空間,甚至本身的用戶 ID 空間。容器內的進程是運行在一個隔離的環境裏,使用起來,就好像是在一個獨立於宿主的系統下操做同樣。這種特性使得容器封裝的應用比直接在宿主運行更加安全。
  • 底層技術支持:Namespaces(作隔離)、CGroups(作資源限制)、UnionFS(鏡像和容器的分層) the-underlying-technology Docker 底層架構分析

1.5 安裝

直接前往官方文檔選擇合適的平臺安裝便可,好比咱們這裏想要在centos系統上安裝 Docker,這前往地址https://docs.docker.com/install/linux/docker-ce/centos/根據提示安裝便可。nginx

安裝依賴軟件包:git

$ sudo yum install -y yum-utils device-mapper-persistent-data lvm2

 

添加軟件倉庫,咱們這裏使用穩定版 Docker,執行下面命令添加 yum 倉庫地址:golang

$ sudo yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo

 

而後直接安裝便可:web

$ sudo yum install docker-ce

 

若是要安裝指定的版本,可使用 yum list 列出可用的版本:

$ yum list docker-ce --showduplicates | sort -r
docker-ce.x86_64            18.03.0.ce-1.el7.centos             docker-ce-stable

 

好比這裏能夠安裝18.03.0.ce版本:

$ sudo yum install docker-ce-18.03.0.ce

 

要啓動 Docker 也很是簡單:

$ sudo systemctl enable docker
$ sudo systemctl start docker

 

另一種安裝方式是能夠直接下載指定的軟件包直接安裝便可,前往地址:https://download.docker.com/linux/centos/7/x86_64/stable/Packages/ 找到合適的.rpm包下載,而後安裝便可:

$ sudo yum install /path/to/package.rpm

 

2. 鏡像和容器的基本操做

這節課給你們講解Docker鏡像和容器的一些基本操做方法。

2.1 獲取鏡像

以前咱們提到過 Docker 官方提供了一個公共的鏡像倉庫:Docker Hub,咱們就能夠從這上面獲取鏡像,獲取鏡像的命令:docker pull,格式爲:

$ docker pull [選項] [Docker Registry 地址[:端口]/]倉庫名[:標籤] 
  • Docker 鏡像倉庫地址:地址的格式通常是 <域名/IP>[:端口號],默認地址是 Docker Hub。
  • 倉庫名:這裏的倉庫名是兩段式名稱,即 <用戶名>/<軟件名>。對於 Docker Hub,若是不給出用戶名,則默認爲 library,也就是官方鏡像。好比:
    $ docker pull ubuntu:16.04
    16.04: Pulling from library/ubuntu
    bf5d46315322: Pull complete
    9f13e0ac480c: Pull complete
    e8988b5b3097: Pull complete
    40af181810e7: Pull complete
    e6f7c7e5c03e: Pull complete
    Digest: sha256:147913621d9cdea08853f6ba9116c2e27a3ceffecf3b492983ae97c3d643fbbe
    Status: Downloaded newer image for ubuntu:16.04

     

    上面的命令中沒有給出 Docker 鏡像倉庫地址,所以將會從 Docker Hub 獲取鏡像。而鏡像名稱是 ubuntu:16.04,所以將會獲取官方鏡像 library/ubuntu 倉庫中標籤爲 16.04 的鏡像。 從下載過程當中能夠看到咱們以前說起的分層存儲的概念,鏡像是由多層存儲所構成。下載也是一層層的去下載,並不是單一文件。下載過程當中給出了每一層的 ID 的前 12 位。而且下載結束後,給出該鏡像完整的sha256的摘要,以確保下載一致性。

2.2 運行

有了鏡像後,咱們就可以以這個鏡像爲基礎啓動並運行一個容器。以上面的 ubuntu:16.04 爲例,若是咱們打算啓動裏面的 bash 而且進行交互式操做的話,能夠執行下面的命令。

$ docker run -it --rm \
    ubuntu:16.04 \
    /bin/bash

root@e7009c6ce357:/# cat /etc/os-release
NAME="Ubuntu"
VERSION="16.04.4 LTS, Trusty Tahr"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 16.04.4 LTS"
VERSION_ID="16.04"
HOME_URL="http://www.ubuntu.com/"
SUPPORT_URL="http://help.ubuntu.com/"
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"

 

docker run就是運行容器的命令,具體格式咱們會在後面的課程中進行詳細講解,咱們這裏簡要的說明一下上面用到的參數。

  • -it:這是兩個參數,一個是 -i:交互式操做,一個是 -t 終端。咱們這裏打算進入 bash 執行一些命令並查看返回結果,所以咱們須要交互式終端。
  • --rm:這個參數是說容器退出後隨之將其刪除。默認狀況下,爲了排障需求,退出的容器並不會當即刪除,除非手動 docker rm。咱們這裏只是隨便執行個命令,看看結果,不須要排障和保留結果,所以使用--rm能夠避免浪費空間。
  • ubuntu:16.04:這是指用 ubuntu:16.04 鏡像爲基礎來啓動容器。
  • bash:放在鏡像名後的是命令,這裏咱們但願有個交互式 Shell,所以用的是 bash。

進入容器後,咱們能夠在 Shell 下操做,執行任何所需的命令。這裏,咱們執行了cat /etc/os-release,這是 Linux 經常使用的查看當前系統版本的命令,從返回的結果能夠看到容器內是 Ubuntu 16.04.4 LTS 系統。最後咱們經過 exit 退出了這個容器。

2.3 列出鏡像

$ docker image ls

 

列表包含了倉庫名、標籤、鏡像 ID、建立時間以及所佔用的空間。鏡像 ID 則是鏡像的惟一標識,一個鏡像能夠對應多個標籤。

2.4 鏡像大小

若是仔細觀察,會注意到,這裏標識的所佔用空間和在 Docker Hub 上看到的鏡像大小不一樣。好比,ubuntu:16.04 鏡像大小,在這裏是 127 MB,可是在Docker Hub顯示的倒是 43 MB。這是由於 Docker Hub 中顯示的體積是壓縮後的體積。在鏡像下載和上傳過程當中鏡像是保持着壓縮狀態的,所以 Docker Hub 所顯示的大小是網絡傳輸中更關心的流量大小。而docker image ls顯示的是鏡像下載到本地後,展開的大小,準確說,是展開後的各層所佔空間的總和,由於鏡像到本地後,查看空間的時候,更關心的是本地磁盤空間佔用的大小。

另一個須要注意的問題是,docker image ls列表中的鏡像體積總和並不是是全部鏡像實際硬盤消耗。因爲 Docker 鏡像是多層存儲結構,而且能夠繼承、複用,所以不一樣鏡像可能會由於使用相同的基礎鏡像,從而擁有共同的層。因爲 Docker 使用Union FS,相同的層只須要保存一份便可,所以實際鏡像硬盤佔用空間極可能要比這個列表鏡像大小的總和要小的多。你能夠經過如下命令來便捷的查看鏡像、容器、數據卷所佔用的空間。

$ docker system df

 

2.5 新建並啓動

所須要的命令主要爲docker run。 例如,下面的命令輸出一個 「Hello World」,以後終止容器。

$ docker run ubuntu:16.04 /bin/echo 'Hello world'
Hello world

 

這跟在本地直接執行/bin/echo 'hello world'幾乎感受不出任何區別。下面的命令則啓動一個 bash 終端,容許用戶進行交互。

$ docker run -t -i ubuntu:16.04 /bin/bash
root@af8bae53bdd3:/#

 

其中,-t選項讓Docker分配一個僞終端(pseudo-tty)並綁定到容器的標準輸入上,-i則讓容器的標準輸入保持打開。 在交互模式下,用戶能夠經過所建立的終端來輸入命令,例如:

root@af8bae53bdd3:/# pwd
/
root@af8bae53bdd3:/# ls
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var

 

當利用docker run來建立容器時,Docker 在後臺運行的標準操做包括:

  • 檢查本地是否存在指定的鏡像,不存在就從公有倉庫下載
  • 利用鏡像建立並啓動一個容器
  • 分配一個文件系統,並在只讀的鏡像層外面掛載一層可讀寫層
  • 從宿主主機配置的網橋接口中橋接一個虛擬接口到容器中去
  • 從地址池配置一個 ip 地址給容器
  • 執行用戶指定的應用程序
  • 執行完畢後容器被終止

2.6 啓動已終止容器

能夠利用docker container start命令,直接將一個已經終止的容器啓動運行。

容器的核心爲所執行的應用程序,所須要的資源都是應用程序運行所必需的。除此以外,並無其它的資源。能夠在僞終端中利用 ps 或 top 來查看進程信息。

root@ba267838cc1b:/# ps
  PID TTY          TIME CMD
    1 ?        00:00:00 bash
   11 ?        00:00:00 ps

 

可見,容器中僅運行了指定的 bash 應用。這種特色使得 Docker 對資源的利用率極高,是貨真價實的輕量級虛擬化。

2.7 後臺運行

更多的時候,須要讓 Docker 在後臺運行而不是直接把執行命令的結果輸出在當前宿主機下。此時,能夠經過添加-d參數來實現。下面舉兩個例子來講明一下。

若是不使用-d參數運行容器。

$ docker run ubuntu:16.04 /bin/sh -c "while true; do echo hello world; sleep 1; done"
hello world
hello world
hello world
hello world

 

容器會把輸出的結果 (STDOUT) 打印到宿主機上面。若是使用了-d參數運行容器。

$ docker run -d ubuntu:16.04 /bin/sh -c "while true; do echo hello world; sleep 1; done"
77b2dc01fe0f3f1265df143181e7b9af5e05279a884f4776ee75350ea9d8017a

 

此時容器會在後臺運行並不會把輸出的結果 (STDOUT) 打印到宿主機上面(輸出結果能夠用 docker logs 查看)。

注: 容器是否會長久運行,是和 docker run 指定的命令有關,和 -d 參數無關。

使用-d參數啓動後會返回一個惟一的 id,也能夠經過docker container ls命令來查看容器信息。

$ docker container ls
CONTAINER ID  IMAGE         COMMAND               CREATED        STATUS       PORTS NAMES
77b2dc01fe0f  ubuntu:16.04  /bin/sh -c 'while tr  2 minutes ago  Up 1 minute        agitated_wright
 要獲取容器的輸出信息,能夠經過 docker container logs 命令。 
$ docker container logs [container ID or NAMES]
hello world
hello world
hello world
. . .

 

2.8 終止容器

可使用docker container stop來終止一個運行中的容器。此外,當 Docker 容器中指定的應用終結時,容器也自動終止。

例如對於上一章節中只啓動了一個終端的容器,用戶經過 exit 命令或 Ctrl+d 來退出終端時,所建立的容器馬上終止。終止狀態的容器能夠用docker container ls -a 命令看到。例如

$ docker container ls -a
CONTAINER ID        IMAGE                    COMMAND                CREATED             STATUS                          PORTS               NAMES
ba267838cc1b        ubuntu:16.04             "/bin/bash"            30 minutes ago      Exited (0) About a minute ago                       trusting_newton

 

處於終止狀態的容器,能夠經過docker container start命令來從新啓動。

此外,docker container restart命令會將一個運行態的容器終止,而後再從新啓動它。

2.9 進入容器

在使用-d參數時,容器啓動後會進入後臺。某些時候須要進入容器進行操做:exec 命令 -i -t 參數。

只用-i參數時,因爲沒有分配僞終端,界面沒有咱們熟悉的Linux命令提示符,但命令執行結果仍然能夠返回。 當-i -t參數一塊兒使用時,則能夠看到咱們熟悉的 Linux命令提示符。

$ docker run -dit ubuntu:16.04
69d137adef7a8a689cbcb059e94da5489d3cddd240ff675c640c8d96e84fe1f6

$ docker container ls
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
69d137adef7a        ubuntu:16.04       "/bin/bash"         18 seconds ago      Up 17 seconds                           zealous_swirles

$ docker exec -i 69d1 bash
ls
bin
boot
dev
...

$ docker exec -it 69d1 bash
root@69d137adef7a:/#

 

若是從這個 stdin 中 exit,不會致使容器的中止。這就是爲何推薦你們使用docker exec的緣由。

更多參數說明請使用docker exec --help查看。

2.10 刪除容器

可使用docker container rm來刪除一個處於終止狀態的容器。例如:

$ docker container rm  trusting_newton
trusting_newton

也可用使用docker rm容器名來刪除,若是要刪除一個運行中的容器,能夠添加-f參數。Docker 會發送 SIGKILL信號給容器。

docker container ls -a (或者docker ps -a)命令能夠查看全部已經建立的包括終止狀態的容器,若是數量太多要一個個刪除可能會很麻煩,用下面的命令能夠清理掉全部處於終止狀態的容器。

$ docker container prune

或者

$ docker ps -aq

 

2.11 刪除本地鏡像

若是要刪除本地的鏡像,可使用`docker image rm·命令,其格式爲:

$ docker image rm [選項] <鏡像1> [<鏡像2> ...]

 

或者

$ docker rmi 鏡像名

 

或者用 ID、鏡像名、摘要刪除鏡像 其中,<鏡像> 能夠是 鏡像短 ID、鏡像長 ID、鏡像名 或者 鏡像摘要。 好比咱們有這麼一些鏡像:

$ docker image ls
REPOSITORY                  TAG                 IMAGE ID            CREATED             SIZE
centos                      latest              0584b3d2cf6d        3 weeks ago         196.5 MB
redis                       alpine              501ad78535f0        3 weeks ago         21.03 MB
docker                      latest              cf693ec9b5c7        3 weeks ago         105.1 MB
nginx                       latest              e43d811ce2f4        5 weeks ago         181.5 MB

 

咱們能夠用鏡像的完整 ID,也稱爲 長 ID,來刪除鏡像。使用腳本的時候可能會用長 ID,可是人工輸入就太累了,因此更多的時候是用 短 ID 來刪除鏡像。docker image ls默認列出的就已是短 ID 了,通常取前3個字符以上,只要足夠區分於別的鏡像就能夠了。

好比這裏,若是咱們要刪除redis:alpine鏡像,能夠執行:

$ docker image rm 501
Untagged: redis:alpine
Untagged: redis@sha256:f1ed3708f538b537eb9c2a7dd50dc90a706f7debd7e1196c9264edeea521a86d
Deleted: sha256:501ad78535f015d88872e13fa87a828425117e3d28075d0c117932b05bf189b7
Deleted: sha256:96167737e29ca8e9d74982ef2a0dda76ed7b430da55e321c071f0dbff8c2899b
Deleted: sha256:32770d1dcf835f192cafd6b9263b7b597a1778a403a109e2cc2ee866f74adf23
Deleted: sha256:127227698ad74a5846ff5153475e03439d96d4b1c7f2a449c7a826ef74a2d2fa
Deleted: sha256:1333ecc582459bac54e1437335c0816bc17634e131ea0cc48daa27d32c75eab3
Deleted: sha256:4fc455b921edf9c4aea207c51ab39b10b06540c8b4825ba57b3feed1668fa7c7

 

咱們也能夠用鏡像名,也就是 <倉庫名>:<標籤>,來刪除鏡像。

$ docker image rm centos
Untagged: centos:latest
Untagged: centos@sha256:b2f9d1c0ff5f87a4743104d099a3d561002ac500db1b9bfa02a783a46e0d366c
Deleted: sha256:0584b3d2cf6d235ee310cf14b54667d889887b838d3f3d3033acd70fc3c48b8a
Deleted: sha256:97ca462ad9eeae25941546209454496e1d66749d53dfa2ee32bf1faabd239d38

 

2.12 docker commit定製鏡像

鏡像是容器的基礎,每次執行docker run的時候都會指定哪一個鏡像做爲容器運行的基礎。在以前的例子中,咱們所使用的都是來自於 Docker Hub 的鏡像。直接使用這些鏡像是能夠知足必定的需求,而當這些鏡像沒法直接知足需求時,咱們就須要定製這些鏡像。接下來的幾節就將講解如何定製鏡像。

回顧一下以前咱們學到的知識,鏡像是多層存儲,每一層是在前一層的基礎上進行的修改;而容器一樣也是多層存儲,是在以鏡像爲基礎層,在其基礎上加一層做爲容器運行時的存儲層。

如今讓咱們以定製一個 Web 服務器爲例子,來說解鏡像是如何構建的。

$ docker run --name webserver -d -p 80:80 nginx

 

這條命令會用 nginx 鏡像啓動一個容器,命名爲 webserver,而且映射了 80 端口,這樣咱們能夠用瀏覽器去訪問這個 nginx 服務器。

若是是在 Linux 本機運行的 Docker,或者若是使用的是 Docker for Mac、Docker for Windows,那麼能夠直接訪問:http://localhost;若是使用的是 Docker Toolbox,或者是在虛擬機、雲服務器上安裝的 Docker,則須要將 localhost 換爲虛擬機地址或者實際雲服務器地址。

直接用瀏覽器訪問的話,咱們會看到默認的 Nginx 歡迎頁面。

如今,假設咱們很是不喜歡這個歡迎頁面,咱們但願改爲歡迎 Docker 的文字,咱們可使用 docker exec命令進入容器,修改其內容。

$ docker exec -it webserver bash
root@3729b97e8226:/# echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
root@3729b97e8226:/# exit

 

咱們以交互式終端方式進入 webserver 容器,並執行了 bash 命令,也就是得到一個可操做的 Shell。 而後,咱們用<h1>Hello, Docker!</h1>覆蓋了 /usr/share/nginx/html/index.html的內容。 如今咱們再刷新瀏覽器的話,會發現內容被改變了。

咱們修改了容器的文件,也就是改動了容器的存儲層。咱們能夠經過docker diff命令看到具體的改動。

$ docker diff webserver
C /root
A /root/.bash_history
C /run
C /usr
C /usr/share
C /usr/share/nginx
C /usr/share/nginx/html
C /usr/share/nginx/html/index.html
C /var
C /var/cache
C /var/cache/nginx
A /var/cache/nginx/client_temp
A /var/cache/nginx/fastcgi_temp
A /var/cache/nginx/proxy_temp
A /var/cache/nginx/scgi_temp
A /var/cache/nginx/uwsgi_temp

 

如今咱們定製好了變化,咱們但願能將其保存下來造成鏡像。

要知道,當咱們運行一個容器的時候(若是不使用卷的話),咱們作的任何文件修改都會被記錄於容器存儲層裏。而 Docker 提供了一個docker commit命令,能夠將容器的存儲層保存下來成爲鏡像。換句話說,就是在原有鏡像的基礎上,再疊加上容器的存儲層,並構成新的鏡像。之後咱們運行這個新鏡像的時候,就會擁有原有容器最後的文件變化。

咱們能夠用下面的命令將容器保存爲鏡像:

$ docker commit \
    --author "海馬學院" \
    --message "修改了默認首頁" \
    webserver \
    nginx:v2
sha256:07e33465974800ce65751acc279adc6ed2dc5ed4e0838f8b86f0c87aa1795214

 

其中--author是指定修改的做者,而--message則是記錄本次修改的內容。這點和 git 版本控制類似,不過這裏這些信息能夠省略留空。

咱們能夠在docker image ls中看到這個新定製的鏡像:

$ docker image ls nginx
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
nginx               v2                  07e334659748        9 seconds ago       181.5 MB
nginx               1.11                05a60462f8ba        12 days ago         181.5 MB
nginx               latest              e43d811ce2f4        4 weeks ago         181.5 MB

 

咱們還能夠用docker history具體查看鏡像內的歷史記錄,若是比較 nginx:latest 的歷史記錄,咱們會發現新增了咱們剛剛提交的這一層。

$ docker history nginx:v2
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
07e334659748        54 seconds ago      nginx -g daemon off;                            95 B                修改了默認網頁
e43d811ce2f4        4 weeks ago         /bin/sh -c #(nop)  CMD ["nginx" "-g" "daemon    0 B
<missing>           4 weeks ago         /bin/sh -c #(nop)  EXPOSE 443/tcp 80/tcp        0 B
<missing>           4 weeks ago         /bin/sh -c ln -sf /dev/stdout /var/log/nginx/   22 B
<missing>           4 weeks ago         /bin/sh -c apt-key adv --keyserver hkp://pgp.   58.46 MB
<missing>           4 weeks ago         /bin/sh -c #(nop)  ENV NGINX_VERSION=1.11.5-1   0 B
<missing>           4 weeks ago         /bin/sh -c #(nop)  MAINTAINER NGINX Docker Ma   0 B
<missing>           4 weeks ago         /bin/sh -c #(nop)  CMD ["/bin/bash"]            0 B
<missing>           4 weeks ago         /bin/sh -c #(nop) ADD file:23aa4f893e3288698c   123 MB

 

新的鏡像定製好後,咱們能夠來運行這個鏡像。

$ docker run --name webserv2 -d -p 81:80 nginx:v2

 

這裏咱們命名爲新的服務爲 webserv2,而且映射到 81 端口。若是是 Docker for Mac/Windows 或 Linux 桌面的話,咱們就能夠直接訪問 http://localhost:81 看到結果,其內容應該和以前修改後的 webserver 同樣。

至此,咱們第一次完成了定製鏡像,使用的是docker commit命令,手動操做給舊的鏡像添加了新的一層,造成新的鏡像,對鏡像多層存儲應該有了更直觀的感受。

注意: docker commit 命令除了學習以外,還有一些特殊的應用場合,好比被入侵後保存現場等。可是,不要使用 docker commit 定製鏡像,定製鏡像應該使用Dockerfile來完成。若是你想要定製鏡像請查看下一小節。

 

3.Dockerfile 定製鏡像

從前面一節的docker commit的學習中,咱們能夠了解到,鏡像的定製實際上就是定製每一層所添加的配置、文件等信息,可是命令畢竟只是命令,每次定製都得去重複執行這個命令,並且還不夠直觀,若是咱們能夠把每一層修改、安裝、構建、操做的命令都寫入一個腳本,用這個腳原本構建、定製鏡像,那麼這些問題不就均可以解決了嗎?對的,這個腳本就是咱們說的Dockerfile

3.1 介紹

Dockerfile 是一個文本文件,其內包含了一條條的指令(Instruction),每一條指令構建一層,所以每一條指令的內容,就是描述該層應當如何構建。

還以以前定製 nginx 鏡像爲例,此次咱們使用 Dockerfile 來定製。在一個空白目錄中,創建一個文本文件,並命名爲 Dockerfile:

$ mkdir mynginx
$ cd mynginx
$ touch Dockerfile

 

其內容爲:

FROM nginx
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html

 

這個 Dockerfile 很簡單,一共就兩行。涉及到了兩條指令,FROM 和 RUN。

3.2 FROM 指定基礎鏡像

所謂定製鏡像,那必定是以一個鏡像爲基礎,在其上進行定製。就像咱們以前運行了一個 nginx 鏡像的容器,再進行修改同樣,基礎鏡像是必須指定的。而FROM就是指定基礎鏡像,所以一個 Dockerfile 中 FROM 是必備的指令,而且必須是第一條指令。

Docker Store上有很是多的高質量的官方鏡像,有能夠直接拿來使用的服務類的鏡像,如 nginx、redis、mongo、mysql、httpd、php、tomcat 等;也有一些方便開發、構建、運行各類語言應用的鏡像,如 node、openjdk、python、ruby、golang 等。能夠在其中尋找一個最符合咱們最終目標的鏡像爲基礎鏡像進行定製。

若是沒有找到對應服務的鏡像,官方鏡像中還提供了一些更爲基礎的操做系統鏡像,如 ubuntu、debian、centos、fedora、alpine 等,這些操做系統的軟件庫爲咱們提供了更廣闊的擴展空間。

除了選擇現有鏡像爲基礎鏡像外,Docker 還存在一個特殊的鏡像,名爲scratch。這個鏡像是虛擬的概念,並不實際存在,它表示一個空白的鏡像。

FROM scratch ... 

若是你以scratch爲基礎鏡像的話,意味着你不以任何鏡像爲基礎,接下來所寫的指令將做爲鏡像第一層開始存在。有的同窗可能感受很奇怪,沒有任何基礎鏡像,我怎麼去執行個人程序呢,其實對於 Linux 下靜態編譯的程序來講,並不須要有操做系統提供運行時支持,所需的一切庫都已經在可執行文件裏了,所以直接FROM scratch會讓鏡像體積更加小巧。使用 Go 語言 開發的應用不少會使用這種方式來製做鏡像,這也是爲何有人認爲 Go 是特別適合容器微服務架構的語言的緣由之一。

3.3 RUN 執行命令

RUN指令是用來執行命令行命令的。因爲命令行的強大能力,RUN指令在定製鏡像時是最經常使用的指令之一。其格式有兩種:

  • shell 格式:RUN <命令>,就像直接在命令行中輸入的命令同樣。剛纔寫的 Dockerfile 中的 RUN 指令就是這種格式。
    RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html 
  • exec 格式:RUN ["可執行文件", "參數1", "參數2"],這更像是函數調用中的格式。 既然 RUN 就像 Shell 腳本同樣能夠執行命令,那麼咱們是否就能夠像 Shell 腳本同樣把每一個命令對應一個 RUN 呢?好比這樣:
    FROM debian:jessie RUN apt-get update RUN apt-get install -y gcc libc6-dev make RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz" RUN mkdir -p /usr/src/redis RUN tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 RUN make -C /usr/src/redis RUN make -C /usr/src/redis install 

以前說過,Dockerfile 中每個指令都會創建一層,RUN 也不例外。每個 RUN 的行爲,就和剛纔咱們手工創建鏡像的過程同樣:新創建一層,在其上執行這些命令,執行結束後,commit 這一層的修改,構成新的鏡像。

而上面的這種寫法,建立了 7 層鏡像。這是徹底沒有意義的,並且不少運行時不須要的東西,都被裝進了鏡像裏,好比編譯環境、更新的軟件包等等。結果就是產生很是臃腫、很是多層的鏡像,不只僅增長了構建部署的時間,也很容易出錯。 這是不少初學 Docker 的人常犯的一個錯誤。

Union FS 是有最大層數限制的,好比 AUFS,曾經是最大不得超過 42 層,如今是不得超過 127 層。

上面的 Dockerfile 正確的寫法應該是這樣:

FROM debian:jessie
RUN buildDeps='gcc libc6-dev make' \
    && apt-get update \
    && apt-get install -y $buildDeps \
    && wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz" \
    && mkdir -p /usr/src/redis \
    && tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 \
    && make -C /usr/src/redis \
    && make -C /usr/src/redis install \
    && rm -rf /var/lib/apt/lists/* \
    && rm redis.tar.gz \
    && rm -r /usr/src/redis \
    && apt-get purge -y --auto-remove $buildDeps

 

首先,以前全部的命令只有一個目的,就是編譯、安裝 redis 可執行文件。所以沒有必要創建不少層,這只是一層的事情。所以,這裏沒有使用不少個 RUN 對一一對應不一樣的命令,而是僅僅使用一個 RUN 指令,並使用&&將各個所需命令串聯起來。將以前的 7 層,簡化爲了 1 層。在撰寫 Dockerfile 的時候,要常常提醒本身,這並非在寫 Shell 腳本,而是在定義每一層該如何構建。

而且,這裏爲了格式化還進行了換行。Dockerfile 支持 Shell 類的行尾添加\的命令換行方式,以及行首#進行註釋的格式。良好的格式,好比換行、縮進、註釋等,會讓維護、排障更爲容易,這是一個比較好的習慣。

此外,還能夠看到這一組命令的最後添加了清理工做的命令,刪除了爲了編譯構建所須要的軟件,清理了全部下載、展開的文件,而且還清理了 apt 緩存文件。這是很重要的一步,咱們以前說過,鏡像是多層存儲,每一層的東西並不會在下一層被刪除,會一直跟隨着鏡像。所以鏡像構建時,必定要確保每一層只添加真正須要添加的東西,任何無關的東西都應該清理掉。 不少人初學 Docker 製做出了很臃腫的鏡像的緣由之一,就是忘記了每一層構建的最後必定要清理掉無關文件。

3.4 構建鏡像

好了,讓咱們再回到以前定製的 nginx 鏡像的 Dockerfile 來。如今咱們明白了這個 Dockerfile的內容,那麼讓咱們來構建這個鏡像吧。在 Dockerfile 文件所在目錄執行:

$ docker build -t nginx:v3 .
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM nginx
 ---> e43d811ce2f4
Step 2 : RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
 ---> Running in 9cdc27646c7b
 ---> 44aa4490ce2c
Removing intermediate container 9cdc27646c7b
Successfully built 44aa4490ce2c

 

從命令的輸出結果中,咱們能夠清晰的看到鏡像的構建過程。在 Step 2 中,如同咱們以前所說的那樣,RUN 指令啓動了一個容器 9cdc27646c7b,執行了所要求的命令,並最後提交了這一層 44aa4490ce2c,隨後刪除了所用到的這個容器 9cdc27646c7b。這裏咱們使用了 docker build命令進行鏡像構建。其格式爲:

$ docker build [選項] <上下文路徑/URL/->

 

在這裏咱們指定了最終鏡像的名稱 -t nginx:v3,構建成功後,咱們能夠像以前運行 nginx:v2 那樣來運行這個鏡像,其結果會和 nginx:v2 同樣。

3.5 鏡像構建上下文(Context)

若是注意,會看到 docker build 命令最後有一個..表示當前目錄,而 Dockerfile 就在當前目錄,所以很多初學者覺得這個路徑是在指定 Dockerfile 所在路徑,這麼理解實際上是不許確的。若是對應上面的命令格式,你可能會發現,這是在指定上下文路徑。那麼什麼是上下文呢?

首先咱們要理解 docker build 的工做原理。Docker 在運行時分爲 Docker 引擎(也就是服務端守護進程)和客戶端工具。Docker 的引擎提供了一組 REST API,被稱爲 Docker Remote API,而如 docker 命令這樣的客戶端工具,則是經過這組 API 與 Docker 引擎交互,從而完成各類功能。所以,雖然表面上咱們好像是在本機執行各類 docker 功能,但實際上,一切都是使用的遠程調用形式在服務端(Docker 引擎)完成。也由於這種 C/S 設計,讓咱們操做遠程服務器的 Docker 引擎變得垂手可得。

當咱們進行鏡像構建的時候,並不是全部定製都會經過 RUN 指令完成,常常會須要將一些本地文件複製進鏡像,好比經過 COPY 指令、ADD 指令等。而 docker build 命令構建鏡像,其實並不是在本地構建,而是在服務端,也就是 Docker 引擎中構建的。那麼在這種客戶端/服務端的架構中,如何才能讓服務端得到本地文件呢?

這就引入了上下文的概念。當構建的時候,用戶會指定構建鏡像上下文的路徑,docker build 命令得知這個路徑後,會將路徑下的全部內容打包,而後上傳給 Docker 引擎。這樣 Docker 引擎收到這個上下文包後,展開就會得到構建鏡像所需的一切文件。若是在 Dockerfile 中這麼寫:

COPY ./package.json /app/

 

這並非要複製執行 docker build 命令所在的目錄下的 package.json,也不是複製 Dockerfile 所在目錄下的 package.json,而是複製 上下文(context) 目錄下的 package.json。

所以,COPY這類指令中的源文件的路徑都是相對路徑。這也是初學者常常會問的爲何 COPY ../package.json /app 或者 COPY /opt/xxxx /app 沒法工做的緣由,由於這些路徑已經超出了上下文的範圍,Docker 引擎沒法得到這些位置的文件。若是真的須要那些文件,應該將它們複製到上下文目錄中去。

如今就能夠理解剛纔的命令docker build -t nginx:v3 .中的這個.,其實是在指定上下文的目錄,docker build 命令會將該目錄下的內容打包交給 Docker 引擎以幫助構建鏡像。

若是觀察 docker build 輸出,咱們其實已經看到了這個發送上下文的過程:

$ docker build -t nginx:v3 .
Sending build context to Docker daemon 2.048 kB
...

 

理解構建上下文對於鏡像構建是很重要的,能夠避免犯一些不該該的錯誤。好比有些初學者在發現 COPY /opt/xxxx /app 不工做後,因而乾脆將 Dockerfile 放到了硬盤根目錄去構建,結果發現 docker build 執行後,在發送一個幾十 GB 的東西,極爲緩慢並且很容易構建失敗。那是由於這種作法是在讓 docker build 打包整個硬盤,這顯然是使用錯誤。

通常來講,應該會將 Dockerfile 置於一個空目錄下,或者項目根目錄下。若是該目錄下沒有所需文件,那麼應該把所需文件複製一份過來。若是目錄下有些東西確實不但願構建時傳給 Docker 引擎,那麼能夠用 .gitignore 同樣的語法寫一個.dockerignore,該文件是用於剔除不須要做爲上下文傳遞給 Docker 引擎的。

那麼爲何會有人誤覺得 . 是指定 Dockerfile 所在目錄呢?這是由於在默認狀況下,若是不額外指定 Dockerfile 的話,會將上下文目錄下的名爲 Dockerfile 的文件做爲 Dockerfile。

這只是默認行爲,實際上 Dockerfile 的文件名並不要求必須爲 Dockerfile,並且並不要求必須位於上下文目錄中,好比能夠用-f ../Dockerfile.php參數指定某個文件做爲 Dockerfile。

固然,通常你們習慣性的會使用默認的文件名 Dockerfile,以及會將其置於鏡像構建上下文目錄中。

3.6 遷移鏡像

Docker 還提供了docker loaddocker save命令,用以將鏡像保存爲一個 tar 文件,而後傳輸到另外一個位置上,再加載進來。這是在沒有 Docker Registry 時的作法,如今已經不推薦,鏡像遷移應該直接使用 Docker Registry,不管是直接使用 Docker Hub 仍是使用內網私有 Registry 均可以。

使用docker save命令能夠將鏡像保存爲歸檔文件。好比咱們但願保存這個 alpine 鏡像。

$ docker image ls alpine
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
alpine              latest              baa5d63471ea        5 weeks ago         4.803 MB

 

保存鏡像的命令爲:

$ docker save alpine | gzip > alpine-latest.tar.gz

 

而後咱們將 alpine-latest.tar.gz 文件複製到了到了另外一個機器上,能夠用下面這個命令加載鏡像:

$ docker load -i alpine-latest.tar.gz
Loaded image: alpine:latest

 

若是咱們結合這兩個命令以及 ssh 甚至 pv 的話,利用 Linux 強大的管道,咱們能夠寫一個命令完成從一個機器將鏡像遷移到另外一個機器,而且帶進度條的功能:

docker save <鏡像名> | bzip2 | pv | ssh <用戶名>@<主機名> 'cat | docker load'

 




4. 私有鏡像倉庫

這節課給你們講講私有鏡像倉庫的使用。

4.1 Docker Hub

目前 Docker 官方維護了一個公共倉庫Docker Hub,大部分需求均可以經過在 Docker Hub 中直接下載鏡像來實現。若是你以爲拉取 Docker Hub 的鏡像比較慢的話,咱們能夠配置一個鏡像加速器:http://docker-cn.com/,固然國內大部分雲廠商都提供了相應的加速器,簡單配置便可。

4.1.1 註冊

你能夠在 https://cloud.docker.com 免費註冊一個 Docker 帳號。

4.1.2 登陸

經過執行docker login命令交互式的輸入用戶名及密碼來完成在命令行界面登陸 Docker Hub。

4.1.3 註銷

你能夠經過docker logout退出登陸。 拉取鏡像

4.1.4 拉取鏡像

你能夠經過docker search命令來查找官方倉庫中的鏡像,並利用docker pull命令來將它下載到本地。

例如以 centos 爲關鍵詞進行搜索:

$ docker search centos
NAME                                            DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
centos                                          The official build of CentOS.                   465       [OK]
tianon/centos                                   CentOS 5 and 6, created using rinse instea...   28
blalor/centos                                   Bare-bones base CentOS 6.5 image                6                    [OK]
saltstack/centos-6-minimal                                                                      6                    [OK]
tutum/centos-6.4                                DEPRECATED. Use tutum/centos:6.4 instead. ...   5                    [OK]

 

能夠看到返回了不少包含關鍵字的鏡像,其中包括鏡像名字、描述、收藏數(表示該鏡像的受關注程度)、是否官方建立、是否自動建立。

官方的鏡像說明是官方項目組建立和維護的,automated資源容許用戶驗證鏡像的來源和內容。

根據是不是官方提供,可將鏡像資源分爲兩類。

  • 一種是相似 centos 這樣的鏡像,被稱爲基礎鏡像或根鏡像。這些基礎鏡像由 Docker 公司建立、驗證、支持、提供。這樣的鏡像每每使用單個單詞做爲名字。
  • 還有一種類型,好比 tianon/centos 鏡像,它是由 Docker 的用戶建立並維護的,每每帶有用戶名稱前綴。能夠經過前綴username/來指定使用某個用戶提供的鏡像,好比 tianon 用戶。

另外,在查找的時候經過--filter=stars=N參數能夠指定僅顯示收藏數量爲 N 以上的鏡像。下載官方 centos 鏡像到本地。

$ docker pull centos
Pulling repository centos
0b443ba03958: Download complete
539c0211cd76: Download complete
511136ea3c5a: Download complete
7064731afe90: Download complete

 

4.1.5 推送鏡像

用戶也能夠在登陸後經過docker push命令來將本身的鏡像推送到 Docker Hub。如下命令中的 username 請替換爲你的 Docker 帳號用戶名。

$ docker tag ubuntu:17.10 username/ubuntu:17.10
$ docker image ls

REPOSITORY                                               TAG                    IMAGE ID            CREATED             SIZE
ubuntu                                                   17.10                  275d79972a86        6 days ago          94.6MB
username/ubuntu                                          17.10                  275d79972a86        6 days ago          94.6MB
$ docker push username/ubuntu:17.10
$ docker search username

NAME                      DESCRIPTION                                     STARS               OFFICIAL            AUTOMATED
username/ubuntu

 

4.2 私有倉庫

有時候使用 Docker Hub 這樣的公共倉庫可能不方便,用戶能夠建立一個本地倉庫供私人使用。

docker-registry是官方提供的工具,能夠用於構建私有的鏡像倉庫。本文內容基於 docker-registry v2.x 版本。你能夠經過獲取官方 registry 鏡像來運行。

$ docker run -d -p 5000:5000 --restart=always --name registry registry

 

這將使用官方的registry鏡像來啓動私有倉庫。默認狀況下,倉庫會被建立在容器的/var/lib/registry目錄下。你能夠經過 -v 參數來將鏡像文件存放在本地的指定路徑。例以下面的例子將上傳的鏡像放到本地的 /opt/data/registry 目錄。

$ docker run -d \
    -p 5000:5000 \
    -v /opt/data/registry:/var/lib/registry \
    registry

 

4.2.1 在私有倉庫上傳、搜索、下載鏡像

建立好私有倉庫以後,就可使用docker tag來標記一個鏡像,而後推送它到倉庫。例如私有倉庫地址爲 127.0.0.1:5000。先在本機查看已有的鏡像。

$ docker image ls
REPOSITORY                        TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
ubuntu                            latest              ba5877dc9bec        6 weeks ago         192.7 MB

 

使用docker tag將 ubuntu:latest 這個鏡像標記爲 127.0.0.1:5000/ubuntu:latest。 格式爲 docker tag IMAGE[:TAG] [REGISTRY_HOST[:REGISTRY_PORT]/]REPOSITORY[:TAG]

$ docker tag ubuntu:latest 127.0.0.1:5000/ubuntu:latest
$ docker image ls
REPOSITORY                        TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
ubuntu                            latest              ba5877dc9bec        6 weeks ago         192.7 MB
127.0.0.1:5000/ubuntu:latest      latest              ba5877dc9bec        6 weeks ago         192.7 MB

 

使用docker push上傳標記的鏡像。

$ docker push 127.0.0.1:5000/ubuntu:latest
The push refers to repository [127.0.0.1:5000/ubuntu]
373a30c24545: Pushed
a9148f5200b0: Pushed
cdd3de0940ab: Pushedfc56279bbb33: Pushed
b38367233d37: Pushed
2aebd096e0e2: Pushed
latest: digest: sha256:fe4277621f10b5026266932ddf760f5a756d2facd505a94d2da12f4f52f71f5a size: 1568

 

curl查看倉庫中的鏡像。

$ curl 127.0.0.1:5000/v2/_catalog
{"repositories":["ubuntu"]}

 

這裏能夠看到 {"repositories":["ubuntu"]},代表鏡像已經被成功上傳了。

先刪除已有鏡像,再嘗試從私有倉庫中下載這個鏡像。

$ docker image rm 127.0.0.1:5000/ubuntu:latest

$ docker pull 127.0.0.1:5000/ubuntu:latest
Pulling repository 127.0.0.1:5000/ubuntu:latest
ba5877dc9bec: Download complete
511136ea3c5a: Download complete
9bad880da3d2: Download complete
25f11f5fb0cb: Download complete
ebc34468f71d: Download complete
2318d26665ef: Download complete

$ docker image ls
REPOSITORY                         TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
127.0.0.1:5000/ubuntu:latest       latest              ba5877dc9bec        6 weeks ago         192.7 MB

 

4.3 注意事項

若是你不想使用 127.0.0.1:5000 做爲倉庫地址,好比想讓本網段的其餘主機也能把鏡像推送到私有倉庫。你就得把例如 192.168.199.100:5000 這樣的內網地址做爲私有倉庫地址,這時你會發現沒法成功推送鏡像。

這是由於 Docker 默認不容許非 HTTPS 方式推送鏡像。咱們能夠經過 Docker 的配置選項來取消這個限制。

4.3.1 Ubuntu 14.04, Debian 7 Wheezy

對於使用 upstart 的系統而言,編輯/etc/default/docker文件,在其中的DOCKER_OPTS中增長以下內容:

DOCKER_OPTS="--registry-mirror=https://registry.docker-cn.com --insecure-registries=192.168.199.100:5000"

 

從新啓動服務:

$ sudo service docker restart

 

4.3.2 Ubuntu 16.04+, Debian 8+, centos 7

對於使用 systemd 的系統,請在/etc/docker/daemon.json中寫入以下內容(若是文件不存在請新建該文件)

{
  "registry-mirror": [
    "https://registry.docker-cn.com"
  ],
  "insecure-registries": [
    "192.168.199.100:5000"
  ]
}

 

注意:該文件必須符合json規範,不然 Docker 將不能啓動。

4.3.3 其餘

對於 Docker for Windows、Docker for Mac 在設置中編輯daemon.json增長和上邊同樣的字符串便可。

 

 

5. 數據共享與持久化

這一節介紹如何在 Docker 內部以及容器之間管理數據,在容器中管理數據主要有兩種方式:

  • 數據卷(Data Volumes)
  • 掛載主機目錄 (Bind mounts)

5.1 數據卷

數據卷是一個可供一個或多個容器使用的特殊目錄,它繞過UFS,能夠提供不少有用的特性:

  • 數據卷 能夠在容器之間共享和重用
  • 對 數據卷 的修改會立馬生效
  • 對 數據卷 的更新,不會影響鏡像
  • 數據卷 默認會一直存在,即便容器被刪除

注意:數據卷 的使用,相似於 Linux 下對目錄或文件進行 mount,鏡像中的被指定爲掛載點的目錄中的文件會隱藏掉,能顯示看的是掛載的 數據卷。

選擇 -v 仍是 -–mount 參數: Docker 新用戶應該選擇--mount參數,經驗豐富的 Docker 使用者對-v或者 --volume已經很熟悉了,可是推薦使用--mount參數。

建立一個數據卷:

$ docker volume create my-vol

 

查看全部的 數據卷:

$ docker volume ls
local               my-vol

 

在主機裏使用如下命令能夠查看指定 數據卷 的信息

$ docker volume inspect my-vol
[
    {
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/my-vol/_data",
        "Name": "my-vol",
        "Options": {},
        "Scope": "local"
    }
]

 

啓動一個掛載數據卷的容器:在用docker run命令的時候,使用--mount標記來將 數據卷 掛載到容器裏。在一次docker run中能夠掛載多個 數據卷。下面建立一個名爲 web 的容器,並加載一個 數據卷 到容器的 /webapp 目錄。

$ docker run -d -P \
    --name web \
    # -v my-vol:/wepapp \
    --mount source=my-vol,target=/webapp \
    training/webapp \
    python app.py

 

查看數據卷的具體信息:在主機裏使用如下命令能夠查看 web 容器的信息

$ docker inspect web
...
"Mounts": [
    {
        "Type": "volume",
        "Name": "my-vol",
        "Source": "/var/lib/docker/volumes/my-vol/_data",
        "Destination": "/app",
        "Driver": "local",
        "Mode": "",
        "RW": true,
        "Propagation": ""
    }
],
...

 

刪除數據卷:

$ docker volume rm my-vol

 

數據卷 是被設計用來持久化數據的,它的生命週期獨立於容器,Docker 不會在容器被刪除後自動刪除 數據卷,而且也不存在垃圾回收這樣的機制來處理沒有任何容器引用的 數據卷。若是須要在刪除容器的同時移除數據卷。能夠在刪除容器的時候使用docker rm -v這個命令。 無主的數據卷可能會佔據不少空間,要清理請使用如下命令

$ docker volume prune

 

5.2 掛載主機目錄

選擇 -v 仍是 -–mount 參數: Docker 新用戶應該選擇 --mount 參數,經驗豐富的 Docker 使用者對 -v 或者 --volume 已經很熟悉了,可是推薦使用 --mount 參數。

掛載一個主機目錄做爲數據卷:使用 --mount 標記能夠指定掛載一個本地主機的目錄到容器中去。

$ docker run -d -P \
    --name web \
    # -v /src/webapp:/opt/webapp \
    --mount type=bind,source=/src/webapp,target=/opt/webapp \
    training/webapp \
    python app.py

 

上面的命令加載主機的 /src/webapp 目錄到容器的 /opt/webapp目錄。這個功能在進行測試的時候十分方便,好比用戶能夠放置一些程序到本地目錄中,來查看容器是否正常工做。本地目錄的路徑必須是絕對路徑,之前使用 -v 參數時若是本地目錄不存在 Docker 會自動爲你建立一個文件夾,如今使用 --mount 參數時若是本地目錄不存在,Docker 會報錯。

Docker 掛載主機目錄的默認權限是 讀寫,用戶也能夠經過增長readonly指定爲 只讀。

$ docker run -d -P \
    --name web \
    # -v /src/webapp:/opt/webapp:ro \
    --mount type=bind,source=/src/webapp,target=/opt/webapp,readonly \
    training/webapp \
    python app.py

 

加了readonly以後,就掛載爲 只讀 了。若是你在容器內 /opt/webapp 目錄新建文件,會顯示以下錯誤:

/opt/webapp # touch new.txt
touch: new.txt: Read-only file system

 

查看數據卷的具體信息:在主機裏使用如下命令能夠查看 web 容器的信息

$ docker inspect web
...
"Mounts": [
    {
        "Type": "bind",
        "Source": "/src/webapp",
        "Destination": "/opt/webapp",
        "Mode": "",
        "RW": true,
        "Propagation": "rprivate"
    }
],

 

掛載一個本地主機文件做爲數據卷:--mount標記也能夠從主機掛載單個文件到容器中

$ docker run --rm -it \
   # -v $HOME/.bash_history:/root/.bash_history \
   --mount type=bind,source=$HOME/.bash_history,target=/root/.bash_history \
   ubuntu:17.10 \
   bash

root@2affd44b4667:/# history
1  ls
2  diskutil list

 

這樣就能夠記錄在容器輸入過的命令了。

6. Docker 的網絡模式

6.1 Bridge模式

Docker進程啓動時,會在主機上建立一個名爲docker0的虛擬網橋,此主機上啓動的Docker容器會鏈接到這個虛擬網橋上。虛擬網橋的工做方式和物理交換機相似,這樣主機上的全部容器就經過交換機連在了一個二層網絡中。從docker0子網中分配一個 IP 給容器使用,並設置 docker0 的 IP 地址爲容器的默認網關。在主機上建立一對虛擬網卡veth pair設備,Docker 將 veth pair 設備的一端放在新建立的容器中,並命名爲eth0(容器的網卡),另外一端放在主機中,以vethxxx這樣相似的名字命名,並將這個網絡設備加入到 docker0 網橋中。能夠經過brctl show命令查看。

bridge模式是 docker 的默認網絡模式,不寫–net參數,就是bridge模式。使用docker run -p時,docker 實際是在iptables作了DNAT規則,實現端口轉發功能。可使用iptables -t nat -vnL查看。bridge模式以下圖所示:​​

演示:

$ docker run -tid --net=bridge --name docker_bri1 \
             ubuntu-base:v3
             docker run -tid --net=bridge --name docker_bri2 \
             ubuntu-base:v3 

$ brctl show
$ docker exec -ti docker_bri1 /bin/bash
$ ifconfig –a
$ route –n

 

若是你以前有 Docker 使用經驗,你可能已經習慣了使用--link參數來使容器互聯。

隨着 Docker 網絡的完善,強烈建議你們將容器加入自定義的 Docker 網絡來鏈接多個容器,而不是使用 --link 參數。

下面先建立一個新的 Docker 網絡。

$ docker network create -d bridge my-net

 

-d參數指定 Docker 網絡類型,有 bridge overlay。其中 overlay 網絡類型用於 Swarm mode,在本小節中你能夠忽略它。

運行一個容器並鏈接到新建的 my-net 網絡

$ docker run -it --rm --name busybox1 --network my-net busybox sh

 

打開新的終端,再運行一個容器並加入到 my-net 網絡

$ docker run -it --rm --name busybox2 --network my-net busybox sh

 

再打開一個新的終端查看容器信息

$ docker container ls

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
b47060aca56b        busybox             "sh"                11 minutes ago      Up 11 minutes                           busybox2
8720575823ec        busybox             "sh"                16 minutes ago      Up 16 minutes                           busybox1

 

下面經過 ping 來證實 busybox1 容器和 busybox2 容器創建了互聯關係。 在 busybox1 容器輸入如下命令

/ # ping busybox2
PING busybox2 (172.19.0.3): 56 data bytes
64 bytes from 172.19.0.3: seq=0 ttl=64 time=0.072 ms
64 bytes from 172.19.0.3: seq=1 ttl=64 time=0.118 ms

 

用 ping 來測試鏈接 busybox2 容器,它會解析成 172.19.0.3。 同理在 busybox2 容器執行 ping busybox1,也會成功鏈接到。

/ # ping busybox1
PING busybox1 (172.19.0.2): 56 data bytes
64 bytes from 172.19.0.2: seq=0 ttl=64 time=0.064 ms
64 bytes from 172.19.0.2: seq=1 ttl=64 time=0.143 ms

 

這樣,busybox1 容器和 busybox2 容器創建了互聯關係。

若是你有多個容器之間須要互相鏈接,推薦使用Docker Compose

6.2 Host 模式

若是啓動容器的時候使用host模式,那麼這個容器將不會得到一個獨立的Network Namespace,而是和宿主機共用一個 Network Namespace。容器將不會虛擬出本身的網卡,配置本身的 IP 等,而是使用宿主機的 IP 和端口。可是,容器的其餘方面,如文件系統、進程列表等仍是和宿主機隔離的。 Host模式以下圖所示:

​​

演示:

$ docker run -tid --net=host --name docker_host1 ubuntu-base:v3
$ docker run -tid --net=host --name docker_host2 ubuntu-base:v3

$ docker exec -ti docker_host1 /bin/bash
$ docker exec -ti docker_host1 /bin/bash

$ ifconfig –a
$ route –n

 

6.3 Container 模式

這個模式指定新建立的容器和已經存在的一個容器共享一個 Network Namespace,而不是和宿主機共享。新建立的容器不會建立本身的網卡,配置本身的 IP,而是和一個指定的容器共享 IP、端口範圍等。一樣,兩個容器除了網絡方面,其餘的如文件系統、進程列表等仍是隔離的。兩個容器的進程能夠經過 lo 網卡設備通訊。 Container模式示意圖:​​

演示:

$ docker run -tid --net=container:docker_bri1 \
              --name docker_con1 ubuntu-base:v3

$ docker exec -ti docker_con1 /bin/bash
$ docker exec -ti docker_bri1 /bin/bash

$ ifconfig –a
$ route -n

 

6.4 None模式

使用none模式,Docker 容器擁有本身的 Network Namespace,可是,並不爲Docker 容器進行任何網絡配置。也就是說,這個 Docker 容器沒有網卡、IP、路由等信息。須要咱們本身爲 Docker 容器添加網卡、配置 IP 等。 None模式示意圖: ​​演示:

$ docker run -tid --net=none --name \
                docker_non1 ubuntu-base:v3

$ docker exec -ti docker_non1 /bin/bash

$ ifconfig –a
$ route -n

 

Docker 的跨主機通訊咱們這裏就先暫時不講解,咱們在後面的Kubernetes課程當中會用到。

相關文章
相關標籤/搜索