Docker 镜像构建

作者: AlienPaul | 来源:发表于2020-02-11 13:18 被阅读0次

Dockerfile

以一个镜像为基础,通过指令构建其他镜像。

构建Docker镜像

在Dockerfile所在目录执行docker build -t 镜像名:标签 .(上下文路径)创建新的镜像。

默认Docker在构建的时候会从上下文路径中查找名字为Dockerfile的文件,来执行构建。

Dockerfile中所有针对宿主机文件的指令均以上下文路径作为工作目录。

可以使用-f 指定Dockerfile的路径和context路径,例如

docker build --no-cache -t helloapp:v2 -f dockerfiles/Dockerfile context

使用标准输入(STDIN)方式传入Dockerfile和context的内容并构建镜像

docker build -t myimage:latest -<<EOF
FROM busybox
RUN echo "hello world"
EOF

使用当前目录作为context,标准输入(STDIN)方式传入Dockerfile构建镜像

docker build -t myimage:latest -f- . <<EOF
FROM busybox
COPY somefile.txt .
RUN cat /somefile.txt
EOF

使用远程的context,标准输入(STDIN)方式传入Dockerfile构建镜像

docker build -t myimage:latest -f- https://github.com/docker-library/hello-world.git <<EOF
FROM busybox
COPY hello.c .
EOF

.dockerignore 文件

.gitignore文件作用类似,.dockerignore文件用来排除build context中的特定文件或目录。

.dockerignore文件的语法和.gitignore文件相同。

Dockerfile 指令

FROM

Dockerfile的第一个指令。用于指定镜像从什么基础镜像开始构建。

常用的基础镜像有:

  • busybox:一个集成了常用Linux命令的基础工具箱。
  • alpine:推荐使用,具备完整的Linux功能,体积非常小,只有5M左右,使用apk工具安装软件包。
  • centos:CentOS系统。
  • ubuntu:Ubuntu系统。
  • jdk:自带指定版本JDK的基础镜像。可以作为Java项目的基础镜像。
  • maven:自带maven的基础镜像。可以作为编译环境构建的基础镜像。
  • scratch:没有任何基础镜像,从零开始构建。

LABEL

LABEL和注释类似,负责给镜像添加一些额外的key-value信息。

使用例子:

# Set one or more individual labels
LABEL com.example.version="0.0.1-beta"
LABEL vendor1="ACME Incorporated"
LABEL vendor2=ZENITH\ Incorporated
LABEL com.example.release-date="2015-02-12"
LABEL com.example.version.is-production=""

注意:字符串的value如果带有空格,必须要用引号引起来。字符串内部的引号同样需要引号引起来。

Docker1.10之前不推荐使用多个LABEL,因为每一个LABEL指令会生成一个新的镜像层。但是Docker1.10之后可以不用刻意这么做。

# Set multiple labels on one line
LABEL com.example.version="0.0.1-beta" com.example.release-date="2015-02-12"

LABEL一行指令还可以分到多行来写,例如:

# Set multiple labels at once, using line-continuation characters to break long lines
LABEL vendor=ACME\ Incorporated \
      com.example.is-beta= \
      com.example.is-production="" \
      com.example.version="0.0.1-beta" \
      com.example.release-date="2015-02-12"

RUN

构建镜像时执行Linux命令。RUN执行使用sh -c来运行命令。

注意:

  1. RUN执行每执行一次会产生一个新的镜像层,尽量不要产生过多的镜像层。
  2. 构建镜像每一层都会生成一个缓存。例如:
FROM ubuntu:18.04
RUN apt-get update
RUN apt-get install -y curl

运行apt-get update之后的结果会生成一个缓存。
如果之后修改了这个Dockerfile,增加一个安装nginx包,Docker仍会使用之前apt-get update之后的缓存继续构建。这样会存在问题。新构建的镜像使用的包不是构建时最新的。要解决这个问题,需要将update和安装命令写到同一行。

RUN apt-get update && apt-get install -y \
    package-bar \
    package-baz \
    package-foo=1.3.*
  1. 使用命令pipeline,只要最后一个命令运行正常,这条RUN命令会被认为执行成功。例如:
