上一篇:Docker的那些事兒—docker commit與docker build比較(12)
Dockerfile是由一行行命令語句組成绝编,并且支持以#開頭的注釋行。
一般的,Dockerfile分為四部分:基礎鏡像信息、維護者信息、鏡像操作指令和容器啟動時執(zhí)行指令哎壳。通過一個例子看下:
FROM debian:stretch-slim
LABEL maintainer="NGINX Docker Maintainers "
ENVNGINX_VERSION 1.13.8-1~stretch
ENVNJS_VERSION? 1.13.8.0.1.15-1~stretch
RUN set-x \
? ? ? && apt-get update \
? ? ? && apt-get install --no-install-recommends --no-install-suggests -y gnupg1 \
? ? ? && \
? ? ? NGINX_GPGKEY=573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62;\
? ? ? found=''; \
? ? ? for server in \
? ? ? ? ? ? ? ha.pool.sks-keyservers.net \
? ? ? ? ? ? ? hkp://keyserver.ubuntu.com:80 \
? ? ? ? ? ? ? hkp://p80.pool.sks-keyservers.net:80\
? ? ? ? ? ? ? pgp.mit.edu \
? ? ? ; do \
? ? ? ? ? ? ? echo "Fetching GPG key$NGINX_GPGKEY from $server"; \
? ? ? ? ? ? ? apt-key adv --keyserver"$server" --keyserver-options timeout=10 --recv-keys"$NGINX_GPGKEY" && found=yes && break; \
? ? ? done; \
? ? ? test -z "$found" &&echo >&2 "error: failed to fetch GPG key $NGINX_GPGKEY"&& exit 1; \
? ? ? apt-get remove --purge --auto-remove -ygnupg1 && rm -rf /var/lib/apt/lists/* \
? ? ? && dpkgArch="$(dpkg--print-architecture)" \
? ? ? && nginxPackages=" \
? ? ? ? ? ? ? nginx=${NGINX_VERSION} \
? ? ? ? ? ? ? nginx-module-xslt=${NGINX_VERSION}\
? ? ? ? ? ? ? nginx-module-geoip=${NGINX_VERSION}\
? ? ? ? ? ? ? nginx-module-image-filter=${NGINX_VERSION}\
? ? ? ? ? ? ? nginx-module-njs=${NJS_VERSION} \
? ? ? " \
? ? ? && case "$dpkgArch" in\
? ? ? ? ? ? ? amd64|i386) \
# archesofficialy built by upstream
? ? ? ? ? ? ? ? ? ? echo "debhttp://nginx.org/packages/mainline/debian/ stretch nginx" >>/etc/apt/sources.list \
? ? ? ? ? ? ? ? ? ? && apt-get update \
? ? ? ? ? ? ? ? ? ? ;; \
? ? ? ? ? ? ? *) \
# we'reon an architecture upstream doesn't officially build for
# let'sbuild binaries from the published source packages
? ? ? ? ? ? ? ? ? ? echo "deb-srchttp://nginx.org/packages/mainline/debian/ stretch nginx" >>/etc/apt/sources.list \
? ? ? ? ? ? ? ? ? ? \
# newdirectory for storing sources and .deb files
? ? ? ? ? ? ? ? ? ? &&tempDir="$(mktemp -d)" \
? ? ? ? ? ? ? ? ? ? && chmod 777"$tempDir" \
# (777 toensure APT's "_apt" user can access it too)
? ? ? ? ? ? ? ? ? ? \
# savelist of currently-installed packages so build dependencies can be cleanlyremoved later
? ? ? ? ? ? ? ? ? ? && savedAptMark="$(apt-markshowmanual)" \
? ? ? ? ? ? ? ? ? ? \
# build.deb files from upstream's source packages (which are verified by apt-get)
? ? ? ? ? ? ? ? ? ? && apt-get update \
? ? ? ? ? ? ? ? ? ? && apt-getbuild-dep -y $nginxPackages \
? ? ? ? ? ? ? ? ? ? && ( \
? ? ? ? ? ? ? ? ? ? ? ? ? ? cd"$tempDir" \
? ? ? ? ? ? ? ? ? ? ? ? ? ? &&DEB_BUILD_OPTIONS="nocheck parallel=$(nproc)" \
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? apt-getsource --compile $nginxPackages \
? ? ? ? ? ? ? ? ? ? ) \
# wedon't remove APT lists here because they get re-downloaded and removed later
? ? ? ? ? ? ? ? ? ? \
# resetapt-mark's "manual" list so that "purge --auto-remove" willremove all build dependencies
# (whichis done after we install the built packages so we don't have to redownload anyoverlapping dependencies)
? ? ? ? ? ? ? ? ? ? && apt-markshowmanual | xargs apt-mark auto > /dev/null \
? ? ? ? ? ? ? ? ? ? && { [ -z"$savedAptMark" ] || apt-mark manual $savedAptMark; } \
? ? ? ? ? ? ? ? ? ? \
# createa temporary local APT repo to install from (so that dependency resolution canbe handled by APT, as it should be)
? ? ? ? ? ? ? ? ? ? && ls -lAFh"$tempDir" \
? ? ? ? ? ? ? ? ? ? && ( cd"$tempDir" && dpkg-scanpackages . > Packages ) \
? ? ? ? ? ? ? ? ? ? && grep '^Package:' "$tempDir/Packages" \
? ? ? ? ? ? ? ? ? ? && echo "deb [trusted=yes ] file://$tempDir ./" > /etc/apt/sources.list.d/temp.list \
# workaround the following APT issue by using "Acquire::GzipIndexes=false"(overriding "/etc/apt/apt.conf.d/docker-gzip-indexes")
#? Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages- open (13: Permission denied)
#? ...
#? E: Failed to fetchstore:/var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages? Could not open file/var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permissiondenied)
? ? ? ? ? ? ? ? ? ? && apt-get -oAcquire::GzipIndexes=false update \
? ? ? ? ? ? ? ? ? ? ;; \
? ? ? esac \
? ? ? \
? ? ? && apt-get install--no-install-recommends --no-install-suggests -y \
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? $nginxPackages\
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? gettext-base\
? ? ? && rm -rf /var/lib/apt/lists/* \
? ? ? \
# if wehave leftovers from building, let's purge them (including extra, unnecessarybuild deps)
? ? ? && if [ -n "$tempDir"]; then \
? ? ? ? ? ? ? apt-get purge -y --auto-remove \
? ? ? ? ? ? ? && rm -rf"$tempDir" /etc/apt/sources.list.d/temp.list; \
? ? ? fi
# forwardrequest and error logs to docker log collector
RUN ln-sf /dev/stdout /var/log/nginx/access.log \
? ? ? && ln -sf /dev/stderr/var/log/nginx/error.log
EXPOSE 80
STOPSIGNALSIGTERM
CMD["nginx", "-g", "daemon off;"]
指令的一般格式為INSTRUCTION arguments,指令包括 FROM、 MAINTAINER攘滩、RUN 等。INSTRUCTION實際上是不區(qū)分大小寫的纸泡,只所以寫成大寫是為了區(qū)別于后面的arguments漂问。
FROM
格式為:
? ? FROM image
? ? FROM image:tag
Docker逐條運行Dockerfile中的指令。第一條指令必須為FROM指令女揭。并且如果在同一個Dockerfile中創(chuàng)建多個鏡像時蚤假,可以使用多個FROM指令(每個鏡像一次)。
MAINTAINER
格式為:
? ? MAINTAINER name
指定維護者信息吧兔。
ENV
格式為:
? ? ENV key value
指定一個環(huán)境變量磷仰,并在容器運行時保持,環(huán)境變量可用于ADD境蔼、COPY灶平、ENV伺通、EXPOSE、FROM民逼、LABEL泵殴、USER、VOLUME拼苍、WORKDIR笑诅、ONBUILD指令中。
RUN
格式為:
? ? RUN command
? ? RUN ["executable","param1", "param2"]
前者將在shell終端中運行命令疮鲫,即/bin/sh -c吆你;后者則使用exec執(zhí)行。
指定使用其它終端可以通過第二種方式實現(xiàn)俊犯,例如 RUN ["/bin/bash", "-c", "echo hello"]妇多。每條 RUN 指令將在當前鏡像基礎上執(zhí)行指定命令,并提交為新的鏡像燕侠。當命令較長時可以使用 \ 來換行者祖。例如:
RUN apt-get update && apt-getinstall -y --no-install-recommends \
? ? ? ? ? ? ? g++\
? ? ? ? ? ? ? gcc\
? ? ? ? ? ? ? libc6-dev\
? ? ? ? ? ? ? make\
? ? ? ? ? ? ? pkg-config\
? ? ? &&rm -rf /var/lib/apt/lists/*
CMD
支持三種格式:
? ? CMD ["executable","param1","param2"] 使用exec執(zhí)行,推薦方式绢彤;
? ? CMD command param1 param2在/bin/sh中執(zhí)行七问,提供給需要交互的應用;
? ? CMD ["param1","param2"] 提供給ENTRYPOINT的默認參數(shù)茫舶;
指定啟動容器時執(zhí)行的命令械巡,每個Dockerfile 只能有一條CMD命令。如果指定了多條命令饶氏,只有最后一條會被執(zhí)行讥耗。如果用戶啟動容器時候指定了運行的命令,則會覆蓋掉CMD指定的命令疹启。
LABEL
格式為:
? ? LABEL key=value key=value ...
LABEL指令增加元數(shù)據(jù)到鏡像中古程。可以通過docker inspect imageID查看:
EXPOSE
格式為:
? ? EXPOSE port [port...]
告訴Docker服務端容器暴露的端口號喊崖。在啟動容器時需要通過-P指定籍琳,Docker主機會自動分配一個端口轉發(fā)到指定的端口。
ADD
格式為:
? ? ADD src dest
該命令將復制指定的src到容器中的dest贷祈。 其中src可以是Dockerfile所在目錄的一個相對路徑趋急;也可以是一個URL;還可以是一個tar文件(自動解壓為目錄)势誊。
COPY
格式為:
? ? COPY src dest
復制本地主機的src(為Dockerfile所在目錄的相對路徑)到容器中的 dest呜达。當使用本地目錄為源目錄時,推薦使用COPY粟耻。
ENTRYPOINT
兩種格式:
? ? ENTRYPOINT ["executable", "param1", "param2"]
? ? ENTRYPOINT command param1 param2 (shell中執(zhí)行)
配置容器啟動后執(zhí)行的命令查近,并且不可被docker run提供的參數(shù)覆蓋眉踱。每個Dockerfile中只能有一個ENTRYPOINT,當指定多個時霜威,只有最后一個起效谈喳。
VOLUME
格式為:
? ? VOLUME ["/data"]
創(chuàng)建一個可以從本地主機或其他容器掛載的掛載點,一般用來存放數(shù)據(jù)庫和需要保持的數(shù)據(jù)等戈泼。
USER
格式為:
? ? USER daemon
指定運行容器時的用戶名或UID婿禽,后續(xù)的RUN也會使用指定用戶。當服務不需要管理員權限時大猛,可以通過該命令指定運行用戶扭倾。并且可以在之前創(chuàng)建所需要的用戶,例如:RUN groupadd -r postgres && useradd -r -g postgres postgres挽绩。
WORKDIR
格式為:
? ? WORKDIR /path/to/workdir
為后續(xù)的RUN膛壹、CMD、ENTRYPOINT指令配置工作目錄唉堪。
可以使用多個WORKDIR指令模聋,后續(xù)命令如果參數(shù)是相對路徑,則會基于之前命令指定的路徑唠亚。例如:
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
則最終路徑為/a/b/c链方。
ONBUILD
格式為:
? ? ONBUILD [INSTRUCTION]
配置當所創(chuàng)建的鏡像作為其它新創(chuàng)建鏡像的基礎鏡像時,所執(zhí)行的操作指令趾撵。
例如,Dockerfile使用如下的內容創(chuàng)建了鏡像image-A共啃。
[...]
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
[...]
如果基于image-A創(chuàng)建新的鏡像時占调,新的Dockerfile中使用FROM image-A 指定基礎鏡像時,會自動執(zhí)行ONBUILD指令內容移剪,等價于在后面添加了兩條指令究珊。
FROM image-A
#Automatically run the following
ADD . /app/src
RUN /usr/local/bin/python-build --dir /app/src
使用ONBUILD指令的鏡像,推薦在標簽中注明纵苛,例如ruby:1.9-onbuild或者ruby:2.0-onbuild剿涮。