《Docker從入門到實踐》閱讀筆記

《Docker從入門到實踐》閱讀筆記

原書地址: https://yeasy.gitbooks.io/docker_practice/content/introduction/what.html

[TOC]

1. Docker簡介

1.1 什么是Docker

1.2 為什么要用Docker

2. 基本概念

2.1 鏡像

  • Docker 鏡像是一個特殊的文件系統,除了提供容器運行時所需的程序、庫、資源、配置等文件外,還包含了一些為運行時準備的一些配置參數(如匿名卷、環境變量、用戶等)。鏡像不包含任何動態數據,其內容在構建之后也不會被改變。
  • 分層存儲:由多層文件系統聯合組成。

2.2 容器

  • 鏡像(Image)和容器(Container)的關系,就像是面向對象程序設計中的類和實例 一樣,鏡像是靜態的定義,容器是鏡像運行時的實體。
  • 容器的實質是進程,但與直接在宿主執行的進程不同,容器進程運行于屬于自己的獨立的 命名空間。因此容器可以擁有自己的 root 文件系統、自己的網絡配置、自己的進程空間,甚至自己的用戶 ID 空間。容器內的進程是運行在一個隔離的環境里,使用起來,就好像是在一個獨立于宿主的系統下操作一樣。這種特性使得容器封裝的應用比直接在宿主運行更加安全。也因為這種隔離的特性,很多人初學 Docker 時常常會混淆容器和虛擬機。
  • 前面講過鏡像使用的是分層存儲,容器也是如此。每一個容器運行時,是以鏡像為基礎層,在其上創建一個當前容器的存儲層,我們可以稱這個為容器運行時讀寫而準備的存儲層為容器存儲層。
  • 容器存儲層的生存周期和容器一樣,容器消亡時,容器存儲層也隨之消亡。因此,任何保存于容器存儲層的信息都會隨容器刪除而丟失。
  • 按照 Docker 最佳實踐的要求,容器不應該向其存儲層內寫入任何數據,容器存儲層要保持無狀態化。所有的文件寫入操作,都應該使用 數據卷(Volume)、或者綁定宿主目錄,在這些位置的讀寫會跳過容器存儲層,直接對宿主(或網絡存儲)發生讀寫,其性能和穩定性更高。
  • 數據卷的生存周期獨立于容器,容器消亡,數據卷不會消亡。因此,使用數據卷后,容器刪除或者重新運行之后,數據卻不會丟失。

2.3 倉庫

2.3.1 Docker Registry

  • Docker倉庫是一個集中的存儲、分發鏡像的服務。
  • 一個Docker Registry中可以包含多個倉庫(Repository);每個倉庫可以包含多個標簽(Tag);每個標簽對應一個鏡像。
  • 通常,一個倉庫會包含同一個軟件不同版本的鏡像,而標簽就常用于對應該軟件的各個版本。我們可以通過 <倉庫名>:<標簽>的格式來指定具體是這個軟件哪個版本的鏡像。如果不給出標簽,將以 latest 作為默認標簽。
  • 倉庫名經常以 兩段式路徑 形式出現,比如jwilder/nginx-proxy前者往往意味著Docker Registry多用戶環境下的用戶名,后者則往往是對應的軟件名。但這并非絕對,取決于所使用的具體Docker Registry的軟件或服務。

2.3.2 Docker Registry 公開服務

  • Docker Registry 公開服務是開放給用戶使用、允許用戶管理鏡像的 Registry 服務。一般這類公開服務允許用戶免費上傳、下載公開的鏡像,并可能提供收費服務供用戶管理私有鏡像。最常使用的 Registry 公開服務是官方的Docker Hub。
  • 由于某些原因,在國內訪問這些服務可能會比較慢。國內的一些云服務商提供了針對 Docker Hub 的鏡像服務(Registry Mirror),這些鏡像服務被稱為加速器。常見的有 阿里云加速器、DaoCloud 加速器 等。使用加速器會直接從國內的地址下載 Docker Hub 的鏡像,比直接從 Docker Hub 下載速度會提高很多。在 3. 安裝 Docker 一節中有詳細的配置方法。
  • 國內也有一些云服務商提供類似于 Docker Hub 的公開服務。比如 時速云鏡像倉庫、網易云鏡像服務、DaoCloud 鏡像市場阿里云鏡像庫 等。

2.3.3 私有 Docker Registry

  • 除了使用公開服務外,用戶還可以在本地搭建私有 Docker Registry。Docker 官方提供了 Docker Registry 鏡像,可以直接使用做為私有 Registry 服務。在 私有倉庫 一節中,會有進一步的搭建私有 Registry 服務的講解。

