Docker使用Go語言開發,基於Linux內核的cgroup、namespace以及AUFS等技術對進程進行封裝隔離,是一種操作系統層面的虛擬化技術。由於隔離的進程獨立於宿主和其它的隔離的進程,因此也稱其為容器。
Docker則使用宿主機內核提供的隔離機制創建沙盒環境,容器內的應用進程直接運行於宿主的內核。 因為容器內沒有虛擬硬件和內核,容器在啟動時間、執行效率、內存占用以及鏡像大小等方面相對於傳統虛擬機都擁有很大優勢。
Docker容器將程序及其運行環境打包在一起,鏡像創建后可以在任何安裝了Docker的系統上運行,無需配置運行環境, 加之較小的鏡像體積極大方便了協作開發和部署。
使用Dockerfile將鏡像構建過程透明化,也便於開發和運維人員理解程序的運行環境。
Docker相對於虛擬機的優勢來源於它運行與宿主內核而減少了開銷,這使得Docker不能虛擬不同的內核環境。也就是說我們可以很容易地在Windows操作系統上啟動一個Linux虛擬機,但是在Windows上啟動一個基於Linux的Docker容器則是很困難的事情。
docker官方已經發布了docker for mac
及docker for windows
。目前docker for mac
使用MAC OS內核提供的HyperKit
虛擬化技術代替了docker-toolbox采用的使用Linux虛擬機提供支持的方式。
目錄:
What's Docker
cgroup和namespace是Linux內核提供的兩種隔離機制,是Docker虛擬化的技術基礎:
- cgroup: 全名Control Groups, Linux內核提供的用於監控和管理進程組占用資源的機制。 這里的資源包括CPU,內存和IO等硬件資源。
- namespace: 為某個進程提供獨立的空間,包括獨立的:
- 進程樹: 進程擁有獨立的init進程及其下進程樹
- 文件系統: 進程擁有獨立的根目錄
/
及其下目錄樹 - 用戶: 進程可以定義自己的用戶,組和權限系統
- 協議棧: 進程可以擁有獨立的ip地址及tcp/udp端口空間
- 其它
在了解隔離機制之后我們可以了解Docker中的兩個核心概念:
- 容器:容器的本質是進程,它擁有獨立的命名空間。因此容器表現很像一個虛擬操作系統,擁有自己的進程樹,文件系統等。
- 鏡像:鏡像是容器運行依賴的文件系統,就像每個Linux操作系統時都需要掛載root文件系統
/
,鏡像就是容器的root文件系統。
Docker采用服務端/客戶端架構:
- 守護進程(dockerd): Docker服務端每個dockerd下可以運行多個容器,此外還提供了鏡像和容器管理的功能。
- 客戶端: 通過API與dockerd通信進行操作,官方提供了命令行客戶端以及Go和Python語言的SDK
- 宿主機:用於運行dockerd及其容器的操作系統環境
- Registry:Docker鏡像存儲中心,用於管理和共享docker鏡像。官方存儲中心DockerHub中提供了大量官方和第三方鏡像。
Docker采用UnionFS技術將鏡像設計為分層結構,即一個鏡像分為多層,每一層在上一層的基礎上構建(即存儲增量)。在容器中只能看到所有層疊加后的結果,隱藏了分層的結構。因為鏡像層會被其它層依賴,為了保證下層能正常工作, 鏡像層在創建后就無法進行修改。
Get Started
Docker目前分為免費的社區版(CE)和付費的商業版(EE)兩種, 這里我們選用社區版。 Docker官網上提供了各種常用操作系統的安裝說明:
下面看一個簡單的示例:
finley@mbp:$ uname
Darwin
finley@mbp:$ docker run -it ubuntu
root@528ab91753d6:/# uname
Linux
在執行docker run
命令后發現我們已經從宿主機Mac的終端進入到了Linux終端中。 這個運行中的"Linux"就是容器,啟動這個虛擬的Linux環境所需的文件即是鏡像。
上面的示例中我們使用docker代替了ubuntu虛擬機,對比之下docker在鏡像大小,啟動時間和運行內存占用方面都具有巨大的優勢。
因為啟動docker容器的開銷和啟動相應進程的開銷基本相同, 因此完全可以使用docker代替原生進程, 避免復雜的編譯安裝配置過程。
$ docker run -p 6379:6379 -d redis
3a5748eef653
$ redis-cli
127.0.0.1:6379> keys *
(empty list or set)
這個示例中我們一鍵安裝並啟動了redis-server。
在終端中輸入docker
命令則會顯示所有命令和使用幫助, 在docker命令后添加--help
選項可以查看該命令的幫助信息, 如docker run --help
可以查看run命令的幫助。
鏡像
本節將簡單介紹如何搜索和管理鏡像, 為介紹容器做准備。更多關於鏡像的構建和共享的內容將在下文中介紹。
docker images
docker images
命令用於顯示本地所有鏡像:
REPOSITORY | TAG | IMAGE ID | CREATED | SIZE |
---|---|---|---|---|
ubuntu | latest | 00fd29ccc6f1 | 3 weeks ago | 111MB |
redis | latest | 1e70071f4af4 | 4 weeks ago | 107MB |
和其它軟件一樣docker鏡像也可以演進出不同版本,tag用於標識鏡像的版本。
repository代表同一鏡像多個版本的集合, 它的值是一個URI用於全局唯一標識一組鏡像,如"registry.cn-hangzhou.aliyuncs.com/acs/agent"。
對於DockerHub中的鏡像則會省略倉庫的地址,諸如dorowu/ubuntu-desktop-lxde-vnc
和ubuntu
就是DockerHub中的鏡像。
由此可見,repository
和tag
可以唯一標識docker鏡像。
除此之外每個鏡像還擁有一個摘要(DIGEST),docker images --digest
可以顯示鏡像的摘要。
docker search
DockerHub是docker官方提供的公有倉庫,docker search
命令用於根據關鍵字搜索DockerHub中的鏡像:
format: docker search TERM
demo: docker search ubuntu
NAME | DESCRIPTION | STARS | OFFICIAL | AUTOMATED |
---|---|---|---|---|
ubuntu | Ubuntu is a ... | 7076 | [OK] | |
dorowu/ubuntu-desktop-lxde-vnc | Ubuntu with openssh-server and NoVNC | 156 | [OK] |
docker pull
docker pull
命令用於從遠程下載鏡像,可以通過NAME:TAG
或NAME@DIGEST
的格式來指明目標鏡像。
當只提供了鏡像NAME時,默認下載tag為latest
的鏡像。
$docker pull ubuntu
$docker pull ubuntu:16.04
$docker pull ubuntu@sha256:fbaf303d18563e57a3c1a0005356ad102509b60884f3aa89ef9a90c0ea5d1212
NAME也可以是私有倉庫中一個REPOSITORY的URI。
docker rmi
docker rmi
用於刪除鏡像,可以使用IMAGE ID
或REPOSITORY:TAG
來標記一個容器。
容器
docker run
docker run
命令用於新建一個容器並啟動, 是最重要的和最常用的docker命令之一。
format: docker run IMAGE CMD ARGS
docker run
命令的標准執行流程包括:
- 檢查本地是否包含指定的鏡像,若不存在就從公有倉庫下載
- 在只讀的基礎鏡像層上掛載一個可讀寫層,創建容器的文件系統
- 根據文件系統啟動容器
- 將容器與宿主機橋接
- 用指定的身份登錄容器,並在指定目錄下執行
CMD ARGS
參數指定的命令 - 在命令執行完成后關閉容器
docker容器在命令執行完畢后會自動退出, 容器的生存周期僅決定於執行命令所需的時間。 如果在容器中執行bash
等長期運行的命令, 就可以保證容器長期運行。
docker run
命令默認會把標准輸出流(stdout)重定向到終端,並與容器保持連接狀態(attach)。
在attach狀態下, 容器退出之前doceker run
命令不會返回,而是在終端回顯容器的輸出。 若發出kill
信號(如ctrl + c
快捷鍵)殺死docekr run
, 那么容器也會提前退出。
容器對文件系統的修改在可讀寫層,不會對鏡像產生影響, 除非使用docker commit
創建新的鏡像層。 容器退出后其文件系統仍將保存在磁盤中,下次啟動后會保留所有修改。
打開終端
docker run
命令默認將容器的標准輸出流重定向到終端, 但是沒有將終端的標准輸入流(stdin)重定向到容器。 也就是說,容器無法接收我們在終端中輸入的命令。
使用-i
或--interactive
選項會保持容器的輸入流(stdin)打開,即使docker run
不與容器保持attach狀態。
docker run -i ubuntu bash
命令可以打開一個ubuntu終端, 該終端也可以接受我們輸入的指令。
和標准的Linux系統一樣,docker鏡像也為用戶指定了默認終端。 使用-t
或--tty
選項會打開一個虛擬終端(Pseudo-TTY)。
也就是說,docker run -it ubuntu
命令可以輕松地打開一個可交互ubuntu虛擬環境。
后台運行
docker run
命令默認與容器保持連接狀態(attach), -d
或--detach
選項可以與容器斷開連接。 docker run
命令在顯示容器ID后立即返回,容器則會在后台運行。
上文redis-server的示例即使用了-d
選項, docker run
立即返回, redis-server在后台繼續運行。
$ docker run -p 6379:6379 -d redis
3a5748eef653
$ redis-cli
127.0.0.1:6379>
端口映射
如上文中redis-server示例, 我們經常使用docker容器提供服務,因此需要docker容器監聽宿主機的某個端口。
docker run -p 6379:6379 -d redis
將對宿主機TCP6379端口的訪問映射到容器的TCP6379端口。
-p 6379:6379/udp
則可以映射udp訪問(雖然對redis-server來說沒有意義)。 多個-p
選項可以映射多個端口docker run -p 80:8080 -p 8080:8081 -d my_server
.
端口映射是將對宿主機某個端口的訪問映射到容器的某個端口上, 容器訪問宿主機端口不需要配置端口映射。
-v
選項可以將宿主機上某個目錄掛載到容器中的某個目錄
$ ls ~/myvol
history.txt
$ docker run -it -v ~/myvol:/app ubuntu
root@e690c508219e:/# ls /app
history.txt
上述指令將宿主機目錄~/myvol
掛載到鏡像的/app
目錄下,/app
目錄下原來的內容會被隱藏而是顯示宿主機目錄~/myvol
下的內容。
這種方式我們稱為在容器上掛載了數據卷,對數據卷的讀寫獨立於容器之外:
- 容器對數據卷的修改將立即存儲到數據卷所在的(即宿主機的文件系統)上
- 除非指明刪除數據卷,否則容器刪除不會對數據卷產生影響
- 其它進程對數據卷的修改將立即生效
若數據卷~/myvol
或掛載點/app
不存在的時候, docker會自動創建空目錄。
docker提供了獨立於容器的數據卷管理功能,參考docker volume。
為容器命名
每個容器都擁有一個唯一的CONTAINER ID
來標識,但ID不便於記憶和使用。 因此在docker run
創建容器時可以使用--name
選項來為容器指定一個名稱。
在某個dockerd中容器名稱是唯一的, 我們可以使用容器名來唯一指定容器。
示例:docker run --name my_ubuntu -it ubuntu
退出時自動刪除
docker run --rm
選項會在容器退出時自動刪除容器。 使用該命令時需謹慎, 容器一旦刪除便不可恢復。
自定義工作目錄
docker run -w PATH
選項會在啟動容器時,使用PATH參數指定的路徑作為工作目錄。
docker create
docker create
命令與docker run
指令極為相似,區別在於docker run
創建容器后自動啟動容器, 而docker create
不自動啟動容器需要使用docker start
命令來啟動。
docker ps
docker ps
命令用於顯示容器的信息,默認情況下顯示運行中的容器:
finley@mbp $ docker ps
CONTAINER ID | IMAGE | COMMAND | CREATED | STATUS | PORTS | NAMES |
---|---|---|---|---|---|---|
3a5748eef653 | redis | "docker-entrypoint..." | 3 hours ago | Up 3 hours | 0.0.0.0:6379->6379/tcp | redis-server |
c10921921bfb | ubuntu | "/bin/bash" | 3 hours ago | Up 52 seconds | my_ubuntu |
-a
選項可以顯示包括已停止容器在內的所有容器信息。
docker start
docker start
命令用於啟動一個已停止的容器,默認情況下不與容器連接(attach)也不將輸入重定向到容器。
$ docker run --name redis-server -p 6379:6379 redis
$ docker stop redis-server # 此時已經存在一個名為redis-server的已停止容器
$ docker start redis-server
redis-server
$ redis-cli
127.0.0.1:6379>
若使用-a
或--attach
選項將docker start
與容器連接(attach), 終端將回顯容器輸出。
-i
或--interactive
選項則會將輸入重定向到容器。
$ docker run --name my_ubuntu -it ubuntu
root@c10921921bfb # exit
# 此時已經存在一個名為my_ubuntu的已停止容器
$ docker start -ai my_ubuntu
root@c10921921bfb:/#
docker exec
docker exec
用於讓一個正在運行的容器執行命令,命令執行完成后docker exec
將返回容器繼續運行。
$ docker run --name my_ubuntu -d ubuntu
$ docker exec my_ubuntu ls
home bin ...
默認情況下docker exec
與容器連接,-d
或--detach
選項可以不與容器連接而在后台執行命令。
與docker run
命令類似, -i
選項用於將標准輸入重定向到容器以便接收用戶輸入, -t
選項可以打開終端。
$docker exec -it my_ubuntu bash
root@c10921921bfb:/#
docker attach
docker attach
命令用於與容器連接, 即將容器輸出流重定向到終端,終端輸入流重定向到容器。
$ docker attach my_ubuntu
# 再按一次回車
root@c10921921bfb:/#
當容器沒有輸出時終端中沒有回顯,可能令用戶誤以為卡死。 attach之后再次輸入回車, bash將回顯命令行提示符。
attach狀態下ctrl + c
快捷鍵會發送SIGKILL
信號中止容器, 而ctrl + p, ctrl + q
快捷鍵會退出attach容器繼續運行。
docker stop
docker stop
用於終止容器的運行
$docker stop redis-server
docker stop
命令會先發送SIGTERM
信號要求容器中的進程執行退出操作,若達到超時時間(默認10s)容器仍未退出則會發送SIGKILL
信號強制退出。
-t
或-time
選項可以秒為單位設置強制殺死之前的等待時間:
$docker stop -t 20 redis-server
docker kill
docker kill
命令將直接向容器發送SIGKILL
命令停止容器:
$docker kill redis-server
-s
或--signal
選項可以向容器發送指定的信號:
docker kill -s SIGINT my_ubuntu
docker cp
docker cp
命令負責在運行的容器和宿主機之間復制文件:
# format: docker cp FROM TO
$ docker cp test.txt my_ubuntu:/root/test.txt
$ docker cp my_ubuntu:/root/test.txt test.txt
docker rm
docker rm
用於刪除一個鏡像,通過鏡像ID或鏡像名來指定要刪除的鏡像, 默認情況下只能刪除已退出的鏡像:
$ docker stop my_ubuntu
$ docker rm my_ubuntu
-f
或--force
選項可以強制刪除運行中的鏡像。
-v
或--volumes
選項將刪除所有掛載的數據卷。
docker volume
docker volume
系列指令用於獨立的管理數據卷, 請先閱讀docker run:掛載數據卷
create
將宿主機上一個目錄創建為數據卷:
# format: docker volume create PATH
$ docker volume create myvol
ls
查看所有數據卷:
docker volume ls
DRIVER VOLUME NAME
local 0c28b79a9b3f
local myvol
inspect
查看某個數據卷的詳細信息:
docker volume inspect myvol
[
{
"CreatedAt": "2018-01-16T09:04:16Z",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/myvol/_data",
"Name": "myvol",
"Options": {},
"Scope": "local"
}
]
rm
刪除某個數據卷:
docker volume remove myvol
myvol
prune
刪除所有未被使用的數據卷:
docker volume prune
WARNING! This will remove all volumes not used by at least one container.
Are you sure you want to continue? [y/N] y
Total reclaimed space: 0B
使用數據卷
docker run
和docker create
命令可以使用-v
或--volume
選項來使用數據卷:
docker run -d -it -v myvol:/app ubuntu
docker會優先尋找數據卷列表中的myvol而不是當前目錄下的myvol子目錄。
若找不到myvol數據卷且當前目錄下也不存在myvol子目錄,docker會自動創建myvol數據卷不會再當前目錄下創建子目錄。
--mount
選項雖然語法復雜,但是可以配置更多選項:
docker run -it --mount source=my_volume,target=/app ubuntu
構建鏡像
上文中已經介紹了如何獲取和使用已有鏡像,接下來介紹如何構建自己的鏡像。
我們可以將自己的程序及其運行環境打包成Docker容器,部署到服務器或者共享給其它開發者免去配置環境的煩惱。
docker diff
前文已經提到過Docker鏡像采用分層結構,容器運行時無法修改鏡像的內容,而是在鏡像層上掛載了一個可讀寫層。
docker diff
命令可以查看容器對鏡像的修改:
$ docker run --name my_ubuntu -it ubuntu
# if exists: docker start -ai my_ubuntu
root@c10921921bfb:~# echo "Hello, from finley" > hello.txt
root@c10921921bfb:~# exit
➜ ~ docker diff my_ubuntu
C /root
A /root/.bash_history
A /root/hello.txt
我們打開一個ubuntu容器在/root
目錄中創建了hello.txt
文件, 並在其中寫入"Hello, from finley"。
通過docker diff
可以看出這個操作產生了三個影響:
- 工作目錄修改為
/root
, C代表變更工作目錄 - 命令歷史
.bash_history
文件改變, A代表文件或目錄內容發生改變 - 修改了文件
hello.txt
docker commit
docker commit
命令根據容器對鏡像的修改創建新的鏡像層
# format: docker commit CONTAINER REPOSITORY[:TAG]
$docker commit my_ubuntu my_ubuntu:2018_01_14
sha256:8096f47d8d6b80e52e617030938c7a83a9d50bafd73915d6952675e7126bb38a
$docker images
REPOSITORY | TAG | IMAGE ID | CREATED | SIZE |
---|---|---|---|---|
my_ubuntu | 2018_01_14 | 8096f47d8d6b | Less than a second ago | 111MB |
-a
或--author
選項可以指定容器的作者, -m
或--message
可以添加備注信息:
$docker commit \
-a "finley<finley@finley.pw>" \
-m "say hello" \
my_ubuntu my_ubuntu:2018_01_14
-c
或--change
選項可以添加一系列Dockerfile指令,關於Dockerfile的內容我們將在下文介紹。
docker tag
docker tag
命令用於為鏡像創建一個別名。
# format: docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]
$ docker tag ubuntu my_ubuntu
TARGET_IMAGE可以是私有倉庫REPOSITORY的URI,tag命令經常被用來協助將鏡像推送到私有倉庫。
docker history
docker history
命令用於顯示鏡像各層的信息,即查看commit歷史:
$ docker history ubuntu
Dockerfile
docker commit
命令雖然可以很方便的創建新的鏡像,但是我們在容器內操作(特別是嘗試性的操作)可能會在新鏡像內產生無關的內容,如命令歷史和日志等。
Dockerfile是控制鏡像構建過程的腳本,使用Dockerfile可以有效避免鏡像中包含無關內容。
Dockerfile中包含多行指令,每個指令會在鏡像中創建一層。
FROM nginx
RUN echo '<h1>Hello from finley!</h1>' > /usr/share/nginx/html/index.html
$ docker build -t nginx_hello .
Step 1/2 : FROM nginx
---> 3f8a4339aadd
Step 2/2 : RUN echo '<h1>Hello from finley!</h1>' > /usr/share/nginx/html/index.html
---> Running in 2b3c5df09f9c
---> a918263d0f2f
Removing intermediate container 2b3c5df09f9c
Successfully built a918263d0f2f
Successfully tagged nginx_hello:latest
$ docker run --name nginx_hello -p 80:80 -d --rm nginx_hello
$ curl localhost
<h1>Hello from finley!</h1>
上面示例中我們以nginx為基礎構建了一個新鏡像,並啟動它提供服務。
在具體介紹Dockerfile的指令之前,我們先介紹一下docker build
命令。
docker build
指令用於根據Dockerfile構建鏡像,通常該指令需要一個PATH參數來指定某個目錄做為構建上下文(context)。
-f
或--file
選項用於在構建上下文指定Dockerfile文件的路徑, 默認為PATH/Dockerfile
。
-t
或--tag
選項用於為鏡像指定標簽,默認情況下名稱仍然與基礎鏡像相同, 而-t NAME:TAG
則會指定容器的名稱。
除了使用一個目錄作為構建上下文之外,也可以使用tar壓縮包、git倉庫作為上下文, 甚至從標准輸入中讀取Dockerfile或tar進行構建。
接下來介紹常用的Dockerfile命令:
FROM
FROM
指令用於指定構建容器的基礎鏡像,標記鏡像的語法與docker pull
命令相同。
除了使用已存在的鏡像作為基礎鏡像之外還有一個特殊的鏡像scratch
, 它是一個空鏡像。
我們可以把靜態編譯的可執行文件放入空白鏡像中,由內核直接執行可以極大的降低鏡像的體積。
RUN
RUN
命令在鏡像中創建一個容器並執行指定指令,執行結束后commit修改作為鏡像的一層。
上文示例中的Step2展示了RUN
指令運行的過程:
- 創建一個中間容器(intermediate container): 2b3c5df09f9c
- 運行命令
echo '<h1>Hello from finley!</h1>' > /usr/share/nginx/html/index.html
- 將修改提交,產生新的容器: a918263d0f2f
- 刪除中間容器:2b3c5df09f9c
RUN
命令有兩種參數格式:
- 命令行式:
RUN ls -al
- 函數調用式:
RUN ["ls", "-al"]
在實際執行過程中命令行式指令會被映射為RUN ["sh", "-c", "ls", "-al"]
。
因為每個RUN
指令都會生成一層鏡像, 因此最好用&&
將多條指令連接而不是寫多條RUN
指令。
COPY
COPY from to
指令將文件或目錄從構建上下文(context)將文件復制到鏡像內部。
COPY ./config.json /root/config.json
WORKDIR
WORKDIR path
命令用於指定容器的默認工作目錄。
WORKDIR
命令對Dockerfile中的后續命令都有影響, 除非有另一條WORKDIR
命令修改了工作目錄
上文已經介紹過每條RUN
指令都會創建一個臨時容器, 因此RUN cd PATH &&...
只對同一條RUN
中的后續指令有效。
USER
USER
命令用於指定容器中的當前用戶:
RUN groupadd -r redis && useradd -r -g redis redis
USER redis
RUN redis-server
和WORKDIR
命令一樣,USER
指令直到下一條USER
指令之前始終有效。
CMD
CMD
命令用於指定啟動容器的指令, 和RUN
命令一樣有兩種格式。
EXPOSE
EXPOSE
命令用於聲明需要暴露哪些接口,注意EXPOSE
命令沒有建立端口映射, 需要在創建容器時啟動端口映射。
ENV
ENV
命令用於為容器設置環境變量:
ENV DEBUG=on VERSION='1.0'
ENTRYPOINT
ENTRYPOINT
指令和CMD一樣,用於指定啟動容器的指令。 當指定了ENTRYPOINT之后,CMD命令的內容將被作為參數傳遞給ENTRYPOINT指定的指令。
首先創建一個容器:
FROM ubuntu
CMD ls
$ docker build -t ls:v1 .
$ docker run --rm ls:v1
bin boot dev etc home lib ...
如果我們試圖傳遞-al
選項給ls以顯示更詳細的信息:
$ docker docker run --rm ls -al
container_linux.go:265: starting container process caused "exec: \"-al\": executable file not found in $PATH"
根據docker run
的語法我們試圖讓ls
鏡像執行-al
命令代替默認命令, 這當然行不通。
用ENTRYPOINT代替CMD:
FROM ubuntu
ENTRYPOINT ["ls"]
重新創建:
$ docker build -t ls:v2 .
$ docker run --rm ls:v2 -al
total 72
drwxr-xr-x 1 root root 4096 Jan 14 13:29 .
drwxr-xr-x 1 root root 4096 Jan 14 13:29 ..
-rwxr-xr-x 1 root root 0 Jan 14 13:29 .dockerenv
drwxr-xr-x 2 root root 4096 Dec 1 21:53 bin
drwxr-xr-x 2 root root 4096 Apr 12 2016 boot
drwxr-xr-x 5 root root 340 Jan 14 13:29 dev
ENTRYPOINT通常用於在執行容器CMD做一些准備工作, 比如官方redis鏡像中:
FROM alpine:3.4
...
RUN addgroup -S redis && adduser -S -G redis redis
...
ENTRYPOINT ["docker-entrypoint.sh"]
docker run --entrypoint CMD
可以用來更改容器的ENTRYPOINT。
共享鏡像
現在我們已經了解如何構造自己的鏡像,接下來的問題是如何將鏡像分享給其它開發者或者部署到服務器。
docker export
docker export
命令用於導出容器的快照, 默認輸出到標准輸出流,需要將輸出重定向到文件進行保存。
# format: docker export CONTAINER > TAR
$ docker export my_ubuntu > my_ubuntu.tar
或者使用-o
或--output
選項
$ docker export -o my_ubuntu.tar my_ubuntu
docker export
命令導出時會新建一個空白鏡像然后將容器文件系統的內容寫入,不包含容器基礎鏡像各層和tag等信息。
docker save
docker save
用於將鏡像存儲為tar壓縮包,默認導出到標准輸出流需要重定向以寫入文件
# format: docker save IMAGE > TAR
$ docker save ubuntu > ubuntu.tar
或者使用-o
或--output
選項
$ docker save -o ubuntu.tar ubuntu
docker save
命令會將鏡像各層及其tag信息導出到文件。
docker import
docker import
命令用於導入tar格式的快照,生成的鏡像只有一層不導入各層和tag信息。
$ docker import my_ubuntu.tar my_ubuntu:v2
該指令與docker export
相對用於導入容器鏡像,但也可以導入docker save
生成的tar文件。
docker load
docker load
用於導入tar格式的鏡像副本,導入時保留各層和tag等元數據, 因此不需要指定鏡像名和tag。
默認從標准輸入流導入:
$ cat ubuntu.tar | docker load
或者使用-i
或--input
選項:
docker load -i ubuntu.tar
docker load
命令與docker save
命令相對,因為缺少元數據不能導入docker export
生成的tar文件。
Registry
雖然docker支持以文件的形式導入導出鏡像,但這種方式對於共享鏡像來說仍十分不便。
Docker官方提供了一個registry鏡像,我們可以使用它輕松搭建私有倉庫。
$ mkdir registry
$ docker run -d \
-p 5000:5000 \
-v registry:/var/lib/registry \
registry
-v
選項將./registry
目錄掛載到了容器中,這個目錄將用來實際存儲鏡像。
docker默認采用HTTPS方式推送和拉取鏡像,本文不再介紹如何為私有倉庫配置HTTPS證書。
私有倉庫可以配置權限認證,僅授權用戶才能推送或拉取鏡像,這里也不再詳細介紹權限認證相關功能。
docker push
docker push
命令用於將鏡像推送到倉庫,推送到的倉庫由鏡像的REPOSITORY屬性決定。
$ docker tag ubuntu 127.0.0.1:5000/finley/my_ubuntu:v1
$ docker docker push 127.0.0.1:5000/finley/my_ubuntu:v1
The push refers to a repository [127.0.0.1:5000/finley/my_ubuntu]
f17fc24fb8d0: Mounted from registry/my_ubuntu
6458f770d435: Mounted from registry/my_ubuntu
5a876f8f1a3d: Mounted from registry/my_ubuntu
d2f8c05d353b: Mounted from registry/my_ubuntu
48e0baf45d4d: Mounted from registry/my_ubuntu
v1: digest: sha256:f871d0805ee3ce1c52b0608108dbdf1b447a34d22d5c7278a3a9dd78fc12c663 size: 1357
$ curl 127.0.0.1:5000/v2/_catalog
{"repositories":["registry/my_ubuntu"]}
首先,使用docker tag
命令給想要推送的鏡像unbuntu:latest
創建一個別名127.0.0.1:5000/registry/my_ubuntu:v1
。
在這個URI中, 127.0.0.1:5000
是Registry的地址,finley
是私有倉庫中的一個命名空間, my_ubuntu
是私有倉庫中的一個REPOSITORY。
訪問127.0.0.1:5000/v2/_catalog
來查看私有倉庫中的鏡像。