共計 10839 個字符,預計需要花費 28 分鐘才能閱讀完成。
本篇文章為大家展示了怎樣進行 Docker 鏡像管理的分析,內容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細介紹希望你能有所收獲。
運行容器示例 Nginx
直接下載鏡像并啟動容器,這里選擇 alpine 版的:
$ docker run --name web1 -p 8001:80 -d nginx:alpine
Unable to find image nginx:alpine locally
alpine: Pulling from library/nginx
e7c96db7181b: Downloading
3fb6217217ef: Download complete
alpine: Pulling from library/nginx
e7c96db7181b: Pull complete
3fb6217217ef: Pull complete
Digest: sha256:17bd1698318e9c0f9ba2c5ed49f53d690684dab7fe3e8019b855c352528d57be
Status: Downloaded newer image for nginx:alpine
01c17a72e943e93d71b56b433bea7a3d6ffa1f848dc3947f2adaf2bb2e3e7fee
$
啟動參數說明:
-d,表示啟動容器后在宿主機的后臺運行。
-p,端口映射,將宿主機的 8001 端口,映射到容器內部的 80 端口。端口映射是 network 的內容,之后會詳細說明。
查看啟動的容器:
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f57cd5f9d50f nginx:alpine nginx -g daemon of… 13 minutes ago Up 12 minutes 0.0.0.0:8001- 80/tcp web1
$
可以看到端口和端口映射的情況。
這里主要關注 COMMAND,上面的顯示被截斷了:
$ docker container ls --no-trunc --format {{.Command}}
nginx -g daemon off;
$
這里啟動 nginx 加了參數,daemon off 字面的意思就是關閉守護進程。這是為了讓 nginx 在前臺運行。
如果是 nginx 默認的啟動方式,那么 nginx 程序將在后臺運行,一旦 nginx 啟動完就沒有任何程序了,結果容器也就退出了。
在容器中執行任何程序或服務,一定不能在容器中運行在后臺。只要運行在后臺,一啟動就會終止。
既然 Nginx 已經啟動,就可以直接用瀏覽器訪問了。并且做了端口映射,所以可以直接通過宿主機的端口來進行訪問:http://[宿主機的 IP 地址]:8001。
容器的日志
每一個容器的目的只是為了運行一個程序,這個程序就是容器的主進程 PID=1。傳統的程序的日志一般是保存在日志文件中的,但是容器中沒有這個必要。因為現在整個容器就只為了運行一個進程,日志就可以直接打印在控制臺上了,就是程序直接在前臺運行的效果。
使用下面的命令可以查看日志:
$ docker container logs web1
查看后,訪問幾次頁面再看下是否有訪問日志刷新。
redis
首先,直接啟動一個 redis:
$ docker container run --name redis -d redis:alpine
容器啟動后,依然是停留在宿主機的命令行界面。
進入容器內部
現在需要進入到容器內部進行操作,就像之前的 busybox 那樣。但是,這次容量內部運行的是一個 redis-server 的程序,并且一個容器內部一般只運行一個程序。所以容器里并沒有 shell。
這里和之前的 busybox 容器的情況不同,在 busybox 容器內部就有一個 shell。所以直接進入是沒有任何終端界面的。這里需要啟動一個 shell 然后進入:
$ docker container exec -it redis /bin/sh
/data # ps
PID USER TIME COMMAND
1 redis 0:00 redis-server
12 root 0:00 /bin/sh
23 root 0:00 ps
/data #
進入并且執行命令查看當前容器內的進程。
這里看到,除了 ps 命令,還有原本的 redis-server 以及進入容器時啟動的 shell。所以在容器內部運行多個進程也是可以的,現在就是這個情況。不過一般也就這在這種場景下需要在容器中運行多個進程。
執行其他操作
既然都進來了,就運行些命令。看下系統的時間:
/data # date
Tue Jul 16 13:03:05 UTC 2019
/data #
時間沒問題,不過時區不對,這個略過。
查看端口監聽情況:
/data # netstat -tnl
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:6379 0.0.0.0:* LISTEN
tcp 0 0 :::6379 :::* LISTEN
/data #
使用 redis-cli 命令:
/data # redis-cli
127.0.0.1:6379 set age 23
127.0.0.1:6379 set name Adam
127.0.0.1:6379 keys *
1) name
2) age
127.0.0.1:6379 exit
/data # exit
$
這里看到 redis-cli 自帶用戶界面,所以不用啟動 /bin/sh 也能直接進來:
$ docker container exec -it redis redis-cli
127.0.0.1:6379 exit
$
Docker 鏡像
Docker 鏡像的基礎知識。
鏡像啟動
docker 鏡像含有啟動容器所需要的文件系統及其內容,因此,其用于創建并啟動 docker 容器。
采用分層構建機制,最底層為 bootfs,次之為 rootfs:
bootfs: 用于系統引導的文件系統,包括 bootloader 和 kernel, 容器啟動完成后會被卸載以節約內存資源
rootfs: 位于 bootfs 之上,表現為 docker 容器的根文件系統:
傳統模式中,系統啟動之時,內核掛載 rootfs 時會首先將其掛載為“只讀”模式,完整性自檢完成后將其重新掛載為讀寫模式
docker 中,rootfs 由內核掛載為“只讀”模式,而后通過 聯合掛載“技術額外掛載一個”可寫“層
啟動一個 busybox 容器,命令 ls 查看容器內部,擁有完整意義上的文件系統:
$ docker container run --name shell -it busybox
/ # ls
bin dev etc home proc root sys tmp usr var
/ # exit
$
分層構建
分層構建的鏡像:
位于下層的鏡像為父鏡像(parent image),最底層的稱為基礎鏡像(base image)
最上層為 可讀寫”層,其下的均為 只讀“層
如圖,是一個 Apache 鏡像。最底層是一個 Debian 的基礎鏡像,一個純凈的操作系統。在系統之上,添加了一個 emacs,這是一個代碼編輯器。再然后添加了一個 Apache。這里每添加一個軟件都是一個獨立的層次。
最最下面的 bootfs,在容器啟動時,一旦引導完 rootfs 就會被卸載并移除(從內存中移除)。
對于一個容器,所有寫操作,只能在最上層的可讀寫層進行。如果容器刪除了,這個最上面的可讀寫層也會一起被刪除。
操作系統基礎鏡像
關于 Linux 操作系統的基礎鏡像,可以參考下表來選擇合適的基礎鏡像:
busybox: 臨時測試用
alpine: 主要用于測試,也可用于生產環境
centos: 主要用于生產環境,支持 CentOS/Red Hat,常用于追求穩定性的企業應用
ubuntu: 主要用于生產環境,常用于人工智能計算和企業應用
debian: 主要用于生產環境
推薦使用 Alpine 鏡像,因為它被嚴格控制并保持最小尺寸(目前小于 5MB),但它仍然是一個完整的發行版。
alpine 的好處主要是小,并且基本功能全。用于測試是非常方便的,而且生產上也是可以用。雖然不建議這么做,主要是因為缺少調試工具。
busybox 的鏡像比 alpine 更小,它并不是一個系統發行版。最初這個工具是為了在一張軟盤上創建一個可引導的 GNU/Linux 系統,這可以用作安裝盤和急救盤。它是一個集成了三百多個最常用 Linux 命令和工具的軟件。所以如果是需要啟動一個容器并運行一些系統的工具和命令,那么可以使用這個作為基礎鏡像。
另外 3 個就是常用的 Linux 發行版,推薦在生產系統上用。鏡像大也不是什么問題,因為容器是分層構建的,所以本地的多個鏡像理論上是共用同一個基礎鏡像。
文件系統
Docker 鏡像的分層構建和聯合掛載依賴于它的專有文件系統。
aufs
在早期這個文件系統是 aufs(advanced multi-layered unification filesystem),高級多層統一文件系統。
overlayfs
aufs 的競爭產品是 overlayfs,overlayfs 在 3.18 版本開始被合并到 Linux 內核。使用 docker info 命令可以找到當前使用的文件系統:
Storage Driver: overlay2
Backing Filesystem: xfs
Supports d_tpe: true
Native Overlay Diff: true
overlay2 是一種抽象的二級文件系統,它需要建構在本地文件系統之上。上面的信息顯示,這里作為基礎的本地文件系統是 xfs。
其他文件系統
docker 的分層鏡像,除了 aufs,還支持 btrfs,devicemapper 和 vfs 等。早期默認支持的文件系統:
Ubuntu 系統,默認使用 aufs
CentOS 系統,默認使用 devicemapper
Device Mapper 是 Linux2.6 內核中支持邏輯卷管理的通用設備映射機制,它為實現用于存儲資源管理的塊設備驅動提供了一個高度模塊化的內核構架。
Docker Registry
最著名的 Registry 就是 Docker Hub:https://hub.docker.com/
其他的和有比如這個 Quay:https://quay.io/
啟動容器時,會先試圖從本地獲取相關的鏡像。如果本地鏡像不存在,再從 Registry 中下載鏡像并保存到本地。
一個 Registry 通常由 2 部分組成:
Repository
Index
Repository(倉庫)
Repository,由特定的 docker 鏡像的所有迭代版本組成的鏡像倉庫。一個 Registry 中可以存在多個 Repository。每個倉庫可以包含多個 Tag(標簽),每個標簽對應一個鏡像。
Repository 可分為頂層倉庫和用戶倉庫,用戶倉庫名稱格式為“用戶名 / 倉庫名”。使用 docker search 命令看一下:
$ docker search --limit 3 nginx
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
nginx Official build of Nginx. 11704 [OK]
jwilder/nginx-proxy Automated Nginx reverse proxy for docker con… 1628 [OK]
bitnami/nginx Bitnami nginx Docker Image 69 [OK]
$
這里顯示了 3 個,第一個是沒有用戶名的屬于頂層倉庫。后面是用戶倉庫,可以看到分別屬于的用戶名。
Index
Idxex 的作用:
維護用戶賬戶、鏡像的校驗以及公共命名空間的信息
相當于為 Registry 提供了一個完成用戶認證功能的檢索接口
云原生
這里只是簡單的提一下這個概念,主要是程序配置文件的問題。
鏡像的使用有一個問題,就是鏡像內部使用的配置信息。配置信息可以直接注入在鏡像里,但是這樣就要為不同的配置生成好多個不同版本的鏡像。
云原生是一種為了云計算環境運行而生的應用程序,并且可以解決不同配置的信息的問題。
以 Nginx 為例,傳統的開發運行在服務器上的程序,使用配置文件來管理配置。如果把它托管到容器云上運行,就會有諸多不便之處,最大的問題就是修改配置文件。
而那些云原生開發的程序,會使用對于云計算場景方便的接口來提供配置邏輯。具體到容器,相當于為應用程序加了一層外殼,再去操作里面的數據是不方便的。有一種做法是向容器傳入環境變量來傳遞配置信息,而配置則可以從環境變量加載自動注入到配置中。
云原生的大量配置都可以直接通過環境變量來獲取。
基于容器制作鏡像
使用命令 docker commint 會把容器最上面的可寫層,單獨創建為一個鏡像層,生成一個新的鏡像。
其他制作鏡像的方法,并且是制作鏡像的最主要的方法是,基于 Dockerfile 制作鏡像。這部分內容很重要也很多,需要單獨再寫一篇。
修改基礎鏡像的內容
基于 busybox,添加一個 httpd 的服務。
$ docker run --name httpd -it busybox
/ # echo h2 Hello world. Busybox httpd. /h2 /var/www/index.html
/ # cat /var/www/index.html
h2 Hello world. Busybox httpd. /h2
/ #
在容器內部進行修改
現在創建好了一個 html 文件,但是下次 docker 再啟動這個容器時這個文件是不會有的。現在需要做的是將之前做的改變保存好。
保存對容器的修改,生成新的鏡像
要保持這個容器的運行狀態,那就再另外開一個會話執行 commit 命令:
$ docker commit -p httpd
sha256:5bd093efd84001a2f7412292431ead5c760acef8f4e3a2298abf9f28aa7b3cd7
$
這里的 - p 參數是將容器處于暫停狀態,這樣可以防止鏡像制作過程中可能會有操作來改變容器的內容,建議 - p 參數都加上。
修改鏡像標簽
查看鏡像信息,新制作完成的鏡像信息如下:
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
none none 5bd093efd840 2 minutes ago 1.22MB
busybox latest e4db68de4ff2 4 weeks ago 1.22MB
$
由于制作的時候沒有指明倉庫名和標簽名,所以都是空。這兩個字段是允許為空的,這樣只能通過鏡像的 ID 來指明這個鏡像。
添加標簽信息
為了引用時方便,還是把倉庫名和標簽名加上吧:
$ docker image tag 5bd093efd840 myimg/httpd:v1
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
myimg/httpd v1 5bd093efd840 9 minutes ago 1.22MB
busybox latest e4db68de4ff2 4 weeks ago 1.22MB
$
一個鏡像可以有多個標簽,再加一個 latest 標簽:
$ docker image tag myimg/httpd:v1 myimg/httpd:latest
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
myimg/httpd latest 5bd093efd840 11 minutes ago 1.22MB
myimg/httpd v1 5bd093efd840 11 minutes ago 1.22MB
busybox latest e4db68de4ff2 4 weeks ago 1.22MB
$
這里可以確認下,2 個標簽的鏡像的 ID 是一樣的,所以這里是一個鏡像,只是為這個鏡像添加了 2 個標簽。
刪除標簽
沒有刪除標簽的命令,要刪除某個標簽,就直接用刪除鏡像的命令:
$ docker image tag myimg/httpd:v1 myimg/httpd:tmp1
$ docker image rm myimg/httpd:tmp1
Untagged: myimg/httpd:tmp1
$
這里又添加了一個標簽,然后再把這個標簽給刪除,命令執行結果顯示只是把指定的標簽去掉了。所以同一個鏡像打了多個標簽,本地存的只有一份。刪除某個標簽的鏡像,只是把這個標簽從鏡像標簽的列表里去除。之后刪除最后一個標簽的時候才是真正的刪除了一個鏡像。
修改鏡像默認啟動命令
使用 inspect 命令可以查看 docker 對象的底層信息。這里要找的是鏡像的底層信息中的默認啟動的命令,具體如下:
$ docker image inspect busybox
Cmd : [
sh
],
$
啟動時運行的命令是 sh,這個也是 busybox 鏡像默認啟動時運行的命令,因為制作新鏡像的時候沒有指定這個內容。
重新制作一版新的鏡像,這次要指定默認啟動時運行的命令:
$ docker commit -c CMD [httpd , -f , -h , /var/www/] -p httpd myimg/httpd:v2
sha256:850da6d87c65a2c6084cdbfcabbeeeaf6c13ddbb9fbb984fec5ca05cab38830d
$
參數 - c 不是用來指定命令的,而是指定所有要做的修改,當然這里只要修改啟動的命令。
httpd 命令參數說明
關于啟動命令 httpd -f -h /var/www/,這個具體可以去看 httpd 的參數說明。- f 表示作為守護進程也就是在前臺運行,而 - h 參數則是指定首頁的路徑。
啟動驗證鏡像
帶參數啟動鏡像:
$ docker container run --name httpd2 -d -p 8002:80 myimg/httpd:v2
80522bb422e16dae4ea052bcb36e51203f4d7b023fefdf3de4114598b3e95b29
$
鏡像啟動后,可以使用瀏覽器訪問宿主機的 IP 地址加上映射的端口號來打開這個頁面,比如:http://192.168.24.170:8002/
鏡像導入和導出
可以在已有鏡像的主機上把鏡像打包,將打包的文件復制到另外一臺主機上再把鏡像導入,就可以在主機之間傳遞鏡像了。這種方法不需要連接鏡像倉庫。
導出鏡像
導出鏡像就是將鏡像導出到一個 tar 包:
$ docker image save -o httpd.tar myimg/httpd
這條命令省略了 Tag 標簽,這樣就會把整個倉庫打包,就是打包所有的版本。
save 命令僅有一個參數 -o,就是指定導出的位置。如果沒有 - o 參數,那就是輸出到終端。不過也不能直接輸出到終端,這樣的做法是再通過輸出重定向來把內容保存起來。所以這條命令的效果是一樣的:
$ docker image save myimg/httpd httpd2.tar
可以加上標簽信息,就可以指定打包對應的 Tag 的鏡像。鏡像的參數可以傳入多個,就打包多個鏡像:
$ docker image save -o httpd3.tar myimg/httpd:v1 myimg/httpd:v2
導出為 tar 文件
導入的文件名可以任意指定,不過建議使用 tar 擴展名。這確實是一個 tar 包,使用 tar 命令來查看 tar 包內部的文件列表:
$ tar -tvf httpd.tar
-rw-r--r-- 0/0 1491 2019-07-18 15:10 25079c1e47bf896a028e55d715dc06e251f3efe53ca655ad63f6085ce6a465a8.json
-rw-r--r-- 0/0 1464 2019-07-18 15:05 7f36d8e3488df22381081d68c7f2215750167250114abd0b2f31d99e81a7bfd7.json
drwxr-xr-x 0/0 0 2019-07-18 15:05 957ac2430f81aaa485efe07e872a460156d73e48f53f31a9743ed0d5f0fa44d7/
-rw-r--r-- 0/0 3 2019-07-18 15:05 957ac2430f81aaa485efe07e872a460156d73e48f53f31a9743ed0d5f0fa44d7/VERSION
-rw-r--r-- 0/0 1081 2019-07-18 15:05 957ac2430f81aaa485efe07e872a460156d73e48f53f31a9743ed0d5f0fa44d7/json
-rw-r--r-- 0/0 4608 2019-07-18 15:05 957ac2430f81aaa485efe07e872a460156d73e48f53f31a9743ed0d5f0fa44d7/layer.tar
drwxr-xr-x 0/0 0 2019-07-18 15:10 a24e93a2c2b0548055a10d18f0c88dc138c57ee6f13020538bf80da2bfefc59f/
-rw-r--r-- 0/0 3 2019-07-18 15:10 a24e93a2c2b0548055a10d18f0c88dc138c57ee6f13020538bf80da2bfefc59f/VERSION
-rw-r--r-- 0/0 1107 2019-07-18 15:10 a24e93a2c2b0548055a10d18f0c88dc138c57ee6f13020538bf80da2bfefc59f/json
-rw-r--r-- 0/0 4608 2019-07-18 15:10 a24e93a2c2b0548055a10d18f0c88dc138c57ee6f13020538bf80da2bfefc59f/layer.tar
drwxr-xr-x 0/0 0 2019-07-18 15:05 dea411b43d1b59da62f22c37c8507e7757c2dd9a5467a523f92e612d88e83ae8/
-rw-r--r-- 0/0 3 2019-07-18 15:05 dea411b43d1b59da62f22c37c8507e7757c2dd9a5467a523f92e612d88e83ae8/VERSION
-rw-r--r-- 0/0 406 2019-07-18 15:05 dea411b43d1b59da62f22c37c8507e7757c2dd9a5467a523f92e612d88e83ae8/json
-rw-r--r-- 0/0 1441280 2019-07-18 15:05 dea411b43d1b59da62f22c37c8507e7757c2dd9a5467a523f92e612d88e83ae8/layer.tar
-rw-r--r-- 0/0 579 1970-01-01 08:00 manifest.json
-rw-r--r-- 0/0 238 1970-01-01 08:00 repositories
$
從 IMAGE ID 可以看出,這里確實是將 2 個版本的鏡像到打包了。
導出并壓縮
用下面的方法完成導出并壓縮:
$ docker save myimage:latest | gzip myimage_latest.tar.gz
導入鏡像
使用 load 命令可以方便的將鏡像導入:
$ docker image load -i httpd3.tar
6194458b07fc: Loading layer [==================================================] 1.441MB/1.441MB
dd0dd7cb79c9: Loading layer [==================================================] 4.608kB/4.608kB
Loaded image: myimg/httpd:latest
Loaded image: myimg/httpd:v1
698704828883: Loading layer [==================================================] 4.608kB/4.608kB
Loaded image: myimg/httpd:v2
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
myimg/httpd v2 25079c1e47bf 30 minutes ago 1.22MB
myimg/httpd latest 7f36d8e3488d 35 minutes ago 1.22MB
myimg/httpd v1 7f36d8e3488d 35 minutes ago 1.22MB
$
上面最后一次打包的文件是 httpd3.tar。執行打包命令的時候指定了 v1 和 v2 標簽,并沒有指定 latest 標簽。不過這里能看到所有的 3 個標簽。所以標簽只是一個標簽,一個鏡像可以有多個標簽,但是不同標簽的鏡像是同一個鏡像。
不使用 - i 參數的話,默認從標準輸出導入,用下面的方法也是一樣的:
$ docker image load httpd3.tar
導入壓縮文件
Load an image or repository from a tar archive (even if compressed with gzip, bzip2, or xz) from a file or STDIN. It restores both images and tags.
導入鏡像可以從 tar 文件,也可以從幾種壓縮文件直接導入。操作起來都一樣,程序會自己識別。應該是通過文件名后綴吧。
上述內容就是怎樣進行 Docker 鏡像管理的分析,你們學到知識或技能了嗎?如果還想學到更多技能或者豐富自己的知識儲備,歡迎關注丸趣 TV 行業資訊頻道。