Docker系列-(2) 鏡像製作與發佈

上篇文章引入了Docker的基本原理和操作,本節文章主要介紹如何製作Docker鏡像和發佈。

鏡像文件結構

Docker鏡像的本質是一系列文件的集合,這些文件依次疊加,形成了最後的鏡像文件,類似於下圖所示的結構,

Screen Shot 2019-11-24 at 11.42.39 AM.png

從底層往上,依次是文件系統層,操作系統層,專有鏡像層,讀寫層。

  • 啓動文件層:Docker啓動時的用到的文件系統,啓動完成後會自動脫離,用戶不會與這一層直接打交道。

  • 操作系統層:這一層主要是操作系統相關的一些文件,根據發行版本的不同,可能有CentsOS、Ubuntu等等。文件包含dev,/proc,/bin,/etc 等目錄, 是一個最小化的操作系統,很多工具都沒有提供,包括vi、wget、curl等。注意這一層不包含linux內核,只是可在任何滿足要求的linux內核上運行。

  • 專有鏡像層:一般各大軟件都會基於上面兩層製作專有的鏡像,比如nginx、tomcat等,都有專門的官方鏡像,可以直接在docker hub上下載。

  • 讀寫層:這是我們製作自己的鏡像時需要操作的層,是一個動態的運行環境,在後續鏡像製作中的比如ENV, Volume,cmd等操作最終落實到此運行環境中。

製作鏡像的實質就是修改讀寫層。當需要修改鏡像內的某個文件時,只對處於最上方的讀寫層進行了變動,不復寫下層已有文件系統的內容,已有文件在只讀層中的原始版本仍然存在,但會被讀寫層中的新版本文件所隱藏,當 docker commit 這個修改過的容器文件系統爲一個新的鏡像時,保存的內容僅爲最上層讀寫文件系統中被更新過的文件。

可以通過history命令查看鏡像層,

Screen Shot 2019-11-24 at 3.05.19 PM.png

製作鏡像

製作鏡像有兩種通用的方法,第一種是直接通過配置好的Container來生成鏡像;另外一種是通過Dockerfile的方式,基於已有的鏡像來生成新的鏡像,這種方法更爲常用。

配置Container製作鏡像

這裏以製作nginx的鏡像爲例,介紹整個製作流程。

1)下載基礎鏡像,這裏以Ubuntu作爲基礎鏡像。由於本地沒有鏡像可以先利用docker search獲取官方鏡像的名稱,然後docker pull將鏡像下載到本地。

Screen Shot 2019-11-24 at 3.18.12 PM.png

2)以交互方式啓動鏡像,方便在容器中安裝軟件。-it表示交互方式,/bin/bash爲指定啓動的終端。下圖可以看到已經成功進入到容器內部了。

docker run -it ubuntu:latest /bin/bash

Screen Shot 2019-11-24 at 3.21.23 PM.png

3)現在按照Nginx正常的安裝流程安裝即可,由於Ubuntu鏡像只是一個最小化的系統,可能你需要通過apt-get install來安裝一些需要的軟件。

4)退出容器,使用commit指令生成新的鏡像。

注意退出容器的時候,也有兩種方法,通常直接exit就可以,但是這樣容器也會關閉。如果不想關閉容器,只是退出終端,可以使用Ctrl+P+Q快捷鍵,此時退出後,容器依然在後臺運行。

直接運行docker commit同時指定容器id或者name,以及鏡像名就可以了,新的鏡像製作完成了。

docker commit e0c618df0979 ubuntu-nginx

Screen Shot 2019-11-24 at 3.32.46 PM.png

接下來可以通過正常的方式啓動鏡像了。

使用Dockerfile製作

上面介紹了手動進入容器內部,製作Docker鏡像的方式,一般比較繁瑣。通常我們會使用Dockerfile的方式製作鏡像,這種方式下我們需要編寫Dockerfile文件。

Dockerfile文件

Dockerfile是一個文本格式的配置文件,用戶可以使用Dockerfile快速創建自定義鏡像。

下面是一個簡單的Dockerfile文件,先將編譯生成的jar文件複製到容器,然後聲明容器暴露的端口,最後指定在啓動容器時需要運行的指令。

FROM openjdk:8
ADD ["target/bazaar-1.0.0.jar", "bazaar.jar"]
EXPOSE 1234
ENTRYPOINT ["java", "-jar", "/bazaar.jar"]

Dockerfile中常用的指令集有:

Picture1.png

  • FROM: 第一條指令必須爲FROM指令,用於指定基礎鏡像。
  • MAINTAINER: 指定維護者信息。
  • RUN: 會在shell終端運行命令。
  • EXPOSE: 格式爲 EXPOSE [ ...],聲明容器需要暴露的端口號。鏡像啓動可以通過 –P 或 -p 進行端口映射的綁定。
  • ENV: 指定一個環境變量,可以被後續的RUN引用,並且在容器中記錄該環境變量。
  • ADD: 該命令將複製指定的到容器中的。其中可以是Dockerfile所在目錄的一個相對路徑;也可以是tar文件(自動解壓)。
  • VOLUME: 格式爲 VOLUME [path]。創建一個可以從本地主機或其他容器掛載點,一般用來存放需要保持的數據。
  • USER: 指定運行容器時的用戶名,後續的RUN也會指定該用戶。
  • WORKDIR: 指定工作空間,後續命令都在此目錄下執行。

