各種自製 Docker Image 的方法
使用的環境
系統與使用工具 |
---|
Centos 7.6 |
Docker 1.13.1 |
一、從運行中的 Container 產生 Image 檔
注意事項:
(1) 執行docker commit
時,Container 的狀態是可以停止中的。
(2) 掛載在 Container 的儲存空間不會被存到新產生的 Image 的,需要在docker run
時再用 -v 掛載。
先找一個接近自己需要的 Docker Image 來當基底並 docker run
起來,並用 docker exec
進入這個 Container 來安裝和設定,再用 docker commit
把啟動後產生的變動寫到原來所使用的 Docker Image。
# 從運行中的 Container 產生 image 檔
$ docker commit <Container 名稱或 ID> <產出的 Docker Image 名稱>
# 透過 -m 來增加說明文字,-a 加上作者資訊
$ docker commit -m "註解" -a "作者" <Container 名稱或 ID> <產出的 Docker Image 名稱>
# 製作一個把 Nginx 預設首頁的訊息從 Welcome 改成 Hello Nginx 的新 Docker Image
$ docker pull nginx
$ docker run --name nginx-welcome -p 8080:80 -d nginx
$ docker cp index.html nginx-welcome:/usr/share/nginx/html
$ docker commit nginx-welcome nginx-hello-commit
$ docker stop nginx-welcome
$ docker images
使用 docker images
就可以看到剛剛 commit 出來的 image 檔。
再來執行剛剛 commit 出來的 docker-hello-commit,成功!
二、用 Bulid 指令自動化產生新的 Image
Dockerfile 的檔案名稱有大小寫的分別。
透過 docker build
和 Dockerfile 就可以以全自動的方式來產生 Image 檔,
Dockerfile 可以看成是一個腳本,用來明確告訴 docker build
產生新 Image 所需要用到的資訊和步驟。
如果一切都順利,在執行結束後會看到 Successfully built XXXXX
,表示已經成功建立!
docker build
執行完成後,用 docker images
查詢,可以看到剛剛建立的 Image。
# 自動化產生新的 Image
$ docker build -t <新產生的 Image 的名稱:版本> <Dockfile 所在的檔案路徑或網址>
$ cd nginx-docker
$ docker build -t nginx-ayu:demo .
$ docker build -t nginx-ayu:demo http://xxx.xxxx.xxx.xxx
三、Dockerfile常用的指令和格式
1. FROM
用途:指定要下載的 Image 檔名稱。
從 Docker Hub 下載 Debain 的 Jessie 版本的 Image 檔。
FROM debain:jessie
2. ENV
用途:設定環境變數。
在 Container 裡設定指定 Nginx 的 NGINX_VERSION
環境變數。
之後執行 apt-get install
安裝指令時,會需要這個 ENV
所設定的變數,
通常會把經常變動的參數設成環境變數,以便後續維護或更新時,可以不用直接修改相關的指令。
ENV NGINX_VERSION 1.11.10-1~jessie
3. EXPOSE
用途:指定 Container 啟用的連結埠號。
設定這個 Container 要啟用 80 和 443 port,透過 EXPOSE
啟用的連結埠不能被外部或主機連線存取,必須透過 docker run
的 -p
選項設定,主機的連線或程式才可以透過該對應的主機連結埠進行連結及存取。
EXPOSE 80 443
4. RUN
用途:建立 Image 時執行的指令,只有在 docker build
產生的時候會有作用,大部分被用在設置和安裝軟體。
# 利用 RUN 指令在這個暫時啟用的 Container 執行安裝的指令
RUN apt-key adv --keyserver ........
5. CMD
用途:Container 啟動完成後要執行的指令。
注意!每個 Dockerfile 只能有一個
CMD
指令會有作用,若同一個 Dockfile 裡有多個CMD
指令,只有 最後一個CMD
指令會有作用。
(1)(官方建議寫法)
可用在 Dockfile 裡,但不會使用預設的 Shell 執行,不能使用 Shell 所提供的功能,像是取用環境變數或用 Shell 內建的指令。
CMD ["<要執行的程式>","<要執行的程式用到的第一組選項>","<要執行的程式用到的第二組選項或第一組選的參數值>"]
(2) 可用在 Dockfile 裡,會使用預設的 Shell 執行。
CMD <要執行的程式> <要執行的程式用到的第一組選項> <要執行的程式用到的第二組選項或第一組選的參數值>
(3) 搭配 ENTRYPOINT
指令使用,傳遞指令選項給 ENTRYPOINT
指令使用。
CMD ["<加在 ENTRYPOINT 執行程式的選項一>","<加在 ENTRYPOINT 執行程式的選項二>"]
當 Container 啟動完成後,要求 Container 啟動 Nginx,就等於在 Container 命令列執行 $ nginx -g daemon off
。
CMD ["nginx", "-g", "daemon off;"]
# daemon off 是 -g 選項的參數,且要求須使用雙引號
CMD nginx -g, "daemon off;"
6. ENTRYPOINT
用途:用法和 CMD
相似,都是用來設定 Container 啟動後要執行的指令,但如果是用 ENTRYPOINT
指令來啟動程式的話,就不能在 docker run
後面加入執行指令來蓋掉 ENTRYPOINT
所要求的啟動方式,反而會變成 ENTRYPOINT
的後續選項。
(1) 官方建議寫法
ENTRYPOINT ["<要執行的程式>","<要執行的程式用到的第一組選項>","<要執行的程式用到的第二組選項或第一組選的參數值>"]
(2)
ENTRYPOINT <要執行的程式> <要執行的程式用到的第一組選項> <要執行的程式用到的第二組選項>
假如有個 Dockfile 最後一行從 CMD
改成 ENTRYPOINT ["nginx", "-g", "daemon off;"]
後產生新的 Image 檔,就會發生錯誤,因為 /bin/bash
會變成 ENTRYPOINT
的後續選項: [nginx -g "daemon off;"/bin/bash]
,而 Nginx 沒有 /bin/bash
選項
$ docker build -t nginx-entrypoint .
$ docker run -it nginx-entrypoint /bin/bash
發生錯誤!
7. WORKDIR
用途:指定執行指令的資料夾位置
注意!當資料夾是相對路徑時,預設會以它上一個
WORKDIR
指令所指定的資料夾位置做為目前所在的資料夾位置。
如果用到 RUN
、CMD
、ENTRYPOINT
、COPY
、ADD
指令,便會需要利用 WORKDIR
來進行資料夾位置轉換。
WORKDIR <資料夾完整路徑或相對路徑>
建立 /home/ayubiz/abc 資料夾後,利用 WORKDIR 切換到 /home/ayubiz 資料夾,然後把 hello.html 檔案複製到 /home/ayubiz 裡,透過 WORKDIR
指定相對位置 abc 資烙夾將位置切換進 /home/ayubiz/abc,同時這也是使用 docekr exec
進入新啟動的 Container 後的所在位置。
RUN mkdir -p /home/ayubiz/abc
WORKDIR /home/ayubiz
COPY hello.html ./
WORKDIR abc
8. ADD
需要解自動解壓縮(.tar)時適用。
用途:將一個或多個檔案放進 Image 裡,讓 Dockfile 所產生的 Image 檔能包含這些檔案。
ADD <來源檔案在主機的完整路徑和檔名> <Image 的完整目的路徑和檔名>
ADD <來源檔案完整網址和檔名> <Image 的完整目的路徑和檔名>
ADD 指令支援 * 和 ? 等符號的萬用字元
將 Dockfile 所在目錄下的 hello.html 檔案加到新產生的 Image 檔的 /usr/share/nginx/html 資料夾。
ADD hello.html /usr/share/nginx/html
ADD *.html /usr/share/nginx/html
ADD hello.htm? /usr/share/nginx/html
注意!如果在路徑最後加上 / ,都會被認為是一個資料夾位置而不是檔案,若檔案來源路徑是資料夾,資料夾內的檔案會被加到 Image 檔案中,但資料夾本身不會被加入。
也就是說docker build
不會自動在 Image 檔案裡新增來源檔案的資料夾後再加入來源資料夾內的檔案,但是當目的地檔案是資料夾時,docker build
會自動建立不存在的資料夾。
例如:ADD ./abc/ /demo/
docker build
會新增 demo 資料夾,然後將 abc 資料夾底下的所有檔案加到 demo 資料夾裡,但是 demo 資料夾裡不會有 abc 資料夾的存在。
9. COPY
用途:功能跟 ADD
類似,如果沒有要自動解壓縮檔案,官方還是比較建議用COPY
。
COPY <來源檔案在主機的完整路徑和檔名> <Container 裡的完整目的路徑和檔名>
10. MAINTAINER
用途:標註此 Dockerfile 的作者或維護者
此指令要被淘汰,建議之後用 LABEL 指令。
如果要把 Dockfile 分享出去,建議寫上電子郵件。
MAINTAINER NGINX Docker Maintainers "docker-maint@nginx.com"
11. LABEL
用途:將 Image 檔的作者、版本、描述、其他自訂資訊儲存到新產生的 Image 檔,建議將多欄位與值都使用一個 LABEL
來執行。
LABEL <欄位名稱>="<欄位值>" <欄位名稱>="<欄位值>" <欄位名稱>="<欄位值>"
LABEL version="0.1" description="demo" organization="arthurtoday.com"
存入的資訊可以透過 docker image inspect
查看 Labels 欄位。
$ docker inspect -f '{{json .ContainerConfig.Labels}}' <Image 名稱或 ID>
12. USER
用途:指定新產生的 Image 檔的預設使用者帳號(通常都是 root),要先用 RUN
建立新使用者,USER
指令不會自動建立新帳號,如果指定不存在的使用者帳號是不會有作用的。
使用者帳號會影響到 RUN
、CMD
、ENTRYPOINT
等指令的執行時所用的使用者帳號,也會影響權限,所以若非必要,不建議使用 USER
指令改變執行軟體的使用者。
USER <已建好的使用者名稱>
USER <已建好的使用者 UID>
USER <已建好的使用者名稱>:<已存在的使用者群組名稱>
USER <已建好的使用者 UID>:<已存在的使用者群組 UID>
13. VOLUME
用途:提供在 Image 檔案建立 Data Volume 掛載點的功能。
Volume ["<掛載點的資料夾名稱和完整路徑>", ...]
Volume <掛載點的資料夾名稱和完整路徑> ...
把 Nginx 的 Image 檔裡的 /usr/share/nginx/html 和 /var/log/nginx 設定為掛載點的方式。
Volume ["/usr/share/nginx/html","/var/log/nginx"]
Volume /usr/share/nginx/html /var/log/nginx
也可以用 RUN
指令建立資料夾,再使用 VOLUME
設定掛載點。
RUN mkdir /temp
Volume /temp
14. ARG
用途:可以讓 docker bulid
指令利用 --build-arg
選項傳入參數到 Dockerfile 裡,可以增加 Dockfile 的彈性和再利用性。
有時候某些參數變更就需要修改 Dockerfile,譬如更改環境變數,如果不想經常更改 Dockerfile,可以考慮改用 ARG
指令搭配 docker buil
的 --build-arg
選項。
(1) 只給定參數名稱
ARG <參數名稱>
(2) 同時給定參數的名稱和預設值,不一定需要設定預設值
ARG <參數名稱>=<預設值>
把 ENV
指令的 NGINX_VERSION
改用 ARG
指定設定為參數和預設值,然後如果要更改,就可以利用 docker build --build-arg
指定 NGINX_VERSION 值
。
ARG NGINX_VERSION=1.13.4-1~jessie
$ docker build -t nginx-ayu7 . --build-arg NGINX_VERSION=1.11.20-1~jessie
注意!如果有相同名稱的變數,
ENV
指令會優於ARG
指令。
ARG NGINX_VERSION=1.13.4-1~jessie
ENV NGINX_VERSION=1.11.10-1~jessie
RUN echo $NGINX_VERSION
最後會 echo 出 1.11.10-1~jessie
四、Dockerfile 撰寫與 docker build 指令建議做法
-
資料夾 每一個 Dockfile 都放置各自獨立的資料夾,並把所需的檔案都集中放置到資料夾內,執行
docker build
前也移到該資料夾後再執行,這樣可以簡化 Dockerfile 的撰寫和docker build
指令的執行。 -
一次一種用途 Container 和虛擬機器不同,不適合讓一個 Container 當成一個機器來執行很多功能,建議讓多個 Container 來組合成一個完整的架構,這樣 Dockfile 才能易寫易懂易維護,同時還可以利用 Container 的分散運算能力提高整個系統的可用性
-
只安裝需要的套件 Image 檔案的大小有一部份是取決於安裝軟體數的多寡,改 Dockfile 和 重新產生 Image 的速度很快,有需要再修改 Dockerfile 就好了,不要為了以防萬一而加大 Image 檔案的容量,導致增加檔案傳輸或部屬的時間
-
Build Cache 善用
docker build
指令 在產生 Image 檔時,背後有使用到一個快取的機制,所以同一個 Dockerfile 在執行第二次或第三次的docker build
指令時可以發現產生的速度快很多,因為有 Build Cache 機制在運作。 但是 Build Cache 的作用和行為會依 Dockfile 被修改的內容而不同,如果發現執行docker build
後產出的 Image 檔和預期的不同,可以加入docker build --no-cache
停用 Cache,以確定問題不是 Cache 造成
五、用匯出和匯入的方法搬移 Image 檔
1. 匯出
docker save 指令匯出的檔案為 tar。
$ docker save -o <匯出的檔案名稱.tar> <要匯出的 Image>:<版本>
$ docker save -o nginx.tar nginx:latest
$ docker save -o nginx.tar nginx
$ docker save nginx > nginx.tar
2. 匯入
如果主機上已經有相同的名稱和版本的 Image 檔存在,可能會顯示已匯入但實際上沒覆蓋。
$ docker load -i <之前匯出的 image 檔.tar>
$ docker load -i nginx.tar
$ docker load < nginx.tar
六、用 Container 匯出成新的 Image 檔
除了使用 docker commit
指令產生新的 Image 檔,也利用 docker export
匯出 Container,再用 docker import
產生新的 Image 檔。
1. 匯出
不是將運行中的 Container 整個匯出,而只是將 Container 內部使用中的檔案系統匯出,匯出的檔案會覆蓋相同路徑下的同名稱檔案。
如果 Container 有掛載 Date Volume,則資料卷不會被匯出。
$ docker export -o [匯出的檔案名稱.tar] [Container 名稱或 ID]
$ docker export [Container 名稱或 ID] > [匯出的檔案名稱.tar]
$ docker export -o nginx-export.tar nginx-hello
2. 匯入
匯入時,不會將匯入的檔案內容轉換成運行或停用的 Container,而是直接匯入成 Image 檔,所以可以利用這種特質,將執行中的 Container 檔案內容在同一台或不同主機上,匯出新的 Image 檔。
$ docker import [匯入的檔案名稱與路徑] [Image 名稱:版本標籤]
$ docker import nginx-export.tar ayubiz/nginx:hello
$ docker images
參考資料
- Docker 這樣學才有趣:從入門,到玩直播、挖礦