目錄
- 前言
- 命令列表
- 命令驗證執行
- 結語
前言
docker對我來說是一個很方便的工具,,上一篇文章也寫了docker基本的一些使用,這篇文章重點描述一下Dockerfile的使用,從零建立一個自己定制化的鏡像,并可以執行我們需要的任務。
命令列表
FROM 指定基礎鏡像:所謂定制鏡像,那一定是以一個鏡像為基礎,在其上進行定制。就像我們之前運行了一個 nginx 鏡像的容器,再進行修改一樣,基礎鏡像是必須指定的。而 FROM 就是指定 基礎鏡像,因此一個 Dockerfile 中 FROM 是必備的指令,并且必須是第一條指令
RUN 執行命令:RUN 指令是用來執行命令行命令的。其格式有兩種:
- shell 格式:RUN <命令>,就像直接在命令行中輸入的命令一樣。剛才寫的 Dockerfile 中的 RUN 指令就是這種格式
- exec 格式:RUN ["可執行文件", "參數1", "參數2"],這更像是函數調用中的格式。
COPY 指令將從構建上下文目錄中 <源路徑> 的文件/目錄復制到新的一層的鏡像內的 <目標路徑> 位置
ADD 指令和 COPY 的格式和性質基本一致。但是在 COPY 基礎上增加了一些功能。因此在 COPY 和 ADD 指令中選擇的時候,可以遵循這樣的原則,所有的文件復制均使用 COPY 指令,僅在需要自動解壓縮的場合使用 ADD。
CMD 指令的格式和 RUN 相似,也是兩種格式:
- shell 格式:CMD <命令>
- exec 格式:CMD ["可執行文件", "參數1", "參數2"…]
參數列表格式:CMD ["參數1", "參數2"…]。在指定了 ENTRYPOINT 指令后,用 CMD 指定具體的參數。Docker 不是虛擬機,容器就是進程。既然是進程,那么在啟動容器的時候,需要指定所運行的程序及參數。CMD 指令就是用于指定默認的容器主進程的啟動命令的。
ENTRYPOINT 的格式和 RUN 指令格式一樣,分為 exec 格式和 shell 格式。
LABEL:你可以給鏡像添加標簽來幫助組織鏡像、記錄許可信息、輔助自動化構建等。每個標簽一行,由 LABEL 開頭加上一個或多個標簽對。下面的示例展示了各種不同的可能格式。# 開頭的行是注釋內容。
EXPOSE:EXPOSE 指令用于指定容器將要監聽的端口。因此,你應該為你的應用程序使用常見的端口。例如,提供 Apache web 服務的鏡像應該使用 EXPOSE 80,而提供 MongoDB 服務的鏡像使用 EXPOSE 27017。 對于外部訪問,用戶可以在執行 docker run 時使用一個標志來指示如何將指定的端口映射到所選擇的端口。
ENV:為了方便新程序運行,你可以使用 ENV 來為容器中安裝的程序更新 PATH 環境變量。例如使用 ENV PATH /usr/local/nginx/bin:$PATH 來確保 CMD ["nginx"] 能正確運行。 ENV 指令也可用于為你想要容器化的服務提供必要的環境變量,比如 Postgres 需要的 PGDATA。 最后,ENV 也能用于設置常見的版本號,比如下面的示例:
VOLUME:VOLUME 指令用于暴露任何數據庫存儲文件,配置文件,或容器創建的文件和目錄。強烈建議使用 VOLUME 來管理鏡像中的可變部分和用戶可以改變的部分。
USER:如果某個服務不需要特權執行,建議使用 USER 指令切換到非 root 用戶。先在 Dockerfile 中使用類似 RUN groupadd -r postgres && useradd -r -g postgres postgres 的指令創建用戶和用戶組。
WORKDIR:為了清晰性和可靠性,你應該總是在 WORKDIR 中使用絕對路徑。另外,你應該使用 WORKDIR 來替代類似于 RUN cd … && do-something 的指令,后者難以閱讀、排錯和維護。
https://yeasy.gitbook.io/docker_practice/appendix/best_practices
命令驗證執行
從docker build 開發
先做個簡單編譯demo:
FROM ubuntu:18.04 USER root COPY sources.list /etc/apt/sources.list
docker build . 當前目錄執行 可以看到執行情況,一共分為三步
每條指令創建一層:
此時通過docker images就可以看到我們編好的鏡像了,好了正式進入正題了。
from命令幫助我們找尋原始鏡像
第一行:FROM ubuntu:18.04
from本質上等效于 docker pull命令,我們可以使用本地鏡像,也可以指定鏡像源,用如下
????FROM registry.hub.docker.com/library/ubuntu:18.04???
執行效果:
對于國內鏡像源大家可以從此文獲取: https://segmentfault.com/a/1190000023117518
使用RUN命令安裝工具代替我們在容器執行命令:
RUN apt update 不要在腳本中使用apt命令,如果在腳本中使用apt命令,有可能會得到"WARNING: apt does not have a stable CLI interface. Use with caution in scripts." 提示。請使用apt-get、apt-cache等命令進行替換。apt命令不適合在腳本中運行,因為apt命令是為用戶(人)而設計的,它會有顏色的顯示、進度條顯示等一些友好的交互界面。而在腳本中,對于這些“特性”是不穩定(不支持或者是輸出錯亂等)的。
WARNING:?apt?does?not?have?a?stable?CLI?interface.?Use?with?caution?in?scripts.
修改為RUN apt-get update
此外我們可以進行命令一次執行完成
RUN?apt-get?update RUN?apt-get??--fix-broken?install RUN??apt-get?install?-y?gcc?\ ????zip\ ????curl\ ????python\ ????kmod\ ????openssh-server\ ????sudo
安裝之后進行 設置一個賬戶
RUN?groupadd?-g?1011?lyn RUN?useradd?-d?/home/lyn?-m?-s?/bin/bash?-u?1010?-g?lyn?lyn RUN?mkdir?-p?/home/lyn/work\ &&chown?-R?lyn:lyn?/home/lyn RUN?echo?"lyn?ALL=(ALL)????ALL"?>?/etc/sudoers RUN?echo?'root:root'?|?chpasswd?&&?echo?'lyn:lyn'?|?chpassswd WORKDIR?/home/lyn/work USER?lyn COPY?.bashrc?/home/lyn/.bashrc
設置工具目錄,可以看到進去之后工具目錄被設置為/home/lyn,通過WORKDIR /home/lyn/work
執行。
使用
docker images 看一下打包好的鏡像
看到有個沒有命名的包,就是我們剛剛編出來的
這個時候我們進行改個名字
docker tag IMAGEID(鏡像id) REPOSITORY:TAG(倉庫:標簽)
docker tag 025673a91e65 lyn_image:v1
啟動使用 這個時候給大家介紹 volume命令
docker使用volume實現數據的持久化,不僅如此volume還能幫助容器和容器之間,容器和host之間共享數據。
我們可以使用dockerfile的VOLUME或者 docker run -v參數 ,直接設置需要掛載的目錄。
在Dockerfile增加 VOLUME /home/lyn/work
,開始編譯。
編譯完成后,首先通過docker inspect查看我們編譯好的鏡像信息或者容器信息:
docker inspect 47920709b10c 鏡像id進行查看是否設置掛載目錄
docker inspect f4c2449431c5 啟動之后的容器id
docker volume ls 可以看到當前所有的volume
修改映射的文件夾內容
sudo?touch?/var/lib/docker/volumes/567f9c362f6067b3b354bea8b0b370bf304b845ae18e38b125fbdaaface09cfb/_data/lyn.log
可以看到文件已經同步過來了
同樣我們也可以使用 docker run -v進行控制,首先注釋掉這句VOLUME /home/lyn/work
,重新編譯鏡像
docker run -v /home/lyn/docker_share:/home/lyn/work [imageid] -v A:B A是在主機上的地址,B是在容器中的地址,這兩個地址如果不存在都會創建,一旦容器運行,AB的會完全同步。
具體執行為: docker run -it -v /home/lyn/docker_share:/home/lyn/work 208aca0306ab /bin/bash
關于volume更詳細的介紹大家可以看此文:https://docs.docker.com/engine/reference/commandline/volume_create/
寫了一個循環執行的代碼,編譯成固件,用dockerfile 編譯讓鏡像自動執行
COPY hello_world /home/lyn/work CMD ./hello_world
docker run -it 47920709b10c
最終的Dockerfile文件:
FROM?ubuntu:18.04 USER?root COPY?sources.list?/etc/apt/sources.list RUN?apt-get?update RUN??apt-get?install?-y?gcc?\ ????zip\ ????curl\ ????python\ ????kmod\ ????openssh-server\ ????sudo RUN?groupadd?-g?1011?lyn RUN?useradd?-d?/home/lyn?-m?-s?/bin/bash?-u?1010?-g?lyn?lyn RUN?mkdir?-p?/home/lyn/work?\ ???&&chown?-R?lyn:lyn?/home/lyn RUN?echo?"lyn?ALL=(ALL)????ALL"?>?/etc/sudoers RUN?echo?'root:root'?|?chpasswd?&&?echo?'lyn:lyn'?|?chpasswd WORKDIR?/home/lyn/work USER?lyn COPY?.bashrc?/home/lyn/.bashrc VOLUME?/home/lyn/work COPY?hello_world?/home/lyn/work CMD?./hello_world
docker build執行的log:因為有過編譯了,所以這里好多執行就是Using cache,很少的打印了
lyn@lyn:~/Documents/lyn_test/docker_build_ubuntu$?docker?build?. Sending?build?context?to?Docker?daemon??28.16kB Step?1/16?:?FROM?ubuntu:18.04 ?--->?71eaf13299f4 Step?2/16?:?USER?root ?--->?Using?cache ?--->?d3fe45bd0e46 Step?3/16?:?COPY?sources.list?/etc/apt/sources.list ?--->?Using?cache ?--->?d4f825c3fc77 Step?4/16?:?RUN?apt-get?update ?--->?Using?cache ?--->?3863a99d6e2b Step?5/16?:?RUN??apt-get?install?-y?gcc?????zip????curl????python????kmod????openssh-server????sudo ?--->?Using?cache ?--->?9b77c43d6709 Step?6/16?:?RUN?groupadd?-g?1011?lyn ?--->?Using?cache ?--->?bbba5f18057a Step?7/16?:?RUN?useradd?-d?/home/lyn?-m?-s?/bin/bash?-u?1010?-g?lyn?lyn ?--->?Using?cache ?--->?47e999f10256 Step?8/16?:?RUN?mkdir?-p?/home/lyn/work????&&chown?-R?lyn:lyn?/home/lyn ?--->?Using?cache ?--->?36faf04c6390 Step?9/16?:?RUN?echo?"lyn?ALL=(ALL)????ALL"?>?/etc/sudoers ?--->?Using?cache ?--->?0422bf50db6b Step?10/16?:?RUN?echo?'root:root'?|?chpasswd?&&?echo?'lyn:lyn'?|?chpasswd ?--->?Using?cache ?--->?68da5bb15877 Step?11/16?:?WORKDIR?/home/lyn/work ?--->?Using?cache ?--->?b30d4dcd99f8 Step?12/16?:?USER?lyn ?--->?Using?cache ?--->?5dfa565d0c6a Step?13/16?:?COPY?.bashrc?/home/lyn/.bashrc ?--->?Using?cache ?--->?f2b39d61f05b Step?14/16?:?VOLUME?/home/lyn/work ?--->?Running?in?5482493ab221 Removing?intermediate?container?5482493ab221 ?--->?0e359e093a4f Step?15/16?:?COPY?hello_world?/home/lyn/work ?--->?ac326932752c Step?16/16?:?CMD?./hello_world ?--->?Running?in?430cfd90ad30 Removing?intermediate?container?430cfd90ad30 ?--->?fc180e4919da Successfully?built?fc180e4919da
補充操作:
發現兩個鏡像的id相同,如果用docker rmi [鏡像id]它就不知道該如何刪除,我們可以用:
???Error?response?from?daemon:?conflict:?unable?to?delete?71eaf13299f4?(must?be?forced)?-?image?is?referenced?in?multiple?repositories ???
docker rmi 鏡像名:版本號 當我建立錯誤的鏡像之后,使用rmi進行刪除
結語
這就是我自己的一些Dockerfile使用分享。如果大家有更好的想法和需求,也歡迎大家加我好友交流分享哈。
此外對于想要更加細節的dockerfile使用可以官網的文章: https://docs.docker.com/engine/reference/builder/
https://docs.docker.com/develop/develop-images/dockerfile_best-practices/
這篇文章:https://yeasy.gitbook.io/docker_practice/image/build