Dockerfile是一个文本文件,其中包含额一条一条的指令,每一条指令构建一层,因此每一条指令的作用就是描述这一层应当如何的构建。

以构建nginx镜像为例,使用Dockerfile构建的步骤如下:

在一个空的目录下创建一个名为Dockerfile的文件:

sudo mkdir mynginx
cd mynginx
sudo touch Dockerfile

编辑Dockerfile文件,内容如下:

FROM nginx
RUN echo '<h1>hello,Docker!</h1>' > /usr/share/nginx/html/index.html

上述的Dockerfile文件很简单,只有两个步骤,涉及到两个指令:FROM和RUN

FROM指定基础镜像

所谓的定制镜像,就是以一个镜像为基础,在其上进行定制。而FROM指令的作用就是指定基础镜像,因此在一个Dockerfile文件中FROM指令是必须存在的,并且必须是第一条指令。

在 Docker Store 上有非常多的高质量的官方镜像,有可以直接拿来使用的服务类的镜像,如

nginx 、 redis 、 mongo 、 mysql 、 httpd 、 php 、 tomcat 等;也有一些方便开发、构建、运行各种语言应用的镜像,如 node 、 openjdk 、 python 、 ruby 、 golang 等。可以在其中寻找一个最符合我们最终目标的镜像为基础镜像进行定制。

如果没有找到对应服务的镜像,官方镜像中还提供了一些更为基础的操作系统镜像,如

ubuntu 、 debian 、 centos 、 fedora 、 alpine 等,这些操作系统的软件库为我们提供了更广阔的扩展空间。

除了选择现有镜像为基础镜像外,Docker 还存在一个特殊的镜像,名为 scratch 。这个镜像

是虚拟的概念,并不实际存在,它表示一个空白的镜像。如果你以 scratch 为基础镜像的话,意味着你不以任何镜像为基础,接下来所写的指令将作为镜像第一层开始存在。

RUN执行命令

RUN 指令是用来执行命令行命令的。由于命令行的强大能力, RUN 指令在定制镜像时是最

常用的指令之一。其格式有两种:

  • shell 格式: RUN <命令> ,就像直接在命令行中输入的命令一样。刚才写的 Dockerfile 中的 RUN 指令就是这种格式。
  • exec 格式: RUN ["可执行文件", "参数1", "参数2"] ,这更像是函数调用中的格式

既然RUN就像SHELL脚本一样可以执行命令,那么我们能否向SHELL脚本那样把每条命令对应一个RUN呢?比如一下这样:

FROM debian:jessie
RUN sudo apt-get update
RUN sudo apt-get install -y gcc libc6-dev make
RUN sudo wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz"
RUN sudo mkdir -p /usr/src/redis
RUN sudo tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1
RUN sudo make -C /usr/src/redis
RUN sudo make -C /usr/src/redis install

之前说过Dockerfile的每条指令对应一层,RUN也一样,每一个RUN的行为建立一个新的层,在其上执行这些命令,执行结束后, commit 这一层的修改,构成新的镜像。而上面的写法创建了7层镜像,这是完全没有意义的,很多运行时候不需要的东西被封进了镜像,比如更新的软件包和编译环境。这样产生的镜像就很臃肿,容易出错。

Union FS是有最大层数限制的,不得超过127层,上面的Dockerfile的正确的写法如下:

FROM debian:jessie
RUN buildDeps='gcc libc6-dev make' \
&& apt-get update \
&& apt-get install -y $buildDeps \
&& wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz" \
&& mkdir -p /usr/src/redis \
&& tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 \
&& make -C /usr/src/redis \
&& make -C /usr/src/redis install \
&& rm -rf /var/lib/apt/lists/* \
&& rm redis.tar.gz \
&& rm -r /usr/src/redis \
&& apt-get purge -y --auto-remove $buildDeps

这里使用&&将多个指令链接起来,将原来的7层简化为1层。在撰写 Dockerfile 的时候,要经常提醒自己,这并不是在写 Shell 脚本,而是在定义每一层该如何构建。这一组命令的最后添加了清理工作的命令,删除了为了编译构建所需要的软件,清理了所有下载、展开的文件,并且还清理了 apt 缓存文件。这是很重要的一步,我们之前说过,镜像是多层存储,每一层的东西并不会在下一层被删除,会一直跟随着镜像。因此镜像构建时,一定要确保每一层只添加真正需要添加的东西,任何无关的东西都应该清理掉。

构建镜像

构建镜像可以使用docker build命令,命令格式如下:

docker build [选项] <上下文路径/URL/->

让我们来构建之前nginx的定制镜像,进入到Dockerfile所在的目录,执行一下命令:

sudo docker build -t nginx:v3 .

Sending build context to Docker daemon 2.048 kB
Step 1 : FROM nginx
---> e43d811ce2f4
Step 2 : RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
---> Running in 9cdc27646c7b
---> 44aa4490ce2c
Removing intermediate container 9cdc27646c7b
Successfully built 44aa4490ce2c

