Docker是Go語言開發實現的容器。2013年發布至今,備受推崇。相關文檔、學習資料十分詳盡。近期有docker相關項目,得重新學習一下。博客以筆記。
1 容器誕生背景及優勢
(1)軟件開發和運維中,環境部署、配置,不勝其煩。舉例說明,Python開發和部署都必須配置Python解釋器;運維過程中,有時測試環境能通過,但是到線上卻報錯,究其原因,是環境不一致。
傳統運維過程中,線上有十台機器,每增加一台都需要重新部署一次,簡直就是“體力勞動”。
(2)虛擬機在一定程度可以解決這些問題,但是存在幾個缺點:
- 資源占用多 虛機啟動需要占用幾百M的內存。
- 冗余步驟多 系統級別的操作步驟,往往無法跳過,比如用戶登錄。
- 啟動慢,往往幾分鍾 啟動操作系統需要多久,啟動虛擬機就需要多久。
(3)容器優勢
容器不是模擬一個操作系統,而是對進程進行隔離。屬於進程級別。
- 啟動快, 相當於啟動本機底層系統的一個進程,而不是虛擬機內部的進程,速度快很多。
- 占用資源少,容器只占用需要的資源,不占用那些沒有用到的資源;多個容器可以共享資源,虛擬機是獨享資源。
- 體積小,容器只要包含用到的組件即可,而虛擬機是整個操作系統的打包,所以容器文件比虛擬機文件要小很多。
傳統虛擬化示意圖:
docker虛擬化示意圖:
2 Docker 基本概念
(1)鏡像image
Docker 把應用程序及其依賴,打包在 image 文件里面。只有通過這個文件,才能生成 Docker 容器。image 文件可以看作是容器的模板。Docker 根據 image 文件生成容器的實例。同一個 image 文件,可以生成多個同時運行的容器實例。
image 是二進制文件。實際開發中,一個 image 文件往往通過繼承另一個 image 文件,加上一些個性化設置而生成。舉例來說,你可以在 Ubuntu 的 image 基礎上,往里面加入 Apache 服務器,形成你的 image。
image 文件是通用的,一台機器的 image 文件拷貝到另一台機器,照樣可以使用。一般來說,為了節省時間,我們應該盡量使用別人制作好的 image 文件,而不是自己制作。即使要定制,也應該基於別人的 image 文件進行加工,而不是從零開始制作。
為了方便共享,image 文件制作完成后,可以上傳到網上的倉庫。Docker 的官方倉庫 Docker Hub 是最重要、最常用的 image 倉庫。此外,出售自己制作的 image 文件也是可以的。
(2)容器Container
image 文件生成的容器實例,本身也是一個文件,稱為容器文件。也就是說,一旦容器生成,就會同時存在兩個文件: image 文件和容器文件。而且關閉容器並不會刪除容器文件,只是容器停止運行而已。
(3)倉庫Repository
鏡像構建完成后,可以很容易的在當前宿主機上運行,但是,如果需要在其它服務器上使用這個鏡像,我們就需要一個集中的存儲、分發鏡像的服務,Docker Registry 就是這樣的服務。
一個 Docker Registry 中可以包含多個倉庫(Repository);每個倉庫可以包含多個標簽(Tag);每個標簽對應一個鏡像。
通常,一個倉庫會包含同一個軟件不同版本的鏡像,而標簽就常用於對應該軟件的各個版本。我們可以通過 <倉庫名>:<標簽> 的格式來指定具體是這個軟件哪個版本的鏡像。如果不給出標簽,將以 latest 作為默認標簽。
3 安裝docker
以CentOS為例:
Docker CE 支持 64 位版本 CentOS 7,並且要求內核版本不低於 3.10。 CentOS 7 滿足最低內核的要求,但由於內核版本比較低,部分功能(如 overlay2
存儲層驅動)無法使用,並且部分功能可能不太穩定。
(1)卸載舊版本
sudo yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-selinux docker-engine-selinux docker-engine
(2)安裝依賴包
yum install -y yum-utils device-mapper-persistent-data lvm2
(3)安裝國內源
yum-config-manager --add-repo https://mirrors.ustc.edu.cn/docker-ce/linux/centos/docker-ce.repo
(4)安裝docker-ce
yum makecache fast yum install docker-ce
也可以使用腳本自動安裝:
curl -fsSL get.docker.com -o get-docker.sh sh get-docker.sh --mirror Aliyun
(5)啟動docker-ce
systemctl enable docker systemctl start docker
示例:
(6)創建docker用戶組
默認情況下,docker 命令會使用 Unix socket 與 Docker 引擎通訊。而只有 root 用戶和 docker 組的用戶才可以訪問 Docker 引擎的 Unix socket。出於安全考慮,一般 Linux 系統上不會直接使用 root 用戶。
因此,更好地做法是將需要使用 docker 的用戶加入 docker 用戶組。
# 建立 docker 組: sudo groupadd docker # 將當前用戶加入 docker 組: sudo usermod -aG docker $USER # 退出當前終端並重新登錄,進行如下測試
注意:本次使用的是root用戶。
(7)測試docker是否安裝成功
docker run hello-world
如果出現如下信息表明安裝成功了:
Unable to find image 'hello-world:latest' locally latest: Pulling from library/hello-world ca4f61b1923c: Pull complete Digest: sha256:97ce6fa4b6cdc0790cda65fe7290b74cfebd9fa0c9b8c38e979330d547d22ce1 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/
(8)配置內核參數
默認配置下,如果在 CentOS 使用 Docker CE 看到下面的這些警告信息:
WARNING: bridge-nf-call-iptables is disabled WARNING: bridge-nf-call-ip6tables is disabled
添加內核配置參數以啟用這些功能:
tee -a /etc/sysctl.conf <<EOF net.bridge.bridge-nf-call-ip6tables = 1 net.bridge.bridge-nf-call-iptables = 1 EOF
重新加載內核配置
sysctl -p
4 獲取鏡像
從 Docker 鏡像倉庫獲取鏡像的命令是 docker pull
。其命令格式為:
docker pull [選項] [Docker Registry 地址[:端口號]/]倉庫名[:標簽]
具體的選項可以通過 docker pull --help
命令看到,這里我們說一下鏡像名稱的格式。
- Docker 鏡像倉庫地址:地址的格式一般是
<域名/IP>[:端口號]
。默認地址是 Docker Hub。 - 倉庫名:如之前所說,這里的倉庫名是兩段式名稱,即
<用戶名>/<軟件名>
。對於 Docker Hub,如果不給出用戶名,則默認為library
,也就是官方鏡像。
例如,從鏡像倉庫獲取nginx:
[@sjs_123_183 ~]# docker pull nginx Using default tag: latest latest: Pulling from library/nginx 2a72cbf407d6: Pull complete e19f9e910af9: Pull complete 2f3d26a87e79: Pull complete Digest: sha256:e36d7f5dabf1429d84135bb8a8086908e1150f1a178c75719a9e0e53ebb90353 Status: Downloaded newer image for nginx:latest
(1)上訴命令中沒有指定 Docker 鏡像倉庫地址,因此將會從 Docker Hub 獲取鏡像。
(2)而鏡像名稱是 nginx,因此將會獲取官方鏡像 library/nginx 倉庫中標簽為 latest 的鏡像。
(3)從下載過程中可以看到我們之前提及的分層存儲的概念,鏡像是由多層存儲所構成。下載也是一層層的去下載,並非單一文件。
下載過程中給出了每一層的 ID 的前 12 位。並且下載結束后,給出該鏡像完整的 sha256 的摘要,以確保下載一致性。
(4)官方鏡像是一直在維護,你所看到的層 ID 以及 sha256 的摘要可能和此處不一樣。
我們再獲取一個官方的Ubuntu鏡像看看:
[@sjs_123_183 ~]# docker pull ubuntu:16.04 16.04: Pulling from library/ubuntu 22dc81ace0ea: Pull complete 1a8b3c87dba3: Pull complete 91390a1c435a: Pull complete 07844b14977e: Pull complete b78396653dae: Pull complete Digest: sha256:e348fbbea0e0a0e73ab0370de151e7800684445c509d46195aef73e090a49bd6 Status: Downloaded newer image for ubuntu:16.04
以ubuntu鏡像為例,運行鏡像
docker run -it --rm ubuntu:16.04 bash
(1)docker run 就是運行容器的命令,具體格式可在后面闡述。
(2)-it:這是兩個參數,一個是 -i:交互式操作,一個是 -t 終端。我們這里打算進入 bash 執行一些命令並查看返回結果,因此我們需要交互式終端。
(3)--rm:這個參數是說容器退出后隨之將其刪除。默認情況下,為了排障需求,退出的容器並不會立即刪除,除非手動 docker rm。我們這里只是隨便執行個命令,看看結果,不需要排障和保留結果,因此使用 --rm 可以避免浪費空間。
(4)ubuntu:16.04:這是指用 ubuntu:16.04 鏡像為基礎來啟動容器。
(5)bash:放在鏡像名后的是命令,這里我們希望有個交互式 Shell,因此用的是 bash。
運行上訴命令后會進入ubuntu鏡像示例的的shell界面,我們查看系統,結果如下:
root@a1318dadbea3:/# cat /etc/os-release NAME="Ubuntu" VERSION="16.04.4 LTS (Xenial Xerus)" 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/" VERSION_CODENAME=xenial UBUNTU_CODENAME=xenial root@a1318dadbea3:/#
exit退出當前容器。
5 列出鏡像
列出鏡像的命令:
docker image ls
示例:
[@sjs_123_183 ~]# docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE nginx latest c5c4e8fa2cf7 6 days ago 109MB ubuntu 16.04 f975c5035748 4 weeks ago 112MB hello-world latest f2a91732366c 4 months ago 1.85kB
每列的含義:
REPOSITORY:倉庫名 TAG:標簽, 一個鏡像可以對應多個標簽 IMAGE ID:鏡像 ID,是鏡像的唯一標識 CREATED:創建時間 SIZE:所占用的空間
注意:此處看到所占用空間與Docker Hub 上看到的鏡像大小不同。
比如,ubuntu:16.04 鏡像大小,在這里是 112 MB,但是在 Docker Hub 顯示的卻是 43 MB。這是因為:
(1)Docker Hub 中顯示的體積是壓縮后的體積。減小網絡傳輸流量。
(2)docker image ls 顯示的是鏡像下載到本地后,展開的大小,准確說,是展開后的各層所占空間的總和,因為鏡像到本地后,查看空間的時候,更關心的是本地磁盤空間占用的大小。
(3)docker image ls 列表中的鏡像體積總和並非是所有鏡像實際硬盤消耗。由於 Docker 鏡像是多層存儲結構,並且可以繼承、復用,因此不同鏡像可能會因為使用相同的基礎鏡像,從而擁有共同的層。
由於 Docker 使用 Union FS,相同的層只需要保存一份即可,因此實際鏡像硬盤占用空間很可能要比這個列表鏡像大小的總和要小的多。
6 查看鏡像、容器、數據卷所占用的空間
命令:
docker system df
示例:
[@sjs_123_183 ~]# docker system df TYPE TOTAL ACTIVE SIZE RECLAIMABLE Images 3 2 221.4MB 112.4MB (50%) Containers 2 0 0B 0B Local Volumes 0 0 0B 0B Build Cache 0B 0B
7 虛懸鏡像
倉庫名和標簽均為 <none>,這類無標簽、無倉庫名鏡像被稱 虛懸鏡像(dangling image) 。
鏡像原本是有鏡像名和標簽的,比如原來為 mongo:3.2,隨着官方鏡像維護,發布了新版本后,重新 docker pull mongo:3.2 時,mongo:3.2 這個鏡像名被轉移到了新下載的鏡像身上,而舊的鏡像上的這個名稱則被取消,從而成為了 <none>。
除了 docker pull 可能導致這種情況,docker build 也同樣可以導致這種現象。由於新舊鏡像同名,舊鏡像名稱被取消,從而出現倉庫名、標簽均為 <none> 的鏡像。
虛懸鏡像示例:
<none> <none> 00285df0df87 5 days ago 342 MB
顯示虛懸鏡像命令:
docker image ls -f dangling=true
刪除虛懸鏡像:
docker image prune
8 中間層鏡像
為了加速鏡像構建、重復利用資源,Docker 會利用 中間層鏡像。所以在使用一段時間后,可能會看到一些依賴的中間層鏡像。默認的 docker image ls 列表中只會顯示頂層鏡像,如果希望顯示包括中間層鏡像在內的所有鏡像的話,需要加 -a 參數。
docker image ls -a
這樣會看到很多無標簽的鏡像,與之前的虛懸鏡像不同,這些無標簽的鏡像很多都是中間層鏡像,是其它鏡像所依賴的鏡像。這些無標簽鏡像不應該刪除,否則會導致上層鏡像因為依賴丟失而出錯。
實際上,這些鏡像也沒必要刪除,因為之前說過,相同的層只會存一遍,而這些鏡像是別的鏡像的依賴,因此並不會因為它們被列出來而多存了一份,無論如何你也會需要它們。只要刪除那些依賴它們的鏡像后,這些依賴的中間層鏡像也會被連帶刪除。
9 按需列出鏡像
(1)列出部分鏡像
命令:
docker image ls redis 按倉庫名過濾 docker image ls redis:3.2 按倉庫名和標簽過濾 docker image ls -f since=redis:3.2.11 列出redis:3.2.11之后建立的鏡像 docker image ls -f before=redis:3.2 列出redis:3.2之前建立的鏡像
示例:
[@sjs_123_183 ~]# docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE nginx latest c5c4e8fa2cf7 2 weeks ago 109MB redis latest c5355f8853e4 3 weeks ago 107MB redis 3.2 b05c3d76c8b3 5 weeks ago 99.7MB redis 3.2.11 b05c3d76c8b3 5 weeks ago 99.7MB ubuntu 16.04 f975c5035748 6 weeks ago 112MB hello-world latest f2a91732366c 5 months ago 1.85kB [@sjs_123_183 ~]# docker image ls redis REPOSITORY TAG IMAGE ID CREATED SIZE redis latest c5355f8853e4 3 weeks ago 107MB redis 3.2 b05c3d76c8b3 5 weeks ago 99.7MB redis 3.2.11 b05c3d76c8b3 5 weeks ago 99.7MB [@sjs_123_183 ~]# docker image ls redis:3.2 REPOSITORY TAG IMAGE ID CREATED SIZE redis 3.2 b05c3d76c8b3 5 weeks ago 99.7MB [@sjs_123_183 ~]# docker image ls -f since=redis:3.2.11 REPOSITORY TAG IMAGE ID CREATED SIZE nginx latest c5c4e8fa2cf7 2 weeks ago 109MB redis latest c5355f8853e4 3 weeks ago 107MB [@sjs_123_183 ~]# docker image ls -f before=redis:3.2 REPOSITORY TAG IMAGE ID CREATED SIZE ubuntu 16.04 f975c5035748 6 weeks ago 112MB hello-world latest f2a91732366c 5 months ago 1.85kB
(2)自定義輸出格式
docker images --filter "條件" 按條件過濾
示例:
[@sjs_123_183 ~]# docker images --filter "since=redis:3.2" REPOSITORY TAG IMAGE ID CREATED SIZE nginx latest c5c4e8fa2cf7 2 weeks ago 109MB redis latest c5355f8853e4 3 weeks ago 107MB
(3)使用go語言模板語法
docker images --format "{{.ID}} <--> {{.Repository}}" # 按 ID <--> 倉庫名格式列出 docker images --format "table {{.ID}}\t{{.Repository}}\t{{.Tag}}" # 按ID 倉庫 標簽的格式列出
示例:
[@sjs_123_183 ~]# docker images --format "{{.ID}} <--> {{.Repository}}" c5c4e8fa2cf7 <--> nginx c5355f8853e4 <--> redis b05c3d76c8b3 <--> redis b05c3d76c8b3 <--> redis f975c5035748 <--> ubuntu f2a91732366c <--> hello-world [@sjs_123_183 ~]# docker images --format "table {{.ID}}\t{{.Repository}}\t{{.Tag}}" IMAGE ID REPOSITORY TAG c5c4e8fa2cf7 nginx latest c5355f8853e4 redis latest b05c3d76c8b3 redis 3.2 b05c3d76c8b3 redis 3.2.11 f975c5035748 ubuntu 16.04 f2a91732366c hello-world latest
常用go語言模板:
.ID # 鏡像ID .Repository # 鏡像倉庫名 .Tag # 鏡像標簽 .Digest # 鏡像摘要 .CreatedSince # 鏡像創建到現在的耗時 .CreatedAt # 鏡像創建時間 .Size # 鏡像大小
10 刪除鏡像
10.1 單台刪除
刪除本地鏡像的命令是docker image rm,用法如下:
docker image rm [選項] <鏡像1> [<鏡像2> ...]
(1)<鏡像> 可以是 鏡像短 ID、鏡像長 ID、鏡像名 或者 鏡像摘要。
(2)也可以使用鏡像名,即 <倉庫名>:<標簽> 刪除鏡像。
(3)更精確的是使用 鏡像摘要 刪除鏡像。
注意:刪除行為分為兩類,一類是 Untagged,另一類是 Deleted。
鏡像的唯一標識是其 ID 和摘要,而一個鏡像可以有多個標簽。
因此當我們使用上面命令刪除鏡像的時候,實際上是在要求刪除某個標簽的鏡像。
(1)所以首先需要做的是將滿足我們要求的所有鏡像標簽都取消,這就是我們看到的 Untagged 的信息。因為一個鏡像可以對應多個標簽,因此當我們刪除了所指定的標簽后,可能還有別的標簽指向了這個鏡像,如果是這種情況,那么 Delete 行為就不會發生。所以並非所有的 docker rmi 都會產生刪除鏡像的行為,有可能僅僅是取消了某個標簽而已。
(2)當該鏡像所有的標簽都被取消了,該鏡像很可能會失去了存在的意義,因此會觸發刪除行為。鏡像是多層存儲結構,因此在刪除的時候也是從上層向基礎層方向依次進行判斷刪除。鏡像的多層結構讓鏡像復用變動非常容易,因此很有可能某個其它鏡像正依賴於當前鏡像的某一層。這種情況,依舊不會觸發刪除該層的行為。直到沒有任何層依賴當前層時,才會真實的刪除當前層。這就是為什么,有時候會奇怪,為什么明明沒有別的標簽指向這個鏡像,但是它還是存在的原因,也是為什么有時候會發現所刪除的層數和自己 docker pull 看到的層數不一樣的源。
(3)除了鏡像依賴以外,還需要注意的是容器對鏡像的依賴。如果有用這個鏡像啟動的容器存在(即使容器沒有運行),那么同樣不可以刪除這個鏡像。之前講過,容器是以鏡像為基礎,再加一層容器存儲層,組成這樣的多層存儲結構去運行的。因此該鏡像如果被這個容器所依賴的,那么刪除必然會導致故障。如果這些容器是不需要的,應該先將它們刪除,然后再來刪除鏡像。
示例:
[@sjs_123_183 ~]# docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE nginx latest c5c4e8fa2cf7 2 weeks ago 109MB redis latest c5355f8853e4 3 weeks ago 107MB redis 3.2 b05c3d76c8b3 5 weeks ago 99.7MB redis 3.2.11 b05c3d76c8b3 5 weeks ago 99.7MB ubuntu 16.04 f975c5035748 6 weeks ago 112MB hello-world latest f2a91732366c 5 months ago 1.85kB [@sjs_123_183 ~]# [@sjs_123_183 ~]# [@sjs_123_183 ~]# docker image rm redis:3.2.11 Untagged: redis:3.2.11 [@sjs_123_183 ~]# [@sjs_123_183 ~]# [@sjs_123_183 ~]# docker image rm f97 Untagged: ubuntu:16.04 Untagged: ubuntu@sha256:e348fbbea0e0a0e73ab0370de151e7800684445c509d46195aef73e090a49bd6 Deleted: sha256:f975c50357489439eb9145dbfa16bb7cd06c02c31aa4df45c77de4d2baa4e232 Deleted: sha256:0bd983fc698ee9453dd7d21f8572ea1016ec9255346ceabb0f9e173b4348644f Deleted: sha256:08fe90e1a1644431accc00cc80f519f4628dbf06a653c76800b116d3333d2b6d Deleted: sha256:5dc5eef2b94edd185b4d39586e7beb385a54b6bac05d165c9d47494492448235 Deleted: sha256:14a40a140881d18382e13b37588b3aa70097bb4f3fb44085bc95663bdc68fe20 Deleted: sha256:a94e0d5a7c404d0e6fa15d8cd4010e69663bd8813b5117fbad71365a73656df9
10.2 批量刪除
用 docker image ls 命令來配合批量刪除本地鏡像。
docker image rm $(docker image ls -q redis) # 刪除所有倉庫名為 redis 的鏡像 docker image rm $(docker image ls -q -f before=mongo:3.2) # 刪除所有在 mongo:3.2 之前的鏡像
示例:
[@sjs_123_183 ~]# docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE nginx latest c5c4e8fa2cf7 2 weeks ago 109MB redis latest c5355f8853e4 3 weeks ago 107MB redis 3.2 b05c3d76c8b3 5 weeks ago 99.7MB hello-world latest f2a91732366c 5 months ago 1.85kB [@sjs_123_183 ~]# docker image rm $(docker image ls -q redis) Untagged: redis:latest Untagged: redis@sha256:6b9f935e89af002225c0dcdadf1fd74245b4cc1e3e91222f7e4769c236cf80d4 Deleted: sha256:c5355f8853e4174a55144edfec23ac37f5bb2200ed838dab53c13c7cc835ab15 Deleted: sha256:65f4f6d6a5f66e424046ccfe359ba5668f2905ec688a8ca62079dfd7946bb989 Deleted: sha256:3f490bf3919344e7ba4acd58824bd24a9479374d6b1d67b59bab7cda118572cf Deleted: sha256:678c0094f27aa1100c30946b9d55981537a107639e71527b5f54ff3de54c12d7 Untagged: redis:3.2 Untagged: redis@sha256:fd310669f5f1be3d96cfe560264cacd5067d6b03b35764b608ada2db98be6208 Deleted: sha256:b05c3d76c8b3ef3af8974edda3941a4a028d244681cc420e5304a3829519f1b6 Deleted: sha256:c7310f74bc14105a3f09a921aeec7feb031f70a95be81ceacc42690efcdc146d Deleted: sha256:7d7786b85eeb28eba62636c3088e535ddbe1640a9632aab550f9a58c0c011faa Deleted: sha256:635b643d26091e3396661ab3b2e08d59110d5ecec236eb100168ced03cf7630b Deleted: sha256:05ffc1bd56d27d29a8a9c36c77dca93ba19dabc77adaf9d3a1ff44c760c3690d Deleted: sha256:1df131a973126fb31c35ca41b2071da4bffcbed7ab8456635b699772e9ad0d8b Deleted: sha256:43efe85a991cac5894f91ee8f45b328bbacd14966d89a8a00b0d06060c64b5ad [@sjs_123_183 ~]# docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE nginx latest c5c4e8fa2cf7 2 weeks ago 109MB hello-world latest f2a91732366c 5 months ago 1.85kB