docker学习笔记三

基于Dockerfile构建新镜像

执行docker build命令时,Dockerfile中的所有指令都会被执行并且提交,并且在该命令成功结束后返回一个新镜像

                         docker学习笔记三

 

使用了docker build命令来构建新镜像。我们通过指定-t选项为新镜像设置了仓库和名称,本例中仓库为myhub,镜像名为static_web。强烈建议各位为自己的镜像设置合适的名字以方便追踪和管理

                       docker学习笔记三

                       docker学习笔记三

 

Dockerfile和构建缓存

由于每一步的构建过程都会将结果提交为镜像,所以Docker的构建镜像过程就显得非常聪明。它会将之前的镜像层看作缓存。(比如,在我们的调试例子里,我们不需要在第1步到第3步之间进行任何修改,因此Docker会将之前构建时创建的镜像当做缓存并作为新的开始点。实际上,当再次进行构建时,Docker会直接从第4步开始。当之前的构建步骤没有变化时,这会节省大量的时间。如果真的在第1步到第3步之间做了什么修改,Docker则会从第一条发生了变化的指令开始)

然而,有些时候需要确保构建过程不会使用缓存。比如,如果已经缓存了前面的第3步,即apt-get update,那么Docker将不会再次刷新APT包的缓存。这时用户可能需要取得每个包的最新版本。要想略过缓存功能,可以使用docker build--no-cache标志

 

从构建的镜像启动一个容器

  docker学习笔记三

 

   使用docker run命令,基于刚才构建的镜像的名字,启动了一个名为static_web的新容器。我们同时指定了-d选项,告诉Docker以分离(detached)的方式在后台运行。这种方式非常适合运行类似Nginx守护进程这样的需要长时间运行的进程。我们也指定了需要在容器中运行的命令:nginx -g "daemon off;"。这将以前台运行的方式启动Nginx,来作为我们的Web服务器

   这里也使用了一个新的-p标志,该标志用来控制Docker在运行时应该公开哪些网络端口给外部(宿主机)。运行一个容器时,Docker可以通过两种方法来在宿主机上分配端口。

  • 可以在Docker宿主机中指定一个具体的端口号来映射到容器中的80端口上。

  Docker可以在宿主机上随机选择一个位于32768~61000的一个比较大的端口号来映射到容器中的80端口上

 

可以通过docker port来查看容器的端口映射情况

         

docker学习笔记三

docker学习笔记三

 

 

物理主机查看:

docker学习笔记三

 

-p选项还为我们在将容器端口向宿主机公开时提供了一定的灵活性。比如,可以指定将容器中的端口映射到Docker宿主机的某一特定端口上

docker学习笔记三

    上面的命令会将容器内的80端口绑定到本地宿主机的80端口上。很明显,我们必须非常小心地使用这种直接绑定的做法:如果要运行多个容器,只有一个容器能成功地将端口绑定到本地宿主机上。这将会限制Docker的灵活性。

为了避免这个问题,可以将容器内的端口绑定到不同的宿主机端口上去,如下

docker学习笔记三

 

这里,我们将容器内的80端口绑定到了本地宿主机的127.0.0.1这个IP的80端口上。我们也可以使用类似的方式将容器内的80端口绑定到一个宿主机的随机端口上,如下

docker学习笔记三

 

我们并没有指定具体要绑定的宿主机上的端口号,只指定了一个IP地址127.0.0.1,这时我们可以使用docker inspect或者docker port命令来查看容器内的80端口具体被绑定到了宿主机的哪个端口上

Docker还提供了一个更简单的方式,即-P参数,该参数可以用来对外公开在Dockerfile中通过EXPOSE指令公开的所有端口,如下

docker学习笔记三

 

该命令会将容器内的80端口对本地宿主机公开,并且绑定到宿主机的一个随机端口上。该命令会将用来构建该镜像的Dockerfile文件中EXPOSE指令指定的其他端口也一并公开

Dockerfile指令(必须大写)

我们已经看过了一些Dockerfile中可用的指令,如RUN和EXPOSE。但是,实际上还可以在Dockerfile中放入很多其他指令

CMD

CMD指令用于指定一个容器启动时要运行的命令。这有点儿类似于RUN指令,只是RUN指令是指定镜像被构建时(docker build)要运行的命令,而CMD是指定容器被启动时(docker run)要运行的命令。和使用docker run命令启动容器时指定要运行的命令非常类似

$ sudo docker run -i -t  myhub/static_web  /bin/true

