欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Docker基础-使用Dockerfile创建镜像

程序员文章站 2024-03-12 11:38:56
...

目录

 

1.基本结构

1.1 在debian:jessie基础镜像上安装nginx环境,从而创建一个新的nginx镜像:

1.2 基于buildpack-deps:jessie-scm基础镜像,安装golang相关环境,制作一个GO语言的运行环境。

 

2.指令说明

2.1 FROM

2.2 MAINTAINER

2.3 RUN

2.4 CMD

2.5 LABEL

2.6 EXPOSE

2.7 ENV

2.8 ADD

2.9 COPY

2.10 ENTRYPOINT

2.11 VOLUME

2.12 USER

2.13 WORKDIR

2.14 ARG

2.15 ONBUILD

2.16 STOPSIGNAL

2.17 HEALTHCHECK

2.18 SHELL

3.创建镜像

 

4.使用 .dockerignore文件

 

5.Dockerfile编写小结


 

1.基本结构

  Dockerfile由一行行命令语句组成,并支持以#开头的注释行。例如:

# This dockerfile uses the ubuntu image
# VERSION 2 - EDITION 1
# Author: docker_user
# Command format: Instruction [arguments / command ] ..

# Base image to use, this nust be set as the first line
FROM ubuntu

# Maintainer: docker_user <docker_user at email.com> (@docker_user)
MAINTAINER docker_user [email protected]

# Commands to update the image
RUN echo "deb http://archive.ubuntu.com/ubuntu/ raring main universe" >> /etc/apt/sources.list
RUN apt-get update && apt-get install -y nginx
RUN echo "\ndaemon off;" >> /etc/nginx/nginx.conf

# Commands when creating a new container
CMD /usr/sbin/nginx

  其中,开始必须指明所基于的镜像名称,接下来一般是说明维护者信息。后面则是镜像操作指令,例如RUN指令,RUN指令将对镜像执行跟随的命令。每运行一条RUN指令,镜像就添加新的一层,并提交。最后是CMD指令,用来指定运行容器时的操作命令。

  Docker Hub上两个热门Dockerfile:

1.1 在debian:jessie基础镜像上安装nginx环境,从而创建一个新的nginx镜像:

FROM debian:jessie

MAINTAINER NGINX Docker Maintainers "[email protected]"

ENV NGINX_VERSION 1.10.1-1~jessie

