Docker容器玩转大数据

Docker 镜像构建

2020-02-11  本文已影响0人  AlienPaul

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的第一个指令。用于指定镜像从什么基础镜像开始构建。

常用的基础镜像有:

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"
上一篇 下一篇

猜你喜欢

热点阅读