可以认为上述代码所示的命令和在Dockerfile中使用CMD指令是等效的

CMD ["/bin/true"]

CMD命令的参数格式,一般写成字符串数组的方式,如上面的例子。如:

CMD  ["echo","hello world"]

虽然也可写成CMD echo hello word 方式,但这样docker会在指定的命令加  /bin/sh -c 执行,有时有可能会出问题。 所以推荐采用数据结构的方式来存放命令

如果在docker run 命令末尾指定了要运行的命令,将会覆盖掉DockerfileCMD所指定的命令使用docker run命令可以覆盖CMD指令。如果我们在Dockerfile里指定了CMD指令,而同时在docker run命令行中也指定了要运行的命令,命令行中指定的命令会覆盖Dockerfile中的CMD指令

提示:Dockerfile中只能指定一条CMD指令。如果指定了多条CMD指令,也只有最后一条CMD指令会被使用。如果想在启动容器时运行多个进程或者多条命令,可以考虑使用类似Supervisor这样的服务管理工具

ENTRYPOINT

ENTRYPOINT指令与CMD指令区别在于,ENTRYPOINT指令提供的命令则不会在启动容器时被覆盖

实际上,docker run命令行中指定的任何参数都会被当做参数再次传递给ENTRYPOINT指令中指定的命令

ENTRYPOINT ["/usr/sbin/nginx"]

    类似于CMD指令,我们也可以在该指令中通过数组的方式为命令指定相应的参数

ENTRYPOINT ["/usr/sbin/nginx", "-g", "daemon off;"]

   我们指定了-g "daemon off;"参数,这个参数会传递给用ENTRYPOINT指定的命令,在这里该命令为/usr/sbin/nginx -g "daemon off;"。该命令会以前台运行的方式启动Nginx守护进程,此时这个容器就会作为一台Web服务器来运行

我们也可以组合使用ENTRYPOINTCMD指令来完成一些巧妙的工作

ENTRYPOINT ["/usr/sbin/nginx"] 

CMD ["-h"]

此时当我们启动一个容器时,任何在命令行中指定的参数都会被传递给Nginx守护进程。比如,我们可以指定-g "daemon off";参数让Nginx守护进程以前台方式运行。如果在启动容器时不指定任何参数,则在CMD指令中指定的-h参数会被传递给Nginx守护进程,即Nginx服务器会以/usr/sbin/nginx -h的方式启动,该命令用来显示Nginx的帮助信息。

  这使我们可以构建一个镜像,该镜像既可以运行一个默认的命令,同时它也支持通过docker run命令行为该命令指定可覆盖的选项或者标志

Docker使用nginx为什么要加上-g daemon off?

  Docker 容器启动时,默认会把容器内部第一个进程,也就是pid=1的程序,作为docker容器是否正在运行的依据,如果 docker 容器pid=1的进程挂了,那么docker容器便会直接退出。

   Docker未执行自定义的CMD之前,nginxpid1,执行到CMD之后,nginx就在后台运行,bashsh脚本的pid变成了1。所以一旦执行完自定义CMDnginx容器也就退出了

   daemon off;选项阻止Nginx进入后台,强制其在前台运行。这是因为要想保持Docker容器的活跃状态,需要其中运行的进程不能中断。默认情况下,Nginx会以守护进程的方式启动,这会导致容器只是短暂运行,在守护进程被fork启动后,发起守护进程的原始进程就会退出,这时容器就停止运行了

 

WORKDIR

WORKDIR指令用来在从镜像创建一个新容器时,在容器内部设置一个工作目录,ENTRYPOINT和/或CMD指定的程序会在这个目录下执行。

  我们可以使用该指令为Dockerfile中后续的一系列指令设置工作目录,也可以为最终的容器设置工作目录

WORKDIR /opt/webapp/db 

RUN bundle install 

WORKDIR /opt/webapp 

ENTRYPOINT [ "rackup" ]

这里,我们将工作目录切换为/opt/webapp/db后运行了bundle install命令,之后又将工作目录设置为/opt/webapp,最后设置了ENTRYPOINT指令来启动rackup命令。

  可以通过-w标志在运行时覆盖工作目录

ENV

ENV指令用来在镜像构建过程中设置环境变量

这个新的环境变量可以在后续的任何RUN指令中使用,这就如同在命令前面指定了环境变量前缀一样,指定的这些环境变量也会被持久保存到从我们的镜像创建的任何容器中

USER