RUN apt-key adv --keyserver hkp://pgp.mit.edu:80 --recv-keys 573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62 && \
echo "deb http://nginx.org/package/debian/ jessie nginx" >> /etc/apt/source.list && apt-get update && \
apt-get install --no-install-recommends --no-install-suggests -y ca-certificates nginx=$(NGINX_VERSION) \
nginx-module-xslt nginx-module-geoip nginx-module-image-filter nginx-module-perl nginx-module-njs gettext-base && \
rm -rf /var/lib/apt/lists/*

# forward request and error logs to docker log collector
RUN ln -sf /dev/stdout /var/log/nginx/access.log && ln -sf /dev/stderr /var/log/nginx/err.log

EXPOSE 80 443

CMD ["nginx","-g","daemon off;"]

 

1.2 基于buildpack-deps:jessie-scm基础镜像,安装golang相关环境,制作一个GO语言的运行环境。

FROM buildpack-deps:jessie-scm

# gcc fo cgo
RUN apt-get update && apt-get install -y --no-install-recommends g++ gcc libc6-dev make && rm -rf /var/lib/apt/lists*

ENV GOLANG_VERSION 1.6.3
ENV GOLANG_DOWNLOAD_RUL https://golang.org/dl/go$GOLANG_VERSION.linux-amd64.tar.gz
ENV GOLANG_DOWNLOAD_SHA256 cdd5e08530c0579255d6153b08fdb3b8e47caabbe717bc7bcd7561275a87aeb

RUN curl -fssL "$GOLANG_DOWNLOAD_RUL" -o golang.tar.gz && \
echo "$GOLANG_DOWNLOAD_SHA256 golang.tar.gz" | sha256sum -c - && tar -C /usr/local -xzf golang.tar.gz && rm golang.tar.gz

ENV GOPATH $GOPATH/bin:/usr/local/go/bin:$PATH

RUN mkdir -p "$GOPATH/bin" && chmod -R 777 "$GOPATH"
WORKDIR $GOPATH

COPY go-wrapper /usr/local/bin

 

2.指令说明

  指令的一般格式为INSTRUNCTION arguments,指令包括FROM、MAINTAINER、RUN等。具体指令及说明如下:

指令 说明
FROM 指定所创建镜像的基础镜像
MAINTAINER 指定维护者信息
RUN 运行命令
CMD 指定启动容器时默认执行的命令
LABEL 指定生成镜像的元数据标签信息
EXPOSE 声明镜像内服务所监听的端口
ENV 指定环境变量
ADD 赋值指定的<src>路径下的内容到容器中的<dest>路径下,<src>可以为URL;如果为tar文件,会自动解压到<dest>路径下
COPY 赋值本地主机的<scr>路径下的内容到容器中的<dest>路径下;一般情况下推荐使用COPY而不是ADD
ENTRYPOINT 指定镜像的默认入口
VOLUME 创建数据挂载点
USER 指定运行容器时的用户名或UID
WORKDIR 配置工作目录
ARG 指定镜像内使用的参数(例如版本号信息等)
ONBUILD 配置当前所创建的镜像作为其他镜像的基础镜像时,所执行的创建操作的命令
STOPSIGNAL 容器退出的信号
HEALTHCHECK 如何进行健康检查
SHELL 指定使用SHELL时的默认SHELL类型

 

 

 

 

 

 

 

 

 

 

 

 

 

2.1 FROM

  指定所创建的镜像的基础镜像,如果本地不存在,则默认会去Docker Hub下载指定镜像。
  格式为:FROM<image>,或FROM<image>:<tag>,或FROM<image>@<digest>。
  任何Dockerfile中的第一条指令必须为FROM指令。并且,如果在同一个Dockerfile文件中创建多个镜像,可以使用多个FROM指令(每个镜像一次)。

2.2 MAINTAINER

  指定维护者信息,格式为MAINTAINER<name>。例如:

MAINTAINER [email protected]

  该信息将会写入生成镜像的Author属性域中。

2.3 RUN

  运行指定命令。
  格式为:RUN<command>或RUN ["executable","param1","param2"]。
注意:
        后一个指令会被解析为json数组,所以必须使用双引号。
        前者默认将在shell终端中运行命令,即/bin/sh -c;后者则使用exec执行,不会启动shell环境。
        指定使用其他终端类型可以通过第二种方式实现,例如:
                RUN ["/bin/bash","-c","echo hello"]
        每条RUN指令将在当前镜像的基础上执行指定命令,并提交为新的镜像。当命令较长时可以使用\换行。例如:

RUN apt-get update \
        && apt-get install -y libsnappy-dev zliblg-dev libbz2-dev \
        && rm -rf /var/cache/apt

apt-get

也许RUN指令最常见的用例是安装包用的apt-get。因为RUN apt-get指令会安装包,所以有几个问题需要注意。

不要使用RUN apt-get upgradedist-upgrade,因为许多基础镜像中的“必须”包不会在一个非特权容器中升级。如果基础镜像中的某个包过时了,你应该联系它的维护者。如果你确定某个特定的包,比如foo,需要升级,使用apt-get install -y foo就行,该指令会自动升级foo包。

永远将RUN apt-get updateapt-get install组合成一条RUN声明,例如:

RUN apt-get update && apt-get install -y \
        package-bar \
        package-baz \
        package-foo

apt-get update放在一条单独的RUN声明中会导致缓存问题以及后续的apt-get install失败。比如,假设你有一个 Dockerfile 文件:

FROM ubuntu:14.04
RUN apt-get update
RUN apt-get install -y curl

构建镜像后,所有的层都在 Docker 的缓存中。假设你后来又修改了其中的apt-get install,添加了一个包:

FROM ubuntu:14.04
RUN apt-get update
RUN apt-get install -y curl nginx

Docker 发现修改后的RUN apt-get update指令和之前的完全一样。所以,apt-get update不会执行,而是使用之前的缓存镜像。因为apt-get update没有运行,后面的apt-get install可能安装的是过时的curlnginx版本。

使用RUN apt-get update && apt-get install -y可以确保你的 Dockerfiles 每次安装的都是包的最新的版本,而且这个过程不需要进一步的编码或额外干预。这项技术叫作“cache busting”。你也可以显示指定一个包的版本号来达到 cache-busting。这就是所谓的固定版本,例如:

RUN apt-get update && apt-get install -y \
    package-bar \
    package-baz \
    package-foo=1.3.*

固定版本会迫使构建过程检索特定的版本,而不管缓存中有什么。这项技术也可以减少因所需包中未预料到的变化而导致的失败。

下面是一个RUN指令的示例模板,展示了所有关于apt-get的建议。

RUN apt-get update && apt-get install -y \
    aufs-tools \
    automake \
    build-essential \
    curl \
    dpkg-sig \
    libcap-dev \
    libsqlite3-dev \
    mercurial \
    reprepro \
    ruby1.9.1 \
    ruby1.9.1-dev \
    s3cmd=1.1.* \
 && rm -rf /var/lib/apt/lists/*

其中s3cmd指令指定了一个版本号1.1.0*。如果之前的镜像使用的是更旧的版本,指定新的版本会导致apt-get udpate缓存失效并确保安装的是新版本。

另外,清理掉 apt 缓存,删除var/lib/apt/lists可以减小镜像大小。因为RUN指令的开头为apt-get udpate,包缓存总是会在apt-get install之前刷新。

注意:官方的 Debian 和 Ubuntu 镜像会自动运行apt-get clean,所以不需要显示的调用apt-get clean

 

2.4 CMD

  CMD指令用来指定启动容器时默认执行的命令。它支持三种格式:

1.CMD ["executable","param1","param2"] 使用exec执行,是推荐使用的方式;
2.CMD param1 param2 在/bin/sh中执行,提供给需要交互的应用;
3.CMD ["param1","param2"] 提供给ENTRYPOINT的默认参数。

  每个Dockerfile只能有一条CMD命令。如果指定了多条命令,只有最后一条会被执行。入股用户启动容器时指定了运行的命令(作为run的参数),则会覆盖掉CMD指定的命令。

2.5 LABEL

  LABEL指令用来生成用于生成镜像的元数据的标签信息。
  格式为:LABEL <key>=<value> <key>=<value> <key>=<value> ...。
  例如:

LABEL version="1.0"
LABEL description="This text illustrates \ that label-values can span multiple lines."

2.6 EXPOSE

  声明镜像内服务所监听的端口。
  格式为:EXPOSE <port> [<port>...]
  例如:

EXPOSE 22 80 443 3306

注意:

  该命令只是起到声明租用,并不会自动完成端口映射。
       在容器启动时需要使用-P(大写P),Docker主机会自动分配一个宿主机未被使用的临时端口转发到指定的端口;使用-p(小写p),则可以具体指定哪个宿主机的本地端口映射过来。

2.7 ENV

  指定环境变量,在镜像生成过程中会被后续RUN指令使用,在镜像启动的容器中也会存在。
  格式为:ENV <key><value>或ENV<key>=<value>...。
  例如:

ENV GOLANG_VERSION 1.6.3
ENV GOLANG_DOWNLOAD_RUL https://golang.org/dl/go$GOLANG_VERSION.linux-amd64.tar.gz
ENV GOLANG_DOWNLOAD_SHA256 cdd5e08530c0579255d6153b08fdb3b8e47caabbe717bc7bcd7561275a87aeb

RUN curl -fssL "$GOLANG_DOWNLOAD_RUL" -o golang.tar.gz && echo "$GOLANG_DOWNLOAD_SHA256 golang.tar.gz" | sha256sum -c - && tar -C /usr/local -xzf golang.tar.gz && rm golang.tar.gz

ENV GOPATH $GOPATH/bin:/usr/local/go/bin:$PATH

RUN mkdir -p "$GOPATH/bin" && chmod -R 777 "$GOPATH"

  指令指定的环境变量在运行时可以被覆盖掉,如docker run --env <key>=<value> built_image。

2.8 ADD

  该指令将复制指定的<src>路径下的内容到容器中的<dest>路径下。
  格式为:ADD<src> <dest>
  其中<src>可以使Dockerfile所在目录的一个相对路径(文件或目录),也可以是一个URL,还可以是一个tar文件(如果是tar文件,会自动解压到<dest>路径下)。<dest>可以使镜像内的绝对路径,或者相当于工作目录(WORKDIR)的相对路径。路径支持正则表达式,例如:

ADD *.c /code/

2.9 COPY

  复制本地主机的<src>(为Dockerfile所在目录的一个相对路径、文件或目录)下的内容到镜像中的<dest>下。目标路径不存在时,会自动创建。路径同样支持正则。
  格式为:COPY <src> <dest>
  COPY和ADD指令功能类似,当使用本地目录为源目录时,推荐使用COPY。

2.10 ENTRYPOINT

 指定镜像的默认入口命令,该入口命令会在启动容器时作为根命令执行,所有传入值作为该命令的参数。
 支持两种格式:

1.ENTRYPOINT ["executable","param1","param2"] (exec调用执行);
2.ENTRYPOINT command param1 param2(shell中执行)。

       此时,CMD指令指定值将作为根命令的参数。
  每个Dockerfile中只能有一个ENTRYPOINT,当指定多个时,只有最后一个有效。
  在运行时可以被--entrypoint参数覆盖掉,如docker run --entrypoint。

2.11 VOLUME

  创建一个数据卷挂载点。
  格式为:VOLUME ["/data"]
  可以从本地主机或者其他容器挂载数据卷,一般用来存放数据库和需要保存的数据等。

2.12 USER

  指定运行容器时的用户名或UID,后续的RUN等指令也会使用特定的用户身份。
  格式为:USER daemon
  当服务不需要管理员权限时,可以通过该指令指定运行用户,并且可以在之前创建所需要的用户。例如:

RUN groupadd -r nginx && useradd -r -g nginx nginx

       要临时获取管理员权限可以用gosu或者sudo。

2.13 WORKDIR

  为后续的RUN、CMD和ENTRYPOINT指令配置工作目录。
  格式为:WORKDIR /path/to/workdir。
  可以使用多个WORKDIR指令,后续命令如果参数是相对的,则会基于之前命令指定的路径。例如:

WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd

  则最终路径为/a/b/c

2.14 ARG

  指定一些镜像内使用的参数(例如版本号信息等),这些参数在执行docker build命令时才以--build-arg<varname>=<value>格式传入。
  格式为:ARG<name>[=<default value>]。
  则可以用docker build --build-arg<name>=<value>来指定参数值。

2.15 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指令的内容,等价于在后面添加了两条指令:

FROM image-A

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

  使用ONBUILD指令的镜像,推荐在标签中注明,例如:ruby:1.9-onbuild。

2.16 STOPSIGNAL

  指定所创建镜像启动的容器接收退出的信号值。例如:

STOPSIGNAL singnal

2.17 HEALTHCHECK

  配置所启动容器如何进行健康检查(如何判断是否健康),自Docker 1.12开始支持。
  格式有两种:

1.HEALTHCHECK [OPTIONS] CMD command    :根据所执行命令返回值是否为0判断;
2.HEALTHCHECK NONE              :禁止基础镜像中的健康检查。

  [OPTION]支持:

1.--inerval=DURATION  (默认为:30s):多久检查一次;
2.--timeout=DURATION  (默认为:30s):每次检查等待结果的超时时间;
3.--retries=N        (默认为:3):如果失败了,重试几次才最终确定失败。

2.18 SHELL

  指定其他命令使用shell时的默认shell类型。
  格式为: SHELL ["executable","parameters"]
  默认值为 ["bin/sh","-c"]。
  注意:
    对于Windows系统,建议在Dockerfile开头添加# escape=`来指定转移信息。

 

3.创建镜像

  编写玩Dockerfile之后,可以通过docker build命令来创建镜像。
  基本的docker build [选项] 内容路径,该命令将读取指定路径下(包括子目录)的Dockerfile,并将该路径下的所有内容发送给Docker服务端,由服务端来创建镜像。因此除非生成镜像需要,否则一般建议放置Dockerfile的目录为空目录。

1.如果使用非内容路径下的Dockerfile,可以通过-f选项来指定其路径;
2.要指定生成镜像的标签信息,可以使用-t选项。

  例如:指定Dockerfile所在路径为 /tmp/docker_builder/,并且希望生成镜像标签为build_repo/first_image,可以使用下面的命令:

docker build -t build_repo/first_image /tmp/docker_builder

 

4.使用 .dockerignore文件

  可以通过 .dockeringore文件(每一行添加一条匹配模式)来让Docker忽略匹配模式路径下的目录和文件。例如:

# comment
    */tmp*
    */*/tmp*
    tmp?
    ~*

 

5.Dockerfile编写小结

  从需求出发,定制适合自己需求、高效方便的镜像,可以参考他人优秀的Dockerfile文件,在构建中慢慢优化Dockerfile文件:

1.精简镜像用途:                 尽量让每个镜像的用途都比较集中、单一,避免构造大而复杂、多功能的镜像;
2.选用合适的基础镜像:            过大的基础镜像会造成构建出臃肿的镜像,一般推荐比较小巧的镜像作为基础镜像;
3.提供详细的注释和维护者信息:     Dockerfile也是一种代码,需要考虑方便后续扩展和他人使用;
4.正确使用版本号:               使用明确的具体数字信息的版本号信息,而非latest,可以避免无法确认具体版本号,统一环境;
5.减少镜像层数:                减少镜像层数建议尽量合并RUN指令,可以将多条RUN指令的内容通过&&连接;
6.及时删除临时和缓存文件:        这样可以避免构造的镜像过于臃肿,并且这些缓存文件并没有实际用途;
7.提高生产速度:                合理使用缓存、减少目录下的使用文件,使用.dockeringore文件等;
8.调整合理的指令顺序:           在开启缓存的情况下,内容不变的指令尽量放在前面,这样可以提高指令的复用性;
9.减少外部源的干扰:             如果确实要从外部引入数据,需要制定持久的地址,并带有版本信息,让他人可以重复使用而不出错。