1. 程式人生 > 實用技巧 >Docker容器技術之映象管理基礎(3)

Docker容器技術之映象管理基礎(3)

目錄

1. 映象的概念


映象可以理解為應用程式的集裝箱,而docker用來裝卸集裝箱。

docker映象含有啟動容器所需要的檔案系統及其內容,因此,其用於建立並啟動容器。

docker映象採用分層構建機制,最底層為bootfs,其上為rootfs

  • bootfs:用於系統引導的檔案系統,包括- bootloader和kernel,容器啟動完成後會被解除安裝以節約記憶體資源
  • rootfs:位於bootfs之上,表現為docker容器的根檔案系統
    • 傳統模式中,系統啟動之時,核心掛載rootfs會首先將其掛載為“只讀”模式,完整性自檢完成後將其重新掛載為讀寫模式
    • docker中,rootfs由核心掛載為“只讀”模式,而後通過“聯合掛載”技術額外掛載一個“可寫”層

注意:當刪除容器時,這個容器自有的“可寫”層會一起被刪除

2. docker映象層



位於下層的映象稱為父映象(parrent image),最底層的稱為基礎映象(base image);
最上層為“可讀寫”層,其下的均為“只讀”層。

3. docker儲存驅動


docker提供了多種儲存驅動來實現不同的方式儲存映象,下面是常用的幾種儲存驅動:

  • AUFS
  • OverlayFS
  • Devicemapper
  • Btrfs
  • VFS

3.1 AUFS

AUFS(AnotherUnionFS)是一種Union FS,是檔案級的儲存驅動。AUFS是一個能透明覆蓋一個或多個現有檔案系統的層狀檔案系統,把多層合併成檔案系統的單層表示。簡單來說就是支援將不同目錄掛載到同一個虛擬檔案系統下的檔案系統。這種檔案系統可以一層一層地疊加修改檔案。無論底下有多少層都是隻讀的,只有最上層的檔案系統是可寫的。當需要修改一個檔案時,AUFS建立該檔案的一個副本,使用CoW將檔案從只讀層複製到可寫層進行修改,結果也儲存在可寫層。在Docker中,底下的只讀層就是image,可寫層就是Container。

AUFS檔案系統據說有3W行程式碼,而ext4檔案系統卻只有4000-5000行左右程式碼,這些程式碼是要被整合進核心的,後來AUFS申請要被合併進核心程式碼的時候,linuz覺得它這程式碼太過臃腫,於是拒絕了。因此AUFS這個檔案系統一直以來就不是linux核心中自有的檔案系統,想用AUFS這個檔案系統的話,必須自己向核心打補丁並去編譯使用它,但redhat系列的作業系統一向以穩定著稱,不會幹這種出格的事,所以在redhat系列作業系統中使用AUFS並無可能。而ubuntu上的docker預設使用的就是AUFS。

3.2 OverlayFS

Overlay是Linux核心3.18後支援的,也是一種Union FS,和AUFS的多層不同的是Overlay只有兩層:一個upper檔案系統和一個lower檔案系統,分別代表Docker的映象層和容器層。當需要修改一個檔案時,使用CoW將檔案從只讀的lower複製到可寫的upper進行修改,結果也儲存在upper層。在Docker中,底下的只讀層就是image,可寫層就是Container。目前最新的OverlayFS為Overlay2。

AUFS和Overlay都是聯合檔案系統,但AUFS有多層,而Overlay只有兩層,所以在做寫時複製操作時,如果檔案比較大且存在比較低的層,則AUSF會慢一些。而且Overlay併入了linux kernel mainline,AUFS沒有。目前AUFS已基本被淘汰。

3.3 DeviceMapper

Device mapper是Linux核心2.6.9後支援的,提供的一種從邏輯裝置到物理裝置的對映框架機制,在該機制下,使用者可以很方便的根據自己的需要制定實現儲存資源的管理策略。AUFS和OverlayFS都是檔案級儲存,而Device mapper是塊級儲存,所有的操作都是直接對塊進行操作,而不是檔案。Device mapper驅動會先在塊裝置上建立一個資源池,然後在資源池上建立一個帶有檔案系統的基本裝置,所有映象都是這個基本裝置的快照,而容器則是映象的快照。所以在容器裡看到檔案系統是資源池上基本裝置的檔案系統的快照,並沒有為容器分配空間。當要寫入一個新檔案時,在容器的映象內為其分配新的塊並寫入資料,這個叫用時分配。當要修改已有檔案時,再使用CoW為容器快照分配塊空間,將要修改的資料複製到在容器快照中新的塊裡再進行修改。

