Docker Dockerfile

Docker 能夠經過 Dockerfile 的內容來自動構建鏡像。python

Dockerfile 是一個包含建立鏡像全部命令的文本文件,經過docker build命令能夠根據 Dockerfile 的內容構建鏡像,在介紹如何構建以前先介紹下 Dockerfile 的基本語法結構。mysql

Dockerfile 有如下指令選項:nginx

  • FROM
  • MAINTAINER
  • RUN
  • CMD
  • EXPOSE
  • ENV
  • ADD
  • COPY
  • ENTRYPOINT
  • VOLUME
  • USER
  • WORKDIR
  • ONBUILD

 7.1 FROM

用法:git

FROM <image>
  • FROM指定構建鏡像的基礎源鏡像,若是本地沒有指定的鏡像,則會自動從 Docker 的公共庫 pull 鏡像下來。
  • FROM必須是 Dockerfile 中非註釋行的第一個指令,即一個 Dockerfile 從FROM語句開始。
  • FROM能夠在一個 Dockerfile 中出現屢次,若是有需求在一個 Dockerfile 中建立多個鏡像。
  • 若是FROM語句沒有指定鏡像標籤,則默認使用latest標籤。

7.2 MAINTAINER

用法:github

MAINTAINER <name>

  

指定建立鏡像的用戶golang

RUN 有兩種使用方式sql

每條RUN指令將在當前鏡像基礎上執行指定命令,並提交爲新的鏡像,後續的RUN都在以前RUN提交後的鏡像爲基礎,鏡像是分層的,能夠經過一個鏡像的任何一個歷史提交點來建立,相似源碼的 版本控制 。docker

exec 方式會被解析爲一個 JSON 數組,因此必須使用雙引號而不是單引號。exec 方式不會調用一個命令 shell,因此也就不會繼承相應的變量,如:shell

RUN [ "echo", "$HOME" ]

這種方式是不會達到輸出 HOME 變量的,正確的方式應該是這樣的數據庫

RUN [ "sh", "-c", "echo", "$HOME" ]
RUN產生的緩存在下一次構建的時候是不會失效的,會被重用,可使用--no-cache選項,即docker build --no-cache,如此便不會緩存。

7.3 CMD

CMD有三種使用方式:

CMD指定在 Dockerfile 中只能使用一次,若是有多個,則只有最後一個會生效。

CMD的目的是爲了在啓動容器時提供一個默認的命令執行選項。若是用戶啓動容器時指定了運行的命令,則會覆蓋掉CMD指定的命令。

CMD會在啓動容器的時候執行,build 時不執行,而RUN只是在構建鏡像的時候執行,後續鏡像構建完成以後,啓動容器就與RUN無關了,這個初學者容易弄混這個概念,這裏簡單註解一下。

  7.4 EXPOSE

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

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

 7.5 ENV

ENV <key> <value>       # 只能設置一個變量
ENV <key>=<value> ...   # 容許一次設置多個變量

指定一個環節變量,會被後續RUN指令使用,並在容器運行時保留。

例子:

ENV myName="John Doe" myDog=Rex\ The\ Dog \
    myCat=fluffy

等同於

ENV myName John Doe
ENV myDog Rex The Dog
ENV myCat fluffy

 7.6 ADD

ADD <src>... <dest>
ADD複製本地主機文件、目錄或者遠程文件 URLS 從 而且添加到容器指定路徑中 。

支持經過 Go 的正則模糊匹配,具體規則可參見  Go filepath.Match

ADD hom* /mydir/        # adds all files starting with "hom"
ADD hom?.txt /mydir/    # ? is replaced with any single character
  • 路徑必須是絕對路徑,若是 不存在,會自動建立對應目錄
  • 路徑必須是 Dockerfile 所在路徑的相對路徑
  • 若是是一個目錄,只會複製目錄下的內容,而目錄自己則不會被複制

 7.7 COPY

COPY <src>... <dest>
COPY複製新文件或者目錄從 而且添加到容器指定路徑中 。用法同ADD,惟一的不一樣是不能指定遠程文件 URLS。

 7.8 ENTRYPOINT

配置容器啓動後執行的命令,而且不可被 docker run 提供的參數覆蓋,而CMD是能夠被覆蓋的。若是須要覆蓋,則可使用docker run --entrypoint選項。

每一個 Dockerfile 中只能有一個ENTRYPOINT,當指定多個時,只有最後一個生效。

Exec form ENTRYPOINT 例子

經過ENTRYPOINT使用 exec form 方式設置穩定的默認命令和選項,而使用CMD添加默認以外常常被改動的選項。

