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 .