OverlayFS是檔案級儲存,Device mapper是塊級儲存,當檔案特別大而修改的內容很小,Overlay不管修改的內容大小都會複製整個檔案,對大檔案進行修改顯然要比小檔案要消耗更多的時間,而塊級無論是大檔案還是小檔案都只複製需要修改的塊,並不是整個檔案,在這種場景下,顯然device mapper要快一些。因為塊級的是直接訪問邏輯盤,適合IO密集的場景。而對於程式內部複雜,大併發但少IO的場景,Overlay的效能相對要強一些。

4. docker registry


啟動容器時,docker daemon會試圖從本地獲取相關的映象,本地映象不存在時,其將從Registry中下載該映象並儲存到本地。

Registry用於儲存docker映象,包括映象的層次結構和元資料。使用者可以自建Registry,亦可使用官方的Docker Hub。

docker registry的分類:

  • Sponsor Registry:第三方的Registry,供客戶和Docker社群使用
  • Mirror Registry:第三方的Registry,只讓客戶使用
  • Vendor Registry:由釋出docker映象的供應商提供的registry
  • Private Registry:通過設有防火牆和額外的安全層的私有實體提供的registry

docker registry的組成:

  • Repository
    • 由某特定的docker映象的所有迭代版本組成的映象倉庫
    • 一個Registry中可以存在多個Repository
      • Repository可分為“頂層倉庫”和“使用者倉庫”
      • 使用者倉庫名稱格式為“使用者名稱/倉庫名”
    • 每個倉庫可包含多個Tag(標籤),每個標籤對應一個映象
  • Index
    • 維護使用者帳戶、映象的檢驗以及公共名稱空間的資訊
    • 相當於為Registry提供了一個完成使用者認證等功能的檢索介面

Docker Registry中的映象通常由開發人員製作,而後推送至“公共”或“私有”Registry上儲存,供其他人員使用,例如“部署”到生產環境。

5. 製作Docker映象


多數情況下,我們做映象是基於別人已存在的某個基礎映象來實現的,我們把它稱為base image。比如一個純淨版的最小化的centos、ubuntu或debian。

5.1 獲取映象

獲取映象的方式:Docker Hub

要從遠端Registry(如您自己的Docker Registry)獲取Docker影象並將其新增到您的本地系統,需要使用docker pull命令

語法:docker pull <registry>[:<port>]/[<namespace>/]<name>:<tag>

例:
[root@node02 ~]# docker pull httpd

5.2 5.2 生成映象

映象的生成途徑:

  • Dockerfile
  • 基於容器製作
  • Docker Hub automated builds

基於容器製作映象

語法:

docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]

OPTIONS:

-a    指定作者
-c    對建立的映象應用Dockerfile指令
-m    提交訊息
-p    提交期間暫停容器

下載映象並進行修改

[root@node02 ~]# docker pull busybox
Using default tag: latest
latest: Pulling from library/busybox
61c5ed1cbdf8: Pull complete 
Digest: sha256:4f47c01fa91355af2865ac10fef5bf6ec9c7f42ad2321377c21e844427972977
Status: Downloaded newer image for busybox:latest
docker.io/library/busybox:latest
[root@node02 ~]# docker run -it busybox
/ # ls
bin   dev   etc   home  proc  root  sys   tmp   usr   var
/ # mkdir longnian
/ # echo 'This is longnian.' > longnian/index.html
/ # cat longnian/index.html 
This is longnian.

在建立映象時,我們不能關閉容器,必須使其處於執行狀態,所以我們必須要另起一個終端,然後執行

[root@node02 ~]# docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
48379c69153b        busybox             "sh"                55 seconds ago      Up 54 seconds                           eager_goldwasser
[root@node02 ~]# docker commit -p 48379c69153b
sha256:f82a1ea35100f61d1ea945be76d7f4319fecd574f86c698cc562716c09ae5b2f
[root@node02 ~]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
<none>              <none>              f82a1ea35100        4 seconds ago       1.22MB
busybox             latest              018c9d7b792b        4 weeks ago

5.3 上傳映象

要在Docker Hub上建立一個倉庫,然後再將我們做好的映象push上去

給映象打標籤,這裡的標籤要和我們上一步建立的倉庫名字一樣

