實用 Docker 速查大全:快速認識 Docker 的概念和使用方法

Wi1dcard發表於2019-01-24

譯者序:前幾天在社群看到該 倉庫,中文版本翻譯比較亂,部分資料也比較老舊和落後,於是自己動手豐衣足食,在通讀整篇的同時對翻譯和原文進行大量的改進,便產出了本文。我已提交 PR,目前已經合併。轉載至 Laravel-China 只發布中文版本,英文版本歡迎到 GitHub 倉庫檢視。
原作者倉庫:https://github.com/wsargent/docker-cheat-s...(已合併)
改進後的 Fork 倉庫:https://github.com/wi1dcard/docker-cheat-s...(中文版本位於 zh-CN 目錄)
Pull Request:https://github.com/wsargent/docker-cheat-s...

歡迎提出任何疑問和改進,我將同步到 GitHub,謝謝。

想要一起來完善這份速查表嗎?參見貢獻手冊部分吧!

譯者注:以下部分連結需科學上網後使用。

Due to GFW, varies links below could not be accessed in China Mainland.

目錄

為何使用 Docker

「通過 Docker,開發者可以使用任何語言任何工具建立任何應用。“Dockerized” 的應用是完全可移植的,能在任何地方執行 - 不管是同事的 OS X 和 Windows 筆記本,或是在雲端執行的 Ubuntu QA 服務,還是在虛擬機器執行的 Red Hat 產品資料中心。

Docker Hub 上有 13000+ 的應用,開發者可以從中選取一個進行快速擴充套件開發。Docker 跟蹤管理變更和依賴關係,讓系統管理員能更容易理解開發人員是如何讓應用運轉起來的。而開發者可以通過 Docker Hub 的共有/私有倉庫,構建他們的自動化編譯,與其他合作者共享成果。

Docker 幫助開發者更快地構建和釋出高質量的應用。」—— 什麼是 Docker

系統環境

我用的是 Oh My ZshDocker 外掛,它可以自動補全 Docker 命令。你的環境可能有所不同。

Linux

Docker 對於 Linux 核心版本的 最低要求3.10.x

MacOS

10.8「Mountain Lion」或更新版本。

檢查版本

時刻關注你當前正在使用的 Docker 版本是十分重要的,這能夠幫助你瞭解可用的特性。同時,可以讓你在查詢映象時選擇使用的版本。接下來讓我們看看如何操作。

獲取 Docker 服務版本:

docker version --format '{{.Server.Version}}'

你也可以輸出原始的 JSON 資料:

docker version --format '{{json .}}'

安裝

Linux

Docker 官方提供了快速、易用的安裝指令碼:

curl -sSL https://get.docker.com/ | sh

如果你不想執行一個不明不白的 Shell 指令碼,那麼請看 安裝說明,選擇你在用的發行版本。

如果你是一個 Docker 超新手,那麼你應當先去看看 系列教程

macOS

下載並安裝 Docker Community Edition。如果你在使用 Homebrew-Cask,只需在命令列輸入 brew cask install docker 即可。下載安裝 Docker Toolbox 亦可。Docker For Mac 很贊,但是它的安裝過程與 VirtualBox 不太一樣。詳情請查閱 比較

注意:Docker Toolbox 已經過時。你應當使用 Docker Community Edition,詳見 Docker Toolbox

安裝好 Docker Community Edition 後,點選 Launchpad 內的 Docker 圖示。接著即可啟動容器了:

docker run hello-world

好了,現在你有了一個執行中的 Docker 容器了。

容器(Container)

關於 Docker 程式隔離的基礎。容器 (Container) 之於虛擬機器 (Virtual Machine) 就好比執行緒之於程式。或者你可以把他們想成是「吃了類固醇的 chroots」。

生命週期

通常情況下,不使用任何命令列選項啟動一個容器,該容器將會立即啟動並停止。若需保持其執行,你可以使用 docker run -td container_id 命令。選項 -t 表示分配一個 pseudo-TTY 會話,-d 表示自動將容器與終端分離(也就是說在後臺執行容器,並輸出容器 ID)。

如果你需要一個臨時容器,可使用 docker run --rm 會在容器停止之後刪除它。

