1、基本结构
Dockerfile 文件一般包含基础镜像信息、维护者信息、镜像操作指令和容器启动时执行指令,’#’ 为 Dockerfile 中的注释。
2、文件写法
Docker以从上到下的顺序运行Dockerfile文件中指令。为了指定基本映像,第一条指令必须是FROM。一个声明以#字符开头则被视为注释。可以在Docker文件中使用RUN,CMD,FROM,EXPOSE,ENV等指令。
常用的指令如下表:
指令 | 作用 | 说明 |
FROM | 指定父镜像 | 指定dockerfile基于那个image构建, 必须为第一个命令。 格式:
|
MAINTAINER | 作者信息 | 用来标明这个dockerfile作者。 格式:
|
LABEL | 标签 | 用来标明dockerfile的标签, 可以使用Label代替Maintainer, 最终都是在docker image基本信息中查看。 格式:
一条LABEL指定可以指定一或多条元数据, 指定多条元数据时不同元数据之间通过空格分隔。 推荐将所有的元数据通过一条LABEL指令指定, 以免生成过多的中间镜像。 |
RUN | 执行命令 | 执行一段命令 默认是/bin/sh,构建镜像时执行的命令。 格式:
注意: 第一种格式是shell执行, 第二种格式是exec执行。 |
CMD | 容器启动命令 | 提供启动容器时候的默认命令 和ENTRYPOINT配合使用。 构建容器后调用, 也就是在容器启动时才进行调用。 格式:
注意: 第一种格式是执行可执行文件, 第二种格式是设置了ENTRYPOINT, 则直接调用ENTRYPOINT添加参数。 第三种格式是执行shell内部命令。 CMD不同于RUN, CMD用于指定在容器启动时所要执行的命令, 而RUN用于指定镜像构建时所要执行的命令。 |
ENTRYPOINT | 入口 | 一般在制作一些执行就关闭的容器中会使用。 格式:
注意: 第一种格式是可执行文件, 第二种格式是shell内部命令。 ENTRYPOINT与CMD非常类似, 不同的是通过docker run执行的命令, 不会覆盖ENTRYPOINT, 而docker run命令中指定的任何参数, 都会被当做参数再次传递给ENTRYPOINT。 Dockerfile中只允许有一个ENTRYPOINT命令, 指定多个时会覆盖前面的设置, 而只执行最后的ENTRYPOINT指令。 |
COPY | 复制文件 | build的时候复制文件到image中, 与ADD类似,但不会自动解压文件, 也不能访问网络资源。 格式:
注意: 第二种格式是用于支持包含空格的路径。 |
ADD | 添加文件 | build的时候添加文件到image中, 不仅仅局限于当前build上下文, 可以来源于远程服务, 将本地文件添加到容器中, tar类型文件会自动解压, 但网络压缩资源不会被解压, 可以访问网络资源,相当于wget。 格式:
注意: 第二种格式是用于支持包含空格的路径。 |
ENV | 环境变量 | 指定build时候的环境变量, 可以在启动容器时, 通过 格式:
注意: 第一种格式中 均会被视为其<value>的组成部分, 则一次只能设置一个变量。 第二种格式中可以设置多个变量, 每个变量为一个 如果 也可以通过 另外, |
ARG | 构建参数 | 构建参数只在构建时使用的参数, 如有 则 格式:
|
VOLUME | 定义外部可以挂载的数据卷 | 指定build的image中启动时挂载到文件系统中的目录, 也可以启动容器时可以使用 格式:
注意: 一个卷可以存在于一个或多个容器的指定目录, 卷可以容器间共享和重用, 对卷的修改不会影响镜像。 |
EXPOSE | 暴露端口 | 定义容器运行时监听的端口, 用于容器外部访问。 启动容器的使用 格式:
例如,
|
WORKDIR | 工作目录 | 指定容器内部的工作目录, 如没有创建则自动创建, 如指定/ 开关的路径, 则使用的是绝对路径, 如果不是/开头路径, 则是上一条workdir的路径的相对路径。 格式:
例如,
|
USER | 指定执行用户 | 指定build或者启动时, 在RUN、CMD 和ENTRYPONT执行时使用的用户。 格式:
注意: 通过 可以通过 |
HEALTHCHECK | 健康检查 | 指定监测当前容器的健康监测的命令。
如果基础镜像有健康检查指令, 使用 可以屏蔽掉其健康检查指令。 |
ONBUILD | 触发器 | 当存在ONBUILD的镜像作为基础镜像时, 当执行FROM完成之后, 会执行 ONBUILD的命令, 但不会影响当前镜像。 格式:
例如,
|
STOPSIGNAL | 发送信号量到宿主机 | 该 将发送到容器的系统调用信号以退出。 |
SHELL | 指定执行脚本的shell | 指定RUN、CMD 和ENTRYPOINT 执行命令时, 使用的shell的类型。 |
3、Dockerfile文件示例
1)Java JDK镜像
FROM alpine:latest ADD jdk-8u211-linux-x64.tar.gz /usr/local/ RUN echo http://mirrors.ustc.edu.cn/alpine/v3.9/main > /etc/apk/repositories && \ echo http://mirrors.ustc.edu.cn/alpine/v3.9/community >> /etc/apk/repositories && \ apk update && apk upgrade RUN apk --no-cache add ca-certificates wget && \ wget -q -O /etc/apk/keys/sgerrand.rsa.pub https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub && \ wget https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.29-r0/glibc-2.29-r0.apk && \ apk add glibc-2.29-r0.apk ENV JAVA_HOME=/usr/local/jdk1.8.0_211 ENV CLASSPATH=$JAVA_HOME/bin ENV PATH=.:$JAVA_HOME/bin:$PATH CMD ["java","-version"]
2)Python3 镜像
FROM alpine:3.14 ENV PATH /usr/local/bin:$PATH # http://bugs.python.org/issue19846 # > At the moment, setting "LANG=C" on a Linux system *fundamentally breaks Python 3*, and that's not OK. ENV LANG C.UTF-8 # runtime 依赖 RUN set -eux; \ apk add --no-cache \ # 安装ca-certificates,使HTTPS工作一致 ca-certificates \ # and tzdata for PEP 615 (https://www.python.org/dev/peps/pep-0615/) tzdata \ ; # Python的其他运行时依赖项稍后安装 ENV GPG_KEY E3FF2839C048B25C084DEBE9B26995E310250568 ENV PYTHON_VERSION 3.9.8 RUN set -ex \ && apk add --no-cache --virtual .fetch-deps \ gnupg \ tar \ xz \ \ && wget -O python.tar.xz "https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz" \ && wget -O python.tar.xz.asc "https://www.python.org/ftp/python/${PYTHON_VERSION%%[a-z]*}/Python-$PYTHON_VERSION.tar.xz.asc" \ && export GNUPGHOME="$(mktemp -d)" \ && gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys "$GPG_KEY" \ && gpg --batch --verify python.tar.xz.asc python.tar.xz \ && { command -v gpgconf > /dev/null && gpgconf --kill all || :; } \ && rm -rf "$GNUPGHOME" python.tar.xz.asc \ && mkdir -p /usr/src/python \ && tar -xJC /usr/src/python --strip-components=1 -f python.tar.xz \ && rm python.tar.xz \ \ && apk add --no-cache --virtual .build-deps \ bluez-dev \ bzip2-dev \ coreutils \ dpkg-dev dpkg \ expat-dev \ findutils \ gcc \ gdbm-dev \ libc-dev \ libffi-dev \ libnsl-dev \ libtirpc-dev \ linux-headers \ make \ ncurses-dev \ openssl-dev \ pax-utils \ readline-dev \ sqlite-dev \ tcl-dev \ tk \ tk-dev \ util-linux-dev \ xz-dev \ zlib-dev \ # 在移除获取deps之前添加构建deps,以防有重叠 && apk del --no-network .fetch-deps \ \ && cd /usr/src/python \ && gnuArch="$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)" \ && ./configure \ --build="$gnuArch" \ --enable-loadable-sqlite-extensions \ --enable-optimizations \ --enable-option-checking=fatal \ --enable-shared \ --with-system-expat \ --with-system-ffi \ --without-ensurepip \ && make -j "$(nproc)" \ # 将线程堆栈大小设置为1MB,这样就不会在遇到sys.getrecursionlimit()之前出现段错误。 # https://github.com/alpinelinux/aports/commit/2026e1259422d4e0cf92391ca2d3844356c649d0 EXTRA_CFLAGS="-DTHREAD_STACK_SIZE=0x100000" \ LDFLAGS="-Wl,--strip-all" \ && make install \ && rm -rf /usr/src/python \ \ && find /usr/local -depth \ \( \ \( -type d -a \( -name test -o -name tests -o -name idle_test \) \) \ -o \( -type f -a \( -name '*.pyc' -o -name '*.pyo' -o -name '*.a' \) \) \ \) -exec rm -rf '{}' + \ \ && find /usr/local -type f -executable -not \( -name '*tkinter*' \) -exec scanelf --needed --nobanner --format '%n#p' '{}' ';' \ | tr ',' '\n' \ | sort -u \ | awk 'system("[ -e /usr/local/lib/" $1 " ]") == 0 { next } { print "so:" $1 }' \ | xargs -rt apk add --no-cache --virtual .python-rundeps \ && apk del --no-network .build-deps \ \ && python3 --version # 制作一些预计会存在的有用符号链接 RUN cd /usr/local/bin \ && ln -s idle3 idle \ && ln -s pydoc3 pydoc \ && ln -s python3 python \ && ln -s python3-config python-config ENV PYTHON_PIP_VERSION 21.2.4 # https://github.com/docker-library/python/issues/365 ENV PYTHON_SETUPTOOLS_VERSION 57.5.0 # https://github.com/pypa/get-pip ENV PYTHON_GET_PIP_URL https://github.com/pypa/get-pip/raw/3cb8888cc2869620f57d5d2da64da38f516078c7/public/get-pip.py ENV PYTHON_GET_PIP_SHA256 c518250e91a70d7b20cceb15272209a4ded2a0c263ae5776f129e0d9b5674309 RUN set -ex; \ \ wget -O get-pip.py "$PYTHON_GET_PIP_URL"; \ echo "$PYTHON_GET_PIP_SHA256 *get-pip.py" | sha256sum -c -; \ \ python get-pip.py \ --disable-pip-version-check \ --no-cache-dir \ "pip==$PYTHON_PIP_VERSION" \ "setuptools==$PYTHON_SETUPTOOLS_VERSION" \ ; \ pip --version; \ \ find /usr/local -depth \ \( \ \( -type d -a \( -name test -o -name tests -o -name idle_test \) \) \ -o \ \( -type f -a \( -name '*.pyc' -o -name '*.pyo' \) \) \ \) -exec rm -rf '{}' +; \ rm -f get-pip.py CMD ["python3"]
3)nginx 镜像
# # NOTE: THIS DOCKERFILE IS GENERATED VIA "update.sh" # # FROM debian:buster-slim LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>" ENV NGINX_VERSION 1.20.1 ENV NJS_VERSION 0.5.3 ENV PKG_RELEASE 1~buster RUN set -x \ # 首先创建nginx用户/组,以在整个docker变体中保持一致 && addgroup --system --gid 101 nginx \ && adduser --system --disabled-login --ingroup nginx --no-create-home --home /nonexistent --gecos "nginx user" --shell /bin/false --uid 101 nginx \ && apt-get update \ && apt-get install --no-install-recommends --no-install-suggests -y gnupg1 ca-certificates \ && \ 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 -y gnupg1 && rm -rf /var/lib/apt/lists/* \ && dpkgArch="$(dpkg --print-architecture)" \ && nginxPackages=" \ nginx=${NGINX_VERSION}-${PKG_RELEASE} \ nginx-module-xslt=${NGINX_VERSION}-${PKG_RELEASE} \ nginx-module-geoip=${NGINX_VERSION}-${PKG_RELEASE} \ nginx-module-image-filter=${NGINX_VERSION}-${PKG_RELEASE} \ nginx-module-njs=${NGINX_VERSION}+${NJS_VERSION}-${PKG_RELEASE} \ " \ && case "$dpkgArch" in \ amd64|i386|arm64) \ echo "deb https://nginx.org/packages/debian/ buster nginx" >> /etc/apt/sources.list.d/nginx.list \ && apt-get update \ ;; \ *) \ echo "deb-src https://nginx.org/packages/debian/ buster nginx" >> /etc/apt/sources.list.d/nginx.list \ \ # 用于存储源文件和.deb文件的新目录 && tempDir="$(mktemp -d)" \ && chmod 777 "$tempDir" \ # (777 to ensure APT's "_apt" user can access it too) \ # 保存当前安装的包列表,以便稍后可以干净地删除构建依赖项 && savedAptMark="$(apt-mark showmanual)" \ \ && apt-get update \ && apt-get build-dep -y $nginxPackages \ && ( \ cd "$tempDir" \ && DEB_BUILD_OPTIONS="nocheck parallel=$(nproc)" \ apt-get source --compile $nginxPackages \ ) \ # 在这里不删除APT列表,因为它们会被重新下载并删除 \ && apt-mark showmanual | xargs apt-mark auto > /dev/null \ && { [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; } \ \ # 创建一个临时的本地APT repo来安装 && 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 \ && apt-get -o Acquire::GzipIndexes=false update \ ;; \ esac \ \ && apt-get install --no-install-recommends --no-install-suggests -y \ $nginxPackages \ gettext-base \ curl \ && apt-get remove --purge --auto-remove -y && rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/nginx.list \ \ && if [ -n "$tempDir" ]; then \ apt-get purge -y --auto-remove \ && rm -rf "$tempDir" /etc/apt/sources.list.d/temp.list; \ fi \ # 转发请求日志和错误日志到docker日志采集器 && ln -sf /dev/stdout /var/log/nginx/access.log \ && ln -sf /dev/stderr /var/log/nginx/error.log \ # 创建一个docker-entrypoint.d 目录 && mkdir /docker-entrypoint.d COPY docker-entrypoint.sh / COPY 10-listen-on-ipv6-by-default.sh /docker-entrypoint.d COPY 20-envsubst-on-templates.sh /docker-entrypoint.d COPY 30-tune-worker-processes.sh /docker-entrypoint.d ENTRYPOINT ["/docker-entrypoint.sh"] EXPOSE 80 STOPSIGNAL SIGQUIT CMD ["nginx", "-g", "daemon off;"]
4、构建生成Docker镜像
编写好构建的镜像的Dockerfile文件后,可以docker build
命令构建生成镜像,如下,
docker build -t ImageName:TagName dir
-t
:是提及图像的标签
ImageName
:要为镜像命名的名称。
TagName
:要为镜像提供的标签。
Dir
:Docker 文件所在的目录。
例如,
docker build -t cjavapy/centos7-jdk1.7 .