一、Docker 簡介
Docker 兩個主要部件:
Docker: 開源的容器虛拟化平台
Docker Hub: 用于分享、管理 Docker 容器的 Docker SaaS 平台 -- Docker Hub
Docker 使用用戶端-伺服器 (C/S) 架構模式。Docker 用戶端會與 Docker 守護程序進行通信。Docker 守護程序會處理複雜繁重的任務,例如建立、運作、釋出你的 Docker 容器。Docker 用戶端和守護程序可以運作在同一個系統上,當然你也可以使用 Docker 用戶端去連接配接一個遠端的 Docker 守護程序。Docker 用戶端和守護程序之間通過 socket 或者 RESTful API 進行通信。
<a href="http://s5.51cto.com/wyfs02/M00/8C/15/wKioL1hh5aGzLVxtAABRVcsj0UY964.png" target="_blank"></a>
1.1 Docker 守護程序
如上圖所示,Docker 守護程序運作在一台主機上。使用者并不直接和守護程序進行互動,而是通過 Docker 用戶端間接和其通信。
1.2 Docker 用戶端
Docker 用戶端,實際上是 docker 的二進制程式,是主要的使用者與 Docker 互動方式。它接收使用者指令并且與背後的 Docker 守護程序通信,如此來回往複。
1.3 Docker 内部
要了解 Docker 内部建構,需要了解以下三種部件:
Docker 鏡像 - Docker images
Docker 倉庫 - Docker registeries
Docker 容器 - Docker containers
Docker 鏡像
Docker 鏡像是 Docker 容器運作時的隻讀模闆,每一個鏡像由一系列的層 (layers) 組成。Docker 使用 UnionFS 來将這些層聯合到單獨的鏡像中。UnionFS 允許獨立檔案系統中的檔案和檔案夾(稱之為分支)被透明覆寫,形成一個單獨連貫的檔案系統。正因為有了這些層的存在,Docker 是如此的輕量。當你改變了一個 Docker 鏡像,比如更新到某個程式到新的版本,一個新的層會被建立。是以,不用替換整個原先的鏡像或者重建立立(在使用虛拟機的時候你可能會這麼做),隻是一個新 的層被添加或更新了。現在你不用重新釋出整個鏡像,隻需要更新,層使得分發 Docker 鏡像變得簡單和快速。
Docker 倉庫
Docker 倉庫用來儲存鏡像,可以了解為代碼控制中的代碼倉庫。同樣的,Docker 倉庫也有公有和私有的概念。公有的 Docker 倉庫名字是 Docker Hub。Docker Hub 提供了龐大的鏡像集合供使用。這些鏡像可以是自己建立,或者在别人的鏡像基礎上建立。Docker 倉庫是 Docker 的分發部分。
Docker 容器
Docker 容器和檔案夾很類似,一個Docker容器包含了所有的某個應用運作所需要的環境。每一個 Docker 容器都是從 Docker 鏡像建立的。Docker 容器可以運作、開始、停止、移動和删除。每一個 Docker 容器都是獨立和安全的應用平台,Docker 容器是 Docker 的運作部分。
1.4 libcontainer
Docker 從 0.9 版本開始使用 libcontainer 替代 lxc,libcontainer 和 Linux 系統的互動圖如下:
<a href="http://s4.51cto.com/wyfs02/M00/8C/19/wKiom1hh5cmSbM_JAAFZrJcEz4U134.png" target="_blank"></a>
圖檔來源: Docker 0.9: introducing execution drivers and libcontainer
1.5 命名空間「Namespaces」
pid namespace
不同使用者的程序就是通過 pid namespace 隔離開的,且不同 namespace 中可以有相同 PID。具有以下特征:
每個 namespace 中的 pid 是有自己的 pid=1 的程序(類似 /sbin/init 程序)
每個 namespace 中的程序隻能影響自己的同一個 namespace 或子 namespace 中的程序
因為 /proc 包含正在運作的程序,是以在 container 中的 pseudo-filesystem 的 /proc 目錄隻能看到自己 namespace 中的程序
因為 namespace 允許嵌套,父 namespace 可以影響子 namespace 的程序,是以子 namespace 的程序可以在父 namespace 中看到,但是具有不同的 pid
參考文檔:Introduction to Linux namespaces – Part 3: PID
mnt namespace
類似 chroot,将一個程序放到一個特定的目錄執行。mnt namespace 允許不同 namespace 的程序看到的檔案結構不同,這樣每個 namespace 中的程序所看到的檔案目錄就被隔離開了。同 chroot 不同,每個 namespace 中的 container 在 /proc/mounts 的資訊隻包含所在 namespace 的 mount point。
net namespace
網絡隔離是通過 net namespace 實作的, 每個 net namespace 有獨立的 network devices, IP addresses, IP routing tables, /proc/net 目錄。這樣每個 container 的網絡就能隔離開來。 docker 預設采用 veth 的方式将 container 中的虛拟網卡同 host 上的一個 docker bridge 連接配接在一起。
參考文檔:Introduction to Linux namespaces – Part 5: NET
uts namespace
UTS ("UNIX Time-sharing System") namespace 允許每個 container 擁有獨立的 hostname 和 domain name, 使其在網絡上可以被視作一個獨立的節點而非 Host 上的一個程序。
參考文檔:Introduction to Linux namespaces – Part 1: UTS
ipc namespace
container 中程序互動還是采用 Linux 常見的程序間互動方法 (interprocess communication - IPC), 包括常見的信号量、消息隊列和共享記憶體。然而同 VM 不同,container 的程序間互動實際上還是 host 上具有相同 pid namespace 中的程序間互動,是以需要在IPC資源申請時加入 namespace 資訊 - 每個 IPC 資源有一個唯一的 32bit ID。
參考文檔:Introduction to Linux namespaces – Part 2: IPC
user namespace
每個 container 可以有不同的 user 和 group id, 也就是說可以以 container 内部的使用者在 container 内部執行程式而非 Host 上的使用者。
有了以上 6 種 namespace 從程序、網絡、IPC、檔案系統、UTS 和使用者角度的隔離,一個 container 就可以對外展現出一個獨立計算機的能力,并且不同 container 從 OS 層面實作了隔離。 然而不同 namespace 之間資源還是互相競争的,仍然需要類似 ulimit 來管理每個 container 所能使用的資源 - cgroup。
Reference
Docker Getting Start: Related Knowledge
Docker 介紹以及其相關術語、底層原理和技術
1.6 資源配額「cgroups」
cgroups 實作了對資源的配額和度量。 cgroups 的使用非常簡單,提供類似檔案的接口,在 /cgroup 目錄下建立一個檔案夾即可建立一個 group,在此檔案夾中建立 task 檔案,并将 pid 寫入該檔案,即可實作對該程序的資源控制。具體的資源配置選項可以在該檔案夾中建立子 subsystem ,{子系統字首}.{資源項} 是典型的配置方法, 如 memory.usageinbytes 就定義了該 group 在 subsystem memory 中的一個記憶體限制選項。 另外,cgroups 中的 subsystem 可以随意組合,一個 subsystem 可以在不同的 group 中,也可以一個 group 包含多個 subsystem - 也就是說一個 subsystem。
memory
記憶體相關的限制
cpu
在 cgroup 中,并不能像硬體虛拟化方案一樣能夠定義 CPU 能力,但是能夠定義 CPU 輪轉的優先級,是以具有較高 CPU 優先級的程序會更可能得到 CPU 運算。 通過将參數寫入 cpu.shares ,即可定義改 cgroup 的 CPU 優先級 - 這裡是一個相對權重,而非絕對值
blkio
block IO 相關的統計和限制,byte/operation 統計和限制 (IOPS 等),讀寫速度限制等,但是這裡主要統計的都是同步 IO
devices
裝置權限限制
參考文檔:how to use cgroup
二、Docker 安裝
docker 的相關安裝方法這裡不作介紹,具體安裝參考 官檔
擷取目前 docker 版本
$ sudo docker version
Client version: 1.3.2
Client API version: 1.15
Go version (client): go1.3.3
Git commit (client): 39fa2fa/1.3.2
OS/Arch (client): linux/amd64
Server version: 1.3.2
Server API version: 1.15
Go version (server): go1.3.3
Git commit (server): 39fa2fa/1.3.2
三、Docker 基礎用法
Docker HUB : Docker鏡像首頁,包括官方鏡像和其它公開鏡像
因為國情的原因,國内下載下傳 Docker HUB 官方的相關鏡像比較慢,可以使用 docker.cn 鏡像,鏡像保持和官方一緻,關鍵是速度塊,推薦使用。
3.1 Search images
$ sudo docker search ubuntu
3.2 Pull images
$ sudo docker pull ubuntu # 擷取 ubuntu 官方鏡像 $ sudo docker images # 檢視目前鏡像清單
3.3 Running an interactive shell
$ sudo docker run -i -t ubuntu:14.04 /bin/bash
docker run - 運作一個容器
-t - 配置設定一個(僞)tty (link is external)
-i - 互動模式 (so we can interact with it)
ubuntu:14.04 - 使用 ubuntu 基礎鏡像 14.04
/bin/bash - 運作指令 bash shell
注: ubuntu 會有多個版本,通過指定 tag 來啟動特定的版本 [image]:[tag]
$ sudo docker ps # 檢視目前運作的容器, ps -a 列出目前系統所有的容器 CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6c9129e9df10 ubuntu:14.04 /bin/bash 6 minutes ago Up 6 minutes cranky_babbage
3.4 相關快捷鍵
退出:Ctrl-Dorexit
detach:Ctrl-P + Ctrl-Q
attach:docker attach CONTAINER-ID
四、Docker 指令幫助
4.1 docker help
docker command
$ sudo docker # docker 指令幫助
Commands:
attach Attach to a running container # 目前 shell 下 attach 連接配接指定運作鏡像
build Build an image from a Dockerfile # 通過 Dockerfile 定制鏡像
commit Create a new image from a container's changes # 送出目前容器為新的鏡像
cp Copy files/folders from the containers filesystem to the host path
# 從容器中拷貝指定檔案或者目錄到主控端中
create Create a new container # 建立一個新的容器,同 run,但不啟動容器
diff Inspect changes on a container's filesystem # 檢視 docker 容器變化
events Get real time events from the server # 從 docker 服務擷取容器實時事件
exec Run a command in an existing container # 在已存在的容器上運作指令
export Stream the contents of a container as a tar archive
# 導出容器的内容流作為一個 tar 歸檔檔案[對應 import ]
history Show the history of an image # 展示一個鏡像形成曆史
images List images # 列出系統目前鏡像
import Create a new filesystem image from the contents of a tarball
# 從tar包中的内容建立一個新的檔案系統映像[對應 export]
info Display system-wide information # 顯示系統相關資訊
inspect Return low-level information on a container # 檢視容器詳細資訊
kill Kill a running container # kill 指定 docker 容器
load Load an image from a tar archive # 從一個 tar 包中加載一個鏡像[對應 save]
login Register or Login to the docker registry server
# 注冊或者登陸一個 docker 源伺服器
logout Log out from a Docker registry server # 從目前 Docker registry 退出
logs Fetch the logs of a container # 輸出目前容器日志資訊
port Lookup the public-facing port which is NAT-ed to PRIVATE_PORT
# 檢視映射端口對應的容器内部源端口
pause Pause all processes within a container # 暫停容器
ps List containers # 列出容器清單
pull Pull an image or a repository from the docker registry server
# 從docker鏡像源伺服器拉取指定鏡像或者庫鏡像
push Push an image or a repository to the docker registry server
# 推送指定鏡像或者庫鏡像至docker源伺服器
restart Restart a running container # 重新開機運作的容器
rm Remove one or more containers # 移除一個或者多個容器
rmi Remove one or more images
# 移除一個或多個鏡像[無容器使用該鏡像才可删除,否則需删除相關容器才可繼續或 -f 強制删除]
run Run a command in a new container
# 建立一個新的容器并運作一個指令
save Save an image to a tar archive # 儲存一個鏡像為一個 tar 包[對應 load]
search Search for an image on the Docker Hub # 在 docker hub 中搜尋鏡像
start Start a stopped containers # 啟動容器
stop Stop a running containers # 停止容器
tag Tag an image into a repository # 給源中鏡像打标簽
top Lookup the running processes of a container # 檢視容器中運作的程序資訊
unpause Unpause a paused container # 取消暫停容器
version Show the docker version information # 檢視 docker 版本号
wait Block until a container stops, then print its exit code
# 截取容器停止時的退出狀态值
Run 'docker COMMAND --help' for more information on a command.
docker option
Usage of docker:
--api-enable-cors=false Enable CORS headers in the remote API # 遠端 API 中開啟 CORS 頭
-b, --bridge="" Attach containers to a pre-existing network bridge # 橋接網絡
use 'none' to disable container networking
--bip="" Use this CIDR notation address for the network bridge's IP, not compatible with -b
# 和 -b 選項不相容,具體沒有測試過
-d, --daemon=false Enable daemon mode # daemon 模式
-D, --debug=false Enable debug mode # debug 模式
--dns=[] Force docker to use specific DNS servers # 強制 docker 使用指定 dns 伺服器
--dns-search=[] Force Docker to use specific DNS search domains # 強制 docker 使用指定 dns 搜尋域
-e, --exec-driver="native" Force the docker runtime to use a specific exec driver # 強制 docker 運作時使用指定執行驅動器
--fixed-cidr="" IPv4 subnet for fixed IPs (ex: 10.20.0.0/16)
this subnet must be nested in the bridge subnet (which is defined by -b or --bip)
-G, --group="docker" Group to assign the unix socket specified by -H when running in daemon mode
use '' (the empty string) to disable setting of a group
-g, --graph="/var/lib/docker" Path to use as the root of the docker runtime # 容器運作的根目錄路徑
-H, --host=[] The socket(s) to bind to in daemon mode # daemon 模式下 docker 指定綁定方式[tcp or 本地 socket]
specified using one or more tcp://host:port, unix:///path/to/socket, fd://* or fd://socketfd.
--icc=true Enable inter-container communication # 跨容器通信
--insecure-registry=[] Enable insecure communication with specified registries (no certificate verification for HTTPS and enable HTTP fallback) (e.g., localhost:5000 or 10.20.0.0/16)
--ip="0.0.0.0" Default IP address to use when binding container ports # 指定監聽位址,預設所有 ip
--ip-forward=true Enable net.ipv4.ip_forward # 開啟轉發
--ip-masq=true Enable IP masquerading for bridge's IP range
--iptables=true Enable Docker's addition of iptables rules # 添加對應 iptables 規則
--mtu=0 Set the containers network MTU # 設定網絡 mtu
if no value is provided: default to the default route MTU or 1500 if no default route is available
-p, --pidfile="/var/run/docker.pid" Path to use for daemon PID file # 指定 pid 檔案位置
--registry-mirror=[] Specify a preferred Docker registry mirror
-s, --storage-driver="" Force the docker runtime to use a specific storage driver # 強制 docker 運作時使用指定存儲驅動
--selinux-enabled=false Enable selinux support # 開啟 selinux 支援
--storage-opt=[] Set storage driver options # 設定存儲驅動選項
--tls=false Use TLS; implied by tls-verify flags # 開啟 tls
--tlscacert="/root/.docker/ca.pem" Trust only remotes providing a certificate signed by the CA given here
--tlscert="/root/.docker/cert.pem" Path to TLS certificate file # tls 證書檔案位置
--tlskey="/root/.docker/key.pem" Path to TLS key file # tls key 檔案位置
--tlsverify=false Use TLS and verify the remote (daemon: verify client, client: verify daemon) # 使用 tls 并确認遠端控制主機
-v, --version=false Print version information and quit # 輸出 docker 版本資訊
4.2 docker search
$ sudo docker search --help
Usage: docker search TERM
Search the Docker Hub for images # 從 Docker Hub 搜尋鏡像 --automated=false Only show automated builds
--no-trunc=false Don't truncate output
-s, --stars=0 Only displays with at least xxx stars
示例:
$ sudo docker search -s 100 ubuntu # 查找 star 數至少為 100 的鏡像,找出隻有官方鏡像 start 數超過 100,預設不加 s 選項找出所有相關 ubuntu 鏡像 NAME DESCRIPTION STARS OFFICIAL AUTOMATED
ubuntu Official Ubuntu base image 425 [OK]
4.3 docker info
$ sudo docker info
Containers: 1 # 容器個數 Images: 22 # 鏡像個數 Storage Driver: devicemapper # 存儲驅動 Pool Name: docker-8:17-3221225728-pool
Pool Blocksize: 65.54 kB
Data file: /data/docker/devicemapper/devicemapper/data
Metadata file: /data/docker/devicemapper/devicemapper/metadata
Data Space Used: 1.83 GB
Data Space Total: 107.4 GB
Metadata Space Used: 2.191 MB
Metadata Space Total: 2.147 GB
Library Version: 1.02.84-RHEL7 (2014-03-26) Execution Driver: native-0.2 # 存儲驅動 Kernel Version: 3.10.0-123.el7.x86_64
Operating System: CentOS Linux 7 (Core)
4.4 docker pull && docker push
$ sudo docker pull --help # pull 拉取鏡像 Usage: docker pull [OPTIONS] NAME[:TAG] Pull an image or a repository from the registry
-a, --all-tags=false Download all tagged images in the repository $ sudo docker push # push 推送指定鏡像 Usage: docker push NAME[:TAG] Push an image or a repository to the registry
$ sudo docker pull ubuntu # 下載下傳官方 ubuntu docker 鏡像,預設下載下傳所有 ubuntu 官方庫鏡像 $ sudo docker pull ubuntu:14.04 # 下載下傳指定版本 ubuntu 官方鏡像
$ sudo docker push 192.168.0.100:5000/ubuntu # 推送鏡像庫到私有源[可注冊 docker 官方賬戶,推送到官方自有賬戶] $ sudo docker push 192.168.0.100:5000/ubuntu:14.04 # 推送指定鏡像到私有源
4.5 docker images
列出目前系統鏡像
$ sudo docker images --help
Usage: docker images [OPTIONS] [NAME] List images
-a, --all=false Show all images (by default filter out the intermediate image layers) # -a 顯示目前系統的所有鏡像,包括過渡層鏡像,預設 docker images 顯示最終鏡像,不包括過渡層鏡像 -f, --filter=[] Provide filter values (i.e. 'dangling=true') --no-trunc=false Don't truncate output
-q, --quiet=false Only show numeric IDs
$ sudo docker images # 顯示目前系統鏡像,不包括過渡層鏡像 $ sudo docker images -a # 顯示目前系統所有鏡像,包括過渡層鏡像 $ sudo docker images ubuntu # 顯示目前系統 docker ubuntu 庫中的所有鏡像 REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
ubuntu 12.04 ebe4be4dd427 4 weeks ago 210.6 MB
ubuntu 14.04 e54ca5efa2e9 4 weeks ago 276.5 MB
ubuntu 14.04-ssh 6334d3ac099a 7 weeks ago 383.2 MB
4.6 docker rmi
删除一個或者多個鏡像
$ sudo docker rmi --help
Usage: docker rmi IMAGE [IMAGE...] Remove one or more images
-f, --force=false Force removal of the image # 強制移除鏡像不管是否有容器使用該鏡像 --no-prune=false Do not delete untagged parents # 不要删除未标記的父鏡像
4.7 docker run
$ sudo docker run --help
Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...] Run a command in a new container
-a, --attach=[] Attach to stdin, stdout or stderr.
-c, --cpu-shares=0 CPU shares (relative weight) # 設定 cpu 使用權重 --cap-add=[] Add Linux capabilities
--cap-drop=[] Drop Linux capabilities
--cidfile="" Write the container ID to the file # 把容器 id 寫入到指定檔案 --cpuset="" CPUs in which to allow execution (0-3, 0,1) # cpu 綁定 -d, --detach=false Detached mode: Run container in the background, print new container id # 背景運作容器 --device=[] Add a host device to the container (e.g. --device=/dev/sdc:/dev/xvdc) --dns=[] Set custom dns servers # 設定 dns --dns-search=[] Set custom dns search domains # 設定 dns 域搜尋 -e, --env=[] Set environment variables # 定義環境變量 --entrypoint="" Overwrite the default entrypoint of the image # ? --env-file=[] Read in a line delimited file of ENV variables # 從指定檔案讀取變量值 --expose=[] Expose a port from the container without publishing it to your host # 指定對外提供服務端口 -h, --hostname="" Container host name # 設定容器主機名 -i, --interactive=false Keep stdin open even if not attached # 保持标準輸出開啟即使沒有 attached --link=[] Add link to another container (name:alias) # 添加連結到另外一個容器 --lxc-conf=[] (lxc exec-driver only) Add custom lxc options --lxc-conf="lxc.cgroup.cpuset.cpus = 0,1" -m, --memory="" Memory limit (format: <number><optional unit>, where unit = b, k, m or g) # 記憶體限制 --name="" Assign a name to the container # 設定容器名 --net="bridge" Set the Network mode for the container # 設定容器網絡模式 'bridge': creates a new network stack for the container on the docker bridge 'none': no networking for this container 'container:<name|id>': reuses another container network stack 'host': use the host network stack inside the container. Note: the host mode gives the container full access to local system services such as D-bus and is therefore considered insecure.
-P, --publish-all=false Publish all exposed ports to the host interfaces # 自動映射容器對外提供服務的端口 -p, --publish=[] Publish a container's port to the host # 指定端口映射 format: ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort (use 'docker port' to see the actual mapping) --privileged=false Give extended privileges to this container # 提供更多的權限給容器 --restart="" Restart policy to apply when a container exits (no, on-failure[:max-retry], always) --rm=false Automatically remove the container when it exits (incompatible with -d) # 如果容器退出自動移除和 -d 選項沖突 --security-opt=[] Security Options
--sig-proxy=true Proxify received signals to the process (even in non-tty mode). SIGCHLD is not proxied.
-t, --tty=false Allocate a pseudo-tty # 配置設定僞終端 -u, --user="" Username or UID # 指定運作容器的使用者 uid 或者使用者名 -v, --volume=[] Bind mount a volume (e.g., from the host: -v /host:/container, from docker: -v /container) # 挂載卷 --volumes-from=[] Mount volumes from the specified container(s) # 從指定容器挂載卷 -w, --workdir="" Working directory inside the container # 指定容器工作目錄
$ sudo docker images ubuntu
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
ubuntu 14.04 e54ca5efa2e9 4 weeks ago 276.5 MB
... ... $ sudo docker run -t -i -c 100 -m 512MB -h test1 -d --name="docker_test1" ubuntu /bin/bash # 建立一個 cpu 優先級為 100,記憶體限制 512MB,主機名為 test1,名為 docker_test1 背景運作 bash 的容器 a424ca613c9f2247cd3ede95adfbaf8d28400cbcb1d5f9b69a7b56f97b2b52e5 $ sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a424ca613c9f ubuntu:14.04 /bin/bash 6 seconds ago Up 5 seconds docker_test1 $ sudo docker attach docker_test1
root@test1:/# pwd /
root@test1:/# exit exit
關于cpu優先級:
By default all groups have 1024 shares. A group with 100 shares will get a ~10% portion of the CPU time - archlinux cgroups
4.8 docker start|stop|kill... ...
dockerstart|stop|kill|restart|pause|unpause|rm|commit|inspect|logs
docker start CONTAINER [CONTAINER...]
# 運作一個或多個停止的容器
docker stop CONTAINER [CONTAINER...]
# 停掉一個或多個運作的容器-t選項可指定逾時時間
docker kill [OPTIONS] CONTAINER [CONTAINER...]
# 預設 kill 發送 SIGKILL 信号-s可以指定發送 kill 信号類型
docker restart [OPTIONS] CONTAINER [CONTAINER...]
# 重新開機一個或多個運作的容器-t選項可指定逾時時間
docker pause CONTAINER
# 暫停一個容器,友善 commit
docker unpause CONTAINER
# 繼續暫停的容器
docker rm [OPTIONS] CONTAINER [CONTAINER...]
# 移除一個或多個容器
-f, --force=false Force removal of running container
-l, --link=false Remove the specified link and not the underlying container
-v, --volumes=false Remove the volumes associated with the container
docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
# 送出指定容器為鏡像
-a, --author="" Author (e.g., "John Hannibal Smith [email protected]")
-m, --message="" Commit message
-p, --pause=true Pause container during commit
# 預設 commit 是暫停狀态
docker inspect CONTAINER|IMAGE [CONTAINER|IMAGE...]
# 檢視容器或者鏡像的詳細資訊
docker logs CONTAINER
# 輸出指定容器日志資訊
-f, --follow=false Follow log output
# 類似 tail -f
-t, --timestamps=false Show timestamps
--tail="all" Output the specified number of lines at the end of logs (defaults to all logs)
參考文檔:Docker Run Reference
4.9 Docker 1.3 新增特性和指令
Digital Signature Verification
Docker 1.3 版本将使用數字簽名自動驗證所有官方庫的來源和完整性,如果一個官方鏡像被篡改或者被破壞,目前 Docker 隻會對這種情況發出警告而并不阻止容器的運作。
Inject new processes withdocker exec
docker exec --help
Usage: docker exec [OPTIONS] CONTAINER COMMAND [ARG...] Run a command in an existing container
-d, --detach=false Detached mode: run command in the background
-i, --interactive=false Keep STDIN open even if not attached
-t, --tty=false Allocate a pseudo-TTY
為了簡化調試,可以使用docker exec指令通過 Docker API 和 CLI 在運作的容器上運作程式。
$ docker exec -it ubuntu_bash bash
上例将在容器 ubuntu_bash 中建立一個新的 Bash 會話。
Tune container lifecycles withdocker create
我們可以通過docker run <image name>指令建立一個容器并運作其中的程式,因為有很多使用者要求建立容器的時候不啟動容器,是以docker create應運而生了。
$ docker create -t -i fedora bash
6d8af538ec541dd581ebc2a24153a28329acb5268abe5ef868c1f1a261221752
上例建立了一個可寫的容器層 (并且列印出容器 ID),但是并不運作它,可以使用以下指令運作該容器:
$ docker start -a -i 6d8af538ec5
bash-4.2#
Security Options
通過--security-opt選項,運作容器時使用者可自定義 SELinux 和 AppArmor 卷标和配置。
$ docker run --security-opt label:type:svirt_apache -i -t centos \ bash
上例隻允許容器監聽在 Apache 端口,這個選項的好處是使用者不需要運作 docker 的時候指定--privileged選項,降低安全風險。
參考文檔:Docker 1.3: signed images, process injection, security options, Mac shared directories
4.10 Docker 1.5 新特性
參考文檔:Docker 1.5 新特性
五、Docker 端口映射
# Find IP address of container with ID <container_id> 通過容器 id 擷取 ip $ sudo docker inspect <container_id> | grep IPAddress | cut -d ’"’ -f 4
無論如何,這些 ip 是基于本地系統的并且容器的端口非本地主機是通路不到的。此外,除了端口隻能本地通路外,對于容器的另外一個問題是這些 ip 在容器每次啟動的時候都會改變。
Docker 解決了容器的這兩個問題,并且給容器内部服務的通路提供了一個簡單而可靠的方法。Docker 通過端口綁定主機系統的接口,允許非本地用戶端通路容器内部運作的服務。為了簡便的使得容器間通信,Docker 提供了這種連接配接機制。
5.1 自動映射端口
-P使用時需要指定--expose選項,指定需要對外提供服務的端口
$ sudo docker run -t -P --expose 22 --name server ubuntu:14.04
使用docker run -P自動綁定所有對外提供服務的容器端口,映射的端口将會從沒有使用的端口池中 (49000..49900) 自動選擇,你可以通過docker ps、docker inspect <container_id>或者docker port <container_id> <port>确定具體的綁定資訊。
5.2 綁定端口到指定接口
基本文法
$ sudo docker run -p [([<host_interface>:[host_port]])|(<host_port>):]<container_port>[/udp] <image> <cmd>
預設不指定綁定 ip 則監聽所有網絡接口。
綁定 TCP 端口
# Bind TCP port 8080 of the container to TCP port 80 on 127.0.0.1 of the host machine. $ sudo docker run -p 127.0.0.1:80:8080 <image> <cmd> # Bind TCP port 8080 of the container to a dynamically allocated TCP port on 127.0.0.1 of the host machine. $ sudo docker run -p 127.0.0.1::8080 <image> <cmd> # Bind TCP port 8080 of the container to TCP port 80 on all available interfaces of the host machine. $ sudo docker run -p 80:8080 <image> <cmd> # Bind TCP port 8080 of the container to a dynamically allocated TCP port on all available interfaces $ sudo docker run -p 8080 <image> <cmd>
綁定 UDP 端口
# Bind UDP port 5353 of the container to UDP port 53 on 127.0.0.1 of the host machine. $ sudo docker run -p 127.0.0.1:53:5353/udp <image> <cmd>
六、Docker 網絡配置
圖: Docker - container and lightweight virtualization
Dokcer 通過使用 Linux 橋接提供容器之間的通信,docker0 橋接接口的目的就是友善 Docker 管理。當 Docker daemon 啟動時需要做以下操作:
creates the docker0 bridge if not present
# 如果 docker0 不存在則建立
searches for an IP address range which doesn’t overlap with an existing route
# 搜尋一個與目前路由不沖突的 ip 段
picks an IP in the selected range
# 在确定的範圍中選擇 ip
assigns this IP to the docker0 bridge
# 綁定 ip 到 docker0
6.1 Docker 四種網絡模式
四種網絡模式摘自 Docker 網絡詳解及 pipework 源碼解讀與實踐
docker run 建立 Docker 容器時,可以用 --net 選項指定容器的網絡模式,Docker 有以下 4 種網絡模式:
host 模式,使用 --net=host 指定。
container 模式,使用 --net=container:NAMEorID 指定。
none 模式,使用 --net=none 指定。
bridge 模式,使用 --net=bridge 指定,預設設定。
host 模式
如果啟動容器的時候使用 host 模式,那麼這個容器将不會獲得一個獨立的 Network Namespace,而是和主控端共用一個 Network Namespace。容器将不會虛拟出自己的網卡,配置自己的 IP 等,而是使用主控端的 IP 和端口。
例如,我們在 10.10.101.105/24 的機器上用 host 模式啟動一個含有 web 應用的 Docker 容器,監聽 tcp 80 端口。當我們在容器中執行任何類似 ifconfig 指令檢視網絡環境時,看到的都是主控端上的資訊。而外界通路容器中的應用,則直接使用 10.10.101.105:80 即可,不用任何 NAT 轉換,就如直接跑在主控端中一樣。但是,容器的其他方面,如檔案系統、程序清單等還是和主控端隔離的。
container 模式
這個模式指定新建立的容器和已經存在的一個容器共享一個 Network Namespace,而不是和主控端共享。新建立的容器不會建立自己的網卡,配置自己的 IP,而是和一個指定的容器共享 IP、端口範圍等。同樣,兩個容器除了網絡方面,其他的如檔案系統、程序清單等還是隔離的。兩個容器的程序可以通過 lo 網卡裝置通信。
none模式
這個模式和前兩個不同。在這種模式下,Docker 容器擁有自己的 Network Namespace,但是,并不為 Docker容器進行任何網絡配置。也就是說,這個 Docker 容器沒有網卡、IP、路由等資訊。需要我們自己為 Docker 容器添加網卡、配置 IP 等。
bridge模式
圖:The Container World | Part 2 Networking
bridge 模式是 Docker 預設的網絡設定,此模式會為每一個容器配置設定 Network Namespace、設定 IP 等,并将一個主機上的 Docker 容器連接配接到一個虛拟網橋上。當 Docker server 啟動時,會在主機上建立一個名為 docker0 的虛拟網橋,此主機上啟動的 Docker 容器會連接配接到這個虛拟網橋上。虛拟網橋的工作方式和實體交換機類似,這樣主機上的所有容器就通過交換機連在了一個二層網絡中。接下來就要為容器配置設定 IP 了,Docker 會從 RFC1918 所定義的私有 IP 網段中,選擇一個和主控端不同的IP位址和子網配置設定給 docker0,連接配接到 docker0 的容器就從這個子網中選擇一個未占用的 IP 使用。如一般 Docker 會使用 172.17.0.0/16 這個網段,并将 172.17.42.1/16 配置設定給 docker0 網橋(在主機上使用 ifconfig 指令是可以看到 docker0 的,可以認為它是網橋的管理接口,在主控端上作為一塊虛拟網卡使用)
6.2 列出目前主機網橋
$ sudo brctl show # brctl 工具依賴 bridge-utils 軟體包 bridge name bridge id STP enabled interfaces
docker0 8000.000000000000 no
6.3 檢視目前 docker0 ip
$ sudo ifconfig docker0
docker0 Link encap:Ethernet HWaddr xx:xx:xx:xx:xx:xx
inet addr:172.17.42.1 Bcast:0.0.0.0 Mask:255.255.0.0
在容器運作時,每個容器都會配置設定一個特定的虛拟機口并橋接到 docker0。每個容器都會配置同 docker0 ip 相同網段的專用 ip 位址,docker0 的 IP 位址被用于所有容器的預設網關。
6.4 運作一個容器
$ sudo docker run -t -i -d ubuntu /bin/bash
52f811c5d3d69edddefc75aff5a4525fc8ba8bcfa1818132f9dc7d4f7c7e78b4 $ sudo brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.fef213db5a66 no vethQCDY1N
以上, docker0 扮演着 52f811c5d3d6 container 這個容器的虛拟接口 vethQCDY1N interface 橋接的角色。
使用特定範圍的 IP
Docker 會嘗試尋找沒有被主機使用的 ip 段,盡管它适用于大多數情況下,但是它不是萬能的,有時候我們還是需要對 ip 進一步規劃。Docker 允許你管理 docker0 橋接或者通過-b選項自定義橋接網卡,需要安裝bridge-utils軟體包。
基本步驟如下:
ensure Docker is stopped
# 確定 docker 的程序是停止的
create your own bridge (bridge0 for example)
# 建立自定義網橋
assign a specific IP to this bridge
# 給網橋配置設定特定的 ip
start Docker with the -b=bridge0 parameter
# 以 -b 的方式指定網橋
# Stopping Docker and removing docker0 $ sudo service docker stop $ sudo ip link set dev docker0 down $ sudo brctl delbr docker0 # Create our own bridge $ sudo brctl addbr bridge0 $ sudo ip addr add 192.168.5.1/24 dev bridge0 $ sudo ip link set dev bridge0 up # Confirming that our bridge is up and running $ ip addr show bridge0
4: bridge0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state UP group default
link/ether 66:38:d0:0d:76:18 brd ff:ff:ff:ff:ff:ff
inet 192.168.5.1/24 scope global bridge0
valid_lft forever preferred_lft forever # Tell Docker about it and restart (on Ubuntu) $ echo 'DOCKER_OPTS="-b=bridge0"' >> /etc/default/docker $ sudo service docker start
參考文檔: Network Configuration
6.5 不同主機間容器通信
不同容器之間的通信可以借助于 pipework 這個工具:
$ git clone https://github.com/jpetazzo/pipework.git
$ sudo cp -rp pipework/pipework /usr/local/bin/
安裝相應依賴軟體
$ sudo apt-get install iputils-arping bridge-utils -y
橋接網絡
橋接網絡可以參考 日常問題處理 Tips 關于橋接的配置說明,這裡不再贅述。
# brctl show
bridge name bridge id STP enabled interfaces
br0 8000.000c291412cd no eth0
docker0 8000.56847afe9799 no vetheb48029
可以删除 docker0,直接把 docker 的橋接指定為 br0。也可以保留使用預設的配置,這樣單主機容器之間的通信可以通過 docker0,而跨主機不同容器之間通過 pipework 建立 docker 容器的網卡橋接到 br0,這樣跨主機容器之間就可以通信了。
ubuntu
$ sudo service docker stop
$ sudo ip link set dev docker0 down
$ sudo brctl delbr docker0
$ echo 'DOCKER_OPTS="-b=br0"' >> /etc/default/docker
$ sudo service docker start
CentOS 7/RHEL 7
$ sudo systemctl stop docker
$ cat /etc/sysconfig/docker | grep 'OPTIONS='
OPTIONS=--selinux-enabled -b=br0 -H fd://
$ sudo systemctl start docker
pipework
不同容器之間的通信可以借助于 pipework 這個工具給 docker 容器建立虛拟網卡并綁定 IP 橋接到 br0
$ pipework
Syntax:
pipework <hostinterface> [-i containerinterface] <guest> <ipaddr>/<subnet>[@default_gateway] [macaddr][@vlan]
pipework <hostinterface> [-i containerinterface] <guest> dhcp [macaddr][@vlan]
pipework --wait [-i containerinterface]
如果删除了預設的 docker0 橋接,把 docker 預設橋接指定到了 br0,則最好在建立容器的時候加上--net=none,防止自動配置設定的 IP 在區域網路中有沖突。
$ sudo docker run --rm -ti --net=none ubuntu:14.04 /bin/bash
root@a46657528059:/#
$ # Ctrl-P + Ctrl-Q 回到主控端 shell,容器 detach 狀态
$ sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a46657528059 ubuntu:14.04 "/bin/bash" 4 minutes ago Up 4 minutes hungry_lalande
$ sudo pipework br0 -i eth0 a46657528059 192.168.115.10/[email protected]
# 預設不指定網卡裝置名,則預設添加為 eth1
# 另外 pipework 不能添加靜态路由,如果有需求則可以在 run 的時候加上 --privileged=true 權限在容器中手動添加,
# 但這種安全性有缺陷,可以通過 ip netns 操作
$ sudo docker attach a46657528059
root@a46657528059:/# ifconfig eth0
eth0 Link encap:Ethernet HWaddr 86:b6:6b:e8:2e:4d
inet addr:192.168.115.10 Bcast:0.0.0.0 Mask:255.255.255.0
inet6 addr: fe80::84b6:6bff:fee8:2e4d/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:8 errors:0 dropped:0 overruns:0 frame:0
TX packets:9 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:648 (648.0 B) TX bytes:690 (690.0 B)
root@a46657528059:/# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 192.168.115.2 0.0.0.0 UG 0 0 0 eth0
192.168.115.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
使用ip netns添加靜态路由,避免建立容器使用--privileged=true選項造成一些不必要的安全問題:
$ docker inspect --format="` `.`State`.`Pid `" a46657528059 # 擷取指定容器 pid
6350
$ sudo ln -s /proc/6350/ns/net /var/run/netns/6350
$ sudo ip netns exec 6350 ip route add 192.168.0.0/16 dev eth0 via 192.168.115.2
$ sudo ip netns exec 6350 ip route # 添加成功
192.168.0.0/16 via 192.168.115.2 dev eth0
... ...
在其它主控端進行相應的配置,建立容器并使用 pipework 添加虛拟網卡橋接到 br0,測試通信情況即可。
另外,pipework 可以建立容器的 vlan 網絡,這裡不作過多的介紹了,官方文檔已經寫的很清楚了,可以檢視以下兩篇文章:
Pipework 官方文檔
Docker 網絡詳解及 pipework 源碼解讀與實踐
七、Dockerfile
Docker 可以通過 Dockerfile 的内容來自動建構鏡像。Dockerfile 是一個包含建立鏡像所有指令的文本檔案,通過docker build指令可以根據 Dockerfile 的内容建構鏡像,在介紹如何建構之前先介紹下 Dockerfile 的基本文法結構。
Dockerfile 有以下指令選項:
FROM
MAINTAINER
RUN
CMD
EXPOSE
ENV
ADD
COPY
ENTRYPOINT
VOLUME
USER
WORKDIR
ONBUILD
7.1 FROM
用法:
FROM <image>
或者
FROM指定建構鏡像的基礎源鏡像,如果本地沒有指定的鏡像,則會自動從 Docker 的公共庫 pull 鏡像下來。
FROM必須是 Dockerfile 中非注釋行的第一個指令,即一個 Dockerfile 從FROM語句開始。
FROM可以在一個 Dockerfile 中出現多次,如果有需求在一個 Dockerfile 中建立多個鏡像。
如果FROM語句沒有指定鏡像标簽,則預設使用latest标簽。
7.2 MAINTAINER
MAINTAINER <name>
指定建立鏡像的使用者
RUN 有兩種使用方式
RUN "executable", "param1", "param2"
每條RUN指令将在目前鏡像基礎上執行指定指令,并送出為新的鏡像,後續的RUN都在之前RUN送出後的鏡像為基礎,鏡像是分層的,可以通過一個鏡像的任何一個曆史送出點來建立,類似源碼的版本控制。
exec 方式會被解析為一個 JSON 數組,是以必須使用雙引号而不是單引号。exec 方式不會調用一個指令 shell,是以也就不會繼承相應的變量,如:
RUN [ "echo", "$HOME" ]
這種方式是不會達到輸出 HOME 變量的,正确的方式應該是這樣的
RUN [ "sh", "-c", "echo", "$HOME" ]
RUN産生的緩存在下一次建構的時候是不會失效的,會被重用,可以使用--no-cache選項,即docker build --no-cache,如此便不會緩存。
7.3 CMD
CMD有三種使用方式:
CMD "executable","param1","param2"
CMD "param1","param2"
CMD command param1 param2 (shell form)
CMD指定在 Dockerfile 中隻能使用一次,如果有多個,則隻有最後一個會生效。
CMD的目的是為了在啟動容器時提供一個預設的指令執行選項。如果使用者啟動容器時指定了運作的指令,則會覆寫掉CMD指定的指令。
CMD會在啟動容器的時候執行,build 時不執行,而RUN隻是在建構鏡像的時候執行,後續鏡像建構完成之後,啟動容器就與RUN無關了,這個初學者容易弄混這個概念,這裡簡單注解一下。
7.4 EXPOSE
EXPOSE <port> [<port>...]
告訴 Docker 服務端容器對外映射的本地端口,需要在 docker run 的時候使用-p或者-P選項生效。
7.5 ENV
ENV <key> <value> # 隻能設定一個變量
ENV <key>=<value> ... # 允許一次設定多個變量
指定一個環節變量,會被後續RUN指令使用,并在容器運作時保留。
例子:
ENV myName="John Doe" myDog=Rex\ The\ Dog \
myCat=fluffy
等同于
ENV myName John Doe
ENV myDog Rex The Dog
ENV myCat fluffy
7.6 ADD
ADD <src>... <dest>
ADD複制本地主機檔案、目錄或者遠端檔案 URLS 從 并且添加到容器指定路徑中 。
支援通過 GO 的正則模糊比對,具體規則可參見 Go filepath.Match
ADD hom* /mydir/ # adds all files starting with "hom"
ADD hom?.txt /mydir/ # ? is replaced with any single character
路徑必須是絕對路徑,如果 不存在,會自動建立對應目錄
路徑必須是 Dockerfile 所在路徑的相對路徑
如果是一個目錄,隻會複制目錄下的内容,而目錄本身則不會被複制
7.7 COPY
COPY <src>... <dest>
COPY複制新檔案或者目錄從 并且添加到容器指定路徑中 。用法同ADD,唯一的不同是不能指定遠端檔案 URLS。
7.8 ENTRYPOINT
ENTRYPOINT "executable", "param1", "param2"
ENTRYPOINT command param1 param2 (shell form)
配置容器啟動後執行的指令,并且不可被 docker run 提供的參數覆寫,而CMD是可以被覆寫的。如果需要覆寫,則可以使用docker run --entrypoint選項。
每個 Dockerfile 中隻能有一個ENTRYPOINT,當指定多個時,隻有最後一個生效。
Exec form ENTRYPOINT 例子
通過ENTRYPOINT使用 exec form 方式設定穩定的預設指令和選項,而使用CMD添加預設之外經常被改動的選項。
FROM ubuntu
ENTRYPOINT ["top", "-b"]
CMD ["-c"]
通過 Dockerfile 使用ENTRYPOINT展示前台運作 Apache 服務
FROM debian:stable
RUN apt-get update && apt-get install -y --force-yes apache2
EXPOSE 80 443
VOLUME ["/var/www", "/var/log/apache2", "/etc/apache2"]
ENTRYPOINT ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]
Shell form ENTRYPOINT 例子
這種方式會在/bin/sh -c中執行,會忽略任何CMD或者docker run指令行選項,為了確定docker stop能夠停止長時間運作ENTRYPOINT的容器,確定執行的時候使用exec選項。
ENTRYPOINT exec top -b
如果在ENTRYPOINT忘記使用exec選項,則可以使用CMD補上:
ENTRYPOINT top -b
CMD --ignored-param1 # --ignored-param2 ... --ignored-param3 ... 依此類推
7.9 VOLUME
VOLUME ["/data"]
建立一個可以從本地主機或其他容器挂載的挂載點,後續具體介紹。
7.10 USER
USER daemon
指定運作容器時的使用者名或 UID,後續的RUN、CMD、ENTRYPOINT也會使用指定使用者。
7.11 WORKDIR
WORKDIR /path/to/workdir
為後續的RUN、CMD、ENTRYPOINT指令配置工作目錄。可以使用多個WORKDIR指令,後續指令如果參數是相對路徑,則會基于之前指令指定的路徑。
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
最終路徑是/a/b/c。
WORKDIR指令可以在ENV設定變量之後調用環境變量:
ENV DIRPATH /path
WORKDIR $DIRPATH/$DIRNAME
最終路徑則為 /path/$DIRNAME。
7.12 ONBUILD
ONBUILD [INSTRUCTION]
配置當所建立的鏡像作為其它新建立鏡像的基礎鏡像時,所執行的操作指令。
例如,Dockerfile 使用如下的内容建立了鏡像 image-A:
[...]
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
如果基于 image-A 建立新的鏡像時,新的 Dockerfile 中使用 FROM image-A 指定基礎鏡像時,會自動執行 ONBUILD 指令内容,等價于在後面添加了兩條指令。
# Automatically run the following
ADD . /app/src
RUN /usr/local/bin/python-build --dir /app/src
使用ONBUILD指令的鏡像,推薦在标簽中注明,例如 ruby:1.9-onbuild。
7.13 Dockerfile Examples
# Nginx
#
# VERSION 0.0.1
FROM ubuntu
MAINTAINER Victor Vieux <[email protected]>
RUN apt-get update && apt-get install -y inotify-tools nginx apache2 openssh-server
# Firefox over VNC
# VERSION 0.3
# Install vnc, xvfb in order to create a 'fake' display and firefox
RUN apt-get update && apt-get install -y x11vnc xvfb firefox
RUN mkdir ~/.vnc
# Setup a password
RUN x11vnc -storepasswd 1234 ~/.vnc/passwd
# Autostart firefox (might not be the best way, but it does the trick)
RUN bash -c 'echo "firefox" >> /.bashrc'
EXPOSE 5900
CMD ["x11vnc", "-forever", "-usepw", "-create"]
# Multiple images example
# VERSION 0.1
RUN echo foo > bar
# Will output something like ===> 907ad6c2736f
RUN echo moo > oink
# Will output something like ===> 695d7793cbe4
# Youll now have two images, 907ad6c2736f with /bar, and 695d7793cbe4 with
# /oink.
7.14 docker build
$ docker build --help
Usage: docker build [OPTIONS] PATH | URL | -
Build a new image from the source code at PATH
--force-rm=false Always remove intermediate containers, even after unsuccessful builds # 移除過渡容器,即使建構失敗
--no-cache=false Do not use cache when building the image # 不實用 cache
-q, --quiet=false Suppress the verbose output generated by the containers
--rm=true Remove intermediate containers after a successful build # 建構成功後移除過渡層容器
-t, --tag="" Repository name (and optionally a tag) to be applied to the resulting image in case of success
參考文檔:Dockerfile Reference
7.15 dockerfile 最佳實踐
使用.dockerignore檔案
為了在docker build過程中更快上傳和更加高效,應該使用一個.dockerignore檔案用來排除建構鏡像時不需要的檔案或目錄。例如,除非.git在建構過程中需要用到,否則你應該将它添加到.dockerignore檔案中,這樣可以節省很多時間。
避免安裝不必要的軟體包
為了降低複雜性、依賴性、檔案大小以及建構時間,應該避免安裝額外的或不必要的包。例如,不需要在一個資料庫鏡像中安裝一個文本編輯器。
每個容器都跑一個程序
在大多數情況下,一個容器應該隻單獨跑一個程式。解耦應用到多個容器使其更容易橫向擴充和重用。如果一個服務依賴另外一個服務,可以參考 Linking Containers Together。
最小化層
我們知道每執行一個指令,都會有一次鏡像的送出,鏡像是分層的結構,對于Dockerfile,應該找到可讀性和最小化層之間的平衡。
多行參數排序
如果可能,通過字母順序來排序,這樣可以避免安裝包的重複并且更容易更新清單,另外可讀性也會更強,添加一個空行使用\換行:
RUN apt-get update && apt-get install -y \
bzr \
cvs \
git \
mercurial \
subversion
建立緩存
鏡像建構過程中會按照Dockerfile的順序依次執行,每執行一次指令 Docker 會尋找是否有存在的鏡像緩存可複用,如果沒有則建立新的鏡像。如果不想使用緩存,則可以在docker build時添加--no-cache=true選項。
從基礎鏡像開始就已經在緩存中了,下一個指令會對比所有的子鏡像尋找是否執行相同的指令,如果沒有則緩存失效。在大多數情況下隻對比Dockerfile指令和子鏡像就足夠了。ADD和COPY指令除外,執行ADD和COPY時存放到鏡像的檔案也是需要檢查的,完成一個檔案的校驗之後再利用這個校驗在緩存中查找,如果檢測的檔案改變則緩存失效。RUN apt-get -y update指令隻檢查指令是否比對,如果比對就不會再執行更新了。
為了有效地利用緩存,你需要保持你的 Dockerfile 一緻,并且盡量在末尾修改。
Dockerfile 指令
FROM: 隻要可能就使用官方鏡像庫作為基礎鏡像
RUN: 為保持可讀性、友善了解、可維護性,把長或者複雜的RUN語句使用\分隔符分成多行
不建議RUN apt-get update獨立成行,否則如果後續包有更新,那麼也不會再執行更新
避免使用RUN apt-get upgrade或者dist-upgrade,很多必要的包在一個非privileged權限的容器裡是無法更新的。如果知道某個包更新,使用apt-get install -y xxx
标準寫法
RUN apt-get update && apt-get install -y package-bar package-foo
aufs-tools \
automake \
btrfs-tools \
build-essential \
curl \
dpkg-sig \
git \
iptables \
libapparmor-dev \
libcap-dev \
libsqlite3-dev \
lxc=1.0* \
mercurial \
parallel \
reprepro \
ruby1.9.1 \
ruby1.9.1-dev \
s3cmd=1.1.0*
CMD: 推薦使用CMD [“executable”, “param1”, “param2”…]這種格式,CMD [“param”, “param”]則配合ENTRYPOINT使用
EXPOSE: Dockerfile 指定要公開的端口,使用docker run時指定映射到主控端的端口即可
ENV: 為了使新的軟體更容易運作,可以使用ENV更新PATH變量。如ENV PATH /usr/local/nginx/bin:$PATH確定CMD ["nginx"]即可運作
ENV也可以這樣定義變量:
ENV PG_MAJOR 9.3
ENV PG_VERSION 9.3.4
RUN curl -SL http://example.com/postgres-$PG_VERSION.tar.xz | tar -xJC /usr/src/postgress && …
ENV PATH /usr/local/postgres-$PG_MAJOR/bin:$PATH
ADDorCOPY:ADD比COPY多一些特性「tar 檔案自動解包和支援遠端 URL」,不推薦添加遠端 URL
如不推薦這種方式:
ADD http://example.com/big.tar.xz /usr/src/things/
RUN tar -xJf /usr/src/things/big.tar.xz -C /usr/src/things
RUN make -C /usr/src/things all
推薦使用 curl 或者 wget 替換,使用如下方式:
RUN mkdir -p /usr/src/things \
&& curl -SL http://example.com/big.tar.gz \
| tar -xJC /usr/src/things \
&& make -C /usr/src/things all
如果不需要添加 tar 檔案,推薦使用COPY。
參考文檔:
Best practices for writing Dockerfiles
Dockerfile最佳實踐(一)
Dockerfile最佳實踐(二)
八、容器資料管理
docker管理資料的方式有兩種:
資料卷
資料卷容器
8.1 資料卷
資料卷是一個或多個容器專門指定繞過Union File System的目錄,為持續性或共享資料提供一些有用的功能:
資料卷可以在容器間共享和重用
資料卷資料改變是直接修改的
資料卷資料改變不會被包括在容器中
資料卷是持續性的,直到沒有容器使用它們
添加一個資料卷
你可以使用-v選項添加一個資料卷,或者可以使用多次-v選項為一個 docker 容器運作挂載多個資料卷。
$ sudo docker run --name data -v /data -t -i ubuntu:14.04 /bin/bash # 建立資料卷綁定到到建立容器,建立容器中會建立 /data 資料卷 bash-4.1# ls -ld /data/
drwxr-xr-x 2 root root 4096 Jul 23 06:59 /data/
bash-4.1# df -Th
Filesystem Type Size Used Avail Use% Mounted on
ext4 91G 4.6G 82G 6% /data
建立的資料卷可以通過docker inspect擷取主控端對應路徑
$ sudo docker inspect data
... ... "Volumes": { "/data": "/var/lib/docker/vfs/dir/151de401d268226f96d824fdf444e77a4500aed74c495de5980c807a2ffb7ea9" }, # 可以看到建立的資料卷主控端路徑 ... ...
或者直接指定擷取
$ sudo docker inspect --format="` `.`Volumes `" data
map[/data: /var/lib/docker/vfs/dir/151de401d268226f96d824fdf444e77a4500aed74c495de5980c807a2ffb7ea9]
挂載主控端目錄為一個資料卷
-v選項除了可以建立卷,也可以挂載目前主機的一個目錄到容器中。
$ sudo docker run --name web -v /source/:/web -t -i ubuntu:14.04 /bin/bash
bash-4.1# ls -ld /web/
drwxr-xr-x 2 root root 4096 Jul 23 06:59 /web/
ext4 91G 4.6G 82G 6% /web
bash-4.1# exit
預設挂載卷是可讀寫的,可以在挂載時指定隻讀
$ sudo docker run --rm --name test -v /source/:/test:ro -t -i ubuntu:14.04 /bin/bash
8.2 建立和挂載一個資料卷容器
如果你有一些持久性的資料并且想在容器間共享,或者想用在非持久性的容器上,最好的方法是建立一個資料卷容器,然後從此容器上挂載資料。
建立資料卷容器
$ sudo docker run -t -i -d -v /test --name test ubuntu:14.04 echo hello
使用--volumes-from選項在另一個容器中挂載 /test 卷。不管 test 容器是否運作,其它容器都可以挂載該容器資料卷,當然如果隻是單獨的資料卷是沒必要運作容器的。
$ sudo docker run -t -i -d --volumes-from test --name test1 ubuntu:14.04 /bin/bash
添加另一個容器
$ sudo docker run -t -i -d --volumes-from test --name test2 ubuntu:14.04 /bin/bash
也可以繼承其它挂載有 /test 卷的容器
$ sudo docker run -t -i -d --volumes-from test1 --name test3 ubuntu:14.04 /bin/bash
8.3 備份、恢複或遷移資料卷
備份
$ sudo docker run --rm --volumes-from test -v $(pwd):/backup ubuntu:14.04 tar cvf /backup/test.tar /test
tar: Removing leading `/' from member names
/test/
/test/b
/test/d
/test/c
/test/a
啟動一個新的容器并且從test容器中挂載卷,然後挂載目前目錄到容器中為 backup,并備份 test 卷中所有的資料為 test.tar,執行完成之後删除容器--rm,此時備份就在目前的目錄下,名為test.tar。
$ ls # 主控端目前目錄下産生了 test 卷的備份檔案 test.tar test.tar
恢複
你可以恢複給同一個容器或者另外的容器,建立容器并解壓備份檔案到新的容器資料卷
$ sudo docker run -t -i -d -v /test --name test4 ubuntu:14.04 /bin/bash $ sudo docker run --rm --volumes-from test4 -v $(pwd):/backup ubuntu:14.04 tar xvf /backup/test.tar -C / # 恢複之前的檔案到建立卷中,執行完後自動删除容器 test/ test/b test/d test/c test/a
8.4 删除 Volumes
Volume 隻有在下列情況下才能被删除:
docker rm -v删除容器時添加了-v選項
docker run --rm運作容器時添加了--rm選項
否則,會在/var/lib/docker/vfs/dir目錄中遺留很多不明目錄。
參考文檔:
Managing Data in Containers
深入了解Docker Volume(一)
深入了解Docker Volume(二)
九、連結容器
docker 允許把多個容器連接配接在一起,互相互動資訊。docker 連結會建立一種容器父子級别的關系,其中父容器可以看到其子容器提供的資訊。
9.1 容器命名
在建立容器時,如果不指定容器的名字,則預設會自動建立一個名字,這裡推薦給容器命名:
1、給容器命名友善記憶,如命名運作 web 應用的容器為 web
2、為 docker 容器提供一個參考,允許友善其他容器調用,如把容器 web 連結到容器 db
可以通過--name選項給容器自定義命名:
$ sudo docker run -d -t -i --name test ubuntu:14.04 bash
$ sudo docker inspect --format="` `.`Nmae `" test
/test
注:容器名稱必須唯一,即你隻能命名一個叫test的容器。如果你想複用容器名,則必須在建立新的容器前通過docker rm删除舊的容器或者建立容器時添加--rm選項。
9.2 連結容器
連結允許容器間安全通信,使用--link選項建立連結。
$ sudo docker run -d --name db training/postgres
基于 training/postgres 鏡像建立一個名為 db 的容器,然後下面建立一個叫做 web 的容器,并且将它與 db 互相連接配接在一起
$ sudo docker run -d -P --name web --link db:db training/webapp python app.py
--link <name or id>:alias選項指定連結到的容器。
檢視 web 容器的連結關系:
$ sudo docker inspect -f "` `.`HostConfig`.`Links `" web
[/db:/web/db]
可以看到 web 容器被連結到 db 容器為/web/db,這允許 web 容器通路 db 容器的資訊。
容器之間的連結實際做了什麼?一個連結允許一個源容器提供資訊通路給一個接收容器。在本例中,web 容器作為一個接收者,允許通路源容器 db 的相關服務資訊。Docker 建立了一個安全隧道而不需要對外公開任何端口給外部容器,是以不需要在建立容器的時候添加-p或-P指定對外公開的端口,這也是連結容器的最大好處,本例為 PostgreSQL 資料庫。
Docker 主要通過以下兩個方式提供連接配接資訊給接收容器:
環境變量
更新/etc/hosts檔案
當兩個容器連結,Docker 會在目标容器上設定一些環境變量,以擷取源容器的相關資訊。
首先,Docker 會在每個通過--link選項指定别名的目标容器上設定一個<alias>_NAME環境變量。如果一個名為 web 的容器通過--link db:webdb被連結到一個名為 db 的資料庫容器,那麼 web 容器上會設定一個環境變量為WEBDB_NAME=/web/webdb.
以之前的為例,Docker 還會設定端口變量:
$ sudo docker run --rm --name web2 --link db:db training/webapp env
. . .
DB_NAME=/web2/db
DB_PORT=tcp://172.17.0.5:5432
DB_PORT_5432_TCP=tcp://172.17.0.5:5432 # <name>_PORT_<port>_<protocol> 協定可以是 TCP 或 UDP
DB_PORT_5432_TCP_PROTO=tcp
DB_PORT_5432_TCP_PORT=5432
DB_PORT_5432_TCP_ADDR=172.17.0.5
注:這些環境變量隻設定給容器中的第一個程序,類似一些守護程序 (如 sshd ) 當他們派生 shells 時會清除這些變量
除了環境變量,Docker 會在目标容器上添加相關主機條目到/etc/hosts中,上例中就是 web 容器。
$ sudo docker run -t -i --rm --link db:db training/webapp /bin/bash
root@aed84ee21bde:/opt/webapp# cat /etc/hosts
172.17.0.7 aed84ee21bde
172.17.0.5 db
/etc/host檔案在源容器被重新開機之後會自動更新 IP 位址,而環境變量中的 IP 位址則不會自動更新的。
十、建構私有庫
Docker 官方提供了 docker registry 的建構方法 docker-registry
10.1 快速建構
快速建構 docker registry 通過以下兩步:
安裝 docker
運作 registry:docker run -p 5000:5000 registry
這種方法通過 Docker hub 使用官方鏡像 official image from the Docker hub
10.2 不使用容器建構 registry
安裝必要的軟體
$ sudo apt-get install build-essential python-dev libevent-dev python-pip liblzma-dev
配置 docker-registry
sudo pip install docker-registry
或者 使用 github clone 手動安裝
$ git clone https://github.com/dotcloud/docker-registry.git
$ cd docker-registry/
$ cp config/config_sample.yml config/config.yml
$ mkdir /data/registry -p
$ pip install .
運作
docker-registry
進階啟動方式 [不推薦]
使用gunicorn控制:
gunicorn -c contrib/gunicorn_config.py docker_registry.wsgi:application
或者對外監聽開放
gunicorn --access-logfile - --error-logfile - -k gevent -b 0.0.0.0:5000 -w 4 --max-requests 100 docker_registry.wsgi:application
10.3 送出指定容器到私有庫
$ docker tag ubuntu:12.04 私有庫IP:5000/ubuntu:12.04
$ docker push 私有庫IP:5000/ubuntu
更多的配置選項推薦閱讀官方文檔:
Docker-Registry README
Docker-Registry advanced use
本文轉自 運維小當家 51CTO部落格,原文連結:http://blog.51cto.com/solin/1886455,如需轉載請自行聯系原作者