从命令的输出结果中,我们可以清晰的看到镜像的构建过程。在 Step 2 中,如同我们之前所说的那样, RUN 指令启动了一个容器 9cdc27646c7b ,执行了所要求的命令,并最后提交了这一层 44aa4490ce2c ,随后删除了所用到的这个容器 9cdc27646c7b。

未完待续

使用Dockerfile定制镜像的更多相关文章

  1. 利用dockerfile定制镜像

    利用dockerfile定制镜像 镜像的定制就是定制每一层所添加的配置.文件.如果可以吧每一层修改.安装.构建.操作的命令都写入到一个脚本,用脚本来构建.定制镜像,这个脚本就是dockerfile. ...

  2. Dockerfile定制镜像

    一.Dockerfile是什么? 镜像定制实质就是定制每一层所添加的配置.文件. Dockerfile就是一个脚本来构建和定制镜像,把每一层的修改.安装.构建.操作都写入脚本.以此来解决体积.镜像构建 ...

  3. 微服务架构 ------ Dockerfile定制镜像

    Docker容器不仅仅是运行原生的容器,而是把我们的具体的项目能够布置到容器上面去,这就是Docker定制镜像需要做的事情.  Docker容器 = new Docker镜像  镜像相当于类,容器相当 ...

  4. Docker 学习笔记(二):Dockerfile 定制镜像

    镜像的定制实际上就是定制每一层所添加的配置.文件. 如果我们可以把每一层修改.安装.构建.操作的命令都写入一个脚本,用这个脚本来构建.定制镜像,那么之前提及的无法重复的问题.镜像构建透明性的问题.体积 ...

  5. Docker Dockerfile 定制镜像

    使用 Dockerfile 定制镜像  镜像的定制实际上就是定制每一层所添加的配置.文件.如果我们可以把每一层修改.安装.构建.操作的命令都写入一个脚本,用这个脚本来构建.定制镜像,那么无法重复的问题 ...

  6. Docker用Dockerfile定制镜像

    用Dockerfile定制镜像 镜像的定制实际上就是定制每一层所添加的配置.文件.如果我们可以把每一层修改.安装.构建.操作的命令都写入一个脚本,用这个脚本来构建.定制镜像,那之前提示的无法重复的问题 ...

  7. Docker Dockerfile 定制镜像(转)

    转自: https://yeasy.gitbooks.io/docker_practice/ 及 https://blog.csdn.net/wo18237095579/article/details ...

  8. Dockerfile 定制镜像

    从上一篇文章中我们可以了解到,镜像的定制实际上就是定制每一层所添加的配置.文件等信息,但是命令毕竟只是命令,每次定制都得去重复执行这个命令,而且还不够直观,如果我们可以把每一层修改.安装.构建.操作的 ...

  9. 利用 Dockerfile 定制镜像

    镜像的定制实际上就是定制每一层所添加的配置.文件. 如果我们可以把每一层修改.安装.构建.操作的命令都写入一个脚本,用这个脚本来构建.定制镜像, 那么之前提及的无法重复的问题.镜像构建透明性的问题.体 ...

随机推荐

  1. conn not captured

    线程 和 事件中 变量 not captured 把变量定义为 static 或者添加为全局变量(放在main之前)

  2. [SoapUI] Tips and Tricks(提示和技巧)

    https://www.soapui.org/scripting-properties/tips-tricks.html

  3. efcore 级联删除

    https://docs.microsoft.com/en-us/ef/core/saving/cascade-delete#required-relationships Optional relat ...

  4. a标签的四个伪类

    A标签的css样式   CSS为一些特殊效果准备了特定的工具,我们称之为“伪类”.其中有几项是我们经常用到的,下面我们就详细介绍一下经常用于定义链接样式的四个伪类,它们分别是: :link    :v ...

  5. c++中嵌套类,外部类访问内部类的私有成员变量

    在嵌套类中,内部类可以直接访问外部类的私有成员变量,但是外部类不能直接访问内部类的私有成员变量,必须把外部类声明为内部类的友元类 /********************************** ...

  6. 06 数据库入门学习-视图、sql注入、事务、存储过程

    一.视图 1.什么是视图 视图本质是一张虚拟的表 2.为什么要用 为了原表的安全 只要有两大功能 1.隐藏部分数据,开放指定数据 2.视图可以将查询结果保存,减少sql语句的次数 特点: 1.视图使用 ...

  7. Django入门与实践-第18章:访问已登录用户(完结)

    http://127.0.0.1:8000/boards/1/topics/1/ #boards/views.py @login_required def new_topic(request, pk) ...

  8. 打开子页面及刷新父页面 window.open window.opener.reload()

    //打开子页面 var url=children_url;window.open(url) //刷新parent页面 var url=parent_urlfunction refresh(url){  ...

  9. BZOJ 1014 [JSOI2008]火星人prefix (Splay + Hash + 二分)

    1014: [JSOI2008]火星人prefix Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 8112  Solved: 2569[Submit] ...

  10. 继承方法-->call继承

    function Person(name,age,sex){ this.name = name; this.age = age; this.sex = sex; }function P1(name,a ...