如果你需要對映宿主機 (host) 的目錄到 Docker 容器內,可使用 docker run -v $HOSTDIR:$DOCKERDIR。詳見 卷標(Volumes) 一節。

如果你想同時刪除與容器相關聯的卷標,那麼在刪除容器的時候必須包含 -v 選項,像這樣 docker rm -v

從 Docker 1.10 起,其內建一套各容器獨立的 日誌引擎,每個容器可以獨立使用。你可以使用 docker run --log-driver=syslog 來自定義日誌引擎(例如以上的 syslog)。

啟動和停止

如果你想將容器的埠 (ports) 暴露至宿主機,請見 暴露埠 一節。

關於 Docker 例項崩潰後的重啟策略,詳見 本文

CPU 限制

你可以限制 CPU 資源佔用,無論是指定百分比,或是特定核心數。

例如,你可以設定 cpu-shares。該配置看起來有點奇怪 -- 1024 表示 100% CPU,因此如果你希望容器使用所有 CPU 核心的 50%,應將其設定為 512:

docker run -ti --c 512 agileek/cpuset-test

更多資訊請參閱 https://goldmann.pl/blog/2014/09/11/resour...

通過 cpuset-cpus 可使用特定 CPU 核心。

docker run -ti --cpuset-cpus=0,4,6 agileek/cpuset-test

請參閱 https://agileek.github.io/docker/2014/08/0... 獲取更多細節以及一些不錯的視訊。

注意,Docker 在容器內仍然能夠 看到 全部 CPU -- 它僅僅是不使用全部而已。請參閱 https://github.com/docker/docker/issues/20... 獲取更多細節。

記憶體限制

同樣,亦可給 Docker 設定 記憶體限制

docker run -it -m 300M ubuntu:14.04 /bin/bash

能力(Capabilities)

Linux 的 Capability 可以通過使用 cap-addcap-drop 設定。請參閱 https://docs.docker.com/engine/reference/r... 獲取更多細節。這有助於提高安全性。

如需要掛載基於 FUSE 的檔案系統,你需要結合 --cap-add--device 使用:

docker run --rm -it --cap-add SYS_ADMIN --device /dev/fuse sshfs

授予對某個裝置的訪問許可權:

docker run -it --device=/dev/ttyUSB0 debian bash

授予對所有裝置的訪問許可權:

docker run -it --privileged -v /dev/bus/usb:/dev/bus/usb debian bash

有關容器特權的更多資訊請參閱 本文

資訊

  • docker ps 檢視執行中的所有容器。
  • docker logs 從容器中讀取日誌。(你也可以使用自定義日誌驅動,不過在 1.10 中,它只支援 json-filejournald)。
  • docker inspect 檢視某個容器的所有資訊(包括 IP 地址)。
  • docker events 從容器中獲取事件 (events)。
  • docker port 檢視容器的公開埠。
  • docker top 檢視容器中活動程式。
  • docker stats 檢視容器的資源使用量統計資訊。
  • docker diff 檢視容器檔案系統中存在改動的檔案。

docker ps -a 將顯示所有容器,包括執行中和已停止的。

docker stats --all 同樣將顯示所有容器,預設僅顯示執行中的容器。

匯入 / 匯出

  • docker cp 在容器和本地檔案系統之間複製檔案或目錄。
  • docker export 將容器的檔案系統打包為歸檔檔案流 (tarball archive stream) 並輸出至標準輸出 (STDOUT)。

執行命令

例如,進入正在執行的 foo 容器,並連線 (attach) 到一個新的 Shell 程式:docker exec -it foo /bin/bash

映象(Images)

映象是 Docker 容器的模板

生命週期

  • docker images 檢視所有映象。
  • docker import 從歸檔檔案建立映象。
  • docker build 從 Dockerfile 建立映象。
  • docker commit 為容器建立映象,如果容器正在執行則會臨時暫停。
  • docker rmi 刪除映象。
  • docker load 從標準輸入 (STDIN) 載入歸檔包 (tar archive) 作為映象,包括映象本身和標籤 (tags, 0.7 起)。
  • docker save 將映象打包為歸檔包,並輸出至標準輸出 (STDOUT),包括所有的父層、標籤和版本 (parent layers, tags, versions, 0.7 起)。