FROM ubuntu
ENTRYPOINT ["top", "-b"]
CMD ["-c"]

經過 Dockerfile 使用ENTRYPOINT展現前臺運行 Apache 服務

FROM debian:stable
RUN apt-get update && apt-get install -y --force-yes apache2
EXPOSE 80 443
VOLUME ["/var/www", "/var/log/apache2", "/etc/apache2"]
ENTRYPOINT ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]

Shell form ENTRYPOINT 例子

這種方式會在/bin/sh -c中執行,會忽略任何CMD或者docker run命令行選項,爲了確保docker stop可以中止長時間運行ENTRYPOINT的容器,確保執行的時候使用exec選項。

FROM ubuntu
ENTRYPOINT exec top -b

  

若是在ENTRYPOINT忘記使用exec選項,則可使用CMD補上:

FROM ubuntu
ENTRYPOINT top -b
CMD --ignored-param1 # --ignored-param2 ... --ignored-param3 ... 依此類推

  

 7.9 VOLUME

VOLUME ["/data"]
建立一個能夠從本地主機或其餘容器掛載的掛載點,後續具體介紹。

7.10 USER

USER daemon
指定運行容器時的用戶名或 UID,後續的RUN、CMD、ENTRYPOINT也會使用指定用戶。

 7.11 WORKDIR

WORKDIR /path/to/workdir

  

爲後續的RUN、CMD、ENTRYPOINT指令配置工做目錄。可使用多個WORKDIR指令,後續命令若是參數是相對路徑,則會基於以前命令指定的路徑。
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd

最終路徑是/a/b/c。

WORKDIR指令能夠在ENV設置變量以後調用環境變量:

ENV DIRPATH /path
WORKDIR $DIRPATH/$DIRNAME

最終路徑則爲 /path/$DIRNAME。

7.12 ONBUILD

ONBUILD [INSTRUCTION]
配置當所建立的鏡像做爲其它新建立鏡像的基礎鏡像時,所執行的操做指令。

例如,Dockerfile 使用以下的內容建立了鏡像 image-A:

[...]
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
[...]

若是基於 image-A 建立新的鏡像時,新的 Dockerfile 中使用 FROM image-A 指定基礎鏡像時,會自動執行 ONBUILD 指令內容,等價於在後面添加了兩條指令。

# Automatically run the following
ADD . /app/src
RUN /usr/local/bin/python-build --dir /app/src

使用ONBUILD指令的鏡像,推薦在標籤中註明,例如 ruby:1.9-onbuild。

7.13 Dockerfile Examples

# Nginx
#
# VERSION               0.0.1

FROM      ubuntu
MAINTAINER Victor Vieux <victor@docker.com>

RUN apt-get update && apt-get install -y inotify-tools nginx apache2 openssh-server

# Firefox over VNC
#
# VERSION               0.3

FROM ubuntu

# Install vnc, xvfb in order to create a 'fake' display and firefox
RUN apt-get update && apt-get install -y x11vnc xvfb firefox
RUN mkdir ~/.vnc
# Setup a password
RUN x11vnc -storepasswd 1234 ~/.vnc/passwd
# Autostart firefox (might not be the best way, but it does the trick)
RUN bash -c 'echo "firefox" >> /.bashrc'

EXPOSE 5900
CMD    ["x11vnc", "-forever", "-usepw", "-create"]

# Multiple images example
#
# VERSION               0.1

FROM ubuntu
RUN echo foo > bar
# Will output something like ===> 907ad6c2736f

FROM ubuntu
RUN echo moo > oink
# Will output something like ===> 695d7793cbe4

# You᾿ll now have two images, 907ad6c2736f with /bar, and 695d7793cbe4 with
# /oink.

  

7.14 docker build

$ docker build --help

Usage: docker build [OPTIONS] PATH | URL | -

Build a new image from the source code at PATH

  --force-rm=false     Always remove intermediate containers, even after unsuccessful builds # 移除過渡容器,即便構建失敗
  --no-cache=false     Do not use cache when building the image                              # 不實用 cache        
  -q, --quiet=false    Suppress the verbose output generated by the containers               
  --rm=true            Remove intermediate containers after a successful build               # 構建成功後移除過渡層容器
  -t, --tag=""         Repository name (and optionally a tag) to be applied to the resulting image in case of success

  

參考文檔: Dockerfile Reference

7.15 dockerfile 最佳實踐

  • 使用.dockerignore文件

爲了在docker build過程當中更快上傳和更加高效,應該使用一個.dockerignore文件用來排除構建鏡像時不須要的文件或目錄。例如,除非. Git 在構建過程當中須要用到,不然你應該將它添加到.dockerignore文件中,這樣能夠節省不少時間。

  • 避免安裝沒必要要的軟件包

