类似于版本控制系统的提交命令,比如 git commit 命令。
创建容器 -> 修改 -> 提交
使用docker commit命令提交修改过的容器。该命令只提交与之前差异的部分。
还可以使用 -a 选项指定作者,选项 -m 指定提交信息。
使用 docker inspect 命令查看镜像信息。
访问 docker commit 查看 docker commit 命令的所有选项。
因为通过 Dockerfile 构建镜像具有可重复性、透明性、幂等性,因此推荐使用 Dockerfile 来构建镜像。
新镜像是基于已有进行构建的。
Dockerfile 使用 DSL(Domain Specific Lanaguage)
简单 Dockerfile 示例:
FROM ubuntu:14.04 MAINTAINER James Turnbull "jam@example.com" RUN apt-get update && apt-get install -y nginx RUN echo "Hi, I am in your container. " > /usr/share/nginx/html/index.html EXPOSE 80
Dockerfile 由指令和参数组成,每个指令为大写字母,后跟参数,按照从上到下的顺序执行。
每个指令都会创建一个新的镜像层,并进行提交。Dockerfile 的执行流程如下:
1)从基础镜像(使用 FROM 指定)运行一个容器;
2)执行 Dockerfile 中的一条指令,对容器进行修改;
3)在指令结束后,执行提交动作,提交一个新的镜像层;
4)基于刚才提交的镜像层,运行一个新的容器;
5)回到 2) 直到所有指令执行完成;
如果Dockerfile执行失败了,我们也会得到一个可以的镜像,然后可以在这个镜像的基础上进行调试。
默认情况下,命令在/bin/sh -c中执行,如果在不支持shell的平台上、或希望使用exec执行,则可以使用RUN命令的另一种格式:
RUN [ "apt-get", "install", "-y", "nginx" ]
使用数组来传递参数。
指令EXPOSE用于告诉Docker该”容器内的应用程序将会使用哪些端口“,但这并不意味着可以自动访问任意容器运行中服务的端口(这里是80)。出于安全原因,需要用户使用docker run运行容器时来指定打开的端口。
需要创建一个目录,这个目录称为构建环境,在 Docker 中称之为 上下文,构建时该目录的内容会上传到 Docker 守护进程,这样 Docker 守护进程就能直接访问用户想在镜像中存储的任何代码、文件、数据。
在上下文目录中执行 docker build 命令后,docker 或读取 Dockerfile 文件,然后执行其中的指令。可以使用-t选项指定仓库、名称、TAG信息。
命令类似于:docker build -t "jam/static_web:v1" .
最后的点(.)指出当前目录为上下文目录。如果未指定标签(这里是v1),则默认的为latest标签。
也可以使用Git仓库的源地址来指定Dockerfile的位置:docker build -t "jam/static_web:v1" git@github.com:jam/static_web
还可以使用-f选项指定Dockerfile的位置,用于处理Dockerfile文件名非Dockerfile的情况。但是-f选项指定的文件必须位于「构建上下文目录」中:docker build -t "jam/static_web:v1" -f /path/to/dockerfile-v1 .
构建的每一步都会提交,在构建结束后,最终会返回一个镜像 ID。
再次使用 docker image 命令来查看镜像。
如果想深入查看镜像是如何构建的,可以使用 docker history 命令查看镜像的构建历史。
通常,在构建失败时,会产生错误日志,我们根据日志进行调试修改 Dockerfile 即可。
此外,我们还可以,基于上个成功执行的命令所产生的镜像 ID,来创建一个容器,然后运行出错的命令进行调试。在找到错误原因后,进行处理,然后重新构建。
可以使用 docker history 命令查看镜像个构建历史。如下示例,我们发现导致镜像增大的指令:
# docker history 0xa0000/proxy_exporter:0.0.5 IMAGE CREATED CREATED BY SIZE COMMENT 6e5fb0db3577 4 hours ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c" "\"py… 0B 40a3b4bd0dc9 4 hours ago /bin/sh -c #(nop) ENV PORT=1923 0B f926695ca75f 4 hours ago /bin/sh -c pip3 install -r requirements.txt 7.08MB 006c03ee97e5 4 hours ago /bin/sh -c #(nop) ADD multi:d3ed25191265c52e… 19.1kB a9c8c4ce9fcf 4 hours ago /bin/sh -c #(nop) ADD file:6eade3ef219d74a8b… 14.2MB 8a7453e4653c 4 hours ago /bin/sh -c apt-get update && apt-get install… 403MB b96ea74726a8 4 hours ago /bin/sh -c #(nop) WORKDIR /config 0B 56def654ec22 7 weeks ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B <missing> 7 weeks ago /bin/sh -c mkdir -p /run/systemd && echo ‘do… 7B <missing> 7 weeks ago /bin/sh -c [ -z "$(apt-get indextargets)" ] 0B <missing> 7 weeks ago /bin/sh -c set -xe && echo ‘#!/bin/sh‘ > /… 745B <missing> 7 weeks ago /bin/sh -c #(nop) ADD file:4974bb5483c392fb5… 63.2MB
Docker 的构建过程非常聪明,它会将”每一步的构建过程都会将结果提交为镜像“,它会将之前的镜像层作为缓存。
假如第 4 步构建失败,在调整 Dockerfile 重新构建之后,Docker 会直接从第 4 步开始。如果之前的步骤发生了变化,则会从发生变化的位置开始。
可以使用 docker build --no-cache 选项来跳过构建缓存。
在最开始时,每次修改当前目录,都会导致 pip install 重新执行:
FROM ubuntu:14.04 ENV WORKDIR="/srv/web" COPY . ${WORKDIR} RUN pip install -r requirement.txt # 因为当前目录发生变化,导致 COPY 缓存失效
我们调整步骤,提前复制 requirements.txt 文件,以利用缓存:
FROM ubuntu:14.04 ENV WORKDIR="/srv/web" COPY requirements.txt ${WORKDIR}/requirements.txt RUN apt-get -qq update COPY . ${WORKDIR}
「构建缓存」的一个好处是可以实现一个简单的「Dockerfile模板」:
FROM ubuntu:14.04 MAINTAINER James Thrnbull "jam@exampl.com" ENV REFRESHED_AT 2014-07-01 RUN apt-get -qq update
对于上面的Dockerfile,如果我们要更新apt缓存,可以修改 REFRESHED_AT 变量,然后Docker就会从修改行开始执行,从而更新了APT缓存。实质上是,使用一个可修改的指令来控制是否重新执行该指令后面的某些指令。
可以从新镜像中启动一个容器,检查容器是否可以正常工作。比如:
docker rn -d -p --name static_web jamtur01/static_web nginx -g "daemon off;"
在上面的命令中,我们基于该才构建的镜像,jamtur01/static_web,启动了一个名为static_web的容器。使用-d选项,告诉Docker在后台运行容器,选项适用于”需要长时间运行的守护进程“。也指定了容器要运行的nginx -g "daemon off;"命令。
上面的命令还是用到了-p选项,用于控制Docker运行时应该公开那些网络端口给外部(宿主机)。运行容器是,有两种方式在宿主机上分配端口:
* 随机:在Docker宿主机上随机选择一个在32768-61000范围内的端口号来映射到容器的80端口上。 * 特定:在Docker宿主机上指定一个具体的端口号来映射到容器的80端口上。
上面的命令打开了一个随机的端口,然后映射到容器的80端口上。
可以使用dokcer port命令,或者使用docker ps -l命令查看容器的端口映射情况。
选项-p可以指定”绑定到宿主机的特定端口“:-p 80:80
或者其他端口,比如-p 8080:80,将80端口绑定到宿主机的8080端口。
也可以使用类似的方式,绑定到宿主机器的特定网口或IP地址上:-p 127.0.0.1:8080:80
可以绑定到宿主机器指定IP地址的随机端口上:-p 127.0.0.1::80(可以使用docker port或docker inspect查看具体绑定到了哪个端口)
绑定端口时,使用/udp后缀来指定UDP端口。
可以使用-P参数,对外公开EXPOSE指令公开的所有端口,它们会随机绑定到宿主机上的端口。
Dockerfile reference | Docker Documentation / CMD
指定容器启动时运行的命令,和 docker run 命令非常相似。但是,在 docker run 中指定的命令会覆盖 CMD 指定的命令。
该指令有三种形式:
1)CMD ["executable","param1","param2"] (exec form, this is the preferred form)
2)CMD ["param1","param2"] (as default parameters to ENTRYPOINT)
3)CMD command param1 param2 (shell form)
该指令可以使用多次,但是只有最后一个会生效
例如CMD ["/bin/bash"],或者传递参数CMD ["/bin/bash", "-l"]
最好通过数组的形式传递命令和参数。如果直接指定命令,则Docker会在命令前添加/bin/sh -c来执行命令,容易产生意料之外的行为。
Dockerfile reference | Docker Documentation / ENTRYPOINT
# The exec form ENTRYPOINT ["executable", "param1", "param2"] # The shell form ENTRYPOINT command param1 param2
该命令与 RUN 命令非常相似,但是该命令不会被 docker run 所覆盖,而在 docekr run 中指定的命令,会被当作参数传递给 ENTRYPOINT 指定的命令。除此之外,二者的语法、数组形式是一样的。
能够将 CMD 与 ENTRYPOINT 组合使用:
ENTRYPINT ["/usr/sbin/nginx"] CMD ["-h"]
当执行 docker run 时,如果未指定命令,则容器启动时执行 /usr/sbin/nignx -h 命令。如果指定了命令,如 -g "daemon off;" ,则会覆盖 CMD 指令,执行指定的命令,这里是 /usr/sbin/nginx -h 命令。
如果要覆盖 ENTRYPOINT 命令,需要使用 --entrypoint 选项。
切换工作目录。在Docker中切换到指定工作目录,然后执行命令。使用-w选项覆盖工作目录。
在构建过程中定义环境变量。从Docker 1.4开始,一条指令可以同时定义多个环境变量。
例如:RVM RVM_PATH=/home/rvm RVM_ARCHFLAGS="-arch i386"
可以在其他指令中直接使用这些环境变量:WORKDIR $RVM_PATH
如果有必要可以使用反斜线进行转义。
这些变量会保存到容器中,即在容器中执行env时,可以看到这些环境变量。
也可以通过docker run -e选项传递变量,但是这些变量只会在运行时有效。
指定以什么用户去运行。可以使UID和GID的组合:
**USER user** **USER user:group** **USER uid** **USER uid:gid** **USER user:gid** **USER uid:gorup**
比如:USER nginx
可以使用docker run -u来覆盖。如果未设置USER指令,则默认为root用户。
向基于镜像创建的容器中添加卷。卷可以存在于一个或多个容器内的特定的目录。该目录可以绕过联合文件系统,并提供向下共享数据、数据持久化。
卷可以在容器间共享和重用; 一个容器可以不是必须和其他容器共享卷; 对卷的修改立即生效; 对卷的修改不会对更新镜像产生影响; 卷会一个存在知道没有任何容器再使用它;
通过该功能可以将数据添加到镜像,而不是提交到镜像,并且可以在多个容器之间共享这些内容。可以通过该功能测试容器、内部程序源码,管理日志等等。
例如:VOLUME ["/opt/project", "/data"]
「docker cp | Docker Documentation」「Use volumes | Docker Documentation」
将文件复制到镜像中,文件只能位于构建上下文中。
例如:ADD foo.txt /opt/application/foo.txt
「源文件」文件、目录、URL。另外,如果「源文件」是归档文件(gzip, bzip2, xz)则会自动解压目的目录中;解压时,如果目的文件已存在,则不会复制,即不会覆盖。另外,从URL中读取的归档文件不会自动解压。
Docker通过「地址参数末尾的字符」来判断文件是「目录」还是「文件」。
如果目的目录不存在,Docker会递归创建。新文件和目录的权限默认为0755,UID=0,GID=0;
另外ADD会使构建缓存无效,之后的指令不会使用构建缓存。
复制构建上下文中的文件和目录到容器中,与ADD不同的是,COPY不会做额外的操作。
文件或目录的UID=0,GID=0;文件和目录的元数据也会被复制到容器中的文件和目录上。同样,如果目录不存在,Docker会递归创建。
用于添加元数据,已键值对的形式存在:
LABEL version="1.0" LABEL lacation="Somewhere" type="Data Center" role="Web Server"
一条指令中可以存在多个元数据,键值对形式,元数据间以空格分隔。推荐多条元数据放在一个LABEL指令中,以防止创建过多的镜像层。通过docker inspect来查看元数据。
指定docker stop时发送给容器的信号。这个信号必须是内核中合法的信号值,例如9,或者信号名SIGNAME,例如SIGKILL。
在Docker 1.9中引入该指令,该指令指定在docker build命令运行时,传递给构建运行时的变量,只需要在构建时指定--build-arg标志即可。用户只能在构建时指定定义过的参数。
ARG build ARG webapp_user=root
上面的示例中,第二条ARG设置了默认值root。如果未使用--build-arg指定该参数,则使用默认值。
例如,docker build --build-arg build=1234 -t jamti/webapp .,设置build参数的值为1234,而webapp_user将使用默认值root。
!!!不要使用该功能传递密钥等类型的参数。
在Docker中预定义了一组ARG变量,可以在构建时直接使用,无需再定义一次。「Predefined ARGs」
为镜像添加触发器。当该镜像会用做其他镜像的基础镜像时,该镜像中触发器会被执行。
触发器在构建过程中执行当初添加的指令,可以将其视为实在FROM之后执行的
ONBUILD ADD . /app/src ONBUILD RUN cd /app/src && make
在构建镜像时,这些触发器会被加入到镜像中。使用docker inspect来查看加入镜像中的触发器。
通过这种方式可以为应用程序进行一些特定的配置和设置构建信息。
!!!触发器只能被继承一次!!!有些命令不能放在触发器中,这是为了防止产生递归调用。
编写Dockerfile的最佳实践:「Best practices for writing Dockerfiles」
「Docker」- 借助工具,以树形图的形式,查看镜像层
「Docker」- 删除镜像
「Docker」- 清理镜像(Prune images)
「Docker」- 保存镜像到本地
Dockerfile reference
Dockerfile COPY vs ADD: key differences and best practices
原文:https://www.cnblogs.com/k4nz/p/14588769.html