其它資訊

清理

雖然你可以用 docker rmi 命令來刪除指定的映象,不過有個名為 docker-gc 的工具,它可以以一種安全的方式,清理掉那些不再被任何容器使用的映象。Docker 1.13 起,使用 docker image prune 亦可刪除未使用的映象。參見 清理

載入 / 儲存映象

從檔案中載入映象:

docker load < my_image.tar.gz

儲存既有映象:

docker save my_image:my_tag | gzip > my_image.tar.gz

匯入 / 匯出容器

從檔案中匯入容器映象:

cat my_container.tar.gz | docker import - my_image:my_tag

匯出既有容器:

docker export my_container | gzip > my_container.tar.gz

載入已儲存的映象 與 匯入已匯出為映象的容器 的不同

通過 load 命令來載入映象,會建立一個新的映象,並繼承原映象的所有歷史。
通過 import 將容器作為映象匯入,也會建立一個新的映象,但並不包含原映象的歷史,因此會比使用 load 方式生成的映象更小。

網路(Networks)

Docker 具備 網路 功能。我並不是很瞭解它,所以這是一個擴充套件本文的好地方。文件 使用網路 指出,這是一種無需暴露埠即可實現 Docker 容器間通訊的好方法。

生命週期

其它資訊

建立連線

你可以 為容器指定 IP 地址

# 使用你自己的子網和閘道器建立一個橋接網路
docker network create --subnet 203.0.113.0/24 --gateway 203.0.113.254 iptastic

# 基於以上建立的網路,執行一個 Nginx 容器並指定 IP
$ docker run --rm -it --net iptastic --ip 203.0.113.2 nginx

# 在其他地方使用 CURL 訪問這個 IP(假設該 IP 為公網)
$ curl 203.0.113.2

倉管中心和倉庫(Registry & Repository)

倉庫 (repository) 是 被託管(hosted) 的已命名映象 (tagged images) 的集合,這組映象用於構建容器檔案系統。

倉管中心 (registry) 則是 託管服務(host) -- 用於儲存倉庫並提供 HTTP API,以便 管理倉庫的上傳和下載

Docker 官方託管著自己的 倉管中心,包含著數量眾多的倉庫。不過話雖如此,這個倉管中心 並沒有很好地驗證映象,所以如果你擔心安全問題的話,請儘量避免使用它。

本地倉管中心

你可以使用 docker distribution 專案搭建本地的倉管中心,詳情參閱 本地釋出 (local deploy) 的介紹。

科學上網後,也可以看看 Google+ Group

Dockerfile

當你執行 docker build 時,Docker 將會根據 配置檔案 啟動 Docker 容器。遠優於使用 docker commit

以下是一些編寫 Dockerfile 的常用編輯器,並連結到適配的語法高亮模組︰

指令

  • .dockerignore
  • FROM 為其他指令設定基礎映象 (Base Image)。
  • MAINTAINER (deprecated - use LABEL instead) 為生成的映象設定作者欄位。
  • RUN 在當前映象的基礎上生成一個新層並執行命令。
  • CMD 設定容器預設執行命令。
  • EXPOSE 告知 Docker 容器在執行時所要監聽的網路埠。注意:並沒有實際上將埠設定為可訪問。
  • ENV 設定環境變數。
  • ADD 將檔案、目錄或遠端檔案複製到容器中。快取無效。請儘量用 COPY 代替 ADD
  • COPY 將檔案或資料夾複製到容器中。注意:將使用 ROOT 使用者複製檔案,故無論 USER / WORKDIR 指令如何配置,你都需要手動修改其所有者(chown),ADD 也是一樣。
  • ENTRYPOINT 將容器設為可執行的。
  • VOLUME 在容器內部建立掛載點 (mount point) 指向外部掛載的卷標或其他容器。
  • USER 設定隨後執行 RUN / CMD / ENTRYPOINT 命令的使用者名稱。
  • WORKDIR 設定工作目錄 (working directory)。
  • ARG 定義編譯時 (build-time) 變數。
  • ONBUILD 新增觸發指令,當該映象被作為其他映象的基礎映象時該指令會被觸發。
  • STOPSIGNAL 設定停止容器時,向容器內傳送的系統呼叫訊號 (system call signal)。
  • LABEL 將鍵值對後設資料 (key/value metadata) 應用到映象、容器或是守護程式。