USER指令用来指定该镜像会以什么样的用户去运行,如果不通过USER指令指定用户,默认用户为root

也可以在docker run命令中通过-u标志来覆盖该指令指定的值

VOLUME(卷功能在后边实例有详细描述)

VOLUME指令用来向基于镜像创建的容器添加卷。一个卷是可以存在于一个或者多个容器内的特定的目录,这个目录可以绕过联合文件系统,并提供如下共享数据或者对数据进行持久化的功能。

    ▲卷可以在容器间共享和重用。

    ▲一个容器可以不是必须和其他容器共享卷。

    ▲对卷的修改是立时生效的。

    ▲对卷的修改不会对更新镜像产生影响。

    ▲卷会一直存在直到没有任何容器再使用它。

 

 卷功能让我们可以将数据(如源代码)、数据库或者其他内容添加到镜像中而不是将这些内容提交到镜像中,并且允许我们在多个容器间共享这些内容。我们可以利用此功能来测试容器和内部的应用程序代码,管理日志,或者处理容器内部的数据库

VOLUME ["/opt/project"]

这条指令将会为基于此镜像创建的任何容器创建一个名为/opt/project的挂载点

也可以通过指定数组的方式指定多个卷

VOLUME ["/opt/project", "/data" ]

ADD

ADD指令用来将构建环境下的文件和目录复制到镜像中。比如,在安装一个应用程序时。ADD指令需要源文件位置目的文件位置两个参数

ADD  software.lic  /opt/application/software.lic

这里的ADD指令将会将构建目录下的software.lic文件复制到镜像中的/opt/application/software.lic。指向源文件的位置参数可以是一个URL,或者构建上下文或环境中文件名或者目录。不能对构建目录或者上下文之外的文件进行ADD操作。