[root@node02 ~]# docker tag f82a1ea35100 dragonyear22/nginx:v1.0
[root@node02 ~]# docker images
REPOSITORY           TAG                 IMAGE ID            CREATED              SIZE
dragonyear22         latest              f82a1ea35100        About a minute ago   1.22MB
dragonyear22/nginx   v1.0                f82a1ea35100        About a minute ago   1.22MB
busybox              latest              018c9d7b792b        4 weeks ago

登陸docker hub,將映象上傳至docker hub

[root@node02 ~]# docker login
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: dragonyear22
Password: 
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded
[root@node02 ~]# docker push dragonyear22/nginx:v1.0
The push refers to repository [docker.io/dragonyear22/nginx]
350fb6a9b600: Pushed 
514c3a3e64d4: Mounted from library/busybox 
v1.0: digest: sha256:31893141046ceff6cd9cfb78ad751b244d7ebefb6d9b0be06839e6e171c9afdd size: 734

在docker hub上檢視是否上傳成功

5.4 使用新映象驗證

使用新生成的映象建立容器

[root@node02 ~]# 
[root@node02 ~]# docker run -it dragonyear22/nginx:v1.0
/ # ls
bin       etc       longnian  root      tmp       var
dev       home      proc      sys       usr
/ # cat longnian/index.html 
This is longnian.

由此可見,新生成的映象中是包含了新增的內容的,但是此時有一個問題,那就是容器預設要啟動的程序是什麼?在這裡,預設情況下是啟動的sh程序,但我們是要啟動一個http站點,所以我們要在建立映象時將容器預設啟動的程序設為httpd,這樣一來我們就可以通過新生成的映象來快速構建一個簡單的http站點了。

使用docker inspect命令檢視b1容器啟動的預設程序是什麼

[root@node02 ~]# docker inspect 48379c69153b
[
.......................
        "Cmd":  [
            "sh"
        ]
.......................
                    "Gateway": "172.17.0.1",
                    "IPAddress": "172.17.0.2",
.......................
]

重新生成映象並上傳

[root@node02 ~]# docker commit -a 'dragonyear22' -c 'CMD ["/bin/httpd","-f","-h","/longnian"]' -p t1 dragonyear22/nginx:v0.2
sha256:38e4f740ba20e117a9dfcf3b22460b9e200d2d97df39800936eebd89e16da7a8
[root@node02 ~]# docker push dragonyear22/nginx:v0.2
The push refers to repository [docker.io/dragonyear22/nginx]
f1e9eb7a4fab: Pushed 
350fb6a9b600: Layer already exists 
514c3a3e64d4: Layer already exists 
v0.2: digest: sha256:ce9e32a5ef4708645227f2545d9edacea70220aa8a3bd2cbf9434a97fc28edfd size: 941

使用新生成的映象建立容器

[root@node02 ~]# docker run -d dragonyear22/nginx:v0.2
68bf73ee67f9aca34f3991749f65d29a9f9a627c3ae8c126c90b1c6114f25a75
[root@node02 ~]# docker container ls
CONTAINER ID        IMAGE                     COMMAND                  CREATED             STATUS              PORTS               NAMES
68bf73ee67f9        dragonyear22/nginx:v0.2   "/bin/httpd -f -h /l…"   4 seconds ago       Up 3 seconds                            naughty_bohr
48379c69153b        busybox                   "sh"                     27 minutes ago      Up 27 minutes                           eager_goldwasser

使用docker inspect命令檢視容器啟動的預設程序是什麼,以及其IP地址,然後用curl命令訪問該IP,看是否能訪問到網頁

.......................
            ],
            "Cmd": [
                "/bin/httpd",
                "-f",
                "-h",
                "/longnian"
.......................
            "Gateway": "172.17.0.1",
            "GlobalIPv6Address": "",
            "GlobalIPv6PrefixLen": 0,
            "IPAddress": "172.17.0.3",
.......................
]
[root@node02 ~]# curl 172.17.0.3
This is longnian.

6. 映象的匯入與匯出

docker中我們使用docker save進行匯出,使用docker load進行匯入。

在已生成映象的主機上執行docker save匯出映象

[root@node02 ~]# docker save -o images.gz dragonyear22/nginx:v0.2
[root@node02 ~]# ls
'anaconda-ks.cfg  images.gz

在另一臺沒有映象的主機上執行docker load匯入映象

[root@node01 ~]# docker load -i images.gz