教程

例子

層(Layers)

Docker 的版本化檔案系統是基於層的。就像 Git 的提交或檔案變更系統 一樣。

連結(Links)

連結 (links) 通過 TCP/IP 埠 實現 Docker 容器之間的通訊。Atlassian 展示了可用的例子。你還可以 通過主機名 (hostname) 連結

在某種意義上來說,該特性已經被 自定義網路 所替代。

注意: 如果你希望容器之間通過連結進行通訊,在啟動 Docker 守護程式時,請使用 -icc=false 來禁用內部程式通訊。

假設你有一個名為 CONTAINER 的容器(通過 docker run --name CONTAINER 指定)並且在 Dockerfile 中,暴露了一個埠:

EXPOSE 1337

然後,我們建立另外一個名為 LINKED 的容器:

docker run -d --link CONTAINER:ALIAS --name LINKED user/wordpress

然後 CONTAINER 暴露的埠和別名將會以如下的環境變數出現在 LINKED 中:

$ALIAS_PORT_1337_TCP_PORT
$ALIAS_PORT_1337_TCP_ADDR

那麼你便可以通過這種方式來連線它了。

使用 docker rm --link 即可刪除連結。

通常,Docker 容器(亦可理解為「服務」)之間的連結,是「服務發現」的一個子集。如果你打算在生產中大規模使用 Docker,這將是一個很大的問題。請參閱The Docker Ecosystem: Service Discovery and Distributed Configuration Stores 獲取更多資訊。

卷標(Volumes)

Docker 的卷標 (volumes) 是 獨立的檔案系統。它們並非必須連線到特定的容器上。

生命週期

資訊

卷標在不能使用連結(只有 TCP/IP)的情況下非常有用。例如,如果你有兩個 Docker 例項需要通訊並在檔案系統上留下記錄。

你可以一次性將其掛載到多個 docker 容器上,通過 docker run --volumes-from

因為卷標是獨立的檔案系統,它們通常被用於儲存各容器之間的瞬時狀態。也就是說,你可以配置一個無狀態臨時容器,關掉之後,當你有第二個這種臨時容器例項的時候,你可以從上一次儲存的狀態繼續執行。

檢視 卷標進階 來獲取更多細節。Container42 非常有用。

你可以 將宿主 MacOS 的資料夾對映為 Docker 卷標

docker run -v /Users/wsargent/myapp/src:/src

你也可以用遠端 NFS 卷標,如果你覺得你 有足夠勇氣

還可以考慮執行一個純資料容器,像 這裡 所說的那樣,提供可移植資料。

記得,檔案也可以被掛載為卷標

暴露埠(Exposing ports)

通過宿主容器暴露輸入埠相當 繁瑣但有效的

例如使用 -p 將容器埠對映到宿主埠上(只使用本地主機 (localhost) 介面):

docker run -p 127.0.0.1:$HOSTPORT:$CONTAINERPORT --name CONTAINER -t someimage

你可以使用 EXPOSE 告知 Docker,該容器在執行時監聽指定的埠:

EXPOSE <CONTAINERPORT>

但是注意 EXPOSE 並不會直接暴露埠,你需要用引數 -p 。比如說你要在 localhost 上暴露容器的埠:

iptables -t nat -A DOCKER -p tcp --dport <LOCALHOSTPORT> -j DNAT --to-destination <CONTAINERIP>:<PORT>

如果你是在 Virtualbox 中執行 Docker,那麼你需要配置埠轉發 (forward the port)。使用 forwarded_port 在 Vagrantfile 上配置暴露的埠範圍,這樣你就可以動態地對映了:

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  ...

  (49000..49900).each do |port|
    config.vm.network :forwarded_port, :host => port, :guest => port
  end

  ...
end

如果你忘記了將什麼埠對映到宿主機上的話,可使用 docker port 檢視:

docker port CONTAINER $CONTAINERPORT

