Docker學習——Dockerfile

上一篇咱們講了docker的基本使用,掌握了前一篇,docker使用基本不成問題,可是要是你學習了Dockerfile,你會發現它使用起來有多方便了。項目最終部署時,咱們但願docker容器打開時項目也隨之運行,至於裏面要運行什麼命令才能運行起來,不是使用者該考慮的事情,光有上一篇的知識還作不到。html

 

1、Dockerfile語法python

使用Dockerfile去構建鏡像比如堆積木,Docker經過對於在Dockerfile中的一系列指令的順序解析實現自動的image的構建。Dockerfile是由一系列命令和參數構成的腳本,一個Dockerfile裏面包含了構建整個image的完整命令。Docker經過docker build執行Dockerfile中的一系列命令自動構建image。下面就把Dockerfile中主要的命令介紹一下。web

一、FROMdocker

FROM  <image>[:<tag> | @<digest>] [AS <name>]

    FROM指定一個基礎鏡像,且必須爲Dockerfile文件開篇的每一個非註釋行,至於image則能夠是任何合理存在的image鏡像
    FROM能夠在一個Dockerfile中出現屢次,以便於建立混合的images。若是沒有指定tag,latest將會被指定爲要使用的基礎鏡像版本。
    AS name,能夠給新的構建階段賦予名稱。該名稱可用於後續FROM 和 COPY --from=<name | index>說明能夠引用此階段中構建的鏡像

二、LABELshell

爲鏡像生成元數據標籤信息,就是鏡像的描述信息flask

LABEL <key>=<value> <key>=<value> <key>=<value> ...
好比:
LABEL "com.example.vendor"="ACME Incorporated"
LABEL com.example.label-with-value="foo"
LABEL version="1.0"
LABEL description="This text illustrates that label-values can span multiple lines."

三、MAINTAINER數組

做者信息,寫在FROM後,指明該鏡像的做者和其電子郵件,這個不是必須的項,並且官網顯示將棄用緩存

MAINTAINER zzf  "xxxxxxx@qq.com"

四、COPYruby

將主機的文件複製到鏡像內,若是目的位置不存在,Docker會自動建立全部須要的目錄結構,可是它只是單純的複製,並不會去作文件提取和解壓工做。如:bash

語法:
COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"]

例如:
COPY hom* /mydir/        # 添加全部以hom開頭的文檔到/mydir/下
COPY hom?.txt /mydir/    # ? is replaced with any single character, e.g., "home.txt"
COPY test relativeDir/   # adds "test" to `WORKDIR`/relativeDir/
COPY test /absoluteDir/  # adds "test" to /absoluteDir/
COPY arr[[]0].txt /mydir/    # copy a file named "arr[0].txt" to /mydir/

注意:須要複製的目錄必定要放在Dockerfile文件的同級目錄下

由於構建環境將會上傳到Docker守護進程,而複製是在Docker守護進程中進行的。任何位於構建環境以外的東西都是不可用的。COPY指令的目的的位置則必須是容器內部的一個絕對路徑。

五、ADD

ADD指令相似於COPY指令,ADD支持使用TAR文件和URL路徑,可是ADD會對壓縮文件(tar, gzip, bzip2, etc)作提取和解壓操做。

語法:
ADD <src>...<dest>
ADD ["<src>",..."<dest>"]
規則:
1   若是<src>爲URL且<dest>不以/結尾,則<src>指定的文件將被下載並直接被建立爲<dest>;若是<dest>以/結尾,則文件名URL指定的文件將被直接下載並保存爲<dest>/<filename>
2   若是<src>是一個本地文件系統上的壓縮格式的tar文件,它將被展開爲一個目錄,其行爲相似於"tar -x"命令;然而,經過URL獲取到的tar文件將不會自動展開。
3   若是<src>有多個,或其間接或直接使用了通配符,則<dest>必須是一個以/結尾的目錄路徑;若是<dest>不以/結尾,則其被視做一個普通文件,<src>內容將被直接寫入到<dest>
4   爲了讓鏡像儘可能小,最好不要使用 ADD 指令從遠程 URL 獲取包,而是使用 curl 和 wget。這樣你能夠在文件提取完以後刪掉再也不須要的文件來避免在鏡像中額外添加一層。