比較複雜的是CMD與ENTRYPOINT的對比,兩者都可以運行指令,但是稍有不同。

  • CMD給出的是一個容器的默認的可執行體,可以被覆蓋。
  • ENTRYPOINT纔是正統地用於定義容器啓動以後的執行體,這個執行體一定會被執行。

(1) CMD單獨使用

FROM debian:wheezy
CMD ["/bin/ping", "localhost"]

啓動後不指定任何參數,將會ping localhost,

$ docker run -it test
PING localhost (127.0.0.1): 48 data bytes
56 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.076 ms
56 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.087 ms
56 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.090 ms
^C--- localhost ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max/stddev = 0.076/0.084/0.090/0.000 ms

但是如果啓動容器的同時帶有新的指令,則原有的CMD會被新的指令覆蓋

docker run -it test bash
root@e8bb7249b843:/#

(2)CMD和ENTRYPOINT同時使用

CMD通常用作傳遞參數給ENTRYPOINT,如下例子所示:

FROM debian:wheezy
ENTRYPOINT ["/bin/ping"]
CMD ["localhost"]

直接運行鏡像並不指定任何參數,將會一直ping localhost

$ docker run -it test
PING localhost (127.0.0.1): 48 data bytes
56 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.096 ms
56 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.088 ms
56 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.088 ms
^C--- localhost ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max/stddev = 0.088/0.091/0.096/0.000 ms

如果直接運行的同時,指定一個參數,將會ping對應的參數,此時CMD被覆蓋。

$ docker run -it test google.com
PING google.com (173.194.45.70): 48 data bytes
56 bytes from 173.194.45.70: icmp_seq=0 ttl=55 time=32.583 ms
56 bytes from 173.194.45.70: icmp_seq=2 ttl=55 time=30.327 ms
56 bytes from 173.194.45.70: icmp_seq=4 ttl=55 time=46.379 ms
^C--- google.com ping statistics ---
5 packets transmitted, 3 packets received, 40% packet loss
round-trip min/avg/max/stddev = 30.327/36.430/46.379/7.095 ms

如果你想要製作的容器更加通用一些,可以在Dockerfile中使用CMD ["/path/dedicated_command"],這樣你可以在運行容器的同時,根據需求來覆蓋已有的指令。

利用Dockerfile製作容器

上面介紹了Dockerfile中常用的指令,一般我們寫好Dockerfile之後,直接進入到Dockerfile所在的目錄,運行docker build即可,Docker會根據Dockerfile中指定的步驟來生成我們的鏡像。

$ docker build -t your_image_name .

以上就是製作鏡像的所有流程,接下來介紹鏡像的發佈。

鏡像發佈

鏡像發佈有兩種選擇,可以直接push到官方的docker hub,你只需要註冊一個docker賬號即可;也可以自己在本地創建私有倉庫,將鏡像push這裏。

Docker Hub

打開 https://hub.docker.com/ 註冊好賬戶後,記住好自己的賬戶名,待會需要將本地的鏡像打tag帶上用戶名,然後進行push。

首先使用如下的指令就給本地鏡像打tag,

docker tag myImage:v1 your_user_name/myImage:v1

接下來直接push就行,會自動push到官方倉庫,注意可能會需要docker login一下,這裏直接輸入用戶名密碼即可。

docker push your_user_name/myImage:v1

這樣官方倉庫就有你的Image了, 以後直接docker pull就行了。

本地私有倉庫

(1) 首先下載registry鏡像:docker pull registry.

(2) 接着在5000端口啓動,docker run -d --name reg -p 5000:5000 registry.

(3) 配置http傳輸,私服默認只能使用https,需要配置開放http.

以centos上的配置爲例,

Picture1.png

注意圖中的ip根據實際registry的ip來進行設置,可以通過docker inspect reg來找到。

配置完畢重啓下docker服務

systemctl daemon-reload 
systemctl restart docker

以上就完成了私有倉庫的創建。

接下來直接push Image到倉庫即可,流程和push到官方倉庫類似,只是打tag的用戶名改成私有倉庫的地址。

(1)打tag

docker tag hello-world http://192.168.244.7:5000/hello-world 

(2)push鏡像

docker push 192.168.244.7:5000/hello-world

(3)查詢鏡像:

Picture1.png

(4)查詢鏡像版本:

Picture1.png

以上就是鏡像製作和發佈的全部內容,下節會介紹實際部署中,docker-compose的使用以及docker的網絡通信。

參考鏈接:

  • https://stackoverflow.com/questions/21553353/what-is-the-difference-between-cmd-and-entrypoint-in-a-dockerfile

本文由『後端精進之路』原創,首發於博客 http://teckee.github.io/ , 轉載請註明出處

搜索『後端精進之路』關注公衆號,立刻獲取最新文章和價值2000元的BATJ精品面試課程

後端精進之路.png

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章