最佳實踐

這裡有一些最佳實踐,以及爭論焦點:

安全(Security)

這節準備討論一些關於 Docker 安全性的問題。Docker 官方文件 安全 頁面講述了更多細節。

首先第一件事:Docker 是有 root 許可權的。如果你在 docker 組,那麼你就有 root 許可權。如果你將 Docker 的 Unix Socket 暴露給容器,意味著你賦予了容器 宿主機 root 許可權

Docker 不應當作為唯一的防禦措施。你應當使其更加安全可靠。

為了更好地理解容器暴露了什麼,可參閱由 Aaron Grattafiori 編寫的 Understanding and Hardening Linux Containers。這是一個完整全面且包含大量連結和腳註的容器問題指南,介紹了許多有用的內容。即使你已經加固過容器,以下的安全提示依然十分有幫助,但並不能代替理解的過程。

安全提示

為了最大的安全性,你應當考慮在虛擬機器上執行 Docker。這是直接從 Docker 安全團隊拿來的資料 -- slides / notes。之後,可使用 AppArmor、seccomp、SELinux、grsec 等來 限制容器的許可權。更多細節,請查閱 Docker 1.10 security features

Docker 映象 ID 屬於 敏感資訊 所以它不應該向外界公開。請將它們當作密碼來對待。

閱讀由 Thomas Sjögren 編寫的 Docker Security Cheat Sheet:關於加固容器的不錯的建議。

檢視 Docker 安全測試指令碼,下載 最佳實踐白皮書

你應當遠離使用非穩定版本 grsecurity / pax 的核心,比如 Alpine Linux。如果在產品中用了 grsecurity,那麼你應該考慮使用有 商業支援穩定版本,就像你對待 RedHat 那樣。雖然要 $200 每月,但對於你的運維預算來說不值一提。

從 Docker 1.11 開始,你可以輕鬆的限制在容器中可用的程式數,以防止 fork 炸彈。 這要求 Linux 核心 >= 4.3,並且要在核心配置中開啟 CGROUP_PIDS=y。

docker run --pids-limit=64

同時,你也可以限制程式再獲取新許可權。該功能是 Linux 核心從 3.5 版本開始就擁有的。你可以從 這篇部落格 中閱讀到更多關於這方面的內容。

docker run --security-opt=no-new-privileges

以下內容摘選自 Container SolutionsDocker Security Cheat Sheet(PDF 版本,難以使用,故複製至此):

關閉內部程式通訊:

docker -d --icc=false --iptables

設定容器為只讀:

docker run --read-only

通過 hashsum 來驗證卷標:

docker pull debian@sha256:a25306f3850e1bd44541976aa7b5fd0a29be

設定卷標為只讀:

docker run -v $(pwd)/secrets:/secrets:ro debian

在 Dockerfile 中定義使用者並以該使用者執行,避免在容器中以 ROOT 身份操作:

RUN groupadd -r user && useradd -r -g user user
USER user

使用者名稱空間(User Namespaces)

還可以通過使用 使用者名稱空間 -- 自 1.10 版本起已內建,但預設並未啟用。

要在 Ubuntu 15.10 中啟用使用者名稱空間 (remap the userns),請 跟著這篇部落格的例子 來做。

安全相關視訊

安全路線圖

Docker 的路線圖提到關於 seccomp 的支援
一個名為 bane 的 AppArmor 策略生成器正在實現 安全配置檔案

小貼士

連結:

清理

最新的 資料管理命令 已在 Docker 1.13 實現:

  • docker system prune
  • docker volume prune
  • docker network prune
  • docker container prune
  • docker image prune

df 命令

docker system df 將顯示當前 Docker 各部分佔用的磁碟空間。

Heredoc 宣告 Docker 容器

docker build -t htop - << EOF
FROM alpine
RUN apk --no-cache add htop
EOF

最近一次的容器 ID

alias dl='docker ps -l -q'
docker run ubuntu echo hello world
docker commit $(dl) helloworld

帶命令的提交(需要 Dockerfile)

docker commit -run='{"Cmd":["postgres", "-too -many -opts"]}' $(dl) postgres

獲取 IP 地址