例如:
ADD http://example.com/1.tar.gz /apps/
RUN tar xf /apps/1.tar.gz -C /apps/ && \
    /bin/sh -c /apps/***.sh
     
簡單操做:
RUN mkdir -p /iyunwen/server/ && \
        curl -SL http://example.com/1.tar.gz \
        | tar -xzC /iyunwen/server/ && \
        /bin/sh -c /apps/***.sh

六、WORKDIR

用於爲Dockerfile中全部RUN、CMD、ENTRYPOINT、COPY和ADD指令設定工做目錄,若是不存在,則會建立目錄。在Dockerfile文件中,WORKDIR指令能夠出現屢次,其路徑也能夠爲相對路徑,不過,其是相對此前一個WORKDIR指令指定的路徑,另外,WORKDIR也可調用由ENV指定定義的變量,如

WORKDIR /usr/local
WORKDIR webservice
RUN echo 'hello docker' > text.txt
...
最終會在目錄下生成text.txt文件
WORKDIR $STATEPATH
/usr/local/webservice/

 七、RUN

接受命令做爲參數並用於建立鏡像,在以前的commit層上造成新的層。在新鏡像內部執行的命令,好比安裝一些軟件、配置一些基礎環境,可以使用\來換行

語法:
RUN \<command\>(如同執行shell命令 /bin/sh -c)
RUN ["executable","param1","param2"]
    • RUN 指令將在當前image中執行任意合法命令並提交執行結果。命令執行提交後,就會自動執行Dockerfile中的下一個指令。
    • 分層RUN指令和生成提交符合Docker的核心概念,其中提交很輕量,能夠從image將用於Dockerfile中的下一步。
    • exec形式使得能夠避免shell字符串變化,以及使用不包含指定的shell可執行文件的基本image來運行RUN命令。
    • 在shell形式中,可使用\(反斜槓)將單個RUN指令繼續到下一行。例如:
      RUN yum install -y \ openssl \ pcre-devel \ zlib
    • 第二種語法格式中的參數是一個JSON格式的數組,其中<executable>爲要運行的命令,後面的<paramN>爲傳遞給命令的選項或對數;然而,此種格式指定的命令不會以"/bin/sh -c"來發起,所以常見的shell操做如變量替換以及通配符(?,*等)替換將不會進行;不過,若是要運行的命令依賴於此shell特性的話,能夠將其替換爲相似下面的格式。
RUN ["/bin/bash","-c","<executable>","<param1>"]

RUN 指令的緩存在下一次構建期間不會自動失效。用於諸如:yum repolist 之類的指令的緩存將在下一次構建期間被重用。能夠經過--no-cache 參數來使RUN指令的緩存無效,例如: docker build --no-cache

管理命令
某些RUN 命令依賴於使用管道字符( | )將管道輸出到另外一個命令功能

RUN wget -O - http://www.baidu.com/index.html | wc -l > /app/html/baidu.html

Docker使用 /bin/sh -c 解釋執行這些命令,解釋器只評估管道中最後一個操做的退出代碼以肯定成功。在上面的例子中,只要wc -l 命令成功,即便wget 命令失敗,該構建步驟也會成功並生成新的鏡像。

因爲管道中任何階段的錯誤而致使命令失敗,請預先 set -o pipefail && 確保意外錯誤可防止構建無心中成功。例如:
set -o pipefail : 表示在管道鏈接的命令序列中,只要有任何一個命令返回非0值,則整個管道返回非0值,即便最後一個命令返回0.

RUN set -o pipefail && wget -O - http://www.baidu.com/index.html | wc -l > /app/html/baidu.html

注意:

並不是全部的shell都支持 -o pipefail 選項。在這種狀況下(例如 dash shell,這是基於Debian的映像上的默認shell),請考慮使用exec形式RUN來明確選擇一個支持該pipefail選項的shell。如:

RUN ["/bin/bash","-c","set -o pipefail && wget -O - http://www.baidu.com/index.html | wc -l > /app/html/baidu.html"]

八、CMD

相似於RUN指令,CMD指令也可用於運行任何命令或應用程序,不過,兩者的運行時間點不一樣

  • RUN 指令運行於映像文件構建過程當中,而CMD指令運行於基於Dockerfile構建出的新鏡像文件啓動一個容器時。
  • CMD指令的首要目的在於爲啓動的容器指定默認要運行的程序,且其運行結束後,容器也將終止;不過,CMD指定的命令其能夠被docker run的命令行選項所覆蓋
  • 在Dockerfile中能夠存在多個CMD指令,但僅最後一個生效
語法:
CMD <command>   //支持命令展開,可是不支持傳遞信號 
CMD ["<executable>","<param1>","<param2>"]  //至關於容器的第一個命令,能夠接受信號
CMD ["param1","param2"]
前兩種語法格式的意義同RUN
第三種則用於爲ENTRYPOINT指令提供默認參數

CMD會在啓動容器的時候執行,build時不執行,而RUN只是在構建鏡像的時候執行,後續鏡像構建完成以後,啓動容器就與RUN無關了。這個命令就至關於在/etc/rc.d/rc.local中寫命令

九、ENTRYPOINT

相似CMD指令的功能,用於爲容器指定默認運行程序,從而使得容器像是一具單獨的可執行程序
與CMD不一樣的是,由ENTRYPOINT啓動的程序不會被docker run命令行指定的參數所覆蓋,並且,這些命令行參數會被看成參數傳遞給ENTRYPOINT指定的程序。不過,docker run 命令的--entrypoint 選項的參數可覆蓋ENTRYPOINT指令指定的程序

語法:
ENTRYPOINT <command>    //這種方式能接受shell命令行展開
ENTRYPOINT ["<executable>","param1"]  //展開不了,但能接收到信號

注意:

docker run命令傳入的命令參數會覆蓋CMD指令的內容而且附加到ENTRYPOINT命令最後作爲其參數使用。Dockerfile文件中也能夠存在多個ENTRYPOINT指令,但僅有最後一個會生效

十、EXPOSE

用來指定端口,使容器內的應用能夠經過端口和外界交互。

EXPOSE <port> [<port>...]

告訴Docker服務端容器對外映射的本地端口,須要在docker run 的時候使用-p 或者 -P 選項生效。

EXPOSE 5000

十一、ENV

ENV指令能夠用於docker容器設置環境變量

語法:
ENV <key> <value>
ENV <key>=<value> ...

指定一個環境變量,會被後續RUN指令使用,並在容器運行時保留。
ENV設置的環境變量,可使用 docker inspect 命令來查看。同時還可使用 docker run --env <key>=<value>來修改環境變量

十二、USER

用於指定運行image時的或運行Dockerfile中任何RUN、CMD或ENTRYPOINT指令指定的程序時的用戶名或UID
默認狀況下,container的運行身份爲root用戶

語法:
USER <UID>|<UserName> 

須要注意的是,<UID>能夠爲任意數字,但實踐中其必須爲/etc/passwd中某用戶的有效UID,不然,docker run命令將運行失敗

1三、ONBUILD

用於在Dockerfile中定義一個觸發器
Dockerfile用於build鏡像文件,此鏡像文件亦可做爲base image被另外一個Dockerfile用做FROM指令的參數,並以之構建新的鏡像文件
在後的這個Dockerfile中的FROM指令在build過程當中被執行時,將會「觸發」建立其base image的Dockerfile文件中的ONBUILD指令定義的觸發器

語法:
ONBUILD <INSTRUCTION>

注意:
儘管任何指令均可註冊成爲觸發器指令,但ONBUILD不能自我嵌套,且不會觸發FROM和MAINTAINER指令
使用包含ONBUILD指令的Dockerfile構建的鏡像應該使用特殊的標籤,例如ruby:2.0-onbuild
在ONBUILD指令中使用ADD或COPY指令應該格外當心,由於新構建過程和上下文在缺乏指定的源文件時會失敗。

1四、ARG

ARG指令定義了用戶能夠在編譯時或者運行時傳遞的變量

ARG <name>[=<default value>]

ENV指令是在dockerfile裏面設置環境變量,不能在編譯時或運行時傳遞。

 

2、舉個栗子

咱們作一個很是簡單的例子,只是爲了使用Dockerfile建立一個image:存一個hello.py文件到基礎python鏡像的/opt目錄下,並最後運行它,最後容器啓動時能自動運行。

我在本地Flask_web文件夾下新建了一個hello.py文檔,內容以下:

from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello():
    return 'hello world!'

if __name__ == '__main__':
    app.run(host='0.0.0.0')

就是一個簡單的flask語句,只返回一個hello world。

而後咱們在本地新建一個Dockerfile文件,注意D大寫,內容以下:

FROM python_v5:latest
ADD ./hello.py /opt
WORKDIR /opt
EXPOSE 5000
CMD python hello.py

可見咱們運用python_v5:latest 做爲基礎鏡像,這裏就是一個python鏡像安裝了flask庫後commit出來的一個新鏡像。而後把本地hello.py 文件添加到容器/opt 目錄下,而後容器運行時的工做目錄設置爲/opt,即容器會自動跳轉到/opt下才執行下面的CMD語句,而後開放5000端口給外部,最後容器啓動時執行python hello.py的shell 命令,這裏要注意若是要執行多條shell命令,多條命令之間能夠用&&鏈接起來便可,容器會自動調用/bin/sh -c 執行裏面的命令。

寫好了Dockerfile文件,咱們就能夠開始創建咱們的鏡像了,這裏咱們要學習一個新命令,這個專爲Dockerfile所用

docker build 

命令用於使用 Dockerfile 建立鏡像。

語法:
docker build [OPTIONS] PATH | URL | -

OPTIONS說明:

  • --build-arg=[] :設置鏡像建立時的變量;

  • --cpu-shares :設置 cpu 使用權重;

  • --cpu-period :限制 CPU CFS週期;

  • --cpu-quota :限制 CPU CFS配額;

  • --cpuset-cpus :指定使用的CPU id;

  • --cpuset-mems :指定使用的內存 id;

  • --disable-content-trust :忽略校驗,默認開啓;

  • -f :指定要使用的Dockerfile路徑;

  • --force-rm :設置鏡像過程當中刪除中間容器;

  • --isolation :使用容器隔離技術;

  • --label=[] :設置鏡像使用的元數據;

  • -m :設置內存最大值;

  • --memory-swap :設置Swap的最大值爲內存+swap,"-1"表示不限swap;

  • --no-cache :建立鏡像的過程不使用緩存;

  • --pull :嘗試去更新鏡像的新版本;

  • --quiet, -q :安靜模式,成功後只輸出鏡像 ID;

  • --rm :設置鏡像成功後刪除中間容器;

  • --shm-size :設置/dev/shm的大小,默認值是64M;

  • --ulimit :Ulimit配置。

  • --tag, -t: 鏡像的名字及標籤,一般 name:tag 或者 name 格式;能夠在一次構建中爲一個鏡像設置多個標籤。

  • --network: 默認 default。在構建期間設置RUN指令的網絡模式

最經常使用的命令就是

docker build -t  image_name:tag(定義要生成的鏡像的名稱和版本號)  path(Dockerfile文件所在的目錄路徑)

接下來咱們在Flask_web目錄下執行以下:

可見它一步一步地執行了咱們Dockerfile裏的命令,我這裏多加了個run.sh命令進去,你們忽略就好。用docker images查看到咱們簡歷好了命名爲flask的鏡像。

下面咱們啓動它,看是否是執行了咱們Dockerfile文件中最後CMD中給的shell 命令,即運行hello.py文件,這裏咱們能夠用-d在後臺運行,也能夠-it交互式運行,交互式運行能看到輸出結果。我這裏用交互式運行,並且由於是flask web程序,須要端口鏈接,這裏用-p 宿主機5000端口和容器5000端口匹配,這樣咱們訪問本地5000端口就能夠訪問到容器的5000端口了,咱們Dockerfile文件EXPOSE寫了容器開放端口是5000:

可見flask跑起來了,那咱們測試一下:

並且在其餘電腦輸入本機IP:5000也能夠運行:

好了,是否是以爲Dockerfile很方便呢?鏡像作好了,把它保存爲tar格式文件就能夠傳給別人使用了,使用時只須要下載鏡像到本地而後run命令開啓容器便可,而且開啓容器的同時程序也跑起來了,這樣就作到了用戶和設計的徹底隔離,即用戶不用去管容器裏面程序是怎麼跑起來的,啓動容器就可使用。

相關文章
相關標籤/搜索