Dockerfile中ENTRYPOINT 和 CMD的区别说明

在Docker的系统学习教程中我们了解到使用Dockerfile构建Docker镜像为一个规范的方式,根据Dockerfile可以了解镜像中安装的组件的详细内容。

Dockerfile一般由四部分组成:第一,构建的基础镜像;第二,镜像构建者的信息;第三,构建镜像过程中镜像层添加指令;第四,由该镜像启动容器时执行的程序。

本篇文章中涉及到的ENTRYPOINT 和CMD 属于Dockerfile中的最后一部分,这两个Dockerfile指令是用来告知Docker后台程序启动镜像时需要执行的程序,两者有细微的差别。

下面将从两者的异同以及两者联合使用的高级技巧方面对两个指令进行详解。

CMD 指令

CMD 指令指定容器启动时需要运行的程序。一般用最简单的方式启动一个容器时使用docker run 会传递参数给docker指令

docker run -it image /bin/bash

后面的/bin/bash 其实是传递参数,告知容器启动时运行一个shell。这个过程可以用CMD 指令等效的替换

CMD ['/bin/bash']

因此在Dockerfile中存在这个CMD指令指定的命令时,启动容器就可以不进行参数传递。

docker run -it image

执行效果一致。

[root@MiWiFi-R3L-srv test]# docker run --name test -it test_image

[root@3a1bb0c9e35c /]#

如果dockerfile中已经指定了容器启动时运行的程序,同时在使用docker run 启动容器时使用了命令行参数,那么dockerfile 中的cmd 指令将无效

docker run -it image /bin/ps

发现启动容器后没有shell ,只是打印出了当前容器中的进程状态,cmd 指令效果被覆盖。

 PID TTY     TIME CMD
  1 ?    00:00:00 ps
[root@MiWiFi-R3L-srv test]#

此时可以看到cmd 效果被覆盖。在一个dockerfile中只有最后一个cmd 指令有效,因此一个dockerfile中只写一个cmd 指令。

ENTRYPOINT 指令

ENTRYPOINT 指令效果与CMD 非常的类似,比较容易混淆两者的功能。最大的区别在于使用的方式,ENTRYPOINT 指定的命令需要与docker run 启动容器进行搭配,将docker run 指令后面跟的内容当做参数作为ENTRYPOINT指令指定的运行命令的参数,ENTRYPOINT 指定的linux命令一般是不会被覆盖的。

以nginx 镜像为例说明

首先构建一个nginx镜像,并且指定容器运行时执行的程序为nginx。

FROM centos
MAINTAINER allocator
RUN yum install -y nginx
RUN echo 'hello world' > /usr/share/nginx/html/index.html
EXPOSE 80
ENTRYPOINT ["/usr/sbin/nginx"]

然后启动镜像

docker run --name test -p 5000:80 -it test_nginx -g "daemon off"

后面两个是作为参数传递给nginx启动程序运行,此时nginx作为前台程序运行,是一个web服务器,可以根据外部绑定的端口,通过浏览器正常看到hello world

两者联合使用技巧

已经明白了两者的区别,可以利用两者的特点构建一个含有默认启动运行程序的镜像,并且支持docker run 启动时人为指定启动程序运行的参数。

举个例子。利用ENTRYPOINT 指定启动时运行启动nginx程序,并给定默认的运行参数为显示帮助信息,dockerfile 构建如下:

ENTRYPOINT ["/usr/sbin/nginx"]

CMD ["-h"]

当使用docker run --name test -it test_nginx 不传递任何参数时,此时启动容器会使用cmd 指令后的命令作为默认参数,打印nginx的帮助信息。此时cmd 后的内容并不是一个完整的指令,而是参数,如果其内容是一个完整的指令,那么它将覆盖掉ENTRYPOINT 中的内容。

如果使用docker run --name test -it test_nginx -g "daemon off" 启动时,此时给定的运行参数会覆盖掉CMD 指令对应的内容,此时nginx将作为前台进程运行,作为一个web服务器使用,通过browser可以看到hello world

补充知识:docker-entrypoint.sh 入口文件编写技巧

在 docker 的官方 Registry 中(store.docker.com) 流行的第三方应用在自己的页面中都提供了 dockerfile 的链接. 而很多 dockerfile 的 ENTRYPOINT 命令都是这么写的 ["docker-entrypoint.sh"]

本篇文章就扫盲下 docker-entrypoint.sh 的特殊用法和设计逻辑

MySQL

set -e

你写的每个脚本都应该在文件开头加上set -e, 这句语句告诉bash如果任何语句的执行结果不是true则应该退出. 这样的好处是防止错误像滚雪球般变大导致一个致命的错误, 而这些错误本应该在之前就被处理掉. 如果要增加可读性, 可以使用set -o errexit, 它的作用与set -e相同