RUN wget -O - https://some.site | wc -l > /number

如果wget运行失败,这条RUN执行仍会执行成功。
如果必须要求pipeline前后命令都执行成功,可以使用如下命令:

RUN set -o pipefail && wget -O - https://some.site | wc -l > /number

如果使用的是Debian系Linux镜像,默认使用的dash不支持set -o pipefail。需要明确指定bash去执行命令,如下所示:

RUN ["/bin/bash", "-c", "set -o pipefail && wget -O - https://some.site | wc -l > /number"]

CMD

用于指定运行镜像中用户程序的入口。可以带有启动参数。

CMD指令的使用形式如下:

CMD ["executable", "param1", "param2"…]

注意:用户进程绝对不能以后台形式或者服务形式运行,否则container会认为主线程执行完毕退出。必须使用前台形式运行用户进程。

Docker运行时候用户可以指定入口命令。CMD中的入口命令会被覆盖。

一个例子如下

CMD ["/bin/bash", "-c", "Hello World"]

默认启动container,会打印出"Hello World"

如果启动container的时候使用参数echo "Blabla",CMD会被覆盖,结果打印出"Blabla"。

ENTRYPOINT

ENTRYPOINT指令和CMD指令基本相同。使用形式如下:

ENTRYPOINT ["/bin/bash", "-c", "Hello World"]

但是ENTRYPOINT和CMD命令还有区别。如果用户在启动container时候指定了自定义命令,只会追加到ENTRYPOINT的命令之后,不会完整替换掉ENTRYPOINT命令。

例如:

ENTRYPOINT ["echo"]

启动container传入自定义命令Hello World,container启动的时候会执行echo Hello World打印出"Hello World"字样。

ENTRYPOINT通常和CMD配合使用。ENTRYPOINT指定用户程序入口命令,CMD指定命令的默认执行参数。例如:

ENTRYPOINT ["echo"]
CMD ["Hello World"]

默认运行会执行

echo Hello World

如果运行时指定了自定义命令,例如docker run -it example-image Hola,此时"Hola"会替换掉CMD中的"Hello World",然后追加到ENTRYPOINT之后,相当于执行了:

echo Hola

编写入口脚本的时候最好使用exec命令执行用户程序,这样用户程序在container中的PID为1,方便接收到Unix信号。

EXPOSE

用于声明需要暴露端口,大多数情况用于增加可读性。

使用方式如下:

EXPOSE 8080

如果运行的时候使用了-P参数,Docker会映射镜像所有EXPOSE的端口到宿主机任意端口。

docker run -it example-image -P

如果使用手工指定宿主机和容器的端口映射,EXPOSE将不起作用。

docker run -it example-image -p 80:8080

ENV

指定运行时container中的环境变量。

ENV设置的变量既可以在Dockerfile中使用,也可以在container内使用。

使用例子如下:

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

注意:ENV也会创建一个新的镜像层。这样会导致ENV设置的环境变量,无法被后续的RUN命令清除掉。
例如:

FROM alpine
ENV ADMIN_USER="mark"
RUN echo $ADMIN_USER > ./mark
RUN unset ADMIN_USER

尽管执行了unset,ADMIN_USER环境变量依然存在。

为了避免这种情况,需要把设置环境变量和使用它的地方写到同一层镜像的构建中。例如:

FROM alpine
RUN export ADMIN_USER="mark" \
    && echo $ADMIN_USER > ./mark \
    && unset ADMIN_USER
CMD sh

COPY

COPY用于构建时从宿主机向镜像中复制文件。

如果需要复制多组文件,尽量使用多个COPY指令,这样每次Docker缓存中只有一组文件的复制操作。还有COPY操作尽量在RUN指令之后,减少以后修改Dockerfile文件Docker缓存失效的影响返回,加快构建速度。

ADD

和COPY作用一样也是用于向镜像中添加文件。ADD还有额外的功能,比如说添加一个tar压缩包时会自动解压。ADD后面跟URL会自动从那里下载文件。但是,仅仅推荐在需要解压tar压缩包到镜像的时候才推荐使用。

使用方式:

