您好,登錄后才能下訂單哦!
本篇內容主要講解“Docker鏡像怎么構建”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“Docker鏡像怎么構建”吧!
Docker鏡像介紹
Docker鏡像是由文件系統疊加而成。最底端是一個引導文件系統(bootfs),Docker用戶幾乎不會和引導文件系統有交互,當容器啟動后它會被卸載而移動到內存中。
第二層是root文件系統(rootfs),它位于引導文件系統之上。rootfs可以是一種或多種操作系統。rootfs永遠是只讀狀態。
Docker利用聯合加載(union mount)技術又會在rootfs層上加載更多的只讀文件系統。聯合加載指的是一次同時加載多個文件系統,但在外部看起來像是一個文件系統。聯合加載會將各層文件系統疊加到一起,這樣最終的文件系統會包含所有底層的文件和目錄。
Docker將這樣的文件系統成為鏡像。一個鏡像可以放到另一個鏡像的頂部。位于下面的鏡像成為父鏡像,最底部的鏡像成為基礎鏡像。
最后,當從一個鏡像啟動容器時,Docker會在該鏡像的最頂層加載一個讀寫文件系統。在Docker中運行的程序是在這個讀寫層中執行的。
當Docker第一次啟動一個容器時,初始的讀寫層是空的。當文件系統發生變化時,這些變化都會應用到這一層上。比如,想修改一個文件,這個文件首先會從該讀寫層下面的只讀層復制到讀寫層。該文件的只讀版本依然存在,但是已經被讀寫層中的該文件副本隱藏。
這種機制成為寫時復制,這也是使Docker強大的技術之一。每個只讀鏡像都是只讀的,并且以后永遠不會變化。當創建一個容器時,Docker會構建出一個鏡像棧,并在棧的最頂端添加一個讀寫層。這個讀寫層再加上其下面的鏡像層以及一些配置數據,就構成了一個容器。
列出鏡像
docker images
本地鏡像都保存在Docker宿主機的/var/lib/docker目錄下,/var/lib/docker/containers目錄中保存著所有的容器。
鏡像從倉庫下載下來。鏡像保存在倉庫中,而倉庫保存在Registry中。默認的Registry是由Docker公司運營的公共Registry服務,即Docker Hub。可以將鏡像倉庫想象為類似Git倉庫的東西,它包括鏡像、層以及鏡像的元數據。
每個鏡像倉庫可以存放很多鏡像,比如ubuntu倉庫包含了Ubuntu12.04、12.10、13.04等等。使用下面的名利可以把ubuntu倉庫中的鏡像全部拉取到本地:
docker pull ubuntu
為了區分一個倉庫中的不同鏡像,Docker提供了標簽(功能 ),每個鏡像都帶有一個標簽。我們可以通過在倉庫名后面加上一個冒號和標簽名來指定該倉庫中的某一鏡像:
docker run -t -i --name new_container ubuntu:12.0 /bin/bash
Docker Hub中有兩種類型的倉庫:用戶倉庫(user repository)和頂層倉庫(top-level repository)。用戶倉庫的鏡像都是由用戶創建的,而頂層倉庫則是由Docker官方管理的。
用戶倉庫的命名由用戶名和倉庫名兩部分組成,如jamtur01/puppet。前面是用戶名后面是倉庫名。
與之相對,頂層倉庫只包含倉庫名,如ubuntu倉庫。
拉取鏡像
用docker run 命令從鏡像啟動一個容器時,如果該鏡像不在本地,Docker會先從Docker Hub下載該鏡像。如果沒有指定具體的鏡像標簽,那么Docker會自動下載latest標簽的鏡像。
也可以使用docker pull命令自己預先拉取鏡像到本地。下面的命令拉取所有的fedora基礎鏡像:
docker pull fedora
拉取完成后只查看fedora鏡像的內容:
docker images fedora
如果只想拉取一種,可以在鏡像名后加標簽,例如只拉取Fedora 20:
docker pull fedora:20
查找鏡像
可以使用docker search命令查找所有Docker Hub上公共的可用鏡像:
docker search puppet
上面的命令查找了所有帶puppet的鏡像,返回信息如下:
NAME 倉庫名;
DESCRIPTION 描述;
STARTS 用戶評價;
OFFICIAL 是否官方;
AUTOMATED 是否由Docker Hub的自動構建流程創建的。
構建鏡像
構建鏡像由兩種方法:
使用 docker commit 命令;
使用 docker build 命令和Dockerfile文件。
現在官方不推薦使用docker commit命令,而應使用更靈活、更強大的Dockerfile來構建Docker鏡像。
一般來說我們不是真正創建新鏡像,而是基于一個已有的基礎鏡像,如ubuntu、fedora等,構建鏡像。
(1)創建并登陸Docker Hub賬號
注冊一個Docker Hub賬號后,登陸:
docker login
成功登陸后,認證信息保存到~/.dockercfg文件中,供后面使用。在我的測試環境中未找到~/.dockercfg文件。
(2) 用 docker commit 命令創建鏡像
首先創建一個新容器:
docker run -i -t ubuntu /bin/bash
接下來安裝Apache:
apt-get -yqq update apt-get -y install apache2
使用exti從容器中退出,然后提交:
docker commit 容器ID ivan/apache2
命令指定了要提交修改的容器ID,以及一個目標鏡像倉庫名。
也可以在提交鏡像時指定更多的數據(包括標簽)來詳細描述:
docker commit -m="A new custom image" --author="Ivan" 容器ID ivan/apache2:webserver
在這條命令里,用 -m 指定了鏡像的提交信息, --author 列出鏡像的作者信息,最后為該鏡像添加了:webserver標簽。
可以使用docker inspect命令來查看鏡像的詳細信息:
docker inspect ivan/apache2:webserver
(3)用Dockerfile構建鏡像
官方推薦使用Dockerfile定義文件和docker build 命令來構建鏡像。Dockerfile使用基本的基于DSL語法(聲明式編程語言)的指令來構建一個Docker鏡像,之后使用docker build命令基于該Dockerfile中的指令構建一個新的鏡像。
首先需要創建一個文件夾,然后在這個文件夾里創建初始的Dockerfile。
mkdir static_web cd static_web touch Dockerfile
上述代碼創建了一個static_web的目錄用來保存Dockerfile,這個目錄就是構建環境,Docker稱此為上下文(context)或構建上下文(build context)。Docker會在構建鏡像時將構建上下文和該上下文中的文件和目錄上傳到Docker守護進程。這樣Docker守護進程就能直接訪問想在進程中存儲的代碼、文件或者其他數據。
下面向Dockerfile中添加內容:
# Version: 0.0.1 FROM ubuntu:14.04 MAINTAINER James Turnbull "james@example.com" RUN apt-get update RUN apt-get install -y nginx RUN echo "Hi, I am in your container' > /user/share/nginx/html/index.html EXPOSE 80
該Dockerfile由一系列指令和參數組成。每條指令,如FROM,都必須是大寫字母且后面要跟隨一個參數。Dockerfile中的指令會按順序從上向下執行。
每條指令都會創建一個新的鏡像層并對鏡像進行提交。Docker大體上按照如下流程執行Dockerfile中的指令:
Docker從集成鏡像運行一個容器;
執行一條指令,對容器做出修改;
執行類型docker commit的操作,提交一個新的鏡像層;
Docker再基于剛剛提交的鏡像運行一個新容器;
執行Dockerfile中的下一條指令,直到所有指定都執行完畢。
如果Dockerfile由于某些原因沒有正常結束(如某條指令失敗了),那么將得到一個可以使用的鏡像。這對調試很有幫助:可以基于該鏡像運行一個具備交互功能的容器,使用最后創建的鏡像對為什么指令會失敗進行調試。
Dockerfile也支持注釋,以 # 開發的行都會被認為是注釋。
每個Dockerfile的第一條指令都應該是FROM。FROM指令指定一個已經存在的鏡像,后續指令都將基于這個鏡像運行,這個鏡像稱為基礎鏡像。
接著寫入了MAINTAINER指令,這條指令會告訴Docker該鏡像的作者以及作者的郵件地址。
在這之后指定了三條RUN指令。RUN指令會在當前鏡像中運行指定的命令。在這個例子中,通過RUN指令更新了已經安裝的APT倉庫,安裝nginx包,之后創建了/usr/share/nginx/html/index.html文件。
默認情況下,RUN指令會在shell里使用命令包裝器/bin/sh -c來執行。如果在一個不支持shell的平臺運行或者不希望在shell中運行,也可以使用exec格式的RUN指令,如:
RUN [“apt-get”, "install", "-y", "nginx"]
接著使用了EXPOSE指令,這條指令告訴Docker該容器內的應用程序將會使用容器的指定端口。注意,這并不意味著可以自動訪問該端口。出于安全的原因,Docker不會自動打開該端口,而是需要在使用docker run運行容器時來指定需要打開哪些端口。
可以指定多個EXPOSE指令來向外部公開多個端口。
(4)基于Dockerfile構建新鏡像
執行 docker build 命令,Dockerfile中的所有指令都會被執行并且提交,并且在該命令成功結束后返回一個新鏡像。
cd static_web docker build -t="ivan/static_web" .
-t 參數為新鏡像設置倉庫,也可以在構建時為鏡像設置標簽。
docker build -t="ivan/static_web:v1" .
上面兩個命令中的 . 告訴Docker到本地當前目錄找Dockerfile文件。也可以指定一個Git倉庫的源地址來指定Dockerfile的位置。
docker build -t="ivan/static_web:v1" git@github.com:ivan/docker-static_web
構建過程中,構建上下文會被上傳到Docker守護進程。如果在構建上下文的根目錄中存在.dockerignore文件,那么該文件內容會被按行進行分割,每一行都是一條文件過濾匹配規則。匹配通過的文件不會被上傳到Docker守護進程。該文件中模式的匹配規則采用了Go語言中的filepath。
(5)指令失敗
如果指令失敗會返回失敗前構建的鏡像,我們可以啟動容器加載鏡像檢查失敗原因。
(6)構建的緩存
由于每一步的構建過程都會講結果提交為鏡像,所以之前的鏡像層會被看做是緩存。比如在構建的第四步出錯,修改后第四步后重新構建,由于第一步到第三步未被修改,Docker會從第四步開始構建。如果第一步到第三步之間做了一些修改,Docker會從發生變化的第一條指令開始構建。
有時構建時不希望利用緩存可以加入--no-cache標志:
docker build --no-chace -t-"inva/static_web"
(7)基于構建緩存的Dockerfile模板
一般在Dockerfile文件的開頭都會使用相同的指令集模板,比如
FROM ubuntu:14.04 MAINTAINER James Turnbull "james@example.com" ENV REPERESHED_AT 2016-07-08 RUN apt-get -qq update
前兩條指令的運行結果不會改變,第三條指令使用ENV創建了一個REFRESHED_AT環境變量,這個環境變量用來表明該鏡像模板最后的更新時間。最后使用RUN指令來運行apt-get -qq update指令。該指令運行時會刷新APT包的緩存,用來確保將要安裝的每個軟件包都更新到最新版本。
對于這個模板,如果想刷新一個構建,只需要修改ENV指令中的日期。
(8)查看新鏡像
可以使用docker images 來查看新構建的鏡像。
使用docker history命令深入了解該鏡像是怎么構建的:
docker history 鏡像ID
(9)從新鏡像啟動容器
上面的例子中構建了一個nginx鏡像,現在啟動它看看是否正常:
docker run -d -p 80 --name static_web ivan/static_web nginx -g "daemon off";
命令中參數 -d 表示容器以守護方式運行,nginx -g "daemon off"是需要在容器中運行的命令。
新出現的參數 -p 控制Docker在運行時應該公開哪些網絡端口給宿主機。運行一個容器時,Docker可以通過兩種方法來在宿主機上分配端口:
Docker可以在宿主機上隨機選擇一個位于49153~65535的一個比較大的端口號來映射到容器中的指定端口(例子中是80);
可以在Docker宿主機中指定一個具體的端口號來映射到容器中的指定端口上(例子中是80)。
使用 docker ps 命令可以查看端口分配情況。
也可以通過docker port查看容器的端口映射情況:
docker port 容器ID 80
上面的名利指定了向查看映射情況的容器ID和容器端口號,返回的將是宿主機中映射的端口號。
docker run 的 -p 選項可以靈活的指定容器和宿主機之間的端口映射關系。比如指定將容器中的端口映射到Docker宿主機的某一特定端口上:
docker run -d -p 80:80 --name static_web ivan/static_web nginx -g "daemon off";
頁可以將容器內的端口綁定到特定的IP的端口上:
docker run -d -p 127.0.0.1:80:80 --name static_web ivan/static_web nginx -g "daemon off";
將容器內的80端口綁定到宿主機127.0.0.1這個IP的80端口上。
也可以綁定到一個宿主機的特定IP的隨機端口上:
docker run -d -p 127.0.0.1::80 --name static_web ivan/static_web nginx -g "daemon off";
使用 -P 參數,可以用來將Dockerfile中EXPOSE指令設置的端口綁定到宿主機的隨機端口上:
docker run -d -P --name static_web ivan/static_web nginx -g "daemon off";
在端口綁定時使用/udp后綴來指定UPD端口綁定。
在得到宿主機的綁定IP和端口后可以使用curl來測試nginx:
curl 127.0.0.1:端口號
(10)Dockerfile指令
CMD
用于指定一個容器啟動時要運行的命令。類似于RUN指令,但是RUN指令是在指定鏡像被構建時要運行的命令,而CMD是指定容器被啟動時要運行的命令。這和docker run命令啟動容器指定要運行的命令是一樣的。
CMD ["/bin/bash", "-l]
例子中要運行的命令存放在一個數組結構中。這將告訴Docker按指定的原樣來運行該命令。也可以不使用數組,這時候Docker會在指定的命令前加上/bin/sh -c。這在執行該命令的時候可能會導致意料之外的行為,所以Docker推薦一直使用以數組語法來設置要執行的命令。
使用docker run命令時指定參數會覆蓋Dockerfile中的CMD命令。
在Dockerfile中只能指定一條CMD指令。如果指定了多條,也只有最后一條CMD指令會被使用。如果想在啟動容器時運行多個進程或者多條命令,可以考慮使用類似Supervisor這樣的服務管理工具。
ENTRYPOINT
與CMD相似也是指定一些要運行的命令。如果在Dockerfile中指定了ENTRYPOINT,CMD指令或docker run指定的命令參數都會被當做參數再次傳遞給ENTRYPOIN指定的命令。
例如
ENTRYPOINT ["/user/bin/nginx"]
重新構建鏡像后啟動:
docker run -t -i ivan/static_web -g "daemon off;"
這樣-g "daemon off;"就會傳遞給ENTRYPOINT,組成一條命令。
也可以組合使用ENTRYPOINT和CMD指令:
ENTRYPOINT ["/user/bin/nginx"] CMD ["-h"]
此時當啟動一個容器,如果指定-g "daemon off;"參數就會讓Nginx守護進程以前臺方式運行。如果在啟動容器的時候不指定任何參數,則CMD中的-h就會傳遞給/user/bin/nginx,顯示Nginx的幫助信息。
這使我們可以構建一個鏡像,該鏡像既可以運行一個默認的命令,也支持通過docker run為該命令指定可覆蓋的選項或者標志。
也可以在docker run中指定--entrypoint標志覆蓋ENTRYPOINT指令。
WORKDIR
用來為Dockerfile中后續的一系列指令設置工作目錄,也可以為最終的容器設置工作目錄:
WORKDIR /opt/webapp/db RUN bundle install WORKDIR /opt/webapp ENTRYPOINT ["rackup"]
例子中將工作目錄切換為/opt/webapp/db后運行了bundle install命令,之后又將工作目錄設置為/opt/webapp,最后設置了ENTRYPOINT。
docker run 命令可以使用-w覆蓋WORKDIR。
ENV
用來在鏡像構建過程中指定環境變量:
ENV RVM_PATH /home/rvm/
這個新環境變量可以在后續的任何RUN指令中使用。
也可以在其他指令中直接使用這些環境變量:
ENV TARGET_DIR /opt/app WORKDIR $TARGET_DIR
如果需要可以通過在環境變量前加上一個反斜線來進行轉義。
這些環境變量會被持久保存到從我們的鏡像創建的任何容器中。
也可以使用docker run -e來傳遞環境變量,但這些環境變量只會在運行時有效:
docker run -ti -e "WEB-PORT=8080" ubuntu env
USER
用來指定該鏡像以什么用戶運行,可以指定用戶名或UID,組或GID,也可以是兩者的組合:
USER user USER user:group USER uid USER uid:gid USER user:gid USER uid:group
也可以在docker run 命令中通過-u 選項來覆蓋USER指定的值。
如果不指定,默認用戶為root。
VOLUME
用來向基于鏡像創建的容器添加卷。一個卷是可以存在于一個或者多個容器內的特定目錄。這個目錄可以繞過聯合文件系統,并提供如下功能:
卷可以在容器間共享和重用;
一個容器可以不必須和其他容器共享卷;
對卷的修改是立刻生效的;
對卷的修改不會對更新鏡像產生影響;
卷會一直存在直到沒有任何容器再使用它。
VOLUMN ["/opt/project","/data"]
這條指令將會為基于此鏡像創建的任何容器創建兩個掛載點。
ADD
用來將構建環境下的文件和目錄復制到鏡像中。
ADD software.lic /opt/application/software.lic
ADD指令將構建目錄下的software.lic文件復制到鏡像中去。
源文件的位置可以是一個URL,或者構建上下文中的文件名或目錄。不能對構建目錄之外的文件進行ADD操作。
在ADD文件時,Docker通過目的地址參數末尾的字符來判斷文件源是目錄還是文件。如果目的地址以/結尾,那么Docker認為源位置指向的是目錄;如果目的地址不以/結尾,那么Docker就認為源位置指向的是文件。
如果源文件是本地歸檔文件(合法的歸檔文件包括gzip、bzip2和xz),Docker會自動將歸檔文件解開:
ADD lastest.tar.gz /var/www/wordpress/
上例將歸檔文件解壓到/var/www/wordpress/目錄下。Docker解開歸檔文件的行為和使用帶-x選項的tar命令一樣:原目的目錄已經存在的內容加上歸檔文件中的內容。如果目的位置的目錄下已經存在了和歸檔文件同名的文件或者目錄,目的位置中的文件或者目錄不會被覆蓋。
截止到1.0.0版本還不能解壓URL方式制定的歸檔文件。
如果目的位置不存在,Docker將會為我們創建這個全路徑,包括路徑中的任何目錄。新創建的文件和目錄的模式為0755,并且UID和GID是0.
ADD命令會使得構建緩存無效。
COPY
類似于ADD,它只復制文件而不會做提取和解壓。
COPY conf.d /etc/apache2/
這條指令會將本地conf.d目錄中的文件復制到/etc/apache2/目錄中。
文件源路徑必須是在構建目錄中。
任何由該指令創建的文件或者目錄的UID和GID都會設置為0.
如果目的目錄不存在,Docker會自動創建所有需要的目錄結構。
ONBUILD
為鏡像添加觸發器。當一個鏡像被用做其他鏡像的基礎鏡像時,該鏡像中的觸發器會被執行。
觸發器會在構建過程中插入新指令,可以認為這些指令是緊跟在FROM之后指定的。觸發器可以是任何構建指令:
ONBUILD ADD . /app/src ONBUILD RUN cd /app/src && make
但是FROM、MAINTAINER和ONBUILD不能用于ONBUILD指令,為了防止在Dockerfile構建過程中產生遞歸調用的問題。
使用docker inspect 容器ID可以查看容器使用的鏡像的ONBUILD指令信息。
ONBUILD觸發器會按照在父鏡像中指定的順序執行,并且只能被繼承一次(即只能在子鏡像中執行,而不會再孫鏡像中執行)。
將鏡像推送到Docker Hub上
docker push ivan/static_web
還可以進行自動構建,只需要將Github或BitBucket中含有Dockerfile文件的倉庫連接到Docker Hub即可。向這個代碼倉庫推送代碼時,將會觸發一次鏡像構建活動并創建一個新鏡像。
點擊Dockr Hub網站右上角的Create下面的Create Automated Build,關聯一個Github賬號然后在Github網站上做授權確認。成功后返回Docker Hub點擊“Create Auto-build”,選擇一個Github Repository并輸入描述就創建成功了。
刪除鏡像
docker rmi ivan/static_web
從刪除命令的輸出可以看出Docker的分層文件系統,每個Deleted行都代表一個鏡像層被刪除。
上述操作只會將本地的鏡像刪除。如果之前已經將該鏡像推送到Docker Hub上,它在Docker Hub上將依然存在。如果想刪除Docker Hub上的鏡像倉庫,需要登錄Docker Hub執行刪除操作。
還可以指定一個鏡像名列表來刪除多個鏡像:
docker rmi ivan/apache2 ivan/static_web
刪除全部鏡像的技巧:
docker rmi 'docker images -a -q'
運行自己的Docker Registry
有兩種選擇:
利用Docker Hub上的私有倉庫;
運行自己的Registry。
Docker公司開源了運行Docker Registry當然代碼,可以基于此運行自己的Registry。
(1)從容器中運行Registry
docker run -p 5000:5000 registry
啟動一個Registry應用的容器,并綁定到宿主機的5000端口。
運行完成后在瀏覽器輸入地址:宿主機IP:5000;看到信息:"docker-registry server",說明Registry Server啟動成功了。
(2)提交鏡像到自己的Registry
成功運行了Registry容器,但是無法提交,原因是不知道主機名,自己認為的localhost是不對的。
經過測試用127.0.0.1是可以的。
首先將已有的鏡像打上新的Registry標簽
docker tag 鏡像ID 127.0.0.1:5000/ivan/static_web
最后將鏡像提交
docker push 127.0.0.1:5000/ivan/static_web
到此,相信大家對“Docker鏡像怎么構建”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。