不久前,曾聽到一個神奇的需求:希望在無網路的環境下使用 Docker。這種需求,筆者學 Docker 以來還是第一次聽到。
第二階段的最後一天,來說明如何在無網路的環境下分享 Docker image 與啟動 container。
第一步就是麻煩事了,筆者個人會使用 Vagrant 包 Docker box 再到機器上匯入,接著啟動 VM 後,如果搞砸了,只要 VM 砍掉重練即可。
Vagrant 有安裝檔案,另外再配上 VirtualBox 安裝檔,即可完成虛擬機環境的準備。
再來是 Vagrant Box 準備,參考安裝 Docker 環境提到的 Vagrant 環境建置。使用 vagrant up
建好 VM 後,再使用 vagrant package
指令打包成 .box 檔:
$ vagrant package
==> default: Attempting graceful shutdown of VM...
==> default: Clearing any previously set forwarded ports...
==> default: Exporting VM...
==> default: Compressing package to: /Users/miles/GitHub/MilesChou/docker/package.box
這個動作跟
docker commit
非常像。
到目前為止會有三份檔案如下:
接著放入隨身碟後,就可以移架到主機上安裝 Vagrant 與 VirtualBox 了。
Docker VM 檔是 .box 格式,匯入 Vagrant 使用 vagrant box add
指令:
$ vagrant box add --name docker ./package.box
==> box: Box file was not detected as metadata. Adding it directly...
==> box: Adding box 'docker' (v0) for provider:
box: Unpacking necessary files from: file:///Users/miles/GitHub/MilesChou/docker/package.box
==> box: Successfully added box 'docker' (v0) for 'virtualbox'!
完成匯入後,因 box 名稱不同,因此要換一個名字。依上面的範例,要使用 docker
這個 box 名稱:
Vagrant.configure("2") do |config|
config.vm.box = "docker"
# config.vm.network "forwarded_port", guest: 80, host: 8080
# config.vm.network "private_network", ip: "192.168.33.10"
config.vm.provider "virtualbox" do |vb|
vb.memory = "1024"
end
end
這裡要注意的是,forwarded_port
與之前提到的 Port forwarding 概念完全相同,只是 container 換成了 VM。以下是設定範例參考:
Image | Container port | Docker Run | Vagrantfile | Host port |
---|---|---|---|---|
httpd | 80 | 8080:80 | guest: 8080, host: 8080 | 8080 |
mysql | 3306 | 3306:3306 | guest: 3306, host: 3306 | 3306 |
registry | 5000 | 443:5000 | guest: 443, host: 1443 | 1443 |
當調整完設定後,只要下 vagrant reload
即可重載設定。
再來就是 Docker image 檔該如何產生與匯入了,Docker 提供兩種方法可以產生與匯入 tar 檔,以下簡單做說明。
docker save
與 docker load
昨天有說明如何把 Docker image push 到 registry 上。類似的原理,image 的內容是可以被打包起來的。這裡準備了一個簡單的 Dockerfile 來實驗:
FROM alpine
RUN apk add --no-cache vim
# 使用上面的 Dockerfile build image
docker build -t vim .
Build image 後,使用 docker save
可以將 image 內容輸出成 tar 檔:
# 確認 image 的 SHA256 為 bcdbe56cd759
docker images vim
# 將 image 保存成 tar
docker save vim > vim.tar
# 移除 image
docker rmi vim
# 確認 image 不在
docker images vim
# 把剛剛保存的 image 再載入 repository
docker load < vim.tar
# 確認 image 的 SHA256
docker images vim
這裡可以看到 SHA256 完全一致,這代表 image 內容有被完整保存下來的,而且大小是很接近的:
$ ls -lh vim.tar
-rw-r--r-- 1 miles staff 32M 10 5 01:34 vim.tar
$ docker images vim
REPOSITORY TAG IMAGE ID CREATED SIZE
vim latest bcdbe56cd759 17 hours ago 32.5MB
Docker 有提供 docker history
指令可以查看 image layer 的資訊:
$ docker history vim
IMAGE CREATED CREATED BY SIZE COMMENT
bcdbe56cd759 21 hours ago /bin/sh -c apk add --no-cache vim 26.9MB
<missing> 4 months ago /bin/sh -c #(nop) CMD ["/bin/sh"] 0B
<missing> 4 months ago /bin/sh -c #(nop) ADD file:c92c248239f8c7b9b… 5.57MB
最上面很明顯是 Vim 的,而下面兩行 missing 即為 Alpine 的 layer,可以用同樣的指令查 Alpine 會更清楚:
$ docker history alpine
IMAGE CREATED CREATED BY SIZE COMMENT
a24bb4013296 4 months ago /bin/sh -c #(nop) CMD ["/bin/sh"] 0B
<missing> 4 months ago /bin/sh -c #(nop) ADD file:c92c248239f8c7b9b… 5.57MB
這裡再做另一個實驗,把 Vim image 與 Alpine image 都移除,再匯入 vim.tar
:
# 移除 image
docker rmi vim alpine
# 把剛剛保存的 image 再載入 repository
docker load < vim.tar
# 確認 image 的 SHA256
docker images vim
這代表匯出的 vim.tar
會完整地包含所有 FROM 的 image 資訊。
為什麼要特別確認這件事,因為下一個指令會不一樣。
docker export
與 docker import
一樣是輸出 tar 檔,但 docker export
的目標是 container,它能將 container 輸出成 tar 檔:
# 執行一個 container,注意這裡沒有 --rm
docker run -it --name vim alpine
# 做點檔案系統的改變再離開
apk add --no-cache vim && exit
# 把 container 的檔案系統匯出 tar
docker export vim > vim-export.tar
# 從 tar 導入檔案系統
docker import - vim < vim-export.tar
# 再多做一次看看
docker import - vim < vim-export.tar
最後的 docker import
可以發現,執行兩次的 digest 是不一樣的。這代表 docker export
產出的 tar 檔,其實是沒有包含 FROM image 資訊的。
docker history
資訊如下:
$ docker history vim
IMAGE CREATED CREATED BY SIZE COMMENT
407b5b2c246a 4 hours ago 32.5MB Imported from -
只有一層 layer,Alpine 的檔案系統已經被包含在這裡面了。
save
v.s. export
docker save
與 docker export
的目的都一樣是把 Docker 的系統保存成檔案,但結果是不大一樣的。以下做個簡單的比較:
docker save |
docker export |
---|---|
將 image 打包 | 將 container 打包 |
完整保留 image layer 資訊 | 只會有一層 layer |
可以保存多個 image 在一個 tar 檔裡 | 只能保存一個 container 在一個 tar 檔裡 |
使用上,筆者認為 docker save
用途很明確是分享 image。而 docker export
比較像是想把目前運作中的 container 狀態保存下來,拿到別台機器上做別的用途,比方說 debug 等。
上面的範例是有把 container 停止才下
docker export
指令,實際上也可以用在執行中的 container。
docker save
把 image 使用 tar 打包輸出。預設會使用標準輸出(STDOUT),用法:
docker save [OPTIONS] IMAGE [IMAGE...]
因為是使用標準輸出,所以會使用導出(>
)的方法輸出檔案,也可以使用下面這個參數來取代導出:
-o|--output
不使用標準輸出,改使用輸出檔案,後面接檔案名稱即可docker load
把打包的 tar 檔載入到 Docker repository 裡。預設會使用標準輸入(STDIN),用法:
docker image load [OPTIONS]
類似 save,只是它是使用導入(<
)來讀取檔案內容,一樣可以使用參數來取代導入:
-i|--input
不使用標準輸入,改成直接指定檔案docker export
把 container 的檔案系統使用 tar 打包輸出。預設會使用標準輸出(STDOUT),用法:
docker export [OPTIONS] CONTAINER
與 docker save
類似,也有使用導出(>
)的方法輸出檔案,也有參數可以取代導出:
docker import
把打包的 tar 檔的檔案系統導入到 Docker repository 裡。預設會使用標準輸入(STDIN),用法:
docker image import [OPTIONS] file|URL|- [REPOSITORY[:TAG]]
與 docker load
類似,使用導入(<
)來讀取檔案內容,但它沒有選項可以取代導入,而是改成使用參數的方法。下面是使用導入與不使用的對照範例:
docker image import myimage < my-export.tar
docker image import my-export.tar myimage
基本操作 container 的方法,與 build image 的方法在這二十天裡已說明完了,這樣已可應付八成開發上遇到的問題。
明天會開始說明更詳細的部分,包括各元件的細節,或是維運相關的功能等。