ADD rootfs.tar.xz /

强烈建议不要用ADD执行下载URL指向的文件,应使用curl或者wget命令。

VOLUME

VOLUME指令用来标识需要挂载数据卷的目录。

使用方式如下:

VOLUME ["/data"]

运行这个镜像的时候会创建出来一个volume,挂载到/data目录。

可以在运行命令中指定-v参数覆盖,比如说将/data目录映射到宿主机某个目录。

USER

默认RUN指令用root用户执行命令。如果需要切换到其他用户来执行,使用USER指令。

USER指令的使用方法为:

USER <user>[:<group>] or
USER <UID>[:<GID>]

注意:

  1. 在切换用户前需要先创建用户。例如执行:
RUN groupadd -r postgres && useradd --no-log-init -r -g postgres postgres
  1. 因为Go语言的一个bug,在container内创建一个UID比较大的用户会导致container包含/var/log/faillog的一层填满NULL(\0),磁盘会耗尽。规避这个问题的方法是创建用户的时候useradd命令加上--no-log-init参数。Debian/Ubuntu不支持这个参数。
  2. 不要在构建镜像时使用sudo,应当使用gosugosu的安装和使用方式在https://github.com/tianon/gosu
  3. 使用USER切换用户也会创建一个新镜像层,避免过多使用USER来回切换用户。

WORKDIR

切换当前工作目录。例如:

WORKDIR /data
RUN ls config-*

相当于

RUN ls /data/config-*

ARG

和ENV使用方法类似,但是ARG设定的变量只能在Dockerfile中访问,无法在container内部使用。

使用方法:

ARG <name>[=<default value>]

在构建镜像(docker build)的时候使用--build-arg <varname>=<value>形式来给ARG赋值,或者说覆盖Dockerfile中ARG的默认值。

ONBUILD

ONBUILD指令在构建当前镜像的时候不会执行。但是,在其他镜像基于这个镜像构建(即 FROM 这个镜像)的时候,ONBUILD会执行。

ONBUILD的使用例子:

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

注意:专用于ONBUILD的镜像,最好给一个以onbuild结尾的tag,例如ruby:1.9-onbuild

MAINTAINER

注明镜像的维护者信息。

使用方法:

MAINTAINER <name>

注意:该指令已经废弃,建议使用label来替代。例子如下所示:

LABEL maintainer="SvenDowideit@home.org.au"

相关文章

  • python基于Ubuntu下开发环境的准备

    利用Docker搭建Ubuntu环境 安装Docker 基于Dockerfile构建ubuntu镜像 构建镜像 查...

  • Learning Docker Part 002 Docker

    构建镜像 Docker提供了两种构建镜像的方法: docker commit命令 Dockerfile 构建文件 ...

  • Docker部署(二):MySQL数据库

    Docker 容器构建 使用Dockerfile构建镜像 使用官方提供的Docker镜像 所谓Dockerfile...

  • Docker镜像

    镜像: 获取镜像: 构建制作镜像.docker自带 docker commitdockerfile (推荐)封装...

  • 《第一本Docker书》笔记

    初步 确认Docker信息: 镜像 查看镜像 镜像搜索 获取镜像 构建镜像 docker commit Docke...

  • Docker的一些常用命令

    1、镜像命令: docker images 列出所有镜像 docker build 构建镜像 do...

  • Docker

    [TOC] 使用 Docker 镜像 下载镜像 列出镜像 构建镜像 删除本地镜像 操作 Docker 容器 容器是...

  • (四)Docker镜像与仓库之一(3)——构建镜像

    Docker官网 Docker文档地址 构建镜像的好处: 1.构建docker镜像,可以保存对容器的修改,方便...

  • docker镜像

    Docker镜像是构建docker容器的基础,容器是docker镜像的运行的实例。构建镜像只用三条命令就可以搭建一...

  • Java读取HDFS文件

    最近在实现一个功能:用户在前端页面触发Docker镜像构建,后端监听Docker镜像的构建状态,并将Docker镜...

网友评论

    本文标题:Docker 镜像构建

    本文链接:https://www.haomeiwen.com/subject/eyqxfhtx.html