set -o pipefail

设计用途同上, 就是希望在执行错误之后立即退出, 不要再向下执行了. 而 -o pipefail 的作用域是管道, 也就是说在 Linux 脚本中的管道, 如果前面的命令执行出了问题, 应该立即退出

shopt -s nullglob

在使用 Linux 中的通配符时 * ?等 如果没有匹配到任何文件, 不会报 No such file or directory 而是将命令后面的参数去掉执行

if [ “${1:0:1}” = ‘-‘ ]; then…

这是一个判断语句, 在官方文件中, 上一行已经给出了注释: if command starts with an option, prepend mysqld

这个判断语句是 ${1:0:1} 意思是判断 $1(调用该脚本的第一个参数), 偏移量0(不偏移), 取一个字符(取字符串的长度)

如果判断出来调用这个脚本后面所跟的参数第一个字符是-中横线的话, 就认为后面的所有字符串都是 mysqld 的启动参数

上面的这个操作类似于 Python 的字符串切片

set – mysqld “$@”

在上面判断完第一个参数是-开头之后, 紧接着就执行了 set -- mysqld "$@" 这个命令. 使用了 set -- 的用法. set —会将他后面所有以空格区分的字符串, 按顺序分别存储到$1, $2, $3 变量中, 其中新的$@ 为 set — 后面的全部内容

举例来说: bash docker-entrypoint.sh -f xxx.conf

在这种情况下, set -- mysqld "$@" 中的 $@ 的值为 -f xxx.conf

当执行完 set -- mysqld "$@" 这条命令后:

$1=mysqld
$2=-f
$3=xxx.conf
$@=mysqld -f xxx.conf

可以看到, 当执行 docker-entrypoint.sh脚本的时候后面加了 -x形式的参数之后, $@的值发生的改变, 在原有$@值的基础之上, 在前面又预添加了 mysqld 命令

exec “$@”

几乎在每个 docker-entrypoint.sh 脚本的最后一行, 执行的都是 exec "$@"命令

这个命令的意义在于你已经为你的镜像预想到了应该有的调用情况, 当实际使用镜像的人执行了你没有预料到的可执行命令时, 将会走到脚本的这最后一行, 去执行用户新的可执行命令

情况判断

上面直接说了脚本的最后一行, 在之前的脚本中, 需要充分的去考虑你自己的脚本可能会被调用的情况. 还是拿 MySQL 官方的 dockerfile 来说, 他判断以下情况:

开头是 - , 认为是参数的情况

开头是 mysqld, 且用户 id 为0 (root 用户) 的情况

开头是 mysqld 的情况

判断完自己应用的所有调用形态之后, 最后应该加上exec "$@" 命令兜底

${mysql[@]}

Shell 中的数组, 直接执行 ${mysql[@]} 会把这个数组当做可执行程序来执行

➜ /tmp mysql=( mysql --protocol=socket -uroot -hlocalhost --socket="${SOCKET}" )
➜ /tmp echo ${mysql[1]}
mysql
➜ /tmp echo ${mysql[2]}
--protocol=socket
➜ /tmp echo ${mysql[3]}
-uroot
➜ /tmp echo ${mysql[4]}
-hlocalhost
➜ /tmp echo ${mysql[@]}
mysql --protocol=socket -uroot -hlocalhost --socket=
exec gosu mysql “$BASH_SOURCE” “$@”

这里的 gosu 命令, 是 Linux 中 sudo 命令的轻量级”替代品”

gosu 是一个 golang 语言开发的工具, 用来取代 shell 中的 sudo 命令. su 和 sudo 命令有一些缺陷, 主要是会引起不确定的 TTY, 对信号量的转发也存在问题. 如果仅仅为了使用特定的用户运行程序, 使用 su 或 sudo 显得太重了, 为此 gosu 应运而生.

gosu 直接借用了 libcontainer 在容器中启动应用程序的原理, 使用 /etc/passwd 处理应用程序. gosu 首先找出指定的用户或用户组, 然后切换到该用户或用户组. 接下来, 使用 exec 启动应用程序. 到此为止, gosu 完成了它的工作, 不会参与到应用程序后面的声明周期中. 使用这种方式避免了 gosu 处理 TTY 和转发信号量的问题, 把这两个工作直接交给了应用程序去完成