( 这里的构建环境、构建目录、构建上下文是一个意思,都是docker对Dockerfile文件所在位置的一个叫法。  所以ADD指令的源文件位置只能是Dockerfile文件所在目录下的文件、文件夹或一个URL

Docker通过目的地址参数末尾的字符来判断文件源是目录还是文件。如果目标地址以/结尾,那么Docker就认为源位置指向的是一个目录。如果目的地址不以/结尾,那么Docker就认为源位置指向的是文件

文件源也可以使用URL的格式

ADD http://wordpress.org/latest.zip /root/wordpress.zip

最后值得一提的是,ADD在处理本地归档文件(tar archive)时还有一些小魔法。如果将一个归档文件(合法的归档文件包括gzip、bzip2、xz)指定为源文件,Docker会自动将归档文件解开(unpack)

ADD latest.tar.gz /var/www/wordpress/

这条命令会将归档文件latest.tar.gz解开到/var/www/wordpress/目录下。Docker解开归档文件的行为和使用带-x选项的tar命令一样:该指令执行后的输出是原目的目录已经存在的内容加上归档文件中的内容。如果目的位置的目录下已经存在了和归档文件同名的文件或者目录,那么目的位置中的文件或者目录不会被覆盖

docker学习笔记三

 

最后,如果目的位置不存在的话,Docker将会为我们创建这个全路径,包括路径中的任何目录。新创建的文件和目录的模式为0755,并且UID和GID都是0

docker学习笔记三

 

COPY

COPY指令类似于ADD,区别在于COPY只关心在构建上下文中复制本地文件,而不会去做文件提取(extraction)和解压(decompression)的工作

COPY conf.d/ /etc/apache2/

这条指令将会把本地conf.d目录中的文件复制到/etc/apache2/目录中

COPYADD指令的源文件的来源要求相同,文件源路径必须是一个与当前构建环境相对的文件或者目录,本地文件都放到和Dockerfile同一个目录下。不能复制该目录之外的任何文件,因为构建环境将会上传到Docker守护进程,而复制是在Docker守护进程中进行的。任何位于构建环境之外的东西都是不可用的。COPY指令的目的位置则必须是容器内部的一个绝对路径。

任何由该指令创建的文件或者目录的UID和GID都会设置为0。

如果源路径是一个目录,那么这个目录将整个被复制到容器中,包括文件系统元数据;如果源文件为任何类型的文件,则该文件会随同元数据一起被复制。在这个例子里,源路径以/结尾,所以Docker会认为它是目录,并将它复制到目的目录中

如果目的位置不存在,Docker将会自动创建所有需要的目录结构,就像mkdir -p命令那样

LABEL

LABEL指令用于为Docker镜像添加元数据。元数据以键值对的形式展现

LABEL version="1.0"

LABEL location="New York" type="Data Center" role="Web Server"

LABEL指令以label="value"的形式出现。可以在每一条指令中指定一个元数据,或者指定多个元数据,不同的元数据之间用空格分隔。推荐将所有的元数据都放到一条LABEL指令中,以防止不同的元数据指令创建过多镜像层。可以通过docker inspect命令来查看Docker镜像中的标签信息

使用docker inspect命令查看容器标签

STOPSIGNAL

STOPSIGNAL指令用来设置停止容器时发送什么系统调用信号给容器。这个信号必须是内核系统调用表中合法的数,如9,或者SIGNAME格式中的信号名称,如SIGKILL ( 在shell脚本中有关于LINUX信号的描述 )

ARG

ARG指令用来定义可以在docker build命令运行时传递给构建运行时的变量,只需要在构建时使用--build-arg标志即可。用户只能在构建时指定在Dockerfile文件中定义过的参数。

ARG build

ARG webapp_user=user

上面例子中第二条ARG指令设置了一个默认值,如果构建时没有为该参数指定值,就会使用这个默认值。

下面这个例子将会显示如何在docker build中使用这些参数

$ docker  build  --build-arg  build=1234  -t  myhub/webapp  .

这里构建myhub/webapp镜像时,build变量将会设置为1234,而webapp_user变量则会继承设置的默认值user

docker学习笔记三

 

Docker预定义了一组ARG变量,可以在构建时直接使用,而不必再到Dockerfile中自行定义

HTTP_PROXY

http_proxy

HTTPS_PROXY

https_proxy

FTP_PROXY

ftp_proxy

NO_PROXY

no_proxy

要想使用这些预定义的变量,只需要给docker build命令传递--build-arg <variable>=<value>标志就可以了

ONBUILD

ONBUILD指令能为镜像添加触发器(trigger)。当一个已经创建好的镜像被用做其他镜像的基础镜像时(比如用户的镜像需要从某未准备好的位置添加源代码,或者用户需要执行特定于构建镜像的环境的构建脚本),该镜像中的触发器将会被执行

触发器会在构建过程中插入新指令,可以认为这些指令是紧跟在FROM之后指定的,触发器可以是任何构建指令(前面的12种指令)

ONBUILD ADD . /app/src 

ONBUILD RUN cd /app/src && make

上面的代码将会在创建的镜像中加入ONBUILD触发器,ONBUILD指令可以在镜像上运行docker inspect命令来查看

debain centos 安装apache的包名不一样,debain下叫apache2centos下叫httpd  RUN是在Build时运行的,先于CMDENTRYPOINTBuild完成了,RUN也运行完成后,再运行CMD或者ENTRYPOINT

docker学习笔记三

docker学习笔记三

 

 

问题解决:

第一次尝试运行报错,returned a non-zero code: 1

 

 

docker学习笔记三

docker学习笔记三

 

 

 

查询资料得出解决方案:

需要执行:

yum update -y && yum -y  install  httpd

说明yum update过程中需要确认

添加上 yum update -y之后再次运行:

docker学习笔记三

 

通过docker inspect 命令查看详情

docker学习笔记三

docker学习笔记三

 

 

在新构建的镜像中包含一条ONBUILD指令,该指令会使用ADD指令将构建环境所在的目录下的内容全部添加到镜像中的/var/www/目录下。我们可以轻而易举地将这个Dockerfile作为一个通用的Web应用程序的模板,可以基于这个模板来构建Web应用程序

下面我们通过构建一个名为webapp的镜像来看看如何使用镜像模板功能:

webapp的Dockerfile:

( 注意第一行的FROM镜像是来自刚才buildapache_server )

 

 

docker学习笔记三

docker学习笔记三

 

 

      ONBUILD触发器会按照在父镜像中指定的顺序执行,并且只能被继承一次(也就是说只能在子镜像中执行,而不会在孙子镜像中执行)。如果我们再基于myhub/webapp构建一个镜像,则新镜像是myhub/apache2的孙子镜像,因此在该镜像的构建过程中,ONBUILD触发器是不会被执行的。

注意:

这里有好几条指令是不能用在ONBUILD指令中的,包括FROMMAINTAINERONBUILD本身。之所以这么规定是为了防止在 Dockerfile构建过程中产生递归调用的问题。

另外这里贴出linux的退出状态码:

 docker学习笔记三

returned a non-zero code: 127

returned a non-zero code: 2