爲了下降複雜性、依賴性、文件大小以及構建時間,應該避免安裝額外的或沒必要要的包。例如,不須要在一個 數據庫 鏡像中安裝一個文本編輯器。

  • 每一個容器都跑一個進程

在大多數狀況下,一個容器應該只單獨跑一個程序。解耦應用到多個容器使其更容易橫向擴展和重用。若是一個服務依賴另一個服務,能夠參考  Linking Containers Together 。

  • 最小化層

咱們知道每執行一個指令,都會有一次鏡像的提交,鏡像是分層的結構,對於Dockerfile,應該找到可讀性和最小化層之間的平衡。

  • 多行參數排序

若是可能,經過字母順序來排序,這樣能夠避免安裝包的重複而且更容易更新列表,另外可讀性也會更強,添加一個空行使用\換行:

RUN apt-get update && apt-get install -y \
  bzr \
  cvs \
  git \
  mercurial \
  subversion
  • 建立緩存

鏡像構建過程當中會按照Dockerfile的順序依次執行,每執行一次指令 Docker 會尋找是否有存在的鏡像緩存可複用,若是沒有則建立新的鏡像。若是不想使用緩存,則能夠在docker build時添加--no-cache=true選項。

從基礎鏡像開始就已經在緩存中了,下一個指令會對比全部的子鏡像尋找是否執行相同的指令,若是沒有則緩存失效。在大多數狀況下只對比Dockerfile指令和子鏡像就足夠了。ADD和COPY指令除外,執行ADD和COPY時存放到鏡像的文件也是須要檢查的,完成一個文件的校驗以後再利用這個校驗在緩存中查找,若是檢測的文件改變則緩存失效。RUN apt-get -y update命令只檢查命令是否匹配,若是匹配就不會再執行更新了。

爲了有效地利用緩存,你須要保持你的 Dockerfile 一致,而且儘可能在末尾修改。

Dockerfile 指令

  • FROM: 只要可能就使用官方鏡像庫做爲基礎鏡像
  • RUN: 爲保持可讀性、方便理解、可維護性,把長或者複雜的RUN語句使用\分隔符分紅多行
    • 不建議RUN apt-get update獨立成行,不然若是後續包有更新,那麼也不會再執行更新
    • 避免使用RUN apt-get upgrade或者dist-upgrade,不少必要的包在一個非privileged權限的容器裏是沒法升級的。若是知道某個包更新,使用apt-get install -y xxx
    • 標準寫法
      • RUN apt-get update && apt-get install -y package-bar package-foo

例子:

RUN apt-get update && apt-get install -y \
    aufs-tools \
    automake \
    btrfs-tools \
    build-essential \
    curl \
    dpkg-sig \
    git \
    iptables \
    libapparmor-dev \
    libcap-dev \
    libsqlite3-dev \
    lxc=1.0* \
    mercurial \
    parallel \
    reprepro \
    ruby1.9.1 \
    ruby1.9.1-dev \
    s3cmd=1.1.0*

  

  • CMD: 推薦使用CMD [「executable」, 「param1」, 「param2」…]這種格式,CMD [「param」, 「param」]則配合ENTRYPOINT使用
  • EXPOSE: Dockerfile 指定要公開的端口,使用docker run時指定映射到宿主機的端口便可
  • ENV: 爲了使新的軟件更容易運行,可使用ENV更新PATH變量。如ENV PATH /usr/local/nginx/bin:$PATH確保CMD ["nginx"]便可運行

ENV也能夠這樣定義變量:

ENV PG_MAJOR 9.3
ENV PG_VERSION 9.3.4
RUN curl -SL http://example.com/postgres-$PG_VERSION.tar.xz | tar -xJC /usr/src/postgress && …
ENV PATH /usr/local/postgres-$PG_MAJOR/bin:$PATH

  

  • ADDorCOPY:ADD比COPY多一些特性「tar 文件自動解包和支持遠程 URL」,不推薦添加遠程 URL

如不推薦這種方式:

ADD http://example.com/big.tar.xz /usr/src/things/
RUN tar -xJf /usr/src/things/big.tar.xz -C /usr/src/things
RUN make -C /usr/src/things all

推薦使用 curl 或者 wget 替換,使用以下方式:

RUN mkdir -p /usr/src/things \
    && curl -SL http://example.com/big.tar.gz \
    | tar -xJC /usr/src/things \
    && make -C /usr/src/things all

若是不須要添加 tar 文件,推薦使用COPY。

相關文章
相關標籤/搜索