docker inspect $(dl) | grep -wm1 IPAddress | cut -d '"' -f 4

或使用 jq:

docker inspect $(dl) | jq -r '.[0].NetworkSettings.IPAddress'

或使用 go 模板

docker inspect -f '{{ .NetworkSettings.IPAddress }}' <container_name>

或在通過 Dockerfile 構建映象時,通過構建引數 (build argument) 傳入:

DOCKER_HOST_IP=`ifconfig | grep -E "([0-9]{1,3}\.){3}[0-9]{1,3}" | grep -v 127.0.0.1 | awk '{ print $2 }' | cut -f2 -d: | head -n1`
echo DOCKER_HOST_IP = $DOCKER_HOST_IP
docker build \
  --build-arg ARTIFACTORY_ADDRESS=$DOCKER_HOST_IP 
  -t sometag \
  some-directory/

獲取埠對映

docker inspect -f '{{range $p, $conf := .NetworkSettings.Ports}} {{$p}} -> {{(index $conf 0).HostPort}} {{end}}' <containername>

通過正則匹配容器

for i in $(docker ps -a | grep "REGEXP_PATTERN" | cut -f1 -d" "); do echo $i; done`

獲取環境變數配置

docker run --rm ubuntu env

強行終止執行中的容器

docker kill $(docker ps -q)

刪除所有容器(強行刪除!無論容器執行或停止)

docker rm -f $(docker ps -qa)

刪除舊容器

docker ps -a | grep 'weeks ago' | awk '{print $1}' | xargs docker rm

刪除已停止的容器

docker rm -v `docker ps -a -q -f status=exited`

停止並刪除容器

docker stop $(docker ps -aq) && docker rm -v $(docker ps -aq)

刪除無用 (dangling) 的映象

docker rmi $(docker images -q -f dangling=true)

刪除所有映象

docker rmi $(docker images -q)

刪除無用 (dangling) 的卷標

Docker 1.9 版本起:

docker volume rm $(docker volume ls -q -f dangling=true)

1.9.0 中,引數 dangling=false 居然 用 - 它會被忽略然後列出所有的卷標。

檢視映象依賴

docker images -viz | dot -Tpng -o docker.png

Docker 容器瘦身

  • 在某層 (RUN layer) 清理 APT

這應當和其他 apt 命令在同一層中完成。
否則,前面的層將會保持原有資訊,而你的映象則依舊臃腫。

RUN {apt commands} \
  && apt-get clean \  
  && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
  • 壓縮映象

    ID=$(docker run -d image-name /bin/bash)
    docker export $ID | docker import – flat-image-name
  • 備份

    ID=$(docker run -d image-name /bin/bash)
    (docker export $ID | gzip -c > image.tgz)
    gzip -dc image.tgz | docker import - flat-image-name

監視執行中容器的系統資源利用率

檢查某個容器的 CPU、記憶體以及網路 I/O 使用情況,你可以:

docker stats <container>

按 ID 列出所有容器:

docker stats $(docker ps -q)

按名稱列出所有容器:

docker stats $(docker ps --format '{{.Names}}')

按指定映象名稱列出所有容器:

docker ps -a -f ancestor=ubuntu

刪除所有未標籤命名 (untagged) 的容器:

docker rmi $(docker images | grep “^” | awk '{split($0,a," "); print a[3]}')

通過正則匹配刪除指定容器:

docker ps -a | grep wildfly | awk '{print $1}' | xargs docker rm -f

刪除所有已退出 (exited) 的容器:

docker rm -f $(docker ps -a | grep Exit | awk '{ print $1 }')

將檔案掛載為卷標

檔案也可以被掛載為卷標。例如你可以僅僅注入單個配置檔案:

# 從容器複製檔案
docker run --rm httpd cat /usr/local/apache2/conf/httpd.conf > httpd.conf

# 編輯檔案
vim httpd.conf

# 掛載修改後的配置啟動容器
docker run --rm -ti -v "$PWD/httpd.conf:/usr/local/apache2/conf/httpd.conf:ro" -p "80:80" httpd

我感謝自己平凡,敢愛敢恨沒負擔。
我感謝自己不凡,可愛可恨都包攬。

相關文章