以上这篇Dockerfile中ENTRYPOINT 和 CMD的区别说明就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • 如何基于Dockerfile构建tomcat镜像

    dockerfile 是用来构建docker镜像的文件!命令参数脚本! 构建步骤: 编写一个dockerfile 文件 docker build 构建成一个镜像 docker run 运行镜像 docker push 发布镜像(DockerHub.阿里云镜像仓库) # 以contes为例 查看 dockerhub 上 contes 的 dockerfile FROM scratch ADD centos-7-x86_64-docker.tar.xz / LABEL \ org.label-sch

  • Dockerfile中CMD和ENTRYPOINT命令详解

    前言 CMD 和 ENTRYPOINT 指令都是用来指定容器启动时运行的命令. 单从功能上来看,这两个命令几乎是重复的.单独使用其中的一个就可以实现绝大多数的用例.但是既然 doker 同时提供了它们,为了在使用中不至于混淆,本文试图把它们的用法理清楚.下面话不多说了,来一起看看详细的介绍吧. exec 模式和 shell 模式 CMD 和 ENTRYPOINT 指令都支持 exec 模式和 shell 模式的写法,所以要理解 CMD 和 ENTRYPOINT 指令的用法,就得先区分 exec

  • Jenkins如何使用DockerFile自动部署Java项目

    Jenkins下用DockerFile自动部署Java(SpringBoot)项目,简单自用,勿喷 一.Jenkins构建Java(SpringBoot)项目 新建-构建一个Maven项目 需要提一下,现在新安装的没有这个选项,需要在插件里安装一下 Maven Integration java配置,其他配置查看以前文章 以上配置即可配置完成 二.发布到服务器,DockerFile部署自动构建 Publish SSH配置 这里在目标服务器上放了个shell命令,我这里的web是做了个Nginx的负

  • Docker 制作镜像Dockerfile和commit操作

    构建镜像 构建镜像主要有两种方式: 使用docker commit命令从运行中的容器提交为镜像: 使用docker build命令从 Dockerfile 构建镜像. 首先介绍下如何从运行中的容器提交为镜像.我依旧使用 busybox 镜像举例,使用以下命令创建一个名为 busybox 的容器并进入 busybox 容器. $ docker run --rm --name=busybox -it busybox sh 执行完上面的命令后,当前窗口会启动一个 busybox 容器并且进入容器中.在

  • dockerfile-maven-plugin极简教程(推荐)

    一.简介 maven是一个项目依赖管理和构建的工具,dockerfile-maven-plugin是一个maven的插件,主要作用是在项目构建的时候生成基于项目的docker镜像文件. 简而言之,此插件将maven和docker进行集成. 正常情况下,我们在开发了一个应用程序后,会使用maven进行打包,生成对应的jar文件.而后,会使用docker将jar文件build成一个镜像(docker image).之后,就可以在docker daemon中创建基于镜像的容器,并可提供服务了. doc

  • Docker容器上用DockerFile部署多个tomcat服务的步骤

    1. [admin@JD ~]$ cd opt #进入根目录下的opt 里 2. [admin@JD opt]$ mkdir web #创建Web 文件夹 3. [admin@JD web]$ cd web/ #进入web文件夹 4. [admin@JD web]$ touch Dockerfile #创建一个Dockerfile文件夹 名字不能错必须是这个名字否者检测不到 5. 利用三方工具将 tomcat 和 jdk的 上传到Web文件夹 6. [admin@JD web]$ vim Doc

  • Dockerfile文本文件使用方法实例解析

    Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明. Docker支持快速的创建自定义镜像 Dockerfile常用命令 在目录下创建一个Dockerfile目录,然后里面创建一个Dockerfile 文本文件,注意不要带上.txt 文件内容 FROM nginx RUN echo '这是一个本地构建的nginx镜像' > /usr/share/nginx/html/index.html docker 刚好可以进到这个目录,然后执行命令 此时使用 d

  • 详解Dockerfile创建自定义Docker镜像以及CMD与ENTRYPOINT指令的比较

    1.概述 创建Docker镜像的方式有三种 docker commit命令:由容器生成镜像: Dockerfile文件+docker build命令: 从本地文件系统导入:OpenVZ的模板. 关于这三种方式的大致说明请参考yeasy/docker_practice的创建镜像. 最近学习了Dockerfile文件的相关配置,这里做一下简单的总结,并对之前一直感到有些迷惑的CMD和ENTRYPOINT指令做个差异对比. 2.Dockerfile文件总结 Dockerfile 由一行行命令语句组成,

  • Dockerfile中ENTRYPOINT 和 CMD的区别说明

    在Docker的系统学习教程中我们了解到使用Dockerfile构建Docker镜像为一个规范的方式,根据Dockerfile可以了解镜像中安装的组件的详细内容. Dockerfile一般由四部分组成:第一,构建的基础镜像:第二,镜像构建者的信息:第三,构建镜像过程中镜像层添加指令:第四,由该镜像启动容器时执行的程序. 本篇文章中涉及到的ENTRYPOINT 和CMD 属于Dockerfile中的最后一部分,这两个Dockerfile指令是用来告知Docker后台程序启动镜像时需要执行的程序,两

  • dockerfile中ENTRYPOINT与CMD的结合使用及区别

    我们在上篇小作文[docker容器dockerfile详解]对中dockerfile有了比较全面的认识,我们也提到`ENTRYPOINT`和`CMD`都可以指定容器启动命令.因为这两个命令是掌握dockerfile编写的核心,所以这边还是单独拿出来再讲一讲. 一.写在前面 我们在上篇小作文docker容器dockerfile详解对中dockerfile有了比较全面的认识,我们也提到ENTRYPOINT和CMD都可以指定容器启动命令.因为这两个命令是掌握dockerfile编写的核心,所以这边还是

  • docker中的run/cmd/entrypoint的区别详解

    Dockerfile中run.cmd和entrypoint都能够用于执行命令,下面是三者的主要用途: run命令执行命令并创建新的镜像层,通常用于安装软件包 cmd命令设置容器启动后默认执行的命令及其参数,但CMD设置的命令能够被docker run命令后面的命令行参数替换 entrypoint配置容器启动时的执行命令,不会被忽略,一定会被执行,即使运行 docker run时指定了其他命令. Shell格式和Exec格式运行命令 我们可以用下面两种格式指定run.cmd和entrypoint要

  • Dockerfile 中 VOLUME 与 docker -v 的区别说明

    Dockerfile 中的 VOLUME 挂载与 docker -v 命令挂载,两者有明显区别: 1.VOLUME Dockerfile中 VOLUME 方式挂载到宿主机上的是匿名卷,在宿主机上是自动匿名挂载到 /var/lib/docker/volumes/ 目录下的,代码如下: FROM frolvlad/alpine-java:jre8-slim MAINTAINER oas.cloud COPY nickdir . VOLUME /usr/local/oas/file/ WORKDIR

  • Dockerfile中的保留字指令的过程解析

    目录 一.Dockerfile是什么? 二.Dockerfile构建过程分析 三.Dockerfile保留字指令 3.1.FROM 3.2.MAINTAINER 3.3.RUN 3.4.EXPOSE 3.5.WORKDIR 3.6.ENV 3.7.ADD 和 COPY 3.8.VOLUME 3.9.CMD 和 ENTRYPOINT 3.10.ONBUILD 四.实战案例 4.1.制作一个自己的Centos镜像 4.1.1.引入: 4.1.2.编写Dockerfile文件 4.1.3.构建cent

  • 详解Dockerfile 中的 COPY 与 ADD 命令

    Dockerfile 中提供了两个非常相似的命令 COPY 和 ADD,本文尝试解释这两个命令的基本功能,以及其异同点,然后总结其各自适合的应用场景. Build 上下文的概念 在使用 docker build 命令通过 Dockerfile 创建镜像时,会产生一个 build 上下文(context).所谓的 build 上下文就是 docker build 命令的 PATH 或 URL 指定的路径中的文件的集合.在镜像 build 过程中可以引用上下文中的任何文件,比如我们要介绍的 COPY

  • Dockerfile中常用命令汇总

    语法组成: 1 注释信息 2 指令---参数 [通常要大写|实质上不区分大小写] 3 顺序执行 4 第一个非注释行必须是from [基于那个基础镜像制作]   5 需要一个专用目录[自己创建] 6 首字目必须大写---Dockerfile 7 制作镜像依赖到文件或者包组时,必须提前准备至专用目录下 .dockerignore file --每一行中定义一个忽略文件     --创建在工作目录中     例如:pam.d/su* ..................................

  • C#中Property和Attribute的区别实例详解

    本文实例分析了C#中Property和Attribute的区别.分享给大家供大家参考.具体分析如下: 在C#中有两个属性,分别为Property和Attribute,两个的中文意思都有特性.属性之间,但是用法上却不一样,为了区别,本文暂把Property称为特性,把Attribute称为属性. Attribute才是本文的主角,把它称为属性我觉得很恰当.属性的意思就是附属于某种事物上的,用来说明这个事物的各种特征的一种描述.而Attribute就是干这事的.它允许你将信息与你定义的C#类型相关联

  • Dockerfile中的ENV指令的具体使用详解

    1. Dockerfile中的ENV指令用以定义镜像的环境变量.示例如下: RUN set -ex && apt-get update && apt-get install -y iputils-ping ENV PATH /usr/local/bin:$PATH ENV LANG C.UTF-8 ENV TERM xterm ENV PYTHON_VERSION 3.5.3 ENV name1=ping name2=on_ip CMD $name1 $name2 说明:定

随机推荐