3. 安裝Docker {#install}

  • 版本命名規則:
    項目|說明
    -|-
    版本格式|YY.MM
    Stable版本|每季度發行
    Edge版本|每月發行
  • 同時 Docker 劃分為 CE 和 EE。CE,即社區版(免費,支持周期三個月);EE, 即企業版,強調安全,付費使用。

3.1 Ubuntu

3.1.1 卸載舊版本

$sudo apt-get remove docker docker-engine docker.io

3.1.2 安裝可選內核模塊

$ sudo apt-get update
$ sudo apt-get install linux-image-extra-$(uname -r) \ 
> linux-image-extra-virtual

3.1.3 使用APT安裝

$ sudo apt-get update

$ sudo apt-get install \
>    apt-transport-https \
>    ca-certificates \
>    curl \
>    software-properties-common

鑒于國內網絡問題,強烈建議使用國內源,官方源請在注釋中查看。

為了確認所下載軟件包的合法性,需要添加軟件源的 GPG 密鑰。

$ curl -fsSL \
> https://mirrors.ustc.edu.cn/docker-ce/linux/ubuntu/gpg \ 
> | sudo apt-key add -

然后,我們需要向source.list 中添加 Docker 軟件源

$ sudo add-apt-repository "deb \
> [arch=amd64] https://mirrors.ustc.edu.cn/docker-ce/linux/ubuntu \
> $(lsb_release -cs) stable"

3.1.4 安裝Docker CE

$ sudo apt-get update
$ sudo apt-get install docker-ce

3.1.5 使用腳本自動安裝

$ curl -fsSL get.docker.com -o get-docker.sh
$ sudo sh get-docker.sh --mirror Aliyun

3.1.6 啟動Docker CE

$ sudo systemctl enable docker
$ sudo systemctl start docker

3.1.7 建立docker用戶組

  • 建立Docker組:
$ sudo groupadd docker
  • 將當前用戶加入docker組:
$ sudo usermod -aG docker $USER

退出當前終端并重新登錄,進行如下測試。

3.1.8 測試Docker是否安裝正確

若出現如下信息,說明安裝成功:

$ docker run hello-world

Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
ca4f61b1923c: Pull complete
Digest: sha256:be0cd392e45be79ffeffa6b05338b98ebb16c87b255f48e297ec7f98e123905c
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://cloud.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/engine/userguide/

3.2 Debian

3.3 CentOS

3.4 Raspberry Pi

3.5 macOS

3.6 Windows PC

3.7 鏡像加速器

國內從 Docker Hub 拉取鏡像有時會遇到困難,此時可以配置鏡像加速器。Docker 官方和國內很多云服務商都提供了國內加速器服務,例如:

Ubuntu 16.04+、Debian 8+、CentOS 7
對于使用 systemd 的系統,請在 /etc/docker/daemon.json 中寫入如下內容(如果文件不存在請新建該文件)

{
  "registry-mirrors": [
    "https://registry.docker-cn.com"
  ]
}

之后重啟服務

$ sudo systemctl daemon-reload
$ sudo systemctl restart docker

檢查是否生效:
??配置加速器之后,如果拉取鏡像仍然十分緩慢,請手動檢查加速器配置是否生效,在命令行執行docker info,如果從結果中看到了如下內容,說明配置成功。

Registry Mirrors:
 https://registry.docker-cn.com/

4. 使用Docker鏡像

Docker的三大組件:鏡像、容器和倉庫

4.1 獲取鏡像

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 的摘要,以確保下載一致性。

運行:
??有了鏡像后,我們就能夠以這個鏡像為基礎啟動并運行一個容器。以上面的 ubuntu:16.04 為例,如果我們打算啟動里面的 bash 并且進行交互式操作的話,可以執行下面的命令。

$ docker run -it --rm ubuntu:16.04 bash

root@a1765e7e0b4b:/# cat /etc/os-release
NAME="Ubuntu"
VERSION="16.04.3 LTS (Xenial Xerus)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 16.04.3 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/"
VERSION_CODENAME=xenial
UBUNTU_CODENAME=xenial

參數介紹:

  • -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 退出了這個容器。

4.2 列出鏡像

要想列出已經下載下來的鏡像,可以使用 docker image ls 命令。

$ docker image ls
REPOSITORY    TAG    IMAGE ID        CREATED        SIZE
ubuntu        16.04  0458a4468cbc    3 days ago     112MB
hello-world   latest f2a91732366c    2 months ago   1.85kB

鏡像 ID 則是鏡像的唯一標識,一個鏡像可以對應多個標簽。

鏡像體積
??如果仔細觀察,會注意到,這里標識的所占用空間和在 Docker Hub 上看到的鏡像大小不同。比如,ubuntu:16.04 鏡像大小,在這里是 127 MB,但是在 Docker Hub 顯示的卻是 50 MB。這是因為 Docker Hub 中顯示的體積是壓縮后的體積。在鏡像下載和上傳過程中鏡像是保持著壓縮狀態的,因此 Docker Hub 所顯示的大小是網絡傳輸中更關心的流量大小。而 docker image ls 顯示的是鏡像下載到本地后,展開的大小。

虛懸鏡像(dangling image)
??有些鏡像既沒有倉庫名,也沒有標簽,均為<none>。這個鏡像原本是有鏡像名和標簽的,隨著官方鏡像維護,發布了新版本后,重新 docker pull 時,舊的鏡像名被轉移到了新下載的鏡像身上,而舊的鏡像上的這個名稱則被取消,從而成為了 <none>。除了 docker pull 可能導致這種情況,docker build 也同樣可以導致這種現象。由于新舊鏡像同名,舊鏡像名稱被取消,從而出現倉庫名、標簽均為 <none> 的鏡像??梢杂孟旅娴拿顚iT顯示這類鏡像:

$ docker image ls -f dangling=true

一般來說,虛懸鏡像已經失去了存在的價值,可以隨意刪除,可以用下面的命令刪除。

$ docker image prune

中間層鏡像
??為了加速鏡像構建、重復利用資源,Docker 會利用中間層鏡像。所以在使用一段時間后,可能會看到一些依賴的中間層鏡像。默認的 docker image ls 列表中只會顯示頂層鏡像,如果希望顯示包括中間層鏡像在內的所有鏡像的話,需要加 -a 參數。

$ docker image ls -a

??這樣會看到很多無標簽的鏡像,與之前的虛懸鏡像不同,這些無標簽的鏡像很多都是中間層鏡像,是其它鏡像所依賴的鏡像。這些無標簽鏡像不應該刪除,否則會導致上層鏡像因為依賴丟失而出錯。實際上,這些鏡像也沒必要刪除,因為之前說過,相同的層只會存一遍,而這些鏡像是別的鏡像的依賴,因此并不會因為它們被列出來而多存了一份,無論如何你也會需要它們。只要刪除那些依賴它們的鏡像后,這些依賴的中間層鏡像也會被連帶刪除。

列出部分鏡像
??不加任何參數的情況下,docker image ls 會列出所有頂級鏡像,但是有時候我們只希望列出部分鏡像。docker image ls 有好幾個參數可以幫助做到這個事情。

  • 根據倉庫名列出鏡像
$ docker image ls ubuntu
  • 列出特定的某個鏡像,也就是說指定倉庫名和標簽
$ docker image ls ubuntu:16.04
  • docker image ls 還支持強大的過濾器參數 --filter,或者簡寫 -f。之前我們已經看到了使用過濾器來列出虛懸鏡像的用法,它還有更多的用法。比如,我們希望看到在 mongo:3.2 之后(查看'之前',將since改為before)建立的鏡像,可以用下面的命令:
$ docker image ls -f since=mongo:3.2

以特定格式顯示
??默認情況下,docker image ls 會輸出一個完整的表格,但是我們并非所有時候都會需要這些內容。比如,剛才刪除虛懸鏡像的時候,我們需要利用 docker image ls 把所有的虛懸鏡像的 ID 列出來,然后才可以交給 docker image rm 命令作為參數來刪除指定的這些鏡像,這個時候就用到了 -q 參數。

$ docker image ls -q
5f515359c7f8
05a60462f8ba
fe9198c04d62
00285df0df87
f753707788c5
f753707788c5
1e0c3dd64ccd

??自定義格式返回(可將輸出結果直接作為下一條命令的輸入),如下面的命令會直接列出鏡像結果,并且只包含鏡像ID和倉庫名:

$ docker image ls --format "{{.ID}}: {{.Repository}}"
5f515359c7f8: redis
05a60462f8ba: nginx
fe9198c04d62: mongo
00285df0df87: <none>
f753707788c5: ubuntu
f753707788c5: ubuntu
1e0c3dd64ccd: ubuntu

或者打算以表格等距顯示,并且有標題行,和默認一樣,不過自己定義列:

$ docker image ls --format "table {{.ID}}\t{{.Repository}}\t{{.Tag}}"
IMAGE ID            REPOSITORY          TAG
5f515359c7f8        redis               latest
05a60462f8ba        nginx               latest
fe9198c04d62        mongo               3.2
00285df0df87        <none>              <none>
f753707788c5        ubuntu              16.04
f753707788c5        ubuntu              latest
1e0c3dd64ccd        ubuntu              14.04

4.3 刪除鏡像

如果要刪除本地的鏡像,可以使用 docker image rm 命令,其格式為:

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

其中,<鏡像> 可以是 鏡像短 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

當然,更精確的是使用 鏡像摘要 刪除鏡像。

$ docker image ls --digests
REPOSITORY  TAG     DIGEST      IMAGE ID        CREATED     SIZE
node        slim    sha256:b4f0e0bdeb578043c1ea6862f0d40cc4afe32a4a582f3be235a3b164422be228   6e0c4c8e3913        3 weeks ago         214 MB

$ docker image rm node@sha256:b4f0e0bdeb578043c1ea6862f0d40cc4afe32a4a582f3be235a3b164422be228
Untagged: node@sha256:b4f0e0bdeb578043c1ea6862f0d40cc4afe32a4a582f3be235a3b164422be228

Untagged和Delete

  • 刪除行為分為UntaggedDelete兩類:
  • 我們使用上面命令刪除鏡像的時候,是在取消某個鏡像的標簽,即 Untagged。
  • 一個鏡像可以對應多個標簽,只有某個鏡像的所有標簽都被取消,該鏡像才可能會被Delete(為什么是可能下文解釋)。
  • 由于鏡像的多層結構,有可能某個其它鏡像或容器正依賴于當前鏡像的某一層。在這樣的情況下,該鏡像所有標簽都被取消該鏡像也不會被刪除。



docker image ls命令配合使用
比如,我們需要刪除所有倉庫名為 redis 的鏡像:

$ docker image rm $(docker image ls -q redis)

或者刪除所有在 mongo:3.2 之前的鏡像:

$ docker image rm $(docker image ls -q -f before=mongo:3.2)

4.4利用 commit 理解鏡像構成

// 構建鏡像,其中90表示綁定服務器的端口,80表示鏡像端口,瀏覽器訪問90端口正常
$ docker run --name webserver -d -p 90:80 nginx 

// 更改Web服務器內容
$ docker exec -it webserver bash
root@3729b97e8226:# echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
root@3729b97e8226:/# exit
exit

// 用commit能夠定制自己的鏡像,不過使用Dockerfile更好,因此不細說commit

4.5 使用Dockerfile定制鏡像

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

// 修改服務器內容
$ mkdir mynginx
$ cd mynginx
$ touch Dockerfile

// 內容
FROM nginx
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
  • FROM指定基礎鏡像,FROM scratch表示以空白鏡像為基礎
  • RUN有兩種格式,第一種更常見:
  • RUN <命令>
  • RUN ["可執行文件", "參數1", "參數2"]

注:每使用一個RUN就會加一層鏡像,所以對于沒必要記錄的命令可以使用如下形式

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

//注意:最后一句刪除非常重要

構建鏡像

// 在Dockerfile目錄下,執行以下命令,
// 格式為docker build [選項] <上下文路徑/URL/->
// 執行結束后,即可像上文運行nginx一樣運行nginx:v3
$ docker build -t nginx:v3 .

??docker是C/S結構,并非所有命令都是在本地執行,如docker build在Docker引擎(即服務器端)執行,因此我們在執行該命令時需要給出上下文。如Dockerfile中的命令COPY ./package.json /app/就是復制給出的上下文下的package.json。
??在給出上下文后,該目錄下的內容會被發送到服務器,可以使用.dockerignore文件制定不需要發送的文件。

4.6 Dockerfile指令詳解

// COPY <源路徑>... <目標路徑>,支持正則匹配
COPY hom* /mydir/
COPY hom?.txt /mydir/

// ADD和COPY類似,能夠自動將gzip, bzip2 以及 xz壓縮格式自動解壓,
// 無需自動解壓時,使用COPY
ADD ubuntu-xenial-core-cloudimg-amd64-root.tar.gz /

// CMD 容器啟動命令,用于指定默認的容器主進程的啟動命令,類似RUN
// 使用該格式'CMD ["可執行文件", "參數1", "參數2"...]'
// 注:CMD是注進程,退出后整個容器退出,因此不能后臺執行
CMD service nginx start //后臺執行nginx,執行完立刻退出
CMD ["nginx", "-g", "daemon off;"] //前臺執行,正確

// ENTRYPOINT和CMD類似,不過可以繼續加參數(如docker run myip -i)
// 或者執行腳本
ENTRYPOINT [ "curl", "-s", "http://ip.cn" ]
ENTRYPOINT ["docker-entrypoint.sh"]

// ENV <key> <value>
ENV NODE_VERSION 7.2.0
RUN curl -SLO https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz

// VOLUME 定義匿名卷
// '/data'目錄就會在運行時自動掛載為匿名卷,任何向'/data'中寫入的信息都不會記錄進容器存儲層,從而保證了容器存儲層的無狀態化。
VOLUME /data

// WORKDIR 指定工作目錄
WORKDIR <工作目錄路徑>

// USER 指定當前用戶
USER <用戶名>

// ONBUILD 當內容與該項目相關時,前面加上ONBUILD即可重用
FROM node:slim
RUN mkdir /app
WORKDIR /app
ONBUILD COPY ./package.json /app
ONBUILD RUN [ "npm", "install" ]
ONBUILD COPY . /app/
CMD [ "npm", "start" ]

5. 操作容器

容器是獨立運行的應用和他們的環境。

5.1 啟動

??啟動分為:新建容器并啟動、啟動終止狀態的容器;由于docker容器非常輕量級,用戶選擇隨時新建刪除。

新建并啟動
命令為docker run,來看下面的例子:

$ docker run ubuntu:14.04 /bin/echo 'Hello world'
Hello world
$ docker run -t -i ubuntu:14.04 /bin/bash
root@af8bae53bdd3:/#
//-t 讓Docker分配一個偽終端(pseudo-tty)并綁定到容器的標準輸入上
//-i 則讓容器的標準輸入保持打開。

當利用 docker run 來創建容器時,Docker 在后臺運行的標準操作包括:

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

啟動已終止容器
首先利用docker ps查看已終止容器,再利用docker container start <id>命令啟動

5.2 守護態運行

看下下面例子:

//直接運行
$ docker run ubuntu:17.10 /bin/sh -c "while true; do echo hello world; sleep 1; done"
hello world
hello world
hello world
hello world
//使用-d使容器在后臺運行,獲取id,具體輸出在log中查看
$ docker run -d ubuntu:17.10 /bin/sh -c "while true; do echo hello world; sleep 1; done"
77b2dc01fe0f3f1265df143181e7b9af5e05279a884f4776ee75350ea9d8017a
//使用啟動時返回的id,也可以通過docker container ls命令來查看容器信息。
$ docker container logs [container ID or NAMES]
hello world
hello world
hello world

5.3 終止容器

  • 使用docker container stop終止容器;容器中的應用結束,容器也將終止
  • 終止狀態的容器可以用docker container ls -a命令看到。
  • 處于終止狀態的容器,可以通過docker container start命令來重新啟動。
  • docker container restart可以重啟容器

5.4 進入容器

??使用-d后,容器進入后臺運行,有時候需要進入容器進行操作,可以使用docker attachdocker exec,推薦使用docker exec

  • attach命令
$ docker run -dit ubuntu
243c32535da7d142fb0e6df616a3c3ada0b8ab417937c853a9e1c251f499f550

$ docker container ls
CONTAINER ID ……
243c32535da7 ……

$ docker attach 243c
root@243c32535da7:/#

//注:如果從這個 stdin 中 exit,會導致容器的停止。
  • exec命令
    ??該命令可以使用-i-t參數;相對attach命令,如果從這個 stdin 中 exit,不會導致容器的停止??聪旅胬樱?/li>
$ docker run -dit ubuntu
69d137adef7a8a689cbcb059e94da5489d3cddd240ff675c640c8d96e84fe1f6

$ docker container ls
CONTAINER ID ……
69d137adef7a ……

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

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

5.5 導出和導入容器

  • 導出容器
$ docker container ls -a
CONTAINER ID ……
7691a814370e ……

$ docker export 7691a814370e > ubuntu.tar
  • 導入容器
    將容器導入為鏡像
$ cat ubuntu.tar | docker import - test/ubuntu:v1.0
$ docker image ls
REPOSITORY    TAG   IMAGE ID        CREATED     VIRTUAL SIZE
test/ubuntu   v1.0  9d37a6082e97    20s ago     171.3 MB

//也可以通過指定 URL 或者某個目錄來導入
$ docker import http://ex.com/eximage.tgz ex/imagerepo

5.6 刪除容器

  • 使用docker container rm來刪除一個處于終止狀態的容器。如果要刪除一個運行中的容器,可以添加 -f 參數。
  • docker container prune命令可以清理掉所有處于終止狀態的容器。

6. 訪問倉庫

7. 數據管理

7.1 數據卷

數據卷時用來存放容器共享數據的目錄。

10. Docker三劍客之Compose項目

10.1 安裝

$ sudo curl -L https://github.com/docker/compose/releases/download/1.17.1/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
$ sudo chmod +x /usr/local/bin/docker-compose

10.2 命令補全

$ sudo sh -c 'curl -L https://raw.githubusercontent.com/docker/compose/1.8.0/contrib/completion/bash/docker-compose > /etc/bash_completion.d/docker-compose'

10.3 使用

  • 服務 (service):一個應用容器,實際上可以運行多個相同鏡像的實例。
  • 項目 (project):由一組關聯的應用容器組成的一個完整業務單元。

命令說明

-f, --file FILE 指定使用的 Compose 模板文件,默認為 docker-compose.yml,可以多次指定。

-p, --project-name NAME 指定項目名稱,默認將使用所在目錄名稱作為項目名。

--x-networking 使用 Docker 的可拔插網絡后端特性

--x-network-driver DRIVER 指定網絡后端的驅動,默認為 bridge

--verbose 輸出更多調試信息。

-v, --version 打印版本并退出。

命令使用說明

build

格式為 docker-compose build [options] [SERVICE...]。

構建(重新構建)項目中的服務容器。

服務容器一旦構建后,將會帶上一個標記名,例如對于 web 項目中的一個 db 容器,可能是 web_db。

可以隨時在項目目錄下運行 docker-compose build 來重新構建服務。

選項包括:

--force-rm 刪除構建過程中的臨時容器。

--no-cache 構建鏡像過程中不使用 cache(這將加長構建過程)。

--pull 始終嘗試通過 pull 來獲取更新版本的鏡像。

config

驗證 Compose 文件格式是否正確,若正確則顯示配置,若格式錯誤顯示錯誤原因。

down

此命令將會停止 up 命令所啟動的容器,并移除網絡

exec

進入指定的容器

help

獲得一個命令的幫助

images

列出 Compose 文件中包含的鏡像

kill

格式為 docker-compose kill [options] [SERVICE...]。

通過發送 SIGKILL 信號來強制停止服務容器。

支持通過 -s 參數來指定發送的信號,例如通過如下指令發送 SIGINT 信號。

$ docker-compose kill -s SIGINT

logs

格式為 docker-compose logs [options] [SERVICE...]。

查看服務容器的輸出。默認情況下,docker-compose 將對不同的服務輸出使用不同的顏色來區分??梢酝ㄟ^ --no-color 來關閉顏色。

該命令在調試問題的時候十分有用。

pause

格式為 docker-compose pause [SERVICE...]。

暫停一個服務容器。

port

格式為 docker-compose port [options] SERVICE PRIVATE_PORT。

打印某個容器端口所映射的公共端口。

選項:

--protocol=proto 指定端口協議,tcp(默認值)或者 udp。

--index=index 如果同一服務存在多個容器,指定命令對象容器的序號(默認為 1)。

ps

格式為 docker-compose ps [options] [SERVICE...]。

列出項目中目前的所有容器。

選項:

-q 只打印容器的 ID 信息。

pull

格式為 docker-compose pull [options] [SERVICE...]。

拉取服務依賴的鏡像。

選項:

--ignore-pull-failures 忽略拉取鏡像過程中的錯誤。

push

推送服務依賴的鏡像到 Docker 鏡像倉庫。

restart

格式為 docker-compose restart [options] [SERVICE...]。

重啟項目中的服務。

選項:

-t, --timeout TIMEOUT 指定重啟前停止容器的超時(默認為 10 秒)。

rm

格式為 docker-compose rm [options] [SERVICE...]。

刪除所有(停止狀態的)服務容器。推薦先執行 docker-compose stop 命令來停止容器。

選項:

-f, --force 強制直接刪除,包括非停止狀態的容器。一般盡量不要使用該選項。

-v 刪除容器所掛載的數據卷。

run

格式為 docker-compose run [options] [-p PORT...] [-e KEY=VAL...] SERVICE [COMMAND] [ARGS...]。

在指定服務上執行一個命令。

例如:

$ docker-compose run ubuntu ping docker.com
將會啟動一個 ubuntu 服務容器,并執行 ping docker.com 命令。

默認情況下,如果存在關聯,則所有關聯的服務將會自動被啟動,除非這些服務已經在運行中。

該命令類似啟動容器后運行指定的命令,相關卷、鏈接等等都將會按照配置自動創建。

兩個不同點:

給定命令將會覆蓋原有的自動運行命令;

不會自動創建端口,以避免沖突。

如果不希望自動啟動關聯的容器,可以使用 --no-deps 選項,例如

$ docker-compose run --no-deps web python manage.py shell
將不會啟動 web 容器所關聯的其它容器。

選項:

-d 后臺運行容器。

--name NAME 為容器指定一個名字。

--entrypoint CMD 覆蓋默認的容器啟動指令。

-e KEY=VAL 設置環境變量值,可多次使用選項來設置多個環境變量。

-u, --user="" 指定運行容器的用戶名或者 uid。

--no-deps 不自動啟動關聯的服務容器。

--rm 運行命令后自動刪除容器,d 模式下將忽略。

-p, --publish=[] 映射容器端口到本地主機。

--service-ports 配置服務端口并映射到本地主機。

-T 不分配偽 tty,意味著依賴 tty 的指令將無法運行。

scale

格式為 docker-compose scale [options] [SERVICE=NUM...]。

設置指定服務運行的容器個數。

通過 service=num 的參數來設置數量。例如:

$ docker-compose scale web=3 db=2
將啟動 3 個容器運行 web 服務,2 個容器運行 db 服務。

一般的,當指定數目多于該服務當前實際運行容器,將新創建并啟動容器;反之,將停止容器。

選項:

-t, --timeout TIMEOUT 停止容器時候的超時(默認為 10 秒)。

start

格式為 docker-compose start [SERVICE...]。

啟動已經存在的服務容器。

stop

格式為 docker-compose stop [options] [SERVICE...]。

停止已經處于運行狀態的容器,但不刪除它。通過 docker-compose start 可以再次啟動這些容器。

選項:

-t, --timeout TIMEOUT 停止容器時候的超時(默認為 10 秒)。

top

查看各個服務容器內運行的進程。

unpause

格式為 docker-compose unpause [SERVICE...]。

恢復處于暫停狀態中的服務。

up
格式為 docker-compose up [options] [SERVICE...]。

該命令十分強大,它將嘗試自動完成包括構建鏡像,(重新)創建服務,啟動服務,并關聯服務相關容器的一系列操作。

鏈接的服務都將會被自動啟動,除非已經處于運行狀態。

可以說,大部分時候都可以直接通過該命令來啟動一個項目。

默認情況,docker-compose up 啟動的容器都在前臺,控制臺將會同時打印所有容器的輸出信息,可以很方便進行調試。

當通過 Ctrl-C 停止命令時,所有容器將會停止。

如果使用 docker-compose up -d,將會在后臺啟動并運行所有的容器。一般推薦生產環境下使用該選項。

默認情況,如果服務容器已經存在,docker-compose up 將會嘗試停止容器,然后重新創建(保持使用 volumes-from 掛載的卷),以保證新啟動的服務匹配 docker-compose.yml 文件的最新內容。如果用戶不希望容器被停止并重新創建,可以使用 docker-compose up --no-recreate。這樣將只會啟動處于停止狀態的容器,而忽略已經運行的服務。如果用戶只想重新部署某個服務,可以使用 docker-compose up --no-deps -d <SERVICE_NAME> 來重新創建服務并后臺停止舊服務,啟動新服務,并不會影響到其所依賴的服務。

選項:

-d 在后臺運行服務容器。

--no-color 不使用顏色來區分不同的服務的控制臺輸出。

--no-deps 不啟動服務所鏈接的容器。

--force-recreate 強制重新創建容器,不能與 --no-recreate 同時使用。

--no-recreate 如果容器已經存在了,則不重新創建,不能與 --force-recreate 同時使用。

--no-build 不自動構建缺失的服務鏡像。

-t, --timeout TIMEOUT 停止容器時候的超時(默認為 10 秒)。

version

格式為 docker-compose version。

打印版本信息。

10.4 compose模板文件

默認的模板文件名稱為 docker-compose.yml

version: '3' //表示模板文件版本,不同版本支持的docker引擎不同

services:
  app1:
    image: examples/web //表示基礎鏡像
  app2:
    build: ./dir //指定Dockerfile所在目錄,通過Dockerfile構建鏡像
  app3:
    build:
      context: ./dir //指定Dockerfile所在目錄
      dockerfile: Dockerfile-alternate //指定Dockerfile文件名
      args:
        buildno: 1 //指定參數
  app4:
      build:
          context: .
          cache_from: //指定緩存
            - alpine:latest
            - corp/web_app:3.14

cap_add, cap_drop
指定容器的內核能力(capacity)分配。

例如,讓容器擁有所有能力可以指定為:

cap_add:
  - ALL

去掉 NET_ADMIN 能力可以指定為:

cap_drop:
  - NET_ADMIN

command
覆蓋容器啟動后默認執行的命令。
command: echo "hello world"

configs
僅用于 Swarm mode,詳細內容請查看 Swarm mode 一節。

cgroup_parent
指定父 cgroup 組,意味著將繼承該組的資源限制。

例如,創建了一個 cgroup 組名稱為 cgroups_1。

cgroup_parent: cgroups_1
container_name

指定容器名稱。默認將會使用 項目名稱服務名稱序號 這樣的格式。

container_name: docker-web-container

注意: 指定容器名稱后,該服務將無法進行擴展(scale),因為 Docker 不允許多個容器具有相同的名稱。

deploy
僅用于 Swarm mode,詳細內容請查看 Swarm mode 一節

devices
指定設備映射關系。

devices:
  - "/dev/ttyUSB1:/dev/ttyUSB0"
depends_on

解決容器的依賴、啟動先后的問題。以下例子中會先啟動 redis db 再啟動 web

version: '3'

services:
  web:
    build: .
    depends_on:
      - db
      - redis

  redis:
    image: redis

  db:
    image: postgres
注意:web 服務不會等待 redis db 「完全啟動」之后才啟動。

dns
自定義 DNS 服務器??梢允且粋€值,也可以是一個列表。

dns: 8.8.8.8

dns:
  - 8.8.8.8
  - 114.114.114.114

dns_search
配置 DNS 搜索域??梢允且粋€值,也可以是一個列表。

dns_search: example.com

dns_search:
  - domain1.example.com
  - domain2.example.com

tmpfs
掛載一個 tmpfs 文件系統到容器。

tmpfs: /run
tmpfs:
  - /run
  - /tmp

env_file
從文件中獲取環境變量,可以為單獨的文件路徑或列表。

如果通過 docker-compose -f FILE 方式來指定 Compose 模板文件,則 env_file 中變量的路徑會基于模板文件路徑。

如果有變量名稱與 environment 指令沖突,則按照慣例,以后者為準。

env_file: .env

env_file:
  - ./common.env
  - ./apps/web.env
  - /opt/secrets.env

環境變量文件中每一行必須符合格式,支持 # 開頭的注釋行。

# common.env: Set development environment
PROG_ENV=development

environment
設置環境變量。你可以使用數組或字典兩種格式。

只給定名稱的變量會自動獲取運行 Compose 主機上對應變量的值,可以用來防止泄露不必要的數據。

environment:
  RACK_ENV: development
  SESSION_SECRET:

environment:
  - RACK_ENV=development
  - SESSION_SECRET

如果變量名稱或者值中用到 true|false,yes|no 等表達 布爾 含義的詞匯,最好放到引號里,避免 YAML 自動解析某些內容為對應的布爾語義。這些特定詞匯,包括

y|Y|yes|Yes|YES|n|N|no|No|NO|true|True|TRUE|false|False|FALSE|on|On|ON|off|Off|OFF

expose
暴露端口,但不映射到宿主機,只被連接的服務訪問。

僅可以指定內部端口為參數

expose:
 - "3000"
 - "8000"
external_links

注意:不建議使用該指令。

鏈接到 docker-compose.yml 外部的容器,甚至并非 Compose 管理的外部容器。

external_links:
 - redis_1
 - project_db_1:mysql
 - project_db_1:postgresql
extra_hosts

類似 Docker 中的 --add-host 參數,指定額外的 host 名稱映射信息。

extra_hosts:
 - "googledns:8.8.8.8"
 - "dockerhub:52.1.157.61"

會在啟動后的服務容器中 /etc/hosts 文件中添加如下兩條條目。

8.8.8.8 googledns
52.1.157.61 dockerhub

healthcheck
通過命令檢查容器是否健康運行。

healthcheck:
  test: ["CMD", "curl", "-f", "http://localhost"]
  interval: 1m30s
  timeout: 10s
  retries: 3

image
指定為鏡像名稱或鏡像 ID。如果鏡像在本地不存在,Compose 將會嘗試拉去這個鏡像。

image: ubuntu
image: orchardup/postgresql
image: a4bc65fd
labels

為容器添加 Docker 元數據(metadata)信息。例如可以為容器添加輔助說明信息。

labels:
  com.startupteam.description: "webapp for a startup team"
  com.startupteam.department: "devops department"
  com.startupteam.release: "rc3 for v1.0"

links
注意:不推薦使用該指令。

logging
配置日志選項。

logging:
  driver: syslog
  options:
    syslog-address: "tcp://192.168.0.42:123"

目前支持三種日志驅動類型。

driver: "json-file"
driver: "syslog"
driver: "none"

options 配置日志驅動的相關參數。

options:
  max-size: "200k"
  max-file: "10"

network_mode
設置網絡模式。使用和 docker run 的 --network 參數一樣的值。

network_mode: "bridge"
network_mode: "host"
network_mode: "none"
network_mode: "service:[service name]"
network_mode: "container:[container name/id]"
networks

配置容器連接的網絡。

version: "3"
services:

  some-service:
    networks:
     - some-network
     - other-network

networks:
  some-network:
  other-network:

pid
跟主機系統共享進程命名空間。打開該選項的容器之間,以及容器和宿主機系統之間可以通過進程 ID 來相互訪問和操作。

pid: "host"

ports
暴露端口信息。

使用宿主端口:容器端口 (HOST:CONTAINER) 格式,或者僅僅指定容器的端口(宿主將會隨機選擇端口)都可以。

ports:
 - "3000"
 - "8000:8000"
 - "49100:22"
 - "127.0.0.1:8001:8001"

注意:當使用 HOST:CONTAINER 格式來映射端口時,如果你使用的容器端口小于 60 并且沒放到引號里,可能會得到錯誤結果,因為 YAML 會自動解析 xx:yy 這種數字格式為 60 進制。為避免出現這種問題,建議數字串都采用引號包括起來的字符串格式。

secrets
存儲敏感數據,例如 mysql 服務密碼。

version: "3"
services:

mysql:
  image: mysql
  environment:
    MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_root_password
  secrets:
    - db_root_password
    - my_other_secret

secrets:
  my_secret:
    file: ./my_secret.txt
  my_other_secret:
    external: true

security_opt
指定容器模板標簽(label)機制的默認屬性(用戶、角色、類型、級別等)。例如配置標簽的用戶名和角色名。

security_opt:
    - label:user:USER
    - label:role:ROLE

stop_signal
設置另一個信號來停止容器。在默認情況下使用的是 SIGTERM 停止容器。

stop_signal: SIGUSR1

sysctls
配置容器內核參數。

sysctls:
  net.core.somaxconn: 1024
  net.ipv4.tcp_syncookies: 0

sysctls:
  - net.core.somaxconn=1024
  - net.ipv4.tcp_syncookies=0

ulimits
指定容器的 ulimits 限制值。

例如,指定最大進程數為 65535,指定文件句柄數為 20000(軟限制,應用可以隨時修改,不能超過硬限制) 和 40000(系統硬限制,只能 root 用戶提高)。

  ulimits:
    nproc: 65535
    nofile:
      soft: 20000
      hard: 40000

volumes
數據卷所掛載路徑設置。可以設置宿主機路徑 (HOST:CONTAINER) 或加上訪問模式 (HOST:CONTAINER:ro)。

該指令中路徑支持相對路徑。

volumes:
 - /var/lib/mysql
 - cache/:/tmp/cache
 - ~/configs:/etc/configs/:ro

其它指令
此外,還有包括 domainname, entrypoint, hostname, ipc, mac_address, privileged, read_only, shm_size, restart, stdin_open, tty, user, working_dir 等指令,基本跟 docker run 中對應參數的功能一致。

指定服務容器啟動后執行的入口文件。

entrypoint: /code/entrypoint.sh
指定容器中運行應用的用戶名。

user: nginx
指定容器中工作目錄。

working_dir: /code
指定容器中搜索域名、主機名、mac 地址等。

domainname: your_website.com
hostname: test
mac_address: 08-00-27-00-0C-0A

允許容器中運行一些特權命令。

privileged: true
指定容器退出后的重啟策略為始終重啟。該命令對保持服務始終運行十分有效,在生產環境中推薦配置為 always 或者 unless-stopped。

restart: always
以只讀模式掛載容器的 root 文件系統,意味著不能對容器內容進行修改。

read_only: true
打開標準輸入,可以接受外部輸入。

stdin_open: true
模擬一個偽終端。

tty: true
讀取變量
Compose 模板文件支持動態讀取主機的系統環境變量和當前目錄下的 .env 文件中的變量。

例如,下面的 Compose 文件將從運行它的環境中讀取變量 ${MONGO_VERSION} 的值,并寫入執行的指令中。

version: "3"
services:

db:
  image: "mongo:${MONGO_VERSION}"

如果執行 MONGO_VERSION=3.2 docker-compose up 則會啟動一個 mongo:3.2 鏡像的容器;如果執行 MONGO_VERSION=2.8 docker-compose up 則會啟動一個 mongo:2.8 鏡像的容器。

若當前目錄存在 .env 文件,執行 docker-compose 命令時將從該文件中讀取變量。

在當前目錄新建 .env 文件并寫入以下內容。

# 支持 # 號注釋
MONGO_VERSION=3.6
執行 docker-compose up 則會啟動一個 mongo:3.6 鏡像的容器。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 一 、什么是 Docker Docker 最初是 dotCloud 公司創始人 Solomon Hykes 在法國...
    Blazzer閱讀 3,176評論 0 13
  • 基本概念 鏡像Docker 鏡像就是一個只讀的模板,鏡像可以用來創建 Docker 容器 容器容器是從鏡像創建的運...
    巨子聯盟閱讀 7,602評論 0 3
  • Docker — 云時代的程序分發方式 要說最近一年云計算業界有什么大事件?Google Compute Engi...
    ahohoho閱讀 15,627評論 15 147
  • 轉載自 http://blog.opskumu.com/docker.html 一、Docker 簡介 Docke...
    極客圈閱讀 10,543評論 0 120
  • 周日的早上,遇見你, 一朵石縫中長出的小白花。 露水潤濕了你的臉頰, 風兒又將它吹干。 湖邊的蒹葭郁郁蔥蔥, 山巔...
    嘉茵Lota閱讀 237評論 6 9